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