]> git.donarmstrong.com Git - lilypond.git/blob - lily/grob.cc
Run `make grand-replace'.
[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--2008 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   /* eaa - hmmm, should we do a programming_error() here? */
269   if ((this == NULL) || (refp == this))
270     return 0.0;
271
272   /* We catch PARENT_L_ == nil case with this, but we crash if we did
273      not ask for the absolute coordinate (ie. REFP == nil.)  */
274   Real off = get_offset (a);
275   if (refp == dim_cache_[a].parent_)
276     return off;
277   
278   off += dim_cache_[a].parent_->relative_coordinate (refp, a);
279
280   return off;
281 }
282
283 Real
284 Grob::pure_relative_y_coordinate (Grob const *refp, int start, int end)
285 {
286   if (refp == this)
287     return 0.0;
288
289   Real off = 0;
290
291   if (dim_cache_[Y_AXIS].offset_)
292     {
293       if (to_boolean (get_property ("pure-Y-offset-in-progress")))
294         programming_error ("cyclic chain in pure-Y-offset callbacks");
295
296       off = *dim_cache_[Y_AXIS].offset_;
297     }
298   else
299     {
300       SCM proc = get_property_data ("Y-offset");
301
302       dim_cache_[Y_AXIS].offset_ = new Real (0.0);
303       set_property ("pure-Y-offset-in-progress", SCM_BOOL_T);
304       off = robust_scm2double (call_pure_function (proc,
305                                                    scm_list_1 (self_scm ()),
306                                                    start, end),
307                                0.0);
308       del_property ("pure-Y-offset-in-progress");
309       delete dim_cache_[Y_AXIS].offset_;
310       dim_cache_[Y_AXIS].offset_ = 0;
311     }
312
313   /* we simulate positioning-done if we are the child of a VerticalAlignment,
314      but only if we don't have a cached offset. If we do have a cached offset,
315      it probably means that the Alignment was fixed and it has already been
316      calculated.
317   */
318   if (Grob *p = get_parent (Y_AXIS))
319     {
320       Real trans = 0;
321       if (Align_interface::has_interface (p) && !dim_cache_[Y_AXIS].offset_)
322         trans = Align_interface::get_pure_child_y_translation (p, this, start, end);
323
324       return off + trans + p->pure_relative_y_coordinate (refp, start, end);
325     }
326   return off;
327 }
328
329 /* Invoke callbacks to get offset relative to parent.  */
330 Real
331 Grob::get_offset (Axis a) const
332 {
333   if (dim_cache_[a].offset_)
334     return *dim_cache_[a].offset_;
335
336   Grob *me = (Grob *) this;
337
338   SCM sym = axis_offset_symbol (a);
339   me->dim_cache_[a].offset_ = new Real (0.0);
340
341   /*
342     UGH: can't fold next 2 statements together. Apparently GCC thinks
343     dim_cache_[a].offset_ is unaliased.
344   */
345   Real off = robust_scm2double (internal_get_property (sym), 0.0);
346   if (me->dim_cache_[a].offset_)
347     {
348       *me->dim_cache_[a].offset_ += off;
349       me->del_property (sym);
350       return *me->dim_cache_[a].offset_;
351     }
352   else
353     return 0.0;
354 }
355
356 Real
357 Grob::maybe_pure_coordinate (Grob const *refp, Axis a, bool pure, int start, int end)
358 {
359   if (pure && a != Y_AXIS)
360     programming_error ("tried to get pure X-offset");
361   return (pure && a == Y_AXIS) ? pure_relative_y_coordinate (refp, start, end)
362     : relative_coordinate (refp, a);
363 }
364
365 /****************************************************************
366   extents
367 ****************************************************************/
368
369 void
370 Grob::flush_extent_cache (Axis axis)
371 {
372   if (dim_cache_[axis].extent_)
373     {
374       /*
375         Ugh, this is not accurate; will flush property, causing
376         callback to be called if.
377        */
378       del_property ((axis == X_AXIS) ? ly_symbol2scm ("X-extent") : ly_symbol2scm ("Y-extent"));
379       delete dim_cache_[axis].extent_;
380       dim_cache_[axis].extent_ = 0;
381       if (get_parent (axis))
382         get_parent (axis)->flush_extent_cache (axis);
383     }
384 }
385
386
387 Interval
388 Grob::extent (Grob *refp, Axis a) const
389 {
390   Real offset = relative_coordinate (refp, a);
391   Interval real_ext;
392   if (dim_cache_[a].extent_)
393     {
394       real_ext = *dim_cache_[a].extent_;
395     }
396   else
397     {
398       /*
399         Order is significant: ?-extent may trigger suicide.
400        */
401       SCM ext_sym =
402         (a == X_AXIS)
403         ? ly_symbol2scm ("X-extent")
404         : ly_symbol2scm ("Y-extent");
405         
406       SCM ext = internal_get_property (ext_sym);
407       if (is_number_pair (ext))
408         real_ext.unite (ly_scm2interval (ext));
409
410       SCM min_ext_sym =
411         (a == X_AXIS)
412         ? ly_symbol2scm ("minimum-X-extent")
413         : ly_symbol2scm ("minimum-Y-extent");
414       SCM min_ext = internal_get_property (min_ext_sym);
415       if (is_number_pair (min_ext))
416         real_ext.unite (ly_scm2interval (min_ext));
417
418       ((Grob*)this)->dim_cache_[a].extent_ = new Interval (real_ext);  
419     }
420   
421   real_ext.translate (offset);
422   
423   return real_ext;
424 }
425
426 Interval
427 Grob::pure_height (Grob *refp, int start, int end)
428 {
429   SCM proc = get_property_data (ly_symbol2scm ("Y-extent"));
430   SCM iv_scm = call_pure_function (proc,
431                                    scm_list_1 (self_scm ()),
432                                    start, end);
433   Interval iv = robust_scm2interval (iv_scm, Interval (0, 0));
434   Real offset = pure_relative_y_coordinate (refp, start, end);
435
436   SCM min_ext = get_property ("minimum-Y-extent");
437
438   /* we don't add minimum-Y-extent if the extent is empty. This solves
439      a problem with Hara-kiri spanners. They would request_suicide and
440      return empty extents, but we would force them here to be large. */
441   if (!iv.is_empty () && is_number_pair (min_ext))
442     iv.unite (ly_scm2interval (min_ext));
443
444   if (!iv.is_empty ())
445     iv.translate (offset);
446   return iv;
447 }
448
449 Interval
450 Grob::maybe_pure_extent (Grob *refp, Axis a, bool pure, int start, int end)
451 {
452   if (pure && a != Y_AXIS)
453     programming_error ("tried to get pure width");
454   return (pure && a == Y_AXIS) ? pure_height (refp, start, end) : extent (refp, a);
455 }
456
457 Interval_t<int>
458 Grob::spanned_rank_interval () const
459 {
460   return Interval_t<int> (-1, 0);
461 }
462
463 /****************************************************************
464   REFPOINTS
465 ****************************************************************/
466
467 /* Find the group-element which has both #this# and #s#  */
468 Grob *
469 Grob::common_refpoint (Grob const *s, Axis a) const
470 {
471   /* I don't like the quadratic aspect of this code, but I see no
472      other way.  The largest chain of parents might be 10 high or so,
473      so it shouldn't be a real issue.  */
474   for (Grob const *c = this; c; c = c->dim_cache_[a].parent_)
475     for (Grob const *d = s; d; d = d->dim_cache_[a].parent_)
476       if (d == c)
477         return (Grob *) d;
478
479   return 0;
480 }
481
482 void
483 Grob::set_parent (Grob *g, Axis a)
484 {
485   dim_cache_[a].parent_ = g;
486 }
487
488 Grob *
489 Grob::get_parent (Axis a) const
490 {
491   return dim_cache_[a].parent_;
492 }
493
494
495 void
496 Grob::fixup_refpoint ()
497 {
498   for (int a = X_AXIS; a < NO_AXES; a++)
499     {
500       Axis ax = (Axis)a;
501       Grob *parent = get_parent (ax);
502
503       if (!parent)
504         continue;
505
506       if (parent->get_system () != get_system () && get_system ())
507         {
508           Grob *newparent = parent->find_broken_piece (get_system ());
509           set_parent (newparent, ax);
510         }
511
512       if (Item *i = dynamic_cast<Item *> (this))
513         {
514           Item *parenti = dynamic_cast<Item *> (parent);
515
516           if (parenti && i)
517             {
518               Direction my_dir = i->break_status_dir ();
519               if (my_dir != parenti->break_status_dir ())
520                 {
521                   Item *newparent = parenti->find_prebroken_piece (my_dir);
522                   set_parent (newparent, ax);
523                 }
524             }
525         }
526     }
527 }
528
529
530 /****************************************************************
531   MESSAGES
532 ****************************************************************/
533 void
534 Grob::warning (string s) const
535 {
536   SCM cause = self_scm ();
537   while (Grob *g = unsmob_grob (cause))
538     cause = g->get_property ("cause");
539
540   /* ES TODO: cause can't be Music*/
541   if (Music *m = unsmob_music (cause))
542     m->origin ()->warning (s);
543   else if (Stream_event *ev = unsmob_stream_event (cause))
544     ev->origin ()->warning (s);
545   else
546     ::warning (s);
547 }
548
549
550 string
551 Grob::name () const
552 {
553   SCM meta = get_property ("meta");
554   SCM nm = scm_assq (ly_symbol2scm ("name"), meta);
555   nm = (scm_is_pair (nm)) ? scm_cdr (nm) : SCM_EOL;
556   return scm_is_symbol (nm) ? ly_symbol2string (nm) : this->class_name ();
557 }
558
559 void
560 Grob::programming_error (string s) const
561 {
562   SCM cause = self_scm ();
563   while (Grob *g = unsmob_grob (cause))
564     cause = g->get_property ("cause");
565
566   s = _f ("programming error: %s", s);
567
568   /* ES TODO: cause can't be Music*/
569   if (Music *m = unsmob_music (cause))
570     m->origin ()->message (s);
571   else if (Stream_event *ev = unsmob_stream_event (cause))
572     ev->origin ()->warning (s);
573   else
574     ::message (s);
575 }
576
577
578 ADD_INTERFACE (Grob,
579                "A grob represents a piece of music notation.\n"
580                "\n"
581                "All grobs have an X and Y@tie{}position on the page.  These"
582                " X and Y@tie{}positions are stored in a relative format, thus"
583                " they can easily be combined by stacking them, hanging one"
584                " grob to the side of another, or coupling them into grouping"
585                " objects.\n"
586                "\n"
587                "Each grob has a reference point (a.k.a.@: parent): The"
588                " position of a grob is stored relative to that reference"
589                " point.  For example, the X@tie{}reference point of a staccato"
590                " dot usually is the note head that it applies to.  When the"
591                " note head is moved, the staccato dot moves along"
592                " automatically.\n"
593                "\n"
594                "A grob is often associated with a symbol, but some grobs do"
595                " not print any symbols.  They take care of grouping objects."
596                " For example, there is a separate grob that stacks staves"
597                " vertically.  The @ref{NoteCollision} object is also an"
598                " abstract grob: It only moves around chords, but doesn't print"
599                " anything.\n"
600                "\n"
601                "Grobs have properties (Scheme variables) that can be read and"
602                " set.  Two types of them exist: immutable and mutable."
603                "  Immutable variables define the default style and behavior."
604                "  They are shared between many objects.  They can be changed"
605                " using @code{\\override} and @code{\\revert}.  Mutable"
606                " properties are variables that are specific to one grob."
607                "  Typically, lists of other objects, or results from"
608                " computations are stored in mutable properties.  In"
609                " particular, every call to @code{set-grob-property} (or its"
610                " C++ equivalent) sets a mutable property.\n"
611                "\n"
612                "The properties @code{after-line-breaking} and"
613                " @code{before-line-breaking} are dummies that are not"
614                " user-serviceable.",
615
616                /* properties */
617                "X-extent "
618                "X-offset "
619                "Y-extent "
620                "Y-offset "
621                "after-line-breaking "
622                "avoid-slur "
623                "axis-group-parent-X "
624                "axis-group-parent-Y "
625                "before-line-breaking "
626                "cause "
627                "color "
628                "cross-staff "
629                "extra-X-extent "
630                "extra-Y-extent "
631                "extra-offset "
632                "interfaces "
633                "layer "
634                "meta "
635                "minimum-X-extent "
636                "minimum-Y-extent "
637                "outside-staff-horizontal-padding "
638                "outside-staff-padding "
639                "outside-staff-priority "
640                "pure-Y-offset-in-progress "
641                "rotation "
642                "springs-and-rods "
643                "staff-symbol "
644                "stencil "
645                "transparent "
646                );
647
648 /****************************************************************
649   CALLBACKS
650 ****************************************************************/
651
652 static SCM
653 grob_stencil_extent (Grob *me, Axis a)
654 {
655   Stencil *m = me->get_stencil ();
656   Interval e;
657   if (m)
658     e = m->extent (a);
659   return ly_interval2scm (e);
660 }
661
662
663 MAKE_SCHEME_CALLBACK (Grob, stencil_height, 1);
664 SCM
665 Grob::stencil_height (SCM smob)
666 {
667   Grob *me = unsmob_grob (smob);
668   return grob_stencil_extent (me, Y_AXIS);
669 }
670
671 MAKE_SCHEME_CALLBACK (Grob, y_parent_positioning, 1);
672 SCM
673 Grob::y_parent_positioning (SCM smob)
674 {
675   Grob *me = unsmob_grob (smob);
676   Grob *par = me->get_parent (Y_AXIS);
677   if (par)
678     (void) par->get_property ("positioning-done");
679
680   return scm_from_double (0.0);
681 }
682
683
684 MAKE_SCHEME_CALLBACK (Grob, x_parent_positioning, 1);
685 SCM
686 Grob::x_parent_positioning (SCM smob)
687 {
688   Grob *me = unsmob_grob (smob);
689   
690   Grob *par = me->get_parent (X_AXIS);
691   if (par)
692     (void) par->get_property ("positioning-done");
693
694   return scm_from_double (0.0);
695 }
696
697 MAKE_SCHEME_CALLBACK (Grob, stencil_width, 1);
698 SCM
699 Grob::stencil_width (SCM smob)
700 {
701   Grob *me = unsmob_grob (smob);
702   return grob_stencil_extent (me, X_AXIS);
703 }
704
705
706
707 Grob *
708 common_refpoint_of_list (SCM elist, Grob *common, Axis a)
709 {
710   for (; scm_is_pair (elist); elist = scm_cdr (elist))
711     if (Grob *s = unsmob_grob (scm_car (elist)))
712       {
713         if (common)
714           common = common->common_refpoint (s, a);
715         else
716           common = s;
717       }
718
719   return common;
720 }
721
722 Grob *
723 common_refpoint_of_array (vector<Grob*> const &arr, Grob *common, Axis a)
724 {
725   for (vsize i = 0; i < arr.size (); i++)
726     if (common)
727       common = common->common_refpoint (arr[i], a);
728     else
729       common = arr[i];
730
731   return common;
732 }
733
734 Interval
735 robust_relative_extent (Grob *me, Grob *refpoint, Axis a)
736 {
737   Interval ext = me->extent (refpoint, a);
738   if (ext.is_empty ())
739     ext.add_point (me->relative_coordinate (refpoint, a));
740
741   return ext;
742 }
743