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