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