From: <ju...@us...> - 2008-06-06 17:48:09
|
Revision: 3105 http://urakawa.svn.sourceforge.net/urakawa/?rev=3105&view=rev Author: julienq Date: 2008-06-06 10:48:04 -0700 (Fri, 06 Jun 2008) Log Message: ----------- Adding new prototype project for GUI improvement tests. Added Paths: ----------- trunk/urakawa/application/prototype/Bobi/ trunk/urakawa/application/prototype/Bobi/Bobi/ trunk/urakawa/application/prototype/Bobi/Bobi/BobiForm.resx trunk/urakawa/application/prototype/Bobi/Bobi/Commands/ trunk/urakawa/application/prototype/Bobi/Bobi/Commands/NewTrack.cs trunk/urakawa/application/prototype/Bobi/Bobi/Project.cs trunk/urakawa/application/prototype/Bobi/Bobi/View/ trunk/urakawa/application/prototype/Bobi/Bobi/View/ProjectView.Designer.cs trunk/urakawa/application/prototype/Bobi/Bobi/View/ProjectView.cs trunk/urakawa/application/prototype/Bobi/Bobi/View/ProjectView.resx trunk/urakawa/application/prototype/Bobi/Bobi/View/Track.Designer.cs trunk/urakawa/application/prototype/Bobi/Bobi/View/Track.cs Added: trunk/urakawa/application/prototype/Bobi/Bobi/BobiForm.resx =================================================================== --- trunk/urakawa/application/prototype/Bobi/Bobi/BobiForm.resx (rev 0) +++ trunk/urakawa/application/prototype/Bobi/Bobi/BobiForm.resx 2008-06-06 17:48:04 UTC (rev 3105) @@ -0,0 +1,126 @@ +<?xml version="1.0" encoding="utf-8"?> +<root> + <!-- + Microsoft ResX Schema + + Version 2.0 + + The primary goals of this format is to allow a simple XML format + that is mostly human readable. The generation and parsing of the + various data types are done through the TypeConverter classes + associated with the data types. + + Example: + + ... ado.net/XML headers & schema ... + <resheader name="resmimetype">text/microsoft-resx</resheader> + <resheader name="version">2.0</resheader> + <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader> + <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader> + <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data> + <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data> + <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64"> + <value>[base64 mime encoded serialized .NET Framework object]</value> + </data> + <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> + <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value> + <comment>This is a comment</comment> + </data> + + There are any number of "resheader" rows that contain simple + name/value pairs. + + Each data row contains a name, and value. The row also contains a + type or mimetype. Type corresponds to a .NET class that support + text/value conversion through the TypeConverter architecture. + Classes that don't support this are serialized and stored with the + mimetype set. + + The mimetype is used for serialized objects, and tells the + ResXResourceReader how to depersist the object. This is currently not + extensible. For a given mimetype the value must be set accordingly: + + Note - application/x-microsoft.net.object.binary.base64 is the format + that the ResXResourceWriter will generate, however the reader can + read any of the formats listed below. + + mimetype: application/x-microsoft.net.object.binary.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.soap.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Soap.SoapFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.bytearray.base64 + value : The object must be serialized into a byte array + : using a System.ComponentModel.TypeConverter + : and then encoded with base64 encoding. + --> + <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"> + <xsd:import namespace="http://www.w3.org/XML/1998/namespace" /> + <xsd:element name="root" msdata:IsDataSet="true"> + <xsd:complexType> + <xsd:choice maxOccurs="unbounded"> + <xsd:element name="metadata"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" /> + </xsd:sequence> + <xsd:attribute name="name" use="required" type="xsd:string" /> + <xsd:attribute name="type" type="xsd:string" /> + <xsd:attribute name="mimetype" type="xsd:string" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="assembly"> + <xsd:complexType> + <xsd:attribute name="alias" type="xsd:string" /> + <xsd:attribute name="name" type="xsd:string" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="data"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" /> + <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" /> + <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="resheader"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" /> + </xsd:complexType> + </xsd:element> + </xsd:choice> + </xsd:complexType> + </xsd:element> + </xsd:schema> + <resheader name="resmimetype"> + <value>text/microsoft-resx</value> + </resheader> + <resheader name="version"> + <value>2.0</value> + </resheader> + <resheader name="reader"> + <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <resheader name="writer"> + <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <metadata name="menuStrip.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"> + <value>17, 17</value> + </metadata> + <metadata name="statusStrip.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"> + <value>125, 17</value> + </metadata> +</root> \ No newline at end of file Added: trunk/urakawa/application/prototype/Bobi/Bobi/Commands/NewTrack.cs =================================================================== --- trunk/urakawa/application/prototype/Bobi/Bobi/Commands/NewTrack.cs (rev 0) +++ trunk/urakawa/application/prototype/Bobi/Bobi/Commands/NewTrack.cs 2008-06-06 17:48:04 UTC (rev 3105) @@ -0,0 +1,71 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Bobi.Commands +{ + class NewTrack : urakawa.undo.ICommand + { + private urakawa.Presentation presentation; // presentation in which the track is added + private urakawa.core.TreeNode node; // node for the new track + + public NewTrack(urakawa.Presentation presentation) + { + this.presentation = presentation; + this.node = this.presentation.getTreeNodeFactory().createNode(); + } + + #region ICommand Members + + public event EventHandler<urakawa.events.undo.ExecutedEventArgs> executed; + public event EventHandler<urakawa.events.undo.UnExecutedEventArgs> unExecuted; + + public bool canUnExecute() { return true; } + public List<urakawa.media.data.MediaData> getListOfUsedMediaData() { return new List<urakawa.media.data.MediaData>(); } + + public string getLongDescription() + { + return string.Format("Added new track (number of tracks in project: {0}.)", + ((Project)this.presentation.getProject()).NumberOfTracks); + } + + public string getShortDescription() { return "new track"; } + + public void execute() + { + this.presentation.getRootNode().appendChild(this.node); + if (executed != null) executed(this, new urakawa.events.undo.ExecutedEventArgs(this)); + } + + public void unExecute() + { + this.node.detach(); + if (unExecuted != null) unExecuted(this, new urakawa.events.undo.UnExecutedEventArgs(this)); + } + + + #endregion + + #region IWithPresentation Members + + public urakawa.Presentation getPresentation() { return this.presentation; } + public void setPresentation(urakawa.Presentation newPres) { this.presentation = newPres; } + + #endregion + + #region IXukAble Members + + public string getXukLocalName() { return GetType().Name; } + public string getXukNamespaceUri() { return ""; } + public void xukIn(System.Xml.XmlReader source) { } + public void xukOut(System.Xml.XmlWriter destination, Uri baseUri) { } + + #endregion + + #region IChangeNotifier Members + + public event EventHandler<urakawa.events.DataModelChangedEventArgs> changed; + + #endregion + } +} Added: trunk/urakawa/application/prototype/Bobi/Bobi/Project.cs =================================================================== --- trunk/urakawa/application/prototype/Bobi/Bobi/Project.cs (rev 0) +++ trunk/urakawa/application/prototype/Bobi/Bobi/Project.cs 2008-06-06 17:48:04 UTC (rev 3105) @@ -0,0 +1,115 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Bobi +{ + public class Project : urakawa.Project + { + public Uri Path; // path to .xuk file + private int changes; // changes since last save + private bool initialized; // flag set once the project is properly initialized + + + /// <summary> + /// Create a new empty project. + /// </summary> + public Project() : base() + { + Path = null; + this.changes = 0; + setPresentation(getDataModelFactory().createPresentation(), 0); + SetUndoRedoEvents(); + this.initialized = true; + } + + /// <summary> + /// Create a new, uninitialized project ready for reading. + /// </summary> + public Project(Uri path) : base() + { + Path = path; + this.initialized = false; + } + + + /// <summary> + /// True if there are changes since the last time the project was saved (or created.) + /// </summary> + public bool HasChanges { get { return this.changes != 0; } } + + /// <summary> + /// True once the project has been properly initialized. + /// </summary> + public bool Initialized { get { return this.initialized; } } + + /// <summary> + /// Append a new track to the project. + /// </summary> + public void NewTrack() + { + getPresentation(0).getUndoRedoManager().execute(new Commands.NewTrack(getPresentation(0))); + } + + /// <summary> + /// Get the number of tracks in the current project. + /// </summary> + public int NumberOfTracks { get { return getPresentation(0).getRootNode().getChildCount(); } } + + /// <summary> + /// Open a XUK file at the set path. + /// </summary> + public void Open() + { + openXUK(Path); + SetUndoRedoEvents(); + this.initialized = true; + } + + // Redo the last undone change. + public void Redo() + { + urakawa.undo.UndoRedoManager redo = getPresentation(0).getUndoRedoManager(); + if (redo.canRedo()) redo.redo(); + } + + // Save changes to the current path (only if set.) + public void Save() + { + if (Path != null) + { + saveXUK(Path); + Changes(-this.changes); + } + } + + // Undo the last change. + public void Undo() + { + urakawa.undo.UndoRedoManager undo = getPresentation(0).getUndoRedoManager(); + if (undo.canUndo()) undo.undo(); + } + + + // Keep track of the number of changes since open, and send an event when it changes. + private void Changes(int n) + { + if (this.initialized) + { + this.changes += n; + notifyChanged(new urakawa.events.DataModelChangedEventArgs(this)); + } + } + + private void Project_commandDone(object sender, urakawa.events.undo.DoneEventArgs e) { Changes(+1); } + private void Project_commandReDone(object sender, urakawa.events.undo.ReDoneEventArgs e) { Changes(+1); } + private void Project_commandUnDone(object sender, urakawa.events.undo.UnDoneEventArgs e) { Changes(-1); } + + private void SetUndoRedoEvents() + { + getPresentation(0).getUndoRedoManager().commandDone += new EventHandler<urakawa.events.undo.DoneEventArgs>(Project_commandDone); + getPresentation(0).getUndoRedoManager().commandReDone += new EventHandler<urakawa.events.undo.ReDoneEventArgs>(Project_commandReDone); + getPresentation(0).getUndoRedoManager().commandUnDone += new EventHandler<urakawa.events.undo.UnDoneEventArgs>(Project_commandUnDone); + } + } +} Added: trunk/urakawa/application/prototype/Bobi/Bobi/View/ProjectView.Designer.cs =================================================================== --- trunk/urakawa/application/prototype/Bobi/Bobi/View/ProjectView.Designer.cs (rev 0) +++ trunk/urakawa/application/prototype/Bobi/Bobi/View/ProjectView.Designer.cs 2008-06-06 17:48:04 UTC (rev 3105) @@ -0,0 +1,42 @@ +namespace Bobi.View +{ + partial class ProjectView + { + /// <summary> + /// Required designer variable. + /// </summary> + private System.ComponentModel.IContainer components = null; + + /// <summary> + /// Clean up any resources being used. + /// </summary> + /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param> + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Component Designer generated code + + /// <summary> + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// </summary> + private void InitializeComponent() + { + this.SuspendLayout(); + // + // ProjectView + // + this.AutoScroll = true; + this.ResumeLayout(false); + + } + + #endregion + } +} Added: trunk/urakawa/application/prototype/Bobi/Bobi/View/ProjectView.cs =================================================================== --- trunk/urakawa/application/prototype/Bobi/Bobi/View/ProjectView.cs (rev 0) +++ trunk/urakawa/application/prototype/Bobi/Bobi/View/ProjectView.cs 2008-06-06 17:48:04 UTC (rev 3105) @@ -0,0 +1,90 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.Text; +using System.Windows.Forms; + +namespace Bobi.View +{ + public partial class ProjectView : FlowLayoutPanel + { + private Project project; + private double zoom; + + /// <summary> + /// New project view + /// </summary> + public ProjectView() + { + InitializeComponent(); + DoubleBuffered = true; + Project = null; + Zoom = 1.0; + } + + + /// <summary> + /// Set the project for this view. + /// </summary> + public Project Project + { + get { return this.project; } + set + { + Controls.Clear(); + this.project = value; + if (value != null) + { + this.project.changed += new EventHandler<urakawa.events.DataModelChangedEventArgs>(project_changed); + } + } + } + + /// <summary> + /// Zoom factor for the view + /// </summary> + public double Zoom + { + get { return this.zoom; } + set + { + this.zoom = value; + foreach (Control c in Controls) + { + if (c is Track) ((Track)c).Zoom = value; + } + } + } + + + // Let's custom paint + protected override void OnPaint(PaintEventArgs pe) + { + // TODO: Add custom paint code here + // Calling the base class OnPaint + base.OnPaint(pe); + } + + // React to changes in the project + private void project_changed(object sender, urakawa.events.DataModelChangedEventArgs e) + { + System.Diagnostics.Debug.Print(e.ToString()); + if (e is urakawa.events.presentation.RootNodeChangedEventArgs) + { + } + else if (e is urakawa.events.core.ChildAddedEventArgs) + { + Track t = new Track(); + t.Zoom = this.zoom; + Controls.Add(t); + SetFlowBreak(t, true); + } + else if (e is urakawa.events.core.ChildRemovedEventArgs) + { + Controls.RemoveAt(((urakawa.events.core.ChildRemovedEventArgs)e).RemovedPosition); + } + } + } +} Added: trunk/urakawa/application/prototype/Bobi/Bobi/View/ProjectView.resx =================================================================== --- trunk/urakawa/application/prototype/Bobi/Bobi/View/ProjectView.resx (rev 0) +++ trunk/urakawa/application/prototype/Bobi/Bobi/View/ProjectView.resx 2008-06-06 17:48:04 UTC (rev 3105) @@ -0,0 +1,123 @@ +<?xml version="1.0" encoding="utf-8"?> +<root> + <!-- + Microsoft ResX Schema + + Version 2.0 + + The primary goals of this format is to allow a simple XML format + that is mostly human readable. The generation and parsing of the + various data types are done through the TypeConverter classes + associated with the data types. + + Example: + + ... ado.net/XML headers & schema ... + <resheader name="resmimetype">text/microsoft-resx</resheader> + <resheader name="version">2.0</resheader> + <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader> + <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader> + <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data> + <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data> + <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64"> + <value>[base64 mime encoded serialized .NET Framework object]</value> + </data> + <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> + <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value> + <comment>This is a comment</comment> + </data> + + There are any number of "resheader" rows that contain simple + name/value pairs. + + Each data row contains a name, and value. The row also contains a + type or mimetype. Type corresponds to a .NET class that support + text/value conversion through the TypeConverter architecture. + Classes that don't support this are serialized and stored with the + mimetype set. + + The mimetype is used for serialized objects, and tells the + ResXResourceReader how to depersist the object. This is currently not + extensible. For a given mimetype the value must be set accordingly: + + Note - application/x-microsoft.net.object.binary.base64 is the format + that the ResXResourceWriter will generate, however the reader can + read any of the formats listed below. + + mimetype: application/x-microsoft.net.object.binary.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.soap.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Soap.SoapFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.bytearray.base64 + value : The object must be serialized into a byte array + : using a System.ComponentModel.TypeConverter + : and then encoded with base64 encoding. + --> + <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"> + <xsd:import namespace="http://www.w3.org/XML/1998/namespace" /> + <xsd:element name="root" msdata:IsDataSet="true"> + <xsd:complexType> + <xsd:choice maxOccurs="unbounded"> + <xsd:element name="metadata"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" /> + </xsd:sequence> + <xsd:attribute name="name" use="required" type="xsd:string" /> + <xsd:attribute name="type" type="xsd:string" /> + <xsd:attribute name="mimetype" type="xsd:string" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="assembly"> + <xsd:complexType> + <xsd:attribute name="alias" type="xsd:string" /> + <xsd:attribute name="name" type="xsd:string" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="data"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" /> + <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" /> + <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="resheader"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" /> + </xsd:complexType> + </xsd:element> + </xsd:choice> + </xsd:complexType> + </xsd:element> + </xsd:schema> + <resheader name="resmimetype"> + <value>text/microsoft-resx</value> + </resheader> + <resheader name="version"> + <value>2.0</value> + </resheader> + <resheader name="reader"> + <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <resheader name="writer"> + <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <metadata name="$this.TrayLargeIcon" type="System.Boolean, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> + <value>False</value> + </metadata> +</root> \ No newline at end of file Added: trunk/urakawa/application/prototype/Bobi/Bobi/View/Track.Designer.cs =================================================================== --- trunk/urakawa/application/prototype/Bobi/Bobi/View/Track.Designer.cs (rev 0) +++ trunk/urakawa/application/prototype/Bobi/Bobi/View/Track.Designer.cs 2008-06-06 17:48:04 UTC (rev 3105) @@ -0,0 +1,36 @@ +namespace Bobi.View +{ + partial class Track + { + /// <summary> + /// Required designer variable. + /// </summary> + private System.ComponentModel.IContainer components = null; + + /// <summary> + /// Clean up any resources being used. + /// </summary> + /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param> + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Component Designer generated code + + /// <summary> + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// </summary> + private void InitializeComponent() + { + components = new System.ComponentModel.Container(); + } + + #endregion + } +} Added: trunk/urakawa/application/prototype/Bobi/Bobi/View/Track.cs =================================================================== --- trunk/urakawa/application/prototype/Bobi/Bobi/View/Track.cs (rev 0) +++ trunk/urakawa/application/prototype/Bobi/Bobi/View/Track.cs 2008-06-06 17:48:04 UTC (rev 3105) @@ -0,0 +1,54 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.Text; +using System.Windows.Forms; + +namespace Bobi.View +{ + public partial class Track : Control + { + private Size baseSize; // size at zoom factor 1 + private double zoom; // zoom factor + + public Track() + { + InitializeComponent(); + DoubleBuffered = true; + this.baseSize = new Size(512, 144); + this.BackColor = Color.AliceBlue; + Zoom = 1.0; + } + + [Browsable(true)] + public Size BaseSize + { + get { return this.baseSize; } + set + { + this.baseSize = value; + Zoom = this.Zoom; // resize the window using the current zoom factor + } + } + + [Browsable(true)] + public double Zoom + { + get { return this.zoom; } + set + { + this.zoom = value; + this.Size = new Size((int)Math.Round(baseSize.Width * value), (int)Math.Round(baseSize.Height * value)); + } + } + + protected override void OnPaint(PaintEventArgs pe) + { + // TODO: Add custom paint code here + // Calling the base class OnPaint + base.OnPaint(pe); + } + } +} This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |