2 This file is part of LilyPond, the GNU music typesetter.
4 Copyright (C) 1997--2012 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);
226 - blue arrow representing ideal distance,
227 - red arrow representing minimum distance
228 to aid debugging. To turn this on, simply add
229 \override Score.PaperColumn #'stencil = #ly:paper-column::print
230 \override Score.NonMusicalPaperColumn #'stencil = #ly:paper-column::print
232 Also, as of 2013-10-16 there's a switch in Frescobaldi that turns this on.
234 MAKE_SCHEME_CALLBACK (Paper_column, print, 1);
236 Paper_column::print (SCM p)
238 Paper_column *me = dynamic_cast<Paper_column *> (unsmob_grob (p));
240 string r = to_string (Paper_column::get_rank (me));
242 Moment *mom = unsmob_moment (me->get_property ("when"));
243 string when = mom ? mom->to_string () : "?/?";
245 Font_metric *musfont = Font_interface::get_default_font (me);
246 SCM properties = Font_interface::text_font_alist_chain (me);
247 SCM scm_mol = Text_interface::interpret_markup (me->layout ()->self_scm (),
250 SCM when_mol = Text_interface::interpret_markup (me->layout ()->self_scm (),
252 ly_string2scm (when));
253 Stencil t = *unsmob_stencil (scm_mol);
255 t.add_at_edge (Y_AXIS, DOWN, *unsmob_stencil (when_mol), 0.1);
256 t.align_to (X_AXIS, LEFT);
257 // compensate for font serifs and half letter-distance
258 t.translate (Offset (-0.1, 0));
259 t.align_to (Y_AXIS, DOWN);
261 Stencil l = Lookup::filled_box (Box (Interval (0, 0.02),
264 Real small_pad = 0.15;
267 // number of printed arrows from *both* loops
270 for (SCM s = me->get_object ("ideal-distances");
271 scm_is_pair (s); s = scm_cdr (s))
273 Spring *sp = unsmob_spring (scm_caar (s));
274 if (!unsmob_grob (scm_cdar (s))
275 || !unsmob_grob (scm_cdar (s))->get_system ())
280 Stencil arrowhead (musfont->find_by_name ("arrowheads.open.01"));
281 // initial scaling; it will also scale with font-size.
282 arrowhead.scale (1, 1.66);
283 Real head_len = arrowhead.extent (X_AXIS).length ();
285 SCM stil = Text_interface::interpret_markup (me->layout ()->self_scm (),
287 ly_string2scm (String_convert::form_string ("%5.2lf", sp->distance ())));
288 Stencil *number_stc = unsmob_stencil (stil);
289 number_stc->scale (1, 1.1);
290 Real num_height = number_stc->extent (Y_AXIS).length ();
291 Real num_len = number_stc->extent (X_AXIS).length ();
292 number_stc->align_to (Y_AXIS, DOWN);
294 // arrow's y-coord relative to the top of l stencil:
296 y -= j * (num_height + small_pad + big_pad);
297 // horizontally center number on the arrow, excluding arrowhead.
298 Offset num_off = Offset ((sp->distance () - num_len - head_len) / 2,
302 pts.push_back (Offset (0, y));
304 Offset p2 (sp->distance (), y);
307 Stencil id_stencil = Lookup::points_to_line_stencil (0.1, pts);
308 id_stencil.add_stencil (arrowhead.translated (p2));
309 id_stencil.add_stencil (number_stc->translated (num_off));
310 // use a lighter shade of blue so it will remain legible on black background.
311 id_stencil = id_stencil.in_color (0.2, 0.4, 1);
312 l.add_stencil (id_stencil);
315 for (SCM s = me->get_object ("minimum-distances");
316 scm_is_pair (s); s = scm_cdr (s))
318 Real dist = scm_to_double (scm_cdar (s));
319 Grob *other = unsmob_grob (scm_caar (s));
320 if (!other || other->get_system () != me->get_system ())
325 Stencil arrowhead (musfont->find_by_name ("arrowheads.open.01"));
326 // initial scaling; it will also scale with font-size.
327 arrowhead.scale (1, 1.66);
328 Real head_len = arrowhead.extent (X_AXIS).length ();
330 SCM stil = Text_interface::interpret_markup (me->layout ()->self_scm (),
332 ly_string2scm (String_convert::form_string ("%5.2lf", dist)));
333 Stencil *number_stc = unsmob_stencil (stil);
334 number_stc->scale (1, 1.1);
335 Real num_height = number_stc->extent (Y_AXIS).length ();
336 Real num_len = number_stc->extent (X_AXIS).length ();
337 number_stc->align_to (Y_AXIS, UP);
339 // arrow's y-coord relative to the top of l stencil:
341 y -= j * (num_height + small_pad + big_pad);
342 // horizontally center number on the arrow, excluding arrowhead.
343 Offset num_off = Offset ((dist - num_len - head_len) / 2,
347 pts.push_back (Offset (0, y));
352 Stencil id_stencil = Lookup::points_to_line_stencil (0.1, pts);
353 id_stencil.add_stencil (arrowhead.translated (p2));
354 id_stencil.add_stencil (number_stc->translated (num_off));
355 // use a lighter shade of red so it will remain legible on black background.
356 id_stencil = id_stencil.in_color (1, 0.25, 0.25);
357 l.add_stencil (id_stencil);
360 return t.smobbed_copy ();
364 This is all too hairy. We use bounded-by-me to make sure that some
365 columns are kept "alive". Unfortunately, when spanners are suicided,
366 this falls apart again, because suicided spanners are still in
369 THIS IS BROKEN KLUDGE. WE SHOULD INVENT SOMETHING BETTER.
371 MAKE_SCHEME_CALLBACK (Paper_column, before_line_breaking, 1);
373 Paper_column::before_line_breaking (SCM grob)
375 Grob *me = unsmob_grob (grob);
377 SCM bbm = me->get_object ("bounded-by-me");
378 Grob_array *ga = unsmob_grob_array (bbm);
380 return SCM_UNSPECIFIED;
382 vector<Grob *> &array (ga->array_reference ());
384 for (vsize i = array.size (); i--;)
388 if (!g || !g->is_live ())
389 /* UGH . potentially quadratic. */
390 array.erase (array.begin () + i);
393 return SCM_UNSPECIFIED;
396 /* FIXME: This is a hack that we use to identify columns that used to
397 contain note-heads but whose note-heads were moved by one of the ligature
398 engravers. Once the ligature engravers are fixed to behave nicely, this
399 function can be removed.
402 Paper_column::is_extraneous_column_from_ligature (Grob *me)
404 if (!is_musical (me))
407 // If all the note-heads that I think are my children actually belong
408 // to another column, then I am extraneous.
409 extract_grob_set (me, "elements", elts);
410 bool has_notehead = false;
411 for (vsize i = 0; i < elts.size (); i++)
413 if (Rhythmic_head::has_interface (elts[i]))
416 if (dynamic_cast<Item *> (elts[i])->get_column () == me)
423 ADD_INTERFACE (Paper_column,
424 "@code{Paper_column} objects form the top-most X@tie{}parents"
425 " for items. There are two types of columns: musical and"
426 " non-musical, to which musical and non-musical objects are"
427 " attached respectively. The spacing engine determines the"
428 " X@tie{}positions of these objects.\n"
430 "They are numbered, the first (leftmost) is column@tie{}0."
431 " Numbering happens before line breaking, and columns are not"
432 " renumbered after line breaking. Since many columns go"
433 " unused, you should only use the rank field to get ordering"
434 " information. Two adjacent columns may have non-adjacent"
440 "full-measure-extra-space "
443 "line-break-system-details "
444 "line-break-penalty "
445 "line-break-permission "
447 "page-break-penalty "
448 "page-break-permission "
450 "page-turn-permission "
452 "shortest-playing-duration "
453 "shortest-starter-duration "