]> git.donarmstrong.com Git - lilypond.git/blob - lily/grob.cc
* flower/include/pqueue.hh: Derive from std::vector.
[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
124       /* color support... see interpret_stencil_expression () for more... */
125       SCM color = get_property ("color");
126       if (color != SCM_EOL)
127         {
128           m = unsmob_stencil (stil);
129           SCM expr = scm_list_3 (ly_symbol2scm ("color"),
130                                  color,
131                                  m->expr ());
132
133           retval = Stencil (m->extent_box (), expr);
134         }
135
136     }
137
138   return retval;
139 }
140
141 /****************************************************************
142   VIRTUAL STUBS
143 ****************************************************************/
144 void
145 Grob::do_break_processing ()
146 {
147 }
148
149 void
150 Grob::discretionary_processing ()
151 {
152 }
153
154 System *
155 Grob::get_system () const
156 {
157   return 0;
158 }
159
160
161 void
162 Grob::handle_broken_dependencies ()
163 {
164   Spanner *sp = dynamic_cast<Spanner *> (this);
165   if (original () && sp)
166     return;
167
168   if (sp)
169     /* THIS, SP is the original spanner.  We use a special function
170        because some Spanners have enormously long lists in their
171        properties, and a special function fixes FOO  */
172     {
173       for (SCM s = object_alist_; scm_is_pair (s); s = scm_cdr (s))
174         sp->substitute_one_mutable_property (scm_caar (s), scm_cdar (s));
175     }
176   System *system = get_system ();
177
178   if (is_live ()
179       && system
180       && common_refpoint (system, X_AXIS)
181       && common_refpoint (system, Y_AXIS))
182     substitute_object_links (system->self_scm (), object_alist_);
183   else if (dynamic_cast<System *> (this))
184     substitute_object_links (SCM_UNDEFINED, object_alist_);
185   else
186     /* THIS element is `invalid'; it has been removed from all
187        dependencies, so let's junk the element itself.
188
189        Do not do this for System, since that would remove references
190        to the originals of score-grobs, which get then GC'd (a bad
191        thing).  */
192     suicide ();
193 }
194
195 /* Note that we still want references to this element to be
196    rearranged, and not silently thrown away, so we keep pointers like
197    {broken_into_{drul, array}, original}
198 */
199 void
200 Grob::suicide ()
201 {
202   if (!is_live ())
203     return;
204
205   for (int a = X_AXIS; a < NO_AXES; a++)
206     dim_cache_[a].clear ();
207
208   mutable_property_alist_ = SCM_EOL;
209   object_alist_ = SCM_EOL;
210   immutable_property_alist_ = SCM_EOL;
211   interfaces_ = SCM_EOL;
212 }
213
214 void
215 Grob::handle_prebroken_dependencies ()
216 {
217   /* Don't do this in the derived method, since we want to keep access to
218      object_alist_ centralized.  */
219   if (original ())
220     {
221       Item *it = dynamic_cast<Item *> (this);
222       substitute_object_links (scm_from_int (it->break_status_dir ()),
223                                original ()->object_alist_);
224     }
225 }
226
227 Grob *
228 Grob::find_broken_piece (System *) const
229 {
230   return 0;
231 }
232
233 /****************************************************************
234    OFFSETS
235 ****************************************************************/
236
237 void
238 Grob::translate_axis (Real y, Axis a)
239 {
240   if (isinf (y) || isnan (y))
241     {
242       programming_error (_ ("Infinity or NaN encountered"));
243       return ;
244     }
245   
246   if (!dim_cache_[a].offset_)
247     dim_cache_[a].offset_ = new Real (y);
248   else
249     *dim_cache_[a].offset_ += y;  
250 }
251
252 /* Find the offset relative to D.  If D equals THIS, then it is 0.
253    Otherwise, it recursively defd as
254
255    OFFSET_ + PARENT_L_->relative_coordinate (D) */
256 Real
257 Grob::relative_coordinate (Grob const *refp, Axis a) const
258 {
259   if (refp == this)
260     return 0.0;
261
262   /* We catch PARENT_L_ == nil case with this, but we crash if we did
263      not ask for the absolute coordinate (ie. REFP == nil.)  */
264   Real off = get_offset (a);
265   if (refp == dim_cache_[a].parent_)
266     return off;
267   
268   off += dim_cache_[a].parent_->relative_coordinate (refp, a);
269
270   return off;
271 }
272
273 /* Invoke callbacks to get offset relative to parent.  */
274 Real
275 Grob::get_offset (Axis a) const
276 {
277   if (dim_cache_[a].offset_)
278     return *dim_cache_[a].offset_;
279
280   Grob *me = (Grob *) this;
281
282   SCM sym = axis_offset_symbol (a);
283   me->dim_cache_[a].offset_ = new Real (0.0);
284
285   /*
286     UGH: can't fold next 2 statements together. Apparently GCC thinks
287     dim_cache_[a].offset_ is unaliased.
288   */
289   Real off = robust_scm2double (internal_get_property (sym), 0.0);
290   if (me->dim_cache_[a].offset_)
291     {
292       *me->dim_cache_[a].offset_ += off;
293       me->del_property (sym);
294       return *me->dim_cache_[a].offset_;
295     }
296   else
297     return 0.0;
298 }
299
300
301 /****************************************************************
302   extents
303 ****************************************************************/
304
305 void
306 Grob::flush_extent_cache (Axis axis)
307 {
308   if (dim_cache_[axis].extent_)
309     {
310       /*
311         Ugh, this is not accurate; will flush property, causing
312         callback to be called if.
313        */
314       del_property ((axis == X_AXIS) ? ly_symbol2scm ("X-extent") : ly_symbol2scm ("Y-extent"));
315       delete dim_cache_[axis].extent_;
316       dim_cache_[axis].extent_ = 0;
317       if (get_parent (axis))
318         get_parent (axis)->flush_extent_cache (axis);
319     }
320 }
321
322
323 Interval
324 Grob::extent (Grob *refp, Axis a) const
325 {
326   Real offset = relative_coordinate (refp, a);
327   Interval real_ext;
328   if (dim_cache_[a].extent_)
329     {
330       real_ext = *dim_cache_[a].extent_;
331     }
332   else
333     {
334       /*
335         Order is significant: ?-extent may trigger suicide.
336        */
337       SCM ext_sym =
338         (a == X_AXIS)
339         ? ly_symbol2scm ("X-extent")
340         : ly_symbol2scm ("Y-extent");
341
342       SCM ext = internal_get_property (ext_sym);
343       if (is_number_pair (ext))
344         real_ext.unite (ly_scm2interval (ext));
345
346       SCM min_ext_sym =
347         (a == X_AXIS)
348         ? ly_symbol2scm ("minimum-X-extent")
349         : ly_symbol2scm ("minimum-Y-extent");
350       SCM min_ext = internal_get_property (min_ext_sym);
351       if (is_number_pair (min_ext))
352         real_ext.unite (ly_scm2interval (min_ext));
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 (std::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 std::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 (std::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 (vsize 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