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