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