[rkward-cvs] SF.net SVN: rkward:[2510] trunk/rkward

tfry at users.sourceforge.net tfry at users.sourceforge.net
Fri May 29 15:49:10 UTC 2009


Revision: 2510
          http://rkward.svn.sourceforge.net/rkward/?rev=2510&view=rev
Author:   tfry
Date:     2009-05-29 15:49:10 +0000 (Fri, 29 May 2009)

Log Message:
-----------
make RCallbackArgs less cryptic

Modified Paths:
--------------
    trunk/rkward/ChangeLog
    trunk/rkward/rkward/agents/showedittextfileagent.cpp
    trunk/rkward/rkward/rbackend/rembedinternal.cpp
    trunk/rkward/rkward/rbackend/rembedinternal.h
    trunk/rkward/rkward/rbackend/rinterface.cpp

Modified: trunk/rkward/ChangeLog
===================================================================
--- trunk/rkward/ChangeLog	2009-05-29 14:53:56 UTC (rev 2509)
+++ trunk/rkward/ChangeLog	2009-05-29 15:49:10 UTC (rev 2510)
@@ -1,4 +1,4 @@
-- Remove support for R 2.6.x and earlier		TODO: clean up more (browser, internal.R, RCallbackArgs, RData?), adjust debian control
+- Remove support for R 2.6.x and earlier		TODO: clean up more (options ("browser"), internal.R, RData?), adjust debian control
 - Add basic checks for a correct installation of the RKWard resource files
 - Remove "What to expect" dialog at startup
 - Make keyboard shorcuts configurable

Modified: trunk/rkward/rkward/agents/showedittextfileagent.cpp
===================================================================
--- trunk/rkward/rkward/agents/showedittextfileagent.cpp	2009-05-29 14:53:56 UTC (rev 2509)
+++ trunk/rkward/rkward/agents/showedittextfileagent.cpp	2009-05-29 15:49:10 UTC (rev 2510)
@@ -77,31 +77,37 @@
 	QString message_snip1 = i18n (" For that reason processing has been stopped for now. Press the \"Done\"-button, or close this dialog once you think it is safe to resume.\n\n");
 	QString message_snip2 = i18n ("The file(s) have been opened in text-windows. The following is a list of the file(s) in question:\n\n");
 
+	QStringList files = args->params["files"].toStringList ();
+	QStringList titles = args->params["titles"].toStringList ();
+	int count = files.count ();
+	RK_ASSERT (titles.count () == count);
+
 	QString bad_files_list;
 	if (args->type == RCallbackArgs::RShowFiles) {
+
 		caption = i18n ("Showing file(s)");
 		message = i18n ("A command running in the R-engine wants you to see one or more file(s). RKWard can not determine, whether it is safe to continue processing R commands, before you have read the file(s) in question.") + message_snip1 + message_snip2;
 
-		for (int n = 0; n < args->int_a; ++n) {
-			message.append (args->chars_a[n]).append (" (\"").append (args->chars_b[n]).append ("\")\n");
+		for (int n = 0; n < count; ++n) {
+			message.append (files[n]).append (" (\"").append (titles[n]).append ("\")\n");
 
-			bool ok = RKWorkplace::mainWorkplace ()->openScriptEditor (KUrl::fromPath (args->chars_a[n]), false, true, QString (*(args->chars_c)));
+			bool ok = RKWorkplace::mainWorkplace ()->openScriptEditor (KUrl::fromPath (files[n]), false, true, args->params["wtitle"].toString ());
 
 			if (!ok)  {
-				bad_files_list.append ("- ").append (args->chars_a[n]).append (" (\"").append (args->chars_b[n]).append ("\")\n");
+				bad_files_list.append ("- ").append (files[n]).append (" (\"").append (titles[n]).append ("\")\n");
 			}
 		}
 	} else if (args->type == RCallbackArgs::REditFiles) {
 		caption = i18n ("Edit file(s)");
 		message = i18n ("A command running in the R-engine wants you to edit one or more file(s). RKWard can not determine, whether it is safe to continue processing R commands, before you have read/edited (and saved) the file(s) in question.") + message_snip1 + message_snip2;
 
-		for (int n = 0; n < args->int_a; ++n) {
-			message.append (args->chars_a[n]).append (" (\"").append (args->chars_b[n]).append ("\")\n");
+		for (int n = 0; n < count; ++n) {
+			message.append (files[n]).append (" (\"").append (titles[n]).append ("\")\n");
 
-			bool ok = RKWorkplace::mainWorkplace ()->openScriptEditor (KUrl::fromPath (args->chars_a[n]), true, false, QString (args->chars_b[n]));
+			bool ok = RKWorkplace::mainWorkplace ()->openScriptEditor (KUrl::fromPath (files[n]), true, false, QString (titles[n]));
 
 			if (!ok) {
-				bad_files_list.append ("- ").append (args->chars_a[n]).append (" (\"").append (args->chars_b[n]).append ("\")\n");
+				bad_files_list.append ("- ").append (files[n]).append (" (\"").append (titles[n]).append ("\")\n");
 			}
 		}
 	}
@@ -119,10 +125,10 @@
 	RK_TRACE (APP);
 	dialog->deleteLater ();
 
-	// int_b in RShowFiles means files are to be deleted
-	if ((args->type == RCallbackArgs::RShowFiles) && args->int_b) {
-		for (int n = 0; n < args->int_a; ++n) {
-			RK_ASSERT (QFile::remove (QString (args->chars_a[n])));
+	QStringList files = args->params["files"].toStringList ();
+	if ((args->type == RCallbackArgs::RShowFiles) && args->params["delete"].toBool ()) {
+		for (int n = 0; n < files.count (); ++n) {
+			RK_ASSERT (QFile::remove (QString (files[n])));
 		}
 	}
 

Modified: trunk/rkward/rkward/rbackend/rembedinternal.cpp
===================================================================
--- trunk/rkward/rkward/rbackend/rembedinternal.cpp	2009-05-29 14:53:56 UTC (rev 2509)
+++ trunk/rkward/rkward/rbackend/rembedinternal.cpp	2009-05-29 15:49:10 UTC (rev 2510)
@@ -20,15 +20,12 @@
 // static
 REmbedInternal *REmbedInternal::this_pointer = 0; 
 
-#define TRUE true
-#define FALSE false
 #include <qstring.h>
 #include <QStringList>
 #include <qtextcodec.h>
+#include <klocale.h>
 #include "../core/robject.h"
 #include "../debug.h"
-#undef TRUE
-#undef FALSE
 
 #include "rklocalesupport.h"
 #include "rkpthreadsupport.h"
@@ -118,8 +115,8 @@
 	RK_TRACE (RBACKEND);
 
 	RCallbackArgs args;
-	args.type = RCallbackArgs::RSuicide;
-	args.chars_a = const_cast<char**>(&message);
+	args.type = RCallbackArgs::RBackendExit;
+	args.params["message"] = QVariant (i18n ("The R engine has encountered a fatal error:\n%1").arg (message));
 	REmbedInternal::this_pointer->handleStandardCallback (&args);
 	REmbedInternal::this_pointer->shutdown (true);
 	Rf_error ("Backend dead");	// this jumps us out of the REPL.
@@ -130,7 +127,7 @@
 
 	RCallbackArgs args;
 	args.type = RCallbackArgs::RShowMessage;
-	args.chars_a = const_cast<char**>(&message);
+	args.params["message"] = QVariant (message);
 	REmbedInternal::this_pointer->handleStandardCallback (&args);
 }
 
@@ -160,18 +157,16 @@
 		return 1;
 	}
 
-	// here, we handle readline calls and such, i.e. not the regular prompt for code
+	// here, we handle readline() calls and such, i.e. not the regular prompt for code
+	// browser() also takes us here.
 	RCallbackArgs args;
-	args.type = RCallbackArgs::RReadConsole;
-	args.chars_a = const_cast<char**> (&prompt);
-	args.chars_b = (char **) (&buf);
-	args.int_a = buflen;
-	args.int_b = hist;		// actually, we ignore hist
-	args.int_c = 1;			// not cancelled
+	args.type = RCallbackArgs::RReadLine;
+	args.params["prompt"] = QVariant (prompt);
+	args.params["cancelled"] = QVariant (false);
 
 	REmbedInternal::this_pointer->handleStandardCallback (&args);
 // default implementation seems to return 1 on success, 0 on failure, contrary to some documentation. see unix/std-sys.c
-	if (!(args.int_c)) {
+	if (args.params["cancelled"].toBool ()) {
 #warning TODO: this should be handled in rthread.cpp, instead
 		REmbedInternal::this_pointer->currentCommandWasCancelled ();
 #ifdef Q_WS_WIN
@@ -180,8 +175,14 @@
 #else
 		Rf_onintr ();
 #endif
+		return 0;	// we should not ever get here, but still...
 	}
-	if (buf && args.int_c) return 1;
+	if (buf) {
+		QByteArray localres = REmbedInternal::this_pointer->current_locale_codec->fromUnicode (args.params["result"].toString ());
+		// need to append a newline, here. TODO: theoretically, RReadConsole comes back for more, if \0 was encountered before \n.
+		qstrncpy ((char *) buf, localres.left (buflen - 2).append ('\n').data (), buflen);
+		return 1;
+	}
 	return 0;
 }
 
@@ -201,9 +202,8 @@
 
 	if (saveact != SA_SUICIDE) {
 		RCallbackArgs args;
-		args.type = RCallbackArgs::RCleanUp;
-		args.int_a = status;
-		args.int_b = RunLast;
+		args.type = RCallbackArgs::RBackendExit;
+		args.params["message"] = QVariant (i18n ("The R engine has shut down with status: %1").arg (status));
 		REmbedInternal::this_pointer->handleStandardCallback (&args);
 
 		if(saveact == SA_DEFAULT) saveact = SA_SAVE;
@@ -221,18 +221,24 @@
 	Rf_error ("Backend dead");	// this jumps us out of the REPL.
 }
 
+QStringList charPArrayToQStringList (const char** chars, int count) {
+	QStringList ret;
+	for (int i = 0; i < count; ++i) {
+		// do we need to do locale conversion, here?
+		ret.append (chars[i]);
+	}
+	return ret;
+}
+
 int RShowFiles (int nfile, const char **file, const char **headers, const char *wtitle, Rboolean del, const char *pager) {
 	RK_TRACE (RBACKEND);
 
 	RCallbackArgs args;
 	args.type = RCallbackArgs::RShowFiles;
-	args.int_a = nfile;
-	args.chars_a = const_cast<char**> (file);
-	args.chars_b = const_cast<char**> (headers);		// what exactly are the "headers"?!?
-	args.chars_c = const_cast<char**> (&wtitle);
-	args.chars_d = const_cast<char**> (&pager);		// we ingnore the pager-parameter for now.
-	args.int_b = del;
-
+	args.params["files"] = QVariant (charPArrayToQStringList (file, nfile));
+	args.params["titles"] = QVariant (charPArrayToQStringList (headers, nfile));
+	args.params["wtitle"] = QVariant (wtitle);
+	args.params["delete"] = QVariant (del);
 	REmbedInternal::this_pointer->handleStandardCallback (&args);
 
 // default implementation seems to returns 1 on success, 0 on failure. see unix/std-sys.c
@@ -244,33 +250,41 @@
 
 	RCallbackArgs args;
 	args.type = RCallbackArgs::RChooseFile;
-	args.int_a = isnew;
-	args.int_b = len;
-	args.chars_a = &buf;
+	args.params["new"] = QVariant ((bool) isnew);
 
 	REmbedInternal::this_pointer->handleStandardCallback (&args);
 
+	QByteArray localres = REmbedInternal::this_pointer->current_locale_codec->fromUnicode (args.params["result"].toString ());
+	qstrncpy ((char *) buf, localres.data (), len);
+
 // return length of filename (strlen (buf))
-	return args.int_c;
+	return (qMin (len - 1, localres.size ()));
 }
 
-int REditFiles (int nfile, const char **file, const char **title, const char *editor) {
+void REditFilesHelper (QStringList files, QStringList titles, QString name_or_something) {
 	RK_TRACE (RBACKEND);
 
 	RCallbackArgs args;
 	args.type = RCallbackArgs::REditFiles;
-	args.int_a = nfile;
-	args.chars_a = const_cast<char**> (file);
-	args.chars_b = const_cast<char**> (title);
-	args.chars_c = const_cast<char**> (&editor);
-
+	args.params["files"] = QVariant (files);
+	args.params["titles"] = QVariant (titles);
+	// and whatever the name_or_something from REditFiles is for...
 	REmbedInternal::this_pointer->handleStandardCallback (&args);
+}
 
+int REditFiles (int nfile, const char **file, const char **title, const char *editor) {
+	RK_TRACE (RBACKEND);
+
+	REditFilesHelper (charPArrayToQStringList (file, nfile), charPArrayToQStringList (title, nfile), editor);
+
 // default implementation seems to return 1 if nfile <= 0, else 1. No idea, what for. see unix/std-sys.c
 	return (nfile <= 0);
 }
 
 SEXP doEditFiles (SEXP files, SEXP titles, SEXP name) {
+	RK_TRACE (RBACKEND);
+
+	// this function would be much shorter, if SEXPToStringList would simply return a QStringList...
 	unsigned int files_count, titles_count, name_count;
 	QString *file_strings = SEXPToStringList (files, &files_count);
 	QString *title_strings = SEXPToStringList (titles, &titles_count);
@@ -282,21 +296,21 @@
 
 	files_count = titles_count = qMin (files_count, titles_count);
 
-	char **file_chars = new char*[files_count];
-	char **title_chars = new char*[files_count];
+	QStringList files_list;
+	QStringList titles_list;
 	for (unsigned int i = 0; i < files_count; ++i) {
-		file_chars[i] = file_strings[i].toLocal8Bit ().data ();
-		title_chars[i] = title_strings[i].toLocal8Bit ().data ();
+		files_list.append (file_strings[i]);
+		titles_list.append (title_strings[i]);
 	}
-	char *name_char;
-	if (name_count > 0) name_char = name_strings[0].toLocal8Bit ().data ();
-	else {
-		name_char = new char[1];
-		name_char[0] = '\0';
-	}
+	QString name_string;
+	if (name_count) name_string = name_strings[0];
 
-	REditFiles (files_count, const_cast<const char**> (file_chars), const_cast<const char**> (title_chars), name_char);
-// TODO: fix memory leak!
+	REditFilesHelper (files_list, titles_list, name_string);
+
+	delete [] file_strings;
+	delete [] title_strings;
+	delete [] name_strings;
+
 	return (R_NilValue);
 }
 

Modified: trunk/rkward/rkward/rbackend/rembedinternal.h
===================================================================
--- trunk/rkward/rkward/rbackend/rembedinternal.h	2009-05-29 14:53:56 UTC (rev 2509)
+++ trunk/rkward/rkward/rbackend/rembedinternal.h	2009-05-29 15:49:10 UTC (rev 2510)
@@ -20,35 +20,30 @@
 
 #include <stddef.h>
 
-/** This struct is an ugly hack that allows us to pass all R standard callbacks to the main thread and back using the same mechanism.
-Basically, it contains enough space for all arguments and return values ever used. However, of course, it is inherently totally unsafe.
-All rests on having the handling functions know exactly, how these variables are used. So be extremely careful with modifications!
+#include <QMap>
+#include <QVariant>
 
-Also, of course, this method of passing the arguments is somewhat wasteful, as most of the time we're alocating a lot more memory than needed.
-However, since all R standard callbacks are used very infrequently (and ask for user interaction), this is not really an issue.
-
-The bool done member is used to find out, when exactly the main thread has finished processing the callback. */
+/** This struct is used to pass the standard callbacks from R to the main thread (if needed; some are handled in the backend thread). Note that for the callbacks that need to be passed to the main
+thread, we can be quite wasteful both in terms of cycles and memory, since these are usually
+requests for user interaction. Hence we use a QVariantMap to accommodate all the different needed
+parameters, easily, and in a readable way. */
 struct RCallbackArgs {
 /** is main thread done with the callback, yet? Initialized to false inside the true handler: RThread::doStandardCallback () */
 	bool done;
-
-/** This enum specifies, what sort of callback this is */
-	enum RCallbackType { RSuicide, RShowMessage, RReadConsole, RWriteConsole, RResetConsole, RFlushConsole, RClearerrConsole,
-											RBusy, RCleanUp, RShowFiles, RChooseFile, /*REditFile,*/ REditFiles
-	} type;
-
-	char **chars_a;		/**< a char** parameter. Look at the respective callbacks to find out, what it is used for. */
-	char **chars_b;		/**< a char** parameter. Look at the respective callbacks to find out, what it is used for. */
-	char **chars_c;		/**< a char** parameter. Look at the respective callbacks to find out, what it is used for. */
-	char **chars_d;		/**< a char** parameter. Look at the respective callbacks to find out, what it is used for. */
-	int int_a;		/**< an int parameter. Look at the respective callbacks to find out, what it is used for. */
-	int int_b;		/**< an int parameter. Look at the respective callbacks to find out, what it is used for. */
-	int int_c;		/**< an int parameter. Look at the respective callbacks to find out, what it is used for. */
-	bool bool_a;		/**< a bool parameter. Look at the respective callbacks to find out, what it is used for. */
+/** type of the callback */
+	enum RCallbackType {
+		RBackendExit,
+		RShowMessage,
+		RShowFiles,
+		RChooseFile,
+		REditFiles,
+		RReadLine
+       } type;
+/** All the parameters sent in either direction */
+	QVariantMap params;
 };
 
 class QStringList;
-class QString;
 class RData;
 class QTextCodec;
 /** This function converts a list of strings to a QStringList (locale aware), and returns the pointer. Needed to keep R and Qt includes separate. The strings can be deleted afterwards. Implementation is in rthread.cpp */

Modified: trunk/rkward/rkward/rbackend/rinterface.cpp
===================================================================
--- trunk/rkward/rkward/rbackend/rinterface.cpp	2009-05-29 14:53:56 UTC (rev 2509)
+++ trunk/rkward/rkward/rbackend/rinterface.cpp	2009-05-29 15:49:10 UTC (rev 2510)
@@ -413,47 +413,30 @@
 	RCallbackArgs::RCallbackType type = args->type;
 
 	if (type == RCallbackArgs::RShowMessage) {
-		KMessageBox::information (0, QString (*(args->chars_a)), i18n ("Message from the R backend"));
-	} else if (type == RCallbackArgs::RReadConsole) {
+		KMessageBox::information (0, args->params["message"].toString (), i18n ("Message from the R backend"));
+	} else if (type == RCallbackArgs::RReadLine) {
 		QString result;
 
-		bool ok = RKReadLineDialog::readLine (0, i18n ("R backend requests information"), *(args->chars_a), runningCommand (), &result);
+		bool ok = RKReadLineDialog::readLine (0, i18n ("R backend requests information"), args->params["prompt"].toString (), runningCommand (), &result);
+		args->params["result"] = QVariant (result);
 
-		result = result.left (args->int_a - 2) + '\n';
-		qstrcpy (*(args->chars_b), result.toLocal8Bit ());
-
-		if (!ok) args->int_c = 0;	// will be cancelled deep inside REmbedInternal, where it's safest
+		if (!ok) args->params["cancelled"] = QVariant (true);
 	} else if ((type == RCallbackArgs::RShowFiles) || (type == RCallbackArgs::REditFiles)) {
-		if ((type == RCallbackArgs::RShowFiles) && (QString (*(args->chars_d)) == "rkwardhtml")) {
-			// not to worry, just some help file to display
-			// TODO: maybe move this to ShowEditTextFileAgent instead
-			for (int n=0; n < args->int_a; ++n) {
-				RKWardMainWindow::getMain ()->openHTML (KUrl (args->chars_a[n]));
-			}
-		} else {
-			ShowEditTextFileAgent::showEditFiles (args);
-			return;
-		}
-	} else if (type ==RCallbackArgs::RChooseFile) {
+		ShowEditTextFileAgent::showEditFiles (args);
+		return;		// we are not done, yet!
+	} else if (type == RCallbackArgs::RChooseFile) {
 		QString filename;
-		if (args->int_a) {
+		if (args->params["new"].toBool ()) {
 			filename = KFileDialog::getSaveFileName ();
 		} else {
 			filename = KFileDialog::getOpenFileName ();
 		}
-		filename = filename.left (args->int_b - 2);
-		args->int_c = filename.length ();
-		qstrcpy (*(args->chars_a), filename.toLatin1 ());
-	} else if (type ==RCallbackArgs::RSuicide) {
-		QString message = i18n ("The R engine has encountered a fatal error:\n") + QString (*(args->chars_a));
-		message += i18n ("It will be shut down immediately. This means, you can not use any more functions that rely on the R backend. I.e. you can do hardly anything at all, not even save the workspace. What you can do, however, is save any open command-files, the output, or copy data out of open data editors. Quit RKWard after that. Sorry!");
+		args->params["result"] = QVariant (filename);
+	} else if (type == RCallbackArgs::RBackendExit) {
+		QString message = args->params["message"].toString ();
+		message += i18n ("\nIt will be shut down immediately. This means, you can not use any more functions that rely on the R backend. I.e. you can do hardly anything at all, not even save the workspace (but if you're lucky, R already did that). What you can do, however, is save any open command-files, the output, or copy data out of open data editors. Quit RKWard after that.\nSince this should never happen, please write a mail to rkward-devel at lists.sourceforge.net, and tell us, what you were trying to do, when this happened. Sorry!");
 		KMessageBox::error (0, message, i18n ("R engine has died"));
 		r_thread->kill ();
-	} else if (type ==RCallbackArgs::RCleanUp) {
-		QString message = i18n ("The R engine has shut down with status: ") + QString::number (args->int_a);
-		message += i18n ("\nIt will be shut down immediately. This means, you can not use any more functions that rely on the R backend. I.e. you can do hardly anything at all, not even save the workspace. Hopefully, however, R has already saved the workspace. What you can do, however, is save any open command-files, the output, or copy data out of open data editors. Quit RKWard after that.\nSince this should never happen, please write a mail to rkward-devel at lists.sourceforge.net, and tell us, what you were trying to do, when this happened. Sorry!");
-		KMessageBox::error (0, message, i18n ("R engine has died"));
-		r_thread->kill ();
 	}
 
 	args->done = true;


This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.




More information about the rkward-tracker mailing list