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