Server/MainForm.cs
author sl
Mon, 01 Sep 2014 19:23:36 +0200
changeset 54 fdda7642776a
parent 53 f7ad2dce46a9
child 55 b5ed2e29be23
permissions -rw-r--r--
Now displaying font width and height.
     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 //
    17 using SharpDisplayInterface;
    18 using SharpDisplayClient;
    19 
    20 
    21 namespace SharpDisplayManager
    22 {
    23     public partial class MainForm : Form
    24     {
    25         DateTime LastTickTime;
    26         Display iDisplay;
    27         System.Drawing.Bitmap iBmp;
    28         bool iCreateBitmap; //Workaround render to bitmap issues when minimized
    29         ServiceHost iServiceHost;
    30         /// <summary>
    31         /// Our collection of clients
    32         /// </summary>
    33         public Dictionary<string, ClientData> iClients;
    34         public bool iClosing;
    35 
    36         public MainForm()
    37         {
    38             LastTickTime = DateTime.Now;
    39             iDisplay = new Display();
    40             iClients = new Dictionary<string, ClientData>();
    41 
    42             InitializeComponent();
    43             UpdateStatus();
    44             //We have a bug when drawing minimized and reusing our bitmap
    45             iBmp = new System.Drawing.Bitmap(tableLayoutPanel.Width, tableLayoutPanel.Height, PixelFormat.Format32bppArgb);
    46             iCreateBitmap = false;
    47         }
    48 
    49         private void MainForm_Load(object sender, EventArgs e)
    50         {
    51             StartServer();
    52 
    53             if (Properties.Settings.Default.DisplayConnectOnStartup)
    54             {
    55                 OpenDisplayConnection();
    56             }
    57         }
    58 
    59 
    60         private void buttonFont_Click(object sender, EventArgs e)
    61         {
    62             //fontDialog.ShowColor = true;
    63             //fontDialog.ShowApply = true;
    64             fontDialog.ShowEffects = true;
    65             fontDialog.Font = marqueeLabelTop.Font;
    66 
    67             fontDialog.FixedPitchOnly = checkBoxFixedPitchFontOnly.Checked;
    68 
    69             //fontDialog.ShowHelp = true;
    70 
    71             //fontDlg.MaxSize = 40;
    72             //fontDlg.MinSize = 22;
    73 
    74             //fontDialog.Parent = this;
    75             //fontDialog.StartPosition = FormStartPosition.CenterParent;
    76 
    77             //DlgBox.ShowDialog(fontDialog);
    78 
    79             //if (fontDialog.ShowDialog(this) != DialogResult.Cancel)
    80             if (DlgBox.ShowDialog(fontDialog) != DialogResult.Cancel)
    81             {
    82 
    83                 //MsgBox.Show("MessageBox MsgBox", "MsgBox caption");
    84 
    85                 //MessageBox.Show("Ok");
    86                 marqueeLabelTop.Font = fontDialog.Font;
    87                 marqueeLabelBottom.Font = fontDialog.Font;
    88                 cds.Font = fontDialog.Font;
    89                 Properties.Settings.Default.Save();
    90                 //
    91                 CheckFontHeight();
    92             }
    93         }
    94 
    95         /// <summary>
    96         ///
    97         /// </summary>
    98         void CheckFontHeight()
    99         {
   100             //Show font height and width
   101             labelFontHeight.Text = "Font height: " + cds.Font.Height;
   102             float charWidth = IsFixedWidth(cds.Font);
   103             if (charWidth == 0.0f)
   104             {
   105                 labelFontWidth.Visible = false;
   106             }
   107             else
   108             {
   109                 labelFontWidth.Visible = true;
   110                 labelFontWidth.Text = "Font width: " + charWidth;
   111             }
   112 
   113             //Now check font height and show a warning if needed.
   114             if (marqueeLabelBottom.Font.Height > marqueeLabelBottom.Height)
   115             {
   116                 labelWarning.Text = "WARNING: Selected font is too height by " + (marqueeLabelBottom.Font.Height - marqueeLabelBottom.Height) + " pixels!";
   117                 labelWarning.Visible = true;
   118             }
   119             else
   120             {
   121                 labelWarning.Visible = false;
   122             }
   123 
   124         }
   125 
   126         private void buttonCapture_Click(object sender, EventArgs e)
   127         {
   128             System.Drawing.Bitmap bmp = new System.Drawing.Bitmap(tableLayoutPanel.Width, tableLayoutPanel.Height);
   129             tableLayoutPanel.DrawToBitmap(bmp, tableLayoutPanel.ClientRectangle);
   130             //Bitmap bmpToSave = new Bitmap(bmp);
   131             bmp.Save("D:\\capture.png");
   132 
   133             marqueeLabelTop.Text = "Sweet";
   134 
   135             /*
   136             string outputFileName = "d:\\capture.png";
   137             using (MemoryStream memory = new MemoryStream())
   138             {
   139                 using (FileStream fs = new FileStream(outputFileName, FileMode.OpenOrCreate, FileAccess.ReadWrite))
   140                 {
   141                     bmp.Save(memory, System.Drawing.Imaging.ImageFormat.Png);
   142                     byte[] bytes = memory.ToArray();
   143                     fs.Write(bytes, 0, bytes.Length);
   144                 }
   145             }
   146              */
   147 
   148         }
   149 
   150         private void CheckForRequestResults()
   151         {
   152             if (iDisplay.IsRequestPending())
   153             {
   154                 switch (iDisplay.AttemptRequestCompletion())
   155                 {
   156                     case Display.TMiniDisplayRequest.EMiniDisplayRequestFirmwareRevision:
   157                         toolStripStatusLabelConnect.Text += " v" + iDisplay.FirmwareRevision();
   158                         //Issue next request then
   159                         iDisplay.RequestPowerSupplyStatus();
   160                         break;
   161 
   162                     case Display.TMiniDisplayRequest.EMiniDisplayRequestPowerSupplyStatus:
   163                         if (iDisplay.PowerSupplyStatus())
   164                         {
   165                             toolStripStatusLabelPower.Text = "ON";
   166                         }
   167                         else
   168                         {
   169                             toolStripStatusLabelPower.Text = "OFF";
   170                         }
   171                         //Issue next request then
   172                         iDisplay.RequestDeviceId();
   173                         break;
   174 
   175                     case Display.TMiniDisplayRequest.EMiniDisplayRequestDeviceId:
   176                         toolStripStatusLabelConnect.Text += " - " + iDisplay.DeviceId();
   177                         //No more request to issue
   178                         break;
   179                 }
   180             }
   181         }
   182 
   183 
   184         public delegate int CoordinateTranslationDelegate(System.Drawing.Bitmap aBmp, int aInt);
   185 
   186 
   187         public static int ScreenReversedX(System.Drawing.Bitmap aBmp, int aX)
   188         {
   189             return aBmp.Width - aX - 1;
   190         }
   191 
   192         public int ScreenReversedY(System.Drawing.Bitmap aBmp, int aY)
   193         {
   194             return iBmp.Height - aY - 1;
   195         }
   196 
   197         public int ScreenX(System.Drawing.Bitmap aBmp, int aX)
   198         {
   199             return aX;
   200         }
   201 
   202         public int ScreenY(System.Drawing.Bitmap aBmp, int aY)
   203         {
   204             return aY;
   205         }
   206 
   207 
   208         //This is our timer tick responsible to perform our render
   209         private void timer_Tick(object sender, EventArgs e)
   210         {
   211             //Update our animations
   212             DateTime NewTickTime = DateTime.Now;
   213 
   214             marqueeLabelTop.UpdateAnimation(LastTickTime, NewTickTime);
   215             marqueeLabelBottom.UpdateAnimation(LastTickTime, NewTickTime);
   216 
   217             //Update our display
   218             if (iDisplay.IsOpen())
   219             {
   220                 CheckForRequestResults();
   221 
   222                 //Draw to bitmap
   223                 if (iCreateBitmap)
   224                 {
   225                     iBmp = new System.Drawing.Bitmap(tableLayoutPanel.Width, tableLayoutPanel.Height, PixelFormat.Format32bppArgb);
   226                 }
   227                 tableLayoutPanel.DrawToBitmap(iBmp, tableLayoutPanel.ClientRectangle);
   228                 //iBmp.Save("D:\\capture.png");
   229 
   230                 //Select proper coordinate translation functions
   231                 //We used delegate/function pointer to support reverse screen without doing an extra test on each pixels
   232                 CoordinateTranslationDelegate screenX;
   233                 CoordinateTranslationDelegate screenY;
   234 
   235                 if (cds.ReverseScreen)
   236                 {
   237                     screenX = ScreenReversedX;
   238                     screenY = ScreenReversedY;
   239                 }
   240                 else
   241                 {
   242                     screenX = ScreenX;
   243                     screenY = ScreenY;
   244                 }
   245 
   246                 //Send it to our display
   247                 for (int i = 0; i < iBmp.Width; i++)
   248                 {
   249                     for (int j = 0; j < iBmp.Height; j++)
   250                     {
   251                         unchecked
   252                         {
   253                             uint color = (uint)iBmp.GetPixel(i, j).ToArgb();
   254                             //For some reason when the app is minimized in the task bar only the alpha of our color is set.
   255                             //Thus that strange test for rendering to work both when the app is in the task bar and when it isn't.
   256                             iDisplay.SetPixel(screenX(iBmp, i), screenY(iBmp, j), Convert.ToInt32(!(color != 0xFF000000)));
   257                         }
   258                     }
   259                 }
   260 
   261                 iDisplay.SwapBuffers();
   262 
   263             }
   264 
   265             //Compute instant FPS
   266             toolStripStatusLabelFps.Text = (1.0/NewTickTime.Subtract(LastTickTime).TotalSeconds).ToString("F0") + " / " + (1000/timer.Interval).ToString() + " FPS";
   267 
   268             LastTickTime = NewTickTime;
   269 
   270         }
   271 
   272         private void OpenDisplayConnection()
   273         {
   274             CloseDisplayConnection();
   275 
   276             if (iDisplay.Open((Display.TMiniDisplayType)cds.DisplayType))
   277             {
   278                 UpdateStatus();
   279                 iDisplay.RequestFirmwareRevision();
   280             }
   281             else
   282             {
   283                 UpdateStatus();
   284                 toolStripStatusLabelConnect.Text = "Connection error";
   285             }
   286         }
   287 
   288         private void CloseDisplayConnection()
   289         {
   290             iDisplay.Close();
   291             UpdateStatus();
   292         }
   293 
   294         private void buttonOpen_Click(object sender, EventArgs e)
   295         {
   296             OpenDisplayConnection();
   297         }
   298 
   299         private void buttonClose_Click(object sender, EventArgs e)
   300         {
   301             CloseDisplayConnection();
   302         }
   303 
   304         private void buttonClear_Click(object sender, EventArgs e)
   305         {
   306             iDisplay.Clear();
   307             iDisplay.SwapBuffers();
   308         }
   309 
   310         private void buttonFill_Click(object sender, EventArgs e)
   311         {
   312             iDisplay.Fill();
   313             iDisplay.SwapBuffers();
   314         }
   315 
   316         private void trackBarBrightness_Scroll(object sender, EventArgs e)
   317         {
   318             cds.Brightness = trackBarBrightness.Value;
   319             Properties.Settings.Default.Save();
   320             iDisplay.SetBrightness(trackBarBrightness.Value);
   321 
   322         }
   323 
   324 
   325         /// <summary>
   326         /// CDS stands for Current Display Settings
   327         /// </summary>
   328         private DisplaySettings cds
   329         {
   330             get
   331             {
   332                 DisplaysSettings settings = Properties.Settings.Default.DisplaySettings;
   333                 if (settings == null)
   334                 {
   335                     settings = new DisplaysSettings();
   336                     settings.Init();
   337                     Properties.Settings.Default.DisplaySettings = settings;
   338                 }
   339 
   340                 //Make sure all our settings have been created
   341                 while (settings.Displays.Count <= Properties.Settings.Default.CurrentDisplayIndex)
   342                 {
   343                     settings.Displays.Add(new DisplaySettings());
   344                 }
   345 
   346                 DisplaySettings displaySettings = settings.Displays[Properties.Settings.Default.CurrentDisplayIndex];
   347                 return displaySettings;
   348             }
   349         }
   350 
   351         /// <summary>
   352         /// Check if the given font has a fixed character pitch.
   353         /// </summary>
   354         /// <param name="ft"></param>
   355         /// <returns>0.0f if this is not a monospace font, otherwise returns the character width.</returns>
   356         public float IsFixedWidth(Font ft)
   357         {
   358             Graphics g = CreateGraphics();
   359             char[] charSizes = new char[] { 'i', 'a', 'Z', '%', '#', 'a', 'B', 'l', 'm', ',', '.' };
   360             float charWidth = g.MeasureString("I", ft, Int32.MaxValue, StringFormat.GenericTypographic).Width;
   361 
   362             bool fixedWidth = true;
   363 
   364             foreach (char c in charSizes)
   365                 if (g.MeasureString(c.ToString(), ft, Int32.MaxValue, StringFormat.GenericTypographic).Width != charWidth)
   366                     fixedWidth = false;
   367 
   368             if (fixedWidth)
   369             {
   370                 return charWidth;
   371             }
   372 
   373             return 0.0f;
   374         }
   375 
   376         private void UpdateStatus()
   377         {
   378             //Synchronize UI with settings
   379             //Load settings
   380 
   381             checkBoxShowBorders.Checked = cds.ShowBorders;
   382             tableLayoutPanel.CellBorderStyle = (cds.ShowBorders ? TableLayoutPanelCellBorderStyle.Single : TableLayoutPanelCellBorderStyle.None);
   383             marqueeLabelTop.Font = cds.Font;
   384             marqueeLabelBottom.Font = cds.Font;
   385             CheckFontHeight();
   386             checkBoxConnectOnStartup.Checked = Properties.Settings.Default.DisplayConnectOnStartup;
   387             checkBoxReverseScreen.Checked = cds.ReverseScreen;
   388             comboBoxDisplayType.SelectedIndex = cds.DisplayType;
   389             timer.Interval = cds.TimerInterval;
   390             maskedTextBoxTimerInterval.Text = cds.TimerInterval.ToString();
   391 
   392 
   393             if (iDisplay.IsOpen())
   394             {
   395                 //Only setup brightness if display is open
   396                 trackBarBrightness.Minimum = iDisplay.MinBrightness();
   397                 trackBarBrightness.Maximum = iDisplay.MaxBrightness();
   398                 trackBarBrightness.Value = cds.Brightness;
   399                 trackBarBrightness.LargeChange = Math.Max(1, (iDisplay.MaxBrightness() - iDisplay.MinBrightness()) / 5);
   400                 trackBarBrightness.SmallChange = 1;
   401                 iDisplay.SetBrightness(cds.Brightness);
   402                 //
   403                 buttonFill.Enabled = true;
   404                 buttonClear.Enabled = true;
   405                 buttonOpen.Enabled = false;
   406                 buttonClose.Enabled = true;
   407                 trackBarBrightness.Enabled = true;
   408                 toolStripStatusLabelConnect.Text = "Connected - " + iDisplay.Vendor() + " - " + iDisplay.Product();
   409                 //+ " - " + iDisplay.SerialNumber();
   410 
   411                 if (iDisplay.SupportPowerOnOff())
   412                 {
   413                     buttonPowerOn.Enabled = true;
   414                     buttonPowerOff.Enabled = true;
   415                 }
   416                 else
   417                 {
   418                     buttonPowerOn.Enabled = false;
   419                     buttonPowerOff.Enabled = false;
   420                 }
   421 
   422                 if (iDisplay.SupportClock())
   423                 {
   424                     buttonShowClock.Enabled = true;
   425                     buttonHideClock.Enabled = true;
   426                 }
   427                 else
   428                 {
   429                     buttonShowClock.Enabled = false;
   430                     buttonHideClock.Enabled = false;
   431                 }
   432             }
   433             else
   434             {
   435                 buttonFill.Enabled = false;
   436                 buttonClear.Enabled = false;
   437                 buttonOpen.Enabled = true;
   438                 buttonClose.Enabled = false;
   439                 trackBarBrightness.Enabled = false;
   440                 buttonPowerOn.Enabled = false;
   441                 buttonPowerOff.Enabled = false;
   442                 buttonShowClock.Enabled = false;
   443                 buttonHideClock.Enabled = false;
   444                 toolStripStatusLabelConnect.Text = "Disconnected";
   445                 toolStripStatusLabelPower.Text = "N/A";
   446             }
   447         }
   448 
   449 
   450 
   451         private void checkBoxShowBorders_CheckedChanged(object sender, EventArgs e)
   452         {
   453             //Save our show borders setting
   454             tableLayoutPanel.CellBorderStyle = (checkBoxShowBorders.Checked ? TableLayoutPanelCellBorderStyle.Single : TableLayoutPanelCellBorderStyle.None);
   455             cds.ShowBorders = checkBoxShowBorders.Checked;
   456             Properties.Settings.Default.Save();
   457         }
   458 
   459         private void checkBoxConnectOnStartup_CheckedChanged(object sender, EventArgs e)
   460         {
   461             //Save our connect on startup setting
   462             Properties.Settings.Default.DisplayConnectOnStartup = checkBoxConnectOnStartup.Checked;
   463             Properties.Settings.Default.Save();
   464         }
   465 
   466         private void checkBoxReverseScreen_CheckedChanged(object sender, EventArgs e)
   467         {
   468             //Save our reverse screen setting
   469             cds.ReverseScreen = checkBoxReverseScreen.Checked;
   470             Properties.Settings.Default.Save();
   471         }
   472 
   473         private void MainForm_Resize(object sender, EventArgs e)
   474         {
   475             if (WindowState == FormWindowState.Minimized)
   476             {
   477                 // Do some stuff
   478                 //iBmp = new System.Drawing.Bitmap(tableLayoutPanel.Width, tableLayoutPanel.Height, PixelFormat.Format32bppArgb);
   479                 iCreateBitmap = true;
   480             }
   481         }
   482 
   483         private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
   484         {
   485             StopServer();
   486             e.Cancel = iClosing;
   487         }
   488 
   489         public void StartServer()
   490         {
   491             iServiceHost = new ServiceHost
   492                 (
   493                     typeof(DisplayServer),
   494                     new Uri[] { new Uri("net.tcp://localhost:8001/") }
   495                 );
   496 
   497             iServiceHost.AddServiceEndpoint(typeof(IDisplayService), new NetTcpBinding(SecurityMode.None,true), "DisplayService");
   498             iServiceHost.Open();
   499         }
   500 
   501         public void StopServer()
   502         {
   503             if (iClients.Count > 0 && !iClosing)
   504             {
   505                 //Tell our clients
   506                 iClosing = true;
   507                 BroadcastCloseEvent();
   508             }
   509             else if (iClosing)
   510             {
   511                 if (MessageBox.Show("Force exit?", "Waiting for clients...", MessageBoxButtons.YesNo, MessageBoxIcon.Warning) == DialogResult.Yes)
   512                 {
   513                     iClosing = false; //We make sure we force close if asked twice
   514                 }
   515             }
   516             else
   517             {
   518                 //We removed that as it often lags for some reason
   519                 //iServiceHost.Close();
   520             }
   521         }
   522 
   523         public void BroadcastCloseEvent()
   524         {
   525             Trace.TraceInformation("BroadcastCloseEvent - start");
   526 
   527             var inactiveClients = new List<string>();
   528             foreach (var client in iClients)
   529             {
   530                 //if (client.Key != eventData.ClientName)
   531                 {
   532                     try
   533                     {
   534                         Trace.TraceInformation("BroadcastCloseEvent - " + client.Key);
   535                         client.Value.Callback.OnCloseOrder(/*eventData*/);
   536                     }
   537                     catch (Exception ex)
   538                     {
   539                         inactiveClients.Add(client.Key);
   540                     }
   541                 }
   542             }
   543 
   544             if (inactiveClients.Count > 0)
   545             {
   546                 foreach (var client in inactiveClients)
   547                 {
   548                     iClients.Remove(client);
   549                     Program.iMainForm.treeViewClients.Nodes.Remove(Program.iMainForm.treeViewClients.Nodes.Find(client, false)[0]);
   550                 }
   551             }
   552         }
   553 
   554         private void buttonStartClient_Click(object sender, EventArgs e)
   555         {
   556             Thread clientThread = new Thread(SharpDisplayClient.Program.Main);
   557             clientThread.Start();
   558             BringToFront();
   559         }
   560 
   561         private void buttonSuspend_Click(object sender, EventArgs e)
   562         {
   563             LastTickTime = DateTime.Now; //Reset timer to prevent jump
   564             timer.Enabled = !timer.Enabled;
   565             if (!timer.Enabled)
   566             {
   567                 buttonSuspend.Text = "Run";
   568             }
   569             else
   570             {
   571                 buttonSuspend.Text = "Pause";
   572             }
   573         }
   574 
   575         private void buttonCloseClients_Click(object sender, EventArgs e)
   576         {
   577             BroadcastCloseEvent();
   578         }
   579 
   580         private void treeViewClients_AfterSelect(object sender, TreeViewEventArgs e)
   581         {
   582 
   583         }
   584 
   585         //Delegates are used for our thread safe method
   586         public delegate void AddClientDelegate(string aSessionId, IDisplayServiceCallback aCallback);
   587         public delegate void RemoveClientDelegate(string aSessionId);
   588         public delegate void SetTextDelegate(string SessionId, TextField aTextField);
   589         public delegate void SetTextsDelegate(string SessionId, System.Collections.Generic.IList<TextField> aTextFields);
   590         public delegate void SetClientNameDelegate(string aSessionId, string aName);
   591 
   592 
   593         /// <summary>
   594         ///
   595         /// </summary>
   596         /// <param name="aSessionId"></param>
   597         /// <param name="aCallback"></param>
   598         public void AddClientThreadSafe(string aSessionId, IDisplayServiceCallback aCallback)
   599         {
   600             if (this.InvokeRequired)
   601             {
   602                 //Not in the proper thread, invoke ourselves
   603                 AddClientDelegate d = new AddClientDelegate(AddClientThreadSafe);
   604                 this.Invoke(d, new object[] { aSessionId, aCallback });
   605             }
   606             else
   607             {
   608                 //We are in the proper thread
   609                 //Add this session to our collection of clients
   610                 ClientData newClient = new ClientData(aSessionId, aCallback);
   611                 Program.iMainForm.iClients.Add(aSessionId, newClient);
   612                 //Add this session to our UI
   613                 UpdateClientTreeViewNode(newClient);
   614             }
   615         }
   616 
   617         /// <summary>
   618         ///
   619         /// </summary>
   620         /// <param name="aSessionId"></param>
   621         public void RemoveClientThreadSafe(string aSessionId)
   622         {
   623             if (this.InvokeRequired)
   624             {
   625                 //Not in the proper thread, invoke ourselves
   626                 RemoveClientDelegate d = new RemoveClientDelegate(RemoveClientThreadSafe);
   627                 this.Invoke(d, new object[] { aSessionId });
   628             }
   629             else
   630             {
   631                 //We are in the proper thread
   632                 //Remove this session from both client collection and UI tree view
   633                 if (Program.iMainForm.iClients.Keys.Contains(aSessionId))
   634                 {
   635                     Program.iMainForm.iClients.Remove(aSessionId);
   636                     Program.iMainForm.treeViewClients.Nodes.Remove(Program.iMainForm.treeViewClients.Nodes.Find(aSessionId, false)[0]);
   637                 }
   638 
   639                 if (iClosing && iClients.Count == 0)
   640                 {
   641                     //We were closing our form
   642                     //All clients are now closed
   643                     //Just resume our close operation
   644                     iClosing = false;
   645                     Close();
   646                 }
   647             }
   648         }
   649 
   650         /// <summary>
   651         ///
   652         /// </summary>
   653         /// <param name="aLineIndex"></param>
   654         /// <param name="aText"></param>
   655         public void SetTextThreadSafe(string aSessionId, TextField aTextField)
   656         {
   657             if (this.InvokeRequired)
   658             {
   659                 //Not in the proper thread, invoke ourselves
   660                 SetTextDelegate d = new SetTextDelegate(SetTextThreadSafe);
   661                 this.Invoke(d, new object[] { aSessionId, aTextField });
   662             }
   663             else
   664             {
   665                 ClientData client = iClients[aSessionId];
   666                 if (client != null)
   667                 {
   668                     //Make sure all our texts are in place
   669                     while (client.Texts.Count < (aTextField.Index + 1))
   670                     {
   671                         //Add a text field with proper index
   672                         client.Texts.Add(new TextField(client.Texts.Count));
   673                     }
   674                     client.Texts[aTextField.Index] = aTextField;
   675 
   676                     //We are in the proper thread
   677                     //Only support two lines for now
   678                     if (aTextField.Index == 0)
   679                     {
   680                         marqueeLabelTop.Text = aTextField.Text;
   681                         marqueeLabelTop.TextAlign = aTextField.Alignment;
   682                     }
   683                     else if (aTextField.Index == 1)
   684                     {
   685                         marqueeLabelBottom.Text = aTextField.Text;
   686                         marqueeLabelBottom.TextAlign = aTextField.Alignment;
   687                     }
   688 
   689 
   690                     UpdateClientTreeViewNode(client);
   691                 }
   692             }
   693         }
   694 
   695         /// <summary>
   696         ///
   697         /// </summary>
   698         /// <param name="aTexts"></param>
   699         public void SetTextsThreadSafe(string aSessionId, System.Collections.Generic.IList<TextField> aTextFields)
   700         {
   701             if (this.InvokeRequired)
   702             {
   703                 //Not in the proper thread, invoke ourselves
   704                 SetTextsDelegate d = new SetTextsDelegate(SetTextsThreadSafe);
   705                 this.Invoke(d, new object[] { aSessionId, aTextFields });
   706             }
   707             else
   708             {
   709                 //We are in the proper thread
   710                 ClientData client = iClients[aSessionId];
   711                 if (client != null)
   712                 {
   713                     //Populate our client with the given text fields
   714                     int j = 0;
   715                     foreach (TextField textField in aTextFields)
   716                     {
   717                         if (client.Texts.Count < (j + 1))
   718                         {
   719                             client.Texts.Add(textField);
   720                         }
   721                         else
   722                         {
   723                             client.Texts[j] = textField;
   724                         }
   725                         j++;
   726                     }
   727                     //Only support two lines for now
   728                     for (int i = 0; i < aTextFields.Count; i++)
   729                     {
   730                         if (aTextFields[i].Index == 0)
   731                         {
   732                             marqueeLabelTop.Text = aTextFields[i].Text;
   733                             marqueeLabelTop.TextAlign = aTextFields[i].Alignment;
   734                         }
   735                         else if (aTextFields[i].Index == 1)
   736                         {
   737                             marqueeLabelBottom.Text = aTextFields[i].Text;
   738                             marqueeLabelBottom.TextAlign = aTextFields[i].Alignment;
   739                         }
   740                     }
   741 
   742 
   743                     UpdateClientTreeViewNode(client);
   744                 }
   745             }
   746         }
   747 
   748 
   749         /// <summary>
   750         ///
   751         /// </summary>
   752         /// <param name="aSessionId"></param>
   753         /// <param name="aName"></param>
   754         public void SetClientNameThreadSafe(string aSessionId, string aName)
   755         {
   756             if (this.InvokeRequired)
   757             {
   758                 //Not in the proper thread, invoke ourselves
   759                 SetClientNameDelegate d = new SetClientNameDelegate(SetClientNameThreadSafe);
   760                 this.Invoke(d, new object[] { aSessionId, aName });
   761             }
   762             else
   763             {
   764                 //We are in the proper thread
   765                 //Get our client
   766                 ClientData client = iClients[aSessionId];
   767                 if (client != null)
   768                 {
   769                     //Set its name
   770                     client.Name = aName;
   771                     //Update our tree-view
   772                     UpdateClientTreeViewNode(client);
   773                 }
   774             }
   775         }
   776 
   777         /// <summary>
   778         ///
   779         /// </summary>
   780         /// <param name="aClient"></param>
   781         private void UpdateClientTreeViewNode(ClientData aClient)
   782         {
   783             if (aClient == null)
   784             {
   785                 return;
   786             }
   787 
   788             TreeNode node = null;
   789             //Check that our client node already exists
   790             //Get our client root node using its key which is our session ID
   791             TreeNode[] nodes = treeViewClients.Nodes.Find(aClient.SessionId, false);
   792             if (nodes.Count()>0)
   793             {
   794                 //We already have a node for that client
   795                 node = nodes[0];
   796                 //Clear children as we are going to recreate them below
   797                 node.Nodes.Clear();
   798             }
   799             else
   800             {
   801                 //Client node does not exists create a new one
   802                 treeViewClients.Nodes.Add(aClient.SessionId, aClient.SessionId);
   803                 node = treeViewClients.Nodes.Find(aClient.SessionId, false)[0];
   804             }
   805 
   806             if (node != null)
   807             {
   808                 //Change its name
   809                 if (aClient.Name != "")
   810                 {
   811                     //We have a name, us it as text for our root node
   812                     node.Text = aClient.Name;
   813                     //Add a child with SessionId
   814                     node.Nodes.Add(new TreeNode(aClient.SessionId));
   815                 }
   816                 else
   817                 {
   818                     //No name, use session ID instead
   819                     node.Text = aClient.SessionId;
   820                 }
   821 
   822                 if (aClient.Texts.Count > 0)
   823                 {
   824                     //Create root node for our texts
   825                     TreeNode textsRoot = new TreeNode("Text");
   826                     node.Nodes.Add(textsRoot);
   827                     //For each text add a new entry
   828                     foreach (TextField field in aClient.Texts)
   829                     {
   830                         textsRoot.Nodes.Add(new TreeNode(field.Text));
   831                     }
   832                 }
   833 
   834                 node.ExpandAll();
   835             }
   836         }
   837 
   838         private void buttonAddRow_Click(object sender, EventArgs e)
   839         {
   840             if (tableLayoutPanel.RowCount < 6)
   841             {
   842                 tableLayoutPanel.RowCount++;
   843                 CheckFontHeight();
   844             }
   845         }
   846 
   847         private void buttonRemoveRow_Click(object sender, EventArgs e)
   848         {
   849             if (tableLayoutPanel.RowCount > 1)
   850             {
   851                 tableLayoutPanel.RowCount--;
   852                 CheckFontHeight();
   853             }
   854         }
   855 
   856         private void buttonAddColumn_Click(object sender, EventArgs e)
   857         {
   858             if (tableLayoutPanel.ColumnCount < 8)
   859             {
   860                 tableLayoutPanel.ColumnCount++;
   861                 //CheckFontHeight();
   862             }
   863         }
   864 
   865         private void buttonRemoveColumn_Click(object sender, EventArgs e)
   866         {
   867             if (tableLayoutPanel.ColumnCount > 1)
   868             {
   869                 tableLayoutPanel.ColumnCount--;
   870                 //CheckFontHeight();
   871             }
   872         }
   873 
   874         private void buttonAlignLeft_Click(object sender, EventArgs e)
   875         {
   876             marqueeLabelTop.TextAlign = ContentAlignment.MiddleLeft;
   877             marqueeLabelBottom.TextAlign = ContentAlignment.MiddleLeft;
   878         }
   879 
   880         private void buttonAlignCenter_Click(object sender, EventArgs e)
   881         {
   882             marqueeLabelTop.TextAlign = ContentAlignment.MiddleCenter;
   883             marqueeLabelBottom.TextAlign = ContentAlignment.MiddleCenter;
   884         }
   885 
   886         private void buttonAlignRight_Click(object sender, EventArgs e)
   887         {
   888             marqueeLabelTop.TextAlign = ContentAlignment.MiddleRight;
   889             marqueeLabelBottom.TextAlign = ContentAlignment.MiddleRight;
   890         }
   891 
   892         private void comboBoxDisplayType_SelectedIndexChanged(object sender, EventArgs e)
   893         {
   894             Properties.Settings.Default.CurrentDisplayIndex = comboBoxDisplayType.SelectedIndex;
   895             cds.DisplayType = comboBoxDisplayType.SelectedIndex;
   896             Properties.Settings.Default.Save();
   897             if (iDisplay.IsOpen())
   898             {
   899                 OpenDisplayConnection();
   900             }
   901             else
   902             {
   903                 UpdateStatus();
   904             }
   905         }
   906 
   907 
   908         private void maskedTextBoxTimerInterval_TextChanged(object sender, EventArgs e)
   909         {
   910             if (maskedTextBoxTimerInterval.Text != "")
   911             {
   912                 int interval = Convert.ToInt32(maskedTextBoxTimerInterval.Text);
   913 
   914                 if (interval > 0)
   915                 {
   916                     timer.Interval = interval;
   917                     cds.TimerInterval = timer.Interval;
   918                     Properties.Settings.Default.Save();
   919                 }
   920             }
   921         }
   922 
   923         private void buttonPowerOn_Click(object sender, EventArgs e)
   924         {
   925             iDisplay.PowerOn();
   926         }
   927 
   928         private void buttonPowerOff_Click(object sender, EventArgs e)
   929         {
   930             iDisplay.PowerOff();
   931         }
   932 
   933         private void buttonShowClock_Click(object sender, EventArgs e)
   934         {
   935             iDisplay.ShowClock();
   936         }
   937 
   938         private void buttonHideClock_Click(object sender, EventArgs e)
   939         {
   940             iDisplay.HideClock();
   941         }
   942 
   943     }
   944 
   945     /// <summary>
   946     /// A UI thread copy of a client relevant data.
   947     /// Keeping this copy in the UI thread helps us deal with threading issues.
   948     /// </summary>
   949     public class ClientData
   950     {
   951         public ClientData(string aSessionId, IDisplayServiceCallback aCallback)
   952         {
   953             SessionId = aSessionId;
   954             Name = "";
   955             Texts = new List<TextField>();
   956             Callback = aCallback;
   957         }
   958 
   959         public string SessionId { get; set; }
   960         public string Name { get; set; }
   961         public List<TextField> Texts { get; set; }
   962         public IDisplayServiceCallback Callback { get; set; }
   963     }
   964 }