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