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