2 spanner.cc -- implement Spanner
4 source file of the GNU LilyPond music typesetter
6 (c) 1996--2004 Han-Wen Nienhuys <hanwen@cs.uu.nl>
9 #include <libc-extension.hh>
14 #include "paper-column.hh"
15 #include "paper-score.hh"
17 #include "paper-outputter.hh"
18 #include "paper-column.hh"
20 #include "group-interface.hh"
23 Spanner::do_break_processing ()
26 Item
* left
= spanned_drul_
[LEFT
];
27 Item
* right
= spanned_drul_
[RIGHT
];
33 Check if our parent in X-direction spans equally wide
36 for (int a
= X_AXIS
; a
< NO_AXES
; a
++)
38 if (Spanner
* parent
= dynamic_cast<Spanner
*> (get_parent ((Axis
)a
)))
40 if (!parent
->spanned_rank_iv ().superset (this->spanned_rank_iv ()))
42 programming_error (to_string ("Spanner `%s' is not fully contained in parent spanner `%s'.",
44 parent
->name ().to_str0 ()));
49 if (get_system () || is_broken ())
55 If we have a spanner spanning one column, we must break it
56 anyway because it might provide a parent for another item. */
60 Item
* bound
= left
->find_prebroken_piece (d
);
62 programming_error ("no broken bound");
63 else if (bound
->get_system ())
65 Spanner
* span
= dynamic_cast<Spanner
*> (clone ());
66 span
->set_bound (LEFT
, bound
);
67 span
->set_bound (RIGHT
, bound
);
69 assert (span
->get_system ());
70 span
->get_system ()->typeset_grob (span
);
71 broken_intos_
.push (span
);
74 while ((flip (&d
))!= LEFT
);
78 Link_array
<Item
> break_points
= pscore_
->system_
->broken_col_range (left
,right
);
80 break_points
.insert (left
,0);
81 break_points
.push (right
);
83 for (int i
=1; i
< break_points
.size (); i
++)
85 Drul_array
<Item
*> bounds
;
86 bounds
[LEFT
] = break_points
[i
-1];
87 bounds
[RIGHT
] = break_points
[i
];
91 if (!bounds
[d
]->get_system ())
92 bounds
[d
] = bounds
[d
]->find_prebroken_piece (- d
);
94 while ((flip (&d
))!= LEFT
);
96 if (!bounds
[LEFT
] || ! bounds
[RIGHT
])
98 programming_error ("bounds of this piece aren't breakable. ");
102 Spanner
*span
= dynamic_cast<Spanner
*> (clone ());
103 span
->set_bound (LEFT
,bounds
[LEFT
]);
104 span
->set_bound (RIGHT
,bounds
[RIGHT
]);
106 if (!bounds
[LEFT
]->get_system ()
108 || !bounds
[RIGHT
]->get_system ()
109 || bounds
[LEFT
]->get_system () != bounds
[RIGHT
]->get_system ())
111 programming_error ("bounds of spanner are invalid");
116 bounds
[LEFT
]->get_system ()->typeset_grob (span
);
117 broken_intos_
.push (span
);
121 broken_intos_
.sort (Spanner::compare
);
122 for (int i
= broken_intos_
.size ();i
--;)
123 broken_intos_
[i
]->break_index_
= i
;
127 Spanner::get_break_index ()const
133 Spanner::set_my_columns ()
135 Direction i
= (Direction
) LEFT
;
138 if (!spanned_drul_
[i
]->get_system ())
139 set_bound (i
,spanned_drul_
[i
]->find_prebroken_piece ((Direction
) -i
));
141 while (flip (&i
) != LEFT
);
145 Spanner::spanned_rank_iv ()
147 Interval_t
<int> iv (0, 0);
149 if (spanned_drul_
[LEFT
])
151 iv
[LEFT
] = Paper_column::get_rank (spanned_drul_
[LEFT
]->get_column ());
153 if (spanned_drul_
[RIGHT
])
155 iv
[RIGHT
] = Paper_column::get_rank (spanned_drul_
[RIGHT
]->get_column ());
162 Spanner::get_bound (Direction d
) const
164 return spanned_drul_
[d
];
168 Set the items that this spanner spans. If D == LEFT, we also set the
169 X-axis parent of THIS to S.
172 Spanner::set_bound (Direction d
, Grob
*s
)
174 Item
* i
= dynamic_cast<Item
*> (s
);
177 programming_error ("Must have Item for spanner bound.");
184 We check for System to prevent the column -> line_of_score
185 -> column -> line_of_score -> etc situation */
186 if (d
== LEFT
&& !dynamic_cast<System
*> (this))
188 set_parent (i
, X_AXIS
);
192 Signal that this column needs to be kept alive. They need to be
193 kept alive to have meaningful position and linebreaking.
195 [maybe we should try keeping all columns alive?, and perhaps
196 inherit position from their (non-)musical brother]
199 if (dynamic_cast<Paper_column
*> (i
))
201 Pointer_group_interface::add_grob (i
, ly_symbol2scm ("bounded-by-me"), this);
205 Spanner::Spanner (SCM s
)
209 spanned_drul_
[LEFT
]=0;
210 spanned_drul_
[RIGHT
]=0;
212 Group_interface::add_thing (this, ly_symbol2scm ("interfaces"), ly_symbol2scm ("spanner-interface"));
215 Spanner::Spanner (Spanner
const &s
)
218 spanned_drul_
[LEFT
] = spanned_drul_
[RIGHT
] =0;
222 Spanner::spanner_length () const
224 Real l
= spanned_drul_
[LEFT
]->relative_coordinate (0, X_AXIS
);
225 Real r
= spanned_drul_
[RIGHT
]->relative_coordinate (0, X_AXIS
);
228 programming_error ("spanner with negative length");
234 Spanner::get_system () const
236 if (!spanned_drul_
[LEFT
] || !spanned_drul_
[RIGHT
])
238 if (spanned_drul_
[LEFT
]->get_system () != spanned_drul_
[RIGHT
]->get_system ())
240 return spanned_drul_
[LEFT
]->get_system ();
245 Spanner::find_broken_piece (System
*l
) const
247 int idx
= binsearch_links (broken_intos_
, (Spanner
*)l
, Spanner::compare
);
252 return broken_intos_
[idx
];
257 Spanner::compare (Spanner
* const &p1
, Spanner
* const &p2
)
259 return p1
->get_system ()->rank_
- p2
->get_system ()->rank_
;
263 Spanner::is_broken () const
265 return broken_intos_
.size ();
270 If this is a broken spanner, return the amount the left end is to be
271 shifted horizontally so that the spanner starts after the initial
272 clef and key on the staves. This is necessary for ties, slurs,
273 crescendo and decrescendo signs, for example.
276 Spanner::get_broken_left_end_align () const
278 Paper_column
*sc
= dynamic_cast<Paper_column
*> (spanned_drul_
[LEFT
]->get_column ());
280 // Relevant only if left span point is first column in line
282 sc
->break_status_dir () == RIGHT
)
286 We used to do a full search for the Break_align_item.
287 But that doesn't make a difference, since the Paper_column
288 is likely to contain only a Break_align_item.
290 return sc
->extent (sc
, X_AXIS
)[RIGHT
];
297 Spanner::do_derived_mark () const
300 We'd be fucked if this is called before spanned_drul_[] is inited. */
301 if (status_
== ORPHAN
)
306 if (spanned_drul_
[d
])
307 scm_gc_mark (spanned_drul_
[d
]->self_scm ());
308 while (flip (&d
) != LEFT
);
310 for (int i
= broken_intos_
.size () ; i
--;)
311 scm_gc_mark (broken_intos_
[i
]->self_scm ());
318 Set left or right bound to IT.
320 Warning: caller should ensure that subsequent calls put in ITems
321 that are left-to-right ordered.
324 add_bound_item (Spanner
* sp
, Grob
*it
)
326 if (!sp
->get_bound (LEFT
))
327 sp
->set_bound (LEFT
, it
);
329 sp
->set_bound (RIGHT
, it
);
333 MAKE_SCHEME_CALLBACK (Spanner
,set_spacing_rods
,1);
335 Spanner::set_spacing_rods (SCM smob
)
337 Grob
*me
= unsmob_grob (smob
);
340 Spanner
*sp
= dynamic_cast<Spanner
*> (me
);
341 r
.item_l_drul_
[LEFT
] = sp
->get_bound (LEFT
);
342 r
.item_l_drul_
[RIGHT
] = sp
->get_bound (RIGHT
);
344 robust_scm2double (me
->get_property ("minimum-length"), 0);
347 return SCM_UNSPECIFIED
;
352 Return I such that SP == SP->ORIGINAL_->BROKEN_INTOS_[I].
355 broken_spanner_index (Spanner
* sp
)
357 Spanner
* parent
= dynamic_cast<Spanner
*> (sp
->original_
);
358 return parent
->broken_intos_
.find_index (sp
);
363 unsmob_spanner (SCM s
)
365 return dynamic_cast<Spanner
*> (unsmob_grob (s
));
368 ADD_INTERFACE (Spanner
,
370 "Some objects are horizontally spanned between objects. For\n"
371 "example, slur, beam, tie, etc. These grobs form a subtype called\n"
372 "@code{Spanner}. All spanners have two span-points (these must be\n"
373 "@code{Item} objects), one on the left and one on the right. The left bound is\n"
374 "also the X-reference point of the spanner.\n"