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