]> git.donarmstrong.com Git - lilypond.git/blob - lily/system.cc
Reverts smobification of simple spanners.
[lilypond.git] / lily / system.cc
1 /*
2   This file is part of LilyPond, the GNU music typesetter.
3
4   Copyright (C) 1996--2012 Han-Wen Nienhuys <hanwen@xs4all.nl>
5
6   LilyPond is free software: you can redistribute it and/or modify
7   it under the terms of the GNU General Public License as published by
8   the Free Software Foundation, either version 3 of the License, or
9   (at your option) any later version.
10
11   LilyPond is distributed in the hope that it will be useful,
12   but WITHOUT ANY WARRANTY; without even the implied warranty of
13   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14   GNU General Public License for more details.
15
16   You should have received a copy of the GNU General Public License
17   along with LilyPond.  If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include "system.hh"
21
22 #include "align-interface.hh"
23 #include "all-font-metrics.hh"
24 #include "axis-group-interface.hh"
25 #include "break-align-interface.hh"
26 #include "grob-array.hh"
27 #include "hara-kiri-group-spanner.hh"
28 #include "international.hh"
29 #include "lookup.hh"
30 #include "main.hh"
31 #include "output-def.hh"
32 #include "page-layout-problem.hh"
33 #include "paper-column.hh"
34 #include "paper-score.hh"
35 #include "paper-system.hh"
36 #include "pointer-group-interface.hh"
37 #include "skyline-pair.hh"
38 #include "staff-symbol-referencer.hh"
39 #include "text-interface.hh"
40 #include "warn.hh"
41 #include "unpure-pure-container.hh"
42
43 System::System (System const &src)
44   : Spanner (src)
45 {
46   all_elements_ = 0;
47   pscore_ = 0;
48   rank_ = 0;
49   init_elements ();
50 }
51
52 System::System (SCM s)
53   : Spanner (s)
54 {
55   all_elements_ = 0;
56   rank_ = 0;
57   init_elements ();
58 }
59
60 void
61 System::init_elements ()
62 {
63   SCM scm_arr = Grob_array::make_array ();
64   all_elements_ = unsmob_grob_array (scm_arr);
65   all_elements_->set_ordered (false);
66   set_object ("all-elements", scm_arr);
67 }
68
69 Grob *
70 System::clone () const
71 {
72   return new System (*this);
73 }
74
75 int
76 System::element_count () const
77 {
78   return all_elements_->size ();
79 }
80
81 int
82 System::spanner_count () const
83 {
84   int k = 0;
85   for (vsize i = all_elements_->size (); i--;)
86     if (dynamic_cast<Spanner *> (all_elements_->grob (i)))
87       k++;
88   return k;
89 }
90
91 void
92 System::typeset_grob (Grob *elem)
93 {
94   if (elem->layout_)
95     programming_error ("adding element twice");
96   else
97     {
98       elem->layout_ = pscore_->layout ();
99       all_elements_->add (elem);
100       elem->unprotect ();
101     }
102 }
103
104 void
105 System::derived_mark () const
106 {
107   if (!all_elements_->empty ())
108     {
109       Grob **ptr = &all_elements_->array_reference ()[0];
110       Grob **end = ptr + all_elements_->size ();
111       while (ptr < end)
112         {
113           scm_gc_mark ((*ptr)->self_scm ());
114           ptr++;
115         }
116     }
117
118   if (pscore_)
119     scm_gc_mark (pscore_->self_scm ());
120
121   Spanner::derived_mark ();
122 }
123
124 static void
125 fixup_refpoints (vector<Grob *> const &grobs)
126 {
127   for (vsize i = grobs.size (); i--;)
128     grobs[i]->fixup_refpoint ();
129 }
130
131 void
132 System::do_break_substitution_and_fixup_refpoints ()
133 {
134   for (vsize i = 0; i < all_elements_->size (); i++)
135     {
136       Grob *g = all_elements_->grob (i);
137       if (g->internal_has_interface (ly_symbol2scm ("only-prebreak-interface")))
138         {
139           /*
140             Kill no longer needed grobs.
141           */
142           Item *it = dynamic_cast<Item *> (g);
143           if (it && Item::is_non_musical (it))
144             {
145               it->find_prebroken_piece (LEFT)->suicide ();
146               it->find_prebroken_piece (RIGHT)->suicide ();
147             }
148           g->suicide ();
149         }
150       else if (g->is_live ())
151         g->do_break_processing ();
152     }
153
154   /*
155     fixups must be done in broken line_of_scores, because new elements
156     are put over there.  */
157   int count = 0;
158   for (vsize i = 0; i < broken_intos_.size (); i++)
159     {
160       Grob *se = broken_intos_[i];
161
162       extract_grob_set (se, "all-elements", all_elts);
163       for (vsize j = 0; j < all_elts.size (); j++)
164         {
165           Grob *g = all_elts[j];
166           g->fixup_refpoint ();
167         }
168
169       count += all_elts.size ();
170     }
171
172   /*
173     needed for doing items.
174   */
175   fixup_refpoints (all_elements_->array ());
176
177   for (vsize i = 0; i < all_elements_->size (); i++)
178     all_elements_->grob (i)->handle_broken_dependencies ();
179
180   handle_broken_dependencies ();
181
182   /* Because the this->get_property (all-elements) contains items in 3
183      versions, handle_broken_dependencies () will leave duplicated
184      items in all-elements.  Strictly speaking this is harmless, but
185      it leads to duplicated symbols in the output.  uniq makes sure
186      that no duplicates are in the list.  */
187   for (vsize i = 0; i < broken_intos_.size (); i++)
188     {
189       System *child = dynamic_cast<System *> (broken_intos_[i]);
190       child->all_elements_->remove_duplicates ();
191
192       for (vsize j = 0; j < child->all_elements_->size (); j++)
193         {
194           Grob *g = child->all_elements_->grob (j);
195
196           (void) g->get_property ("after-line-breaking");
197         }
198     }
199
200   debug_output (_f ("Element count %d", count + element_count ()) + "\n");
201 }
202
203 SCM
204 System::get_broken_system_grobs ()
205 {
206   SCM ret = SCM_EOL;
207   for (vsize i = 0; i < broken_intos_.size (); i++)
208     ret = scm_cons (broken_intos_[i]->self_scm (), ret);
209   return scm_reverse (ret);
210 }
211
212 SCM
213 System::get_paper_systems ()
214 {
215   SCM lines = scm_c_make_vector (broken_intos_.size (), SCM_EOL);
216   for (vsize i = 0; i < broken_intos_.size (); i++)
217     {
218       debug_output ("[", false);
219
220       System *system = dynamic_cast<System *> (broken_intos_[i]);
221
222       scm_vector_set_x (lines, scm_from_int (i),
223                         system->get_paper_system ());
224
225       debug_output (to_string (i) + "]", false);
226     }
227   return lines;
228 }
229
230 vector<Grob *>
231 System::get_footnote_grobs_in_range (vsize start, vsize end)
232 {
233   vector<Grob *> out;
234   extract_grob_set (this, "footnotes-before-line-breaking", footnote_grobs);
235   for (vsize i = 0; i < footnote_grobs.size (); i++)
236     {
237       Grob *at_bat = footnote_grobs[i];
238       int pos = at_bat->spanned_rank_interval ()[LEFT];
239       bool end_of_line_visible = true;
240       if (Spanner *s = dynamic_cast<Spanner *>(at_bat))
241         {
242           Direction spanner_placement = robust_scm2dir (s->get_property ("spanner-placement"), LEFT);
243           if (spanner_placement == CENTER)
244             spanner_placement = LEFT;
245
246           pos = s->spanned_rank_interval ()[spanner_placement];
247           if (s->original ())
248             {
249               Spanner *orig = dynamic_cast<Spanner *>(s->original ());
250               at_bat = spanner_placement == LEFT ? orig->broken_intos_[0] : orig->broken_intos_.back ();
251               pos = at_bat->spanned_rank_interval ()[RIGHT];
252             }
253         }
254
255       if (Item *item = dynamic_cast<Item *>(at_bat))
256         {
257           if (!Item::break_visible (item))
258             continue;
259           // safeguard to bring down the column rank so that end of line footnotes show up on the correct line
260           if (pos == int (start) && item->break_status_dir () != RIGHT)
261             continue;
262           if (pos == int (end) && item->break_status_dir () != LEFT)
263             continue;
264           if (pos != int (end) && pos != int (start) && item->break_status_dir () != CENTER)
265             continue;
266         }
267
268       if (pos < int (start))
269         continue;
270       if (pos > int (end))
271         continue;
272       if (pos == int (start) && end_of_line_visible)
273         continue;
274       if (pos == int (end) && !end_of_line_visible)
275         continue;
276       if (!at_bat->is_live ())
277         continue;
278
279       out.push_back (at_bat);
280     }
281   return out;
282 }
283
284 vector<Real>
285 System::get_footnote_heights_in_range (vsize start, vsize end)
286 {
287   return internal_get_note_heights_in_range (start, end, true);
288 }
289
290 vector<Real>
291 System::get_in_note_heights_in_range (vsize start, vsize end)
292 {
293   return internal_get_note_heights_in_range (start, end, false);
294 }
295
296 vector<Real>
297 System::internal_get_note_heights_in_range (vsize start, vsize end, bool foot)
298 {
299   vector<Grob *> footnote_grobs = get_footnote_grobs_in_range (start, end);
300   vector<Real> out;
301
302   for (vsize i = footnote_grobs.size (); i--;)
303     if (foot
304         ? !to_boolean (footnote_grobs[i]->get_property ("footnote"))
305         : to_boolean (footnote_grobs[i]->get_property ("footnote")))
306       footnote_grobs.erase (footnote_grobs.begin () + i);
307
308   for (vsize i = 0; i < footnote_grobs.size (); i++)
309     {
310       SCM footnote_markup = footnote_grobs[i]->get_property ("footnote-text");
311
312       if (!Text_interface::is_markup (footnote_markup))
313         continue;
314
315       SCM props = scm_call_1 (ly_lily_module_constant ("layout-extract-page-properties"),
316                               pscore_->layout ()->self_scm ());
317
318       SCM footnote_stl = Text_interface::interpret_markup (pscore_->layout ()->self_scm (),
319                                                            props, footnote_markup);
320
321       Stencil *footnote_stencil = unsmob_stencil (footnote_stl);
322       out.push_back (footnote_stencil->extent (Y_AXIS).length ());
323     }
324
325   return out;
326 }
327
328 vsize
329 System::num_footnotes ()
330 {
331   extract_grob_set (this, "footnotes-after-line-breaking", footnote_grobs);
332   return footnote_grobs.size ();
333 }
334
335 bool
336 grob_2D_less (Grob *g1, Grob *g2)
337 {
338   int sri[] = {0, 0};
339   Grob *gs[] = {g1, g2};
340
341   for (int i = 0; i < 2; i++)
342     {
343       sri[i] = gs[i]->spanned_rank_interval ()[LEFT];
344       if (Spanner *s = dynamic_cast<Spanner *> (gs[i]))
345         {
346           if (s->broken_intos_.size ())
347             s = (scm_to_int (s->broken_intos_[0]->get_property ("spanner-placement")) == LEFT
348                  ? s->broken_intos_[0]
349                  : s->broken_intos_.back ());
350           gs[i] = s;
351           if (robust_scm2double (s->get_property ("X-offset"), 0.0) > 0)
352             sri[i] = s->spanned_rank_interval ()[RIGHT];
353         }
354     }
355
356   if (sri[0] == sri[1])
357     return Grob::vertical_less (gs[0], gs[1]);
358
359   return sri[0] < sri[1];
360 }
361
362 MAKE_SCHEME_CALLBACK (System, footnotes_before_line_breaking, 1);
363 SCM
364 System::footnotes_before_line_breaking (SCM smob)
365 {
366   Grob *me = unsmob_grob (smob);
367   vector<Grob *> footnotes;
368   SCM grobs_scm = Grob_array::make_array ();
369   extract_grob_set (me, "all-elements", elts);
370   for (vsize i = 0; i < elts.size (); i++)
371     if (elts[i]->internal_has_interface (ly_symbol2scm ("footnote-interface")))
372       footnotes.push_back (elts[i]);
373
374   unsmob_grob_array (grobs_scm)->set_array (footnotes);
375   return grobs_scm;
376 }
377
378 MAKE_SCHEME_CALLBACK (System, footnotes_after_line_breaking, 1);
379 SCM
380 System::footnotes_after_line_breaking (SCM smob)
381 {
382   Spanner *sys_span = unsmob_spanner (smob);
383   System *sys = dynamic_cast<System *> (sys_span);
384   Interval_t<int> sri = sys->spanned_rank_interval ();
385   vector<Grob *> footnote_grobs = sys->get_footnote_grobs_in_range (sri[LEFT], sri[RIGHT]);
386   vector_sort (footnote_grobs, grob_2D_less);
387
388   SCM grobs_scm = Grob_array::make_array ();
389   unsmob_grob_array (grobs_scm)->set_array (footnote_grobs);
390   return grobs_scm;
391 }
392
393 void
394 System::break_into_pieces (vector<Column_x_positions> const &breaking)
395 {
396   for (vsize i = 0; i < breaking.size (); i++)
397     {
398       System *system = dynamic_cast<System *> (clone ());
399       system->rank_ = broken_intos_.size ();
400
401       vector<Grob *> c (breaking[i].cols_);
402       pscore_->typeset_system (system);
403
404       int st = Paper_column::get_rank (c[0]);
405       int end = Paper_column::get_rank (c.back ());
406       Interval iv (pure_height (this, st, end));
407       system->set_property ("pure-Y-extent", ly_interval2scm (iv));
408
409       system->set_bound (LEFT, c[0]);
410       system->set_bound (RIGHT, c.back ());
411       SCM system_labels = SCM_EOL;
412       for (vsize j = 0; j < c.size (); j++)
413         {
414           c[j]->translate_axis (breaking[i].config_[j], X_AXIS);
415           dynamic_cast<Paper_column *> (c[j])->set_system (system);
416           /* collect the column labels */
417           collect_labels (c[j], &system_labels);
418         }
419       /*
420         Collect labels from any loose columns too: theses will be set on
421         an empty bar line or a column which is otherwise unused mid-line
422       */
423       vector<Grob *> loose (breaking[i].loose_cols_);
424       for (vsize j = 0; j < loose.size (); j++)
425         collect_labels (loose[j], &system_labels);
426
427       system->set_property ("labels", system_labels);
428
429       set_loose_columns (system, &breaking[i]);
430       broken_intos_.push_back (system);
431     }
432 }
433
434 void
435 System::collect_labels (Grob const *col, SCM *labels)
436 {
437   SCM col_labels = col->get_property ("labels");
438   if (scm_is_pair (col_labels))
439     *labels = scm_append (scm_list_2 (col_labels, *labels));
440 }
441
442 void
443 System::add_column (Paper_column *p)
444 {
445   Grob *me = this;
446   Grob_array *ga = unsmob_grob_array (me->get_object ("columns"));
447   if (!ga)
448     {
449       SCM scm_ga = Grob_array::make_array ();
450       me->set_object ("columns", scm_ga);
451       ga = unsmob_grob_array (scm_ga);
452     }
453
454   p->set_rank (ga->size ());
455
456   ga->add (p);
457   Axis_group_interface::add_element (this, p);
458 }
459
460 void
461 System::pre_processing ()
462 {
463   for (vsize i = 0; i < all_elements_->size (); i++)
464     all_elements_->grob (i)->discretionary_processing ();
465
466   debug_output (_f ("Grob count %d", element_count ()));
467
468   /*
469     order is significant: broken grobs are added to the end of the
470     array, and should be processed before the original is potentially
471     killed.
472   */
473   for (vsize i = all_elements_->size (); i--;)
474     all_elements_->grob (i)->handle_prebroken_dependencies ();
475
476   fixup_refpoints (all_elements_->array ());
477
478   for (vsize i = 0; i < all_elements_->size (); i++)
479     {
480       Grob *g = all_elements_->grob (i);
481       (void) g->get_property ("before-line-breaking");
482     }
483
484   for (vsize i = 0; i < all_elements_->size (); i++)
485     {
486       Grob *e = all_elements_->grob (i);
487       (void) e->get_property ("springs-and-rods");
488     }
489 }
490
491 void
492 System::post_processing ()
493 {
494   Interval iv (extent (this, Y_AXIS));
495   if (iv.is_empty ())
496     programming_error ("system with empty extent");
497   else
498     translate_axis (-iv[MAX], Y_AXIS);
499
500   /* Generate all stencils to trigger font loads.
501      This might seem inefficient, but Stencils are cached per grob
502      anyway. */
503
504   vector<Grob *> all_elts_sorted (all_elements_->array ());
505   vector_sort (all_elts_sorted, std::less<Grob *> ());
506   uniq (all_elts_sorted);
507   this->get_stencil ();
508   for (vsize i = all_elts_sorted.size (); i--;)
509     {
510       Grob *g = all_elts_sorted[i];
511       g->get_stencil ();
512     }
513 }
514
515 struct Layer_entry
516 {
517   Grob *grob_;
518   int layer_;
519 };
520
521 bool
522 operator < (Layer_entry const &a,
523             Layer_entry const &b)
524 {
525   return a.layer_ < b.layer_;
526 }
527
528 SCM
529 System::get_paper_system ()
530 {
531   SCM exprs = SCM_EOL;
532   SCM *tail = &exprs;
533
534   post_processing ();
535
536   vector<Layer_entry> entries;
537   for (vsize j = 0; j < all_elements_->size (); j++)
538     {
539       Layer_entry e;
540       e.grob_ = all_elements_->grob (j);
541       e.layer_ = robust_scm2int (e.grob_->get_property ("layer"), 1);
542
543       entries.push_back (e);
544     }
545
546   vector_sort (entries, std::less<Layer_entry> ());
547   for (vsize j = 0; j < entries.size (); j++)
548     {
549       Grob *g = entries[j].grob_;
550       Stencil st = g->get_print_stencil ();
551
552       if (st.expr () == SCM_EOL)
553         continue;
554
555       Offset o;
556       for (int a = X_AXIS; a < NO_AXES; a++)
557         o[Axis (a)] = g->relative_coordinate (this, Axis (a));
558
559       Offset extra = robust_scm2offset (g->get_property ("extra-offset"),
560                                         Offset (0, 0))
561                      * Staff_symbol_referencer::staff_space (g);
562
563       /* Must copy the stencil, for we cannot change the stencil
564          cached in G.  */
565
566       st.translate (o + extra);
567
568       *tail = scm_cons (st.expr (), SCM_EOL);
569       tail = SCM_CDRLOC (*tail);
570     }
571
572   if (Stencil *me = get_stencil ())
573     exprs = scm_cons (me->expr (), exprs);
574
575   Interval x (extent (this, X_AXIS));
576   Interval y (extent (this, Y_AXIS));
577   Stencil sys_stencil (Box (x, y),
578                        scm_cons (ly_symbol2scm ("combine-stencil"),
579                                  exprs));
580   if (debug_skylines)
581     {
582       Skyline_pair *skylines = Skyline_pair::unsmob (get_property ("vertical-skylines"));
583       if (skylines)
584         {
585           Stencil up
586             = Lookup::points_to_line_stencil (0.1, (*skylines)[UP].to_points (X_AXIS));
587           Stencil down
588             = Lookup::points_to_line_stencil (0.1, (*skylines)[DOWN].to_points (X_AXIS));
589           sys_stencil.add_stencil (up.in_color (255, 0, 0));
590           sys_stencil.add_stencil (down.in_color (0, 255, 0));
591         }
592     }
593
594   Grob *left_bound = this->get_bound (LEFT);
595   SCM prop_init = left_bound->get_property ("line-break-system-details");
596   Prob *pl = make_paper_system (prop_init);
597   paper_system_set_stencil (pl, sys_stencil);
598
599   /* information that the page breaker might need */
600   Grob *right_bound = this->get_bound (RIGHT);
601   pl->set_property ("vertical-skylines", this->get_property ("vertical-skylines"));
602   pl->set_property ("page-break-permission", right_bound->get_property ("page-break-permission"));
603   pl->set_property ("page-turn-permission", right_bound->get_property ("page-turn-permission"));
604   pl->set_property ("page-break-penalty", right_bound->get_property ("page-break-penalty"));
605   pl->set_property ("page-turn-penalty", right_bound->get_property ("page-turn-penalty"));
606
607   if (right_bound->original () == dynamic_cast<System *> (original ())->get_bound (RIGHT))
608     pl->set_property ("last-in-score", SCM_BOOL_T);
609
610   Interval staff_refpoints;
611   if (Grob *align = get_vertical_alignment ())
612     {
613       extract_grob_set (align, "elements", staves);
614       for (vsize i = 0; i < staves.size (); i++)
615         if (staves[i]->is_live ()
616             && Page_layout_problem::is_spaceable (staves[i]))
617           staff_refpoints.add_point (staves[i]->relative_coordinate (this,
618                                                                      Y_AXIS));
619     }
620
621   pl->set_property ("staff-refpoint-extent", ly_interval2scm (staff_refpoints));
622   pl->set_property ("system-grob", this->self_scm ());
623
624   return pl->unprotect ();
625 }
626
627 vector<Item *>
628 System::broken_col_range (Item const *left, Item const *right) const
629 {
630   vector<Item *> ret;
631
632   left = left->get_column ();
633   right = right->get_column ();
634
635   extract_grob_set (this, "columns", cols);
636
637   vsize i = Paper_column::get_rank (left);
638   int end_rank = Paper_column::get_rank (right);
639   if (i < cols.size ())
640     i++;
641
642   while (i < cols.size ()
643          && Paper_column::get_rank (cols[i]) < end_rank)
644     {
645       Paper_column *c = dynamic_cast<Paper_column *> (cols[i]);
646       if (Paper_column::is_breakable (c) && !c->get_system ())
647         ret.push_back (c);
648       i++;
649     }
650
651   return ret;
652 }
653
654 /** Return all columns, but filter out any unused columns , since they might
655     disrupt the spacing problem. */
656 vector<Grob *>
657 System::used_columns () const
658 {
659   extract_grob_set (this, "columns", ro_columns);
660
661   int last_breakable = ro_columns.size ();
662
663   while (last_breakable--)
664     {
665       if (Paper_column::is_breakable (ro_columns [last_breakable]))
666         break;
667     }
668
669   vector<Grob *> columns;
670   for (int i = 0; i <= last_breakable; i++)
671     {
672       if (Paper_column::is_used (ro_columns[i]))
673         columns.push_back (ro_columns[i]);
674     }
675
676   return columns;
677 }
678
679 Paper_column *
680 System::column (vsize which) const
681 {
682   extract_grob_set (this, "columns", columns);
683   if (which >= columns.size ())
684     return 0;
685
686   return dynamic_cast<Paper_column *> (columns[which]);
687 }
688
689 Paper_score *
690 System::paper_score () const
691 {
692   return pscore_;
693 }
694
695 int
696 System::get_rank () const
697 {
698   return rank_;
699 }
700
701 System *
702 get_root_system (Grob *me)
703 {
704   Grob *system_grob = me;
705
706   while (system_grob->get_parent (Y_AXIS))
707     system_grob = system_grob->get_parent (Y_AXIS);
708
709   return dynamic_cast<System *> (system_grob);
710 }
711
712 Grob *
713 System::get_vertical_alignment ()
714 {
715   extract_grob_set (this, "elements", elts);
716   Grob *ret = 0;
717   for (vsize i = 0; i < elts.size (); i++)
718     if (Align_interface::has_interface (elts[i]))
719       {
720         if (ret)
721           programming_error ("found multiple vertical alignments in this system");
722         ret = elts[i];
723       }
724
725   if (!ret)
726     programming_error ("didn't find a vertical alignment in this system");
727   return ret;
728 }
729
730 // Finds the furthest staff in the given direction whose x-extent
731 // overlaps with the given interval.
732 Grob *
733 System::get_extremal_staff (Direction dir, Interval const &iv)
734 {
735   Grob *align = get_vertical_alignment ();
736   if (!align)
737     return 0;
738
739   extract_grob_set (align, "elements", elts);
740   vsize start = (dir == UP) ? 0 : elts.size () - 1;
741   vsize end = (dir == UP) ? elts.size () : VPOS;
742   for (vsize i = start; i != end; i += dir)
743     {
744       if (Hara_kiri_group_spanner::has_interface (elts[i]))
745         Hara_kiri_group_spanner::consider_suicide (elts[i]);
746
747       Interval intersection = elts[i]->extent (this, X_AXIS);
748       intersection.intersect (iv);
749       if (elts[i]->is_live () && !intersection.is_empty ())
750         return elts[i];
751     }
752   return 0;
753 }
754
755 // Finds the neighboring staff in the given direction over bounds
756 Grob *
757 System::get_neighboring_staff (Direction dir, Grob *vertical_axis_group, Interval_t<int> bounds)
758 {
759   Grob *align = get_vertical_alignment ();
760   if (!align)
761     return 0;
762
763   extract_grob_set (align, "elements", elts);
764   vsize start = (dir == UP) ? 0 : elts.size () - 1;
765   vsize end = (dir == UP) ? elts.size () : VPOS;
766
767   Grob *out = 0;
768
769   for (vsize i = start; i != end; i += dir)
770     {
771       if (elts[i] == vertical_axis_group)
772         return out;
773
774       if (Hara_kiri_group_spanner::has_interface (elts[i]))
775         Hara_kiri_group_spanner::consider_suicide (elts[i]);
776
777       bounds.intersect (elts[i]->spanned_rank_interval ());
778       if (elts[i]->is_live () && !bounds.is_empty ())
779         out = elts[i];
780     }
781
782   return 0;
783 }
784
785 Interval
786 System::pure_refpoint_extent (vsize start, vsize end)
787 {
788   Interval ret;
789   Grob *alignment = get_vertical_alignment ();
790   if (!alignment)
791     return Interval ();
792
793   extract_grob_set (alignment, "elements", staves);
794   vector<Real> offsets = Align_interface::get_pure_minimum_translations (alignment, staves, Y_AXIS, start, end);
795
796   for (vsize i = 0; i < offsets.size (); ++i)
797     if (Page_layout_problem::is_spaceable (staves[i]))
798       {
799         ret[UP] = offsets[i];
800         break;
801       }
802
803   for (vsize i = offsets.size (); i--;)
804     if (Page_layout_problem::is_spaceable (staves[i]))
805       {
806         ret[DOWN] = offsets[i];
807         break;
808       }
809
810   return ret;
811 }
812
813 Interval
814 System::part_of_line_pure_height (vsize start, vsize end, bool begin)
815 {
816   Grob *alignment = get_vertical_alignment ();
817   if (!alignment)
818     return Interval ();
819
820   extract_grob_set (alignment, "elements", staves);
821   vector<Real> offsets = Align_interface::get_pure_minimum_translations (alignment, staves, Y_AXIS, start, end);
822
823   Interval ret;
824   for (vsize i = 0; i < staves.size (); ++i)
825     {
826       Interval iv = begin
827                     ? Axis_group_interface::begin_of_line_pure_height (staves[i], start)
828                     : Axis_group_interface::rest_of_line_pure_height (staves[i], start, end);
829       if (i < offsets.size ())
830         iv.translate (offsets[i]);
831       ret.unite (iv);
832     }
833
834   Interval other_elements = begin
835                             ? Axis_group_interface::begin_of_line_pure_height (this, start)
836                             : Axis_group_interface::rest_of_line_pure_height (this, start, end);
837
838   ret.unite (other_elements);
839
840   return ret;
841 }
842
843 Interval
844 System::begin_of_line_pure_height (vsize start, vsize end)
845 {
846   return part_of_line_pure_height (start, end, true);
847 }
848
849 Interval
850 System::rest_of_line_pure_height (vsize start, vsize end)
851 {
852   return part_of_line_pure_height (start, end, false);
853 }
854
855 // This differs from Axis_group_interface::calc_pure_relevant_grobs
856 // because here, we are only interested in those few elements that aren't
857 // descended from VerticalAlignment (ie. things like RehearsalMark, BarLine).
858 MAKE_SCHEME_CALLBACK (System, calc_pure_relevant_grobs, 1);
859 SCM
860 System::calc_pure_relevant_grobs (SCM smob)
861 {
862   Grob *me = unsmob_grob (smob);
863
864   extract_grob_set (me, "elements", elts);
865   vector<Grob *> relevant_grobs;
866   SCM pure_relevant_p = ly_lily_module_constant ("pure-relevant?");
867
868   for (vsize i = 0; i < elts.size (); ++i)
869     {
870       if (!Axis_group_interface::has_interface (elts[i]))
871         {
872           if (to_boolean (scm_apply_1 (pure_relevant_p, elts[i]->self_scm (), SCM_EOL)))
873             relevant_grobs.push_back (elts[i]);
874
875           if (Item *it = dynamic_cast<Item *> (elts[i]))
876             {
877               Direction d = LEFT;
878               do
879                 {
880                   Item *piece = it->find_prebroken_piece (d);
881                   if (piece && to_boolean (scm_apply_1 (pure_relevant_p, piece->self_scm (), SCM_EOL)))
882                     relevant_grobs.push_back (piece);
883                 }
884               while (flip (&d) != LEFT);
885             }
886         }
887     }
888
889   SCM grobs_scm = Grob_array::make_array ();
890
891   unsmob_grob_array (grobs_scm)->set_array (relevant_grobs);
892   return grobs_scm;
893 }
894
895 MAKE_SCHEME_CALLBACK (System, height, 1);
896 SCM
897 System::height (SCM smob)
898 {
899   return Axis_group_interface::height (smob);
900 }
901
902 MAKE_SCHEME_CALLBACK (System, calc_pure_height, 3);
903 SCM
904 System::calc_pure_height (SCM smob, SCM start_scm, SCM end_scm)
905 {
906   System *me = dynamic_cast<System *> (unsmob_grob (smob));
907   int start = scm_to_int (start_scm);
908   int end = scm_to_int (end_scm);
909
910   Interval begin = me->begin_of_line_pure_height (start, end);
911   Interval rest = me->rest_of_line_pure_height (start, end);
912   begin.unite (rest);
913
914   return ly_interval2scm (begin);
915 }
916
917 Grob *
918 System::get_pure_bound (Direction d, int start, int end)
919 {
920   vector<vsize> ranks = pscore_->get_break_ranks ();
921   vector<vsize> indices = pscore_->get_break_indices ();
922   vector<Grob *> cols = pscore_->get_columns ();
923
924   vsize target_rank = (d == LEFT ? start : end);
925   vector<vsize>::const_iterator i
926     = lower_bound (ranks.begin (), ranks.end (), target_rank, std::less<vsize> ());
927
928   if (i != ranks.end () && (*i) == target_rank)
929     return cols[indices[i - ranks.begin ()]];
930   else
931     return 0;
932 }
933
934 Grob *
935 System::get_maybe_pure_bound (Direction d, bool pure, int start, int end)
936 {
937   return pure ? get_pure_bound (d, start, end) : get_bound (d);
938 }
939
940 enum
941 {
942   SPACEABLE_STAVES,
943   NONSPACEABLE_STAVES,
944   ALL_STAVES
945 };
946
947 static SCM
948 get_maybe_spaceable_staves (SCM smob, int filter)
949 {
950   System *me = dynamic_cast<System *> (unsmob_grob (smob));
951   Grob *align = me->get_vertical_alignment ();
952   SCM ret = SCM_EOL;
953
954   if (align)
955     {
956       SCM *tail = &ret;
957       extract_grob_set (align, "elements", staves);
958
959       for (vsize i = 0; i < staves.size (); ++i)
960         {
961           bool spaceable = Page_layout_problem::is_spaceable (staves[i]);
962           if (staves[i]->is_live ()
963               && ((filter == ALL_STAVES)
964                   || (filter == SPACEABLE_STAVES && spaceable)
965                   || (filter == NONSPACEABLE_STAVES && !spaceable)))
966             {
967               *tail = scm_cons (staves[i]->self_scm (), SCM_EOL);
968               tail = SCM_CDRLOC (*tail);
969             }
970         }
971     }
972
973   return ret;
974 }
975
976 MAKE_SCHEME_CALLBACK (System, get_staves, 1)
977 SCM
978 System::get_staves (SCM smob)
979 {
980   return get_maybe_spaceable_staves (smob, ALL_STAVES);
981 }
982
983 MAKE_SCHEME_CALLBACK (System, get_spaceable_staves, 1)
984 SCM
985 System::get_spaceable_staves (SCM smob)
986 {
987   return get_maybe_spaceable_staves (smob, SPACEABLE_STAVES);
988 }
989
990 MAKE_SCHEME_CALLBACK (System, get_nonspaceable_staves, 1)
991 SCM
992 System::get_nonspaceable_staves (SCM smob)
993 {
994   return get_maybe_spaceable_staves (smob, NONSPACEABLE_STAVES);
995 }
996
997 ADD_INTERFACE (System,
998                "This is the top-level object: Each object in a score"
999                " ultimately has a @code{System} object as its X and"
1000                " Y@tie{}parent.",
1001
1002                /* properties */
1003                "all-elements "
1004                "columns "
1005                "footnote-stencil "
1006                "footnotes-before-line-breaking "
1007                "footnotes-after-line-breaking "
1008                "in-note-direction "
1009                "in-note-padding "
1010                "in-note-stencil "
1011                "labels "
1012                "pure-Y-extent "
1013                "skyline-horizontal-padding "
1014               );