]> git.donarmstrong.com Git - lilypond.git/blobdiff - lily/dynamic-engraver.cc
* lily/tuplet-bracket.cc (calc_positions): multiply with
[lilypond.git] / lily / dynamic-engraver.cc
index d36142c8e2931fc84477c6b03b6c050e0c4e6c74..8d4cb2880f195d1cd0c819092023872ffb154fb5 100644 (file)
 
   source file of the GNU LilyPond music typesetter
 
-  (c)  1997--2000 Han-Wen Nienhuys <hanwen@cs.uu.nl>
+  (c) 1997--2006 Han-Wen Nienhuys <hanwen@xs4all.nl>
 */
-#include "debug.hh"
+
+#include "axis-group-interface.hh"
+#include "context.hh"
 #include "dimensions.hh"
-#include "crescendo.hh"
-#include "musical-request.hh"
-#include "lookup.hh"
-#include "paper-def.hh"
-#include "paper-column.hh"
-#include "staff-symbol.hh"
+#include "directional-element-interface.hh"
+#include "engraver.hh"
+#include "hairpin.hh"
+#include "international.hh"
+#include "interval.hh"
 #include "note-column.hh"
-#include "text-item.hh"
+#include "paper-column.hh"
+#include "pointer-group-interface.hh"
+#include "script-interface.hh"
+#include "self-alignment-interface.hh"
 #include "side-position-interface.hh"
-#include "engraver.hh"
-#include "stem.hh"
-#include "note-head.hh"
-#include "group-interface.hh"
-#include "directional-element-interface.hh"
 #include "staff-symbol-referencer.hh"
-#include "translator-group.hh"
-
-/*
- Wat mij betreft wel DYN_LINE
- */
-#define DYN_LINE
+#include "warn.hh"
 
+#include "translator.icc"
 
-#ifdef DYN_LINE
-class Dynamic_line_spanner : public Spanner
-{
-public:
-  Dynamic_line_spanner ();
-  
-  void add_column (Note_column*);
-  Direction get_default_dir () const;
-protected:
-  virtual void do_post_processing ();
-};
+/*
+  TODO:
 
-Dynamic_line_spanner::Dynamic_line_spanner ()
-{
-  set_elt_property ("transparent", SCM_BOOL_T);
-  side_position (this).set_axis (Y_AXIS);
-}
+  * direction of text-dynamic-event if not equal to direction of
+  line-spanner
 
-void
-Dynamic_line_spanner::add_column (Note_column* n)
-{
-  if (!spanned_drul_[LEFT])
-    set_bounds (LEFT, n);
-  set_bounds (RIGHT, n);
+  - TODO: this engraver is too complicated. We should split it into
+  the handling of the basic grobs and the linespanner
 
-  add_dependency (n);
-}
+  - TODO: the line-spanner is not killed after the (de)crescs are
+  finished.
+*/
 
-Direction
-Dynamic_line_spanner::get_default_dir () const
+/**
+   print text & hairpin dynamics.
+*/
+class Dynamic_engraver : public Engraver
 {
-  return DOWN;
-}
+  Item *script_;
+  Spanner *line_spanner_;
+  Spanner *cresc_;
 
-void
-Dynamic_line_spanner::do_post_processing ()
-{
-  Spanner::do_post_processing ();
-  Direction dir = directional_element (this).get ();
-  if (!dir)
-    dir = get_default_dir ();
+  Spanner *finished_line_spanner_;
+  Spanner *finished_cresc_;
 
-  /*
-    Hier is ook vast iets voor?
-   */
-  Staff_symbol_referencer_interface si (this);
-  Real above_staff = si.line_count () + 2;
+  Music *script_ev_;
+  Music *current_cresc_ev_;
 
-#if 0
-  // Aargh, nu snap ik waarom ik het niet snap
-  // zie Staff_symbol_referencer_interface::set_position 
+  Drul_array<Music *> accepted_spanevents_drul_;
 
-  if (si.position_f () * dir < above_staff)
-    si.set_position (above_staff * (int)dir);
+  vector<Note_column*> pending_columns_;
+  vector<Grob*> pending_elements_;
 
-  SCM s = get_elt_property ("padding");
-  if (gh_number_p (s))
-    {
-      si.set_position (si.position_f () + gh_scm2double (s) * (int) dir);
-    }
-#else
-  Real dy = 0;
-  Real pos = si.position_f () * dir;
-  if (pos * dir < above_staff)
-    dy = above_staff;
-
-  SCM s = get_elt_property ("padding");
-  if (gh_number_p (s))
-    dy += gh_scm2double (s);
-  
-  Real half_space = si.staff_space () / 2;
-  translate_axis (dy*half_space*dir, Y_AXIS);
-#endif
-  
-}
+  void typeset_all ();
 
-#endif
-/*
-  TODO:
-    Baseline alignment / character metrics of dynamic symbols.
- */
+  TRANSLATOR_DECLARATIONS (Dynamic_engraver);
+  DECLARE_ACKNOWLEDGER (script);
+  DECLARE_ACKNOWLEDGER (stem_tremolo);
+  DECLARE_ACKNOWLEDGER (note_column);
+  DECLARE_ACKNOWLEDGER (slur);
 
-/**
-   print text & hairpin dynamics.
- */
-class Dynamic_engraver : public Engraver
-{
-  Text_item * text_p_;
-  Crescendo * finished_cresc_p_;
-  Crescendo * cresc_p_;
-
-  Text_script_req* text_req_l_;
-  Span_req * span_start_req_l_;
-  Drul_array<Span_req*> span_req_l_drul_;
-
-#ifdef DYN_LINE
-  Dynamic_line_spanner* line_spanner_;
-#else
-  Spanner* line_spanner_;
-#endif
-  Moment last_request_mom_;
-  
-  void  typeset_all ();
-
-public:
-  VIRTUAL_COPY_CONS(Translator);
-  Dynamic_engraver ();
-  
 protected:
-  void announce_element (Score_element_info);
-  
-  virtual void do_removal_processing ();
-  virtual void acknowledge_element (Score_element_info);
-  virtual bool do_try_music (Music *req_l);
-  virtual void do_process_requests ();
-  virtual void do_pre_move_processing ();
-  virtual void do_post_move_processing ();
+  virtual void finalize ();
+  virtual bool try_music (Music *event);
+  void stop_translation_timestep ();
+  void process_music ();
 };
 
-ADD_THIS_TRANSLATOR (Dynamic_engraver);
-
-void
-Dynamic_engraver::announce_element (Score_element_info i)
-{
-  group (i.elem_l_, "interfaces").add_thing (ly_symbol2scm ("dynamic"));
-  
-  Engraver::announce_element (i);
-}
-
-
 Dynamic_engraver::Dynamic_engraver ()
 {
-  text_p_ = 0;
-  finished_cresc_p_ = 0;
+  script_ = 0;
+  finished_cresc_ = 0;
   line_spanner_ = 0;
-  span_start_req_l_ = 0;
-  cresc_p_ =0;
-
-  text_req_l_ = 0;
-  span_req_l_drul_[START] = 0;
-  span_req_l_drul_[STOP] = 0;
-}
+  finished_line_spanner_ = 0;
+  current_cresc_ev_ = 0;
+  cresc_ = 0;
 
-void
-Dynamic_engraver::do_post_move_processing ()
-{
-  text_req_l_ = 0;
-  span_req_l_drul_[START] = 0;
-  span_req_l_drul_[STOP] = 0;
+  script_ev_ = 0;
+  accepted_spanevents_drul_[START] = 0;
+  accepted_spanevents_drul_[STOP] = 0;
 }
 
 bool
-Dynamic_engraver::do_try_music (Music * m)
+Dynamic_engraver::try_music (Music *m)
 {
-  if (Text_script_req* d = dynamic_cast <Text_script_req*> (m))
+  if (m->is_mus_type ("absolute-dynamic-event"))
     {
-      if (d->style_str_ == "dynamic")
-       {
-         text_req_l_ = d;
-         return true;
-       }
+      /*
+       TODO: probably broken.
+      */
+      script_ev_ = m;
+      return true;
     }
-  else if (Span_req* s =  dynamic_cast <Span_req*> (m))
+  else if (m->is_mus_type ("decrescendo-event")
+          || m->is_mus_type ("crescendo-event"))
     {
-      if ((s->span_type_str_ == "crescendo"
-          || s->span_type_str_ == "decrescendo"))
-       {
-         span_req_l_drul_[s->span_dir_] = s;
-         return true;
-       }
+      Direction d = to_dir (m->get_property ("span-direction"));
+
+      accepted_spanevents_drul_[d] = m;
+      if (current_cresc_ev_ && d == START)
+       accepted_spanevents_drul_[STOP] = m;
+      return true;
     }
   return false;
 }
 
 void
-Dynamic_engraver::do_process_requests ()
+Dynamic_engraver::process_music ()
 {
-  if ((span_req_l_drul_[START] || text_req_l_) && !line_spanner_)
-    {
-#ifdef DYN_LINE
-      line_spanner_ = new Dynamic_line_spanner;
-#else
-      line_spanner_ = new Spanner;
-      line_spanner_->set_elt_property ("transparent", SCM_BOOL_T);
-      side_position (line_spanner_).set_axis (Y_AXIS);
-#endif
-      announce_element (Score_element_info
-                       (line_spanner_,
-                        text_req_l_ ? text_req_l_ : span_req_l_drul_[START]));
-
-    }
-         
-  if (span_req_l_drul_[START] || text_req_l_)
-    last_request_mom_ = now_mom ();
-  
-#ifndef DYN_LINE
-  if (line_spanner_)
+  if (accepted_spanevents_drul_[START] || accepted_spanevents_drul_[STOP] || script_ev_)
     {
-      /*
-       Generic property will handle this for a Dynamic_line_spanner
-       */
-      Direction dir = DOWN;
-      SCM s = get_property ("dynamicDirection");
-      if (!isdir_b (s))
+      if (!line_spanner_)
        {
-         s = get_property ("verticalDirection");
+         Music *rq = accepted_spanevents_drul_[START];
+         line_spanner_ = make_spanner ("DynamicLineSpanner", rq ? rq->self_scm () : SCM_EOL);
+         if (script_ev_)
+           rq = script_ev_;
        }
-      
-      if (isdir_b (s) && to_dir (s))
-       dir = to_dir (s);
-      
-      line_spanner_->set_elt_property ("direction", gh_int2scm ((int)dir));
-
-      s = get_property ("dynamicPadding");
-      Real padding;
-      if (gh_number_p (s))
-       padding = gh_scm2double (s);
-      else
-       padding = 2;
-      line_spanner_->set_elt_property ("padding", gh_double2scm (padding));
     }
-#endif 
-  
-  if (text_req_l_)
+
+  /*
+    During a (de)crescendo, pending event will not be cleared,
+    and a line-spanner will always be created, as \< \! are already
+    two events.
+
+    Note: line-spanner must always have at least same duration
+    as (de)crecsendo, b.o. line-breaking.
+  */
+
+  /*
+    maybe we should leave dynamic texts to the text-engraver and
+    simply acknowledge them?
+  */
+  if (script_ev_)
     {
-      String loud = text_req_l_->text_str_;
-
-      text_p_ = new Text_item;
-      text_p_->set_elt_property ("text",
-                                         ly_str02scm (loud.ch_C ()));
-      text_p_->set_elt_property ("style", gh_str02scm ("dynamic"));
-      text_p_->set_elt_property ("script-priority",
-                                         gh_int2scm (100));
-         
-      assert (line_spanner_);
-      text_p_->set_parent (line_spanner_, Y_AXIS);
-      announce_element (Score_element_info (text_p_, text_req_l_));
+      script_ = make_item ("DynamicText", script_ev_->self_scm ());
+      script_->set_property ("text",
+                            script_ev_->get_property ("text"));
+
+      if (Direction d = to_dir (script_ev_->get_property ("direction")))
+       set_grob_direction (line_spanner_, d);
+      else if (Direction d = to_dir (line_spanner_->get_property ("direction")))
+       set_grob_direction (script_, d);
+
+      Axis_group_interface::add_element (line_spanner_, script_);
     }
 
-  if (span_req_l_drul_[STOP])
+  Music *stop_ev = accepted_spanevents_drul_ [STOP]
+    ? accepted_spanevents_drul_[STOP] : script_ev_;
+
+  if (accepted_spanevents_drul_[STOP] || script_ev_)
     {
-      if (!cresc_p_)
+      /*
+       finish side position alignment if the (de)cresc ends here, and
+       there are no new dynamics.
+      */
+
+      if (cresc_)
        {
-         span_req_l_drul_[STOP]->warning
-           (_ ("can't find start of (de)crescendo"));
+         assert (!finished_cresc_ && cresc_);
+
+         if (script_)
+           {
+             cresc_->set_bound (RIGHT, script_);
+             add_bound_item (line_spanner_, script_);
+           }
+
+         finished_cresc_ = cresc_;
+         cresc_ = 0;
+         current_cresc_ev_ = 0;
        }
-      else
+      else if (accepted_spanevents_drul_[STOP])
        {
-         assert (!finished_cresc_p_);
-         cresc_p_->set_bounds(RIGHT, get_staff_info ().musical_pcol_l ());
-         finished_cresc_p_ = cresc_p_;
-         cresc_p_ = 0;
-         span_start_req_l_ = 0;
+         accepted_spanevents_drul_[STOP]->origin ()->warning (_ ("can't find start of (de)crescendo"));
+         stop_ev = 0;
        }
     }
 
-  if (span_req_l_drul_[START])
+  if (accepted_spanevents_drul_[START])
     {
-      if (span_start_req_l_)
+      if (current_cresc_ev_)
        {
-         span_req_l_drul_[START]->warning
-           (span_start_req_l_->span_dir_ == 1
-            ?
-            _ ("already have a crescendo")
-            : _ ("already have a decrescendo"));
+         string msg = _ ("already have a decrescendo");
+         if (current_cresc_ev_->is_mus_type ("decrescendo-event"))
+           msg = _ ("already have a crescendo");
+
+         accepted_spanevents_drul_[START]->origin ()->warning (msg);
+         current_cresc_ev_->origin ()->warning (_ ("cresc starts here"));
        }
       else
        {
-         span_start_req_l_ = span_req_l_drul_[START];
-         cresc_p_  = new Crescendo;
-         cresc_p_->set_elt_property
-           ("grow-direction",
-            gh_int2scm ((span_req_l_drul_[START]->span_type_str_ == "crescendo")
-                        ? BIGGER : SMALLER));
-             
-         SCM s = get_property (span_req_l_drul_[START]->span_type_str_ + "Text");
-         if (gh_string_p (s))
+         current_cresc_ev_ = accepted_spanevents_drul_[START];
+
+         if (Direction d = to_dir (current_cresc_ev_->get_property ("direction")))
+           set_grob_direction (line_spanner_, d);
+
+         /*
+           TODO: Use symbols.
+         */
+
+         string start_type
+           = ly_symbol2string (current_cresc_ev_->get_property ("name"));
+
+         /*
+           ugh. Use push/pop?
+         */
+         if (start_type == "DecrescendoEvent")
+           start_type = "decrescendo";
+         else if (start_type == "CrescendoEvent")
+           start_type = "crescendo";
+
+         SCM s = get_property ((start_type + "Spanner").c_str ());
+         if (!scm_is_symbol (s) || s == ly_symbol2scm ("hairpin"))
            {
-             cresc_p_->set_elt_property ("start-text", s);
-             daddy_trans_l_->set_property (span_req_l_drul_[START]->span_type_str_
-                                           + "Text", SCM_UNDEFINED);
+             cresc_ = make_spanner ("Hairpin", accepted_spanevents_drul_[START]->self_scm ());
+             if (finished_cresc_)
+               {
+                 Pointer_group_interface::add_grob (finished_cresc_,
+                                                    ly_symbol2scm ("adjacent-hairpins"),
+                                                    cresc_);
+
+                 Pointer_group_interface::add_grob (cresc_,
+                                                    ly_symbol2scm ("adjacent-hairpins"),
+                                                    finished_cresc_);
+               }
+             cresc_->set_property ("grow-direction",
+                                   scm_from_int ((start_type == "crescendo")
+                                                 ? BIGGER : SMALLER));
            }
 
-         s = get_property (span_req_l_drul_[START]->span_type_str_ + "Spanner");
-         if (gh_string_p (s)) //&& ly_scm2string (s) != "hairpin")
+         /*
+           This is a convenient (and legacy) interface to TextSpanners
+           for use in (de)crescendi.
+           Hmm.
+         */
+         else
            {
-             cresc_p_->set_elt_property ("spanner", s);
-             daddy_trans_l_->set_property (span_req_l_drul_[START]->span_type_str_
-                                           + "Spanner", SCM_UNDEFINED);
+             cresc_ = make_spanner ("DynamicTextSpanner", accepted_spanevents_drul_[START]->self_scm ());
+             cresc_->set_property ("style", s);
+             context ()->set_property ((start_type
+                                        + "Spanner").c_str (), SCM_EOL);
+             s = get_property ((start_type + "Text").c_str ());
+             /*
+               FIXME: use get_markup () to check type.
+             */
+             if (scm_is_string (s) || scm_is_pair (s))
+               {
+                 cresc_->set_property ("edge-text",
+                                       scm_cons (s, scm_makfrom0str ("")));
+                 context ()->set_property ((start_type + "Text").c_str (),
+                                           SCM_EOL);
+               }
            }
 
-         cresc_p_->set_bounds(LEFT, get_staff_info ().musical_pcol_l ());
-         cresc_p_->set_bounds(RIGHT, get_staff_info ().musical_pcol_l ());
-
-         // arrragh, brr, urg: we know how wide text is, no?
-         if (text_p_)
+         if (script_)
            {
-             index_set_cell (cresc_p_->get_elt_property ("dynamic-drul"),
-                             LEFT, SCM_BOOL_T);
-             if (finished_cresc_p_)
-               index_set_cell (finished_cresc_p_->get_elt_property ("dynamic-drul"),
-                               RIGHT, SCM_BOOL_T);
+             cresc_->set_bound (LEFT, script_);
+             add_bound_item (line_spanner_, cresc_->get_bound (LEFT));
            }
 
-         assert (line_spanner_);
-         cresc_p_->set_parent (line_spanner_, Y_AXIS);
-         announce_element (Score_element_info (cresc_p_, span_req_l_drul_[START]));
+         Axis_group_interface::add_element (line_spanner_, cresc_);
        }
     }
 }
 
 void
-Dynamic_engraver::do_pre_move_processing ()
+Dynamic_engraver::stop_translation_timestep ()
 {
+  if (!current_cresc_ev_ && line_spanner_)
+    {
+      assert (!finished_line_spanner_);
+      finished_line_spanner_ = line_spanner_;
+      line_spanner_ = 0;
+    }
+
   typeset_all ();
+
+  if (cresc_ && !cresc_->get_bound (LEFT))
+    {
+      cresc_->set_bound (LEFT, unsmob_grob (get_property ("currentMusicalColumn")));
+      add_bound_item (line_spanner_, cresc_->get_bound (LEFT));
+    }
+
+  script_ev_ = 0;
+  accepted_spanevents_drul_[START] = 0;
+  accepted_spanevents_drul_[STOP] = 0;
 }
 
 void
-Dynamic_engraver::do_removal_processing ()
+Dynamic_engraver::finalize ()
 {
-  if (cresc_p_)
-    {
-      typeset_element (cresc_p_ );
-      span_start_req_l_->warning (_ ("unterminated (de)crescendo"));
-      cresc_p_ =0;
-    }
   typeset_all ();
+
+  if (line_spanner_
+      && !line_spanner_->is_live ())
+    line_spanner_ = 0;
   if (line_spanner_)
     {
-#ifndef DYN_LINE
-      Direction dir = directional_element (line_spanner_).get ();
-      Real staff_space = Staff_symbol_referencer_interface (line_spanner_).staff_space ();
-      SCM s = line_spanner_->get_elt_property ("padding");
-      line_spanner_->translate_axis (gh_scm2double (s) * staff_space * (int)dir, Y_AXIS);
-#endif
-      typeset_element (line_spanner_);
-      line_spanner_ = 0;
+      finished_line_spanner_ = line_spanner_;
+      typeset_all ();
     }
-}
 
+  if (cresc_
+      && !cresc_->is_live ())
+    cresc_ = 0;
+  if (cresc_)
+    {
+      current_cresc_ev_->origin ()->warning (_ ("unterminated (de)crescendo"));
+      cresc_->suicide ();
+      cresc_ = 0;
+    }
+}
 
 void
 Dynamic_engraver::typeset_all ()
-{  
-  if (finished_cresc_p_)
-    {
-      finished_cresc_p_->set_bounds (RIGHT, get_staff_info ().musical_pcol_l ());
-      typeset_element (finished_cresc_p_);
-      finished_cresc_p_ =0;
-    }
-  
-  if (text_p_)
+{
+  if (finished_cresc_)
     {
-      typeset_element (text_p_);
-      text_p_ = 0;
+      if (!finished_cresc_->get_bound (RIGHT))
+       {
+         finished_cresc_->set_bound (RIGHT, script_
+                                     ? script_
+                                     : unsmob_grob (get_property ("currentMusicalColumn")));
+
+         if (finished_line_spanner_)
+           add_bound_item (finished_line_spanner_,
+                           finished_cresc_->get_bound (RIGHT));
+       }
+      finished_cresc_ = 0;
     }
 
-  /*
-    TODO: This should be optionised:
-      * break when group of dynamic requests ends
-      * break now 
-      * continue through piece
-   */
-  if (line_spanner_ && last_request_mom_ < now_mom ())
+  script_ = 0;
+  if (finished_line_spanner_)
     {
-#ifndef DYN_LINE
-      Direction dir = directional_element (line_spanner_).get ();
-      Real staff_space = Staff_symbol_referencer_interface (line_spanner_).staff_space ();
-      SCM s = line_spanner_->get_elt_property ("padding");
-      line_spanner_->translate_axis (gh_scm2double (s) * staff_space * (int)dir, Y_AXIS);
-#endif
-      typeset_element (line_spanner_);
-      line_spanner_ = 0;
+      /*
+       We used to have
+
+       extend-spanner-over-elements (finished_line_spanner_);
+
+       but this is rather kludgy, since finished_line_spanner_
+       typically has a staff-symbol field set , extending it over the
+       entire staff.
+
+      */
+
+      Grob *l = finished_line_spanner_->get_bound (LEFT);
+      Grob *r = finished_line_spanner_->get_bound (RIGHT);
+      if (!r && l)
+       finished_line_spanner_->set_bound (RIGHT, l);
+      else if (!l && r)
+       finished_line_spanner_->set_bound (LEFT, r);
+      else if (!r && !l)
+       {
+         /*
+           This is a isolated dynamic apparently, and does not even have
+           any interesting support item.
+         */
+         Grob *cc = unsmob_grob (get_property ("currentMusicalColumn"));
+         Item *ci = dynamic_cast<Item *> (cc);
+         finished_line_spanner_->set_bound (RIGHT, ci);
+         finished_line_spanner_->set_bound (LEFT, ci);
+       }
+
+      finished_line_spanner_ = 0;
     }
 }
 
+
+void
+Dynamic_engraver::acknowledge_stem_tremolo (Grob_info info)
+{
+  if (line_spanner_)
+    Side_position_interface::add_support (line_spanner_, info.grob ());
+}
+
+
 void
-Dynamic_engraver::acknowledge_element (Score_element_info i)
+Dynamic_engraver::acknowledge_slur (Grob_info info)
 {
   if (line_spanner_)
+    Side_position_interface::add_support (line_spanner_, info.grob ());
+}
+
+
+void
+Dynamic_engraver::acknowledge_note_column (Grob_info info)
+{
+  if (!line_spanner_)
+    return;
+
+  if (line_spanner_
+      /* Don't refill killed spanner */
+      && line_spanner_->is_live ())
     {
-      if (Note_column* n = dynamic_cast<Note_column*> (i.elem_l_))
+      Side_position_interface::add_support (line_spanner_, info.grob ());
+      add_bound_item (line_spanner_, dynamic_cast<Item *> (info.grob ()));
+    }
+
+  if (script_ && !script_->get_parent (X_AXIS))
+    {
+      extract_grob_set (info.grob (), "note-heads", heads);
+      if (heads.size ())
        {
-         side_position (line_spanner_).add_support (n);
-#ifdef DYN_LINE
-         line_spanner_->add_column (n);
-#else
-         if (!line_spanner_->spanned_drul_[LEFT])
-           line_spanner_->set_bounds (LEFT, n);
-         line_spanner_->set_bounds (RIGHT, n);
-         
-         line_spanner_->add_dependency (n);
-#endif
+         Grob *head = heads[0];
+         script_->set_parent (head, X_AXIS);
+         Self_alignment_interface::set_center_parent (script_, X_AXIS);
        }
     }
+
+  if (cresc_)
+    {
+      if (!cresc_->get_bound (LEFT))
+       {
+         cresc_->set_bound (LEFT, info.grob ());
+         add_bound_item (line_spanner_, cresc_->get_bound (LEFT));
+       }
+    }
+
+  if (finished_cresc_ && !finished_cresc_->get_bound (RIGHT))
+    finished_cresc_->set_bound (RIGHT, info.grob ());
 }
+
+void
+Dynamic_engraver::acknowledge_script (Grob_info info)
+{
+  if (!line_spanner_ || !script_)
+    return;
+
+  SCM p = info.grob ()->get_property ("script-priority");
+
+  /*
+    UGH.
+
+    DynamicText doesn't really have a script-priority field.
+  */
+  if (scm_is_number (p)
+      && scm_to_int (p)
+      < scm_to_int (script_->get_property ("script-priority")))
+    Side_position_interface::add_support (line_spanner_, info.grob ());
+}
+
+ADD_ACKNOWLEDGER (Dynamic_engraver, script);
+ADD_ACKNOWLEDGER (Dynamic_engraver, note_column);
+ADD_ACKNOWLEDGER (Dynamic_engraver, slur);
+ADD_ACKNOWLEDGER (Dynamic_engraver, stem_tremolo);
+
+ADD_TRANSLATOR (Dynamic_engraver,
+               /* doc */
+               "This engraver creates hairpins, dynamic texts, and their vertical\n"
+               "alignments.  The symbols are collected onto a DynamicLineSpanner grob\n"
+               "which takes care of vertical positioning.  ",
+
+               /* create */ "DynamicLineSpanner DynamicText Hairpin TextSpanner",
+               /* accept */ "absolute-dynamic-event crescendo-event decrescendo-event",
+               /* read */ "",
+               /* write */ "");