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