Put everything in a monodevelop project.
1
.gitignore
vendored
|
@ -1,2 +1,3 @@
|
||||||
*~
|
*~
|
||||||
*.exe
|
*.exe
|
||||||
|
*.exe.mdb
|
||||||
|
|
31
Makefile
|
@ -1,26 +1,27 @@
|
||||||
SparklePony.exe : src/SparklePony.cs
|
SparkleShare.exe : SparkleShare.sln
|
||||||
gmcs -pkg:gtk-sharp-2.0 -pkg:notify-sharp -pkg:dbus-sharp src/SparklePony.cs
|
mdtool build --f --buildfile:SparkleShare.sln
|
||||||
|
|
||||||
install:
|
install:
|
||||||
mkdir -p /usr/local/share/sparklepony
|
mkdir -p /usr/local/share/sparkleshare
|
||||||
cp src/SparklePony.exe /usr/local/share/sparklepony/
|
cp SparkleShare/bin/Debug/SparkleShare.exe /usr/local/share/sparkleshare/
|
||||||
chmod 755 /usr/local/share/sparklepony/SparklePony.exe
|
cp SparkleShare/bin/Debug/SparkleShare.exe.mdb /usr/local/share/sparkleshare/
|
||||||
cp src/sparklepony /usr/local/bin/
|
chmod 755 /usr/local/share/sparkleshare/SparkleShare.exe
|
||||||
chmod 755 /usr/local/bin/sparklepony
|
cp sparkleshare /usr/local/bin/
|
||||||
|
chmod 755 /usr/local/bin/sparkleshare
|
||||||
cp data/icons /usr/share/ -R
|
cp data/icons /usr/share/ -R
|
||||||
mkdir -p ~/.config/autostart
|
mkdir -p ~/.config/autostart
|
||||||
cp data/sparklepony.desktop.in ~/.config/autostart/sparklepony.desktop
|
cp data/sparkleshare.desktop.in ~/.config/autostart/sparkleshare.desktop
|
||||||
chmod 775 ~/.config/autostart/sparklepony.desktop
|
chmod 775 ~/.config/autostart/sparkleshare.desktop
|
||||||
gtk-update-icon-cache /usr/share/icons/hicolor -f
|
gtk-update-icon-cache /usr/share/icons/hicolor -f
|
||||||
|
|
||||||
uninstall:
|
uninstall:
|
||||||
rm /usr/local/bin/sparklepony
|
rm /usr/local/bin/sparkleshare
|
||||||
rm /usr/local/share/sparklepony/SparklePony.exe
|
rm /usr/local/share/sparkleshare/SparkleShare.exe
|
||||||
rmdir /usr/local/share/sparklepony
|
rmdir /usr/local/share/sparkleshare
|
||||||
rm /usr/share/icons/hicolor/*x*/places/folder-sparklepony.png
|
rm /usr/share/icons/hicolor/*x*/places/folder-sparkleshare.png
|
||||||
rm /usr/share/icons/hicolor/*x*/status/document-*ed.png
|
rm /usr/share/icons/hicolor/*x*/status/document-*ed.png
|
||||||
rm /usr/share/icons/hicolor/*x*/status/avatar-default.png
|
rm /usr/share/icons/hicolor/*x*/status/avatar-default.png
|
||||||
rm ~/.config/autostart/sparklepony.desktop
|
rm ~/.config/autostart/sparkleshare.desktop
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm src/SparklePony.exe
|
rm src/sparkleshare.exe
|
||||||
|
|
24
README
|
@ -1,29 +1,29 @@
|
||||||
SparklePony is a file sharing and collaboration tool inspired by DropBox. It
|
SparkleShare is a file sharing and collaboration tool inspired by DropBox. It
|
||||||
allows you to instantly sync with any Git repository you have access to.
|
allows you to instantly sync with any Git repository you have access to.
|
||||||
|
|
||||||
SparklePony isn't just a piece of software, it's part of your workflow. It's
|
SparkleShare isn't just a piece of software, it's part of your workflow. It's
|
||||||
designed to make sharing documents and collaboration easier, and to make peers
|
designed to make sharing documents and collaboration easier, and to make peers
|
||||||
aware of what you are doing. The user interface and features are made to
|
aware of what you are doing. The user interface and features are made to
|
||||||
support this goal. However, you may find SparklePony useful for other kinds of
|
support this goal. However, you may find SparkleShare useful for other kinds of
|
||||||
purposes as well, like backing up files or monitoring your favourite project.
|
purposes as well, like backing up files or monitoring your favourite project.
|
||||||
|
|
||||||
SparklePony is not designed to be:
|
SparkleShare is not designed to be:
|
||||||
- a graphical frontend for git
|
- a graphical frontend for git
|
||||||
- a backup tool
|
- a backup tool
|
||||||
|
|
||||||
In contrast to the projects name, we will very likely
|
In contrast to the projects name, we will very likely
|
||||||
refuse to implement your personal ponies. :)
|
refuse to implement your personal ponies. :)
|
||||||
|
|
||||||
SparklePony currently only works on Linux/GNOME.
|
SparkleShare currently only works on Linux/GNOME.
|
||||||
Windows and OSX ports are planned for the future.
|
Windows and OSX ports are planned for the future.
|
||||||
|
|
||||||
|
|
||||||
SparklePony is free software and licensed under the GNU GPLv3. You are welcome
|
SparkleShare is free software and licensed under the GNU GPLv3. You are welcome
|
||||||
to change and redistribute it under certain conditions. For more information
|
to change and redistribute it under certain conditions. For more information
|
||||||
see the LICENSE file or visit http://www.gnu.org/licenses/gpl-3.0.html
|
see the LICENSE file or visit http://www.gnu.org/licenses/gpl-3.0.html
|
||||||
|
|
||||||
|
|
||||||
SparklePony currently requires (in alphabetical order):
|
SparkleShare currently requires (in alphabetical order):
|
||||||
- ndesk-dbus-devel >= 0.6
|
- ndesk-dbus-devel >= 0.6
|
||||||
- git >= 1.7
|
- git >= 1.7
|
||||||
- gtk-sharp2 >= 2.12.7
|
- gtk-sharp2 >= 2.12.7
|
||||||
|
@ -35,23 +35,23 @@ Installing dependencies on Fedora:
|
||||||
# yum install git gtk-sharp2 gtk-sharp2-devel mono-core notify-sharp \
|
# yum install git gtk-sharp2 gtk-sharp2-devel mono-core notify-sharp \
|
||||||
notify-sharp-devel ndesk-dbus-devel
|
notify-sharp-devel ndesk-dbus-devel
|
||||||
|
|
||||||
You can build and install SparklePony like this:
|
You can build and install SparkleShare like this:
|
||||||
$ make
|
$ make
|
||||||
$ su
|
$ su
|
||||||
# make install
|
# make install
|
||||||
|
|
||||||
Run the service:
|
Run the service:
|
||||||
$ sparklepony start
|
$ sparkleshare start
|
||||||
|
|
||||||
You can stop the service via the graphical interface or by typing:
|
You can stop the service via the graphical interface or by typing:
|
||||||
$ sparklepony stop
|
$ sparkleshare stop
|
||||||
|
|
||||||
For help:
|
For help:
|
||||||
$ sparklepony --help
|
$ sparkleshare --help
|
||||||
|
|
||||||
|
|
||||||
The official website is:
|
The official website is:
|
||||||
http://www.github.com/hbons/SparklePony
|
http://www.github.com/hbons/SparkleShare
|
||||||
|
|
||||||
|
|
||||||
Have fun! :)
|
Have fun! :)
|
||||||
|
|
22
SparkleShare.sln
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
|
||||||
|
Microsoft Visual Studio Solution File, Format Version 9.00
|
||||||
|
# Visual Studio 2005
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SparkleShare", "SparkleShare\SparkleShare.csproj", "{728483AA-E34B-4441-BF2C-C8BC2901E4E0}"
|
||||||
|
EndProject
|
||||||
|
Global
|
||||||
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
|
Debug|Any CPU = Debug|Any CPU
|
||||||
|
Release|Any CPU = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||||
|
{728483AA-E34B-4441-BF2C-C8BC2901E4E0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{728483AA-E34B-4441-BF2C-C8BC2901E4E0}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{728483AA-E34B-4441-BF2C-C8BC2901E4E0}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{728483AA-E34B-4441-BF2C-C8BC2901E4E0}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(MonoDevelopProperties) = preSolution
|
||||||
|
name = SparkleShare
|
||||||
|
version = 0.1
|
||||||
|
StartupItem = SparkleShare\SparkleShare.csproj
|
||||||
|
EndGlobalSection
|
||||||
|
EndGlobal
|
25
SparkleShare.userprefs
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
<Properties>
|
||||||
|
<MonoDevelop.Ide.Workspace ActiveConfiguration="Debug" ctype="Workspace" />
|
||||||
|
<MonoDevelop.Ide.Workbench ActiveDocument="SparkleShare/SparklePonyWindow.cs" ctype="Workbench">
|
||||||
|
<Files>
|
||||||
|
<File FileName="SparkleShare/Main.cs" Line="35" Column="3" />
|
||||||
|
<File FileName="SparkleShare/Repository.cs" Line="463" Column="1" />
|
||||||
|
<File FileName="SparkleShare/SparklePonyUI.cs" Line="18" Column="21" />
|
||||||
|
<File FileName="SparkleShare/SparklePonyStatusIcon.cs" Line="18" Column="21" />
|
||||||
|
<File FileName="SparkleShare/SparklePonyWindow.cs" Line="490" Column="22" />
|
||||||
|
</Files>
|
||||||
|
<Pads>
|
||||||
|
<Pad Id="ProjectPad">
|
||||||
|
<State expanded="True">
|
||||||
|
<Node name="SparkleShare" expanded="True">
|
||||||
|
<Node name="References" expanded="True" />
|
||||||
|
<Node name="SparklePonyWindow.cs" selected="True" />
|
||||||
|
</Node>
|
||||||
|
</State>
|
||||||
|
</Pad>
|
||||||
|
<Pad Id="ClassPad">
|
||||||
|
<State expanded="True" selected="True" />
|
||||||
|
</Pad>
|
||||||
|
</Pads>
|
||||||
|
</MonoDevelop.Ide.Workbench>
|
||||||
|
</Properties>
|
2
SparkleShare.usertasks
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<ArrayOfUserTask xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" />
|
106
SparkleShare/Main.cs
Normal file
|
@ -0,0 +1,106 @@
|
||||||
|
// SparkleShare, an instant update workflow to Git.
|
||||||
|
// 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 Gtk;
|
||||||
|
using Notifications;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.IO;
|
||||||
|
using System.Net;
|
||||||
|
using System.Security.Cryptography;
|
||||||
|
using System.Text;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
using System.Timers;
|
||||||
|
|
||||||
|
|
||||||
|
namespace SparkleShare {
|
||||||
|
|
||||||
|
// This is SparkleShare!
|
||||||
|
public class SparkleShare {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public static SparkleShareUI SparkleShareUI;
|
||||||
|
|
||||||
|
public static void Main (string [] args) {
|
||||||
|
|
||||||
|
// Check if git is installed
|
||||||
|
Process Process = new Process();
|
||||||
|
Process.StartInfo.RedirectStandardOutput = true;
|
||||||
|
Process.StartInfo.UseShellExecute = false;
|
||||||
|
Process.StartInfo.FileName = "git";
|
||||||
|
Process.Start();
|
||||||
|
if (Process.StandardOutput.ReadToEnd().IndexOf ("version") == -1) {
|
||||||
|
Console.WriteLine ("Git wasn't found.");
|
||||||
|
Console.WriteLine ("You can get it from http://git-scm.com/.");
|
||||||
|
Environment.Exit (0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't allow running as root
|
||||||
|
Process.StartInfo.FileName = "whoami";
|
||||||
|
Process.Start();
|
||||||
|
if (Process.StandardOutput.ReadToEnd().Trim ().Equals ("root")) {
|
||||||
|
Console.WriteLine ("Sorry, you can't run SparkleShare as root.");
|
||||||
|
Console.WriteLine ("Things will go utterly wrong.");
|
||||||
|
Environment.Exit (0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse the command line arguments
|
||||||
|
bool HideUI = false;
|
||||||
|
if (args.Length > 0) {
|
||||||
|
foreach (string Argument in args) {
|
||||||
|
if (Argument.Equals ("--disable-gui") || Argument.Equals ("-d"))
|
||||||
|
HideUI = true;
|
||||||
|
if (Argument.Equals ("--help") || Argument.Equals ("-h")) {
|
||||||
|
ShowHelp ();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Gtk.Application.Init ();
|
||||||
|
|
||||||
|
SparkleShareUI = new SparkleShareUI (HideUI);
|
||||||
|
SparkleShareUI.StartMonitoring ();
|
||||||
|
|
||||||
|
Gtk.Application.Run ();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void ShowHelp () {
|
||||||
|
Console.WriteLine ("SparkleShare Copyright (C) 2010 Hylke Bons");
|
||||||
|
Console.WriteLine ("");
|
||||||
|
Console.WriteLine ("This program comes with ABSOLUTELY NO WARRANTY.");
|
||||||
|
Console.WriteLine ("This is free software, and you are welcome to redistribute it ");
|
||||||
|
Console.WriteLine ("under certain conditions. Please read the GNU GPLv3 for details.");
|
||||||
|
Console.WriteLine ("");
|
||||||
|
Console.WriteLine ("SparkleShare syncs the ~/SparkleShare folder with remote repositories.");
|
||||||
|
Console.WriteLine ("");
|
||||||
|
Console.WriteLine ("Usage: sparkleshare [start|stop|restart] [OPTION]...");
|
||||||
|
Console.WriteLine ("Sync SparkleShare folder with remote repositories.");
|
||||||
|
Console.WriteLine ("");
|
||||||
|
Console.WriteLine ("Arguments:");
|
||||||
|
Console.WriteLine ("\t -d, --disable-gui\tDon't show the notification icon.");
|
||||||
|
Console.WriteLine ("\t -h, --help\t\tDisplay this help text.");
|
||||||
|
Console.WriteLine ("");
|
||||||
|
Environment.Exit (0);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
464
SparkleShare/Repository.cs
Normal file
|
@ -0,0 +1,464 @@
|
||||||
|
// SparkleShare, an instant update workflow to Git.
|
||||||
|
// 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 Gtk;
|
||||||
|
using Notifications;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.IO;
|
||||||
|
using System.Net;
|
||||||
|
using System.Security.Cryptography;
|
||||||
|
using System.Text;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
using System.Timers;
|
||||||
|
|
||||||
|
namespace SparkleShare {
|
||||||
|
|
||||||
|
// Repository class holds repository information and timers
|
||||||
|
public class Repository {
|
||||||
|
|
||||||
|
private Process Process;
|
||||||
|
private Timer FetchTimer;
|
||||||
|
private Timer BufferTimer;
|
||||||
|
private FileSystemWatcher Watcher;
|
||||||
|
|
||||||
|
public string Name;
|
||||||
|
public string Domain;
|
||||||
|
public string LocalPath;
|
||||||
|
public string RemoteOriginUrl;
|
||||||
|
public string CurrentHash;
|
||||||
|
|
||||||
|
public string UserEmail;
|
||||||
|
public string UserName;
|
||||||
|
public bool MonitorOnly;
|
||||||
|
|
||||||
|
public Repository (string Path) {
|
||||||
|
|
||||||
|
MonitorOnly = false;
|
||||||
|
|
||||||
|
Process = new Process();
|
||||||
|
Process.EnableRaisingEvents = false;
|
||||||
|
Process.StartInfo.RedirectStandardOutput = true;
|
||||||
|
Process.StartInfo.UseShellExecute = false;
|
||||||
|
|
||||||
|
// Get the repository's path, example: "/home/user/SparkleShare/repo/"
|
||||||
|
LocalPath = Path;
|
||||||
|
Process.StartInfo.WorkingDirectory = LocalPath + "/";
|
||||||
|
|
||||||
|
// Get user.name, example: "User Name"
|
||||||
|
UserName = "Anonymous";
|
||||||
|
Process.StartInfo.FileName = "git";
|
||||||
|
Process.StartInfo.Arguments = "config --get user.name";
|
||||||
|
Process.Start();
|
||||||
|
UserName = Process.StandardOutput.ReadToEnd().Trim ();
|
||||||
|
|
||||||
|
// Get user.email, example: "user@github.com"
|
||||||
|
UserEmail = "not.set@git-scm.com";
|
||||||
|
Process.StartInfo.FileName = "git";
|
||||||
|
Process.StartInfo.Arguments = "config --get user.email";
|
||||||
|
Process.Start();
|
||||||
|
UserEmail = Process.StandardOutput.ReadToEnd().Trim ();
|
||||||
|
|
||||||
|
// Get remote.origin.url, example: "ssh://git@github.com/user/repo"
|
||||||
|
Process.StartInfo.FileName = "git";
|
||||||
|
Process.StartInfo.Arguments = "config --get remote.origin.url";
|
||||||
|
Process.Start();
|
||||||
|
RemoteOriginUrl = Process.StandardOutput.ReadToEnd().Trim ();
|
||||||
|
|
||||||
|
// Get the repository name, example: "Project"
|
||||||
|
|
||||||
|
string s = LocalPath.TrimEnd ( "/".ToCharArray ());
|
||||||
|
Name = LocalPath.Substring (s.LastIndexOf ("/") + 1);
|
||||||
|
|
||||||
|
// Get the domain, example: "github.com"
|
||||||
|
Domain = RemoteOriginUrl;
|
||||||
|
Domain = Domain.Substring (Domain.IndexOf ("@") + 1);
|
||||||
|
if (Domain.IndexOf (":") > -1)
|
||||||
|
Domain = Domain.Substring (0, Domain.IndexOf (":"));
|
||||||
|
else
|
||||||
|
Domain = Domain.Substring (0, Domain.IndexOf ("/"));
|
||||||
|
|
||||||
|
// Get hash of the current commit
|
||||||
|
Process.StartInfo.FileName = "git";
|
||||||
|
Process.StartInfo.Arguments = "rev-list --max-count=1 HEAD";
|
||||||
|
Process.Start();
|
||||||
|
CurrentHash = Process.StandardOutput.ReadToEnd().Trim ();
|
||||||
|
|
||||||
|
// Watch the repository's folder
|
||||||
|
Watcher = new FileSystemWatcher (LocalPath);
|
||||||
|
Watcher.IncludeSubdirectories = true;
|
||||||
|
Watcher.EnableRaisingEvents = true;
|
||||||
|
Watcher.Filter = "*";
|
||||||
|
Watcher.Changed += new FileSystemEventHandler(OnFileActivity);
|
||||||
|
Watcher.Created += new FileSystemEventHandler(OnFileActivity);
|
||||||
|
Watcher.Deleted += new FileSystemEventHandler(OnFileActivity);
|
||||||
|
|
||||||
|
// Fetch remote changes every 20 seconds
|
||||||
|
FetchTimer = new Timer ();
|
||||||
|
FetchTimer.Interval = 20000;
|
||||||
|
FetchTimer.Elapsed += delegate {
|
||||||
|
Fetch ();
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
FetchTimer.Start();
|
||||||
|
BufferTimer = new Timer ();
|
||||||
|
|
||||||
|
// Add everything that changed
|
||||||
|
// since SparkleShare was stopped
|
||||||
|
Add ();
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Starts a time buffer when something changes
|
||||||
|
public void OnFileActivity (object o, FileSystemEventArgs args) {
|
||||||
|
WatcherChangeTypes wct = args.ChangeType;
|
||||||
|
if (!ShouldIgnore (args.Name) && !MonitorOnly) {
|
||||||
|
Console.WriteLine("[Event][" + Name + "] " + wct.ToString() +
|
||||||
|
" '" + args.Name + "'");
|
||||||
|
StartBufferTimer ();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// A buffer that will fetch changes after
|
||||||
|
// file activity has settles down
|
||||||
|
public void StartBufferTimer () {
|
||||||
|
|
||||||
|
int Interval = 2000;
|
||||||
|
if (!BufferTimer.Enabled) {
|
||||||
|
|
||||||
|
// Delay for a few seconds to see if more files change
|
||||||
|
BufferTimer.Interval = Interval;
|
||||||
|
BufferTimer.Elapsed += delegate (object o, ElapsedEventArgs args) {
|
||||||
|
Console.WriteLine ("[Buffer][" + Name + "] Done waiting.");
|
||||||
|
Add ();
|
||||||
|
};
|
||||||
|
Console.WriteLine ("[Buffer][" + Name + "] " +
|
||||||
|
"Waiting for more changes...");
|
||||||
|
|
||||||
|
BufferTimer.Start();
|
||||||
|
} else {
|
||||||
|
|
||||||
|
// Extend the delay when something changes
|
||||||
|
BufferTimer.Close ();
|
||||||
|
BufferTimer = new Timer ();
|
||||||
|
BufferTimer.Interval = Interval;
|
||||||
|
BufferTimer.Elapsed += delegate (object o, ElapsedEventArgs args) {
|
||||||
|
Console.WriteLine ("[Buffer][" + Name + "] Done waiting.");
|
||||||
|
Add ();
|
||||||
|
};
|
||||||
|
|
||||||
|
BufferTimer.Start();
|
||||||
|
Console.WriteLine ("[Buffer][" + Name + "] " +
|
||||||
|
"Waiting for more changes...");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clones a remote repo
|
||||||
|
public void Clone () {
|
||||||
|
Process.StartInfo.Arguments = "clone " + RemoteOriginUrl;
|
||||||
|
Process.Start();
|
||||||
|
|
||||||
|
// Add a gitignore file
|
||||||
|
TextWriter Writer = new StreamWriter(LocalPath + ".gitignore");
|
||||||
|
Writer.WriteLine("*~"); // Ignore gedit swap files
|
||||||
|
Writer.WriteLine(".*.sw?"); // Ignore vi swap files
|
||||||
|
Writer.Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stages the made changes
|
||||||
|
public void Add () {
|
||||||
|
BufferTimer.Stop ();
|
||||||
|
Console.WriteLine ("[Git][" + Name + "] Staging changes...");
|
||||||
|
Process.StartInfo.Arguments = "add --all";
|
||||||
|
Process.Start();
|
||||||
|
|
||||||
|
string Message = FormatCommitMessage ();
|
||||||
|
if (!Message.Equals ("")) {
|
||||||
|
Commit (Message);
|
||||||
|
Push ();
|
||||||
|
Fetch ();
|
||||||
|
// Push again in case of a conflict
|
||||||
|
Push ();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Commits the made changes
|
||||||
|
public void Commit (string Message) {
|
||||||
|
Console.WriteLine ("[Commit][" + Name + "] " + Message);
|
||||||
|
Console.WriteLine ("[Git][" + Name + "] Commiting changes...");
|
||||||
|
Process.StartInfo.Arguments = "commit -m \"" + Message + "\"";
|
||||||
|
Process.Start();
|
||||||
|
ShowEventNotification (UserName + " " + Message,
|
||||||
|
GetAvatarFileName (UserEmail, 48), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetches changes from the remote repo
|
||||||
|
public void Fetch () {
|
||||||
|
// TODO: change status icon to sync
|
||||||
|
FetchTimer.Stop ();
|
||||||
|
Console.WriteLine ("[Git][" + Name + "] Fetching changes...");
|
||||||
|
Process.StartInfo.Arguments = "fetch";
|
||||||
|
Process.Start();
|
||||||
|
Process.WaitForExit ();
|
||||||
|
Merge ();
|
||||||
|
FetchTimer.Start ();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Merges the fetched changes
|
||||||
|
public void Merge () {
|
||||||
|
Watcher.EnableRaisingEvents = false;
|
||||||
|
|
||||||
|
Console.WriteLine ("[Git][" + Name + "] Merging fetched changes...");
|
||||||
|
|
||||||
|
Process.StartInfo.Arguments = "merge origin/master";
|
||||||
|
Process.Start();
|
||||||
|
Process.WaitForExit ();
|
||||||
|
string Output = Process.StandardOutput.ReadToEnd().Trim ();
|
||||||
|
|
||||||
|
// Show notification if there are updates
|
||||||
|
if (!Output.Equals ("Already up-to-date.")) {
|
||||||
|
|
||||||
|
// Get the last commit message
|
||||||
|
Process.StartInfo.Arguments = "log --format=\"%ae\" -1";
|
||||||
|
Process.Start();
|
||||||
|
string LastCommitEmail = Process.StandardOutput.ReadToEnd().Trim ();
|
||||||
|
|
||||||
|
// Get the last commit message
|
||||||
|
Process.StartInfo.Arguments = "log --format=\"%s\" -1";
|
||||||
|
Process.Start();
|
||||||
|
string LastCommitMessage = Process.StandardOutput.ReadToEnd().Trim ();
|
||||||
|
|
||||||
|
// Get the last commiter
|
||||||
|
Process.StartInfo.Arguments = "log --format=\"%an\" -1";
|
||||||
|
Process.Start();
|
||||||
|
string LastCommitUserName = Process.StandardOutput.ReadToEnd().Trim ();
|
||||||
|
|
||||||
|
ShowEventNotification (LastCommitUserName + " " + LastCommitMessage,
|
||||||
|
GetAvatarFileName (LastCommitEmail, 48), true);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Watcher.EnableRaisingEvents = true;
|
||||||
|
// TODO: change status icon to normal
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pushes the changes to the remote repo
|
||||||
|
public void Push () {
|
||||||
|
// TODO: What happens when network disconnects during a push
|
||||||
|
Console.WriteLine ("[Git][" + Name + "] Pushing changes...");
|
||||||
|
Process.StartInfo.Arguments = "push";
|
||||||
|
Process.Start();
|
||||||
|
Process.WaitForExit ();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ignores Repos, dotfiles, swap files and the like.
|
||||||
|
public bool ShouldIgnore (string FileName) {
|
||||||
|
if (FileName.Substring (0, 1).Equals (".") ||
|
||||||
|
FileName.Contains (".lock") ||
|
||||||
|
FileName.Contains (".git") ||
|
||||||
|
FileName.Contains ("/.") ||
|
||||||
|
Directory.Exists (LocalPath + FileName))
|
||||||
|
return true; // Yes, ignore it.
|
||||||
|
else if (FileName.Length > 3 &&
|
||||||
|
FileName.Substring (FileName.Length - 4).Equals (".swp"))
|
||||||
|
return true;
|
||||||
|
else return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Creates a pretty commit message based on what has changed
|
||||||
|
public string FormatCommitMessage () {
|
||||||
|
|
||||||
|
bool DoneAddCommit = false;
|
||||||
|
bool DoneEditCommit = false;
|
||||||
|
bool DoneRenameCommit = false;
|
||||||
|
bool DoneDeleteCommit = false;
|
||||||
|
int FilesAdded = 0;
|
||||||
|
int FilesEdited = 0;
|
||||||
|
int FilesRenamed = 0;
|
||||||
|
int FilesDeleted = 0;
|
||||||
|
|
||||||
|
Process.StartInfo.Arguments = "status";
|
||||||
|
Process.Start();
|
||||||
|
string Output = Process.StandardOutput.ReadToEnd();
|
||||||
|
|
||||||
|
foreach (string Line in Regex.Split (Output, "\n")) {
|
||||||
|
if (Line.IndexOf ("new file:") > -1)
|
||||||
|
FilesAdded++;
|
||||||
|
if (Line.IndexOf ("modified:") > -1)
|
||||||
|
FilesEdited++;
|
||||||
|
if (Line.IndexOf ("renamed:") > -1)
|
||||||
|
FilesRenamed++;
|
||||||
|
if (Line.IndexOf ("deleted:") > -1)
|
||||||
|
FilesDeleted++;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (string Line in Regex.Split (Output, "\n")) {
|
||||||
|
|
||||||
|
// Format message for when files are added,
|
||||||
|
// example: "added 'file' and 3 more."
|
||||||
|
if (Line.IndexOf ("new file:") > -1 && !DoneAddCommit) {
|
||||||
|
DoneAddCommit = true;
|
||||||
|
if (FilesAdded > 1)
|
||||||
|
return "added ‘" +
|
||||||
|
Line.Replace ("#\tnew file:", "").Trim () +
|
||||||
|
"’ and " + (FilesAdded - 1) + " more.";
|
||||||
|
else
|
||||||
|
return "added ‘" +
|
||||||
|
Line.Replace ("#\tnew file:", "").Trim () + "’.";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Format message for when files are edited,
|
||||||
|
// example: "edited 'file'."
|
||||||
|
if (Line.IndexOf ("modified:") > -1 && !DoneEditCommit) {
|
||||||
|
DoneEditCommit = true;
|
||||||
|
if (FilesEdited > 1)
|
||||||
|
return "edited ‘" +
|
||||||
|
Line.Replace ("#\tmodified:", "").Trim () +
|
||||||
|
"’ and " + (FilesEdited - 1) + " more.";
|
||||||
|
else
|
||||||
|
return "edited ‘" +
|
||||||
|
Line.Replace ("#\tmodified:", "").Trim () + "’.";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Format message for when files are edited,
|
||||||
|
// example: "deleted 'file'."
|
||||||
|
if (Line.IndexOf ("deleted:") > -1 && !DoneDeleteCommit) {
|
||||||
|
DoneDeleteCommit = true;
|
||||||
|
if (FilesDeleted > 1)
|
||||||
|
return "deleted ‘" +
|
||||||
|
Line.Replace ("#\tdeleted:", "").Trim () +
|
||||||
|
"’ and " + (FilesDeleted - 1) + " more.";
|
||||||
|
else
|
||||||
|
return "deleted ‘" +
|
||||||
|
Line.Replace ("#\tdeleted:", "").Trim () + "’.";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Format message for when files are renamed,
|
||||||
|
// example: "renamed 'file' to 'new name'."
|
||||||
|
if (Line.IndexOf ("renamed:") > -1 && !DoneRenameCommit) {
|
||||||
|
DoneDeleteCommit = true;
|
||||||
|
if (FilesRenamed > 1)
|
||||||
|
return "renamed ‘" +
|
||||||
|
Line.Replace ("#\trenamed:", "").Trim ().Replace
|
||||||
|
(" -> ", "’ to ‘") + "’ and " + (FilesDeleted - 1) +
|
||||||
|
" more.";
|
||||||
|
else
|
||||||
|
return "renamed ‘" +
|
||||||
|
Line.Replace ("#\trenamed:", "").Trim ().Replace
|
||||||
|
(" -> ", "’ to ‘") + "’.";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Nothing happened:
|
||||||
|
return "";
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shows a notification with text and image
|
||||||
|
public void ShowEventNotification (string Title,
|
||||||
|
string IconFileName,
|
||||||
|
bool ShowButtons) {
|
||||||
|
|
||||||
|
Notification Notification = new Notification (Title, " ");
|
||||||
|
Notification.Urgency = Urgency.Low;
|
||||||
|
Notification.Timeout = 4500;
|
||||||
|
Notification.Icon = new Gdk.Pixbuf (IconFileName);
|
||||||
|
|
||||||
|
// Add a button to open the folder where the changed file is
|
||||||
|
if (ShowButtons)
|
||||||
|
Notification.AddAction ("", "Open Folder",
|
||||||
|
delegate (object o, ActionArgs args) {
|
||||||
|
Process.StartInfo.FileName = "xdg-open";
|
||||||
|
Process.StartInfo.Arguments = LocalPath;
|
||||||
|
Process.Start();
|
||||||
|
Process.StartInfo.FileName = "git";
|
||||||
|
} );
|
||||||
|
Notification.Show ();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public static string GetAvatarFileName (string Email, int Size) {
|
||||||
|
|
||||||
|
string AvatarPath = Environment.GetEnvironmentVariable("HOME") +
|
||||||
|
"/.config/sparkleshare/avatars/" +
|
||||||
|
Size + "x" + Size + "/";
|
||||||
|
|
||||||
|
if (!Directory.Exists (AvatarPath)) {
|
||||||
|
Directory.CreateDirectory (AvatarPath);
|
||||||
|
Console.WriteLine ("[Config] Created '" + AvatarPath + "'");
|
||||||
|
|
||||||
|
}
|
||||||
|
string AvatarFile = AvatarPath + Email;
|
||||||
|
|
||||||
|
if (File.Exists (AvatarFile))
|
||||||
|
return AvatarFile;
|
||||||
|
|
||||||
|
else {
|
||||||
|
|
||||||
|
// Let's try to get the person's gravatar for next time
|
||||||
|
|
||||||
|
WebClient WebClient = new WebClient ();
|
||||||
|
Uri GravatarUri = new Uri ("http://www.gravatar.com/avatar/" +
|
||||||
|
GetMD5 (Email) + ".jpg?s=" + Size + "&d=404");
|
||||||
|
|
||||||
|
string TmpFile = "/tmp/" + Email + Size;
|
||||||
|
|
||||||
|
if (!File.Exists (TmpFile)) {
|
||||||
|
|
||||||
|
WebClient.DownloadFileAsync (GravatarUri, TmpFile);
|
||||||
|
WebClient.DownloadFileCompleted += delegate {
|
||||||
|
File.Delete (AvatarPath + Email);
|
||||||
|
FileInfo TmpFileInfo = new FileInfo (TmpFile);
|
||||||
|
if (TmpFileInfo.Length > 255)
|
||||||
|
File.Move (TmpFile, AvatarPath + Email);
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
string FallbackFileName = "/usr/share/icons/hicolor/" +
|
||||||
|
Size + "x" + Size +
|
||||||
|
"/status/avatar-default.png";
|
||||||
|
|
||||||
|
if (File.Exists (FallbackFileName))
|
||||||
|
return FallbackFileName;
|
||||||
|
else
|
||||||
|
return "/usr/share/icons/hicolor/16x16/status/avatar-default.png";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper that creates an MD5 hash
|
||||||
|
public static string GetMD5 (string s) {
|
||||||
|
|
||||||
|
MD5 md5 = new MD5CryptoServiceProvider ();
|
||||||
|
Byte[] Bytes = ASCIIEncoding.Default.GetBytes (s);
|
||||||
|
Byte[] EncodedBytes = md5.ComputeHash (Bytes);
|
||||||
|
|
||||||
|
return BitConverter.ToString(EncodedBytes).ToLower ().Replace ("-", "");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
41
SparkleShare/SparkleShare.csproj
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<PropertyGroup>
|
||||||
|
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||||
|
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||||
|
<ProductVersion>8.0.50727</ProductVersion>
|
||||||
|
<ProjectGuid>{728483AA-E34B-4441-BF2C-C8BC2901E4E0}</ProjectGuid>
|
||||||
|
<OutputType>Exe</OutputType>
|
||||||
|
<AssemblyName>SparkleShare</AssemblyName>
|
||||||
|
<SchemaVersion>2.0</SchemaVersion>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||||
|
<DebugSymbols>true</DebugSymbols>
|
||||||
|
<DebugType>full</DebugType>
|
||||||
|
<Optimize>false</Optimize>
|
||||||
|
<OutputPath>bin\Debug</OutputPath>
|
||||||
|
<DefineConstants>DEBUG</DefineConstants>
|
||||||
|
<ErrorReport>prompt</ErrorReport>
|
||||||
|
<WarningLevel>4</WarningLevel>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||||
|
<DebugType>none</DebugType>
|
||||||
|
<Optimize>false</Optimize>
|
||||||
|
<OutputPath>bin\Release</OutputPath>
|
||||||
|
<ErrorReport>prompt</ErrorReport>
|
||||||
|
<WarningLevel>4</WarningLevel>
|
||||||
|
</PropertyGroup>
|
||||||
|
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
|
||||||
|
<ItemGroup>
|
||||||
|
<Reference Include="gtk-sharp, Version=2.12.0.0, Culture=neutral, PublicKeyToken=35e10195dab3c99f" />
|
||||||
|
<Reference Include="notify-sharp, Version=0.4.0.0, Culture=neutral, PublicKeyToken=2df29c54e245917a" />
|
||||||
|
<Reference Include="System" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Include="Main.cs" />
|
||||||
|
<Compile Include="Repository.cs" />
|
||||||
|
<Compile Include="SparkleShareUI.cs" />
|
||||||
|
<Compile Include="SparkleShareStatusIcon.cs" />
|
||||||
|
<Compile Include="SparkleShareWindow.cs" />
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
BIN
SparkleShare/SparkleShare.pidb
Normal file
67
SparkleShare/SparkleShareStatusIcon.cs
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
// SparkleShare, an instant update workflow to Git.
|
||||||
|
// 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 Gtk;
|
||||||
|
using Notifications;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.IO;
|
||||||
|
using System.Net;
|
||||||
|
using System.Security.Cryptography;
|
||||||
|
using System.Text;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
using System.Timers;
|
||||||
|
|
||||||
|
namespace SparkleShare {
|
||||||
|
|
||||||
|
public class SparkleShareStatusIcon : StatusIcon {
|
||||||
|
|
||||||
|
public SparkleShareStatusIcon () : base () {
|
||||||
|
|
||||||
|
IconName = "folder-sparkleshare";
|
||||||
|
|
||||||
|
string UserHome = Environment.GetEnvironmentVariable("HOME") + "/";
|
||||||
|
string FirstRunFile = UserHome + ".config/sparkleshare/firstrun";
|
||||||
|
|
||||||
|
// Show a notification on the first run
|
||||||
|
if (File.Exists (FirstRunFile)) {
|
||||||
|
|
||||||
|
Notification Notification;
|
||||||
|
Notification = new Notification ("Welcome to SparkleShare!",
|
||||||
|
"Click here to add some folders.");
|
||||||
|
|
||||||
|
Notification.Urgency = Urgency.Normal;
|
||||||
|
Notification.Timeout = 7500;
|
||||||
|
Notification.Show ();
|
||||||
|
|
||||||
|
File.Delete (FirstRunFile);
|
||||||
|
Console.WriteLine ("[Config] Deleted '" + FirstRunFile + "'");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetIdleState () {
|
||||||
|
IconName = "folder-sparkleshare";
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetSyncingState () {
|
||||||
|
IconName = "view-refresh"; // Massively abusing this icon here :)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
117
SparkleShare/SparkleShareUI.cs
Normal file
|
@ -0,0 +1,117 @@
|
||||||
|
// SparkleShare, an instant update workflow to Git.
|
||||||
|
// 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 Gtk;
|
||||||
|
using Notifications;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.IO;
|
||||||
|
using System.Net;
|
||||||
|
using System.Security.Cryptography;
|
||||||
|
using System.Text;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
using System.Timers;
|
||||||
|
|
||||||
|
namespace SparkleShare {
|
||||||
|
|
||||||
|
// Holds the status icon, window and repository list
|
||||||
|
public class SparkleShareUI {
|
||||||
|
|
||||||
|
public SparkleShareWindow SparkleShareWindow;
|
||||||
|
public SparkleShareStatusIcon SparkleShareStatusIcon;
|
||||||
|
public Repository [] Repositories;
|
||||||
|
|
||||||
|
public SparkleShareUI (bool HideUI) {
|
||||||
|
|
||||||
|
Process Process = new Process();
|
||||||
|
Process.EnableRaisingEvents = false;
|
||||||
|
Process.StartInfo.RedirectStandardOutput = true;
|
||||||
|
Process.StartInfo.UseShellExecute = false;
|
||||||
|
|
||||||
|
// Get home folder, example: "/home/user/"
|
||||||
|
string UserHome = Environment.GetEnvironmentVariable("HOME") + "/";
|
||||||
|
|
||||||
|
// Create 'SparkleShare' folder in the user's home folder
|
||||||
|
string ReposPath = UserHome + "SparkleShare";
|
||||||
|
if (!Directory.Exists (ReposPath)) {
|
||||||
|
Directory.CreateDirectory (ReposPath);
|
||||||
|
Console.WriteLine ("[Config] Created '" + ReposPath + "'");
|
||||||
|
|
||||||
|
Process.StartInfo.FileName = "gvfs-set-attribute";
|
||||||
|
Process.StartInfo.Arguments = ReposPath + " metadata::custom-icon " +
|
||||||
|
"folder-sparkleshare";
|
||||||
|
Process.Start();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create place to store configuration user's home folder
|
||||||
|
string ConfigPath = UserHome + ".config/sparkleshare/";
|
||||||
|
if (!Directory.Exists (ConfigPath)) {
|
||||||
|
|
||||||
|
Directory.CreateDirectory (ConfigPath);
|
||||||
|
Console.WriteLine ("[Config] Created '" + ConfigPath + "'");
|
||||||
|
|
||||||
|
// Create a first run file to show the intro message
|
||||||
|
File.Create (ConfigPath + "firstrun");
|
||||||
|
Console.WriteLine ("[Config] Created '" + ConfigPath + "firstrun'");
|
||||||
|
|
||||||
|
// Create a place to store the avatars
|
||||||
|
Directory.CreateDirectory (ConfigPath + "avatars/");
|
||||||
|
Console.WriteLine ("[Config] Created '" + ConfigPath + "avatars'");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get all the repos in ~/SparkleShare
|
||||||
|
string [] Repos = Directory.GetDirectories (ReposPath);
|
||||||
|
Repositories = new Repository [Repos.Length];
|
||||||
|
|
||||||
|
int i = 0;
|
||||||
|
foreach (string Folder in Repos) {
|
||||||
|
Repositories [i] = new Repository (Folder);
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't create the window and status
|
||||||
|
// icon when --disable-gui was given
|
||||||
|
if (!HideUI) {
|
||||||
|
|
||||||
|
// Create the window
|
||||||
|
SparkleShareWindow = new SparkleShareWindow (Repositories);
|
||||||
|
SparkleShareWindow.DeleteEvent += CloseSparkleShareWindow;
|
||||||
|
|
||||||
|
// Create the status icon
|
||||||
|
SparkleShareStatusIcon = new SparkleShareStatusIcon ();
|
||||||
|
SparkleShareStatusIcon.Activate += delegate {
|
||||||
|
SparkleShareWindow.ToggleVisibility ();
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Closes the window
|
||||||
|
public void CloseSparkleShareWindow (object o, DeleteEventArgs args) {
|
||||||
|
SparkleShareWindow = new SparkleShareWindow (Repositories);
|
||||||
|
SparkleShareWindow.DeleteEvent += CloseSparkleShareWindow;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void StartMonitoring () { }
|
||||||
|
public void StopMonitoring () { }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
506
SparkleShare/SparkleShareWindow.cs
Normal file
|
@ -0,0 +1,506 @@
|
||||||
|
// SparkleShare, an instant update workflow to Git.
|
||||||
|
// 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 Gtk;
|
||||||
|
using Notifications;
|
||||||
|
using SparkleShare;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.IO;
|
||||||
|
using System.Net;
|
||||||
|
using System.Security.Cryptography;
|
||||||
|
using System.Text;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
using System.Timers;
|
||||||
|
|
||||||
|
namespace SparkleShare {
|
||||||
|
|
||||||
|
public class SparkleShareWindow : Window {
|
||||||
|
|
||||||
|
private bool Visibility;
|
||||||
|
private VBox LayoutVerticalLeft;
|
||||||
|
private VBox LayoutVerticalRight;
|
||||||
|
private HBox LayoutHorizontal;
|
||||||
|
|
||||||
|
private TreeView ReposView;
|
||||||
|
private ListStore ReposStore;
|
||||||
|
private Repository [] Repositories;
|
||||||
|
|
||||||
|
public SparkleShareWindow (Repository [] R) : base ("SparkleShare") {
|
||||||
|
|
||||||
|
Repositories = R;
|
||||||
|
|
||||||
|
Visibility = false;
|
||||||
|
SetSizeRequest (720, 540);
|
||||||
|
SetPosition (WindowPosition.Center);
|
||||||
|
BorderWidth = 6;
|
||||||
|
IconName = "folder-sparkleshare";
|
||||||
|
|
||||||
|
VBox LayoutVertical = new VBox (false, 0);
|
||||||
|
|
||||||
|
Notebook Notebook = new Notebook ();
|
||||||
|
Notebook.BorderWidth = 6;
|
||||||
|
|
||||||
|
LayoutHorizontal = new HBox (false, 0);
|
||||||
|
|
||||||
|
ReposStore = new ListStore (typeof (Gdk.Pixbuf),
|
||||||
|
typeof (string),
|
||||||
|
typeof (Repository));
|
||||||
|
|
||||||
|
LayoutVerticalLeft = CreateReposList ();
|
||||||
|
LayoutVerticalLeft.BorderWidth = 12;
|
||||||
|
|
||||||
|
LayoutVerticalRight = CreateDetailedView (Repositories [1]);
|
||||||
|
|
||||||
|
LayoutHorizontal.PackStart (LayoutVerticalLeft, false, false, 0);
|
||||||
|
LayoutHorizontal.PackStart (LayoutVerticalRight, true, true, 12);
|
||||||
|
|
||||||
|
Notebook.AppendPage (LayoutHorizontal, new Label ("Folders"));
|
||||||
|
Notebook.AppendPage (CreateEventLog (), new Label ("Events"));
|
||||||
|
|
||||||
|
LayoutVertical.PackStart (Notebook, true, true, 0);
|
||||||
|
|
||||||
|
HButtonBox DialogButtons = new HButtonBox ();
|
||||||
|
DialogButtons.BorderWidth = 6;
|
||||||
|
|
||||||
|
Button QuitServiceButton = new Button ("Quit Service");
|
||||||
|
QuitServiceButton.Clicked += Quit;
|
||||||
|
|
||||||
|
Button CloseButton = new Button (Stock.Close);
|
||||||
|
CloseButton.Clicked += delegate (object o, EventArgs args) {
|
||||||
|
Visibility = false;
|
||||||
|
HideAll ();
|
||||||
|
};
|
||||||
|
|
||||||
|
DialogButtons.Add (QuitServiceButton);
|
||||||
|
DialogButtons.Add (CloseButton);
|
||||||
|
|
||||||
|
LayoutVertical.PackStart (DialogButtons, false, false, 0);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Fetch remote changes every 20 seconds
|
||||||
|
Timer RedrawTimer = new Timer ();
|
||||||
|
RedrawTimer.Interval = 5000;
|
||||||
|
RedrawTimer.Elapsed += delegate {
|
||||||
|
|
||||||
|
TreeSelection Selection = ReposView.Selection;;
|
||||||
|
TreeIter Iter = new TreeIter ();;
|
||||||
|
Selection.GetSelected (out Iter);
|
||||||
|
Repository Repository = (Repository)ReposStore.GetValue (Iter, 2);
|
||||||
|
Console.WriteLine(Repository.Name);
|
||||||
|
|
||||||
|
LayoutHorizontal.Remove (LayoutVerticalRight);
|
||||||
|
|
||||||
|
LayoutVerticalRight = CreateDetailedView (Repository);
|
||||||
|
|
||||||
|
LayoutHorizontal.PackStart (LayoutVerticalRight, true, true, 12);
|
||||||
|
ShowAll ();
|
||||||
|
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
RedrawTimer.Start();
|
||||||
|
|
||||||
|
Add (LayoutVertical);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Creates a visual list of repositories
|
||||||
|
public VBox CreateReposList() {
|
||||||
|
|
||||||
|
string RemoteFolderIcon = "/usr/share/icons/gnome/32x32/places/folder.png";
|
||||||
|
TreeIter ReposIter;
|
||||||
|
foreach (Repository Repository in Repositories) {
|
||||||
|
ReposIter = ReposStore.Prepend ();
|
||||||
|
ReposStore.SetValue (ReposIter, 0, new Gdk.Pixbuf (RemoteFolderIcon));
|
||||||
|
ReposStore.SetValue (ReposIter, 1, Repository.Name + " \n" +
|
||||||
|
Repository.Domain + " ");
|
||||||
|
ReposStore.SetValue (ReposIter, 2, Repository);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ScrolledWindow ScrolledWindow = new ScrolledWindow ();
|
||||||
|
|
||||||
|
ReposView = new TreeView (ReposStore);
|
||||||
|
ReposView.AppendColumn ("", new CellRendererPixbuf () , "pixbuf", 0);
|
||||||
|
ReposView.AppendColumn ("", new Gtk.CellRendererText (), "text", 1);
|
||||||
|
TreeViewColumn [] ReposViewColumns = ReposView.Columns;
|
||||||
|
ReposViewColumns [0].MinWidth = 48;
|
||||||
|
|
||||||
|
ReposView.HeadersVisible = false;
|
||||||
|
|
||||||
|
ReposStore.IterNthChild (out ReposIter, 0);
|
||||||
|
ReposView.ActivateRow (ReposStore.GetPath (ReposIter),
|
||||||
|
ReposViewColumns [1]);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
ReposView.CursorChanged += delegate {
|
||||||
|
TreeSelection Selection = ReposView.Selection;;
|
||||||
|
TreeIter Iter = new TreeIter ();;
|
||||||
|
Selection.GetSelected (out Iter);
|
||||||
|
Repository Repository = (Repository)ReposStore.GetValue (Iter, 2);
|
||||||
|
Console.WriteLine(Repository.Name);
|
||||||
|
|
||||||
|
LayoutHorizontal.Remove (LayoutVerticalRight);
|
||||||
|
|
||||||
|
LayoutVerticalRight = CreateDetailedView (Repository);
|
||||||
|
|
||||||
|
LayoutHorizontal.PackStart (LayoutVerticalRight, true, true, 12);
|
||||||
|
ShowAll ();
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
HBox AddRemoveButtons = new HBox (false, 6);
|
||||||
|
Button AddButton = new Button ("Add...");
|
||||||
|
AddRemoveButtons.PackStart (AddButton, true, true, 0);
|
||||||
|
|
||||||
|
Image RemoveImage = new Image ("/usr/share/icons/gnome/16x16/actions/list-remove.png");
|
||||||
|
Button RemoveButton = new Button ();
|
||||||
|
RemoveButton.Image = RemoveImage;
|
||||||
|
AddRemoveButtons.PackStart (RemoveButton, false, false, 0);
|
||||||
|
|
||||||
|
ScrolledWindow.AddWithViewport (ReposView);
|
||||||
|
ScrolledWindow.WidthRequest = 200;
|
||||||
|
VBox VBox = new VBox (false, 6);
|
||||||
|
VBox.PackStart (ScrolledWindow, true, true, 0);
|
||||||
|
VBox.PackStart (AddRemoveButtons, false, false, 0);
|
||||||
|
|
||||||
|
return VBox;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Creates the detailed view
|
||||||
|
public VBox CreateDetailedView (Repository Repository) {
|
||||||
|
Console.WriteLine ("repo: " + Repository.Name);
|
||||||
|
|
||||||
|
// Create box layout for remote url
|
||||||
|
HBox RemoteUrlBox = new HBox (false, 0);
|
||||||
|
|
||||||
|
Label Property1 = new Label ("Remote URL:");
|
||||||
|
Property1.WidthRequest = 120;
|
||||||
|
Property1.SetAlignment (0, 0);
|
||||||
|
|
||||||
|
Label Value1 = new Label
|
||||||
|
("<b>" + Repository.RemoteOriginUrl + "</b>");
|
||||||
|
|
||||||
|
Value1.UseMarkup = true;
|
||||||
|
|
||||||
|
RemoteUrlBox.PackStart (Property1, false, false, 0);
|
||||||
|
RemoteUrlBox.PackStart (Value1, false, false, 0);
|
||||||
|
|
||||||
|
// Create box layout for repository path
|
||||||
|
HBox LocalPathBox = new HBox (false, 0);
|
||||||
|
|
||||||
|
Label Property2 = new Label ("Local path:");
|
||||||
|
Property2.WidthRequest = 120;
|
||||||
|
Property2.SetAlignment (0, 0);
|
||||||
|
|
||||||
|
Label Value2 = new Label
|
||||||
|
("<b>" + Repository.LocalPath + "</b>");
|
||||||
|
|
||||||
|
Value2.UseMarkup = true;
|
||||||
|
|
||||||
|
LocalPathBox.PackStart (Property2, false, false, 0);
|
||||||
|
LocalPathBox.PackStart (Value2, false, false, 0);
|
||||||
|
|
||||||
|
|
||||||
|
CheckButton NotificationsCheckButton =
|
||||||
|
new CheckButton ("Notify me when something changes");
|
||||||
|
NotificationsCheckButton.Active = true;
|
||||||
|
|
||||||
|
CheckButton ChangesCheckButton =
|
||||||
|
new CheckButton ("Synchronize my changes");
|
||||||
|
ChangesCheckButton.Active = true;
|
||||||
|
|
||||||
|
Table Table = new Table(7, 2, false);
|
||||||
|
Table.RowSpacing = 6;
|
||||||
|
|
||||||
|
Table.Attach(RemoteUrlBox, 0, 2, 0, 1);
|
||||||
|
Table.Attach(LocalPathBox, 0, 2, 1, 2);
|
||||||
|
Table.Attach(NotificationsCheckButton, 0, 2, 4, 5);
|
||||||
|
Table.Attach(ChangesCheckButton, 0, 2, 5, 6);
|
||||||
|
|
||||||
|
VBox VBox = new VBox (false, 0);
|
||||||
|
VBox.PackStart (Table, false, false, 12);
|
||||||
|
|
||||||
|
Label PeopleLabel =
|
||||||
|
new Label ("<span font_size='large'><b>Active users" +
|
||||||
|
"</b></span>");
|
||||||
|
|
||||||
|
PeopleLabel.UseMarkup = true;
|
||||||
|
PeopleLabel.SetAlignment (0, 0);
|
||||||
|
|
||||||
|
|
||||||
|
VBox.PackStart (PeopleLabel, false, false, 0);
|
||||||
|
VBox.PackStart
|
||||||
|
(CreatePeopleList (Repository ), true, true, 12);
|
||||||
|
|
||||||
|
return VBox;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public ScrolledWindow CreateEventLog() {
|
||||||
|
|
||||||
|
ListStore LogStore = new ListStore (typeof (Gdk.Pixbuf),
|
||||||
|
typeof (string),
|
||||||
|
typeof (string));
|
||||||
|
|
||||||
|
Process Process = new Process();
|
||||||
|
Process.EnableRaisingEvents = false;
|
||||||
|
Process.StartInfo.RedirectStandardOutput = true;
|
||||||
|
Process.StartInfo.UseShellExecute = false;
|
||||||
|
Process.StartInfo.FileName = "git";
|
||||||
|
|
||||||
|
string Output = "";
|
||||||
|
foreach (Repository Repository in Repositories) {
|
||||||
|
|
||||||
|
// We're using the snowman here to separate messages :)
|
||||||
|
Process.StartInfo.Arguments =
|
||||||
|
"log --format=\"%at☃In ‘" + Repository.Name + "’, %an %s☃%cr\" -25";
|
||||||
|
|
||||||
|
Process.StartInfo.WorkingDirectory = Repository.LocalPath;
|
||||||
|
Process.Start();
|
||||||
|
Output += "\n" + Process.StandardOutput.ReadToEnd().Trim ();
|
||||||
|
}
|
||||||
|
|
||||||
|
Output = Output.TrimStart ("\n".ToCharArray ());
|
||||||
|
string [] Lines = Regex.Split (Output, "\n");
|
||||||
|
|
||||||
|
// Sort by time and get the last 25
|
||||||
|
Array.Sort (Lines);
|
||||||
|
Array.Reverse (Lines);
|
||||||
|
string [] LastTwentyFive = new string [25];
|
||||||
|
Array.Copy (Lines, 0, LastTwentyFive, 0, 25);
|
||||||
|
|
||||||
|
TreeIter Iter;
|
||||||
|
foreach (string Line in LastTwentyFive) {
|
||||||
|
|
||||||
|
// Look for the snowman!
|
||||||
|
string [] Parts = Regex.Split (Line, "☃");
|
||||||
|
string Message = Parts [1];
|
||||||
|
string TimeAgo = Parts [2];
|
||||||
|
|
||||||
|
string IconFile =
|
||||||
|
"/usr/share/icons/hicolor/16x16/status/document-edited.png";
|
||||||
|
|
||||||
|
if (Message.IndexOf (" added ‘") > -1)
|
||||||
|
IconFile =
|
||||||
|
"/usr/share/icons/hicolor/16x16/status/document-added.png";
|
||||||
|
|
||||||
|
if (Message.IndexOf (" deleted ‘") > -1)
|
||||||
|
IconFile =
|
||||||
|
"/usr/share/icons/hicolor/16x16/status/document-removed.png";
|
||||||
|
|
||||||
|
if (Message.IndexOf (" moved ‘") > -1 ||
|
||||||
|
Message.IndexOf (" renamed ‘") > -1)
|
||||||
|
|
||||||
|
IconFile =
|
||||||
|
"/usr/share/icons/hicolor/16x16/status/document-moved.png";
|
||||||
|
|
||||||
|
Iter = LogStore.Append ();
|
||||||
|
LogStore.SetValue (Iter, 0, new Gdk.Pixbuf (IconFile));
|
||||||
|
LogStore.SetValue (Iter, 1, Message);
|
||||||
|
// TODO: right align time
|
||||||
|
LogStore.SetValue (Iter, 2, " " + TimeAgo);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
TreeView LogView = new TreeView (LogStore);
|
||||||
|
LogView.HeadersVisible = false;
|
||||||
|
|
||||||
|
CellRendererText TextCellRight = new Gtk.CellRendererText ();
|
||||||
|
TextCellRight.Alignment = Pango.Alignment.Right;
|
||||||
|
|
||||||
|
LogView.AppendColumn ("", new Gtk.CellRendererPixbuf (), "pixbuf", 0);
|
||||||
|
LogView.AppendColumn ("", new Gtk.CellRendererText (), "text", 1);
|
||||||
|
LogView.AppendColumn ("", TextCellRight, "text", 2);
|
||||||
|
|
||||||
|
TreeViewColumn [] Columns = LogView.Columns;
|
||||||
|
Columns [0].MinWidth = 32;
|
||||||
|
Columns [1].Expand = true;
|
||||||
|
Columns [1].MaxWidth = 150;
|
||||||
|
|
||||||
|
ScrolledWindow ScrolledWindow = new ScrolledWindow ();
|
||||||
|
ScrolledWindow.AddWithViewport (LogView);
|
||||||
|
ScrolledWindow.BorderWidth = 12;
|
||||||
|
|
||||||
|
return ScrolledWindow;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Creates a visual list of people working in the repo
|
||||||
|
public ScrolledWindow CreatePeopleList (Repository Repository) {
|
||||||
|
|
||||||
|
Process Process = new Process ();
|
||||||
|
Process.EnableRaisingEvents = false;
|
||||||
|
Process.StartInfo.RedirectStandardOutput = true;
|
||||||
|
Process.StartInfo.UseShellExecute = false;
|
||||||
|
|
||||||
|
// Get a log of commits, example: "Hylke Bons☃added 'file'."
|
||||||
|
Process.StartInfo.FileName = "git";
|
||||||
|
Process.StartInfo.Arguments = "log --format=\"%an☃%ae\" -50";
|
||||||
|
Process.StartInfo.WorkingDirectory = Repository.LocalPath;
|
||||||
|
Process.Start();
|
||||||
|
|
||||||
|
|
||||||
|
string Output = Process.StandardOutput.ReadToEnd().Trim ();
|
||||||
|
string [] People = new string [50];
|
||||||
|
string [] Lines = Regex.Split (Output, "\n");
|
||||||
|
|
||||||
|
ListStore PeopleStore = new ListStore (typeof (Gdk.Pixbuf),
|
||||||
|
typeof (string));
|
||||||
|
|
||||||
|
TreeIter PeopleIter;
|
||||||
|
int i = 0;
|
||||||
|
foreach (string Line in Lines) {
|
||||||
|
|
||||||
|
// Only add name if it isn't there already
|
||||||
|
if (Array.IndexOf (People, Line) == -1) {
|
||||||
|
|
||||||
|
People [i] = Line;
|
||||||
|
string [] Parts = Regex.Split (Line, "☃");
|
||||||
|
|
||||||
|
string UserName = Parts [0];
|
||||||
|
string UserEmail = Parts [1];
|
||||||
|
|
||||||
|
// Do something special if the person is you
|
||||||
|
if (UserName.Equals (Repository.UserName))
|
||||||
|
UserName += " (that’s you!)";
|
||||||
|
|
||||||
|
// Actually add to the list
|
||||||
|
PeopleIter = PeopleStore.Prepend ();
|
||||||
|
PeopleStore.SetValue (PeopleIter, 0, new Gdk.Pixbuf (GetAvatarFileName (UserEmail, 32)));
|
||||||
|
PeopleStore.SetValue (PeopleIter, 1, UserName + "\n" + UserEmail);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
i++;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
TreeView PeopleView = new TreeView (PeopleStore);
|
||||||
|
PeopleView.AppendColumn ("", new CellRendererPixbuf () , "pixbuf", 0);
|
||||||
|
PeopleView.AppendColumn ("", new Gtk.CellRendererText (), "text", 1);
|
||||||
|
TreeViewColumn [] PeopleViewColumns = PeopleView.Columns;
|
||||||
|
PeopleViewColumns [0].MinWidth = 48;
|
||||||
|
|
||||||
|
PeopleViewColumns [1].Expand = true;
|
||||||
|
|
||||||
|
PeopleView.HeadersVisible = false;
|
||||||
|
|
||||||
|
ScrolledWindow ScrolledWindow = new ScrolledWindow ();
|
||||||
|
ScrolledWindow.AddWithViewport (PeopleView);
|
||||||
|
|
||||||
|
return ScrolledWindow;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdatePeopleList () {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ToggleVisibility() {
|
||||||
|
Present ();
|
||||||
|
if (Visibility) {
|
||||||
|
if (HasFocus)
|
||||||
|
HideAll ();
|
||||||
|
} else {
|
||||||
|
ShowAll ();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Quit (object o, EventArgs args) {
|
||||||
|
File.Delete ("/tmp/sparkleshare/sparkleshare.pid");
|
||||||
|
Application.Quit ();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public static string GetAvatarFileName (string Email, int Size) {
|
||||||
|
|
||||||
|
string AvatarPath = Environment.GetEnvironmentVariable("HOME") +
|
||||||
|
"/.config/sparkleshare/avatars/" +
|
||||||
|
Size + "x" + Size + "/";
|
||||||
|
|
||||||
|
if (!Directory.Exists (AvatarPath)) {
|
||||||
|
Directory.CreateDirectory (AvatarPath);
|
||||||
|
Console.WriteLine ("[Config] Created '" + AvatarPath + "'");
|
||||||
|
|
||||||
|
}
|
||||||
|
string AvatarFile = AvatarPath + Email;
|
||||||
|
|
||||||
|
if (File.Exists (AvatarFile))
|
||||||
|
return AvatarFile;
|
||||||
|
|
||||||
|
else {
|
||||||
|
|
||||||
|
// Let's try to get the person's gravatar for next time
|
||||||
|
|
||||||
|
WebClient WebClient = new WebClient ();
|
||||||
|
Uri GravatarUri = new Uri ("http://www.gravatar.com/avatar/" +
|
||||||
|
GetMD5 (Email) + ".jpg?s=" + Size + "&d=404");
|
||||||
|
|
||||||
|
string TmpFile = "/tmp/" + Email + Size;
|
||||||
|
|
||||||
|
if (!File.Exists (TmpFile)) {
|
||||||
|
|
||||||
|
WebClient.DownloadFileAsync (GravatarUri, TmpFile);
|
||||||
|
WebClient.DownloadFileCompleted += delegate {
|
||||||
|
File.Delete (AvatarPath + Email);
|
||||||
|
FileInfo TmpFileInfo = new FileInfo (TmpFile);
|
||||||
|
if (TmpFileInfo.Length > 255)
|
||||||
|
File.Move (TmpFile, AvatarPath + Email);
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
string FallbackFileName = "/usr/share/icons/hicolor/" +
|
||||||
|
Size + "x" + Size +
|
||||||
|
"/status/avatar-default.png";
|
||||||
|
|
||||||
|
if (File.Exists (FallbackFileName))
|
||||||
|
return FallbackFileName;
|
||||||
|
else
|
||||||
|
return "/usr/share/icons/hicolor/16x16/status/avatar-default.png";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper that creates an MD5 hash
|
||||||
|
public static string GetMD5 (string s) {
|
||||||
|
|
||||||
|
MD5 md5 = new MD5CryptoServiceProvider ();
|
||||||
|
Byte[] Bytes = ASCIIEncoding.Default.GetBytes (s);
|
||||||
|
Byte[] EncodedBytes = md5.ComputeHash (Bytes);
|
||||||
|
|
||||||
|
return BitConverter.ToString(EncodedBytes).ToLower ().Replace ("-", "");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
Before Width: | Height: | Size: 766 B After Width: | Height: | Size: 766 B |
Before Width: | Height: | Size: 1,004 B After Width: | Height: | Size: 1,004 B |
Before Width: | Height: | Size: 1 KiB After Width: | Height: | Size: 1 KiB |
Before Width: | Height: | Size: 49 KiB After Width: | Height: | Size: 49 KiB |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 2.3 KiB |
|
@ -1,5 +0,0 @@
|
||||||
[Desktop Entry]
|
|
||||||
Name=SparklePony
|
|
||||||
Exec=sparklepony start
|
|
||||||
Icon=folder-sparklepony
|
|
||||||
Terminal=false
|
|
5
data/sparkleshare.desktop.in
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
[Desktop Entry]
|
||||||
|
Name=SparkleShare
|
||||||
|
Exec=sparkleshare start
|
||||||
|
Icon=folder-sparkleshare
|
||||||
|
Terminal=false
|
|
@ -16,7 +16,7 @@
|
||||||
inkscape:export-ydpi="90.000000"
|
inkscape:export-ydpi="90.000000"
|
||||||
inkscape:output_extension="org.inkscape.output.svg.inkscape"
|
inkscape:output_extension="org.inkscape.output.svg.inkscape"
|
||||||
inkscape:version="0.46"
|
inkscape:version="0.46"
|
||||||
sodipodi:docname="sparklepony.svg"
|
sodipodi:docname="sparkleshare.svg"
|
||||||
sodipodi:version="0.32"
|
sodipodi:version="0.32"
|
||||||
style="display:inline;enable-background:new"
|
style="display:inline;enable-background:new"
|
||||||
version="1.0"
|
version="1.0"
|
||||||
|
@ -163,7 +163,7 @@
|
||||||
<dc:title>Lapo Calamandrei</dc:title>
|
<dc:title>Lapo Calamandrei</dc:title>
|
||||||
</cc:Agent>
|
</cc:Agent>
|
||||||
</dc:creator>
|
</dc:creator>
|
||||||
<dc:title>SparklePony</dc:title>
|
<dc:title>sparkleshare</dc:title>
|
||||||
<dc:subject>
|
<dc:subject>
|
||||||
<rdf:Bag>
|
<rdf:Bag>
|
||||||
<rdf:li>folder</rdf:li>
|
<rdf:li>folder</rdf:li>
|
||||||
|
@ -1832,7 +1832,7 @@
|
||||||
<g
|
<g
|
||||||
inkscape:groupmode="layer"
|
inkscape:groupmode="layer"
|
||||||
id="layer17"
|
id="layer17"
|
||||||
inkscape:label="artwork:sparklepony"
|
inkscape:label="artwork:sparkleshare"
|
||||||
style="display:inline"
|
style="display:inline"
|
||||||
transform="translate(0,300)">
|
transform="translate(0,300)">
|
||||||
<g
|
<g
|
||||||
|
@ -1911,7 +1911,7 @@
|
||||||
y="-228.48639"
|
y="-228.48639"
|
||||||
x="166.9707"
|
x="166.9707"
|
||||||
sodipodi:role="line"
|
sodipodi:role="line"
|
||||||
id="tspan4716">sparklepony</tspan></text>
|
id="tspan4716">sparkleshare</tspan></text>
|
||||||
</g>
|
</g>
|
||||||
<g
|
<g
|
||||||
inkscape:groupmode="layer"
|
inkscape:groupmode="layer"
|
Before Width: | Height: | Size: 809 KiB After Width: | Height: | Size: 809 KiB |