2 This file is part of LilyPond, the GNU music typesetter.
4 Copyright (C) 1996--2011 Han-Wen Nienhuys <hanwen@xs4all.nl>
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.
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.
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/>.
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"
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"
42 System::System (System
const &src
)
48 checked_footnotes_
= false;
52 System::System (SCM s
)
57 checked_footnotes_
= false;
62 System::init_elements ()
64 SCM scm_arr
= Grob_array::make_array ();
65 all_elements_
= unsmob_grob_array (scm_arr
);
66 all_elements_
->set_ordered (false);
67 set_object ("all-elements", scm_arr
);
71 System::clone () const
73 return new System (*this);
77 System::element_count () const
79 return all_elements_
->size ();
83 System::spanner_count () const
86 for (vsize i
= all_elements_
->size (); i
--;)
87 if (dynamic_cast<Spanner
*> (all_elements_
->grob (i
)))
93 System::typeset_grob (Grob
*elem
)
96 programming_error ("adding element twice");
99 elem
->layout_
= pscore_
->layout ();
100 all_elements_
->add (elem
);
106 System::derived_mark () const
108 if (!all_elements_
->empty ())
110 Grob
**ptr
= &all_elements_
->array_reference ()[0];
111 Grob
**end
= ptr
+ all_elements_
->size ();
114 scm_gc_mark ((*ptr
)->self_scm ());
120 scm_gc_mark (pscore_
->self_scm ());
122 Spanner::derived_mark ();
126 fixup_refpoints (vector
<Grob
*> const &grobs
)
128 for (vsize i
= grobs
.size (); i
--;)
129 grobs
[i
]->fixup_refpoint ();
133 System::do_break_substitution_and_fixup_refpoints ()
135 for (vsize i
= 0; i
< all_elements_
->size (); i
++)
137 Grob
*g
= all_elements_
->grob (i
);
138 if (g
->internal_has_interface (ly_symbol2scm ("only-prebreak-interface")))
141 Kill no longer needed grobs.
143 Item
*it
= dynamic_cast<Item
*> (g
);
144 if (it
&& Item::is_non_musical (it
))
146 it
->find_prebroken_piece (LEFT
)->suicide ();
147 it
->find_prebroken_piece (RIGHT
)->suicide ();
151 else if (g
->is_live ())
152 g
->do_break_processing ();
156 fixups must be done in broken line_of_scores, because new elements
157 are put over there. */
159 for (vsize i
= 0; i
< broken_intos_
.size (); i
++)
161 Grob
*se
= broken_intos_
[i
];
163 extract_grob_set (se
, "all-elements", all_elts
);
164 for (vsize j
= 0; j
< all_elts
.size (); j
++)
166 Grob
*g
= all_elts
[j
];
167 g
->fixup_refpoint ();
170 count
+= all_elts
.size ();
174 needed for doing items.
176 fixup_refpoints (all_elements_
->array ());
178 for (vsize i
= 0; i
< all_elements_
->size (); i
++)
179 all_elements_
->grob (i
)->handle_broken_dependencies ();
181 handle_broken_dependencies ();
183 /* Because the this->get_property (all-elements) contains items in 3
184 versions, handle_broken_dependencies () will leave duplicated
185 items in all-elements. Strictly speaking this is harmless, but
186 it leads to duplicated symbols in the output. uniq makes sure
187 that no duplicates are in the list. */
188 for (vsize i
= 0; i
< broken_intos_
.size (); i
++)
190 System
*child
= dynamic_cast<System
*> (broken_intos_
[i
]);
191 child
->all_elements_
->remove_duplicates ();
192 for (vsize j
= 0; j
< child
->all_elements_
->size (); j
++)
194 Grob
*g
= child
->all_elements_
->grob (j
);
196 (void) g
->get_property ("after-line-breaking");
200 if (be_verbose_global
)
201 message (_f ("Element count %d", count
+ element_count ()) + "\n");
205 System::get_broken_system_grobs ()
208 for (vsize i
= 0; i
< broken_intos_
.size (); i
++)
209 ret
= scm_cons (broken_intos_
[i
]->self_scm (), ret
);
210 return scm_reverse (ret
);
214 System::get_paper_systems ()
216 SCM lines
= scm_c_make_vector (broken_intos_
.size (), SCM_EOL
);
217 for (vsize i
= 0; i
< broken_intos_
.size (); i
++)
219 if (be_verbose_global
)
220 progress_indication ("[");
222 System
*system
= dynamic_cast<System
*> (broken_intos_
[i
]);
224 scm_vector_set_x (lines
, scm_from_int (i
),
225 system
->get_paper_system ());
227 if (be_verbose_global
)
228 progress_indication (to_string (i
) + "]");
234 System::populate_footnote_grob_vector ()
236 extract_grob_set (this, "all-elements", all_elts
);
237 for (vsize i
= 0; i
< all_elts
.size (); i
++)
238 if (all_elts
[i
]->internal_has_interface (ly_symbol2scm ("footnote-interface")))
239 footnote_grobs_
.push_back (all_elts
[i
]);
241 sort (footnote_grobs_
.begin (), footnote_grobs_
.end (), Grob::less
);
242 checked_footnotes_
= true;
246 System::get_footnote_grobs_in_range (vector
<Grob
*> &out
, vsize start
, vsize end
)
248 if (!checked_footnotes_
)
249 populate_footnote_grob_vector ();
251 for (vsize i
= 0; i
< footnote_grobs_
.size (); i
++)
253 int pos
= footnote_grobs_
[i
]->spanned_rank_interval ()[LEFT
];
254 bool end_of_line_visible
= true;
255 if (Spanner
*s
= dynamic_cast<Spanner
*>(footnote_grobs_
[i
]))
257 Direction spanner_placement
= robust_scm2dir (s
->get_property ("spanner-placement"), LEFT
);
258 if (spanner_placement
== CENTER
)
259 spanner_placement
= LEFT
;
261 pos
= s
->spanned_rank_interval ()[spanner_placement
];
264 if (Item
*item
= dynamic_cast<Item
*>(footnote_grobs_
[i
]))
266 if (!Item::break_visible (item
))
268 // safeguard to bring down the column rank so that end of line footnotes show up on the correct line
269 end_of_line_visible
= (LEFT
== item
->break_status_dir ());
272 if (pos
< int (start
))
276 if (pos
== int (start
) && end_of_line_visible
)
278 if (pos
== int (end
) && !end_of_line_visible
)
280 if (!footnote_grobs_
[i
]->is_live ())
283 out
.push_back (footnote_grobs_
[i
]);
288 System::get_footnotes_in_range (vsize start
, vsize end
)
290 vector
<Grob
*> footnote_grobs
;
291 get_footnote_grobs_in_range (footnote_grobs
, start
, end
);
292 vector
<Stencil
*> out
;
294 for (vsize i
= 0; i
< footnote_grobs
.size (); i
++)
296 SCM footnote_markup
= footnote_grobs
[i
]->get_property ("footnote-text");
298 if (!Text_interface::is_markup (footnote_markup
))
301 SCM props
= scm_call_1 (ly_lily_module_constant ("layout-extract-page-properties"),
302 pscore_
->layout ()->self_scm ());
304 SCM footnote_stl
= Text_interface::interpret_markup (pscore_
->layout ()->self_scm (),
305 props
, footnote_markup
);
307 Stencil
*footnote_stencil
= unsmob_stencil (footnote_stl
);
308 out
.push_back (footnote_stencil
);
315 System::make_footnote_stencil (Real padding
)
319 for (vsize i
= 0; i
< footnote_grobs_
.size (); i
++)
321 SCM footnote_markup
= footnote_grobs_
[i
]->get_property ("footnote-text");
322 if (Spanner
*orig
= dynamic_cast<Spanner
*>(footnote_grobs_
[i
]))
323 if (orig
->is_broken ())
324 footnote_markup
= orig
->broken_intos_
[0]->get_property ("footnote-text");
326 if (!Text_interface::is_markup (footnote_markup
))
329 SCM props
= scm_call_1 (ly_lily_module_constant ("layout-extract-page-properties"),
330 pscore_
->layout ()->self_scm ());
332 SCM footnote_stl
= Text_interface::interpret_markup (pscore_
->layout ()->self_scm (),
333 props
, footnote_markup
);
335 mol
.add_at_edge (Y_AXIS
, DOWN
, *unsmob_stencil (footnote_stl
), padding
);
342 System::break_into_pieces (vector
<Column_x_positions
> const &breaking
)
344 for (vsize i
= 0; i
< breaking
.size (); i
++)
346 System
*system
= dynamic_cast<System
*> (clone ());
347 system
->rank_
= broken_intos_
.size ();
349 vector
<Grob
*> c (breaking
[i
].cols_
);
350 pscore_
->typeset_system (system
);
352 int st
= Paper_column::get_rank (c
[0]);
353 int end
= Paper_column::get_rank (c
.back ());
354 Interval
iv (pure_height (this, st
, end
));
355 system
->set_property ("pure-Y-extent", ly_interval2scm (iv
));
357 get_footnote_grobs_in_range (system
->footnote_grobs_
, st
, end
);
359 system
->set_bound (LEFT
, c
[0]);
360 system
->set_bound (RIGHT
, c
.back ());
361 SCM system_labels
= SCM_EOL
;
362 for (vsize j
= 0; j
< c
.size (); j
++)
364 c
[j
]->translate_axis (breaking
[i
].config_
[j
], X_AXIS
);
365 dynamic_cast<Paper_column
*> (c
[j
])->set_system (system
);
366 /* collect the column labels */
367 collect_labels (c
[j
], &system_labels
);
370 Collect labels from any loose columns too: theses will be set on
371 an empty bar line or a column which is otherwise unused mid-line
373 vector
<Grob
*> loose (breaking
[i
].loose_cols_
);
374 for (vsize j
= 0; j
< loose
.size (); j
++)
375 collect_labels (loose
[j
], &system_labels
);
377 system
->set_property ("labels", system_labels
);
379 set_loose_columns (system
, &breaking
[i
]);
380 broken_intos_
.push_back (system
);
385 System::collect_labels (Grob
const *col
, SCM
*labels
)
387 SCM col_labels
= col
->get_property ("labels");
388 if (scm_is_pair (col_labels
))
389 *labels
= scm_append (scm_list_2 (col_labels
, *labels
));
393 System::add_column (Paper_column
*p
)
396 Grob_array
*ga
= unsmob_grob_array (me
->get_object ("columns"));
399 SCM scm_ga
= Grob_array::make_array ();
400 me
->set_object ("columns", scm_ga
);
401 ga
= unsmob_grob_array (scm_ga
);
404 p
->set_rank (ga
->size ());
407 Axis_group_interface::add_element (this, p
);
411 System::pre_processing ()
413 for (vsize i
= 0; i
< all_elements_
->size (); i
++)
414 all_elements_
->grob (i
)->discretionary_processing ();
416 if (be_verbose_global
)
417 message (_f ("Grob count %d", element_count ()));
420 order is significant: broken grobs are added to the end of the
421 array, and should be processed before the original is potentially
424 for (vsize i
= all_elements_
->size (); i
--;)
425 all_elements_
->grob (i
)->handle_prebroken_dependencies ();
427 fixup_refpoints (all_elements_
->array ());
429 for (vsize i
= 0; i
< all_elements_
->size (); i
++)
431 Grob
*g
= all_elements_
->grob (i
);
432 (void) g
->get_property ("before-line-breaking");
435 for (vsize i
= 0; i
< all_elements_
->size (); i
++)
437 Grob
*e
= all_elements_
->grob (i
);
438 (void) e
->get_property ("springs-and-rods");
443 System::post_processing ()
445 Interval
iv (extent (this, Y_AXIS
));
447 programming_error ("system with empty extent");
449 translate_axis (-iv
[MAX
], Y_AXIS
);
451 /* Generate all stencils to trigger font loads.
452 This might seem inefficient, but Stencils are cached per grob
455 vector
<Grob
*> all_elts_sorted (all_elements_
->array ());
456 vector_sort (all_elts_sorted
, std::less
<Grob
*> ());
457 uniq (all_elts_sorted
);
458 this->get_stencil ();
459 for (vsize i
= all_elts_sorted
.size (); i
--;)
461 Grob
*g
= all_elts_sorted
[i
];
473 operator< (Layer_entry
const &a
,
474 Layer_entry
const &b
)
476 return a
.layer_
< b
.layer_
;
480 System::get_paper_system ()
487 vector
<Layer_entry
> entries
;
488 for (vsize j
= 0; j
< all_elements_
->size (); j
++)
491 e
.grob_
= all_elements_
->grob (j
);
492 e
.layer_
= robust_scm2int (e
.grob_
->get_property ("layer"), 1);
494 entries
.push_back (e
);
497 vector_sort (entries
, std::less
<Layer_entry
> ());
498 for (vsize j
= 0; j
< entries
.size (); j
++)
500 Grob
*g
= entries
[j
].grob_
;
501 Stencil st
= g
->get_print_stencil ();
503 if (st
.expr () == SCM_EOL
)
507 for (int a
= X_AXIS
; a
< NO_AXES
; a
++)
508 o
[Axis (a
)] = g
->relative_coordinate (this, Axis (a
));
510 Offset extra
= robust_scm2offset (g
->get_property ("extra-offset"),
512 * Staff_symbol_referencer::staff_space (g
);
514 /* Must copy the stencil, for we cannot change the stencil
517 st
.translate (o
+ extra
);
519 *tail
= scm_cons (st
.expr (), SCM_EOL
);
520 tail
= SCM_CDRLOC (*tail
);
523 if (Stencil
*me
= get_stencil ())
524 exprs
= scm_cons (me
->expr (), exprs
);
526 Interval
x (extent (this, X_AXIS
));
527 Interval
y (extent (this, Y_AXIS
));
528 Stencil
sys_stencil (Box (x
, y
),
529 scm_cons (ly_symbol2scm ("combine-stencil"),
533 Skyline_pair
*skylines
= Skyline_pair::unsmob (get_property ("vertical-skylines"));
537 = Lookup::points_to_line_stencil (0.1, (*skylines
)[UP
].to_points (X_AXIS
));
539 = Lookup::points_to_line_stencil (0.1, (*skylines
)[DOWN
].to_points (X_AXIS
));
540 sys_stencil
.add_stencil (up
.in_color (255, 0, 0));
541 sys_stencil
.add_stencil (down
.in_color (0, 255, 0));
545 Grob
*left_bound
= this->get_bound (LEFT
);
546 SCM prop_init
= left_bound
->get_property ("line-break-system-details");
547 Prob
*pl
= make_paper_system (prop_init
);
548 paper_system_set_stencil (pl
, sys_stencil
);
550 /* information that the page breaker might need */
551 Grob
*right_bound
= this->get_bound (RIGHT
);
552 pl
->set_property ("vertical-skylines", this->get_property ("vertical-skylines"));
553 pl
->set_property ("page-break-permission", right_bound
->get_property ("page-break-permission"));
554 pl
->set_property ("page-turn-permission", right_bound
->get_property ("page-turn-permission"));
555 pl
->set_property ("page-break-penalty", right_bound
->get_property ("page-break-penalty"));
556 pl
->set_property ("page-turn-penalty", right_bound
->get_property ("page-turn-penalty"));
558 Interval staff_refpoints
;
559 if (Grob
*align
= get_vertical_alignment ())
561 extract_grob_set (align
, "elements", staves
);
562 for (vsize i
= 0; i
< staves
.size (); i
++)
563 if (staves
[i
]->is_live ()
564 && Page_layout_problem::is_spaceable (staves
[i
]))
565 staff_refpoints
.add_point (staves
[i
]->relative_coordinate (this,
569 pl
->set_property ("staff-refpoint-extent", ly_interval2scm (staff_refpoints
));
570 pl
->set_property ("system-grob", this->self_scm ());
572 return pl
->unprotect ();
576 System::broken_col_range (Item
const *left
, Item
const *right
) const
580 left
= left
->get_column ();
581 right
= right
->get_column ();
583 extract_grob_set (this, "columns", cols
);
585 vsize i
= Paper_column::get_rank (left
);
586 int end_rank
= Paper_column::get_rank (right
);
587 if (i
< cols
.size ())
590 while (i
< cols
.size ()
591 && Paper_column::get_rank (cols
[i
]) < end_rank
)
593 Paper_column
*c
= dynamic_cast<Paper_column
*> (cols
[i
]);
594 if (Paper_column::is_breakable (c
) && !c
->get_system ())
602 /** Return all columns, but filter out any unused columns , since they might
603 disrupt the spacing problem. */
605 System::used_columns () const
607 extract_grob_set (this, "columns", ro_columns
);
609 int last_breakable
= ro_columns
.size ();
611 while (last_breakable
--)
613 if (Paper_column::is_breakable (ro_columns
[last_breakable
]))
617 vector
<Grob
*> columns
;
618 for (int i
= 0; i
<= last_breakable
; i
++)
620 if (Paper_column::is_used (ro_columns
[i
]))
621 columns
.push_back (ro_columns
[i
]);
628 System::column (vsize which
) const
630 extract_grob_set (this, "columns", columns
);
631 if (which
>= columns
.size ())
634 return dynamic_cast<Paper_column
*> (columns
[which
]);
638 System::paper_score () const
644 System::get_rank () const
650 get_root_system (Grob
*me
)
652 Grob
*system_grob
= me
;
654 while (system_grob
->get_parent (Y_AXIS
))
655 system_grob
= system_grob
->get_parent (Y_AXIS
);
657 return dynamic_cast<System
*> (system_grob
);
661 System::get_vertical_alignment ()
663 extract_grob_set (this, "elements", elts
);
665 for (vsize i
= 0; i
< elts
.size (); i
++)
666 if (Align_interface::has_interface (elts
[i
]))
669 programming_error ("found multiple vertical alignments in this system");
674 programming_error ("didn't find a vertical alignment in this system");
678 // Finds the furthest staff in the given direction whose x-extent
679 // overlaps with the given interval.
681 System::get_extremal_staff (Direction dir
, Interval
const &iv
)
683 Grob
*align
= get_vertical_alignment ();
687 extract_grob_set (align
, "elements", elts
);
688 vsize start
= (dir
== UP
) ? 0 : elts
.size () - 1;
689 vsize end
= (dir
== UP
) ? elts
.size () : VPOS
;
690 for (vsize i
= start
; i
!= end
; i
+= dir
)
692 if (Hara_kiri_group_spanner::has_interface (elts
[i
]))
693 Hara_kiri_group_spanner::consider_suicide (elts
[i
]);
695 Interval intersection
= elts
[i
]->extent (this, X_AXIS
);
696 intersection
.intersect (iv
);
697 if (elts
[i
]->is_live () && !intersection
.is_empty ())
704 System::pure_refpoint_extent (vsize start
, vsize end
)
707 Grob
*alignment
= get_vertical_alignment ();
711 extract_grob_set (alignment
, "elements", staves
);
712 vector
<Real
> offsets
= Align_interface::get_pure_minimum_translations (alignment
, staves
, Y_AXIS
, start
, end
);
714 for (vsize i
= 0; i
< offsets
.size (); ++i
)
715 if (Page_layout_problem::is_spaceable (staves
[i
]))
717 ret
[UP
] = offsets
[i
];
721 for (vsize i
= offsets
.size (); i
--;)
722 if (Page_layout_problem::is_spaceable (staves
[i
]))
724 ret
[DOWN
] = offsets
[i
];
732 System::part_of_line_pure_height (vsize start
, vsize end
, bool begin
)
734 Grob
*alignment
= get_vertical_alignment ();
738 extract_grob_set (alignment
, "elements", staves
);
739 vector
<Real
> offsets
= Align_interface::get_pure_minimum_translations (alignment
, staves
, Y_AXIS
, start
, end
);
742 for (vsize i
= 0; i
< staves
.size (); ++i
)
745 ? Axis_group_interface::begin_of_line_pure_height (staves
[i
], start
)
746 : Axis_group_interface::rest_of_line_pure_height (staves
[i
], start
, end
);
747 if (i
< offsets
.size ())
748 iv
.translate (offsets
[i
]);
752 Interval other_elements
= begin
753 ? Axis_group_interface::begin_of_line_pure_height (this, start
)
754 : Axis_group_interface::rest_of_line_pure_height (this, start
, end
);
756 ret
.unite (other_elements
);
762 System::begin_of_line_pure_height (vsize start
, vsize end
)
764 return part_of_line_pure_height (start
, end
, true);
768 System::rest_of_line_pure_height (vsize start
, vsize end
)
770 return part_of_line_pure_height (start
, end
, false);
773 // This differs from Axis_group_interface::calc_pure_relevant_grobs
774 // because here, we are only interested in those few elements that aren't
775 // descended from VerticalAlignment (ie. things like RehearsalMark, BarLine).
776 MAKE_SCHEME_CALLBACK (System
, calc_pure_relevant_grobs
, 1);
778 System::calc_pure_relevant_grobs (SCM smob
)
780 Grob
*me
= unsmob_grob (smob
);
782 extract_grob_set (me
, "elements", elts
);
783 vector
<Grob
*> relevant_grobs
;
784 SCM pure_relevant_p
= ly_lily_module_constant ("pure-relevant?");
786 for (vsize i
= 0; i
< elts
.size (); ++i
)
788 if (!Axis_group_interface::has_interface (elts
[i
]))
790 if (to_boolean (scm_apply_1 (pure_relevant_p
, elts
[i
]->self_scm (), SCM_EOL
)))
791 relevant_grobs
.push_back (elts
[i
]);
793 if (Item
*it
= dynamic_cast<Item
*> (elts
[i
]))
798 Item
*piece
= it
->find_prebroken_piece (d
);
799 if (piece
&& to_boolean (scm_apply_1 (pure_relevant_p
, piece
->self_scm (), SCM_EOL
)))
800 relevant_grobs
.push_back (piece
);
802 while (flip (&d
) != LEFT
);
807 SCM grobs_scm
= Grob_array::make_array ();
809 unsmob_grob_array (grobs_scm
)->set_array (relevant_grobs
);
813 MAKE_SCHEME_CALLBACK (System
, height
, 1);
815 System::height (SCM smob
)
817 return Axis_group_interface::height (smob
);
820 MAKE_SCHEME_CALLBACK (System
, calc_pure_height
, 3);
822 System::calc_pure_height (SCM smob
, SCM start_scm
, SCM end_scm
)
824 System
*me
= dynamic_cast<System
*> (unsmob_grob (smob
));
825 int start
= scm_to_int (start_scm
);
826 int end
= scm_to_int (end_scm
);
828 Interval begin
= me
->begin_of_line_pure_height (start
, end
);
829 Interval rest
= me
->rest_of_line_pure_height (start
, end
);
832 return ly_interval2scm (begin
);
836 System::get_pure_bound (Direction d
, int start
, int end
)
838 vector
<vsize
> ranks
= pscore_
->get_break_ranks ();
839 vector
<vsize
> indices
= pscore_
->get_break_indices ();
840 vector
<Grob
*> cols
= pscore_
->get_columns ();
842 vsize target_rank
= (d
== LEFT
? start
: end
);
843 vector
<vsize
>::const_iterator i
=
844 lower_bound (ranks
.begin (), ranks
.end (), target_rank
, std::less
<vsize
> ());
846 if (i
!= ranks
.end () && (*i
) == target_rank
)
847 return cols
[indices
[i
- ranks
.begin ()]];
853 System::get_maybe_pure_bound (Direction d
, bool pure
, int start
, int end
)
855 return pure
? get_pure_bound (d
, start
, end
) : get_bound (d
);
858 ADD_INTERFACE (System
,
859 "This is the top-level object: Each object in a score"
860 " ultimately has a @code{System} object as its X and"
868 "skyline-horizontal-padding "