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