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()
96 Score_element::get_elt_pointer (const char *nm) const
98 SCM sym = ly_symbol2scm (nm);
99 SCM s = scm_assq(sym, pointer_alist_);
101 return (s == SCM_BOOL_F) ? SCM_UNDEFINED : gh_cdr (s);
104 // should also have one that takes SCM arg.
106 Score_element::get_elt_property (String nm) const
108 SCM sym = ly_symbol2scm (nm.ch_C());
109 SCM s = scm_assq(sym, property_alist_);
114 return SCM_UNDEFINED;
118 Remove the value associated with KEY, and return it. The result is
119 that a next call will yield SCM_UNDEFINED (and not the underlying
123 Score_element::remove_elt_property (const char* key)
125 SCM val = get_elt_property (key);
126 if (val != SCM_UNDEFINED)
127 set_elt_property (key, SCM_UNDEFINED);
132 Score_element::set_elt_property (String k, SCM val)
134 SCM sym = ly_symbol2scm (k.ch_C ());
135 #ifndef READONLY_PROPS
137 destructive if found in my part of the list.
139 for (SCM s = property_alist_; s != basic_property_list_; s =gh_cdr (s))
141 if (gh_caar (s)== sym)
143 gh_set_cdr_x (gh_car (s), val);
148 not found in private list. Override in private list.
153 property_alist_ = gh_cons (gh_cons (sym, val), property_alist_);
158 Score_element::set_elt_pointer (const char* k, SCM v)
160 SCM s = ly_symbol2scm (k);
161 pointer_alist_ = scm_assq_set_x (pointer_alist_, s, v);
166 Score_element::molecule_extent (Score_element const *s, Axis a )
168 Molecule m = s->do_brew_molecule();
173 Score_element::preset_extent (Score_element const *s , Axis a )
175 SCM ext = s->get_elt_property ((a == X_AXIS)
181 Real l = gh_scm2double (gh_car (ext));
182 Real r = gh_scm2double (gh_cdr (ext));
183 l *= s->paper_l ()->get_var ("staffspace");
184 r *= s->paper_l ()->get_var ("staffspace");
185 return Interval (l, r);
194 Score_element::paper_l () const
196 return pscore_l_ ? pscore_l_->paper_l_ : 0;
200 Score_element::lookup_l () const
204 Score_element * urg = (Score_element*)this;
205 SCM sz = urg->remove_elt_property ("fontsize");
206 int i = (gh_number_p (sz))
210 urg->lookup_l_ = (Lookup*)pscore_l_->paper_l_->lookup_l (i);
216 Score_element::add_processing()
218 assert (status_i_ >=0);
227 Score_element::calculate_dependencies (int final, int busy,
228 Score_element_method_pointer funcptr)
230 assert (status_i_ >=0);
232 if (status_i_ >= final)
235 if (status_i_== busy)
237 programming_error ("Element is busy, come back later");
243 for (SCM d= get_elt_pointer ("dependencies"); gh_pair_p (d); d = gh_cdr (d))
245 unsmob_element (gh_car (d))
246 ->calculate_dependencies (final, busy, funcptr);
254 Score_element::get_molecule () const
256 if (to_boolean (get_elt_property ("transparent")))
259 return do_brew_molecule ();
269 Score_element::do_break_processing()
274 Score_element::after_line_breaking ()
280 Score_element::before_line_breaking ()
285 Score_element::do_space_processing ()
290 Score_element::do_add_processing()
299 Score_element::do_brew_molecule() const
301 SCM glyph = get_elt_property ("glyph");
302 if (gh_string_p (glyph))
304 return lookup_l ()->afm_find (String (ly_scm2string (glyph)));
317 Score_element::line_l() const
323 Score_element::add_dependency (Score_element*e)
327 Pointer_group_interface gi (this, "dependencies");
331 programming_error ("Null dependency added");
338 Do break substitution in S, using CRITERION. Return new value.
339 CRITERION is either a SMOB pointer to the desired line, or a number
340 representing the break direction. Do not modify SRC.
343 Score_element::handle_broken_smobs (SCM src, SCM criterion)
346 Score_element *sc = unsmob_element (src);
349 if (gh_number_p (criterion))
351 Item * i = dynamic_cast<Item*> (sc);
352 Direction d = to_dir (criterion);
353 if (i && i->break_status_dir () != d)
355 Item *br = i->find_prebroken_piece (d);
356 return (br) ? br->self_scm_ : SCM_UNDEFINED;
362 = dynamic_cast<Line_of_score*> (unsmob_element (criterion));
363 if (sc->line_l () != line)
365 sc = sc->find_broken_piece (line);
369 /* now: !sc || (sc && sc->line_l () == line) */
371 return SCM_UNDEFINED;
373 /* now: sc && sc->line_l () == line */
375 || (sc->common_refpoint (line, X_AXIS)
376 && sc->common_refpoint (line, Y_AXIS)))
378 return sc->self_scm_;
380 return SCM_UNDEFINED;
383 else if (gh_pair_p (src))
385 SCM oldcar =gh_car (src);
387 UGH! breaks on circular lists.
389 SCM newcar = handle_broken_smobs (oldcar, criterion);
390 SCM oldcdr = gh_cdr (src);
392 if (newcar == SCM_UNDEFINED
393 && (gh_pair_p (oldcdr) || oldcdr == SCM_EOL))
396 This is tail-recursion, ie.
398 return handle_broken_smobs (cdr, criterion);
400 We don't want to rely on the compiler to do this. Without
401 tail-recursion, this easily crashes with a stack overflow. */
406 SCM newcdr = handle_broken_smobs (oldcdr, criterion);
407 return gh_cons (newcar, newcdr);
416 Score_element::handle_broken_dependencies()
418 Spanner * s= dynamic_cast<Spanner*> (this);
419 if (original_l_ && s)
424 for (int i = 0; i< s->broken_into_l_arr_ .size (); i++)
426 Score_element * sc = s->broken_into_l_arr_[i];
427 Line_of_score * l = sc->line_l ();
429 handle_broken_smobs (pointer_alist_,
430 l ? l->self_scm_ : SCM_UNDEFINED);
435 Line_of_score *line = line_l();
437 if (line && common_refpoint (line, X_AXIS) && common_refpoint (line, Y_AXIS))
440 = handle_broken_smobs (pointer_alist_,
441 line ? line->self_scm_ : SCM_UNDEFINED);
443 else if (dynamic_cast <Line_of_score*> (this))
445 pointer_alist_ = handle_broken_smobs (pointer_alist_,
451 This element is `invalid'; it has been removed from all
452 dependencies, so let's junk the element itself.
454 do not do this for Line_of_score, since that would remove
455 references to the originals of score-elts, which get then GC'd
463 Note that we still want references to this element to be
464 rearranged, and not silently thrown away, so we keep pointers
465 like {broken_into_{drul,array}, original}
468 Score_element::suicide ()
470 property_alist_ = SCM_EOL;
471 pointer_alist_ = SCM_EOL;
472 set_extent_callback (0, Y_AXIS);
473 set_extent_callback (0, X_AXIS);
478 Score_element::handle_prebroken_dependencies()
484 Score_element::find_broken_piece (Line_of_score*) const
490 Score_element::translate_axis (Real y, Axis a)
492 dim_cache_[a].offset_ += y;
496 Score_element::relative_coordinate (Score_element const*refp, Axis a) const
502 We catch PARENT_L_ == nil case with this, but we crash if we did
503 not ask for the absolute coordinate (ie. REFP == nil.)
506 if (refp == dim_cache_[a].parent_l_)
507 return get_offset (a);
509 return get_offset (a) + dim_cache_[a].parent_l_->relative_coordinate (refp, a);
513 Score_element::get_offset (Axis a) const
515 Score_element *me = (Score_element*) this;
516 while (dim_cache_[a].off_callbacks_.size ())
518 Offset_callback c = dim_cache_[a].off_callbacks_[0];
519 me->dim_cache_[a].off_callbacks_.del (0);
520 Real r = (*c) (me,a );
521 if (isinf (r) || isnan (r))
524 programming_error ("Infinity or NaN encountered");
526 me->dim_cache_[a].offset_ +=r;
528 return dim_cache_[a].offset_;
533 Score_element::point_dimension_callback (Score_element const* , Axis)
535 return Interval (0,0);
539 Score_element::empty_b (Axis a)const
541 return !dim_cache_[a].extent_callback_l_;
545 Score_element::extent (Axis a) const
547 Dimension_cache * d = (Dimension_cache *)&dim_cache_[a];
548 if (!d->extent_callback_l_)
550 d->dim_.set_empty ();
552 else if (!d->valid_b_)
554 d->dim_= (*d->extent_callback_l_ ) (this, a);
558 Interval ext = d->dim_;
563 SCM extra = get_elt_property (a == X_AXIS
570 Real s = paper_l ()->get_var ("staffspace");
571 if (gh_pair_p (extra))
573 ext[BIGGER] += s * gh_scm2double (gh_cdr (extra));
574 ext[SMALLER] += s * gh_scm2double (gh_car (extra));
577 extra = get_elt_property (a == X_AXIS
579 : "minimum-extent-Y");
580 if (gh_pair_p (extra))
582 ext.unite (Interval (s * gh_scm2double (gh_car (extra)),
583 s * gh_scm2double (gh_cdr (extra))));
591 Score_element::parent_l (Axis a) const
593 return dim_cache_[a].parent_l_;
597 Score_element::common_refpoint (Score_element const* s, Axis a) const
600 I don't like the quadratic aspect of this code. Maybe this should
601 be rewritten some time, but the largest chain of parents might be
602 10 high or so, so it shouldn't be a real issue. */
603 for (Score_element const *c = this; c; c = c->dim_cache_[a].parent_l_)
604 for (Score_element const * d = s; d; d = d->dim_cache_[a].parent_l_)
606 return (Score_element*)d;
613 Score_element::common_refpoint (SCM elist, Axis a) const
615 Score_element * common = (Score_element*) this;
616 for (; gh_pair_p (elist); elist = gh_cdr (elist))
618 Score_element * s = unsmob_element (gh_car (elist));
620 common = common->common_refpoint (s, a);
627 Score_element::name () const
629 return classname (this);
633 Score_element::add_offset_callback (Offset_callback cb, Axis a)
635 dim_cache_[a].off_callbacks_.push (cb);
639 Score_element::has_extent_callback_b (Extent_callback cb, Axis a)const
641 return cb == dim_cache_[a].extent_callback_l_;
645 Score_element::has_offset_callback_b (Offset_callback cb, Axis a)const
647 for (int i= dim_cache_[a].off_callbacks_.size (); i--;)
649 if (dim_cache_[a].off_callbacks_[i] == cb)
656 Score_element::set_extent_callback (Dim_cache_callback dc, Axis a)
658 dim_cache_[a].extent_callback_l_ = dc ;
663 Score_element::set_parent (Score_element *g, Axis a)
665 dim_cache_[a].parent_l_ = g;
669 Score_element::fixup_refpoint ()
671 for (int a = X_AXIS; a < NO_AXES; a ++)
674 Score_element * parent = parent_l (ax);
679 if (parent->line_l () != line_l () && line_l ())
681 Score_element * newparent = parent->find_broken_piece (line_l ());
682 set_parent (newparent, ax);
685 if (Item * i = dynamic_cast<Item*> (this))
687 Item *parenti = dynamic_cast<Item*> (parent);
691 Direction my_dir = i->break_status_dir () ;
692 if (my_dir!= parenti->break_status_dir())
694 Item *newparent = parenti->find_prebroken_piece (my_dir);
695 set_parent (newparent, ax);
704 /****************************************************
706 ****************************************************/
708 #include "ly-smobs.icc"
710 IMPLEMENT_UNSMOB(Score_element, element);
711 IMPLEMENT_SMOBS(Score_element);
714 Score_element::mark_smob (SCM ses)
716 Score_element * s = SMOB_TO_TYPE (Score_element, ses);
717 if (s->self_scm_ != ses)
719 programming_error ("SMOB marking gone awry");
722 scm_gc_mark (s->pointer_alist_);
723 return s->property_alist_;
727 Score_element::print_smob (SCM s, SCM port, scm_print_state *)
729 Score_element *sc = (Score_element *) gh_cdr (s);
731 scm_puts ("#<Score_element ", port);
732 scm_puts ((char *)sc->name (), port);
735 don't try to print properties, that is too much hassle.
737 scm_puts (" >", port);
742 Score_element::do_smobify_self ()
747 Score_element::equal_p (SCM a, SCM b)
749 return gh_cdr(a) == gh_cdr(b) ? SCM_BOOL_T : SCM_BOOL_F;
754 Score_element::ly_set_elt_property (SCM elt, SCM sym, SCM val)
756 Score_element * sc = unsmob_element (elt);
758 if (!gh_symbol_p (sym))
760 error ("Not a symbol");
761 ly_display_scm (sym);
762 return SCM_UNDEFINED;
767 sc->property_alist_ = scm_assq_set_x (sc->property_alist_, sym, val);
771 error ("Not a score element");
772 ly_display_scm (elt);
775 return SCM_UNDEFINED;
780 Score_element::ly_get_elt_property (SCM elt, SCM sym)
782 Score_element * sc = unsmob_element (elt);
786 SCM s = scm_assq(sym, sc->property_alist_);
791 return SCM_UNDEFINED;
795 error ("Not a score element");
796 ly_display_scm (elt);
798 return SCM_UNDEFINED;
805 scm_make_gsubr ("ly-get-elt-property", 2, 0, 0, (SCM(*)(...))Score_element::ly_get_elt_property);
806 scm_make_gsubr ("ly-set-elt-property", 3, 0, 0, (SCM(*)(...))Score_element::ly_set_elt_property);
809 ADD_SCM_INIT_FUNC(scoreelt, init_functions);
812 Score_element::discretionary_processing()