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