Server/MainForm.cs
author StephaneLenclud
Sat, 07 Feb 2015 13:51:30 +0100
changeset 109 ff1ed5f07d7e
parent 108 7dd1d881c142
child 110 31e63bd07dfa
permissions -rw-r--r--
MiniDisplay: Improved icon APIs.
     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(1);
   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         private void buttonAddRow_Click(object sender, EventArgs e)
  1287         {
  1288             if (tableLayoutPanel.RowCount < 6)
  1289             {
  1290                 UpdateTableLayoutPanel(tableLayoutPanel.ColumnCount, tableLayoutPanel.RowCount + 1);
  1291             }
  1292         }
  1293 
  1294         private void buttonRemoveRow_Click(object sender, EventArgs e)
  1295         {
  1296             if (tableLayoutPanel.RowCount > 1)
  1297             {
  1298                 UpdateTableLayoutPanel(tableLayoutPanel.ColumnCount, tableLayoutPanel.RowCount - 1);
  1299             }
  1300 
  1301             UpdateTableLayoutRowStyles();
  1302         }
  1303 
  1304         private void buttonAddColumn_Click(object sender, EventArgs e)
  1305         {
  1306             if (tableLayoutPanel.ColumnCount < 8)
  1307             {
  1308                 UpdateTableLayoutPanel(tableLayoutPanel.ColumnCount + 1, tableLayoutPanel.RowCount);
  1309             }
  1310         }
  1311 
  1312         private void buttonRemoveColumn_Click(object sender, EventArgs e)
  1313         {
  1314             if (tableLayoutPanel.ColumnCount > 1)
  1315             {
  1316                 UpdateTableLayoutPanel(tableLayoutPanel.ColumnCount - 1, tableLayoutPanel.RowCount);
  1317             }
  1318         }
  1319 
  1320 
  1321         /// <summary>
  1322         /// Update our table layout row styles to make sure each rows have similar height
  1323         /// </summary>
  1324         private void UpdateTableLayoutRowStyles()
  1325         {
  1326             foreach (RowStyle rowStyle in tableLayoutPanel.RowStyles)
  1327             {
  1328                 rowStyle.SizeType = SizeType.Percent;
  1329                 rowStyle.Height = 100 / tableLayoutPanel.RowCount;
  1330             }
  1331         }
  1332 
  1333         /// DEPRECATED
  1334         /// <summary>
  1335         /// Empty and recreate our table layout with the given number of columns and rows.
  1336         /// Sizes of rows and columns are uniform.
  1337         /// </summary>
  1338         /// <param name="aColumn"></param>
  1339         /// <param name="aRow"></param>
  1340         private void UpdateTableLayoutPanel(int aColumn, int aRow)
  1341         {
  1342             tableLayoutPanel.Controls.Clear();
  1343             tableLayoutPanel.RowStyles.Clear();
  1344             tableLayoutPanel.ColumnStyles.Clear();
  1345             tableLayoutPanel.RowCount = 0;
  1346             tableLayoutPanel.ColumnCount = 0;
  1347 
  1348             while (tableLayoutPanel.RowCount < aRow)
  1349             {
  1350                 tableLayoutPanel.RowCount++;
  1351             }
  1352 
  1353             while (tableLayoutPanel.ColumnCount < aColumn)
  1354             {
  1355                 tableLayoutPanel.ColumnCount++;
  1356             }
  1357 
  1358             for (int i = 0; i < tableLayoutPanel.ColumnCount; i++)
  1359             {
  1360                 //Create our column styles
  1361                 this.tableLayoutPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100 / tableLayoutPanel.ColumnCount));
  1362 
  1363                 for (int j = 0; j < tableLayoutPanel.RowCount; j++)
  1364                 {
  1365                     if (i == 0)
  1366                     {
  1367                         //Create our row styles
  1368                         this.tableLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100 / tableLayoutPanel.RowCount));
  1369                     }
  1370 
  1371                     MarqueeLabel control = new SharpDisplayManager.MarqueeLabel();
  1372                     control.AutoEllipsis = true;
  1373                     control.AutoSize = true;
  1374                     control.BackColor = System.Drawing.Color.Transparent;
  1375                     control.Dock = System.Windows.Forms.DockStyle.Fill;
  1376                     control.Location = new System.Drawing.Point(1, 1);
  1377                     control.Margin = new System.Windows.Forms.Padding(0);
  1378                     control.Name = "marqueeLabelCol" + aColumn + "Row" + aRow;
  1379                     control.OwnTimer = false;
  1380                     control.PixelsPerSecond = 64;
  1381                     control.Separator = cds.Separator;
  1382                     control.MinFontSize = cds.MinFontSize;
  1383                     control.ScaleToFit = cds.ScaleToFit;
  1384                     //control.Size = new System.Drawing.Size(254, 30);
  1385                     //control.TabIndex = 2;
  1386                     control.Font = cds.Font;
  1387                     control.Text = "ABCDEFGHIJKLMNOPQRST-0123456789";
  1388                     control.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
  1389                     control.UseCompatibleTextRendering = true;
  1390                     //
  1391                     tableLayoutPanel.Controls.Add(control, i, j);
  1392                 }
  1393             }
  1394 
  1395             CheckFontHeight();
  1396         }
  1397 
  1398 
  1399         /// <summary>
  1400         /// Update our display table layout.
  1401         /// </summary>
  1402         /// <param name="aLayout"></param>
  1403         private void UpdateTableLayoutPanel(ClientData aClient)
  1404         {
  1405 			if (aClient == null)
  1406 			{
  1407 				//Just drop it
  1408 				return;
  1409 			}
  1410 
  1411 
  1412             TableLayout layout = aClient.Layout;
  1413             int fieldCount = 0;
  1414 
  1415             tableLayoutPanel.Controls.Clear();
  1416             tableLayoutPanel.RowStyles.Clear();
  1417             tableLayoutPanel.ColumnStyles.Clear();
  1418             tableLayoutPanel.RowCount = 0;
  1419             tableLayoutPanel.ColumnCount = 0;
  1420 
  1421             while (tableLayoutPanel.RowCount < layout.Rows.Count)
  1422             {
  1423                 tableLayoutPanel.RowCount++;
  1424             }
  1425 
  1426             while (tableLayoutPanel.ColumnCount < layout.Columns.Count)
  1427             {
  1428                 tableLayoutPanel.ColumnCount++;
  1429             }
  1430 
  1431             for (int i = 0; i < tableLayoutPanel.ColumnCount; i++)
  1432             {
  1433                 //Create our column styles
  1434                 this.tableLayoutPanel.ColumnStyles.Add(layout.Columns[i]);
  1435 
  1436                 for (int j = 0; j < tableLayoutPanel.RowCount; j++)
  1437                 {
  1438                     if (i == 0)
  1439                     {
  1440                         //Create our row styles
  1441                         this.tableLayoutPanel.RowStyles.Add(layout.Rows[j]);
  1442                     }
  1443 
  1444                     //Check if we already have a control
  1445                     Control existingControl = tableLayoutPanel.GetControlFromPosition(i,j);
  1446                     if (existingControl!=null)
  1447                     {
  1448                         //We already have a control in that cell as a results of row/col spanning
  1449                         //Move on to next cell then
  1450                         continue;
  1451                     }
  1452 
  1453                     fieldCount++;
  1454 
  1455                     //Check if a client field already exists for that cell
  1456                     if (aClient.Fields.Count <= tableLayoutPanel.Controls.Count)
  1457                     {
  1458                         //No client field specified, create a text field by default
  1459                         aClient.Fields.Add(new DataField(aClient.Fields.Count));
  1460                     }
  1461 
  1462                     //Create a control corresponding to the field specified for that cell
  1463                     DataField field = aClient.Fields[tableLayoutPanel.Controls.Count];
  1464                     Control control = CreateControlForDataField(field);
  1465 
  1466                     //Add newly created control to our table layout at the specified row and column
  1467                     tableLayoutPanel.Controls.Add(control, i, j);
  1468                     //Make sure we specify row and column span for that new control
  1469                     tableLayoutPanel.SetRowSpan(control,field.RowSpan);
  1470                     tableLayoutPanel.SetColumnSpan(control, field.ColumnSpan);
  1471                 }
  1472             }
  1473 
  1474             //
  1475             while (aClient.Fields.Count > fieldCount)
  1476             {
  1477                 //We have too much fields for this layout
  1478                 //Just discard them until we get there
  1479                 aClient.Fields.RemoveAt(aClient.Fields.Count-1);
  1480             }
  1481 
  1482             CheckFontHeight();
  1483         }
  1484 
  1485         /// <summary>
  1486         /// Check our type of data field and create corresponding control
  1487         /// </summary>
  1488         /// <param name="aField"></param>
  1489         private Control CreateControlForDataField(DataField aField)
  1490         {
  1491             Control control=null;
  1492             if (!aField.IsBitmap)
  1493             {
  1494                 MarqueeLabel label = new SharpDisplayManager.MarqueeLabel();
  1495                 label.AutoEllipsis = true;
  1496                 label.AutoSize = true;
  1497                 label.BackColor = System.Drawing.Color.Transparent;
  1498                 label.Dock = System.Windows.Forms.DockStyle.Fill;
  1499                 label.Location = new System.Drawing.Point(1, 1);
  1500                 label.Margin = new System.Windows.Forms.Padding(0);
  1501                 label.Name = "marqueeLabel" + aField.Index;
  1502                 label.OwnTimer = false;
  1503                 label.PixelsPerSecond = cds.ScrollingSpeedInPixelsPerSecond;
  1504                 label.Separator = cds.Separator;
  1505                 label.MinFontSize = cds.MinFontSize;
  1506                 label.ScaleToFit = cds.ScaleToFit;
  1507                 //control.Size = new System.Drawing.Size(254, 30);
  1508                 //control.TabIndex = 2;
  1509                 label.Font = cds.Font;
  1510 
  1511 				label.TextAlign = aField.Alignment;
  1512                 label.UseCompatibleTextRendering = true;
  1513                 label.Text = aField.Text;
  1514                 //
  1515                 control = label;
  1516             }
  1517             else
  1518             {
  1519                 //Create picture box
  1520                 PictureBox picture = new PictureBox();
  1521                 picture.AutoSize = true;
  1522                 picture.BackColor = System.Drawing.Color.Transparent;
  1523                 picture.Dock = System.Windows.Forms.DockStyle.Fill;
  1524                 picture.Location = new System.Drawing.Point(1, 1);
  1525                 picture.Margin = new System.Windows.Forms.Padding(0);
  1526                 picture.Name = "pictureBox" + aField;
  1527                 //Set our image
  1528                 picture.Image = aField.Bitmap;
  1529                 //
  1530                 control = picture;
  1531             }
  1532 
  1533             return control;
  1534         }
  1535 
  1536 
  1537         private void buttonAlignLeft_Click(object sender, EventArgs e)
  1538         {
  1539             foreach (MarqueeLabel ctrl in tableLayoutPanel.Controls)
  1540             {
  1541                 ctrl.TextAlign = ContentAlignment.MiddleLeft;
  1542             }
  1543         }
  1544 
  1545         private void buttonAlignCenter_Click(object sender, EventArgs e)
  1546         {
  1547             foreach (MarqueeLabel ctrl in tableLayoutPanel.Controls)
  1548             {
  1549                 ctrl.TextAlign = ContentAlignment.MiddleCenter;
  1550             }
  1551         }
  1552 
  1553         private void buttonAlignRight_Click(object sender, EventArgs e)
  1554         {
  1555             foreach (MarqueeLabel ctrl in tableLayoutPanel.Controls)
  1556             {
  1557                 ctrl.TextAlign = ContentAlignment.MiddleRight;
  1558             }
  1559         }
  1560 
  1561 		/// <summary>
  1562 		/// Called when the user selected a new display type.
  1563 		/// </summary>
  1564 		/// <param name="sender"></param>
  1565 		/// <param name="e"></param>
  1566         private void comboBoxDisplayType_SelectedIndexChanged(object sender, EventArgs e)
  1567         {
  1568 			//Store the selected display type in our settings
  1569             Properties.Settings.Default.CurrentDisplayIndex = comboBoxDisplayType.SelectedIndex;
  1570             cds.DisplayType = comboBoxDisplayType.SelectedIndex;
  1571             Properties.Settings.Default.Save();
  1572 
  1573 			//Try re-opening the display connection if we were already connected.
  1574 			//Otherwise just update our status to reflect display type change.
  1575             if (iDisplay.IsOpen())
  1576             {
  1577                 OpenDisplayConnection();
  1578             }
  1579             else
  1580             {
  1581                 UpdateStatus();
  1582             }
  1583         }
  1584 
  1585         private void maskedTextBoxTimerInterval_TextChanged(object sender, EventArgs e)
  1586         {
  1587             if (maskedTextBoxTimerInterval.Text != "")
  1588             {
  1589                 int interval = Convert.ToInt32(maskedTextBoxTimerInterval.Text);
  1590 
  1591                 if (interval > 0)
  1592                 {
  1593                     timer.Interval = interval;
  1594                     cds.TimerInterval = timer.Interval;
  1595                     Properties.Settings.Default.Save();
  1596                 }
  1597             }
  1598         }
  1599 
  1600         private void maskedTextBoxMinFontSize_TextChanged(object sender, EventArgs e)
  1601         {
  1602             if (maskedTextBoxMinFontSize.Text != "")
  1603             {
  1604                 int minFontSize = Convert.ToInt32(maskedTextBoxMinFontSize.Text);
  1605 
  1606                 if (minFontSize > 0)
  1607                 {
  1608                     cds.MinFontSize = minFontSize;
  1609                     Properties.Settings.Default.Save();
  1610 					//We need to recreate our layout for that change to take effect
  1611 					UpdateTableLayoutPanel(iCurrentClientData);
  1612                 }
  1613             }
  1614         }
  1615 
  1616 
  1617 		private void maskedTextBoxScrollingSpeed_TextChanged(object sender, EventArgs e)
  1618 		{
  1619 			if (maskedTextBoxScrollingSpeed.Text != "")
  1620 			{
  1621 				int scrollingSpeed = Convert.ToInt32(maskedTextBoxScrollingSpeed.Text);
  1622 
  1623 				if (scrollingSpeed > 0)
  1624 				{
  1625 					cds.ScrollingSpeedInPixelsPerSecond = scrollingSpeed;
  1626 					Properties.Settings.Default.Save();
  1627 					//We need to recreate our layout for that change to take effect
  1628 					UpdateTableLayoutPanel(iCurrentClientData);
  1629 				}
  1630 			}
  1631 		}
  1632 
  1633         private void textBoxScrollLoopSeparator_TextChanged(object sender, EventArgs e)
  1634         {
  1635             //TODO: re-create layout? update our fields?
  1636             cds.Separator = textBoxScrollLoopSeparator.Text;
  1637             Properties.Settings.Default.Save();
  1638         }
  1639 
  1640         private void buttonPowerOn_Click(object sender, EventArgs e)
  1641         {
  1642             iDisplay.PowerOn();
  1643         }
  1644 
  1645         private void buttonPowerOff_Click(object sender, EventArgs e)
  1646         {
  1647             iDisplay.PowerOff();
  1648         }
  1649 
  1650         private void buttonShowClock_Click(object sender, EventArgs e)
  1651         {
  1652             iDisplay.ShowClock();
  1653         }
  1654 
  1655         private void buttonHideClock_Click(object sender, EventArgs e)
  1656         {
  1657             iDisplay.HideClock();
  1658         }
  1659 
  1660         private void buttonUpdate_Click(object sender, EventArgs e)
  1661         {
  1662             InstallUpdateSyncWithInfo();
  1663         }
  1664 
  1665 
  1666         private void InstallUpdateSyncWithInfo()
  1667         {
  1668             UpdateCheckInfo info = null;
  1669 
  1670             if (ApplicationDeployment.IsNetworkDeployed)
  1671             {
  1672                 ApplicationDeployment ad = ApplicationDeployment.CurrentDeployment;
  1673 
  1674                 try
  1675                 {
  1676                     info = ad.CheckForDetailedUpdate();
  1677 
  1678                 }
  1679                 catch (DeploymentDownloadException dde)
  1680                 {
  1681                     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);
  1682                     return;
  1683                 }
  1684                 catch (InvalidDeploymentException ide)
  1685                 {
  1686                     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);
  1687                     return;
  1688                 }
  1689                 catch (InvalidOperationException ioe)
  1690                 {
  1691                     MessageBox.Show("This application cannot be updated. It is likely not a ClickOnce application. Error: " + ioe.Message);
  1692                     return;
  1693                 }
  1694 
  1695 				if (info.UpdateAvailable)
  1696 				{
  1697 					Boolean doUpdate = true;
  1698 
  1699 					if (!info.IsUpdateRequired)
  1700 					{
  1701 						DialogResult dr = MessageBox.Show("An update is available. Would you like to update the application now?", "Update Available", MessageBoxButtons.OKCancel);
  1702 						if (!(DialogResult.OK == dr))
  1703 						{
  1704 							doUpdate = false;
  1705 						}
  1706 					}
  1707 					else
  1708 					{
  1709 						// Display a message that the app MUST reboot. Display the minimum required version.
  1710 						MessageBox.Show("This application has detected a mandatory update from your current " +
  1711 							"version to version " + info.MinimumRequiredVersion.ToString() +
  1712 							". The application will now install the update and restart.",
  1713 							"Update Available", MessageBoxButtons.OK,
  1714 							MessageBoxIcon.Information);
  1715 					}
  1716 
  1717 					if (doUpdate)
  1718 					{
  1719 						try
  1720 						{
  1721 							ad.Update();
  1722 							MessageBox.Show("The application has been upgraded, and will now restart.");
  1723 							Application.Restart();
  1724 						}
  1725 						catch (DeploymentDownloadException dde)
  1726 						{
  1727 							MessageBox.Show("Cannot install the latest version of the application. \n\nPlease check your network connection, or try again later. Error: " + dde);
  1728 							return;
  1729 						}
  1730 					}
  1731 				}
  1732 				else
  1733 				{
  1734 					MessageBox.Show("You are already running the latest version.", "Application up-to-date");
  1735 				}
  1736             }
  1737         }
  1738 
  1739 
  1740 		/// <summary>
  1741 		/// Used to
  1742 		/// </summary>
  1743 		private void SysTrayHideShow()
  1744 		{
  1745 			Visible = !Visible;
  1746 			if (Visible)
  1747 			{
  1748 				Activate();
  1749 				WindowState = FormWindowState.Normal;
  1750 			}
  1751 		}
  1752 
  1753 		/// <summary>
  1754 		/// Use to handle minimize events.
  1755 		/// </summary>
  1756 		/// <param name="sender"></param>
  1757 		/// <param name="e"></param>
  1758 		private void MainForm_SizeChanged(object sender, EventArgs e)
  1759 		{
  1760 			if (WindowState == FormWindowState.Minimized && Properties.Settings.Default.MinimizeToTray)
  1761 			{
  1762 				if (Visible)
  1763 				{
  1764 					SysTrayHideShow();
  1765 				}
  1766 			}
  1767 
  1768 		}
  1769 
  1770 		/// <summary>
  1771 		/// 
  1772 		/// </summary>
  1773 		/// <param name="sender"></param>
  1774 		/// <param name="e"></param>
  1775 		private void tableLayoutPanel_SizeChanged(object sender, EventArgs e)
  1776 		{
  1777 			//Our table layout size has changed which means our display size has changed.
  1778 			//We need to re-create our bitmap.
  1779 			iCreateBitmap = true;
  1780 		}
  1781 
  1782     }
  1783 
  1784     /// <summary>
  1785     /// A UI thread copy of a client relevant data.
  1786     /// Keeping this copy in the UI thread helps us deal with threading issues.
  1787     /// </summary>
  1788     public class ClientData
  1789     {
  1790         public ClientData(string aSessionId, ICallback aCallback)
  1791         {
  1792             SessionId = aSessionId;
  1793             Name = "";
  1794             Fields = new List<DataField>();
  1795             Layout = new TableLayout(1, 2); //Default to one column and two rows
  1796             Callback = aCallback;
  1797         }
  1798 
  1799         public string SessionId { get; set; }
  1800         public string Name { get; set; }
  1801         public List<DataField> Fields { get; set; }
  1802         public TableLayout Layout { get; set; }
  1803         public ICallback Callback { get; set; }
  1804     }
  1805 }