Merge branch 'master' into gettext-cs

Conflicts:
	SparkleLib/Git/SparkleRepoGit.cs
This commit is contained in:
serras 2012-02-05 18:54:32 +01:00
commit d096c39e5d
31 changed files with 559 additions and 342 deletions

11
NEWS
View file

@ -1,3 +1,14 @@
0.8.1 for Linux and Mac (Sun Jan 29):
Hylke:
- Disable the Quit menu item when a sync is going on
- Show remote url when hovering project names in the log
- Fix the event log on Linux
- Allow server ports to be specified once again
- Open Folder button after setup now takes you to SparkleShare/subfolder
- Close event log on Cmd+W on Mac
0.8.0 for Linux and Mac (Sun Jan 22): 0.8.0 for Linux and Mac (Sun Jan 22):
Hylke: Hylke:

View file

@ -51,7 +51,7 @@ namespace SparkleLib {
if (!uri.Scheme.Equals ("ssh") && if (!uri.Scheme.Equals ("ssh") &&
!uri.Scheme.Equals ("git")) { !uri.Scheme.Equals ("git")) {
uri = new Uri ("ssh://" + server); uri = new Uri ("ssh://" + uri);
} }
@ -72,8 +72,10 @@ namespace SparkleLib {
uri = new Uri ("ssh://git@gnome.org/git" + uri.AbsolutePath); uri = new Uri ("ssh://git@gnome.org/git" + uri.AbsolutePath);
} else { } else {
if (string.IsNullOrEmpty (uri.UserInfo)) if (string.IsNullOrEmpty (uri.UserInfo)) {
uri = new Uri (uri.Scheme + "://git@" + uri.Host + uri.AbsolutePath); uri = new Uri (uri.Scheme + "://git@" + uri.Host + ":" + uri.Port + uri.AbsolutePath);
uri = new Uri (uri.ToString ().Replace (":-1", ""));
}
} }
@ -221,75 +223,11 @@ namespace SparkleLib {
SparkleHelpers.CombineMore (this.target_folder, ".git", "info")); SparkleHelpers.CombineMore (this.target_folder, ".git", "info"));
// File that lists the files we want git to ignore // File that lists the files we want git to ignore
string exlude_rules_file_path = Path.Combine (info.FullName, "exclude"); string exclude_rules_file_path = Path.Combine (info.FullName, "exclude");
TextWriter writer = new StreamWriter (exlude_rules_file_path); TextWriter writer = new StreamWriter (exclude_rules_file_path);
// gedit and emacs foreach (string exclude_rule in ExcludeRules)
writer.WriteLine ("*~"); writer.WriteLine (exclude_rule);
// Firefox and Chromium temporary download files
writer.WriteLine ("*.part");
writer.WriteLine ("*.crdownload");
// vi(m)
writer.WriteLine (".*.sw[a-z]");
writer.WriteLine ("*.un~");
writer.WriteLine ("*.swp");
writer.WriteLine ("*.swo");
// KDE
writer.WriteLine (".directory");
// Mac OS X
writer.WriteLine (".DS_Store");
writer.WriteLine ("Icon?");
writer.WriteLine ("._*");
writer.WriteLine (".Spotlight-V100");
writer.WriteLine (".Trashes");
// Omnigraffle
writer.WriteLine ("*(Autosaved).graffle");
// Windows
writer.WriteLine ("Thumbs.db");
writer.WriteLine ("Desktop.ini");
// MS Office
writer.WriteLine ("~*.tmp");
writer.WriteLine ("~*.TMP");
writer.WriteLine ("*~*.tmp");
writer.WriteLine ("*~*.TMP");
writer.WriteLine ("~*.ppt");
writer.WriteLine ("~*.PPT");
writer.WriteLine ("~*.pptx");
writer.WriteLine ("~*.PPTX");
writer.WriteLine ("~*.xls");
writer.WriteLine ("~*.XLS");
writer.WriteLine ("~*.xlsx");
writer.WriteLine ("~*.XLSX");
writer.WriteLine ("~*.doc");
writer.WriteLine ("~*.DOC");
writer.WriteLine ("~*.docx");
writer.WriteLine ("~*.DOCX");
// CVS
writer.WriteLine ("*/CVS/*");
writer.WriteLine (".cvsignore");
writer.WriteLine ("*/.cvsignore");
// Subversion
writer.WriteLine ("/.svn/*");
writer.WriteLine ("*/.svn/*");
// Mercurial
writer.WriteLine ("/.hg/*");
writer.WriteLine ("*/.hg/*");
writer.WriteLine ("*/.hgignore");
// Bazaar
writer.WriteLine ("/.bzr/*");
writer.WriteLine ("*/.bzr/*");
writer.WriteLine ("*/.bzrignore");
writer.Close (); writer.Close ();

View file

@ -32,6 +32,7 @@ namespace SparkleLib {
StartInfo.RedirectStandardOutput = true; StartInfo.RedirectStandardOutput = true;
StartInfo.UseShellExecute = false; StartInfo.UseShellExecute = false;
StartInfo.WorkingDirectory = path; StartInfo.WorkingDirectory = path;
StartInfo.CreateNoWindow = true;
if (!string.IsNullOrEmpty (ExecPath)) if (!string.IsNullOrEmpty (ExecPath))
StartInfo.Arguments = "--exec-path=\"" + ExecPath + "\" " + args; StartInfo.Arguments = "--exec-path=\"" + ExecPath + "\" " + args;

View file

@ -26,8 +26,9 @@ namespace SparkleLib {
public class SparkleRepoGit : SparkleRepoBase { public class SparkleRepoGit : SparkleRepoBase {
public SparkleRepoGit (string path, SparkleBackend backend) : public SparkleRepoGit (string path) : base (path)
base (path, backend) { } {
}
private string identifier = null; private string identifier = null;
@ -71,7 +72,7 @@ namespace SparkleLib {
public override double Size { public override double Size {
get { get {
string file_path = SparkleHelpers.CombineMore (LocalPath, ".git", "repo_size"); string file_path = new string [] {LocalPath, ".git", "repo_size"}.Combine ();
try { try {
return double.Parse (File.ReadAllText (file_path)); return double.Parse (File.ReadAllText (file_path));
@ -85,7 +86,7 @@ namespace SparkleLib {
public override double HistorySize { public override double HistorySize {
get { get {
string file_path = SparkleHelpers.CombineMore(LocalPath, ".git", "repo_history_size"); string file_path = new string [] {LocalPath, ".git", "repo_history_size"}.Combine ();
try { try {
return double.Parse (File.ReadAllText (file_path)); return double.Parse (File.ReadAllText (file_path));
@ -105,8 +106,8 @@ namespace SparkleLib {
double history_size = CalculateSize ( double history_size = CalculateSize (
new DirectoryInfo (Path.Combine (LocalPath, ".git"))); new DirectoryInfo (Path.Combine (LocalPath, ".git")));
string size_file_path = SparkleHelpers.CombineMore(LocalPath, ".git", "repo_size"); string size_file_path = new string [] {LocalPath, ".git", "repo_size"}.Combine ();
string history_size_file_path = SparkleHelpers.CombineMore(LocalPath, ".git", "repo_history_size"); string history_size_file_path = new string [] {LocalPath, ".git", "repo_history_size"}.Combine ();
File.WriteAllText (size_file_path, size.ToString ()); File.WriteAllText (size_file_path, size.ToString ());
File.WriteAllText (history_size_file_path, history_size.ToString ()); File.WriteAllText (history_size_file_path, history_size.ToString ());
@ -169,7 +170,7 @@ namespace SparkleLib {
public override bool CheckForRemoteChanges () public override bool CheckForRemoteChanges ()
{ {
SparkleHelpers.DebugInfo ("Git", "[" + Name + "] Checking for remote changes..."); SparkleHelpers.DebugInfo ("Git", "[" + Name + "] Checking for remote changes...");
SparkleGit git = new SparkleGit (LocalPath, "ls-remote origin master"); SparkleGit git = new SparkleGit (LocalPath, "ls-remote " + Url + " master");
git.Start (); git.Start ();
git.WaitForExit (); git.WaitForExit ();
@ -201,7 +202,7 @@ namespace SparkleLib {
SparkleGit git = new SparkleGit (LocalPath, SparkleGit git = new SparkleGit (LocalPath,
"push --progress " + // Redirects progress stats to standarderror "push --progress " + // Redirects progress stats to standarderror
"origin master"); Url + " master");
git.StartInfo.RedirectStandardError = true; git.StartInfo.RedirectStandardError = true;
git.Start (); git.Start ();
@ -258,7 +259,7 @@ namespace SparkleLib {
public override bool SyncDown () public override bool SyncDown ()
{ {
SparkleGit git = new SparkleGit (LocalPath, "fetch --progress"); SparkleGit git = new SparkleGit (LocalPath, "fetch --progress " + Url);
git.StartInfo.RedirectStandardError = true; git.StartInfo.RedirectStandardError = true;
git.Start (); git.Start ();
@ -628,6 +629,7 @@ namespace SparkleLib {
change_set.User.Name = match.Groups [2].Value; change_set.User.Name = match.Groups [2].Value;
change_set.User.Email = match.Groups [3].Value; change_set.User.Email = match.Groups [3].Value;
change_set.IsMagical = is_merge_commit; change_set.IsMagical = is_merge_commit;
change_set.Url = Url;
change_set.Timestamp = new DateTime (int.Parse (match.Groups [4].Value), change_set.Timestamp = new DateTime (int.Parse (match.Groups [4].Value),
int.Parse (match.Groups [5].Value), int.Parse (match.Groups [6].Value), int.Parse (match.Groups [5].Value), int.Parse (match.Groups [6].Value),

View file

@ -9,6 +9,7 @@ SOURCES = \
SparkleBackend.cs \ SparkleBackend.cs \
SparkleChangeSet.cs \ SparkleChangeSet.cs \
SparkleConfig.cs \ SparkleConfig.cs \
SparkleExtensions.cs \
SparkleFetcherBase.cs \ SparkleFetcherBase.cs \
SparkleHelpers.cs \ SparkleHelpers.cs \
SparkleListenerBase.cs \ SparkleListenerBase.cs \

View file

@ -30,6 +30,7 @@ namespace SparkleLib {
public DateTime Timestamp; public DateTime Timestamp;
public DateTime FirstTimestamp; public DateTime FirstTimestamp;
public bool IsMagical = false; public bool IsMagical = false;
public Uri Url;
public List<string> Added = new List<string> (); public List<string> Added = new List<string> ();
public List<string> Deleted = new List<string> (); public List<string> Deleted = new List<string> ();

35
SparkleLib/SparkleExtensions.cs Executable file
View file

@ -0,0 +1,35 @@
// SparkleShare, a collaboration and sharing tool.
// Copyright (C) 2010 Hylke Bons (hylkebons@gmail.com)
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see (http://www.gnu.org/licenses/).
using System;
using System.IO;
namespace SparkleLib {
public static class Extensions {
public static string Combine (this String [] parts)
{
string new_path = "";
foreach (string part in parts)
new_path = Path.Combine (new_path, part);
return new_path;
}
}
}

View file

@ -16,8 +16,8 @@
using System; using System;
using System.IO;
using System.Diagnostics; using System.Diagnostics;
using System.IO;
using System.Security.AccessControl; using System.Security.AccessControl;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using System.Threading; using System.Threading;
@ -37,6 +37,8 @@ namespace SparkleLib {
public event FailedEventHandler Failed; public event FailedEventHandler Failed;
public event ProgressChangedEventHandler ProgressChanged; public event ProgressChangedEventHandler ProgressChanged;
public string [] ExcludeRules;
protected string target_folder; protected string target_folder;
protected string remote_url; protected string remote_url;
@ -47,12 +49,82 @@ namespace SparkleLib {
{ {
this.target_folder = target_folder; this.target_folder = target_folder;
this.remote_url = server + "/" + remote_folder; this.remote_url = server + "/" + remote_folder;
ExcludeRules = new string [] {
// gedit and emacs
"*~",
// Firefox and Chromium temporary download files
"*.part",
"*.crdownload",
// vi(m)
".*.sw[a-z]",
"*.un~",
"*.swp",
"*.swo",
// KDE
".directory",
// Mac OS X
".DS_Store",
"Icon?",
"._*",
".Spotlight-V100",
".Trashes",
// Omnigraffle
"*(Autosaved).graffle",
// Windows
"Thumbs.db",
"Desktop.ini",
// MS Office
"~*.tmp",
"~*.TMP",
"*~*.tmp",
"*~*.TMP",
"~*.ppt",
"~*.PPT",
"~*.pptx",
"~*.PPTX",
"~*.xls",
"~*.XLS",
"~*.xlsx",
"~*.XLSX",
"~*.doc",
"~*.DOC",
"~*.docx",
"~*.DOCX",
// CVS
"*/CVS/*",
".cvsignore",
"*/.cvsignore",
// Subversion
"/.svn/*",
"*/.svn/*",
// Mercurial
"/.hg/*",
"*/.hg/*",
"*/.hgignore",
// Bazaar
"/.bzr/*",
"*/.bzr/*",
"*/.bzrignore"
};
} }
public abstract bool Fetch (); public abstract bool Fetch ();
public abstract string [] Warnings { get; } public abstract string [] Warnings { get; }
// Clones the remote repository // Clones the remote repository
public void Start () public void Start ()
{ {

View file

@ -49,6 +49,7 @@
<Compile Include="SparkleBackend.cs" /> <Compile Include="SparkleBackend.cs" />
<Compile Include="SparkleConfig.cs" /> <Compile Include="SparkleConfig.cs" />
<Compile Include="SparkleWatcher.cs" /> <Compile Include="SparkleWatcher.cs" />
<Compile Include="SparkleExtensions.cs" />
</ItemGroup> </ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<ProjectExtensions> <ProjectExtensions>

View file

@ -64,7 +64,7 @@ namespace SparkleLib {
SparkleHelpers.DebugInfo ("ListenerFactory", SparkleHelpers.DebugInfo ("ListenerFactory",
"Refered to existing listener for " + announce_uri); "Refered to existing listener for " + announce_uri);
listener.AlsoListenTo (folder_identifier); listener.AlsoListenToBase (folder_identifier);
return (SparkleListenerBase) listener; return (SparkleListenerBase) listener;
} }
} }
@ -90,40 +90,52 @@ namespace SparkleLib {
// listens for change notifications // listens for change notifications
public abstract class SparkleListenerBase { public abstract class SparkleListenerBase {
// We've connected to the server
public event ConnectedEventHandler Connected; public event ConnectedEventHandler Connected;
public delegate void ConnectedEventHandler (); public delegate void ConnectedEventHandler ();
// We've disconnected from the server
public event DisconnectedEventHandler Disconnected; public event DisconnectedEventHandler Disconnected;
public delegate void DisconnectedEventHandler (); public delegate void DisconnectedEventHandler ();
// We've been notified about a remote public event ReceivedEventHandler Received;
// change by the channel public delegate void ReceivedEventHandler (SparkleAnnouncement announcement);
public event AnnouncementEventHandler Announcement;
public delegate void AnnouncementEventHandler (SparkleAnnouncement announcement); public readonly Uri Server;
public abstract void Connect (); public abstract void Connect ();
public abstract void Announce (SparkleAnnouncement announcent);
public abstract void AlsoListenTo (string folder_identifier);
public abstract bool IsConnected { get; } public abstract bool IsConnected { get; }
public abstract bool IsConnecting { get; }
protected abstract void Announce (SparkleAnnouncement announcent);
protected abstract void AlsoListenTo (string folder_identifier);
protected List<string> channels = new List<string> (); protected List<string> channels = new List<string> ();
protected Dictionary<string,List<SparkleAnnouncement>> recent_announcements = new Dictionary<string, List<SparkleAnnouncement>> ();
protected int max_recent_announcements = 10;
protected Dictionary<string, SparkleAnnouncement> queue_up = new Dictionary<string, SparkleAnnouncement> (); private int max_recent_announcements = 10;
protected Dictionary<string,SparkleAnnouncement> queue_down = new Dictionary<string, SparkleAnnouncement> ();
protected bool is_connecting; private Dictionary<string, List<SparkleAnnouncement>> recent_announcements =
protected Uri server; new Dictionary<string, List<SparkleAnnouncement>> ();
protected Timer reconnect_timer = new Timer { Interval = 60 * 1000, Enabled = true };
private Dictionary<string, SparkleAnnouncement> queue_up =
new Dictionary<string, SparkleAnnouncement> ();
private Dictionary<string, SparkleAnnouncement> queue_down =
new Dictionary<string, SparkleAnnouncement> ();
private Timer reconnect_timer = new Timer {
Interval = 60 * 1000,
Enabled = true
};
public SparkleListenerBase (Uri server, string folder_identifier) public SparkleListenerBase (Uri server, string folder_identifier)
{ {
this.server = server; Server = server;
this.channels.Add (folder_identifier);
this.reconnect_timer.Elapsed += delegate { this.reconnect_timer.Elapsed += delegate {
if (!IsConnected && !this.is_connecting) if (!IsConnected && !IsConnecting)
Reconnect (); Reconnect ();
}; };
@ -133,29 +145,46 @@ namespace SparkleLib {
public void AnnounceBase (SparkleAnnouncement announcement) public void AnnounceBase (SparkleAnnouncement announcement)
{ {
if (!this.IsRecentAnnounement (announcement)) { if (!IsRecentAnnouncement (announcement)) {
if (IsConnected) { if (IsConnected) {
SparkleHelpers.DebugInfo ("Listener", SparkleHelpers.DebugInfo ("Listener",
"Announcing message " + announcement.Message + " to " + announcement.FolderIdentifier + " on " + this.server); "Announcing message " + announcement.Message + " to " +
announcement.FolderIdentifier + " on " + Server);
Announce (announcement); Announce (announcement);
this.AddRecentAnnouncement (announcement); AddRecentAnnouncement (announcement);
} else { } else {
SparkleHelpers.DebugInfo ("Listener", "Can't send message to " + this.server + ". Queuing message"); SparkleHelpers.DebugInfo ("Listener",
"Can't send message to " +
Server + ". Queuing message");
this.queue_up [announcement.FolderIdentifier] = announcement; this.queue_up [announcement.FolderIdentifier] = announcement;
} }
} else { } else {
SparkleHelpers.DebugInfo ("Listener", SparkleHelpers.DebugInfo ("Listener",
"Already processed message " + announcement.Message + " to " + announcement.FolderIdentifier + " from " + this.server); "Already processed message " + announcement.Message + " to " +
announcement.FolderIdentifier + " from " + Server);
}
} }
public void AlsoListenToBase (string channel)
{
if (!this.channels.Contains (channel) && IsConnected) {
SparkleHelpers.DebugInfo ("Listener",
"Subscribing to channel " + channel);
this.channels.Add (channel);
AlsoListenTo (channel);
}
} }
public void Reconnect () public void Reconnect ()
{ {
SparkleHelpers.DebugInfo ("Listener", "Trying to reconnect to " + this.server); SparkleHelpers.DebugInfo ("Listener", "Trying to reconnect to " + Server);
Connect (); Connect ();
} }
@ -168,7 +197,8 @@ namespace SparkleLib {
Connected (); Connected ();
if (this.queue_up.Count > 0) { if (this.queue_up.Count > 0) {
SparkleHelpers.DebugInfo ("Listener", "Delivering " + this.queue_up.Count + " queued messages..."); SparkleHelpers.DebugInfo ("Listener",
"Delivering " + this.queue_up.Count + " queued messages...");
foreach (KeyValuePair<string, SparkleAnnouncement> item in this.queue_up) { foreach (KeyValuePair<string, SparkleAnnouncement> item in this.queue_up) {
SparkleAnnouncement announcement = item.Value; SparkleAnnouncement announcement = item.Value;
@ -192,34 +222,46 @@ namespace SparkleLib {
public void OnAnnouncement (SparkleAnnouncement announcement) public void OnAnnouncement (SparkleAnnouncement announcement)
{ {
SparkleHelpers.DebugInfo ("Listener", SparkleHelpers.DebugInfo ("Listener",
"Got message " + announcement.Message + " from " + announcement.FolderIdentifier + " on " + this.server); "Got message " + announcement.Message + " from " +
announcement.FolderIdentifier + " on " + Server);
if (IsRecentAnnounement(announcement) ){ if (IsRecentAnnouncement (announcement)) {
SparkleHelpers.DebugInfo ("Listener", SparkleHelpers.DebugInfo ("Listener",
"Ignoring previously processed message " + announcement.Message + "Ignoring previously processed message " + announcement.Message +
" from " + announcement.FolderIdentifier + " on " + this.server); " from " + announcement.FolderIdentifier + " on " + Server);
return; return;
} }
SparkleHelpers.DebugInfo ("Listener", SparkleHelpers.DebugInfo ("Listener",
"Processing message " + announcement.Message + " from " + announcement.FolderIdentifier + " on " + this.server); "Processing message " + announcement.Message + " from " +
announcement.FolderIdentifier + " on " + Server);
AddRecentAnnouncement (announcement); AddRecentAnnouncement (announcement);
this.queue_down [announcement.FolderIdentifier] = announcement; this.queue_down [announcement.FolderIdentifier] = announcement;
if (Announcement != null) if (Received != null)
Announcement (announcement); Received (announcement);
} }
private bool IsRecentAnnounement (SparkleAnnouncement announcement) public virtual void Dispose ()
{ {
if (!HasRecentAnnouncements (announcement.FolderIdentifier)) { this.reconnect_timer.Dispose ();
}
private bool IsRecentAnnouncement (SparkleAnnouncement announcement)
{
if (!this.recent_announcements
.ContainsKey (announcement.FolderIdentifier)) {
return false; return false;
} else { } else {
foreach (SparkleAnnouncement recent_announcement in GetRecentAnnouncements (announcement.FolderIdentifier)) { foreach (SparkleAnnouncement recent_announcement in
GetRecentAnnouncements (announcement.FolderIdentifier)) {
if (recent_announcement.Message.Equals (announcement.Message)) if (recent_announcement.Message.Equals (announcement.Message))
return true; return true;
} }
@ -240,39 +282,15 @@ namespace SparkleLib {
private void AddRecentAnnouncement (SparkleAnnouncement announcement) private void AddRecentAnnouncement (SparkleAnnouncement announcement)
{ {
List<SparkleAnnouncement> recent_announcements = this.GetRecentAnnouncements (announcement.FolderIdentifier); List<SparkleAnnouncement> recent_announcements =
GetRecentAnnouncements (announcement.FolderIdentifier);
if (!IsRecentAnnounement (announcement)) if (!IsRecentAnnouncement (announcement))
recent_announcements.Add (announcement); recent_announcements.Add (announcement);
if (recent_announcements.Count > this.max_recent_announcements) if (recent_announcements.Count > this.max_recent_announcements)
recent_announcements.RemoveRange (0, (recent_announcements.Count - this.max_recent_announcements)); recent_announcements.RemoveRange (0,
} (recent_announcements.Count - this.max_recent_announcements));
private bool HasRecentAnnouncements (string folder_identifier)
{
return this.recent_announcements.ContainsKey (folder_identifier);
}
public virtual void Dispose ()
{
this.reconnect_timer.Dispose ();
}
public Uri Server {
get {
return this.server;
}
}
public bool IsConnecting {
get {
return this.is_connecting;
}
} }
} }
} }

View file

@ -28,30 +28,31 @@ namespace SparkleLib {
public class SparkleListenerTcp : SparkleListenerBase { public class SparkleListenerTcp : SparkleListenerBase {
private Thread thread;
// these are shared
private readonly Object mutex = new Object();
private Socket socket; private Socket socket;
private bool connected; private Object socket_lock = new Object ();
private Thread thread;
private bool is_connected = false;
private bool is_connecting = false;
public SparkleListenerTcp (Uri server, string folder_identifier) : public SparkleListenerTcp (Uri server, string folder_identifier) :
base (server, folder_identifier) base (server, folder_identifier)
{ {
base.channels.Add (folder_identifier);
this.connected = false;
} }
public override bool IsConnected { public override bool IsConnected {
get { get {
bool result = false; lock (this.socket_lock)
return this.is_connected;
lock (this.mutex) { }
result = this.connected;
} }
return result;
public override bool IsConnecting {
get {
lock (this.socket_lock)
return this.is_connecting;
} }
} }
@ -61,21 +62,29 @@ namespace SparkleLib {
{ {
SparkleHelpers.DebugInfo ("ListenerTcp", "Connecting to " + Server.Host); SparkleHelpers.DebugInfo ("ListenerTcp", "Connecting to " + Server.Host);
base.is_connecting = true; this.is_connecting = true;
this.thread = new Thread ( this.thread = new Thread (
new ThreadStart (delegate { new ThreadStart (delegate {
try { try {
// Connect and subscribe to the channel // Connect and subscribe to the channel
int port = Server.Port; int port = Server.Port;
if (port < 0) port = 9999;
lock (this.mutex) { if (port < 0)
this.socket = new Socket (AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); port = 9999;
lock (this.socket_lock) {
this.socket = new Socket (AddressFamily.InterNetwork,
SocketType.Stream, ProtocolType.Tcp) {
ReceiveTimeout = 30 * 1000
};
this.socket.Connect (Server.Host, port); this.socket.Connect (Server.Host, port);
base.is_connecting = false; this.is_connecting = false;
this.connected = true; this.is_connected = true;
OnConnected (); OnConnected ();
@ -85,6 +94,7 @@ namespace SparkleLib {
} }
} }
byte [] bytes = new byte [4096]; byte [] bytes = new byte [4096];
// List to the channels, this blocks the thread // List to the channels, this blocks the thread
@ -94,19 +104,25 @@ namespace SparkleLib {
if (bytes_read > 0) { if (bytes_read > 0) {
string received = Encoding.UTF8.GetString (bytes); string received = Encoding.UTF8.GetString (bytes);
string line = received.Substring (0, received.IndexOf ("\n")); string line = received.Substring (0, received.IndexOf ("\n"));
if (!line.Contains ("!")) if (!line.Contains ("!"))
continue; continue;
string folder_identifier = line.Substring (0, line.IndexOf ("!")); string folder_identifier = line.Substring (0, line.IndexOf ("!"));
string message = this.CleanMessage (line.Substring (line.IndexOf ("!") + 1)); string message = CleanMessage (line.Substring (line.IndexOf ("!") + 1));
if (!folder_identifier.Equals("debug") &&
!String.IsNullOrEmpty(message)) if (!folder_identifier.Equals ("debug") &&
!String.IsNullOrEmpty (message)) {
OnAnnouncement (new SparkleAnnouncement (folder_identifier, message)); OnAnnouncement (new SparkleAnnouncement (folder_identifier, message));
}
} else { } else {
SparkleHelpers.DebugInfo ("ListenerTcp", "Error on socket"); SparkleHelpers.DebugInfo ("ListenerTcp", "Error on socket");
lock (this.mutex) { lock (this.socket_lock) {
this.socket.Close (); this.socket.Close ();
this.connected = false; this.is_connected = false;
OnDisconnected (); OnDisconnected ();
} }
@ -117,6 +133,8 @@ namespace SparkleLib {
} catch (SocketException e) { } catch (SocketException e) {
SparkleHelpers.DebugInfo ("ListenerTcp", "Could not connect to " + Server + ": " + e.Message); SparkleHelpers.DebugInfo ("ListenerTcp", "Could not connect to " + Server + ": " + e.Message);
this.is_connected = false;
this.is_connecting = false;
OnDisconnected (); OnDisconnected ();
} }
@ -127,44 +145,42 @@ namespace SparkleLib {
} }
public override void AlsoListenTo (string folder_identifier) protected override void AlsoListenTo (string folder_identifier)
{ {
string channel = folder_identifier;
if (!base.channels.Contains (channel)) {
base.channels.Add (channel);
if (IsConnected) {
SparkleHelpers.DebugInfo ("ListenerTcp", "Subscribing to channel " + channel);
string to_send = "subscribe " + folder_identifier + "\n"; string to_send = "subscribe " + folder_identifier + "\n";
try { try {
lock (this.mutex) { lock (this.socket_lock) {
this.socket.Send (Encoding.UTF8.GetBytes (to_send)); this.socket.Send (Encoding.UTF8.GetBytes (to_send));
} }
} catch (SocketException e) { } catch (SocketException e) {
SparkleHelpers.DebugInfo ("ListenerTcp", "Could not connect to " + Server + ": " + e.Message); SparkleHelpers.DebugInfo ("ListenerTcp",
"Could not connect to " + Server + ": " + e.Message);
this.is_connected = false;
this.is_connecting = false;
OnDisconnected (); OnDisconnected ();
} }
} }
}
}
public override void Announce (SparkleAnnouncement announcement) protected override void Announce (SparkleAnnouncement announcement)
{ {
string to_send = "announce " + announcement.FolderIdentifier string to_send = "announce " + announcement.FolderIdentifier
+ " " + announcement.Message + "\n"; + " " + announcement.Message + "\n";
try { try {
lock (this.socket_lock)
lock (this.mutex) {
this.socket.Send (Encoding.UTF8.GetBytes (to_send)); this.socket.Send (Encoding.UTF8.GetBytes (to_send));
}
} catch (SocketException e) { } catch (SocketException e) {
SparkleHelpers.DebugInfo ("ListenerTcp", "Could not connect to " + Server + ": " + e.Message); SparkleHelpers.DebugInfo ("ListenerTcp",
"Could not connect to " + Server + ": " + e.Message);
this.is_connected = false;
this.is_connecting = false;
OnDisconnected (); OnDisconnected ();
} }
@ -175,12 +191,16 @@ namespace SparkleLib {
{ {
this.thread.Abort (); this.thread.Abort ();
this.thread.Join (); this.thread.Join ();
base.Dispose (); base.Dispose ();
} }
private string CleanMessage(string message)
private string CleanMessage (string message)
{ {
return message.Trim ().Replace ("\n", "").Replace ("\0", ""); return message.Trim ()
.Replace ("\n", "")
.Replace ("\0", "");
} }
} }
} }

View file

@ -57,9 +57,9 @@ namespace SparkleLib {
protected bool is_buffering = false; protected bool is_buffering = false;
protected bool server_online = true; protected bool server_online = true;
public readonly SparkleBackend Backend;
public readonly string LocalPath; public readonly string LocalPath;
public readonly string Name; public readonly string Name;
public readonly Uri Url;
public abstract bool AnyDifferences { get; } public abstract bool AnyDifferences { get; }
public abstract string Identifier { get; } public abstract string Identifier { get; }
@ -92,11 +92,12 @@ namespace SparkleLib {
public event ChangesDetectedEventHandler ChangesDetected; public event ChangesDetectedEventHandler ChangesDetected;
public SparkleRepoBase (string path, SparkleBackend backend) public SparkleRepoBase (string path)
{ {
LocalPath = path; LocalPath = path;
Name = Path.GetFileName (LocalPath); Name = Path.GetFileName (LocalPath);
Backend = backend; Url = new Uri (SparkleConfig.DefaultConfig.GetUrlForFolder (Name));
this.poll_interval = this.short_interval; this.poll_interval = this.short_interval;
SyncStatusChanged += delegate (SyncStatus status) { SyncStatusChanged += delegate (SyncStatus status) {
@ -298,7 +299,7 @@ namespace SparkleLib {
}; };
// Fetch changes when there is a message in the irc channel // Fetch changes when there is a message in the irc channel
this.listener.Announcement += delegate (SparkleAnnouncement announcement) { this.listener.Received += delegate (SparkleAnnouncement announcement) {
string identifier = Identifier; string identifier = Identifier;
if (announcement.FolderIdentifier.Equals (identifier) && if (announcement.FolderIdentifier.Equals (identifier) &&

View file

@ -1,4 +1,5 @@
EXTRA_DIST = \ EXTRA_DIST = \
config \
AppDelegate.cs \ AppDelegate.cs \
Growl.framework \ Growl.framework \
Growl.plist \ Growl.plist \

View file

@ -46,6 +46,8 @@ namespace SparkleShare {
private NSTextField size_label_value; private NSTextField size_label_value;
private NSTextField history_label; private NSTextField history_label;
private NSTextField history_label_value; private NSTextField history_label_value;
private NSButton hidden_close_button;
public SparkleEventLog (IntPtr handle) : base (handle) { } public SparkleEventLog (IntPtr handle) : base (handle) { }
@ -68,6 +70,19 @@ namespace SparkleShare {
BackingType = NSBackingStore.Buffered; BackingType = NSBackingStore.Buffered;
this.hidden_close_button = new NSButton () {
Frame = new RectangleF (0, 0, 0, 0),
KeyEquivalentModifierMask = NSEventModifierMask.CommandKeyMask,
KeyEquivalent = "w"
};
this.hidden_close_button.Activated += delegate {
PerformClose (this);
};
ContentView.AddSubview (this.hidden_close_button);
this.size_label = new NSTextField () { this.size_label = new NSTextField () {
Alignment = NSTextAlignment.Right, Alignment = NSTextAlignment.Right,
BackgroundColor = NSColor.WindowBackground, BackgroundColor = NSColor.WindowBackground,

View file

@ -462,10 +462,9 @@ namespace SparkleShare {
case PageType.Finished: { case PageType.Finished: {
Header = "Project succesfully added!"; Header = "Project " + Path.GetFileName (Controller.PreviousPath) +
Description = "Now you can access the files from " + " succesfully added!";
"" + Controller.SyncingFolder + " in " + Description = "Access the files from your SparkleShare folder.";
"your SparkleShare folder.";
if (warnings != null) { if (warnings != null) {
WarningImage = NSImage.ImageNamed ("NSCaution"); WarningImage = NSImage.ImageNamed ("NSCaution");
@ -505,7 +504,7 @@ namespace SparkleShare {
}; };
OpenFolderButton.Activated += delegate { OpenFolderButton.Activated += delegate {
Program.Controller.OpenSparkleShareFolder (Controller.SyncingFolder); Program.Controller.OpenSparkleShareFolder (Path.GetFileName (Controller.PreviousPath));
}; };
Buttons.Add (FinishButton); Buttons.Add (FinishButton);

View file

@ -87,6 +87,8 @@ namespace SparkleShare {
MakeKeyAndOrderFront (this); MakeKeyAndOrderFront (this);
OrderFrontRegardless (); OrderFrontRegardless ();
if (Program.UI != null)
Program.UI.UpdateDockIconVisibility (); Program.UI.UpdateDockIconVisibility ();
} }
@ -146,6 +148,8 @@ namespace SparkleShare {
{ {
base.OrderOut (this); base.OrderOut (this);
NSApplication.SharedApplication.RemoveWindowsItem (this); NSApplication.SharedApplication.RemoveWindowsItem (this);
if (Program.UI != null)
Program.UI.UpdateDockIconVisibility (); Program.UI.UpdateDockIconVisibility ();
return; return;

View file

@ -11,7 +11,7 @@
<RootNamespace>SparkleShare</RootNamespace> <RootNamespace>SparkleShare</RootNamespace>
<AssemblyName>SparkleShare</AssemblyName> <AssemblyName>SparkleShare</AssemblyName>
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion> <TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
<ReleaseVersion>0.8.0</ReleaseVersion> <ReleaseVersion>0.8.1</ReleaseVersion>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols> <DebugSymbols>true</DebugSymbols>

View file

@ -16,6 +16,6 @@ Global
EndGlobalSection EndGlobalSection
GlobalSection(MonoDevelopProperties) = preSolution GlobalSection(MonoDevelopProperties) = preSolution
StartupItem = SparkleShare.csproj StartupItem = SparkleShare.csproj
version = 0.8.0 version = 0.8.1
EndGlobalSection EndGlobalSection
EndGlobal EndGlobal

View file

@ -91,6 +91,15 @@ namespace SparkleShare {
Menu.Delegate = new SparkleStatusIconMenuDelegate (); Menu.Delegate = new SparkleStatusIconMenuDelegate ();
} }
Controller.UpdateQuitItemEvent += delegate (bool quit_item_enabled) {
InvokeOnMainThread (delegate {
if (QuitMenuItem != null) {
QuitMenuItem.Enabled = quit_item_enabled;
StatusItem.Menu.Update ();
}
});
};
Controller.UpdateMenuEvent += delegate (IconState state) { Controller.UpdateMenuEvent += delegate (IconState state) {
InvokeOnMainThread (delegate { InvokeOnMainThread (delegate {
using (var a = new NSAutoreleasePool ()) { using (var a = new NSAutoreleasePool ()) {
@ -137,6 +146,8 @@ namespace SparkleShare {
break; break;
} }
StatusItem.Menu.Update ();
} }
}); });
}; };
@ -311,7 +322,7 @@ namespace SparkleShare {
QuitMenuItem = new NSMenuItem () { QuitMenuItem = new NSMenuItem () {
Title = "Quit", Title = "Quit",
Enabled = true Enabled = Controller.QuitItemEnabled
}; };
QuitMenuItem.Activated += delegate { QuitMenuItem.Activated += delegate {

View file

@ -69,6 +69,8 @@ namespace SparkleShare {
if (Program.Controller.FirstRun) { if (Program.Controller.FirstRun) {
Setup = new SparkleSetup (); Setup = new SparkleSetup ();
Setup.Controller.ShowSetupPage (); Setup.Controller.ShowSetupPage ();
UpdateDockIconVisibility ();
} }
} }
} }

View file

@ -29,7 +29,7 @@ namespace SparkleShare {
public SparkleBubblesController () public SparkleBubblesController ()
{ {
Program.Controller.ConflictNotificationRaised += delegate { Program.Controller.ConflictNotificationRaised += delegate {
ShowBubble ("Ouch! Mid-air collision!", ShowBubble ("Conflict detected.",
"Don't worry, SparkleShare made a copy of each conflicting file.", "Don't worry, SparkleShare made a copy of each conflicting file.",
null); null);
}; };

View file

@ -263,7 +263,6 @@ namespace SparkleShare {
public abstract string DayEntryHTML { get; } public abstract string DayEntryHTML { get; }
public abstract string EventEntryHTML { get; } public abstract string EventEntryHTML { get; }
public string GetHTMLLog (List<SparkleChangeSet> change_sets) public string GetHTMLLog (List<SparkleChangeSet> change_sets)
{ {
List <ActivityDay> activity_days = new List <ActivityDay> (); List <ActivityDay> activity_days = new List <ActivityDay> ();
@ -456,6 +455,7 @@ namespace SparkleShare {
.Replace ("<!-- $event-avatar-url -->", change_set_avatar) .Replace ("<!-- $event-avatar-url -->", change_set_avatar)
.Replace ("<!-- $event-time -->", timestamp) .Replace ("<!-- $event-time -->", timestamp)
.Replace ("<!-- $event-folder -->", change_set.Folder) .Replace ("<!-- $event-folder -->", change_set.Folder)
.Replace ("<!-- $event-url -->", change_set.Url.ToString ())
.Replace ("<!-- $event-revision -->", change_set.Revision) .Replace ("<!-- $event-revision -->", change_set.Revision)
.Replace ("<!-- $event-folder-color -->", AssignColor (change_set.Folder)) .Replace ("<!-- $event-folder-color -->", AssignColor (change_set.Folder))
.Replace ("<!-- $event-comments -->", comments); .Replace ("<!-- $event-comments -->", comments);
@ -570,16 +570,23 @@ namespace SparkleShare {
// Adds a repository to the list of repositories // Adds a repository to the list of repositories
private void AddRepository (string folder_path) private void AddRepository (string folder_path)
{ {
if (folder_path.Equals (SparkleConfig.DefaultConfig.TmpPath)) SparkleRepoBase repo = null;
return;
string folder_name = Path.GetFileName (folder_path); string folder_name = Path.GetFileName (folder_path);
string backend = SparkleConfig.DefaultConfig.GetBackendForFolder (folder_name); string backend = SparkleConfig.DefaultConfig.GetBackendForFolder (folder_name);
if (backend == null) try {
return; repo = (SparkleRepoBase) Activator.CreateInstance (
Type.GetType ("SparkleLib.SparkleRepo" + backend + ", SparkleLib"),
folder_path
);
} catch {
SparkleHelpers.DebugInfo ("Controller",
"Failed to load \"" + backend + "\" backend for \"" + folder_name + "\"");
return;
}
SparkleRepoBase repo = new SparkleRepoGit (folder_path, SparkleBackend.DefaultBackend);
repo.NewChangeSet += delegate (SparkleChangeSet change_set) { repo.NewChangeSet += delegate (SparkleChangeSet change_set) {
@ -624,6 +631,7 @@ namespace SparkleShare {
UpdateState (); UpdateState ();
}; };
Repositories.Add (repo); Repositories.Add (repo);
repo.Initialize (); repo.Initialize ();
} }
@ -770,6 +778,7 @@ namespace SparkleShare {
process.StartInfo.UseShellExecute = false; process.StartInfo.UseShellExecute = false;
process.StartInfo.FileName = "ssh-add"; process.StartInfo.FileName = "ssh-add";
process.StartInfo.Arguments = "\"" + key_file_path + "\""; process.StartInfo.Arguments = "\"" + key_file_path + "\"";
process.StartInfo.CreateNoWindow = true;
process.Start (); process.Start ();
process.WaitForExit (); process.WaitForExit ();
@ -817,7 +826,7 @@ namespace SparkleShare {
string key_file_path = Path.Combine (keys_path, key_file_name); string key_file_path = Path.Combine (keys_path, key_file_name);
if (File.Exists (key_file_path)) { if (File.Exists (key_file_path)) {
SparkleHelpers.DebugInfo ("Config", "Key already exists ('" + key_file_name + "'), " + SparkleHelpers.DebugInfo ("Auth", "Key already exists ('" + key_file_name + "'), " +
"leaving it untouched"); "leaving it untouched");
return; return;
} }
@ -834,6 +843,7 @@ namespace SparkleShare {
process.StartInfo.UseShellExecute = false; process.StartInfo.UseShellExecute = false;
process.StartInfo.RedirectStandardOutput = true; process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.FileName = "ssh-keygen"; process.StartInfo.FileName = "ssh-keygen";
process.StartInfo.CreateNoWindow = true;
// -t is the crypto type // -t is the crypto type
// -P is the password (none) // -P is the password (none)
@ -843,8 +853,8 @@ namespace SparkleShare {
process.Start (); process.Start ();
process.WaitForExit (); process.WaitForExit ();
SparkleHelpers.DebugInfo ("Config", "Created private key '" + key_file_name + "'"); SparkleHelpers.DebugInfo ("Auth", "Created private key '" + key_file_name + "'");
SparkleHelpers.DebugInfo ("Config", "Created public key '" + key_file_name + ".pub'"); SparkleHelpers.DebugInfo ("Auth", "Created public key '" + key_file_name + ".pub'");
// Add some restrictions to what the key can // Add some restrictions to what the key can
// do when uploaded to the server // do when uploaded to the server
@ -877,7 +887,7 @@ namespace SparkleShare {
if (!Directory.Exists (avatar_path)) { if (!Directory.Exists (avatar_path)) {
Directory.CreateDirectory (avatar_path); Directory.CreateDirectory (avatar_path);
SparkleHelpers.DebugInfo ("Config", "Created '" + avatar_path + "'"); SparkleHelpers.DebugInfo ("Avatar", "Created '" + avatar_path + "'");
} }
foreach (string raw_email in emails) { foreach (string raw_email in emails) {
@ -923,11 +933,11 @@ namespace SparkleShare {
lock (this.avatar_lock) lock (this.avatar_lock)
File.WriteAllBytes (avatar_file_path, buffer); File.WriteAllBytes (avatar_file_path, buffer);
SparkleHelpers.DebugInfo ("Controller", "Fetched gravatar for " + email); SparkleHelpers.DebugInfo ("Avatar", "Fetched gravatar for " + email);
} }
} catch (WebException e) { } catch (WebException e) {
SparkleHelpers.DebugInfo ("Controller", "Failed fetching gravatar for " + email); SparkleHelpers.DebugInfo ("Avatar", "Failed fetching gravatar for " + email);
// Stop downloading further avatars if we have no internet access // Stop downloading further avatars if we have no internet access
if (e.Status == WebExceptionStatus.Timeout){ if (e.Status == WebExceptionStatus.Timeout){
@ -980,26 +990,42 @@ namespace SparkleShare {
File.SetAttributes (tmp_path, File.GetAttributes (tmp_path) | FileAttributes.Hidden); File.SetAttributes (tmp_path, File.GetAttributes (tmp_path) | FileAttributes.Hidden);
} }
// Strip the '.git' from the name // Strip the '.git' from the name
string canonical_name = Path.GetFileNameWithoutExtension (remote_folder); string canonical_name = Path.GetFileNameWithoutExtension (remote_folder);
string tmp_folder = Path.Combine (tmp_path, canonical_name); string tmp_folder = Path.Combine (tmp_path, canonical_name);
string backend = Path.GetExtension (remote_folder);
string backend = null; if (!string.IsNullOrEmpty (backend)) {
backend = backend.Substring (1);
/* if (remote_folder.EndsWith (".hg")) { char [] letters = backend.ToCharArray ();
remote_folder = remote_folder.Substring (0, (remote_folder.Length - 3)); letters [0] = char.ToUpper (letters [0]);
fetcher = new SparkleFetcherHg (server, remote_folder, tmp_folder); backend = new string (letters);
backend = "Hg";
} else if (remote_folder.EndsWith (".scp")) { } else {
remote_folder = remote_folder.Substring (0, (remote_folder.Length - 4));
fetcher = new SparkleFetcherScp (server, remote_folder, tmp_folder);
backend = "Scp";
} else {*/
this.fetcher = new SparkleFetcherGit (server, remote_folder, tmp_folder);
backend = "Git"; backend = "Git";
//} }
try {
this.fetcher = (SparkleFetcherBase) Activator.CreateInstance (
Type.GetType ("SparkleLib.SparkleFetcher" + backend + ", SparkleLib"),
server,
remote_folder,
tmp_folder
);
} catch {
SparkleHelpers.DebugInfo ("Controller",
"Failed to load \"" + backend + "\" backend for \"" + canonical_name + "\"");
if (FolderFetchError != null)
FolderFetchError (Path.Combine (server, remote_folder));
return;
}
bool target_folder_exists = Directory.Exists ( bool target_folder_exists = Directory.Exists (
Path.Combine (SparkleConfig.DefaultConfig.FoldersPath, canonical_name)); Path.Combine (SparkleConfig.DefaultConfig.FoldersPath, canonical_name));
@ -1046,7 +1072,6 @@ namespace SparkleShare {
} }
}; };
this.fetcher.Failed += delegate { this.fetcher.Failed += delegate {
if (FolderFetchError != null) if (FolderFetchError != null)
FolderFetchError (this.fetcher.RemoteUrl); FolderFetchError (this.fetcher.RemoteUrl);
@ -1057,7 +1082,6 @@ namespace SparkleShare {
Directory.Delete (tmp_path, true); Directory.Delete (tmp_path, true);
}; };
this.fetcher.ProgressChanged += delegate (double percentage) { this.fetcher.ProgressChanged += delegate (double percentage) {
if (FolderFetching != null) if (FolderFetching != null)
FolderFetching (percentage); FolderFetching (percentage);

View file

@ -174,8 +174,9 @@ namespace SparkleShare {
TreeIter iter; TreeIter iter;
this.combo_box.GetActiveIter (out iter); this.combo_box.GetActiveIter (out iter);
string selection = (string) this.combo_box.Model.GetValue (iter, 0); string selection = (string) this.combo_box.Model.GetValue (iter, 0);
TreePath path = this.combo_box.Model.GetPath (iter);
if (selection.Equals (_("All Folders"))) if (path.Indices [0] == 0)
Controller.SelectedFolder = null; Controller.SelectedFolder = null;
else else
Controller.SelectedFolder = selection; Controller.SelectedFolder = selection;
@ -224,7 +225,7 @@ namespace SparkleShare {
Application.Invoke (delegate { Application.Invoke (delegate {
this.spinner.Stop (); this.spinner.Stop ();
this.web_view.LoadString (html, null, null, "file:///"); this.web_view.LoadString (html, null, null, "file://");
this.content_wrapper.Remove (this.content_wrapper.Child); this.content_wrapper.Remove (this.content_wrapper.Child);
this.content_wrapper.Add (this.scrolled_window); this.content_wrapper.Add (this.scrolled_window);
this.content_wrapper.ShowAll (); this.content_wrapper.ShowAll ();

View file

@ -66,8 +66,8 @@ namespace SparkleShare {
// A short delay is less annoying than // A short delay is less annoying than
// a flashing window // a flashing window
if (watch.ElapsedMilliseconds < 500) if (watch.ElapsedMilliseconds < 750)
Thread.Sleep (500 - (int) watch.ElapsedMilliseconds); Thread.Sleep (750 - (int) watch.ElapsedMilliseconds);
if (UpdateContentEvent != null) if (UpdateContentEvent != null)
UpdateContentEvent (html); UpdateContentEvent (html);

View file

@ -441,20 +441,21 @@ namespace SparkleShare {
UrgencyHint = true; UrgencyHint = true;
if (!HasToplevelFocus) { if (!HasToplevelFocus) {
string title = String.Format (_("{0} has been successfully added"), Controller.SyncingFolder); string title = _("Project successfully added!");
string subtext = ""; string subtext = "";
SparkleUI.Bubbles.Controller.ShowBubble (title, subtext, null); SparkleUI.Bubbles.Controller.ShowBubble (title, subtext, null);
} }
Header = _("Project successfully added!"); Header = _("Project " + System.IO.Path.GetFileName (Controller.PreviousPath) +
" successfully added!");
Description = _("Access the files from your SparkleShare folder."); Description = _("Access the files from your SparkleShare folder.");
// A button that opens the synced folder // A button that opens the synced folder
Button open_folder_button = new Button (_("Open Folder")); Button open_folder_button = new Button (_("Open Folder"));
open_folder_button.Clicked += delegate { open_folder_button.Clicked += delegate {
Program.Controller.OpenSparkleShareFolder (Controller.SyncingFolder); Program.Controller.OpenSparkleShareFolder (System.IO.Path.GetFileName (Controller.PreviousPath));
}; };
Button finish_button = new Button (_("Finish")); Button finish_button = new Button (_("Finish"));

View file

@ -33,11 +33,13 @@ namespace SparkleShare {
public SparkleStatusIconController Controller = new SparkleStatusIconController (); public SparkleStatusIconController Controller = new SparkleStatusIconController ();
// TODO: fix case
private Timer Animation; private Timer Animation;
private Gdk.Pixbuf [] AnimationFrames; private Gdk.Pixbuf [] AnimationFrames;
private int FrameNumber; private int FrameNumber;
private string StateText; private string StateText;
private Menu Menu; private Menu Menu;
private MenuItem quit_item;
#if HAVE_APP_INDICATOR #if HAVE_APP_INDICATOR
private ApplicationIndicator indicator; private ApplicationIndicator indicator;
@ -78,6 +80,16 @@ namespace SparkleShare {
CreateMenu (); CreateMenu ();
Controller.UpdateQuitItemEvent += delegate (bool quit_item_enabled) {
Application.Invoke (delegate {
if (this.quit_item != null) {
this.quit_item.Sensitive = quit_item_enabled;
Menu.ShowAll ();
}
});
};
Controller.UpdateMenuEvent += delegate (IconState state) { Controller.UpdateMenuEvent += delegate (IconState state) {
Application.Invoke (delegate { Application.Invoke (delegate {
switch (state) { switch (state) {
@ -130,6 +142,8 @@ namespace SparkleShare {
break; break;
} }
Menu.ShowAll ();
}); });
}; };
} }
@ -311,13 +325,15 @@ namespace SparkleShare {
Menu.Add (new SeparatorMenuItem ()); Menu.Add (new SeparatorMenuItem ());
// A menu item that quits the application // A menu item that quits the application
MenuItem quit_item = new MenuItem (_("Quit")); this.quit_item = new MenuItem (_("Quit")) {
Sensitive = Controller.QuitItemEnabled
};
quit_item.Activated += delegate { this.quit_item.Activated += delegate {
Program.Controller.Quit (); Program.Controller.Quit ();
}; };
Menu.Add (quit_item); Menu.Add (this.quit_item);
Menu.ShowAll (); Menu.ShowAll ();
#if HAVE_APP_INDICATOR #if HAVE_APP_INDICATOR

View file

@ -38,6 +38,9 @@ namespace SparkleShare {
public event UpdateMenuEventHandler UpdateMenuEvent; public event UpdateMenuEventHandler UpdateMenuEvent;
public delegate void UpdateMenuEventHandler (IconState state); public delegate void UpdateMenuEventHandler (IconState state);
public event UpdateQuitItemEventHandler UpdateQuitItemEvent;
public delegate void UpdateQuitItemEventHandler (bool quit_item_enabled);
public IconState CurrentState = IconState.Idle; public IconState CurrentState = IconState.Idle;
public string [] Folders { public string [] Folders {
@ -76,6 +79,14 @@ namespace SparkleShare {
} }
} }
public bool QuitItemEnabled {
get {
return (CurrentState != IconState.Syncing &&
CurrentState != IconState.SyncingDown &&
CurrentState != IconState.SyncingUp);
}
}
public SparkleStatusIconController () public SparkleStatusIconController ()
{ {
@ -89,6 +100,9 @@ namespace SparkleShare {
if (CurrentState != IconState.Error) if (CurrentState != IconState.Error)
CurrentState = IconState.Idle; CurrentState = IconState.Idle;
if (UpdateQuitItemEvent != null)
UpdateQuitItemEvent (QuitItemEnabled);
if (UpdateMenuEvent != null) if (UpdateMenuEvent != null)
UpdateMenuEvent (CurrentState); UpdateMenuEvent (CurrentState);
}; };
@ -97,6 +111,9 @@ namespace SparkleShare {
Program.Controller.OnSyncing += delegate { Program.Controller.OnSyncing += delegate {
CurrentState = IconState.Syncing; CurrentState = IconState.Syncing;
if (UpdateQuitItemEvent != null)
UpdateQuitItemEvent (QuitItemEnabled);
if (UpdateMenuEvent != null) if (UpdateMenuEvent != null)
UpdateMenuEvent (IconState.Syncing); UpdateMenuEvent (IconState.Syncing);
}; };
@ -105,6 +122,9 @@ namespace SparkleShare {
Program.Controller.OnError += delegate { Program.Controller.OnError += delegate {
CurrentState = IconState.Error; CurrentState = IconState.Error;
if (UpdateQuitItemEvent != null)
UpdateQuitItemEvent (QuitItemEnabled);
if (UpdateMenuEvent != null) if (UpdateMenuEvent != null)
UpdateMenuEvent (IconState.Error); UpdateMenuEvent (IconState.Error);
}; };

View file

@ -1,4 +1,4 @@
#!/bin/bash #!/usr/bin/env bash
if [[ $UID -eq 0 ]]; then if [[ $UID -eq 0 ]]; then
echo "Cannot run as root. Things would go utterly wrong." echo "Cannot run as root. Things would go utterly wrong."

View file

@ -1,13 +1,13 @@
dnl Process this file with autoconf to produce a configure script. dnl Process this file with autoconf to produce a configure script.
m4_define([sparkleshare_version], m4_define([sparkleshare_version],
[0.8.0]) [0.8.1])
m4_define([sparkleshare_asm_version], m4_define([sparkleshare_asm_version],
[0.8.0]) [0.8.1])
AC_PREREQ([2.54]) AC_PREREQ([2.54])
AC_INIT([SparkleShare], sparkleshare_version) AC_INIT([SparkleShare], sparkleshare_version)
AM_INIT_AUTOMAKE([1.11 dist-bzip2 dist-zip foreign tar-ustar]) AM_INIT_AUTOMAKE([1.11 dist-bzip2 dist-zip foreign tar-pax])
AM_MAINTAINER_MODE AM_MAINTAINER_MODE
dnl Export Version Info dnl Export Version Info

View file

@ -1,6 +1,6 @@
<div class='event-entry' style='background-image: url("<!-- $event-avatar-url -->");'> <div class='event-entry' style='background-image: url("<!-- $event-avatar-url -->");'>
<div class='event-user-name'><!-- $event-user-name --></div> <div class='event-user-name'><!-- $event-user-name --></div>
<div class='event-folder'><!-- $event-folder --></div> <div class='event-folder'><!-- $event-folder --><span><!-- $event-url --></span></div>
<!-- $event-entry-content --> <!-- $event-entry-content -->

View file

@ -93,7 +93,7 @@
} }
body { body {
background-color: #fff; background-color: #fafafa;
color: <!-- $body-color -->; color: <!-- $body-color -->;
font-size: <!-- $body-font-size -->; font-size: <!-- $body-font-size -->;
font-family: <!-- $body-font-family -->; font-family: <!-- $body-font-family -->;
@ -152,16 +152,38 @@
font-weight: bold; font-weight: bold;
} }
.event-folder { .event-folder {
float: right; float: right;
color: <!-- $secondary-font-color -->; color: <!-- $secondary-font-color -->;
position: relative;
cursor: pointer;
} }
.event-folder span {
display: none
}
.event-folder:hover span {
display: block;
position: absolute;
top: 20px;
right: 0;
background-color: rgba(20, 20, 20, 0.8);
color: #fafafa;
padding: 7px 12px;
border-radius: 5px;
font-family: sans-serif;
font-size: 12px;
float:right;
}
.event-entry { .event-entry {
margin-bottom: 24px; margin-bottom: 24px;
padding-bottom: 24px; padding-bottom: 24px;
border-bottom: 1px <!-- $secondary-font-color --> solid; border-bottom: 1px #ddd solid;
width: 95%; width: 99%;
margin-left: auto; margin-left: auto;
margin-right: auto; margin-right: auto;
padding-left: 72px; padding-left: 72px;