2 This file is part of LilyPond, the GNU music typesetter.
4 Copyright (C) 1997--2010 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/>.
21 #include "break-align-interface.hh"
23 #include "align-interface.hh"
24 #include "axis-group-interface.hh"
25 #include "dimensions.hh"
26 #include "international.hh"
27 #include "output-def.hh"
28 #include "paper-column.hh"
29 #include "pointer-group-interface.hh"
30 #include "self-alignment-interface.hh"
31 #include "side-position-interface.hh"
37 This is tricky: we cannot modify 'elements, since callers are
38 iterating the same list. Reordering the list in-place, or resetting
39 'elements will skip elements in the loops of callers.
41 So we return the correct order as an array.
44 Break_alignment_interface::break_align_order (Item
*me
)
46 SCM order_vec
= me
->get_property ("break-align-orders");
47 if (!scm_is_vector (order_vec
)
48 || scm_c_vector_length (order_vec
) < 3)
51 SCM order
= scm_vector_ref (order_vec
,
52 scm_from_int (me
->break_status_dir () + 1));
60 Break_alignment_interface::ordered_elements (Grob
*grob
)
62 Item
*me
= dynamic_cast<Item
*> (grob
);
63 extract_grob_set (me
, "elements", elts
);
66 SCM order
= break_align_order (me
);
68 if (order
== SCM_BOOL_F
)
71 vector
<Grob
*> writable_elts (elts
);
73 Copy in order specified in BREAK-ALIGN-ORDER.
75 vector
<Grob
*> new_elts
;
76 for (; scm_is_pair (order
); order
= scm_cdr (order
))
78 SCM sym
= scm_car (order
);
80 for (vsize i
= writable_elts
.size (); i
--;)
82 Grob
*g
= writable_elts
[i
];
83 if (g
&& sym
== g
->get_property ("break-align-symbol"))
85 new_elts
.push_back (g
);
86 writable_elts
.erase (writable_elts
.begin () + i
);
95 Break_alignment_interface::add_element (Grob
*me
, Grob
*toadd
)
97 Align_interface::add_element (me
, toadd
);
100 MAKE_SCHEME_CALLBACK (Break_alignment_interface
, calc_positioning_done
, 1)
102 Break_alignment_interface::calc_positioning_done (SCM smob
)
104 Grob
*grob
= unsmob_grob (smob
);
105 Item
*me
= dynamic_cast<Item
*> (grob
);
108 me
->set_property ("positioning-done", SCM_BOOL_T
);
110 vector
<Grob
*> elems
= ordered_elements (me
);
111 vector
<Interval
> extents
;
113 int last_nonempty
= -1;
114 for (vsize i
= 0; i
< elems
.size (); i
++)
116 Interval y
= elems
[i
]->extent (elems
[i
], X_AXIS
);
117 extents
.push_back (y
);
123 while (idx
< extents
.size () && extents
[idx
].is_empty ())
126 vector
<Real
> offsets
;
127 offsets
.resize (elems
.size ());
128 for (vsize i
= 0; i
< offsets
.size ();i
++)
131 Real extra_right_space
= 0.0;
132 vsize edge_idx
= VPOS
;
133 while (idx
< elems
.size ())
135 vsize next_idx
= idx
+ 1;
136 while (next_idx
< elems
.size ()
137 && extents
[next_idx
].is_empty ())
140 Grob
*l
= elems
[idx
];
143 if (next_idx
< elems
.size ())
149 Find the first grob with a space-alist entry.
151 extract_grob_set (l
, "elements", elts
);
153 for (vsize i
= elts
.size (); i
--;)
158 && (elt
->get_property ("break-align-symbol")
159 == ly_symbol2scm ("left-edge")))
162 SCM l
= elt
->get_property ("space-alist");
170 SCM rsym
= r
? SCM_EOL
: ly_symbol2scm ("right-edge");
173 We used to use #'cause to find out the symbol and the spacing
174 table, but that gets icky when that grob is suicided for some
179 extract_grob_set (r
, "elements", elts
);
180 for (vsize i
= elts
.size ();
181 !scm_is_symbol (rsym
) && i
--;)
184 rsym
= elt
->get_property ("break-align-symbol");
188 if (rsym
== ly_symbol2scm ("left-edge"))
192 if (scm_is_symbol (rsym
))
193 entry
= scm_assq (rsym
, alist
);
195 bool entry_found
= scm_is_pair (entry
);
199 if (scm_is_symbol (rsym
))
200 sym_string
= ly_symbol2string (rsym
);
203 if (unsmob_grob (l
->get_property ("cause")))
204 orig_string
= unsmob_grob (l
->get_property ("cause"))->name ();
206 programming_error (_f ("No spacing entry from %s to `%s'",
207 orig_string
.c_str (),
208 sym_string
.c_str ()));
212 SCM type
= ly_symbol2scm ("extra-space");
216 entry
= scm_cdr (entry
);
218 distance
= scm_to_double (scm_cdr (entry
));
219 type
= scm_car (entry
);
224 if (type
== ly_symbol2scm ("extra-space"))
225 offsets
[next_idx
] = extents
[idx
][RIGHT
] + distance
226 - extents
[next_idx
][LEFT
];
227 /* should probably junk minimum-space */
228 else if (type
== ly_symbol2scm ("minimum-space"))
229 offsets
[next_idx
] = max (extents
[idx
][RIGHT
], distance
);
233 extra_right_space
= distance
;
234 if (idx
+ 1 < offsets
.size ())
235 offsets
[idx
+1] = extents
[idx
][RIGHT
] + distance
;
242 Interval total_extent
;
244 Real alignment_off
= 0.0;
245 for (vsize i
= 0; i
< offsets
.size (); i
++)
249 alignment_off
= -here
;
250 total_extent
.unite (extents
[i
] + here
);
253 if (total_extent
.is_empty ())
256 if (me
->break_status_dir () == LEFT
)
257 alignment_off
= -total_extent
[RIGHT
] - extra_right_space
;
258 else if (edge_idx
== VPOS
)
259 alignment_off
= -total_extent
[LEFT
];
261 here
= alignment_off
;
262 for (vsize i
= 0; i
< offsets
.size (); i
++)
265 elems
[i
]->translate_axis (here
, X_AXIS
);
273 MAKE_SCHEME_CALLBACK (Break_alignable_interface
, self_align_callback
, 1)
275 Break_alignable_interface::self_align_callback (SCM grob
)
277 Grob
*me
= unsmob_grob (grob
);
278 Item
*alignment
= dynamic_cast<Item
*> (me
->get_parent (X_AXIS
));
279 if (!Break_alignment_interface::has_interface (alignment
))
280 return scm_from_int (0);
282 SCM symbol_list
= me
->get_property ("break-align-symbols");
283 vector
<Grob
*> elements
= Break_alignment_interface::ordered_elements (alignment
);
284 if (elements
.size () == 0)
285 return scm_from_int (0);
287 int break_aligned_grob
= -1;
288 for (; scm_is_pair (symbol_list
); symbol_list
= scm_cdr (symbol_list
))
290 SCM sym
= scm_car (symbol_list
);
291 for (vsize i
= 0; i
< elements
.size (); i
++)
293 if (elements
[i
]->get_property ("break-align-symbol") == sym
)
295 if (Item::break_visible (elements
[i
])
296 && !elements
[i
]->extent (elements
[i
], X_AXIS
).is_empty ())
298 break_aligned_grob
= i
;
299 goto found_break_aligned_grob
; /* ugh. need to break out of 2 loops */
301 else if (break_aligned_grob
== -1)
302 break_aligned_grob
= i
;
307 found_break_aligned_grob
:
308 if (break_aligned_grob
== -1)
309 return scm_from_int (0);
311 Grob
*alignment_parent
= elements
[break_aligned_grob
];
312 Grob
*common
= me
->common_refpoint (alignment_parent
, X_AXIS
);
313 Real anchor
= robust_scm2double (alignment_parent
->get_property ("break-align-anchor"), 0);
315 return scm_from_double (alignment_parent
->relative_coordinate (common
, X_AXIS
)
316 - me
->relative_coordinate (common
, X_AXIS
)
320 MAKE_SCHEME_CALLBACK (Break_aligned_interface
, calc_average_anchor
, 1)
322 Break_aligned_interface::calc_average_anchor (SCM grob
)
324 Grob
*me
= unsmob_grob (grob
);
328 /* average the anchors of those children that have it set */
329 extract_grob_set (me
, "elements", elts
);
330 for (vsize i
= 0; i
< elts
.size (); i
++)
332 SCM anchor
= elts
[i
]->get_property ("break-align-anchor");
333 if (scm_is_number (anchor
))
336 avg
+= scm_to_double (anchor
);
340 return scm_from_double (count
> 0 ? avg
/ count
: 0);
343 MAKE_SCHEME_CALLBACK (Break_aligned_interface
, calc_extent_aligned_anchor
, 1)
345 Break_aligned_interface::calc_extent_aligned_anchor (SCM smob
)
347 Grob
*me
= unsmob_grob (smob
);
348 Real alignment
= robust_scm2double (me
->get_property ("break-align-anchor-alignment"), 0.0);
349 Interval iv
= me
->extent (me
, X_AXIS
);
351 if (isinf (iv
[LEFT
]) && isinf (iv
[RIGHT
])) /* avoid NaN */
352 return scm_from_double (0.0);
354 return scm_from_double (iv
.linear_combination (alignment
));
357 MAKE_SCHEME_CALLBACK (Break_aligned_interface
, calc_break_visibility
, 1)
359 Break_aligned_interface::calc_break_visibility (SCM smob
)
361 /* a BreakAlignGroup is break-visible if it has one element that is break-visible */
362 Grob
*me
= unsmob_grob (smob
);
363 SCM ret
= scm_c_make_vector (3, SCM_EOL
);
364 extract_grob_set (me
, "elements", elts
);
365 for (int dir
= 0; dir
<= 2; dir
++)
367 bool visible
= false;
368 for (vsize i
= 0; i
< elts
.size (); i
++)
370 SCM vis
= elts
[i
]->get_property ("break-visibility");
371 if (scm_is_vector (vis
) && to_boolean (scm_c_vector_ref (vis
, dir
)))
374 scm_c_vector_set_x (ret
, dir
, scm_from_bool (visible
));
379 ADD_INTERFACE (Break_alignable_interface
,
380 "Object that is aligned on a break alignment.",
383 "break-align-symbols "
386 ADD_INTERFACE (Break_aligned_interface
,
387 "Items that are aligned in prefatory matter.\n"
389 "The spacing of these items is controlled by the"
390 " @code{space-alist} property. It contains a list"
391 " @code{break-align-symbol}s with a specification of the"
392 " associated space. The space specification can be\n"
395 "@item (minimum-space . @var{spc}))\n"
396 "Pad space until the distance is @var{spc}.\n"
397 "@item (fixed-space . @var{spc})\n"
398 "Set a fixed space.\n"
399 "@item (semi-fixed-space . @var{spc})\n"
400 "Set a space. Half of it is fixed and half is stretchable."
401 " (does not work at start of line. fixme)\n"
402 "@item (extra-space . @var{spc})\n"
403 "Add @var{spc} amount of space.\n"
406 "Special keys for the alist are @code{first-note} and"
407 " @code{next-note}, signifying the first note on a line, and"
408 " the next note halfway a line.\n"
410 "Rules for this spacing are much more complicated than this."
411 " See [Wanske] page 126--134, [Ross] page 143--147.",
414 "break-align-anchor "
415 "break-align-anchor-alignment "
416 "break-align-symbol "
420 ADD_INTERFACE (Break_alignment_interface
,
421 "The object that performs break alignment. See"
422 " @ref{break-aligned-interface}.",
426 "break-align-orders "