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