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