]> git.donarmstrong.com Git - lilypond.git/blob - lily/grob.cc
Run grand-replace for 2010.
[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 proc = get_property_data (ly_symbol2scm ("Y-extent"));
452   SCM iv_scm = call_pure_function (proc,
453                                    scm_list_1 (self_scm ()),
454                                    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