]> git.donarmstrong.com Git - lilypond.git/blob - lily/grob.cc
* lily/main.cc (LY_DEFINE): add gmane address.
[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
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   mutable_property_alist_ = SCM_EOL;
214   object_alist_ = SCM_EOL;
215   immutable_property_alist_ = SCM_EOL;
216   interfaces_ = SCM_EOL;
217 }
218
219 void
220 Grob::handle_prebroken_dependencies ()
221 {
222   /* Don't do this in the derived method, since we want to keep access to
223      object_alist_ centralized.  */
224   if (original ())
225     {
226       Item *it = dynamic_cast<Item *> (this);
227       substitute_object_links (scm_from_int (it->break_status_dir ()),
228                                original ()->object_alist_);
229     }
230 }
231
232 Grob *
233 Grob::find_broken_piece (System *) const
234 {
235   return 0;
236 }
237
238 /****************************************************************
239    OFFSETS
240 ****************************************************************/
241
242 void
243 Grob::translate_axis (Real y, Axis a)
244 {
245   if (isinf (y) || isnan (y))
246     {
247       programming_error (_ ("Infinity or NaN encountered"));
248       return ;
249     }
250   
251   if (!dim_cache_[a].offset_)
252     dim_cache_[a].offset_ = new Real (y);
253   else
254     *dim_cache_[a].offset_ += y;  
255 }
256
257 /* Find the offset relative to D.  If D equals THIS, then it is 0.
258    Otherwise, it recursively defd as
259
260    OFFSET_ + PARENT_L_->relative_coordinate (D) */
261 Real
262 Grob::relative_coordinate (Grob const *refp, Axis a) const
263 {
264   if (refp == this)
265     return 0.0;
266
267   /* We catch PARENT_L_ == nil case with this, but we crash if we did
268      not ask for the absolute coordinate (ie. REFP == nil.)  */
269   Real off = get_offset (a);
270   if (refp == dim_cache_[a].parent_)
271     return off;
272   
273   off += dim_cache_[a].parent_->relative_coordinate (refp, a);
274
275   return off;
276 }
277
278 /* Invoke callbacks to get offset relative to parent.  */
279 Real
280 Grob::get_offset (Axis a) const
281 {
282   if (dim_cache_[a].offset_)
283     return *dim_cache_[a].offset_;
284
285   Grob *me = (Grob *) this;
286
287   SCM sym = axis_offset_symbol (a);
288   me->dim_cache_[a].offset_ = new Real (0.0);
289
290   /*
291     UGH: can't fold next 2 statements together. Apparently GCC thinks
292     dim_cache_[a].offset_ is unaliased.
293   */
294   Real off = robust_scm2double (internal_get_property (sym), 0.0);
295   *me->dim_cache_[a].offset_ += off;
296
297   me->del_property (sym);
298   return *me->dim_cache_[a].offset_;
299 }
300
301
302 /****************************************************************
303   extents
304 ****************************************************************/
305
306 void
307 Grob::flush_extent_cache (Axis axis)
308 {
309   if (dim_cache_[axis].extent_)
310     {
311       /*
312         Ugh, this is not accurate; will flush property, causing
313         callback to be called if.
314        */
315       del_property ((axis == X_AXIS) ? ly_symbol2scm ("X-extent") : ly_symbol2scm ("Y-extent"));
316       delete dim_cache_[axis].extent_;
317       dim_cache_[axis].extent_ = 0;
318       if (get_parent (axis))
319         get_parent (axis)->flush_extent_cache (axis);
320     }
321 }
322
323
324 Interval
325 Grob::extent (Grob *refp, Axis a) const
326 {
327   Real offset = relative_coordinate (refp, a);
328   Interval real_ext;
329   if (dim_cache_[a].extent_)
330     {
331       real_ext = *dim_cache_[a].extent_;
332     }
333   else
334     {
335       SCM min_ext_sym =
336         (a == X_AXIS)
337         ? ly_symbol2scm ("minimum-X-extent")
338         : ly_symbol2scm ("minimum-Y-extent");
339
340       SCM ext_sym =
341         (a == X_AXIS)
342         ? ly_symbol2scm ("X-extent")
343         : ly_symbol2scm ("Y-extent");
344   
345       SCM min_ext = internal_get_property (min_ext_sym);
346       SCM ext = internal_get_property (ext_sym);
347
348       if (is_number_pair (min_ext))
349         real_ext.unite (ly_scm2interval (min_ext));
350       if (is_number_pair (ext))
351         real_ext.unite (ly_scm2interval (ext));
352
353       ((Grob*)this)->del_property (ext_sym);
354       ((Grob*)this)->dim_cache_[a].extent_ = new Interval (real_ext);  
355     }
356   
357   real_ext.translate (offset);
358   
359   return real_ext;
360 }
361
362 /****************************************************************
363   REFPOINTS
364 ****************************************************************/
365
366 /* Find the group-element which has both #this# and #s#  */
367 Grob *
368 Grob::common_refpoint (Grob const *s, Axis a) const
369 {
370   /* I don't like the quadratic aspect of this code, but I see no
371      other way.  The largest chain of parents might be 10 high or so,
372      so it shouldn't be a real issue.  */
373   for (Grob const *c = this; c; c = c->dim_cache_[a].parent_)
374     for (Grob const *d = s; d; d = d->dim_cache_[a].parent_)
375       if (d == c)
376         return (Grob *) d;
377
378   return 0;
379 }
380
381 void
382 Grob::set_parent (Grob *g, Axis a)
383 {
384   dim_cache_[a].parent_ = g;
385 }
386
387 Grob *
388 Grob::get_parent (Axis a) const
389 {
390   return dim_cache_[a].parent_;
391 }
392
393
394 void
395 Grob::fixup_refpoint ()
396 {
397   for (int a = X_AXIS; a < NO_AXES; a++)
398     {
399       Axis ax = (Axis)a;
400       Grob *parent = get_parent (ax);
401
402       if (!parent)
403         continue;
404
405       if (parent->get_system () != get_system () && get_system ())
406         {
407           Grob *newparent = parent->find_broken_piece (get_system ());
408           set_parent (newparent, ax);
409         }
410
411       if (Item *i = dynamic_cast<Item *> (this))
412         {
413           Item *parenti = dynamic_cast<Item *> (parent);
414
415           if (parenti && i)
416             {
417               Direction my_dir = i->break_status_dir ();
418               if (my_dir != parenti->break_status_dir ())
419                 {
420                   Item *newparent = parenti->find_prebroken_piece (my_dir);
421                   set_parent (newparent, ax);
422                 }
423             }
424         }
425     }
426 }
427
428
429 /****************************************************************
430   MESSAGES
431 ****************************************************************/
432 void
433 Grob::warning (String s) const
434 {
435   SCM cause = self_scm ();
436   while (Grob *g = unsmob_grob (cause))
437     cause = g->get_property ("cause");
438
439   if (Music *m = unsmob_music (cause))
440     m->origin ()->warning (s);
441   else
442     ::warning (s);
443 }
444
445
446 String
447 Grob::name () const
448 {
449   SCM meta = get_property ("meta");
450   SCM nm = scm_assq (ly_symbol2scm ("name"), meta);
451   nm = (scm_is_pair (nm)) ? scm_cdr (nm) : SCM_EOL;
452   return scm_is_symbol (nm) ? ly_symbol2string (nm) : this->class_name ();
453 }
454
455 void
456 Grob::programming_error (String s) const
457 {
458   SCM cause = self_scm ();
459   while (Grob *g = unsmob_grob (cause))
460     cause = g->get_property ("cause");
461
462   s = _f ("programming error: %s", s);
463
464   if (Music *m = unsmob_music (cause))
465     m->origin ()->message (s);
466   else
467     ::message (s);
468 }
469
470
471 ADD_INTERFACE (Grob, "grob-interface",
472                "A grob represents a piece of music notation\n"
473                "\n"
474                "All grobs have an X and Y-position on the page.  These X and Y positions\n"
475                "are stored in a relative format, so they can easily be combined by\n"
476                "stacking them, hanging one grob to the side of another, and coupling\n"
477                "them into a grouping objects.\n"
478                "\n"
479                "Each grob has a reference point (a.k.a.  parent): the position of a grob\n"
480                "is stored relative to that reference point. For example the X-reference\n"
481                "point of a staccato dot usually is the note head that it applies\n"
482                "to. When the note head is moved, the staccato dot moves along\n"
483                "automatically.\n"
484                "\n"
485                "A grob is often associated with a symbol, but some grobs do not print\n"
486                "any symbols. They take care of grouping objects. For example, there is a\n"
487                "separate grob that stacks staves vertically. The @ref{NoteCollision}\n"
488                "is also an abstract grob: it only moves around chords, but doesn't print\n"
489                "anything.\n"
490                "\n"
491                "Grobs have a properties: Scheme variables, that can be read and set. "
492                "They have two types. Immutable variables "
493                "define the default style and behavior.  They are shared between  many objects. "
494                "They can be changed using @code{\\override} and @code{\\revert}. "
495                "\n\n"
496                "Mutable properties are variables that are specific to one grob. Typically, "
497                "lists of other objects, or results from computations are stored in"
498                "mutable properties: every call to set-grob-property (or its C++ equivalent) "
499                "sets a mutable property. "
500                "\n\n"
501                "The properties @code{after-line-breaking} and @code{before-line-breaking} "
502                "are dummies that are not user-serviceable. "
503
504                ,
505
506                /* properties */
507                "X-extent "
508                "X-offset "
509                "Y-extent "
510                "Y-offset "
511                "after-line-breaking "
512                "axis-group-parent-X "
513                "axis-group-parent-Y "
514                "before-line-breaking "
515                "cause "
516                "color "
517                "extra-X-extent "
518                "extra-Y-extent "
519                "extra-offset "
520                "interfaces "
521                "layer "
522                "meta "
523                "minimum-X-extent "
524                "minimum-Y-extent "
525                "springs-and-rods "
526                "staff-symbol "
527                "stencil "
528                "transparent "
529                );
530
531
532
533
534
535 /****************************************************************
536   CALLBACKS
537 ****************************************************************/
538
539
540 static SCM
541 grob_stencil_extent (Grob *me, Axis a)
542 {
543   Stencil *m = me->get_stencil ();
544   Interval e;
545   if (m)
546     e = m->extent (a);
547   return ly_interval2scm (e);
548 }
549
550
551 MAKE_SCHEME_CALLBACK (Grob, stencil_height, 1);
552 SCM
553 Grob::stencil_height (SCM smob)
554 {
555   Grob *me = unsmob_grob (smob);
556   return grob_stencil_extent (me, Y_AXIS);
557 }
558
559 MAKE_SCHEME_CALLBACK(Grob, y_parent_positioning, 1);
560 SCM
561 Grob::y_parent_positioning (SCM smob)
562 {
563   Grob *me = unsmob_grob (smob);
564   Grob *par = me->get_parent (Y_AXIS);
565   if (par)
566     (void) par->get_property ("positioning-done");
567
568   return scm_from_double (0.0);
569 }
570
571
572 MAKE_SCHEME_CALLBACK(Grob, x_parent_positioning, 1);
573 SCM
574 Grob::x_parent_positioning (SCM smob)
575 {
576   Grob *me = unsmob_grob (smob);
577   
578   Grob *par = me->get_parent (X_AXIS);
579   if (par)
580     (void) par->get_property ("positioning-done");
581
582   return scm_from_double (0.0);
583 }
584
585 MAKE_SCHEME_CALLBACK (Grob, stencil_width, 1);
586 SCM
587 Grob::stencil_width (SCM smob)
588 {
589   Grob *me = unsmob_grob (smob);
590   return grob_stencil_extent (me, X_AXIS);
591 }
592
593
594
595 Grob *
596 common_refpoint_of_list (SCM elist, Grob *common, Axis a)
597 {
598   for (; scm_is_pair (elist); elist = scm_cdr (elist))
599     if (Grob *s = unsmob_grob (scm_car (elist)))
600       {
601         if (common)
602           common = common->common_refpoint (s, a);
603         else
604           common = s;
605       }
606
607   return common;
608 }
609
610 Grob *
611 common_refpoint_of_array (Link_array<Grob> const &arr, Grob *common, Axis a)
612 {
613   for (int i = arr.size (); i--;)
614     if (Grob *s = arr[i])
615       {
616         if (common)
617           common = common->common_refpoint (s, a);
618         else
619           common = s;
620       }
621
622   return common;
623 }
624
625 Interval
626 robust_relative_extent (Grob *me, Grob *refpoint, Axis a)
627 {
628   Interval ext = me->extent (refpoint, a);
629   if (ext.is_empty ())
630     ext.add_point (me->relative_coordinate (refpoint, a));
631
632   return ext;
633 }
634