[tasque/transition: 94/213] Outsource tray logic from Application.cs



commit 65346c9dbc9445521547894cdd0dde9803c4ff8b
Author: Antonius Riha <antoniusriha gmail com>
Date:   Mon Jul 30 16:15:10 2012 +0200

    Outsource tray logic from Application.cs
    
    * AppIndicatorTray.cs: Tray icon for Ubuntu's Unity desktop. This
    should fix the bug, that the tray icon isn't displayed in Unity.
    * StatusIconTray.cs: Fallback systray for non-Unity desktops
    * GtkTray.cs: Common base class of the above two.
    
    Note: AppIndicator tray and StatusIcon tray work a little different:
    Toggling the main window in AppIndicator is done via a menu item
    (due to a limitation of AppIndicator API),
    whereas StatusIcon toggles via clicking the icon.

 INSTALL                            |   13 ++
 build/build.csproj                 |    3 +
 src/Tasque.Gtk/AppIndicatorTray.cs |   51 +++++++
 src/Tasque.Gtk/Application.cs      |  287 ++++++------------------------------
 src/Tasque.Gtk/GtkTray.cs          |  225 ++++++++++++++++++++++++++++
 src/Tasque.Gtk/StatusIconTray.cs   |   51 +++++++
 src/Tasque.Gtk/TaskWindow.cs       |    1 +
 src/Tasque.Gtk/Tasque.Gtk.csproj   |   13 ++-
 8 files changed, 404 insertions(+), 240 deletions(-)
---
diff --git a/INSTALL b/INSTALL
index 7d7c28d..6c0e146 100644
--- a/INSTALL
+++ b/INSTALL
@@ -15,6 +15,19 @@ opening the solution in Monodevelop. Otherwise Monodevelop will issue
 warnings about some missing projects. Those projects are fetched during build.
 
 
+AppIndicator:
+For the systray icon to work on the Unity desktop appindicator-sharp is needed.
+By default appindicator support is enabled. It can be switched off by adding
+
+	/property:EnableAppIndicator=false
+
+to the command line, thus yielding
+
+	xbuild /property:Configuration=GtkLinuxDebug /property:EnableAppIndicator=false tasque.sln
+
+as the build command.
+
+
 Instructions below are deprecated.
 --------------------------------------------------------
 
diff --git a/build/build.csproj b/build/build.csproj
index 544dac4..27e83ef 100644
--- a/build/build.csproj
+++ b/build/build.csproj
@@ -137,6 +137,9 @@
     <None Include="..\src\tasque\tasque.csproj">
       <Link>tasque.csproj</Link>
     </None>
+    <None Include="..\INSTALL">
+      <Link>INSTALL</Link>
+    </None>
   </ItemGroup>
   <ItemGroup>
     <Compile Include="SetDataAtBuildTime.cs" />
diff --git a/src/Tasque.Gtk/AppIndicatorTray.cs b/src/Tasque.Gtk/AppIndicatorTray.cs
new file mode 100644
index 0000000..0be991f
--- /dev/null
+++ b/src/Tasque.Gtk/AppIndicatorTray.cs
@@ -0,0 +1,51 @@
+// 
+// AppIndicatorTray.cs
+//  
+// Author:
+//       Antonius Riha <antoniusriha gmail com>
+// 
+// Copyright (c) 2012 Antonius Riha
+// 
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+// 
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+using Gtk;
+using AppIndicator;
+
+namespace Tasque
+{
+	public class AppIndicatorTray : GtkTray
+	{
+		public AppIndicatorTray ()
+		{
+			appIndicator = new ApplicationIndicator ("TasqueTray", IconName,
+			                                         AppIndicator.Category.ApplicationStatus);
+			appIndicator.Status = Status.Active;
+
+			var menu = Menu;
+			var toggleTaskWindowMenuItem = new MenuItem ();
+			ToggleTaskWindowAction.ConnectProxy (toggleTaskWindowMenuItem);
+			menu.Insert (toggleTaskWindowMenuItem, 0);
+			menu.Insert (new SeparatorMenuItem (), 1);
+			menu.ShowAll ();
+			
+			appIndicator.Menu = menu;
+		}
+
+		ApplicationIndicator appIndicator;
+	}
+}
diff --git a/src/Tasque.Gtk/Application.cs b/src/Tasque.Gtk/Application.cs
index d7d3789..4894fc3 100644
--- a/src/Tasque.Gtk/Application.cs
+++ b/src/Tasque.Gtk/Application.cs
@@ -40,7 +40,6 @@ using Gtk;
 using Gdk;
 using Mono.Unix;
 using Mono.Unix.Native;
-using Tasque.UIModel.Legacy;
 using System.Diagnostics;
 
 
@@ -56,10 +55,9 @@ namespace Tasque
 		private static System.Object locker = new System.Object();
 		private NativeApplication nativeApp;
 
-		private Gtk.StatusIcon trayIcon;	
+		private GtkTray trayIcon;	
 		private Preferences preferences;
 		private Backend backend;
-		private TaskGroupModel overdue_tasks, today_tasks, tomorrow_tasks;
 		private PreferencesDialog preferencesDialog;
 		private bool quietStart = false;
 		
@@ -73,22 +71,6 @@ namespace Tasque
 		
 		private Backend customBackend;
 
-		private UIManager uiManager;
-		private const string menuXml = @"
-<ui>
-	<popup name=""TrayIconMenu"">
-		<menuitem action=""NewTaskAction""/>
-		<separator/>
-		<menuitem action=""PreferencesAction""/>
-		<menuitem action=""AboutAction""/>
-		<separator/>
-		<menuitem action=""RefreshAction""/>
-		<separator/>
-		<menuitem action=""QuitAction""/>
-	</popup>
-</ui>
-";
-
 		public static Backend Backend
 		{ 
 			get { return Application.Instance.backend; }
@@ -125,16 +107,20 @@ namespace Tasque
 			}
 		}
 
-		public UIManager UIManager
-		{
-			get { return uiManager; }
-		}
+		public event EventHandler BackendChanged;
 
-		public StatusIcon Tray
+		void OnBackendChanged ()
 		{
-			get { return trayIcon; }
+			if (BackendChanged != null)
+				BackendChanged (this, EventArgs.Empty);
 		}
-
+		
+		public TaskGroupModel OverdueTasks { get; private set; }
+		
+		public TaskGroupModel TodayTasks { get; private set; }
+		
+		public TaskGroupModel TomorrowTasks { get; private set; }
+		
 		public static Preferences Preferences
 		{
 			get { return Application.Instance.preferences; }
@@ -155,13 +141,9 @@ namespace Tasque
 			nativeApp = new GnomeApplication ();
 #endif
 			nativeApp.Initialize (args);
-			
-			RegisterUIManager ();
 
 			preferences = new Preferences (nativeApp.ConfDir);
 			
-
-			
 			string potentialBackendClassName = null;
 			
 			for (int i = 0; i < args.Length; i++) {
@@ -320,9 +302,11 @@ namespace Tasque
 			}
 				
 			// Initialize the new backend
+			var oldBackend = backend;
 			this.backend = value;
 			if (this.backend == null) {
-				RefreshTrayIconTooltip ();
+				if (trayIcon != null)
+					trayIcon.RefreshTrayIconTooltip ();
 				return;
 			}
 				
@@ -338,10 +322,14 @@ namespace Tasque
 			}
 
 			RebuildTooltipTaskGroupModels ();
-			RefreshTrayIconTooltip ();
+			if (trayIcon != null)
+				trayIcon.RefreshTrayIconTooltip ();
 			
 			Debug.WriteLine("Configuration status: {0}",
 			             this.backend.Configured.ToString());
+
+			if (backend != oldBackend)
+				OnBackendChanged ();
 		}
 
 		private bool InitializeIdle()
@@ -360,7 +348,7 @@ namespace Tasque
 				}
 			}
 			
-			SetupTrayIcon ();
+			trayIcon = GtkTray.CreateTray ();
 			
 			if (backend == null) {
 				// Pop open the preferences dialog so the user can choose a
@@ -404,30 +392,16 @@ namespace Tasque
 				
 				UnhookFromTooltipTaskGroupModels ();
 				RebuildTooltipTaskGroupModels ();
-				RefreshTrayIconTooltip ();
+				if (trayIcon != null)
+					trayIcon.RefreshTrayIconTooltip ();
 			}
 			
 			return true;
 		}
 
-		private void SetupTrayIcon ()
-		{
-			trayIcon = new Gtk.StatusIcon();
-			trayIcon.Pixbuf = Utilities.GetIcon ("tasque-24", 24);
-			
-			// hooking event
-			trayIcon.Activate += OnTrayIconClick;
-			trayIcon.PopupMenu += OnTrayIconPopupMenu;
-
-			// showing the trayicon
-			trayIcon.Visible = true;
-
-			RefreshTrayIconTooltip ();
-		}
-
 		void UnhookFromTooltipTaskGroupModels ()
 		{
-			foreach (TaskGroupModel model in new TaskGroupModel[] { overdue_tasks, today_tasks, tomorrow_tasks })
+			foreach (TaskGroupModel model in new TaskGroupModel[] { OverdueTasks, TodayTasks, TomorrowTasks })
 			{
 				if (model == null)
 					continue;
@@ -438,24 +412,25 @@ namespace Tasque
 
 		void OnTooltipModelChanged (object sender, EventArgs args)
 		{
-			RefreshTrayIconTooltip ();
+			if (trayIcon != null)
+				trayIcon.RefreshTrayIconTooltip ();
 		}
 
 		void RebuildTooltipTaskGroupModels ()
 		{
 			if (backend == null || backend.Tasks2 == null) {
-				overdue_tasks = null;
-				today_tasks = null;
-				tomorrow_tasks = null;
+				OverdueTasks = null;
+				TodayTasks = null;
+				TomorrowTasks = null;
 				
 				return;
 			}
 
-			overdue_tasks = TaskGroupModelFactory.CreateOverdueModel (backend.Tasks);
-			today_tasks = TaskGroupModelFactory.CreateTodayModel (backend.Tasks);
-			tomorrow_tasks = TaskGroupModelFactory.CreateTomorrowModel (backend.Tasks);
+			OverdueTasks = TaskGroupModelFactory.CreateOverdueModel (backend.Tasks);
+			TodayTasks = TaskGroupModelFactory.CreateTodayModel (backend.Tasks);
+			TomorrowTasks = TaskGroupModelFactory.CreateTomorrowModel (backend.Tasks);
 
-			foreach (TaskGroupModel model in new TaskGroupModel[] { overdue_tasks, today_tasks, tomorrow_tasks })
+			foreach (TaskGroupModel model in new TaskGroupModel[] { OverdueTasks, TodayTasks, TomorrowTasks })
 			{
 				if (model == null)
 					continue;
@@ -463,154 +438,24 @@ namespace Tasque
 				model.CollectionChanged += OnTooltipModelChanged;
 			}
 		}
-		
-		private void RefreshTrayIconTooltip ()
-		{
-			if (trayIcon == null) {
-				return;
-			}
-
-			StringBuilder sb = new StringBuilder ();
-			if (overdue_tasks != null) {
-				int count =  overdue_tasks.Count;
-
-				if (count > 0) {
-					sb.Append (String.Format (Catalog.GetPluralString ("{0} task is Overdue\n", "{0} tasks are Overdue\n", count), count));
-				}
-			}
-			
-			if (today_tasks != null) {
-				int count =  today_tasks.Count;
-
-				if (count > 0) {
-					sb.Append (String.Format (Catalog.GetPluralString ("{0} task for Today\n", "{0} tasks for Today\n", count), count));
-				}
-			}
-
-			if (tomorrow_tasks != null) {
-				int count =  tomorrow_tasks.Count;
-
-				if (count > 0) {
-					sb.Append (String.Format (Catalog.GetPluralString ("{0} task for Tomorrow\n", "{0} tasks for Tomorrow\n", count), count));
-				}
-			}
-
-			if (sb.Length == 0) {
-				// Translators: This is the status icon's tooltip. When no tasks are overdue, due today, or due tomorrow, it displays this fun message
-				trayIcon.Tooltip = Catalog.GetString ("Tasque Rocks");
-				return;
-			}
-
-			trayIcon.Tooltip = sb.ToString ().TrimEnd ('\n');
-		}
-
 
-		private void OnPreferences (object sender, EventArgs args)
-		{
-			Trace.TraceInformation ("OnPreferences called");
-			if (preferencesDialog == null) {
-				preferencesDialog = new PreferencesDialog ();
-				preferencesDialog.Hidden += OnPreferencesDialogHidden;
-			}
-			
-			preferencesDialog.Present ();
-		}
-		
 		private void OnPreferencesDialogHidden (object sender, EventArgs args)
 		{
 			preferencesDialog.Destroy ();
 			preferencesDialog = null;
 		}
 		
-		public static void ShowPreferences()
+		public static void ShowPreferences ()
 		{
-			application.OnPreferences(null, EventArgs.Empty);
-		}
-
-		private void OnAbout (object sender, EventArgs args)
-		{
-			string [] authors = new string [] {
-				"Boyd Timothy <btimothy gmail com>",
-				"Calvin Gaisford <calvinrg gmail com>",
-				"Sandy Armstrong <sanfordarmstrong gmail com>",
-				"Brian G. Merrell <bgmerrell novell com>"
-			};
-
-			/* string [] documenters = new string [] {
-			   "Calvin Gaisford <calvinrg gmail com>"
-			   };
-			   */
-
-			string translators = Catalog.GetString ("translator-credits");
-			if (translators == "translator-credits")
-				translators = null;
-			
-			Gtk.AboutDialog about = new Gtk.AboutDialog ();
-			about.ProgramName = "Tasque";
-			about.Version = GlobalDefines.Version;
-			about.Logo = Utilities.GetIcon("tasque-48", 48);
-			about.Copyright =
-				Catalog.GetString ("Copyright \xa9 2008 Novell, Inc.");
-			about.Comments = Catalog.GetString ("A Useful Task List");
-			about.Website = "http://live.gnome.org/Tasque";;
-			about.WebsiteLabel = Catalog.GetString("Tasque Project Homepage");
-			about.Authors = authors;
-			//about.Documenters = documenters;
-			about.TranslatorCredits = translators;
-			about.IconName = "tasque";
-			about.SetSizeRequest(300, 300);
-			about.Run ();
-			about.Destroy ();
-
-		}
-
-
-		private void OnShowTaskWindow (object sender, EventArgs args)
-		{
-			TaskWindow.ShowWindow();
-		}
-		
-		private void OnNewTask (object sender, EventArgs args)
-		{
-			// Show the TaskWindow and then cause a new task to be created
-			TaskWindow.ShowWindow ();
-			TaskWindow.GrabNewTaskEntryFocus ();
-		}
-
-		private void OnQuit (object sender, EventArgs args)
-		{
-			Trace.TraceInformation ("OnQuit called - terminating application");
-			if (backend != null) {
-				UnhookFromTooltipTaskGroupModels ();
-				backend.Cleanup();
+			var app = application;
+			Trace.TraceInformation ("OnPreferences called");
+			if (app.preferencesDialog == null) {
+				app.preferencesDialog = new PreferencesDialog ();
+				app.preferencesDialog.Hidden += OnPreferencesDialogHidden;
 			}
-			TaskWindow.SavePosition();
-
-			nativeApp.QuitMainLoop ();
-		}
-		
-		private void OnRefreshAction (object sender, EventArgs args)
-		{
-			Application.Backend.Refresh();
-		}
-
-		private void OnTrayIconClick (object sender, EventArgs args) // handler for mouse click
-		{
-			TaskWindow.ToggleWindowVisible ();
-		}
-
-		private void OnTrayIconPopupMenu (object sender, EventArgs args)
-		{
-			Menu popupMenu = (Menu) uiManager.GetWidget ("/TrayIconMenu");
-
-			bool backendItemsSensitive = (backend != null && backend.Initialized);
 			
-			uiManager.GetAction ("/TrayIconMenu/NewTaskAction").Sensitive = backendItemsSensitive;
-			uiManager.GetAction ("/TrayIconMenu/RefreshAction").Sensitive = backendItemsSensitive;
-
-			popupMenu.ShowAll(); // shows everything
-			popupMenu.Popup();
-		}		
+			app.preferencesDialog.Present ();
+		}
 
 		public static void Main(string[] args)
 		{
@@ -658,50 +503,14 @@ namespace Tasque
 
 		public void Quit ()
 		{
-			OnQuit (null, null);
-		}
+			Trace.TraceInformation ("OnQuit called - terminating application");
+			if (backend != null) {
+				UnhookFromTooltipTaskGroupModels ();
+				backend.Cleanup ();
+			}
+			TaskWindow.SavePosition ();
 
-		private void RegisterUIManager ()
-		{
-			ActionGroup trayActionGroup = new ActionGroup ("Tray");
-			trayActionGroup.Add (new ActionEntry [] {
-				new ActionEntry ("NewTaskAction",
-				                 Stock.New,
-				                 Catalog.GetString ("New Task ..."),
-				                 null,
-				                 null,
-				                 OnNewTask),
-				
-				new ActionEntry ("ShowTasksAction",
-				                 null,
-				                 Catalog.GetString ("Show Tasks ..."),
-				                 null,
-				                 null,
-				                 OnShowTaskWindow),
-
-				new ActionEntry ("AboutAction",
-				                 Stock.About,
-				                 OnAbout),
-				
-				new ActionEntry ("PreferencesAction",
-				                 Stock.Preferences,
-				                 OnPreferences),
-				
-				new ActionEntry ("RefreshAction",
-				                 Stock.Execute,
-				                 Catalog.GetString ("Refresh Tasks ..."),
-				                 null,
-				                 null,
-				                 OnRefreshAction),
-				
-				new ActionEntry ("QuitAction",
-				                 Stock.Quit,
-				                 OnQuit)
-			});
-			
-			uiManager = new UIManager ();
-			uiManager.AddUiFromString (menuXml);
-			uiManager.InsertActionGroup (trayActionGroup, 0);
+			nativeApp.QuitMainLoop ();
 		}
 	}
 }
diff --git a/src/Tasque.Gtk/GtkTray.cs b/src/Tasque.Gtk/GtkTray.cs
new file mode 100644
index 0000000..a3f8d17
--- /dev/null
+++ b/src/Tasque.Gtk/GtkTray.cs
@@ -0,0 +1,225 @@
+// 
+// GtkTray.cs
+//  
+// Author:
+//       Antonius Riha <antoniusriha gmail com>
+// 
+// Copyright (c) 2012 Antonius Riha
+// 
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+// 
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+using System;
+using System.Text;
+using Mono.Unix;
+using Gtk;
+
+namespace Tasque
+{
+	public class GtkTray : IDisposable
+	{
+		public static GtkTray CreateTray ()
+		{
+#if APPINDICATOR
+			return new AppIndicatorTray ();
+#else
+			return new StatusIconTray ();
+#endif
+		}
+
+		protected GtkTray ()
+		{
+			UpdateBackend ();
+			Application.Instance.BackendChanged += HandleBackendChanged;
+			RefreshTrayIconTooltip ();
+		}
+
+		#region IDisposable implementation
+		public void Dispose ()
+		{
+			var app = Application.Instance;
+			if (app != null)
+				app.BackendChanged -= HandleBackendChanged;
+		}
+		#endregion
+		
+		public void RefreshTrayIconTooltip ()
+		{
+			var oldTooltip = Tooltip;
+			
+			var overdueTasks = Application.Instance.OverdueTasks;
+			var todayTasks = Application.Instance.TodayTasks;
+			var tomorrowTasks = Application.Instance.TomorrowTasks;
+			
+			var sb = new StringBuilder ();
+			if (overdueTasks != null) {
+				int count = overdueTasks.Count;
+
+				if (count > 0)
+					sb.Append (String.Format (Catalog.GetPluralString ("{0} task is Overdue\n",
+					                          "{0} tasks are Overdue\n", count), count));
+			}
+			
+			if (todayTasks != null) {
+				int count = todayTasks.Count;
+
+				if (count > 0)
+					sb.Append (String.Format (Catalog.GetPluralString ("{0} task for Today\n",
+					                          "{0} tasks for Today\n", count), count));
+			}
+
+			if (tomorrowTasks != null) {
+				int count = tomorrowTasks.Count;
+
+				if (count > 0)
+					sb.Append (String.Format (Catalog.GetPluralString ("{0} task for Tomorrow\n",
+					                          "{0} tasks for Tomorrow\n", count), count));
+			}
+
+			if (sb.Length == 0) {
+				// Translators: This is the status icon's tooltip. When no tasks are overdue, due
+				// today, or due tomorrow, it displays this fun message
+				Tooltip = Catalog.GetString ("Tasque Rocks");
+				return;
+			}
+
+			Tooltip = sb.ToString ().TrimEnd ('\n');
+			
+			if (Tooltip != oldTooltip)
+				OnTooltipChanged ();
+		}
+
+		protected string IconName { get { return "tasque-24"; } }
+		
+		protected Menu Menu {
+			get {
+				if (uiManager == null)
+					RegisterUIManager ();
+				return (Menu)uiManager.GetWidget ("/TrayIconMenu");
+			}
+		}
+		
+		protected Gtk.Action ToggleTaskWindowAction { get; private set; }
+
+		protected string Tooltip { get; private set; }
+		
+		protected virtual void OnTooltipChanged () {}
+		
+		void HandleBackendChanged (object sender, EventArgs e)
+		{
+			UpdateBackend ();
+		}
+
+		void HandleBackendInitialized (object sender, EventArgs e)
+		{
+			UpdateActionSensitivity ();
+		}
+
+		void OnAbout (object sender, EventArgs args)
+		{
+			var authorsList = GlobalDefines.Authors;
+			var authors = new string [authorsList.Count];
+			authorsList.CopyTo (authors, 0);
+
+			string translators = Catalog.GetString ("translator-credits");
+			if (translators == "translator-credits")
+				translators = null;
+			
+			var about = new AboutDialog ();
+			about.ProgramName = "Tasque";
+			about.Version = GlobalDefines.Version;
+			about.Logo = Utilities.GetIcon ("tasque-48", 48);
+			about.Copyright = Catalog.GetString ("Copyright \xa9 2008 Novell, Inc.");
+			about.License = GlobalDefines.License;
+			about.Comments = Catalog.GetString ("A Useful Task List");
+			about.Website = GlobalDefines.Website;
+			about.WebsiteLabel = Catalog.GetString("Tasque Project Homepage");
+			about.Authors = authors;
+			about.TranslatorCredits = translators;
+			about.IconName = "tasque";
+			about.SetSizeRequest(300, 300);
+			about.Run ();
+			about.Destroy ();
+		}
+
+		void RegisterUIManager ()
+		{
+			var trayActionGroup = new ActionGroup ("Tray");
+			trayActionGroup.Add (new ActionEntry [] {
+				new ActionEntry ("NewTaskAction", Stock.New, Catalog.GetString ("New Task ..."), null, null, delegate {
+					// Show the TaskWindow and then cause a new task to be created
+					TaskWindow.ShowWindow ();
+					TaskWindow.GrabNewTaskEntryFocus ();
+				}),
+
+				new ActionEntry ("AboutAction", Stock.About, OnAbout),
+
+				new ActionEntry ("PreferencesAction", Stock.Preferences, delegate { Application.ShowPreferences (); }),
+
+				new ActionEntry ("RefreshAction", Stock.Execute, Catalog.GetString ("Refresh Tasks ..."),
+				                 null, null, delegate { Application.Backend.Refresh(); }),
+
+				new ActionEntry ("QuitAction", Stock.Quit, delegate { Application.Instance.Quit (); })
+			});
+			
+			ToggleTaskWindowAction = new Gtk.Action ("ToggleTaskWindowAction", "Toggle Task Window");
+			ToggleTaskWindowAction.ActionGroup = trayActionGroup;
+			ToggleTaskWindowAction.Activated += delegate { TaskWindow.ToggleWindowVisible (); };
+			
+			uiManager = new UIManager ();
+			uiManager.AddUiFromString (MenuXml);
+			uiManager.InsertActionGroup (trayActionGroup, 0);
+		}
+
+		void UpdateActionSensitivity ()
+		{
+			var backend = Application.Backend;
+			bool backendItemsSensitive = (backend != null && backend.Initialized);
+			uiManager.GetAction ("/TrayIconMenu/NewTaskAction").Sensitive = backendItemsSensitive;
+			uiManager.GetAction ("/TrayIconMenu/RefreshAction").Sensitive = backendItemsSensitive;
+		}
+
+		void UpdateBackend ()
+		{
+			if (backend != null)
+				backend.BackendInitialized -= HandleBackendInitialized;
+			backend = Application.Backend;
+
+			if (backend != null)
+				backend.BackendInitialized += HandleBackendInitialized;
+
+			UpdateActionSensitivity ();
+		}
+
+		Backend backend;
+		UIManager uiManager;
+		const string MenuXml = @"
+<ui>
+	<popup name=""TrayIconMenu"">
+		<menuitem action=""NewTaskAction""/>
+		<separator/>
+		<menuitem action=""PreferencesAction""/>
+		<menuitem action=""AboutAction""/>
+		<separator/>
+		<menuitem action=""RefreshAction""/>
+		<separator/>
+		<menuitem action=""QuitAction""/>
+	</popup>
+</ui>
+";
+	}
+}
diff --git a/src/Tasque.Gtk/StatusIconTray.cs b/src/Tasque.Gtk/StatusIconTray.cs
new file mode 100644
index 0000000..b056b17
--- /dev/null
+++ b/src/Tasque.Gtk/StatusIconTray.cs
@@ -0,0 +1,51 @@
+// 
+// StatusIconTray.cs
+//  
+// Author:
+//       Antonius Riha <antoniusriha gmail com>
+// 
+// Copyright (c) 2012 Antonius Riha
+// 
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+// 
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+using Gtk;
+
+namespace Tasque
+{
+	public class StatusIconTray : GtkTray
+	{
+		public StatusIconTray ()
+		{
+			tray = new StatusIcon (Utilities.GetIcon (IconName, 24));
+			tray.Visible = true;
+			tray.Activate += delegate { ToggleTaskWindowAction.Activate (); };
+			tray.PopupMenu += (sender, e) => {
+				var popupMenu = Menu;
+				popupMenu.ShowAll (); // shows everything
+				tray.PresentMenu (popupMenu, (uint)e.Args [0], (uint)e.Args [1]);
+			};
+		}
+		
+		protected override void OnTooltipChanged ()
+		{
+			tray.Tooltip = Tooltip;
+		}
+		
+		StatusIcon tray;
+	}
+}
diff --git a/src/Tasque.Gtk/TaskWindow.cs b/src/Tasque.Gtk/TaskWindow.cs
index 80a2484..1938d3e 100644
--- a/src/Tasque.Gtk/TaskWindow.cs
+++ b/src/Tasque.Gtk/TaskWindow.cs
@@ -37,6 +37,7 @@ using Mono.Unix;
 using Tasque;
 using CollectionTransforms;
 using System.Collections;
+using System.Diagnostics;
 
 namespace Tasque
 {
diff --git a/src/Tasque.Gtk/Tasque.Gtk.csproj b/src/Tasque.Gtk/Tasque.Gtk.csproj
index 39ed83a..d5859aa 100644
--- a/src/Tasque.Gtk/Tasque.Gtk.csproj
+++ b/src/Tasque.Gtk/Tasque.Gtk.csproj
@@ -10,6 +10,7 @@
     <AssemblyName>Tasque.Gtk</AssemblyName>
     <RootNamespace>Tasque</RootNamespace>
     <ReleaseVersion>0.1.10</ReleaseVersion>
+    <EnableAppIndicator Condition=" '$(EnableAppIndicator)' == '' ">true</EnableAppIndicator>
   </PropertyGroup>
   <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'GtkLinuxDebug|AnyCPU' ">
     <DebugSymbols>true</DebugSymbols>
@@ -17,6 +18,7 @@
     <Optimize>false</Optimize>
     <OutputPath>bin\Debug</OutputPath>
     <DefineConstants>DEBUG;TRACE;GTKLINUX</DefineConstants>
+    <DefineConstants Condition=" '$(EnableAppIndicator)' ">$(DefineConstants);APPINDICATOR</DefineConstants>
     <WarningLevel>4</WarningLevel>
   </PropertyGroup>
   <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'GtkLinuxRelease|AnyCPU' ">
@@ -24,6 +26,7 @@
     <Optimize>true</Optimize>
     <OutputPath>bin\LinuxRelease</OutputPath>
     <DefineConstants>TRACE;GTKLINUX</DefineConstants>
+    <DefineConstants Condition=" '$(EnableAppIndicator)' ">$(DefineConstants);APPINDICATOR</DefineConstants>
     <ErrorReport>prompt</ErrorReport>
     <WarningLevel>4</WarningLevel>
     <CustomCommands>
@@ -87,13 +90,16 @@
     </Reference>
     <Reference Include="notify-sharp, Version=0.4.0.0, Culture=neutral, PublicKeyToken=2df29c54e245917a">
       <Private>False</Private>
-      <Package>notify-sharp</Package>
     </Reference>
     <Reference Include="NDesk.DBus.GLib, Version=1.0.0.0, Culture=neutral, PublicKeyToken=f6716e4f9b2ed099">
       <Private>False</Private>
       <Package>ndesk-dbus-glib-1.0</Package>
     </Reference>
     <Reference Include="WindowsBase" />
+    <Reference Include="appindicator-sharp, Version=0.2.0.0, Culture=neutral, PublicKeyToken=bcae265d1c7ab4c2">
+      <Private>False</Private>
+      <Package>appindicator-sharp-0.1</Package>
+    </Reference>
   </ItemGroup>
   <ItemGroup>
     <None Include="..\..\data\sounds\notify.wav">
@@ -126,6 +132,8 @@
     <Compile Include="TaskWindow.cs" />
     <Compile Include="Utilities.cs" />
     <Compile Include="GtkApplicationBase.cs" />
+    <Compile Include="StatusIconTray.cs" />
+    <Compile Include="GtkTray.cs" />
   </ItemGroup>
   <ItemGroup Condition=" '$(Configuration)' == 'GtkWinDebug' or '$(Configuration)' == 'GtkWinRelease' ">
     <Compile Include="GtkWinApplication.cs" />
@@ -134,6 +142,9 @@
     <Compile Include="GtkLinuxApplication.cs" />
     <Compile Include="RemoteControl.cs" />
   </ItemGroup>
+  <ItemGroup Condition=" '$(EnableAppIndicator)' ">
+    <Compile Include="AppIndicatorTray.cs" />
+  </ItemGroup>
   <ItemGroup>
     <Folder Include="Properties\" />
     <Folder Include="data\" />



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]