]> git.lyx.org Git - lyx.git/blobdiff - src/frontends/qt4/GuiView.cpp
File->Close All now really closes all buffers. That is, all visible buffers in all...
[lyx.git] / src / frontends / qt4 / GuiView.cpp
index 06a68e3410e7b6b383ce3ec1072fd7fd4254de6d..c92103062a2825b8e5ac76032da303c7e8ca9d83 100644 (file)
@@ -25,6 +25,7 @@
 #include "GuiKeySymbol.h"
 #include "GuiToc.h"
 #include "GuiToolbar.h"
+#include "LayoutBox.h"
 #include "Menus.h"
 #include "TocModel.h"
 
@@ -39,6 +40,7 @@
 #include "BufferView.h"
 #include "Converter.h"
 #include "Cursor.h"
+#include "CutAndPaste.h"
 #include "Encoding.h"
 #include "ErrorList.h"
 #include "Format.h"
@@ -52,6 +54,7 @@
 #include "LyXRC.h"
 #include "LyXVC.h"
 #include "Paragraph.h"
+#include "SpellChecker.h"
 #include "TextClass.h"
 #include "Text.h"
 #include "Toolbars.h"
@@ -264,7 +267,7 @@ public:
         * FIXME: replace that with a proper model so that we are not limited
         * to only one dialog.
         */
-       GuiLayoutBox * layout_;
+       LayoutBox * layout_;
 
        ///
        map<string, Inset *> open_insets_;
@@ -386,8 +389,12 @@ bool GuiView::restoreLayout()
        resize(size);
        move(pos);
 #else
-       if (!restoreGeometry(settings.value("geometry").toByteArray()))
-               setGeometry(50, 50, 690, 510);
+       // Work-around for bug #6034: the window ends up in an undetermined
+       // state when trying to restore a maximized window when it is
+       // already maximized.
+       if (!(windowState() & Qt::WindowMaximized))
+               if (!restoreGeometry(settings.value("geometry").toByteArray()))
+                       setGeometry(50, 50, 690, 510);
 #endif
        // Make sure layout is correctly oriented.
        setLayoutDirection(qApp->layoutDirection());
@@ -426,7 +433,12 @@ void GuiView::constructToolbars()
        for (; it != d.toolbars_.end(); ++it)
                delete it->second;
        d.toolbars_.clear();
-       d.layout_ = 0;
+
+       // I don't like doing this here, but the standard toolbar
+       // destroys this object when it's destroyed itself (vfr)
+       d.layout_ = new LayoutBox(*this);
+       d.stack_widget_->addWidget(d.layout_);
+       d.layout_->move(0,0);
 
        // extracts the toolbars from the backend
        Toolbars::Infos::iterator cit = guiApp->toolbars().begin();
@@ -532,87 +544,18 @@ void GuiView::closeEvent(QCloseEvent * close_event)
        LYXERR(Debug::DEBUG, "GuiView::closeEvent()");
        closing_ = true;
 
+       writeSession();
+
        // it can happen that this event arrives without selecting the view,
        // e.g. when clicking the close button on a background window.
        setFocus();
-       GuiWorkArea * active_wa = currentMainWorkArea();
-       setCurrentWorkArea(active_wa);
-
-       // We might be in a situation that there is still a tabWorkArea, but
-       // there are no tabs anymore. This can happen when we get here after a 
-       // TabWorkArea::lastWorkAreaRemoved() signal. Therefore we count how
-       // many TabWorkArea's have no documents anymore.
-       int empty_twa = 0;
-
-       // We have to call count() each time, because it can happen that
-       // more than one splitter will disappear in one iteration (bug 5998).
-       for (; d.splitter_->count() > empty_twa; ) {
-               TabWorkArea * twa = d.tabWorkArea(empty_twa);
-                               
-               int twa_count = twa->count();
-               if (twa->count() == 0)
-                       ++empty_twa;
-
-               for (; twa_count; --twa_count) {
-                       twa->setCurrentIndex(twa_count-1);
-
-                       GuiWorkArea * wa = twa->currentWorkArea();
-                       bool const is_active_wa = active_wa == wa;
-                       Buffer * b = &wa->bufferView().buffer();
-                       if (b->parent()) {
-                               // This is a child document, just close the tab
-                               // after saving but keep the file loaded.
-                               if (!closeBuffer(*b, true, is_active_wa)) {
-                                       closing_ = false;
-                                       close_event->ignore();
-                                       return;
-                               }
-                               continue;
-                       }
-
-                       vector<Buffer *> clist = b->getChildren();
-                       for (vector<Buffer *>::const_iterator it = clist.begin();
-                                it != clist.end(); ++it) {
-                               if ((*it)->isClean())
-                                       continue;
-                               Buffer * c = *it;
-                               // If a child is dirty, do not close
-                               // without user intervention
-                               if (!closeBuffer(*c, false)) {
-                                       closing_ = false;
-                                       close_event->ignore();
-                                       return;
-                               }
-                       }
-
-                       QList<int> const ids = guiApp->viewIds();
-                       for (int i = 0; i != ids.size(); ++i) {
-                               if (id_ == ids[i])
-                                       continue;
-                               if (guiApp->view(ids[i]).workArea(*b)) {
-                                       // FIXME 1: should we put an alert box here
-                                       // that the buffer is viewed elsewhere?
-                                       // FIXME 2: should we try to save this buffer in any case?
-                                       //saveBuffer(b);
-
-                                       // This buffer is also opened in another view, so
-                                       // close the associated work area...
-                                       removeWorkArea(wa);
-                                       // ... but don't close the buffer.
-                                       b = 0;
-                                       break;
-                               }
-                       }
-                       // closeBuffer() needs buffer workArea still alive and
-                       // set as currrent one, and destroys it
-                       if (b && !closeBuffer(*b, true, is_active_wa)) {
-                               closing_ = false;
-                               close_event->ignore();
-                               return;
-                       }
-               }
+       if (!closeWorkAreaAll()) {
+               closing_ = false;
+               close_event->ignore();
+               return;
        }
-       // Make sure that nothing will use this close to be closed View.
+
+       // Make sure that nothing will use this to be closed View.
        guiApp->unregisterView(this);
 
        if (isFullScreen()) {
@@ -986,6 +929,9 @@ void GuiView::setCurrentWorkArea(GuiWorkArea * wa)
        if (old_gwa == wa)
                return;
 
+       if (view())
+               cap::saveSelection(view()->cursor());
+
        theGuiApp()->setCurrentView(this);
        d.current_work_area_ = wa;
        for (int i = 0; i != d.splitter_->count(); ++i) {
@@ -1054,9 +1000,9 @@ void GuiView::removeWorkArea(GuiWorkArea * wa)
 }
 
 
-void GuiView::setLayoutDialog(GuiLayoutBox * layout)
+LayoutBox * GuiView::getLayoutDialog() const
 {
-       d.layout_ = layout;
+       return d.layout_;
 }
 
 
@@ -1136,7 +1082,7 @@ void GuiView::connectBuffer(Buffer & buf)
 void GuiView::disconnectBuffer()
 {
        if (d.current_work_area_)
-               d.current_work_area_->bufferView().setGuiDelegate(0);
+               d.current_work_area_->bufferView().buffer().setGuiDelegate(0);
 }
 
 
@@ -1242,6 +1188,10 @@ bool GuiView::getStatus(FuncRequest const & cmd, FuncStatus & flag)
                enable = buf;
                break;
 
+       case LFUN_BUFFER_CLOSE_ALL:
+               enable = theBufferList().last() != theBufferList().first();
+               break;
+
        case LFUN_SPLIT_VIEW:
                if (cmd.getArg(0) == "vertical")
                        enable = buf && (d.splitter_->count() == 1 ||
@@ -1278,7 +1228,7 @@ bool GuiView::getStatus(FuncRequest const & cmd, FuncStatus & flag)
                        enable = buf->isExportable("dvi")
                                && lyxrc.print_command != "none";
                else if (name == "character" || name == "symbols") {
-                       if (!view() || !view()->cursor().inTexted())
+                       if (buf->isReadonly() || !view() || !view()->cursor().inTexted())
                                enable = false;
                        else {
                                // FIXME we should consider passthru
@@ -1894,34 +1844,167 @@ bool GuiView::saveBuffer(Buffer & b)
 }
 
 
+bool GuiView::hideWorkArea(GuiWorkArea * wa)
+{
+       return closeWorkArea(wa, false);
+}
+
+
+bool GuiView::closeWorkArea(GuiWorkArea * wa)
+{
+       Buffer & buf = wa->bufferView().buffer();
+       return closeWorkArea(wa, !buf.parent());
+}
+
+
 bool GuiView::closeBuffer()
 {
-       Buffer * buf = buffer();
-       return buf && closeBuffer(*buf);
+       GuiWorkArea * wa = currentMainWorkArea();
+       Buffer & buf = wa->bufferView().buffer();
+       return wa && closeWorkArea(wa, !buf.parent());
+}
+
+
+void GuiView::writeSession() const {
+       GuiWorkArea const * active_wa = currentMainWorkArea();
+       for (int i = 0; i < d.splitter_->count(); ++i) {
+               TabWorkArea * twa = d.tabWorkArea(i);
+               for (int j = 0; j < twa->count(); ++j) {
+                       GuiWorkArea * wa = static_cast<GuiWorkArea *>(twa->widget(j));
+                       Buffer & buf = wa->bufferView().buffer();
+                       theSession().lastOpened().add(buf.fileName(), wa == active_wa);
+               }
+       }
+}
+
+
+bool GuiView::closeBufferAll()
+{
+       // Close the workareas in all other views
+       QList<int> const ids = guiApp->viewIds();
+       for (int i = 0; i != ids.size(); ++i) {
+               if (id_ != ids[i] && !guiApp->view(ids[i]).closeWorkAreaAll())
+                       return false;
+       }
+
+       // Close our own workareas
+       if (!closeWorkAreaAll())
+               return false;
+
+       // Now close the hidden buffers. We prevent hidden buffers from being
+       // dirty, so we can just close them.
+       theBufferList().closeAll();
+       return true;
 }
 
 
-bool GuiView::closeBuffer(Buffer & buf, bool tolastopened, bool mark_active)
+bool GuiView::closeWorkAreaAll()
 {
+       // To write in the session file which workarea was active.
+       GuiWorkArea * active_wa = currentMainWorkArea();
+       setCurrentWorkArea(active_wa);
+
+       // We might be in a situation that there is still a tabWorkArea, but
+       // there are no tabs anymore. This can happen when we get here after a 
+       // TabWorkArea::lastWorkAreaRemoved() signal. Therefore we count how
+       // many TabWorkArea's have no documents anymore.
+       int empty_twa = 0;
+
+       // We have to call count() each time, because it can happen that
+       // more than one splitter will disappear in one iteration (bug 5998).
+       for (; d.splitter_->count() > empty_twa; ) {
+               TabWorkArea * twa = d.tabWorkArea(empty_twa);
+
+               if (twa->count() == 0)
+                       ++empty_twa;
+               else {
+                       setCurrentWorkArea(twa->currentWorkArea());
+                       if (!closeTabWorkArea(twa, active_wa))
+                               return false;
+               }
+       }
+       return true;
+}
+
+
+bool GuiView::closeWorkArea(GuiWorkArea * wa, bool close_buffer,
+       bool is_active)
+{
+       Buffer & buf = wa->bufferView().buffer();
+
+       // If we are in a close_event all children will be closed in some time,
+       // so no need to do it here. This will ensure that the children end up
+       // in the session file in the correct order. If we close the master
+       // buffer, we can close or release the child buffers here too.
+       if (close_buffer && !closing_) {
+               vector<Buffer *> clist = buf.getChildren();
+               for (vector<Buffer *>::const_iterator it = clist.begin();
+                        it != clist.end(); ++it) {
+                       // If a child is dirty, do not close
+                       // without user intervention
+                       //FIXME: should we look in other tabworkareas?
+                       Buffer * child_buf = *it;
+                       GuiWorkArea * child_wa = workArea(*child_buf);
+                       if (child_wa) {
+                               if (!closeWorkArea(child_wa, true))
+                                       return false;
+                       } else
+                               theBufferList().releaseChild(&buf, child_buf);
+               }
+       }
        // goto bookmark to update bookmark pit.
        //FIXME: we should update only the bookmarks related to this buffer!
        LYXERR(Debug::DEBUG, "GuiView::closeBuffer()");
        for (size_t i = 0; i < theSession().bookmarks().size(); ++i)
                theLyXFunc().gotoBookmark(i+1, false, false);
 
-       if (buf.isClean() || buf.paragraphs().empty()) {
+       // if we are only hiding the buffer and there are multiple views
+       // of the buffer, then we do not need to ensure a clean buffer.
+       bool const allow_dirty = inMultiTabs(wa) && !close_buffer;
+
+       if (allow_dirty || saveBufferIfNeeded(buf, !close_buffer)) {
                // save in sessions if requested
                // do not save childs if their master
                // is opened as well
-               if (tolastopened)
-                       theSession().lastOpened().add(buf.fileName(), mark_active);
-               if (buf.parent())
-                       // Don't close child documents.
-                       removeWorkArea(currentMainWorkArea());
+               if (closing_)
+                       theSession().lastOpened().add(buf.fileName(), is_active);
+               if (!close_buffer)
+                       removeWorkArea(wa);
                else
                        theBufferList().release(&buf);
                return true;
        }
+       return false;
+}
+
+
+bool GuiView::closeTabWorkArea(TabWorkArea * twa, GuiWorkArea * main_work_area)
+{
+       while (twa == d.currentTabWorkArea()) {
+               twa->setCurrentIndex(twa->count()-1);
+
+               GuiWorkArea * wa = twa->currentWorkArea();
+               bool const is_active_wa = main_work_area == wa;
+               Buffer & b = wa->bufferView().buffer();
+
+               // We only want to close the buffer if the same buffer is not visible
+               // in another view, and if this is not a child and if we are closing
+               // a view (not a tabgroup).
+               bool const close_buffer = 
+                       !inMultiViews(wa) && !b.parent() && closing_;
+
+               if (!closeWorkArea(wa, close_buffer, is_active_wa))
+                       return false;
+       }
+       return true;
+}
+
+
+bool GuiView::saveBufferIfNeeded(Buffer & buf, bool hiding)
+{
+       if (buf.isClean() || buf.paragraphs().empty())
+               return true;
+
        // Switch to this Buffer.
        setBuffer(&buf);
 
@@ -1936,10 +2019,21 @@ bool GuiView::closeBuffer(Buffer & buf, bool tolastopened, bool mark_active)
        raise();
        activateWindow();
 
-       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"));
+       int ret;
+       if (hiding && buf.isUnnamed()) {
+               docstring const text = bformat(_("The document %1$s has not been "
+                                            "saved yet.\n\nDo you want to save "
+                                            "the document?"), file);
+               ret = Alert::prompt(_("Save new document?"), 
+                       text, 0, 1, _("&Save"), _("&Cancel"));
+               if (ret == 1)
+                       ++ret;
+       } else {
+               docstring const text = bformat(_("The document %1$s has unsaved changes."
+                       "\n\nDo you want to save the document or discard the changes?"), file);
+               ret = Alert::prompt(_("Save changed document?"),
+                       text, 0, 2, _("&Save"), _("&Discard"), _("&Cancel"));
+       }
 
        switch (ret) {
        case 0:
@@ -1951,22 +2045,45 @@ bool GuiView::closeBuffer(Buffer & buf, bool tolastopened, bool mark_active)
                // have no autosave file but I guess
                // this is really improbable (Jug)
                buf.removeAutosaveFile();
+               if (hiding)
+                       // revert all changes
+                       buf.loadLyXFile(buf.fileName());
+               buf.markClean();
                break;
        case 2:
                return false;
        }
+       return true;
+}
 
-       // save file names to .lyx/session
-       if (tolastopened)
-               theSession().lastOpened().add(buf.fileName(), mark_active);
 
-       if (buf.parent())
-               // Don't close child documents.
-               removeWorkArea(currentMainWorkArea());
-       else
-               theBufferList().release(&buf);
+bool GuiView::inMultiTabs(GuiWorkArea * wa)
+{
+       Buffer & buf = wa->bufferView().buffer();
 
-       return true;
+       for (int i = 0; i != d.splitter_->count(); ++i) {
+               GuiWorkArea * wa_ = d.tabWorkArea(i)->workArea(buf);
+               if (wa_ && wa_ != wa)
+                       return true;
+       }
+       return inMultiViews(wa);
+}
+
+
+bool GuiView::inMultiViews(GuiWorkArea * wa)
+{
+       QList<int> const ids = guiApp->viewIds();
+       Buffer & buf = wa->bufferView().buffer();
+
+       int found_twa = 0;
+       for (int i = 0; i != ids.size() && found_twa <= 1; ++i) {
+               if (id_ == ids[i])
+                       continue;
+               
+               if (guiApp->view(ids[i]).workArea(buf))
+                       return true;
+       }
+       return false;
 }
 
 
@@ -2042,8 +2159,7 @@ bool GuiView::dispatch(FuncRequest const & cmd)
                        break;
                }
                case LFUN_DROP_LAYOUTS_CHOICE:
-                       if (d.layout_)
-                               d.layout_->showPopup();
+                       d.layout_->showPopup();
                        break;
 
                case LFUN_MENU_OPEN:
@@ -2209,7 +2325,8 @@ bool GuiView::dispatch(FuncRequest const & cmd)
 
                case LFUN_CLOSE_TAB_GROUP:
                        if (TabWorkArea * twa = d.currentTabWorkArea()) {
-                               delete twa;
+                               closeTabWorkArea(twa, false);
+                               d.current_work_area_ = 0;
                                twa = d.currentTabWorkArea();
                                // Switch to the next GuiWorkArea in the found TabWorkArea.
                                if (twa) {
@@ -2452,8 +2569,7 @@ void GuiView::resetDialogs()
        menuBar()->clear();
        constructToolbars();
        guiApp->menus().fillMenuBar(menuBar(), this, false);
-       if (d.layout_)
-               d.layout_->updateContents(true);
+       d.layout_->updateContents(true);
        // Now update controls with current buffer.
        theLyXFunc().setLyXView(this);
        restoreLayout();