]> git.donarmstrong.com Git - lilypond.git/blob - lily/grob.cc
Merge branch 'master' into jneeman
[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--2007 Han-Wen Nienhuys <hanwen@xs4all.nl>
7 */
8
9 #include "grob.hh"
10
11 #include <cstring>
12
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"
26
27 #include "ly-smobs.icc"
28
29 Grob *
30 Grob::clone () const
31 {
32   return new Grob (*this);
33 }
34
35 Grob::Grob (SCM basicprops)         
36 {
37   
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;
46   
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 ();
51
52   SCM meta = get_property ("meta");
53   if (scm_is_pair (meta))
54     {
55       interfaces_ = scm_cdr (scm_assq (ly_symbol2scm ("interfaces"), meta));
56
57       SCM object_cbs = scm_assq (ly_symbol2scm ("object-callbacks"), meta);
58       if (scm_is_pair (object_cbs))
59         {
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)); 
62         }
63     }
64   
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);
69 }
70
71 Grob::Grob (Grob const &s)
72   : dim_cache_ (s.dim_cache_)
73 {
74   original_ = (Grob *) & s;
75   self_scm_ = SCM_EOL;
76
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;
81
82   layout_ = 0;
83
84   smobify_self ();
85 }
86
87 Grob::~Grob ()
88 {
89 }
90 /****************************************************************
91   STENCILS
92 ****************************************************************/
93
94 Stencil *
95 Grob::get_stencil () const
96 {
97   if (!is_live ())
98     return 0;
99
100   SCM stil = get_property ("stencil");
101   return unsmob_stencil (stil);
102 }
103
104 Stencil
105 Grob::get_print_stencil () const
106 {
107   SCM stil = get_property ("stencil");
108
109   Stencil retval;
110   if (Stencil *m = unsmob_stencil (stil))
111     {
112       retval = *m;
113       if (to_boolean (get_property ("transparent")))
114         retval = Stencil (m->extent_box (), SCM_EOL);
115       else
116         {
117           SCM expr = m->expr ();
118           expr = scm_list_3 (ly_symbol2scm ("grob-cause"),
119                              self_scm (), expr);
120
121           retval = Stencil (m->extent_box (), expr);
122         }
123
124       SCM rot = get_property ("rotation");
125       if (scm_is_pair (rot))
126         {
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));
130
131           retval.rotate_degrees (angle, Offset (x, y));
132         }
133
134       /* color support... see interpret_stencil_expression () for more... */
135       SCM color = get_property ("color");
136       if (scm_is_pair (color))
137         {
138           SCM expr = scm_list_3 (ly_symbol2scm ("color"),
139                                  color,
140                                  retval.expr ());
141
142           retval = Stencil (retval.extent_box (), expr);
143         }
144
145     }
146
147   return retval;
148 }
149
150 /****************************************************************
151   VIRTUAL STUBS
152 ****************************************************************/
153 void
154 Grob::do_break_processing ()
155 {
156 }
157
158 void
159 Grob::discretionary_processing ()
160 {
161 }
162
163 System *
164 Grob::get_system () const
165 {
166   return 0;
167 }
168
169
170 void
171 Grob::handle_broken_dependencies ()
172 {
173   Spanner *sp = dynamic_cast<Spanner *> (this);
174   if (original () && sp)
175     return;
176
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  */
181     {
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));
184     }
185   System *system = get_system ();
186
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.
197
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 ();
202 }
203
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}
207 */
208 void
209 Grob::suicide ()
210 {
211   if (!is_live ())
212     return;
213
214   for (int a = X_AXIS; a < NO_AXES; a++)
215     dim_cache_[a].clear ();
216
217   mutable_property_alist_ = SCM_EOL;
218   object_alist_ = SCM_EOL;
219   immutable_property_alist_ = SCM_EOL;
220   interfaces_ = SCM_EOL;
221 }
222
223 void
224 Grob::handle_prebroken_dependencies ()
225 {
226   /* Don't do this in the derived method, since we want to keep access to
227      object_alist_ centralized.  */
228   if (original ())
229     {
230       Item *it = dynamic_cast<Item *> (this);
231       substitute_object_links (scm_from_int (it->break_status_dir ()),
232                                original ()->object_alist_);
233     }
234 }
235
236 Grob *
237 Grob::find_broken_piece (System *) const
238 {
239   return 0;
240 }
241
242 /****************************************************************
243    OFFSETS
244 ****************************************************************/
245
246 void
247 Grob::translate_axis (Real y, Axis a)
248 {
249   if (isinf (y) || isnan (y))
250     {
251       programming_error (_ ("Infinity or NaN encountered"));
252       return ;
253     }
254   
255   if (!dim_cache_[a].offset_)
256     dim_cache_[a].offset_ = new Real (y);
257   else
258     *dim_cache_[a].offset_ += y;  
259 }
260
261 /* Find the offset relative to D.  If D equals THIS, then it is 0.
262    Otherwise, it recursively defd as
263
264    OFFSET_ + PARENT_L_->relative_coordinate (D) */
265 Real
266 Grob::relative_coordinate (Grob const *refp, Axis a) const
267 {
268   if (refp == this)
269     return 0.0;
270
271   /* We catch PARENT_L_ == nil case with this, but we crash if we did
272      not ask for the absolute coordinate (ie. REFP == nil.)  */
273   Real off = get_offset (a);
274   if (refp == dim_cache_[a].parent_)
275     return off;
276   
277   off += dim_cache_[a].parent_->relative_coordinate (refp, a);
278
279   return off;
280 }
281
282 Real
283 Grob::pure_relative_y_coordinate (Grob const *refp, int start, int end)
284 {
285   if (refp == this)
286     return 0.0;
287
288   Real off = 0;
289
290   if (dim_cache_[Y_AXIS].offset_)
291     {
292       if (to_boolean (get_property ("pure-Y-offset-in-progress")))
293         programming_error ("cyclic chain in pure-Y-offset callbacks");
294
295       off = *dim_cache_[Y_AXIS].offset_;
296     }
297   else
298     {
299       SCM proc = get_property_data ("Y-offset");
300
301       dim_cache_[Y_AXIS].offset_ = new Real (0.0);
302       set_property ("pure-Y-offset-in-progress", SCM_BOOL_T);
303       off = robust_scm2double (call_pure_function (proc,
304                                                    scm_list_1 (self_scm ()),
305                                                    start, end),
306                                0.0);
307       del_property ("pure-Y-offset-in-progress");
308       delete dim_cache_[Y_AXIS].offset_;
309       dim_cache_[Y_AXIS].offset_ = 0;
310     }
311
312   /* we simulate positioning-done if we are the child of a VerticalAlignment,
313      but only if we don't have a cached offset. If we do have a cached offset,
314      it probably means that the Alignment was fixed and it has already been
315      calculated.
316   */
317   if (Grob *p = get_parent (Y_AXIS))
318     {
319       Real trans = 0;
320       if (Align_interface::has_interface (p) && !dim_cache_[Y_AXIS].offset_)
321         trans = Align_interface::get_pure_child_y_translation (p, this, start, end);
322
323       return off + trans + p->pure_relative_y_coordinate (refp, start, end);
324     }
325   return off;
326 }
327
328 /* Invoke callbacks to get offset relative to parent.  */
329 Real
330 Grob::get_offset (Axis a) const
331 {
332   if (dim_cache_[a].offset_)
333     return *dim_cache_[a].offset_;
334
335   Grob *me = (Grob *) this;
336
337   SCM sym = axis_offset_symbol (a);
338   me->dim_cache_[a].offset_ = new Real (0.0);
339
340   /*
341     UGH: can't fold next 2 statements together. Apparently GCC thinks
342     dim_cache_[a].offset_ is unaliased.
343   */
344   Real off = robust_scm2double (internal_get_property (sym), 0.0);
345   if (me->dim_cache_[a].offset_)
346     {
347       *me->dim_cache_[a].offset_ += off;
348       me->del_property (sym);
349       return *me->dim_cache_[a].offset_;
350     }
351   else
352     return 0.0;
353 }
354
355 Real
356 Grob::maybe_pure_coordinate (Grob const *refp, Axis a, bool pure, int start, int end)
357 {
358   if (pure && a != Y_AXIS)
359     programming_error ("tried to get pure X-offset");
360   return (pure && a == Y_AXIS) ? pure_relative_y_coordinate (refp, start, end)
361     : relative_coordinate (refp, a);
362 }
363
364 /****************************************************************
365   extents
366 ****************************************************************/
367
368 void
369 Grob::flush_extent_cache (Axis axis)
370 {
371   if (dim_cache_[axis].extent_)
372     {
373       /*
374         Ugh, this is not accurate; will flush property, causing
375         callback to be called if.
376        */
377       del_property ((axis == X_AXIS) ? ly_symbol2scm ("X-extent") : ly_symbol2scm ("Y-extent"));
378       delete dim_cache_[axis].extent_;
379       dim_cache_[axis].extent_ = 0;
380       if (get_parent (axis))
381         get_parent (axis)->flush_extent_cache (axis);
382     }
383 }
384
385
386 Interval
387 Grob::extent (Grob *refp, Axis a) const
388 {
389   Real offset = relative_coordinate (refp, a);
390   Interval real_ext;
391   if (dim_cache_[a].extent_)
392     {
393       real_ext = *dim_cache_[a].extent_;
394     }
395   else
396     {
397       /*
398         Order is significant: ?-extent may trigger suicide.
399        */
400       SCM ext_sym =
401         (a == X_AXIS)
402         ? ly_symbol2scm ("X-extent")
403         : ly_symbol2scm ("Y-extent");
404         
405       SCM ext = internal_get_property (ext_sym);
406       if (is_number_pair (ext))
407         real_ext.unite (ly_scm2interval (ext));
408
409       SCM min_ext_sym =
410         (a == X_AXIS)
411         ? ly_symbol2scm ("minimum-X-extent")
412         : ly_symbol2scm ("minimum-Y-extent");
413       SCM min_ext = internal_get_property (min_ext_sym);
414       if (is_number_pair (min_ext))
415         real_ext.unite (ly_scm2interval (min_ext));
416
417       ((Grob*)this)->dim_cache_[a].extent_ = new Interval (real_ext);  
418     }
419   
420   real_ext.translate (offset);
421   
422   return real_ext;
423 }
424
425 Interval
426 Grob::pure_height (Grob *refp, int start, int end)
427 {
428   SCM proc = get_property_data (ly_symbol2scm ("Y-extent"));
429   SCM iv_scm = call_pure_function (proc,
430                                    scm_list_1 (self_scm ()),
431                                    start, end);
432   Interval iv = robust_scm2interval (iv_scm, Interval (0, 0));
433   Real offset = pure_relative_y_coordinate (refp, start, end);
434
435   SCM min_ext = get_property ("minimum-Y-extent");
436
437   /* we don't add minimum-Y-extent if the extent is empty. This solves
438      a problem with Hara-kiri spanners. They would request_suicide and
439      return empty extents, but we would force them here to be large. */
440   if (!iv.is_empty () && is_number_pair (min_ext))
441     iv.unite (ly_scm2interval (min_ext));
442
443   if (!iv.is_empty ())
444     iv.translate (offset);
445   return iv;
446 }
447
448 Interval
449 Grob::maybe_pure_extent (Grob *refp, Axis a, bool pure, int start, int end)
450 {
451   if (pure && a != Y_AXIS)
452     programming_error ("tried to get pure width");
453   return (pure && a == Y_AXIS) ? pure_height (refp, start, end) : extent (refp, a);
454 }
455
456 Interval_t<int>
457 Grob::spanned_rank_interval () const
458 {
459   return Interval_t<int> (-1, 0);
460 }
461
462 /****************************************************************
463   REFPOINTS
464 ****************************************************************/
465
466 /* Find the group-element which has both #this# and #s#  */
467 Grob *
468 Grob::common_refpoint (Grob const *s, Axis a) const
469 {
470   /* I don't like the quadratic aspect of this code, but I see no
471      other way.  The largest chain of parents might be 10 high or so,
472      so it shouldn't be a real issue.  */
473   for (Grob const *c = this; c; c = c->dim_cache_[a].parent_)
474     for (Grob const *d = s; d; d = d->dim_cache_[a].parent_)
475       if (d == c)
476         return (Grob *) d;
477
478   return 0;
479 }
480
481 void
482 Grob::set_parent (Grob *g, Axis a)
483 {
484   dim_cache_[a].parent_ = g;
485 }
486
487 Grob *
488 Grob::get_parent (Axis a) const
489 {
490   return dim_cache_[a].parent_;
491 }
492
493
494 void
495 Grob::fixup_refpoint ()
496 {
497   for (int a = X_AXIS; a < NO_AXES; a++)
498     {
499       Axis ax = (Axis)a;
500       Grob *parent = get_parent (ax);
501
502       if (!parent)
503         continue;
504
505       if (parent->get_system () != get_system () && get_system ())
506         {
507           Grob *newparent = parent->find_broken_piece (get_system ());
508           set_parent (newparent, ax);
509         }
510
511       if (Item *i = dynamic_cast<Item *> (this))
512         {
513           Item *parenti = dynamic_cast<Item *> (parent);
514
515           if (parenti && i)
516             {
517               Direction my_dir = i->break_status_dir ();
518               if (my_dir != parenti->break_status_dir ())
519                 {
520                   Item *newparent = parenti->find_prebroken_piece (my_dir);
521                   set_parent (newparent, ax);
522                 }
523             }
524         }
525     }
526 }
527
528
529 /****************************************************************
530   MESSAGES
531 ****************************************************************/
532 void
533 Grob::warning (string s) const
534 {
535   SCM cause = self_scm ();
536   while (Grob *g = unsmob_grob (cause))
537     cause = g->get_property ("cause");
538
539   /* ES TODO: cause can't be Music*/
540   if (Music *m = unsmob_music (cause))
541     m->origin ()->warning (s);
542   else if (Stream_event *ev = unsmob_stream_event (cause))
543     ev->origin ()->warning (s);
544   else
545     ::warning (s);
546 }
547
548
549 string
550 Grob::name () const
551 {
552   SCM meta = get_property ("meta");
553   SCM nm = scm_assq (ly_symbol2scm ("name"), meta);
554   nm = (scm_is_pair (nm)) ? scm_cdr (nm) : SCM_EOL;
555   return scm_is_symbol (nm) ? ly_symbol2string (nm) : this->class_name ();
556 }
557
558 void
559 Grob::programming_error (string s) const
560 {
561   SCM cause = self_scm ();
562   while (Grob *g = unsmob_grob (cause))
563     cause = g->get_property ("cause");
564
565   s = _f ("programming error: %s", s);
566
567   /* ES TODO: cause can't be Music*/
568   if (Music *m = unsmob_music (cause))
569     m->origin ()->message (s);
570   else if (Stream_event *ev = unsmob_stream_event (cause))
571     ev->origin ()->warning (s);
572   else
573     ::message (s);
574 }
575
576
577 ADD_INTERFACE (Grob,
578                "A grob represents a piece of music notation\n"
579                "\n"
580                "All grobs have an X and Y-position on the page.  These X and Y positions\n"
581                "are stored in a relative format, so they can easily be combined by\n"
582                "stacking them, hanging one grob to the side of another, and coupling\n"
583                "them into a grouping objects.\n"
584                "\n"
585                "Each grob has a reference point (a.k.a.  parent): the position of a grob\n"
586                "is stored relative to that reference point. For example the X-reference\n"
587                "point of a staccato dot usually is the note head that it applies\n"
588                "to. When the note head is moved, the staccato dot moves along\n"
589                "automatically.\n"
590                "\n"
591                "A grob is often associated with a symbol, but some grobs do not print\n"
592                "any symbols. They take care of grouping objects. For example, there is a\n"
593                "separate grob that stacks staves vertically. The @ref{NoteCollision}\n"
594                "is also an abstract grob: it only moves around chords, but doesn't print\n"
595                "anything.\n"
596                "\n"
597                "Grobs have a properties: Scheme variables, that can be read and set. "
598                "They have two types. Immutable variables "
599                "define the default style and behavior.  They are shared between  many objects. "
600                "They can be changed using @code{\\override} and @code{\\revert}. "
601                "\n\n"
602                "Mutable properties are variables that are specific to one grob. Typically, "
603                "lists of other objects, or results from computations are stored in"
604                "mutable properties: every call to set-grob-property (or its C++ equivalent) "
605                "sets a mutable property. "
606                "\n\n"
607                "The properties @code{after-line-breaking} and @code{before-line-breaking} "
608                "are dummies that are not user-serviceable. "
609
610                ,
611
612                /* properties */
613                "X-extent "
614                "X-offset "
615                "Y-extent "
616                "Y-offset "
617                "after-line-breaking "
618                "avoid-slur "
619                "axis-group-parent-X "
620                "axis-group-parent-Y "
621                "before-line-breaking "
622                "cause "
623                "color "
624                "cross-staff "
625                "extra-X-extent "
626                "extra-Y-extent "
627                "extra-offset "
628                "interfaces "
629                "layer "
630                "meta "
631                "minimum-X-extent "
632                "minimum-Y-extent "
633                "outside-staff-horizontal-padding "
634                "outside-staff-padding "
635                "outside-staff-priority "
636                "pure-Y-offset-in-progress "
637                "rotation "
638                "springs-and-rods "
639                "staff-symbol "
640                "stencil "
641                "transparent "
642                );
643
644 /****************************************************************
645   CALLBACKS
646 ****************************************************************/
647
648 static SCM
649 grob_stencil_extent (Grob *me, Axis a)
650 {
651   Stencil *m = me->get_stencil ();
652   Interval e;
653   if (m)
654     e = m->extent (a);
655   return ly_interval2scm (e);
656 }
657
658
659 MAKE_SCHEME_CALLBACK (Grob, stencil_height, 1);
660 SCM
661 Grob::stencil_height (SCM smob)
662 {
663   Grob *me = unsmob_grob (smob);
664   return grob_stencil_extent (me, Y_AXIS);
665 }
666
667 MAKE_SCHEME_CALLBACK (Grob, y_parent_positioning, 1);
668 SCM
669 Grob::y_parent_positioning (SCM smob)
670 {
671   Grob *me = unsmob_grob (smob);
672   Grob *par = me->get_parent (Y_AXIS);
673   if (par)
674     (void) par->get_property ("positioning-done");
675
676   return scm_from_double (0.0);
677 }
678
679
680 MAKE_SCHEME_CALLBACK (Grob, x_parent_positioning, 1);
681 SCM
682 Grob::x_parent_positioning (SCM smob)
683 {
684   Grob *me = unsmob_grob (smob);
685   
686   Grob *par = me->get_parent (X_AXIS);
687   if (par)
688     (void) par->get_property ("positioning-done");
689
690   return scm_from_double (0.0);
691 }
692
693 MAKE_SCHEME_CALLBACK (Grob, stencil_width, 1);
694 SCM
695 Grob::stencil_width (SCM smob)
696 {
697   Grob *me = unsmob_grob (smob);
698   return grob_stencil_extent (me, X_AXIS);
699 }
700
701
702
703 Grob *
704 common_refpoint_of_list (SCM elist, Grob *common, Axis a)
705 {
706   for (; scm_is_pair (elist); elist = scm_cdr (elist))
707     if (Grob *s = unsmob_grob (scm_car (elist)))
708       {
709         if (common)
710           common = common->common_refpoint (s, a);
711         else
712           common = s;
713       }
714
715   return common;
716 }
717
718 Grob *
719 common_refpoint_of_array (vector<Grob*> const &arr, Grob *common, Axis a)
720 {
721   for (vsize i = arr.size (); i--;)
722     if (Grob *s = arr[i])
723       {
724         if (common)
725           common = common->common_refpoint (s, a);
726         else
727           common = s;
728       }
729
730   return common;
731 }
732
733 Interval
734 robust_relative_extent (Grob *me, Grob *refpoint, Axis a)
735 {
736   Interval ext = me->extent (refpoint, a);
737   if (ext.is_empty ())
738     ext.add_point (me->relative_coordinate (refpoint, a));
739
740   return ext;
741 }
742