[tasque/viewmodel: 30/78] Add Windows single app instance logic



commit 7d6f9720d7e27037c8abf74db517f8ec9558a355
Author: Antonius Riha <antoniusriha gmail com>
Date:   Sun Jul 29 18:24:41 2012 +0200

    Add Windows single app instance logic
    
    * split GtkApp in two classes which are conditionally built:
    	- GtkLinuxApplication: holds single app instance logic with dbus
    	- GtkWinApplication: holds single app instance logic with named events
    * GtkApplicationBase: Common base class
    * Program.cs: Add GtkWinApplication init
    * WindowsSingleInstanceSolution.cs: don't needed anymore (saved though in
    a gist: https://gist.github.com/3199448)

 .../{GtkApplication.cs => GtkApplicationBase.cs}   |  141 +++++++++-----------
 src/Tasque.Gtk/GtkLinuxApplication.cs              |   65 +++++++++
 src/Tasque.Gtk/GtkWinApplication.cs                |   75 +++++++++++
 .../Samples/WindowsSingleInstanceSolution.cs       |   89 ------------
 src/Tasque.Gtk/Tasque.Gtk.csproj                   |    7 +-
 src/tasque/Program.cs                              |    4 +-
 6 files changed, 212 insertions(+), 169 deletions(-)
---
diff --git a/src/Tasque.Gtk/GtkApplication.cs b/src/Tasque.Gtk/GtkApplicationBase.cs
similarity index 58%
rename from src/Tasque.Gtk/GtkApplication.cs
rename to src/Tasque.Gtk/GtkApplicationBase.cs
index b8d276c..ef3c955 100644
--- a/src/Tasque.Gtk/GtkApplication.cs
+++ b/src/Tasque.Gtk/GtkApplicationBase.cs
@@ -33,9 +33,36 @@ using Tasque.UIModel.Legacy;
 
 namespace Tasque
 {
-	public class GtkApplication : NativeApplication
+	public abstract class GtkApplicationBase : NativeApplication
 	{
-		public GtkApplication ()
+		#region just copied
+		public UIManager UIManager
+		{
+			get { return uiManager; }
+		}
+		
+		#region implemented abstract members of Tasque.UIModel.Legacy.NativeApplication
+		public override Backend CurrentBackend {
+			get {
+				throw new System.NotImplementedException ();
+			}
+		}
+
+		public override System.Collections.ObjectModel.ReadOnlyCollection<Backend> AvailableBackends {
+			get {
+				throw new System.NotImplementedException ();
+			}
+		}
+
+		public override Tasque.UIModel.Legacy.MainWindowModel MainWindowModel {
+			get {
+				throw new System.NotImplementedException ();
+			}
+		}
+		#endregion
+		#endregion
+		
+		public GtkApplicationBase ()
 		{
 			confDir = Path.Combine (
 				Environment.GetFolderPath (Environment.SpecialFolder.ApplicationData), "tasque");
@@ -73,88 +100,51 @@ namespace Tasque
 			}
 		}
 		
-		protected override bool IsRemoteInstanceRunning ()
-		{
-#if !WIN32
-			// Register Tasque RemoteControl
-			try {
-				remoteInstance = RemoteControl.Register ();
-				if (remoteInstance != null) {
-					remoteInstance.RemoteInstanceKnocked = HandleRemoteInstanceKnocked;
-					Debug.Write ("Tasque remote control active.");
-				} else {
-					// If Tasque is already running, open the tasks window
-					// so the user gets some sort of feedback when they
-					// attempt to run Tasque again.
-					RemoteControl remote = null;
-					try {
-						remote = RemoteControl.GetInstance ();
-						remote.KnockKnock ();
-					} catch {}
-
-					Debug.WriteLine ("Tasque is already running.  Exiting...");
-					return true;
-				}
-			} catch (Exception e) {
-				Debug.WriteLine ("Tasque remote control disabled (DBus exception): {0}", e.Message);
-			}
-			return false;
-#else
-			
-#endif
-		}
-		
 		protected override event EventHandler RemoteInstanceKnocked;
 		
-		void HandleRemoteInstanceKnocked ()
+		protected void OnRemoteInstanceKnocked ()
 		{
 			if (RemoteInstanceKnocked != null)
 				RemoteInstanceKnocked (this, EventArgs.Empty);
 		}
 		
-		protected override void Dispose (bool disposing)
-		{
-			if (disposing)
-				remoteInstance.RemoteInstanceKnocked = null;
-		}
-		
 		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)
-			});
+//			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);
@@ -178,8 +168,5 @@ namespace Tasque
 ";
 		
 		string confDir;
-#if !WIN32
-		RemoteControl remoteInstance;
-#endif
 	}
 }
diff --git a/src/Tasque.Gtk/GtkLinuxApplication.cs b/src/Tasque.Gtk/GtkLinuxApplication.cs
new file mode 100644
index 0000000..25f3159
--- /dev/null
+++ b/src/Tasque.Gtk/GtkLinuxApplication.cs
@@ -0,0 +1,65 @@
+// 
+// GtkLinuxApplication.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.Diagnostics;
+
+namespace Tasque
+{
+	public class GtkLinuxApplication : GtkApplicationBase
+	{
+		protected override void Dispose (bool disposing)
+		{
+			if (disposing)
+				remoteInstance = null;
+			
+			base.Dispose (disposing);
+		}
+		
+		protected override bool IsRemoteInstanceRunning ()
+		{
+			// Register Tasque RemoteControl
+			try {
+				remoteInstance = RemoteControl.Register ();
+				if (remoteInstance != null) {
+					remoteInstance.RemoteInstanceKnocked = OnRemoteInstanceKnocked;
+					Debug.WriteLine ("Tasque remote control created.");
+				} else {
+					RemoteControl remote = null;
+					try {
+						remote = RemoteControl.GetInstance ();
+						remote.KnockKnock ();
+					} catch {}
+					return true;
+				}
+			} catch (Exception e) {
+				Debug.WriteLine ("Tasque remote control disabled (DBus exception): {0}", e.Message);
+			}
+			return false;
+		}
+		
+		RemoteControl remoteInstance;
+	}
+}
diff --git a/src/Tasque.Gtk/GtkWinApplication.cs b/src/Tasque.Gtk/GtkWinApplication.cs
new file mode 100644
index 0000000..183bbd6
--- /dev/null
+++ b/src/Tasque.Gtk/GtkWinApplication.cs
@@ -0,0 +1,75 @@
+// 
+// GtkWinApplication.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.Diagnostics;
+using System.Threading;
+
+namespace Tasque
+{
+	public class GtkWinApplication : GtkApplicationBase
+	{
+		protected override void Dispose (bool disposing)
+		{
+			if (disposing) {
+				waitHandle.Dispose ();
+				waitHandle = null;
+			}
+			
+			base.Dispose (disposing);
+		}
+		
+		protected override bool IsRemoteInstanceRunning ()
+		{
+			try {
+				waitHandle = EventWaitHandle.OpenExisting(waitHandleName);
+				waitHandle.Set();
+				return true;
+			} catch (WaitHandleCannotBeOpenedException) {
+				waitHandle = new EventWaitHandle(false, EventResetMode.AutoReset, waitHandleName);
+				Debug.WriteLine("EventWaitHandle created.");
+				
+				var porter = new Thread(new ThreadStart(WaitForAnotherInstance));
+				porter.Start();
+			}
+			return false;
+		}
+		
+		void WaitForAnotherInstance ()
+		{
+			while (!exiting) {
+				waitHandle.WaitOne ();
+				if (!exiting) {
+					Debug.WriteLine ("Another app instance has just knocked on the door.");
+					OnRemoteInstanceKnocked ();
+				}
+			}
+		}
+		
+		bool exiting;
+		EventWaitHandle waitHandle;
+		readonly string waitHandleName = "Tasque." + Environment.UserName;
+	}
+}
diff --git a/src/Tasque.Gtk/Tasque.Gtk.csproj b/src/Tasque.Gtk/Tasque.Gtk.csproj
index 059d97e..1edd6c6 100644
--- a/src/Tasque.Gtk/Tasque.Gtk.csproj
+++ b/src/Tasque.Gtk/Tasque.Gtk.csproj
@@ -99,7 +99,6 @@
       <Link>data\sounds\notify.wav</Link>
     </None>
     <None Include="Samples\Gtk3SingleInstanceSolution.cs" />
-    <None Include="Samples\WindowsSingleInstanceSolution.cs" />
   </ItemGroup>
   <ItemGroup>
     <Compile Include="Properties\AssemblyInfo.cs" />
@@ -113,7 +112,6 @@
     <Compile Include="CellRendererDate.cs" />
     <Compile Include="CompletedTaskGroup.cs" />
     <Compile Include="DateButton.cs" />
-    <Compile Include="GtkApplication.cs" />
     <Compile Include="NoteDialog.cs" />
     <Compile Include="NoteWidget.cs" />
     <Compile Include="Preferences.cs" />
@@ -123,8 +121,13 @@
     <Compile Include="TaskTreeView.cs" />
     <Compile Include="TaskWindow.cs" />
     <Compile Include="Utilities.cs" />
+    <Compile Include="GtkApplicationBase.cs" />
+  </ItemGroup>
+  <ItemGroup Condition=" '$(Configuration)' == 'GtkWinDebug' or '$(Configuration)' == 'GtkWinRelease' ">
+    <Compile Include="GtkWinApplication.cs" />
   </ItemGroup>
   <ItemGroup Condition=" '$(Configuration)' == 'GtkLinuxDebug' or '$(Configuration)' == 'GtkLinuxRelease' ">
+    <Compile Include="GtkLinuxApplication.cs" />
     <Compile Include="RemoteControl.cs" />
   </ItemGroup>
   <ItemGroup>
diff --git a/src/tasque/Program.cs b/src/tasque/Program.cs
index 0ba8262..fd8d2e7 100644
--- a/src/tasque/Program.cs
+++ b/src/tasque/Program.cs
@@ -39,8 +39,10 @@ namespace Tasque
 			NativeApplication app;
 #if OSX
 			app = new OSXApplication ();
+#elif WIN32
+			app = new GtkWinApplication ();
 #else
-			app = new GtkApplication ();
+			app = new GtkLinuxApplication ();
 #endif
 			return app;
 		}



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