string tcp;
LayoutFileList & bcl = LayoutFileList::get();
if (tcp.empty() && !filepath.empty())
- tcp = bcl.addLayoutFile(classname, filepath.absFilename(), LayoutFileList::Local);
+ tcp = bcl.addLocalLayout(classname, filepath.absFilename());
if (!tcp.empty())
setBaseClass(tcp);
- else if (bcl.haveClass(classname)) {
+ else
setBaseClass(classname);
- } else {
- // a warning will be given for unknown class
- setBaseClass(defaultBaseclass());
- return classname;
- }
- // FIXME: this warning will be given even if there exists a local .cls
- // file. Even worse, the .lyx file can not be compiled or exported
- // because the textclass is marked as unavilable.
+ // We assume that a tex class exists for local or unknown layouts so this warning
+ // will only be given for system layouts.
if (!baseClass()->isTeXClassAvailable()) {
docstring const msg =
bformat(_("The layout file requested by this document,\n"
frontend::Alert::warning(_("Document class not available"),
msg + _("LyX will not be able to produce output."));
}
-
} else if (token == "\\begin_preamble") {
readPreamble(lex);
} else if (token == "\\begin_local_layout") {
bool BufferParams::setBaseClass(string const & classname)
{
LYXERR(Debug::TCLASS, "setBaseClass: " << classname);
- LayoutFileList const & bcl = LayoutFileList::get();
+ LayoutFileList & bcl = LayoutFileList::get();
if (!bcl.haveClass(classname)) {
docstring s =
- bformat(_("The document class %1$s could not be found."),
+ bformat(_("The document class %1$s could not be found. "
+ "A default textclass with default layouts will be used. "
+ "LyX might not be able to produce output unless a correct "
+ "textclass is selected from the document settings dialog."),
from_utf8(classname));
- frontend::Alert::error(_("Class not found"), s);
- return false;
+ frontend::Alert::error(_("Document class not found"), s);
+ bcl.addDefaultClass(classname);
}
if (bcl[classname].load()) {
ParIterator end = par_iterator_end(in);
for (ParIterator it = par_iterator_begin(in); it != end; ++it) {
docstring const name = it->layout().name();
- bool hasLayout = newtc.hasLayout(name);
+
+ // the pasted text will keep their own layout name. If this layout does
+ // not exist in the new document, it will behave like a standard layout.
+ newtc.addLayoutIfNeeded(name);
if (in.usePlainLayout())
it->setLayout(newtc.emptyLayout());
- else if (hasLayout)
- it->setLayout(newtc[name]);
else
- it->setLayout(newtc.defaultLayout());
-
- if (!hasLayout && name != oldtc.defaultLayoutName()) {
- docstring const s = bformat(
- _("Layout had to be changed from\n%1$s to %2$s\n"
- "because of class conversion from\n%3$s to %4$s"),
- name, it->layout().name(),
- from_utf8(oldtc.name()), from_utf8(newtc.name()));
- // To warn the user that something had to be done.
- errorlist.push_back(ErrorItem(_("Changed Layout"), s,
- it->id(), 0,
- it->size()));
- }
+ it->setLayout(newtc[name]);
}
// character styles
Layout::Layout()
{
+ unknown_ = false;
margintype = MARGIN_STATIC;
latextype = LATEX_PARAGRAPH;
intitle = false;
public:
///
Layout();
+ /// is this layout a default layout created for an unknown layout
+ bool isUnknown() const { return unknown_; }
+ void setUnknown(bool unknown) { unknown_ = unknown; }
/// Reads a layout definition from file
/// \return true on success.
bool read(Lexer &, TextClass const &);
////////////////////////////////////////////////////////////////
// members
////////////////////////////////////////////////////////////////
+ /** Is this layout the default layout for an unknown layout? If
+ * so, its name will be displayed as xxx (unknown).
+ */
+ bool unknown_;
+
/** Default font for this layout/environment.
The main font for this kind of environment. If an attribute has
INHERITED_*, it means that the value is specified by
}
-string const LayoutFileList::localPrefix = "LOCAL:";
+LayoutFileIndex LayoutFileList::addDefaultClass(string const & textclass)
+{
+ if (haveClass(textclass))
+ return textclass;
+
+ FileName const tempLayout = FileName::tempName();
+ ofstream ofs(tempLayout.toFilesystemEncoding().c_str());
+ ofs << "# This layout is automatically generated\n"
+ "# \\DeclareLaTeXClass{" << textclass << "}\n\n"
+ "Format 7\n"
+ "Input stdclass.inc\n\n"
+ "Columns 1\n"
+ "Sides 1\n"
+ "SecNumDepth 2\n"
+ "TocDepth 2\n"
+ "DefaultStyle Standard\n\n"
+ "Style Standard\n"
+ " Category MainText\n"
+ " Margin Static\n"
+ " LatexType Paragraph\n"
+ " LatexName dummy\n"
+ " ParIndent MM\n"
+ " ParSkip 0.4\n"
+ " Align Block\n"
+ " AlignPossible Block, Left, Right, Center\n"
+ " LabelType No_Label\n"
+ "End\n";
+ ofs.close();
+
+ // We do not know if a LaTeX class is available for this document, but setting
+ // the last parameter to true will suppress a warning message about missing
+ // tex class.
+ LayoutFile * tc = new LayoutFile(textclass, textclass, "Unknown text class " + textclass, true);
+ if (!tc->load(tempLayout.absFilename()))
+ // The only way this happens is because the hardcoded layout file above
+ // is wrong.
+ LASSERT(false, /**/);
+ classmap_[textclass] = tc;
+ return textclass;
+}
+
LayoutFileIndex
- LayoutFileList::addLayoutFile(string const & textclass, string const & path,
- Layout_Type type)
+ LayoutFileList::addLocalLayout(string const & textclass, string const & path)
{
// FIXME There is a bug here: 4593
//
// NOTE: latex class name is defined in textclass.layout, which can be
// different from textclass
string fullName = addName(path, textclass + ".layout");
- string localIndex;
- if (type == Local)
- localIndex = localPrefix + fullName;
-
- // if the local file has already been loaded, return it
- if (haveClass(localIndex))
- return localIndex;
-
FileName const layout_file(fullName);
if (layout_file.exists()) {
LYXERR(Debug::TCLASS, "Adding class " << textclass << " from directory " << path);
// now, create a TextClass with description containing path information
string className(sub.str(2) == "" ? textclass : sub.str(2));
LayoutFile * tmpl =
- new LayoutFile(textclass, className, localIndex, true);
+ new LayoutFile(textclass, className, textclass, true);
// This textclass is added on request so it will definitely be
// used. Load it now because other load() calls may fail if they
// are called in a context without buffer path information.
tmpl->load(path);
- classmap_[localIndex] = tmpl;
- return localIndex;
+ // There will be only one textclass with this name, even if different
+ // layout files are loaded from different directories.
+ if (haveClass(textclass)) {
+ LYXERR0("Exisint textclass " << textclass << " is redefined by " << fullName);
+ delete classmap_[textclass];
+ }
+ classmap_[textclass] = tmpl;
+ return textclass;
}
}
}
/// Clears the textclass so as to force it to be reloaded
void reset(LayoutFileIndex const & tc);
- enum Layout_Type {
- System,
- Local
- };
-
+ /// add a default textclass with all standard layouts.
+ LayoutFileIndex addDefaultClass(std::string const & textclass);
+
/// add a textclass from user local directory.
/// \return the identifier for the loaded file, or else an
/// empty string if no file was loaded.
LayoutFileIndex
- addLayoutFile(std::string const & textclass, std::string const & path,
- Layout_Type type);
+ addLocalLayout(std::string const & textclass, std::string const & path);
/// a list of the available classes
std::vector<LayoutFileIndex> classList() const;
- ///
- static std::string const localPrefix;
private:
///
typedef std::map<std::string, LayoutFile *> ClassMap;
layoutname = tclass.defaultLayoutName();
}
- bool hasLayout = tclass.hasLayout(layoutname);
-
- if (!hasLayout) {
- errorList.push_back(ErrorItem(_("Unknown layout"),
- bformat(_("Layout '%1$s' does not exist in textclass '%2$s'\nTrying to use the default instead.\n"),
- layoutname, from_utf8(tclass.name())), par.id(), 0, par.size()));
- layoutname = par.usePlainLayout() ?
- tclass.emptyLayoutName() :
- tclass.defaultLayoutName();
- }
+ // When we apply an unknown layout to a document, we add this layout to the textclass
+ // of this document. For example, when you apply class article to a beamer document,
+ // all unknown layouts such as frame will be added to document class article so that
+ // these layouts can keep their original names.
+ tclass.addLayoutIfNeeded(layoutname);
par.setLayout(bp.documentClass()[layoutname]);
}
-bool TextClass::readStyle(Lexer & lexrc, Layout & lay)
+bool TextClass::readStyle(Lexer & lexrc, Layout & lay) const
{
LYXERR(Debug::TCLASS, "Reading style " << to_utf8(lay.name()));
if (!lay.read(lexrc, *this)) {
// 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);
}
+void TextClass::addLayoutIfNeeded(docstring const & n) const
+{
+ if (!hasLayout(n))
+ layoutlist_.push_back(createDefaultLayout(n, true));
+}
+
Layout const & TextClass::operator[](docstring const & name) const
{
return true;
// Read style-file, provided path is searched before the system ones
- FileName layout_file;
- if (!path.empty())
+ // If path is a file, it is loaded directly.
+ FileName layout_file(path);
+ if (!path.empty() && !layout_file.isReadableFile())
layout_file = FileName(addName(path, name_ + ".layout"));
if (layout_file.empty() || !layout_file.exists())
layout_file = libFileSearch("layouts", name_, "layout");
}
+Layout TextClass::createDefaultLayout(docstring const & name, bool unknown) const
+{
+ static Layout * defaultLayout = NULL;
+
+ if (defaultLayout) {
+ defaultLayout->setUnknown(unknown);
+ defaultLayout->setName(name);
+ return *defaultLayout;
+ }
+
+ 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);
+ defaultLayout = new Layout;
+ defaultLayout->setUnknown(unknown);
+ defaultLayout->setName(name);
+ if (!readStyle(lex, *defaultLayout)) {
+ // The only way this happens is because the hardcoded layout above
+ // is wrong.
+ LASSERT(false, /**/);
+ };
+ return *defaultLayout;
+}
+
/////////////////////////////////////////////////////////////////////////
//
// DocumentClassBundle
#include <map>
#include <set>
#include <vector>
+#include <list>
namespace lyx {
// 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;
+ //
+ // NOTE: Layout pointers are directly assigned to paragraphs so a
+ // container that does not invalidate these pointers after insertion
+ // is needed.
+ //
+ // NOTE: It makes sense to add unknown layouts to DocumentClass
+ // and make them buffer-dependent. However, this requires
+ // reimplementation of a lot of functions such as hasLayout
+ // and operator[], with little benefit.
+ typedef std::list<Layout> LayoutList;
/// The inset layouts available to this class
typedef std::map<docstring, InsetLayout> InsetLayouts;
///
bool isDefaultLayout(Layout const &) const;
///
bool isPlainLayout(Layout const &) const;
+ /// Create a default layout for this textclass.
+ /** \param unknown Set to true if this layout is a default layout used to
+ * represent an unknown layout
+ */
+ Layout createDefaultLayout(docstring const & name, bool unknown = false) const;
/// returns a special layout for use when we don't really want one,
/// e.g., in table cells
Layout const & emptyLayout() const
size_t layoutCount() const { return layoutlist_.size(); }
///
bool hasLayout(docstring const & name) const;
+ /// add a default layout \c name if it does not exist in layoutlist_
+ void addLayoutIfNeeded(docstring const & name) const;
///
Layout const & operator[](docstring const & vname) const;
// loading
///////////////////////////////////////////////////////////////////
/// Sees to it the textclass structure has been loaded
+ /// This function will search for $classname.layout in default directories
+ /// and an optional path, but if path points to a file, it will be loaded
+ /// directly.
bool load(std::string const & path = std::string()) const;
/// Has this layout file been loaded yet?
/// Overridden by DocumentClass
// members
///////////////////////////////////////////////////////////////////
/// Paragraph styles used in this layout
- LayoutList layoutlist_;
+ /// This variable is mutable because unknown layouts can be added
+ /// to const textclass.
+ mutable LayoutList layoutlist_;
/// Layout file name
std::string name_;
/// document class name
///
bool convertLayoutFormat(support::FileName const &, ReadType);
/// \return true for success.
- bool readStyle(Lexer &, Layout &);
+ bool readStyle(Lexer &, Layout &) const;
///
void readOutputType(Lexer &);
///
this, SLOT(change_adaptor()));
connect(latexModule->layoutPB, SIGNAL(clicked()),
this, SLOT(browseLayout()));
+ connect(latexModule->layoutPB, SIGNAL(clicked()),
+ this, SLOT(change_adaptor()));
connect(latexModule->childDocGB, SIGNAL(clicked()),
this, SLOT(change_adaptor()));
connect(latexModule->childDocLE, SIGNAL(textChanged(const QString &)),
latexModule->psdriverCO->addItem(enc);
}
// latex classes
- // FIXME hide local layout button due to bug 4812.
- latexModule->layoutPB->hide();
latexModule->classCO->setModel(&classes_model_);
LayoutFileList const & bcl = LayoutFileList::get();
vector<LayoutFileIndex> classList = bcl.classList();
FileName layoutFile = support::makeAbsPath(fromqstr(file),
fromqstr(bufferFilepath()));
+ int const ret = Alert::prompt(_("Local layout file"),
+ _("The layout file you have selected is a local layout\n"
+ "file, not one in the system or user directory. Your\n"
+ "document may not work with this layout if you do not\n"
+ "keep the layout file in the document directory."),
+ 1, 1, _("&Set Layout"), _("&Cancel"));
+ if (ret == 1)
+ return;
+
// load the layout file
LayoutFileList & bcl = LayoutFileList::get();
string classname = layoutFile.onlyFileName();
- LayoutFileIndex name = bcl.addLayoutFile(
+ // this will update an existing layout if that layout has been loaded before.
+ LayoutFileIndex name = bcl.addLocalLayout(
classname.substr(0, classname.size() - 7),
- layoutFile.onlyPath().absFilename(),
- LayoutFileList::Local);
+ layoutFile.onlyPath().absFilename());
if (name.empty()) {
Alert::error(_("Error"),
latexModule->classCO->setCurrentIndex(0);
} else
latexModule->classCO->setCurrentIndex(idx);
+
classChanged();
}
if (idx < 0)
return;
string const classname = classes_model_.getIDString(idx);
- // check if this is a local layout file
- if (prefixIs(classname, LayoutFileList::localPrefix)) {
- int const ret = Alert::prompt(_("Local layout file"),
- _("The layout file you have selected is a local layout\n"
- "file, not one in the system or user directory. Your\n"
- "document may not work with this layout if you do not\n"
- "keep the layout file in the same directory."),
- 1, 1, _("&Set Layout"), _("&Cancel"));
- if (ret == 1) {
- // try to reset the layout combo
- setLayoutComboByIDString(bp_.baseClassID());
- return;
- }
- }
+
// FIXME Note that by doing things this way, we load the TextClass
// as soon as it is selected. So, if you use the scroll wheel when
// sitting on the combo box, we'll load a lot of TextClass objects
if (!text_class_)
return;
- QString const & name = toqstr((*text_class_)[layout].name());
+ Layout const & lay = (*text_class_)[layout];
+ QString const & name = toqstr(lay.name() + (lay.isUnknown() ? " (unknown)" : ""));
if (name == currentText())
return;
// if it doesn't require the empty layout, we skip it
if (name == text_class_->emptyLayoutName() && inset_ && !useEmpty)
continue;
- addItemSort(name, lit->category(), lyxrc.sort_layouts, lyxrc.group_layouts);
+ addItemSort(name + (lit->isUnknown() ? " (unknown)" : ""),
+ lit->category(), lyxrc.sort_layouts, lyxrc.group_layouts);
}
set(owner_.view()->cursor().innerParagraph().layout().name());
{
// get selection
QModelIndex mindex = filterModel_->mapToSource(filterModel_->index(index, 1));
- docstring const layoutName =
- qstring_to_ucs4(model_->itemFromIndex(mindex)->text());
+ docstring const layoutName = rtrim(
+ qstring_to_ucs4(model_->itemFromIndex(mindex)->text()), " (unknown)");
owner_.setFocus();
</layout>
</widget>
</item>
+ <item row="2" column="2" colspan="2" >
+ <spacer>
+ <property name="orientation" >
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" >
+ <size>
+ <width>261</width>
+ <height>22</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
<item row="2" column="1" >
<widget class="QComboBox" name="psdriverCO" >
<property name="duplicatesEnabled" >
</property>
</widget>
</item>
+ <item row="0" column="3" >
+ <widget class="QPushButton" name="layoutPB" >
+ <property name="sizePolicy" >
+ <sizepolicy>
+ <hsizetype>1</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="toolTip" >
+ <string>Click to select a local document class definition file</string>
+ </property>
+ <property name="text" >
+ <string>&Local Layout...</string>
+ </property>
+ </widget>
+ </item>
<item row="0" column="0" >
<widget class="QLabel" name="classL" >
<property name="text" >
</property>
</widget>
</item>
- <item row="0" column="1" colspan="3" >
+ <item row="0" column="1" colspan="2" >
<widget class="QComboBox" name="classCO" >
<property name="maxVisibleItems" >
<number>20</number>
</property>
</spacer>
</item>
- <item row="2" column="2" >
- <spacer>
- <property name="orientation" >
- <enum>Qt::Horizontal</enum>
- </property>
- <property name="sizeHint" >
- <size>
- <width>31</width>
- <height>23</height>
- </size>
- </property>
- </spacer>
- </item>
- <item row="2" column="3" >
- <widget class="QPushButton" name="layoutPB" >
- <property name="sizePolicy" >
- <sizepolicy>
- <hsizetype>1</hsizetype>
- <vsizetype>0</vsizetype>
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="toolTip" >
- <string>Click to select a local document class definition file</string>
- </property>
- <property name="text" >
- <string>&Local Layout...</string>
- </property>
- </widget>
- </item>
</layout>
</widget>
<tabstops>