Setting layout size according to display.
Added comments.
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 //Open display connection on start-up if needed
112 if (Properties.Settings.Default.DisplayConnectOnStartup)
114 OpenDisplayConnection();
117 //Setup notification icon
120 // To make sure start up with minimize to tray works
121 if (WindowState == FormWindowState.Minimized && Properties.Settings.Default.MinimizeToTray)
127 //When not debugging we want the screen to be empty until a client takes over
135 private void SetupTrayIcon()
137 iNotifyIcon.Icon = GetIcon("vfd.ico");
138 iNotifyIcon.Text = "Sharp Display Manager";
139 iNotifyIcon.Visible = true;
141 //Double click toggles visibility - typically brings up the application
142 iNotifyIcon.DoubleClick += delegate(object obj, EventArgs args)
147 //Adding a context menu, useful to be able to exit the application
148 ContextMenu contextMenu = new ContextMenu();
149 //Context menu item to toggle visibility
150 MenuItem hideShowItem = new MenuItem("Hide/Show");
151 hideShowItem.Click += delegate(object obj, EventArgs args)
155 contextMenu.MenuItems.Add(hideShowItem);
157 //Context menu item separator
158 contextMenu.MenuItems.Add(new MenuItem("-"));
160 //Context menu exit item
161 MenuItem exitItem = new MenuItem("Exit");
162 exitItem.Click += delegate(object obj, EventArgs args)
166 contextMenu.MenuItems.Add(exitItem);
168 iNotifyIcon.ContextMenu = contextMenu;
172 /// Access icons from embedded resources.
174 /// <param name="name"></param>
175 /// <returns></returns>
176 public static Icon GetIcon(string name)
178 name = "SharpDisplayManager.Resources." + name;
181 Assembly.GetExecutingAssembly().GetManifestResourceNames();
182 for (int i = 0; i < names.Length; i++)
184 if (names[i].Replace('\\', '.') == name)
186 using (Stream stream = Assembly.GetExecutingAssembly().
187 GetManifestResourceStream(names[i]))
189 return new Icon(stream);
199 /// Set our current client.
200 /// This will take care of applying our client layout and set data fields.
202 /// <param name="aSessionId"></param>
203 void SetCurrentClient(string aSessionId)
205 if (aSessionId == iCurrentClientSessionId)
207 //Given client is already the current one.
208 //Don't bother changing anything then.
212 //Set current client ID.
213 iCurrentClientSessionId = aSessionId;
214 //Fetch and set current client data.
215 iCurrentClientData = iClients[aSessionId];
216 //Apply layout and set data fields.
217 UpdateTableLayoutPanel(iCurrentClientData);
220 private void buttonFont_Click(object sender, EventArgs e)
222 //fontDialog.ShowColor = true;
223 //fontDialog.ShowApply = true;
224 fontDialog.ShowEffects = true;
225 fontDialog.Font = cds.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)
242 //Set the fonts to all our labels in our layout
243 foreach (Control ctrl in tableLayoutPanel.Controls)
245 if (ctrl is MarqueeLabel)
247 ((MarqueeLabel)ctrl).Font = fontDialog.Font;
252 cds.Font = fontDialog.Font;
253 Properties.Settings.Default.Save();
262 void CheckFontHeight()
264 //Show font height and width
265 labelFontHeight.Text = "Font height: " + cds.Font.Height;
266 float charWidth = IsFixedWidth(cds.Font);
267 if (charWidth == 0.0f)
269 labelFontWidth.Visible = false;
273 labelFontWidth.Visible = true;
274 labelFontWidth.Text = "Font width: " + charWidth;
277 MarqueeLabel label = null;
278 //Get the first label control we can find
279 foreach (Control ctrl in tableLayoutPanel.Controls)
281 if (ctrl is MarqueeLabel)
283 label = (MarqueeLabel)ctrl;
288 //Now check font height and show a warning if needed.
289 if (label != null && label.Font.Height > label.Height)
291 labelWarning.Text = "WARNING: Selected font is too height by " + (label.Font.Height - label.Height) + " pixels!";
292 labelWarning.Visible = true;
296 labelWarning.Visible = false;
301 private void buttonCapture_Click(object sender, EventArgs e)
303 System.Drawing.Bitmap bmp = new System.Drawing.Bitmap(tableLayoutPanel.Width, tableLayoutPanel.Height);
304 tableLayoutPanel.DrawToBitmap(bmp, tableLayoutPanel.ClientRectangle);
305 //Bitmap bmpToSave = new Bitmap(bmp);
306 bmp.Save("D:\\capture.png");
308 ((MarqueeLabel)tableLayoutPanel.Controls[0]).Text = "Captured";
311 string outputFileName = "d:\\capture.png";
312 using (MemoryStream memory = new MemoryStream())
314 using (FileStream fs = new FileStream(outputFileName, FileMode.OpenOrCreate, FileAccess.ReadWrite))
316 bmp.Save(memory, System.Drawing.Imaging.ImageFormat.Png);
317 byte[] bytes = memory.ToArray();
318 fs.Write(bytes, 0, bytes.Length);
325 private void CheckForRequestResults()
327 if (iDisplay.IsRequestPending())
329 switch (iDisplay.AttemptRequestCompletion())
331 case Display.TMiniDisplayRequest.EMiniDisplayRequestFirmwareRevision:
332 toolStripStatusLabelConnect.Text += " v" + iDisplay.FirmwareRevision();
333 //Issue next request then
334 iDisplay.RequestPowerSupplyStatus();
337 case Display.TMiniDisplayRequest.EMiniDisplayRequestPowerSupplyStatus:
338 if (iDisplay.PowerSupplyStatus())
340 toolStripStatusLabelPower.Text = "ON";
344 toolStripStatusLabelPower.Text = "OFF";
346 //Issue next request then
347 iDisplay.RequestDeviceId();
350 case Display.TMiniDisplayRequest.EMiniDisplayRequestDeviceId:
351 toolStripStatusLabelConnect.Text += " - " + iDisplay.DeviceId();
352 //No more request to issue
358 public static uint ColorWhiteIsOn(int aX, int aY, uint aPixel)
360 if ((aPixel & 0x00FFFFFF) == 0x00FFFFFF)
367 public static uint ColorUntouched(int aX, int aY, uint aPixel)
372 public static uint ColorInversed(int aX, int aY, uint aPixel)
377 public static uint ColorChessboard(int aX, int aY, uint aPixel)
379 if ((aX % 2 == 0) && (aY % 2 == 0))
383 else if ((aX % 2 != 0) && (aY % 2 != 0))
391 public static int ScreenReversedX(System.Drawing.Bitmap aBmp, int aX)
393 return aBmp.Width - aX - 1;
396 public int ScreenReversedY(System.Drawing.Bitmap aBmp, int aY)
398 return iBmp.Height - aY - 1;
401 public int ScreenX(System.Drawing.Bitmap aBmp, int aX)
406 public int ScreenY(System.Drawing.Bitmap aBmp, int aY)
412 /// Select proper pixel delegates according to our current settings.
414 private void SetupPixelDelegates()
416 //Select our pixel processing routine
417 if (cds.InverseColors)
419 //iColorFx = ColorChessboard;
420 iColorFx = ColorInversed;
424 iColorFx = ColorWhiteIsOn;
427 //Select proper coordinate translation functions
428 //We used delegate/function pointer to support reverse screen without doing an extra test on each pixels
429 if (cds.ReverseScreen)
431 iScreenX = ScreenReversedX;
432 iScreenY = ScreenReversedY;
442 //This is our timer tick responsible to perform our render
443 private void timer_Tick(object sender, EventArgs e)
445 //Update our animations
446 DateTime NewTickTime = DateTime.Now;
448 //Update animation for all our marquees
449 foreach (Control ctrl in tableLayoutPanel.Controls)
451 if (ctrl is MarqueeLabel)
453 ((MarqueeLabel)ctrl).UpdateAnimation(LastTickTime, NewTickTime);
459 if (iDisplay.IsOpen())
461 CheckForRequestResults();
466 iBmp = new System.Drawing.Bitmap(tableLayoutPanel.Width, tableLayoutPanel.Height, PixelFormat.Format32bppArgb);
468 tableLayoutPanel.DrawToBitmap(iBmp, tableLayoutPanel.ClientRectangle);
469 //iBmp.Save("D:\\capture.png");
471 //Send it to our display
472 for (int i = 0; i < iBmp.Width; i++)
474 for (int j = 0; j < iBmp.Height; j++)
478 //Get our processed pixel coordinates
479 int x = iScreenX(iBmp, i);
480 int y = iScreenY(iBmp, j);
482 uint color = (uint)iBmp.GetPixel(i, j).ToArgb();
483 //Apply color effects
484 color = iColorFx(x,y,color);
486 iDisplay.SetPixel(x, y, color);
491 iDisplay.SwapBuffers();
495 //Compute instant FPS
496 toolStripStatusLabelFps.Text = (1.0/NewTickTime.Subtract(LastTickTime).TotalSeconds).ToString("F0") + " / " + (1000/timer.Interval).ToString() + " FPS";
498 LastTickTime = NewTickTime;
503 /// Attempt to establish connection with our display hardware.
505 private void OpenDisplayConnection()
507 CloseDisplayConnection();
509 if (iDisplay.Open((Display.TMiniDisplayType)cds.DisplayType))
512 iDisplay.RequestFirmwareRevision();
517 toolStripStatusLabelConnect.Text = "Connection error";
521 private void CloseDisplayConnection()
527 private void buttonOpen_Click(object sender, EventArgs e)
529 OpenDisplayConnection();
532 private void buttonClose_Click(object sender, EventArgs e)
534 CloseDisplayConnection();
537 private void buttonClear_Click(object sender, EventArgs e)
540 iDisplay.SwapBuffers();
543 private void buttonFill_Click(object sender, EventArgs e)
546 iDisplay.SwapBuffers();
549 private void trackBarBrightness_Scroll(object sender, EventArgs e)
551 cds.Brightness = trackBarBrightness.Value;
552 Properties.Settings.Default.Save();
553 iDisplay.SetBrightness(trackBarBrightness.Value);
559 /// CDS stands for Current Display Settings
561 private DisplaySettings cds
565 DisplaysSettings settings = Properties.Settings.Default.DisplaysSettings;
566 if (settings == null)
568 settings = new DisplaysSettings();
570 Properties.Settings.Default.DisplaysSettings = settings;
573 //Make sure all our settings have been created
574 while (settings.Displays.Count <= Properties.Settings.Default.CurrentDisplayIndex)
576 settings.Displays.Add(new DisplaySettings());
579 DisplaySettings displaySettings = settings.Displays[Properties.Settings.Default.CurrentDisplayIndex];
580 return displaySettings;
585 /// Check if the given font has a fixed character pitch.
587 /// <param name="ft"></param>
588 /// <returns>0.0f if this is not a monospace font, otherwise returns the character width.</returns>
589 public float IsFixedWidth(Font ft)
591 Graphics g = CreateGraphics();
592 char[] charSizes = new char[] { 'i', 'a', 'Z', '%', '#', 'a', 'B', 'l', 'm', ',', '.' };
593 float charWidth = g.MeasureString("I", ft, Int32.MaxValue, StringFormat.GenericTypographic).Width;
595 bool fixedWidth = true;
597 foreach (char c in charSizes)
598 if (g.MeasureString(c.ToString(), ft, Int32.MaxValue, StringFormat.GenericTypographic).Width != charWidth)
610 /// Synchronize UI with settings
612 private void UpdateStatus()
615 checkBoxShowBorders.Checked = cds.ShowBorders;
616 tableLayoutPanel.CellBorderStyle = (cds.ShowBorders ? TableLayoutPanelCellBorderStyle.Single : TableLayoutPanelCellBorderStyle.None);
618 //Set the proper font to each of our labels
619 foreach (MarqueeLabel ctrl in tableLayoutPanel.Controls)
621 ctrl.Font = cds.Font;
625 //Check if "run on Windows startup" is enabled
626 checkBoxAutoStart.Checked = iStartupManager.Startup;
628 checkBoxConnectOnStartup.Checked = Properties.Settings.Default.DisplayConnectOnStartup;
629 checkBoxMinimizeToTray.Checked = Properties.Settings.Default.MinimizeToTray;
630 checkBoxStartMinimized.Checked = Properties.Settings.Default.StartMinimized;
631 checkBoxReverseScreen.Checked = cds.ReverseScreen;
632 checkBoxInverseColors.Checked = cds.InverseColors;
633 checkBoxScaleToFit.Checked = cds.ScaleToFit;
634 maskedTextBoxMinFontSize.Enabled = cds.ScaleToFit;
635 labelMinFontSize.Enabled = cds.ScaleToFit;
636 maskedTextBoxMinFontSize.Text = cds.MinFontSize.ToString();
637 comboBoxDisplayType.SelectedIndex = cds.DisplayType;
638 timer.Interval = cds.TimerInterval;
639 maskedTextBoxTimerInterval.Text = cds.TimerInterval.ToString();
640 textBoxScrollLoopSeparator.Text = cds.Separator;
642 SetupPixelDelegates();
644 if (iDisplay.IsOpen())
646 //We have a display connection
647 //Reflect that in our UI
649 //Set our screen size
650 tableLayoutPanel.Width = iDisplay.WidthInPixels();
651 tableLayoutPanel.Height = iDisplay.HeightInPixels();
652 tableLayoutPanel.Enabled = true;
654 //Only setup brightness if display is open
655 trackBarBrightness.Minimum = iDisplay.MinBrightness();
656 trackBarBrightness.Maximum = iDisplay.MaxBrightness();
657 trackBarBrightness.Value = cds.Brightness;
658 trackBarBrightness.LargeChange = Math.Max(1, (iDisplay.MaxBrightness() - iDisplay.MinBrightness()) / 5);
659 trackBarBrightness.SmallChange = 1;
660 iDisplay.SetBrightness(cds.Brightness);
662 buttonFill.Enabled = true;
663 buttonClear.Enabled = true;
664 buttonOpen.Enabled = false;
665 buttonClose.Enabled = true;
666 trackBarBrightness.Enabled = true;
667 toolStripStatusLabelConnect.Text = "Connected - " + iDisplay.Vendor() + " - " + iDisplay.Product();
668 //+ " - " + iDisplay.SerialNumber();
670 if (iDisplay.SupportPowerOnOff())
672 buttonPowerOn.Enabled = true;
673 buttonPowerOff.Enabled = true;
677 buttonPowerOn.Enabled = false;
678 buttonPowerOff.Enabled = false;
681 if (iDisplay.SupportClock())
683 buttonShowClock.Enabled = true;
684 buttonHideClock.Enabled = true;
688 buttonShowClock.Enabled = false;
689 buttonHideClock.Enabled = false;
694 //Display is connection not available
695 //Reflect that in our UI
696 tableLayoutPanel.Enabled = false;
697 buttonFill.Enabled = false;
698 buttonClear.Enabled = false;
699 buttonOpen.Enabled = true;
700 buttonClose.Enabled = false;
701 trackBarBrightness.Enabled = false;
702 buttonPowerOn.Enabled = false;
703 buttonPowerOff.Enabled = false;
704 buttonShowClock.Enabled = false;
705 buttonHideClock.Enabled = false;
706 toolStripStatusLabelConnect.Text = "Disconnected";
707 toolStripStatusLabelPower.Text = "N/A";
713 private void checkBoxShowBorders_CheckedChanged(object sender, EventArgs e)
715 //Save our show borders setting
716 tableLayoutPanel.CellBorderStyle = (checkBoxShowBorders.Checked ? TableLayoutPanelCellBorderStyle.Single : TableLayoutPanelCellBorderStyle.None);
717 cds.ShowBorders = checkBoxShowBorders.Checked;
718 Properties.Settings.Default.Save();
722 private void checkBoxConnectOnStartup_CheckedChanged(object sender, EventArgs e)
724 //Save our connect on startup setting
725 Properties.Settings.Default.DisplayConnectOnStartup = checkBoxConnectOnStartup.Checked;
726 Properties.Settings.Default.Save();
729 private void checkBoxMinimizeToTray_CheckedChanged(object sender, EventArgs e)
731 //Save our "Minimize to tray" setting
732 Properties.Settings.Default.MinimizeToTray = checkBoxMinimizeToTray.Checked;
733 Properties.Settings.Default.Save();
737 private void checkBoxStartMinimized_CheckedChanged(object sender, EventArgs e)
739 //Save our "Start minimized" setting
740 Properties.Settings.Default.StartMinimized = checkBoxStartMinimized.Checked;
741 Properties.Settings.Default.Save();
744 private void checkBoxAutoStart_CheckedChanged(object sender, EventArgs e)
746 iStartupManager.Startup = checkBoxAutoStart.Checked;
750 private void checkBoxReverseScreen_CheckedChanged(object sender, EventArgs e)
752 //Save our reverse screen setting
753 cds.ReverseScreen = checkBoxReverseScreen.Checked;
754 Properties.Settings.Default.Save();
755 SetupPixelDelegates();
758 private void checkBoxInverseColors_CheckedChanged(object sender, EventArgs e)
760 //Save our inverse colors setting
761 cds.InverseColors = checkBoxInverseColors.Checked;
762 Properties.Settings.Default.Save();
763 SetupPixelDelegates();
766 private void checkBoxScaleToFit_CheckedChanged(object sender, EventArgs e)
768 //Save our scale to fit setting
769 cds.ScaleToFit = checkBoxScaleToFit.Checked;
770 Properties.Settings.Default.Save();
772 labelMinFontSize.Enabled = cds.ScaleToFit;
773 maskedTextBoxMinFontSize.Enabled = cds.ScaleToFit;
776 private void MainForm_Resize(object sender, EventArgs e)
778 if (WindowState == FormWindowState.Minimized)
781 //iBmp = new System.Drawing.Bitmap(tableLayoutPanel.Width, tableLayoutPanel.Height, PixelFormat.Format32bppArgb);
782 iCreateBitmap = true;
786 private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
792 public void StartServer()
794 iServiceHost = new ServiceHost
797 new Uri[] { new Uri("net.tcp://localhost:8001/") }
800 iServiceHost.AddServiceEndpoint(typeof(IService), new NetTcpBinding(SecurityMode.None, true), "DisplayService");
804 public void StopServer()
806 if (iClients.Count > 0 && !iClosing)
810 BroadcastCloseEvent();
814 if (MessageBox.Show("Force exit?", "Waiting for clients...", MessageBoxButtons.YesNo, MessageBoxIcon.Warning) == DialogResult.Yes)
816 iClosing = false; //We make sure we force close if asked twice
821 //We removed that as it often lags for some reason
822 //iServiceHost.Close();
826 public void BroadcastCloseEvent()
828 Trace.TraceInformation("BroadcastCloseEvent - start");
830 var inactiveClients = new List<string>();
831 foreach (var client in iClients)
833 //if (client.Key != eventData.ClientName)
837 Trace.TraceInformation("BroadcastCloseEvent - " + client.Key);
838 client.Value.Callback.OnCloseOrder(/*eventData*/);
842 inactiveClients.Add(client.Key);
847 if (inactiveClients.Count > 0)
849 foreach (var client in inactiveClients)
851 iClients.Remove(client);
852 Program.iMainForm.treeViewClients.Nodes.Remove(Program.iMainForm.treeViewClients.Nodes.Find(client, false)[0]);
856 if (iClients.Count==0)
863 /// Just remove all our fields.
865 private void ClearLayout()
867 tableLayoutPanel.Controls.Clear();
868 tableLayoutPanel.RowStyles.Clear();
869 tableLayoutPanel.ColumnStyles.Clear();
872 private void buttonStartClient_Click(object sender, EventArgs e)
874 Thread clientThread = new Thread(SharpDisplayClient.Program.Main);
875 clientThread.Start();
879 private void buttonSuspend_Click(object sender, EventArgs e)
881 LastTickTime = DateTime.Now; //Reset timer to prevent jump
882 timer.Enabled = !timer.Enabled;
885 buttonSuspend.Text = "Run";
889 buttonSuspend.Text = "Pause";
893 private void buttonCloseClients_Click(object sender, EventArgs e)
895 BroadcastCloseEvent();
898 private void treeViewClients_AfterSelect(object sender, TreeViewEventArgs e)
907 /// <param name="aSessionId"></param>
908 /// <param name="aCallback"></param>
909 public void AddClientThreadSafe(string aSessionId, ICallback aCallback)
911 if (this.InvokeRequired)
913 //Not in the proper thread, invoke ourselves
914 AddClientDelegate d = new AddClientDelegate(AddClientThreadSafe);
915 this.Invoke(d, new object[] { aSessionId, aCallback });
919 //We are in the proper thread
920 //Add this session to our collection of clients
921 ClientData newClient = new ClientData(aSessionId, aCallback);
922 Program.iMainForm.iClients.Add(aSessionId, newClient);
923 //Add this session to our UI
924 UpdateClientTreeViewNode(newClient);
931 /// <param name="aSessionId"></param>
932 public void RemoveClientThreadSafe(string aSessionId)
934 if (this.InvokeRequired)
936 //Not in the proper thread, invoke ourselves
937 RemoveClientDelegate d = new RemoveClientDelegate(RemoveClientThreadSafe);
938 this.Invoke(d, new object[] { aSessionId });
942 //We are in the proper thread
943 //Remove this session from both client collection and UI tree view
944 if (Program.iMainForm.iClients.Keys.Contains(aSessionId))
946 Program.iMainForm.iClients.Remove(aSessionId);
947 Program.iMainForm.treeViewClients.Nodes.Remove(Program.iMainForm.treeViewClients.Nodes.Find(aSessionId, false)[0]);
950 if (iClients.Count == 0)
952 //Clear our screen when last client disconnects
957 //We were closing our form
958 //All clients are now closed
959 //Just resume our close operation
970 /// <param name="aSessionId"></param>
971 /// <param name="aLayout"></param>
972 public void SetClientLayoutThreadSafe(string aSessionId, TableLayout aLayout)
974 if (this.InvokeRequired)
976 //Not in the proper thread, invoke ourselves
977 SetLayoutDelegate d = new SetLayoutDelegate(SetClientLayoutThreadSafe);
978 this.Invoke(d, new object[] { aSessionId, aLayout });
982 ClientData client = iClients[aSessionId];
985 client.Layout = aLayout;
986 UpdateTableLayoutPanel(client);
988 UpdateClientTreeViewNode(client);
996 /// <param name="aSessionId"></param>
997 /// <param name="aField"></param>
998 public void SetClientFieldThreadSafe(string aSessionId, DataField aField)
1000 if (this.InvokeRequired)
1002 //Not in the proper thread, invoke ourselves
1003 SetFieldDelegate d = new SetFieldDelegate(SetClientFieldThreadSafe);
1004 this.Invoke(d, new object[] { aSessionId, aField });
1008 //We are in the proper thread
1009 //Call the non-thread-safe variant
1010 SetClientField(aSessionId, aField);
1017 /// <param name="aSessionId"></param>
1018 /// <param name="aField"></param>
1019 private void SetClientField(string aSessionId, DataField aField)
1021 SetCurrentClient(aSessionId);
1022 ClientData client = iClients[aSessionId];
1025 bool somethingChanged = false;
1027 //Make sure all our fields are in place
1028 while (client.Fields.Count < (aField.Index + 1))
1030 //Add a text field with proper index
1031 client.Fields.Add(new DataField(client.Fields.Count));
1032 somethingChanged = true;
1035 if (client.Fields[aField.Index].IsSameLayout(aField))
1037 //Same layout just update our field
1038 client.Fields[aField.Index] = aField;
1040 if (aField.IsText && tableLayoutPanel.Controls[aField.Index] is MarqueeLabel)
1042 //Text field control already in place, just change the text
1043 MarqueeLabel label = (MarqueeLabel)tableLayoutPanel.Controls[aField.Index];
1044 somethingChanged = (label.Text != aField.Text || label.TextAlign != aField.Alignment);
1045 label.Text = aField.Text;
1046 label.TextAlign = aField.Alignment;
1048 else if (aField.IsBitmap && tableLayoutPanel.Controls[aField.Index] is PictureBox)
1050 somethingChanged = true; //TODO: Bitmap comp or should we leave that to clients?
1051 //Bitmap field control already in place just change the bitmap
1052 PictureBox pictureBox = (PictureBox)tableLayoutPanel.Controls[aField.Index];
1053 pictureBox.Image = aField.Bitmap;
1057 somethingChanged = true;
1058 //The requested control in our layout it not of the correct type
1059 //Wrong control type, re-create them all
1060 UpdateTableLayoutPanel(iCurrentClientData);
1065 somethingChanged = true;
1066 //Different layout, need to rebuild it
1067 client.Fields[aField.Index] = aField;
1068 UpdateTableLayoutPanel(iCurrentClientData);
1072 if (somethingChanged)
1074 UpdateClientTreeViewNode(client);
1082 /// <param name="aTexts"></param>
1083 public void SetClientFieldsThreadSafe(string aSessionId, System.Collections.Generic.IList<DataField> aFields)
1085 if (this.InvokeRequired)
1087 //Not in the proper thread, invoke ourselves
1088 SetFieldsDelegate d = new SetFieldsDelegate(SetClientFieldsThreadSafe);
1089 this.Invoke(d, new object[] { aSessionId, aFields });
1093 //Put each our text fields in a label control
1094 foreach (DataField field in aFields)
1096 SetClientField(aSessionId, field);
1104 /// <param name="aSessionId"></param>
1105 /// <param name="aName"></param>
1106 public void SetClientNameThreadSafe(string aSessionId, string aName)
1108 if (this.InvokeRequired)
1110 //Not in the proper thread, invoke ourselves
1111 SetClientNameDelegate d = new SetClientNameDelegate(SetClientNameThreadSafe);
1112 this.Invoke(d, new object[] { aSessionId, aName });
1116 //We are in the proper thread
1118 ClientData client = iClients[aSessionId];
1122 client.Name = aName;
1123 //Update our tree-view
1124 UpdateClientTreeViewNode(client);
1132 /// <param name="aClient"></param>
1133 private void UpdateClientTreeViewNode(ClientData aClient)
1135 if (aClient == null)
1140 TreeNode node = null;
1141 //Check that our client node already exists
1142 //Get our client root node using its key which is our session ID
1143 TreeNode[] nodes = treeViewClients.Nodes.Find(aClient.SessionId, false);
1144 if (nodes.Count()>0)
1146 //We already have a node for that client
1148 //Clear children as we are going to recreate them below
1153 //Client node does not exists create a new one
1154 treeViewClients.Nodes.Add(aClient.SessionId, aClient.SessionId);
1155 node = treeViewClients.Nodes.Find(aClient.SessionId, false)[0];
1161 if (aClient.Name != "")
1163 //We have a name, us it as text for our root node
1164 node.Text = aClient.Name;
1165 //Add a child with SessionId
1166 node.Nodes.Add(new TreeNode(aClient.SessionId));
1170 //No name, use session ID instead
1171 node.Text = aClient.SessionId;
1174 if (aClient.Fields.Count > 0)
1176 //Create root node for our texts
1177 TreeNode textsRoot = new TreeNode("Fields");
1178 node.Nodes.Add(textsRoot);
1179 //For each text add a new entry
1180 foreach (DataField field in aClient.Fields)
1182 if (!field.IsBitmap)
1184 DataField textField = (DataField)field;
1185 textsRoot.Nodes.Add(new TreeNode("[Text]" + textField.Text));
1189 textsRoot.Nodes.Add(new TreeNode("[Bitmap]"));
1198 private void buttonAddRow_Click(object sender, EventArgs e)
1200 if (tableLayoutPanel.RowCount < 6)
1202 UpdateTableLayoutPanel(tableLayoutPanel.ColumnCount, tableLayoutPanel.RowCount + 1);
1206 private void buttonRemoveRow_Click(object sender, EventArgs e)
1208 if (tableLayoutPanel.RowCount > 1)
1210 UpdateTableLayoutPanel(tableLayoutPanel.ColumnCount, tableLayoutPanel.RowCount - 1);
1213 UpdateTableLayoutRowStyles();
1216 private void buttonAddColumn_Click(object sender, EventArgs e)
1218 if (tableLayoutPanel.ColumnCount < 8)
1220 UpdateTableLayoutPanel(tableLayoutPanel.ColumnCount + 1, tableLayoutPanel.RowCount);
1224 private void buttonRemoveColumn_Click(object sender, EventArgs e)
1226 if (tableLayoutPanel.ColumnCount > 1)
1228 UpdateTableLayoutPanel(tableLayoutPanel.ColumnCount - 1, tableLayoutPanel.RowCount);
1234 /// Update our table layout row styles to make sure each rows have similar height
1236 private void UpdateTableLayoutRowStyles()
1238 foreach (RowStyle rowStyle in tableLayoutPanel.RowStyles)
1240 rowStyle.SizeType = SizeType.Percent;
1241 rowStyle.Height = 100 / tableLayoutPanel.RowCount;
1247 /// Empty and recreate our table layout with the given number of columns and rows.
1248 /// Sizes of rows and columns are uniform.
1250 /// <param name="aColumn"></param>
1251 /// <param name="aRow"></param>
1252 private void UpdateTableLayoutPanel(int aColumn, int aRow)
1254 tableLayoutPanel.Controls.Clear();
1255 tableLayoutPanel.RowStyles.Clear();
1256 tableLayoutPanel.ColumnStyles.Clear();
1257 tableLayoutPanel.RowCount = 0;
1258 tableLayoutPanel.ColumnCount = 0;
1260 while (tableLayoutPanel.RowCount < aRow)
1262 tableLayoutPanel.RowCount++;
1265 while (tableLayoutPanel.ColumnCount < aColumn)
1267 tableLayoutPanel.ColumnCount++;
1270 for (int i = 0; i < tableLayoutPanel.ColumnCount; i++)
1272 //Create our column styles
1273 this.tableLayoutPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100 / tableLayoutPanel.ColumnCount));
1275 for (int j = 0; j < tableLayoutPanel.RowCount; j++)
1279 //Create our row styles
1280 this.tableLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100 / tableLayoutPanel.RowCount));
1283 MarqueeLabel control = new SharpDisplayManager.MarqueeLabel();
1284 control.AutoEllipsis = true;
1285 control.AutoSize = true;
1286 control.BackColor = System.Drawing.Color.Transparent;
1287 control.Dock = System.Windows.Forms.DockStyle.Fill;
1288 control.Location = new System.Drawing.Point(1, 1);
1289 control.Margin = new System.Windows.Forms.Padding(0);
1290 control.Name = "marqueeLabelCol" + aColumn + "Row" + aRow;
1291 control.OwnTimer = false;
1292 control.PixelsPerSecond = 64;
1293 control.Separator = cds.Separator;
1294 control.MinFontSize = cds.MinFontSize;
1295 control.ScaleToFit = cds.ScaleToFit;
1296 //control.Size = new System.Drawing.Size(254, 30);
1297 //control.TabIndex = 2;
1298 control.Font = cds.Font;
1299 control.Text = "ABCDEFGHIJKLMNOPQRST-0123456789";
1300 control.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
1301 control.UseCompatibleTextRendering = true;
1303 tableLayoutPanel.Controls.Add(control, i, j);
1312 /// Update our display table layout.
1314 /// <param name="aLayout"></param>
1315 private void UpdateTableLayoutPanel(ClientData aClient)
1317 TableLayout layout = aClient.Layout;
1320 tableLayoutPanel.Controls.Clear();
1321 tableLayoutPanel.RowStyles.Clear();
1322 tableLayoutPanel.ColumnStyles.Clear();
1323 tableLayoutPanel.RowCount = 0;
1324 tableLayoutPanel.ColumnCount = 0;
1326 while (tableLayoutPanel.RowCount < layout.Rows.Count)
1328 tableLayoutPanel.RowCount++;
1331 while (tableLayoutPanel.ColumnCount < layout.Columns.Count)
1333 tableLayoutPanel.ColumnCount++;
1336 for (int i = 0; i < tableLayoutPanel.ColumnCount; i++)
1338 //Create our column styles
1339 this.tableLayoutPanel.ColumnStyles.Add(layout.Columns[i]);
1341 for (int j = 0; j < tableLayoutPanel.RowCount; j++)
1345 //Create our row styles
1346 this.tableLayoutPanel.RowStyles.Add(layout.Rows[j]);
1349 //Check if we already have a control
1350 Control existingControl = tableLayoutPanel.GetControlFromPosition(i,j);
1351 if (existingControl!=null)
1353 //We already have a control in that cell as a results of row/col spanning
1354 //Move on to next cell then
1360 //Check if a client field already exists for that cell
1361 if (aClient.Fields.Count <= tableLayoutPanel.Controls.Count)
1363 //No client field specified, create a text field by default
1364 aClient.Fields.Add(new DataField(aClient.Fields.Count));
1367 //Create a control corresponding to the field specified for that cell
1368 DataField field = aClient.Fields[tableLayoutPanel.Controls.Count];
1369 Control control = CreateControlForDataField(field);
1371 //Add newly created control to our table layout at the specified row and column
1372 tableLayoutPanel.Controls.Add(control, i, j);
1373 //Make sure we specify row and column span for that new control
1374 tableLayoutPanel.SetRowSpan(control,field.RowSpan);
1375 tableLayoutPanel.SetColumnSpan(control, field.ColumnSpan);
1380 while (aClient.Fields.Count > fieldCount)
1382 //We have too much fields for this layout
1383 //Just discard them until we get there
1384 aClient.Fields.RemoveAt(aClient.Fields.Count-1);
1391 /// Check our type of data field and create corresponding control
1393 /// <param name="aField"></param>
1394 private Control CreateControlForDataField(DataField aField)
1396 Control control=null;
1397 if (!aField.IsBitmap)
1399 MarqueeLabel label = new SharpDisplayManager.MarqueeLabel();
1400 label.AutoEllipsis = true;
1401 label.AutoSize = true;
1402 label.BackColor = System.Drawing.Color.Transparent;
1403 label.Dock = System.Windows.Forms.DockStyle.Fill;
1404 label.Location = new System.Drawing.Point(1, 1);
1405 label.Margin = new System.Windows.Forms.Padding(0);
1406 label.Name = "marqueeLabel" + aField.Index;
1407 label.OwnTimer = false;
1408 label.PixelsPerSecond = 64;
1409 label.Separator = cds.Separator;
1410 label.MinFontSize = cds.MinFontSize;
1411 label.ScaleToFit = cds.ScaleToFit;
1412 //control.Size = new System.Drawing.Size(254, 30);
1413 //control.TabIndex = 2;
1414 label.Font = cds.Font;
1416 label.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
1417 label.UseCompatibleTextRendering = true;
1418 label.Text = aField.Text;
1424 //Create picture box
1425 PictureBox picture = new PictureBox();
1426 picture.AutoSize = true;
1427 picture.BackColor = System.Drawing.Color.Transparent;
1428 picture.Dock = System.Windows.Forms.DockStyle.Fill;
1429 picture.Location = new System.Drawing.Point(1, 1);
1430 picture.Margin = new System.Windows.Forms.Padding(0);
1431 picture.Name = "pictureBox" + aField;
1433 picture.Image = aField.Bitmap;
1442 private void buttonAlignLeft_Click(object sender, EventArgs e)
1444 foreach (MarqueeLabel ctrl in tableLayoutPanel.Controls)
1446 ctrl.TextAlign = ContentAlignment.MiddleLeft;
1450 private void buttonAlignCenter_Click(object sender, EventArgs e)
1452 foreach (MarqueeLabel ctrl in tableLayoutPanel.Controls)
1454 ctrl.TextAlign = ContentAlignment.MiddleCenter;
1458 private void buttonAlignRight_Click(object sender, EventArgs e)
1460 foreach (MarqueeLabel ctrl in tableLayoutPanel.Controls)
1462 ctrl.TextAlign = ContentAlignment.MiddleRight;
1467 /// Called when the user selected a new display type.
1469 /// <param name="sender"></param>
1470 /// <param name="e"></param>
1471 private void comboBoxDisplayType_SelectedIndexChanged(object sender, EventArgs e)
1473 //Store the selected display type in our settings
1474 Properties.Settings.Default.CurrentDisplayIndex = comboBoxDisplayType.SelectedIndex;
1475 cds.DisplayType = comboBoxDisplayType.SelectedIndex;
1476 Properties.Settings.Default.Save();
1478 //Try re-opening the display connection if we were already connected.
1479 //Otherwise just update our status to reflect display type change.
1480 if (iDisplay.IsOpen())
1482 OpenDisplayConnection();
1490 private void maskedTextBoxTimerInterval_TextChanged(object sender, EventArgs e)
1492 if (maskedTextBoxTimerInterval.Text != "")
1494 int interval = Convert.ToInt32(maskedTextBoxTimerInterval.Text);
1498 timer.Interval = interval;
1499 cds.TimerInterval = timer.Interval;
1500 Properties.Settings.Default.Save();
1505 private void maskedTextBoxMinFontSize_TextChanged(object sender, EventArgs e)
1507 if (maskedTextBoxMinFontSize.Text != "")
1509 int minFontSize = Convert.ToInt32(maskedTextBoxMinFontSize.Text);
1511 if (minFontSize > 0)
1513 //TODO: re-create layout? update our fields?
1514 cds.MinFontSize = minFontSize;
1515 Properties.Settings.Default.Save();
1520 private void textBoxScrollLoopSeparator_TextChanged(object sender, EventArgs e)
1522 //TODO: re-create layout? update our fields?
1523 cds.Separator = textBoxScrollLoopSeparator.Text;
1524 Properties.Settings.Default.Save();
1527 private void buttonPowerOn_Click(object sender, EventArgs e)
1532 private void buttonPowerOff_Click(object sender, EventArgs e)
1534 iDisplay.PowerOff();
1537 private void buttonShowClock_Click(object sender, EventArgs e)
1539 iDisplay.ShowClock();
1542 private void buttonHideClock_Click(object sender, EventArgs e)
1544 iDisplay.HideClock();
1547 private void buttonUpdate_Click(object sender, EventArgs e)
1549 InstallUpdateSyncWithInfo();
1553 private void InstallUpdateSyncWithInfo()
1555 UpdateCheckInfo info = null;
1557 if (ApplicationDeployment.IsNetworkDeployed)
1559 ApplicationDeployment ad = ApplicationDeployment.CurrentDeployment;
1563 info = ad.CheckForDetailedUpdate();
1566 catch (DeploymentDownloadException dde)
1568 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);
1571 catch (InvalidDeploymentException ide)
1573 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);
1576 catch (InvalidOperationException ioe)
1578 MessageBox.Show("This application cannot be updated. It is likely not a ClickOnce application. Error: " + ioe.Message);
1582 if (info.UpdateAvailable)
1584 Boolean doUpdate = true;
1586 if (!info.IsUpdateRequired)
1588 DialogResult dr = MessageBox.Show("An update is available. Would you like to update the application now?", "Update Available", MessageBoxButtons.OKCancel);
1589 if (!(DialogResult.OK == dr))
1596 // Display a message that the app MUST reboot. Display the minimum required version.
1597 MessageBox.Show("This application has detected a mandatory update from your current " +
1598 "version to version " + info.MinimumRequiredVersion.ToString() +
1599 ". The application will now install the update and restart.",
1600 "Update Available", MessageBoxButtons.OK,
1601 MessageBoxIcon.Information);
1609 MessageBox.Show("The application has been upgraded, and will now restart.");
1610 Application.Restart();
1612 catch (DeploymentDownloadException dde)
1614 MessageBox.Show("Cannot install the latest version of the application. \n\nPlease check your network connection, or try again later. Error: " + dde);
1621 MessageBox.Show("You are already running the latest version.", "Application up-to-date");
1630 private void SysTrayHideShow()
1636 WindowState = FormWindowState.Normal;
1641 /// Use to handle minimize events.
1643 /// <param name="sender"></param>
1644 /// <param name="e"></param>
1645 private void MainForm_SizeChanged(object sender, EventArgs e)
1647 if (WindowState == FormWindowState.Minimized && Properties.Settings.Default.MinimizeToTray)
1663 /// A UI thread copy of a client relevant data.
1664 /// Keeping this copy in the UI thread helps us deal with threading issues.
1666 public class ClientData
1668 public ClientData(string aSessionId, ICallback aCallback)
1670 SessionId = aSessionId;
1672 Fields = new List<DataField>();
1673 Layout = new TableLayout(1, 2); //Default to one column and two rows
1674 Callback = aCallback;
1677 public string SessionId { get; set; }
1678 public string Name { get; set; }
1679 public List<DataField> Fields { get; set; }
1680 public TableLayout Layout { get; set; }
1681 public ICallback Callback { get; set; }