[prev in list] [next in list] [prev in thread] [next in thread] 

List:       lilypond-devel
Subject:    Re: RFC: book parts
From:       Nicolas Sceaux <nicolas.sceaux () free ! fr>
Date:       2008-08-23 18:04:53
Message-ID: 08D9811F-790B-4814-8E9C-2C06F1DF451E () free ! fr
[Download RAW message or body]

Le 18 août 08 à 08:08, Joe Neeman a écrit :

> On Sun, 2008-08-17 at 00:25 +0200, Nicolas Sceaux wrote:
>> Hi,
>>
>> I have taken advantage of this rainy week-end to work on an
>> implementation of book parts. I'd like to know whether this draft
>> patch qualifies as relatively simple, before going on polishing it.
>>
>> It introduces a level between book and scores, named bookparts, hence
>> the Book_part and Paper_book_part classes. Some functionality of the
>> former Paper_book has been moved to Paper_book_part, in particular  
>> the
>> processing relative to page breaking: page breaking functions deal  
>> with
>> Paper_book_part instead of Paper_book, but apart from the renaming of
>> parameters they have not changed.
>
> I've had a quick look through your patch and it all seems clear and
> well-structured. I wonder if it could be made simpler, though, by
> allowing Book and Paper_book to be nested arbitrarily (for example, by
> allowing their scores_ lists to contain (Paper_)Books in addition to
> what they already contain). Then you wouldn't need the extra classes.


Here is a new patch, with the same functionalities as the previous one
but using the nested Book/Paper_book appraoch. Indeed that seems
clearer.

nicolas


["0001-Book-parts-nestable-book-parts.patch" (0001-Book-parts-nestable-book-parts.patch)]

From 1522d86ccab5a663a2bbb1ae809db001e05ca221 Mon Sep 17 00:00:00 2001
From: Nicolas Sceaux <nicolas.sceaux@free.fr>
Date: Sat, 23 Aug 2008 18:34:30 +0200
Subject: [PATCH] Book parts: nestable book parts

- Book and Paper_book instances respectively are nestable: children
  book or paper_book are added to the bookparts_ slot;

- the paper_ slot of a child Book (or Book_paper) is created empty,
  and has its parent set to the paper object of the parent Book (or
  Paper_book), so that default paper properties are got from the
  higher level paper object, and child objects only store part-wide
  overrides. This way, we ensure that fonts are loaded in the higher
  level paper object, so that the output framework can get all the
  loaded fonts from the top level book;

- a Paper_book::top_paper() method is added to access the higher level
  paper object, to access properties that are book-wide, for instance
  the table used to store labels and page numbers;

- in the parser, \bookpart blocks are introduced, which can be used at
  toplevel, or inside a \book block. It can contain the same things as
  \book blocks (except \bookpart blocks, though that would be
  possible). The associated handlers are added.
---
 input/regression/bookparts.ly   |   39 +++++++
 lily/book-scheme.cc             |   22 ++++
 lily/book.cc                    |  174 +++++++++++++++++++++++++-------
 lily/include/book.hh            |    8 ++
 lily/include/paper-book.hh      |    9 ++
 lily/lily-lexer.cc              |    1 +
 lily/minimal-page-breaking.cc   |    2 +-
 lily/optimal-page-breaking.cc   |    2 +-
 lily/page-breaking.cc           |   11 ++-
 lily/page-turn-page-breaking.cc |    4 +-
 lily/paper-book.cc              |  209 ++++++++++++++++++++++++++------------
 lily/parser.yy                  |   71 +++++++++++++
 ly/declarations-init.ly         |    5 +
 ly/init.ly                      |   28 ++++--
 scm/lily-library.scm            |   16 +++-
 15 files changed, 480 insertions(+), 121 deletions(-)
 create mode 100644 input/regression/bookparts.ly

diff --git a/input/regression/bookparts.ly b/input/regression/bookparts.ly
new file mode 100644
index 0000000..da5300f
--- /dev/null
+++ b/input/regression/bookparts.ly
@@ -0,0 +1,39 @@
+\version "2.11.55"
+
+\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 e00f1de..58a0565 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 3355076..700405f 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,145 @@ 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_;
 
-/* Concatenate all score outputs into a Paper_book
- */
-Paper_book *
-Book::process (Output_def *default_paper,
-	       Output_def *default_layout)
+  if (header_ == SCM_EOL)
+    {
+      header_ = ly_make_anonymous_module (false);
+      if (ly_is_module (parent->header_))
+        ly_module_copy (header_, parent->header_);
+    }
+}
+
+void
+Book::add_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_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);
+}
+
+/* Concatenate all score or book part outputs into a Paper_book
+ */
+Paper_book *
+Book::process (Output_def *default_paper,
+	       Output_def *default_layout,
+               Output_def *parent_paper)
+{
   Output_def *paper = paper_ ? paper_ : default_paper;
+
+  /* If top book, recursively check score errors */
+  if (!parent_paper && 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_paper)
+    paper_book->paper_->parent_ = parent_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<Performance *> (output))
-		paper_book->add_performance (perf->self_scm ());
-	      else if (Paper_score *pscore = dynamic_cast<Paper_score *> (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 */
+      add_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, default_layout, paper_book->paper_);
+              if (paper_book_part)
+                paper_book->add_bookpart (paper_book_part->self_scm ());
+            }
+        }
+    }
+  else
+    {
+      /* Process scores */
+      /* Render in order of parsing.  */
+      for (SCM s = scm_reverse (scores_); scm_is_pair (s); s = scm_cdr (s))
+        {
+          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<Performance *> (output))
+                    paper_book->add_performance (perf->self_scm ());
+                  else if (Paper_score *pscore = dynamic_cast<Paper_score *> (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);
+        }
     }
 
   return paper_book;
 }
-
diff --git a/lily/include/book.hh b/lily/include/book.hh
index cf47843..d92bcc7 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,8 +32,15 @@ public:
   VIRTUAL_COPY_CONSTRUCTOR(Book, Book);
   Book ();
   void add_score (SCM);
+  void add_bookpart ();
+  void add_bookpart (SCM);
+  void set_parent (Book *parent);
+  bool error_found ();
   Paper_book *process (Output_def *def_paper,
 		       Output_def *def_layout);
+  Paper_book *process (Output_def *default_paper,
+                       Output_def *default_layout,
+                       Output_def *parent_paper);
   void set_keys ();
 };
 
diff --git a/lily/include/paper-book.hh b/lily/include/paper-book.hh
index 4ea1927..7482949 100644
--- a/lily/include/paper-book.hh
+++ b/lily/include/paper-book.hh
@@ -31,11 +31,15 @@ public:
   SCM header_;
   SCM header_0_;
   SCM scores_;
+  SCM bookparts_;
   Output_def *paper_;
 
   Paper_book ();
 
+  Output_def *top_paper ();
+
   void add_score (SCM);
+  void add_bookpart (SCM);
   void add_performance (SCM);
 
   SCM performances () const;
@@ -47,7 +51,12 @@ public:
   Stencil book_title ();
   Stencil score_title (SCM);
   
+  void classic_output_aux (SCM output);
   void classic_output (SCM output_channel);
+  int output_aux (SCM output_channel,
+                  int first_page_number,
+                  bool is_first,
+                  bool is_last);
   void output (SCM output_channel);
 };
 
diff --git a/lily/lily-lexer.cc b/lily/lily-lexer.cc
index 7b77d58..374eed4 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/minimal-page-breaking.cc b/lily/minimal-page-breaking.cc
index 870340f..0826e18 100644
--- a/lily/minimal-page-breaking.cc
+++ b/lily/minimal-page-breaking.cc
@@ -40,7 +40,7 @@ Minimal_page_breaking::solve ()
   break_into_pieces (0, end, current_configuration (0));
 
   message (_ ("Computing page breaks..."));
-  vsize first_page_num = robust_scm2int (book_->paper_->c_variable ("first-page-number"), 1);
+  vsize first_page_num = robust_scm2int (book_->paper_->c_variable ("part-first-page-number"), 1);
   Page_spacing_result res = pack_systems_on_least_pages (0, first_page_num);
   SCM lines = systems ();
   return make_pages (res.systems_per_page_, lines);
diff --git a/lily/optimal-page-breaking.cc b/lily/optimal-page-breaking.cc
index 3c26c9a..9d395a1 100644
--- a/lily/optimal-page-breaking.cc
+++ b/lily/optimal-page-breaking.cc
@@ -38,7 +38,7 @@ Optimal_page_breaking::solve ()
 {
   vsize end = last_break_position ();
   vsize max_sys_count = max_system_count (0, end);
-  vsize first_page_num = robust_scm2int (book_->paper_->c_variable ("first-page-number"), 1);
+  vsize first_page_num = robust_scm2int (book_->paper_->c_variable ("part-first-page-number"), 1);
   SCM forced_page_count = book_->paper_->c_variable ("page-count");
 
   set_to_ideal_line_configuration (0, end);
diff --git a/lily/page-breaking.cc b/lily/page-breaking.cc
index 1ae769e..a0aeddd 100644
--- a/lily/page-breaking.cc
+++ b/lily/page-breaking.cc
@@ -253,14 +253,17 @@ 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);
+    = robust_scm2int (book_->paper_->c_variable ("part-first-page-number"), 1);
+  bool last_part = ly_scm2bool (book_->paper_->c_variable ("part-is-last"));
   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 last = scm_from_bool (last_part && (i == lines_per_page.size () - 1));
       SCM rag = scm_from_bool (ragged () || (to_boolean (last)
 					     && ragged_last ()));
       SCM line_count = scm_from_int (lines_per_page[i]);
@@ -290,7 +293,7 @@ Page_breaking::make_pages (vector<vsize> 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/page-turn-page-breaking.cc b/lily/page-turn-page-breaking.cc
index 75e1a3b..d2e501c 100644
--- a/lily/page-turn-page-breaking.cc
+++ b/lily/page-turn-page-breaking.cc
@@ -122,7 +122,7 @@ Page_turn_page_breaking::calc_subproblem (vsize ending_breakpoint)
       if (start > 0 && best.demerits_ < state_[start-1].demerits_)
         continue;
 
-      int p_num = robust_scm2int (book_->paper_->c_variable ("first-page-number"), 1);
+      int p_num = robust_scm2int (book_->paper_->c_variable ("part-first-page-number"), 1);
       if (start > 0)
         {
 	  /* except possibly for the first page, enforce the fact that first_page_number_
@@ -260,7 +260,7 @@ Page_turn_page_breaking::make_pages (vector<Break_node> const &soln, SCM systems
 
   /* this should only actually modify first-page-number if
      auto-first-page-number was true. */
-  book_->paper_->set_variable (ly_symbol2scm ("first-page-number"),
+  book_->paper_->set_variable (ly_symbol2scm ("part-first-page-number"),
 			       scm_from_int (soln[0].first_page_number_));
   return Page_breaking::make_pages (lines_per_page, systems);
 }
diff --git a/lily/paper-book.cc b/lily/paper-book.cc
index 020c2e7..43e2012 100644
--- a/lily/paper-book.cc
+++ b/lily/paper-book.cc
@@ -27,6 +27,7 @@ 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;
 
@@ -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->bookparts_);
   return b->systems_;
 }
 
@@ -65,6 +67,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 ()
 {
@@ -83,13 +94,22 @@ Paper_book::add_score (SCM s)
 }
 
 void
+Paper_book::add_bookpart (SCM p)
+{
+  bookparts_ = scm_cons (p, bookparts_);
+}
+
+void
 Paper_book::add_performance (SCM s)
 {
   performances_ = scm_cons (s, performances_);
 }
 
-void
-Paper_book::output (SCM output_channel)
+int
+Paper_book::output_aux (SCM output_channel,
+                        int first_page_number,
+                        bool is_first,
+                        bool is_last)
 {
   if (scm_is_pair (performances_))
     {
@@ -98,11 +118,44 @@ Paper_book::output (SCM output_channel)
       scm_call_2 (proc, performances (), output_channel);
     }
 
-  if (scores_ == SCM_EOL)
-    return;
+  if (scm_is_pair (bookparts_))
+    {
+      bool is_first_part = is_first;
+      int page_number = first_page_number;
+      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)));
+            page_number += pbookpart->output_aux (output_channel,
+                                                  page_number,
+                                                  is_first_part,
+                                                  is_last_part);
+            is_first_part = false;
+          }
+      return page_number;
+    }
+  else
+    {
+      if (scores_ == SCM_EOL)
+        return 0;
+      paper_->set_variable (ly_symbol2scm ("part-first-page-number"),
+                            scm_long2num (first_page_number));
+      paper_->set_variable (ly_symbol2scm ("part-is-first"),
+                            ly_bool2scm (is_first));
+      paper_->set_variable (ly_symbol2scm ("part-is-last"),
+                            ly_bool2scm (is_last));
+      /* Generate all stencils to trigger font loads.  */
+      return scm_ilength (pages ());
+    }
+}
 
-  /* Generate all stencils to trigger font loads.  */
-  pages ();
+void
+Paper_book::output (SCM output_channel)
+{
+  output_aux (output_channel,
+              robust_scm2int (paper_->c_variable ("first-page-number"), 1),
+              true,
+              true);
 
   SCM scopes = SCM_EOL;
   if (ly_is_module (header_))
@@ -137,7 +190,7 @@ Paper_book::output (SCM output_channel)
 }
 
 void
-Paper_book::classic_output (SCM output)
+Paper_book::classic_output_aux (SCM output)
 {
   if (scm_is_pair (performances_))
     {
@@ -148,6 +201,12 @@ Paper_book::classic_output (SCM output)
   
   /* Generate all stencils to trigger font loads.  */
   systems ();
+}
+
+void
+Paper_book::classic_output (SCM output)
+{
+  classic_output_aux (output);
 
   SCM scopes = SCM_EOL;
   if (ly_is_module (header_))
@@ -322,15 +381,18 @@ Paper_book::get_system_specs ()
 {
   SCM system_specs = SCM_EOL;
   
-  Stencil title = book_title ();
-  if (!title.is_empty ())
+  if (ly_scm2bool (paper_->c_variable ("part-is-first")))
     {
-      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 ();
+      Stencil title = book_title ();
+      if (!title.is_empty ())
+        {
+          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 ();
+        }
     }
 
   SCM page_properties
@@ -448,46 +510,54 @@ 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<Paper_score*> (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;
-
-      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));
-	}
+      SCM specs = get_system_specs ();
+      for (SCM s = specs; scm_is_pair (s); s = scm_cdr (s))
+        {
+          if (Paper_score *pscore = dynamic_cast<Paper_score*> (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_);
+
+      /* 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))
+        {
+          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));
+            }
+        }
     }
 
   return systems_;
@@ -500,22 +570,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");
-
-	  systems_ = scm_append (scm_list_2 (systems_, 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)))
+          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 ()));
+
+      /* 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 be4e7e2..213f3ec 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> book_block
 %type <book> book_body
+%type <book> bookpart_block
+%type <book> bookpart_body
 
 %type <i> bare_unsigned
 %type <scm> 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 ();
@@ -664,6 +677,12 @@ book_body:
 		$$->paper_ = $2;
 		$2->unprotect ();
 	}
+	| 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;
 		SCM proc = PARSER->lexer_->lookup_identifier ("book-score-handler");
@@ -689,12 +708,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 88fdfe1..70498e7 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 a4bd1c3..5a2103e 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/scm/lily-library.scm b/scm/lily-library.scm
index f772d05..553d6c7 100644
--- a/scm/lily-library.scm
+++ b/scm/lily-library.scm
@@ -56,8 +56,22 @@
 
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;; 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
-- 
1.5.3.7





[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic