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