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

tfry at users.sourceforge.net tfry at users.sourceforge.net
Thu Oct 20 09:37:56 UTC 2011


Revision: 3975
          http://rkward.svn.sourceforge.net/rkward/?rev=3975&view=rev
Author:   tfry
Date:     2011-10-20 09:37:56 +0000 (Thu, 20 Oct 2011)
Log Message:
-----------
Add some debugging support. This is not terribly refined, so far, but I think it's fairly useful, already.

Modified Paths:
--------------
    trunk/rkward/ChangeLog
    trunk/rkward/rkward/agents/CMakeLists.txt
    trunk/rkward/rkward/misc/rkstandardicons.cpp
    trunk/rkward/rkward/misc/rkstandardicons.h
    trunk/rkward/rkward/plugin/rkstandardcomponentgui.cpp
    trunk/rkward/rkward/rbackend/rinterface.cpp
    trunk/rkward/rkward/rbackend/rkrbackend.cpp
    trunk/rkward/rkward/rbackend/rkrbackend.h
    trunk/rkward/rkward/rbackend/rkrbackendprotocol_shared.h
    trunk/rkward/rkward/rkward.cpp
    trunk/rkward/rkward/windows/CMakeLists.txt
    trunk/rkward/rkward/windows/rkcommandeditorwindow.cpp
    trunk/rkward/rkward/windows/rkmdiwindow.cpp
    trunk/rkward/rkward/windows/rkmdiwindow.h
    trunk/rkward/rkward/windows/rktoolwindowbar.cpp
    trunk/rkward/rkward/windows/rktoplevelwindowgui.rc

Added Paths:
-----------
    trunk/rkward/rkward/agents/rkdebughandler.cpp
    trunk/rkward/rkward/agents/rkdebughandler.h
    trunk/rkward/rkward/rbackend/rpackages/rkward/R/internal_debugger.R
    trunk/rkward/rkward/windows/rkcallstackviewer.cpp
    trunk/rkward/rkward/windows/rkcallstackviewer.h
    trunk/rkward/rkward/windows/rkdebugconsole.cpp
    trunk/rkward/rkward/windows/rkdebugconsole.h

Modified: trunk/rkward/ChangeLog
===================================================================
--- trunk/rkward/ChangeLog	2011-10-19 21:09:36 UTC (rev 3974)
+++ trunk/rkward/ChangeLog	2011-10-20 09:37:56 UTC (rev 3975)
@@ -1,3 +1,4 @@
+- Add GUI support for inspecting the call stack during debugging
 - The backend executable is no longer linked against KDE libraries
 - Objects, which are not acceptable in a varslot, will still be shown, there, with a warning		TODO: add a UI to override
 

Modified: trunk/rkward/rkward/agents/CMakeLists.txt
===================================================================
--- trunk/rkward/rkward/agents/CMakeLists.txt	2011-10-19 21:09:36 UTC (rev 3974)
+++ trunk/rkward/rkward/agents/CMakeLists.txt	2011-10-20 09:37:56 UTC (rev 3975)
@@ -4,6 +4,7 @@
 ########### next target ###############
 
 SET(agents_STAT_SRCS
+   rkdebughandler.cpp
    rkeditobjectagent.cpp
    rkloadagent.cpp
    rkprintagent.cpp

Added: trunk/rkward/rkward/agents/rkdebughandler.cpp
===================================================================
--- trunk/rkward/rkward/agents/rkdebughandler.cpp	                        (rev 0)
+++ trunk/rkward/rkward/agents/rkdebughandler.cpp	2011-10-20 09:37:56 UTC (rev 3975)
@@ -0,0 +1,80 @@
+/***************************************************************************
+                          rkdebughandler  -  description
+                             -------------------
+    begin                : Wed Oct 19 2011
+    copyright            : (C) 2011 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 "rkdebughandler.h"
+
+#include "../rbackend/rkrbackendprotocol_frontend.h"
+
+#include "../debug.h"
+
+RKDebugHandler* RKDebugHandler::_instance = 0;
+
+RKDebugHandler::RKDebugHandler (QObject *parent) : QObject (parent) {
+	RK_TRACE (APP);
+
+	_state = NotInDebugger;
+	_request = 0;
+	_instance = this;
+}
+
+RKDebugHandler::~RKDebugHandler () {
+	RK_TRACE (APP);
+}
+
+void RKDebugHandler::debugCall (RBackendRequest *request, RCommand *command) {
+	RK_TRACE (APP);
+
+	_request = request;
+	if (command) _output_context = command->fullOutput ();
+	else _output_context.clear ();
+
+	_calls = request->params["calls"].toStringList ();
+	_functions = request->params["funs"].toStringList ();
+	_environments = request->params["envs"].toStringList ();
+	_locals = request->params["locals"].toStringList ();
+	_prompt = request->params["prompt"].toString ();
+
+	_state = InDebugPrompt;
+	newDebugState ();
+}
+
+void RKDebugHandler::submitDebugString (const QString &command) {
+	RK_TRACE (APP);
+
+	if (!_request) {
+		RK_ASSERT (false);
+		return;
+	}
+
+	_request->params["result"] = command;
+
+	RKRBackendProtocolFrontend::setRequestCompleted (_request);
+
+	_state = InDebugRun;
+	newDebugState ();
+}
+
+void RKDebugHandler::endDebug () {
+	RK_TRACE (APP);
+
+	_request = 0;
+	_state = NotInDebugger;
+	newDebugState ();
+}
+
+#include "rkdebughandler.moc"
+

Added: trunk/rkward/rkward/agents/rkdebughandler.h
===================================================================
--- trunk/rkward/rkward/agents/rkdebughandler.h	                        (rev 0)
+++ trunk/rkward/rkward/agents/rkdebughandler.h	2011-10-20 09:37:56 UTC (rev 3975)
@@ -0,0 +1,64 @@
+/***************************************************************************
+                          rkdebughandler  -  description
+                             -------------------
+    begin                : Wed Oct 19 2011
+    copyright            : (C) 2011 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 RKDEBUGHANDLER_H
+#define RKDEBUGHANDLER_H
+
+#include <QObject>
+
+#include "../rbackend/rcommand.h"
+
+class RBackendRequest;
+
+/** A central handler, responsible for keeping all debug related widgets up-to-date */
+class RKDebugHandler : public QObject {
+	Q_OBJECT
+public:
+	RKDebugHandler (QObject *parent);
+	~RKDebugHandler ();
+
+	static RKDebugHandler *instance () { return _instance; };
+
+	void debugCall (RBackendRequest *request, RCommand *command);
+	void submitDebugString (const QString &command);
+	void endDebug ();
+
+	enum DebugState {
+		NotInDebugger,
+		InDebugPrompt,
+		InDebugRun
+	};
+	DebugState state () const { return _state; };
+
+	QString outputContext () const { return _output_context; };
+	QStringList calls () const { return _calls; };
+	QStringList functions () const { return _functions; };
+	QStringList environments () const { return _environments; };
+	QStringList locals () const { return _locals; };
+	QString debugPrompt () const { return _prompt; };
+signals:
+	void newDebugState ();
+private:
+	QStringList _calls, _functions, _environments, _locals;
+	QString _prompt, _output_context;
+	DebugState _state;
+	RBackendRequest *_request;
+
+	static RKDebugHandler *_instance;
+};
+
+#endif

Modified: trunk/rkward/rkward/misc/rkstandardicons.cpp
===================================================================
--- trunk/rkward/rkward/misc/rkstandardicons.cpp	2011-10-19 21:09:36 UTC (rev 3974)
+++ trunk/rkward/rkward/misc/rkstandardicons.cpp	2011-10-20 09:37:56 UTC (rev 3975)
@@ -106,6 +106,8 @@
 	icons[WindowSearchHelp] = KIcon ("help-contents");
 	icons[WindowPendingJobs] = KIcon ("system-run");
 	icons[WindowFileBrowser] = KIcon ("folder");
+	icons[WindowDebugConsole] = KIcon ("view-process-system");
+	icons[WindowCallstackViewer] = KIcon ("view-sort-ascending");
 
 	icons[DocumentPDF] = KIcon ("application-pdf");
 
@@ -165,6 +167,8 @@
 	if (window->isType (RKMDIWindow::SearchHelpWindow)) return icons[WindowSearchHelp];
 	if (window->isType (RKMDIWindow::PendingJobsWindow)) return icons[WindowPendingJobs];
 	if (window->isType (RKMDIWindow::FileBrowserWindow)) return icons[WindowFileBrowser];
+	if (window->isType (RKMDIWindow::DebugConsoleWindow)) return icons[WindowDebugConsole];
+	if (window->isType (RKMDIWindow::CallstackViewerWindow)) return icons[WindowCallstackViewer];
 
 	RK_ASSERT (false);
 	return QIcon ();

Modified: trunk/rkward/rkward/misc/rkstandardicons.h
===================================================================
--- trunk/rkward/rkward/misc/rkstandardicons.h	2011-10-19 21:09:36 UTC (rev 3974)
+++ trunk/rkward/rkward/misc/rkstandardicons.h	2011-10-20 09:37:56 UTC (rev 3975)
@@ -102,6 +102,8 @@
 		WindowSearchHelp,
 		WindowPendingJobs,
 		WindowFileBrowser,
+		WindowDebugConsole,
+		WindowCallstackViewer,
 
 		DocumentPDF,
 

Modified: trunk/rkward/rkward/plugin/rkstandardcomponentgui.cpp
===================================================================
--- trunk/rkward/rkward/plugin/rkstandardcomponentgui.cpp	2011-10-19 21:09:36 UTC (rev 3974)
+++ trunk/rkward/rkward/plugin/rkstandardcomponentgui.cpp	2011-10-20 09:37:56 UTC (rev 3975)
@@ -257,14 +257,12 @@
 void RKStandardComponentGUI::updateCodeNow () {
 	RK_TRACE (PLUGIN);
 
-	code_display->setReadOnly (false);	// sigh. The kate part does not allow setting text programatically, when it is read only
 	if (!code_property->isValid ()) {
 		code_display->setText (i18n ("Processing. Please wait"));
 		RK_DO (qDebug ("code not ready to be displayed: pre %d, cal %d, pri %d", !code_property->preprocess ().isNull (), !code_property->calculate ().isNull (), !code_property->printout ().isNull ()), PLUGIN, DL_DEBUG);
 	} else {
 		code_display->setText ("local({\n" + code_property->preprocess () + code_property->calculate () + code_property->printout () + "})\n");
 	}
-	code_display->setReadOnly (true);
 }
 
 ///////////////////////////////// RKStandardComponentWizard /////////////////////////////////

Modified: trunk/rkward/rkward/rbackend/rinterface.cpp
===================================================================
--- trunk/rkward/rkward/rbackend/rinterface.cpp	2011-10-19 21:09:36 UTC (rev 3974)
+++ trunk/rkward/rkward/rbackend/rinterface.cpp	2011-10-20 09:37:56 UTC (rev 3975)
@@ -36,6 +36,7 @@
 #include "../agents/showedittextfileagent.h"
 #include "../agents/rkeditobjectagent.h"
 #include "../agents/rkprintagent.h"
+#include "../agents/rkdebughandler.h"
 #include "../windows/rcontrolwindow.h"
 #include "../windows/rkworkplace.h"
 #include "../windows/rkcommandlog.h"
@@ -106,6 +107,7 @@
 	issueCommand (fake);
 
 	new RKSessionVars (this);
+	new RKDebugHandler (this);
 	new RKRBackendProtocolFrontend (this);
 	RKRBackendProtocolFrontend::instance ()->setupBackend ();
 
@@ -606,6 +608,8 @@
 		}
 	} else if (call == "printPreview") {
 		RKPrintAgent::printPostscript (calllist.value (1), true);
+	} else if (call == "endBrowserContext") {
+		RKDebugHandler::instance ()->endDebug ();
 	} else {
 		return (QStringList ("Error: unrecognized request '" + call + "'."));
 	}
@@ -766,11 +770,15 @@
 			dummy_command = true;
 		}
 
+
 		bool ok = RKReadLineDialog::readLine (0, i18n ("R backend requests information"), request->params["prompt"].toString (), command, &result);
 		request->params["result"] = QVariant (result);
 
 		if (dummy_command) delete command;
 		if (!ok) request->params["cancelled"] = QVariant (true);
+	} else if (type == RBackendRequest::Debugger) {
+		RKDebugHandler::instance ()->debugCall (request, runningCommand ());
+		return;		// request will be closed by the debug handler
 	} else if ((type == RBackendRequest::ShowFiles) || (type == RBackendRequest::EditFiles)) {
 		ShowEditTextFileAgent::showEditFiles (request);
 		return;		// we are not done, yet!

Modified: trunk/rkward/rkward/rbackend/rkrbackend.cpp
===================================================================
--- trunk/rkward/rkward/rbackend/rkrbackend.cpp	2011-10-19 21:09:36 UTC (rev 3974)
+++ trunk/rkward/rkward/rbackend/rkrbackend.cpp	2011-10-20 09:37:56 UTC (rev 3975)
@@ -31,7 +31,7 @@
 
 // statics
 RKRBackend *RKRBackend::this_pointer = 0;
-RKRBackend::RKReplStatus RKRBackend::repl_status = { QByteArray (), 0, true, 0, 0, RKRBackend::RKReplStatus::NoUserCommand, 0, false, false };
+RKRBackend::RKReplStatus RKRBackend::repl_status = { QByteArray (), 0, true, 0, 0, RKRBackend::RKReplStatus::NoUserCommand, 0, RKRBackend::RKReplStatus::NotInBrowserContext, false };
 void* RKRBackend::default_global_context = 0;
 
 #include <qstring.h>
@@ -195,7 +195,7 @@
 	Q_UNUSED (value);
 	Q_UNUSED (visible);
 
-	if ((RKRBackend::repl_status.eval_depth == 0) && (!RKRBackend::repl_status.in_browser_context)) {		// Yes, toplevel-handlers _do_ get called in a browser context!
+	if ((RKRBackend::repl_status.eval_depth == 0) && (!RKRBackend::repl_status.browser_context)) {		// Yes, toplevel-handlers _do_ get called in a browser context!
 		RK_ASSERT (RKRBackend::repl_status.user_command_status = RKRBackend::RKReplStatus::UserCommandRunning);
 		if (succeeded) {
 			RKRBackend::repl_status.user_command_successful_up_to = RKRBackend::repl_status.user_command_parsed_up_to;
@@ -265,14 +265,14 @@
 	RK_ASSERT (buf && buflen);
 	RK_ASSERT (RKRBackend::repl_status.eval_depth >= 0);
 
-	if (RKRBackend::repl_status.in_browser_context) {		// previously we were in a browser context. Check, whether we've left that.
+	if (RKRBackend::repl_status.browser_context) {		// previously we were in a browser context. Check, whether we've left that.
 		if (RKRBackend::default_global_context == R_GlobalContext) {
-			RKRBackend::repl_status.in_browser_context = false;
-			RK_ASSERT (!hist);
+			RKRBackend::repl_status.browser_context = RKRBackend::RKReplStatus::NotInBrowserContext;
+			RKRBackend::this_pointer->handlePlainGenericRequest (QStringList ("endBrowserContext"), false);
 		}
 	}
 	
-	if ((!RKRBackend::repl_status.in_browser_context) && (RKRBackend::repl_status.eval_depth == 0)) {
+	if ((!RKRBackend::repl_status.browser_context) && (RKRBackend::repl_status.eval_depth == 0)) {
 		while (1) {
 			if (RKRBackend::repl_status.user_command_status == RKRBackend::RKReplStatus::NoUserCommand) {
 				RCommandProxy *command = RKRBackend::this_pointer->fetchNextCommand ();
@@ -371,14 +371,38 @@
 
 	// here, we handle readline() calls and such, i.e. not the regular prompt for code
 	// browser() also takes us here.
+	QVariantMap params;
+	RBackendRequest::RCallbackType request_type = RBackendRequest::ReadLine;
+	params["prompt"] = QVariant (prompt);
+	params["cancelled"] = QVariant (false);
+
+	// add info for browser requests
 	if (hist && (RKRBackend::default_global_context != R_GlobalContext)) {
-		// TODO: give browser() special handling!
-		RKRBackend::repl_status.in_browser_context = true;
+		if (RKRBackend::repl_status.browser_context == RKRBackend::RKReplStatus::InBrowserContextPreventRecursion) {
+			qstrncpy ((char *) buf, "n\n", buflen);	// skip this, by feeding the browser() a continue
+			return 1;
+		} else {
+			RKRBackend::repl_status.browser_context = RKRBackend::RKReplStatus::InBrowserContextPreventRecursion;
+			RCommandProxy *dummy = RKRBackend::this_pointer->runDirectCommand (".rk.callstack.info()", RCommand::GetStructuredData);
+
+			request_type = RBackendRequest::Debugger;
+			if ((dummy->getDataType () == RData::StructureVector) && (dummy->getDataLength () >= 4)) {
+				params["calls"] = QVariant (dummy->getStructureVector ()[0]->getStringVector ());
+				params["funs"] = QVariant (dummy->getStructureVector ()[1]->getStringVector ());
+				params["envs"] = QVariant (dummy->getStructureVector ()[2]->getStringVector ());
+				params["locals"] = QVariant (dummy->getStructureVector ()[3]->getStringVector ());
+			} else {
+				RK_ASSERT (false);
+			}
+
+			RKRBackend::repl_status.browser_context = RKRBackend::RKReplStatus::InBrowserContext;
+		}
+
+		RK_ASSERT (RKRBackend::repl_status.browser_context == RKRBackend::RKReplStatus::InBrowserContext);
 	}
 
-	RBackendRequest request (true, RBackendRequest::ReadLine);
-	request.params["prompt"] = QVariant (prompt);
-	request.params["cancelled"] = QVariant (false);
+	RBackendRequest request (true, request_type);
+	request.params = params;
 
 	RKRBackend::this_pointer->handleRequest (&request);
 	if (request.params["cancelled"].toBool ()) {
@@ -427,7 +451,7 @@
 	RK_TRACE (RBACKEND);
 
 	// output while nothing else is running (including handlers?) -> This may be a syntax error.
-	if ((RKRBackend::repl_status.eval_depth == 0) && (!RKRBackend::repl_status.in_browser_context) && (!RKRBackend::this_pointer->isKilled ())) {
+	if ((RKRBackend::repl_status.eval_depth == 0) && (!RKRBackend::repl_status.browser_context) && (!RKRBackend::this_pointer->isKilled ())) {
 		if (RKRBackend::repl_status.user_command_status == RKRBackend::RKReplStatus::UserCommandTransmitted) {
 			// status UserCommandTransmitted might have been set from RKToplevelStatementFinishedHandler, too, in which case all is fine
 			// (we're probably inside another task handler at this point, then)
@@ -443,6 +467,7 @@
 	}
 
 	if (RKRBackend::this_pointer->killed == RKRBackend::AlreadyDead) return;	// this check is mostly for fork()ed clients
+	if (RKRBackend::repl_status.browser_context == RKRBackend::RKReplStatus::InBrowserContextPreventRecursion) return;
 	RKRBackend::this_pointer->fetchStdoutStderr (true);
 	RKRBackend::this_pointer->handleOutput (RKRBackend::this_pointer->current_locale_codec->toUnicode (buf, buflen), buflen, type == 0 ? ROutput::Output : ROutput::Warning);
 }
@@ -843,7 +868,7 @@
 SEXP doError (SEXP call) {
 	RK_TRACE (RBACKEND);
 
-	if ((RKRBackend::repl_status.eval_depth == 0) && (!RKRBackend::repl_status.in_browser_context) && (!RKRBackend::this_pointer->isKilled ()) && (RKRBackend::repl_status.user_command_status != RKRBackend::RKReplStatus::ReplIterationKilled)) {
+	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::UserCommandFailed;
 	}
 	if (RKRBackend::repl_status.interrupted) {

Modified: trunk/rkward/rkward/rbackend/rkrbackend.h
===================================================================
--- trunk/rkward/rkward/rbackend/rkrbackend.h	2011-10-19 21:09:36 UTC (rev 3974)
+++ trunk/rkward/rkward/rbackend/rkrbackend.h	2011-10-20 09:37:56 UTC (rev 3975)
@@ -160,7 +160,11 @@
 			ReplIterationKilled
 		} user_command_status;
 		int eval_depth;		// Number (depth) of non-user commands currently running. User commands can only run at depth 0
-		bool in_browser_context;
+		enum {
+			NotInBrowserContext = 0,
+			InBrowserContext,
+			InBrowserContextPreventRecursion
+		} browser_context;
 		bool interrupted;
 	};
 	static RKReplStatus repl_status;

Modified: trunk/rkward/rkward/rbackend/rkrbackendprotocol_shared.h
===================================================================
--- trunk/rkward/rkward/rbackend/rkrbackendprotocol_shared.h	2011-10-19 21:09:36 UTC (rev 3974)
+++ trunk/rkward/rkward/rbackend/rkrbackendprotocol_shared.h	2011-10-20 09:37:56 UTC (rev 3975)
@@ -44,6 +44,7 @@
 		HistoricalSubstackRequest,
 		PlainGenericRequest,
 		SetParamsFromBackend,
+		Debugger,
 		CommandLineIn,	/**< The next line of the current user command has been submitted in the backend. */
 #ifndef RKWARD_THREADED
 		Output,		/**< A piece of output. Note: If the backend runs in a single process, output is handled in a pull fashion, instead of using requests. */

Added: trunk/rkward/rkward/rbackend/rpackages/rkward/R/internal_debugger.R
===================================================================
--- trunk/rkward/rkward/rbackend/rpackages/rkward/R/internal_debugger.R	                        (rev 0)
+++ trunk/rkward/rkward/rbackend/rpackages/rkward/R/internal_debugger.R	2011-10-20 09:37:56 UTC (rev 3975)
@@ -0,0 +1,19 @@
+# gather debug information.
+# Note: subsequent browser() calls should be suppressed while inside this function!
+.rk.callstack.info <- function () {
+	nframes <- sys.nframe() - 1	# strip this function call
+	calls <- character (0)
+	funs <- character (0)
+	envs <- character (0)
+	locals <- character (0)
+
+	if (nframes > 0) {
+		for (i in 1:nframes) {
+			calls[i] <- as.character (try (paste (deparse (sys.call (i)), collapse="\n")), silent=TRUE)
+			funs[i] <- as.character (try (paste (deparse (sys.function (i), control="all"), collapse="\n"), silent=TRUE))
+			envs[i] <- as.character (try (capture.output (print (environment (sys.function (i)))), silent=TRUE))
+			locals[i] <- as.character (try (paste (ls (sys.frame (i), all.names=TRUE), collapse="\n"), silent=TRUE))
+		}
+	}
+	list ("calls"=calls, "functions"=funs, "environments"=envs, "locals"=locals)
+}

Modified: trunk/rkward/rkward/rkward.cpp
===================================================================
--- trunk/rkward/rkward/rkward.cpp	2011-10-19 21:09:36 UTC (rev 3974)
+++ trunk/rkward/rkward/rkward.cpp	2011-10-20 09:37:56 UTC (rev 3975)
@@ -82,6 +82,8 @@
 #include "windows/rktoplevelwindowgui.h"
 #include "windows/rkfilebrowser.h"
 #include "windows/rktoolwindowlist.h"
+#include "windows/rkdebugconsole.h"
+#include "windows/rkcallstackviewer.h"
 #include "rkconsole.h"
 #include "debug.h"
 #include "version.h"
@@ -343,6 +345,15 @@
 	RKHelpSearchWindow::main_help_search = help_search;
 	RKToolWindowList::registerToolWindow (help_search, "helpsearch", RKToolWindowList::Bottom, Qt::AltModifier + Qt::Key_6);
 
+	RKCallstackViewer::_instance = new RKCallstackViewer (0, true);
+	RKCallstackViewer::instance ()->setCaption (i18n ("Debugger Frames"));
+	RKToolWindowList::registerToolWindow (RKCallstackViewer::instance (), "debugframes", RKToolWindowList::Right, Qt::AltModifier + Qt::Key_8);
+
+	// HACK: Creating this _after_ the callstackviewer is important, so the debug console will end up the active window when entering a debug context
+	RKDebugConsole::_instance = new RKDebugConsole (0, true);
+	RKDebugConsole::instance ()->setCaption (i18n ("Debugger Console"));
+	RKToolWindowList::registerToolWindow (RKDebugConsole::instance (), "debugconsole", RKToolWindowList::Nowhere, Qt::AltModifier + Qt::Key_7);
+
 	RKWorkplace::mainWorkplace ()->placeToolWindows ();
 }
 

Modified: trunk/rkward/rkward/windows/CMakeLists.txt
===================================================================
--- trunk/rkward/rkward/windows/CMakeLists.txt	2011-10-19 21:09:36 UTC (rev 3974)
+++ trunk/rkward/rkward/windows/CMakeLists.txt	2011-10-20 09:37:56 UTC (rev 3975)
@@ -4,6 +4,8 @@
 
 SET(windows_STAT_SRCS
 	rkcommandeditorwindow.cpp
+	rkdebugconsole.cpp
+	rkcallstackviewer.cpp
 	rkhtmlwindow.cpp
 	rcontrolwindow.cpp
 	detachedwindowcontainer.cpp

Added: trunk/rkward/rkward/windows/rkcallstackviewer.cpp
===================================================================
--- trunk/rkward/rkward/windows/rkcallstackviewer.cpp	                        (rev 0)
+++ trunk/rkward/rkward/windows/rkcallstackviewer.cpp	2011-10-20 09:37:56 UTC (rev 3975)
@@ -0,0 +1,151 @@
+/***************************************************************************
+                          rkcallstackviewer  -  description
+                             -------------------
+    begin                : Wed Oct 19 2011
+    copyright            : (C) 2011 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 "rkcallstackviewer.h"
+
+#include <klocale.h>
+#include <kvbox.h>
+
+#include <QHBoxLayout>
+#include <QVBoxLayout>
+#include <QLabel>
+#include <QListWidget>
+#include <QTextDocument>
+
+#include "../misc/rkdummypart.h"
+#include "../agents/rkdebughandler.h"
+#include "rkcommandeditorwindow.h"
+
+#include "../debug.h"
+
+RKCallstackViewer* RKCallstackViewer::_instance = 0;
+
+RKCallstackViewer::RKCallstackViewer (QWidget *parent, bool tool_window, const char *name) : RKMDIWindow (parent, RKMDIWindow::CallstackViewerWindow, tool_window, name) {
+	RK_TRACE (APP);
+
+	real_widget = 0;
+
+	QVBoxLayout *layout = new QVBoxLayout (this);
+	layout->setContentsMargins (0, 0, 0, 0);
+	layout_widget = new KVBox (this);
+	layout->addWidget (layout_widget);
+	layout_widget->setFocusPolicy (Qt::StrongFocus);
+
+	setPart (new RKDummyPart (this, layout_widget));
+	initializeActivationSignals ();
+
+	connect (RKDebugHandler::instance (), SIGNAL (newDebugState()), this, SLOT (newDebugState()));
+}
+
+RKCallstackViewer::~RKCallstackViewer () {
+	RK_TRACE (APP);
+}
+
+void RKCallstackViewer::showEvent (QShowEvent *e) {
+	RK_TRACE (APP);
+
+	createRealWidget ();
+	RKMDIWindow::showEvent (e);
+}
+
+void RKCallstackViewer::createRealWidget () {
+	RK_TRACE (APP);
+
+	if (!real_widget) {
+		RK_DO (qDebug ("creating callstack viewer"), APP, DL_INFO);
+
+		real_widget = new RKCallstackViewerWidget (layout_widget);
+		setFocusProxy (real_widget);
+	}
+}
+
+void RKCallstackViewer::newDebugState () {
+	RK_TRACE (APP);
+
+	if (!real_widget) createRealWidget ();
+	else real_widget->updateState ();
+	if (RKDebugHandler::instance ()->state () == RKDebugHandler::InDebugPrompt) activate ();
+}
+
+
+
+RKCallstackViewerWidget::RKCallstackViewerWidget (QWidget *parent) : QWidget (parent) {
+	RK_TRACE (APP);
+
+	QHBoxLayout *h_layout = new QHBoxLayout (this);
+	h_layout->setContentsMargins (0, 0, 0, 0);
+
+	QVBoxLayout *v_layout = new QVBoxLayout ();
+	h_layout->addLayout (v_layout);
+	h_layout->setStretchFactor (v_layout, 1);
+
+	QLabel *label = new QLabel (i18n ("<b>Active calls</b>"), this);
+	v_layout->addWidget (label);
+	frame_selector = new QListWidget (this);
+	frame_selector->setSelectionMode (QAbstractItemView::SingleSelection);
+	connect (frame_selector, SIGNAL (currentRowChanged(int)), this, SLOT (frameChanged(int)));
+	v_layout->addWidget (frame_selector);
+
+	v_layout = new QVBoxLayout ();
+	h_layout->addLayout (v_layout);
+	h_layout->setStretchFactor (v_layout, 2);
+
+	frame_info = new QLabel (this);
+	frame_info->setWordWrap (true);
+	v_layout->addWidget (frame_info);
+
+	frame_source = new RKCommandEditorWindow (this, true);
+	v_layout->addWidget (frame_source);
+
+	updateState ();
+}
+
+RKCallstackViewerWidget::~RKCallstackViewerWidget () {
+	RK_TRACE (APP);
+}
+
+void RKCallstackViewerWidget::updateState () {
+	RK_TRACE (APP);
+
+	if (RKDebugHandler::instance ()->state () == RKDebugHandler::NotInDebugger) {
+		QString info = i18n ("Not in a debugger context");
+		frame_source->setText (info);
+		frame_selector->clear ();
+		frame_info->setText ("<b>" + info + "</b>");
+	} else if (RKDebugHandler::instance ()->state () == RKDebugHandler::InDebugPrompt) {
+		frame_selector->clear ();
+		frame_selector->setEnabled (true);
+		frame_selector->insertItems (0, RKDebugHandler::instance ()->calls ());
+		frame_selector->setCurrentRow (frame_selector->count () - 1);
+	} else {
+		frame_selector->setEnabled (false);
+	}
+}
+
+void RKCallstackViewerWidget::frameChanged (int frame_number) {
+	RK_TRACE (APP);
+
+	if (RKDebugHandler::instance ()->state () == RKDebugHandler::NotInDebugger) return;
+
+	frame_info->setText (i18n ("<b>Current call:</b> %1<br><b>Environment:</b> %2<br><b>Local objects:</b> %3",
+									Qt::escape (RKDebugHandler::instance ()->calls ().value (frame_number)),
+									Qt::escape (RKDebugHandler::instance ()->environments ().value (frame_number)),
+									Qt::escape (RKDebugHandler::instance ()->locals ().value (frame_number).split ('\n').join (", "))));
+	frame_source->setText (RKDebugHandler::instance ()->functions ().value (frame_number));
+}
+
+#include "rkcallstackviewer.moc"

Added: trunk/rkward/rkward/windows/rkcallstackviewer.h
===================================================================
--- trunk/rkward/rkward/windows/rkcallstackviewer.h	                        (rev 0)
+++ trunk/rkward/rkward/windows/rkcallstackviewer.h	2011-10-20 09:37:56 UTC (rev 3975)
@@ -0,0 +1,65 @@
+/***************************************************************************
+                          rkcallstackviewer  -  description
+                             -------------------
+    begin                : Wed Oct 19 2011
+    copyright            : (C) 2011 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 RKCALLSTACKVIEWER_H
+#define RKCALLSTACKVIEWER_H
+
+#include "rkmdiwindow.h"
+
+class RKCallstackViewerWidget;
+class RKCommandEditorWindow;
+class QListWidget;
+class QLabel;
+
+/** The call stack (tool) window. In order to save some startup time, the widget is not really created until it is first shown. Hence, this is mostly just a wrapper around RKCallstackViewerWidget */
+class RKCallstackViewer : public RKMDIWindow {
+	Q_OBJECT
+public:
+	RKCallstackViewer (QWidget *parent, bool tool_window, const char *name=0);
+	~RKCallstackViewer ();
+
+/** reimplemented to create the real widget only when the viewer is shown for the first time */
+	void showEvent (QShowEvent *e);
+	static RKCallstackViewer *instance () { return _instance; };
+public slots:
+	void newDebugState ();
+private:
+	void createRealWidget ();
+	RKCallstackViewerWidget *real_widget;
+	QWidget *layout_widget;
+friend class RKWardMainWindow;
+	static RKCallstackViewer *_instance;
+};
+
+/** The internal widget used in RKCallstackViewer 
+*/
+class RKCallstackViewerWidget : public QWidget {
+	Q_OBJECT
+public:
+	RKCallstackViewerWidget (QWidget *parent);
+	~RKCallstackViewerWidget ();
+
+	void updateState ();
+private slots:
+	void frameChanged (int frame_number);
+private:
+	QListWidget *frame_selector;
+	QLabel *frame_info;
+	RKCommandEditorWindow *frame_source;
+};
+
+#endif

Modified: trunk/rkward/rkward/windows/rkcommandeditorwindow.cpp
===================================================================
--- trunk/rkward/rkward/windows/rkcommandeditorwindow.cpp	2011-10-19 21:09:36 UTC (rev 3974)
+++ trunk/rkward/rkward/windows/rkcommandeditorwindow.cpp	2011-10-20 09:37:56 UTC (rev 3975)
@@ -483,7 +483,9 @@
 
 void RKCommandEditorWindow::setText (const QString &text) {
 	RK_TRACE (COMMANDEDITOR);
+	m_doc->setReadWrite (true);
 	m_doc->setText (text);
+	m_doc->setReadWrite (false);
 }
 
 void RKCommandEditorWindow::updateCaption (KTextEditor::Document*) {

Added: trunk/rkward/rkward/windows/rkdebugconsole.cpp
===================================================================
--- trunk/rkward/rkward/windows/rkdebugconsole.cpp	                        (rev 0)
+++ trunk/rkward/rkward/windows/rkdebugconsole.cpp	2011-10-20 09:37:56 UTC (rev 3975)
@@ -0,0 +1,151 @@
+/***************************************************************************
+                          rkdebugconsole  -  description
+                             -------------------
+    begin                : Wed Oct 19 2011
+    copyright            : (C) 2011 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 "rkdebugconsole.h"
+
+#include <QPushButton>
+#include <QTextEdit>
+#include <QLineEdit>
+#include <QLabel>
+#include <QVBoxLayout>
+#include <QHBoxLayout>
+
+#include <klocale.h>
+#include <kmessagebox.h>
+
+#include "../agents/rkdebughandler.h"
+#include "../misc/rkdummypart.h"
+
+#include "../debug.h"
+
+RKDebugConsole* RKDebugConsole::_instance = 0;
+
+RKDebugConsole::RKDebugConsole (QWidget *parent, bool tool_window, const char *name) : RKMDIWindow (parent, DebugConsoleWindow, tool_window, name) {
+	RK_TRACE (APP);
+
+	QVBoxLayout *main_layout = new QVBoxLayout (this);
+	main_layout->setContentsMargins (0, 0, 0, 0);
+	QHBoxLayout *upper_layout = new QHBoxLayout ();
+	main_layout->addLayout (upper_layout);
+
+	context_view = new QTextEdit (this);
+	context_view->setReadOnly (true);
+	context_view->setAcceptRichText (false);
+	upper_layout->addWidget (context_view);
+
+	QVBoxLayout *button_layout = new QVBoxLayout ();
+	upper_layout->addLayout (button_layout);
+	step_button = new QPushButton (i18n ("Next"), this);
+	connect (step_button, SIGNAL (clicked()), this, SLOT (stepButtonClicked()));
+	button_layout->addWidget (step_button);
+	continue_button = new QPushButton (i18n ("Continue"), this);
+	connect (continue_button, SIGNAL (clicked()), this, SLOT (continueButtonClicked()));
+	button_layout->addWidget (continue_button);
+	cancel_button = new QPushButton (i18n ("Cancel"), this);
+	connect (cancel_button, SIGNAL (clicked()), this, SLOT (cancelButtonClicked()));
+	button_layout->addWidget (cancel_button);
+	button_layout->addStretch ();
+
+	QHBoxLayout *lower_layout = new QHBoxLayout ();
+	main_layout->addLayout (lower_layout);
+
+	prompt_label = new QLabel (this);
+	lower_layout->addWidget (prompt_label);
+	reply_edit = new QLineEdit (this);
+	connect (reply_edit, SIGNAL (returnPressed()), this, SLOT (sendReply()));
+	lower_layout->addWidget (reply_edit);
+	setFocusProxy (reply_edit);
+
+	setFocusPolicy (Qt::StrongFocus);
+
+	setPart (new RKDummyPart (this, this));
+	initializeActivationSignals ();
+
+	connect (RKDebugHandler::instance (), SIGNAL (newDebugState()), this, SLOT (newDebugState()));
+	newDebugState ();
+}
+
+RKDebugConsole::~RKDebugConsole () {
+	RK_TRACE (APP);
+}
+
+void RKDebugConsole::newDebugState () {
+	RK_TRACE (APP);
+
+	bool enable = true;
+	if (RKDebugHandler::instance ()->state () == RKDebugHandler::NotInDebugger) {
+		context_view->setPlainText (i18n ("Not in a debugger context"));
+		setEnabled (false);
+		return;
+	} else if (RKDebugHandler::instance ()->state () == RKDebugHandler::InDebugRun) {
+		enable = false;
+		reply_edit->setEnabled (false);
+	} else {
+		context_view->setPlainText (RKDebugHandler::instance ()->outputContext ());
+		prompt_label->setText (RKDebugHandler::instance ()->debugPrompt ());
+		reply_edit->setEnabled (true);	// must come before focus
+		activate (true);
+	}
+
+	setEnabled (true);
+	step_button->setEnabled (enable);
+	continue_button->setEnabled (enable);
+	cancel_button->setEnabled (enable);
+}
+
+void RKDebugConsole::sendReply () {
+	RK_TRACE (APP);
+
+	sendReply (reply_edit->text ());
+	reply_edit->clear ();
+}
+
+void RKDebugConsole::stepButtonClicked () {
+	RK_TRACE (APP);
+
+	sendReply ("n\n");
+}
+
+void RKDebugConsole::continueButtonClicked () {
+	RK_TRACE (APP);
+
+	sendReply ("cont\n");
+}
+
+void RKDebugConsole::cancelButtonClicked () {
+	RK_TRACE (APP);
+
+	sendReply ("Q\n");
+}
+
+void RKDebugConsole::sendReply (const QString &reply) {
+	RK_TRACE (APP);
+
+	RKDebugHandler::instance ()->submitDebugString (reply);
+}
+
+bool RKDebugConsole::close (bool also_delete) {
+	RK_TRACE (APP);
+
+	if (RKDebugHandler::instance ()->state () != RKDebugHandler::NotInDebugger) {
+		KMessageBox::sorry (this, i18n ("This window cannot be closed, while a debugger is active. If you have no idea what this means, and you want to get out, press the 'Cancel' button on the right hand side of this window."));
+		return false;
+	}
+	return RKMDIWindow::close (also_delete);
+}
+
+#include "rkdebugconsole.moc"

Added: trunk/rkward/rkward/windows/rkdebugconsole.h
===================================================================
--- trunk/rkward/rkward/windows/rkdebugconsole.h	                        (rev 0)
+++ trunk/rkward/rkward/windows/rkdebugconsole.h	2011-10-20 09:37:56 UTC (rev 3975)
@@ -0,0 +1,61 @@
+/***************************************************************************
+                          rkdebugconsole  -  description
+                             -------------------
+    begin                : Wed Oct 19 2011
+    copyright            : (C) 2011 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 RKDEBUGCONSOLE_H
+#define RKDEBUGCONSOLE_H
+
+#include "rkmdiwindow.h"
+
+class QPushButton;
+class QLineEdit;
+class QTextEdit;
+class QLabel;
+
+/** A very simple debugger console */
+class RKDebugConsole : public RKMDIWindow {
+	Q_OBJECT
+public:
+	RKDebugConsole (QWidget *parent, bool tool_window, const char *name=0);
+	~RKDebugConsole ();
+
+	static RKDebugConsole *instance () { return _instance; };
+
+	// reimplemented to refuse closing while inside the debugger
+	bool close (bool auto_delete);
+public slots:
+	void newDebugState ();
+private slots:
+	void sendReply ();
+	void stepButtonClicked ();
+	void continueButtonClicked ();
+	void cancelButtonClicked ();
+private:
+	void sendReply (const QString &reply);
+
+	QTextEdit* context_view;
+	QLineEdit* reply_edit;
+	QLabel* prompt_label;
+
+	QPushButton* step_button;
+	QPushButton* continue_button;
+	QPushButton* cancel_button;
+
+friend class RKWardMainWindow;
+	static RKDebugConsole *_instance;
+};
+
+#endif

Modified: trunk/rkward/rkward/windows/rkmdiwindow.cpp
===================================================================
--- trunk/rkward/rkward/windows/rkmdiwindow.cpp	2011-10-19 21:09:36 UTC (rev 3974)
+++ trunk/rkward/rkward/windows/rkmdiwindow.cpp	2011-10-20 09:37:56 UTC (rev 3975)
@@ -152,7 +152,12 @@
 		if (!isAttached ()) {
 			topLevelWidget ()->deleteLater ();
 			// flee the dying DetachedWindowContainer
-			RKWorkplace::mainWorkplace ()->attachWindow (this);
+			if (tool_window_bar) RKWorkplace::mainWorkplace ()->attachWindow (this);
+			else {
+				state = Attached;
+				hide ();
+				setParent (0);
+			}
 		}
 
 		if (tool_window_bar) tool_window_bar->hideWidget (this);

Modified: trunk/rkward/rkward/windows/rkmdiwindow.h
===================================================================
--- trunk/rkward/rkward/windows/rkmdiwindow.h	2011-10-19 21:09:36 UTC (rev 3974)
+++ trunk/rkward/rkward/windows/rkmdiwindow.h	2011-10-20 09:37:56 UTC (rev 3975)
@@ -53,9 +53,11 @@
 		SearchHelpWindow=1 << 13,
 		PendingJobsWindow=1 << 14,
 		FileBrowserWindow=1 << 15,
+		DebugConsoleWindow=1 << 16,
+		CallstackViewerWindow=1 << 17,
 
-		DocumentWindow=1 << 20,
-		ToolWindow=1 << 21,
+		DocumentWindow=1 << 29,
+		ToolWindow=1 << 30,
 		AnyType=DocumentWindow | ToolWindow
 	};
 

Modified: trunk/rkward/rkward/windows/rktoolwindowbar.cpp
===================================================================
--- trunk/rkward/rkward/windows/rktoolwindowbar.cpp	2011-10-19 21:09:36 UTC (rev 3974)
+++ trunk/rkward/rkward/windows/rktoolwindowbar.cpp	2011-10-20 09:37:56 UTC (rev 3975)
@@ -170,6 +170,11 @@
 	widget_to_id.remove (widget);
 	widget->tool_window_bar = 0;
 
+	if (widget->isAttached ()) {
+		widget->setParent (0);
+		widget->hide ();
+	}
+
 	if (was_active_in_bar) {
 		RK_ASSERT (widget->isAttached ());
 		container->hide ();
@@ -237,7 +242,8 @@
 	RK_ASSERT (widget);
 
 	if (widget->isActive ()) {
-		widget->close (false);
+		if (!widget->isAttached ()) widget->close (false);
+		else hideWidget (widget);
 	} else {
 		widget->activate (true);
 	}

Modified: trunk/rkward/rkward/windows/rktoplevelwindowgui.rc
===================================================================
--- trunk/rkward/rkward/windows/rktoplevelwindowgui.rc	2011-10-19 21:09:36 UTC (rev 3974)
+++ trunk/rkward/rkward/windows/rktoplevelwindowgui.rc	2011-10-20 09:37:56 UTC (rev 3975)
@@ -1,5 +1,5 @@
 <!DOCTYPE kpartgui>
-<kpartgui name="rkward_toplevel" version="560">
+<kpartgui name="rkward_toplevel" version="580">
 	<MenuBar>
 		<Merge/>
 		<Menu name="window"><text>&Window</text>
@@ -16,6 +16,8 @@
 				<Action name="window_show_pendingjobs"/>
 				<Action name="window_show_console"/>
 				<Action name="window_show_helpsearch"/>
+				<Action name="window_show_debugconsole"/>
+				<Action name="window_show_debugframes"/>
 				<Separator/>
 				<Action name="window_activate_docview"/>
 			</Menu>

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