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