]> git.lyx.org Git - lyx.git/blobdiff - src/Buffer.cpp
adjust
[lyx.git] / src / Buffer.cpp
index a7bca7bd371d498cb5e050d5c2ec106451f920c2..6499c89f9bc461d91e97e4575fb036d22a51f08e 100644 (file)
@@ -33,6 +33,7 @@
 #include "Language.h"
 #include "LaTeX.h"
 #include "LaTeXFeatures.h"
+#include "Layout.h"
 #include "LyXAction.h"
 #include "Lexer.h"
 #include "Text.h"
@@ -47,6 +48,7 @@
 #include "paragraph_funcs.h"
 #include "ParagraphParameters.h"
 #include "ParIterator.h"
+#include "Session.h"
 #include "sgml.h"
 #include "TexRow.h"
 #include "TextClassList.h"
@@ -55,6 +57,7 @@
 #include "Undo.h"
 #include "version.h"
 #include "EmbeddedFiles.h"
+#include "PDFOptions.h"
 
 #include "insets/InsetBibitem.h"
 #include "insets/InsetBibtex.h"
 #include "mathed/MathSupport.h"
 
 #include "frontends/alert.h"
+#include "frontends/Delegates.h"
+#include "frontends/WorkAreaManager.h"
+#include "frontends/FileDialog.h"
 
 #include "graphics/Previews.h"
 
 #include "support/types.h"
 #include "support/lyxalgo.h"
+#include "support/FileFilterList.h"
 #include "support/filetools.h"
+#include "support/Forkedcall.h"
 #include "support/fs_extras.h"
 #include "support/gzstream.h"
 #include "support/lyxlib.h"
 #include "support/textutils.h"
 #include "support/convert.h"
 
+#if !defined (HAVE_FORK)
+# define fork() -1
+#endif
+
 #include <boost/bind.hpp>
 #include <boost/filesystem/exception.hpp>
 #include <boost/filesystem/operations.hpp>
+#include <boost/shared_ptr.hpp>
 
 #include <algorithm>
 #include <iomanip>
@@ -106,7 +119,6 @@ using std::vector;
 using std::string;
 using std::time_t;
 
-
 namespace lyx {
 
 using support::addName;
@@ -142,7 +154,7 @@ namespace fs = boost::filesystem;
 
 namespace {
 
-int const LYX_FORMAT = 283;
+int const LYX_FORMAT = 295; //Uwe: htmlurl, href
 
 } // namespace anon
 
@@ -204,13 +216,17 @@ public:
        /// modified. (Used to properly enable 'File->Revert to saved', bug 4114).
        time_t timestamp_;
        unsigned long checksum_;
+
+       ///
+       frontend::WorkAreaManager * wa_;
 };
 
 
 Buffer::Impl::Impl(Buffer & parent, FileName const & file, bool readonly_)
        : lyx_clean(true), bak_clean(true), unnamed(false), read_only(readonly_),
          filename(file), file_fully_loaded(false), inset(params),
-         toc_backend(&parent), embedded_files(&parent), timestamp_(0), checksum_(0)
+         toc_backend(&parent), embedded_files(&parent), timestamp_(0),
+         checksum_(0), wa_(0)
 {
        inset.setAutoBreakRows(true);
        lyxvc.buffer(&parent);
@@ -219,11 +235,14 @@ Buffer::Impl::Impl(Buffer & parent, FileName const & file, bool readonly_)
        // FIXME: And now do something if temppath == string(), because we
        // assume from now on that temppath points to a valid temp dir.
        // See http://www.mail-archive.com/lyx-devel@lists.lyx.org/msg67406.html
+
+       if (use_gui)
+               wa_ = new frontend::WorkAreaManager;
 }
 
 
 Buffer::Buffer(string const & file, bool readonly)
-       : pimpl_(new Impl(*this, FileName(file), readonly))
+       : pimpl_(new Impl(*this, FileName(file), readonly)), gui_(0)
 {
        LYXERR(Debug::INFO) << "Buffer::Buffer()" << endl;
 }
@@ -251,7 +270,25 @@ Buffer::~Buffer()
        // Remove any previewed LaTeX snippets associated with this buffer.
        graphics::Previews::get().removeLoader(*this);
 
-       closing(this);
+       if (pimpl_->wa_) {
+               pimpl_->wa_->closeAll();
+               delete pimpl_->wa_;
+       }
+       delete pimpl_;
+}
+
+
+void Buffer::changed() const
+{
+       if (pimpl_->wa_)
+               pimpl_->wa_->redrawAll();
+}
+
+
+frontend::WorkAreaManager & Buffer::workAreaManager() const
+{
+       BOOST_ASSERT(pimpl_->wa_);
+       return *pimpl_->wa_;
 }
 
 
@@ -459,6 +496,7 @@ int Buffer::readHeader(Lexer & lex)
        params().footskip.erase();
        params().listings_params.clear();
        params().clearLayoutModules();
+       params().pdfoptions().clear();
        
        for (int i = 0; i < 4; ++i) {
                params().user_defined_bullet(i) = ITEMIZE_DEFAULTS[i];
@@ -559,7 +597,10 @@ bool Buffer::readDocument(Lexer & lex)
                                         "\\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);
        for_each(text().paragraphs().begin(),
                 text().paragraphs().end(),
@@ -661,20 +702,11 @@ bool Buffer::readFile(FileName const & filename)
                LYXERR(Debug::FILES) << filename << " is in zip format. Unzip to " << temppath() << endl;
                ::unzipToDir(filename.toFilesystemEncoding(), temppath());
                //
-               FileName manifest(addName(temppath(), "manifest.txt"));
-               FileName lyxfile(addName(temppath(), 
-                       onlyFilename(filename.toFilesystemEncoding())));
+               FileName lyxfile(addName(temppath(), "content.lyx"));
                // if both manifest.txt and file.lyx exist, this is am embedded file
-               if (fs::exists(manifest.toFilesystemEncoding()) &&
-                       fs::exists(lyxfile.toFilesystemEncoding())) {
+               if (fs::exists(lyxfile.toFilesystemEncoding())) {
                        params().embedded = true;
                        fname = lyxfile;
-                       // read manifest file
-                       ifstream is(manifest.toFilesystemEncoding().c_str());
-                       is >> pimpl_->embedded_files;
-                       is.close();
-                       LYXERR(Debug::FILES) << filename << " is a embedded file. Its manifest is:\n"
-                                       << pimpl_->embedded_files;
                }
        }
        // The embedded lyx file can also be compressed, for backward compatibility
@@ -895,8 +927,7 @@ bool Buffer::writeFile(FileName const & fname) const
        FileName content;
        if (params().embedded)
                // first write the .lyx file to the temporary directory
-               content = FileName(addName(temppath(), 
-                       onlyFilename(fname.toFilesystemEncoding())));
+               content = FileName(addName(temppath(), "content.lyx"));
        else
                content = fname;
        
@@ -916,9 +947,8 @@ bool Buffer::writeFile(FileName const & fname) const
 
        if (retval && params().embedded) {
                // write file.lyx and all the embedded files to the zip file fname
-               // if embedding is enabled, and there is any embedded file
-               pimpl_->embedded_files.update();
-               return pimpl_->embedded_files.write(fname);
+               // if embedding is enabled
+               return pimpl_->embedded_files.writeFile(fname);
        }
        return retval;
 }
@@ -957,6 +987,12 @@ bool Buffer::write(ostream & ofs) const
        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);
@@ -1449,14 +1485,14 @@ void Buffer::updateBibfilesCache()
 
        bibfilesCache_.clear();
        for (InsetIterator it = inset_iterator_begin(inset()); it; ++it) {
-               if (it->lyxCode() == Inset::BIBTEX_CODE) {
+               if (it->lyxCode() == BIBTEX_CODE) {
                        InsetBibtex const & inset =
                                static_cast<InsetBibtex const &>(*it);
                        vector<FileName> const bibfiles = inset.getFiles(*this);
                        bibfilesCache_.insert(bibfilesCache_.end(),
                                bibfiles.begin(),
                                bibfiles.end());
-               } else if (it->lyxCode() == Inset::INCLUDE_CODE) {
+               } else if (it->lyxCode() == INCLUDE_CODE) {
                        InsetInclude & inset =
                                static_cast<InsetInclude &>(*it);
                        inset.updateBibfilesCache(*this);
@@ -1658,7 +1694,7 @@ void Buffer::markClean() const
 }
 
 
-void Buffer::markBakClean()
+void Buffer::markBakClean() const
 {
        pimpl_->bak_clean = true;
 }
@@ -1742,7 +1778,10 @@ Buffer * Buffer::getMasterBuffer()
        if (!params().parentname.empty()
            && theBufferList().exists(params().parentname)) {
                Buffer * buf = theBufferList().getBuffer(params().parentname);
-               if (buf)
+               //We need to check if the parent is us...
+               //FIXME RECURSIVE INCLUDE
+               //This is not sufficient, since recursive includes could be downstream.
+               if (buf && buf != this)
                        return buf->getMasterBuffer();
        }
 
@@ -1784,7 +1823,7 @@ void Buffer::buildMacros()
                InsetList::const_iterator end = insets.end();
                for ( ; it != end; ++it) {
                        //lyxerr << "found inset code " << it->inset->lyxCode() << std::endl;
-                       if (it->inset->lyxCode() == Inset::MATHMACRO_CODE) {
+                       if (it->inset->lyxCode() == MATHMACRO_CODE) {
                                MathMacroTemplate const & mac
                                        = static_cast<MathMacroTemplate const &>(*it->inset);
                                insertMacro(mac.name(), mac.asMacroData());
@@ -1795,14 +1834,14 @@ void Buffer::buildMacros()
 
 
 void Buffer::changeRefsIfUnique(docstring const & from, docstring const & to,
-       Inset::Code code)
+       InsetCode code)
 {
        //FIXME: This does not work for child documents yet.
-       BOOST_ASSERT(code == Inset::CITE_CODE || code == Inset::REF_CODE);
+       BOOST_ASSERT(code == CITE_CODE || code == REF_CODE);
        // Check if the label 'from' appears more than once
        vector<docstring> labels;
 
-       if (code == Inset::CITE_CODE) {
+       if (code == CITE_CODE) {
                BiblioInfo keys;
                keys.fillWithBibKeys(this);
                BiblioInfo::const_iterator bit  = keys.begin();
@@ -1889,4 +1928,284 @@ ErrorList & Buffer::errorList(string const & type)
 }
 
 
+void Buffer::structureChanged() const
+{
+       if (gui_)
+               gui_->structureChanged();
+}
+
+
+void Buffer::embeddingChanged() const
+{
+       if (gui_)
+               gui_->embeddingChanged();
+}
+
+
+void Buffer::errors(std::string const & err) const
+{
+       if (gui_)
+               gui_->errors(err);
+}
+
+
+void Buffer::message(docstring const & msg) const
+{
+       if (gui_)
+               gui_->message(msg);
+}
+
+
+void Buffer::busy(bool on) const
+{
+       if (gui_)
+               gui_->busy(on);
+}
+
+
+void Buffer::readonly(bool on) const
+{
+       if (gui_)
+               gui_->readonly(on);
+}
+
+
+void Buffer::updateTitles() const
+{
+       if (gui_)
+               gui_->updateTitles();
+}
+
+
+void Buffer::resetAutosaveTimers() const
+{
+       if (gui_)
+               gui_->resetAutosaveTimers();
+}
+
+
+void Buffer::setGuiDelegate(frontend::GuiBufferDelegate * gui)
+{
+       gui_ = gui;
+}
+
+
+
+namespace {
+
+class AutoSaveBuffer : public support::ForkedProcess {
+public:
+       ///
+       AutoSaveBuffer(Buffer const & buffer, FileName const & fname)
+               : buffer_(buffer), fname_(fname) {}
+       ///
+       virtual boost::shared_ptr<ForkedProcess> clone() const
+       {
+               return boost::shared_ptr<ForkedProcess>(new AutoSaveBuffer(*this));
+       }
+       ///
+       int start()
+       {
+               command_ = to_utf8(bformat(_("Auto-saving %1$s"), 
+                                                from_utf8(fname_.absFilename())));
+               return run(DontWait);
+       }
+private:
+       ///
+       virtual int generateChild();
+       ///
+       Buffer const & buffer_;
+       FileName fname_;
+};
+
+
+#if !defined (HAVE_FORK)
+# define fork() -1
+#endif
+
+int AutoSaveBuffer::generateChild()
+{
+       // tmp_ret will be located (usually) in /tmp
+       // will that be a problem?
+       pid_t const pid = fork();
+       // If you want to debug the autosave
+       // you should set pid to -1, and comment out the fork.
+       if (pid == 0 || pid == -1) {
+               // pid = -1 signifies that lyx was unable
+               // to fork. But we will do the save
+               // anyway.
+               bool failed = false;
+
+               FileName const tmp_ret(tempName(FileName(), "lyxauto"));
+               if (!tmp_ret.empty()) {
+                       buffer_.writeFile(tmp_ret);
+                       // assume successful write of tmp_ret
+                       if (!rename(tmp_ret, fname_)) {
+                               failed = true;
+                               // most likely couldn't move between
+                               // filesystems unless write of tmp_ret
+                               // failed so remove tmp file (if it
+                               // exists)
+                               unlink(tmp_ret);
+                       }
+               } else {
+                       failed = true;
+               }
+
+               if (failed) {
+                       // failed to write/rename tmp_ret so try writing direct
+                       if (!buffer_.writeFile(fname_)) {
+                               // It is dangerous to do this in the child,
+                               // but safe in the parent, so...
+                               if (pid == -1) // emit message signal.
+                                       buffer_.message(_("Autosave failed!"));
+                       }
+               }
+               if (pid == 0) { // we are the child so...
+                       _exit(0);
+               }
+       }
+       return pid;
+}
+
+} // namespace anon
+
+
+// Perfect target for a thread...
+void Buffer::autoSave() const
+{
+       if (isBakClean() || isReadonly()) {
+               // We don't save now, but we'll try again later
+               resetAutosaveTimers();
+               return;
+       }
+
+       // emit message signal.
+       message(_("Autosaving current document..."));
+
+       // create autosave filename
+       string fname = filePath();
+       fname += '#';
+       fname += onlyFilename(fileName());
+       fname += '#';
+
+       AutoSaveBuffer autosave(*this, FileName(fname));
+       autosave.start();
+
+       markBakClean();
+       resetAutosaveTimers();
+}
+
+
+/** Write a buffer to a new file name and rename the buffer
+    according to the new file name.
+
+    This function is e.g. used by menu callbacks and
+    LFUN_BUFFER_WRITE_AS.
+
+    If 'newname' is empty (the default), the user is asked via a
+    dialog for the buffer's new name and location.
+
+    If 'newname' is non-empty and has an absolute path, that is used.
+    Otherwise the base directory of the buffer is used as the base
+    for any relative path in 'newname'.
+*/
+
+bool Buffer::writeAs(string const & newname)
+{
+       string fname = fileName();
+       string const oldname = fname;
+
+       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)));
+
+               if (!support::isLyXFilename(fname))
+                       fname += ".lyx";
+
+               support::FileFilterList const filter(_("LyX Documents (*.lyx)"));
+
+               FileDialog::Result result =
+                       fileDlg.save(from_utf8(onlyPath(fname)),
+                                    filter,
+                                    from_utf8(onlyFilename(fname)));
+
+               if (result.first == FileDialog::Later)
+                       return false;
+
+               fname = to_utf8(result.second);
+
+               if (fname.empty())
+                       return false;
+
+               // Make sure the absolute filename ends with appropriate suffix
+               fname = makeAbsPath(fname).absFilename();
+               if (!support::isLyXFilename(fname))
+                       fname += ".lyx";
+
+       } else 
+               fname = makeAbsPath(newname, onlyPath(oldname)).absFilename();
+
+       if (fs::exists(FileName(fname).toFilesystemEncoding())) {
+               docstring const file = makeDisplayPath(fname, 30);
+               docstring text = bformat(_("The document %1$s already "
+                                          "exists.\n\nDo you want to "
+                                          "overwrite that document?"), 
+                                        file);
+               int const ret = Alert::prompt(_("Overwrite document?"),
+                       text, 0, 1, _("&Overwrite"), _("&Cancel"));
+
+               if (ret == 1)
+                       return false;
+       }
+
+       // Ok, change the name of the buffer
+       setFileName(fname);
+       markDirty();
+       bool unnamed = isUnnamed();
+       setUnnamed(false);
+       saveCheckSum(fname);
+
+       if (!menuWrite()) {
+               setFileName(oldname);
+               setUnnamed(unnamed);
+               saveCheckSum(oldname);
+               return false;
+       }
+
+       removeAutosaveFile(oldname);
+       return true;
+}
+
+
+bool Buffer::menuWrite()
+{
+       if (save()) {
+               LyX::ref().session().lastFiles().add(FileName(fileName()));
+               return true;
+       }
+
+       // FIXME: we don't tell the user *WHY* the save failed !!
+
+       docstring const file = makeDisplayPath(fileName(), 30);
+
+       docstring text = bformat(_("The document %1$s could not be saved.\n\n"
+                                  "Do you want to rename the document and "
+                                  "try again?"), file);
+       int const ret = Alert::prompt(_("Rename and save?"),
+               text, 0, 1, _("&Rename"), _("&Cancel"));
+
+       if (ret != 0)
+               return false;
+
+       return writeAs();
+}
+
+
 } // namespace lyx