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