]> git.donarmstrong.com Git - lilypond.git/blob - lily/grob.cc
* Documentation/topdocs/INSTALL.texi (Top): Remove information
[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 #include <math.h>
13
14 #include "main.hh"
15 #include "input-smob.hh"
16 #include "warn.hh"
17 #include "group-interface.hh"
18 #include "misc.hh"
19 #include "paper-score.hh"
20 #include "stencil.hh"
21 #include "warn.hh"
22 #include "system.hh"
23 #include "item.hh"
24 #include "stencil.hh"
25 #include "misc.hh"
26 #include "music.hh"
27 #include "item.hh"
28 #include "paper-score.hh"
29 #include "ly-smobs.icc"
30 #include "output-def.hh"
31
32 Grob *
33 Grob::clone (int count) const
34 {
35   return new Grob (*this, count);
36 }
37
38 /* TODO:
39
40 - remove dynamic_cast<Spanner, Item> and put this code into respective
41 subclass.  */
42
43 #define HASH_SIZE 3
44 #define INFINITY_MSG "Infinity or NaN encountered"
45
46 Grob::Grob (SCM basicprops,
47             Object_key const *key)
48 {
49   key_ = key;
50   /* FIXME: default should be no callback.  */
51   self_scm_ = SCM_EOL;
52   pscore_ = 0;
53   status_ = 0;
54   original_ = 0;
55   immutable_property_alist_ = basicprops;
56   mutable_property_alist_ = SCM_EOL;
57
58   /* We do smobify_self () as the first step.  Since the object lives
59      on the heap, none of its SCM variables are protected from
60      GC. After smobify_self (), they are.  */
61   smobify_self ();
62
63   /*
64     We always get a new key object for a new grob.
65   */
66   scm_gc_unprotect_object (key_->self_scm ());
67   SCM meta = get_property ("meta");
68   if (scm_is_pair (meta))
69     {
70       SCM ifs = scm_assoc (ly_symbol2scm ("interfaces"), meta);
71
72       /* Switch off interface checks for the moment.  */
73       bool itc = do_internal_type_checking_global;
74       do_internal_type_checking_global = false;
75       internal_set_property (ly_symbol2scm ("interfaces"), scm_cdr (ifs));
76       do_internal_type_checking_global = itc;
77     }
78
79   /* TODO:
80
81   - destill this into a function, so we can re-init the immutable
82   properties with a new BASICPROPS value after
83   creation. Convenient eg. when using \override with
84   StaffSymbol.  */
85
86   char const *onames[] = {"X-offset-callbacks", "Y-offset-callbacks"};
87   char const *xnames[] = {"X-extent", "Y-extent"};
88   char const *enames[] = {"X-extent-callback", "Y-extent-callback"};
89
90   for (int a = X_AXIS; a <= Y_AXIS; a++)
91     {
92       SCM l = get_property (onames[a]);
93
94       if (scm_ilength (l) >= 0)
95         {
96           dim_cache_[a].offset_callbacks_ = l;
97           dim_cache_[a].offsets_left_ = scm_ilength (l);
98         }
99       else
100         programming_error ("[XY]-offset-callbacks must be a list");
101
102       SCM cb = get_property (enames[a]);
103       if (cb == SCM_BOOL_F)
104         {
105           dim_cache_[a].dimension_ = SCM_BOOL_F;
106         }
107
108       SCM xt = get_property (xnames[a]);
109       if (is_number_pair (xt))
110         {
111           dim_cache_[a].dimension_ = xt;
112         }
113       else if (ly_c_procedure_p (cb))
114         {
115           dim_cache_[a].dimension_callback_ = cb;
116         }
117       else if (cb == SCM_EOL
118                && ly_c_procedure_p (get_property ("print-function")))
119         dim_cache_[a].dimension_callback_ = stencil_extent_proc;
120     }
121 }
122
123 Grob::Grob (Grob const &s, int copy_index)
124   : dim_cache_ (s.dim_cache_)
125 {
126   key_ = new Copied_key (s.key_, copy_index);
127   original_ = (Grob *) & s;
128   self_scm_ = SCM_EOL;
129
130   immutable_property_alist_ = s.immutable_property_alist_;
131   mutable_property_alist_ = SCM_EOL;
132
133   /* No properties are copied.  That is the job of
134      handle_broken_dependencies.  */
135   status_ = s.status_;
136   pscore_ = 0;
137
138   smobify_self ();
139   scm_gc_unprotect_object (key_->self_scm ());
140 }
141
142 Grob::~Grob ()
143 {
144 }
145
146 MAKE_SCHEME_CALLBACK (Grob, stencil_extent, 2);
147 SCM
148 Grob::stencil_extent (SCM element_smob, SCM scm_axis)
149 {
150   Grob *s = unsmob_grob (element_smob);
151   Axis a = (Axis) scm_to_int (scm_axis);
152
153   Stencil *m = s->get_stencil ();
154   Interval e;
155   if (m)
156     e = m->extent (a);
157   return ly_interval2scm (e);
158 }
159
160 Interval
161 robust_relative_extent (Grob *me, Grob *refp, Axis a)
162 {
163   Interval ext = me->extent (refp, a);
164   if (ext.is_empty ())
165     {
166       ext.add_point (me->relative_coordinate (refp, a));
167     }
168
169   return ext;
170 }
171
172 Output_def *
173 Grob::get_layout () const
174 {
175   return pscore_ ? pscore_->layout_ : 0;
176 }
177
178 /* Recursively track all dependencies of this Grob.  The status_ field
179    is used as a mark-field.  It is marked with BUSY during execution
180    of this function, and marked with FINAL when finished.
181
182    FUNCPTR is the function to call to update this element.  */
183 void
184 Grob::calculate_dependencies (int final, int busy, SCM funcname)
185 {
186   if (status_ >= final)
187     return;
188
189   if (status_ == busy)
190     {
191       programming_error ("Element is busy, come back later");
192       return;
193     }
194
195   status_ = busy;
196
197   for (SCM d = get_property ("dependencies"); scm_is_pair (d);
198        d = scm_cdr (d))
199     unsmob_grob (scm_car (d))->calculate_dependencies (final, busy, funcname);
200
201   SCM proc = internal_get_property (funcname);
202   if (ly_c_procedure_p (proc))
203     scm_call_1 (proc, this->self_scm ());
204
205   status_ = final;
206 }
207
208 Stencil *
209 Grob::get_stencil () const
210 {
211   if (!is_live ())
212     return 0;
213
214   SCM stil = get_property ("stencil");
215   if (unsmob_stencil (stil))
216     return unsmob_stencil (stil);
217
218   stil = get_uncached_stencil ();
219   if (is_live ())
220     {
221       Grob *me = (Grob *) this;
222       me->set_property ("stencil", stil);
223     }
224
225   return unsmob_stencil (stil);
226 }
227
228 SCM
229 Grob::get_uncached_stencil () const
230 {
231   SCM proc = get_property ("print-function");
232
233   SCM stil = SCM_EOL;
234   if (ly_c_procedure_p (proc))
235     stil = scm_apply_0 (proc, scm_list_n (this->self_scm (), SCM_UNDEFINED));
236
237   if (Stencil *m = unsmob_stencil (stil))
238     {
239       if (to_boolean (get_property ("transparent")))
240         stil = Stencil (m->extent_box (), SCM_EOL).smobbed_copy ();
241       else
242         {
243           SCM expr = scm_list_3 (ly_symbol2scm ("grob-cause"), self_scm (),
244                                  m->expr ());
245           stil = Stencil (m->extent_box (), expr).smobbed_copy ();
246         }
247
248       /* color support... see interpret_stencil_expression () for more... */
249       SCM color = get_property ("color");
250       if (color != SCM_EOL)
251         {
252           m = unsmob_stencil (stil);
253           SCM expr = scm_list_3 (ly_symbol2scm ("color"),
254                                  color,
255                                  m->expr ());
256
257           stil = Stencil (m->extent_box (), expr).smobbed_copy ();
258         }
259     }
260
261   return stil;
262 }
263
264 /*
265   VIRTUAL STUBS
266 */
267 void
268 Grob::do_break_processing ()
269 {
270 }
271
272 System *
273 Grob::get_system () const
274 {
275   return 0;
276 }
277
278 void
279 Grob::add_dependency (Grob *e)
280 {
281   if (e)
282     Pointer_group_interface::add_grob (this, ly_symbol2scm ("dependencies"),
283                                        e);
284   else
285     programming_error ("Null dependency added");
286 }
287
288 void
289 Grob::handle_broken_dependencies ()
290 {
291   Spanner *sp = dynamic_cast<Spanner *> (this);
292   if (original_ && sp)
293     return;
294
295   if (sp)
296     /* THIS, SP is the original spanner.  We use a special function
297        because some Spanners have enormously long lists in their
298        properties, and a special function fixes FOO  */
299     for (SCM s = mutable_property_alist_; scm_is_pair (s); s = scm_cdr (s))
300       sp->substitute_one_mutable_property (scm_caar (s), scm_cdar (s));
301
302   System *system = get_system ();
303
304   if (is_live ()
305       && system && common_refpoint (system, X_AXIS)
306       && common_refpoint (system, Y_AXIS))
307     substitute_mutable_properties (system
308                                    ? system->self_scm () : SCM_UNDEFINED,
309                                    mutable_property_alist_);
310   else if (dynamic_cast<System *> (this))
311     substitute_mutable_properties (SCM_UNDEFINED, mutable_property_alist_);
312   else
313     /* THIS element is `invalid'; it has been removed from all
314        dependencies, so let's junk the element itself.
315
316        Do not do this for System, since that would remove references
317        to the originals of score-grobs, which get then GC'd (a bad
318        thing).  */
319     suicide ();
320 }
321
322 /* Note that we still want references to this element to be
323    rearranged, and not silently thrown away, so we keep pointers like
324    {broken_into_{drul, array}, original}
325 */
326 void
327 Grob::suicide ()
328 {
329   if (!is_live ())
330     return;
331
332   mutable_property_alist_ = SCM_EOL;
333   immutable_property_alist_ = SCM_EOL;
334
335   set_extent (SCM_EOL, Y_AXIS);
336   set_extent (SCM_EOL, X_AXIS);
337
338   set_extent_callback (SCM_EOL, Y_AXIS);
339   set_extent_callback (SCM_EOL, X_AXIS);
340
341   for (int a = X_AXIS; a <= Y_AXIS; a++)
342     {
343       dim_cache_[a].offset_callbacks_ = SCM_EOL;
344       dim_cache_[a].offsets_left_ = 0;
345     }
346 }
347
348 void
349 Grob::handle_prebroken_dependencies ()
350 {
351   /* Don't do this in the derived method, since we want to keep access to
352      mutable_property_alist_ centralized.  */
353   if (original_)
354     {
355       Item *it = dynamic_cast<Item *> (this);
356       substitute_mutable_properties (scm_int2num (it->break_status_dir ()),
357                                      original_->mutable_property_alist_);
358     }
359 }
360
361 Grob *
362 Grob::find_broken_piece (System *) const
363 {
364   return 0;
365 }
366
367 /* Translate in one direction.  */
368 void
369 Grob::translate_axis (Real y, Axis a)
370 {
371   if (isinf (y) || isnan (y))
372     programming_error (_ (INFINITY_MSG));
373   else
374     dim_cache_[a].offset_ += y;
375 }
376
377 /* Find the offset relative to D.  If D equals THIS, then it is 0.
378    Otherwise, it recursively defd as
379
380    OFFSET_ + PARENT_L_->relative_coordinate (D) */
381 Real
382 Grob::relative_coordinate (Grob const *refp, Axis a) const
383 {
384   if (refp == this)
385     return 0.0;
386
387   /* We catch PARENT_L_ == nil case with this, but we crash if we did
388      not ask for the absolute coordinate (ie. REFP == nil.)  */
389   if (refp == dim_cache_[a].parent_)
390     return get_offset (a);
391
392   return get_offset (a) + dim_cache_[a].parent_->relative_coordinate (refp, a);
393 }
394
395 /* Invoke callbacks to get offset relative to parent.  */
396 Real
397 Grob::get_offset (Axis a) const
398 {
399   Grob *me = (Grob *) this;
400   while (dim_cache_[a].offsets_left_)
401     {
402       int l = --me->dim_cache_[a].offsets_left_;
403       SCM cb = scm_list_ref (dim_cache_[a].offset_callbacks_, scm_int2num (l));
404       SCM retval = scm_call_2 (cb, self_scm (), scm_int2num (a));
405
406       Real r = scm_to_double (retval);
407       if (isinf (r) || isnan (r))
408         {
409           programming_error (INFINITY_MSG);
410           r = 0.0;
411         }
412       me->dim_cache_[a].offset_ += r;
413     }
414   return dim_cache_[a].offset_;
415 }
416
417 bool
418 Grob::is_empty (Axis a) const
419 {
420   return !(scm_is_pair (dim_cache_[a].dimension_)
421            || ly_c_procedure_p (dim_cache_[a].dimension_callback_));
422 }
423
424 void
425 Grob::flush_extent_cache (Axis axis)
426 {
427   Dimension_cache *d = &dim_cache_[axis];
428   if (ly_c_procedure_p (d->dimension_callback_)
429       && scm_is_pair (d->dimension_))
430     {
431       d->dimension_ = SCM_EOL;
432
433       if (get_parent (axis))
434         get_parent (axis)->flush_extent_cache (axis);
435     }
436 }
437
438 Interval
439 Grob::extent (Grob *refp, Axis a) const
440 {
441   Real x = relative_coordinate (refp, a);
442
443   Dimension_cache *d = (Dimension_cache *) & dim_cache_[a];
444   Interval ext;
445
446   SCM dimpair = d->dimension_;
447   if (scm_is_pair (dimpair))
448     ;
449   else if (ly_c_procedure_p (d->dimension_callback_)
450            && d->dimension_ == SCM_EOL)
451     d->dimension_ = scm_call_2 (d->dimension_callback_, self_scm (), scm_int2num (a));
452   else
453     return ext;
454
455   if (!scm_is_pair (d->dimension_))
456     return ext;
457
458   ext = ly_scm2interval (d->dimension_);
459
460   SCM extra = get_property (a == X_AXIS
461                             ? "extra-X-extent"
462                             : "extra-Y-extent");
463
464   /* Signs ?  */
465   if (scm_is_pair (extra))
466     {
467       ext[BIGGER] += scm_to_double (scm_cdr (extra));
468       ext[SMALLER] += scm_to_double (scm_car (extra));
469     }
470
471   extra = get_property (a == X_AXIS
472                         ? "minimum-X-extent"
473                         : "minimum-Y-extent");
474   if (scm_is_pair (extra))
475     ext.unite (Interval (scm_to_double (scm_car (extra)),
476                          scm_to_double (scm_cdr (extra))));
477
478   ext.translate (x);
479
480   return ext;
481 }
482
483 /* Find the group-element which has both #this# and #s#  */
484 Grob *
485 Grob::common_refpoint (Grob const *s, Axis a) const
486 {
487   /* I don't like the quadratic aspect of this code, but I see no
488      other way.  The largest chain of parents might be 10 high or so,
489      so it shouldn't be a real issue.  */
490   for (Grob const *c = this; c; c = c->dim_cache_[a].parent_)
491     for (Grob const *d = s; d; d = d->dim_cache_[a].parent_)
492       if (d == c)
493         return (Grob *) d;
494
495   return 0;
496 }
497
498 Grob *
499 common_refpoint_of_list (SCM elist, Grob *common, Axis a)
500 {
501   for (; scm_is_pair (elist); elist = scm_cdr (elist))
502     if (Grob *s = unsmob_grob (scm_car (elist)))
503       {
504         if (common)
505           common = common->common_refpoint (s, a);
506         else
507           common = s;
508       }
509
510   return common;
511 }
512
513 Grob *
514 common_refpoint_of_array (Link_array<Grob> const &arr, Grob *common, Axis a)
515 {
516   for (int i = arr.size (); i--;)
517     if (Grob *s = arr[i])
518       {
519         if (common)
520           common = common->common_refpoint (s, a);
521         else
522           common = s;
523       }
524
525   return common;
526 }
527
528 String
529 Grob::name () const
530 {
531   SCM meta = get_property ("meta");
532   SCM nm = scm_assoc (ly_symbol2scm ("name"), meta);
533   nm = (scm_is_pair (nm)) ? scm_cdr (nm) : SCM_EOL;
534   return scm_is_symbol (nm) ? ly_symbol2string (nm) : classname (this);
535 }
536
537 void
538 Grob::add_offset_callback (SCM cb, Axis a)
539 {
540   if (!has_offset_callback (cb, a))
541     {
542       dim_cache_[a].offset_callbacks_
543         = scm_cons (cb, dim_cache_[a].offset_callbacks_);
544       dim_cache_[a].offsets_left_++;
545     }
546 }
547
548 bool
549 Grob::has_extent_callback (SCM cb, Axis a) const
550 {
551   return scm_equal_p (cb, dim_cache_[a].dimension_callback_) == SCM_BOOL_T;
552 }
553
554 bool
555 Grob::has_offset_callback (SCM cb, Axis a) const
556 {
557   return scm_c_memq (cb, dim_cache_[a].offset_callbacks_) != SCM_BOOL_F;
558 }
559
560 void
561 Grob::set_extent (SCM dc, Axis a)
562 {
563   dim_cache_[a].dimension_ = dc;
564 }
565
566 void
567 Grob::set_extent_callback (SCM dc, Axis a)
568 {
569   dim_cache_[a].dimension_callback_ = dc;
570 }
571
572 void
573 Grob::set_parent (Grob *g, Axis a)
574 {
575   dim_cache_[a].parent_ = g;
576 }
577
578 MAKE_SCHEME_CALLBACK (Grob, fixup_refpoint, 1);
579 SCM
580 Grob::fixup_refpoint (SCM smob)
581 {
582   Grob *me = unsmob_grob (smob);
583   for (int a = X_AXIS; a < NO_AXES; a++)
584     {
585       Axis ax = (Axis)a;
586       Grob *parent = me->get_parent (ax);
587
588       if (!parent)
589         continue;
590
591       if (parent->get_system () != me->get_system () && me->get_system ())
592         {
593           Grob *newparent = parent->find_broken_piece (me->get_system ());
594           me->set_parent (newparent, ax);
595         }
596
597       if (Item *i = dynamic_cast<Item *> (me))
598         {
599           Item *parenti = dynamic_cast<Item *> (parent);
600
601           if (parenti && i)
602             {
603               Direction my_dir = i->break_status_dir ();
604               if (my_dir != parenti->break_status_dir ())
605                 {
606                   Item *newparent = parenti->find_prebroken_piece (my_dir);
607                   me->set_parent (newparent, ax);
608                 }
609             }
610         }
611     }
612   return smob;
613 }
614
615 void
616 Grob::warning (String s) const
617 {
618   SCM cause = self_scm ();
619   while (Grob *g = unsmob_grob (cause))
620     cause = g->get_property ("cause");
621
622   if (Music *m = unsmob_music (cause))
623     m->origin ()->warning (s);
624   else
625     ::warning (s);
626 }
627
628 void
629 Grob::programming_error (String s) const
630 {
631   s = "Programming error: " + s;
632   warning (s);
633 }
634
635 /****************************************************
636   SMOB funcs
637 ****************************************************/
638
639 IMPLEMENT_SMOBS (Grob);
640 IMPLEMENT_DEFAULT_EQUAL_P (Grob);
641
642 SCM
643 Grob::mark_smob (SCM ses)
644 {
645   Grob *s = (Grob *) SCM_CELL_WORD_1 (ses);
646   scm_gc_mark (s->immutable_property_alist_);
647   scm_gc_mark (s->key_->self_scm ());
648   for (int a = 0; a < 2; a++)
649     {
650       scm_gc_mark (s->dim_cache_[a].offset_callbacks_);
651       scm_gc_mark (s->dim_cache_[a].dimension_);
652       scm_gc_mark (s->dim_cache_[a].dimension_callback_);
653
654       /* Do not mark the parents.  The pointers in the mutable
655          property list form two tree like structures (one for X
656          relations, one for Y relations).  Marking these can be done
657          in limited stack space.  If we add the parents, we will jump
658          between X and Y in an erratic manner, leading to much more
659          recursion depth (and core dumps if we link to pthreads).  */
660     }
661
662   if (s->original_)
663     scm_gc_mark (s->original_->self_scm ());
664
665   if (s->pscore_)
666     scm_gc_mark (s->pscore_->layout_->self_scm ());
667
668   s->do_derived_mark ();
669   return s->mutable_property_alist_;
670 }
671
672 int
673 Grob::print_smob (SCM s, SCM port, scm_print_state *)
674 {
675   Grob *sc = (Grob *) SCM_CELL_WORD_1 (s);
676
677   scm_puts ("#<Grob ", port);
678   scm_puts ((char *) sc->name ().to_str0 (), port);
679
680   /* Do not print properties, that is too much hassle.  */
681   scm_puts (" >", port);
682   return 1;
683 }
684
685 SCM
686 Grob::do_derived_mark () const
687 {
688   return SCM_EOL;
689 }
690
691 void
692 Grob::discretionary_processing ()
693 {
694 }
695
696 bool
697 Grob::internal_has_interface (SCM k)
698 {
699   SCM ifs = get_property ("interfaces");
700
701   return scm_c_memq (k, ifs) != SCM_BOOL_F;
702 }
703
704 Grob *
705 Grob::get_parent (Axis a) const
706 {
707   return dim_cache_[a].parent_;
708 }
709
710 /** Return Array of Grobs in SCM list LST */
711 Link_array<Grob>
712 ly_scm2grobs (SCM lst)
713 {
714   Link_array<Grob> arr;
715
716   for (SCM s = lst; scm_is_pair (s); s = scm_cdr (s))
717     {
718       SCM e = scm_car (s);
719       arr.push (unsmob_grob (e));
720     }
721
722   arr.reverse ();
723   return arr;
724 }
725
726 Object_key const *
727 Grob::get_key () const
728 {
729   return key_;
730 }
731
732 /** Return SCM list of Grob array A */
733 SCM
734 ly_grobs2scm (Link_array<Grob> a)
735 {
736   SCM s = SCM_EOL;
737   for (int i = a.size (); i; i--)
738     s = scm_cons (a[i - 1]->self_scm (), s);
739
740   return s;
741 }
742
743 IMPLEMENT_TYPE_P (Grob, "ly:grob?");
744
745 ADD_INTERFACE (Grob, "grob-interface",
746                "A grob represents a piece of music notation\n"
747                "\n"
748                "All grobs have an X and Y-position on the page.  These X and Y positions\n"
749                "are stored in a relative format, so they can easily be combined by\n"
750                "stacking them, hanging one grob to the side of another, and coupling\n"
751                "them into a grouping objects.\n"
752                "\n"
753                "Each grob has a reference point (a.k.a.  parent): the position of a grob\n"
754                "is stored relative to that reference point. For example the X-reference\n"
755                "point of a staccato dot usually is the note head that it applies\n"
756                "to. When the note head is moved, the staccato dot moves along\n"
757                "automatically.\n"
758                "\n"
759                "A grob is often associated with a symbol, but some grobs do not print\n"
760                "any symbols. They take care of grouping objects. For example, there is a\n"
761                "separate grob that stacks staves vertically. The @ref{NoteCollision}\n"
762                "is also an abstract grob: it only moves around chords, but doesn't print\n"
763                "anything.\n"
764                "\n"
765                "Grobs have a properties: Scheme variables, that can be read and set. "
766                "They have two types. Immutable variables "
767                "define the default style and behavior.  They are shared between  many objects. "
768                "They can be changed using @code{\\override} and @code{\\revert}. "
769                "\n\n"
770                "Mutable properties are variables that are specific to one grob. Typically, "
771                "lists of other objects, or results from computations are stored in"
772                "mutable properties: every call to set-grob-property (or its C++ equivalent) "
773                "sets a mutable property. ",
774                "X-offset-callbacks Y-offset-callbacks X-extent-callback stencil cause "
775                "Y-extent-callback print-function extra-offset spacing-procedure "
776                "context staff-symbol interfaces dependencies X-extent Y-extent extra-X-extent "
777                "meta layer before-line-breaking-callback "
778                "color "
779                "axis-group-parent-X "
780                "axis-group-parent-Y "
781                "after-line-breaking-callback extra-Y-extent minimum-X-extent "
782                "minimum-Y-extent transparent tweak-count tweak-rank");
783