]> git.donarmstrong.com Git - lilypond.git/blob - lily/grob.cc
Merge branch 'master' of ssh+git://gpercival@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--2007 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 iv_scm = call_pure_function (proc,
434                                    scm_list_1 (self_scm ()),
435                                    start, end);
436   Interval iv = robust_scm2interval (iv_scm, Interval (0, 0));
437   Real offset = pure_relative_y_coordinate (refp, start, end);
438
439   SCM min_ext = get_property ("minimum-Y-extent");
440
441   /* we don't add minimum-Y-extent if the extent is empty. This solves
442      a problem with Hara-kiri spanners. They would request_suicide and
443      return empty extents, but we would force them here to be large. */
444   if (!iv.is_empty () && is_number_pair (min_ext))
445     iv.unite (ly_scm2interval (min_ext));
446
447   iv.translate (offset);
448   return iv;
449 }
450
451 Interval
452 Grob::maybe_pure_extent (Grob *refp, Axis a, bool pure, int start, int end)
453 {
454   if (pure && a != Y_AXIS)
455     programming_error ("tried to get pure width");
456   return (pure && a == Y_AXIS) ? pure_height (refp, start, end) : extent (refp, a);
457 }
458
459 Interval_t<int>
460 Grob::spanned_rank_iv ()
461 {
462   return Interval_t<int> (-1, 0);
463 }
464
465 /****************************************************************
466   REFPOINTS
467 ****************************************************************/
468
469 /* Find the group-element which has both #this# and #s#  */
470 Grob *
471 Grob::common_refpoint (Grob const *s, Axis a) const
472 {
473   /* I don't like the quadratic aspect of this code, but I see no
474      other way.  The largest chain of parents might be 10 high or so,
475      so it shouldn't be a real issue.  */
476   for (Grob const *c = this; c; c = c->dim_cache_[a].parent_)
477     for (Grob const *d = s; d; d = d->dim_cache_[a].parent_)
478       if (d == c)
479         return (Grob *) d;
480
481   return 0;
482 }
483
484 void
485 Grob::set_parent (Grob *g, Axis a)
486 {
487   dim_cache_[a].parent_ = g;
488 }
489
490 Grob *
491 Grob::get_parent (Axis a) const
492 {
493   return dim_cache_[a].parent_;
494 }
495
496
497 void
498 Grob::fixup_refpoint ()
499 {
500   for (int a = X_AXIS; a < NO_AXES; a++)
501     {
502       Axis ax = (Axis)a;
503       Grob *parent = get_parent (ax);
504
505       if (!parent)
506         continue;
507
508       if (parent->get_system () != get_system () && get_system ())
509         {
510           Grob *newparent = parent->find_broken_piece (get_system ());
511           set_parent (newparent, ax);
512         }
513
514       if (Item *i = dynamic_cast<Item *> (this))
515         {
516           Item *parenti = dynamic_cast<Item *> (parent);
517
518           if (parenti && i)
519             {
520               Direction my_dir = i->break_status_dir ();
521               if (my_dir != parenti->break_status_dir ())
522                 {
523                   Item *newparent = parenti->find_prebroken_piece (my_dir);
524                   set_parent (newparent, ax);
525                 }
526             }
527         }
528     }
529 }
530
531
532 /****************************************************************
533   MESSAGES
534 ****************************************************************/
535 void
536 Grob::warning (string s) const
537 {
538   SCM cause = self_scm ();
539   while (Grob *g = unsmob_grob (cause))
540     cause = g->get_property ("cause");
541
542   /* ES TODO: cause can't be Music*/
543   if (Music *m = unsmob_music (cause))
544     m->origin ()->warning (s);
545   else if (Stream_event *ev = unsmob_stream_event (cause))
546     ev->origin ()->warning (s);
547   else
548     ::warning (s);
549 }
550
551
552 string
553 Grob::name () const
554 {
555   SCM meta = get_property ("meta");
556   SCM nm = scm_assq (ly_symbol2scm ("name"), meta);
557   nm = (scm_is_pair (nm)) ? scm_cdr (nm) : SCM_EOL;
558   return scm_is_symbol (nm) ? ly_symbol2string (nm) : this->class_name ();
559 }
560
561 void
562 Grob::programming_error (string s) const
563 {
564   SCM cause = self_scm ();
565   while (Grob *g = unsmob_grob (cause))
566     cause = g->get_property ("cause");
567
568   s = _f ("programming error: %s", s);
569
570   /* ES TODO: cause can't be Music*/
571   if (Music *m = unsmob_music (cause))
572     m->origin ()->message (s);
573   else if (Stream_event *ev = unsmob_stream_event (cause))
574     ev->origin ()->warning (s);
575   else
576     ::message (s);
577 }
578
579
580 ADD_INTERFACE (Grob,
581                "A grob represents a piece of music notation\n"
582                "\n"
583                "All grobs have an X and Y-position on the page.  These X and Y positions\n"
584                "are stored in a relative format, so they can easily be combined by\n"
585                "stacking them, hanging one grob to the side of another, and coupling\n"
586                "them into a grouping objects.\n"
587                "\n"
588                "Each grob has a reference point (a.k.a.  parent): the position of a grob\n"
589                "is stored relative to that reference point. For example the X-reference\n"
590                "point of a staccato dot usually is the note head that it applies\n"
591                "to. When the note head is moved, the staccato dot moves along\n"
592                "automatically.\n"
593                "\n"
594                "A grob is often associated with a symbol, but some grobs do not print\n"
595                "any symbols. They take care of grouping objects. For example, there is a\n"
596                "separate grob that stacks staves vertically. The @ref{NoteCollision}\n"
597                "is also an abstract grob: it only moves around chords, but doesn't print\n"
598                "anything.\n"
599                "\n"
600                "Grobs have a properties: Scheme variables, that can be read and set. "
601                "They have two types. Immutable variables "
602                "define the default style and behavior.  They are shared between  many objects. "
603                "They can be changed using @code{\\override} and @code{\\revert}. "
604                "\n\n"
605                "Mutable properties are variables that are specific to one grob. Typically, "
606                "lists of other objects, or results from computations are stored in"
607                "mutable properties: every call to set-grob-property (or its C++ equivalent) "
608                "sets a mutable property. "
609                "\n\n"
610                "The properties @code{after-line-breaking} and @code{before-line-breaking} "
611                "are dummies that are not user-serviceable. "
612
613                ,
614
615                /* properties */
616                "X-extent "
617                "X-offset "
618                "Y-extent "
619                "Y-offset "
620                "after-line-breaking "
621                "avoid-slur "
622                "axis-group-parent-X "
623                "axis-group-parent-Y "
624                "before-line-breaking "
625                "cause "
626                "color "
627                "extra-X-extent "
628                "extra-Y-extent "
629                "extra-offset "
630                "interfaces "
631                "layer "
632                "meta "
633                "minimum-X-extent "
634                "minimum-Y-extent "
635                "outside-staff-horizontal-padding "
636                "outside-staff-padding "
637                "outside-staff-priority "
638                "rotation "
639                "springs-and-rods "
640                "staff-symbol "
641                "stencil "
642                "transparent "
643                );
644
645 /****************************************************************
646   CALLBACKS
647 ****************************************************************/
648
649 static SCM
650 grob_stencil_extent (Grob *me, Axis a)
651 {
652   Stencil *m = me->get_stencil ();
653   Interval e;
654   if (m)
655     e = m->extent (a);
656   return ly_interval2scm (e);
657 }
658
659
660 MAKE_SCHEME_CALLBACK (Grob, stencil_height, 1);
661 SCM
662 Grob::stencil_height (SCM smob)
663 {
664   Grob *me = unsmob_grob (smob);
665   return grob_stencil_extent (me, Y_AXIS);
666 }
667
668 MAKE_SCHEME_CALLBACK(Grob, y_parent_positioning, 1);
669 SCM
670 Grob::y_parent_positioning (SCM smob)
671 {
672   Grob *me = unsmob_grob (smob);
673   Grob *par = me->get_parent (Y_AXIS);
674   if (par)
675     (void) par->get_property ("positioning-done");
676
677   return scm_from_double (0.0);
678 }
679
680
681 MAKE_SCHEME_CALLBACK(Grob, x_parent_positioning, 1);
682 SCM
683 Grob::x_parent_positioning (SCM smob)
684 {
685   Grob *me = unsmob_grob (smob);
686   
687   Grob *par = me->get_parent (X_AXIS);
688   if (par)
689     (void) par->get_property ("positioning-done");
690
691   return scm_from_double (0.0);
692 }
693
694 MAKE_SCHEME_CALLBACK (Grob, stencil_width, 1);
695 SCM
696 Grob::stencil_width (SCM smob)
697 {
698   Grob *me = unsmob_grob (smob);
699   return grob_stencil_extent (me, X_AXIS);
700 }
701
702
703
704 Grob *
705 common_refpoint_of_list (SCM elist, Grob *common, Axis a)
706 {
707   for (; scm_is_pair (elist); elist = scm_cdr (elist))
708     if (Grob *s = unsmob_grob (scm_car (elist)))
709       {
710         if (common)
711           common = common->common_refpoint (s, a);
712         else
713           common = s;
714       }
715
716   return common;
717 }
718
719 Grob *
720 common_refpoint_of_array (vector<Grob*> const &arr, Grob *common, Axis a)
721 {
722   for (vsize i = arr.size (); i--;)
723     if (Grob *s = arr[i])
724       {
725         if (common)
726           common = common->common_refpoint (s, a);
727         else
728           common = s;
729       }
730
731   return common;
732 }
733
734 Interval
735 robust_relative_extent (Grob *me, Grob *refpoint, Axis a)
736 {
737   Interval ext = me->extent (refpoint, a);
738   if (ext.is_empty ())
739     ext.add_point (me->relative_coordinate (refpoint, a));
740
741   return ext;
742 }
743