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