* Licence details can be found in the file COPYING.
*
* \author Lars Gullik Bjønnes
+ * \author Stefan Schimanski
*
* Full author contact details are available in file CREDITS.
*/
#include "Counters.h"
#include "debug.h"
#include "DocIterator.h"
+#include "EmbeddedFiles.h"
#include "Encoding.h"
#include "ErrorList.h"
#include "Exporter.h"
#include "InsetIterator.h"
#include "InsetList.h"
#include "Language.h"
-#include "LaTeX.h"
#include "LaTeXFeatures.h"
+#include "LaTeX.h"
#include "Layout.h"
-#include "LyXAction.h"
#include "Lexer.h"
-#include "Text.h"
+#include "LyXAction.h"
#include "LyX.h"
#include "LyXRC.h"
#include "LyXVC.h"
#include "Messages.h"
-#include "output.h"
#include "output_docbook.h"
+#include "output.h"
#include "output_latex.h"
#include "output_plaintext.h"
-#include "Paragraph.h"
#include "paragraph_funcs.h"
+#include "Paragraph.h"
#include "ParagraphParameters.h"
#include "ParIterator.h"
+#include "PDFOptions.h"
#include "Session.h"
#include "sgml.h"
#include "TexRow.h"
-#include "TextClassList.h"
#include "TexStream.h"
+#include "TextClassList.h"
+#include "Text.h"
#include "TocBackend.h"
#include "Undo.h"
+#include "VCBackend.h"
#include "version.h"
-#include "EmbeddedFiles.h"
-#include "PDFOptions.h"
#include "insets/InsetBibitem.h"
#include "insets/InsetBibtex.h"
#include "insets/InsetInclude.h"
#include "insets/InsetText.h"
-#include "mathed/MathMacroTemplate.h"
#include "mathed/MacroTable.h"
+#include "mathed/MathMacroTemplate.h"
#include "mathed/MathSupport.h"
#include "frontends/alert.h"
using support::changeExtension;
using support::cmd_ret;
using support::createBufferTmpDir;
-using support::destroyDir;
using support::FileName;
-using support::getFormatFromContents;
using support::libFileSearch;
using support::latex_path;
using support::ltrim;
namespace {
-int const LYX_FORMAT = 295; //Uwe: htmlurl, href
+int const LYX_FORMAT = 299; //Uwe: Hyperlink types
} // namespace anon
/// our Text that should be wrapped in an InsetText
InsetText inset;
- ///
- MacroTable macros;
-
///
TocBackend toc_backend;
+ /// macro table
+ typedef std::map<unsigned int, MacroData, std::greater<int> > PositionToMacroMap;
+ typedef std::map<docstring, PositionToMacroMap> NameToPositionMacroMap;
+ NameToPositionMacroMap macros;
+
/// Container for all sort of Buffer dependant errors.
map<string, ErrorList> errorLists;
checksum_(0), wa_(0), undo_(parent)
{
inset.setAutoBreakRows(true);
- lyxvc.buffer(&parent);
+ lyxvc.setBuffer(&parent);
temppath = createBufferTmpDir();
params.filepath = onlyPath(file.absFilename());
// FIXME: And now do something if temppath == string(), because we
// Buffer.
updateLabels(*master);
- if (!temppath().empty() && !destroyDir(FileName(temppath()))) {
+ if (!temppath().empty() && !FileName(temppath()).destroyDirectory()) {
Alert::warning(_("Could not remove temporary directory"),
bformat(_("Could not remove the temporary directory %1$s"),
from_utf8(temppath())));
}
-pair<Buffer::LogType, string> Buffer::logName() const
+string Buffer::logName(LogType * type) const
{
string const filename = latexName(false);
- if (filename.empty())
- return make_pair(Buffer::latexlog, string());
+ if (filename.empty()) {
+ if (type)
+ *type = latexlog;
+ return string();
+ }
string const path = temppath();
if (bname.exists() &&
(!fname.exists() || fname.lastModified() < bname.lastModified())) {
LYXERR(Debug::FILES) << "Log name calculated as: " << bname << endl;
- return make_pair(Buffer::buildlog, bname.absFilename());
+ if (type)
+ *type = buildlog;
+ return bname.absFilename();
}
LYXERR(Debug::FILES) << "Log name calculated as: " << fname << endl;
- return make_pair(Buffer::latexlog, fname.absFilename());
+ if (type)
+ *type = latexlog;
+ return fname.absFilename();
}
{
if (pimpl_->read_only != flag) {
pimpl_->read_only = flag;
- readonly(flag);
+ setReadOnly(flag);
}
}
"\\lyxadded and \\lyxdeleted in the LaTeX preamble."));
}
}
- // read manifest after header
- embeddedFiles().readManifest(lex, errorList);
// read main text
bool const res = text().read(*this, lex, errorList);
{
FileName fname(filename);
// Check if the file is compressed.
- string format = getFormatFromContents(filename);
+ string format = filename.guessFormatFromContents();
if (format == "zip") {
// decompress to a temp directory
LYXERR(Debug::FILES) << filename << " is in zip format. Unzip to " << temppath() << endl;
}
}
// The embedded lyx file can also be compressed, for backward compatibility
- format = getFormatFromContents(fname);
- if (format == "gzip" || format == "zip" || format == "compress") {
+ format = fname.guessFormatFromContents();
+ if (format == "gzip" || format == "zip" || format == "compress")
params().compressed = true;
- }
// remove dummy empty par
paragraphs().clear();
from_utf8(filename.absFilename())));
}
- //lyxerr << "removing " << MacroTable::localMacros().size()
- // << " temporary macro entries" << endl;
- //MacroTable::localMacros().clear();
-
pimpl_->file_fully_loaded = true;
return success;
}
// make a backup if the file already exists
if (lyxrc.make_backup && fs::exists(encodedFilename)) {
backupName = FileName(absFileName() + '~');
- if (!lyxrc.backupdir_path.empty())
+ if (!lyxrc.backupdir_path.empty()) {
+ string const mangledName =
+ subst(subst(os::internal_path(
+ backupName.absFilename()), '/', '!'), ':', '!');
backupName = FileName(addName(lyxrc.backupdir_path,
- subst(os::internal_path(backupName.absFilename()), '/', '!')));
-
+ mangledName));
+ }
try {
fs::copy_file(encodedFilename, backupName.toFilesystemEncoding(), false);
madeBackup = true;
AuthorList::Authors::const_iterator a_it = params().authors().begin();
AuthorList::Authors::const_iterator a_end = params().authors().end();
for (; a_it != a_end; ++a_it)
- a_it->second.used(false);
+ a_it->second.setUsed(false);
ParIterator const end = par_iterator_end();
ParIterator it = par_iterator_begin();
params().writeFile(ofs);
ofs << "\\end_header\n";
- // write the manifest after header
- ofs << "\n\\begin_manifest\n";
- pimpl_->embedded_files.update();
- embeddedFiles().writeManifest(ofs);
- ofs << "\\end_manifest\n";
-
// write the text
ofs << "\n\\begin_body\n";
text().write(*this, ofs);
// Other flags: -wall -v0 -x
int Buffer::runChktex()
{
- busy(true);
+ setBusy(true);
// get LaTeX-Filename
FileName const path(temppath());
Alert::error(_("chktex failure"),
_("Could not run chktex successfully."));
} else if (res > 0) {
- ErrorList & errorList = pimpl_->errorLists["ChkTeX"];
- // Clear out old errors
- errorList.clear();
- // Fill-in the error list with the TeX errors
- bufferErrors(*this, terr, errorList);
+ ErrorList & errlist = pimpl_->errorLists["ChkTeX"];
+ errlist.clear();
+ bufferErrors(terr, errlist);
}
- busy(false);
+ setBusy(false);
errors("ChkTeX");
Buffer const * Buffer::masterBuffer() const
{
if (!params().parentname.empty()
- && theBufferList().exists(params().parentname)) {
+ && theBufferList().exists(params().parentname)) {
Buffer const * buf = theBufferList().getBuffer(params().parentname);
//We need to check if the parent is us...
//FIXME RECURSIVE INCLUDE
}
-MacroData const & Buffer::getMacro(docstring const & name) const
+bool Buffer::hasMacro(docstring const & name, Paragraph const & par) const
{
- return pimpl_->macros.get(name);
+ Impl::PositionToMacroMap::iterator it;
+ it = pimpl_->macros[name].upper_bound(par.macrocontextPosition());
+ if( it != pimpl_->macros[name].end() )
+ return true;
+
+ // If there is a master buffer, query that
+ const Buffer *master = masterBuffer();
+ if (master && master!=this)
+ return master->hasMacro(name);
+
+ return MacroTable::globalMacros().has(name);
}
bool Buffer::hasMacro(docstring const & name) const
{
- return pimpl_->macros.has(name);
+ if( !pimpl_->macros[name].empty() )
+ return true;
+
+ // If there is a master buffer, query that
+ const Buffer *master = masterBuffer();
+ if (master && master!=this)
+ return master->hasMacro(name);
+
+ return MacroTable::globalMacros().has(name);
+}
+
+
+MacroData const & Buffer::getMacro(docstring const & name, Paragraph const & par) const
+{
+ Impl::PositionToMacroMap::iterator it;
+ it = pimpl_->macros[name].upper_bound(par.macrocontextPosition());
+ if( it != pimpl_->macros[name].end() )
+ return it->second;
+
+ // If there is a master buffer, query that
+ const Buffer *master = masterBuffer();
+ if (master && master!=this)
+ return master->getMacro(name);
+
+ return MacroTable::globalMacros().get(name);
}
-void Buffer::insertMacro(docstring const & name, MacroData const & data)
+MacroData const & Buffer::getMacro(docstring const & name) const
{
- MacroTable::globalMacros().insert(name, data);
- pimpl_->macros.insert(name, data);
+ Impl::PositionToMacroMap::iterator it;
+ it = pimpl_->macros[name].begin();
+ if( it != pimpl_->macros[name].end() )
+ return it->second;
+
+ // If there is a master buffer, query that
+ const Buffer *master = masterBuffer();
+ if (master && master!=this)
+ return master->getMacro(name);
+
+ return MacroTable::globalMacros().get(name);
}
-void Buffer::buildMacros()
+void Buffer::updateMacros()
{
- // Start with global table.
- pimpl_->macros = MacroTable::globalMacros();
+ // start with empty table
+ pimpl_->macros = Impl::NameToPositionMacroMap();
- // Now add our own.
- ParagraphList const & pars = text().paragraphs();
+ // Iterate over buffer
+ ParagraphList & pars = text().paragraphs();
for (size_t i = 0, n = pars.size(); i != n; ++i) {
+ // set position again
+ pars[i].setMacrocontextPosition(i);
+
//lyxerr << "searching main par " << i
// << " for macro definitions" << std::endl;
InsetList const & insets = pars[i].insetList();
InsetList::const_iterator it = insets.begin();
InsetList::const_iterator end = insets.end();
for ( ; it != end; ++it) {
- //lyxerr << "found inset code " << it->inset->lyxCode() << std::endl;
- if (it->inset->lyxCode() == MATHMACRO_CODE) {
- MathMacroTemplate const & mac
- = static_cast<MathMacroTemplate const &>(*it->inset);
- insertMacro(mac.name(), mac.asMacroData());
+ if (it->inset->lyxCode() != MATHMACRO_CODE)
+ continue;
+
+ // get macro data
+ MathMacroTemplate const & macroTemplate
+ = static_cast<MathMacroTemplate const &>(*it->inset);
+
+ // valid?
+ if (macroTemplate.validMacro()) {
+ MacroData macro = macroTemplate.asMacroData();
+
+ // redefinition?
+ // call hasMacro here instead of directly querying mc to
+ // also take the master document into consideration
+ macro.setRedefinition(hasMacro(macroTemplate.name()));
+
+ // register macro (possibly overwrite the previous one of this paragraph)
+ pimpl_->macros[macroTemplate.name()][i] = macro;
}
}
}
// Check if the label 'from' appears more than once
vector<docstring> labels;
+ string paramName;
if (code == CITE_CODE) {
BiblioInfo keys;
keys.fillWithBibKeys(this);
for (; bit != bend; ++bit)
// FIXME UNICODE
labels.push_back(bit->first);
- } else
+ paramName = "key";
+ } else {
getLabelList(labels);
+ paramName = "reference";
+ }
if (std::count(labels.begin(), labels.end(), from) > 1)
return;
for (InsetIterator it = inset_iterator_begin(inset()); it; ++it) {
if (it->lyxCode() == code) {
InsetCommand & inset = static_cast<InsetCommand &>(*it);
- inset.replaceContents(to_utf8(from), to_utf8(to));
+ docstring const oldValue = inset.getParam(paramName);
+ if (oldValue == from)
+ inset.setParam(paramName, to);
}
}
}
}
-void Buffer::embeddingChanged() const
-{
- if (gui_)
- gui_->embeddingChanged();
-}
-
-
void Buffer::errors(std::string const & err) const
{
if (gui_)
}
-void Buffer::busy(bool on) const
+void Buffer::setBusy(bool on) const
{
if (gui_)
- gui_->busy(on);
+ gui_->setBusy(on);
}
-void Buffer::readonly(bool on) const
+void Buffer::setReadOnly(bool on) const
{
if (gui_)
- gui_->readonly(on);
+ gui_->setReadOnly(on);
}
if (newname.empty()) { /// No argument? Ask user through dialog
// FIXME UNICODE
- FileDialog fileDlg(_("Choose a filename to save document as"),
- LFUN_BUFFER_WRITE_AS,
- make_pair(_("Documents|#o#O"),
- from_utf8(lyxrc.document_path)),
- make_pair(_("Templates|#T#t"),
- from_utf8(lyxrc.template_path)));
+ FileDialog dlg(_("Choose a filename to save document as"),
+ LFUN_BUFFER_WRITE_AS);
+ dlg.setButton1(_("Documents|#o#O"), from_utf8(lyxrc.document_path));
+ dlg.setButton2(_("Templates|#T#t"), from_utf8(lyxrc.template_path));
if (!support::isLyXFilename(fname))
fname += ".lyx";
support::FileFilterList const filter(_("LyX Documents (*.lyx)"));
FileDialog::Result result =
- fileDlg.save(from_utf8(onlyPath(fname)),
+ dlg.save(from_utf8(onlyPath(fname)),
filter,
from_utf8(onlyFilename(fname)));
for (InsetIterator it = inset_iterator_begin(inset()); it; ++it) {
if (it->lyxCode() != INCLUDE_CODE)
continue;
- InsetInclude const & inset = static_cast<InsetInclude const &>(*it);
+ InsetCommand const & inset = static_cast<InsetCommand const &>(*it);
InsetCommandParams const & ip = inset.params();
Buffer * child = loadIfNeeded(*this, ip);
if (!child)
}
-bool Buffer::doExport(string const & format,
- bool put_in_tempdir, string & result_file)
+bool Buffer::doExport(string const & format, bool put_in_tempdir,
+ string & result_file)
{
string backend_format;
OutputParams runparams(¶ms().encoding());
}
+bool Buffer::readFileHelper(FileName const & s)
+{
+ // File information about normal file
+ if (!s.exists()) {
+ docstring const file = makeDisplayPath(s.absFilename(), 50);
+ docstring text = bformat(_("The specified document\n%1$s"
+ "\ncould not be read."), file);
+ Alert::error(_("Could not read document"), text);
+ return false;
+ }
+
+ // Check if emergency save file exists and is newer.
+ FileName const e(s.absFilename() + ".emergency");
+
+ if (e.exists() && s.exists() && e.lastModified() > s.lastModified()) {
+ docstring const file = makeDisplayPath(s.absFilename(), 20);
+ docstring const text =
+ bformat(_("An emergency save of the document "
+ "%1$s exists.\n\n"
+ "Recover emergency save?"), file);
+ switch (Alert::prompt(_("Load emergency save?"), text, 0, 2,
+ _("&Recover"), _("&Load Original"),
+ _("&Cancel")))
+ {
+ case 0:
+ // the file is not saved if we load the emergency file.
+ markDirty();
+ return readFile(e);
+ case 1:
+ break;
+ default:
+ return false;
+ }
+ }
+
+ // Now check if autosave file is newer.
+ FileName const a(onlyPath(s.absFilename()) + '#' + onlyFilename(s.absFilename()) + '#');
+
+ if (a.exists() && s.exists() && a.lastModified() > s.lastModified()) {
+ docstring const file = makeDisplayPath(s.absFilename(), 20);
+ docstring const text =
+ bformat(_("The backup of the document "
+ "%1$s is newer.\n\nLoad the "
+ "backup instead?"), file);
+ switch (Alert::prompt(_("Load backup?"), text, 0, 2,
+ _("&Load backup"), _("Load &original"),
+ _("&Cancel") ))
+ {
+ case 0:
+ // the file is not saved if we load the autosave file.
+ markDirty();
+ return readFile(a);
+ case 1:
+ // Here we delete the autosave
+ unlink(a);
+ break;
+ default:
+ return false;
+ }
+ }
+ return readFile(s);
+}
+
+
+bool Buffer::loadLyXFile(FileName const & s)
+{
+ if (s.isReadable()) {
+ if (readFileHelper(s)) {
+ lyxvc().file_found_hook(s);
+ if (!s.isWritable())
+ setReadonly(true);
+ return true;
+ }
+ } else {
+ docstring const file = makeDisplayPath(s.absFilename(), 20);
+ // Here we probably should run
+ if (LyXVC::file_not_found_hook(s)) {
+ docstring const text =
+ bformat(_("Do you want to retrieve the document"
+ " %1$s from version control?"), file);
+ int const ret = Alert::prompt(_("Retrieve from version control?"),
+ text, 0, 1, _("&Retrieve"), _("&Cancel"));
+
+ if (ret == 0) {
+ // How can we know _how_ to do the checkout?
+ // With the current VC support it has to be,
+ // a RCS file since CVS do not have special ,v files.
+ RCS::retrieve(s);
+ return loadLyXFile(s);
+ }
+ }
+ }
+ return false;
+}
+
+
+void Buffer::bufferErrors(TeXErrors const & terr, ErrorList & errorList) const
+{
+ TeXErrors::Errors::const_iterator cit = terr.begin();
+ TeXErrors::Errors::const_iterator end = terr.end();
+
+ for (; cit != end; ++cit) {
+ int id_start = -1;
+ int pos_start = -1;
+ int errorRow = cit->error_in_line;
+ bool found = texrow().getIdFromRow(errorRow, id_start,
+ pos_start);
+ int id_end = -1;
+ int pos_end = -1;
+ do {
+ ++errorRow;
+ found = 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));
+ }
+}
+
} // namespace lyx