]> git.donarmstrong.com Git - lilypond.git/blob - lily/grob.cc
* lily/include/event.hh: remove file.
[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 "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   immutable_property_alist_ = basicprops;
56   mutable_property_alist_ = SCM_EOL;
57
58   /* We do smobify_self () as the first step.  Since the object lives
59      on the heap, none of its SCM variables are protected from
60      GC. After smobify_self (), they are.  */
61   smobify_self ();
62
63   /*
64     We always get a new key object for a new grob.
65   */
66   if (key_)
67     scm_gc_unprotect_object (key_->self_scm ());
68   SCM meta = get_property ("meta");
69   if (scm_is_pair (meta))
70     {
71       SCM ifs = scm_assoc (ly_symbol2scm ("interfaces"), meta);
72
73       /* Switch off interface checks for the moment.  */
74       bool itc = do_internal_type_checking_global;
75       do_internal_type_checking_global = false;
76       internal_set_property (ly_symbol2scm ("interfaces"), scm_cdr (ifs));
77       do_internal_type_checking_global = itc;
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   char const *onames[] = {"X-offset-callbacks", "Y-offset-callbacks"};
88   char const *xnames[] = {"X-extent", "Y-extent"};
89   char const *enames[] = {"X-extent-callback", "Y-extent-callback"};
90
91   for (int a = X_AXIS; a <= Y_AXIS; a++)
92     {
93       SCM l = get_property (onames[a]);
94
95       if (scm_ilength (l) >= 0)
96         {
97           dim_cache_[a].offset_callbacks_ = l;
98           dim_cache_[a].offsets_left_ = scm_ilength (l);
99         }
100       else
101         programming_error ("[XY]-offset-callbacks must be a list");
102
103       SCM cb = get_property (enames[a]);
104       if (cb == SCM_BOOL_F)
105         {
106           dim_cache_[a].dimension_ = SCM_BOOL_F;
107         }
108
109       SCM xt = get_property (xnames[a]);
110       if (is_number_pair (xt))
111         {
112           dim_cache_[a].dimension_ = xt;
113         }
114       else if (ly_is_procedure (cb))
115         {
116           dim_cache_[a].dimension_callback_ = cb;
117         }
118       else if (cb == SCM_EOL
119                && ly_is_procedure (get_property ("print-function")))
120         dim_cache_[a].dimension_callback_ = stencil_extent_proc;
121     }
122 }
123
124 Grob::Grob (Grob const &s, int copy_index)
125   : dim_cache_ (s.dim_cache_)
126 {
127   key_ = (use_object_keys) ? new Copied_key (s.key_, copy_index) : 0;
128   original_ = (Grob *) & s;
129   self_scm_ = SCM_EOL;
130
131   immutable_property_alist_ = s.immutable_property_alist_;
132   mutable_property_alist_ = SCM_EOL;
133
134   /* No properties are copied.  That is the job of
135      handle_broken_dependencies.  */
136   status_ = s.status_;
137   pscore_ = 0;
138
139   smobify_self ();
140   scm_gc_unprotect_object (key_->self_scm ());
141 }
142
143 Grob::~Grob ()
144 {
145 }
146
147 MAKE_SCHEME_CALLBACK (Grob, stencil_extent, 2);
148 SCM
149 Grob::stencil_extent (SCM element_smob, SCM scm_axis)
150 {
151   Grob *s = unsmob_grob (element_smob);
152   Axis a = (Axis) scm_to_int (scm_axis);
153
154   Stencil *m = s->get_stencil ();
155   Interval e;
156   if (m)
157     e = m->extent (a);
158   return ly_interval2scm (e);
159 }
160
161 Interval
162 robust_relative_extent (Grob *me, Grob *refp, Axis a)
163 {
164   Interval ext = me->extent (refp, a);
165   if (ext.is_empty ())
166     {
167       ext.add_point (me->relative_coordinate (refp, a));
168     }
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   for (SCM d = get_property ("dependencies"); scm_is_pair (d);
199        d = scm_cdr (d))
200     unsmob_grob (scm_car (d))->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     for (SCM s = mutable_property_alist_; scm_is_pair (s); s = scm_cdr (s))
302       sp->substitute_one_mutable_property (scm_caar (s), scm_cdar (s));
303
304   System *system = get_system ();
305
306   if (is_live ()
307       && system && common_refpoint (system, X_AXIS)
308       && common_refpoint (system, Y_AXIS))
309     substitute_mutable_properties (system
310                                    ? system->self_scm () : SCM_UNDEFINED,
311                                    mutable_property_alist_);
312   else if (dynamic_cast<System *> (this))
313     substitute_mutable_properties (SCM_UNDEFINED, mutable_property_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   immutable_property_alist_ = SCM_EOL;
336
337   set_extent (SCM_EOL, Y_AXIS);
338   set_extent (SCM_EOL, X_AXIS);
339
340   set_extent_callback (SCM_EOL, Y_AXIS);
341   set_extent_callback (SCM_EOL, X_AXIS);
342
343   for (int a = X_AXIS; a <= Y_AXIS; a++)
344     {
345       dim_cache_[a].offset_callbacks_ = SCM_EOL;
346       dim_cache_[a].offsets_left_ = 0;
347     }
348 }
349
350 void
351 Grob::handle_prebroken_dependencies ()
352 {
353   /* Don't do this in the derived method, since we want to keep access to
354      mutable_property_alist_ centralized.  */
355   if (original_)
356     {
357       Item *it = dynamic_cast<Item *> (this);
358       substitute_mutable_properties (scm_int2num (it->break_status_dir ()),
359                                      original_->mutable_property_alist_);
360     }
361 }
362
363 Grob *
364 Grob::find_broken_piece (System *) const
365 {
366   return 0;
367 }
368
369 /* Translate in one direction.  */
370 void
371 Grob::translate_axis (Real y, Axis a)
372 {
373   if (isinf (y) || isnan (y))
374     programming_error (_ (INFINITY_MSG));
375   else
376     dim_cache_[a].offset_ += y;
377 }
378
379 /* Find the offset relative to D.  If D equals THIS, then it is 0.
380    Otherwise, it recursively defd as
381
382    OFFSET_ + PARENT_L_->relative_coordinate (D) */
383 Real
384 Grob::relative_coordinate (Grob const *refp, Axis a) const
385 {
386   if (refp == this)
387     return 0.0;
388
389   /* We catch PARENT_L_ == nil case with this, but we crash if we did
390      not ask for the absolute coordinate (ie. REFP == nil.)  */
391   if (refp == dim_cache_[a].parent_)
392     return get_offset (a);
393
394   return get_offset (a) + dim_cache_[a].parent_->relative_coordinate (refp, a);
395 }
396
397 /* Invoke callbacks to get offset relative to parent.  */
398 Real
399 Grob::get_offset (Axis a) const
400 {
401   Grob *me = (Grob *) this;
402   while (dim_cache_[a].offsets_left_)
403     {
404       int l = --me->dim_cache_[a].offsets_left_;
405       SCM cb = scm_list_ref (dim_cache_[a].offset_callbacks_, scm_int2num (l));
406       SCM retval = scm_call_2 (cb, self_scm (), scm_int2num (a));
407
408       Real r = scm_to_double (retval);
409       if (isinf (r) || isnan (r))
410         {
411           programming_error (INFINITY_MSG);
412           r = 0.0;
413         }
414       me->dim_cache_[a].offset_ += r;
415     }
416   return dim_cache_[a].offset_;
417 }
418
419 bool
420 Grob::is_empty (Axis a) const
421 {
422   return !(scm_is_pair (dim_cache_[a].dimension_)
423            || ly_is_procedure (dim_cache_[a].dimension_callback_));
424 }
425
426 void
427 Grob::flush_extent_cache (Axis axis)
428 {
429   Dimension_cache *d = &dim_cache_[axis];
430   if (ly_is_procedure (d->dimension_callback_)
431       && scm_is_pair (d->dimension_))
432     {
433       d->dimension_ = SCM_EOL;
434
435       if (get_parent (axis))
436         get_parent (axis)->flush_extent_cache (axis);
437     }
438 }
439
440 Interval
441 Grob::extent (Grob *refp, Axis a) const
442 {
443   Real x = relative_coordinate (refp, a);
444
445   Dimension_cache *d = (Dimension_cache *) & dim_cache_[a];
446   Interval ext;
447
448   SCM dimpair = d->dimension_;
449   if (scm_is_pair (dimpair))
450     ;
451   else if (ly_is_procedure (d->dimension_callback_)
452            && d->dimension_ == SCM_EOL)
453     d->dimension_ = scm_call_2 (d->dimension_callback_, self_scm (), scm_int2num (a));
454   else
455     return ext;
456
457   if (!scm_is_pair (d->dimension_))
458     return ext;
459
460   ext = ly_scm2interval (d->dimension_);
461
462   SCM extra = get_property (a == X_AXIS
463                             ? "extra-X-extent"
464                             : "extra-Y-extent");
465
466   /* Signs ?  */
467   if (scm_is_pair (extra))
468     {
469       ext[BIGGER] += scm_to_double (scm_cdr (extra));
470       ext[SMALLER] += scm_to_double (scm_car (extra));
471     }
472
473   extra = get_property (a == X_AXIS
474                         ? "minimum-X-extent"
475                         : "minimum-Y-extent");
476   if (scm_is_pair (extra))
477     ext.unite (Interval (scm_to_double (scm_car (extra)),
478                          scm_to_double (scm_cdr (extra))));
479
480   ext.translate (x);
481
482   return ext;
483 }
484
485 /* Find the group-element which has both #this# and #s#  */
486 Grob *
487 Grob::common_refpoint (Grob const *s, Axis a) const
488 {
489   /* I don't like the quadratic aspect of this code, but I see no
490      other way.  The largest chain of parents might be 10 high or so,
491      so it shouldn't be a real issue.  */
492   for (Grob const *c = this; c; c = c->dim_cache_[a].parent_)
493     for (Grob const *d = s; d; d = d->dim_cache_[a].parent_)
494       if (d == c)
495         return (Grob *) d;
496
497   return 0;
498 }
499
500 Grob *
501 common_refpoint_of_list (SCM elist, Grob *common, Axis a)
502 {
503   for (; scm_is_pair (elist); elist = scm_cdr (elist))
504     if (Grob *s = unsmob_grob (scm_car (elist)))
505       {
506         if (common)
507           common = common->common_refpoint (s, a);
508         else
509           common = s;
510       }
511
512   return common;
513 }
514
515 Grob *
516 common_refpoint_of_array (Link_array<Grob> const &arr, Grob *common, Axis a)
517 {
518   for (int i = arr.size (); i--;)
519     if (Grob *s = arr[i])
520       {
521         if (common)
522           common = common->common_refpoint (s, a);
523         else
524           common = s;
525       }
526
527   return common;
528 }
529
530 String
531 Grob::name () const
532 {
533   SCM meta = get_property ("meta");
534   SCM nm = scm_assoc (ly_symbol2scm ("name"), meta);
535   nm = (scm_is_pair (nm)) ? scm_cdr (nm) : SCM_EOL;
536   return scm_is_symbol (nm) ? ly_symbol2string (nm) : classname (this);
537 }
538
539 void
540 Grob::add_offset_callback (SCM cb, Axis a)
541 {
542   if (!has_offset_callback (cb, a))
543     {
544       dim_cache_[a].offset_callbacks_
545         = scm_cons (cb, dim_cache_[a].offset_callbacks_);
546       dim_cache_[a].offsets_left_++;
547     }
548 }
549
550 bool
551 Grob::has_extent_callback (SCM cb, Axis a) const
552 {
553   return scm_equal_p (cb, dim_cache_[a].dimension_callback_) == SCM_BOOL_T;
554 }
555
556 bool
557 Grob::has_offset_callback (SCM cb, Axis a) const
558 {
559   return scm_c_memq (cb, dim_cache_[a].offset_callbacks_) != SCM_BOOL_F;
560 }
561
562 void
563 Grob::set_extent (SCM dc, Axis a)
564 {
565   dim_cache_[a].dimension_ = dc;
566 }
567
568 void
569 Grob::set_extent_callback (SCM dc, Axis a)
570 {
571   dim_cache_[a].dimension_callback_ = dc;
572 }
573
574 void
575 Grob::set_parent (Grob *g, Axis a)
576 {
577   dim_cache_[a].parent_ = g;
578 }
579
580 MAKE_SCHEME_CALLBACK (Grob, fixup_refpoint, 1);
581 SCM
582 Grob::fixup_refpoint (SCM smob)
583 {
584   Grob *me = unsmob_grob (smob);
585   for (int a = X_AXIS; a < NO_AXES; a++)
586     {
587       Axis ax = (Axis)a;
588       Grob *parent = me->get_parent (ax);
589
590       if (!parent)
591         continue;
592
593       if (parent->get_system () != me->get_system () && me->get_system ())
594         {
595           Grob *newparent = parent->find_broken_piece (me->get_system ());
596           me->set_parent (newparent, ax);
597         }
598
599       if (Item *i = dynamic_cast<Item *> (me))
600         {
601           Item *parenti = dynamic_cast<Item *> (parent);
602
603           if (parenti && i)
604             {
605               Direction my_dir = i->break_status_dir ();
606               if (my_dir != parenti->break_status_dir ())
607                 {
608                   Item *newparent = parenti->find_prebroken_piece (my_dir);
609                   me->set_parent (newparent, ax);
610                 }
611             }
612         }
613     }
614   return smob;
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   s = _f ("programming error: %s", s);
634   message (s);
635 }
636
637 /****************************************************
638   SMOB funcs
639 ****************************************************/
640
641 IMPLEMENT_SMOBS (Grob);
642 IMPLEMENT_DEFAULT_EQUAL_P (Grob);
643
644 SCM
645 Grob::mark_smob (SCM ses)
646 {
647   Grob *s = (Grob *) SCM_CELL_WORD_1 (ses);
648   scm_gc_mark (s->immutable_property_alist_);
649
650   if (s->key_)
651     scm_gc_mark (s->key_->self_scm ());
652   for (int a = 0; a < 2; a++)
653     {
654       scm_gc_mark (s->dim_cache_[a].offset_callbacks_);
655       scm_gc_mark (s->dim_cache_[a].dimension_);
656       scm_gc_mark (s->dim_cache_[a].dimension_callback_);
657
658       /* Do not mark the parents.  The pointers in the mutable
659          property list form two tree like structures (one for X
660          relations, one for Y relations).  Marking these can be done
661          in limited stack space.  If we add the parents, we will jump
662          between X and Y in an erratic manner, leading to much more
663          recursion depth (and core dumps if we link to pthreads).  */
664     }
665
666   if (s->original_)
667     scm_gc_mark (s->original_->self_scm ());
668
669   if (s->pscore_)
670     scm_gc_mark (s->pscore_->self_scm ());
671
672   s->do_derived_mark ();
673   return s->mutable_property_alist_;
674 }
675
676 int
677 Grob::print_smob (SCM s, SCM port, scm_print_state *)
678 {
679   Grob *sc = (Grob *) SCM_CELL_WORD_1 (s);
680
681   scm_puts ("#<Grob ", port);
682   scm_puts ((char *) sc->name ().to_str0 (), port);
683
684   /* Do not print properties, that is too much hassle.  */
685   scm_puts (" >", port);
686   return 1;
687 }
688
689 SCM
690 Grob::do_derived_mark () const
691 {
692   return SCM_EOL;
693 }
694
695 void
696 Grob::discretionary_processing ()
697 {
698 }
699
700 bool
701 Grob::internal_has_interface (SCM k)
702 {
703   SCM ifs = get_property ("interfaces");
704
705   return scm_c_memq (k, ifs) != SCM_BOOL_F;
706 }
707
708 Grob *
709 Grob::get_parent (Axis a) const
710 {
711   return dim_cache_[a].parent_;
712 }
713
714 /** Return Array of Grobs in SCM list LST */
715 Link_array<Grob>
716 ly_scm2grobs (SCM lst)
717 {
718   Link_array<Grob> arr;
719
720   for (SCM s = lst; scm_is_pair (s); s = scm_cdr (s))
721     {
722       SCM e = scm_car (s);
723       arr.push (unsmob_grob (e));
724     }
725
726   arr.reverse ();
727   return arr;
728 }
729
730 Object_key const *
731 Grob::get_key () const
732 {
733   return key_;
734 }
735
736 /** Return SCM list of Grob array A */
737 SCM
738 ly_grobs2scm (Link_array<Grob> a)
739 {
740   SCM s = SCM_EOL;
741   for (int i = a.size (); i; i--)
742     s = scm_cons (a[i - 1]->self_scm (), s);
743
744   return s;
745 }
746
747 IMPLEMENT_TYPE_P (Grob, "ly:grob?");
748
749 ADD_INTERFACE (Grob, "grob-interface",
750                "A grob represents a piece of music notation\n"
751                "\n"
752                "All grobs have an X and Y-position on the page.  These X and Y positions\n"
753                "are stored in a relative format, so they can easily be combined by\n"
754                "stacking them, hanging one grob to the side of another, and coupling\n"
755                "them into a grouping objects.\n"
756                "\n"
757                "Each grob has a reference point (a.k.a.  parent): the position of a grob\n"
758                "is stored relative to that reference point. For example the X-reference\n"
759                "point of a staccato dot usually is the note head that it applies\n"
760                "to. When the note head is moved, the staccato dot moves along\n"
761                "automatically.\n"
762                "\n"
763                "A grob is often associated with a symbol, but some grobs do not print\n"
764                "any symbols. They take care of grouping objects. For example, there is a\n"
765                "separate grob that stacks staves vertically. The @ref{NoteCollision}\n"
766                "is also an abstract grob: it only moves around chords, but doesn't print\n"
767                "anything.\n"
768                "\n"
769                "Grobs have a properties: Scheme variables, that can be read and set. "
770                "They have two types. Immutable variables "
771                "define the default style and behavior.  They are shared between  many objects. "
772                "They can be changed using @code{\\override} and @code{\\revert}. "
773                "\n\n"
774                "Mutable properties are variables that are specific to one grob. Typically, "
775                "lists of other objects, or results from computations are stored in"
776                "mutable properties: every call to set-grob-property (or its C++ equivalent) "
777                "sets a mutable property. ",
778                "X-offset-callbacks Y-offset-callbacks X-extent-callback stencil cause "
779                "Y-extent-callback print-function extra-offset spacing-procedure "
780                "context staff-symbol interfaces dependencies X-extent Y-extent extra-X-extent "
781                "meta layer before-line-breaking-callback "
782                "color "
783                "axis-group-parent-X "
784                "axis-group-parent-Y "
785                "after-line-breaking-callback extra-Y-extent minimum-X-extent "
786                "minimum-Y-extent transparent tweak-count tweak-rank");
787