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