Better client documentation regarding our bitmap layout.
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, TextField aTextField);
29 public delegate void SetBitmapDelegate(string SessionId, BitmapField aTextField);
30 public delegate void SetLayoutDelegate(string SessionId, TableLayout aLayout);
31 public delegate void SetTextsDelegate(string SessionId, System.Collections.Generic.IList<TextField> aTextFields);
32 public delegate void SetClientNameDelegate(string aSessionId, string aName);
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="aTextField"></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="aTextField"></param>
806 public void SetClientTextThreadSafe(string aSessionId, TextField aTextField)
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, aTextField });
816 SetCurrentClient(aSessionId);
817 ClientData client = iClients[aSessionId];
820 //Make sure all our texts are in place
821 while (client.Fields.Count < (aTextField.Index + 1))
823 //Add a text field with proper index
824 client.Fields.Add(new TextField(client.Fields.Count));
826 client.Fields[aTextField.Index] = aTextField;
828 //We are in the proper thread
829 if (tableLayoutPanel.Controls[aTextField.Index] is MarqueeLabel)
831 MarqueeLabel label = (MarqueeLabel)tableLayoutPanel.Controls[aTextField.Index];
832 label.Text = aTextField.Text;
833 label.TextAlign = aTextField.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<TextField> aTextFields)
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, aTextFields });
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 (TextField textField in aTextFields)
868 //Make sure all our texts are in place
869 while (client.Fields.Count < (textField.Index + 1))
871 //Add a text field with proper index
872 client.Fields.Add(new TextField(client.Fields.Count));
874 client.Fields[textField.Index] = textField;
876 //Put each our text fields in a label control
877 foreach (TextField field in aTextFields)
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
896 UpdateClientTreeViewNode(client);
904 /// <param name="aSessionId"></param>
905 /// <param name="aTextField"></param>
906 public void SetClientBitmapThreadSafe(string aSessionId, BitmapField aBitmapField)
908 if (this.InvokeRequired)
910 //Not in the proper thread, invoke ourselves
911 SetBitmapDelegate d = new SetBitmapDelegate(SetClientBitmapThreadSafe);
912 this.Invoke(d, new object[] { aSessionId, aBitmapField });
916 SetCurrentClient(aSessionId);
917 ClientData client = iClients[aSessionId];
920 //Make sure all our texts are in place
921 while (client.Fields.Count < (aBitmapField.Index + 1))
923 //Add a text field with proper index
924 client.Fields.Add(new TextField(client.Fields.Count));
927 client.Fields[aBitmapField.Index] = aBitmapField;
929 //We are in the proper thread
930 if (tableLayoutPanel.Controls[aBitmapField.Index] is PictureBox)
932 PictureBox pictureBox = (PictureBox)tableLayoutPanel.Controls[aBitmapField.Index];
933 pictureBox.Image = aBitmapField.Bitmap;
937 //Wrong control type re-create them all
938 UpdateTableLayoutPanel(iCurrentClientData);
941 UpdateClientTreeViewNode(client);
951 /// <param name="aSessionId"></param>
952 /// <param name="aName"></param>
953 public void SetClientNameThreadSafe(string aSessionId, string aName)
955 if (this.InvokeRequired)
957 //Not in the proper thread, invoke ourselves
958 SetClientNameDelegate d = new SetClientNameDelegate(SetClientNameThreadSafe);
959 this.Invoke(d, new object[] { aSessionId, aName });
963 //We are in the proper thread
965 ClientData client = iClients[aSessionId];
970 //Update our tree-view
971 UpdateClientTreeViewNode(client);
979 /// <param name="aClient"></param>
980 private void UpdateClientTreeViewNode(ClientData aClient)
987 TreeNode node = null;
988 //Check that our client node already exists
989 //Get our client root node using its key which is our session ID
990 TreeNode[] nodes = treeViewClients.Nodes.Find(aClient.SessionId, false);
993 //We already have a node for that client
995 //Clear children as we are going to recreate them below
1000 //Client node does not exists create a new one
1001 treeViewClients.Nodes.Add(aClient.SessionId, aClient.SessionId);
1002 node = treeViewClients.Nodes.Find(aClient.SessionId, false)[0];
1008 if (aClient.Name != "")
1010 //We have a name, us it as text for our root node
1011 node.Text = aClient.Name;
1012 //Add a child with SessionId
1013 node.Nodes.Add(new TreeNode(aClient.SessionId));
1017 //No name, use session ID instead
1018 node.Text = aClient.SessionId;
1021 if (aClient.Fields.Count > 0)
1023 //Create root node for our texts
1024 TreeNode textsRoot = new TreeNode("Fields");
1025 node.Nodes.Add(textsRoot);
1026 //For each text add a new entry
1027 foreach (DataField field in aClient.Fields)
1029 if (field is TextField)
1031 TextField textField = (TextField)field;
1032 textsRoot.Nodes.Add(new TreeNode("[Text]" + textField.Text));
1034 else if (field is BitmapField)
1036 textsRoot.Nodes.Add(new TreeNode("[Bitmap]"));
1040 textsRoot.Nodes.Add(new TreeNode("[Unknown]"));
1049 private void buttonAddRow_Click(object sender, EventArgs e)
1051 if (tableLayoutPanel.RowCount < 6)
1053 UpdateTableLayoutPanel(tableLayoutPanel.ColumnCount, tableLayoutPanel.RowCount + 1);
1057 private void buttonRemoveRow_Click(object sender, EventArgs e)
1059 if (tableLayoutPanel.RowCount > 1)
1061 UpdateTableLayoutPanel(tableLayoutPanel.ColumnCount, tableLayoutPanel.RowCount - 1);
1064 UpdateTableLayoutRowStyles();
1067 private void buttonAddColumn_Click(object sender, EventArgs e)
1069 if (tableLayoutPanel.ColumnCount < 8)
1071 UpdateTableLayoutPanel(tableLayoutPanel.ColumnCount + 1, tableLayoutPanel.RowCount);
1075 private void buttonRemoveColumn_Click(object sender, EventArgs e)
1077 if (tableLayoutPanel.ColumnCount > 1)
1079 UpdateTableLayoutPanel(tableLayoutPanel.ColumnCount - 1, tableLayoutPanel.RowCount);
1085 /// Update our table layout row styles to make sure each rows have similar height
1087 private void UpdateTableLayoutRowStyles()
1089 foreach (RowStyle rowStyle in tableLayoutPanel.RowStyles)
1091 rowStyle.SizeType = SizeType.Percent;
1092 rowStyle.Height = 100 / tableLayoutPanel.RowCount;
1098 /// Empty and recreate our table layout with the given number of columns and rows.
1099 /// Sizes of rows and columns are uniform.
1101 /// <param name="aColumn"></param>
1102 /// <param name="aRow"></param>
1103 private void UpdateTableLayoutPanel(int aColumn, int aRow)
1105 tableLayoutPanel.Controls.Clear();
1106 tableLayoutPanel.RowStyles.Clear();
1107 tableLayoutPanel.ColumnStyles.Clear();
1108 tableLayoutPanel.RowCount = 0;
1109 tableLayoutPanel.ColumnCount = 0;
1111 while (tableLayoutPanel.RowCount < aRow)
1113 tableLayoutPanel.RowCount++;
1116 while (tableLayoutPanel.ColumnCount < aColumn)
1118 tableLayoutPanel.ColumnCount++;
1121 for (int i = 0; i < tableLayoutPanel.ColumnCount; i++)
1123 //Create our column styles
1124 this.tableLayoutPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100 / tableLayoutPanel.ColumnCount));
1126 for (int j = 0; j < tableLayoutPanel.RowCount; j++)
1130 //Create our row styles
1131 this.tableLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100 / tableLayoutPanel.RowCount));
1134 MarqueeLabel control = new SharpDisplayManager.MarqueeLabel();
1135 control.AutoEllipsis = true;
1136 control.AutoSize = true;
1137 control.BackColor = System.Drawing.Color.Transparent;
1138 control.Dock = System.Windows.Forms.DockStyle.Fill;
1139 control.Location = new System.Drawing.Point(1, 1);
1140 control.Margin = new System.Windows.Forms.Padding(0);
1141 control.Name = "marqueeLabelCol" + aColumn + "Row" + aRow;
1142 control.OwnTimer = false;
1143 control.PixelsPerSecond = 64;
1144 control.Separator = "|";
1145 //control.Size = new System.Drawing.Size(254, 30);
1146 //control.TabIndex = 2;
1147 control.Font = cds.Font;
1148 control.Text = "ABCDEFGHIJKLMNOPQRST-0123456789";
1149 control.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
1150 control.UseCompatibleTextRendering = true;
1152 tableLayoutPanel.Controls.Add(control, i, j);
1161 /// Update our display table layout.
1163 /// <param name="aLayout"></param>
1164 private void UpdateTableLayoutPanel(ClientData aClient)
1166 TableLayout layout = aClient.Layout;
1169 tableLayoutPanel.Controls.Clear();
1170 tableLayoutPanel.RowStyles.Clear();
1171 tableLayoutPanel.ColumnStyles.Clear();
1172 tableLayoutPanel.RowCount = 0;
1173 tableLayoutPanel.ColumnCount = 0;
1175 while (tableLayoutPanel.RowCount < layout.Rows.Count)
1177 tableLayoutPanel.RowCount++;
1180 while (tableLayoutPanel.ColumnCount < layout.Columns.Count)
1182 tableLayoutPanel.ColumnCount++;
1185 for (int i = 0; i < tableLayoutPanel.ColumnCount; i++)
1187 //Create our column styles
1188 this.tableLayoutPanel.ColumnStyles.Add(layout.Columns[i]);
1190 for (int j = 0; j < tableLayoutPanel.RowCount; j++)
1194 //Create our row styles
1195 this.tableLayoutPanel.RowStyles.Add(layout.Rows[j]);
1198 //Check if we already have a control
1199 Control existingControl = tableLayoutPanel.GetControlFromPosition(i,j);
1200 if (existingControl!=null)
1202 //We already have a control in that cell as a results of row/col spanning
1203 //Move on to next cell then
1209 //Check if a client field already exists for that cell
1210 if (aClient.Fields.Count <= tableLayoutPanel.Controls.Count)
1212 //No client field specified, create a text field by default
1213 aClient.Fields.Add(new TextField(aClient.Fields.Count));
1216 //Create a control corresponding to the field specified for that cell
1217 DataField field = aClient.Fields[tableLayoutPanel.Controls.Count];
1218 Control control = CreateControlForDataField(field);
1220 //Add newly created control to our table layout at the specified row and column
1221 tableLayoutPanel.Controls.Add(control, i, j);
1222 //Make sure we specify row and column span for that new control
1223 tableLayoutPanel.SetRowSpan(control,field.RowSpan);
1224 tableLayoutPanel.SetColumnSpan(control, field.ColumnSpan);
1229 while (aClient.Fields.Count > fieldCount)
1231 //We have too much fields for this layout
1232 //Just discard them until we get there
1233 aClient.Fields.RemoveAt(aClient.Fields.Count-1);
1240 /// Check our type of data field and create corresponding control
1242 /// <param name="aField"></param>
1243 private Control CreateControlForDataField(DataField aField)
1245 Control control=null;
1246 if (aField is TextField)
1248 MarqueeLabel label = new SharpDisplayManager.MarqueeLabel();
1249 label.AutoEllipsis = true;
1250 label.AutoSize = true;
1251 label.BackColor = System.Drawing.Color.Transparent;
1252 label.Dock = System.Windows.Forms.DockStyle.Fill;
1253 label.Location = new System.Drawing.Point(1, 1);
1254 label.Margin = new System.Windows.Forms.Padding(0);
1255 label.Name = "marqueeLabel" + aField.Index;
1256 label.OwnTimer = false;
1257 label.PixelsPerSecond = 64;
1258 label.Separator = "|";
1259 //control.Size = new System.Drawing.Size(254, 30);
1260 //control.TabIndex = 2;
1261 label.Font = cds.Font;
1263 label.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
1264 label.UseCompatibleTextRendering = true;
1265 TextField textField = (TextField)aField;
1266 label.Text = textField.Text;
1270 else if (aField is BitmapField)
1272 //Create picture box
1273 PictureBox picture = new PictureBox();
1274 picture.AutoSize = true;
1275 picture.BackColor = System.Drawing.Color.Transparent;
1276 picture.Dock = System.Windows.Forms.DockStyle.Fill;
1277 picture.Location = new System.Drawing.Point(1, 1);
1278 picture.Margin = new System.Windows.Forms.Padding(0);
1279 picture.Name = "pictureBox" + aField;
1281 BitmapField bitmapField = (BitmapField)aField;
1282 picture.Image = bitmapField.Bitmap;
1291 private void buttonAlignLeft_Click(object sender, EventArgs e)
1293 foreach (MarqueeLabel ctrl in tableLayoutPanel.Controls)
1295 ctrl.TextAlign = ContentAlignment.MiddleLeft;
1299 private void buttonAlignCenter_Click(object sender, EventArgs e)
1301 foreach (MarqueeLabel ctrl in tableLayoutPanel.Controls)
1303 ctrl.TextAlign = ContentAlignment.MiddleCenter;
1307 private void buttonAlignRight_Click(object sender, EventArgs e)
1309 foreach (MarqueeLabel ctrl in tableLayoutPanel.Controls)
1311 ctrl.TextAlign = ContentAlignment.MiddleRight;
1315 private void comboBoxDisplayType_SelectedIndexChanged(object sender, EventArgs e)
1317 Properties.Settings.Default.CurrentDisplayIndex = comboBoxDisplayType.SelectedIndex;
1318 cds.DisplayType = comboBoxDisplayType.SelectedIndex;
1319 Properties.Settings.Default.Save();
1320 if (iDisplay.IsOpen())
1322 OpenDisplayConnection();
1331 private void maskedTextBoxTimerInterval_TextChanged(object sender, EventArgs e)
1333 if (maskedTextBoxTimerInterval.Text != "")
1335 int interval = Convert.ToInt32(maskedTextBoxTimerInterval.Text);
1339 timer.Interval = interval;
1340 cds.TimerInterval = timer.Interval;
1341 Properties.Settings.Default.Save();
1346 private void buttonPowerOn_Click(object sender, EventArgs e)
1351 private void buttonPowerOff_Click(object sender, EventArgs e)
1353 iDisplay.PowerOff();
1356 private void buttonShowClock_Click(object sender, EventArgs e)
1358 iDisplay.ShowClock();
1361 private void buttonHideClock_Click(object sender, EventArgs e)
1363 iDisplay.HideClock();
1368 /// A UI thread copy of a client relevant data.
1369 /// Keeping this copy in the UI thread helps us deal with threading issues.
1371 public class ClientData
1373 public ClientData(string aSessionId, ICallback aCallback)
1375 SessionId = aSessionId;
1377 Fields = new List<DataField>();
1378 Layout = new TableLayout(1, 2); //Default to one column and two rows
1379 Callback = aCallback;
1382 public string SessionId { get; set; }
1383 public string Name { get; set; }
1384 public List<DataField> Fields { get; set; }
1385 public TableLayout Layout { get; set; }
1386 public ICallback Callback { get; set; }