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