Adding Harmony command selection dialog.
authorStephaneLenclud
Sun, 21 Aug 2016 18:35:58 +0200
changeset 250b2121d03f6f0
parent 249 cadc4e4302c6
child 251 f60cfcb98c9a
Adding Harmony command selection dialog.
Server/Actions/ActionHarmonyCommand.cs
Server/FormEditObject.cs
Server/FormMain.cs
Server/FormSelectHarmonyCommand.Designer.cs
Server/FormSelectHarmonyCommand.cs
Server/FormSelectHarmonyCommand.resx
Server/SharpDisplayManager.csproj
SharpDisplayManager.sln
SharpLibEar/Object.cs
SharpLibEar/PropertyButton.cs
SharpLibEar/SharpLibEar.csproj
     1.1 --- a/Server/Actions/ActionHarmonyCommand.cs	Sun Aug 21 16:35:20 2016 +0200
     1.2 +++ b/Server/Actions/ActionHarmonyCommand.cs	Sun Aug 21 18:35:58 2016 +0200
     1.3 @@ -6,6 +6,7 @@
     1.4  using System.Text;
     1.5  using System.Threading.Tasks;
     1.6  using System.Runtime.Serialization;
     1.7 +using System.Windows.Forms;
     1.8  
     1.9  namespace SharpDisplayManager
    1.10  {
    1.11 @@ -30,6 +31,14 @@
    1.12          )]
    1.13          public string FunctionName { get; set; } = "";
    1.14  
    1.15 +        [DataMember]
    1.16 +        [AttributeObjectProperty(
    1.17 +        Id = "Harmony.Command.SelectCommand",
    1.18 +        Name = "Select command",
    1.19 +        Description = "Click to select a command."
    1.20 +        )]
    1.21 +        public PropertyButton SelectCommand { get; set; } = new PropertyButton { Text = "None" };
    1.22 +
    1.23          /// <summary>
    1.24          /// 
    1.25          /// </summary>
    1.26 @@ -54,6 +63,18 @@
    1.27              return brief;
    1.28          }
    1.29  
    1.30 +
    1.31 +        protected override void DoConstruct()
    1.32 +        {
    1.33 +            base.DoConstruct();
    1.34 +
    1.35 +            if (SelectCommand == null)
    1.36 +            {
    1.37 +                SelectCommand = new PropertyButton { Text = "None"};
    1.38 +            }
    1.39 +            SelectCommand.ClickEventHandler = ClickEventHandler;
    1.40 +        }
    1.41 +
    1.42          /// <summary>
    1.43          /// 
    1.44          /// </summary>
    1.45 @@ -72,5 +93,52 @@
    1.46              
    1.47          }
    1.48  
    1.49 +
    1.50 +        /// <summary>
    1.51 +        /// 
    1.52 +        /// </summary>
    1.53 +        /// <returns></returns>
    1.54 +        public override bool IsValid()
    1.55 +        {
    1.56 +            if (Program.HarmonyConfig != null)
    1.57 +            {
    1.58 +                foreach (HarmonyHub.Device d in Program.HarmonyConfig.Devices)
    1.59 +                {
    1.60 +                    if (d.Id.Equals(DeviceId))
    1.61 +                    {
    1.62 +                        foreach (HarmonyHub.ControlGroup cg in d.ControlGroups)
    1.63 +                        {
    1.64 +                            foreach (HarmonyHub.Function f in cg.Functions)
    1.65 +                            {
    1.66 +                                if (f.Name.Equals(FunctionName))
    1.67 +                                {
    1.68 +                                    //We found our device and our function
    1.69 +                                    return true;
    1.70 +                                }
    1.71 +                            }
    1.72 +                        }
    1.73 +                    }
    1.74 +                }
    1.75 +            }
    1.76 +
    1.77 +            return false;
    1.78 +        }
    1.79 +
    1.80 +
    1.81 +        void ClickEventHandler(object sender, EventArgs e)
    1.82 +        {
    1.83 +            FormSelectHarmonyCommand dlg = new FormSelectHarmonyCommand();
    1.84 +            DialogResult res = CodeProject.Dialog.DlgBox.ShowDialog(dlg);
    1.85 +            if (res == DialogResult.OK)
    1.86 +            {
    1.87 +                DeviceId = dlg.DeviceId;
    1.88 +                FunctionName = dlg.FunctionName;
    1.89 +                SelectCommand.Text = Brief();
    1.90 +                //Tell observer the object itself changed
    1.91 +                OnPropertyChanged("Brief");
    1.92 +            }
    1.93 +
    1.94 +        }
    1.95 +
    1.96      }
    1.97  }
     2.1 --- a/Server/FormEditObject.cs	Sun Aug 21 16:35:20 2016 +0200
     2.2 +++ b/Server/FormEditObject.cs	Sun Aug 21 18:35:58 2016 +0200
     2.3 @@ -196,6 +196,12 @@
     2.4                  //Not strictly needed but makes sure the set method is called
     2.5                  aInfo.SetValue(aObject, value);                
     2.6              }
     2.7 +            else if (aInfo.PropertyType == typeof(PropertyButton))
     2.8 +            {
     2.9 +                Button ctrl = (Button)aControl;
    2.10 +                PropertyButton value = new PropertyButton { Text = ctrl.Text };
    2.11 +                aInfo.SetValue(aObject, value);
    2.12 +            }
    2.13  
    2.14              //TODO: add support for other types here
    2.15          }
    2.16 @@ -324,6 +330,20 @@
    2.17                  //
    2.18                  return ctrl;
    2.19              }
    2.20 +            else if (aInfo.PropertyType == typeof(PropertyButton))
    2.21 +            {
    2.22 +                // We have a button property
    2.23 +                // Create a button that will trigger the custom action.
    2.24 +                Button ctrl = new Button();
    2.25 +                ctrl.AutoSize = true;
    2.26 +                ctrl.Text = ((PropertyButton)aInfo.GetValue(aObject)).Text;
    2.27 +                // Hook in click event
    2.28 +                ctrl.Click += ((PropertyButton)aInfo.GetValue(aObject)).ClickEventHandler;
    2.29 +                // Hook-in change notification after setting the value 
    2.30 +                ctrl.TextChanged += ControlValueChanged;
    2.31 +                return ctrl;
    2.32 +            }
    2.33 +
    2.34              //TODO: add support for other control type here
    2.35              return null;
    2.36          }
    2.37 @@ -358,6 +378,10 @@
    2.38              {
    2.39                  return true;
    2.40              }
    2.41 +            else if (aInfo.PropertyType == typeof(PropertyButton))
    2.42 +            {
    2.43 +                return true;
    2.44 +            }
    2.45  
    2.46              //TODO: add support for other type here
    2.47  
    2.48 @@ -465,7 +489,17 @@
    2.49  
    2.50                  // Our object has changed behind our back.
    2.51                  // That's currently only the case for HID events that are listening for inputs.
    2.52 -                UpdateStaticControls();
    2.53 +                if (Object is EventHid)
    2.54 +                {
    2.55 +                    //HID can't do full control updates for some reason
    2.56 +                    //We are getting spammed with HID events after a few clicks
    2.57 +                    //We need to investigate, HID bug?
    2.58 +                    UpdateStaticControls();
    2.59 +                }
    2.60 +                else
    2.61 +                {
    2.62 +                    UpdateControls();
    2.63 +                }
    2.64              }
    2.65          }
    2.66  
     3.1 --- a/Server/FormMain.cs	Sun Aug 21 16:35:20 2016 +0200
     3.2 +++ b/Server/FormMain.cs	Sun Aug 21 18:35:58 2016 +0200
     3.3 @@ -2576,6 +2576,8 @@
     3.4                  try
     3.5                  {
     3.6                      await ConnectHarmonyAsync();
     3.7 +                    //To make sure harmony commands are showing device name instead of device id
     3.8 +                    PopulateEventsTreeView();
     3.9                  }
    3.10                  finally
    3.11                  {
    3.12 @@ -2892,6 +2894,11 @@
    3.13              }
    3.14          }
    3.15  
    3.16 +        /// <summary>
    3.17 +        /// 
    3.18 +        /// </summary>
    3.19 +        /// <param name="sender"></param>
    3.20 +        /// <param name="e"></param>
    3.21          private void buttonEventAdd_Click(object sender, EventArgs e)
    3.22          {
    3.23              FormEditObject<Ear.Event> ea = new FormEditObject<Ear.Event>();
    3.24 @@ -2906,6 +2913,11 @@
    3.25              }
    3.26          }
    3.27  
    3.28 +        /// <summary>
    3.29 +        /// 
    3.30 +        /// </summary>
    3.31 +        /// <param name="sender"></param>
    3.32 +        /// <param name="e"></param>
    3.33          private void buttonEventDelete_Click(object sender, EventArgs e)
    3.34          {
    3.35              Ear.Event currentEvent = CurrentEvent();
    3.36 @@ -2920,6 +2932,11 @@
    3.37              PopulateEventsTreeView();
    3.38          }
    3.39  
    3.40 +        /// <summary>
    3.41 +        /// 
    3.42 +        /// </summary>
    3.43 +        /// <param name="sender"></param>
    3.44 +        /// <param name="e"></param>
    3.45          private void buttonEventEdit_Click(object sender, EventArgs e)
    3.46          {
    3.47              Ear.Event selectedEvent = CurrentEvent();
    3.48 @@ -2946,12 +2963,22 @@
    3.49              }
    3.50          }
    3.51  
    3.52 +        /// <summary>
    3.53 +        /// 
    3.54 +        /// </summary>
    3.55 +        /// <param name="sender"></param>
    3.56 +        /// <param name="e"></param>
    3.57          private void iTreeViewEvents_Leave(object sender, EventArgs e)
    3.58          {
    3.59              //Make sure our event tree never looses focus
    3.60              ((TreeView) sender).Focus();
    3.61          }
    3.62  
    3.63 +        /// <summary>
    3.64 +        /// 
    3.65 +        /// </summary>
    3.66 +        /// <param name="sender"></param>
    3.67 +        /// <param name="e"></param>
    3.68          private async void iButtonHarmonyConnect_Click(object sender, EventArgs e)
    3.69          {
    3.70              //Save hub address
    3.71 @@ -2970,7 +2997,10 @@
    3.72  
    3.73          }
    3.74  
    3.75 -
    3.76 +        /// <summary>
    3.77 +        /// 
    3.78 +        /// </summary>
    3.79 +        /// <returns></returns>
    3.80          private async Task ConnectHarmonyAsync()
    3.81          {
    3.82              if (Program.HarmonyClient != null)
     4.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     4.2 +++ b/Server/FormSelectHarmonyCommand.Designer.cs	Sun Aug 21 18:35:58 2016 +0200
     4.3 @@ -0,0 +1,94 @@
     4.4 +namespace SharpDisplayManager
     4.5 +{
     4.6 +    partial class FormSelectHarmonyCommand
     4.7 +    {
     4.8 +        /// <summary>
     4.9 +        /// Required designer variable.
    4.10 +        /// </summary>
    4.11 +        private System.ComponentModel.IContainer components = null;
    4.12 +
    4.13 +        /// <summary>
    4.14 +        /// Clean up any resources being used.
    4.15 +        /// </summary>
    4.16 +        /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
    4.17 +        protected override void Dispose(bool disposing)
    4.18 +        {
    4.19 +            if (disposing && (components != null))
    4.20 +            {
    4.21 +                components.Dispose();
    4.22 +            }
    4.23 +            base.Dispose(disposing);
    4.24 +        }
    4.25 +
    4.26 +        #region Windows Form Designer generated code
    4.27 +
    4.28 +        /// <summary>
    4.29 +        /// Required method for Designer support - do not modify
    4.30 +        /// the contents of this method with the code editor.
    4.31 +        /// </summary>
    4.32 +        private void InitializeComponent()
    4.33 +        {
    4.34 +            this.iTreeViewHarmony = new System.Windows.Forms.TreeView();
    4.35 +            this.buttonCancel = new System.Windows.Forms.Button();
    4.36 +            this.iButtonOk = new System.Windows.Forms.Button();
    4.37 +            this.SuspendLayout();
    4.38 +            // 
    4.39 +            // iTreeViewHarmony
    4.40 +            // 
    4.41 +            this.iTreeViewHarmony.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
    4.42 +            | System.Windows.Forms.AnchorStyles.Left) 
    4.43 +            | System.Windows.Forms.AnchorStyles.Right)));
    4.44 +            this.iTreeViewHarmony.Location = new System.Drawing.Point(12, 12);
    4.45 +            this.iTreeViewHarmony.Name = "iTreeViewHarmony";
    4.46 +            this.iTreeViewHarmony.Size = new System.Drawing.Size(302, 317);
    4.47 +            this.iTreeViewHarmony.TabIndex = 0;
    4.48 +            this.iTreeViewHarmony.AfterSelect += new System.Windows.Forms.TreeViewEventHandler(this.iTreeViewHarmony_AfterSelect);
    4.49 +            this.iTreeViewHarmony.NodeMouseDoubleClick += new System.Windows.Forms.TreeNodeMouseClickEventHandler(this.iTreeViewHarmony_NodeMouseDoubleClick);
    4.50 +            // 
    4.51 +            // buttonCancel
    4.52 +            // 
    4.53 +            this.buttonCancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
    4.54 +            this.buttonCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel;
    4.55 +            this.buttonCancel.Location = new System.Drawing.Point(93, 335);
    4.56 +            this.buttonCancel.Name = "buttonCancel";
    4.57 +            this.buttonCancel.Size = new System.Drawing.Size(75, 23);
    4.58 +            this.buttonCancel.TabIndex = 24;
    4.59 +            this.buttonCancel.Text = "Cancel";
    4.60 +            this.buttonCancel.UseVisualStyleBackColor = true;
    4.61 +            // 
    4.62 +            // iButtonOk
    4.63 +            // 
    4.64 +            this.iButtonOk.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
    4.65 +            this.iButtonOk.DialogResult = System.Windows.Forms.DialogResult.OK;
    4.66 +            this.iButtonOk.Enabled = false;
    4.67 +            this.iButtonOk.Location = new System.Drawing.Point(12, 335);
    4.68 +            this.iButtonOk.Name = "iButtonOk";
    4.69 +            this.iButtonOk.Size = new System.Drawing.Size(75, 23);
    4.70 +            this.iButtonOk.TabIndex = 23;
    4.71 +            this.iButtonOk.Text = "Ok";
    4.72 +            this.iButtonOk.UseVisualStyleBackColor = true;
    4.73 +            // 
    4.74 +            // FormSelectHarmonyCommand
    4.75 +            // 
    4.76 +            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
    4.77 +            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
    4.78 +            this.ClientSize = new System.Drawing.Size(326, 370);
    4.79 +            this.Controls.Add(this.buttonCancel);
    4.80 +            this.Controls.Add(this.iButtonOk);
    4.81 +            this.Controls.Add(this.iTreeViewHarmony);
    4.82 +            this.MaximizeBox = false;
    4.83 +            this.MinimizeBox = false;
    4.84 +            this.Name = "FormSelectHarmonyCommand";
    4.85 +            this.Text = "Select command";
    4.86 +            this.Load += new System.EventHandler(this.FormSelectHarmonyCommand_Load);
    4.87 +            this.ResumeLayout(false);
    4.88 +
    4.89 +        }
    4.90 +
    4.91 +        #endregion
    4.92 +
    4.93 +        private System.Windows.Forms.TreeView iTreeViewHarmony;
    4.94 +        private System.Windows.Forms.Button buttonCancel;
    4.95 +        private System.Windows.Forms.Button iButtonOk;
    4.96 +    }
    4.97 +}
    4.98 \ No newline at end of file
     5.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     5.2 +++ b/Server/FormSelectHarmonyCommand.cs	Sun Aug 21 18:35:58 2016 +0200
     5.3 @@ -0,0 +1,109 @@
     5.4 +using System;
     5.5 +using System.Collections.Generic;
     5.6 +using System.ComponentModel;
     5.7 +using System.Data;
     5.8 +using System.Drawing;
     5.9 +using System.Linq;
    5.10 +using System.Text;
    5.11 +using System.Threading.Tasks;
    5.12 +using System.Windows.Forms;
    5.13 +
    5.14 +namespace SharpDisplayManager
    5.15 +{
    5.16 +    public partial class FormSelectHarmonyCommand : Form
    5.17 +    {
    5.18 +        public FormSelectHarmonyCommand()
    5.19 +        {
    5.20 +            InitializeComponent();
    5.21 +        }
    5.22 +
    5.23 +        public string DeviceId;
    5.24 +        public string FunctionName;
    5.25 +
    5.26 +        /// <summary>
    5.27 +        /// 
    5.28 +        /// </summary>
    5.29 +        /// <param name="sender"></param>
    5.30 +        /// <param name="e"></param>
    5.31 +        private void FormSelectHarmonyCommand_Load(object sender, EventArgs e)
    5.32 +        {
    5.33 +            PopulateTreeViewHarmony(Program.HarmonyConfig);
    5.34 +        }
    5.35 +
    5.36 +        /// <summary>
    5.37 +        /// 
    5.38 +        /// </summary>
    5.39 +        /// <param name="aConfig"></param>
    5.40 +        private void PopulateTreeViewHarmony(HarmonyHub.Config aConfig)
    5.41 +        {
    5.42 +            iTreeViewHarmony.Nodes.Clear();
    5.43 +            //Add our devices
    5.44 +            foreach (HarmonyHub.Device device in aConfig.Devices)
    5.45 +            {
    5.46 +                TreeNode deviceNode = iTreeViewHarmony.Nodes.Add(device.Id, $"{device.Label} ({device.DeviceTypeDisplayName}/{device.Model})");
    5.47 +                deviceNode.Tag = device;
    5.48 +
    5.49 +                foreach (HarmonyHub.ControlGroup cg in device.ControlGroups)
    5.50 +                {
    5.51 +                    TreeNode cgNode = deviceNode.Nodes.Add(cg.Name);
    5.52 +                    cgNode.Tag = cg;
    5.53 +
    5.54 +                    foreach (HarmonyHub.Function f in cg.Functions)
    5.55 +                    {
    5.56 +                        TreeNode fNode = cgNode.Nodes.Add(f.Name);
    5.57 +                        fNode.Tag = f;
    5.58 +                    }
    5.59 +                }
    5.60 +            }
    5.61 +
    5.62 +            //treeViewConfig.ExpandAll();
    5.63 +        }
    5.64 +
    5.65 +        /// <summary>
    5.66 +        /// 
    5.67 +        /// </summary>
    5.68 +        /// <param name="sender"></param>
    5.69 +        /// <param name="e"></param>
    5.70 +        private async void iTreeViewHarmony_NodeMouseDoubleClick(object sender, TreeNodeMouseClickEventArgs e)
    5.71 +        {
    5.72 +            //Upon function node double click we execute it, so that user can test
    5.73 +            var tag = e.Node.Tag as HarmonyHub.Function;
    5.74 +            if (tag != null && e.Node.Parent.Parent.Tag is HarmonyHub.Device)
    5.75 +            {
    5.76 +                HarmonyHub.Function f = tag;
    5.77 +                HarmonyHub.Device d = (HarmonyHub.Device)e.Node.Parent.Parent.Tag;
    5.78 +
    5.79 +                Console.WriteLine($"Harmony: Sending {f.Name} to {d.Label}...");
    5.80 +
    5.81 +                await Program.HarmonyClient.SendCommandAsync(d.Id, f.Name);
    5.82 +            }
    5.83 +        }
    5.84 +
    5.85 +        /// <summary>
    5.86 +        /// 
    5.87 +        /// </summary>
    5.88 +        /// <param name="sender"></param>
    5.89 +        /// <param name="e"></param>
    5.90 +        private void iTreeViewHarmony_AfterSelect(object sender, TreeViewEventArgs e)
    5.91 +        {
    5.92 +            //Enable ok button if a function is selected
    5.93 +            iButtonOk.Enabled = e.Node.Tag is HarmonyHub.Function;
    5.94 +
    5.95 +            // Get selected device ID and function name
    5.96 +            HarmonyHub.Function f = e.Node.Tag as HarmonyHub.Function;
    5.97 +            if (f != null && e.Node.Parent.Parent.Tag is HarmonyHub.Device)
    5.98 +            {
    5.99 +                HarmonyHub.Device d = (HarmonyHub.Device)e.Node.Parent.Parent.Tag;
   5.100 +
   5.101 +                DeviceId = d.Id;
   5.102 +                FunctionName = f.Name;
   5.103 +            }
   5.104 +            else
   5.105 +            {
   5.106 +                DeviceId = "";
   5.107 +                FunctionName = "";
   5.108 +            }
   5.109 +
   5.110 +        }
   5.111 +    }
   5.112 +}
     6.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     6.2 +++ b/Server/FormSelectHarmonyCommand.resx	Sun Aug 21 18:35:58 2016 +0200
     6.3 @@ -0,0 +1,120 @@
     6.4 +<?xml version="1.0" encoding="utf-8"?>
     6.5 +<root>
     6.6 +  <!-- 
     6.7 +    Microsoft ResX Schema 
     6.8 +    
     6.9 +    Version 2.0
    6.10 +    
    6.11 +    The primary goals of this format is to allow a simple XML format 
    6.12 +    that is mostly human readable. The generation and parsing of the 
    6.13 +    various data types are done through the TypeConverter classes 
    6.14 +    associated with the data types.
    6.15 +    
    6.16 +    Example:
    6.17 +    
    6.18 +    ... ado.net/XML headers & schema ...
    6.19 +    <resheader name="resmimetype">text/microsoft-resx</resheader>
    6.20 +    <resheader name="version">2.0</resheader>
    6.21 +    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
    6.22 +    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
    6.23 +    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
    6.24 +    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
    6.25 +    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
    6.26 +        <value>[base64 mime encoded serialized .NET Framework object]</value>
    6.27 +    </data>
    6.28 +    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
    6.29 +        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
    6.30 +        <comment>This is a comment</comment>
    6.31 +    </data>
    6.32 +                
    6.33 +    There are any number of "resheader" rows that contain simple 
    6.34 +    name/value pairs.
    6.35 +    
    6.36 +    Each data row contains a name, and value. The row also contains a 
    6.37 +    type or mimetype. Type corresponds to a .NET class that support 
    6.38 +    text/value conversion through the TypeConverter architecture. 
    6.39 +    Classes that don't support this are serialized and stored with the 
    6.40 +    mimetype set.
    6.41 +    
    6.42 +    The mimetype is used for serialized objects, and tells the 
    6.43 +    ResXResourceReader how to depersist the object. This is currently not 
    6.44 +    extensible. For a given mimetype the value must be set accordingly:
    6.45 +    
    6.46 +    Note - application/x-microsoft.net.object.binary.base64 is the format 
    6.47 +    that the ResXResourceWriter will generate, however the reader can 
    6.48 +    read any of the formats listed below.
    6.49 +    
    6.50 +    mimetype: application/x-microsoft.net.object.binary.base64
    6.51 +    value   : The object must be serialized with 
    6.52 +            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
    6.53 +            : and then encoded with base64 encoding.
    6.54 +    
    6.55 +    mimetype: application/x-microsoft.net.object.soap.base64
    6.56 +    value   : The object must be serialized with 
    6.57 +            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
    6.58 +            : and then encoded with base64 encoding.
    6.59 +
    6.60 +    mimetype: application/x-microsoft.net.object.bytearray.base64
    6.61 +    value   : The object must be serialized into a byte array 
    6.62 +            : using a System.ComponentModel.TypeConverter
    6.63 +            : and then encoded with base64 encoding.
    6.64 +    -->
    6.65 +  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
    6.66 +    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
    6.67 +    <xsd:element name="root" msdata:IsDataSet="true">
    6.68 +      <xsd:complexType>
    6.69 +        <xsd:choice maxOccurs="unbounded">
    6.70 +          <xsd:element name="metadata">
    6.71 +            <xsd:complexType>
    6.72 +              <xsd:sequence>
    6.73 +                <xsd:element name="value" type="xsd:string" minOccurs="0" />
    6.74 +              </xsd:sequence>
    6.75 +              <xsd:attribute name="name" use="required" type="xsd:string" />
    6.76 +              <xsd:attribute name="type" type="xsd:string" />
    6.77 +              <xsd:attribute name="mimetype" type="xsd:string" />
    6.78 +              <xsd:attribute ref="xml:space" />
    6.79 +            </xsd:complexType>
    6.80 +          </xsd:element>
    6.81 +          <xsd:element name="assembly">
    6.82 +            <xsd:complexType>
    6.83 +              <xsd:attribute name="alias" type="xsd:string" />
    6.84 +              <xsd:attribute name="name" type="xsd:string" />
    6.85 +            </xsd:complexType>
    6.86 +          </xsd:element>
    6.87 +          <xsd:element name="data">
    6.88 +            <xsd:complexType>
    6.89 +              <xsd:sequence>
    6.90 +                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
    6.91 +                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
    6.92 +              </xsd:sequence>
    6.93 +              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
    6.94 +              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
    6.95 +              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
    6.96 +              <xsd:attribute ref="xml:space" />
    6.97 +            </xsd:complexType>
    6.98 +          </xsd:element>
    6.99 +          <xsd:element name="resheader">
   6.100 +            <xsd:complexType>
   6.101 +              <xsd:sequence>
   6.102 +                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
   6.103 +              </xsd:sequence>
   6.104 +              <xsd:attribute name="name" type="xsd:string" use="required" />
   6.105 +            </xsd:complexType>
   6.106 +          </xsd:element>
   6.107 +        </xsd:choice>
   6.108 +      </xsd:complexType>
   6.109 +    </xsd:element>
   6.110 +  </xsd:schema>
   6.111 +  <resheader name="resmimetype">
   6.112 +    <value>text/microsoft-resx</value>
   6.113 +  </resheader>
   6.114 +  <resheader name="version">
   6.115 +    <value>2.0</value>
   6.116 +  </resheader>
   6.117 +  <resheader name="reader">
   6.118 +    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
   6.119 +  </resheader>
   6.120 +  <resheader name="writer">
   6.121 +    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
   6.122 +  </resheader>
   6.123 +</root>
   6.124 \ No newline at end of file
     7.1 --- a/Server/SharpDisplayManager.csproj	Sun Aug 21 16:35:20 2016 +0200
     7.2 +++ b/Server/SharpDisplayManager.csproj	Sun Aug 21 18:35:58 2016 +0200
     7.3 @@ -34,7 +34,7 @@
     7.4      <WebPage>index.htm</WebPage>
     7.5      <OpenBrowserOnPublish>false</OpenBrowserOnPublish>
     7.6      <ApplicationRevision>0</ApplicationRevision>
     7.7 -    <ApplicationVersion>0.11.0.0</ApplicationVersion>
     7.8 +    <ApplicationVersion>0.12.0.0</ApplicationVersion>
     7.9      <UseApplicationTrust>false</UseApplicationTrust>
    7.10      <CreateDesktopShortcut>true</CreateDesktopShortcut>
    7.11      <PublishWizardCompleted>true</PublishWizardCompleted>
    7.12 @@ -193,6 +193,12 @@
    7.13        <DependentUpon>FormMain.cs</DependentUpon>
    7.14      </Compile>
    7.15      <Compile Include="FormMain.Hid.cs" />
    7.16 +    <Compile Include="FormSelectHarmonyCommand.cs">
    7.17 +      <SubType>Form</SubType>
    7.18 +    </Compile>
    7.19 +    <Compile Include="FormSelectHarmonyCommand.Designer.cs">
    7.20 +      <DependentUpon>FormSelectHarmonyCommand.cs</DependentUpon>
    7.21 +    </Compile>
    7.22      <Compile Include="FxControl.cs">
    7.23        <SubType>UserControl</SubType>
    7.24      </Compile>
    7.25 @@ -227,6 +233,9 @@
    7.26      <EmbeddedResource Include="FormMain.resx">
    7.27        <DependentUpon>FormMain.cs</DependentUpon>
    7.28      </EmbeddedResource>
    7.29 +    <EmbeddedResource Include="FormSelectHarmonyCommand.resx">
    7.30 +      <DependentUpon>FormSelectHarmonyCommand.cs</DependentUpon>
    7.31 +    </EmbeddedResource>
    7.32      <EmbeddedResource Include="FxControl.resx">
    7.33        <DependentUpon>FxControl.cs</DependentUpon>
    7.34      </EmbeddedResource>
    7.35 @@ -288,9 +297,9 @@
    7.36      </ProjectReference>
    7.37    </ItemGroup>
    7.38    <ItemGroup>
    7.39 -    <BootstrapperPackage Include=".NETFramework,Version=v4.0">
    7.40 +    <BootstrapperPackage Include=".NETFramework,Version=v4.6">
    7.41        <Visible>False</Visible>
    7.42 -      <ProductName>Microsoft .NET Framework 4 %28x86 and x64%29</ProductName>
    7.43 +      <ProductName>Microsoft .NET Framework 4.6 %28x86 and x64%29</ProductName>
    7.44        <Install>true</Install>
    7.45      </BootstrapperPackage>
    7.46      <BootstrapperPackage Include="Microsoft.Net.Client.3.5">
     8.1 --- a/SharpDisplayManager.sln	Sun Aug 21 16:35:20 2016 +0200
     8.2 +++ b/SharpDisplayManager.sln	Sun Aug 21 18:35:58 2016 +0200
     8.3 @@ -61,8 +61,8 @@
     8.4  		{A76579E5-AA8D-45A3-99E1-239A5C030A78}.Debug|Any CPU.Build.0 = Debug|Any CPU
     8.5  		{A76579E5-AA8D-45A3-99E1-239A5C030A78}.Debug|x64.ActiveCfg = Debug|Any CPU
     8.6  		{A76579E5-AA8D-45A3-99E1-239A5C030A78}.Debug|x64.Build.0 = Debug|Any CPU
     8.7 -		{A76579E5-AA8D-45A3-99E1-239A5C030A78}.Debug|x86.ActiveCfg = Debug|Any CPU
     8.8 -		{A76579E5-AA8D-45A3-99E1-239A5C030A78}.Debug|x86.Build.0 = Debug|Any CPU
     8.9 +		{A76579E5-AA8D-45A3-99E1-239A5C030A78}.Debug|x86.ActiveCfg = Debug|x86
    8.10 +		{A76579E5-AA8D-45A3-99E1-239A5C030A78}.Debug|x86.Build.0 = Debug|x86
    8.11  		{A76579E5-AA8D-45A3-99E1-239A5C030A78}.Release|Any CPU.ActiveCfg = Release|Any CPU
    8.12  		{A76579E5-AA8D-45A3-99E1-239A5C030A78}.Release|Any CPU.Build.0 = Release|Any CPU
    8.13  		{A76579E5-AA8D-45A3-99E1-239A5C030A78}.Release|x64.ActiveCfg = Release|Any CPU
    8.14 @@ -85,8 +85,8 @@
    8.15  		{AE897704-461D-4018-8336-2517988BF7AD}.Debug|Any CPU.Build.0 = Debug|Any CPU
    8.16  		{AE897704-461D-4018-8336-2517988BF7AD}.Debug|x64.ActiveCfg = Debug|Any CPU
    8.17  		{AE897704-461D-4018-8336-2517988BF7AD}.Debug|x64.Build.0 = Debug|Any CPU
    8.18 -		{AE897704-461D-4018-8336-2517988BF7AD}.Debug|x86.ActiveCfg = Debug|Any CPU
    8.19 -		{AE897704-461D-4018-8336-2517988BF7AD}.Debug|x86.Build.0 = Debug|Any CPU
    8.20 +		{AE897704-461D-4018-8336-2517988BF7AD}.Debug|x86.ActiveCfg = Debug|x86
    8.21 +		{AE897704-461D-4018-8336-2517988BF7AD}.Debug|x86.Build.0 = Debug|x86
    8.22  		{AE897704-461D-4018-8336-2517988BF7AD}.Release|Any CPU.ActiveCfg = Release|Any CPU
    8.23  		{AE897704-461D-4018-8336-2517988BF7AD}.Release|Any CPU.Build.0 = Release|Any CPU
    8.24  		{AE897704-461D-4018-8336-2517988BF7AD}.Release|x64.ActiveCfg = Release|Any CPU
    8.25 @@ -97,8 +97,8 @@
    8.26  		{84A9ED37-E6EA-4CBD-B995-B713F46EAAB0}.Debug|Any CPU.Build.0 = Debug|Any CPU
    8.27  		{84A9ED37-E6EA-4CBD-B995-B713F46EAAB0}.Debug|x64.ActiveCfg = Debug|Any CPU
    8.28  		{84A9ED37-E6EA-4CBD-B995-B713F46EAAB0}.Debug|x64.Build.0 = Debug|Any CPU
    8.29 -		{84A9ED37-E6EA-4CBD-B995-B713F46EAAB0}.Debug|x86.ActiveCfg = Debug|Any CPU
    8.30 -		{84A9ED37-E6EA-4CBD-B995-B713F46EAAB0}.Debug|x86.Build.0 = Debug|Any CPU
    8.31 +		{84A9ED37-E6EA-4CBD-B995-B713F46EAAB0}.Debug|x86.ActiveCfg = Debug|x86
    8.32 +		{84A9ED37-E6EA-4CBD-B995-B713F46EAAB0}.Debug|x86.Build.0 = Debug|x86
    8.33  		{84A9ED37-E6EA-4CBD-B995-B713F46EAAB0}.Release|Any CPU.ActiveCfg = Release|Any CPU
    8.34  		{84A9ED37-E6EA-4CBD-B995-B713F46EAAB0}.Release|Any CPU.Build.0 = Release|Any CPU
    8.35  		{84A9ED37-E6EA-4CBD-B995-B713F46EAAB0}.Release|x64.ActiveCfg = Release|Any CPU
    8.36 @@ -109,8 +109,8 @@
    8.37  		{D9AAD299-E97F-47E0-8E92-110F49F2B89C}.Debug|Any CPU.Build.0 = Debug|Any CPU
    8.38  		{D9AAD299-E97F-47E0-8E92-110F49F2B89C}.Debug|x64.ActiveCfg = Debug|Any CPU
    8.39  		{D9AAD299-E97F-47E0-8E92-110F49F2B89C}.Debug|x64.Build.0 = Debug|Any CPU
    8.40 -		{D9AAD299-E97F-47E0-8E92-110F49F2B89C}.Debug|x86.ActiveCfg = Debug|Any CPU
    8.41 -		{D9AAD299-E97F-47E0-8E92-110F49F2B89C}.Debug|x86.Build.0 = Debug|Any CPU
    8.42 +		{D9AAD299-E97F-47E0-8E92-110F49F2B89C}.Debug|x86.ActiveCfg = Debug|x86
    8.43 +		{D9AAD299-E97F-47E0-8E92-110F49F2B89C}.Debug|x86.Build.0 = Debug|x86
    8.44  		{D9AAD299-E97F-47E0-8E92-110F49F2B89C}.Release|Any CPU.ActiveCfg = Release|Any CPU
    8.45  		{D9AAD299-E97F-47E0-8E92-110F49F2B89C}.Release|Any CPU.Build.0 = Release|Any CPU
    8.46  		{D9AAD299-E97F-47E0-8E92-110F49F2B89C}.Release|x64.ActiveCfg = Release|Any CPU
     9.1 --- a/SharpLibEar/Object.cs	Sun Aug 21 16:35:20 2016 +0200
     9.2 +++ b/SharpLibEar/Object.cs	Sun Aug 21 18:35:58 2016 +0200
     9.3 @@ -3,10 +3,6 @@
     9.4  using System.Linq;
     9.5  using System.Text;
     9.6  using System.Threading.Tasks;
     9.7 -
     9.8 -
     9.9 -using System;
    9.10 -using System.Collections.Generic;
    9.11  using System.Runtime.Serialization;
    9.12  using System.ComponentModel;
    9.13  
    10.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    10.2 +++ b/SharpLibEar/PropertyButton.cs	Sun Aug 21 18:35:58 2016 +0200
    10.3 @@ -0,0 +1,22 @@
    10.4 +using System;
    10.5 +using System.Collections.Generic;
    10.6 +using System.Linq;
    10.7 +using System.Runtime.Serialization;
    10.8 +using System.Text;
    10.9 +using System.Threading.Tasks;
   10.10 +
   10.11 +namespace SharpLib.Ear
   10.12 +{
   10.13 +    /// <summary>
   10.14 +    /// Generic button property
   10.15 +    /// </summary>
   10.16 +    [DataContract]
   10.17 +    [AttributeObject(Id = "Property.Button", Name = "Button", Description = "Button property.")]
   10.18 +    public class PropertyButton : Object
   10.19 +    {
   10.20 +        public EventHandler ClickEventHandler;
   10.21 +
   10.22 +        [DataMember]
   10.23 +        public string Text { get; set; }
   10.24 +    }
   10.25 +}
    11.1 --- a/SharpLibEar/SharpLibEar.csproj	Sun Aug 21 16:35:20 2016 +0200
    11.2 +++ b/SharpLibEar/SharpLibEar.csproj	Sun Aug 21 18:35:58 2016 +0200
    11.3 @@ -77,6 +77,7 @@
    11.4      <Compile Include="Manager.cs" />
    11.5      <Compile Include="Object.cs" />
    11.6      <Compile Include="Properties\AssemblyInfo.cs" />
    11.7 +    <Compile Include="PropertyButton.cs" />
    11.8      <Compile Include="PropertyComboBox.cs" />
    11.9      <Compile Include="PropertyFile.cs" />
   11.10    </ItemGroup>