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