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