Version.
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;
16 using System.Deployment.Application;
17 using System.Reflection;
19 using SharpDisplayClient;
22 namespace SharpDisplayManager
25 public delegate uint ColorProcessingDelegate(int aX, int aY, uint aPixel);
26 public delegate int CoordinateTranslationDelegate(System.Drawing.Bitmap aBmp, int aInt);
27 //Delegates are used for our thread safe method
28 public delegate void AddClientDelegate(string aSessionId, ICallback aCallback);
29 public delegate void RemoveClientDelegate(string aSessionId);
30 public delegate void SetFieldDelegate(string SessionId, DataField aField);
31 public delegate void SetFieldsDelegate(string SessionId, System.Collections.Generic.IList<DataField> aFields);
32 public delegate void SetLayoutDelegate(string SessionId, TableLayout aLayout);
33 public delegate void SetClientNameDelegate(string aSessionId, string aName);
37 /// Our Display manager main form
39 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;
61 /// Manage run when Windows startup option
63 private StartupManager iStartupManager;
68 private NotifyIconAdv iNotifyIcon;
72 iCurrentClientSessionId = "";
73 iCurrentClientData = null;
74 LastTickTime = DateTime.Now;
75 iDisplay = new Display();
76 iClients = new Dictionary<string, ClientData>();
77 iStartupManager = new StartupManager();
78 iNotifyIcon = new NotifyIconAdv();
80 InitializeComponent();
82 //We have a bug when drawing minimized and reusing our bitmap
83 iBmp = new System.Drawing.Bitmap(tableLayoutPanel.Width, tableLayoutPanel.Height, PixelFormat.Format32bppArgb);
84 iCreateBitmap = false;
86 if (Properties.Settings.Default.StartMinimized)
88 WindowState = FormWindowState.Minimized;
96 /// <param name="sender"></param>
97 /// <param name="e"></param>
98 private void MainForm_Load(object sender, EventArgs e)
100 if (ApplicationDeployment.IsNetworkDeployed)
102 this.Text += " - v" + ApplicationDeployment.CurrentDeployment.CurrentVersion;
106 this.Text += " - development";
111 if (Properties.Settings.Default.DisplayConnectOnStartup)
113 OpenDisplayConnection();
116 //Setup notification icon
119 // To make sure start up with minimize to tray works
120 if (WindowState == FormWindowState.Minimized && Properties.Settings.Default.MinimizeToTray)
126 //When not debugging we want the screen to be empty until a client takes over
134 private void SetupTrayIcon()
136 iNotifyIcon.Icon = GetIcon("vfd.ico");
137 iNotifyIcon.Text = "Sharp Display Manager";
138 iNotifyIcon.Visible = true;
140 //Double click toggles visibility - typically brings up the application
141 iNotifyIcon.DoubleClick += delegate(object obj, EventArgs args)
146 //Adding a context menu, useful to be able to exit the application
147 ContextMenu contextMenu = new ContextMenu();
148 //Context menu item to toggle visibility
149 MenuItem hideShowItem = new MenuItem("Hide/Show");
150 hideShowItem.Click += delegate(object obj, EventArgs args)
154 contextMenu.MenuItems.Add(hideShowItem);
156 //Context menu item separator
157 contextMenu.MenuItems.Add(new MenuItem("-"));
159 //Context menu exit item
160 MenuItem exitItem = new MenuItem("Exit");
161 exitItem.Click += delegate(object obj, EventArgs args)
165 contextMenu.MenuItems.Add(exitItem);
167 iNotifyIcon.ContextMenu = contextMenu;
171 /// Access icons from embedded resources.
173 /// <param name="name"></param>
174 /// <returns></returns>
175 public static Icon GetIcon(string name)
177 name = "SharpDisplayManager.Resources." + name;
180 Assembly.GetExecutingAssembly().GetManifestResourceNames();
181 for (int i = 0; i < names.Length; i++)
183 if (names[i].Replace('\\', '.') == name)
185 using (Stream stream = Assembly.GetExecutingAssembly().
186 GetManifestResourceStream(names[i]))
188 return new Icon(stream);
198 /// Set our current client.
199 /// This will take care of applying our client layout and set data fields.
201 /// <param name="aSessionId"></param>
202 void SetCurrentClient(string aSessionId)
204 if (aSessionId == iCurrentClientSessionId)
206 //Given client is already the current one.
207 //Don't bother changing anything then.
211 //Set current client ID.
212 iCurrentClientSessionId = aSessionId;
213 //Fetch and set current client data.
214 iCurrentClientData = iClients[aSessionId];
215 //Apply layout and set data fields.
216 UpdateTableLayoutPanel(iCurrentClientData);
219 private void buttonFont_Click(object sender, EventArgs e)
221 //fontDialog.ShowColor = true;
222 //fontDialog.ShowApply = true;
223 fontDialog.ShowEffects = true;
224 MarqueeLabel label = (MarqueeLabel)tableLayoutPanel.Controls[0];
225 fontDialog.Font = label.Font;
227 fontDialog.FixedPitchOnly = checkBoxFixedPitchFontOnly.Checked;
229 //fontDialog.ShowHelp = true;
231 //fontDlg.MaxSize = 40;
232 //fontDlg.MinSize = 22;
234 //fontDialog.Parent = this;
235 //fontDialog.StartPosition = FormStartPosition.CenterParent;
237 //DlgBox.ShowDialog(fontDialog);
239 //if (fontDialog.ShowDialog(this) != DialogResult.Cancel)
240 if (DlgBox.ShowDialog(fontDialog) != DialogResult.Cancel)
243 //MsgBox.Show("MessageBox MsgBox", "MsgBox caption");
245 //MessageBox.Show("Ok");
246 foreach (MarqueeLabel ctrl in tableLayoutPanel.Controls)
248 ctrl.Font = fontDialog.Font;
250 cds.Font = fontDialog.Font;
251 Properties.Settings.Default.Save();
260 void CheckFontHeight()
262 //Show font height and width
263 labelFontHeight.Text = "Font height: " + cds.Font.Height;
264 float charWidth = IsFixedWidth(cds.Font);
265 if (charWidth == 0.0f)
267 labelFontWidth.Visible = false;
271 labelFontWidth.Visible = true;
272 labelFontWidth.Text = "Font width: " + charWidth;
275 MarqueeLabel label = null;
276 //Get the first label control we can find
277 foreach (Control ctrl in tableLayoutPanel.Controls)
279 if (ctrl is MarqueeLabel)
281 label = (MarqueeLabel)ctrl;
286 //Now check font height and show a warning if needed.
287 if (label != null && label.Font.Height > label.Height)
289 labelWarning.Text = "WARNING: Selected font is too height by " + (label.Font.Height - label.Height) + " pixels!";
290 labelWarning.Visible = true;
294 labelWarning.Visible = false;
299 private void buttonCapture_Click(object sender, EventArgs e)
301 System.Drawing.Bitmap bmp = new System.Drawing.Bitmap(tableLayoutPanel.Width, tableLayoutPanel.Height);
302 tableLayoutPanel.DrawToBitmap(bmp, tableLayoutPanel.ClientRectangle);
303 //Bitmap bmpToSave = new Bitmap(bmp);
304 bmp.Save("D:\\capture.png");
306 ((MarqueeLabel)tableLayoutPanel.Controls[0]).Text = "Captured";
309 string outputFileName = "d:\\capture.png";
310 using (MemoryStream memory = new MemoryStream())
312 using (FileStream fs = new FileStream(outputFileName, FileMode.OpenOrCreate, FileAccess.ReadWrite))
314 bmp.Save(memory, System.Drawing.Imaging.ImageFormat.Png);
315 byte[] bytes = memory.ToArray();
316 fs.Write(bytes, 0, bytes.Length);
323 private void CheckForRequestResults()
325 if (iDisplay.IsRequestPending())
327 switch (iDisplay.AttemptRequestCompletion())
329 case Display.TMiniDisplayRequest.EMiniDisplayRequestFirmwareRevision:
330 toolStripStatusLabelConnect.Text += " v" + iDisplay.FirmwareRevision();
331 //Issue next request then
332 iDisplay.RequestPowerSupplyStatus();
335 case Display.TMiniDisplayRequest.EMiniDisplayRequestPowerSupplyStatus:
336 if (iDisplay.PowerSupplyStatus())
338 toolStripStatusLabelPower.Text = "ON";
342 toolStripStatusLabelPower.Text = "OFF";
344 //Issue next request then
345 iDisplay.RequestDeviceId();
348 case Display.TMiniDisplayRequest.EMiniDisplayRequestDeviceId:
349 toolStripStatusLabelConnect.Text += " - " + iDisplay.DeviceId();
350 //No more request to issue
356 public static uint ColorWhiteIsOn(int aX, int aY, uint aPixel)
358 if ((aPixel & 0x00FFFFFF) == 0x00FFFFFF)
365 public static uint ColorUntouched(int aX, int aY, uint aPixel)
370 public static uint ColorInversed(int aX, int aY, uint aPixel)
375 public static uint ColorChessboard(int aX, int aY, uint aPixel)
377 if ((aX % 2 == 0) && (aY % 2 == 0))
381 else if ((aX % 2 != 0) && (aY % 2 != 0))
389 public static int ScreenReversedX(System.Drawing.Bitmap aBmp, int aX)
391 return aBmp.Width - aX - 1;
394 public int ScreenReversedY(System.Drawing.Bitmap aBmp, int aY)
396 return iBmp.Height - aY - 1;
399 public int ScreenX(System.Drawing.Bitmap aBmp, int aX)
404 public int ScreenY(System.Drawing.Bitmap aBmp, int aY)
410 /// Select proper pixel delegates according to our current settings.
412 private void SetupPixelDelegates()
414 //Select our pixel processing routine
415 if (cds.InverseColors)
417 //iColorFx = ColorChessboard;
418 iColorFx = ColorInversed;
422 iColorFx = ColorWhiteIsOn;
425 //Select proper coordinate translation functions
426 //We used delegate/function pointer to support reverse screen without doing an extra test on each pixels
427 if (cds.ReverseScreen)
429 iScreenX = ScreenReversedX;
430 iScreenY = ScreenReversedY;
440 //This is our timer tick responsible to perform our render
441 private void timer_Tick(object sender, EventArgs e)
443 //Update our animations
444 DateTime NewTickTime = DateTime.Now;
446 //Update animation for all our marquees
447 foreach (Control ctrl in tableLayoutPanel.Controls)
449 if (ctrl is MarqueeLabel)
451 ((MarqueeLabel)ctrl).UpdateAnimation(LastTickTime, NewTickTime);
457 if (iDisplay.IsOpen())
459 CheckForRequestResults();
464 iBmp = new System.Drawing.Bitmap(tableLayoutPanel.Width, tableLayoutPanel.Height, PixelFormat.Format32bppArgb);
466 tableLayoutPanel.DrawToBitmap(iBmp, tableLayoutPanel.ClientRectangle);
467 //iBmp.Save("D:\\capture.png");
469 //Send it to our display
470 for (int i = 0; i < iBmp.Width; i++)
472 for (int j = 0; j < iBmp.Height; j++)
476 //Get our processed pixel coordinates
477 int x = iScreenX(iBmp, i);
478 int y = iScreenY(iBmp, j);
480 uint color = (uint)iBmp.GetPixel(i, j).ToArgb();
481 //Apply color effects
482 color = iColorFx(x,y,color);
484 iDisplay.SetPixel(x, y, color);
489 iDisplay.SwapBuffers();
493 //Compute instant FPS
494 toolStripStatusLabelFps.Text = (1.0/NewTickTime.Subtract(LastTickTime).TotalSeconds).ToString("F0") + " / " + (1000/timer.Interval).ToString() + " FPS";
496 LastTickTime = NewTickTime;
500 private void OpenDisplayConnection()
502 CloseDisplayConnection();
504 if (iDisplay.Open((Display.TMiniDisplayType)cds.DisplayType))
507 iDisplay.RequestFirmwareRevision();
512 toolStripStatusLabelConnect.Text = "Connection error";
516 private void CloseDisplayConnection()
522 private void buttonOpen_Click(object sender, EventArgs e)
524 OpenDisplayConnection();
527 private void buttonClose_Click(object sender, EventArgs e)
529 CloseDisplayConnection();
532 private void buttonClear_Click(object sender, EventArgs e)
535 iDisplay.SwapBuffers();
538 private void buttonFill_Click(object sender, EventArgs e)
541 iDisplay.SwapBuffers();
544 private void trackBarBrightness_Scroll(object sender, EventArgs e)
546 cds.Brightness = trackBarBrightness.Value;
547 Properties.Settings.Default.Save();
548 iDisplay.SetBrightness(trackBarBrightness.Value);
554 /// CDS stands for Current Display Settings
556 private DisplaySettings cds
560 DisplaysSettings settings = Properties.Settings.Default.DisplaysSettings;
561 if (settings == null)
563 settings = new DisplaysSettings();
565 Properties.Settings.Default.DisplaysSettings = settings;
568 //Make sure all our settings have been created
569 while (settings.Displays.Count <= Properties.Settings.Default.CurrentDisplayIndex)
571 settings.Displays.Add(new DisplaySettings());
574 DisplaySettings displaySettings = settings.Displays[Properties.Settings.Default.CurrentDisplayIndex];
575 return displaySettings;
580 /// Check if the given font has a fixed character pitch.
582 /// <param name="ft"></param>
583 /// <returns>0.0f if this is not a monospace font, otherwise returns the character width.</returns>
584 public float IsFixedWidth(Font ft)
586 Graphics g = CreateGraphics();
587 char[] charSizes = new char[] { 'i', 'a', 'Z', '%', '#', 'a', 'B', 'l', 'm', ',', '.' };
588 float charWidth = g.MeasureString("I", ft, Int32.MaxValue, StringFormat.GenericTypographic).Width;
590 bool fixedWidth = true;
592 foreach (char c in charSizes)
593 if (g.MeasureString(c.ToString(), ft, Int32.MaxValue, StringFormat.GenericTypographic).Width != charWidth)
604 private void UpdateStatus()
606 //Synchronize UI with settings
608 checkBoxShowBorders.Checked = cds.ShowBorders;
609 tableLayoutPanel.CellBorderStyle = (cds.ShowBorders ? TableLayoutPanelCellBorderStyle.Single : TableLayoutPanelCellBorderStyle.None);
611 //Set the proper font to each of our labels
612 foreach (MarqueeLabel ctrl in tableLayoutPanel.Controls)
614 ctrl.Font = cds.Font;
618 //Check if "run on Windows startup" is enabled
619 checkBoxAutoStart.Checked = iStartupManager.Startup;
621 checkBoxConnectOnStartup.Checked = Properties.Settings.Default.DisplayConnectOnStartup;
622 checkBoxMinimizeToTray.Checked = Properties.Settings.Default.MinimizeToTray;
623 checkBoxStartMinimized.Checked = Properties.Settings.Default.StartMinimized;
624 checkBoxReverseScreen.Checked = cds.ReverseScreen;
625 checkBoxInverseColors.Checked = cds.InverseColors;
626 comboBoxDisplayType.SelectedIndex = cds.DisplayType;
627 timer.Interval = cds.TimerInterval;
628 maskedTextBoxTimerInterval.Text = cds.TimerInterval.ToString();
630 SetupPixelDelegates();
632 if (iDisplay.IsOpen())
634 //Only setup brightness if display is open
635 trackBarBrightness.Minimum = iDisplay.MinBrightness();
636 trackBarBrightness.Maximum = iDisplay.MaxBrightness();
637 trackBarBrightness.Value = cds.Brightness;
638 trackBarBrightness.LargeChange = Math.Max(1, (iDisplay.MaxBrightness() - iDisplay.MinBrightness()) / 5);
639 trackBarBrightness.SmallChange = 1;
640 iDisplay.SetBrightness(cds.Brightness);
642 buttonFill.Enabled = true;
643 buttonClear.Enabled = true;
644 buttonOpen.Enabled = false;
645 buttonClose.Enabled = true;
646 trackBarBrightness.Enabled = true;
647 toolStripStatusLabelConnect.Text = "Connected - " + iDisplay.Vendor() + " - " + iDisplay.Product();
648 //+ " - " + iDisplay.SerialNumber();
650 if (iDisplay.SupportPowerOnOff())
652 buttonPowerOn.Enabled = true;
653 buttonPowerOff.Enabled = true;
657 buttonPowerOn.Enabled = false;
658 buttonPowerOff.Enabled = false;
661 if (iDisplay.SupportClock())
663 buttonShowClock.Enabled = true;
664 buttonHideClock.Enabled = true;
668 buttonShowClock.Enabled = false;
669 buttonHideClock.Enabled = false;
674 buttonFill.Enabled = false;
675 buttonClear.Enabled = false;
676 buttonOpen.Enabled = true;
677 buttonClose.Enabled = false;
678 trackBarBrightness.Enabled = false;
679 buttonPowerOn.Enabled = false;
680 buttonPowerOff.Enabled = false;
681 buttonShowClock.Enabled = false;
682 buttonHideClock.Enabled = false;
683 toolStripStatusLabelConnect.Text = "Disconnected";
684 toolStripStatusLabelPower.Text = "N/A";
690 private void checkBoxShowBorders_CheckedChanged(object sender, EventArgs e)
692 //Save our show borders setting
693 tableLayoutPanel.CellBorderStyle = (checkBoxShowBorders.Checked ? TableLayoutPanelCellBorderStyle.Single : TableLayoutPanelCellBorderStyle.None);
694 cds.ShowBorders = checkBoxShowBorders.Checked;
695 Properties.Settings.Default.Save();
699 private void checkBoxConnectOnStartup_CheckedChanged(object sender, EventArgs e)
701 //Save our connect on startup setting
702 Properties.Settings.Default.DisplayConnectOnStartup = checkBoxConnectOnStartup.Checked;
703 Properties.Settings.Default.Save();
706 private void checkBoxMinimizeToTray_CheckedChanged(object sender, EventArgs e)
708 //Save our "Minimize to tray" setting
709 Properties.Settings.Default.MinimizeToTray = checkBoxMinimizeToTray.Checked;
710 Properties.Settings.Default.Save();
714 private void checkBoxStartMinimized_CheckedChanged(object sender, EventArgs e)
716 //Save our "Start minimized" setting
717 Properties.Settings.Default.StartMinimized = checkBoxStartMinimized.Checked;
718 Properties.Settings.Default.Save();
721 private void checkBoxAutoStart_CheckedChanged(object sender, EventArgs e)
723 iStartupManager.Startup = checkBoxAutoStart.Checked;
727 private void checkBoxReverseScreen_CheckedChanged(object sender, EventArgs e)
729 //Save our reverse screen setting
730 cds.ReverseScreen = checkBoxReverseScreen.Checked;
731 Properties.Settings.Default.Save();
732 SetupPixelDelegates();
735 private void checkBoxInverseColors_CheckedChanged(object sender, EventArgs e)
737 //Save our inverse colors setting
738 cds.InverseColors = checkBoxInverseColors.Checked;
739 Properties.Settings.Default.Save();
740 SetupPixelDelegates();
743 private void MainForm_Resize(object sender, EventArgs e)
745 if (WindowState == FormWindowState.Minimized)
748 //iBmp = new System.Drawing.Bitmap(tableLayoutPanel.Width, tableLayoutPanel.Height, PixelFormat.Format32bppArgb);
749 iCreateBitmap = true;
753 private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
759 public void StartServer()
761 iServiceHost = new ServiceHost
764 new Uri[] { new Uri("net.tcp://localhost:8001/") }
767 iServiceHost.AddServiceEndpoint(typeof(IService), new NetTcpBinding(SecurityMode.None, true), "DisplayService");
771 public void StopServer()
773 if (iClients.Count > 0 && !iClosing)
777 BroadcastCloseEvent();
781 if (MessageBox.Show("Force exit?", "Waiting for clients...", MessageBoxButtons.YesNo, MessageBoxIcon.Warning) == DialogResult.Yes)
783 iClosing = false; //We make sure we force close if asked twice
788 //We removed that as it often lags for some reason
789 //iServiceHost.Close();
793 public void BroadcastCloseEvent()
795 Trace.TraceInformation("BroadcastCloseEvent - start");
797 var inactiveClients = new List<string>();
798 foreach (var client in iClients)
800 //if (client.Key != eventData.ClientName)
804 Trace.TraceInformation("BroadcastCloseEvent - " + client.Key);
805 client.Value.Callback.OnCloseOrder(/*eventData*/);
809 inactiveClients.Add(client.Key);
814 if (inactiveClients.Count > 0)
816 foreach (var client in inactiveClients)
818 iClients.Remove(client);
819 Program.iMainForm.treeViewClients.Nodes.Remove(Program.iMainForm.treeViewClients.Nodes.Find(client, false)[0]);
823 if (iClients.Count==0)
830 /// Just remove all our fields.
832 private void ClearLayout()
834 tableLayoutPanel.Controls.Clear();
835 tableLayoutPanel.RowStyles.Clear();
836 tableLayoutPanel.ColumnStyles.Clear();
839 private void buttonStartClient_Click(object sender, EventArgs e)
841 Thread clientThread = new Thread(SharpDisplayClient.Program.Main);
842 clientThread.Start();
846 private void buttonSuspend_Click(object sender, EventArgs e)
848 LastTickTime = DateTime.Now; //Reset timer to prevent jump
849 timer.Enabled = !timer.Enabled;
852 buttonSuspend.Text = "Run";
856 buttonSuspend.Text = "Pause";
860 private void buttonCloseClients_Click(object sender, EventArgs e)
862 BroadcastCloseEvent();
865 private void treeViewClients_AfterSelect(object sender, TreeViewEventArgs e)
874 /// <param name="aSessionId"></param>
875 /// <param name="aCallback"></param>
876 public void AddClientThreadSafe(string aSessionId, ICallback aCallback)
878 if (this.InvokeRequired)
880 //Not in the proper thread, invoke ourselves
881 AddClientDelegate d = new AddClientDelegate(AddClientThreadSafe);
882 this.Invoke(d, new object[] { aSessionId, aCallback });
886 //We are in the proper thread
887 //Add this session to our collection of clients
888 ClientData newClient = new ClientData(aSessionId, aCallback);
889 Program.iMainForm.iClients.Add(aSessionId, newClient);
890 //Add this session to our UI
891 UpdateClientTreeViewNode(newClient);
898 /// <param name="aSessionId"></param>
899 public void RemoveClientThreadSafe(string aSessionId)
901 if (this.InvokeRequired)
903 //Not in the proper thread, invoke ourselves
904 RemoveClientDelegate d = new RemoveClientDelegate(RemoveClientThreadSafe);
905 this.Invoke(d, new object[] { aSessionId });
909 //We are in the proper thread
910 //Remove this session from both client collection and UI tree view
911 if (Program.iMainForm.iClients.Keys.Contains(aSessionId))
913 Program.iMainForm.iClients.Remove(aSessionId);
914 Program.iMainForm.treeViewClients.Nodes.Remove(Program.iMainForm.treeViewClients.Nodes.Find(aSessionId, false)[0]);
917 if (iClients.Count == 0)
919 //Clear our screen when last client disconnects
924 //We were closing our form
925 //All clients are now closed
926 //Just resume our close operation
937 /// <param name="aSessionId"></param>
938 /// <param name="aLayout"></param>
939 public void SetClientLayoutThreadSafe(string aSessionId, TableLayout aLayout)
941 if (this.InvokeRequired)
943 //Not in the proper thread, invoke ourselves
944 SetLayoutDelegate d = new SetLayoutDelegate(SetClientLayoutThreadSafe);
945 this.Invoke(d, new object[] { aSessionId, aLayout });
949 ClientData client = iClients[aSessionId];
952 client.Layout = aLayout;
953 UpdateTableLayoutPanel(client);
955 UpdateClientTreeViewNode(client);
963 /// <param name="aSessionId"></param>
964 /// <param name="aField"></param>
965 public void SetClientFieldThreadSafe(string aSessionId, DataField aField)
967 if (this.InvokeRequired)
969 //Not in the proper thread, invoke ourselves
970 SetFieldDelegate d = new SetFieldDelegate(SetClientFieldThreadSafe);
971 this.Invoke(d, new object[] { aSessionId, aField });
975 //We are in the proper thread
976 //Call the non-thread-safe variant
977 SetClientField(aSessionId, aField);
984 /// <param name="aSessionId"></param>
985 /// <param name="aField"></param>
986 private void SetClientField(string aSessionId, DataField aField)
988 SetCurrentClient(aSessionId);
989 ClientData client = iClients[aSessionId];
992 bool somethingChanged = false;
994 //Make sure all our fields are in place
995 while (client.Fields.Count < (aField.Index + 1))
997 //Add a text field with proper index
998 client.Fields.Add(new DataField(client.Fields.Count));
999 somethingChanged = true;
1002 if (client.Fields[aField.Index].IsSameLayout(aField))
1004 //Same layout just update our field
1005 client.Fields[aField.Index] = aField;
1007 if (aField.IsText && tableLayoutPanel.Controls[aField.Index] is MarqueeLabel)
1009 //Text field control already in place, just change the text
1010 MarqueeLabel label = (MarqueeLabel)tableLayoutPanel.Controls[aField.Index];
1011 somethingChanged = (label.Text != aField.Text || label.TextAlign != aField.Alignment);
1012 label.Text = aField.Text;
1013 label.TextAlign = aField.Alignment;
1015 else if (aField.IsBitmap && tableLayoutPanel.Controls[aField.Index] is PictureBox)
1017 somethingChanged = true; //TODO: Bitmap comp or should we leave that to clients?
1018 //Bitmap field control already in place just change the bitmap
1019 PictureBox pictureBox = (PictureBox)tableLayoutPanel.Controls[aField.Index];
1020 pictureBox.Image = aField.Bitmap;
1024 somethingChanged = true;
1025 //The requested control in our layout it not of the correct type
1026 //Wrong control type, re-create them all
1027 UpdateTableLayoutPanel(iCurrentClientData);
1032 somethingChanged = true;
1033 //Different layout, need to rebuild it
1034 client.Fields[aField.Index] = aField;
1035 UpdateTableLayoutPanel(iCurrentClientData);
1039 if (somethingChanged)
1041 UpdateClientTreeViewNode(client);
1049 /// <param name="aTexts"></param>
1050 public void SetClientFieldsThreadSafe(string aSessionId, System.Collections.Generic.IList<DataField> aFields)
1052 if (this.InvokeRequired)
1054 //Not in the proper thread, invoke ourselves
1055 SetFieldsDelegate d = new SetFieldsDelegate(SetClientFieldsThreadSafe);
1056 this.Invoke(d, new object[] { aSessionId, aFields });
1060 //Put each our text fields in a label control
1061 foreach (DataField field in aFields)
1063 SetClientField(aSessionId, field);
1071 /// <param name="aSessionId"></param>
1072 /// <param name="aName"></param>
1073 public void SetClientNameThreadSafe(string aSessionId, string aName)
1075 if (this.InvokeRequired)
1077 //Not in the proper thread, invoke ourselves
1078 SetClientNameDelegate d = new SetClientNameDelegate(SetClientNameThreadSafe);
1079 this.Invoke(d, new object[] { aSessionId, aName });
1083 //We are in the proper thread
1085 ClientData client = iClients[aSessionId];
1089 client.Name = aName;
1090 //Update our tree-view
1091 UpdateClientTreeViewNode(client);
1099 /// <param name="aClient"></param>
1100 private void UpdateClientTreeViewNode(ClientData aClient)
1102 if (aClient == null)
1107 TreeNode node = null;
1108 //Check that our client node already exists
1109 //Get our client root node using its key which is our session ID
1110 TreeNode[] nodes = treeViewClients.Nodes.Find(aClient.SessionId, false);
1111 if (nodes.Count()>0)
1113 //We already have a node for that client
1115 //Clear children as we are going to recreate them below
1120 //Client node does not exists create a new one
1121 treeViewClients.Nodes.Add(aClient.SessionId, aClient.SessionId);
1122 node = treeViewClients.Nodes.Find(aClient.SessionId, false)[0];
1128 if (aClient.Name != "")
1130 //We have a name, us it as text for our root node
1131 node.Text = aClient.Name;
1132 //Add a child with SessionId
1133 node.Nodes.Add(new TreeNode(aClient.SessionId));
1137 //No name, use session ID instead
1138 node.Text = aClient.SessionId;
1141 if (aClient.Fields.Count > 0)
1143 //Create root node for our texts
1144 TreeNode textsRoot = new TreeNode("Fields");
1145 node.Nodes.Add(textsRoot);
1146 //For each text add a new entry
1147 foreach (DataField field in aClient.Fields)
1149 if (!field.IsBitmap)
1151 DataField textField = (DataField)field;
1152 textsRoot.Nodes.Add(new TreeNode("[Text]" + textField.Text));
1156 textsRoot.Nodes.Add(new TreeNode("[Bitmap]"));
1165 private void buttonAddRow_Click(object sender, EventArgs e)
1167 if (tableLayoutPanel.RowCount < 6)
1169 UpdateTableLayoutPanel(tableLayoutPanel.ColumnCount, tableLayoutPanel.RowCount + 1);
1173 private void buttonRemoveRow_Click(object sender, EventArgs e)
1175 if (tableLayoutPanel.RowCount > 1)
1177 UpdateTableLayoutPanel(tableLayoutPanel.ColumnCount, tableLayoutPanel.RowCount - 1);
1180 UpdateTableLayoutRowStyles();
1183 private void buttonAddColumn_Click(object sender, EventArgs e)
1185 if (tableLayoutPanel.ColumnCount < 8)
1187 UpdateTableLayoutPanel(tableLayoutPanel.ColumnCount + 1, tableLayoutPanel.RowCount);
1191 private void buttonRemoveColumn_Click(object sender, EventArgs e)
1193 if (tableLayoutPanel.ColumnCount > 1)
1195 UpdateTableLayoutPanel(tableLayoutPanel.ColumnCount - 1, tableLayoutPanel.RowCount);
1201 /// Update our table layout row styles to make sure each rows have similar height
1203 private void UpdateTableLayoutRowStyles()
1205 foreach (RowStyle rowStyle in tableLayoutPanel.RowStyles)
1207 rowStyle.SizeType = SizeType.Percent;
1208 rowStyle.Height = 100 / tableLayoutPanel.RowCount;
1214 /// Empty and recreate our table layout with the given number of columns and rows.
1215 /// Sizes of rows and columns are uniform.
1217 /// <param name="aColumn"></param>
1218 /// <param name="aRow"></param>
1219 private void UpdateTableLayoutPanel(int aColumn, int aRow)
1221 tableLayoutPanel.Controls.Clear();
1222 tableLayoutPanel.RowStyles.Clear();
1223 tableLayoutPanel.ColumnStyles.Clear();
1224 tableLayoutPanel.RowCount = 0;
1225 tableLayoutPanel.ColumnCount = 0;
1227 while (tableLayoutPanel.RowCount < aRow)
1229 tableLayoutPanel.RowCount++;
1232 while (tableLayoutPanel.ColumnCount < aColumn)
1234 tableLayoutPanel.ColumnCount++;
1237 for (int i = 0; i < tableLayoutPanel.ColumnCount; i++)
1239 //Create our column styles
1240 this.tableLayoutPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100 / tableLayoutPanel.ColumnCount));
1242 for (int j = 0; j < tableLayoutPanel.RowCount; j++)
1246 //Create our row styles
1247 this.tableLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100 / tableLayoutPanel.RowCount));
1250 MarqueeLabel control = new SharpDisplayManager.MarqueeLabel();
1251 control.AutoEllipsis = true;
1252 control.AutoSize = true;
1253 control.BackColor = System.Drawing.Color.Transparent;
1254 control.Dock = System.Windows.Forms.DockStyle.Fill;
1255 control.Location = new System.Drawing.Point(1, 1);
1256 control.Margin = new System.Windows.Forms.Padding(0);
1257 control.Name = "marqueeLabelCol" + aColumn + "Row" + aRow;
1258 control.OwnTimer = false;
1259 control.PixelsPerSecond = 64;
1260 control.Separator = "|";
1261 //control.Size = new System.Drawing.Size(254, 30);
1262 //control.TabIndex = 2;
1263 control.Font = cds.Font;
1264 control.Text = "ABCDEFGHIJKLMNOPQRST-0123456789";
1265 control.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
1266 control.UseCompatibleTextRendering = true;
1268 tableLayoutPanel.Controls.Add(control, i, j);
1277 /// Update our display table layout.
1279 /// <param name="aLayout"></param>
1280 private void UpdateTableLayoutPanel(ClientData aClient)
1282 TableLayout layout = aClient.Layout;
1285 tableLayoutPanel.Controls.Clear();
1286 tableLayoutPanel.RowStyles.Clear();
1287 tableLayoutPanel.ColumnStyles.Clear();
1288 tableLayoutPanel.RowCount = 0;
1289 tableLayoutPanel.ColumnCount = 0;
1291 while (tableLayoutPanel.RowCount < layout.Rows.Count)
1293 tableLayoutPanel.RowCount++;
1296 while (tableLayoutPanel.ColumnCount < layout.Columns.Count)
1298 tableLayoutPanel.ColumnCount++;
1301 for (int i = 0; i < tableLayoutPanel.ColumnCount; i++)
1303 //Create our column styles
1304 this.tableLayoutPanel.ColumnStyles.Add(layout.Columns[i]);
1306 for (int j = 0; j < tableLayoutPanel.RowCount; j++)
1310 //Create our row styles
1311 this.tableLayoutPanel.RowStyles.Add(layout.Rows[j]);
1314 //Check if we already have a control
1315 Control existingControl = tableLayoutPanel.GetControlFromPosition(i,j);
1316 if (existingControl!=null)
1318 //We already have a control in that cell as a results of row/col spanning
1319 //Move on to next cell then
1325 //Check if a client field already exists for that cell
1326 if (aClient.Fields.Count <= tableLayoutPanel.Controls.Count)
1328 //No client field specified, create a text field by default
1329 aClient.Fields.Add(new DataField(aClient.Fields.Count));
1332 //Create a control corresponding to the field specified for that cell
1333 DataField field = aClient.Fields[tableLayoutPanel.Controls.Count];
1334 Control control = CreateControlForDataField(field);
1336 //Add newly created control to our table layout at the specified row and column
1337 tableLayoutPanel.Controls.Add(control, i, j);
1338 //Make sure we specify row and column span for that new control
1339 tableLayoutPanel.SetRowSpan(control,field.RowSpan);
1340 tableLayoutPanel.SetColumnSpan(control, field.ColumnSpan);
1345 while (aClient.Fields.Count > fieldCount)
1347 //We have too much fields for this layout
1348 //Just discard them until we get there
1349 aClient.Fields.RemoveAt(aClient.Fields.Count-1);
1356 /// Check our type of data field and create corresponding control
1358 /// <param name="aField"></param>
1359 private Control CreateControlForDataField(DataField aField)
1361 Control control=null;
1362 if (!aField.IsBitmap)
1364 MarqueeLabel label = new SharpDisplayManager.MarqueeLabel();
1365 label.AutoEllipsis = true;
1366 label.AutoSize = true;
1367 label.BackColor = System.Drawing.Color.Transparent;
1368 label.Dock = System.Windows.Forms.DockStyle.Fill;
1369 label.Location = new System.Drawing.Point(1, 1);
1370 label.Margin = new System.Windows.Forms.Padding(0);
1371 label.Name = "marqueeLabel" + aField.Index;
1372 label.OwnTimer = false;
1373 label.PixelsPerSecond = 64;
1374 label.Separator = "|";
1375 //control.Size = new System.Drawing.Size(254, 30);
1376 //control.TabIndex = 2;
1377 label.Font = cds.Font;
1379 label.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
1380 label.UseCompatibleTextRendering = true;
1381 label.Text = aField.Text;
1387 //Create picture box
1388 PictureBox picture = new PictureBox();
1389 picture.AutoSize = true;
1390 picture.BackColor = System.Drawing.Color.Transparent;
1391 picture.Dock = System.Windows.Forms.DockStyle.Fill;
1392 picture.Location = new System.Drawing.Point(1, 1);
1393 picture.Margin = new System.Windows.Forms.Padding(0);
1394 picture.Name = "pictureBox" + aField;
1396 picture.Image = aField.Bitmap;
1405 private void buttonAlignLeft_Click(object sender, EventArgs e)
1407 foreach (MarqueeLabel ctrl in tableLayoutPanel.Controls)
1409 ctrl.TextAlign = ContentAlignment.MiddleLeft;
1413 private void buttonAlignCenter_Click(object sender, EventArgs e)
1415 foreach (MarqueeLabel ctrl in tableLayoutPanel.Controls)
1417 ctrl.TextAlign = ContentAlignment.MiddleCenter;
1421 private void buttonAlignRight_Click(object sender, EventArgs e)
1423 foreach (MarqueeLabel ctrl in tableLayoutPanel.Controls)
1425 ctrl.TextAlign = ContentAlignment.MiddleRight;
1429 private void comboBoxDisplayType_SelectedIndexChanged(object sender, EventArgs e)
1431 Properties.Settings.Default.CurrentDisplayIndex = comboBoxDisplayType.SelectedIndex;
1432 cds.DisplayType = comboBoxDisplayType.SelectedIndex;
1433 Properties.Settings.Default.Save();
1434 if (iDisplay.IsOpen())
1436 OpenDisplayConnection();
1445 private void maskedTextBoxTimerInterval_TextChanged(object sender, EventArgs e)
1447 if (maskedTextBoxTimerInterval.Text != "")
1449 int interval = Convert.ToInt32(maskedTextBoxTimerInterval.Text);
1453 timer.Interval = interval;
1454 cds.TimerInterval = timer.Interval;
1455 Properties.Settings.Default.Save();
1460 private void buttonPowerOn_Click(object sender, EventArgs e)
1465 private void buttonPowerOff_Click(object sender, EventArgs e)
1467 iDisplay.PowerOff();
1470 private void buttonShowClock_Click(object sender, EventArgs e)
1472 iDisplay.ShowClock();
1475 private void buttonHideClock_Click(object sender, EventArgs e)
1477 iDisplay.HideClock();
1480 private void buttonUpdate_Click(object sender, EventArgs e)
1482 InstallUpdateSyncWithInfo();
1486 private void InstallUpdateSyncWithInfo()
1488 UpdateCheckInfo info = null;
1490 if (ApplicationDeployment.IsNetworkDeployed)
1492 ApplicationDeployment ad = ApplicationDeployment.CurrentDeployment;
1496 info = ad.CheckForDetailedUpdate();
1499 catch (DeploymentDownloadException dde)
1501 MessageBox.Show("The new version of the application cannot be downloaded at this time. \n\nPlease check your network connection, or try again later. Error: " + dde.Message);
1504 catch (InvalidDeploymentException ide)
1506 MessageBox.Show("Cannot check for a new version of the application. The ClickOnce deployment is corrupt. Please redeploy the application and try again. Error: " + ide.Message);
1509 catch (InvalidOperationException ioe)
1511 MessageBox.Show("This application cannot be updated. It is likely not a ClickOnce application. Error: " + ioe.Message);
1515 if (info.UpdateAvailable)
1517 Boolean doUpdate = true;
1519 if (!info.IsUpdateRequired)
1521 DialogResult dr = MessageBox.Show("An update is available. Would you like to update the application now?", "Update Available", MessageBoxButtons.OKCancel);
1522 if (!(DialogResult.OK == dr))
1529 // Display a message that the app MUST reboot. Display the minimum required version.
1530 MessageBox.Show("This application has detected a mandatory update from your current " +
1531 "version to version " + info.MinimumRequiredVersion.ToString() +
1532 ". The application will now install the update and restart.",
1533 "Update Available", MessageBoxButtons.OK,
1534 MessageBoxIcon.Information);
1542 MessageBox.Show("The application has been upgraded, and will now restart.");
1543 Application.Restart();
1545 catch (DeploymentDownloadException dde)
1547 MessageBox.Show("Cannot install the latest version of the application. \n\nPlease check your network connection, or try again later. Error: " + dde);
1554 MessageBox.Show("You are already running the latest version.", "Application up-to-date");
1563 private void SysTrayHideShow()
1569 WindowState = FormWindowState.Normal;
1574 /// Use to handle minimize events.
1576 /// <param name="sender"></param>
1577 /// <param name="e"></param>
1578 private void MainForm_SizeChanged(object sender, EventArgs e)
1580 if (WindowState == FormWindowState.Minimized && Properties.Settings.Default.MinimizeToTray)
1593 /// A UI thread copy of a client relevant data.
1594 /// Keeping this copy in the UI thread helps us deal with threading issues.
1596 public class ClientData
1598 public ClientData(string aSessionId, ICallback aCallback)
1600 SessionId = aSessionId;
1602 Fields = new List<DataField>();
1603 Layout = new TableLayout(1, 2); //Default to one column and two rows
1604 Callback = aCallback;
1607 public string SessionId { get; set; }
1608 public string Name { get; set; }
1609 public List<DataField> Fields { get; set; }
1610 public TableLayout Layout { get; set; }
1611 public ICallback Callback { get; set; }