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