]> git.donarmstrong.com Git - lilypond.git/commitdiff
bad
authorMike Solomon <mike@apollinemike.com>
Sun, 27 Feb 2011 13:34:38 +0000 (08:34 -0500)
committerMike Solomon <mike@apollinemike.com>
Sun, 27 Feb 2011 13:34:38 +0000 (08:34 -0500)
26 files changed:
lily/balloon.cc
lily/constrained-breaking.cc
lily/dynamic-align-engraver.cc
lily/include/constrained-breaking.hh
lily/include/page-breaking.hh
lily/include/page-layout-problem.hh
lily/include/page-spacing.hh
lily/include/paper-system.hh
lily/include/system.hh
lily/page-breaking.cc
lily/page-layout-problem.cc
lily/page-spacing.cc
lily/paper-book.cc
lily/paper-system.cc
lily/stencil-interpret.cc
lily/system.cc
ly/engraver-init.ly
ly/music-functions-init.ly
ly/paper-defaults-init.ly
scm/define-event-classes.scm
scm/define-grob-interfaces.scm
scm/define-grob-properties.scm
scm/define-grobs.scm
scm/define-markup-commands.scm
scm/define-music-types.scm
scm/define-stencil-commands.scm

index f4efd4c4894a7b827e62b4a203a312daa8da0c28..672fb95a4d2e9212d8548617d56124a0495fc637 100644 (file)
 
 #include "text-interface.hh"
 #include "grob.hh"
+#include "item.hh"
 #include "line-interface.hh"
 #include "lookup.hh"
 #include "font-interface.hh"
 #include "lily-guile.hh"
 #include "output-def.hh"
 #include "misc.hh"
+#include "spanner.hh"
+#include "international.hh"
 
 class Balloon_interface
 {
 public:
   DECLARE_SCHEME_CALLBACK (print, (SCM));
+  DECLARE_SCHEME_CALLBACK (print_spanner, (SCM));
   DECLARE_GROB_INTERFACE ();
 };
 
@@ -51,7 +55,9 @@ Balloon_interface::print (SCM smob)
   b.widen (padding, padding);
 
   // FIXME
-  Stencil fr = Lookup::frame (b, 0.1, 0.05);
+  Stencil fr;
+  if (to_boolean (me->get_property ("annotation-balloon")))
+    fr = Lookup::frame (b, 0.1, 0.05);
 
   SCM bt = me->get_property ("text");
   SCM chain = Font_interface::text_font_alist_chain (me);
@@ -71,7 +77,77 @@ Balloon_interface::print (SCM smob)
 
   Offset z2 = z1 + off;
 
-  fr.add_stencil (Line_interface::line (me, z1, z2));
+  if (to_boolean (me->get_property ("annotation-line")))
+    fr.add_stencil (Line_interface::line (me, z1, z2));
+
+  text_stil->translate (z2);
+  fr.add_stencil (*text_stil);
+
+  fr.translate (-off);
+  return fr.smobbed_copy ();
+}
+
+// ugh...code dup...hopefully can be consolidated w/ above one day
+MAKE_SCHEME_CALLBACK (Balloon_interface, print_spanner, 1);
+SCM
+Balloon_interface::print_spanner (SCM smob)
+{
+  Spanner *me = unsmob_spanner (smob);
+  Spanner *parent = unsmob_spanner (me->get_property ("parent-spanner"));
+  Spanner *p;
+  message (_f ("foo %d", robust_scm2int (me->get_property ("spanner-to-annotate"), 0)));
+  message (_f ("bar %d", robust_scm2int (me->broken_intos_[0]->get_property ("spanner-to-annotate"), 0)));
+  if (parent->broken_intos_.size () == 0)
+    p = parent;
+  else
+    p = parent->broken_intos_[robust_scm2int (me->get_property ("spanner-to-annotate"), 0) % parent->broken_intos_.size ()];
+
+  
+  Drul_array<Item *> bounds;
+  Direction d = LEFT;
+  
+  do
+    {
+      bounds[d] = me->get_bound (d);
+    }
+  while (flip (&d) != LEFT);
+  
+  Grob *commonx = bounds[LEFT]->common_refpoint (bounds[RIGHT], X_AXIS);
+
+  Offset off (me->relative_coordinate (commonx, X_AXIS),
+             me->relative_coordinate (p, Y_AXIS));
+  
+  Box b (p->extent (p, X_AXIS),
+        p->extent (p, Y_AXIS));
+
+  Real padding = robust_scm2double (me->get_property ("padding"), .1);
+  b.widen (padding, padding);
+
+  // FIXME
+  Stencil fr;
+  if (to_boolean (me->get_property ("annotation-balloon")))
+    fr = Lookup::frame (b, 0.1, 0.05);
+
+  SCM bt = me->get_property ("text");
+  SCM chain = Font_interface::text_font_alist_chain (me);
+
+  SCM stencil = Text_interface::interpret_markup (me->layout ()->self_scm (),
+                                                 chain, bt);
+
+  Stencil *text_stil = unsmob_stencil (stencil);
+
+  Offset z1;
+  for (int i = X_AXIS; i < NO_AXES; i++)
+    {
+      Axis a ((Axis)i);
+      z1[a] = b[a].linear_combination (sign (off[a]));
+      text_stil->align_to (a, -sign (off[a]));
+    }
+
+  Offset z2 = z1 + off;
+
+  if (to_boolean (me->get_property ("annotation-line")))
+    fr.add_stencil (Line_interface::line (me, z1, z2));
 
   text_stil->translate (z2);
   fr.add_stencil (*text_stil);
@@ -85,7 +161,10 @@ ADD_INTERFACE (Balloon_interface,
               " object.",
 
               /* properties */
+              "annotation-balloon "
+              "annotation-line "
               "padding "
+              "spanner-to-annotate "
               "text "
               );
 
index fdcd64586600f5c3c50cfc66c2f169f390393e54..f6f84b80907714907d218525deb8c868a95f397e 100644 (file)
@@ -519,6 +519,8 @@ Constrained_breaking::fill_line_details (Line_details *const out, vsize start, v
   out->title_space_ = system_markup_space_;
   out->inverse_hooke_ = out->full_height () + system_system_space_;
 
+  out->footnotes_ = sys->get_footnotes_in_range (start_rank, end_rank);
+
   out->refpoint_extent_ = sys->pure_refpoint_extent (start_rank, end_rank);
   if (out->refpoint_extent_.is_empty ())
     out->refpoint_extent_ = Interval (0, 0);
@@ -550,6 +552,11 @@ Line_details::Line_details (Prob *pb, Output_def *paper)
   Page_layout_problem::read_spacing_spec (spec, &min_distance_, ly_symbol2scm ("minimum-distance"));
   Page_layout_problem::read_spacing_spec (title_spec, &title_min_distance_, ly_symbol2scm ("minimum-distance"));
 
+  SCM footnotes = pb->get_property ("footnotes");
+  if (scm_is_pair (footnotes))
+    for (SCM s = footnotes; scm_is_pair (s); s = scm_cdr (s))
+      footnotes_.push_back (unsmob_stencil (scm_car (s)));
+
   last_column_ = 0;
   force_ = 0;
   Interval stencil_extent = unsmob_stencil (pb->get_property ("stencil"))->extent (Y_AXIS);
index 43498089d003fd96d1d066bcdc5af61825f219a9..e34554e65408c70905d76ab5bf036145fd6133e3 100644 (file)
@@ -37,6 +37,7 @@ class Dynamic_align_engraver : public Engraver
   DECLARE_TRANSLATOR_LISTENER (break_span);
   DECLARE_ACKNOWLEDGER (note_column);
   DECLARE_ACKNOWLEDGER (dynamic);
+  DECLARE_ACKNOWLEDGER (footnote_spanner);
   DECLARE_END_ACKNOWLEDGER (dynamic);
 
 protected:
@@ -63,6 +64,7 @@ Dynamic_align_engraver::Dynamic_align_engraver ()
 
 ADD_ACKNOWLEDGER (Dynamic_align_engraver, dynamic);
 ADD_ACKNOWLEDGER (Dynamic_align_engraver, note_column);
+ADD_ACKNOWLEDGER (Dynamic_align_engraver, footnote_spanner);
 ADD_END_ACKNOWLEDGER (Dynamic_align_engraver, dynamic);
 
 void
@@ -80,6 +82,17 @@ Dynamic_align_engraver::acknowledge_end_dynamic (Grob_info info)
     ended_.push_back (info.spanner ());
 }
 
+void
+Dynamic_align_engraver::acknowledge_footnote_spanner (Grob_info info)
+{
+  Grob *parent = unsmob_grob (info.grob ()->get_property ("parent-spanner"));
+  if (line_ && parent
+      && parent->internal_has_interface (ly_symbol2scm ("dynamic-interface"))
+      && (scm_to_int (line_->get_property ("direction"))
+          * robust_scm2double (info.grob ()->get_property ("Y-offset"), 0.0) > 0.0)) {
+    Axis_group_interface::add_element (line_, info.grob ());message ("ADD"); }
+}
+
 void
 Dynamic_align_engraver::acknowledge_note_column (Grob_info info)
 {
index fc1b44e41f586f2f830edf47cf0e107007736ee8..52d99b6ba27629f5090b8013eba21cc494df243d 100644 (file)
@@ -44,6 +44,7 @@ struct Line_details {
   Grob *last_column_;
   Real force_;
   Line_shape shape_;
+  vector<Stencil *> footnotes_;
   Interval refpoint_extent_; /* The refpoints of the first and last
                                spaceable staff in this line.  min-distance
                                should be measured from the bottom
index b34b6c4c84a14e481a5a225cdd519634f632af62..fa68c6175207068f4543498ec5262ca1fc352089 100644 (file)
@@ -124,6 +124,8 @@ public:
   Real page_height (int page_number, bool last) const;
   Real paper_height () const;
   vsize system_count () const;
+  Real footnote_separator_stencil_height () const;
+  Real footnote_padding () const;
   Real line_count_penalty (int line_count) const;
   int line_count_status (int line_count) const;
   bool too_many_lines (int line_count) const;
@@ -145,6 +147,7 @@ protected:
 
   void break_into_pieces (vsize start, vsize end, Line_division const &div);
   SCM systems ();
+  SCM footnotes ();
 
   void set_current_breakpoints (vsize start,
                                vsize end,
@@ -184,6 +187,8 @@ private:
   int max_systems_per_page_;
   int min_systems_per_page_;
   vsize system_count_;
+  Real footnote_separator_stencil_height_;
+  Real footnote_padding_;
   int orphan_penalty_;
 
   vector<Line_division> current_configurations_;
index 941ccef2a793dd59fa4d05f8ff1f4d8cbb6a5366..fb1c7c373d4a1ad72252f8ebd2c4b8294aeeb7d0 100644 (file)
@@ -34,8 +34,11 @@ public:
   static bool read_spacing_spec (SCM spec, Real* dest, SCM sym);
   static bool is_spaceable (Grob *g);
   static SCM get_details (Grob *g);
+  static SCM get_footnotes_from_lines (SCM lines, Real padding);
+  static Stencil* get_footnote_separator_stencil (Output_def *paper);
   static SCM get_spacing_spec (Grob *before, Grob *after, bool pure, int start, int end);
   static Real get_fixed_spacing (Grob *before, Grob *after, int spaceable_index, bool pure, int start, int end);
+  static void add_footnotes_to_footer (SCM footnotes, Stencil *foot, Paper_book *pb);
 
 protected:
   void append_system (System*, Spring const&, Real indent, Real padding);
index be261735aa2f61ae608220bb280178ca9c76dfb1..206bcbf426bb4193ab7c0bdf03c3665c01711229 100644 (file)
@@ -102,6 +102,7 @@ struct Page_spacing
   Real rod_height_;
   Real spring_len_;
   Real inverse_spring_k_;
+  bool has_footnotes_;
 
   Line_details last_line_;
   Line_details first_line_;
@@ -111,11 +112,13 @@ struct Page_spacing
   {
     page_height_ = page_height;
     breaker_ = breaker;
+    has_footnotes_ = false;
     clear ();
   }
 
   void calc_force ();
   void resize (Real new_height);
+  Real account_for_footnotes (Line_details const &line);
   void append_system (const Line_details &line);
   void prepend_system (const Line_details &line);
   void clear ();
index 96053d8d03de635d3c6f07d9a36ca624f73344e8..1aaa530b8a916b37288f3ed7efe43ac3bd0c8636 100644 (file)
@@ -30,5 +30,6 @@
 */
 Prob *make_paper_system (SCM immutable_init);
 void paper_system_set_stencil (Prob *prob, Stencil s);
+SCM get_footnotes (SCM expr);
 
 #endif /* PAPER_SYSTEM_HH */
index 882f209f9680b052afa354d72a99d5be11c312ef..f4053da5e658dfeb57672b7e1acaa512e4f160d3 100644 (file)
@@ -36,19 +36,25 @@ class System : public Spanner
   void init_elements ();
   friend class Paper_score;    // ugh.
   Paper_score *pscore_;        // ugh.
-  
+  vector<Grob *> footnote_grobs_;
+
 public:
   Paper_score *paper_score () const;
+  vector<Stencil *> footnotes_;
   Grob *get_vertical_alignment ();
   Grob *get_extremal_staff (Direction dir, Interval const&);
   Grob *get_pure_bound (Direction dir, int start, int end);
   Grob *get_maybe_pure_bound (Direction dir, bool pure, int start, int end);
   int get_rank () const;
+  vector<Stencil *> get_footnotes_in_range (vsize st, vsize end);
+  Stencil make_footnote_stencil (Real padding);
   void do_break_substitution_and_fixup_refpoints ();
   void post_processing ();
+  void populate_footnote_grob_vector ();
   SCM get_paper_system ();
   SCM get_paper_systems ();
   SCM get_broken_system_grobs ();
+  SCM get_broken_footnote_stencils ();
 
   DECLARE_SCHEME_CALLBACK (calc_pure_relevant_grobs, (SCM));
   DECLARE_SCHEME_CALLBACK (height, (SCM));
index 4e386b81832186cf48f7014f0b879b025cb616db..25044078df0b61ae807879f6ff0b7dd3e8b7f271 100644 (file)
 
 #include "international.hh"
 #include "item.hh"
+#include "line-interface.hh"
 #include "output-def.hh"
 #include "page-layout-problem.hh"
 #include "page-spacing.hh"
 #include "paper-book.hh"
 #include "paper-score.hh"
 #include "paper-system.hh"
+#include "text-interface.hh"
 #include "system.hh"
 #include "warn.hh"
 
@@ -178,7 +180,12 @@ compress_lines (const vector<Line_details> &orig)
 
          // compressed.title_ is true if and only if the first of its
          // compressed lines was a title.
-         compressed.title_ = old.title_;
+          compressed.title_ = old.title_;
+         
+         // take care of footnotes
+          compressed.footnotes_.insert (compressed.footnotes_.begin (),
+            old.footnotes_.begin (), old.footnotes_.end ());
+          
          ret.back () = compressed;
        }
       else
@@ -243,6 +250,20 @@ Page_breaking::Page_breaking (Paper_book *pb, Break_predicate is_break, Prob_bre
   min_systems_per_page_ = max (0, robust_scm2int (pb->paper_->c_variable ("min-systems-per-page"), 0));
   orphan_penalty_ = robust_scm2int (pb->paper_->c_variable ("orphan-penalty"), 100000);
 
+  Stencil *footnote_separator = Page_layout_problem::get_footnote_separator_stencil (pb->paper_);
+
+  if (footnote_separator)
+    {
+      Interval separator_extent = footnote_separator->extent (Y_AXIS);
+      Real separator_span = max (separator_extent[UP] - separator_extent[DOWN], 0.0);
+
+      footnote_separator_stencil_height_ = separator_span;
+    }
+  else
+    footnote_separator_stencil_height_ = 0.0;
+
+  footnote_padding_ = robust_scm2double (pb->paper_->c_variable ("footnote-padding"), 0.0);
+
   if (systems_per_page_ && (max_systems_per_page_ || min_systems_per_page_))
     {
       warning (_f ("ignoring min-systems-per-page and max-systems-per-page because systems-per-page was set"));
@@ -302,6 +323,18 @@ Page_breaking::system_count () const
   return system_count_;
 }
 
+Real
+Page_breaking::footnote_separator_stencil_height () const
+{
+  return footnote_separator_stencil_height_;
+}
+
+Real
+Page_breaking::footnote_padding () const
+{
+  return footnote_padding_;
+}
+
 bool
 Page_breaking::too_many_lines (int line_count) const
 {
@@ -514,6 +547,12 @@ Page_breaking::draw_page (SCM systems, SCM configuration, int page_num, bool las
   Prob *p = unsmob_prob (page);
   p->set_property ("lines", paper_systems);
   p->set_property ("configuration", configuration);
+
+  Stencil *foot = unsmob_stencil (p->get_property ("foot-stencil"));
+  SCM footnotes = Page_layout_problem::get_footnotes_from_lines (systems, footnote_padding ());
+  Page_layout_problem::add_footnotes_to_footer (footnotes, foot, unsmob_paper_book (p->get_property ("paper-book")));
+
+  p->set_property ("foot-stencil", foot->smobbed_copy ());
   scm_apply_1 (page_stencil, page, SCM_EOL);
 
   return page;
@@ -559,6 +598,7 @@ Page_breaking::make_pages (vector<vsize> lines_per_page, SCM systems)
     {
       SCM lines = scm_caar (s);
       SCM config = scm_cdar (s);
+      
       bool bookpart_last_page = (s == systems_and_configs);
       SCM page = draw_page (lines, config, page_num, bookpart_last_page);
 
index c0af4dae1e0dd54b6cebdfb4e03edd3c285eef87..fa0689bf8f285738af61a02f3654a12ad12fed66 100644 (file)
 #include "prob.hh"
 #include "skyline-pair.hh"
 #include "system.hh"
+#include "text-interface.hh"
+
+SCM
+Page_layout_problem::get_footnotes_from_lines (SCM lines, Real padding)
+{
+  SCM footnotes = SCM_EOL;
+  // ugh...code dup
+  for (SCM s = lines; scm_is_pair (s); s = scm_cdr (s))
+    {
+      if (Grob *g = unsmob_grob (scm_car (s)))
+       {
+         System *sys = dynamic_cast<System *> (g);
+         if (!sys)
+            {
+              programming_error ("got a grob for footnotes that wasn't a System");
+              continue;
+            }
+          footnotes = scm_cons (sys->make_footnote_stencil (padding).smobbed_copy (), footnotes);
+        }
+      else if (Prob *p = unsmob_prob (scm_car (s)))
+        {
+          SCM stencils = p->get_property ("footnotes");
+          if (stencils == SCM_EOL)
+            continue;
+          Stencil footnote_stencil;
+
+          for (SCM st = stencils; scm_is_pair (st); st = scm_cdr (st))
+            footnote_stencil.add_at_edge (Y_AXIS, DOWN, *unsmob_stencil (scm_car (st)), padding);
+          footnotes = scm_cons (footnote_stencil.smobbed_copy (), footnotes);
+        }
+    }
+
+  if (!scm_is_pair (footnotes))
+    return SCM_EOL;
+    
+  return scm_reverse (footnotes);
+}
+
+Stencil*
+Page_layout_problem::get_footnote_separator_stencil (Output_def *paper)
+{
+  SCM props = scm_call_1 (ly_lily_module_constant ("layout-extract-page-properties"),
+                paper->self_scm ());
+
+  SCM markup = paper->c_variable ("footnote-separator-markup");
+
+  if (!Text_interface::is_markup (markup))
+    return NULL;
+
+  SCM footnote_stencil = Text_interface::interpret_markup (paper->self_scm (),
+                                                           props, markup);
+
+  Stencil *footnote_separator = unsmob_stencil (footnote_stencil);
+
+  return footnote_separator;
+}
+
+void
+Page_layout_problem::add_footnotes_to_footer (SCM footnotes, Stencil *foot, Paper_book *pb)
+{
+  bool are_footnotes = false;
+  Real footnote_padding = robust_scm2double (pb->paper_->c_variable ("footnote-padding"), 0.0);
+  
+  footnotes = scm_reverse (footnotes);
+  for (SCM s = footnotes; scm_is_pair (s); s = scm_cdr (s))
+    {
+      if (scm_car (s) == SCM_EOL)
+        continue;
+      Stencil *stencil = unsmob_stencil (scm_car (s));
+      if (stencil->extent (Y_AXIS).length() > 0.0)
+        {
+          foot->add_at_edge (Y_AXIS, UP, *stencil, footnote_padding);
+          are_footnotes = true;
+        }
+    }
+  
+  if (are_footnotes)
+    {
+      Stencil *separator = get_footnote_separator_stencil (pb->paper_);
+      if (separator)
+        foot->add_at_edge (Y_AXIS, UP, *separator, footnote_padding);
+    }
+}
 
 Page_layout_problem::Page_layout_problem (Paper_book *pb, SCM page_scm, SCM systems)
   : bottom_skyline_ (DOWN)
@@ -47,7 +130,13 @@ Page_layout_problem::Page_layout_problem (Paper_book *pb, SCM page_scm, SCM syst
     {
       Stencil *head = unsmob_stencil (page->get_property ("head-stencil"));
       Stencil *foot = unsmob_stencil (page->get_property ("foot-stencil"));
-
+      
+      Real footnote_padding = 0.0;
+      if (pb && pb->paper_)
+        footnote_padding = robust_scm2double (pb->paper_->c_variable ("footnote-padding"), 0.0);
+      SCM footnotes = get_footnotes_from_lines (systems, footnote_padding);
+      add_footnotes_to_footer (footnotes, foot, pb);
+      
       header_height_ = head ? head->extent (Y_AXIS).length () : 0;
       footer_height_ = foot ? foot->extent (Y_AXIS).length () : 0;
       page_height_ = robust_scm2double (page->get_property ("paper-height"), 100);
index 17ba73d9239846e49c4e1d8e7fe23345e30e6efe..13c6a0c02a98f45cd0d9208e4a13f2e61fe315c5 100644 (file)
@@ -19,6 +19,7 @@
 
 #include "page-spacing.hh"
 
+#include "international.hh"
 #include "matrix.hh"
 #include "page-breaking.hh"
 #include "warn.hh"
@@ -59,6 +60,7 @@ Page_spacing::append_system (const Line_details &line)
       first_line_ = line;
     }
 
+  rod_height_ += account_for_footnotes (line);
   inverse_spring_k_ += line.inverse_hooke_;
 
   last_line_ = line;
@@ -66,6 +68,25 @@ Page_spacing::append_system (const Line_details &line)
   calc_force ();
 }
 
+Real
+Page_spacing::account_for_footnotes (Line_details const &line)
+{
+  Real footnote_height = 0.0;
+  for (vsize i = 0; i < line.footnotes_.size (); i++)
+    {
+      footnote_height += (has_footnotes_
+                          ? 0.0
+                          : (breaker_->footnote_separator_stencil_height ()
+                             + breaker_->footnote_padding ()));
+
+      has_footnotes_ = true;
+      Interval extent = line.footnotes_[i]->extent (Y_AXIS);
+      footnote_height += extent[UP] - extent[DOWN];
+      footnote_height += breaker_->footnote_padding ();
+    }  
+  return footnote_height;
+}
+
 void
 Page_spacing::prepend_system (const Line_details &line)
 {
@@ -77,7 +98,7 @@ Page_spacing::prepend_system (const Line_details &line)
   rod_height_ -= first_line_.full_height ();
   rod_height_ += first_line_.tallness_;
   rod_height_ += line.full_height();
-
+  rod_height_ += account_for_footnotes (line);
   inverse_spring_k_ += line.inverse_hooke_;
 
   first_line_ = line;
@@ -90,6 +111,7 @@ Page_spacing::clear ()
 {
   force_ = rod_height_ = spring_len_ = 0;
   inverse_spring_k_ = 0;
+  has_footnotes_ = false;
 }
 
 
index 4678ee3895b88d6f8b11bd1e200d213a5dafac8e..0de38c54fa700e988074157eb455a637c2a5962f 100644 (file)
@@ -532,6 +532,9 @@ Paper_book::get_system_specs ()
                          list == texts? SCM_BOOL_T : SCM_BOOL_F);
 
              paper_system_set_stencil (ps, *unsmob_stencil (t));
+             
+             SCM footnotes = get_footnotes (unsmob_stencil (t)->expr ());
+             ps->set_property ("footnotes", scm_reverse (footnotes));
              ps->set_property ("is-title", SCM_BOOL_T);
              if (list != texts)
                /* For each markup other than the first, place it as closely as
index aef20f10c04ea9a8d81e80d1f1ae263d9e88e8d1..352334a0a7cf7efd85ab26975b4b7a33d2e59251 100644 (file)
@@ -27,6 +27,46 @@ make_paper_system (SCM immutable_init)
   return prob;
 }
 
+SCM
+get_footnotes (SCM expr)
+{
+  if (!scm_is_pair (expr))
+    return SCM_EOL;
+
+  SCM head = scm_car (expr);
+
+  if (head == ly_symbol2scm ("delay-stencil-evaluation"))
+    {
+      // we likely need to do something here...just don't know what...
+      return SCM_EOL;
+    }
+  
+  if (head == ly_symbol2scm ("combine-stencil"))
+    {
+      SCM out = SCM_EOL;
+      for (SCM x = scm_cdr (expr); scm_is_pair (x); x = scm_cdr (x))
+        {
+          SCM footnote = get_footnotes (scm_car (x));
+          if (scm_is_pair (footnote))
+            {
+              for (SCM y = scm_reverse (footnote); scm_is_pair (y); y = scm_cdr (y))
+                out = scm_cons (scm_car (y), out);
+            }
+          else if (SCM_EOL != footnote)
+            out = scm_cons (footnote, out);
+        }
+      return out;
+    }
+  if (head == ly_symbol2scm ("translate-stencil"))
+    return get_footnotes (scm_caddr (expr));
+
+  if (head == ly_symbol2scm ("footnote"))
+    return scm_cadr (expr);
+
+  return SCM_EOL;
+}
+
+
 void
 paper_system_set_stencil (Prob *prob, Stencil s)
 {
index 19c241ea849fa8f09f591e9e30ec9219746e2595..4f8c6bbc8be85712cf2bdea34b6fc25f78bd51a8 100644 (file)
@@ -37,6 +37,8 @@ interpret_stencil_expression (SCM expr,
          interpret_stencil_expression (scm_force (scm_cadr (expr)), func, func_arg, o);
          return;
        }
+      if (head == ly_symbol2scm ("footnote"))
+        return;
       if (head == ly_symbol2scm ("translate-stencil"))
        {
          o += ly_scm2offset (scm_cadr (expr));
index 19279d7cd061e7b0c309f96c2a6f74b67f236e95..82c368e78467f74786f89cf66cdffe3065ca1110 100644 (file)
@@ -35,6 +35,7 @@
 #include "pointer-group-interface.hh"
 #include "skyline-pair.hh"
 #include "staff-symbol-referencer.hh"
+#include "text-interface.hh"
 #include "warn.hh"
 
 System::System (System const &src)
@@ -162,7 +163,7 @@ System::do_break_substitution_and_fixup_refpoints ()
          Grob *g = all_elts[j];
          g->fixup_refpoint ();
        }
-
+        
       count += all_elts.size ();
     }
 
@@ -226,6 +227,61 @@ System::get_paper_systems ()
   return lines;
 }
 
+void
+System::populate_footnote_grob_vector ()
+{
+  extract_grob_set (this, "all-elements", all_elts);
+  for (vsize i = 0; i < all_elts.size (); i++)
+    {
+      if ((all_elts[i]->name () == "Footnote") || (all_elts[i]->name () == "FootnoteSpanner"))
+        footnote_grobs_.push_back (all_elts[i]);
+    }
+  sort (footnote_grobs_.begin (), footnote_grobs_.end (), Grob::less);
+
+}
+
+vector<Stencil *>
+System::get_footnotes_in_range (vsize st, vsize end)
+{
+  vector<Stencil *> out;
+  if (footnote_grobs_.size () == 0)
+    populate_footnote_grob_vector ();
+
+  for (vsize j = 0; j < footnote_grobs_.size (); j++)
+    {
+      if (footnote_grobs_[j]->spanned_rank_interval ()[LEFT] < (int)st)
+        continue;
+      if (footnote_grobs_[j]->spanned_rank_interval ()[LEFT] >= (int)end)
+        break;
+      SCM footnote_markup = footnote_grobs_[j]->get_property ("footnote-text");
+
+      if (!Text_interface::is_markup (footnote_markup))
+        continue;
+
+      SCM props = scm_call_1 (ly_lily_module_constant ("layout-extract-page-properties"),
+                              pscore_->layout ()->self_scm ());
+
+      SCM footnote_stl = Text_interface::interpret_markup (pscore_->layout ()->self_scm (),
+                                                           props, footnote_markup);
+
+      Stencil *footnote_stencil = unsmob_stencil (footnote_stl);
+      out.push_back (footnote_stencil);
+    }
+
+  return out;
+}
+
+Stencil
+System::make_footnote_stencil (Real padding)
+{
+  Stencil mol;
+
+  for (vsize i = 0; i < footnotes_.size (); i++)
+    mol.add_at_edge (Y_AXIS, DOWN, *footnotes_[i], padding);
+
+  return mol;
+}
+
 void
 System::break_into_pieces (vector<Column_x_positions> const &breaking)
 {
@@ -242,6 +298,8 @@ System::break_into_pieces (vector<Column_x_positions> const &breaking)
       Interval iv (pure_height (this, st, end));
       system->set_property ("pure-Y-extent", ly_interval2scm (iv));
 
+      system->footnotes_ = get_footnotes_in_range (st, end);
+
       system->set_bound (LEFT, c[0]);
       system->set_bound (RIGHT, c.back ());
       SCM system_labels = SCM_EOL;
index 28c7c9706f09b042de8b02f442eb9ff946bbe52c..e8d39ab560308d0a910b2a69e17cc496c1c40715 100644 (file)
@@ -534,6 +534,7 @@ automatically when an output definition (a @code{\score} or
   \consists "Stanza_number_align_engraver"
   \consists "Bar_number_engraver"
   \consists "Parenthesis_engraver"
+  \consists "Footnote_engraver"
 
   \defaultchild "Staff"
 
index ac62bffa9ae67575da8bbce6e60ec12fd8d18595..2ebbd8f1bce3bd83b240ffa066b754d6cac37b82 100644 (file)
@@ -342,7 +342,28 @@ featherDurations=
 
      argument))
 
-
+footnoteGrob =
+#(define-music-function (parser location grob-name offset text footnote)
+   (symbol? number-pair? markup? markup?)
+   (_i "Attach @var{text} to @var{grob-name} at offset @var{offset},
+ with @var{text} referring to @var{footnote} (use like @code{\\once})")
+   (make-music 'FootnoteEvent
+              'symbol grob-name
+              'X-offset (car offset)
+              'Y-offset (cdr offset)
+              'text text
+              'footnote-text footnote))
+
+footnote =
+#(define-music-function (parser location offset text footnote)
+   (number-pair? markup? markup?)
+   (_i "Attach @var{text} at @var{offset} with @var{text} referring
+ to @var{footnote} (use like @code{\\tweak})")
+   (make-music 'FootnoteEvent
+              'X-offset (car offset)
+              'Y-offset (cdr offset)
+              'text text
+              'footnote-text footnote))
 
 grace =
 #(def-grace-function startGraceMusic stopGraceMusic
index 69fecc16b61756a24343d6eee6a0b2801c35b6fd..9f88def048a7cee394aa93cb396d00fe12b2ee37 100644 (file)
@@ -56,7 +56,6 @@
   ragged-bottom = ##f
   ragged-last-bottom = ##t  % best for shorter scores
 
-
   %%
   %% Flexible vertical spacing
   %%
   page-breaking = #ly:optimal-breaking
 
 
+  %%
+  %% Footnotes
+  %%
+  footnote-separator-markup = \markup { \draw-hline }
+  footnote-padding = 0.5\mm
+
+
   %%
   %% Page numbering
   %%
   print-first-page-number = ##f
   print-page-number = ##t
 
-
   %%
   %% Headers, footers, and titles
   %%
index 022ae640e1f208b82292ae682b88c72e274f502b..c632e430a95f37a1dd184e8a8cdef5e4906ce132 100644 (file)
@@ -25,7 +25,7 @@
                 (RemoveContext ChangeParent Override Revert UnsetProperty
                                SetProperty music-event OldMusicEvent CreateContext Prepare
                                OneTimeStep Finish))
-    (music-event . (annotate-output-event
+    (music-event . (annotate-output-event footnote-event
                    arpeggio-event breathing-event extender-event span-event
       rhythmic-event dynamic-event break-event label-event percent-event
       key-change-event string-number-event stroke-finger-event tie-event
index 23e5f4a0a9575a07c3fd34fd09050dc1fec74e2e..237f8d1805c205b6007c35d33e80e12e0124982d 100644 (file)
@@ -81,6 +81,16 @@ note)."
  "A fingering instruction."
  '())
 
+(ly:add-interface
+ 'footnote-interface
+ "Make a footnote."
+ '(footnote-text))
+
+(ly:add-interface
+ 'footnote-spanner-interface
+ "Make a footnote spanner."
+ '(footnote-text))
+
 (ly:add-interface
  'fret-diagram-interface
  "A fret diagram"
index 168a4f43d8a3432621128d76f0e48b0c90d3bd05..434d02c4192ea9398913c3cad59466e0202687b8 100644 (file)
@@ -48,6 +48,8 @@ be created below this bar line.")
      (alteration-alist ,list? "List of @code{(@var{pitch}
 . @var{accidental})} pairs for key signature.")
      (annotation ,string? "Annotate a grob for debug purposes.")
+     (annotation-balloon ,boolean? "Draw a balloon around an annotation.")
+     (annotation-line ,boolean? "Draw a line from an annotation.")
      (arpeggio-direction ,ly:dir? "If set, put an arrow on the
 arpeggio squiggly line.")
      (arrow-length ,number? "Arrow length.")
@@ -293,6 +295,7 @@ include @code{upright}, @code{italic}, @code{caps}.")
 @code{-1} is smaller, @code{+1} is bigger.  Each step of@tie{}1 is
 approximately 12% larger; 6@tie{}steps are exactly a factor@tie{}2
 larger.  Fractional values are allowed.")
+     (footnote-text ,markup? "A footnote for the grob.")
      (force-hshift ,number? "This specifies a manual shift for notes
 in collisions.  The unit is the note head width of the first voice
 note.  This is used by @rinternals{note-collision-interface}.")
@@ -731,6 +734,10 @@ slashes in percent repeat glyphs.  Larger values bring the two
 elements closer together.")
      (slope ,number? "The slope of this object.")
      (slur-padding ,number? "Extra distance between slur and script.")
+     (spanner-to-annotate ,integer? "The number of a spanner in a
+spanner's broken spanner list to annotate.  Values less than 0 or
+greater than the number of broken spanners will be wrapped modulo the
+number of spanners.")
      (space-alist ,list? "A table that specifies distances between
 prefatory items, like clef and time-signature.  The format is an alist
 of spacing tuples: @code{(@var{break-align-symbol} @var{type}
index 286490e81beffd11d05169fb2858eeb4b6736e9d..7369e95e4286addf27becb5dbe8f9461ddc3dd1c 100644 (file)
 
     (BalloonTextItem
      . (
+        (annotation-balloon . #t)
+        (annotation-line . #t)
        (stencil . ,ly:balloon-interface::print)
        (text . ,(grob::calc-property-by-copy 'text))
        (X-offset . ,(grob::calc-property-by-copy 'X-offset))
                                text-interface
                                text-script-interface))))))
 
+    (Footnote
+     . (
+        (annotation-balloon . #f)
+        (annotation-line . #t)
+       (stencil . ,ly:balloon-interface::print)
+       (text . ,(grob::calc-property-by-copy 'text))
+       (X-offset . ,(grob::calc-property-by-copy 'X-offset))
+       (Y-offset . ,(grob::calc-property-by-copy 'Y-offset))
+       (meta . ((class . Item)
+                (interfaces . (balloon-interface
+                               footnote-interface
+                               font-interface
+                               text-interface))))))
+
+    (FootnoteSpanner
+     . (
+        (annotation-balloon . #f)
+        (annotation-line . #t)
+        (footnote-text . #f)
+       (stencil . ,ly:balloon-interface::print-spanner)
+       (text . ,(grob::calc-property-by-copy 'text))
+        (X-offset . ,(grob::calc-property-by-copy 'X-offset))
+       (Y-offset . ,(grob::calc-property-by-copy 'Y-offset))
+       (meta . ((class . Spanner)
+                (interfaces . (balloon-interface
+                               footnote-spanner-interface
+                               font-interface
+                               text-interface))))))
+
     (FretBoard
      . (
        (after-line-breaking . ,ly:chord-name::after-line-breaking)
index 5dbc5d2f5254b61bd4e3bd4c42b4143807501812..716c37f28483c496446b7b9cd353cc39591263df 100644 (file)
@@ -139,6 +139,25 @@ A simple line.
         (y (cdr dest)))
     (make-line-stencil th 0 0 x y)))
 
+(define-markup-command (draw-hline layout props)
+  ()
+  #:category graphic
+  #:properties ((thickness 1))
+  "
+@cindex drawing a line across a page
+
+Draws a line across a page.
+@lilypond[verbatim,quote]
+\\markup {
+  \\draw-hline
+}
+@end lilypond"
+  (let ((th (* (ly:output-def-lookup layout 'line-thickness)
+               thickness))
+        (x (ly:output-def-lookup layout 'line-width))
+        (y 0.0))
+    (make-line-stencil th 0 0 x y)))
+
 (define-markup-command (draw-circle layout props radius thickness filled)
   (number? number? boolean?)
   #:category graphic
@@ -1812,6 +1831,20 @@ returns an empty markup.
                          (list markup?))
     (interpret-markup layout props (list anonymous-with-signature arg))))
 
+(define-markup-command (footnote layout props arg1 arg2)
+  (markup? markup?)
+  #:category other
+  "Apply the footnote @var{arg2} to @var{arg1}."
+  (ly:stencil-combine-at-edge
+    (interpret-markup layout props arg1)
+    X
+    RIGHT
+    (ly:make-stencil
+      `(footnote ,(interpret-markup layout props arg2))
+      '(0 . 0)
+      '(0 . 0))
+    0.0))
+
 (define-markup-command (override layout props new-prop arg)
   (pair? markup?)
   #:category other
index cdf5c0a6a1e1e92b5a8ef2f301894e0a53a5f031..d57cba811816f44214c150b8e781ae403eac8f95 100644 (file)
@@ -209,6 +209,11 @@ An alternative syntax is @var{note}@code{\\decr} @dots{}
        (types . (general-music fingering-event event))
        ))
 
+    (FootnoteEvent
+     . ((description . "Footnote a grob.")
+       (types . (general-music event footnote-event))
+       ))
+
     (GlissandoEvent
      . ((description . "Start a glissando on this note.")
        (types . (general-music glissando-event event))
index eb7f85ddc1dc137a7538348888bcbe5f2af4627f..50787c2bef2771864daf6a7ae299602fb1f4b58c 100644 (file)
@@ -34,6 +34,7 @@ defined in the output modules (@file{output-*.scm})."
     ellipse
     embedded-ps
     embedded-svg
+    footnote
     glyph-string
     grob-cause
     named-glyph