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