Server/MainForm.cs
author StephaneLenclud
Wed, 02 Sep 2015 00:24:34 +0200
changeset 151 7f62f2c35288
parent 142 45afadb954ba
child 152 03a1757a38df
permissions -rw-r--r--
Published v0.5.2
Media ejection now working with hardcoded E drive.
     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 //Network
    42 using NETWORKLIST;
    43 //
    44 using SharpDisplayClient;
    45 using SharpDisplay;
    46 using MiniDisplayInterop;
    47 
    48 
    49 namespace SharpDisplayManager
    50 {
    51     //Types declarations
    52     public delegate uint ColorProcessingDelegate(int aX, int aY, uint aPixel);
    53     public delegate int CoordinateTranslationDelegate(System.Drawing.Bitmap aBmp, int aInt);
    54     //Delegates are used for our thread safe method
    55     public delegate void AddClientDelegate(string aSessionId, ICallback aCallback);
    56     public delegate void RemoveClientDelegate(string aSessionId);
    57     public delegate void SetFieldDelegate(string SessionId, DataField aField);
    58     public delegate void SetFieldsDelegate(string SessionId, System.Collections.Generic.IList<DataField> aFields);
    59     public delegate void SetLayoutDelegate(string SessionId, TableLayout aLayout);
    60     public delegate void SetClientNameDelegate(string aSessionId, string aName);
    61 	public delegate void PlainUpdateDelegate();
    62 
    63 
    64     /// <summary>
    65     /// Our Display manager main form
    66     /// </summary>
    67 	[System.ComponentModel.DesignerCategory("Form")]
    68 	public partial class MainForm : MainFormHid, IMMNotificationClient
    69     {
    70         DateTime LastTickTime;
    71         Display iDisplay;
    72         System.Drawing.Bitmap iBmp;
    73         bool iCreateBitmap; //Workaround render to bitmap issues when minimized
    74         ServiceHost iServiceHost;
    75         // Our collection of clients sorted by session id.
    76         public Dictionary<string, ClientData> iClients;
    77         // The name of the client which informations are currently displayed.
    78         public string iCurrentClientSessionId;
    79         ClientData iCurrentClientData;
    80         //
    81         public bool iClosing;
    82 		//
    83 		public bool iSkipFrameRendering;
    84         //Function pointer for pixel color filtering
    85         ColorProcessingDelegate iColorFx;
    86         //Function pointer for pixel X coordinate intercept
    87         CoordinateTranslationDelegate iScreenX;
    88         //Function pointer for pixel Y coordinate intercept
    89         CoordinateTranslationDelegate iScreenY;
    90 		//NAudio
    91 		private MMDeviceEnumerator iMultiMediaDeviceEnumerator;
    92 		private MMDevice iMultiMediaDevice;
    93 		//Network
    94 		private NetworkManager iNetworkManager;
    95 		
    96 
    97 		/// <summary>
    98 		/// Manage run when Windows startup option
    99 		/// </summary>
   100 		private StartupManager iStartupManager;
   101 
   102 		/// <summary>
   103 		/// System tray icon.
   104 		/// </summary>
   105 		private NotifyIconAdv iNotifyIcon;
   106 
   107         public MainForm()
   108         {
   109 			iSkipFrameRendering = false;
   110 			iClosing = false;
   111             iCurrentClientSessionId = "";
   112             iCurrentClientData = null;
   113             LastTickTime = DateTime.Now;
   114 			//Instantiate our display and register for events notifications
   115             iDisplay = new Display();
   116 			iDisplay.OnOpened += OnDisplayOpened;
   117 			iDisplay.OnClosed += OnDisplayClosed;
   118 			//
   119 			iClients = new Dictionary<string, ClientData>();
   120 			iStartupManager = new StartupManager();
   121 			iNotifyIcon = new NotifyIconAdv();
   122 
   123 			//Have our designer initialize its controls
   124             InitializeComponent();
   125 
   126 			//Populate device types
   127 			PopulateDeviceTypes();
   128 
   129 			//Initial status update 
   130             UpdateStatus();
   131 
   132             //We have a bug when drawing minimized and reusing our bitmap
   133             iBmp = new System.Drawing.Bitmap(tableLayoutPanel.Width, tableLayoutPanel.Height, PixelFormat.Format32bppArgb);
   134             iCreateBitmap = false;
   135 
   136 			//Minimize our window if desired
   137 			if (Properties.Settings.Default.StartMinimized)
   138 			{
   139 				WindowState = FormWindowState.Minimized;
   140 			}
   141 
   142         }
   143 
   144 		/// <summary>
   145 		///
   146 		/// </summary>
   147 		/// <param name="sender"></param>
   148 		/// <param name="e"></param>
   149         private void MainForm_Load(object sender, EventArgs e)
   150         {
   151 			//Check if we are running a Click Once deployed application
   152 			if (ApplicationDeployment.IsNetworkDeployed)
   153 			{
   154 				//This is a proper Click Once installation, fetch and show our version number
   155 				this.Text += " - v" + ApplicationDeployment.CurrentDeployment.CurrentVersion;
   156 			}
   157 			else
   158 			{
   159 				//Not a proper Click Once installation, assuming development build then
   160 				this.Text += " - development";
   161 			}
   162 
   163 			//NAudio
   164 			iMultiMediaDeviceEnumerator = new MMDeviceEnumerator();
   165 			iMultiMediaDeviceEnumerator.RegisterEndpointNotificationCallback(this);			
   166 			UpdateAudioDeviceAndMasterVolumeThreadSafe();
   167 
   168 			//Network
   169 			iNetworkManager = new NetworkManager();
   170 			iNetworkManager.OnConnectivityChanged += OnConnectivityChanged;
   171 			UpdateNetworkStatus();
   172 
   173 			//Setup notification icon
   174 			SetupTrayIcon();
   175 
   176 			// To make sure start up with minimize to tray works
   177 			if (WindowState == FormWindowState.Minimized && Properties.Settings.Default.MinimizeToTray)
   178 			{
   179 				Visible = false;
   180 			}
   181 
   182 #if !DEBUG
   183 			//When not debugging we want the screen to be empty until a client takes over
   184 			ClearLayout();
   185 #else
   186 			//When developing we want at least one client for testing
   187 			StartNewClient("abcdefghijklmnopqrst-0123456789","ABCDEFGHIJKLMNOPQRST-0123456789");
   188 #endif
   189 
   190 			//Open display connection on start-up if needed
   191 			if (Properties.Settings.Default.DisplayConnectOnStartup)
   192 			{
   193 				OpenDisplayConnection();
   194 			}
   195 
   196 			//Start our server so that we can get client requests
   197 			StartServer();
   198 
   199 			//Register for HID events
   200 			RegisterHidDevices();
   201         }
   202 
   203 		/// <summary>
   204 		/// Called when our display is opened.
   205 		/// </summary>
   206 		/// <param name="aDisplay"></param>
   207 		private void OnDisplayOpened(Display aDisplay)
   208 		{
   209 			//Make sure we resume frame rendering
   210 			iSkipFrameRendering = false;
   211 
   212 			//Set our screen size now that our display is connected
   213 			//Our panelDisplay is the container of our tableLayoutPanel
   214 			//tableLayoutPanel will resize itself to fit the client size of our panelDisplay
   215 			//panelDisplay needs an extra 2 pixels for borders on each sides
   216 			//tableLayoutPanel will eventually be the exact size of our display
   217 			Size size = new Size(iDisplay.WidthInPixels() + 2, iDisplay.HeightInPixels() + 2);
   218 			panelDisplay.Size = size;
   219 
   220 			//Our display was just opened, update our UI
   221 			UpdateStatus();
   222 			//Initiate asynchronous request
   223 			iDisplay.RequestFirmwareRevision();
   224 
   225 			//Audio
   226 			UpdateMasterVolumeThreadSafe();
   227 			//Network
   228 			UpdateNetworkStatus();
   229 
   230 #if DEBUG
   231 			//Testing icon in debug, no arm done if icon not supported
   232 			//iDisplay.SetIconStatus(Display.TMiniDisplayIconType.EMiniDisplayIconRecording, 0, 1);
   233 			//iDisplay.SetAllIconsStatus(2);
   234 #endif
   235 
   236 		}
   237 
   238 		/// <summary>
   239 		/// Called when our display is closed.
   240 		/// </summary>
   241 		/// <param name="aDisplay"></param>
   242 		private void OnDisplayClosed(Display aDisplay)
   243 		{
   244 			//Our display was just closed, update our UI consequently
   245 			UpdateStatus();
   246 		}
   247 
   248 		public void OnConnectivityChanged(NetworkManager aNetwork, NLM_CONNECTIVITY newConnectivity)
   249 		{
   250 			//Update network status
   251 			UpdateNetworkStatus();			
   252 		}
   253 
   254 		/// <summary>
   255 		/// Update our Network Status
   256 		/// </summary>
   257 		private void UpdateNetworkStatus()
   258 		{
   259 			if (iDisplay.IsOpen())
   260 			{
   261                 iDisplay.SetIconOnOff(MiniDisplay.IconType.Internet, iNetworkManager.NetworkListManager.IsConnectedToInternet);
   262                 iDisplay.SetIconOnOff(MiniDisplay.IconType.NetworkSignal, iNetworkManager.NetworkListManager.IsConnected);
   263 			}
   264 		}
   265 
   266 
   267 		int iLastNetworkIconIndex = 0;
   268 		int iUpdateCountSinceLastNetworkAnimation = 0;
   269 
   270 		/// <summary>
   271 		/// 
   272 		/// </summary>
   273 		private void UpdateNetworkSignal(DateTime aLastTickTime, DateTime aNewTickTime)
   274 		{
   275 			iUpdateCountSinceLastNetworkAnimation++;
   276 			iUpdateCountSinceLastNetworkAnimation = iUpdateCountSinceLastNetworkAnimation % 4;
   277 
   278 			if (iDisplay.IsOpen() && iNetworkManager.NetworkListManager.IsConnected && iUpdateCountSinceLastNetworkAnimation==0)
   279 			{
   280                 int iconCount = iDisplay.IconCount(MiniDisplay.IconType.NetworkSignal);
   281 				if (iconCount <= 0)
   282 				{
   283 					//Prevents div by zero and other undefined behavior
   284 					return;
   285 				}
   286 				iLastNetworkIconIndex++;
   287 				iLastNetworkIconIndex = iLastNetworkIconIndex % (iconCount*2);
   288 				for (int i=0;i<iconCount;i++)
   289 				{
   290 					if (i < iLastNetworkIconIndex && !(i == 0 && iLastNetworkIconIndex > 3) && !(i == 1 && iLastNetworkIconIndex > 4))
   291 					{
   292                         iDisplay.SetIconOn(MiniDisplay.IconType.NetworkSignal, i);
   293 					}
   294 					else
   295 					{
   296                         iDisplay.SetIconOff(MiniDisplay.IconType.NetworkSignal, i);
   297 					}
   298 				}				
   299 			}
   300 		}
   301 
   302 
   303 
   304         /// <summary>
   305         /// Receive volume change notification and reflect changes on our slider.
   306         /// </summary>
   307         /// <param name="data"></param>
   308         public void OnVolumeNotificationThreadSafe(AudioVolumeNotificationData data)
   309         {
   310 			UpdateMasterVolumeThreadSafe();
   311         }
   312 
   313         /// <summary>
   314         /// Update master volume when user moves our slider.
   315         /// </summary>
   316         /// <param name="sender"></param>
   317         /// <param name="e"></param>
   318         private void trackBarMasterVolume_Scroll(object sender, EventArgs e)
   319         {
   320 			//Just like Windows Volume Mixer we unmute if the volume is adjusted
   321 			iMultiMediaDevice.AudioEndpointVolume.Mute = false;
   322 			//Set volume level according to our volume slider new position
   323 			iMultiMediaDevice.AudioEndpointVolume.MasterVolumeLevelScalar = trackBarMasterVolume.Value / 100.0f;
   324         }
   325 
   326 
   327 		/// <summary>
   328 		/// Mute check box changed.
   329 		/// </summary>
   330 		/// <param name="sender"></param>
   331 		/// <param name="e"></param>
   332 		private void checkBoxMute_CheckedChanged(object sender, EventArgs e)
   333 		{
   334 			iMultiMediaDevice.AudioEndpointVolume.Mute = checkBoxMute.Checked;
   335 		}
   336 
   337         /// <summary>
   338         /// Device State Changed
   339         /// </summary>
   340         public void OnDeviceStateChanged([MarshalAs(UnmanagedType.LPWStr)] string deviceId, [MarshalAs(UnmanagedType.I4)] DeviceState newState){}
   341 
   342         /// <summary>
   343         /// Device Added
   344         /// </summary>
   345         public void OnDeviceAdded([MarshalAs(UnmanagedType.LPWStr)] string pwstrDeviceId) { }
   346 
   347         /// <summary>
   348         /// Device Removed
   349         /// </summary>
   350         public void OnDeviceRemoved([MarshalAs(UnmanagedType.LPWStr)] string deviceId) { }
   351 
   352         /// <summary>
   353         /// Default Device Changed
   354         /// </summary>
   355         public void OnDefaultDeviceChanged(DataFlow flow, Role role, [MarshalAs(UnmanagedType.LPWStr)] string defaultDeviceId)
   356         {
   357             if (role == Role.Multimedia && flow == DataFlow.Render)
   358             {
   359                 UpdateAudioDeviceAndMasterVolumeThreadSafe();
   360             }
   361         }
   362 
   363         /// <summary>
   364         /// Property Value Changed
   365         /// </summary>
   366         /// <param name="pwstrDeviceId"></param>
   367         /// <param name="key"></param>
   368         public void OnPropertyValueChanged([MarshalAs(UnmanagedType.LPWStr)] string pwstrDeviceId, PropertyKey key){}
   369 
   370 
   371         
   372 
   373 		/// <summary>
   374 		/// Update master volume indicators based our current system states.
   375 		/// This typically includes volume levels and mute status.
   376 		/// </summary>
   377 		private void UpdateMasterVolumeThreadSafe()
   378 		{
   379 			if (this.InvokeRequired)
   380 			{
   381 				//Not in the proper thread, invoke ourselves
   382 				PlainUpdateDelegate d = new PlainUpdateDelegate(UpdateMasterVolumeThreadSafe);
   383 				this.Invoke(d, new object[] { });
   384 				return;
   385 			}
   386 
   387 			//Update volume slider
   388 			float volumeLevelScalar = iMultiMediaDevice.AudioEndpointVolume.MasterVolumeLevelScalar;
   389 			trackBarMasterVolume.Value = Convert.ToInt32(volumeLevelScalar * 100);
   390 			//Update mute checkbox
   391 			checkBoxMute.Checked = iMultiMediaDevice.AudioEndpointVolume.Mute;
   392 
   393 			//If our display connection is open we need to update its icons
   394 			if (iDisplay.IsOpen())
   395 			{
   396 				//First take care our our volume level icons
   397                 int volumeIconCount = iDisplay.IconCount(MiniDisplay.IconType.Volume);
   398 				if (volumeIconCount > 0)
   399 				{					
   400 					//Compute current volume level from system level and the number of segments in our display volume bar.
   401 					//That tells us how many segments in our volume bar needs to be turned on.
   402 					float currentVolume = volumeLevelScalar * volumeIconCount;
   403 					int segmentOnCount = Convert.ToInt32(currentVolume);
   404 					//Check if our segment count was rounded up, this will later be used for half brightness segment
   405 					bool roundedUp = segmentOnCount > currentVolume;
   406 
   407 					for (int i = 0; i < volumeIconCount; i++)
   408 					{
   409 						if (i < segmentOnCount)
   410 						{
   411 							//If we are dealing with our last segment and our segment count was rounded up then we will use half brightness.
   412 							if (i == segmentOnCount - 1 && roundedUp)
   413 							{
   414 								//Half brightness
   415                                 iDisplay.SetIconStatus(MiniDisplay.IconType.Volume, i, (iDisplay.IconStatusCount(MiniDisplay.IconType.Volume) - 1) / 2);
   416 							}
   417 							else
   418 							{
   419 								//Full brightness
   420                                 iDisplay.SetIconStatus(MiniDisplay.IconType.Volume, i, iDisplay.IconStatusCount(MiniDisplay.IconType.Volume) - 1);
   421 							}
   422 						}
   423 						else
   424 						{
   425                             iDisplay.SetIconStatus(MiniDisplay.IconType.Volume, i, 0);
   426 						}
   427 					}
   428 				}
   429 
   430 				//Take care our our mute icon
   431                 iDisplay.SetIconOnOff(MiniDisplay.IconType.Mute, iMultiMediaDevice.AudioEndpointVolume.Mute);
   432 			}
   433 
   434 		}
   435 
   436         /// <summary>
   437         /// 
   438         /// </summary>
   439         private void UpdateAudioDeviceAndMasterVolumeThreadSafe()
   440         {
   441             if (this.InvokeRequired)
   442             {
   443                 //Not in the proper thread, invoke ourselves
   444 				PlainUpdateDelegate d = new PlainUpdateDelegate(UpdateAudioDeviceAndMasterVolumeThreadSafe);
   445                 this.Invoke(d, new object[] { });
   446                 return;
   447             }
   448             
   449             //We are in the correct thread just go ahead.
   450             try
   451             {                
   452                 //Get our master volume            
   453 				iMultiMediaDevice = iMultiMediaDeviceEnumerator.GetDefaultAudioEndpoint(DataFlow.Render, Role.Multimedia);
   454 				//Update our label
   455 				labelDefaultAudioDevice.Text = iMultiMediaDevice.FriendlyName;
   456 
   457                 //Show our volume in our track bar
   458 				UpdateMasterVolumeThreadSafe();
   459 
   460                 //Register to get volume modifications
   461 				iMultiMediaDevice.AudioEndpointVolume.OnVolumeNotification += OnVolumeNotificationThreadSafe;
   462                 //
   463 				trackBarMasterVolume.Enabled = true;
   464             }
   465             catch (Exception ex)
   466             {
   467                 Debug.WriteLine("Exception thrown in UpdateAudioDeviceAndMasterVolume");
   468                 Debug.WriteLine(ex.ToString());
   469                 //Something went wrong S/PDIF device ca throw exception I guess
   470 				trackBarMasterVolume.Enabled = false;
   471             }
   472         }
   473 
   474 		/// <summary>
   475 		/// 
   476 		/// </summary>
   477 		private void PopulateDeviceTypes()
   478 		{
   479 			int count = Display.TypeCount();
   480 
   481 			for (int i = 0; i < count; i++)
   482 			{
   483 				comboBoxDisplayType.Items.Add(Display.TypeName((MiniDisplay.Type)i));
   484 			}
   485 		}
   486 
   487 		/// <summary>
   488 		///
   489 		/// </summary>
   490 		private void SetupTrayIcon()
   491 		{
   492 			iNotifyIcon.Icon = GetIcon("vfd.ico");
   493 			iNotifyIcon.Text = "Sharp Display Manager";
   494 			iNotifyIcon.Visible = true;
   495 
   496 			//Double click toggles visibility - typically brings up the application
   497 			iNotifyIcon.DoubleClick += delegate(object obj, EventArgs args)
   498 			{
   499 				SysTrayHideShow();
   500 			};
   501 
   502 			//Adding a context menu, useful to be able to exit the application
   503 			ContextMenu contextMenu = new ContextMenu();
   504 			//Context menu item to toggle visibility
   505 			MenuItem hideShowItem = new MenuItem("Hide/Show");
   506 			hideShowItem.Click += delegate(object obj, EventArgs args)
   507 			{
   508 				SysTrayHideShow();
   509 			};
   510 			contextMenu.MenuItems.Add(hideShowItem);
   511 
   512 			//Context menu item separator
   513 			contextMenu.MenuItems.Add(new MenuItem("-"));
   514 
   515 			//Context menu exit item
   516 			MenuItem exitItem = new MenuItem("Exit");
   517 			exitItem.Click += delegate(object obj, EventArgs args)
   518 			{
   519 				Application.Exit();
   520 			};
   521 			contextMenu.MenuItems.Add(exitItem);
   522 
   523 			iNotifyIcon.ContextMenu = contextMenu;
   524 		}
   525 
   526 		/// <summary>
   527 		/// Access icons from embedded resources.
   528 		/// </summary>
   529 		/// <param name="name"></param>
   530 		/// <returns></returns>
   531 		public static Icon GetIcon(string name)
   532 		{
   533 			name = "SharpDisplayManager.Resources." + name;
   534 
   535 			string[] names =
   536 			  Assembly.GetExecutingAssembly().GetManifestResourceNames();
   537 			for (int i = 0; i < names.Length; i++)
   538 			{
   539 				if (names[i].Replace('\\', '.') == name)
   540 				{
   541 					using (Stream stream = Assembly.GetExecutingAssembly().
   542 					  GetManifestResourceStream(names[i]))
   543 					{
   544 						return new Icon(stream);
   545 					}
   546 				}
   547 			}
   548 
   549 			return null;
   550 		}
   551 
   552 
   553         /// <summary>
   554         /// Set our current client.
   555         /// This will take care of applying our client layout and set data fields.
   556         /// </summary>
   557         /// <param name="aSessionId"></param>
   558         void SetCurrentClient(string aSessionId, bool aForce=false)
   559         {
   560             if (aSessionId == iCurrentClientSessionId)
   561             {
   562                 //Given client is already the current one.
   563                 //Don't bother changing anything then.
   564                 return;
   565             }
   566 
   567 
   568             //Check when was the last time we switched to that client
   569             if (iCurrentClientData != null)
   570             {
   571                 double lastSwitchToClientSecondsAgo = (DateTime.Now - iCurrentClientData.LastSwitchTime).TotalSeconds;
   572                 //TODO: put that hard coded value as a client property
   573                 //Clients should be able to define how often they can be interrupted
   574                 //Thus a background client can set this to zero allowing any other client to interrupt at any time
   575                 //We could also compute this delay by looking at the requests frequencies?
   576                 if (!aForce && (lastSwitchToClientSecondsAgo < 30)) //Make sure a client is on for at least 30 seconds
   577                 {
   578                     //Don't switch clients too often
   579                     return;
   580                 }
   581             }
   582 
   583             //Set current client ID.
   584             iCurrentClientSessionId = aSessionId;
   585             //Set the time we last switched to that client
   586             iClients[aSessionId].LastSwitchTime = DateTime.Now;
   587             //Fetch and set current client data.
   588             iCurrentClientData = iClients[aSessionId];
   589             //Apply layout and set data fields.
   590             UpdateTableLayoutPanel(iCurrentClientData);
   591         }
   592 
   593         private void buttonFont_Click(object sender, EventArgs e)
   594         {
   595             //fontDialog.ShowColor = true;
   596             //fontDialog.ShowApply = true;
   597             fontDialog.ShowEffects = true;
   598             fontDialog.Font = cds.Font;
   599 
   600             fontDialog.FixedPitchOnly = checkBoxFixedPitchFontOnly.Checked;
   601 
   602             //fontDialog.ShowHelp = true;
   603 
   604             //fontDlg.MaxSize = 40;
   605             //fontDlg.MinSize = 22;
   606 
   607             //fontDialog.Parent = this;
   608             //fontDialog.StartPosition = FormStartPosition.CenterParent;
   609 
   610             //DlgBox.ShowDialog(fontDialog);
   611 
   612             //if (fontDialog.ShowDialog(this) != DialogResult.Cancel)
   613             if (DlgBox.ShowDialog(fontDialog) != DialogResult.Cancel)
   614             {
   615                 //Set the fonts to all our labels in our layout
   616                 foreach (Control ctrl in tableLayoutPanel.Controls)
   617                 {
   618                     if (ctrl is MarqueeLabel)
   619                     {
   620                         ((MarqueeLabel)ctrl).Font = fontDialog.Font;
   621                     }
   622                 }
   623 
   624                 //Save font settings
   625                 cds.Font = fontDialog.Font;
   626                 Properties.Settings.Default.Save();
   627                 //
   628                 CheckFontHeight();
   629             }
   630         }
   631 
   632         /// <summary>
   633         ///
   634         /// </summary>
   635         void CheckFontHeight()
   636         {
   637             //Show font height and width
   638             labelFontHeight.Text = "Font height: " + cds.Font.Height;
   639             float charWidth = IsFixedWidth(cds.Font);
   640             if (charWidth == 0.0f)
   641             {
   642                 labelFontWidth.Visible = false;
   643             }
   644             else
   645             {
   646                 labelFontWidth.Visible = true;
   647                 labelFontWidth.Text = "Font width: " + charWidth;
   648             }
   649 
   650             MarqueeLabel label = null;
   651             //Get the first label control we can find
   652             foreach (Control ctrl in tableLayoutPanel.Controls)
   653             {
   654                 if (ctrl is MarqueeLabel)
   655                 {
   656                     label = (MarqueeLabel)ctrl;
   657                     break;
   658                 }
   659             }
   660 
   661             //Now check font height and show a warning if needed.
   662             if (label != null && label.Font.Height > label.Height)
   663             {
   664                 labelWarning.Text = "WARNING: Selected font is too height by " + (label.Font.Height - label.Height) + " pixels!";
   665                 labelWarning.Visible = true;
   666             }
   667             else
   668             {
   669                 labelWarning.Visible = false;
   670             }
   671 
   672         }
   673 
   674         private void buttonCapture_Click(object sender, EventArgs e)
   675         {
   676             System.Drawing.Bitmap bmp = new System.Drawing.Bitmap(tableLayoutPanel.Width, tableLayoutPanel.Height);
   677             tableLayoutPanel.DrawToBitmap(bmp, tableLayoutPanel.ClientRectangle);
   678             //Bitmap bmpToSave = new Bitmap(bmp);
   679             bmp.Save("D:\\capture.png");
   680 
   681             ((MarqueeLabel)tableLayoutPanel.Controls[0]).Text = "Captured";
   682 
   683             /*
   684             string outputFileName = "d:\\capture.png";
   685             using (MemoryStream memory = new MemoryStream())
   686             {
   687                 using (FileStream fs = new FileStream(outputFileName, FileMode.OpenOrCreate, FileAccess.ReadWrite))
   688                 {
   689                     bmp.Save(memory, System.Drawing.Imaging.ImageFormat.Png);
   690                     byte[] bytes = memory.ToArray();
   691                     fs.Write(bytes, 0, bytes.Length);
   692                 }
   693             }
   694              */
   695 
   696         }
   697 
   698         private void CheckForRequestResults()
   699         {
   700             if (iDisplay.IsRequestPending())
   701             {
   702                 switch (iDisplay.AttemptRequestCompletion())
   703                 {
   704                     case MiniDisplay.Request.FirmwareRevision:
   705                         toolStripStatusLabelConnect.Text += " v" + iDisplay.FirmwareRevision();
   706                         //Issue next request then
   707                         iDisplay.RequestPowerSupplyStatus();
   708                         break;
   709 
   710                     case MiniDisplay.Request.PowerSupplyStatus:
   711                         if (iDisplay.PowerSupplyStatus())
   712                         {
   713                             toolStripStatusLabelPower.Text = "ON";
   714                         }
   715                         else
   716                         {
   717                             toolStripStatusLabelPower.Text = "OFF";
   718                         }
   719                         //Issue next request then
   720                         iDisplay.RequestDeviceId();
   721                         break;
   722 
   723                     case MiniDisplay.Request.DeviceId:
   724                         toolStripStatusLabelConnect.Text += " - " + iDisplay.DeviceId();
   725                         //No more request to issue
   726                         break;
   727                 }
   728             }
   729         }
   730 
   731         public static uint ColorWhiteIsOn(int aX, int aY, uint aPixel)
   732         {
   733             if ((aPixel & 0x00FFFFFF) == 0x00FFFFFF)
   734             {
   735                 return 0xFFFFFFFF;
   736             }
   737             return 0x00000000;
   738         }
   739 
   740         public static uint ColorUntouched(int aX, int aY, uint aPixel)
   741         {
   742             return aPixel;
   743         }
   744 
   745         public static uint ColorInversed(int aX, int aY, uint aPixel)
   746         {
   747             return ~aPixel;
   748         }
   749 
   750         public static uint ColorChessboard(int aX, int aY, uint aPixel)
   751         {
   752             if ((aX % 2 == 0) && (aY % 2 == 0))
   753             {
   754                 return ~aPixel;
   755             }
   756             else if ((aX % 2 != 0) && (aY % 2 != 0))
   757             {
   758                 return ~aPixel;
   759             }
   760             return 0x00000000;
   761         }
   762 
   763 
   764         public static int ScreenReversedX(System.Drawing.Bitmap aBmp, int aX)
   765         {
   766             return aBmp.Width - aX - 1;
   767         }
   768 
   769         public int ScreenReversedY(System.Drawing.Bitmap aBmp, int aY)
   770         {
   771             return iBmp.Height - aY - 1;
   772         }
   773 
   774         public int ScreenX(System.Drawing.Bitmap aBmp, int aX)
   775         {
   776             return aX;
   777         }
   778 
   779         public int ScreenY(System.Drawing.Bitmap aBmp, int aY)
   780         {
   781             return aY;
   782         }
   783 
   784         /// <summary>
   785         /// Select proper pixel delegates according to our current settings.
   786         /// </summary>
   787         private void SetupPixelDelegates()
   788         {
   789             //Select our pixel processing routine
   790             if (cds.InverseColors)
   791             {
   792                 //iColorFx = ColorChessboard;
   793                 iColorFx = ColorInversed;
   794             }
   795             else
   796             {
   797                 iColorFx = ColorWhiteIsOn;
   798             }
   799 
   800             //Select proper coordinate translation functions
   801             //We used delegate/function pointer to support reverse screen without doing an extra test on each pixels
   802             if (cds.ReverseScreen)
   803             {
   804                 iScreenX = ScreenReversedX;
   805                 iScreenY = ScreenReversedY;
   806             }
   807             else
   808             {
   809                 iScreenX = ScreenX;
   810                 iScreenY = ScreenY;
   811             }
   812 
   813         }
   814 
   815         //This is our timer tick responsible to perform our render
   816         private void timer_Tick(object sender, EventArgs e)
   817         {
   818             //Update our animations
   819             DateTime NewTickTime = DateTime.Now;
   820 
   821 			UpdateNetworkSignal(LastTickTime, NewTickTime);
   822 
   823             //Update animation for all our marquees
   824             foreach (Control ctrl in tableLayoutPanel.Controls)
   825             {
   826                 if (ctrl is MarqueeLabel)
   827                 {
   828                     ((MarqueeLabel)ctrl).UpdateAnimation(LastTickTime, NewTickTime);
   829                 }
   830             }
   831 
   832             //Update our display
   833             if (iDisplay.IsOpen())
   834             {
   835                 CheckForRequestResults();
   836 
   837 				//Check if frame rendering is needed
   838 				//Typically used when showing clock
   839 				if (!iSkipFrameRendering)
   840 				{
   841 					//Draw to bitmap
   842 					if (iCreateBitmap)
   843 					{
   844 						iBmp = new System.Drawing.Bitmap(tableLayoutPanel.Width, tableLayoutPanel.Height, PixelFormat.Format32bppArgb);
   845 					}
   846 					tableLayoutPanel.DrawToBitmap(iBmp, tableLayoutPanel.ClientRectangle);
   847 					//iBmp.Save("D:\\capture.png");
   848 
   849 					//Send it to our display
   850 					for (int i = 0; i < iBmp.Width; i++)
   851 					{
   852 						for (int j = 0; j < iBmp.Height; j++)
   853 						{
   854 							unchecked
   855 							{
   856 								//Get our processed pixel coordinates
   857 								int x = iScreenX(iBmp, i);
   858 								int y = iScreenY(iBmp, j);
   859 								//Get pixel color
   860 								uint color = (uint)iBmp.GetPixel(i, j).ToArgb();
   861 								//Apply color effects
   862 								color = iColorFx(x, y, color);
   863 								//Now set our pixel
   864 								iDisplay.SetPixel(x, y, color);
   865 							}
   866 						}
   867 					}
   868 
   869 					iDisplay.SwapBuffers();
   870 				}
   871             }
   872 
   873             //Compute instant FPS
   874             toolStripStatusLabelFps.Text = (1.0/NewTickTime.Subtract(LastTickTime).TotalSeconds).ToString("F0") + " / " + (1000/timer.Interval).ToString() + " FPS";
   875 
   876             LastTickTime = NewTickTime;
   877 
   878         }
   879 
   880 		/// <summary>
   881 		/// Attempt to establish connection with our display hardware.
   882 		/// </summary>
   883         private void OpenDisplayConnection()
   884         {
   885             CloseDisplayConnection();
   886 
   887             if (!iDisplay.Open((MiniDisplay.Type)cds.DisplayType))
   888             {   
   889 				UpdateStatus();               
   890 				toolStripStatusLabelConnect.Text = "Connection error";
   891             }
   892         }
   893 
   894         private void CloseDisplayConnection()
   895         {
   896 			//Status will be updated upon receiving the closed event
   897 
   898 			if (iDisplay == null || !iDisplay.IsOpen())
   899 			{
   900 				return;
   901 			}
   902 
   903 			//Do not clear if we gave up on rendering already.
   904 			//This means we will keep on displaying clock on MDM166AA for instance.
   905 			if (!iSkipFrameRendering)
   906 			{
   907 				iDisplay.Clear();
   908 				iDisplay.SwapBuffers();
   909 			}
   910 
   911 			iDisplay.SetAllIconsStatus(0); //Turn off all icons
   912             iDisplay.Close();
   913         }
   914 
   915         private void buttonOpen_Click(object sender, EventArgs e)
   916         {
   917             OpenDisplayConnection();
   918         }
   919 
   920         private void buttonClose_Click(object sender, EventArgs e)
   921         {
   922             CloseDisplayConnection();
   923         }
   924 
   925         private void buttonClear_Click(object sender, EventArgs e)
   926         {
   927             iDisplay.Clear();
   928             iDisplay.SwapBuffers();
   929         }
   930 
   931         private void buttonFill_Click(object sender, EventArgs e)
   932         {
   933             iDisplay.Fill();
   934             iDisplay.SwapBuffers();
   935         }
   936 
   937         private void trackBarBrightness_Scroll(object sender, EventArgs e)
   938         {
   939             cds.Brightness = trackBarBrightness.Value;
   940             Properties.Settings.Default.Save();
   941             iDisplay.SetBrightness(trackBarBrightness.Value);
   942 
   943         }
   944 
   945 
   946         /// <summary>
   947         /// CDS stands for Current Display Settings
   948         /// </summary>
   949         private DisplaySettings cds
   950         {
   951             get
   952             {
   953                 DisplaysSettings settings = Properties.Settings.Default.DisplaysSettings;
   954                 if (settings == null)
   955                 {
   956                     settings = new DisplaysSettings();
   957                     settings.Init();
   958                     Properties.Settings.Default.DisplaysSettings = settings;
   959                 }
   960 
   961                 //Make sure all our settings have been created
   962                 while (settings.Displays.Count <= Properties.Settings.Default.CurrentDisplayIndex)
   963                 {
   964                     settings.Displays.Add(new DisplaySettings());
   965                 }
   966 
   967                 DisplaySettings displaySettings = settings.Displays[Properties.Settings.Default.CurrentDisplayIndex];
   968                 return displaySettings;
   969             }
   970         }
   971 
   972         /// <summary>
   973         /// Check if the given font has a fixed character pitch.
   974         /// </summary>
   975         /// <param name="ft"></param>
   976         /// <returns>0.0f if this is not a monospace font, otherwise returns the character width.</returns>
   977         public float IsFixedWidth(Font ft)
   978         {
   979             Graphics g = CreateGraphics();
   980             char[] charSizes = new char[] { 'i', 'a', 'Z', '%', '#', 'a', 'B', 'l', 'm', ',', '.' };
   981             float charWidth = g.MeasureString("I", ft, Int32.MaxValue, StringFormat.GenericTypographic).Width;
   982 
   983             bool fixedWidth = true;
   984 
   985             foreach (char c in charSizes)
   986                 if (g.MeasureString(c.ToString(), ft, Int32.MaxValue, StringFormat.GenericTypographic).Width != charWidth)
   987                     fixedWidth = false;
   988 
   989             if (fixedWidth)
   990             {
   991                 return charWidth;
   992             }
   993 
   994             return 0.0f;
   995         }
   996 
   997 		/// <summary>
   998 		/// Synchronize UI with settings
   999 		/// </summary>
  1000         private void UpdateStatus()
  1001         {            
  1002             //Load settings
  1003             checkBoxShowBorders.Checked = cds.ShowBorders;
  1004             tableLayoutPanel.CellBorderStyle = (cds.ShowBorders ? TableLayoutPanelCellBorderStyle.Single : TableLayoutPanelCellBorderStyle.None);
  1005 
  1006             //Set the proper font to each of our labels
  1007             foreach (MarqueeLabel ctrl in tableLayoutPanel.Controls)
  1008             {
  1009                 ctrl.Font = cds.Font;
  1010             }
  1011 
  1012             CheckFontHeight();
  1013 			//Check if "run on Windows startup" is enabled
  1014 			checkBoxAutoStart.Checked = iStartupManager.Startup;
  1015 			//
  1016             checkBoxConnectOnStartup.Checked = Properties.Settings.Default.DisplayConnectOnStartup;
  1017 			checkBoxMinimizeToTray.Checked = Properties.Settings.Default.MinimizeToTray;
  1018 			checkBoxStartMinimized.Checked = Properties.Settings.Default.StartMinimized;
  1019 			labelStartFileName.Text = Properties.Settings.Default.StartFileName;
  1020             checkBoxReverseScreen.Checked = cds.ReverseScreen;
  1021             checkBoxInverseColors.Checked = cds.InverseColors;
  1022 			checkBoxShowVolumeLabel.Checked = cds.ShowVolumeLabel;
  1023             checkBoxScaleToFit.Checked = cds.ScaleToFit;
  1024             maskedTextBoxMinFontSize.Enabled = cds.ScaleToFit;
  1025             labelMinFontSize.Enabled = cds.ScaleToFit;
  1026             maskedTextBoxMinFontSize.Text = cds.MinFontSize.ToString();
  1027 			maskedTextBoxScrollingSpeed.Text = cds.ScrollingSpeedInPixelsPerSecond.ToString();
  1028             comboBoxDisplayType.SelectedIndex = cds.DisplayType;
  1029             timer.Interval = cds.TimerInterval;
  1030             maskedTextBoxTimerInterval.Text = cds.TimerInterval.ToString();
  1031             textBoxScrollLoopSeparator.Text = cds.Separator;
  1032             //
  1033             SetupPixelDelegates();
  1034 
  1035             if (iDisplay.IsOpen())
  1036             {
  1037 				//We have a display connection
  1038 				//Reflect that in our UI
  1039 
  1040 				tableLayoutPanel.Enabled = true;
  1041 				panelDisplay.Enabled = true;
  1042 
  1043                 //Only setup brightness if display is open
  1044                 trackBarBrightness.Minimum = iDisplay.MinBrightness();
  1045                 trackBarBrightness.Maximum = iDisplay.MaxBrightness();
  1046 				if (cds.Brightness < iDisplay.MinBrightness() || cds.Brightness > iDisplay.MaxBrightness())
  1047 				{
  1048 					//Brightness out of range, this can occur when using auto-detect
  1049 					//Use max brightness instead
  1050 					trackBarBrightness.Value = iDisplay.MaxBrightness();
  1051 					iDisplay.SetBrightness(iDisplay.MaxBrightness());
  1052 				}
  1053 				else
  1054 				{
  1055 					trackBarBrightness.Value = cds.Brightness;
  1056 					iDisplay.SetBrightness(cds.Brightness);
  1057 				}
  1058 
  1059 				//Try compute the steps to something that makes sense
  1060                 trackBarBrightness.LargeChange = Math.Max(1, (iDisplay.MaxBrightness() - iDisplay.MinBrightness()) / 5);
  1061                 trackBarBrightness.SmallChange = 1;
  1062                 
  1063                 //
  1064                 buttonFill.Enabled = true;
  1065                 buttonClear.Enabled = true;
  1066                 buttonOpen.Enabled = false;
  1067                 buttonClose.Enabled = true;
  1068                 trackBarBrightness.Enabled = true;
  1069                 toolStripStatusLabelConnect.Text = "Connected - " + iDisplay.Vendor() + " - " + iDisplay.Product();
  1070                 //+ " - " + iDisplay.SerialNumber();
  1071 
  1072                 if (iDisplay.SupportPowerOnOff())
  1073                 {
  1074                     buttonPowerOn.Enabled = true;
  1075                     buttonPowerOff.Enabled = true;
  1076                 }
  1077                 else
  1078                 {
  1079                     buttonPowerOn.Enabled = false;
  1080                     buttonPowerOff.Enabled = false;
  1081                 }
  1082 
  1083                 if (iDisplay.SupportClock())
  1084                 {
  1085                     buttonShowClock.Enabled = true;
  1086                     buttonHideClock.Enabled = true;
  1087                 }
  1088                 else
  1089                 {
  1090                     buttonShowClock.Enabled = false;
  1091                     buttonHideClock.Enabled = false;
  1092                 }
  1093 
  1094 				
  1095 				//Check if Volume Label is supported. To date only MDM166AA supports that crap :)
  1096 				checkBoxShowVolumeLabel.Enabled = iDisplay.IconCount(MiniDisplay.IconType.VolumeLabel)>0;
  1097 
  1098 				if (cds.ShowVolumeLabel)
  1099 				{
  1100                     iDisplay.SetIconOn(MiniDisplay.IconType.VolumeLabel);
  1101 				}
  1102 				else
  1103 				{
  1104                     iDisplay.SetIconOff(MiniDisplay.IconType.VolumeLabel);
  1105 				}
  1106             }
  1107             else
  1108             {
  1109 				//Display is connection not available
  1110 				//Reflect that in our UI
  1111 				checkBoxShowVolumeLabel.Enabled = false;
  1112 				tableLayoutPanel.Enabled = false;
  1113 				panelDisplay.Enabled = false;
  1114                 buttonFill.Enabled = false;
  1115                 buttonClear.Enabled = false;
  1116                 buttonOpen.Enabled = true;
  1117                 buttonClose.Enabled = false;
  1118                 trackBarBrightness.Enabled = false;
  1119                 buttonPowerOn.Enabled = false;
  1120                 buttonPowerOff.Enabled = false;
  1121                 buttonShowClock.Enabled = false;
  1122                 buttonHideClock.Enabled = false;
  1123                 toolStripStatusLabelConnect.Text = "Disconnected";
  1124                 toolStripStatusLabelPower.Text = "N/A";
  1125             }
  1126 
  1127         }
  1128 
  1129 
  1130 		/// <summary>
  1131 		/// 
  1132 		/// </summary>
  1133 		/// <param name="sender"></param>
  1134 		/// <param name="e"></param>
  1135 		private void checkBoxShowVolumeLabel_CheckedChanged(object sender, EventArgs e)
  1136 		{
  1137 			cds.ShowVolumeLabel = checkBoxShowVolumeLabel.Checked;
  1138 			Properties.Settings.Default.Save();
  1139 			UpdateStatus();
  1140 		}
  1141 
  1142         private void checkBoxShowBorders_CheckedChanged(object sender, EventArgs e)
  1143         {
  1144             //Save our show borders setting
  1145             tableLayoutPanel.CellBorderStyle = (checkBoxShowBorders.Checked ? TableLayoutPanelCellBorderStyle.Single : TableLayoutPanelCellBorderStyle.None);
  1146             cds.ShowBorders = checkBoxShowBorders.Checked;
  1147             Properties.Settings.Default.Save();
  1148             CheckFontHeight();
  1149         }
  1150 
  1151         private void checkBoxConnectOnStartup_CheckedChanged(object sender, EventArgs e)
  1152         {
  1153             //Save our connect on startup setting
  1154             Properties.Settings.Default.DisplayConnectOnStartup = checkBoxConnectOnStartup.Checked;
  1155             Properties.Settings.Default.Save();
  1156         }
  1157 
  1158 		private void checkBoxMinimizeToTray_CheckedChanged(object sender, EventArgs e)
  1159 		{
  1160 			//Save our "Minimize to tray" setting
  1161 			Properties.Settings.Default.MinimizeToTray = checkBoxMinimizeToTray.Checked;
  1162 			Properties.Settings.Default.Save();
  1163 
  1164 		}
  1165 
  1166 		private void checkBoxStartMinimized_CheckedChanged(object sender, EventArgs e)
  1167 		{
  1168 			//Save our "Start minimized" setting
  1169 			Properties.Settings.Default.StartMinimized = checkBoxStartMinimized.Checked;
  1170 			Properties.Settings.Default.Save();
  1171 		}
  1172 
  1173 		private void checkBoxAutoStart_CheckedChanged(object sender, EventArgs e)
  1174 		{
  1175 			iStartupManager.Startup = checkBoxAutoStart.Checked;
  1176 		}
  1177 
  1178 
  1179         private void checkBoxReverseScreen_CheckedChanged(object sender, EventArgs e)
  1180         {
  1181             //Save our reverse screen setting
  1182             cds.ReverseScreen = checkBoxReverseScreen.Checked;
  1183             Properties.Settings.Default.Save();
  1184             SetupPixelDelegates();
  1185         }
  1186 
  1187         private void checkBoxInverseColors_CheckedChanged(object sender, EventArgs e)
  1188         {
  1189             //Save our inverse colors setting
  1190             cds.InverseColors = checkBoxInverseColors.Checked;
  1191             Properties.Settings.Default.Save();
  1192             SetupPixelDelegates();
  1193         }
  1194 
  1195         private void checkBoxScaleToFit_CheckedChanged(object sender, EventArgs e)
  1196         {
  1197             //Save our scale to fit setting
  1198             cds.ScaleToFit = checkBoxScaleToFit.Checked;
  1199             Properties.Settings.Default.Save();
  1200             //
  1201             labelMinFontSize.Enabled = cds.ScaleToFit;
  1202             maskedTextBoxMinFontSize.Enabled = cds.ScaleToFit;
  1203         }
  1204 
  1205         private void MainForm_Resize(object sender, EventArgs e)
  1206         {
  1207             if (WindowState == FormWindowState.Minimized)
  1208             {
  1209                 // Do some stuff
  1210                 //iBmp = new System.Drawing.Bitmap(tableLayoutPanel.Width, tableLayoutPanel.Height, PixelFormat.Format32bppArgb);
  1211                 iCreateBitmap = true;
  1212             }
  1213         }
  1214 
  1215         private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
  1216         {
  1217 			iNetworkManager.Dispose();
  1218 			CloseDisplayConnection();
  1219             StopServer();
  1220             e.Cancel = iClosing;
  1221         }
  1222 
  1223         public void StartServer()
  1224         {
  1225             iServiceHost = new ServiceHost
  1226                 (
  1227                     typeof(Session),
  1228                     new Uri[] { new Uri("net.tcp://localhost:8001/") }
  1229                 );
  1230 
  1231             iServiceHost.AddServiceEndpoint(typeof(IService), new NetTcpBinding(SecurityMode.None, true), "DisplayService");
  1232             iServiceHost.Open();
  1233         }
  1234 
  1235         public void StopServer()
  1236         {
  1237             if (iClients.Count > 0 && !iClosing)
  1238             {
  1239                 //Tell our clients
  1240                 iClosing = true;
  1241                 BroadcastCloseEvent();
  1242             }
  1243             else if (iClosing)
  1244             {
  1245                 if (MessageBox.Show("Force exit?", "Waiting for clients...", MessageBoxButtons.YesNo, MessageBoxIcon.Warning) == DialogResult.Yes)
  1246                 {
  1247                     iClosing = false; //We make sure we force close if asked twice
  1248                 }
  1249             }
  1250             else
  1251             {
  1252                 //We removed that as it often lags for some reason
  1253                 //iServiceHost.Close();
  1254             }
  1255         }
  1256 
  1257         public void BroadcastCloseEvent()
  1258         {
  1259             Trace.TraceInformation("BroadcastCloseEvent - start");
  1260 
  1261             var inactiveClients = new List<string>();
  1262             foreach (var client in iClients)
  1263             {
  1264                 //if (client.Key != eventData.ClientName)
  1265                 {
  1266                     try
  1267                     {
  1268                         Trace.TraceInformation("BroadcastCloseEvent - " + client.Key);
  1269                         client.Value.Callback.OnCloseOrder(/*eventData*/);
  1270                     }
  1271                     catch (Exception ex)
  1272                     {
  1273                         inactiveClients.Add(client.Key);
  1274                     }
  1275                 }
  1276             }
  1277 
  1278             if (inactiveClients.Count > 0)
  1279             {
  1280                 foreach (var client in inactiveClients)
  1281                 {
  1282                     iClients.Remove(client);
  1283                     Program.iMainForm.treeViewClients.Nodes.Remove(Program.iMainForm.treeViewClients.Nodes.Find(client, false)[0]);
  1284                 }
  1285             }
  1286 
  1287 			if (iClients.Count==0)
  1288 			{
  1289 				ClearLayout();
  1290 			}
  1291         }
  1292 
  1293 		/// <summary>
  1294 		/// Just remove all our fields.
  1295 		/// </summary>
  1296 		private void ClearLayout()
  1297 		{
  1298 			tableLayoutPanel.Controls.Clear();
  1299 			tableLayoutPanel.RowStyles.Clear();
  1300 			tableLayoutPanel.ColumnStyles.Clear();
  1301 			iCurrentClientData = null;
  1302 		}
  1303 
  1304 		/// <summary>
  1305 		/// Just launch a demo client.
  1306 		/// </summary>
  1307 		private void StartNewClient(string aTopText = "", string aBottomText = "")
  1308 		{
  1309 			Thread clientThread = new Thread(SharpDisplayClient.Program.MainWithParams);
  1310 			SharpDisplayClient.StartParams myParams = new SharpDisplayClient.StartParams(new Point(this.Right, this.Top),aTopText,aBottomText);
  1311 			clientThread.Start(myParams);
  1312 			BringToFront();
  1313 		}
  1314 
  1315         private void buttonStartClient_Click(object sender, EventArgs e)
  1316         {
  1317 			StartNewClient();
  1318         }
  1319 
  1320         private void buttonSuspend_Click(object sender, EventArgs e)
  1321         {
  1322             LastTickTime = DateTime.Now; //Reset timer to prevent jump
  1323             timer.Enabled = !timer.Enabled;
  1324             if (!timer.Enabled)
  1325             {
  1326                 buttonSuspend.Text = "Run";
  1327             }
  1328             else
  1329             {
  1330                 buttonSuspend.Text = "Pause";
  1331             }
  1332         }
  1333 
  1334         private void buttonCloseClients_Click(object sender, EventArgs e)
  1335         {
  1336             BroadcastCloseEvent();
  1337         }
  1338 
  1339         private void treeViewClients_AfterSelect(object sender, TreeViewEventArgs e)
  1340         {
  1341             //Root node must have at least one child
  1342             if (e.Node.Nodes.Count == 0)
  1343             {
  1344                 return;
  1345             }
  1346 
  1347             //If the selected node is the root node of a client then switch to it
  1348             string sessionId=e.Node.Nodes[0].Text; //First child of a root node is the sessionId
  1349             if (iClients.ContainsKey(sessionId)) //Check that's actually what we are looking at
  1350             {
  1351                 //We have a valid session just switch to that client
  1352                 SetCurrentClient(sessionId,true);
  1353             }
  1354             
  1355         }
  1356 
  1357 
  1358         /// <summary>
  1359         ///
  1360         /// </summary>
  1361         /// <param name="aSessionId"></param>
  1362         /// <param name="aCallback"></param>
  1363         public void AddClientThreadSafe(string aSessionId, ICallback aCallback)
  1364         {
  1365             if (this.InvokeRequired)
  1366             {
  1367                 //Not in the proper thread, invoke ourselves
  1368                 AddClientDelegate d = new AddClientDelegate(AddClientThreadSafe);
  1369                 this.Invoke(d, new object[] { aSessionId, aCallback });
  1370             }
  1371             else
  1372             {
  1373                 //We are in the proper thread
  1374                 //Add this session to our collection of clients
  1375                 ClientData newClient = new ClientData(aSessionId, aCallback);
  1376                 Program.iMainForm.iClients.Add(aSessionId, newClient);
  1377                 //Add this session to our UI
  1378                 UpdateClientTreeViewNode(newClient);
  1379             }
  1380         }
  1381 
  1382         /// <summary>
  1383         ///
  1384         /// </summary>
  1385         /// <param name="aSessionId"></param>
  1386         public void RemoveClientThreadSafe(string aSessionId)
  1387         {
  1388             if (this.InvokeRequired)
  1389             {
  1390                 //Not in the proper thread, invoke ourselves
  1391                 RemoveClientDelegate d = new RemoveClientDelegate(RemoveClientThreadSafe);
  1392                 this.Invoke(d, new object[] { aSessionId });
  1393             }
  1394             else
  1395             {
  1396                 //We are in the proper thread
  1397                 //Remove this session from both client collection and UI tree view
  1398                 if (Program.iMainForm.iClients.Keys.Contains(aSessionId))
  1399                 {
  1400                     Program.iMainForm.iClients.Remove(aSessionId);
  1401                     Program.iMainForm.treeViewClients.Nodes.Remove(Program.iMainForm.treeViewClients.Nodes.Find(aSessionId, false)[0]);
  1402                 }
  1403 
  1404 				if (iClients.Count == 0)
  1405 				{
  1406 					//Clear our screen when last client disconnects
  1407 					ClearLayout();
  1408 
  1409 					if (iClosing)
  1410 					{
  1411 						//We were closing our form
  1412 						//All clients are now closed
  1413 						//Just resume our close operation
  1414 						iClosing = false;
  1415 						Close();
  1416 					}
  1417 				}
  1418             }
  1419         }
  1420 
  1421         /// <summary>
  1422         ///
  1423         /// </summary>
  1424         /// <param name="aSessionId"></param>
  1425         /// <param name="aLayout"></param>
  1426         public void SetClientLayoutThreadSafe(string aSessionId, TableLayout aLayout)
  1427         {
  1428             if (this.InvokeRequired)
  1429             {
  1430                 //Not in the proper thread, invoke ourselves
  1431                 SetLayoutDelegate d = new SetLayoutDelegate(SetClientLayoutThreadSafe);
  1432                 this.Invoke(d, new object[] { aSessionId, aLayout });
  1433             }
  1434             else
  1435             {
  1436                 ClientData client = iClients[aSessionId];
  1437                 if (client != null)
  1438                 {
  1439                     //Don't change a thing if the layout is the same
  1440                     if (!client.Layout.IsSameAs(aLayout))
  1441                     {
  1442                         //Set our client layout then
  1443                         client.Layout = aLayout;
  1444                         //
  1445                         UpdateClientTreeViewNode(client);
  1446                     }
  1447                 }
  1448             }
  1449         }
  1450 
  1451         /// <summary>
  1452         ///
  1453         /// </summary>
  1454         /// <param name="aSessionId"></param>
  1455         /// <param name="aField"></param>
  1456         public void SetClientFieldThreadSafe(string aSessionId, DataField aField)
  1457         {
  1458             if (this.InvokeRequired)
  1459             {
  1460                 //Not in the proper thread, invoke ourselves
  1461                 SetFieldDelegate d = new SetFieldDelegate(SetClientFieldThreadSafe);
  1462                 this.Invoke(d, new object[] { aSessionId, aField });
  1463             }
  1464             else
  1465             {
  1466                 //We are in the proper thread
  1467                 //Call the non-thread-safe variant
  1468                 SetClientField(aSessionId, aField);
  1469             }
  1470         }
  1471 
  1472         /// <summary>
  1473         ///
  1474         /// </summary>
  1475         /// <param name="aSessionId"></param>
  1476         /// <param name="aField"></param>
  1477         private void SetClientField(string aSessionId, DataField aField)
  1478         {   
  1479             //TODO: should check if the field actually changed?
  1480 
  1481             ClientData client = iClients[aSessionId];
  1482             bool layoutChanged = false;
  1483             bool contentChanged = true;
  1484 
  1485             //Make sure all our fields are in place
  1486             while (client.Fields.Count < (aField.Index + 1))
  1487             {
  1488                 //Add a data field with proper index
  1489                 client.Fields.Add(new DataField(client.Fields.Count));
  1490                 layoutChanged = true;
  1491             }
  1492 
  1493             //Now that we know hour fields are in place just update that one
  1494             client.Fields[aField.Index] = aField;
  1495 
  1496             if (client.Fields[aField.Index].IsSameLayout(aField))
  1497             {
  1498                 //If we are updating a field in our current client we need to update it in our panel
  1499                 if (aSessionId == iCurrentClientSessionId)
  1500                 {
  1501                     if (aField.IsText && aField.Index < tableLayoutPanel.Controls.Count && tableLayoutPanel.Controls[aField.Index] is MarqueeLabel)
  1502                     {
  1503                         //Text field control already in place, just change the text
  1504                         MarqueeLabel label = (MarqueeLabel)tableLayoutPanel.Controls[aField.Index];
  1505                         contentChanged = (label.Text != aField.Text || label.TextAlign != aField.Alignment);
  1506                         label.Text = aField.Text;
  1507                         label.TextAlign = aField.Alignment;
  1508                     }
  1509                     else if (aField.IsBitmap && aField.Index < tableLayoutPanel.Controls.Count && tableLayoutPanel.Controls[aField.Index] is PictureBox)
  1510                     {
  1511                         contentChanged = true; //TODO: Bitmap comp or should we leave that to clients?
  1512                         //Bitmap field control already in place just change the bitmap
  1513                         PictureBox pictureBox = (PictureBox)tableLayoutPanel.Controls[aField.Index];
  1514                         pictureBox.Image = aField.Bitmap;
  1515                     }
  1516                     else
  1517                     {
  1518                         layoutChanged = true;
  1519                     }
  1520                 }
  1521             }
  1522             else
  1523             {                
  1524                 layoutChanged = true;
  1525             }
  1526 
  1527             //If either content or layout changed we need to update our tree view to reflect the changes
  1528             if (contentChanged || layoutChanged)
  1529             {
  1530                 UpdateClientTreeViewNode(client);
  1531                 //
  1532                 if (layoutChanged)
  1533                 {
  1534                     Debug.Print("Layout changed");
  1535                     //Our layout has changed, if we are already the current client we need to update our panel
  1536                     if (aSessionId == iCurrentClientSessionId)
  1537                     {
  1538                         //Apply layout and set data fields.
  1539                         UpdateTableLayoutPanel(iCurrentClientData);
  1540                     }
  1541                 }
  1542             }
  1543 
  1544             //When a client field is set we try switching to this client to present the new information to our user
  1545             SetCurrentClient(aSessionId);
  1546         }
  1547 
  1548         /// <summary>
  1549         ///
  1550         /// </summary>
  1551         /// <param name="aTexts"></param>
  1552         public void SetClientFieldsThreadSafe(string aSessionId, System.Collections.Generic.IList<DataField> aFields)
  1553         {
  1554             if (this.InvokeRequired)
  1555             {
  1556                 //Not in the proper thread, invoke ourselves
  1557                 SetFieldsDelegate d = new SetFieldsDelegate(SetClientFieldsThreadSafe);
  1558                 this.Invoke(d, new object[] { aSessionId, aFields });
  1559             }
  1560             else
  1561             {
  1562                 //Put each our text fields in a label control
  1563                 foreach (DataField field in aFields)
  1564                 {
  1565                     SetClientField(aSessionId, field);
  1566                 }
  1567             }
  1568         }
  1569 
  1570         /// <summary>
  1571         ///
  1572         /// </summary>
  1573         /// <param name="aSessionId"></param>
  1574         /// <param name="aName"></param>
  1575         public void SetClientNameThreadSafe(string aSessionId, string aName)
  1576         {
  1577             if (this.InvokeRequired)
  1578             {
  1579                 //Not in the proper thread, invoke ourselves
  1580                 SetClientNameDelegate d = new SetClientNameDelegate(SetClientNameThreadSafe);
  1581                 this.Invoke(d, new object[] { aSessionId, aName });
  1582             }
  1583             else
  1584             {
  1585                 //We are in the proper thread
  1586                 //Get our client
  1587                 ClientData client = iClients[aSessionId];
  1588                 if (client != null)
  1589                 {
  1590                     //Set its name
  1591                     client.Name = aName;
  1592                     //Update our tree-view
  1593                     UpdateClientTreeViewNode(client);
  1594                 }
  1595             }
  1596         }
  1597 
  1598         /// <summary>
  1599         ///
  1600         /// </summary>
  1601         /// <param name="aClient"></param>
  1602         private void UpdateClientTreeViewNode(ClientData aClient)
  1603         {
  1604             Debug.Print("UpdateClientTreeViewNode");
  1605 
  1606             if (aClient == null)
  1607             {
  1608                 return;
  1609             }
  1610 
  1611             TreeNode node = null;
  1612             //Check that our client node already exists
  1613             //Get our client root node using its key which is our session ID
  1614             TreeNode[] nodes = treeViewClients.Nodes.Find(aClient.SessionId, false);
  1615             if (nodes.Count()>0)
  1616             {
  1617                 //We already have a node for that client
  1618                 node = nodes[0];
  1619                 //Clear children as we are going to recreate them below
  1620                 node.Nodes.Clear();
  1621             }
  1622             else
  1623             {
  1624                 //Client node does not exists create a new one
  1625                 treeViewClients.Nodes.Add(aClient.SessionId, aClient.SessionId);
  1626                 node = treeViewClients.Nodes.Find(aClient.SessionId, false)[0];
  1627             }
  1628 
  1629             if (node != null)
  1630             {
  1631                 //Change its name
  1632                 if (aClient.Name != "")
  1633                 {
  1634                     //We have a name, us it as text for our root node
  1635                     node.Text = aClient.Name;
  1636                     //Add a child with SessionId
  1637                     node.Nodes.Add(new TreeNode(aClient.SessionId));
  1638                 }
  1639                 else
  1640                 {
  1641                     //No name, use session ID instead
  1642                     node.Text = aClient.SessionId;
  1643                 }
  1644 
  1645                 if (aClient.Fields.Count > 0)
  1646                 {
  1647                     //Create root node for our texts
  1648                     TreeNode textsRoot = new TreeNode("Fields");
  1649                     node.Nodes.Add(textsRoot);
  1650                     //For each text add a new entry
  1651                     foreach (DataField field in aClient.Fields)
  1652                     {
  1653                         if (!field.IsBitmap)
  1654                         {
  1655                             DataField textField = (DataField)field;
  1656                             textsRoot.Nodes.Add(new TreeNode("[Text]" + textField.Text));
  1657                         }
  1658                         else
  1659                         {
  1660                             textsRoot.Nodes.Add(new TreeNode("[Bitmap]"));
  1661                         }
  1662                     }
  1663                 }
  1664 
  1665                 node.ExpandAll();
  1666             }
  1667         }
  1668 
  1669         /// <summary>
  1670         /// Update our table layout row styles to make sure each rows have similar height
  1671         /// </summary>
  1672         private void UpdateTableLayoutRowStyles()
  1673         {
  1674             foreach (RowStyle rowStyle in tableLayoutPanel.RowStyles)
  1675             {
  1676                 rowStyle.SizeType = SizeType.Percent;
  1677                 rowStyle.Height = 100 / tableLayoutPanel.RowCount;
  1678             }
  1679         }
  1680 
  1681         /// <summary>
  1682         /// Update our display table layout.
  1683         /// </summary>
  1684         /// <param name="aLayout"></param>
  1685         private void UpdateTableLayoutPanel(ClientData aClient)
  1686         {
  1687             Debug.Print("UpdateClientTreeViewNode");
  1688 
  1689 			if (aClient == null)
  1690 			{
  1691 				//Just drop it
  1692 				return;
  1693 			}
  1694 
  1695 
  1696             TableLayout layout = aClient.Layout;
  1697             int fieldCount = 0;
  1698 
  1699             //First clean our current panel
  1700             tableLayoutPanel.Controls.Clear();
  1701             tableLayoutPanel.RowStyles.Clear();
  1702             tableLayoutPanel.ColumnStyles.Clear();
  1703             tableLayoutPanel.RowCount = 0;
  1704             tableLayoutPanel.ColumnCount = 0;
  1705 
  1706             while (tableLayoutPanel.RowCount < layout.Rows.Count)
  1707             {
  1708                 tableLayoutPanel.RowCount++;
  1709             }
  1710 
  1711             while (tableLayoutPanel.ColumnCount < layout.Columns.Count)
  1712             {
  1713                 tableLayoutPanel.ColumnCount++;
  1714             }
  1715 
  1716             for (int i = 0; i < tableLayoutPanel.ColumnCount; i++)
  1717             {
  1718                 //Create our column styles
  1719                 this.tableLayoutPanel.ColumnStyles.Add(layout.Columns[i]);
  1720 
  1721                 for (int j = 0; j < tableLayoutPanel.RowCount; j++)
  1722                 {
  1723                     if (i == 0)
  1724                     {
  1725                         //Create our row styles
  1726                         this.tableLayoutPanel.RowStyles.Add(layout.Rows[j]);
  1727                     }
  1728 
  1729                     //Check if we already have a control
  1730                     Control existingControl = tableLayoutPanel.GetControlFromPosition(i,j);
  1731                     if (existingControl!=null)
  1732                     {
  1733                         //We already have a control in that cell as a results of row/col spanning
  1734                         //Move on to next cell then
  1735                         continue;
  1736                     }
  1737 
  1738                     fieldCount++;
  1739 
  1740                     //Check if a client field already exists for that cell
  1741                     if (aClient.Fields.Count <= tableLayoutPanel.Controls.Count)
  1742                     {
  1743                         //No client field specified, create a text field by default
  1744                         aClient.Fields.Add(new DataField(aClient.Fields.Count));
  1745                     }
  1746 
  1747                     //Create a control corresponding to the field specified for that cell
  1748                     DataField field = aClient.Fields[tableLayoutPanel.Controls.Count];
  1749                     Control control = CreateControlForDataField(field);
  1750 
  1751                     //Add newly created control to our table layout at the specified row and column
  1752                     tableLayoutPanel.Controls.Add(control, i, j);
  1753                     //Make sure we specify row and column span for that new control
  1754                     tableLayoutPanel.SetRowSpan(control,field.RowSpan);
  1755                     tableLayoutPanel.SetColumnSpan(control, field.ColumnSpan);
  1756                 }
  1757             }
  1758 
  1759             //
  1760             while (aClient.Fields.Count > fieldCount)
  1761             {
  1762                 //We have too much fields for this layout
  1763                 //Just discard them until we get there
  1764                 aClient.Fields.RemoveAt(aClient.Fields.Count-1);
  1765             }
  1766 
  1767             CheckFontHeight();
  1768         }
  1769 
  1770         /// <summary>
  1771         /// Check our type of data field and create corresponding control
  1772         /// </summary>
  1773         /// <param name="aField"></param>
  1774         private Control CreateControlForDataField(DataField aField)
  1775         {
  1776             Control control=null;
  1777             if (!aField.IsBitmap)
  1778             {
  1779                 MarqueeLabel label = new SharpDisplayManager.MarqueeLabel();
  1780                 label.AutoEllipsis = true;
  1781                 label.AutoSize = true;
  1782                 label.BackColor = System.Drawing.Color.Transparent;
  1783                 label.Dock = System.Windows.Forms.DockStyle.Fill;
  1784                 label.Location = new System.Drawing.Point(1, 1);
  1785                 label.Margin = new System.Windows.Forms.Padding(0);
  1786                 label.Name = "marqueeLabel" + aField.Index;
  1787                 label.OwnTimer = false;
  1788                 label.PixelsPerSecond = cds.ScrollingSpeedInPixelsPerSecond;
  1789                 label.Separator = cds.Separator;
  1790                 label.MinFontSize = cds.MinFontSize;
  1791                 label.ScaleToFit = cds.ScaleToFit;
  1792                 //control.Size = new System.Drawing.Size(254, 30);
  1793                 //control.TabIndex = 2;
  1794                 label.Font = cds.Font;
  1795 
  1796 				label.TextAlign = aField.Alignment;
  1797                 label.UseCompatibleTextRendering = true;
  1798                 label.Text = aField.Text;
  1799                 //
  1800                 control = label;
  1801             }
  1802             else
  1803             {
  1804                 //Create picture box
  1805                 PictureBox picture = new PictureBox();
  1806                 picture.AutoSize = true;
  1807                 picture.BackColor = System.Drawing.Color.Transparent;
  1808                 picture.Dock = System.Windows.Forms.DockStyle.Fill;
  1809                 picture.Location = new System.Drawing.Point(1, 1);
  1810                 picture.Margin = new System.Windows.Forms.Padding(0);
  1811                 picture.Name = "pictureBox" + aField;
  1812                 //Set our image
  1813                 picture.Image = aField.Bitmap;
  1814                 //
  1815                 control = picture;
  1816             }
  1817 
  1818             return control;
  1819         }
  1820 
  1821 		/// <summary>
  1822 		/// Called when the user selected a new display type.
  1823 		/// </summary>
  1824 		/// <param name="sender"></param>
  1825 		/// <param name="e"></param>
  1826         private void comboBoxDisplayType_SelectedIndexChanged(object sender, EventArgs e)
  1827         {
  1828 			//Store the selected display type in our settings
  1829             Properties.Settings.Default.CurrentDisplayIndex = comboBoxDisplayType.SelectedIndex;
  1830             cds.DisplayType = comboBoxDisplayType.SelectedIndex;
  1831             Properties.Settings.Default.Save();
  1832 
  1833 			//Try re-opening the display connection if we were already connected.
  1834 			//Otherwise just update our status to reflect display type change.
  1835             if (iDisplay.IsOpen())
  1836             {
  1837                 OpenDisplayConnection();
  1838             }
  1839             else
  1840             {
  1841                 UpdateStatus();
  1842             }
  1843         }
  1844 
  1845         private void maskedTextBoxTimerInterval_TextChanged(object sender, EventArgs e)
  1846         {
  1847             if (maskedTextBoxTimerInterval.Text != "")
  1848             {
  1849                 int interval = Convert.ToInt32(maskedTextBoxTimerInterval.Text);
  1850 
  1851                 if (interval > 0)
  1852                 {
  1853                     timer.Interval = interval;
  1854                     cds.TimerInterval = timer.Interval;
  1855                     Properties.Settings.Default.Save();
  1856                 }
  1857             }
  1858         }
  1859 
  1860         private void maskedTextBoxMinFontSize_TextChanged(object sender, EventArgs e)
  1861         {
  1862             if (maskedTextBoxMinFontSize.Text != "")
  1863             {
  1864                 int minFontSize = Convert.ToInt32(maskedTextBoxMinFontSize.Text);
  1865 
  1866                 if (minFontSize > 0)
  1867                 {
  1868                     cds.MinFontSize = minFontSize;
  1869                     Properties.Settings.Default.Save();
  1870 					//We need to recreate our layout for that change to take effect
  1871 					UpdateTableLayoutPanel(iCurrentClientData);
  1872                 }
  1873             }
  1874         }
  1875 
  1876 
  1877 		private void maskedTextBoxScrollingSpeed_TextChanged(object sender, EventArgs e)
  1878 		{
  1879 			if (maskedTextBoxScrollingSpeed.Text != "")
  1880 			{
  1881 				int scrollingSpeed = Convert.ToInt32(maskedTextBoxScrollingSpeed.Text);
  1882 
  1883 				if (scrollingSpeed > 0)
  1884 				{
  1885 					cds.ScrollingSpeedInPixelsPerSecond = scrollingSpeed;
  1886 					Properties.Settings.Default.Save();
  1887 					//We need to recreate our layout for that change to take effect
  1888 					UpdateTableLayoutPanel(iCurrentClientData);
  1889 				}
  1890 			}
  1891 		}
  1892 
  1893         private void textBoxScrollLoopSeparator_TextChanged(object sender, EventArgs e)
  1894         {
  1895             cds.Separator = textBoxScrollLoopSeparator.Text;
  1896             Properties.Settings.Default.Save();
  1897 
  1898 			//Update our text fields
  1899 			foreach (MarqueeLabel ctrl in tableLayoutPanel.Controls)
  1900 			{
  1901 				ctrl.Separator = cds.Separator;
  1902 			}
  1903 
  1904         }
  1905 
  1906         private void buttonPowerOn_Click(object sender, EventArgs e)
  1907         {
  1908             iDisplay.PowerOn();
  1909         }
  1910 
  1911         private void buttonPowerOff_Click(object sender, EventArgs e)
  1912         {
  1913             iDisplay.PowerOff();
  1914         }
  1915 
  1916         private void buttonShowClock_Click(object sender, EventArgs e)
  1917         {
  1918 			ShowClock();
  1919         }
  1920 
  1921         private void buttonHideClock_Click(object sender, EventArgs e)
  1922         {
  1923 			HideClock();
  1924         }
  1925 
  1926         private void buttonUpdate_Click(object sender, EventArgs e)
  1927         {
  1928             InstallUpdateSyncWithInfo();
  1929         }
  1930 
  1931 		/// <summary>
  1932 		/// 
  1933 		/// </summary>
  1934 		void ShowClock()
  1935 		{
  1936 			if (!iDisplay.IsOpen())
  1937 			{
  1938 				return;
  1939 			}
  1940 
  1941 			//Devices like MDM166AA don't support windowing and frame rendering must be stopped while showing our clock
  1942 			iSkipFrameRendering = true;
  1943 			//Clear our screen 
  1944 			iDisplay.Clear();
  1945 			iDisplay.SwapBuffers();
  1946 			//Then show our clock
  1947 			iDisplay.ShowClock();
  1948 		}
  1949 
  1950 		/// <summary>
  1951 		/// 
  1952 		/// </summary>
  1953 		void HideClock()
  1954 		{
  1955 			if (!iDisplay.IsOpen())
  1956 			{
  1957 				return;
  1958 			}
  1959 
  1960 			//Devices like MDM166AA don't support windowing and frame rendering must be stopped while showing our clock
  1961 			iSkipFrameRendering = false;
  1962 			iDisplay.HideClock();
  1963 		}
  1964 
  1965         private void InstallUpdateSyncWithInfo()
  1966         {
  1967             UpdateCheckInfo info = null;
  1968 
  1969             if (ApplicationDeployment.IsNetworkDeployed)
  1970             {
  1971                 ApplicationDeployment ad = ApplicationDeployment.CurrentDeployment;
  1972 
  1973                 try
  1974                 {
  1975                     info = ad.CheckForDetailedUpdate();
  1976 
  1977                 }
  1978                 catch (DeploymentDownloadException dde)
  1979                 {
  1980                     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);
  1981                     return;
  1982                 }
  1983                 catch (InvalidDeploymentException ide)
  1984                 {
  1985                     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);
  1986                     return;
  1987                 }
  1988                 catch (InvalidOperationException ioe)
  1989                 {
  1990                     MessageBox.Show("This application cannot be updated. It is likely not a ClickOnce application. Error: " + ioe.Message);
  1991                     return;
  1992                 }
  1993 
  1994 				if (info.UpdateAvailable)
  1995 				{
  1996 					Boolean doUpdate = true;
  1997 
  1998 					if (!info.IsUpdateRequired)
  1999 					{
  2000 						DialogResult dr = MessageBox.Show("An update is available. Would you like to update the application now?", "Update Available", MessageBoxButtons.OKCancel);
  2001 						if (!(DialogResult.OK == dr))
  2002 						{
  2003 							doUpdate = false;
  2004 						}
  2005 					}
  2006 					else
  2007 					{
  2008 						// Display a message that the app MUST reboot. Display the minimum required version.
  2009 						MessageBox.Show("This application has detected a mandatory update from your current " +
  2010 							"version to version " + info.MinimumRequiredVersion.ToString() +
  2011 							". The application will now install the update and restart.",
  2012 							"Update Available", MessageBoxButtons.OK,
  2013 							MessageBoxIcon.Information);
  2014 					}
  2015 
  2016 					if (doUpdate)
  2017 					{
  2018 						try
  2019 						{
  2020 							ad.Update();
  2021 							MessageBox.Show("The application has been upgraded, and will now restart.");
  2022 							Application.Restart();
  2023 						}
  2024 						catch (DeploymentDownloadException dde)
  2025 						{
  2026 							MessageBox.Show("Cannot install the latest version of the application. \n\nPlease check your network connection, or try again later. Error: " + dde);
  2027 							return;
  2028 						}
  2029 					}
  2030 				}
  2031 				else
  2032 				{
  2033 					MessageBox.Show("You are already running the latest version.", "Application up-to-date");
  2034 				}
  2035             }
  2036         }
  2037 
  2038 
  2039 		/// <summary>
  2040 		/// Used to
  2041 		/// </summary>
  2042 		private void SysTrayHideShow()
  2043 		{
  2044 			Visible = !Visible;
  2045 			if (Visible)
  2046 			{
  2047 				Activate();
  2048 				WindowState = FormWindowState.Normal;
  2049 			}
  2050 		}
  2051 
  2052 		/// <summary>
  2053 		/// Use to handle minimize events.
  2054 		/// </summary>
  2055 		/// <param name="sender"></param>
  2056 		/// <param name="e"></param>
  2057 		private void MainForm_SizeChanged(object sender, EventArgs e)
  2058 		{
  2059 			if (WindowState == FormWindowState.Minimized && Properties.Settings.Default.MinimizeToTray)
  2060 			{
  2061 				if (Visible)
  2062 				{
  2063 					SysTrayHideShow();
  2064 				}
  2065 			}
  2066 		}
  2067 
  2068 		/// <summary>
  2069 		/// 
  2070 		/// </summary>
  2071 		/// <param name="sender"></param>
  2072 		/// <param name="e"></param>
  2073 		private void tableLayoutPanel_SizeChanged(object sender, EventArgs e)
  2074 		{
  2075 			//Our table layout size has changed which means our display size has changed.
  2076 			//We need to re-create our bitmap.
  2077 			iCreateBitmap = true;
  2078 		}
  2079 
  2080 		/// <summary>
  2081 		/// 
  2082 		/// </summary>
  2083 		/// <param name="sender"></param>
  2084 		/// <param name="e"></param>
  2085 		private void buttonSelectFile_Click(object sender, EventArgs e)
  2086 		{
  2087 			//openFileDialog1.InitialDirectory = "c:\\";
  2088 			//openFileDialog.Filter = "EXE files (*.exe)|*.exe|All files (*.*)|*.*";
  2089 			//openFileDialog.FilterIndex = 1;
  2090 			openFileDialog.RestoreDirectory = true;
  2091 
  2092 			if (DlgBox.ShowDialog(openFileDialog) == DialogResult.OK)
  2093 			{
  2094 				labelStartFileName.Text = openFileDialog.FileName;
  2095 				Properties.Settings.Default.StartFileName = openFileDialog.FileName;
  2096 				Properties.Settings.Default.Save();
  2097 			}
  2098 		}
  2099     }
  2100 }