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