new Add Project dialog, powered by plugins

This commit is contained in:
Hylke Bons 2011-10-01 23:15:46 +02:00
parent 18ace1afc1
commit 5a9b7d4ba6
19 changed files with 748 additions and 121 deletions

View file

@ -357,7 +357,7 @@ namespace SparkleLib {
// We need to specifically mention the file, so
// we can't reuse the Add () method
SparkleGit git_add = new SparkleGit (LocalPath,
"add " + conflicting_path);
"add \"" + conflicting_path + "\"");
git_add.Start ();
git_add.WaitForExit ();

View file

@ -79,7 +79,7 @@ namespace SparkleLib {
if (file.Length == 0) {
File.Delete (FullPath);
CreateInitialConfig ();
CreateInitialConfig ();
} else {
throw new XmlException (FullPath + " does not contain a valid config XML structure.");

View file

@ -23,6 +23,7 @@ SOURCES = \
SparkleEventLog.cs \
SparkleEventLogController.cs \
SparkleExtensions.cs \
SparklePlugin.cs \
SparkleSetup.cs \
SparkleSetupController.cs \
SparkleSetupWindow.cs \

View file

@ -22,8 +22,9 @@ namespace SparkleShare {
public class SparkleEntry : Entry {
public bool ExampleTextActive;
private string pExampleText;
private string example_text;
private bool example_text_active;
public SparkleEntry ()
@ -32,7 +33,7 @@ namespace SparkleShare {
FocusGrabbed += delegate { OnEntered (); };
ClipboardPasted += delegate { OnEntered (); };
FocusOutEvent += delegate {
if (Text.Equals ("") || Text == null)
ExampleTextActive = true;
@ -47,33 +48,46 @@ namespace SparkleShare {
{
if (ExampleTextActive) {
ExampleTextActive = false;
Text = "";
Text = "";
UseNormalTextColor ();
}
}
public bool ExampleTextActive {
get {
return this.example_text_active;
}
set {
this.example_text_active = value;
if (this.example_text_active)
UseSecondaryTextColor ();
else
UseNormalTextColor ();
}
}
public string ExampleText
{
get {
return pExampleText;
return this.example_text;
}
set {
pExampleText = value;
if (ExampleTextActive) {
this.example_text = value;
if (this.example_text_active)
UseExampleText ();
}
}
}
private void UseExampleText ()
{
Text = pExampleText;
Text = this.example_text;
UseSecondaryTextColor ();
}

View file

@ -0,0 +1,71 @@
// SparkleShare, a collaboration and sharing tool.
// Copyright (C) 2010 Hylke Bons (hylkebons@gmail.com)
//
// 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 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see (http://www.gnu.org/licenses/).
using System;
using System.IO;
using System.Xml;
namespace SparkleShare {
public class SparklePlugin {
public string Name;
public string Description;
public string ImagePath;
public string Backend;
public string Address;
public string AddressExample;
public string Path;
public string PathExample;
public SparklePlugin (string plugin_path)
{
string plugin_directory = System.IO.Path.GetDirectoryName (plugin_path);
XmlDocument xml = new XmlDocument ();
xml.Load (plugin_path);
XmlNode node;
node = xml.SelectSingleNode ("/sparkleshare/plugin/info/name/text()");
if (node != null) { Name = node.Value; }
node = xml.SelectSingleNode ("/sparkleshare/plugin/info/description/text()");
if (node != null) { Description = node.Value; }
node = xml.SelectSingleNode ("/sparkleshare/plugin/info/icon/text()");
if (node != null) { ImagePath = System.IO.Path.Combine (plugin_directory, node.Value); }
node = xml.SelectSingleNode ("/sparkleshare/plugin/info/backend/text()");
if (node != null) { Backend = node.Value; }
node = xml.SelectSingleNode ("/sparkleshare/plugin/address/value/text()");
if (node != null) { Address = node.Value; }
node = xml.SelectSingleNode ("/sparkleshare/plugin/address/example/text()");
if (node != null) { AddressExample = node.Value; }
node = xml.SelectSingleNode ("/sparkleshare/plugin/path/value/text()");
if (node != null) { Path = node.Value; }
node = xml.SelectSingleNode ("/sparkleshare/plugin/path/example/text()");
if (node != null) { PathExample = node.Value; }
}
}
}

View file

@ -53,6 +53,15 @@ namespace SparkleShare {
}
private void RenderServiceColumn (TreeViewColumn column, CellRenderer cell,
TreeModel model, TreeIter iter)
{
(cell as Gtk.CellRendererText).Markup = (string) model.GetValue (iter, 1);
// TODO: When the row is highlighted, the description text should be
// colored with a mix of the selected text color + the selected row color
}
public SparkleSetup () : base ()
{
SecondaryTextColor = SparkleUIHelpers.GdkColorToHex (Style.Foreground (StateType.Insensitive));
@ -118,133 +127,184 @@ namespace SparkleShare {
case PageType.Add: {
Header = _("Where is your project?");
Header = _("Where's your project hosted?");
Table = new Table (6, 2, false) {
RowSpacing = 0
VBox layout_vertical = new VBox (false, 12);
HBox layout_fields = new HBox (true, 12);
VBox layout_address = new VBox (true, 0);
VBox layout_path = new VBox (true, 0);
ListStore store = new ListStore (typeof (Gdk.Pixbuf),
typeof (string), typeof (SparklePlugin));
TreeView tree = new TreeView (store) { HeadersVisible = false };
// Icon column
tree.AppendColumn ("Icon", new Gtk.CellRendererPixbuf (), "pixbuf", 0);
tree.Columns [0].Cells [0].Xpad = 6;
// Service column
TreeViewColumn service_column = new TreeViewColumn () { Title = "Service" };
CellRendererText service_cell = new CellRendererText () { Ypad = 4 };
service_column.PackStart (service_cell, true);
service_column.SetCellDataFunc (service_cell, new TreeCellDataFunc (RenderServiceColumn));
store.AppendValues (new Gdk.Pixbuf ("/usr/share/icons/gnome/24x24/places/network-server.png"),
"<span size=\"small\"><b>On my own server</b>\n" +
"<span fgcolor=\"" + SecondaryTextColor + "\">Everything under my control</span></span>",
null);
foreach (SparklePlugin plugin in Controller.Plugins) {
store.AppendValues (
new Gdk.Pixbuf (plugin.ImagePath),
"<span size=\"small\"><b>" + plugin.Name + "</b>\n" +
"<span fgcolor=\"" + SecondaryTextColor + "\">" + plugin.Description + "</span></span>",
plugin);
}
tree.AppendColumn (service_column);
// Select "On my own server" by default
TreeSelection default_selection = tree.Selection;
TreePath default_path = new TreePath ("0");
default_selection.SelectPath (default_path);
tree.Model.Foreach (new TreeModelForeachFunc (delegate (TreeModel model,
TreePath path, TreeIter iter) {
string address;
try {
address = (model.GetValue (iter, 2) as SparklePlugin).Address;
} catch (NullReferenceException) {
address = "";
}
if (!string.IsNullOrEmpty (address) &&
address.Equals (Controller.PreviousServer)) {
tree.SetCursor (path, service_column, false);
// TODO: Scroll to the selection
return true;
} else {
return false;
}
}));
// Update the address field text when the selection changes
tree.CursorChanged += delegate(object sender, EventArgs e) {
TreeIter iter;
TreeModel model;
TreeSelection selection = (sender as TreeView).Selection;
selection.GetSelected (out model, out iter);
SparklePlugin plugin = (SparklePlugin) model.GetValue (iter, 2);
ServerEntry.Sensitive = true;
FolderEntry.Sensitive = true;
if (plugin != null) {
if (plugin.Path != null) {
FolderEntry.Text = plugin.Path;
FolderEntry.Sensitive = false;
FolderEntry.ExampleTextActive = false;
} else if (plugin.PathExample != null) {
FolderEntry.Text = "";
FolderEntry.ExampleText = plugin.PathExample;
FolderEntry.ExampleTextActive = true;
}
if (plugin.Address != null) {
ServerEntry.Text = plugin.Address;
ServerEntry.Sensitive = false;
ServerEntry.ExampleTextActive = false;
} else if (plugin.AddressExample != null) {
ServerEntry.Text = "";
ServerEntry.ExampleText = plugin.AddressExample;
ServerEntry.ExampleTextActive = true;
}
} else {
ServerEntry.Text = "";
ServerEntry.ExampleTextActive = true;
ServerEntry.ExampleText = _("domain name or IP address");
FolderEntry.Text = "";
FolderEntry.ExampleTextActive = true;
FolderEntry.ExampleText = _("/path/to/project");
}
// TODO: Scroll along with the selection
};
HBox layout_server = new HBox (true, 0);
ScrolledWindow scrolled_window = new ScrolledWindow ();
scrolled_window.AddWithViewport (tree);
// Own server radiobutton
RadioButton radio_button = new RadioButton ("<b>" + _("On my own server:") + "</b>");
(radio_button.Child as Label).UseMarkup = true;
radio_button.Toggled += delegate {
if (radio_button.Active) {
FolderEntry.ExampleText = _("Folder");
ServerEntry.Sensitive = true;
CheckAddPage ();
} else {
ServerEntry.Sensitive = false;
CheckAddPage ();
}
ShowAll ();
};
// Own server entry
ServerEntry = new SparkleEntry () { };
ServerEntry.Completion = new EntryCompletion();
ListStore server_store = new ListStore (typeof (string));
ServerEntry = new SparkleEntry ();
ServerEntry.Completion = new EntryCompletion();
ListStore server_store = new ListStore (typeof (string));
foreach (string host in Program.Controller.PreviousHosts)
server_store.AppendValues (host);
ServerEntry.Completion.Model = server_store;
ServerEntry.Completion.Model = server_store;
ServerEntry.Completion.TextColumn = 0;
if (!string.IsNullOrEmpty (Controller.PreviousServer)) {
ServerEntry.Text = Controller.PreviousServer;
ServerEntry.Text = Controller.PreviousServer;
ServerEntry.ExampleTextActive = false;
} else {
ServerEntry.ExampleText = _("address-to-server.com");
ServerEntry.ExampleText = _("domain name or IP address");
}
ServerEntry.Changed += delegate {
CheckAddPage ();
};
layout_server.Add (radio_button);
layout_server.Add (ServerEntry);
layout_address.PackStart (new Label () {
Markup = "<b>" + _("Address") + "</b>",
Xalign = 0
}, true, true, 0);
Table.Attach (layout_server, 0, 2, 1, 2);
layout_address.PackStart (ServerEntry, true, true, 0);
// Github radiobutton
string github_text = "<b>" + "Github" + "</b>";
FolderEntry = new SparkleEntry ();
FolderEntry.ExampleText = _("/path/to/project");
FolderEntry.Completion = new EntryCompletion();
RadioButton radio_button_github = new RadioButton (radio_button, github_text);
(radio_button_github.Child as Label).UseMarkup = true;
(radio_button_github.Child as Label).Wrap = true;
if (!string.IsNullOrEmpty (Controller.PreviousFolder)) {
FolderEntry.Text = Controller.PreviousFolder;
FolderEntry.ExampleTextActive = false;
}
radio_button_github.Toggled += delegate {
if (radio_button_github.Active)
FolderEntry.ExampleText = _("Username/Folder");
};
ListStore folder_store = new ListStore (typeof (string));
//foreach (string host in Program.Controller.FolderPaths)
// folder_store.AppendValues (host);
// Gitorious radiobutton
string gitorious_text = "<b>" + _("Gitorious") + "</b>";
FolderEntry.Completion.Model = folder_store;
FolderEntry.Completion.TextColumn = 0;
RadioButton radio_button_gitorious = new RadioButton (radio_button, gitorious_text);
(radio_button_gitorious.Child as Label).UseMarkup = true;
(radio_button_gitorious.Child as Label).Wrap = true;
FolderEntry.Changed += delegate {
CheckAddPage ();
};
radio_button_gitorious.Toggled += delegate {
if (radio_button_gitorious.Active)
FolderEntry.ExampleText = _("Project/Folder");
};
layout_path.PackStart (new Label () { Markup = "<b>" + _("Remote Path") + "</b>", Xalign = 0 },
true, true, 0);
layout_path.PackStart (FolderEntry, true, true, 0);
layout_fields.PackStart (layout_address);
layout_fields.PackStart (layout_path);
// GNOME radiobutton
string gnome_text = "<b>" + _("The GNOME Project") + "</b>";
layout_vertical.PackStart (new Label (""), false, false, 0);
layout_vertical.PackStart (scrolled_window, true, true, 0);
layout_vertical.PackStart (layout_fields, false, false, 0);
RadioButton radio_button_gnome = new RadioButton (radio_button, gnome_text);
(radio_button_gnome.Child as Label).UseMarkup = true;
(radio_button_gnome.Child as Label).Wrap = true;
radio_button_gnome.Toggled += delegate {
if (radio_button_gnome.Active)
FolderEntry.ExampleText = _("Project");
};
Table.Attach (radio_button_github, 0, 2, 2, 3);
Table.Attach (radio_button_gitorious, 0, 2, 3, 4);
Table.Attach (radio_button_gnome, 0, 2, 4, 5);
// Folder label and entry
HBox layout_folder = new HBox (true, 0);
Label folder_label = new Label (_("Folder Name:")) {
UseMarkup = true,
Xalign = 1
};
FolderEntry = new SparkleEntry ();
FolderEntry.ExampleText = _("Folder");
FolderEntry.Completion = new EntryCompletion();
ListStore folder_store = new ListStore (typeof (string));
//foreach (string host in Program.Controller.FolderPaths)
// folder_store.AppendValues (host);
FolderEntry.Completion.Model = folder_store;
FolderEntry.Completion.TextColumn = 0;
FolderEntry.Changed += delegate {
CheckAddPage ();
};
layout_folder.PackStart (folder_label, true, true, 12);
layout_folder.PackStart (FolderEntry, true, true, 0);
Table.Attach (layout_folder, 0, 2, 5, 6);
VBox box = new VBox (false, 0);
box.PackStart (Table, false, false, 0);
Add (box);
Add (layout_vertical);
// Cancel button
Button cancel_button = new Button (_("Cancel"));
@ -253,7 +313,6 @@ namespace SparkleShare {
Close ();
};
// Sync button
SyncButton = new Button (_("Add"));
@ -261,15 +320,6 @@ namespace SparkleShare {
string server = ServerEntry.Text;
string folder_name = FolderEntry.Text;
if (radio_button_gitorious.Active)
server = "gitorious.org";
if (radio_button_github.Active)
server = "github.com";
if (radio_button_gnome.Active)
server = "gnome.org";
Controller.AddPageCompleted (server, folder_name);
};

View file

@ -16,8 +16,11 @@
using System;
using System.Collections.Generic;
using System.IO;
using SparkleLib;
namespace SparkleShare {
public enum PageType {
@ -38,6 +41,8 @@ namespace SparkleShare {
public event UpdateProgressBarEventHandler UpdateProgressBarEvent;
public delegate void UpdateProgressBarEventHandler (double percentage);
public readonly List<SparklePlugin> Plugins = new List<SparklePlugin> ();
public int TutorialPageNumber {
get {
@ -101,6 +106,24 @@ namespace SparkleShare {
public SparkleSetupController ()
{
string local_plugins_path = SparkleHelpers.CombineMore (
Environment.GetFolderPath (Environment.SpecialFolder.ApplicationData),
"sparkleshare", "plugins");
string plugins_path = SparkleHelpers.CombineMore (
Defines.DATAROOTDIR, "sparkleshare", "plugins");
try {
foreach (string xml_file_path in Directory.GetFiles (local_plugins_path, "*.xml"))
Plugins.Add (new SparklePlugin (xml_file_path));
foreach (string xml_file_path in Directory.GetFiles (plugins_path, "*.xml"))
Plugins.Add (new SparklePlugin (xml_file_path));
} catch (DirectoryNotFoundException e) {
Console.WriteLine ("Could not find any plugins: " + e.Message);
}
ChangePageEvent += delegate (PageType page) {
this.previous_page = page;
};

View file

@ -24,7 +24,6 @@ using System.Timers;
using Gtk;
using Mono.Unix;
using SparkleLib;
namespace SparkleShare {

View file

@ -73,5 +73,6 @@
<Compile Include="SparkleAbout.cs" />
<Compile Include="SparkleExtensions.cs" />
<Compile Include="SparkleControllerBase.cs" />
<Compile Include="SparklePlugin.cs" />
</ItemGroup>
</Project>

14
data/plugins/Makefile.am Normal file
View file

@ -0,0 +1,14 @@
dist_plugins_DATA = \
github.xml \
github.png \
gitorious.xml \
gitorious.png \
bitbucket.xml \
bitbucket.png \
gnome.xml \
gnome.png
pluginsdir = $(pkgdatadir)/plugins/
MAINTAINERCLEANFILES = \
Makefile.in

BIN
data/plugins/bitbucket.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

View file

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<sparkleshare>
<plugin>
<info>
<name>Bitbucket</name>
<description>Free code hosting for Mercurial</description>
<icon>bitbucket.png</icon>
<backend>Mercurial</backend>
</info>
<address>
<value>ssh://hg@bitbucket.org/</value>
<example></example>
</address>
<path>
<value></value>
<example>/username/project</example>
</path>
</plugin>
</sparkleshare>

BIN
data/plugins/github.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

20
data/plugins/github.xml Normal file
View file

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<sparkleshare>
<plugin>
<info>
<name>Github</name>
<description>Free public Git repositories with collaborator management</description>
<icon>github.png</icon>
<backend>Git</backend>
</info>
<address>
<value>ssh://git@github.com/</value>
<example></example>
</address>
<path>
<value></value>
<example>/username/project</example>
</path>
</plugin>
</sparkleshare>

BIN
data/plugins/gitorious.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

View file

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<sparkleshare>
<plugin>
<info>
<name>Gitorious</name>
<description>Open source infrastructure for hosting open source projects</description>
<icon>gitorious.png</icon>
<backend>Git</backend>
</info>
<address>
<value>ssh://git@gitorious.org/</value>
<example></example>
</address>
<path>
<value></value>
<example>/project/repository</example>
</path>
</plugin>
</sparkleshare>

BIN
data/plugins/gnome.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

20
data/plugins/gnome.xml Normal file
View file

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<sparkleshare>
<plugin>
<info>
<name>The GNOME Project</name>
<description>A free and easy interface for your computer</description>
<icon>gnome.png</icon>
<backend>Git</backend>
</info>
<address>
<value>ssh://git@gnome.org/</value>
<example></example>
</address>
<path>
<value></value>
<example>/project</example>
</path>
</plugin>
</sparkleshare>

View file

@ -0,0 +1,374 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="210mm"
height="297mm"
id="svg9662"
version="1.1"
inkscape:version="0.48.2 r9819"
sodipodi:docname="add-project-dialog.svg">
<defs
id="defs3" />
<sodipodi:namedview
inkscape:document-units="mm"
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="1"
inkscape:cx="549.60727"
inkscape:cy="754.52765"
inkscape:current-layer="layer1"
showgrid="false"
inkscape:window-width="1440"
inkscape:window-height="843"
inkscape:window-x="0"
inkscape:window-y="26"
inkscape:window-maximized="1" />
<metadata
id="metadata4">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<rect
style="fill:none;stroke:#000000;stroke-width:1.13221943;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
id="rect9672"
width="678.8678"
height="438.8678"
x="31.066612"
y="121.42778" />
<rect
y="450.4278"
x="217.06659"
height="23.867767"
width="216.315"
id="rect10182"
style="fill:none;stroke:#000000;stroke-width:1.13221943;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
<flowRoot
xml:space="preserve"
id="flowRoot10184"
style="font-size:37.57078552px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Overpass;-inkscape-font-specification:Sans Bold"
transform="matrix(0.37262995,0,0,0.37262995,110.35909,348.69358)"><flowRegion
id="flowRegion10186"><rect
id="rect10188"
width="363"
height="135"
x="286"
y="218.36218"
style="font-size:37.57078552px" /></flowRegion><flowPara
id="flowPara10190">Address</flowPara></flowRoot> <rect
y="121.42778"
x="31.066612"
height="438.8678"
width="149.8678"
id="rect10192"
style="fill:#e7e8e9;stroke:#000000;stroke-width:1.13221942999999992;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
<rect
style="fill:none;stroke:#000000;stroke-width:1.13221943;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
id="rect10194"
width="215.96146"
height="23.867767"
x="456.9729"
y="450.4278" />
<flowRoot
transform="matrix(0.37262995,0,0,0.37262995,349.35909,348.69358)"
style="font-size:37.57078552px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Overpass;-inkscape-font-specification:Sans Bold"
id="flowRoot10196"
xml:space="preserve"><flowRegion
id="flowRegion10198"><rect
style="font-size:37.57078552px"
y="218.36218"
x="286"
height="135"
width="363"
id="rect10200" /></flowRegion><flowPara
id="flowPara10202">Remote Path</flowPara></flowRoot> <rect
style="fill:none;stroke:#000000;stroke-width:1.13221943;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
id="rect10204"
width="456.8678"
height="210.3028"
x="217.06659"
y="201.84201" />
<rect
y="201.84203"
x="217.06659"
height="42.718502"
width="435.8678"
id="rect10396"
style="fill:#0093d9;fill-opacity:1;stroke:none" />
<flowRoot
xml:space="preserve"
id="flowRoot10206"
style="font-size:37.57078552px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none;font-family:Overpass;-inkscape-font-specification:Sans Bold"
transform="matrix(0.37262995,0,0,0.37262995,160.47179,128.53063)"><flowRegion
id="flowRegion10208"><rect
id="rect10210"
width="363"
height="135"
x="286"
y="218.36218"
style="font-size:37.57078552px;fill:#ffffff;fill-opacity:1" /></flowRegion><flowPara
id="flowPara10212">On my own server</flowPara></flowRoot> <path
style="opacity:0.5;fill:#000000;fill-opacity:1;stroke:none"
d="m 234.20527,258.47303 c 0,0 -0.49003,3.81374 0.69989,5.47854 -0.40472,0.9103 -0.64157,1.91296 -0.64157,2.95471 0,4.35379 3.89014,7.87925 8.69026,7.87925 4.80012,0 8.69026,-3.52546 8.69026,-7.87925 0,-1.09761 -0.22388,-2.15913 -0.67072,-3.1086 1.05799,-1.73411 0.58323,-5.32465 0.58323,-5.32465 0,0 -3.16937,0.24047 -4.60758,1.44658 -1.1976,-0.5643 -2.55237,-0.89257 -3.99519,-0.89257 -1.47632,0 -2.86505,0.33433 -4.08267,0.92335 -1.41269,-1.24459 -4.66591,-1.47736 -4.66591,-1.47736 z"
id="path10222"
inkscape:connector-curvature="0" />
<flowRoot
transform="matrix(0.37262995,0,0,0.37262995,160.47179,172.53063)"
style="font-size:37.57078552px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Overpass;-inkscape-font-specification:Sans Bold"
id="flowRoot10236"
xml:space="preserve"><flowRegion
id="flowRegion10238"><rect
style="font-size:37.57078552px"
y="218.36218"
x="286"
height="135"
width="363"
id="rect10240" /></flowRegion><flowPara
id="flowPara10242">Github</flowPara></flowRoot> <flowRoot
xml:space="preserve"
id="flowRoot10244"
style="font-size:32.20352936px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Overpass;-inkscape-font-specification:Overpass"
transform="matrix(0.37262995,0,0,0.37262995,160.47179,188.79409)"><flowRegion
id="flowRegion10246"><rect
id="rect10248"
width="960.10712"
height="74.61837"
x="286"
y="218.36218"
style="font-size:32.20352936px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:Overpass;-inkscape-font-specification:Overpass" /></flowRegion><flowPara
style="font-size:32.20352936px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;opacity:0.5;font-family:Overpass;-inkscape-font-specification:Overpass"
id="flowPara10250">Free public repositories with collaborator management</flowPara></flowRoot> <flowRoot
transform="matrix(0.37262995,0,0,0.37262995,107.47179,69.53063)"
style="font-size:59.03980637px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Overpass;-inkscape-font-specification:Sans Bold"
id="flowRoot10252"
xml:space="preserve"><flowRegion
id="flowRegion10254"><rect
style="font-size:59.03980637px"
y="218.36218"
x="286"
height="118.89823"
width="660.88263"
id="rect10256" /></flowRegion><flowPara
id="flowPara10258">Where is your project?</flowPara></flowRoot> <flowRoot
transform="matrix(0.37262995,0,0,0.37262995,117.84437,374.44232)"
style="font-size:37.57078552px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;opacity:0.5;fill:#000000;fill-opacity:1;stroke:none;font-family:Overpass;-inkscape-font-specification:Overpass"
id="flowRoot10260"
xml:space="preserve"><flowRegion
id="flowRegion10262"><rect
style="font-size:37.57078552px;font-weight:normal;-inkscape-font-specification:Overpass"
y="218.36218"
x="286"
height="53.402714"
width="512.91132"
id="rect10264" /></flowRegion><flowPara
id="flowPara10266">domain name or IP address</flowPara></flowRoot> <flowRoot
xml:space="preserve"
id="flowRoot10274"
style="font-size:37.57078552px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;opacity:0.5;fill:#000000;fill-opacity:1;stroke:none;font-family:Overpass;-inkscape-font-specification:Overpass"
transform="matrix(0.37262995,0,0,0.37262995,357.84437,374.44232)"><flowRegion
id="flowRegion10276"><rect
id="rect10278"
width="512.91132"
height="53.402714"
x="286"
y="218.36218"
style="font-size:37.57078552px;font-weight:normal;-inkscape-font-specification:Overpass" /></flowRegion><flowPara
id="flowPara10280">/path/to/project</flowPara></flowRoot> <path
sodipodi:type="arc"
style="opacity:0.5;fill:#0093d9;fill-opacity:1;stroke:none"
id="path10282"
sodipodi:cx="431.5"
sodipodi:cy="417.36218"
sodipodi:rx="6.25"
sodipodi:ry="6.25"
d="m 437.75,417.36218 a 6.25,6.25 0 1 1 -12.5,0 6.25,6.25 0 1 1 12.5,0 z"
transform="translate(-6.7500001,18.75)" />
<flowRoot
xml:space="preserve"
id="flowRoot10284"
style="font-size:37.57078552px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none;font-family:Overpass;-inkscape-font-specification:Sans Bold"
transform="matrix(0.26916654,0,0,0.26916654,345.40245,372.9967)"><flowRegion
id="flowRegion10286"><rect
id="rect10288"
width="363"
height="135"
x="286"
y="218.36218"
style="font-size:37.57078552px;fill:#ffffff;fill-opacity:1" /></flowRegion><flowPara
id="flowPara10290">?</flowPara></flowRoot> <path
transform="translate(233.25,18.75)"
d="m 437.75,417.36218 a 6.25,6.25 0 1 1 -12.5,0 6.25,6.25 0 1 1 12.5,0 z"
sodipodi:ry="6.25"
sodipodi:rx="6.25"
sodipodi:cy="417.36218"
sodipodi:cx="431.5"
id="path10292"
style="opacity:0.5;fill:#0093d9;fill-opacity:1;stroke:none"
sodipodi:type="arc" />
<flowRoot
transform="matrix(0.26916654,0,0,0.26916654,585.40245,372.9967)"
style="font-size:37.57078552px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none;font-family:Overpass;-inkscape-font-specification:Sans Bold"
id="flowRoot10294"
xml:space="preserve"><flowRegion
id="flowRegion10296"><rect
style="font-size:37.57078552px;fill:#ffffff;fill-opacity:1"
y="218.36218"
x="286"
height="135"
width="363"
id="rect10298" /></flowRegion><flowPara
id="flowPara10300">?</flowPara></flowRoot> <rect
y="511.4278"
x="577.70184"
height="23.867767"
width="95.232468"
id="rect10302"
style="fill:none;stroke:#000000;stroke-width:1.13221943;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
<rect
style="fill:none;stroke:#000000;stroke-width:1.13221943;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
id="rect10304"
width="95.232468"
height="23.867767"
x="472.35767"
y="511.4278" />
<flowRoot
xml:space="preserve"
id="flowRoot10306"
style="font-size:37.57078552px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Overpass;-inkscape-font-specification:Sans Bold"
transform="matrix(0.37262995,0,0,0.37262995,503.35909,435.69358)"><flowRegion
id="flowRegion10308"><rect
id="rect10310"
width="363"
height="135"
x="286"
y="218.36218"
style="font-size:37.57078552px" /></flowRegion><flowPara
id="flowPara10312">Add</flowPara></flowRoot> <flowRoot
transform="matrix(0.37262995,0,0,0.37262995,391.35909,435.69358)"
style="font-size:37.57078552px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Overpass;-inkscape-font-specification:Sans Bold"
id="flowRoot10314"
xml:space="preserve"><flowRegion
id="flowRegion10316"><rect
style="font-size:37.57078552px"
y="218.36218"
x="286"
height="135"
width="363"
id="rect10318" /></flowRegion><flowPara
id="flowPara10320">Cancel</flowPara></flowRoot> <flowRoot
xml:space="preserve"
id="flowRoot10324"
style="font-size:37.57078552px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Overpass;-inkscape-font-specification:Sans Bold"
transform="matrix(0.37262995,0,0,0.37262995,160.47179,214.53063)"><flowRegion
id="flowRegion10326"><rect
id="rect10328"
width="363"
height="135"
x="286"
y="218.36218"
style="font-size:37.57078552px" /></flowRegion><flowPara
id="flowPara10330">Gitorious</flowPara></flowRoot> <flowRoot
transform="matrix(0.37262995,0,0,0.37262995,160.47179,230.79409)"
style="font-size:32.20352936px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Overpass;-inkscape-font-specification:Overpass"
id="flowRoot10332"
xml:space="preserve"><flowRegion
id="flowRegion10334"><rect
style="font-size:32.20352936px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:Overpass;-inkscape-font-specification:Overpass"
y="218.36218"
x="286"
height="74.61837"
width="960.10712"
id="rect10336" /></flowRegion><flowPara
id="flowPara10338"
style="font-size:32.20352936px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;opacity:0.5;font-family:Overpass;-inkscape-font-specification:Overpass">Open source infrastructure for hosting open source projects</flowPara></flowRoot> <flowRoot
transform="matrix(0.37262995,0,0,0.37262995,160.47179,143.79409)"
style="font-size:32.20352936px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none;font-family:Overpass;-inkscape-font-specification:Overpass"
id="flowRoot10340"
xml:space="preserve"><flowRegion
id="flowRegion10342"><rect
style="font-size:32.20352936px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#ffffff;fill-opacity:1;font-family:Overpass;-inkscape-font-specification:Overpass"
y="218.36218"
x="286"
height="74.61837"
width="960.10712"
id="rect10344" /></flowRegion><flowPara
id="flowPara10346"
style="font-size:32.20352936px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;opacity:0.5;fill:#ffffff;fill-opacity:1;font-family:Overpass;-inkscape-font-specification:Overpass">Everything under my control</flowPara></flowRoot> <path
sodipodi:type="arc"
style="opacity:0.5;fill:#000000;fill-opacity:1;stroke:none"
id="path10348"
sodipodi:cx="255.375"
sodipodi:cy="284.73718"
sodipodi:rx="8.375"
sodipodi:ry="8.375"
d="m 263.75,284.73718 a 8.375,8.375 0 1 1 -16.75,0 8.375,8.375 0 1 1 16.75,0 z"
transform="matrix(1.3731343,0,0,1.1492537,-106.03918,-19.498087)" />
<flowRoot
xml:space="preserve"
id="flowRoot10350"
style="font-size:40px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none;font-family:Overpass;-inkscape-font-specification:Sans Bold"
transform="matrix(0.38366294,0,0,0.38366294,154.47427,185.77404)"><flowRegion
id="flowRegion10352"><rect
id="rect10354"
width="124.25"
height="78"
x="220.25"
y="300.36218"
style="fill:#ffffff;fill-opacity:1" /></flowRegion><flowPara
id="flowPara10356">G</flowPara></flowRoot> <rect
y="201.84201"
x="653.06659"
height="210.3028"
width="20.867798"
id="rect10406"
style="fill:#e7e8e9;stroke:#000000;stroke-width:1.13221942999999992;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
<rect
style="fill:#ffffff;stroke:#000000;stroke-width:1.13221942999999992;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;fill-opacity:1"
id="rect10408"
width="20.867798"
height="20.444624"
x="653.06659"
y="201.84201" />
<rect
y="391.34662"
x="653.06659"
height="20.444624"
width="20.867798"
id="rect10412"
style="fill:#ffffff;stroke:#000000;stroke-width:1.13221942999999992;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;fill-opacity:1" />
<path
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 667.61366,213.52367 -4.19991,-4.07696 -4.25764,4.16822"
id="path10414"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccc" />
<path
sodipodi:nodetypes="ccc"
inkscape:connector-curvature="0"
id="path10416"
d="m 667.61366,400.10325 -4.19991,4.07696 -4.25764,-4.16822"
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 19 KiB