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