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