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