Move ambitus print callback to scheme.
[lilypond/mpolesky.git] / lily / grob.cc
blob540f279b44d109fcb8679eb4ac7fe176756b2132
1 /*
2 grob.cc -- implement Grob
4 source file of the GNU LilyPond music typesetter
6 (c) 1997--2009 Han-Wen Nienhuys <hanwen@xs4all.nl>
7 */
9 #include "grob.hh"
11 #include <cstring>
13 #include "align-interface.hh"
14 #include "axis-group-interface.hh"
15 #include "input.hh"
16 #include "international.hh"
17 #include "item.hh"
18 #include "main.hh"
19 #include "misc.hh"
20 #include "music.hh"
21 #include "output-def.hh"
22 #include "pointer-group-interface.hh"
23 #include "program-option.hh"
24 #include "stencil.hh"
25 #include "stream-event.hh"
26 #include "system.hh"
27 #include "warn.hh"
29 #include "ly-smobs.icc"
31 Grob *
32 Grob::clone () const
34 return new Grob (*this);
37 Grob::Grob (SCM basicprops)
40 /* FIXME: default should be no callback. */
41 self_scm_ = SCM_EOL;
42 layout_ = 0;
43 original_ = 0;
44 interfaces_ = SCM_EOL;
45 immutable_property_alist_ = basicprops;
46 mutable_property_alist_ = SCM_EOL;
47 object_alist_ = SCM_EOL;
49 /* We do smobify_self () as the first step. Since the object lives
50 on the heap, none of its SCM variables are protected from
51 GC. After smobify_self (), they are. */
52 smobify_self ();
54 SCM meta = get_property ("meta");
55 if (scm_is_pair (meta))
57 interfaces_ = scm_cdr (scm_assq (ly_symbol2scm ("interfaces"), meta));
59 SCM object_cbs = scm_assq (ly_symbol2scm ("object-callbacks"), meta);
60 if (scm_is_pair (object_cbs))
62 for (SCM s = scm_cdr (object_cbs); scm_is_pair (s); s = scm_cdr (s))
63 set_object (scm_caar (s), scm_cdar (s));
67 if (get_property_data ("X-extent") == SCM_EOL)
68 set_property ("X-extent", Grob::stencil_width_proc);
69 if (get_property_data ("Y-extent") == SCM_EOL)
70 set_property ("Y-extent", Grob::stencil_height_proc);
73 Grob::Grob (Grob const &s)
74 : dim_cache_ (s.dim_cache_)
76 original_ = (Grob *) & s;
77 self_scm_ = SCM_EOL;
79 immutable_property_alist_ = s.immutable_property_alist_;
80 mutable_property_alist_ = ly_deep_copy (s.mutable_property_alist_);
81 interfaces_ = s.interfaces_;
82 object_alist_ = SCM_EOL;
84 layout_ = 0;
86 smobify_self ();
89 Grob::~Grob ()
92 /****************************************************************
93 STENCILS
94 ****************************************************************/
96 Stencil *
97 Grob::get_stencil () const
99 if (!is_live ())
100 return 0;
102 SCM stil = get_property ("stencil");
103 return unsmob_stencil (stil);
106 Stencil
107 Grob::get_print_stencil () const
109 SCM stil = get_property ("stencil");
111 Stencil retval;
112 if (Stencil *m = unsmob_stencil (stil))
114 retval = *m;
115 if (to_boolean (get_property ("transparent")))
116 retval = Stencil (m->extent_box (), SCM_EOL);
117 else
119 SCM expr = m->expr ();
120 expr = scm_list_3 (ly_symbol2scm ("grob-cause"),
121 self_scm (), expr);
123 retval = Stencil (m->extent_box (), expr);
126 SCM rot = get_property ("rotation");
127 if (scm_is_pair (rot))
129 Real angle = scm_to_double (scm_car (rot));
130 Real x = scm_to_double (scm_cadr (rot));
131 Real y = scm_to_double (scm_caddr (rot));
133 retval.rotate_degrees (angle, Offset (x, y));
136 /* color support... see interpret_stencil_expression () for more... */
137 SCM color = get_property ("color");
138 if (scm_is_pair (color))
140 SCM expr = scm_list_3 (ly_symbol2scm ("color"),
141 color,
142 retval.expr ());
144 retval = Stencil (retval.extent_box (), expr);
149 return retval;
152 /****************************************************************
153 VIRTUAL STUBS
154 ****************************************************************/
155 void
156 Grob::do_break_processing ()
160 void
161 Grob::discretionary_processing ()
165 System *
166 Grob::get_system () const
168 return 0;
172 void
173 Grob::handle_broken_dependencies ()
175 Spanner *sp = dynamic_cast<Spanner *> (this);
176 if (original () && sp)
177 return;
179 if (sp)
180 /* THIS, SP is the original spanner. We use a special function
181 because some Spanners have enormously long lists in their
182 properties, and a special function fixes FOO */
184 for (SCM s = object_alist_; scm_is_pair (s); s = scm_cdr (s))
185 sp->substitute_one_mutable_property (scm_caar (s), scm_cdar (s));
187 System *system = get_system ();
189 if (is_live ()
190 && system
191 && common_refpoint (system, X_AXIS)
192 && common_refpoint (system, Y_AXIS))
193 substitute_object_links (system->self_scm (), object_alist_);
194 else if (dynamic_cast<System *> (this))
195 substitute_object_links (SCM_UNDEFINED, object_alist_);
196 else
197 /* THIS element is `invalid'; it has been removed from all
198 dependencies, so let's junk the element itself.
200 Do not do this for System, since that would remove references
201 to the originals of score-grobs, which get then GC'd (a bad
202 thing). */
203 suicide ();
206 /* Note that we still want references to this element to be
207 rearranged, and not silently thrown away, so we keep pointers like
208 {broken_into_{drul, array}, original}
210 void
211 Grob::suicide ()
213 if (!is_live ())
214 return;
216 for (int a = X_AXIS; a < NO_AXES; a++)
217 dim_cache_[a].clear ();
219 mutable_property_alist_ = SCM_EOL;
220 object_alist_ = SCM_EOL;
221 immutable_property_alist_ = SCM_EOL;
222 interfaces_ = SCM_EOL;
225 void
226 Grob::handle_prebroken_dependencies ()
228 /* Don't do this in the derived method, since we want to keep access to
229 object_alist_ centralized. */
230 if (original ())
232 Item *it = dynamic_cast<Item *> (this);
233 substitute_object_links (scm_from_int (it->break_status_dir ()),
234 original ()->object_alist_);
238 Grob *
239 Grob::find_broken_piece (System *) const
241 return 0;
244 /****************************************************************
245 OFFSETS
246 ****************************************************************/
248 void
249 Grob::translate_axis (Real y, Axis a)
251 if (isinf (y) || isnan (y))
253 programming_error (_ ("Infinity or NaN encountered"));
254 return ;
257 if (!dim_cache_[a].offset_)
258 dim_cache_[a].offset_ = new Real (y);
259 else
260 *dim_cache_[a].offset_ += y;
263 /* Find the offset relative to D. If D equals THIS, then it is 0.
264 Otherwise, it recursively defd as
266 OFFSET_ + PARENT_L_->relative_coordinate (D) */
267 Real
268 Grob::relative_coordinate (Grob const *refp, Axis a) const
270 /* eaa - hmmm, should we do a programming_error() here? */
271 if ((this == NULL) || (refp == this))
272 return 0.0;
274 /* We catch PARENT_L_ == nil case with this, but we crash if we did
275 not ask for the absolute coordinate (ie. REFP == nil.) */
276 Real off = get_offset (a);
277 if (refp == dim_cache_[a].parent_)
278 return off;
280 off += dim_cache_[a].parent_->relative_coordinate (refp, a);
282 return off;
285 Real
286 Grob::pure_relative_y_coordinate (Grob const *refp, int start, int end)
288 if (refp == this)
289 return 0.0;
291 Real off = 0;
293 if (dim_cache_[Y_AXIS].offset_)
295 if (to_boolean (get_property ("pure-Y-offset-in-progress")))
296 programming_error ("cyclic chain in pure-Y-offset callbacks");
298 off = *dim_cache_[Y_AXIS].offset_;
300 else
302 SCM proc = get_property_data ("Y-offset");
304 dim_cache_[Y_AXIS].offset_ = new Real (0.0);
305 set_property ("pure-Y-offset-in-progress", SCM_BOOL_T);
306 off = robust_scm2double (call_pure_function (proc,
307 scm_list_1 (self_scm ()),
308 start, end),
309 0.0);
310 del_property ("pure-Y-offset-in-progress");
311 delete dim_cache_[Y_AXIS].offset_;
312 dim_cache_[Y_AXIS].offset_ = 0;
315 /* we simulate positioning-done if we are the child of a VerticalAlignment,
316 but only if we don't have a cached offset. If we do have a cached offset,
317 it probably means that the Alignment was fixed and it has already been
318 calculated.
320 if (Grob *p = get_parent (Y_AXIS))
322 Real trans = 0;
323 if (Align_interface::has_interface (p) && !dim_cache_[Y_AXIS].offset_)
324 trans = Align_interface::get_pure_child_y_translation (p, this, start, end);
326 return off + trans + p->pure_relative_y_coordinate (refp, start, end);
328 return off;
331 /* Invoke callbacks to get offset relative to parent. */
332 Real
333 Grob::get_offset (Axis a) const
335 if (dim_cache_[a].offset_)
336 return *dim_cache_[a].offset_;
338 Grob *me = (Grob *) this;
340 SCM sym = axis_offset_symbol (a);
341 me->dim_cache_[a].offset_ = new Real (0.0);
344 UGH: can't fold next 2 statements together. Apparently GCC thinks
345 dim_cache_[a].offset_ is unaliased.
347 Real off = robust_scm2double (internal_get_property (sym), 0.0);
348 if (me->dim_cache_[a].offset_)
350 *me->dim_cache_[a].offset_ += off;
351 me->del_property (sym);
352 return *me->dim_cache_[a].offset_;
354 else
355 return 0.0;
358 Real
359 Grob::maybe_pure_coordinate (Grob const *refp, Axis a, bool pure, int start, int end)
361 if (pure && a != Y_AXIS)
362 programming_error ("tried to get pure X-offset");
363 return (pure && a == Y_AXIS) ? pure_relative_y_coordinate (refp, start, end)
364 : relative_coordinate (refp, a);
367 /****************************************************************
368 extents
369 ****************************************************************/
371 void
372 Grob::flush_extent_cache (Axis axis)
374 if (dim_cache_[axis].extent_)
377 Ugh, this is not accurate; will flush property, causing
378 callback to be called if.
380 del_property ((axis == X_AXIS) ? ly_symbol2scm ("X-extent") : ly_symbol2scm ("Y-extent"));
381 delete dim_cache_[axis].extent_;
382 dim_cache_[axis].extent_ = 0;
383 if (get_parent (axis))
384 get_parent (axis)->flush_extent_cache (axis);
389 Interval
390 Grob::extent (Grob *refp, Axis a) const
392 Real offset = relative_coordinate (refp, a);
393 Interval real_ext;
394 if (dim_cache_[a].extent_)
396 real_ext = *dim_cache_[a].extent_;
398 else
401 Order is significant: ?-extent may trigger suicide.
403 SCM ext_sym =
404 (a == X_AXIS)
405 ? ly_symbol2scm ("X-extent")
406 : ly_symbol2scm ("Y-extent");
408 SCM ext = internal_get_property (ext_sym);
409 if (is_number_pair (ext))
410 real_ext.unite (ly_scm2interval (ext));
412 SCM min_ext_sym =
413 (a == X_AXIS)
414 ? ly_symbol2scm ("minimum-X-extent")
415 : ly_symbol2scm ("minimum-Y-extent");
416 SCM min_ext = internal_get_property (min_ext_sym);
417 if (is_number_pair (min_ext))
418 real_ext.unite (ly_scm2interval (min_ext));
420 ((Grob*)this)->dim_cache_[a].extent_ = new Interval (real_ext);
423 real_ext.translate (offset);
425 return real_ext;
428 Interval
429 Grob::pure_height (Grob *refp, int start, int end)
431 SCM proc = get_property_data (ly_symbol2scm ("Y-extent"));
432 SCM iv_scm = call_pure_function (proc,
433 scm_list_1 (self_scm ()),
434 start, end);
435 Interval iv = robust_scm2interval (iv_scm, Interval (0, 0));
436 Real offset = pure_relative_y_coordinate (refp, start, end);
438 SCM min_ext = get_property ("minimum-Y-extent");
440 /* we don't add minimum-Y-extent if the extent is empty. This solves
441 a problem with Hara-kiri spanners. They would request_suicide and
442 return empty extents, but we would force them here to be large. */
443 if (!iv.is_empty () && is_number_pair (min_ext))
444 iv.unite (ly_scm2interval (min_ext));
446 if (!iv.is_empty ())
447 iv.translate (offset);
448 return iv;
451 Interval
452 Grob::maybe_pure_extent (Grob *refp, Axis a, bool pure, int start, int end)
454 if (pure && a != Y_AXIS)
455 programming_error ("tried to get pure width");
456 return (pure && a == Y_AXIS) ? pure_height (refp, start, end) : extent (refp, a);
459 Interval_t<int>
460 Grob::spanned_rank_interval () const
462 return Interval_t<int> (-1, 0);
465 /****************************************************************
466 REFPOINTS
467 ****************************************************************/
469 /* Find the group-element which has both #this# and #s# */
470 Grob *
471 Grob::common_refpoint (Grob const *s, Axis a) const
473 /* I don't like the quadratic aspect of this code, but I see no
474 other way. The largest chain of parents might be 10 high or so,
475 so it shouldn't be a real issue. */
476 for (Grob const *c = this; c; c = c->dim_cache_[a].parent_)
477 for (Grob const *d = s; d; d = d->dim_cache_[a].parent_)
478 if (d == c)
479 return (Grob *) d;
481 return 0;
484 void
485 Grob::set_parent (Grob *g, Axis a)
487 dim_cache_[a].parent_ = g;
490 Grob *
491 Grob::get_parent (Axis a) const
493 return dim_cache_[a].parent_;
497 void
498 Grob::fixup_refpoint ()
500 for (int a = X_AXIS; a < NO_AXES; a++)
502 Axis ax = (Axis)a;
503 Grob *parent = get_parent (ax);
505 if (!parent)
506 continue;
508 if (parent->get_system () != get_system () && get_system ())
510 Grob *newparent = parent->find_broken_piece (get_system ());
511 set_parent (newparent, ax);
514 if (Item *i = dynamic_cast<Item *> (this))
516 Item *parenti = dynamic_cast<Item *> (parent);
518 if (parenti && i)
520 Direction my_dir = i->break_status_dir ();
521 if (my_dir != parenti->break_status_dir ())
523 Item *newparent = parenti->find_prebroken_piece (my_dir);
524 set_parent (newparent, ax);
532 /****************************************************************
533 MESSAGES
534 ****************************************************************/
535 void
536 Grob::warning (string s) const
538 if (get_program_option ("warning-as-error"))
539 error (s);
541 SCM cause = self_scm ();
542 while (Grob *g = unsmob_grob (cause))
543 cause = g->get_property ("cause");
545 /* ES TODO: cause can't be Music*/
546 if (Music *m = unsmob_music (cause))
547 m->origin ()->warning (s);
548 else if (Stream_event *ev = unsmob_stream_event (cause))
549 ev->origin ()->warning (s);
550 else
551 ::warning (s);
555 string
556 Grob::name () const
558 SCM meta = get_property ("meta");
559 SCM nm = scm_assq (ly_symbol2scm ("name"), meta);
560 nm = (scm_is_pair (nm)) ? scm_cdr (nm) : SCM_EOL;
561 return scm_is_symbol (nm) ? ly_symbol2string (nm) : this->class_name ();
564 void
565 Grob::programming_error (string s) const
567 if (get_program_option ("warning-as-error"))
568 error (s);
570 SCM cause = self_scm ();
571 while (Grob *g = unsmob_grob (cause))
572 cause = g->get_property ("cause");
574 s = _f ("programming error: %s", s);
576 /* ES TODO: cause can't be Music*/
577 if (Music *m = unsmob_music (cause))
578 m->origin ()->message (s);
579 else if (Stream_event *ev = unsmob_stream_event (cause))
580 ev->origin ()->message (s);
581 else
582 ::message (s);
586 ADD_INTERFACE (Grob,
587 "A grob represents a piece of music notation.\n"
588 "\n"
589 "All grobs have an X and Y@tie{}position on the page. These"
590 " X and Y@tie{}positions are stored in a relative format, thus"
591 " they can easily be combined by stacking them, hanging one"
592 " grob to the side of another, or coupling them into grouping"
593 " objects.\n"
594 "\n"
595 "Each grob has a reference point (a.k.a.@: parent): The"
596 " position of a grob is stored relative to that reference"
597 " point. For example, the X@tie{}reference point of a staccato"
598 " dot usually is the note head that it applies to. When the"
599 " note head is moved, the staccato dot moves along"
600 " automatically.\n"
601 "\n"
602 "A grob is often associated with a symbol, but some grobs do"
603 " not print any symbols. They take care of grouping objects."
604 " For example, there is a separate grob that stacks staves"
605 " vertically. The @ref{NoteCollision} object is also an"
606 " abstract grob: It only moves around chords, but doesn't print"
607 " anything.\n"
608 "\n"
609 "Grobs have properties (Scheme variables) that can be read and"
610 " set. Two types of them exist: immutable and mutable."
611 " Immutable variables define the default style and behavior."
612 " They are shared between many objects. They can be changed"
613 " using @code{\\override} and @code{\\revert}. Mutable"
614 " properties are variables that are specific to one grob."
615 " Typically, lists of other objects, or results from"
616 " computations are stored in mutable properties. In"
617 " particular, every call to @code{ly:grob-set-property!}"
618 " (or its C++ equivalent) sets a mutable property.\n"
619 "\n"
620 "The properties @code{after-line-breaking} and"
621 " @code{before-line-breaking} are dummies that are not"
622 " user-serviceable.",
624 /* properties */
625 "X-extent "
626 "X-offset "
627 "Y-extent "
628 "Y-offset "
629 "after-line-breaking "
630 "avoid-slur "
631 "axis-group-parent-X "
632 "axis-group-parent-Y "
633 "before-line-breaking "
634 "cause "
635 "color "
636 "cross-staff "
637 "extra-X-extent "
638 "extra-Y-extent "
639 "extra-offset "
640 "interfaces "
641 "layer "
642 "meta "
643 "minimum-X-extent "
644 "minimum-Y-extent "
645 "outside-staff-horizontal-padding "
646 "outside-staff-padding "
647 "outside-staff-priority "
648 "pure-Y-offset-in-progress "
649 "rotation "
650 "springs-and-rods "
651 "staff-symbol "
652 "stencil "
653 "transparent "
656 /****************************************************************
657 CALLBACKS
658 ****************************************************************/
660 static SCM
661 grob_stencil_extent (Grob *me, Axis a)
663 Stencil *m = me->get_stencil ();
664 Interval e;
665 if (m)
666 e = m->extent (a);
667 return ly_interval2scm (e);
671 MAKE_SCHEME_CALLBACK (Grob, stencil_height, 1);
673 Grob::stencil_height (SCM smob)
675 Grob *me = unsmob_grob (smob);
676 return grob_stencil_extent (me, Y_AXIS);
679 MAKE_SCHEME_CALLBACK (Grob, y_parent_positioning, 1);
681 Grob::y_parent_positioning (SCM smob)
683 Grob *me = unsmob_grob (smob);
684 Grob *par = me->get_parent (Y_AXIS);
685 if (par)
686 (void) par->get_property ("positioning-done");
688 return scm_from_double (0.0);
692 MAKE_SCHEME_CALLBACK (Grob, x_parent_positioning, 1);
694 Grob::x_parent_positioning (SCM smob)
696 Grob *me = unsmob_grob (smob);
698 Grob *par = me->get_parent (X_AXIS);
699 if (par)
700 (void) par->get_property ("positioning-done");
702 return scm_from_double (0.0);
705 MAKE_SCHEME_CALLBACK (Grob, stencil_width, 1);
707 Grob::stencil_width (SCM smob)
709 Grob *me = unsmob_grob (smob);
710 return grob_stencil_extent (me, X_AXIS);
714 Grob *
715 common_refpoint_of_list (SCM elist, Grob *common, Axis a)
717 for (; scm_is_pair (elist); elist = scm_cdr (elist))
718 if (Grob *s = unsmob_grob (scm_car (elist)))
720 if (common)
721 common = common->common_refpoint (s, a);
722 else
723 common = s;
726 return common;
729 Grob *
730 common_refpoint_of_array (vector<Grob*> const &arr, Grob *common, Axis a)
732 for (vsize i = 0; i < arr.size (); i++)
733 if (common)
734 common = common->common_refpoint (arr[i], a);
735 else
736 common = arr[i];
738 return common;
741 Interval
742 robust_relative_extent (Grob *me, Grob *refpoint, Axis a)
744 Interval ext = me->extent (refpoint, a);
745 if (ext.is_empty ())
746 ext.add_point (me->relative_coordinate (refpoint, a));
748 return ext;
751 // Checks whether there is a vertical alignment in the chain of
752 // parents between this and commony.
753 bool
754 Grob::check_cross_staff (Grob *commony)
756 if (Align_interface::has_interface (commony))
757 return true;
759 for (Grob *g = this; g && g != commony; g = g->get_parent (Y_AXIS))
760 if (Align_interface::has_interface (g))
761 return true;
763 return false;