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