]> git.donarmstrong.com Git - lilypond.git/commitdiff
Bookmarking labels and page referencing:
authorNicolas Sceaux <nicolas.sceaux@free.fr>
Mon, 28 May 2007 17:37:57 +0000 (19:37 +0200)
committerNicolas Sceaux <nicolas.sceaux@free.fr>
Mon, 28 May 2007 17:37:57 +0000 (19:37 +0200)
 - the \label command can be used at top-level (Page_marker) or inside
   music (LabelEvent);

 - for labels inside music: the paper_column_engraver handles
   label-events and fills the labels property of the paper column;

 - for top-level labels: Paper_book::get_system_specs() fill the labels
   property of the previous prob or of the first column of the previous
   paper_score;

 - when systems are built, the labels of their columns are collected to
   set the system labels property;

 - in Page_breaking::make_pages(), a label->page-number table is built,
   and stored in a variable of the paper_book paper;

 - the \page-ref markup command accesses the table though its layout
   argument. The actual evaluation of the stencil is postponed to the
   layout output phase using a new stencil command:
   delayed-stencil-evaluation;

 - a MARKUP_HEAD_SCM0_MARKUP1_MARKUP2 token has been added to the
   parser for this new markup command.

18 files changed:
input/regression/page-label.ly [new file with mode: 0644]
lily/include/page-marker.hh
lily/include/paper-column-engraver.hh
lily/lexer.ll
lily/page-breaking.cc
lily/page-marker-scheme.cc
lily/page-marker.cc
lily/paper-book.cc
lily/paper-column-engraver.cc
lily/parser.yy
lily/stencil-interpret.cc
lily/system.cc
ly/music-functions-init.ly
scm/define-event-classes.scm
scm/define-markup-commands.scm
scm/define-music-types.scm
scm/define-stencil-commands.scm
scm/lily-library.scm

diff --git a/input/regression/page-label.ly b/input/regression/page-label.ly
new file mode 100644 (file)
index 0000000..a83df21
--- /dev/null
@@ -0,0 +1,42 @@
+\version "2.11.24"
+
+\header {
+  texidoc = "Page labels may be placed inside music or at top-level,
+and refered to in markups."
+}
+
+#(set-default-paper-size "a6")
+
+#(define-markup-command (toc-line layout props label text) (symbol? markup?)
+  (interpret-markup layout props
+   (markup #:fill-line (text #:page-ref label "8" "?"))))
+
+\markup \huge \fill-line { \null "Title Page" \null }
+
+\pageBreak
+
+\markup \column {
+  \large \fill-line { \null "Table of contents" \null }
+  \toc-line #'toc "Table of contents"
+  \toc-line #'firstScore "First Score"
+  \toc-line #'markA "Mark A"
+  \toc-line #'markB "Mark B"
+  \toc-line #'markC "Mark C"
+  \toc-line #'unknown "Unknown label"
+} \label #'toc
+
+\pageBreak
+
+\score {
+  { c'2 c'
+    \mark \markup { A (page \concat { \page-ref #'markA "0" "?" ) }} \label #'markA 
+    c' c'
+    \pageBreak
+    \mark "B" \label #'markB
+    d' d'
+    d' d'
+    \once \override Score . RehearsalMark #'break-visibility = #begin-of-line-invisible
+    \mark "C" \label #'markC
+  }
+  \header { piece = "First score" }
+} \label #'firstScore
\ No newline at end of file
index a925a6c9ce87c6d8da562f04c1104195d9a652ad..69962e6af8e3a6a46e7289281569fe53bf0aaa1f 100644 (file)
@@ -17,12 +17,17 @@ class Page_marker
 
   SCM symbol_; /* either 'page-turn-permission or 'page-break-permission */
   SCM permission_;  /* 'force, 'allow, or '() */
+  SCM label_; /* bookmarking label (a symbol) */
 
 public:
-  Page_marker (SCM symbol, SCM permission);
+  Page_marker ();
   
+  void set_permission (SCM symbol, SCM permission);
+  void set_label (SCM label);
+
   SCM permission_symbol ();
   SCM permission_value ();
+  SCM label ();
 };
 
 DECLARE_UNSMOB (Page_marker, page_marker)
index 793d085e0cdaae1edf8c33762712f51545812e9d..fbe51a5c0c719c288b4d018873728f73e59ee687 100644 (file)
@@ -31,6 +31,7 @@ protected:
   virtual void finalize ();
 
   DECLARE_TRANSLATOR_LISTENER (break);
+  DECLARE_TRANSLATOR_LISTENER (label);
 
   DECLARE_ACKNOWLEDGER (item);
   DECLARE_ACKNOWLEDGER (note_spacing);
@@ -38,6 +39,7 @@ protected:
 
   System *system_;
   vector<Stream_event*> break_events_;
+  vector<Stream_event*> label_events_;
   int breaks_;                 // used for stat printing
   Paper_column *command_column_;
   Paper_column *musical_column_;
index 66457f4b9e75b6b1b3b850c0912080de01cfc040..ec17fb399e03963fb9dcdf1ec8753d345d82c8dc 100644 (file)
@@ -536,6 +536,8 @@ BOM_UTF8    \357\273\277
                                return MARKUP_HEAD_SCM0_MARKUP1;
                        else if (tag == ly_symbol2scm ("scheme0-scheme1-markup2"))
                                return MARKUP_HEAD_SCM0_SCM1_MARKUP2;
+                       else if (tag == ly_symbol2scm ("scheme0-markup1-markup2"))
+                               return MARKUP_HEAD_SCM0_MARKUP1_MARKUP2;
                        else if (tag == ly_symbol2scm ("scheme0-scheme1-scheme2"))
                                return MARKUP_HEAD_SCM0_SCM1_SCM2;
                        else {
index 46a1eed02d04d81c0e65bd51fccea9582d275881..a972e3e904162d0aa2366deb5622f46bd7435af8 100644 (file)
@@ -235,6 +235,7 @@ Page_breaking::make_pages (vector<vsize> lines_per_page, SCM systems)
   SCM book = book_->self_scm ();
   int first_page_number = robust_scm2int (book_->paper_->c_variable ("first-page-number"), 1);
   SCM ret = SCM_EOL;
+  SCM label_page_table = SCM_EOL;
 
   for (vsize i = 0; i < lines_per_page.size (); i++)
     {
@@ -246,10 +247,28 @@ Page_breaking::make_pages (vector<vsize> lines_per_page, SCM systems)
       SCM page = scm_apply_0 (make_page,
                              scm_list_n (book, lines, page_num, rag, last, SCM_UNDEFINED));
 
+      /* collect labels */
+      for (SCM l = lines ; scm_is_pair (l)  ; l = scm_cdr (l))
+       {
+         SCM labels = SCM_EOL;
+         if (Grob * line = unsmob_grob (scm_car (l)))
+           {
+             System *system = dynamic_cast<System*> (line);
+             labels = system->get_property ("labels");
+           }
+         else if (Prob *prob = unsmob_prob (scm_car (l)))
+           labels = prob->get_property ("labels");
+
+         for (SCM lbls = labels ; scm_is_pair (lbls) ; lbls = scm_cdr (lbls))
+           label_page_table = scm_cons (scm_cons (scm_car (lbls), page_num),
+                                        label_page_table);
+       }
+
       scm_apply_1 (page_stencil, page, SCM_EOL);
       ret = scm_cons (page, ret);
       systems = scm_list_tail (systems, line_count);
     }
+  book_->paper_->set_variable (ly_symbol2scm ("label-page-table"), label_page_table);
   ret = scm_reverse (ret);
   return ret;
 }
index 3278a7b013d6d9a350108342f778279b4718c4cc..f2450be0f3240535a3a8aed6cc1e1d329b67fb1d 100644 (file)
@@ -8,12 +8,24 @@
 
 #include "page-marker.hh"
 
-LY_DEFINE (ly_make_page_marker, "ly:make-page-marker",
+LY_DEFINE (ly_make_page_permission_marker, "ly:make-page-permission-marker",
           2, 0, 0,
           (SCM symbol, SCM permission),
           "Return page marker with page breaking and turning permissions.")
 {
   LY_ASSERT_TYPE (ly_is_symbol, symbol, 1);
-  Page_marker *page_marker = new Page_marker (symbol, permission);
+  Page_marker *page_marker = new Page_marker ();
+  page_marker->set_permission (symbol, permission);
+  return page_marker->unprotect ();
+}
+
+LY_DEFINE (ly_make_page_label_marker, "ly:make-page-label-marker",
+          1, 0, 0,
+          (SCM label),
+          "Return page marker with label.")
+{
+  LY_ASSERT_TYPE (ly_is_symbol, label, 1);
+  Page_marker *page_marker = new Page_marker ();
+  page_marker->set_label (label);
   return page_marker->unprotect ();
 }
index dd43c35640d23585a38b9394fba61d6a9520c0dc..11b19999a1597ac79189a23d8f94f948625b172e 100644 (file)
@@ -9,10 +9,11 @@
 #include "page-marker.hh"
 #include "ly-smobs.icc"
 
-Page_marker::Page_marker (SCM symbol, SCM permission)
+Page_marker::Page_marker ()
 {
-  symbol_ = symbol;
-  permission_ = permission;
+  symbol_ = SCM_EOL;
+  permission_ = SCM_EOL;
+  label_ = SCM_EOL;
   smobify_self ();
 }
 
@@ -30,6 +31,7 @@ Page_marker::mark_smob (SCM smob)
   Page_marker *pm = (Page_marker *) SCM_CELL_WORD_1 (smob);
   scm_gc_mark (pm->symbol_);
   scm_gc_mark (pm->permission_);
+  scm_gc_mark (pm->label_);
   return SCM_EOL;
 }
 
@@ -53,3 +55,24 @@ Page_marker::permission_value ()
 {
   return permission_;
 }
+
+SCM
+Page_marker::label ()
+{
+  return label_;
+}
+
+void
+Page_marker::set_permission (SCM symbol, SCM permission)
+{
+  symbol_ = symbol;
+  permission_ = permission;
+}
+
+void
+Page_marker::set_label (SCM label)
+{
+  label_ = label;
+}
+
+
index 95be368234106029c178a74ed3ae152e9a6e6823..641379ab9575555f85e5e485fe325ac1b925dab1 100644 (file)
@@ -270,6 +270,24 @@ set_system_penalty (SCM sys, SCM header)
     }
 }
 
+void
+set_label (SCM sys, SCM label)
+{
+  if (Paper_score *ps = dynamic_cast<Paper_score*> (unsmob_music_output (sys)))
+    {
+      vector<Grob*> cols = ps->get_columns ();
+      if (cols.size ())
+       {
+         Paper_column *col = dynamic_cast<Paper_column*> (cols[0]);
+         col->set_property ("labels", scm_cons (label, col->get_property ("labels")));
+         Paper_column *col_right = col->find_prebroken_piece (RIGHT);
+         col_right->set_property ("labels", scm_cons (label, col_right->get_property ("labels")));
+       }
+    }
+  else if (Prob *pb = unsmob_prob (sys))
+    pb->set_property ("labels", scm_cons (label, pb->get_property ("labels")));
+}
+
 SCM
 Paper_book::get_score_title (SCM header)
 {
@@ -324,11 +342,22 @@ Paper_book::get_system_specs ()
        }
       else if (Page_marker *page_marker = unsmob_page_marker (scm_car (s)))
        {
-         /* a page marker: set previous element page break or turn permission */
-         if (scm_is_pair (system_specs))
-           set_page_permission (scm_car (system_specs),
-                                page_marker->permission_symbol (),
-                                page_marker->permission_value ());
+         /* page markers are used to set page breaking/turning permission,
+            or to place bookmarking labels */ 
+         if (scm_is_symbol (page_marker->permission_symbol ()))
+           {
+             /* set previous element page break or turn permission */
+             if (scm_is_pair (system_specs))
+               set_page_permission (scm_car (system_specs),
+                                    page_marker->permission_symbol (),
+                                    page_marker->permission_value ());
+           }
+         if (scm_is_symbol (page_marker->label ()))
+           {
+             /* set previous element label */
+             if (scm_is_pair (system_specs))
+               set_label (scm_car (system_specs), page_marker->label ());
+           }
        }
       else if (Music_output *mop = unsmob_music_output (scm_car (s)))
        {
index d583bc513b88816c781b11652fba4528dff2e559..db088a57359ad75246fb9cd1b2f06f035d9d4f69 100644 (file)
@@ -116,6 +116,13 @@ Paper_column_engraver::listen_break (Stream_event *ev)
   break_events_.push_back (ev);
 }
 
+IMPLEMENT_TRANSLATOR_LISTENER (Paper_column_engraver, label);
+void
+Paper_column_engraver::listen_label (Stream_event *ev)
+{
+  label_events_.push_back (ev);
+}
+
 void
 Paper_column_engraver::process_music ()
 {
@@ -150,6 +157,13 @@ Paper_column_engraver::process_music ()
        command_column_->set_property (perm_str.c_str (), perm);
     }
 
+  for (vsize i = 0 ; i < label_events_.size () ; i ++)
+    {
+      SCM label = label_events_[i]->get_property ("label");
+      SCM labels = command_column_->get_property ("labels");
+      command_column_->set_property ("labels", scm_cons (label, labels));
+    }
+
   bool start_of_measure = (last_moment_.main_part_ != now_mom ().main_part_
                           && !measure_position (context ()).main_part_);
 
@@ -211,7 +225,7 @@ Paper_column_engraver::stop_translation_timestep ()
 
   first_ = false;
   break_events_.clear ();
-
+  label_events_.clear ();
 
   SCM mpos = get_property ("measurePosition");
   SCM barnum = get_property ("internalBarNumber");
index f901a6de0e67cdc6bc54ea00e4ca4bfc08d811ec..18668dead09d914cee5e9f18031eab974ac86b3e 100644 (file)
@@ -279,6 +279,7 @@ If we give names, Bison complains.
 %token <scm> MARKUP_HEAD_SCM0_MARKUP1
 %token <scm> MARKUP_HEAD_SCM0_SCM1
 %token <scm> MARKUP_HEAD_SCM0_SCM1_MARKUP2
+%token <scm> MARKUP_HEAD_SCM0_MARKUP1_MARKUP2
 %token <scm> MARKUP_HEAD_SCM0_SCM1_SCM2
 %token <scm> MARKUP_IDENTIFIER
 %token <scm> MUSIC_FUNCTION
@@ -2293,6 +2294,9 @@ simple_markup:
        | MARKUP_HEAD_SCM0_SCM1 embedded_scm embedded_scm {
                $$ = scm_list_3 ($1, $2, $3);
        }
+       | MARKUP_HEAD_SCM0_MARKUP1_MARKUP2 embedded_scm markup markup {
+               $$ = scm_list_4 ($1, $2, $3, $4);
+       }
        | MARKUP_HEAD_EMPTY {
                $$ = scm_list_1 ($1);
        }
index f6deee072e969f9db966e5d4a2b34d3f703ebcae..8afc330d6f8bd63fcd75f3b6b8f074ffb7b9f2fc 100644 (file)
@@ -21,6 +21,11 @@ interpret_stencil_expression (SCM expr,
 
       SCM head = scm_car (expr);
 
+      if (head == ly_symbol2scm ("delay-stencil-evaluation"))
+       {
+         interpret_stencil_expression (scm_force (scm_cadr (expr)), func, func_arg, o);
+         return;
+       }
       if (head == ly_symbol2scm ("translate-stencil"))
        {
          o += ly_scm2offset (scm_cadr (expr));
index e700615523045a4882030f00cf08e8bf73e56d45..ef0e0679ab54e34a66245bf00ecb74bd48d69179 100644 (file)
@@ -227,11 +227,17 @@ System::break_into_pieces (vector<Column_x_positions> const &breaking)
 
       system->set_bound (LEFT, c[0]);
       system->set_bound (RIGHT, c.back ());
+      SCM system_labels = SCM_EOL;
       for (vsize j = 0; j < c.size (); j++)
        {
          c[j]->translate_axis (breaking[i].config_[j], X_AXIS);
          dynamic_cast<Paper_column *> (c[j])->system_ = system;
+         /* collect the column labels */
+         SCM col_labels = c[j]->get_property ("labels");
+         if (scm_is_pair (col_labels))
+           system_labels = scm_append (scm_list_2 (col_labels, system_labels));
        }
+      system->set_property ("labels", system_labels);
       
       set_loose_columns (system, &breaking[i]);
       broken_intos_.push_back (system);
index 8360dcbe84b7f7e6e938ff99dc7f1f4b874cb657..ea28ba4a58ccf86fd6995b5d03d6529a9852730b 100644 (file)
@@ -298,7 +298,15 @@ killCues =
       (if (string? (ly:music-property mus 'quoted-music-name))
          (ly:music-property mus 'element)
          mus)) music))
-   
+
+label = 
+#(define-music-function (parser location label) (symbol?)
+   (_i "Place a bookmarking label, either at top-level or inside music.")
+   (make-music 'EventChord
+              'page-marker #t
+              'label label
+              'elements (list (make-music 'LabelEvent
+                                          'label label)))) 
 
 makeClusters =
 #(define-music-function
index 2bda1a2d025f020c4fcc3c20c5c3a05bc367db9b..f5d7b790de852048db22b202fefa30c7ff4e1560 100644 (file)
@@ -16,7 +16,7 @@
                                OneTimeStep Finish)) 
     (music-event . (annotate-output-event
                    arpeggio-event breathing-event extender-event span-event
-      rhythmic-event dynamic-event break-event percent-event
+      rhythmic-event dynamic-event break-event label-event percent-event
       key-change-event string-number-event stroke-finger-event tie-event part-combine-event
       beam-forbid-event script-event
       tremolo-event bend-after-event fingering-event glissando-event
@@ -86,7 +86,7 @@
 ;; All leaf event classes that no translator listens to
 ;; directly. Avoids printing a warning.
 (define unlistened-music-event-classes
-  '(harmonic-event line-break-event page-break-event page-turn-event
+  '(harmonic-event line-break-event page-break-event page-turn-event label-event
                   solo-one-event solo-two-event skip-event unisono-event))
 
 ;; produce neater representation of music event tree.
index 8a0853eb2c1e6d8fd8493111825d1e26ef7cf6b5..d1f5dd04f81b2642d0ce862dc9948f24a75e8ceb 100644 (file)
@@ -1463,8 +1463,30 @@ that."
         (m (interpret-markup layout props arg)))
     (bracketify-stencil m Y th (* 2.5 th) th)))
 \f
-
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; size indications arrow
+;; Delayed markup evaluation
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
+(define-builtin-markup-command (page-ref layout props label gauge default)
+  (symbol? markup? markup?)
+  "Reference to a page number. @var{label} is the label set on the referenced
+page (using the @code{\\label} command), @var{gauge} a markup used to estimate
+the maximum width of the page number, and @var{default} the value to display
+when @var{label} is not found."
+  (let* ((gauge-stencil (interpret-markup layout props gauge))
+        (x-ext (ly:stencil-extent gauge-stencil X))
+        (y-ext (ly:stencil-extent gauge-stencil Y)))
+    (ly:make-stencil
+     `(delay-stencil-evaluation
+       ,(delay (ly:stencil-expr
+               (let* ((table (ly:output-def-lookup layout 'label-page-table))
+                      (label-page (and (list? table) (assoc label table)))
+                      (page-number (and label-page (cdr label-page)))
+                      (page-markup (if page-number (format "~a" page-number) default))
+                      (page-stencil (interpret-markup layout props page-markup))
+                      (gap (- (interval-length x-ext)
+                              (interval-length (ly:stencil-extent page-stencil X)))))
+                 (interpret-markup layout props
+                                   (markup #:concat (#:hspace gap page-markup)))))))
+     x-ext
+     y-ext)))
index 03ac6f83e3dd3317bfec84ba56447d617dadd8ea..c6a83c42c2eed4ea5e9adcbbec3381f59df1aa03 100644 (file)
@@ -238,6 +238,9 @@ Syntax: @code{\\key } @var{name} @var{scale}.")
        (to-relative-callback . ,(lambda (x p) p))
        (types . (general-music key-change-event event))
        ))
+    (LabelEvent
+     . ((description . "Place a bookmarking label")
+       (types . (general-music label-event event))))
     (LaissezVibrerEvent
      . ((description . "Don't damp this chord.
 
index 9b0b9422d6adc459071fbc497fdbb47f552e246e..6a027b6ed4a62b8ac1729221cd63241614a70146 100644 (file)
@@ -37,6 +37,8 @@
        no-origin
        placebox
        unknown
+
+       delay-stencil-evaluation
        ))
 
 ;; TODO:
index 7a1141d8f4e284af472aa6d6ae0331587d283784..54355336d89513d4b263c0c5caab657439e4f6ee 100644 (file)
          value
          #f)))
   (cond ((music-property 'page-marker)
-        ;; a page marker: set page break/turn permissions
-        (for-each (lambda (symbol)
-                    (let ((permission (music-property symbol)))
-                      (if (symbol? permission)
-                          (score-handler
-                           (ly:make-page-marker symbol
-                                                (if (eqv? 'forbid permission)
-                                                    '()
-                                                    permission))))))
-                  (list 'line-break-permission 'page-break-permission
-                        'page-turn-permission)))
+        ;; a page marker: set page break/turn permissions or label
+        (begin
+          (let ((label (music-property 'label)))
+            (if (symbol? label)
+                (score-handler (ly:make-page-label-marker label))))
+          (for-each (lambda (symbol)
+                      (let ((permission (music-property symbol)))
+                        (if (symbol? permission)
+                            (score-handler
+                             (ly:make-page-permission-marker symbol
+                                                             (if (eqv? 'forbid permission)
+                                                                 '()
+                                                                 permission))))))
+                    (list 'line-break-permission 'page-break-permission
+                          'page-turn-permission))))
        ((not (music-property 'void))
         ;; a regular music expression: make a score with this music
         ;; void music is discarded