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