]> git.donarmstrong.com Git - lilypond.git/blob - lily/grob.cc
check for cyclic dependencies in pure Y-offset stuff
[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--2007 Han-Wen Nienhuys <hanwen@xs4all.nl>
7 */
8
9 #include "grob.hh"
10
11 #include <cstring>
12
13 #include "align-interface.hh"
14 #include "input.hh"
15 #include "international.hh"
16 #include "item.hh"
17 #include "main.hh"
18 #include "misc.hh"
19 #include "music.hh"
20 #include "output-def.hh"
21 #include "pointer-group-interface.hh"
22 #include "stencil.hh"
23 #include "stream-event.hh"
24 #include "system.hh"
25 #include "warn.hh"
26
27 #include "ly-smobs.icc"
28
29 Grob *
30 Grob::clone () const
31 {
32   return new Grob (*this);
33 }
34
35 Grob::Grob (SCM basicprops)         
36 {
37   
38   /* FIXME: default should be no callback.  */
39   self_scm_ = SCM_EOL;
40   layout_ = 0;
41   original_ = 0;
42   interfaces_ = SCM_EOL;
43   immutable_property_alist_ = basicprops;
44   mutable_property_alist_ = SCM_EOL;
45   object_alist_ = SCM_EOL;
46   
47   /* We do smobify_self () as the first step.  Since the object lives
48      on the heap, none of its SCM variables are protected from
49      GC. After smobify_self (), they are.  */
50   smobify_self ();
51
52   SCM meta = get_property ("meta");
53   if (scm_is_pair (meta))
54     {
55       interfaces_ = scm_cdr (scm_assq (ly_symbol2scm ("interfaces"), meta));
56
57       SCM object_cbs = scm_assq (ly_symbol2scm ("object-callbacks"), meta);
58       if (scm_is_pair (object_cbs))
59         {
60           for (SCM s = scm_cdr (object_cbs); scm_is_pair (s); s = scm_cdr (s))
61             set_object (scm_caar (s), scm_cdar (s)); 
62         }
63     }
64   
65   if (get_property_data ("X-extent") == SCM_EOL)
66     set_property ("X-extent", Grob::stencil_width_proc);
67   if (get_property_data ("Y-extent") == SCM_EOL)
68     set_property ("Y-extent", Grob::stencil_height_proc);
69 }
70
71 Grob::Grob (Grob const &s)
72   : dim_cache_ (s.dim_cache_)
73 {
74   original_ = (Grob *) & s;
75   self_scm_ = SCM_EOL;
76
77   immutable_property_alist_ = s.immutable_property_alist_;
78   mutable_property_alist_ = ly_deep_copy (s.mutable_property_alist_);
79   interfaces_ = s.interfaces_;
80   object_alist_ = SCM_EOL;
81
82   layout_ = 0;
83
84   smobify_self ();
85 }
86
87 Grob::~Grob ()
88 {
89 }
90 /****************************************************************
91   STENCILS
92 ****************************************************************/
93
94 Stencil *
95 Grob::get_stencil () const
96 {
97   if (!is_live ())
98     return 0;
99
100   SCM stil = get_property ("stencil");
101   return unsmob_stencil (stil);
102 }
103
104 Stencil
105 Grob::get_print_stencil () const
106 {
107   SCM stil = get_property ("stencil");
108
109   Stencil retval;
110   if (Stencil *m = unsmob_stencil (stil))
111     {
112       retval = *m;
113       if (to_boolean (get_property ("transparent")))
114         retval = Stencil (m->extent_box (), SCM_EOL);
115       else
116         {
117           SCM expr = m->expr ();
118           expr = scm_list_3 (ly_symbol2scm ("grob-cause"),
119                              self_scm (), expr);
120
121           retval = Stencil (m->extent_box (), expr);
122         }
123
124       SCM rot = get_property ("rotation");
125       if (scm_is_pair (rot))
126         {
127           Real angle = scm_to_double (scm_car (rot));
128           Real x = scm_to_double (scm_cadr (rot));
129           Real y = scm_to_double (scm_caddr (rot));
130
131           retval.rotate_degrees (angle, Offset (x, y));
132         }
133
134       /* color support... see interpret_stencil_expression () for more... */
135       SCM color = get_property ("color");
136       if (scm_is_pair (color))
137         {
138           SCM expr = scm_list_3 (ly_symbol2scm ("color"),
139                                  color,
140                                  retval.expr ());
141
142           retval = Stencil (retval.extent_box (), expr);
143         }
144
145     }
146
147   return retval;
148 }
149
150 /****************************************************************
151   VIRTUAL STUBS
152 ****************************************************************/
153 void
154 Grob::do_break_processing ()
155 {
156 }
157
158 void
159 Grob::discretionary_processing ()
160 {
161 }
162
163 System *
164 Grob::get_system () const
165 {
166   return 0;
167 }
168
169
170 void
171 Grob::handle_broken_dependencies ()
172 {
173   Spanner *sp = dynamic_cast<Spanner *> (this);
174   if (original () && sp)
175     return;
176
177   if (sp)
178     /* THIS, SP is the original spanner.  We use a special function
179        because some Spanners have enormously long lists in their
180        properties, and a special function fixes FOO  */
181     {
182       for (SCM s = object_alist_; scm_is_pair (s); s = scm_cdr (s))
183         sp->substitute_one_mutable_property (scm_caar (s), scm_cdar (s));
184     }
185   System *system = get_system ();
186
187   if (is_live ()
188       && system
189       && common_refpoint (system, X_AXIS)
190       && common_refpoint (system, Y_AXIS))
191     substitute_object_links (system->self_scm (), object_alist_);
192   else if (dynamic_cast<System *> (this))
193     substitute_object_links (SCM_UNDEFINED, object_alist_);
194   else
195     /* THIS element is `invalid'; it has been removed from all
196        dependencies, so let's junk the element itself.
197
198        Do not do this for System, since that would remove references
199        to the originals of score-grobs, which get then GC'd (a bad
200        thing).  */
201     suicide ();
202 }
203
204 /* Note that we still want references to this element to be
205    rearranged, and not silently thrown away, so we keep pointers like
206    {broken_into_{drul, array}, original}
207 */
208 void
209 Grob::suicide ()
210 {
211   if (!is_live ())
212     return;
213
214   for (int a = X_AXIS; a < NO_AXES; a++)
215     dim_cache_[a].clear ();
216
217   mutable_property_alist_ = SCM_EOL;
218   object_alist_ = SCM_EOL;
219   immutable_property_alist_ = SCM_EOL;
220   interfaces_ = SCM_EOL;
221 }
222
223 void
224 Grob::handle_prebroken_dependencies ()
225 {
226   /* Don't do this in the derived method, since we want to keep access to
227      object_alist_ centralized.  */
228   if (original ())
229     {
230       Item *it = dynamic_cast<Item *> (this);
231       substitute_object_links (scm_from_int (it->break_status_dir ()),
232                                original ()->object_alist_);
233     }
234 }
235
236 Grob *
237 Grob::find_broken_piece (System *) const
238 {
239   return 0;
240 }
241
242 /****************************************************************
243    OFFSETS
244 ****************************************************************/
245
246 void
247 Grob::translate_axis (Real y, Axis a)
248 {
249   if (isinf (y) || isnan (y))
250     {
251       programming_error (_ ("Infinity or NaN encountered"));
252       return ;
253     }
254   
255   if (!dim_cache_[a].offset_)
256     dim_cache_[a].offset_ = new Real (y);
257   else
258     *dim_cache_[a].offset_ += y;  
259 }
260
261 /* Find the offset relative to D.  If D equals THIS, then it is 0.
262    Otherwise, it recursively defd as
263
264    OFFSET_ + PARENT_L_->relative_coordinate (D) */
265 Real
266 Grob::relative_coordinate (Grob const *refp, Axis a) const
267 {
268   if (refp == this)
269     return 0.0;
270
271   /* We catch PARENT_L_ == nil case with this, but we crash if we did
272      not ask for the absolute coordinate (ie. REFP == nil.)  */
273   Real off = get_offset (a);
274   if (refp == dim_cache_[a].parent_)
275     return off;
276   
277   off += dim_cache_[a].parent_->relative_coordinate (refp, a);
278
279   return off;
280 }
281
282 Real
283 Grob::pure_relative_y_coordinate (Grob const *refp, int start, int end)
284 {
285   if (refp == this)
286     return 0.0;
287
288   Real off = 0;
289
290   if (dim_cache_[Y_AXIS].offset_)
291     {
292       if (to_boolean (get_property ("pure-Y-offset-in-progress")))
293         programming_error ("cyclic chain in pure-Y-offset callbacks");
294
295       off = *dim_cache_[Y_AXIS].offset_;
296     }
297   else
298     {
299       SCM proc = get_property_data ("Y-offset");
300
301       dim_cache_[Y_AXIS].offset_ = new Real (0.0);
302       set_property ("pure-Y-offset-in-progress", SCM_BOOL_T);
303       off = robust_scm2double (call_pure_function (proc,
304                                                    scm_list_1 (self_scm ()),
305                                                    start, end),
306                                0.0);
307       del_property ("pure-Y-offset-in-progress");
308       delete dim_cache_[Y_AXIS].offset_;
309       dim_cache_[Y_AXIS].offset_ = 0;
310     }
311
312   /* we simulate positioning-done if we are the child of a VerticalAlignment,
313      but only if we don't have a cached offset. If we do have a cached offset,
314      it probably means that the Alignment was fixed and it has already been
315      calculated.
316   */
317   if (Grob *p = get_parent (Y_AXIS))
318     {
319       Real trans = 0;
320       if (Align_interface::has_interface (p) && !dim_cache_[Y_AXIS].offset_)
321         trans = Align_interface::get_pure_child_y_translation (p, this, start, end);
322
323       return off + trans + p->pure_relative_y_coordinate (refp, start, end);
324     }
325   return off;
326 }
327
328 /* Invoke callbacks to get offset relative to parent.  */
329 Real
330 Grob::get_offset (Axis a) const
331 {
332   if (dim_cache_[a].offset_)
333     return *dim_cache_[a].offset_;
334
335   Grob *me = (Grob *) this;
336
337   SCM sym = axis_offset_symbol (a);
338   me->dim_cache_[a].offset_ = new Real (0.0);
339
340   /*
341     UGH: can't fold next 2 statements together. Apparently GCC thinks
342     dim_cache_[a].offset_ is unaliased.
343   */
344   Real off = robust_scm2double (internal_get_property (sym), 0.0);
345   if (me->dim_cache_[a].offset_)
346     {
347       *me->dim_cache_[a].offset_ += off;
348       me->del_property (sym);
349       return *me->dim_cache_[a].offset_;
350     }
351   else
352     return 0.0;
353 }
354
355 Real
356 Grob::maybe_pure_coordinate (Grob const *refp, Axis a, bool pure, int start, int end)
357 {
358   if (pure && a != Y_AXIS)
359     programming_error ("tried to get pure X-offset");
360   return (pure && a == Y_AXIS) ? pure_relative_y_coordinate (refp, start, end)
361     : relative_coordinate (refp, a);
362 }
363
364 /****************************************************************
365   extents
366 ****************************************************************/
367
368 void
369 Grob::flush_extent_cache (Axis axis)
370 {
371   if (dim_cache_[axis].extent_)
372     {
373       /*
374         Ugh, this is not accurate; will flush property, causing
375         callback to be called if.
376        */
377       del_property ((axis == X_AXIS) ? ly_symbol2scm ("X-extent") : ly_symbol2scm ("Y-extent"));
378       delete dim_cache_[axis].extent_;
379       dim_cache_[axis].extent_ = 0;
380       if (get_parent (axis))
381         get_parent (axis)->flush_extent_cache (axis);
382     }
383 }
384
385
386 Interval
387 Grob::extent (Grob *refp, Axis a) const
388 {
389   Real offset = relative_coordinate (refp, a);
390   Interval real_ext;
391   if (dim_cache_[a].extent_)
392     {
393       real_ext = *dim_cache_[a].extent_;
394     }
395   else
396     {
397       /*
398         Order is significant: ?-extent may trigger suicide.
399        */
400       SCM ext_sym =
401         (a == X_AXIS)
402         ? ly_symbol2scm ("X-extent")
403         : ly_symbol2scm ("Y-extent");
404         
405       SCM ext = internal_get_property (ext_sym);
406       if (is_number_pair (ext))
407         real_ext.unite (ly_scm2interval (ext));
408
409       SCM min_ext_sym =
410         (a == X_AXIS)
411         ? ly_symbol2scm ("minimum-X-extent")
412         : ly_symbol2scm ("minimum-Y-extent");
413       SCM min_ext = internal_get_property (min_ext_sym);
414       if (is_number_pair (min_ext))
415         real_ext.unite (ly_scm2interval (min_ext));
416
417       ((Grob*)this)->dim_cache_[a].extent_ = new Interval (real_ext);  
418     }
419   
420   real_ext.translate (offset);
421   
422   return real_ext;
423 }
424
425 Interval
426 Grob::pure_height (Grob *refp, int start, int end)
427 {
428   SCM proc = get_property_data (ly_symbol2scm ("Y-extent"));
429   SCM iv_scm = call_pure_function (proc,
430                                    scm_list_1 (self_scm ()),
431                                    start, end);
432   Interval iv = robust_scm2interval (iv_scm, Interval (0, 0));
433   Real offset = pure_relative_y_coordinate (refp, start, end);
434
435   SCM min_ext = get_property ("minimum-Y-extent");
436
437   /* we don't add minimum-Y-extent if the extent is empty. This solves
438      a problem with Hara-kiri spanners. They would request_suicide and
439      return empty extents, but we would force them here to be large. */
440   if (!iv.is_empty () && is_number_pair (min_ext))
441     iv.unite (ly_scm2interval (min_ext));
442
443   iv.translate (offset);
444   return iv;
445 }
446
447 Interval
448 Grob::maybe_pure_extent (Grob *refp, Axis a, bool pure, int start, int end)
449 {
450   if (pure && a != Y_AXIS)
451     programming_error ("tried to get pure width");
452   return (pure && a == Y_AXIS) ? pure_height (refp, start, end) : extent (refp, a);
453 }
454
455 Interval_t<int>
456 Grob::spanned_rank_interval () const
457 {
458   return Interval_t<int> (-1, 0);
459 }
460
461 /****************************************************************
462   REFPOINTS
463 ****************************************************************/
464
465 /* Find the group-element which has both #this# and #s#  */
466 Grob *
467 Grob::common_refpoint (Grob const *s, Axis a) const
468 {
469   /* I don't like the quadratic aspect of this code, but I see no
470      other way.  The largest chain of parents might be 10 high or so,
471      so it shouldn't be a real issue.  */
472   for (Grob const *c = this; c; c = c->dim_cache_[a].parent_)
473     for (Grob const *d = s; d; d = d->dim_cache_[a].parent_)
474       if (d == c)
475         return (Grob *) d;
476
477   return 0;
478 }
479
480 void
481 Grob::set_parent (Grob *g, Axis a)
482 {
483   dim_cache_[a].parent_ = g;
484 }
485
486 Grob *
487 Grob::get_parent (Axis a) const
488 {
489   return dim_cache_[a].parent_;
490 }
491
492
493 void
494 Grob::fixup_refpoint ()
495 {
496   for (int a = X_AXIS; a < NO_AXES; a++)
497     {
498       Axis ax = (Axis)a;
499       Grob *parent = get_parent (ax);
500
501       if (!parent)
502         continue;
503
504       if (parent->get_system () != get_system () && get_system ())
505         {
506           Grob *newparent = parent->find_broken_piece (get_system ());
507           set_parent (newparent, ax);
508         }
509
510       if (Item *i = dynamic_cast<Item *> (this))
511         {
512           Item *parenti = dynamic_cast<Item *> (parent);
513
514           if (parenti && i)
515             {
516               Direction my_dir = i->break_status_dir ();
517               if (my_dir != parenti->break_status_dir ())
518                 {
519                   Item *newparent = parenti->find_prebroken_piece (my_dir);
520                   set_parent (newparent, ax);
521                 }
522             }
523         }
524     }
525 }
526
527
528 /****************************************************************
529   MESSAGES
530 ****************************************************************/
531 void
532 Grob::warning (string s) const
533 {
534   SCM cause = self_scm ();
535   while (Grob *g = unsmob_grob (cause))
536     cause = g->get_property ("cause");
537
538   /* ES TODO: cause can't be Music*/
539   if (Music *m = unsmob_music (cause))
540     m->origin ()->warning (s);
541   else if (Stream_event *ev = unsmob_stream_event (cause))
542     ev->origin ()->warning (s);
543   else
544     ::warning (s);
545 }
546
547
548 string
549 Grob::name () const
550 {
551   SCM meta = get_property ("meta");
552   SCM nm = scm_assq (ly_symbol2scm ("name"), meta);
553   nm = (scm_is_pair (nm)) ? scm_cdr (nm) : SCM_EOL;
554   return scm_is_symbol (nm) ? ly_symbol2string (nm) : this->class_name ();
555 }
556
557 void
558 Grob::programming_error (string s) const
559 {
560   SCM cause = self_scm ();
561   while (Grob *g = unsmob_grob (cause))
562     cause = g->get_property ("cause");
563
564   s = _f ("programming error: %s", s);
565
566   /* ES TODO: cause can't be Music*/
567   if (Music *m = unsmob_music (cause))
568     m->origin ()->message (s);
569   else if (Stream_event *ev = unsmob_stream_event (cause))
570     ev->origin ()->warning (s);
571   else
572     ::message (s);
573 }
574
575
576 ADD_INTERFACE (Grob,
577                "A grob represents a piece of music notation\n"
578                "\n"
579                "All grobs have an X and Y-position on the page.  These X and Y positions\n"
580                "are stored in a relative format, so they can easily be combined by\n"
581                "stacking them, hanging one grob to the side of another, and coupling\n"
582                "them into a grouping objects.\n"
583                "\n"
584                "Each grob has a reference point (a.k.a.  parent): the position of a grob\n"
585                "is stored relative to that reference point. For example the X-reference\n"
586                "point of a staccato dot usually is the note head that it applies\n"
587                "to. When the note head is moved, the staccato dot moves along\n"
588                "automatically.\n"
589                "\n"
590                "A grob is often associated with a symbol, but some grobs do not print\n"
591                "any symbols. They take care of grouping objects. For example, there is a\n"
592                "separate grob that stacks staves vertically. The @ref{NoteCollision}\n"
593                "is also an abstract grob: it only moves around chords, but doesn't print\n"
594                "anything.\n"
595                "\n"
596                "Grobs have a properties: Scheme variables, that can be read and set. "
597                "They have two types. Immutable variables "
598                "define the default style and behavior.  They are shared between  many objects. "
599                "They can be changed using @code{\\override} and @code{\\revert}. "
600                "\n\n"
601                "Mutable properties are variables that are specific to one grob. Typically, "
602                "lists of other objects, or results from computations are stored in"
603                "mutable properties: every call to set-grob-property (or its C++ equivalent) "
604                "sets a mutable property. "
605                "\n\n"
606                "The properties @code{after-line-breaking} and @code{before-line-breaking} "
607                "are dummies that are not user-serviceable. "
608
609                ,
610
611                /* properties */
612                "X-extent "
613                "X-offset "
614                "Y-extent "
615                "Y-offset "
616                "after-line-breaking "
617                "avoid-slur "
618                "axis-group-parent-X "
619                "axis-group-parent-Y "
620                "before-line-breaking "
621                "cause "
622                "color "
623                "cross-staff "
624                "extra-X-extent "
625                "extra-Y-extent "
626                "extra-offset "
627                "interfaces "
628                "layer "
629                "meta "
630                "minimum-X-extent "
631                "minimum-Y-extent "
632                "outside-staff-horizontal-padding "
633                "outside-staff-padding "
634                "outside-staff-priority "
635                "pure-Y-offset-in-progress "
636                "rotation "
637                "springs-and-rods "
638                "staff-symbol "
639                "stencil "
640                "transparent "
641                );
642
643 /****************************************************************
644   CALLBACKS
645 ****************************************************************/
646
647 static SCM
648 grob_stencil_extent (Grob *me, Axis a)
649 {
650   Stencil *m = me->get_stencil ();
651   Interval e;
652   if (m)
653     e = m->extent (a);
654   return ly_interval2scm (e);
655 }
656
657
658 MAKE_SCHEME_CALLBACK (Grob, stencil_height, 1);
659 SCM
660 Grob::stencil_height (SCM smob)
661 {
662   Grob *me = unsmob_grob (smob);
663   return grob_stencil_extent (me, Y_AXIS);
664 }
665
666 MAKE_SCHEME_CALLBACK (Grob, y_parent_positioning, 1);
667 SCM
668 Grob::y_parent_positioning (SCM smob)
669 {
670   Grob *me = unsmob_grob (smob);
671   Grob *par = me->get_parent (Y_AXIS);
672   if (par)
673     (void) par->get_property ("positioning-done");
674
675   return scm_from_double (0.0);
676 }
677
678
679 MAKE_SCHEME_CALLBACK (Grob, x_parent_positioning, 1);
680 SCM
681 Grob::x_parent_positioning (SCM smob)
682 {
683   Grob *me = unsmob_grob (smob);
684   
685   Grob *par = me->get_parent (X_AXIS);
686   if (par)
687     (void) par->get_property ("positioning-done");
688
689   return scm_from_double (0.0);
690 }
691
692 MAKE_SCHEME_CALLBACK (Grob, stencil_width, 1);
693 SCM
694 Grob::stencil_width (SCM smob)
695 {
696   Grob *me = unsmob_grob (smob);
697   return grob_stencil_extent (me, X_AXIS);
698 }
699
700
701
702 Grob *
703 common_refpoint_of_list (SCM elist, Grob *common, Axis a)
704 {
705   for (; scm_is_pair (elist); elist = scm_cdr (elist))
706     if (Grob *s = unsmob_grob (scm_car (elist)))
707       {
708         if (common)
709           common = common->common_refpoint (s, a);
710         else
711           common = s;
712       }
713
714   return common;
715 }
716
717 Grob *
718 common_refpoint_of_array (vector<Grob*> const &arr, Grob *common, Axis a)
719 {
720   for (vsize i = arr.size (); i--;)
721     if (Grob *s = arr[i])
722       {
723         if (common)
724           common = common->common_refpoint (s, a);
725         else
726           common = s;
727       }
728
729   return common;
730 }
731
732 Interval
733 robust_relative_extent (Grob *me, Grob *refpoint, Axis a)
734 {
735   Interval ext = me->extent (refpoint, a);
736   if (ext.is_empty ())
737     ext.add_point (me->relative_coordinate (refpoint, a));
738
739   return ext;
740 }
741