// 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 System.Timers; #if HAVE_APP_INDICATOR using AppIndicator; #endif using Gtk; using Mono.Unix; namespace SparkleShare { // The statusicon that stays in the // user's notification area public class SparkleStatusIcon { private Timer Animation; private Gdk.Pixbuf [] AnimationFrames; private int FrameNumber; private string StateText; private Menu Menu; #if HAVE_APP_INDICATOR private ApplicationIndicator indicator; #else private StatusIcon status_icon; #endif // Short alias for the translations public static string _ (string s) { return Catalog.GetString (s); } public SparkleStatusIcon () { AnimationFrames = CreateAnimationFrames (); Animation = CreateAnimation (); #if HAVE_APP_INDICATOR this.indicator = new ApplicationIndicator ("sparkleshare", "process-syncing-sparkleshare-i", Category.ApplicationStatus) { Status = Status.Attention }; #else this.status_icon = new StatusIcon (); this.status_icon.Activate += ShowMenu; // Primary mouse button click this.status_icon.PopupMenu += ShowMenu; // Secondary mouse button click #endif SetNormalState (); CreateMenu (); SparkleShare.Controller.FolderSizeChanged += delegate { Application.Invoke (delegate { if (!Animation.Enabled) SetNormalState (); UpdateMenu (); }); }; SparkleShare.Controller.FolderListChanged += delegate { Application.Invoke (delegate { SetNormalState (); CreateMenu (); }); }; SparkleShare.Controller.OnIdle += delegate { Application.Invoke (delegate { SetNormalState (); UpdateMenu (); }); }; SparkleShare.Controller.OnSyncing += delegate { Application.Invoke (delegate { SetAnimationState (); UpdateMenu (); }); }; SparkleShare.Controller.OnError += delegate { Application.Invoke (delegate { SetNormalState (true); UpdateMenu (); }); }; } // Slices up the graphic that contains the // animation frames. private Gdk.Pixbuf [] CreateAnimationFrames () { Gdk.Pixbuf [] animation_frames = new Gdk.Pixbuf [5]; Gdk.Pixbuf frames_pixbuf = SparkleUIHelpers.GetIcon ("process-syncing-sparkleshare", 24); for (int i = 0; i < animation_frames.Length; i++) animation_frames [i] = new Gdk.Pixbuf (frames_pixbuf, (i * 24), 0, 24, 24); return animation_frames; } // Creates the Animation that handles the syncing animation private Timer CreateAnimation () { FrameNumber = 0; Timer Animation = new Timer () { Interval = 35 }; Animation.Elapsed += delegate { if (FrameNumber < AnimationFrames.Length - 1) FrameNumber++; else FrameNumber = 0; string icon_name = "process-syncing-sparkleshare-"; for (int i = 0; i <= FrameNumber; i++) icon_name += "i"; Application.Invoke (delegate { #if HAVE_APP_INDICATOR this.indicator.IconName = icon_name; #else this.status_icon.Pixbuf = SparkleUIHelpers.GetIcon (icon_name, 24); #endif }); }; return Animation; } // Creates the menu that is popped up when the // user clicks the status icon public void CreateMenu () { Menu = new Menu (); // The menu item showing the status and size of the SparkleShare folder MenuItem status_menu_item = new MenuItem (StateText) { Sensitive = false }; Menu.Add (status_menu_item); Menu.Add (new SeparatorMenuItem ()); ImageMenuItem folder_item = new SparkleMenuItem ("SparkleShare"){ Image = new Image (SparkleUIHelpers.GetIcon ("folder-sparkleshare", 16)) }; folder_item.Activated += delegate { SparkleShare.Controller.OpenSparkleShareFolder (); }; Menu.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) { Gdk.Pixbuf folder_icon; if (SparkleShare.Controller.UnsyncedFolders.Contains (folder_name)) { folder_icon = IconTheme.Default.LoadIcon ("dialog-error", 16, IconLookupFlags.GenericFallback); } else { folder_icon = IconTheme.Default.LoadIcon ("folder", 16, IconLookupFlags.GenericFallback); } ImageMenuItem subfolder_item = new SparkleMenuItem (folder_name) { Image = new Image (folder_icon) }; subfolder_item.Activated += OpenFolderDelegate (folder_name); Menu.Add (subfolder_item); } } else { MenuItem no_folders_item = new MenuItem (_("No Remote Folders Yet")) { Sensitive = false }; Menu.Add (no_folders_item); } Menu.Add (new SeparatorMenuItem ()); // Opens the wizard to add a new remote folder MenuItem sync_item = new MenuItem (_("Add Remote Folder…")); if (SparkleShare.Controller.FirstRun) sync_item.Sensitive = false; sync_item.Activated += delegate { Application.Invoke (delegate { if (SparkleUI.Intro == null) { SparkleUI.Intro = new SparkleIntro (); SparkleUI.Intro.ShowServerForm (true); } if (!SparkleUI.Intro.Visible) SparkleUI.Intro.ShowServerForm (true); SparkleUI.Intro.ShowAll (); SparkleUI.Intro.Present (); }); }; Menu.Add (sync_item); Menu.Add (new SeparatorMenuItem ()); MenuItem recent_events_item = new MenuItem (_("Show Recent Events")); if (SparkleShare.Controller.Folders.Count < 1) recent_events_item.Sensitive = false; recent_events_item.Activated += delegate { Application.Invoke (delegate { if (SparkleUI.EventLog == null) SparkleUI.EventLog = new SparkleEventLog (); SparkleUI.EventLog.ShowAll (); SparkleUI.EventLog.Present (); }); }; Menu.Add (recent_events_item); MenuItem notify_item; if (SparkleShare.Controller.NotificationsEnabled) notify_item = new MenuItem (_("Turn Notifications Off")); else notify_item = new MenuItem (_("Turn Notifications On")); notify_item.Activated += delegate { SparkleShare.Controller.ToggleNotifications (); CreateMenu (); }; Menu.Add (notify_item); Menu.Add (new SeparatorMenuItem ()); // A menu item that takes the user to http://www.sparkleshare.org/ MenuItem about_item = new MenuItem (_("About SparkleShare")); about_item.Activated += delegate { SparkleAbout about = new SparkleAbout (); about.ShowAll (); }; Menu.Add (about_item); Menu.Add (new SeparatorMenuItem ()); // A menu item that quits the application MenuItem quit_item = new MenuItem (_("Quit")); quit_item.Activated += delegate { SparkleShare.Controller.Quit (); }; Menu.Add (quit_item); Menu.ShowAll (); #if HAVE_APP_INDICATOR this.indicator.Menu = Menu; #endif } // 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 () { ((Menu.Children [0] as MenuItem).Child as Label).Text = StateText; Menu.ShowAll (); } #if !HAVE_APP_INDICATOR // Makes the menu visible private void ShowMenu (object o, EventArgs args) { Menu.Popup (null, null, SetPosition, 0, Global.CurrentEventTime); } // Makes sure the menu pops up in the right position private void SetPosition (Menu menu, out int x, out int y, out bool push_in) { StatusIcon.PositionMenu (menu, out x, out y, out push_in, this.status_icon.Handle); } #endif // 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!"); Application.Invoke (delegate { #if HAVE_APP_INDICATOR this.indicator.IconName = "process-syncing-sparkleshare-i"; #else this.status_icon.Pixbuf = AnimationFrames [0]; #endif }); } else { if (error) { StateText = _("Not everything is synced"); Application.Invoke (delegate { #if HAVE_APP_INDICATOR this.indicator.IconName = "sparkleshare-syncing-error"; #else this.status_icon.Pixbuf = SparkleUIHelpers.GetIcon ("sparkleshare-syncing-error", 24); #endif }); } else { StateText = _("Up to date") + " (" + SparkleShare.Controller.FolderSize + ")"; Application.Invoke (delegate { #if HAVE_APP_INDICATOR this.indicator.IconName = "process-syncing-sparkleshare-i"; #else this.status_icon.Pixbuf = AnimationFrames [0]; #endif }); } } } // The state when animating private void SetAnimationState () { StateText = _("Syncing…"); if (!Animation.Enabled) Animation.Start (); } } public class SparkleMenuItem : ImageMenuItem { public SparkleMenuItem (string text) : base (text) { SetProperty ("always-show-image", new GLib.Value (true)); } } }