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