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