2 This file is part of LilyPond, the GNU music typesetter.
4 Copyright (C) 1997--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/>.
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_loose (Grob *g)
129 return (scm_is_pair (g->get_object ("between-cols")));
133 Paper_column::is_musical (Grob *me)
135 SCM m = me->get_property ("shortest-starter-duration");
137 if (unsmob_moment (m))
138 s = *unsmob_moment (m);
139 return s != Moment (0);
143 Paper_column::is_used (Grob *me)
145 extract_grob_set (me, "elements", elts);
149 extract_grob_set (me, "bounded-by-me", bbm);
153 if (Paper_column::is_breakable (me))
156 if (to_boolean (me->get_property ("used")))
159 if (scm_is_pair (me->get_property ("labels")))
166 Paper_column::is_breakable (Grob *me)
168 return scm_is_symbol (me->get_property ("line-break-permission"));
172 Paper_column::minimum_distance (Grob *left, Grob *right)
174 Drul_array<Grob *> cols (left, right);
175 Drul_array<Skyline> skys = Drul_array<Skyline> (Skyline (RIGHT), Skyline (LEFT));
180 Skyline_pair *sp = Skyline_pair::unsmob (cols[d]->get_property ("horizontal-skylines"));
184 while (flip (&d) != LEFT);
186 skys[RIGHT].merge (Separation_item::conditional_skyline (right, left));
188 return max (0.0, skys[LEFT].distance (skys[RIGHT]));
192 Paper_column::break_align_width (Grob *me, SCM align_sym)
194 Grob *p = me->get_parent (X_AXIS);
198 me->programming_error ("tried to get break-align-width of a musical column");
199 return Interval (0, 0) + me->relative_coordinate (p, X_AXIS);
203 if (align_sym == ly_symbol2scm ("staff-bar")
204 || align_sym == ly_symbol2scm ("break-alignment"))
206 = Pointer_group_interface::find_grob (me, ly_symbol2scm ("elements"),
207 (align_sym == ly_symbol2scm ("staff-bar")
208 ? Bar_line::non_empty_barline
209 : Break_alignment_interface::has_interface));
212 extract_grob_set (me, "elements", elts);
213 for (vsize i = 0; i < elts.size (); i++)
215 if (elts[i]->get_property ("break-align-symbol") == align_sym)
224 return Interval (0, 0) + me->relative_coordinate (p, X_AXIS);
226 return align->extent (p, X_AXIS);
230 Print a vertical line and the rank number, to aid debugging.
232 MAKE_SCHEME_CALLBACK (Paper_column, print, 1);
234 Paper_column::print (SCM p)
236 Paper_column *me = dynamic_cast<Paper_column *> (unsmob_grob (p));
238 string r = to_string (Paper_column::get_rank (me));
240 Moment *mom = unsmob_moment (me->get_property ("when"));
241 string when = mom ? mom->to_string () : "?/?";
243 Font_metric *musfont = Font_interface::get_default_font (me);
244 SCM properties = Font_interface::text_font_alist_chain (me);
246 SCM scm_mol = Text_interface::interpret_markup (me->layout ()->self_scm (),
249 SCM when_mol = Text_interface::interpret_markup (me->layout ()->self_scm (),
251 ly_string2scm (when));
252 Stencil t = *unsmob_stencil (scm_mol);
253 t.add_at_edge (Y_AXIS, DOWN, *unsmob_stencil (when_mol), 0.1);
254 t.align_to (X_AXIS, CENTER);
255 t.align_to (Y_AXIS, DOWN);
257 Stencil l = Lookup::filled_box (Box (Interval (-0.01, 0.01),
260 SCM small_letters = scm_cons (scm_acons (ly_symbol2scm ("font-size"),
261 scm_from_int (-6), SCM_EOL),
265 for (SCM s = me->get_object ("ideal-distances");
266 scm_is_pair (s); s = scm_cdr (s))
268 Spring *sp = unsmob_spring (scm_caar (s));
269 if (!unsmob_grob (scm_cdar (s))
270 || !unsmob_grob (scm_cdar (s))->get_system ())
276 pts.push_back (Offset (0, y));
278 Offset p2 (sp->distance (), y);
281 Stencil id_stencil = Lookup::points_to_line_stencil (0.1, pts);
282 Stencil head (musfont->find_by_name ("arrowheads.open.01"));
284 SCM distance_stc = Text_interface::interpret_markup (me->layout ()->self_scm (),
286 ly_string2scm (String_convert::form_string ("%5.2lf", sp->distance ())));
288 id_stencil.add_stencil (unsmob_stencil (distance_stc)->translated (Offset (sp->distance () / 3, y + 1)));
289 id_stencil.add_stencil (head.translated (p2));
290 id_stencil = id_stencil.in_color (0, 0, 1);
291 l.add_stencil (id_stencil);
294 for (SCM s = me->get_object ("minimum-distances");
295 scm_is_pair (s); s = scm_cdr (s))
297 Real dist = scm_to_double (scm_cdar (s));
298 Grob *other = unsmob_grob (scm_caar (s));
299 if (!other || other->get_system () != me->get_system ())
304 Real y = -j * 1.0 - 3.5;
306 pts.push_back (Offset (0, y));
311 Stencil id_stencil = Lookup::points_to_line_stencil (0.1, pts);
312 Stencil head (musfont->find_by_name ("arrowheads.open.0M1"));
313 head.translate_axis (y, Y_AXIS);
314 id_stencil.add_stencil (head);
316 SCM distance_stc = Text_interface::interpret_markup (me->layout ()->self_scm (),
318 ly_string2scm (String_convert::form_string ("%5.2lf",
321 id_stencil.add_stencil (unsmob_stencil (distance_stc)->translated (Offset (dist / 3, y - 1)));
323 id_stencil = id_stencil.in_color (1, 0, 0);
324 l.add_stencil (id_stencil);
327 return t.smobbed_copy ();
331 This is all too hairy. We use bounded-by-me to make sure that some
332 columns are kept "alive". Unfortunately, when spanners are suicided,
333 this falls apart again, because suicided spanners are still in
336 THIS IS BROKEN KLUDGE. WE SHOULD INVENT SOMETHING BETTER.
338 MAKE_SCHEME_CALLBACK (Paper_column, before_line_breaking, 1);
340 Paper_column::before_line_breaking (SCM grob)
342 Grob *me = unsmob_grob (grob);
344 SCM bbm = me->get_object ("bounded-by-me");
345 Grob_array *ga = unsmob_grob_array (bbm);
347 return SCM_UNSPECIFIED;
349 vector<Grob *> &array (ga->array_reference ());
351 for (vsize i = array.size (); i--;)
355 if (!g || !g->is_live ())
356 /* UGH . potentially quadratic. */
357 array.erase (array.begin () + i);
360 return SCM_UNSPECIFIED;
363 /* FIXME: This is a hack that we use to identify columns that used to
364 contain note-heads but whose note-heads were moved by one of the ligature
365 engravers. Once the ligature engravers are fixed to behave nicely, this
366 function can be removed.
369 Paper_column::is_extraneous_column_from_ligature (Grob *me)
371 if (!is_musical (me))
374 // If all the note-heads that I think are my children actually belong
375 // to another column, then I am extraneous.
376 extract_grob_set (me, "elements", elts);
377 bool has_notehead = false;
378 for (vsize i = 0; i < elts.size (); i++)
380 if (Rhythmic_head::has_interface (elts[i]))
383 if (dynamic_cast<Item *> (elts[i])->get_column () == me)
390 ADD_INTERFACE (Paper_column,
391 "@code{Paper_column} objects form the top-most X@tie{}parents"
392 " for items. There are two types of columns: musical and"
393 " non-musical, to which musical and non-musical objects are"
394 " attached respectively. The spacing engine determines the"
395 " X@tie{}positions of these objects.\n"
397 "They are numbered, the first (leftmost) is column@tie{}0."
398 " Numbering happens before line breaking, and columns are not"
399 " renumbered after line breaking. Since many columns go"
400 " unused, you should only use the rank field to get ordering"
401 " information. Two adjacent columns may have non-adjacent"
407 "full-measure-extra-space "
410 "line-break-system-details "
411 "line-break-penalty "
412 "line-break-permission "
414 "page-break-penalty "
415 "page-break-permission "
417 "page-turn-permission "
419 "shortest-playing-duration "
420 "shortest-starter-duration "