[LyX/master] Support for front/main/backmatter (#12424)
Juergen Spitzmueller
spitz at lyx.org
Mon Aug 3 09:30:41 UTC 2026
commit e569426b1583df3b7dd6c606cc0feadc37e5723b
Author: Juergen Spitzmueller <spitz at lyx.org>
Date: Mon Aug 3 11:29:12 2026 +0200
Support for front/main/backmatter (#12424)
Based on a patch by Daniel Ramöller
File and layout format change
---
development/FORMAT | 5 ++
lib/doc/Customization.lyx | 121 ++++++++++++++++++++++++++++++++++++++++++
lib/doc/UserGuide.lyx | 72 +++++++++++++++++++++++++
lib/doc/de/Customization.lyx | 76 ++++++++++++++++++++++++++
lib/doc/de/UserGuide.lyx | 76 ++++++++++++++++++++++++++
lib/layouts/amsbook.layout | 1 +
lib/layouts/book.layout | 4 +-
lib/layouts/memoir.layout | 1 +
lib/layouts/recipebook.layout | 1 +
lib/layouts/scrbook.layout | 1 +
lib/layouts/tufte-book.layout | 1 +
lib/lyx2lyx/lyx_2_6.py | 33 +++++++++++-
lib/lyx2lyx/parser_tools.py | 1 +
lib/scripts/layout2layout.py | 7 ++-
lib/ui/stdmenus.inc | 7 +++
src/Buffer.cpp | 11 +++-
src/Counters.cpp | 4 +-
src/Counters.h | 9 ++++
src/FuncCode.h | 1 +
src/LayoutEnums.h | 12 +++++
src/LyXAction.cpp | 10 ++++
src/ParagraphParameters.cpp | 46 ++++++++++++++++
src/ParagraphParameters.h | 12 +++++
src/RowPainter.cpp | 47 ++++++++++++----
src/RowPainter.h | 2 +-
src/Text.cpp | 44 +++++++++++++++
src/TextClass.cpp | 11 +++-
src/TextClass.h | 4 ++
src/TextMetrics.cpp | 7 +--
src/output_latex.cpp | 15 ++++++
src/tex2lyx/text.cpp | 72 +++++++++++++++++++++++++
src/version.h | 4 +-
32 files changed, 692 insertions(+), 26 deletions(-)
diff --git a/development/FORMAT b/development/FORMAT
index 21bad579ed..fae968c761 100644
--- a/development/FORMAT
+++ b/development/FORMAT
@@ -7,6 +7,11 @@ changes happened in particular if possible. A good example would be
-----------------------
+2026-08-03 Jürgen Spitzmüller <spitz at lyx.org>
+ * Format incremented to 657: Support for \fronmatter, \mainmatter,
+ and \backmatter via paragraph parameter
+ \start_of_matter {front,main,back}
+
2026-08-02 Jürgen Spitzmüller <spitz at lyx.org>
* Format incremented to 656: Support for parfillskip
New buffer param \parfillskip "<type>" "<glue length>"
diff --git a/lib/doc/Customization.lyx b/lib/doc/Customization.lyx
index c4e396b38f..fad374325d 100644
--- a/lib/doc/Customization.lyx
+++ b/lib/doc/Customization.lyx
@@ -11524,6 +11524,107 @@ nolink "false"
\end_inset
for the list of features.
+\change_inserted -712698321 1785747170
+
+\end_layout
+
+\begin_layout Description
+
+\change_inserted -712698321 1785747231
+\begin_inset Flex Code
+status collapsed
+
+\begin_layout Plain Layout
+
+\change_inserted -712698321 1785747178
+ProvidesMatters
+\end_layout
+
+\end_inset
+
+ [
+\begin_inset Flex Code
+status collapsed
+
+\begin_layout Plain Layout
+
+\change_inserted -712698321 1785747171
+
+\emph on
+0
+\end_layout
+
+\end_inset
+
+
+\begin_inset space \thinspace{}
+\end_inset
+
+
+\begin_inset Flex Code
+status collapsed
+
+\begin_layout Plain Layout
+
+\change_inserted -712698321 1785747171
+1
+\end_layout
+
+\end_inset
+
+] indicates whether the text class provides for
+\begin_inset Flex Code
+status collapsed
+
+\begin_layout Plain Layout
+
+\change_inserted -712698321 1785747217
+
+\backslash
+frontmatter
+\change_unchanged
+
+\end_layout
+
+\end_inset
+
+,
+
+\begin_inset Flex Code
+status collapsed
+
+\begin_layout Plain Layout
+
+\change_inserted -712698321 1785747224
+
+\backslash
+mainmatter
+\change_unchanged
+
+\end_layout
+
+\end_inset
+
+,
+ and
+\begin_inset Flex Code
+status collapsed
+
+\begin_layout Plain Layout
+
+\change_inserted -712698321 1785747231
+
+\backslash
+backmatter
+\change_unchanged
+
+\end_layout
+
+\end_inset
+
+ document division.
+\change_unchanged
+
\end_layout
\begin_layout Description
@@ -12607,6 +12708,8 @@ status collapsed
\change_inserted -712698321 1785664317
ParfillskipNone
+\change_unchanged
+
\end_layout
\end_inset
@@ -12619,6 +12722,8 @@ status collapsed
\change_inserted -712698321 1785664297
string
+\change_unchanged
+
\end_layout
\end_inset
@@ -12647,6 +12752,8 @@ status collapsed
\change_inserted -712698321 1785664387
parfullskip=none
+\change_unchanged
+
\end_layout
\end_inset
@@ -12709,6 +12816,8 @@ status collapsed
\change_inserted -712698321 1785664507
ParskipFull parskip=full
+\change_unchanged
+
\end_layout
\end_inset
@@ -12728,6 +12837,8 @@ status collapsed
\change_inserted -712698321 1785664326
ParfillskipQuarter
+\change_unchanged
+
\end_layout
\end_inset
@@ -12740,6 +12851,8 @@ status collapsed
\change_inserted -712698321 1785664297
string
+\change_unchanged
+
\end_layout
\end_inset
@@ -12752,6 +12865,8 @@ status collapsed
\change_inserted -712698321 1785664574
ParfillskipNone
+\change_unchanged
+
\end_layout
\end_inset
@@ -12784,6 +12899,8 @@ status collapsed
\change_inserted -712698321 1785664337
ParfillskipThird
+\change_unchanged
+
\end_layout
\end_inset
@@ -12796,6 +12913,8 @@ status collapsed
\change_inserted -712698321 1785664331
string
+\change_unchanged
+
\end_layout
\end_inset
@@ -12808,6 +12927,8 @@ status collapsed
\change_inserted -712698321 1785664602
ParfillskipNone
+\change_unchanged
+
\end_layout
\end_inset
diff --git a/lib/doc/UserGuide.lyx b/lib/doc/UserGuide.lyx
index 227bc04b78..3f96a26068 100644
--- a/lib/doc/UserGuide.lyx
+++ b/lib/doc/UserGuide.lyx
@@ -33664,6 +33664,78 @@ Restore value of counter:
The dialog allows you to choose which counter you wish to modify from all those that are available in the current document class.
\end_layout
+\begin_layout Section
+
+\change_inserted -712698321 1785747694
+Structural Units
+\end_layout
+
+\begin_layout Standard
+
+\change_inserted -712698321 1785747937
+Some document classes,
+ particularly book classes,
+ support the division of documents in structural units,
+ named
+\begin_inset Quotes eld
+\end_inset
+
+front matter
+\begin_inset Quotes erd
+\end_inset
+
+ (for the preliminary parts of the book such as titling,
+ table of contents,
+ preface),
+
+\begin_inset Quotes eld
+\end_inset
+
+main matter
+\begin_inset Quotes erd
+\end_inset
+
+ (for the main part),
+ and
+\begin_inset Quotes eld
+\end_inset
+
+back matter
+\begin_inset Quotes erd
+\end_inset
+
+ (for appendices and the like).
+ Usually,
+ front matters are differently paginated (e.g.,
+ using roman numbering),
+ whereas main matters start with page 1.
+ Back matters often do not use section numbering.
+ The details how the three units are supposed to look are defined in the class.
+\end_layout
+
+\begin_layout Standard
+
+\change_inserted -712698321 1785748007
+In \SpecialChar LyX
+,
+ you can start these units via
+\family sans
+Document\SpecialChar menuseparator
+Start
+\begin_inset space ~
+\end_inset
+
+Structural Unit
+\begin_inset space ~
+\end_inset
+
+Here
+\family default
+ with the classes that support these divisions.
+\change_unchanged
+
+\end_layout
+
\begin_layout Section
Appendices
\begin_inset Index idx
diff --git a/lib/doc/de/Customization.lyx b/lib/doc/de/Customization.lyx
index 5efed2f20e..4ec0000336 100644
--- a/lib/doc/de/Customization.lyx
+++ b/lib/doc/de/Customization.lyx
@@ -9966,6 +9966,82 @@ nolink "false"
\begin_inset Flex Code
status collapsed
+\begin_layout Plain Layout
+ProvidesMatters
+\end_layout
+
+\end_inset
+
+ [
+\begin_inset Flex Code
+status collapsed
+
+\begin_layout Plain Layout
+
+\emph on
+0
+\end_layout
+
+\end_inset
+
+
+\begin_inset space \thinspace{}
+\end_inset
+
+
+\begin_inset Flex Code
+status collapsed
+
+\begin_layout Plain Layout
+1
+\end_layout
+
+\end_inset
+
+] zeigt an ob die Klasse die Strukturierung des Dokuments mittels
+\begin_inset Flex Code
+status collapsed
+
+\begin_layout Plain Layout
+
+\backslash
+frontmatter
+\end_layout
+
+\end_inset
+
+,
+
+\begin_inset Flex Code
+status collapsed
+
+\begin_layout Plain Layout
+
+\backslash
+mainmatter
+\end_layout
+
+\end_inset
+
+ und
+\begin_inset Flex Code
+status collapsed
+
+\begin_layout Plain Layout
+
+\backslash
+backmatter
+\end_layout
+
+\end_inset
+
+ unterstützt.
+\end_layout
+
+\begin_layout Description
+\begin_inset Flex Code
+status collapsed
+
\begin_layout Plain Layout
ProvidesModule
\end_layout
diff --git a/lib/doc/de/UserGuide.lyx b/lib/doc/de/UserGuide.lyx
index e5ab356c92..60e0a9ee0a 100644
--- a/lib/doc/de/UserGuide.lyx
+++ b/lib/doc/de/UserGuide.lyx
@@ -32850,6 +32850,82 @@ In diesem Dialog können Sie aus allen Zählern,
den Sie ändern möchten.
\end_layout
+\begin_layout Section
+Strukturelle Einheiten
+\end_layout
+
+\begin_layout Standard
+Manche Dokumentklassen,
+ besonders die für Bücher,
+ unterstützen die Einteilung von Dokumenten in strukturelle Einheiten,
+ nämlich Vorspann (engl.
+
+\begin_inset Quotes gld
+\end_inset
+
+
+\lang english
+front matter
+\lang ngerman
+
+\begin_inset Quotes grd
+\end_inset
+
+,
+ für die Titelei,
+ das Inhaltsverzeichnis,
+ Vorworte usw.),
+ Hauptteil (engl.
+
+\begin_inset Quotes gld
+\end_inset
+
+
+\lang english
+main matter
+\lang ngerman
+
+\begin_inset Quotes grd
+\end_inset
+
+) und Nachspann (engl.
+
+\begin_inset Quotes gld
+\end_inset
+
+
+\lang english
+back matter
+\lang ngerman
+
+\begin_inset Quotes grd
+\end_inset
+
+,
+ für Anhänge und Ähnliches).
+ Meistens ist der Vorspann anders paginiert (bspw.
+ mit römischen Seitenzahlen),
+ wobei der Hauptteil dann mit Seite
+\begin_inset space ~
+\end_inset
+
+1 beginnt.
+ Der Nachspann verwendet oft keine Abschnittsnummerierung.
+ Wie es genau ausschaut definiert aber die jeweilige Klasse.
+\end_layout
+
+\begin_layout Standard
+In \SpecialChar LyX
+ können Sie diese Einheiten über
+\family sans
+Dokument\SpecialChar menuseparator
+Strukturelle Einheit hier beginnen
+\family default
+im Fall der Klassen,
+ die solche Unterteilungen unterstützen,
+ markieren.
+\end_layout
+
\begin_layout Section
Anhänge
\begin_inset CommandInset label
diff --git a/lib/layouts/amsbook.layout b/lib/layouts/amsbook.layout
index 5546c9dc3b..c82c372528 100644
--- a/lib/layouts/amsbook.layout
+++ b/lib/layouts/amsbook.layout
@@ -17,6 +17,7 @@ Format 119
Columns 1
Sides 2
PageStyle Headers
+ProvidesMatters 1
DocBookRoot book
Provides amsmath 1
diff --git a/lib/layouts/book.layout b/lib/layouts/book.layout
index 3e56a6cdc2..e13894d6be 100644
--- a/lib/layouts/book.layout
+++ b/lib/layouts/book.layout
@@ -11,10 +11,10 @@ Format 119
Input stdclass.inc
Input numreport.inc
-
Sides 2
PageStyle Headings
-DocBookRoot book
+ProvidesMatters 1
+DocBookRoot book
DocBookForceAbstract true
diff --git a/lib/layouts/memoir.layout b/lib/layouts/memoir.layout
index 8d52a03e59..9f3502cbe0 100644
--- a/lib/layouts/memoir.layout
+++ b/lib/layouts/memoir.layout
@@ -12,6 +12,7 @@ Columns 1
Sides 2
SecNumDepth 1
TocDepth 1
+ProvidesMatters 1
DefaultStyle Standard
PageStyle Headings
PageSize letter
diff --git a/lib/layouts/recipebook.layout b/lib/layouts/recipebook.layout
index 08ced25e8e..8295994c51 100644
--- a/lib/layouts/recipebook.layout
+++ b/lib/layouts/recipebook.layout
@@ -35,6 +35,7 @@ Input numreport.inc
SecNumDepth 2
TocDepth 3
PageStyle Headings
+ProvidesMatters 1
DocBookRoot book
# There is no abstract environment in scrbook.cls
diff --git a/lib/layouts/scrbook.layout b/lib/layouts/scrbook.layout
index 4e5860c409..6ba036fa70 100644
--- a/lib/layouts/scrbook.layout
+++ b/lib/layouts/scrbook.layout
@@ -10,6 +10,7 @@ Input numreport.inc
Sides 2
PageStyle Headings
+ProvidesMatters 1
DocBookRoot book
NoStyle Abstract
diff --git a/lib/layouts/tufte-book.layout b/lib/layouts/tufte-book.layout
index fb683e4ff4..c91192f04c 100644
--- a/lib/layouts/tufte-book.layout
+++ b/lib/layouts/tufte-book.layout
@@ -12,6 +12,7 @@ Provides natbib-internal 1
Sides 2
PageStyle Headings
+ProvidesMatters 1
DocBookRoot book
ClassOptions
diff --git a/lib/lyx2lyx/lyx_2_6.py b/lib/lyx2lyx/lyx_2_6.py
index 5eed5cbe05..c123d1fb64 100644
--- a/lib/lyx2lyx/lyx_2_6.py
+++ b/lib/lyx2lyx/lyx_2_6.py
@@ -1600,6 +1600,35 @@ def revert_parfillskip(document):
if preamble != "":
add_to_preamble(document, preamble)
+
+def revert_matters(document):
+ """Revert \\frontmatter, \\mainmatter and \\backmatter to ERT"""
+
+ for matter in ["front", "main", "back"]:
+ i = 0
+ while True:
+ i = find_token(document.body, "\\start_of_matter " + matter, i)
+ if i == -1:
+ break
+
+ # Add ERT
+ lay = get_containing_layout(document.body, i)
+ if lay == False:
+ document.warning("Matter has no layout!")
+ i += 1
+ continue
+ beglay = lay[1]
+ endlay = find_end_of_layout(document.body, beglay)
+ if endlay == False:
+ document.warning("Matter has no endlayout!")
+ i += 1
+ continue
+
+ layb = document.body[beglay]
+ laye = document.body[endlay]
+ document.body[i : i + 1] = put_cmd_in_ert(["\\" + matter + "matter"]) + ["", laye, "", layb]
+ i += 1
+
##
# Conversion hub
#
@@ -1618,11 +1647,13 @@ convert = [
[653, []],
[654, []],
[655, []],
- [656, []]
+ [656, []],
+ [657, []]
]
revert = [
+ [656, [revert_matters]],
[655, [revert_parfillskip]],
[654, [revert_hex32colors]],
[653, [revert_mintedlangs]],
diff --git a/lib/lyx2lyx/parser_tools.py b/lib/lyx2lyx/parser_tools.py
index 839b922278..3e3de99125 100644
--- a/lib/lyx2lyx/parser_tools.py
+++ b/lib/lyx2lyx/parser_tools.py
@@ -684,6 +684,7 @@ def get_containing_layout(lines, i):
"\\indent-toggle",
"\\leftindent",
"\\start_of_appendix",
+ "\\start_of_matter",
"\\paragraph_spacing",
"\\align",
"\\labelwidthstring",
diff --git a/lib/scripts/layout2layout.py b/lib/scripts/layout2layout.py
index f506b5deb5..cbaa08904b 100644
--- a/lib/scripts/layout2layout.py
+++ b/lib/scripts/layout2layout.py
@@ -9,7 +9,7 @@
# This script will update a .layout file to current format
# The latest layout format is also defined in src/TextClass.cpp
-currentFormat = 119
+currentFormat = 120
# Incremented to format 4, 6 April 2007, lasgouttes
@@ -404,6 +404,9 @@ currentFormat = 119
# New class tags ParfillskipNone, ParfillskipQuarter
# and ParfillskipThird
+# Incremented to format 120, 3 August 2026 by spitz
+# New class tag ProvidesMatters
+
# Do not forget to document format change in Customization
# Manual (section "Declaring a new text class").
@@ -743,7 +746,7 @@ def convert(lines, end_format):
i += 1
continue
- if 101 <= format <= 119:
+ if 101 <= format <= 120:
# nothing to do.
i += 1
continue
diff --git a/lib/ui/stdmenus.inc b/lib/ui/stdmenus.inc
index cecf4616c3..edc736a62a 100644
--- a/lib/ui/stdmenus.inc
+++ b/lib/ui/stdmenus.inc
@@ -569,6 +569,7 @@ Menuset
Item "LaTeX Log|L" "dialog-show latexlog"
Item "Processing Errors|r" "errors-show"
Item "Start Appendix Here|x" "appendix"
+ OptSubmenu "Start Structural Unit Here" "matter"
Separator
ViewFormats
UpdateFormats
@@ -592,6 +593,12 @@ Menuset
Item "Show Changes in Output|S" "changes-output"
End
+ Menu "matter"
+ OptItem "Front Matter" "matter front"
+ OptItem "Main Matter" "matter main"
+ OptItem "Back Matter" "matter back"
+ End
+
#
# NAVIGATE MENU
#
diff --git a/src/Buffer.cpp b/src/Buffer.cpp
index 85124d0f1f..e0dbb0659b 100644
--- a/src/Buffer.cpp
+++ b/src/Buffer.cpp
@@ -5405,6 +5405,11 @@ void Buffer::Impl::setLabel(ParIterator & it, UpdateType utype) const
}
par.params().appendix(counters.appendix());
+ if (par.params().startOfMatter() != MATTER_NONE) {
+ counters.mainMatter(par.params().startOfMatter() == MATTER_MAIN);
+ }
+ par.params().mainMatter(counters.mainMatter());
+
// Compute the item depth of the paragraph
par.itemdepth = getItemDepth(it);
@@ -5494,8 +5499,10 @@ void Buffer::Impl::setLabel(ParIterator & it, UpdateType utype) const
docstring const & lcounter = layout.counter;
if (!lcounter.empty()) {
if (layout.toclevel <= bp.secnumdepth
- && (layout.latextype != LATEX_ENVIRONMENT
- || it.text()->isFirstInSequence(it.pit()))) {
+ && (layout.latextype != LATEX_ENVIRONMENT
+ || it.text()->isFirstInSequence(it.pit()))
+ // No numbering for chapters outside of main matter.
+ && (layout.toclevel != 0 || par.params().mainMatter())) {
if (counters.hasCounter(lcounter))
counters.step(lcounter, utype);
par.params().labelString(par.expandLabel(layout, bp));
diff --git a/src/Counters.cpp b/src/Counters.cpp
index a93274558a..12e6867187 100644
--- a/src/Counters.cpp
+++ b/src/Counters.cpp
@@ -250,7 +250,8 @@ Counter::StringMap & Counter::flatLabelStrings(bool in_appendix) const
}
-Counters::Counters() : appendix_(false), subfloat_(false), longtable_(false)
+Counters::Counters() : appendix_(false), main_matter_(true), subfloat_(false),
+ longtable_(false)
{
layout_stack_.push_back(nullptr);
counter_stack_.push_back(from_ascii(""));
@@ -444,6 +445,7 @@ docstring const & Counters::latexName(docstring const & cntr) const
void Counters::reset()
{
appendix_ = false;
+ main_matter_ = true;
subfloat_ = false;
current_float_.erase();
for (auto & ctr : counterList_)
diff --git a/src/Counters.h b/src/Counters.h
index d522a7b757..e1265b8ccb 100644
--- a/src/Counters.h
+++ b/src/Counters.h
@@ -16,6 +16,7 @@
#define COUNTERS_H
#include "OutputEnums.h"
+#include "LayoutEnums.h"
#include "support/docstring.h"
@@ -149,6 +150,8 @@ public:
/// \return true on success
bool read(support::Lexer & lex, docstring const & name, bool makenew);
///
+ void setEnabled(docstring const & ctr, bool const enabled);
+ ///
void set(docstring const & ctr, int val);
///
void addto(docstring const & ctr, int val);
@@ -210,6 +213,10 @@ public:
bool appendix() const { return appendix_; }
/// Set the state variable indicating whether we are in appendix.
void appendix(bool a) { appendix_ = a; }
+ /// Are we in main matter?
+ bool mainMatter() const { return main_matter_; }
+ /// Set the state variable indicating whether which matter we are in.
+ void mainMatter(bool m) { main_matter_ = m; }
/// Returns the current enclosing float.
std::string const & current_float() const { return current_float_; }
/// Sets the current enclosing float.
@@ -275,6 +282,8 @@ private:
CounterList counterList_;
/// Are we in an appendix?
bool appendix_;
+ /// Are we in the main matter?
+ bool main_matter_;
/// The current enclosing float.
std::string current_float_;
/// Are we in a subfloat?
diff --git a/src/FuncCode.h b/src/FuncCode.h
index 4f78596b9c..d8ad69e67b 100644
--- a/src/FuncCode.h
+++ b/src/FuncCode.h
@@ -516,6 +516,7 @@ enum FuncCode
LFUN_ERROR_NEXT, // spitz 20200101,
/// 405
LFUN_BUFFER_UPDATE_EXTERNAL, // spitz 20250406,
+ LFUN_MATTER, // spitz/daniel, 20260805
LFUN_LASTACTION // end of the table
};
diff --git a/src/LayoutEnums.h b/src/LayoutEnums.h
index 48e849f1a4..3215311806 100644
--- a/src/LayoutEnums.h
+++ b/src/LayoutEnums.h
@@ -158,6 +158,18 @@ enum ArgPassThru {
PT_TRUE
};
+///
+enum Matter {
+ ///
+ MATTER_NONE,
+ ///
+ MATTER_FRONT,
+ ///
+ MATTER_MAIN,
+ ///
+ MATTER_BACK
+};
+
} // namespace lyx
#endif
diff --git a/src/LyXAction.cpp b/src/LyXAction.cpp
index bd9c9df054..f7e174e1bf 100644
--- a/src/LyXAction.cpp
+++ b/src/LyXAction.cpp
@@ -298,6 +298,16 @@ void LyXAction::init()
*/
{ LFUN_APPENDIX, "appendix", Noop, Edit },
+/*!
+ * \var lyx::FuncCode lyx::LFUN_MATTER
+ * \li Action: Start (or unstart) matter on the given cursor position.
+ * \li Syntax: matter <type>
+ * \li Params: <type>: front, main, back.
+ * \li Origin: daniel/spitz, 3 Aug 2026
+ * \endvar
+ */
+ { LFUN_MATTER, "matter", Noop, Edit },
+
/*!
* \var lyx::FuncCode lyx::LFUN_ARGUMENT_INSERT
diff --git a/src/ParagraphParameters.cpp b/src/ParagraphParameters.cpp
index 32dc44900b..ec50faafdf 100644
--- a/src/ParagraphParameters.cpp
+++ b/src/ParagraphParameters.cpp
@@ -41,6 +41,7 @@ static char const * const string_align[] = {
ParagraphParameters::ParagraphParameters()
: noindent_(false),
start_of_appendix_(false), appendix_(false),
+ start_of_matter_(MATTER_NONE), main_matter_(true),
align_(LYX_ALIGN_LAYOUT), depth_(0)
{}
@@ -132,6 +133,30 @@ void ParagraphParameters::appendix(bool a)
}
+Matter ParagraphParameters::startOfMatter() const
+{
+ return start_of_matter_;
+}
+
+
+void ParagraphParameters::startOfMatter(Matter som)
+{
+ start_of_matter_ = som;
+}
+
+
+bool ParagraphParameters::mainMatter() const
+{
+ return main_matter_;
+}
+
+
+void ParagraphParameters::mainMatter(bool m)
+{
+ main_matter_ = m;
+}
+
+
docstring const & ParagraphParameters::labelString() const
{
return labelstring_;
@@ -207,6 +232,15 @@ void ParagraphParameters::read(Lexer & lex, bool merge)
leftIndent(value);
} else if (token == "\\start_of_appendix") {
startOfAppendix(true);
+ } else if (token == "\\start_of_matter") {
+ lex.next();
+ string const tmp = rtrim(lex.getString());
+ if (tmp == "front")
+ startOfMatter(MATTER_FRONT);
+ else if (tmp == "main")
+ startOfMatter(MATTER_MAIN);
+ else if (tmp == "back")
+ startOfMatter(MATTER_BACK);
} else if (token == "\\paragraph_spacing") {
lex.next();
string const tmp = rtrim(lex.getString());
@@ -269,6 +303,18 @@ void ParagraphParameters::write(ostream & os) const
if (startOfAppendix())
os << "\\start_of_appendix\n";
+ // Start of front matter?
+ if (startOfMatter() != MATTER_NONE) {
+ string tmp;
+ if (startOfMatter() == MATTER_FRONT)
+ tmp = "front";
+ else if (startOfMatter() == MATTER_MAIN)
+ tmp = "main";
+ else if (startOfMatter() == MATTER_BACK)
+ tmp = "back";
+ os << "\\start_of_matter " << tmp << "\n";
+ }
+
// Noindent?
if (noindent())
os << "\\noindent\n";
diff --git a/src/ParagraphParameters.h b/src/ParagraphParameters.h
index 540ea95679..9ff42b0cf2 100644
--- a/src/ParagraphParameters.h
+++ b/src/ParagraphParameters.h
@@ -63,6 +63,14 @@ public:
///
void appendix(bool);
///
+ Matter startOfMatter() const;
+ ///
+ void startOfMatter(Matter);
+ ///
+ bool mainMatter() const;
+ ///
+ void mainMatter(bool);
+ ///
docstring const & labelString() const;
///
void labelString(docstring const &);
@@ -102,6 +110,10 @@ private:
///
bool appendix_;
///
+ Matter start_of_matter_;
+ ///
+ bool main_matter_;
+ ///
LyXAlignment align_;
///
depth_type depth_;
diff --git a/src/RowPainter.cpp b/src/RowPainter.cpp
index 6d2dd5ec65..8a6a5a6800 100644
--- a/src/RowPainter.cpp
+++ b/src/RowPainter.cpp
@@ -30,6 +30,7 @@
#include "ParagraphList.h"
#include "ParagraphParameters.h"
#include "Text.h"
+#include "TextClass.h"
#include "TextMetrics.h"
#include "frontends/FontMetrics.h"
@@ -324,12 +325,14 @@ void RowPainter::paintChangeBar() const
void RowPainter::paintAppendix() const
{
// only draw the appendix frame once (for the main text)
- if (!par_.params().appendix() || !text_.isMainText())
+ if ((!par_.params().appendix() && par_.params().mainMatter())
+ || !text_.isMainText())
return;
int y = yo_ - row_.ascent();
- if (par_.params().startOfAppendix())
+ if (par_.params().startOfAppendix()
+ || par_.params().startOfMatter() != MATTER_NONE)
y += 2 * defaultRowHeight();
pi_.pain.line(1, y, 1, yo_ + row_.descent(), Color_appendix);
@@ -381,7 +384,7 @@ void RowPainter::paintDepthBar() const
}
-void RowPainter::paintAppendixStart(int y) const
+void RowPainter::paintMatterStart(int y, docstring const & label) const
{
FontInfo pb_font = sane_font;
pb_font.setColor(Color_appendix);
@@ -391,7 +394,6 @@ void RowPainter::paintAppendixStart(int y) const
int a = 0;
int d = 0;
- docstring const label = _("Appendix");
theFontMetrics(pb_font).rectText(label, w, a, d);
int const text_start = int(xo_ + (tm_.width() - w) / 2);
@@ -423,21 +425,46 @@ void RowPainter::paintTooLargeMarks(bool const left, bool const right) const
void RowPainter::paintFirst() const
{
+ Layout const & TOCLayout = pi_.base.bv->buffer().params().documentClass().getTOCLayout();
Layout const & layout = par_.layout();
+ docstring label;
// start of appendix?
if (par_.params().startOfAppendix())
- paintAppendixStart(yo_ - row_.ascent() + 2 * defaultRowHeight());
+ label = _("Appendix");
+ // start of matter?
+ if (par_.params().startOfMatter() != MATTER_NONE) {
+ if (!label.empty())
+ label += " & ";
+ switch (par_.params().startOfMatter()) {
+ case MATTER_FRONT:
+ label += _("Front Matter");
+ break;
+ case MATTER_MAIN:
+ label += _("Main Matter");
+ break;
+ case MATTER_BACK:
+ label += _("Back Matter");
+ break;
+ }
+ // Matters are supported only by book (sectioning toplevel 0)
+ if (TOCLayout.toclevel != 0)
+ label += _(" (Senseless!!!)");
+ }
+ if (!label.empty())
+ paintMatterStart(yo_ - row_.ascent() + 2 * defaultRowHeight(), label);
bool const is_first =
text_.isFirstInSequence(row_.pit()) || !layout.isParagraphGroup();
//lyxerr << "paintFirst: " << par_.id() << " is_seq: " << is_seq << endl;
- if (layout.labelIsInline()
- && (layout.labeltype != LABEL_STATIC || is_first))
- paintLabel();
- else if (is_first && layout.labelIsAbove())
- paintTopLevelLabel();
+ if (par_.layout().toclevel != 0 || par_.params().mainMatter()) {
+ if (layout.labelIsInline()
+ && (layout.labeltype != LABEL_STATIC || is_first))
+ paintLabel();
+ else if (is_first && layout.labelIsAbove())
+ paintTopLevelLabel();
+ }
}
diff --git a/src/RowPainter.h b/src/RowPainter.h
index 918fc9f989..ce77554ac5 100644
--- a/src/RowPainter.h
+++ b/src/RowPainter.h
@@ -54,7 +54,7 @@ private:
void paintTextDecoration(Row::Element const & e) const;
void paintMisspelledMark(Row::Element const & e) const;
void paintChange(Row::Element const & e) const;
- void paintAppendixStart(int y) const;
+ void paintMatterStart(int y, docstring const & label) const;
void paintInset(Row::Element const & e) const;
/// return the label font for this row (end label when \c end is true)
diff --git a/src/Text.cpp b/src/Text.cpp
index 22e1c2c87b..0a8be834fe 100644
--- a/src/Text.cpp
+++ b/src/Text.cpp
@@ -798,9 +798,11 @@ static void breakParagraph(Text & text, pit_type par_offset, pos_type pos,
if (!isempty) {
bool const soa = par.params().startOfAppendix();
+ Matter const som = par.params().startOfMatter();
par.params().clear();
// do not lose start of appendix marker (bug 4212)
par.params().startOfAppendix(soa);
+ par.params().startOfMatter(som);
par.setPlainOrDefaultLayout(bparams.documentClass());
}
@@ -3294,10 +3296,13 @@ bool Text::deleteEmptyParagraphMechanism(Cursor & cur,
min(old.pit() + 1, old.lastpit()));
ParagraphList & plist = old.text()->paragraphs();
bool const soa = oldpar.params().startOfAppendix();
+ Matter const som = oldpar.params().startOfMatter();
plist.erase(plist.iterator_at(old.pit()));
// do not lose start of appendix marker (bug 4212)
if (soa && old.pit() < pit_type(plist.size()))
plist[old.pit()].params().startOfAppendix(true);
+ if (som != MATTER_NONE && old.pit() < pit_type(plist.size()))
+ plist[old.pit()].params().startOfMatter(som);
// see #warning (FIXME?) above
if (cur.depth() >= old.depth()) {
@@ -4135,6 +4140,27 @@ void Text::dispatch(Cursor & cur, FuncRequest & cmd)
break;
}
+ case LFUN_MATTER: {
+ Paragraph & par = cur.paragraph();
+ docstring const & arg = cmd.argument();
+ Matter start = MATTER_NONE;
+ if (arg == "front") {
+ start = par.params().startOfMatter() == MATTER_FRONT
+ ? MATTER_NONE : MATTER_FRONT;
+ } else if (arg == "main") {
+ start = par.params().startOfMatter() == MATTER_MAIN
+ ? MATTER_NONE : MATTER_MAIN;
+ } else if (arg == "back") {
+ start = par.params().startOfMatter() == MATTER_BACK
+ ? MATTER_NONE : MATTER_BACK;
+ }
+ cur.recordUndo();
+ par.params().startOfMatter(start);
+ // we can set the refreshing parameters now
+ cur.forceBufferUpdate();
+ break;
+ }
+
case LFUN_WORD_DELETE_FORWARD:
if (cur.selection())
cutSelection(cur, false);
@@ -6396,6 +6422,24 @@ bool Text::getStatus(Cursor & cur, FuncRequest const & cmd,
status.setOnOff(cur.paragraph().params().startOfAppendix());
break;
+ case LFUN_MATTER:
+ if (!cur.buffer()->params().documentClass().hasMatters()) {
+ enable = false;
+ break;
+ }
+ // FIXME We really should not allow this to be put, e.g.,
+ // in a footnote, or in ERT. But it would make sense in a
+ // branch, so I'm not sure what to do.
+ if (cmd.argument() == "front")
+ status.setOnOff(cur.paragraph().params().startOfMatter() == MATTER_FRONT);
+ else if (cmd.argument() == "main")
+ status.setOnOff(cur.paragraph().params().startOfMatter() == MATTER_MAIN);
+ else if (cmd.argument() == "back")
+ status.setOnOff(cur.paragraph().params().startOfMatter() == MATTER_BACK);
+ else
+ enable = false;
+ break;
+
case LFUN_DIALOG_SHOW_NEW_INSET:
if (cmd.argument() == "bibitem")
code = BIBITEM_CODE;
diff --git a/src/TextClass.cpp b/src/TextClass.cpp
index d908473aff..c0477533e3 100644
--- a/src/TextClass.cpp
+++ b/src/TextClass.cpp
@@ -60,7 +60,7 @@ namespace lyx {
// You should also run the development/tools/updatelayouts.py script,
// to update the format of all of our layout files.
//
-int const LAYOUT_FORMAT = 119; // spitz: ParfillSkip
+int const LAYOUT_FORMAT = 120; // spitz: ProvidesMatters
// Layout format for the current lyx file format. Controls which format is
@@ -142,7 +142,7 @@ TextClass::TextClass()
columns_(1), sides_(OneSide), secnumdepth_(3), tocdepth_(3), outputType_(LATEX),
outputFormat_("latex"), has_output_format_(false), defaultfont_(sane_font),
titletype_(TITLE_COMMAND_AFTER), titlename_("maketitle"),
- min_toclevel_(0), max_toclevel_(0), maxcitenames_(2),
+ min_toclevel_(0), max_toclevel_(0), has_matters_(false), maxcitenames_(2),
cite_full_author_list_(true), bibintoc_(false)
{}
@@ -185,6 +185,7 @@ enum TextClassTags {
TC_DEFAULTFONT,
TC_SECNUMDEPTH,
TC_TOCDEPTH,
+ TC_PROVIDESMATTERS,
TC_CLASSOPTIONS,
TC_SPECIALCHARS,
TC_PREAMBLE,
@@ -278,6 +279,7 @@ LexerKeyword textClassTags[] = {
{ "preamble", TC_PREAMBLE },
{ "provideinsetlayout", TC_PROVIDEINSETLAYOUT },
{ "provides", TC_PROVIDES },
+ { "providesmatters", TC_PROVIDESMATTERS },
{ "providesmodule", TC_PROVIDESMODULE },
{ "providestyle", TC_PROVIDESTYLE },
{ "requires", TC_REQUIRES },
@@ -661,6 +663,11 @@ TextClass::ReturnValues TextClass::read(Lexer & lexrc, ReadType rt)
tocdepth_ = lexrc.getInteger();
break;
+ case TC_PROVIDESMATTERS:
+ if (lexrc.next())
+ has_matters_ = lexrc.getBool();
+ break;
+
// First step to support options
case TC_CLASSOPTIONS:
readClassOptions(lexrc);
diff --git a/src/TextClass.h b/src/TextClass.h
index f7673b10d8..e8bf0d768e 100644
--- a/src/TextClass.h
+++ b/src/TextClass.h
@@ -209,6 +209,8 @@ public:
std::string const & outputFormat() const { return outputFormat_; }
/// Does this class redefine the output format?
bool hasOutputFormat() const { return has_output_format_; }
+ /// Does this class provide \forntmatter, \mainmatter, and \backmatter?
+ bool hasMatters() const { return has_matters_; }
/// Return the non-localised names for the toc types.
std::map<std::string, docstring> const &
outlinerNames() const { return outliner_names_; }
@@ -368,6 +370,8 @@ protected:
int min_toclevel_;
/// The maximal TocLevel of sectioning layouts
int max_toclevel_;
+ /// Does this class provide \frontmatter, \mainmatter, and \backmatter?
+ bool has_matters_;
/// Citation formatting information
std::map<CiteEngineType, std::map<std::string, std::string> > cite_formats_;
/// Citation macros
diff --git a/src/TextMetrics.cpp b/src/TextMetrics.cpp
index fe4f775809..06e3fce2db 100644
--- a/src/TextMetrics.cpp
+++ b/src/TextMetrics.cpp
@@ -1309,14 +1309,15 @@ int TextMetrics::parTopSpacing(pit_type const pit) const
&& pars[pit - 1].getDepth() == 0))) {
asc += bparams.getDefSkip().inPixels(*bv_);
}
-
- if (par.params().startOfAppendix())
+ // extra row height for start of appendix and matters
+ if (par.params().startOfAppendix() || par.params().startOfMatter() != MATTER_NONE)
asc += int(3 * dh);
// special code for the top label
if (layout.labelIsAbove()
&& (!layout.isParagraphGroup() || text_->isFirstInSequence(pit))
- && !par.labelString().empty()) {
+ && !par.labelString().empty()
+ && (layout.toclevel != 0 || par.params().mainMatter())) {
FontInfo labelfont = text_->labelFont(par);
FontMetrics const & lfm = theFontMetrics(labelfont);
asc += int(lfm.maxHeight() * layout.spacing.getValue()
diff --git a/src/output_latex.cpp b/src/output_latex.cpp
index 94ed36b3f3..8aef8c75d0 100644
--- a/src/output_latex.cpp
+++ b/src/output_latex.cpp
@@ -1237,6 +1237,21 @@ void TeXOnePar(Buffer const & buf,
if (par.params().startOfAppendix()) {
os << "\n\\appendix\n";
}
+ if (bparams.documentClass().hasMatters()) {
+ switch (par.params().startOfMatter()) {
+ case MATTER_FRONT:
+ os << "\n\\frontmatter\n";
+ break;
+ case MATTER_MAIN:
+ os << "\n\\mainmatter\n";
+ break;
+ case MATTER_BACK:
+ os << "\n\\backmatter\n";
+ break;
+ default:
+ break;
+ }
+ }
// InTitle commands must use switches (not environments)
// inside the commands (see #9332)
diff --git a/src/tex2lyx/text.cpp b/src/tex2lyx/text.cpp
index a8ebbf9364..31b509ffcc 100644
--- a/src/tex2lyx/text.cpp
+++ b/src/tex2lyx/text.cpp
@@ -3945,6 +3945,78 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
continue;
}
+ if (t.cs() == "frontmatter") {
+ context.add_par_extra_stuff("\\start_of_matter front\n");
+ // We need to start a new paragraph. Otherwise the
+ // frontmatter in 'bla\frontmatter\chapter{' would start
+ // too late.
+ context.new_paragraph(os);
+ // We need to make sure that the paragraph is
+ // generated even if it is empty. Otherwise the
+ // frontmatter in '\par\frontmatter\par\chapter{' would
+ // start too late.
+ context.check_layout(os);
+ // FIXME: This is a hack to prevent paragraph
+ // deletion if it is empty. Handle this better!
+ output_comment(p, os,
+ "dummy comment inserted by tex2lyx to "
+ "ensure that this paragraph is not empty",
+ context);
+ // Both measures above may generate an additional
+ // empty paragraph, but that does not hurt, because
+ // whitespace does not matter here.
+ eat_whitespace(p, os, context, true);
+ continue;
+ }
+
+ if (t.cs() == "mainmatter") {
+ context.add_par_extra_stuff("\\start_of_matter main\n");
+ // We need to start a new paragraph. Otherwise the
+ // mainmatter in 'bla\mainmatter\chapter{' would start
+ // too late.
+ context.new_paragraph(os);
+ // We need to make sure that the paragraph is
+ // generated even if it is empty. Otherwise the
+ // mainmatter in '\par\mainmatter\par\chapter{' would
+ // start too late.
+ context.check_layout(os);
+ // FIXME: This is a hack to prevent paragraph
+ // deletion if it is empty. Handle this better!
+ output_comment(p, os,
+ "dummy comment inserted by tex2lyx to "
+ "ensure that this paragraph is not empty",
+ context);
+ // Both measures above may generate an additional
+ // empty paragraph, but that does not hurt, because
+ // whitespace does not matter here.
+ eat_whitespace(p, os, context, true);
+ continue;
+ }
+
+ if (t.cs() == "backmatter") {
+ context.add_par_extra_stuff("\\start_of_matter back\n");
+ // We need to start a new paragraph. Otherwise the
+ // mainmatter in 'bla\mainmatter\chapter{' would start
+ // too late.
+ context.new_paragraph(os);
+ // We need to make sure that the paragraph is
+ // generated even if it is empty. Otherwise the
+ // mainmatter in '\par\mainmatter\par\chapter{' would
+ // start too late.
+ context.check_layout(os);
+ // FIXME: This is a hack to prevent paragraph
+ // deletion if it is empty. Handle this better!
+ output_comment(p, os,
+ "dummy comment inserted by tex2lyx to "
+ "ensure that this paragraph is not empty",
+ context);
+ // Both measures above may generate an additional
+ // empty paragraph, but that does not hurt, because
+ // whitespace does not matter here.
+ eat_whitespace(p, os, context, true);
+ continue;
+ }
+
// Must catch empty dates before findLayout is called below
if (t.cs() == "date") {
eat_whitespace(p, os, context, false);
diff --git a/src/version.h b/src/version.h
index cd47b6268d..93eeff04a6 100644
--- a/src/version.h
+++ b/src/version.h
@@ -32,8 +32,8 @@ extern char const * const lyx_version_info;
// Do not remove the comment below, so we get merge conflict in
// independent branches. Instead add your own.
-#define LYX_FORMAT_LYX 656 // spitz: parfillskip
-#define LYX_FORMAT_TEX2LYX 656
+#define LYX_FORMAT_LYX 657 // spitz: matters
+#define LYX_FORMAT_TEX2LYX 657
#if LYX_FORMAT_TEX2LYX != LYX_FORMAT_LYX
#ifndef _MSC_VER
More information about the lyx-cvs
mailing list