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);
222 Print a vertical line and the rank number, to aid debugging.
224 MAKE_SCHEME_CALLBACK (Paper_column, print, 1);
226 Paper_column::print (SCM p)
228 Paper_column *me = dynamic_cast<Paper_column *> (unsmob_grob (p));
230 string r = to_string (Paper_column::get_rank (me));
232 Moment *mom = unsmob_moment (me->get_property ("when"));
233 string when = mom ? mom->to_string () : "?/?";
235 Font_metric *musfont = Font_interface::get_default_font (me);
236 SCM properties = Font_interface::text_font_alist_chain (me);
238 SCM scm_mol = Text_interface::interpret_markup (me->layout ()->self_scm (),
241 SCM when_mol = Text_interface::interpret_markup (me->layout ()->self_scm (),
243 ly_string2scm (when));
244 Stencil t = *unsmob_stencil (scm_mol);
245 t.add_at_edge (Y_AXIS, DOWN, *unsmob_stencil (when_mol), 0.1);
246 t.align_to (X_AXIS, CENTER);
247 t.align_to (Y_AXIS, DOWN);
249 Stencil l = Lookup::filled_box (Box (Interval (-0.01, 0.01),
252 SCM small_letters = scm_cons (scm_acons (ly_symbol2scm ("font-size"),
253 scm_from_int (-6), SCM_EOL),
257 for (SCM s = me->get_object ("ideal-distances");
258 scm_is_pair (s); s = scm_cdr (s))
260 Spring *sp = unsmob_spring (scm_caar (s));
261 if (!unsmob_grob (scm_cdar (s))
262 || !unsmob_grob (scm_cdar (s))->get_system ())
268 pts.push_back (Offset (0, y));
270 Offset p2 (sp->distance (), y);
273 Stencil id_stencil = Lookup::points_to_line_stencil (0.1, pts);
274 Stencil head (musfont->find_by_name ("arrowheads.open.01"));
276 SCM distance_stc = Text_interface::interpret_markup (me->layout ()->self_scm (),
278 ly_string2scm (String_convert::form_string ("%5.2lf", sp->distance ())));
280 id_stencil.add_stencil (unsmob_stencil (distance_stc)->translated (Offset (sp->distance () / 3, y + 1)));
281 id_stencil.add_stencil (head.translated (p2));
282 id_stencil = id_stencil.in_color (0, 0, 1);
283 l.add_stencil (id_stencil);
286 for (SCM s = me->get_object ("minimum-distances");
287 scm_is_pair (s); s = scm_cdr (s))
289 Real dist = scm_to_double (scm_cdar (s));
290 Grob *other = unsmob_grob (scm_caar (s));
291 if (!other || other->get_system () != me->get_system ())
296 Real y = -j * 1.0 - 3.5;
298 pts.push_back (Offset (0, y));
303 Stencil id_stencil = Lookup::points_to_line_stencil (0.1, pts);
304 Stencil head (musfont->find_by_name ("arrowheads.open.0M1"));
305 head.translate_axis (y, Y_AXIS);
306 id_stencil.add_stencil (head);
308 SCM distance_stc = Text_interface::interpret_markup (me->layout ()->self_scm (),
310 ly_string2scm (String_convert::form_string ("%5.2lf",
313 id_stencil.add_stencil (unsmob_stencil (distance_stc)->translated (Offset (dist / 3, y - 1)));
315 id_stencil = id_stencil.in_color (1, 0, 0);
316 l.add_stencil (id_stencil);
319 return t.smobbed_copy ();
323 This is all too hairy. We use bounded-by-me to make sure that some
324 columns are kept "alive". Unfortunately, when spanners are suicided,
325 this falls apart again, because suicided spanners are still in
328 THIS IS BROKEN KLUDGE. WE SHOULD INVENT SOMETHING BETTER.
330 MAKE_SCHEME_CALLBACK (Paper_column, before_line_breaking, 1);
332 Paper_column::before_line_breaking (SCM grob)
334 Grob *me = unsmob_grob (grob);
336 SCM bbm = me->get_object ("bounded-by-me");
337 Grob_array *ga = unsmob_grob_array (bbm);
339 return SCM_UNSPECIFIED;
341 vector<Grob *> &array (ga->array_reference ());
343 for (vsize i = array.size (); i--;)
347 if (!g || !g->is_live ())
348 /* UGH . potentially quadratic. */
349 array.erase (array.begin () + i);
352 return SCM_UNSPECIFIED;
355 /* FIXME: This is a hack that we use to identify columns that used to
356 contain note-heads but whose note-heads were moved by one of the ligature
357 engravers. Once the ligature engravers are fixed to behave nicely, this
358 function can be removed.
361 Paper_column::is_extraneous_column_from_ligature (Grob *me)
363 if (!is_musical (me))
366 // If all the note-heads that I think are my children actually belong
367 // to another column, then I am extraneous.
368 extract_grob_set (me, "elements", elts);
369 bool has_notehead = false;
370 for (vsize i = 0; i < elts.size (); i++)
372 if (Rhythmic_head::has_interface (elts[i]))
375 if (dynamic_cast<Item *> (elts[i])->get_column () == me)
382 ADD_INTERFACE (Paper_column,
383 "@code{Paper_column} objects form the top-most X@tie{}parents"
384 " for items. There are two types of columns: musical and"
385 " non-musical, to which musical and non-musical objects are"
386 " attached respectively. The spacing engine determines the"
387 " X@tie{}positions of these objects.\n"
389 "They are numbered, the first (leftmost) is column@tie{}0."
390 " Numbering happens before line breaking, and columns are not"
391 " renumbered after line breaking. Since many columns go"
392 " unused, you should only use the rank field to get ordering"
393 " information. Two adjacent columns may have non-adjacent"
399 "full-measure-extra-space "
402 "line-break-system-details "
403 "line-break-penalty "
404 "line-break-permission "
406 "page-break-penalty "
407 "page-break-permission "
409 "page-turn-permission "
411 "shortest-playing-duration "
412 "shortest-starter-duration "