]> git.donarmstrong.com Git - lilypond.git/blob - lily/score-element.cc
patch::: 1.3.27.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
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
438   if (s)
439     {
440       for (int i = 0;  i< s->broken_into_l_arr_ .size (); i++)
441         {
442           Score_element * sc = s->broken_into_l_arr_[i];
443           Line_of_score * l = sc->line_l ();
444           s->broken_into_l_arr_[i]->element_property_alist_ =
445             handle_broken_smobs (element_property_alist_,
446                                  l ? l->self_scm_ : SCM_UNDEFINED);
447         }
448     }
449
450   Line_of_score *line = line_l();
451   element_property_alist_
452     = handle_broken_smobs (element_property_alist_,
453                            line ? line->self_scm_ : SCM_UNDEFINED);
454 }
455
456
457 /*
458   TODO: cleanify.
459  */
460 void
461 Score_element::handle_prebroken_dependencies()
462 {
463   if (Item*i =dynamic_cast<Item*> (this))
464     {
465       if (original_l_)
466         {
467           element_property_alist_
468             = handle_broken_smobs (original_l_->element_property_alist_,
469                                gh_int2scm (i->break_status_dir ()));
470         }
471     }
472 }
473
474 Link_array<Score_element>
475 Score_element::get_extra_dependencies() const
476 {
477   Link_array<Score_element> empty;
478   return empty;
479 }
480
481 bool
482 Score_element::linked_b() const
483 {
484   return used_b_;
485 }
486
487 void
488 Score_element::do_print () const
489 {
490 }
491
492 Score_element*
493 Score_element::find_broken_piece (Line_of_score*) const
494 {
495   return 0;
496 }
497
498 void
499 Score_element::translate_axis (Real y, Axis a)
500 {
501   dim_cache_[a]->translate (y);
502 }  
503
504 Real
505 Score_element::relative_coordinate (Score_element const*e, Axis a) const
506 {
507   return dim_cache_[a]->relative_coordinate (e ? e->dim_cache_[a] : 0);
508 }
509
510 Score_element * 
511 Score_element::common_refpoint (Score_element const* s, Axis a) const
512 {
513   Dimension_cache *dim = dim_cache_[a]->common_refpoint (s->dim_cache_[a]);
514   return  dim ? dim->element_l () : 0;
515 }
516
517 void
518 Score_element::set_empty (Axis a)
519 {
520   dim_cache_[a]->callback_l_ =0;
521 }
522
523 bool
524 Score_element::empty_b (Axis a)const
525 {
526   return !dim_cache_[a]->callback_l_;
527 }
528
529 Interval
530 Score_element::extent (Axis a) const
531 {
532   Dimension_cache const * d = dim_cache_[a];
533
534   return d->get_dim ();
535 }
536
537
538 Score_element*
539 Score_element::parent_l (Axis a) const
540 {
541   Dimension_cache*d= dim_cache_[a]->parent_l_;
542   return d ? d->elt_l_ : 0;
543 }
544
545 Score_element *
546 Score_element::common_refpoint (Link_array<Score_element> gs, Axis a) const
547 {
548   Dimension_cache * common = dim_cache_[a];
549   for (int i=0; i < gs.size (); i++)
550     {
551       common = common->common_refpoint (gs[i]->dim_cache_[a]);
552     }
553
554   return common->element_l ();
555 }
556
557 char const *
558 Score_element::name () const
559 {
560   return classname (this);
561 }
562
563
564 void
565 Score_element::set_parent (Score_element *g, Axis a)
566 {
567   dim_cache_[a]->parent_l_ = g ? g->dim_cache_[a]: 0;
568 }
569
570 void
571 Score_element::fixup_refpoint ()
572 {
573   for (int a = X_AXIS; a < NO_AXES; a ++)
574     {
575       Axis ax = (Axis)a;
576       Score_element * par = parent_l (ax);
577
578       if (!par)
579         continue;
580       
581       if (par->line_l () != line_l ())
582         {
583           Score_element * newpar = par->find_broken_piece (line_l ());
584           set_parent (newpar, ax);
585         }
586
587       if (Item * i  = dynamic_cast<Item*> (this))
588         {
589           Item *pari = dynamic_cast<Item*> (par);
590
591           if (pari && i)
592             {
593               Direction  my_dir = i->break_status_dir () ;
594               if (my_dir!= pari->break_status_dir())
595                 {
596                   Item *newpar =  pari->find_broken_piece (my_dir);
597                   set_parent (newpar, ax);
598                 }
599             }
600         }
601     }
602 }
603
604
605
606 /****************************************************
607   SMOB funcs
608  ****************************************************/
609
610
611 #include "ly-smobs.icc"
612
613 IMPLEMENT_SMOBS(Score_element);
614 IMPLEMENT_UNSMOB(Score_element, element);
615 SCM
616 Score_element::mark_smob (SCM ses)
617 {
618   Score_element * s = SMOB_TO_TYPE (Score_element, ses);
619   if (s->self_scm_ != ses)
620     {
621       programming_error ("SMOB marking gone awry");
622       return SCM_EOL;
623     }
624   return s->element_property_alist_;
625 }
626
627 int
628 Score_element::print_smob (SCM s, SCM port, scm_print_state *)
629 {
630   Score_element *sc = (Score_element *) gh_cdr (s);
631      
632   scm_puts ("#<Score_element ", port);
633   scm_puts ((char *)sc->name (), port);
634
635   /*
636     don't try to print properties, that is too much hassle.
637    */
638   scm_puts (" >", port);
639   return 1;
640 }
641
642 void
643 Score_element::do_smobify_self ()
644 {
645 }
646
647 SCM
648 Score_element::equal_p (SCM a, SCM b)
649 {
650   return gh_cdr(a) == gh_cdr(b) ? SCM_BOOL_T : SCM_BOOL_F;
651 }
652
653
654 SCM
655 Score_element::ly_set_elt_property (SCM elt, SCM sym, SCM val)
656 {
657   Score_element * sc = unsmob_element (elt);
658
659   if (!gh_symbol_p (sym))
660     {
661       error ("Not a symbol");
662       ly_display_scm (sym);
663       return SCM_UNDEFINED;
664     }
665
666   if (sc)
667     {
668       sc->element_property_alist_ = scm_assoc_set_x (sc->element_property_alist_, sym, val);
669     }
670   else
671     {
672       error ("Not a score element");
673       ly_display_scm (elt);
674     }
675
676   return SCM_UNDEFINED;
677 }
678
679
680 SCM
681 Score_element::ly_get_elt_property (SCM elt, SCM sym)
682 {
683   Score_element * sc = unsmob_element (elt);
684   
685   if (sc)
686     {
687       SCM s = scm_assq(sym, sc->element_property_alist_);
688
689       if (s != SCM_BOOL_F)
690         return gh_cdr (s); 
691       else
692         return SCM_UNDEFINED;
693     }
694   else
695     {
696       error ("Not a score element");
697       ly_display_scm (elt);
698     }
699   return SCM_UNDEFINED;
700 }
701
702
703 static void
704 init_functions ()
705 {
706   scm_make_gsubr ("ly-get-elt-property", 2, 0, 0, (SCM(*)(...))Score_element::ly_get_elt_property);
707   scm_make_gsubr ("ly-set-elt-property", 3, 0, 0, (SCM(*)(...))Score_element::ly_set_elt_property);
708 }
709
710 ADD_SCM_INIT_FUNC(scoreelt, init_functions);