Adding events for display closed/opened.
Display types now loaded dynamically from our MiniDisplay library.
Adding editor config.
Tested against Futaba GP1212A02.
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 //Instantiate our display and register for events notifications
76 iDisplay = new Display();
77 iDisplay.OnOpened += OnDisplayOpened;
78 iDisplay.OnClosed += OnDisplayClosed;
80 iClients = new Dictionary<string, ClientData>();
81 iStartupManager = new StartupManager();
82 iNotifyIcon = new NotifyIconAdv();
84 //Have our designer initialize its controls
85 InitializeComponent();
87 //Populate device types
88 PopulateDeviceTypes();
90 //Initial status update
93 //We have a bug when drawing minimized and reusing our bitmap
94 iBmp = new System.Drawing.Bitmap(tableLayoutPanel.Width, tableLayoutPanel.Height, PixelFormat.Format32bppArgb);
95 iCreateBitmap = false;
97 //Minimize our window if desired
98 if (Properties.Settings.Default.StartMinimized)
100 WindowState = FormWindowState.Minimized;
106 /// Called when our display is opened.
108 /// <param name="aDisplay"></param>
109 private void OnDisplayOpened(Display aDisplay)
111 //Our display was just opened, update our UI
113 //Initiate asynchronous request
114 iDisplay.RequestFirmwareRevision();
118 /// Called when our display is closed.
120 /// <param name="aDisplay"></param>
121 private void OnDisplayClosed(Display aDisplay)
123 //Our display was just closed, update our UI consequently
130 private void PopulateDeviceTypes()
132 int count = Display.TypeCount();
134 for (int i=0; i<count; i++)
136 comboBoxDisplayType.Items.Add(Display.TypeName((Display.TMiniDisplayType)i));
143 /// <param name="sender"></param>
144 /// <param name="e"></param>
145 private void MainForm_Load(object sender, EventArgs e)
147 //Check if we are running a Click Once deployed application
148 if (ApplicationDeployment.IsNetworkDeployed)
150 //This is a proper Click Once installation, fetch and show our version number
151 this.Text += " - v" + ApplicationDeployment.CurrentDeployment.CurrentVersion;
155 //Not a proper Click Once installation, assuming development build then
156 this.Text += " - development";
159 //Setup notification icon
162 // To make sure start up with minimize to tray works
163 if (WindowState == FormWindowState.Minimized && Properties.Settings.Default.MinimizeToTray)
169 //When not debugging we want the screen to be empty until a client takes over
173 //Open display connection on start-up if needed
174 if (Properties.Settings.Default.DisplayConnectOnStartup)
176 OpenDisplayConnection();
179 //Start our server so that we can get client requests
186 private void SetupTrayIcon()
188 iNotifyIcon.Icon = GetIcon("vfd.ico");
189 iNotifyIcon.Text = "Sharp Display Manager";
190 iNotifyIcon.Visible = true;
192 //Double click toggles visibility - typically brings up the application
193 iNotifyIcon.DoubleClick += delegate(object obj, EventArgs args)
198 //Adding a context menu, useful to be able to exit the application
199 ContextMenu contextMenu = new ContextMenu();
200 //Context menu item to toggle visibility
201 MenuItem hideShowItem = new MenuItem("Hide/Show");
202 hideShowItem.Click += delegate(object obj, EventArgs args)
206 contextMenu.MenuItems.Add(hideShowItem);
208 //Context menu item separator
209 contextMenu.MenuItems.Add(new MenuItem("-"));
211 //Context menu exit item
212 MenuItem exitItem = new MenuItem("Exit");
213 exitItem.Click += delegate(object obj, EventArgs args)
217 contextMenu.MenuItems.Add(exitItem);
219 iNotifyIcon.ContextMenu = contextMenu;
223 /// Access icons from embedded resources.
225 /// <param name="name"></param>
226 /// <returns></returns>
227 public static Icon GetIcon(string name)
229 name = "SharpDisplayManager.Resources." + name;
232 Assembly.GetExecutingAssembly().GetManifestResourceNames();
233 for (int i = 0; i < names.Length; i++)
235 if (names[i].Replace('\\', '.') == name)
237 using (Stream stream = Assembly.GetExecutingAssembly().
238 GetManifestResourceStream(names[i]))
240 return new Icon(stream);
250 /// Set our current client.
251 /// This will take care of applying our client layout and set data fields.
253 /// <param name="aSessionId"></param>
254 void SetCurrentClient(string aSessionId)
256 if (aSessionId == iCurrentClientSessionId)
258 //Given client is already the current one.
259 //Don't bother changing anything then.
263 //Set current client ID.
264 iCurrentClientSessionId = aSessionId;
265 //Fetch and set current client data.
266 iCurrentClientData = iClients[aSessionId];
267 //Apply layout and set data fields.
268 UpdateTableLayoutPanel(iCurrentClientData);
271 private void buttonFont_Click(object sender, EventArgs e)
273 //fontDialog.ShowColor = true;
274 //fontDialog.ShowApply = true;
275 fontDialog.ShowEffects = true;
276 fontDialog.Font = cds.Font;
278 fontDialog.FixedPitchOnly = checkBoxFixedPitchFontOnly.Checked;
280 //fontDialog.ShowHelp = true;
282 //fontDlg.MaxSize = 40;
283 //fontDlg.MinSize = 22;
285 //fontDialog.Parent = this;
286 //fontDialog.StartPosition = FormStartPosition.CenterParent;
288 //DlgBox.ShowDialog(fontDialog);
290 //if (fontDialog.ShowDialog(this) != DialogResult.Cancel)
291 if (DlgBox.ShowDialog(fontDialog) != DialogResult.Cancel)
293 //Set the fonts to all our labels in our layout
294 foreach (Control ctrl in tableLayoutPanel.Controls)
296 if (ctrl is MarqueeLabel)
298 ((MarqueeLabel)ctrl).Font = fontDialog.Font;
303 cds.Font = fontDialog.Font;
304 Properties.Settings.Default.Save();
313 void CheckFontHeight()
315 //Show font height and width
316 labelFontHeight.Text = "Font height: " + cds.Font.Height;
317 float charWidth = IsFixedWidth(cds.Font);
318 if (charWidth == 0.0f)
320 labelFontWidth.Visible = false;
324 labelFontWidth.Visible = true;
325 labelFontWidth.Text = "Font width: " + charWidth;
328 MarqueeLabel label = null;
329 //Get the first label control we can find
330 foreach (Control ctrl in tableLayoutPanel.Controls)
332 if (ctrl is MarqueeLabel)
334 label = (MarqueeLabel)ctrl;
339 //Now check font height and show a warning if needed.
340 if (label != null && label.Font.Height > label.Height)
342 labelWarning.Text = "WARNING: Selected font is too height by " + (label.Font.Height - label.Height) + " pixels!";
343 labelWarning.Visible = true;
347 labelWarning.Visible = false;
352 private void buttonCapture_Click(object sender, EventArgs e)
354 System.Drawing.Bitmap bmp = new System.Drawing.Bitmap(tableLayoutPanel.Width, tableLayoutPanel.Height);
355 tableLayoutPanel.DrawToBitmap(bmp, tableLayoutPanel.ClientRectangle);
356 //Bitmap bmpToSave = new Bitmap(bmp);
357 bmp.Save("D:\\capture.png");
359 ((MarqueeLabel)tableLayoutPanel.Controls[0]).Text = "Captured";
362 string outputFileName = "d:\\capture.png";
363 using (MemoryStream memory = new MemoryStream())
365 using (FileStream fs = new FileStream(outputFileName, FileMode.OpenOrCreate, FileAccess.ReadWrite))
367 bmp.Save(memory, System.Drawing.Imaging.ImageFormat.Png);
368 byte[] bytes = memory.ToArray();
369 fs.Write(bytes, 0, bytes.Length);
376 private void CheckForRequestResults()
378 if (iDisplay.IsRequestPending())
380 switch (iDisplay.AttemptRequestCompletion())
382 case Display.TMiniDisplayRequest.EMiniDisplayRequestFirmwareRevision:
383 toolStripStatusLabelConnect.Text += " v" + iDisplay.FirmwareRevision();
384 //Issue next request then
385 iDisplay.RequestPowerSupplyStatus();
388 case Display.TMiniDisplayRequest.EMiniDisplayRequestPowerSupplyStatus:
389 if (iDisplay.PowerSupplyStatus())
391 toolStripStatusLabelPower.Text = "ON";
395 toolStripStatusLabelPower.Text = "OFF";
397 //Issue next request then
398 iDisplay.RequestDeviceId();
401 case Display.TMiniDisplayRequest.EMiniDisplayRequestDeviceId:
402 toolStripStatusLabelConnect.Text += " - " + iDisplay.DeviceId();
403 //No more request to issue
409 public static uint ColorWhiteIsOn(int aX, int aY, uint aPixel)
411 if ((aPixel & 0x00FFFFFF) == 0x00FFFFFF)
418 public static uint ColorUntouched(int aX, int aY, uint aPixel)
423 public static uint ColorInversed(int aX, int aY, uint aPixel)
428 public static uint ColorChessboard(int aX, int aY, uint aPixel)
430 if ((aX % 2 == 0) && (aY % 2 == 0))
434 else if ((aX % 2 != 0) && (aY % 2 != 0))
442 public static int ScreenReversedX(System.Drawing.Bitmap aBmp, int aX)
444 return aBmp.Width - aX - 1;
447 public int ScreenReversedY(System.Drawing.Bitmap aBmp, int aY)
449 return iBmp.Height - aY - 1;
452 public int ScreenX(System.Drawing.Bitmap aBmp, int aX)
457 public int ScreenY(System.Drawing.Bitmap aBmp, int aY)
463 /// Select proper pixel delegates according to our current settings.
465 private void SetupPixelDelegates()
467 //Select our pixel processing routine
468 if (cds.InverseColors)
470 //iColorFx = ColorChessboard;
471 iColorFx = ColorInversed;
475 iColorFx = ColorWhiteIsOn;
478 //Select proper coordinate translation functions
479 //We used delegate/function pointer to support reverse screen without doing an extra test on each pixels
480 if (cds.ReverseScreen)
482 iScreenX = ScreenReversedX;
483 iScreenY = ScreenReversedY;
493 //This is our timer tick responsible to perform our render
494 private void timer_Tick(object sender, EventArgs e)
496 //Update our animations
497 DateTime NewTickTime = DateTime.Now;
499 //Update animation for all our marquees
500 foreach (Control ctrl in tableLayoutPanel.Controls)
502 if (ctrl is MarqueeLabel)
504 ((MarqueeLabel)ctrl).UpdateAnimation(LastTickTime, NewTickTime);
510 if (iDisplay.IsOpen())
512 CheckForRequestResults();
517 iBmp = new System.Drawing.Bitmap(tableLayoutPanel.Width, tableLayoutPanel.Height, PixelFormat.Format32bppArgb);
519 tableLayoutPanel.DrawToBitmap(iBmp, tableLayoutPanel.ClientRectangle);
520 //iBmp.Save("D:\\capture.png");
522 //Send it to our display
523 for (int i = 0; i < iBmp.Width; i++)
525 for (int j = 0; j < iBmp.Height; j++)
529 //Get our processed pixel coordinates
530 int x = iScreenX(iBmp, i);
531 int y = iScreenY(iBmp, j);
533 uint color = (uint)iBmp.GetPixel(i, j).ToArgb();
534 //Apply color effects
535 color = iColorFx(x,y,color);
537 iDisplay.SetPixel(x, y, color);
542 iDisplay.SwapBuffers();
546 //Compute instant FPS
547 toolStripStatusLabelFps.Text = (1.0/NewTickTime.Subtract(LastTickTime).TotalSeconds).ToString("F0") + " / " + (1000/timer.Interval).ToString() + " FPS";
549 LastTickTime = NewTickTime;
554 /// Attempt to establish connection with our display hardware.
556 private void OpenDisplayConnection()
558 CloseDisplayConnection();
560 if (!iDisplay.Open((Display.TMiniDisplayType)cds.DisplayType))
563 toolStripStatusLabelConnect.Text = "Connection error";
567 private void CloseDisplayConnection()
569 //Status will be updated upon receiving the closed event
573 private void buttonOpen_Click(object sender, EventArgs e)
575 OpenDisplayConnection();
578 private void buttonClose_Click(object sender, EventArgs e)
580 CloseDisplayConnection();
583 private void buttonClear_Click(object sender, EventArgs e)
586 iDisplay.SwapBuffers();
589 private void buttonFill_Click(object sender, EventArgs e)
592 iDisplay.SwapBuffers();
595 private void trackBarBrightness_Scroll(object sender, EventArgs e)
597 cds.Brightness = trackBarBrightness.Value;
598 Properties.Settings.Default.Save();
599 iDisplay.SetBrightness(trackBarBrightness.Value);
605 /// CDS stands for Current Display Settings
607 private DisplaySettings cds
611 DisplaysSettings settings = Properties.Settings.Default.DisplaysSettings;
612 if (settings == null)
614 settings = new DisplaysSettings();
616 Properties.Settings.Default.DisplaysSettings = settings;
619 //Make sure all our settings have been created
620 while (settings.Displays.Count <= Properties.Settings.Default.CurrentDisplayIndex)
622 settings.Displays.Add(new DisplaySettings());
625 DisplaySettings displaySettings = settings.Displays[Properties.Settings.Default.CurrentDisplayIndex];
626 return displaySettings;
631 /// Check if the given font has a fixed character pitch.
633 /// <param name="ft"></param>
634 /// <returns>0.0f if this is not a monospace font, otherwise returns the character width.</returns>
635 public float IsFixedWidth(Font ft)
637 Graphics g = CreateGraphics();
638 char[] charSizes = new char[] { 'i', 'a', 'Z', '%', '#', 'a', 'B', 'l', 'm', ',', '.' };
639 float charWidth = g.MeasureString("I", ft, Int32.MaxValue, StringFormat.GenericTypographic).Width;
641 bool fixedWidth = true;
643 foreach (char c in charSizes)
644 if (g.MeasureString(c.ToString(), ft, Int32.MaxValue, StringFormat.GenericTypographic).Width != charWidth)
656 /// Synchronize UI with settings
658 private void UpdateStatus()
661 checkBoxShowBorders.Checked = cds.ShowBorders;
662 tableLayoutPanel.CellBorderStyle = (cds.ShowBorders ? TableLayoutPanelCellBorderStyle.Single : TableLayoutPanelCellBorderStyle.None);
664 //Set the proper font to each of our labels
665 foreach (MarqueeLabel ctrl in tableLayoutPanel.Controls)
667 ctrl.Font = cds.Font;
671 //Check if "run on Windows startup" is enabled
672 checkBoxAutoStart.Checked = iStartupManager.Startup;
674 checkBoxConnectOnStartup.Checked = Properties.Settings.Default.DisplayConnectOnStartup;
675 checkBoxMinimizeToTray.Checked = Properties.Settings.Default.MinimizeToTray;
676 checkBoxStartMinimized.Checked = Properties.Settings.Default.StartMinimized;
677 checkBoxReverseScreen.Checked = cds.ReverseScreen;
678 checkBoxInverseColors.Checked = cds.InverseColors;
679 checkBoxScaleToFit.Checked = cds.ScaleToFit;
680 maskedTextBoxMinFontSize.Enabled = cds.ScaleToFit;
681 labelMinFontSize.Enabled = cds.ScaleToFit;
682 maskedTextBoxMinFontSize.Text = cds.MinFontSize.ToString();
683 comboBoxDisplayType.SelectedIndex = cds.DisplayType;
684 timer.Interval = cds.TimerInterval;
685 maskedTextBoxTimerInterval.Text = cds.TimerInterval.ToString();
686 textBoxScrollLoopSeparator.Text = cds.Separator;
688 SetupPixelDelegates();
690 if (iDisplay.IsOpen())
692 //We have a display connection
693 //Reflect that in our UI
695 //Set our screen size
696 tableLayoutPanel.Width = iDisplay.WidthInPixels();
697 tableLayoutPanel.Height = iDisplay.HeightInPixels();
698 tableLayoutPanel.Enabled = true;
700 //Only setup brightness if display is open
701 trackBarBrightness.Minimum = iDisplay.MinBrightness();
702 trackBarBrightness.Maximum = iDisplay.MaxBrightness();
703 trackBarBrightness.Value = cds.Brightness;
704 trackBarBrightness.LargeChange = Math.Max(1, (iDisplay.MaxBrightness() - iDisplay.MinBrightness()) / 5);
705 trackBarBrightness.SmallChange = 1;
706 iDisplay.SetBrightness(cds.Brightness);
708 buttonFill.Enabled = true;
709 buttonClear.Enabled = true;
710 buttonOpen.Enabled = false;
711 buttonClose.Enabled = true;
712 trackBarBrightness.Enabled = true;
713 toolStripStatusLabelConnect.Text = "Connected - " + iDisplay.Vendor() + " - " + iDisplay.Product();
714 //+ " - " + iDisplay.SerialNumber();
716 if (iDisplay.SupportPowerOnOff())
718 buttonPowerOn.Enabled = true;
719 buttonPowerOff.Enabled = true;
723 buttonPowerOn.Enabled = false;
724 buttonPowerOff.Enabled = false;
727 if (iDisplay.SupportClock())
729 buttonShowClock.Enabled = true;
730 buttonHideClock.Enabled = true;
734 buttonShowClock.Enabled = false;
735 buttonHideClock.Enabled = false;
740 //Display is connection not available
741 //Reflect that in our UI
742 tableLayoutPanel.Enabled = false;
743 buttonFill.Enabled = false;
744 buttonClear.Enabled = false;
745 buttonOpen.Enabled = true;
746 buttonClose.Enabled = false;
747 trackBarBrightness.Enabled = false;
748 buttonPowerOn.Enabled = false;
749 buttonPowerOff.Enabled = false;
750 buttonShowClock.Enabled = false;
751 buttonHideClock.Enabled = false;
752 toolStripStatusLabelConnect.Text = "Disconnected";
753 toolStripStatusLabelPower.Text = "N/A";
759 private void checkBoxShowBorders_CheckedChanged(object sender, EventArgs e)
761 //Save our show borders setting
762 tableLayoutPanel.CellBorderStyle = (checkBoxShowBorders.Checked ? TableLayoutPanelCellBorderStyle.Single : TableLayoutPanelCellBorderStyle.None);
763 cds.ShowBorders = checkBoxShowBorders.Checked;
764 Properties.Settings.Default.Save();
768 private void checkBoxConnectOnStartup_CheckedChanged(object sender, EventArgs e)
770 //Save our connect on startup setting
771 Properties.Settings.Default.DisplayConnectOnStartup = checkBoxConnectOnStartup.Checked;
772 Properties.Settings.Default.Save();
775 private void checkBoxMinimizeToTray_CheckedChanged(object sender, EventArgs e)
777 //Save our "Minimize to tray" setting
778 Properties.Settings.Default.MinimizeToTray = checkBoxMinimizeToTray.Checked;
779 Properties.Settings.Default.Save();
783 private void checkBoxStartMinimized_CheckedChanged(object sender, EventArgs e)
785 //Save our "Start minimized" setting
786 Properties.Settings.Default.StartMinimized = checkBoxStartMinimized.Checked;
787 Properties.Settings.Default.Save();
790 private void checkBoxAutoStart_CheckedChanged(object sender, EventArgs e)
792 iStartupManager.Startup = checkBoxAutoStart.Checked;
796 private void checkBoxReverseScreen_CheckedChanged(object sender, EventArgs e)
798 //Save our reverse screen setting
799 cds.ReverseScreen = checkBoxReverseScreen.Checked;
800 Properties.Settings.Default.Save();
801 SetupPixelDelegates();
804 private void checkBoxInverseColors_CheckedChanged(object sender, EventArgs e)
806 //Save our inverse colors setting
807 cds.InverseColors = checkBoxInverseColors.Checked;
808 Properties.Settings.Default.Save();
809 SetupPixelDelegates();
812 private void checkBoxScaleToFit_CheckedChanged(object sender, EventArgs e)
814 //Save our scale to fit setting
815 cds.ScaleToFit = checkBoxScaleToFit.Checked;
816 Properties.Settings.Default.Save();
818 labelMinFontSize.Enabled = cds.ScaleToFit;
819 maskedTextBoxMinFontSize.Enabled = cds.ScaleToFit;
822 private void MainForm_Resize(object sender, EventArgs e)
824 if (WindowState == FormWindowState.Minimized)
827 //iBmp = new System.Drawing.Bitmap(tableLayoutPanel.Width, tableLayoutPanel.Height, PixelFormat.Format32bppArgb);
828 iCreateBitmap = true;
832 private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
838 public void StartServer()
840 iServiceHost = new ServiceHost
843 new Uri[] { new Uri("net.tcp://localhost:8001/") }
846 iServiceHost.AddServiceEndpoint(typeof(IService), new NetTcpBinding(SecurityMode.None, true), "DisplayService");
850 public void StopServer()
852 if (iClients.Count > 0 && !iClosing)
856 BroadcastCloseEvent();
860 if (MessageBox.Show("Force exit?", "Waiting for clients...", MessageBoxButtons.YesNo, MessageBoxIcon.Warning) == DialogResult.Yes)
862 iClosing = false; //We make sure we force close if asked twice
867 //We removed that as it often lags for some reason
868 //iServiceHost.Close();
872 public void BroadcastCloseEvent()
874 Trace.TraceInformation("BroadcastCloseEvent - start");
876 var inactiveClients = new List<string>();
877 foreach (var client in iClients)
879 //if (client.Key != eventData.ClientName)
883 Trace.TraceInformation("BroadcastCloseEvent - " + client.Key);
884 client.Value.Callback.OnCloseOrder(/*eventData*/);
888 inactiveClients.Add(client.Key);
893 if (inactiveClients.Count > 0)
895 foreach (var client in inactiveClients)
897 iClients.Remove(client);
898 Program.iMainForm.treeViewClients.Nodes.Remove(Program.iMainForm.treeViewClients.Nodes.Find(client, false)[0]);
902 if (iClients.Count==0)
909 /// Just remove all our fields.
911 private void ClearLayout()
913 tableLayoutPanel.Controls.Clear();
914 tableLayoutPanel.RowStyles.Clear();
915 tableLayoutPanel.ColumnStyles.Clear();
918 private void buttonStartClient_Click(object sender, EventArgs e)
920 Thread clientThread = new Thread(SharpDisplayClient.Program.Main);
921 clientThread.Start();
925 private void buttonSuspend_Click(object sender, EventArgs e)
927 LastTickTime = DateTime.Now; //Reset timer to prevent jump
928 timer.Enabled = !timer.Enabled;
931 buttonSuspend.Text = "Run";
935 buttonSuspend.Text = "Pause";
939 private void buttonCloseClients_Click(object sender, EventArgs e)
941 BroadcastCloseEvent();
944 private void treeViewClients_AfterSelect(object sender, TreeViewEventArgs e)
953 /// <param name="aSessionId"></param>
954 /// <param name="aCallback"></param>
955 public void AddClientThreadSafe(string aSessionId, ICallback aCallback)
957 if (this.InvokeRequired)
959 //Not in the proper thread, invoke ourselves
960 AddClientDelegate d = new AddClientDelegate(AddClientThreadSafe);
961 this.Invoke(d, new object[] { aSessionId, aCallback });
965 //We are in the proper thread
966 //Add this session to our collection of clients
967 ClientData newClient = new ClientData(aSessionId, aCallback);
968 Program.iMainForm.iClients.Add(aSessionId, newClient);
969 //Add this session to our UI
970 UpdateClientTreeViewNode(newClient);
977 /// <param name="aSessionId"></param>
978 public void RemoveClientThreadSafe(string aSessionId)
980 if (this.InvokeRequired)
982 //Not in the proper thread, invoke ourselves
983 RemoveClientDelegate d = new RemoveClientDelegate(RemoveClientThreadSafe);
984 this.Invoke(d, new object[] { aSessionId });
988 //We are in the proper thread
989 //Remove this session from both client collection and UI tree view
990 if (Program.iMainForm.iClients.Keys.Contains(aSessionId))
992 Program.iMainForm.iClients.Remove(aSessionId);
993 Program.iMainForm.treeViewClients.Nodes.Remove(Program.iMainForm.treeViewClients.Nodes.Find(aSessionId, false)[0]);
996 if (iClients.Count == 0)
998 //Clear our screen when last client disconnects
1003 //We were closing our form
1004 //All clients are now closed
1005 //Just resume our close operation
1016 /// <param name="aSessionId"></param>
1017 /// <param name="aLayout"></param>
1018 public void SetClientLayoutThreadSafe(string aSessionId, TableLayout aLayout)
1020 if (this.InvokeRequired)
1022 //Not in the proper thread, invoke ourselves
1023 SetLayoutDelegate d = new SetLayoutDelegate(SetClientLayoutThreadSafe);
1024 this.Invoke(d, new object[] { aSessionId, aLayout });
1028 ClientData client = iClients[aSessionId];
1031 client.Layout = aLayout;
1032 UpdateTableLayoutPanel(client);
1034 UpdateClientTreeViewNode(client);
1042 /// <param name="aSessionId"></param>
1043 /// <param name="aField"></param>
1044 public void SetClientFieldThreadSafe(string aSessionId, DataField aField)
1046 if (this.InvokeRequired)
1048 //Not in the proper thread, invoke ourselves
1049 SetFieldDelegate d = new SetFieldDelegate(SetClientFieldThreadSafe);
1050 this.Invoke(d, new object[] { aSessionId, aField });
1054 //We are in the proper thread
1055 //Call the non-thread-safe variant
1056 SetClientField(aSessionId, aField);
1063 /// <param name="aSessionId"></param>
1064 /// <param name="aField"></param>
1065 private void SetClientField(string aSessionId, DataField aField)
1067 SetCurrentClient(aSessionId);
1068 ClientData client = iClients[aSessionId];
1071 bool somethingChanged = false;
1073 //Make sure all our fields are in place
1074 while (client.Fields.Count < (aField.Index + 1))
1076 //Add a text field with proper index
1077 client.Fields.Add(new DataField(client.Fields.Count));
1078 somethingChanged = true;
1081 if (client.Fields[aField.Index].IsSameLayout(aField))
1083 //Same layout just update our field
1084 client.Fields[aField.Index] = aField;
1086 if (aField.IsText && tableLayoutPanel.Controls[aField.Index] is MarqueeLabel)
1088 //Text field control already in place, just change the text
1089 MarqueeLabel label = (MarqueeLabel)tableLayoutPanel.Controls[aField.Index];
1090 somethingChanged = (label.Text != aField.Text || label.TextAlign != aField.Alignment);
1091 label.Text = aField.Text;
1092 label.TextAlign = aField.Alignment;
1094 else if (aField.IsBitmap && tableLayoutPanel.Controls[aField.Index] is PictureBox)
1096 somethingChanged = true; //TODO: Bitmap comp or should we leave that to clients?
1097 //Bitmap field control already in place just change the bitmap
1098 PictureBox pictureBox = (PictureBox)tableLayoutPanel.Controls[aField.Index];
1099 pictureBox.Image = aField.Bitmap;
1103 somethingChanged = true;
1104 //The requested control in our layout it not of the correct type
1105 //Wrong control type, re-create them all
1106 UpdateTableLayoutPanel(iCurrentClientData);
1111 somethingChanged = true;
1112 //Different layout, need to rebuild it
1113 client.Fields[aField.Index] = aField;
1114 UpdateTableLayoutPanel(iCurrentClientData);
1118 if (somethingChanged)
1120 UpdateClientTreeViewNode(client);
1128 /// <param name="aTexts"></param>
1129 public void SetClientFieldsThreadSafe(string aSessionId, System.Collections.Generic.IList<DataField> aFields)
1131 if (this.InvokeRequired)
1133 //Not in the proper thread, invoke ourselves
1134 SetFieldsDelegate d = new SetFieldsDelegate(SetClientFieldsThreadSafe);
1135 this.Invoke(d, new object[] { aSessionId, aFields });
1139 //Put each our text fields in a label control
1140 foreach (DataField field in aFields)
1142 SetClientField(aSessionId, field);
1150 /// <param name="aSessionId"></param>
1151 /// <param name="aName"></param>
1152 public void SetClientNameThreadSafe(string aSessionId, string aName)
1154 if (this.InvokeRequired)
1156 //Not in the proper thread, invoke ourselves
1157 SetClientNameDelegate d = new SetClientNameDelegate(SetClientNameThreadSafe);
1158 this.Invoke(d, new object[] { aSessionId, aName });
1162 //We are in the proper thread
1164 ClientData client = iClients[aSessionId];
1168 client.Name = aName;
1169 //Update our tree-view
1170 UpdateClientTreeViewNode(client);
1178 /// <param name="aClient"></param>
1179 private void UpdateClientTreeViewNode(ClientData aClient)
1181 if (aClient == null)
1186 TreeNode node = null;
1187 //Check that our client node already exists
1188 //Get our client root node using its key which is our session ID
1189 TreeNode[] nodes = treeViewClients.Nodes.Find(aClient.SessionId, false);
1190 if (nodes.Count()>0)
1192 //We already have a node for that client
1194 //Clear children as we are going to recreate them below
1199 //Client node does not exists create a new one
1200 treeViewClients.Nodes.Add(aClient.SessionId, aClient.SessionId);
1201 node = treeViewClients.Nodes.Find(aClient.SessionId, false)[0];
1207 if (aClient.Name != "")
1209 //We have a name, us it as text for our root node
1210 node.Text = aClient.Name;
1211 //Add a child with SessionId
1212 node.Nodes.Add(new TreeNode(aClient.SessionId));
1216 //No name, use session ID instead
1217 node.Text = aClient.SessionId;
1220 if (aClient.Fields.Count > 0)
1222 //Create root node for our texts
1223 TreeNode textsRoot = new TreeNode("Fields");
1224 node.Nodes.Add(textsRoot);
1225 //For each text add a new entry
1226 foreach (DataField field in aClient.Fields)
1228 if (!field.IsBitmap)
1230 DataField textField = (DataField)field;
1231 textsRoot.Nodes.Add(new TreeNode("[Text]" + textField.Text));
1235 textsRoot.Nodes.Add(new TreeNode("[Bitmap]"));
1244 private void buttonAddRow_Click(object sender, EventArgs e)
1246 if (tableLayoutPanel.RowCount < 6)
1248 UpdateTableLayoutPanel(tableLayoutPanel.ColumnCount, tableLayoutPanel.RowCount + 1);
1252 private void buttonRemoveRow_Click(object sender, EventArgs e)
1254 if (tableLayoutPanel.RowCount > 1)
1256 UpdateTableLayoutPanel(tableLayoutPanel.ColumnCount, tableLayoutPanel.RowCount - 1);
1259 UpdateTableLayoutRowStyles();
1262 private void buttonAddColumn_Click(object sender, EventArgs e)
1264 if (tableLayoutPanel.ColumnCount < 8)
1266 UpdateTableLayoutPanel(tableLayoutPanel.ColumnCount + 1, tableLayoutPanel.RowCount);
1270 private void buttonRemoveColumn_Click(object sender, EventArgs e)
1272 if (tableLayoutPanel.ColumnCount > 1)
1274 UpdateTableLayoutPanel(tableLayoutPanel.ColumnCount - 1, tableLayoutPanel.RowCount);
1280 /// Update our table layout row styles to make sure each rows have similar height
1282 private void UpdateTableLayoutRowStyles()
1284 foreach (RowStyle rowStyle in tableLayoutPanel.RowStyles)
1286 rowStyle.SizeType = SizeType.Percent;
1287 rowStyle.Height = 100 / tableLayoutPanel.RowCount;
1293 /// Empty and recreate our table layout with the given number of columns and rows.
1294 /// Sizes of rows and columns are uniform.
1296 /// <param name="aColumn"></param>
1297 /// <param name="aRow"></param>
1298 private void UpdateTableLayoutPanel(int aColumn, int aRow)
1300 tableLayoutPanel.Controls.Clear();
1301 tableLayoutPanel.RowStyles.Clear();
1302 tableLayoutPanel.ColumnStyles.Clear();
1303 tableLayoutPanel.RowCount = 0;
1304 tableLayoutPanel.ColumnCount = 0;
1306 while (tableLayoutPanel.RowCount < aRow)
1308 tableLayoutPanel.RowCount++;
1311 while (tableLayoutPanel.ColumnCount < aColumn)
1313 tableLayoutPanel.ColumnCount++;
1316 for (int i = 0; i < tableLayoutPanel.ColumnCount; i++)
1318 //Create our column styles
1319 this.tableLayoutPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100 / tableLayoutPanel.ColumnCount));
1321 for (int j = 0; j < tableLayoutPanel.RowCount; j++)
1325 //Create our row styles
1326 this.tableLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100 / tableLayoutPanel.RowCount));
1329 MarqueeLabel control = new SharpDisplayManager.MarqueeLabel();
1330 control.AutoEllipsis = true;
1331 control.AutoSize = true;
1332 control.BackColor = System.Drawing.Color.Transparent;
1333 control.Dock = System.Windows.Forms.DockStyle.Fill;
1334 control.Location = new System.Drawing.Point(1, 1);
1335 control.Margin = new System.Windows.Forms.Padding(0);
1336 control.Name = "marqueeLabelCol" + aColumn + "Row" + aRow;
1337 control.OwnTimer = false;
1338 control.PixelsPerSecond = 64;
1339 control.Separator = cds.Separator;
1340 control.MinFontSize = cds.MinFontSize;
1341 control.ScaleToFit = cds.ScaleToFit;
1342 //control.Size = new System.Drawing.Size(254, 30);
1343 //control.TabIndex = 2;
1344 control.Font = cds.Font;
1345 control.Text = "ABCDEFGHIJKLMNOPQRST-0123456789";
1346 control.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
1347 control.UseCompatibleTextRendering = true;
1349 tableLayoutPanel.Controls.Add(control, i, j);
1358 /// Update our display table layout.
1360 /// <param name="aLayout"></param>
1361 private void UpdateTableLayoutPanel(ClientData aClient)
1363 TableLayout layout = aClient.Layout;
1366 tableLayoutPanel.Controls.Clear();
1367 tableLayoutPanel.RowStyles.Clear();
1368 tableLayoutPanel.ColumnStyles.Clear();
1369 tableLayoutPanel.RowCount = 0;
1370 tableLayoutPanel.ColumnCount = 0;
1372 while (tableLayoutPanel.RowCount < layout.Rows.Count)
1374 tableLayoutPanel.RowCount++;
1377 while (tableLayoutPanel.ColumnCount < layout.Columns.Count)
1379 tableLayoutPanel.ColumnCount++;
1382 for (int i = 0; i < tableLayoutPanel.ColumnCount; i++)
1384 //Create our column styles
1385 this.tableLayoutPanel.ColumnStyles.Add(layout.Columns[i]);
1387 for (int j = 0; j < tableLayoutPanel.RowCount; j++)
1391 //Create our row styles
1392 this.tableLayoutPanel.RowStyles.Add(layout.Rows[j]);
1395 //Check if we already have a control
1396 Control existingControl = tableLayoutPanel.GetControlFromPosition(i,j);
1397 if (existingControl!=null)
1399 //We already have a control in that cell as a results of row/col spanning
1400 //Move on to next cell then
1406 //Check if a client field already exists for that cell
1407 if (aClient.Fields.Count <= tableLayoutPanel.Controls.Count)
1409 //No client field specified, create a text field by default
1410 aClient.Fields.Add(new DataField(aClient.Fields.Count));
1413 //Create a control corresponding to the field specified for that cell
1414 DataField field = aClient.Fields[tableLayoutPanel.Controls.Count];
1415 Control control = CreateControlForDataField(field);
1417 //Add newly created control to our table layout at the specified row and column
1418 tableLayoutPanel.Controls.Add(control, i, j);
1419 //Make sure we specify row and column span for that new control
1420 tableLayoutPanel.SetRowSpan(control,field.RowSpan);
1421 tableLayoutPanel.SetColumnSpan(control, field.ColumnSpan);
1426 while (aClient.Fields.Count > fieldCount)
1428 //We have too much fields for this layout
1429 //Just discard them until we get there
1430 aClient.Fields.RemoveAt(aClient.Fields.Count-1);
1437 /// Check our type of data field and create corresponding control
1439 /// <param name="aField"></param>
1440 private Control CreateControlForDataField(DataField aField)
1442 Control control=null;
1443 if (!aField.IsBitmap)
1445 MarqueeLabel label = new SharpDisplayManager.MarqueeLabel();
1446 label.AutoEllipsis = true;
1447 label.AutoSize = true;
1448 label.BackColor = System.Drawing.Color.Transparent;
1449 label.Dock = System.Windows.Forms.DockStyle.Fill;
1450 label.Location = new System.Drawing.Point(1, 1);
1451 label.Margin = new System.Windows.Forms.Padding(0);
1452 label.Name = "marqueeLabel" + aField.Index;
1453 label.OwnTimer = false;
1454 label.PixelsPerSecond = 64;
1455 label.Separator = cds.Separator;
1456 label.MinFontSize = cds.MinFontSize;
1457 label.ScaleToFit = cds.ScaleToFit;
1458 //control.Size = new System.Drawing.Size(254, 30);
1459 //control.TabIndex = 2;
1460 label.Font = cds.Font;
1462 label.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
1463 label.UseCompatibleTextRendering = true;
1464 label.Text = aField.Text;
1470 //Create picture box
1471 PictureBox picture = new PictureBox();
1472 picture.AutoSize = true;
1473 picture.BackColor = System.Drawing.Color.Transparent;
1474 picture.Dock = System.Windows.Forms.DockStyle.Fill;
1475 picture.Location = new System.Drawing.Point(1, 1);
1476 picture.Margin = new System.Windows.Forms.Padding(0);
1477 picture.Name = "pictureBox" + aField;
1479 picture.Image = aField.Bitmap;
1488 private void buttonAlignLeft_Click(object sender, EventArgs e)
1490 foreach (MarqueeLabel ctrl in tableLayoutPanel.Controls)
1492 ctrl.TextAlign = ContentAlignment.MiddleLeft;
1496 private void buttonAlignCenter_Click(object sender, EventArgs e)
1498 foreach (MarqueeLabel ctrl in tableLayoutPanel.Controls)
1500 ctrl.TextAlign = ContentAlignment.MiddleCenter;
1504 private void buttonAlignRight_Click(object sender, EventArgs e)
1506 foreach (MarqueeLabel ctrl in tableLayoutPanel.Controls)
1508 ctrl.TextAlign = ContentAlignment.MiddleRight;
1513 /// Called when the user selected a new display type.
1515 /// <param name="sender"></param>
1516 /// <param name="e"></param>
1517 private void comboBoxDisplayType_SelectedIndexChanged(object sender, EventArgs e)
1519 //Store the selected display type in our settings
1520 Properties.Settings.Default.CurrentDisplayIndex = comboBoxDisplayType.SelectedIndex;
1521 cds.DisplayType = comboBoxDisplayType.SelectedIndex;
1522 Properties.Settings.Default.Save();
1524 //Try re-opening the display connection if we were already connected.
1525 //Otherwise just update our status to reflect display type change.
1526 if (iDisplay.IsOpen())
1528 OpenDisplayConnection();
1536 private void maskedTextBoxTimerInterval_TextChanged(object sender, EventArgs e)
1538 if (maskedTextBoxTimerInterval.Text != "")
1540 int interval = Convert.ToInt32(maskedTextBoxTimerInterval.Text);
1544 timer.Interval = interval;
1545 cds.TimerInterval = timer.Interval;
1546 Properties.Settings.Default.Save();
1551 private void maskedTextBoxMinFontSize_TextChanged(object sender, EventArgs e)
1553 if (maskedTextBoxMinFontSize.Text != "")
1555 int minFontSize = Convert.ToInt32(maskedTextBoxMinFontSize.Text);
1557 if (minFontSize > 0)
1559 //TODO: re-create layout? update our fields?
1560 cds.MinFontSize = minFontSize;
1561 Properties.Settings.Default.Save();
1566 private void textBoxScrollLoopSeparator_TextChanged(object sender, EventArgs e)
1568 //TODO: re-create layout? update our fields?
1569 cds.Separator = textBoxScrollLoopSeparator.Text;
1570 Properties.Settings.Default.Save();
1573 private void buttonPowerOn_Click(object sender, EventArgs e)
1578 private void buttonPowerOff_Click(object sender, EventArgs e)
1580 iDisplay.PowerOff();
1583 private void buttonShowClock_Click(object sender, EventArgs e)
1585 iDisplay.ShowClock();
1588 private void buttonHideClock_Click(object sender, EventArgs e)
1590 iDisplay.HideClock();
1593 private void buttonUpdate_Click(object sender, EventArgs e)
1595 InstallUpdateSyncWithInfo();
1599 private void InstallUpdateSyncWithInfo()
1601 UpdateCheckInfo info = null;
1603 if (ApplicationDeployment.IsNetworkDeployed)
1605 ApplicationDeployment ad = ApplicationDeployment.CurrentDeployment;
1609 info = ad.CheckForDetailedUpdate();
1612 catch (DeploymentDownloadException dde)
1614 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);
1617 catch (InvalidDeploymentException ide)
1619 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);
1622 catch (InvalidOperationException ioe)
1624 MessageBox.Show("This application cannot be updated. It is likely not a ClickOnce application. Error: " + ioe.Message);
1628 if (info.UpdateAvailable)
1630 Boolean doUpdate = true;
1632 if (!info.IsUpdateRequired)
1634 DialogResult dr = MessageBox.Show("An update is available. Would you like to update the application now?", "Update Available", MessageBoxButtons.OKCancel);
1635 if (!(DialogResult.OK == dr))
1642 // Display a message that the app MUST reboot. Display the minimum required version.
1643 MessageBox.Show("This application has detected a mandatory update from your current " +
1644 "version to version " + info.MinimumRequiredVersion.ToString() +
1645 ". The application will now install the update and restart.",
1646 "Update Available", MessageBoxButtons.OK,
1647 MessageBoxIcon.Information);
1655 MessageBox.Show("The application has been upgraded, and will now restart.");
1656 Application.Restart();
1658 catch (DeploymentDownloadException dde)
1660 MessageBox.Show("Cannot install the latest version of the application. \n\nPlease check your network connection, or try again later. Error: " + dde);
1667 MessageBox.Show("You are already running the latest version.", "Application up-to-date");
1676 private void SysTrayHideShow()
1682 WindowState = FormWindowState.Normal;
1687 /// Use to handle minimize events.
1689 /// <param name="sender"></param>
1690 /// <param name="e"></param>
1691 private void MainForm_SizeChanged(object sender, EventArgs e)
1693 if (WindowState == FormWindowState.Minimized && Properties.Settings.Default.MinimizeToTray)
1709 /// A UI thread copy of a client relevant data.
1710 /// Keeping this copy in the UI thread helps us deal with threading issues.
1712 public class ClientData
1714 public ClientData(string aSessionId, ICallback aCallback)
1716 SessionId = aSessionId;
1718 Fields = new List<DataField>();
1719 Layout = new TableLayout(1, 2); //Default to one column and two rows
1720 Callback = aCallback;
1723 public string SessionId { get; set; }
1724 public string Name { get; set; }
1725 public List<DataField> Fields { get; set; }
1726 public TableLayout Layout { get; set; }
1727 public ICallback Callback { get; set; }