script.scm: Quantize staccatissimo; de-quantize accent, espressivo.
[lilypond/mpolesky.git] / lily / grob.cc
bloba6e715d206b428f0164ab0c2c3b01795eeae9213
1 /*
2 This file is part of LilyPond, the GNU music typesetter.
4 Copyright (C) 1997--2009 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 if (to_boolean (get_property ("transparent")))
127 retval = Stencil (m->extent_box (), SCM_EOL);
128 else
130 SCM expr = m->expr ();
131 expr = scm_list_3 (ly_symbol2scm ("grob-cause"),
132 self_scm (), expr);
134 retval = Stencil (m->extent_box (), expr);
137 SCM rot = get_property ("rotation");
138 if (scm_is_pair (rot))
140 Real angle = scm_to_double (scm_car (rot));
141 Real x = scm_to_double (scm_cadr (rot));
142 Real y = scm_to_double (scm_caddr (rot));
144 retval.rotate_degrees (angle, Offset (x, y));
147 /* color support... see interpret_stencil_expression () for more... */
148 SCM color = get_property ("color");
149 if (scm_is_pair (color))
151 SCM expr = scm_list_3 (ly_symbol2scm ("color"),
152 color,
153 retval.expr ());
155 retval = Stencil (retval.extent_box (), expr);
160 return retval;
163 /****************************************************************
164 VIRTUAL STUBS
165 ****************************************************************/
166 void
167 Grob::do_break_processing ()
171 void
172 Grob::discretionary_processing ()
176 System *
177 Grob::get_system () const
179 return 0;
183 void
184 Grob::handle_broken_dependencies ()
186 Spanner *sp = dynamic_cast<Spanner *> (this);
187 if (original () && sp)
188 return;
190 if (sp)
191 /* THIS, SP is the original spanner. We use a special function
192 because some Spanners have enormously long lists in their
193 properties, and a special function fixes FOO */
195 for (SCM s = object_alist_; scm_is_pair (s); s = scm_cdr (s))
196 sp->substitute_one_mutable_property (scm_caar (s), scm_cdar (s));
198 System *system = get_system ();
200 if (is_live ()
201 && system
202 && common_refpoint (system, X_AXIS)
203 && common_refpoint (system, Y_AXIS))
204 substitute_object_links (system->self_scm (), object_alist_);
205 else if (dynamic_cast<System *> (this))
206 substitute_object_links (SCM_UNDEFINED, object_alist_);
207 else
208 /* THIS element is `invalid'; it has been removed from all
209 dependencies, so let's junk the element itself.
211 Do not do this for System, since that would remove references
212 to the originals of score-grobs, which get then GC'd (a bad
213 thing). */
214 suicide ();
217 /* Note that we still want references to this element to be
218 rearranged, and not silently thrown away, so we keep pointers like
219 {broken_into_{drul, array}, original}
221 void
222 Grob::suicide ()
224 if (!is_live ())
225 return;
227 for (int a = X_AXIS; a < NO_AXES; a++)
228 dim_cache_[a].clear ();
230 mutable_property_alist_ = SCM_EOL;
231 object_alist_ = SCM_EOL;
232 immutable_property_alist_ = SCM_EOL;
233 interfaces_ = SCM_EOL;
236 void
237 Grob::handle_prebroken_dependencies ()
239 /* Don't do this in the derived method, since we want to keep access to
240 object_alist_ centralized. */
241 if (original ())
243 Item *it = dynamic_cast<Item *> (this);
244 substitute_object_links (scm_from_int (it->break_status_dir ()),
245 original ()->object_alist_);
249 Grob *
250 Grob::find_broken_piece (System *) const
252 return 0;
255 /****************************************************************
256 OFFSETS
257 ****************************************************************/
259 void
260 Grob::translate_axis (Real y, Axis a)
262 if (isinf (y) || isnan (y))
264 programming_error (_ ("Infinity or NaN encountered"));
265 return ;
268 if (!dim_cache_[a].offset_)
269 dim_cache_[a].offset_ = new Real (y);
270 else
271 *dim_cache_[a].offset_ += y;
274 /* Find the offset relative to D. If D equals THIS, then it is 0.
275 Otherwise, it recursively defd as
277 OFFSET_ + PARENT_L_->relative_coordinate (D) */
278 Real
279 Grob::relative_coordinate (Grob const *refp, Axis a) const
281 /* eaa - hmmm, should we do a programming_error() here? */
282 if ((this == NULL) || (refp == this))
283 return 0.0;
285 /* We catch PARENT_L_ == nil case with this, but we crash if we did
286 not ask for the absolute coordinate (ie. REFP == nil.) */
287 Real off = get_offset (a);
288 if (refp == dim_cache_[a].parent_)
289 return off;
291 off += dim_cache_[a].parent_->relative_coordinate (refp, a);
293 return off;
296 Real
297 Grob::pure_relative_y_coordinate (Grob const *refp, int start, int end)
299 if (refp == this)
300 return 0.0;
302 Real off = 0;
304 if (dim_cache_[Y_AXIS].offset_)
306 if (to_boolean (get_property ("pure-Y-offset-in-progress")))
307 programming_error ("cyclic chain in pure-Y-offset callbacks");
309 off = *dim_cache_[Y_AXIS].offset_;
311 else
313 SCM proc = get_property_data ("Y-offset");
315 dim_cache_[Y_AXIS].offset_ = new Real (0.0);
316 set_property ("pure-Y-offset-in-progress", SCM_BOOL_T);
317 off = robust_scm2double (call_pure_function (proc,
318 scm_list_1 (self_scm ()),
319 start, end),
320 0.0);
321 del_property ("pure-Y-offset-in-progress");
322 delete dim_cache_[Y_AXIS].offset_;
323 dim_cache_[Y_AXIS].offset_ = 0;
326 /* we simulate positioning-done if we are the child of a VerticalAlignment,
327 but only if we don't have a cached offset. If we do have a cached offset,
328 it probably means that the Alignment was fixed and it has already been
329 calculated.
331 if (Grob *p = get_parent (Y_AXIS))
333 Real trans = 0;
334 if (Align_interface::has_interface (p) && !dim_cache_[Y_AXIS].offset_)
335 trans = Align_interface::get_pure_child_y_translation (p, this, start, end);
337 return off + trans + p->pure_relative_y_coordinate (refp, start, end);
339 return off;
342 /* Invoke callbacks to get offset relative to parent. */
343 Real
344 Grob::get_offset (Axis a) const
346 if (dim_cache_[a].offset_)
347 return *dim_cache_[a].offset_;
349 Grob *me = (Grob *) this;
351 SCM sym = axis_offset_symbol (a);
352 me->dim_cache_[a].offset_ = new Real (0.0);
355 UGH: can't fold next 2 statements together. Apparently GCC thinks
356 dim_cache_[a].offset_ is unaliased.
358 Real off = robust_scm2double (internal_get_property (sym), 0.0);
359 if (me->dim_cache_[a].offset_)
361 *me->dim_cache_[a].offset_ += off;
362 me->del_property (sym);
363 return *me->dim_cache_[a].offset_;
365 else
366 return 0.0;
369 Real
370 Grob::maybe_pure_coordinate (Grob const *refp, Axis a, bool pure, int start, int end)
372 if (pure && a != Y_AXIS)
373 programming_error ("tried to get pure X-offset");
374 return (pure && a == Y_AXIS) ? pure_relative_y_coordinate (refp, start, end)
375 : relative_coordinate (refp, a);
378 /****************************************************************
379 extents
380 ****************************************************************/
382 void
383 Grob::flush_extent_cache (Axis axis)
385 if (dim_cache_[axis].extent_)
388 Ugh, this is not accurate; will flush property, causing
389 callback to be called if.
391 del_property ((axis == X_AXIS) ? ly_symbol2scm ("X-extent") : ly_symbol2scm ("Y-extent"));
392 delete dim_cache_[axis].extent_;
393 dim_cache_[axis].extent_ = 0;
394 if (get_parent (axis))
395 get_parent (axis)->flush_extent_cache (axis);
400 Interval
401 Grob::extent (Grob *refp, Axis a) const
403 Real offset = relative_coordinate (refp, a);
404 Interval real_ext;
405 if (dim_cache_[a].extent_)
407 real_ext = *dim_cache_[a].extent_;
409 else
412 Order is significant: ?-extent may trigger suicide.
414 SCM ext_sym =
415 (a == X_AXIS)
416 ? ly_symbol2scm ("X-extent")
417 : ly_symbol2scm ("Y-extent");
419 SCM ext = internal_get_property (ext_sym);
420 if (is_number_pair (ext))
421 real_ext.unite (ly_scm2interval (ext));
423 SCM min_ext_sym =
424 (a == X_AXIS)
425 ? ly_symbol2scm ("minimum-X-extent")
426 : ly_symbol2scm ("minimum-Y-extent");
427 SCM min_ext = internal_get_property (min_ext_sym);
428 if (is_number_pair (min_ext))
429 real_ext.unite (ly_scm2interval (min_ext));
431 ((Grob*)this)->dim_cache_[a].extent_ = new Interval (real_ext);
434 real_ext.translate (offset);
436 return real_ext;
439 Interval
440 Grob::pure_height (Grob *refp, int start, int end)
442 SCM proc = get_property_data (ly_symbol2scm ("Y-extent"));
443 SCM iv_scm = call_pure_function (proc,
444 scm_list_1 (self_scm ()),
445 start, end);
446 Interval iv = robust_scm2interval (iv_scm, Interval (0, 0));
447 Real offset = pure_relative_y_coordinate (refp, start, end);
449 SCM min_ext = get_property ("minimum-Y-extent");
451 /* we don't add minimum-Y-extent if the extent is empty. This solves
452 a problem with Hara-kiri spanners. They would request_suicide and
453 return empty extents, but we would force them here to be large. */
454 if (!iv.is_empty () && is_number_pair (min_ext))
455 iv.unite (ly_scm2interval (min_ext));
457 if (!iv.is_empty ())
458 iv.translate (offset);
459 return iv;
462 Interval
463 Grob::maybe_pure_extent (Grob *refp, Axis a, bool pure, int start, int end)
465 if (pure && a != Y_AXIS)
466 programming_error ("tried to get pure width");
467 return (pure && a == Y_AXIS) ? pure_height (refp, start, end) : extent (refp, a);
470 Interval_t<int>
471 Grob::spanned_rank_interval () const
473 return Interval_t<int> (-1, 0);
476 /****************************************************************
477 REFPOINTS
478 ****************************************************************/
480 /* Find the group-element which has both #this# and #s# */
481 Grob *
482 Grob::common_refpoint (Grob const *s, Axis a) const
484 /* I don't like the quadratic aspect of this code, but I see no
485 other way. The largest chain of parents might be 10 high or so,
486 so it shouldn't be a real issue. */
487 for (Grob const *c = this; c; c = c->dim_cache_[a].parent_)
488 for (Grob const *d = s; d; d = d->dim_cache_[a].parent_)
489 if (d == c)
490 return (Grob *) d;
492 return 0;
495 void
496 Grob::set_parent (Grob *g, Axis a)
498 dim_cache_[a].parent_ = g;
501 Grob *
502 Grob::get_parent (Axis a) const
504 return dim_cache_[a].parent_;
508 void
509 Grob::fixup_refpoint ()
511 for (int a = X_AXIS; a < NO_AXES; a++)
513 Axis ax = (Axis)a;
514 Grob *parent = get_parent (ax);
516 if (!parent)
517 continue;
519 if (parent->get_system () != get_system () && get_system ())
521 Grob *newparent = parent->find_broken_piece (get_system ());
522 set_parent (newparent, ax);
525 if (Item *i = dynamic_cast<Item *> (this))
527 Item *parenti = dynamic_cast<Item *> (parent);
529 if (parenti && i)
531 Direction my_dir = i->break_status_dir ();
532 if (my_dir != parenti->break_status_dir ())
534 Item *newparent = parenti->find_prebroken_piece (my_dir);
535 set_parent (newparent, ax);
543 /****************************************************************
544 MESSAGES
545 ****************************************************************/
546 void
547 Grob::warning (string s) const
549 if (get_program_option ("warning-as-error"))
550 error (s);
552 SCM cause = self_scm ();
553 while (Grob *g = unsmob_grob (cause))
554 cause = g->get_property ("cause");
556 /* ES TODO: cause can't be Music*/
557 if (Music *m = unsmob_music (cause))
558 m->origin ()->warning (s);
559 else if (Stream_event *ev = unsmob_stream_event (cause))
560 ev->origin ()->warning (s);
561 else
562 ::warning (s);
566 string
567 Grob::name () const
569 SCM meta = get_property ("meta");
570 SCM nm = scm_assq (ly_symbol2scm ("name"), meta);
571 nm = (scm_is_pair (nm)) ? scm_cdr (nm) : SCM_EOL;
572 return scm_is_symbol (nm) ? ly_symbol2string (nm) : this->class_name ();
575 void
576 Grob::programming_error (string s) const
578 if (get_program_option ("warning-as-error"))
579 error (s);
581 SCM cause = self_scm ();
582 while (Grob *g = unsmob_grob (cause))
583 cause = g->get_property ("cause");
585 s = _f ("programming error: %s", s);
587 /* ES TODO: cause can't be Music*/
588 if (Music *m = unsmob_music (cause))
589 m->origin ()->message (s);
590 else if (Stream_event *ev = unsmob_stream_event (cause))
591 ev->origin ()->message (s);
592 else
593 ::message (s);
597 ADD_INTERFACE (Grob,
598 "A grob represents a piece of music notation.\n"
599 "\n"
600 "All grobs have an X and Y@tie{}position on the page. These"
601 " X and Y@tie{}positions are stored in a relative format, thus"
602 " they can easily be combined by stacking them, hanging one"
603 " grob to the side of another, or coupling them into grouping"
604 " objects.\n"
605 "\n"
606 "Each grob has a reference point (a.k.a.@: parent): The"
607 " position of a grob is stored relative to that reference"
608 " point. For example, the X@tie{}reference point of a staccato"
609 " dot usually is the note head that it applies to. When the"
610 " note head is moved, the staccato dot moves along"
611 " automatically.\n"
612 "\n"
613 "A grob is often associated with a symbol, but some grobs do"
614 " not print any symbols. They take care of grouping objects."
615 " For example, there is a separate grob that stacks staves"
616 " vertically. The @ref{NoteCollision} object is also an"
617 " abstract grob: It only moves around chords, but doesn't print"
618 " anything.\n"
619 "\n"
620 "Grobs have properties (Scheme variables) that can be read and"
621 " set. Two types of them exist: immutable and mutable."
622 " Immutable variables define the default style and behavior."
623 " They are shared between many objects. They can be changed"
624 " using @code{\\override} and @code{\\revert}. Mutable"
625 " properties are variables that are specific to one grob."
626 " Typically, lists of other objects, or results from"
627 " computations are stored in mutable properties. In"
628 " particular, every call to @code{ly:grob-set-property!}"
629 " (or its C++ equivalent) sets a mutable property.\n"
630 "\n"
631 "The properties @code{after-line-breaking} and"
632 " @code{before-line-breaking} are dummies that are not"
633 " user-serviceable.",
635 /* properties */
636 "X-extent "
637 "X-offset "
638 "Y-extent "
639 "Y-offset "
640 "after-line-breaking "
641 "avoid-slur "
642 "axis-group-parent-X "
643 "axis-group-parent-Y "
644 "before-line-breaking "
645 "cause "
646 "color "
647 "cross-staff "
648 "extra-X-extent "
649 "extra-Y-extent "
650 "extra-offset "
651 "interfaces "
652 "layer "
653 "meta "
654 "minimum-X-extent "
655 "minimum-Y-extent "
656 "outside-staff-horizontal-padding "
657 "outside-staff-padding "
658 "outside-staff-priority "
659 "pure-Y-offset-in-progress "
660 "rotation "
661 "springs-and-rods "
662 "staff-symbol "
663 "stencil "
664 "transparent "
667 /****************************************************************
668 CALLBACKS
669 ****************************************************************/
671 static SCM
672 grob_stencil_extent (Grob *me, Axis a)
674 Stencil *m = me->get_stencil ();
675 Interval e;
676 if (m)
677 e = m->extent (a);
678 return ly_interval2scm (e);
682 MAKE_SCHEME_CALLBACK (Grob, stencil_height, 1);
684 Grob::stencil_height (SCM smob)
686 Grob *me = unsmob_grob (smob);
687 return grob_stencil_extent (me, Y_AXIS);
690 MAKE_SCHEME_CALLBACK (Grob, y_parent_positioning, 1);
692 Grob::y_parent_positioning (SCM smob)
694 Grob *me = unsmob_grob (smob);
695 Grob *par = me->get_parent (Y_AXIS);
696 if (par)
697 (void) par->get_property ("positioning-done");
699 return scm_from_double (0.0);
703 MAKE_SCHEME_CALLBACK (Grob, x_parent_positioning, 1);
705 Grob::x_parent_positioning (SCM smob)
707 Grob *me = unsmob_grob (smob);
709 Grob *par = me->get_parent (X_AXIS);
710 if (par)
711 (void) par->get_property ("positioning-done");
713 return scm_from_double (0.0);
716 MAKE_SCHEME_CALLBACK (Grob, stencil_width, 1);
718 Grob::stencil_width (SCM smob)
720 Grob *me = unsmob_grob (smob);
721 return grob_stencil_extent (me, X_AXIS);
725 Grob *
726 common_refpoint_of_list (SCM elist, Grob *common, Axis a)
728 for (; scm_is_pair (elist); elist = scm_cdr (elist))
729 if (Grob *s = unsmob_grob (scm_car (elist)))
731 if (common)
732 common = common->common_refpoint (s, a);
733 else
734 common = s;
737 return common;
740 Grob *
741 common_refpoint_of_array (vector<Grob*> const &arr, Grob *common, Axis a)
743 for (vsize i = 0; i < arr.size (); i++)
744 if (common)
745 common = common->common_refpoint (arr[i], a);
746 else
747 common = arr[i];
749 return common;
752 Interval
753 robust_relative_extent (Grob *me, Grob *refpoint, Axis a)
755 Interval ext = me->extent (refpoint, a);
756 if (ext.is_empty ())
757 ext.add_point (me->relative_coordinate (refpoint, a));
759 return ext;
762 // Checks whether there is a vertical alignment in the chain of
763 // parents between this and commony.
764 bool
765 Grob::check_cross_staff (Grob *commony)
767 if (Align_interface::has_interface (commony))
768 return true;
770 for (Grob *g = this; g && g != commony; g = g->get_parent (Y_AXIS))
771 if (Align_interface::has_interface (g))
772 return true;
774 return false;