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