]> git.donarmstrong.com Git - lilypond.git/blob - lily/grob.cc
Merge branch 'master' of git+ssh://jneem@git.sv.gnu.org/srv/git/lilypond
[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     off = *dim_cache_[Y_AXIS].offset_;
292   else
293     {
294       SCM proc = get_property_data ("Y-offset");
295
296       dim_cache_[Y_AXIS].offset_ = new Real (0.0);
297       off = robust_scm2double (call_pure_function (proc,
298                                                    scm_list_1 (self_scm ()),
299                                                    start, end),
300                                0.0);
301       delete dim_cache_[Y_AXIS].offset_;
302       dim_cache_[Y_AXIS].offset_ = 0;
303     }
304
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.
309   */
310   if (Grob *p = get_parent (Y_AXIS))
311     {
312       Real trans = 0;
313       if (Align_interface::has_interface (p) && !dim_cache_[Y_AXIS].offset_)
314         trans = Align_interface::get_pure_child_y_translation (p, this, start, end);
315
316       return off + trans + p->pure_relative_y_coordinate (refp, start, end);
317     }
318   return off;
319 }
320
321 /* Invoke callbacks to get offset relative to parent.  */
322 Real
323 Grob::get_offset (Axis a) const
324 {
325   if (dim_cache_[a].offset_)
326     return *dim_cache_[a].offset_;
327
328   Grob *me = (Grob *) this;
329
330   SCM sym = axis_offset_symbol (a);
331   me->dim_cache_[a].offset_ = new Real (0.0);
332
333   /*
334     UGH: can't fold next 2 statements together. Apparently GCC thinks
335     dim_cache_[a].offset_ is unaliased.
336   */
337   Real off = robust_scm2double (internal_get_property (sym), 0.0);
338   if (me->dim_cache_[a].offset_)
339     {
340       *me->dim_cache_[a].offset_ += off;
341       me->del_property (sym);
342       return *me->dim_cache_[a].offset_;
343     }
344   else
345     return 0.0;
346 }
347
348 Real
349 Grob::maybe_pure_coordinate (Grob const *refp, Axis a, bool pure, int start, int end)
350 {
351   if (pure && a != Y_AXIS)
352     programming_error ("tried to get pure X-offset");
353   return (pure && a == Y_AXIS) ? pure_relative_y_coordinate (refp, start, end)
354     : relative_coordinate (refp, a);
355 }
356
357 /****************************************************************
358   extents
359 ****************************************************************/
360
361 void
362 Grob::flush_extent_cache (Axis axis)
363 {
364   if (dim_cache_[axis].extent_)
365     {
366       /*
367         Ugh, this is not accurate; will flush property, causing
368         callback to be called if.
369        */
370       del_property ((axis == X_AXIS) ? ly_symbol2scm ("X-extent") : ly_symbol2scm ("Y-extent"));
371       delete dim_cache_[axis].extent_;
372       dim_cache_[axis].extent_ = 0;
373       if (get_parent (axis))
374         get_parent (axis)->flush_extent_cache (axis);
375     }
376 }
377
378
379 Interval
380 Grob::extent (Grob *refp, Axis a) const
381 {
382   Real offset = relative_coordinate (refp, a);
383   Interval real_ext;
384   if (dim_cache_[a].extent_)
385     {
386       real_ext = *dim_cache_[a].extent_;
387     }
388   else
389     {
390       /*
391         Order is significant: ?-extent may trigger suicide.
392        */
393       SCM ext_sym =
394         (a == X_AXIS)
395         ? ly_symbol2scm ("X-extent")
396         : ly_symbol2scm ("Y-extent");
397         
398       SCM ext = internal_get_property (ext_sym);
399       if (is_number_pair (ext))
400         real_ext.unite (ly_scm2interval (ext));
401
402       SCM min_ext_sym =
403         (a == X_AXIS)
404         ? ly_symbol2scm ("minimum-X-extent")
405         : ly_symbol2scm ("minimum-Y-extent");
406       SCM min_ext = internal_get_property (min_ext_sym);
407       if (is_number_pair (min_ext))
408         real_ext.unite (ly_scm2interval (min_ext));
409
410       ((Grob*)this)->dim_cache_[a].extent_ = new Interval (real_ext);  
411     }
412   
413   real_ext.translate (offset);
414   
415   return real_ext;
416 }
417
418 Interval
419 Grob::pure_height (Grob *refp, int start, int end)
420 {
421   SCM proc = get_property_data (ly_symbol2scm ("Y-extent"));
422   SCM iv_scm = call_pure_function (proc,
423                                    scm_list_1 (self_scm ()),
424                                    start, end);
425   Interval iv = robust_scm2interval (iv_scm, Interval (0, 0));
426   Real offset = pure_relative_y_coordinate (refp, start, end);
427
428   SCM min_ext = get_property ("minimum-Y-extent");
429
430   /* we don't add minimum-Y-extent if the extent is empty. This solves
431      a problem with Hara-kiri spanners. They would request_suicide and
432      return empty extents, but we would force them here to be large. */
433   if (!iv.is_empty () && is_number_pair (min_ext))
434     iv.unite (ly_scm2interval (min_ext));
435
436   iv.translate (offset);
437   return iv;
438 }
439
440 Interval
441 Grob::maybe_pure_extent (Grob *refp, Axis a, bool pure, int start, int end)
442 {
443   if (pure && a != Y_AXIS)
444     programming_error ("tried to get pure width");
445   return (pure && a == Y_AXIS) ? pure_height (refp, start, end) : extent (refp, a);
446 }
447
448 Interval_t<int>
449 Grob::spanned_rank_iv () const
450 {
451   return Interval_t<int> (-1, 0);
452 }
453
454 /****************************************************************
455   REFPOINTS
456 ****************************************************************/
457
458 /* Find the group-element which has both #this# and #s#  */
459 Grob *
460 Grob::common_refpoint (Grob const *s, Axis a) const
461 {
462   /* I don't like the quadratic aspect of this code, but I see no
463      other way.  The largest chain of parents might be 10 high or so,
464      so it shouldn't be a real issue.  */
465   for (Grob const *c = this; c; c = c->dim_cache_[a].parent_)
466     for (Grob const *d = s; d; d = d->dim_cache_[a].parent_)
467       if (d == c)
468         return (Grob *) d;
469
470   return 0;
471 }
472
473 void
474 Grob::set_parent (Grob *g, Axis a)
475 {
476   dim_cache_[a].parent_ = g;
477 }
478
479 Grob *
480 Grob::get_parent (Axis a) const
481 {
482   return dim_cache_[a].parent_;
483 }
484
485
486 void
487 Grob::fixup_refpoint ()
488 {
489   for (int a = X_AXIS; a < NO_AXES; a++)
490     {
491       Axis ax = (Axis)a;
492       Grob *parent = get_parent (ax);
493
494       if (!parent)
495         continue;
496
497       if (parent->get_system () != get_system () && get_system ())
498         {
499           Grob *newparent = parent->find_broken_piece (get_system ());
500           set_parent (newparent, ax);
501         }
502
503       if (Item *i = dynamic_cast<Item *> (this))
504         {
505           Item *parenti = dynamic_cast<Item *> (parent);
506
507           if (parenti && i)
508             {
509               Direction my_dir = i->break_status_dir ();
510               if (my_dir != parenti->break_status_dir ())
511                 {
512                   Item *newparent = parenti->find_prebroken_piece (my_dir);
513                   set_parent (newparent, ax);
514                 }
515             }
516         }
517     }
518 }
519
520
521 /****************************************************************
522   MESSAGES
523 ****************************************************************/
524 void
525 Grob::warning (string s) const
526 {
527   SCM cause = self_scm ();
528   while (Grob *g = unsmob_grob (cause))
529     cause = g->get_property ("cause");
530
531   /* ES TODO: cause can't be Music*/
532   if (Music *m = unsmob_music (cause))
533     m->origin ()->warning (s);
534   else if (Stream_event *ev = unsmob_stream_event (cause))
535     ev->origin ()->warning (s);
536   else
537     ::warning (s);
538 }
539
540
541 string
542 Grob::name () const
543 {
544   SCM meta = get_property ("meta");
545   SCM nm = scm_assq (ly_symbol2scm ("name"), meta);
546   nm = (scm_is_pair (nm)) ? scm_cdr (nm) : SCM_EOL;
547   return scm_is_symbol (nm) ? ly_symbol2string (nm) : this->class_name ();
548 }
549
550 void
551 Grob::programming_error (string s) const
552 {
553   SCM cause = self_scm ();
554   while (Grob *g = unsmob_grob (cause))
555     cause = g->get_property ("cause");
556
557   s = _f ("programming error: %s", s);
558
559   /* ES TODO: cause can't be Music*/
560   if (Music *m = unsmob_music (cause))
561     m->origin ()->message (s);
562   else if (Stream_event *ev = unsmob_stream_event (cause))
563     ev->origin ()->warning (s);
564   else
565     ::message (s);
566 }
567
568
569 ADD_INTERFACE (Grob,
570                "A grob represents a piece of music notation\n"
571                "\n"
572                "All grobs have an X and Y-position on the page.  These X and Y positions\n"
573                "are stored in a relative format, so they can easily be combined by\n"
574                "stacking them, hanging one grob to the side of another, and coupling\n"
575                "them into a grouping objects.\n"
576                "\n"
577                "Each grob has a reference point (a.k.a.  parent): the position of a grob\n"
578                "is stored relative to that reference point. For example the X-reference\n"
579                "point of a staccato dot usually is the note head that it applies\n"
580                "to. When the note head is moved, the staccato dot moves along\n"
581                "automatically.\n"
582                "\n"
583                "A grob is often associated with a symbol, but some grobs do not print\n"
584                "any symbols. They take care of grouping objects. For example, there is a\n"
585                "separate grob that stacks staves vertically. The @ref{NoteCollision}\n"
586                "is also an abstract grob: it only moves around chords, but doesn't print\n"
587                "anything.\n"
588                "\n"
589                "Grobs have a properties: Scheme variables, that can be read and set. "
590                "They have two types. Immutable variables "
591                "define the default style and behavior.  They are shared between  many objects. "
592                "They can be changed using @code{\\override} and @code{\\revert}. "
593                "\n\n"
594                "Mutable properties are variables that are specific to one grob. Typically, "
595                "lists of other objects, or results from computations are stored in"
596                "mutable properties: every call to set-grob-property (or its C++ equivalent) "
597                "sets a mutable property. "
598                "\n\n"
599                "The properties @code{after-line-breaking} and @code{before-line-breaking} "
600                "are dummies that are not user-serviceable. "
601
602                ,
603
604                /* properties */
605                "X-extent "
606                "X-offset "
607                "Y-extent "
608                "Y-offset "
609                "after-line-breaking "
610                "avoid-slur "
611                "axis-group-parent-X "
612                "axis-group-parent-Y "
613                "before-line-breaking "
614                "cause "
615                "color "
616                "cross-staff "
617                "extra-X-extent "
618                "extra-Y-extent "
619                "extra-offset "
620                "interfaces "
621                "layer "
622                "meta "
623                "minimum-X-extent "
624                "minimum-Y-extent "
625                "outside-staff-horizontal-padding "
626                "outside-staff-padding "
627                "outside-staff-priority "
628                "rotation "
629                "springs-and-rods "
630                "staff-symbol "
631                "stencil "
632                "transparent "
633                );
634
635 /****************************************************************
636   CALLBACKS
637 ****************************************************************/
638
639 static SCM
640 grob_stencil_extent (Grob *me, Axis a)
641 {
642   Stencil *m = me->get_stencil ();
643   Interval e;
644   if (m)
645     e = m->extent (a);
646   return ly_interval2scm (e);
647 }
648
649
650 MAKE_SCHEME_CALLBACK (Grob, stencil_height, 1);
651 SCM
652 Grob::stencil_height (SCM smob)
653 {
654   Grob *me = unsmob_grob (smob);
655   return grob_stencil_extent (me, Y_AXIS);
656 }
657
658 MAKE_SCHEME_CALLBACK(Grob, y_parent_positioning, 1);
659 SCM
660 Grob::y_parent_positioning (SCM smob)
661 {
662   Grob *me = unsmob_grob (smob);
663   Grob *par = me->get_parent (Y_AXIS);
664   if (par)
665     (void) par->get_property ("positioning-done");
666
667   return scm_from_double (0.0);
668 }
669
670
671 MAKE_SCHEME_CALLBACK(Grob, x_parent_positioning, 1);
672 SCM
673 Grob::x_parent_positioning (SCM smob)
674 {
675   Grob *me = unsmob_grob (smob);
676   
677   Grob *par = me->get_parent (X_AXIS);
678   if (par)
679     (void) par->get_property ("positioning-done");
680
681   return scm_from_double (0.0);
682 }
683
684 MAKE_SCHEME_CALLBACK (Grob, stencil_width, 1);
685 SCM
686 Grob::stencil_width (SCM smob)
687 {
688   Grob *me = unsmob_grob (smob);
689   return grob_stencil_extent (me, X_AXIS);
690 }
691
692
693
694 Grob *
695 common_refpoint_of_list (SCM elist, Grob *common, Axis a)
696 {
697   for (; scm_is_pair (elist); elist = scm_cdr (elist))
698     if (Grob *s = unsmob_grob (scm_car (elist)))
699       {
700         if (common)
701           common = common->common_refpoint (s, a);
702         else
703           common = s;
704       }
705
706   return common;
707 }
708
709 Grob *
710 common_refpoint_of_array (vector<Grob*> const &arr, Grob *common, Axis a)
711 {
712   for (vsize i = arr.size (); i--;)
713     if (Grob *s = arr[i])
714       {
715         if (common)
716           common = common->common_refpoint (s, a);
717         else
718           common = s;
719       }
720
721   return common;
722 }
723
724 Interval
725 robust_relative_extent (Grob *me, Grob *refpoint, Axis a)
726 {
727   Interval ext = me->extent (refpoint, a);
728   if (ext.is_empty ())
729     ext.add_point (me->relative_coordinate (refpoint, a));
730
731   return ext;
732 }
733