Han-wen,

Here is a new patch:

 - the \label command can be used at top-level or inside music;

 - 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 the labels_ slot of the paper-book object;

 - the \page-ref markup command accesses the paper-book object by its
   props argument, and then the label/page-number table using a
   ly:paper-book-labels accessor. This evaluation of the stencil is
   postponed to the layout output phase. A user-supplied gauge is used
   in the mean time to estimate the stencil dimensions.

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

The regression test illustrates how this can be used for a table of
contents.

However, an major issue is remaining: only top-level markups can access
the paper-book instance, and thus the labels table. (With little effort,
page headers and footers could also do it). This means that \page-ref
won't work in a TextScript. I can't find a way to make that possible,
other than by modifying the paper instance. (Grobs can't access the
paper-book, do they?).

>> +      else if (Prob * markup = unsmob_prob (scm_car (l)))
>> +        labels = markup->get_property ("labels");
>> +
>
> This looks fishy; the type checking should be stricter, music is a prob too.

I haven't addressed this. Whatever the actual type of the prob that is
found here (this is in Page_breaking::make_pages), we would like to
collect the labels anyway, no? I've renamed the variable prob.

nicolas

diff --git a/input/regression/page-label.ly b/input/regression/page-label.ly
new file mode 100644
index 0000000..7adf975
--- /dev/null
+++ b/input/regression/page-label.ly
@@ -0,0 +1,32 @@
+\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" "?"))))
+
+\score {
+  { c'1 c'1 \pageBreak
+    \mark "A" \label #'markA d'
+    \mark "B" \label #'markB d'2 
+    \once \override Score . RehearsalMark #'break-visibility = #begin-of-line-invisible
+    \mark "C" \label #'markC
+  }
+  \header { piece = "First score" }
+} \label #'firstScore
+\pageBreak
+\markup \column {
+  \large \fill-line { \null "Table of contents" \null }
+  \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"
+  \toc-line #'toc "Table of contents"
+} \label #'toc
diff --git a/lily/include/page-marker.hh b/lily/include/page-marker.hh
index a925a6c..69962e6 100644
--- a/lily/include/page-marker.hh
+++ b/lily/include/page-marker.hh
@@ -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)
diff --git a/lily/include/paper-book.hh b/lily/include/paper-book.hh
index 4ea1927..25a4d6f 100644
--- a/lily/include/paper-book.hh
+++ b/lily/include/paper-book.hh
@@ -32,6 +32,7 @@ public:
   SCM header_0_;
   SCM scores_;
   Output_def *paper_;
+  SCM labels_;
 
   Paper_book ();
 
diff --git a/lily/include/paper-column-engraver.hh b/lily/include/paper-column-engraver.hh
index 793d085..fbe51a5 100644
--- a/lily/include/paper-column-engraver.hh
+++ b/lily/include/paper-column-engraver.hh
@@ -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_;
diff --git a/lily/lexer.ll b/lily/lexer.ll
index 66457f4..ec17fb3 100644
--- a/lily/lexer.ll
+++ b/lily/lexer.ll
@@ -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 {
diff --git a/lily/page-breaking.cc b/lily/page-breaking.cc
index 46a1eed..87aefc7 100644
--- a/lily/page-breaking.cc
+++ b/lily/page-breaking.cc
@@ -235,6 +235,7 @@ Page_breaking::make_pages (vector<vsize>
   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>
       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_->labels_ = label_page_table;
   ret = scm_reverse (ret);
   return ret;
 }
diff --git a/lily/page-marker-scheme.cc b/lily/page-marker-scheme.cc
index 3278a7b..f2450be 100644
--- a/lily/page-marker-scheme.cc
+++ b/lily/page-marker-scheme.cc
@@ -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 ();
 }
diff --git a/lily/page-marker.cc b/lily/page-marker.cc
index dd43c35..11b1999 100644
--- a/lily/page-marker.cc
+++ b/lily/page-marker.cc
@@ -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;
+}
+
+
diff --git a/lily/paper-book-scheme.cc b/lily/paper-book-scheme.cc
index 94bb17f..f366d88 100644
--- a/lily/paper-book-scheme.cc
+++ b/lily/paper-book-scheme.cc
@@ -56,3 +56,11 @@ LY_DEFINE (ly_paper_book_paper, "ly:pape
   Paper_book *pbook = unsmob_paper_book (pb);
   return pbook->paper_->self_scm ();
 }
+
+LY_DEFINE (ly_paper_book_labels, "ly:paper-book-labels",
+	   1, 0, 0, (SCM pb),
+	   "Return the labels - page numbers table of book PB.")
+{
+  LY_ASSERT_SMOB (Paper_book, pb, 1);
+  return unsmob_paper_book (pb)->labels_;
+}
diff --git a/lily/paper-book.cc b/lily/paper-book.cc
index 95be368..df51cf0 100644
--- a/lily/paper-book.cc
+++ b/lily/paper-book.cc
@@ -29,6 +29,7 @@ Paper_book::Paper_book ()
   scores_ = SCM_EOL;
   performances_ = SCM_EOL;
   systems_ = SCM_BOOL_F;
+  labels_ = SCM_EOL;
 
   paper_ = 0;
   smobify_self ();
@@ -53,6 +54,7 @@ Paper_book::mark_smob (SCM smob)
   scm_gc_mark (b->pages_);
   scm_gc_mark (b->performances_);
   scm_gc_mark (b->scores_);
+  scm_gc_mark (b->labels_);
   return b->systems_;
 }
 
@@ -270,6 +272,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)
 {
@@ -310,8 +330,9 @@ Paper_book::get_system_specs ()
     }
 
   SCM page_properties
-    = scm_call_1 (ly_lily_module_constant ("layout-extract-page-properties"),
-		  paper_->self_scm ());
+    = scm_cons (scm_list_1 (scm_cons (ly_symbol2scm ("paper-book"), self_scm ())),
+		scm_call_1 (ly_lily_module_constant ("layout-extract-page-properties"),
+			    paper_->self_scm ()));
 
   SCM header = SCM_EOL;
   for (SCM s = scm_reverse (scores_); scm_is_pair (s); s = scm_cdr (s))
@@ -324,11 +345,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)))
 	{
diff --git a/lily/paper-column-engraver.cc b/lily/paper-column-engraver.cc
index d583bc5..6dbafb5 100644
--- a/lily/paper-column-engraver.cc
+++ b/lily/paper-column-engraver.cc
@@ -116,6 +116,13 @@ Paper_column_engraver::listen_break (Str
   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_);
 
diff --git a/lily/parser.yy b/lily/parser.yy
index f901a6d..18668de 100644
--- a/lily/parser.yy
+++ b/lily/parser.yy
@@ -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);
 	}
diff --git a/lily/stencil-interpret.cc b/lily/stencil-interpret.cc
index f6deee0..8afc330 100644
--- a/lily/stencil-interpret.cc
+++ b/lily/stencil-interpret.cc
@@ -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));
diff --git a/lily/system.cc b/lily/system.cc
index e700615..ef0e067 100644
--- a/lily/system.cc
+++ b/lily/system.cc
@@ -227,11 +227,17 @@ System::break_into_pieces (vector<Column
 
       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);
diff --git a/ly/music-functions-init.ly b/ly/music-functions-init.ly
index 8360dcb..ea28ba4 100644
--- a/ly/music-functions-init.ly
+++ b/ly/music-functions-init.ly
@@ -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
diff --git a/scm/define-event-classes.scm b/scm/define-event-classes.scm
index 2bda1a2..f5d7b79 100644
--- a/scm/define-event-classes.scm
+++ b/scm/define-event-classes.scm
@@ -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.
diff --git a/scm/define-markup-commands.scm b/scm/define-markup-commands.scm
index 8a0853e..8198cbc 100644
--- a/scm/define-markup-commands.scm
+++ b/scm/define-markup-commands.scm
@@ -1463,8 +1463,31 @@ that."
         (m (interpret-markup layout props arg)))
     (bracketify-stencil m Y th (* 2.5 th) th)))
 
-
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; 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* ((paper-book (chain-assoc-get 'paper-book props #f))
+		       (table (and paper-book (ly:paper-book-labels paper-book)))
+		       (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)))
diff --git a/scm/define-music-types.scm b/scm/define-music-types.scm
index 03ac6f8..c6a83c4 100644
--- a/scm/define-music-types.scm
+++ b/scm/define-music-types.scm
@@ -238,6 +238,9 @@ Syntax: @code{\\key } @var{name} @var{sc
 	(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.
 
diff --git a/scm/define-stencil-commands.scm b/scm/define-stencil-commands.scm
index 9b0b942..6a027b6 100644
--- a/scm/define-stencil-commands.scm
+++ b/scm/define-stencil-commands.scm
@@ -37,6 +37,8 @@
        no-origin
        placebox
        unknown
+
+       delay-stencil-evaluation
        ))
 
 ;; TODO:
diff --git a/scm/lily-library.scm b/scm/lily-library.scm
index 7a1141d..5435533 100644
--- a/scm/lily-library.scm
+++ b/scm/lily-library.scm
@@ -70,17 +70,21 @@
 	  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
_______________________________________________
lilypond-devel mailing list
[email protected]
http://lists.gnu.org/mailman/listinfo/lilypond-devel

Reply via email to