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_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")))
156 Paper_column::is_breakable (Grob
*me
)
158 return scm_is_symbol (me
->get_property ("line-break-permission"));
162 Paper_column::minimum_distance (Grob
*left
, Grob
*right
)
164 Drul_array
<Grob
*> cols (left
, right
);
165 Drul_array
<Skyline
> skys
= Drul_array
<Skyline
> (Skyline (RIGHT
), Skyline (LEFT
));
170 Skyline_pair
*sp
= Skyline_pair::unsmob (cols
[d
]->get_property ("horizontal-skylines"));
174 while (flip (&d
) != LEFT
);
176 skys
[RIGHT
].merge (Separation_item::conditional_skyline (right
, left
));
178 return max (0.0, skys
[LEFT
].distance (skys
[RIGHT
]));
182 Paper_column::break_align_width (Grob
*me
, SCM align_sym
)
184 Grob
*p
= me
->get_parent (X_AXIS
);
188 me
->programming_error ("tried to get break-align-width of a musical column");
189 return Interval (0, 0) + me
->relative_coordinate (p
, X_AXIS
);
193 if (align_sym
== ly_symbol2scm ("staff-bar")
194 || align_sym
== ly_symbol2scm ("break-alignment"))
196 = Pointer_group_interface::find_grob (me
, ly_symbol2scm ("elements"),
197 (align_sym
== ly_symbol2scm ("staff-bar")
198 ? Bar_line::non_empty_barline
199 : Break_alignment_interface::has_interface
));
202 extract_grob_set (me
, "elements", elts
);
203 for (vsize i
= 0; i
< elts
.size (); i
++)
205 if (elts
[i
]->get_property ("break-align-symbol") == align_sym
)
214 return Interval (0, 0) + me
->relative_coordinate (p
, X_AXIS
);
216 return align
->extent (p
, X_AXIS
);
220 Print a vertical line and the rank number, to aid debugging.
222 MAKE_SCHEME_CALLBACK (Paper_column
, print
, 1);
224 Paper_column::print (SCM p
)
226 Paper_column
*me
= dynamic_cast<Paper_column
*> (unsmob_grob (p
));
228 string r
= to_string (Paper_column::get_rank (me
));
230 Moment
*mom
= unsmob_moment (me
->get_property ("when"));
231 string when
= mom
? mom
->to_string () : "?/?";
233 Font_metric
*musfont
= Font_interface::get_default_font (me
);
234 SCM properties
= Font_interface::text_font_alist_chain (me
);
236 SCM scm_mol
= Text_interface::interpret_markup (me
->layout ()->self_scm (),
239 SCM when_mol
= Text_interface::interpret_markup (me
->layout ()->self_scm (),
241 ly_string2scm (when
));
242 Stencil t
= *unsmob_stencil (scm_mol
);
243 t
.add_at_edge (Y_AXIS
, DOWN
, *unsmob_stencil (when_mol
), 0.1);
244 t
.align_to (X_AXIS
, CENTER
);
245 t
.align_to (Y_AXIS
, DOWN
);
247 Stencil l
= Lookup::filled_box (Box (Interval (-0.01, 0.01),
250 SCM small_letters
= scm_cons (scm_acons (ly_symbol2scm ("font-size"),
251 scm_from_int (-6), SCM_EOL
),
255 for (SCM s
= me
->get_object ("ideal-distances");
256 scm_is_pair (s
); s
= scm_cdr (s
))
258 Spring
*sp
= unsmob_spring (scm_caar (s
));
259 if (!unsmob_grob (scm_cdar (s
))
260 || !unsmob_grob (scm_cdar (s
))->get_system ())
266 pts
.push_back (Offset (0, y
));
268 Offset
p2 (sp
->distance (), y
);
271 Stencil id_stencil
= Lookup::points_to_line_stencil (0.1, pts
);
272 Stencil
head (musfont
->find_by_name ("arrowheads.open.01"));
274 SCM distance_stc
= Text_interface::interpret_markup (me
->layout ()->self_scm (),
276 ly_string2scm (String_convert::form_string ("%5.2lf", sp
->distance ())));
278 id_stencil
.add_stencil (unsmob_stencil (distance_stc
)->translated (Offset (sp
->distance () / 3, y
+ 1)));
279 id_stencil
.add_stencil (head
.translated (p2
));
280 id_stencil
= id_stencil
.in_color (0,0,1);
281 l
.add_stencil (id_stencil
);
284 for (SCM s
= me
->get_object ("minimum-distances");
285 scm_is_pair (s
); s
= scm_cdr (s
))
287 Real dist
= scm_to_double (scm_cdar (s
));
288 Grob
*other
= unsmob_grob (scm_caar (s
));
289 if (!other
|| other
->get_system () != me
->get_system ())
294 Real y
= -j
* 1.0 -3.5;
296 pts
.push_back (Offset (0, y
));
301 Stencil id_stencil
= Lookup::points_to_line_stencil (0.1, pts
);
302 Stencil
head (musfont
->find_by_name ("arrowheads.open.0M1"));
303 head
.translate_axis (y
, Y_AXIS
);
304 id_stencil
.add_stencil (head
);
306 SCM distance_stc
= Text_interface::interpret_markup (me
->layout ()->self_scm (),
308 ly_string2scm (String_convert::form_string ("%5.2lf",
311 id_stencil
.add_stencil (unsmob_stencil (distance_stc
)->translated (Offset (dist
/ 3, y
- 1)));
313 id_stencil
= id_stencil
.in_color (1,0,0);
314 l
.add_stencil (id_stencil
);
317 return t
.smobbed_copy ();
321 This is all too hairy. We use bounded-by-me to make sure that some
322 columns are kept "alive". Unfortunately, when spanners are suicided,
323 this falls apart again, because suicided spanners are still in
326 THIS IS BROKEN KLUDGE. WE SHOULD INVENT SOMETHING BETTER.
328 MAKE_SCHEME_CALLBACK (Paper_column
, before_line_breaking
, 1);
330 Paper_column::before_line_breaking (SCM grob
)
332 Grob
*me
= unsmob_grob (grob
);
334 SCM bbm
= me
->get_object ("bounded-by-me");
335 Grob_array
*ga
= unsmob_grob_array (bbm
);
337 return SCM_UNSPECIFIED
;
339 vector
<Grob
*> &array (ga
->array_reference ());
341 for (vsize i
= array
.size (); i
--;)
345 if (!g
|| !g
->is_live ())
346 /* UGH . potentially quadratic. */
347 array
.erase (array
.begin () + i
);
350 return SCM_UNSPECIFIED
;
353 /* FIXME: This is a hack that we use to identify columns that used to
354 contain note-heads but whose note-heads were moved by one of the ligature
355 engravers. Once the ligature engravers are fixed to behave nicely, this
356 function can be removed.
359 Paper_column::is_extraneous_column_from_ligature (Grob
*me
)
361 if (!is_musical (me
))
364 // If all the note-heads that I think are my children actually belong
365 // to another column, then I am extraneous.
366 extract_grob_set (me
, "elements", elts
);
367 bool has_notehead
= false;
368 for (vsize i
= 0; i
< elts
.size (); i
++)
370 if (Rhythmic_head::has_interface (elts
[i
]))
373 if (dynamic_cast<Item
*> (elts
[i
])->get_column () == me
)
380 ADD_INTERFACE (Paper_column
,
381 "@code{Paper_column} objects form the top-most X@tie{}parents"
382 " for items. There are two types of columns: musical and"
383 " non-musical, to which musical and non-musical objects are"
384 " attached respectively. The spacing engine determines the"
385 " X@tie{}positions of these objects.\n"
387 "They are numbered, the first (leftmost) is column@tie{}0."
388 " Numbering happens before line breaking, and columns are not"
389 " renumbered after line breaking. Since many columns go"
390 " unused, you should only use the rank field to get ordering"
391 " information. Two adjacent columns may have non-adjacent"
397 "full-measure-extra-space "
400 "line-break-system-details "
401 "line-break-penalty "
402 "line-break-permission "
404 "page-break-penalty "
405 "page-break-permission "
407 "page-turn-permission "
409 "shortest-playing-duration "
410 "shortest-starter-duration "