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