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