2 grob.cc -- implement Grob
4 source file of the GNU LilyPond music typesetter
6 (c) 1997--2005 Han-Wen Nienhuys <hanwen@cs.uu.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"
34 MAKE_SCHEME_CALLBACK(Grob, y_parent_positioning, 1);
36 Grob::y_parent_positioning (SCM smob)
38 Grob *me = unsmob_grob (smob);
39 Grob *par = me->get_parent (Y_AXIS);
41 (void) par->get_property ("positioning-done");
43 return scm_from_double (0.0);
47 MAKE_SCHEME_CALLBACK(Grob, x_parent_positioning, 1);
49 Grob::x_parent_positioning (SCM smob)
51 Grob *me = unsmob_grob (smob);
53 Grob *par = me->get_parent (X_AXIS);
55 (void) par->get_property ("positioning-done");
57 return scm_from_double (0.0);
61 Grob::clone (int count) const
63 return new Grob (*this, count);
68 - remove dynamic_cast<Spanner, Item> and put this code into respective
72 #define INFINITY_MSG "Infinity or NaN encountered"
74 Grob::Grob (SCM basicprops,
75 Object_key const *key)
78 /* FIXME: default should be no callback. */
82 interfaces_ = SCM_EOL;
83 immutable_property_alist_ = basicprops;
84 mutable_property_alist_ = SCM_EOL;
85 object_alist_ = SCM_EOL;
87 /* We do smobify_self () as the first step. Since the object lives
88 on the heap, none of its SCM variables are protected from
89 GC. After smobify_self (), they are. */
93 We always get a new key object for a new grob.
96 ((Object_key *)key_)->unprotect ();
98 SCM meta = get_property ("meta");
99 if (scm_is_pair (meta))
100 interfaces_ = scm_cdr (scm_assq (ly_symbol2scm ("interfaces"), meta));
102 if (get_property_data (ly_symbol2scm ("X-extent")) == SCM_EOL)
103 set_property ("X-extent", Grob::stencil_width_proc);
104 if (get_property_data (ly_symbol2scm ("Y-extent")) == SCM_EOL)
105 set_property ("Y-extent", Grob::stencil_height_proc);
108 Grob::Grob (Grob const &s, int copy_index)
109 : dim_cache_ (s.dim_cache_)
111 key_ = (use_object_keys) ? new Copied_key (s.key_, copy_index) : 0;
112 original_ = (Grob *) & s;
115 immutable_property_alist_ = s.immutable_property_alist_;
116 mutable_property_alist_ = ly_deep_copy (s.mutable_property_alist_);
117 interfaces_ = s.interfaces_;
118 object_alist_ = SCM_EOL;
124 ((Object_key *)key_)->unprotect ();
133 grob_stencil_extent (Grob *me, Axis a)
135 Stencil *m = me->get_stencil ();
139 return ly_interval2scm (e);
143 MAKE_SCHEME_CALLBACK (Grob, stencil_height, 1);
145 Grob::stencil_height (SCM smob)
147 Grob *me = unsmob_grob (smob);
148 return grob_stencil_extent (me, Y_AXIS);
151 MAKE_SCHEME_CALLBACK (Grob, stencil_width, 1);
153 Grob::stencil_width (SCM smob)
155 Grob *me = unsmob_grob (smob);
156 return grob_stencil_extent (me, X_AXIS);
161 robust_relative_extent (Grob *me, Grob *refpoint, Axis a)
163 Interval ext = me->extent (refpoint, a);
165 ext.add_point (me->relative_coordinate (refpoint, a));
172 Grob::get_stencil () const
177 SCM stil = get_property ("stencil");
178 return unsmob_stencil (stil);
182 Grob::get_print_stencil () const
184 SCM stil = get_property ("stencil");
187 if (Stencil *m = unsmob_stencil (stil))
190 if (to_boolean (get_property ("transparent")))
191 retval = Stencil (m->extent_box (), SCM_EOL);
194 SCM expr = m->expr ();
195 if (point_and_click_global)
196 expr = scm_list_3 (ly_symbol2scm ("grob-cause"),
199 retval = Stencil (m->extent_box (), expr);
202 /* color support... see interpret_stencil_expression () for more... */
203 SCM color = get_property ("color");
204 if (color != SCM_EOL)
206 m = unsmob_stencil (stil);
207 SCM expr = scm_list_3 (ly_symbol2scm ("color"),
211 retval = Stencil (m->extent_box (), expr);
223 Grob::do_break_processing ()
228 Grob::get_system () const
235 Grob::handle_broken_dependencies ()
237 Spanner *sp = dynamic_cast<Spanner *> (this);
238 if (original () && sp)
242 /* THIS, SP is the original spanner. We use a special function
243 because some Spanners have enormously long lists in their
244 properties, and a special function fixes FOO */
246 for (SCM s = object_alist_; scm_is_pair (s); s = scm_cdr (s))
247 sp->substitute_one_mutable_property (scm_caar (s), scm_cdar (s));
249 System *system = get_system ();
253 && common_refpoint (system, X_AXIS)
254 && common_refpoint (system, Y_AXIS))
255 substitute_object_links (system->self_scm (), object_alist_);
256 else if (dynamic_cast<System *> (this))
257 substitute_object_links (SCM_UNDEFINED, object_alist_);
259 /* THIS element is `invalid'; it has been removed from all
260 dependencies, so let's junk the element itself.
262 Do not do this for System, since that would remove references
263 to the originals of score-grobs, which get then GC'd (a bad
268 /* Note that we still want references to this element to be
269 rearranged, and not silently thrown away, so we keep pointers like
270 {broken_into_{drul, array}, original}
278 mutable_property_alist_ = SCM_EOL;
279 object_alist_ = SCM_EOL;
280 immutable_property_alist_ = SCM_EOL;
281 interfaces_ = SCM_EOL;
285 Grob::handle_prebroken_dependencies ()
287 /* Don't do this in the derived method, since we want to keep access to
288 object_alist_ centralized. */
291 Item *it = dynamic_cast<Item *> (this);
292 substitute_object_links (scm_from_int (it->break_status_dir ()),
293 original ()->object_alist_);
298 Grob::find_broken_piece (System *) const
303 /* Translate in one direction. */
305 Grob::translate_axis (Real y, Axis a)
307 if (isinf (y) || isnan (y))
309 programming_error (_ (INFINITY_MSG));
313 if (!dim_cache_[a].offset_)
314 dim_cache_[a].offset_ = new Real (y);
316 *dim_cache_[a].offset_ += y;
319 /* Find the offset relative to D. If D equals THIS, then it is 0.
320 Otherwise, it recursively defd as
322 OFFSET_ + PARENT_L_->relative_coordinate (D) */
324 Grob::relative_coordinate (Grob const *refp, Axis a) const
329 /* We catch PARENT_L_ == nil case with this, but we crash if we did
330 not ask for the absolute coordinate (ie. REFP == nil.) */
331 Real off = get_offset (a);
332 if (refp == dim_cache_[a].parent_)
335 off += dim_cache_[a].parent_->relative_coordinate (refp, a);
340 /* Invoke callbacks to get offset relative to parent. */
342 Grob::get_offset (Axis a) const
344 if (dim_cache_[a].offset_)
345 return *dim_cache_[a].offset_;
347 Grob *me = (Grob *) this;
349 SCM sym = axis_offset_symbol (a);
350 me->dim_cache_[a].offset_ = new Real (0.0);
353 UGH: can't fold next 2 statements together. Apparently GCC thinks
354 dim_cache_[a].offset_ is unaliased.
356 Real off = robust_scm2double (internal_get_property (sym), 0.0);
357 *me->dim_cache_[a].offset_ += off;
359 me->del_property (sym);
360 return *me->dim_cache_[a].offset_;
364 Grob::flush_extent_cache (Axis axis)
366 if (dim_cache_[axis].extent_)
369 Ugh, this is not accurate; will flush property, causing
370 callback to be called if.
372 del_property ((axis == X_AXIS) ? ly_symbol2scm ("X-extent") : ly_symbol2scm ("Y-extent"));
373 delete dim_cache_[axis].extent_;
374 dim_cache_[axis].extent_ = 0;
375 if (get_parent (axis))
376 get_parent (axis)->flush_extent_cache (axis);
382 Grob::extent (Grob *refp, Axis a) const
384 Real offset = relative_coordinate (refp, a);
386 if (dim_cache_[a].extent_)
388 real_ext = *dim_cache_[a].extent_;
394 ? ly_symbol2scm ("minimum-X-extent")
395 : ly_symbol2scm ("minimum-Y-extent");
399 ? ly_symbol2scm ("X-extent")
400 : ly_symbol2scm ("Y-extent");
402 SCM min_ext = internal_get_property (min_ext_sym);
403 SCM ext = internal_get_property (ext_sym);
405 if (is_number_pair (min_ext))
406 real_ext.unite (ly_scm2interval (min_ext));
407 if (is_number_pair (ext))
408 real_ext.unite (ly_scm2interval (ext));
410 ((Grob*)this)->del_property (ext_sym);
411 ((Grob*)this)->dim_cache_[a].extent_ = new Interval (real_ext);
414 real_ext.translate (offset);
419 /* Find the group-element which has both #this# and #s# */
421 Grob::common_refpoint (Grob const *s, Axis a) const
423 /* I don't like the quadratic aspect of this code, but I see no
424 other way. The largest chain of parents might be 10 high or so,
425 so it shouldn't be a real issue. */
426 for (Grob const *c = this; c; c = c->dim_cache_[a].parent_)
427 for (Grob const *d = s; d; d = d->dim_cache_[a].parent_)
435 common_refpoint_of_list (SCM elist, Grob *common, Axis a)
437 for (; scm_is_pair (elist); elist = scm_cdr (elist))
438 if (Grob *s = unsmob_grob (scm_car (elist)))
441 common = common->common_refpoint (s, a);
450 common_refpoint_of_array (Link_array<Grob> const &arr, Grob *common, Axis a)
452 for (int i = arr.size (); i--;)
453 if (Grob *s = arr[i])
456 common = common->common_refpoint (s, a);
467 SCM meta = get_property ("meta");
468 SCM nm = scm_assq (ly_symbol2scm ("name"), meta);
469 nm = (scm_is_pair (nm)) ? scm_cdr (nm) : SCM_EOL;
470 return scm_is_symbol (nm) ? ly_symbol2string (nm) : this->class_name ();
474 Grob::set_parent (Grob *g, Axis a)
476 dim_cache_[a].parent_ = g;
480 Grob::fixup_refpoint ()
482 for (int a = X_AXIS; a < NO_AXES; a++)
485 Grob *parent = get_parent (ax);
490 if (parent->get_system () != get_system () && get_system ())
492 Grob *newparent = parent->find_broken_piece (get_system ());
493 set_parent (newparent, ax);
496 if (Item *i = dynamic_cast<Item *> (this))
498 Item *parenti = dynamic_cast<Item *> (parent);
502 Direction my_dir = i->break_status_dir ();
503 if (my_dir != parenti->break_status_dir ())
505 Item *newparent = parenti->find_prebroken_piece (my_dir);
506 set_parent (newparent, ax);
514 Grob::warning (String s) const
516 SCM cause = self_scm ();
517 while (Grob *g = unsmob_grob (cause))
518 cause = g->get_property ("cause");
520 if (Music *m = unsmob_music (cause))
521 m->origin ()->warning (s);
527 Grob::programming_error (String s) const
529 SCM cause = self_scm ();
530 while (Grob *g = unsmob_grob (cause))
531 cause = g->get_property ("cause");
533 s = _f ("programming error: %s", s);
535 if (Music *m = unsmob_music (cause))
536 m->origin ()->message (s);
541 Grob::discretionary_processing ()
546 Grob::internal_has_interface (SCM k)
548 return scm_c_memq (k, interfaces_) != SCM_BOOL_F;
552 Grob::get_parent (Axis a) const
554 return dim_cache_[a].parent_;
558 ADD_INTERFACE (Grob, "grob-interface",
559 "A grob represents a piece of music notation\n"
561 "All grobs have an X and Y-position on the page. These X and Y positions\n"
562 "are stored in a relative format, so they can easily be combined by\n"
563 "stacking them, hanging one grob to the side of another, and coupling\n"
564 "them into a grouping objects.\n"
566 "Each grob has a reference point (a.k.a. parent): the position of a grob\n"
567 "is stored relative to that reference point. For example the X-reference\n"
568 "point of a staccato dot usually is the note head that it applies\n"
569 "to. When the note head is moved, the staccato dot moves along\n"
572 "A grob is often associated with a symbol, but some grobs do not print\n"
573 "any symbols. They take care of grouping objects. For example, there is a\n"
574 "separate grob that stacks staves vertically. The @ref{NoteCollision}\n"
575 "is also an abstract grob: it only moves around chords, but doesn't print\n"
578 "Grobs have a properties: Scheme variables, that can be read and set. "
579 "They have two types. Immutable variables "
580 "define the default style and behavior. They are shared between many objects. "
581 "They can be changed using @code{\\override} and @code{\\revert}. "
583 "Mutable properties are variables that are specific to one grob. Typically, "
584 "lists of other objects, or results from computations are stored in"
585 "mutable properties: every call to set-grob-property (or its C++ equivalent) "
586 "sets a mutable property. "
588 "The properties @code{after-line-breaking} and @code{before-line-breaking} "
589 "are dummies that are not user-serviceable. "
598 "after-line-breaking "
599 "axis-group-parent-X "
600 "axis-group-parent-Y "
601 "before-line-breaking "