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