[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