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