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