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