From 291f1d623e2ceb9916532622ba02b02807c4b22d Mon Sep 17 00:00:00 2001 From: Nicolas Sceaux Date: Mon, 28 May 2007 19:37:57 +0200 Subject: [PATCH] Bookmarking labels and page referencing: - 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. --- input/regression/page-label.ly | 42 +++++++++++++++++++++++++++ lily/include/page-marker.hh | 7 ++++- lily/include/paper-column-engraver.hh | 2 ++ lily/lexer.ll | 2 ++ lily/page-breaking.cc | 19 ++++++++++++ lily/page-marker-scheme.cc | 16 ++++++++-- lily/page-marker.cc | 29 ++++++++++++++++-- lily/paper-book.cc | 39 +++++++++++++++++++++---- lily/paper-column-engraver.cc | 16 +++++++++- lily/parser.yy | 4 +++ lily/stencil-interpret.cc | 5 ++++ lily/system.cc | 6 ++++ ly/music-functions-init.ly | 10 ++++++- scm/define-event-classes.scm | 4 +-- scm/define-markup-commands.scm | 26 +++++++++++++++-- scm/define-music-types.scm | 3 ++ scm/define-stencil-commands.scm | 2 ++ scm/lily-library.scm | 26 ++++++++++------- 18 files changed, 230 insertions(+), 28 deletions(-) create mode 100644 input/regression/page-label.ly diff --git a/input/regression/page-label.ly b/input/regression/page-label.ly new file mode 100644 index 0000000000..a83df21d8e --- /dev/null +++ b/input/regression/page-label.ly @@ -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 diff --git a/lily/include/page-marker.hh b/lily/include/page-marker.hh index a925a6c9ce..69962e6af8 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-column-engraver.hh b/lily/include/paper-column-engraver.hh index 793d085e0c..fbe51a5c0c 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 break_events_; + vector 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 66457f4b9e..ec17fb399e 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 46a1eed02d..a972e3e904 100644 --- a/lily/page-breaking.cc +++ b/lily/page-breaking.cc @@ -235,6 +235,7 @@ Page_breaking::make_pages (vector 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 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 (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; } diff --git a/lily/page-marker-scheme.cc b/lily/page-marker-scheme.cc index 3278a7b013..f2450be0f3 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 dd43c35640..11b19999a1 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.cc b/lily/paper-book.cc index 95be368234..641379ab95 100644 --- a/lily/paper-book.cc +++ b/lily/paper-book.cc @@ -270,6 +270,24 @@ set_system_penalty (SCM sys, SCM header) } } +void +set_label (SCM sys, SCM label) +{ + if (Paper_score *ps = dynamic_cast (unsmob_music_output (sys))) + { + vector cols = ps->get_columns (); + if (cols.size ()) + { + Paper_column *col = dynamic_cast (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))) { diff --git a/lily/paper-column-engraver.cc b/lily/paper-column-engraver.cc index d583bc513b..db088a5735 100644 --- a/lily/paper-column-engraver.cc +++ b/lily/paper-column-engraver.cc @@ -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"); diff --git a/lily/parser.yy b/lily/parser.yy index f901a6de0e..18668dead0 100644 --- a/lily/parser.yy +++ b/lily/parser.yy @@ -279,6 +279,7 @@ If we give names, Bison complains. %token MARKUP_HEAD_SCM0_MARKUP1 %token MARKUP_HEAD_SCM0_SCM1 %token MARKUP_HEAD_SCM0_SCM1_MARKUP2 +%token MARKUP_HEAD_SCM0_MARKUP1_MARKUP2 %token MARKUP_HEAD_SCM0_SCM1_SCM2 %token MARKUP_IDENTIFIER %token 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 f6deee072e..8afc330d6f 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 e700615523..ef0e0679ab 100644 --- a/lily/system.cc +++ b/lily/system.cc @@ -227,11 +227,17 @@ System::break_into_pieces (vector 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 (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 8360dcbe84..ea28ba4a58 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 2bda1a2d02..f5d7b790de 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 8a0853eb2c..d1f5dd04f8 100644 --- a/scm/define-markup-commands.scm +++ b/scm/define-markup-commands.scm @@ -1463,8 +1463,30 @@ 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* ((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))) diff --git a/scm/define-music-types.scm b/scm/define-music-types.scm index 03ac6f83e3..c6a83c42c2 100644 --- a/scm/define-music-types.scm +++ b/scm/define-music-types.scm @@ -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. diff --git a/scm/define-stencil-commands.scm b/scm/define-stencil-commands.scm index 9b0b9422d6..6a027b6ed4 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 7a1141d8f4..54355336d8 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 -- 2.39.2