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