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