]> git.donarmstrong.com Git - lilypond.git/commitdiff
Adds automatic numbering to footnotes.
authorMike Solomon <mike@apollinemike.com>
Thu, 28 Jul 2011 14:47:08 +0000 (16:47 +0200)
committerMike Solomon <mike@apollinemike.com>
Thu, 28 Jul 2011 14:55:58 +0000 (16:55 +0200)
There are currently two types of automatic numbering possible:
automatic numbering where the footnote resets on each page
and automatic numbering where the footnotes increase in
sequence over pages.  The bulk of the commit lies in
paper-layout-problem.cc.  The two new regtests show how this
is done.

24 files changed:
input/regression/footnote-auto-numbering-page-reset.ly [new file with mode: 0644]
input/regression/footnote-auto-numbering.ly [new file with mode: 0644]
input/regression/footnote-break-visibility.ly
input/regression/footnote-spanner.ly
input/regression/footnote.ly
lily/align-interface.cc
lily/balloon.cc
lily/constrained-breaking.cc
lily/include/page-breaking.hh
lily/include/page-layout-problem.hh
lily/include/system.hh
lily/output-def-scheme.cc
lily/page-breaking.cc
lily/page-layout-problem.cc
lily/page-spacing.cc
lily/paper-book.cc
lily/paper-system.cc
lily/system.cc
ly/music-functions-init.ly
ly/paper-defaults-init.ly
scm/define-grobs.scm
scm/define-markup-commands.scm
scm/lily-library.scm
scm/output-lib.scm

diff --git a/input/regression/footnote-auto-numbering-page-reset.ly b/input/regression/footnote-auto-numbering-page-reset.ly
new file mode 100644 (file)
index 0000000..c5436e8
--- /dev/null
@@ -0,0 +1,40 @@
+\version "2.15.0"
+\header {
+  texidoc = "Lilypond does footnotes."
+}
+
+\paper {
+  footnote-numbering-function = #symbol-footnotes
+}
+
+#(set-default-paper-size "a6")
+\book {
+
+\markup {
+  a \footnote b c
+  \footnote d e
+  \footnote f g
+}
+
+\markup { h i }
+
+\relative c' {
+\autoFootnoteGrob #'NoteHead #'(1 . -1) \markup { j }
+a b c d }
+
+\pageBreak
+
+\markup { k \footnote l m }
+
+\relative c' { a1 }
+
+\relative c' {
+  d4 e
+  < f  a-\autoFootnote #'(1 . -1) \markup { n } c >
+  \autoFootnoteGrob #'Beam #'(1 . 1) \markup { o }
+  \autoFootnoteGrob #'Hairpin #'(1 . 1) \markup { p }
+  a8\< [ b c d ] a4 b c |
+  d a b c |
+  d a b c |
+  d a b c\f |
+}}
diff --git a/input/regression/footnote-auto-numbering.ly b/input/regression/footnote-auto-numbering.ly
new file mode 100644 (file)
index 0000000..46d0448
--- /dev/null
@@ -0,0 +1,40 @@
+\version "2.15.0"
+\header {
+  texidoc = "Lilypond does footnotes."
+}
+
+\paper {
+  reset-footnotes-on-new-page = ##f
+}
+
+#(set-default-paper-size "a6")
+\book {
+
+\markup {
+  a \footnote b c
+  \footnote d e
+  \footnote f g
+}
+
+\markup { h i }
+
+\relative c' {
+\autoFootnoteGrob #'NoteHead #'(1 . -1) \markup { j }
+a b c d }
+
+\pageBreak
+
+\markup { k \footnote l m }
+
+\relative c' { a1 }
+
+\relative c' {
+  d4 e
+  < f  a-\autoFootnote #'(1 . -1) \markup { n } c >
+  \autoFootnoteGrob #'Beam #'(1 . 1) \markup { o }
+  \autoFootnoteGrob #'Hairpin #'(1 . 1) \markup { p }
+  a8\< [ b c d ] a4 b c |
+  d a b c |
+  d a b c |
+  d a b c\f |
+}}
index 23e9035a7128688ffb8ab050ab39291fb6ed843c..6159b5dc5f35cf3ad6caaebd26ebc297dec71570 100644 (file)
@@ -7,6 +7,10 @@ can be overrided."
 
 #(set-default-paper-size "a6")
 
+\paper {
+  footnote-auto-numbering = ##f
+}
+
 \book {
 
 \new Staff \with { \consists "Footnote_engraver" }
index 6efca6671b53ea6ceae5af321baa17f47bc0e37b..8129d6fd4b982e369d8c7aa83dcb0d96666cb8a3 100644 (file)
@@ -6,7 +6,10 @@ annotation goes to the correct page."
 
 #(set-default-paper-size "a6")
 
-\paper { ragged-last-bottom = ##f }
+\paper {
+  ragged-last-bottom = ##f
+  footnote-auto-numbering = ##f
+}
 
 \book {
 
index f13347d31aeb3f73ef988fa96ae59732bfe27357..a2b1c658034654c93efb5a85e77b2e2bdd00595d 100644 (file)
@@ -3,6 +3,10 @@
   texidoc = "Lilypond does footnotes."
 }
 
+\paper {
+  footnote-auto-numbering = ##f
+}
+
 #(set-default-paper-size "a6")
 \book {
 
index 403f903e490ccea3d811be8b1f99f6b0c341b960..cf957449ec784ecff5f384376c48b930e35fd7cb 100644 (file)
@@ -291,7 +291,7 @@ Align_interface::align_elements_to_ideal_distances (Grob *me)
   System *sys = me->get_system ();
   if (sys)
     {
-      Page_layout_problem layout (NULL, SCM_EOL, scm_list_1 (sys->self_scm ()));
+      Page_layout_problem layout (NULL, SCM_EOL, scm_list_1 (sys->self_scm ()), 0);
       layout.solution (true);
     }
   else
index c4efb46b687c17ac4cafb93cabef601915f9584a..b9872b2cd5835ae45501c8c496a45cb0bc16f1ae 100644 (file)
@@ -101,13 +101,12 @@ Balloon_interface::internal_balloon_print (Grob *me, Grob *p, Offset off)
 
   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);
-
+                                                  chain, bt);
   Stencil *text_stil = unsmob_stencil (stencil);
-
+  
   Offset z1;
+
   for (int i = X_AXIS; i < NO_AXES; i++)
     {
       Axis a ((Axis)i);
index d687f1e87da0745f409825757cc41f3af9ad8bf0..233011587f5f2bb0e56cc599e00a66544b51b65e 100644 (file)
@@ -552,9 +552,10 @@ Line_details::Line_details (Prob *pb, Output_def *paper)
   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)));
+      footnotes_.push_back (unsmob_stencil (scm_cadar (s)));
 
   last_column_ = 0;
   force_ = 0;
index 17f64d754b87089b324263e4ea8d1b875ba75286..acf6bac51ce43e9d9a11263994c2deabb8eebd03 100644 (file)
@@ -126,6 +126,7 @@ public:
   vsize system_count () const;
   Real footnote_separator_stencil_height () const;
   Real footnote_padding () const;
+  Real footnote_number_raise () const;
   Real footnote_footer_padding () const;
   Real line_count_penalty (int line_count) const;
   int line_count_status (int line_count) const;
@@ -190,6 +191,7 @@ private:
   vsize system_count_;
   Real footnote_separator_stencil_height_;
   Real footnote_padding_;
+  Real footnote_number_raise_;
   Real footnote_footer_padding_;
   int orphan_penalty_;
 
@@ -229,7 +231,7 @@ private:
   void create_system_list ();
   void find_chunks_and_breaks (Break_predicate, Prob_break_predicate);
   SCM make_page (int page_num, bool last) const;
-  SCM get_page_configuration (SCM systems, int page_num, bool ragged, bool last);
-  SCM draw_page (SCM systems, SCM config, int page_num, bool last);
+  SCM get_page_configuration (SCM systems, int page_num, int footnote_count, bool ragged, bool last);
+  SCM draw_page (SCM systems, SCM config, int page_num, int footnote_num, bool last);
 };
 #endif /* PAGE_BREAKING_HH */
index ff6fcece22c154be7ae5e9d66c747dc2bf5a06c1..629528bb53db51c0f4ea5acee8646b59abda545a 100644 (file)
@@ -26,7 +26,7 @@
 class Page_layout_problem
 {
 public:
-  Page_layout_problem (Paper_book*, SCM page, SCM systems);
+  Page_layout_problem (Paper_book*, SCM page, SCM systems, int footnote_count);
 
   SCM solution (bool ragged);
   void set_header_height (Real);
@@ -34,7 +34,8 @@ 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 vsize get_footnote_count (SCM lines);
+  static SCM get_footnotes_from_lines (SCM lines, int counter, Paper_book *pb);
   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);
index 940f6a5fb876e1b2ca76cc58280f714b570f9757..4b56238f367c49f563469bd93b6454a9fdd8c091 100644 (file)
@@ -48,7 +48,8 @@ public:
   int get_rank () const;
   vector<Stencil *> get_footnotes_in_range (vsize st, vsize end);
   void get_footnote_grobs_in_range (vector<Grob *> &out, vsize st, vsize end);
-  Stencil make_footnote_stencil (Real padding);
+  vector<Grob *> *footnote_grobs ();
+  vsize num_footnotes ();
   void do_break_substitution_and_fixup_refpoints ();
   void post_processing ();
   void populate_footnote_grob_vector ();
index d1ee31083792d90ac3958e71e87b0bfd8d1b0e13..455702b0aa82866dada472910eb3cfbda2a54558 100644 (file)
@@ -196,4 +196,4 @@ LY_DEFINE (ly_paper_fonts, "ly:paper-fonts",
     }
 
   return font_list;
-}
+}
\ No newline at end of file
index fe1d163658a548f7a59489c348bf3eadb8ea91cf..5d55c7a9f41e26114d796dd8e22fa0c84136afcc 100644 (file)
@@ -265,6 +265,10 @@ Page_breaking::Page_breaking (Paper_book *pb, Break_predicate is_break, Prob_bre
   footnote_padding_ = robust_scm2double (pb->paper_->c_variable ("footnote-padding"), 0.0);
   footnote_footer_padding_ = robust_scm2double (pb->paper_->c_variable ("footnote-footer-padding"), 0.0);
 
+  footnote_number_raise_ = (to_boolean (pb->paper_->c_variable ("footnote-auto-numbering"))
+                            ? robust_scm2double (pb->paper_->c_variable ("footnote-number-raise"), 0.0)
+                            : 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"));
@@ -342,6 +346,12 @@ Page_breaking::footnote_footer_padding () const
   return footnote_footer_padding_;
 }
 
+Real
+Page_breaking::footnote_number_raise () const
+{
+  return footnote_number_raise_;
+}
+
 bool
 Page_breaking::too_many_lines (int line_count) const
 {
@@ -521,15 +531,15 @@ Page_breaking::breakpoint_property (vsize breakpoint, char const *str)
 }
 
 SCM
-Page_breaking::get_page_configuration (SCM systems, int page_num, bool ragged, bool last)
+Page_breaking::get_page_configuration (SCM systems, int page_num, int footnote_count, bool ragged, bool last)
 {
   SCM dummy_page = make_page (page_num, last);
-  Page_layout_problem layout (book_, dummy_page, systems);
+  Page_layout_problem layout (book_, dummy_page, systems, footnote_count);
   return scm_is_pair (systems) ? layout.solution (ragged) : SCM_EOL;
 }
 
 SCM
-Page_breaking::draw_page (SCM systems, SCM configuration, int page_num, bool last)
+Page_breaking::draw_page (SCM systems, SCM configuration, int page_num, int footnote_num, bool last)
 {
   // Create a stencil for each system.
   SCM paper_systems = SCM_EOL;
@@ -556,8 +566,16 @@ Page_breaking::draw_page (SCM systems, SCM configuration, int page_num, bool las
   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")));
+
+  footnote_num = (to_boolean (book_->paper_->c_variable ("reset-footnotes-on-new-page"))
+                  ? 0
+                  : footnote_num);
+
+  SCM footnotes = Page_layout_problem::get_footnotes_from_lines (systems,
+                                                                 footnote_num,
+                                                                 book_);
+
+  Page_layout_problem::add_footnotes_to_footer (footnotes, foot, book_);
 
   p->set_property ("foot-stencil", foot->smobbed_copy ());
   scm_apply_1 (page_stencil, page, SCM_EOL);
@@ -584,8 +602,9 @@ Page_breaking::make_pages (vector<vsize> lines_per_page, SCM systems)
   // themselves. If this happens before the neighbouring staves have
   // been laid out, bad side-effects could happen (in particular,
   // Align_interface::align_to_ideal_distances might be called).
-  SCM systems_and_configs = SCM_EOL;
-
+  SCM systems_configs_fncounts = SCM_EOL;
+  vsize footnote_count = 0;
+    
   for (vsize i = 0; i < lines_per_page.size (); i++)
     {
       int page_num = i + first_page_number;
@@ -593,22 +612,24 @@ Page_breaking::make_pages (vector<vsize> lines_per_page, SCM systems)
       bool rag = ragged () || (bookpart_last_page && ragged_last ());
       SCM line_count = scm_from_int (lines_per_page[i]);
       SCM lines = scm_list_head (systems, line_count);
-      SCM config = get_page_configuration (lines, page_num, rag, bookpart_last_page);
-
-      systems_and_configs = scm_cons (scm_cons (lines, config), systems_and_configs);
+      int fn_lines = Page_layout_problem::get_footnote_count (lines);
+      SCM config = get_page_configuration (lines, page_num, footnote_count, rag, bookpart_last_page);
+      
+      systems_configs_fncounts = scm_cons (scm_list_3 (lines, config, scm_from_int ((int)footnote_count)), systems_configs_fncounts);
+      footnote_count += fn_lines;
       systems = scm_list_tail (systems, line_count);
     }
 
   // Now it's safe to make the pages.
   int page_num = first_page_number + lines_per_page.size () - 1;
-  for (SCM s = systems_and_configs; scm_is_pair (s); s = scm_cdr (s))
+  for (SCM s = systems_configs_fncounts; scm_is_pair (s); s = scm_cdr (s))
     {
       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);
+      SCM config = scm_cadar (s);
+      int footnote_num = scm_to_int (scm_caddar (s));
 
+      bool bookpart_last_page = (s == systems_configs_fncounts);
+      SCM page = draw_page (lines, config, page_num, footnote_num, bookpart_last_page);
       /* collect labels */
       SCM page_num_scm = scm_from_int (page_num);
       for (SCM l = lines ; scm_is_pair (l)  ; l = scm_cdr (l))
index 07c99586ee29c4715d21f1db56ad89255b9dfbc1..7445d21f6269f9f85bd82a416391cec929816dcb 100644 (file)
 #include "system.hh"
 #include "text-interface.hh"
 
+/*
+ Returns the number of footntoes associated with a given line.
+*/
+
+vsize
+Page_layout_problem::get_footnote_count (SCM lines)
+{
+  vsize fn_count = 0;
+  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;
+            }
+          fn_count += sys->num_footnotes ();
+        }
+      else if (Prob *p = unsmob_prob (scm_car (s)))
+        {
+          SCM stencils = p->get_property ("footnotes");
+          if (stencils == SCM_EOL)
+            continue;
+          for (SCM st = stencils; scm_is_pair (st); st = scm_cdr (st))
+            fn_count++;
+        }
+    }
+
+  return fn_count;
+}
+
 /*
    Returns a stencil for the footnote of each system.  This stencil may
    itself be comprised of several footnotes.
+
+   This is a long function, but it seems better to keep it intact rather than
+   splitting it into parts.
 */
 
 SCM
-Page_layout_problem::get_footnotes_from_lines (SCM lines, Real padding)
+Page_layout_problem::get_footnotes_from_lines (SCM lines, int counter, Paper_book *pb)
 {
+  /*
+    first, we have to see how many footnotes are on this page.
+    we need to do this first so that we can line them up
+  */
+
+  Output_def *paper = pb->paper_;
+
+  if (!paper)
+    {
+      programming_error ("Cannot get footnotes because there is no valid paper block.");
+      return SCM_EOL;
+    }
+
+  SCM number_footnote_table = pb->top_paper ()->c_variable ("number-footnote-table");
+  if (!scm_is_pair (number_footnote_table))
+    number_footnote_table = SCM_EOL;
+  SCM numbering_function = paper->c_variable ("footnote-numbering-function");
+  SCM layout = paper->self_scm ();
+  SCM props = scm_call_1 (ly_lily_module_constant ("layout-extract-page-properties"),
+                          paper->self_scm ());
+  Real padding = robust_scm2double (paper->c_variable ("footnote-padding"), 0.0);
+  Real number_raise = robust_scm2double (paper->c_variable ("footnote-number-raise"), 0.0);
+
+  vsize fn_count = get_footnote_count (lines);
+
+  // now, make the footnote stencils with the numbering function
+  SCM numbers = SCM_EOL;
+  SCM in_text_numbers = SCM_EOL;
+  bool do_numbering = to_boolean (paper->c_variable ("footnote-auto-numbering"));
+  // if there's no numbering, skip all this
+  /*
+    TODO: This recalculates numbering every time this function is called, including once
+    after the balloon prints are called.  Although it is not a huge computational drain,
+    it'd be more elegant to turn this calculation off when it is no longer needed.
+    
+    In a separate commit, it'd be nice to streamline the way that page layout property
+    is handled so that the process of building `config's in page-breaking does result
+    in duplicated work, either by making this process less complicated or (preferably)
+    by passing its results downstream.
+  */
+  if (do_numbering)
+    {
+      vector<SCM> footnote_number_markups; // Holds the numbering markups.
+      vector<Stencil *> footnote_number_stencils; // Holds translated versions of the stencilized numbering markups.
+      for (vsize i = 0; i < fn_count; i++)
+        {
+          SCM markup = scm_call_1 (numbering_function, scm_from_int (counter));
+          Stencil *s = unsmob_stencil (Text_interface::interpret_markup (layout, props, markup));
+          if (!s)
+            {
+              programming_error ("Your numbering function needs to return a stencil.");
+              markup = SCM_EOL;
+              s = new Stencil (Box (Interval (0,0), Interval (0,0)), SCM_EOL);
+            }
+          footnote_number_markups.push_back (markup);
+          footnote_number_stencils.push_back (s);
+          counter++;
+        }
+
+      // find the maximum X_AXIS length
+      Real max_length;
+      for (vsize i = 0; i < fn_count; i++)
+        max_length = max (max_length, footnote_number_stencils[i]->extent (X_AXIS).length ());
+
+      /*
+        translate each stencil such that it attains the correct maximum length and bundle the
+        footnotes into a scheme object.
+      */
+      SCM *tail = &numbers;
+      SCM *in_text_tail = &in_text_numbers;
+
+      for (vsize i = 0; i < fn_count; i++)
+        {
+          *in_text_tail = scm_cons (footnote_number_markups[i], SCM_EOL);
+          in_text_tail = SCM_CDRLOC (*in_text_tail);
+          footnote_number_stencils[i]->translate_axis (max_length - footnote_number_stencils[i]->extent (X_AXIS).length (), X_AXIS);
+          *tail = scm_cons (footnote_number_stencils[i]->smobbed_copy (), SCM_EOL);
+          tail = SCM_CDRLOC (*tail);
+        }
+    }
+  // build the footnotes
+
   SCM footnotes = SCM_EOL;
-  // ugh...code dup from the Page_layout_problem constructor
+
   for (SCM s = lines; scm_is_pair (s); s = scm_cdr (s))
     {
+      // Take care of musical systems.
       if (Grob *g = unsmob_grob (scm_car (s)))
        {
          System *sys = dynamic_cast<System *> (g);
@@ -54,8 +173,49 @@ Page_layout_problem::get_footnotes_from_lines (SCM lines, Real padding)
               programming_error ("got a grob for footnotes that wasn't a System");
               continue;
             }
-          footnotes = scm_cons (sys->make_footnote_stencil (padding).smobbed_copy (), footnotes);
+            Stencil mol;
+
+            for (vsize i = 0; i < sys->footnote_grobs ()->size (); i++)
+              {
+                Grob *footnote = sys->footnote_grobs ()->at (i);
+                SCM footnote_markup = footnote->get_property ("footnote-text");
+                if (Spanner *orig = dynamic_cast<Spanner *>(footnote))
+                  if (orig->is_broken ())
+                    footnote_markup = orig->broken_intos_[0]->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"),
+                                        paper->self_scm ());
+
+                SCM footnote_stl = Text_interface::interpret_markup (paper->self_scm (),
+                                                                     props, footnote_markup);
+
+                Stencil *footnote_stencil = unsmob_stencil (footnote_stl);
+                if (do_numbering)
+                  {
+                    SCM annotation_scm = scm_car (in_text_numbers);
+                    footnote->set_property ("text", annotation_scm);
+                    if (Spanner *orig = dynamic_cast<Spanner *>(footnote))
+                      {
+                        orig->set_property ("text", annotation_scm);
+                        if (orig->is_broken ())
+                          for (vsize i = 0; i < orig->broken_intos_.size (); i++)
+                              orig->broken_intos_[i]->set_property ("text", annotation_scm);
+                      }
+                    
+                    Stencil *annotation = unsmob_stencil (scm_car (numbers));
+                    annotation->translate_axis (footnote_stencil->extent (Y_AXIS)[UP] + number_raise - annotation->extent(Y_AXIS)[UP], Y_AXIS);
+                    footnote_stencil->add_at_edge (X_AXIS, LEFT, *annotation, 0.0);
+                    numbers = scm_cdr (numbers);
+                    in_text_numbers = scm_cdr (in_text_numbers);
+                  }
+                mol.add_at_edge (Y_AXIS, DOWN, *footnote_stencil, padding);
+              }
+            footnotes = scm_cons (mol.smobbed_copy (), footnotes);
         }
+      // Take care of top-level markups
       else if (Prob *p = unsmob_prob (scm_car (s)))
         {
           SCM stencils = p->get_property ("footnotes");
@@ -64,11 +224,31 @@ Page_layout_problem::get_footnotes_from_lines (SCM lines, Real padding)
           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);
+            {
+              Stencil mol;
+              Stencil *footnote = unsmob_stencil (scm_cadar (st));
+              mol.add_stencil (*footnote);
+              if (do_numbering)
+                {
+                  Stencil *annotation = unsmob_stencil (scm_car (numbers));
+                  SCM in_text_annotation = scm_car (in_text_numbers);
+                  SCM in_text_stencil = Text_interface::interpret_markup (layout, props, in_text_annotation);
+                  if (!unsmob_stencil (in_text_stencil))
+                    in_text_stencil = SCM_EOL;
+                  number_footnote_table = scm_cons (scm_cons (scm_caar (st), in_text_stencil), number_footnote_table);
+                  annotation->translate_axis (mol.extent (Y_AXIS)[UP] + number_raise - annotation->extent(Y_AXIS)[UP], Y_AXIS);
+                  mol.add_at_edge (X_AXIS, LEFT, *annotation, 0.0);
+                  numbers = scm_cdr (numbers);
+                  in_text_numbers = scm_cdr (in_text_numbers);
+                }
+              footnote_stencil.add_at_edge (Y_AXIS, DOWN, mol, padding);
+            }
           footnotes = scm_cons (footnote_stencil.smobbed_copy (), footnotes);
         }
     }
 
+  // note that this line of code doesn't do anything if numbering isn't turned on
+  pb->top_paper ()->set_variable (ly_symbol2scm ("number-footnote-table"), number_footnote_table);
   if (!scm_is_pair (footnotes))
     return SCM_EOL;
 
@@ -116,7 +296,7 @@ Page_layout_problem::add_footnotes_to_footer (SCM footnotes, Stencil *foot, Pape
           footnotes_found = true;
         }
     }
-  
+
   if (footnotes_found)
     {
       Stencil *separator = get_footnote_separator_stencil (pb->paper_);
@@ -125,7 +305,7 @@ Page_layout_problem::add_footnotes_to_footer (SCM footnotes, Stencil *foot, Pape
     }
 }
 
-Page_layout_problem::Page_layout_problem (Paper_book *pb, SCM page_scm, SCM systems)
+Page_layout_problem::Page_layout_problem (Paper_book *pb, SCM page_scm, SCM systems, int footnote_count)
   : bottom_skyline_ (DOWN)
 {
   Prob *page = unsmob_prob (page_scm);
@@ -139,13 +319,17 @@ 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);
-      
+        {
+          if (to_boolean (pb->paper_->c_variable ("reset-footnotes-on-new-page")))
+            footnote_count = 0;
+          SCM footnotes = get_footnotes_from_lines (systems, footnote_count, pb);
+          add_footnotes_to_footer (footnotes, foot, pb);
+        }
+      else
+        warning ("A page layout problem has been initiated that cannot accommodate footnotes.");
+
       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);
@@ -797,7 +981,7 @@ Page_layout_problem::get_fixed_spacing (Grob *before, Grob *after, int spaceable
   // Cache the result.  As above, we ignore "end."
   if (pure)
     after_sp->cache_pure_property (cache_symbol, start, 0, scm_from_double (ret));
-    
+
   return ret;
 }
 
index b04d3f394a17bde64118bc01598bf6bf19f04488..bae7ae6fb773d861440184ccf9685d194305eebb 100644 (file)
@@ -77,7 +77,8 @@ Page_spacing::account_for_footnotes (Line_details const &line)
       footnote_height += (has_footnotes_
                           ? 0.0
                           : (breaker_->footnote_separator_stencil_height ()
-                             + breaker_->footnote_padding ()));
+                             + breaker_->footnote_padding ()
+                             + breaker_->footnote_number_raise ()));
 
       has_footnotes_ = true;
       Interval extent = line.footnotes_[i]->extent (Y_AXIS);
index 9907e56d95a16b94d1f90d0e4c040c96e9c86d2a..7fcea66c62d5831c898eb8c52a69a8d1ffe2a6a1 100644 (file)
@@ -531,7 +531,7 @@ Paper_book::get_system_specs ()
              ps->set_property ("first-markup-line", SCM_BOOL_F);
 
              paper_system_set_stencil (ps, *unsmob_stencil (t));
-             
+
              SCM footnotes = get_footnotes (unsmob_stencil (t)->expr ());
              ps->set_property ("footnotes", footnotes);
              ps->set_property ("is-title", SCM_BOOL_T);
index 711d919446d4f8d21a3205563d6dde914aceb8b2..437a0eb450d32e165536e693f1fa63fdf92f9f7f 100644 (file)
@@ -18,6 +18,7 @@
 */
 
 #include "paper-system.hh"
+#include "international.hh"
 #include "item.hh"
 
 Prob *
@@ -59,27 +60,19 @@ get_footnotes (SCM expr)
       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 = footnote; scm_is_pair (y); y = scm_cdr (y))
-                {
-                  *tail = scm_cons (scm_car (y), SCM_EOL);
-                  tail = SCM_CDRLOC (*tail);
-                }
-            }
-          else if (SCM_EOL != footnote)
+          if (SCM_EOL != footnote)
             {
               *tail = scm_cons (footnote, SCM_EOL);
               tail = SCM_CDRLOC (*tail);
             }
         }
-      return out;
+      return scm_append (out);
     }
   if (head == ly_symbol2scm ("translate-stencil"))
     return get_footnotes (scm_caddr (expr));
 
   if (head == ly_symbol2scm ("footnote"))
-    return scm_cadr (expr);
+    return scm_list_1 (scm_cdr (expr));
 
   return SCM_EOL;
 }
index 1cd582de7525bc2d628b24cb062ddfc188ac7904..d151911ea5361fbe5ede643979be1343bfed1078 100644 (file)
@@ -311,31 +311,16 @@ System::get_footnotes_in_range (vsize start, vsize end)
   return out;
 }
 
-Stencil
-System::make_footnote_stencil (Real padding)
+vsize
+System::num_footnotes ()
 {
-  Stencil mol;
-
-  for (vsize i = 0; i < footnote_grobs_.size (); i++)
-    {
-      SCM footnote_markup = footnote_grobs_[i]->get_property ("footnote-text");
-      if (Spanner *orig = dynamic_cast<Spanner *>(footnote_grobs_[i]))
-        if (orig->is_broken ())
-          footnote_markup = orig->broken_intos_[0]->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);
-
-      mol.add_at_edge (Y_AXIS, DOWN, *unsmob_stencil (footnote_stl), padding);
-    }
+  return footnote_grobs_.size ();
+}
 
-  return mol;
+vector<Grob *>*
+System::footnote_grobs ()
+{
+  return &footnote_grobs_;
 }
 
 void
index 8fb4010f4e1be852db2aa8514476b9df8251a7f5..3535a0a6a7b0dc5a03188dca78078e178ede3f16 100644 (file)
@@ -344,7 +344,7 @@ 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})")
+with @var{text} referring to @var{footnote} (use like @code{\\once})")
    (make-music 'FootnoteEvent
               'symbol grob-name
               'X-offset (car offset)
@@ -352,17 +352,46 @@ footnoteGrob =
               'text text
               'footnote-text footnote))
 
+autoFootnoteGrob =
+#(define-music-function (parser location grob-name offset footnote)
+   (symbol? number-pair? markup?)
+   (_i "Footnote @var{grob-name} with the text in @var{footnote}
+allowing for the footnote to be automatically numbered such that
+the number appears at @var{offset}.  Note that, for this to take effect,
+auto-numbering must be turned on in the paper block.  Otherwise, no
+number will appear.  Use like @code{\\once})")
+   #{
+     \footnoteGrob $grob-name $offset \markup { "" } $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})")
+to @var{footnote} (use like @code{\\tweak})")
    (make-music 'FootnoteEvent
               'X-offset (car offset)
               'Y-offset (cdr offset)
               'text text
               'footnote-text footnote))
 
+% this function can't be a simple copy and past of the above because
+% it needs to be encapsulated in a Sequential Music.
+% so, there's a code dup of above :-(
+autoFootnote =
+#(define-music-function (parser location offset footnote)
+   (number-pair? markup?)
+   (_i "Footnote the item after which this comes with the text in
+@var{footnote} allowing for the footnote to be automatically numbered
+such that the number appears at @var{offset}.  Note that, for this to
+take effect, auto-numbering must be turned on in the paper block.
+Otherwise, no number will appear.  Use like @code{\\tweak})")
+   (make-music 'FootnoteEvent
+              'X-offset (car offset)
+              'Y-offset (cdr offset)
+              'text (markup "")
+              'footnote-text footnote))
+
 grace =
 #(def-grace-function startGraceMusic stopGraceMusic
    (_i "Insert @var{music} as grace notes."))
index 145fe9a52ec953bc84b45bfc4fb42f7875dd5c42..c68652d8a403778c1c490e4a13672dd90de60190 100644 (file)
   %%
   %% Footnotes
   %%
-  footnote-separator-markup = \markup { \fill-line \override #`(span-factor . 1/2) { \draw-hline } }
+  footnote-separator-markup = \markup \fill-line { \override #'(span-factor . 1/2) \draw-hline }
   footnote-padding = 0.5\mm
   footnote-footer-padding = 0.5\mm
+  footnote-number-raise = 0.5\mm
+  footnote-auto-numbering = ##t
+  footnote-numbering-function = #numbered-footnotes
+  reset-footnotes-on-new-page = ##t
 
   %%
   %% Page numbering
index 65b59bb01bdbfcec293b3a0b2a807b58558661d0..4545e5bd9f6f7c130267397d3947812e31c82881 100644 (file)
        (footnote-text . ,(grob::calc-property-by-copy 'footnote-text))
        (stencil . ,ly:balloon-interface::print)
        (text . ,(grob::calc-property-by-copy 'text))
+       (X-extent . #f)
        (Y-extent . #f)
        (X-offset . ,(grob::calc-property-by-copy 'X-offset))
        (Y-offset . ,(grob::calc-property-by-copy 'Y-offset))
        (spanner-placement . ,LEFT)
        (stencil . ,ly:balloon-interface::print-spanner)
        (text . ,(grob::calc-property-by-copy 'text))
+       (X-extent . #f)
        (Y-extent . #f)
        (X-offset . ,(grob::calc-property-by-copy 'X-offset))
        (Y-offset . ,(grob::calc-property-by-copy 'Y-offset))
index cf3417d3b58e29c31398f23a8dfef9b1519c3d79..38bf9623dc2167ade13f8b23459c70bbdb1eb842 100644 (file)
@@ -1897,16 +1897,77 @@ returns an empty markup.
 (define-markup-command (footnote layout props mkup note)
   (markup? markup?)
   #:category other
-  "Have footnote @var{note} act as an annotation to the markup @var{mkup}."
-  (ly:stencil-combine-at-edge
-    (interpret-markup layout props mkup)
-    X
-    RIGHT
+  #:properties ((raise 0.5)
+                (padding 0.0))
+  "Have footnote @var{note} act as an annotation to the markup @var{mkup}.
+
+@lilypond[verbatim,quote]
+\\markup {
+  \\footnote a b
+  \\override #'(padding . 0.2)
+  \\footnote c d
+}
+@end lilypond"
+  (let* ((markup-stencil (interpret-markup layout props mkup))
+         (auto-numbering (ly:output-def-lookup layout
+                                               'footnote-auto-numbering))
+         (footnote-hash (gensym "footnote"))
+         (stencil-seed 0)
+         (gauge-stencil (if auto-numbering
+                            (interpret-markup
+                              layout
+                              props
+                              ((ly:output-def-lookup
+                                 layout
+                                 'footnote-numbering-function)
+                                stencil-seed))
+                            empty-stencil))
+         (x-ext (if auto-numbering
+                    (ly:stencil-extent gauge-stencil X)
+                    '(0 . 0)))
+        (y-ext (if auto-numbering
+                    (ly:stencil-extent gauge-stencil Y)
+                    '(0 . 0)))
+         (footnote-number
+           (if auto-numbering
+             `(delay-stencil-evaluation
+                ,(delay
+                  (ly:stencil-expr
+                    (let* ((table
+                            (ly:output-def-lookup layout
+                                                  'number-footnote-table))
+                           (footnote-stencil (if (list? table)
+                                                (assoc-get footnote-hash
+                                                            table)
+                                                empty-stencil))
+                           (footnote-stencil (if (ly:stencil? footnote-stencil)
+                                                 footnote-stencil
+                                                 (begin
+                                                   (ly:programming-error
+"Cannot find correct footnote for a markup object.")
+                                                   empty-stencil)))
+                           (gap (- (interval-length x-ext)
+                                  (interval-length
+                                     (ly:stencil-extent footnote-stencil X))))
+                           (y-trans (- (+ (cdr y-ext)
+                                          raise)
+                                       (cdr (ly:stencil-extent footnote-stencil
+                                                               Y)))))
+                     (ly:stencil-translate footnote-stencil
+                                            (cons gap y-trans))))))
+             '()))
+         (main-stencil (ly:stencil-combine-at-edge
+                         markup-stencil
+                         X
+                         RIGHT
+                         (ly:make-stencil footnote-number x-ext y-ext)
+                         padding)))
+  (ly:stencil-add
+    main-stencil
     (ly:make-stencil
-      `(footnote ,(interpret-markup layout props note))
+      `(footnote ,footnote-hash ,(interpret-markup layout props note))
       '(0 . 0)
-      '(0 . 0))
-    0.0))
+      '(0 . 0)))))
 
 (define-markup-command (override layout props new-prop arg)
   (pair? markup?)
@@ -3428,7 +3489,7 @@ a column containing several lines of text.
     (parenthesize-stencil
      markup half-thickness scaled-width angularity padding)))
 
-\f
+
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;; Delayed markup evaluation
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
index ede65ff5e2a54b88d303e0a2429e3980384c7022..34c9cb3820096c04603fd358393651bb165491e1 100644 (file)
@@ -746,6 +746,23 @@ Handy for debugging, possibly turned off."
 
    (reverse matches))
 
+(define-public (random-string pool n)
+  "Produces a random lowercase string of length n"
+  (define (helper alphabet out num)
+    (let ((rand (random (string-length pool))))
+      (if (< num 1)
+          out
+          (helper alphabet
+                  (string-concatenate `(,out
+                                        ,(substring alphabet
+                                                    rand
+                                                    (+ 1 rand))))
+                  (- num 1)))))
+  (helper pool "" n))
+
+(define-public (random-lowercase-string n)
+  (random-string "abcdefghijklmnopqrstuvwxyz" n))
+
 ;;;;;;;;;;;;;;;;
 ;; other
 
index 5d4bdb1b617a1cc4b962eb7e8c6d27de5e554fa3..025590e5bae0e001db8f089a96b811485faa1198 100644 (file)
@@ -467,6 +467,25 @@ and duration-log @var{log}."
        (+ c0 p))))
 
 
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; annotations
+
+(define-public (numbered-footnotes int)
+  (markup #:tiny (number->string (+ 1 int))))
+
+(define-public (symbol-footnotes int)
+  (define (helper symbols out idx n)
+    (if (< n 1)
+        out
+        (helper symbols
+                (string-append out (list-ref symbols idx))
+                idx
+                (- n 1))))
+  (markup #:tiny (helper '("*" "†" "‡" "§" "¶")
+                          ""
+                          (remainder int 5)
+                          (+ 1 (quotient int 5)))))
+
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;; accidentals