]> 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   SCM sym = axis_offset_symbol (a);
353   me->dim_cache_[a].offset_ = new Real (0.0);
354
355   /*
356     UGH: can't fold next 2 statements together. Apparently GCC thinks
357     dim_cache_[a].offset_ is unaliased.
358   */
359   Real off = robust_scm2double (internal_get_property (sym), 0.0);
360   *me->dim_cache_[a].offset_ += off;
361
362   me->del_property (sym);
363   return *me->dim_cache_[a].offset_;
364 }
365
366 void
367 Grob::flush_extent_cache (Axis axis)
368 {
369   if (dim_cache_[axis].extent_)
370     {
371       /*
372         Ugh, this is not accurate; will flush property, causing
373         callback to be called if.
374        */
375       del_property ((axis == X_AXIS) ? ly_symbol2scm ("X-extent") : ly_symbol2scm ("Y-extent"));
376       delete dim_cache_[axis].extent_;
377       dim_cache_[axis].extent_ = 0;
378       if (get_parent (axis))
379         get_parent (axis)->flush_extent_cache (axis);
380     }
381 }
382
383
384 Interval
385 Grob::extent (Grob *refp, Axis a) const
386 {
387   Real offset = relative_coordinate (refp, a);
388   Interval real_ext;
389   if (dim_cache_[a].extent_)
390     {
391       real_ext = *dim_cache_[a].extent_;
392     }
393   else
394     {
395       SCM min_ext_sym =
396         (a == X_AXIS)
397         ? ly_symbol2scm ("minimum-X-extent")
398         : ly_symbol2scm ("minimum-Y-extent");
399
400       SCM ext_sym =
401         (a == X_AXIS)
402         ? ly_symbol2scm ("X-extent")
403         : ly_symbol2scm ("Y-extent");
404   
405       SCM min_ext = internal_get_property (min_ext_sym);
406       SCM ext = internal_get_property (ext_sym);
407
408       if (is_number_pair (min_ext))
409         real_ext.unite (ly_scm2interval (min_ext));
410       if (is_number_pair (ext))
411         real_ext.unite (ly_scm2interval (ext));
412
413       ((Grob*)this)->del_property (ext_sym);
414       ((Grob*)this)->dim_cache_[a].extent_ = new Interval (real_ext);  
415     }
416   
417   real_ext.translate (offset);
418   
419   return real_ext;
420 }
421
422 /* Find the group-element which has both #this# and #s#  */
423 Grob *
424 Grob::common_refpoint (Grob const *s, Axis a) const
425 {
426   /* I don't like the quadratic aspect of this code, but I see no
427      other way.  The largest chain of parents might be 10 high or so,
428      so it shouldn't be a real issue.  */
429   for (Grob const *c = this; c; c = c->dim_cache_[a].parent_)
430     for (Grob const *d = s; d; d = d->dim_cache_[a].parent_)
431       if (d == c)
432         return (Grob *) d;
433
434   return 0;
435 }
436
437 Grob *
438 common_refpoint_of_list (SCM elist, Grob *common, Axis a)
439 {
440   for (; scm_is_pair (elist); elist = scm_cdr (elist))
441     if (Grob *s = unsmob_grob (scm_car (elist)))
442       {
443         if (common)
444           common = common->common_refpoint (s, a);
445         else
446           common = s;
447       }
448
449   return common;
450 }
451
452 Grob *
453 common_refpoint_of_array (Link_array<Grob> const &arr, Grob *common, Axis a)
454 {
455   for (int i = arr.size (); i--;)
456     if (Grob *s = arr[i])
457       {
458         if (common)
459           common = common->common_refpoint (s, a);
460         else
461           common = s;
462       }
463
464   return common;
465 }
466
467 String
468 Grob::name () const
469 {
470   SCM meta = get_property ("meta");
471   SCM nm = scm_assq (ly_symbol2scm ("name"), meta);
472   nm = (scm_is_pair (nm)) ? scm_cdr (nm) : SCM_EOL;
473   return scm_is_symbol (nm) ? ly_symbol2string (nm) : this->class_name ();
474 }
475
476 void
477 Grob::set_parent (Grob *g, Axis a)
478 {
479   dim_cache_[a].parent_ = g;
480 }
481
482 void
483 Grob::fixup_refpoint ()
484 {
485   for (int a = X_AXIS; a < NO_AXES; a++)
486     {
487       Axis ax = (Axis)a;
488       Grob *parent = get_parent (ax);
489
490       if (!parent)
491         continue;
492
493       if (parent->get_system () != get_system () && get_system ())
494         {
495           Grob *newparent = parent->find_broken_piece (get_system ());
496           set_parent (newparent, ax);
497         }
498
499       if (Item *i = dynamic_cast<Item *> (this))
500         {
501           Item *parenti = dynamic_cast<Item *> (parent);
502
503           if (parenti && i)
504             {
505               Direction my_dir = i->break_status_dir ();
506               if (my_dir != parenti->break_status_dir ())
507                 {
508                   Item *newparent = parenti->find_prebroken_piece (my_dir);
509                   set_parent (newparent, ax);
510                 }
511             }
512         }
513     }
514 }
515
516 void
517 Grob::warning (String s) const
518 {
519   SCM cause = self_scm ();
520   while (Grob *g = unsmob_grob (cause))
521     cause = g->get_property ("cause");
522
523   if (Music *m = unsmob_music (cause))
524     m->origin ()->warning (s);
525   else
526     ::warning (s);
527 }
528
529 void
530 Grob::programming_error (String s) const
531 {
532   SCM cause = self_scm ();
533   while (Grob *g = unsmob_grob (cause))
534     cause = g->get_property ("cause");
535
536   s = _f ("programming error: %s", s);
537
538   if (Music *m = unsmob_music (cause))
539     m->origin ()->message (s);
540   else
541     ::message (s);
542 }
543 void
544 Grob::discretionary_processing ()
545 {
546 }
547
548 bool
549 Grob::internal_has_interface (SCM k)
550 {
551   return scm_c_memq (k, interfaces_) != SCM_BOOL_F;
552 }
553
554 Grob *
555 Grob::get_parent (Axis a) const
556 {
557   return dim_cache_[a].parent_;
558 }
559
560 /** Return Array of Grobs in SCM list LST */
561 Link_array<Grob>
562 ly_scm2grobs (SCM lst)
563 {
564   Link_array<Grob> arr;
565
566   for (SCM s = lst; scm_is_pair (s); s = scm_cdr (s))
567     {
568       SCM e = scm_car (s);
569       arr.push (unsmob_grob (e));
570     }
571
572   arr.reverse ();
573   return arr;
574 }
575
576 Object_key const *
577 Grob::get_key () const
578 {
579   return key_;
580 }
581
582 /** Return SCM list of Grob array A */
583 SCM
584 ly_grobs2scm (Link_array<Grob> a)
585 {
586   SCM s = SCM_EOL;
587   for (int i = a.size (); i; i--)
588     s = scm_cons (a[i - 1]->self_scm (), s);
589
590   return s;
591 }
592
593 ADD_INTERFACE (Grob, "grob-interface",
594                "A grob represents a piece of music notation\n"
595                "\n"
596                "All grobs have an X and Y-position on the page.  These X and Y positions\n"
597                "are stored in a relative format, so they can easily be combined by\n"
598                "stacking them, hanging one grob to the side of another, and coupling\n"
599                "them into a grouping objects.\n"
600                "\n"
601                "Each grob has a reference point (a.k.a.  parent): the position of a grob\n"
602                "is stored relative to that reference point. For example the X-reference\n"
603                "point of a staccato dot usually is the note head that it applies\n"
604                "to. When the note head is moved, the staccato dot moves along\n"
605                "automatically.\n"
606                "\n"
607                "A grob is often associated with a symbol, but some grobs do not print\n"
608                "any symbols. They take care of grouping objects. For example, there is a\n"
609                "separate grob that stacks staves vertically. The @ref{NoteCollision}\n"
610                "is also an abstract grob: it only moves around chords, but doesn't print\n"
611                "anything.\n"
612                "\n"
613                "Grobs have a properties: Scheme variables, that can be read and set. "
614                "They have two types. Immutable variables "
615                "define the default style and behavior.  They are shared between  many objects. "
616                "They can be changed using @code{\\override} and @code{\\revert}. "
617                "\n\n"
618                "Mutable properties are variables that are specific to one grob. Typically, "
619                "lists of other objects, or results from computations are stored in"
620                "mutable properties: every call to set-grob-property (or its C++ equivalent) "
621                "sets a mutable property. "
622                "\n\n"
623                "The properties @code{after-line-breaking} and @code{before-line-breaking} "
624                "are dummies that are not user-serviceable. "
625
626                ,
627
628                /* properties */
629                "X-extent "
630                "X-offset "
631                "Y-extent "
632                "Y-offset "
633                "after-line-breaking "
634                "axis-group-parent-X "
635                "axis-group-parent-Y "
636                "before-line-breaking "
637                "cause "
638                "color "
639                "extra-X-extent "
640                "extra-Y-extent "
641                "extra-offset "
642                "interfaces "
643                "layer "
644                "meta "
645                "minimum-X-extent "
646                "minimum-Y-extent "
647                "springs-and-rods "
648                "staff-symbol "
649                "stencil "
650                "transparent "
651                );
652