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