diff --git a/SparkleShare/Windows/SparkleShare.csproj b/SparkleShare/Windows/SparkleShare.csproj index 4d7df5a5..352f712a 100644 --- a/SparkleShare/Windows/SparkleShare.csproj +++ b/SparkleShare/Windows/SparkleShare.csproj @@ -96,7 +96,6 @@ - @@ -104,6 +103,7 @@ + @@ -193,4 +193,4 @@ - + \ No newline at end of file diff --git a/SparkleShare/Windows/SparkleStatusIcon.cs b/SparkleShare/Windows/SparkleStatusIcon.cs new file mode 100644 index 00000000..3e393c28 --- /dev/null +++ b/SparkleShare/Windows/SparkleStatusIcon.cs @@ -0,0 +1,356 @@ +// SparkleShare, a collaboration and sharing tool. +// Copyright (C) 2010 Hylke Bons +// +// 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 System; +using System.IO; + +using SparkleLib; +using System.Windows.Forms; +using System.Drawing; + +namespace SparkleShare { + + // The statusicon that stays in the + // user's notification area + public class SparkleStatusIcon { + + private Timer Animation; + private Bitmap [] AnimationFrames; + private int FrameNumber; + private string StateText; + + private ToolStripItem status_menu_item; + private NotifyIcon status_icon; + + // Short alias for the translations + public static string _ (string s) + { + return s; + } + + + public SparkleStatusIcon () + { + AnimationFrames = CreateAnimationFrames (); + Animation = CreateAnimation (); + + this.status_icon = new NotifyIcon (); + status_icon.Text = "SparkleShare"; + status_icon.Icon = Icons.sparkleshare; + status_icon.Visible = true; + + CreateMenu (); + SetNormalState (); + + SparkleShare.Controller.FolderSizeChanged += delegate { + status_icon.ContextMenuStrip.SafeInvoke ((Action)delegate { + if (!Animation.Enabled) + SetNormalState (); + + UpdateMenu (); + }); + }; + + SparkleShare.Controller.FolderListChanged += delegate { + status_icon.ContextMenuStrip.SafeInvoke ((Action)delegate { + SetNormalState (); + CreateMenu (); + }); + }; + + SparkleShare.Controller.OnIdle += delegate { + status_icon.ContextMenuStrip.SafeInvoke ((Action)delegate { + SetNormalState (); + UpdateMenu (); + }); + }; + + SparkleShare.Controller.OnSyncing += delegate { + status_icon.ContextMenuStrip.SafeInvoke ((Action)delegate { + SetAnimationState (); + UpdateMenu (); + }); + }; + + SparkleShare.Controller.OnError += delegate { + status_icon.ContextMenuStrip.SafeInvoke ((Action)delegate { + SetNormalState (true); + UpdateMenu (); + }); + }; + } + + + // Slices up the graphic that contains the + // animation frames. + private Bitmap [] CreateAnimationFrames () + { + Bitmap [] animation_frames = new Bitmap [5]; + animation_frames [0] = Icons.process_syncing_sparkleshare_i_24; + animation_frames [1] = Icons.process_syncing_sparkleshare_ii_24; + animation_frames [2] = Icons.process_syncing_sparkleshare_iii_24; + animation_frames [3] = Icons.process_syncing_sparkleshare_iiii_24; + animation_frames [4] = Icons.process_syncing_sparkleshare_iiiii_24; + + return animation_frames; + } + + + // Creates the Animation that handles the syncing animation + private Timer CreateAnimation () + { + FrameNumber = 0; + + Timer Animation = new Timer () { + Interval = 35 + }; + + Animation.Tick += delegate { + if (FrameNumber < AnimationFrames.Length - 1) + FrameNumber++; + else + FrameNumber = 0; + + status_icon.ContextMenuStrip.SafeInvoke ((Action)delegate { + this.status_icon.Icon = Icon.FromHandle (AnimationFrames [FrameNumber].GetHicon ()); + }); + }; + + return Animation; + } + + + // Creates the menu that is popped up when the + // user clicks the status icon + public void CreateMenu () + { + ContextMenuStrip Menu = new ContextMenuStrip (); + + // The menu item showing the status and size of the SparkleShare folder + status_menu_item = new ToolStripLabel (StateText); + + Menu.Items.Add (status_menu_item); + Menu.Items.Add (new ToolStripSeparator ()); + + ToolStripMenuItem folder_item = new ToolStripMenuItem ("SparkleShare") { + Image = Icons.folder_sparkleshare_16 + }; + + folder_item.Click += delegate { + SparkleShare.Controller.OpenSparkleShareFolder (); + }; + + Menu.Items.Add (folder_item); + + if (SparkleShare.Controller.Folders.Count > 0) { + + // Creates a menu item for each repository with a link to their logs + foreach (string folder_name in SparkleShare.Controller.Folders) { + Bitmap folder_icon; + + if (SparkleShare.Controller.UnsyncedFolders.Contains (folder_name)) { + folder_icon = Icons.dialog_error_16; + } else { + folder_icon = Icons.sparkleshare_windows_status; + } + + ToolStripMenuItem subfolder_item = new ToolStripMenuItem (folder_name) { + Image = folder_icon + }; + + subfolder_item.Click += OpenFolderDelegate (folder_name); + Menu.Items.Add (subfolder_item); + } + + } else { + ToolStripMenuItem no_folders_item = new ToolStripMenuItem (_ ("No Remote Folders Yet")) { + Enabled = false + }; + + Menu.Items.Add (no_folders_item); + } + + Menu.Items.Add (new ToolStripSeparator ()); + + // Opens the wizard to add a new remote folder + ToolStripMenuItem sync_item = new ToolStripMenuItem (_ ("Add Remote Folder…")); + + if (SparkleShare.Controller.FirstRun) + sync_item.Enabled = false; + + sync_item.Click += delegate { + + if (SparkleUI.Setup == null) { + SparkleUI.Setup = new SparkleSetup (); + SparkleUI.Setup.Controller.ShowAddPage (); + } + + if (!SparkleUI.Setup.Visible) + SparkleUI.Setup.Controller.ShowAddPage (); + }; + + Menu.Items.Add (sync_item); + Menu.Items.Add (new ToolStripSeparator ()); + + ToolStripMenuItem recent_events_item = new ToolStripMenuItem (_ ("Show Recent Events")); + + if (SparkleShare.Controller.Folders.Count < 1) + recent_events_item.Enabled = false; + + recent_events_item.Click += delegate { + if (SparkleUI.EventLog == null) + SparkleUI.EventLog = new SparkleEventLog (); + + SparkleUI.EventLog.ShowAll (); + SparkleUI.EventLog.Present (); + }; + + Menu.Items.Add (recent_events_item); + + ToolStripMenuItem notify_item; + + if (SparkleShare.Controller.NotificationsEnabled) + notify_item = new ToolStripMenuItem (_ ("Turn Notifications Off")); + else + notify_item = new ToolStripMenuItem (_ ("Turn Notifications On")); + + notify_item.Click += delegate { + SparkleShare.Controller.ToggleNotifications (); + CreateMenu (); + }; + + Menu.Items.Add (notify_item); + Menu.Items.Add (new ToolStripSeparator ()); + + // A menu item that takes the user to http://www.sparkleshare.org/ + ToolStripMenuItem about_item = new ToolStripMenuItem (_ ("About SparkleShare")); + + about_item.Click += delegate { + if (SparkleUI.About == null) + SparkleUI.About = new SparkleAbout (); + + SparkleUI.About.Show (); + SparkleUI.About.Present (); + }; + + Menu.Items.Add (about_item); + Menu.Items.Add (new ToolStripSeparator ()); + + // A menu item that quits the application + ToolStripMenuItem quit_item = new ToolStripMenuItem (_ ("Quit")); + + quit_item.Click += delegate { + SparkleShare.Controller.Quit (); + }; + + Menu.Items.Add (quit_item); + + status_icon.ContextMenuStrip = Menu; + } + + + // A method reference that makes sure that opening the + // event log for each repository works correctly + private EventHandler OpenFolderDelegate (string name) + { + return delegate { + SparkleShare.Controller.OpenSparkleShareFolder (name); + }; + } + + + public void UpdateMenu () + { + status_menu_item.Text=StateText; + } + + + // The state when there's nothing going on + private void SetNormalState () + { + SetNormalState (false); + } + + + // The state when there's nothing going on + private void SetNormalState (bool error) + { + Animation.Stop (); + + if (SparkleShare.Controller.Folders.Count == 0) { + StateText = _("Welcome to SparkleShare!"); + + status_icon.ContextMenuStrip.SafeInvoke ((Action)delegate { + this.status_icon.Icon = Icon.FromHandle (AnimationFrames [0].GetHicon ()); + }); + + } else { + if (error) { + StateText = _("Not everything is synced"); + + status_icon.ContextMenuStrip.SafeInvoke ((Action)delegate { + this.status_icon.Icon = Icon.FromHandle (Icons.sparkleshare_syncing_error_24.GetHicon ()); + }); + } else { + StateText = _("Up to date") + " (" + SparkleShare.Controller.FolderSize + ")"; + status_icon.ContextMenuStrip.SafeInvoke ((Action)delegate { + this.status_icon.Icon = Icon.FromHandle (AnimationFrames [0].GetHicon ()); + }); + } + } + } + + + // The state when animating + private void SetAnimationState () + { + StateText = _("Syncing…"); + + if (!Animation.Enabled) + Animation.Start (); + } + } + + + public static class ControlExtention { + public static void SafeInvoke (this Control uiElement, Action updater, bool forceSynchronous) + { + if (uiElement == null) { + throw new ArgumentNullException ("uiElement"); + } + + if (uiElement.InvokeRequired) { + if (forceSynchronous) { + uiElement.Invoke ((Action)delegate { SafeInvoke (uiElement, updater, forceSynchronous); }); + } else { + uiElement.BeginInvoke ((Action)delegate { SafeInvoke (uiElement, updater, forceSynchronous); }); + } + } else { + if (uiElement.IsDisposed) { + throw new ObjectDisposedException ("Control is already disposed."); + } + + updater (); + } + } + public static void SafeInvoke (this Control uiElement, Action updater) + { + uiElement.SafeInvoke (updater, false); + } + + } +} diff --git a/SparkleShare/Windows/SparkleWinController.cs b/SparkleShare/Windows/SparkleWinController.cs index dafe65df..9cec97a4 100644 --- a/SparkleShare/Windows/SparkleWinController.cs +++ b/SparkleShare/Windows/SparkleWinController.cs @@ -23,6 +23,7 @@ using System.Runtime.InteropServices; using System.Text; using System.Threading; using System.Text.RegularExpressions; +using System.Windows.Forms; namespace SparkleShare { @@ -34,6 +35,9 @@ namespace SparkleShare { public override void Initialize () { + Application.EnableVisualStyles (); + Application.SetCompatibleTextRenderingDefault (false); + // Add msysgit to path, as we cannot asume it is added to the path // Asume it is installed in @"C:\msysgit\bin" for now string MSysGit=@"C:\msysgit";