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 mutable_property_alist_ = SCM_EOL;
214 object_alist_ = SCM_EOL;
215 immutable_property_alist_ = SCM_EOL;
216 interfaces_ = SCM_EOL;
220 Grob::handle_prebroken_dependencies ()
222 /* Don't do this in the derived method, since we want to keep access to
223 object_alist_ centralized. */
226 Item *it = dynamic_cast<Item *> (this);
227 substitute_object_links (scm_from_int (it->break_status_dir ()),
228 original ()->object_alist_);
233 Grob::find_broken_piece (System *) const
238 /****************************************************************
240 ****************************************************************/
243 Grob::translate_axis (Real y, Axis a)
245 if (isinf (y) || isnan (y))
247 programming_error (_ ("Infinity or NaN encountered"));
251 if (!dim_cache_[a].offset_)
252 dim_cache_[a].offset_ = new Real (y);
254 *dim_cache_[a].offset_ += y;
257 /* Find the offset relative to D. If D equals THIS, then it is 0.
258 Otherwise, it recursively defd as
260 OFFSET_ + PARENT_L_->relative_coordinate (D) */
262 Grob::relative_coordinate (Grob const *refp, Axis a) const
267 /* We catch PARENT_L_ == nil case with this, but we crash if we did
268 not ask for the absolute coordinate (ie. REFP == nil.) */
269 Real off = get_offset (a);
270 if (refp == dim_cache_[a].parent_)
273 off += dim_cache_[a].parent_->relative_coordinate (refp, a);
278 /* Invoke callbacks to get offset relative to parent. */
280 Grob::get_offset (Axis a) const
282 if (dim_cache_[a].offset_)
283 return *dim_cache_[a].offset_;
285 Grob *me = (Grob *) this;
287 SCM sym = axis_offset_symbol (a);
288 me->dim_cache_[a].offset_ = new Real (0.0);
291 UGH: can't fold next 2 statements together. Apparently GCC thinks
292 dim_cache_[a].offset_ is unaliased.
294 Real off = robust_scm2double (internal_get_property (sym), 0.0);
295 *me->dim_cache_[a].offset_ += off;
297 me->del_property (sym);
298 return *me->dim_cache_[a].offset_;
302 /****************************************************************
304 ****************************************************************/
307 Grob::flush_extent_cache (Axis axis)
309 if (dim_cache_[axis].extent_)
312 Ugh, this is not accurate; will flush property, causing
313 callback to be called if.
315 del_property ((axis == X_AXIS) ? ly_symbol2scm ("X-extent") : ly_symbol2scm ("Y-extent"));
316 delete dim_cache_[axis].extent_;
317 dim_cache_[axis].extent_ = 0;
318 if (get_parent (axis))
319 get_parent (axis)->flush_extent_cache (axis);
325 Grob::extent (Grob *refp, Axis a) const
327 Real offset = relative_coordinate (refp, a);
329 if (dim_cache_[a].extent_)
331 real_ext = *dim_cache_[a].extent_;
337 ? ly_symbol2scm ("minimum-X-extent")
338 : ly_symbol2scm ("minimum-Y-extent");
342 ? ly_symbol2scm ("X-extent")
343 : ly_symbol2scm ("Y-extent");
345 SCM min_ext = internal_get_property (min_ext_sym);
346 SCM ext = internal_get_property (ext_sym);
348 if (is_number_pair (min_ext))
349 real_ext.unite (ly_scm2interval (min_ext));
350 if (is_number_pair (ext))
351 real_ext.unite (ly_scm2interval (ext));
353 ((Grob*)this)->del_property (ext_sym);
354 ((Grob*)this)->dim_cache_[a].extent_ = new Interval (real_ext);
357 real_ext.translate (offset);
362 /****************************************************************
364 ****************************************************************/
366 /* Find the group-element which has both #this# and #s# */
368 Grob::common_refpoint (Grob const *s, Axis a) const
370 /* I don't like the quadratic aspect of this code, but I see no
371 other way. The largest chain of parents might be 10 high or so,
372 so it shouldn't be a real issue. */
373 for (Grob const *c = this; c; c = c->dim_cache_[a].parent_)
374 for (Grob const *d = s; d; d = d->dim_cache_[a].parent_)
382 Grob::set_parent (Grob *g, Axis a)
384 dim_cache_[a].parent_ = g;
388 Grob::get_parent (Axis a) const
390 return dim_cache_[a].parent_;
395 Grob::fixup_refpoint ()
397 for (int a = X_AXIS; a < NO_AXES; a++)
400 Grob *parent = get_parent (ax);
405 if (parent->get_system () != get_system () && get_system ())
407 Grob *newparent = parent->find_broken_piece (get_system ());
408 set_parent (newparent, ax);
411 if (Item *i = dynamic_cast<Item *> (this))
413 Item *parenti = dynamic_cast<Item *> (parent);
417 Direction my_dir = i->break_status_dir ();
418 if (my_dir != parenti->break_status_dir ())
420 Item *newparent = parenti->find_prebroken_piece (my_dir);
421 set_parent (newparent, ax);
429 /****************************************************************
431 ****************************************************************/
433 Grob::warning (String s) const
435 SCM cause = self_scm ();
436 while (Grob *g = unsmob_grob (cause))
437 cause = g->get_property ("cause");
439 if (Music *m = unsmob_music (cause))
440 m->origin ()->warning (s);
449 SCM meta = get_property ("meta");
450 SCM nm = scm_assq (ly_symbol2scm ("name"), meta);
451 nm = (scm_is_pair (nm)) ? scm_cdr (nm) : SCM_EOL;
452 return scm_is_symbol (nm) ? ly_symbol2string (nm) : this->class_name ();
456 Grob::programming_error (String s) const
458 SCM cause = self_scm ();
459 while (Grob *g = unsmob_grob (cause))
460 cause = g->get_property ("cause");
462 s = _f ("programming error: %s", s);
464 if (Music *m = unsmob_music (cause))
465 m->origin ()->message (s);
471 ADD_INTERFACE (Grob, "grob-interface",
472 "A grob represents a piece of music notation\n"
474 "All grobs have an X and Y-position on the page. These X and Y positions\n"
475 "are stored in a relative format, so they can easily be combined by\n"
476 "stacking them, hanging one grob to the side of another, and coupling\n"
477 "them into a grouping objects.\n"
479 "Each grob has a reference point (a.k.a. parent): the position of a grob\n"
480 "is stored relative to that reference point. For example the X-reference\n"
481 "point of a staccato dot usually is the note head that it applies\n"
482 "to. When the note head is moved, the staccato dot moves along\n"
485 "A grob is often associated with a symbol, but some grobs do not print\n"
486 "any symbols. They take care of grouping objects. For example, there is a\n"
487 "separate grob that stacks staves vertically. The @ref{NoteCollision}\n"
488 "is also an abstract grob: it only moves around chords, but doesn't print\n"
491 "Grobs have a properties: Scheme variables, that can be read and set. "
492 "They have two types. Immutable variables "
493 "define the default style and behavior. They are shared between many objects. "
494 "They can be changed using @code{\\override} and @code{\\revert}. "
496 "Mutable properties are variables that are specific to one grob. Typically, "
497 "lists of other objects, or results from computations are stored in"
498 "mutable properties: every call to set-grob-property (or its C++ equivalent) "
499 "sets a mutable property. "
501 "The properties @code{after-line-breaking} and @code{before-line-breaking} "
502 "are dummies that are not user-serviceable. "
511 "after-line-breaking "
512 "axis-group-parent-X "
513 "axis-group-parent-Y "
514 "before-line-breaking "
535 /****************************************************************
537 ****************************************************************/
541 grob_stencil_extent (Grob *me, Axis a)
543 Stencil *m = me->get_stencil ();
547 return ly_interval2scm (e);
551 MAKE_SCHEME_CALLBACK (Grob, stencil_height, 1);
553 Grob::stencil_height (SCM smob)
555 Grob *me = unsmob_grob (smob);
556 return grob_stencil_extent (me, Y_AXIS);
559 MAKE_SCHEME_CALLBACK(Grob, y_parent_positioning, 1);
561 Grob::y_parent_positioning (SCM smob)
563 Grob *me = unsmob_grob (smob);
564 Grob *par = me->get_parent (Y_AXIS);
566 (void) par->get_property ("positioning-done");
568 return scm_from_double (0.0);
572 MAKE_SCHEME_CALLBACK(Grob, x_parent_positioning, 1);
574 Grob::x_parent_positioning (SCM smob)
576 Grob *me = unsmob_grob (smob);
578 Grob *par = me->get_parent (X_AXIS);
580 (void) par->get_property ("positioning-done");
582 return scm_from_double (0.0);
585 MAKE_SCHEME_CALLBACK (Grob, stencil_width, 1);
587 Grob::stencil_width (SCM smob)
589 Grob *me = unsmob_grob (smob);
590 return grob_stencil_extent (me, X_AXIS);
596 common_refpoint_of_list (SCM elist, Grob *common, Axis a)
598 for (; scm_is_pair (elist); elist = scm_cdr (elist))
599 if (Grob *s = unsmob_grob (scm_car (elist)))
602 common = common->common_refpoint (s, a);
611 common_refpoint_of_array (Link_array<Grob> const &arr, Grob *common, Axis a)
613 for (int i = arr.size (); i--;)
614 if (Grob *s = arr[i])
617 common = common->common_refpoint (s, a);
626 robust_relative_extent (Grob *me, Grob *refpoint, Axis a)
628 Interval ext = me->extent (refpoint, a);
630 ext.add_point (me->relative_coordinate (refpoint, a));