''
[lilypond.git] / lily / grob.cc
blob3121658c01ce40a268ca2e2a781c1d7fdb05cb10
1 /*
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>
7 */
10 #include <string.h>
11 #include <math.h>
13 #include "main.hh"
14 #include "input-smob.hh"
16 #include "group-interface.hh"
17 #include "misc.hh"
18 #include "paper-score.hh"
19 #include "paper-def.hh"
20 #include "molecule.hh"
21 #include "grob.hh"
22 #include "debug.hh"
23 #include "spanner.hh"
24 #include "system.hh"
25 #include "item.hh"
26 #include "paper-column.hh"
27 #include "molecule.hh"
28 #include "misc.hh"
29 #include "paper-outputter.hh"
30 #include "music.hh"
31 #include "item.hh"
33 #include "ly-smobs.icc"
36 TODO:
38 remove dynamic_cast<Spanner,Item> and put this code into respective
39 subclass.
43 #define INFINITY_MSG "Infinity or NaN encountered"
45 Grob::Grob (SCM basicprops)
48 fixme: default should be no callback.
51 pscore_l_=0;
52 status_c_ = 0;
53 original_l_ = 0;
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.
62 smobify_self ();
65 SCM meta = get_grob_property ("meta");
66 if (gh_pair_p (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"),
74 gh_cdr (ifs)),
75 mutable_property_alist_);
79 TODO:
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);
97 else
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?
107 if (cb != SCM_BOOL_F
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_;
132 smobify_self ();
137 Grob::~Grob ()
140 do nothing scm-ish and no unprotecting here.
146 extern void check_interfaces_for_property (Grob const *me, SCM sym);
148 void
149 Grob::internal_set_grob_property (SCM s, SCM v)
151 #ifndef NDEBUG
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);
157 #endif
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_);
168 if (s != SCM_BOOL_F)
169 return ly_cdr (s);
171 s = scm_sloppy_assq (sym, immutable_property_alist_);
173 #ifndef NDEBUG
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);
179 #endif
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
187 `basic' property.
190 Grob::remove_grob_property (const char* key)
192 SCM val = get_grob_property (key);
193 if (val != SCM_EOL)
194 set_grob_property (key, SCM_EOL);
195 return val;
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 ();
208 Interval e ;
209 if (m)
210 e = m->extent (a);
211 return ly_interval2scm (e);
214 MAKE_SCHEME_CALLBACK (Grob,preset_extent,2);
217 Grob::preset_extent (SCM element_smob, SCM scm_axis)
219 Grob *s = unsmob_grob (element_smob);
220 Axis a = (Axis) gh_scm2int (scm_axis);
222 SCM ext = s->get_grob_property ((a == X_AXIS)
223 ? "extent-X"
224 : "extent-Y");
226 if (gh_pair_p (ext))
228 Real l = gh_scm2double (ly_car (ext));
229 Real r = gh_scm2double (ly_cdr (ext));
230 return ly_interval2scm (Interval (l, r));
233 return ly_interval2scm (Interval ());
238 Paper_def*
239 Grob::paper_l () const
241 return pscore_l_ ? pscore_l_->paper_l_ : 0;
244 void
245 Grob::calculate_dependencies (int final, int busy, SCM funcname)
247 if (status_c_ >= final)
248 return;
250 if (status_c_== busy)
252 programming_error ("Element is busy, come back later");
253 return;
256 status_c_= busy;
258 for (SCM d = get_grob_property ("dependencies"); gh_pair_p (d);
259 d = ly_cdr (d))
261 unsmob_grob (ly_car (d))
262 ->calculate_dependencies (final, busy, funcname);
266 SCM proc = internal_get_grob_property (funcname);
267 if (gh_procedure_p (proc))
268 gh_call1 (proc, this->self_scm ());
270 status_c_= final;
273 Molecule *
274 Grob::get_molecule () const
276 if (immutable_property_alist_ == SCM_EOL)
278 return 0;
282 SCM mol = get_grob_property ("molecule");
283 if (unsmob_molecule (mol))
284 return unsmob_molecule (mol);
286 mol = get_uncached_molecule ();
288 Grob *me = (Grob*)this;
289 me->set_grob_property ("molecule", mol);
291 return unsmob_molecule (mol);
294 Grob::get_uncached_molecule ()const
296 SCM proc = get_grob_property ("molecule-callback");
298 SCM mol = SCM_EOL;
299 if (gh_procedure_p (proc))
300 mol = gh_apply (proc, scm_list_n (this->self_scm (), SCM_UNDEFINED));
303 Molecule *m = unsmob_molecule (mol);
305 if (unsmob_molecule (mol))
307 SCM origin = ly_symbol2scm ("no-origin");
309 if (store_locations_global_b){
310 SCM cause = get_grob_property ("cause");
311 if (Music*m = unsmob_music (cause))
313 SCM music_origin = m->get_mus_property ("origin");
314 if (unsmob_input (music_origin))
315 origin = music_origin;
319 // ugr.
321 mol = Molecule (m->extent_box (),
322 scm_list_n (origin, m->get_expr (), SCM_UNDEFINED)
323 ). smobbed_copy ();
325 m = unsmob_molecule (mol);
329 transparent retains dimensions of element.
331 if (m && to_boolean (get_grob_property ("transparent")))
332 mol = Molecule (m->extent_box (), SCM_EOL).smobbed_copy ();
334 return mol;
339 VIRTUAL STUBS
342 void
343 Grob::do_break_processing ()
352 System *
353 Grob::line_l () const
355 return 0;
358 void
359 Grob::add_dependency (Grob*e)
361 if (e)
363 Pointer_group_interface::add_grob (this, ly_symbol2scm ("dependencies"),e);
365 else
366 programming_error ("Null dependency added");
373 Do break substitution in S, using CRITERION. Return new value.
374 CRITERION is either a SMOB pointer to the desired line, or a number
375 representing the break direction. Do not modify SRC.
377 It is rather tightly coded, since it takes a lot of time; it is
378 one of the top functions in the profile.
380 We don't pass break_criterion as a parameter, since it is
381 `constant', but takes up stack space.
386 static SCM break_criterion;
387 void
388 set_break_subsititution (SCM criterion)
390 break_criterion = criterion;
395 TODO: check wether we can do this in-place; now we generate a lot of
396 garbage.
399 do_break_substitution (SCM src)
401 again:
403 if (Grob *sc = unsmob_grob (src))
405 if (SCM_INUMP (break_criterion))
407 Item * i = dynamic_cast<Item*> (sc);
408 Direction d = to_dir (break_criterion);
409 if (i && i->break_status_dir () != d)
411 Item *br = i->find_prebroken_piece (d);
412 return (br) ? br->self_scm () : SCM_UNDEFINED;
415 else
417 System * line
418 = dynamic_cast<System*> (unsmob_grob (break_criterion));
419 if (sc->line_l () != line)
421 sc = sc->find_broken_piece (line);
425 /* now: !sc || (sc && sc->line_l () == line) */
426 if (!sc)
427 return SCM_UNDEFINED;
429 /* now: sc && sc->line_l () == line */
430 if (!line)
431 return sc->self_scm();
433 This was introduced in 1.3.49 as a measure to prevent
434 programming errors. It looks expensive (?).
436 TODO:
438 benchmark , document when (what kind of programming
439 errors) this happens.
441 if (sc->common_refpoint (line, X_AXIS)
442 && sc->common_refpoint (line, Y_AXIS))
444 return sc->self_scm ();
446 return SCM_UNDEFINED;
449 else if (ly_pair_p (src))
452 UGH! breaks on circular lists.
454 SCM newcar = do_break_substitution (ly_car (src));
455 SCM oldcdr = ly_cdr (src);
457 if (newcar == SCM_UNDEFINED
458 && (gh_pair_p (oldcdr) || oldcdr == SCM_EOL))
461 This is tail-recursion, ie.
463 return do_break_substution (cdr);
465 We don't want to rely on the compiler to do this. Without
466 tail-recursion, this easily crashes with a stack overflow. */
467 src = oldcdr;
468 goto again;
471 return scm_cons (newcar, do_break_substitution (oldcdr));
473 else
474 return src;
476 return src;
479 void
480 Grob::handle_broken_dependencies ()
482 Spanner * s= dynamic_cast<Spanner*> (this);
483 if (original_l_ && s)
484 return;
486 if (s)
488 for (int i = 0; i< s->broken_into_l_arr_ .size (); i++)
490 Grob * sc = s->broken_into_l_arr_[i];
491 System * l = sc->line_l ();
493 set_break_subsititution (l ? l->self_scm () : SCM_UNDEFINED);
494 sc->mutable_property_alist_ =
495 do_break_substitution (mutable_property_alist_);
501 System *line = line_l ();
503 if (line && common_refpoint (line, X_AXIS) && common_refpoint (line, Y_AXIS))
505 set_break_subsititution (line ? line->self_scm () : SCM_UNDEFINED);
506 mutable_property_alist_ = do_break_substitution (mutable_property_alist_);
508 else if (dynamic_cast <System*> (this))
510 set_break_subsititution (SCM_UNDEFINED);
511 mutable_property_alist_ = do_break_substitution (mutable_property_alist_);
513 else
516 This element is `invalid'; it has been removed from all
517 dependencies, so let's junk the element itself.
519 do not do this for System, since that would remove
520 references to the originals of score-grobs, which get then GC'd
521 (a bad thing.)
523 suicide ();
528 Note that we still want references to this element to be
529 rearranged, and not silently thrown away, so we keep pointers
530 like {broken_into_{drul,array}, original}
532 void
533 Grob::suicide ()
535 mutable_property_alist_ = SCM_EOL;
536 immutable_property_alist_ = SCM_EOL;
538 set_extent (SCM_EOL, Y_AXIS);
539 set_extent (SCM_EOL, X_AXIS);
541 for (int a= X_AXIS; a <= Y_AXIS; a++)
543 dim_cache_[a].offset_callbacks_ = SCM_EOL;
544 dim_cache_[a].offsets_left_ = 0;
548 void
549 Grob::handle_prebroken_dependencies ()
553 Grob*
554 Grob::find_broken_piece (System*) const
556 return 0;
560 translate in one direction
562 void
563 Grob::translate_axis (Real y, Axis a)
565 if (isinf (y) || isnan (y))
566 programming_error (_ (INFINITY_MSG));
567 else
569 dim_cache_[a].offset_ += y;
575 Find the offset relative to D. If D equals THIS, then it is 0.
576 Otherwise, it recursively defd as
578 OFFSET_ + PARENT_L_->relative_coordinate (D)
580 Real
581 Grob::relative_coordinate (Grob const*refp, Axis a) const
583 if (refp == this)
584 return 0.0;
587 We catch PARENT_L_ == nil case with this, but we crash if we did
588 not ask for the absolute coordinate (ie. REFP == nil.)
591 if (refp == dim_cache_[a].parent_l_)
592 return get_offset (a);
593 else
594 return get_offset (a) + dim_cache_[a].parent_l_->relative_coordinate (refp, a);
600 Invoke callbacks to get offset relative to parent.
602 Real
603 Grob::get_offset (Axis a) const
605 Grob *me = (Grob*) this;
606 while (dim_cache_[a].offsets_left_)
608 int l = --me->dim_cache_[a].offsets_left_;
609 SCM cb = scm_list_ref (dim_cache_[a].offset_callbacks_, gh_int2scm (l));
610 SCM retval = gh_call2 (cb, self_scm (), gh_int2scm (a));
612 Real r = gh_scm2double (retval);
613 if (isinf (r) || isnan (r))
615 programming_error (INFINITY_MSG);
616 r = 0.0;
618 me->dim_cache_[a].offset_ +=r;
620 return dim_cache_[a].offset_;
624 MAKE_SCHEME_CALLBACK (Grob,point_dimension_callback,2);
626 Grob::point_dimension_callback (SCM , SCM)
628 return ly_interval2scm (Interval (0,0));
631 bool
632 Grob::empty_b (Axis a)const
634 return ! (gh_pair_p (dim_cache_[a].dimension_) ||
635 gh_procedure_p (dim_cache_[a].dimension_));
638 Interval
639 Grob::extent (Grob * refp, Axis a) const
641 Real x = relative_coordinate (refp, a);
644 Dimension_cache * d = (Dimension_cache *)&dim_cache_[a];
645 Interval ext ;
646 if (gh_pair_p (d->dimension_))
648 else if (gh_procedure_p (d->dimension_))
651 FIXME: add doco on types, and should typecheck maybe?
653 d->dimension_= gh_call2 (d->dimension_, self_scm (), gh_int2scm (a));
655 else
656 return ext;
658 if (!gh_pair_p (d->dimension_))
659 return ext;
661 ext = ly_scm2interval (d->dimension_);
663 SCM extra = get_grob_property (a == X_AXIS
664 ? "extra-extent-X"
665 : "extra-extent-Y");
668 signs ?
670 if (gh_pair_p (extra))
672 ext[BIGGER] += gh_scm2double (ly_cdr (extra));
673 ext[SMALLER] += gh_scm2double (ly_car (extra));
676 extra = get_grob_property (a == X_AXIS
677 ? "minimum-extent-X"
678 : "minimum-extent-Y");
679 if (gh_pair_p (extra))
681 ext.unite (Interval (gh_scm2double (ly_car (extra)),
682 gh_scm2double (ly_cdr (extra))));
685 ext.translate (x);
687 return ext;
691 Find the group-element which has both #this# and #s#
693 Grob *
694 Grob::common_refpoint (Grob const* s, Axis a) const
697 I don't like the quadratic aspect of this code, but I see no other
698 way. The largest chain of parents might be 10 high or so, so
699 it shouldn't be a real issue. */
700 for (Grob const *c = this; c; c = c->dim_cache_[a].parent_l_)
701 for (Grob const * d = s; d; d = d->dim_cache_[a].parent_l_)
702 if (d == c)
703 return (Grob*)d;
705 return 0;
709 Grob *
710 common_refpoint_of_list (SCM elist, Grob *common, Axis a)
712 for (; gh_pair_p (elist); elist = ly_cdr (elist))
714 Grob * s = unsmob_grob (ly_car (elist));
715 if (!s)
716 continue;
717 if (common)
718 common = common->common_refpoint (s, a);
719 else
720 common = s;
723 return common;
728 Grob *
729 common_refpoint_of_array (Link_array<Grob> const &arr, Grob *common, Axis a)
731 for (int i = arr.size() ; i--; )
733 Grob * s = arr[i];
734 if (!s)
735 continue;
737 if (common)
738 common = common->common_refpoint (s, a);
739 else
740 common = s;
743 return common;
746 String
747 Grob::name () const
749 SCM meta = get_grob_property ("meta");
750 SCM nm = scm_assoc (ly_symbol2scm ("name"), meta);
751 nm = (gh_pair_p (nm)) ? ly_cdr (nm) : SCM_EOL;
752 return gh_symbol_p (nm) ? ly_symbol2string (nm) : classname (this);
755 void
756 Grob::add_offset_callback (SCM cb, Axis a)
758 if (!has_offset_callback_b (cb, a))
760 dim_cache_[a].offset_callbacks_ = gh_cons (cb, dim_cache_[a].offset_callbacks_);
761 dim_cache_[a].offsets_left_ ++;
765 bool
766 Grob::has_extent_callback_b (SCM cb, Axis a)const
768 return scm_equal_p (cb, dim_cache_[a].dimension_) == SCM_BOOL_T;
772 bool
773 Grob::has_offset_callback_b (SCM cb, Axis a)const
775 return scm_memq (cb, dim_cache_[a].offset_callbacks_) != SCM_BOOL_F;
778 void
779 Grob::set_extent (SCM dc, Axis a)
781 dim_cache_[a].dimension_ =dc;
784 void
785 Grob::set_parent (Grob *g, Axis a)
787 dim_cache_[a].parent_l_ = g;
790 MAKE_SCHEME_CALLBACK (Grob,fixup_refpoint,1);
792 Grob::fixup_refpoint (SCM smob)
794 Grob *me = unsmob_grob (smob);
795 for (int a = X_AXIS; a < NO_AXES; a ++)
797 Axis ax = (Axis)a;
798 Grob * parent = me->get_parent (ax);
800 if (!parent)
801 continue;
803 if (parent->line_l () != me->line_l () && me->line_l ())
805 Grob * newparent = parent->find_broken_piece (me->line_l ());
806 me->set_parent (newparent, ax);
809 if (Item * i = dynamic_cast<Item*> (me))
811 Item *parenti = dynamic_cast<Item*> (parent);
813 if (parenti && i)
815 Direction my_dir = i->break_status_dir () ;
816 if (my_dir!= parenti->break_status_dir ())
818 Item *newparent = parenti->find_prebroken_piece (my_dir);
819 me->set_parent (newparent, ax);
824 return smob;
827 void
828 Grob::warning (String s)const
830 SCM cause = self_scm();
831 while (cause != SCM_EOL && !unsmob_music (cause))
833 Grob * g = unsmob_grob (cause);
834 cause = g->get_grob_property ("cause");
837 if (Music *m = unsmob_music (cause))
839 m->origin()->warning (s);
841 else
842 ::warning (s);
845 void
846 Grob::programming_error (String s)const
848 s = "Programming error: " + s;
849 warning (s);
853 /****************************************************
854 SMOB funcs
855 ****************************************************/
859 IMPLEMENT_SMOBS (Grob);
860 IMPLEMENT_DEFAULT_EQUAL_P (Grob);
863 Grob::mark_smob (SCM ses)
865 Grob * s = (Grob*) SCM_CELL_WORD_1 (ses);
866 scm_gc_mark (s->immutable_property_alist_);
867 scm_gc_mark (s->mutable_property_alist_);
869 for (int a =0 ; a < 2; a++)
871 scm_gc_mark (s->dim_cache_[a].offset_callbacks_);
872 scm_gc_mark (s->dim_cache_[a].dimension_);
873 Grob *p = s->get_parent (Y_AXIS);
874 if (p)
875 scm_gc_mark (p->self_scm ());
878 if (s->original_l_)
879 scm_gc_mark (s->original_l_->self_scm ());
881 return s->do_derived_mark ();
885 Grob::print_smob (SCM s, SCM port, scm_print_state *)
887 Grob *sc = (Grob *) ly_cdr (s);
889 scm_puts ("#<Grob ", port);
890 scm_puts ((char *)sc->name ().ch_C (), port);
893 don't try to print properties, that is too much hassle.
895 scm_puts (" >", port);
896 return 1;
900 Grob::do_derived_mark ()
902 return SCM_EOL;
905 LY_DEFINE(ly_set_grob_property,"ly-set-grob-property", 3, 0, 0,
906 (SCM grob, SCM sym, SCM val),
908 Set @var{sym} in grob @var{grob} to value @var{val}")
910 Grob * sc = unsmob_grob (grob);
911 SCM_ASSERT_TYPE(sc, grob, SCM_ARG1, __FUNCTION__, "grob");
912 SCM_ASSERT_TYPE(gh_symbol_p(sym), sym, SCM_ARG2, __FUNCTION__, "symbol");
914 if (!type_check_assignment (sym, val, ly_symbol2scm ("backend-type?")))
915 error ("typecheck failed");
917 sc->internal_set_grob_property (sym, val);
918 return SCM_UNSPECIFIED;
921 LY_DEFINE(ly_get_grob_property,
922 "ly-get-grob-property", 2, 0, 0, (SCM grob, SCM sym),
923 " Get the value of a value in grob @var{g} of property @var{sym}. It
924 will return @code{'()} (end-of-list) if @var{g} doesn't have @var{sym} set.
927 Grob * sc = unsmob_grob (grob);
928 SCM_ASSERT_TYPE(sc, grob, SCM_ARG1, __FUNCTION__, "grob");
929 SCM_ASSERT_TYPE(gh_symbol_p(sym), sym, SCM_ARG2, __FUNCTION__, "symbol");
931 return sc->internal_get_grob_property (sym);
935 void
936 Grob::discretionary_processing ()
941 LY_DEFINE(spanner_get_bound, "ly-get-spanner-bound", 2 , 0, 0,
942 (SCM slur, SCM dir),
943 "Get one of the bounds of @var{spanner}. @var{dir} may be @code{-1} for
944 left, and @code{1} for right.
947 Spanner * sl = dynamic_cast<Spanner*> (unsmob_grob (slur));
948 SCM_ASSERT_TYPE(sl, slur, SCM_ARG1, __FUNCTION__, "spanner grob");
949 SCM_ASSERT_TYPE(ly_dir_p (dir), slur, SCM_ARG2, __FUNCTION__, "dir");
950 return sl->get_bound (to_dir (dir))->self_scm ();
953 LY_DEFINE(ly_get_paper_var,"ly-get-paper-variable", 2, 0, 0,
954 (SCM grob, SCM sym),
955 "Get a variable from the \\paper block.")
957 Grob * sc = unsmob_grob (grob);
958 SCM_ASSERT_TYPE(sc, grob, SCM_ARG1, __FUNCTION__, "grob");
959 SCM_ASSERT_TYPE(gh_symbol_p(sym), sym, SCM_ARG2, __FUNCTION__, "symbol");
961 return sc->paper_l() ->get_scmvar_scm (sym);
966 LY_DEFINE(ly_get_extent, "ly-get-extent", 3, 0, 0,
967 (SCM grob, SCM refp, SCM axis),
968 "Get the extent in @var{axis} direction of @var{grob} relative to the
969 grob @var{refp}")
971 Grob * sc = unsmob_grob (grob);
972 Grob * ref = unsmob_grob (refp);
973 SCM_ASSERT_TYPE(sc, grob, SCM_ARG1, __FUNCTION__, "grob");
974 SCM_ASSERT_TYPE(ref, refp, SCM_ARG2, __FUNCTION__, "grob");
976 SCM_ASSERT_TYPE(ly_axis_p(axis), axis, SCM_ARG3, __FUNCTION__, "axis");
978 return ly_interval2scm ( sc->extent (ref, Axis (gh_scm2int (axis))));
981 LY_DEFINE (ly_get_parent, "ly-get-parent", 2, 0, 0, (SCM grob, SCM axis),
982 "Get the parent of @var{grob}. @var{axis} can be 0 for the X-axis, 1
983 for the Y-axis.")
985 Grob * sc = unsmob_grob (grob);
986 SCM_ASSERT_TYPE(sc, grob, SCM_ARG1, __FUNCTION__, "grob");
987 SCM_ASSERT_TYPE(ly_axis_p(axis), axis, SCM_ARG2, __FUNCTION__, "axis");
989 return sc->get_parent (Axis (gh_scm2int (axis)))->self_scm();
993 bool
994 Grob::internal_has_interface (SCM k)
996 SCM ifs = get_grob_property ("interfaces");
998 return scm_memq (k, ifs) != SCM_BOOL_F;
1001 IMPLEMENT_TYPE_P (Grob, "ly-grob?");
1003 ADD_INTERFACE (Grob, "grob-interface",
1004 "All grobs support this",
1005 "X-offset-callbacks Y-offset-callbacks X-extent-callback molecule cause
1006 Y-extent-callback molecule-callback extra-offset
1007 spacing-procedure
1008 staff-symbol interfaces dependencies extra-extent-X causes meta
1009 layer before-line-breaking-callback after-line-breaking-callback extra-extent-Y minimum-extent-X minimum-extent-Y transparent");