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