2 This file is part of LilyPond, the GNU music typesetter.
4 Copyright (C) 2005--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/>.
20 #include "staff-spacing.hh"
22 #include "spacing-options.hh"
24 #include "paper-column.hh"
25 #include "column-x-positions.hh"
26 #include "pointer-group-interface.hh"
27 #include "spacing-interface.hh"
28 #include "spacing-spanner.hh"
29 #include "note-spacing.hh"
31 #include "grob-array.hh"
32 #include "break-align-interface.hh"
36 Return whether COL is fixed to its neighbors by some kind of spacing
40 If in doubt, then we're not loose; the spacing engine should space
41 for it, risking suboptimal spacing.
43 (Otherwise, we might risk core dumps, and other weird stuff.)
46 is_loose_column (Grob
*l
, Grob
*col
, Grob
*r
, Spacing_options
const *options
)
48 if (!to_boolean (col
->get_property ("allow-loose-spacing")))
52 if ((options
->float_nonmusical_columns_
53 || options
->float_grace_columns_
)
54 && Paper_column::when_mom (col
).grace_part_
)
60 if (Paper_column::is_musical (col
))
64 If this column doesn't have a proper neighbor, we should really
65 make it loose, but spacing it correctly is more than we can
68 (this happens in the following situation:
79 the column containing the clef is really loose, and should be
80 attached right to the first column, but that is a lot of work for
81 such a borderline case.)
85 Item
*r_neighbor
= unsmob_item (col
->get_object ("right-neighbor"));
86 Item
*l_neighbor
= unsmob_item (col
->get_object ("left-neighbor"));
88 if (!l_neighbor
|| !r_neighbor
)
91 /* If a non-empty column (ie. not \bar "") is placed nicely in series with
92 its neighbor (ie. no funny polyphonic stuff), don't make it loose.
94 if (l
== l_neighbor
&& r
== r_neighbor
&& col
->extent (col
, X_AXIS
).length () > 0)
98 Only declare loose if the bounds make a little sense. This means
99 some cases (two isolated, consecutive clef changes) won't be
100 nicely folded, but hey, then don't do that.
102 if (! ((Paper_column::is_musical (l_neighbor
) || Paper_column::is_breakable (l_neighbor
))
103 && (Paper_column::is_musical (r_neighbor
) || Paper_column::is_breakable (r_neighbor
))))
107 in any case, we don't want to move bar lines.
109 extract_grob_set (col
, "elements", elts
);
110 for (vsize i
= elts
.size (); i
--;)
113 if (g
&& Break_alignment_interface::has_interface (g
))
115 extract_grob_set (g
, "elements", gelts
);
116 for (vsize j
= gelts
.size (); j
--;)
120 if (h
&& h
->get_property ("break-align-symbol") == ly_symbol2scm ("staff-bar"))
122 extract_grob_set (h
, "elements", helts
);
123 for (vsize k
= helts
.size (); k
--;)
124 if ("" != robust_scm2string (helts
[k
]->get_property ("glyph-name"), ""))
135 Spacing_spanner::set_distances_for_loose_col (Grob
*me
, Grob
*c
,
136 Drul_array
<Item
*> next_door
,
137 Spacing_options
const *options
)
140 Drul_array
<Real
> dists (0, 0);
144 Item
*lc
= dynamic_cast<Item
*> ((d
== LEFT
) ? next_door
[LEFT
] : c
);
145 Item
*rc
= dynamic_cast<Item
*> (d
== LEFT
? c
: next_door
[RIGHT
]);
147 extract_grob_set (lc
, "spacing-wishes", wishes
);
148 for (vsize k
= wishes
.size (); k
--;)
150 Grob
*sp
= wishes
[k
];
151 if (Spacing_interface::left_column (sp
) != lc
152 || Spacing_interface::right_column (sp
) != rc
)
155 if (Note_spacing::has_interface (sp
))
158 The note spacing should be taken from the musical
161 Real base
= note_spacing (me
, lc
, rc
, options
);
162 Spring spring
= Note_spacing::get_spacing (sp
, rc
, base
, options
->increment_
);
164 dists
[d
] = max (dists
[d
], spring
.min_distance ());
166 else if (Staff_spacing::has_interface (sp
))
168 Spring spring
= Staff_spacing::get_spacing (sp
, rc
);
170 dists
[d
] = max (dists
[d
], spring
.min_distance ());
173 programming_error ("Subversive spacing wish");
176 while (flip (&d
) != LEFT
);
179 r
.distance_
= dists
[LEFT
] + dists
[RIGHT
];
180 r
.item_drul_
= next_door
;
187 Remove columns that are not tightly fitting from COLS. In the
188 removed columns, set 'between-cols to the columns where it is in
192 Spacing_spanner::prune_loose_columns (Grob
*me
,
194 Spacing_options
*options
)
196 vector
<Grob
*> newcols
;
198 for (vsize i
= 0; i
< cols
->size (); i
++)
200 Grob
*c
= cols
->at (i
);
202 bool loose
= (i
> 0 && i
+ 1 < cols
->size ())
203 && is_loose_column (cols
->at (i
- 1), c
, cols
->at (i
+ 1), options
);
205 /* Breakable columns never get pruned; even if they are loose,
206 their broken pieces are not. However, we mark them so that
207 the spacing can take their mid-line looseness into account. */
208 if (loose
&& Paper_column::is_breakable (c
))
211 c
->set_property ("maybe-loose", SCM_BOOL_T
);
216 Grob
*right_neighbor
= unsmob_grob (c
->get_object ("right-neighbor"));
217 Grob
*left_neighbor
= unsmob_grob (c
->get_object ("left-neighbor"));
220 Either object can be non existent, if the score ends
223 if (!right_neighbor
|| !left_neighbor
)
225 c
->programming_error ("Cannot determine neighbors for floating column. ");
226 c
->set_object ("between-cols", scm_cons (cols
->at (i
-1)->self_scm (),
227 cols
->at (i
+1)->self_scm ()));
231 c
->set_object ("between-cols", scm_cons (left_neighbor
->self_scm (),
232 right_neighbor
->self_scm ()));
236 Set distance constraints for loose columns
238 Drul_array
<Item
*> next_door (dynamic_cast<Item
*> (cols
->at (i
- 1)),
239 dynamic_cast<Item
*> (cols
->at (i
+ 1)));
241 set_distances_for_loose_col (me
, c
, next_door
, options
);
246 newcols
.push_back (c
);
253 Set neighboring columns determined by the spacing-wishes grob property.
256 Spacing_spanner::set_explicit_neighbor_columns (vector
<Grob
*> const &cols
)
258 for (vsize i
= 0; i
< cols
.size (); i
++)
260 extract_grob_set (cols
[i
], "spacing-wishes", wishes
);
261 for (vsize j
= wishes
.size (); j
--;)
263 Item
*wish
= dynamic_cast<Item
*> (wishes
[j
]);
264 Item
*left_col
= wish
->get_column ();
265 int left_rank
= Paper_column::get_rank (left_col
);
266 int min_right_rank
= INT_MAX
;
268 extract_grob_set (wish
, "right-items", right_items
);
269 for (vsize k
= right_items
.size (); k
--;)
271 Item
*right_col
= dynamic_cast<Item
*> (right_items
[k
])->get_column ();
272 int right_rank
= Paper_column::get_rank (right_col
);
274 if (right_rank
< min_right_rank
)
276 left_col
->set_object ("right-neighbor", right_col
->self_scm ());
277 min_right_rank
= right_rank
;
280 Grob
*old_left_neighbor
= unsmob_grob (right_col
->get_object ("left-neighbor"));
281 if (!old_left_neighbor
|| left_rank
> Paper_column::get_rank (old_left_neighbor
))
282 right_col
->set_object ("left-neighbor", left_col
->self_scm ());
289 Set neighboring columns that have no left/right-neighbor set
290 yet. Only do breakable non-musical columns, and musical columns.
291 Why only these? --jneem
294 Spacing_spanner::set_implicit_neighbor_columns (vector
<Grob
*> const &cols
)
296 for (vsize i
= 0; i
< cols
.size (); i
++)
298 Item
*it
= dynamic_cast<Item
*> (cols
[i
]);
299 if (!Paper_column::is_breakable (it
) && !Paper_column::is_musical (it
))
302 if (i
&& !unsmob_grob (cols
[i
]->get_object ("left-neighbor")))
303 cols
[i
]->set_object ("left-neighbor", cols
[i
-1]->self_scm ());
304 if (i
+ 1 < cols
.size () && !unsmob_grob (cols
[i
]->get_object ("right-neighbor")))
305 cols
[i
]->set_object ("right-neighbor", cols
[i
+1]->self_scm ());