]> git.donarmstrong.com Git - lilypond.git/blob - lily/grob.cc
(get_print_stencil): use retval.expr() as base for
[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 "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   /* FIXME: default should be no callback.  */
40   self_scm_ = SCM_EOL;
41   layout_ = 0;
42   original_ = 0;
43   interfaces_ = SCM_EOL;
44   immutable_property_alist_ = basicprops;
45   mutable_property_alist_ = SCM_EOL;
46   object_alist_ = SCM_EOL;
47   
48   /* We do smobify_self () as the first step.  Since the object lives
49      on the heap, none of its SCM variables are protected from
50      GC. After smobify_self (), they are.  */
51   smobify_self ();
52
53   /*
54     We always get a new key object for a new grob.
55   */
56   if (key_)
57     ((Object_key *)key_)->unprotect ();
58
59   SCM meta = get_property ("meta");
60   if (scm_is_pair (meta))
61     interfaces_ = scm_cdr (scm_assq (ly_symbol2scm ("interfaces"), meta));
62   
63   if (get_property_data (ly_symbol2scm ("X-extent")) == SCM_EOL)
64     set_property ("X-extent", Grob::stencil_width_proc);
65   if (get_property_data (ly_symbol2scm ("Y-extent")) == SCM_EOL)
66     set_property ("Y-extent", Grob::stencil_height_proc);
67 }
68
69 Grob::Grob (Grob const &s, int copy_index)
70   : dim_cache_ (s.dim_cache_)
71 {
72   key_ = (use_object_keys) ? new Copied_key (s.key_, copy_index) : 0;
73   original_ = (Grob *) & s;
74   self_scm_ = SCM_EOL;
75
76   immutable_property_alist_ = s.immutable_property_alist_;
77   mutable_property_alist_ = ly_deep_copy (s.mutable_property_alist_);
78   interfaces_ = s.interfaces_;
79   object_alist_ = SCM_EOL;
80
81   layout_ = 0;
82
83   smobify_self ();
84   if (key_)
85     ((Object_key *)key_)->unprotect ();
86 }
87
88 Grob::~Grob ()
89 {
90 }
91 /****************************************************************
92   STENCILS
93 ****************************************************************/
94
95 Stencil *
96 Grob::get_stencil () const
97 {
98   if (!is_live ())
99     return 0;
100
101   SCM stil = get_property ("stencil");
102   return unsmob_stencil (stil);
103 }
104
105 Stencil
106 Grob::get_print_stencil () const
107 {
108   SCM stil = get_property ("stencil");
109
110   Stencil retval;
111   if (Stencil *m = unsmob_stencil (stil))
112     {
113       retval = *m;
114       if (to_boolean (get_property ("transparent")))
115         retval = Stencil (m->extent_box (), SCM_EOL);
116       else
117         {
118           SCM expr = m->expr ();
119           expr = scm_list_3 (ly_symbol2scm ("grob-cause"),
120                              self_scm (), expr);
121
122           retval = Stencil (m->extent_box (), expr);
123         }
124
125       SCM rot = get_property ("rotation");
126       if (scm_is_pair (rot))
127         {
128           Real angle = scm_to_double (scm_car (rot));
129           Real x = scm_to_double (scm_cadr (rot));
130           Real y = scm_to_double (scm_caddr (rot));
131
132           retval.rotate (angle, Offset (x, y));
133         }
134
135       /* color support... see interpret_stencil_expression () for more... */
136       SCM color = get_property ("color");
137       if (color != SCM_EOL)
138         {
139           SCM expr = scm_list_3 (ly_symbol2scm ("color"),
140                                  color,
141                                  retval.expr ());
142
143           retval = Stencil (retval.extent_box (), expr);
144         }
145
146     }
147
148   return retval;
149 }
150
151 /****************************************************************
152   VIRTUAL STUBS
153 ****************************************************************/
154 void
155 Grob::do_break_processing ()
156 {
157 }
158
159 void
160 Grob::discretionary_processing ()
161 {
162 }
163
164 System *
165 Grob::get_system () const
166 {
167   return 0;
168 }
169
170
171 void
172 Grob::handle_broken_dependencies ()
173 {
174   Spanner *sp = dynamic_cast<Spanner *> (this);
175   if (original () && sp)
176     return;
177
178   if (sp)
179     /* THIS, SP is the original spanner.  We use a special function
180        because some Spanners have enormously long lists in their
181        properties, and a special function fixes FOO  */
182     {
183       for (SCM s = object_alist_; scm_is_pair (s); s = scm_cdr (s))
184         sp->substitute_one_mutable_property (scm_caar (s), scm_cdar (s));
185     }
186   System *system = get_system ();
187
188   if (is_live ()
189       && system
190       && common_refpoint (system, X_AXIS)
191       && common_refpoint (system, Y_AXIS))
192     substitute_object_links (system->self_scm (), object_alist_);
193   else if (dynamic_cast<System *> (this))
194     substitute_object_links (SCM_UNDEFINED, object_alist_);
195   else
196     /* THIS element is `invalid'; it has been removed from all
197        dependencies, so let's junk the element itself.
198
199        Do not do this for System, since that would remove references
200        to the originals of score-grobs, which get then GC'd (a bad
201        thing).  */
202     suicide ();
203 }
204
205 /* Note that we still want references to this element to be
206    rearranged, and not silently thrown away, so we keep pointers like
207    {broken_into_{drul, array}, original}
208 */
209 void
210 Grob::suicide ()
211 {
212   if (!is_live ())
213     return;
214
215   for (int a = X_AXIS; a < NO_AXES; a++)
216     dim_cache_[a].clear ();
217
218   mutable_property_alist_ = SCM_EOL;
219   object_alist_ = SCM_EOL;
220   immutable_property_alist_ = SCM_EOL;
221   interfaces_ = SCM_EOL;
222 }
223
224 void
225 Grob::handle_prebroken_dependencies ()
226 {
227   /* Don't do this in the derived method, since we want to keep access to
228      object_alist_ centralized.  */
229   if (original ())
230     {
231       Item *it = dynamic_cast<Item *> (this);
232       substitute_object_links (scm_from_int (it->break_status_dir ()),
233                                original ()->object_alist_);
234     }
235 }
236
237 Grob *
238 Grob::find_broken_piece (System *) const
239 {
240   return 0;
241 }
242
243 /****************************************************************
244    OFFSETS
245 ****************************************************************/
246
247 void
248 Grob::translate_axis (Real y, Axis a)
249 {
250   if (isinf (y) || isnan (y))
251     {
252       programming_error (_ ("Infinity or NaN encountered"));
253       return ;
254     }
255   
256   if (!dim_cache_[a].offset_)
257     dim_cache_[a].offset_ = new Real (y);
258   else
259     *dim_cache_[a].offset_ += y;  
260 }
261
262 /* Find the offset relative to D.  If D equals THIS, then it is 0.
263    Otherwise, it recursively defd as
264
265    OFFSET_ + PARENT_L_->relative_coordinate (D) */
266 Real
267 Grob::relative_coordinate (Grob const *refp, Axis a) const
268 {
269   if (refp == this)
270     return 0.0;
271
272   /* We catch PARENT_L_ == nil case with this, but we crash if we did
273      not ask for the absolute coordinate (ie. REFP == nil.)  */
274   Real off = get_offset (a);
275   if (refp == dim_cache_[a].parent_)
276     return off;
277   
278   off += dim_cache_[a].parent_->relative_coordinate (refp, a);
279
280   return off;
281 }
282
283 Real
284 Grob::pure_relative_y_coordinate (Grob const *refp, int start, int end)
285 {
286   if (refp == this)
287     return 0.0;
288
289   SCM pure_off = ly_lily_module_constant ("pure-Y-offset");
290   Real off = 0;
291
292   if (dim_cache_[Y_AXIS].offset_)
293     off = *dim_cache_[Y_AXIS].offset_;
294   else if (ly_is_procedure (pure_off))
295     {
296       dim_cache_[Y_AXIS].offset_ = new Real (0.0);
297       off = scm_to_double (scm_apply_3 (pure_off, self_scm (),
298                                         scm_from_int (start), scm_from_int (end),
299                                         SCM_EOL));
300       delete dim_cache_[Y_AXIS].offset_;
301       dim_cache_[Y_AXIS].offset_ = 0;
302     }
303
304   /* we simulate positioning-done if we are the child of a VerticalAlignment */
305   Grob *p = get_parent (Y_AXIS);
306   Real trans = 0;
307   if (Align_interface::has_interface (p))
308     trans = Align_interface::get_pure_child_y_translation (p, this, start, end);
309
310   return off + trans
311     + dim_cache_[Y_AXIS].parent_->pure_relative_y_coordinate (refp, start, end);
312 }
313
314 /* Invoke callbacks to get offset relative to parent.  */
315 Real
316 Grob::get_offset (Axis a) const
317 {
318   if (dim_cache_[a].offset_)
319     return *dim_cache_[a].offset_;
320
321   Grob *me = (Grob *) this;
322
323   SCM sym = axis_offset_symbol (a);
324   me->dim_cache_[a].offset_ = new Real (0.0);
325
326   /*
327     UGH: can't fold next 2 statements together. Apparently GCC thinks
328     dim_cache_[a].offset_ is unaliased.
329   */
330   Real off = robust_scm2double (internal_get_property (sym), 0.0);
331   if (me->dim_cache_[a].offset_)
332     {
333       *me->dim_cache_[a].offset_ += off;
334       me->del_property (sym);
335       return *me->dim_cache_[a].offset_;
336     }
337   else
338     return 0.0;
339 }
340
341 Real
342 Grob::maybe_pure_coordinate (Grob const *refp, Axis a, bool pure, int start, int end)
343 {
344   if (pure && a != Y_AXIS)
345     programming_error ("tried to get pure X-offset");
346   return (pure && a == Y_AXIS) ? pure_relative_y_coordinate (refp, start, end)
347     : relative_coordinate (refp, a);
348 }
349
350 /****************************************************************
351   extents
352 ****************************************************************/
353
354 void
355 Grob::flush_extent_cache (Axis axis)
356 {
357   if (dim_cache_[axis].extent_)
358     {
359       /*
360         Ugh, this is not accurate; will flush property, causing
361         callback to be called if.
362        */
363       del_property ((axis == X_AXIS) ? ly_symbol2scm ("X-extent") : ly_symbol2scm ("Y-extent"));
364       delete dim_cache_[axis].extent_;
365       dim_cache_[axis].extent_ = 0;
366       if (get_parent (axis))
367         get_parent (axis)->flush_extent_cache (axis);
368     }
369 }
370
371
372 Interval
373 Grob::extent (Grob *refp, Axis a) const
374 {
375   Real offset = relative_coordinate (refp, a);
376   Interval real_ext;
377   if (dim_cache_[a].extent_)
378     {
379       real_ext = *dim_cache_[a].extent_;
380     }
381   else
382     {
383       /*
384         Order is significant: ?-extent may trigger suicide.
385        */
386       SCM ext_sym =
387         (a == X_AXIS)
388         ? ly_symbol2scm ("X-extent")
389         : ly_symbol2scm ("Y-extent");
390
391       SCM ext = internal_get_property (ext_sym);
392       if (is_number_pair (ext))
393         real_ext.unite (ly_scm2interval (ext));
394
395       SCM min_ext_sym =
396         (a == X_AXIS)
397         ? ly_symbol2scm ("minimum-X-extent")
398         : ly_symbol2scm ("minimum-Y-extent");
399       SCM min_ext = internal_get_property (min_ext_sym);
400       if (is_number_pair (min_ext))
401         real_ext.unite (ly_scm2interval (min_ext));
402       ((Grob*)this)->dim_cache_[a].extent_ = new Interval (real_ext);  
403     }
404   
405   real_ext.translate (offset);
406   
407   return real_ext;
408 }
409
410 Interval
411 Grob::pure_height (Grob *refp, int start, int end)
412 {
413   SCM pure_height = ly_lily_module_constant ("pure-Y-extent");
414   Interval iv (0, 0);
415
416   if (ly_is_procedure (pure_height))
417     iv = ly_scm2interval (scm_apply_3 (pure_height, self_scm (),
418                                        scm_from_int (start), scm_from_int (end),
419                                        SCM_EOL));
420   Real offset = pure_relative_y_coordinate (refp, start, end);
421
422   SCM min_ext = get_property ("minimum-Y-extent");
423   if (is_number_pair (min_ext))
424     iv.unite (ly_scm2interval (min_ext));
425
426   iv.translate (offset);
427   return iv;
428 }
429
430 Interval
431 Grob::maybe_pure_extent (Grob *refp, Axis a, bool pure, int start, int end)
432 {
433   if (pure && a != Y_AXIS)
434     programming_error ("tried to get pure width");
435   return (pure && a == Y_AXIS) ? pure_height (refp, start, end) : extent (refp, a);
436 }
437
438 Interval_t<int>
439 Grob::spanned_rank_iv ()
440 {
441   return Interval_t<int> (-1, 0);
442 }
443
444 /****************************************************************
445   REFPOINTS
446 ****************************************************************/
447
448 /* Find the group-element which has both #this# and #s#  */
449 Grob *
450 Grob::common_refpoint (Grob const *s, Axis a) const
451 {
452   /* I don't like the quadratic aspect of this code, but I see no
453      other way.  The largest chain of parents might be 10 high or so,
454      so it shouldn't be a real issue.  */
455   for (Grob const *c = this; c; c = c->dim_cache_[a].parent_)
456     for (Grob const *d = s; d; d = d->dim_cache_[a].parent_)
457       if (d == c)
458         return (Grob *) d;
459
460   return 0;
461 }
462
463 void
464 Grob::set_parent (Grob *g, Axis a)
465 {
466   dim_cache_[a].parent_ = g;
467 }
468
469 Grob *
470 Grob::get_parent (Axis a) const
471 {
472   return dim_cache_[a].parent_;
473 }
474
475
476 void
477 Grob::fixup_refpoint ()
478 {
479   for (int a = X_AXIS; a < NO_AXES; a++)
480     {
481       Axis ax = (Axis)a;
482       Grob *parent = get_parent (ax);
483
484       if (!parent)
485         continue;
486
487       if (parent->get_system () != get_system () && get_system ())
488         {
489           Grob *newparent = parent->find_broken_piece (get_system ());
490           set_parent (newparent, ax);
491         }
492
493       if (Item *i = dynamic_cast<Item *> (this))
494         {
495           Item *parenti = dynamic_cast<Item *> (parent);
496
497           if (parenti && i)
498             {
499               Direction my_dir = i->break_status_dir ();
500               if (my_dir != parenti->break_status_dir ())
501                 {
502                   Item *newparent = parenti->find_prebroken_piece (my_dir);
503                   set_parent (newparent, ax);
504                 }
505             }
506         }
507     }
508 }
509
510
511 /****************************************************************
512   MESSAGES
513 ****************************************************************/
514 void
515 Grob::warning (string s) const
516 {
517   SCM cause = self_scm ();
518   while (Grob *g = unsmob_grob (cause))
519     cause = g->get_property ("cause");
520
521   /* ES TODO: cause can't be Music*/
522   if (Music *m = unsmob_music (cause))
523     m->origin ()->warning (s);
524   else if (Stream_event *ev = unsmob_stream_event (cause))
525     ev->origin ()->warning (s);
526   else
527     ::warning (s);
528 }
529
530
531 string
532 Grob::name () const
533 {
534   SCM meta = get_property ("meta");
535   SCM nm = scm_assq (ly_symbol2scm ("name"), meta);
536   nm = (scm_is_pair (nm)) ? scm_cdr (nm) : SCM_EOL;
537   return scm_is_symbol (nm) ? ly_symbol2string (nm) : this->class_name ();
538 }
539
540 void
541 Grob::programming_error (string s) const
542 {
543   SCM cause = self_scm ();
544   while (Grob *g = unsmob_grob (cause))
545     cause = g->get_property ("cause");
546
547   s = _f ("programming error: %s", s);
548
549   /* ES TODO: cause can't be Music*/
550   if (Music *m = unsmob_music (cause))
551     m->origin ()->message (s);
552   else if (Stream_event *ev = unsmob_stream_event (cause))
553     ev->origin ()->warning (s);
554   else
555     ::message (s);
556 }
557
558
559 ADD_INTERFACE (Grob, "grob-interface",
560                "A grob represents a piece of music notation\n"
561                "\n"
562                "All grobs have an X and Y-position on the page.  These X and Y positions\n"
563                "are stored in a relative format, so they can easily be combined by\n"
564                "stacking them, hanging one grob to the side of another, and coupling\n"
565                "them into a grouping objects.\n"
566                "\n"
567                "Each grob has a reference point (a.k.a.  parent): the position of a grob\n"
568                "is stored relative to that reference point. For example the X-reference\n"
569                "point of a staccato dot usually is the note head that it applies\n"
570                "to. When the note head is moved, the staccato dot moves along\n"
571                "automatically.\n"
572                "\n"
573                "A grob is often associated with a symbol, but some grobs do not print\n"
574                "any symbols. They take care of grouping objects. For example, there is a\n"
575                "separate grob that stacks staves vertically. The @ref{NoteCollision}\n"
576                "is also an abstract grob: it only moves around chords, but doesn't print\n"
577                "anything.\n"
578                "\n"
579                "Grobs have a properties: Scheme variables, that can be read and set. "
580                "They have two types. Immutable variables "
581                "define the default style and behavior.  They are shared between  many objects. "
582                "They can be changed using @code{\\override} and @code{\\revert}. "
583                "\n\n"
584                "Mutable properties are variables that are specific to one grob. Typically, "
585                "lists of other objects, or results from computations are stored in"
586                "mutable properties: every call to set-grob-property (or its C++ equivalent) "
587                "sets a mutable property. "
588                "\n\n"
589                "The properties @code{after-line-breaking} and @code{before-line-breaking} "
590                "are dummies that are not user-serviceable. "
591
592                ,
593
594                /* properties */
595                "X-extent "
596                "X-offset "
597                "Y-extent "
598                "Y-offset "
599                "after-line-breaking "
600                "axis-group-parent-X "
601                "axis-group-parent-Y "
602                "before-line-breaking "
603                "cause "
604                "color "
605                "extra-X-extent "
606                "extra-Y-extent "
607                "extra-offset "
608                "interfaces "
609                "layer "
610                "meta "
611                "minimum-X-extent "
612                "minimum-Y-extent "
613                "rotation "
614                "springs-and-rods "
615                "staff-symbol "
616                "stencil "
617                "transparent "
618                );
619
620
621
622
623
624 /****************************************************************
625   CALLBACKS
626 ****************************************************************/
627
628
629 static SCM
630 grob_stencil_extent (Grob *me, Axis a)
631 {
632   Stencil *m = me->get_stencil ();
633   Interval e;
634   if (m)
635     e = m->extent (a);
636   return ly_interval2scm (e);
637 }
638
639
640 MAKE_SCHEME_CALLBACK (Grob, stencil_height, 1);
641 SCM
642 Grob::stencil_height (SCM smob)
643 {
644   Grob *me = unsmob_grob (smob);
645   return grob_stencil_extent (me, Y_AXIS);
646 }
647
648 MAKE_SCHEME_CALLBACK(Grob, y_parent_positioning, 1);
649 SCM
650 Grob::y_parent_positioning (SCM smob)
651 {
652   Grob *me = unsmob_grob (smob);
653   Grob *par = me->get_parent (Y_AXIS);
654   if (par)
655     (void) par->get_property ("positioning-done");
656
657   return scm_from_double (0.0);
658 }
659
660
661 MAKE_SCHEME_CALLBACK(Grob, x_parent_positioning, 1);
662 SCM
663 Grob::x_parent_positioning (SCM smob)
664 {
665   Grob *me = unsmob_grob (smob);
666   
667   Grob *par = me->get_parent (X_AXIS);
668   if (par)
669     (void) par->get_property ("positioning-done");
670
671   return scm_from_double (0.0);
672 }
673
674 MAKE_SCHEME_CALLBACK (Grob, stencil_width, 1);
675 SCM
676 Grob::stencil_width (SCM smob)
677 {
678   Grob *me = unsmob_grob (smob);
679   return grob_stencil_extent (me, X_AXIS);
680 }
681
682
683
684 Grob *
685 common_refpoint_of_list (SCM elist, Grob *common, Axis a)
686 {
687   for (; scm_is_pair (elist); elist = scm_cdr (elist))
688     if (Grob *s = unsmob_grob (scm_car (elist)))
689       {
690         if (common)
691           common = common->common_refpoint (s, a);
692         else
693           common = s;
694       }
695
696   return common;
697 }
698
699 Grob *
700 common_refpoint_of_array (vector<Grob*> const &arr, Grob *common, Axis a)
701 {
702   for (vsize i = arr.size (); i--;)
703     if (Grob *s = arr[i])
704       {
705         if (common)
706           common = common->common_refpoint (s, a);
707         else
708           common = s;
709       }
710
711   return common;
712 }
713
714 Interval
715 robust_relative_extent (Grob *me, Grob *refpoint, Axis a)
716 {
717   Interval ext = me->extent (refpoint, a);
718   if (ext.is_empty ())
719     ext.add_point (me->relative_coordinate (refpoint, a));
720
721   return ext;
722 }
723