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