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