Dear list

Here are two patches that I would like to include in master.

* The first one adds a flag [-t FORMAT] to lib/scripts/layout2layout.py
to specify a target format for conversion. I took some code from
lyx2lyx and I would be happy if somebody can confirm that the
python2-specific code is appropriate as I do not understand much
of this issue.

* The second patch adds a new constant LYXFILE_LAYOUT_FORMAT in
src/TextClass.cpp. This constant determines the target format
when one clicks "Convert" in the Local Layout pane.

This is some groundwork for a branch "lyx unstable" that I have, that is
based on master but that reads and writes the 2.2 format. This allows me
to set LYXFILE_LAYOUT_FORMAT = 60 (the 2.2 layout format), so that it
does not create files that cannot be read by 2.2. But, both patches
are meaningful for master.

In the attached patches for master, the old behaviour is preserved
because of the definition LYXFILE_LAYOUT_FORMAT = LAYOUT_FORMAT.


Sincerely
Guillaume
>From ad59b14909cc2add8c48bd3247ad933af02442d3 Mon Sep 17 00:00:00 2001
From: Guillaume Munch <[email protected]>
Date: Sat, 23 Jul 2016 15:29:40 +0100
Subject: [PATCH 2/2] Enable to specify the target format for converting the
 local layout

* New constant LYXFILE_LAYOUT_FORMAT in src/TextClass.cpp. This determines the
  layout format corresponding to the current lyx file format.

* The Local Layout pane is changed so that the "Convert" button does not convert
  to the internal layout format but to the current lyx file format, to make sure
  that the file does not become unreadable with a specified earlier version of
  LyX.

* If LYXFILE_LAYOUT_FORMAT == LAYOUT_FORMAT then LyX behaves as before. This is
  the value defined in master.
---
 src/TextClass.cpp                 | 27 ++++++++++++++-------
 src/TextClass.h                   |  6 ++++-
 src/frontends/qt4/GuiDocument.cpp | 50 +++++++++++++++++++++++----------------
 src/frontends/qt4/GuiDocument.h   |  1 +
 4 files changed, 53 insertions(+), 31 deletions(-)

diff --git a/src/TextClass.cpp b/src/TextClass.cpp
index b282ba3..a783437 100644
--- a/src/TextClass.cpp
+++ b/src/TextClass.cpp
@@ -63,6 +63,12 @@ namespace lyx {
 //
 int const LAYOUT_FORMAT = 60; //lasgouttes LongTableNoNumber => Unnumbered
 
+
+// Layout format for the current lyx file format. Controls which format is
+// targeted by Local Layout > Convert. In master, equal to LAYOUT_FORMAT.
+int const LYXFILE_LAYOUT_FORMAT = LAYOUT_FORMAT;
+
+
 namespace {
 
 class LayoutNamesEqual : public unary_function<Layout, bool> {
@@ -79,26 +85,29 @@ private:
 };
 
 
-bool layout2layout(FileName const & filename, FileName const & tempfile)
+bool layout2layout(FileName const & filename, FileName const & tempfile,
+                   int const format = LAYOUT_FORMAT)
 {
 	FileName const script = libFileSearch("scripts", "layout2layout.py");
 	if (script.empty()) {
 		LYXERR0("Could not find layout conversion "
-			  "script layout2layout.py.");
+		        "script layout2layout.py.");
 		return false;
 	}
 
 	ostringstream command;
 	command << os::python() << ' ' << quoteName(script.toFilesystemEncoding())
-		<< ' ' << quoteName(filename.toFilesystemEncoding())
-		<< ' ' << quoteName(tempfile.toFilesystemEncoding());
+	        << " -t " << format
+	        << ' ' << quoteName(filename.toFilesystemEncoding())
+	        << ' ' << quoteName(tempfile.toFilesystemEncoding());
 	string const command_str = command.str();
 
 	LYXERR(Debug::TCLASS, "Running `" << command_str << '\'');
 
 	cmd_ret const ret = runCommand(command_str);
 	if (ret.first != 0) {
-		LYXERR0("Could not run layout conversion script layout2layout.py.");
+		if (format == LAYOUT_FORMAT)
+			LYXERR0("Conversion of layout with layout2layout.py has failed.");
 		return false;
 	}
 	return true;
@@ -290,7 +299,7 @@ std::string TextClass::convert(std::string const & str)
 	os.close();
 	TempFile tmp2("convert_localXXXXXX.layout");
 	FileName const tempfile = tmp2.name();
-	bool success = layout2layout(fn, tempfile);
+	bool success = layout2layout(fn, tempfile, LYXFILE_LAYOUT_FORMAT);
 	if (!success)
 		return "";
 	ifstream is(tempfile.toFilesystemEncoding().c_str());
@@ -375,13 +384,13 @@ TextClass::ReturnValues TextClass::read(std::string const & str, ReadType rt)
 	os << str;
 	os.close();
 
-	// now try to convert it
-	bool const worx = convertLayoutFormat(tempfile, rt);
-	if (!worx) {
+	// now try to convert it to LAYOUT_FORMAT
+	if (!convertLayoutFormat(tempfile, rt)) {
 		LYXERR0("Unable to convert internal layout information to format "
 			<< LAYOUT_FORMAT);
 		return ERROR;
 	}
+
 	return OK_OLDFORMAT;
 }
 
diff --git a/src/TextClass.h b/src/TextClass.h
index 88f9303..22d426c 100644
--- a/src/TextClass.h
+++ b/src/TextClass.h
@@ -171,7 +171,8 @@ public:
 	ReturnValues read(Lexer & lex, ReadType rt = BASECLASS);
 	/// validates the layout information passed in str
 	static ReturnValues validate(std::string const & str);
-	///
+	/// \return the conversion of \param str to the latest layout format
+	/// compatible with the lyx format.
 	static std::string convert(std::string const & str);
 
 	///////////////////////////////////////////////////////////////////
@@ -514,6 +515,9 @@ std::ostream & operator<<(std::ostream & os, PageSides p);
 
 /// current format of layout files
 extern int const LAYOUT_FORMAT;
+/// layout format for the current lyx file format (usually equal to
+/// LAYOUT_FORMAT)
+extern int const LYXFILE_LAYOUT_FORMAT;
 
 
 } // namespace lyx
diff --git a/src/frontends/qt4/GuiDocument.cpp b/src/frontends/qt4/GuiDocument.cpp
index 3faa902..c4ccb06 100644
--- a/src/frontends/qt4/GuiDocument.cpp
+++ b/src/frontends/qt4/GuiDocument.cpp
@@ -545,6 +545,15 @@ void LocalLayout::apply(BufferParams & params)
 }
 
 
+void LocalLayout::hideConvert()
+{
+	convertPB->setEnabled(false);
+	convertLB->setText("");
+	convertPB->hide();
+	convertLB->hide();
+}
+
+
 void LocalLayout::textChanged()
 {
 	static const QString message =
@@ -556,15 +565,14 @@ void LocalLayout::textChanged()
 		validated_ = true;
 		validatePB->setEnabled(false);
 		validLB->setText("");
-		convertPB->hide();
-		convertLB->hide();
+		hideConvert();
 		changed();
 	} else if (!validatePB->isEnabled()) {
 		// if that's already enabled, we shouldn't need to do anything.
 		validated_ = false;
 		validLB->setText(message);
 		validatePB->setEnabled(true);
-		convertPB->setEnabled(false);
+		hideConvert();
 		changed();
 	}
 }
@@ -574,44 +582,44 @@ void LocalLayout::convert() {
 	string const layout =
 		fromqstr(locallayoutTE->document()->toPlainText().trimmed());
 	string const newlayout = TextClass::convert(layout);
-	LYXERR0(newlayout);
-	if (newlayout.empty()) {
-		Alert::error(_("Conversion Failed!"),
-		      _("Failed to convert local layout to current format."));
-	} else {
+	if (!newlayout.empty())
 		locallayoutTE->setPlainText(toqstr(newlayout));
-	}
 	validate();
 }
 
 
 void LocalLayout::convertPressed() {
 	convert();
+	hideConvert();
 	changed();
 }
 
 
 void LocalLayout::validate() {
-	static const QString valid = qt_("Layout is valid!");
-	static const QString vtext =
-		toqstr("<p style=\"font-weight: bold; \">")
-		  + valid + toqstr("</p>");
-	static const QString invalid = qt_("Layout is invalid!");
-	static const QString ivtext =
-		toqstr("<p style=\"color: #c00000; font-weight: bold; \">")
-		  + invalid + toqstr("</p>");
-
+	static const QString vpar("<p style=\"font-weight: bold;\">%1</p>");
+	static const QString ivpar("<p style=\"color: #c00000; font-weight: bold; \">"
+	                           "%1</p>");
 	string const layout =
 		fromqstr(locallayoutTE->document()->toPlainText().trimmed());
 	if (!layout.empty()) {
 		TextClass::ReturnValues const ret = TextClass::validate(layout);
 		validated_ = (ret == TextClass::OK) || (ret == TextClass::OK_OLDFORMAT);
 		validatePB->setEnabled(false);
-		validLB->setText(validated_ ? vtext : ivtext);
+		validLB->setText(validated_ ? vpar.arg(qt_("Layout is valid!"))
+		                            : ivpar.arg(qt_("Layout is invalid!")));
 		if (ret == TextClass::OK_OLDFORMAT) {
 			convertPB->show();
-			convertPB->setEnabled(true);
-			convertLB->setText(qt_("Convert to current format"));
+			if (TextClass::convert(layout).empty()) {
+				convertPB->setEnabled(false);
+				const QString text = (LAYOUT_FORMAT == LYXFILE_LAYOUT_FORMAT)
+					? ivpar.arg(qt_("Conversion to current format impossible!"))
+					: vpar.arg(qt_("Conversion to current stable format "
+					               "impossible."));
+				convertLB->setText(text);
+			} else {
+				convertPB->setEnabled(true);
+				convertLB->setText(qt_("Convert to current format"));
+			}
 			convertLB->show();
 		} else {
 			convertPB->hide();
diff --git a/src/frontends/qt4/GuiDocument.h b/src/frontends/qt4/GuiDocument.h
index 097abff..e0d42c9 100644
--- a/src/frontends/qt4/GuiDocument.h
+++ b/src/frontends/qt4/GuiDocument.h
@@ -322,6 +322,7 @@ Q_SIGNALS:
 private:
 	void validate();
 	void convert();
+	void hideConvert();
 private Q_SLOTS:
 	void textChanged();
 	void validatePressed();
-- 
2.7.4

>From a053cf0353ec320cdce0f31177e78b5879b7db3a Mon Sep 17 00:00:00 2001
From: Guillaume Munch <[email protected]>
Date: Fri, 22 Jul 2016 20:52:29 +0100
Subject: [PATCH 1/2] Add an option to layout2layout to convert to a particular
 format

---
 lib/scripts/layout2layout.py | 68 +++++++++++++++++++++++++++++++++-----------
 1 file changed, 51 insertions(+), 17 deletions(-)

diff --git a/lib/scripts/layout2layout.py b/lib/scripts/layout2layout.py
index 7de844b..113227b 100644
--- a/lib/scripts/layout2layout.py
+++ b/lib/scripts/layout2layout.py
@@ -10,8 +10,9 @@
 
 # This script will update a .layout file to current format
 
+# The latest layout format is also defined in src/TextClass.cpp
+currentFormat = 60
 
-import os, re, string, sys
 
 # Incremented to format 4, 6 April 2007, lasgouttes
 # Introduction of generic "Provides" declaration
@@ -208,12 +209,22 @@ import os, re, string, sys
 # development/tools/updatelayouts.py script to update all
 # layout files to the new format.
 
-currentFormat = 60
 
+import os, re, string, sys
+import argparse
 
-def usage(prog_name):
-    return ("Usage: %s inputfile outputfile\n" % prog_name +
-            "or     %s <inputfile >outputfile" % prog_name)
+# Provide support for both python 2 and 3
+# (copied from lyx2lyx)
+PY2 = sys.version_info[0] == 2
+if PY2:
+    # argparse returns strings in the commandline encoding, we need to convert.
+    # sys.getdefaultencoding() would not always be correct, see
+    # http://legacy.python.org/dev/peps/pep-0383/
+    def cmd_arg(arg):
+        return arg.decode(sys.getfilesystemencoding())
+else:
+    cmd_arg = str
+# End of code to support for both python 2 and 3
 
 
 def error(message):
@@ -257,7 +268,7 @@ def addstring(s, l):
     l.append(s)
 
 
-def convert(lines):
+def convert(lines, end_format):
     " Convert to new format."
     re_Comment = re.compile(r'^(\s*)#')
     re_Counter = re.compile(r'\s*Counter\s*', re.IGNORECASE)
@@ -404,14 +415,14 @@ def convert(lines):
             if match:
                 formatline = i
                 format = int(match.group(4))
-                if format > 1 and format < currentFormat:
+                if format > 1 and format < end_format:
                     lines[i] = "Format %d" % (format + 1)
                     only_comment = 0
-                elif format == currentFormat:
+                elif format == end_format:
                     # nothing to do
                     return format
                 else:
-                    error('Cannot convert file format %s to %s' % (format, currentFormat))
+                    error('Cannot convert file format %s to %s' % (format, end_format))
             else:
                 lines.insert(i, "Format 2")
                 only_comment = 0
@@ -1140,27 +1151,50 @@ def convert(lines):
 
 
 def main(argv):
+    args = {}
+    args["description"] = "Convert layout file <inputfile> to a newer format."
+
+    parser = argparse.ArgumentParser(**args)
+
+    parser.add_argument("-t", "--to", type=int, dest="format",
+                        help=("destination layout format, default %i (latest)") % currentFormat)
+    parser.add_argument("input_file", nargs='?', type=cmd_arg, default=None,
+                        help="input file (default stdin)")
+    parser.add_argument("output_file", nargs='?', type=cmd_arg, default=None,
+                        help="output file (default stdout)")
+
+    options = parser.parse_args()
 
     # Open files
-    if len(argv) == 1:
+    if options.input_file:
+        source = open(options.input_file, 'rb')
+    else:
         source = sys.stdin
+
+    if options.output_file:
+        output = open(options.output_file, 'wb')
+    else:
         output = sys.stdout
-    elif len(argv) == 3:
-        source = open(argv[1], 'rb')
-        output = open(argv[2], 'wb')
+
+    if options.format:
+        end_format = options.format
     else:
-        error(usage(argv[0]))
+        end_format = currentFormat
+
+    if end_format > currentFormat:
+        error("Format %i does not exist" % end_format);
 
     # Do the real work
     lines = read(source)
     format = 1
-    while (format < currentFormat):
-        format = convert(lines)
+    while (format < end_format):
+        format = convert(lines, end_format)
     write(output, lines)
 
     # Close files
-    if len(argv) == 3:
+    if options.input_file:
         source.close()
+    if options.output_file:
         output.close()
 
     return 0
-- 
2.7.4

Reply via email to