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