]> git.donarmstrong.com Git - lilypond.git/blob - lily/grob.cc
Fix some bugs in the dynamic engraver and PostScript backend
[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--2006 Han-Wen Nienhuys <hanwen@xs4all.nl>
7 */
8
9 #include "grob.hh"
10
11 #include <cstring>
12
13 #include "input-smob.hh"
14 #include "international.hh"
15 #include "item.hh"
16 #include "main.hh"
17 #include "misc.hh"
18 #include "music.hh"
19 #include "output-def.hh"
20 #include "pointer-group-interface.hh"
21 #include "stencil.hh"
22 #include "system.hh"
23 #include "warn.hh"
24
25 #include "ly-smobs.icc"
26
27 Grob *
28 Grob::clone (int count) const
29 {
30   return new Grob (*this, count);
31 }
32
33 Grob::Grob (SCM basicprops,
34             Object_key const *key)
35 {
36   key_ = key;
37   /* FIXME: default should be no callback.  */
38   self_scm_ = SCM_EOL;
39   layout_ = 0;
40   original_ = 0;
41   interfaces_ = SCM_EOL;
42   immutable_property_alist_ = basicprops;
43   mutable_property_alist_ = SCM_EOL;
44   object_alist_ = SCM_EOL;
45   
46   /* We do smobify_self () as the first step.  Since the object lives
47      on the heap, none of its SCM variables are protected from
48      GC. After smobify_self (), they are.  */
49   smobify_self ();
50
51   /*
52     We always get a new key object for a new grob.
53   */
54   if (key_)
55     ((Object_key *)key_)->unprotect ();
56
57   SCM meta = get_property ("meta");
58   if (scm_is_pair (meta))
59     interfaces_ = scm_cdr (scm_assq (ly_symbol2scm ("interfaces"), meta));
60   
61   if (get_property_data (ly_symbol2scm ("X-extent")) == SCM_EOL)
62     set_property ("X-extent", Grob::stencil_width_proc);
63   if (get_property_data (ly_symbol2scm ("Y-extent")) == SCM_EOL)
64     set_property ("Y-extent", Grob::stencil_height_proc);
65 }
66
67 Grob::Grob (Grob const &s, int copy_index)
68   : dim_cache_ (s.dim_cache_)
69 {
70   key_ = (use_object_keys) ? new Copied_key (s.key_, copy_index) : 0;
71   original_ = (Grob *) & s;
72   self_scm_ = SCM_EOL;
73
74   immutable_property_alist_ = s.immutable_property_alist_;
75   mutable_property_alist_ = ly_deep_copy (s.mutable_property_alist_);
76   interfaces_ = s.interfaces_;
77   object_alist_ = SCM_EOL;
78
79   layout_ = 0;
80
81   smobify_self ();
82   if (key_)
83     ((Object_key *)key_)->unprotect ();
84 }
85
86 Grob::~Grob ()
87 {
88 }
89 /****************************************************************
90   STENCILS
91 ****************************************************************/
92
93 Stencil *
94 Grob::get_stencil () const
95 {
96   if (!is_live ())
97     return 0;
98
99   SCM stil = get_property ("stencil");
100   return unsmob_stencil (stil);
101 }
102
103 Stencil
104 Grob::get_print_stencil () const
105 {
106   SCM stil = get_property ("stencil");
107
108   Stencil retval;
109   if (Stencil *m = unsmob_stencil (stil))
110     {
111       retval = *m;
112       if (to_boolean (get_property ("transparent")))
113         retval = Stencil (m->extent_box (), SCM_EOL);
114       else
115         {
116           SCM expr = m->expr ();
117           expr = scm_list_3 (ly_symbol2scm ("grob-cause"),
118                              self_scm (), expr);
119
120           retval = Stencil (m->extent_box (), expr);
121         }
122       SCM rot = get_property ("rotation");
123       if (scm_is_pair (rot))
124         {
125           Real angle = scm_to_double (scm_car (rot));
126           Real x = scm_to_double (scm_cadr (rot));
127           Real y = scm_to_double (scm_caddr (rot));
128
129           retval.rotate (angle, Offset (x, y));
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   if (me->dim_cache_[a].offset_)
299     {
300       *me->dim_cache_[a].offset_ += off;
301       me->del_property (sym);
302       return *me->dim_cache_[a].offset_;
303     }
304   else
305     return 0.0;
306 }
307
308
309 /****************************************************************
310   extents
311 ****************************************************************/
312
313 void
314 Grob::flush_extent_cache (Axis axis)
315 {
316   if (dim_cache_[axis].extent_)
317     {
318       /*
319         Ugh, this is not accurate; will flush property, causing
320         callback to be called if.
321        */
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);
327     }
328 }
329
330
331 Interval
332 Grob::extent (Grob *refp, Axis a) const
333 {
334   Real offset = relative_coordinate (refp, a);
335   Interval real_ext;
336   if (dim_cache_[a].extent_)
337     {
338       real_ext = *dim_cache_[a].extent_;
339     }
340   else
341     {
342       /*
343         Order is significant: ?-extent may trigger suicide.
344        */
345       SCM ext_sym =
346         (a == X_AXIS)
347         ? ly_symbol2scm ("X-extent")
348         : ly_symbol2scm ("Y-extent");
349
350       SCM ext = internal_get_property (ext_sym);
351       if (is_number_pair (ext))
352         real_ext.unite (ly_scm2interval (ext));
353
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);  
363     }
364   
365   real_ext.translate (offset);
366   
367   return real_ext;
368 }
369
370 /****************************************************************
371   REFPOINTS
372 ****************************************************************/
373
374 /* Find the group-element which has both #this# and #s#  */
375 Grob *
376 Grob::common_refpoint (Grob const *s, Axis a) const
377 {
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;
385
386   return 0;
387 }
388
389 void
390 Grob::set_parent (Grob *g, Axis a)
391 {
392   dim_cache_[a].parent_ = g;
393 }
394
395 Grob *
396 Grob::get_parent (Axis a) const
397 {
398   return dim_cache_[a].parent_;
399 }
400
401
402 void
403 Grob::fixup_refpoint ()
404 {
405   for (int a = X_AXIS; a < NO_AXES; a++)
406     {
407       Axis ax = (Axis)a;
408       Grob *parent = get_parent (ax);
409
410       if (!parent)
411         continue;
412
413       if (parent->get_system () != get_system () && get_system ())
414         {
415           Grob *newparent = parent->find_broken_piece (get_system ());
416           set_parent (newparent, ax);
417         }
418
419       if (Item *i = dynamic_cast<Item *> (this))
420         {
421           Item *parenti = dynamic_cast<Item *> (parent);
422
423           if (parenti && i)
424             {
425               Direction my_dir = i->break_status_dir ();
426               if (my_dir != parenti->break_status_dir ())
427                 {
428                   Item *newparent = parenti->find_prebroken_piece (my_dir);
429                   set_parent (newparent, ax);
430                 }
431             }
432         }
433     }
434 }
435
436
437 /****************************************************************
438   MESSAGES
439 ****************************************************************/
440 void
441 Grob::warning (string s) const
442 {
443   SCM cause = self_scm ();
444   while (Grob *g = unsmob_grob (cause))
445     cause = g->get_property ("cause");
446
447   if (Music *m = unsmob_music (cause))
448     m->origin ()->warning (s);
449   else
450     ::warning (s);
451 }
452
453
454 string
455 Grob::name () const
456 {
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 ();
461 }
462
463 void
464 Grob::programming_error (string s) const
465 {
466   SCM cause = self_scm ();
467   while (Grob *g = unsmob_grob (cause))
468     cause = g->get_property ("cause");
469
470   s = _f ("programming error: %s", s);
471
472   if (Music *m = unsmob_music (cause))
473     m->origin ()->message (s);
474   else
475     ::message (s);
476 }
477
478
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. "
511
512                ,
513
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                "rotation "
534                "springs-and-rods "
535                "staff-symbol "
536                "stencil "
537                "transparent "
538                );
539
540
541
542
543
544 /****************************************************************
545   CALLBACKS
546 ****************************************************************/
547
548
549 static SCM
550 grob_stencil_extent (Grob *me, Axis a)
551 {
552   Stencil *m = me->get_stencil ();
553   Interval e;
554   if (m)
555     e = m->extent (a);
556   return ly_interval2scm (e);
557 }
558
559
560 MAKE_SCHEME_CALLBACK (Grob, stencil_height, 1);
561 SCM
562 Grob::stencil_height (SCM smob)
563 {
564   Grob *me = unsmob_grob (smob);
565   return grob_stencil_extent (me, Y_AXIS);
566 }
567
568 MAKE_SCHEME_CALLBACK(Grob, y_parent_positioning, 1);
569 SCM
570 Grob::y_parent_positioning (SCM smob)
571 {
572   Grob *me = unsmob_grob (smob);
573   Grob *par = me->get_parent (Y_AXIS);
574   if (par)
575     (void) par->get_property ("positioning-done");
576
577   return scm_from_double (0.0);
578 }
579
580
581 MAKE_SCHEME_CALLBACK(Grob, x_parent_positioning, 1);
582 SCM
583 Grob::x_parent_positioning (SCM smob)
584 {
585   Grob *me = unsmob_grob (smob);
586   
587   Grob *par = me->get_parent (X_AXIS);
588   if (par)
589     (void) par->get_property ("positioning-done");
590
591   return scm_from_double (0.0);
592 }
593
594 MAKE_SCHEME_CALLBACK (Grob, stencil_width, 1);
595 SCM
596 Grob::stencil_width (SCM smob)
597 {
598   Grob *me = unsmob_grob (smob);
599   return grob_stencil_extent (me, X_AXIS);
600 }
601
602
603
604 Grob *
605 common_refpoint_of_list (SCM elist, Grob *common, Axis a)
606 {
607   for (; scm_is_pair (elist); elist = scm_cdr (elist))
608     if (Grob *s = unsmob_grob (scm_car (elist)))
609       {
610         if (common)
611           common = common->common_refpoint (s, a);
612         else
613           common = s;
614       }
615
616   return common;
617 }
618
619 Grob *
620 common_refpoint_of_array (vector<Grob*> const &arr, Grob *common, Axis a)
621 {
622   for (vsize i = arr.size (); i--;)
623     if (Grob *s = arr[i])
624       {
625         if (common)
626           common = common->common_refpoint (s, a);
627         else
628           common = s;
629       }
630
631   return common;
632 }
633
634 Interval
635 robust_relative_extent (Grob *me, Grob *refpoint, Axis a)
636 {
637   Interval ext = me->extent (refpoint, a);
638   if (ext.is_empty ())
639     ext.add_point (me->relative_coordinate (refpoint, a));
640
641   return ext;
642 }
643