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