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