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