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