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