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