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