Dear all,

Currently, when a LyX document without a proper (system or local)
layout file is opened, the default textclass (article) is used. This
changes the document and will result in data loss if the document is
saved.

The attached patch:

1. Adds a default Text "unknown" layout in the constructor of LayoutFileList:

+LayoutFileList::LayoutFileList()
+{
+       // add a default "unknown" layout
+       LayoutFile * unknown = new LayoutFile("unknown", "unknown", "unknown
text class", false);
+       // this will only set the loaded_ flag
+       unknown->load();
+       unknown->defaultlayout_ = unknown->emptyLayoutName();
+       
unknown->layoutlist_.push_back(unknown->createDefaultLayout(unknown->defaultLayoutName()));
+       classmap_["unknown"] = unknown;
+}

2. In TextClass::hasLayout(n), a missing layout is added to
layoutlist_ so this "unknown" textclass appears to have all layouts.

+       if (!has_layout && name_ == "unknown") {
+               const_cast<LayoutList&>(layoutlist_).push_back(
+                       
const_cast<TextClass*>(this)->createDefaultLayout(name));
+               return true;
+       }
+       return has_layout;
 }

3. Use this expandable "unknown" layout for a document without valid layout.

                } else {
                        // a warning will be given for unknown class
-                       setBaseClass(defaultBaseclass());
+                       setBaseClass(unknownBaseclass());
                        return classname;
                }

The result:

1. A document without valid layout will be opened with a warning. (The same)
2. All layouts are usable, although they all look like "standard" (changed)
3. Save such a document will not lose data. The original layout name
will be used. (improved)
4. Another layout can be chosen from Document -> settings, if a
correct textclass is selected, all layouts will work fine. (improved,
because the current approach will change some unknown layouts to
standard.)

The biggest problem with this patch is that because layout pointers
are used directly, LayoutList, which is std:;vector<Layout> can not be
expanded dynamically. I change it to std::list<Layout> to allow
dynamic growth of the "unknown" textclass but this may lead to
performance problems.

Any opinion is welcome.

Cheers,
Bo
Index: src/TextClass.cpp
===================================================================
--- src/TextClass.cpp	(revision 25460)
+++ src/TextClass.cpp	(working copy)
@@ -246,26 +246,8 @@
 	// Define the `empty' layout used in table cells, ert, etc. Note that 
 	// we do this before loading any layout file, so that classes can 
 	// override features of this layout if they should choose to do so.
-	if (rt == BASECLASS && !hasLayout(emptylayout_)) {
-		static char const * s = "Margin Static\n"
-			"LatexType Paragraph\n"
-			"LatexName dummy\n"
-			"Align Block\n"
-			"AlignPossible Left, Right, Center\n"
-			"LabelType No_Label\n"
-			"End";
-		istringstream ss(s);
-		Lexer lex(textClassTags);
-		lex.setStream(ss);
-		Layout lay;
-		lay.setName(emptylayout_);
-		if (!readStyle(lex, lay)) {
-			// The only way this happens is because the hardcoded layout above
-			// is wrong.
-			LASSERT(false, /**/);
-		};
-		layoutlist_.push_back(lay);
-	}
+	if (rt == BASECLASS && !hasLayout(emptylayout_))
+		layoutlist_.push_back(createDefaultLayout(emptylayout_));
 
 	Lexer lexrc(textClassTags);
 	lexrc.setFile(filename);
@@ -920,13 +902,19 @@
 {
 	docstring const name = n.empty() ? defaultLayoutName() : n;
 
-	return find_if(layoutlist_.begin(), layoutlist_.end(),
+	bool has_layout = find_if(layoutlist_.begin(), layoutlist_.end(),
 		       LayoutNamesEqual(name))
 		!= layoutlist_.end();
+
+	if (!has_layout && name_ == "unknown") {
+		const_cast<LayoutList&>(layoutlist_).push_back(
+			const_cast<TextClass*>(this)->createDefaultLayout(name));
+		return true;
+	}
+	return has_layout;
 }
 
 
-
 Layout const & TextClass::operator[](docstring const & name) const
 {
 	LASSERT(!name.empty(), /**/);
@@ -991,6 +979,11 @@
 	if (loaded_)
 		return true;
 
+	if (name_ == "unknown") {
+		loaded_ = true;
+		return true;
+	}
+
 	// Read style-file, provided path is searched before the system ones
 	FileName layout_file;
 	if (!path.empty())
@@ -1035,6 +1028,29 @@
 }
 
 
+Layout TextClass::createDefaultLayout(docstring const & name)
+{
+		static char const * s = "Margin Static\n"
+			"LatexType Paragraph\n"
+			"LatexName dummy\n"
+			"Align Block\n"
+			"AlignPossible Left, Right, Center\n"
+			"LabelType No_Label\n"
+			"End";
+		istringstream ss(s);
+		Lexer lex(textClassTags);
+		lex.setStream(ss);
+		Layout lay;
+		lay.setName(name);
+		if (!readStyle(lex, lay)) {
+			// The only way this happens is because the hardcoded layout above
+			// is wrong.
+			LASSERT(false, /**/);
+		};
+	return lay;
+}
+
+
 Layout const & TextClass::defaultLayout() const
 {
 	return operator[](defaultLayoutName());
Index: src/LayoutFile.cpp
===================================================================
--- src/LayoutFile.cpp	(revision 25460)
+++ src/LayoutFile.cpp	(working copy)
@@ -47,6 +47,19 @@
 	texClassAvail_ = texClassAvail;
 }
 
+
+LayoutFileList::LayoutFileList()
+{
+	// add a default "unknown" layout
+	LayoutFile * unknown = new LayoutFile("unknown", "unknown", "unknown text class", false);
+	// this will only set the loaded_ flag
+	unknown->load();
+	unknown->defaultlayout_ = unknown->emptyLayoutName();
+	unknown->layoutlist_.push_back(unknown->createDefaultLayout(unknown->defaultLayoutName()));
+	classmap_["unknown"] = unknown;
+}
+
+
 LayoutFileList::~LayoutFileList()
 {
 	ClassMap::const_iterator it = classmap_.begin();
@@ -261,7 +274,12 @@
 }
 
 
+LayoutFileIndex unknownBaseclass()
+{
+	return string("unknown");
+}
 
+
 // Reads the style files
 bool LyXSetStyle()
 {
Index: src/TextClass.h
===================================================================
--- src/TextClass.h	(revision 25460)
+++ src/TextClass.h	(working copy)
@@ -27,6 +27,7 @@
 #include <map>
 #include <set>
 #include <vector>
+#include <list>
 
 namespace lyx {
 
@@ -79,7 +80,7 @@
 	// that was based upon the same DocumentClass. (Of course, if you 
 	// really, REALLY want to make LayoutList a vector<Layout *>, then
 	// you can implement custom assignment and copy constructors.)
-	typedef std::vector<Layout> LayoutList;
+	typedef std::list<Layout> LayoutList;
 	/// The inset layouts available to this class
 	typedef std::map<docstring, InsetLayout> InsetLayouts;
 	///
@@ -98,6 +99,7 @@
 	// Layout Info
 	///////////////////////////////////////////////////////////////////
 	///
+	Layout createDefaultLayout(docstring const & name);
 	Layout const & defaultLayout() const;
 	///
 	docstring const & defaultLayoutName() const;
Index: src/LayoutFile.h
===================================================================
--- src/LayoutFile.h	(revision 25460)
+++ src/LayoutFile.h	(working copy)
@@ -71,7 +71,7 @@
 class LayoutFileList {
 public:
 	///
-	LayoutFileList() {}
+	LayoutFileList();
 	~LayoutFileList();
 	/// \return The sole instance of this class.
 	static LayoutFileList & get();
@@ -116,6 +116,7 @@
 
 ///
 LayoutFileIndex defaultBaseclass();
+LayoutFileIndex unknownBaseclass();
 
 
 } // namespace lyx
Index: src/BufferParams.cpp
===================================================================
--- src/BufferParams.cpp	(revision 25460)
+++ src/BufferParams.cpp	(working copy)
@@ -470,7 +470,7 @@
 			setBaseClass(classname);
 		} else {
 			// a warning will be given for unknown class
-			setBaseClass(defaultBaseclass());
+			setBaseClass(unknownBaseclass());
 			return classname;
 		}
 		// FIXME: this warning will be given even if there exists a local .cls

Reply via email to