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