]> git.donarmstrong.com Git - lilypond.git/commitdiff
Book parts: nestable book parts
authorNicolas Sceaux <nicolas.sceaux@free.fr>
Sat, 23 Aug 2008 16:34:30 +0000 (18:34 +0200)
committerNicolas Sceaux <nicolas.sceaux@free.fr>
Sat, 23 Aug 2008 16:34:30 +0000 (18:34 +0200)
- 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.

15 files changed:
input/regression/bookparts.ly [new file with mode: 0644]
lily/book-scheme.cc
lily/book.cc
lily/include/book.hh
lily/include/paper-book.hh
lily/lily-lexer.cc
lily/minimal-page-breaking.cc
lily/optimal-page-breaking.cc
lily/page-breaking.cc
lily/page-turn-page-breaking.cc
lily/paper-book.cc
lily/parser.yy
ly/declarations-init.ly
ly/init.ly
scm/lily-library.scm

diff --git a/input/regression/bookparts.ly b/input/regression/bookparts.ly
new file mode 100644 (file)
index 0000000..da5300f
--- /dev/null
@@ -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
+}
index e00f1ded496965f1882390f31976f613b26debb7..58a05650611adeb383276f44953f3a93cc62b747 100644 (file)
@@ -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;
+}
index 335507601d37fc46d3e1fa381e904b4b27421f82..700405f3142d6ff0cdd5c71cba1eda25f449fa78 100644 (file)
@@ -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;
 }
-
index cf478432388fea996d8a35224815a64d1a56de57..d92bcc72a25e346e164c4418f452f3e61a3caf35 100644 (file)
@@ -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 ();
 };
 
index 4ea192794603b6fe9d021c911c21e36aed0bbb72..74829492edc3c7ef501ee6ce8f097182a5a5b8c0 100644 (file)
@@ -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);
 };
 
index 7b77d58922b6816c55607a1d30c9c0f622ae87d1..374eed4ec812dc51ab33a66994b7a70fc168f164 100644 (file)
@@ -31,6 +31,7 @@ static Keyword_ent the_key_tab[]
   {"alias", ALIAS},
   {"alternative", ALTERNATIVE},
   {"book", BOOK},
+  {"bookpart", BOOKPART},
   {"change", CHANGE},
   {"chordmode", CHORDMODE},
   {"chords", CHORDS},
index 870340f83d352aab22e54e1d1a5a62a9738d41fe..0826e181901069adcbcfbd24b72fea4149e452a2 100644 (file)
@@ -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);
index 3c26c9abe08c5d15ac932cd4f2ed7f025f6a19f2..9d395a1b509cc272a23ec6a5e109b96976ad2fae 100644 (file)
@@ -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);
index 1ae769e82d641d79145e53491bd77795baf2d4a4..a0aeddd43d41b26cae71abaa6c8a3bb2aca9cb55 100644 (file)
@@ -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;
 }
index 75e1a3bbf28204e5d6a52e36fba7a793d27ec020..d2e501cb65d034bcb8c63a52aa6e0418bf19c3c9 100644 (file)
@@ -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);
 }
index 020c2e7b21248722977acd93f0885a4ac5ef876e..43e2012043a8fd48ef10fb65c2e40ee0b9d4db28 100644 (file)
@@ -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 ()
 {
@@ -82,14 +93,23 @@ 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)
 {
   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_;
 }
 
index be4e7e26e521cba2ff39792d7c14dcd8b475f0d8..213f3ec53967486e6ea63e8563f44d7e20e5c603 100644 (file)
@@ -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;
index 88fdfe1c0308061213294bec0cf0384177a3e384..70498e717c98d8d35453cb86d550a8b1f4f6f94f 100644 (file)
@@ -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"
index a4bd1c3a3985a466f1c888ac2a58e30197739ae8..5a2103e83c4a47822fad73649b43e143bfda1589 100644 (file)
@@ -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)
   (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)
index f772d0527e931efa53cdf0917fcf359012e911bb..553d6c7dc4c7cfd94502004ccb6b460e6dab3b49 100644 (file)
 
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;; 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