[education/rkward/devel/workspace_output] rkward: Implement bulk saving of selected windows.

Thomas Friedrichsmeier null at kde.org
Sat Oct 10 09:18:45 BST 2020


Git commit 92f762fe1f59dd1a310ae37f1d495f48c2a662f9 by Thomas Friedrichsmeier.
Committed on 30/11/2017 at 10:35.
Pushed by tfry into branch 'devel/workspace_output'.

Implement bulk saving of selected windows.

As this means we do can no longer rely on the Windows to ask for saving when closed, this needed several changes all over the place:
- Add save() method on RKMDIWindow
- Add parameter to control automatic save prompt when closing (and remove redundant "also_delete" parameter while at it)

TODO: Save handling for data and output(s)

M  +10   -4    rkward/dialogs/rksavemodifieddialog.cpp
M  +4    -2    rkward/dialogs/rksavemodifieddialog.h
M  +7    -12   rkward/rkward.cpp
M  +1    -1    rkward/windows/detachedwindowcontainer.cpp
M  +7    -1    rkward/windows/rkcommandeditorwindow.cpp
M  +2    -0    rkward/windows/rkcommandeditorwindow.h
M  +2    -2    rkward/windows/rkdebugconsole.cpp
M  +1    -1    rkward/windows/rkdebugconsole.h
M  +15   -17   rkward/windows/rkmdiwindow.cpp
M  +10   -2    rkward/windows/rkmdiwindow.h
M  +1    -1    rkward/windows/rktoolwindowbar.cpp
M  +1    -1    rkward/windows/rktoplevelwindowgui.cpp
M  +5    -5    rkward/windows/rkwindowcatcher.cpp
M  +1    -1    rkward/windows/rkwindowcatcher.h
M  +3    -3    rkward/windows/rkworkplace.cpp
M  +1    -1    rkward/windows/rkworkplace.h
M  +1    -1    rkward/windows/rkworkplaceview.cpp

https://invent.kde.org/education/rkward/commit/92f762fe1f59dd1a310ae37f1d495f48c2a662f9

diff --git a/rkward/dialogs/rksavemodifieddialog.cpp b/rkward/dialogs/rksavemodifieddialog.cpp
index 3e0fc845..d5e12df2 100644
--- a/rkward/dialogs/rksavemodifieddialog.cpp
+++ b/rkward/dialogs/rksavemodifieddialog.cpp
@@ -77,7 +77,7 @@ RKSaveModifiedDialog::RKSaveModifiedDialog (QWidget* parent, QList<RKMDIWindow*>
 			item->setFirstColumnSpanned (true);
 			header->addChild (item);
 			item->setCheckState (0, Qt::Checked);
-			item->setData (0, Qt::UserRole, QVariant::fromValue (QPointer<RKMDIWindow> (modified_wins[i])));
+			window_checklist.insert (item, QPointer<RKMDIWindow> (modified_wins[i]));
 		}
 	}
 
@@ -108,7 +108,13 @@ void RKSaveModifiedDialog::saveWorkplaceChanged () {
 
 void RKSaveModifiedDialog::saveSelected () {
 	RK_TRACE (APP);
-// TODO: get list of selected items from tree, and save them
-// TODO: _when done_:
-//	accepted ();
+
+	bool all_ok = true;
+	for (QMap<QTreeWidgetItem *, QPointer<RKMDIWindow>>::const_iterator it = window_checklist.constBegin (); it != window_checklist.constEnd (); ++it) {
+		if (it.key ()->checkState (0) != Qt::Checked) continue;
+		if (!it.value ()) continue;
+		if (!it.value ()->save ()) all_ok = false; // but we proceed with the others
+	}
+	if (all_ok) accept ();
+	else reject ();
 }
diff --git a/rkward/dialogs/rksavemodifieddialog.h b/rkward/dialogs/rksavemodifieddialog.h
index 9a711bf9..88cbfa8e 100644
--- a/rkward/dialogs/rksavemodifieddialog.h
+++ b/rkward/dialogs/rksavemodifieddialog.h
@@ -20,10 +20,12 @@
 
 #include <QDialog>
 #include <QList>
+#include <QMap>
+#include <QPointer>
 
 #include "../windows/rkmdiwindow.h"
 
-class QTreeWidget;
+class QTreeWidgetItem;
 
 class RKSaveModifiedDialog : public QDialog {
 	Q_OBJECT
@@ -36,7 +38,7 @@ public:
 private:
 	RKSaveModifiedDialog (QWidget* parent, QList<RKMDIWindow*> modified_windows, bool project);
 	virtual ~RKSaveModifiedDialog ();
-	QTreeWidget *tree;
+	QMap<QTreeWidgetItem *, QPointer<RKMDIWindow>> window_checklist;
 private slots:
 	void saveWorkplaceChanged ();
 	void saveSelected ();
diff --git a/rkward/rkward.cpp b/rkward/rkward.cpp
index 41a29ff3..676c255f 100644
--- a/rkward/rkward.cpp
+++ b/rkward/rkward.cpp
@@ -751,19 +751,14 @@ bool RKWardMainWindow::doQueryQuit () {
 	}
 //	}
 
-	RKWorkplace::RKWorkplaceObjectList map = RKWorkplace::mainWorkplace ()->getObjectList ();
-	for (RKWorkplace::RKWorkplaceObjectList::const_iterator it = map.constBegin (); it != map.constEnd (); ++it) {
-		lockGUIRebuild (true);
-		if (!(*it)->close (true)) {
-			if (!(*it)->isType (RKMDIWindow::X11Window)) {	// X11 windows have a delayed close
-				// If a child refuses to close, we return false.
-				slotSetStatusReady ();
-				lockGUIRebuild (false);
-				return false;
-			}
-		}
-//		lockGUIRebuild (false);  // No need to update GUI anymore (and doing so is potentially asking for trouble, anyway)
+	lockGUIRebuild (true);
+	if (!RKWorkplace::mainWorkplace ()->closeAll ()) {
+		// User cancelled closing, when asked to save changes
+		slotSetStatusReady ();
+		lockGUIRebuild (false);
+		return false;
 	}
+//	lockGUIRebuild (false);  // No need to update GUI anymore (and doing so is potentially asking for trouble, anyway)
 
 	return true;
 }
diff --git a/rkward/windows/detachedwindowcontainer.cpp b/rkward/windows/detachedwindowcontainer.cpp
index f298287a..c0e35984 100644
--- a/rkward/windows/detachedwindowcontainer.cpp
+++ b/rkward/windows/detachedwindowcontainer.cpp
@@ -148,7 +148,7 @@ void DetachedWindowContainer::slotReattach () {
 void DetachedWindowContainer::closeEvent (QCloseEvent *e) {
 	RK_TRACE (APP);
 
-	if (captured->close (true)) {
+	if (captured->close (RKMDIWindow::AutoAskSaveModified)) {
 		e->accept ();
 	} else {
 		e->ignore ();
diff --git a/rkward/windows/rkcommandeditorwindow.cpp b/rkward/windows/rkcommandeditorwindow.cpp
index 21a63173..2d1e203a 100644
--- a/rkward/windows/rkcommandeditorwindow.cpp
+++ b/rkward/windows/rkcommandeditorwindow.cpp
@@ -250,6 +250,7 @@ RKCommandEditorWindow::~RKCommandEditorWindow () {
 
 	delete hinter;
 	delete m_view;
+	m_doc->waitSaveComplete ();
 	QList<KTextEditor::View*> views = m_doc->views ();
 	if (views.isEmpty ()) {
 		delete m_doc;
@@ -554,7 +555,12 @@ QUrl RKCommandEditorWindow::url () const {
 
 bool RKCommandEditorWindow::isModified () {
 	RK_TRACE (COMMANDEDITOR);
-	return m_doc->isModified();
+	return m_doc->isModified ();
+}
+
+bool RKCommandEditorWindow::save () {
+	RK_TRACE (COMMANDEDITOR);
+	return m_doc->documentSave ();
 }
 
 void RKCommandEditorWindow::insertText (const QString &text) {
diff --git a/rkward/windows/rkcommandeditorwindow.h b/rkward/windows/rkcommandeditorwindow.h
index 187075bc..5982c89d 100644
--- a/rkward/windows/rkcommandeditorwindow.h
+++ b/rkward/windows/rkcommandeditorwindow.h
@@ -149,6 +149,8 @@ public:
 	~RKCommandEditorWindow ();
 /** returns, whether the document was modified since the last save */
 	bool isModified () override;
+/** saves the document, returns true on success */
+	bool save () override;
 /** insert the given text into the document at the current cursor position. Additionally, focuses the view */
 	void insertText (const QString &text);
 /** set the current text (clear all previous text, and sets new text) */
diff --git a/rkward/windows/rkdebugconsole.cpp b/rkward/windows/rkdebugconsole.cpp
index 1e1a154f..53502b7c 100644
--- a/rkward/windows/rkdebugconsole.cpp
+++ b/rkward/windows/rkdebugconsole.cpp
@@ -163,13 +163,13 @@ void RKDebugConsole::sendReply (const QString &reply) {
 	RKDebugHandler::instance ()->submitDebugString (reply);
 }
 
-bool RKDebugConsole::close (bool also_delete) {
+bool RKDebugConsole::close (CloseWindowMode ask_save) {
 	RK_TRACE (APP);
 
 	if (RKDebugHandler::instance ()->state () != RKDebugHandler::NotInDebugger) {
 		KMessageBox::sorry (this, i18n ("This window cannot be closed, while a debugger is active. If you have no idea what this means, and you want to get out, press the 'Cancel' button on the right hand side of this window."));
 		return false;
 	}
-	return RKMDIWindow::close (also_delete);
+	return RKMDIWindow::close (ask_save);
 }
 
diff --git a/rkward/windows/rkdebugconsole.h b/rkward/windows/rkdebugconsole.h
index 70be9267..c29f3667 100644
--- a/rkward/windows/rkdebugconsole.h
+++ b/rkward/windows/rkdebugconsole.h
@@ -35,7 +35,7 @@ public:
 	static RKDebugConsole *instance () { return _instance; };
 
 	// reimplemented to refuse closing while inside the debugger
-	bool close (bool auto_delete);
+	bool close (CloseWindowMode aks_save) override;
 public slots:
 	void newDebugState ();
 private slots:
diff --git a/rkward/windows/rkmdiwindow.cpp b/rkward/windows/rkmdiwindow.cpp
index 60c8e881..e4ea7773 100644
--- a/rkward/windows/rkmdiwindow.cpp
+++ b/rkward/windows/rkmdiwindow.cpp
@@ -26,6 +26,7 @@
 #include <QVBoxLayout>
 
 #include <kparts/partactivateevent.h>
+#include <kparts/readwritepart.h>
 #include <kxmlguifactory.h>
 #include <kactioncollection.h>
 #include <KLocalizedString>
@@ -156,7 +157,7 @@ void RKMDIWindow::activate (bool with_focus) {
 	}
 }
 
-bool RKMDIWindow::close (bool also_delete) {
+bool RKMDIWindow::close (CloseWindowMode ask_save) {
 	RK_TRACE (APP);
 
 	if (isToolWindow ()) {
@@ -173,27 +174,24 @@ bool RKMDIWindow::close (bool also_delete) {
 
 		if (tool_window_bar) tool_window_bar->hideWidget (this);
 		else hide ();
+
 		return true;
 	}
 
-	if (also_delete) {
-		bool closed = QWidget::close ();
-		if (closed) {
-			// WORKAROUND for https://bugs.kde.org/show_bug.cgi?id=170806
-			// NOTE: can't move this to the d'tor, since the part is already partially deleted, then
-			// TODO: use version check / remove once fixed in kdelibs
-			if (part && part->factory ()) {
-				part->factory ()->removeClient (part);
-			}
-			// WORKAROUND end
+	bool ok_to_close = (ask_save == NoAskSaveModified) || QWidget::close ();
+	if (!ok_to_close) return false;
 
-			delete this;	// Note: using deleteLater(), here does not work well while restoring workplaces (window is not fully removed from workplace before restoring)
-		}
-		return closed;
-	} else {
-		RK_ASSERT (!testAttribute (Qt::WA_DeleteOnClose));
-		return QWidget::close ();
+	// WORKAROUND for https://bugs.kde.org/show_bug.cgi?id=170806
+	// NOTE: can't move this to the d'tor, since the part is already partially deleted, then
+	// TODO: use version check / remove once fixed in kdelibs
+	if (part && part->factory ()) {
+		part->factory ()->removeClient (part);
 	}
+	// WORKAROUND end
+
+	delete this;	// Note: using deleteLater(), here does not work well while restoring workplaces (window is not fully removed from workplace before restoring)
+
+	return true;
 }
 
 void RKMDIWindow::prepareToBeAttached () {
diff --git a/rkward/windows/rkmdiwindow.h b/rkward/windows/rkmdiwindow.h
index d5632a43..5e7a2cf7 100644
--- a/rkward/windows/rkmdiwindow.h
+++ b/rkward/windows/rkmdiwindow.h
@@ -82,6 +82,9 @@ public slots:
 public:
 /** @returns true, if the window's document was modified (and would need to be saved) */
 	virtual bool isModified () { return false; };
+/** Ask the window's document to save itself.
+ at returns true on success, _or_ if the document cannot be saved at all (in which case isModified() should return false, too. False, if saving failed / was cancelled */
+	virtual bool save () { return true; };
 /** @returns A long / complete caption. Default implementation simply calls shortCaption () */
 	virtual QString fullCaption ();
 /** @returns A short caption (e.g. only the filename without the path). Default implementation simply calls QWidget::caption () */
@@ -102,8 +105,13 @@ public:
 	virtual void prepareToBeAttached ();
 /** If your mdi window should perform any adjustments before being detached, reimplement this function. Default implementation does nothing, but raises an assert, if this is a tool window */
 	virtual void prepareToBeDetached ();
-/** Tool windows will only hide themselves, and ignore the also_delete flag */
-	virtual bool close (bool also_delete);
+	enum CloseWindowMode {
+		AutoAskSaveModified,
+		NoAskSaveModified
+	};
+/** Closes the window. Most windows will be autodestruct (unless user choses to cancel closing while asked whether to save modifications), tool windows will only hide themselves (and never ask for saving).
+ at returns true, if the window was closed, false otherwise. */
+	virtual bool close (CloseWindowMode ask_save);
 /** Set a status message to be shown in a popup inside the window. The message persists until the given R command has finished, or until this function is called with an empty string.
 This should be used, when the information shown is currently out-of-date (e.g. when refreshing a preview / loading a plot from history), _not_ when the window
 is simply busy (e.g. when saving the current plot to history). */
diff --git a/rkward/windows/rktoolwindowbar.cpp b/rkward/windows/rktoolwindowbar.cpp
index cc903279..6c5a5252 100644
--- a/rkward/windows/rktoolwindowbar.cpp
+++ b/rkward/windows/rktoolwindowbar.cpp
@@ -242,7 +242,7 @@ void RKToolWindowBar::tabClicked (int id) {
 	RK_ASSERT (widget);
 
 	if (widget->isActive ()) {
-		if (!widget->isAttached ()) widget->close (false);
+		if (!widget->isAttached ()) widget->close (RKMDIWindow::NoAskSaveModified);
 		else hideWidget (widget);
 	} else {
 		widget->activate (true);
diff --git a/rkward/windows/rktoplevelwindowgui.cpp b/rkward/windows/rktoplevelwindowgui.cpp
index cd36ce5b..5948715f 100644
--- a/rkward/windows/rktoplevelwindowgui.cpp
+++ b/rkward/windows/rktoplevelwindowgui.cpp
@@ -157,7 +157,7 @@ void RKTopLevelWindowGUI::toggleToolView (RKMDIWindow *tool_window) {
 	RK_ASSERT (tool_window);
 
 	if (tool_window->isActive ()) {
-		tool_window->close (false);
+		tool_window->close (RKMDIWindow::NoAskSaveModified);
 		activateDocumentView ();
 	} else {
 		tool_window->activate (true);
diff --git a/rkward/windows/rkwindowcatcher.cpp b/rkward/windows/rkwindowcatcher.cpp
index 7e305d01..aa949bd2 100644
--- a/rkward/windows/rkwindowcatcher.cpp
+++ b/rkward/windows/rkwindowcatcher.cpp
@@ -202,7 +202,7 @@ void RKWindowCatcher::killDevice (int device_number) {
 	RKCaughtX11Window* window = RKCaughtX11Window::getWindow (device_number);
 	if (window) {
 		window->setKilledInR ();
-		window->close (true);
+		window->close (RKMDIWindow::AutoAskSaveModified);
 		QApplication::sync ();
 	}
 }
@@ -356,7 +356,7 @@ RKCaughtX11Window::~RKCaughtX11Window () {
 	RK_ASSERT (device_windows.contains (device_number));
 	device_windows.remove (device_number);
 
-	close (false);
+	close (NoAskSaveModified);
 	if (embedded) RKWindowCatcher::instance ()->unregisterWatcher (embedded->winId ());
 	error_dialog->autoDeleteWhenDone ();
 }
@@ -380,14 +380,14 @@ void RKCaughtX11Window::forceClose () {
 		embedded->setParent (0);
 		qApp->processEvents ();
 	}
-	RKMDIWindow::close (true);
+	RKMDIWindow::close (NoAskSaveModified);
 }
 
-bool RKCaughtX11Window::close (bool also_delete) {
+bool RKCaughtX11Window::close (CloseWindowMode ask_save) {
 	RK_TRACE (MISC);
 
 	if (killed_in_r || RKGlobals::rInterface ()->backendIsDead ()) {
-		return RKMDIWindow::close (also_delete);
+		return RKMDIWindow::close (ask_save);
 	}
 
 	if (rk_native_device) rk_native_device->stopInteraction ();
diff --git a/rkward/windows/rkwindowcatcher.h b/rkward/windows/rkwindowcatcher.h
index 33534ebd..249d898d 100644
--- a/rkward/windows/rkwindowcatcher.h
+++ b/rkward/windows/rkwindowcatcher.h
@@ -168,7 +168,7 @@ public slots:
 	void showPlotInfo ();
 
 /** reimplemented to keep window alive while saving history */
-	bool close (bool also_delete) override;
+	bool close (CloseWindowMode ask_save) override;
 	void setKilledInR () { killed_in_r = true; };
 	void setWindowStyleHint (const QString& hint) override;
 private slots:
diff --git a/rkward/windows/rkworkplace.cpp b/rkward/windows/rkworkplace.cpp
index f33be251..383506ef 100644
--- a/rkward/windows/rkworkplace.cpp
+++ b/rkward/windows/rkworkplace.cpp
@@ -611,12 +611,12 @@ void RKWorkplace::flushAllData () {
 	}
 }
 
-void RKWorkplace::closeWindow (RKMDIWindow *window) {
+void RKWorkplace::closeWindow (RKMDIWindow *window, RKMDIWindow::CloseWindowMode ask_save) {
 	RK_TRACE (APP);
 	RK_ASSERT (windows.contains (window));
 
 	bool tool_window = window->isToolWindow ();
-	window->close (true);		// all the rest should happen in removeWindow ()
+	window->close (ask_save);		// all the rest should happen in removeWindow ()
 	
 	if (tool_window) windowRemoved ();	// for regular windows, this happens in removeWindow(), already
 }
@@ -653,7 +653,7 @@ bool RKWorkplace::closeWindows (QList<RKMDIWindow*> windows) {
 	bool ok = RKSaveModifiedDialog::askSaveModified (this, windows, false);
 	if (ok) {
 		for (int i = windows.size () - 1; i >= 0; --i) {
-			closeWindow (windows[i]);
+			closeWindow (windows[i], RKMDIWindow::NoAskSaveModified);
 			// TODO: Do not ask for saving _again_
 		}
 	}
diff --git a/rkward/windows/rkworkplace.h b/rkward/windows/rkworkplace.h
index f43673ab..c0527791 100644
--- a/rkward/windows/rkworkplace.h
+++ b/rkward/windows/rkworkplace.h
@@ -137,7 +137,7 @@ public:
 	void closeActiveWindow ();
 /** Close the given window, whether it is attached or detached.
 @param window window to close */
-	void closeWindow (RKMDIWindow *window);
+	void closeWindow (RKMDIWindow *window, RKMDIWindow::CloseWindowMode ask_save = RKMDIWindow::AutoAskSaveModified);
 /** Close the given windows, whether they are attached or detached. TODO: Be smart about asking what to save.
 @param windows list windows to close
 @returns false if cancelled by user (user was prompted for saving, and chose cancel) */
diff --git a/rkward/windows/rkworkplaceview.cpp b/rkward/windows/rkworkplaceview.cpp
index 04d76728..2b0cfef5 100644
--- a/rkward/windows/rkworkplaceview.cpp
+++ b/rkward/windows/rkworkplaceview.cpp
@@ -132,7 +132,7 @@ void RKWorkplaceViewPane::closePage (QWidget* page) {
 		RK_ASSERT (false);
 		return;
 	}
-	static_cast<RKMDIWindow*>(page)->close (true);
+	static_cast<RKMDIWindow*>(page)->close (RKMDIWindow::AutoAskSaveModified);
 }
 
 void RKWorkplaceViewPane::tabRemoved (int index) {




More information about the rkward-tracker mailing list