statusicon: On sync fail, add error descriptions as submenus. Closes #874

This commit is contained in:
Hylke Bons 2012-09-18 19:40:06 +01:00
parent 06f7a12c72
commit cb5617c300
7 changed files with 225 additions and 95 deletions

View file

@ -33,6 +33,8 @@ namespace SparkleLib.Git {
public SparkleRepo (string path, SparkleConfig config) : base (path, config)
{
// TODO: Set git locale to en-US
SparkleGit git = new SparkleGit (LocalPath, "config --get filter.bin.clean");
git.StartAndWaitForExit ();
@ -157,6 +159,7 @@ namespace SparkleLib.Git {
SparkleLogger.LogInfo ("Git", Name + " | Remote changes found, local: " +
current_revision + ", remote: " + remote_revision);
Error = ErrorStatus.None;
return true;
} else {
@ -212,11 +215,6 @@ namespace SparkleLib.Git {
number = (number / 100 * 20);
} else {
if (line.StartsWith ("ERROR: QUOTA EXCEEDED")) {
int quota_limit = int.Parse (line.Substring (21).Trim ());
throw new QuotaExceededException ("Quota exceeded", quota_limit);
}
// "Writing objects" stage
number = (number / 100 * 80 + 20);
@ -231,6 +229,9 @@ namespace SparkleLib.Git {
} else {
SparkleLogger.LogInfo ("Git", Name + " | " + line);
if (FindError (line))
return false;
}
if (number >= percentage) {
@ -247,6 +248,7 @@ namespace SparkleLib.Git {
return true;
} else {
Error = ErrorStatus.HostUnreachable;
return false;
}
}
@ -293,6 +295,9 @@ namespace SparkleLib.Git {
} else {
SparkleLogger.LogInfo ("Git", Name + " | " + line);
if (FindError (line))
return false;
}
@ -316,6 +321,7 @@ namespace SparkleLib.Git {
return true;
} else {
Error = ErrorStatus.HostUnreachable;
return false;
}
}
@ -548,6 +554,31 @@ namespace SparkleLib.Git {
}
private bool FindError (string line)
{
Error = ErrorStatus.None;
if (line.StartsWith ("WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!") ||
line.StartsWith ("WARNING: POSSIBLE DNS SPOOFING DETECTED!")) {
Error = ErrorStatus.HostIdentityChanged;
} else if (line.StartsWith ("Permission denied")) {
Error = ErrorStatus.AuthenticationFailed;
} else if (line.StartsWith ("error: Disk space exceeded")) {
Error = ErrorStatus.DiskSpaceExcedeed;
}
if (Error != ErrorStatus.None) {
SparkleLogger.LogInfo ("Git", Name + " | Error status changed to " + Error);
return true;
} else {
return false;
}
}
private List<SparkleChangeSet> GetChangeSetsInternal (string path, int count)
{
if (count < 1)

View file

@ -32,6 +32,15 @@ namespace SparkleLib {
}
public enum ErrorStatus {
None,
HostUnreachable,
HostIdentityChanged,
AuthenticationFailed,
DiskSpaceExcedeed
}
public abstract class SparkleRepoBase {
public static bool UseCustomWatcher = false;
@ -67,7 +76,7 @@ namespace SparkleLib {
public readonly Uri RemoteUrl;
public List<SparkleChangeSet> ChangeSets { get; protected set; }
public SyncStatus Status { get; private set; }
public bool ServerOnline { get; private set; }
public ErrorStatus Error { get; protected set; }
public bool IsBuffering { get; private set; }
public double ProgressPercentage { get; private set; }
public string ProgressSpeed { get; private set; }
@ -136,12 +145,13 @@ namespace SparkleLib {
public SparkleRepoBase (string path, SparkleConfig config)
{
Status = SyncStatus.Idle;
Error = ErrorStatus.None;
this.local_config = config;
LocalPath = path;
Name = Path.GetFileName (LocalPath);
RemoteUrl = new Uri (this.local_config.GetUrlForFolder (Name));
IsBuffering = false;
ServerOnline = true;
this.identifier = Identifier;
ChangeSets = GetChangeSets ();
@ -170,7 +180,7 @@ namespace SparkleLib {
// In the unlikely case that we haven't synced up our
// changes or the server was down, sync up again
if (HasUnsyncedChanges && !is_syncing && ServerOnline)
if (HasUnsyncedChanges && !is_syncing && Error == ErrorStatus.None)
SyncUpBase ();
};
}
@ -309,14 +319,13 @@ namespace SparkleLib {
if (!UseCustomWatcher)
this.watcher.Disable ();
if (ServerOnline && SyncUp ()) {
if (Error == ErrorStatus.None && SyncUp ()) {
HasUnsyncedChanges = false;
SyncStatusChanged (SyncStatus.Idle);
this.listener.Announce (new SparkleAnnouncement (Identifier, CurrentRevision));
} else {
ServerOnline = false;
SyncStatusChanged (SyncStatus.Error);
}
}
@ -341,7 +350,7 @@ namespace SparkleLib {
if (SyncDown ()) {
SparkleLogger.LogInfo ("SyncDown", Name + " | Done");
ServerOnline = true;
Error = ErrorStatus.None;
ChangeSets = GetChangeSets ();
@ -373,10 +382,8 @@ namespace SparkleLib {
} else {
SparkleLogger.LogInfo ("SyncDown", Name + " | Error");
ServerOnline = false;
ChangeSets = GetChangeSets ();
SyncStatusChanged (SyncStatus.Error);
}

View file

@ -130,7 +130,7 @@ namespace SparkleShare {
});
};
Controller.UpdateOpenRecentEventsItemEvent += delegate (bool item_enabled) {
Controller.UpdateRecentEventsItemEvent += delegate (bool item_enabled) {
Application.Invoke (delegate {
this.recent_events_item.Sensitive = item_enabled;
this.recent_events_item.ShowAll ();
@ -167,46 +167,66 @@ namespace SparkleShare {
this.menu.Add (folder_item);
if (Program.Controller.Folders.Count > 0) {
int i = 0;
foreach (string folder_name in Controller.Folders) {
ImageMenuItem item = new SparkleMenuItem (folder_name);
Gdk.Pixbuf folder_icon;
if (Program.Controller.UnsyncedFolders.Contains (folder_name)) {
folder_icon = IconTheme.Default.LoadIcon ("dialog-error", 16,
if (!string.IsNullOrEmpty (Controller.FolderErrors [i])) {
folder_icon = IconTheme.Default.LoadIcon ("dialog-warning", 16,
IconLookupFlags.GenericFallback);
item.Submenu = new Menu ();
MenuItem error_item = new MenuItem (Controller.FolderErrors [i]) {
Sensitive = false
};
item.Submenu.Add (error_item);
} else {
folder_icon = IconTheme.Default.LoadIcon ("folder", 16,
IconLookupFlags.GenericFallback);
}
ImageMenuItem subfolder_item = new SparkleMenuItem (folder_name) {
Image = new Image (folder_icon)
};
item.Image = new Image (folder_icon);
subfolder_item.Activated += OpenFolderDelegate (folder_name);
this.menu.Add (subfolder_item);
item.Activated += OpenFolderDelegate (folder_name);
this.menu.Add (item);
i++;
}
Menu submenu = new Menu ();
i = 0;
foreach (string folder_name in Controller.OverflowFolders) {
ImageMenuItem item = new SparkleMenuItem (folder_name);
Gdk.Pixbuf folder_icon;
if (Program.Controller.UnsyncedFolders.Contains (folder_name)) {
folder_icon = IconTheme.Default.LoadIcon ("dialog-error", 16,
if (!string.IsNullOrEmpty (Controller.OverflowFolderErrors [i])) {
folder_icon = IconTheme.Default.LoadIcon ("dialog-warning", 16,
IconLookupFlags.GenericFallback);
item.Submenu = new Menu ();
MenuItem error_item = new MenuItem (Controller.OverflowFolderErrors [i]) {
Sensitive = false
};
item.Submenu.Add (error_item);
} else {
folder_icon = IconTheme.Default.LoadIcon ("folder", 16,
IconLookupFlags.GenericFallback);
}
ImageMenuItem subfolder_item = new SparkleMenuItem (folder_name) {
Image = new Image (folder_icon)
};
item.Image = new Image (folder_icon);
item.Activated += OpenFolderDelegate (folder_name);
submenu.Add (item);
subfolder_item.Activated += OpenFolderDelegate (folder_name);
submenu.Add (subfolder_item);
i++;
}
if (submenu.Children.Length > 0) {
@ -232,10 +252,10 @@ namespace SparkleShare {
this.recent_events_item = new MenuItem ("Recent Changes…");
this.recent_events_item.Sensitive = Controller.OpenRecentEventsItemEnabled;
this.recent_events_item.Sensitive = Controller.RecentEventsItemEnabled;
this.recent_events_item.Activated += delegate {
Controller.OpenRecentEventsClicked ();
Controller.RecentEventsClicked ();
};
this.menu.Add (this.recent_events_item);

View file

@ -162,7 +162,7 @@ namespace SparkleShare {
}
};
Controller.UpdateOpenRecentEventsItemEvent += delegate (bool events_item_enabled) {
Controller.UpdateRecentEventsItemEvent += delegate (bool events_item_enabled) {
using (var a = new NSAutoreleasePool ())
{
InvokeOnMainThread (delegate {
@ -208,12 +208,12 @@ namespace SparkleShare {
this.recent_events_item = new NSMenuItem () {
Title = "Recent Changes…",
Enabled = Controller.OpenRecentEventsItemEnabled
Enabled = Controller.RecentEventsItemEnabled
};
if (Controller.Folders.Length > 0) {
this.recent_events_item.Activated += delegate {
Controller.OpenRecentEventsClicked ();
Controller.RecentEventsClicked ();
};
}
@ -272,10 +272,19 @@ namespace SparkleShare {
NSMenuItem item = new NSMenuItem ();
item.Title = folder_name;
if (Program.Controller.UnsyncedFolders.Contains (folder_name))
item.Image = this.caution_image;
else
if (!string.IsNullOrEmpty (Controller.FolderErrors [i])) {
item.Image = this.caution_image;
item.Submenu = new NSMenu ();
NSMenuItem error_item = new NSMenuItem () {
Title = Controller.FolderErrors [i]
};
item.Submenu.AddItem (error_item);
} else {
item.Image = this.folder_image;
}
item.Image.Size = new SizeF (16, 16);
this.folder_tasks [i] = OpenFolderDelegate (folder_name);
@ -291,10 +300,19 @@ namespace SparkleShare {
NSMenuItem item = new NSMenuItem ();
item.Title = folder_name;
if (Program.Controller.UnsyncedFolders.Contains (folder_name))
item.Image = this.caution_image;
else
if (!string.IsNullOrEmpty (Controller.OverflowFolderErrors [i])) {
item.Image = this.caution_image;
item.Submenu = new NSMenu ();
NSMenuItem error_item = new NSMenuItem () {
Title = Controller.OverflowFolderErrors [i]
};
item.Submenu.AddItem (error_item);
} else {
item.Image = this.folder_image;
}
item.Image.Size = new SizeF (16, 16);
this.overflow_tasks [i] = OpenFolderDelegate (folder_name);

View file

@ -16,9 +16,7 @@
using System;
using System.Timers;
using Threading = System.Threading;
using System.Threading;
using SparkleLib;
@ -47,38 +45,21 @@ namespace SparkleShare {
public event UpdateQuitItemEventHandler UpdateQuitItemEvent = delegate { };
public delegate void UpdateQuitItemEventHandler (bool quit_item_enabled);
public event UpdateOpenRecentEventsItemEventHandler UpdateOpenRecentEventsItemEvent = delegate { };
public delegate void UpdateOpenRecentEventsItemEventHandler (bool open_recent_events_item_enabled);
public event UpdateRecentEventsItemEventHandler UpdateRecentEventsItemEvent = delegate { };
public delegate void UpdateRecentEventsItemEventHandler (bool recent_events_item_enabled);
public IconState CurrentState = IconState.Idle;
public string StateText = "Welcome to SparkleShare!";
public readonly int MenuOverflowThreshold = 9;
public readonly int MinSubmenuOverflowCount = 3;
public string [] Folders {
get {
int overflow_count = (Program.Controller.Folders.Count - MenuOverflowThreshold);
if (overflow_count >= MinSubmenuOverflowCount)
return Program.Controller.Folders.GetRange (0, MenuOverflowThreshold).ToArray ();
else
return Program.Controller.Folders.ToArray ();
}
}
public string [] OverflowFolders {
get {
int overflow_count = (Program.Controller.Folders.Count - MenuOverflowThreshold);
if (overflow_count >= MinSubmenuOverflowCount)
return Program.Controller.Folders.GetRange (MenuOverflowThreshold, overflow_count).ToArray ();
else
return new string [0];
}
}
public string [] Folders;
public string [] FolderErrors;
public string [] OverflowFolders;
public string [] OverflowFolderErrors;
public string FolderSize {
get {
@ -112,7 +93,7 @@ namespace SparkleShare {
}
}
public bool OpenRecentEventsItemEnabled {
public bool RecentEventsItemEnabled {
get {
return (Program.Controller.RepositoriesLoaded && Program.Controller.Folders.Count > 0);
}
@ -121,6 +102,8 @@ namespace SparkleShare {
public SparkleStatusIconController ()
{
UpdateFolders ();
Program.Controller.FolderListChanged += delegate {
if (CurrentState != IconState.Error) {
CurrentState = IconState.Idle;
@ -131,12 +114,16 @@ namespace SparkleShare {
StateText = "Files up to date " + FolderSize;
}
UpdateFolders ();
UpdateStatusItemEvent (StateText);
UpdateOpenRecentEventsItemEvent (OpenRecentEventsItemEnabled);
UpdateRecentEventsItemEvent (RecentEventsItemEnabled);
UpdateMenuEvent (CurrentState);
};
Program.Controller.OnIdle += delegate {
UpdateFolders ();
if (CurrentState != IconState.Error) {
CurrentState = IconState.Idle;
@ -148,7 +135,6 @@ namespace SparkleShare {
UpdateQuitItemEvent (QuitItemEnabled);
UpdateStatusItemEvent (StateText);
UpdateIconEvent (CurrentState);
UpdateMenuEvent (CurrentState);
};
@ -191,10 +177,12 @@ namespace SparkleShare {
CurrentState = IconState.Error;
StateText = "Failed to send some changes";
UpdateFolders ();
UpdateQuitItemEvent (QuitItemEnabled);
UpdateStatusItemEvent (StateText);
UpdateIconEvent (CurrentState);
UpdateMenuEvent (CurrentState);
};
}
@ -217,9 +205,9 @@ namespace SparkleShare {
}
public void OpenRecentEventsClicked ()
public void RecentEventsClicked ()
{
new Threading.Thread (() => Program.Controller.ShowEventLogWindow ()).Start ();
new Thread (() => Program.Controller.ShowEventLogWindow ()).Start ();
}
@ -233,5 +221,53 @@ namespace SparkleShare {
{
Program.Controller.Quit ();
}
private void UpdateFolders ()
{
int overflow_count = (Program.Controller.Folders.Count - MenuOverflowThreshold);
if (overflow_count >= MinSubmenuOverflowCount) {
Folders = Program.Controller.Folders.GetRange (0, MenuOverflowThreshold).ToArray ();
OverflowFolders = Program.Controller.Folders.GetRange (MenuOverflowThreshold, overflow_count).ToArray ();
} else {
Folders = Program.Controller.Folders.ToArray ();
OverflowFolders = new string [0];
}
string [] errors = new string [Folders.Length];
string [] overflow_errors = new string [OverflowFolders.Length];
int i = 0;
foreach (SparkleRepoBase repo in Program.Controller.Repositories) {
string error_message;
if (repo.Error == ErrorStatus.HostUnreachable) {
error_message = "Host unreachable";
} else if (repo.Error == ErrorStatus.HostIdentityChanged) {
error_message = "Host identity changed";
} else if (repo.Error == ErrorStatus.AuthenticationFailed) {
error_message = "Authentication failed";
} else if (repo.Error == ErrorStatus.DiskSpaceExcedeed) {
error_message = "Host identity changed";
} else {
error_message = "";
}
if (i > Folders.Length - 1)
overflow_errors [i] = error_message;
else
errors [i] = error_message;
i++;
}
FolderErrors = errors;
OverflowFolderErrors = overflow_errors;
}
}
}

View file

@ -47,7 +47,7 @@ namespace SparkleShare {
Height = 440;
ResizeMode = ResizeMode.NoResize;
Background = new SolidColorBrush (Colors.WhiteSmoke);
Icon = SparkleUIHelpers.GetImageSource("sparkleshare-app", "ico");
Icon = SparkleUIHelpers.GetImageSource ("sparkleshare-app", "ico");
TaskbarItemInfo = new TaskbarItemInfo () {
Description = "SparkleShare"

View file

@ -110,7 +110,7 @@ namespace SparkleShare {
});
};
Controller.UpdateOpenRecentEventsItemEvent += delegate (bool item_enabled) {
Controller.UpdateRecentEventsItemEvent += delegate (bool item_enabled) {
Dispatcher.BeginInvoke ((Action) delegate {
this.log_item.IsEnabled = item_enabled;
this.log_item.UpdateLayout ();
@ -155,11 +155,11 @@ namespace SparkleShare {
this.log_item = new SparkleMenuItem () {
Header = "Recent changes…",
IsEnabled = Controller.OpenRecentEventsItemEnabled
IsEnabled = Controller.RecentEventsItemEnabled
};
this.log_item.Click += delegate {
Controller.OpenRecentEventsClicked ();
Controller.RecentEventsClicked ();
};
SparkleMenuItem notify_item = new SparkleMenuItem () {
@ -207,6 +207,7 @@ namespace SparkleShare {
this.context_menu.Items.Add (folder_item);
if (Controller.Folders.Length > 0) {
int i = 0;
foreach (string folder_name in Controller.Folders) {
SparkleMenuItem subfolder_item = new SparkleMenuItem () {
Header = folder_name
@ -220,26 +221,35 @@ namespace SparkleShare {
Height = 16
};
if (Program.Controller.UnsyncedFolders.Contains (folder_name)) {
subfolder_item.Icon = new Image () {
Source = (BitmapSource) Imaging.CreateBitmapSourceFromHIcon (
System.Drawing.SystemIcons.Exclamation.Handle,
Int32Rect.Empty,
BitmapSizeOptions.FromWidthAndHeight (16,16)
)
};
if (!string.IsNullOrEmpty (Controller.FolderErrors [i])) {
subfolder_item.Icon = new Image () {
Source = (BitmapSource) Imaging.CreateBitmapSourceFromHIcon (
System.Drawing.SystemIcons.Exclamation.Handle, Int32Rect.Empty,
BitmapSizeOptions.FromWidthAndHeight (16,16)
)
};
SparkleMenuItem error_item = new SparkleMenuItem () {
Header = Controller.FolderErrors [i],
IsEnabled = false
};
subfolder_item.Items.Add (error_item);
} else {
subfolder_item.Icon = subfolder_image;
}
this.context_menu.Items.Add (subfolder_item);
i++;
}
SparkleMenuItem more_item = new SparkleMenuItem () {
Header = "More projects"
};
i = 0;
foreach (string folder_name in Controller.OverflowFolders) {
SparkleMenuItem subfolder_item = new SparkleMenuItem () {
Header = folder_name
@ -253,20 +263,28 @@ namespace SparkleShare {
Height = 16
};
if (Program.Controller.UnsyncedFolders.Contains (folder_name)) {
subfolder_item.Icon = new Image () {
Source = (BitmapSource) Imaging.CreateBitmapSourceFromHIcon (
System.Drawing.SystemIcons.Exclamation.Handle,
Int32Rect.Empty,
BitmapSizeOptions.FromWidthAndHeight (16,16)
)
};
if (!string.IsNullOrEmpty (Controller.OverflowFolderErrors [i])) {
subfolder_item.Icon = new Image () {
Source = (BitmapSource) Imaging.CreateBitmapSourceFromHIcon (
System.Drawing.SystemIcons.Exclamation.Handle, Int32Rect.Empty,
BitmapSizeOptions.FromWidthAndHeight (16,16)
)
};
SparkleMenuItem error_item = new SparkleMenuItem () {
Header = Controller.OverflowFolderErrors [i],
IsEnabled = false
};
subfolder_item.Items.Add (error_item);
} else {
subfolder_item.Icon = subfolder_image;
}
more_item.Items.Add (subfolder_item);
i++;
}
if (more_item.Items.Count > 0) {