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 "align-interface.hh"
14 #include "input-smob.hh"
15 #include "international.hh"
20 #include "output-def.hh"
21 #include "pointer-group-interface.hh"
26 #include "ly-smobs.icc"
29 Grob::clone (int count) const
31 return new Grob (*this, count);
34 Grob::Grob (SCM basicprops,
35 Object_key const *key)
38 /* FIXME: default should be no callback. */
42 interfaces_ = SCM_EOL;
43 immutable_property_alist_ = basicprops;
44 mutable_property_alist_ = SCM_EOL;
45 object_alist_ = SCM_EOL;
47 /* We do smobify_self () as the first step. Since the object lives
48 on the heap, none of its SCM variables are protected from
49 GC. After smobify_self (), they are. */
53 We always get a new key object for a new grob.
56 ((Object_key *)key_)->unprotect ();
58 SCM meta = get_property ("meta");
59 if (scm_is_pair (meta))
60 interfaces_ = scm_cdr (scm_assq (ly_symbol2scm ("interfaces"), meta));
62 if (get_property_data (ly_symbol2scm ("X-extent")) == SCM_EOL)
63 set_property ("X-extent", Grob::stencil_width_proc);
64 if (get_property_data (ly_symbol2scm ("Y-extent")) == SCM_EOL)
65 set_property ("Y-extent", Grob::stencil_height_proc);
68 Grob::Grob (Grob const &s, int copy_index)
69 : dim_cache_ (s.dim_cache_)
71 key_ = (use_object_keys) ? new Copied_key (s.key_, copy_index) : 0;
72 original_ = (Grob *) & s;
75 immutable_property_alist_ = s.immutable_property_alist_;
76 mutable_property_alist_ = ly_deep_copy (s.mutable_property_alist_);
77 interfaces_ = s.interfaces_;
78 object_alist_ = SCM_EOL;
84 ((Object_key *)key_)->unprotect ();
90 /****************************************************************
92 ****************************************************************/
95 Grob::get_stencil () const
100 SCM stil = get_property ("stencil");
101 return unsmob_stencil (stil);
105 Grob::get_print_stencil () const
107 SCM stil = get_property ("stencil");
110 if (Stencil *m = unsmob_stencil (stil))
113 if (to_boolean (get_property ("transparent")))
114 retval = Stencil (m->extent_box (), SCM_EOL);
117 SCM expr = m->expr ();
118 expr = scm_list_3 (ly_symbol2scm ("grob-cause"),
121 retval = Stencil (m->extent_box (), expr);
123 SCM rot = get_property ("rotation");
124 if (scm_is_pair (rot))
126 Real angle = scm_to_double (scm_car (rot));
127 Real x = scm_to_double (scm_cadr (rot));
128 Real y = scm_to_double (scm_caddr (rot));
130 retval.rotate (angle, Offset (x, y));
133 /* color support... see interpret_stencil_expression () for more... */
134 SCM color = get_property ("color");
135 if (color != SCM_EOL)
137 m = unsmob_stencil (stil);
138 SCM expr = scm_list_3 (ly_symbol2scm ("color"),
142 retval = Stencil (m->extent_box (), expr);
150 /****************************************************************
152 ****************************************************************/
154 Grob::do_break_processing ()
159 Grob::discretionary_processing ()
164 Grob::get_system () const
171 Grob::handle_broken_dependencies ()
173 Spanner *sp = dynamic_cast<Spanner *> (this);
174 if (original () && sp)
178 /* THIS, SP is the original spanner. We use a special function
179 because some Spanners have enormously long lists in their
180 properties, and a special function fixes FOO */
182 for (SCM s = object_alist_; scm_is_pair (s); s = scm_cdr (s))
183 sp->substitute_one_mutable_property (scm_caar (s), scm_cdar (s));
185 System *system = get_system ();
189 && common_refpoint (system, X_AXIS)
190 && common_refpoint (system, Y_AXIS))
191 substitute_object_links (system->self_scm (), object_alist_);
192 else if (dynamic_cast<System *> (this))
193 substitute_object_links (SCM_UNDEFINED, object_alist_);
195 /* THIS element is `invalid'; it has been removed from all
196 dependencies, so let's junk the element itself.
198 Do not do this for System, since that would remove references
199 to the originals of score-grobs, which get then GC'd (a bad
204 /* Note that we still want references to this element to be
205 rearranged, and not silently thrown away, so we keep pointers like
206 {broken_into_{drul, array}, original}
214 for (int a = X_AXIS; a < NO_AXES; a++)
215 dim_cache_[a].clear ();
217 mutable_property_alist_ = SCM_EOL;
218 object_alist_ = SCM_EOL;
219 immutable_property_alist_ = SCM_EOL;
220 interfaces_ = SCM_EOL;
224 Grob::handle_prebroken_dependencies ()
226 /* Don't do this in the derived method, since we want to keep access to
227 object_alist_ centralized. */
230 Item *it = dynamic_cast<Item *> (this);
231 substitute_object_links (scm_from_int (it->break_status_dir ()),
232 original ()->object_alist_);
237 Grob::find_broken_piece (System *) const
242 /****************************************************************
244 ****************************************************************/
247 Grob::translate_axis (Real y, Axis a)
249 if (isinf (y) || isnan (y))
251 programming_error (_ ("Infinity or NaN encountered"));
255 if (!dim_cache_[a].offset_)
256 dim_cache_[a].offset_ = new Real (y);
258 *dim_cache_[a].offset_ += y;
261 /* Find the offset relative to D. If D equals THIS, then it is 0.
262 Otherwise, it recursively defd as
264 OFFSET_ + PARENT_L_->relative_coordinate (D) */
266 Grob::relative_coordinate (Grob const *refp, Axis a) const
271 /* We catch PARENT_L_ == nil case with this, but we crash if we did
272 not ask for the absolute coordinate (ie. REFP == nil.) */
273 Real off = get_offset (a);
274 if (refp == dim_cache_[a].parent_)
277 off += dim_cache_[a].parent_->relative_coordinate (refp, a);
283 Grob::pure_relative_y_coordinate (Grob const *refp, int start, int end)
288 SCM pure_off = ly_lily_module_constant ("pure-Y-offset");
291 if (dim_cache_[Y_AXIS].offset_)
292 off = *dim_cache_[Y_AXIS].offset_;
293 else if (ly_is_procedure (pure_off))
295 dim_cache_[Y_AXIS].offset_ = new Real (0.0);
296 off = scm_to_double (scm_apply_3 (pure_off, self_scm (),
297 scm_from_int (start), scm_from_int (end),
299 delete dim_cache_[Y_AXIS].offset_;
300 dim_cache_[Y_AXIS].offset_ = 0;
303 /* we simulate positioning-done if we are the child of a VerticalAlignment */
304 Grob *p = get_parent (Y_AXIS);
306 if (Align_interface::has_interface (p))
307 trans = Align_interface::get_pure_child_y_translation (p, this, start, end);
310 + dim_cache_[Y_AXIS].parent_->pure_relative_y_coordinate (refp, start, end);
313 /* Invoke callbacks to get offset relative to parent. */
315 Grob::get_offset (Axis a) const
317 if (dim_cache_[a].offset_)
318 return *dim_cache_[a].offset_;
320 Grob *me = (Grob *) this;
322 SCM sym = axis_offset_symbol (a);
323 me->dim_cache_[a].offset_ = new Real (0.0);
326 UGH: can't fold next 2 statements together. Apparently GCC thinks
327 dim_cache_[a].offset_ is unaliased.
329 Real off = robust_scm2double (internal_get_property (sym), 0.0);
330 if (me->dim_cache_[a].offset_)
332 *me->dim_cache_[a].offset_ += off;
333 me->del_property (sym);
334 return *me->dim_cache_[a].offset_;
341 Grob::maybe_pure_coordinate (Grob const *refp, Axis a, bool pure, int start, int end)
343 if (pure && a != Y_AXIS)
344 programming_error ("tried to get pure X-offset");
345 return (pure && a == Y_AXIS) ? pure_relative_y_coordinate (refp, start, end)
346 : relative_coordinate (refp, a);
349 /****************************************************************
351 ****************************************************************/
354 Grob::flush_extent_cache (Axis axis)
356 if (dim_cache_[axis].extent_)
359 Ugh, this is not accurate; will flush property, causing
360 callback to be called if.
362 del_property ((axis == X_AXIS) ? ly_symbol2scm ("X-extent") : ly_symbol2scm ("Y-extent"));
363 delete dim_cache_[axis].extent_;
364 dim_cache_[axis].extent_ = 0;
365 if (get_parent (axis))
366 get_parent (axis)->flush_extent_cache (axis);
372 Grob::extent (Grob *refp, Axis a) const
374 Real offset = relative_coordinate (refp, a);
376 if (dim_cache_[a].extent_)
378 real_ext = *dim_cache_[a].extent_;
383 Order is significant: ?-extent may trigger suicide.
387 ? ly_symbol2scm ("X-extent")
388 : ly_symbol2scm ("Y-extent");
390 SCM ext = internal_get_property (ext_sym);
391 if (is_number_pair (ext))
392 real_ext.unite (ly_scm2interval (ext));
396 ? ly_symbol2scm ("minimum-X-extent")
397 : ly_symbol2scm ("minimum-Y-extent");
398 SCM min_ext = internal_get_property (min_ext_sym);
399 if (is_number_pair (min_ext))
400 real_ext.unite (ly_scm2interval (min_ext));
401 ((Grob*)this)->dim_cache_[a].extent_ = new Interval (real_ext);
404 real_ext.translate (offset);
410 Grob::pure_height (Grob *refp, int start, int end)
412 SCM pure_height = ly_lily_module_constant ("pure-Y-extent");
415 if (ly_is_procedure (pure_height))
416 iv = ly_scm2interval (scm_apply_3 (pure_height, self_scm (),
417 scm_from_int (start), scm_from_int (end),
419 Real offset = pure_relative_y_coordinate (refp, start, end);
421 iv.translate (offset);
426 Grob::maybe_pure_extent (Grob *refp, Axis a, bool pure, int start, int end)
428 if (pure && a != Y_AXIS)
429 programming_error ("tried to get pure width");
430 return (pure && a == Y_AXIS) ? pure_height (refp, start, end) : extent (refp, a);
434 Grob::spanned_rank_iv ()
436 return Interval_t<int> (INT_MIN, INT_MAX);
439 /****************************************************************
441 ****************************************************************/
443 /* Find the group-element which has both #this# and #s# */
445 Grob::common_refpoint (Grob const *s, Axis a) const
447 /* I don't like the quadratic aspect of this code, but I see no
448 other way. The largest chain of parents might be 10 high or so,
449 so it shouldn't be a real issue. */
450 for (Grob const *c = this; c; c = c->dim_cache_[a].parent_)
451 for (Grob const *d = s; d; d = d->dim_cache_[a].parent_)
459 Grob::set_parent (Grob *g, Axis a)
461 dim_cache_[a].parent_ = g;
465 Grob::get_parent (Axis a) const
467 return dim_cache_[a].parent_;
472 Grob::fixup_refpoint ()
474 for (int a = X_AXIS; a < NO_AXES; a++)
477 Grob *parent = get_parent (ax);
482 if (parent->get_system () != get_system () && get_system ())
484 Grob *newparent = parent->find_broken_piece (get_system ());
485 set_parent (newparent, ax);
488 if (Item *i = dynamic_cast<Item *> (this))
490 Item *parenti = dynamic_cast<Item *> (parent);
494 Direction my_dir = i->break_status_dir ();
495 if (my_dir != parenti->break_status_dir ())
497 Item *newparent = parenti->find_prebroken_piece (my_dir);
498 set_parent (newparent, ax);
506 /****************************************************************
508 ****************************************************************/
510 Grob::warning (string s) const
512 SCM cause = self_scm ();
513 while (Grob *g = unsmob_grob (cause))
514 cause = g->get_property ("cause");
516 if (Music *m = unsmob_music (cause))
517 m->origin ()->warning (s);
526 SCM meta = get_property ("meta");
527 SCM nm = scm_assq (ly_symbol2scm ("name"), meta);
528 nm = (scm_is_pair (nm)) ? scm_cdr (nm) : SCM_EOL;
529 return scm_is_symbol (nm) ? ly_symbol2string (nm) : this->class_name ();
533 Grob::programming_error (string s) const
535 SCM cause = self_scm ();
536 while (Grob *g = unsmob_grob (cause))
537 cause = g->get_property ("cause");
539 s = _f ("programming error: %s", s);
541 if (Music *m = unsmob_music (cause))
542 m->origin ()->message (s);
548 ADD_INTERFACE (Grob, "grob-interface",
549 "A grob represents a piece of music notation\n"
551 "All grobs have an X and Y-position on the page. These X and Y positions\n"
552 "are stored in a relative format, so they can easily be combined by\n"
553 "stacking them, hanging one grob to the side of another, and coupling\n"
554 "them into a grouping objects.\n"
556 "Each grob has a reference point (a.k.a. parent): the position of a grob\n"
557 "is stored relative to that reference point. For example the X-reference\n"
558 "point of a staccato dot usually is the note head that it applies\n"
559 "to. When the note head is moved, the staccato dot moves along\n"
562 "A grob is often associated with a symbol, but some grobs do not print\n"
563 "any symbols. They take care of grouping objects. For example, there is a\n"
564 "separate grob that stacks staves vertically. The @ref{NoteCollision}\n"
565 "is also an abstract grob: it only moves around chords, but doesn't print\n"
568 "Grobs have a properties: Scheme variables, that can be read and set. "
569 "They have two types. Immutable variables "
570 "define the default style and behavior. They are shared between many objects. "
571 "They can be changed using @code{\\override} and @code{\\revert}. "
573 "Mutable properties are variables that are specific to one grob. Typically, "
574 "lists of other objects, or results from computations are stored in"
575 "mutable properties: every call to set-grob-property (or its C++ equivalent) "
576 "sets a mutable property. "
578 "The properties @code{after-line-breaking} and @code{before-line-breaking} "
579 "are dummies that are not user-serviceable. "
588 "after-line-breaking "
589 "axis-group-parent-X "
590 "axis-group-parent-Y "
591 "before-line-breaking "
613 /****************************************************************
615 ****************************************************************/
619 grob_stencil_extent (Grob *me, Axis a)
621 Stencil *m = me->get_stencil ();
625 return ly_interval2scm (e);
629 MAKE_SCHEME_CALLBACK (Grob, stencil_height, 1);
631 Grob::stencil_height (SCM smob)
633 Grob *me = unsmob_grob (smob);
634 return grob_stencil_extent (me, Y_AXIS);
637 MAKE_SCHEME_CALLBACK(Grob, y_parent_positioning, 1);
639 Grob::y_parent_positioning (SCM smob)
641 Grob *me = unsmob_grob (smob);
642 Grob *par = me->get_parent (Y_AXIS);
644 (void) par->get_property ("positioning-done");
646 return scm_from_double (0.0);
650 MAKE_SCHEME_CALLBACK(Grob, x_parent_positioning, 1);
652 Grob::x_parent_positioning (SCM smob)
654 Grob *me = unsmob_grob (smob);
656 Grob *par = me->get_parent (X_AXIS);
658 (void) par->get_property ("positioning-done");
660 return scm_from_double (0.0);
663 MAKE_SCHEME_CALLBACK (Grob, stencil_width, 1);
665 Grob::stencil_width (SCM smob)
667 Grob *me = unsmob_grob (smob);
668 return grob_stencil_extent (me, X_AXIS);
674 common_refpoint_of_list (SCM elist, Grob *common, Axis a)
676 for (; scm_is_pair (elist); elist = scm_cdr (elist))
677 if (Grob *s = unsmob_grob (scm_car (elist)))
680 common = common->common_refpoint (s, a);
689 common_refpoint_of_array (vector<Grob*> const &arr, Grob *common, Axis a)
691 for (vsize i = arr.size (); i--;)
692 if (Grob *s = arr[i])
695 common = common->common_refpoint (s, a);
704 robust_relative_extent (Grob *me, Grob *refpoint, Axis a)
706 Interval ext = me->extent (refpoint, a);
708 ext.add_point (me->relative_coordinate (refpoint, a));