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...
93 // real weird Paper_column::~Paper_column () -> infinity_mom
99 Score_element::get_elt_pointer (const char *nm) const
101 SCM sym = ly_symbol2scm (nm);
102 SCM s = scm_assq(sym, pointer_alist_);
104 return (s == SCM_BOOL_F) ? SCM_UNDEFINED : gh_cdr (s);
107 // should also have one that takes SCM arg.
109 Score_element::get_elt_property (String nm) const
111 SCM sym = ly_symbol2scm (nm.ch_C());
112 SCM s = scm_assq(sym, property_alist_);
117 return SCM_UNDEFINED;
121 Remove the value associated with KEY, and return it. The result is
122 that a next call will yield SCM_UNDEFINED (and not the underlying
126 Score_element::remove_elt_property (const char* key)
128 SCM val = get_elt_property (key);
129 if (val != SCM_UNDEFINED)
130 set_elt_property (key, SCM_UNDEFINED);
135 Score_element::set_elt_property (String k, SCM val)
137 SCM sym = ly_symbol2scm (k.ch_C ());
138 #ifndef READONLY_PROPS
140 destructive if found in my part of the list.
142 for (SCM s = property_alist_; s != basic_property_list_; s =gh_cdr (s))
144 if (gh_caar (s)== sym)
146 gh_set_cdr_x (gh_car (s), val);
151 not found in private list. Override in private list.
156 property_alist_ = gh_cons (gh_cons (sym, val), property_alist_);
160 Score_element::set_elt_pointer (const char* k, SCM v)
162 SCM s = ly_symbol2scm (k);
163 pointer_alist_ = scm_assq_set_x (pointer_alist_, s, v);
168 Score_element::molecule_extent (Score_element const *s, Axis a )
170 Molecule m = s->get_molecule ();
175 Score_element::preset_extent (Score_element const *s , Axis a )
177 SCM ext = s->get_elt_property ((a == X_AXIS)
183 Real l = gh_scm2double (gh_car (ext));
184 Real r = gh_scm2double (gh_cdr (ext));
185 l *= s->paper_l ()->get_var ("staffspace");
186 r *= s->paper_l ()->get_var ("staffspace");
187 return Interval (l, r);
196 Score_element::paper_l () const
198 return pscore_l_ ? pscore_l_->paper_l_ : 0;
202 Score_element::lookup_l () const
206 Score_element * urg = (Score_element*)this;
207 SCM sz = urg->remove_elt_property ("fontsize");
208 int i = (gh_number_p (sz))
212 urg->lookup_l_ = (Lookup*)pscore_l_->paper_l_->lookup_l (i);
218 Score_element::add_processing()
220 assert (status_i_ >=0);
229 Score_element::calculate_dependencies (int final, int busy,
230 Score_element_method_pointer funcptr)
232 assert (status_i_ >=0);
234 if (status_i_ >= final)
237 if (status_i_== busy)
239 programming_error ("Element is busy, come back later");
245 for (SCM d= get_elt_pointer ("dependencies"); gh_pair_p (d); d = gh_cdr (d))
247 unsmob_element (gh_car (d))
248 ->calculate_dependencies (final, busy, funcptr);
256 Score_element::get_molecule () const
258 SCM proc = get_elt_property ("molecule-callback");
259 if (gh_procedure_p (proc))
260 return create_molecule (gh_apply (proc, gh_list (this->self_scm_, SCM_UNDEFINED)));
272 Score_element::do_break_processing()
277 Score_element::after_line_breaking ()
283 Score_element::before_line_breaking ()
288 Score_element::do_space_processing ()
293 Score_element::do_add_processing()
298 MAKE_SCHEME_SCORE_ELEMENT_CALLBACKS(Score_element)
305 Score_element::do_brew_molecule () const
307 SCM glyph = get_elt_property ("glyph");
308 if (gh_string_p (glyph))
310 return lookup_l ()->afm_find (String (ly_scm2string (glyph)));
322 Score_element::line_l() const
328 Score_element::add_dependency (Score_element*e)
332 Pointer_group_interface gi (this, "dependencies");
336 programming_error ("Null dependency added");
343 Do break substitution in S, using CRITERION. Return new value.
344 CRITERION is either a SMOB pointer to the desired line, or a number
345 representing the break direction. Do not modify SRC.
348 Score_element::handle_broken_smobs (SCM src, SCM criterion)
351 Score_element *sc = unsmob_element (src);
354 if (gh_number_p (criterion))
356 Item * i = dynamic_cast<Item*> (sc);
357 Direction d = to_dir (criterion);
358 if (i && i->break_status_dir () != d)
360 Item *br = i->find_prebroken_piece (d);
361 return (br) ? br->self_scm_ : SCM_UNDEFINED;
367 = dynamic_cast<Line_of_score*> (unsmob_element (criterion));
368 if (sc->line_l () != line)
370 sc = sc->find_broken_piece (line);
374 /* now: !sc || (sc && sc->line_l () == line) */
376 return SCM_UNDEFINED;
378 /* now: sc && sc->line_l () == line */
380 || (sc->common_refpoint (line, X_AXIS)
381 && sc->common_refpoint (line, Y_AXIS)))
383 return sc->self_scm_;
385 return SCM_UNDEFINED;
388 else if (gh_pair_p (src))
390 SCM oldcar =gh_car (src);
392 UGH! breaks on circular lists.
394 SCM newcar = handle_broken_smobs (oldcar, criterion);
395 SCM oldcdr = gh_cdr (src);
397 if (newcar == SCM_UNDEFINED
398 && (gh_pair_p (oldcdr) || oldcdr == SCM_EOL))
401 This is tail-recursion, ie.
403 return handle_broken_smobs (cdr, criterion);
405 We don't want to rely on the compiler to do this. Without
406 tail-recursion, this easily crashes with a stack overflow. */
411 SCM newcdr = handle_broken_smobs (oldcdr, criterion);
412 return gh_cons (newcar, newcdr);
421 Score_element::handle_broken_dependencies()
423 Spanner * s= dynamic_cast<Spanner*> (this);
424 if (original_l_ && s)
429 for (int i = 0; i< s->broken_into_l_arr_ .size (); i++)
431 Score_element * sc = s->broken_into_l_arr_[i];
432 Line_of_score * l = sc->line_l ();
434 handle_broken_smobs (pointer_alist_,
435 l ? l->self_scm_ : SCM_UNDEFINED);
440 Line_of_score *line = line_l();
442 if (line && common_refpoint (line, X_AXIS) && common_refpoint (line, Y_AXIS))
445 = handle_broken_smobs (pointer_alist_,
446 line ? line->self_scm_ : SCM_UNDEFINED);
448 else if (dynamic_cast <Line_of_score*> (this))
450 pointer_alist_ = handle_broken_smobs (pointer_alist_,
456 This element is `invalid'; it has been removed from all
457 dependencies, so let's junk the element itself.
459 do not do this for Line_of_score, since that would remove
460 references to the originals of score-elts, which get then GC'd
468 Note that we still want references to this element to be
469 rearranged, and not silently thrown away, so we keep pointers
470 like {broken_into_{drul,array}, original}
473 Score_element::suicide ()
475 property_alist_ = SCM_EOL;
476 pointer_alist_ = SCM_EOL;
477 set_extent_callback (0, Y_AXIS);
478 set_extent_callback (0, X_AXIS);
480 for (int a= X_AXIS; a <= Y_AXIS; a++)
482 dim_cache_[a].off_callbacks_.clear ();
487 Score_element::handle_prebroken_dependencies()
492 Score_element::find_broken_piece (Line_of_score*) const
498 Score_element::translate_axis (Real y, Axis a)
500 dim_cache_[a].offset_ += y;
504 Score_element::relative_coordinate (Score_element const*refp, Axis a) const
510 We catch PARENT_L_ == nil case with this, but we crash if we did
511 not ask for the absolute coordinate (ie. REFP == nil.)
514 if (refp == dim_cache_[a].parent_l_)
515 return get_offset (a);
517 return get_offset (a) + dim_cache_[a].parent_l_->relative_coordinate (refp, a);
521 Score_element::get_offset (Axis a) const
523 Score_element *me = (Score_element*) this;
524 while (dim_cache_[a].off_callbacks_.size ())
526 Offset_callback c = dim_cache_[a].off_callbacks_[0];
527 me->dim_cache_[a].off_callbacks_.del (0);
528 Real r = (*c) (me,a );
529 if (isinf (r) || isnan (r))
532 programming_error ("Infinity or NaN encountered");
534 me->dim_cache_[a].offset_ +=r;
536 return dim_cache_[a].offset_;
541 Score_element::point_dimension_callback (Score_element const* , Axis)
543 return Interval (0,0);
547 Score_element::empty_b (Axis a)const
549 return !dim_cache_[a].extent_callback_l_;
553 Score_element::extent (Axis a) const
555 Dimension_cache * d = (Dimension_cache *)&dim_cache_[a];
556 if (!d->extent_callback_l_)
558 d->dim_.set_empty ();
560 else if (!d->valid_b_)
562 d->dim_= (*d->extent_callback_l_ ) (this, a);
566 Interval ext = d->dim_;
571 SCM extra = get_elt_property (a == X_AXIS
578 Real s = paper_l ()->get_var ("staffspace");
579 if (gh_pair_p (extra))
581 ext[BIGGER] += s * gh_scm2double (gh_cdr (extra));
582 ext[SMALLER] += s * gh_scm2double (gh_car (extra));
585 extra = get_elt_property (a == X_AXIS
587 : "minimum-extent-Y");
588 if (gh_pair_p (extra))
590 ext.unite (Interval (s * gh_scm2double (gh_car (extra)),
591 s * gh_scm2double (gh_cdr (extra))));
599 Score_element::parent_l (Axis a) const
601 return dim_cache_[a].parent_l_;
605 Score_element::common_refpoint (Score_element const* s, Axis a) const
608 I don't like the quadratic aspect of this code. Maybe this should
609 be rewritten some time, but the largest chain of parents might be
610 10 high or so, so it shouldn't be a real issue. */
611 for (Score_element const *c = this; c; c = c->dim_cache_[a].parent_l_)
612 for (Score_element const * d = s; d; d = d->dim_cache_[a].parent_l_)
614 return (Score_element*)d;
621 Score_element::common_refpoint (SCM elist, Axis a) const
623 Score_element * common = (Score_element*) this;
624 for (; gh_pair_p (elist); elist = gh_cdr (elist))
626 Score_element * s = unsmob_element (gh_car (elist));
628 common = common->common_refpoint (s, a);
635 Score_element::name () const
637 return classname (this);
641 Score_element::add_offset_callback (Offset_callback cb, Axis a)
643 dim_cache_[a].off_callbacks_.push (cb);
647 Score_element::has_extent_callback_b (Extent_callback cb, Axis a)const
649 return cb == dim_cache_[a].extent_callback_l_;
653 Score_element::has_offset_callback_b (Offset_callback cb, Axis a)const
655 for (int i= dim_cache_[a].off_callbacks_.size (); i--;)
657 if (dim_cache_[a].off_callbacks_[i] == cb)
664 Score_element::set_extent_callback (Dim_cache_callback dc, Axis a)
666 dim_cache_[a].extent_callback_l_ = dc ;
671 Score_element::set_parent (Score_element *g, Axis a)
673 dim_cache_[a].parent_l_ = g;
677 Score_element::fixup_refpoint ()
679 for (int a = X_AXIS; a < NO_AXES; a ++)
682 Score_element * parent = parent_l (ax);
687 if (parent->line_l () != line_l () && line_l ())
689 Score_element * newparent = parent->find_broken_piece (line_l ());
690 set_parent (newparent, ax);
693 if (Item * i = dynamic_cast<Item*> (this))
695 Item *parenti = dynamic_cast<Item*> (parent);
699 Direction my_dir = i->break_status_dir () ;
700 if (my_dir!= parenti->break_status_dir())
702 Item *newparent = parenti->find_prebroken_piece (my_dir);
703 set_parent (newparent, ax);
712 /****************************************************
714 ****************************************************/
716 #include "ly-smobs.icc"
718 IMPLEMENT_UNSMOB(Score_element, element);
719 IMPLEMENT_SMOBS(Score_element);
722 Score_element::mark_smob (SCM ses)
724 Score_element * s = SMOB_TO_TYPE (Score_element, ses);
725 if (s->self_scm_ != ses)
727 programming_error ("SMOB marking gone awry");
730 scm_gc_mark (s->pointer_alist_);
732 s->do_derived_mark ();
733 if (s->parent_l (Y_AXIS))
734 scm_gc_mark (s->parent_l (Y_AXIS)->self_scm_);
735 if (s->parent_l (X_AXIS))
736 scm_gc_mark (s->parent_l (X_AXIS)->self_scm_);
738 return s->property_alist_;
742 Score_element::print_smob (SCM s, SCM port, scm_print_state *)
744 Score_element *sc = (Score_element *) gh_cdr (s);
746 scm_puts ("#<Score_element ", port);
747 scm_puts ((char *)sc->name (), port);
750 don't try to print properties, that is too much hassle.
752 scm_puts (" >", port);
757 Score_element::do_derived_mark ()
762 Score_element::do_smobify_self ()
767 Score_element::equal_p (SCM a, SCM b)
769 return gh_cdr(a) == gh_cdr(b) ? SCM_BOOL_T : SCM_BOOL_F;
774 Score_element::ly_set_elt_property (SCM elt, SCM sym, SCM val)
776 Score_element * sc = unsmob_element (elt);
778 if (!gh_symbol_p (sym))
780 error ("Not a symbol");
781 ly_display_scm (sym);
782 return SCM_UNDEFINED;
787 sc->property_alist_ = scm_assq_set_x (sc->property_alist_, sym, val);
791 error ("Not a score element");
792 ly_display_scm (elt);
795 return SCM_UNDEFINED;
800 Score_element::ly_get_elt_property (SCM elt, SCM sym)
802 Score_element * sc = unsmob_element (elt);
806 SCM s = scm_assq(sym, sc->property_alist_);
811 return SCM_UNDEFINED;
815 error ("Not a score element");
816 ly_display_scm (elt);
818 return SCM_UNDEFINED;
825 scm_make_gsubr ("ly-get-elt-property", 2, 0, 0, (SCM(*)(...))Score_element::ly_get_elt_property);
826 scm_make_gsubr ("ly-set-elt-property", 3, 0, 0, (SCM(*)(...))Score_element::ly_set_elt_property);
829 ADD_SCM_INIT_FUNC(scoreelt, init_functions);
832 Score_element::discretionary_processing()