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