2 grob.cc -- implement Grob
4 source file of the GNU LilyPond music typesetter
6 (c) 1997--2006 Han-Wen Nienhuys <hanwen@xs4all.nl>
13 #include "input-smob.hh"
14 #include "international.hh"
19 #include "output-def.hh"
20 #include "pointer-group-interface.hh"
25 #include "ly-smobs.icc"
28 Grob::clone (int count) const
30 return new Grob (*this, count);
33 Grob::Grob (SCM basicprops,
34 Object_key const *key)
37 /* FIXME: default should be no callback. */
41 interfaces_ = SCM_EOL;
42 immutable_property_alist_ = basicprops;
43 mutable_property_alist_ = SCM_EOL;
44 object_alist_ = SCM_EOL;
46 /* We do smobify_self () as the first step. Since the object lives
47 on the heap, none of its SCM variables are protected from
48 GC. After smobify_self (), they are. */
52 We always get a new key object for a new grob.
55 ((Object_key *)key_)->unprotect ();
57 SCM meta = get_property ("meta");
58 if (scm_is_pair (meta))
59 interfaces_ = scm_cdr (scm_assq (ly_symbol2scm ("interfaces"), meta));
61 if (get_property_data (ly_symbol2scm ("X-extent")) == SCM_EOL)
62 set_property ("X-extent", Grob::stencil_width_proc);
63 if (get_property_data (ly_symbol2scm ("Y-extent")) == SCM_EOL)
64 set_property ("Y-extent", Grob::stencil_height_proc);
67 Grob::Grob (Grob const &s, int copy_index)
68 : dim_cache_ (s.dim_cache_)
70 key_ = (use_object_keys) ? new Copied_key (s.key_, copy_index) : 0;
71 original_ = (Grob *) & s;
74 immutable_property_alist_ = s.immutable_property_alist_;
75 mutable_property_alist_ = ly_deep_copy (s.mutable_property_alist_);
76 interfaces_ = s.interfaces_;
77 object_alist_ = SCM_EOL;
83 ((Object_key *)key_)->unprotect ();
89 /****************************************************************
91 ****************************************************************/
94 Grob::get_stencil () const
99 SCM stil = get_property ("stencil");
100 return unsmob_stencil (stil);
104 Grob::get_print_stencil () const
106 SCM stil = get_property ("stencil");
109 if (Stencil *m = unsmob_stencil (stil))
112 if (to_boolean (get_property ("transparent")))
113 retval = Stencil (m->extent_box (), SCM_EOL);
116 SCM expr = m->expr ();
117 if (point_and_click_global)
118 expr = scm_list_3 (ly_symbol2scm ("grob-cause"),
121 retval = Stencil (m->extent_box (), expr);
124 /* color support... see interpret_stencil_expression () for more... */
125 SCM color = get_property ("color");
126 if (color != SCM_EOL)
128 m = unsmob_stencil (stil);
129 SCM expr = scm_list_3 (ly_symbol2scm ("color"),
133 retval = Stencil (m->extent_box (), expr);
141 /****************************************************************
143 ****************************************************************/
145 Grob::do_break_processing ()
150 Grob::discretionary_processing ()
155 Grob::get_system () const
162 Grob::handle_broken_dependencies ()
164 Spanner *sp = dynamic_cast<Spanner *> (this);
165 if (original () && sp)
169 /* THIS, SP is the original spanner. We use a special function
170 because some Spanners have enormously long lists in their
171 properties, and a special function fixes FOO */
173 for (SCM s = object_alist_; scm_is_pair (s); s = scm_cdr (s))
174 sp->substitute_one_mutable_property (scm_caar (s), scm_cdar (s));
176 System *system = get_system ();
180 && common_refpoint (system, X_AXIS)
181 && common_refpoint (system, Y_AXIS))
182 substitute_object_links (system->self_scm (), object_alist_);
183 else if (dynamic_cast<System *> (this))
184 substitute_object_links (SCM_UNDEFINED, object_alist_);
186 /* THIS element is `invalid'; it has been removed from all
187 dependencies, so let's junk the element itself.
189 Do not do this for System, since that would remove references
190 to the originals of score-grobs, which get then GC'd (a bad
195 /* Note that we still want references to this element to be
196 rearranged, and not silently thrown away, so we keep pointers like
197 {broken_into_{drul, array}, original}
205 for (int a = X_AXIS; a < NO_AXES; a++)
206 dim_cache_[a].clear ();
208 mutable_property_alist_ = SCM_EOL;
209 object_alist_ = SCM_EOL;
210 immutable_property_alist_ = SCM_EOL;
211 interfaces_ = SCM_EOL;
215 Grob::handle_prebroken_dependencies ()
217 /* Don't do this in the derived method, since we want to keep access to
218 object_alist_ centralized. */
221 Item *it = dynamic_cast<Item *> (this);
222 substitute_object_links (scm_from_int (it->break_status_dir ()),
223 original ()->object_alist_);
228 Grob::find_broken_piece (System *) const
233 /****************************************************************
235 ****************************************************************/
238 Grob::translate_axis (Real y, Axis a)
240 if (isinf (y) || isnan (y))
242 programming_error (_ ("Infinity or NaN encountered"));
246 if (!dim_cache_[a].offset_)
247 dim_cache_[a].offset_ = new Real (y);
249 *dim_cache_[a].offset_ += y;
252 /* Find the offset relative to D. If D equals THIS, then it is 0.
253 Otherwise, it recursively defd as
255 OFFSET_ + PARENT_L_->relative_coordinate (D) */
257 Grob::relative_coordinate (Grob const *refp, Axis a) const
262 /* We catch PARENT_L_ == nil case with this, but we crash if we did
263 not ask for the absolute coordinate (ie. REFP == nil.) */
264 Real off = get_offset (a);
265 if (refp == dim_cache_[a].parent_)
268 off += dim_cache_[a].parent_->relative_coordinate (refp, a);
273 /* Invoke callbacks to get offset relative to parent. */
275 Grob::get_offset (Axis a) const
277 if (dim_cache_[a].offset_)
278 return *dim_cache_[a].offset_;
280 Grob *me = (Grob *) this;
282 SCM sym = axis_offset_symbol (a);
283 me->dim_cache_[a].offset_ = new Real (0.0);
286 UGH: can't fold next 2 statements together. Apparently GCC thinks
287 dim_cache_[a].offset_ is unaliased.
289 Real off = robust_scm2double (internal_get_property (sym), 0.0);
290 if (me->dim_cache_[a].offset_)
292 *me->dim_cache_[a].offset_ += off;
293 me->del_property (sym);
294 return *me->dim_cache_[a].offset_;
301 /****************************************************************
303 ****************************************************************/
306 Grob::flush_extent_cache (Axis axis)
308 if (dim_cache_[axis].extent_)
311 Ugh, this is not accurate; will flush property, causing
312 callback to be called if.
314 del_property ((axis == X_AXIS) ? ly_symbol2scm ("X-extent") : ly_symbol2scm ("Y-extent"));
315 delete dim_cache_[axis].extent_;
316 dim_cache_[axis].extent_ = 0;
317 if (get_parent (axis))
318 get_parent (axis)->flush_extent_cache (axis);
324 Grob::extent (Grob *refp, Axis a) const
326 Real offset = relative_coordinate (refp, a);
328 if (dim_cache_[a].extent_)
330 real_ext = *dim_cache_[a].extent_;
335 Order is significant: ?-extent may trigger suicide.
339 ? ly_symbol2scm ("X-extent")
340 : ly_symbol2scm ("Y-extent");
342 SCM ext = internal_get_property (ext_sym);
343 if (is_number_pair (ext))
344 real_ext.unite (ly_scm2interval (ext));
348 ? ly_symbol2scm ("minimum-X-extent")
349 : ly_symbol2scm ("minimum-Y-extent");
350 SCM min_ext = internal_get_property (min_ext_sym);
351 if (is_number_pair (min_ext))
352 real_ext.unite (ly_scm2interval (min_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 (vector<Grob*> const &arr, Grob *common, Axis a)
613 for (vsize 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));