]> git.lyx.org Git - lyx.git/blobdiff - src/frontends/qt4/GuiView.cpp
On Mac, moving down a paragraph should place the cursor at the end of the current...
[lyx.git] / src / frontends / qt4 / GuiView.cpp
index 8ad76d9ba7c3b360ee4f2ff349db2769f83b7a1a..993e830036c5fb3a3e2a7c1d346fd9736e07262a 100644 (file)
@@ -34,6 +34,7 @@
 #include "qt_helpers.h"
 
 #include "frontends/alert.h"
+#include "frontends/KeySymbol.h"
 
 #include "buffer_funcs.h"
 #include "Buffer.h"
@@ -58,6 +59,7 @@
 #include "LyXVC.h"
 #include "Paragraph.h"
 #include "SpellChecker.h"
+#include "Session.h"
 #include "TexRow.h"
 #include "TextClass.h"
 #include "Text.h"
@@ -76,7 +78,7 @@
 #include "support/lstrings.h"
 #include "support/os.h"
 #include "support/Package.h"
-#include "support/Path.h"
+#include "support/PathChanger.h"
 #include "support/Systemcall.h"
 #include "support/Timeout.h"
 #include "support/ProgressInterface.h"
 #include <QDesktopWidget>
 #include <QDragEnterEvent>
 #include <QDropEvent>
+#include <QFuture>
+#include <QFutureWatcher>
 #include <QLabel>
 #include <QList>
 #include <QMenu>
 #include <QMenuBar>
+#include <QMimeData>
 #include <QMovie>
 #include <QPainter>
 #include <QPixmap>
 #include <QPixmapCache>
 #include <QPoint>
 #include <QPushButton>
+#include <QScrollBar>
 #include <QSettings>
 #include <QShowEvent>
 #include <QSplitter>
 #include <QStackedWidget>
 #include <QStatusBar>
+#include <QtConcurrentRun>
 #include <QTime>
 #include <QTimer>
 #include <QToolBar>
 #include <QUrl>
-#include <QScrollBar>
 
 
 
 #define EXPORT_in_THREAD 1
 
-#include <QFuture>
-#include <QFutureWatcher>
-#include <QtConcurrentRun>
 
 #include "support/bind.h"
 
@@ -457,6 +460,7 @@ GuiView::GuiView(int id)
 #endif
 
 #endif
+       resetWindowTitleAndIconText();
 
        // use tabbed dock area for multiple docks
        // (such as "source" and "messages")
@@ -672,6 +676,16 @@ bool GuiView::restoreLayout()
 
        if (!restoreState(settings.value("layout").toByteArray(), 0))
                initToolbars();
+       
+       // init the toolbars that have not been restored
+       Toolbars::Infos::iterator cit = guiApp->toolbars().begin();
+       Toolbars::Infos::iterator end = guiApp->toolbars().end();
+       for (; cit != end; ++cit) {
+               GuiToolbar * tb = toolbar(cit->name);
+               if (tb && !tb->isRestored())
+                       initToolbar(cit->name);
+       }
+
        updateDialogs();
        return true;
 }
@@ -714,42 +728,47 @@ void GuiView::initToolbars()
        // extracts the toolbars from the backend
        Toolbars::Infos::iterator cit = guiApp->toolbars().begin();
        Toolbars::Infos::iterator end = guiApp->toolbars().end();
-       for (; cit != end; ++cit) {
-               GuiToolbar * tb = toolbar(cit->name);
-               if (!tb)
-                       continue;
-               int const visibility = guiApp->toolbars().defaultVisibility(cit->name);
-               bool newline = !(visibility & Toolbars::SAMEROW);
-               tb->setVisible(false);
-               tb->setVisibility(visibility);
-
-               if (visibility & Toolbars::TOP) {
-                       if (newline)
-                               addToolBarBreak(Qt::TopToolBarArea);
-                       addToolBar(Qt::TopToolBarArea, tb);
-               }
+       for (; cit != end; ++cit)
+               initToolbar(cit->name);
+}
 
-               if (visibility & Toolbars::BOTTOM) {
-                       if (newline)
-                               addToolBarBreak(Qt::BottomToolBarArea);
-                       addToolBar(Qt::BottomToolBarArea, tb);
-               }
 
-               if (visibility & Toolbars::LEFT) {
-                       if (newline)
-                               addToolBarBreak(Qt::LeftToolBarArea);
-                       addToolBar(Qt::LeftToolBarArea, tb);
-               }
+void GuiView::initToolbar(string const & name)
+{
+       GuiToolbar * tb = toolbar(name);
+       if (!tb)
+               return;
+       int const visibility = guiApp->toolbars().defaultVisibility(name);
+       bool newline = !(visibility & Toolbars::SAMEROW);
+       tb->setVisible(false);
+       tb->setVisibility(visibility);
+
+       if (visibility & Toolbars::TOP) {
+               if (newline)
+                       addToolBarBreak(Qt::TopToolBarArea);
+               addToolBar(Qt::TopToolBarArea, tb);
+       }
 
-               if (visibility & Toolbars::RIGHT) {
-                       if (newline)
-                               addToolBarBreak(Qt::RightToolBarArea);
-                       addToolBar(Qt::RightToolBarArea, tb);
-               }
+       if (visibility & Toolbars::BOTTOM) {
+               if (newline)
+                       addToolBarBreak(Qt::BottomToolBarArea);
+               addToolBar(Qt::BottomToolBarArea, tb);
+       }
+
+       if (visibility & Toolbars::LEFT) {
+               if (newline)
+                       addToolBarBreak(Qt::LeftToolBarArea);
+               addToolBar(Qt::LeftToolBarArea, tb);
+       }
 
-               if (visibility & Toolbars::ON)
-                       tb->setVisible(true);
+       if (visibility & Toolbars::RIGHT) {
+               if (newline)
+                       addToolBarBreak(Qt::RightToolBarArea);
+               addToolBar(Qt::RightToolBarArea, tb);
        }
+
+       if (visibility & Toolbars::ON)
+               tb->setVisible(true);
 }
 
 
@@ -1062,6 +1081,8 @@ void GuiView::updateStatusBar()
 
 void GuiView::showMessage()
 {
+       if (busy_)
+               return;
        QString msg = toqstr(theGuiApp()->viewStatusMessage());
        if (msg.isEmpty()) {
                BufferView const * bv = currentBufferView();
@@ -1329,7 +1350,7 @@ void GuiView::removeWorkArea(GuiWorkArea * wa)
 
        // It is not a tabbed work area (i.e., the search work area), so it
        // should be deleted by other means.
-       LASSERT(found_twa, /* */);
+       LASSERT(found_twa, return);
 
        if (d.current_work_area_ == 0) {
                if (d.splitter_->count() != 0) {
@@ -1545,14 +1566,10 @@ void GuiView::autoSave()
                return;
        }
 
-#if (QT_VERSION >= 0x040400)
        GuiViewPrivate::busyBuffers.insert(buffer);
        QFuture<docstring> f = QtConcurrent::run(GuiViewPrivate::autosaveAndDestroy,
                buffer, buffer->cloneBufferOnly());
        d.autosave_watcher_.setFuture(f);
-#else
-       buffer->autoSave();
-#endif
        resetAutosaveTimers();
 }
 
@@ -1806,6 +1823,12 @@ bool GuiView::getStatus(FuncRequest const & cmd, FuncStatus & flag)
        case LFUN_VC_REGISTER:
                enable = doc_buffer && !doc_buffer->lyxvc().inUse();
                break;
+       case LFUN_VC_RENAME:
+               enable = doc_buffer && doc_buffer->lyxvc().renameEnabled();
+               break;
+       case LFUN_VC_COPY:
+               enable = doc_buffer && doc_buffer->lyxvc().copyEnabled();
+               break;
        case LFUN_VC_CHECK_IN:
                enable = doc_buffer && doc_buffer->lyxvc().checkInEnabled();
                break;
@@ -1848,6 +1871,10 @@ bool GuiView::getStatus(FuncRequest const & cmd, FuncStatus & flag)
                enable = documentBufferView() && documentBufferView()->cursor().inTexted();
                break;
 
+       case LFUN_SPELLING_CONTINUOUSLY:
+               flag.setOnOff(lyxrc.spellcheck_continuously);
+               break;
+
        default:
                return false;
        }
@@ -1960,7 +1987,8 @@ void GuiView::openDocument(string const & fname)
 
        // if the file doesn't exist and isn't already open (bug 6645),
        // let the user create one
-       if (!fullname.exists() && !theBufferList().exists(fullname)) {
+       if (!fullname.exists() && !theBufferList().exists(fullname) &&
+           !LyXVC::file_not_found_hook(fullname)) {
                // the user specifically chose this name. Believe him.
                Buffer * const b = newFile(filename, string(), true);
                if (b)
@@ -2210,7 +2238,7 @@ void GuiView::insertLyXFile(docstring const & fname)
 }
 
 
-bool GuiView::renameBuffer(Buffer & b, docstring const & newname)
+bool GuiView::renameBuffer(Buffer & b, docstring const & newname, RenameKind kind)
 {
        FileName fname = b.fileName();
        FileName const oldname = fname;
@@ -2256,7 +2284,7 @@ bool GuiView::renameBuffer(Buffer & b, docstring const & newname)
        // trying to overwrite ourselves, which is fine.)
        if (theBufferList().exists(fname) && fname != oldname
                  && theBufferList().getBuffer(fname) != &b) {
-               docstring const text = 
+               docstring const text =
                        bformat(_("The file\n%1$s\nis already open in your current session.\n"
                            "Please close it before attempting to overwrite it.\n"
                            "Do you want to choose a new filename?"),
@@ -2264,27 +2292,74 @@ bool GuiView::renameBuffer(Buffer & b, docstring const & newname)
                int const ret = Alert::prompt(_("Chosen File Already Open"),
                        text, 0, 1, _("&Rename"), _("&Cancel"));
                switch (ret) {
-               case 0: return renameBuffer(b, docstring());
+               case 0: return renameBuffer(b, docstring(), kind);
                case 1: return false;
                }
                //return false;
        }
-       
-       if (FileName(fname).exists()) {
+
+       bool const existsLocal = fname.exists();
+       bool const existsInVC = LyXVC::fileInVC(fname);
+       if (existsLocal || existsInVC) {
                docstring const file = makeDisplayPath(fname.absFileName(), 30);
-               docstring const 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;
+               if (kind != LV_WRITE_AS && existsInVC) {
+                       // renaming to a name that is already in VC
+                       // would not work
+                       docstring text = bformat(_("The document %1$s "
+                                       "is already registered.\n\n"
+                                       "Do you want to choose a new name?"),
+                               file);
+                       docstring const title = (kind == LV_VC_RENAME) ?
+                               _("Rename document?") : _("Copy document?");
+                       docstring const button = (kind == LV_VC_RENAME) ?
+                               _("&Rename") : _("&Copy");
+                       int const ret = Alert::prompt(title, text, 0, 1,
+                               button, _("&Cancel"));
+                       switch (ret) {
+                       case 0: return renameBuffer(b, docstring(), kind);
+                       case 1: return false;
+                       }
+               }
+
+               if (existsLocal) {
+                       docstring text = bformat(_("The document %1$s "
+                                       "already exists.\n\n"
+                                       "Do 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(), kind);
+                       case 2: return false;
+                       }
                }
        }
 
+       switch (kind) {
+       case LV_VC_RENAME: {
+               string msg = b.lyxvc().rename(fname);
+               if (msg.empty())
+                       return false;
+               message(from_utf8(msg));
+               break;
+       }
+       case LV_VC_COPY: {
+               string msg = b.lyxvc().copy(fname);
+               if (msg.empty())
+                       return false;
+               message(from_utf8(msg));
+               break;
+       }
+       case LV_WRITE_AS:
+               break;
+       }
+       // LyXVC created the file already in case of LV_VC_RENAME or
+       // LV_VC_COPY, but call saveBuffer() nevertheless to get
+       // relative paths of included stuff right if we moved e.g. from
+       // /a/b.lyx to /a/c/b.lyx.
+
        bool const saved = saveBuffer(b, fname);
        if (saved)
                b.reload(false);
@@ -2330,7 +2405,7 @@ bool GuiView::exportBufferAs(Buffer & b)
 
        string s = fromqstr(filter);
        size_t pos = s.find(" (*.");
-       LASSERT(pos != string::npos, /**/);
+       LATTEST(pos != string::npos);
        string fmt_prettyname = s.substr(0, pos);
        string fmt_name;
        fname.set(fromqstr(result.second));
@@ -2367,7 +2442,8 @@ bool GuiView::exportBufferAs(Buffer & b)
 }
 
 
-bool GuiView::saveBuffer(Buffer & b) {
+bool GuiView::saveBuffer(Buffer & b)
+{
        return saveBuffer(b, FileName());
 }
 
@@ -2378,14 +2454,9 @@ bool GuiView::saveBuffer(Buffer & b, FileName const & fn)
                return true;
 
        if (fn.empty() && b.isUnnamed())
-                       return renameBuffer(b, docstring());
+               return renameBuffer(b, docstring());
 
-       bool success;
-       if (fn.empty())
-               success = b.save();
-       else
-               success = b.saveAs(fn);
-       
+       bool const success = (fn.empty() ? b.save() : b.saveAs(fn));
        if (success) {
                theSession().lastFiles().add(b.fileName());
                return true;
@@ -2412,7 +2483,7 @@ bool GuiView::saveBuffer(Buffer & b, FileName const & fn)
                return false;
        }
 
-       return saveBuffer(b);
+       return saveBuffer(b, fn);
 }
 
 
@@ -2809,12 +2880,48 @@ void GuiView::dispatchVC(FuncRequest const & cmd, DispatchResult & dr)
                }
                break;
 
+       case LFUN_VC_RENAME:
+       case LFUN_VC_COPY: {
+               if (!buffer || !ensureBufferClean(buffer))
+                       break;
+               if (buffer->lyxvc().inUse() && !buffer->isReadonly()) {
+                       if (buffer->lyxvc().isCheckInWithConfirmation()) {
+                               // Some changes are not yet committed.
+                               // We test here and not in getStatus(), since
+                               // this test is expensive.
+                               string log;
+                               LyXVC::CommandResult ret =
+                                       buffer->lyxvc().checkIn(log);
+                               dr.setMessage(log);
+                               if (ret == LyXVC::ErrorCommand ||
+                                   ret == LyXVC::VCSuccess)
+                                       reloadBuffer(*buffer);
+                               if (buffer->lyxvc().isCheckInWithConfirmation()) {
+                                       frontend::Alert::error(
+                                               _("Revision control error."),
+                                               _("Document could not be checked in."));
+                                       break;
+                               }
+                       }
+                       RenameKind const kind = (cmd.action() == LFUN_VC_RENAME) ?
+                               LV_VC_RENAME : LV_VC_COPY;
+                       renameBuffer(*buffer, cmd.argument(), kind);
+               }
+               break;
+       }
+
        case LFUN_VC_CHECK_IN:
                if (!buffer || !ensureBufferClean(buffer))
                        break;
                if (buffer->lyxvc().inUse() && !buffer->isReadonly()) {
-                       dr.setMessage(buffer->lyxvc().checkIn());
-                       if (!dr.message().empty())
+                       string log;
+                       LyXVC::CommandResult ret = buffer->lyxvc().checkIn(log);
+                       dr.setMessage(log);
+                       // Only skip reloading if the checkin was cancelled or
+                       // an error occured before the real checkin VCS command
+                       // was executed, since the VCS might have changed the
+                       // file even if it could not checkin successfully.
+                       if (ret == LyXVC::ErrorCommand || ret == LyXVC::VCSuccess)
                                reloadBuffer(*buffer);
                }
                break;
@@ -3125,7 +3232,7 @@ bool GuiView::GuiViewPrivate::asyncBufferProcessing(
 void GuiView::dispatchToBufferView(FuncRequest const & cmd, DispatchResult & dr)
 {
        BufferView * bv = currentBufferView();
-       LASSERT(bv, /**/);
+       LASSERT(bv, return);
 
        // Let the current BufferView dispatch its own actions.
        bv->dispatch(cmd, dr);
@@ -3600,6 +3707,8 @@ void GuiView::dispatch(FuncRequest const & cmd, DispatchResult & dr)
                        break;
 
                case LFUN_VC_REGISTER:
+               case LFUN_VC_RENAME:
+               case LFUN_VC_COPY:
                case LFUN_VC_CHECK_IN:
                case LFUN_VC_CHECK_OUT:
                case LFUN_VC_REPO_UPDATE:
@@ -3669,6 +3778,12 @@ void GuiView::dispatch(FuncRequest const & cmd, DispatchResult & dr)
                        one.startscript(Systemcall::DontWait, command);
                        break;
                }
+
+               case LFUN_SPELLING_CONTINUOUSLY:
+                       lyxrc.spellcheck_continuously = !lyxrc.spellcheck_continuously;
+                       dr.screenUpdate(Update::Force | Update::FitCursor);
+                       break;
+
                default:
                        // The LFUN must be for one of BufferView, Buffer or Cursor;
                        // let's try that: