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