]> git.donarmstrong.com Git - lilypond.git/blob - lily/grob.cc
* scm/framework-pdf.scm (scm): new 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
29 #include "ly-smobs.icc"
30
31 Grob * 
32 Grob::clone (int count) const
33 {
34   return new Grob (*this, count);
35 }
36
37 /* TODO:
38
39   - remove dynamic_cast<Spanner, Item> and put this code into respective
40     subclass.  */
41
42 #define HASH_SIZE 3
43 #define INFINITY_MSG "Infinity or NaN encountered"
44
45 Grob::Grob (SCM basicprops,
46             Object_key const* key)
47 {
48   key_ = key;
49   /* FIXME: default should be no callback.  */
50   self_scm_ = SCM_EOL;
51   pscore_ = 0;
52   status_ = 0;
53   original_ = 0;
54   immutable_property_alist_ =  basicprops;
55   mutable_property_alist_ = SCM_EOL;
56
57   /* We do smobify_self () as the first step.  Since the object lives
58      on the heap, none of its SCM variables are protected from
59      GC. After smobify_self (), they are.  */
60   smobify_self ();
61
62   /*
63     We always get a new key object for a new grob.
64    */
65   scm_gc_unprotect_object (key_->self_scm ());
66   SCM meta = get_property ("meta");
67   if (scm_is_pair (meta))
68     {
69       SCM ifs = scm_assoc (ly_symbol2scm ("interfaces"), meta);
70
71       /* Switch off interface checks for the moment.  */
72       bool itc = do_internal_type_checking_global;
73       do_internal_type_checking_global = false;
74       internal_set_property (ly_symbol2scm ("interfaces"), scm_cdr (ifs));
75       do_internal_type_checking_global = itc;
76     }
77
78   /* TODO:
79
80     - destill this into a function, so we can re-init the immutable
81       properties with a new BASICPROPS value after
82       creation. Convenient eg. when using \override with
83       StaffSymbol.  */
84
85   char const*onames[] = {"X-offset-callbacks", "Y-offset-callbacks"};
86   char const*xnames[] = {"X-extent", "Y-extent"};
87   char const*enames[] = {"X-extent-callback", "Y-extent-callback"};
88
89   for (int a = X_AXIS; a <= Y_AXIS; a++)
90     {
91       SCM l = get_property (onames[a]);
92
93       if (scm_ilength (l) >= 0)
94         {
95           dim_cache_[a].offset_callbacks_ = l;
96           dim_cache_[a].offsets_left_ = scm_ilength (l);
97         }
98       else
99         programming_error ("[XY]-offset-callbacks must be a list");
100
101       SCM cb = get_property (enames[a]);
102       if (cb == SCM_BOOL_F)
103         {
104           dim_cache_[a].dimension_ = SCM_BOOL_F;
105         }
106       
107       SCM xt = get_property (xnames[a]);
108       if (is_number_pair (xt))
109         {
110           dim_cache_[a].dimension_ = xt;
111         }
112       else if (ly_c_procedure_p (cb))
113         {
114           dim_cache_[a].dimension_callback_ = cb;
115         }
116       else if (cb == SCM_EOL
117                && ly_c_procedure_p (get_property ("print-function")))
118         dim_cache_[a].dimension_callback_ = stencil_extent_proc;
119     }
120 }
121
122 Grob::Grob (Grob const &s, int copy_index)
123    : dim_cache_ (s.dim_cache_)
124 {
125   key_ = new Copied_key (s.key_, copy_index);
126   original_ = (Grob*) &s;
127   self_scm_ = SCM_EOL;
128
129   immutable_property_alist_ = s.immutable_property_alist_;
130   mutable_property_alist_ = SCM_EOL;
131
132   /* No properties are copied.  That is the job of
133      handle_broken_dependencies.  */
134   status_ = s.status_;
135   pscore_ = 0;
136
137   smobify_self ();
138   scm_gc_unprotect_object (key_->self_scm ());
139 }
140
141 Grob::~Grob ()
142 {
143 }
144
145 MAKE_SCHEME_CALLBACK (Grob, stencil_extent, 2);
146 SCM
147 Grob::stencil_extent (SCM element_smob, SCM scm_axis)
148 {
149   Grob *s = unsmob_grob (element_smob);
150   Axis a = (Axis) scm_to_int (scm_axis);
151
152   Stencil *m = s->get_stencil ();
153   Interval e;
154   if (m)
155     e = m->extent (a);
156   return ly_interval2scm (e);
157 }
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
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_c_procedure_p (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_c_procedure_p (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 = scm_list_3 (ly_symbol2scm ("grob-cause"), self_scm(),
245                                  m->expr ());
246           stil = Stencil (m->extent_box (), expr). smobbed_copy ();
247         }
248
249       /* color support... see interpret_stencil_expression() for more... */
250       SCM color = get_property ("color");
251       if (color != SCM_EOL)
252         {
253           m = unsmob_stencil (stil);
254           SCM expr = scm_list_3 (ly_symbol2scm ("color"),
255                                  color,
256                                  m->expr ());
257
258           stil = Stencil (m->extent_box (), expr).smobbed_copy();
259         }
260      }
261
262   return stil;
263 }
264
265 /*
266   VIRTUAL STUBS
267  */
268 void
269 Grob::do_break_processing ()
270 {
271 }
272
273 System *
274 Grob::get_system () const
275 {
276   return 0;
277 }
278
279 void
280 Grob::add_dependency (Grob *e)
281 {
282   if (e)
283     Pointer_group_interface::add_grob (this, ly_symbol2scm ("dependencies"),
284                                        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
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
436       if (get_parent (axis))
437         get_parent(axis)->flush_extent_cache (axis);
438     }
439 }
440
441 Interval
442 Grob::extent (Grob *refp, Axis a) const
443 {
444   Real x = relative_coordinate (refp, a);
445
446   Dimension_cache *d = (Dimension_cache *) &dim_cache_[a];
447   Interval ext;
448
449   SCM dimpair = d->dimension_;
450   if (scm_is_pair (dimpair))
451     ;
452   else if (ly_c_procedure_p (d->dimension_callback_)
453            && d->dimension_ == SCM_EOL)
454     d->dimension_ = scm_call_2 (d->dimension_callback_, self_scm (), scm_int2num (a));
455   else
456     return ext;
457
458   if (!scm_is_pair (d->dimension_))
459     return ext;
460
461   ext = ly_scm2interval (d->dimension_);
462
463   SCM extra = get_property (a == X_AXIS
464                             ? "extra-X-extent"
465                             : "extra-Y-extent");
466
467   /* Signs ?  */
468   if (scm_is_pair (extra))
469     {
470       ext[BIGGER] += scm_to_double (scm_cdr (extra));
471       ext[SMALLER] += scm_to_double (scm_car (extra));
472     }
473
474   extra = get_property (a == X_AXIS
475                         ? "minimum-X-extent"
476                         : "minimum-Y-extent");
477   if (scm_is_pair (extra))
478     ext.unite (Interval (scm_to_double (scm_car (extra)),
479                          scm_to_double (scm_cdr (extra))));
480
481   ext.translate (x);
482
483   return ext;
484 }
485
486 /* Find the group-element which has both #this# and #s#  */
487 Grob *
488 Grob::common_refpoint (Grob const *s, Axis a) const
489 {
490   /* I don't like the quadratic aspect of this code, but I see no
491      other way.  The largest chain of parents might be 10 high or so,
492      so it shouldn't be a real issue.  */
493   for (Grob const *c = this; c; c = c->dim_cache_[a].parent_)
494     for (Grob const *d = s; d; d = d->dim_cache_[a].parent_)
495       if (d == c)
496         return (Grob*) d;
497
498   return 0;
499 }
500
501 Grob *
502 common_refpoint_of_list (SCM elist, Grob *common, Axis a)
503 {
504   for (; scm_is_pair (elist); elist = scm_cdr (elist))
505     if (Grob *s = unsmob_grob (scm_car (elist)))
506       {
507         if (common)
508           common = common->common_refpoint (s, a);
509         else
510           common = s;
511       }
512
513   return common;
514 }
515
516 Grob *
517 common_refpoint_of_array (Link_array<Grob> const &arr, Grob *common, Axis a)
518 {
519   for (int i = arr.size (); i--; )
520     if (Grob *s = arr[i])
521       {
522         if (common)
523           common = common->common_refpoint (s, a);
524         else
525           common = s;
526       }
527
528   return common;
529 }
530
531 String
532 Grob::name () const
533 {
534   SCM meta = get_property ("meta");
535   SCM nm = scm_assoc (ly_symbol2scm ("name"), meta);
536   nm = (scm_is_pair (nm)) ? scm_cdr (nm) : SCM_EOL;
537   return scm_is_symbol (nm) ? ly_symbol2string (nm) : classname (this);
538 }
539
540 void
541 Grob::add_offset_callback (SCM cb, Axis a)
542 {
543   if (!has_offset_callback (cb, a))
544   {
545     dim_cache_[a].offset_callbacks_
546       = scm_cons (cb, dim_cache_[a].offset_callbacks_);
547     dim_cache_[a].offsets_left_ ++;
548   }
549 }
550
551 bool
552 Grob::has_extent_callback (SCM cb, Axis a) const
553 {
554   return scm_equal_p (cb, dim_cache_[a].dimension_callback_) == SCM_BOOL_T;
555 }
556
557 bool
558 Grob::has_offset_callback (SCM cb, Axis a)const
559 {
560   return scm_c_memq (cb, dim_cache_[a].offset_callbacks_) != SCM_BOOL_F;
561 }
562
563 void
564 Grob::set_extent (SCM dc, Axis a)
565 {
566   dim_cache_[a].dimension_ = dc;
567 }
568
569 void
570 Grob::set_extent_callback (SCM dc, Axis a)
571 {
572   dim_cache_[a].dimension_callback_ = dc;
573 }
574
575 void
576 Grob::set_parent (Grob *g, Axis a)
577 {
578   dim_cache_[a].parent_ = g;
579 }
580
581 MAKE_SCHEME_CALLBACK (Grob, fixup_refpoint, 1);
582 SCM
583 Grob::fixup_refpoint (SCM smob)
584 {
585   Grob *me = unsmob_grob (smob);
586   for (int a = X_AXIS; a < NO_AXES; a ++)
587     {
588       Axis ax = (Axis)a;
589       Grob *parent = me->get_parent (ax);
590
591       if (!parent)
592         continue;
593
594       if (parent->get_system () != me->get_system () && me->get_system ())
595         {
596           Grob *newparent = parent->find_broken_piece (me->get_system ());
597           me->set_parent (newparent, ax);
598         }
599
600       if (Item *i = dynamic_cast<Item*> (me))
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                   me->set_parent (newparent, ax);
611                 }
612             }
613         }
614     }
615   return smob;
616 }
617
618 void
619 Grob::warning (String s)const
620 {
621   SCM cause = self_scm ();
622   while (Grob *g = unsmob_grob (cause))
623     cause = g->get_property ("cause");
624
625   if (Music *m = unsmob_music (cause))
626     m->origin ()->warning (s);
627   else
628     ::warning (s);
629 }
630
631 void
632 Grob::programming_error (String s) const
633 {
634   s = "Programming error: " + s;
635   warning (s);
636 }
637
638
639 /****************************************************
640   SMOB funcs
641  ****************************************************/
642
643 IMPLEMENT_SMOBS (Grob);
644 IMPLEMENT_DEFAULT_EQUAL_P (Grob);
645
646 SCM
647 Grob::mark_smob (SCM ses)
648 {
649   Grob *s = (Grob*) SCM_CELL_WORD_1 (ses);
650   scm_gc_mark (s->immutable_property_alist_);
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   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
698
699 bool
700 Grob::internal_has_interface (SCM k)
701 {
702   SCM ifs = get_property ("interfaces");
703
704   return scm_c_memq (k, ifs) != SCM_BOOL_F;
705 }
706
707 Grob*
708 Grob::get_parent (Axis a) const
709 {
710   return dim_cache_[a].parent_;
711 }
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
748 IMPLEMENT_TYPE_P (Grob, "ly:grob?");
749
750 ADD_INTERFACE (Grob, "grob-interface",
751                "A grob represents a piece of music notation\n"
752                "\n"
753 "All grobs have an X and Y-position on the page.  These X and Y positions\n"
754 "are stored in a relative format, so they can easily be combined by\n"
755 "stacking them, hanging one grob to the side of another, and coupling\n"
756 "them into a grouping objects.\n"
757 "\n"
758 "Each grob has a reference point (a.k.a.  parent): the position of a grob\n"
759 "is stored relative to that reference point. For example the X-reference\n"
760 "point of a staccato dot usually is the note head that it applies\n"
761 "to. When the note head is moved, the staccato dot moves along\n"
762 "automatically.\n"
763 "\n"
764 "A grob is often associated with a symbol, but some grobs do not print\n"
765 "any symbols. They take care of grouping objects. For example, there is a\n"
766 "separate grob that stacks staves vertically. The @ref{NoteCollision}\n"
767 "is also an abstract grob: it only moves around chords, but doesn't print\n"
768 "anything.\n"
769 "\n"
770                "Grobs have a properties: Scheme variables, that can be read and set. "
771                "They have two types. Immutable variables "
772                "define the default style and behavior.  They are shared between  many objects. "
773                "They can be changed using @code{\\override} and @code{\\revert}. "
774                "\n\n"
775                "Mutable properties are variables that are specific to one grob. Typically, "
776                "lists of other objects, or results from computations are stored in"
777                "mutable properties: every call to set-grob-property (or its C++ equivalent) "
778                "sets a mutable property. "
779
780 ,
781                "X-offset-callbacks Y-offset-callbacks X-extent-callback stencil cause "
782                "Y-extent-callback print-function extra-offset spacing-procedure "
783                "context staff-symbol interfaces dependencies X-extent Y-extent extra-X-extent "
784                "meta layer before-line-breaking-callback "
785                "color "
786                "axis-group-parent-X "
787                "axis-group-parent-Y "
788                "after-line-breaking-callback extra-Y-extent minimum-X-extent "
789                "minimum-Y-extent transparent tweak-count tweak-rank"
790                );
791
792
793