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