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