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 property_alist_ = SCM_EOL;
54 pointer_alist_ = SCM_EOL;
57 set_elt_pointer ("dependencies", SCM_EOL);
58 set_elt_property ("interfaces", SCM_EOL);
62 Score_element::Score_element (Score_element const&s)
63 : dim_cache_ (s.dim_cache_)
67 original_l_ =(Score_element*) &s;
68 property_alist_ = s.property_alist_;
69 pointer_alist_ = SCM_EOL;
71 status_i_ = s.status_i_;
72 lookup_l_ = s.lookup_l_;
73 pscore_l_ = s.pscore_l_;
78 Score_element::~Score_element()
82 // should also have one that takes SCM arg.
84 Score_element::get_elt_property (String nm) const
86 SCM sym = ly_symbol2scm (nm.ch_C());
87 SCM s = scm_assq(sym, property_alist_);
97 SCM sym2 = ly_symbol2scm ((name () + ("::" + nm)).ch_C());
100 // should probably check for Type::sym as well.
101 Paper_def * p= pscore_l_->paper_l_;
102 if (p->default_properties_.try_retrieve (sym2, &val))
104 else if (p->default_properties_.try_retrieve (sym, &val))
108 return SCM_UNDEFINED;
112 Score_element::get_elt_pointer (const char *nm) const
114 SCM sym = ly_symbol2scm (nm);
115 SCM s = scm_assq(sym, pointer_alist_);
117 return (s == SCM_BOOL_F) ? SCM_UNDEFINED : gh_cdr (s);
121 Score_element::remove_elt_property (const char* key)
123 SCM s = get_elt_property (key);
124 SCM sym = ly_symbol2scm (key);
126 property_alist_ = gh_cons (gh_cons (sym, SCM_UNDEFINED), property_alist_);
132 Score_element::set_elt_property (String k, SCM v)
134 SCM s = ly_symbol2scm (k.ch_C ());
136 property_alist_ = gh_cons (gh_cons (s, v),property_alist_);
140 Score_element::set_elt_pointer (const char* k, SCM v)
142 SCM s = ly_symbol2scm (k);
143 pointer_alist_ = scm_assq_set_x (pointer_alist_, s, v);
148 Score_element::molecule_extent (Score_element const *s, Axis a )
150 Molecule m = s->do_brew_molecule();
155 Score_element::preset_extent (Score_element const *s , Axis a )
157 SCM ext = s->get_elt_property ((a == X_AXIS)
163 Real l = gh_scm2double (gh_car (ext));
164 Real r = gh_scm2double (gh_cdr (ext));
165 l *= s->paper_l ()->get_var ("staffspace");
166 r *= s->paper_l ()->get_var ("staffspace");
167 return Interval (l, r);
176 Score_element::paper_l () const
178 return pscore_l_ ? pscore_l_->paper_l_ : 0;
182 Score_element::lookup_l () const
186 Score_element * urg = (Score_element*)this;
187 SCM sz = urg->remove_elt_property ("fontsize");
188 int i = (gh_number_p (sz))
192 urg->lookup_l_ = (Lookup*)pscore_l_->paper_l_->lookup_l (i);
198 Score_element::add_processing()
200 assert (status_i_ >=0);
209 Score_element::calculate_dependencies (int final, int busy,
210 Score_element_method_pointer funcptr)
212 assert (status_i_ >=0);
214 if (status_i_ >= final)
217 if (status_i_== busy)
219 programming_error ("Element is busy, come back later");
225 for (SCM d= get_elt_pointer ("dependencies"); gh_pair_p (d); d = gh_cdr (d))
227 unsmob_element (gh_car (d))
228 ->calculate_dependencies (final, busy, funcptr);
236 Score_element::get_molecule () const
238 if (to_boolean (get_elt_property ("transparent")))
241 return do_brew_molecule ();
251 Score_element::do_break_processing()
256 Score_element::after_line_breaking ()
262 Score_element::before_line_breaking ()
267 Score_element::do_space_processing ()
272 Score_element::do_add_processing()
281 Score_element::do_brew_molecule() const
283 SCM glyph = get_elt_property ("glyph");
284 if (gh_string_p (glyph))
286 return lookup_l ()->afm_find (String (ly_scm2string (glyph)));
299 Score_element::line_l() const
305 Score_element::add_dependency (Score_element*e)
309 Pointer_group_interface gi (this, "dependencies");
313 programming_error ("Null dependency added");
320 Do break substitution in S, using CRITERION. Return new value.
321 CRITERION is either a SMOB pointer to the desired line, or a number
322 representing the break direction. Do not modify SRC.
325 Score_element::handle_broken_smobs (SCM src, SCM criterion)
328 Score_element *sc = unsmob_element (src);
331 if (gh_number_p (criterion))
333 Item * i = dynamic_cast<Item*> (sc);
334 Direction d = to_dir (criterion);
335 if (i && i->break_status_dir () != d)
337 Item *br = i->find_prebroken_piece (d);
338 return (br) ? br->self_scm_ : SCM_UNDEFINED;
344 = dynamic_cast<Line_of_score*> (unsmob_element (criterion));
345 if (sc->line_l () != line)
347 sc = sc->find_broken_piece (line);
351 /* now: !sc || (sc && sc->line_l () == line) */
353 return SCM_UNDEFINED;
355 /* now: sc && sc->line_l () == line */
357 || (sc->common_refpoint (line, X_AXIS)
358 && sc->common_refpoint (line, Y_AXIS)))
360 return sc->self_scm_;
362 return SCM_UNDEFINED;
365 else if (gh_pair_p (src))
367 SCM oldcar =gh_car (src);
369 UGH! breaks on circular lists.
371 SCM newcar = handle_broken_smobs (oldcar, criterion);
372 SCM oldcdr = gh_cdr (src);
374 if (newcar == SCM_UNDEFINED
375 && (gh_pair_p (oldcdr) || oldcdr == SCM_EOL))
378 This is tail-recursion, ie.
380 return handle_broken_smobs (cdr, criterion);
382 We don't want to rely on the compiler to do this. Without
383 tail-recursion, this easily crashes with a stack overflow. */
388 SCM newcdr = handle_broken_smobs (oldcdr, criterion);
389 return gh_cons (newcar, newcdr);
398 Score_element::handle_broken_dependencies()
400 Spanner * s= dynamic_cast<Spanner*> (this);
401 if (original_l_ && s)
406 for (int i = 0; i< s->broken_into_l_arr_ .size (); i++)
408 Score_element * sc = s->broken_into_l_arr_[i];
409 Line_of_score * l = sc->line_l ();
411 handle_broken_smobs (pointer_alist_,
412 l ? l->self_scm_ : SCM_UNDEFINED);
417 Line_of_score *line = line_l();
419 if (line && common_refpoint (line, X_AXIS) && common_refpoint (line, Y_AXIS))
422 = handle_broken_smobs (pointer_alist_,
423 line ? line->self_scm_ : SCM_UNDEFINED);
425 else if (dynamic_cast <Line_of_score*> (this))
427 pointer_alist_ = handle_broken_smobs (pointer_alist_,
433 This element is `invalid'; it has been removed from all
434 dependencies, so let's junk the element itself.
436 do not do this for Line_of_score, since that would remove
437 references to the originals of score-elts, which get then GC'd
445 Note that we still want references to this element to be
446 rearranged, and not silently thrown away, so we keep pointers
447 like {broken_into_{drul,array}, original}
450 Score_element::suicide ()
452 property_alist_ = SCM_EOL;
453 pointer_alist_ = SCM_EOL;
454 set_extent_callback (0, Y_AXIS);
455 set_extent_callback (0, X_AXIS);
460 Score_element::handle_prebroken_dependencies()
466 Score_element::linked_b() const
472 Score_element::find_broken_piece (Line_of_score*) const
478 Score_element::translate_axis (Real y, Axis a)
480 dim_cache_[a].offset_ += y;
484 Score_element::relative_coordinate (Score_element const*refp, Axis a) const
490 We catch PARENT_L_ == nil case with this, but we crash if we did
491 not ask for the absolute coordinate (ie. REFP == nil.)
494 if (refp == dim_cache_[a].parent_l_)
495 return get_offset (a);
497 return get_offset (a) + dim_cache_[a].parent_l_->relative_coordinate (refp, a);
501 Score_element::get_offset (Axis a) const
503 Score_element *me = (Score_element*) this;
504 while (dim_cache_[a].off_callbacks_.size ())
506 Offset_callback c = dim_cache_[a].off_callbacks_[0];
507 me->dim_cache_[a].off_callbacks_.del (0);
508 Real r = (*c) (me,a );
509 if (isinf (r) || isnan (r))
512 programming_error ("Infinity or NaN encountered");
514 me->dim_cache_[a].offset_ +=r;
516 return dim_cache_[a].offset_;
521 Score_element::point_dimension_callback (Score_element const* , Axis)
523 return Interval (0,0);
527 Score_element::empty_b (Axis a)const
529 return !dim_cache_[a].extent_callback_l_;
533 Score_element::extent (Axis a) const
535 Dimension_cache * d = (Dimension_cache *)&dim_cache_[a];
536 if (!d->extent_callback_l_)
538 d->dim_.set_empty ();
540 else if (!d->valid_b_)
542 d->dim_= (*d->extent_callback_l_ ) (this, a);
546 Interval ext = d->dim_;
551 SCM extra = get_elt_property (a == X_AXIS
558 Real s = paper_l ()->get_var ("staffspace");
559 if (gh_pair_p (extra))
561 ext[BIGGER] += s * gh_scm2double (gh_cdr (extra));
562 ext[SMALLER] += s * gh_scm2double (gh_car (extra));
565 extra = get_elt_property (a == X_AXIS
567 : "minimum-extent-Y");
568 if (gh_pair_p (extra))
570 ext.unite (Interval (s * gh_scm2double (gh_car (extra)),
571 s * gh_scm2double (gh_cdr (extra))));
579 Score_element::parent_l (Axis a) const
581 return dim_cache_[a].parent_l_;
585 Score_element::common_refpoint (Score_element const* s, Axis a) const
588 I don't like the quadratic aspect of this code. Maybe this should
589 be rewritten some time, but the largest chain of parents might be
590 10 high or so, so it shouldn't be a real issue. */
591 for (Score_element const *c = this; c; c = c->dim_cache_[a].parent_l_)
592 for (Score_element const * d = s; d; d = d->dim_cache_[a].parent_l_)
594 return (Score_element*)d;
601 Score_element::common_refpoint (SCM elist, Axis a) const
603 Score_element * common = (Score_element*) this;
604 for (; gh_pair_p (elist); elist = gh_cdr (elist))
606 Score_element * s = unsmob_element (gh_car (elist));
608 common = common->common_refpoint (s, a);
615 Score_element::name () const
617 return classname (this);
621 Score_element::add_offset_callback (Offset_callback cb, Axis a)
623 dim_cache_[a].off_callbacks_.push (cb);
627 Score_element::has_extent_callback_b (Extent_callback cb, Axis a)const
629 return cb == dim_cache_[a].extent_callback_l_;
633 Score_element::has_offset_callback_b (Offset_callback cb, Axis a)const
635 for (int i= dim_cache_[a].off_callbacks_.size (); i--;)
637 if (dim_cache_[a].off_callbacks_[i] == cb)
644 Score_element::set_extent_callback (Dim_cache_callback dc, Axis a)
646 dim_cache_[a].extent_callback_l_ = dc ;
651 Score_element::set_parent (Score_element *g, Axis a)
653 dim_cache_[a].parent_l_ = g;
657 Score_element::fixup_refpoint ()
659 for (int a = X_AXIS; a < NO_AXES; a ++)
662 Score_element * parent = parent_l (ax);
667 if (parent->line_l () != line_l () && line_l ())
669 Score_element * newparent = parent->find_broken_piece (line_l ());
670 set_parent (newparent, ax);
673 if (Item * i = dynamic_cast<Item*> (this))
675 Item *parenti = dynamic_cast<Item*> (parent);
679 Direction my_dir = i->break_status_dir () ;
680 if (my_dir!= parenti->break_status_dir())
682 Item *newparent = parenti->find_prebroken_piece (my_dir);
683 set_parent (newparent, ax);
692 /****************************************************
694 ****************************************************/
696 #include "ly-smobs.icc"
698 IMPLEMENT_UNSMOB(Score_element, element);
699 IMPLEMENT_SMOBS(Score_element);
702 Score_element::mark_smob (SCM ses)
704 Score_element * s = SMOB_TO_TYPE (Score_element, ses);
705 if (s->self_scm_ != ses)
707 programming_error ("SMOB marking gone awry");
710 scm_gc_mark (s->pointer_alist_);
711 return s->property_alist_;
715 Score_element::print_smob (SCM s, SCM port, scm_print_state *)
717 Score_element *sc = (Score_element *) gh_cdr (s);
719 scm_puts ("#<Score_element ", port);
720 scm_puts ((char *)sc->name (), port);
723 don't try to print properties, that is too much hassle.
725 scm_puts (" >", port);
730 Score_element::do_smobify_self ()
735 Score_element::equal_p (SCM a, SCM b)
737 return gh_cdr(a) == gh_cdr(b) ? SCM_BOOL_T : SCM_BOOL_F;
742 Score_element::ly_set_elt_property (SCM elt, SCM sym, SCM val)
744 Score_element * sc = unsmob_element (elt);
746 if (!gh_symbol_p (sym))
748 error ("Not a symbol");
749 ly_display_scm (sym);
750 return SCM_UNDEFINED;
755 sc->property_alist_ = scm_assq_set_x (sc->property_alist_, sym, val);
759 error ("Not a score element");
760 ly_display_scm (elt);
763 return SCM_UNDEFINED;
768 Score_element::ly_get_elt_property (SCM elt, SCM sym)
770 Score_element * sc = unsmob_element (elt);
774 SCM s = scm_assq(sym, sc->property_alist_);
779 return SCM_UNDEFINED;
783 error ("Not a score element");
784 ly_display_scm (elt);
786 return SCM_UNDEFINED;
793 scm_make_gsubr ("ly-get-elt-property", 2, 0, 0, (SCM(*)(...))Score_element::ly_get_elt_property);
794 scm_make_gsubr ("ly-set-elt-property", 3, 0, 0, (SCM(*)(...))Score_element::ly_set_elt_property);
797 ADD_SCM_INIT_FUNC(scoreelt, init_functions);
800 Score_element::discretionary_processing()