* lexer-gcc-3.1.sh: Remove.
[lilypond/patrick.git] / lily / grob.cc
blobdd8726cac8bdc972324dc54391dcf6b7e4142072
1 /*
2 grob.cc -- implement Grob
4 source file of the GNU LilyPond music typesetter
6 (c) 1997--2006 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 (int count) const
32 return new Grob (*this, count);
35 Grob::Grob (SCM basicprops,
36 Object_key const *key)
38 key_ = key;
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 ();
55 We always get a new key object for a new grob.
57 if (key_)
58 ((Object_key *)key_)->unprotect ();
60 SCM meta = get_property ("meta");
61 if (scm_is_pair (meta))
62 interfaces_ = scm_cdr (scm_assq (ly_symbol2scm ("interfaces"), meta));
64 if (get_property_data (ly_symbol2scm ("X-extent")) == SCM_EOL)
65 set_property ("X-extent", Grob::stencil_width_proc);
66 if (get_property_data (ly_symbol2scm ("Y-extent")) == SCM_EOL)
67 set_property ("Y-extent", Grob::stencil_height_proc);
70 Grob::Grob (Grob const &s, int copy_index)
71 : dim_cache_ (s.dim_cache_)
73 key_ = (use_object_keys) ? new Copied_key (s.key_, copy_index) : 0;
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 ();
85 if (key_)
86 ((Object_key *)key_)->unprotect ();
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 (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 if (refp == this)
271 return 0.0;
273 /* We catch PARENT_L_ == nil case with this, but we crash if we did
274 not ask for the absolute coordinate (ie. REFP == nil.) */
275 Real off = get_offset (a);
276 if (refp == dim_cache_[a].parent_)
277 return off;
279 off += dim_cache_[a].parent_->relative_coordinate (refp, a);
281 return off;
284 Real
285 Grob::pure_relative_y_coordinate (Grob const *refp, int start, int end)
287 if (refp == this)
288 return 0.0;
290 SCM pure_off = ly_lily_module_constant ("pure-Y-offset");
291 Real off = 0;
293 if (dim_cache_[Y_AXIS].offset_)
294 off = *dim_cache_[Y_AXIS].offset_;
295 else if (ly_is_procedure (pure_off))
297 dim_cache_[Y_AXIS].offset_ = new Real (0.0);
298 off = scm_to_double (scm_apply_3 (pure_off, self_scm (),
299 scm_from_int (start), scm_from_int (end),
300 SCM_EOL));
301 delete dim_cache_[Y_AXIS].offset_;
302 dim_cache_[Y_AXIS].offset_ = 0;
305 /* we simulate positioning-done if we are the child of a VerticalAlignment,
306 but only if we don't have a cached offset. If we do have a cached offset,
307 it probably means that the Alignment was fixed and it has already been
308 calculated.
310 Grob *p = get_parent (Y_AXIS);
311 Real trans = 0;
312 if (Align_interface::has_interface (p) && !dim_cache_[Y_AXIS].offset_)
313 trans = Align_interface::get_pure_child_y_translation (p, this, start, end);
315 return off + trans
316 + dim_cache_[Y_AXIS].parent_->pure_relative_y_coordinate (refp, start, end);
319 /* Invoke callbacks to get offset relative to parent. */
320 Real
321 Grob::get_offset (Axis a) const
323 if (dim_cache_[a].offset_)
324 return *dim_cache_[a].offset_;
326 Grob *me = (Grob *) this;
328 SCM sym = axis_offset_symbol (a);
329 me->dim_cache_[a].offset_ = new Real (0.0);
332 UGH: can't fold next 2 statements together. Apparently GCC thinks
333 dim_cache_[a].offset_ is unaliased.
335 Real off = robust_scm2double (internal_get_property (sym), 0.0);
336 if (me->dim_cache_[a].offset_)
338 *me->dim_cache_[a].offset_ += off;
339 me->del_property (sym);
340 return *me->dim_cache_[a].offset_;
342 else
343 return 0.0;
346 Real
347 Grob::maybe_pure_coordinate (Grob const *refp, Axis a, bool pure, int start, int end)
349 if (pure && a != Y_AXIS)
350 programming_error ("tried to get pure X-offset");
351 return (pure && a == Y_AXIS) ? pure_relative_y_coordinate (refp, start, end)
352 : relative_coordinate (refp, a);
355 /****************************************************************
356 extents
357 ****************************************************************/
359 void
360 Grob::flush_extent_cache (Axis axis)
362 if (dim_cache_[axis].extent_)
365 Ugh, this is not accurate; will flush property, causing
366 callback to be called if.
368 del_property ((axis == X_AXIS) ? ly_symbol2scm ("X-extent") : ly_symbol2scm ("Y-extent"));
369 delete dim_cache_[axis].extent_;
370 dim_cache_[axis].extent_ = 0;
371 if (get_parent (axis))
372 get_parent (axis)->flush_extent_cache (axis);
377 Interval
378 Grob::extent (Grob *refp, Axis a) const
380 Real offset = relative_coordinate (refp, a);
381 Interval real_ext;
382 if (dim_cache_[a].extent_)
384 real_ext = *dim_cache_[a].extent_;
386 else
389 Order is significant: ?-extent may trigger suicide.
391 SCM ext_sym =
392 (a == X_AXIS)
393 ? ly_symbol2scm ("X-extent")
394 : ly_symbol2scm ("Y-extent");
396 SCM ext = internal_get_property (ext_sym);
397 if (is_number_pair (ext))
398 real_ext.unite (ly_scm2interval (ext));
400 SCM min_ext_sym =
401 (a == X_AXIS)
402 ? ly_symbol2scm ("minimum-X-extent")
403 : ly_symbol2scm ("minimum-Y-extent");
404 SCM min_ext = internal_get_property (min_ext_sym);
405 if (is_number_pair (min_ext))
406 real_ext.unite (ly_scm2interval (min_ext));
407 ((Grob*)this)->dim_cache_[a].extent_ = new Interval (real_ext);
410 real_ext.translate (offset);
412 return real_ext;
415 Interval
416 Grob::pure_height (Grob *refp, int start, int end)
418 SCM pure_height = ly_lily_module_constant ("pure-Y-extent");
419 Interval iv (0, 0);
421 if (ly_is_procedure (pure_height))
422 iv = ly_scm2interval (scm_apply_3 (pure_height, self_scm (),
423 scm_from_int (start), scm_from_int (end),
424 SCM_EOL));
425 Real offset = pure_relative_y_coordinate (refp, start, end);
427 SCM min_ext = get_property ("minimum-Y-extent");
429 /* we don't add minimum-Y-extent if the extent is empty. This solves
430 a problem with Hara-kiri spanners. They would request_suicide and
431 return empty extents, but we would force them here to be large. */
432 if (!iv.is_empty () && is_number_pair (min_ext))
433 iv.unite (ly_scm2interval (min_ext));
435 iv.translate (offset);
436 return iv;
439 Interval
440 Grob::maybe_pure_extent (Grob *refp, Axis a, bool pure, int start, int end)
442 if (pure && a != Y_AXIS)
443 programming_error ("tried to get pure width");
444 return (pure && a == Y_AXIS) ? pure_height (refp, start, end) : extent (refp, a);
447 Interval_t<int>
448 Grob::spanned_rank_iv ()
450 return Interval_t<int> (-1, 0);
453 /****************************************************************
454 REFPOINTS
455 ****************************************************************/
457 /* Find the group-element which has both #this# and #s# */
458 Grob *
459 Grob::common_refpoint (Grob const *s, Axis a) const
461 /* I don't like the quadratic aspect of this code, but I see no
462 other way. The largest chain of parents might be 10 high or so,
463 so it shouldn't be a real issue. */
464 for (Grob const *c = this; c; c = c->dim_cache_[a].parent_)
465 for (Grob const *d = s; d; d = d->dim_cache_[a].parent_)
466 if (d == c)
467 return (Grob *) d;
469 return 0;
472 void
473 Grob::set_parent (Grob *g, Axis a)
475 dim_cache_[a].parent_ = g;
478 Grob *
479 Grob::get_parent (Axis a) const
481 return dim_cache_[a].parent_;
485 void
486 Grob::fixup_refpoint ()
488 for (int a = X_AXIS; a < NO_AXES; a++)
490 Axis ax = (Axis)a;
491 Grob *parent = get_parent (ax);
493 if (!parent)
494 continue;
496 if (parent->get_system () != get_system () && get_system ())
498 Grob *newparent = parent->find_broken_piece (get_system ());
499 set_parent (newparent, ax);
502 if (Item *i = dynamic_cast<Item *> (this))
504 Item *parenti = dynamic_cast<Item *> (parent);
506 if (parenti && i)
508 Direction my_dir = i->break_status_dir ();
509 if (my_dir != parenti->break_status_dir ())
511 Item *newparent = parenti->find_prebroken_piece (my_dir);
512 set_parent (newparent, ax);
520 /****************************************************************
521 MESSAGES
522 ****************************************************************/
523 void
524 Grob::warning (string s) const
526 SCM cause = self_scm ();
527 while (Grob *g = unsmob_grob (cause))
528 cause = g->get_property ("cause");
530 /* ES TODO: cause can't be Music*/
531 if (Music *m = unsmob_music (cause))
532 m->origin ()->warning (s);
533 else if (Stream_event *ev = unsmob_stream_event (cause))
534 ev->origin ()->warning (s);
535 else
536 ::warning (s);
540 string
541 Grob::name () const
543 SCM meta = get_property ("meta");
544 SCM nm = scm_assq (ly_symbol2scm ("name"), meta);
545 nm = (scm_is_pair (nm)) ? scm_cdr (nm) : SCM_EOL;
546 return scm_is_symbol (nm) ? ly_symbol2string (nm) : this->class_name ();
549 void
550 Grob::programming_error (string s) const
552 SCM cause = self_scm ();
553 while (Grob *g = unsmob_grob (cause))
554 cause = g->get_property ("cause");
556 s = _f ("programming error: %s", s);
558 /* ES TODO: cause can't be Music*/
559 if (Music *m = unsmob_music (cause))
560 m->origin ()->message (s);
561 else if (Stream_event *ev = unsmob_stream_event (cause))
562 ev->origin ()->warning (s);
563 else
564 ::message (s);
568 ADD_INTERFACE (Grob, "grob-interface",
569 "A grob represents a piece of music notation\n"
570 "\n"
571 "All grobs have an X and Y-position on the page. These X and Y positions\n"
572 "are stored in a relative format, so they can easily be combined by\n"
573 "stacking them, hanging one grob to the side of another, and coupling\n"
574 "them into a grouping objects.\n"
575 "\n"
576 "Each grob has a reference point (a.k.a. parent): the position of a grob\n"
577 "is stored relative to that reference point. For example the X-reference\n"
578 "point of a staccato dot usually is the note head that it applies\n"
579 "to. When the note head is moved, the staccato dot moves along\n"
580 "automatically.\n"
581 "\n"
582 "A grob is often associated with a symbol, but some grobs do not print\n"
583 "any symbols. They take care of grouping objects. For example, there is a\n"
584 "separate grob that stacks staves vertically. The @ref{NoteCollision}\n"
585 "is also an abstract grob: it only moves around chords, but doesn't print\n"
586 "anything.\n"
587 "\n"
588 "Grobs have a properties: Scheme variables, that can be read and set. "
589 "They have two types. Immutable variables "
590 "define the default style and behavior. They are shared between many objects. "
591 "They can be changed using @code{\\override} and @code{\\revert}. "
592 "\n\n"
593 "Mutable properties are variables that are specific to one grob. Typically, "
594 "lists of other objects, or results from computations are stored in"
595 "mutable properties: every call to set-grob-property (or its C++ equivalent) "
596 "sets a mutable property. "
597 "\n\n"
598 "The properties @code{after-line-breaking} and @code{before-line-breaking} "
599 "are dummies that are not user-serviceable. "
603 /* properties */
604 "X-extent "
605 "X-offset "
606 "Y-extent "
607 "Y-offset "
608 "after-line-breaking "
609 "axis-group-parent-X "
610 "axis-group-parent-Y "
611 "before-line-breaking "
612 "cause "
613 "color "
614 "extra-X-extent "
615 "extra-Y-extent "
616 "extra-offset "
617 "interfaces "
618 "layer "
619 "meta "
620 "minimum-X-extent "
621 "minimum-Y-extent "
622 "rotation "
623 "springs-and-rods "
624 "staff-symbol "
625 "stencil "
626 "transparent "
633 /****************************************************************
634 CALLBACKS
635 ****************************************************************/
638 static SCM
639 grob_stencil_extent (Grob *me, Axis a)
641 Stencil *m = me->get_stencil ();
642 Interval e;
643 if (m)
644 e = m->extent (a);
645 return ly_interval2scm (e);
649 MAKE_SCHEME_CALLBACK (Grob, stencil_height, 1);
651 Grob::stencil_height (SCM smob)
653 Grob *me = unsmob_grob (smob);
654 return grob_stencil_extent (me, Y_AXIS);
657 MAKE_SCHEME_CALLBACK(Grob, y_parent_positioning, 1);
659 Grob::y_parent_positioning (SCM smob)
661 Grob *me = unsmob_grob (smob);
662 Grob *par = me->get_parent (Y_AXIS);
663 if (par)
664 (void) par->get_property ("positioning-done");
666 return scm_from_double (0.0);
670 MAKE_SCHEME_CALLBACK(Grob, x_parent_positioning, 1);
672 Grob::x_parent_positioning (SCM smob)
674 Grob *me = unsmob_grob (smob);
676 Grob *par = me->get_parent (X_AXIS);
677 if (par)
678 (void) par->get_property ("positioning-done");
680 return scm_from_double (0.0);
683 MAKE_SCHEME_CALLBACK (Grob, stencil_width, 1);
685 Grob::stencil_width (SCM smob)
687 Grob *me = unsmob_grob (smob);
688 return grob_stencil_extent (me, X_AXIS);
693 Grob *
694 common_refpoint_of_list (SCM elist, Grob *common, Axis a)
696 for (; scm_is_pair (elist); elist = scm_cdr (elist))
697 if (Grob *s = unsmob_grob (scm_car (elist)))
699 if (common)
700 common = common->common_refpoint (s, a);
701 else
702 common = s;
705 return common;
708 Grob *
709 common_refpoint_of_array (vector<Grob*> const &arr, Grob *common, Axis a)
711 for (vsize i = arr.size (); i--;)
712 if (Grob *s = arr[i])
714 if (common)
715 common = common->common_refpoint (s, a);
716 else
717 common = s;
720 return common;
723 Interval
724 robust_relative_extent (Grob *me, Grob *refpoint, Axis a)
726 Interval ext = me->extent (refpoint, a);
727 if (ext.is_empty ())
728 ext.add_point (me->relative_coordinate (refpoint, a));
730 return ext;