+
+MAKE_SCHEME_CALLBACK (Stem, calc_length, 1)
+SCM
+Stem::calc_length (SCM smob)
+{
+ Grob *me = unsmob_grob (smob);
+
+ SCM details = me->get_property ("details");
+ int durlog = duration_log (me);
+
+ Real ss = Staff_symbol_referencer::staff_space (me);
+ Real length = 7;
+ SCM s = scm_cdr (scm_assq (ly_symbol2scm ("lengths"), details));
+ if (scm_is_pair (s))
+ length = 2 * scm_to_double (robust_list_ref (durlog - 2, s));
+
+ Direction dir = get_grob_direction (me);
+
+ /* Stems in unnatural (forced) direction should be shortened,
+ according to [Roush & Gourlay] */
+ Interval hp = head_positions (me);
+ if (dir && dir * hp[dir] >= 0)
+ {
+ SCM sshorten = scm_cdr (scm_assq (ly_symbol2scm ("stem-shorten"), details));
+ SCM scm_shorten = scm_is_pair (sshorten)
+ ? robust_list_ref (max (duration_log (me) - 2, 0), sshorten) : SCM_EOL;
+ Real shorten = 2* robust_scm2double (scm_shorten, 0);
+
+ /* On boundary: shorten only half */
+ if (abs (head_positions (me)[dir]) <= 1)
+ shorten *= 0.5;
+
+ length -= shorten;
+ }
+
+ length *= robust_scm2double (me->get_property ("length-fraction"), 1.0);
+
+ /* Tremolo stuff. */
+ Grob *t_flag = unsmob_grob (me->get_object ("tremolo-flag"));
+ if (t_flag && !unsmob_grob (me->get_object ("beam")))
+ {
+ /* Crude hack: add extra space if tremolo flag is there.
+
+ We can't do this for the beam, since we get into a loop
+ (Stem_tremolo::raw_stencil () looks at the beam.) --hwn */
+
+ Real minlen = 1.0
+ + 2 * t_flag->extent (t_flag, Y_AXIS).length ()
+ / ss;
+
+ /* We don't want to add the whole extent of the flag because the trem
+ and the flag can overlap partly. beam_translation gives a good
+ approximation */
+ if (durlog >= 3)
+ {
+ Real beam_trans = Stem_tremolo::get_beam_translation (t_flag);
+ /* the obvious choice is (durlog - 2) here, but we need a bit more space. */
+ minlen += 2 * (durlog - 1.5) * beam_trans;
+
+ /* up-stems need even a little more space to avoid collisions. This
+ needs to be in sync with the tremolo positioning code in
+ Stem_tremolo::print */
+ if (dir == UP)
+ minlen += beam_trans;
+ }
+ length = max (length, minlen + 1.0);
+ }
+
+ return scm_from_double (length);
+}