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