2 score-elem.cc -- implement Score_element
4 source file of the GNU LilyPond music typesetter
6 (c) 1997--2000 Han-Wen Nienhuys <hanwen@cs.uu.nl>
13 #include "libc-extension.hh"
14 #include "group-interface.hh"
16 #include "paper-score.hh"
17 #include "paper-def.hh"
19 #include "molecule.hh"
20 #include "score-element.hh"
23 #include "line-of-score.hh"
25 #include "paper-column.hh"
26 #include "molecule.hh"
28 #include "paper-outputter.hh"
29 #include "dimension-cache.hh"
30 #include "side-position-interface.hh"
36 remove dynamic_cast<Spanner,Item> and put this code into respective
42 Score_element::Score_element(SCM basicprops)
44 set_extent_callback (molecule_extent, X_AXIS);
45 set_extent_callback (molecule_extent, Y_AXIS);
52 #ifndef READONLY_PROPS
53 basic_property_list_ = basicprops;
55 property_alist_ = basicprops;
56 pointer_alist_ = SCM_EOL;
59 set_elt_pointer ("dependencies", SCM_EOL);
60 set_elt_property ("interfaces", SCM_EOL);
64 Score_element::Score_element (Score_element const&s)
65 : dim_cache_ (s.dim_cache_)
68 original_l_ =(Score_element*) &s;
69 property_alist_ = s.property_alist_;
70 #ifndef READONLY_PROPS
71 basic_property_list_ = s.basic_property_list_;
73 TODO: should copy the private part of the list.
76 for (SCM *sp = &s.property_alist_; *sp != basic_property_list_; sp = &SCM_CDR(*sp))
81 pointer_alist_ = SCM_EOL;
83 status_i_ = s.status_i_;
84 lookup_l_ = s.lookup_l_;
85 pscore_l_ = s.pscore_l_;
90 Score_element::~Score_element()
92 // this goes awry when score-elements are copied...
95 Kijk goed naar hoe smobify en unsmobify werken. unsmobify_self
96 is te gebruiken wanneer C++ geheugen beheer weer overneemt van
102 Score_element::get_elt_pointer (const char *nm) const
104 SCM sym = ly_symbol2scm (nm);
105 SCM s = scm_assq(sym, pointer_alist_);
107 return (s == SCM_BOOL_F) ? SCM_UNDEFINED : gh_cdr (s);
110 // should also have one that takes SCM arg.
112 Score_element::get_elt_property (String nm) const
114 SCM sym = ly_symbol2scm (nm.ch_C());
115 SCM s = scm_assq(sym, property_alist_);
120 return SCM_UNDEFINED;
124 Remove the value associated with KEY, and return it. The result is
125 that a next call will yield SCM_UNDEFINED (and not the underlying
129 Score_element::remove_elt_property (const char* key)
131 SCM val = get_elt_property (key);
132 if (val != SCM_UNDEFINED)
133 set_elt_property (key, SCM_UNDEFINED);
138 Score_element::set_elt_property (String k, SCM val)
140 SCM sym = ly_symbol2scm (k.ch_C ());
141 #ifndef READONLY_PROPS
143 destructive if found in my part of the list.
145 for (SCM s = property_alist_; s != basic_property_list_; s =gh_cdr (s))
147 if (gh_caar (s)== sym)
149 gh_set_cdr_x (gh_car (s), val);
154 not found in private list. Override in private list.
159 property_alist_ = gh_cons (gh_cons (sym, val), property_alist_);
163 Score_element::set_elt_pointer (const char* k, SCM v)
165 SCM s = ly_symbol2scm (k);
166 pointer_alist_ = scm_assq_set_x (pointer_alist_, s, v);
171 Score_element::molecule_extent (Score_element const *s, Axis a )
173 Molecule m = s->get_molecule ();
178 Score_element::preset_extent (Score_element const *s , Axis a )
180 SCM ext = s->get_elt_property ((a == X_AXIS)
186 Real l = gh_scm2double (gh_car (ext));
187 Real r = gh_scm2double (gh_cdr (ext));
188 l *= s->paper_l ()->get_var ("staffspace");
189 r *= s->paper_l ()->get_var ("staffspace");
190 return Interval (l, r);
199 Score_element::paper_l () const
201 return pscore_l_ ? pscore_l_->paper_l_ : 0;
205 Score_element::lookup_l () const
209 Score_element * urg = (Score_element*)this;
210 SCM sz = urg->remove_elt_property ("fontsize");
211 int i = (gh_number_p (sz))
215 urg->lookup_l_ = (Lookup*)pscore_l_->paper_l_->lookup_l (i);
221 Score_element::add_processing()
223 assert (status_i_ >=0);
232 Score_element::calculate_dependencies (int final, int busy,
233 Score_element_method_pointer funcptr)
235 assert (status_i_ >=0);
237 if (status_i_ >= final)
240 if (status_i_== busy)
242 programming_error ("Element is busy, come back later");
248 for (SCM d= get_elt_pointer ("dependencies"); gh_pair_p (d); d = gh_cdr (d))
250 unsmob_element (gh_car (d))
251 ->calculate_dependencies (final, busy, funcptr);
259 Score_element::get_molecule () const
261 SCM proc = get_elt_property ("molecule-callback");
262 if (gh_procedure_p (proc))
263 return create_molecule (gh_apply (proc, gh_list (this->self_scm_, SCM_UNDEFINED)));
275 Score_element::do_break_processing()
280 Score_element::after_line_breaking ()
286 Score_element::before_line_breaking ()
291 Score_element::do_space_processing ()
296 Score_element::do_add_processing()
301 MAKE_SCHEME_SCORE_ELEMENT_CALLBACKS(Score_element)
308 Score_element::do_brew_molecule () const
310 SCM glyph = get_elt_property ("glyph");
311 if (gh_string_p (glyph))
313 return lookup_l ()->afm_find (String (ly_scm2string (glyph)));
325 Score_element::line_l() const
331 Score_element::add_dependency (Score_element*e)
335 Pointer_group_interface gi (this, "dependencies");
339 programming_error ("Null dependency added");
346 Do break substitution in S, using CRITERION. Return new value.
347 CRITERION is either a SMOB pointer to the desired line, or a number
348 representing the break direction. Do not modify SRC.
351 Score_element::handle_broken_smobs (SCM src, SCM criterion)
354 Score_element *sc = unsmob_element (src);
357 if (gh_number_p (criterion))
359 Item * i = dynamic_cast<Item*> (sc);
360 Direction d = to_dir (criterion);
361 if (i && i->break_status_dir () != d)
363 Item *br = i->find_prebroken_piece (d);
364 return (br) ? br->self_scm_ : SCM_UNDEFINED;
370 = dynamic_cast<Line_of_score*> (unsmob_element (criterion));
371 if (sc->line_l () != line)
373 sc = sc->find_broken_piece (line);
377 /* now: !sc || (sc && sc->line_l () == line) */
379 return SCM_UNDEFINED;
381 /* now: sc && sc->line_l () == line */
383 || (sc->common_refpoint (line, X_AXIS)
384 && sc->common_refpoint (line, Y_AXIS)))
386 return sc->self_scm_;
388 return SCM_UNDEFINED;
391 else if (gh_pair_p (src))
393 SCM oldcar =gh_car (src);
395 UGH! breaks on circular lists.
397 SCM newcar = handle_broken_smobs (oldcar, criterion);
398 SCM oldcdr = gh_cdr (src);
400 if (newcar == SCM_UNDEFINED
401 && (gh_pair_p (oldcdr) || oldcdr == SCM_EOL))
404 This is tail-recursion, ie.
406 return handle_broken_smobs (cdr, criterion);
408 We don't want to rely on the compiler to do this. Without
409 tail-recursion, this easily crashes with a stack overflow. */
414 SCM newcdr = handle_broken_smobs (oldcdr, criterion);
415 return gh_cons (newcar, newcdr);
424 Score_element::handle_broken_dependencies()
426 Spanner * s= dynamic_cast<Spanner*> (this);
427 if (original_l_ && s)
432 for (int i = 0; i< s->broken_into_l_arr_ .size (); i++)
434 Score_element * sc = s->broken_into_l_arr_[i];
435 Line_of_score * l = sc->line_l ();
437 handle_broken_smobs (pointer_alist_,
438 l ? l->self_scm_ : SCM_UNDEFINED);
443 Line_of_score *line = line_l();
445 if (line && common_refpoint (line, X_AXIS) && common_refpoint (line, Y_AXIS))
448 = handle_broken_smobs (pointer_alist_,
449 line ? line->self_scm_ : SCM_UNDEFINED);
451 else if (dynamic_cast <Line_of_score*> (this))
453 pointer_alist_ = handle_broken_smobs (pointer_alist_,
459 This element is `invalid'; it has been removed from all
460 dependencies, so let's junk the element itself.
462 do not do this for Line_of_score, since that would remove
463 references to the originals of score-elts, which get then GC'd
471 Note that we still want references to this element to be
472 rearranged, and not silently thrown away, so we keep pointers
473 like {broken_into_{drul,array}, original}
476 Score_element::suicide ()
478 property_alist_ = SCM_EOL;
479 pointer_alist_ = SCM_EOL;
480 set_extent_callback (0, Y_AXIS);
481 set_extent_callback (0, X_AXIS);
483 for (int a= X_AXIS; a <= Y_AXIS; a++)
485 dim_cache_[a].off_callbacks_.clear ();
490 Score_element::handle_prebroken_dependencies()
495 Score_element::find_broken_piece (Line_of_score*) const
501 Score_element::translate_axis (Real y, Axis a)
503 dim_cache_[a].offset_ += y;
507 Score_element::relative_coordinate (Score_element const*refp, Axis a) const
513 We catch PARENT_L_ == nil case with this, but we crash if we did
514 not ask for the absolute coordinate (ie. REFP == nil.)
517 if (refp == dim_cache_[a].parent_l_)
518 return get_offset (a);
520 return get_offset (a) + dim_cache_[a].parent_l_->relative_coordinate (refp, a);
524 Score_element::get_offset (Axis a) const
526 Score_element *me = (Score_element*) this;
527 while (dim_cache_[a].off_callbacks_.size ())
529 Offset_callback c = dim_cache_[a].off_callbacks_[0];
530 me->dim_cache_[a].off_callbacks_.del (0);
531 Real r = (*c) (me,a );
532 if (isinf (r) || isnan (r))
535 programming_error ("Infinity or NaN encountered");
537 me->dim_cache_[a].offset_ +=r;
539 return dim_cache_[a].offset_;
544 Score_element::point_dimension_callback (Score_element const* , Axis)
546 return Interval (0,0);
550 Score_element::empty_b (Axis a)const
552 return !dim_cache_[a].extent_callback_l_;
556 Score_element::extent (Axis a) const
558 Dimension_cache * d = (Dimension_cache *)&dim_cache_[a];
559 if (!d->extent_callback_l_)
561 d->dim_.set_empty ();
563 else if (!d->valid_b_)
565 d->dim_= (*d->extent_callback_l_ ) (this, a);
569 Interval ext = d->dim_;
574 SCM extra = get_elt_property (a == X_AXIS
581 Real s = paper_l ()->get_var ("staffspace");
582 if (gh_pair_p (extra))
584 ext[BIGGER] += s * gh_scm2double (gh_cdr (extra));
585 ext[SMALLER] += s * gh_scm2double (gh_car (extra));
588 extra = get_elt_property (a == X_AXIS
590 : "minimum-extent-Y");
591 if (gh_pair_p (extra))
593 ext.unite (Interval (s * gh_scm2double (gh_car (extra)),
594 s * gh_scm2double (gh_cdr (extra))));
602 Score_element::parent_l (Axis a) const
604 return dim_cache_[a].parent_l_;
608 Score_element::common_refpoint (Score_element const* s, Axis a) const
611 I don't like the quadratic aspect of this code. Maybe this should
612 be rewritten some time, but the largest chain of parents might be
613 10 high or so, so it shouldn't be a real issue. */
614 for (Score_element const *c = this; c; c = c->dim_cache_[a].parent_l_)
615 for (Score_element const * d = s; d; d = d->dim_cache_[a].parent_l_)
617 return (Score_element*)d;
624 Score_element::common_refpoint (SCM elist, Axis a) const
626 Score_element * common = (Score_element*) this;
627 for (; gh_pair_p (elist); elist = gh_cdr (elist))
629 Score_element * s = unsmob_element (gh_car (elist));
631 common = common->common_refpoint (s, a);
638 Score_element::name () const
640 return classname (this);
644 Score_element::add_offset_callback (Offset_callback cb, Axis a)
646 dim_cache_[a].off_callbacks_.push (cb);
650 Score_element::has_extent_callback_b (Extent_callback cb, Axis a)const
652 return cb == dim_cache_[a].extent_callback_l_;
656 Score_element::has_offset_callback_b (Offset_callback cb, Axis a)const
658 for (int i= dim_cache_[a].off_callbacks_.size (); i--;)
660 if (dim_cache_[a].off_callbacks_[i] == cb)
667 Score_element::set_extent_callback (Dim_cache_callback dc, Axis a)
669 dim_cache_[a].extent_callback_l_ = dc ;
674 Score_element::set_parent (Score_element *g, Axis a)
676 dim_cache_[a].parent_l_ = g;
680 Score_element::fixup_refpoint ()
682 for (int a = X_AXIS; a < NO_AXES; a ++)
685 Score_element * parent = parent_l (ax);
690 if (parent->line_l () != line_l () && line_l ())
692 Score_element * newparent = parent->find_broken_piece (line_l ());
693 set_parent (newparent, ax);
696 if (Item * i = dynamic_cast<Item*> (this))
698 Item *parenti = dynamic_cast<Item*> (parent);
702 Direction my_dir = i->break_status_dir () ;
703 if (my_dir!= parenti->break_status_dir())
705 Item *newparent = parenti->find_prebroken_piece (my_dir);
706 set_parent (newparent, ax);
715 /****************************************************
717 ****************************************************/
719 #include "ly-smobs.icc"
721 IMPLEMENT_UNSMOB(Score_element, element);
722 IMPLEMENT_SMOBS(Score_element);
725 Score_element::mark_smob (SCM ses)
727 Score_element * s = SMOB_TO_TYPE (Score_element, ses);
728 if (s->self_scm_ != ses)
730 programming_error ("SMOB marking gone awry");
733 scm_gc_mark (s->pointer_alist_);
735 s->do_derived_mark ();
736 if (s->parent_l (Y_AXIS))
737 scm_gc_mark (s->parent_l (Y_AXIS)->self_scm_);
738 if (s->parent_l (X_AXIS))
739 scm_gc_mark (s->parent_l (X_AXIS)->self_scm_);
741 return s->property_alist_;
745 Score_element::print_smob (SCM s, SCM port, scm_print_state *)
747 Score_element *sc = (Score_element *) gh_cdr (s);
749 scm_puts ("#<Score_element ", port);
750 scm_puts ((char *)sc->name (), port);
753 don't try to print properties, that is too much hassle.
755 scm_puts (" >", port);
760 Score_element::do_derived_mark ()
765 Score_element::do_smobify_self ()
770 Score_element::equal_p (SCM a, SCM b)
772 return gh_cdr(a) == gh_cdr(b) ? SCM_BOOL_T : SCM_BOOL_F;
777 Score_element::ly_set_elt_property (SCM elt, SCM sym, SCM val)
779 Score_element * sc = unsmob_element (elt);
781 if (!gh_symbol_p (sym))
783 error ("Not a symbol");
784 ly_display_scm (sym);
785 return SCM_UNDEFINED;
790 sc->property_alist_ = scm_assq_set_x (sc->property_alist_, sym, val);
794 error ("Not a score element");
795 ly_display_scm (elt);
798 return SCM_UNDEFINED;
803 Score_element::ly_get_elt_property (SCM elt, SCM sym)
805 Score_element * sc = unsmob_element (elt);
809 SCM s = scm_assq(sym, sc->property_alist_);
814 return SCM_UNDEFINED;
818 error ("Not a score element");
819 ly_display_scm (elt);
821 return SCM_UNDEFINED;
828 scm_make_gsubr ("ly-get-elt-property", 2, 0, 0, (SCM(*)(...))Score_element::ly_get_elt_property);
829 scm_make_gsubr ("ly-set-elt-property", 3, 0, 0, (SCM(*)(...))Score_element::ly_set_elt_property);
832 ADD_SCM_INIT_FUNC(scoreelt, init_functions);
835 Score_element::discretionary_processing()