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