From e01cf0c2da276d9ea1741751504be5618b301e6a Mon Sep 17 00:00:00 2001 From: Hylke Bons Date: Mon, 3 May 2010 01:04:39 +0100 Subject: [PATCH] Put everything in a monodevelop project. --- .gitignore | 1 + Makefile | 31 +- README | 24 +- src/sparklepony => SparkleShare | 0 SparkleShare.sln | 22 + SparkleShare.userprefs | 25 + SparkleShare.usertasks | 2 + ...parklepony.png => folder-sparkleshare.png} | Bin ...parklepony.png => folder-sparkleshare.png} | Bin ...parklepony.png => folder-sparkleshare.png} | Bin ...parklepony.png => folder-sparkleshare.png} | Bin ...parklepony.png => folder-sparkleshare.png} | Bin ...parklepony.png => folder-sparkleshare.png} | Bin data/sparklepony.desktop.in | 5 - data/sparkleshare.desktop.in | 5 + data/{sparklepony.svg => sparkleshare.svg} | 8 +- src/SparklePony.cs | 1043 ----------------- 17 files changed, 87 insertions(+), 1079 deletions(-) rename src/sparklepony => SparkleShare (100%) create mode 100644 SparkleShare.sln create mode 100644 SparkleShare.userprefs create mode 100644 SparkleShare.usertasks rename data/icons/hicolor/16x16/places/{folder-sparklepony.png => folder-sparkleshare.png} (100%) rename data/icons/hicolor/22x22/places/{folder-sparklepony.png => folder-sparkleshare.png} (100%) rename data/icons/hicolor/24x24/places/{folder-sparklepony.png => folder-sparkleshare.png} (100%) rename data/icons/hicolor/256x256/places/{folder-sparklepony.png => folder-sparkleshare.png} (100%) rename data/icons/hicolor/32x32/places/{folder-sparklepony.png => folder-sparkleshare.png} (100%) rename data/icons/hicolor/48x48/places/{folder-sparklepony.png => folder-sparkleshare.png} (100%) delete mode 100644 data/sparklepony.desktop.in create mode 100644 data/sparkleshare.desktop.in rename data/{sparklepony.svg => sparkleshare.svg} (99%) delete mode 100644 src/SparklePony.cs diff --git a/.gitignore b/.gitignore index 9bb48774..691d2dfa 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ *~ *.exe +*.exe.mdb diff --git a/Makefile b/Makefile index 188d0d19..b9c7c0fa 100644 --- a/Makefile +++ b/Makefile @@ -1,26 +1,27 @@ -SparklePony.exe : src/SparklePony.cs - gmcs -pkg:gtk-sharp-2.0 -pkg:notify-sharp -pkg:dbus-sharp src/SparklePony.cs +SparkleShare.exe : SparkleShare.sln + mdtool build --f --buildfile:SparkleShare.sln install: - mkdir -p /usr/local/share/sparklepony - cp src/SparklePony.exe /usr/local/share/sparklepony/ - chmod 755 /usr/local/share/sparklepony/SparklePony.exe - cp src/sparklepony /usr/local/bin/ - chmod 755 /usr/local/bin/sparklepony + mkdir -p /usr/local/share/sparkleshare + cp SparkleShare/bin/Debug/SparkleShare.exe /usr/local/share/sparkleshare/ + cp SparkleShare/bin/Debug/SparkleShare.exe.mdb /usr/local/share/sparkleshare/ + chmod 755 /usr/local/share/sparkleshare/SparkleShare.exe + cp sparkleshare /usr/local/bin/ + chmod 755 /usr/local/bin/sparkleshare cp data/icons /usr/share/ -R mkdir -p ~/.config/autostart - cp data/sparklepony.desktop.in ~/.config/autostart/sparklepony.desktop - chmod 775 ~/.config/autostart/sparklepony.desktop + cp data/sparkleshare.desktop.in ~/.config/autostart/sparkleshare.desktop + chmod 775 ~/.config/autostart/sparkleshare.desktop gtk-update-icon-cache /usr/share/icons/hicolor -f uninstall: - rm /usr/local/bin/sparklepony - rm /usr/local/share/sparklepony/SparklePony.exe - rmdir /usr/local/share/sparklepony - rm /usr/share/icons/hicolor/*x*/places/folder-sparklepony.png + rm /usr/local/bin/sparkleshare + rm /usr/local/share/sparkleshare/SparkleShare.exe + rmdir /usr/local/share/sparkleshare + 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/avatar-default.png - rm ~/.config/autostart/sparklepony.desktop + rm ~/.config/autostart/sparkleshare.desktop clean: - rm src/SparklePony.exe + rm src/sparkleshare.exe diff --git a/README b/README index 2fcee6f3..24e6ce5a 100644 --- a/README +++ b/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. -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 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. -SparklePony is not designed to be: +SparkleShare is not designed to be: - a graphical frontend for git - a backup tool In contrast to the projects name, we will very likely 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. -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 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 - git >= 1.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 \ notify-sharp-devel ndesk-dbus-devel -You can build and install SparklePony like this: +You can build and install SparkleShare like this: $ make $ su # make install Run the service: - $ sparklepony start + $ sparkleshare start You can stop the service via the graphical interface or by typing: - $ sparklepony stop + $ sparkleshare stop For help: - $ sparklepony --help + $ sparkleshare --help The official website is: -http://www.github.com/hbons/SparklePony +http://www.github.com/hbons/SparkleShare Have fun! :) diff --git a/src/sparklepony b/SparkleShare similarity index 100% rename from src/sparklepony rename to SparkleShare diff --git a/SparkleShare.sln b/SparkleShare.sln new file mode 100644 index 00000000..aa92b441 --- /dev/null +++ b/SparkleShare.sln @@ -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 diff --git a/SparkleShare.userprefs b/SparkleShare.userprefs new file mode 100644 index 00000000..51146c5c --- /dev/null +++ b/SparkleShare.userprefs @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/SparkleShare.usertasks b/SparkleShare.usertasks new file mode 100644 index 00000000..d887d0ef --- /dev/null +++ b/SparkleShare.usertasks @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/data/icons/hicolor/16x16/places/folder-sparklepony.png b/data/icons/hicolor/16x16/places/folder-sparkleshare.png similarity index 100% rename from data/icons/hicolor/16x16/places/folder-sparklepony.png rename to data/icons/hicolor/16x16/places/folder-sparkleshare.png diff --git a/data/icons/hicolor/22x22/places/folder-sparklepony.png b/data/icons/hicolor/22x22/places/folder-sparkleshare.png similarity index 100% rename from data/icons/hicolor/22x22/places/folder-sparklepony.png rename to data/icons/hicolor/22x22/places/folder-sparkleshare.png diff --git a/data/icons/hicolor/24x24/places/folder-sparklepony.png b/data/icons/hicolor/24x24/places/folder-sparkleshare.png similarity index 100% rename from data/icons/hicolor/24x24/places/folder-sparklepony.png rename to data/icons/hicolor/24x24/places/folder-sparkleshare.png diff --git a/data/icons/hicolor/256x256/places/folder-sparklepony.png b/data/icons/hicolor/256x256/places/folder-sparkleshare.png similarity index 100% rename from data/icons/hicolor/256x256/places/folder-sparklepony.png rename to data/icons/hicolor/256x256/places/folder-sparkleshare.png diff --git a/data/icons/hicolor/32x32/places/folder-sparklepony.png b/data/icons/hicolor/32x32/places/folder-sparkleshare.png similarity index 100% rename from data/icons/hicolor/32x32/places/folder-sparklepony.png rename to data/icons/hicolor/32x32/places/folder-sparkleshare.png diff --git a/data/icons/hicolor/48x48/places/folder-sparklepony.png b/data/icons/hicolor/48x48/places/folder-sparkleshare.png similarity index 100% rename from data/icons/hicolor/48x48/places/folder-sparklepony.png rename to data/icons/hicolor/48x48/places/folder-sparkleshare.png diff --git a/data/sparklepony.desktop.in b/data/sparklepony.desktop.in deleted file mode 100644 index 324b9941..00000000 --- a/data/sparklepony.desktop.in +++ /dev/null @@ -1,5 +0,0 @@ -[Desktop Entry] -Name=SparklePony -Exec=sparklepony start -Icon=folder-sparklepony -Terminal=false diff --git a/data/sparkleshare.desktop.in b/data/sparkleshare.desktop.in new file mode 100644 index 00000000..2df9446a --- /dev/null +++ b/data/sparkleshare.desktop.in @@ -0,0 +1,5 @@ +[Desktop Entry] +Name=SparkleShare +Exec=sparkleshare start +Icon=folder-sparkleshare +Terminal=false diff --git a/data/sparklepony.svg b/data/sparkleshare.svg similarity index 99% rename from data/sparklepony.svg rename to data/sparkleshare.svg index 98403f0e..23dad42b 100644 --- a/data/sparklepony.svg +++ b/data/sparkleshare.svg @@ -16,7 +16,7 @@ inkscape:export-ydpi="90.000000" inkscape:output_extension="org.inkscape.output.svg.inkscape" inkscape:version="0.46" - sodipodi:docname="sparklepony.svg" + sodipodi:docname="sparkleshare.svg" sodipodi:version="0.32" style="display:inline;enable-background:new" version="1.0" @@ -163,7 +163,7 @@ Lapo Calamandrei - SparklePony + sparkleshare folder @@ -1832,7 +1832,7 @@ sparklepony + id="tspan4716">sparkleshare -// -// 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 . - -using Gtk; -using Notifications; -using SparklePonyHelpers; -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; - -// This is SparklePony! -public class SparklePony { - - public static SparklePonyUI SparklePonyUI; - - 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 SparklePony 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 (); - - SparklePonyUI = new SparklePonyUI (HideUI); - SparklePonyUI.StartMonitoring (); - - Gtk.Application.Run (); - - } - - public static void ShowHelp () { - Console.WriteLine ("SparklePony 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 ("SparklePony syncs the ~/SparklePony folder with remote repositories."); - Console.WriteLine (""); - Console.WriteLine ("Usage: sparklepony [start|stop|restart] [OPTION]..."); - Console.WriteLine ("Sync SparklePony 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); - } - -} - -// Holds the status icon, window and repository list -public class SparklePonyUI { - - public SparklePonyWindow SparklePonyWindow; - public SparklePonyStatusIcon SparklePonyStatusIcon; - public Repository [] Repositories; - - public SparklePonyUI (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 'SparklePony' folder in the user's home folder - string ReposPath = UserHome + "SparklePony"; - 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-sparklepony"; - Process.Start(); - - } - - // Create place to store configuration user's home folder - string ConfigPath = UserHome + ".config/sparklepony/"; - 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 ~/SparklePony - 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 - SparklePonyWindow = new SparklePonyWindow (Repositories); - SparklePonyWindow.DeleteEvent += CloseSparklePonyWindow; - - // Create the status icon - SparklePonyStatusIcon = new SparklePonyStatusIcon (); - SparklePonyStatusIcon.Activate += delegate { - SparklePonyWindow.ToggleVisibility (); - }; - - } - - } - - // Closes the window - public void CloseSparklePonyWindow (object o, DeleteEventArgs args) { - SparklePonyWindow = new SparklePonyWindow (Repositories); - SparklePonyWindow.DeleteEvent += CloseSparklePonyWindow; - } - - public void StartMonitoring () { } - public void StopMonitoring () { } - -} - -public class SparklePonyStatusIcon : StatusIcon { - - public SparklePonyStatusIcon () : base () { - - IconName = "folder-sparklepony"; - - string UserHome = Environment.GetEnvironmentVariable("HOME") + "/"; - string FirstRunFile = UserHome + ".config/sparklepony/firstrun"; - - // Show a notification on the first run - if (File.Exists (FirstRunFile)) { - - Notification Notification; - Notification = new Notification ("Welcome to SparklePony!", - "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-sparklepony"; - } - - public void SetSyncingState () { - IconName = "view-refresh"; // Massively abusing this icon here :) - } - -} - - -// 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/SparklePony/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 SparklePony 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, - SparklePonyHelpers.SparklePonyHelpers.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, - SparklePonyHelpers.SparklePonyHelpers.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 class SparklePonyWindow : Window { - - private bool Visibility; - private VBox LayoutVerticalLeft; - private VBox LayoutVerticalRight; - private HBox LayoutHorizontal; - - private TreeView ReposView; - private ListStore ReposStore; - private Repository [] Repositories; - - public SparklePonyWindow (Repository [] R) : base ("SparkleShare") { - - Repositories = R; - - Visibility = false; - SetSizeRequest (720, 540); - SetPosition (WindowPosition.Center); - BorderWidth = 6; - IconName = "folder-sparklepony"; - - 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); - - 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 - ("" + Repository.RemoteOriginUrl + ""); - - 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 - ("" + Repository.LocalPath + ""); - - 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 ("Active users" + - ""); - - 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 (SparklePonyHelpers.SparklePonyHelpers.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/sparklepony/sparklepony.pid"); - Application.Quit (); - } - -} - -namespace SparklePonyHelpers { - - // Helper that returns a user's avatar, - // either from a file or the web - class SparklePonyHelpers { - - public static string GetAvatarFileName (string Email, int Size) { - - string AvatarPath = Environment.GetEnvironmentVariable("HOME") + - "/.config/sparklepony/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 ("-", ""); - - } - - } - -}