Client now recovers from faulty states and timeout.
2 using System.Collections.Generic;
3 using System.ComponentModel;
8 using System.Threading.Tasks;
9 using System.Windows.Forms;
11 using CodeProject.Dialog;
12 using System.Drawing.Imaging;
13 using System.ServiceModel;
14 using System.Threading;
15 using System.Diagnostics;
17 using SharpDisplayClient;
20 namespace SharpDisplayManager
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 SetBitmapDelegate(string SessionId, DataField aField);
30 public delegate void SetLayoutDelegate(string SessionId, TableLayout aLayout);
31 public delegate void SetTextsDelegate(string SessionId, System.Collections.Generic.IList<DataField> aFields);
32 public delegate void SetClientNameDelegate(string aSessionId, string aName);
36 /// Our Display manager main form
38 public partial class MainForm : Form
41 DateTime LastTickTime;
43 System.Drawing.Bitmap iBmp;
44 bool iCreateBitmap; //Workaround render to bitmap issues when minimized
45 ServiceHost iServiceHost;
46 // Our collection of clients sorted by session id.
47 public Dictionary<string, ClientData> iClients;
48 // The name of the client which informations are currently displayed.
49 public string iCurrentClientSessionId;
50 ClientData iCurrentClientData;
53 //Function pointer for pixel color filtering
54 ColorProcessingDelegate iColorFx;
55 //Function pointer for pixel X coordinate intercept
56 CoordinateTranslationDelegate iScreenX;
57 //Function pointer for pixel Y coordinate intercept
58 CoordinateTranslationDelegate iScreenY;
62 iCurrentClientSessionId = "";
63 iCurrentClientData = null;
64 LastTickTime = DateTime.Now;
65 iDisplay = new Display();
66 iClients = new Dictionary<string, ClientData>();
68 InitializeComponent();
70 //We have a bug when drawing minimized and reusing our bitmap
71 iBmp = new System.Drawing.Bitmap(tableLayoutPanel.Width, tableLayoutPanel.Height, PixelFormat.Format32bppArgb);
72 iCreateBitmap = false;
75 private void MainForm_Load(object sender, EventArgs e)
79 if (Properties.Settings.Default.DisplayConnectOnStartup)
81 OpenDisplayConnection();
86 /// Set our current client.
87 /// This will take care of applying our client layout and set data fields.
89 /// <param name="aSessionId"></param>
90 void SetCurrentClient(string aSessionId)
92 if (aSessionId == iCurrentClientSessionId)
94 //Given client is already the current one.
95 //Don't bother changing anything then.
99 //Set current client ID.
100 iCurrentClientSessionId = aSessionId;
101 //Fetch and set current client data.
102 iCurrentClientData = iClients[aSessionId];
103 //Apply layout and set data fields.
104 UpdateTableLayoutPanel(iCurrentClientData);
107 private void buttonFont_Click(object sender, EventArgs e)
109 //fontDialog.ShowColor = true;
110 //fontDialog.ShowApply = true;
111 fontDialog.ShowEffects = true;
112 MarqueeLabel label = (MarqueeLabel)tableLayoutPanel.Controls[0];
113 fontDialog.Font = label.Font;
115 fontDialog.FixedPitchOnly = checkBoxFixedPitchFontOnly.Checked;
117 //fontDialog.ShowHelp = true;
119 //fontDlg.MaxSize = 40;
120 //fontDlg.MinSize = 22;
122 //fontDialog.Parent = this;
123 //fontDialog.StartPosition = FormStartPosition.CenterParent;
125 //DlgBox.ShowDialog(fontDialog);
127 //if (fontDialog.ShowDialog(this) != DialogResult.Cancel)
128 if (DlgBox.ShowDialog(fontDialog) != DialogResult.Cancel)
131 //MsgBox.Show("MessageBox MsgBox", "MsgBox caption");
133 //MessageBox.Show("Ok");
134 foreach (MarqueeLabel ctrl in tableLayoutPanel.Controls)
136 ctrl.Font = fontDialog.Font;
138 cds.Font = fontDialog.Font;
139 Properties.Settings.Default.Save();
148 void CheckFontHeight()
150 //Show font height and width
151 labelFontHeight.Text = "Font height: " + cds.Font.Height;
152 float charWidth = IsFixedWidth(cds.Font);
153 if (charWidth == 0.0f)
155 labelFontWidth.Visible = false;
159 labelFontWidth.Visible = true;
160 labelFontWidth.Text = "Font width: " + charWidth;
163 MarqueeLabel label = null;
164 //Get the first label control we can find
165 foreach (Control ctrl in tableLayoutPanel.Controls)
167 if (ctrl is MarqueeLabel)
169 label = (MarqueeLabel)ctrl;
174 //Now check font height and show a warning if needed.
175 if (label != null && label.Font.Height > label.Height)
177 labelWarning.Text = "WARNING: Selected font is too height by " + (label.Font.Height - label.Height) + " pixels!";
178 labelWarning.Visible = true;
182 labelWarning.Visible = false;
187 private void buttonCapture_Click(object sender, EventArgs e)
189 System.Drawing.Bitmap bmp = new System.Drawing.Bitmap(tableLayoutPanel.Width, tableLayoutPanel.Height);
190 tableLayoutPanel.DrawToBitmap(bmp, tableLayoutPanel.ClientRectangle);
191 //Bitmap bmpToSave = new Bitmap(bmp);
192 bmp.Save("D:\\capture.png");
194 ((MarqueeLabel)tableLayoutPanel.Controls[0]).Text = "Captured";
197 string outputFileName = "d:\\capture.png";
198 using (MemoryStream memory = new MemoryStream())
200 using (FileStream fs = new FileStream(outputFileName, FileMode.OpenOrCreate, FileAccess.ReadWrite))
202 bmp.Save(memory, System.Drawing.Imaging.ImageFormat.Png);
203 byte[] bytes = memory.ToArray();
204 fs.Write(bytes, 0, bytes.Length);
211 private void CheckForRequestResults()
213 if (iDisplay.IsRequestPending())
215 switch (iDisplay.AttemptRequestCompletion())
217 case Display.TMiniDisplayRequest.EMiniDisplayRequestFirmwareRevision:
218 toolStripStatusLabelConnect.Text += " v" + iDisplay.FirmwareRevision();
219 //Issue next request then
220 iDisplay.RequestPowerSupplyStatus();
223 case Display.TMiniDisplayRequest.EMiniDisplayRequestPowerSupplyStatus:
224 if (iDisplay.PowerSupplyStatus())
226 toolStripStatusLabelPower.Text = "ON";
230 toolStripStatusLabelPower.Text = "OFF";
232 //Issue next request then
233 iDisplay.RequestDeviceId();
236 case Display.TMiniDisplayRequest.EMiniDisplayRequestDeviceId:
237 toolStripStatusLabelConnect.Text += " - " + iDisplay.DeviceId();
238 //No more request to issue
244 public static uint ColorWhiteIsOn(int aX, int aY, uint aPixel)
246 if ((aPixel & 0x00FFFFFF) == 0x00FFFFFF)
253 public static uint ColorUntouched(int aX, int aY, uint aPixel)
258 public static uint ColorInversed(int aX, int aY, uint aPixel)
263 public static uint ColorChessboard(int aX, int aY, uint aPixel)
265 if ((aX % 2 == 0) && (aY % 2 == 0))
269 else if ((aX % 2 != 0) && (aY % 2 != 0))
277 public static int ScreenReversedX(System.Drawing.Bitmap aBmp, int aX)
279 return aBmp.Width - aX - 1;
282 public int ScreenReversedY(System.Drawing.Bitmap aBmp, int aY)
284 return iBmp.Height - aY - 1;
287 public int ScreenX(System.Drawing.Bitmap aBmp, int aX)
292 public int ScreenY(System.Drawing.Bitmap aBmp, int aY)
298 /// Select proper pixel delegates according to our current settings.
300 private void SetupPixelDelegates()
302 //Select our pixel processing routine
303 if (cds.InverseColors)
305 //iColorFx = ColorChessboard;
306 iColorFx = ColorInversed;
310 iColorFx = ColorWhiteIsOn;
313 //Select proper coordinate translation functions
314 //We used delegate/function pointer to support reverse screen without doing an extra test on each pixels
315 if (cds.ReverseScreen)
317 iScreenX = ScreenReversedX;
318 iScreenY = ScreenReversedY;
328 //This is our timer tick responsible to perform our render
329 private void timer_Tick(object sender, EventArgs e)
331 //Update our animations
332 DateTime NewTickTime = DateTime.Now;
334 //Update animation for all our marquees
335 foreach (Control ctrl in tableLayoutPanel.Controls)
337 if (ctrl is MarqueeLabel)
339 ((MarqueeLabel)ctrl).UpdateAnimation(LastTickTime, NewTickTime);
345 if (iDisplay.IsOpen())
347 CheckForRequestResults();
352 iBmp = new System.Drawing.Bitmap(tableLayoutPanel.Width, tableLayoutPanel.Height, PixelFormat.Format32bppArgb);
354 tableLayoutPanel.DrawToBitmap(iBmp, tableLayoutPanel.ClientRectangle);
355 //iBmp.Save("D:\\capture.png");
357 //Send it to our display
358 for (int i = 0; i < iBmp.Width; i++)
360 for (int j = 0; j < iBmp.Height; j++)
364 //Get our processed pixel coordinates
365 int x = iScreenX(iBmp, i);
366 int y = iScreenY(iBmp, j);
368 uint color = (uint)iBmp.GetPixel(i, j).ToArgb();
369 //Apply color effects
370 color = iColorFx(x,y,color);
372 iDisplay.SetPixel(x, y, color);
377 iDisplay.SwapBuffers();
381 //Compute instant FPS
382 toolStripStatusLabelFps.Text = (1.0/NewTickTime.Subtract(LastTickTime).TotalSeconds).ToString("F0") + " / " + (1000/timer.Interval).ToString() + " FPS";
384 LastTickTime = NewTickTime;
388 private void OpenDisplayConnection()
390 CloseDisplayConnection();
392 if (iDisplay.Open((Display.TMiniDisplayType)cds.DisplayType))
395 iDisplay.RequestFirmwareRevision();
400 toolStripStatusLabelConnect.Text = "Connection error";
404 private void CloseDisplayConnection()
410 private void buttonOpen_Click(object sender, EventArgs e)
412 OpenDisplayConnection();
415 private void buttonClose_Click(object sender, EventArgs e)
417 CloseDisplayConnection();
420 private void buttonClear_Click(object sender, EventArgs e)
423 iDisplay.SwapBuffers();
426 private void buttonFill_Click(object sender, EventArgs e)
429 iDisplay.SwapBuffers();
432 private void trackBarBrightness_Scroll(object sender, EventArgs e)
434 cds.Brightness = trackBarBrightness.Value;
435 Properties.Settings.Default.Save();
436 iDisplay.SetBrightness(trackBarBrightness.Value);
442 /// CDS stands for Current Display Settings
444 private DisplaySettings cds
448 DisplaysSettings settings = Properties.Settings.Default.DisplaysSettings;
449 if (settings == null)
451 settings = new DisplaysSettings();
453 Properties.Settings.Default.DisplaysSettings = settings;
456 //Make sure all our settings have been created
457 while (settings.Displays.Count <= Properties.Settings.Default.CurrentDisplayIndex)
459 settings.Displays.Add(new DisplaySettings());
462 DisplaySettings displaySettings = settings.Displays[Properties.Settings.Default.CurrentDisplayIndex];
463 return displaySettings;
468 /// Check if the given font has a fixed character pitch.
470 /// <param name="ft"></param>
471 /// <returns>0.0f if this is not a monospace font, otherwise returns the character width.</returns>
472 public float IsFixedWidth(Font ft)
474 Graphics g = CreateGraphics();
475 char[] charSizes = new char[] { 'i', 'a', 'Z', '%', '#', 'a', 'B', 'l', 'm', ',', '.' };
476 float charWidth = g.MeasureString("I", ft, Int32.MaxValue, StringFormat.GenericTypographic).Width;
478 bool fixedWidth = true;
480 foreach (char c in charSizes)
481 if (g.MeasureString(c.ToString(), ft, Int32.MaxValue, StringFormat.GenericTypographic).Width != charWidth)
492 private void UpdateStatus()
494 //Synchronize UI with settings
496 checkBoxShowBorders.Checked = cds.ShowBorders;
497 tableLayoutPanel.CellBorderStyle = (cds.ShowBorders ? TableLayoutPanelCellBorderStyle.Single : TableLayoutPanelCellBorderStyle.None);
499 //Set the proper font to each of our labels
500 foreach (MarqueeLabel ctrl in tableLayoutPanel.Controls)
502 ctrl.Font = cds.Font;
506 checkBoxConnectOnStartup.Checked = Properties.Settings.Default.DisplayConnectOnStartup;
507 checkBoxReverseScreen.Checked = cds.ReverseScreen;
508 checkBoxInverseColors.Checked = cds.InverseColors;
509 comboBoxDisplayType.SelectedIndex = cds.DisplayType;
510 timer.Interval = cds.TimerInterval;
511 maskedTextBoxTimerInterval.Text = cds.TimerInterval.ToString();
513 SetupPixelDelegates();
515 if (iDisplay.IsOpen())
517 //Only setup brightness if display is open
518 trackBarBrightness.Minimum = iDisplay.MinBrightness();
519 trackBarBrightness.Maximum = iDisplay.MaxBrightness();
520 trackBarBrightness.Value = cds.Brightness;
521 trackBarBrightness.LargeChange = Math.Max(1, (iDisplay.MaxBrightness() - iDisplay.MinBrightness()) / 5);
522 trackBarBrightness.SmallChange = 1;
523 iDisplay.SetBrightness(cds.Brightness);
525 buttonFill.Enabled = true;
526 buttonClear.Enabled = true;
527 buttonOpen.Enabled = false;
528 buttonClose.Enabled = true;
529 trackBarBrightness.Enabled = true;
530 toolStripStatusLabelConnect.Text = "Connected - " + iDisplay.Vendor() + " - " + iDisplay.Product();
531 //+ " - " + iDisplay.SerialNumber();
533 if (iDisplay.SupportPowerOnOff())
535 buttonPowerOn.Enabled = true;
536 buttonPowerOff.Enabled = true;
540 buttonPowerOn.Enabled = false;
541 buttonPowerOff.Enabled = false;
544 if (iDisplay.SupportClock())
546 buttonShowClock.Enabled = true;
547 buttonHideClock.Enabled = true;
551 buttonShowClock.Enabled = false;
552 buttonHideClock.Enabled = false;
557 buttonFill.Enabled = false;
558 buttonClear.Enabled = false;
559 buttonOpen.Enabled = true;
560 buttonClose.Enabled = false;
561 trackBarBrightness.Enabled = false;
562 buttonPowerOn.Enabled = false;
563 buttonPowerOff.Enabled = false;
564 buttonShowClock.Enabled = false;
565 buttonHideClock.Enabled = false;
566 toolStripStatusLabelConnect.Text = "Disconnected";
567 toolStripStatusLabelPower.Text = "N/A";
573 private void checkBoxShowBorders_CheckedChanged(object sender, EventArgs e)
575 //Save our show borders setting
576 tableLayoutPanel.CellBorderStyle = (checkBoxShowBorders.Checked ? TableLayoutPanelCellBorderStyle.Single : TableLayoutPanelCellBorderStyle.None);
577 cds.ShowBorders = checkBoxShowBorders.Checked;
578 Properties.Settings.Default.Save();
582 private void checkBoxConnectOnStartup_CheckedChanged(object sender, EventArgs e)
584 //Save our connect on startup setting
585 Properties.Settings.Default.DisplayConnectOnStartup = checkBoxConnectOnStartup.Checked;
586 Properties.Settings.Default.Save();
589 private void checkBoxReverseScreen_CheckedChanged(object sender, EventArgs e)
591 //Save our reverse screen setting
592 cds.ReverseScreen = checkBoxReverseScreen.Checked;
593 Properties.Settings.Default.Save();
594 SetupPixelDelegates();
597 private void checkBoxInverseColors_CheckedChanged(object sender, EventArgs e)
599 //Save our inverse colors setting
600 cds.InverseColors = checkBoxInverseColors.Checked;
601 Properties.Settings.Default.Save();
602 SetupPixelDelegates();
605 private void MainForm_Resize(object sender, EventArgs e)
607 if (WindowState == FormWindowState.Minimized)
610 //iBmp = new System.Drawing.Bitmap(tableLayoutPanel.Width, tableLayoutPanel.Height, PixelFormat.Format32bppArgb);
611 iCreateBitmap = true;
615 private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
621 public void StartServer()
623 iServiceHost = new ServiceHost
626 new Uri[] { new Uri("net.tcp://localhost:8001/") }
629 iServiceHost.AddServiceEndpoint(typeof(IService), new NetTcpBinding(SecurityMode.None, true), "DisplayService");
633 public void StopServer()
635 if (iClients.Count > 0 && !iClosing)
639 BroadcastCloseEvent();
643 if (MessageBox.Show("Force exit?", "Waiting for clients...", MessageBoxButtons.YesNo, MessageBoxIcon.Warning) == DialogResult.Yes)
645 iClosing = false; //We make sure we force close if asked twice
650 //We removed that as it often lags for some reason
651 //iServiceHost.Close();
655 public void BroadcastCloseEvent()
657 Trace.TraceInformation("BroadcastCloseEvent - start");
659 var inactiveClients = new List<string>();
660 foreach (var client in iClients)
662 //if (client.Key != eventData.ClientName)
666 Trace.TraceInformation("BroadcastCloseEvent - " + client.Key);
667 client.Value.Callback.OnCloseOrder(/*eventData*/);
671 inactiveClients.Add(client.Key);
676 if (inactiveClients.Count > 0)
678 foreach (var client in inactiveClients)
680 iClients.Remove(client);
681 Program.iMainForm.treeViewClients.Nodes.Remove(Program.iMainForm.treeViewClients.Nodes.Find(client, false)[0]);
686 private void buttonStartClient_Click(object sender, EventArgs e)
688 Thread clientThread = new Thread(SharpDisplayClient.Program.Main);
689 clientThread.Start();
693 private void buttonSuspend_Click(object sender, EventArgs e)
695 LastTickTime = DateTime.Now; //Reset timer to prevent jump
696 timer.Enabled = !timer.Enabled;
699 buttonSuspend.Text = "Run";
703 buttonSuspend.Text = "Pause";
707 private void buttonCloseClients_Click(object sender, EventArgs e)
709 BroadcastCloseEvent();
712 private void treeViewClients_AfterSelect(object sender, TreeViewEventArgs e)
721 /// <param name="aSessionId"></param>
722 /// <param name="aCallback"></param>
723 public void AddClientThreadSafe(string aSessionId, ICallback aCallback)
725 if (this.InvokeRequired)
727 //Not in the proper thread, invoke ourselves
728 AddClientDelegate d = new AddClientDelegate(AddClientThreadSafe);
729 this.Invoke(d, new object[] { aSessionId, aCallback });
733 //We are in the proper thread
734 //Add this session to our collection of clients
735 ClientData newClient = new ClientData(aSessionId, aCallback);
736 Program.iMainForm.iClients.Add(aSessionId, newClient);
737 //Add this session to our UI
738 UpdateClientTreeViewNode(newClient);
745 /// <param name="aSessionId"></param>
746 public void RemoveClientThreadSafe(string aSessionId)
748 if (this.InvokeRequired)
750 //Not in the proper thread, invoke ourselves
751 RemoveClientDelegate d = new RemoveClientDelegate(RemoveClientThreadSafe);
752 this.Invoke(d, new object[] { aSessionId });
756 //We are in the proper thread
757 //Remove this session from both client collection and UI tree view
758 if (Program.iMainForm.iClients.Keys.Contains(aSessionId))
760 Program.iMainForm.iClients.Remove(aSessionId);
761 Program.iMainForm.treeViewClients.Nodes.Remove(Program.iMainForm.treeViewClients.Nodes.Find(aSessionId, false)[0]);
764 if (iClosing && iClients.Count == 0)
766 //We were closing our form
767 //All clients are now closed
768 //Just resume our close operation
778 /// <param name="aSessionId"></param>
779 /// <param name="aLayout"></param>
780 public void SetClientLayoutThreadSafe(string aSessionId, TableLayout aLayout)
782 if (this.InvokeRequired)
784 //Not in the proper thread, invoke ourselves
785 SetLayoutDelegate d = new SetLayoutDelegate(SetClientLayoutThreadSafe);
786 this.Invoke(d, new object[] { aSessionId, aLayout });
790 ClientData client = iClients[aSessionId];
793 client.Layout = aLayout;
794 UpdateTableLayoutPanel(client);
796 UpdateClientTreeViewNode(client);
804 /// <param name="aSessionId"></param>
805 /// <param name="aField"></param>
806 public void SetClientTextThreadSafe(string aSessionId, DataField aField)
808 if (this.InvokeRequired)
810 //Not in the proper thread, invoke ourselves
811 SetTextDelegate d = new SetTextDelegate(SetClientTextThreadSafe);
812 this.Invoke(d, new object[] { aSessionId, aField });
816 SetCurrentClient(aSessionId);
817 ClientData client = iClients[aSessionId];
820 //Make sure all our texts are in place
821 while (client.Fields.Count < (aField.Index + 1))
823 //Add a text field with proper index
824 client.Fields.Add(new DataField(client.Fields.Count));
826 client.Fields[aField.Index] = aField;
828 //We are in the proper thread
829 if (tableLayoutPanel.Controls[aField.Index] is MarqueeLabel)
831 MarqueeLabel label = (MarqueeLabel)tableLayoutPanel.Controls[aField.Index];
832 label.Text = aField.Text;
833 label.TextAlign = aField.Alignment;
837 //Wrong control type, re-create them all
838 UpdateTableLayoutPanel(iCurrentClientData);
841 UpdateClientTreeViewNode(client);
849 /// <param name="aTexts"></param>
850 public void SetClientTextsThreadSafe(string aSessionId, System.Collections.Generic.IList<DataField> aFields)
852 if (this.InvokeRequired)
854 //Not in the proper thread, invoke ourselves
855 SetTextsDelegate d = new SetTextsDelegate(SetClientTextsThreadSafe);
856 this.Invoke(d, new object[] { aSessionId, aFields });
860 SetCurrentClient(aSessionId);
861 //We are in the proper thread
862 ClientData client = iClients[aSessionId];
865 //Populate our client with the given text fields
866 foreach (DataField field in aFields)
868 //Make sure all our texts are in place
869 while (client.Fields.Count < (field.Index + 1))
871 //Add a text field with proper index
872 client.Fields.Add(new DataField(client.Fields.Count));
874 client.Fields[field.Index] = field;
876 //Put each our text fields in a label control
877 foreach (DataField field in aFields)
879 if (tableLayoutPanel.Controls[field.Index] is MarqueeLabel)
881 //Proper control type just update the text
882 MarqueeLabel label = (MarqueeLabel)tableLayoutPanel.Controls[field.Index];
883 label.Text = field.Text;
884 label.TextAlign = field.Alignment;
888 //Wrong control for the given field
889 //Update our layout thus re-creating our controls
890 UpdateTableLayoutPanel(iCurrentClientData);
891 break; //No need to keep on looping layout update will take care of everything
895 UpdateClientTreeViewNode(client);
903 /// <param name="aSessionId"></param>
904 /// <param name="aTextField"></param>
905 public void SetClientBitmapThreadSafe(string aSessionId, DataField aField)
907 if (this.InvokeRequired)
909 //Not in the proper thread, invoke ourselves
910 SetBitmapDelegate d = new SetBitmapDelegate(SetClientBitmapThreadSafe);
911 this.Invoke(d, new object[] { aSessionId, aField });
915 SetCurrentClient(aSessionId);
916 ClientData client = iClients[aSessionId];
919 //Make sure all our texts are in place
920 while (client.Fields.Count < (aField.Index + 1))
922 //Add a text field with proper index
923 client.Fields.Add(new DataField(client.Fields.Count));
926 client.Fields[aField.Index] = aField;
928 //We are in the proper thread
929 if (tableLayoutPanel.Controls[aField.Index] is PictureBox)
931 PictureBox pictureBox = (PictureBox)tableLayoutPanel.Controls[aField.Index];
932 pictureBox.Image = aField.Bitmap;
936 //Wrong control type re-create them all
937 UpdateTableLayoutPanel(iCurrentClientData);
940 UpdateClientTreeViewNode(client);
950 /// <param name="aSessionId"></param>
951 /// <param name="aName"></param>
952 public void SetClientNameThreadSafe(string aSessionId, string aName)
954 if (this.InvokeRequired)
956 //Not in the proper thread, invoke ourselves
957 SetClientNameDelegate d = new SetClientNameDelegate(SetClientNameThreadSafe);
958 this.Invoke(d, new object[] { aSessionId, aName });
962 //We are in the proper thread
964 ClientData client = iClients[aSessionId];
969 //Update our tree-view
970 UpdateClientTreeViewNode(client);
978 /// <param name="aClient"></param>
979 private void UpdateClientTreeViewNode(ClientData aClient)
986 TreeNode node = null;
987 //Check that our client node already exists
988 //Get our client root node using its key which is our session ID
989 TreeNode[] nodes = treeViewClients.Nodes.Find(aClient.SessionId, false);
992 //We already have a node for that client
994 //Clear children as we are going to recreate them below
999 //Client node does not exists create a new one
1000 treeViewClients.Nodes.Add(aClient.SessionId, aClient.SessionId);
1001 node = treeViewClients.Nodes.Find(aClient.SessionId, false)[0];
1007 if (aClient.Name != "")
1009 //We have a name, us it as text for our root node
1010 node.Text = aClient.Name;
1011 //Add a child with SessionId
1012 node.Nodes.Add(new TreeNode(aClient.SessionId));
1016 //No name, use session ID instead
1017 node.Text = aClient.SessionId;
1020 if (aClient.Fields.Count > 0)
1022 //Create root node for our texts
1023 TreeNode textsRoot = new TreeNode("Fields");
1024 node.Nodes.Add(textsRoot);
1025 //For each text add a new entry
1026 foreach (DataField field in aClient.Fields)
1028 if (!field.HasBitmap)
1030 DataField textField = (DataField)field;
1031 textsRoot.Nodes.Add(new TreeNode("[Text]" + textField.Text));
1035 textsRoot.Nodes.Add(new TreeNode("[Bitmap]"));
1044 private void buttonAddRow_Click(object sender, EventArgs e)
1046 if (tableLayoutPanel.RowCount < 6)
1048 UpdateTableLayoutPanel(tableLayoutPanel.ColumnCount, tableLayoutPanel.RowCount + 1);
1052 private void buttonRemoveRow_Click(object sender, EventArgs e)
1054 if (tableLayoutPanel.RowCount > 1)
1056 UpdateTableLayoutPanel(tableLayoutPanel.ColumnCount, tableLayoutPanel.RowCount - 1);
1059 UpdateTableLayoutRowStyles();
1062 private void buttonAddColumn_Click(object sender, EventArgs e)
1064 if (tableLayoutPanel.ColumnCount < 8)
1066 UpdateTableLayoutPanel(tableLayoutPanel.ColumnCount + 1, tableLayoutPanel.RowCount);
1070 private void buttonRemoveColumn_Click(object sender, EventArgs e)
1072 if (tableLayoutPanel.ColumnCount > 1)
1074 UpdateTableLayoutPanel(tableLayoutPanel.ColumnCount - 1, tableLayoutPanel.RowCount);
1080 /// Update our table layout row styles to make sure each rows have similar height
1082 private void UpdateTableLayoutRowStyles()
1084 foreach (RowStyle rowStyle in tableLayoutPanel.RowStyles)
1086 rowStyle.SizeType = SizeType.Percent;
1087 rowStyle.Height = 100 / tableLayoutPanel.RowCount;
1093 /// Empty and recreate our table layout with the given number of columns and rows.
1094 /// Sizes of rows and columns are uniform.
1096 /// <param name="aColumn"></param>
1097 /// <param name="aRow"></param>
1098 private void UpdateTableLayoutPanel(int aColumn, int aRow)
1100 tableLayoutPanel.Controls.Clear();
1101 tableLayoutPanel.RowStyles.Clear();
1102 tableLayoutPanel.ColumnStyles.Clear();
1103 tableLayoutPanel.RowCount = 0;
1104 tableLayoutPanel.ColumnCount = 0;
1106 while (tableLayoutPanel.RowCount < aRow)
1108 tableLayoutPanel.RowCount++;
1111 while (tableLayoutPanel.ColumnCount < aColumn)
1113 tableLayoutPanel.ColumnCount++;
1116 for (int i = 0; i < tableLayoutPanel.ColumnCount; i++)
1118 //Create our column styles
1119 this.tableLayoutPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100 / tableLayoutPanel.ColumnCount));
1121 for (int j = 0; j < tableLayoutPanel.RowCount; j++)
1125 //Create our row styles
1126 this.tableLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100 / tableLayoutPanel.RowCount));
1129 MarqueeLabel control = new SharpDisplayManager.MarqueeLabel();
1130 control.AutoEllipsis = true;
1131 control.AutoSize = true;
1132 control.BackColor = System.Drawing.Color.Transparent;
1133 control.Dock = System.Windows.Forms.DockStyle.Fill;
1134 control.Location = new System.Drawing.Point(1, 1);
1135 control.Margin = new System.Windows.Forms.Padding(0);
1136 control.Name = "marqueeLabelCol" + aColumn + "Row" + aRow;
1137 control.OwnTimer = false;
1138 control.PixelsPerSecond = 64;
1139 control.Separator = "|";
1140 //control.Size = new System.Drawing.Size(254, 30);
1141 //control.TabIndex = 2;
1142 control.Font = cds.Font;
1143 control.Text = "ABCDEFGHIJKLMNOPQRST-0123456789";
1144 control.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
1145 control.UseCompatibleTextRendering = true;
1147 tableLayoutPanel.Controls.Add(control, i, j);
1156 /// Update our display table layout.
1158 /// <param name="aLayout"></param>
1159 private void UpdateTableLayoutPanel(ClientData aClient)
1161 TableLayout layout = aClient.Layout;
1164 tableLayoutPanel.Controls.Clear();
1165 tableLayoutPanel.RowStyles.Clear();
1166 tableLayoutPanel.ColumnStyles.Clear();
1167 tableLayoutPanel.RowCount = 0;
1168 tableLayoutPanel.ColumnCount = 0;
1170 while (tableLayoutPanel.RowCount < layout.Rows.Count)
1172 tableLayoutPanel.RowCount++;
1175 while (tableLayoutPanel.ColumnCount < layout.Columns.Count)
1177 tableLayoutPanel.ColumnCount++;
1180 for (int i = 0; i < tableLayoutPanel.ColumnCount; i++)
1182 //Create our column styles
1183 this.tableLayoutPanel.ColumnStyles.Add(layout.Columns[i]);
1185 for (int j = 0; j < tableLayoutPanel.RowCount; j++)
1189 //Create our row styles
1190 this.tableLayoutPanel.RowStyles.Add(layout.Rows[j]);
1193 //Check if we already have a control
1194 Control existingControl = tableLayoutPanel.GetControlFromPosition(i,j);
1195 if (existingControl!=null)
1197 //We already have a control in that cell as a results of row/col spanning
1198 //Move on to next cell then
1204 //Check if a client field already exists for that cell
1205 if (aClient.Fields.Count <= tableLayoutPanel.Controls.Count)
1207 //No client field specified, create a text field by default
1208 aClient.Fields.Add(new DataField(aClient.Fields.Count));
1211 //Create a control corresponding to the field specified for that cell
1212 DataField field = aClient.Fields[tableLayoutPanel.Controls.Count];
1213 Control control = CreateControlForDataField(field);
1215 //Add newly created control to our table layout at the specified row and column
1216 tableLayoutPanel.Controls.Add(control, i, j);
1217 //Make sure we specify row and column span for that new control
1218 tableLayoutPanel.SetRowSpan(control,field.RowSpan);
1219 tableLayoutPanel.SetColumnSpan(control, field.ColumnSpan);
1224 while (aClient.Fields.Count > fieldCount)
1226 //We have too much fields for this layout
1227 //Just discard them until we get there
1228 aClient.Fields.RemoveAt(aClient.Fields.Count-1);
1235 /// Check our type of data field and create corresponding control
1237 /// <param name="aField"></param>
1238 private Control CreateControlForDataField(DataField aField)
1240 Control control=null;
1241 if (!aField.HasBitmap)
1243 MarqueeLabel label = new SharpDisplayManager.MarqueeLabel();
1244 label.AutoEllipsis = true;
1245 label.AutoSize = true;
1246 label.BackColor = System.Drawing.Color.Transparent;
1247 label.Dock = System.Windows.Forms.DockStyle.Fill;
1248 label.Location = new System.Drawing.Point(1, 1);
1249 label.Margin = new System.Windows.Forms.Padding(0);
1250 label.Name = "marqueeLabel" + aField.Index;
1251 label.OwnTimer = false;
1252 label.PixelsPerSecond = 64;
1253 label.Separator = "|";
1254 //control.Size = new System.Drawing.Size(254, 30);
1255 //control.TabIndex = 2;
1256 label.Font = cds.Font;
1258 label.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
1259 label.UseCompatibleTextRendering = true;
1260 label.Text = aField.Text;
1266 //Create picture box
1267 PictureBox picture = new PictureBox();
1268 picture.AutoSize = true;
1269 picture.BackColor = System.Drawing.Color.Transparent;
1270 picture.Dock = System.Windows.Forms.DockStyle.Fill;
1271 picture.Location = new System.Drawing.Point(1, 1);
1272 picture.Margin = new System.Windows.Forms.Padding(0);
1273 picture.Name = "pictureBox" + aField;
1275 picture.Image = aField.Bitmap;
1284 private void buttonAlignLeft_Click(object sender, EventArgs e)
1286 foreach (MarqueeLabel ctrl in tableLayoutPanel.Controls)
1288 ctrl.TextAlign = ContentAlignment.MiddleLeft;
1292 private void buttonAlignCenter_Click(object sender, EventArgs e)
1294 foreach (MarqueeLabel ctrl in tableLayoutPanel.Controls)
1296 ctrl.TextAlign = ContentAlignment.MiddleCenter;
1300 private void buttonAlignRight_Click(object sender, EventArgs e)
1302 foreach (MarqueeLabel ctrl in tableLayoutPanel.Controls)
1304 ctrl.TextAlign = ContentAlignment.MiddleRight;
1308 private void comboBoxDisplayType_SelectedIndexChanged(object sender, EventArgs e)
1310 Properties.Settings.Default.CurrentDisplayIndex = comboBoxDisplayType.SelectedIndex;
1311 cds.DisplayType = comboBoxDisplayType.SelectedIndex;
1312 Properties.Settings.Default.Save();
1313 if (iDisplay.IsOpen())
1315 OpenDisplayConnection();
1324 private void maskedTextBoxTimerInterval_TextChanged(object sender, EventArgs e)
1326 if (maskedTextBoxTimerInterval.Text != "")
1328 int interval = Convert.ToInt32(maskedTextBoxTimerInterval.Text);
1332 timer.Interval = interval;
1333 cds.TimerInterval = timer.Interval;
1334 Properties.Settings.Default.Save();
1339 private void buttonPowerOn_Click(object sender, EventArgs e)
1344 private void buttonPowerOff_Click(object sender, EventArgs e)
1346 iDisplay.PowerOff();
1349 private void buttonShowClock_Click(object sender, EventArgs e)
1351 iDisplay.ShowClock();
1354 private void buttonHideClock_Click(object sender, EventArgs e)
1356 iDisplay.HideClock();
1361 /// A UI thread copy of a client relevant data.
1362 /// Keeping this copy in the UI thread helps us deal with threading issues.
1364 public class ClientData
1366 public ClientData(string aSessionId, ICallback aCallback)
1368 SessionId = aSessionId;
1370 Fields = new List<DataField>();
1371 Layout = new TableLayout(1, 2); //Default to one column and two rows
1372 Callback = aCallback;
1375 public string SessionId { get; set; }
1376 public string Name { get; set; }
1377 public List<DataField> Fields { get; set; }
1378 public TableLayout Layout { get; set; }
1379 public ICallback Callback { get; set; }