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