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