2 This file is part of LilyPond, the GNU music typesetter.
4 Copyright (C) 1997--2014 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/>.
20 #include "paper-column.hh"
22 #include "axis-group-interface.hh"
23 #include "bar-line.hh"
24 #include "break-align-interface.hh"
25 #include "font-interface.hh"
26 #include "grob-array.hh"
30 #include "output-def.hh"
31 #include "paper-score.hh"
32 #include "pointer-group-interface.hh"
33 #include "rhythmic-head.hh"
34 #include "separation-item.hh"
35 #include "skyline-pair.hh"
36 #include "spaceable-grob.hh"
38 #include "string-convert.hh"
40 #include "text-interface.hh"
44 Paper_column::clone () const
46 return new Paper_column (*this);
50 Paper_column::do_break_processing ()
52 Item::do_break_processing ();
56 Paper_column::get_rank (Grob const *me)
58 return dynamic_cast<Paper_column const *> (me)->rank_;
62 Paper_column::set_rank (int rank)
68 Paper_column::get_system () const
74 Paper_column::set_system (System *s)
80 Paper_column::get_column () const
82 return (Paper_column *) (this);
85 Paper_column::Paper_column (SCM l)
92 Paper_column::Paper_column (Paper_column const &src)
100 Paper_column::compare (Grob *const &a,
103 return sign (dynamic_cast<Paper_column *> (a)->rank_
104 - dynamic_cast<Paper_column *> (b)->rank_);
108 Paper_column::less_than (Grob *const &a,
111 Paper_column *pa = dynamic_cast<Paper_column *> (a);
112 Paper_column *pb = dynamic_cast<Paper_column *> (b);
114 return pa->rank_ < pb->rank_;
118 Paper_column::when_mom (Grob *me)
120 SCM m = me->get_property ("when");
121 if (Moment *when = unsmob_moment (m))
127 Paper_column::is_musical (Grob *me)
129 SCM m = me->get_property ("shortest-starter-duration");
131 if (unsmob_moment (m))
132 s = *unsmob_moment (m);
133 return s != Moment (0);
137 Paper_column::is_used (Grob *me)
139 extract_grob_set (me, "elements", elts);
143 extract_grob_set (me, "bounded-by-me", bbm);
147 if (Paper_column::is_breakable (me))
150 if (to_boolean (me->get_property ("used")))
153 if (scm_is_pair (me->get_property ("labels")))
160 Paper_column::is_breakable (Grob *me)
162 return scm_is_symbol (me->get_property ("line-break-permission"));
166 Paper_column::minimum_distance (Grob *left, Grob *right)
168 Drul_array<Grob *> cols (left, right);
169 Drul_array<Skyline> skys = Drul_array<Skyline> (Skyline (RIGHT), Skyline (LEFT));
171 for (LEFT_and_RIGHT (d))
173 Skyline_pair *sp = Skyline_pair::unsmob (cols[d]->get_property ("horizontal-skylines"));
178 skys[RIGHT].merge (Separation_item::conditional_skyline (right, left));
180 return max (0.0, skys[LEFT].distance (skys[RIGHT]));
184 Paper_column::break_align_width (Grob *me, SCM align_sym)
186 Grob *p = me->get_parent (X_AXIS);
190 me->programming_error ("tried to get break-align-width of a musical column");
191 return Interval (0, 0) + me->relative_coordinate (p, X_AXIS);
195 if (align_sym == ly_symbol2scm ("staff-bar")
196 || align_sym == ly_symbol2scm ("break-alignment"))
198 = Pointer_group_interface::find_grob (me, ly_symbol2scm ("elements"),
199 (align_sym == ly_symbol2scm ("staff-bar")
200 ? Bar_line::non_empty_barline
201 : Break_alignment_interface::has_interface));
204 extract_grob_set (me, "elements", elts);
205 for (vsize i = 0; i < elts.size (); i++)
207 if (elts[i]->get_property ("break-align-symbol") == align_sym)
216 return Interval (0, 0) + me->relative_coordinate (p, X_AXIS);
218 return align->extent (p, X_AXIS);
222 Loop through elements of a PaperColumn, find all grobs implementing specified
223 interface and return their combined extent.
226 Paper_column::get_interface_extent (Grob *column, SCM iface, Axis a)
228 Interval extent = Interval (0, 0);
229 extract_grob_set (column, "elements", elts);
231 for (vsize i = 0; i < elts.size (); i++)
232 if (elts[i]->internal_has_interface (iface))
233 extent.unite (robust_relative_extent (elts[i], elts[i], a));
243 - blue arrow representing ideal distance,
244 - red arrow representing minimum distance
245 to aid debugging. To turn this on, simply add
246 \override Score.PaperColumn #'stencil = #ly:paper-column::print
247 \override Score.NonMusicalPaperColumn #'stencil = #ly:paper-column::print
249 Also, as of 2013-10-16 there's a switch in Frescobaldi that turns this on.
251 MAKE_SCHEME_CALLBACK (Paper_column, print, 1);
253 Paper_column::print (SCM p)
255 Paper_column *me = dynamic_cast<Paper_column *> (unsmob_grob (p));
257 string r = ::to_string (Paper_column::get_rank (me));
259 Moment *mom = unsmob_moment (me->get_property ("when"));
260 string when = mom ? mom->to_string () : "?/?";
262 Font_metric *musfont = Font_interface::get_default_font (me);
263 SCM properties = Font_interface::text_font_alist_chain (me);
264 SCM scm_mol = Text_interface::interpret_markup (me->layout ()->self_scm (),
267 SCM when_mol = Text_interface::interpret_markup (me->layout ()->self_scm (),
269 ly_string2scm (when));
270 Stencil t = *unsmob_stencil (scm_mol);
272 t.add_at_edge (Y_AXIS, DOWN, *unsmob_stencil (when_mol), 0.1);
273 t.align_to (X_AXIS, LEFT);
274 // compensate for font serifs and half letter-distance
275 t.translate (Offset (-0.1, 0));
276 t.align_to (Y_AXIS, DOWN);
278 Stencil l = Lookup::filled_box (Box (Interval (0, 0.02),
281 Real small_pad = 0.15;
284 // number of printed arrows from *both* loops
287 for (SCM s = me->get_object ("ideal-distances");
288 scm_is_pair (s); s = scm_cdr (s))
290 Spring *sp = unsmob_spring (scm_caar (s));
291 if (!unsmob_grob (scm_cdar (s))
292 || !unsmob_grob (scm_cdar (s))->get_system ())
297 Stencil arrowhead (musfont->find_by_name ("arrowheads.open.01"));
298 // initial scaling; it will also scale with font-size.
299 arrowhead.scale (1, 1.66);
300 Real head_len = arrowhead.extent (X_AXIS).length ();
302 SCM stil = Text_interface::interpret_markup (me->layout ()->self_scm (),
304 ly_string2scm (String_convert::form_string ("%5.2lf", sp->distance ())));
305 Stencil *number_stc = unsmob_stencil (stil);
306 number_stc->scale (1, 1.1);
307 Real num_height = number_stc->extent (Y_AXIS).length ();
308 Real num_len = number_stc->extent (X_AXIS).length ();
309 number_stc->align_to (Y_AXIS, DOWN);
311 // arrow's y-coord relative to the top of l stencil:
313 y -= j * (num_height + small_pad + big_pad);
314 // horizontally center number on the arrow, excluding arrowhead.
315 Offset num_off = Offset ((sp->distance () - num_len - head_len) / 2,
319 pts.push_back (Offset (0, y));
321 Offset p2 (sp->distance (), y);
324 Stencil id_stencil = Lookup::points_to_line_stencil (0.1, pts);
325 id_stencil.add_stencil (arrowhead.translated (p2));
326 id_stencil.add_stencil (number_stc->translated (num_off));
327 // use a lighter shade of blue so it will remain legible on black background.
328 id_stencil = id_stencil.in_color (0.2, 0.4, 1);
329 l.add_stencil (id_stencil);
332 for (SCM s = me->get_object ("minimum-distances");
333 scm_is_pair (s); s = scm_cdr (s))
335 Real dist = scm_to_double (scm_cdar (s));
336 Grob *other = unsmob_grob (scm_caar (s));
337 if (!other || other->get_system () != me->get_system ())
342 Stencil arrowhead (musfont->find_by_name ("arrowheads.open.01"));
343 // initial scaling; it will also scale with font-size.
344 arrowhead.scale (1, 1.66);
345 Real head_len = arrowhead.extent (X_AXIS).length ();
347 SCM stil = Text_interface::interpret_markup (me->layout ()->self_scm (),
349 ly_string2scm (String_convert::form_string ("%5.2lf", dist)));
350 Stencil *number_stc = unsmob_stencil (stil);
351 number_stc->scale (1, 1.1);
352 Real num_height = number_stc->extent (Y_AXIS).length ();
353 Real num_len = number_stc->extent (X_AXIS).length ();
354 number_stc->align_to (Y_AXIS, UP);
356 // arrow's y-coord relative to the top of l stencil:
358 y -= j * (num_height + small_pad + big_pad);
359 // horizontally center number on the arrow, excluding arrowhead.
360 Offset num_off = Offset ((dist - num_len - head_len) / 2,
364 pts.push_back (Offset (0, y));
369 Stencil id_stencil = Lookup::points_to_line_stencil (0.1, pts);
370 id_stencil.add_stencil (arrowhead.translated (p2));
371 id_stencil.add_stencil (number_stc->translated (num_off));
372 // use a lighter shade of red so it will remain legible on black background.
373 id_stencil = id_stencil.in_color (1, 0.25, 0.25);
374 l.add_stencil (id_stencil);
377 return t.smobbed_copy ();
381 This is all too hairy. We use bounded-by-me to make sure that some
382 columns are kept "alive". Unfortunately, when spanners are suicided,
383 this falls apart again, because suicided spanners are still in
386 THIS IS BROKEN KLUDGE. WE SHOULD INVENT SOMETHING BETTER.
388 MAKE_SCHEME_CALLBACK (Paper_column, before_line_breaking, 1);
390 Paper_column::before_line_breaking (SCM grob)
392 Grob *me = unsmob_grob (grob);
394 SCM bbm = me->get_object ("bounded-by-me");
395 Grob_array *ga = unsmob_grob_array (bbm);
397 return SCM_UNSPECIFIED;
399 vector<Grob *> &array (ga->array_reference ());
401 for (vsize i = array.size (); i--;)
405 if (!g || !g->is_live ())
406 /* UGH . potentially quadratic. */
407 array.erase (array.begin () + i);
410 return SCM_UNSPECIFIED;
413 /* FIXME: This is a hack that we use to identify columns that used to
414 contain note-heads but whose note-heads were moved by one of the ligature
415 engravers. Once the ligature engravers are fixed to behave nicely, this
416 function can be removed.
419 Paper_column::is_extraneous_column_from_ligature (Grob *me)
421 if (!is_musical (me))
424 // If all the note-heads that I think are my children actually belong
425 // to another column, then I am extraneous.
426 extract_grob_set (me, "elements", elts);
427 bool has_notehead = false;
428 for (vsize i = 0; i < elts.size (); i++)
430 if (Rhythmic_head::has_interface (elts[i]))
433 if (dynamic_cast<Item *> (elts[i])->get_column () == me)
440 ADD_INTERFACE (Paper_column,
441 "@code{Paper_column} objects form the top-most X@tie{}parents"
442 " for items. There are two types of columns: musical and"
443 " non-musical, to which musical and non-musical objects are"
444 " attached respectively. The spacing engine determines the"
445 " X@tie{}positions of these objects.\n"
447 "They are numbered, the first (leftmost) is column@tie{}0."
448 " Numbering happens before line breaking, and columns are not"
449 " renumbered after line breaking. Since many columns go"
450 " unused, you should only use the rank field to get ordering"
451 " information. Two adjacent columns may have non-adjacent"
457 "full-measure-extra-space "
460 "line-break-system-details "
461 "line-break-penalty "
462 "line-break-permission "
464 "page-break-penalty "
465 "page-break-permission "
467 "page-turn-permission "
469 "shortest-playing-duration "
470 "shortest-starter-duration "