2 grob.cc -- implement Grob
4 source file of the GNU LilyPond music typesetter
6 (c) 1997--2004 Han-Wen Nienhuys <hanwen@cs.uu.nl>
14 #include "input-smob.hh"
16 #include "group-interface.hh"
18 #include "paper-score.hh"
30 #include "ly-smobs.icc"
35 remove dynamic_cast<Spanner,Item> and put this code into respective
40 #define INFINITY_MSG "Infinity or NaN encountered"
42 Grob::Grob (SCM basicprops)
45 fixme: default should be no callback.
51 immutable_property_alist_ = basicprops;
52 mutable_property_alist_ = SCM_EOL;
55 We do smobify_self () as the first step. Since the object lives on
56 the heap, none of its SCM variables are protected from GC. After
57 smobify_self (), they are.
63 SCM meta = get_property ("meta");
64 if (ly_c_pair_p (meta))
66 SCM ifs = scm_assoc (ly_symbol2scm ("interfaces"), meta);
69 Switch off interface checks for the moment.
71 bool itc = internal_type_checking_global_b;
72 internal_type_checking_global_b = false;
73 internal_set_property (ly_symbol2scm ("interfaces"), ly_cdr (ifs));
74 internal_type_checking_global_b = itc;
80 destill this into a function, so we can re-init the immutable
81 properties with a new BASICPROPS value after creation. Convenient
82 eg. when using \override with StaffSymbol. */
84 char const*onames[] = {"X-offset-callbacks", "Y-offset-callbacks"};
85 char const*xnames[] = {"X-extent", "Y-extent"};
86 char const*enames[] = {"X-extent-callback", "Y-extent-callback"};
88 for (int a = X_AXIS; a <= Y_AXIS; a++)
90 SCM l = get_property (onames[a]);
92 if (scm_ilength (l) >=0)
94 dim_cache_[a].offset_callbacks_ = l;
95 dim_cache_[a].offsets_left_ = scm_ilength (l);
99 programming_error ("[XY]-offset-callbacks must be a list");
102 SCM cb = get_property (enames[a]);
103 SCM xt = get_property (xnames[a]);
106 Should change default to empty?
108 if (is_number_pair (xt))
110 else if (cb != SCM_BOOL_F
111 && !ly_c_procedure_p (cb) && !ly_c_pair_p (cb)
112 && ly_c_procedure_p (get_property ("print-function")))
113 cb = stencil_extent_proc;
115 dim_cache_[a].dimension_ = cb;
120 Grob::Grob (Grob const&s)
121 : dim_cache_ (s.dim_cache_)
123 original_ = (Grob*) &s;
126 immutable_property_alist_ = s.immutable_property_alist_;
127 mutable_property_alist_ = SCM_EOL;
130 No properties are copied. That is the job of handle_broken_dependencies.
144 MAKE_SCHEME_CALLBACK (Grob,stencil_extent,2);
146 Grob::stencil_extent (SCM element_smob, SCM scm_axis)
148 Grob *s = unsmob_grob (element_smob);
149 Axis a = (Axis) ly_scm2int (scm_axis);
151 Stencil *m = s->get_stencil ();
155 return ly_interval2scm (e);
159 Grob::get_paper () const
161 return pscore_ ? pscore_->paper_ : 0;
166 Recursively track all dependencies of this Grob. The
167 status_ field is used as a mark-field. It is marked with
168 BUSY during execution of this function, and marked with FINAL
171 FUNCPTR is the function to call to update this element.
174 Grob::calculate_dependencies (int final, int busy, SCM funcname)
176 if (status_ >= final)
181 programming_error ("Element is busy, come back later");
187 for (SCM d = get_property ("dependencies"); ly_c_pair_p (d);
190 unsmob_grob (ly_car (d))
191 ->calculate_dependencies (final, busy, funcname);
195 SCM proc = internal_get_property (funcname);
196 if (ly_c_procedure_p (proc))
197 scm_call_1 (proc, this->self_scm ());
203 Grob::get_stencil () const
210 SCM mol = get_property ("stencil");
211 if (unsmob_stencil (mol))
212 return unsmob_stencil (mol);
214 mol = get_uncached_stencil ();
218 Grob *me = (Grob*)this;
219 me->set_property ("stencil", mol);
222 return unsmob_stencil (mol);
226 Grob::get_uncached_stencil ()const
228 SCM proc = get_property ("print-function");
231 if (ly_c_procedure_p (proc))
232 mol = scm_apply_0 (proc, scm_list_n (this->self_scm (), SCM_UNDEFINED));
234 if (Stencil *m = unsmob_stencil (mol))
236 if (to_boolean (get_property ("transparent")))
237 mol = Stencil (m->extent_box (), SCM_EOL).smobbed_copy ();
240 SCM origin = ly_symbol2scm ("no-origin");
242 if (store_locations_global_b)
244 SCM cause = get_property ("cause");
245 if (Music*m = unsmob_music (cause))
247 SCM music_origin = m->get_property ("origin");
248 if (unsmob_input (music_origin))
249 origin = music_origin;
254 mol = Stencil (m->extent_box (),
255 scm_list_n (origin, m->expr (), SCM_UNDEFINED)
269 Grob::do_break_processing ()
274 Grob::get_system () const
280 Grob::add_dependency (Grob*e)
284 Pointer_group_interface::add_grob (this, ly_symbol2scm ("dependencies"),e);
287 programming_error ("Null dependency added");
291 Grob::handle_broken_dependencies ()
293 Spanner * sp = dynamic_cast<Spanner*> (this);
300 This is the original spanner. We use a special function
301 because some Spanners have enormously long lists in their
304 for (SCM s = mutable_property_alist_; ly_c_pair_p (s);
307 sp->substitute_one_mutable_property (ly_caar (s),
312 System *system = get_system ();
315 && system && common_refpoint (system, X_AXIS) && common_refpoint (system, Y_AXIS))
317 substitute_mutable_properties (system ? system->self_scm () : SCM_UNDEFINED,
318 mutable_property_alist_);
320 else if (dynamic_cast <System*> (this))
322 substitute_mutable_properties (SCM_UNDEFINED, mutable_property_alist_);
327 This element is `invalid'; it has been removed from all
328 dependencies, so let's junk the element itself.
330 do not do this for System, since that would remove references
331 to the originals of score-grobs, which get then GC'd (a bad
340 Note that we still want references to this element to be
341 rearranged, and not silently thrown away, so we keep pointers
342 like {broken_into_{drul,array}, original}
351 mutable_property_alist_ = SCM_EOL;
352 immutable_property_alist_ = SCM_EOL;
354 set_extent (SCM_EOL, Y_AXIS);
355 set_extent (SCM_EOL, X_AXIS);
357 for (int a= X_AXIS; a <= Y_AXIS; a++)
359 dim_cache_[a].offset_callbacks_ = SCM_EOL;
360 dim_cache_[a].offsets_left_ = 0;
365 Grob::handle_prebroken_dependencies ()
368 Don't do this in the derived method, since we want to keep access to
369 mutable_property_alist_ centralized.
373 Item * it = dynamic_cast<Item*> (this);
374 substitute_mutable_properties (scm_int2num (it->break_status_dir ()),
375 original_->mutable_property_alist_);
380 Grob::find_broken_piece (System*) const
386 translate in one direction
389 Grob::translate_axis (Real y, Axis a)
391 if (isinf (y) || isnan (y))
392 programming_error (_ (INFINITY_MSG));
395 dim_cache_[a].offset_ += y;
401 Find the offset relative to D. If D equals THIS, then it is 0.
402 Otherwise, it recursively defd as
404 OFFSET_ + PARENT_L_->relative_coordinate (D)
407 Grob::relative_coordinate (Grob const*refp, Axis a) const
413 We catch PARENT_L_ == nil case with this, but we crash if we did
414 not ask for the absolute coordinate (ie. REFP == nil.)
417 if (refp == dim_cache_[a].parent_)
418 return get_offset (a);
420 return get_offset (a) + dim_cache_[a].parent_->relative_coordinate (refp, a);
426 Invoke callbacks to get offset relative to parent.
429 Grob::get_offset (Axis a) const
431 Grob *me = (Grob*) this;
432 while (dim_cache_[a].offsets_left_)
434 int l = --me->dim_cache_[a].offsets_left_;
435 SCM cb = scm_list_ref (dim_cache_[a].offset_callbacks_, scm_int2num (l));
436 SCM retval = scm_call_2 (cb, self_scm (), scm_int2num (a));
438 Real r = ly_scm2double (retval);
439 if (isinf (r) || isnan (r))
441 programming_error (INFINITY_MSG);
444 me->dim_cache_[a].offset_ +=r;
446 return dim_cache_[a].offset_;
451 Grob::is_empty (Axis a)const
453 return ! (ly_c_pair_p (dim_cache_[a].dimension_) ||
454 ly_c_procedure_p (dim_cache_[a].dimension_));
458 Grob::extent (Grob * refp, Axis a) const
460 Real x = relative_coordinate (refp, a);
463 Dimension_cache * d = (Dimension_cache *)&dim_cache_[a];
465 if (ly_c_pair_p (d->dimension_))
467 else if (ly_c_procedure_p (d->dimension_))
470 FIXME: add doco on types, and should typecheck maybe?
472 d->dimension_= scm_call_2 (d->dimension_, self_scm (), scm_int2num (a));
477 if (!ly_c_pair_p (d->dimension_))
480 ext = ly_scm2interval (d->dimension_);
482 SCM extra = get_property (a == X_AXIS
489 if (ly_c_pair_p (extra))
491 ext[BIGGER] += ly_scm2double (ly_cdr (extra));
492 ext[SMALLER] += ly_scm2double (ly_car (extra));
495 extra = get_property (a == X_AXIS
497 : "minimum-Y-extent");
498 if (ly_c_pair_p (extra))
500 ext.unite (Interval (ly_scm2double (ly_car (extra)),
501 ly_scm2double (ly_cdr (extra))));
510 Find the group-element which has both #this# and #s#
513 Grob::common_refpoint (Grob const* s, Axis a) const
516 I don't like the quadratic aspect of this code, but I see no other
517 way. The largest chain of parents might be 10 high or so, so
518 it shouldn't be a real issue. */
519 for (Grob const *c = this; c; c = c->dim_cache_[a].parent_)
520 for (Grob const * d = s; d; d = d->dim_cache_[a].parent_)
529 common_refpoint_of_list (SCM elist, Grob *common, Axis a)
531 for (; ly_c_pair_p (elist); elist = ly_cdr (elist))
533 Grob * s = unsmob_grob (ly_car (elist));
537 common = common->common_refpoint (s, a);
548 common_refpoint_of_array (Link_array<Grob> const &arr, Grob *common, Axis a)
550 for (int i = arr.size () ; i--; )
557 common = common->common_refpoint (s, a);
568 SCM meta = get_property ("meta");
569 SCM nm = scm_assoc (ly_symbol2scm ("name"), meta);
570 nm = (ly_c_pair_p (nm)) ? ly_cdr (nm) : SCM_EOL;
571 return ly_c_symbol_p (nm) ? ly_symbol2string (nm) : classname (this);
575 Grob::add_offset_callback (SCM cb, Axis a)
577 if (!has_offset_callback (cb, a))
579 dim_cache_[a].offset_callbacks_ = scm_cons (cb, dim_cache_[a].offset_callbacks_);
580 dim_cache_[a].offsets_left_ ++;
585 Grob::has_extent_callback (SCM cb, Axis a)const
587 return scm_equal_p (cb, dim_cache_[a].dimension_) == SCM_BOOL_T;
592 Grob::has_offset_callback (SCM cb, Axis a)const
594 return scm_c_memq (cb, dim_cache_[a].offset_callbacks_) != SCM_BOOL_F;
598 Grob::set_extent (SCM dc, Axis a)
600 dim_cache_[a].dimension_ = dc;
604 Grob::set_parent (Grob *g, Axis a)
606 dim_cache_[a].parent_ = g;
609 MAKE_SCHEME_CALLBACK (Grob,fixup_refpoint,1);
611 Grob::fixup_refpoint (SCM smob)
613 Grob *me = unsmob_grob (smob);
614 for (int a = X_AXIS; a < NO_AXES; a ++)
617 Grob * parent = me->get_parent (ax);
622 if (parent->get_system () != me->get_system () && me->get_system ())
624 Grob * newparent = parent->find_broken_piece (me->get_system ());
625 me->set_parent (newparent, ax);
628 if (Item * i = dynamic_cast<Item*> (me))
630 Item *parenti = dynamic_cast<Item*> (parent);
634 Direction my_dir = i->break_status_dir () ;
635 if (my_dir!= parenti->break_status_dir ())
637 Item *newparent = parenti->find_prebroken_piece (my_dir);
638 me->set_parent (newparent, ax);
647 Grob::warning (String s)const
649 SCM cause = self_scm ();
650 while (Grob * g = unsmob_grob (cause))
652 cause = g->get_property ("cause");
655 if (Music *m = unsmob_music (cause))
657 m->origin ()->warning (s);
664 Grob::programming_error (String s)const
666 s = "Programming error: " + s;
671 /****************************************************
673 ****************************************************/
677 IMPLEMENT_SMOBS (Grob);
678 IMPLEMENT_DEFAULT_EQUAL_P (Grob);
681 Grob::mark_smob (SCM ses)
683 Grob * s = (Grob*) SCM_CELL_WORD_1 (ses);
684 scm_gc_mark (s->immutable_property_alist_);
686 for (int a =0 ; a < 2; a++)
688 scm_gc_mark (s->dim_cache_[a].offset_callbacks_);
689 scm_gc_mark (s->dim_cache_[a].dimension_);
692 don't mark the parents. The pointers in the mutable property
693 list form two tree like structures (one for X relations, one
694 for Y relations). Marking these can be done in limited stack
695 space. If we add the parents, we will jump between X and Y in
696 an erratic manner, leading to much more recursion depth (and
697 core dumps if we link to pthreads.)
702 scm_gc_mark (s->original_->self_scm ());
704 s->do_derived_mark ();
705 return s->mutable_property_alist_;
709 Grob::print_smob (SCM s, SCM port, scm_print_state *)
711 Grob *sc = (Grob *) ly_cdr (s);
713 scm_puts ("#<Grob ", port);
714 scm_puts ((char *)sc->name ().to_str0 (), port);
717 don't try to print properties, that is too much hassle.
719 scm_puts (" >", port);
724 Grob::do_derived_mark () const
732 Grob::discretionary_processing ()
737 Grob::internal_has_interface (SCM k)
739 SCM ifs = get_property ("interfaces");
741 return scm_c_memq (k, ifs) != SCM_BOOL_F;
745 /** Return Array of Grobs in SCM list L */
749 Link_array<Grob> arr;
751 for (SCM s = l; ly_c_pair_p (s); s = ly_cdr (s))
754 arr.push (unsmob_grob (e));
761 /** Return SCM list of Grob array A */
763 ly_grobs2scm (Link_array<Grob> a)
766 for (int i = a.size (); i; i--)
767 s = scm_cons (a[i-1]->self_scm (), s);
773 IMPLEMENT_TYPE_P (Grob, "ly:grob?");
775 ADD_INTERFACE (Grob, "grob-interface",
776 "A grob represents a piece of music notation\n"
778 "All grobs have an X and Y-position on the page. These X and Y positions\n"
779 "are stored in a relative format, so they can easily be combined by\n"
780 "stacking them, hanging one grob to the side of another, and coupling\n"
781 "them into a grouping objects.\n"
783 "Each grob has a reference point (a.k.a. parent): the position of a grob\n"
784 "is stored relative to that reference point. For example the X-reference\n"
785 "point of a staccato dot usually is the note head that it applies\n"
786 "to. When the note head is moved, the staccato dot moves along\n"
789 "A grob is often associated with a symbol, but some grobs do not print\n"
790 "any symbols. They take care of grouping objects. For example, there is a\n"
791 "separate grob that stacks staves vertically. The @ref{NoteCollision}\n"
792 "is also an abstract grob: it only moves around chords, but doesn't print\n"
795 "Grobs have a properties: Scheme variables, that can be read and set. "
796 "They have two types. Immutable variables "
797 "define the default style and behavior. They are shared between many objects. "
798 "They can be changed using @code{\\override} and @code{\\revert}. "
800 "Mutable properties are variables that are specific to one grob. Typically, "
801 "lists of other objects, or results from computations are stored in"
802 "mutable properties: every call to set-grob-property (or its C++ equivalent) "
803 "sets a mutable property. "
806 "X-offset-callbacks Y-offset-callbacks X-extent-callback stencil cause "
807 "Y-extent-callback print-function extra-offset spacing-procedure "
808 "staff-symbol interfaces dependencies X-extent Y-extent extra-X-extent "
809 "meta layer before-line-breaking-callback "
810 "after-line-breaking-callback extra-Y-extent minimum-X-extent "
811 // FIXME: page-penalty?
812 "minimum-Y-extent page-penalty transparent "