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