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