]> git.lyx.org Git - lyx.git/blobdiff - src/frontends/qt4/GuiView.cpp
New file is placed in current directory by default.
[lyx.git] / src / frontends / qt4 / GuiView.cpp
index 950631194fa08da8fcb9a5acd5dc7ee303a5ad9f..1ecc27b612676ed0821ec0e85ea94c112afafe88 100644 (file)
@@ -14,8 +14,9 @@
 #include <config.h>
 
 #include "GuiView.h"
-#include "Dialog.h"
 
+#include "Dialog.h"
+#include "frontends/FileDialog.h"
 #include "GuiApplication.h"
 #include "GuiWorkArea.h"
 #include "GuiKeySymbol.h"
 
 #include "qt_helpers.h"
 
+#include "frontends/alert.h"
+
 #include "buffer_funcs.h"
 #include "Buffer.h"
 #include "BufferList.h"
 #include "BufferParams.h"
 #include "BufferView.h"
 #include "Cursor.h"
-#include "debug.h"
+#include "support/debug.h"
 #include "ErrorList.h"
 #include "FuncRequest.h"
-#include "gettext.h"
+#include "support/gettext.h"
 #include "Intl.h"
 #include "Layout.h"
 #include "Lexer.h"
 #include "ToolbarBackend.h"
 #include "version.h"
 
+#include "support/FileFilterList.h"
 #include "support/FileName.h"
+#include "support/filetools.h"
 #include "support/lstrings.h"
 #include "support/os.h"
+#include "support/Package.h"
 #include "support/Timeout.h"
 
 #include <QAction>
@@ -96,8 +102,14 @@ extern bool quitting;
 
 namespace frontend {
 
+using support::addPath;
 using support::bformat;
+using support::FileFilterList;
 using support::FileName;
+using support::makeAbsPath;
+using support::makeDisplayPath;
+using support::package;
+using support::removeAutosaveFile;
 using support::trim;
 
 namespace {
@@ -162,8 +174,8 @@ struct GuiView::GuiViewPrivate
        ~GuiViewPrivate()
        {
                delete splitter_;
-               delete stack_widget_;
                delete bg_widget_;
+               delete stack_widget_;
                delete menubar_;
                delete toolbars_;
        }
@@ -386,7 +398,7 @@ void GuiView::closeEvent(QCloseEvent * close_event)
        // we may have been called through the close window button
        // which bypasses the LFUN machinery.
        if (!d.quitting_by_menu_ && guiApp->viewCount() == 1) {
-               if (!theBufferList().quitWriteAll()) {
+               if (!quitWriteAll()) {
                        close_event->ignore();
                        return;
                }
@@ -705,15 +717,14 @@ GuiWorkArea const * GuiView::currentWorkArea() const
 }
 
 
-void GuiView::setCurrentWorkArea(GuiWorkArea * work_area)
+void GuiView::setCurrentWorkArea(GuiWorkArea * wa)
 {
-       BOOST_ASSERT(work_area);
+       BOOST_ASSERT(wa);
 
        // Changing work area can result from opening a file so
        // update the toc in any case.
        updateToc();
 
-       GuiWorkArea * wa = static_cast<GuiWorkArea *>(work_area);
        d.current_work_area_ = wa;
        for (int i = 0; i != d.splitter_->count(); ++i) {
                if (d.tabWorkArea(i)->setCurrentWorkArea(wa))
@@ -722,24 +733,19 @@ void GuiView::setCurrentWorkArea(GuiWorkArea * work_area)
 }
 
 
-void GuiView::removeWorkArea(GuiWorkArea * work_area)
+void GuiView::removeWorkArea(GuiWorkArea * wa)
 {
-       BOOST_ASSERT(work_area);
-       GuiWorkArea * gwa = static_cast<GuiWorkArea *>(work_area);
-       if (gwa == d.current_work_area_) {
+       BOOST_ASSERT(wa);
+       if (wa == d.current_work_area_) {
                disconnectBuffer();
                disconnectBufferView();
                hideBufferDependent();
                d.current_work_area_ = 0;
        }
 
-       // removing a work area often results from closing a file so
-       // update the toc in any case.
-       updateToc();
-
        for (int i = 0; i != d.splitter_->count(); ++i) {
                TabWorkArea * twa = d.tabWorkArea(i);
-               if (!twa->removeWorkArea(gwa))
+               if (!twa->removeWorkArea(wa))
                        // Not found in this tab group.
                        continue;
 
@@ -936,6 +942,14 @@ FuncStatus GuiView::getStatus(FuncRequest const & cmd)
                buf = 0;
 
        switch(cmd.action) {
+       case LFUN_BUFFER_WRITE:
+               enable = buf && (buf->isUnnamed() || !buf->isClean());
+               break;
+
+       case LFUN_BUFFER_WRITE_AS:
+               enable = buf;
+               break;
+
        case LFUN_TOOLBAR_TOGGLE:
                flag.setOnOff(d.toolbars_->visible(cmd.getArg(0)));
                break;
@@ -962,7 +976,7 @@ FuncStatus GuiView::getStatus(FuncRequest const & cmd)
                        }
                }
                else if (name == "latexlog")
-                       enable = FileName(buf->logName()).isFileReadable();
+                       enable = FileName(buf->logName()).isReadableFile();
                else if (name == "spellchecker")
 #if defined (USE_ASPELL) || defined (USE_ISPELL) || defined (USE_PSPELL)
                        enable = !buf->isReadonly();
@@ -1018,16 +1032,332 @@ FuncStatus GuiView::getStatus(FuncRequest const & cmd)
 }
 
 
-void GuiView::dispatch(FuncRequest const & cmd)
+static FileName selectTemplateFile()
+{
+       FileDialog dlg(_("Select template file"));
+       dlg.setButton1(_("Documents|#o#O"), from_utf8(lyxrc.document_path));
+       dlg.setButton1(_("Templates|#T#t"), from_utf8(lyxrc.template_path));
+
+       FileDialog::Result result =
+               dlg.open(from_utf8(lyxrc.template_path),
+                            FileFilterList(_("LyX Documents (*.lyx)")),
+                            docstring());
+
+       if (result.first == FileDialog::Later)
+               return FileName();
+       if (result.second.empty())
+               return FileName();
+       return FileName(to_utf8(result.second));
+}
+
+
+void GuiView::newDocument(string const & filename, bool from_template)
+{
+       FileName initpath(lyxrc.document_path);
+       Buffer * buf = buffer();
+       if (buf) {
+               FileName const trypath(buf->filePath());
+               // If directory is writeable, use this as default.
+               if (trypath.isDirWritable())
+                       initpath = trypath;
+       }
+
+       string templatefile = from_template ?
+               selectTemplateFile().absFilename() : string();
+       Buffer * b;
+       if (filename.empty())
+               b = newUnnamedFile(templatefile, initpath);
+       else
+               b = newFile(filename, templatefile, true);
+
+       if (b)
+               setBuffer(b);
+}
+
+
+void GuiView::insertLyXFile(docstring const & fname)
+{
+       BufferView * bv = view();
+       if (!bv)
+               return;
+
+       // FIXME UNICODE
+       FileName filename(to_utf8(fname));
+       
+       if (!filename.empty()) {
+               bv->insertLyXFile(filename);
+               return;
+       }
+
+       // Launch a file browser
+       // FIXME UNICODE
+       string initpath = lyxrc.document_path;
+       string const trypath = bv->buffer().filePath();
+       // If directory is writeable, use this as default.
+       if (FileName(trypath).isDirWritable())
+               initpath = trypath;
+
+       // FIXME UNICODE
+       FileDialog dlg(_("Select LyX document to insert"), LFUN_FILE_INSERT);
+       dlg.setButton1(_("Documents|#o#O"), from_utf8(lyxrc.document_path));
+       dlg.setButton2(_("Examples|#E#e"),
+               from_utf8(addPath(package().system_support().absFilename(),
+               "examples")));
+
+       FileDialog::Result result =
+               dlg.open(from_utf8(initpath),
+                            FileFilterList(_("LyX Documents (*.lyx)")),
+                            docstring());
+
+       if (result.first == FileDialog::Later)
+               return;
+
+       // FIXME UNICODE
+       filename.set(to_utf8(result.second));
+
+       // check selected filename
+       if (filename.empty()) {
+               // emit message signal.
+               message(_("Canceled."));
+               return;
+       }
+
+       bv->insertLyXFile(filename);
+}
+
+
+void GuiView::insertPlaintextFile(docstring const & fname,
+       bool asParagraph)
+{
+       BufferView * bv = view();
+       if (!bv)
+               return;
+
+       // FIXME UNICODE
+       FileName filename(to_utf8(fname));
+       
+       if (!filename.empty()) {
+               bv->insertPlaintextFile(filename, asParagraph);
+               return;
+       }
+
+       FileDialog dlg(_("Select file to insert"), (asParagraph ?
+               LFUN_FILE_INSERT_PLAINTEXT_PARA : LFUN_FILE_INSERT_PLAINTEXT));
+
+       FileDialog::Result result = dlg.open(from_utf8(bv->buffer().filePath()),
+               FileFilterList(), docstring());
+
+       if (result.first == FileDialog::Later)
+               return;
+
+       // FIXME UNICODE
+       filename.set(to_utf8(result.second));
+
+       // check selected filename
+       if (filename.empty()) {
+               // emit message signal.
+               message(_("Canceled."));
+               return;
+       }
+
+       bv->insertPlaintextFile(filename, asParagraph);
+}
+
+
+bool GuiView::renameBuffer(Buffer & b, docstring const & newname)
+{
+       FileName fname = b.fileName();
+       FileName const oldname = fname;
+
+       if (!newname.empty()) {
+               // FIXME UNICODE
+               fname = makeAbsPath(to_utf8(newname), oldname.onlyPath().absFilename());
+       } else {
+               // Switch to this Buffer.
+               setBuffer(&b);
+
+               /// No argument? Ask user through dialog.
+               // FIXME UNICODE
+               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.absFilename()))
+                       fname.changeExtension(".lyx");
+
+               support::FileFilterList const filter(_("LyX Documents (*.lyx)"));
+
+               FileDialog::Result result =
+                       dlg.save(from_utf8(fname.onlyPath().absFilename()),
+                                    filter,
+                                    from_utf8(fname.onlyFileName()));
+
+               if (result.first == FileDialog::Later)
+                       return false;
+
+               fname.set(to_utf8(result.second));
+
+               if (fname.empty())
+                       return false;
+
+               if (!support::isLyXFilename(fname.absFilename()))
+                       fname.changeExtension(".lyx");
+       }
+
+       if (FileName(fname).exists()) {
+               docstring const file = makeDisplayPath(fname.absFilename(), 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, 2, _("&Overwrite"), _("&Rename"), _("&Cancel"));
+               switch (ret) {
+               case 0: break;
+               case 1: return renameBuffer(b, docstring());
+               case 2: return false;
+               }
+       }
+
+       // Ok, change the name of the buffer
+       b.setFileName(fname.absFilename());
+       b.markDirty();
+       bool unnamed = b.isUnnamed();
+       b.setUnnamed(false);
+       b.saveCheckSum(fname);
+
+       if (!saveBuffer(b)) {
+               b.setFileName(oldname.absFilename());
+               b.setUnnamed(unnamed);
+               b.saveCheckSum(oldname);
+               return false;
+       }
+
+       return true;
+}
+
+
+bool GuiView::saveBuffer(Buffer & b)
+{
+       if (b.isUnnamed())
+               return renameBuffer(b, docstring());
+
+       if (b.save()) {
+               LyX::ref().session().lastFiles().add(b.fileName());
+               return true;
+       }
+
+       // Switch to this Buffer.
+       setBuffer(&b);
+
+       // FIXME: we don't tell the user *WHY* the save failed !!
+       docstring const file = makeDisplayPath(b.absFileName(), 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, 2, _("&Rename"), _("&Retry"), _("&Cancel"));
+       switch (ret) {
+       case 0:
+               if (!renameBuffer(b, docstring()))
+                       return false;
+               break;
+       case 1:
+               return false;
+       case 2:
+               break;
+       }
+
+       return saveBuffer(b);
+}
+
+
+bool GuiView::closeBuffer()
+{
+       Buffer * buf = buffer();
+       return buf && closeBuffer(*buf);
+}
+
+
+bool GuiView::closeBuffer(Buffer & buf)
+{
+       if (buf.isClean() || buf.paragraphs().empty()) {
+               theBufferList().release(&buf);
+               return true;
+       }
+       // Switch to this Buffer.
+       setBuffer(&buf);
+
+       docstring file;
+       // FIXME: Unicode?
+       if (buf.isUnnamed())
+               file = from_utf8(buf.fileName().onlyFileName());
+       else
+               file = buf.fileName().displayName(30);
+
+       docstring const text = bformat(_("The document %1$s has unsaved changes."
+               "\n\nDo you want to save the document or discard the changes?"), file);
+       int const ret = Alert::prompt(_("Save changed document?"),
+               text, 0, 2, _("&Save"), _("&Discard"), _("&Cancel"));
+
+       switch (ret) {
+       case 0:
+               if (!saveBuffer(buf))
+                       return false;
+               break;
+       case 1:
+               // if we crash after this we could
+               // have no autosave file but I guess
+               // this is really improbable (Jug)
+               removeAutosaveFile(buf.absFileName());
+               break;
+       case 2:
+               return false;
+       }
+
+       // save file names to .lyx/session
+       // if master/slave are both open, do not save slave since it
+       // will be automatically loaded when the master is loaded
+       if (buf.masterBuffer() == &buf)
+               LyX::ref().session().lastOpened().add(buf.fileName());
+
+       theBufferList().release(&buf);
+       return true;
+}
+
+
+bool GuiView::quitWriteAll()
 {
-       // By default we won't need any new update.
-       Update::flags update_flags = Update::None;
+       while (!theBufferList().empty()) {
+               Buffer * b = theBufferList().first();
+               if (!closeBuffer(*b))
+                       return false;
+       }
+       return true;
+}
+
+
+bool GuiView::dispatch(FuncRequest const & cmd)
+{
+       BufferView * bv = view();       
+       // By default we won't need any update.
+       if (bv)
+               bv->cursor().updateFlags(Update::None);
 
        switch(cmd.action) {
                case LFUN_BUFFER_SWITCH:
                        setBuffer(theBufferList().getBuffer(to_utf8(cmd.argument())));
                        break;
 
+               case LFUN_BUFFER_NEXT:
+                       setBuffer(theBufferList().next(buffer()));
+                       break;
+
+               case LFUN_BUFFER_PREVIOUS:
+                       setBuffer(theBufferList().previous(buffer()));
+                       break;
+
                case LFUN_COMMAND_EXECUTE: {
                        bool const show_it = cmd.argument() != "off";
                        d.toolbars_->showCommandBuffer(show_it);
@@ -1042,6 +1372,45 @@ void GuiView::dispatch(FuncRequest const & cmd)
                        d.menubar_->openByName(toqstr(cmd.argument()));
                        break;
 
+               case LFUN_FILE_INSERT:
+                       insertLyXFile(cmd.argument());
+                       break;
+               case LFUN_FILE_INSERT_PLAINTEXT_PARA:
+                       insertPlaintextFile(cmd.argument(), true);
+                       break;
+
+               case LFUN_FILE_INSERT_PLAINTEXT:
+                       insertPlaintextFile(cmd.argument(), false);
+                       break;
+
+               case LFUN_BUFFER_WRITE:
+                       if (bv)
+                               saveBuffer(bv->buffer());
+                       break;
+
+               case LFUN_BUFFER_WRITE_AS:
+                       if (bv)
+                               renameBuffer(bv->buffer(), cmd.argument());
+                       break;
+
+               case LFUN_BUFFER_WRITE_ALL: {
+                       Buffer * first = theBufferList().first();
+                       if (!first)
+                               break;
+                       message(_("Saving all documents..."));
+                       // We cannot use a for loop as the buffer list cycles.
+                       Buffer * b = first;
+                       do {
+                               if (b->isClean())
+                                       continue;
+                               saveBuffer(*b);
+                               LYXERR(Debug::ACTION, "Saved " << b->absFileName());
+                               b = theBufferList().next(b);
+                       } while (b != first); 
+                       message(_("All documents saved."));
+                       break;
+               }
+
                case LFUN_TOOLBAR_TOGGLE: {
                        string const name = cmd.getArg(0);
                        bool const allowauto = cmd.getArg(1) == "allowauto";
@@ -1143,9 +1512,6 @@ void GuiView::dispatch(FuncRequest const & cmd)
                        if (inset) {
                                FuncRequest fr(LFUN_INSET_MODIFY, cmd.argument());
                                inset->dispatch(view()->cursor(), fr);
-                               // ideally, the update flag should be set by the insets,
-                               // but this is not possible currently
-                               update_flags = Update::Force | Update::FitCursor;
                        } else {
                                FuncRequest fr(LFUN_INSET_INSERT, cmd.argument());
                                lyx::dispatch(fr);
@@ -1154,12 +1520,10 @@ void GuiView::dispatch(FuncRequest const & cmd)
                }
 
                default:
-                       theLyXFunc().setLyXView(this);
-                       lyx::dispatch(cmd);
+                       return false;
        }
 
-       if (view())
-               view()->cursor().updateFlags(update_flags);
+       return true;
 }
 
 
@@ -1367,7 +1731,7 @@ void GuiView::updateBufferDependent(bool switched) const
                        // A bit clunky, but the dialog will request
                        // that the kernel provides it with the necessary
                        // data.
-                       dialog->updateDialog(dialog->name());
+                       dialog->updateDialog();
                }
        }
 }