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

tfry at users.sourceforge.net tfry at users.sourceforge.net
Thu Jan 18 16:27:00 UTC 2007


Revision: 1173
          http://svn.sourceforge.net/rkward/?rev=1173&view=rev
Author:   tfry
Date:     2007-01-18 08:27:00 -0800 (Thu, 18 Jan 2007)

Log Message:
-----------
Safer quitting:
Make sure, even long running commands finish up before tearing down the thread
Try hard to call proper shutdown routine
Make sure not to call processX11Events if the R backend is dead (due to quitting or crash)
Allow user to quit immediately (with a warning)

Modified Paths:
--------------
    trunk/rkward/ChangeLog
    trunk/rkward/TODO
    trunk/rkward/rkward/agents/Makefile.am
    trunk/rkward/rkward/agents/rksaveagent.cpp
    trunk/rkward/rkward/agents/rksaveagent.h
    trunk/rkward/rkward/misc/rkprogresscontrol.cpp
    trunk/rkward/rkward/misc/rkprogresscontrol.h
    trunk/rkward/rkward/rbackend/rcommand.cpp
    trunk/rkward/rkward/rbackend/rcommand.h
    trunk/rkward/rkward/rbackend/rembedinternal.cpp
    trunk/rkward/rkward/rbackend/rembedinternal.h
    trunk/rkward/rkward/rbackend/rinterface.cpp
    trunk/rkward/rkward/rbackend/rinterface.h
    trunk/rkward/rkward/rbackend/rthread.cpp
    trunk/rkward/rkward/rbackend/rthread.h
    trunk/rkward/rkward/rkward.cpp
    trunk/rkward/rkward/rkward.h

Added Paths:
-----------
    trunk/rkward/rkward/agents/rkquitagent.cpp
    trunk/rkward/rkward/agents/rkquitagent.h

Modified: trunk/rkward/ChangeLog
===================================================================
--- trunk/rkward/ChangeLog	2007-01-17 23:34:44 UTC (rev 1172)
+++ trunk/rkward/ChangeLog	2007-01-18 16:27:00 UTC (rev 1173)
@@ -1,3 +1,4 @@
+- safer destruction of R backend on quitting
 - fix compilation for the upcoming R 2.5.0		TODO: check again
 							TODO: update debian dependencies:
 							- the old depends + :

Modified: trunk/rkward/TODO
===================================================================
--- trunk/rkward/TODO	2007-01-17 23:34:44 UTC (rev 1172)
+++ trunk/rkward/TODO	2007-01-18 16:27:00 UTC (rev 1173)
@@ -136,8 +136,6 @@
 	- in REmbedInternal, add extractTemporaryStrings (), for strings that do not have to be copied
 	- Object editing / modification tracking:
 		- functions to set double/int/string-vectors directly. Use in paste-operations
-	- on exit, check whether there are still RCommands left in the backend and wait/request user-interaction before tearing down the thread. After all, the last command might have been a "save"!
-		- How about always doing exiting via a "q ()" command in the backend, instead? That way we could always be sure, all previous commands have been completed before the q ()!
 	- error-handling/raising in .rk.do.call
 	- I've changed the simple RInterface::issueCommand (QString, ...) command to return a pointer to the RCommand created. This will allow for lots of small cleanups. Do them.
 		- well, on second thought, it might be better to rely more on RCommand::id () instead of pointers. Why? The id is unique (until integer overflow). The pointer may be reused after a delete -> potential problem when trying to cancel command which is actually already deleted (and potentially other cases)

Modified: trunk/rkward/rkward/agents/Makefile.am
===================================================================
--- trunk/rkward/rkward/agents/Makefile.am	2007-01-17 23:34:44 UTC (rev 1172)
+++ trunk/rkward/rkward/agents/Makefile.am	2007-01-18 16:27:00 UTC (rev 1173)
@@ -1,5 +1,5 @@
 INCLUDES = $(all_includes)
 METASOURCES = AUTO
 noinst_LIBRARIES =  libagents.a
-noinst_HEADERS = rksaveagent.h rkloadagent.h showedittextfileagent.h
-libagents_a_SOURCES = rksaveagent.cpp rkloadagent.cpp showedittextfileagent.cpp
+noinst_HEADERS = rksaveagent.h rkloadagent.h showedittextfileagent.h rkquitagent.h
+libagents_a_SOURCES = rksaveagent.cpp rkloadagent.cpp showedittextfileagent.cpp rkquitagent.cpp

Added: trunk/rkward/rkward/agents/rkquitagent.cpp
===================================================================
--- trunk/rkward/rkward/agents/rkquitagent.cpp	                        (rev 0)
+++ trunk/rkward/rkward/agents/rkquitagent.cpp	2007-01-18 16:27:00 UTC (rev 1173)
@@ -0,0 +1,81 @@
+/***************************************************************************
+                          rkquitagent  -  description
+                             -------------------
+    begin                : Thu Jan 18 2007
+    copyright            : (C) 2007 by Thomas Friedrichsmeier
+    email                : tfry at users.sourceforge.net
+ ***************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ ***************************************************************************/
+
+#include "rkquitagent.h"
+
+#include <klocale.h>
+
+#include <qtimer.h>
+
+#include "../rkglobals.h"
+#include "../rbackend/rinterface.h"
+#include "../rkward.h"
+#include "../misc/rkprogresscontrol.h"
+
+#include "../debug.h"
+
+//static
+bool RKQuitAgent::quitting = false;
+
+RKQuitAgent::RKQuitAgent (QObject *parent) : QObject (parent) {
+	RK_TRACE (APP);
+
+	quitting = true;
+	RCommand *command = new RCommand (QString::null, RCommand::EmptyCommand | RCommand::QuitCommand, QString::null, this);
+
+	RKWardMainWindow::getMain ()->hide ();
+	cancel_dialog = new RKProgressControl (this, i18n ("Waiting for remaining R commands to finish. To quit immediately, press Cancel (WARNING: This may result in loss of data)"), i18n ("Waiting for R to finish"), RKProgressControl::AllowCancel | RKProgressControl::ShowAtOnce);
+	cancel_dialog->addRCommand (command, true);
+	connect (cancel_dialog, SIGNAL (cancelled ()), this, SLOT (doQuitNow ()));
+
+	if (RKGlobals::rInterface ()->backendIsDead ()) {	// nothing to loose
+		QTimer::singleShot (0, this, SLOT (doQuitNow ()));
+		return;
+	} else if (RKGlobals::rInterface ()->backendIsIdle ()) {
+		// there should be no problem while quitting. If there is, show the dialog after 300 msec
+		QTimer::singleShot (300, this, SLOT (showWaitDialog ()));
+	} else {
+		showWaitDialog ();
+	}
+
+	RKGlobals::rInterface ()->issueCommand (command);
+}
+
+RKQuitAgent::~RKQuitAgent () {
+	RK_TRACE (APP);
+}
+
+void RKQuitAgent::showWaitDialog () {
+	RK_TRACE (APP);
+
+	cancel_dialog->doNonModal (true);
+}
+
+void RKQuitAgent::doQuitNow () {
+	RK_TRACE (APP);
+
+	RKWardMainWindow::getMain ()->close (true);		// this will kill the agent as well.
+}
+
+void RKQuitAgent::rCommandDone (RCommand *) {
+	RK_TRACE (APP);
+
+	QTimer::singleShot (0, this, SLOT (doQuitNow ()));
+}
+
+
+#include "rkquitagent.moc"

Added: trunk/rkward/rkward/agents/rkquitagent.h
===================================================================
--- trunk/rkward/rkward/agents/rkquitagent.h	                        (rev 0)
+++ trunk/rkward/rkward/agents/rkquitagent.h	2007-01-18 16:27:00 UTC (rev 1173)
@@ -0,0 +1,46 @@
+/***************************************************************************
+                          rkquitagent  -  description
+                             -------------------
+    begin                : Thu Jan 18 2007
+    copyright            : (C) 2007 by Thomas Friedrichsmeier
+    email                : tfry at users.sourceforge.net
+ ***************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ ***************************************************************************/
+#ifndef RKQUITAGENT_H
+#define RKQUITAGENT_H
+
+#include <qobject.h>
+#include "../rbackend/rcommandreceiver.h"
+
+class RKProgressControl;
+
+/** The purpose of RKQuitAgent is to delay the actual destruction of the app until all commands have finished in the backend. The quit agent can NOT handle queries for saving some more data, or similar things. Do not call before you really want to quit the application.
+ at author Thomas Friedrichsmeier
+*/
+class RKQuitAgent : public QObject, public RCommandReceiver {
+	Q_OBJECT
+public:
+/** Constructor. As soon as you contruct an object of this type, the RKWard application *will* quit (but maybe with a short delay)! */
+	RKQuitAgent (QObject *parent);
+
+	~RKQuitAgent ();
+	static bool quittingInProgress () { return quitting; };
+public slots:
+	void doQuitNow ();
+	void showWaitDialog ();
+protected:
+	void rCommandDone (RCommand *command);
+private:
+	RKProgressControl *cancel_dialog;
+	static bool quitting;
+};
+
+#endif

Modified: trunk/rkward/rkward/agents/rksaveagent.cpp
===================================================================
--- trunk/rkward/rkward/agents/rksaveagent.cpp	2007-01-17 23:34:44 UTC (rev 1172)
+++ trunk/rkward/rkward/agents/rksaveagent.cpp	2007-01-18 16:27:00 UTC (rev 1173)
@@ -104,10 +104,7 @@
 	if (save_chain) {
 		RKGlobals::rInterface ()->closeChain (save_chain);
 	}
-	if (when_done == Quit) {
-		delete RKWardMainWindow::getMain ();
-		qApp->quit ();
-	} else if (when_done == Load) {
+	if (when_done == Load) {
 		RKWardMainWindow::getMain ()->fileOpenNoSave (load_url);
 		delete this;
 	} else {

Modified: trunk/rkward/rkward/agents/rksaveagent.h
===================================================================
--- trunk/rkward/rkward/agents/rksaveagent.h	2007-01-17 23:34:44 UTC (rev 1172)
+++ trunk/rkward/rkward/agents/rksaveagent.h	2007-01-18 16:27:00 UTC (rev 1173)
@@ -30,7 +30,7 @@
 */
 class RKSaveAgent : public RCommandReceiver {
 public:
-	enum DoneAction { DoNothing=0, Quit=1, Load=2 };
+	enum DoneAction { DoNothing=0, Load=1 };
 
 /** creates a new RKSaveAgent. If when_done == Quit, the RKSaveAgent will quit the application as soon as saving was successful (or it asked to by the user). Similarly, if when_done==Load, it will load a new workspace after saving (specify the url in load_url). If url is given (not empty), and not save_file_as, the agent will try to save to the given url, else it will ask the user to specify a url. RKSaveAgent will self destruct when done. */
 	explicit RKSaveAgent (KURL url, bool save_file_as=false, DoneAction when_done=DoNothing, KURL load_url=QString::null);

Modified: trunk/rkward/rkward/misc/rkprogresscontrol.cpp
===================================================================
--- trunk/rkward/rkward/misc/rkprogresscontrol.cpp	2007-01-17 23:34:44 UTC (rev 1172)
+++ trunk/rkward/rkward/misc/rkprogresscontrol.cpp	2007-01-18 16:27:00 UTC (rev 1173)
@@ -2,7 +2,7 @@
                           rkprogresscontol  -  description
                              -------------------
     begin                : Sun Sep 10 2006
-    copyright            : (C) 2006 by Thomas Friedrichsmeier
+    copyright            : (C) 2006, 2007 by Thomas Friedrichsmeier
     email                : tfry at users.sourceforge.net
  ***************************************************************************/
 
@@ -67,6 +67,7 @@
 	RKProgressControl::autodelete = autodelete;
 	if ((!dialog) && (mode & ShowAtOnce)) {		// actually, dialog should alway be 0 at this point
 		createDialog ();
+		dialog->show ();
 	}
 }
 

Modified: trunk/rkward/rkward/misc/rkprogresscontrol.h
===================================================================
--- trunk/rkward/rkward/misc/rkprogresscontrol.h	2007-01-17 23:34:44 UTC (rev 1172)
+++ trunk/rkward/rkward/misc/rkprogresscontrol.h	2007-01-18 16:27:00 UTC (rev 1173)
@@ -2,7 +2,7 @@
                           rkprogresscontol  -  description
                              -------------------
     begin                : Sun Sep 10 2006
-    copyright            : (C) 2006 by Thomas Friedrichsmeier
+    copyright            : (C) 2006, 2007 by Thomas Friedrichsmeier
     email                : tfry at users.sourceforge.net
  ***************************************************************************/
 

Modified: trunk/rkward/rkward/rbackend/rcommand.cpp
===================================================================
--- trunk/rkward/rkward/rbackend/rcommand.cpp	2007-01-17 23:34:44 UTC (rev 1172)
+++ trunk/rkward/rkward/rbackend/rcommand.cpp	2007-01-18 16:27:00 UTC (rev 1173)
@@ -87,7 +87,7 @@
 		RK_DO (qDebug ("Was not a receiver in RCommand::removeReceiver"), RBACKEND, DL_WARNING);
 	}
 
-	delete receivers;
+	delete [] receivers;
 	receivers = newlist;
 	num_receivers = num_new_receivers;
 }

Modified: trunk/rkward/rkward/rbackend/rcommand.h
===================================================================
--- trunk/rkward/rkward/rbackend/rcommand.h	2007-01-17 23:34:44 UTC (rev 1172)
+++ trunk/rkward/rkward/rbackend/rcommand.h	2007-01-18 16:27:00 UTC (rev 1173)
@@ -162,7 +162,8 @@
 		GetRealVector=2048,		/**< Try to fetch result as an array of doubles */
 		GetStructuredData=4096,		/**< Try to fetch result as an RData structure */
 		DirectToOutput=8192,		/**< Append command output to the HTML-output file */
-		ObjectListUpdate=16384		/**< The command may change the list of objects available. Do an update */
+		ObjectListUpdate=16384,		/**< The command may change the list of objects available. Do an update */
+		QuitCommand=32768		/**< The R backend should be killed */
 	};
 	enum CommandStatus {
 		WasTried=1,						/**< the command has been passed to the backend. */

Modified: trunk/rkward/rkward/rbackend/rembedinternal.cpp
===================================================================
--- trunk/rkward/rkward/rbackend/rembedinternal.cpp	2007-01-17 23:34:44 UTC (rev 1172)
+++ trunk/rkward/rkward/rbackend/rembedinternal.cpp	2007-01-18 16:27:00 UTC (rev 1173)
@@ -276,6 +276,7 @@
 char *REmbedInternal::na_char_internal = new char;
 
 REmbedInternal::REmbedInternal () {
+	r_running = false;
 }
 
 void REmbedInternal::connectCallbacks () {
@@ -314,7 +315,9 @@
 }
 
 void REmbedInternal::shutdown (bool suicidal) {
-// Code-recipe below essentially copied from http://stat.ethz.ch/R-manual/R-devel/doc/manual/R-exts.html#Linking-GUIs-and-other-front_002dends-to-R
+	if (!REmbedInternal::this_pointer->r_running) return;		// already shut down
+
+// Code-recipe below essentially copied from http://stat.ethz.ch/R-manual/R-devel/doc/manual/R-exts.html#Linking-GUIs-and-other-front_ends-to-R
 // modified quite a bit for our needs.
 	char *tmpdir;
 
@@ -333,11 +336,14 @@
 	/* close all the graphics devices */
 	if (!suicidal) KillAllDevices ();
 	fpu_setup ((Rboolean) FALSE);
+
+	REmbedInternal::this_pointer->r_running = false;
 }
 
 static int timeout_counter = 0;
 
 void REmbedInternal::processX11Events () {
+	if (!this_pointer->r_running) return;
 /* what we do here is walk the list of objects, that have told R, they're listening for events.
 We figure out which ones look for X11-events and tell those to do their stuff (regardless of whether events actually occurred) */
 	extern InputHandler *R_InputHandlers;
@@ -511,6 +517,7 @@
 }
 
 bool REmbedInternal::startR (int argc, char** argv) {
+	r_running = true;
 #ifdef R_2_3
 	Rf_initialize_R (argc, argv);
 	R_CStackLimit = (unsigned long) -1;

Modified: trunk/rkward/rkward/rbackend/rembedinternal.h
===================================================================
--- trunk/rkward/rkward/rbackend/rembedinternal.h	2007-01-17 23:34:44 UTC (rev 1172)
+++ trunk/rkward/rkward/rbackend/rembedinternal.h	2007-01-18 16:27:00 UTC (rev 1173)
@@ -154,6 +154,7 @@
 /** only one instance of this class may be around. This pointer keeps the reference to it, for interfacing to from C to C++ */
 	static REmbedInternal *this_pointer;
 	static char *na_char_internal;
+	bool r_running;
 
 /** Flags used to classify output. */
 //	static bool output_is_warning;

Modified: trunk/rkward/rkward/rbackend/rinterface.cpp
===================================================================
--- trunk/rkward/rkward/rbackend/rinterface.cpp	2007-01-17 23:34:44 UTC (rev 1172)
+++ trunk/rkward/rkward/rbackend/rinterface.cpp	2007-01-18 16:27:00 UTC (rev 1173)
@@ -2,7 +2,7 @@
                           rinterface.cpp  -  description
                              -------------------
     begin                : Fri Nov 1 2002
-    copyright            : (C) 2002 by Thomas Friedrichsmeier
+    copyright            : (C) 2002, 2004, 2005, 2006, 2007 by Thomas Friedrichsmeier
     email                : tfry at users.sourceforge.net
  ***************************************************************************/
 
@@ -64,7 +64,7 @@
 #ifndef DISABLE_RKWINDOWCATCHER
 	window_catcher = new RKWindowCatcher ();
 #endif // DISABLE_RKWINDOWCATCHER
-	
+
 // If R_HOME is not set, most certainly the user called the binary without the wrapper script
 	if (!getenv ("R_HOME")) {
 		RK_DO (qDebug ("No R_HOME environment variable set. RKWard will quit in a moment. Always start rkward in the default way unless you know what you're doing."), RBACKEND, DL_ERROR);
@@ -88,27 +88,40 @@
 
 RInterface::~RInterface(){
 	RK_TRACE (RBACKEND);
-	
-	// kill the thread gracefully
-	MUTEX_LOCK
-	r_thread->kill ();
-	MUTEX_UNLOCK
-	r_thread->wait (5000);
-	
-	// timeout in wait? Try a little harder
+
 	if (r_thread->running ()) {
-		MUTEX_LOCK
+		RK_DO (qDebug ("Waiting for R thread to finish up..."), RBACKEND, DL_INFO);
 		r_thread->interruptProcessing (true);
-		MUTEX_UNLOCK
-		r_thread->wait (10000);
-		// if the thread did not exit, yet - bad luck.
+		r_thread->kill ();
+		r_thread->wait (1000);
+		if (r_thread->running ()) {
+			RK_DO (qDebug ("Backend thread is still running. It will be killed, now."), RBACKEND, DL_WARNING);
+			r_thread->terminate ();
+			RK_ASSERT (false);
+		}
 	}
-
+	delete r_thread;
 	delete flush_timer;
 	RKCommandLog::destroy ();
 	delete window_catcher;
 }
 
+bool RInterface::backendIsDead () {
+	RK_TRACE (RBACKEND);
+
+	return (!r_thread->running ());
+}
+
+bool RInterface::backendIsIdle () {
+	RK_TRACE (RBACKEND);
+
+	bool idle;
+	MUTEX_LOCK;
+	idle = (RCommandStack::regular_stack->isEmpty() && (!r_thread->current_command));
+	MUTEX_UNLOCK;
+	return (idle);
+}
+
 void RInterface::startThread () {
 	RK_TRACE (RBACKEND);
 	r_thread->start ();
@@ -397,12 +410,12 @@
 		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!");
 		KMessageBox::error (0, message, i18n ("R engine has died"));
-		r_thread->terminate ();
+		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->terminate ();
+		r_thread->kill ();
 	}
 
 	args->done = true;

Modified: trunk/rkward/rkward/rbackend/rinterface.h
===================================================================
--- trunk/rkward/rkward/rbackend/rinterface.h	2007-01-17 23:34:44 UTC (rev 1172)
+++ trunk/rkward/rkward/rbackend/rinterface.h	2007-01-18 16:27:00 UTC (rev 1173)
@@ -2,7 +2,7 @@
                           rinterface.h  -  description
                              -------------------
     begin                : Fri Nov 1 2002
-    copyright            : (C) 2002 by Thomas Friedrichsmeier
+    copyright            : (C) 2002, 2004, 2005, 2006, 2007 by Thomas Friedrichsmeier
     email                : tfry at users.sourceforge.net
  ***************************************************************************/
 
@@ -85,6 +85,9 @@
 
 /** returns the command currently running in the thread. Be careful when using the returned pointer! */
 	RCommand *runningCommand ();
+
+	bool backendIsDead ();
+	bool backendIsIdle ();
 public slots:
 /** called periodically to flush output buffer in RThread */
 	void flushOutput ();

Modified: trunk/rkward/rkward/rbackend/rthread.cpp
===================================================================
--- trunk/rkward/rkward/rbackend/rthread.cpp	2007-01-17 23:34:44 UTC (rev 1172)
+++ trunk/rkward/rkward/rbackend/rthread.cpp	2007-01-18 16:27:00 UTC (rev 1173)
@@ -117,7 +117,7 @@
 				return;
 			}
 		}
-		
+
 		if (!previously_idle) {
 			if (RCommandStack::regular_stack->isEmpty ()) {
 				qApp->postEvent (RKGlobals::rInterface (), new QCustomEvent (RIDLE_EVENT));
@@ -216,6 +216,13 @@
 		RK_DO (qDebug ("done running command"), RBACKEND, DL_DEBUG);
 
 		flushOutput ();
+	} else {
+		if (command->type () & RCommand::QuitCommand) {
+			killed = true;
+			MUTEX_UNLOCK;
+			shutdown (false);
+			MUTEX_LOCK;
+		}
 	}
 
 	// notify GUI-thread that command was finished
@@ -344,6 +351,11 @@
 		processX11Events ();
 		// while commands are in queue, don't wait
 		while (reply_stack->isActive () && !locked) {
+			if (killed) {
+				done = true;
+				break;
+			}
+
 			RCommand *command = reply_stack->pop ();
 			
 			if (command) {
@@ -466,6 +478,7 @@
 
 void RThread::checkObjectUpdatesNeeded (bool check_list) {
 	RK_TRACE (RBACKEND);
+	if (killed) return;
 
 	/* NOTE: We're keeping separate lists of the items on the search path, and the toplevel symbols in .GlobalEnv here.
 	This info is also present in RObjectList (and it's children). However: a) in a less convenient form, b) in the other thread. To avoid locking, and other complexity, keeping separate lists seems an ok solution. Keep in mind that only the names of only the toplevel objects are kept, here, so the memory overhead should be minimal */

Modified: trunk/rkward/rkward/rbackend/rthread.h
===================================================================
--- trunk/rkward/rkward/rbackend/rthread.h	2007-01-17 23:34:44 UTC (rev 1172)
+++ trunk/rkward/rkward/rbackend/rthread.h	2007-01-18 16:27:00 UTC (rev 1173)
@@ -90,6 +90,7 @@
 	void unlock (LockType reason) { locked -= (locked & reason); };
 /** "Kills" the thread. Actually this just tells the thread that is is about to be terminated. Allows the thread to terminate gracefully */
 	void kill () { killed = true; };
+	bool isKilled () { return killed; };
 /** Pause output by placing it in a delay loop, until unpaused again */
 	void pauseOutput (bool paused) { output_paused = paused; };
 /** the internal counterpart to pauseOutput () */

Modified: trunk/rkward/rkward/rkward.cpp
===================================================================
--- trunk/rkward/rkward/rkward.cpp	2007-01-17 23:34:44 UTC (rev 1172)
+++ trunk/rkward/rkward/rkward.cpp	2007-01-18 16:27:00 UTC (rev 1173)
@@ -66,6 +66,7 @@
 #include "dialogs/rkloadlibsdialog.h"
 #include "agents/rksaveagent.h"
 #include "agents/rkloadagent.h"
+#include "agents/rkquitagent.h"
 #include "windows/rcontrolwindow.h"
 #include "windows/rkhtmlwindow.h"
 #include "windows/rkworkplaceview.h"
@@ -159,6 +160,19 @@
 	delete RKGlobals::tracker ();
 }
 
+void RKWardMainWindow::closeEvent (QCloseEvent *e) {
+	RK_TRACE (APP);
+
+	if (RKQuitAgent::quittingInProgress ()) {
+		KMdiMainFrm::closeEvent (e);
+		return;
+	}
+
+	if (doQueryQuit ()) {
+		new RKQuitAgent (this);
+	}
+}
+
 void RKWardMainWindow::doPostInit () {
 	RK_TRACE (APP);
 
@@ -487,7 +501,7 @@
   } */
 }
 
-bool RKWardMainWindow::queryClose () {
+bool RKWardMainWindow::doQueryQuit () {
 	RK_TRACE (APP);
 
 	slotSetStatusBarText (i18n ("Exiting..."));
@@ -500,7 +514,7 @@
 		int res;
 		res = KMessageBox::questionYesNoCancel (this, i18n ("Quitting RKWard: Do you want to save the workspace?\nRKWard will remain open if you press Cancel"), i18n ("Save Workspace?"));
 		if (res == KMessageBox::Yes) {
-			new RKSaveAgent (RObjectList::getObjectList ()->getWorkspaceURL (), false, RKSaveAgent::Quit);
+			new RKSaveAgent (RObjectList::getObjectList ()->getWorkspaceURL (), false, RKSaveAgent::DoNothing);
 		} else if (res != KMessageBox::No) {
 			slotSetStatusReady ();
 			return false;

Modified: trunk/rkward/rkward/rkward.h
===================================================================
--- trunk/rkward/rkward/rkward.h	2007-01-17 23:34:44 UTC (rev 1172)
+++ trunk/rkward/rkward/rkward.h	2007-01-18 16:27:00 UTC (rev 1173)
@@ -90,6 +90,9 @@
 	static RKWardMainWindow *getMain () { return rkward_mainwin; };
 
 	void makeRKWardHelpMenu (QWidget *for_window, KActionCollection *ac);
+
+/** (try to) close all windows, and ask whether it is ok to quit */
+	bool doQueryQuit ();
 protected:
 	void openWorkspace (const KURL &url);
 	/** save Options/Settings. Includes general Options like all bar positions and status as well as the geometry and the recent file list */
@@ -101,13 +104,8 @@
 	/** sets up the statusbar for the main window by initialzing a statuslabel.
 	*/
 	void initStatusBar();
-	/** queryClose is called by KTMainWindow on each closeEvent of a window. Against the
-	* default implementation (only returns true), this calles saveModified() on the document object to ask if the document shall
-	* be saved if Modified; on cancel the closeEvent is rejected.
-	* @see KTMainWindow#queryClose
-	* @see KTMainWindow#closeEvent
-	*/
-	virtual bool queryClose();
+	/** reimplemented from KMainWindow to call our doQueryClose (), and then (if quitting was not cancelled), invoke an RKQuitAgent to wait for the R-backend to finish up before actually quitting. */
+	virtual void closeEvent (QCloseEvent *e);
 	/** saves the window properties for each open window during session end to the session config file, including saving the currently
 	* opened file by a temporary filename provided by KApplication.
 	* @see KTMainWindow#saveProperties


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