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