# HG changeset patch
# User StephaneLenclud
# Date 1471523750 -7200
# Node ID c92587ddabcdd4da1984d03612b5263ea6fb1121
# Parent  1a1c2ae3a29c7ea86f55eda3527cddfad2ca6069
Support for launch action and WMC HID events.

diff -r 1a1c2ae3a29c -r c92587ddabcd .hgignore
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/.hgignore	Thu Aug 18 14:35:50 2016 +0200
@@ -0,0 +1,5 @@
+obj/*.*
+bin/*.*
+packages/*.*
+.vs/*.*
+ipch/*.*
\ No newline at end of file
diff -r 1a1c2ae3a29c -r c92587ddabcd Server/Actions/ActionHarmonyCommand.cs
--- a/Server/Actions/ActionHarmonyCommand.cs	Wed Aug 17 16:39:36 2016 +0200
+++ b/Server/Actions/ActionHarmonyCommand.cs	Thu Aug 18 14:35:50 2016 +0200
@@ -36,7 +36,7 @@
         /// <returns></returns>
         public override string Brief()
         {
-            string brief="";
+            string brief="Harmony: ";
 
             if (Program.HarmonyConfig != null)
             {
diff -r 1a1c2ae3a29c -r c92587ddabcd Server/Events/EventHidConsumerControl.cs
--- a/Server/Events/EventHidConsumerControl.cs	Wed Aug 17 16:39:36 2016 +0200
+++ b/Server/Events/EventHidConsumerControl.cs	Thu Aug 18 14:35:50 2016 +0200
@@ -42,10 +42,14 @@
         /// <returns></returns>
         public override bool Equals(object obj)
         {
-            EventHidConsumerControl e = (EventHidConsumerControl) obj;
-            bool res = (e != null && e.Usage == Usage);
-            return res;
+            if (obj is EventHidConsumerControl)
+            {
+                EventHidConsumerControl e = (EventHidConsumerControl)obj;
+                bool res = (e.Usage == Usage);
+                return res;
+            }
+
+            return false;
         }
-
     }
 }
diff -r 1a1c2ae3a29c -r c92587ddabcd Server/Events/EventHidWindowsMediaCenter.cs
--- a/Server/Events/EventHidWindowsMediaCenter.cs	Wed Aug 17 16:39:36 2016 +0200
+++ b/Server/Events/EventHidWindowsMediaCenter.cs	Thu Aug 18 14:35:50 2016 +0200
@@ -3,10 +3,54 @@
 using System.Linq;
 using System.Text;
 using System.Threading.Tasks;
+using System.Runtime.Serialization;
+using Ear = SharpLib.Ear;
+using Hid = SharpLib.Hid;
 
-namespace SharpDisplayManager.Events
+namespace SharpDisplayManager
 {
-    class EventHidWindowsMediaCenter
+    [DataContract]
+    [Ear.AttributeObject(Id = "Event.Hid.WindowsMediaCenter", Name = "HID Windows Media Center", Description = "Corresponding HID message received.")]
+    public class EventHidWindowsMediaCenter : Ear.Event
     {
+        public EventHidWindowsMediaCenter()
+        {
+        }
+
+        [DataMember]
+        [Ear.AttributeObjectProperty
+            (
+            Id = "HID.WMC.Usage",
+            Name = "Usage",
+            Description = "The usage corresponding to your remote button."
+            )]
+        public Hid.Usage.WindowsMediaCenterRemoteControl Usage { get; set; }
+
+        /// <summary>
+        /// Make sure we distinguish between various configuration of this event 
+        /// </summary>
+        /// <returns></returns>
+        public override string Brief()
+        {
+            return Name + ": " + Usage.ToString();
+        }
+
+        /// <summary>
+        ///
+        /// </summary>
+        /// <param name="obj"></param>
+        /// <returns></returns>
+        public override bool Equals(object obj)
+        {
+            if (obj is EventHidWindowsMediaCenter)
+            {
+                EventHidWindowsMediaCenter e = (EventHidWindowsMediaCenter)obj;
+                bool res = (e.Usage == Usage);
+                return res;
+            }
+
+            return false;
+        }
+
     }
 }
diff -r 1a1c2ae3a29c -r c92587ddabcd Server/FormEditObject.cs
--- a/Server/FormEditObject.cs	Wed Aug 17 16:39:36 2016 +0200
+++ b/Server/FormEditObject.cs	Thu Aug 18 14:35:50 2016 +0200
@@ -13,6 +13,7 @@
 using System.Reflection;
 using Microsoft.VisualBasic.CompilerServices;
 using SharpLib.Utils;
+using CodeProject.Dialog;
 
 namespace SharpDisplayManager
 {
@@ -146,6 +147,12 @@
                 TextBox ctrl = (TextBox)aControl;
                 aInfo.SetValue(aObject, ctrl.Text);
             }
+            else if (aInfo.PropertyType == typeof(PropertyFile))
+            {
+                Button ctrl = (Button)aControl;
+                PropertyFile value = new PropertyFile {FullPath=ctrl.Text};
+                aInfo.SetValue(aObject, value);
+            }
             //TODO: add support for other types here
         }
 
@@ -218,6 +225,31 @@
                 ctrl.Text = (string)aInfo.GetValue(aObject);
                 return ctrl;
             }
+            else if (aInfo.PropertyType == typeof(PropertyFile))
+            {
+                // We have a file property
+                // Create a button that will trigger the open file dialog to select our file.
+                Button ctrl = new Button();
+                ctrl.AutoSize = true;
+                ctrl.Text = ((PropertyFile)aInfo.GetValue(aObject)).FullPath;
+                // Add lambda expression to Click event
+                ctrl.Click += (sender, e) =>
+                {
+                    // Create open file dialog
+                    OpenFileDialog ofd = new OpenFileDialog();
+                    ofd.RestoreDirectory = true;
+                    // Use file filter specified by our property
+                    ofd.Filter = aAttribute.Filter;
+                    // Show our dialog
+                    if (DlgBox.ShowDialog(ofd) == DialogResult.OK)
+                    {
+                        // Fetch selected file name
+                        ctrl.Text = ofd.FileName;
+                    }
+                };
+
+                return ctrl;
+            }
             //TODO: add support for other control type here
 
             return null;
@@ -245,6 +277,10 @@
             {
                 return true;
             }
+            else if (aInfo.PropertyType == typeof(PropertyFile))
+            {
+                return true;
+            }
             //TODO: add support for other type here
 
             return false;
diff -r 1a1c2ae3a29c -r c92587ddabcd Server/FormMain.Hid.cs
--- a/Server/FormMain.Hid.cs	Wed Aug 17 16:39:36 2016 +0200
+++ b/Server/FormMain.Hid.cs	Thu Aug 18 14:35:50 2016 +0200
@@ -127,6 +127,12 @@
                 //We are in the proper thread
                 if (aHidEvent.UsagePage == (ushort) Hid.UsagePage.WindowsMediaCenterRemoteControl)
                 {
+                    //Trigger events as needed
+                    EventHidWindowsMediaCenter e = new EventHidWindowsMediaCenter { Usage = (Hid.Usage.WindowsMediaCenterRemoteControl)aHidEvent.Usages[0] };
+                    Properties.Settings.Default.EarManager.TriggerEvent(e);
+
+                    //Old legacy hard coded stuff
+                    //TODO: remove it
                     switch (aHidEvent.Usages[0])
                     {
                         case (ushort)Hid.Usage.WindowsMediaCenterRemoteControl.GreenStart:
@@ -140,16 +146,14 @@
                 }
                 else if (aHidEvent.UsagePage == (ushort)Hid.UsagePage.Consumer)
                 {
+                    EventHidConsumerControl e = new EventHidConsumerControl { Usage = (Hid.Usage.ConsumerControl)aHidEvent.Usages[0] };
+                    Properties.Settings.Default.EarManager.TriggerEvent(e);
+
                     //Keep this for debug when only ThinkPad keyboard is available
                     //if (aHidEvent.Usages[0] == (ushort)Hid.Usage.ConsumerControl.ThinkPadFullscreenMagnifier)
                     //{
                     //    HandleEject();
                     //}
-
-                    EventHidConsumerControl e = new EventHidConsumerControl {Usage = (Hid.Usage.ConsumerControl)aHidEvent.Usages[0]};
-                    Properties.Settings.Default.EarManager.TriggerEvent(e);
-
-
                 }
 
             }
diff -r 1a1c2ae3a29c -r c92587ddabcd SharpLibEar/Action.cs
--- a/SharpLibEar/Action.cs	Wed Aug 17 16:39:36 2016 +0200
+++ b/SharpLibEar/Action.cs	Thu Aug 18 14:35:50 2016 +0200
@@ -9,8 +9,7 @@
 namespace SharpLib.Ear
 {
     [DataContract]
-    [KnownType("DerivedTypes")]
-    public abstract class Action: IComparable
+    public abstract class Action: Object
     {
         protected abstract void DoExecute();
 
@@ -29,28 +28,6 @@
             DoExecute();
         }
 
-        public string Name {
-            //Get the name of this object action attribute
-            get { return Utils.Reflection.GetAttribute<AttributeObject>(GetType()).Name; }
-            private set { }
-        }
-
-        public virtual string Brief()
-        {
-            return Name;
-        }
-
-        public int CompareTo(object obj)
-        {
-            //Sort by action name
-            return Utils.Reflection.GetAttribute<AttributeObject>(GetType()).Name.CompareTo(obj.GetType());            
-        }
-
-        private static IEnumerable<Type> DerivedTypes()
-        {
-            return SharpLib.Utils.Reflection.GetDerivedTypes<Action>();
-        }
-
     }
 
 
diff -r 1a1c2ae3a29c -r c92587ddabcd SharpLibEar/ActionLaunchApp.cs
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SharpLibEar/ActionLaunchApp.cs	Thu Aug 18 14:35:50 2016 +0200
@@ -0,0 +1,88 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using System.Linq;
+using System.Runtime.Serialization;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace SharpLib.Ear
+{
+    [DataContract]
+    [AttributeObject(Id = "Action.Launch.App", Name = "Launch application", Description = "Launch an application.")]
+    public class ActionLaunchApp : Action
+    {
+        [DataMember]
+        [AttributeObjectProperty
+            (
+            Id = "Action.Launch.App.File",
+            Name = "File to launch",
+            Description = "Specifies the application file to launch.",
+            Filter = "EXE files (*.exe)|*.exe"
+            )
+        ]
+        public PropertyFile File { get; set; } = new PropertyFile();
+
+        [DataMember]
+        [AttributeObjectProperty
+            (
+            Id = "Action.Launch.App.SwitchTo",
+            Name = "Switch to",
+            Description = "Specifies if we should switch the application if it's already launched."
+            )
+        ]
+        public bool SwitchTo { get; set; } = true;
+
+        [DataMember]
+        [AttributeObjectProperty
+            (
+            Id = "Action.Launch.App.MultipleInstance",
+            Name = "Multiple Instance",
+            Description = "Specifies if We should launch multiple instance."
+            )
+        ]
+        public bool MultipleInstance { get; set; } = false;
+
+        public override string Brief()
+        {
+            return Name + ": " + Path.GetFileName(File.FullPath);
+        }
+
+        [System.Runtime.InteropServices.DllImportAttribute("user32.dll", EntryPoint = "SwitchToThisWindow")]
+        public static extern void SwitchToThisWindow([System.Runtime.InteropServices.InAttribute()] System.IntPtr hwnd, [System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.Bool)] bool fUnknown);
+
+
+        protected override void DoExecute()
+        {
+            //First check if the process we want to launch already exists
+            string procName = Path.GetFileNameWithoutExtension(File.FullPath);
+            Process[] existingProcesses = Process.GetProcessesByName(procName);
+            if (existingProcesses == null || existingProcesses.Length == 0 || MultipleInstance)
+            {
+                // Process do not exists just try to launch it
+                ProcessStartInfo start = new ProcessStartInfo();
+                // Enter in the command line arguments, everything you would enter after the executable name itself
+                //start.Arguments = arguments; 
+                // Enter the executable to run, including the complete path
+                start.FileName = File.FullPath;
+                start.WindowStyle = ProcessWindowStyle.Normal;
+                start.CreateNoWindow = true;
+                start.UseShellExecute = true;
+                // Run the external process & wait for it to finish
+                Process proc = Process.Start(start);
+
+                //SL: We could have used that too
+                //Shell32.Shell shell = new Shell32.Shell();
+                //shell.ShellExecute(Properties.Settings.Default.StartFileName);
+            }
+            else if (SwitchTo)
+            {
+                //This won't work properly until we have a manifest that enables uiAccess.
+                //However uiAccess just won't work with ClickOnce so we will have to use a different deployment system.
+                SwitchToThisWindow(existingProcesses[0].MainWindowHandle, true);
+            }
+
+        }
+    }
+}
diff -r 1a1c2ae3a29c -r c92587ddabcd SharpLibEar/AttributeObjectProperty.cs
--- a/SharpLibEar/AttributeObjectProperty.cs	Wed Aug 17 16:39:36 2016 +0200
+++ b/SharpLibEar/AttributeObjectProperty.cs	Thu Aug 18 14:35:50 2016 +0200
@@ -19,6 +19,8 @@
         public string Minimum;
         public string Maximum;
         public string Increment;
+        // For file dialog
+        public string Filter;
     }
 
 
diff -r 1a1c2ae3a29c -r c92587ddabcd SharpLibEar/Event.cs
--- a/SharpLibEar/Event.cs	Wed Aug 17 16:39:36 2016 +0200
+++ b/SharpLibEar/Event.cs	Thu Aug 18 14:35:50 2016 +0200
@@ -8,8 +8,7 @@
 namespace SharpLib.Ear
 {
     [DataContract]
-    [KnownType("DerivedTypes")]
-    public abstract class Event
+    public abstract class Event : Object
     {
         [DataMember]
         [AttributeObjectProperty
@@ -24,24 +23,7 @@
         [DataMember]
         public List<Action> Actions = new List<Action>();
 
-        public string Name
-        {
-            //Get the name of this object attribute
-            get { return Utils.Reflection.GetAttribute<AttributeObject>(GetType()).Name; }
-            private set { }
-        }
 
-        public string Description
-        {
-            //Get the description of this object attribute
-            get { return Utils.Reflection.GetAttribute<AttributeObject>(GetType()).Description; }
-            private set { }
-        }
-
-        public virtual string Brief()
-        {
-            return Name;
-        }
 
         protected Event()
         {
@@ -68,15 +50,6 @@
             }
         }
 
-        /// <summary>
-        /// So that data contract knows all our types.
-        /// </summary>
-        /// <returns></returns>
-        private static IEnumerable<Type> DerivedTypes()
-        {
-            return SharpLib.Utils.Reflection.GetDerivedTypes<Event>();
-        }
-
         //
         public override bool Equals(object obj)
         {
diff -r 1a1c2ae3a29c -r c92587ddabcd SharpLibEar/Object.cs
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SharpLibEar/Object.cs	Thu Aug 18 14:35:50 2016 +0200
@@ -0,0 +1,60 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+
+using System;
+using System.Collections.Generic;
+using System.Runtime.Serialization;
+
+
+namespace SharpLib.Ear
+{
+
+    /// <summary>
+    /// EAR object provides serialization support.
+    /// It assumes most derived class is decorated with AttributeObject.
+    /// </summary>
+    [DataContract]
+    [KnownType("DerivedTypes")]
+    public abstract class Object: IComparable
+    {
+
+        public string Name
+        {
+            //Get the name of this object attribute
+            get { return Utils.Reflection.GetAttribute<AttributeObject>(GetType()).Name; }
+            private set { }
+        }
+
+        public string Description
+        {
+            //Get the description of this object attribute
+            get { return Utils.Reflection.GetAttribute<AttributeObject>(GetType()).Description; }
+            private set { }
+        }
+
+        public virtual string Brief()
+        {
+            return Name;
+        }
+
+        public int CompareTo(object obj)
+        {
+            //Sort by object name
+            return Utils.Reflection.GetAttribute<AttributeObject>(GetType()).Name.CompareTo(obj.GetType());
+        }
+
+        /// <summary>
+        /// So that data contract knows all our types.
+        /// </summary>
+        /// <returns></returns>
+        private static IEnumerable<Type> DerivedTypes()
+        {
+            return SharpLib.Utils.Reflection.GetDerivedTypes<Object>();
+        }
+
+    }
+}
diff -r 1a1c2ae3a29c -r c92587ddabcd SharpLibEar/PropertyFile.cs
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SharpLibEar/PropertyFile.cs	Thu Aug 18 14:35:50 2016 +0200
@@ -0,0 +1,20 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Runtime.Serialization;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace SharpLib.Ear
+{
+    /// <summary>
+    /// File property
+    /// </summary>
+    [DataContract]
+    [AttributeObject(Id = "Property.File", Name = "File", Description = "Holds a full file path.")]
+    public class PropertyFile : Object
+    {
+        [DataMember]
+        public string FullPath { get; set; } = "";
+    }
+}
diff -r 1a1c2ae3a29c -r c92587ddabcd SharpLibEar/SharpLibEar.csproj
--- a/SharpLibEar/SharpLibEar.csproj	Wed Aug 17 16:39:36 2016 +0200
+++ b/SharpLibEar/SharpLibEar.csproj	Thu Aug 18 14:35:50 2016 +0200
@@ -61,6 +61,7 @@
   <ItemGroup>
     <Compile Include="Action.cs" />
     <Compile Include="ActionCallback.cs" />
+    <Compile Include="ActionLaunchApp.cs" />
     <Compile Include="ActionSleep.cs" />
     <Compile Include="ActionType.cs" />
     <Compile Include="AttributeObject.cs" />
@@ -69,7 +70,9 @@
     <Compile Include="EventMonitorPowerOff.cs" />
     <Compile Include="EventMonitorPowerOn.cs" />
     <Compile Include="Manager.cs" />
+    <Compile Include="Object.cs" />
     <Compile Include="Properties\AssemblyInfo.cs" />
+    <Compile Include="PropertyFile.cs" />
   </ItemGroup>
   <ItemGroup>
     <ProjectReference Include="..\SharpLibUtils\SharpLibUtils.csproj">