Fix flag snippet in NEWS.tely.
[lilypond.git] / lily / grob.cc
blob9479f17f41179cb3fbdc3cf6769fb533ffbbde9a
1 /*
2 grob.cc -- implement Grob
4 source file of the GNU LilyPond music typesetter
6 (c) 1997--2007 Han-Wen Nienhuys <hanwen@xs4all.nl>
7 */
9 #include "grob.hh"
11 #include <cstring>
13 #include "align-interface.hh"
14 #include "input.hh"
15 #include "international.hh"
16 #include "item.hh"
17 #include "main.hh"
18 #include "misc.hh"
19 #include "music.hh"
20 #include "output-def.hh"
21 #include "pointer-group-interface.hh"
22 #include "stencil.hh"
23 #include "stream-event.hh"
24 #include "system.hh"
25 #include "warn.hh"
27 #include "ly-smobs.icc"
29 Grob *
30 Grob::clone () const
32 return new Grob (*this);
35 Grob::Grob (SCM basicprops)
38 /* FIXME: default should be no callback. */
39 self_scm_ = SCM_EOL;
40 layout_ = 0;
41 original_ = 0;
42 interfaces_ = SCM_EOL;
43 immutable_property_alist_ = basicprops;
44 mutable_property_alist_ = SCM_EOL;
45 object_alist_ = SCM_EOL;
47 /* We do smobify_self () as the first step. Since the object lives
48 on the heap, none of its SCM variables are protected from
49 GC. After smobify_self (), they are. */
50 smobify_self ();
52 SCM meta = get_property ("meta");
53 if (scm_is_pair (meta))
55 interfaces_ = scm_cdr (scm_assq (ly_symbol2scm ("interfaces"), meta));
57 SCM object_cbs = scm_assq (ly_symbol2scm ("object-callbacks"), meta);
58 if (scm_is_pair (object_cbs))
60 for (SCM s = scm_cdr (object_cbs); scm_is_pair (s); s = scm_cdr (s))
61 set_object (scm_caar (s), scm_cdar (s));
65 if (get_property_data ("X-extent") == SCM_EOL)
66 set_property ("X-extent", Grob::stencil_width_proc);
67 if (get_property_data ("Y-extent") == SCM_EOL)
68 set_property ("Y-extent", Grob::stencil_height_proc);
71 Grob::Grob (Grob const &s)
72 : dim_cache_ (s.dim_cache_)
74 original_ = (Grob *) & s;
75 self_scm_ = SCM_EOL;
77 immutable_property_alist_ = s.immutable_property_alist_;
78 mutable_property_alist_ = ly_deep_copy (s.mutable_property_alist_);
79 interfaces_ = s.interfaces_;
80 object_alist_ = SCM_EOL;
82 layout_ = 0;
84 smobify_self ();
87 Grob::~Grob ()
90 /****************************************************************
91 STENCILS
92 ****************************************************************/
94 Stencil *
95 Grob::get_stencil () const
97 if (!is_live ())
98 return 0;
100 SCM stil = get_property ("stencil");
101 return unsmob_stencil (stil);
104 Stencil
105 Grob::get_print_stencil () const
107 SCM stil = get_property ("stencil");
109 Stencil retval;
110 if (Stencil *m = unsmob_stencil (stil))
112 retval = *m;
113 if (to_boolean (get_property ("transparent")))
114 retval = Stencil (m->extent_box (), SCM_EOL);
115 else
117 SCM expr = m->expr ();
118 expr = scm_list_3 (ly_symbol2scm ("grob-cause"),
119 self_scm (), expr);
121 retval = Stencil (m->extent_box (), expr);
124 SCM rot = get_property ("rotation");
125 if (scm_is_pair (rot))
127 Real angle = scm_to_double (scm_car (rot));
128 Real x = scm_to_double (scm_cadr (rot));
129 Real y = scm_to_double (scm_caddr (rot));
131 retval.rotate_degrees (angle, Offset (x, y));
134 /* color support... see interpret_stencil_expression () for more... */
135 SCM color = get_property ("color");
136 if (scm_is_pair (color))
138 SCM expr = scm_list_3 (ly_symbol2scm ("color"),
139 color,
140 retval.expr ());
142 retval = Stencil (retval.extent_box (), expr);
147 return retval;
150 /****************************************************************
151 VIRTUAL STUBS
152 ****************************************************************/
153 void
154 Grob::do_break_processing ()
158 void
159 Grob::discretionary_processing ()
163 System *
164 Grob::get_system () const
166 return 0;
170 void
171 Grob::handle_broken_dependencies ()
173 Spanner *sp = dynamic_cast<Spanner *> (this);
174 if (original () && sp)
175 return;
177 if (sp)
178 /* THIS, SP is the original spanner. We use a special function
179 because some Spanners have enormously long lists in their
180 properties, and a special function fixes FOO */
182 for (SCM s = object_alist_; scm_is_pair (s); s = scm_cdr (s))
183 sp->substitute_one_mutable_property (scm_caar (s), scm_cdar (s));
185 System *system = get_system ();
187 if (is_live ()
188 && system
189 && common_refpoint (system, X_AXIS)
190 && common_refpoint (system, Y_AXIS))
191 substitute_object_links (system->self_scm (), object_alist_);
192 else if (dynamic_cast<System *> (this))
193 substitute_object_links (SCM_UNDEFINED, object_alist_);
194 else
195 /* THIS element is `invalid'; it has been removed from all
196 dependencies, so let's junk the element itself.
198 Do not do this for System, since that would remove references
199 to the originals of score-grobs, which get then GC'd (a bad
200 thing). */
201 suicide ();
204 /* Note that we still want references to this element to be
205 rearranged, and not silently thrown away, so we keep pointers like
206 {broken_into_{drul, array}, original}
208 void
209 Grob::suicide ()
211 if (!is_live ())
212 return;
214 for (int a = X_AXIS; a < NO_AXES; a++)
215 dim_cache_[a].clear ();
217 mutable_property_alist_ = SCM_EOL;
218 object_alist_ = SCM_EOL;
219 immutable_property_alist_ = SCM_EOL;
220 interfaces_ = SCM_EOL;
223 void
224 Grob::handle_prebroken_dependencies ()
226 /* Don't do this in the derived method, since we want to keep access to
227 object_alist_ centralized. */
228 if (original ())
230 Item *it = dynamic_cast<Item *> (this);
231 substitute_object_links (scm_from_int (it->break_status_dir ()),
232 original ()->object_alist_);
236 Grob *
237 Grob::find_broken_piece (System *) const
239 return 0;
242 /****************************************************************
243 OFFSETS
244 ****************************************************************/
246 void
247 Grob::translate_axis (Real y, Axis a)
249 if (isinf (y) || isnan (y))
251 programming_error (_ ("Infinity or NaN encountered"));
252 return ;
255 if (!dim_cache_[a].offset_)
256 dim_cache_[a].offset_ = new Real (y);
257 else
258 *dim_cache_[a].offset_ += y;
261 /* Find the offset relative to D. If D equals THIS, then it is 0.
262 Otherwise, it recursively defd as
264 OFFSET_ + PARENT_L_->relative_coordinate (D) */
265 Real
266 Grob::relative_coordinate (Grob const *refp, Axis a) const
268 /* eaa - hmmm, should we do a programming_error() here? */
269 if ((this == NULL) || (refp == this))
270 return 0.0;
272 /* We catch PARENT_L_ == nil case with this, but we crash if we did
273 not ask for the absolute coordinate (ie. REFP == nil.) */
274 Real off = get_offset (a);
275 if (refp == dim_cache_[a].parent_)
276 return off;
278 off += dim_cache_[a].parent_->relative_coordinate (refp, a);
280 return off;
283 Real
284 Grob::pure_relative_y_coordinate (Grob const *refp, int start, int end)
286 if (refp == this)
287 return 0.0;
289 Real off = 0;
291 if (dim_cache_[Y_AXIS].offset_)
293 if (to_boolean (get_property ("pure-Y-offset-in-progress")))
294 programming_error ("cyclic chain in pure-Y-offset callbacks");
296 off = *dim_cache_[Y_AXIS].offset_;
298 else
300 SCM proc = get_property_data ("Y-offset");
302 dim_cache_[Y_AXIS].offset_ = new Real (0.0);
303 set_property ("pure-Y-offset-in-progress", SCM_BOOL_T);
304 off = robust_scm2double (call_pure_function (proc,
305 scm_list_1 (self_scm ()),
306 start, end),
307 0.0);
308 del_property ("pure-Y-offset-in-progress");
309 delete dim_cache_[Y_AXIS].offset_;
310 dim_cache_[Y_AXIS].offset_ = 0;
313 /* we simulate positioning-done if we are the child of a VerticalAlignment,
314 but only if we don't have a cached offset. If we do have a cached offset,
315 it probably means that the Alignment was fixed and it has already been
316 calculated.
318 if (Grob *p = get_parent (Y_AXIS))
320 Real trans = 0;
321 if (Align_interface::has_interface (p) && !dim_cache_[Y_AXIS].offset_)
322 trans = Align_interface::get_pure_child_y_translation (p, this, start, end);
324 return off + trans + p->pure_relative_y_coordinate (refp, start, end);
326 return off;
329 /* Invoke callbacks to get offset relative to parent. */
330 Real
331 Grob::get_offset (Axis a) const
333 if (dim_cache_[a].offset_)
334 return *dim_cache_[a].offset_;
336 Grob *me = (Grob *) this;
338 SCM sym = axis_offset_symbol (a);
339 me->dim_cache_[a].offset_ = new Real (0.0);
342 UGH: can't fold next 2 statements together. Apparently GCC thinks
343 dim_cache_[a].offset_ is unaliased.
345 Real off = robust_scm2double (internal_get_property (sym), 0.0);
346 if (me->dim_cache_[a].offset_)
348 *me->dim_cache_[a].offset_ += off;
349 me->del_property (sym);
350 return *me->dim_cache_[a].offset_;
352 else
353 return 0.0;
356 Real
357 Grob::maybe_pure_coordinate (Grob const *refp, Axis a, bool pure, int start, int end)
359 if (pure && a != Y_AXIS)
360 programming_error ("tried to get pure X-offset");
361 return (pure && a == Y_AXIS) ? pure_relative_y_coordinate (refp, start, end)
362 : relative_coordinate (refp, a);
365 /****************************************************************
366 extents
367 ****************************************************************/
369 void
370 Grob::flush_extent_cache (Axis axis)
372 if (dim_cache_[axis].extent_)
375 Ugh, this is not accurate; will flush property, causing
376 callback to be called if.
378 del_property ((axis == X_AXIS) ? ly_symbol2scm ("X-extent") : ly_symbol2scm ("Y-extent"));
379 delete dim_cache_[axis].extent_;
380 dim_cache_[axis].extent_ = 0;
381 if (get_parent (axis))
382 get_parent (axis)->flush_extent_cache (axis);
387 Interval
388 Grob::extent (Grob *refp, Axis a) const
390 Real offset = relative_coordinate (refp, a);
391 Interval real_ext;
392 if (dim_cache_[a].extent_)
394 real_ext = *dim_cache_[a].extent_;
396 else
399 Order is significant: ?-extent may trigger suicide.
401 SCM ext_sym =
402 (a == X_AXIS)
403 ? ly_symbol2scm ("X-extent")
404 : ly_symbol2scm ("Y-extent");
406 SCM ext = internal_get_property (ext_sym);
407 if (is_number_pair (ext))
408 real_ext.unite (ly_scm2interval (ext));
410 SCM min_ext_sym =
411 (a == X_AXIS)
412 ? ly_symbol2scm ("minimum-X-extent")
413 : ly_symbol2scm ("minimum-Y-extent");
414 SCM min_ext = internal_get_property (min_ext_sym);
415 if (is_number_pair (min_ext))
416 real_ext.unite (ly_scm2interval (min_ext));
418 ((Grob*)this)->dim_cache_[a].extent_ = new Interval (real_ext);
421 real_ext.translate (offset);
423 return real_ext;
426 Interval
427 Grob::pure_height (Grob *refp, int start, int end)
429 SCM proc = get_property_data (ly_symbol2scm ("Y-extent"));
430 SCM iv_scm = call_pure_function (proc,
431 scm_list_1 (self_scm ()),
432 start, end);
433 Interval iv = robust_scm2interval (iv_scm, Interval (0, 0));
434 Real offset = pure_relative_y_coordinate (refp, start, end);
436 SCM min_ext = get_property ("minimum-Y-extent");
438 /* we don't add minimum-Y-extent if the extent is empty. This solves
439 a problem with Hara-kiri spanners. They would request_suicide and
440 return empty extents, but we would force them here to be large. */
441 if (!iv.is_empty () && is_number_pair (min_ext))
442 iv.unite (ly_scm2interval (min_ext));
444 if (!iv.is_empty ())
445 iv.translate (offset);
446 return iv;
449 Interval
450 Grob::maybe_pure_extent (Grob *refp, Axis a, bool pure, int start, int end)
452 if (pure && a != Y_AXIS)
453 programming_error ("tried to get pure width");
454 return (pure && a == Y_AXIS) ? pure_height (refp, start, end) : extent (refp, a);
457 Interval_t<int>
458 Grob::spanned_rank_interval () const
460 return Interval_t<int> (-1, 0);
463 /****************************************************************
464 REFPOINTS
465 ****************************************************************/
467 /* Find the group-element which has both #this# and #s# */
468 Grob *
469 Grob::common_refpoint (Grob const *s, Axis a) const
471 /* I don't like the quadratic aspect of this code, but I see no
472 other way. The largest chain of parents might be 10 high or so,
473 so it shouldn't be a real issue. */
474 for (Grob const *c = this; c; c = c->dim_cache_[a].parent_)
475 for (Grob const *d = s; d; d = d->dim_cache_[a].parent_)
476 if (d == c)
477 return (Grob *) d;
479 return 0;
482 void
483 Grob::set_parent (Grob *g, Axis a)
485 dim_cache_[a].parent_ = g;
488 Grob *
489 Grob::get_parent (Axis a) const
491 return dim_cache_[a].parent_;
495 void
496 Grob::fixup_refpoint ()
498 for (int a = X_AXIS; a < NO_AXES; a++)
500 Axis ax = (Axis)a;
501 Grob *parent = get_parent (ax);
503 if (!parent)
504 continue;
506 if (parent->get_system () != get_system () && get_system ())
508 Grob *newparent = parent->find_broken_piece (get_system ());
509 set_parent (newparent, ax);
512 if (Item *i = dynamic_cast<Item *> (this))
514 Item *parenti = dynamic_cast<Item *> (parent);
516 if (parenti && i)
518 Direction my_dir = i->break_status_dir ();
519 if (my_dir != parenti->break_status_dir ())
521 Item *newparent = parenti->find_prebroken_piece (my_dir);
522 set_parent (newparent, ax);
530 /****************************************************************
531 MESSAGES
532 ****************************************************************/
533 void
534 Grob::warning (string s) const
536 SCM cause = self_scm ();
537 while (Grob *g = unsmob_grob (cause))
538 cause = g->get_property ("cause");
540 /* ES TODO: cause can't be Music*/
541 if (Music *m = unsmob_music (cause))
542 m->origin ()->warning (s);
543 else if (Stream_event *ev = unsmob_stream_event (cause))
544 ev->origin ()->warning (s);
545 else
546 ::warning (s);
550 string
551 Grob::name () const
553 SCM meta = get_property ("meta");
554 SCM nm = scm_assq (ly_symbol2scm ("name"), meta);
555 nm = (scm_is_pair (nm)) ? scm_cdr (nm) : SCM_EOL;
556 return scm_is_symbol (nm) ? ly_symbol2string (nm) : this->class_name ();
559 void
560 Grob::programming_error (string s) const
562 SCM cause = self_scm ();
563 while (Grob *g = unsmob_grob (cause))
564 cause = g->get_property ("cause");
566 s = _f ("programming error: %s", s);
568 /* ES TODO: cause can't be Music*/
569 if (Music *m = unsmob_music (cause))
570 m->origin ()->message (s);
571 else if (Stream_event *ev = unsmob_stream_event (cause))
572 ev->origin ()->warning (s);
573 else
574 ::message (s);
578 ADD_INTERFACE (Grob,
579 "A grob represents a piece of music notation.\n"
580 "\n"
581 "All grobs have an X and Y@tie{}position on the page. These"
582 " X and Y@tie{}positions are stored in a relative format, thus"
583 " they can easily be combined by stacking them, hanging one"
584 " grob to the side of another, or coupling them into grouping"
585 " objects.\n"
586 "\n"
587 "Each grob has a reference point (a.k.a.@: parent): The"
588 " position of a grob is stored relative to that reference"
589 " point. For example, the X@tie{}reference point of a staccato"
590 " dot usually is the note head that it applies to. When the"
591 " note head is moved, the staccato dot moves along"
592 " automatically.\n"
593 "\n"
594 "A grob is often associated with a symbol, but some grobs do"
595 " not print any symbols. They take care of grouping objects."
596 " For example, there is a separate grob that stacks staves"
597 " vertically. The @ref{NoteCollision} object is also an"
598 " abstract grob: It only moves around chords, but doesn't print"
599 " anything.\n"
600 "\n"
601 "Grobs have properties (Scheme variables) that can be read and"
602 " set. Two types of them exist: immutable and mutable."
603 " Immutable variables define the default style and behavior."
604 " They are shared between many objects. They can be changed"
605 " using @code{\\override} and @code{\\revert}. Mutable"
606 " properties are variables that are specific to one grob."
607 " Typically, lists of other objects, or results from"
608 " computations are stored in mutable properties. In"
609 " particular, every call to @code{set-grob-property} (or its"
610 " C++ equivalent) sets a mutable property.\n"
611 "\n"
612 "The properties @code{after-line-breaking} and"
613 " @code{before-line-breaking} are dummies that are not"
614 " user-serviceable.",
616 /* properties */
617 "X-extent "
618 "X-offset "
619 "Y-extent "
620 "Y-offset "
621 "after-line-breaking "
622 "avoid-slur "
623 "axis-group-parent-X "
624 "axis-group-parent-Y "
625 "before-line-breaking "
626 "cause "
627 "color "
628 "cross-staff "
629 "extra-X-extent "
630 "extra-Y-extent "
631 "extra-offset "
632 "interfaces "
633 "layer "
634 "meta "
635 "minimum-X-extent "
636 "minimum-Y-extent "
637 "outside-staff-horizontal-padding "
638 "outside-staff-padding "
639 "outside-staff-priority "
640 "pure-Y-offset-in-progress "
641 "rotation "
642 "springs-and-rods "
643 "staff-symbol "
644 "stencil "
645 "transparent "
648 /****************************************************************
649 CALLBACKS
650 ****************************************************************/
652 static SCM
653 grob_stencil_extent (Grob *me, Axis a)
655 Stencil *m = me->get_stencil ();
656 Interval e;
657 if (m)
658 e = m->extent (a);
659 return ly_interval2scm (e);
663 MAKE_SCHEME_CALLBACK (Grob, stencil_height, 1);
665 Grob::stencil_height (SCM smob)
667 Grob *me = unsmob_grob (smob);
668 return grob_stencil_extent (me, Y_AXIS);
671 MAKE_SCHEME_CALLBACK (Grob, y_parent_positioning, 1);
673 Grob::y_parent_positioning (SCM smob)
675 Grob *me = unsmob_grob (smob);
676 Grob *par = me->get_parent (Y_AXIS);
677 if (par)
678 (void) par->get_property ("positioning-done");
680 return scm_from_double (0.0);
684 MAKE_SCHEME_CALLBACK (Grob, x_parent_positioning, 1);
686 Grob::x_parent_positioning (SCM smob)
688 Grob *me = unsmob_grob (smob);
690 Grob *par = me->get_parent (X_AXIS);
691 if (par)
692 (void) par->get_property ("positioning-done");
694 return scm_from_double (0.0);
697 MAKE_SCHEME_CALLBACK (Grob, stencil_width, 1);
699 Grob::stencil_width (SCM smob)
701 Grob *me = unsmob_grob (smob);
702 return grob_stencil_extent (me, X_AXIS);
707 Grob *
708 common_refpoint_of_list (SCM elist, Grob *common, Axis a)
710 for (; scm_is_pair (elist); elist = scm_cdr (elist))
711 if (Grob *s = unsmob_grob (scm_car (elist)))
713 if (common)
714 common = common->common_refpoint (s, a);
715 else
716 common = s;
719 return common;
722 Grob *
723 common_refpoint_of_array (vector<Grob*> const &arr, Grob *common, Axis a)
725 for (vsize i = 0; i < arr.size (); i++)
726 if (common)
727 common = common->common_refpoint (arr[i], a);
728 else
729 common = arr[i];
731 return common;
734 Interval
735 robust_relative_extent (Grob *me, Grob *refpoint, Axis a)
737 Interval ext = me->extent (refpoint, a);
738 if (ext.is_empty ())
739 ext.add_point (me->relative_coordinate (refpoint, a));
741 return ext;