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