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