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