]> git.donarmstrong.com Git - lilypond.git/blob - lily/grob.cc
*** empty log message ***
[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   scm_gc_unprotect_object (key_->self_scm ());
67   SCM meta = get_property ("meta");
68   if (scm_is_pair (meta))
69     {
70       SCM ifs = scm_assoc (ly_symbol2scm ("interfaces"), meta);
71
72       /* Switch off interface checks for the moment.  */
73       bool itc = do_internal_type_checking_global;
74       do_internal_type_checking_global = false;
75       internal_set_property (ly_symbol2scm ("interfaces"), scm_cdr (ifs));
76       do_internal_type_checking_global = itc;
77     }
78
79   /* TODO:
80
81   - destill this into a function, so we can re-init the immutable
82   properties with a new BASICPROPS value after
83   creation. Convenient eg. when using \override with
84   StaffSymbol.  */
85
86   char const *onames[] = {"X-offset-callbacks", "Y-offset-callbacks"};
87   char const *xnames[] = {"X-extent", "Y-extent"};
88   char const *enames[] = {"X-extent-callback", "Y-extent-callback"};
89
90   for (int a = X_AXIS; a <= Y_AXIS; a++)
91     {
92       SCM l = get_property (onames[a]);
93
94       if (scm_ilength (l) >= 0)
95         {
96           dim_cache_[a].offset_callbacks_ = l;
97           dim_cache_[a].offsets_left_ = scm_ilength (l);
98         }
99       else
100         programming_error ("[XY]-offset-callbacks must be a list");
101
102       SCM cb = get_property (enames[a]);
103       if (cb == SCM_BOOL_F)
104         {
105           dim_cache_[a].dimension_ = SCM_BOOL_F;
106         }
107
108       SCM xt = get_property (xnames[a]);
109       if (is_number_pair (xt))
110         {
111           dim_cache_[a].dimension_ = xt;
112         }
113       else if (ly_c_procedure_p (cb))
114         {
115           dim_cache_[a].dimension_callback_ = cb;
116         }
117       else if (cb == SCM_EOL
118                && ly_c_procedure_p (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_ = new Copied_key (s.key_, copy_index);
127   original_ = (Grob *) & s;
128   self_scm_ = SCM_EOL;
129
130   immutable_property_alist_ = s.immutable_property_alist_;
131   mutable_property_alist_ = SCM_EOL;
132
133   /* No properties are copied.  That is the job of
134      handle_broken_dependencies.  */
135   status_ = s.status_;
136   pscore_ = 0;
137
138   smobify_self ();
139   scm_gc_unprotect_object (key_->self_scm ());
140 }
141
142 Grob::~Grob ()
143 {
144 }
145
146 MAKE_SCHEME_CALLBACK (Grob, stencil_extent, 2);
147 SCM
148 Grob::stencil_extent (SCM element_smob, SCM scm_axis)
149 {
150   Grob *s = unsmob_grob (element_smob);
151   Axis a = (Axis) scm_to_int (scm_axis);
152
153   Stencil *m = s->get_stencil ();
154   Interval e;
155   if (m)
156     e = m->extent (a);
157   return ly_interval2scm (e);
158 }
159
160 Interval
161 robust_relative_extent (Grob *me, Grob *refp, Axis a)
162 {
163   Interval ext = me->extent (refp, a);
164   if (ext.is_empty ())
165     {
166       ext.add_point (me->relative_coordinate (refp, a));
167     }
168
169   return ext;
170 }
171
172 Output_def *
173 Grob::get_layout () const
174 {
175   return pscore_ ? pscore_->layout_ : 0;
176 }
177
178 /* Recursively track all dependencies of this Grob.  The status_ field
179    is used as a mark-field.  It is marked with BUSY during execution
180    of this function, and marked with FINAL when finished.
181
182    FUNCPTR is the function to call to update this element.  */
183 void
184 Grob::calculate_dependencies (int final, int busy, SCM funcname)
185 {
186   if (status_ >= final)
187     return;
188
189   if (status_ == busy)
190     {
191       programming_error ("Element is busy, come back later");
192       return;
193     }
194
195   status_ = busy;
196
197   for (SCM d = get_property ("dependencies"); scm_is_pair (d);
198        d = scm_cdr (d))
199     unsmob_grob (scm_car (d))->calculate_dependencies (final, busy, funcname);
200
201   SCM proc = internal_get_property (funcname);
202   if (ly_c_procedure_p (proc))
203     scm_call_1 (proc, this->self_scm ());
204
205   status_ = final;
206 }
207
208 Stencil *
209 Grob::get_stencil () const
210 {
211   if (!is_live ())
212     return 0;
213
214   SCM stil = get_property ("stencil");
215   if (unsmob_stencil (stil))
216     return unsmob_stencil (stil);
217
218   stil = get_uncached_stencil ();
219   if (is_live ())
220     {
221       Grob *me = (Grob *) this;
222       me->set_property ("stencil", stil);
223     }
224
225   return unsmob_stencil (stil);
226 }
227
228 SCM
229 Grob::get_uncached_stencil () const
230 {
231   SCM proc = get_property ("print-function");
232
233   SCM stil = SCM_EOL;
234   if (ly_c_procedure_p (proc))
235     stil = scm_apply_0 (proc, scm_list_n (this->self_scm (), SCM_UNDEFINED));
236
237   if (Stencil *m = unsmob_stencil (stil))
238     {
239       if (to_boolean (get_property ("transparent")))
240         stil = Stencil (m->extent_box (), SCM_EOL).smobbed_copy ();
241       else
242         {
243           SCM expr = m->expr ();
244           if (point_and_click_global)
245             expr = scm_list_3 (ly_symbol2scm ("grob-cause"), self_scm (), expr);
246           
247           stil = Stencil (m->extent_box (), expr).smobbed_copy ();
248         }
249
250       /* color support... see interpret_stencil_expression () for more... */
251       SCM color = get_property ("color");
252       if (color != SCM_EOL)
253         {
254           m = unsmob_stencil (stil);
255           SCM expr = scm_list_3 (ly_symbol2scm ("color"),
256                                  color,
257                                  m->expr ());
258
259           stil = Stencil (m->extent_box (), expr).smobbed_copy ();
260         }
261     }
262
263   return stil;
264 }
265
266 /*
267   VIRTUAL STUBS
268 */
269 void
270 Grob::do_break_processing ()
271 {
272 }
273
274 System *
275 Grob::get_system () const
276 {
277   return 0;
278 }
279
280 void
281 Grob::add_dependency (Grob *e)
282 {
283   if (e)
284     Pointer_group_interface::add_grob (this, ly_symbol2scm ("dependencies"),
285                                        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_c_procedure_p (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_c_procedure_p (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_c_procedure_p (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 = "Programming error: " + s;
634   warning (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   scm_gc_mark (s->key_->self_scm ());
650   for (int a = 0; a < 2; a++)
651     {
652       scm_gc_mark (s->dim_cache_[a].offset_callbacks_);
653       scm_gc_mark (s->dim_cache_[a].dimension_);
654       scm_gc_mark (s->dim_cache_[a].dimension_callback_);
655
656       /* Do not mark the parents.  The pointers in the mutable
657          property list form two tree like structures (one for X
658          relations, one for Y relations).  Marking these can be done
659          in limited stack space.  If we add the parents, we will jump
660          between X and Y in an erratic manner, leading to much more
661          recursion depth (and core dumps if we link to pthreads).  */
662     }
663
664   if (s->original_)
665     scm_gc_mark (s->original_->self_scm ());
666
667   if (s->pscore_ && s->pscore_->layout_)
668     scm_gc_mark (s->pscore_->layout_->self_scm ());
669
670   s->do_derived_mark ();
671   return s->mutable_property_alist_;
672 }
673
674 int
675 Grob::print_smob (SCM s, SCM port, scm_print_state *)
676 {
677   Grob *sc = (Grob *) SCM_CELL_WORD_1 (s);
678
679   scm_puts ("#<Grob ", port);
680   scm_puts ((char *) sc->name ().to_str0 (), port);
681
682   /* Do not print properties, that is too much hassle.  */
683   scm_puts (" >", port);
684   return 1;
685 }
686
687 SCM
688 Grob::do_derived_mark () const
689 {
690   return SCM_EOL;
691 }
692
693 void
694 Grob::discretionary_processing ()
695 {
696 }
697
698 bool
699 Grob::internal_has_interface (SCM k)
700 {
701   SCM ifs = get_property ("interfaces");
702
703   return scm_c_memq (k, ifs) != SCM_BOOL_F;
704 }
705
706 Grob *
707 Grob::get_parent (Axis a) const
708 {
709   return dim_cache_[a].parent_;
710 }
711
712 /** Return Array of Grobs in SCM list LST */
713 Link_array<Grob>
714 ly_scm2grobs (SCM lst)
715 {
716   Link_array<Grob> arr;
717
718   for (SCM s = lst; scm_is_pair (s); s = scm_cdr (s))
719     {
720       SCM e = scm_car (s);
721       arr.push (unsmob_grob (e));
722     }
723
724   arr.reverse ();
725   return arr;
726 }
727
728 Object_key const *
729 Grob::get_key () const
730 {
731   return key_;
732 }
733
734 /** Return SCM list of Grob array A */
735 SCM
736 ly_grobs2scm (Link_array<Grob> a)
737 {
738   SCM s = SCM_EOL;
739   for (int i = a.size (); i; i--)
740     s = scm_cons (a[i - 1]->self_scm (), s);
741
742   return s;
743 }
744
745 IMPLEMENT_TYPE_P (Grob, "ly:grob?");
746
747 ADD_INTERFACE (Grob, "grob-interface",
748                "A grob represents a piece of music notation\n"
749                "\n"
750                "All grobs have an X and Y-position on the page.  These X and Y positions\n"
751                "are stored in a relative format, so they can easily be combined by\n"
752                "stacking them, hanging one grob to the side of another, and coupling\n"
753                "them into a grouping objects.\n"
754                "\n"
755                "Each grob has a reference point (a.k.a.  parent): the position of a grob\n"
756                "is stored relative to that reference point. For example the X-reference\n"
757                "point of a staccato dot usually is the note head that it applies\n"
758                "to. When the note head is moved, the staccato dot moves along\n"
759                "automatically.\n"
760                "\n"
761                "A grob is often associated with a symbol, but some grobs do not print\n"
762                "any symbols. They take care of grouping objects. For example, there is a\n"
763                "separate grob that stacks staves vertically. The @ref{NoteCollision}\n"
764                "is also an abstract grob: it only moves around chords, but doesn't print\n"
765                "anything.\n"
766                "\n"
767                "Grobs have a properties: Scheme variables, that can be read and set. "
768                "They have two types. Immutable variables "
769                "define the default style and behavior.  They are shared between  many objects. "
770                "They can be changed using @code{\\override} and @code{\\revert}. "
771                "\n\n"
772                "Mutable properties are variables that are specific to one grob. Typically, "
773                "lists of other objects, or results from computations are stored in"
774                "mutable properties: every call to set-grob-property (or its C++ equivalent) "
775                "sets a mutable property. ",
776                "X-offset-callbacks Y-offset-callbacks X-extent-callback stencil cause "
777                "Y-extent-callback print-function extra-offset spacing-procedure "
778                "context staff-symbol interfaces dependencies X-extent Y-extent extra-X-extent "
779                "meta layer before-line-breaking-callback "
780                "color "
781                "axis-group-parent-X "
782                "axis-group-parent-Y "
783                "after-line-breaking-callback extra-Y-extent minimum-X-extent "
784                "minimum-Y-extent transparent tweak-count tweak-rank");
785