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
40 Score_element::Score_element()
44 set_extent_callback (molecule_extent, X_AXIS);
45 set_extent_callback (molecule_extent, Y_AXIS);
53 element_property_alist_ = SCM_EOL;
56 set_elt_property ("dependencies", SCM_EOL);
57 set_elt_property ("interfaces", SCM_EOL);
61 Score_element::Score_element (Score_element const&s)
62 : dim_cache_ (s.dim_cache_)
66 original_l_ =(Score_element*) &s;
67 element_property_alist_ = SCM_EOL; // onstack;
69 status_i_ = s.status_i_;
70 lookup_l_ = s.lookup_l_;
71 pscore_l_ = s.pscore_l_;
76 Score_element::~Score_element()
80 // should also have one that takes SCM arg.
82 Score_element::get_elt_property (String nm) const
84 SCM sym = ly_symbol2scm (nm.ch_C());
85 SCM s = scm_assq(sym, element_property_alist_);
92 SCM sym2 = ly_symbol2scm ((name () + ("::" + nm)).ch_C());
95 // should probably check for Type::sym as well.
96 Paper_def * p= pscore_l_->paper_l_;
97 if (p->default_properties_.try_retrieve (sym2, &val))
99 else if (p->default_properties_.try_retrieve (sym, &val))
103 return SCM_UNDEFINED;
107 Score_element::remove_elt_property (String key)
109 SCM s = get_elt_property (key);
110 SCM sym = ly_symbol2scm (key.ch_C());
111 element_property_alist_ = scm_assq_remove_x (element_property_alist_, sym);
119 Score_element::set_elt_property (String k, SCM v)
121 SCM s = ly_symbol2scm (k.ch_C( ));
122 element_property_alist_ = scm_assoc_set_x (element_property_alist_, s, v);
126 Score_element::molecule_extent (Score_element const *s, Axis a )
128 Molecule m = s->do_brew_molecule();
133 Score_element::preset_extent (Score_element const *s , Axis a )
135 SCM ext = s->get_elt_property ((a == X_AXIS)
141 Real l = gh_scm2double (gh_car (ext));
142 Real r = gh_scm2double (gh_cdr (ext));
143 l *= s->paper_l ()->get_var ("staffspace");
144 r *= s->paper_l ()->get_var ("staffspace");
145 return Interval (l, r);
154 Score_element::paper_l () const
156 return pscore_l_ ? pscore_l_->paper_l_ : 0;
160 Score_element::lookup_l () const
164 Score_element * urg = (Score_element*)this;
165 SCM sz = urg->remove_elt_property ("fontsize");
166 int i = (gh_number_p (sz))
170 urg->lookup_l_ = (Lookup*)pscore_l_->paper_l_->lookup_l (i);
176 Score_element::add_processing()
178 assert (status_i_ >=0);
187 Score_element::calculate_dependencies (int final, int busy,
188 Score_element_method_pointer funcptr)
190 assert (status_i_ >=0);
192 if (status_i_ >= final)
195 if (status_i_== busy)
197 programming_error ("Element is busy, come back later");
203 for (SCM d= get_elt_property ("dependencies"); gh_pair_p (d); d = gh_cdr (d))
205 unsmob_element (gh_car (d))
206 ->calculate_dependencies (final, busy, funcptr);
214 Score_element::get_molecule () const
216 if (to_boolean (get_elt_property ("transparent")))
219 return do_brew_molecule ();
229 Score_element::do_break_processing()
234 Score_element::after_line_breaking ()
240 Score_element::before_line_breaking ()
245 Score_element::do_space_processing ()
250 Score_element::do_add_processing()
259 Score_element::do_brew_molecule() const
261 SCM glyph = get_elt_property ("glyph");
262 if (gh_string_p (glyph))
264 return lookup_l ()->afm_find (String (ly_scm2string (glyph)));
277 Score_element::line_l() const
283 Score_element::add_dependency (Score_element*e)
287 Group_interface gi (this, "dependencies");
291 programming_error ("Null dependency added");
298 Do break substitution in S, using CRITERION. Return new value.
299 CRITERION is either a SMOB pointer to the desired line, or a number
300 representing the break direction. Do not modify SRC.
303 Score_element::handle_broken_smobs (SCM src, SCM criterion)
306 Score_element *sc = unsmob_element (src);
309 if (gh_number_p (criterion))
311 Item * i = dynamic_cast<Item*> (sc);
312 Direction d = to_dir (criterion);
313 if (i && i->break_status_dir () != d)
315 Item *br = i->find_prebroken_piece (d);
316 return (br) ? br->self_scm_ : SCM_UNDEFINED;
322 = dynamic_cast<Line_of_score*> (unsmob_element (criterion));
323 if (sc->line_l () != line)
325 sc = sc->find_broken_piece (line);
329 /* now: !sc || (sc && sc->line_l () == line) */
331 return SCM_UNDEFINED;
333 /* now: sc && sc->line_l () == line */
335 || (sc->common_refpoint (line, X_AXIS)
336 && sc->common_refpoint (line, Y_AXIS)))
338 return sc->self_scm_;
340 return SCM_UNDEFINED;
343 else if (gh_pair_p (src))
345 SCM oldcar =gh_car (src);
347 UGH! breaks on circular lists.
349 SCM newcar = handle_broken_smobs (oldcar, criterion);
350 SCM oldcdr = gh_cdr (src);
352 if (newcar == SCM_UNDEFINED
353 && (gh_pair_p (oldcdr) || oldcdr == SCM_EOL))
356 This is tail-recursion, ie.
358 return handle_broken_smobs (cdr, criterion);
360 We don't want to rely on the compiler to do this. Without
361 tail-recursion, this easily crashes with a stack overflow. */
366 SCM newcdr = handle_broken_smobs (oldcdr, criterion);
367 return gh_cons (newcar, newcdr);
376 Score_element::handle_broken_dependencies()
378 Spanner * s= dynamic_cast<Spanner*> (this);
379 if (original_l_ && s)
384 for (int i = 0; i< s->broken_into_l_arr_ .size (); i++)
386 Score_element * sc = s->broken_into_l_arr_[i];
387 Line_of_score * l = sc->line_l ();
388 s->broken_into_l_arr_[i]->element_property_alist_ =
389 handle_broken_smobs (element_property_alist_,
390 l ? l->self_scm_ : SCM_UNDEFINED);
395 Line_of_score *line = line_l();
397 if (line && common_refpoint (line, X_AXIS) && common_refpoint (line, Y_AXIS))
399 element_property_alist_
400 = handle_broken_smobs (element_property_alist_,
401 line ? line->self_scm_ : SCM_UNDEFINED);
403 else if (dynamic_cast <Line_of_score*> (this))
405 element_property_alist_ = handle_broken_smobs (element_property_alist_,
411 This element is `invalid'; it has been removed from all
412 dependencies, so let's junk the element itself.
414 do not do this for Line_of_score, since that would remove
415 references to the originals of score-elts, which get then GC'd
423 Note that we still want references to this element to be
424 rearranged, and not silently thrown away, so we keep pointers
425 like {broken_into_{drul,array}, original}
428 Score_element::suicide ()
430 element_property_alist_ = SCM_EOL;
431 set_extent_callback (0, Y_AXIS);
432 set_extent_callback (0, X_AXIS);
437 Score_element::handle_prebroken_dependencies()
443 Score_element::linked_b() const
449 Score_element::find_broken_piece (Line_of_score*) const
455 Score_element::translate_axis (Real y, Axis a)
457 dim_cache_[a].offset_ += y;
461 Score_element::relative_coordinate (Score_element const*refp, Axis a) const
467 We catch PARENT_L_ == nil case with this, but we crash if we did
468 not ask for the absolute coordinate (ie. REFP == nil.)
471 if (refp == dim_cache_[a].parent_l_)
472 return get_offset (a);
474 return get_offset (a) + dim_cache_[a].parent_l_->relative_coordinate (refp, a);
478 Score_element::get_offset (Axis a) const
480 Score_element *me = (Score_element*) this;
481 while (dim_cache_[a].off_callbacks_.size ())
483 Offset_callback c = dim_cache_[a].off_callbacks_[0];
484 me->dim_cache_[a].off_callbacks_.del (0);
485 Real r = (*c) (me,a );
486 if (isinf (r) || isnan (r))
489 programming_error ("Infinity or NaN encountered");
491 me->dim_cache_[a].offset_ +=r;
493 return dim_cache_[a].offset_;
498 Score_element::point_dimension_callback (Score_element const* , Axis)
500 return Interval (0,0);
504 Score_element::empty_b (Axis a)const
506 return !dim_cache_[a].extent_callback_l_;
510 Score_element::extent (Axis a) const
512 Dimension_cache * d = (Dimension_cache *)&dim_cache_[a];
513 if (!d->extent_callback_l_)
515 d->dim_.set_empty ();
517 else if (!d->valid_b_)
519 d->dim_= (*d->extent_callback_l_ ) (this, a);
523 Interval ext = d->dim_;
528 SCM extra = get_elt_property (a == X_AXIS
535 Real s = paper_l ()->get_var ("staffspace");
536 if (gh_pair_p (extra))
538 ext[BIGGER] += s * gh_scm2double (gh_cdr (extra));
539 ext[SMALLER] += s * gh_scm2double (gh_car (extra));
542 extra = get_elt_property (a == X_AXIS
544 : "minimum-extent-Y");
545 if (gh_pair_p (extra))
547 ext.unite (Interval (s * gh_scm2double (gh_car (extra)),
548 s * gh_scm2double (gh_cdr (extra))));
556 Score_element::parent_l (Axis a) const
558 return dim_cache_[a].parent_l_;
562 Score_element::common_refpoint (Score_element const* s, Axis a) const
565 I don't like the quadratic aspect of this code. Maybe this should
566 be rewritten some time, but the largest chain of parents might be
567 10 high or so, so it shouldn't be a real issue. */
568 for (Score_element const *c = this; c; c = c->dim_cache_[a].parent_l_)
569 for (Score_element const * d = s; d; d = d->dim_cache_[a].parent_l_)
571 return (Score_element*)d;
578 Score_element::common_refpoint (SCM elist, Axis a) const
580 Score_element * common = (Score_element*) this;
581 for (; gh_pair_p (elist); elist = gh_cdr (elist))
583 Score_element * s = unsmob_element (gh_car (elist));
585 common = common->common_refpoint (s, a);
592 Score_element::name () const
594 return classname (this);
598 Score_element::add_offset_callback (Offset_callback cb, Axis a)
600 dim_cache_[a].off_callbacks_.push (cb);
604 Score_element::has_extent_callback_b (Extent_callback cb, Axis a)const
606 return cb == dim_cache_[a].extent_callback_l_;
610 Score_element::has_offset_callback_b (Offset_callback cb, Axis a)const
612 for (int i= dim_cache_[a].off_callbacks_.size (); i--;)
614 if (dim_cache_[a].off_callbacks_[i] == cb)
621 Score_element::set_extent_callback (Dim_cache_callback dc, Axis a)
623 dim_cache_[a].extent_callback_l_ = dc ;
628 Score_element::set_parent (Score_element *g, Axis a)
630 dim_cache_[a].parent_l_ = g;
634 Score_element::fixup_refpoint ()
636 for (int a = X_AXIS; a < NO_AXES; a ++)
639 Score_element * parent = parent_l (ax);
644 if (parent->line_l () != line_l () && line_l ())
646 Score_element * newparent = parent->find_broken_piece (line_l ());
647 set_parent (newparent, ax);
650 if (Item * i = dynamic_cast<Item*> (this))
652 Item *parenti = dynamic_cast<Item*> (parent);
656 Direction my_dir = i->break_status_dir () ;
657 if (my_dir!= parenti->break_status_dir())
659 Item *newparent = parenti->find_prebroken_piece (my_dir);
660 set_parent (newparent, ax);
669 /****************************************************
671 ****************************************************/
673 #include "ly-smobs.icc"
675 IMPLEMENT_UNSMOB(Score_element, element);
676 IMPLEMENT_SMOBS(Score_element);
679 Score_element::mark_smob (SCM ses)
681 Score_element * s = SMOB_TO_TYPE (Score_element, ses);
682 if (s->self_scm_ != ses)
684 programming_error ("SMOB marking gone awry");
687 return s->element_property_alist_;
691 Score_element::print_smob (SCM s, SCM port, scm_print_state *)
693 Score_element *sc = (Score_element *) gh_cdr (s);
695 scm_puts ("#<Score_element ", port);
696 scm_puts ((char *)sc->name (), port);
699 don't try to print properties, that is too much hassle.
701 scm_puts (" >", port);
706 Score_element::do_smobify_self ()
711 Score_element::equal_p (SCM a, SCM b)
713 return gh_cdr(a) == gh_cdr(b) ? SCM_BOOL_T : SCM_BOOL_F;
718 Score_element::ly_set_elt_property (SCM elt, SCM sym, SCM val)
720 Score_element * sc = unsmob_element (elt);
722 if (!gh_symbol_p (sym))
724 error ("Not a symbol");
725 ly_display_scm (sym);
726 return SCM_UNDEFINED;
731 sc->element_property_alist_ = scm_assoc_set_x (sc->element_property_alist_, sym, val);
735 error ("Not a score element");
736 ly_display_scm (elt);
739 return SCM_UNDEFINED;
744 Score_element::ly_get_elt_property (SCM elt, SCM sym)
746 Score_element * sc = unsmob_element (elt);
750 SCM s = scm_assq(sym, sc->element_property_alist_);
755 return SCM_UNDEFINED;
759 error ("Not a score element");
760 ly_display_scm (elt);
762 return SCM_UNDEFINED;
769 scm_make_gsubr ("ly-get-elt-property", 2, 0, 0, (SCM(*)(...))Score_element::ly_get_elt_property);
770 scm_make_gsubr ("ly-set-elt-property", 3, 0, 0, (SCM(*)(...))Score_element::ly_set_elt_property);
773 ADD_SCM_INIT_FUNC(scoreelt, init_functions);
776 Score_element::discretionary_processing()