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);
123 SCM rot = get_property ("rotation");
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);
282 /* Invoke callbacks to get offset relative to parent. */
284 Grob::get_offset (Axis a) const
286 if (dim_cache_[a].offset_)
287 return *dim_cache_[a].offset_;
289 Grob *me = (Grob *) this;
291 SCM sym = axis_offset_symbol (a);
292 me->dim_cache_[a].offset_ = new Real (0.0);
295 UGH: can't fold next 2 statements together. Apparently GCC thinks
296 dim_cache_[a].offset_ is unaliased.
298 Real off = robust_scm2double (internal_get_property (sym), 0.0);
299 if (me->dim_cache_[a].offset_)
301 *me->dim_cache_[a].offset_ += off;
302 me->del_property (sym);
303 return *me->dim_cache_[a].offset_;
310 /****************************************************************
312 ****************************************************************/
315 Grob::flush_extent_cache (Axis axis)
317 if (dim_cache_[axis].extent_)
320 Ugh, this is not accurate; will flush property, causing
321 callback to be called if.
323 del_property ((axis == X_AXIS) ? ly_symbol2scm ("X-extent") : ly_symbol2scm ("Y-extent"));
324 delete dim_cache_[axis].extent_;
325 dim_cache_[axis].extent_ = 0;
326 if (get_parent (axis))
327 get_parent (axis)->flush_extent_cache (axis);
333 Grob::extent (Grob *refp, Axis a) const
335 Real offset = relative_coordinate (refp, a);
337 if (dim_cache_[a].extent_)
339 real_ext = *dim_cache_[a].extent_;
344 Order is significant: ?-extent may trigger suicide.
348 ? ly_symbol2scm ("X-extent")
349 : ly_symbol2scm ("Y-extent");
351 SCM ext = internal_get_property (ext_sym);
352 if (is_number_pair (ext))
353 real_ext.unite (ly_scm2interval (ext));
357 ? ly_symbol2scm ("minimum-X-extent")
358 : ly_symbol2scm ("minimum-Y-extent");
359 SCM min_ext = internal_get_property (min_ext_sym);
360 if (is_number_pair (min_ext))
361 real_ext.unite (ly_scm2interval (min_ext));
362 ((Grob*)this)->del_property (ext_sym);
363 ((Grob*)this)->dim_cache_[a].extent_ = new Interval (real_ext);
366 real_ext.translate (offset);
371 /****************************************************************
373 ****************************************************************/
375 /* Find the group-element which has both #this# and #s# */
377 Grob::common_refpoint (Grob const *s, Axis a) const
379 /* I don't like the quadratic aspect of this code, but I see no
380 other way. The largest chain of parents might be 10 high or so,
381 so it shouldn't be a real issue. */
382 for (Grob const *c = this; c; c = c->dim_cache_[a].parent_)
383 for (Grob const *d = s; d; d = d->dim_cache_[a].parent_)
391 Grob::set_parent (Grob *g, Axis a)
393 dim_cache_[a].parent_ = g;
397 Grob::get_parent (Axis a) const
399 return dim_cache_[a].parent_;
404 Grob::fixup_refpoint ()
406 for (int a = X_AXIS; a < NO_AXES; a++)
409 Grob *parent = get_parent (ax);
414 if (parent->get_system () != get_system () && get_system ())
416 Grob *newparent = parent->find_broken_piece (get_system ());
417 set_parent (newparent, ax);
420 if (Item *i = dynamic_cast<Item *> (this))
422 Item *parenti = dynamic_cast<Item *> (parent);
426 Direction my_dir = i->break_status_dir ();
427 if (my_dir != parenti->break_status_dir ())
429 Item *newparent = parenti->find_prebroken_piece (my_dir);
430 set_parent (newparent, ax);
438 /****************************************************************
440 ****************************************************************/
442 Grob::warning (string s) const
444 SCM cause = self_scm ();
445 while (Grob *g = unsmob_grob (cause))
446 cause = g->get_property ("cause");
448 if (Music *m = unsmob_music (cause))
449 m->origin ()->warning (s);
458 SCM meta = get_property ("meta");
459 SCM nm = scm_assq (ly_symbol2scm ("name"), meta);
460 nm = (scm_is_pair (nm)) ? scm_cdr (nm) : SCM_EOL;
461 return scm_is_symbol (nm) ? ly_symbol2string (nm) : this->class_name ();
465 Grob::programming_error (string s) const
467 SCM cause = self_scm ();
468 while (Grob *g = unsmob_grob (cause))
469 cause = g->get_property ("cause");
471 s = _f ("programming error: %s", s);
473 if (Music *m = unsmob_music (cause))
474 m->origin ()->message (s);
480 ADD_INTERFACE (Grob, "grob-interface",
481 "A grob represents a piece of music notation\n"
483 "All grobs have an X and Y-position on the page. These X and Y positions\n"
484 "are stored in a relative format, so they can easily be combined by\n"
485 "stacking them, hanging one grob to the side of another, and coupling\n"
486 "them into a grouping objects.\n"
488 "Each grob has a reference point (a.k.a. parent): the position of a grob\n"
489 "is stored relative to that reference point. For example the X-reference\n"
490 "point of a staccato dot usually is the note head that it applies\n"
491 "to. When the note head is moved, the staccato dot moves along\n"
494 "A grob is often associated with a symbol, but some grobs do not print\n"
495 "any symbols. They take care of grouping objects. For example, there is a\n"
496 "separate grob that stacks staves vertically. The @ref{NoteCollision}\n"
497 "is also an abstract grob: it only moves around chords, but doesn't print\n"
500 "Grobs have a properties: Scheme variables, that can be read and set. "
501 "They have two types. Immutable variables "
502 "define the default style and behavior. They are shared between many objects. "
503 "They can be changed using @code{\\override} and @code{\\revert}. "
505 "Mutable properties are variables that are specific to one grob. Typically, "
506 "lists of other objects, or results from computations are stored in"
507 "mutable properties: every call to set-grob-property (or its C++ equivalent) "
508 "sets a mutable property. "
510 "The properties @code{after-line-breaking} and @code{before-line-breaking} "
511 "are dummies that are not user-serviceable. "
520 "after-line-breaking "
521 "axis-group-parent-X "
522 "axis-group-parent-Y "
523 "before-line-breaking "
545 /****************************************************************
547 ****************************************************************/
551 grob_stencil_extent (Grob *me, Axis a)
553 Stencil *m = me->get_stencil ();
557 return ly_interval2scm (e);
561 MAKE_SCHEME_CALLBACK (Grob, stencil_height, 1);
563 Grob::stencil_height (SCM smob)
565 Grob *me = unsmob_grob (smob);
566 return grob_stencil_extent (me, Y_AXIS);
569 MAKE_SCHEME_CALLBACK(Grob, y_parent_positioning, 1);
571 Grob::y_parent_positioning (SCM smob)
573 Grob *me = unsmob_grob (smob);
574 Grob *par = me->get_parent (Y_AXIS);
576 (void) par->get_property ("positioning-done");
578 return scm_from_double (0.0);
582 MAKE_SCHEME_CALLBACK(Grob, x_parent_positioning, 1);
584 Grob::x_parent_positioning (SCM smob)
586 Grob *me = unsmob_grob (smob);
588 Grob *par = me->get_parent (X_AXIS);
590 (void) par->get_property ("positioning-done");
592 return scm_from_double (0.0);
595 MAKE_SCHEME_CALLBACK (Grob, stencil_width, 1);
597 Grob::stencil_width (SCM smob)
599 Grob *me = unsmob_grob (smob);
600 return grob_stencil_extent (me, X_AXIS);
606 common_refpoint_of_list (SCM elist, Grob *common, Axis a)
608 for (; scm_is_pair (elist); elist = scm_cdr (elist))
609 if (Grob *s = unsmob_grob (scm_car (elist)))
612 common = common->common_refpoint (s, a);
621 common_refpoint_of_array (vector<Grob*> const &arr, Grob *common, Axis a)
623 for (vsize i = arr.size (); i--;)
624 if (Grob *s = arr[i])
627 common = common->common_refpoint (s, a);
636 robust_relative_extent (Grob *me, Grob *refpoint, Axis a)
638 Interval ext = me->extent (refpoint, a);
640 ext.add_point (me->relative_coordinate (refpoint, a));