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