]> git.donarmstrong.com Git - lilypond.git/blob - lily/score-element.cc
release: 1.3.25
[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 }
55
56 SCM ly_deep_copy (SCM);
57
58 SCM
59 ly_deep_copy (SCM l)
60 {
61   if (gh_pair_p (l))
62     {
63       return gh_cons (ly_deep_copy (gh_car (l)), ly_deep_copy (gh_cdr (l)));
64     }
65   else
66     return l;
67 }
68
69
70 Score_element::Score_element (Score_element const&s)
71 {
72   dim_cache_[X_AXIS] = new Dimension_cache (*s.dim_cache_[X_AXIS]);
73   dim_cache_[Y_AXIS] = new Dimension_cache (*s.dim_cache_[Y_AXIS]);
74   dim_cache_[X_AXIS]->elt_l_ = dim_cache_[Y_AXIS]->elt_l_ = this;
75   
76   self_scm_ = SCM_EOL;
77   used_b_ = true;
78   original_l_ =(Score_element*) &s;
79
80   /*
81     should protect because smobify_self () might trigger GC.
82    */
83   SCM onstack = ly_deep_copy (s.element_property_alist_);
84   element_property_alist_ = onstack;
85
86   output_p_ =0;
87   status_i_ = s.status_i_;
88   lookup_l_ = s.lookup_l_;
89   pscore_l_ = s.pscore_l_;
90
91   smobify_self ();
92 }
93
94 Score_element::~Score_element()
95 {
96   assert (!output_p_);
97   assert (status_i_ >=0);
98   status_i_  = -1;
99
100   delete dim_cache_[X_AXIS];
101   delete dim_cache_[Y_AXIS];  
102 }
103
104
105 Real
106 Score_element::get_real (String s) const
107 {
108   return gh_scm2double (get_elt_property (s));
109 }
110
111 void
112 Score_element::set_real (String s, Real r)
113 {
114   set_elt_property (s, gh_double2scm (r));
115 }
116
117 // should also have one that takes SCM arg. 
118 SCM
119 Score_element::get_elt_property (String nm) const
120 {
121   SCM sym =  ly_symbol2scm (nm.ch_C());
122   SCM s = scm_assq(sym, element_property_alist_);
123
124   if (s != SCM_BOOL_F)
125     return gh_cdr (s); 
126   
127   if (pscore_l_)
128     {
129       SCM sym2 = ly_symbol2scm ((name () + ("::" + nm)).ch_C());
130       SCM val;
131       
132       // should probably check for Type::sym as well.
133       Paper_def * p= pscore_l_->paper_l_;
134       if (p->default_properties_.try_retrieve (sym2, &val))
135         return val;
136       else if (p->default_properties_.try_retrieve (sym, &val))
137         return val;
138     }
139   
140   return SCM_UNDEFINED;
141 }
142
143 SCM
144 Score_element::remove_elt_property (String key)
145 {
146   SCM s = get_elt_property (key); 
147   SCM sym = ly_symbol2scm (key.ch_C());
148   element_property_alist_ =  scm_assq_remove_x (element_property_alist_, sym);
149   return s;
150 }
151
152 /*
153   UGH. assoc vs. assq
154  */
155 void
156 Score_element::set_elt_property (String k, SCM v)
157 {
158   SCM s = ly_symbol2scm (k.ch_C( ));
159   element_property_alist_ = scm_assoc_set_x (element_property_alist_, s, v);
160 }
161
162 Interval
163 Score_element::molecule_extent(Dimension_cache const *c)
164 {
165   Score_element *s = dynamic_cast<Score_element*>(c->element_l());
166   Molecule*m = s->do_brew_molecule_p();
167   return  m->extent()[c->axis ()];
168 }
169
170
171 void
172 Score_element::print() const
173 {
174 #ifndef NPRINT
175   DEBUG_OUT << classname(this) << "{\n";
176     
177   if (flower_dstream && !flower_dstream->silent_b ("Score_element"))
178     ly_display_scm (element_property_alist_);
179
180   if (original_l_)
181     DEBUG_OUT << "Copy ";
182   do_print();
183   
184   DEBUG_OUT <<  "}\n";
185 #endif
186 }
187
188 Paper_def*
189 Score_element::paper_l ()  const
190 {
191  return pscore_l_ ? pscore_l_->paper_l_ : 0;
192 }
193
194 Lookup const *
195 Score_element::lookup_l () const
196 {
197   if (!lookup_l_)
198     {
199       Score_element * urg = (Score_element*)this;
200       SCM sz = urg->remove_elt_property ("fontsize");
201       int i = (gh_number_p (sz))
202         ? gh_scm2int  (sz)
203         : 0;
204
205       urg->lookup_l_ =  (Lookup*)pscore_l_->paper_l_->lookup_l (i);
206     }
207   return lookup_l_;
208 }
209
210 void
211 Score_element::add_processing()
212 {
213   assert (status_i_ >=0);
214   if (status_i_)
215     return;
216   status_i_ ++;
217
218 #if 0
219     /*
220     UGH. UGH. UGH.
221    */
222   if (get_elt_property ("self-alignment-X") != SCM_UNDEFINED
223       && !dim_cache_[X_AXIS]->off_callback_l_)
224     {
225       dim_cache_[X_AXIS]->off_callbacks_.push (Side_position_interface::self_alignment);
226     }
227   
228   if (get_elt_property ("self-alignment-Y") != SCM_UNDEFINED
229       && !dim_cache_[X_AXIS]->off_callback_l_)
230       
231     {
232       dim_cache_[Y_AXIS]->set_offset_callback (Side_position_interface::self_alignment);
233     }
234 #endif
235   
236   do_add_processing();
237 }
238
239 void
240 Score_element::calculate_dependencies (int final, int busy,
241                                        Score_element_method_pointer funcptr)
242 {
243   assert (status_i_ >=0);
244
245   if (status_i_ >= final)
246     return;
247
248   assert (status_i_!= busy);
249   status_i_= busy;
250
251   Link_array<Score_element> dependency_arr =
252     Group_interface__extract_elements (this, (Score_element*)0, "dependencies");
253   
254   for (int i=0; i < dependency_arr.size(); i++)
255     dependency_arr[i]->calculate_dependencies (final, busy, funcptr);
256
257   Link_array<Score_element> extra (get_extra_dependencies());
258   for (int i=0; i < extra.size(); i++)
259     extra[i]->calculate_dependencies (final, busy, funcptr);
260   
261   (this->*funcptr)();
262   status_i_= final;
263 }
264
265 void
266 Score_element::output_processing () 
267 {
268   if (to_boolean  (get_elt_property ("transparent")))
269     return;
270
271   // we're being silly here. 
272   if (output_p_)
273     delete output_p_;
274   
275   output_p_ = do_brew_molecule_p ();
276   Offset o (relative_coordinate (0, X_AXIS), relative_coordinate (0, Y_AXIS));
277
278   SCM s = get_elt_property ("extra-offset");
279   if (gh_pair_p (s))
280     {
281       Real il = paper_l ()->get_var ("interline");
282       o[X_AXIS] += il * gh_scm2double (gh_car (s));
283       o[Y_AXIS] += il * gh_scm2double (gh_cdr (s));      
284     }
285   
286   pscore_l_->outputter_l_->output_molecule (output_p_,
287                                             o,
288                                             classname(this));
289
290   delete output_p_;
291   output_p_ =0;
292 }
293
294 /*
295   
296   VIRTUAL STUBS
297
298  */
299 void
300 Score_element::do_break_processing()
301 {
302 }
303
304 void
305 Score_element::do_post_processing()
306 {
307 }
308
309 void
310 Score_element::do_breakable_col_processing()
311 {
312   handle_prebroken_dependencies();
313 }
314
315 void
316 Score_element::do_pre_processing()
317 {
318 }
319
320 void
321 Score_element::do_space_processing ()
322 {
323 }
324
325 void
326 Score_element::do_add_processing()
327 {
328 }
329
330
331
332 Molecule*
333 Score_element::do_brew_molecule_p() const
334 {
335   SCM glyph = get_elt_property ("glyph");
336   if (gh_string_p (glyph))
337     {
338       Molecule*output = new Molecule (lookup_l ()->afm_find (String (ly_scm2string (glyph))));
339       
340       return output;
341     }
342   else
343     {
344       Interval emp;
345       emp.set_empty ();
346       Molecule a (lookup_l ()->fill (Box (emp,emp)));
347       return new Molecule (a);
348     }
349 }
350
351
352 Line_of_score *
353 Score_element::line_l() const
354 {
355   return 0;
356 }
357
358 void
359 Score_element::add_dependency (Score_element*e)
360 {
361   if (e)
362     {
363       Group_interface gi (this, "dependencies");
364       gi.add_element (e);
365     }
366   else
367     programming_error ("Null dependency added");
368 }
369
370
371
372
373 /**
374       Do break substitution in S, using CRITERION. Return new value.
375          CRITERION is either a SMOB pointer to the desired line, or a number
376          representing the break direction.  */
377 SCM
378 Score_element::handle_broken_smobs (SCM s, SCM criterion)
379 {
380  again:
381
382   
383   Score_element *sc = unsmob_element ( s);
384   if (sc)
385     {
386       if (criterion == SCM_UNDEFINED)
387         return SCM_UNDEFINED;
388       else if (gh_number_p (criterion))
389         {
390           Item * i = dynamic_cast<Item*> (sc);
391           Direction d = to_dir (criterion);
392           if (i && i->break_status_dir () != d)
393             {
394               Item *br = i->find_broken_piece (d);
395               return  (br) ? br->self_scm_ : SCM_UNDEFINED;
396             }
397         }
398       else
399         {
400           Score_element * ln = unsmob_element ( criterion);
401           Line_of_score * line = dynamic_cast<Line_of_score*> (ln);
402           Score_element * br =0;
403           Line_of_score * dep_line = sc->line_l ();
404           if (dep_line != line)
405             {
406               br = sc->find_broken_piece (line);
407               return  (br) ?  br->self_scm_ : SCM_UNDEFINED;
408             }
409           if (!dep_line)
410             return SCM_UNDEFINED;
411         }
412     }
413   else if (gh_pair_p (s))
414     {
415       /*
416         UGH! breaks on circular lists.
417       */
418       SCM car = handle_broken_smobs (gh_car (s), criterion);
419       SCM cdr = gh_cdr (s);
420       
421       if (car == SCM_UNDEFINED
422           && (gh_pair_p (cdr) || cdr == SCM_EOL))
423         {
424           /*
425             This is tail-recursion, ie. 
426             
427             return handle_broken_smobs (cdr, criterion);
428
429             We don't want to rely on the compiler to do this.  */
430           s =  cdr;     
431           goto again;
432         }
433
434       gh_set_car_x (s, car);
435       gh_set_cdr_x (s, handle_broken_smobs (cdr, criterion));
436       return s;
437     }
438   return s;
439 }
440
441 void
442 Score_element::handle_broken_dependencies()
443 {
444   Line_of_score *line  = line_l();
445   element_property_alist_ = handle_broken_smobs (element_property_alist_,
446                                                  line ? line->self_scm_ : SCM_UNDEFINED);
447
448   if (!line)
449     return;
450 }
451
452
453 /*
454   TODO: cleanify.
455  */
456 void
457 Score_element::handle_prebroken_dependencies()
458 {
459   if (Item*i =dynamic_cast<Item*> (this))
460     {
461       element_property_alist_
462         = handle_broken_smobs (element_property_alist_,
463                                gh_int2scm (i->break_status_dir ()));
464     }
465 }
466
467
468
469
470
471 Link_array<Score_element>
472 Score_element::get_extra_dependencies() const
473 {
474   Link_array<Score_element> empty;
475   return empty;
476 }
477
478 bool
479 Score_element::linked_b() const
480 {
481   return used_b_;
482 }
483
484 void
485 Score_element::do_print () const
486 {
487 }
488
489 Score_element*
490 Score_element::find_broken_piece (Line_of_score*) const
491 {
492   return 0;
493 }
494
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   // scm_puts (" properties = ", port);
635   // scm_display (sc->element_property_alist_, port);
636   scm_puts (" >", port);
637   return 1;
638 }
639
640 void
641 Score_element::do_smobify_self ()
642 {
643 }
644
645 SCM
646 Score_element::equal_p (SCM a, SCM b)
647 {
648   return gh_cdr(a) == gh_cdr(b) ? SCM_BOOL_T : SCM_BOOL_F;
649 }