]> git.donarmstrong.com Git - lilypond.git/blob - lily/grob.cc
Merge branch 'master' into lilypond/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
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                "forced-spacing "
803                "interfaces "
804                "layer "
805                "meta "
806                "minimum-X-extent "
807                "minimum-Y-extent "
808                "outside-staff-horizontal-padding "
809                "outside-staff-padding "
810                "outside-staff-priority "
811                "pure-Y-offset-in-progress "
812                "rotation "
813                "springs-and-rods "
814                "staff-symbol "
815                "stencil "
816                "transparent "
817                "whiteout "
818               );
819
820 /****************************************************************
821   CALLBACKS
822 ****************************************************************/
823
824 static SCM
825 grob_stencil_extent (Grob *me, Axis a)
826 {
827   Stencil *m = me->get_stencil ();
828   Interval e;
829   if (m)
830     e = m->extent (a);
831   return ly_interval2scm (e);
832 }
833
834 MAKE_SCHEME_CALLBACK (Grob, stencil_height, 1);
835 SCM
836 Grob::stencil_height (SCM smob)
837 {
838   Grob *me = unsmob_grob (smob);
839   return grob_stencil_extent (me, Y_AXIS);
840 }
841
842 MAKE_SCHEME_CALLBACK (Grob, y_parent_positioning, 1);
843 SCM
844 Grob::y_parent_positioning (SCM smob)
845 {
846   Grob *me = unsmob_grob (smob);
847   Grob *par = me->get_parent (Y_AXIS);
848   if (par)
849     (void) par->get_property ("positioning-done");
850
851   return scm_from_double (0.0);
852 }
853
854 MAKE_SCHEME_CALLBACK (Grob, x_parent_positioning, 1);
855 SCM
856 Grob::x_parent_positioning (SCM smob)
857 {
858   Grob *me = unsmob_grob (smob);
859
860   Grob *par = me->get_parent (X_AXIS);
861   if (par)
862     (void) par->get_property ("positioning-done");
863
864   return scm_from_double (0.0);
865 }
866
867 MAKE_SCHEME_CALLBACK (Grob, stencil_width, 1);
868 SCM
869 Grob::stencil_width (SCM smob)
870 {
871   Grob *me = unsmob_grob (smob);
872   return grob_stencil_extent (me, X_AXIS);
873 }
874
875 Grob *
876 common_refpoint_of_list (SCM elist, Grob *common, Axis a)
877 {
878   for (; scm_is_pair (elist); elist = scm_cdr (elist))
879     if (Grob *s = unsmob_grob (scm_car (elist)))
880       {
881         if (common)
882           common = common->common_refpoint (s, a);
883         else
884           common = s;
885       }
886
887   return common;
888 }
889
890 Grob *
891 common_refpoint_of_array (vector<Grob *> const &arr, Grob *common, Axis a)
892 {
893   for (vsize i = 0; i < arr.size (); i++)
894     if (common)
895       common = common->common_refpoint (arr[i], a);
896     else
897       common = arr[i];
898
899   return common;
900 }
901
902 Interval
903 robust_relative_extent (Grob *me, Grob *refpoint, Axis a)
904 {
905   Interval ext = me->extent (refpoint, a);
906   if (ext.is_empty ())
907     ext.add_point (me->relative_coordinate (refpoint, a));
908
909   return ext;
910 }
911
912 // Checks whether there is a vertical alignment in the chain of
913 // parents between this and commony.
914 bool
915 Grob::check_cross_staff (Grob *commony)
916 {
917   if (Align_interface::has_interface (commony))
918     return true;
919
920   for (Grob *g = this; g && g != commony; g = g->get_parent (Y_AXIS))
921     if (Align_interface::has_interface (g))
922       return true;
923
924   return false;
925 }
926