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