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

tfry at users.sourceforge.net tfry at users.sourceforge.net
Wed Jan 24 17:18:05 UTC 2007


Revision: 1208
          http://svn.sourceforge.net/rkward/?rev=1208&view=rev
Author:   tfry
Date:     2007-01-24 09:18:04 -0800 (Wed, 24 Jan 2007)

Log Message:
-----------
add preview checkbox for graph plugins. Some quirks left

Modified Paths:
--------------
    trunk/rkward/ChangeLog
    trunk/rkward/rkward/plugin/Makefile.am
    trunk/rkward/rkward/plugin/rkcomponent.h
    trunk/rkward/rkward/plugin/rkcomponentproperties.h
    trunk/rkward/rkward/plugin/rkstandardcomponent.cpp
    trunk/rkward/rkward/plugin/rkstandardcomponentgui.cpp
    trunk/rkward/rkward/plugins/plots/histogram.php
    trunk/rkward/rkward/plugins/plots/histogram.xml
    trunk/rkward/rkward/rbackend/rpackages/rkward/DESCRIPTION
    trunk/rkward/rkward/rbackend/rpackages/rkward/R/internal.R
    trunk/rkward/rkward/scriptbackends/phpbackend.cpp
    trunk/rkward/rkward/scriptbackends/phpbackend.h

Added Paths:
-----------
    trunk/rkward/rkward/plugin/rkpreviewbox.cpp
    trunk/rkward/rkward/plugin/rkpreviewbox.h

Modified: trunk/rkward/ChangeLog
===================================================================
--- trunk/rkward/ChangeLog	2007-01-24 15:47:47 UTC (rev 1207)
+++ trunk/rkward/ChangeLog	2007-01-24 17:18:04 UTC (rev 1208)
@@ -1,3 +1,4 @@
+- plugins can now be context sensitive (e.g. work on a particular x11 device)		TODO: document
 - new plugin: Wilcoxon Rank Sum test and Wilcoxon Exact Rank Sum test
 - new plugin: scatterplot matrix
 - new plugin: correlation matrix plot

Modified: trunk/rkward/rkward/plugin/Makefile.am
===================================================================
--- trunk/rkward/rkward/plugin/Makefile.am	2007-01-24 15:47:47 UTC (rev 1207)
+++ trunk/rkward/rkward/plugin/Makefile.am	2007-01-24 17:18:04 UTC (rev 1208)
@@ -4,11 +4,13 @@
 libplugin_a_SOURCES = rkcomponentmap.cpp rkcomponentproperties.cpp rkcomponent.cpp \
 	rkstandardcomponent.cpp rkvarselector.cpp rkvarslot.cpp rkformula.cpp rkradio.cpp \
 	rkcheckbox.cpp rkpluginspinbox.cpp rkinput.cpp rkpluginbrowser.cpp rktext.cpp \
-	rktabpage.cpp rkstandardcomponentgui.cpp rkdropdown.cpp rkcomponentcontext.cpp
+	rktabpage.cpp rkstandardcomponentgui.cpp rkdropdown.cpp rkcomponentcontext.cpp \
+	rkpreviewbox.cpp
 
 noinst_HEADERS = rkcomponentmap.h rkcomponentproperties.h rkcomponent.h \
 	rkstandardcomponent.h rkvarselector.h rkvarslot.h rkformula.h rkradio.h \
 	rkcheckbox.h rkpluginspinbox.h rkinput.h rkpluginbrowser.h rktext.h \
-	rktabpage.h rkstandardcomponentgui.h rkdropdown.h rkcomponentcontext.h
+	rktabpage.h rkstandardcomponentgui.h rkdropdown.h rkcomponentcontext.h \
+	rkpreviewbox.h
 
 

Modified: trunk/rkward/rkward/plugin/rkcomponent.h
===================================================================
--- trunk/rkward/rkward/plugin/rkcomponent.h	2007-01-24 15:47:47 UTC (rev 1207)
+++ trunk/rkward/rkward/plugin/rkcomponent.h	2007-01-24 17:18:04 UTC (rev 1208)
@@ -55,6 +55,7 @@
 		ComponentText = 2011,
 		ComponentTab = 2012,
 		ComponentDropDown = 2013,
+		ComponentPreviewBox = 2014,
 		ComponentStandard = 2100,
 		ComponentUser = 3000	/**< for user expansion */
 	};

Modified: trunk/rkward/rkward/plugin/rkcomponentproperties.h
===================================================================
--- trunk/rkward/rkward/plugin/rkcomponentproperties.h	2007-01-24 15:47:47 UTC (rev 1207)
+++ trunk/rkward/rkward/plugin/rkcomponentproperties.h	2007-01-24 17:18:04 UTC (rev 1208)
@@ -2,7 +2,7 @@
                           rkcomponentproperties  -  description
                              -------------------
     begin                : Fri Nov 25 2005
-    copyright            : (C) 2005, 2006 by Thomas Friedrichsmeier
+    copyright            : (C) 2005, 2006, 2007 by Thomas Friedrichsmeier
     email                : tfry at users.sourceforge.net
  ***************************************************************************/
 
@@ -310,6 +310,8 @@
 	QString printout () { return printout_code; };
 /** the cleanup code */
 	QString cleanup () { return cleanup_code; };
+/** the preview code */
+	QString preview () { return preview_code; };
 
 	QString value (const QString &modifier=QString::null);
 
@@ -322,6 +324,8 @@
 	void setPrintout (const QString &code) { printout_code = code; emit (valueChanged (this)); };
 /** see setPreprocess () */
 	void setCleanup (const QString &code) { cleanup_code = code; emit (valueChanged (this)); };
+/** see setPreview () */
+	void setPreview (const QString &code) { preview_code = code; emit (valueChanged (this)); };
 
 	bool isValid () { return (!(preprocess_code.isNull () || calculate_code.isNull () || printout_code.isNull () || cleanup_code.isNull ())); };
 
@@ -332,6 +336,7 @@
 	QString calculate_code;
 	QString printout_code;
 	QString cleanup_code;
+	QString preview_code;
 };
 
 class RKComponent;	// what the ... why do I need this?

Added: trunk/rkward/rkward/plugin/rkpreviewbox.cpp
===================================================================
--- trunk/rkward/rkward/plugin/rkpreviewbox.cpp	                        (rev 0)
+++ trunk/rkward/rkward/plugin/rkpreviewbox.cpp	2007-01-24 17:18:04 UTC (rev 1208)
@@ -0,0 +1,180 @@
+/***************************************************************************
+                          rkpreviewbox  -  description
+                             -------------------
+    begin                : Wed Jan 24 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 "rkpreviewbox.h"
+
+#include <qlayout.h>
+#include <qlabel.h>
+#include <qcheckbox.h>
+#include <qtimer.h>
+
+#include <klocale.h>
+
+#include "../rkglobals.h"
+#include "../rbackend/rinterface.h"
+#include "../misc/xmlhelper.h"
+#include "../debug.h"
+
+RKPreviewBox::RKPreviewBox (const QDomElement &element, RKComponent *parent_component, QWidget *parent_widget) : RKComponent (parent_component, parent_widget) {
+	RK_TRACE (PLUGIN);
+
+	preview_active = false;
+	last_plot_done = true;
+	new_plot_pending = false;
+
+	// get xml-helper
+	XMLHelper *xml = XMLHelper::getStaticHelper ();
+
+	// create and add property
+	addChild ("state", state = new RKComponentPropertyBool (this, true, preview_active, "active", "inactive"));
+	connect (state, SIGNAL (valueChanged (RKComponentPropertyBase *)), this, SLOT (changedState (RKComponentPropertyBase *)));
+
+	// create checkbox
+	QVBoxLayout *vbox = new QVBoxLayout (this, RKGlobals::spacingHint ());
+	toggle_preview_box = new QCheckBox (xml->getStringAttribute (element, "label", i18n ("Preview"), DL_WARNING), this);
+	vbox->addWidget (toggle_preview_box);
+	toggle_preview_box->setChecked (preview_active);
+	connect (toggle_preview_box, SIGNAL (stateChanged (int)), this, SLOT (changedState (int)));
+
+	// status lable
+	status_label = new QLabel (QString::null, this);
+	vbox->addWidget (status_label);
+
+	// find and connect to code property of the parent
+	QString dummy;
+	RKComponentBase *cp = parentComponent ()->lookupComponent ("code", &dummy);
+	if (cp && dummy.isNull () && (cp->type () == PropertyCode)) {
+		code_property = static_cast<RKComponentPropertyCode *> (cp);
+		connect (code_property, SIGNAL (valueChanged (RKComponentPropertyBase *)), this, SLOT (changedCode (RKComponentPropertyBase *)));
+	} else {
+		RK_DO (qDebug ("Could not find code property in preview box"), PLUGIN, DL_WARNING);
+		code_property = 0;
+	}
+
+	// initialize
+	updating = false;
+	changedState (0);
+}
+
+RKPreviewBox::~RKPreviewBox () {
+	RK_TRACE (PLUGIN);
+
+	qDebug ("dtor");
+	killPreview ();
+}
+
+void RKPreviewBox::changedState (RKComponentPropertyBase *) {
+	RK_TRACE (PLUGIN);
+
+	if (updating) return;
+	updating = true;
+	toggle_preview_box->setChecked (state->boolValue ());
+	updating = false;
+
+	tryPreview ();
+	changed ();
+}
+
+void RKPreviewBox::changedCode (RKComponentPropertyBase *) {
+	RK_TRACE (PLUGIN);
+
+	tryPreview ();
+}
+
+void RKPreviewBox::changedState (int) {
+	RK_TRACE (PLUGIN);
+
+	state->setBoolValue (toggle_preview_box->isChecked ());
+}
+
+void RKPreviewBox::tryPreview () {
+	RK_TRACE (PLUGIN);
+
+	if (toggle_preview_box->isChecked ()) QTimer::singleShot (0, this, SLOT (tryPreviewNow ()));
+	else killPreview ();
+
+	updateStatusLabel ();
+}
+
+void RKPreviewBox::tryPreviewNow () {
+	RK_TRACE (PLUGIN);
+
+	if (!code_property) return;
+	if (!parentComponent ()->isSatisfied ()) {
+		return;
+	}
+	if (!parentComponent ()->isReady ()) {
+		tryPreview ();
+		return;
+	}
+
+	if (!last_plot_done) {		// if the last plot is not done, yet, wait before starting the next.
+		new_plot_pending = true;
+		updateStatusLabel ();
+		return;
+	}
+
+	preview_active = true;
+	QString dummy;
+	RKGlobals::rInterface ()->issueCommand (dummy.sprintf (".rk.startPreviewDevice (\"%p\")", this), RCommand::Plugin | RCommand::Sync);
+	RKGlobals::rInterface ()->issueCommand (code_property->preview (), RCommand::Plugin | RCommand::Sync, QString::null, this);
+
+	last_plot_done = false;
+	new_plot_pending = false;
+
+	updateStatusLabel ();
+}
+
+void RKPreviewBox::killPreview () {
+	RK_TRACE (PLUGIN);
+
+	if (!preview_active) return;
+	preview_active = false;
+	QString command;
+	RKGlobals::rInterface ()->issueCommand (command.sprintf (".rk.killPreviewDevice (\"%p\")", this), RCommand::Plugin | RCommand::Sync);
+
+	last_plot_done = true;
+	new_plot_pending = false;
+}
+
+void RKPreviewBox::rCommandDone (RCommand *) {
+	RK_TRACE (PLUGIN);
+
+	last_plot_done = true;
+	if (new_plot_pending) tryPreview ();
+
+	updateStatusLabel ();
+}
+
+void RKPreviewBox::updateStatusLabel () {
+	RK_TRACE (PLUGIN);
+
+	if (!toggle_preview_box->isChecked ()) {
+		status_label->setText (i18n ("Preview disabled"));
+	} else {
+		if (parentComponent ()->isSatisfied () && parentComponent ()->isReady ()) {
+			if (last_plot_done && (!new_plot_pending)) {
+				status_label->setText (i18n ("Preview up to date"));
+			} else {
+				status_label->setText (i18n ("Preview updating"));
+			}
+		} else {
+			status_label->setText (i18n ("Preview not (yet) possible"));
+		}
+	}
+}
+
+#include "rkpreviewbox.moc"

Added: trunk/rkward/rkward/plugin/rkpreviewbox.h
===================================================================
--- trunk/rkward/rkward/plugin/rkpreviewbox.h	                        (rev 0)
+++ trunk/rkward/rkward/plugin/rkpreviewbox.h	2007-01-24 17:18:04 UTC (rev 1208)
@@ -0,0 +1,64 @@
+/***************************************************************************
+                          rkpreviewbox  -  description
+                             -------------------
+    begin                : Wed Jan 24 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 RKPREVIEWBOX_H
+#define RKPREVIEWBOX_H
+
+#include "rkcomponent.h"
+
+#include "rkcomponentproperties.h"
+
+#include "../rbackend/rcommandreceiver.h"
+
+class QCheckBox;
+class QDomElement;
+class QLabel;
+
+/**
+This RKComponent provides a (togglable) automatic graphical preview. WARNING: This component violates some standards of "good component behavior", esp. by assuming several things about the nature of the parent component. So please do not take this as an example for basing other components on.
+
+ at author Thomas Friedrichsmeier
+*/
+class RKPreviewBox : public RKComponent, public RCommandReceiver {
+	Q_OBJECT
+public: 
+	RKPreviewBox (const QDomElement &element, RKComponent *parent_component, QWidget *parent_widget);
+	~RKPreviewBox ();
+	int type () { return ComponentPreviewBox; };
+	RKComponentPropertyBool *state;
+	QString value (const QString &modifier) { return (state->value (modifier)); };
+public slots:
+	void changedState (int);
+	void changedState (RKComponentPropertyBase *);
+	void changedCode (RKComponentPropertyBase *);
+	void tryPreviewNow ();
+protected:
+	void rCommandDone (RCommand *);
+private:
+	bool updating;		// prevent recursion
+	bool preview_active;
+	bool last_plot_done;
+	bool new_plot_pending;
+	void tryPreview ();
+	void killPreview ();
+	void updateStatusLabel ();
+	QCheckBox *toggle_preview_box;
+	QLabel *status_label;
+	RKComponentPropertyCode *code_property;
+};
+
+#endif

Modified: trunk/rkward/rkward/plugin/rkstandardcomponent.cpp
===================================================================
--- trunk/rkward/rkward/plugin/rkstandardcomponent.cpp	2007-01-24 15:47:47 UTC (rev 1207)
+++ trunk/rkward/rkward/plugin/rkstandardcomponent.cpp	2007-01-24 17:18:04 UTC (rev 1208)
@@ -45,6 +45,7 @@
 #include "rkpluginspinbox.h"
 #include "rkinput.h"
 #include "rkpluginbrowser.h"
+#include "rkpreviewbox.h"
 #include "rktext.h"
 #include "rktabpage.h"
 
@@ -62,7 +63,7 @@
 	gui = 0;
 	wizard = 0;
 	created = false;
-	addChild ("code", code = new RKComponentPropertyCode (this, true));
+	addChild ("code", code = new RKComponentPropertyCode (this, true));		// do not change this name!
 	
 	// open the main description file for parsing
 	XMLHelper* xml = XMLHelper::getStaticHelper ();
@@ -139,6 +140,7 @@
 RKStandardComponent::~RKStandardComponent () {
 	RK_TRACE (PLUGIN);
 
+	qDebug ("sc detor");
 	delete backend;
 }
 
@@ -300,6 +302,7 @@
 	backend->calculate (0);
 	backend->printout (0);
 	backend->cleanup (0);
+	backend->preview (0);
 
 	if (gui) {
 		gui->updateCode ();
@@ -498,6 +501,8 @@
 			widget = new RKPluginBrowser (e, component (), parent_widget);
 		} else if (e.tagName () == "text") {
 			widget = new RKText (e, component (), parent_widget);
+		} else if (e.tagName () == "preview") {
+			widget = new RKPreviewBox (e, component (), parent_widget);
 		} else if (e.tagName () == "embed") {
 			QString component_id = xml->getStringAttribute (e, "component", QString::null, DL_ERROR);
 			RKComponentHandle *handle = RKComponentMap::getComponentHandle (component_id);

Modified: trunk/rkward/rkward/plugin/rkstandardcomponentgui.cpp
===================================================================
--- trunk/rkward/rkward/plugin/rkstandardcomponentgui.cpp	2007-01-24 15:47:47 UTC (rev 1207)
+++ trunk/rkward/rkward/plugin/rkstandardcomponentgui.cpp	2007-01-24 17:18:04 UTC (rev 1208)
@@ -144,6 +144,7 @@
 
 	hide ();
 	if (!enslaved) {
+		qDebug ("triggering destruct");
 		component->deleteLater ();
 	}
 }

Modified: trunk/rkward/rkward/plugins/plots/histogram.php
===================================================================
--- trunk/rkward/rkward/plugins/plots/histogram.php	2007-01-24 15:47:47 UTC (rev 1207)
+++ trunk/rkward/rkward/plugins/plots/histogram.php	2007-01-24 17:18:04 UTC (rev 1208)
@@ -6,6 +6,43 @@
 }
 
 function printout () {
+	global $scale;
+	global $bw;
+	global $adjust;
+	global $narm;
+	global $giveRkern;
+	global $x;
+	global $breaksopt;
+
+	preparedata ();
+?>
+rk.header ("Histogram", list ("Frequency", "<? echo $scale; ?>", "Breaks algorithm", <? echo ("\"" . $breaks . "\""); ?>, "Variable", rk.get.description (<? echo ($x); ?>)))
+rk.graph.on ()
+<?	makeplotcommand (); ?>
+rk.graph.off ()
+<?
+}
+
+function cleanup () {
+}
+
+function preview () {
+	preparedata ();
+	makeplotcommand ();
+}
+
+
+
+// internal helper functions below;
+function preparedata () {
+	global $scale;
+	global $bw;
+	global $adjust;
+	global $narm;
+	global $giveRkern;
+	global $x;
+	global $breaksopt;
+
 	$breaks = getRK_val ("breaks");
 	$scale = getRK_val ("scale");
 	$bw =  getRK_val ("bw");
@@ -19,19 +56,24 @@
 	} else if (!empty ($breaks)) {
 		$breaksopt = "breaks=\"" . $breaks . "\"";
 	}
+}
+
+function makeplotcommand () {
+	global $scale;
+	global $bw;
+	global $adjust;
+	global $narm;
+	global $giveRkern;
+	global $x;
+	global $breaksopt;
 ?>
-rk.header ("Histogram", list ("Frequency", "<? echo $scale; ?>", "Breaks algorithm", <? echo ("\"" . $breaks . "\""); ?>, "Variable", rk.get.description (<? echo ($x); ?>)))
-rk.graph.on ()
 try ({
 	hist (<? echo ($x); ?>, <? echo ($breaksopt); ?>, freq = <? echo $scale; ?><? getRK ("plotoptions.code.printout"); ?>)
 <?	if (($scale=="FALSE") && getRK_val ("density")) { ?>
 	lines(density(<? echo ($x); ?>, bw="<? echo ($bw); ?>", adjust = <? echo ($adjust); ?>, <? echo ($giveRkern); ?>, <? echo ($narm); ?>, n = <? getRK ("n"); ?>), col= "<? getRK ("col_density"); ?>")
 <?	} ?>
 })
-rk.graph.off ()
 <?
 }
 
-function cleanup () {
-}
 ?>

Modified: trunk/rkward/rkward/plugins/plots/histogram.xml
===================================================================
--- trunk/rkward/rkward/plugins/plots/histogram.xml	2007-01-24 15:47:47 UTC (rev 1207)
+++ trunk/rkward/rkward/plugins/plots/histogram.xml	2007-01-24 17:18:04 UTC (rev 1208)
@@ -14,7 +14,10 @@
 			<tab label="Variable(s)" >
 				<row>
 					<varselector id="vars" />
-					<varslot multi="false" type="numeric" id="x" source="vars" label="variable(s):" required="true" />
+					<column>
+						<varslot multi="false" type="numeric" id="x" source="vars" label="variable(s):" required="true" />
+						<preview id="preview"/>
+					</column>
 				</row>
 			</tab>
 			<tab label="Options" >

Modified: trunk/rkward/rkward/rbackend/rpackages/rkward/DESCRIPTION
===================================================================
--- trunk/rkward/rkward/rbackend/rpackages/rkward/DESCRIPTION	2007-01-24 15:47:47 UTC (rev 1207)
+++ trunk/rkward/rkward/rbackend/rpackages/rkward/DESCRIPTION	2007-01-24 17:18:04 UTC (rev 1208)
@@ -1,6 +1,6 @@
 Package: rkward
 Title: Provides some helper functions for the RKWard frontend
-Version: 0.4.5
+Version: 0.4.6
 Author: Thomas Friedrichsmeier and the RKWard Team
 Description: Most of the functions in here are really only needed for the internal communication between RKWard and R. There are also a few functions, which allow access to RKWard or RKWard specifics. Most is not implemented, yet.
 Maintainer: RKWard-devel mailing list <rkward-devel at lists.sourceforge.net>

Modified: trunk/rkward/rkward/rbackend/rpackages/rkward/R/internal.R
===================================================================
--- trunk/rkward/rkward/rbackend/rpackages/rkward/R/internal.R	2007-01-24 15:47:47 UTC (rev 1207)
+++ trunk/rkward/rkward/rbackend/rpackages/rkward/R/internal.R	2007-01-24 17:18:04 UTC (rev 1208)
@@ -337,3 +337,24 @@
 
 	ret
 }
+
+".rk.preview.devices" <- list ();
+
+".rk.startPreviewDevice" <- function (x) {
+	if (is.null (.rk.preview.devices[[x]])) {
+		a <- dev.cur ()
+		x11 ()
+		if (a != dev.cur ()) {
+			.rk.preview.devices[[x]] <<- dev.cur ()
+		}
+	}
+}
+
+".rk.killPreviewDevice" <- function (x) {
+	a <- .rk.preview.devices[[x]]
+	if (!is.null (a)) {
+		.rk.preview.devices[[x]] <<- NULL
+		dev.set (a)
+		dev.off ()
+	}
+}

Modified: trunk/rkward/rkward/scriptbackends/phpbackend.cpp
===================================================================
--- trunk/rkward/rkward/scriptbackends/phpbackend.cpp	2007-01-24 15:47:47 UTC (rev 1207)
+++ trunk/rkward/rkward/scriptbackends/phpbackend.cpp	2007-01-24 17:18:04 UTC (rev 1208)
@@ -255,6 +255,10 @@
 						if (add_headings) code_property->setCleanup (i18n ("## Clean up\n") + retrieveOutput ());
 						else code_property->setCleanup (retrieveOutput ());
 						resetOutput ();
+					} else if (current_type == Preview) {
+						// no heading for the preview code (not shown in the code box)
+						code_property->setPreview (retrieveOutput ());
+						resetOutput ();
 					} else {
 						emit (commandDone (current_flags));
 					}

Modified: trunk/rkward/rkward/scriptbackends/phpbackend.h
===================================================================
--- trunk/rkward/rkward/scriptbackends/phpbackend.h	2007-01-24 15:47:47 UTC (rev 1207)
+++ trunk/rkward/rkward/scriptbackends/phpbackend.h	2007-01-24 17:18:04 UTC (rev 1208)
@@ -47,7 +47,7 @@
 	void calculate (int flags) { callFunction ("calculate ();", flags, Calculate); };
 	void printout (int flags) { callFunction ("printout ();", flags, Printout); };
 	void cleanup (int flags) { callFunction ("cleanup ();", flags, Cleanup); };
-	void preview (int flags) { callFunction ("doPreview ();", flags, Preview); };
+	void preview (int flags) { callFunction ("getPreview ();", flags, Preview); };
 	void writeData (const QString &data);
 public slots:
 	void gotOutput (KProcess *proc, char* buf, int len);


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