]> git.donarmstrong.com Git - lilypond.git/blob - lily/grob.cc
Improvements in vertical skyline approximations (issue 2148).
[lilypond.git] / lily / grob.cc
1 /*
2   This file is part of LilyPond, the GNU music typesetter.
3
4   Copyright (C) 1997--2012 Han-Wen Nienhuys <hanwen@xs4all.nl>
5
6   LilyPond is free software: you can redistribute it and/or modify
7   it under the terms of the GNU General Public License as published by
8   the Free Software Foundation, either version 3 of the License, or
9   (at your option) any later version.
10
11   LilyPond is distributed in the hope that it will be useful,
12   but WITHOUT ANY WARRANTY; without even the implied warranty of
13   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14   GNU General Public License for more details.
15
16   You should have received a copy of the GNU General Public License
17   along with LilyPond.  If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include "grob.hh"
21
22 #include <cstring>
23
24 #include "align-interface.hh"
25 #include "axis-group-interface.hh"
26 #include "input.hh"
27 #include "international.hh"
28 #include "item.hh"
29 #include "main.hh"
30 #include "misc.hh"
31 #include "music.hh"
32 #include "output-def.hh"
33 #include "pointer-group-interface.hh"
34 #include "program-option.hh"
35 #include "stencil.hh"
36 #include "stream-event.hh"
37 #include "system.hh"
38 #include "warn.hh"
39
40 #include "ly-smobs.icc"
41
42 Grob *
43 Grob::clone () const
44 {
45   return new Grob (*this);
46 }
47
48 Grob::Grob (SCM basicprops)
49 {
50
51   /* FIXME: default should be no callback.  */
52   self_scm_ = SCM_EOL;
53   layout_ = 0;
54   original_ = 0;
55   interfaces_ = SCM_EOL;
56   immutable_property_alist_ = basicprops;
57   mutable_property_alist_ = SCM_EOL;
58   object_alist_ = SCM_EOL;
59
60   /* We do smobify_self () as the first step.  Since the object lives
61      on the heap, none of its SCM variables are protected from
62      GC. After smobify_self (), they are.  */
63   smobify_self ();
64
65   SCM meta = get_property ("meta");
66   if (scm_is_pair (meta))
67     {
68       interfaces_ = scm_cdr (scm_assq (ly_symbol2scm ("interfaces"), meta));
69
70       SCM object_cbs = scm_assq (ly_symbol2scm ("object-callbacks"), meta);
71       if (scm_is_pair (object_cbs))
72         {
73           for (SCM s = scm_cdr (object_cbs); scm_is_pair (s); s = scm_cdr (s))
74             set_object (scm_caar (s), scm_cdar (s));
75         }
76     }
77
78   if (get_property_data ("X-extent") == SCM_EOL)
79     set_property ("X-extent", Grob::stencil_width_proc);
80   if (get_property_data ("Y-extent") == SCM_EOL)
81     set_property ("Y-extent", Grob::stencil_height_proc);
82   if (get_property_data ("vertical-skylines") == SCM_EOL)
83     set_property ("vertical-skylines", Grob::simple_vertical_skylines_from_stencil_proc);
84   if (get_property_data ("horizontal-skylines") == SCM_EOL)
85     set_property ("horizontal-skylines", Grob::simple_horizontal_skylines_from_stencil_proc);
86 }
87
88 Grob::Grob (Grob const &s)
89   : dim_cache_ (s.dim_cache_)
90 {
91   original_ = (Grob *) & s;
92   self_scm_ = SCM_EOL;
93
94   immutable_property_alist_ = s.immutable_property_alist_;
95   mutable_property_alist_ = SCM_EOL;
96   interfaces_ = s.interfaces_;
97   object_alist_ = SCM_EOL;
98
99   layout_ = 0;
100
101   smobify_self ();
102
103   mutable_property_alist_ = ly_deep_copy (s.mutable_property_alist_);
104
105 }
106
107 Grob::~Grob ()
108 {
109 }
110 /****************************************************************
111   STENCILS
112 ****************************************************************/
113
114 Stencil *
115 Grob::get_stencil () const
116 {
117   if (!is_live ())
118     return 0;
119
120   SCM stil = get_property ("stencil");
121   return unsmob_stencil (stil);
122 }
123
124 Stencil
125 Grob::get_print_stencil () const
126 {
127   SCM stil = get_property ("stencil");
128
129   Stencil retval;
130   if (Stencil *m = unsmob_stencil (stil))
131     {
132       retval = *m;
133       bool transparent = to_boolean (get_property ("transparent"));
134
135       if (transparent)
136         retval = Stencil (m->extent_box (), SCM_EOL);
137       else
138         {
139           SCM expr = m->expr ();
140           expr = scm_list_3 (ly_symbol2scm ("grob-cause"),
141                              self_scm (), expr);
142
143           retval = Stencil (m->extent_box (), expr);
144         }
145
146       SCM rot = get_property ("rotation");
147       if (scm_is_pair (rot))
148         {
149           Real angle = scm_to_double (scm_car (rot));
150           Real x = scm_to_double (scm_cadr (rot));
151           Real y = scm_to_double (scm_caddr (rot));
152
153           retval.rotate_degrees (angle, Offset (x, y));
154         }
155
156       /* color support... see interpret_stencil_expression () for more... */
157       SCM color = get_property ("color");
158       if (scm_is_pair (color))
159         {
160           SCM expr = scm_list_3 (ly_symbol2scm ("color"),
161                                  color,
162                                  retval.expr ());
163
164           retval = Stencil (retval.extent_box (), expr);
165         }
166
167       /* process whiteout */
168       /* a grob has to be visible, otherwise the whiteout property has no effect */
169       if (!transparent && to_boolean (get_property ("whiteout")))
170         {
171           /* Call the scheme procedure stencil-whiteout in scm/stencils.scm */
172           /* to add a round-filled-box stencil to the stencil list */
173           retval
174             = *unsmob_stencil (scm_call_1 (ly_lily_module_constant ("stencil-whiteout"),
175                                            retval.smobbed_copy ()));
176         }
177
178       SCM id = get_property ("id");
179       if (scm_is_string (id))
180         {
181           SCM expr = scm_list_3 (ly_symbol2scm ("id"),
182                                  id,
183                                  retval.expr ());
184
185           retval = Stencil (retval.extent_box (), expr);
186         }
187
188     }
189
190   return retval;
191 }
192
193 /****************************************************************
194   VIRTUAL STUBS
195 ****************************************************************/
196 void
197 Grob::do_break_processing ()
198 {
199 }
200
201 void
202 Grob::discretionary_processing ()
203 {
204 }
205
206 System *
207 Grob::get_system () const
208 {
209   return 0;
210 }
211
212 /* This version of get_system is more reliable than this->get_system ()
213    before line-breaking has been done, at which point there is only
214    one system in the whole score and we can find it just by following
215    parent pointers. */
216 System *
217 Grob::get_system (Grob *me)
218 {
219   Grob *p = me->get_parent (X_AXIS);
220   return p ? get_system (p) : dynamic_cast<System *>(me);
221 }
222
223 void
224 Grob::handle_broken_dependencies ()
225 {
226   Spanner *sp = dynamic_cast<Spanner *> (this);
227   if (original () && sp)
228     return;
229
230   if (sp)
231     /* THIS, SP is the original spanner.  We use a special function
232        because some Spanners have enormously long lists in their
233        properties, and a special function fixes FOO  */
234     {
235       for (SCM s = object_alist_; scm_is_pair (s); s = scm_cdr (s))
236         sp->substitute_one_mutable_property (scm_caar (s), scm_cdar (s));
237     }
238   System *system = get_system ();
239
240   if (is_live ()
241       && system
242       && common_refpoint (system, X_AXIS)
243       && common_refpoint (system, Y_AXIS))
244     substitute_object_links (system->self_scm (), object_alist_);
245   else if (dynamic_cast<System *> (this))
246     substitute_object_links (SCM_UNDEFINED, object_alist_);
247   else
248     /* THIS element is `invalid'; it has been removed from all
249        dependencies, so let's junk the element itself.
250
251        Do not do this for System, since that would remove references
252        to the originals of score-grobs, which get then GC'd (a bad
253        thing).  */
254     suicide ();
255 }
256
257 /* Note that we still want references to this element to be
258    rearranged, and not silently thrown away, so we keep pointers like
259    {broken_into_{drul, array}, original}
260 */
261 void
262 Grob::suicide ()
263 {
264   if (!is_live ())
265     return;
266
267   for (int a = X_AXIS; a < NO_AXES; a++)
268     dim_cache_[a].clear ();
269
270   mutable_property_alist_ = SCM_EOL;
271   object_alist_ = SCM_EOL;
272   immutable_property_alist_ = SCM_EOL;
273   interfaces_ = SCM_EOL;
274 }
275
276 void
277 Grob::handle_prebroken_dependencies ()
278 {
279   /* Don't do this in the derived method, since we want to keep access to
280      object_alist_ centralized.  */
281   if (original ())
282     {
283       Item *it = dynamic_cast<Item *> (this);
284       substitute_object_links (scm_from_int (it->break_status_dir ()),
285                                original ()->object_alist_);
286     }
287 }
288
289 Grob *
290 Grob::find_broken_piece (System *) const
291 {
292   return 0;
293 }
294
295 /****************************************************************
296    OFFSETS
297 ****************************************************************/
298
299 void
300 Grob::translate_axis (Real y, Axis a)
301 {
302   if (isinf (y) || isnan (y))
303     {
304       programming_error ("Infinity or NaN encountered");
305       return;
306     }
307
308   if (!dim_cache_[a].offset_)
309     dim_cache_[a].offset_ = new Real (y);
310   else
311     *dim_cache_[a].offset_ += y;
312 }
313
314 /* Find the offset relative to D.  If D equals THIS, then it is 0.
315    Otherwise, it recursively defd as
316
317    OFFSET_ + PARENT_L_->relative_coordinate (D) */
318 Real
319 Grob::relative_coordinate (Grob const *refp, Axis a) const
320 {
321   /* eaa - hmmm, should we do a programming_error() here? */
322   if ((this == NULL) || (refp == this))
323     return 0.0;
324
325   /* We catch PARENT_L_ == nil case with this, but we crash if we did
326      not ask for the absolute coordinate (ie. REFP == nil.)  */
327   Real off = get_offset (a);
328   if (refp == dim_cache_[a].parent_)
329     return off;
330
331   off += dim_cache_[a].parent_->relative_coordinate (refp, a);
332
333   return off;
334 }
335
336 Real
337 Grob::pure_relative_y_coordinate (Grob const *refp, int start, int end)
338 {
339   if (refp == this)
340     return 0.0;
341
342   Real off = 0;
343
344   if (dim_cache_[Y_AXIS].offset_)
345     {
346       if (to_boolean (get_property ("pure-Y-offset-in-progress")))
347         programming_error ("cyclic chain in pure-Y-offset callbacks");
348
349       off = *dim_cache_[Y_AXIS].offset_;
350     }
351   else
352     {
353       SCM proc = get_property_data ("Y-offset");
354
355       dim_cache_[Y_AXIS].offset_ = new Real (0.0);
356       set_property ("pure-Y-offset-in-progress", SCM_BOOL_T);
357       off = robust_scm2double (call_pure_function (proc,
358                                                    scm_list_1 (self_scm ()),
359                                                    start, end),
360                                0.0);
361       del_property ("pure-Y-offset-in-progress");
362       delete dim_cache_[Y_AXIS].offset_;
363       dim_cache_[Y_AXIS].offset_ = 0;
364     }
365
366   /* we simulate positioning-done if we are the child of a VerticalAlignment,
367      but only if we don't have a cached offset. If we do have a cached offset,
368      it probably means that the Alignment was fixed and it has already been
369      calculated.
370   */
371   if (Grob *p = get_parent (Y_AXIS))
372     {
373       Real trans = 0;
374       if (Align_interface::has_interface (p) && !dim_cache_[Y_AXIS].offset_)
375         trans = Align_interface::get_pure_child_y_translation (p, this, start, end);
376
377       return off + trans + p->pure_relative_y_coordinate (refp, start, end);
378     }
379   return off;
380 }
381
382 /* Invoke callbacks to get offset relative to parent.  */
383 Real
384 Grob::get_offset (Axis a) const
385 {
386   if (dim_cache_[a].offset_)
387     return *dim_cache_[a].offset_;
388
389   Grob *me = (Grob *) this;
390
391   SCM sym = axis_offset_symbol (a);
392   me->dim_cache_[a].offset_ = new Real (0.0);
393
394   /*
395     UGH: can't fold next 2 statements together. Apparently GCC thinks
396     dim_cache_[a].offset_ is unaliased.
397   */
398   Real off = robust_scm2double (internal_get_property (sym), 0.0);
399   if (me->dim_cache_[a].offset_)
400     {
401       *me->dim_cache_[a].offset_ += off;
402       me->del_property (sym);
403       return *me->dim_cache_[a].offset_;
404     }
405   else
406     return 0.0;
407 }
408
409 Real
410 Grob::maybe_pure_coordinate (Grob const *refp, Axis a, bool pure, int start, int end)
411 {
412   if (pure && a != Y_AXIS)
413     programming_error ("tried to get pure X-offset");
414   return (pure && a == Y_AXIS) ? pure_relative_y_coordinate (refp, start, end)
415          : relative_coordinate (refp, a);
416 }
417
418 /****************************************************************
419   extents
420 ****************************************************************/
421
422 void
423 Grob::flush_extent_cache (Axis axis)
424 {
425   if (dim_cache_[axis].extent_)
426     {
427       /*
428         Ugh, this is not accurate; will flush property, causing
429         callback to be called if.
430        */
431       del_property ((axis == X_AXIS) ? ly_symbol2scm ("X-extent") : ly_symbol2scm ("Y-extent"));
432       delete dim_cache_[axis].extent_;
433       dim_cache_[axis].extent_ = 0;
434       if (get_parent (axis))
435         get_parent (axis)->flush_extent_cache (axis);
436     }
437 }
438
439 Interval
440 Grob::extent (Grob *refp, Axis a) const
441 {
442   Real offset = relative_coordinate (refp, a);
443   Interval real_ext;
444   if (dim_cache_[a].extent_)
445     {
446       real_ext = *dim_cache_[a].extent_;
447     }
448   else
449     {
450       /*
451         Order is significant: ?-extent may trigger suicide.
452        */
453       SCM ext_sym
454         = (a == X_AXIS)
455           ? ly_symbol2scm ("X-extent")
456           : ly_symbol2scm ("Y-extent");
457
458       SCM ext = internal_get_property (ext_sym);
459       if (is_number_pair (ext))
460         real_ext.unite (ly_scm2interval (ext));
461
462       SCM min_ext_sym
463         = (a == X_AXIS)
464           ? ly_symbol2scm ("minimum-X-extent")
465           : ly_symbol2scm ("minimum-Y-extent");
466       SCM min_ext = internal_get_property (min_ext_sym);
467       if (is_number_pair (min_ext))
468         real_ext.unite (ly_scm2interval (min_ext));
469
470       ((Grob *)this)->dim_cache_[a].extent_ = new Interval (real_ext);
471     }
472
473   // We never want nan, so we avoid shifting infinite values.
474   for (LEFT_and_RIGHT (d))
475     if (!isinf (real_ext[d]))
476       real_ext[d] += offset;
477
478   return real_ext;
479 }
480
481 Interval
482 Grob::pure_height (Grob *refp, int start, int end)
483 {
484   SCM iv_scm = get_pure_property ("Y-extent", start, end);
485   Interval iv = robust_scm2interval (iv_scm, Interval (0, 0));
486   Real offset = pure_relative_y_coordinate (refp, start, end);
487
488   SCM min_ext = get_property ("minimum-Y-extent");
489
490   /* we don't add minimum-Y-extent if the extent is empty. This solves
491      a problem with Hara-kiri spanners. They would request_suicide and
492      return empty extents, but we would force them here to be large. */
493   if (!iv.is_empty () && is_number_pair (min_ext))
494     iv.unite (ly_scm2interval (min_ext));
495
496   if (!iv.is_empty ())
497     iv.translate (offset);
498   return iv;
499 }
500
501 Interval
502 Grob::maybe_pure_extent (Grob *refp, Axis a, bool pure, int start, int end)
503 {
504   return (pure && a == Y_AXIS) ? pure_height (refp, start, end) : extent (refp, a);
505 }
506
507 Interval_t<int>
508 Grob::spanned_rank_interval () const
509 {
510   return Interval_t<int> (-1, 0);
511 }
512
513 bool
514 Grob::pure_is_visible (int /* start */, int /* end */) const
515 {
516   return true;
517 }
518
519 /* Sort grobs according to their starting column. */
520 bool
521 Grob::less (Grob *g1, Grob *g2)
522 {
523   return g1->spanned_rank_interval ()[LEFT] < g2->spanned_rank_interval ()[LEFT];
524 }
525
526 /****************************************************************
527   REFPOINTS
528 ****************************************************************/
529
530 /* Find the group-element which has both #this# and #s#  */
531 Grob *
532 Grob::common_refpoint (Grob const *s, Axis a) const
533 {
534
535   /* Catching the trivial cases is likely costlier than just running
536      through: one can't avoid going to the respective chain ends
537      anyway.  We might save the second run through when the chain ends
538      differ, but keeping track of the ends makes the loop more costly.
539   */
540
541   int balance = 0;
542   Grob const *c;
543   Grob const *d;
544
545   for (c = this; c; ++balance)
546     c = c->dim_cache_[a].parent_;
547
548   for (d = s; d; --balance)
549     d = d->dim_cache_[a].parent_;
550
551   /* Cut down ancestry to same size */
552
553   for (c = this; balance > 0; --balance)
554     c = c->dim_cache_[a].parent_;
555
556   for (d = s; balance < 0; ++balance)
557     d = d->dim_cache_[a].parent_;
558
559   /* Now find point where our lineages converge */
560   while (c != d)
561     {
562       c = c->dim_cache_[a].parent_;
563       d = d->dim_cache_[a].parent_;
564     }
565
566   return (Grob *) c;
567 }
568
569 void
570 Grob::set_parent (Grob *g, Axis a)
571 {
572   dim_cache_[a].parent_ = g;
573 }
574
575 Grob *
576 Grob::get_parent (Axis a) const
577 {
578   return dim_cache_[a].parent_;
579 }
580
581 void
582 Grob::fixup_refpoint ()
583 {
584   for (int a = X_AXIS; a < NO_AXES; a++)
585     {
586       Axis ax = (Axis)a;
587       Grob *parent = get_parent (ax);
588
589       if (!parent)
590         continue;
591
592       if (parent->get_system () != get_system () && get_system ())
593         {
594           Grob *newparent = parent->find_broken_piece (get_system ());
595           set_parent (newparent, ax);
596         }
597
598       if (Item *i = dynamic_cast<Item *> (this))
599         {
600           Item *parenti = dynamic_cast<Item *> (parent);
601
602           if (parenti && i)
603             {
604               Direction my_dir = i->break_status_dir ();
605               if (my_dir != parenti->break_status_dir ())
606                 {
607                   Item *newparent = parenti->find_prebroken_piece (my_dir);
608                   set_parent (newparent, ax);
609                 }
610             }
611         }
612     }
613 }
614
615 /****************************************************************
616   VERTICAL ORDERING
617 ****************************************************************/
618
619 Grob *
620 get_maybe_root_vertical_alignment (Grob *g, Grob *maybe)
621 {
622   if (!g)
623     return maybe;
624   if (Align_interface::has_interface (g))
625     return get_maybe_root_vertical_alignment (g->get_parent (Y_AXIS), g);
626   return get_maybe_root_vertical_alignment (g->get_parent (Y_AXIS), maybe);
627
628 }
629
630 Grob *
631 Grob::get_root_vertical_alignment (Grob *g)
632 {
633   return get_maybe_root_vertical_alignment (g, 0);
634 }
635
636 Grob *
637 Grob::get_vertical_axis_group (Grob *g)
638 {
639   if (!g)
640     return 0;
641   if (!g->get_parent (Y_AXIS))
642     return 0;
643   if (Axis_group_interface::has_interface (g)
644       && Align_interface::has_interface (g->get_parent (Y_AXIS)))
645     return g;
646   return get_vertical_axis_group (g->get_parent (Y_AXIS));
647
648 }
649
650 int
651 Grob::get_vertical_axis_group_index (Grob *g)
652 {
653   Grob *val = get_root_vertical_alignment (g);
654   if (!val)
655     return -1;
656   Grob *vax = get_vertical_axis_group (g);
657   extract_grob_set (val, "elements", elts);
658   for (vsize i = 0; i < elts.size (); i++)
659     if (elts[i] == vax)
660       return (int) i;
661   g->programming_error ("could not find this grob's vertical axis group in the vertical alignment");
662   return -1;
663 }
664
665 bool
666 Grob::vertical_less (Grob *g1, Grob *g2)
667 {
668   return internal_vertical_less (g1, g2, false);
669 }
670
671 bool
672 Grob::pure_vertical_less (Grob *g1, Grob *g2)
673 {
674   return internal_vertical_less (g1, g2, true);
675 }
676
677 bool
678 Grob::internal_vertical_less (Grob *g1, Grob *g2, bool pure)
679 {
680   Grob *vag = get_root_vertical_alignment (g1);
681   if (!vag)
682     {
683       g1->programming_error ("grob does not belong to a VerticalAlignment?");
684       return false;
685     }
686
687   Grob *ag1 = get_vertical_axis_group (g1);
688   Grob *ag2 = get_vertical_axis_group (g2);
689
690   extract_grob_set (vag, "elements", elts);
691
692   if (ag1 == ag2 && !pure)
693     {
694       Grob *common = g1->common_refpoint (g2, Y_AXIS);
695       return g1->relative_coordinate (common, Y_AXIS) > g2->relative_coordinate (common, Y_AXIS);
696     }
697
698   for (vsize i = 0; i < elts.size (); i++)
699     {
700       if (elts[i] == ag1)
701         return true;
702       if (elts[i] == ag2)
703         return false;
704     }
705
706   g1->programming_error ("could not place this grob in its axis group");
707   return false;
708 }
709
710 /****************************************************************
711   MESSAGES
712 ****************************************************************/
713 void
714 Grob::programming_error (string s) const
715 {
716   SCM cause = self_scm ();
717   while (Grob *g = unsmob_grob (cause))
718     cause = g->get_property ("cause");
719
720   /* ES TODO: cause can't be Music*/
721   if (Music *m = unsmob_music (cause))
722     m->origin ()->programming_error (s);
723   else if (Stream_event *ev = unsmob_stream_event (cause))
724     ev->origin ()->programming_error (s);
725   else
726     ::programming_error (s);
727 }
728
729 void
730 Grob::warning (string s) const
731 {
732   SCM cause = self_scm ();
733   while (Grob *g = unsmob_grob (cause))
734     cause = g->get_property ("cause");
735
736   /* ES TODO: cause can't be Music*/
737   if (Music *m = unsmob_music (cause))
738     m->origin ()->warning (s);
739   else if (Stream_event *ev = unsmob_stream_event (cause))
740     ev->origin ()->warning (s);
741   else
742     ::warning (s);
743 }
744
745 string
746 Grob::name () const
747 {
748   SCM meta = get_property ("meta");
749   SCM nm = scm_assq (ly_symbol2scm ("name"), meta);
750   nm = (scm_is_pair (nm)) ? scm_cdr (nm) : SCM_EOL;
751   return scm_is_symbol (nm) ? ly_symbol2string (nm) : this->class_name ();
752 }
753
754 ADD_INTERFACE (Grob,
755                "A grob represents a piece of music notation.\n"
756                "\n"
757                "All grobs have an X and Y@tie{}position on the page.  These"
758                " X and Y@tie{}positions are stored in a relative format, thus"
759                " they can easily be combined by stacking them, hanging one"
760                " grob to the side of another, or coupling them into grouping"
761                " objects.\n"
762                "\n"
763                "Each grob has a reference point (a.k.a.@: parent): The"
764                " position of a grob is stored relative to that reference"
765                " point.  For example, the X@tie{}reference point of a staccato"
766                " dot usually is the note head that it applies to.  When the"
767                " note head is moved, the staccato dot moves along"
768                " automatically.\n"
769                "\n"
770                "A grob is often associated with a symbol, but some grobs do"
771                " not print any symbols.  They take care of grouping objects."
772                " For example, there is a separate grob that stacks staves"
773                " vertically.  The @ref{NoteCollision} object is also an"
774                " abstract grob: It only moves around chords, but doesn't print"
775                " anything.\n"
776                "\n"
777                "Grobs have properties (Scheme variables) that can be read and"
778                " set.  Two types of them exist: immutable and mutable."
779                "  Immutable variables define the default style and behavior."
780                "  They are shared between many objects.  They can be changed"
781                " using @code{\\override} and @code{\\revert}.  Mutable"
782                " properties are variables that are specific to one grob."
783                "  Typically, lists of other objects, or results from"
784                " computations are stored in mutable properties.  In"
785                " particular, every call to @code{ly:grob-set-property!}"
786                " (or its C++ equivalent) sets a mutable property.\n"
787                "\n"
788                "The properties @code{after-line-breaking} and"
789                " @code{before-line-breaking} are dummies that are not"
790                " user-serviceable.",
791
792                /* properties */
793                "X-extent "
794                "X-offset "
795                "Y-extent "
796                "Y-offset "
797                "after-line-breaking "
798                "avoid-slur "
799                "axis-group-parent-X "
800                "axis-group-parent-Y "
801                "before-line-breaking "
802                "cause "
803                "color "
804                "cross-staff "
805                "id "
806                "extra-X-extent "
807                "extra-Y-extent "
808                "extra-offset "
809                "footnote-music "
810                "forced-spacing "
811                "horizontal-skylines "
812                "interfaces "
813                "layer "
814                "meta "
815                "minimum-X-extent "
816                "minimum-Y-extent "
817                "outside-staff-horizontal-padding "
818                "outside-staff-padding "
819                "outside-staff-priority "
820                "pure-Y-offset-in-progress "
821                "rotation "
822                "skyline-horizontal-padding "
823                "springs-and-rods "
824                "staff-symbol "
825                "stencil "
826                "transparent "
827                "vertical-skylines "
828                "whiteout "
829               );
830
831 /****************************************************************
832   CALLBACKS
833 ****************************************************************/
834
835 static SCM
836 grob_stencil_extent (Grob *me, Axis a)
837 {
838   Stencil *m = me->get_stencil ();
839   Interval e;
840   if (m)
841     e = m->extent (a);
842   return ly_interval2scm (e);
843 }
844
845 MAKE_SCHEME_CALLBACK (Grob, stencil_height, 1);
846 SCM
847 Grob::stencil_height (SCM smob)
848 {
849   Grob *me = unsmob_grob (smob);
850   return grob_stencil_extent (me, Y_AXIS);
851 }
852
853 MAKE_SCHEME_CALLBACK (Grob, y_parent_positioning, 1);
854 SCM
855 Grob::y_parent_positioning (SCM smob)
856 {
857   Grob *me = unsmob_grob (smob);
858   Grob *par = me->get_parent (Y_AXIS);
859   if (par)
860     (void) par->get_property ("positioning-done");
861
862   return scm_from_double (0.0);
863 }
864
865 MAKE_SCHEME_CALLBACK (Grob, x_parent_positioning, 1);
866 SCM
867 Grob::x_parent_positioning (SCM smob)
868 {
869   Grob *me = unsmob_grob (smob);
870
871   Grob *par = me->get_parent (X_AXIS);
872   if (par)
873     (void) par->get_property ("positioning-done");
874
875   return scm_from_double (0.0);
876 }
877
878 MAKE_SCHEME_CALLBACK (Grob, stencil_width, 1);
879 SCM
880 Grob::stencil_width (SCM smob)
881 {
882   Grob *me = unsmob_grob (smob);
883   return grob_stencil_extent (me, X_AXIS);
884 }
885
886 Grob *
887 common_refpoint_of_list (SCM elist, Grob *common, Axis a)
888 {
889   for (; scm_is_pair (elist); elist = scm_cdr (elist))
890     if (Grob *s = unsmob_grob (scm_car (elist)))
891       {
892         if (common)
893           common = common->common_refpoint (s, a);
894         else
895           common = s;
896       }
897
898   return common;
899 }
900
901 Grob *
902 common_refpoint_of_array (vector<Grob *> const &arr, Grob *common, Axis a)
903 {
904   for (vsize i = 0; i < arr.size (); i++)
905     if (common)
906       common = common->common_refpoint (arr[i], a);
907     else
908       common = arr[i];
909
910   return common;
911 }
912
913 Interval
914 robust_relative_extent (Grob *me, Grob *refpoint, Axis a)
915 {
916   Interval ext = me->extent (refpoint, a);
917   if (ext.is_empty ())
918     ext.add_point (me->relative_coordinate (refpoint, a));
919
920   return ext;
921 }
922
923 // Checks whether there is a vertical alignment in the chain of
924 // parents between this and commony.
925 bool
926 Grob::check_cross_staff (Grob *commony)
927 {
928   if (Align_interface::has_interface (commony))
929     return true;
930
931   for (Grob *g = this; g && g != commony; g = g->get_parent (Y_AXIS))
932     if (Align_interface::has_interface (g))
933       return true;
934
935   return false;
936 }
937