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 ();
132 MAKE_SCHEME_CALLBACK (Grob, stencil_height, 1);
134 Grob::stencil_height (SCM smob)
136 Grob *me = unsmob_grob (smob);
137 return stencil_extent (me, Y_AXIS);
140 MAKE_SCHEME_CALLBACK (Grob, stencil_width, 1);
142 Grob::stencil_width (SCM smob)
144 Grob *me = unsmob_grob (smob);
145 return stencil_extent (me, X_AXIS);
149 Grob::stencil_extent (Grob *me, Axis a)
151 Stencil *m = me->get_stencil ();
155 return ly_interval2scm (e);
159 robust_relative_extent (Grob *me, Grob *refpoint, Axis a)
161 Interval ext = me->extent (refpoint, a);
163 ext.add_point (me->relative_coordinate (refpoint, a));
169 Grob::get_layout () const
171 return pscore_ ? pscore_->layout () : 0;
175 Grob::get_stencil () const
180 SCM stil = get_property ("stencil");
181 return unsmob_stencil (stil);
185 Grob::get_print_stencil () const
187 SCM stil = get_property ("stencil");
190 if (Stencil *m = unsmob_stencil (stil))
193 if (to_boolean (get_property ("transparent")))
194 retval = Stencil (m->extent_box (), SCM_EOL);
197 SCM expr = m->expr ();
198 if (point_and_click_global)
199 expr = scm_list_3 (ly_symbol2scm ("grob-cause"),
202 retval = Stencil (m->extent_box (), expr);
205 /* color support... see interpret_stencil_expression () for more... */
206 SCM color = get_property ("color");
207 if (color != SCM_EOL)
209 m = unsmob_stencil (stil);
210 SCM expr = scm_list_3 (ly_symbol2scm ("color"),
214 retval = Stencil (m->extent_box (), expr);
226 Grob::do_break_processing ()
231 Grob::get_system () const
238 Grob::handle_broken_dependencies ()
240 Spanner *sp = dynamic_cast<Spanner *> (this);
245 /* THIS, SP is the original spanner. We use a special function
246 because some Spanners have enormously long lists in their
247 properties, and a special function fixes FOO */
249 for (SCM s = object_alist_; scm_is_pair (s); s = scm_cdr (s))
250 sp->substitute_one_mutable_property (scm_caar (s), scm_cdar (s));
252 System *system = get_system ();
256 && common_refpoint (system, X_AXIS)
257 && common_refpoint (system, Y_AXIS))
258 substitute_object_links (system->self_scm (), object_alist_);
259 else if (dynamic_cast<System *> (this))
260 substitute_object_links (SCM_UNDEFINED, object_alist_);
262 /* THIS element is `invalid'; it has been removed from all
263 dependencies, so let's junk the element itself.
265 Do not do this for System, since that would remove references
266 to the originals of score-grobs, which get then GC'd (a bad
271 /* Note that we still want references to this element to be
272 rearranged, and not silently thrown away, so we keep pointers like
273 {broken_into_{drul, array}, original}
281 mutable_property_alist_ = SCM_EOL;
282 object_alist_ = SCM_EOL;
283 immutable_property_alist_ = SCM_EOL;
284 interfaces_ = SCM_EOL;
288 Grob::handle_prebroken_dependencies ()
290 /* Don't do this in the derived method, since we want to keep access to
291 object_alist_ centralized. */
294 Item *it = dynamic_cast<Item *> (this);
295 substitute_object_links (scm_from_int (it->break_status_dir ()),
296 original_->object_alist_);
301 Grob::find_broken_piece (System *) const
306 /* Translate in one direction. */
308 Grob::translate_axis (Real y, Axis a)
310 if (isinf (y) || isnan (y))
312 programming_error (_ (INFINITY_MSG));
316 if (!dim_cache_[a].offset_)
317 dim_cache_[a].offset_ = new Real (y);
319 *dim_cache_[a].offset_ += y;
322 /* Find the offset relative to D. If D equals THIS, then it is 0.
323 Otherwise, it recursively defd as
325 OFFSET_ + PARENT_L_->relative_coordinate (D) */
327 Grob::relative_coordinate (Grob const *refp, Axis a) const
332 /* We catch PARENT_L_ == nil case with this, but we crash if we did
333 not ask for the absolute coordinate (ie. REFP == nil.) */
334 Real off = get_offset (a);
335 if (refp == dim_cache_[a].parent_)
338 off += dim_cache_[a].parent_->relative_coordinate (refp, a);
343 /* Invoke callbacks to get offset relative to parent. */
345 Grob::get_offset (Axis a) const
347 if (dim_cache_[a].offset_)
348 return *dim_cache_[a].offset_;
350 Grob *me = (Grob *) this;
352 me->dim_cache_[a].offset_ = new Real (0.0);
353 Real off = robust_scm2double (internal_get_property (axis_offset_symbol (a)), 0.0);
357 ? ly_symbol2scm ("self-X-offset")
358 : ly_symbol2scm ("self-Y-offset");
360 off += robust_scm2double (internal_get_property (self_off_sym), 0.0);
362 *me->dim_cache_[a].offset_ += off;
364 me->del_property (self_off_sym);
365 me->del_property (axis_offset_symbol (a));
370 Grob::flush_extent_cache (Axis axis)
372 if (dim_cache_[axis].extent_)
375 Ugh, this is not accurate; will flush property, causing
376 callback to be called if.
378 del_property ((axis == X_AXIS) ? ly_symbol2scm ("X-extent") : ly_symbol2scm ("Y-extent"));
379 delete dim_cache_[axis].extent_;
380 dim_cache_[axis].extent_ = 0;
381 if (get_parent (axis))
382 get_parent (axis)->flush_extent_cache (axis);
388 Grob::extent (Grob *refp, Axis a) const
390 Real offset = relative_coordinate (refp, a);
392 if (dim_cache_[a].extent_)
394 real_ext = *dim_cache_[a].extent_;
400 ? ly_symbol2scm ("minimum-X-extent")
401 : ly_symbol2scm ("minimum-Y-extent");
405 ? ly_symbol2scm ("X-extent")
406 : ly_symbol2scm ("Y-extent");
408 SCM min_ext = internal_get_property (min_ext_sym);
409 SCM ext = internal_get_property (ext_sym);
411 if (is_number_pair (min_ext))
412 real_ext.unite (ly_scm2interval (min_ext));
413 if (is_number_pair (ext))
414 real_ext.unite (ly_scm2interval (ext));
416 ((Grob*)this)->del_property (ext_sym);
417 ((Grob*)this)->dim_cache_[a].extent_ = new Interval (real_ext);
420 real_ext.translate (offset);
425 /* Find the group-element which has both #this# and #s# */
427 Grob::common_refpoint (Grob const *s, Axis a) const
429 /* I don't like the quadratic aspect of this code, but I see no
430 other way. The largest chain of parents might be 10 high or so,
431 so it shouldn't be a real issue. */
432 for (Grob const *c = this; c; c = c->dim_cache_[a].parent_)
433 for (Grob const *d = s; d; d = d->dim_cache_[a].parent_)
441 common_refpoint_of_list (SCM elist, Grob *common, Axis a)
443 for (; scm_is_pair (elist); elist = scm_cdr (elist))
444 if (Grob *s = unsmob_grob (scm_car (elist)))
447 common = common->common_refpoint (s, a);
456 common_refpoint_of_array (Link_array<Grob> const &arr, Grob *common, Axis a)
458 for (int i = arr.size (); i--;)
459 if (Grob *s = arr[i])
462 common = common->common_refpoint (s, a);
473 SCM meta = get_property ("meta");
474 SCM nm = scm_assq (ly_symbol2scm ("name"), meta);
475 nm = (scm_is_pair (nm)) ? scm_cdr (nm) : SCM_EOL;
476 return scm_is_symbol (nm) ? ly_symbol2string (nm) : this->class_name ();
480 Grob::set_parent (Grob *g, Axis a)
482 dim_cache_[a].parent_ = g;
486 Grob::fixup_refpoint ()
488 for (int a = X_AXIS; a < NO_AXES; a++)
491 Grob *parent = get_parent (ax);
496 if (parent->get_system () != get_system () && get_system ())
498 Grob *newparent = parent->find_broken_piece (get_system ());
499 set_parent (newparent, ax);
502 if (Item *i = dynamic_cast<Item *> (this))
504 Item *parenti = dynamic_cast<Item *> (parent);
508 Direction my_dir = i->break_status_dir ();
509 if (my_dir != parenti->break_status_dir ())
511 Item *newparent = parenti->find_prebroken_piece (my_dir);
512 set_parent (newparent, ax);
520 Grob::warning (String s) const
522 SCM cause = self_scm ();
523 while (Grob *g = unsmob_grob (cause))
524 cause = g->get_property ("cause");
526 if (Music *m = unsmob_music (cause))
527 m->origin ()->warning (s);
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);
547 Grob::discretionary_processing ()
552 Grob::internal_has_interface (SCM k)
554 return scm_c_memq (k, interfaces_) != SCM_BOOL_F;
558 Grob::get_parent (Axis a) const
560 return dim_cache_[a].parent_;
563 /** Return Array of Grobs in SCM list LST */
565 ly_scm2grobs (SCM lst)
567 Link_array<Grob> arr;
569 for (SCM s = lst; scm_is_pair (s); s = scm_cdr (s))
572 arr.push (unsmob_grob (e));
580 Grob::get_key () const
585 /** Return SCM list of Grob array A */
587 ly_grobs2scm (Link_array<Grob> a)
590 for (int i = a.size (); i; i--)
591 s = scm_cons (a[i - 1]->self_scm (), s);
596 ADD_INTERFACE (Grob, "grob-interface",
597 "A grob represents a piece of music notation\n"
599 "All grobs have an X and Y-position on the page. These X and Y positions\n"
600 "are stored in a relative format, so they can easily be combined by\n"
601 "stacking them, hanging one grob to the side of another, and coupling\n"
602 "them into a grouping objects.\n"
604 "Each grob has a reference point (a.k.a. parent): the position of a grob\n"
605 "is stored relative to that reference point. For example the X-reference\n"
606 "point of a staccato dot usually is the note head that it applies\n"
607 "to. When the note head is moved, the staccato dot moves along\n"
610 "A grob is often associated with a symbol, but some grobs do not print\n"
611 "any symbols. They take care of grouping objects. For example, there is a\n"
612 "separate grob that stacks staves vertically. The @ref{NoteCollision}\n"
613 "is also an abstract grob: it only moves around chords, but doesn't print\n"
616 "Grobs have a properties: Scheme variables, that can be read and set. "
617 "They have two types. Immutable variables "
618 "define the default style and behavior. They are shared between many objects. "
619 "They can be changed using @code{\\override} and @code{\\revert}. "
621 "Mutable properties are variables that are specific to one grob. Typically, "
622 "lists of other objects, or results from computations are stored in"
623 "mutable properties: every call to set-grob-property (or its C++ equivalent) "
624 "sets a mutable property. "
626 "The properties @code{after-line-breaking} and @code{before-line-breaking} "
627 "are dummies that are not user-serviceable. "
633 "X-offset-callbacks "
635 "Y-offset-callbacks "
636 "after-line-breaking "
637 "axis-group-parent-X "
638 "axis-group-parent-Y "
639 "before-line-breaking "
657 #include "simple-closure.hh"
660 UGH : todo -> to different file.
664 axis_offset_symbol (Axis a)
667 ? ly_symbol2scm ("X-offset")
668 : ly_symbol2scm ("Y-offset");
672 axis_parent_positioning (Axis a)
675 ? Grob::x_parent_positioning_proc
676 : Grob::y_parent_positioning_proc;
688 (+ (PROC GROB) (orig-proc GROB))
692 add_offset_callback (Grob *g, SCM proc, Axis a)
694 SCM data = g->get_property_data (axis_offset_symbol (a));
696 if (ly_is_procedure (data))
697 data = ly_make_simple_closure (scm_list_1 (data));
698 else if (is_simple_closure (data))
699 data = simple_closure_expression (data);
700 else if (!scm_is_number (data))
701 data = scm_from_int (0);
703 SCM plus = ly_lily_module_constant ("+");
704 SCM expr = scm_list_3 (plus,
705 ly_make_simple_closure (scm_list_1 (proc)),
707 g->internal_set_property (axis_offset_symbol (a),
708 ly_make_simple_closure (expr));
719 (PROC GROB (orig-proc GROB))
723 chain_offset_callback (Grob *g, SCM proc, Axis a)
725 SCM data = g->get_property_data (axis_offset_symbol (a));
727 if (ly_is_procedure (data))
728 data = ly_make_simple_closure (scm_list_1 (data));
729 else if (is_simple_closure (data))
730 data = simple_closure_expression (data);
731 else if (!scm_is_number (data))
732 data = scm_from_int (0);
734 SCM expr = scm_list_2 (proc, data);
735 g->internal_set_property (axis_offset_symbol (a),
737 // twice: one as a wrapper for grob property routines,
738 // once for the actual delayed binding.
739 ly_make_simple_closure (ly_make_simple_closure (expr)));