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_ = SCM_EOL;
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);
125 property_alist_ = scm_assq_remove_x (property_alist_, sym);
130 Score_element::set_elt_property (String k, SCM v)
132 SCM s = ly_symbol2scm (k.ch_C ());
133 property_alist_ = scm_assq_set_x (property_alist_, s, v);
137 Score_element::set_elt_pointer (const char* k, SCM v)
139 SCM s = ly_symbol2scm (k);
140 pointer_alist_ = scm_assq_set_x (pointer_alist_, s, v);
145 Score_element::molecule_extent (Score_element const *s, Axis a )
147 Molecule m = s->do_brew_molecule();
152 Score_element::preset_extent (Score_element const *s , Axis a )
154 SCM ext = s->get_elt_property ((a == X_AXIS)
160 Real l = gh_scm2double (gh_car (ext));
161 Real r = gh_scm2double (gh_cdr (ext));
162 l *= s->paper_l ()->get_var ("staffspace");
163 r *= s->paper_l ()->get_var ("staffspace");
164 return Interval (l, r);
173 Score_element::paper_l () const
175 return pscore_l_ ? pscore_l_->paper_l_ : 0;
179 Score_element::lookup_l () const
183 Score_element * urg = (Score_element*)this;
184 SCM sz = urg->remove_elt_property ("fontsize");
185 int i = (gh_number_p (sz))
189 urg->lookup_l_ = (Lookup*)pscore_l_->paper_l_->lookup_l (i);
195 Score_element::add_processing()
197 assert (status_i_ >=0);
206 Score_element::calculate_dependencies (int final, int busy,
207 Score_element_method_pointer funcptr)
209 assert (status_i_ >=0);
211 if (status_i_ >= final)
214 if (status_i_== busy)
216 programming_error ("Element is busy, come back later");
222 for (SCM d= get_elt_pointer ("dependencies"); gh_pair_p (d); d = gh_cdr (d))
224 unsmob_element (gh_car (d))
225 ->calculate_dependencies (final, busy, funcptr);
233 Score_element::get_molecule () const
235 if (to_boolean (get_elt_property ("transparent")))
238 return do_brew_molecule ();
248 Score_element::do_break_processing()
253 Score_element::after_line_breaking ()
259 Score_element::before_line_breaking ()
264 Score_element::do_space_processing ()
269 Score_element::do_add_processing()
278 Score_element::do_brew_molecule() const
280 SCM glyph = get_elt_property ("glyph");
281 if (gh_string_p (glyph))
283 return lookup_l ()->afm_find (String (ly_scm2string (glyph)));
296 Score_element::line_l() const
302 Score_element::add_dependency (Score_element*e)
306 Pointer_group_interface gi (this, "dependencies");
310 programming_error ("Null dependency added");
317 Do break substitution in S, using CRITERION. Return new value.
318 CRITERION is either a SMOB pointer to the desired line, or a number
319 representing the break direction. Do not modify SRC.
322 Score_element::handle_broken_smobs (SCM src, SCM criterion)
325 Score_element *sc = unsmob_element (src);
328 if (gh_number_p (criterion))
330 Item * i = dynamic_cast<Item*> (sc);
331 Direction d = to_dir (criterion);
332 if (i && i->break_status_dir () != d)
334 Item *br = i->find_prebroken_piece (d);
335 return (br) ? br->self_scm_ : SCM_UNDEFINED;
341 = dynamic_cast<Line_of_score*> (unsmob_element (criterion));
342 if (sc->line_l () != line)
344 sc = sc->find_broken_piece (line);
348 /* now: !sc || (sc && sc->line_l () == line) */
350 return SCM_UNDEFINED;
352 /* now: sc && sc->line_l () == line */
354 || (sc->common_refpoint (line, X_AXIS)
355 && sc->common_refpoint (line, Y_AXIS)))
357 return sc->self_scm_;
359 return SCM_UNDEFINED;
362 else if (gh_pair_p (src))
364 SCM oldcar =gh_car (src);
366 UGH! breaks on circular lists.
368 SCM newcar = handle_broken_smobs (oldcar, criterion);
369 SCM oldcdr = gh_cdr (src);
371 if (newcar == SCM_UNDEFINED
372 && (gh_pair_p (oldcdr) || oldcdr == SCM_EOL))
375 This is tail-recursion, ie.
377 return handle_broken_smobs (cdr, criterion);
379 We don't want to rely on the compiler to do this. Without
380 tail-recursion, this easily crashes with a stack overflow. */
385 SCM newcdr = handle_broken_smobs (oldcdr, criterion);
386 return gh_cons (newcar, newcdr);
395 Score_element::handle_broken_dependencies()
397 Spanner * s= dynamic_cast<Spanner*> (this);
398 if (original_l_ && s)
403 for (int i = 0; i< s->broken_into_l_arr_ .size (); i++)
405 Score_element * sc = s->broken_into_l_arr_[i];
406 Line_of_score * l = sc->line_l ();
407 s->broken_into_l_arr_[i]->property_alist_ =
408 handle_broken_smobs (property_alist_,
409 l ? l->self_scm_ : SCM_UNDEFINED);
410 s->broken_into_l_arr_[i]->pointer_alist_ =
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()