SparkleShare/SparkleShare/Mac/SparkleStatusIcon.cs
2012-02-24 04:12:13 +01:00

378 lines
14 KiB
C#
Executable file

// SparkleShare, an instant update workflow to Git.
// Copyright (C) 2010 Hylke Bons <hylkebons@gmail.com>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
using System;
using System.Drawing;
using System.IO;
using System.Timers;
using Mono.Unix;
using MonoMac.Foundation;
using MonoMac.AppKit;
using MonoMac.ObjCRuntime;
namespace SparkleShare {
// The statusicon that stays in the
// user's notification area
public class SparkleStatusIcon : NSObject {
public SparkleStatusIconController Controller = new SparkleStatusIconController ();
// TODO: Fix case
private Timer Animation;
private int FrameNumber;
private string StateText;
private NSStatusItem StatusItem;
private NSMenu Menu;
private NSMenuItem StateMenuItem;
private NSMenuItem FolderMenuItem;
private NSMenuItem [] FolderMenuItems;
private NSMenuItem SyncMenuItem;
private NSMenuItem AboutMenuItem;
private NSMenuItem NotificationsMenuItem;
private NSMenuItem RecentEventsMenuItem;
private NSMenuItem QuitMenuItem;
private NSImage [] AnimationFrames;
private NSImage [] AnimationFramesActive;
private NSImage ErrorImage;
private NSImage ErrorImageActive;
private NSImage FolderImage;
private NSImage CautionImage;
private NSImage SparkleShareImage;
private delegate void Task ();
private EventHandler [] Tasks;
// Short alias for the translations
public static string _ (string s)
{
return Catalog.GetString (s);
}
public SparkleStatusIcon () : base ()
{
using (var a = new NSAutoreleasePool ())
{
ErrorImage = new NSImage (NSBundle.MainBundle.ResourcePath + "/Pixmaps/sparkleshare-syncing-error-mac.png");
ErrorImageActive = new NSImage (NSBundle.MainBundle.ResourcePath + "/Pixmaps/sparkleshare-syncing-error-mac-active.png");
FolderImage = NSImage.ImageNamed ("NSFolder");
CautionImage = NSImage.ImageNamed ("NSCaution");
SparkleShareImage = NSImage.ImageNamed ("sparkleshare-mac");
Animation = CreateAnimation ();
StatusItem = NSStatusBar.SystemStatusBar.CreateStatusItem (28);
StatusItem.HighlightMode = true;
if (Controller.Folders.Length == 0)
StateText = _("Welcome to SparkleShare!");
else
StateText = _("Files up to date") + Controller.FolderSize;
CreateMenu ();
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) {
InvokeOnMainThread (delegate {
using (var a = new NSAutoreleasePool ()) {
switch (state) {
case IconState.Idle:
Animation.Stop ();
if (Controller.Folders.Length == 0)
StateText = _("Welcome to SparkleShare!");
else
StateText = _("Files up to date") + Controller.FolderSize;
StateMenuItem.Title = StateText;
CreateMenu ();
break;
case IconState.Syncing:
StateText = _("Syncing… ") +
Controller.ProgressPercentage + "% " +
Controller.ProgressSpeed;
StateMenuItem.Title = StateText;
if (!Animation.Enabled)
Animation.Start ();
break;
case IconState.Error:
Animation.Stop ();
StateText = _("Not everything is synced");
StateMenuItem.Title = StateText;
CreateMenu ();
StatusItem.Image = ErrorImage;
StatusItem.AlternateImage = ErrorImageActive;
StatusItem.Image.Size = new SizeF (16, 16);
StatusItem.AlternateImage.Size = new SizeF (16, 16);
break;
}
StatusItem.Menu.Update ();
}
});
};
}
public void CreateMenu ()
{
using (NSAutoreleasePool a = new NSAutoreleasePool ())
{
StatusItem.Image = AnimationFrames [0];
StatusItem.AlternateImage = AnimationFramesActive [0];
StatusItem.Image.Size = new SizeF (16, 16);
StatusItem.AlternateImage.Size = new SizeF (16, 16);
Menu = new NSMenu ();
Menu.AutoEnablesItems = false;
StateMenuItem = new NSMenuItem () {
Title = StateText,
Enabled = false
};
Menu.AddItem (StateMenuItem);
Menu.AddItem (NSMenuItem.SeparatorItem);
FolderMenuItem = new NSMenuItem () {
Title = "SparkleShare"
};
FolderMenuItem.Activated += delegate {
Controller.SparkleShareClicked ();
};
FolderMenuItem.Image = SparkleShareImage;
FolderMenuItem.Image.Size = new SizeF (16, 16);
FolderMenuItem.Enabled = true;
Menu.AddItem (FolderMenuItem);
FolderMenuItems = new NSMenuItem [Program.Controller.Folders.Count];
if (Controller.Folders.Length > 0) {
Tasks = new EventHandler [Program.Controller.Folders.Count];
int i = 0;
foreach (string folder_name in Program.Controller.Folders) {
NSMenuItem item = new NSMenuItem ();
item.Title = folder_name;
if (Program.Controller.UnsyncedFolders.Contains (folder_name))
item.Image = CautionImage;
else
item.Image = FolderImage;
item.Image.Size = new SizeF (16, 16);
Tasks [i] = OpenFolderDelegate (folder_name);
FolderMenuItems [i] = item;
FolderMenuItems [i].Activated += Tasks [i];
FolderMenuItem.Enabled = true;
i++;
};
} else {
FolderMenuItems = new NSMenuItem [1];
FolderMenuItems [0] = new NSMenuItem () {
Title = "No projects yet",
Enabled = false
};
}
foreach (NSMenuItem item in FolderMenuItems)
Menu.AddItem (item);
Menu.AddItem (NSMenuItem.SeparatorItem);
SyncMenuItem = new NSMenuItem () {
Title = "Add Hosted Project…",
Enabled = true
};
SyncMenuItem.Activated += delegate {
Controller.AddHostedProjectClicked ();
};
Menu.AddItem (SyncMenuItem);
Menu.AddItem (NSMenuItem.SeparatorItem);
RecentEventsMenuItem = new NSMenuItem () {
Title = "View Recent Changes…",
Enabled = (Controller.Folders.Length > 0)
};
if (Controller.Folders.Length > 0) {
RecentEventsMenuItem.Activated += delegate {
Controller.OpenRecentEventsClicked ();
};
}
Menu.AddItem (RecentEventsMenuItem);
NotificationsMenuItem = new NSMenuItem () {
Enabled = true
};
if (Program.Controller.NotificationsEnabled)
NotificationsMenuItem.Title = "Turn Notifications Off";
else
NotificationsMenuItem.Title = "Turn Notifications On";
NotificationsMenuItem.Activated += delegate {
Program.Controller.ToggleNotifications ();
InvokeOnMainThread (delegate {
if (Program.Controller.NotificationsEnabled)
NotificationsMenuItem.Title = "Turn Notifications Off";
else
NotificationsMenuItem.Title = "Turn Notifications On";
});
};
Menu.AddItem (NotificationsMenuItem);
Menu.AddItem (NSMenuItem.SeparatorItem);
AboutMenuItem = new NSMenuItem () {
Title = "About SparkleShare",
Enabled = true
};
AboutMenuItem.Activated += delegate {
Controller.AboutClicked ();
};
Menu.AddItem (AboutMenuItem);
Menu.AddItem (NSMenuItem.SeparatorItem);
QuitMenuItem = new NSMenuItem () {
Title = "Quit",
Enabled = Controller.QuitItemEnabled
};
QuitMenuItem.Activated += delegate {
Program.Controller.Quit ();
};
Menu.AddItem (QuitMenuItem);
StatusItem.Menu = Menu;
StatusItem.Menu.Update ();
}
}
// A method reference that makes sure that opening the
// event log for each repository works correctly
private EventHandler OpenFolderDelegate (string name)
{
return delegate {
Controller.SubfolderClicked (name);
};
}
// Creates the Animation that handles the syncing animation
private Timer CreateAnimation ()
{
FrameNumber = 0;
AnimationFrames = new NSImage [] {
new NSImage (Path.Combine (NSBundle.MainBundle.ResourcePath, "Pixmaps", "process-syncing-sparkleshare-mac-i.png")),
new NSImage (Path.Combine (NSBundle.MainBundle.ResourcePath, "Pixmaps", "process-syncing-sparkleshare-mac-ii.png")),
new NSImage (Path.Combine (NSBundle.MainBundle.ResourcePath, "Pixmaps", "process-syncing-sparkleshare-mac-iii.png")),
new NSImage (Path.Combine (NSBundle.MainBundle.ResourcePath, "Pixmaps", "process-syncing-sparkleshare-mac-iiii.png")),
new NSImage (Path.Combine (NSBundle.MainBundle.ResourcePath, "Pixmaps", "process-syncing-sparkleshare-mac-iiiii.png"))
};
AnimationFramesActive = new NSImage [] {
new NSImage (Path.Combine (NSBundle.MainBundle.ResourcePath, "Pixmaps", "process-syncing-sparkleshare-mac-i-active.png")),
new NSImage (Path.Combine (NSBundle.MainBundle.ResourcePath, "Pixmaps", "process-syncing-sparkleshare-mac-ii-active.png")),
new NSImage (Path.Combine (NSBundle.MainBundle.ResourcePath, "Pixmaps", "process-syncing-sparkleshare-mac-iii-active.png")),
new NSImage (Path.Combine (NSBundle.MainBundle.ResourcePath, "Pixmaps", "process-syncing-sparkleshare-mac-iiii-active.png")),
new NSImage (Path.Combine (NSBundle.MainBundle.ResourcePath, "Pixmaps", "process-syncing-sparkleshare-mac-iiiii-active.png"))
};
Timer Animation = new Timer () {
Interval = 40
};
Animation.Elapsed += delegate {
if (FrameNumber < 4)
FrameNumber++;
else
FrameNumber = 0;
InvokeOnMainThread (delegate {
StatusItem.Image = AnimationFrames [FrameNumber];
StatusItem.AlternateImage = AnimationFramesActive [FrameNumber];
StatusItem.Image.Size = new SizeF (16, 16);
StatusItem.AlternateImage.Size = new SizeF (16, 16);
});
};
return Animation;
}
}
public class SparkleStatusIconMenuDelegate : NSMenuDelegate {
public override void MenuWillHighlightItem (NSMenu menu, NSMenuItem item) { }
public override void MenuWillOpen (NSMenu menu)
{
InvokeOnMainThread (delegate {
NSApplication.SharedApplication.DockTile.BadgeLabel = null;
});
}
}
}