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