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