]> git.donarmstrong.com Git - lilypond.git/blob - lily/grob.cc
Merge branch 'master' of ssh+git://hanwen@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--2006 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 (int count) const
31 {
32   return new Grob (*this, count);
33 }
34
35 Grob::Grob (SCM basicprops,
36             Object_key const *key)
37 {
38   key_ = key;
39   
40   /* FIXME: default should be no callback.  */
41   self_scm_ = SCM_EOL;
42   layout_ = 0;
43   original_ = 0;
44   interfaces_ = SCM_EOL;
45   immutable_property_alist_ = basicprops;
46   mutable_property_alist_ = SCM_EOL;
47   object_alist_ = SCM_EOL;
48   
49   /* We do smobify_self () as the first step.  Since the object lives
50      on the heap, none of its SCM variables are protected from
51      GC. After smobify_self (), they are.  */
52   smobify_self ();
53
54   /*
55     We always get a new key object for a new grob.
56   */
57   if (key_)
58     ((Object_key *)key_)->unprotect ();
59
60   SCM meta = get_property ("meta");
61   if (scm_is_pair (meta))
62     interfaces_ = scm_cdr (scm_assq (ly_symbol2scm ("interfaces"), meta));
63   
64   if (get_property_data ("X-extent") == SCM_EOL)
65     set_property ("X-extent", Grob::stencil_width_proc);
66   if (get_property_data ("Y-extent") == SCM_EOL)
67     set_property ("Y-extent", Grob::stencil_height_proc);
68 }
69
70 Grob::Grob (Grob const &s, int copy_index)
71   : dim_cache_ (s.dim_cache_)
72 {
73   key_ = (use_object_keys) ? new Copied_key (s.key_, copy_index) : 0;
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   if (key_)
86     ((Object_key *)key_)->unprotect ();
87 }
88
89 Grob::~Grob ()
90 {
91 }
92 /****************************************************************
93   STENCILS
94 ****************************************************************/
95
96 Stencil *
97 Grob::get_stencil () const
98 {
99   if (!is_live ())
100     return 0;
101
102   SCM stil = get_property ("stencil");
103   return unsmob_stencil (stil);
104 }
105
106 Stencil
107 Grob::get_print_stencil () const
108 {
109   SCM stil = get_property ("stencil");
110
111   Stencil retval;
112   if (Stencil *m = unsmob_stencil (stil))
113     {
114       retval = *m;
115       if (to_boolean (get_property ("transparent")))
116         retval = Stencil (m->extent_box (), SCM_EOL);
117       else
118         {
119           SCM expr = m->expr ();
120           expr = scm_list_3 (ly_symbol2scm ("grob-cause"),
121                              self_scm (), expr);
122
123           retval = Stencil (m->extent_box (), expr);
124         }
125
126       SCM rot = get_property ("rotation");
127       if (scm_is_pair (rot))
128         {
129           Real angle = scm_to_double (scm_car (rot));
130           Real x = scm_to_double (scm_cadr (rot));
131           Real y = scm_to_double (scm_caddr (rot));
132
133           retval.rotate (angle, Offset (x, y));
134         }
135
136       /* color support... see interpret_stencil_expression () for more... */
137       SCM color = get_property ("color");
138       if (scm_is_pair (color))
139         {
140           SCM expr = scm_list_3 (ly_symbol2scm ("color"),
141                                  color,
142                                  retval.expr ());
143
144           retval = Stencil (retval.extent_box (), expr);
145         }
146
147     }
148
149   return retval;
150 }
151
152 /****************************************************************
153   VIRTUAL STUBS
154 ****************************************************************/
155 void
156 Grob::do_break_processing ()
157 {
158 }
159
160 void
161 Grob::discretionary_processing ()
162 {
163 }
164
165 System *
166 Grob::get_system () const
167 {
168   return 0;
169 }
170
171
172 void
173 Grob::handle_broken_dependencies ()
174 {
175   Spanner *sp = dynamic_cast<Spanner *> (this);
176   if (original () && sp)
177     return;
178
179   if (sp)
180     /* THIS, SP is the original spanner.  We use a special function
181        because some Spanners have enormously long lists in their
182        properties, and a special function fixes FOO  */
183     {
184       for (SCM s = object_alist_; scm_is_pair (s); s = scm_cdr (s))
185         sp->substitute_one_mutable_property (scm_caar (s), scm_cdar (s));
186     }
187   System *system = get_system ();
188
189   if (is_live ()
190       && system
191       && common_refpoint (system, X_AXIS)
192       && common_refpoint (system, Y_AXIS))
193     substitute_object_links (system->self_scm (), object_alist_);
194   else if (dynamic_cast<System *> (this))
195     substitute_object_links (SCM_UNDEFINED, object_alist_);
196   else
197     /* THIS element is `invalid'; it has been removed from all
198        dependencies, so let's junk the element itself.
199
200        Do not do this for System, since that would remove references
201        to the originals of score-grobs, which get then GC'd (a bad
202        thing).  */
203     suicide ();
204 }
205
206 /* Note that we still want references to this element to be
207    rearranged, and not silently thrown away, so we keep pointers like
208    {broken_into_{drul, array}, original}
209 */
210 void
211 Grob::suicide ()
212 {
213   if (!is_live ())
214     return;
215
216   for (int a = X_AXIS; a < NO_AXES; a++)
217     dim_cache_[a].clear ();
218
219   mutable_property_alist_ = SCM_EOL;
220   object_alist_ = SCM_EOL;
221   immutable_property_alist_ = SCM_EOL;
222   interfaces_ = SCM_EOL;
223 }
224
225 void
226 Grob::handle_prebroken_dependencies ()
227 {
228   /* Don't do this in the derived method, since we want to keep access to
229      object_alist_ centralized.  */
230   if (original ())
231     {
232       Item *it = dynamic_cast<Item *> (this);
233       substitute_object_links (scm_from_int (it->break_status_dir ()),
234                                original ()->object_alist_);
235     }
236 }
237
238 Grob *
239 Grob::find_broken_piece (System *) const
240 {
241   return 0;
242 }
243
244 /****************************************************************
245    OFFSETS
246 ****************************************************************/
247
248 void
249 Grob::translate_axis (Real y, Axis a)
250 {
251   if (isinf (y) || isnan (y))
252     {
253       programming_error (_ ("Infinity or NaN encountered"));
254       return ;
255     }
256   
257   if (!dim_cache_[a].offset_)
258     dim_cache_[a].offset_ = new Real (y);
259   else
260     *dim_cache_[a].offset_ += y;  
261 }
262
263 /* Find the offset relative to D.  If D equals THIS, then it is 0.
264    Otherwise, it recursively defd as
265
266    OFFSET_ + PARENT_L_->relative_coordinate (D) */
267 Real
268 Grob::relative_coordinate (Grob const *refp, Axis a) const
269 {
270   if (refp == this)
271     return 0.0;
272
273   /* We catch PARENT_L_ == nil case with this, but we crash if we did
274      not ask for the absolute coordinate (ie. REFP == nil.)  */
275   Real off = get_offset (a);
276   if (refp == dim_cache_[a].parent_)
277     return off;
278   
279   off += dim_cache_[a].parent_->relative_coordinate (refp, a);
280
281   return off;
282 }
283
284 Real
285 Grob::pure_relative_y_coordinate (Grob const *refp, int start, int end)
286 {
287   if (refp == this)
288     return 0.0;
289
290   Real off = 0;
291
292   if (dim_cache_[Y_AXIS].offset_)
293     off = *dim_cache_[Y_AXIS].offset_;
294   else
295     {
296       SCM proc = get_property_data ("Y-offset");
297
298       dim_cache_[Y_AXIS].offset_ = new Real (0.0);
299       off = robust_scm2double (call_pure_function (proc,
300                                                    scm_list_1 (self_scm ()),
301                                                    start, end),
302                                0.0);
303       delete dim_cache_[Y_AXIS].offset_;
304       dim_cache_[Y_AXIS].offset_ = 0;
305     }
306
307   /* we simulate positioning-done if we are the child of a VerticalAlignment,
308      but only if we don't have a cached offset. If we do have a cached offset,
309      it probably means that the Alignment was fixed and it has already been
310      calculated.
311   */
312   Grob *p = get_parent (Y_AXIS);
313   Real trans = 0;
314   if (Align_interface::has_interface (p) && !dim_cache_[Y_AXIS].offset_)
315     trans = Align_interface::get_pure_child_y_translation (p, this, start, end);
316
317   return off + trans
318     + dim_cache_[Y_AXIS].parent_->pure_relative_y_coordinate (refp, start, end);
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   Interval iv = robust_scm2interval (call_pure_function (proc,
423                                                          scm_list_1 (self_scm ()),
424                                                          start, end),
425                                      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 ()
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                "extra-X-extent "
617                "extra-Y-extent "
618                "extra-offset "
619                "interfaces "
620                "layer "
621                "meta "
622                "minimum-X-extent "
623                "minimum-Y-extent "
624                "outside-staff-priority "
625                "rotation "
626                "springs-and-rods "
627                "staff-symbol "
628                "stencil "
629                "transparent "
630                );
631
632
633
634
635
636 /****************************************************************
637   CALLBACKS
638 ****************************************************************/
639
640
641 static SCM
642 grob_stencil_extent (Grob *me, Axis a)
643 {
644   Stencil *m = me->get_stencil ();
645   Interval e;
646   if (m)
647     e = m->extent (a);
648   return ly_interval2scm (e);
649 }
650
651
652 MAKE_SCHEME_CALLBACK (Grob, stencil_height, 1);
653 SCM
654 Grob::stencil_height (SCM smob)
655 {
656   Grob *me = unsmob_grob (smob);
657   return grob_stencil_extent (me, Y_AXIS);
658 }
659
660 MAKE_SCHEME_CALLBACK(Grob, y_parent_positioning, 1);
661 SCM
662 Grob::y_parent_positioning (SCM smob)
663 {
664   Grob *me = unsmob_grob (smob);
665   Grob *par = me->get_parent (Y_AXIS);
666   if (par)
667     (void) par->get_property ("positioning-done");
668
669   return scm_from_double (0.0);
670 }
671
672
673 MAKE_SCHEME_CALLBACK(Grob, x_parent_positioning, 1);
674 SCM
675 Grob::x_parent_positioning (SCM smob)
676 {
677   Grob *me = unsmob_grob (smob);
678   
679   Grob *par = me->get_parent (X_AXIS);
680   if (par)
681     (void) par->get_property ("positioning-done");
682
683   return scm_from_double (0.0);
684 }
685
686 MAKE_SCHEME_CALLBACK (Grob, stencil_width, 1);
687 SCM
688 Grob::stencil_width (SCM smob)
689 {
690   Grob *me = unsmob_grob (smob);
691   return grob_stencil_extent (me, X_AXIS);
692 }
693
694
695
696 Grob *
697 common_refpoint_of_list (SCM elist, Grob *common, Axis a)
698 {
699   for (; scm_is_pair (elist); elist = scm_cdr (elist))
700     if (Grob *s = unsmob_grob (scm_car (elist)))
701       {
702         if (common)
703           common = common->common_refpoint (s, a);
704         else
705           common = s;
706       }
707
708   return common;
709 }
710
711 Grob *
712 common_refpoint_of_array (vector<Grob*> const &arr, Grob *common, Axis a)
713 {
714   for (vsize i = arr.size (); i--;)
715     if (Grob *s = arr[i])
716       {
717         if (common)
718           common = common->common_refpoint (s, a);
719         else
720           common = s;
721       }
722
723   return common;
724 }
725
726 Interval
727 robust_relative_extent (Grob *me, Grob *refpoint, Axis a)
728 {
729   Interval ext = me->extent (refpoint, a);
730   if (ext.is_empty ())
731     ext.add_point (me->relative_coordinate (refpoint, a));
732
733   return ext;
734 }
735