]> git.donarmstrong.com Git - lilypond.git/blob - lily/grob.cc
Merge remote-tracking branch 'origin/translation' into master
[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     if(!isinf (offset))
476       real_ext.translate(offset);
477     else
478       this->warning(_f ("ignored infinite %s-offset",
479                         a == X_AXIS ? "X" : "Y"));
480
481   return real_ext;
482 }
483
484 Interval
485 Grob::pure_height (Grob *refp, int start, int end)
486 {
487   SCM iv_scm = get_pure_property ("Y-extent", start, end);
488   Interval iv = robust_scm2interval (iv_scm, Interval (0, 0));
489   Real offset = pure_relative_y_coordinate (refp, start, end);
490
491   SCM min_ext = get_property ("minimum-Y-extent");
492
493   /* we don't add minimum-Y-extent if the extent is empty. This solves
494      a problem with Hara-kiri spanners. They would request_suicide and
495      return empty extents, but we would force them here to be large. */
496   if (!iv.is_empty () && is_number_pair (min_ext))
497     iv.unite (ly_scm2interval (min_ext));
498
499   if (!iv.is_empty ())
500     iv.translate (offset);
501   return iv;
502 }
503
504 Interval
505 Grob::maybe_pure_extent (Grob *refp, Axis a, bool pure, int start, int end)
506 {
507   return (pure && a == Y_AXIS) ? pure_height (refp, start, end) : extent (refp, a);
508 }
509
510 Interval_t<int>
511 Grob::spanned_rank_interval () const
512 {
513   return Interval_t<int> (-1, 0);
514 }
515
516 bool
517 Grob::pure_is_visible (int /* start */, int /* end */) const
518 {
519   return true;
520 }
521
522 /* Sort grobs according to their starting column. */
523 bool
524 Grob::less (Grob *g1, Grob *g2)
525 {
526   return g1->spanned_rank_interval ()[LEFT] < g2->spanned_rank_interval ()[LEFT];
527 }
528
529 /****************************************************************
530   REFPOINTS
531 ****************************************************************/
532
533 /* Find the group-element which has both #this# and #s#  */
534 Grob *
535 Grob::common_refpoint (Grob const *s, Axis a) const
536 {
537
538   /* Catching the trivial cases is likely costlier than just running
539      through: one can't avoid going to the respective chain ends
540      anyway.  We might save the second run through when the chain ends
541      differ, but keeping track of the ends makes the loop more costly.
542   */
543
544   int balance = 0;
545   Grob const *c;
546   Grob const *d;
547
548   for (c = this; c; ++balance)
549     c = c->dim_cache_[a].parent_;
550
551   for (d = s; d; --balance)
552     d = d->dim_cache_[a].parent_;
553
554   /* Cut down ancestry to same size */
555
556   for (c = this; balance > 0; --balance)
557     c = c->dim_cache_[a].parent_;
558
559   for (d = s; balance < 0; ++balance)
560     d = d->dim_cache_[a].parent_;
561
562   /* Now find point where our lineages converge */
563   while (c != d)
564     {
565       c = c->dim_cache_[a].parent_;
566       d = d->dim_cache_[a].parent_;
567     }
568
569   return (Grob *) c;
570 }
571
572 void
573 Grob::set_parent (Grob *g, Axis a)
574 {
575   dim_cache_[a].parent_ = g;
576 }
577
578 Grob *
579 Grob::get_parent (Axis a) const
580 {
581   return dim_cache_[a].parent_;
582 }
583
584 void
585 Grob::fixup_refpoint ()
586 {
587   for (int a = X_AXIS; a < NO_AXES; a++)
588     {
589       Axis ax = (Axis)a;
590       Grob *parent = get_parent (ax);
591
592       if (!parent)
593         continue;
594
595       if (parent->get_system () != get_system () && get_system ())
596         {
597           Grob *newparent = parent->find_broken_piece (get_system ());
598           set_parent (newparent, ax);
599         }
600
601       if (Item *i = dynamic_cast<Item *> (this))
602         {
603           Item *parenti = dynamic_cast<Item *> (parent);
604
605           if (parenti && i)
606             {
607               Direction my_dir = i->break_status_dir ();
608               if (my_dir != parenti->break_status_dir ())
609                 {
610                   Item *newparent = parenti->find_prebroken_piece (my_dir);
611                   set_parent (newparent, ax);
612                 }
613             }
614         }
615     }
616 }
617
618 /****************************************************************
619   VERTICAL ORDERING
620 ****************************************************************/
621
622 Grob *
623 get_maybe_root_vertical_alignment (Grob *g, Grob *maybe)
624 {
625   if (!g)
626     return maybe;
627   if (Align_interface::has_interface (g))
628     return get_maybe_root_vertical_alignment (g->get_parent (Y_AXIS), g);
629   return get_maybe_root_vertical_alignment (g->get_parent (Y_AXIS), maybe);
630
631 }
632
633 Grob *
634 Grob::get_root_vertical_alignment (Grob *g)
635 {
636   return get_maybe_root_vertical_alignment (g, 0);
637 }
638
639 Grob *
640 Grob::get_vertical_axis_group (Grob *g)
641 {
642   if (!g)
643     return 0;
644   if (!g->get_parent (Y_AXIS))
645     return 0;
646   if (Axis_group_interface::has_interface (g)
647       && Align_interface::has_interface (g->get_parent (Y_AXIS)))
648     return g;
649   return get_vertical_axis_group (g->get_parent (Y_AXIS));
650
651 }
652
653 int
654 Grob::get_vertical_axis_group_index (Grob *g)
655 {
656   Grob *val = get_root_vertical_alignment (g);
657   if (!val)
658     return -1;
659   Grob *vax = get_vertical_axis_group (g);
660   extract_grob_set (val, "elements", elts);
661   for (vsize i = 0; i < elts.size (); i++)
662     if (elts[i] == vax)
663       return (int) i;
664   g->programming_error ("could not find this grob's vertical axis group in the vertical alignment");
665   return -1;
666 }
667
668 bool
669 Grob::vertical_less (Grob *g1, Grob *g2)
670 {
671   return internal_vertical_less (g1, g2, false);
672 }
673
674 bool
675 Grob::pure_vertical_less (Grob *g1, Grob *g2)
676 {
677   return internal_vertical_less (g1, g2, true);
678 }
679
680 bool
681 Grob::internal_vertical_less (Grob *g1, Grob *g2, bool pure)
682 {
683   Grob *vag = get_root_vertical_alignment (g1);
684   if (!vag)
685     {
686       g1->programming_error ("grob does not belong to a VerticalAlignment?");
687       return false;
688     }
689
690   Grob *ag1 = get_vertical_axis_group (g1);
691   Grob *ag2 = get_vertical_axis_group (g2);
692
693   extract_grob_set (vag, "elements", elts);
694
695   if (ag1 == ag2 && !pure)
696     {
697       Grob *common = g1->common_refpoint (g2, Y_AXIS);
698       return g1->relative_coordinate (common, Y_AXIS) > g2->relative_coordinate (common, Y_AXIS);
699     }
700
701   for (vsize i = 0; i < elts.size (); i++)
702     {
703       if (elts[i] == ag1)
704         return true;
705       if (elts[i] == ag2)
706         return false;
707     }
708
709   g1->programming_error ("could not place this grob in its axis group");
710   return false;
711 }
712
713 /****************************************************************
714   MESSAGES
715 ****************************************************************/
716 void
717 Grob::programming_error (string s) const
718 {
719   SCM cause = self_scm ();
720   while (Grob *g = unsmob_grob (cause))
721     cause = g->get_property ("cause");
722
723   /* ES TODO: cause can't be Music*/
724   if (Music *m = unsmob_music (cause))
725     m->origin ()->programming_error (s);
726   else if (Stream_event *ev = unsmob_stream_event (cause))
727     ev->origin ()->programming_error (s);
728   else
729     ::programming_error (s);
730 }
731
732 void
733 Grob::warning (string s) const
734 {
735   SCM cause = self_scm ();
736   while (Grob *g = unsmob_grob (cause))
737     cause = g->get_property ("cause");
738
739   /* ES TODO: cause can't be Music*/
740   if (Music *m = unsmob_music (cause))
741     m->origin ()->warning (s);
742   else if (Stream_event *ev = unsmob_stream_event (cause))
743     ev->origin ()->warning (s);
744   else
745     ::warning (s);
746 }
747
748 string
749 Grob::name () const
750 {
751   SCM meta = get_property ("meta");
752   SCM nm = scm_assq (ly_symbol2scm ("name"), meta);
753   nm = (scm_is_pair (nm)) ? scm_cdr (nm) : SCM_EOL;
754   return scm_is_symbol (nm) ? ly_symbol2string (nm) : this->class_name ();
755 }
756
757 ADD_INTERFACE (Grob,
758                "A grob represents a piece of music notation.\n"
759                "\n"
760                "All grobs have an X and Y@tie{}position on the page.  These"
761                " X and Y@tie{}positions are stored in a relative format, thus"
762                " they can easily be combined by stacking them, hanging one"
763                " grob to the side of another, or coupling them into grouping"
764                " objects.\n"
765                "\n"
766                "Each grob has a reference point (a.k.a.@: parent): The"
767                " position of a grob is stored relative to that reference"
768                " point.  For example, the X@tie{}reference point of a staccato"
769                " dot usually is the note head that it applies to.  When the"
770                " note head is moved, the staccato dot moves along"
771                " automatically.\n"
772                "\n"
773                "A grob is often associated with a symbol, but some grobs do"
774                " not print any symbols.  They take care of grouping objects."
775                " For example, there is a separate grob that stacks staves"
776                " vertically.  The @ref{NoteCollision} object is also an"
777                " abstract grob: It only moves around chords, but doesn't print"
778                " anything.\n"
779                "\n"
780                "Grobs have properties (Scheme variables) that can be read and"
781                " set.  Two types of them exist: immutable and mutable."
782                "  Immutable variables define the default style and behavior."
783                "  They are shared between many objects.  They can be changed"
784                " using @code{\\override} and @code{\\revert}.  Mutable"
785                " properties are variables that are specific to one grob."
786                "  Typically, lists of other objects, or results from"
787                " computations are stored in mutable properties.  In"
788                " particular, every call to @code{ly:grob-set-property!}"
789                " (or its C++ equivalent) sets a mutable property.\n"
790                "\n"
791                "The properties @code{after-line-breaking} and"
792                " @code{before-line-breaking} are dummies that are not"
793                " user-serviceable.",
794
795                /* properties */
796                "X-extent "
797                "X-offset "
798                "Y-extent "
799                "Y-offset "
800                "after-line-breaking "
801                "avoid-slur "
802                "axis-group-parent-X "
803                "axis-group-parent-Y "
804                "before-line-breaking "
805                "cause "
806                "color "
807                "cross-staff "
808                "id "
809                "extra-offset "
810                "footnote-music "
811                "forced-spacing "
812                "horizontal-skylines "
813                "interfaces "
814                "layer "
815                "meta "
816                "minimum-X-extent "
817                "minimum-Y-extent "
818                "outside-staff-horizontal-padding "
819                "outside-staff-padding "
820                "outside-staff-priority "
821                "pure-Y-offset-in-progress "
822                "rotation "
823                "skyline-horizontal-padding "
824                "springs-and-rods "
825                "staff-symbol "
826                "stencil "
827                "transparent "
828                "vertical-skylines "
829                "whiteout "
830               );
831
832 /****************************************************************
833   CALLBACKS
834 ****************************************************************/
835
836 static SCM
837 grob_stencil_extent (Grob *me, Axis a)
838 {
839   Stencil *m = me->get_stencil ();
840   Interval e;
841   if (m)
842     e = m->extent (a);
843   return ly_interval2scm (e);
844 }
845
846 MAKE_SCHEME_CALLBACK (Grob, stencil_height, 1);
847 SCM
848 Grob::stencil_height (SCM smob)
849 {
850   Grob *me = unsmob_grob (smob);
851   return grob_stencil_extent (me, Y_AXIS);
852 }
853
854 MAKE_SCHEME_CALLBACK (Grob, y_parent_positioning, 1);
855 SCM
856 Grob::y_parent_positioning (SCM smob)
857 {
858   Grob *me = unsmob_grob (smob);
859   Grob *par = me->get_parent (Y_AXIS);
860   if (par)
861     (void) par->get_property ("positioning-done");
862
863   return scm_from_double (0.0);
864 }
865
866 MAKE_SCHEME_CALLBACK (Grob, x_parent_positioning, 1);
867 SCM
868 Grob::x_parent_positioning (SCM smob)
869 {
870   Grob *me = unsmob_grob (smob);
871
872   Grob *par = me->get_parent (X_AXIS);
873   if (par)
874     (void) par->get_property ("positioning-done");
875
876   return scm_from_double (0.0);
877 }
878
879 MAKE_SCHEME_CALLBACK (Grob, stencil_width, 1);
880 SCM
881 Grob::stencil_width (SCM smob)
882 {
883   Grob *me = unsmob_grob (smob);
884   return grob_stencil_extent (me, X_AXIS);
885 }
886
887 Grob *
888 common_refpoint_of_list (SCM elist, Grob *common, Axis a)
889 {
890   for (; scm_is_pair (elist); elist = scm_cdr (elist))
891     if (Grob *s = unsmob_grob (scm_car (elist)))
892       {
893         if (common)
894           common = common->common_refpoint (s, a);
895         else
896           common = s;
897       }
898
899   return common;
900 }
901
902 Grob *
903 common_refpoint_of_array (vector<Grob *> const &arr, Grob *common, Axis a)
904 {
905   for (vsize i = 0; i < arr.size (); i++)
906     if (common)
907       common = common->common_refpoint (arr[i], a);
908     else
909       common = arr[i];
910
911   return common;
912 }
913
914 Grob *
915 common_refpoint_of_array (set<Grob *> const &arr, Grob *common, Axis a)
916 {
917   set<Grob *>::iterator it;
918
919   for (it = arr.begin (); it != arr.end (); it++)
920     if (common)
921       common = common->common_refpoint (*it, a);
922     else
923       common = *it;
924
925   return common;
926 }
927
928 Interval
929 robust_relative_extent (Grob *me, Grob *refpoint, Axis a)
930 {
931   Interval ext = me->extent (refpoint, a);
932   if (ext.is_empty ())
933     ext.add_point (me->relative_coordinate (refpoint, a));
934
935   return ext;
936 }
937
938 // Checks whether there is a vertical alignment in the chain of
939 // parents between this and commony.
940 bool
941 Grob::check_cross_staff (Grob *commony)
942 {
943   if (Align_interface::has_interface (commony))
944     return true;
945
946   for (Grob *g = this; g && g != commony; g = g->get_parent (Y_AXIS))
947     if (Align_interface::has_interface (g))
948       return true;
949
950   return false;
951 }
952