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