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