Fixes Issue 1504, allowing feather beam line breaking.
[lilypond/patrick.git] / lily / grob.cc
blobc613effccc65fd6be001aad742e41a1ed29b15a4
1 /*
2 This file is part of LilyPond, the GNU music typesetter.
4 Copyright (C) 1997--2011 Han-Wen Nienhuys <hanwen@xs4all.nl>
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.
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.
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/>.
20 #include "grob.hh"
22 #include <cstring>
24 #include "align-interface.hh"
25 #include "axis-group-interface.hh"
26 #include "input.hh"
27 #include "international.hh"
28 #include "item.hh"
29 #include "main.hh"
30 #include "misc.hh"
31 #include "music.hh"
32 #include "output-def.hh"
33 #include "pointer-group-interface.hh"
34 #include "program-option.hh"
35 #include "stencil.hh"
36 #include "stream-event.hh"
37 #include "system.hh"
38 #include "warn.hh"
40 #include "ly-smobs.icc"
42 Grob *
43 Grob::clone () const
45 return new Grob (*this);
48 Grob::Grob (SCM basicprops)
51 /* FIXME: default should be no callback. */
52 self_scm_ = SCM_EOL;
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;
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 ();
65 SCM meta = get_property ("meta");
66 if (scm_is_pair (meta))
68 interfaces_ = scm_cdr (scm_assq (ly_symbol2scm ("interfaces"), meta));
70 SCM object_cbs = scm_assq (ly_symbol2scm ("object-callbacks"), meta);
71 if (scm_is_pair (object_cbs))
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));
78 if (get_property_data ("X-extent") == SCM_EOL)
79 set_property ("X-extent", Grob::stencil_width_proc);
80 if (get_property_data ("Y-extent") == SCM_EOL)
81 set_property ("Y-extent", Grob::stencil_height_proc);
84 Grob::Grob (Grob const &s)
85 : dim_cache_ (s.dim_cache_)
87 original_ = (Grob *) & s;
88 self_scm_ = SCM_EOL;
90 immutable_property_alist_ = s.immutable_property_alist_;
91 mutable_property_alist_ = ly_deep_copy (s.mutable_property_alist_);
92 interfaces_ = s.interfaces_;
93 object_alist_ = SCM_EOL;
95 layout_ = 0;
97 smobify_self ();
100 Grob::~Grob ()
103 /****************************************************************
104 STENCILS
105 ****************************************************************/
107 Stencil *
108 Grob::get_stencil () const
110 if (!is_live ())
111 return 0;
113 SCM stil = get_property ("stencil");
114 return unsmob_stencil (stil);
117 Stencil
118 Grob::get_print_stencil () const
120 SCM stil = get_property ("stencil");
122 Stencil retval;
123 if (Stencil *m = unsmob_stencil (stil))
125 retval = *m;
126 bool transparent = to_boolean (get_property ("transparent"));
128 if (transparent)
129 retval = Stencil (m->extent_box (), SCM_EOL);
130 else
132 SCM expr = m->expr ();
133 expr = scm_list_3 (ly_symbol2scm ("grob-cause"),
134 self_scm (), expr);
136 retval = Stencil (m->extent_box (), expr);
139 SCM rot = get_property ("rotation");
140 if (scm_is_pair (rot))
142 Real angle = scm_to_double (scm_car (rot));
143 Real x = scm_to_double (scm_cadr (rot));
144 Real y = scm_to_double (scm_caddr (rot));
146 retval.rotate_degrees (angle, Offset (x, y));
149 /* color support... see interpret_stencil_expression () for more... */
150 SCM color = get_property ("color");
151 if (scm_is_pair (color))
153 SCM expr = scm_list_3 (ly_symbol2scm ("color"),
154 color,
155 retval.expr ());
157 retval = Stencil (retval.extent_box (), expr);
160 /* process whiteout */
161 /* a grob has to be visible, otherwise the whiteout property has no effect */
162 if (!transparent && to_boolean (get_property ("whiteout")))
164 /* Call the scheme procedure stencil-whiteout in scm/stencils.scm */
165 /* to add a round-filled-box stencil to the stencil list */
166 retval
167 = *unsmob_stencil (scm_call_1 (ly_lily_module_constant ("stencil-whiteout"),
168 retval.smobbed_copy()));
172 return retval;
175 /****************************************************************
176 VIRTUAL STUBS
177 ****************************************************************/
178 void
179 Grob::do_break_processing ()
183 void
184 Grob::discretionary_processing ()
188 System *
189 Grob::get_system () const
191 return 0;
194 /* This version of get_system is more reliable than this->get_system ()
195 before line-breaking has been done, at which point there is only
196 one system in the whole score and we can find it just by following
197 parent pointers. */
198 System *
199 Grob::get_system(Grob *me)
201 Grob *p = me->get_parent (X_AXIS);
202 return p ? get_system (p) : dynamic_cast<System *>(me);
205 void
206 Grob::handle_broken_dependencies ()
208 Spanner *sp = dynamic_cast<Spanner *> (this);
209 if (original () && sp)
210 return;
212 if (sp)
213 /* THIS, SP is the original spanner. We use a special function
214 because some Spanners have enormously long lists in their
215 properties, and a special function fixes FOO */
217 for (SCM s = object_alist_; scm_is_pair (s); s = scm_cdr (s))
218 sp->substitute_one_mutable_property (scm_caar (s), scm_cdar (s));
220 System *system = get_system ();
222 if (is_live ()
223 && system
224 && common_refpoint (system, X_AXIS)
225 && common_refpoint (system, Y_AXIS))
226 substitute_object_links (system->self_scm (), object_alist_);
227 else if (dynamic_cast<System *> (this))
228 substitute_object_links (SCM_UNDEFINED, object_alist_);
229 else
230 /* THIS element is `invalid'; it has been removed from all
231 dependencies, so let's junk the element itself.
233 Do not do this for System, since that would remove references
234 to the originals of score-grobs, which get then GC'd (a bad
235 thing). */
236 suicide ();
239 /* Note that we still want references to this element to be
240 rearranged, and not silently thrown away, so we keep pointers like
241 {broken_into_{drul, array}, original}
243 void
244 Grob::suicide ()
246 if (!is_live ())
247 return;
249 for (int a = X_AXIS; a < NO_AXES; a++)
250 dim_cache_[a].clear ();
252 mutable_property_alist_ = SCM_EOL;
253 object_alist_ = SCM_EOL;
254 immutable_property_alist_ = SCM_EOL;
255 interfaces_ = SCM_EOL;
258 void
259 Grob::handle_prebroken_dependencies ()
261 /* Don't do this in the derived method, since we want to keep access to
262 object_alist_ centralized. */
263 if (original ())
265 Item *it = dynamic_cast<Item *> (this);
266 substitute_object_links (scm_from_int (it->break_status_dir ()),
267 original ()->object_alist_);
271 Grob *
272 Grob::find_broken_piece (System *) const
274 return 0;
277 /****************************************************************
278 OFFSETS
279 ****************************************************************/
281 void
282 Grob::translate_axis (Real y, Axis a)
284 if (isinf (y) || isnan (y))
286 programming_error (_ ("Infinity or NaN encountered"));
287 return ;
290 if (!dim_cache_[a].offset_)
291 dim_cache_[a].offset_ = new Real (y);
292 else
293 *dim_cache_[a].offset_ += y;
296 /* Find the offset relative to D. If D equals THIS, then it is 0.
297 Otherwise, it recursively defd as
299 OFFSET_ + PARENT_L_->relative_coordinate (D) */
300 Real
301 Grob::relative_coordinate (Grob const *refp, Axis a) const
303 /* eaa - hmmm, should we do a programming_error() here? */
304 if ((this == NULL) || (refp == this))
305 return 0.0;
307 /* We catch PARENT_L_ == nil case with this, but we crash if we did
308 not ask for the absolute coordinate (ie. REFP == nil.) */
309 Real off = get_offset (a);
310 if (refp == dim_cache_[a].parent_)
311 return off;
313 off += dim_cache_[a].parent_->relative_coordinate (refp, a);
315 return off;
318 Real
319 Grob::pure_relative_y_coordinate (Grob const *refp, int start, int end)
321 if (refp == this)
322 return 0.0;
324 Real off = 0;
326 if (dim_cache_[Y_AXIS].offset_)
328 if (to_boolean (get_property ("pure-Y-offset-in-progress")))
329 programming_error ("cyclic chain in pure-Y-offset callbacks");
331 off = *dim_cache_[Y_AXIS].offset_;
333 else
335 SCM proc = get_property_data ("Y-offset");
337 dim_cache_[Y_AXIS].offset_ = new Real (0.0);
338 set_property ("pure-Y-offset-in-progress", SCM_BOOL_T);
339 off = robust_scm2double (call_pure_function (proc,
340 scm_list_1 (self_scm ()),
341 start, end),
342 0.0);
343 del_property ("pure-Y-offset-in-progress");
344 delete dim_cache_[Y_AXIS].offset_;
345 dim_cache_[Y_AXIS].offset_ = 0;
348 /* we simulate positioning-done if we are the child of a VerticalAlignment,
349 but only if we don't have a cached offset. If we do have a cached offset,
350 it probably means that the Alignment was fixed and it has already been
351 calculated.
353 if (Grob *p = get_parent (Y_AXIS))
355 Real trans = 0;
356 if (Align_interface::has_interface (p) && !dim_cache_[Y_AXIS].offset_)
357 trans = Align_interface::get_pure_child_y_translation (p, this, start, end);
359 return off + trans + p->pure_relative_y_coordinate (refp, start, end);
361 return off;
364 /* Invoke callbacks to get offset relative to parent. */
365 Real
366 Grob::get_offset (Axis a) const
368 if (dim_cache_[a].offset_)
369 return *dim_cache_[a].offset_;
371 Grob *me = (Grob *) this;
373 SCM sym = axis_offset_symbol (a);
374 me->dim_cache_[a].offset_ = new Real (0.0);
377 UGH: can't fold next 2 statements together. Apparently GCC thinks
378 dim_cache_[a].offset_ is unaliased.
380 Real off = robust_scm2double (internal_get_property (sym), 0.0);
381 if (me->dim_cache_[a].offset_)
383 *me->dim_cache_[a].offset_ += off;
384 me->del_property (sym);
385 return *me->dim_cache_[a].offset_;
387 else
388 return 0.0;
391 Real
392 Grob::maybe_pure_coordinate (Grob const *refp, Axis a, bool pure, int start, int end)
394 if (pure && a != Y_AXIS)
395 programming_error ("tried to get pure X-offset");
396 return (pure && a == Y_AXIS) ? pure_relative_y_coordinate (refp, start, end)
397 : relative_coordinate (refp, a);
400 /****************************************************************
401 extents
402 ****************************************************************/
404 void
405 Grob::flush_extent_cache (Axis axis)
407 if (dim_cache_[axis].extent_)
410 Ugh, this is not accurate; will flush property, causing
411 callback to be called if.
413 del_property ((axis == X_AXIS) ? ly_symbol2scm ("X-extent") : ly_symbol2scm ("Y-extent"));
414 delete dim_cache_[axis].extent_;
415 dim_cache_[axis].extent_ = 0;
416 if (get_parent (axis))
417 get_parent (axis)->flush_extent_cache (axis);
422 Interval
423 Grob::extent (Grob *refp, Axis a) const
425 Real offset = relative_coordinate (refp, a);
426 Interval real_ext;
427 if (dim_cache_[a].extent_)
429 real_ext = *dim_cache_[a].extent_;
431 else
434 Order is significant: ?-extent may trigger suicide.
436 SCM ext_sym =
437 (a == X_AXIS)
438 ? ly_symbol2scm ("X-extent")
439 : ly_symbol2scm ("Y-extent");
441 SCM ext = internal_get_property (ext_sym);
442 if (is_number_pair (ext))
443 real_ext.unite (ly_scm2interval (ext));
445 SCM min_ext_sym =
446 (a == X_AXIS)
447 ? ly_symbol2scm ("minimum-X-extent")
448 : ly_symbol2scm ("minimum-Y-extent");
449 SCM min_ext = internal_get_property (min_ext_sym);
450 if (is_number_pair (min_ext))
451 real_ext.unite (ly_scm2interval (min_ext));
453 ((Grob*)this)->dim_cache_[a].extent_ = new Interval (real_ext);
456 real_ext.translate (offset);
458 return real_ext;
461 Interval
462 Grob::pure_height (Grob *refp, int start, int end)
464 SCM iv_scm = get_pure_property ("Y-extent", start, end);
465 Interval iv = robust_scm2interval (iv_scm, Interval (0, 0));
466 Real offset = pure_relative_y_coordinate (refp, start, end);
468 SCM min_ext = get_property ("minimum-Y-extent");
470 /* we don't add minimum-Y-extent if the extent is empty. This solves
471 a problem with Hara-kiri spanners. They would request_suicide and
472 return empty extents, but we would force them here to be large. */
473 if (!iv.is_empty () && is_number_pair (min_ext))
474 iv.unite (ly_scm2interval (min_ext));
476 if (!iv.is_empty ())
477 iv.translate (offset);
478 return iv;
481 Interval
482 Grob::maybe_pure_extent (Grob *refp, Axis a, bool pure, int start, int end)
484 if (pure && a != Y_AXIS)
485 programming_error ("tried to get pure width");
486 return (pure && a == Y_AXIS) ? pure_height (refp, start, end) : extent (refp, a);
489 Interval_t<int>
490 Grob::spanned_rank_interval () const
492 return Interval_t<int> (-1, 0);
495 bool
496 Grob::pure_is_visible (int /* start */, int /* end */) const
498 return true;
501 /* Sort grobs according to their starting column. */
502 bool
503 Grob::less (Grob *g1, Grob *g2)
505 return g1->spanned_rank_interval ()[LEFT] < g2->spanned_rank_interval ()[LEFT];
508 /****************************************************************
509 REFPOINTS
510 ****************************************************************/
512 /* Find the group-element which has both #this# and #s# */
513 Grob *
514 Grob::common_refpoint (Grob const *s, Axis a) const
516 /* I don't like the quadratic aspect of this code, but I see no
517 other way. The largest chain of parents might be 10 high or so,
518 so it shouldn't be a real issue. */
519 for (Grob const *c = this; c; c = c->dim_cache_[a].parent_)
520 for (Grob const *d = s; d; d = d->dim_cache_[a].parent_)
521 if (d == c)
522 return (Grob *) d;
524 return 0;
527 void
528 Grob::set_parent (Grob *g, Axis a)
530 dim_cache_[a].parent_ = g;
533 Grob *
534 Grob::get_parent (Axis a) const
536 return dim_cache_[a].parent_;
540 void
541 Grob::fixup_refpoint ()
543 for (int a = X_AXIS; a < NO_AXES; a++)
545 Axis ax = (Axis)a;
546 Grob *parent = get_parent (ax);
548 if (!parent)
549 continue;
551 if (parent->get_system () != get_system () && get_system ())
553 Grob *newparent = parent->find_broken_piece (get_system ());
554 set_parent (newparent, ax);
557 if (Item *i = dynamic_cast<Item *> (this))
559 Item *parenti = dynamic_cast<Item *> (parent);
561 if (parenti && i)
563 Direction my_dir = i->break_status_dir ();
564 if (my_dir != parenti->break_status_dir ())
566 Item *newparent = parenti->find_prebroken_piece (my_dir);
567 set_parent (newparent, ax);
575 /****************************************************************
576 MESSAGES
577 ****************************************************************/
578 void
579 Grob::warning (string s) const
581 if (get_program_option ("warning-as-error"))
582 error (s);
584 SCM cause = self_scm ();
585 while (Grob *g = unsmob_grob (cause))
586 cause = g->get_property ("cause");
588 /* ES TODO: cause can't be Music*/
589 if (Music *m = unsmob_music (cause))
590 m->origin ()->warning (s);
591 else if (Stream_event *ev = unsmob_stream_event (cause))
592 ev->origin ()->warning (s);
593 else
594 ::warning (s);
598 string
599 Grob::name () const
601 SCM meta = get_property ("meta");
602 SCM nm = scm_assq (ly_symbol2scm ("name"), meta);
603 nm = (scm_is_pair (nm)) ? scm_cdr (nm) : SCM_EOL;
604 return scm_is_symbol (nm) ? ly_symbol2string (nm) : this->class_name ();
607 void
608 Grob::programming_error (string s) const
610 if (get_program_option ("warning-as-error"))
611 error (s);
613 SCM cause = self_scm ();
614 while (Grob *g = unsmob_grob (cause))
615 cause = g->get_property ("cause");
617 s = _f ("programming error: %s", s);
619 /* ES TODO: cause can't be Music*/
620 if (Music *m = unsmob_music (cause))
621 m->origin ()->message (s);
622 else if (Stream_event *ev = unsmob_stream_event (cause))
623 ev->origin ()->message (s);
624 else
625 ::message (s);
629 ADD_INTERFACE (Grob,
630 "A grob represents a piece of music notation.\n"
631 "\n"
632 "All grobs have an X and Y@tie{}position on the page. These"
633 " X and Y@tie{}positions are stored in a relative format, thus"
634 " they can easily be combined by stacking them, hanging one"
635 " grob to the side of another, or coupling them into grouping"
636 " objects.\n"
637 "\n"
638 "Each grob has a reference point (a.k.a.@: parent): The"
639 " position of a grob is stored relative to that reference"
640 " point. For example, the X@tie{}reference point of a staccato"
641 " dot usually is the note head that it applies to. When the"
642 " note head is moved, the staccato dot moves along"
643 " automatically.\n"
644 "\n"
645 "A grob is often associated with a symbol, but some grobs do"
646 " not print any symbols. They take care of grouping objects."
647 " For example, there is a separate grob that stacks staves"
648 " vertically. The @ref{NoteCollision} object is also an"
649 " abstract grob: It only moves around chords, but doesn't print"
650 " anything.\n"
651 "\n"
652 "Grobs have properties (Scheme variables) that can be read and"
653 " set. Two types of them exist: immutable and mutable."
654 " Immutable variables define the default style and behavior."
655 " They are shared between many objects. They can be changed"
656 " using @code{\\override} and @code{\\revert}. Mutable"
657 " properties are variables that are specific to one grob."
658 " Typically, lists of other objects, or results from"
659 " computations are stored in mutable properties. In"
660 " particular, every call to @code{ly:grob-set-property!}"
661 " (or its C++ equivalent) sets a mutable property.\n"
662 "\n"
663 "The properties @code{after-line-breaking} and"
664 " @code{before-line-breaking} are dummies that are not"
665 " user-serviceable.",
667 /* properties */
668 "X-extent "
669 "X-offset "
670 "Y-extent "
671 "Y-offset "
672 "after-line-breaking "
673 "avoid-slur "
674 "axis-group-parent-X "
675 "axis-group-parent-Y "
676 "before-line-breaking "
677 "cause "
678 "color "
679 "cross-staff "
680 "extra-X-extent "
681 "extra-Y-extent "
682 "extra-offset "
683 "interfaces "
684 "layer "
685 "meta "
686 "minimum-X-extent "
687 "minimum-Y-extent "
688 "outside-staff-horizontal-padding "
689 "outside-staff-padding "
690 "outside-staff-priority "
691 "pure-Y-offset-in-progress "
692 "rotation "
693 "springs-and-rods "
694 "staff-symbol "
695 "stencil "
696 "transparent "
697 "whiteout "
700 /****************************************************************
701 CALLBACKS
702 ****************************************************************/
704 static SCM
705 grob_stencil_extent (Grob *me, Axis a)
707 Stencil *m = me->get_stencil ();
708 Interval e;
709 if (m)
710 e = m->extent (a);
711 return ly_interval2scm (e);
715 MAKE_SCHEME_CALLBACK (Grob, stencil_height, 1);
717 Grob::stencil_height (SCM smob)
719 Grob *me = unsmob_grob (smob);
720 return grob_stencil_extent (me, Y_AXIS);
723 MAKE_SCHEME_CALLBACK (Grob, y_parent_positioning, 1);
725 Grob::y_parent_positioning (SCM smob)
727 Grob *me = unsmob_grob (smob);
728 Grob *par = me->get_parent (Y_AXIS);
729 if (par)
730 (void) par->get_property ("positioning-done");
732 return scm_from_double (0.0);
736 MAKE_SCHEME_CALLBACK (Grob, x_parent_positioning, 1);
738 Grob::x_parent_positioning (SCM smob)
740 Grob *me = unsmob_grob (smob);
742 Grob *par = me->get_parent (X_AXIS);
743 if (par)
744 (void) par->get_property ("positioning-done");
746 return scm_from_double (0.0);
749 MAKE_SCHEME_CALLBACK (Grob, stencil_width, 1);
751 Grob::stencil_width (SCM smob)
753 Grob *me = unsmob_grob (smob);
754 return grob_stencil_extent (me, X_AXIS);
758 Grob *
759 common_refpoint_of_list (SCM elist, Grob *common, Axis a)
761 for (; scm_is_pair (elist); elist = scm_cdr (elist))
762 if (Grob *s = unsmob_grob (scm_car (elist)))
764 if (common)
765 common = common->common_refpoint (s, a);
766 else
767 common = s;
770 return common;
773 Grob *
774 common_refpoint_of_array (vector<Grob*> const &arr, Grob *common, Axis a)
776 for (vsize i = 0; i < arr.size (); i++)
777 if (common)
778 common = common->common_refpoint (arr[i], a);
779 else
780 common = arr[i];
782 return common;
785 Interval
786 robust_relative_extent (Grob *me, Grob *refpoint, Axis a)
788 Interval ext = me->extent (refpoint, a);
789 if (ext.is_empty ())
790 ext.add_point (me->relative_coordinate (refpoint, a));
792 return ext;
795 // Checks whether there is a vertical alignment in the chain of
796 // parents between this and commony.
797 bool
798 Grob::check_cross_staff (Grob *commony)
800 if (Align_interface::has_interface (commony))
801 return true;
803 for (Grob *g = this; g && g != commony; g = g->get_parent (Y_AXIS))
804 if (Align_interface::has_interface (g))
805 return true;
807 return false;