2 grob.cc -- implement Grob
4 source file of the GNU LilyPond music typesetter
6 (c) 1997--2002 Han-Wen Nienhuys <hanwen@cs.uu.nl>
14 #include "input-smob.hh"
16 #include "group-interface.hh"
18 #include "paper-score.hh"
19 #include "paper-def.hh"
20 #include "molecule.hh"
26 #include "paper-column.hh"
27 #include "molecule.hh"
29 #include "paper-outputter.hh"
33 #include "ly-smobs.icc"
38 remove dynamic_cast<Spanner,Item> and put this code into respective
43 #define INFINITY_MSG "Infinity or NaN encountered"
45 Grob::Grob (SCM basicprops)
48 fixme: default should be no callback.
54 immutable_property_alist_ = basicprops;
55 mutable_property_alist_ = SCM_EOL;
58 We do smobify_self() as the first step. Since the object lives on
59 the heap, none of its SCM variables are protected from GC. After
60 smobify_self(), they are.
65 SCM meta = get_grob_property ("meta");
68 SCM ifs = scm_assoc (ly_symbol2scm ("interfaces"), meta);
71 do it directly to bypass interface checks.
73 mutable_property_alist_ = gh_cons (gh_cons (ly_symbol2scm ("interfaces"),
75 mutable_property_alist_);
81 destill this into a function, so we can re-init the immutable
82 properties with a new BASICPROPS value after creation. Convenient
83 eg. when using \override with StaffSymbol. */
85 char const*onames[] = {"X-offset-callbacks", "Y-offset-callbacks"};
86 char const*enames[] = {"X-extent-callback", "Y-extent-callback"};
88 for (int a = X_AXIS; a <= Y_AXIS; a++)
90 SCM l = get_grob_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_grob_property (enames[a]);
105 Should change default to be empty?
108 && !gh_procedure_p (cb) && !gh_pair_p (cb)
109 && gh_procedure_p (get_grob_property ("molecule-callback"))
111 cb = molecule_extent_proc;
113 dim_cache_[a].dimension_ = cb;
118 Grob::Grob (Grob const&s)
119 : dim_cache_ (s.dim_cache_)
121 original_l_ = (Grob*) &s;
122 immutable_property_alist_ = s.immutable_property_alist_;
123 mutable_property_alist_ = SCM_EOL;
126 No properties are copied. That is the job of handle_broken_dependencies.
129 status_c_ = s.status_c_;
130 pscore_l_ = s.pscore_l_;
140 do nothing scm-ish and no unprotecting here.
146 extern void check_interfaces_for_property (Grob const *me, SCM sym);
149 Grob::internal_set_grob_property (SCM s, SCM v)
152 if (internal_type_checking_global_b)
154 assert (type_check_assignment (s, v, ly_symbol2scm ("backend-type?")));
155 check_interfaces_for_property(this, s);
160 mutable_property_alist_ = scm_assq_set_x (mutable_property_alist_, s, v);
165 Grob::internal_get_grob_property (SCM sym) const
167 SCM s = scm_sloppy_assq (sym, mutable_property_alist_);
171 s = scm_sloppy_assq (sym, immutable_property_alist_);
174 if (internal_type_checking_global_b && gh_pair_p (s))
176 assert (type_check_assignment (sym, gh_cdr (s), ly_symbol2scm ("backend-type?")));
177 check_interfaces_for_property(this, sym);
181 return (s == SCM_BOOL_F) ? SCM_EOL : ly_cdr (s);
185 Remove the value associated with KEY, and return it. The result is
186 that a next call will yield SCM_EOL (and not the underlying
190 Grob::remove_grob_property (const char* key)
192 SCM val = get_grob_property (key);
194 set_grob_property (key, SCM_EOL);
200 MAKE_SCHEME_CALLBACK (Grob,molecule_extent,2);
202 Grob::molecule_extent (SCM element_smob, SCM scm_axis)
204 Grob *s = unsmob_grob (element_smob);
205 Axis a = (Axis) gh_scm2int (scm_axis);
207 Molecule *m = s->get_molecule ();
211 return ly_interval2scm (e);
214 MAKE_SCHEME_CALLBACK (Grob,preset_extent,2);
216 Grob::preset_extent (SCM element_smob, SCM scm_axis)
218 Grob *s = unsmob_grob (element_smob);
219 Axis a = (Axis) gh_scm2int (scm_axis);
221 SCM ext = s->get_grob_property ((a == X_AXIS)
227 Real l = gh_scm2double (ly_car (ext));
228 Real r = gh_scm2double (ly_cdr (ext));
229 return ly_interval2scm (Interval (l, r));
232 return ly_interval2scm (Interval ());
238 Grob::paper_l () const
240 return pscore_l_ ? pscore_l_->paper_l_ : 0;
244 Grob::calculate_dependencies (int final, int busy, SCM funcname)
246 if (status_c_ >= final)
249 if (status_c_== busy)
251 programming_error ("Element is busy, come back later");
257 for (SCM d = get_grob_property ("dependencies"); gh_pair_p (d);
260 unsmob_grob (ly_car (d))
261 ->calculate_dependencies (final, busy, funcname);
265 SCM proc = internal_get_grob_property (funcname);
266 if (gh_procedure_p (proc))
267 gh_call1 (proc, this->self_scm ());
273 Grob::get_molecule () const
275 if (immutable_property_alist_ == SCM_EOL)
281 SCM mol = get_grob_property ("molecule");
282 if (unsmob_molecule (mol))
283 return unsmob_molecule (mol);
285 mol = get_uncached_molecule ();
287 Grob *me = (Grob*)this;
288 me->set_grob_property ("molecule", mol);
290 return unsmob_molecule (mol);
293 Grob::get_uncached_molecule ()const
295 SCM proc = get_grob_property ("molecule-callback");
298 if (gh_procedure_p (proc))
299 mol = gh_apply (proc, scm_list_n (this->self_scm (), SCM_UNDEFINED));
302 Molecule *m = unsmob_molecule (mol);
304 if (unsmob_molecule (mol))
306 SCM origin = ly_symbol2scm ("no-origin");
308 if (store_locations_global_b){
309 SCM cause = get_grob_property ("cause");
310 if (Music*m = unsmob_music (cause))
312 SCM music_origin = m->get_mus_property ("origin");
313 if (unsmob_input (music_origin))
314 origin = music_origin;
320 mol = Molecule (m->extent_box (),
321 scm_list_n (origin, m->get_expr (), SCM_UNDEFINED)
324 m = unsmob_molecule (mol);
328 transparent retains dimensions of element.
330 if (m && to_boolean (get_grob_property ("transparent")))
331 mol = Molecule (m->extent_box (), SCM_EOL).smobbed_copy ();
342 Grob::do_break_processing ()
352 Grob::line_l () const
358 Grob::add_dependency (Grob*e)
362 Pointer_group_interface::add_grob (this, ly_symbol2scm ("dependencies"),e);
365 programming_error ("Null dependency added");
371 Grob::handle_broken_dependencies ()
373 Spanner * s= dynamic_cast<Spanner*> (this);
374 if (original_l_ && s)
379 for (int i = 0; i< s->broken_into_l_arr_ .size (); i++)
381 Grob * sc = s->broken_into_l_arr_[i];
382 System * l = sc->line_l ();
384 set_break_subsititution (l ? l->self_scm () : SCM_UNDEFINED);
385 sc->mutable_property_alist_ =
386 substitute_mutable_properties (mutable_property_alist_);
392 System *line = line_l ();
394 if (line && common_refpoint (line, X_AXIS) && common_refpoint (line, Y_AXIS))
396 set_break_subsititution (line ? line->self_scm () : SCM_UNDEFINED);
397 mutable_property_alist_ = substitute_mutable_properties (mutable_property_alist_);
399 else if (dynamic_cast <System*> (this))
401 set_break_subsititution (SCM_UNDEFINED);
402 mutable_property_alist_ = substitute_mutable_properties (mutable_property_alist_);
407 This element is `invalid'; it has been removed from all
408 dependencies, so let's junk the element itself.
410 do not do this for System, since that would remove references
411 to the originals of score-grobs, which get then GC'd (a bad
420 Note that we still want references to this element to be
421 rearranged, and not silently thrown away, so we keep pointers
422 like {broken_into_{drul,array}, original}
427 mutable_property_alist_ = SCM_EOL;
428 immutable_property_alist_ = SCM_EOL;
430 set_extent (SCM_EOL, Y_AXIS);
431 set_extent (SCM_EOL, X_AXIS);
433 for (int a= X_AXIS; a <= Y_AXIS; a++)
435 dim_cache_[a].offset_callbacks_ = SCM_EOL;
436 dim_cache_[a].offsets_left_ = 0;
441 Grob::handle_prebroken_dependencies ()
446 Grob::find_broken_piece (System*) const
452 translate in one direction
455 Grob::translate_axis (Real y, Axis a)
457 if (isinf (y) || isnan (y))
458 programming_error (_ (INFINITY_MSG));
461 dim_cache_[a].offset_ += y;
467 Find the offset relative to D. If D equals THIS, then it is 0.
468 Otherwise, it recursively defd as
470 OFFSET_ + PARENT_L_->relative_coordinate (D)
473 Grob::relative_coordinate (Grob const*refp, Axis a) const
479 We catch PARENT_L_ == nil case with this, but we crash if we did
480 not ask for the absolute coordinate (ie. REFP == nil.)
483 if (refp == dim_cache_[a].parent_l_)
484 return get_offset (a);
486 return get_offset (a) + dim_cache_[a].parent_l_->relative_coordinate (refp, a);
492 Invoke callbacks to get offset relative to parent.
495 Grob::get_offset (Axis a) const
497 Grob *me = (Grob*) this;
498 while (dim_cache_[a].offsets_left_)
500 int l = --me->dim_cache_[a].offsets_left_;
501 SCM cb = scm_list_ref (dim_cache_[a].offset_callbacks_, gh_int2scm (l));
502 SCM retval = gh_call2 (cb, self_scm (), gh_int2scm (a));
504 Real r = gh_scm2double (retval);
505 if (isinf (r) || isnan (r))
507 programming_error (INFINITY_MSG);
510 me->dim_cache_[a].offset_ +=r;
512 return dim_cache_[a].offset_;
516 MAKE_SCHEME_CALLBACK (Grob,point_dimension_callback,2);
518 Grob::point_dimension_callback (SCM , SCM)
520 return ly_interval2scm (Interval (0,0));
524 Grob::empty_b (Axis a)const
526 return ! (gh_pair_p (dim_cache_[a].dimension_) ||
527 gh_procedure_p (dim_cache_[a].dimension_));
531 Grob::extent (Grob * refp, Axis a) const
533 Real x = relative_coordinate (refp, a);
536 Dimension_cache * d = (Dimension_cache *)&dim_cache_[a];
538 if (gh_pair_p (d->dimension_))
540 else if (gh_procedure_p (d->dimension_))
543 FIXME: add doco on types, and should typecheck maybe?
545 d->dimension_= gh_call2 (d->dimension_, self_scm (), gh_int2scm (a));
550 if (!gh_pair_p (d->dimension_))
553 ext = ly_scm2interval (d->dimension_);
555 SCM extra = get_grob_property (a == X_AXIS
562 if (gh_pair_p (extra))
564 ext[BIGGER] += gh_scm2double (ly_cdr (extra));
565 ext[SMALLER] += gh_scm2double (ly_car (extra));
568 extra = get_grob_property (a == X_AXIS
570 : "minimum-extent-Y");
571 if (gh_pair_p (extra))
573 ext.unite (Interval (gh_scm2double (ly_car (extra)),
574 gh_scm2double (ly_cdr (extra))));
583 Find the group-element which has both #this# and #s#
586 Grob::common_refpoint (Grob const* s, Axis a) const
589 I don't like the quadratic aspect of this code, but I see no other
590 way. The largest chain of parents might be 10 high or so, so
591 it shouldn't be a real issue. */
592 for (Grob const *c = this; c; c = c->dim_cache_[a].parent_l_)
593 for (Grob const * d = s; d; d = d->dim_cache_[a].parent_l_)
602 common_refpoint_of_list (SCM elist, Grob *common, Axis a)
604 for (; gh_pair_p (elist); elist = ly_cdr (elist))
606 Grob * s = unsmob_grob (ly_car (elist));
610 common = common->common_refpoint (s, a);
621 common_refpoint_of_array (Link_array<Grob> const &arr, Grob *common, Axis a)
623 for (int i = arr.size() ; i--; )
630 common = common->common_refpoint (s, a);
641 SCM meta = get_grob_property ("meta");
642 SCM nm = scm_assoc (ly_symbol2scm ("name"), meta);
643 nm = (gh_pair_p (nm)) ? ly_cdr (nm) : SCM_EOL;
644 return gh_symbol_p (nm) ? ly_symbol2string (nm) : classname (this);
648 Grob::add_offset_callback (SCM cb, Axis a)
650 if (!has_offset_callback_b (cb, a))
652 dim_cache_[a].offset_callbacks_ = gh_cons (cb, dim_cache_[a].offset_callbacks_);
653 dim_cache_[a].offsets_left_ ++;
658 Grob::has_extent_callback_b (SCM cb, Axis a)const
660 return scm_equal_p (cb, dim_cache_[a].dimension_) == SCM_BOOL_T;
665 Grob::has_offset_callback_b (SCM cb, Axis a)const
667 return scm_memq (cb, dim_cache_[a].offset_callbacks_) != SCM_BOOL_F;
671 Grob::set_extent (SCM dc, Axis a)
673 dim_cache_[a].dimension_ =dc;
677 Grob::set_parent (Grob *g, Axis a)
679 dim_cache_[a].parent_l_ = g;
682 MAKE_SCHEME_CALLBACK (Grob,fixup_refpoint,1);
684 Grob::fixup_refpoint (SCM smob)
686 Grob *me = unsmob_grob (smob);
687 for (int a = X_AXIS; a < NO_AXES; a ++)
690 Grob * parent = me->get_parent (ax);
695 if (parent->line_l () != me->line_l () && me->line_l ())
697 Grob * newparent = parent->find_broken_piece (me->line_l ());
698 me->set_parent (newparent, ax);
701 if (Item * i = dynamic_cast<Item*> (me))
703 Item *parenti = dynamic_cast<Item*> (parent);
707 Direction my_dir = i->break_status_dir () ;
708 if (my_dir!= parenti->break_status_dir ())
710 Item *newparent = parenti->find_prebroken_piece (my_dir);
711 me->set_parent (newparent, ax);
720 Grob::warning (String s)const
722 SCM cause = self_scm();
723 while (cause != SCM_EOL && !unsmob_music (cause))
725 Grob * g = unsmob_grob (cause);
726 cause = g->get_grob_property ("cause");
729 if (Music *m = unsmob_music (cause))
731 m->origin()->warning (s);
738 Grob::programming_error (String s)const
740 s = "Programming error: " + s;
745 /****************************************************
747 ****************************************************/
751 IMPLEMENT_SMOBS (Grob);
752 IMPLEMENT_DEFAULT_EQUAL_P (Grob);
755 Grob::mark_smob (SCM ses)
757 Grob * s = (Grob*) SCM_CELL_WORD_1 (ses);
758 scm_gc_mark (s->immutable_property_alist_);
759 scm_gc_mark (s->mutable_property_alist_);
761 for (int a =0 ; a < 2; a++)
763 scm_gc_mark (s->dim_cache_[a].offset_callbacks_);
764 scm_gc_mark (s->dim_cache_[a].dimension_);
767 don't mark the parents. The pointers in the mutable property
768 list form two tree like structures (one for X relations, one
769 for Y relations). Marking these can be done in limited stack
770 space. If we add the parents, we will jump between X and Y in
771 an erratic manner, leading to much more recursion depth (and
772 core dumps if we link to pthreads.
777 scm_gc_mark (s->original_l_->self_scm ());
779 return s->do_derived_mark ();
783 Grob::print_smob (SCM s, SCM port, scm_print_state *)
785 Grob *sc = (Grob *) ly_cdr (s);
787 scm_puts ("#<Grob ", port);
788 scm_puts ((char *)sc->name ().ch_C (), port);
791 don't try to print properties, that is too much hassle.
793 scm_puts (" >", port);
798 Grob::do_derived_mark ()
806 Grob::discretionary_processing ()
811 Grob::internal_has_interface (SCM k)
813 SCM ifs = get_grob_property ("interfaces");
815 return scm_memq (k, ifs) != SCM_BOOL_F;
818 IMPLEMENT_TYPE_P (Grob, "ly-grob?");
820 ADD_INTERFACE (Grob, "grob-interface",
821 "All grobs support this",
822 "X-offset-callbacks Y-offset-callbacks X-extent-callback molecule cause
823 Y-extent-callback molecule-callback extra-offset
825 staff-symbol interfaces dependencies extra-extent-X causes meta
826 layer before-line-breaking-callback after-line-breaking-callback extra-extent-Y minimum-extent-X minimum-extent-Y transparent");