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