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