2 This file is part of LilyPond, the GNU music typesetter.
4 Copyright (C) 1997--2009 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 "break-align-interface.hh"
24 #include "font-interface.hh"
25 #include "grob-array.hh"
29 #include "output-def.hh"
30 #include "paper-score.hh"
31 #include "pointer-group-interface.hh"
32 #include "rhythmic-head.hh"
33 #include "separation-item.hh"
34 #include "skyline-pair.hh"
35 #include "spaceable-grob.hh"
37 #include "string-convert.hh"
39 #include "text-interface.hh"
43 Paper_column::clone () const
45 return new Paper_column (*this);
49 Paper_column::do_break_processing ()
51 Item::do_break_processing ();
55 Paper_column::get_rank (Grob const *me)
57 return dynamic_cast<Paper_column const *> (me)->rank_;
61 Paper_column::get_system () const
67 Paper_column::set_system (System *s)
73 Paper_column::get_column () const
75 return (Paper_column *) (this);
78 Paper_column::Paper_column (SCM l)
85 Paper_column::Paper_column (Paper_column const &src)
93 Paper_column::compare (Grob * const &a,
96 return sign (dynamic_cast<Paper_column*> (a)->rank_
97 - dynamic_cast<Paper_column*> (b)->rank_);
101 Paper_column::less_than (Grob *const &a,
104 Paper_column *pa = dynamic_cast<Paper_column*> (a);
105 Paper_column *pb = dynamic_cast<Paper_column*> (b);
107 return pa->rank_ < pb->rank_;
111 Paper_column::when_mom (Grob *me)
113 SCM m = me->get_property ("when");
114 if (Moment *when = unsmob_moment (m))
120 Paper_column::is_musical (Grob *me)
122 SCM m = me->get_property ("shortest-starter-duration");
124 if (unsmob_moment (m))
125 s = *unsmob_moment (m);
126 return s != Moment (0);
130 Paper_column::is_used (Grob *me)
132 extract_grob_set (me, "elements", elts);
136 extract_grob_set (me, "bounded-by-me", bbm);
140 if (Paper_column::is_breakable (me))
143 if (to_boolean (me->get_property ("used")))
149 Paper_column::is_breakable (Grob *me)
151 return scm_is_symbol (me->get_property ("line-break-permission"));
155 Paper_column::minimum_distance (Grob *left, Grob *right)
157 Drul_array<Grob*> cols (left, right);
158 Drul_array<Skyline> skys = Drul_array<Skyline> (Skyline (RIGHT), Skyline (LEFT));
163 Skyline_pair *sp = Skyline_pair::unsmob (cols[d]->get_property ("horizontal-skylines"));
167 while (flip (&d) != LEFT);
169 skys[RIGHT].merge (Separation_item::conditional_skyline (right, left));
171 return max (0.0, skys[LEFT].distance (skys[RIGHT]));
175 Paper_column::break_align_width (Grob *me)
177 Grob *p = me->get_parent (X_AXIS);
181 me->programming_error ("tried to get break-align-width of a non-musical column");
182 return Interval (0, 0) + me->relative_coordinate (p, X_AXIS);
185 Grob *align = Pointer_group_interface::find_grob (me, ly_symbol2scm ("elements"),
186 Break_alignment_interface::has_interface);
188 return Interval (0, 0) + me->relative_coordinate (p, X_AXIS);
190 return align->extent (p, X_AXIS);
194 Print a vertical line and the rank number, to aid debugging.
196 MAKE_SCHEME_CALLBACK (Paper_column, print, 1);
198 Paper_column::print (SCM p)
200 Paper_column *me = dynamic_cast<Paper_column*> (unsmob_grob (p));
202 string r = to_string (Paper_column::get_rank (me));
204 Moment *mom = unsmob_moment (me->get_property ("when"));
205 string when = mom ? mom->to_string () : "?/?";
207 Font_metric *musfont = Font_interface::get_default_font (me);
208 SCM properties = Font_interface::text_font_alist_chain (me);
210 SCM scm_mol = Text_interface::interpret_markup (me->layout ()->self_scm (),
213 SCM when_mol = Text_interface::interpret_markup (me->layout ()->self_scm (),
215 ly_string2scm (when));
216 Stencil t = *unsmob_stencil (scm_mol);
217 t.add_at_edge (Y_AXIS, DOWN, *unsmob_stencil (when_mol), 0.1);
218 t.align_to (X_AXIS, CENTER);
219 t.align_to (Y_AXIS, DOWN);
221 Stencil l = Lookup::filled_box (Box (Interval (-0.01, 0.01),
224 SCM small_letters = scm_cons (scm_acons (ly_symbol2scm ("font-size"),
225 scm_from_int (-6), SCM_EOL),
229 for (SCM s = me->get_object ("ideal-distances");
230 scm_is_pair (s); s = scm_cdr (s))
232 Spring *sp = unsmob_spring (scm_caar (s));
233 if (!unsmob_grob (scm_cdar (s))
234 || !unsmob_grob (scm_cdar (s))->get_system ())
240 pts.push_back (Offset (0, y));
242 Offset p2 (sp->distance (), y);
245 Stencil id_stencil = Lookup::points_to_line_stencil (0.1, pts);
246 Stencil head (musfont->find_by_name ("arrowheads.open.01"));
248 SCM distance_stc = Text_interface::interpret_markup (me->layout ()->self_scm (),
250 ly_string2scm (String_convert::form_string ("%5.2lf", sp->distance ())));
252 id_stencil.add_stencil (unsmob_stencil (distance_stc)->translated (Offset (sp->distance ()/3, y+1)));
253 id_stencil.add_stencil (head.translated (p2));
254 id_stencil = id_stencil.in_color (0,0,1);
255 l.add_stencil (id_stencil);
258 for (SCM s = me->get_object ("minimum-distances");
259 scm_is_pair (s); s = scm_cdr (s))
261 Real dist = scm_to_double (scm_cdar (s));
262 Grob *other = unsmob_grob (scm_caar (s));
263 if (!other || other->get_system () != me->get_system ())
268 Real y = -j * 1.0 -3.5;
270 pts.push_back (Offset (0, y));
275 Stencil id_stencil = Lookup::points_to_line_stencil (0.1, pts);
276 Stencil head (musfont->find_by_name ("arrowheads.open.0M1"));
277 head.translate_axis (y, Y_AXIS);
278 id_stencil.add_stencil (head);
280 SCM distance_stc = Text_interface::interpret_markup (me->layout ()->self_scm (),
282 ly_string2scm (String_convert::form_string ("%5.2lf",
285 id_stencil.add_stencil (unsmob_stencil (distance_stc)->translated (Offset (dist/3, y-1)));
288 id_stencil = id_stencil.in_color (1,0,0);
289 l.add_stencil (id_stencil);
292 return t.smobbed_copy ();
296 This is all too hairy. We use bounded-by-me to make sure that some
297 columns are kept "alive". Unfortunately, when spanners are suicided,
298 this falls apart again, because suicided spanners are still in
301 THIS IS BROKEN KLUDGE. WE SHOULD INVENT SOMETHING BETTER.
303 MAKE_SCHEME_CALLBACK (Paper_column, before_line_breaking, 1);
305 Paper_column::before_line_breaking (SCM grob)
307 Grob *me = unsmob_grob (grob);
309 SCM bbm = me->get_object ("bounded-by-me");
310 Grob_array *ga = unsmob_grob_array (bbm);
312 return SCM_UNSPECIFIED;
314 vector<Grob*> &array (ga->array_reference ());
316 for (vsize i = array.size (); i--;)
320 if (!g || !g->is_live ())
321 /* UGH . potentially quadratic. */
322 array.erase (array.begin () + i);
325 return SCM_UNSPECIFIED;
328 /* FIXME: This is a hack that we use to identify columns that used to
329 contain note-heads but whose note-heads were moved by one of the ligature
330 engravers. Once the ligature engravers are fixed to behave nicely, this
331 function can be removed.
334 Paper_column::is_extraneous_column_from_ligature (Grob *me)
336 if (!is_musical (me))
339 // If all the note-heads that I think are my children actually belong
340 // to another column, then I am extraneous.
341 extract_grob_set (me, "elements", elts);
342 bool has_notehead = false;
343 for (vsize i = 0; i < elts.size (); i++)
345 if (Rhythmic_head::has_interface (elts[i]))
348 if (dynamic_cast<Item*> (elts[i])->get_column () == me)
356 ADD_INTERFACE (Paper_column,
357 "@code{Paper_column} objects form the top-most X@tie{}parents"
358 " for items. There are two types of columns: musical and"
359 " non-musical, to which musical and non-musical objects are"
360 " attached respectively. The spacing engine determines the"
361 " X@tie{}positions of these objects.\n"
363 "They are numbered, the first (leftmost) is column@tie{}0."
364 " Numbering happens before line breaking, and columns are not"
365 " renumbered after line breaking. Since many columns go"
366 " unused, you should only use the rank field to get ordering"
367 " information. Two adjacent columns may have non-adjacent"
373 "full-measure-extra-space "
376 "line-break-system-details "
377 "line-break-penalty "
378 "line-break-permission "
380 "page-break-penalty "
381 "page-break-permission "
383 "page-turn-permission "
385 "shortest-playing-duration "
386 "shortest-starter-duration "