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