]> git.donarmstrong.com Git - lilypond.git/blob - lily/grob.cc
* lily/grob.cc (suicide): clear dim_caches_.
[lilypond.git] / lily / grob.cc
1 /*
2   grob.cc -- implement Grob
3
4   source file of the GNU LilyPond music typesetter
5
6   (c) 1997--2005 Han-Wen Nienhuys <hanwen@xs4all.nl>
7 */
8
9 #include "grob.hh"
10
11 #include <cstring>
12
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"
30
31
32
33
34
35 Grob *
36 Grob::clone (int count) const
37 {
38   return new Grob (*this, count);
39 }
40
41 Grob::Grob (SCM basicprops,
42             Object_key const *key)
43 {
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;
53   
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 ();
58
59   /*
60     We always get a new key object for a new grob.
61   */
62   if (key_)
63     ((Object_key *)key_)->unprotect ();
64
65   SCM meta = get_property ("meta");
66   if (scm_is_pair (meta))
67     interfaces_ = scm_cdr (scm_assq (ly_symbol2scm ("interfaces"), meta));
68   
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);
73 }
74
75 Grob::Grob (Grob const &s, int copy_index)
76   : dim_cache_ (s.dim_cache_)
77 {
78   key_ = (use_object_keys) ? new Copied_key (s.key_, copy_index) : 0;
79   original_ = (Grob *) & s;
80   self_scm_ = SCM_EOL;
81
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;
86
87   layout_ = 0;
88
89   smobify_self ();
90   if (key_)
91     ((Object_key *)key_)->unprotect ();
92 }
93
94 Grob::~Grob ()
95 {
96 }
97 /****************************************************************
98   STENCILS
99 ****************************************************************/
100
101 Stencil *
102 Grob::get_stencil () const
103 {
104   if (!is_live ())
105     return 0;
106
107   SCM stil = get_property ("stencil");
108   return unsmob_stencil (stil);
109 }
110
111 Stencil
112 Grob::get_print_stencil () const
113 {
114   SCM stil = get_property ("stencil");
115
116   Stencil retval;
117   if (Stencil *m = unsmob_stencil (stil))
118     {
119       retval = *m;
120       if (to_boolean (get_property ("transparent")))
121         retval = Stencil (m->extent_box (), SCM_EOL);
122       else
123         {
124           SCM expr = m->expr ();
125           if (point_and_click_global)
126             expr = scm_list_3 (ly_symbol2scm ("grob-cause"),
127                                self_scm (), expr);
128
129           retval = Stencil (m->extent_box (), expr);
130         }
131
132       /* color support... see interpret_stencil_expression () for more... */
133       SCM color = get_property ("color");
134       if (color != SCM_EOL)
135         {
136           m = unsmob_stencil (stil);
137           SCM expr = scm_list_3 (ly_symbol2scm ("color"),
138                                  color,
139                                  m->expr ());
140
141           retval = Stencil (m->extent_box (), expr);
142         }
143
144     }
145
146   return retval;
147 }
148
149 /****************************************************************
150   VIRTUAL STUBS
151 ****************************************************************/
152 void
153 Grob::do_break_processing ()
154 {
155 }
156
157 void
158 Grob::discretionary_processing ()
159 {
160 }
161
162 System *
163 Grob::get_system () const
164 {
165   return 0;
166 }
167
168
169 void
170 Grob::handle_broken_dependencies ()
171 {
172   Spanner *sp = dynamic_cast<Spanner *> (this);
173   if (original () && sp)
174     return;
175
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  */
180     {
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));
183     }
184   System *system = get_system ();
185
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.
196
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 ();
201 }
202
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}
206 */
207 void
208 Grob::suicide ()
209 {
210   if (!is_live ())
211     return;
212
213   for (int a = X_AXIS; a < NO_AXES; a++)
214     dim_cache_[a].clear ();
215
216   mutable_property_alist_ = SCM_EOL;
217   object_alist_ = SCM_EOL;
218   immutable_property_alist_ = SCM_EOL;
219   interfaces_ = SCM_EOL;
220 }
221
222 void
223 Grob::handle_prebroken_dependencies ()
224 {
225   /* Don't do this in the derived method, since we want to keep access to
226      object_alist_ centralized.  */
227   if (original ())
228     {
229       Item *it = dynamic_cast<Item *> (this);
230       substitute_object_links (scm_from_int (it->break_status_dir ()),
231                                original ()->object_alist_);
232     }
233 }
234
235 Grob *
236 Grob::find_broken_piece (System *) const
237 {
238   return 0;
239 }
240
241 /****************************************************************
242    OFFSETS
243 ****************************************************************/
244
245 void
246 Grob::translate_axis (Real y, Axis a)
247 {
248   if (isinf (y) || isnan (y))
249     {
250       programming_error (_ ("Infinity or NaN encountered"));
251       return ;
252     }
253   
254   if (!dim_cache_[a].offset_)
255     dim_cache_[a].offset_ = new Real (y);
256   else
257     *dim_cache_[a].offset_ += y;  
258 }
259
260 /* Find the offset relative to D.  If D equals THIS, then it is 0.
261    Otherwise, it recursively defd as
262
263    OFFSET_ + PARENT_L_->relative_coordinate (D) */
264 Real
265 Grob::relative_coordinate (Grob const *refp, Axis a) const
266 {
267   if (refp == this)
268     return 0.0;
269
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;
275   
276   off += dim_cache_[a].parent_->relative_coordinate (refp, a);
277
278   return off;
279 }
280
281 /* Invoke callbacks to get offset relative to parent.  */
282 Real
283 Grob::get_offset (Axis a) const
284 {
285   if (dim_cache_[a].offset_)
286     return *dim_cache_[a].offset_;
287
288   Grob *me = (Grob *) this;
289
290   SCM sym = axis_offset_symbol (a);
291   me->dim_cache_[a].offset_ = new Real (0.0);
292
293   /*
294     UGH: can't fold next 2 statements together. Apparently GCC thinks
295     dim_cache_[a].offset_ is unaliased.
296   */
297   Real off = robust_scm2double (internal_get_property (sym), 0.0);
298   *me->dim_cache_[a].offset_ += off;
299
300   me->del_property (sym);
301   return *me->dim_cache_[a].offset_;
302 }
303
304
305 /****************************************************************
306   extents
307 ****************************************************************/
308
309 void
310 Grob::flush_extent_cache (Axis axis)
311 {
312   if (dim_cache_[axis].extent_)
313     {
314       /*
315         Ugh, this is not accurate; will flush property, causing
316         callback to be called if.
317        */
318       del_property ((axis == X_AXIS) ? ly_symbol2scm ("X-extent") : ly_symbol2scm ("Y-extent"));
319       delete dim_cache_[axis].extent_;
320       dim_cache_[axis].extent_ = 0;
321       if (get_parent (axis))
322         get_parent (axis)->flush_extent_cache (axis);
323     }
324 }
325
326
327 Interval
328 Grob::extent (Grob *refp, Axis a) const
329 {
330   Real offset = relative_coordinate (refp, a);
331   Interval real_ext;
332   if (dim_cache_[a].extent_)
333     {
334       real_ext = *dim_cache_[a].extent_;
335     }
336   else
337     {
338       /*
339         Order is significant: ?-extent may trigger suicide.
340        */
341       SCM ext_sym =
342         (a == X_AXIS)
343         ? ly_symbol2scm ("X-extent")
344         : ly_symbol2scm ("Y-extent");
345
346       SCM ext = internal_get_property (ext_sym);
347       if (is_number_pair (ext))
348         real_ext.unite (ly_scm2interval (ext));
349
350       SCM min_ext_sym =
351         (a == X_AXIS)
352         ? ly_symbol2scm ("minimum-X-extent")
353         : ly_symbol2scm ("minimum-Y-extent");
354       SCM min_ext = internal_get_property (min_ext_sym);
355       if (is_number_pair (min_ext))
356         real_ext.unite (ly_scm2interval (min_ext));
357       ((Grob*)this)->del_property (ext_sym);
358       ((Grob*)this)->dim_cache_[a].extent_ = new Interval (real_ext);  
359     }
360   
361   real_ext.translate (offset);
362   
363   return real_ext;
364 }
365
366 /****************************************************************
367   REFPOINTS
368 ****************************************************************/
369
370 /* Find the group-element which has both #this# and #s#  */
371 Grob *
372 Grob::common_refpoint (Grob const *s, Axis a) const
373 {
374   /* I don't like the quadratic aspect of this code, but I see no
375      other way.  The largest chain of parents might be 10 high or so,
376      so it shouldn't be a real issue.  */
377   for (Grob const *c = this; c; c = c->dim_cache_[a].parent_)
378     for (Grob const *d = s; d; d = d->dim_cache_[a].parent_)
379       if (d == c)
380         return (Grob *) d;
381
382   return 0;
383 }
384
385 void
386 Grob::set_parent (Grob *g, Axis a)
387 {
388   dim_cache_[a].parent_ = g;
389 }
390
391 Grob *
392 Grob::get_parent (Axis a) const
393 {
394   return dim_cache_[a].parent_;
395 }
396
397
398 void
399 Grob::fixup_refpoint ()
400 {
401   for (int a = X_AXIS; a < NO_AXES; a++)
402     {
403       Axis ax = (Axis)a;
404       Grob *parent = get_parent (ax);
405
406       if (!parent)
407         continue;
408
409       if (parent->get_system () != get_system () && get_system ())
410         {
411           Grob *newparent = parent->find_broken_piece (get_system ());
412           set_parent (newparent, ax);
413         }
414
415       if (Item *i = dynamic_cast<Item *> (this))
416         {
417           Item *parenti = dynamic_cast<Item *> (parent);
418
419           if (parenti && i)
420             {
421               Direction my_dir = i->break_status_dir ();
422               if (my_dir != parenti->break_status_dir ())
423                 {
424                   Item *newparent = parenti->find_prebroken_piece (my_dir);
425                   set_parent (newparent, ax);
426                 }
427             }
428         }
429     }
430 }
431
432
433 /****************************************************************
434   MESSAGES
435 ****************************************************************/
436 void
437 Grob::warning (String s) const
438 {
439   SCM cause = self_scm ();
440   while (Grob *g = unsmob_grob (cause))
441     cause = g->get_property ("cause");
442
443   if (Music *m = unsmob_music (cause))
444     m->origin ()->warning (s);
445   else
446     ::warning (s);
447 }
448
449
450 String
451 Grob::name () const
452 {
453   SCM meta = get_property ("meta");
454   SCM nm = scm_assq (ly_symbol2scm ("name"), meta);
455   nm = (scm_is_pair (nm)) ? scm_cdr (nm) : SCM_EOL;
456   return scm_is_symbol (nm) ? ly_symbol2string (nm) : this->class_name ();
457 }
458
459 void
460 Grob::programming_error (String s) const
461 {
462   SCM cause = self_scm ();
463   while (Grob *g = unsmob_grob (cause))
464     cause = g->get_property ("cause");
465
466   s = _f ("programming error: %s", s);
467
468   if (Music *m = unsmob_music (cause))
469     m->origin ()->message (s);
470   else
471     ::message (s);
472 }
473
474
475 ADD_INTERFACE (Grob, "grob-interface",
476                "A grob represents a piece of music notation\n"
477                "\n"
478                "All grobs have an X and Y-position on the page.  These X and Y positions\n"
479                "are stored in a relative format, so they can easily be combined by\n"
480                "stacking them, hanging one grob to the side of another, and coupling\n"
481                "them into a grouping objects.\n"
482                "\n"
483                "Each grob has a reference point (a.k.a.  parent): the position of a grob\n"
484                "is stored relative to that reference point. For example the X-reference\n"
485                "point of a staccato dot usually is the note head that it applies\n"
486                "to. When the note head is moved, the staccato dot moves along\n"
487                "automatically.\n"
488                "\n"
489                "A grob is often associated with a symbol, but some grobs do not print\n"
490                "any symbols. They take care of grouping objects. For example, there is a\n"
491                "separate grob that stacks staves vertically. The @ref{NoteCollision}\n"
492                "is also an abstract grob: it only moves around chords, but doesn't print\n"
493                "anything.\n"
494                "\n"
495                "Grobs have a properties: Scheme variables, that can be read and set. "
496                "They have two types. Immutable variables "
497                "define the default style and behavior.  They are shared between  many objects. "
498                "They can be changed using @code{\\override} and @code{\\revert}. "
499                "\n\n"
500                "Mutable properties are variables that are specific to one grob. Typically, "
501                "lists of other objects, or results from computations are stored in"
502                "mutable properties: every call to set-grob-property (or its C++ equivalent) "
503                "sets a mutable property. "
504                "\n\n"
505                "The properties @code{after-line-breaking} and @code{before-line-breaking} "
506                "are dummies that are not user-serviceable. "
507
508                ,
509
510                /* properties */
511                "X-extent "
512                "X-offset "
513                "Y-extent "
514                "Y-offset "
515                "after-line-breaking "
516                "axis-group-parent-X "
517                "axis-group-parent-Y "
518                "before-line-breaking "
519                "cause "
520                "color "
521                "extra-X-extent "
522                "extra-Y-extent "
523                "extra-offset "
524                "interfaces "
525                "layer "
526                "meta "
527                "minimum-X-extent "
528                "minimum-Y-extent "
529                "springs-and-rods "
530                "staff-symbol "
531                "stencil "
532                "transparent "
533                );
534
535
536
537
538
539 /****************************************************************
540   CALLBACKS
541 ****************************************************************/
542
543
544 static SCM
545 grob_stencil_extent (Grob *me, Axis a)
546 {
547   Stencil *m = me->get_stencil ();
548   Interval e;
549   if (m)
550     e = m->extent (a);
551   return ly_interval2scm (e);
552 }
553
554
555 MAKE_SCHEME_CALLBACK (Grob, stencil_height, 1);
556 SCM
557 Grob::stencil_height (SCM smob)
558 {
559   Grob *me = unsmob_grob (smob);
560   return grob_stencil_extent (me, Y_AXIS);
561 }
562
563 MAKE_SCHEME_CALLBACK(Grob, y_parent_positioning, 1);
564 SCM
565 Grob::y_parent_positioning (SCM smob)
566 {
567   Grob *me = unsmob_grob (smob);
568   Grob *par = me->get_parent (Y_AXIS);
569   if (par)
570     (void) par->get_property ("positioning-done");
571
572   return scm_from_double (0.0);
573 }
574
575
576 MAKE_SCHEME_CALLBACK(Grob, x_parent_positioning, 1);
577 SCM
578 Grob::x_parent_positioning (SCM smob)
579 {
580   Grob *me = unsmob_grob (smob);
581   
582   Grob *par = me->get_parent (X_AXIS);
583   if (par)
584     (void) par->get_property ("positioning-done");
585
586   return scm_from_double (0.0);
587 }
588
589 MAKE_SCHEME_CALLBACK (Grob, stencil_width, 1);
590 SCM
591 Grob::stencil_width (SCM smob)
592 {
593   Grob *me = unsmob_grob (smob);
594   return grob_stencil_extent (me, X_AXIS);
595 }
596
597
598
599 Grob *
600 common_refpoint_of_list (SCM elist, Grob *common, Axis a)
601 {
602   for (; scm_is_pair (elist); elist = scm_cdr (elist))
603     if (Grob *s = unsmob_grob (scm_car (elist)))
604       {
605         if (common)
606           common = common->common_refpoint (s, a);
607         else
608           common = s;
609       }
610
611   return common;
612 }
613
614 Grob *
615 common_refpoint_of_array (Link_array<Grob> const &arr, Grob *common, Axis a)
616 {
617   for (int i = arr.size (); i--;)
618     if (Grob *s = arr[i])
619       {
620         if (common)
621           common = common->common_refpoint (s, a);
622         else
623           common = s;
624       }
625
626   return common;
627 }
628
629 Interval
630 robust_relative_extent (Grob *me, Grob *refpoint, Axis a)
631 {
632   Interval ext = me->extent (refpoint, a);
633   if (ext.is_empty ())
634     ext.add_point (me->relative_coordinate (refpoint, a));
635
636   return ext;
637 }
638