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