]> git.donarmstrong.com Git - lilypond.git/blob - lily/grob.cc
release: 1.5.45
[lilypond.git] / lily / grob.cc
1 /*
2   grob.cc -- implement Grob
3
4   source file of the GNU LilyPond music typesetter
5
6   (c)  1997--2002 Han-Wen Nienhuys <hanwen@cs.uu.nl>
7 */
8
9
10 #include <string.h>
11 #include <math.h>
12
13 #include "main.hh"
14 #include "input-smob.hh"
15
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 "line-of-score.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"
32
33 #include "ly-smobs.icc"
34
35 /*
36 TODO:
37
38 remove dynamic_cast<Spanner,Item> and put this code into respective
39   subclass.
40 */
41
42
43 #define INFINITY_MSG "Infinity or NaN encountered"
44
45 Grob::Grob (SCM basicprops)
46 {
47   /*
48     fixme: default should be no callback.
49    */
50
51   pscore_l_=0;
52   status_c_ = 0;
53   original_l_ = 0;
54   immutable_property_alist_ =  basicprops;
55   mutable_property_alist_ = SCM_EOL;
56
57   smobify_self ();
58
59   /*
60     TODO:
61
62     destill this into a function, so we can re-init the immutable
63     properties with a new BASICPROPS value after creation. Convenient
64     eg. when using \override with StaffSymbol.  */
65   
66   char const*onames[] = {"X-offset-callbacks", "Y-offset-callbacks"};
67   char const*enames[] = {"X-extent-callback", "Y-extent-callback"};
68   
69   for (int a = X_AXIS; a <= Y_AXIS; a++)
70     {
71       SCM l = get_grob_property (onames[a]);
72
73       if (scm_ilength (l) >=0)
74         {
75           dim_cache_[a].offset_callbacks_ = l;
76           dim_cache_[a].offsets_left_ = scm_ilength (l);
77         }
78       else
79         {
80           programming_error ("[XY]-offset-callbacks must be a list");
81         }
82
83       SCM cb = get_grob_property (enames[a]);
84
85       /*
86         Should change default to be empty? 
87       */
88       if (cb != SCM_BOOL_F && !gh_procedure_p (cb) && !gh_pair_p (cb))
89         cb = molecule_extent_proc;
90     
91       dim_cache_[a].dimension_ = cb;
92     }
93
94   SCM meta = get_grob_property ("meta");
95   if (gh_pair_p (meta))
96     {
97       SCM ifs = scm_assoc (ly_symbol2scm ("interfaces"), meta);
98   
99       set_grob_property ("interfaces",ly_cdr (ifs));
100     }
101 }
102
103 Grob::Grob (Grob const&s)
104    : dim_cache_ (s.dim_cache_)
105 {
106   original_l_ = (Grob*) &s;
107   immutable_property_alist_ = s.immutable_property_alist_;
108   mutable_property_alist_ = SCM_EOL;
109   
110   status_c_ = s.status_c_;
111   pscore_l_ = s.pscore_l_;
112
113   smobify_self ();
114 }
115
116 Grob::~Grob ()
117 {
118   /*
119     do nothing scm-ish and no unprotecting here.
120    */
121 }
122
123
124 SCM
125 Grob::internal_get_grob_property (SCM sym) const
126 {
127   SCM s = scm_sloppy_assq (sym, mutable_property_alist_);
128   if (s != SCM_BOOL_F)
129     return ly_cdr (s);
130
131   s = scm_sloppy_assq (sym, immutable_property_alist_);
132   return (s == SCM_BOOL_F) ? SCM_EOL : ly_cdr (s); 
133 }
134
135 /*
136   Remove the value associated with KEY, and return it. The result is
137   that a next call will yield SCM_EOL (and not the underlying
138   `basic' property.
139 */
140 SCM
141 Grob::remove_grob_property (const char* key)
142 {
143   SCM val = get_grob_property (key);
144   if (val != SCM_EOL)
145     set_grob_property (key, SCM_EOL);
146   return val;
147 }
148
149
150 #if 0
151 /*
152   Puts the k, v in the immutable_property_alist_, which is convenient for
153   storing variables that are needed during the breaking process. (eg.
154   System::rank : int)
155  */
156 void
157 Grob::set_immutable_grob_property (const char*k, SCM v)
158 {
159   SCM s = ly_symbol2scm (k);
160   set_immutable_grob_property (s, v);
161 }
162
163 void
164 Grob::set_immutable_grob_property (SCM s, SCM v)
165 {
166   immutable_property_alist_ = gh_cons (gh_cons (s,v), mutable_property_alist_);
167   mutable_property_alist_ = scm_assq_remove_x (mutable_property_alist_, s);
168 }
169 #endif
170
171 void
172 Grob::internal_set_grob_property (SCM s, SCM v)
173 {
174 #ifndef NDEBUG
175   if (internal_type_checking_global_b)
176     assert (type_check_assignment (s, v, ly_symbol2scm ("backend-type?")));
177 #endif
178
179   
180   mutable_property_alist_ = scm_assq_set_x (mutable_property_alist_, s, v);
181 }
182
183
184 MAKE_SCHEME_CALLBACK (Grob,molecule_extent,2);
185 SCM
186 Grob::molecule_extent (SCM element_smob, SCM scm_axis)
187 {
188   Grob *s = unsmob_grob (element_smob);
189   Axis a = (Axis) gh_scm2int (scm_axis);
190
191   Molecule *m = s->get_molecule ();
192   Interval e ;
193   if (m)
194     e = m->extent (a);
195   return ly_interval2scm (e);
196 }
197
198 MAKE_SCHEME_CALLBACK (Grob,preset_extent,2);
199
200 SCM
201 Grob::preset_extent (SCM element_smob, SCM scm_axis)
202 {
203   Grob *s = unsmob_grob (element_smob);
204   Axis a = (Axis) gh_scm2int (scm_axis);
205
206   SCM ext = s->get_grob_property ((a == X_AXIS)
207                                  ? "extent-X"
208                                  : "extent-Y");
209   
210   if (gh_pair_p (ext))
211     {
212       Real l = gh_scm2double (ly_car (ext));
213       Real r = gh_scm2double (ly_cdr (ext));
214       return ly_interval2scm (Interval (l, r));
215     }
216   
217   return ly_interval2scm (Interval ());
218 }
219
220
221
222 Paper_def*
223 Grob::paper_l ()  const
224 {
225  return pscore_l_ ? pscore_l_->paper_l_ : 0;
226 }
227
228 void
229 Grob::calculate_dependencies (int final, int busy, SCM funcname)
230 {
231   if (status_c_ >= final)
232     return;
233
234   if (status_c_== busy)
235     {
236       programming_error ("Element is busy, come back later");
237       return;
238     }
239   
240   status_c_= busy;
241
242   for (SCM d = get_grob_property ("dependencies"); gh_pair_p (d);
243        d = ly_cdr (d))
244     {
245       unsmob_grob (ly_car (d))
246         ->calculate_dependencies (final, busy, funcname);
247     }
248
249   // ughugh.
250   String s = ly_symbol2string (funcname);
251   SCM proc = get_grob_property (s.ch_C ());
252   if (gh_procedure_p (proc))
253     gh_call1 (proc, this->self_scm ());
254   else if (gh_list_p (proc))
255     for (SCM i = proc; gh_pair_p (i); i = ly_cdr (i))
256       gh_call1 (ly_car (i), this->self_scm ());
257   
258   status_c_= final;
259 }
260
261 Molecule *
262 Grob::get_molecule ()  const
263 {
264   SCM mol = get_grob_property ("molecule");
265   if (unsmob_molecule (mol))
266     return unsmob_molecule (mol);
267
268   mol =  get_uncached_molecule ();
269   
270   Grob *me = (Grob*)this;
271   me->set_grob_property ("molecule", mol);
272   
273   return unsmob_molecule (mol);  
274 }
275 SCM
276 Grob::get_uncached_molecule ()const
277 {
278   SCM proc = get_grob_property ("molecule-callback");
279
280   SCM  mol = SCM_EOL;
281   if (gh_procedure_p (proc)) 
282     mol = gh_apply (proc, scm_list_n (this->self_scm (), SCM_UNDEFINED));
283
284   
285   Molecule *m = unsmob_molecule (mol);
286   
287   if (unsmob_molecule (mol))
288     {
289       SCM origin = ly_symbol2scm ("no-origin");
290       
291       if (store_locations_global_b){
292         SCM cause = get_grob_property ("cause");
293         if (Music*m = unsmob_music (cause))
294           {
295             SCM music_origin = m->get_mus_property ("origin");
296             if (unsmob_input (music_origin))
297               origin = music_origin;
298           }
299       }
300
301       // ugr.
302       
303       mol = Molecule (m->extent_box (),
304                       scm_list_n (origin, m->get_expr (), SCM_UNDEFINED)
305                       ). smobbed_copy ();
306
307       m = unsmob_molecule (mol);
308     }
309   
310   /*
311     transparent retains dimensions of element.
312    */
313   if (m && to_boolean (get_grob_property ("transparent")))
314     mol = Molecule (m->extent_box (), SCM_EOL).smobbed_copy ();
315
316   return mol;
317 }
318
319 /*
320   
321   VIRTUAL STUBS
322
323  */
324 void
325 Grob::do_break_processing ()
326 {
327 }
328
329
330
331
332
333
334 System *
335 Grob::line_l () const
336 {
337   return 0;
338 }
339
340 void
341 Grob::add_dependency (Grob*e)
342 {
343   if (e)
344     {
345       Pointer_group_interface::add_grob (this, ly_symbol2scm ("dependencies"),e);
346     }
347   else
348     programming_error ("Null dependency added");
349 }
350
351
352
353
354 /**
355       Do break substitution in S, using CRITERION. Return new value.
356       CRITERION is either a SMOB pointer to the desired line, or a number
357       representing the break direction. Do not modify SRC.
358
359       It is rather tightly coded, since it takes a lot of time; it is
360       one of the top functions in the profile.
361
362 */
363 SCM
364 Grob::handle_broken_grobs (SCM src, SCM criterion)
365 {
366  again:
367   Grob *sc = unsmob_grob (src);
368   if (sc)
369     {
370       if (SCM_INUMP (criterion))
371         {
372           Item * i = dynamic_cast<Item*> (sc);
373           Direction d = to_dir (criterion);
374           if (i && i->break_status_dir () != d)
375             {
376               Item *br = i->find_prebroken_piece (d);
377               return (br) ? br->self_scm () : SCM_UNDEFINED;
378             }
379         }
380       else
381         {
382           System * line
383             = dynamic_cast<System*> (unsmob_grob (criterion));
384           if (sc->line_l () != line)
385             {
386               sc = sc->find_broken_piece (line);
387
388             }
389
390           /* now: !sc || (sc && sc->line_l () == line) */
391           if (!sc)
392             return SCM_UNDEFINED;
393
394           /* now: sc && sc->line_l () == line */
395           if (!line)
396             return sc->self_scm();
397               /*
398                 This was introduced in 1.3.49 as a measure to prevent
399                 programming errors. It looks expensive (?). TODO:
400                 benchmark , document when (what kind of programming
401                 errors) this happens.
402                */
403           if (sc->common_refpoint (line, X_AXIS)
404                && sc->common_refpoint (line, Y_AXIS))
405             {
406               return sc->self_scm ();
407             }
408           return SCM_UNDEFINED;
409         }
410     }
411   else if (ly_pair_p (src)) // SCM_CONSP (src))  // huh?
412     {
413       SCM oldcar =ly_car (src);
414       /*
415         UGH! breaks on circular lists.
416       */
417       SCM newcar = handle_broken_grobs (oldcar, criterion);
418       SCM oldcdr = ly_cdr (src);
419       
420       if (newcar == SCM_UNDEFINED
421           && (gh_pair_p (oldcdr) || oldcdr == SCM_EOL))
422         {
423           /*
424             This is tail-recursion, ie. 
425             
426             return handle_broken_grobs (cdr, criterion);
427
428             We don't want to rely on the compiler to do this.  Without
429             tail-recursion, this easily crashes with a stack overflow.  */
430           src =  oldcdr;
431           goto again;
432         }
433
434       SCM newcdr = handle_broken_grobs (oldcdr, criterion);
435       return scm_cons (newcar, newcdr);
436     }
437   else
438     return src;
439
440   return src;
441 }
442
443 void
444 Grob::handle_broken_dependencies ()
445 {
446   Spanner * s= dynamic_cast<Spanner*> (this);
447   if (original_l_ && s)
448     return;
449
450   if (s)
451     {
452       for (int i = 0;  i< s->broken_into_l_arr_ .size (); i++)
453         {
454           Grob * sc = s->broken_into_l_arr_[i];
455           System * l = sc->line_l ();
456           sc->mutable_property_alist_ =
457             handle_broken_grobs (mutable_property_alist_,
458                                  l ? l->self_scm () : SCM_UNDEFINED);
459         }
460     }
461
462
463   System *line = line_l ();
464
465   if (line && common_refpoint (line, X_AXIS) && common_refpoint (line, Y_AXIS))
466     {
467       mutable_property_alist_
468         = handle_broken_grobs (mutable_property_alist_,
469                                line ? line->self_scm () : SCM_UNDEFINED);
470     }
471   else if (dynamic_cast <System*> (this))
472     {
473       mutable_property_alist_ = handle_broken_grobs (mutable_property_alist_,
474                                             SCM_UNDEFINED);
475     }
476   else
477     {
478       /*
479         This element is `invalid'; it has been removed from all
480         dependencies, so let's junk the element itself.
481
482         do not do this for System, since that would remove
483         references to the originals of score-elts, which get then GC'd
484  (a bad thing.)
485       */
486       suicide ();
487     }
488 }
489
490 /*
491  Note that we still want references to this element to be
492  rearranged, and not silently thrown away, so we keep pointers
493  like {broken_into_{drul,array}, original}
494 */
495 void
496 Grob::suicide ()
497 {
498   mutable_property_alist_ = SCM_EOL;
499   immutable_property_alist_ = SCM_EOL;
500
501   set_extent_callback (SCM_EOL, Y_AXIS);
502   set_extent_callback (SCM_EOL, X_AXIS);
503
504   for (int a= X_AXIS; a <= Y_AXIS; a++)
505     {
506       dim_cache_[a].offset_callbacks_ = SCM_EOL;
507       dim_cache_[a].offsets_left_ = 0;
508     }
509 }
510
511 void
512 Grob::handle_prebroken_dependencies ()
513 {
514 }
515
516 Grob*
517 Grob::find_broken_piece (System*) const
518 {
519   return 0;
520 }
521
522 void
523 Grob::translate_axis (Real y, Axis a)
524 {
525   if (isinf (y) || isnan (y))
526     programming_error (_ (INFINITY_MSG));
527   else
528     {
529       dim_cache_[a].offset_ += y;
530     }
531 }  
532
533 Real
534 Grob::relative_coordinate (Grob const*refp, Axis a) const
535 {
536   if (refp == this)
537     return 0.0;
538
539   /*
540     We catch PARENT_L_ == nil case with this, but we crash if we did
541     not ask for the absolute coordinate (ie. REFP == nil.)
542     
543    */
544   if (refp == dim_cache_[a].parent_l_)
545     return get_offset (a);
546   else
547     return get_offset (a) + dim_cache_[a].parent_l_->relative_coordinate (refp, a);
548 }
549
550 Real
551 Grob::get_offset (Axis a) const
552 {
553   Grob *me = (Grob*) this;
554   while (dim_cache_[a].offsets_left_)
555     {
556       int l = --me->dim_cache_[a].offsets_left_;
557       SCM cb = scm_list_ref (dim_cache_[a].offset_callbacks_,  gh_int2scm (l));
558       SCM retval = gh_call2 (cb, self_scm (), gh_int2scm (a));
559
560       Real r =  gh_scm2double (retval);
561       if (isinf (r) || isnan (r))
562         {
563           programming_error (INFINITY_MSG);
564           r = 0.0;
565         }
566       me->dim_cache_[a].offset_ +=r;
567     }
568   return dim_cache_[a].offset_;
569 }
570
571
572 MAKE_SCHEME_CALLBACK (Grob,point_dimension_callback,2);
573 SCM
574 Grob::point_dimension_callback (SCM , SCM)
575 {
576   return ly_interval2scm (Interval (0,0));
577 }
578
579 bool
580 Grob::empty_b (Axis a)const
581 {
582   return ! (gh_pair_p (dim_cache_[a].dimension_) ||
583             gh_procedure_p (dim_cache_[a].dimension_));
584 }
585
586 /*
587   TODO: add
588
589     Grob *refpoint
590
591   to arguments?
592  */
593 Interval
594 Grob::extent (Grob * refp, Axis a) const
595 {
596   Real x = relative_coordinate (refp, a);
597
598   
599   Dimension_cache * d = (Dimension_cache *)&dim_cache_[a];
600   Interval ext ;   
601   if (gh_pair_p (d->dimension_))
602     ;
603   else if (gh_procedure_p (d->dimension_))
604     {
605       /*
606         FIXME: add doco on types, and should typecheck maybe? 
607        */
608       d->dimension_= gh_call2 (d->dimension_, self_scm (), gh_int2scm (a));
609     }
610   else
611     return ext;
612
613   if (!gh_pair_p (d->dimension_))
614     return ext;
615   
616   ext = ly_scm2interval (d->dimension_);
617
618   SCM extra = get_grob_property (a == X_AXIS
619                                 ? "extra-extent-X"
620                                 : "extra-extent-Y");
621
622   /*
623     signs ?
624    */
625   if (gh_pair_p (extra))
626     {
627       ext[BIGGER] +=  gh_scm2double (ly_cdr (extra));
628       ext[SMALLER] +=   gh_scm2double (ly_car (extra));
629     }
630   
631   extra = get_grob_property (a == X_AXIS
632                                 ? "minimum-extent-X"
633                                 : "minimum-extent-Y");
634   if (gh_pair_p (extra))
635     {
636       ext.unite (Interval (gh_scm2double (ly_car (extra)),
637                            gh_scm2double (ly_cdr (extra))));
638     }
639
640   ext.translate (x);
641   
642   return ext;
643 }
644
645 Grob * 
646 Grob::common_refpoint (Grob const* s, Axis a) const
647 {
648   /*
649     I don't like the quadratic aspect of this code, but I see no other
650     way. The largest chain of parents might be 10 high or so, so
651     it shouldn't be a real issue. */
652   for (Grob const *c = this; c; c = c->dim_cache_[a].parent_l_)
653     for (Grob const * d = s; d; d = d->dim_cache_[a].parent_l_)
654       if (d == c)
655         return (Grob*)d;
656
657   return 0;
658 }
659
660
661 Grob *
662 Grob::common_refpoint (SCM elist, Axis a) const
663 {
664   Grob * common = (Grob*) this;
665   for (; gh_pair_p (elist); elist = ly_cdr (elist))
666     {
667       Grob * s = unsmob_grob (ly_car (elist));
668       if (s)
669         common = common->common_refpoint (s, a);
670     }
671
672   return common;
673 }
674
675 String
676 Grob::name () const
677 {
678   SCM meta = get_grob_property ("meta");
679   SCM nm = scm_assoc (ly_symbol2scm ("name"), meta);
680   nm = (gh_pair_p (nm)) ? ly_cdr (nm) : SCM_EOL;
681   return  gh_symbol_p (nm) ? ly_symbol2string (nm) :  classname (this);  
682 }
683
684 void
685 Grob::add_offset_callback (SCM cb, Axis a)
686 {
687   if (!has_offset_callback_b (cb, a))
688   {
689     dim_cache_[a].offset_callbacks_ = gh_cons (cb, dim_cache_[a].offset_callbacks_);
690     dim_cache_[a].offsets_left_ ++;
691   }
692 }
693
694 bool
695 Grob::has_extent_callback_b (SCM cb, Axis a)const
696 {
697   return scm_equal_p (cb, dim_cache_[a].dimension_) == SCM_BOOL_T;
698 }
699
700
701 bool
702 Grob::has_extent_callback_b (Axis a) const
703 {
704   return gh_procedure_p (dim_cache_[a].dimension_);
705 }
706
707 bool
708 Grob::has_offset_callback_b (SCM cb, Axis a)const
709 {
710   return scm_memq (cb, dim_cache_[a].offset_callbacks_) != SCM_BOOL_F;
711 }
712
713 void
714 Grob::set_extent_callback (SCM dc, Axis a)
715 {
716   dim_cache_[a].dimension_ =dc;
717 }
718
719 void
720 Grob::set_parent (Grob *g, Axis a)
721 {
722   dim_cache_[a].parent_l_ = g;
723 }
724
725 MAKE_SCHEME_CALLBACK (Grob,fixup_refpoint,1);
726 SCM
727 Grob::fixup_refpoint (SCM smob)
728 {
729   Grob *me = unsmob_grob (smob);
730   for (int a = X_AXIS; a < NO_AXES; a ++)
731     {
732       Axis ax = (Axis)a;
733       Grob * parent = me->get_parent (ax);
734
735       if (!parent)
736         continue;
737       
738       if (parent->line_l () != me->line_l () && me->line_l ())
739         {
740           Grob * newparent = parent->find_broken_piece (me->line_l ());
741           me->set_parent (newparent, ax);
742         }
743
744       if (Item * i  = dynamic_cast<Item*> (me))
745         {
746           Item *parenti = dynamic_cast<Item*> (parent);
747
748           if (parenti && i)
749             {
750               Direction  my_dir = i->break_status_dir () ;
751               if (my_dir!= parenti->break_status_dir ())
752                 {
753                   Item *newparent =  parenti->find_prebroken_piece (my_dir);
754                   me->set_parent (newparent, ax);
755                 }
756             }
757         }
758     }
759   return smob;
760 }
761
762 void
763 Grob::warning (String s)
764 {
765   SCM cause = self_scm();
766   while (cause != SCM_EOL && !unsmob_music (cause))
767     {
768       Grob * g = unsmob_grob (cause);
769       cause = g->get_grob_property ("cause");
770     }
771
772   if (Music *m = unsmob_music (cause))
773     {
774       m->origin()->warning (s);
775     }
776   else
777     ::warning (s);
778       
779 }
780
781
782 /****************************************************
783   SMOB funcs
784  ****************************************************/
785
786
787
788 IMPLEMENT_SMOBS (Grob);
789 IMPLEMENT_DEFAULT_EQUAL_P (Grob);
790
791 SCM
792 Grob::mark_smob (SCM ses)
793 {
794   Grob * s = (Grob*) SCM_CELL_WORD_1 (ses);
795   scm_gc_mark (s->immutable_property_alist_);
796   scm_gc_mark (s->mutable_property_alist_);
797
798   for (int a =0 ; a < 2; a++)
799     {
800       scm_gc_mark (s->dim_cache_[a].offset_callbacks_);
801       scm_gc_mark (s->dim_cache_[a].dimension_);
802       Grob *p = s->get_parent (Y_AXIS);
803       if (p)
804         scm_gc_mark (p->self_scm ());
805     }
806   
807   if (s->original_l_)
808     scm_gc_mark (s->original_l_->self_scm ());
809
810   return s->do_derived_mark ();
811 }
812
813 int
814 Grob::print_smob (SCM s, SCM port, scm_print_state *)
815 {
816   Grob *sc = (Grob *) ly_cdr (s);
817      
818   scm_puts ("#<Grob ", port);
819   scm_puts ((char *)sc->name ().ch_C (), port);
820
821   /*
822     don't try to print properties, that is too much hassle.
823    */
824   scm_puts (" >", port);
825   return 1;
826 }
827
828 SCM
829 Grob::do_derived_mark ()
830 {
831   return SCM_EOL;
832 }
833
834
835 SCM
836 ly_set_grob_property (SCM elt, SCM sym, SCM val)
837 {
838   Grob * sc = unsmob_grob (elt);
839   SCM_ASSERT_TYPE(sc, elt, SCM_ARG1, __FUNCTION__, "grob");
840   SCM_ASSERT_TYPE(gh_symbol_p(sym), sym, SCM_ARG2, __FUNCTION__, "symbol");  
841
842   if (!type_check_assignment (sym, val, ly_symbol2scm ("backend-type?")))
843     error ("typecheck failed");
844       
845   sc->internal_set_grob_property (sym, val);
846   return SCM_UNSPECIFIED;
847 }
848
849
850 SCM
851 ly_get_grob_property (SCM elt, SCM sym)
852 {
853   Grob * sc = unsmob_grob (elt);
854   SCM_ASSERT_TYPE(sc, elt, SCM_ARG1, __FUNCTION__, "grob");
855   SCM_ASSERT_TYPE(gh_symbol_p(sym), sym, SCM_ARG2, __FUNCTION__, "symbol");  
856
857   return sc->internal_get_grob_property (sym);
858 }
859
860
861 void
862 Grob::discretionary_processing ()
863 {
864 }
865
866
867
868 SCM
869 spanner_get_bound (SCM slur, SCM dir)
870 {
871   Spanner * sl = dynamic_cast<Spanner*> (unsmob_grob (slur));
872   SCM_ASSERT_TYPE(sl, slur, SCM_ARG1, __FUNCTION__, "spanner grob");
873   SCM_ASSERT_TYPE(ly_dir_p (dir), slur, SCM_ARG2, __FUNCTION__, "dir");
874   return sl->get_bound (to_dir (dir))->self_scm ();
875 }
876
877 SCM
878 ly_get_paper_var (SCM grob, SCM sym)
879 {
880   Grob * sc = unsmob_grob (grob);
881   SCM_ASSERT_TYPE(sc, grob, SCM_ARG1, __FUNCTION__, "grob");
882   SCM_ASSERT_TYPE(gh_symbol_p(sym), sym, SCM_ARG2, __FUNCTION__, "symbol");  
883
884   return sc->paper_l() ->get_scmvar_scm (sym);
885 }
886
887
888
889 SCM
890 ly_get_extent (SCM grob, SCM refp, SCM axis)
891 {
892   Grob * sc = unsmob_grob (grob);
893   Grob * ref = unsmob_grob (refp);
894   SCM_ASSERT_TYPE(sc, grob, SCM_ARG1, __FUNCTION__, "grob");
895   SCM_ASSERT_TYPE(ref, refp, SCM_ARG2, __FUNCTION__, "grob");
896   
897   SCM_ASSERT_TYPE(ly_axis_p(axis), axis, SCM_ARG3, __FUNCTION__, "axis");
898
899   return ly_interval2scm ( sc->extent (ref, Axis (gh_scm2int (axis))));
900 }
901
902 SCM
903 ly_get_parent (SCM grob, SCM axis)
904 {
905   Grob * sc = unsmob_grob (grob);
906   SCM_ASSERT_TYPE(sc, grob, SCM_ARG1, __FUNCTION__, "grob");
907   SCM_ASSERT_TYPE(ly_axis_p(axis), axis, SCM_ARG2, __FUNCTION__, "axis");
908
909   return sc->get_parent (Axis (gh_scm2int (axis)))->self_scm();
910 }
911
912
913 static void
914 init_functions ()
915 {
916   scm_c_define_gsubr ("ly-get-grob-property", 2, 0, 0,
917                       (Scheme_function_unknown)ly_get_grob_property);
918   scm_c_define_gsubr ("ly-set-grob-property", 3, 0, 0,
919                       (Scheme_function_unknown)ly_set_grob_property);
920   scm_c_define_gsubr ("ly-get-spanner-bound", 2 , 0, 0,
921                       (Scheme_function_unknown) spanner_get_bound);
922   scm_c_define_gsubr ("ly-get-extent", 3, 0, 0,
923                       (Scheme_function_unknown) ly_get_extent);
924   scm_c_define_gsubr ("ly-get-parent", 2, 0, 0,
925                       (Scheme_function_unknown) ly_get_parent);
926 }
927
928 bool
929 Grob::has_interface (SCM k)
930 {
931   SCM ifs = get_grob_property ("interfaces");
932
933   return scm_memq (k, ifs) != SCM_BOOL_F;
934 }
935
936 void
937 Grob::set_interface (SCM k)
938 {
939   if (has_interface (k))
940     return ;
941   else
942     {
943       set_grob_property ("interfaces",
944                          gh_cons (k, get_grob_property ("interfaces")));
945     }
946 }
947
948
949 ADD_SCM_INIT_FUNC (scoreelt, init_functions);
950 IMPLEMENT_TYPE_P (Grob, "ly-grob?");