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