Server/FormMain.cs
author StephaneLenclud
Fri, 12 Aug 2016 20:25:05 +0200
changeset 231 4c706feaf706
parent 229 7c631055b94b
child 232 5a739e2e5255
permissions -rw-r--r--
Events can now be instantiated.
Action editor is now a generic object editor.
     1 //
     2 // Copyright (C) 2014-2015 Stéphane Lenclud.
     3 //
     4 // This file is part of SharpDisplayManager.
     5 //
     6 // SharpDisplayManager is free software: you can redistribute it and/or modify
     7 // it under the terms of the GNU General Public License as published by
     8 // the Free Software Foundation, either version 3 of the License, or
     9 // (at your option) any later version.
    10 //
    11 // SharpDisplayManager is distributed in the hope that it will be useful,
    12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
    13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    14 // GNU General Public License for more details.
    15 //
    16 // You should have received a copy of the GNU General Public License
    17 // along with SharpDisplayManager.  If not, see <http://www.gnu.org/licenses/>.
    18 //
    19 
    20 using System;
    21 using System.Collections.Generic;
    22 using System.ComponentModel;
    23 using System.Data;
    24 using System.Drawing;
    25 using System.Linq;
    26 using System.Text;
    27 using System.Threading.Tasks;
    28 using System.Windows.Forms;
    29 using System.IO;
    30 using CodeProject.Dialog;
    31 using System.Drawing.Imaging;
    32 using System.ServiceModel;
    33 using System.Threading;
    34 using System.Diagnostics;
    35 using System.Deployment.Application;
    36 using System.Reflection;
    37 //NAudio
    38 using NAudio.CoreAudioApi;
    39 using NAudio.CoreAudioApi.Interfaces;
    40 using System.Runtime.InteropServices;
    41 using CecSharp;
    42 //Network
    43 using NETWORKLIST;
    44 //
    45 using SharpDisplayClient;
    46 using SharpDisplay;
    47 using MiniDisplayInterop;
    48 using SharpLib.Display;
    49 using SharpLib.Ear;
    50 
    51 namespace SharpDisplayManager
    52 {
    53     //Types declarations
    54     public delegate uint ColorProcessingDelegate(int aX, int aY, uint aPixel);
    55 
    56     public delegate int CoordinateTranslationDelegate(System.Drawing.Bitmap aBmp, int aInt);
    57 
    58     //Delegates are used for our thread safe method
    59     public delegate void AddClientDelegate(string aSessionId, ICallback aCallback);
    60 
    61     public delegate void RemoveClientDelegate(string aSessionId);
    62 
    63     public delegate void SetFieldDelegate(string SessionId, DataField aField);
    64 
    65     public delegate void SetFieldsDelegate(string SessionId, System.Collections.Generic.IList<DataField> aFields);
    66 
    67     public delegate void SetLayoutDelegate(string SessionId, TableLayout aLayout);
    68 
    69     public delegate void SetClientNameDelegate(string aSessionId, string aName);
    70 
    71     public delegate void SetClientPriorityDelegate(string aSessionId, uint aPriority);
    72 
    73     public delegate void PlainUpdateDelegate();
    74 
    75     public delegate void WndProcDelegate(ref Message aMessage);
    76 
    77     /// <summary>
    78     /// Our Display manager main form
    79     /// </summary>
    80     [System.ComponentModel.DesignerCategory("Form")]
    81     public partial class FormMain : FormMainHid, IMMNotificationClient
    82     {
    83         //public ManagerEventAction iManager = new ManagerEventAction();        
    84         DateTime LastTickTime;
    85         Display iDisplay;
    86         System.Drawing.Bitmap iBmp;
    87         bool iCreateBitmap; //Workaround render to bitmap issues when minimized
    88         ServiceHost iServiceHost;
    89         // Our collection of clients sorted by session id.
    90         public Dictionary<string, ClientData> iClients;
    91         // The name of the client which informations are currently displayed.
    92         public string iCurrentClientSessionId;
    93         ClientData iCurrentClientData;
    94         //
    95         public bool iClosing;
    96         //
    97         public bool iSkipFrameRendering;
    98         //Function pointer for pixel color filtering
    99         ColorProcessingDelegate iColorFx;
   100         //Function pointer for pixel X coordinate intercept
   101         CoordinateTranslationDelegate iScreenX;
   102         //Function pointer for pixel Y coordinate intercept
   103         CoordinateTranslationDelegate iScreenY;
   104         //NAudio
   105         private MMDeviceEnumerator iMultiMediaDeviceEnumerator;
   106         private MMDevice iMultiMediaDevice;
   107         //Network
   108         private NetworkManager iNetworkManager;
   109 
   110         /// <summary>
   111         /// CEC - Consumer Electronic Control.
   112         /// Notably used to turn TV on and off as Windows broadcast monitor on and off notifications.
   113         /// </summary>
   114         private ConsumerElectronicControl iCecManager;
   115 
   116         /// <summary>
   117         /// Manage run when Windows startup option
   118         /// </summary>
   119         private StartupManager iStartupManager;
   120 
   121         /// <summary>
   122         /// System notification icon used to hide our application from the task bar.
   123         /// </summary>
   124         private SharpLib.Notification.Control iNotifyIcon;
   125 
   126         /// <summary>
   127         /// System recording notification icon.
   128         /// </summary>
   129         private SharpLib.Notification.Control iRecordingNotification;
   130 
   131         /// <summary>
   132         /// 
   133         /// </summary>
   134         RichTextBoxTextWriter iWriter;
   135 
   136 
   137         /// <summary>
   138         /// Allow user to receive window messages;
   139         /// </summary>
   140         public event WndProcDelegate OnWndProc;
   141 
   142         public FormMain()
   143         {
   144             ManagerEventAction.Current = Properties.Settings.Default.Actions;
   145             if (ManagerEventAction.Current == null)
   146             {
   147                 //No actions in our settings yet
   148                 ManagerEventAction.Current = new ManagerEventAction();
   149                 Properties.Settings.Default.Actions = ManagerEventAction.Current;
   150             }
   151             else
   152             {
   153                 //We loaded actions from our settings
   154                 //We need to hook them with corresponding events
   155                 ManagerEventAction.Current.Init();
   156             }
   157             iSkipFrameRendering = false;
   158             iClosing = false;
   159             iCurrentClientSessionId = "";
   160             iCurrentClientData = null;
   161             LastTickTime = DateTime.Now;
   162             //Instantiate our display and register for events notifications
   163             iDisplay = new Display();
   164             iDisplay.OnOpened += OnDisplayOpened;
   165             iDisplay.OnClosed += OnDisplayClosed;
   166             //
   167             iClients = new Dictionary<string, ClientData>();
   168             iStartupManager = new StartupManager();
   169             iNotifyIcon = new SharpLib.Notification.Control();
   170             iRecordingNotification = new SharpLib.Notification.Control();
   171 
   172             //Have our designer initialize its controls
   173             InitializeComponent();
   174 
   175             //Redirect console output
   176             iWriter = new RichTextBoxTextWriter(richTextBoxLogs);
   177             Console.SetOut(iWriter);
   178 
   179             //Populate device types
   180             PopulateDeviceTypes();
   181 
   182             //Populate optical drives
   183             PopulateOpticalDrives();
   184 
   185             //Initial status update 
   186             UpdateStatus();
   187 
   188             //We have a bug when drawing minimized and reusing our bitmap
   189             //Though I could not reproduce it on Windows 10
   190             iBmp = new System.Drawing.Bitmap(iTableLayoutPanel.Width, iTableLayoutPanel.Height,
   191                 PixelFormat.Format32bppArgb);
   192             iCreateBitmap = false;
   193 
   194             //Minimize our window if desired
   195             if (Properties.Settings.Default.StartMinimized)
   196             {
   197                 WindowState = FormWindowState.Minimized;
   198             }
   199 
   200         }
   201 
   202         /// <summary>
   203         ///
   204         /// </summary>
   205         /// <param name="sender"></param>
   206         /// <param name="e"></param>
   207         private void MainForm_Load(object sender, EventArgs e)
   208         {
   209             //Check if we are running a Click Once deployed application
   210             if (ApplicationDeployment.IsNetworkDeployed)
   211             {
   212                 //This is a proper Click Once installation, fetch and show our version number
   213                 this.Text += " - v" + ApplicationDeployment.CurrentDeployment.CurrentVersion;
   214             }
   215             else
   216             {
   217                 //Not a proper Click Once installation, assuming development build then
   218                 this.Text += " - development";
   219             }
   220 
   221             //NAudio
   222             iMultiMediaDeviceEnumerator = new MMDeviceEnumerator();
   223             iMultiMediaDeviceEnumerator.RegisterEndpointNotificationCallback(this);
   224             UpdateAudioDeviceAndMasterVolumeThreadSafe();
   225 
   226             //Network
   227             iNetworkManager = new NetworkManager();
   228             iNetworkManager.OnConnectivityChanged += OnConnectivityChanged;
   229             UpdateNetworkStatus();
   230 
   231             //CEC
   232             iCecManager = new ConsumerElectronicControl();
   233             OnWndProc += iCecManager.OnWndProc;
   234             ResetCec();
   235 
   236             //Setup Events
   237             PopulateEventsTreeView();
   238 
   239             //Setup notification icon
   240             SetupTrayIcon();
   241 
   242             //Setup recording notification
   243             SetupRecordingNotification();
   244 
   245             // To make sure start up with minimize to tray works
   246             if (WindowState == FormWindowState.Minimized && Properties.Settings.Default.MinimizeToTray)
   247             {
   248                 Visible = false;
   249             }
   250 
   251 #if !DEBUG
   252     //When not debugging we want the screen to be empty until a client takes over
   253 			ClearLayout();
   254 #else
   255             //When developing we want at least one client for testing
   256             StartNewClient("abcdefghijklmnopqrst-0123456789", "ABCDEFGHIJKLMNOPQRST-0123456789");
   257 #endif
   258 
   259             //Open display connection on start-up if needed
   260             if (Properties.Settings.Default.DisplayConnectOnStartup)
   261             {
   262                 OpenDisplayConnection();
   263             }
   264 
   265             //Start our server so that we can get client requests
   266             StartServer();
   267 
   268             //Register for HID events
   269             RegisterHidDevices();
   270 
   271             //Start Idle client if needed
   272             if (Properties.Settings.Default.StartIdleClient)
   273             {
   274                 StartIdleClient();
   275             }
   276         }
   277 
   278         /// <summary>
   279         /// Called when our display is opened.
   280         /// </summary>
   281         /// <param name="aDisplay"></param>
   282         private void OnDisplayOpened(Display aDisplay)
   283         {
   284             //Make sure we resume frame rendering
   285             iSkipFrameRendering = false;
   286 
   287             //Set our screen size now that our display is connected
   288             //Our panelDisplay is the container of our tableLayoutPanel
   289             //tableLayoutPanel will resize itself to fit the client size of our panelDisplay
   290             //panelDisplay needs an extra 2 pixels for borders on each sides
   291             //tableLayoutPanel will eventually be the exact size of our display
   292             Size size = new Size(iDisplay.WidthInPixels() + 2, iDisplay.HeightInPixels() + 2);
   293             panelDisplay.Size = size;
   294 
   295             //Our display was just opened, update our UI
   296             UpdateStatus();
   297             //Initiate asynchronous request
   298             iDisplay.RequestFirmwareRevision();
   299 
   300             //Audio
   301             UpdateMasterVolumeThreadSafe();
   302             //Network
   303             UpdateNetworkStatus();
   304 
   305 #if DEBUG
   306             //Testing icon in debug, no arm done if icon not supported
   307             //iDisplay.SetIconStatus(Display.TMiniDisplayIconType.EMiniDisplayIconRecording, 0, 1);
   308             //iDisplay.SetAllIconsStatus(2);
   309 #endif
   310 
   311         }
   312 
   313         /// <summary>
   314         /// Populate tree view with events and actions
   315         /// </summary>
   316         private void PopulateEventsTreeView()
   317         {
   318             //Disable action buttons
   319             buttonActionAdd.Enabled = false;
   320             buttonActionDelete.Enabled = false;
   321 
   322             Event currentEvent = CurrentEvent();
   323             SharpLib.Ear.Action currentAction = CurrentAction();
   324             TreeNode treeNodeToSelect = null;
   325             
   326             //Reset our tree
   327             iTreeViewEvents.Nodes.Clear();
   328             //Populate registered events
   329             foreach (SharpLib.Ear.Event e in ManagerEventAction.Current.Events)
   330             {
   331                 //Create our event node
   332                 TreeNode eventNode = iTreeViewEvents.Nodes.Add(e.Name);
   333                 eventNode.Tag = e; //For easy access to our event
   334                 if (!e.Enabled)
   335                 {
   336                     //Dim our nodes if disabled
   337                     eventNode.ForeColor = Color.DimGray;
   338                 }
   339 
   340                 //Add event description as child node
   341                 eventNode.Nodes.Add(e.Description).ForeColor = eventNode.ForeColor; 
   342                 //Create child node for actions root
   343                 TreeNode actionsNodes = eventNode.Nodes.Add("Actions");
   344                 actionsNodes.ForeColor = eventNode.ForeColor;
   345 
   346                 // Add our actions for that event
   347                 foreach (SharpLib.Ear.Action a in e.Actions)
   348                 {
   349                     TreeNode actionNode = actionsNodes.Nodes.Add(a.Brief());
   350                     actionNode.Tag = a;
   351                     actionNode.ForeColor = eventNode.ForeColor;
   352                     if (a == currentAction)
   353                     {
   354                         treeNodeToSelect = actionNode;
   355                     }
   356                 }
   357             }
   358 
   359             iTreeViewEvents.ExpandAll();
   360             SelectEvent(currentEvent);
   361             
   362             if (treeNodeToSelect != null)
   363             {
   364                 iTreeViewEvents.SelectedNode = treeNodeToSelect;
   365             }
   366             else if (iTreeViewEvents.SelectedNode != null && iTreeViewEvents.SelectedNode.Nodes[1].GetNodeCount(false) > 0)
   367             {
   368                 //Select the last action if any 
   369                 iTreeViewEvents.SelectedNode =
   370                     iTreeViewEvents.SelectedNode.Nodes[1].Nodes[
   371                         iTreeViewEvents.SelectedNode.Nodes[1].GetNodeCount(false) - 1];
   372             }
   373             else if (iTreeViewEvents.SelectedNode == null && iTreeViewEvents.Nodes.Count > 0)
   374             {
   375                 //Still no selected node select the first one then
   376                 iTreeViewEvents.SelectedNode = iTreeViewEvents.Nodes[0];
   377             }
   378 
   379 
   380             UpdateEventView();
   381         }
   382 
   383         /// <summary>
   384         /// Called when our display is closed.
   385         /// </summary>
   386         /// <param name="aDisplay"></param>
   387         private void OnDisplayClosed(Display aDisplay)
   388         {
   389             //Our display was just closed, update our UI consequently
   390             UpdateStatus();
   391         }
   392 
   393         public void OnConnectivityChanged(NetworkManager aNetwork, NLM_CONNECTIVITY newConnectivity)
   394         {
   395             //Update network status
   396             UpdateNetworkStatus();
   397         }
   398 
   399         /// <summary>
   400         /// Update our Network Status
   401         /// </summary>
   402         private void UpdateNetworkStatus()
   403         {
   404             if (iDisplay.IsOpen())
   405             {
   406                 iDisplay.SetIconOnOff(MiniDisplay.IconType.Internet,
   407                     iNetworkManager.NetworkListManager.IsConnectedToInternet);
   408                 iDisplay.SetIconOnOff(MiniDisplay.IconType.NetworkSignal, iNetworkManager.NetworkListManager.IsConnected);
   409             }
   410         }
   411 
   412 
   413         int iLastNetworkIconIndex = 0;
   414         int iUpdateCountSinceLastNetworkAnimation = 0;
   415 
   416         /// <summary>
   417         /// 
   418         /// </summary>
   419         private void UpdateNetworkSignal(DateTime aLastTickTime, DateTime aNewTickTime)
   420         {
   421             iUpdateCountSinceLastNetworkAnimation++;
   422             iUpdateCountSinceLastNetworkAnimation = iUpdateCountSinceLastNetworkAnimation%4;
   423 
   424             if (iDisplay.IsOpen() && iNetworkManager.NetworkListManager.IsConnected &&
   425                 iUpdateCountSinceLastNetworkAnimation == 0)
   426             {
   427                 int iconCount = iDisplay.IconCount(MiniDisplay.IconType.NetworkSignal);
   428                 if (iconCount <= 0)
   429                 {
   430                     //Prevents div by zero and other undefined behavior
   431                     return;
   432                 }
   433                 iLastNetworkIconIndex++;
   434                 iLastNetworkIconIndex = iLastNetworkIconIndex%(iconCount*2);
   435                 for (int i = 0; i < iconCount; i++)
   436                 {
   437                     if (i < iLastNetworkIconIndex && !(i == 0 && iLastNetworkIconIndex > 3) &&
   438                         !(i == 1 && iLastNetworkIconIndex > 4))
   439                     {
   440                         iDisplay.SetIconOn(MiniDisplay.IconType.NetworkSignal, i);
   441                     }
   442                     else
   443                     {
   444                         iDisplay.SetIconOff(MiniDisplay.IconType.NetworkSignal, i);
   445                     }
   446                 }
   447             }
   448         }
   449 
   450 
   451 
   452         /// <summary>
   453         /// Receive volume change notification and reflect changes on our slider.
   454         /// </summary>
   455         /// <param name="data"></param>
   456         public void OnVolumeNotificationThreadSafe(AudioVolumeNotificationData data)
   457         {
   458             UpdateMasterVolumeThreadSafe();
   459         }
   460 
   461         /// <summary>
   462         /// Update master volume when user moves our slider.
   463         /// </summary>
   464         /// <param name="sender"></param>
   465         /// <param name="e"></param>
   466         private void trackBarMasterVolume_Scroll(object sender, EventArgs e)
   467         {
   468             //Just like Windows Volume Mixer we unmute if the volume is adjusted
   469             iMultiMediaDevice.AudioEndpointVolume.Mute = false;
   470             //Set volume level according to our volume slider new position
   471             iMultiMediaDevice.AudioEndpointVolume.MasterVolumeLevelScalar = trackBarMasterVolume.Value/100.0f;
   472         }
   473 
   474 
   475         /// <summary>
   476         /// Mute check box changed.
   477         /// </summary>
   478         /// <param name="sender"></param>
   479         /// <param name="e"></param>
   480         private void checkBoxMute_CheckedChanged(object sender, EventArgs e)
   481         {
   482             iMultiMediaDevice.AudioEndpointVolume.Mute = checkBoxMute.Checked;
   483         }
   484 
   485         /// <summary>
   486         /// Device State Changed
   487         /// </summary>
   488         public void OnDeviceStateChanged([MarshalAs(UnmanagedType.LPWStr)] string deviceId,
   489             [MarshalAs(UnmanagedType.I4)] DeviceState newState)
   490         {
   491         }
   492 
   493         /// <summary>
   494         /// Device Added
   495         /// </summary>
   496         public void OnDeviceAdded([MarshalAs(UnmanagedType.LPWStr)] string pwstrDeviceId)
   497         {
   498         }
   499 
   500         /// <summary>
   501         /// Device Removed
   502         /// </summary>
   503         public void OnDeviceRemoved([MarshalAs(UnmanagedType.LPWStr)] string deviceId)
   504         {
   505         }
   506 
   507         /// <summary>
   508         /// Default Device Changed
   509         /// </summary>
   510         public void OnDefaultDeviceChanged(DataFlow flow, Role role,
   511             [MarshalAs(UnmanagedType.LPWStr)] string defaultDeviceId)
   512         {
   513             if (role == Role.Multimedia && flow == DataFlow.Render)
   514             {
   515                 UpdateAudioDeviceAndMasterVolumeThreadSafe();
   516             }
   517         }
   518 
   519         /// <summary>
   520         /// Property Value Changed
   521         /// </summary>
   522         /// <param name="pwstrDeviceId"></param>
   523         /// <param name="key"></param>
   524         public void OnPropertyValueChanged([MarshalAs(UnmanagedType.LPWStr)] string pwstrDeviceId, PropertyKey key)
   525         {
   526         }
   527 
   528 
   529 
   530 
   531         /// <summary>
   532         /// Update master volume indicators based our current system states.
   533         /// This typically includes volume levels and mute status.
   534         /// </summary>
   535         private void UpdateMasterVolumeThreadSafe()
   536         {
   537             if (this.InvokeRequired)
   538             {
   539                 //Not in the proper thread, invoke ourselves
   540                 PlainUpdateDelegate d = new PlainUpdateDelegate(UpdateMasterVolumeThreadSafe);
   541                 this.Invoke(d, new object[] {});
   542                 return;
   543             }
   544 
   545             //Update volume slider
   546             float volumeLevelScalar = iMultiMediaDevice.AudioEndpointVolume.MasterVolumeLevelScalar;
   547             trackBarMasterVolume.Value = Convert.ToInt32(volumeLevelScalar*100);
   548             //Update mute checkbox
   549             checkBoxMute.Checked = iMultiMediaDevice.AudioEndpointVolume.Mute;
   550 
   551             //If our display connection is open we need to update its icons
   552             if (iDisplay.IsOpen())
   553             {
   554                 //First take care our our volume level icons
   555                 int volumeIconCount = iDisplay.IconCount(MiniDisplay.IconType.Volume);
   556                 if (volumeIconCount > 0)
   557                 {
   558                     //Compute current volume level from system level and the number of segments in our display volume bar.
   559                     //That tells us how many segments in our volume bar needs to be turned on.
   560                     float currentVolume = volumeLevelScalar*volumeIconCount;
   561                     int segmentOnCount = Convert.ToInt32(currentVolume);
   562                     //Check if our segment count was rounded up, this will later be used for half brightness segment
   563                     bool roundedUp = segmentOnCount > currentVolume;
   564 
   565                     for (int i = 0; i < volumeIconCount; i++)
   566                     {
   567                         if (i < segmentOnCount)
   568                         {
   569                             //If we are dealing with our last segment and our segment count was rounded up then we will use half brightness.
   570                             if (i == segmentOnCount - 1 && roundedUp)
   571                             {
   572                                 //Half brightness
   573                                 iDisplay.SetIconStatus(MiniDisplay.IconType.Volume, i,
   574                                     (iDisplay.IconStatusCount(MiniDisplay.IconType.Volume) - 1)/2);
   575                             }
   576                             else
   577                             {
   578                                 //Full brightness
   579                                 iDisplay.SetIconStatus(MiniDisplay.IconType.Volume, i,
   580                                     iDisplay.IconStatusCount(MiniDisplay.IconType.Volume) - 1);
   581                             }
   582                         }
   583                         else
   584                         {
   585                             iDisplay.SetIconStatus(MiniDisplay.IconType.Volume, i, 0);
   586                         }
   587                     }
   588                 }
   589 
   590                 //Take care of our mute icon
   591                 iDisplay.SetIconOnOff(MiniDisplay.IconType.Mute, iMultiMediaDevice.AudioEndpointVolume.Mute);
   592             }
   593 
   594         }
   595 
   596         /// <summary>
   597         /// 
   598         /// </summary>
   599         private void UpdateAudioDeviceAndMasterVolumeThreadSafe()
   600         {
   601             if (this.InvokeRequired)
   602             {
   603                 //Not in the proper thread, invoke ourselves
   604                 PlainUpdateDelegate d = new PlainUpdateDelegate(UpdateAudioDeviceAndMasterVolumeThreadSafe);
   605                 this.Invoke(d, new object[] {});
   606                 return;
   607             }
   608 
   609             //We are in the correct thread just go ahead.
   610             try
   611             {
   612                 //Get our master volume            
   613                 iMultiMediaDevice = iMultiMediaDeviceEnumerator.GetDefaultAudioEndpoint(DataFlow.Render, Role.Multimedia);
   614                 //Update our label
   615                 labelDefaultAudioDevice.Text = iMultiMediaDevice.FriendlyName;
   616 
   617                 //Show our volume in our track bar
   618                 UpdateMasterVolumeThreadSafe();
   619 
   620                 //Register to get volume modifications
   621                 iMultiMediaDevice.AudioEndpointVolume.OnVolumeNotification += OnVolumeNotificationThreadSafe;
   622                 //
   623                 trackBarMasterVolume.Enabled = true;
   624             }
   625             catch (Exception ex)
   626             {
   627                 Debug.WriteLine("Exception thrown in UpdateAudioDeviceAndMasterVolume");
   628                 Debug.WriteLine(ex.ToString());
   629                 //Something went wrong S/PDIF device ca throw exception I guess
   630                 trackBarMasterVolume.Enabled = false;
   631             }
   632         }
   633 
   634         /// <summary>
   635         /// 
   636         /// </summary>
   637         private void PopulateDeviceTypes()
   638         {
   639             int count = Display.TypeCount();
   640 
   641             for (int i = 0; i < count; i++)
   642             {
   643                 comboBoxDisplayType.Items.Add(Display.TypeName((MiniDisplay.Type) i));
   644             }
   645         }
   646 
   647         /// <summary>
   648         /// 
   649         /// </summary>
   650         private void PopulateOpticalDrives()
   651         {
   652             //Reset our list of drives
   653             comboBoxOpticalDrives.Items.Clear();
   654             comboBoxOpticalDrives.Items.Add("None");
   655 
   656             //Go through each drives on our system and collected the optical ones in our list
   657             DriveInfo[] allDrives = DriveInfo.GetDrives();
   658             foreach (DriveInfo d in allDrives)
   659             {
   660                 Debug.WriteLine("Drive " + d.Name);
   661                 Debug.WriteLine("  Drive type: {0}", d.DriveType);
   662 
   663                 if (d.DriveType == DriveType.CDRom)
   664                 {
   665                     //This is an optical drive, add it now
   666                     comboBoxOpticalDrives.Items.Add(d.Name.Substring(0, 2));
   667                 }
   668             }
   669         }
   670 
   671         /// <summary>
   672         /// 
   673         /// </summary>
   674         /// <returns></returns>
   675         public string OpticalDriveToEject()
   676         {
   677             return comboBoxOpticalDrives.SelectedItem.ToString();
   678         }
   679 
   680 
   681 
   682         /// <summary>
   683         ///
   684         /// </summary>
   685         private void SetupTrayIcon()
   686         {
   687             iNotifyIcon.Icon = GetIcon("vfd.ico");
   688             iNotifyIcon.Text = "Sharp Display Manager";
   689             iNotifyIcon.Visible = true;
   690 
   691             //Double click toggles visibility - typically brings up the application
   692             iNotifyIcon.DoubleClick += delegate(object obj, EventArgs args)
   693             {
   694                 SysTrayHideShow();
   695             };
   696 
   697             //Adding a context menu, useful to be able to exit the application
   698             ContextMenu contextMenu = new ContextMenu();
   699             //Context menu item to toggle visibility
   700             MenuItem hideShowItem = new MenuItem("Hide/Show");
   701             hideShowItem.Click += delegate(object obj, EventArgs args)
   702             {
   703                 SysTrayHideShow();
   704             };
   705             contextMenu.MenuItems.Add(hideShowItem);
   706 
   707             //Context menu item separator
   708             contextMenu.MenuItems.Add(new MenuItem("-"));
   709 
   710             //Context menu exit item
   711             MenuItem exitItem = new MenuItem("Exit");
   712             exitItem.Click += delegate(object obj, EventArgs args)
   713             {
   714                 Application.Exit();
   715             };
   716             contextMenu.MenuItems.Add(exitItem);
   717 
   718             iNotifyIcon.ContextMenu = contextMenu;
   719         }
   720 
   721         /// <summary>
   722         ///
   723         /// </summary>
   724         private void SetupRecordingNotification()
   725         {
   726             iRecordingNotification.Icon = GetIcon("record.ico");
   727             iRecordingNotification.Text = "No recording";
   728             iRecordingNotification.Visible = false;
   729         }
   730 
   731         /// <summary>
   732         /// Access icons from embedded resources.
   733         /// </summary>
   734         /// <param name="aName"></param>
   735         /// <returns></returns>
   736         public static Icon GetIcon(string aName)
   737         {
   738             string[] names = Assembly.GetExecutingAssembly().GetManifestResourceNames();
   739             foreach (string name in names)
   740             {
   741                 //Find a resource name that ends with the given name
   742                 if (name.EndsWith(aName))
   743                 {
   744                     using (Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(name))
   745                     {
   746                         return new Icon(stream);
   747                     }
   748                 }
   749             }
   750 
   751             return null;
   752         }
   753 
   754 
   755         /// <summary>
   756         /// Set our current client.
   757         /// This will take care of applying our client layout and set data fields.
   758         /// </summary>
   759         /// <param name="aSessionId"></param>
   760         void SetCurrentClient(string aSessionId, bool aForce = false)
   761         {
   762             if (aSessionId == iCurrentClientSessionId)
   763             {
   764                 //Given client is already the current one.
   765                 //Don't bother changing anything then.
   766                 return;
   767             }
   768 
   769             ClientData requestedClientData = iClients[aSessionId];
   770 
   771             //Check when was the last time we switched to that client
   772             if (iCurrentClientData != null)
   773             {
   774                 //Do not switch client if priority of current client is higher 
   775                 if (!aForce && requestedClientData.Priority < iCurrentClientData.Priority)
   776                 {
   777                     return;
   778                 }
   779 
   780 
   781                 double lastSwitchToClientSecondsAgo = (DateTime.Now - iCurrentClientData.LastSwitchTime).TotalSeconds;
   782                 //TODO: put that hard coded value as a client property
   783                 //Clients should be able to define how often they can be interrupted
   784                 //Thus a background client can set this to zero allowing any other client to interrupt at any time
   785                 //We could also compute this delay by looking at the requests frequencies?
   786                 if (!aForce &&
   787                     requestedClientData.Priority == iCurrentClientData.Priority &&
   788                     //Time sharing is only if clients have the same priority
   789                     (lastSwitchToClientSecondsAgo < 30)) //Make sure a client is on for at least 30 seconds
   790                 {
   791                     //Don't switch clients too often
   792                     return;
   793                 }
   794             }
   795 
   796             //Set current client ID.
   797             iCurrentClientSessionId = aSessionId;
   798             //Set the time we last switched to that client
   799             iClients[aSessionId].LastSwitchTime = DateTime.Now;
   800             //Fetch and set current client data.
   801             iCurrentClientData = requestedClientData;
   802             //Apply layout and set data fields.
   803             UpdateTableLayoutPanel(iCurrentClientData);
   804         }
   805 
   806         private void buttonFont_Click(object sender, EventArgs e)
   807         {
   808             //fontDialog.ShowColor = true;
   809             //fontDialog.ShowApply = true;
   810             fontDialog.ShowEffects = true;
   811             fontDialog.Font = cds.Font;
   812 
   813             fontDialog.FixedPitchOnly = checkBoxFixedPitchFontOnly.Checked;
   814 
   815             //fontDialog.ShowHelp = true;
   816 
   817             //fontDlg.MaxSize = 40;
   818             //fontDlg.MinSize = 22;
   819 
   820             //fontDialog.Parent = this;
   821             //fontDialog.StartPosition = FormStartPosition.CenterParent;
   822 
   823             //DlgBox.ShowDialog(fontDialog);
   824 
   825             //if (fontDialog.ShowDialog(this) != DialogResult.Cancel)
   826             if (DlgBox.ShowDialog(fontDialog) != DialogResult.Cancel)
   827             {
   828                 //Set the fonts to all our labels in our layout
   829                 foreach (Control ctrl in iTableLayoutPanel.Controls)
   830                 {
   831                     if (ctrl is MarqueeLabel)
   832                     {
   833                         ((MarqueeLabel) ctrl).Font = fontDialog.Font;
   834                     }
   835                 }
   836 
   837                 //Save font settings
   838                 cds.Font = fontDialog.Font;
   839                 Properties.Settings.Default.Save();
   840                 //
   841                 CheckFontHeight();
   842             }
   843         }
   844 
   845         /// <summary>
   846         ///
   847         /// </summary>
   848         void CheckFontHeight()
   849         {
   850             //Show font height and width
   851             labelFontHeight.Text = "Font height: " + cds.Font.Height;
   852             float charWidth = IsFixedWidth(cds.Font);
   853             if (charWidth == 0.0f)
   854             {
   855                 labelFontWidth.Visible = false;
   856             }
   857             else
   858             {
   859                 labelFontWidth.Visible = true;
   860                 labelFontWidth.Text = "Font width: " + charWidth;
   861             }
   862 
   863             MarqueeLabel label = null;
   864             //Get the first label control we can find
   865             foreach (Control ctrl in iTableLayoutPanel.Controls)
   866             {
   867                 if (ctrl is MarqueeLabel)
   868                 {
   869                     label = (MarqueeLabel) ctrl;
   870                     break;
   871                 }
   872             }
   873 
   874             //Now check font height and show a warning if needed.
   875             if (label != null && label.Font.Height > label.Height)
   876             {
   877                 labelWarning.Text = "WARNING: Selected font is too height by " + (label.Font.Height - label.Height) +
   878                                     " pixels!";
   879                 labelWarning.Visible = true;
   880             }
   881             else
   882             {
   883                 labelWarning.Visible = false;
   884             }
   885 
   886         }
   887 
   888         private void buttonCapture_Click(object sender, EventArgs e)
   889         {
   890             System.Drawing.Bitmap bmp = new System.Drawing.Bitmap(iTableLayoutPanel.Width, iTableLayoutPanel.Height);
   891             iTableLayoutPanel.DrawToBitmap(bmp, iTableLayoutPanel.ClientRectangle);
   892             //Bitmap bmpToSave = new Bitmap(bmp);
   893             bmp.Save("D:\\capture.png");
   894 
   895             ((MarqueeLabel) iTableLayoutPanel.Controls[0]).Text = "Captured";
   896 
   897             /*
   898             string outputFileName = "d:\\capture.png";
   899             using (MemoryStream memory = new MemoryStream())
   900             {
   901                 using (FileStream fs = new FileStream(outputFileName, FileMode.OpenOrCreate, FileAccess.ReadWrite))
   902                 {
   903                     bmp.Save(memory, System.Drawing.Imaging.ImageFormat.Png);
   904                     byte[] bytes = memory.ToArray();
   905                     fs.Write(bytes, 0, bytes.Length);
   906                 }
   907             }
   908              */
   909 
   910         }
   911 
   912         private void CheckForRequestResults()
   913         {
   914             if (iDisplay.IsRequestPending())
   915             {
   916                 switch (iDisplay.AttemptRequestCompletion())
   917                 {
   918                     case MiniDisplay.Request.FirmwareRevision:
   919                         toolStripStatusLabelConnect.Text += " v" + iDisplay.FirmwareRevision();
   920                         //Issue next request then
   921                         iDisplay.RequestPowerSupplyStatus();
   922                         break;
   923 
   924                     case MiniDisplay.Request.PowerSupplyStatus:
   925                         if (iDisplay.PowerSupplyStatus())
   926                         {
   927                             toolStripStatusLabelPower.Text = "ON";
   928                         }
   929                         else
   930                         {
   931                             toolStripStatusLabelPower.Text = "OFF";
   932                         }
   933                         //Issue next request then
   934                         iDisplay.RequestDeviceId();
   935                         break;
   936 
   937                     case MiniDisplay.Request.DeviceId:
   938                         toolStripStatusLabelConnect.Text += " - " + iDisplay.DeviceId();
   939                         //No more request to issue
   940                         break;
   941                 }
   942             }
   943         }
   944 
   945         public static uint ColorWhiteIsOn(int aX, int aY, uint aPixel)
   946         {
   947             if ((aPixel & 0x00FFFFFF) == 0x00FFFFFF)
   948             {
   949                 return 0xFFFFFFFF;
   950             }
   951             return 0x00000000;
   952         }
   953 
   954         public static uint ColorUntouched(int aX, int aY, uint aPixel)
   955         {
   956             return aPixel;
   957         }
   958 
   959         public static uint ColorInversed(int aX, int aY, uint aPixel)
   960         {
   961             return ~aPixel;
   962         }
   963 
   964         public static uint ColorChessboard(int aX, int aY, uint aPixel)
   965         {
   966             if ((aX%2 == 0) && (aY%2 == 0))
   967             {
   968                 return ~aPixel;
   969             }
   970             else if ((aX%2 != 0) && (aY%2 != 0))
   971             {
   972                 return ~aPixel;
   973             }
   974             return 0x00000000;
   975         }
   976 
   977 
   978         public static int ScreenReversedX(System.Drawing.Bitmap aBmp, int aX)
   979         {
   980             return aBmp.Width - aX - 1;
   981         }
   982 
   983         public int ScreenReversedY(System.Drawing.Bitmap aBmp, int aY)
   984         {
   985             return iBmp.Height - aY - 1;
   986         }
   987 
   988         public int ScreenX(System.Drawing.Bitmap aBmp, int aX)
   989         {
   990             return aX;
   991         }
   992 
   993         public int ScreenY(System.Drawing.Bitmap aBmp, int aY)
   994         {
   995             return aY;
   996         }
   997 
   998         /// <summary>
   999         /// Select proper pixel delegates according to our current settings.
  1000         /// </summary>
  1001         private void SetupPixelDelegates()
  1002         {
  1003             //Select our pixel processing routine
  1004             if (cds.InverseColors)
  1005             {
  1006                 //iColorFx = ColorChessboard;
  1007                 iColorFx = ColorInversed;
  1008             }
  1009             else
  1010             {
  1011                 iColorFx = ColorWhiteIsOn;
  1012             }
  1013 
  1014             //Select proper coordinate translation functions
  1015             //We used delegate/function pointer to support reverse screen without doing an extra test on each pixels
  1016             if (cds.ReverseScreen)
  1017             {
  1018                 iScreenX = ScreenReversedX;
  1019                 iScreenY = ScreenReversedY;
  1020             }
  1021             else
  1022             {
  1023                 iScreenX = ScreenX;
  1024                 iScreenY = ScreenY;
  1025             }
  1026 
  1027         }
  1028 
  1029         //This is our timer tick responsible to perform our render
  1030         private void timer_Tick(object sender, EventArgs e)
  1031         {
  1032             //Not ideal cause this has nothing to do with display render
  1033             LogsUpdate();
  1034 
  1035             //Update our animations
  1036             DateTime NewTickTime = DateTime.Now;
  1037 
  1038             UpdateNetworkSignal(LastTickTime, NewTickTime);
  1039 
  1040             //Update animation for all our marquees
  1041             foreach (Control ctrl in iTableLayoutPanel.Controls)
  1042             {
  1043                 if (ctrl is MarqueeLabel)
  1044                 {
  1045                     ((MarqueeLabel) ctrl).UpdateAnimation(LastTickTime, NewTickTime);
  1046                 }
  1047             }
  1048 
  1049             //Update our display
  1050             if (iDisplay.IsOpen())
  1051             {
  1052                 CheckForRequestResults();
  1053 
  1054                 //Check if frame rendering is needed
  1055                 //Typically used when showing clock
  1056                 if (!iSkipFrameRendering)
  1057                 {
  1058                     //Draw to bitmap
  1059                     if (iCreateBitmap)
  1060                     {
  1061                         iBmp = new System.Drawing.Bitmap(iTableLayoutPanel.Width, iTableLayoutPanel.Height,
  1062                             PixelFormat.Format32bppArgb);
  1063                         iCreateBitmap = false;
  1064                     }
  1065                     iTableLayoutPanel.DrawToBitmap(iBmp, iTableLayoutPanel.ClientRectangle);
  1066                     //iBmp.Save("D:\\capture.png");
  1067 
  1068                     //Send it to our display
  1069                     for (int i = 0; i < iBmp.Width; i++)
  1070                     {
  1071                         for (int j = 0; j < iBmp.Height; j++)
  1072                         {
  1073                             unchecked
  1074                             {
  1075                                 //Get our processed pixel coordinates
  1076                                 int x = iScreenX(iBmp, i);
  1077                                 int y = iScreenY(iBmp, j);
  1078                                 //Get pixel color
  1079                                 uint color = (uint) iBmp.GetPixel(i, j).ToArgb();
  1080                                 //Apply color effects
  1081                                 color = iColorFx(x, y, color);
  1082                                 //Now set our pixel
  1083                                 iDisplay.SetPixel(x, y, color);
  1084                             }
  1085                         }
  1086                     }
  1087 
  1088                     iDisplay.SwapBuffers();
  1089                 }
  1090             }
  1091 
  1092             //Compute instant FPS
  1093             toolStripStatusLabelFps.Text = (1.0/NewTickTime.Subtract(LastTickTime).TotalSeconds).ToString("F0") + " / " +
  1094                                            (1000/timer.Interval).ToString() + " FPS";
  1095 
  1096             LastTickTime = NewTickTime;
  1097 
  1098         }
  1099 
  1100         /// <summary>
  1101         /// Attempt to establish connection with our display hardware.
  1102         /// </summary>
  1103         private void OpenDisplayConnection()
  1104         {
  1105             CloseDisplayConnection();
  1106 
  1107             if (!iDisplay.Open((MiniDisplay.Type) cds.DisplayType))
  1108             {
  1109                 UpdateStatus();
  1110                 toolStripStatusLabelConnect.Text = "Connection error";
  1111             }
  1112         }
  1113 
  1114         private void CloseDisplayConnection()
  1115         {
  1116             //Status will be updated upon receiving the closed event
  1117 
  1118             if (iDisplay == null || !iDisplay.IsOpen())
  1119             {
  1120                 return;
  1121             }
  1122 
  1123             //Do not clear if we gave up on rendering already.
  1124             //This means we will keep on displaying clock on MDM166AA for instance.
  1125             if (!iSkipFrameRendering)
  1126             {
  1127                 iDisplay.Clear();
  1128                 iDisplay.SwapBuffers();
  1129             }
  1130 
  1131             iDisplay.SetAllIconsStatus(0); //Turn off all icons
  1132             iDisplay.Close();
  1133         }
  1134 
  1135         private void buttonOpen_Click(object sender, EventArgs e)
  1136         {
  1137             OpenDisplayConnection();
  1138         }
  1139 
  1140         private void buttonClose_Click(object sender, EventArgs e)
  1141         {
  1142             CloseDisplayConnection();
  1143         }
  1144 
  1145         private void buttonClear_Click(object sender, EventArgs e)
  1146         {
  1147             iDisplay.Clear();
  1148             iDisplay.SwapBuffers();
  1149         }
  1150 
  1151         private void buttonFill_Click(object sender, EventArgs e)
  1152         {
  1153             iDisplay.Fill();
  1154             iDisplay.SwapBuffers();
  1155         }
  1156 
  1157         private void trackBarBrightness_Scroll(object sender, EventArgs e)
  1158         {
  1159             cds.Brightness = trackBarBrightness.Value;
  1160             Properties.Settings.Default.Save();
  1161             iDisplay.SetBrightness(trackBarBrightness.Value);
  1162 
  1163         }
  1164 
  1165 
  1166         /// <summary>
  1167         /// CDS stands for Current Display Settings
  1168         /// </summary>
  1169         private DisplaySettings cds
  1170         {
  1171             get
  1172             {
  1173                 DisplaysSettings settings = Properties.Settings.Default.DisplaysSettings;
  1174                 if (settings == null)
  1175                 {
  1176                     settings = new DisplaysSettings();
  1177                     settings.Init();
  1178                     Properties.Settings.Default.DisplaysSettings = settings;
  1179                 }
  1180 
  1181                 //Make sure all our settings have been created
  1182                 while (settings.Displays.Count <= Properties.Settings.Default.CurrentDisplayIndex)
  1183                 {
  1184                     settings.Displays.Add(new DisplaySettings());
  1185                 }
  1186 
  1187                 DisplaySettings displaySettings = settings.Displays[Properties.Settings.Default.CurrentDisplayIndex];
  1188                 return displaySettings;
  1189             }
  1190         }
  1191 
  1192         /// <summary>
  1193         /// Check if the given font has a fixed character pitch.
  1194         /// </summary>
  1195         /// <param name="ft"></param>
  1196         /// <returns>0.0f if this is not a monospace font, otherwise returns the character width.</returns>
  1197         public float IsFixedWidth(Font ft)
  1198         {
  1199             Graphics g = CreateGraphics();
  1200             char[] charSizes = new char[] {'i', 'a', 'Z', '%', '#', 'a', 'B', 'l', 'm', ',', '.'};
  1201             float charWidth = g.MeasureString("I", ft, Int32.MaxValue, StringFormat.GenericTypographic).Width;
  1202 
  1203             bool fixedWidth = true;
  1204 
  1205             foreach (char c in charSizes)
  1206                 if (g.MeasureString(c.ToString(), ft, Int32.MaxValue, StringFormat.GenericTypographic).Width !=
  1207                     charWidth)
  1208                     fixedWidth = false;
  1209 
  1210             if (fixedWidth)
  1211             {
  1212                 return charWidth;
  1213             }
  1214 
  1215             return 0.0f;
  1216         }
  1217 
  1218         /// <summary>
  1219         /// Synchronize UI with settings
  1220         /// </summary>
  1221         private void UpdateStatus()
  1222         {
  1223             //Load settings
  1224             checkBoxShowBorders.Checked = cds.ShowBorders;
  1225             iTableLayoutPanel.CellBorderStyle = (cds.ShowBorders
  1226                 ? TableLayoutPanelCellBorderStyle.Single
  1227                 : TableLayoutPanelCellBorderStyle.None);
  1228 
  1229             //Set the proper font to each of our labels
  1230             foreach (MarqueeLabel ctrl in iTableLayoutPanel.Controls)
  1231             {
  1232                 ctrl.Font = cds.Font;
  1233             }
  1234 
  1235             CheckFontHeight();
  1236             //Check if "run on Windows startup" is enabled
  1237             checkBoxAutoStart.Checked = iStartupManager.Startup;
  1238             //
  1239             checkBoxConnectOnStartup.Checked = Properties.Settings.Default.DisplayConnectOnStartup;
  1240             checkBoxMinimizeToTray.Checked = Properties.Settings.Default.MinimizeToTray;
  1241             checkBoxStartMinimized.Checked = Properties.Settings.Default.StartMinimized;
  1242             iCheckBoxStartIdleClient.Checked = Properties.Settings.Default.StartIdleClient;
  1243             labelStartFileName.Text = Properties.Settings.Default.StartFileName;
  1244 
  1245 
  1246             //Try find our drive in our drive list
  1247             int opticalDriveItemIndex = 0;
  1248             bool driveNotFound = true;
  1249             string opticalDriveToEject = Properties.Settings.Default.OpticalDriveToEject;
  1250             foreach (object item in comboBoxOpticalDrives.Items)
  1251             {
  1252                 if (opticalDriveToEject == item.ToString())
  1253                 {
  1254                     comboBoxOpticalDrives.SelectedIndex = opticalDriveItemIndex;
  1255                     driveNotFound = false;
  1256                     break;
  1257                 }
  1258                 opticalDriveItemIndex++;
  1259             }
  1260 
  1261             if (driveNotFound)
  1262             {
  1263                 //We could not find the drive we had saved.
  1264                 //Select "None" then.
  1265                 comboBoxOpticalDrives.SelectedIndex = 0;
  1266             }
  1267 
  1268             //CEC settings
  1269             checkBoxCecEnabled.Checked = Properties.Settings.Default.CecEnabled;
  1270             comboBoxHdmiPort.SelectedIndex = Properties.Settings.Default.CecHdmiPort - 1;
  1271 
  1272             //Mini Display settings
  1273             checkBoxReverseScreen.Checked = cds.ReverseScreen;
  1274             checkBoxInverseColors.Checked = cds.InverseColors;
  1275             checkBoxShowVolumeLabel.Checked = cds.ShowVolumeLabel;
  1276             checkBoxScaleToFit.Checked = cds.ScaleToFit;
  1277             maskedTextBoxMinFontSize.Enabled = cds.ScaleToFit;
  1278             labelMinFontSize.Enabled = cds.ScaleToFit;
  1279             maskedTextBoxMinFontSize.Text = cds.MinFontSize.ToString();
  1280             maskedTextBoxScrollingSpeed.Text = cds.ScrollingSpeedInPixelsPerSecond.ToString();
  1281             comboBoxDisplayType.SelectedIndex = cds.DisplayType;
  1282             timer.Interval = cds.TimerInterval;
  1283             maskedTextBoxTimerInterval.Text = cds.TimerInterval.ToString();
  1284             textBoxScrollLoopSeparator.Text = cds.Separator;
  1285             //
  1286             SetupPixelDelegates();
  1287 
  1288             if (iDisplay.IsOpen())
  1289             {
  1290                 //We have a display connection
  1291                 //Reflect that in our UI
  1292                 StartTimer();
  1293 
  1294                 iTableLayoutPanel.Enabled = true;
  1295                 panelDisplay.Enabled = true;
  1296 
  1297                 //Only setup brightness if display is open
  1298                 trackBarBrightness.Minimum = iDisplay.MinBrightness();
  1299                 trackBarBrightness.Maximum = iDisplay.MaxBrightness();
  1300                 if (cds.Brightness < iDisplay.MinBrightness() || cds.Brightness > iDisplay.MaxBrightness())
  1301                 {
  1302                     //Brightness out of range, this can occur when using auto-detect
  1303                     //Use max brightness instead
  1304                     trackBarBrightness.Value = iDisplay.MaxBrightness();
  1305                     iDisplay.SetBrightness(iDisplay.MaxBrightness());
  1306                 }
  1307                 else
  1308                 {
  1309                     trackBarBrightness.Value = cds.Brightness;
  1310                     iDisplay.SetBrightness(cds.Brightness);
  1311                 }
  1312 
  1313                 //Try compute the steps to something that makes sense
  1314                 trackBarBrightness.LargeChange = Math.Max(1, (iDisplay.MaxBrightness() - iDisplay.MinBrightness())/5);
  1315                 trackBarBrightness.SmallChange = 1;
  1316 
  1317                 //
  1318                 buttonFill.Enabled = true;
  1319                 buttonClear.Enabled = true;
  1320                 buttonOpen.Enabled = false;
  1321                 buttonClose.Enabled = true;
  1322                 trackBarBrightness.Enabled = true;
  1323                 toolStripStatusLabelConnect.Text = "Connected - " + iDisplay.Vendor() + " - " + iDisplay.Product();
  1324                 //+ " - " + iDisplay.SerialNumber();
  1325 
  1326                 if (iDisplay.SupportPowerOnOff())
  1327                 {
  1328                     buttonPowerOn.Enabled = true;
  1329                     buttonPowerOff.Enabled = true;
  1330                 }
  1331                 else
  1332                 {
  1333                     buttonPowerOn.Enabled = false;
  1334                     buttonPowerOff.Enabled = false;
  1335                 }
  1336 
  1337                 if (iDisplay.SupportClock())
  1338                 {
  1339                     buttonShowClock.Enabled = true;
  1340                     buttonHideClock.Enabled = true;
  1341                 }
  1342                 else
  1343                 {
  1344                     buttonShowClock.Enabled = false;
  1345                     buttonHideClock.Enabled = false;
  1346                 }
  1347 
  1348 
  1349                 //Check if Volume Label is supported. To date only MDM166AA supports that crap :)
  1350                 checkBoxShowVolumeLabel.Enabled = iDisplay.IconCount(MiniDisplay.IconType.VolumeLabel) > 0;
  1351 
  1352                 if (cds.ShowVolumeLabel)
  1353                 {
  1354                     iDisplay.SetIconOn(MiniDisplay.IconType.VolumeLabel);
  1355                 }
  1356                 else
  1357                 {
  1358                     iDisplay.SetIconOff(MiniDisplay.IconType.VolumeLabel);
  1359                 }
  1360             }
  1361             else
  1362             {
  1363                 //Display connection not available
  1364                 //Reflect that in our UI
  1365 #if DEBUG
  1366                 //In debug start our timer even if we don't have a display connection
  1367                 StartTimer();
  1368 #else
  1369     //In production environment we don't need our timer if no display connection
  1370                 StopTimer();
  1371 #endif
  1372                 checkBoxShowVolumeLabel.Enabled = false;
  1373                 iTableLayoutPanel.Enabled = false;
  1374                 panelDisplay.Enabled = false;
  1375                 buttonFill.Enabled = false;
  1376                 buttonClear.Enabled = false;
  1377                 buttonOpen.Enabled = true;
  1378                 buttonClose.Enabled = false;
  1379                 trackBarBrightness.Enabled = false;
  1380                 buttonPowerOn.Enabled = false;
  1381                 buttonPowerOff.Enabled = false;
  1382                 buttonShowClock.Enabled = false;
  1383                 buttonHideClock.Enabled = false;
  1384                 toolStripStatusLabelConnect.Text = "Disconnected";
  1385                 toolStripStatusLabelPower.Text = "N/A";
  1386             }
  1387 
  1388         }
  1389 
  1390 
  1391         /// <summary>
  1392         /// 
  1393         /// </summary>
  1394         /// <param name="sender"></param>
  1395         /// <param name="e"></param>
  1396         private void checkBoxShowVolumeLabel_CheckedChanged(object sender, EventArgs e)
  1397         {
  1398             cds.ShowVolumeLabel = checkBoxShowVolumeLabel.Checked;
  1399             Properties.Settings.Default.Save();
  1400             UpdateStatus();
  1401         }
  1402 
  1403         private void checkBoxShowBorders_CheckedChanged(object sender, EventArgs e)
  1404         {
  1405             //Save our show borders setting
  1406             iTableLayoutPanel.CellBorderStyle = (checkBoxShowBorders.Checked
  1407                 ? TableLayoutPanelCellBorderStyle.Single
  1408                 : TableLayoutPanelCellBorderStyle.None);
  1409             cds.ShowBorders = checkBoxShowBorders.Checked;
  1410             Properties.Settings.Default.Save();
  1411             CheckFontHeight();
  1412         }
  1413 
  1414         private void checkBoxConnectOnStartup_CheckedChanged(object sender, EventArgs e)
  1415         {
  1416             //Save our connect on startup setting
  1417             Properties.Settings.Default.DisplayConnectOnStartup = checkBoxConnectOnStartup.Checked;
  1418             Properties.Settings.Default.Save();
  1419         }
  1420 
  1421         private void checkBoxMinimizeToTray_CheckedChanged(object sender, EventArgs e)
  1422         {
  1423             //Save our "Minimize to tray" setting
  1424             Properties.Settings.Default.MinimizeToTray = checkBoxMinimizeToTray.Checked;
  1425             Properties.Settings.Default.Save();
  1426 
  1427         }
  1428 
  1429         private void checkBoxStartMinimized_CheckedChanged(object sender, EventArgs e)
  1430         {
  1431             //Save our "Start minimized" setting
  1432             Properties.Settings.Default.StartMinimized = checkBoxStartMinimized.Checked;
  1433             Properties.Settings.Default.Save();
  1434         }
  1435 
  1436         private void checkBoxStartIdleClient_CheckedChanged(object sender, EventArgs e)
  1437         {
  1438             Properties.Settings.Default.StartIdleClient = iCheckBoxStartIdleClient.Checked;
  1439             Properties.Settings.Default.Save();
  1440         }
  1441 
  1442         private void checkBoxAutoStart_CheckedChanged(object sender, EventArgs e)
  1443         {
  1444             iStartupManager.Startup = checkBoxAutoStart.Checked;
  1445         }
  1446 
  1447 
  1448         private void checkBoxReverseScreen_CheckedChanged(object sender, EventArgs e)
  1449         {
  1450             //Save our reverse screen setting
  1451             cds.ReverseScreen = checkBoxReverseScreen.Checked;
  1452             Properties.Settings.Default.Save();
  1453             SetupPixelDelegates();
  1454         }
  1455 
  1456         private void checkBoxInverseColors_CheckedChanged(object sender, EventArgs e)
  1457         {
  1458             //Save our inverse colors setting
  1459             cds.InverseColors = checkBoxInverseColors.Checked;
  1460             Properties.Settings.Default.Save();
  1461             SetupPixelDelegates();
  1462         }
  1463 
  1464         private void checkBoxScaleToFit_CheckedChanged(object sender, EventArgs e)
  1465         {
  1466             //Save our scale to fit setting
  1467             cds.ScaleToFit = checkBoxScaleToFit.Checked;
  1468             Properties.Settings.Default.Save();
  1469             //
  1470             labelMinFontSize.Enabled = cds.ScaleToFit;
  1471             maskedTextBoxMinFontSize.Enabled = cds.ScaleToFit;
  1472         }
  1473 
  1474         private void MainForm_Resize(object sender, EventArgs e)
  1475         {
  1476             if (WindowState == FormWindowState.Minimized)
  1477             {
  1478                 // To workaround our empty bitmap bug on Windows 7 we need to recreate our bitmap when the application is minimized
  1479                 // That's apparently not needed on Windows 10 but we better leave it in place.
  1480                 iCreateBitmap = true;
  1481             }
  1482         }
  1483 
  1484         private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
  1485         {
  1486             iCecManager.Stop();
  1487             iNetworkManager.Dispose();
  1488             CloseDisplayConnection();
  1489             StopServer();
  1490             e.Cancel = iClosing;
  1491         }
  1492 
  1493         public void StartServer()
  1494         {
  1495             iServiceHost = new ServiceHost
  1496                 (
  1497                 typeof(Session),
  1498                 new Uri[] {new Uri("net.tcp://localhost:8001/")}
  1499                 );
  1500 
  1501             iServiceHost.AddServiceEndpoint(typeof(IService), new NetTcpBinding(SecurityMode.None, true),
  1502                 "DisplayService");
  1503             iServiceHost.Open();
  1504         }
  1505 
  1506         public void StopServer()
  1507         {
  1508             if (iClients.Count > 0 && !iClosing)
  1509             {
  1510                 //Tell our clients
  1511                 iClosing = true;
  1512                 BroadcastCloseEvent();
  1513             }
  1514             else if (iClosing)
  1515             {
  1516                 if (
  1517                     MessageBox.Show("Force exit?", "Waiting for clients...", MessageBoxButtons.YesNo,
  1518                         MessageBoxIcon.Warning) == DialogResult.Yes)
  1519                 {
  1520                     iClosing = false; //We make sure we force close if asked twice
  1521                 }
  1522             }
  1523             else
  1524             {
  1525                 //We removed that as it often lags for some reason
  1526                 //iServiceHost.Close();
  1527             }
  1528         }
  1529 
  1530         public void BroadcastCloseEvent()
  1531         {
  1532             Trace.TraceInformation("BroadcastCloseEvent - start");
  1533 
  1534             var inactiveClients = new List<string>();
  1535             foreach (var client in iClients)
  1536             {
  1537                 //if (client.Key != eventData.ClientName)
  1538                 {
  1539                     try
  1540                     {
  1541                         Trace.TraceInformation("BroadcastCloseEvent - " + client.Key);
  1542                         client.Value.Callback.OnCloseOrder( /*eventData*/);
  1543                     }
  1544                     catch (Exception ex)
  1545                     {
  1546                         inactiveClients.Add(client.Key);
  1547                     }
  1548                 }
  1549             }
  1550 
  1551             if (inactiveClients.Count > 0)
  1552             {
  1553                 foreach (var client in inactiveClients)
  1554                 {
  1555                     iClients.Remove(client);
  1556                     Program.iFormMain.iTreeViewClients.Nodes.Remove(
  1557                         Program.iFormMain.iTreeViewClients.Nodes.Find(client, false)[0]);
  1558                 }
  1559             }
  1560 
  1561             if (iClients.Count == 0)
  1562             {
  1563                 ClearLayout();
  1564             }
  1565         }
  1566 
  1567         /// <summary>
  1568         /// Just remove all our fields.
  1569         /// </summary>
  1570         private void ClearLayout()
  1571         {
  1572             iTableLayoutPanel.Controls.Clear();
  1573             iTableLayoutPanel.RowStyles.Clear();
  1574             iTableLayoutPanel.ColumnStyles.Clear();
  1575             iCurrentClientData = null;
  1576         }
  1577 
  1578         /// <summary>
  1579         /// Just launch a demo client.
  1580         /// </summary>
  1581         private void StartNewClient(string aTopText = "", string aBottomText = "")
  1582         {
  1583             Thread clientThread = new Thread(SharpDisplayClient.Program.MainWithParams);
  1584             SharpDisplayClient.StartParams myParams = new SharpDisplayClient.StartParams(
  1585                 new Point(this.Right, this.Top), aTopText, aBottomText);
  1586             clientThread.Start(myParams);
  1587             BringToFront();
  1588         }
  1589 
  1590         /// <summary>
  1591         /// Just launch our idle client.
  1592         /// </summary>
  1593         private void StartIdleClient(string aTopText = "", string aBottomText = "")
  1594         {
  1595             Thread clientThread = new Thread(SharpDisplayClientIdle.Program.MainWithParams);
  1596             SharpDisplayClientIdle.StartParams myParams =
  1597                 new SharpDisplayClientIdle.StartParams(new Point(this.Right, this.Top), aTopText, aBottomText);
  1598             clientThread.Start(myParams);
  1599             BringToFront();
  1600         }
  1601 
  1602 
  1603         private void buttonStartClient_Click(object sender, EventArgs e)
  1604         {
  1605             StartNewClient();
  1606         }
  1607 
  1608         private void buttonSuspend_Click(object sender, EventArgs e)
  1609         {
  1610             ToggleTimer();
  1611         }
  1612 
  1613         private void StartTimer()
  1614         {
  1615             LastTickTime = DateTime.Now; //Reset timer to prevent jump
  1616             timer.Enabled = true;
  1617             UpdateSuspendButton();
  1618         }
  1619 
  1620         private void StopTimer()
  1621         {
  1622             LastTickTime = DateTime.Now; //Reset timer to prevent jump
  1623             timer.Enabled = false;
  1624             UpdateSuspendButton();
  1625         }
  1626 
  1627         private void ToggleTimer()
  1628         {
  1629             LastTickTime = DateTime.Now; //Reset timer to prevent jump
  1630             timer.Enabled = !timer.Enabled;
  1631             UpdateSuspendButton();
  1632         }
  1633 
  1634         private void UpdateSuspendButton()
  1635         {
  1636             if (!timer.Enabled)
  1637             {
  1638                 buttonSuspend.Text = "Run";
  1639             }
  1640             else
  1641             {
  1642                 buttonSuspend.Text = "Pause";
  1643             }
  1644         }
  1645 
  1646 
  1647         private void buttonCloseClients_Click(object sender, EventArgs e)
  1648         {
  1649             BroadcastCloseEvent();
  1650         }
  1651 
  1652         private void treeViewClients_AfterSelect(object sender, TreeViewEventArgs e)
  1653         {
  1654             //Root node must have at least one child
  1655             if (e.Node.Nodes.Count == 0)
  1656             {
  1657                 return;
  1658             }
  1659 
  1660             //If the selected node is the root node of a client then switch to it
  1661             string sessionId = e.Node.Nodes[0].Text; //First child of a root node is the sessionId
  1662             if (iClients.ContainsKey(sessionId)) //Check that's actually what we are looking at
  1663             {
  1664                 //We have a valid session just switch to that client
  1665                 SetCurrentClient(sessionId, true);
  1666             }
  1667 
  1668         }
  1669 
  1670 
  1671         /// <summary>
  1672         ///
  1673         /// </summary>
  1674         /// <param name="aSessionId"></param>
  1675         /// <param name="aCallback"></param>
  1676         public void AddClientThreadSafe(string aSessionId, ICallback aCallback)
  1677         {
  1678             if (this.InvokeRequired)
  1679             {
  1680                 //Not in the proper thread, invoke ourselves
  1681                 AddClientDelegate d = new AddClientDelegate(AddClientThreadSafe);
  1682                 this.Invoke(d, new object[] {aSessionId, aCallback});
  1683             }
  1684             else
  1685             {
  1686                 //We are in the proper thread
  1687                 //Add this session to our collection of clients
  1688                 ClientData newClient = new ClientData(aSessionId, aCallback);
  1689                 Program.iFormMain.iClients.Add(aSessionId, newClient);
  1690                 //Add this session to our UI
  1691                 UpdateClientTreeViewNode(newClient);
  1692             }
  1693         }
  1694 
  1695 
  1696         /// <summary>
  1697         /// Find the client with the highest priority if any.
  1698         /// </summary>
  1699         /// <returns>Our highest priority client or null if not a single client is connected.</returns>
  1700         public ClientData FindHighestPriorityClient()
  1701         {
  1702             ClientData highestPriorityClient = null;
  1703             foreach (var client in iClients)
  1704             {
  1705                 if (highestPriorityClient == null || client.Value.Priority > highestPriorityClient.Priority)
  1706                 {
  1707                     highestPriorityClient = client.Value;
  1708                 }
  1709             }
  1710 
  1711             return highestPriorityClient;
  1712         }
  1713 
  1714         /// <summary>
  1715         ///
  1716         /// </summary>
  1717         /// <param name="aSessionId"></param>
  1718         public void RemoveClientThreadSafe(string aSessionId)
  1719         {
  1720             if (this.InvokeRequired)
  1721             {
  1722                 //Not in the proper thread, invoke ourselves
  1723                 RemoveClientDelegate d = new RemoveClientDelegate(RemoveClientThreadSafe);
  1724                 this.Invoke(d, new object[] {aSessionId});
  1725             }
  1726             else
  1727             {
  1728                 //We are in the proper thread
  1729                 //Remove this session from both client collection and UI tree view
  1730                 if (Program.iFormMain.iClients.Keys.Contains(aSessionId))
  1731                 {
  1732                     Program.iFormMain.iClients.Remove(aSessionId);
  1733                     Program.iFormMain.iTreeViewClients.Nodes.Remove(
  1734                         Program.iFormMain.iTreeViewClients.Nodes.Find(aSessionId, false)[0]);
  1735                     //Update recording status too whenever a client is removed
  1736                     UpdateRecordingNotification();
  1737                 }
  1738 
  1739                 if (iCurrentClientSessionId == aSessionId)
  1740                 {
  1741                     //The current client is closing
  1742                     iCurrentClientData = null;
  1743                     //Find the client with the highest priority and set it as current
  1744                     ClientData newCurrentClient = FindHighestPriorityClient();
  1745                     if (newCurrentClient != null)
  1746                     {
  1747                         SetCurrentClient(newCurrentClient.SessionId, true);
  1748                     }
  1749                 }
  1750 
  1751                 if (iClients.Count == 0)
  1752                 {
  1753                     //Clear our screen when last client disconnects
  1754                     ClearLayout();
  1755 
  1756                     if (iClosing)
  1757                     {
  1758                         //We were closing our form
  1759                         //All clients are now closed
  1760                         //Just resume our close operation
  1761                         iClosing = false;
  1762                         Close();
  1763                     }
  1764                 }
  1765             }
  1766         }
  1767 
  1768         /// <summary>
  1769         ///
  1770         /// </summary>
  1771         /// <param name="aSessionId"></param>
  1772         /// <param name="aLayout"></param>
  1773         public void SetClientLayoutThreadSafe(string aSessionId, TableLayout aLayout)
  1774         {
  1775             if (this.InvokeRequired)
  1776             {
  1777                 //Not in the proper thread, invoke ourselves
  1778                 SetLayoutDelegate d = new SetLayoutDelegate(SetClientLayoutThreadSafe);
  1779                 this.Invoke(d, new object[] {aSessionId, aLayout});
  1780             }
  1781             else
  1782             {
  1783                 ClientData client = iClients[aSessionId];
  1784                 if (client != null)
  1785                 {
  1786                     //Don't change a thing if the layout is the same
  1787                     if (!client.Layout.IsSameAs(aLayout))
  1788                     {
  1789                         Debug.Print("SetClientLayoutThreadSafe: Layout updated.");
  1790                         //Set our client layout then
  1791                         client.Layout = aLayout;
  1792                         //So that next time we update all our fields at ones
  1793                         client.HasNewLayout = true;
  1794                         //Layout has changed clear our fields then
  1795                         client.Fields.Clear();
  1796                         //
  1797                         UpdateClientTreeViewNode(client);
  1798                     }
  1799                     else
  1800                     {
  1801                         Debug.Print("SetClientLayoutThreadSafe: Layout has not changed.");
  1802                     }
  1803                 }
  1804             }
  1805         }
  1806 
  1807         /// <summary>
  1808         ///
  1809         /// </summary>
  1810         /// <param name="aSessionId"></param>
  1811         /// <param name="aField"></param>
  1812         public void SetClientFieldThreadSafe(string aSessionId, DataField aField)
  1813         {
  1814             if (this.InvokeRequired)
  1815             {
  1816                 //Not in the proper thread, invoke ourselves
  1817                 SetFieldDelegate d = new SetFieldDelegate(SetClientFieldThreadSafe);
  1818                 this.Invoke(d, new object[] {aSessionId, aField});
  1819             }
  1820             else
  1821             {
  1822                 //We are in the proper thread
  1823                 //Call the non-thread-safe variant
  1824                 SetClientField(aSessionId, aField);
  1825             }
  1826         }
  1827 
  1828 
  1829 
  1830 
  1831         /// <summary>
  1832         /// Set a data field in the given client.
  1833         /// </summary>
  1834         /// <param name="aSessionId"></param>
  1835         /// <param name="aField"></param>
  1836         private void SetClientField(string aSessionId, DataField aField)
  1837         {
  1838             //TODO: should check if the field actually changed?
  1839 
  1840             ClientData client = iClients[aSessionId];
  1841             bool layoutChanged = false;
  1842             bool contentChanged = true;
  1843 
  1844             //Fetch our field index
  1845             int fieldIndex = client.FindSameFieldIndex(aField);
  1846 
  1847             if (fieldIndex < 0)
  1848             {
  1849                 //No corresponding field, just bail out
  1850                 return;
  1851             }
  1852 
  1853             //Keep our previous field in there
  1854             DataField previousField = client.Fields[fieldIndex];
  1855             //Just update that field then 
  1856             client.Fields[fieldIndex] = aField;
  1857 
  1858             if (!aField.IsTableField)
  1859             {
  1860                 //We are done then if that field is not in our table layout
  1861                 return;
  1862             }
  1863 
  1864             TableField tableField = (TableField) aField;
  1865 
  1866             if (previousField.IsSameLayout(aField))
  1867             {
  1868                 //If we are updating a field in our current client we need to update it in our panel
  1869                 if (aSessionId == iCurrentClientSessionId)
  1870                 {
  1871                     Control ctrl = iTableLayoutPanel.GetControlFromPosition(tableField.Column, tableField.Row);
  1872                     if (aField.IsTextField && ctrl is MarqueeLabel)
  1873                     {
  1874                         TextField textField = (TextField) aField;
  1875                         //Text field control already in place, just change the text
  1876                         MarqueeLabel label = (MarqueeLabel) ctrl;
  1877                         contentChanged = (label.Text != textField.Text || label.TextAlign != textField.Alignment);
  1878                         label.Text = textField.Text;
  1879                         label.TextAlign = textField.Alignment;
  1880                     }
  1881                     else if (aField.IsBitmapField && ctrl is PictureBox)
  1882                     {
  1883                         BitmapField bitmapField = (BitmapField) aField;
  1884                         contentChanged = true; //TODO: Bitmap comp or should we leave that to clients?
  1885                         //Bitmap field control already in place just change the bitmap
  1886                         PictureBox pictureBox = (PictureBox) ctrl;
  1887                         pictureBox.Image = bitmapField.Bitmap;
  1888                     }
  1889                     else
  1890                     {
  1891                         layoutChanged = true;
  1892                     }
  1893                 }
  1894             }
  1895             else
  1896             {
  1897                 layoutChanged = true;
  1898             }
  1899 
  1900             //If either content or layout changed we need to update our tree view to reflect the changes
  1901             if (contentChanged || layoutChanged)
  1902             {
  1903                 UpdateClientTreeViewNode(client);
  1904                 //
  1905                 if (layoutChanged)
  1906                 {
  1907                     Debug.Print("Layout changed");
  1908                     //Our layout has changed, if we are already the current client we need to update our panel
  1909                     if (aSessionId == iCurrentClientSessionId)
  1910                     {
  1911                         //Apply layout and set data fields.
  1912                         UpdateTableLayoutPanel(iCurrentClientData);
  1913                     }
  1914                 }
  1915                 else
  1916                 {
  1917                     Debug.Print("Layout has not changed.");
  1918                 }
  1919             }
  1920             else
  1921             {
  1922                 Debug.Print("WARNING: content and layout have not changed!");
  1923             }
  1924 
  1925             //When a client field is set we try switching to this client to present the new information to our user
  1926             SetCurrentClient(aSessionId);
  1927         }
  1928 
  1929         /// <summary>
  1930         ///
  1931         /// </summary>
  1932         /// <param name="aTexts"></param>
  1933         public void SetClientFieldsThreadSafe(string aSessionId, System.Collections.Generic.IList<DataField> aFields)
  1934         {
  1935             if (this.InvokeRequired)
  1936             {
  1937                 //Not in the proper thread, invoke ourselves
  1938                 SetFieldsDelegate d = new SetFieldsDelegate(SetClientFieldsThreadSafe);
  1939                 this.Invoke(d, new object[] {aSessionId, aFields});
  1940             }
  1941             else
  1942             {
  1943                 ClientData client = iClients[aSessionId];
  1944 
  1945                 if (client.HasNewLayout)
  1946                 {
  1947                     //TODO: Assert client.Count == 0
  1948                     //Our layout was just changed
  1949                     //Do some special handling to avoid re-creating our panel N times, once for each fields
  1950                     client.HasNewLayout = false;
  1951                     //Just set all our fields then
  1952                     client.Fields.AddRange(aFields);
  1953                     //Try switch to that client
  1954                     SetCurrentClient(aSessionId);
  1955 
  1956                     //If we are updating the current client update our panel
  1957                     if (aSessionId == iCurrentClientSessionId)
  1958                     {
  1959                         //Apply layout and set data fields.
  1960                         UpdateTableLayoutPanel(iCurrentClientData);
  1961                     }
  1962 
  1963                     UpdateClientTreeViewNode(client);
  1964                 }
  1965                 else
  1966                 {
  1967                     //Put each our text fields in a label control
  1968                     foreach (DataField field in aFields)
  1969                     {
  1970                         SetClientField(aSessionId, field);
  1971                     }
  1972                 }
  1973             }
  1974         }
  1975 
  1976         /// <summary>
  1977         ///
  1978         /// </summary>
  1979         /// <param name="aSessionId"></param>
  1980         /// <param name="aName"></param>
  1981         public void SetClientNameThreadSafe(string aSessionId, string aName)
  1982         {
  1983             if (this.InvokeRequired)
  1984             {
  1985                 //Not in the proper thread, invoke ourselves
  1986                 SetClientNameDelegate d = new SetClientNameDelegate(SetClientNameThreadSafe);
  1987                 this.Invoke(d, new object[] {aSessionId, aName});
  1988             }
  1989             else
  1990             {
  1991                 //We are in the proper thread
  1992                 //Get our client
  1993                 ClientData client = iClients[aSessionId];
  1994                 if (client != null)
  1995                 {
  1996                     //Set its name
  1997                     client.Name = aName;
  1998                     //Update our tree-view
  1999                     UpdateClientTreeViewNode(client);
  2000                 }
  2001             }
  2002         }
  2003 
  2004         ///
  2005         public void SetClientPriorityThreadSafe(string aSessionId, uint aPriority)
  2006         {
  2007             if (this.InvokeRequired)
  2008             {
  2009                 //Not in the proper thread, invoke ourselves
  2010                 SetClientPriorityDelegate d = new SetClientPriorityDelegate(SetClientPriorityThreadSafe);
  2011                 this.Invoke(d, new object[] {aSessionId, aPriority});
  2012             }
  2013             else
  2014             {
  2015                 //We are in the proper thread
  2016                 //Get our client
  2017                 ClientData client = iClients[aSessionId];
  2018                 if (client != null)
  2019                 {
  2020                     //Set its name
  2021                     client.Priority = aPriority;
  2022                     //Update our tree-view
  2023                     UpdateClientTreeViewNode(client);
  2024                     //Change our current client as per new priority
  2025                     ClientData newCurrentClient = FindHighestPriorityClient();
  2026                     if (newCurrentClient != null)
  2027                     {
  2028                         SetCurrentClient(newCurrentClient.SessionId);
  2029                     }
  2030                 }
  2031             }
  2032         }
  2033 
  2034         /// <summary>
  2035         /// 
  2036         /// </summary>
  2037         /// <param name="value"></param>
  2038         /// <param name="maxChars"></param>
  2039         /// <returns></returns>
  2040         public static string Truncate(string value, int maxChars)
  2041         {
  2042             return value.Length <= maxChars ? value : value.Substring(0, maxChars - 3) + "...";
  2043         }
  2044 
  2045         /// <summary>
  2046         /// Update our recording notification.
  2047         /// </summary>
  2048         private void UpdateRecordingNotification()
  2049         {
  2050             //Go through each 
  2051             bool activeRecording = false;
  2052             string text = "";
  2053             RecordingField recField = new RecordingField();
  2054             foreach (var client in iClients)
  2055             {
  2056                 RecordingField rec = (RecordingField) client.Value.FindSameFieldAs(recField);
  2057                 if (rec != null && rec.IsActive)
  2058                 {
  2059                     activeRecording = true;
  2060                     //Don't break cause we are collecting the names/texts.
  2061                     if (!String.IsNullOrEmpty(rec.Text))
  2062                     {
  2063                         text += (rec.Text + "\n");
  2064                     }
  2065                     else
  2066                     {
  2067                         //Not text for that recording, use client name instead
  2068                         text += client.Value.Name + " recording\n";
  2069                     }
  2070 
  2071                 }
  2072             }
  2073 
  2074             //Update our text no matter what, can't have more than 63 characters otherwise it throws an exception.
  2075             iRecordingNotification.Text = Truncate(text, 63);
  2076 
  2077             //Change visibility of notification if needed
  2078             if (iRecordingNotification.Visible != activeRecording)
  2079             {
  2080                 iRecordingNotification.Visible = activeRecording;
  2081                 //Assuming the notification icon is in sync with our display icon
  2082                 //Take care of our REC icon
  2083                 if (iDisplay.IsOpen())
  2084                 {
  2085                     iDisplay.SetIconOnOff(MiniDisplay.IconType.Recording, activeRecording);
  2086                 }
  2087             }
  2088         }
  2089 
  2090         /// <summary>
  2091         ///
  2092         /// </summary>
  2093         /// <param name="aClient"></param>
  2094         private void UpdateClientTreeViewNode(ClientData aClient)
  2095         {
  2096             Debug.Print("UpdateClientTreeViewNode");
  2097 
  2098             if (aClient == null)
  2099             {
  2100                 return;
  2101             }
  2102 
  2103             //Hook in record icon update too
  2104             UpdateRecordingNotification();
  2105 
  2106             TreeNode node = null;
  2107             //Check that our client node already exists
  2108             //Get our client root node using its key which is our session ID
  2109             TreeNode[] nodes = iTreeViewClients.Nodes.Find(aClient.SessionId, false);
  2110             if (nodes.Count() > 0)
  2111             {
  2112                 //We already have a node for that client
  2113                 node = nodes[0];
  2114                 //Clear children as we are going to recreate them below
  2115                 node.Nodes.Clear();
  2116             }
  2117             else
  2118             {
  2119                 //Client node does not exists create a new one
  2120                 iTreeViewClients.Nodes.Add(aClient.SessionId, aClient.SessionId);
  2121                 node = iTreeViewClients.Nodes.Find(aClient.SessionId, false)[0];
  2122             }
  2123 
  2124             if (node != null)
  2125             {
  2126                 //Change its name
  2127                 if (!String.IsNullOrEmpty(aClient.Name))
  2128                 {
  2129                     //We have a name, use it as text for our root node
  2130                     node.Text = aClient.Name;
  2131                     //Add a child with SessionId
  2132                     node.Nodes.Add(new TreeNode(aClient.SessionId));
  2133                 }
  2134                 else
  2135                 {
  2136                     //No name, use session ID instead
  2137                     node.Text = aClient.SessionId;
  2138                 }
  2139 
  2140                 //Display client priority
  2141                 node.Nodes.Add(new TreeNode("Priority: " + aClient.Priority));
  2142 
  2143                 if (aClient.Fields.Count > 0)
  2144                 {
  2145                     //Create root node for our texts
  2146                     TreeNode textsRoot = new TreeNode("Fields");
  2147                     node.Nodes.Add(textsRoot);
  2148                     //For each text add a new entry
  2149                     foreach (DataField field in aClient.Fields)
  2150                     {
  2151                         if (field.IsTextField)
  2152                         {
  2153                             TextField textField = (TextField) field;
  2154                             textsRoot.Nodes.Add(new TreeNode("[Text]" + textField.Text));
  2155                         }
  2156                         else if (field.IsBitmapField)
  2157                         {
  2158                             textsRoot.Nodes.Add(new TreeNode("[Bitmap]"));
  2159                         }
  2160                         else if (field.IsRecordingField)
  2161                         {
  2162                             RecordingField recordingField = (RecordingField) field;
  2163                             textsRoot.Nodes.Add(new TreeNode("[Recording]" + recordingField.IsActive));
  2164                         }
  2165                     }
  2166                 }
  2167 
  2168                 node.ExpandAll();
  2169             }
  2170         }
  2171 
  2172         /// <summary>
  2173         /// Update our table layout row styles to make sure each rows have similar height
  2174         /// </summary>
  2175         private void UpdateTableLayoutRowStyles()
  2176         {
  2177             foreach (RowStyle rowStyle in iTableLayoutPanel.RowStyles)
  2178             {
  2179                 rowStyle.SizeType = SizeType.Percent;
  2180                 rowStyle.Height = 100/iTableLayoutPanel.RowCount;
  2181             }
  2182         }
  2183 
  2184         /// <summary>
  2185         /// Update our display table layout.
  2186         /// Will instanciated every field control as defined by our client.
  2187         /// Fields must be specified by rows from the left.
  2188         /// </summary>
  2189         /// <param name="aLayout"></param>
  2190         private void UpdateTableLayoutPanel(ClientData aClient)
  2191         {
  2192             Debug.Print("UpdateTableLayoutPanel");
  2193 
  2194             if (aClient == null)
  2195             {
  2196                 //Just drop it
  2197                 return;
  2198             }
  2199 
  2200 
  2201             TableLayout layout = aClient.Layout;
  2202 
  2203             //First clean our current panel
  2204             iTableLayoutPanel.Controls.Clear();
  2205             iTableLayoutPanel.RowStyles.Clear();
  2206             iTableLayoutPanel.ColumnStyles.Clear();
  2207             iTableLayoutPanel.RowCount = 0;
  2208             iTableLayoutPanel.ColumnCount = 0;
  2209 
  2210             //Then recreate our rows...
  2211             while (iTableLayoutPanel.RowCount < layout.Rows.Count)
  2212             {
  2213                 iTableLayoutPanel.RowCount++;
  2214             }
  2215 
  2216             // ...and columns 
  2217             while (iTableLayoutPanel.ColumnCount < layout.Columns.Count)
  2218             {
  2219                 iTableLayoutPanel.ColumnCount++;
  2220             }
  2221 
  2222             //For each column
  2223             for (int i = 0; i < iTableLayoutPanel.ColumnCount; i++)
  2224             {
  2225                 //Create our column styles
  2226                 this.iTableLayoutPanel.ColumnStyles.Add(layout.Columns[i]);
  2227 
  2228                 //For each rows
  2229                 for (int j = 0; j < iTableLayoutPanel.RowCount; j++)
  2230                 {
  2231                     if (i == 0)
  2232                     {
  2233                         //Create our row styles
  2234                         this.iTableLayoutPanel.RowStyles.Add(layout.Rows[j]);
  2235                     }
  2236                     else
  2237                     {
  2238                         continue;
  2239                     }
  2240                 }
  2241             }
  2242 
  2243             //For each field
  2244             foreach (DataField field in aClient.Fields)
  2245             {
  2246                 if (!field.IsTableField)
  2247                 {
  2248                     //That field is not taking part in our table layout skip it
  2249                     continue;
  2250                 }
  2251 
  2252                 TableField tableField = (TableField) field;
  2253 
  2254                 //Create a control corresponding to the field specified for that cell
  2255                 Control control = CreateControlForDataField(tableField);
  2256 
  2257                 //Add newly created control to our table layout at the specified row and column
  2258                 iTableLayoutPanel.Controls.Add(control, tableField.Column, tableField.Row);
  2259                 //Make sure we specify column and row span for that new control
  2260                 iTableLayoutPanel.SetColumnSpan(control, tableField.ColumnSpan);
  2261                 iTableLayoutPanel.SetRowSpan(control, tableField.RowSpan);
  2262             }
  2263 
  2264 
  2265             CheckFontHeight();
  2266         }
  2267 
  2268         /// <summary>
  2269         /// Check our type of data field and create corresponding control
  2270         /// </summary>
  2271         /// <param name="aField"></param>
  2272         private Control CreateControlForDataField(DataField aField)
  2273         {
  2274             Control control = null;
  2275             if (aField.IsTextField)
  2276             {
  2277                 MarqueeLabel label = new SharpDisplayManager.MarqueeLabel();
  2278                 label.AutoEllipsis = true;
  2279                 label.AutoSize = true;
  2280                 label.BackColor = System.Drawing.Color.Transparent;
  2281                 label.Dock = System.Windows.Forms.DockStyle.Fill;
  2282                 label.Location = new System.Drawing.Point(1, 1);
  2283                 label.Margin = new System.Windows.Forms.Padding(0);
  2284                 label.Name = "marqueeLabel" + aField;
  2285                 label.OwnTimer = false;
  2286                 label.PixelsPerSecond = cds.ScrollingSpeedInPixelsPerSecond;
  2287                 label.Separator = cds.Separator;
  2288                 label.MinFontSize = cds.MinFontSize;
  2289                 label.ScaleToFit = cds.ScaleToFit;
  2290                 //control.Size = new System.Drawing.Size(254, 30);
  2291                 //control.TabIndex = 2;
  2292                 label.Font = cds.Font;
  2293 
  2294                 TextField field = (TextField) aField;
  2295                 label.TextAlign = field.Alignment;
  2296                 label.UseCompatibleTextRendering = true;
  2297                 label.Text = field.Text;
  2298                 //
  2299                 control = label;
  2300             }
  2301             else if (aField.IsBitmapField)
  2302             {
  2303                 //Create picture box
  2304                 PictureBox picture = new PictureBox();
  2305                 picture.AutoSize = true;
  2306                 picture.BackColor = System.Drawing.Color.Transparent;
  2307                 picture.Dock = System.Windows.Forms.DockStyle.Fill;
  2308                 picture.Location = new System.Drawing.Point(1, 1);
  2309                 picture.Margin = new System.Windows.Forms.Padding(0);
  2310                 picture.Name = "pictureBox" + aField;
  2311                 //Set our image
  2312                 BitmapField field = (BitmapField) aField;
  2313                 picture.Image = field.Bitmap;
  2314                 //
  2315                 control = picture;
  2316             }
  2317             //TODO: Handle recording field?
  2318 
  2319             return control;
  2320         }
  2321 
  2322         /// <summary>
  2323         /// Called when the user selected a new display type.
  2324         /// </summary>
  2325         /// <param name="sender"></param>
  2326         /// <param name="e"></param>
  2327         private void comboBoxDisplayType_SelectedIndexChanged(object sender, EventArgs e)
  2328         {
  2329             //Store the selected display type in our settings
  2330             Properties.Settings.Default.CurrentDisplayIndex = comboBoxDisplayType.SelectedIndex;
  2331             cds.DisplayType = comboBoxDisplayType.SelectedIndex;
  2332             Properties.Settings.Default.Save();
  2333 
  2334             //Try re-opening the display connection if we were already connected.
  2335             //Otherwise just update our status to reflect display type change.
  2336             if (iDisplay.IsOpen())
  2337             {
  2338                 OpenDisplayConnection();
  2339             }
  2340             else
  2341             {
  2342                 UpdateStatus();
  2343             }
  2344         }
  2345 
  2346         private void maskedTextBoxTimerInterval_TextChanged(object sender, EventArgs e)
  2347         {
  2348             if (maskedTextBoxTimerInterval.Text != "")
  2349             {
  2350                 int interval = Convert.ToInt32(maskedTextBoxTimerInterval.Text);
  2351 
  2352                 if (interval > 0)
  2353                 {
  2354                     timer.Interval = interval;
  2355                     cds.TimerInterval = timer.Interval;
  2356                     Properties.Settings.Default.Save();
  2357                 }
  2358             }
  2359         }
  2360 
  2361         private void maskedTextBoxMinFontSize_TextChanged(object sender, EventArgs e)
  2362         {
  2363             if (maskedTextBoxMinFontSize.Text != "")
  2364             {
  2365                 int minFontSize = Convert.ToInt32(maskedTextBoxMinFontSize.Text);
  2366 
  2367                 if (minFontSize > 0)
  2368                 {
  2369                     cds.MinFontSize = minFontSize;
  2370                     Properties.Settings.Default.Save();
  2371                     //We need to recreate our layout for that change to take effect
  2372                     UpdateTableLayoutPanel(iCurrentClientData);
  2373                 }
  2374             }
  2375         }
  2376 
  2377 
  2378         private void maskedTextBoxScrollingSpeed_TextChanged(object sender, EventArgs e)
  2379         {
  2380             if (maskedTextBoxScrollingSpeed.Text != "")
  2381             {
  2382                 int scrollingSpeed = Convert.ToInt32(maskedTextBoxScrollingSpeed.Text);
  2383 
  2384                 if (scrollingSpeed > 0)
  2385                 {
  2386                     cds.ScrollingSpeedInPixelsPerSecond = scrollingSpeed;
  2387                     Properties.Settings.Default.Save();
  2388                     //We need to recreate our layout for that change to take effect
  2389                     UpdateTableLayoutPanel(iCurrentClientData);
  2390                 }
  2391             }
  2392         }
  2393 
  2394         private void textBoxScrollLoopSeparator_TextChanged(object sender, EventArgs e)
  2395         {
  2396             cds.Separator = textBoxScrollLoopSeparator.Text;
  2397             Properties.Settings.Default.Save();
  2398 
  2399             //Update our text fields
  2400             foreach (MarqueeLabel ctrl in iTableLayoutPanel.Controls)
  2401             {
  2402                 ctrl.Separator = cds.Separator;
  2403             }
  2404 
  2405         }
  2406 
  2407         private void buttonPowerOn_Click(object sender, EventArgs e)
  2408         {
  2409             iDisplay.PowerOn();
  2410         }
  2411 
  2412         private void buttonPowerOff_Click(object sender, EventArgs e)
  2413         {
  2414             iDisplay.PowerOff();
  2415         }
  2416 
  2417         private void buttonShowClock_Click(object sender, EventArgs e)
  2418         {
  2419             ShowClock();
  2420         }
  2421 
  2422         private void buttonHideClock_Click(object sender, EventArgs e)
  2423         {
  2424             HideClock();
  2425         }
  2426 
  2427         private void buttonUpdate_Click(object sender, EventArgs e)
  2428         {
  2429             InstallUpdateSyncWithInfo();
  2430         }
  2431 
  2432         /// <summary>
  2433         /// 
  2434         /// </summary>
  2435         void ShowClock()
  2436         {
  2437             if (!iDisplay.IsOpen())
  2438             {
  2439                 return;
  2440             }
  2441 
  2442             //Devices like MDM166AA don't support windowing and frame rendering must be stopped while showing our clock
  2443             iSkipFrameRendering = true;
  2444             //Clear our screen 
  2445             iDisplay.Clear();
  2446             iDisplay.SwapBuffers();
  2447             //Then show our clock
  2448             iDisplay.ShowClock();
  2449         }
  2450 
  2451         /// <summary>
  2452         /// 
  2453         /// </summary>
  2454         void HideClock()
  2455         {
  2456             if (!iDisplay.IsOpen())
  2457             {
  2458                 return;
  2459             }
  2460 
  2461             //Devices like MDM166AA don't support windowing and frame rendering must be stopped while showing our clock
  2462             iSkipFrameRendering = false;
  2463             iDisplay.HideClock();
  2464         }
  2465 
  2466         private void InstallUpdateSyncWithInfo()
  2467         {
  2468             UpdateCheckInfo info = null;
  2469 
  2470             if (ApplicationDeployment.IsNetworkDeployed)
  2471             {
  2472                 ApplicationDeployment ad = ApplicationDeployment.CurrentDeployment;
  2473 
  2474                 try
  2475                 {
  2476                     info = ad.CheckForDetailedUpdate();
  2477 
  2478                 }
  2479                 catch (DeploymentDownloadException dde)
  2480                 {
  2481                     MessageBox.Show(
  2482                         "The new version of the application cannot be downloaded at this time. \n\nPlease check your network connection, or try again later. Error: " +
  2483                         dde.Message);
  2484                     return;
  2485                 }
  2486                 catch (InvalidDeploymentException ide)
  2487                 {
  2488                     MessageBox.Show(
  2489                         "Cannot check for a new version of the application. The ClickOnce deployment is corrupt. Please redeploy the application and try again. Error: " +
  2490                         ide.Message);
  2491                     return;
  2492                 }
  2493                 catch (InvalidOperationException ioe)
  2494                 {
  2495                     MessageBox.Show(
  2496                         "This application cannot be updated. It is likely not a ClickOnce application. Error: " +
  2497                         ioe.Message);
  2498                     return;
  2499                 }
  2500 
  2501                 if (info.UpdateAvailable)
  2502                 {
  2503                     Boolean doUpdate = true;
  2504 
  2505                     if (!info.IsUpdateRequired)
  2506                     {
  2507                         DialogResult dr =
  2508                             MessageBox.Show("An update is available. Would you like to update the application now?",
  2509                                 "Update Available", MessageBoxButtons.OKCancel);
  2510                         if (!(DialogResult.OK == dr))
  2511                         {
  2512                             doUpdate = false;
  2513                         }
  2514                     }
  2515                     else
  2516                     {
  2517                         // Display a message that the application MUST reboot. Display the minimum required version.
  2518                         MessageBox.Show("This application has detected a mandatory update from your current " +
  2519                                         "version to version " + info.MinimumRequiredVersion.ToString() +
  2520                                         ". The application will now install the update and restart.",
  2521                             "Update Available", MessageBoxButtons.OK,
  2522                             MessageBoxIcon.Information);
  2523                     }
  2524 
  2525                     if (doUpdate)
  2526                     {
  2527                         try
  2528                         {
  2529                             ad.Update();
  2530                             MessageBox.Show("The application has been upgraded, and will now restart.");
  2531                             Application.Restart();
  2532                         }
  2533                         catch (DeploymentDownloadException dde)
  2534                         {
  2535                             MessageBox.Show(
  2536                                 "Cannot install the latest version of the application. \n\nPlease check your network connection, or try again later. Error: " +
  2537                                 dde);
  2538                             return;
  2539                         }
  2540                     }
  2541                 }
  2542                 else
  2543                 {
  2544                     MessageBox.Show("You are already running the latest version.", "Application up-to-date");
  2545                 }
  2546             }
  2547         }
  2548 
  2549 
  2550         /// <summary>
  2551         /// Used to
  2552         /// </summary>
  2553         private void SysTrayHideShow()
  2554         {
  2555             Visible = !Visible;
  2556             if (Visible)
  2557             {
  2558                 Activate();
  2559                 WindowState = FormWindowState.Normal;
  2560             }
  2561         }
  2562 
  2563         /// <summary>
  2564         /// Use to handle minimize events.
  2565         /// </summary>
  2566         /// <param name="sender"></param>
  2567         /// <param name="e"></param>
  2568         private void MainForm_SizeChanged(object sender, EventArgs e)
  2569         {
  2570             if (WindowState == FormWindowState.Minimized && Properties.Settings.Default.MinimizeToTray)
  2571             {
  2572                 if (Visible)
  2573                 {
  2574                     SysTrayHideShow();
  2575                 }
  2576             }
  2577         }
  2578 
  2579         /// <summary>
  2580         /// 
  2581         /// </summary>
  2582         /// <param name="sender"></param>
  2583         /// <param name="e"></param>
  2584         private void tableLayoutPanel_SizeChanged(object sender, EventArgs e)
  2585         {
  2586             //Our table layout size has changed which means our display size has changed.
  2587             //We need to re-create our bitmap.
  2588             iCreateBitmap = true;
  2589         }
  2590 
  2591         /// <summary>
  2592         /// 
  2593         /// </summary>
  2594         /// <param name="sender"></param>
  2595         /// <param name="e"></param>
  2596         private void buttonSelectFile_Click(object sender, EventArgs e)
  2597         {
  2598             //openFileDialog1.InitialDirectory = "c:\\";
  2599             //openFileDialog.Filter = "EXE files (*.exe)|*.exe|All files (*.*)|*.*";
  2600             //openFileDialog.FilterIndex = 1;
  2601             openFileDialog.RestoreDirectory = true;
  2602 
  2603             if (DlgBox.ShowDialog(openFileDialog) == DialogResult.OK)
  2604             {
  2605                 labelStartFileName.Text = openFileDialog.FileName;
  2606                 Properties.Settings.Default.StartFileName = openFileDialog.FileName;
  2607                 Properties.Settings.Default.Save();
  2608             }
  2609         }
  2610 
  2611         /// <summary>
  2612         /// 
  2613         /// </summary>
  2614         /// <param name="sender"></param>
  2615         /// <param name="e"></param>
  2616         private void comboBoxOpticalDrives_SelectedIndexChanged(object sender, EventArgs e)
  2617         {
  2618             //Save the optical drive the user selected for ejection
  2619             Properties.Settings.Default.OpticalDriveToEject = comboBoxOpticalDrives.SelectedItem.ToString();
  2620             Properties.Settings.Default.Save();
  2621         }
  2622 
  2623 
  2624         /// <summary>
  2625         /// 
  2626         /// </summary>
  2627         private void LogsUpdate()
  2628         {
  2629             if (iWriter != null)
  2630             {
  2631                 iWriter.Flush();
  2632             }
  2633 
  2634         }
  2635 
  2636         /// <summary>
  2637         /// Broadcast messages to subscribers.
  2638         /// </summary>
  2639         /// <param name="message"></param>
  2640         protected override void WndProc(ref Message aMessage)
  2641         {
  2642             LogsUpdate();
  2643 
  2644             if (OnWndProc != null)
  2645             {
  2646                 OnWndProc(ref aMessage);
  2647             }
  2648 
  2649             base.WndProc(ref aMessage);
  2650         }
  2651 
  2652         private void checkBoxCecEnabled_CheckedChanged(object sender, EventArgs e)
  2653         {
  2654             //Save CEC enabled status
  2655             Properties.Settings.Default.CecEnabled = checkBoxCecEnabled.Checked;
  2656             Properties.Settings.Default.Save();
  2657             //
  2658             ResetCec();
  2659         }
  2660 
  2661         private void comboBoxHdmiPort_SelectedIndexChanged(object sender, EventArgs e)
  2662         {
  2663             //Save CEC HDMI port
  2664             Properties.Settings.Default.CecHdmiPort = Convert.ToByte(comboBoxHdmiPort.SelectedIndex);
  2665             Properties.Settings.Default.CecHdmiPort++;
  2666             Properties.Settings.Default.Save();
  2667             //
  2668             ResetCec();
  2669         }
  2670 
  2671         /// <summary>
  2672         /// 
  2673         /// </summary>
  2674         private void ResetCec()
  2675         {
  2676             if (iCecManager == null)
  2677             {
  2678                 //Thus skipping initial UI setup
  2679                 return;
  2680             }
  2681 
  2682             iCecManager.Stop();
  2683             //
  2684             if (Properties.Settings.Default.CecEnabled)
  2685             {
  2686                 iCecManager.Start(Handle, "CEC",
  2687                     Properties.Settings.Default.CecHdmiPort);
  2688 
  2689                 SetupCecLogLevel();
  2690             }
  2691         }
  2692 
  2693         /// <summary>
  2694         /// 
  2695         /// </summary>
  2696         private void SetupCecLogLevel()
  2697         {
  2698             //Setup log level
  2699             iCecManager.Client.LogLevel = 0;
  2700 
  2701             if (checkBoxCecLogError.Checked)
  2702                 iCecManager.Client.LogLevel |= (int) CecLogLevel.Error;
  2703 
  2704             if (checkBoxCecLogWarning.Checked)
  2705                 iCecManager.Client.LogLevel |= (int) CecLogLevel.Warning;
  2706 
  2707             if (checkBoxCecLogNotice.Checked)
  2708                 iCecManager.Client.LogLevel |= (int) CecLogLevel.Notice;
  2709 
  2710             if (checkBoxCecLogTraffic.Checked)
  2711                 iCecManager.Client.LogLevel |= (int) CecLogLevel.Traffic;
  2712 
  2713             if (checkBoxCecLogDebug.Checked)
  2714                 iCecManager.Client.LogLevel |= (int) CecLogLevel.Debug;
  2715 
  2716             iCecManager.Client.FilterOutPollLogs = checkBoxCecLogNoPoll.Checked;
  2717 
  2718         }
  2719 
  2720         private void ButtonStartIdleClient_Click(object sender, EventArgs e)
  2721         {
  2722             StartIdleClient();
  2723         }
  2724 
  2725         private void buttonClearLogs_Click(object sender, EventArgs e)
  2726         {
  2727             richTextBoxLogs.Clear();
  2728         }
  2729 
  2730         private void checkBoxCecLogs_CheckedChanged(object sender, EventArgs e)
  2731         {
  2732             SetupCecLogLevel();
  2733         }
  2734 
  2735 
  2736         /// <summary>
  2737         /// 
  2738         /// </summary>
  2739         /// <param name="aEvent"></param>
  2740         private void SelectEvent(Event aEvent)
  2741         {
  2742             if (aEvent == null)
  2743             {
  2744                 return;
  2745             }
  2746 
  2747             foreach (TreeNode node in iTreeViewEvents.Nodes)
  2748             {
  2749                 if (node.Tag == aEvent)
  2750                 {
  2751                     iTreeViewEvents.SelectedNode = node;
  2752                     iTreeViewEvents.Focus();
  2753                 }
  2754             }
  2755         }
  2756 
  2757 
  2758 
  2759         /// <summary>
  2760         /// Get the current event based on event tree view selection.
  2761         /// </summary>
  2762         /// <returns></returns>
  2763         private Event CurrentEvent()
  2764         {
  2765             //Walk up the tree from the selected node to find our event
  2766             TreeNode node = iTreeViewEvents.SelectedNode;
  2767             Event selectedEvent = null;
  2768             while (node != null)
  2769             {
  2770                 if (node.Tag is Event)
  2771                 {
  2772                     selectedEvent = (Event) node.Tag;
  2773                     break;
  2774                 }
  2775                 node = node.Parent;
  2776             }
  2777 
  2778             return selectedEvent;
  2779         }
  2780 
  2781         /// <summary>
  2782         /// Get the current action based on event tree view selection
  2783         /// </summary>
  2784         /// <returns></returns>
  2785         private SharpLib.Ear.Action CurrentAction()
  2786         {
  2787             TreeNode node = iTreeViewEvents.SelectedNode;
  2788             if (node != null && node.Tag is SharpLib.Ear.Action)
  2789             {
  2790                 return (SharpLib.Ear.Action) node.Tag;
  2791             }
  2792 
  2793             return null;
  2794         }
  2795 
  2796         /// <summary>
  2797         /// 
  2798         /// </summary>
  2799         /// <param name="sender"></param>
  2800         /// <param name="e"></param>
  2801         private void buttonActionAdd_Click(object sender, EventArgs e)
  2802         {
  2803             Event selectedEvent = CurrentEvent();
  2804             if (selectedEvent == null)
  2805             {
  2806                 //We did not find a corresponding event
  2807                 return;
  2808             }
  2809 
  2810             FormEditObject<SharpLib.Ear.Action> ea = new FormEditObject<SharpLib.Ear.Action>();
  2811             ea.Text = "Add action";
  2812             DialogResult res = CodeProject.Dialog.DlgBox.ShowDialog(ea);
  2813             if (res == DialogResult.OK)
  2814             {
  2815                 selectedEvent.Actions.Add(ea.Object);
  2816                 Properties.Settings.Default.Actions = ManagerEventAction.Current;
  2817                 Properties.Settings.Default.Save();
  2818                 PopulateEventsTreeView();
  2819             }
  2820         }
  2821 
  2822         /// <summary>
  2823         /// 
  2824         /// </summary>
  2825         /// <param name="sender"></param>
  2826         /// <param name="e"></param>
  2827         private void buttonActionEdit_Click(object sender, EventArgs e)
  2828         {
  2829             Event selectedEvent = CurrentEvent();
  2830             SharpLib.Ear.Action selectedAction = CurrentAction();
  2831             if (selectedEvent == null || selectedAction == null)
  2832             {
  2833                 //We did not find a corresponding event
  2834                 return;
  2835             }
  2836 
  2837             FormEditObject<SharpLib.Ear.Action> ea = new FormEditObject<SharpLib.Ear.Action>();
  2838             ea.Text = "Edit action";
  2839             ea.Object = selectedAction;
  2840             int actionIndex = iTreeViewEvents.SelectedNode.Index;
  2841             DialogResult res = CodeProject.Dialog.DlgBox.ShowDialog(ea);
  2842             if (res == DialogResult.OK)
  2843             {
  2844                 //Update our action
  2845                 selectedEvent.Actions[actionIndex]=ea.Object;
  2846                 //Save and rebuild our event tree view
  2847                 Properties.Settings.Default.Actions = ManagerEventAction.Current;
  2848                 Properties.Settings.Default.Save();
  2849                 PopulateEventsTreeView();
  2850             }
  2851         }
  2852 
  2853         /// <summary>
  2854         /// 
  2855         /// </summary>
  2856         /// <param name="sender"></param>
  2857         /// <param name="e"></param>
  2858         private void buttonActionDelete_Click(object sender, EventArgs e)
  2859         {
  2860 
  2861             SharpLib.Ear.Action action = CurrentAction();
  2862             if (action == null)
  2863             {
  2864                 //Must select action node
  2865                 return;
  2866             }
  2867 
  2868             ManagerEventAction.Current.RemoveAction(action);
  2869             Properties.Settings.Default.Actions = ManagerEventAction.Current;
  2870             Properties.Settings.Default.Save();
  2871             PopulateEventsTreeView();
  2872         }
  2873 
  2874         /// <summary>
  2875         /// 
  2876         /// </summary>
  2877         /// <param name="sender"></param>
  2878         /// <param name="e"></param>
  2879         private void buttonActionTest_Click(object sender, EventArgs e)
  2880         {
  2881             SharpLib.Ear.Action a = CurrentAction();
  2882             if (a != null)
  2883             {
  2884                 a.Test();
  2885             }
  2886             iTreeViewEvents.Focus();
  2887         }
  2888 
  2889         /// <summary>
  2890         /// 
  2891         /// </summary>
  2892         /// <param name="sender"></param>
  2893         /// <param name="e"></param>
  2894         private void buttonActionMoveUp_Click(object sender, EventArgs e)
  2895         {
  2896             SharpLib.Ear.Action a = CurrentAction();
  2897             if (a == null || 
  2898                 //Action already at the top of the list
  2899                 iTreeViewEvents.SelectedNode.Index == 0)
  2900             {
  2901                 return;
  2902             }
  2903 
  2904             //Swap actions in event's action list
  2905             Event currentEvent = CurrentEvent();
  2906             int currentIndex = iTreeViewEvents.SelectedNode.Index;
  2907             SharpLib.Ear.Action movingUp = currentEvent.Actions[currentIndex];
  2908             SharpLib.Ear.Action movingDown = currentEvent.Actions[currentIndex-1];
  2909             currentEvent.Actions[currentIndex] = movingDown;
  2910             currentEvent.Actions[currentIndex-1] = movingUp;
  2911 
  2912             //Save and populate our tree again
  2913             Properties.Settings.Default.Actions = ManagerEventAction.Current;
  2914             Properties.Settings.Default.Save();
  2915             PopulateEventsTreeView();
  2916 
  2917         }
  2918 
  2919         /// <summary>
  2920         /// 
  2921         /// </summary>
  2922         /// <param name="sender"></param>
  2923         /// <param name="e"></param>
  2924         private void buttonActionMoveDown_Click(object sender, EventArgs e)
  2925         {
  2926             SharpLib.Ear.Action a = CurrentAction();
  2927             if (a == null ||
  2928                 //Action already at the bottom of the list
  2929                 iTreeViewEvents.SelectedNode.Index == iTreeViewEvents.SelectedNode.Parent.Nodes.Count - 1)
  2930             {
  2931                 return;
  2932             }
  2933 
  2934             //Swap actions in event's action list
  2935             Event currentEvent = CurrentEvent();
  2936             int currentIndex = iTreeViewEvents.SelectedNode.Index;
  2937             SharpLib.Ear.Action movingDown = currentEvent.Actions[currentIndex];
  2938             SharpLib.Ear.Action movingUp = currentEvent.Actions[currentIndex + 1];
  2939             currentEvent.Actions[currentIndex] = movingUp;
  2940             currentEvent.Actions[currentIndex + 1] = movingDown;
  2941 
  2942             //Save and populate our tree again
  2943             Properties.Settings.Default.Actions = ManagerEventAction.Current;
  2944             Properties.Settings.Default.Save();
  2945             PopulateEventsTreeView();
  2946         }
  2947 
  2948 
  2949         /// <summary>
  2950         /// 
  2951         /// </summary>
  2952         /// <param name="sender"></param>
  2953         /// <param name="e"></param>
  2954         private void buttonEventTest_Click(object sender, EventArgs e)
  2955         {
  2956             Event earEvent = CurrentEvent();
  2957             if (earEvent != null)
  2958             {
  2959                 earEvent.Test();
  2960             }
  2961         }
  2962 
  2963         /// <summary>
  2964         /// Manages events and actions buttons according to selected item in event tree.
  2965         /// </summary>
  2966         /// <param name="sender"></param>
  2967         /// <param name="e"></param>
  2968         private void iTreeViewEvents_AfterSelect(object sender, TreeViewEventArgs e)
  2969         {
  2970             UpdateEventView();
  2971         }
  2972 
  2973         /// <summary>
  2974         /// 
  2975         /// </summary>
  2976         private void UpdateEventView()
  2977         {
  2978             //One can always add an event
  2979             buttonEventAdd.Enabled = true;
  2980 
  2981             //Enable buttons according to selected item
  2982             buttonActionAdd.Enabled =
  2983             buttonEventTest.Enabled =
  2984             buttonEventDelete.Enabled =
  2985             buttonEventEdit.Enabled =
  2986                 CurrentEvent() != null;
  2987 
  2988             SharpLib.Ear.Action currentAction = CurrentAction();
  2989             //If an action is selected enable the following buttons
  2990             buttonActionTest.Enabled =
  2991             buttonActionDelete.Enabled =
  2992             buttonActionMoveUp.Enabled =
  2993             buttonActionMoveDown.Enabled =
  2994             buttonActionEdit.Enabled =
  2995                     currentAction != null;
  2996 
  2997             if (currentAction != null)
  2998             {
  2999                 //If an action is selected enable move buttons if needed
  3000                 buttonActionMoveUp.Enabled = iTreeViewEvents.SelectedNode.Index != 0;
  3001                 buttonActionMoveDown.Enabled = iTreeViewEvents.SelectedNode.Index <
  3002                                                iTreeViewEvents.SelectedNode.Parent.Nodes.Count - 1;
  3003             }
  3004         }
  3005 
  3006         private void buttonEventAdd_Click(object sender, EventArgs e)
  3007         {
  3008             FormEditObject<SharpLib.Ear.Event> ea = new FormEditObject<SharpLib.Ear.Event>();
  3009             ea.Text = "Add event";
  3010             DialogResult res = CodeProject.Dialog.DlgBox.ShowDialog(ea);
  3011             if (res == DialogResult.OK)
  3012             {
  3013                 ManagerEventAction.Current.Events.Add(ea.Object);
  3014                 Properties.Settings.Default.Actions = ManagerEventAction.Current;
  3015                 Properties.Settings.Default.Save();
  3016                 PopulateEventsTreeView();
  3017                 SelectEvent(ea.Object);
  3018             }
  3019         }
  3020 
  3021         private void buttonEventDelete_Click(object sender, EventArgs e)
  3022         {
  3023             SharpLib.Ear.Event currentEvent = CurrentEvent();
  3024             if (currentEvent == null)
  3025             {
  3026                 //Must select action node
  3027                 return;
  3028             }
  3029 
  3030             ManagerEventAction.Current.Events.Remove(currentEvent);
  3031             Properties.Settings.Default.Actions = ManagerEventAction.Current;
  3032             Properties.Settings.Default.Save();
  3033             PopulateEventsTreeView();
  3034         }
  3035 
  3036         private void buttonEventEdit_Click(object sender, EventArgs e)
  3037         {
  3038             Event selectedEvent = CurrentEvent();
  3039             if (selectedEvent == null)
  3040             {
  3041                 //We did not find a corresponding event
  3042                 return;
  3043             }
  3044 
  3045             FormEditObject<SharpLib.Ear.Event> ea = new FormEditObject<SharpLib.Ear.Event>();
  3046             ea.Text = "Edit event";
  3047             ea.Object = selectedEvent;
  3048             int actionIndex = iTreeViewEvents.SelectedNode.Index;
  3049             DialogResult res = CodeProject.Dialog.DlgBox.ShowDialog(ea);
  3050             if (res == DialogResult.OK)
  3051             {
  3052                 //Save and rebuild our event tree view
  3053                 Properties.Settings.Default.Actions = ManagerEventAction.Current;
  3054                 Properties.Settings.Default.Save();
  3055                 PopulateEventsTreeView();
  3056             }
  3057         }
  3058 
  3059         private void iTreeViewEvents_Leave(object sender, EventArgs e)
  3060         {
  3061             //Make sure our event tree never looses focus
  3062             ((TreeView) sender).Focus();
  3063         }
  3064     }
  3065 }