]> git.lyx.org Git - lyx.git/blobdiff - src/frontends/qt4/GuiView.cpp
Fix a crash when closing LyX while a master and a dirty child were open, and if the...
[lyx.git] / src / frontends / qt4 / GuiView.cpp
index 77e916b518af49488c41b41b6e9b0a105babe3d8..df9986b29e7fab6bc735549aab8417e6a54110bb 100644 (file)
@@ -25,6 +25,7 @@
 #include "GuiKeySymbol.h"
 #include "GuiToc.h"
 #include "GuiToolbar.h"
+#include "LayoutBox.h"
 #include "Menus.h"
 #include "TocModel.h"
 
@@ -266,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_;
@@ -432,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();
@@ -547,7 +553,7 @@ void GuiView::closeEvent(QCloseEvent * close_event)
                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()) {
@@ -598,21 +604,22 @@ bool GuiView::closeBufferAll(bool tolastopened)
                if (twa->count() == 0)
                        ++empty_twa;
 
-               for (; twa_count; --twa_count) {
+               for (; twa == d.tabWorkArea(empty_twa) && 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()) {
+                       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, tolastopened, is_active_wa))
+                               if (!closeWorkArea(wa, false, tolastopened, is_active_wa))
                                        return false;
                                continue;
                        }
 
-                       vector<Buffer *> clist = b->getChildren();
+                       vector<Buffer *> clist = b.getChildren();
                        for (vector<Buffer *>::const_iterator it = clist.begin();
                                 it != clist.end(); ++it) {
                                if ((*it)->isClean())
@@ -620,31 +627,18 @@ bool GuiView::closeBufferAll(bool tolastopened)
                                Buffer * c = *it;
                                // If a child is dirty, do not close
                                // without user intervention
-                               if (!closeBuffer(*c, false))
+                               //FIXME: should buffers be closed or not?
+                               if (!closeWorkArea(workArea(*c), false, false))
                                        return false;
                        }
 
-                       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;
-                               }
-                       }
+                       // We only want to close the buffer if the same buffer is not in
+                       // another view.
+                       bool const close_buffer = !inMultiViews(wa);
+
                        // closeBuffer() needs buffer workArea still alive and
                        // set as currrent one, and destroys it
-                       if (b && !closeBuffer(*b, tolastopened, is_active_wa))
+                       if (!closeWorkArea(wa, close_buffer, tolastopened, is_active_wa))
                                return false;
                }
        }
@@ -1066,9 +1060,9 @@ void GuiView::removeWorkArea(GuiWorkArea * wa)
 }
 
 
-void GuiView::setLayoutDialog(GuiLayoutBox * layout)
+LayoutBox * GuiView::getLayoutDialog() const
 {
-       d.layout_ = layout;
+       return d.layout_;
 }
 
 
@@ -1922,14 +1916,22 @@ bool GuiView::saveBuffer(Buffer & b)
 }
 
 
+bool GuiView::hideWorkArea(GuiWorkArea * wa)
+{
+       return closeWorkArea(wa, false);
+}
+
+
 bool GuiView::closeBuffer()
 {
-       Buffer * buf = buffer();
-       return buf && closeBuffer(*buf);
+       GuiWorkArea * wa = currentMainWorkArea();
+       Buffer & buf = wa->bufferView().buffer();
+       return wa && closeWorkArea(wa, !buf.parent());
 }
 
 
-bool GuiView::closeBuffer(Buffer & buf, bool tolastopened, bool mark_active)
+bool GuiView::closeWorkArea(GuiWorkArea * wa, bool close_buffer,
+       bool tolastopened, bool mark_active)
 {
        // goto bookmark to update bookmark pit.
        //FIXME: we should update only the bookmarks related to this buffer!
@@ -1937,15 +1939,19 @@ bool GuiView::closeBuffer(Buffer & buf, bool tolastopened, bool mark_active)
        for (size_t i = 0; i < theSession().bookmarks().size(); ++i)
                theLyXFunc().gotoBookmark(i+1, false, false);
 
-       if (saveBufferIfNeeded(buf)) {
+       // 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;
+
+       Buffer & buf = wa->bufferView().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 (!close_buffer)
+                       removeWorkArea(wa);
                else
                        theBufferList().release(&buf);
                return true;
@@ -1954,7 +1960,7 @@ bool GuiView::closeBuffer(Buffer & buf, bool tolastopened, bool mark_active)
 }
 
 
-bool GuiView::saveBufferIfNeeded(Buffer & buf)
+bool GuiView::saveBufferIfNeeded(Buffer & buf, bool hiding)
 {
        if (buf.isClean() || buf.paragraphs().empty())
                return true;
@@ -1973,10 +1979,21 @@ bool GuiView::saveBufferIfNeeded(Buffer & buf)
        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:
@@ -1988,6 +2005,11 @@ bool GuiView::saveBufferIfNeeded(Buffer & buf)
                // 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;
@@ -1996,6 +2018,36 @@ bool GuiView::saveBufferIfNeeded(Buffer & buf)
 }
 
 
+bool GuiView::inMultiTabs(GuiWorkArea * wa)
+{
+       Buffer & buf = wa->bufferView().buffer();
+
+       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;
+}
+
+
 void GuiView::gotoNextOrPreviousBuffer(NextOrPrevious np)
 {
        Buffer * const curbuf = buffer();
@@ -2068,8 +2120,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:
@@ -2236,6 +2287,7 @@ bool GuiView::dispatch(FuncRequest const & cmd)
                case LFUN_CLOSE_TAB_GROUP:
                        if (TabWorkArea * twa = d.currentTabWorkArea()) {
                                delete twa;
+                               d.current_work_area_ = 0;
                                twa = d.currentTabWorkArea();
                                // Switch to the next GuiWorkArea in the found TabWorkArea.
                                if (twa) {
@@ -2478,8 +2530,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();