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