[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 ¶m) {
@@ -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