]> git.donarmstrong.com Git - lilypond.git/blob - lily/grob.cc
* lily/stencil.cc (interpret_stencil_expression): add grob-cause
[lilypond.git] / lily / grob.cc
1 /*
2   grob.cc -- implement Grob
3
4   source file of the GNU LilyPond music typesetter
5
6   (c) 1997--2004 Han-Wen Nienhuys <hanwen@cs.uu.nl>
7 */
8
9
10 #include <string.h>
11 #include <math.h>
12
13 #include "main.hh"
14 #include "input-smob.hh"
15 #include "warn.hh"
16 #include "group-interface.hh"
17 #include "misc.hh"
18 #include "paper-score.hh"
19 #include "stencil.hh"
20 #include "grob.hh"
21 #include "warn.hh"
22 #include "spanner.hh"
23 #include "system.hh"
24 #include "item.hh"
25 #include "stencil.hh"
26 #include "misc.hh"
27 #include "music.hh"
28 #include "item.hh"
29
30 #include "ly-smobs.icc"
31
32 /*
33 TODO:
34
35 remove dynamic_cast<Spanner,Item> and put this code into respective
36   subclass.
37 */
38
39 #define HASH_SIZE 3
40 #define INFINITY_MSG "Infinity or NaN encountered"
41
42 Grob::Grob (SCM basicprops)
43 {
44   /*
45     fixme: default should be no callback.
46    */
47   self_scm_ = SCM_EOL;
48   pscore_=0;
49   status_ = 0;
50   original_ = 0;
51   immutable_property_alist_ =  basicprops;
52   mutable_property_alist_ = SCM_EOL;
53
54   /*
55     We do smobify_self () as the first step. Since the object lives on
56     the heap, none of its SCM variables are protected from GC. After
57     smobify_self (), they are.
58    */
59   smobify_self ();
60
61
62   
63   SCM meta = get_property ("meta");
64   if (ly_c_pair_p (meta))
65     {
66       SCM ifs = scm_assoc (ly_symbol2scm ("interfaces"), meta);
67
68       /*
69         Switch off interface checks for the moment.
70        */
71       bool itc = internal_type_checking_global_b;
72       internal_type_checking_global_b = false;
73       internal_set_property (ly_symbol2scm ("interfaces"), ly_cdr (ifs));
74       internal_type_checking_global_b = itc;
75     }
76   
77   /*
78     TODO:
79
80     destill this into a function, so we can re-init the immutable
81     properties with a new BASICPROPS value after creation. Convenient
82     eg. when using \override with StaffSymbol.  */
83   
84   char const*onames[] = {"X-offset-callbacks", "Y-offset-callbacks"};
85   char const*xnames[] = {"X-extent", "Y-extent"};
86   char const*enames[] = {"X-extent-callback", "Y-extent-callback"};
87   
88   for (int a = X_AXIS; a <= Y_AXIS; a++)
89     {
90       SCM l = get_property (onames[a]);
91
92       if (scm_ilength (l) >=0)
93         {
94           dim_cache_[a].offset_callbacks_ = l;
95           dim_cache_[a].offsets_left_ = scm_ilength (l);
96         }
97       else
98         {
99           programming_error ("[XY]-offset-callbacks must be a list");
100         }
101
102       SCM cb = get_property (enames[a]);
103       SCM xt = get_property (xnames[a]);
104       
105       /*
106         Should change default to empty? 
107       */
108       if (is_number_pair (xt))
109         cb = xt;
110       else if (cb != SCM_BOOL_F
111           && !ly_c_procedure_p (cb) && !ly_c_pair_p (cb)
112           && ly_c_procedure_p (get_property ("print-function")))
113         cb = stencil_extent_proc;
114     
115       dim_cache_[a].dimension_ = cb;
116     }
117
118 }
119
120 Grob::Grob (Grob const&s)
121    : dim_cache_ (s.dim_cache_)
122 {
123   original_ = (Grob*) &s;
124   self_scm_ = SCM_EOL;
125
126   immutable_property_alist_ = s.immutable_property_alist_;
127   mutable_property_alist_ = SCM_EOL;
128   
129   /*
130     No properties are copied. That is the job of handle_broken_dependencies.
131    */
132   
133   status_ = s.status_;
134   pscore_ = 0;
135
136   smobify_self ();
137 }
138
139 Grob::~Grob ()
140 {
141 }
142
143
144 MAKE_SCHEME_CALLBACK (Grob,stencil_extent,2);
145 SCM
146 Grob::stencil_extent (SCM element_smob, SCM scm_axis)
147 {
148   Grob *s = unsmob_grob (element_smob);
149   Axis a = (Axis) ly_scm2int (scm_axis);
150
151   Stencil *m = s->get_stencil ();
152   Interval e ;
153   if (m)
154     e = m->extent (a);
155   return ly_interval2scm (e);
156 }
157
158 Output_def*
159 Grob::get_paper ()  const
160 {
161  return pscore_ ? pscore_->paper_ : 0;
162 }
163
164
165 /*
166   Recursively track all dependencies of this Grob.  The
167   status_ field is used as a mark-field.  It is marked with
168   BUSY during execution of this function, and marked with FINAL
169   when finished.
170
171   FUNCPTR is the function to call to update this element.
172 */
173 void
174 Grob::calculate_dependencies (int final, int busy, SCM funcname)
175 {
176   if (status_ >= final)
177     return;
178
179   if (status_ == busy)
180     {
181       programming_error ("Element is busy, come back later");
182       return;
183     }
184   
185   status_= busy;
186
187   for (SCM d = get_property ("dependencies"); ly_c_pair_p (d);
188        d = ly_cdr (d))
189     {
190       unsmob_grob (ly_car (d))
191         ->calculate_dependencies (final, busy, funcname);
192     }
193
194   
195   SCM proc = internal_get_property (funcname);
196   if (ly_c_procedure_p (proc))
197     scm_call_1 (proc, this->self_scm ());
198  
199   status_ = final;
200 }
201
202 Stencil *
203 Grob::get_stencil ()  const
204 {
205   if (!is_live ())
206     {
207       return 0;
208     }
209   
210   SCM mol = get_property ("stencil");
211   if (unsmob_stencil (mol))
212     return unsmob_stencil (mol);
213
214   mol = get_uncached_stencil ();
215   
216   if (is_live ())
217     {
218       Grob *me = (Grob*)this;
219       me->set_property ("stencil", mol);
220     }
221   
222   return unsmob_stencil (mol);  
223 }
224
225 SCM
226 Grob::get_uncached_stencil ()const
227 {
228   SCM proc = get_property ("print-function");
229
230   SCM  mol = SCM_EOL;
231   if (ly_c_procedure_p (proc)) 
232     mol = scm_apply_0 (proc, scm_list_n (this->self_scm (), SCM_UNDEFINED));
233   
234   if (Stencil *m = unsmob_stencil (mol))
235      {
236       if (to_boolean (get_property ("transparent")))
237         mol = Stencil (m->extent_box (), SCM_EOL).smobbed_copy ();
238     else
239       {
240         SCM expr = scm_list_3 (ly_symbol2scm ("grob-cause"), self_scm(), m->expr ());
241         mol = Stencil (m->extent_box (),expr). smobbed_copy ();
242       }
243     }  
244
245   return mol;
246 }
247
248 /*
249   
250   VIRTUAL STUBS
251
252  */
253 void
254 Grob::do_break_processing ()
255 {
256 }
257
258 System *
259 Grob::get_system () const
260 {
261   return 0;
262 }
263
264 void
265 Grob::add_dependency (Grob*e)
266 {
267   if (e)
268     {
269       Pointer_group_interface::add_grob (this, ly_symbol2scm ("dependencies"),e);
270     }
271   else
272     programming_error ("Null dependency added");
273 }
274
275 void
276 Grob::handle_broken_dependencies ()
277 {
278   Spanner * sp = dynamic_cast<Spanner*> (this);
279   if (original_ && sp)
280     return;
281
282   if (sp)
283     {
284       /*
285         This is the original spanner. We use a special function
286         because some Spanners have enormously long lists in their
287         properties.
288        */
289       for (SCM s = mutable_property_alist_; ly_c_pair_p (s);
290            s = ly_cdr (s))
291         {
292           sp->substitute_one_mutable_property (ly_caar (s),
293                                               ly_cdar (s));
294         }
295     }
296
297   System *system = get_system ();
298
299   if (is_live ()
300       && system && common_refpoint (system, X_AXIS) && common_refpoint (system, Y_AXIS))
301     {
302       substitute_mutable_properties (system ? system->self_scm () : SCM_UNDEFINED,
303                                      mutable_property_alist_);
304     }
305   else if (dynamic_cast <System*> (this))
306     {
307       substitute_mutable_properties (SCM_UNDEFINED, mutable_property_alist_);
308     }
309   else
310     {
311       /*
312         This element is `invalid'; it has been removed from all
313         dependencies, so let's junk the element itself.
314
315         do not do this for System, since that would remove references
316         to the originals of score-grobs, which get then GC'd (a bad
317         thing.)
318  
319       */
320       suicide ();
321     }
322 }
323
324 /*
325  Note that we still want references to this element to be
326  rearranged, and not silently thrown away, so we keep pointers
327  like {broken_into_{drul,array}, original}
328 */
329 void
330 Grob::suicide ()
331 {
332   if (!is_live ())
333     return; 
334
335   
336   mutable_property_alist_ = SCM_EOL;
337   immutable_property_alist_ = SCM_EOL;
338
339   set_extent (SCM_EOL, Y_AXIS);
340   set_extent (SCM_EOL, X_AXIS);
341
342   for (int a= X_AXIS; a <= Y_AXIS; a++)
343     {
344       dim_cache_[a].offset_callbacks_ = SCM_EOL;
345       dim_cache_[a].offsets_left_ = 0;
346     }
347 }
348
349 void
350 Grob::handle_prebroken_dependencies ()
351 {
352   /*
353     Don't do this in the derived method, since we want to keep access to
354     mutable_property_alist_ centralized.
355    */
356   if (original_)
357     {
358       Item * it = dynamic_cast<Item*> (this);
359       substitute_mutable_properties (scm_int2num (it->break_status_dir ()),
360                                original_->mutable_property_alist_);
361     }
362 }
363
364 Grob*
365 Grob::find_broken_piece (System*) const
366 {
367   return 0;
368 }
369
370 /*
371   translate in one direction
372 */
373 void
374 Grob::translate_axis (Real y, Axis a)
375 {
376   if (isinf (y) || isnan (y))
377     programming_error (_ (INFINITY_MSG));
378   else
379     {
380       dim_cache_[a].offset_ += y;
381     }
382 }  
383
384
385 /*
386   Find the offset relative to D.  If   D equals THIS, then it is 0.
387   Otherwise, it recursively defd as
388   
389   OFFSET_ + PARENT_L_->relative_coordinate (D)
390 */
391 Real
392 Grob::relative_coordinate (Grob const*refp, Axis a) const
393 {
394   if (refp == this)
395     return 0.0;
396
397   /*
398     We catch PARENT_L_ == nil case with this, but we crash if we did
399     not ask for the absolute coordinate (ie. REFP == nil.)
400     
401    */
402   if (refp == dim_cache_[a].parent_)
403     return get_offset (a);
404   else
405     return get_offset (a) + dim_cache_[a].parent_->relative_coordinate (refp, a);
406 }
407
408
409   
410 /*
411   Invoke callbacks to get offset relative to parent.
412 */
413 Real
414 Grob::get_offset (Axis a) const
415 {
416   Grob *me = (Grob*) this;
417   while (dim_cache_[a].offsets_left_)
418     {
419       int l = --me->dim_cache_[a].offsets_left_;
420       SCM cb = scm_list_ref (dim_cache_[a].offset_callbacks_,  scm_int2num (l));
421       SCM retval = scm_call_2 (cb, self_scm (), scm_int2num (a));
422
423       Real r =  ly_scm2double (retval);
424       if (isinf (r) || isnan (r))
425         {
426           programming_error (INFINITY_MSG);
427           r = 0.0;
428         }
429       me->dim_cache_[a].offset_ +=r;
430     }
431   return dim_cache_[a].offset_;
432 }
433
434
435 bool
436 Grob::is_empty (Axis a)const
437 {
438   return ! (ly_c_pair_p (dim_cache_[a].dimension_) ||
439             ly_c_procedure_p (dim_cache_[a].dimension_));
440 }
441
442 Interval
443 Grob::extent (Grob * refp, Axis a) const
444 {
445   Real x = relative_coordinate (refp, a);
446
447   
448   Dimension_cache * d = (Dimension_cache *)&dim_cache_[a];
449   Interval ext ;   
450   if (ly_c_pair_p (d->dimension_))
451     ;
452   else if (ly_c_procedure_p (d->dimension_))
453     {
454       /*
455         FIXME: add doco on types, and should typecheck maybe? 
456        */
457       d->dimension_= scm_call_2 (d->dimension_, self_scm (), scm_int2num (a));
458     }
459   else
460     return ext;
461
462   if (!ly_c_pair_p (d->dimension_))
463     return ext;
464   
465   ext = ly_scm2interval (d->dimension_);
466
467   SCM extra = get_property (a == X_AXIS
468                                 ? "extra-X-extent"
469                                 : "extra-Y-extent");
470
471   /*
472     signs ?
473    */
474   if (ly_c_pair_p (extra))
475     {
476       ext[BIGGER] +=  ly_scm2double (ly_cdr (extra));
477       ext[SMALLER] +=   ly_scm2double (ly_car (extra));
478     }
479   
480   extra = get_property (a == X_AXIS
481                                 ? "minimum-X-extent"
482                                 : "minimum-Y-extent");
483   if (ly_c_pair_p (extra))
484     {
485       ext.unite (Interval (ly_scm2double (ly_car (extra)),
486                            ly_scm2double (ly_cdr (extra))));
487     }
488
489   ext.translate (x);
490   
491   return ext;
492 }
493
494 /*
495   Find the group-element which has both #this# and #s#
496 */
497 Grob * 
498 Grob::common_refpoint (Grob const* s, Axis a) const
499 {
500   /*
501     I don't like the quadratic aspect of this code, but I see no other
502     way. The largest chain of parents might be 10 high or so, so
503     it shouldn't be a real issue. */
504   for (Grob const *c = this; c; c = c->dim_cache_[a].parent_)
505     for (Grob const * d = s; d; d = d->dim_cache_[a].parent_)
506       if (d == c)
507         return (Grob*)d;
508
509   return 0;
510 }
511
512
513 Grob *
514 common_refpoint_of_list (SCM elist, Grob *common, Axis a) 
515 {
516   for (; ly_c_pair_p (elist); elist = ly_cdr (elist))
517     {
518       Grob * s = unsmob_grob (ly_car (elist));
519       if (!s)
520         continue;
521       if (common)
522         common = common->common_refpoint (s, a);
523       else
524         common = s;
525     }
526
527   return common;
528 }
529
530
531
532 Grob *
533 common_refpoint_of_array (Link_array<Grob> const &arr, Grob *common, Axis a) 
534 {
535   for (int i = arr.size () ; i--; )
536     {
537       Grob * s = arr[i];
538       if (!s)
539         continue;
540
541       if (common)
542         common = common->common_refpoint (s, a);
543       else
544         common = s;
545     }
546
547   return common;
548 }
549
550 String
551 Grob::name () const
552 {
553   SCM meta = get_property ("meta");
554   SCM nm = scm_assoc (ly_symbol2scm ("name"), meta);
555   nm = (ly_c_pair_p (nm)) ? ly_cdr (nm) : SCM_EOL;
556   return  ly_c_symbol_p (nm) ? ly_symbol2string (nm) :  classname (this);  
557 }
558
559 void
560 Grob::add_offset_callback (SCM cb, Axis a)
561 {
562   if (!has_offset_callback (cb, a))
563   {
564     dim_cache_[a].offset_callbacks_ = scm_cons (cb, dim_cache_[a].offset_callbacks_);
565     dim_cache_[a].offsets_left_ ++;
566   }
567 }
568
569 bool
570 Grob::has_extent_callback (SCM cb, Axis a)const
571 {
572   return scm_equal_p (cb, dim_cache_[a].dimension_) == SCM_BOOL_T;
573 }
574
575
576 bool
577 Grob::has_offset_callback (SCM cb, Axis a)const
578 {
579   return scm_c_memq (cb, dim_cache_[a].offset_callbacks_) != SCM_BOOL_F;
580 }
581
582 void
583 Grob::set_extent (SCM dc, Axis a)
584 {
585   dim_cache_[a].dimension_ = dc;
586 }
587
588 void
589 Grob::set_parent (Grob *g, Axis a)
590 {
591   dim_cache_[a].parent_ = g;
592 }
593
594 MAKE_SCHEME_CALLBACK (Grob,fixup_refpoint,1);
595 SCM
596 Grob::fixup_refpoint (SCM smob)
597 {
598   Grob *me = unsmob_grob (smob);
599   for (int a = X_AXIS; a < NO_AXES; a ++)
600     {
601       Axis ax = (Axis)a;
602       Grob * parent = me->get_parent (ax);
603
604       if (!parent)
605         continue;
606       
607       if (parent->get_system () != me->get_system () && me->get_system ())
608         {
609           Grob * newparent = parent->find_broken_piece (me->get_system ());
610           me->set_parent (newparent, ax);
611         }
612
613       if (Item * i  = dynamic_cast<Item*> (me))
614         {
615           Item *parenti = dynamic_cast<Item*> (parent);
616
617           if (parenti && i)
618             {
619               Direction  my_dir = i->break_status_dir () ;
620               if (my_dir!= parenti->break_status_dir ())
621                 {
622                   Item *newparent =  parenti->find_prebroken_piece (my_dir);
623                   me->set_parent (newparent, ax);
624                 }
625             }
626         }
627     }
628   return smob;
629 }
630
631 void
632 Grob::warning (String s)const
633 {
634   SCM cause = self_scm ();
635   while (Grob * g = unsmob_grob (cause))
636     {
637       cause = g->get_property ("cause");
638     }
639
640   if (Music *m = unsmob_music (cause))
641     {
642       m->origin ()->warning (s);
643     }
644   else
645     ::warning (s);
646 }
647
648 void
649 Grob::programming_error (String s)const
650 {
651   s = "Programming error: "  + s;
652   warning (s);
653 }
654
655
656 /****************************************************
657   SMOB funcs
658  ****************************************************/
659
660
661
662 IMPLEMENT_SMOBS (Grob);
663 IMPLEMENT_DEFAULT_EQUAL_P (Grob);
664
665 SCM
666 Grob::mark_smob (SCM ses)
667 {
668   Grob * s = (Grob*) SCM_CELL_WORD_1 (ses);
669   scm_gc_mark (s->immutable_property_alist_);
670
671   for (int a =0 ; a < 2; a++)
672     {
673       scm_gc_mark (s->dim_cache_[a].offset_callbacks_);
674       scm_gc_mark (s->dim_cache_[a].dimension_);
675       
676       /*
677         don't mark the parents. The pointers in the mutable property
678         list form two tree like structures (one for X relations, one
679         for Y relations). Marking these can be done in limited stack
680         space.  If we add the parents, we will jump between X and Y in
681         an erratic manner, leading to much more recursion depth (and
682         core dumps if we link to pthreads.)
683        */
684     }
685   
686   if (s->original_)
687     scm_gc_mark (s->original_->self_scm ());
688
689   s->do_derived_mark ();  
690   return s->mutable_property_alist_;
691 }
692
693 int
694 Grob::print_smob (SCM s, SCM port, scm_print_state *)
695 {
696   Grob *sc = (Grob *) ly_cdr (s);
697      
698   scm_puts ("#<Grob ", port);
699   scm_puts ((char *)sc->name ().to_str0 (), port);
700
701   /*
702     don't try to print properties, that is too much hassle.
703    */
704   scm_puts (" >", port);
705   return 1;
706 }
707
708 SCM
709 Grob::do_derived_mark () const
710 {
711   return SCM_EOL;
712 }
713
714
715
716 void
717 Grob::discretionary_processing ()
718 {
719 }
720
721 bool
722 Grob::internal_has_interface (SCM k)
723 {
724   SCM ifs = get_property ("interfaces");
725
726   return scm_c_memq (k, ifs) != SCM_BOOL_F;
727 }
728
729
730 /** Return Array of Grobs in SCM list L */
731 Link_array<Grob>
732 ly_scm2grobs (SCM l)
733 {
734   Link_array<Grob> arr;
735
736   for (SCM s = l; ly_c_pair_p (s); s = ly_cdr (s))
737     {
738       SCM e = ly_car (s);
739       arr.push (unsmob_grob (e));
740     }
741
742   arr.reverse ();
743   return arr;
744 }
745
746 /** Return SCM list of Grob array A */
747 SCM
748 ly_grobs2scm (Link_array<Grob> a)
749 {
750   SCM s = SCM_EOL;
751   for (int i = a.size (); i; i--)
752     s = scm_cons (a[i-1]->self_scm (), s);
753
754   return s;
755 }
756
757
758 IMPLEMENT_TYPE_P (Grob, "ly:grob?");
759
760 ADD_INTERFACE (Grob, "grob-interface",
761                "A grob represents a piece of music notation\n"
762                "\n"
763 "All grobs have an X and Y-position on the page.  These X and Y positions\n"
764 "are stored in a relative format, so they can easily be combined by\n"
765 "stacking them, hanging one grob to the side of another, and coupling\n"
766 "them into a grouping objects.\n"
767 "\n"
768 "Each grob has a reference point (a.k.a.  parent): the position of a grob\n"
769 "is stored relative to that reference point. For example the X-reference\n"
770 "point of a staccato dot usually is the note head that it applies\n"
771 "to. When the note head is moved, the staccato dot moves along\n"
772 "automatically.\n"
773 "\n"
774 "A grob is often associated with a symbol, but some grobs do not print\n"
775 "any symbols. They take care of grouping objects. For example, there is a\n"
776 "separate grob that stacks staves vertically. The @ref{NoteCollision}\n"
777 "is also an abstract grob: it only moves around chords, but doesn't print\n"
778 "anything.\n"
779 "\n"
780                "Grobs have a properties: Scheme variables, that can be read and set. "
781                "They have two types. Immutable variables "
782                "define the default style and behavior.  They are shared between  many objects. "
783                "They can be changed using @code{\\override} and @code{\\revert}. "
784                "\n\n"
785                "Mutable properties are variables that are specific to one grob. Typically, "
786                "lists of other objects, or results from computations are stored in"
787                "mutable properties: every call to set-grob-property (or its C++ equivalent) "
788                "sets a mutable property. " 
789
790 ,
791                "X-offset-callbacks Y-offset-callbacks X-extent-callback stencil cause "
792                "Y-extent-callback print-function extra-offset spacing-procedure "
793                "staff-symbol interfaces dependencies X-extent Y-extent extra-X-extent "
794                "meta layer before-line-breaking-callback "
795                "after-line-breaking-callback extra-Y-extent minimum-X-extent "
796                // FIXME: page-penalty?
797                "minimum-Y-extent page-penalty transparent "
798                );
799
800