[LyX/master] Add support for parfill(skip) (#12949)

Juergen Spitzmueller spitz at lyx.org
Sun Aug 2 10:22:25 UTC 2026


commit d497dde1e06809c6caa3b2bb4af647082bfa480a
Author: Juergen Spitzmueller <spitz at lyx.org>
Date:   Sun Aug 2 12:21:50 2026 +0200

    Add support for parfill(skip) (#12949)
    
    File format change
---
 development/FORMAT                  |   7 ++
 lib/doc/Customization.lyx           | 243 ++++++++++++++++++++++++++++++++++++
 lib/doc/de/Customization.lyx        | 181 +++++++++++++++++++++++++++
 lib/layouts/scrclass.inc            |   3 +
 lib/lyx2lyx/lyx_2_6.py              |  38 +++++-
 src/BufferParams.cpp                | 138 +++++++++++++++++++-
 src/BufferParams.h                  |  22 ++++
 src/TextClass.cpp                   |  19 +++
 src/TextClass.h                     |  12 ++
 src/frontends/qt/GuiDocument.cpp    |  49 ++++++++
 src/frontends/qt/GuiDocument.h      |   1 +
 src/frontends/qt/LengthCombo.cpp    |   5 +-
 src/frontends/qt/ui/TextLayoutUi.ui |  79 ++++++++++--
 src/support/Length.cpp              |   2 +
 src/support/Length.h                |   1 +
 src/support/lengthcommon.cpp        |   4 +-
 src/version.h                       |   4 +-
 17 files changed, 791 insertions(+), 17 deletions(-)

diff --git a/development/FORMAT b/development/FORMAT
index 0e9e95f012..21bad579ed 100644
--- a/development/FORMAT
+++ b/development/FORMAT
@@ -7,6 +7,13 @@ changes happened in particular if possible. A good example would be
 
 -----------------------
 
+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>"
+	  where <type> is either of "default", "none", "thirdline",
+	  "quarterline" or "custom", and <glue length> is only used
+	  in the custom case (otherwise an empty string).
+
 2026-07-30 Koji Yokota <yokota at lyx.org>
 	* Format incremented to 655:
 	  Support ARGB colors. \color now takes 32bit hexnames instead of 16bit
diff --git a/lib/doc/Customization.lyx b/lib/doc/Customization.lyx
index 6d66f45aa2..c4e396b38f 100644
--- a/lib/doc/Customization.lyx
+++ b/lib/doc/Customization.lyx
@@ -12593,6 +12593,243 @@ parskip=half
 parskip
 \emph default
  package.
+\change_inserted -712698321 1785664297
+
+\end_layout
+
+\begin_layout Description
+
+\change_inserted -712698321 1785665160
+\begin_inset Flex Code
+status collapsed
+
+\begin_layout Plain Layout
+
+\change_inserted -712698321 1785664317
+ParfillskipNone
+\end_layout
+
+\end_inset
+
+ [
+\begin_inset Flex Code
+status collapsed
+
+\begin_layout Plain Layout
+
+\change_inserted -712698321 1785664297
+string
+\end_layout
+
+\end_inset
+
+] If the class has an option to set 
+\begin_inset Flex Code
+status collapsed
+
+\begin_layout Plain Layout
+
+\change_inserted -712698321 1785665160
+parfillskip
+\change_unchanged
+
+\end_layout
+
+\end_inset
+
+ to zero,
+ specify it here (e.g.,
+ 
+\begin_inset Flex Code
+status collapsed
+
+\begin_layout Plain Layout
+
+\change_inserted -712698321 1785664387
+parfullskip=none
+\end_layout
+
+\end_inset
+
+).
+ This then outputs the option rather than using the 
+\emph on
+parskip
+\emph default
+ package.
+ If the option is combined with the parskip option (as in KOMA classes),
+ you can use the 
+\begin_inset Flex Code
+status collapsed
+
+\begin_layout Plain Layout
+
+\change_inserted -712698321 1785664524
+$$PSOPT
+\change_unchanged
+
+\end_layout
+
+\end_inset
+
+ placeholder (e.g.,
+ 
+\begin_inset Flex Code
+status collapsed
+
+\begin_layout Plain Layout
+
+\change_inserted -712698321 1785664534
+$$PSOPT-
+\change_unchanged
+
+\end_layout
+
+\end_inset
+
+ in KOMA results in 
+\begin_inset Flex Code
+status collapsed
+
+\begin_layout Plain Layout
+
+\change_inserted -712698321 1785664542
+parskip=full-
+\change_unchanged
+
+\end_layout
+
+\end_inset
+
+ together with 
+\begin_inset Flex Code
+status collapsed
+
+\begin_layout Plain Layout
+
+\change_inserted -712698321 1785664507
+ParskipFull parskip=full
+\end_layout
+
+\end_inset
+
+).
+\change_unchanged
+
+\end_layout
+
+\begin_layout Description
+
+\change_inserted -712698321 1785665172
+\begin_inset Flex Code
+status collapsed
+
+\begin_layout Plain Layout
+
+\change_inserted -712698321 1785664326
+ParfillskipQuarter
+\end_layout
+
+\end_inset
+
+ [
+\begin_inset Flex Code
+status collapsed
+
+\begin_layout Plain Layout
+
+\change_inserted -712698321 1785664297
+string
+\end_layout
+
+\end_inset
+
+] Similar than 
+\begin_inset Flex Code
+status collapsed
+
+\begin_layout Plain Layout
+
+\change_inserted -712698321 1785664574
+ParfillskipNone
+\end_layout
+
+\end_inset
+
+,
+ but for 
+\begin_inset Flex Code
+status collapsed
+
+\begin_layout Plain Layout
+
+\change_inserted -712698321 1785665172
+parfillskip
+\change_unchanged
+
+\end_layout
+
+\end_inset
+
+ of a quarter of the line.
+\end_layout
+
+\begin_layout Description
+
+\change_inserted -712698321 1785665179
+\begin_inset Flex Code
+status collapsed
+
+\begin_layout Plain Layout
+
+\change_inserted -712698321 1785664337
+ParfillskipThird
+\end_layout
+
+\end_inset
+
+ [
+\begin_inset Flex Code
+status collapsed
+
+\begin_layout Plain Layout
+
+\change_inserted -712698321 1785664331
+string
+\end_layout
+
+\end_inset
+
+] Similar than 
+\begin_inset Flex Code
+status collapsed
+
+\begin_layout Plain Layout
+
+\change_inserted -712698321 1785664602
+ParfillskipNone
+\end_layout
+
+\end_inset
+
+,
+ but for 
+\begin_inset Flex Code
+status collapsed
+
+\begin_layout Plain Layout
+
+\change_inserted -712698321 1785665179
+parfillskip
+\change_unchanged
+
+\end_layout
+
+\end_inset
+
+ of a third of the line.
+\change_unchanged
+
 \end_layout
 
 \begin_layout Description
@@ -24355,6 +24592,8 @@ status collapsed
 
 \change_inserted -712698321 1783145646
 IsInherited
+\change_unchanged
+
 \end_layout
 
 \end_inset
@@ -24369,6 +24608,8 @@ status collapsed
 
 \emph on
 0
+\change_unchanged
+
 \end_layout
 
 \end_inset
@@ -24385,6 +24626,8 @@ status collapsed
 
 \change_inserted -712698321 1783145638
 1
+\change_unchanged
+
 \end_layout
 
 \end_inset
diff --git a/lib/doc/de/Customization.lyx b/lib/doc/de/Customization.lyx
index 748c8b27b2..5efed2f20e 100644
--- a/lib/doc/de/Customization.lyx
+++ b/lib/doc/de/Customization.lyx
@@ -11055,6 +11055,187 @@ parskip
  diese Option verwendet.
 \end_layout
 
+\begin_layout Description
+\begin_inset Flex Code
+status collapsed
+
+\begin_layout Plain Layout
+ParfillskipNone
+\end_layout
+
+\end_inset
+
+ [
+\begin_inset Flex Code
+status collapsed
+
+\begin_layout Plain Layout
+string
+\end_layout
+
+\end_inset
+
+] Falls die Dokumentklasse eine Option zum Setzen des Werts von 
+\begin_inset Flex Code
+status collapsed
+
+\begin_layout Plain Layout
+parfillskip
+\end_layout
+
+\end_inset
+
+ auf Null,
+ geben Sie diese hier an (bspw.
+ 
+\begin_inset Flex Code
+status collapsed
+
+\begin_layout Plain Layout
+parfullskip=none
+\end_layout
+
+\end_inset
+
+).
+ Dann wird statt des Pakets 
+\emph on
+parskip
+\emph default
+ diese Option verwendet.
+ Falls die Option mit der Parskip-Option kombiniert ist (wie in den KOMA-Klassen),
+ können Sie den Platzhalter 
+\begin_inset Flex Code
+status collapsed
+
+\begin_layout Plain Layout
+$$PSOPT
+\end_layout
+
+\end_inset
+
+ verwenden (bspw.
+ resultiert 
+\begin_inset Flex Code
+status collapsed
+
+\begin_layout Plain Layout
+$$PSOPT-
+\end_layout
+
+\end_inset
+
+ in KOMA zusammen 
+\begin_inset Flex Code
+status collapsed
+
+\begin_layout Plain Layout
+ParskipFull parskip=full
+\end_layout
+
+\end_inset
+
+ in 
+\begin_inset Flex Code
+status collapsed
+
+\begin_layout Plain Layout
+parskip=full-
+\end_layout
+
+\end_inset
+
+ ).
+\end_layout
+
+\begin_layout Description
+\begin_inset Flex Code
+status collapsed
+
+\begin_layout Plain Layout
+ParfillskipQuarter
+\end_layout
+
+\end_inset
+
+ [
+\begin_inset Flex Code
+status collapsed
+
+\begin_layout Plain Layout
+string
+\end_layout
+
+\end_inset
+
+] Ähnlich wie 
+\begin_inset Flex Code
+status collapsed
+
+\begin_layout Plain Layout
+ParfillskipNone
+\end_layout
+
+\end_inset
+
+,
+ aber für 
+\begin_inset Flex Code
+status collapsed
+
+\begin_layout Plain Layout
+parfillskip
+\end_layout
+
+\end_inset
+
+ mit einem Viertel der Zeile.
+\end_layout
+
+\begin_layout Description
+\begin_inset Flex Code
+status collapsed
+
+\begin_layout Plain Layout
+ParfillskipThird
+\end_layout
+
+\end_inset
+
+ [
+\begin_inset Flex Code
+status collapsed
+
+\begin_layout Plain Layout
+string
+\end_layout
+
+\end_inset
+
+] Ähnlich wie 
+\begin_inset Flex Code
+status collapsed
+
+\begin_layout Plain Layout
+ParfillskipNone
+\end_layout
+
+\end_inset
+
+,
+ aber für 
+\begin_inset Flex Code
+status collapsed
+
+\begin_layout Plain Layout
+parfillskip
+\end_layout
+
+\end_inset
+
+ mit einem Drittel der Zeile.
+\end_layout
+
 \begin_layout Standard
 Der Abschnitt 
 \begin_inset Flex Code
diff --git a/lib/layouts/scrclass.inc b/lib/layouts/scrclass.inc
index 6d989d390f..ff6f322347 100644
--- a/lib/layouts/scrclass.inc
+++ b/lib/layouts/scrclass.inc
@@ -23,6 +23,9 @@ ClassOptions
 	PageSizeFormat       paper=$$s
 	ParskipHalf          parskip=half
 	ParskipFull          parskip=full
+	ParfillskipNone      $$PSOPT-
+	ParfillskipQuarter   $$PSOPT*
+	ParfillskipThird     $$PSOPT+
 End
 
 Style Standard
diff --git a/lib/lyx2lyx/lyx_2_6.py b/lib/lyx2lyx/lyx_2_6.py
index 68969a9191..5eed5cbe05 100644
--- a/lib/lyx2lyx/lyx_2_6.py
+++ b/lib/lyx2lyx/lyx_2_6.py
@@ -26,7 +26,6 @@ import unicodedata
 #    get_ert,
 #    hex2ratio,
 #    insert_to_preamble,
-#    latex_length,
 #    length_in_bp,
 #    lyx2latex,
 #    lyx2verbatim,
@@ -37,6 +36,7 @@ import unicodedata
 #    str2bool
 from lyx2lyx_tools import (
     add_to_preamble,
+    latex_length,
     put_cmd_in_ert
 )
 
@@ -1525,6 +1525,7 @@ def revert_mintedlangs(document):
             lps_res.append(l)
         document.body[k] = "lstparams \"%s\"" % ",".join(lps_res)
 
+
 def revert_hex32colors(document):
     """Revert 32bit ARGB hex colors to 16bit RGB"""
     i = 0
@@ -1568,6 +1569,37 @@ def revert_hex32colors(document):
         document.body[i] = "\\color %s" % ' '.join(hex16colors)
 
 
+def revert_parfillskip(document):
+    """Revert parfillskip setting to preamble code"""
+    i = find_token(document.header, "\\parfillskip", 0)
+    if i == -1:
+        return
+
+    # We need to use this regex since split() does not handle quote protection
+    parfillskip = [p for p in re.split("( |\\\".*?\\\"|'.*?')", document.header[i]) if p.strip()]
+    if len(parfillskip) == 0:
+        return
+
+    pfs_type = parfillskip[1].strip('"')
+    pfs_value = parfillskip[2].strip('"')
+    
+    del document.header[i]
+
+    if pfs_type == "default":
+        return
+    preamble = ""
+    if pfs_type == "none":
+        preamble = "\\setlength\\parfillskip{\\z@ plus 1fil}"
+    elif pfs_type == "thirdline":
+        preamble = "\\setlength\\parfillskip{.3333\\linewidth plus 1fil}"
+    elif pfs_type == "quarterline":
+        preamble = "\\setlength\\parfillskip{.25\\linewidth plus 1fil}"
+    elif pfs_type == "custom":
+        preamble = "\\setlength\\parfillskip{%s}" % latex_length(pfs_value)[1]
+
+    if preamble != "":
+        add_to_preamble(document, preamble)
+
 ##
 # Conversion hub
 #
@@ -1585,11 +1617,13 @@ convert = [
     [652, [convert_removed_numeric]],
     [653, []],
     [654, []],
-    [655, []]
+    [655, []],
+    [656, []]
 ]
 
 
 revert = [
+    [655, [revert_parfillskip]],
     [654, [revert_hex32colors]],
     [653, [revert_mintedlangs]],
     [652, [revert_specialfonts]],
diff --git a/src/BufferParams.cpp b/src/BufferParams.cpp
index 8714dddb4a..206a231481 100644
--- a/src/BufferParams.cpp
+++ b/src/BufferParams.cpp
@@ -75,6 +75,11 @@ static char const * const string_paragraph_separation[] = {
 };
 
 
+static char const * const string_parfill_skip[] = {
+	"default", "none", "thirdline", "quarterline", "custom", ""
+};
+
+
 static char const * const string_quotes_style[] = {
 	"english", "swedish", "german", "polish", "swiss", "danish", "plain",
 	"british", "swedishg", "french", "frenchin", "russian", "cjk", "cjkangle",
@@ -140,6 +145,30 @@ ParSepTranslator const & parseptranslator()
 }
 
 
+// ParfillsKIP
+typedef Translator<string, BufferParams::ParfillSkip> ParfillSkipTranslator;
+
+
+ParfillSkipTranslator const init_parfillskiptranslator()
+{
+	ParfillSkipTranslator translator
+		(string_parfill_skip[0], BufferParams::ParfillSkipDefault);
+	translator.addPair(string_parfill_skip[1], BufferParams::ParfillSkipNone);
+	translator.addPair(string_parfill_skip[2], BufferParams::ParfillSkipThirdLine);
+	translator.addPair(string_parfill_skip[3], BufferParams::ParfillSkipQuarterLine);
+	translator.addPair(string_parfill_skip[4], BufferParams::ParfillSkipCustom);
+	return translator;
+}
+
+
+ParfillSkipTranslator const & parfillskiptranslator()
+{
+	static ParfillSkipTranslator const translator =
+		init_parfillskiptranslator();
+	return translator;
+}
+
+
 // Quotes style
 typedef Translator<string, QuoteStyle> QuotesStyleTranslator;
 
@@ -353,6 +382,7 @@ public:
 	IndicesList indiceslist;
 	Spacing spacing;
 	Length parindent;
+	GlueLength parfillskip;
 	Length mathindent;
 	/** This is the amount of space used for paragraph_separation "skip",
 	 * and for detached paragraphs in "indented" documents.
@@ -404,6 +434,7 @@ BufferParams::BufferParams()
 	cite_engine_type_ = ENGINE_TYPE_DEFAULT;
 	makeDocumentClass();
 	paragraph_separation = ParagraphIndentSeparation;
+	parfill_skip = ParfillSkipDefault;
 	is_math_indent = false;
 	math_numbering_side = DEFAULT;
 	quotes_style = QuoteStyle::English;
@@ -736,6 +767,41 @@ void BufferParams::setParIndent(Length const & indent)
 }
 
 
+GlueLength const & BufferParams::getParfillSkip() const
+{
+	return pimpl_->parfillskip;
+}
+
+
+string const BufferParams::getParfillSkipString() const
+{
+	string res;
+	switch (parfill_skip) {
+	case ParfillSkipDefault:
+		break;
+	case ParfillSkipNone:
+		res = "\\z@ plus 1fil";
+		break;
+	case ParfillSkipThirdLine:
+		res = ".3333\\linewidth plus 1fil";
+		break;
+	case ParfillSkipQuarterLine:
+		res = ".25\\linewidth plus 1fil";
+		break;
+	case ParfillSkipCustom:
+		res = getParfillSkip().asLatexString();
+		break;
+	}
+	return res;
+}
+
+
+void BufferParams::setParFillSkip(GlueLength const & skip)
+{
+	pimpl_->parfillskip = skip;
+}
+
+
 VSpace const & BufferParams::getDefSkip() const
 {
 	return pimpl_->defskip;
@@ -1181,6 +1247,18 @@ string BufferParams::readToken(Lexer & lex, string const & token,
 				pimpl_->defskip = VSpace(VSpace::MEDSKIP);
 			break;
 		}
+		if (token == "\\parfillskip") {
+			string type;
+			lex >> type;
+			string val;
+			lex >> val;
+			parfill_skip = parfillskiptranslator().find(type);
+			if (parfill_skip == ParfillSkipCustom)
+				setParFillSkip(GlueLength(val));
+			else
+				setParFillSkip(GlueLength());
+			break;
+		}
 		if (token == "\\is_math_indent") {
 			lex >> is_math_indent;
 			break;
@@ -1993,6 +2071,7 @@ void BufferParams::writeFile(ostream & os, Buffer const * buf) const
 		   << (getParIndent().empty() ? "default" : getParIndent().asString());
 	else
 		os << "\n\\defskip " << getDefSkip().asLyXCommand();
+	os << "\n\\parfillskip \"" << string_parfill_skip[parfill_skip] << "\" \"" << getParfillSkip().asString() << "\"";
 	os << "\n\\is_math_indent " << is_math_indent;
 	if (is_math_indent)
 		os << "\n\\math_indentation "
@@ -2366,11 +2445,49 @@ bool BufferParams::writeLaTeX(otexstream & os, LaTeXFeatures & features,
 		break;
 	}
 
+	bool parfillskip_classopt = false;
 	if (paragraph_separation) {
+		string psopt;
 		if (!tclass.halfparskip().empty() && getDefSkip().kind() == VSpace::HALFLINE)
-			clsoptions << tclass.halfparskip() << ",";
+			psopt = tclass.halfparskip();
 		if (!tclass.fullparskip().empty() && getDefSkip().kind() == VSpace::FULLLINE)
-			clsoptions << tclass.fullparskip() << ",";
+			psopt = tclass.fullparskip();
+		switch (parfill_skip) {
+		case ParfillSkipNone: {
+			if (contains(tclass.noneparfillskip(), "$$PSOPT")) {
+				psopt = subst(tclass.noneparfillskip(), "$$PSOPT", psopt);
+				parfillskip_classopt = true;
+			} else if (!tclass.noneparfillskip().empty()) {
+				clsoptions << tclass.noneparfillskip() << ",";
+				parfillskip_classopt = true;
+			}
+			break;
+		}
+		case ParfillSkipThirdLine: {
+			if (contains(tclass.thirdparfillskip(), "$$PSOPT")) {
+				psopt = subst(tclass.thirdparfillskip(), "$$PSOPT", psopt);
+				parfillskip_classopt = true;
+			} else if (!tclass.thirdparfillskip().empty()) {
+				clsoptions << tclass.thirdparfillskip() << ",";
+				parfillskip_classopt = true;
+			}
+			break;
+		}
+		case ParfillSkipQuarterLine: {
+			if (contains(tclass.quarterparfillskip(), "$$PSOPT")) {
+				psopt = subst(tclass.quarterparfillskip(), "$$PSOPT", psopt);
+				parfillskip_classopt = true;
+			} else if (!tclass.quarterparfillskip().empty()) {
+				clsoptions << tclass.quarterparfillskip() << ",";
+				parfillskip_classopt = true;
+			}
+			break;
+		}
+		case ParfillSkipDefault:
+		case ParfillSkipCustom:
+			break;
+		}
+		clsoptions << psopt << ",";
 	}
 
 	// language should be a parameter to \documentclass
@@ -2748,6 +2865,10 @@ bool BufferParams::writeLaTeX(otexstream & os, LaTeXFeatures & features,
 				// explicitly reset default (might have been changed
 				// in a class or package)
 				os << "\\parskip=.5\\baselineskip plus 2pt\\relax\n";
+			// parfillskip
+			if (parfill_skip != ParfillSkipDefault)
+				os << "\\setlength{\\parfillskip}{" << getParfillSkipString() << "}\n";
+
 		} else if (!by_class_option) {
 			// load parskip package with required options
 			string psopts;
@@ -2758,6 +2879,16 @@ bool BufferParams::writeLaTeX(otexstream & os, LaTeXFeatures & features,
 				else
 					psopts = "skip=" + psopt;
 			}
+			string const parfill = getParfillSkipString();
+			if (!parfill.empty()) {
+				if (!psopts.empty())
+					psopts += ",";
+				if (contains(parfill, ' '))
+					// glue length has spaces: embrace
+					psopts += "parfill={" + parfill + "}";
+				else
+					psopts += "parfill=" + parfill;
+			}
 			string const xpsopts = getPackageOptions("parskip");
 			if (!xpsopts.empty()) {
 				if (!psopts.empty())
@@ -2777,6 +2908,9 @@ bool BufferParams::writeLaTeX(otexstream & os, LaTeXFeatures & features,
 			   << from_utf8(getParIndent().asLatexString())
 			   << "}\n";
 		}
+		// parfillskip
+		if (!parfillskip_classopt && parfill_skip != ParfillSkipDefault)
+			os << "\\setlength{\\parfillskip}{" << getParfillSkipString() << "}\n";
 	}
 
 	if (is_math_indent) {
diff --git a/src/BufferParams.h b/src/BufferParams.h
index 1da3f1cdc7..5a1a67341a 100644
--- a/src/BufferParams.h
+++ b/src/BufferParams.h
@@ -45,6 +45,7 @@ class DocumentClass;
 class Encoding;
 class Font;
 class Format;
+class GlueLength;
 class IndicesList;
 class Language;
 class LaTeXFeatures;
@@ -75,6 +76,19 @@ public:
 		ParagraphSkipSeparation
 	};
 	///
+	enum ParfillSkip {
+		///
+		ParfillSkipDefault,
+		///
+		ParfillSkipNone,
+		///
+		ParfillSkipThirdLine,
+		///
+		ParfillSkipQuarterLine,
+		///
+		ParfillSkipCustom
+	};
+	///
 	BufferParams();
 
 	/// get l10n translated to the buffers language
@@ -111,6 +125,10 @@ public:
 	VSpace const & getDefSkip() const;
 	///
 	void setDefSkip(VSpace const & vs);
+	///
+	GlueLength const & getParfillSkip() const;
+	///
+	void setParFillSkip(GlueLength const & skip);
 
 	///
 	Length const & getMathIndent() const;
@@ -137,6 +155,8 @@ public:
 	 */
 	ParagraphSeparation paragraph_separation;
 	///
+	ParfillSkip parfill_skip;
+	///
 	QuoteStyle quotes_style;
 	///
 	bool dynamic_quotes;
@@ -739,6 +759,8 @@ private:
 	std::string const getBibtexCommand(std::string const & cmd,
 					   bool const warn) const;
 	///
+	std::string const getParfillSkipString() const;
+	///
 	DocumentClassPtr doc_class_;
 	///
 	LayoutModuleList layout_modules_;
diff --git a/src/TextClass.cpp b/src/TextClass.cpp
index 87c50d7546..38107c75ec 100644
--- a/src/TextClass.cpp
+++ b/src/TextClass.cpp
@@ -137,6 +137,7 @@ TextClass::TextClass()
 	  opt_pagesize_("default|a4|a5|b5|letter|legal|executive"),
 	  opt_pagestyle_("empty|plain|headings|fancy"), fontsize_format_("$$spt"), pagesize_("default"),
 	  pagesize_format_("$$spaper"), pagestyle_("default"), parskip_full_(""), parskip_half_(""),
+	  parfillskip_none_(""), parfillskip_quarter_(""), parfillskip_third_(""),
 	  tablestyle_("default"), docbookroot_("article"), docbookforceabstract_(false),
 	  columns_(1), sides_(OneSide), secnumdepth_(3), tocdepth_(3), outputType_(LATEX),
 	  outputFormat_("latex"), has_output_format_(false), defaultfont_(sane_font), 
@@ -1091,6 +1092,9 @@ void TextClass::readClassOptions(Lexer & lexrc)
 		CO_PAGESIZE,
 		CO_PAGESIZE_FORMAT,
 		CO_PAGESTYLE,
+		CO_PARFILLSKIP_NONE,
+		CO_PARFILLSKIP_QUARTER,
+		CO_PARFILLSKIP_THIRD,
 		CO_PARSKIP_FULL,
 		CO_PARSKIP_HALF,
 		CO_OTHER,
@@ -1105,6 +1109,9 @@ void TextClass::readClassOptions(Lexer & lexrc)
 		{"pagesize",  CO_PAGESIZE },
 		{"pagesizeformat", CO_PAGESIZE_FORMAT },
 		{"pagestyle", CO_PAGESTYLE },
+		{"parfillskipnone", CO_PARFILLSKIP_NONE },
+		{"parfillskipquarter", CO_PARFILLSKIP_QUARTER },
+		{"parfillskipthird", CO_PARFILLSKIP_THIRD },
 		{"parskipfull", CO_PARSKIP_FULL },
 		{"parskiphalf", CO_PARSKIP_HALF }
 	};
@@ -1149,6 +1156,18 @@ void TextClass::readClassOptions(Lexer & lexrc)
 			lexrc.next();
 			parskip_half_ = rtrim(lexrc.getString());
 			break;
+		case CO_PARFILLSKIP_NONE:
+			lexrc.next();
+			parfillskip_none_ = rtrim(lexrc.getString());
+			break;
+		case CO_PARFILLSKIP_QUARTER:
+			lexrc.next();
+			parfillskip_quarter_ = rtrim(lexrc.getString());
+			break;
+		case CO_PARFILLSKIP_THIRD:
+			lexrc.next();
+			parfillskip_third_ = rtrim(lexrc.getString());
+			break;
 		case CO_OTHER:
 			lexrc.next();
 			if (options_.empty())
diff --git a/src/TextClass.h b/src/TextClass.h
index 9840a77237..f7673b10d8 100644
--- a/src/TextClass.h
+++ b/src/TextClass.h
@@ -292,6 +292,12 @@ protected:
 	std::string parskip_full_;
 	/// Option for half parskip
 	std::string parskip_half_;
+	/// Option for zero parfillskip
+	std::string parfillskip_none_;
+	/// Option for 1/4 parfillskip
+	std::string parfillskip_quarter_;
+	/// Option for 1/3 parfillskip
+	std::string parfillskip_third_;
 	///
 	std::string tablestyle_;
 	///
@@ -494,6 +500,12 @@ public:
 	///
 	std::string const & halfparskip() const { return parskip_half_; }
 	///
+	std::string const & noneparfillskip() const { return parfillskip_none_; }
+	///
+	std::string const & quarterparfillskip() const { return parfillskip_quarter_; }
+	///
+	std::string const & thirdparfillskip() const { return parfillskip_third_; }
+	///
 	std::string const & pagestyle() const { return pagestyle_; }
 	///
 	std::string const & tablestyle() const { return tablestyle_; }
diff --git a/src/frontends/qt/GuiDocument.cpp b/src/frontends/qt/GuiDocument.cpp
index aac142cbb2..eae5f727ca 100644
--- a/src/frontends/qt/GuiDocument.cpp
+++ b/src/frontends/qt/GuiDocument.cpp
@@ -885,6 +885,15 @@ GuiDocument::GuiDocument(GuiView & lv)
 	connect(textLayoutModule->skipLengthCO, SIGNAL(activated(int)),
 		this, SLOT(change_adaptor()));
 
+	connect(textLayoutModule->parfillskipCO, SIGNAL(activated(int)),
+		this, SLOT(setParfillSkip(int)));
+	connect(textLayoutModule->parfillskipCO, SIGNAL(activated(int)),
+		this, SLOT(change_adaptor()));
+	connect(textLayoutModule->parfillskipLE, SIGNAL(textChanged(const QString &)),
+		this, SLOT(change_adaptor()));
+	connect(textLayoutModule->parfillskipLengthCO, SIGNAL(activated(int)),
+		this, SLOT(change_adaptor()));
+
 	connect(textLayoutModule->indentRB, SIGNAL(toggled(bool)),
 		this, SLOT(enableIndent(bool)));
 	connect(textLayoutModule->skipRB, SIGNAL(toggled(bool)),
@@ -918,6 +927,15 @@ GuiDocument::GuiDocument(GuiView & lv)
 	textLayoutModule->skipCO->addItem(qt_("Medium Skip"), VSpace::MEDSKIP);
 	textLayoutModule->skipCO->addItem(qt_("Big Skip"), VSpace::BIGSKIP);
 	textLayoutModule->skipCO->addItem(qt_("Custom"), VSpace::LENGTH);
+
+	textLayoutModule->parfillskipCO->addItem(qt_("Default"), BufferParams::ParfillSkipDefault);
+	textLayoutModule->parfillskipCO->addItem(qt_("None[[Parfillskip]]"), BufferParams::ParfillSkipNone);
+	textLayoutModule->parfillskipCO->addItem(qt_("1/4 of Line"), BufferParams::ParfillSkipQuarterLine);
+	textLayoutModule->parfillskipCO->addItem(qt_("1/3 of Line"), BufferParams::ParfillSkipThirdLine);
+	textLayoutModule->parfillskipCO->addItem(qt_("Custom"), BufferParams::ParfillSkipCustom);
+	// parfillskip accepts glue length
+	textLayoutModule->parfillskipLE->setValidator(glueLengthValidator(textLayoutModule->parfillskipLE));
+
 	textLayoutModule->lspacingCO->insertItem(
 		Spacing::Single, qt_("Single"));
 	textLayoutModule->lspacingCO->insertItem(
@@ -931,6 +949,8 @@ GuiDocument::GuiDocument(GuiView & lv)
 	bc().addCheckedLineEditPanel(textLayoutModule->indentLE, docPS, N_("Text Layout"));
 	bc().addCheckedLineEdit(textLayoutModule->skipLE, textLayoutModule->skipRB);
 	bc().addCheckedLineEditPanel(textLayoutModule->skipLE, docPS, N_("Text Layout"));
+	bc().addCheckedLineEdit(textLayoutModule->parfillskipLE, textLayoutModule->parfillskipLA);
+	bc().addCheckedLineEditPanel(textLayoutModule->parfillskipLE, docPS, N_("Text Layout"));
 
 	textLayoutModule->tableStyleCO->addItem(qt_("Default"), toqstr("default"));
 	getTableStyles();
@@ -2319,6 +2339,19 @@ void GuiDocument::setSkip(int item)
 }
 
 
+void GuiDocument::setParfillSkip(int item)
+{
+	BufferParams::ParfillSkip kind =
+		BufferParams::ParfillSkip(textLayoutModule->parfillskipCO->itemData(item).toInt());
+	bool const enable = (kind == BufferParams::ParfillSkipCustom);
+	textLayoutModule->parfillskipLE->setEnabled(enable);
+	textLayoutModule->parfillskipLengthCO->setEnabled(enable);
+	// needed to catch empty custom case
+	bc().refresh();
+	isValid();
+}
+
+
 void GuiDocument::enableSkip(bool skip)
 {
 	textLayoutModule->indentLE->setEnabled(!skip);
@@ -4426,6 +4459,16 @@ void GuiDocument::applyView()
 			break;
 		}
 	}
+	bp_.parfill_skip = BufferParams::ParfillSkip(textLayoutModule->parfillskipCO->itemData(
+			textLayoutModule->parfillskipCO->currentIndex()).toInt());
+	if (bp_.parfill_skip == BufferParams::ParfillSkipCustom) {
+		GlueLength parfillskip(widgetsToLength(textLayoutModule->parfillskipLE,
+						 textLayoutModule->parfillskipLengthCO));
+		bp_.setParFillSkip(parfillskip);
+	} else
+		// default
+		bp_.setParFillSkip(GlueLength());
+
 	bp_.tablestyle = fromqstr(textLayoutModule->tableStyleCO->itemData(
 				      textLayoutModule->tableStyleCO->currentIndex()).toString());
 
@@ -5062,6 +5105,12 @@ void GuiDocument::paramsToDialog()
 		setSkip(textLayoutModule->skipCO->currentIndex());
 	}
 
+	textLayoutModule->parfillskipCO->setCurrentIndex(textLayoutModule->parfillskipCO->findData(bp_.parfill_skip));
+
+	lengthToWidgets(textLayoutModule->parfillskipLE,
+		textLayoutModule->parfillskipLengthCO,
+		bp_.getParfillSkip().asString(), default_unit);
+
 	textLayoutModule->twoColumnCB->setChecked(
 		bp_.columns == 2);
 	textLayoutModule->justCO->setCurrentIndex(textLayoutModule->justCO->findData(toqstr(bp_.justification)));
diff --git a/src/frontends/qt/GuiDocument.h b/src/frontends/qt/GuiDocument.h
index 41290a3a74..b5cc1cf05f 100644
--- a/src/frontends/qt/GuiDocument.h
+++ b/src/frontends/qt/GuiDocument.h
@@ -140,6 +140,7 @@ private Q_SLOTS:
 	void enableIndent(bool);
 	void setSkip(int);
 	void enableSkip(bool);
+	void setParfillSkip(int);
 	void allowMathIndent();
 	void enableMathIndent(int);
 	void browseLayout();
diff --git a/src/frontends/qt/LengthCombo.cpp b/src/frontends/qt/LengthCombo.cpp
index 1ab6d7df24..851e2fc286 100644
--- a/src/frontends/qt/LengthCombo.cpp
+++ b/src/frontends/qt/LengthCombo.cpp
@@ -28,9 +28,10 @@ LengthCombo::LengthCombo(QWidget * parent)
 	: QComboBox(parent)
 {
 	for (int i = 0; i < lyx::num_units; i++) {
-		// mu does not make sense usually
+		// mu and fil do not make sense usually
 		// so it must be added manually, if needed
-		if (QLatin1String(lyx::unit_name[i]) == "mu")
+		if (QLatin1String(lyx::unit_name[i]) == "mu"
+		    || QLatin1String(lyx::unit_name[i]) == "fil")
 			continue;
 		QComboBox::addItem(lyx::qt_(lyx::unit_name_gui[i]),
 			lyx::toqstr(lyx::unit_name[i]));
diff --git a/src/frontends/qt/ui/TextLayoutUi.ui b/src/frontends/qt/ui/TextLayoutUi.ui
index 3c2195a0b3..f9d92b25d2 100644
--- a/src/frontends/qt/ui/TextLayoutUi.ui
+++ b/src/frontends/qt/ui/TextLayoutUi.ui
@@ -6,14 +6,14 @@
    <rect>
     <x>0</x>
     <y>0</y>
-    <width>470</width>
-    <height>468</height>
+    <width>495</width>
+    <height>510</height>
    </rect>
   </property>
   <property name="windowTitle">
    <string/>
   </property>
-  <layout class="QGridLayout" name="gridLayout_4">
+  <layout class="QGridLayout" name="gridLayout_5">
    <item row="0" column="0">
     <widget class="QGroupBox" name="separateGB">
      <property name="title">
@@ -198,6 +198,71 @@
     </widget>
    </item>
    <item row="1" column="0">
+    <widget class="QGroupBox" name="parfillGB">
+     <property name="title">
+      <string>Paragraph Fill</string>
+     </property>
+     <layout class="QGridLayout" name="gridLayout_4">
+      <item row="0" column="0">
+       <widget class="QLabel" name="parfillskipLA">
+        <property name="text">
+         <string>Ma&ximum last line fill:</string>
+        </property>
+        <property name="buddy">
+         <cstring>parfillskipCO</cstring>
+        </property>
+       </widget>
+      </item>
+      <item row="0" column="1">
+       <widget class="QComboBox" name="parfillskipCO">
+        <property name="toolTip">
+         <string>Specifiy the minimum amount of free space in the last line of a paragraph</string>
+        </property>
+       </widget>
+      </item>
+      <item row="0" column="2">
+       <widget class="QLineEdit" name="parfillskipLE">
+        <property name="enabled">
+         <bool>false</bool>
+        </property>
+        <property name="toolTip">
+         <string/>
+        </property>
+        <property name="inputMethodHints">
+         <set>Qt::ImhFormattedNumbersOnly</set>
+        </property>
+       </widget>
+      </item>
+      <item row="0" column="3">
+       <widget class="lyx::frontend::LengthCombo" name="parfillskipLengthCO">
+        <property name="enabled">
+         <bool>false</bool>
+        </property>
+        <property name="toolTip">
+         <string/>
+        </property>
+        <property name="inputMethodHints">
+         <set>Qt::ImhFormattedNumbersOnly|Qt::ImhLatinOnly</set>
+        </property>
+       </widget>
+      </item>
+      <item row="0" column="4">
+       <spacer name="spacer">
+        <property name="orientation">
+         <enum>Qt::Horizontal</enum>
+        </property>
+        <property name="sizeHint" stdset="0">
+         <size>
+          <width>2</width>
+          <height>20</height>
+         </size>
+        </property>
+       </spacer>
+      </item>
+     </layout>
+    </widget>
+   </item>
+   <item row="2" column="0">
     <widget class="QGroupBox" name="spacingGB">
      <property name="minimumSize">
       <size>
@@ -270,7 +335,7 @@
      </layout>
     </widget>
    </item>
-   <item row="2" column="0">
+   <item row="3" column="0">
     <widget class="QGroupBox" name="tableStyleGB">
      <property name="title">
       <string>Table Style</string>
@@ -319,7 +384,7 @@
      </layout>
     </widget>
    </item>
-   <item row="3" column="0">
+   <item row="4" column="0">
     <widget class="QCheckBox" name="twoColumnCB">
      <property name="toolTip">
       <string>Format text into two columns</string>
@@ -329,7 +394,7 @@
      </property>
     </widget>
    </item>
-   <item row="4" column="0">
+   <item row="5" column="0">
     <layout class="QHBoxLayout" name="horizontalLayout_2">
      <item>
       <widget class="QLabel" name="justLA">
@@ -366,7 +431,7 @@
      </item>
     </layout>
    </item>
-   <item row="5" column="0">
+   <item row="6" column="0">
     <spacer>
      <property name="orientation">
       <enum>Qt::Vertical</enum>
diff --git a/src/support/Length.cpp b/src/support/Length.cpp
index f5c723f543..cdc0049e36 100644
--- a/src/support/Length.cpp
+++ b/src/support/Length.cpp
@@ -142,6 +142,7 @@ string const Length::asHTMLString() const
 		os << formatFPNumber(val_) << '%';
 		break;
 	case SP:
+	case FIL:
 	case UNIT_NONE:
 		break;
 	}
@@ -251,6 +252,7 @@ double Length::inInch(double text_width, double em_width, double margin) const
 		// but we do not have this information here.
 		result = val_ * em_width * 1.2 / 100;
 		break;
+	case FIL:
 	case Length::UNIT_NONE:
 		result = 0;  // this cannot happen
 		break;
diff --git a/src/support/Length.h b/src/support/Length.h
index 7863a9cfa5..865f61e791 100644
--- a/src/support/Length.h
+++ b/src/support/Length.h
@@ -44,6 +44,7 @@ public:
 		DD, ///< Didot point = 1/72 of a French inch, = 0.376mm
 		EM, ///< Width of capital "M" in current font.
 		EX, ///< Height of a small "x" for the current font.
+		FIL, ///< Stretchable space, used as glue.
 		IN, ///< Inch = 25.4mm = 72.27pt = 6.022pc
 		MM, ///< Millimeter = 2.845pt
 		MU, ///< Math unit (18mu = 1em) for positioning in math mode
diff --git a/src/support/lengthcommon.cpp b/src/support/lengthcommon.cpp
index 303dbcbc1f..f837f55c63 100644
--- a/src/support/lengthcommon.cpp
+++ b/src/support/lengthcommon.cpp
@@ -28,7 +28,7 @@ namespace lyx {
 
 // the latex units
 char const * const unit_name[] = {
-	"bp", "cc", "cm", "dd", "em", "ex", "in", "mm", "mu",
+	"bp", "cc", "cm", "dd", "em", "ex", "fil", "in", "mm", "mu",
 	"pc", "pt", "sp",
 	"text%",  "col%", "page%", "line%",
 	"theight%", "pheight%", "baselineskip%", "" };
@@ -38,7 +38,7 @@ int const num_units = int(sizeof(unit_name) / sizeof(unit_name[0]) - 1);
 // the LyX gui units
 char const * const unit_name_gui[] = {
 	N_("bp"), N_("cc[[unit of measure]]"), N_("cm"), N_("dd"), N_("em"),
-	N_("ex"), N_("in[[unit of measure]]"), N_("mm"), N_("mu[[unit of measure]]"), N_("pc"),
+	N_("ex"), N_("Elastic"), N_("in[[unit of measure]]"), N_("mm"), N_("mu[[unit of measure]]"), N_("pc"),
 	N_("pt"), N_("sp"), N_("Text Width %"),
 	N_("Column Width %"), N_("Page Width %"), N_("Line Width %"),
 	N_("Text Height %"), N_("Page Height %"), N_("Line Distance %"), "" };
diff --git a/src/version.h b/src/version.h
index 8a494df25b..cd47b6268d 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 655 // koji: ARGB colors
-#define LYX_FORMAT_TEX2LYX 655
+#define LYX_FORMAT_LYX 656 // spitz: parfillskip
+#define LYX_FORMAT_TEX2LYX 656
 
 #if LYX_FORMAT_TEX2LYX != LYX_FORMAT_LYX
 #ifndef _MSC_VER


More information about the lyx-cvs mailing list