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