2 grob.cc -- implement Grob
4 source file of the GNU LilyPond music typesetter
6 (c) 1997--2005 Han-Wen Nienhuys <hanwen@xs4all.nl>
14 #include "input-smob.hh"
16 #include "pointer-group-interface.hh"
18 #include "paper-score.hh"
27 #include "paper-score.hh"
28 #include "ly-smobs.icc"
29 #include "output-def.hh"
36 Grob::clone (int count) const
38 return new Grob (*this, count);
41 Grob::Grob (SCM basicprops,
42 Object_key const *key)
45 /* FIXME: default should be no callback. */
49 interfaces_ = SCM_EOL;
50 immutable_property_alist_ = basicprops;
51 mutable_property_alist_ = SCM_EOL;
52 object_alist_ = SCM_EOL;
54 /* We do smobify_self () as the first step. Since the object lives
55 on the heap, none of its SCM variables are protected from
56 GC. After smobify_self (), they are. */
60 We always get a new key object for a new grob.
63 ((Object_key *)key_)->unprotect ();
65 SCM meta = get_property ("meta");
66 if (scm_is_pair (meta))
67 interfaces_ = scm_cdr (scm_assq (ly_symbol2scm ("interfaces"), meta));
69 if (get_property_data (ly_symbol2scm ("X-extent")) == SCM_EOL)
70 set_property ("X-extent", Grob::stencil_width_proc);
71 if (get_property_data (ly_symbol2scm ("Y-extent")) == SCM_EOL)
72 set_property ("Y-extent", Grob::stencil_height_proc);
75 Grob::Grob (Grob const &s, int copy_index)
76 : dim_cache_ (s.dim_cache_)
78 key_ = (use_object_keys) ? new Copied_key (s.key_, copy_index) : 0;
79 original_ = (Grob *) & s;
82 immutable_property_alist_ = s.immutable_property_alist_;
83 mutable_property_alist_ = ly_deep_copy (s.mutable_property_alist_);
84 interfaces_ = s.interfaces_;
85 object_alist_ = SCM_EOL;
91 ((Object_key *)key_)->unprotect ();
97 /****************************************************************
99 ****************************************************************/
102 Grob::get_stencil () const
107 SCM stil = get_property ("stencil");
108 return unsmob_stencil (stil);
112 Grob::get_print_stencil () const
114 SCM stil = get_property ("stencil");
117 if (Stencil *m = unsmob_stencil (stil))
120 if (to_boolean (get_property ("transparent")))
121 retval = Stencil (m->extent_box (), SCM_EOL);
124 SCM expr = m->expr ();
125 if (point_and_click_global)
126 expr = scm_list_3 (ly_symbol2scm ("grob-cause"),
129 retval = Stencil (m->extent_box (), expr);
132 /* color support... see interpret_stencil_expression () for more... */
133 SCM color = get_property ("color");
134 if (color != SCM_EOL)
136 m = unsmob_stencil (stil);
137 SCM expr = scm_list_3 (ly_symbol2scm ("color"),
141 retval = Stencil (m->extent_box (), expr);
149 /****************************************************************
151 ****************************************************************/
153 Grob::do_break_processing ()
158 Grob::discretionary_processing ()
163 Grob::get_system () const
170 Grob::handle_broken_dependencies ()
172 Spanner *sp = dynamic_cast<Spanner *> (this);
173 if (original () && sp)
177 /* THIS, SP is the original spanner. We use a special function
178 because some Spanners have enormously long lists in their
179 properties, and a special function fixes FOO */
181 for (SCM s = object_alist_; scm_is_pair (s); s = scm_cdr (s))
182 sp->substitute_one_mutable_property (scm_caar (s), scm_cdar (s));
184 System *system = get_system ();
188 && common_refpoint (system, X_AXIS)
189 && common_refpoint (system, Y_AXIS))
190 substitute_object_links (system->self_scm (), object_alist_);
191 else if (dynamic_cast<System *> (this))
192 substitute_object_links (SCM_UNDEFINED, object_alist_);
194 /* THIS element is `invalid'; it has been removed from all
195 dependencies, so let's junk the element itself.
197 Do not do this for System, since that would remove references
198 to the originals of score-grobs, which get then GC'd (a bad
203 /* Note that we still want references to this element to be
204 rearranged, and not silently thrown away, so we keep pointers like
205 {broken_into_{drul, array}, original}
213 for (int a = X_AXIS; a < NO_AXES; a++)
214 dim_cache_[a].clear ();
216 mutable_property_alist_ = SCM_EOL;
217 object_alist_ = SCM_EOL;
218 immutable_property_alist_ = SCM_EOL;
219 interfaces_ = SCM_EOL;
223 Grob::handle_prebroken_dependencies ()
225 /* Don't do this in the derived method, since we want to keep access to
226 object_alist_ centralized. */
229 Item *it = dynamic_cast<Item *> (this);
230 substitute_object_links (scm_from_int (it->break_status_dir ()),
231 original ()->object_alist_);
236 Grob::find_broken_piece (System *) const
241 /****************************************************************
243 ****************************************************************/
246 Grob::translate_axis (Real y, Axis a)
248 if (isinf (y) || isnan (y))
250 programming_error (_ ("Infinity or NaN encountered"));
254 if (!dim_cache_[a].offset_)
255 dim_cache_[a].offset_ = new Real (y);
257 *dim_cache_[a].offset_ += y;
260 /* Find the offset relative to D. If D equals THIS, then it is 0.
261 Otherwise, it recursively defd as
263 OFFSET_ + PARENT_L_->relative_coordinate (D) */
265 Grob::relative_coordinate (Grob const *refp, Axis a) const
270 /* We catch PARENT_L_ == nil case with this, but we crash if we did
271 not ask for the absolute coordinate (ie. REFP == nil.) */
272 Real off = get_offset (a);
273 if (refp == dim_cache_[a].parent_)
276 off += dim_cache_[a].parent_->relative_coordinate (refp, a);
281 /* Invoke callbacks to get offset relative to parent. */
283 Grob::get_offset (Axis a) const
285 if (dim_cache_[a].offset_)
286 return *dim_cache_[a].offset_;
288 Grob *me = (Grob *) this;
290 SCM sym = axis_offset_symbol (a);
291 me->dim_cache_[a].offset_ = new Real (0.0);
294 UGH: can't fold next 2 statements together. Apparently GCC thinks
295 dim_cache_[a].offset_ is unaliased.
297 Real off = robust_scm2double (internal_get_property (sym), 0.0);
298 *me->dim_cache_[a].offset_ += off;
300 me->del_property (sym);
301 return *me->dim_cache_[a].offset_;
305 /****************************************************************
307 ****************************************************************/
310 Grob::flush_extent_cache (Axis axis)
312 if (dim_cache_[axis].extent_)
315 Ugh, this is not accurate; will flush property, causing
316 callback to be called if.
318 del_property ((axis == X_AXIS) ? ly_symbol2scm ("X-extent") : ly_symbol2scm ("Y-extent"));
319 delete dim_cache_[axis].extent_;
320 dim_cache_[axis].extent_ = 0;
321 if (get_parent (axis))
322 get_parent (axis)->flush_extent_cache (axis);
328 Grob::extent (Grob *refp, Axis a) const
330 Real offset = relative_coordinate (refp, a);
332 if (dim_cache_[a].extent_)
334 real_ext = *dim_cache_[a].extent_;
339 Order is significant: ?-extent may trigger suicide.
343 ? ly_symbol2scm ("X-extent")
344 : ly_symbol2scm ("Y-extent");
346 SCM ext = internal_get_property (ext_sym);
347 if (is_number_pair (ext))
348 real_ext.unite (ly_scm2interval (ext));
352 ? ly_symbol2scm ("minimum-X-extent")
353 : ly_symbol2scm ("minimum-Y-extent");
354 SCM min_ext = internal_get_property (min_ext_sym);
355 if (is_number_pair (min_ext))
356 real_ext.unite (ly_scm2interval (min_ext));
357 ((Grob*)this)->del_property (ext_sym);
358 ((Grob*)this)->dim_cache_[a].extent_ = new Interval (real_ext);
361 real_ext.translate (offset);
366 /****************************************************************
368 ****************************************************************/
370 /* Find the group-element which has both #this# and #s# */
372 Grob::common_refpoint (Grob const *s, Axis a) const
374 /* I don't like the quadratic aspect of this code, but I see no
375 other way. The largest chain of parents might be 10 high or so,
376 so it shouldn't be a real issue. */
377 for (Grob const *c = this; c; c = c->dim_cache_[a].parent_)
378 for (Grob const *d = s; d; d = d->dim_cache_[a].parent_)
386 Grob::set_parent (Grob *g, Axis a)
388 dim_cache_[a].parent_ = g;
392 Grob::get_parent (Axis a) const
394 return dim_cache_[a].parent_;
399 Grob::fixup_refpoint ()
401 for (int a = X_AXIS; a < NO_AXES; a++)
404 Grob *parent = get_parent (ax);
409 if (parent->get_system () != get_system () && get_system ())
411 Grob *newparent = parent->find_broken_piece (get_system ());
412 set_parent (newparent, ax);
415 if (Item *i = dynamic_cast<Item *> (this))
417 Item *parenti = dynamic_cast<Item *> (parent);
421 Direction my_dir = i->break_status_dir ();
422 if (my_dir != parenti->break_status_dir ())
424 Item *newparent = parenti->find_prebroken_piece (my_dir);
425 set_parent (newparent, ax);
433 /****************************************************************
435 ****************************************************************/
437 Grob::warning (String s) const
439 SCM cause = self_scm ();
440 while (Grob *g = unsmob_grob (cause))
441 cause = g->get_property ("cause");
443 if (Music *m = unsmob_music (cause))
444 m->origin ()->warning (s);
453 SCM meta = get_property ("meta");
454 SCM nm = scm_assq (ly_symbol2scm ("name"), meta);
455 nm = (scm_is_pair (nm)) ? scm_cdr (nm) : SCM_EOL;
456 return scm_is_symbol (nm) ? ly_symbol2string (nm) : this->class_name ();
460 Grob::programming_error (String s) const
462 SCM cause = self_scm ();
463 while (Grob *g = unsmob_grob (cause))
464 cause = g->get_property ("cause");
466 s = _f ("programming error: %s", s);
468 if (Music *m = unsmob_music (cause))
469 m->origin ()->message (s);
475 ADD_INTERFACE (Grob, "grob-interface",
476 "A grob represents a piece of music notation\n"
478 "All grobs have an X and Y-position on the page. These X and Y positions\n"
479 "are stored in a relative format, so they can easily be combined by\n"
480 "stacking them, hanging one grob to the side of another, and coupling\n"
481 "them into a grouping objects.\n"
483 "Each grob has a reference point (a.k.a. parent): the position of a grob\n"
484 "is stored relative to that reference point. For example the X-reference\n"
485 "point of a staccato dot usually is the note head that it applies\n"
486 "to. When the note head is moved, the staccato dot moves along\n"
489 "A grob is often associated with a symbol, but some grobs do not print\n"
490 "any symbols. They take care of grouping objects. For example, there is a\n"
491 "separate grob that stacks staves vertically. The @ref{NoteCollision}\n"
492 "is also an abstract grob: it only moves around chords, but doesn't print\n"
495 "Grobs have a properties: Scheme variables, that can be read and set. "
496 "They have two types. Immutable variables "
497 "define the default style and behavior. They are shared between many objects. "
498 "They can be changed using @code{\\override} and @code{\\revert}. "
500 "Mutable properties are variables that are specific to one grob. Typically, "
501 "lists of other objects, or results from computations are stored in"
502 "mutable properties: every call to set-grob-property (or its C++ equivalent) "
503 "sets a mutable property. "
505 "The properties @code{after-line-breaking} and @code{before-line-breaking} "
506 "are dummies that are not user-serviceable. "
515 "after-line-breaking "
516 "axis-group-parent-X "
517 "axis-group-parent-Y "
518 "before-line-breaking "
539 /****************************************************************
541 ****************************************************************/
545 grob_stencil_extent (Grob *me, Axis a)
547 Stencil *m = me->get_stencil ();
551 return ly_interval2scm (e);
555 MAKE_SCHEME_CALLBACK (Grob, stencil_height, 1);
557 Grob::stencil_height (SCM smob)
559 Grob *me = unsmob_grob (smob);
560 return grob_stencil_extent (me, Y_AXIS);
563 MAKE_SCHEME_CALLBACK(Grob, y_parent_positioning, 1);
565 Grob::y_parent_positioning (SCM smob)
567 Grob *me = unsmob_grob (smob);
568 Grob *par = me->get_parent (Y_AXIS);
570 (void) par->get_property ("positioning-done");
572 return scm_from_double (0.0);
576 MAKE_SCHEME_CALLBACK(Grob, x_parent_positioning, 1);
578 Grob::x_parent_positioning (SCM smob)
580 Grob *me = unsmob_grob (smob);
582 Grob *par = me->get_parent (X_AXIS);
584 (void) par->get_property ("positioning-done");
586 return scm_from_double (0.0);
589 MAKE_SCHEME_CALLBACK (Grob, stencil_width, 1);
591 Grob::stencil_width (SCM smob)
593 Grob *me = unsmob_grob (smob);
594 return grob_stencil_extent (me, X_AXIS);
600 common_refpoint_of_list (SCM elist, Grob *common, Axis a)
602 for (; scm_is_pair (elist); elist = scm_cdr (elist))
603 if (Grob *s = unsmob_grob (scm_car (elist)))
606 common = common->common_refpoint (s, a);
615 common_refpoint_of_array (Link_array<Grob> const &arr, Grob *common, Axis a)
617 for (int i = arr.size (); i--;)
618 if (Grob *s = arr[i])
621 common = common->common_refpoint (s, a);
630 robust_relative_extent (Grob *me, Grob *refpoint, Axis a)
632 Interval ext = me->extent (refpoint, a);
634 ext.add_point (me->relative_coordinate (refpoint, a));