[education/rkward/devel/workspace_output] /: Restore output windows

Thomas Friedrichsmeier null at kde.org
Sat Feb 19 14:22:06 GMT 2022


Git commit 563e70b547d0aa8694196f25271c51dab7b7aa2c by Thomas Friedrichsmeier.
Committed on 19/02/2022 at 14:21.
Pushed by tfry into branch 'devel/workspace_output'.

Restore output windows

M  +3    -0    ChangeLog
M  +5    -4    rkward/dialogs/rksavemodifieddialog.cpp
M  +46   -34   rkward/misc/rkoutputdirectory.cpp
M  +15   -3    rkward/misc/rkoutputdirectory.h
M  +1    -0    rkward/rkward.cpp
M  +17   -11   rkward/windows/rkworkplace.cpp

https://invent.kde.org/education/rkward/commit/563e70b547d0aa8694196f25271c51dab7b7aa2c

diff --git a/ChangeLog b/ChangeLog
index 1ee31fb3..df562e9f 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -5,6 +5,9 @@ TODOS for autotests:
 TODOS for output directories:
   - save outputs to .rkworkplace and restore them (create default output, if there is none)
   - UI
+  - migration support (existing old standard output should be converted to "default.rko"; setting, when opening workspace with no attached active output file:
+    - create new empty file
+    - open "default.rko" <- default setting?)
 
 - rkwardtests library gains helper functions for checking for expected errors
 - Internal: Allow R-level calls to support both subcommands, and a return value at the same time
diff --git a/rkward/dialogs/rksavemodifieddialog.cpp b/rkward/dialogs/rksavemodifieddialog.cpp
index 50331462..b28b9786 100644
--- a/rkward/dialogs/rksavemodifieddialog.cpp
+++ b/rkward/dialogs/rksavemodifieddialog.cpp
@@ -153,10 +153,6 @@ void RKSaveModifiedDialog::saveSelected() {
 		if (!it.value ()->save ()) all_ok = false; // but we proceed with the others
 	}
 
-	if (save_project_check && save_project_check->checkState(0) == Qt::Checked) {
-		if (!RKSaveAgent::saveWorkspace()) all_ok = false;
-	}
-
 	for (auto it = outputdir_checklist.constBegin(); it != outputdir_checklist.constEnd(); ++it) {
 		if (it.key ()->checkState (0) != Qt::Checked) continue;
 		RKOutputDirectory *dir = RKOutputDirectory::getOutputById(it.value());
@@ -166,6 +162,11 @@ void RKSaveModifiedDialog::saveSelected() {
 		else RK_ASSERT(dir);
 	}
 
+	// Save workspace (+ workplace!) last, as some urls may still have changed, above.
+	if (save_project_check && save_project_check->checkState(0) == Qt::Checked) {
+		if (!RKSaveAgent::saveWorkspace()) all_ok = false;
+	}
+
 	if (all_ok) accept();
 	else reject();
 }
diff --git a/rkward/misc/rkoutputdirectory.cpp b/rkward/misc/rkoutputdirectory.cpp
index a28d6f89..13f587c9 100644
--- a/rkward/misc/rkoutputdirectory.cpp
+++ b/rkward/misc/rkoutputdirectory.cpp
@@ -63,6 +63,11 @@ QString hashDirectoryState(const QString& dir) {
 //	return QCryptographicHash::hash (list.toUtf8 (), QCryptographicHash::Md5);
 }
 
+void RKOutputDirectoryCallResult::setDir(RKOutputDirectory *d) {
+	_dir = d;
+	if (d) ret = d->getId();
+}
+
 QMap<QString, RKOutputDirectory*> RKOutputDirectory::outputs;
 
 RKOutputDirectory::RKOutputDirectory() : initialized(false) {
@@ -339,14 +344,12 @@ GenericRRequestResult RKOutputDirectory::purge(RKOutputDirectory::OverwriteBehav
 		}
 	}
 
-	bool activate = activate_other && isActive();
 	QDir dir(work_dir);
 	dir.removeRecursively();
 	outputs.remove(id);
 	deleteLater();
-	GenericRRequestResult messages;
-	if (activate) getCurrentOutput(chain, &messages);
-	return messages;
+	if (activate_other) return getCurrentOutput(chain);
+	return GenericRRequestResult();
 }
 
 void RKOutputDirectory::purgeAllNoAsk() {
@@ -398,27 +401,33 @@ QList<RKOutputDirectory *> RKOutputDirectory::allOutputs() {
 	return ret;
 }
 
-RKOutputDirectory* RKOutputDirectory::getCurrentOutput(RCommandChain* chain, GenericRRequestResult* message_res) {
+RKOutputDirectoryCallResult RKOutputDirectory::getCurrentOutput(RCommandChain* chain) {
 	RK_TRACE(APP);
 
+	RKOutputDirectoryCallResult ret;
 	if (outputs.isEmpty()) {
 		auto n = createOutputDirectoryInternal();
 		n->activate(chain);
-		if (message_res) message_res->addMessages(GenericRRequestResult(QVariant(), i18n("New empty output directory has been created, automatically")));
-		return n;
+		ret.addMessages(GenericRRequestResult(QVariant(), i18n("New empty output directory has been created, automatically")));
+		ret.setDir(n);
+		return ret;
 	}
 
 	RKOutputDirectory* candidate = nullptr;
 	for (auto it = outputs.constBegin(); it != outputs.constEnd(); ++it) {
-		if (it.value()->isActive()) return it.value();
+		if (it.value()->isActive()) {
+			ret.setDir(it.value());
+			return ret;
+		}
 		if (it.value()->filename().isEmpty()) candidate = it.value();
 	}
 
 	if (!candidate) candidate = outputs[0];
 	RK_ASSERT(candidate);
 	candidate->activate(chain);
-	if (message_res) message_res->addMessages(GenericRRequestResult(QVariant(), i18n("Output has been activated, automatically")));
-	return candidate;
+	ret.addMessages(GenericRRequestResult(QVariant(), i18n("Output has been activated, automatically")));
+	ret.setDir(candidate);
+	return ret;
 }
 
 RKOutputDirectory::OverwriteBehavior parseOverwrite(const QString &param) {
@@ -442,6 +451,32 @@ GenericRRequestResult RKOutputDirectory::view(bool raise) {
 	return GenericRRequestResult(id);
 }
 
+RKOutputDirectoryCallResult RKOutputDirectory::get(const QString &_filename, bool create, RCommandChain *chain) {
+	RKOutputDirectoryCallResult ret;
+	if (_filename.isEmpty()) {
+		if (create) {
+			ret.setDir(createOutputDirectoryInternal());
+		} else {
+			return (getCurrentOutput(chain));
+		}
+	} else {
+		QString filename = QFileInfo(_filename).canonicalFilePath();
+		ret.setDir(getOutputBySaveUrl(filename));
+		if (create) {
+			if (ret.dir()) return GenericRRequestResult::makeError(i18n("Output '1%' is already loaded in this session. Cannot create it.", filename));
+			if (QFileInfo(filename).exists()) return GenericRRequestResult::makeError(i18n("A file named '1%' already exists. Cannot create it.", filename));
+			ret.setDir(createOutputDirectoryInternal());
+			ret.addMessages(ret.dir()->import(filename));
+		} else {
+			if (!ret.dir()) {
+				ret.setDir(createOutputDirectoryInternal());
+				ret.addMessages(ret.dir()->import(filename));
+			}
+		}
+	}
+	return ret;
+}
+
 GenericRRequestResult RKOutputDirectory::handleRCall(const QStringList& params, RCommandChain *chain) {
 	RK_TRACE(APP);
 
@@ -458,30 +493,7 @@ GenericRRequestResult RKOutputDirectory::handleRCall(const QStringList& params,
 
 		QString filename = params.value(3);
 		bool create = (params.value(2) == QStringLiteral("create"));
-		RKOutputDirectory *out = nullptr;
-		GenericRRequestResult messages;
-		if (filename.isEmpty()) {
-			if (create) {
-				out = createOutputDirectoryInternal();
-			} else {
-				out = getCurrentOutput(chain, &messages);
-			}
-		} else {
-			filename = QFileInfo(filename).canonicalFilePath();
-			out = getOutputBySaveUrl(filename);
-			if (create) {
-				if (out) return GenericRRequestResult::makeError(i18n("Output '1%' is already loaded in this session. Cannot create it.", filename));
-				if (QFileInfo(filename).exists()) return GenericRRequestResult::makeError(i18n("As file named '1%' already exists. Cannot create it.", filename));
-				out = createOutputDirectoryInternal();
-				return (out->import(filename));
-			} else {
-				if (!out) {
-					out = createOutputDirectoryInternal();
-					return (out->import(filename));
-				}
-			}
-		}
-		return GenericRRequestResult(out->getId()).addMessages(messages);
+		return get(filename, create);
 	} else {
 		// all other commands pass the output id as second parameter. Look that up, first
 		QString id = params.value(1);
diff --git a/rkward/misc/rkoutputdirectory.h b/rkward/misc/rkoutputdirectory.h
index 82224191..9c27c16d 100644
--- a/rkward/misc/rkoutputdirectory.h
+++ b/rkward/misc/rkoutputdirectory.h
@@ -29,6 +29,18 @@
 
 class RKMDIWindow;
 class RCommandChain;
+class RKOutputDirectory;
+
+// convenience struct to avoid defining separate functions for R API and C++ API;
+// this struct encapsulates the relevant results for both
+struct RKOutputDirectoryCallResult : public GenericRRequestResult {
+	RKOutputDirectoryCallResult() : GenericRRequestResult(), _dir(nullptr) {};
+	RKOutputDirectoryCallResult(const GenericRRequestResult &other) : GenericRRequestResult(other), _dir(nullptr) {};
+	void setDir(RKOutputDirectory *d);
+	RKOutputDirectory* dir() const { return _dir; }
+private:
+	RKOutputDirectory* _dir;
+};
 
 class RKOutputDirectory : public QObject {
 	Q_OBJECT
@@ -60,12 +72,12 @@ public:
 /** Return a list of all current output directories that have been modified. Used for asking for save during shutdown. */
 	static QList<RKOutputDirectory*> modifiedOutputDirectories();
 
-	static GenericRRequestResult R_rk_Output(const QString& filename=QString(), bool create=false, bool all=false);
 /** Returns the active output (in case there is one).
- *  If no output is active, find an activate the next output without a save url.
+ *  If no output is active, find and activate the next output without a save url.
  *  If that does not exist, activate and return the next existing output.
  *  If that does not exist, create a new output, activate and return it. */
-	static RKOutputDirectory* getCurrentOutput(RCommandChain *chain=0, GenericRRequestResult* message_res=0);
+	static RKOutputDirectoryCallResult getCurrentOutput(RCommandChain *chain=0);
+	static RKOutputDirectoryCallResult get(const QString &filename=QString(), bool create=false, RCommandChain *chain=0);
 	static QList<RKOutputDirectory*> allOutputs();
 	static void purgeAllNoAsk();
 private:
diff --git a/rkward/rkward.cpp b/rkward/rkward.cpp
index 590db5ab..72e39da2 100644
--- a/rkward/rkward.cpp
+++ b/rkward/rkward.cpp
@@ -783,6 +783,7 @@ bool RKWardMainWindow::doQueryQuit () {
 
 	slotSetStatusBarText (i18n ("Exiting..."));
 	saveOptions ();
+	// TODO: This may not be correct, some urls may still change while closing Workspace (due to save as)!
 	if (RKSettingsModuleGeneral::workplaceSaveMode () == RKSettingsModuleGeneral::SaveWorkplaceWithSession) {
 		RKSettingsModuleGeneral::setSavedWorkplace (RKWorkplace::mainWorkplace ()->makeWorkplaceDescription ().join ("\n"), KSharedConfig::openConfig ().data ());
 	}
diff --git a/rkward/windows/rkworkplace.cpp b/rkward/windows/rkworkplace.cpp
index a8995a63..4eaaa965 100644
--- a/rkward/windows/rkworkplace.cpp
+++ b/rkward/windows/rkworkplace.cpp
@@ -507,20 +507,20 @@ RKMDIWindow* RKWorkplace::openHelpWindow (const QUrl &url, bool only_once) {
 	return (hw);
 }
 
-RKMDIWindow* RKWorkplace::openOutputWindow (const QUrl &url) {
+RKMDIWindow* RKWorkplace::openOutputWindow(const QUrl &url) {
 	RK_TRACE (APP);
 
-	QList<RKHTMLWindow*> owins = RKOutputWindowManager::self ()->existingOutputWindows ();
+	QList<RKHTMLWindow*> owins = RKOutputWindowManager::self()->existingOutputWindows(url.toLocalFile());
 	for (int i = 0; i < owins.size (); ++i) {
-		if (view ()->windowInActivePane (owins[i])) {
-			owins[i]->activate ();
-			return (owins[i]);
+		if (view()->windowInActivePane(owins[i])) {
+			owins[i]->activate();
+			return owins[i];
 		}
 	}
 
-	RKHTMLWindow* ret = RKOutputWindowManager::self ()->newOutputWindow ();
-	addWindow (ret);
-	return (ret);
+	RKHTMLWindow* ret = RKOutputWindowManager::self()->newOutputWindow(url.toLocalFile());
+	addWindow(ret);
+	return ret;
 }
 
 void RKWorkplace::newX11Window (QWindow* window_to_embed, int device_number) {
@@ -803,7 +803,8 @@ QString RKWorkplace::makeItemDescription (RKMDIWindow *win) const {
 		RKOutputDirectory *dir = RKOutputDirectory::getOutputByWindow(win);
 		if (dir) {
 			type = "rkoutput";
-			specification = dir->filename();
+			specification = QUrl::fromLocalFile(dir->filename()).url();
+			if (dir->isActive()) type.append(QStringLiteral(".active"));
 		} else {
 			// legacy support for rk.set.html.output.file()
 			type = "output";
@@ -873,7 +874,7 @@ ItemSpecification parseItemDescription (const QString &description) {
 RKMDIWindow* restoreDocumentWindowInternal (RKWorkplace* wp, ItemSpecification spec, const QString &base) {
 	RK_TRACE (APP);
 
-	RKMDIWindow *win = 0;
+	RKMDIWindow *win = nullptr;
 	if (spec.type == "data") {
 		RObject *object = RObjectList::getObjectList ()->findObject (spec.specification);
 		if (object) win = wp->editObject (object);
@@ -881,7 +882,12 @@ RKMDIWindow* restoreDocumentWindowInternal (RKWorkplace* wp, ItemSpecification s
 		QUrl url = checkAdjustRestoredUrl (spec.specification, base);
 		win = wp->openScriptEditor (url, QString ());
 	} else if (spec.type == "output") {
-		win = wp->openOutputWindow (checkAdjustRestoredUrl (spec.specification, base));
+		win = wp->openOutputWindow (checkAdjustRestoredUrl(spec.specification, base));
+	} else if (spec.type.startsWith("rkoutput")) {
+		RKOutputDirectory *dir = RKOutputDirectory::get(checkAdjustRestoredUrl(spec.specification, base).toLocalFile(), false).dir();
+		if (!dir) return nullptr;
+		if (spec.type.endsWith(".active")) dir->activate();
+		win = RKWorkplace::mainWorkplace()->openOutputWindow(QUrl::fromLocalFile(dir->workPath()));
 	} else if (spec.type == "help") {
 		win = wp->openHelpWindow (checkAdjustRestoredUrl (spec.specification, base), true);
 	} else if (spec.type == "object") {


More information about the rkward-tracker mailing list