]> git.donarmstrong.com Git - lilypond.git/blob - lily/score-element.cc
release: 1.3.8
[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
28
29 Interval
30 Score_element::dim_cache_callback (Dimension_cache const*c)
31 {
32   Score_element *  e =dynamic_cast<Score_element*> (c->element_l());
33   if(e->dim_cache_[X_AXIS] == c)
34     return e->do_width ();
35   else
36     return e->do_height ();
37 }
38
39
40
41 Real
42 set_alignment_callback (Dimension_cache const *c)
43 {
44   String s ("self-alignment-");
45   Axis ax = c->axis ();
46   s +=  (ax == X_AXIS) ? "X" : "Y";
47   Score_element *elm = dynamic_cast<Score_element*> (c->element_l ());
48   SCM align (elm->get_elt_property (s));
49   if (isdir_b (align))
50     {
51       Direction d = to_dir (align);
52       Interval ext(elm->extent (ax));
53       if (d)
54         {
55           return - ext[d];
56         }
57       return - ext.center ();
58     }
59   else
60     return 0.0;
61 }
62
63
64 Score_element::Score_element()
65 {
66   output_p_ =0;
67   dim_cache_[X_AXIS]->set_callback (dim_cache_callback);
68   dim_cache_[Y_AXIS]->set_callback (dim_cache_callback); 
69   used_b_ = false;
70   pscore_l_=0;
71   lookup_l_ =0;
72   status_i_ = 0;
73   self_scm_ = SCM_EOL;
74   original_l_ = 0;
75   element_property_alist_ = SCM_EOL;
76
77   smobify_self ();
78 }
79
80 Score_element::Score_element (Score_element const&s)
81   : Graphical_element (s)
82 {
83   
84   self_scm_ = SCM_EOL;
85   used_b_ = true;
86   original_l_ =(Score_element*) &s;
87   element_property_alist_ = scm_protect_object (scm_list_copy (s.element_property_alist_));
88   dependency_arr_ = s.dependency_arr_;
89   output_p_ =0;
90   status_i_ = s.status_i_;
91   lookup_l_ = s.lookup_l_;
92   pscore_l_ = s.pscore_l_;
93
94   smobify_self ();
95 }
96
97 Score_element::~Score_element()
98 {
99   assert (!output_p_);
100   assert (status_i_ >=0);
101   status_i_  = -1;
102 }
103
104 Score_element*
105 Score_element::dependency (int i) const
106 {
107   return dependency_arr_ [i];
108 }
109
110 int
111 Score_element::dependency_size () const
112 {
113   return dependency_arr_.size ();
114 }
115
116 // should also have one that takes SCM arg. 
117 SCM
118 Score_element::get_elt_property (String nm) const
119 {
120   SCM sym =  ly_symbol2scm (nm.ch_C());
121   SCM s = scm_assq(sym, element_property_alist_);
122
123   if (s != SCM_BOOL_F)
124     return SCM_CDR (s); 
125   
126   if (pscore_l_)
127     {
128       SCM sym2 = ly_symbol2scm ((name () + ("::" + nm)).ch_C());
129       SCM val;
130       
131       // should probably check for Type::sym as well.
132       Paper_def * p= pscore_l_->paper_l_;
133       if (p->default_properties_.try_retrieve (sym2, &val))
134         return val;
135       else if (p->default_properties_.try_retrieve (sym, &val))
136         return val;
137     }
138   
139   return SCM_UNDEFINED;
140 }
141
142 SCM
143 Score_element::remove_elt_property (String key)
144 {
145   SCM s = get_elt_property (key); 
146   SCM sym = ly_symbol2scm (key.ch_C());
147   element_property_alist_ =  scm_assq_remove_x (element_property_alist_, sym);
148   return s;
149 }
150
151 /*
152   UGH. assoc vs. assq
153  */
154 void
155 Score_element::set_elt_property (String k, SCM v)
156 {
157   SCM s = ly_symbol2scm (k.ch_C( ));
158   element_property_alist_ = scm_assoc_set_x (element_property_alist_, s, v);
159 }
160
161 Interval
162 Score_element::do_width() const 
163 {
164   Interval r;
165
166   Molecule*m = output_p_ ?  output_p_ : do_brew_molecule_p();
167   r = m->extent().x ();
168
169   if (!output_p_)
170     delete m;
171   
172   return r;
173 }
174
175 Interval
176 Score_element::do_height() const 
177 {
178   Interval r;
179   Molecule*m = output_p_ ?  output_p_ : do_brew_molecule_p();
180   r = m->extent().y ();
181   if (!output_p_)
182     delete m;
183
184   return r;
185 }
186
187 void
188 Score_element::print() const
189 {
190 #ifndef NPRINT
191   DEBUG_OUT << classname(this) << "{\n";
192   if (flower_dstream && !flower_dstream->silent_b ("Score_element"))
193     ly_display_scm (element_property_alist_);
194   DEBUG_OUT << "dependencies: " << dependency_size();
195   if (original_l_)
196     DEBUG_OUT << "Copy ";
197   Graphical_element::do_print ();
198   do_print();
199   
200   DEBUG_OUT <<  "}\n";
201 #endif
202 }
203
204 Paper_def*
205 Score_element::paper_l ()  const
206 {
207  return pscore_l_->paper_l_;
208 }
209
210 Lookup const *
211 Score_element::lookup_l () const
212 {
213   if (!lookup_l_)
214     {
215       Score_element * urg = (Score_element*)this;
216       SCM sz = urg->remove_elt_property ("fontsize");
217       int i = (sz != SCM_UNDEFINED)
218         ? gh_scm2int  (sz)
219         : 0;
220
221       urg->lookup_l_ =  (Lookup*)pscore_l_->paper_l_->lookup_l (i);
222     }
223   return lookup_l_;
224 }
225
226 void
227 Score_element::add_processing()
228 {
229   assert (status_i_ >=0);
230   if (status_i_)
231     return;
232   status_i_ ++;
233
234   if (get_elt_property ("self-alignment-X") != SCM_UNDEFINED)
235     {
236       dim_cache_[X_AXIS]->set_offset_callback (set_alignment_callback);
237     }
238   
239   if (get_elt_property ("self-alignment-Y") != SCM_UNDEFINED)
240     {
241       dim_cache_[Y_AXIS]->set_offset_callback (set_alignment_callback);
242     }
243   
244   do_add_processing();
245 }
246
247 void
248 Score_element::calculate_dependencies (int final, int busy,
249                                        Score_element_method_pointer funcptr)
250 {
251   assert (status_i_ >=0);
252
253   if (status_i_ >= final)
254     return;
255
256   assert (status_i_!= busy);
257   status_i_= busy;
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   invalidate_cache (X_AXIS);
267   invalidate_cache (Y_AXIS);
268   (this->*funcptr)();
269   status_i_= final;
270 }
271
272 void
273 Score_element::output_processing () 
274 {
275   if (get_elt_property ("transparent") != SCM_UNDEFINED)
276     return;
277
278   // we're being silly here. 
279   if (output_p_)
280     delete output_p_;
281   
282   output_p_ = do_brew_molecule_p ();
283   Offset o (relative_coordinate (0, X_AXIS), relative_coordinate (0, Y_AXIS));
284
285   SCM s = get_elt_property ("extra-offset");
286   if (gh_pair_p (s))
287     {
288       Real il = paper_l ()->get_var ("interline");
289       o[X_AXIS] += il * gh_scm2double (gh_car (s));
290       o[Y_AXIS] += il * gh_scm2double (gh_cdr (s));      
291     }
292   
293   pscore_l_->outputter_l_->output_molecule (output_p_,
294                                             o,
295                                             classname(this));
296
297   delete output_p_;
298   output_p_ =0;
299 }
300
301 /*
302   
303   VIRTUAL STUBS
304
305  */
306 void
307 Score_element::do_break_processing()
308 {
309   handle_broken_dependencies();
310 }
311
312 void
313 Score_element::do_post_processing()
314 {
315 }
316
317 void
318 Score_element::do_breakable_col_processing()
319 {
320   handle_prebroken_dependencies();
321 }
322
323 void
324 Score_element::do_pre_processing()
325 {
326 }
327
328 void
329 Score_element::do_space_processing ()
330 {
331 }
332
333 void
334 Score_element::do_add_processing()
335 {
336 }
337
338 void
339 Score_element::do_substitute_element_pointer (Score_element*,Score_element*)
340 {
341 }
342
343
344 Molecule*
345 Score_element::do_brew_molecule_p() const
346 {
347   Molecule a (lookup_l ()->fill (Box (Interval (0,0), Interval (0,0))));
348   return new Molecule (a);
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       dependency_arr_.push (e);
364       e->used_b_ = true;
365     }
366   else
367     programming_error ("Null dependency added");
368 }
369
370 void
371 Score_element::substitute_dependency (Score_element* old, Score_element* new_l)
372 {
373   do_substitute_element_pointer (old,new_l);
374   old->do_substitute_element_pointer (this, 0);
375 }
376
377
378 /**
379    Do break substitution, and return new value.
380  */
381 SCM 
382 Score_element::handle_broken_smobs (SCM s, Line_of_score * line)
383 {
384   if (SMOB_IS_TYPE_B (Score_element, s))
385     {
386       Score_element *sc = SMOB_TO_TYPE (Score_element, s);
387       Score_element * br =0;
388       if (sc->line_l () != line)
389         {
390           br = sc->find_broken_piece (line);
391         }
392
393       if (br)
394         return br->self_scm_;
395     }
396   else if (gh_pair_p (s))
397     {
398       /*
399         UGH! breaks on circular lists.
400        */
401       gh_set_car_x (s, handle_broken_smobs (gh_car (s), line));
402       gh_set_cdr_x (s, handle_broken_smobs (gh_cdr (s), line));
403     }
404   return s;
405 }
406
407 void
408 Score_element::handle_broken_dependencies()
409 {
410   Line_of_score *line  = line_l();
411   if (!line)
412     return;
413
414   element_property_alist_ = handle_broken_smobs (element_property_alist_, line);
415
416   Link_array<Score_element> new_deps;
417
418   for (int i=0; i < dependency_size(); i++) 
419     {
420       Score_element * elt = dependency (i);
421       if (elt->line_l() != line)
422         {
423           Score_element * broken = elt->find_broken_piece (line);
424           substitute_dependency (elt, broken);
425           elt  = broken ;
426         }
427       if (elt)
428         new_deps.push (elt);
429     }
430   dependency_arr_ = new_deps;
431
432 }
433
434
435 /*
436   TODO: cleanify.
437  */
438 void
439 Score_element::handle_prebroken_dependencies()
440 {
441   element_property_alist_
442     = handle_broken_smobs (element_property_alist_, line_l ());
443
444   Link_array<Score_element> old_arr, new_arr;
445   
446   for (int i=0; i < dependency_size(); i++) 
447     {
448       Score_element * elt = dependency (i);
449       Item *it_l = dynamic_cast <Item *> (elt);
450       if (it_l && it_l->broken_original_b ())
451         if (Item *me = dynamic_cast<Item*> (this) )
452           {
453             Score_element *new_l = it_l->find_broken_piece (me->break_status_dir ());
454             if (new_l != elt) 
455               {
456                 new_arr.push (new_l);
457                 old_arr.push (elt);
458               }
459           }
460         else 
461           {
462             Direction d = LEFT;
463             do {
464               old_arr.push (0);
465               new_arr.push (it_l->find_broken_piece (d));
466             } while (flip(&d)!= LEFT);
467           }
468     }
469   
470   for (int i=0;  i < old_arr.size(); i++)
471     if (old_arr[i])
472       substitute_dependency (old_arr[i], new_arr[i]);
473 }
474
475 void
476 Score_element::handle_prebroken_dependents()
477 {
478 }
479
480 void
481 Score_element::handle_broken_dependents()
482 {
483 }
484
485
486
487 Link_array<Score_element>
488 Score_element::get_extra_dependencies() const
489 {
490   Link_array<Score_element> empty;
491   return empty;
492 }
493
494 bool
495 Score_element::linked_b() const
496 {
497   return used_b_;
498 }
499
500 void
501 Score_element::do_print () const
502 {
503 }
504
505
506
507 Score_element*
508 Score_element::find_broken_piece (Line_of_score*) const
509 {
510   return 0;
511 }
512
513 SCM
514 Score_element::mark_smob (SCM ses)
515 {
516   void * mp = (void*) SCM_CDR(ses);
517   Score_element * s = (Score_element*) mp;
518
519   assert (s->self_scm_ == ses);
520   return s->element_property_alist_;
521 }
522
523
524 int
525 Score_element::print_smob (SCM s, SCM port, scm_print_state *)
526 {
527   Score_element *sc = (Score_element *) SCM_CDR (s);
528      
529   scm_puts ("#<Score_element ", port);
530   scm_puts ((char *)sc->name (), port);
531   scm_puts ("properties = ", port);
532   scm_display (sc->element_property_alist_, port);
533   scm_puts (" >", port);
534   return 1;
535 }
536
537 void
538 Score_element::do_smobify_self ()
539 {
540   scm_unprotect_object (element_property_alist_); // ugh
541 }
542 #include "ly-smobs.icc"
543 IMPLEMENT_SMOBS(Score_element);
544
545 SCM
546 Score_element::equal_p (SCM a, SCM b)
547 {
548   return SCM_CDR(a) == SCM_CDR(b) ? SCM_BOOL_T : SCM_BOOL_F;
549 }
550