]> git.donarmstrong.com Git - lilypond.git/blob - lily/grob.cc
eafa66288efdab93c9ab72bf49a3831c749c24b2
[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   Real off = get_offset (a);
342   if (refp == dim_cache_[a].parent_)
343     return off;
344
345   if (dim_cache_[a].parent_ != NULL)
346     off += dim_cache_[a].parent_->relative_coordinate (refp, a);
347
348   return off;
349 }
350
351 Real
352 Grob::pure_relative_y_coordinate (Grob const *refp, int start, int end)
353 {
354   if (refp == this)
355     return 0.0;
356
357   Real off = 0;
358
359   if (dim_cache_[Y_AXIS].offset_)
360     {
361       if (to_boolean (get_property ("pure-Y-offset-in-progress")))
362         programming_error ("cyclic chain in pure-Y-offset callbacks");
363
364       off = *dim_cache_[Y_AXIS].offset_;
365     }
366   else
367     {
368       SCM proc = get_property_data ("Y-offset");
369
370       dim_cache_[Y_AXIS].offset_ = new Real (0.0);
371       set_property ("pure-Y-offset-in-progress", SCM_BOOL_T);
372       off = robust_scm2double (call_pure_function (proc,
373                                                    scm_list_1 (self_scm ()),
374                                                    start, end),
375                                0.0);
376       del_property ("pure-Y-offset-in-progress");
377       delete dim_cache_[Y_AXIS].offset_;
378       dim_cache_[Y_AXIS].offset_ = 0;
379     }
380
381   /* we simulate positioning-done if we are the child of a VerticalAlignment,
382      but only if we don't have a cached offset. If we do have a cached offset,
383      it probably means that the Alignment was fixed and it has already been
384      calculated.
385   */
386   if (Grob *p = get_parent (Y_AXIS))
387     {
388       Real trans = 0;
389       if (has_interface<Align_interface> (p) && !dim_cache_[Y_AXIS].offset_)
390         trans = Align_interface::get_pure_child_y_translation (p, this, start, end);
391
392       return off + trans + p->pure_relative_y_coordinate (refp, start, end);
393     }
394   return off;
395 }
396
397 /* Invoke callbacks to get offset relative to parent.  */
398 Real
399 Grob::get_offset (Axis a) const
400 {
401   if (dim_cache_[a].offset_)
402     return *dim_cache_[a].offset_;
403
404   Grob *me = (Grob *) this;
405
406   SCM sym = axis_offset_symbol (a);
407   me->dim_cache_[a].offset_ = new Real (0.0);
408
409   /*
410     UGH: can't fold next 2 statements together. Apparently GCC thinks
411     dim_cache_[a].offset_ is unaliased.
412   */
413   Real off = robust_scm2double (get_property (sym), 0.0);
414   if (me->dim_cache_[a].offset_)
415     {
416       *me->dim_cache_[a].offset_ += off;
417       me->del_property (sym);
418       return *me->dim_cache_[a].offset_;
419     }
420   else
421     return 0.0;
422 }
423
424 Real
425 Grob::maybe_pure_coordinate (Grob const *refp, Axis a, bool pure, int start, int end)
426 {
427   if (pure && a != Y_AXIS)
428     programming_error ("tried to get pure X-offset");
429   return (pure && a == Y_AXIS) ? pure_relative_y_coordinate (refp, start, end)
430          : relative_coordinate (refp, a);
431 }
432
433 /****************************************************************
434   extents
435 ****************************************************************/
436
437 void
438 Grob::flush_extent_cache (Axis axis)
439 {
440   if (dim_cache_[axis].extent_)
441     {
442       /*
443         Ugh, this is not accurate; will flush property, causing
444         callback to be called if.
445        */
446       del_property ((axis == X_AXIS) ? ly_symbol2scm ("X-extent") : ly_symbol2scm ("Y-extent"));
447       delete dim_cache_[axis].extent_;
448       dim_cache_[axis].extent_ = 0;
449       if (get_parent (axis))
450         get_parent (axis)->flush_extent_cache (axis);
451     }
452 }
453
454 Interval
455 Grob::extent (Grob *refp, Axis a) const
456 {
457   Real offset = relative_coordinate (refp, a);
458   Interval real_ext;
459   if (dim_cache_[a].extent_)
460     {
461       real_ext = *dim_cache_[a].extent_;
462     }
463   else
464     {
465       /*
466         Order is significant: ?-extent may trigger suicide.
467        */
468       SCM ext = (a == X_AXIS)
469               ? get_property ("X-extent")
470               : get_property ("Y-extent");
471       if (is_number_pair (ext))
472         real_ext.unite (ly_scm2interval (ext));
473
474       SCM min_ext = (a == X_AXIS)
475               ? get_property ("minimum-X-extent")
476               : get_property ("minimum-Y-extent");
477       if (is_number_pair (min_ext))
478         real_ext.unite (ly_scm2interval (min_ext));
479
480       ((Grob *)this)->dim_cache_[a].extent_ = new Interval (real_ext);
481     }
482
483   // We never want nan, so we avoid shifting infinite values.
484     if(!isinf (offset))
485       real_ext.translate(offset);
486     else
487       warning(_f ("ignored infinite %s-offset",
488                         a == X_AXIS ? "X" : "Y"));
489
490   return real_ext;
491 }
492
493 Interval
494 Grob::pure_y_extent (Grob *refp, int start, int end)
495 {
496   SCM iv_scm = get_pure_property ("Y-extent", start, end);
497   Interval iv = robust_scm2interval (iv_scm, Interval ());
498   Real offset = pure_relative_y_coordinate (refp, start, end);
499
500   SCM min_ext = get_property ("minimum-Y-extent");
501
502   /* we don't add minimum-Y-extent if the extent is empty. This solves
503      a problem with Hara-kiri spanners. They would request_suicide and
504      return empty extents, but we would force them here to be large. */
505   if (!iv.is_empty () && is_number_pair (min_ext))
506     iv.unite (ly_scm2interval (min_ext));
507
508   if (!iv.is_empty ())
509     iv.translate (offset);
510   return iv;
511 }
512
513 Interval
514 Grob::maybe_pure_extent (Grob *refp, Axis a, bool pure, int start, int end)
515 {
516   return (pure && a == Y_AXIS) ? pure_y_extent (refp, start, end) : extent (refp, a);
517 }
518
519 Interval_t<int>
520 Grob::spanned_rank_interval () const
521 {
522   return Interval_t<int> (-1, 0);
523 }
524
525 bool
526 Grob::pure_is_visible (int /* start */, int /* end */) const
527 {
528   return true;
529 }
530
531 /* Sort grobs according to their starting column. */
532 bool
533 Grob::less (Grob *g1, Grob *g2)
534 {
535   return g1->spanned_rank_interval ()[LEFT] < g2->spanned_rank_interval ()[LEFT];
536 }
537
538 /****************************************************************
539   REFPOINTS
540 ****************************************************************/
541
542 /* Find the group-element which has both #this# and #s#  */
543 Grob *
544 Grob::common_refpoint (Grob const *s, Axis a) const
545 {
546
547   /* Catching the trivial cases is likely costlier than just running
548      through: one can't avoid going to the respective chain ends
549      anyway.  We might save the second run through when the chain ends
550      differ, but keeping track of the ends makes the loop more costly.
551   */
552
553   int balance = 0;
554   Grob const *c;
555   Grob const *d;
556
557   for (c = this; c; ++balance)
558     c = c->dim_cache_[a].parent_;
559
560   for (d = s; d; --balance)
561     d = d->dim_cache_[a].parent_;
562
563   /* Cut down ancestry to same size */
564
565   for (c = this; balance > 0; --balance)
566     c = c->dim_cache_[a].parent_;
567
568   for (d = s; balance < 0; ++balance)
569     d = d->dim_cache_[a].parent_;
570
571   /* Now find point where our lineages converge */
572   while (c != d)
573     {
574       c = c->dim_cache_[a].parent_;
575       d = d->dim_cache_[a].parent_;
576     }
577
578   return (Grob *) c;
579 }
580
581 void
582 Grob::set_parent (Grob *g, Axis a)
583 {
584   dim_cache_[a].parent_ = g;
585 }
586
587 Grob *
588 Grob::get_parent (Axis a) const
589 {
590   return dim_cache_[a].parent_;
591 }
592
593 void
594 Grob::fixup_refpoint ()
595 {
596   for (int a = X_AXIS; a < NO_AXES; a++)
597     {
598       Axis ax = (Axis)a;
599       Grob *parent = get_parent (ax);
600
601       if (!parent)
602         continue;
603
604       if (parent->get_system () != get_system () && get_system ())
605         {
606           Grob *newparent = parent->find_broken_piece (get_system ());
607           set_parent (newparent, ax);
608         }
609
610       if (Item *i = dynamic_cast<Item *> (this))
611         {
612           Item *parenti = dynamic_cast<Item *> (parent);
613
614           if (parenti && i)
615             {
616               Direction my_dir = i->break_status_dir ();
617               if (my_dir != parenti->break_status_dir ())
618                 {
619                   Item *newparent = parenti->find_prebroken_piece (my_dir);
620                   set_parent (newparent, ax);
621                 }
622             }
623         }
624     }
625 }
626
627 /****************************************************************
628   VERTICAL ORDERING
629 ****************************************************************/
630
631 Grob *
632 get_maybe_root_vertical_alignment (Grob *g, Grob *maybe)
633 {
634   if (!g)
635     return maybe;
636   if (has_interface<Align_interface> (g))
637     return get_maybe_root_vertical_alignment (g->get_parent (Y_AXIS), g);
638   return get_maybe_root_vertical_alignment (g->get_parent (Y_AXIS), maybe);
639
640 }
641
642 Grob *
643 Grob::get_root_vertical_alignment (Grob *g)
644 {
645   return get_maybe_root_vertical_alignment (g, 0);
646 }
647
648 Grob *
649 Grob::get_vertical_axis_group (Grob *g)
650 {
651   if (!g)
652     return 0;
653   if (!g->get_parent (Y_AXIS))
654     return 0;
655   if (has_interface<Axis_group_interface> (g)
656       && has_interface<Align_interface> (g->get_parent (Y_AXIS)))
657     return g;
658   return get_vertical_axis_group (g->get_parent (Y_AXIS));
659
660 }
661
662 int
663 Grob::get_vertical_axis_group_index (Grob *g)
664 {
665   Grob *val = get_root_vertical_alignment (g);
666   if (!val)
667     return -1;
668   Grob *vax = get_vertical_axis_group (g);
669   extract_grob_set (val, "elements", elts);
670   for (vsize i = 0; i < elts.size (); i++)
671     if (elts[i] == vax)
672       return (int) i;
673   g->programming_error ("could not find this grob's vertical axis group in the vertical alignment");
674   return -1;
675 }
676
677 bool
678 Grob::vertical_less (Grob *g1, Grob *g2)
679 {
680   return internal_vertical_less (g1, g2, false);
681 }
682
683 bool
684 Grob::pure_vertical_less (Grob *g1, Grob *g2)
685 {
686   return internal_vertical_less (g1, g2, true);
687 }
688
689 bool
690 Grob::internal_vertical_less (Grob *g1, Grob *g2, bool pure)
691 {
692   Grob *vag = get_root_vertical_alignment (g1);
693   if (!vag)
694     {
695       g1->programming_error ("grob does not belong to a VerticalAlignment?");
696       return false;
697     }
698
699   Grob *ag1 = get_vertical_axis_group (g1);
700   Grob *ag2 = get_vertical_axis_group (g2);
701
702   extract_grob_set (vag, "elements", elts);
703
704   if (ag1 == ag2 && !pure)
705     {
706       Grob *common = g1->common_refpoint (g2, Y_AXIS);
707       return g1->relative_coordinate (common, Y_AXIS) > g2->relative_coordinate (common, Y_AXIS);
708     }
709
710   for (vsize i = 0; i < elts.size (); i++)
711     {
712       if (elts[i] == ag1)
713         return true;
714       if (elts[i] == ag2)
715         return false;
716     }
717
718   g1->programming_error ("could not place this grob in its axis group");
719   return false;
720 }
721
722 /****************************************************************
723   MESSAGES
724 ****************************************************************/
725 void
726 Grob::programming_error (const string &s) const
727 {
728   SCM cause = self_scm ();
729   while (Grob *g = unsmob<Grob> (cause))
730     cause = g->get_property ("cause");
731
732   /* ES TODO: cause can't be Music*/
733   if (Music *m = unsmob<Music> (cause))
734     m->origin ()->programming_error (s);
735   else if (Stream_event *ev = unsmob<Stream_event> (cause))
736     ev->origin ()->programming_error (s);
737   else
738     ::programming_error (s);
739 }
740
741 void
742 Grob::warning (const string &s) const
743 {
744   SCM cause = self_scm ();
745   while (Grob *g = unsmob<Grob> (cause))
746     cause = g->get_property ("cause");
747
748   /* ES TODO: cause can't be Music*/
749   if (Music *m = unsmob<Music> (cause))
750     m->origin ()->warning (s);
751   else if (Stream_event *ev = unsmob<Stream_event> (cause))
752     ev->origin ()->warning (s);
753   else
754     ::warning (s);
755 }
756
757 string
758 Grob::name () const
759 {
760   SCM meta = get_property ("meta");
761   SCM nm = scm_assq (ly_symbol2scm ("name"), meta);
762   nm = (scm_is_pair (nm)) ? scm_cdr (nm) : SCM_EOL;
763   return scm_is_symbol (nm) ? ly_symbol2string (nm) : class_name ();
764 }
765
766 ADD_INTERFACE (Grob,
767                "A grob represents a piece of music notation.\n"
768                "\n"
769                "All grobs have an X and Y@tie{}position on the page.  These"
770                " X and Y@tie{}positions are stored in a relative format, thus"
771                " they can easily be combined by stacking them, hanging one"
772                " grob to the side of another, or coupling them into grouping"
773                " objects.\n"
774                "\n"
775                "Each grob has a reference point (a.k.a.@: parent): The"
776                " position of a grob is stored relative to that reference"
777                " point.  For example, the X@tie{}reference point of a staccato"
778                " dot usually is the note head that it applies to.  When the"
779                " note head is moved, the staccato dot moves along"
780                " automatically.\n"
781                "\n"
782                "A grob is often associated with a symbol, but some grobs do"
783                " not print any symbols.  They take care of grouping objects."
784                " For example, there is a separate grob that stacks staves"
785                " vertically.  The @ref{NoteCollision} object is also an"
786                " abstract grob: It only moves around chords, but doesn't print"
787                " anything.\n"
788                "\n"
789                "Grobs have properties (Scheme variables) that can be read and"
790                " set.  Two types of them exist: immutable and mutable."
791                "  Immutable variables define the default style and behavior."
792                "  They are shared between many objects.  They can be changed"
793                " using @code{\\override} and @code{\\revert}.  Mutable"
794                " properties are variables that are specific to one grob."
795                "  Typically, lists of other objects, or results from"
796                " computations are stored in mutable properties.  In"
797                " particular, every call to @code{ly:grob-set-property!}"
798                " (or its C++ equivalent) sets a mutable property.\n"
799                "\n"
800                "The properties @code{after-line-breaking} and"
801                " @code{before-line-breaking} are dummies that are not"
802                " user-serviceable.",
803
804                /* properties */
805                "X-extent "
806                "X-offset "
807                "Y-extent "
808                "Y-offset "
809                "after-line-breaking "
810                "avoid-slur "
811                "axis-group-parent-X "
812                "axis-group-parent-Y "
813                "before-line-breaking "
814                "cause "
815                "color "
816                "cross-staff "
817                "id "
818                "extra-offset "
819                "footnote-music "
820                "forced-spacing "
821                "horizontal-skylines "
822                "interfaces "
823                "layer "
824                "meta "
825                "minimum-X-extent "
826                "minimum-Y-extent "
827                "parenthesis-friends "
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                "whiteout-style "
838               );
839
840 /****************************************************************
841   CALLBACKS
842 ****************************************************************/
843
844 static SCM
845 grob_stencil_extent (Grob *me, Axis a)
846 {
847   Stencil *m = me->get_stencil ();
848   Interval e;
849   if (m)
850     e = m->extent (a);
851   return ly_interval2scm (e);
852 }
853
854 MAKE_SCHEME_CALLBACK (Grob, stencil_height, 1);
855 SCM
856 Grob::stencil_height (SCM smob)
857 {
858   Grob *me = unsmob<Grob> (smob);
859   return grob_stencil_extent (me, Y_AXIS);
860 }
861
862 MAKE_SCHEME_CALLBACK (Grob, pure_stencil_height, 3);
863 SCM
864 Grob::pure_stencil_height (SCM smob, SCM /* beg */, SCM /* end */)
865 {
866   Grob *me = unsmob<Grob> (smob);
867   if (unsmob<Stencil> (me->get_property_data ("stencil")))
868     return grob_stencil_extent (me, Y_AXIS);
869
870   return ly_interval2scm (Interval ());
871
872 }
873
874 MAKE_SCHEME_CALLBACK (Grob, y_parent_positioning, 1);
875 SCM
876 Grob::y_parent_positioning (SCM smob)
877 {
878   Grob *me = unsmob<Grob> (smob);
879   Grob *par = me->get_parent (Y_AXIS);
880   if (par)
881     (void) par->get_property ("positioning-done");
882
883   return scm_from_double (0.0);
884 }
885
886 MAKE_SCHEME_CALLBACK (Grob, x_parent_positioning, 1);
887 SCM
888 Grob::x_parent_positioning (SCM smob)
889 {
890   Grob *me = unsmob<Grob> (smob);
891
892   Grob *par = me->get_parent (X_AXIS);
893   if (par)
894     (void) par->get_property ("positioning-done");
895
896   return scm_from_double (0.0);
897 }
898
899 MAKE_SCHEME_CALLBACK (Grob, stencil_width, 1);
900 SCM
901 Grob::stencil_width (SCM smob)
902 {
903   Grob *me = unsmob<Grob> (smob);
904   return grob_stencil_extent (me, X_AXIS);
905 }
906
907 Grob *
908 common_refpoint_of_list (SCM elist, Grob *common, Axis a)
909 {
910   for (; scm_is_pair (elist); elist = scm_cdr (elist))
911     if (Grob *s = unsmob<Grob> (scm_car (elist)))
912       {
913         if (common)
914           common = common->common_refpoint (s, a);
915         else
916           common = s;
917       }
918
919   return common;
920 }
921
922 Grob *
923 common_refpoint_of_array (vector<Grob *> const &arr, Grob *common, Axis a)
924 {
925   for (vsize i = 0; i < arr.size (); i++)
926     if (common)
927       common = common->common_refpoint (arr[i], a);
928     else
929       common = arr[i];
930
931   return common;
932 }
933
934 Grob *
935 common_refpoint_of_array (set<Grob *> const &arr, Grob *common, Axis a)
936 {
937   set<Grob *>::iterator it;
938
939   for (it = arr.begin (); it != arr.end (); it++)
940     if (common)
941       common = common->common_refpoint (*it, a);
942     else
943       common = *it;
944
945   return common;
946 }
947
948 Interval
949 robust_relative_extent (Grob *me, Grob *refpoint, Axis a)
950 {
951   Interval ext = me->extent (refpoint, a);
952   if (ext.is_empty ())
953     ext.add_point (me->relative_coordinate (refpoint, a));
954
955   return ext;
956 }
957
958 // Checks whether there is a vertical alignment in the chain of
959 // parents between this and commony.
960 bool
961 Grob::check_cross_staff (Grob *commony)
962 {
963   if (has_interface<Align_interface> (commony))
964     return true;
965
966   for (Grob *g = this; g && g != commony; g = g->get_parent (Y_AXIS))
967     if (has_interface<Align_interface> (g))
968       return true;
969
970   return false;
971 }
972
973 static
974 bool
975 indirect_less (Grob **a, Grob **b)
976 {
977   // Use original order as tie breaker.  That gives us a stable sort
978   // at the lower price tag of an unstable one, and we want a stable
979   // sort in order to reliably retain the first instance of a grob
980   // pointer.
981   return *a < *b || (*a == *b && a < b);
982 }
983
984 static
985 bool
986 indirect_eq (Grob **a, Grob **b)
987 {
988   return *a == *b;
989 }
990
991 static
992 bool
993 direct_less (Grob **a, Grob **b)
994 {
995   return a < b;
996 }
997
998 // uniquify uniquifies on the memory addresses of the Grobs, but then
999 // uses the original order.  This makes results independent from the
1000 // memory allocation of Grobs.
1001
1002 void
1003 uniquify (vector <Grob *> & grobs)
1004 {
1005   vector <Grob **> vec (grobs.size ());
1006   for (vsize i = 0; i < grobs.size (); i++)
1007     vec[i] = &grobs[i];
1008   vector_sort (vec, indirect_less);
1009   vec.erase (unique (vec.begin (), vec.end (), indirect_eq), vec.end ());
1010   vector_sort (vec, direct_less);
1011
1012   // Since the output is a sorted copy of the input with some elements
1013   // removed, we can fill in the vector in-place if we do it starting
1014   // from the front.
1015   for (vsize i = 0; i < vec.size (); i++)
1016     grobs[i] = *vec[i];
1017   grobs.erase (grobs.begin () + vec.size (), grobs.end ());
1018   return;
1019 }