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