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