* The grand 2005-2006 replace.
[lilypond/patrick.git] / lily / grob.cc
blob402f9dfc87499f033c466f92005d4812cb2aa4ca
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 "main.hh"
14 #include "input-smob.hh"
15 #include "warn.hh"
16 #include "pointer-group-interface.hh"
17 #include "misc.hh"
18 #include "paper-score.hh"
19 #include "stencil.hh"
20 #include "warn.hh"
21 #include "system.hh"
22 #include "item.hh"
23 #include "stencil.hh"
24 #include "misc.hh"
25 #include "music.hh"
26 #include "item.hh"
27 #include "paper-score.hh"
28 #include "ly-smobs.icc"
29 #include "output-def.hh"
35 Grob *
36 Grob::clone (int count) const
38 return new Grob (*this, count);
41 Grob::Grob (SCM basicprops,
42 Object_key const *key)
44 key_ = key;
45 /* FIXME: default should be no callback. */
46 self_scm_ = SCM_EOL;
47 layout_ = 0;
48 original_ = 0;
49 interfaces_ = SCM_EOL;
50 immutable_property_alist_ = basicprops;
51 mutable_property_alist_ = SCM_EOL;
52 object_alist_ = SCM_EOL;
54 /* We do smobify_self () as the first step. Since the object lives
55 on the heap, none of its SCM variables are protected from
56 GC. After smobify_self (), they are. */
57 smobify_self ();
60 We always get a new key object for a new grob.
62 if (key_)
63 ((Object_key *)key_)->unprotect ();
65 SCM meta = get_property ("meta");
66 if (scm_is_pair (meta))
67 interfaces_ = scm_cdr (scm_assq (ly_symbol2scm ("interfaces"), meta));
69 if (get_property_data (ly_symbol2scm ("X-extent")) == SCM_EOL)
70 set_property ("X-extent", Grob::stencil_width_proc);
71 if (get_property_data (ly_symbol2scm ("Y-extent")) == SCM_EOL)
72 set_property ("Y-extent", Grob::stencil_height_proc);
75 Grob::Grob (Grob const &s, int copy_index)
76 : dim_cache_ (s.dim_cache_)
78 key_ = (use_object_keys) ? new Copied_key (s.key_, copy_index) : 0;
79 original_ = (Grob *) & s;
80 self_scm_ = SCM_EOL;
82 immutable_property_alist_ = s.immutable_property_alist_;
83 mutable_property_alist_ = ly_deep_copy (s.mutable_property_alist_);
84 interfaces_ = s.interfaces_;
85 object_alist_ = SCM_EOL;
87 layout_ = 0;
89 smobify_self ();
90 if (key_)
91 ((Object_key *)key_)->unprotect ();
94 Grob::~Grob ()
97 /****************************************************************
98 STENCILS
99 ****************************************************************/
101 Stencil *
102 Grob::get_stencil () const
104 if (!is_live ())
105 return 0;
107 SCM stil = get_property ("stencil");
108 return unsmob_stencil (stil);
111 Stencil
112 Grob::get_print_stencil () const
114 SCM stil = get_property ("stencil");
116 Stencil retval;
117 if (Stencil *m = unsmob_stencil (stil))
119 retval = *m;
120 if (to_boolean (get_property ("transparent")))
121 retval = Stencil (m->extent_box (), SCM_EOL);
122 else
124 SCM expr = m->expr ();
125 if (point_and_click_global)
126 expr = scm_list_3 (ly_symbol2scm ("grob-cause"),
127 self_scm (), expr);
129 retval = Stencil (m->extent_box (), expr);
132 /* color support... see interpret_stencil_expression () for more... */
133 SCM color = get_property ("color");
134 if (color != SCM_EOL)
136 m = unsmob_stencil (stil);
137 SCM expr = scm_list_3 (ly_symbol2scm ("color"),
138 color,
139 m->expr ());
141 retval = Stencil (m->extent_box (), expr);
146 return retval;
149 /****************************************************************
150 VIRTUAL STUBS
151 ****************************************************************/
152 void
153 Grob::do_break_processing ()
157 void
158 Grob::discretionary_processing ()
162 System *
163 Grob::get_system () const
165 return 0;
169 void
170 Grob::handle_broken_dependencies ()
172 Spanner *sp = dynamic_cast<Spanner *> (this);
173 if (original () && sp)
174 return;
176 if (sp)
177 /* THIS, SP is the original spanner. We use a special function
178 because some Spanners have enormously long lists in their
179 properties, and a special function fixes FOO */
181 for (SCM s = object_alist_; scm_is_pair (s); s = scm_cdr (s))
182 sp->substitute_one_mutable_property (scm_caar (s), scm_cdar (s));
184 System *system = get_system ();
186 if (is_live ()
187 && system
188 && common_refpoint (system, X_AXIS)
189 && common_refpoint (system, Y_AXIS))
190 substitute_object_links (system->self_scm (), object_alist_);
191 else if (dynamic_cast<System *> (this))
192 substitute_object_links (SCM_UNDEFINED, object_alist_);
193 else
194 /* THIS element is `invalid'; it has been removed from all
195 dependencies, so let's junk the element itself.
197 Do not do this for System, since that would remove references
198 to the originals of score-grobs, which get then GC'd (a bad
199 thing). */
200 suicide ();
203 /* Note that we still want references to this element to be
204 rearranged, and not silently thrown away, so we keep pointers like
205 {broken_into_{drul, array}, original}
207 void
208 Grob::suicide ()
210 if (!is_live ())
211 return;
213 for (int a = X_AXIS; a < NO_AXES; a++)
214 dim_cache_[a].clear ();
216 mutable_property_alist_ = SCM_EOL;
217 object_alist_ = SCM_EOL;
218 immutable_property_alist_ = SCM_EOL;
219 interfaces_ = SCM_EOL;
222 void
223 Grob::handle_prebroken_dependencies ()
225 /* Don't do this in the derived method, since we want to keep access to
226 object_alist_ centralized. */
227 if (original ())
229 Item *it = dynamic_cast<Item *> (this);
230 substitute_object_links (scm_from_int (it->break_status_dir ()),
231 original ()->object_alist_);
235 Grob *
236 Grob::find_broken_piece (System *) const
238 return 0;
241 /****************************************************************
242 OFFSETS
243 ****************************************************************/
245 void
246 Grob::translate_axis (Real y, Axis a)
248 if (isinf (y) || isnan (y))
250 programming_error (_ ("Infinity or NaN encountered"));
251 return ;
254 if (!dim_cache_[a].offset_)
255 dim_cache_[a].offset_ = new Real (y);
256 else
257 *dim_cache_[a].offset_ += y;
260 /* Find the offset relative to D. If D equals THIS, then it is 0.
261 Otherwise, it recursively defd as
263 OFFSET_ + PARENT_L_->relative_coordinate (D) */
264 Real
265 Grob::relative_coordinate (Grob const *refp, Axis a) const
267 if (refp == this)
268 return 0.0;
270 /* We catch PARENT_L_ == nil case with this, but we crash if we did
271 not ask for the absolute coordinate (ie. REFP == nil.) */
272 Real off = get_offset (a);
273 if (refp == dim_cache_[a].parent_)
274 return off;
276 off += dim_cache_[a].parent_->relative_coordinate (refp, a);
278 return off;
281 /* Invoke callbacks to get offset relative to parent. */
282 Real
283 Grob::get_offset (Axis a) const
285 if (dim_cache_[a].offset_)
286 return *dim_cache_[a].offset_;
288 Grob *me = (Grob *) this;
290 SCM sym = axis_offset_symbol (a);
291 me->dim_cache_[a].offset_ = new Real (0.0);
294 UGH: can't fold next 2 statements together. Apparently GCC thinks
295 dim_cache_[a].offset_ is unaliased.
297 Real off = robust_scm2double (internal_get_property (sym), 0.0);
298 if (me->dim_cache_[a].offset_)
300 *me->dim_cache_[a].offset_ += off;
301 me->del_property (sym);
302 return *me->dim_cache_[a].offset_;
304 else
305 return 0.0;
309 /****************************************************************
310 extents
311 ****************************************************************/
313 void
314 Grob::flush_extent_cache (Axis axis)
316 if (dim_cache_[axis].extent_)
319 Ugh, this is not accurate; will flush property, causing
320 callback to be called if.
322 del_property ((axis == X_AXIS) ? ly_symbol2scm ("X-extent") : ly_symbol2scm ("Y-extent"));
323 delete dim_cache_[axis].extent_;
324 dim_cache_[axis].extent_ = 0;
325 if (get_parent (axis))
326 get_parent (axis)->flush_extent_cache (axis);
331 Interval
332 Grob::extent (Grob *refp, Axis a) const
334 Real offset = relative_coordinate (refp, a);
335 Interval real_ext;
336 if (dim_cache_[a].extent_)
338 real_ext = *dim_cache_[a].extent_;
340 else
343 Order is significant: ?-extent may trigger suicide.
345 SCM ext_sym =
346 (a == X_AXIS)
347 ? ly_symbol2scm ("X-extent")
348 : ly_symbol2scm ("Y-extent");
350 SCM ext = internal_get_property (ext_sym);
351 if (is_number_pair (ext))
352 real_ext.unite (ly_scm2interval (ext));
354 SCM min_ext_sym =
355 (a == X_AXIS)
356 ? ly_symbol2scm ("minimum-X-extent")
357 : ly_symbol2scm ("minimum-Y-extent");
358 SCM min_ext = internal_get_property (min_ext_sym);
359 if (is_number_pair (min_ext))
360 real_ext.unite (ly_scm2interval (min_ext));
361 ((Grob*)this)->del_property (ext_sym);
362 ((Grob*)this)->dim_cache_[a].extent_ = new Interval (real_ext);
365 real_ext.translate (offset);
367 return real_ext;
370 /****************************************************************
371 REFPOINTS
372 ****************************************************************/
374 /* Find the group-element which has both #this# and #s# */
375 Grob *
376 Grob::common_refpoint (Grob const *s, Axis a) const
378 /* I don't like the quadratic aspect of this code, but I see no
379 other way. The largest chain of parents might be 10 high or so,
380 so it shouldn't be a real issue. */
381 for (Grob const *c = this; c; c = c->dim_cache_[a].parent_)
382 for (Grob const *d = s; d; d = d->dim_cache_[a].parent_)
383 if (d == c)
384 return (Grob *) d;
386 return 0;
389 void
390 Grob::set_parent (Grob *g, Axis a)
392 dim_cache_[a].parent_ = g;
395 Grob *
396 Grob::get_parent (Axis a) const
398 return dim_cache_[a].parent_;
402 void
403 Grob::fixup_refpoint ()
405 for (int a = X_AXIS; a < NO_AXES; a++)
407 Axis ax = (Axis)a;
408 Grob *parent = get_parent (ax);
410 if (!parent)
411 continue;
413 if (parent->get_system () != get_system () && get_system ())
415 Grob *newparent = parent->find_broken_piece (get_system ());
416 set_parent (newparent, ax);
419 if (Item *i = dynamic_cast<Item *> (this))
421 Item *parenti = dynamic_cast<Item *> (parent);
423 if (parenti && i)
425 Direction my_dir = i->break_status_dir ();
426 if (my_dir != parenti->break_status_dir ())
428 Item *newparent = parenti->find_prebroken_piece (my_dir);
429 set_parent (newparent, ax);
437 /****************************************************************
438 MESSAGES
439 ****************************************************************/
440 void
441 Grob::warning (String s) const
443 SCM cause = self_scm ();
444 while (Grob *g = unsmob_grob (cause))
445 cause = g->get_property ("cause");
447 if (Music *m = unsmob_music (cause))
448 m->origin ()->warning (s);
449 else
450 ::warning (s);
454 String
455 Grob::name () const
457 SCM meta = get_property ("meta");
458 SCM nm = scm_assq (ly_symbol2scm ("name"), meta);
459 nm = (scm_is_pair (nm)) ? scm_cdr (nm) : SCM_EOL;
460 return scm_is_symbol (nm) ? ly_symbol2string (nm) : this->class_name ();
463 void
464 Grob::programming_error (String s) const
466 SCM cause = self_scm ();
467 while (Grob *g = unsmob_grob (cause))
468 cause = g->get_property ("cause");
470 s = _f ("programming error: %s", s);
472 if (Music *m = unsmob_music (cause))
473 m->origin ()->message (s);
474 else
475 ::message (s);
479 ADD_INTERFACE (Grob, "grob-interface",
480 "A grob represents a piece of music notation\n"
481 "\n"
482 "All grobs have an X and Y-position on the page. These X and Y positions\n"
483 "are stored in a relative format, so they can easily be combined by\n"
484 "stacking them, hanging one grob to the side of another, and coupling\n"
485 "them into a grouping objects.\n"
486 "\n"
487 "Each grob has a reference point (a.k.a. parent): the position of a grob\n"
488 "is stored relative to that reference point. For example the X-reference\n"
489 "point of a staccato dot usually is the note head that it applies\n"
490 "to. When the note head is moved, the staccato dot moves along\n"
491 "automatically.\n"
492 "\n"
493 "A grob is often associated with a symbol, but some grobs do not print\n"
494 "any symbols. They take care of grouping objects. For example, there is a\n"
495 "separate grob that stacks staves vertically. The @ref{NoteCollision}\n"
496 "is also an abstract grob: it only moves around chords, but doesn't print\n"
497 "anything.\n"
498 "\n"
499 "Grobs have a properties: Scheme variables, that can be read and set. "
500 "They have two types. Immutable variables "
501 "define the default style and behavior. They are shared between many objects. "
502 "They can be changed using @code{\\override} and @code{\\revert}. "
503 "\n\n"
504 "Mutable properties are variables that are specific to one grob. Typically, "
505 "lists of other objects, or results from computations are stored in"
506 "mutable properties: every call to set-grob-property (or its C++ equivalent) "
507 "sets a mutable property. "
508 "\n\n"
509 "The properties @code{after-line-breaking} and @code{before-line-breaking} "
510 "are dummies that are not user-serviceable. "
514 /* properties */
515 "X-extent "
516 "X-offset "
517 "Y-extent "
518 "Y-offset "
519 "after-line-breaking "
520 "axis-group-parent-X "
521 "axis-group-parent-Y "
522 "before-line-breaking "
523 "cause "
524 "color "
525 "extra-X-extent "
526 "extra-Y-extent "
527 "extra-offset "
528 "interfaces "
529 "layer "
530 "meta "
531 "minimum-X-extent "
532 "minimum-Y-extent "
533 "springs-and-rods "
534 "staff-symbol "
535 "stencil "
536 "transparent "
543 /****************************************************************
544 CALLBACKS
545 ****************************************************************/
548 static SCM
549 grob_stencil_extent (Grob *me, Axis a)
551 Stencil *m = me->get_stencil ();
552 Interval e;
553 if (m)
554 e = m->extent (a);
555 return ly_interval2scm (e);
559 MAKE_SCHEME_CALLBACK (Grob, stencil_height, 1);
561 Grob::stencil_height (SCM smob)
563 Grob *me = unsmob_grob (smob);
564 return grob_stencil_extent (me, Y_AXIS);
567 MAKE_SCHEME_CALLBACK(Grob, y_parent_positioning, 1);
569 Grob::y_parent_positioning (SCM smob)
571 Grob *me = unsmob_grob (smob);
572 Grob *par = me->get_parent (Y_AXIS);
573 if (par)
574 (void) par->get_property ("positioning-done");
576 return scm_from_double (0.0);
580 MAKE_SCHEME_CALLBACK(Grob, x_parent_positioning, 1);
582 Grob::x_parent_positioning (SCM smob)
584 Grob *me = unsmob_grob (smob);
586 Grob *par = me->get_parent (X_AXIS);
587 if (par)
588 (void) par->get_property ("positioning-done");
590 return scm_from_double (0.0);
593 MAKE_SCHEME_CALLBACK (Grob, stencil_width, 1);
595 Grob::stencil_width (SCM smob)
597 Grob *me = unsmob_grob (smob);
598 return grob_stencil_extent (me, X_AXIS);
603 Grob *
604 common_refpoint_of_list (SCM elist, Grob *common, Axis a)
606 for (; scm_is_pair (elist); elist = scm_cdr (elist))
607 if (Grob *s = unsmob_grob (scm_car (elist)))
609 if (common)
610 common = common->common_refpoint (s, a);
611 else
612 common = s;
615 return common;
618 Grob *
619 common_refpoint_of_array (Link_array<Grob> const &arr, Grob *common, Axis a)
621 for (int i = arr.size (); i--;)
622 if (Grob *s = arr[i])
624 if (common)
625 common = common->common_refpoint (s, a);
626 else
627 common = s;
630 return common;
633 Interval
634 robust_relative_extent (Grob *me, Grob *refpoint, Axis a)
636 Interval ext = me->extent (refpoint, a);
637 if (ext.is_empty ())
638 ext.add_point (me->relative_coordinate (refpoint, a));
640 return ext;