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