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