2 grob.cc -- implement Grob
4 source file of the GNU LilyPond music typesetter
6 (c) 1997--2006 Han-Wen Nienhuys <hanwen@xs4all.nl>
14 #include "input-smob.hh"
16 #include "pointer-group-interface.hh"
26 #include "ly-smobs.icc"
27 #include "output-def.hh"
30 Grob::clone (int count) const
32 return new Grob (*this, count);
35 Grob::Grob (SCM basicprops,
36 Object_key const *key)
39 /* FIXME: default should be no callback. */
43 interfaces_ = SCM_EOL;
44 immutable_property_alist_ = basicprops;
45 mutable_property_alist_ = SCM_EOL;
46 object_alist_ = SCM_EOL;
48 /* We do smobify_self () as the first step. Since the object lives
49 on the heap, none of its SCM variables are protected from
50 GC. After smobify_self (), they are. */
54 We always get a new key object for a new grob.
57 ((Object_key *)key_)->unprotect ();
59 SCM meta = get_property ("meta");
60 if (scm_is_pair (meta))
61 interfaces_ = scm_cdr (scm_assq (ly_symbol2scm ("interfaces"), meta));
63 if (get_property_data (ly_symbol2scm ("X-extent")) == SCM_EOL)
64 set_property ("X-extent", Grob::stencil_width_proc);
65 if (get_property_data (ly_symbol2scm ("Y-extent")) == SCM_EOL)
66 set_property ("Y-extent", Grob::stencil_height_proc);
69 Grob::Grob (Grob const &s, int copy_index)
70 : dim_cache_ (s.dim_cache_)
72 key_ = (use_object_keys) ? new Copied_key (s.key_, copy_index) : 0;
73 original_ = (Grob *) & s;
76 immutable_property_alist_ = s.immutable_property_alist_;
77 mutable_property_alist_ = ly_deep_copy (s.mutable_property_alist_);
78 interfaces_ = s.interfaces_;
79 object_alist_ = SCM_EOL;
85 ((Object_key *)key_)->unprotect ();
91 /****************************************************************
93 ****************************************************************/
96 Grob::get_stencil () const
101 SCM stil = get_property ("stencil");
102 return unsmob_stencil (stil);
106 Grob::get_print_stencil () const
108 SCM stil = get_property ("stencil");
111 if (Stencil *m = unsmob_stencil (stil))
114 if (to_boolean (get_property ("transparent")))
115 retval = Stencil (m->extent_box (), SCM_EOL);
118 SCM expr = m->expr ();
119 if (point_and_click_global)
120 expr = scm_list_3 (ly_symbol2scm ("grob-cause"),
123 retval = Stencil (m->extent_box (), expr);
126 /* color support... see interpret_stencil_expression () for more... */
127 SCM color = get_property ("color");
128 if (color != SCM_EOL)
130 m = unsmob_stencil (stil);
131 SCM expr = scm_list_3 (ly_symbol2scm ("color"),
135 retval = Stencil (m->extent_box (), expr);
143 /****************************************************************
145 ****************************************************************/
147 Grob::do_break_processing ()
152 Grob::discretionary_processing ()
157 Grob::get_system () const
164 Grob::handle_broken_dependencies ()
166 Spanner *sp = dynamic_cast<Spanner *> (this);
167 if (original () && sp)
171 /* THIS, SP is the original spanner. We use a special function
172 because some Spanners have enormously long lists in their
173 properties, and a special function fixes FOO */
175 for (SCM s = object_alist_; scm_is_pair (s); s = scm_cdr (s))
176 sp->substitute_one_mutable_property (scm_caar (s), scm_cdar (s));
178 System *system = get_system ();
182 && common_refpoint (system, X_AXIS)
183 && common_refpoint (system, Y_AXIS))
184 substitute_object_links (system->self_scm (), object_alist_);
185 else if (dynamic_cast<System *> (this))
186 substitute_object_links (SCM_UNDEFINED, object_alist_);
188 /* THIS element is `invalid'; it has been removed from all
189 dependencies, so let's junk the element itself.
191 Do not do this for System, since that would remove references
192 to the originals of score-grobs, which get then GC'd (a bad
197 /* Note that we still want references to this element to be
198 rearranged, and not silently thrown away, so we keep pointers like
199 {broken_into_{drul, array}, original}
207 for (int a = X_AXIS; a < NO_AXES; a++)
208 dim_cache_[a].clear ();
210 mutable_property_alist_ = SCM_EOL;
211 object_alist_ = SCM_EOL;
212 immutable_property_alist_ = SCM_EOL;
213 interfaces_ = SCM_EOL;
217 Grob::handle_prebroken_dependencies ()
219 /* Don't do this in the derived method, since we want to keep access to
220 object_alist_ centralized. */
223 Item *it = dynamic_cast<Item *> (this);
224 substitute_object_links (scm_from_int (it->break_status_dir ()),
225 original ()->object_alist_);
230 Grob::find_broken_piece (System *) const
235 /****************************************************************
237 ****************************************************************/
240 Grob::translate_axis (Real y, Axis a)
242 if (isinf (y) || isnan (y))
244 programming_error (_ ("Infinity or NaN encountered"));
248 if (!dim_cache_[a].offset_)
249 dim_cache_[a].offset_ = new Real (y);
251 *dim_cache_[a].offset_ += y;
254 /* Find the offset relative to D. If D equals THIS, then it is 0.
255 Otherwise, it recursively defd as
257 OFFSET_ + PARENT_L_->relative_coordinate (D) */
259 Grob::relative_coordinate (Grob const *refp, Axis a) const
264 /* We catch PARENT_L_ == nil case with this, but we crash if we did
265 not ask for the absolute coordinate (ie. REFP == nil.) */
266 Real off = get_offset (a);
267 if (refp == dim_cache_[a].parent_)
270 off += dim_cache_[a].parent_->relative_coordinate (refp, a);
275 /* Invoke callbacks to get offset relative to parent. */
277 Grob::get_offset (Axis a) const
279 if (dim_cache_[a].offset_)
280 return *dim_cache_[a].offset_;
282 Grob *me = (Grob *) this;
284 SCM sym = axis_offset_symbol (a);
285 me->dim_cache_[a].offset_ = new Real (0.0);
288 UGH: can't fold next 2 statements together. Apparently GCC thinks
289 dim_cache_[a].offset_ is unaliased.
291 Real off = robust_scm2double (internal_get_property (sym), 0.0);
292 if (me->dim_cache_[a].offset_)
294 *me->dim_cache_[a].offset_ += off;
295 me->del_property (sym);
296 return *me->dim_cache_[a].offset_;
303 /****************************************************************
305 ****************************************************************/
308 Grob::flush_extent_cache (Axis axis)
310 if (dim_cache_[axis].extent_)
313 Ugh, this is not accurate; will flush property, causing
314 callback to be called if.
316 del_property ((axis == X_AXIS) ? ly_symbol2scm ("X-extent") : ly_symbol2scm ("Y-extent"));
317 delete dim_cache_[axis].extent_;
318 dim_cache_[axis].extent_ = 0;
319 if (get_parent (axis))
320 get_parent (axis)->flush_extent_cache (axis);
326 Grob::extent (Grob *refp, Axis a) const
328 Real offset = relative_coordinate (refp, a);
330 if (dim_cache_[a].extent_)
332 real_ext = *dim_cache_[a].extent_;
337 Order is significant: ?-extent may trigger suicide.
341 ? ly_symbol2scm ("X-extent")
342 : ly_symbol2scm ("Y-extent");
344 SCM ext = internal_get_property (ext_sym);
345 if (is_number_pair (ext))
346 real_ext.unite (ly_scm2interval (ext));
350 ? ly_symbol2scm ("minimum-X-extent")
351 : ly_symbol2scm ("minimum-Y-extent");
352 SCM min_ext = internal_get_property (min_ext_sym);
353 if (is_number_pair (min_ext))
354 real_ext.unite (ly_scm2interval (min_ext));
355 ((Grob*)this)->del_property (ext_sym);
356 ((Grob*)this)->dim_cache_[a].extent_ = new Interval (real_ext);
359 real_ext.translate (offset);
364 /****************************************************************
366 ****************************************************************/
368 /* Find the group-element which has both #this# and #s# */
370 Grob::common_refpoint (Grob const *s, Axis a) const
372 /* I don't like the quadratic aspect of this code, but I see no
373 other way. The largest chain of parents might be 10 high or so,
374 so it shouldn't be a real issue. */
375 for (Grob const *c = this; c; c = c->dim_cache_[a].parent_)
376 for (Grob const *d = s; d; d = d->dim_cache_[a].parent_)
384 Grob::set_parent (Grob *g, Axis a)
386 dim_cache_[a].parent_ = g;
390 Grob::get_parent (Axis a) const
392 return dim_cache_[a].parent_;
397 Grob::fixup_refpoint ()
399 for (int a = X_AXIS; a < NO_AXES; a++)
402 Grob *parent = get_parent (ax);
407 if (parent->get_system () != get_system () && get_system ())
409 Grob *newparent = parent->find_broken_piece (get_system ());
410 set_parent (newparent, ax);
413 if (Item *i = dynamic_cast<Item *> (this))
415 Item *parenti = dynamic_cast<Item *> (parent);
419 Direction my_dir = i->break_status_dir ();
420 if (my_dir != parenti->break_status_dir ())
422 Item *newparent = parenti->find_prebroken_piece (my_dir);
423 set_parent (newparent, ax);
431 /****************************************************************
433 ****************************************************************/
435 Grob::warning (String s) const
437 SCM cause = self_scm ();
438 while (Grob *g = unsmob_grob (cause))
439 cause = g->get_property ("cause");
441 if (Music *m = unsmob_music (cause))
442 m->origin ()->warning (s);
451 SCM meta = get_property ("meta");
452 SCM nm = scm_assq (ly_symbol2scm ("name"), meta);
453 nm = (scm_is_pair (nm)) ? scm_cdr (nm) : SCM_EOL;
454 return scm_is_symbol (nm) ? ly_symbol2string (nm) : this->class_name ();
458 Grob::programming_error (String s) const
460 SCM cause = self_scm ();
461 while (Grob *g = unsmob_grob (cause))
462 cause = g->get_property ("cause");
464 s = _f ("programming error: %s", s);
466 if (Music *m = unsmob_music (cause))
467 m->origin ()->message (s);
473 ADD_INTERFACE (Grob, "grob-interface",
474 "A grob represents a piece of music notation\n"
476 "All grobs have an X and Y-position on the page. These X and Y positions\n"
477 "are stored in a relative format, so they can easily be combined by\n"
478 "stacking them, hanging one grob to the side of another, and coupling\n"
479 "them into a grouping objects.\n"
481 "Each grob has a reference point (a.k.a. parent): the position of a grob\n"
482 "is stored relative to that reference point. For example the X-reference\n"
483 "point of a staccato dot usually is the note head that it applies\n"
484 "to. When the note head is moved, the staccato dot moves along\n"
487 "A grob is often associated with a symbol, but some grobs do not print\n"
488 "any symbols. They take care of grouping objects. For example, there is a\n"
489 "separate grob that stacks staves vertically. The @ref{NoteCollision}\n"
490 "is also an abstract grob: it only moves around chords, but doesn't print\n"
493 "Grobs have a properties: Scheme variables, that can be read and set. "
494 "They have two types. Immutable variables "
495 "define the default style and behavior. They are shared between many objects. "
496 "They can be changed using @code{\\override} and @code{\\revert}. "
498 "Mutable properties are variables that are specific to one grob. Typically, "
499 "lists of other objects, or results from computations are stored in"
500 "mutable properties: every call to set-grob-property (or its C++ equivalent) "
501 "sets a mutable property. "
503 "The properties @code{after-line-breaking} and @code{before-line-breaking} "
504 "are dummies that are not user-serviceable. "
513 "after-line-breaking "
514 "axis-group-parent-X "
515 "axis-group-parent-Y "
516 "before-line-breaking "
537 /****************************************************************
539 ****************************************************************/
543 grob_stencil_extent (Grob *me, Axis a)
545 Stencil *m = me->get_stencil ();
549 return ly_interval2scm (e);
553 MAKE_SCHEME_CALLBACK (Grob, stencil_height, 1);
555 Grob::stencil_height (SCM smob)
557 Grob *me = unsmob_grob (smob);
558 return grob_stencil_extent (me, Y_AXIS);
561 MAKE_SCHEME_CALLBACK(Grob, y_parent_positioning, 1);
563 Grob::y_parent_positioning (SCM smob)
565 Grob *me = unsmob_grob (smob);
566 Grob *par = me->get_parent (Y_AXIS);
568 (void) par->get_property ("positioning-done");
570 return scm_from_double (0.0);
574 MAKE_SCHEME_CALLBACK(Grob, x_parent_positioning, 1);
576 Grob::x_parent_positioning (SCM smob)
578 Grob *me = unsmob_grob (smob);
580 Grob *par = me->get_parent (X_AXIS);
582 (void) par->get_property ("positioning-done");
584 return scm_from_double (0.0);
587 MAKE_SCHEME_CALLBACK (Grob, stencil_width, 1);
589 Grob::stencil_width (SCM smob)
591 Grob *me = unsmob_grob (smob);
592 return grob_stencil_extent (me, X_AXIS);
598 common_refpoint_of_list (SCM elist, Grob *common, Axis a)
600 for (; scm_is_pair (elist); elist = scm_cdr (elist))
601 if (Grob *s = unsmob_grob (scm_car (elist)))
604 common = common->common_refpoint (s, a);
613 common_refpoint_of_array (Link_array<Grob> const &arr, Grob *common, Axis a)
615 for (int i = arr.size (); i--;)
616 if (Grob *s = arr[i])
619 common = common->common_refpoint (s, a);
628 robust_relative_extent (Grob *me, Grob *refpoint, Axis a)
630 Interval ext = me->extent (refpoint, a);
632 ext.add_point (me->relative_coordinate (refpoint, a));