From: Nicolas Sceaux Date: Tue, 11 Nov 2008 15:57:57 +0000 (+0100) Subject: Merge branch 'master' into nested-bookparts X-Git-Tag: release/2.11.65-1~51^2~2^2~6 X-Git-Url: https://git.donarmstrong.com/?a=commitdiff_plain;h=c6554467b0a9beddf0d7ef12746ae31a25fe36e7;hp=39dbc0fafd835267b23c2ea4166046d19040f281;p=lilypond.git Merge branch 'master' into nested-bookparts --- diff --git a/input/new/book-parts.ly b/input/new/book-parts.ly new file mode 100644 index 0000000000..1b4754dc6c --- /dev/null +++ b/input/new/book-parts.ly @@ -0,0 +1,64 @@ +\version "2.11.63" + +\header { + lsrtags = "paper-and-layout" + texidocs = "@code{\\bookpart} can be used to split a book into +several parts. +Each part last page can be affected by @code{ragged-bottom-last}. +Header and footer markups can detect a part last page, and make the +difference with the book last page. +" + doctitle = "Book parts" +} + +#(set-default-paper-size "a6") + +\book { + + %% book paper, which is inherited by all children bookparts + \paper { + ragged-last-bottom = ##t + %% Page footer: add a different part-tagline at part last page + oddFooterMarkup = \markup { + \column { + \fill-line { + %% Copyright header field only on book first page. + \on-the-fly #first-page \fromproperty #'header:copyright + } + \fill-line { + %% Part tagline header field only on each part last page. + \on-the-fly #part-last-page \fromproperty #'header:parttagline + } + \fill-line { + %% Tagline header field only on book last page. + \on-the-fly #last-page \fromproperty #'header:tagline + } + } + } + } + + %% book header, which is inherited by the first bookpart + \header { + title = "Book title" + copyright = "Copyright line on book first page" + parttagline = "Part tagline" + tagline = "Book tagline" + } + + \bookpart { + %% a different page breaking function may be used on each part + \paper { #(define page-breaking optimal-page-breaks) } + \header { subtitle = "First part" } + \markup { The first book part } + \markup { a page break } + \pageBreak + \markup { first part last page } + \markup \wordwrap { with ragged-last-bottom (see the space below this text) } + } + + \bookpart { + \header { subtitle = "Second part" } + { c' } + } + +} diff --git a/input/regression/bookparts.ly b/input/regression/bookparts.ly new file mode 100644 index 0000000000..a612a0fe8b --- /dev/null +++ b/input/regression/bookparts.ly @@ -0,0 +1,39 @@ +\version "2.11.63" + +\header { + texidoc = "A book can be split into several parts with different paper settings, +using @code{\\bookpart}. + +Fonts are loaded into the top-level paper. +Page labels are also collected into the top-level paper." +} + +#(set-default-paper-size "a6") + +#(define-markup-command (roman-page-number layout props) () + (let ((page-number (chain-assoc-get 'page:page-number props))) + (interpret-markup layout props (format #f "~@r" page-number)))) + +\book { + \tocItem \markup "First part" + \header { title = "Book with several parts" } + \markup { First part } + \markup { with default paper settings. } + + \bookpart { + \paper { + left-margin = 20\mm + right-margin = 20\mm + line-width = 65\mm + evenHeaderMarkup = \markup \fill-line { \roman-page-number "SECOND PART" \null } + oddHeaderMarkup = \markup \fill-line { \null "SECOND PART" \roman-page-number } + } + \tocItem \markup "Second part" + \markup \justify { Second part, with different margins and page header. } + { c' } + } + + \tocItem \markup "Third part" + \markup { Third part } + \markuplines \table-of-contents +} diff --git a/lily/book-scheme.cc b/lily/book-scheme.cc index e00f1ded49..58a0565061 100644 --- a/lily/book-scheme.cc +++ b/lily/book-scheme.cc @@ -34,6 +34,18 @@ LY_DEFINE (ly_make_book, "ly:make-book", return x; } +LY_DEFINE (ly_make_book_part, "ly:make-book-part", + 1, 0, 0, (SCM scores), + "Make a @code{\\bookpart} containing @code{\\scores}.") +{ + Book *book = new Book; + book->scores_ = scm_append (scm_list_2 (scores, book->scores_)); + + SCM x = book->self_scm (); + book->unprotect (); + return x; +} + LY_DEFINE (ly_book_process, "ly:book-process", 4, 0, 0, (SCM book_smob, SCM default_paper, @@ -96,3 +108,13 @@ LY_DEFINE (ly_book_add_score_x, "ly:book-add-score!", book->add_score (score); return SCM_UNSPECIFIED; } + +LY_DEFINE (ly_book_add_bookpart_x, "ly:book-add-bookpart!", + 2, 0, 0, (SCM book_smob, SCM book_part), + "Add @var{book_part} to @var{book-smob} book part list.") +{ + LY_ASSERT_SMOB (Book, book_smob, 1); + Book *book = unsmob_book (book_smob); + book->add_bookpart (book_part); + return SCM_UNSPECIFIED; +} diff --git a/lily/book.cc b/lily/book.cc index 335507601d..96243e1b95 100644 --- a/lily/book.cc +++ b/lily/book.cc @@ -29,6 +29,7 @@ Book::Book () paper_ = 0; header_ = SCM_EOL; scores_ = SCM_EOL; + bookparts_ = SCM_EOL; input_location_ = SCM_EOL; smobify_self (); @@ -40,6 +41,7 @@ Book::Book (Book const &s) paper_ = 0; header_ = SCM_EOL; scores_ = SCM_EOL; + bookparts_ = SCM_EOL; input_location_ = SCM_EOL; smobify_self (); @@ -64,6 +66,16 @@ Book::Book (Book const &s) t = SCM_CDRLOC (*t); newscore->unprotect (); } + + t = &bookparts_; + for (SCM p = s.bookparts_; scm_is_pair (p); p = scm_cdr (p)) + { + Book *newpart = unsmob_book (scm_car (p))->clone (); + + *t = scm_cons (newpart->self_scm (), SCM_EOL); + t = SCM_CDRLOC (*t); + newpart->unprotect (); + } } Input * @@ -87,6 +99,7 @@ Book::mark_smob (SCM s) if (book->paper_) scm_gc_mark (book->paper_->self_scm ()); scm_gc_mark (book->scores_); + scm_gc_mark (book->bookparts_); scm_gc_mark (book->input_location_); return book->header_; @@ -105,62 +118,166 @@ Book::add_score (SCM s) scores_ = scm_cons (s, scores_); } +void +Book::set_parent (Book *parent) +{ + if (!paper_) + { + paper_ = new Output_def (); + paper_->unprotect (); + } + paper_->parent_ = parent->paper_; + /* If this part is the first child of parent, copy its header */ + if (ly_is_module (parent->header_) && (scm_is_null (parent->bookparts_))) + { + SCM tmp_header = ly_make_anonymous_module (false); + ly_module_copy (tmp_header, parent->header_); + if (ly_is_module (header_)) + ly_module_copy (tmp_header, header_); + header_ = tmp_header; + } +} -/* Concatenate all score outputs into a Paper_book +/* Before an explicit \bookpart is encountered, scores are added to the book. + * But once a bookpart is added, the previous scores shall be collected into + * a new bookpart. */ -Paper_book * -Book::process (Output_def *default_paper, - Output_def *default_layout) +void +Book::add_scores_to_bookpart () +{ + if (scm_is_pair (scores_)) + { + /* If scores have been added to this book, add them to a child + * book part */ + Book *part = new Book; + part->set_parent (this); + part->scores_ = scores_; + bookparts_ = scm_cons (part->self_scm (), bookparts_); + part->unprotect (); + scores_ = SCM_EOL; + } +} + +void +Book::add_bookpart (SCM b) +{ + add_scores_to_bookpart (); + Book *part = unsmob_book (b); + part->set_parent (this); + bookparts_ = scm_cons (b, bookparts_); +} + +bool +Book::error_found () { for (SCM s = scores_; scm_is_pair (s); s = scm_cdr (s)) if (Score *score = unsmob_score (scm_car (s))) if (score->error_found_) - return 0; + return true; + + for (SCM part = bookparts_; scm_is_pair (part); part = scm_cdr (part)) + if (Book *bookpart = unsmob_book (scm_car (part))) + if (bookpart->error_found ()) + return true; + + return false; +} + +Paper_book * +Book::process (Output_def *default_paper, + Output_def *default_layout) +{ + return process (default_paper, default_layout, 0); +} + +void +Book::process_bookparts (Paper_book *output_paper_book, Output_def *paper, Output_def *layout) +{ + add_scores_to_bookpart (); + for (SCM p = scm_reverse (bookparts_); scm_is_pair (p); p = scm_cdr (p)) + { + if (Book *book = unsmob_book (scm_car (p))) + { + Paper_book *paper_book_part = book->process (paper, layout, output_paper_book); + if (paper_book_part) + output_paper_book->add_bookpart (paper_book_part->self_scm ()); + } + } +} +void +Book::process_score (SCM s, Paper_book *output_paper_book, Output_def *layout) +{ + if (Score *score = unsmob_score (scm_car (s))) + { + SCM outputs = score + ->book_rendering (output_paper_book->paper_, layout); + + while (scm_is_pair (outputs)) + { + Music_output *output = unsmob_music_output (scm_car (outputs)); + + if (Performance *perf = dynamic_cast (output)) + output_paper_book->add_performance (perf->self_scm ()); + else if (Paper_score *pscore = dynamic_cast (output)) + { + if (ly_is_module (score->get_header ())) + output_paper_book->add_score (score->get_header ()); + output_paper_book->add_score (pscore->self_scm ()); + } + + outputs = scm_cdr (outputs); + } + } + else if (Text_interface::is_markup_list (scm_car (s)) + || unsmob_page_marker (scm_car (s))) + output_paper_book->add_score (scm_car (s)); + else + assert (0); + +} + +/* Concatenate all score or book part outputs into a Paper_book + */ +Paper_book * +Book::process (Output_def *default_paper, + Output_def *default_layout, + Paper_book *parent_part) +{ Output_def *paper = paper_ ? paper_ : default_paper; + + /* If top book, recursively check score errors */ + if (!parent_part && error_found ()) + return 0; + if (!paper) return 0; - + Paper_book *paper_book = new Paper_book (); Real scale = scm_to_double (paper->c_variable ("output-scale")); Output_def *scaled_bookdef = scale_output_def (paper, scale); - paper_book->paper_ = scaled_bookdef; - scaled_bookdef->unprotect (); - + if (parent_part) + { + paper_book->parent_ = parent_part; + paper_book->paper_->parent_ = parent_part->paper_; + } paper_book->header_ = header_; - /* Render in order of parsing. */ - for (SCM s = scm_reverse (scores_); scm_is_pair (s); s = scm_cdr (s)) + if (scm_is_pair (bookparts_)) { - if (Score *score = unsmob_score (scm_car (s))) - { - SCM outputs = score - ->book_rendering (paper_book->paper_, default_layout); - - while (scm_is_pair (outputs)) - { - Music_output *output = unsmob_music_output (scm_car (outputs)); - - if (Performance *perf = dynamic_cast (output)) - paper_book->add_performance (perf->self_scm ()); - else if (Paper_score *pscore = dynamic_cast (output)) - { - if (ly_is_module (score->get_header ())) - paper_book->add_score (score->get_header ()); - paper_book->add_score (pscore->self_scm ()); - } - - outputs = scm_cdr (outputs); - } - } - else if (Text_interface::is_markup_list (scm_car (s)) - || unsmob_page_marker (scm_car (s))) - paper_book->add_score (scm_car (s)); - else - assert (0); + /* Process children book parts */ + process_bookparts (paper_book, paper, default_layout); + } + else + { + /* Process scores */ + /* Render in order of parsing. */ + for (SCM s = scm_reverse (scores_); scm_is_pair (s); s = scm_cdr (s)) + { + process_score (s, paper_book, default_layout); + } } return paper_book; } - diff --git a/lily/include/book.hh b/lily/include/book.hh index cf47843238..a29cb07449 100644 --- a/lily/include/book.hh +++ b/lily/include/book.hh @@ -24,6 +24,7 @@ public: SCM header_; Output_def *paper_; SCM scores_; + SCM bookparts_; SCM input_location_; Book (Book const &); @@ -31,9 +32,24 @@ public: VIRTUAL_COPY_CONSTRUCTOR(Book, Book); Book (); void add_score (SCM); + void add_bookpart (SCM); Paper_book *process (Output_def *def_paper, Output_def *def_layout); + Paper_book *process (Output_def *default_paper, + Output_def *default_layout, + Paper_book *parent_part); void set_keys (); + +protected: + void set_parent (Book *parent); + void add_scores_to_bookpart (); + bool error_found (); + void process_score (SCM score, + Paper_book *output_paper_book, + Output_def *layout); + void process_bookparts (Paper_book *output_paper_book, + Output_def *paper, + Output_def *layout); }; DECLARE_UNSMOB (Book, book); diff --git a/lily/include/lily-parser.hh b/lily/include/lily-parser.hh index ed4673de78..c73f8f7991 100644 --- a/lily/include/lily-parser.hh +++ b/lily/include/lily-parser.hh @@ -74,6 +74,9 @@ SCM ly_parser_scorify (SCM, SCM); Output_def *get_layout (Lily_parser *parser); Output_def *get_midi (Lily_parser *parser); Output_def *get_paper (Lily_parser *parser); +void stack_paper (Lily_parser *parser, Output_def *paper); +void unstack_paper (Lily_parser *parser); +void set_paper (Lily_parser *parser, Output_def *paper); SCM get_header (Lily_parser *parser); #endif /* LILY_PARSER_HH */ diff --git a/lily/include/paper-book.hh b/lily/include/paper-book.hh index 4ea1927946..d06f8fdd7c 100644 --- a/lily/include/paper-book.hh +++ b/lily/include/paper-book.hh @@ -31,11 +31,16 @@ public: SCM header_; SCM header_0_; SCM scores_; + SCM bookparts_; + Paper_book *parent_; Output_def *paper_; Paper_book (); + Output_def *top_paper (); + void add_score (SCM); + void add_bookpart (SCM); void add_performance (SCM); SCM performances () const; @@ -49,6 +54,14 @@ public: void classic_output (SCM output_channel); void output (SCM output_channel); + +protected: + void classic_output_aux (SCM output, + int *first_performance_number); + void output_aux (SCM output_channel, + bool is_last, + int *first_page_number, + int *first_performance_number); }; DECLARE_UNSMOB (Paper_book, paper_book) diff --git a/lily/lily-lexer.cc b/lily/lily-lexer.cc index 7b77d58922..374eed4ec8 100644 --- a/lily/lily-lexer.cc +++ b/lily/lily-lexer.cc @@ -31,6 +31,7 @@ static Keyword_ent the_key_tab[] {"alias", ALIAS}, {"alternative", ALTERNATIVE}, {"book", BOOK}, + {"bookpart", BOOKPART}, {"change", CHANGE}, {"chordmode", CHORDMODE}, {"chords", CHORDS}, diff --git a/lily/lily-parser.cc b/lily/lily-parser.cc index 13d62ebc5c..7e41491665 100644 --- a/lily/lily-parser.cc +++ b/lily/lily-parser.cc @@ -221,17 +221,46 @@ get_midi (Lily_parser *parser) return layout; } +/* Return a copy of the top of $papers stack, or $defaultpaper if the + * stack is empty */ Output_def * get_paper (Lily_parser *parser) { - SCM id = parser->lexer_->lookup_identifier ("$defaultpaper"); - Output_def *layout = unsmob_output_def (id); + SCM papers = parser->lexer_->lookup_identifier ("$papers"); + Output_def *layout = ((papers == SCM_UNDEFINED) || scm_is_null (papers)) ? + 0 : unsmob_output_def (scm_car (papers)); + SCM default_paper = parser->lexer_->lookup_identifier ("$defaultpaper"); + layout = layout ? layout : unsmob_output_def (default_paper); layout = layout ? dynamic_cast (layout->clone ()) : new Output_def; layout->set_variable (ly_symbol2scm ("is-paper"), SCM_BOOL_T); return layout; } +/* Stack a paper on top of $papers */ +void +stack_paper (Lily_parser *parser, Output_def *paper) +{ + parser->lexer_->set_identifier (ly_symbol2scm ("$papers"), + scm_cons (paper->self_scm (), + parser->lexer_->lookup_identifier ("$papers"))); +} + +/* Unstack a paper from $papers */ +void +unstack_paper (Lily_parser *parser) +{ + parser->lexer_->set_identifier (ly_symbol2scm ("$papers"), + scm_cdr (parser->lexer_->lookup_identifier ("$papers"))); +} + +/* Change the paper on top of $papers */ +void +set_paper (Lily_parser *parser, Output_def *paper) +{ + scm_set_car_x (parser->lexer_->lookup_identifier ("$papers"), paper->self_scm ()); +} + SCM get_header (Lily_parser *parser) { diff --git a/lily/page-breaking.cc b/lily/page-breaking.cc index 1ae769e82d..a111d933e5 100644 --- a/lily/page-breaking.cc +++ b/lily/page-breaking.cc @@ -212,6 +212,7 @@ Page_breaking::systems () Real Page_breaking::page_height (int page_num, bool last) const { + bool last_part = ly_scm2bool (book_->paper_->c_variable ("is-last-bookpart")); SCM mod = scm_c_resolve_module ("scm page"); SCM calc_height = scm_c_module_lookup (mod, "calc-printable-height"); SCM make_page = scm_c_module_lookup (mod, "make-page"); @@ -222,7 +223,8 @@ Page_breaking::page_height (int page_num, bool last) const SCM page = scm_apply_0 (make_page, scm_list_n ( book_->self_scm (), ly_symbol2scm ("page-number"), scm_from_int (page_num), - ly_symbol2scm ("is-last"), scm_from_bool (last), + ly_symbol2scm ("is-last-bookpart"), scm_from_bool (last_part), + ly_symbol2scm ("is-bookpart-last-page"), scm_from_bool (last), SCM_UNDEFINED)); SCM height = scm_apply_1 (calc_height, page, SCM_EOL); return scm_to_double (height) - page_top_space_; @@ -254,21 +256,24 @@ 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); + bool last_bookpart = ly_scm2bool (book_->paper_->c_variable ("is-last-bookpart")); SCM ret = SCM_EOL; - SCM label_page_table = SCM_EOL; + SCM label_page_table = book_->top_paper ()->c_variable ("label-page-table"); + if (label_page_table == SCM_UNDEFINED) + label_page_table = SCM_EOL; for (vsize i = 0; i < lines_per_page.size (); i++) { SCM page_num = scm_from_int (i + first_page_number); - SCM last = scm_from_bool (i == lines_per_page.size () - 1); - SCM rag = scm_from_bool (ragged () || (to_boolean (last) - && ragged_last ())); + bool partbook_last_page = (i == lines_per_page.size () - 1); + SCM rag = scm_from_bool (ragged () || ( partbook_last_page && ragged_last ())); SCM line_count = scm_from_int (lines_per_page[i]); SCM lines = scm_list_head (systems, line_count); SCM page = scm_apply_0 (make_page, - scm_list_n (book, lines, page_num, - rag, last, SCM_UNDEFINED)); - + scm_list_n (book, lines, page_num, rag, + scm_from_bool (last_bookpart), + scm_from_bool (partbook_last_page), + SCM_UNDEFINED)); /* collect labels */ for (SCM l = lines ; scm_is_pair (l) ; l = scm_cdr (l)) { @@ -290,7 +295,7 @@ Page_breaking::make_pages (vector lines_per_page, SCM systems) ret = scm_cons (page, ret); systems = scm_list_tail (systems, line_count); } - book_->paper_->set_variable (ly_symbol2scm ("label-page-table"), label_page_table); + book_->top_paper ()->set_variable (ly_symbol2scm ("label-page-table"), label_page_table); ret = scm_reverse (ret); return ret; } diff --git a/lily/paper-book-scheme.cc b/lily/paper-book-scheme.cc index 275e3ced53..d9832e4c0e 100644 --- a/lily/paper-book-scheme.cc +++ b/lily/paper-book-scheme.cc @@ -20,12 +20,16 @@ LY_DEFINE (ly_paper_book_pages, "ly:paper-book-pages", LY_DEFINE (ly_paper_book_scopes, "ly:paper-book-scopes", 1, 0, 0, (SCM book), - "Return pages in layout book @var{book}.") + "Return scopes in layout book @var{book}.") { LY_ASSERT_SMOB (Paper_book, book, 1); Paper_book *pb = unsmob_paper_book (book); SCM scopes = SCM_EOL; + if (pb->parent_) + { + scopes = ly_paper_book_scopes (pb->parent_->self_scm ()); + } if (ly_is_module (pb->header_)) scopes = scm_cons (pb->header_, scopes); diff --git a/lily/paper-book.cc b/lily/paper-book.cc index 020c2e7b21..069fca16fd 100644 --- a/lily/paper-book.cc +++ b/lily/paper-book.cc @@ -27,10 +27,12 @@ Paper_book::Paper_book () header_0_ = SCM_EOL; pages_ = SCM_BOOL_F; scores_ = SCM_EOL; + bookparts_ = SCM_EOL; performances_ = SCM_EOL; systems_ = SCM_BOOL_F; paper_ = 0; + parent_ = 0; smobify_self (); } @@ -48,11 +50,14 @@ Paper_book::mark_smob (SCM smob) Paper_book *b = (Paper_book *) SCM_CELL_WORD_1 (smob); if (b->paper_) scm_gc_mark (b->paper_->self_scm ()); + if (b->parent_) + scm_gc_mark (b->parent_->self_scm ()); scm_gc_mark (b->header_); scm_gc_mark (b->header_0_); scm_gc_mark (b->pages_); scm_gc_mark (b->performances_); scm_gc_mark (b->scores_); + scm_gc_mark (b->bookparts_); return b->systems_; } @@ -65,6 +70,15 @@ Paper_book::print_smob (SCM smob, SCM port, scm_print_state*) return 1; } +Output_def * +Paper_book::top_paper () +{ + Output_def *paper = paper_; + while (paper->parent_) + paper = paper->parent_; + return paper; +} + SCM dump_fields () { @@ -82,6 +96,12 @@ Paper_book::add_score (SCM s) scores_ = scm_cons (s, scores_); } +void +Paper_book::add_bookpart (SCM p) +{ + bookparts_ = scm_cons (p, bookparts_); +} + void Paper_book::add_performance (SCM s) { @@ -89,20 +109,56 @@ Paper_book::add_performance (SCM s) } void -Paper_book::output (SCM output_channel) +Paper_book::output_aux (SCM output_channel, + bool is_last, + int *first_page_number, + int *first_performance_number) { if (scm_is_pair (performances_)) { SCM proc = ly_lily_module_constant ("write-performances-midis"); - scm_call_2 (proc, performances (), output_channel); + scm_call_3 (proc, + performances (), + output_channel, + scm_long2num (*first_performance_number)); + *first_performance_number += scm_ilength (performances_); } - if (scores_ == SCM_EOL) - return; + if (scm_is_pair (bookparts_)) + { + for (SCM p = scm_reverse (bookparts_); scm_is_pair (p); p = scm_cdr (p)) + if (Paper_book *pbookpart = unsmob_paper_book (scm_car (p))) + { + bool is_last_part = (is_last && !scm_is_pair (scm_cdr (p))); + pbookpart->output_aux (output_channel, + is_last_part, + first_page_number, + first_performance_number); + } + } + else + { + if (scores_ == SCM_EOL) + return; + paper_->set_variable (ly_symbol2scm ("first-page-number"), + scm_long2num (*first_page_number)); + paper_->set_variable (ly_symbol2scm ("is-last-bookpart"), + ly_bool2scm (is_last)); + /* Generate all stencils to trigger font loads. */ + *first_page_number += scm_ilength (pages ()); + } +} - /* Generate all stencils to trigger font loads. */ - pages (); +void +Paper_book::output (SCM output_channel) +{ + int first_page_number = robust_scm2int (paper_->c_variable ("first-page-number"), 1); + int first_performance_number = 0; + output_aux (output_channel, + true, + &first_page_number, + &first_performance_number); SCM scopes = SCM_EOL; if (ly_is_module (header_)) @@ -137,17 +193,28 @@ Paper_book::output (SCM output_channel) } void -Paper_book::classic_output (SCM output) +Paper_book::classic_output_aux (SCM output, + int *first_performance_number) { if (scm_is_pair (performances_)) { SCM proc = ly_lily_module_constant ("write-performances-midis"); - - scm_call_2 (proc, performances (), output); + scm_call_3 (proc, + performances (), + output, + scm_long2num (*first_performance_number)); + *first_performance_number += scm_ilength (performances_); } /* Generate all stencils to trigger font loads. */ systems (); +} + +void +Paper_book::classic_output (SCM output) +{ + int first_performance_number = 0; + classic_output_aux (output, &first_performance_number); SCM scopes = SCM_EOL; if (ly_is_module (header_)) @@ -328,7 +395,7 @@ Paper_book::get_system_specs () SCM props = paper_->lookup_variable (ly_symbol2scm ("book-title-properties")); Prob *ps = make_paper_system (props); paper_system_set_stencil (ps, title); - + system_specs = scm_cons (ps->self_scm (), system_specs); ps->unprotect (); } @@ -448,45 +515,53 @@ Paper_book::systems () return systems_; systems_ = SCM_EOL; - SCM specs = get_system_specs (); - for (SCM s = specs; scm_is_pair (s); s = scm_cdr (s)) + if (scm_is_pair (bookparts_)) { - if (Paper_score *pscore = dynamic_cast (unsmob_music_output (scm_car (s)))) - { - SCM system_list = scm_vector_to_list (pscore->get_paper_systems ()); - system_list = scm_reverse (system_list); - systems_ = scm_append (scm_list_2 (system_list, systems_)); - } - else - { - systems_ = scm_cons (scm_car (s), systems_); - } + for (SCM p = scm_reverse (bookparts_); scm_is_pair (p); p = scm_cdr (p)) + if (Paper_book *pbookpart = unsmob_paper_book (scm_car (p))) + systems_ = scm_append_x (scm_list_2 (systems_, pbookpart->systems ())); } - - systems_ = scm_reverse (systems_); - - /* backwards compatibility for the old page breaker */ - int i = 0; - Prob *last = 0; - for (SCM s = systems_; scm_is_pair (s); s = scm_cdr (s)) + else { - Prob *ps = unsmob_prob (scm_car (s)); - ps->set_property ("number", scm_from_int (++i)); - - if (last - && to_boolean (last->get_property ("is-title")) - && !scm_is_number (ps->get_property ("penalty"))) - ps->set_property ("penalty", scm_from_int (10000)); - last = ps; + SCM specs = get_system_specs (); + for (SCM s = specs; scm_is_pair (s); s = scm_cdr (s)) + { + if (Paper_score *pscore = dynamic_cast (unsmob_music_output (scm_car (s)))) + { + SCM system_list = scm_vector_to_list (pscore->get_paper_systems ()); + system_list = scm_reverse (system_list); + systems_ = scm_append (scm_list_2 (system_list, systems_)); + } + else + { + systems_ = scm_cons (scm_car (s), systems_); + } + } + systems_ = scm_reverse (systems_); - if (scm_is_pair (scm_cdr (s))) + /* backwards compatibility for the old page breaker */ + int i = 0; + Prob *last = 0; + for (SCM s = systems_; scm_is_pair (s); s = scm_cdr (s)) { - SCM perm = ps->get_property ("page-break-permission"); - Prob *next = unsmob_prob (scm_cadr (s)); - if (perm == SCM_EOL) - next->set_property ("penalty", scm_from_int (10001)); - else if (perm == ly_symbol2scm ("force")) - next->set_property ("penalty", scm_from_int (-10001)); + Prob *ps = unsmob_prob (scm_car (s)); + ps->set_property ("number", scm_from_int (++i)); + + if (last + && to_boolean (last->get_property ("is-title")) + && !scm_is_number (ps->get_property ("penalty"))) + ps->set_property ("penalty", scm_from_int (10000)); + last = ps; + + if (scm_is_pair (scm_cdr (s))) + { + SCM perm = ps->get_property ("page-break-permission"); + Prob *next = unsmob_prob (scm_cadr (s)); + if (perm == SCM_EOL) + next->set_property ("penalty", scm_from_int (10001)); + else if (perm == ly_symbol2scm ("force")) + next->set_property ("penalty", scm_from_int (-10001)); + } } } @@ -500,22 +575,29 @@ Paper_book::pages () return pages_; pages_ = SCM_EOL; - SCM proc = paper_->c_variable ("page-breaking-wrapper"); - pages_ = scm_apply_0 (proc, scm_list_1 (self_scm ())); - - /* set systems_ from the pages */ - if (systems_ == SCM_BOOL_F) + if (scm_is_pair (bookparts_)) { - systems_ = SCM_EOL; - for (SCM p = pages_; scm_is_pair (p); p = scm_cdr (p)) - { - Prob *page = unsmob_prob (scm_car (p)); - SCM systems = page->get_property ("lines"); + for (SCM p = scm_reverse (bookparts_); scm_is_pair (p); p = scm_cdr (p)) + if (Paper_book *pbookpart = unsmob_paper_book (scm_car (p))) + pages_ = scm_append_x (scm_list_2 (pages_, pbookpart->pages ())); + } + else + { + SCM proc = paper_->c_variable ("page-breaking-wrapper"); + pages_ = scm_apply_0 (proc, scm_list_1 (self_scm ())); - systems_ = scm_append (scm_list_2 (systems_, systems)); + /* set systems_ from the pages */ + if (systems_ == SCM_BOOL_F) + { + systems_ = SCM_EOL; + for (SCM p = pages_; scm_is_pair (p); p = scm_cdr (p)) + { + Prob *page = unsmob_prob (scm_car (p)); + SCM systems = page->get_property ("lines"); + systems_ = scm_append (scm_list_2 (systems_, systems)); + } } } - return pages_; } diff --git a/lily/parser.yy b/lily/parser.yy index be4e7e26e5..2e0887f954 100644 --- a/lily/parser.yy +++ b/lily/parser.yy @@ -159,6 +159,7 @@ void set_music_properties (Music *p, SCM a); %token ALIAS "\\alias" %token ALTERNATIVE "\\alternative" %token BOOK "\\book" +%token BOOKPART "\\bookpart" %token CHANGE "\\change" %token CHORDMODE "\\chordmode" %token CHORDS "\\chords" @@ -306,6 +307,8 @@ If we give names, Bison complains. %type book_block %type book_body +%type bookpart_block +%type bookpart_body %type bare_unsigned %type figured_bass_alteration @@ -474,6 +477,12 @@ toplevel_expression: scm_call_2 (proc, PARSER->self_scm (), book->self_scm ()); book->unprotect (); } + | bookpart_block { + Book *bookpart = $1; + SCM proc = PARSER->lexer_->lookup_identifier ("toplevel-bookpart-handler"); + scm_call_2 (proc, PARSER->self_scm (), bookpart->self_scm ()); + bookpart->unprotect (); + } | score_block { Score *score = $1; @@ -565,6 +574,10 @@ identifier_init: $$ = $1->self_scm (); $1->unprotect (); } + | bookpart_block { + $$ = $1->self_scm (); + $1->unprotect (); + } | output_def { $$ = $1->self_scm (); $1->unprotect (); @@ -641,6 +654,7 @@ context_def_spec_body: book_block: BOOK '{' book_body '}' { $$ = $3; + unstack_paper (PARSER); } ; @@ -653,6 +667,7 @@ book_body: $$->origin ()->set_spot (@$); $$->paper_ = dynamic_cast (unsmob_output_def (PARSER->lexer_->lookup_identifier ("$defaultpaper"))->clone ()); $$->paper_->unprotect (); + stack_paper (PARSER, $$->paper_); $$->header_ = PARSER->lexer_->lookup_identifier ("$defaultheader"); } | BOOK_IDENTIFIER { @@ -663,6 +678,13 @@ book_body: | book_body paper_block { $$->paper_ = $2; $2->unprotect (); + set_paper (PARSER, $2); + } + | book_body bookpart_block { + Book *bookpart = $2; + SCM proc = PARSER->lexer_->lookup_identifier ("book-bookpart-handler"); + scm_call_2 (proc, $$->self_scm (), bookpart->self_scm ()); + bookpart->unprotect (); } | book_body score_block { Score *score = $2; @@ -689,12 +711,64 @@ book_body: | book_body error { $$->paper_ = 0; $$->scores_ = SCM_EOL; + $$->bookparts_ = SCM_EOL; } | book_body object_id_setting { $$->user_key_ = ly_scm2string ($2); } ; +bookpart_block: + BOOKPART '{' bookpart_body '}' { + $$ = $3; + } + ; + +bookpart_body: + { + $$ = new Book; + $$->origin ()->set_spot (@$); + } + | BOOK_IDENTIFIER { + $$ = unsmob_book ($1); + $$->protect (); + $$->origin ()->set_spot (@$); + } + | bookpart_body paper_block { + $$->paper_ = $2; + $2->unprotect (); + } + | bookpart_body score_block { + Score *score = $2; + SCM proc = PARSER->lexer_->lookup_identifier ("bookpart-score-handler"); + scm_call_2 (proc, $$->self_scm (), score->self_scm ()); + score->unprotect (); + } + | bookpart_body composite_music { + Music *music = unsmob_music ($2); + SCM proc = PARSER->lexer_->lookup_identifier ("bookpart-music-handler"); + scm_call_3 (proc, PARSER->self_scm (), $$->self_scm (), music->self_scm ()); + } + | bookpart_body full_markup { + SCM proc = PARSER->lexer_->lookup_identifier ("bookpart-text-handler"); + scm_call_2 (proc, $$->self_scm (), scm_list_1 ($2)); + } + | bookpart_body full_markup_list { + SCM proc = PARSER->lexer_->lookup_identifier ("bookpart-text-handler"); + scm_call_2 (proc, $$->self_scm (), $2); + } + | bookpart_body lilypond_header { + $$->header_ = $2; + } + | bookpart_body error { + $$->paper_ = 0; + $$->scores_ = SCM_EOL; + } + | bookpart_body object_id_setting { + $$->user_key_ = ly_scm2string ($2); + } + ; + score_block: SCORE '{' score_body '}' { $$ = $3; diff --git a/ly/declarations-init.ly b/ly/declarations-init.ly index 661c7f8854..2e2f46ecb6 100644 --- a/ly/declarations-init.ly +++ b/ly/declarations-init.ly @@ -111,13 +111,18 @@ setDefaultDurationToQuarter = { c4 } #(define musicQuotes (make-hash-table 29)) #(define toplevel-book-handler print-book-with-defaults) +#(define toplevel-bookpart-handler collect-bookpart-for-book) #(define toplevel-music-handler collect-music-for-book) #(define toplevel-score-handler collect-scores-for-book) #(define toplevel-text-handler collect-scores-for-book) +#(define book-bookpart-handler ly:book-add-bookpart!) #(define book-music-handler collect-book-music-for-book) #(define book-score-handler ly:book-add-score!) #(define book-text-handler ly:book-add-score!) +#(define bookpart-score-handler ly:book-add-score!) +#(define bookpart-text-handler ly:book-add-score!) +#(define bookpart-music-handler collect-book-music-for-book) \include "predefined-fretboards-init.ly" diff --git a/ly/init.ly b/ly/init.ly index a4bd1c3a39..5a2103e83c 100644 --- a/ly/init.ly +++ b/ly/init.ly @@ -10,7 +10,8 @@ #(ly:set-option 'old-relative #f) -#(define toplevel-scores '()) +#(define toplevel-scores (list)) +#(define toplevel-bookparts (list)) #(define output-count 0) #(define $defaultheader #f) #(define version-seen #f) @@ -35,13 +36,26 @@ (version-not-seen-message input-file-name)) #(ly:set-option 'protected-scheme-parsing #f) -#(if (or (pair? toplevel-scores) output-empty-score-list) - ((if (defined? 'default-toplevel-book-handler) - default-toplevel-book-handler - toplevel-book-handler) - parser - (apply ly:make-book $defaultpaper $defaultheader toplevel-scores))) +#(let ((book-handler (if (defined? 'default-toplevel-book-handler) + default-toplevel-book-handler + toplevel-book-handler))) + (cond ((pair? toplevel-bookparts) + (let ((book (ly:make-book $defaultpaper $defaultheader))) + (map (lambda (part) + (ly:book-add-bookpart! book part)) + (reverse! toplevel-bookparts)) + (set! toplevel-bookparts (list)) + ;; if scores have been defined after the last explicit \bookpart: + (if (pair? toplevel-scores) + (map (lambda (score) + (ly:book-add-score! book score)) + (reverse! toplevel-scores))) + (set! toplevel-scores (list)) + (book-handler parser book))) + ((or (pair? toplevel-scores) output-empty-score-list) + (book-handler parser (apply ly:make-book $defaultpaper + $defaultheader toplevel-scores))))) #(if (eq? expect-error (ly:parser-has-error? parser)) (ly:parser-clear-error parser) diff --git a/ly/titling-init.ly b/ly/titling-init.ly index 9ef8cd48dd..08b23d469d 100644 --- a/ly/titling-init.ly +++ b/ly/titling-init.ly @@ -69,22 +69,48 @@ scoreTitleMarkup = \markup { \column { } } +%% Book first page and last page predicates #(define (first-page layout props arg) + (define (ancestor layout) + "Return the topmost layout ancestor" + (let ((parent (ly:output-def-parent layout))) + (if (not (ly:output-def? parent)) + layout + (ancestor parent)))) (if (= (chain-assoc-get 'page:page-number props -1) - (ly:output-def-lookup layout 'first-page-number)) + (ly:output-def-lookup (ancestor layout) 'first-page-number)) (interpret-markup layout props arg) empty-stencil)) #(define (last-page layout props arg) - (if (chain-assoc-get 'page:last? props #f) - (interpret-markup layout props arg) - empty-stencil)) + (if (and (chain-assoc-get 'page:is-bookpart-last-page props #f) + (chain-assoc-get 'page:is-last-bookpart props #f)) + (interpret-markup layout props arg) + empty-stencil)) #(define (not-first-page layout props arg) + (define (ancestor layout) + "Return the topmost layout ancestor" + (let ((parent (ly:output-def-parent layout))) + (if (not (ly:output-def? parent)) + layout + (ancestor parent)))) (if (not (= (chain-assoc-get 'page:page-number props -1) - (ly:output-def-lookup layout 'first-page-number))) - (interpret-markup layout props arg) - empty-stencil)) + (ly:output-def-lookup (ancestor layout) 'first-page-number))) + (interpret-markup layout props arg) + empty-stencil)) + +%% Bookpart first page and last page predicates +#(define (part-first-page layout props arg) + (if (= (chain-assoc-get 'page:page-number props -1) + (ly:output-def-lookup layout 'first-page-number)) + (interpret-markup layout props arg) + empty-stencil)) + +#(define (part-last-page layout props arg) + (if (chain-assoc-get 'page:is-bookpart-last-page props #f) + (interpret-markup layout props arg) + empty-stencil)) %% unused #(define (not-single-page layout props arg) diff --git a/scm/layout-page-layout.scm b/scm/layout-page-layout.scm index 1686d9613a..00add35806 100644 --- a/scm/layout-page-layout.scm +++ b/scm/layout-page-layout.scm @@ -31,7 +31,8 @@ (= (interval-start system-extent) (interval-end system-extent)))))) -(define (stretch-and-draw-page paper-book systems page-number ragged last) +(define (stretch-and-draw-page paper-book systems page-number ragged + is-last-bookpart is-bookpart-last-page) (define (max-stretch sys) (if (ly:grob? sys) (ly:grob-property sys 'max-stretch) @@ -78,7 +79,8 @@ (let* ((page (make-page paper-book 'page-number page-number - 'is-last last)) + 'is-last-bookpart is-last-bookpart + 'is-bookpart-last-page is-bookpart-last-page)) (paper (ly:paper-book-paper paper-book)) (height (page-printable-height page)) ; there is a certain amount of impreciseness going on here: @@ -351,8 +353,8 @@ is what have collected so far, and has ascending page numbers." inter-system-space)) user))) -(define (walk-paths done-lines best-paths current-lines last current-best - paper-book page-alist) +(define (walk-paths done-lines best-paths current-lines is-last-bookpart + is-bookpart-last-page current-best paper-book page-alist) "Return the best optimal-page-break-node that contains CURRENT-LINES. DONE-LINES.reversed ++ CURRENT-LINES is a consecutive ascending range of lines, and BEST-PATHS contains the optimal breaks @@ -362,18 +364,19 @@ CURRENT-BEST is the best result sofar, or #f." (let* ((paper (ly:paper-book-paper paper-book)) (this-page (make-page paper-book - 'is-last last + 'is-last-bookpart is-last-bookpart + 'is-bookpart-last-page is-bookpart-last-page 'page-number (if (null? best-paths) (ly:output-def-lookup paper 'first-page-number) (1+ (page-page-number (first best-paths)))))) (ragged-all (eq? #t (ly:output-def-lookup paper 'ragged-bottom))) (ragged-last (eq? #t (ly:output-def-lookup paper 'ragged-last-bottom))) - (ragged (or ragged-all (and ragged-last last))) + (ragged (or ragged-all (and ragged-last is-bookpart-last-page))) (space-to-fill (page-maximum-space-to-fill this-page current-lines paper)) (vertical-spacing (space-systems space-to-fill current-lines ragged paper #f)) (satisfied-constraints (car vertical-spacing)) (force (if satisfied-constraints - (if (and last ragged-last) + (if (and is-bookpart-last-page ragged-last) 0.0 satisfied-constraints) 10000)) @@ -411,7 +414,7 @@ CURRENT-BEST is the best result sofar, or #f." (list "\nuser pen " user-penalty "\nsatisfied-constraints" satisfied-constraints - "\nlast? " last "ragged?" ragged + "\nlast? " is-bookpart-last-page "ragged?" ragged "\nis-better " is-better " total-penalty " total-penalty "\n" "\nconfig " positions "\nforce " force @@ -427,11 +430,11 @@ CURRENT-BEST is the best result sofar, or #f." satisfied-constraints) (walk-paths (cdr done-lines) (cdr best-paths) (cons (car done-lines) current-lines) - last new-best + is-last-bookpart is-bookpart-last-page new-best paper-book page-alist) new-best))) -(define (walk-lines done best-paths todo paper-book page-alist) +(define (walk-lines done best-paths todo paper-book page-alist is-last-bookpart) "Return the best page breaking as a single page node for optimally breaking TODO ++ DONE.reversed. BEST-PATHS is a list of break nodes corresponding to @@ -439,14 +442,15 @@ DONE." (if (null? todo) (car best-paths) (let* ((this-line (car todo)) - (last (null? (cdr todo))) - (next (walk-paths done best-paths (list this-line) last #f - paper-book page-alist))) + (is-bookpart-last-page (null? (cdr todo))) + (next (walk-paths done best-paths (list this-line) is-last-bookpart + is-bookpart-last-page #f paper-book page-alist))) (walk-lines (cons this-line done) (cons next best-paths) (cdr todo) paper-book - page-alist)))) + page-alist + is-last-bookpart)))) (define-public (optimal-page-breaks paper-book) "Return pages as a list starting with 1st page. Each page is a 'page Prob." @@ -454,11 +458,11 @@ DONE." (lines (ly:paper-book-systems paper-book)) (page-alist (layout->page-init paper)) (force-equalization-factor (ly:output-def-lookup - paper 'verticalequalizationfactor 0.3))) + paper 'verticalequalizationfactor 0.3)) + (is-last-bookpart (ly:output-def-lookup paper 'is-last-bookpart))) (ly:message (_ "Calculating page breaks...")) - (let* ((best-break-node (walk-lines '() '() lines paper-book page-alist)) + (let* ((best-break-node (walk-lines '() '() lines paper-book page-alist is-last-bookpart)) (break-nodes (get-path best-break-node '()))) - (page-set-property! (car (last-pair break-nodes)) 'is-last #t) (if #f; (ly:get-option 'verbose) (begin (display (list diff --git a/scm/lily-library.scm b/scm/lily-library.scm index f772d0527e..58c8720d84 100644 --- a/scm/lily-library.scm +++ b/scm/lily-library.scm @@ -56,14 +56,28 @@ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; parser <-> output hooks. - +(define-public (collect-bookpart-for-book parser book-part) + "Toplevel book-part handler" + (define (add-bookpart book-part) + (ly:parser-define! + parser 'toplevel-bookparts + (cons book-part (ly:parser-lookup parser 'toplevel-bookparts)))) + ;; If toplevel scores have been found before this \bookpart, + ;; add them first to a dedicated bookpart + (if (pair? (ly:parser-lookup parser 'toplevel-scores)) + (begin + (add-bookpart (ly:make-book-part + (ly:parser-lookup parser 'toplevel-scores))) + (ly:parser-define! parser 'toplevel-scores (list)))) + (add-bookpart book-part)) + (define-public (collect-scores-for-book parser score) (ly:parser-define! parser 'toplevel-scores (cons score (ly:parser-lookup parser 'toplevel-scores)))) -(define (collect-music-aux score-handler parser music) +(define-public (collect-music-aux score-handler parser music) (define (music-property symbol) (let ((value (ly:music-property music symbol))) (if (not (null? value)) diff --git a/scm/midi.scm b/scm/midi.scm index 5d3277d820..a5c1bd338d 100644 --- a/scm/midi.scm +++ b/scm/midi.scm @@ -284,14 +284,12 @@ returns the program of the instrument ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; -(define-public (write-performances-midis performances basename) +(define-public (write-performances-midis performances basename . rest) (let ((midi-ext (ly:get-option 'midi-extension))) (let loop ((perfs performances) - (count 0)) - - + (count (if (null? rest) 0 (car rest)))) (if (pair? perfs) (begin (ly:performance-write diff --git a/scm/page.scm b/scm/page.scm index 3e25bdaaee..5e2764100a 100644 --- a/scm/page.scm +++ b/scm/page.scm @@ -136,8 +136,8 @@ -(define (page-headfoot layout scopes number - sym separation-symbol dir last?) +(define (page-headfoot layout scopes number sym separation-symbol dir + is-last-bookpart is-bookpart-last-page) "Create a stencil including separating space." @@ -146,9 +146,8 @@ (stencil (ly:make-stencil "" '(0 . 0) '(0 . 0))) (head-stencil (if (procedure? header-proc) - (header-proc layout scopes number last?) - #f)) - ) + (header-proc layout scopes number is-last-bookpart is-bookpart-last-page) + #f))) (if (and (number? sep) (ly:stencil? head-stencil) @@ -198,8 +197,8 @@ (layout (ly:paper-book-paper paper-book)) (scopes (ly:paper-book-scopes paper-book)) (number (page-page-number page)) - (last? (page-property page 'is-last)) - ) + (is-last-bookpart (page-property page 'is-last-bookpart)) + (is-bookpart-last-page (page-property page 'is-bookpart-last-page))) (page-headfoot layout scopes number (if (= dir UP) @@ -208,7 +207,7 @@ (if (= dir UP) 'head-separation 'foot-separation) - dir last?))) + dir is-last-bookpart is-bookpart-last-page))) (define (page-header page) (page-header-or-footer page UP)) diff --git a/scm/titling.scm b/scm/titling.scm index 23fdce8775..1ab08d73b7 100644 --- a/scm/titling.scm +++ b/scm/titling.scm @@ -12,12 +12,13 @@ ;;;;;;;;;;;;;;;;;; -(define-public ((marked-up-headfoot what-odd what-even) layout scopes page-number last?) +(define-public ((marked-up-headfoot what-odd what-even) + layout scopes page-number is-last-bookpart is-bookpart-last-page) "Read variables WHAT-ODD, WHAT-EVEN from LAYOUT, and interpret them as markup. The PROPS argument will include variables set in SCOPES and -page:last?, page:page-number-string and page:page-number -" +page:is-bookpart-last-page, page:is-last-bookpart, page:page-number-string +and page:page-number" (define (get sym) (ly:output-def-lookup layout sym)) @@ -41,7 +42,8 @@ page:last?, page:page-number-string and page:page-number (cons 'header:tagline (ly:modules-lookup scopes 'tagline (ly:output-def-lookup layout 'tagline))) - (cons 'page:last? last?) + (cons 'page:is-last-bookpart is-last-bookpart) + (cons 'page:is-bookpart-last-page is-bookpart-last-page) (cons 'page:page-number-string (number->string page-number)) (cons 'page:page-number page-number))) @@ -49,7 +51,6 @@ page:last?, page:page-number-string and page:page-number (list pgnum-alist) prefixed-alists (layout-extract-page-properties layout)))) - (interpret-markup layout props potential-markup)) empty-stencil))