[education/rkward/devel/workspace_output] /: Handle rk.get.tempfile.name() inside the backend, and allow to use it with custom dir.

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


Git commit 69e2d08eeca55b5e54797b70af40f69b00dcdd0b by Thomas Friedrichsmeier.
Committed on 26/06/2017 at 09:25.
Pushed by tfry into branch 'devel/workspace_output'.

Handle rk.get.tempfile.name() inside the backend, and allow to use it with custom dir.

This is the first step in decoupling output files from other (temp) files.

While at it, use a common basis for some furthe calls that are also handled purely inside the backend.

M  +2    -0    ChangeLog
M  +0    -13   rkward/misc/rkcommonfunctions.cpp
M  +0    -2    rkward/misc/rkcommonfunctions.h
M  +1    -1    rkward/rbackend/rkfrontendtransmitter.cpp
M  +50   -30   rkward/rbackend/rkrbackend.cpp
M  +1    -4    rkward/rbackend/rkrinterface.cpp
M  +1    -0    rkward/rbackend/rpackages/rkward/NAMESPACE
M  +7    -3    rkward/rbackend/rpackages/rkward/R/internal.R
M  +5    -5    rkward/rbackend/rpackages/rkward/R/public_graphics.R
M  +24   -6    rkward/rbackend/rpackages/rkward/R/rk.filename-functions.R
M  +1    -1    rkward/rbackend/rpackages/rkward/R/rk.plugin-functions.R
M  +2    -2    rkward/rbackend/rpackages/rkward/R/rk.workspace-functions.R
M  +1    -1    rkward/rbackend/rpackages/rkward/man/rk.assign.preview.data.Rd
M  +15   -3    rkward/rbackend/rpackages/rkward/man/rk.get.tempfile.name.Rd
M  +1    -1    rkward/rbackend/rpackages/rkwardtests/R/rkwardtests-internal.R

https://invent.kde.org/education/rkward/commit/69e2d08eeca55b5e54797b70af40f69b00dcdd0b

diff --git a/ChangeLog b/ChangeLog
index e1b5a987..e30191d6 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,5 @@
+TODO: Check for duplicated factor levels in data editor, as these are no longer allowed in R
+
 - Implement "split view" feature, allowing to partion the main window, and to hvae several views of the same files / data side-by-side
 - Fixed: Creating trellis on-screen plots, while package lattice is not on the search path would produce errors in plot history mechanism
 - Limit the number of debug log-files to keep (at most three, each, for frontend and backend)
diff --git a/rkward/misc/rkcommonfunctions.cpp b/rkward/misc/rkcommonfunctions.cpp
index 09f7dccb..e12bd970 100644
--- a/rkward/misc/rkcommonfunctions.cpp
+++ b/rkward/misc/rkcommonfunctions.cpp
@@ -158,19 +158,6 @@ namespace RKCommonFunctions {
 		return (QStandardPaths::locate (QStandardPaths::GenericDataLocation, "rkward/resource.ver").replace ("resource.ver", QString ()));
 	}
 
-	QString getUseableRKWardSavefileName (const QString &prefix, const QString &postfix) {
-		QDir dir (RKSettingsModuleGeneral::filesPath ());
-
-		int i=0;
-		while (true) {
-			QString candidate = prefix + QString::number (i) + postfix;
-			if (!dir.exists (candidate)) {
-				return dir.filePath (candidate);
-			}
-			i++;
-		}
-	}
-
 	QString escape (const QString &in) {
 		QString out;
 
diff --git a/rkward/misc/rkcommonfunctions.h b/rkward/misc/rkcommonfunctions.h
index 34683fc9..cf48730b 100644
--- a/rkward/misc/rkcommonfunctions.h
+++ b/rkward/misc/rkcommonfunctions.h
@@ -40,8 +40,6 @@ namespace RKCommonFunctions {
 
 /** Get the base directory where RKWard data files are stored */
 	QString getRKWardDataDir ();
-/** Get a suitable file name in the RKWard data directory */
-	QString getUseableRKWardSavefileName (const QString &prefix, const QString &postfix);
 
 /** given a context line, find the end of a quote started by quote_char. @returns -1 if not end of quote was found. */
 	int quoteEndPosition (const QChar& quote_char, const QString& haystack, int from = 0);
diff --git a/rkward/rbackend/rkfrontendtransmitter.cpp b/rkward/rbackend/rkfrontendtransmitter.cpp
index cb41e532..71cbd81d 100644
--- a/rkward/rbackend/rkfrontendtransmitter.cpp
+++ b/rkward/rbackend/rkfrontendtransmitter.cpp
@@ -105,9 +105,9 @@ void RKFrontendTransmitter::run () {
 #ifdef Q_OS_MACOS
 	if (backend_executable.isNull ()) backend_executable = findBackendAtPath (QCoreApplication::applicationDirPath () + "../Resources"); // an appropriate location in a standalone app-bundle
 #endif
+	if (backend_executable.isEmpty ()) backend_executable = findBackendAtPath (QCoreApplication::applicationDirPath () + "/rbackend");	// for running directly from the build-dir
 	if (backend_executable.isEmpty ()) backend_executable = findBackendAtPath (RKWARD_BACKEND_PATH);
 	if (backend_executable.isNull ()) backend_executable = findBackendAtPath (QCoreApplication::applicationDirPath () + "../lib/libexec");
-	if (backend_executable.isEmpty ()) backend_executable = findBackendAtPath (QCoreApplication::applicationDirPath () + "/rbackend");	// for running directly from the build-dir
 #ifdef Q_OS_MACOS
         if (backend_executable.isEmpty ()) backend_executable = findBackendAtPath (QCoreApplication::applicationDirPath () + "/../../../rbackend");
 #endif
diff --git a/rkward/rbackend/rkrbackend.cpp b/rkward/rbackend/rkrbackend.cpp
index 548d8d1f..48512505 100644
--- a/rkward/rbackend/rkrbackend.cpp
+++ b/rkward/rbackend/rkrbackend.cpp
@@ -844,7 +844,7 @@ extern "C" int R_interrupts_pending;
 #else
 LibExtern int R_interrupts_pending;
 #endif
-SEXP doError (SEXP call) {
+void doError (QString callstring) {
 	RK_TRACE (RBACKEND);
 
 	if ((RKRBackend::repl_status.eval_depth == 0) && (!RKRBackend::repl_status.browser_context) && (!RKRBackend::this_pointer->isKilled ()) && (RKRBackend::repl_status.user_command_status != RKRBackend::RKReplStatus::ReplIterationKilled) && (!RKRBackend::repl_status.user_command_status == RKRBackend::RKReplStatus::NoUserCommand)) {
@@ -862,11 +862,9 @@ SEXP doError (SEXP call) {
 			}
 		}
 	} else if (RKRBackend::repl_status.user_command_status != RKRBackend::RKReplStatus::ReplIterationKilled) {
-		QString string = RKRSupport::SEXPToString (call);
-		RKRBackend::this_pointer->handleOutput (string, string.length (), ROutput::Error);
-		RK_DEBUG (RBACKEND, DL_DEBUG, "error '%s'", qPrintable (string));
+		RKRBackend::this_pointer->handleOutput (callstring, callstring.length (), ROutput::Error);
+		RK_DEBUG (RBACKEND, DL_DEBUG, "error '%s'", qPrintable (callstring));
 	}
-	return R_NilValue;
 }
 
 SEXP doSubstackCall (SEXP call) {
@@ -876,17 +874,7 @@ SEXP doSubstackCall (SEXP call) {
 
 	QStringList list = RKRSupport::SEXPToStringList (call);
 
-	// handle symbol updates inline
-	if (list.count () == 2) {		// schedule symbol update for later
-		if (list[0] == "ws") {
-			// always keep in mind: No current command can happen for tcl/tk events.
-			if ((!RKRBackend::this_pointer->current_command) || (RKRBackend::this_pointer->current_command->type & RCommand::ObjectListUpdate) || (!(RKRBackend::this_pointer->current_command->type & RCommand::Sync))) {		// ignore Sync commands that are not flagged as ObjectListUpdate
-				if (!RKRBackend::this_pointer->changed_symbol_names.contains (list[1])) RKRBackend::this_pointer->changed_symbol_names.append (list[1]);
-			}
-			return R_NilValue;
-		}
-	}
-/*	// this is a useful place to sneak in test code for profiling
+	/*	// this is a useful place to sneak in test code for profiling
 	if (list.value (0) == "testit") {
 		for (int i = 10000; i >= 1; --i) {
 			setWarnOption (i);
@@ -909,6 +897,51 @@ SEXP doPlainGenericRequest (SEXP call, SEXP synchronous) {
 	return RKRSupport::StringListToSEXP (ret);
 }
 
+// Function to handle several simple calls from R code, that do not need any special arguments, or interaction with the frontend process.
+SEXP doSimpleBackendCall (SEXP _call) {
+	RK_TRACE (RBACKEND);
+
+	QStringList list = RKRSupport::SEXPToStringList (_call);
+	QString call = list[0];
+
+	// symbol updates
+	if (list.count () == 2 && call == QStringLiteral ("ws")) {		// schedule symbol update for later
+		// always keep in mind: No current command can happen for tcl/tk events.
+		if ((!RKRBackend::this_pointer->current_command) || (RKRBackend::this_pointer->current_command->type & RCommand::ObjectListUpdate) || (!(RKRBackend::this_pointer->current_command->type & RCommand::Sync))) {		// ignore Sync commands that are not flagged as ObjectListUpdate
+			if (!RKRBackend::this_pointer->changed_symbol_names.contains (list[1])) RKRBackend::this_pointer->changed_symbol_names.append (list[1]);
+		}
+		return R_NilValue;
+	} else if (call == QStringLiteral ("unused.filename")) {
+		QString prefix = list.value (1);
+		QString extension = list.value (2);
+		QString dirs = list.value (3);
+		QDir  dir (dirs);
+		if (dirs.isEmpty ()) {
+			dir = QDir (RKRBackendProtocolBackend::dataDir ());
+		}
+
+		int i = 0;
+		while (true) {
+			QString candidate = prefix + QString::number (i) + extension;
+			if (!dir.exists (candidate)) {
+				return (RKRSupport::StringListToSEXP (QStringList (candidate) << dir.absoluteFilePath (candidate))); // return as c (relpath, abspath)
+			}
+			i++;
+		}
+	} else if (call == QStringLiteral ("error")) {  // capture error message
+		doError (list.value (1));
+		return R_NilValue;
+	} else if (call == QStringLiteral ("locale.name")) {
+		RK_ASSERT (QTextCodec::codecForLocale());
+		return (RKRSupport::StringListToSEXP (QStringList (QTextCodec::codecForLocale()->name ().data ())));
+	} else if (call == QStringLiteral ("tempdir")) {
+		return (RKRSupport::StringListToSEXP (QStringList (RKRBackendProtocolBackend::dataDir ())));
+	}
+
+	RK_ASSERT (false);  // Unhandled call.
+	return R_NilValue;
+}
+
 void R_CheckStackWrapper (void *) {
 	R_CheckStack ();
 }
@@ -930,18 +963,6 @@ SEXP doUpdateLocale () {
 	return R_NilValue;
 }
 
-// returns the MIME-name of the current locale encoding (from Qt)
-SEXP doLocaleName () {
-	RK_TRACE (RBACKEND);
-
-	RK_ASSERT (QTextCodec::codecForLocale());
-	SEXP res = Rf_allocVector(STRSXP, 1);
-	PROTECT (res);
-	SET_STRING_ELT (res, 0, Rf_mkChar (QTextCodec::codecForLocale()->name ().data ()));
-	UNPROTECT (1);
-	return res;
-}
-
 SEXP doGetStructure (SEXP toplevel, SEXP name, SEXP envlevel, SEXP namespacename) {
 	RK_TRACE (RBACKEND);
 
@@ -1043,7 +1064,7 @@ bool RKRBackend::startR () {
 
 // register our functions
 	R_CallMethodDef callMethods [] = {
-		{ "rk.do.error", (DL_FUNC) &doError, 1 },
+		{ "rk.simple", (DL_FUNC) &doSimpleBackendCall, 1},
 		{ "rk.do.command", (DL_FUNC) &doSubstackCall, 1 },
 		{ "rk.do.generic.request", (DL_FUNC) &doPlainGenericRequest, 2 },
 		{ "rk.get.structure", (DL_FUNC) &doGetStructure, 4 },
@@ -1053,7 +1074,6 @@ bool RKRBackend::startR () {
 		{ "rk.show.files", (DL_FUNC) &doShowFiles, 5 },
 		{ "rk.dialog", (DL_FUNC) &doDialog, 6 },
 		{ "rk.update.locale", (DL_FUNC) &doUpdateLocale, 0 },
-		{ "rk.locale.name", (DL_FUNC) &doLocaleName, 0 },
 		{ "rk.graphics.device", (DL_FUNC) &RKStartGraphicsDevice, 7},
 		{ "rk.graphics.device.resize", (DL_FUNC) &RKD_AdjustSize, 1},
 		{ 0, 0, 0 }
diff --git a/rkward/rbackend/rkrinterface.cpp b/rkward/rbackend/rkrinterface.cpp
index 7df0a10f..3bc4c296 100644
--- a/rkward/rbackend/rkrinterface.cpp
+++ b/rkward/rbackend/rkrinterface.cpp
@@ -554,10 +554,7 @@ QStringList RInterface::processPlainGenericRequest (const QStringList &calllist)
 	RK_TRACE (RBACKEND);
 
 	QString call = calllist.value (0);
-	if (call == "get.tempfile.name") {
-		RK_ASSERT (calllist.count () == 3);
-		return (QStringList (RKCommonFunctions::getUseableRKWardSavefileName (calllist.value (1), calllist.value (2))));
-	} else if (call == "set.output.file") {
+	if (call == "set.output.file") {
 		RK_ASSERT (calllist.count () == 2);
 		RKOutputWindowManager::self ()->setCurrentOutputPath (calllist.value (1));
 	} else if (call == "wdChange") {
diff --git a/rkward/rbackend/rpackages/rkward/NAMESPACE b/rkward/rbackend/rpackages/rkward/NAMESPACE
index 811ab90a..5b1e0595 100644
--- a/rkward/rbackend/rpackages/rkward/NAMESPACE
+++ b/rkward/rbackend/rpackages/rkward/NAMESPACE
@@ -116,6 +116,7 @@ export(rk.show.question)
 export(rk.switch.frontend.language)
 export(rk.sync)
 export(rk.sync.global)
+export(rk.tempdir)
 export(rk.toggle.plot.history)
 export(rk.verify.plot.hist.limits)
 export(rk.without.plot.history)
diff --git a/rkward/rbackend/rpackages/rkward/R/internal.R b/rkward/rbackend/rpackages/rkward/R/internal.R
index 82ce60a3..f08fe20a 100644
--- a/rkward/rbackend/rpackages/rkward/R/internal.R
+++ b/rkward/rbackend/rpackages/rkward/R/internal.R
@@ -117,7 +117,7 @@
 ".rk.do.error" <- function () {
 # comment in R sources says, it may not be good to query options during error handling. But what can we do, if R_ShowErrorMessages is not longer exported?
 	if (getOption ("show.error.messages")) {
-		.Call ("rk.do.error", c (geterrmessage ()), PACKAGE="(embedding)");
+		.rk.do.simple.call ("error", geterrmessage ())
 	}
 }
 
@@ -136,6 +136,10 @@
 	.Call ("rk.do.generic.request", c (x, args), isTRUE (synchronous), PACKAGE="(embedding)")
 }
 
+".rk.do.simple.call" <- function (x, args=NULL) {
+	.Call ("rk.simple", c (x, args), PACKAGE="(embedding)")
+}
+
 #' @export
 ".rk.find.package.pluginmaps" <- function (package, all.maps=FALSE) {
 	if(isTRUE(all.maps)){
@@ -241,9 +245,9 @@
 	function (value) {
 		if (!missing (value)) {
 			assign (k, value, envir=.rk.watched.symbols)
-			.Call ("rk.do.command", c ("ws", k), PACKAGE="(embedding)");
+			.Call ("rk.simple", c ("ws", k), PACKAGE="(embedding)");
 #			NOTE: the above is essentially the same a
-#				.rk.do.call ("ws", k);
+#				.rk.do.simple.call ("ws", k);
 #			only minimally faster.
 			invisible (value)
 		} else {
diff --git a/rkward/rbackend/rpackages/rkward/R/public_graphics.R b/rkward/rbackend/rpackages/rkward/R/public_graphics.R
index d7641881..d01e42f9 100644
--- a/rkward/rbackend/rpackages/rkward/R/public_graphics.R
+++ b/rkward/rbackend/rpackages/rkward/R/public_graphics.R
@@ -50,7 +50,7 @@
 			paste ("file:///", filename, sep="")
 		} else if (substr (filename, 1, 1) == "/") {
 			paste ("file://", filename, sep="")
-		} else { # relative path: return unchanged. NOTE that this will currently happen during automated tesing, only. Usually rk.get.tempfile.name() always returns absolute paths.
+		} else { # The common case: a relative path. Return unchanged.
 			filename
 		}
 	}
@@ -65,7 +65,7 @@
 	if (device.type == "PNG") {
 		filename <- rk.get.tempfile.name(prefix = "graph", extension = ".png")
 		ret <- png(filename = file.path(filename), width = width, height = height, ...)
-		.rk.cat.output(paste("<img src=\"", make.url (filename), "\" width=\"", width,
+		.rk.cat.output(paste("<img src=\"", make.url (names (filename)), "\" width=\"", width,
 			"\" height=\"", height, "\"><br>", sep = ""))
 	} else if (device.type == "JPG") {
 		if (missing (quality)) {
@@ -74,7 +74,7 @@
 		}
 		filename <- rk.get.tempfile.name(prefix = "graph", extension = ".jpg")
 		ret <- jpeg(filename = file.path(filename), width = width, height = height, "quality"=quality, ...)
-		.rk.cat.output(paste("<img src=\"", make.url (filename), "\" width=\"", width,
+		.rk.cat.output(paste("<img src=\"", make.url (names (filename)), "\" width=\"", width,
 			"\" height=\"", height, "\"><br>", sep = ""))
 	} else if (device.type == "SVG") {
 		if (!capabilities ("cairo")) {	# cairo support is not always compiled in
@@ -83,9 +83,9 @@
 		}
 		filename <- rk.get.tempfile.name(prefix = "graph", extension = ".svg")
 		ret <- svg(filename = file.path(filename), ...)
-		.rk.cat.output(paste("<object data=\"", make.url (filename), "\" type=\"image/svg+xml\" width=\"", width,
+		.rk.cat.output(paste("<object data=\"", make.url (names (filename)), "\" type=\"image/svg+xml\" width=\"", width,
 			"\" height=\"", height, "\">\n", sep = ""))
-		.rk.cat.output(paste("<param name=\"src\" value=\"", make.url (filename), "\">\n", sep = ""))
+		.rk.cat.output(paste("<param name=\"src\" value=\"", make.url (names (filename)), "\">\n", sep = ""))
 		.rk.cat.output(paste("This browser appears incapable of displaying SVG object. The SVG source is at:", filename))
 		.rk.cat.output("</object>")
 	} else {
diff --git a/rkward/rbackend/rpackages/rkward/R/rk.filename-functions.R b/rkward/rbackend/rpackages/rkward/R/rk.filename-functions.R
index 472dd8e0..a4763254 100644
--- a/rkward/rbackend/rpackages/rkward/R/rk.filename-functions.R
+++ b/rkward/rbackend/rpackages/rkward/R/rk.filename-functions.R
@@ -7,12 +7,18 @@
 #' the current (or specified) html file, and re-initialize it.
 #' 
 #' \code{rk.get.tempfile.name} returns a non-existing filename inside the
-#' directory of the output file. It is mainly used by \link{rk.graph.on} to
+#' directory of the output file. The filename is returned as an absolute path,
+#' but the relative path with respect to the base directory can be obtained via
+#' \code{names()}. It is mainly used by \link{rk.graph.on} to
 #' create filenames suitable for storing images in the output. The filenames of
 #' the temporary files are of the form
 #' "\code{prefix}\emph{xyz}.\code{extension}". \code{rk.get.tempfile.name} is
 #' somewhat misnamed. For truly temporary files, \link{tempfile} is generally
 #' more suitable.
+#'
+#' \code{rk.tempdir} returns a directory suitable for storing rkward related
+#'   temporary files. Almost the same as \code{tempdir()}, but the directory
+#'   returned will be inside the configured RKWard data path  ("$HOME/.rkward", by default).
 #' 
 #' \code{rk.get.workspace.url} returns the url of workspace file which has been
 #' loaded in RKWard, or NULL, if no workspace has been loaded. NOTE: This value
@@ -20,11 +26,15 @@
 #' via the RKWard GUI.
 #' 
 #' @aliases rk.get.tempfile.name rk.get.workspace.url rk.get.output.html.file
-#'   rk.set.output.html.file
+#'   rk.set.output.html.file rk.tempdir
 #' @param prefix a string, used as a filename prefix when saving images to the
-#'   output file
+#'   output file. This is usually just a plain file name, but can also be a relative or absolute
+#'   path. Relative paths are resolved with the default output directory as base, absolute paths
+#'   are kept as is.
 #' @param extension a string, used as a filename extension when saving images
 #'   to the output file
+#' @param directory a string, The base directory for the file. If left empty, this will default to the
+#'   write directory of the current output file (usually "~.rkward)
 #' @param x a string, giving the filename of the of the output file
 #' @param additional.header.contents NULL or an additional string to add to the HTML header section.
 #'        This could be scripts or additional CSS definitions, for example. Note that
@@ -62,8 +72,16 @@
 #' rk.set.output.html.file(outfile)
 #' 
 #' @export
-"rk.get.tempfile.name" <- function (prefix="image", extension=".jpg") {
-	return (.rk.do.plain.call ("get.tempfile.name", c (prefix, extension)))
+"rk.get.tempfile.name" <- function (prefix="image", extension=".jpg", directory="") {
+	x <- .rk.do.simple.call ("unused.filename", c (prefix, extension, directory))
+	ret <- x[2]
+	names (ret) <- x[1]
+	ret
+}
+
+#' @export
+"rk.tempdir" <- function () {
+	.rk.do.simple.call ("tempdir")
 }
 
 #' @export
@@ -89,7 +107,7 @@
 	assign (".rk.output.html.file", x, .rk.variables)
 
 	if (!file.exists (x)) {
-		.rk.cat.output (paste ("<?xml version=\"1.0\" encoding=\"", .Call ("rk.locale.name", PACKAGE="(embedding)"), "\"?>\n", sep=""))
+		.rk.cat.output (paste ("<?xml version=\"1.0\" encoding=\"", .rk.do.simple.call ("locale.name"), "\"?>\n", sep=""))
 		.rk.cat.output ("<html><head>\n<title>RKWard Output</title>\n")
 		if (!is.null (css)) {
 			cssfilename <- paste (sub ("\\.[^.]*$", "", basename (x)), ".css", sep="")
diff --git a/rkward/rbackend/rpackages/rkward/R/rk.plugin-functions.R b/rkward/rbackend/rpackages/rkward/R/rk.plugin-functions.R
index f55ce0c3..191612b8 100644
--- a/rkward/rbackend/rpackages/rkward/R/rk.plugin-functions.R
+++ b/rkward/rbackend/rpackages/rkward/R/rk.plugin-functions.R
@@ -214,7 +214,7 @@ assign(".rk.preview.data", list (), envir=.rk.variables)
 #' ## NOT RUN
 #' pdata <- rk.get.preview.data("SOMEID")
 #' if (is.null (pdata)) {
-#'   outfile <- rk.get.tempfile.name(prefix="preview", extension=".txt")
+#'   outfile <- rk.get.tempfile.name(prefix="preview", extension=".txt", directory=rk.tempdir ())
 #'   pdata <- list(filename=outfile, on.delete=function (id) {
 #'     unlink(rk.get.preview.data(id)$filename)
 #'   })
diff --git a/rkward/rbackend/rpackages/rkward/R/rk.workspace-functions.R b/rkward/rbackend/rpackages/rkward/R/rk.workspace-functions.R
index 31f42785..bfe60bcb 100644
--- a/rkward/rbackend/rpackages/rkward/R/rk.workspace-functions.R
+++ b/rkward/rbackend/rpackages/rkward/R/rk.workspace-functions.R
@@ -39,7 +39,7 @@
 "rk.save.workplace" <- function (file=NULL, description=NULL) {
 	if (is.null (file)) {
 		file <- URLdecode (rk.get.workspace.url ())
-		if (is.null (file)) file <- rk.get.tempfile.name (prefix="unsaved", extension=".RData")
+		if (is.null (file)) file <- rk.get.tempfile.name (prefix="unsaved", extension=".RData", directory=rk.tempdir ())
 		file <- paste (file, "rkworkplace", sep=".")
 	}
 	if (is.null (description)) lines <- .rk.do.plain.call ("workplace.layout", "get")
@@ -58,7 +58,7 @@
 			rm (list = c (".rk.workplace.save"), envir=globalenv ())
 		} else {
 			file <- URLdecode (rk.get.workspace.url ())
-			if (is.null (file)) file <- rk.get.tempfile.name (prefix="unsaved", extension=".RData")
+			if (is.null (file)) file <- rk.get.tempfile.name (prefix="unsaved", extension=".RData", directory=rk.tempdir ())
 			file <- paste (file, "rkworkplace", sep=".")
 		}
 	}
diff --git a/rkward/rbackend/rpackages/rkward/man/rk.assign.preview.data.Rd b/rkward/rbackend/rpackages/rkward/man/rk.assign.preview.data.Rd
index 0d306c0e..1987e5dd 100644
--- a/rkward/rbackend/rpackages/rkward/man/rk.assign.preview.data.Rd
+++ b/rkward/rbackend/rpackages/rkward/man/rk.assign.preview.data.Rd
@@ -42,7 +42,7 @@ code, such as removing temporary files, etc.}
 ## NOT RUN
 pdata <- rk.get.preview.data("SOMEID")
 if (is.null (pdata)) {
-  outfile <- rk.get.tempfile.name(prefix="preview", extension=".txt")
+  outfile <- rk.get.tempfile.name(prefix="preview", extension=".txt", directory=rk.tempdir ())
   pdata <- list(filename=outfile, on.delete=function (id) {
     unlink(rk.get.preview.data(id)$filename)
   })
diff --git a/rkward/rbackend/rpackages/rkward/man/rk.get.tempfile.name.Rd b/rkward/rbackend/rpackages/rkward/man/rk.get.tempfile.name.Rd
index 91fc8fc4..2d61db2c 100644
--- a/rkward/rbackend/rpackages/rkward/man/rk.get.tempfile.name.Rd
+++ b/rkward/rbackend/rpackages/rkward/man/rk.get.tempfile.name.Rd
@@ -6,9 +6,10 @@
 \alias{rk.get.tempfile.name}
 \alias{rk.get.workspace.url}
 \alias{rk.set.output.html.file}
+\alias{rk.tempdir}
 \title{RKWard file names}
 \usage{
-rk.get.tempfile.name(prefix = "image", extension = ".jpg")
+rk.get.tempfile.name(prefix = "image", extension = ".jpg", directory = "")
 
 rk.get.workspace.url()
 
@@ -23,11 +24,16 @@ rk.flush.output(x = rk.get.output.html.file(), flush.images = TRUE,
 }
 \arguments{
 \item{prefix}{a string, used as a filename prefix when saving images to the
-output file}
+output file. This is usually just a plain file name, but can also be a relative or absolute
+path. Relative paths are resolved with the default output directory as base, absolute paths
+are kept as is.}
 
 \item{extension}{a string, used as a filename extension when saving images
 to the output file}
 
+\item{directory}{a string, The base directory for the file. If left empty, this will default to the
+write directory of the current output file (usually "~.rkward)}
+
 \item{x}{a string, giving the filename of the of the output file}
 
 \item{additional.header.contents}{NULL or an additional string to add to the HTML header section.
@@ -60,13 +66,19 @@ the current (or specified) html file, and re-initialize it.
 }
 \details{
 \code{rk.get.tempfile.name} returns a non-existing filename inside the
-directory of the output file. It is mainly used by \link{rk.graph.on} to
+directory of the output file. The filename is returned as an absolute path,
+but the relative path with respect to the base directory can be obtained via
+\code{names()}. It is mainly used by \link{rk.graph.on} to
 create filenames suitable for storing images in the output. The filenames of
 the temporary files are of the form
 "\code{prefix}\emph{xyz}.\code{extension}". \code{rk.get.tempfile.name} is
 somewhat misnamed. For truly temporary files, \link{tempfile} is generally
 more suitable.
 
+\code{rk.tempdir} returns a directory suitable for storing rkward related
+  temporary files. Almost the same as \code{tempdir()}, but the directory
+  returned will be inside the configured RKWard data path  ("$HOME/.rkward", by default).
+
 \code{rk.get.workspace.url} returns the url of workspace file which has been
 loaded in RKWard, or NULL, if no workspace has been loaded. NOTE: This value
 is note affected by running \code{load} in R, only by loading R workspaces
diff --git a/rkward/rbackend/rpackages/rkwardtests/R/rkwardtests-internal.R b/rkward/rbackend/rpackages/rkwardtests/R/rkwardtests-internal.R
index 153d179c..9b9f2f4e 100644
--- a/rkward/rbackend/rpackages/rkwardtests/R/rkwardtests-internal.R
+++ b/rkward/rbackend/rpackages/rkwardtests/R/rkwardtests-internal.R
@@ -232,7 +232,7 @@ rktest.initializeEnvironment <- function () {
 	rktest.replace (".rk.make.hr", function (...) list (...))
 
 	# This should make the output of rk.graph.on() fixed
-	rktest.replace ("rk.get.tempfile.name", function (prefix="image", extension=".jpg") paste (prefix, extension, sep=""))
+	rktest.replace ("rk.get.tempfile.name", function (prefix="image", extension=".jpg", directory="") paste (prefix, extension, directory, sep=""))
 	options (rk.graphics.type="PNG", rk.graphics.width=480, rk.graphics.height=480)
 
 	# HACK: Override date, so we don't get a difference for each call of rk.header ()




More information about the rkward-tracker mailing list