* lily/include/lily-guile.hh: many new ly_ functions. Thanks to
[lilypond.git] / lily / grob.cc
blob12d29d7b8c28b690233523dc440621290c96837f
1 /*
2 grob.cc -- implement Grob
4 source file of the GNU LilyPond music typesetter
6 (c) 1997--2004 Han-Wen Nienhuys <hanwen@cs.uu.nl>
7 */
10 #include <string.h>
11 #include <math.h>
13 #include "main.hh"
14 #include "input-smob.hh"
15 #include "warn.hh"
16 #include "group-interface.hh"
17 #include "misc.hh"
18 #include "paper-score.hh"
19 #include "stencil.hh"
20 #include "grob.hh"
21 #include "warn.hh"
22 #include "spanner.hh"
23 #include "system.hh"
24 #include "item.hh"
25 #include "stencil.hh"
26 #include "misc.hh"
27 #include "music.hh"
28 #include "item.hh"
30 #include "ly-smobs.icc"
33 TODO:
35 remove dynamic_cast<Spanner,Item> and put this code into respective
36 subclass.
39 //#define HASHING_FOR_MUTABLE_PROPS
40 #define HASH_SIZE 3
41 #define INFINITY_MSG "Infinity or NaN encountered"
43 Grob::Grob (SCM basicprops)
46 fixme: default should be no callback.
48 self_scm_ = SCM_EOL;
49 pscore_=0;
50 status_ = 0;
51 original_ = 0;
52 immutable_property_alist_ = basicprops;
53 mutable_property_alist_ = SCM_EOL;
56 We do smobify_self () as the first step. Since the object lives on
57 the heap, none of its SCM variables are protected from GC. After
58 smobify_self (), they are.
60 smobify_self ();
63 #ifdef HASHING_FOR_MUTABLE_PROPS
64 mutable_property_alist_ = scm_c_make_hash_table (HASH_SIZE);
65 #endif
67 SCM meta = get_property ("meta");
68 if (ly_pair_p (meta))
70 SCM ifs = scm_assoc (ly_symbol2scm ("interfaces"), meta);
73 Switch off interface checks for the moment.
75 bool itc = internal_type_checking_global_b;
76 internal_type_checking_global_b = false;
77 internal_set_property (ly_symbol2scm ("interfaces"), ly_cdr (ifs));
78 internal_type_checking_global_b = itc;
82 TODO:
84 destill this into a function, so we can re-init the immutable
85 properties with a new BASICPROPS value after creation. Convenient
86 eg. when using \override with StaffSymbol. */
88 char const*onames[] = {"X-offset-callbacks", "Y-offset-callbacks"};
89 char const*xnames[] = {"X-extent", "Y-extent"};
90 char const*enames[] = {"X-extent-callback", "Y-extent-callback"};
92 for (int a = X_AXIS; a <= Y_AXIS; a++)
94 SCM l = get_property (onames[a]);
96 if (scm_ilength (l) >=0)
98 dim_cache_[a].offset_callbacks_ = l;
99 dim_cache_[a].offsets_left_ = scm_ilength (l);
101 else
103 programming_error ("[XY]-offset-callbacks must be a list");
106 SCM cb = get_property (enames[a]);
107 SCM xt = get_property (xnames[a]);
110 Should change default to empty?
112 if (is_number_pair (xt))
113 cb = xt;
114 else if (cb != SCM_BOOL_F
115 && !ly_procedure_p (cb) && !ly_pair_p (cb)
116 && ly_procedure_p (get_property ("print-function")))
117 cb = stencil_extent_proc;
119 dim_cache_[a].dimension_ = cb;
124 Grob::Grob (Grob const&s)
125 : dim_cache_ (s.dim_cache_)
127 original_ = (Grob*) &s;
128 self_scm_ = SCM_EOL;
130 immutable_property_alist_ = s.immutable_property_alist_;
131 mutable_property_alist_ = SCM_EOL;
134 No properties are copied. That is the job of handle_broken_dependencies.
137 status_ = s.status_;
138 pscore_ = 0;
140 smobify_self ();
142 #ifdef HASHING_FOR_MUTABLE_PROPS
143 mutable_property_alist_ = scm_c_make_hash_table (HASH_SIZE);
144 #endif
147 Grob::~Grob ()
150 do nothing scm-ish and no unprotecting here.
155 MAKE_SCHEME_CALLBACK (Grob,stencil_extent,2);
157 Grob::stencil_extent (SCM element_smob, SCM scm_axis)
159 Grob *s = unsmob_grob (element_smob);
160 Axis a = (Axis) ly_scm2int (scm_axis);
162 Stencil *m = s->get_stencil ();
163 Interval e ;
164 if (m)
165 e = m->extent (a);
166 return ly_interval2scm (e);
169 Paper_def*
170 Grob::get_paper () const
172 return pscore_ ? pscore_->paper_ : 0;
175 void
176 Grob::calculate_dependencies (int final, int busy, SCM funcname)
178 if (status_ >= final)
179 return;
181 if (status_== busy)
183 programming_error ("Element is busy, come back later");
184 return;
187 status_= busy;
189 for (SCM d = get_property ("dependencies"); ly_pair_p (d);
190 d = ly_cdr (d))
192 unsmob_grob (ly_car (d))
193 ->calculate_dependencies (final, busy, funcname);
197 SCM proc = internal_get_property (funcname);
198 if (ly_procedure_p (proc))
199 scm_call_1 (proc, this->self_scm ());
201 status_= final;
204 Stencil *
205 Grob::get_stencil () const
207 if (!live ())
209 return 0;
212 SCM mol = get_property ("stencil");
213 if (unsmob_stencil (mol))
214 return unsmob_stencil (mol);
216 mol = get_uncached_stencil ();
218 if (live ())
220 Grob *me = (Grob*)this;
221 me->set_property ("stencil", mol);
224 return unsmob_stencil (mol);
228 Grob::get_uncached_stencil ()const
230 SCM proc = get_property ("print-function");
232 SCM mol = SCM_EOL;
233 if (ly_procedure_p (proc))
234 mol = scm_apply_0 (proc, scm_list_n (this->self_scm (), SCM_UNDEFINED));
236 Stencil *m = unsmob_stencil (mol);
238 if (unsmob_stencil (mol))
240 SCM origin = ly_symbol2scm ("no-origin");
242 if (store_locations_global_b)
244 SCM cause = get_property ("cause");
245 if (Music*m = unsmob_music (cause))
247 SCM music_origin = m->get_property ("origin");
248 if (unsmob_input (music_origin))
249 origin = music_origin;
253 // ugr.
255 mol = Stencil (m->extent_box (),
256 scm_list_n (origin, m->get_expr (), SCM_UNDEFINED)
257 ). smobbed_copy ();
259 m = unsmob_stencil (mol);
263 transparent retains dimensions of element.
265 if (m && to_boolean (get_property ("transparent")))
266 mol = Stencil (m->extent_box (), SCM_EOL).smobbed_copy ();
268 return mol;
273 VIRTUAL STUBS
276 void
277 Grob::do_break_processing ()
281 System *
282 Grob::get_system () const
284 return 0;
287 void
288 Grob::add_dependency (Grob*e)
290 if (e)
292 Pointer_group_interface::add_grob (this, ly_symbol2scm ("dependencies"),e);
294 else
295 programming_error ("Null dependency added");
299 void
300 Grob::handle_broken_dependencies ()
302 Spanner * sp = dynamic_cast<Spanner*> (this);
303 if (original_ && sp)
304 return;
306 if (sp)
309 This is the original spanner. We use a special function
310 because some Spanners have enormously long lists in their
311 properties.
313 for (SCM s = mutable_property_alist_; ly_pair_p (s);
314 s = ly_cdr (s))
316 sp->substitute_one_mutable_property (ly_caar (s),
317 ly_cdar (s));
321 System *system = get_system ();
323 if (live ()
324 && system && common_refpoint (system, X_AXIS) && common_refpoint (system, Y_AXIS))
326 substitute_mutable_properties (system ? system->self_scm () : SCM_UNDEFINED,
327 mutable_property_alist_);
329 else if (dynamic_cast <System*> (this))
331 substitute_mutable_properties (SCM_UNDEFINED, mutable_property_alist_);
333 else
336 This element is `invalid'; it has been removed from all
337 dependencies, so let's junk the element itself.
339 do not do this for System, since that would remove references
340 to the originals of score-grobs, which get then GC'd (a bad
341 thing.)
344 suicide ();
349 Note that we still want references to this element to be
350 rearranged, and not silently thrown away, so we keep pointers
351 like {broken_into_{drul,array}, original}
353 void
354 Grob::suicide ()
356 if (!live ())
357 return;
359 #if 0 // see below.
360 String nm = name ();
361 #endif
363 mutable_property_alist_ = SCM_EOL;
364 immutable_property_alist_ = SCM_EOL;
366 set_extent (SCM_EOL, Y_AXIS);
367 set_extent (SCM_EOL, X_AXIS);
369 for (int a= X_AXIS; a <= Y_AXIS; a++)
371 dim_cache_[a].offset_callbacks_ = SCM_EOL;
372 dim_cache_[a].offsets_left_ = 0;
375 #if 0
377 This can make debugging a little easier: we can still know what
378 the object used to be. However, since all its links have been
379 broken, it's usually more convenient to set a conditional
380 breakpoint in GDB before the property lists are wiped.
382 mutable_property_alist_ = scm_acons (ly_symbol2scm ("name"),
383 scm_makfrom0str (nm.to_str0()),
384 mutable_property_alist_
386 #endif
389 void
390 Grob::handle_prebroken_dependencies ()
393 Don't do this in the derived method, since we want to keep access to
394 mutable_property_alist_ centralized.
396 if (original_)
398 Item * it = dynamic_cast<Item*> (this);
399 substitute_mutable_properties (scm_int2num (it->break_status_dir ()),
400 original_->mutable_property_alist_);
404 Grob*
405 Grob::find_broken_piece (System*) const
407 return 0;
411 translate in one direction
413 void
414 Grob::translate_axis (Real y, Axis a)
416 if (isinf (y) || isnan (y))
417 programming_error (_ (INFINITY_MSG));
418 else
420 dim_cache_[a].offset_ += y;
426 Find the offset relative to D. If D equals THIS, then it is 0.
427 Otherwise, it recursively defd as
429 OFFSET_ + PARENT_L_->relative_coordinate (D)
431 Real
432 Grob::relative_coordinate (Grob const*refp, Axis a) const
434 if (refp == this)
435 return 0.0;
438 We catch PARENT_L_ == nil case with this, but we crash if we did
439 not ask for the absolute coordinate (ie. REFP == nil.)
442 if (refp == dim_cache_[a].parent_)
443 return get_offset (a);
444 else
445 return get_offset (a) + dim_cache_[a].parent_->relative_coordinate (refp, a);
451 Invoke callbacks to get offset relative to parent.
453 Real
454 Grob::get_offset (Axis a) const
456 Grob *me = (Grob*) this;
457 while (dim_cache_[a].offsets_left_)
459 int l = --me->dim_cache_[a].offsets_left_;
460 SCM cb = scm_list_ref (dim_cache_[a].offset_callbacks_, scm_int2num (l));
461 SCM retval = scm_call_2 (cb, self_scm (), scm_int2num (a));
463 Real r = ly_scm2double (retval);
464 if (isinf (r) || isnan (r))
466 programming_error (INFINITY_MSG);
467 r = 0.0;
469 me->dim_cache_[a].offset_ +=r;
471 return dim_cache_[a].offset_;
475 bool
476 Grob::is_empty (Axis a)const
478 return ! (ly_pair_p (dim_cache_[a].dimension_) ||
479 ly_procedure_p (dim_cache_[a].dimension_));
482 Interval
483 Grob::extent (Grob * refp, Axis a) const
485 Real x = relative_coordinate (refp, a);
488 Dimension_cache * d = (Dimension_cache *)&dim_cache_[a];
489 Interval ext ;
490 if (ly_pair_p (d->dimension_))
492 else if (ly_procedure_p (d->dimension_))
495 FIXME: add doco on types, and should typecheck maybe?
497 d->dimension_= scm_call_2 (d->dimension_, self_scm (), scm_int2num (a));
499 else
500 return ext;
502 if (!ly_pair_p (d->dimension_))
503 return ext;
505 ext = ly_scm2interval (d->dimension_);
507 SCM extra = get_property (a == X_AXIS
508 ? "extra-X-extent"
509 : "extra-Y-extent");
512 signs ?
514 if (ly_pair_p (extra))
516 ext[BIGGER] += ly_scm2double (ly_cdr (extra));
517 ext[SMALLER] += ly_scm2double (ly_car (extra));
520 extra = get_property (a == X_AXIS
521 ? "minimum-X-extent"
522 : "minimum-Y-extent");
523 if (ly_pair_p (extra))
525 ext.unite (Interval (ly_scm2double (ly_car (extra)),
526 ly_scm2double (ly_cdr (extra))));
529 ext.translate (x);
531 return ext;
535 Find the group-element which has both #this# and #s#
537 Grob *
538 Grob::common_refpoint (Grob const* s, Axis a) const
541 I don't like the quadratic aspect of this code, but I see no other
542 way. The largest chain of parents might be 10 high or so, so
543 it shouldn't be a real issue. */
544 for (Grob const *c = this; c; c = c->dim_cache_[a].parent_)
545 for (Grob const * d = s; d; d = d->dim_cache_[a].parent_)
546 if (d == c)
547 return (Grob*)d;
549 return 0;
553 Grob *
554 common_refpoint_of_list (SCM elist, Grob *common, Axis a)
556 for (; ly_pair_p (elist); elist = ly_cdr (elist))
558 Grob * s = unsmob_grob (ly_car (elist));
559 if (!s)
560 continue;
561 if (common)
562 common = common->common_refpoint (s, a);
563 else
564 common = s;
567 return common;
572 Grob *
573 common_refpoint_of_array (Link_array<Grob> const &arr, Grob *common, Axis a)
575 for (int i = arr.size () ; i--; )
577 Grob * s = arr[i];
578 if (!s)
579 continue;
581 if (common)
582 common = common->common_refpoint (s, a);
583 else
584 common = s;
587 return common;
590 String
591 Grob::name () const
593 SCM meta = get_property ("meta");
594 SCM nm = scm_assoc (ly_symbol2scm ("name"), meta);
595 nm = (ly_pair_p (nm)) ? ly_cdr (nm) : SCM_EOL;
596 return ly_symbol_p (nm) ? ly_symbol2string (nm) : classname (this);
599 void
600 Grob::add_offset_callback (SCM cb, Axis a)
602 if (!has_offset_callback (cb, a))
604 dim_cache_[a].offset_callbacks_ = scm_cons (cb, dim_cache_[a].offset_callbacks_);
605 dim_cache_[a].offsets_left_ ++;
609 bool
610 Grob::has_extent_callback (SCM cb, Axis a)const
612 return scm_equal_p (cb, dim_cache_[a].dimension_) == SCM_BOOL_T;
616 bool
617 Grob::has_offset_callback (SCM cb, Axis a)const
619 return scm_c_memq (cb, dim_cache_[a].offset_callbacks_) != SCM_BOOL_F;
622 void
623 Grob::set_extent (SCM dc, Axis a)
625 dim_cache_[a].dimension_ =dc;
628 void
629 Grob::set_parent (Grob *g, Axis a)
631 dim_cache_[a].parent_ = g;
634 MAKE_SCHEME_CALLBACK (Grob,fixup_refpoint,1);
636 Grob::fixup_refpoint (SCM smob)
638 Grob *me = unsmob_grob (smob);
639 for (int a = X_AXIS; a < NO_AXES; a ++)
641 Axis ax = (Axis)a;
642 Grob * parent = me->get_parent (ax);
644 if (!parent)
645 continue;
647 if (parent->get_system () != me->get_system () && me->get_system ())
649 Grob * newparent = parent->find_broken_piece (me->get_system ());
650 me->set_parent (newparent, ax);
653 if (Item * i = dynamic_cast<Item*> (me))
655 Item *parenti = dynamic_cast<Item*> (parent);
657 if (parenti && i)
659 Direction my_dir = i->break_status_dir () ;
660 if (my_dir!= parenti->break_status_dir ())
662 Item *newparent = parenti->find_prebroken_piece (my_dir);
663 me->set_parent (newparent, ax);
668 return smob;
671 void
672 Grob::warning (String s)const
674 SCM cause = self_scm ();
675 while (Grob * g = unsmob_grob (cause))
677 cause = g->get_property ("cause");
680 if (Music *m = unsmob_music (cause))
682 m->origin ()->warning (s);
684 else
685 ::warning (s);
688 void
689 Grob::programming_error (String s)const
691 s = "Programming error: " + s;
692 warning (s);
696 /****************************************************
697 SMOB funcs
698 ****************************************************/
702 IMPLEMENT_SMOBS (Grob);
703 IMPLEMENT_DEFAULT_EQUAL_P (Grob);
706 Grob::mark_smob (SCM ses)
708 Grob * s = (Grob*) SCM_CELL_WORD_1 (ses);
709 scm_gc_mark (s->immutable_property_alist_);
711 for (int a =0 ; a < 2; a++)
713 scm_gc_mark (s->dim_cache_[a].offset_callbacks_);
714 scm_gc_mark (s->dim_cache_[a].dimension_);
717 don't mark the parents. The pointers in the mutable property
718 list form two tree like structures (one for X relations, one
719 for Y relations). Marking these can be done in limited stack
720 space. If we add the parents, we will jump between X and Y in
721 an erratic manner, leading to much more recursion depth (and
722 core dumps if we link to pthreads.)
726 if (s->original_)
727 scm_gc_mark (s->original_->self_scm ());
729 s->do_derived_mark ();
730 return s->mutable_property_alist_;
734 Grob::print_smob (SCM s, SCM port, scm_print_state *)
736 Grob *sc = (Grob *) ly_cdr (s);
738 scm_puts ("#<Grob ", port);
739 scm_puts ((char *)sc->name ().to_str0 (), port);
742 don't try to print properties, that is too much hassle.
744 scm_puts (" >", port);
745 return 1;
749 Grob::do_derived_mark () const
751 return SCM_EOL;
756 void
757 Grob::discretionary_processing ()
761 bool
762 Grob::internal_has_interface (SCM k)
764 SCM ifs = get_property ("interfaces");
766 return scm_c_memq (k, ifs) != SCM_BOOL_F;
770 /** Return Array of Grobs in SCM list L */
771 Link_array<Grob>
772 ly_scm2grobs (SCM l)
774 Link_array<Grob> arr;
776 for (SCM s = l; ly_pair_p (s); s = ly_cdr (s))
778 SCM e = ly_car (s);
779 arr.push (unsmob_grob (e));
782 arr.reverse ();
783 return arr;
786 /** Return SCM list of Grob array A */
788 ly_grobs2scm (Link_array<Grob> a)
790 SCM s = SCM_EOL;
791 for (int i = a.size (); i; i--)
792 s = scm_cons (a[i-1]->self_scm (), s);
794 return s;
798 IMPLEMENT_TYPE_P (Grob, "ly:grob?");
800 ADD_INTERFACE (Grob, "grob-interface",
801 "A grob represents a piece of music notation\n"
802 "\n"
803 "All grobs have an X and Y-position on the page. These X and Y positions\n"
804 "are stored in a relative format, so they can easily be combined by\n"
805 "stacking them, hanging one grob to the side of another, and coupling\n"
806 "them into a grouping objects.\n"
807 "\n"
808 "Each grob has a reference point (a.k.a. parent): the position of a grob\n"
809 "is stored relative to that reference point. For example the X-reference\n"
810 "point of a staccato dot usually is the note head that it applies\n"
811 "to. When the note head is moved, the staccato dot moves along\n"
812 "automatically.\n"
813 "\n"
814 "A grob is often associated with a symbol, but some grobs do not print\n"
815 "any symbols. They take care of grouping objects. For example, there is a\n"
816 "separate grob that stacks staves vertically. The @ref{NoteCollision}\n"
817 "is also an abstract grob: it only moves around chords, but doesn't print\n"
818 "anything.\n"
819 "\n"
820 "Grobs have a properties: Scheme variables, that can be read and set. "
821 "They have two types. Immutable variables "
822 "define the default style and behavior. They are shared between many objects. "
823 "They can be changed using @code{\\override} and @code{\\revert}. "
824 "\n\n"
825 "Mutable properties are variables that are specific to one grob. Typically, "
826 "lists of other objects, or results from computations are stored in"
827 "mutable properties: every call to set-grob-property (or its C++ equivalent) "
828 "sets a mutable property. "
831 "X-offset-callbacks Y-offset-callbacks X-extent-callback stencil cause "
832 "Y-extent-callback print-function extra-offset spacing-procedure "
833 "staff-symbol interfaces dependencies X-extent Y-extent extra-X-extent "
834 "meta layer before-line-breaking-callback "
835 "after-line-breaking-callback extra-Y-extent minimum-X-extent "
836 "minimum-Y-extent transparent");