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