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