mac statusicon: Implement per repo status and pausing

This commit is contained in:
Hylke Bons 2014-10-30 19:40:53 +00:00
parent b6ea49f059
commit e04d2957f8
2 changed files with 141 additions and 80 deletions

View file

@ -16,6 +16,7 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
@ -35,7 +36,8 @@ namespace SparkleShare {
private NSMenuItem state_item, folder_item, add_item, about_item, recent_events_item, quit_item,
code_item, copy_item, link_code_item;
private NSMenuItem [] folder_menu_items, error_menu_items, try_again_menu_items;
private NSMenuItem [] folder_menu_items, try_again_menu_items, pause_menu_items,
resume_menu_items, state_menu_items;
private NSImage syncing_idle_image = NSImage.ImageNamed ("process-syncing-idle");
private NSImage syncing_up_image = NSImage.ImageNamed ("process-syncing-up");
@ -95,7 +97,15 @@ namespace SparkleShare {
};
Controller.UpdateStatusItemEvent += delegate (string state_text) {
Program.Controller.Invoke (() => { this.state_item.Title = state_text; });
Program.Controller.Invoke (() => {
this.state_item.Title = state_text;
if (Controller.Projects.Length == this.state_menu_items.Length) {
for (int i = 0; i < Controller.Projects.Length; i++)
this.state_menu_items [i].Title = Controller.Projects [i].StatusMessage;
}
});
};
Controller.UpdateMenuEvent += delegate {
@ -168,36 +178,63 @@ namespace SparkleShare {
Enabled = Controller.QuitItemEnabled
};
this.folder_menu_items = new NSMenuItem [Controller.Folders.Length];
this.error_menu_items = new NSMenuItem [Controller.Folders.Length];
this.try_again_menu_items = new NSMenuItem [Controller.Folders.Length];
this.folder_menu_items = new NSMenuItem [Controller.Projects.Length];
this.try_again_menu_items = new NSMenuItem [Controller.Projects.Length];
this.pause_menu_items = new NSMenuItem [Controller.Projects.Length];
this.resume_menu_items = new NSMenuItem [Controller.Projects.Length];
this.state_menu_items = new NSMenuItem [Controller.Projects.Length];
if (Controller.Folders.Length > 0) {
if (Controller.Projects.Length > 0) {
int i = 0;
foreach (string folder_name in Controller.Folders) {
NSMenuItem item = new NSMenuItem () { Title = folder_name };
foreach (ProjectInfo project in Controller.Projects) {
NSMenuItem item = new NSMenuItem () { Title = project.Name };
this.folder_menu_items [i] = item;
if (!string.IsNullOrEmpty (Controller.FolderErrors [i])) {
item.Image = this.caution_image;
item.Submenu = new NSMenu ();
item.Submenu = new NSMenu ();
item.Image = this.folder_image;
this.error_menu_items [i] = new NSMenuItem ();
this.error_menu_items [i].Title = Controller.FolderErrors [i];
this.state_menu_items [i] = new NSMenuItem (project.StatusMessage);
this.try_again_menu_items [i] = new NSMenuItem ();
this.try_again_menu_items [i].Title = "Try Again";
this.try_again_menu_items [i].Activated += Controller.TryAgainDelegate (folder_name);;
item.Submenu.AddItem (this.state_menu_items [i]);
item.Submenu.AddItem (NSMenuItem.SeparatorItem);
item.Submenu.AddItem (this.error_menu_items [i]);
item.Submenu.AddItem (NSMenuItem.SeparatorItem);
item.Submenu.AddItem (this.try_again_menu_items [i]);
if (project.IsPaused) {
if (project.UnsyncedChangesInfo.Count > 0) {
foreach (KeyValuePair<string, string> pair in project.UnsyncedChangesInfo)
item.Submenu.AddItem (new NSMenuItem (pair.Key) {
Image = NSImage.ImageNamed (pair.Value)
});
item.Submenu.AddItem (NSMenuItem.SeparatorItem);
this.resume_menu_items [i] = new NSMenuItem ("Sync and Resume…");
} else {
this.resume_menu_items [i] = new NSMenuItem ("Resume");
}
this.resume_menu_items [i].Activated += Controller.ResumeDelegate (project.Name);
item.Submenu.AddItem (this.resume_menu_items [i]);
} else {
item.Image = this.folder_image;
this.folder_menu_items [i].Activated += Controller.OpenFolderDelegate (folder_name);
if (Controller.Projects [i].HasError) {
item.Image = this.caution_image;
this.try_again_menu_items [i] = new NSMenuItem ();
this.try_again_menu_items [i].Title = "Try Again";
this.try_again_menu_items [i].Activated += Controller.TryAgainDelegate (project.Name);
item.Submenu.AddItem (this.try_again_menu_items [i]);
} else {
this.pause_menu_items [i] = new NSMenuItem ("Pause");
this.pause_menu_items [i].Activated += Controller.PauseDelegate (project.Name);
item.Submenu.AddItem (this.pause_menu_items [i]);
}
}
if (!Controller.Projects [i].HasError)
this.folder_menu_items [i].Activated += Controller.OpenFolderDelegate (project.Name);
item.Image.Size = new SizeF (16, 16);
i++;
};
@ -263,6 +300,5 @@ namespace SparkleShare {
MenuIsOpen = false;
}
}
}
}

View file

@ -46,24 +46,35 @@ namespace SparkleShare {
public string StatusMessage {
get {
string status_message = "Last sync at <time>";
if (IsPaused) {
status_message = "Paused";
string status_message = "";
} else if (this.repo.Status == SyncStatus.Error) {
if (this.repo.Status == SyncStatus.SyncUp)
status_message = "Sending changes… " + this.repo.ProgressPercentage + "%";
if (this.repo.Status == SyncStatus.SyncDown)
status_message = "Receiving changes… " + this.repo.ProgressPercentage + "%";
if (this.repo.Status == SyncStatus.SyncUp || this.repo.Status == SyncStatus.SyncDown) {
if (this.repo.ProgressSpeed > 0)
status_message += " " + this.repo.ProgressSpeed.ToSize () + "/s";
}
if (IsPaused) {
return "Paused";
} else if (HasError) {
switch (this.repo.Error) {
case ErrorStatus.HostUnreachable: return "Can't reach the host";
case ErrorStatus.HostIdentityChanged: return "The host's identity has changed";
case ErrorStatus.HostUnreachable: return "Cant reach the host";
case ErrorStatus.HostIdentityChanged: return "The hosts identity has changed";
case ErrorStatus.AuthenticationFailed: return "Authentication failed";
case ErrorStatus.DiskSpaceExceeded: return "Host is out of disk space";
case ErrorStatus.UnreadableFiles: return "Some local files are unreadable or in use";
case ErrorStatus.NotFound: return "Project doesn't exist on host";
case ErrorStatus.NotFound: return "Project doesnt exist on host";
case ErrorStatus.IncompatibleClientServer: return "Incompatible client/server versions";
}
}
return status_message;
return string.Format ("Synced {0}", this.repo.LastSync.ToPrettyDate ());
}
}
@ -113,20 +124,6 @@ namespace SparkleShare {
public ProjectInfo [] Projects = new ProjectInfo [0];
public string FolderSize {
get {
double size = 0;
foreach (SparkleRepoBase repo in Program.Controller.Repositories)
size += repo.Size;
if (size == 0)
return "";
else
return "— " + size.ToSize ();
}
}
public int ProgressPercentage {
get {
return (int) Program.Controller.ProgressPercentage;
@ -184,7 +181,7 @@ namespace SparkleShare {
if (Projects.Length == 0)
StateText = "Welcome to SparkleShare!";
else
StateText = "Projects up to date " + FolderSize;
StateText = "Projects up to date";
}
UpdateFolders ();
@ -200,7 +197,7 @@ namespace SparkleShare {
if (Projects.Length == 0)
StateText = "Welcome to SparkleShare!";
else
StateText = "Projects up to date " + FolderSize;
StateText = "Projects up to date";
}
UpdateFolders ();
@ -247,7 +244,7 @@ namespace SparkleShare {
Program.Controller.OnError += delegate {
CurrentState = IconState.Error;
StateText = "Failed to send some changes";
StateText = "Some changes werent synced";
UpdateFolders ();
@ -257,7 +254,7 @@ namespace SparkleShare {
};
// FIXME: Hack to work around a race condition causing
// FIXME: Work around a race condition causing
// the icon to not always show the right state
Timers.Timer timer = new Timers.Timer () { Interval = 30 * 1000 };
@ -270,32 +267,7 @@ namespace SparkleShare {
}
public void SubfolderClicked (string subfolder)
{
Program.Controller.OpenSparkleShareFolder (subfolder);
}
public void TryAgainClicked (string subfolder)
{
foreach (SparkleRepoBase repo in Program.Controller.Repositories)
if (repo.Name.Equals (subfolder))
new Thread (() => repo.ForceRetry ()).Start ();
}
public EventHandler OpenFolderDelegate (string subfolder)
{
return delegate { SubfolderClicked (subfolder); };
}
public EventHandler TryAgainDelegate (string subfolder)
{
return delegate { TryAgainClicked (subfolder); };
}
// Main menu items
public void RecentEventsClicked ()
{
new Thread (() => {
@ -307,31 +279,74 @@ namespace SparkleShare {
}).Start ();
}
public void AddHostedProjectClicked ()
{
new Thread (() => Program.Controller.ShowSetupWindow (PageType.Add)).Start ();
}
public void CopyToClipboardClicked ()
{
Program.Controller.CopyToClipboard (Program.Controller.CurrentUser.PublicKey);
}
public void AboutClicked ()
{
Program.Controller.ShowAboutWindow ();
}
public void QuitClicked ()
{
Program.Controller.Quit ();
}
// Project items
public void ProjectClicked (string project)
{
Program.Controller.OpenSparkleShareFolder (project);
}
public void PauseClicked (string project)
{
GetRepoByName (project).Pause ();
UpdateMenuEvent (CurrentState);
}
public void ResumeClicked (string project)
{
GetRepoByName (project).Resume ("");
UpdateMenuEvent (CurrentState);
TryAgainClicked (project);
}
public void TryAgainClicked (string project)
{
new Thread (() => GetRepoByName (project).ForceRetry ()).Start ();
}
// Helper delegates
public EventHandler OpenFolderDelegate (string project)
{
return delegate { ProjectClicked (project); };
}
public EventHandler TryAgainDelegate (string project)
{
return delegate { TryAgainClicked (project); };
}
public EventHandler PauseDelegate (string project)
{
return delegate { PauseClicked (project); };
}
public EventHandler ResumeDelegate (string project)
{
return delegate { ResumeClicked (project); };
}
private Object projects_lock = new Object ();
private void UpdateFolders ()
@ -345,5 +360,15 @@ namespace SparkleShare {
Projects = projects.ToArray ();
}
}
private SparkleRepoBase GetRepoByName (string name)
{
foreach (SparkleRepoBase repo in Program.Controller.Repositories)
if (repo.Name.Equals (name))
return repo;
return null;
}
}
}