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

tfry at users.sourceforge.net tfry at users.sourceforge.net
Wed May 11 17:51:25 UTC 2011


Revision: 3547
          http://rkward.svn.sourceforge.net/rkward/?rev=3547&view=rev
Author:   tfry
Date:     2011-05-11 17:51:25 +0000 (Wed, 11 May 2011)

Log Message:
-----------
Add 'carbon copy' feature.

Modified Paths:
--------------
    trunk/rkward/ChangeLog
    trunk/rkward/rkward/plugin/rkstandardcomponentgui.cpp
    trunk/rkward/rkward/rbackend/rcommand.h
    trunk/rkward/rkward/rbackend/rcommandstack.cpp
    trunk/rkward/rkward/rbackend/rinterface.cpp
    trunk/rkward/rkward/rbackend/rinterface.h
    trunk/rkward/rkward/rbackend/rkrbackend.cpp
    trunk/rkward/rkward/rbackend/rkrbackend.h
    trunk/rkward/rkward/settings/rksettingsmoduleoutput.cpp
    trunk/rkward/rkward/settings/rksettingsmoduleoutput.h
    trunk/rkward/rkward/windows/rkwindowcatcher.cpp

Modified: trunk/rkward/ChangeLog
===================================================================
--- trunk/rkward/ChangeLog	2011-05-11 17:50:33 UTC (rev 3546)
+++ trunk/rkward/ChangeLog	2011-05-11 17:51:25 UTC (rev 3547)
@@ -1,7 +1,8 @@
+- R commands and their output can be "carbon copied" to the output window		# TODO: Add a dialog for this in the 'Run' Menu
 - On Windows, RKWard will detect, and offer to disable "native" file system dialogs
 - Object browsers that show only the global environment now hide the ".GlobalEnv" item
 - Fixed: Function argument hints would not be shown in some corner cases
-- Added function rk.print.code() to write highlighted R code to the output window		# TODO: Next, implement the 'carbon copy' feature
+- Added function rk.print.code() to write highlighted R code to the output window
 - Box plot plugin gains support for grouped outcome data
 - Fixed: Pressing Ctrl+C would not reset syntactically incomplete commands in the R console
 - Crosstabs N to 1 plugin gains options to compute proportions and margins (thanks to Andrés Necochea)

Modified: trunk/rkward/rkward/plugin/rkstandardcomponentgui.cpp
===================================================================
--- trunk/rkward/rkward/plugin/rkstandardcomponentgui.cpp	2011-05-11 17:50:33 UTC (rev 3546)
+++ trunk/rkward/rkward/plugin/rkstandardcomponentgui.cpp	2011-05-11 17:51:25 UTC (rev 3547)
@@ -160,7 +160,7 @@
 	command.append (code_property->calculate ());
 	command.append (code_property->printout ());
 	command.append ("})\n");
-	RKGlobals::rInterface ()->issueCommand (new RCommand (command, RCommand::Plugin | RCommand::DirectToOutput | RCommand::ObjectListUpdate), component->commandChain ());
+	RKGlobals::rInterface ()->issueCommand (new RCommand (command, RCommand::Plugin | RCommand::CCOutput | RCommand::ObjectListUpdate), component->commandChain ());
 
 	// re-run link
 	// This should be run in a separate command, in case the above command bails out with an error. Even in that case, the re-run link should be printed.
@@ -172,7 +172,7 @@
 	}
 	// separator line
 	command.append (".rk.make.hr()\n");
-	RKGlobals::rInterface ()->issueCommand (new RCommand (command, RCommand::Plugin | RCommand::DirectToOutput | RCommand::ObjectListUpdate), component->commandChain ());
+	RKGlobals::rInterface ()->issueCommand (new RCommand (command, RCommand::Plugin | RCommand::ObjectListUpdate | RCommand::Silent), component->commandChain ());
 
 	if (auto_close_box->isChecked ()) cancel ();
 }

Modified: trunk/rkward/rkward/rbackend/rcommand.h
===================================================================
--- trunk/rkward/rkward/rbackend/rcommand.h	2011-05-11 17:50:33 UTC (rev 3546)
+++ trunk/rkward/rkward/rbackend/rcommand.h	2011-05-11 17:51:25 UTC (rev 3547)
@@ -68,6 +68,7 @@
 QString is, that additionally we store information on whether the output was "normal", "warning", or an "error". */
 struct ROutput {
 	enum ROutputType {
+		NoOutput,		/**< No output. Rarely used. */
 		Output,			/**< normal output */
 		Warning,		/**< R warning */
 		Error			/**< R error */
@@ -135,19 +136,21 @@
 	although partially exclusive. See \ref UsingTheInterfaceToR for a overview of what these are used for. TODO: find out, why Canceled is in here, and document that fact. */
 	enum CommandTypes {
 		User=1,		/**< Command was created directly by the user (e.g. in the console or in a command editor window) */
-		Plugin=2,		/**< Command comes from a plugin */
-		App=8,			/**< Command comes from the application (e.g. loading / saving the workspace */
-		Sync=16,		/**< Command is used to sync data to or from R-space. Typically used in the editor classes */
-		EmptyCommand=32,		/**< Command is empty and will not be processed (an empty command may be used as a "marker") */
-		Console=64,	/**< Command originated in the console. These commands will get some extra treatment in RKwatch */
-		Internal=128,	/**< Command is meant to be used in the backend, only. Do not use outside rbackend classes! */
-		GetIntVector=512,			/**< Try to fetch result as an array of integers */
-		GetStringVector=1024,	/**< Try to fetch result as an array of chars */
-		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 */
-		QuitCommand=32768		/**< The R backend should be killed */
+		Plugin=1 << 1,		/**< Command comes from a plugin */
+		App=1 << 2,			/**< Command comes from the application (e.g. loading / saving the workspace */
+		Sync=1 << 3,		/**< Command is used to sync data to or from R-space. Typically used in the editor classes */
+		EmptyCommand=1 << 4,		/**< Command is empty and will not be processed (an empty command may be used as a "marker") */
+		Console=1 << 5,	/**< Command originated in the console. These commands will get some extra treatment in RKwatch */
+		Internal=1 << 6,	/**< Command is meant to be used in the backend, only. Do not use outside rbackend classes! */
+		Silent=1 << 7,	/**< Command can be interrupted, but is otherwise an internal command. In particular it should not be carbon copied to the output. */
+		GetIntVector=1 << 8,			/**< Try to fetch result as an array of integers */
+		GetStringVector=1 << 9,	/**< Try to fetch result as an array of chars */
+		GetRealVector=1 << 10,		/**< Try to fetch result as an array of doubles */
+		GetStructuredData=1 << 11,		/**< Try to fetch result as an RData structure */
+		CCOutput=1 << 12,		/**< Append command output to the HTML-output file */
+		CCCommand=1 << 13,		/**< Append the command itself to the HTML-output file */
+		ObjectListUpdate=1 << 14,		/**< The command may change the list of objects available. Do an update */
+		QuitCommand=1 << 15		/**< The R backend should be killed */
 	};
 	enum CommandStatus {
 		Running=1,						/**< command is currently running */

Modified: trunk/rkward/rkward/rbackend/rcommandstack.cpp
===================================================================
--- trunk/rkward/rkward/rbackend/rcommandstack.cpp	2011-05-11 17:50:33 UTC (rev 3546)
+++ trunk/rkward/rkward/rbackend/rcommandstack.cpp	2011-05-11 17:51:25 UTC (rev 3547)
@@ -344,7 +344,7 @@
 			if (command->type () & RCommand::Sync) ret += 'S';
 			if (command->type () & RCommand::EmptyCommand) ret += 'E';
 			if (command->type () & (RCommand::GetIntVector | RCommand::GetRealVector | RCommand::GetStringVector | RCommand::GetStructuredData)) ret += 'D';
-			if (command->type () & RCommand::DirectToOutput) ret += 'O';
+			if (command->type () & RCommand::CCOutput) ret += 'O';
 			return (ret);
 		}
 		if ((index.column () == STATUS_COL) && (role == Qt::DisplayRole)) {

Modified: trunk/rkward/rkward/rbackend/rinterface.cpp
===================================================================
--- trunk/rkward/rkward/rbackend/rinterface.cpp	2011-05-11 17:50:33 UTC (rev 3546)
+++ trunk/rkward/rkward/rbackend/rinterface.cpp	2011-05-11 17:51:25 UTC (rev 3547)
@@ -95,6 +95,8 @@
 	previously_idle = false;
 	locked = 0;
 	backend_dead = false;
+	num_active_output_record_requests = 0;
+	previous_output_type = ROutput::NoOutput;
 
 	// create a fake init command
 	RCommand *fake = new RCommand (i18n ("R Startup"), RCommand::App | RCommand::Sync | RCommand::ObjectListUpdate, i18n ("R Startup"), this, STARTUP_PHASE2_COMPLETE);
@@ -116,6 +118,7 @@
 RInterface::~RInterface(){
 	RK_TRACE (RBACKEND);
 
+	if (num_active_output_record_requests) RK_DO (qDebug ("%d requests for recording output still active on interface shutdown", num_active_output_record_requests), RBACKEND, DL_WARNING);
 	delete flush_timer;
 	delete window_catcher;
 }
@@ -123,9 +126,7 @@
 bool RInterface::backendIsIdle () {
 	RK_TRACE (RBACKEND);
 
-	bool idle;
-	idle = (RCommandStack::regular_stack->isEmpty() && (!runningCommand()));
-	return (idle);
+	return (RCommandStack::regular_stack->isEmpty() && (!runningCommand()));
 }
 
 RCommand *RInterface::popPreviousCommand () {
@@ -362,6 +363,23 @@
 			RK_DO (qDebug ("output '%s'", qPrintable (output->output)), RBACKEND, DL_DEBUG);
 		}
 
+		if (num_active_output_record_requests) {
+			if (output->type >= previous_output_type) {
+				if (!recorded_output.isEmpty ()) recorded_output.append ("</pre>\n");
+
+				if (output->type == ROutput::Output) recorded_output.append ("<pre class=\"output_normal\">");
+				else if (output->type == ROutput::Warning) recorded_output.append ("<pre class=\"output_warning\">");
+				else if (output->type == ROutput::Error) recorded_output.append ("<pre class=\"output_error\">");
+				else {
+					RK_ASSERT (false);
+					recorded_output.append ("<pre>");
+				}
+
+				previous_output_type = output->type;
+			}
+			recorded_output.append (Qt::escape (output->output));
+		}
+
 		bool first = true;
 		foreach (RCommand* command, all_current_commands) {
 			ROutput *coutput = output;
@@ -398,6 +416,10 @@
 	RK_TRACE (RBACKEND);
 
 	if (command->command ().isEmpty ()) command->_type |= RCommand::EmptyCommand;
+	if (RKCarbonCopySettings::shouldCarbonCopyCommand (command)) {
+		command->_type |= RCommand::CCCommand;
+		if (RKCarbonCopySettings::includeOutputInCarbonCopy ()) command->_type |= RCommand::CCOutput;
+	}
 	RCommandStack::issueCommand (command, chain);
 	tryNextCommand ();
 }
@@ -539,6 +561,20 @@
 				}
 			}
 		}
+	} else if (call == "recordOutput") {
+		// NOTE: requests to record output can overlap (i.e. several can be active at the same time). However, we always clear the buffer, each time a request ends, i.e. then
+		// recorded output does NOT overlap.
+		if (calllist.value (1) == "end") {
+			RK_ASSERT (num_active_output_record_requests > 0);
+			--num_active_output_record_requests;
+			QString dummy = recorded_output;
+			recorded_output.clear ();
+			if (!dummy.isEmpty ()) dummy.append ("</pre>\n");
+			previous_output_type = ROutput::NoOutput;
+			return QStringList (dummy);
+		} else {
+			++num_active_output_record_requests;
+		}
 	} else {
 		return (QStringList ("Error: unrecognized request '" + call + "'."));
 	}

Modified: trunk/rkward/rkward/rbackend/rinterface.h
===================================================================
--- trunk/rkward/rkward/rbackend/rinterface.h	2011-05-11 17:50:33 UTC (rev 3546)
+++ trunk/rkward/rkward/rbackend/rinterface.h	2011-05-11 17:51:25 UTC (rev 3547)
@@ -121,6 +121,9 @@
 
 	QString startup_errors;
 	bool startup_phase2_error;
+	int num_active_output_record_requests;
+	ROutput::ROutputType previous_output_type;
+	QString recorded_output;
 friend class RKRBackendProtocolFrontend;
 	bool backend_dead;
 	static double na_real;

Modified: trunk/rkward/rkward/rbackend/rkrbackend.cpp
===================================================================
--- trunk/rkward/rkward/rbackend/rkrbackend.cpp	2011-05-11 17:50:33 UTC (rev 3546)
+++ trunk/rkward/rkward/rbackend/rkrbackend.cpp	2011-05-11 17:51:25 UTC (rev 3547)
@@ -301,6 +301,7 @@
 					}
 					if (incomplete) RKRBackend::this_pointer->current_command->status |= RCommand::Failed | RCommand::ErrorIncomplete;
 					RKRBackend::repl_status.user_command_status = RKRBackend::RKReplStatus::ReplIterationKilled;
+					if (RKRBackend::RKRBackend::repl_status.user_command_parsed_up_to <= 0) RKRBackend::this_pointer->startOutputCapture ();	// HACK: No capture active, but commandFinished() will try to end one
 					Rf_error ("");	// to discard the buffer
 				} else {
 					RKTransmitNextUserCommandChunk (buf, buflen);
@@ -309,6 +310,7 @@
 			} else if (RKRBackend::repl_status.user_command_status == RKRBackend::RKReplStatus::UserCommandSyntaxError) {
 				RKRBackend::this_pointer->current_command->status |= RCommand::Failed | RCommand::ErrorSyntax;
 				RKRBackend::repl_status.user_command_status = RKRBackend::RKReplStatus::NoUserCommand;
+				if (RKRBackend::RKRBackend::repl_status.user_command_parsed_up_to <= 0) RKRBackend::this_pointer->startOutputCapture ();	// HACK: No capture active, but commandFinished() will try to end one
 				RKRBackend::this_pointer->commandFinished ();
 			} else if (RKRBackend::repl_status.user_command_status == RKRBackend::RKReplStatus::UserCommandRunning) {
 				// This can mean three different things:
@@ -398,7 +400,6 @@
 		}
 	}
 
-	if (RKRBackend::this_pointer->capturing_messages) RKRBackend::this_pointer->captured_messages.append (RKRBackend::this_pointer->current_locale_codec->toUnicode (buf, buflen));
 	RKRBackend::this_pointer->handleOutput (RKRBackend::this_pointer->current_locale_codec->toUnicode (buf, buflen), buflen, type == 0 ? ROutput::Output : ROutput::Warning);
 }
 
@@ -647,6 +648,15 @@
 	// R_ReplIteration calls R_Busy (1) after reading in code (if needed), successfully parsing it, and right before evaluating it.
 	if (busy) {
 		if (RKRBackend::repl_status.user_command_status == RKRBackend::RKReplStatus::UserCommandTransmitted) {
+			if (RKRBackend::this_pointer->current_command->type & RCommand::CCCommand) {
+				QByteArray chunk = RKRBackend::repl_status.user_command_buffer.mid (RKRBackend::repl_status.user_command_parsed_up_to, RKRBackend::repl_status.user_command_transmitted_up_to - RKRBackend::repl_status.user_command_parsed_up_to);
+				RKRBackend::this_pointer->printCommand (RKRBackend::this_pointer->current_locale_codec->toUnicode (chunk));
+			}
+			if (RKRBackend::this_pointer->current_command->type & RCommand::CCOutput) {
+				// flush any previous output caputre and start a new one
+				if (RKRBackend::repl_status.user_command_successful_up_to > 0) RKRBackend::this_pointer->printAndClearCapturedMessages (false);
+				RKRBackend::this_pointer->startOutputCapture ();
+			}
 			RKRBackend::repl_status.user_command_parsed_up_to = RKRBackend::repl_status.user_command_transmitted_up_to;
 			RKRBackend::repl_status.user_command_status = RKRBackend::RKReplStatus::UserCommandRunning;
 		}
@@ -660,7 +670,6 @@
 
 	current_locale_codec = QTextCodec::codecForLocale ();
 	r_running = false;
-	capturing_messages = false;
 
 	current_command = 0;
 
@@ -1126,10 +1135,8 @@
 	// running user commands is quite different from all other commands and should have been handled by RReadConsole
 	RK_ASSERT (!(ctype & RCommand::User));
 
-	if (ctype & RCommand::DirectToOutput) {
-		printAndClearCapturedMessages ();
-		capturing_messages = true;
-	}
+	if (ctype & RCommand::CCCommand) printCommand (command->command);
+	if (ctype & RCommand::CCOutput) startOutputCapture ();
 
 	if (ctype & RCommand::QuitCommand) {
 		R_dot_Last ();		// should run while communication with frontend is still possible
@@ -1161,8 +1168,6 @@
 		repl_status.eval_depth--;
 	}
 
-	if (ctype & RCommand::DirectToOutput) printAndClearCapturedMessages ();
-
 	// common error/status handling
 	if (error != NoError) {
 		command->status |= RCommand::WasTried | RCommand::Failed;
@@ -1178,18 +1183,34 @@
 	}
 }
 
-void RKRBackend::printAndClearCapturedMessages () {
+void RKRBackend::printCommand (const QString &command) {
 	RK_TRACE (RBACKEND);
 
-	if (!captured_messages.isEmpty ()) {
-		runDirectCommand (".rk.cat.output (\"<h2>Messages, warnings, or errors:</h2>\\n\")");
-		runDirectCommand ("rk.print.literal (" + RKRSharedFunctionality::quote (captured_messages) + ")");
-		captured_messages.clear ();
-	}
+	QStringList params ("highlightRCode");
+	params.append (command);
+	QString highlighted = handlePlainGenericRequest (params, true).value (0);
+	runDirectCommand (".rk.cat.output (" + RKRSharedFunctionality::quote (highlighted) + ")");
+}
 
-	capturing_messages = false;
+void RKRBackend::startOutputCapture () {
+	RK_TRACE (RBACKEND);
+
+	// TODO: One of those days, we need to revisit output handling. This request could be perfectly async, but unfortunately, in that case, output chunks can sneak in front of it.
+	handlePlainGenericRequest (QStringList ("recordOutput"), true);
 }
 
+void RKRBackend::printAndClearCapturedMessages (bool with_header) {
+	RK_TRACE (RBACKEND);
+
+	QStringList params ("recordOutput");
+	params.append ("end");
+	QString out = handlePlainGenericRequest (params, true).value (0);
+
+	if (out.isEmpty ()) return;
+	if (with_header) runDirectCommand (".rk.cat.output (\"<h2>Messages, warnings, or errors:</h2>\\n\")");
+	runDirectCommand (".rk.cat.output (" + RKRSharedFunctionality::quote (out) + ")");
+}
+
 void RKRBackend::run () {
 	RK_TRACE (RBACKEND);
 	killed = NotKilled;
@@ -1210,6 +1231,7 @@
 	}
 	clearPendingInterrupt ();	// Mutex must be unlocked for this!
 
+	if (current_command->type & RCommand::CCOutput) printAndClearCapturedMessages (current_command->type & RCommand::Plugin);
 	current_command->status -= (current_command->status & RCommand::Running);
 	current_command->status |= RCommand::WasTried;
 

Modified: trunk/rkward/rkward/rbackend/rkrbackend.h
===================================================================
--- trunk/rkward/rkward/rbackend/rkrbackend.h	2011-05-11 17:50:33 UTC (rev 3546)
+++ trunk/rkward/rkward/rbackend/rkrbackend.h	2011-05-11 17:51:25 UTC (rev 3547)
@@ -174,9 +174,9 @@
 	void run ();
 	static void scheduleInterrupt ();
 
-	bool capturing_messages;
-	QString captured_messages;
-	void printAndClearCapturedMessages ();
+	void startOutputCapture ();
+	void printAndClearCapturedMessages (bool with_header);
+	void printCommand (const QString &command);
 
 	QMutex all_current_commands_mutex;
 	QList<RCommandProxy*> current_commands_to_cancel;

Modified: trunk/rkward/rkward/settings/rksettingsmoduleoutput.cpp
===================================================================
--- trunk/rkward/rkward/settings/rksettingsmoduleoutput.cpp	2011-05-11 17:50:33 UTC (rev 3546)
+++ trunk/rkward/rkward/settings/rksettingsmoduleoutput.cpp	2011-05-11 17:51:25 UTC (rev 3547)
@@ -2,7 +2,7 @@
                           rksettingsmoduleoutput  -  description
                              -------------------
     begin                : Fri Jul 30 2004
-    copyright            : (C) 2004, 2010 by Thomas Friedrichsmeier
+    copyright            : (C) 2004, 2010, 2011 by Thomas Friedrichsmeier
     email                : tfry at users.sourceforge.net
  ***************************************************************************/
 
@@ -33,6 +33,115 @@
 #include "../debug.h"
 
 // static members
+bool RKCarbonCopySettings::cc_globally_enabled;
+bool RKCarbonCopySettings::cc_console_commands;
+bool RKCarbonCopySettings::cc_script_commands;
+bool RKCarbonCopySettings::cc_app_plugin_commands;
+bool RKCarbonCopySettings::cc_command_output;
+QList<RKCarbonCopySettings*> RKCarbonCopySettings::instances;
+
+RKCarbonCopySettings::RKCarbonCopySettings (QWidget* parent) : QWidget (parent) {
+	RK_TRACE (SETTINGS);
+
+	QVBoxLayout *main_vbox = new QVBoxLayout (this);
+	cc_globally_enabled_box = new QGroupBox (i18n ("Carbon copy commands to output"), this);
+	cc_globally_enabled_box->setCheckable (true);
+	connect (cc_globally_enabled_box, SIGNAL (clicked(bool)), this, SLOT (settingChanged()));
+	main_vbox->addWidget (cc_globally_enabled_box);
+
+	QVBoxLayout *group_layout = new QVBoxLayout (cc_globally_enabled_box);
+	cc_console_commands_box = new QCheckBox (i18n ("Commands entered in the console"), cc_globally_enabled_box);
+	connect (cc_console_commands_box, SIGNAL (clicked(bool)), this, SLOT (settingChanged()));
+	group_layout->addWidget (cc_console_commands_box);
+
+	cc_script_commands_box = new QCheckBox (i18n ("Commands run via the 'Run' menu"), cc_globally_enabled_box);
+	connect (cc_script_commands_box, SIGNAL (clicked(bool)), this, SLOT (settingChanged()));
+	group_layout->addWidget (cc_script_commands_box);
+
+	cc_app_plugin_commands_box = new QCheckBox (i18n ("Commands originating from dialogs and plugins"), cc_globally_enabled_box);
+	connect (cc_app_plugin_commands_box, SIGNAL (clicked(bool)), this, SLOT (settingChanged()));
+	group_layout->addWidget (cc_app_plugin_commands_box);
+
+	cc_command_output_box = new QCheckBox (i18n ("Also carbon copy the command ouptut"), cc_globally_enabled_box);
+	connect (cc_command_output_box, SIGNAL (clicked(bool)), this, SLOT (settingChanged()));
+	group_layout->addWidget (cc_command_output_box);
+
+	cc_globally_enabled_box->setLayout (group_layout);
+
+	update ();
+	instances.append (this);
+}
+
+RKCarbonCopySettings::~RKCarbonCopySettings () {
+	RK_TRACE (SETTINGS);
+
+	instances.removeAll (this);
+}
+
+void RKCarbonCopySettings::update() {
+	RK_TRACE (SETTINGS);
+
+	cc_globally_enabled_box->setChecked (cc_globally_enabled);
+	cc_console_commands_box->setChecked (cc_console_commands);
+	cc_script_commands_box->setChecked (cc_script_commands);
+	cc_app_plugin_commands_box->setChecked (cc_app_plugin_commands);
+	cc_command_output_box->setChecked (cc_command_output);
+}
+
+void RKCarbonCopySettings::saveSettings (KConfig *config) {
+	RK_TRACE (SETTINGS);
+
+	KConfigGroup cg = config->group ("Carbon Copy Settings");
+	cg.writeEntry ("CC enabled", cc_globally_enabled);
+	cg.writeEntry ("CC console commands", cc_console_commands);
+	cg.writeEntry ("CC script commands", cc_script_commands);
+	cg.writeEntry ("CC app/plugin commands", cc_app_plugin_commands);
+	cg.writeEntry ("CC command output", cc_command_output);
+}
+
+void RKCarbonCopySettings::loadSettings (KConfig *config) {
+	RK_TRACE (SETTINGS);
+
+	KConfigGroup cg = config->group ("Carbon Copy Settings");
+	cc_globally_enabled = cg.readEntry ("CC enabled", false);
+	cc_console_commands = cg.readEntry ("CC console commands", true);
+	cc_script_commands = cg.readEntry ("CC script commands", false);
+	cc_app_plugin_commands = cg.readEntry ("CC app/plugin commands", false);
+	cc_command_output = cg.readEntry ("CC command output", true);
+}
+
+bool RKCarbonCopySettings::shouldCarbonCopyCommand (const RCommand *command) {
+	RK_TRACE (SETTINGS);
+
+	if (!cc_globally_enabled) return false;
+	if (command->type () & (RCommand::Silent | RCommand::Sync)) return false;
+	if (command->type () & RCommand::Console) return cc_console_commands;
+	if (command->type () & RCommand::User) return cc_script_commands;
+	if (command->type () & (RCommand::App | RCommand::Plugin)) return cc_app_plugin_commands;
+	return false;
+}
+
+void RKCarbonCopySettings::settingChanged () {
+	RK_TRACE (SETTINGS);
+
+	emit (changed ());
+}
+
+void RKCarbonCopySettings::applyChanges() {
+	RK_TRACE (SETTINGS);
+
+	cc_globally_enabled = cc_globally_enabled_box->isChecked ();
+	cc_console_commands = cc_console_commands_box->isChecked ();
+	cc_script_commands = cc_script_commands_box->isChecked ();
+	cc_app_plugin_commands = cc_app_plugin_commands_box->isChecked ();
+	cc_command_output = cc_command_output_box->isChecked ();
+
+	foreach (RKCarbonCopySettings *sibling, instances) {
+		if (sibling != this) sibling->update ();
+	}
+}
+
+// static members
 bool RKSettingsModuleOutput::auto_show;
 bool RKSettingsModuleOutput::auto_raise;
 QString RKSettingsModuleOutput::graphics_type;
@@ -49,11 +158,11 @@
 	QVBoxLayout* group_layout = new QVBoxLayout (group);
 	group_layout->addWidget (auto_show_box = new QCheckBox (i18n ("show window on new output"), group));
 	auto_show_box->setChecked (auto_show);
-	connect (auto_show_box, SIGNAL (stateChanged (int)), this, SLOT (boxChanged (int)));
+	connect (auto_show_box, SIGNAL (stateChanged (int)), this, SLOT (boxChanged()));
 	group_layout->addWidget (auto_raise_box = new QCheckBox (i18n ("raise window on new output"), group));
 	auto_raise_box->setChecked (auto_raise);
 	auto_raise_box->setEnabled (auto_show);
-	connect (auto_raise_box, SIGNAL (stateChanged (int)), this, SLOT (boxChanged (int)));
+	connect (auto_raise_box, SIGNAL (stateChanged (int)), this, SLOT (boxChanged()));
 
 	main_vbox->addWidget (group);
 
@@ -69,12 +178,12 @@
 	graphics_type_box->addItem (i18n ("JPG"), QString ("\"JPG\""));
 	graphics_type_box->setCurrentIndex (graphics_type_box->findData (graphics_type));
 	graphics_type_box->setEditable (false);
-	connect (graphics_type_box, SIGNAL (currentIndexChanged (int)), this, SLOT (boxChanged (int)));
+	connect (graphics_type_box, SIGNAL (currentIndexChanged (int)), this, SLOT (boxChanged()));
 	h_layout->addSpacing (2*RKGlobals::spacingHint ());
 	h_layout->addWidget (new QLabel (i18n ("JPG quality"), group));
 	h_layout->addWidget (graphics_jpg_quality_box = new KIntSpinBox (1, 100, 1, graphics_jpg_quality, group));
 	graphics_jpg_quality_box->setEnabled (graphics_type == "\"JPG\"");
-	connect (graphics_jpg_quality_box, SIGNAL (valueChanged (int)), this, SLOT (boxChanged (int)));
+	connect (graphics_jpg_quality_box, SIGNAL (valueChanged (int)), this, SLOT (boxChanged()));
 	h_layout->addStretch ();
 
 	h_layout = new QHBoxLayout (group);
@@ -85,11 +194,15 @@
 	h_layout->addWidget (new QLabel (i18n ("Height:"), group));
 	h_layout->addWidget (graphics_height_box = new KIntSpinBox (1, INT_MAX, 1, graphics_height, group));
 	h_layout->addStretch ();
-	connect (graphics_width_box, SIGNAL (valueChanged (int)), this, SLOT (boxChanged (int)));
-	connect (graphics_height_box, SIGNAL (valueChanged (int)), this, SLOT (boxChanged (int)));
+	connect (graphics_width_box, SIGNAL (valueChanged (int)), this, SLOT (boxChanged()));
+	connect (graphics_height_box, SIGNAL (valueChanged (int)), this, SLOT (boxChanged()));
 
 	main_vbox->addWidget (group);
 
+	cc_settings = new RKCarbonCopySettings (this);
+	connect (cc_settings, SIGNAL (changed()), this, SLOT (boxChanged()));
+	main_vbox->addWidget (cc_settings);
+
 	main_vbox->addStretch ();
 }
 
@@ -97,7 +210,7 @@
 	RK_TRACE (SETTINGS);
 }
 
-void RKSettingsModuleOutput::boxChanged (int) {
+void RKSettingsModuleOutput::boxChanged () {
 	RK_TRACE (SETTINGS);
 	change ();
 	auto_raise_box->setEnabled (auto_show_box->isChecked ());
@@ -129,6 +242,8 @@
 	for (QStringList::const_iterator it = commands.begin (); it != commands.end (); ++it) {
 		RKGlobals::rInterface ()->issueCommand (*it, RCommand::App, QString::null, 0, 0, commandChain ());
 	}
+
+	cc_settings->applyChanges ();
 }
 
 void RKSettingsModuleOutput::save (KConfig *config) {
@@ -147,6 +262,8 @@
 	cg.writeEntry ("graphics_width", graphics_width);
 	cg.writeEntry ("graphics_height", graphics_height);
 	cg.writeEntry ("graphics_jpg_quality", graphics_jpg_quality);
+
+	RKCarbonCopySettings::saveSettings (config);
 }
 
 void RKSettingsModuleOutput::loadSettings (KConfig *config) {
@@ -159,6 +276,8 @@
 	graphics_width = cg.readEntry ("graphics_width", 480);
 	graphics_height = cg.readEntry ("graphics_height", 480);
 	graphics_jpg_quality = cg.readEntry ("graphics_jpg_quality", 75);
+
+	RKCarbonCopySettings::loadSettings (config);
 }
 
 //static

Modified: trunk/rkward/rkward/settings/rksettingsmoduleoutput.h
===================================================================
--- trunk/rkward/rkward/settings/rksettingsmoduleoutput.h	2011-05-11 17:50:33 UTC (rev 3546)
+++ trunk/rkward/rkward/settings/rksettingsmoduleoutput.h	2011-05-11 17:51:25 UTC (rev 3547)
@@ -2,7 +2,7 @@
                           rksettingsmoduleoutput  -  description
                              -------------------
     begin                : Fri Jul 30 2004
-    copyright            : (C) 2004, 2010 by Thomas Friedrichsmeier
+    copyright            : (C) 2004, 2010, 2011 by Thomas Friedrichsmeier
     email                : tfry at users.sourceforge.net
  ***************************************************************************/
 
@@ -22,12 +22,53 @@
 #include <QStringList>
 
 class QCheckBox;
+class QGroupBox;
 class QComboBox;
 class KIntSpinBox;
+class RCommand;
 
 /**
+Allows to configure which types of commands should be "carbon copied" to the output window. Like the RKSettingsModules classes, this class encapsulates both, the setting itself,
+and a widget to configure the settings.
 @author Thomas Friedrichsmeier
 */
+class RKCarbonCopySettings : public QWidget {
+	Q_OBJECT
+public:
+	RKCarbonCopySettings (QWidget* parent);
+	~RKCarbonCopySettings ();
+
+	static void saveSettings (KConfig *config);
+	static void loadSettings (KConfig *config);
+
+	static bool shouldCarbonCopyCommand (const RCommand *command);
+	static bool includeOutputInCarbonCopy () { return (cc_globally_enabled && cc_command_output); };
+	void applyChanges ();
+signals:
+	void changed ();
+private slots:
+	void settingChanged ();
+private:
+	// There can be multiple instances of this widget, which need to be kept in sync.
+	static QList<RKCarbonCopySettings*> instances;
+	void update ();
+
+	QGroupBox *cc_globally_enabled_box;
+	QCheckBox *cc_console_commands_box;
+	QCheckBox *cc_script_commands_box;
+	QCheckBox *cc_app_plugin_commands_box;
+	QCheckBox *cc_command_output_box;
+
+	static bool cc_globally_enabled;
+	static bool cc_console_commands;
+	static bool cc_script_commands;
+	static bool cc_app_plugin_commands;
+	static bool cc_command_output;
+};
+
+/**
+ at author Thomas Friedrichsmeier
+*/
 class RKSettingsModuleOutput : public RKSettingsModule {
 	Q_OBJECT
 public:
@@ -49,7 +90,7 @@
 	static bool autoShow () { return auto_show; };
 	static bool autoRaise () { return auto_raise; };
 public slots:
-	void boxChanged (int);
+	void boxChanged ();
 private:
 	QCheckBox *auto_show_box;
 	QCheckBox *auto_raise_box;
@@ -57,6 +98,7 @@
 	KIntSpinBox *graphics_width_box;
 	KIntSpinBox *graphics_height_box;
 	KIntSpinBox *graphics_jpg_quality_box;
+	RKCarbonCopySettings *cc_settings;
 
 	static bool auto_show;
 	static bool auto_raise;

Modified: trunk/rkward/rkward/windows/rkwindowcatcher.cpp
===================================================================
--- trunk/rkward/rkward/windows/rkwindowcatcher.cpp	2011-05-11 17:50:33 UTC (rev 3546)
+++ trunk/rkward/rkward/windows/rkwindowcatcher.cpp	2011-05-11 17:51:25 UTC (rev 3547)
@@ -369,7 +369,7 @@
 void RKCaughtX11Window::copyDeviceToOutput () {
 	RK_TRACE (MISC);
 
-	RKGlobals::rInterface ()->issueCommand ("dev.set (" + QString::number (device_number) + ")\ndev.copy (device=rk.graph.on)\nrk.graph.off ()", RCommand::App | RCommand::DirectToOutput, i18n ("Copy contents of graphics device number %1 to output", device_number), error_dialog);
+	RKGlobals::rInterface ()->issueCommand ("dev.set (" + QString::number (device_number) + ")\ndev.copy (device=rk.graph.on)\nrk.graph.off ()", RCommand::App | RCommand::CCOutput, i18n ("Copy contents of graphics device number %1 to output", device_number), error_dialog);
 }
 
 void RKCaughtX11Window::printDevice () {


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