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