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