]> git.donarmstrong.com Git - lilypond.git/blob - lily/grob.cc
* lily/include/paper-score.hh (class Paper_score): remove unused prototypes.
[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"), e);
285   else
286     programming_error ("null dependency added");
287 }
288
289 void
290 Grob::handle_broken_dependencies ()
291 {
292   Spanner *sp = dynamic_cast<Spanner *> (this);
293   if (original_ && sp)
294     return;
295
296   if (sp)
297     /* THIS, SP is the original spanner.  We use a special function
298        because some Spanners have enormously long lists in their
299        properties, and a special function fixes FOO  */
300     for (SCM s = mutable_property_alist_; scm_is_pair (s); s = scm_cdr (s))
301       sp->substitute_one_mutable_property (scm_caar (s), scm_cdar (s));
302
303   System *system = get_system ();
304
305   if (is_live ()
306       && system && common_refpoint (system, X_AXIS)
307       && common_refpoint (system, Y_AXIS))
308     substitute_mutable_properties (system
309                                    ? system->self_scm () : SCM_UNDEFINED,
310                                    mutable_property_alist_);
311   else if (dynamic_cast<System *> (this))
312     substitute_mutable_properties (SCM_UNDEFINED, mutable_property_alist_);
313   else
314     /* THIS element is `invalid'; it has been removed from all
315        dependencies, so let's junk the element itself.
316
317        Do not do this for System, since that would remove references
318        to the originals of score-grobs, which get then GC'd (a bad
319        thing).  */
320     suicide ();
321 }
322
323 /* Note that we still want references to this element to be
324    rearranged, and not silently thrown away, so we keep pointers like
325    {broken_into_{drul, array}, original}
326 */
327 void
328 Grob::suicide ()
329 {
330   if (!is_live ())
331     return;
332
333   mutable_property_alist_ = SCM_EOL;
334   immutable_property_alist_ = SCM_EOL;
335
336   set_extent (SCM_EOL, Y_AXIS);
337   set_extent (SCM_EOL, X_AXIS);
338
339   set_extent_callback (SCM_EOL, Y_AXIS);
340   set_extent_callback (SCM_EOL, X_AXIS);
341
342   for (int a = X_AXIS; a <= Y_AXIS; a++)
343     {
344       dim_cache_[a].offset_callbacks_ = SCM_EOL;
345       dim_cache_[a].offsets_left_ = 0;
346     }
347 }
348
349 void
350 Grob::handle_prebroken_dependencies ()
351 {
352   /* Don't do this in the derived method, since we want to keep access to
353      mutable_property_alist_ centralized.  */
354   if (original_)
355     {
356       Item *it = dynamic_cast<Item *> (this);
357       substitute_mutable_properties (scm_int2num (it->break_status_dir ()),
358                                      original_->mutable_property_alist_);
359     }
360 }
361
362 Grob *
363 Grob::find_broken_piece (System *) const
364 {
365   return 0;
366 }
367
368 /* Translate in one direction.  */
369 void
370 Grob::translate_axis (Real y, Axis a)
371 {
372   if (isinf (y) || isnan (y))
373     programming_error (_ (INFINITY_MSG));
374   else
375     dim_cache_[a].offset_ += y;
376 }
377
378 /* Find the offset relative to D.  If D equals THIS, then it is 0.
379    Otherwise, it recursively defd as
380
381    OFFSET_ + PARENT_L_->relative_coordinate (D) */
382 Real
383 Grob::relative_coordinate (Grob const *refp, Axis a) const
384 {
385   if (refp == this)
386     return 0.0;
387
388   /* We catch PARENT_L_ == nil case with this, but we crash if we did
389      not ask for the absolute coordinate (ie. REFP == nil.)  */
390   if (refp == dim_cache_[a].parent_)
391     return get_offset (a);
392
393   return get_offset (a) + dim_cache_[a].parent_->relative_coordinate (refp, a);
394 }
395
396 /* Invoke callbacks to get offset relative to parent.  */
397 Real
398 Grob::get_offset (Axis a) const
399 {
400   Grob *me = (Grob *) this;
401   while (dim_cache_[a].offsets_left_)
402     {
403       int l = --me->dim_cache_[a].offsets_left_;
404       SCM cb = scm_list_ref (dim_cache_[a].offset_callbacks_, scm_int2num (l));
405       SCM retval = scm_call_2 (cb, self_scm (), scm_int2num (a));
406
407       Real r = scm_to_double (retval);
408       if (isinf (r) || isnan (r))
409         {
410           programming_error (INFINITY_MSG);
411           r = 0.0;
412         }
413       me->dim_cache_[a].offset_ += r;
414     }
415   return dim_cache_[a].offset_;
416 }
417
418 bool
419 Grob::is_empty (Axis a) const
420 {
421   return !(scm_is_pair (dim_cache_[a].dimension_)
422            || ly_c_procedure_p (dim_cache_[a].dimension_callback_));
423 }
424
425 void
426 Grob::flush_extent_cache (Axis axis)
427 {
428   Dimension_cache *d = &dim_cache_[axis];
429   if (ly_c_procedure_p (d->dimension_callback_)
430       && scm_is_pair (d->dimension_))
431     {
432       d->dimension_ = SCM_EOL;
433
434       if (get_parent (axis))
435         get_parent (axis)->flush_extent_cache (axis);
436     }
437 }
438
439 Interval
440 Grob::extent (Grob *refp, Axis a) const
441 {
442   Real x = relative_coordinate (refp, a);
443
444   Dimension_cache *d = (Dimension_cache *) & dim_cache_[a];
445   Interval ext;
446
447   SCM dimpair = d->dimension_;
448   if (scm_is_pair (dimpair))
449     ;
450   else if (ly_c_procedure_p (d->dimension_callback_)
451            && d->dimension_ == SCM_EOL)
452     d->dimension_ = scm_call_2 (d->dimension_callback_, self_scm (), scm_int2num (a));
453   else
454     return ext;
455
456   if (!scm_is_pair (d->dimension_))
457     return ext;
458
459   ext = ly_scm2interval (d->dimension_);
460
461   SCM extra = get_property (a == X_AXIS
462                             ? "extra-X-extent"
463                             : "extra-Y-extent");
464
465   /* Signs ?  */
466   if (scm_is_pair (extra))
467     {
468       ext[BIGGER] += scm_to_double (scm_cdr (extra));
469       ext[SMALLER] += scm_to_double (scm_car (extra));
470     }
471
472   extra = get_property (a == X_AXIS
473                         ? "minimum-X-extent"
474                         : "minimum-Y-extent");
475   if (scm_is_pair (extra))
476     ext.unite (Interval (scm_to_double (scm_car (extra)),
477                          scm_to_double (scm_cdr (extra))));
478
479   ext.translate (x);
480
481   return ext;
482 }
483
484 /* Find the group-element which has both #this# and #s#  */
485 Grob *
486 Grob::common_refpoint (Grob const *s, Axis a) const
487 {
488   /* I don't like the quadratic aspect of this code, but I see no
489      other way.  The largest chain of parents might be 10 high or so,
490      so it shouldn't be a real issue.  */
491   for (Grob const *c = this; c; c = c->dim_cache_[a].parent_)
492     for (Grob const *d = s; d; d = d->dim_cache_[a].parent_)
493       if (d == c)
494         return (Grob *) d;
495
496   return 0;
497 }
498
499 Grob *
500 common_refpoint_of_list (SCM elist, Grob *common, Axis a)
501 {
502   for (; scm_is_pair (elist); elist = scm_cdr (elist))
503     if (Grob *s = unsmob_grob (scm_car (elist)))
504       {
505         if (common)
506           common = common->common_refpoint (s, a);
507         else
508           common = s;
509       }
510
511   return common;
512 }
513
514 Grob *
515 common_refpoint_of_array (Link_array<Grob> const &arr, Grob *common, Axis a)
516 {
517   for (int i = arr.size (); i--;)
518     if (Grob *s = arr[i])
519       {
520         if (common)
521           common = common->common_refpoint (s, a);
522         else
523           common = s;
524       }
525
526   return common;
527 }
528
529 String
530 Grob::name () const
531 {
532   SCM meta = get_property ("meta");
533   SCM nm = scm_assoc (ly_symbol2scm ("name"), meta);
534   nm = (scm_is_pair (nm)) ? scm_cdr (nm) : SCM_EOL;
535   return scm_is_symbol (nm) ? ly_symbol2string (nm) : classname (this);
536 }
537
538 void
539 Grob::add_offset_callback (SCM cb, Axis a)
540 {
541   if (!has_offset_callback (cb, a))
542     {
543       dim_cache_[a].offset_callbacks_
544         = scm_cons (cb, dim_cache_[a].offset_callbacks_);
545       dim_cache_[a].offsets_left_++;
546     }
547 }
548
549 bool
550 Grob::has_extent_callback (SCM cb, Axis a) const
551 {
552   return scm_equal_p (cb, dim_cache_[a].dimension_callback_) == SCM_BOOL_T;
553 }
554
555 bool
556 Grob::has_offset_callback (SCM cb, Axis a) const
557 {
558   return scm_c_memq (cb, dim_cache_[a].offset_callbacks_) != SCM_BOOL_F;
559 }
560
561 void
562 Grob::set_extent (SCM dc, Axis a)
563 {
564   dim_cache_[a].dimension_ = dc;
565 }
566
567 void
568 Grob::set_extent_callback (SCM dc, Axis a)
569 {
570   dim_cache_[a].dimension_callback_ = dc;
571 }
572
573 void
574 Grob::set_parent (Grob *g, Axis a)
575 {
576   dim_cache_[a].parent_ = g;
577 }
578
579 MAKE_SCHEME_CALLBACK (Grob, fixup_refpoint, 1);
580 SCM
581 Grob::fixup_refpoint (SCM smob)
582 {
583   Grob *me = unsmob_grob (smob);
584   for (int a = X_AXIS; a < NO_AXES; a++)
585     {
586       Axis ax = (Axis)a;
587       Grob *parent = me->get_parent (ax);
588
589       if (!parent)
590         continue;
591
592       if (parent->get_system () != me->get_system () && me->get_system ())
593         {
594           Grob *newparent = parent->find_broken_piece (me->get_system ());
595           me->set_parent (newparent, ax);
596         }
597
598       if (Item *i = dynamic_cast<Item *> (me))
599         {
600           Item *parenti = dynamic_cast<Item *> (parent);
601
602           if (parenti && i)
603             {
604               Direction my_dir = i->break_status_dir ();
605               if (my_dir != parenti->break_status_dir ())
606                 {
607                   Item *newparent = parenti->find_prebroken_piece (my_dir);
608                   me->set_parent (newparent, ax);
609                 }
610             }
611         }
612     }
613   return smob;
614 }
615
616 void
617 Grob::warning (String s) const
618 {
619   SCM cause = self_scm ();
620   while (Grob *g = unsmob_grob (cause))
621     cause = g->get_property ("cause");
622
623   if (Music *m = unsmob_music (cause))
624     m->origin ()->warning (s);
625   else
626     ::warning (s);
627 }
628
629 void
630 Grob::programming_error (String s) const
631 {
632   s = _f ("programming error: %s", s);
633   message (s);
634 }
635
636 /****************************************************
637   SMOB funcs
638 ****************************************************/
639
640 IMPLEMENT_SMOBS (Grob);
641 IMPLEMENT_DEFAULT_EQUAL_P (Grob);
642
643 SCM
644 Grob::mark_smob (SCM ses)
645 {
646   Grob *s = (Grob *) SCM_CELL_WORD_1 (ses);
647   scm_gc_mark (s->immutable_property_alist_);
648   scm_gc_mark (s->key_->self_scm ());
649   for (int a = 0; a < 2; a++)
650     {
651       scm_gc_mark (s->dim_cache_[a].offset_callbacks_);
652       scm_gc_mark (s->dim_cache_[a].dimension_);
653       scm_gc_mark (s->dim_cache_[a].dimension_callback_);
654
655       /* Do not mark the parents.  The pointers in the mutable
656          property list form two tree like structures (one for X
657          relations, one for Y relations).  Marking these can be done
658          in limited stack space.  If we add the parents, we will jump
659          between X and Y in an erratic manner, leading to much more
660          recursion depth (and core dumps if we link to pthreads).  */
661     }
662
663   if (s->original_)
664     scm_gc_mark (s->original_->self_scm ());
665
666   if (s->pscore_ && s->pscore_->layout ())
667     scm_gc_mark (s->pscore_->layout ()->self_scm ());
668
669   s->do_derived_mark ();
670   return s->mutable_property_alist_;
671 }
672
673 int
674 Grob::print_smob (SCM s, SCM port, scm_print_state *)
675 {
676   Grob *sc = (Grob *) SCM_CELL_WORD_1 (s);
677
678   scm_puts ("#<Grob ", port);
679   scm_puts ((char *) sc->name ().to_str0 (), port);
680
681   /* Do not print properties, that is too much hassle.  */
682   scm_puts (" >", port);
683   return 1;
684 }
685
686 SCM
687 Grob::do_derived_mark () const
688 {
689   return SCM_EOL;
690 }
691
692 void
693 Grob::discretionary_processing ()
694 {
695 }
696
697 bool
698 Grob::internal_has_interface (SCM k)
699 {
700   SCM ifs = get_property ("interfaces");
701
702   return scm_c_memq (k, ifs) != SCM_BOOL_F;
703 }
704
705 Grob *
706 Grob::get_parent (Axis a) const
707 {
708   return dim_cache_[a].parent_;
709 }
710
711 /** Return Array of Grobs in SCM list LST */
712 Link_array<Grob>
713 ly_scm2grobs (SCM lst)
714 {
715   Link_array<Grob> arr;
716
717   for (SCM s = lst; scm_is_pair (s); s = scm_cdr (s))
718     {
719       SCM e = scm_car (s);
720       arr.push (unsmob_grob (e));
721     }
722
723   arr.reverse ();
724   return arr;
725 }
726
727 Object_key const *
728 Grob::get_key () const
729 {
730   return key_;
731 }
732
733 /** Return SCM list of Grob array A */
734 SCM
735 ly_grobs2scm (Link_array<Grob> a)
736 {
737   SCM s = SCM_EOL;
738   for (int i = a.size (); i; i--)
739     s = scm_cons (a[i - 1]->self_scm (), s);
740
741   return s;
742 }
743
744 IMPLEMENT_TYPE_P (Grob, "ly:grob?");
745
746 ADD_INTERFACE (Grob, "grob-interface",
747                "A grob represents a piece of music notation\n"
748                "\n"
749                "All grobs have an X and Y-position on the page.  These X and Y positions\n"
750                "are stored in a relative format, so they can easily be combined by\n"
751                "stacking them, hanging one grob to the side of another, and coupling\n"
752                "them into a grouping objects.\n"
753                "\n"
754                "Each grob has a reference point (a.k.a.  parent): the position of a grob\n"
755                "is stored relative to that reference point. For example the X-reference\n"
756                "point of a staccato dot usually is the note head that it applies\n"
757                "to. When the note head is moved, the staccato dot moves along\n"
758                "automatically.\n"
759                "\n"
760                "A grob is often associated with a symbol, but some grobs do not print\n"
761                "any symbols. They take care of grouping objects. For example, there is a\n"
762                "separate grob that stacks staves vertically. The @ref{NoteCollision}\n"
763                "is also an abstract grob: it only moves around chords, but doesn't print\n"
764                "anything.\n"
765                "\n"
766                "Grobs have a properties: Scheme variables, that can be read and set. "
767                "They have two types. Immutable variables "
768                "define the default style and behavior.  They are shared between  many objects. "
769                "They can be changed using @code{\\override} and @code{\\revert}. "
770                "\n\n"
771                "Mutable properties are variables that are specific to one grob. Typically, "
772                "lists of other objects, or results from computations are stored in"
773                "mutable properties: every call to set-grob-property (or its C++ equivalent) "
774                "sets a mutable property. ",
775                "X-offset-callbacks Y-offset-callbacks X-extent-callback stencil cause "
776                "Y-extent-callback print-function extra-offset spacing-procedure "
777                "context staff-symbol interfaces dependencies X-extent Y-extent extra-X-extent "
778                "meta layer before-line-breaking-callback "
779                "color "
780                "axis-group-parent-X "
781                "axis-group-parent-Y "
782                "after-line-breaking-callback extra-Y-extent minimum-X-extent "
783                "minimum-Y-extent transparent tweak-count tweak-rank");
784