2 spanner.cc -- implement Spanner
4 source file of the GNU LilyPond music typesetter
6 (c) 1996--2001 Han-Wen Nienhuys <hanwen@cs.uu.nl>
9 #include <libc-extension.hh>
14 #include "paper-column.hh"
15 #include "paper-score.hh"
16 #include "molecule.hh"
17 #include "paper-outputter.hh"
18 #include "paper-column.hh"
19 #include "line-of-score.hh"
20 #include "break-align-item.hh"
21 #include "group-interface.hh"
24 Spanner::do_break_processing ()
27 Item
* left
= spanned_drul_
[LEFT
];
28 Item
* right
= spanned_drul_
[RIGHT
];
34 Check if our parent in X-direction spans equally wide
37 for (int a
= X_AXIS
; a
< NO_AXES
; a
++)
39 if (Spanner
* parent
= dynamic_cast<Spanner
*> (parent_l ((Axis
)a
)))
41 if (!parent
->spanned_rank_iv ().contains_b (this->spanned_rank_iv ()))
43 programming_error (to_str ("Spanner `%s' is not fully contained in parent spanner `%s'.",
45 parent
->name ().ch_C ()));
50 if (line_l () || broken_b ())
56 If we have a spanner spanning one column, we must break it
57 anyway because it might provide a parent for another item. */
61 Item
* bound
= left
->find_prebroken_piece (d
);
63 programming_error ("no broken bound");
64 else if (bound
->line_l ())
66 Spanner
* span_p
= dynamic_cast<Spanner
*> (clone ());
67 span_p
->set_bound (LEFT
, bound
);
68 span_p
->set_bound (RIGHT
, bound
);
70 assert (span_p
->line_l ());
71 span_p
->line_l ()->typeset_grob (span_p
);
72 broken_into_l_arr_
.push (span_p
);
75 while ((flip (&d
))!= LEFT
);
79 Link_array
<Item
> break_points
= pscore_l_
->line_l_
->broken_col_range (left
,right
);
81 break_points
.insert (left
,0);
82 break_points
.push (right
);
84 for (int i
=1; i
< break_points
.size (); i
++)
86 Drul_array
<Item
*> bounds
;
87 bounds
[LEFT
] = break_points
[i
-1];
88 bounds
[RIGHT
] = break_points
[i
];
92 if (!bounds
[d
]->line_l ())
93 bounds
[d
] = bounds
[d
]->find_prebroken_piece (- d
);
95 while ((flip (&d
))!= LEFT
);
97 if (!bounds
[LEFT
] || ! bounds
[RIGHT
])
99 programming_error ("bounds of this piece aren't breakable. ");
103 Spanner
*span_p
= dynamic_cast<Spanner
*> (clone ());
104 span_p
->set_bound (LEFT
,bounds
[LEFT
]);
105 span_p
->set_bound (RIGHT
,bounds
[RIGHT
]);
107 if (!bounds
[LEFT
]->line_l ()
109 || !bounds
[RIGHT
]->line_l ()
110 || bounds
[LEFT
]->line_l () != bounds
[RIGHT
]->line_l ())
112 programming_error ("bounds of spanner are invalid");
117 bounds
[LEFT
]->line_l ()->typeset_grob (span_p
);
118 broken_into_l_arr_
.push (span_p
);
122 broken_into_l_arr_
.sort (Spanner::compare
);
126 Spanner::set_my_columns ()
128 Direction i
= (Direction
) LEFT
;
131 if (!spanned_drul_
[i
]->line_l ())
132 set_bound (i
,spanned_drul_
[i
]->find_prebroken_piece ((Direction
) -i
));
134 while (flip (&i
) != LEFT
);
138 Spanner::spanned_rank_iv ()
140 Interval_t
<int> iv (0, 0);
142 if (spanned_drul_
[LEFT
])
144 iv
[LEFT
] = Paper_column::rank_i (spanned_drul_
[LEFT
]->column_l ());
146 if (spanned_drul_
[RIGHT
])
148 iv
[RIGHT
] = Paper_column::rank_i (spanned_drul_
[RIGHT
]->column_l ());
154 Spanner::get_bound (Direction d
) const
156 return spanned_drul_
[d
];
160 Set the items that this spanner spans. If D == LEFT, we also set the
161 X-axis parent of THIS to S.
164 Spanner::set_bound (Direction d
, Grob
*s
)
166 Item
* i
= dynamic_cast<Item
*> (s
);
169 programming_error ("Must have Item for spanner bound.");
176 We check for Line_of_score to prevent the column -> line_of_score
177 -> column -> line_of_score -> etc situation */
178 if (d
== LEFT
&& !dynamic_cast<Line_of_score
*> (this))
180 set_parent (i
, X_AXIS
);
184 Signal that this column needs to be kept alive. They need to be
185 kept alive to have meaningful position and linebreaking.
187 [maybe we should try keeping all columns alive?, and perhaps
188 inherit position from their (non-)musical brother]
191 if (dynamic_cast<Paper_column
*> (i
))
193 Pointer_group_interface::add_element (i
, "bounded-by-me",this);
197 Spanner::Spanner (SCM s
)
200 spanned_drul_
[LEFT
]=0;
201 spanned_drul_
[RIGHT
]=0;
204 Spanner::Spanner (Spanner
const &s
)
207 spanned_drul_
[LEFT
] = spanned_drul_
[RIGHT
] =0;
211 Spanner::spanner_length () const
213 Real l
= spanned_drul_
[LEFT
]->relative_coordinate (0, X_AXIS
);
214 Real r
= spanned_drul_
[RIGHT
]->relative_coordinate (0, X_AXIS
);
217 programming_error ("spanner with negative length");
223 Spanner::line_l () const
225 if (!spanned_drul_
[LEFT
] || !spanned_drul_
[RIGHT
])
227 if (spanned_drul_
[LEFT
]->line_l () != spanned_drul_
[RIGHT
]->line_l ())
229 return spanned_drul_
[LEFT
]->line_l ();
234 Spanner::find_broken_piece (Line_of_score
*l
) const
236 int idx
= binsearch_link_array (broken_into_l_arr_
, (Spanner
*)l
, Spanner::compare
);
241 return broken_into_l_arr_
[idx
];
246 Spanner::compare (Spanner
* const &p1
, Spanner
* const &p2
)
248 return p1
->line_l ()->rank_i_
- p2
->line_l ()->rank_i_
;
252 Spanner::broken_b () const
254 return broken_into_l_arr_
.size ();
259 If this is a broken spanner, return the amount the left end is to be
260 shifted horizontally so that the spanner starts after the initial
261 clef and key on the staves. This is necessary for ties, slurs,
262 crescendo and decrescendo signs, for example.
265 Spanner::get_broken_left_end_align () const
267 Paper_column
*sc
= dynamic_cast<Paper_column
*> (spanned_drul_
[LEFT
]->column_l ());
269 // Relevant only if left span point is first column in line
271 sc
->break_status_dir () == RIGHT
)
275 We used to do a full search for the Break_align_item.
276 But that doesn't make a difference, since the Paper_column
277 is likely to contain only a Break_align_item.
279 return sc
->extent (sc
, X_AXIS
)[RIGHT
];
286 Spanner::do_derived_mark ()
289 We'd be fucked if this is called before spanned_drul_[] is inited. */
290 if (status_c_
== ORPHAN
)
295 if (spanned_drul_
[d
])
296 scm_gc_mark (spanned_drul_
[d
]->self_scm ());
297 while (flip (&d
) != LEFT
);
299 for (int i
= broken_into_l_arr_
.size () ; i
--;)
300 scm_gc_mark (broken_into_l_arr_
[i
]->self_scm ());
306 add_bound_item (Spanner
* sp
, Item
*it
)
308 if (!sp
->get_bound (LEFT
))
309 sp
->set_bound (LEFT
, it
);
311 sp
->set_bound (RIGHT
, it
);
315 Extends EXTREMAL_PAIR to include IT
318 extend_spanner_over_item (Item
*it
, SCM extremal_pair
)
322 Item
* col
= it
->column_l ();
323 Item
* i1
= dynamic_cast<Item
*> (unsmob_grob (gh_car (extremal_pair
)));
324 Item
* i2
= dynamic_cast<Item
*> (unsmob_grob (gh_cdr (extremal_pair
)));
325 int r
= Paper_column::rank_i (col
);
326 if (!i1
|| r
< Paper_column::rank_i (i1
->column_l ()))
328 gh_set_car_x (extremal_pair
, it
->self_scm ());
330 if (!i2
|| r
> Paper_column::rank_i (i2
->column_l ()))
332 gh_set_cdr_x (extremal_pair
, it
->self_scm ());
337 Extends EXTREMAL_PAIR to include every grob in VALUE
340 extend_spanner_over_elements (SCM value
, SCM extremal_pair
)
342 if (gh_pair_p (value
))
344 extend_spanner_over_elements (gh_car (value
), extremal_pair
);
345 extend_spanner_over_elements (gh_cdr (value
), extremal_pair
);
347 else if (unsmob_grob (value
))
349 if (Spanner
* sp
= dynamic_cast<Spanner
*> (unsmob_grob (value
)))
351 extend_spanner_over_item (sp
->get_bound (LEFT
), extremal_pair
);
352 extend_spanner_over_item (sp
->get_bound (RIGHT
), extremal_pair
);
354 else if (Item
* it
= dynamic_cast<Item
*> (unsmob_grob (value
)))
355 extend_spanner_over_item (it
, extremal_pair
);
361 Make sure that the left and right bounds encompasses all objects it
364 TODO: maybe be more specific. Most probably fucks up if someone sets
365 a pointer to the staff symbol in S
368 extend_spanner_over_elements (Grob
*s
)
370 Spanner
*sp
= dynamic_cast<Spanner
*> (s
);
372 SCM s1
= sp
->get_bound (LEFT
) ? sp
->get_bound (LEFT
)->self_scm () : SCM_EOL
;
373 SCM s2
= sp
->get_bound (RIGHT
) ? sp
->get_bound (RIGHT
)->self_scm () : SCM_EOL
;
375 SCM pair
= gh_cons (s1
,s2
);
376 extend_spanner_over_elements (sp
->mutable_property_alist_
, pair
);
378 Grob
*p1
= unsmob_grob (gh_car (pair
));
379 Grob
* p2
= unsmob_grob (gh_cdr (pair
));
380 sp
->set_bound (LEFT
,p1
);
381 sp
->set_bound (RIGHT
, p2
);
385 MAKE_SCHEME_CALLBACK (Spanner
,set_spacing_rods
,1);
387 Spanner::set_spacing_rods (SCM smob
)
389 Grob
*me
= unsmob_grob (smob
);
392 Spanner
*sp
= dynamic_cast<Spanner
*> (me
);
393 r
.item_l_drul_
[LEFT
] = sp
->get_bound (LEFT
);
394 r
.item_l_drul_
[RIGHT
] = sp
->get_bound (RIGHT
);
396 gh_scm2double (me
->get_grob_property ("minimum-length"))
400 return SCM_UNSPECIFIED
;