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 "note-head.hh"
31 #include "output-def.hh"
32 #include "paper-score.hh"
33 #include "pointer-group-interface.hh"
34 #include "rhythmic-head.hh"
35 #include "separation-item.hh"
36 #include "skyline-pair.hh"
37 #include "spaceable-grob.hh"
39 #include "string-convert.hh"
41 #include "text-interface.hh"
45 Paper_column::clone () const
47 return new Paper_column (*this);
51 Paper_column::do_break_processing ()
53 Item::do_break_processing ();
57 Paper_column::get_rank (Grob const *me)
59 return dynamic_cast<Paper_column const *> (me)->rank_;
63 Paper_column::set_rank (int rank)
69 Paper_column::get_system () const
75 Paper_column::set_system (System *s)
81 Paper_column::get_column () const
83 return (Paper_column *) (this);
86 Paper_column::Paper_column (SCM l)
93 Paper_column::Paper_column (Paper_column const &src)
101 Paper_column::compare (Grob *const &a,
104 return sign (dynamic_cast<Paper_column *> (a)->rank_
105 - dynamic_cast<Paper_column *> (b)->rank_);
109 Paper_column::less_than (Grob *const &a,
112 Paper_column *pa = dynamic_cast<Paper_column *> (a);
113 Paper_column *pb = dynamic_cast<Paper_column *> (b);
115 return pa->rank_ < pb->rank_;
119 Paper_column::when_mom (Grob *me)
121 SCM m = me->get_property ("when");
122 if (Moment *when = unsmob_moment (m))
128 Paper_column::is_musical (Grob *me)
130 SCM m = me->get_property ("shortest-starter-duration");
132 if (unsmob_moment (m))
133 s = *unsmob_moment (m);
134 return s != Moment (0);
138 Paper_column::is_used (Grob *me)
140 extract_grob_set (me, "elements", elts);
144 extract_grob_set (me, "bounded-by-me", bbm);
148 if (Paper_column::is_breakable (me))
151 if (to_boolean (me->get_property ("used")))
154 if (scm_is_pair (me->get_property ("labels")))
161 Paper_column::is_breakable (Grob *me)
163 return scm_is_symbol (me->get_property ("line-break-permission"));
167 Paper_column::minimum_distance (Grob *left, Grob *right)
169 Drul_array<Grob *> cols (left, right);
170 Drul_array<Skyline> skys = Drul_array<Skyline> (Skyline (RIGHT), Skyline (LEFT));
172 for (LEFT_and_RIGHT (d))
174 Skyline_pair *sp = Skyline_pair::unsmob (cols[d]->get_property ("horizontal-skylines"));
179 skys[RIGHT].merge (Separation_item::conditional_skyline (right, left));
181 return max (0.0, skys[LEFT].distance (skys[RIGHT]));
185 Paper_column::break_align_width (Grob *me, SCM align_sym)
187 Grob *p = me->get_parent (X_AXIS);
191 me->programming_error ("tried to get break-align-width of a musical column");
192 return Interval (0, 0) + me->relative_coordinate (p, X_AXIS);
196 if (align_sym == ly_symbol2scm ("staff-bar")
197 || align_sym == ly_symbol2scm ("break-alignment"))
199 = Pointer_group_interface::find_grob (me, ly_symbol2scm ("elements"),
200 (align_sym == ly_symbol2scm ("staff-bar")
201 ? Bar_line::non_empty_barline
202 : Break_alignment_interface::has_interface));
205 extract_grob_set (me, "elements", elts);
206 for (vsize i = 0; i < elts.size (); i++)
208 if (elts[i]->get_property ("break-align-symbol") == align_sym)
217 return Interval (0, 0) + me->relative_coordinate (p, X_AXIS);
219 return align->extent (p, X_AXIS);
224 Print a vertical line and the rank number, to aid debugging.
226 MAKE_SCHEME_CALLBACK (Paper_column, print, 1);
228 Paper_column::print (SCM p)
230 Paper_column *me = dynamic_cast<Paper_column *> (unsmob_grob (p));
232 string r = to_string (Paper_column::get_rank (me));
234 Moment *mom = unsmob_moment (me->get_property ("when"));
235 string when = mom ? mom->to_string () : "?/?";
237 Font_metric *musfont = Font_interface::get_default_font (me);
238 SCM properties = Font_interface::text_font_alist_chain (me);
240 SCM scm_mol = Text_interface::interpret_markup (me->layout ()->self_scm (),
243 SCM when_mol = Text_interface::interpret_markup (me->layout ()->self_scm (),
245 ly_string2scm (when));
246 Stencil t = *unsmob_stencil (scm_mol);
247 t.add_at_edge (Y_AXIS, DOWN, *unsmob_stencil (when_mol), 0.1);
248 t.align_to (X_AXIS, CENTER);
249 t.align_to (Y_AXIS, DOWN);
251 Stencil l = Lookup::filled_box (Box (Interval (-0.01, 0.01),
254 SCM small_letters = scm_cons (scm_acons (ly_symbol2scm ("font-size"),
255 scm_from_int (-6), SCM_EOL),
259 for (SCM s = me->get_object ("ideal-distances");
260 scm_is_pair (s); s = scm_cdr (s))
262 Spring *sp = unsmob_spring (scm_caar (s));
263 if (!unsmob_grob (scm_cdar (s))
264 || !unsmob_grob (scm_cdar (s))->get_system ())
270 pts.push_back (Offset (0, y));
272 Offset p2 (sp->distance (), y);
275 Stencil id_stencil = Lookup::points_to_line_stencil (0.1, pts);
276 Stencil head (musfont->find_by_name ("arrowheads.open.01"));
278 SCM distance_stc = Text_interface::interpret_markup (me->layout ()->self_scm (),
280 ly_string2scm (String_convert::form_string ("%5.2lf", sp->distance ())));
282 id_stencil.add_stencil (unsmob_stencil (distance_stc)->translated (Offset (sp->distance () / 3, y + 1)));
283 id_stencil.add_stencil (head.translated (p2));
284 id_stencil = id_stencil.in_color (0, 0, 1);
285 l.add_stencil (id_stencil);
288 for (SCM s = me->get_object ("minimum-distances");
289 scm_is_pair (s); s = scm_cdr (s))
291 Real dist = scm_to_double (scm_cdar (s));
292 Grob *other = unsmob_grob (scm_caar (s));
293 if (!other || other->get_system () != me->get_system ())
298 Real y = -j * 1.0 - 3.5;
300 pts.push_back (Offset (0, y));
305 Stencil id_stencil = Lookup::points_to_line_stencil (0.1, pts);
306 Stencil head (musfont->find_by_name ("arrowheads.open.0M1"));
307 head.translate_axis (y, Y_AXIS);
308 id_stencil.add_stencil (head);
310 SCM distance_stc = Text_interface::interpret_markup (me->layout ()->self_scm (),
312 ly_string2scm (String_convert::form_string ("%5.2lf",
315 id_stencil.add_stencil (unsmob_stencil (distance_stc)->translated (Offset (dist / 3, y - 1)));
317 id_stencil = id_stencil.in_color (1, 0, 0);
318 l.add_stencil (id_stencil);
321 return t.smobbed_copy ();
325 This is all too hairy. We use bounded-by-me to make sure that some
326 columns are kept "alive". Unfortunately, when spanners are suicided,
327 this falls apart again, because suicided spanners are still in
330 THIS IS BROKEN KLUDGE. WE SHOULD INVENT SOMETHING BETTER.
332 MAKE_SCHEME_CALLBACK (Paper_column, before_line_breaking, 1);
334 Paper_column::before_line_breaking (SCM grob)
336 Grob *me = unsmob_grob (grob);
338 SCM bbm = me->get_object ("bounded-by-me");
339 Grob_array *ga = unsmob_grob_array (bbm);
341 return SCM_UNSPECIFIED;
343 vector<Grob *> &array (ga->array_reference ());
345 for (vsize i = array.size (); i--;)
349 if (!g || !g->is_live ())
350 /* UGH . potentially quadratic. */
351 array.erase (array.begin () + i);
354 return SCM_UNSPECIFIED;
357 /* FIXME: This is a hack that we use to identify columns that used to
358 contain note-heads but whose note-heads were moved by one of the ligature
359 engravers. Once the ligature engravers are fixed to behave nicely, this
360 function can be removed.
363 Paper_column::is_extraneous_column_from_ligature (Grob *me)
365 if (!is_musical (me))
368 // If all the note-heads that I think are my children actually belong
369 // to another column, then I am extraneous.
370 extract_grob_set (me, "elements", elts);
371 bool has_notehead = false;
372 for (vsize i = 0; i < elts.size (); i++)
374 if (Rhythmic_head::has_interface (elts[i]))
377 if (dynamic_cast<Item *> (elts[i])->get_column () == me)
384 ADD_INTERFACE (Paper_column,
385 "@code{Paper_column} objects form the top-most X@tie{}parents"
386 " for items. There are two types of columns: musical and"
387 " non-musical, to which musical and non-musical objects are"
388 " attached respectively. The spacing engine determines the"
389 " X@tie{}positions of these objects.\n"
391 "They are numbered, the first (leftmost) is column@tie{}0."
392 " Numbering happens before line breaking, and columns are not"
393 " renumbered after line breaking. Since many columns go"
394 " unused, you should only use the rank field to get ordering"
395 " information. Two adjacent columns may have non-adjacent"
401 "full-measure-extra-space "
404 "line-break-system-details "
405 "line-break-penalty "
406 "line-break-permission "
408 "page-break-penalty "
409 "page-break-permission "
411 "page-turn-permission "
413 "shortest-playing-duration "
414 "shortest-starter-duration "