#include "mathed/MathMacroTemplate.h"
#include "mathed/MathSupport.h"
+#include "graphics/PreviewLoader.h"
+
#include "frontends/alert.h"
#include "frontends/Delegates.h"
#include "frontends/WorkAreaManager.h"
-#include "graphics/Previews.h"
-
#include "support/lassert.h"
#include "support/convert.h"
#include "support/debug.h"
using namespace std;
using namespace lyx::support;
+using namespace lyx::graphics;
namespace lyx {
namespace {
-// Do not remove the comment below, so we get merge conflict in
-// independent branches. Instead add your own.
-int const LYX_FORMAT = 413; // rgh: html_css_as_file
+int const LYX_FORMAT = LYX_FORMAT_LYX;
typedef map<string, bool> DepClean;
typedef map<docstring, pair<InsetLabel const *, Buffer::References> > RefCache;
} // namespace anon
+// A storehouse for the cloned buffers.
+list<CloneList *> cloned_buffers;
+
+
class Buffer::Impl
{
public:
~Impl()
{
+ delete preview_loader_;
if (wa_) {
wa_->closeAll();
delete wa_;
}
delete inset;
}
-
+
/// search for macro in local (buffer) table or in children
MacroData const * getBufferMacro(docstring const & name,
DocIterator const & pos) const;
mutable bool bibfile_cache_valid_;
/// Cache of timestamps of .bib files
map<FileName, time_t> bibfile_status_;
+ /// Indicates whether the bibinfo has changed since the last time
+ /// we ran updateBuffer(), i.e., whether citation labels may need
+ /// to be updated.
+ mutable bool cite_labels_valid_;
mutable RefCache ref_cache_;
/// our Text that should be wrapped in an InsetText
InsetText * inset;
+ ///
+ PreviewLoader * preview_loader_;
+
/// This is here to force the test to be done whenever parent_buffer
/// is accessed.
- Buffer const * parent() const {
+ Buffer const * parent() const {
// if parent_buffer is not loaded, then it has been unloaded,
// which means that parent_buffer is an invalid pointer. So we
// set it to null in that case.
// for the best.
if (!cloned_buffer_ && !theBufferList().isLoaded(parent_buffer))
parent_buffer = 0;
- return parent_buffer;
+ return parent_buffer;
}
-
+
///
void setParent(Buffer const * pb) {
if (parent_buffer == pb)
/// If non zero, this buffer is a clone of existing buffer \p cloned_buffer_
/// This one is useful for preview detached in a thread.
Buffer const * cloned_buffer_;
+ ///
+ CloneList * clone_list_;
/// are we in the process of exporting this buffer?
mutable bool doing_export;
-
+
+ /// compute statistics
+ /// \p from initial position
+ /// \p to points to the end position
+ void updateStatistics(DocIterator & from, DocIterator & to,
+ bool skipNoOutput = true);
+ /// statistics accessor functions
+ int wordCount() const { return word_count_; }
+ int charCount(bool with_blanks) const {
+ return char_count_
+ + (with_blanks ? blank_count_ : 0);
+ }
+
private:
/// So we can force access via the accessors.
mutable Buffer const * parent_buffer;
+ int word_count_;
+ int char_count_;
+ int blank_count_;
+
};
Buffer const * cloned_buffer)
: owner_(owner), lyx_clean(true), bak_clean(true), unnamed(false),
read_only(readonly_), filename(file), file_fully_loaded(false),
- toc_backend(owner), macro_lock(false), timestamp_(0),
- checksum_(0), wa_(0), gui_(0), undo_(*owner), bibinfo_cache_valid_(false),
- bibfile_cache_valid_(false), cloned_buffer_(cloned_buffer),
- doing_export(false), parent_buffer(0)
+ toc_backend(owner), macro_lock(false), timestamp_(0), checksum_(0),
+ wa_(0), gui_(0), undo_(*owner), bibinfo_cache_valid_(false),
+ bibfile_cache_valid_(false), cite_labels_valid_(false),
+ preview_loader_(0), cloned_buffer_(cloned_buffer), clone_list_(0),
+ doing_export(false), parent_buffer(0)
{
if (!cloned_buffer_) {
temppath = createBufferTmpDir();
bibinfo_cache_valid_ = cloned_buffer_->d->bibinfo_cache_valid_;
bibfile_cache_valid_ = cloned_buffer_->d->bibfile_cache_valid_;
bibfile_status_ = cloned_buffer_->d->bibfile_status_;
+ cite_labels_valid_ = cloned_buffer_->d->cite_labels_valid_;
+ unnamed = cloned_buffer_->d->unnamed;
}
return;
}
- // loop over children
- Impl::BufferPositionMap::iterator it = d->children_positions.begin();
- Impl::BufferPositionMap::iterator end = d->children_positions.end();
- for (; it != end; ++it) {
- Buffer * child = const_cast<Buffer *>(it->first);
- if (d->cloned_buffer_)
- delete child;
- // The child buffer might have been closed already.
- else if (theBufferList().isLoaded(child))
- theBufferList().releaseChild(this, child);
- }
+ if (isClone()) {
+ // this is in case of recursive includes: we won't try to delete
+ // ourselves as a child.
+ d->clone_list_->erase(this);
+ // loop over children
+ Impl::BufferPositionMap::iterator it = d->children_positions.begin();
+ Impl::BufferPositionMap::iterator end = d->children_positions.end();
+ for (; it != end; ++it) {
+ Buffer * child = const_cast<Buffer *>(it->first);
+ if (d->clone_list_->erase(child))
+ delete child;
+ }
+ // if we're the master buffer, then we should get rid of the list
+ // of clones
+ if (!parent()) {
+ // if this is not empty, we have leaked something. worse, one of the
+ // children still has a reference to this list.
+ LASSERT(d->clone_list_->empty(), /* */);
+ list<CloneList *>::iterator it =
+ find(cloned_buffers.begin(), cloned_buffers.end(), d->clone_list_);
+ LASSERT(it != cloned_buffers.end(), /* */);
+ cloned_buffers.erase(it);
+ delete d->clone_list_;
+ }
+ // FIXME Do we really need to do this right before we delete d?
+ // clear references to children in macro tables
+ d->children_positions.clear();
+ d->position_to_children.clear();
+ } else {
+ // loop over children
+ Impl::BufferPositionMap::iterator it = d->children_positions.begin();
+ Impl::BufferPositionMap::iterator end = d->children_positions.end();
+ for (; it != end; ++it) {
+ Buffer * child = const_cast<Buffer *>(it->first);
+ if (theBufferList().isLoaded(child))
+ theBufferList().releaseChild(this, child);
+ }
- if (!isClean()) {
- docstring msg = _("LyX attempted to close a document that had unsaved changes!\n");
- msg += emergencyWrite();
- Alert::warning(_("Attempting to close changed document!"), msg);
- }
-
- // clear references to children in macro tables
- d->children_positions.clear();
- d->position_to_children.clear();
+ if (!isClean()) {
+ docstring msg = _("LyX attempted to close a document that had unsaved changes!\n");
+ msg += emergencyWrite();
+ Alert::warning(_("Attempting to close changed document!"), msg);
+ }
- if (!d->cloned_buffer_ && !d->temppath.destroyDirectory()) {
- Alert::warning(_("Could not remove temporary directory"),
- bformat(_("Could not remove the temporary directory %1$s"),
- from_utf8(d->temppath.absFileName())));
- }
+ // FIXME Do we really need to do this right before we delete d?
+ // clear references to children in macro tables
+ d->children_positions.clear();
+ d->position_to_children.clear();
- if (!isClone())
+ if (!d->temppath.destroyDirectory()) {
+ Alert::warning(_("Could not remove temporary directory"),
+ bformat(_("Could not remove the temporary directory %1$s"),
+ from_utf8(d->temppath.absFileName())));
+ }
removePreviews();
+ }
delete d;
}
-Buffer * Buffer::clone() const
+Buffer * Buffer::cloneFromMaster() const
+{
+ BufferMap bufmap;
+ cloned_buffers.push_back(new CloneList());
+ CloneList * clones = cloned_buffers.back();
+
+ masterBuffer()->cloneWithChildren(bufmap, clones);
+
+ // make sure we got cloned
+ BufferMap::const_iterator bit = bufmap.find(this);
+ LASSERT(bit != bufmap.end(), return 0);
+ Buffer * cloned_buffer = bit->second;
+
+ return cloned_buffer;
+}
+
+
+void Buffer::cloneWithChildren(BufferMap & bufmap, CloneList * clones) const
{
+ // have we already been cloned?
+ if (bufmap.find(this) != bufmap.end())
+ return;
+
Buffer * buffer_clone = new Buffer(fileName().absFileName(), false, this);
+ bufmap[this] = buffer_clone;
+ clones->insert(buffer_clone);
+ buffer_clone->d->clone_list_ = clones;
buffer_clone->d->macro_lock = true;
buffer_clone->d->children_positions.clear();
// FIXME (Abdel 09/01/2010): this is too complicated. The whole children_positions and
// math macro caches need to be rethought and simplified.
// I am not sure wether we should handle Buffer cloning here or in BufferList.
// Right now BufferList knows nothing about buffer clones.
- Impl::BufferPositionMap::iterator it = d->children_positions.begin();
- Impl::BufferPositionMap::iterator end = d->children_positions.end();
+ Impl::PositionScopeBufferMap::iterator it = d->position_to_children.begin();
+ Impl::PositionScopeBufferMap::iterator end = d->position_to_children.end();
for (; it != end; ++it) {
- DocIterator dit = it->second.clone(buffer_clone);
+ DocIterator dit = it->first.clone(buffer_clone);
dit.setBuffer(buffer_clone);
- Buffer * child = const_cast<Buffer *>(it->first);
- Buffer * child_clone = child->clone();
+ Buffer * child = const_cast<Buffer *>(it->second.second);
+
+ child->cloneWithChildren(bufmap, clones);
+ BufferMap::iterator const bit = bufmap.find(child);
+ LASSERT(bit != bufmap.end(), continue);
+ Buffer * child_clone = bit->second;
+
Inset * inset = dit.nextInset();
LASSERT(inset && inset->lyxCode() == INCLUDE_CODE, continue);
InsetInclude * inset_inc = static_cast<InsetInclude *>(inset);
inset_inc->setChildBuffer(child_clone);
child_clone->d->setParent(buffer_clone);
+ // FIXME Do we need to do this now, or can we wait until we run updateMacros()?
buffer_clone->setChild(dit, child_clone);
}
buffer_clone->d->macro_lock = false;
+ return;
+}
+
+
+Buffer * Buffer::cloneBufferOnly() const {
+ cloned_buffers.push_back(new CloneList());
+ CloneList * clones = cloned_buffers.back();
+ Buffer * buffer_clone = new Buffer(fileName().absFileName(), false, this);
+ clones->insert(buffer_clone);
+ buffer_clone->d->clone_list_ = clones;
+ // we won't be cloning the children
+ buffer_clone->d->children_positions.clear();
return buffer_clone;
}
FileName const bname(
addName(path, onlyFileName(
changeExtension(filename,
- formats.extension(bufferFormat()) + ".out"))));
+ formats.extension(params().bufferFormat()) + ".out"))));
// Also consider the master buffer log file
FileName masterfname = fname;
params().indiceslist().clear();
params().backgroundcolor = lyx::rgbFromHexName("#ffffff");
params().isbackgroundcolor = false;
- params().fontcolor = lyx::rgbFromHexName("#000000");
+ params().fontcolor = RGBColor(0, 0, 0);
params().isfontcolor = false;
- params().notefontcolor = lyx::rgbFromHexName("#cccccc");
- params().boxbgcolor = lyx::rgbFromHexName("#ff0000");
+ params().notefontcolor = RGBColor(0xCC, 0xCC, 0xCC);
+ params().boxbgcolor = RGBColor(0xFF, 0, 0);
params().html_latex_start.clear();
params().html_latex_end.clear();
params().html_math_img_scale = 1.0;
FileName const master_file = makeAbsPath(params().master,
onlyPath(absFileName()));
if (isLyXFileName(master_file.absFileName())) {
- Buffer * master =
+ Buffer * master =
checkAndLoadLyXFile(master_file, true);
if (master) {
// necessary e.g. after a reload
}
}
}
-
+
// assure we have a default index
params().indiceslist().addDefault(B_("Index"));
}
usermacros.clear();
updateMacros();
- updateMacroInstances();
+ updateMacroInstances(InternalUpdate);
return res;
}
{
FileName fname(fn);
Lexer lex;
- lex.setFile(fname);
+ if (!lex.setFile(fname)) {
+ Alert::error(_("File Not Found"),
+ bformat(_("Unable to open file `%1$s'."),
+ from_utf8(fn.absFileName())));
+ return ReadFileNotFound;
+ }
int file_format;
ReadStatus const ret_plf = parseLyXFormat(lex, fn, file_format);
return readFile(tmpFile);
}
- // FIXME: InsetInfo needs to know whether the file is under VCS
+ // FIXME: InsetInfo needs to know whether the file is under VCS
// during the parse process, so this has to be done before.
lyxvc().file_found_hook(d->filename);
}
-void Buffer::updatePreviews() const
+PreviewLoader * Buffer::loader() const
{
- if (graphics::Previews::status() != LyXRC::PREVIEW_OFF)
- thePreviews().generateBufferPreviews(*this);
+ if (!isExporting() && lyxrc.preview == LyXRC::PREVIEW_OFF)
+ return 0;
+ if (!d->preview_loader_)
+ d->preview_loader_ = new PreviewLoader(*this);
+ return d->preview_loader_;
}
void Buffer::removePreviews() const
{
- thePreviews().removeLoader(*this);
+ delete d->preview_loader_;
+ d->preview_loader_ = 0;
+}
+
+
+void Buffer::updatePreviews() const
+{
+ PreviewLoader * ploader = loader();
+ if (!ploader)
+ return;
+
+ InsetIterator it = inset_iterator_begin(*d->inset);
+ InsetIterator const end = inset_iterator_end(*d->inset);
+ for (; it != end; ++it)
+ it->addPreview(it, *ploader);
+
+ ploader->startLoading();
}
bformat(_("%1$s is not a readable LyX document."),
from_utf8(fn.absFileName())));
return ReadNoLyXFormat;
- }
+ }
string tmp_format;
lex >> tmp_format;
}
-Buffer::ReadStatus Buffer::convertLyXFormat(FileName const & fn,
+Buffer::ReadStatus Buffer::convertLyXFormat(FileName const & fn,
FileName & tmpfile, int from_format)
{
tmpfile = FileName::tempName("Buffer_convertLyXFormat");
// ask if the disk file has been externally modified (use checksum method)
if (fileName().exists() && isExternallyModified(checksum_method)) {
- docstring text =
+ docstring text =
bformat(_("Document %1$s has been externally modified. "
"Are you sure you want to overwrite this file?"), file);
int const ret = Alert::prompt(_("Overwrite modified file?"),
bool Buffer::writeFile(FileName const & fname) const
{
+ // FIXME Do we need to do these here? I don't think writing
+ // the LyX file depends upon it. (RGH)
+ // updateBuffer();
+ // updateMacroInstances();
+
if (d->read_only && fname == d->filename)
return false;
}
// 2) In HOME directory.
- string s = addName(package().home_dir().absFileName(), absFileName());
+ string s = addName(Package::get_home_dir().absFileName(), absFileName());
s += ".emergency";
lyxerr << ' ' << s << endl;
if (writeFile(FileName(s))) {
bool Buffer::makeLaTeXFile(FileName const & fname,
string const & original_path,
OutputParams const & runparams_in,
- bool output_preamble, bool output_body) const
+ OutputWhat output) const
{
OutputParams runparams = runparams_in;
runparams.encoding = encodings.fromLyXName("utf8-plain");
string const encoding = runparams.encoding->iconvName();
- LYXERR(Debug::LATEX, "makeLaTeXFile encoding: " << encoding << "...");
+ LYXERR(Debug::LATEX, "makeLaTeXFile encoding: " << encoding << ", fname=" << fname.realPath());
ofdocstream ofs;
try { ofs.reset(encoding); }
errorList.clear();
bool failed_export = false;
otexstream os(ofs, d->texrow);
+
+ // make sure we are ready to export
+ // this needs to be done before we validate
+ // FIXME Do we need to do this all the time? I.e., in children
+ // of a master we are exporting?
+ updateBuffer();
+ updateMacroInstances(OutputUpdate);
+
try {
os.texrow().reset();
- writeLaTeXSource(os, original_path,
- runparams, output_preamble, output_body);
+ writeLaTeXSource(os, original_path, runparams, output);
}
catch (EncodingException & e) {
odocstringstream ods;
void Buffer::writeLaTeXSource(otexstream & os,
string const & original_path,
OutputParams const & runparams_in,
- bool const output_preamble, bool const output_body) const
+ OutputWhat output) const
{
// The child documents, if any, shall be already loaded at this point.
validate(features);
LYXERR(Debug::LATEX, " Buffer validation done.");
+ bool const output_preamble =
+ output == FullSource || output == OnlyPreamble;
+ bool const output_body =
+ output == FullSource || output == OnlyBody;
+
// The starting paragraph of the coming rows is the
// first paragraph of the document. (Asger)
if (output_preamble && runparams.nice) {
}
LYXERR(Debug::INFO, "lyx document header finished");
- // Don't move this behind the parent_buffer=0 code below,
- // because then the macros will not get the right "redefinition"
- // flag as they don't see the parent macros which are output before.
- updateMacros();
-
- // fold macros if possible, still with parent buffer as the
- // macros will be put in the prefix anyway.
- updateMacroInstances();
-
// There are a few differences between nice LaTeX and usual files:
- // usual is \batchmode and has a
- // special input@path to allow the including of figures
- // with either \input or \includegraphics (what figinsets do).
- // input@path is set when the actual parameter
- // original_path is set. This is done for usual tex-file, but not
- // for nice-latex-file. (Matthias 250696)
+ // usual files have \batchmode and special input@path to allow
+ // inclusion of figures specified by an explicitly relative path
+ // (i.e., a path starting with './' or '../') with either \input or
+ // \includegraphics, as the TEXINPUTS method doesn't work in this case.
+ // input@path is set when the actual parameter original_path is set.
+ // This is done for usual tex-file, but not for nice-latex-file.
+ // (Matthias 250696)
// Note that input@path is only needed for something the user does
// in the preamble, included .tex files or ERT, files included by
// LyX work without it.
if (!original_path.empty()) {
// FIXME UNICODE
// We don't know the encoding of inputpath
- docstring const inputpath = from_utf8(support::latex_path(original_path));
+ docstring const inputpath = from_utf8(original_path);
docstring uncodable_glyphs;
Encoding const * const enc = runparams.encoding;
if (enc) {
// warn user if we found uncodable glyphs.
if (!uncodable_glyphs.empty()) {
- frontend::Alert::warning(_("Uncodable character in file path"),
- support::bformat(_("The path of your document\n"
- "(%1$s)\n"
- "contains glyphs that are unknown in the\n"
- "current document encoding (namely %2$s).\n"
- "This will likely result in incomplete output.\n\n"
- "Choose an appropriate document encoding (such as utf8)\n"
- "or change the file path name."), inputpath, uncodable_glyphs));
+ frontend::Alert::warning(
+ _("Uncodable character in file path"),
+ support::bformat(
+ _("The path of your document\n"
+ "(%1$s)\n"
+ "contains glyphs that are unknown "
+ "in the current document encoding "
+ "(namely %2$s). This may result in "
+ "incomplete output, unless "
+ "TEXINPUTS contains the document "
+ "directory and you don't use "
+ "explicitly relative paths (i.e., "
+ "paths starting with './' or "
+ "'../') in the preamble or in ERT."
+ "\n\nIn case of problems, choose "
+ "an appropriate document encoding\n"
+ "(such as utf8) or change the "
+ "file path name."),
+ inputpath, uncodable_glyphs));
} else {
+ string docdir =
+ support::latex_path(original_path);
+ if (contains(docdir, '#')) {
+ docdir = subst(docdir, "#", "\\#");
+ os << "\\catcode`\\#=11"
+ "\\def\\#{#}\\catcode`\\#=6\n";
+ }
+ if (contains(docdir, '%')) {
+ docdir = subst(docdir, "%", "\\%");
+ os << "\\catcode`\\%=11"
+ "\\def\\%{%}\\catcode`\\%=14\n";
+ }
os << "\\makeatletter\n"
<< "\\def\\input@path{{"
- << inputpath << "/}}\n"
+ << docdir << "/}}\n"
<< "\\makeatother\n";
}
}
int num_lines = (*it)->write(os.os(), true);
os.texrow().newlines(num_lines);
}
-
+
} // output_preamble
os.texrow().start(paragraphs().begin()->id(), 0);
}
-bool Buffer::isLatex() const
-{
- return params().documentClass().outputType() == LATEX;
-}
-
-
-bool Buffer::isLiterate() const
-{
- return params().documentClass().outputType() == LITERATE;
-}
-
-
-bool Buffer::isDocBook() const
-{
- return params().documentClass().outputType() == DOCBOOK;
-}
-
-
void Buffer::makeDocBookFile(FileName const & fname,
OutputParams const & runparams,
- bool const body_only) const
+ OutputWhat output) const
{
LYXERR(Debug::LATEX, "makeDocBookFile...");
if (!openFileWrite(ofs, fname))
return;
- writeDocBookSource(ofs, fname.absFileName(), runparams, body_only);
+ // make sure we are ready to export
+ // this needs to be done before we validate
+ updateBuffer();
+ updateMacroInstances(OutputUpdate);
+
+ writeDocBookSource(ofs, fname.absFileName(), runparams, output);
ofs.close();
if (ofs.fail())
void Buffer::writeDocBookSource(odocstream & os, string const & fname,
OutputParams const & runparams,
- bool const only_body) const
+ OutputWhat output) const
{
LaTeXFeatures features(*this, params(), runparams);
validate(features);
DocumentClass const & tclass = params().documentClass();
string const top_element = tclass.latexname();
- if (!only_body) {
+ bool const output_preamble =
+ output == FullSource || output == OnlyPreamble;
+ bool const output_body =
+ output == FullSource || output == OnlyBody;
+
+ if (output_preamble) {
if (runparams.flavor == OutputParams::XML)
os << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
os << ">\n\n";
}
- string top = top_element;
- top += " lang=\"";
- if (runparams.flavor == OutputParams::XML)
- top += params().language->code();
- else
- top += params().language->code().substr(0, 2);
- top += '"';
-
- if (!params().options.empty()) {
- top += ' ';
- top += params().options;
- }
+ if (output_body) {
+ string top = top_element;
+ top += " lang=\"";
+ if (runparams.flavor == OutputParams::XML)
+ top += params().language->code();
+ else
+ top += params().language->code().substr(0, 2);
+ top += '"';
- os << "<!-- " << ((runparams.flavor == OutputParams::XML)? "XML" : "SGML")
- << " file was created by LyX " << lyx_version
- << "\n See http://www.lyx.org/ for more information -->\n";
+ if (!params().options.empty()) {
+ top += ' ';
+ top += params().options;
+ }
- params().documentClass().counters().reset();
+ os << "<!-- " << ((runparams.flavor == OutputParams::XML)? "XML" : "SGML")
+ << " file was created by LyX " << lyx_version
+ << "\n See http://www.lyx.org/ for more information -->\n";
- updateMacros();
+ params().documentClass().counters().reset();
- sgml::openTag(os, top);
- os << '\n';
- docbookParagraphs(text(), *this, os, runparams);
- sgml::closeTag(os, top_element);
+ sgml::openTag(os, top);
+ os << '\n';
+ docbookParagraphs(text(), *this, os, runparams);
+ sgml::closeTag(os, top_element);
+ }
}
void Buffer::makeLyXHTMLFile(FileName const & fname,
- OutputParams const & runparams,
- bool const body_only) const
+ OutputParams const & runparams) const
{
LYXERR(Debug::LATEX, "makeLyXHTMLFile...");
if (!openFileWrite(ofs, fname))
return;
- writeLyXHTMLSource(ofs, runparams, body_only);
+ // make sure we are ready to export
+ // this has to be done before we validate
+ updateBuffer(UpdateMaster, OutputUpdate);
+ updateMacroInstances(OutputUpdate);
+
+ writeLyXHTMLSource(ofs, runparams, FullSource);
ofs.close();
if (ofs.fail())
void Buffer::writeLyXHTMLSource(odocstream & os,
OutputParams const & runparams,
- bool const only_body) const
+ OutputWhat output) const
{
LaTeXFeatures features(*this, params(), runparams);
validate(features);
- updateBuffer(UpdateMaster, OutputUpdate);
d->bibinfo_.makeCitationLabels(*this);
- updateMacros();
- updateMacroInstances();
- if (!only_body) {
+ bool const output_preamble =
+ output == FullSource || output == OnlyPreamble;
+ bool const output_body =
+ output == FullSource || output == OnlyBody;
+
+ if (output_preamble) {
os << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
<< "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.1 plus MathML 2.0//EN\" \"http://www.w3.org/TR/MathML2/dtd/xhtml-math11-f.dtd\">\n"
// FIXME Language should be set properly.
docstring const & doctitle = features.htmlTitle();
os << "<title>"
- << (doctitle.empty() ? from_ascii("LyX Document") : doctitle)
+ << (doctitle.empty() ?
+ from_ascii("LyX Document") :
+ html::htmlize(doctitle, XHTMLStream::ESCAPE_ALL))
<< "</title>\n";
- os << "\n<!-- Text Class Preamble -->\n"
- << features.getTClassHTMLPreamble()
- << "\n<!-- Preamble Snippets -->\n"
- << from_utf8(features.getPreambleSnippets());
-
- os << "\n<!-- Layout-provided Styles -->\n";
- docstring const styleinfo = features.getTClassHTMLStyles();
- if (!styleinfo.empty()) {
- os << "<style type='text/css'>\n"
- << styleinfo
- << "</style>\n";
+ docstring styles = features.getTClassHTMLPreamble();
+ if (!styles.empty())
+ os << "\n<!-- Text Class Preamble -->\n" << styles << '\n';
+
+ styles = from_utf8(features.getPreambleSnippets());
+ if (!styles.empty())
+ os << "\n<!-- Preamble Snippets -->\n" << styles << '\n';
+
+ // we will collect CSS information in a stream, and then output it
+ // either here, as part of the header, or else in a separate file.
+ odocstringstream css;
+ styles = from_utf8(features.getCSSSnippets());
+ if (!styles.empty())
+ css << "/* LyX Provided Styles */\n" << styles << '\n';
+
+ styles = features.getTClassHTMLStyles();
+ if (!styles.empty())
+ css << "/* Layout-provided Styles */\n" << styles << '\n';
+
+ bool const needfg = params().fontcolor != RGBColor(0, 0, 0);
+ bool const needbg = params().backgroundcolor != RGBColor(0xFF, 0xFF, 0xFF);
+ if (needfg || needbg) {
+ css << "\nbody {\n";
+ if (needfg)
+ css << " color: "
+ << from_ascii(X11hexname(params().fontcolor))
+ << ";\n";
+ if (needbg)
+ css << " background-color: "
+ << from_ascii(X11hexname(params().backgroundcolor))
+ << ";\n";
+ css << "}\n";
+ }
+
+ docstring const dstyles = css.str();
+ if (!dstyles.empty()) {
+ bool written = false;
+ if (params().html_css_as_file) {
+ // open a file for CSS info
+ ofdocstream ocss;
+ string const fcssname = addName(temppath(), "docstyle.css");
+ FileName const fcssfile = FileName(fcssname);
+ if (openFileWrite(ocss, fcssfile)) {
+ ocss << dstyles;
+ ocss.close();
+ written = true;
+ // write link to header
+ os << "<link rel='stylesheet' href='docstyle.css' type='text/css' />\n";
+ // register file
+ runparams.exportdata->addExternalFile("xhtml", fcssfile);
+ }
+ }
+ // we are here if the CSS is supposed to be written to the header
+ // or if we failed to write it to an external file.
+ if (!written) {
+ os << "<style type='text/css'>\n"
+ << dstyles
+ << "\n</style>\n";
+ }
}
- os << "</head>\n<body>\n";
+ os << "</head>\n";
}
- XHTMLStream xs(os);
- params().documentClass().counters().reset();
- xhtmlParagraphs(text(), *this, xs, runparams);
- if (!only_body)
- os << "</body>\n</html>\n";
+ if (output_body) {
+ os << "<body>\n";
+ XHTMLStream xs(os);
+ params().documentClass().counters().reset();
+ xhtmlParagraphs(text(), *this, xs, runparams);
+ os << "</body>\n";
+ }
+
+ if (output_preamble)
+ os << "</html>\n";
}
{
params().validate(features);
- updateMacros();
-
for_each(paragraphs().begin(), paragraphs().end(),
bind(&Paragraph::validate, _1, ref(features)));
}
d->bibfile_cache_valid_ = true;
d->bibinfo_cache_valid_ = false;
+ d->cite_labels_valid_ = false;
}
void Buffer::invalidateBibinfoCache() const
{
d->bibinfo_cache_valid_ = false;
+ d->cite_labels_valid_ = false;
// also invalidate the cache for the parent buffer
Buffer const * const pbuf = d->parent();
if (pbuf)
{
d->bibfile_cache_valid_ = false;
d->bibinfo_cache_valid_ = false;
+ d->cite_labels_valid_ = false;
// also invalidate the cache for the parent buffer
Buffer const * const pbuf = d->parent();
if (pbuf)
time_t prevw = d->bibfile_status_[*ei];
if (lastw != prevw) {
d->bibinfo_cache_valid_ = false;
+ d->cite_labels_valid_ = false;
d->bibfile_status_[*ei] = lastw;
}
}
}
+bool Buffer::citeLabelsValid() const
+{
+ return masterBuffer()->d->cite_labels_valid_;
+}
+
+
+void Buffer::removeBiblioTempFiles() const
+{
+ // We remove files that contain LaTeX commands specific to the
+ // particular bibliographic style being used, in order to avoid
+ // LaTeX errors when we switch style.
+ FileName const aux_file(addName(temppath(), changeExtension(latexName(),".aux")));
+ FileName const bbl_file(addName(temppath(), changeExtension(latexName(),".bbl")));
+ LYXERR(Debug::FILES, "Removing the .aux file " << aux_file);
+ aux_file.removeFile();
+ LYXERR(Debug::FILES, "Removing the .bbl file " << bbl_file);
+ bbl_file.removeFile();
+ // Also for the parent buffer
+ Buffer const * const pbuf = parent();
+ if (pbuf)
+ pbuf->removeBiblioTempFiles();
+}
+
+
bool Buffer::isDepClean(string const & name) const
{
DepClean::const_iterator const it = d->dep_clean.find(name);
}
-bool Buffer::isExportableFormat(string const & format) const
-{
- typedef vector<Format const *> Formats;
- Formats formats;
- formats = exportableFormats(true);
- Formats::const_iterator fit = formats.begin();
- Formats::const_iterator end = formats.end();
- for (; fit != end ; ++fit) {
- if ((*fit)->name() == format)
- return true;
- }
- return false;
-}
-
-
bool Buffer::getStatus(FuncRequest const & cmd, FuncStatus & flag)
{
if (isInternal()) {
case LFUN_BUFFER_EXPORT: {
docstring const arg = cmd.argument();
- enable = arg == "custom" || isExportable(to_utf8(arg));
+ if (arg == "custom") {
+ enable = true;
+ break;
+ }
+ string format = to_utf8(arg);
+ size_t pos = format.find(' ');
+ if (pos != string::npos)
+ format = format.substr(0, pos);
+ enable = params().isExportable(format);
if (!enable)
flag.message(bformat(
_("Don't know how to export to format: %1$s"), arg));
}
case LFUN_BUFFER_CHKTEX:
- enable = isLatex() && !lyxrc.chktex_command.empty();
+ enable = params().isLatex() && !lyxrc.chktex_command.empty();
break;
case LFUN_BUILD_PROGRAM:
- enable = isExportable("program");
+ enable = params().isExportable("program");
break;
- case LFUN_BRANCH_ACTIVATE:
- case LFUN_BRANCH_DEACTIVATE: {
- BranchList const & branchList = params().branchlist();
- docstring const branchName = cmd.argument();
- enable = !branchName.empty() && branchList.find(branchName);
- break;
- }
-
case LFUN_BRANCH_ADD:
case LFUN_BRANCHES_RENAME:
case LFUN_BUFFER_PRINT:
// NOTE We can end up here even if we have no GUI, because we are called
-// by LyX::exec to handled command-line requests. So we may need to check
+// by LyX::exec to handled command-line requests. So we may need to check
// whether we have a GUI or not. The boolean use_gui holds this information.
void Buffer::dispatch(FuncRequest const & func, DispatchResult & dr)
{
break;
case LFUN_BUFFER_EXPORT: {
- bool success = doExport(argument, false, false);
- dr.setError(!success);
- if (!success)
- dr.setMessage(bformat(_("Error exporting to format: %1$s."),
+ ExportStatus const status = doExport(argument, false);
+ dr.setError(status != ExportSuccess);
+ if (status != ExportSuccess)
+ dr.setMessage(bformat(_("Error exporting to format: %1$s."),
func.argument()));
break;
}
case LFUN_BUILD_PROGRAM:
- doExport("program", true, false);
+ doExport("program", true);
break;
case LFUN_BUFFER_CHKTEX:
break;
} else {
- doExport(format_name, true, false, filename);
+ doExport(format_name, true, filename);
}
// Substitute $$FName for filename
// Execute the command in the background
Systemcall call;
- call.startscript(Systemcall::DontWait, command);
+ call.startscript(Systemcall::DontWait, command, filePath());
break;
}
break;
}
- case LFUN_BRANCH_ACTIVATE:
- case LFUN_BRANCH_DEACTIVATE: {
- BranchList & branchList = params().branchlist();
- docstring const branchName = func.argument();
- // the case without a branch name is handled elsewhere
- if (branchName.empty()) {
- dispatched = false;
- break;
- }
- Branch * branch = branchList.find(branchName);
- if (!branch) {
- LYXERR0("Branch " << branchName << " does not exist.");
- dr.setError(true);
- docstring const msg =
- bformat(_("Branch \"%1$s\" does not exist."), branchName);
- dr.setMessage(msg);
- } else {
- branch->setSelected(func.action() == LFUN_BRANCH_ACTIVATE);
- dr.setError(false);
- dr.screenUpdate(Update::Force);
- dr.forceBufferUpdate();
- }
- break;
- }
-
case LFUN_BRANCHES_RENAME: {
if (func.argument().empty())
break;
case LFUN_BUFFER_PRINT: {
// we'll assume there's a problem until we succeed
- dr.setError(true);
+ dr.setError(true);
string target = func.getArg(0);
string target_name = func.getArg(1);
string command = func.getArg(2);
|| target_name.empty()
|| command.empty()) {
LYXERR0("Unable to parse " << func.argument());
- docstring const msg =
+ docstring const msg =
bformat(_("Unable to parse \"%1$s\""), func.argument());
dr.setMessage(msg);
break;
}
if (target != "printer" && target != "file") {
LYXERR0("Unrecognized target \"" << target << '"');
- docstring const msg =
+ docstring const msg =
bformat(_("Unrecognized target \"%1$s\""), from_utf8(target));
dr.setMessage(msg);
break;
}
- bool const update_unincluded =
- params().maintain_unincluded_children
- && !params().getIncludedChildren().empty();
- if (!doExport("dvi", true, update_unincluded)) {
+ if (!doExport("dvi", true)) {
showPrintError(absFileName());
dr.setMessage(_("Error exporting to DVI."));
break;
command2 += quoteName(psname);
// First run dvips.
// If successful, then spool command
- res = one.startscript(Systemcall::Wait, command);
+ res = one.startscript(Systemcall::Wait, command,
+ filePath());
if (res == 0) {
// If there's no GUI, we have to wait on this command. Otherwise,
// file, before it can be printed!!
Systemcall::Starttype stype = use_gui ?
Systemcall::DontWait : Systemcall::Wait;
- res = one.startscript(stype, command2);
+ res = one.startscript(stype, command2,
+ filePath());
}
} else {
// case 2: print directly to a printer
// as above....
Systemcall::Starttype stype = use_gui ?
Systemcall::DontWait : Systemcall::Wait;
- res = one.startscript(stype, command + quoteName(dviname));
+ res = one.startscript(stype, command +
+ quoteName(dviname), filePath());
}
} else {
// as above....
Systemcall::Starttype stype = use_gui ?
Systemcall::DontWait : Systemcall::Wait;
- res = one.startscript(stype, command);
+ res = one.startscript(stype, command, filePath());
}
- if (res == 0)
+ if (res == 0)
dr.setError(false);
else {
dr.setMessage(_("Error running external commands."));
break;
}
- case LFUN_BUFFER_LANGUAGE: {
- Language const * oldL = params().language;
- Language const * newL = languages.getLanguage(argument);
- if (!newL || oldL == newL)
- break;
- if (oldL->rightToLeft() == newL->rightToLeft() && !isMultiLingual()) {
- changeLanguage(oldL, newL);
- dr.forceBufferUpdate();
- }
- break;
- }
-
default:
dispatched = false;
break;
ListOfBuffers Buffer::allRelatives() const
{
ListOfBuffers lb = masterBuffer()->getDescendents();
- lb.push_front(const_cast<Buffer *>(this));
+ lb.push_front(const_cast<Buffer *>(masterBuffer()));
return lb;
}
bool Buffer::hasChildren() const
{
- return !d->children_positions.empty();
+ return !d->children_positions.empty();
}
if (bit != clist.end())
continue;
clist.push_back(child);
- if (grand_children)
+ if (grand_children)
// there might be grandchildren
child->collectChildren(clist, true);
}
{
ListOfBuffers v;
collectChildren(v, false);
+ // Make sure we have not included ourselves.
+ ListOfBuffers::iterator bit = find(v.begin(), v.end(), this);
+ if (bit != v.end()) {
+ LYXERR0("Recursive include detected in `" << fileName() << "'.");
+ v.erase(bit);
+ }
return v;
}
{
ListOfBuffers v;
collectChildren(v, true);
+ // Make sure we have not included ourselves.
+ ListOfBuffers::iterator bit = find(v.begin(), v.end(), this);
+ if (bit != v.end()) {
+ LYXERR0("Recursive include detected in `" << fileName() << "'.");
+ v.erase(bit);
+ }
return v;
}
it.pop_back();
continue;
}
-
+
if (iit->inset->asInsetTabular()) {
CursorSlice slice(*iit->inset);
size_t const numcells = slice.nargs();
}
-void Buffer::updateMacroInstances() const
+void Buffer::updateMacroInstances(UpdateType utype) const
{
LYXERR(Debug::MACROS, "updateMacroInstances for "
<< d->filename.onlyFileName());
MacroContext mc = MacroContext(this, it);
for (DocIterator::idx_type i = 0; i < n; ++i) {
MathData & data = minset->cell(i);
- data.updateMacros(0, mc);
+ data.updateMacros(0, mc, utype);
}
}
}
void Buffer::getSourceCode(odocstream & os, string const format,
pit_type par_begin, pit_type par_end,
- bool full_source) const
+ OutputWhat output) const
{
OutputParams runparams(¶ms().encoding());
runparams.nice = true;
- runparams.flavor = getOutputFlavor(format);
+ runparams.flavor = params().getOutputFlavor(format);
runparams.linelen = lyxrc.plaintext_linelen;
// No side effect of file copying and image conversion
runparams.dryrun = true;
- if (full_source) {
- os << "% " << _("Preview source code") << "\n\n";
- d->texrow.reset();
- d->texrow.newline();
- d->texrow.newline();
- if (isDocBook())
- writeDocBookSource(os, absFileName(), runparams, false);
- else if (runparams.flavor == OutputParams::HTML)
- writeLyXHTMLSource(os, runparams, false);
- else {
- // latex or literate
- otexstream ots(os, d->texrow);
- writeLaTeXSource(ots, string(), runparams, true, true);
- }
- } else {
+ if (output == CurrentParagraph) {
runparams.par_begin = par_begin;
runparams.par_end = par_end;
if (par_begin + 1 == par_end) {
texrow.newline();
texrow.newline();
// output paragraphs
- if (isDocBook())
+ if (params().isDocBook())
docbookParagraphs(text(), *this, os, runparams);
else if (runparams.flavor == OutputParams::HTML) {
XHTMLStream xs(os);
+ setMathFlavor(runparams);
xhtmlParagraphs(text(), *this, xs, runparams);
} else {
// latex or literate
otexstream ots(os, texrow);
latexParagraphs(*this, text(), ots, runparams);
}
+ } else {
+ os << "% ";
+ if (output == FullSource)
+ os << _("Preview source code");
+ else if (output == OnlyPreamble)
+ os << _("Preview preamble");
+ else if (output == OnlyBody)
+ os << _("Preview body");
+ os << "\n\n";
+ d->texrow.reset();
+ d->texrow.newline();
+ d->texrow.newline();
+ if (params().isDocBook())
+ writeDocBookSource(os, absFileName(), runparams, output);
+ else if (runparams.flavor == OutputParams::HTML)
+ writeLyXHTMLSource(os, runparams, output);
+ else {
+ // latex or literate
+ otexstream ots(os, d->texrow);
+ writeLaTeXSource(ots, string(), runparams, output);
+ }
}
}
ErrorList & Buffer::errorList(string const & type) const
{
- static ErrorList emptyErrorList;
- map<string, ErrorList>::iterator it = d->errorLists.find(type);
- if (it == d->errorLists.end())
- return emptyErrorList;
-
- return it->second;
+ return d->errorLists[type];
}
int AutoSaveBuffer::generateChild()
{
#if defined(__APPLE__)
- /* FIXME fork() is not usable for autosave on Mac OS X 10.6 (snow leopard)
+ /* FIXME fork() is not usable for autosave on Mac OS X 10.6 (snow leopard)
* We should use something else like threads.
*
* Since I do not know how to determine at run time what is the OS X
FileName Buffer::getAutosaveFileName() const
{
// if the document is unnamed try to save in the backup dir, else
- // in the default document path, and as a last try in the filePath,
+ // in the default document path, and as a last try in the filePath,
// which will most often be the temporary directory
string fpath;
if (isUnnamed())
}
-bool Buffer::autoSave() const
+bool Buffer::autoSave() const
{
Buffer const * buf = d->cloned_buffer_ ? d->cloned_buffer_ : this;
if (buf->d->bak_clean || isReadonly())
message(_("Autosaving current document..."));
buf->d->bak_clean = true;
-
+
FileName const fname = getAutosaveFileName();
if (d->cloned_buffer_) {
// If this buffer is cloned, we assume that
}
// failed to write/rename tmp_ret so try writing direct
return writeFile(fname);
- } else {
+ } else {
/// This function is deprecated as the frontend needs to take care
/// of cloning the buffer and autosaving it in another thread. It
/// is still here to allow (QT_VERSION < 0x040400).
}
-string Buffer::bufferFormat() const
-{
- string format = params().documentClass().outputFormat();
- if (format == "latex") {
- if (params().useNonTeXFonts)
- return "xetex";
- if (params().encoding().package() == Encoding::japanese)
- return "platex";
+// helper class, to guarantee this gets reset properly
+class Buffer::MarkAsExporting {
+public:
+ MarkAsExporting(Buffer const * buf) : buf_(buf)
+ {
+ LASSERT(buf_, /* */);
+ buf_->setExportStatus(true);
}
- return format;
-}
+ ~MarkAsExporting()
+ {
+ buf_->setExportStatus(false);
+ }
+private:
+ Buffer const * const buf_;
+};
-string Buffer::getDefaultOutputFormat() const
+
+void Buffer::setExportStatus(bool e) const
{
- if (!params().default_output_format.empty()
- && params().default_output_format != "default")
- return params().default_output_format;
- if (isDocBook()
- || params().useNonTeXFonts
- || params().encoding().package() == Encoding::japanese) {
- vector<Format const *> const formats = exportableFormats(true);
- if (formats.empty())
- return string();
- // return the first we find
- return formats.front()->name();
- }
- return lyxrc.default_view_format;
+ d->doing_export = e;
+ ListOfBuffers clist = getDescendents();
+ ListOfBuffers::const_iterator cit = clist.begin();
+ ListOfBuffers::const_iterator const cen = clist.end();
+ for (; cit != cen; ++cit)
+ (*cit)->d->doing_export = e;
}
-OutputParams::FLAVOR Buffer::getOutputFlavor(string const format) const
+bool Buffer::isExporting() const
{
- string const dformat = (format.empty() || format == "default") ?
- getDefaultOutputFormat() : format;
- DefaultFlavorCache::const_iterator it =
- default_flavors_.find(dformat);
-
- if (it != default_flavors_.end())
- return it->second;
-
- OutputParams::FLAVOR result = OutputParams::LATEX;
-
- if (dformat == "xhtml")
- result = OutputParams::HTML;
- else {
- // Try to determine flavor of default output format
- vector<string> backs = backends();
- if (find(backs.begin(), backs.end(), dformat) == backs.end()) {
- // Get shortest path to format
- Graph::EdgePath path;
- for (vector<string>::const_iterator it = backs.begin();
- it != backs.end(); ++it) {
- Graph::EdgePath p = theConverters().getPath(*it, dformat);
- if (!p.empty() && (path.empty() || p.size() < path.size())) {
- path = p;
- }
- }
- if (!path.empty())
- result = theConverters().getFlavor(path);
- }
- }
- // cache this flavor
- default_flavors_[dformat] = result;
- return result;
+ return d->doing_export;
}
-namespace {
- // helper class, to guarantee this gets reset properly
- class MarkAsExporting {
- public:
- MarkAsExporting(Buffer const * buf) : buf_(buf)
- {
- LASSERT(buf_, /* */);
- buf_->setExportStatus(true);
- }
- ~MarkAsExporting()
- {
- buf_->setExportStatus(false);
- }
- private:
- Buffer const * const buf_;
- };
+Buffer::ExportStatus Buffer::doExport(string const & target, bool put_in_tempdir)
+ const
+{
+ string result_file;
+ return doExport(target, put_in_tempdir, result_file);
}
-
-void Buffer::setExportStatus(bool e) const
+Buffer::ExportStatus Buffer::doExport(string const & target, bool put_in_tempdir,
+ string & result_file) const
{
- d->doing_export = e;
+ bool const update_unincluded =
+ params().maintain_unincluded_children
+ && !params().getIncludedChildren().empty();
+
+ // (1) export with all included children (omit \includeonly)
+ if (update_unincluded) {
+ ExportStatus const status =
+ doExport(target, put_in_tempdir, true, result_file);
+ if (status != ExportSuccess)
+ return status;
+ }
+ // (2) export with included children only
+ return doExport(target, put_in_tempdir, false, result_file);
}
-bool Buffer::isExporting() const
+void Buffer::setMathFlavor(OutputParams & op) const
{
- return d->doing_export;
+ switch (params().html_math_output) {
+ case BufferParams::MathML:
+ op.math_flavor = OutputParams::MathAsMathML;
+ break;
+ case BufferParams::HTML:
+ op.math_flavor = OutputParams::MathAsHTML;
+ break;
+ case BufferParams::Images:
+ op.math_flavor = OutputParams::MathAsImages;
+ break;
+ case BufferParams::LaTeX:
+ op.math_flavor = OutputParams::MathAsLaTeX;
+ break;
+ }
}
-bool Buffer::doExport(string const & format, bool put_in_tempdir,
+Buffer::ExportStatus Buffer::doExport(string const & target, bool put_in_tempdir,
bool includeall, string & result_file) const
{
+ LYXERR(Debug::FILES, "target=" << target);
+ OutputParams runparams(¶ms().encoding());
+ string format = target;
+ string dest_filename;
+ size_t pos = target.find(' ');
+ if (pos != string::npos) {
+ dest_filename = target.substr(pos + 1, target.length() - pos - 1);
+ format = target.substr(0, pos);
+ runparams.export_folder = FileName(dest_filename).onlyPath().realPath();
+ FileName(dest_filename).onlyPath().createPath();
+ LYXERR(Debug::FILES, "format=" << format << ", dest_filename=" << dest_filename << ", export_folder=" << runparams.export_folder);
+ }
MarkAsExporting exporting(this);
string backend_format;
- OutputParams runparams(¶ms().encoding());
runparams.flavor = OutputParams::LATEX;
runparams.linelen = lyxrc.plaintext_linelen;
runparams.includeall = includeall;
- vector<string> backs = backends();
+ vector<string> backs = params().backends();
Converters converters = theConverters();
if (find(backs.begin(), backs.end(), format) == backs.end()) {
// Get shortest path to format
_("No information for exporting the format %1$s."),
formats.prettyName(format)));
}
- return false;
+ return ExportNoPathToFormat;
}
runparams.flavor = converters.getFlavor(path);
} else {
backend_format = format;
+ LYXERR(Debug::FILES, "backend_format=" << backend_format);
// FIXME: Don't hardcode format names here, but use a flag
if (backend_format == "pdflatex")
runparams.flavor = OutputParams::PDFLATEX;
else if (backend_format == "luatex")
runparams.flavor = OutputParams::LUATEX;
+ else if (backend_format == "dviluatex")
+ runparams.flavor = OutputParams::DVILUATEX;
else if (backend_format == "xetex")
runparams.flavor = OutputParams::XETEX;
}
filename = addName(temppath(), filename);
filename = changeExtension(filename,
formats.extension(backend_format));
-
- // fix macros
- updateMacroInstances();
+ LYXERR(Debug::FILES, "filename=" << filename);
// Plain text backend
if (backend_format == "text") {
// HTML backend
else if (backend_format == "xhtml") {
runparams.flavor = OutputParams::HTML;
- switch (params().html_math_output) {
- case BufferParams::MathML:
- runparams.math_flavor = OutputParams::MathAsMathML;
- break;
- case BufferParams::HTML:
- runparams.math_flavor = OutputParams::MathAsHTML;
- break;
- case BufferParams::Images:
- runparams.math_flavor = OutputParams::MathAsImages;
- break;
- case BufferParams::LaTeX:
- runparams.math_flavor = OutputParams::MathAsLaTeX;
- break;
- }
+ setMathFlavor(runparams);
makeLyXHTMLFile(FileName(filename), runparams);
} else if (backend_format == "lyx")
writeFile(FileName(filename));
// Docbook backend
- else if (isDocBook()) {
+ else if (params().isDocBook()) {
runparams.nice = !put_in_tempdir;
makeDocBookFile(FileName(filename), runparams);
}
d->cloned_buffer_->d->errorLists["Export"] =
d->errorLists["Export"];
}
- return false;
+ return ExportError;
}
} else if (!lyxrc.tex_allows_spaces
&& contains(filePath(), ' ')) {
Alert::error(_("File name error"),
_("The directory path to the document cannot contain spaces."));
- return false;
+ return ExportTexPathHasSpaces;
} else {
runparams.nice = false;
if (!makeLaTeXFile(FileName(filename), filePath(), runparams)) {
d->cloned_buffer_->d->errorLists["Export"] =
d->errorLists["Export"];
}
- return false;
+ return ExportError;
}
}
string const error_type = (format == "program")
- ? "Build" : bufferFormat();
+ ? "Build" : params().bufferFormat();
ErrorList & error_list = d->errorLists[error_type];
string const ext = formats.extension(format);
FileName const tmp_result_file(changeExtension(filename, ext));
// cloned Buffer so that it can be emitted afterwards.
if (format != backend_format) {
if (d->cloned_buffer_) {
- d->cloned_buffer_->d->errorLists[error_type] =
+ d->cloned_buffer_->d->errorLists[error_type] =
d->errorLists[error_type];
- } else
+ } else
errors(error_type);
// also to the children, in case of master-buffer-view
ListOfBuffers clist = getDescendents();
// texrow object to the cloned buffer.
// FIXME: this is not thread safe.
(*cit)->d->cloned_buffer_->d->texrow = (*cit)->d->texrow;
- (*cit)->d->cloned_buffer_->d->errorLists[error_type] =
+ (*cit)->d->cloned_buffer_->d->errorLists[error_type] =
(*cit)->d->errorLists[error_type];
} else
(*cit)->errors(error_type, true);
// FIXME: There is a possibility of concurrent access to texrow
// here from the main GUI thread that should be securized.
d->cloned_buffer_->d->texrow = d->texrow;
- string const error_type = bufferFormat();
+ string const error_type = params().bufferFormat();
d->cloned_buffer_->d->errorLists[error_type] = d->errorLists[error_type];
}
if (!success)
- return false;
+ return ExportConverterError;
if (put_in_tempdir) {
result_file = tmp_result_file.absFileName();
- return true;
+ return ExportSuccess;
}
- result_file = changeExtension(d->exportFileName().absFileName(), ext);
+ if (dest_filename.empty())
+ result_file = changeExtension(d->exportFileName().absFileName(), ext);
+ else
+ result_file = dest_filename;
// We need to copy referenced files (e. g. included graphics
// if format == "dvi") to the result dir.
vector<ExportedFile> const files =
runparams.exportdata->externalFiles(format);
- string const dest = onlyPath(result_file);
+ string const dest = runparams.export_folder.empty() ?
+ onlyPath(result_file) : runparams.export_folder;
bool use_force = use_gui ? lyxrc.export_overwrite == ALL_FILES
: force_overwrite == ALL_FILES;
CopyStatus status = use_force ? FORCE : SUCCESS;
-
+
vector<ExportedFile>::const_iterator it = files.begin();
vector<ExportedFile>::const_iterator const en = files.end();
for (; it != en && status != CANCEL; ++it) {
string const fmt = formats.getFormatFromFile(it->sourceName);
+ string fixedName = it->exportName;
+ if (!runparams.export_folder.empty()) {
+ // Relative pathnames starting with ../ will be sanitized
+ // if exporting to a different folder
+ while (fixedName.substr(0, 3) == "../")
+ fixedName = fixedName.substr(3, fixedName.length() - 3);
+ }
+ FileName fixedFileName = makeAbsPath(fixedName, dest);
+ fixedFileName.onlyPath().createPath();
status = copyFile(fmt, it->sourceName,
- makeAbsPath(it->exportName, dest),
- it->exportName, status == FORCE);
+ fixedFileName,
+ it->exportName, status == FORCE,
+ runparams.export_folder.empty());
}
if (status == CANCEL) {
message(_("Document export cancelled."));
- } else if (tmp_result_file.exists()) {
+ return ExportCancel;
+ }
+
+ if (tmp_result_file.exists()) {
// Finally copy the main file
use_force = use_gui ? lyxrc.export_overwrite != NO_FILES
: force_overwrite != NO_FILES;
status = copyFile(format, tmp_result_file,
FileName(result_file), result_file,
status == FORCE);
- message(bformat(_("Document exported as %1$s "
- "to file `%2$s'"),
- formats.prettyName(format),
- makeDisplayPath(result_file)));
+ if (status == CANCEL) {
+ message(_("Document export cancelled."));
+ return ExportCancel;
+ } else {
+ message(bformat(_("Document exported as %1$s "
+ "to file `%2$s'"),
+ formats.prettyName(format),
+ makeDisplayPath(result_file)));
+ }
} else {
// This must be a dummy converter like fax (bug 1888)
message(bformat(_("Document exported as %1$s"),
formats.prettyName(format)));
}
- return true;
+ return ExportSuccess;
}
-bool Buffer::doExport(string const & format, bool put_in_tempdir,
- bool includeall) const
+Buffer::ExportStatus Buffer::preview(string const & format) const
{
- string result_file;
- // (1) export with all included children (omit \includeonly)
- if (includeall && !doExport(format, put_in_tempdir, true, result_file))
- return false;
- // (2) export with included children only
- return doExport(format, put_in_tempdir, false, result_file);
+ bool const update_unincluded =
+ params().maintain_unincluded_children
+ && !params().getIncludedChildren().empty();
+ return preview(format, update_unincluded);
}
-
-bool Buffer::preview(string const & format, bool includeall) const
+Buffer::ExportStatus Buffer::preview(string const & format, bool includeall) const
{
MarkAsExporting exporting(this);
string result_file;
// (1) export with all included children (omit \includeonly)
- if (includeall && !doExport(format, true, true))
- return false;
- // (2) export with included children only
- if (!doExport(format, true, false, result_file))
- return false;
- return formats.view(*this, FileName(result_file), format);
-}
-
-
-bool Buffer::isExportable(string const & format) const
-{
- vector<string> backs = backends();
- for (vector<string>::const_iterator it = backs.begin();
- it != backs.end(); ++it)
- if (theConverters().isReachable(*it, format))
- return true;
- return false;
-}
-
-
-vector<Format const *> Buffer::exportableFormats(bool only_viewable) const
-{
- vector<string> const backs = backends();
- vector<Format const *> result =
- theConverters().getReachable(backs[0], only_viewable, true);
- for (vector<string>::const_iterator it = backs.begin() + 1;
- it != backs.end(); ++it) {
- vector<Format const *> r =
- theConverters().getReachable(*it, only_viewable, false);
- result.insert(result.end(), r.begin(), r.end());
+ if (includeall) {
+ ExportStatus const status = doExport(format, true, true, result_file);
+ if (status != ExportSuccess)
+ return status;
}
- return result;
-}
-
-
-vector<string> Buffer::backends() const
-{
- vector<string> v;
- v.push_back(bufferFormat());
- // FIXME: Don't hardcode format names here, but use a flag
- if (v.back() == "latex") {
- v.push_back("pdflatex");
- v.push_back("luatex");
- v.push_back("xetex");
- } else if (v.back() == "xetex")
- v.push_back("luatex");
- v.push_back("xhtml");
- v.push_back("text");
- v.push_back("lyx");
- return v;
+ // (2) export with included children only
+ ExportStatus const status = doExport(format, true, false, result_file);
+ if (status != ExportSuccess)
+ return status;
+ if (!formats.view(*this, FileName(result_file), format))
+ return PreviewError;
+ return PreviewSuccess;
}
Buffer::ReadStatus Buffer::loadEmergency()
{
FileName const emergencyFile = getEmergencyFileName();
- if (!emergencyFile.exists()
+ if (!emergencyFile.exists()
|| emergencyFile.lastModified() <= d->filename.lastModified())
return ReadFileNotFound;
docstring const file = makeDisplayPath(d->filename.absFileName(), 20);
docstring const text = bformat(_("An emergency save of the document "
"%1$s exists.\n\nRecover emergency save?"), file);
-
+
int const load_emerg = Alert::prompt(_("Load emergency save?"), text,
0, 2, _("&Recover"), _("&Load Original"), _("&Cancel"));
str += "\n\n" + bformat(_("Remove emergency file now?\n(%1$s)"),
makeDisplayPath(emergencyFile.absFileName()));
- int const del_emerg =
+ int const del_emerg =
Alert::prompt(_("Delete emergency file?"), str, 1, 1,
_("&Remove"), _("&Keep"));
if (del_emerg == 0) {
{
// Now check if autosave file is newer.
FileName const autosaveFile = getAutosaveFileName();
- if (!autosaveFile.exists()
- || autosaveFile.lastModified() <= d->filename.lastModified())
+ if (!autosaveFile.exists()
+ || autosaveFile.lastModified() <= d->filename.lastModified())
return ReadFileNotFound;
docstring const file = makeDisplayPath(d->filename.absFileName(), 20);
- docstring const text = bformat(_("The backup of the document %1$s "
+ docstring const text = bformat(_("The backup of the document %1$s "
"is newer.\n\nLoad the backup instead?"), file);
int const ret = Alert::prompt(_("Load backup?"), text, 0, 2,
_("&Load backup"), _("Load &original"), _("&Cancel"));
-
+
switch (ret)
{
case 0: {
bformat(_("A backup file is successfully loaded, "
"but the original file %1$s is marked read-only. "
"Please make sure to save the document as a "
- "different file."),
+ "different file."),
from_utf8(d->filename.absFileName())));
}
markDirty();
ReadStatus const ret_re = loadEmergency();
if (ret_re == ReadSuccess || ret_re == ReadCancel)
return ret_re;
-
+
ReadStatus const ret_ra = loadAutosave();
if (ret_ra == ReadSuccess || ret_ra == ReadCancel)
return ret_ra;
void Buffer::bufferErrors(TeXErrors const & terr, ErrorList & errorList) const
{
- TeXErrors::Errors::const_iterator cit = terr.begin();
+ TeXErrors::Errors::const_iterator it = terr.begin();
TeXErrors::Errors::const_iterator end = terr.end();
+ ListOfBuffers clist = getDescendents();
+ ListOfBuffers::const_iterator cen = clist.end();
- for (; cit != end; ++cit) {
+ for (; it != end; ++it) {
int id_start = -1;
int pos_start = -1;
- int errorRow = cit->error_in_line;
- bool found = d->texrow.getIdFromRow(errorRow, id_start,
- pos_start);
+ int errorRow = it->error_in_line;
+ Buffer const * buf = 0;
+ Impl const * p = d;
+ if (it->child_name.empty())
+ p->texrow.getIdFromRow(errorRow, id_start, pos_start);
+ else {
+ // The error occurred in a child
+ ListOfBuffers::const_iterator cit = clist.begin();
+ for (; cit != cen; ++cit) {
+ string const child_name =
+ DocFileName(changeExtension(
+ (*cit)->absFileName(), "tex")).
+ mangledFileName();
+ if (it->child_name != child_name)
+ continue;
+ (*cit)->d->texrow.getIdFromRow(errorRow,
+ id_start, pos_start);
+ if (id_start != -1) {
+ buf = d->cloned_buffer_
+ ? (*cit)->d->cloned_buffer_->d->owner_
+ : (*cit)->d->owner_;
+ p = (*cit)->d;
+ break;
+ }
+ }
+ }
int id_end = -1;
int pos_end = -1;
+ bool found;
do {
++errorRow;
- found = d->texrow.getIdFromRow(errorRow, id_end, pos_end);
+ found = p->texrow.getIdFromRow(errorRow, id_end, pos_end);
} while (found && id_start == id_end && pos_start == pos_end);
- errorList.push_back(ErrorItem(cit->error_desc,
- cit->error_text, id_start, pos_start, pos_end));
+ if (id_start != id_end) {
+ // Next registered position is outside the inset where
+ // the error occurred, so signal end-of-paragraph
+ pos_end = 0;
+ }
+
+ errorList.push_back(ErrorItem(it->error_desc,
+ it->error_text, id_start, pos_start, pos_end, buf));
}
}
void Buffer::setBuffersForInsets() const
{
- inset().setBuffer(const_cast<Buffer &>(*this));
+ inset().setBuffer(const_cast<Buffer &>(*this));
}
// Use the master text class also for child documents
Buffer const * const master = masterBuffer();
DocumentClass const & textclass = master->params().documentClass();
-
+
// do this only if we are the top-level Buffer
if (master == this)
reloadBibInfoCache();
if (master != this) {
bufToUpdate.insert(this);
master->updateBuffer(UpdateMaster, utype);
- // Do this here in case the master has no gui associated with it. Then,
+ // Do this here in case the master has no gui associated with it. Then,
// the TocModel is not updated and TocModel::toc_ is invalid (bug 5699).
if (!master->d->gui_)
structureChanged();
return;
d->bibinfo_cache_valid_ = true;
+ d->cite_labels_valid_ = true;
cbuf.tocBackend().update();
if (scope == UpdateMaster)
cbuf.structureChanged();
// Compute the item depth of the paragraph
par.itemdepth = getItemDepth(it);
- if (layout.margintype == MARGIN_MANUAL
- || layout.latextype == LATEX_BIB_ENVIRONMENT) {
+ if (layout.margintype == MARGIN_MANUAL) {
if (par.params().labelWidthString().empty())
par.params().labelWidthString(par.expandLabel(layout, bp));
+ } else if (layout.latextype == LATEX_BIB_ENVIRONMENT) {
+ // we do not need to do anything here, since the empty case is
+ // handled during export.
} else {
par.params().labelWidthString(docstring());
}
if (counters.hasCounter(from_utf8(type))) {
string const & lang = par.getParLanguage(bp)->code();
counters.step(from_utf8(type), utype);
- full_label = bformat(from_ascii("%1$s %2$s:"),
- name,
+ full_label = bformat(from_ascii("%1$s %2$s:"),
+ name,
counters.theCounter(from_utf8(type), lang));
} else
- full_label = bformat(from_ascii("%1$s #:"), name);
+ full_label = bformat(from_ascii("%1$s #:"), name);
}
- par.params().labelString(full_label);
+ par.params().labelString(full_label);
break;
}
case LABEL_MANUAL:
case LABEL_TOP_ENVIRONMENT:
case LABEL_CENTERED_TOP_ENVIRONMENT:
- case LABEL_STATIC:
+ case LABEL_STATIC:
case LABEL_BIBLIO:
par.params().labelString(par.expandLabel(layout, bp));
break;
masterBuffer()->params().documentClass().counters().
setActiveLayout(parit->layout());
}
-
+
// set the counter for this paragraph
d->setLabel(parit, utype);
WordLangTuple wl;
suggestions.clear();
word_lang = WordLangTuple();
+ bool const to_end = to.empty();
+ DocIterator const end = to_end ? doc_iterator_end(this) : to;
// OK, we start from here.
- DocIterator const end = doc_iterator_end(this);
for (; from != end; from.forwardPos()) {
// We are only interested in text so remove the math CursorSlice.
while (from.inMathed()) {
from.pos()++;
}
// If from is at the end of the document (which is possible
- // when leaving the mathed) LyX will crash later.
- if (from == end)
+ // when leaving the mathed) LyX will crash later otherwise.
+ if (from.atEnd() || (!to_end && from >= end))
break;
to = from;
from.paragraph().spellCheck();
}
+void Buffer::Impl::updateStatistics(DocIterator & from, DocIterator & to, bool skipNoOutput)
+{
+ bool inword = false;
+ word_count_ = 0;
+ char_count_ = 0;
+ blank_count_ = 0;
+
+ for (DocIterator dit = from ; dit != to && !dit.atEnd(); ) {
+ if (!dit.inTexted()) {
+ dit.forwardPos();
+ continue;
+ }
+
+ Paragraph const & par = dit.paragraph();
+ pos_type const pos = dit.pos();
+
+ // Copied and adapted from isWordSeparator() in Paragraph
+ if (pos == dit.lastpos()) {
+ inword = false;
+ } else {
+ Inset const * ins = par.getInset(pos);
+ if (ins && skipNoOutput && !ins->producesOutput()) {
+ // skip this inset
+ ++dit.top().pos();
+ // stop if end of range was skipped
+ if (!to.atEnd() && dit >= to)
+ break;
+ continue;
+ } else if (!par.isDeleted(pos)) {
+ if (par.isWordSeparator(pos))
+ inword = false;
+ else if (!inword) {
+ ++word_count_;
+ inword = true;
+ }
+ if (ins && ins->isLetter())
+ ++char_count_;
+ else if (ins && ins->isSpace())
+ ++blank_count_;
+ else {
+ char_type const c = par.getChar(pos);
+ if (isPrintableNonspace(c))
+ ++char_count_;
+ else if (isSpace(c))
+ ++blank_count_;
+ }
+ }
+ }
+ dit.forwardPos();
+ }
+}
+
+
+void Buffer::updateStatistics(DocIterator & from, DocIterator & to, bool skipNoOutput) const
+{
+ d->updateStatistics(from, to, skipNoOutput);
+}
+
+
+int Buffer::wordCount() const
+{
+ return d->wordCount();
+}
+
+
+int Buffer::charCount(bool with_blanks) const
+{
+ return d->charCount(with_blanks);
+}
+
+
Buffer::ReadStatus Buffer::reload()
{
setBusy(true);
d->filename.refresh();
docstring const disp_fn = makeDisplayPath(d->filename.absFileName());
+ // clear parent. this will get reset if need be.
+ d->setParent(0);
ReadStatus const status = loadLyXFile();
if (status == ReadSuccess) {
updateBuffer();
d->undo_.clear();
} else {
message(bformat(_("Could not reload document %1$s."), disp_fn));
- }
+ }
setBusy(false);
removePreviews();
updatePreviews();