MDM166AA: Adding support for volume label.
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 NAudio.CoreAudioApi;
20 using NAudio.CoreAudioApi.Interfaces;
21 using System.Runtime.InteropServices;
23 using SharpDisplayClient;
26 namespace SharpDisplayManager
29 public delegate uint ColorProcessingDelegate(int aX, int aY, uint aPixel);
30 public delegate int CoordinateTranslationDelegate(System.Drawing.Bitmap aBmp, int aInt);
31 //Delegates are used for our thread safe method
32 public delegate void AddClientDelegate(string aSessionId, ICallback aCallback);
33 public delegate void RemoveClientDelegate(string aSessionId);
34 public delegate void SetFieldDelegate(string SessionId, DataField aField);
35 public delegate void SetFieldsDelegate(string SessionId, System.Collections.Generic.IList<DataField> aFields);
36 public delegate void SetLayoutDelegate(string SessionId, TableLayout aLayout);
37 public delegate void SetClientNameDelegate(string aSessionId, string aName);
38 public delegate void PlainUpdateDelegate();
42 /// Our Display manager main form
44 public partial class MainForm : Form, IMMNotificationClient
46 DateTime LastTickTime;
48 System.Drawing.Bitmap iBmp;
49 bool iCreateBitmap; //Workaround render to bitmap issues when minimized
50 ServiceHost iServiceHost;
51 // Our collection of clients sorted by session id.
52 public Dictionary<string, ClientData> iClients;
53 // The name of the client which informations are currently displayed.
54 public string iCurrentClientSessionId;
55 ClientData iCurrentClientData;
58 //Function pointer for pixel color filtering
59 ColorProcessingDelegate iColorFx;
60 //Function pointer for pixel X coordinate intercept
61 CoordinateTranslationDelegate iScreenX;
62 //Function pointer for pixel Y coordinate intercept
63 CoordinateTranslationDelegate iScreenY;
65 private MMDeviceEnumerator iMultiMediaDeviceEnumerator;
66 private MMDevice iMultiMediaDevice;
70 /// Manage run when Windows startup option
72 private StartupManager iStartupManager;
77 private NotifyIconAdv iNotifyIcon;
81 iCurrentClientSessionId = "";
82 iCurrentClientData = null;
83 LastTickTime = DateTime.Now;
84 //Instantiate our display and register for events notifications
85 iDisplay = new Display();
86 iDisplay.OnOpened += OnDisplayOpened;
87 iDisplay.OnClosed += OnDisplayClosed;
89 iClients = new Dictionary<string, ClientData>();
90 iStartupManager = new StartupManager();
91 iNotifyIcon = new NotifyIconAdv();
93 //Have our designer initialize its controls
94 InitializeComponent();
96 //Populate device types
97 PopulateDeviceTypes();
99 //Initial status update
102 //We have a bug when drawing minimized and reusing our bitmap
103 iBmp = new System.Drawing.Bitmap(tableLayoutPanel.Width, tableLayoutPanel.Height, PixelFormat.Format32bppArgb);
104 iCreateBitmap = false;
106 //Minimize our window if desired
107 if (Properties.Settings.Default.StartMinimized)
109 WindowState = FormWindowState.Minimized;
117 /// <param name="sender"></param>
118 /// <param name="e"></param>
119 private void MainForm_Load(object sender, EventArgs e)
121 //Check if we are running a Click Once deployed application
122 if (ApplicationDeployment.IsNetworkDeployed)
124 //This is a proper Click Once installation, fetch and show our version number
125 this.Text += " - v" + ApplicationDeployment.CurrentDeployment.CurrentVersion;
129 //Not a proper Click Once installation, assuming development build then
130 this.Text += " - development";
134 iMultiMediaDeviceEnumerator = new MMDeviceEnumerator();
135 iMultiMediaDeviceEnumerator.RegisterEndpointNotificationCallback(this);
137 UpdateAudioDeviceAndMasterVolumeThreadSafe();
139 //Setup notification icon
142 // To make sure start up with minimize to tray works
143 if (WindowState == FormWindowState.Minimized && Properties.Settings.Default.MinimizeToTray)
149 //When not debugging we want the screen to be empty until a client takes over
152 //When developing we want at least one client for testing
153 StartNewClient("abcdefghijklmnopqrst-0123456789","ABCDEFGHIJKLMNOPQRST-0123456789");
156 //Open display connection on start-up if needed
157 if (Properties.Settings.Default.DisplayConnectOnStartup)
159 OpenDisplayConnection();
162 //Start our server so that we can get client requests
167 /// Called when our display is opened.
169 /// <param name="aDisplay"></param>
170 private void OnDisplayOpened(Display aDisplay)
172 //Set our screen size now that our display is connected
173 //Our panelDisplay is the container of our tableLayoutPanel
174 //tableLayoutPanel will resize itself to fit the client size of our panelDisplay
175 //panelDisplay needs an extra 2 pixels for borders on each sides
176 //tableLayoutPanel will eventually be the exact size of our display
177 Size size = new Size(iDisplay.WidthInPixels() + 2, iDisplay.HeightInPixels() + 2);
178 panelDisplay.Size = size;
180 //Our display was just opened, update our UI
182 //Initiate asynchronous request
183 iDisplay.RequestFirmwareRevision();
186 UpdateMasterVolumeThreadSafe();
189 //Testing icon in debug, no arm done if icon not supported
190 //iDisplay.SetIconStatus(Display.TMiniDisplayIconType.EMiniDisplayIconRecording, 0, 1);
191 //iDisplay.SetAllIconsStatus(2);
197 /// Called when our display is closed.
199 /// <param name="aDisplay"></param>
200 private void OnDisplayClosed(Display aDisplay)
202 //Our display was just closed, update our UI consequently
207 /// Receive volume change notification and reflect changes on our slider.
209 /// <param name="data"></param>
210 public void OnVolumeNotificationThreadSafe(AudioVolumeNotificationData data)
212 UpdateMasterVolumeThreadSafe();
216 /// Update master volume when user moves our slider.
218 /// <param name="sender"></param>
219 /// <param name="e"></param>
220 private void trackBarMasterVolume_Scroll(object sender, EventArgs e)
222 iMultiMediaDevice.AudioEndpointVolume.MasterVolumeLevelScalar = trackBarMasterVolume.Value / 100.0f;
227 /// Mute check box changed.
229 /// <param name="sender"></param>
230 /// <param name="e"></param>
231 private void checkBoxMute_CheckedChanged(object sender, EventArgs e)
233 iMultiMediaDevice.AudioEndpointVolume.Mute = checkBoxMute.Checked;
237 /// Device State Changed
239 public void OnDeviceStateChanged([MarshalAs(UnmanagedType.LPWStr)] string deviceId, [MarshalAs(UnmanagedType.I4)] DeviceState newState){}
244 public void OnDeviceAdded([MarshalAs(UnmanagedType.LPWStr)] string pwstrDeviceId) { }
249 public void OnDeviceRemoved([MarshalAs(UnmanagedType.LPWStr)] string deviceId) { }
252 /// Default Device Changed
254 public void OnDefaultDeviceChanged(DataFlow flow, Role role, [MarshalAs(UnmanagedType.LPWStr)] string defaultDeviceId)
256 if (role == Role.Multimedia && flow == DataFlow.Render)
258 UpdateAudioDeviceAndMasterVolumeThreadSafe();
263 /// Property Value Changed
265 /// <param name="pwstrDeviceId"></param>
266 /// <param name="key"></param>
267 public void OnPropertyValueChanged([MarshalAs(UnmanagedType.LPWStr)] string pwstrDeviceId, PropertyKey key){}
275 private void UpdateMasterVolumeThreadSafe()
277 if (this.InvokeRequired)
279 //Not in the proper thread, invoke ourselves
280 PlainUpdateDelegate d = new PlainUpdateDelegate(UpdateMasterVolumeThreadSafe);
281 this.Invoke(d, new object[] { });
285 //Update volume slider
286 float volumeLevelScalar = iMultiMediaDevice.AudioEndpointVolume.MasterVolumeLevelScalar;
287 trackBarMasterVolume.Value = Convert.ToInt32(volumeLevelScalar * 100);
288 //Update mute checkbox
289 checkBoxMute.Checked = iMultiMediaDevice.AudioEndpointVolume.Mute;
291 //TODO: Check our display device too
292 if (iDisplay.IsOpen())
294 int volumeIconCount = iDisplay.IconCount(Display.TMiniDisplayIconType.EMiniDisplayIconVolume);
295 if (volumeIconCount > 0)
297 int currentVolume = Convert.ToInt32(volumeLevelScalar * volumeIconCount);
299 bool roundedUp = currentVolume > (volumeLevelScalar * volumeIconCount);
301 for (int i = 0; i < volumeIconCount; i++)
303 if (i < currentVolume)
305 if (i == currentVolume - 1 && roundedUp)
308 iDisplay.SetIconStatus(Display.TMiniDisplayIconType.EMiniDisplayIconVolume, i, (iDisplay.IconStatusCount(Display.TMiniDisplayIconType.EMiniDisplayIconVolume) - 1)/2);
313 iDisplay.SetIconStatus(Display.TMiniDisplayIconType.EMiniDisplayIconVolume, i, iDisplay.IconStatusCount(Display.TMiniDisplayIconType.EMiniDisplayIconVolume) - 1);
318 iDisplay.SetIconStatus(Display.TMiniDisplayIconType.EMiniDisplayIconVolume, i, 0);
323 int muteIconCount = iDisplay.IconCount(Display.TMiniDisplayIconType.EMiniDisplayIconMute);
324 if (muteIconCount > 0)
326 for (int i = 0; i < muteIconCount; i++)
328 if (iMultiMediaDevice.AudioEndpointVolume.Mute)
330 iDisplay.SetIconStatus(Display.TMiniDisplayIconType.EMiniDisplayIconMute, i, iDisplay.IconStatusCount(Display.TMiniDisplayIconType.EMiniDisplayIconMute)-1);
334 iDisplay.SetIconStatus(Display.TMiniDisplayIconType.EMiniDisplayIconMute, i, 0);
346 private void UpdateAudioDeviceAndMasterVolumeThreadSafe()
348 if (this.InvokeRequired)
350 //Not in the proper thread, invoke ourselves
351 PlainUpdateDelegate d = new PlainUpdateDelegate(UpdateAudioDeviceAndMasterVolumeThreadSafe);
352 this.Invoke(d, new object[] { });
356 //We are in the correct thread just go ahead.
359 //Get our master volume
360 iMultiMediaDevice = iMultiMediaDeviceEnumerator.GetDefaultAudioEndpoint(DataFlow.Render, Role.Multimedia);
361 //Show our volume in our track bar
362 UpdateMasterVolumeThreadSafe();
364 //Register to get volume modifications
365 iMultiMediaDevice.AudioEndpointVolume.OnVolumeNotification += OnVolumeNotificationThreadSafe;
367 trackBarMasterVolume.Enabled = true;
371 Debug.WriteLine("Exception thrown in UpdateAudioDeviceAndMasterVolume");
372 Debug.WriteLine(ex.ToString());
373 //Something went wrong S/PDIF device ca throw exception I guess
374 trackBarMasterVolume.Enabled = false;
381 private void PopulateDeviceTypes()
383 int count = Display.TypeCount();
385 for (int i = 0; i < count; i++)
387 comboBoxDisplayType.Items.Add(Display.TypeName((Display.TMiniDisplayType)i));
394 private void SetupTrayIcon()
396 iNotifyIcon.Icon = GetIcon("vfd.ico");
397 iNotifyIcon.Text = "Sharp Display Manager";
398 iNotifyIcon.Visible = true;
400 //Double click toggles visibility - typically brings up the application
401 iNotifyIcon.DoubleClick += delegate(object obj, EventArgs args)
406 //Adding a context menu, useful to be able to exit the application
407 ContextMenu contextMenu = new ContextMenu();
408 //Context menu item to toggle visibility
409 MenuItem hideShowItem = new MenuItem("Hide/Show");
410 hideShowItem.Click += delegate(object obj, EventArgs args)
414 contextMenu.MenuItems.Add(hideShowItem);
416 //Context menu item separator
417 contextMenu.MenuItems.Add(new MenuItem("-"));
419 //Context menu exit item
420 MenuItem exitItem = new MenuItem("Exit");
421 exitItem.Click += delegate(object obj, EventArgs args)
425 contextMenu.MenuItems.Add(exitItem);
427 iNotifyIcon.ContextMenu = contextMenu;
431 /// Access icons from embedded resources.
433 /// <param name="name"></param>
434 /// <returns></returns>
435 public static Icon GetIcon(string name)
437 name = "SharpDisplayManager.Resources." + name;
440 Assembly.GetExecutingAssembly().GetManifestResourceNames();
441 for (int i = 0; i < names.Length; i++)
443 if (names[i].Replace('\\', '.') == name)
445 using (Stream stream = Assembly.GetExecutingAssembly().
446 GetManifestResourceStream(names[i]))
448 return new Icon(stream);
458 /// Set our current client.
459 /// This will take care of applying our client layout and set data fields.
461 /// <param name="aSessionId"></param>
462 void SetCurrentClient(string aSessionId)
464 if (aSessionId == iCurrentClientSessionId)
466 //Given client is already the current one.
467 //Don't bother changing anything then.
471 //Set current client ID.
472 iCurrentClientSessionId = aSessionId;
473 //Fetch and set current client data.
474 iCurrentClientData = iClients[aSessionId];
475 //Apply layout and set data fields.
476 UpdateTableLayoutPanel(iCurrentClientData);
479 private void buttonFont_Click(object sender, EventArgs e)
481 //fontDialog.ShowColor = true;
482 //fontDialog.ShowApply = true;
483 fontDialog.ShowEffects = true;
484 fontDialog.Font = cds.Font;
486 fontDialog.FixedPitchOnly = checkBoxFixedPitchFontOnly.Checked;
488 //fontDialog.ShowHelp = true;
490 //fontDlg.MaxSize = 40;
491 //fontDlg.MinSize = 22;
493 //fontDialog.Parent = this;
494 //fontDialog.StartPosition = FormStartPosition.CenterParent;
496 //DlgBox.ShowDialog(fontDialog);
498 //if (fontDialog.ShowDialog(this) != DialogResult.Cancel)
499 if (DlgBox.ShowDialog(fontDialog) != DialogResult.Cancel)
501 //Set the fonts to all our labels in our layout
502 foreach (Control ctrl in tableLayoutPanel.Controls)
504 if (ctrl is MarqueeLabel)
506 ((MarqueeLabel)ctrl).Font = fontDialog.Font;
511 cds.Font = fontDialog.Font;
512 Properties.Settings.Default.Save();
521 void CheckFontHeight()
523 //Show font height and width
524 labelFontHeight.Text = "Font height: " + cds.Font.Height;
525 float charWidth = IsFixedWidth(cds.Font);
526 if (charWidth == 0.0f)
528 labelFontWidth.Visible = false;
532 labelFontWidth.Visible = true;
533 labelFontWidth.Text = "Font width: " + charWidth;
536 MarqueeLabel label = null;
537 //Get the first label control we can find
538 foreach (Control ctrl in tableLayoutPanel.Controls)
540 if (ctrl is MarqueeLabel)
542 label = (MarqueeLabel)ctrl;
547 //Now check font height and show a warning if needed.
548 if (label != null && label.Font.Height > label.Height)
550 labelWarning.Text = "WARNING: Selected font is too height by " + (label.Font.Height - label.Height) + " pixels!";
551 labelWarning.Visible = true;
555 labelWarning.Visible = false;
560 private void buttonCapture_Click(object sender, EventArgs e)
562 System.Drawing.Bitmap bmp = new System.Drawing.Bitmap(tableLayoutPanel.Width, tableLayoutPanel.Height);
563 tableLayoutPanel.DrawToBitmap(bmp, tableLayoutPanel.ClientRectangle);
564 //Bitmap bmpToSave = new Bitmap(bmp);
565 bmp.Save("D:\\capture.png");
567 ((MarqueeLabel)tableLayoutPanel.Controls[0]).Text = "Captured";
570 string outputFileName = "d:\\capture.png";
571 using (MemoryStream memory = new MemoryStream())
573 using (FileStream fs = new FileStream(outputFileName, FileMode.OpenOrCreate, FileAccess.ReadWrite))
575 bmp.Save(memory, System.Drawing.Imaging.ImageFormat.Png);
576 byte[] bytes = memory.ToArray();
577 fs.Write(bytes, 0, bytes.Length);
584 private void CheckForRequestResults()
586 if (iDisplay.IsRequestPending())
588 switch (iDisplay.AttemptRequestCompletion())
590 case Display.TMiniDisplayRequest.EMiniDisplayRequestFirmwareRevision:
591 toolStripStatusLabelConnect.Text += " v" + iDisplay.FirmwareRevision();
592 //Issue next request then
593 iDisplay.RequestPowerSupplyStatus();
596 case Display.TMiniDisplayRequest.EMiniDisplayRequestPowerSupplyStatus:
597 if (iDisplay.PowerSupplyStatus())
599 toolStripStatusLabelPower.Text = "ON";
603 toolStripStatusLabelPower.Text = "OFF";
605 //Issue next request then
606 iDisplay.RequestDeviceId();
609 case Display.TMiniDisplayRequest.EMiniDisplayRequestDeviceId:
610 toolStripStatusLabelConnect.Text += " - " + iDisplay.DeviceId();
611 //No more request to issue
617 public static uint ColorWhiteIsOn(int aX, int aY, uint aPixel)
619 if ((aPixel & 0x00FFFFFF) == 0x00FFFFFF)
626 public static uint ColorUntouched(int aX, int aY, uint aPixel)
631 public static uint ColorInversed(int aX, int aY, uint aPixel)
636 public static uint ColorChessboard(int aX, int aY, uint aPixel)
638 if ((aX % 2 == 0) && (aY % 2 == 0))
642 else if ((aX % 2 != 0) && (aY % 2 != 0))
650 public static int ScreenReversedX(System.Drawing.Bitmap aBmp, int aX)
652 return aBmp.Width - aX - 1;
655 public int ScreenReversedY(System.Drawing.Bitmap aBmp, int aY)
657 return iBmp.Height - aY - 1;
660 public int ScreenX(System.Drawing.Bitmap aBmp, int aX)
665 public int ScreenY(System.Drawing.Bitmap aBmp, int aY)
671 /// Select proper pixel delegates according to our current settings.
673 private void SetupPixelDelegates()
675 //Select our pixel processing routine
676 if (cds.InverseColors)
678 //iColorFx = ColorChessboard;
679 iColorFx = ColorInversed;
683 iColorFx = ColorWhiteIsOn;
686 //Select proper coordinate translation functions
687 //We used delegate/function pointer to support reverse screen without doing an extra test on each pixels
688 if (cds.ReverseScreen)
690 iScreenX = ScreenReversedX;
691 iScreenY = ScreenReversedY;
701 //This is our timer tick responsible to perform our render
702 private void timer_Tick(object sender, EventArgs e)
704 //Update our animations
705 DateTime NewTickTime = DateTime.Now;
707 //Update animation for all our marquees
708 foreach (Control ctrl in tableLayoutPanel.Controls)
710 if (ctrl is MarqueeLabel)
712 ((MarqueeLabel)ctrl).UpdateAnimation(LastTickTime, NewTickTime);
718 if (iDisplay.IsOpen())
720 CheckForRequestResults();
725 iBmp = new System.Drawing.Bitmap(tableLayoutPanel.Width, tableLayoutPanel.Height, PixelFormat.Format32bppArgb);
727 tableLayoutPanel.DrawToBitmap(iBmp, tableLayoutPanel.ClientRectangle);
728 //iBmp.Save("D:\\capture.png");
730 //Send it to our display
731 for (int i = 0; i < iBmp.Width; i++)
733 for (int j = 0; j < iBmp.Height; j++)
737 //Get our processed pixel coordinates
738 int x = iScreenX(iBmp, i);
739 int y = iScreenY(iBmp, j);
741 uint color = (uint)iBmp.GetPixel(i, j).ToArgb();
742 //Apply color effects
743 color = iColorFx(x,y,color);
745 iDisplay.SetPixel(x, y, color);
750 iDisplay.SwapBuffers();
754 //Compute instant FPS
755 toolStripStatusLabelFps.Text = (1.0/NewTickTime.Subtract(LastTickTime).TotalSeconds).ToString("F0") + " / " + (1000/timer.Interval).ToString() + " FPS";
757 LastTickTime = NewTickTime;
762 /// Attempt to establish connection with our display hardware.
764 private void OpenDisplayConnection()
766 CloseDisplayConnection();
768 if (!iDisplay.Open((Display.TMiniDisplayType)cds.DisplayType))
771 toolStripStatusLabelConnect.Text = "Connection error";
775 private void CloseDisplayConnection()
777 //Status will be updated upon receiving the closed event
781 private void buttonOpen_Click(object sender, EventArgs e)
783 OpenDisplayConnection();
786 private void buttonClose_Click(object sender, EventArgs e)
788 CloseDisplayConnection();
791 private void buttonClear_Click(object sender, EventArgs e)
794 iDisplay.SwapBuffers();
797 private void buttonFill_Click(object sender, EventArgs e)
800 iDisplay.SwapBuffers();
803 private void trackBarBrightness_Scroll(object sender, EventArgs e)
805 cds.Brightness = trackBarBrightness.Value;
806 Properties.Settings.Default.Save();
807 iDisplay.SetBrightness(trackBarBrightness.Value);
813 /// CDS stands for Current Display Settings
815 private DisplaySettings cds
819 DisplaysSettings settings = Properties.Settings.Default.DisplaysSettings;
820 if (settings == null)
822 settings = new DisplaysSettings();
824 Properties.Settings.Default.DisplaysSettings = settings;
827 //Make sure all our settings have been created
828 while (settings.Displays.Count <= Properties.Settings.Default.CurrentDisplayIndex)
830 settings.Displays.Add(new DisplaySettings());
833 DisplaySettings displaySettings = settings.Displays[Properties.Settings.Default.CurrentDisplayIndex];
834 return displaySettings;
839 /// Check if the given font has a fixed character pitch.
841 /// <param name="ft"></param>
842 /// <returns>0.0f if this is not a monospace font, otherwise returns the character width.</returns>
843 public float IsFixedWidth(Font ft)
845 Graphics g = CreateGraphics();
846 char[] charSizes = new char[] { 'i', 'a', 'Z', '%', '#', 'a', 'B', 'l', 'm', ',', '.' };
847 float charWidth = g.MeasureString("I", ft, Int32.MaxValue, StringFormat.GenericTypographic).Width;
849 bool fixedWidth = true;
851 foreach (char c in charSizes)
852 if (g.MeasureString(c.ToString(), ft, Int32.MaxValue, StringFormat.GenericTypographic).Width != charWidth)
864 /// Synchronize UI with settings
866 private void UpdateStatus()
869 checkBoxShowBorders.Checked = cds.ShowBorders;
870 tableLayoutPanel.CellBorderStyle = (cds.ShowBorders ? TableLayoutPanelCellBorderStyle.Single : TableLayoutPanelCellBorderStyle.None);
872 //Set the proper font to each of our labels
873 foreach (MarqueeLabel ctrl in tableLayoutPanel.Controls)
875 ctrl.Font = cds.Font;
879 //Check if "run on Windows startup" is enabled
880 checkBoxAutoStart.Checked = iStartupManager.Startup;
882 checkBoxConnectOnStartup.Checked = Properties.Settings.Default.DisplayConnectOnStartup;
883 checkBoxMinimizeToTray.Checked = Properties.Settings.Default.MinimizeToTray;
884 checkBoxStartMinimized.Checked = Properties.Settings.Default.StartMinimized;
885 checkBoxReverseScreen.Checked = cds.ReverseScreen;
886 checkBoxInverseColors.Checked = cds.InverseColors;
887 checkBoxShowVolumeLabel.Checked = cds.ShowVolumeLabel;
888 checkBoxScaleToFit.Checked = cds.ScaleToFit;
889 maskedTextBoxMinFontSize.Enabled = cds.ScaleToFit;
890 labelMinFontSize.Enabled = cds.ScaleToFit;
891 maskedTextBoxMinFontSize.Text = cds.MinFontSize.ToString();
892 maskedTextBoxScrollingSpeed.Text = cds.ScrollingSpeedInPixelsPerSecond.ToString();
893 comboBoxDisplayType.SelectedIndex = cds.DisplayType;
894 timer.Interval = cds.TimerInterval;
895 maskedTextBoxTimerInterval.Text = cds.TimerInterval.ToString();
896 textBoxScrollLoopSeparator.Text = cds.Separator;
898 SetupPixelDelegates();
900 if (iDisplay.IsOpen())
902 //We have a display connection
903 //Reflect that in our UI
905 tableLayoutPanel.Enabled = true;
906 panelDisplay.Enabled = true;
908 //Only setup brightness if display is open
909 trackBarBrightness.Minimum = iDisplay.MinBrightness();
910 trackBarBrightness.Maximum = iDisplay.MaxBrightness();
911 if (cds.Brightness < iDisplay.MinBrightness() || cds.Brightness > iDisplay.MaxBrightness())
913 //Brightness out of range, this can occur when using auto-detect
914 //Use max brightness instead
915 trackBarBrightness.Value = iDisplay.MaxBrightness();
916 iDisplay.SetBrightness(iDisplay.MaxBrightness());
920 trackBarBrightness.Value = cds.Brightness;
921 iDisplay.SetBrightness(cds.Brightness);
924 //Try compute the steps to something that makes sense
925 trackBarBrightness.LargeChange = Math.Max(1, (iDisplay.MaxBrightness() - iDisplay.MinBrightness()) / 5);
926 trackBarBrightness.SmallChange = 1;
929 buttonFill.Enabled = true;
930 buttonClear.Enabled = true;
931 buttonOpen.Enabled = false;
932 buttonClose.Enabled = true;
933 trackBarBrightness.Enabled = true;
934 toolStripStatusLabelConnect.Text = "Connected - " + iDisplay.Vendor() + " - " + iDisplay.Product();
935 //+ " - " + iDisplay.SerialNumber();
937 if (iDisplay.SupportPowerOnOff())
939 buttonPowerOn.Enabled = true;
940 buttonPowerOff.Enabled = true;
944 buttonPowerOn.Enabled = false;
945 buttonPowerOff.Enabled = false;
948 if (iDisplay.SupportClock())
950 buttonShowClock.Enabled = true;
951 buttonHideClock.Enabled = true;
955 buttonShowClock.Enabled = false;
956 buttonHideClock.Enabled = false;
960 //Check if Volume Label is supported. To date only MDM166AA supports that crap :)
961 checkBoxShowVolumeLabel.Enabled = iDisplay.IconCount(Display.TMiniDisplayIconType.EMiniDisplayIconVolumeLabel)>0;
963 if (cds.ShowVolumeLabel)
965 iDisplay.SetIconOn(Display.TMiniDisplayIconType.EMiniDisplayIconVolumeLabel);
969 iDisplay.SetIconOff(Display.TMiniDisplayIconType.EMiniDisplayIconVolumeLabel);
974 //Display is connection not available
975 //Reflect that in our UI
976 checkBoxShowVolumeLabel.Enabled = false;
977 tableLayoutPanel.Enabled = false;
978 panelDisplay.Enabled = false;
979 buttonFill.Enabled = false;
980 buttonClear.Enabled = false;
981 buttonOpen.Enabled = true;
982 buttonClose.Enabled = false;
983 trackBarBrightness.Enabled = false;
984 buttonPowerOn.Enabled = false;
985 buttonPowerOff.Enabled = false;
986 buttonShowClock.Enabled = false;
987 buttonHideClock.Enabled = false;
988 toolStripStatusLabelConnect.Text = "Disconnected";
989 toolStripStatusLabelPower.Text = "N/A";
998 /// <param name="sender"></param>
999 /// <param name="e"></param>
1000 private void checkBoxShowVolumeLabel_CheckedChanged(object sender, EventArgs e)
1002 cds.ShowVolumeLabel = checkBoxShowVolumeLabel.Checked;
1003 Properties.Settings.Default.Save();
1007 private void checkBoxShowBorders_CheckedChanged(object sender, EventArgs e)
1009 //Save our show borders setting
1010 tableLayoutPanel.CellBorderStyle = (checkBoxShowBorders.Checked ? TableLayoutPanelCellBorderStyle.Single : TableLayoutPanelCellBorderStyle.None);
1011 cds.ShowBorders = checkBoxShowBorders.Checked;
1012 Properties.Settings.Default.Save();
1016 private void checkBoxConnectOnStartup_CheckedChanged(object sender, EventArgs e)
1018 //Save our connect on startup setting
1019 Properties.Settings.Default.DisplayConnectOnStartup = checkBoxConnectOnStartup.Checked;
1020 Properties.Settings.Default.Save();
1023 private void checkBoxMinimizeToTray_CheckedChanged(object sender, EventArgs e)
1025 //Save our "Minimize to tray" setting
1026 Properties.Settings.Default.MinimizeToTray = checkBoxMinimizeToTray.Checked;
1027 Properties.Settings.Default.Save();
1031 private void checkBoxStartMinimized_CheckedChanged(object sender, EventArgs e)
1033 //Save our "Start minimized" setting
1034 Properties.Settings.Default.StartMinimized = checkBoxStartMinimized.Checked;
1035 Properties.Settings.Default.Save();
1038 private void checkBoxAutoStart_CheckedChanged(object sender, EventArgs e)
1040 iStartupManager.Startup = checkBoxAutoStart.Checked;
1044 private void checkBoxReverseScreen_CheckedChanged(object sender, EventArgs e)
1046 //Save our reverse screen setting
1047 cds.ReverseScreen = checkBoxReverseScreen.Checked;
1048 Properties.Settings.Default.Save();
1049 SetupPixelDelegates();
1052 private void checkBoxInverseColors_CheckedChanged(object sender, EventArgs e)
1054 //Save our inverse colors setting
1055 cds.InverseColors = checkBoxInverseColors.Checked;
1056 Properties.Settings.Default.Save();
1057 SetupPixelDelegates();
1060 private void checkBoxScaleToFit_CheckedChanged(object sender, EventArgs e)
1062 //Save our scale to fit setting
1063 cds.ScaleToFit = checkBoxScaleToFit.Checked;
1064 Properties.Settings.Default.Save();
1066 labelMinFontSize.Enabled = cds.ScaleToFit;
1067 maskedTextBoxMinFontSize.Enabled = cds.ScaleToFit;
1070 private void MainForm_Resize(object sender, EventArgs e)
1072 if (WindowState == FormWindowState.Minimized)
1075 //iBmp = new System.Drawing.Bitmap(tableLayoutPanel.Width, tableLayoutPanel.Height, PixelFormat.Format32bppArgb);
1076 iCreateBitmap = true;
1080 private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
1082 CloseDisplayConnection();
1084 e.Cancel = iClosing;
1087 public void StartServer()
1089 iServiceHost = new ServiceHost
1092 new Uri[] { new Uri("net.tcp://localhost:8001/") }
1095 iServiceHost.AddServiceEndpoint(typeof(IService), new NetTcpBinding(SecurityMode.None, true), "DisplayService");
1096 iServiceHost.Open();
1099 public void StopServer()
1101 if (iClients.Count > 0 && !iClosing)
1105 BroadcastCloseEvent();
1109 if (MessageBox.Show("Force exit?", "Waiting for clients...", MessageBoxButtons.YesNo, MessageBoxIcon.Warning) == DialogResult.Yes)
1111 iClosing = false; //We make sure we force close if asked twice
1116 //We removed that as it often lags for some reason
1117 //iServiceHost.Close();
1121 public void BroadcastCloseEvent()
1123 Trace.TraceInformation("BroadcastCloseEvent - start");
1125 var inactiveClients = new List<string>();
1126 foreach (var client in iClients)
1128 //if (client.Key != eventData.ClientName)
1132 Trace.TraceInformation("BroadcastCloseEvent - " + client.Key);
1133 client.Value.Callback.OnCloseOrder(/*eventData*/);
1135 catch (Exception ex)
1137 inactiveClients.Add(client.Key);
1142 if (inactiveClients.Count > 0)
1144 foreach (var client in inactiveClients)
1146 iClients.Remove(client);
1147 Program.iMainForm.treeViewClients.Nodes.Remove(Program.iMainForm.treeViewClients.Nodes.Find(client, false)[0]);
1151 if (iClients.Count==0)
1158 /// Just remove all our fields.
1160 private void ClearLayout()
1162 tableLayoutPanel.Controls.Clear();
1163 tableLayoutPanel.RowStyles.Clear();
1164 tableLayoutPanel.ColumnStyles.Clear();
1168 /// Just launch a demo client.
1170 private void StartNewClient(string aTopText = "", string aBottomText = "")
1172 Thread clientThread = new Thread(SharpDisplayClient.Program.MainWithParams);
1173 SharpDisplayClient.StartParams myParams = new SharpDisplayClient.StartParams(new Point(this.Right, this.Top),aTopText,aBottomText);
1174 clientThread.Start(myParams);
1178 private void buttonStartClient_Click(object sender, EventArgs e)
1183 private void buttonSuspend_Click(object sender, EventArgs e)
1185 LastTickTime = DateTime.Now; //Reset timer to prevent jump
1186 timer.Enabled = !timer.Enabled;
1189 buttonSuspend.Text = "Run";
1193 buttonSuspend.Text = "Pause";
1197 private void buttonCloseClients_Click(object sender, EventArgs e)
1199 BroadcastCloseEvent();
1202 private void treeViewClients_AfterSelect(object sender, TreeViewEventArgs e)
1211 /// <param name="aSessionId"></param>
1212 /// <param name="aCallback"></param>
1213 public void AddClientThreadSafe(string aSessionId, ICallback aCallback)
1215 if (this.InvokeRequired)
1217 //Not in the proper thread, invoke ourselves
1218 AddClientDelegate d = new AddClientDelegate(AddClientThreadSafe);
1219 this.Invoke(d, new object[] { aSessionId, aCallback });
1223 //We are in the proper thread
1224 //Add this session to our collection of clients
1225 ClientData newClient = new ClientData(aSessionId, aCallback);
1226 Program.iMainForm.iClients.Add(aSessionId, newClient);
1227 //Add this session to our UI
1228 UpdateClientTreeViewNode(newClient);
1235 /// <param name="aSessionId"></param>
1236 public void RemoveClientThreadSafe(string aSessionId)
1238 if (this.InvokeRequired)
1240 //Not in the proper thread, invoke ourselves
1241 RemoveClientDelegate d = new RemoveClientDelegate(RemoveClientThreadSafe);
1242 this.Invoke(d, new object[] { aSessionId });
1246 //We are in the proper thread
1247 //Remove this session from both client collection and UI tree view
1248 if (Program.iMainForm.iClients.Keys.Contains(aSessionId))
1250 Program.iMainForm.iClients.Remove(aSessionId);
1251 Program.iMainForm.treeViewClients.Nodes.Remove(Program.iMainForm.treeViewClients.Nodes.Find(aSessionId, false)[0]);
1254 if (iClients.Count == 0)
1256 //Clear our screen when last client disconnects
1261 //We were closing our form
1262 //All clients are now closed
1263 //Just resume our close operation
1274 /// <param name="aSessionId"></param>
1275 /// <param name="aLayout"></param>
1276 public void SetClientLayoutThreadSafe(string aSessionId, TableLayout aLayout)
1278 if (this.InvokeRequired)
1280 //Not in the proper thread, invoke ourselves
1281 SetLayoutDelegate d = new SetLayoutDelegate(SetClientLayoutThreadSafe);
1282 this.Invoke(d, new object[] { aSessionId, aLayout });
1286 ClientData client = iClients[aSessionId];
1289 client.Layout = aLayout;
1290 UpdateTableLayoutPanel(client);
1292 UpdateClientTreeViewNode(client);
1300 /// <param name="aSessionId"></param>
1301 /// <param name="aField"></param>
1302 public void SetClientFieldThreadSafe(string aSessionId, DataField aField)
1304 if (this.InvokeRequired)
1306 //Not in the proper thread, invoke ourselves
1307 SetFieldDelegate d = new SetFieldDelegate(SetClientFieldThreadSafe);
1308 this.Invoke(d, new object[] { aSessionId, aField });
1312 //We are in the proper thread
1313 //Call the non-thread-safe variant
1314 SetClientField(aSessionId, aField);
1321 /// <param name="aSessionId"></param>
1322 /// <param name="aField"></param>
1323 private void SetClientField(string aSessionId, DataField aField)
1325 SetCurrentClient(aSessionId);
1326 ClientData client = iClients[aSessionId];
1329 bool somethingChanged = false;
1331 //Make sure all our fields are in place
1332 while (client.Fields.Count < (aField.Index + 1))
1334 //Add a text field with proper index
1335 client.Fields.Add(new DataField(client.Fields.Count));
1336 somethingChanged = true;
1339 if (client.Fields[aField.Index].IsSameLayout(aField))
1341 //Same layout just update our field
1342 client.Fields[aField.Index] = aField;
1344 if (aField.IsText && tableLayoutPanel.Controls[aField.Index] is MarqueeLabel)
1346 //Text field control already in place, just change the text
1347 MarqueeLabel label = (MarqueeLabel)tableLayoutPanel.Controls[aField.Index];
1348 somethingChanged = (label.Text != aField.Text || label.TextAlign != aField.Alignment);
1349 label.Text = aField.Text;
1350 label.TextAlign = aField.Alignment;
1352 else if (aField.IsBitmap && tableLayoutPanel.Controls[aField.Index] is PictureBox)
1354 somethingChanged = true; //TODO: Bitmap comp or should we leave that to clients?
1355 //Bitmap field control already in place just change the bitmap
1356 PictureBox pictureBox = (PictureBox)tableLayoutPanel.Controls[aField.Index];
1357 pictureBox.Image = aField.Bitmap;
1361 somethingChanged = true;
1362 //The requested control in our layout it not of the correct type
1363 //Wrong control type, re-create them all
1364 UpdateTableLayoutPanel(iCurrentClientData);
1369 somethingChanged = true;
1370 //Different layout, need to rebuild it
1371 client.Fields[aField.Index] = aField;
1372 UpdateTableLayoutPanel(iCurrentClientData);
1376 if (somethingChanged)
1378 UpdateClientTreeViewNode(client);
1386 /// <param name="aTexts"></param>
1387 public void SetClientFieldsThreadSafe(string aSessionId, System.Collections.Generic.IList<DataField> aFields)
1389 if (this.InvokeRequired)
1391 //Not in the proper thread, invoke ourselves
1392 SetFieldsDelegate d = new SetFieldsDelegate(SetClientFieldsThreadSafe);
1393 this.Invoke(d, new object[] { aSessionId, aFields });
1397 //Put each our text fields in a label control
1398 foreach (DataField field in aFields)
1400 SetClientField(aSessionId, field);
1408 /// <param name="aSessionId"></param>
1409 /// <param name="aName"></param>
1410 public void SetClientNameThreadSafe(string aSessionId, string aName)
1412 if (this.InvokeRequired)
1414 //Not in the proper thread, invoke ourselves
1415 SetClientNameDelegate d = new SetClientNameDelegate(SetClientNameThreadSafe);
1416 this.Invoke(d, new object[] { aSessionId, aName });
1420 //We are in the proper thread
1422 ClientData client = iClients[aSessionId];
1426 client.Name = aName;
1427 //Update our tree-view
1428 UpdateClientTreeViewNode(client);
1436 /// <param name="aClient"></param>
1437 private void UpdateClientTreeViewNode(ClientData aClient)
1439 if (aClient == null)
1444 TreeNode node = null;
1445 //Check that our client node already exists
1446 //Get our client root node using its key which is our session ID
1447 TreeNode[] nodes = treeViewClients.Nodes.Find(aClient.SessionId, false);
1448 if (nodes.Count()>0)
1450 //We already have a node for that client
1452 //Clear children as we are going to recreate them below
1457 //Client node does not exists create a new one
1458 treeViewClients.Nodes.Add(aClient.SessionId, aClient.SessionId);
1459 node = treeViewClients.Nodes.Find(aClient.SessionId, false)[0];
1465 if (aClient.Name != "")
1467 //We have a name, us it as text for our root node
1468 node.Text = aClient.Name;
1469 //Add a child with SessionId
1470 node.Nodes.Add(new TreeNode(aClient.SessionId));
1474 //No name, use session ID instead
1475 node.Text = aClient.SessionId;
1478 if (aClient.Fields.Count > 0)
1480 //Create root node for our texts
1481 TreeNode textsRoot = new TreeNode("Fields");
1482 node.Nodes.Add(textsRoot);
1483 //For each text add a new entry
1484 foreach (DataField field in aClient.Fields)
1486 if (!field.IsBitmap)
1488 DataField textField = (DataField)field;
1489 textsRoot.Nodes.Add(new TreeNode("[Text]" + textField.Text));
1493 textsRoot.Nodes.Add(new TreeNode("[Bitmap]"));
1503 /// Update our table layout row styles to make sure each rows have similar height
1505 private void UpdateTableLayoutRowStyles()
1507 foreach (RowStyle rowStyle in tableLayoutPanel.RowStyles)
1509 rowStyle.SizeType = SizeType.Percent;
1510 rowStyle.Height = 100 / tableLayoutPanel.RowCount;
1516 /// Empty and recreate our table layout with the given number of columns and rows.
1517 /// Sizes of rows and columns are uniform.
1519 /// <param name="aColumn"></param>
1520 /// <param name="aRow"></param>
1521 private void UpdateTableLayoutPanel(int aColumn, int aRow)
1523 tableLayoutPanel.Controls.Clear();
1524 tableLayoutPanel.RowStyles.Clear();
1525 tableLayoutPanel.ColumnStyles.Clear();
1526 tableLayoutPanel.RowCount = 0;
1527 tableLayoutPanel.ColumnCount = 0;
1529 while (tableLayoutPanel.RowCount < aRow)
1531 tableLayoutPanel.RowCount++;
1534 while (tableLayoutPanel.ColumnCount < aColumn)
1536 tableLayoutPanel.ColumnCount++;
1539 for (int i = 0; i < tableLayoutPanel.ColumnCount; i++)
1541 //Create our column styles
1542 this.tableLayoutPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100 / tableLayoutPanel.ColumnCount));
1544 for (int j = 0; j < tableLayoutPanel.RowCount; j++)
1548 //Create our row styles
1549 this.tableLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100 / tableLayoutPanel.RowCount));
1552 MarqueeLabel control = new SharpDisplayManager.MarqueeLabel();
1553 control.AutoEllipsis = true;
1554 control.AutoSize = true;
1555 control.BackColor = System.Drawing.Color.Transparent;
1556 control.Dock = System.Windows.Forms.DockStyle.Fill;
1557 control.Location = new System.Drawing.Point(1, 1);
1558 control.Margin = new System.Windows.Forms.Padding(0);
1559 control.Name = "marqueeLabelCol" + aColumn + "Row" + aRow;
1560 control.OwnTimer = false;
1561 control.PixelsPerSecond = 64;
1562 control.Separator = cds.Separator;
1563 control.MinFontSize = cds.MinFontSize;
1564 control.ScaleToFit = cds.ScaleToFit;
1565 //control.Size = new System.Drawing.Size(254, 30);
1566 //control.TabIndex = 2;
1567 control.Font = cds.Font;
1568 control.Text = "ABCDEFGHIJKLMNOPQRST-0123456789";
1569 control.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
1570 control.UseCompatibleTextRendering = true;
1572 tableLayoutPanel.Controls.Add(control, i, j);
1581 /// Update our display table layout.
1583 /// <param name="aLayout"></param>
1584 private void UpdateTableLayoutPanel(ClientData aClient)
1586 if (aClient == null)
1593 TableLayout layout = aClient.Layout;
1596 tableLayoutPanel.Controls.Clear();
1597 tableLayoutPanel.RowStyles.Clear();
1598 tableLayoutPanel.ColumnStyles.Clear();
1599 tableLayoutPanel.RowCount = 0;
1600 tableLayoutPanel.ColumnCount = 0;
1602 while (tableLayoutPanel.RowCount < layout.Rows.Count)
1604 tableLayoutPanel.RowCount++;
1607 while (tableLayoutPanel.ColumnCount < layout.Columns.Count)
1609 tableLayoutPanel.ColumnCount++;
1612 for (int i = 0; i < tableLayoutPanel.ColumnCount; i++)
1614 //Create our column styles
1615 this.tableLayoutPanel.ColumnStyles.Add(layout.Columns[i]);
1617 for (int j = 0; j < tableLayoutPanel.RowCount; j++)
1621 //Create our row styles
1622 this.tableLayoutPanel.RowStyles.Add(layout.Rows[j]);
1625 //Check if we already have a control
1626 Control existingControl = tableLayoutPanel.GetControlFromPosition(i,j);
1627 if (existingControl!=null)
1629 //We already have a control in that cell as a results of row/col spanning
1630 //Move on to next cell then
1636 //Check if a client field already exists for that cell
1637 if (aClient.Fields.Count <= tableLayoutPanel.Controls.Count)
1639 //No client field specified, create a text field by default
1640 aClient.Fields.Add(new DataField(aClient.Fields.Count));
1643 //Create a control corresponding to the field specified for that cell
1644 DataField field = aClient.Fields[tableLayoutPanel.Controls.Count];
1645 Control control = CreateControlForDataField(field);
1647 //Add newly created control to our table layout at the specified row and column
1648 tableLayoutPanel.Controls.Add(control, i, j);
1649 //Make sure we specify row and column span for that new control
1650 tableLayoutPanel.SetRowSpan(control,field.RowSpan);
1651 tableLayoutPanel.SetColumnSpan(control, field.ColumnSpan);
1656 while (aClient.Fields.Count > fieldCount)
1658 //We have too much fields for this layout
1659 //Just discard them until we get there
1660 aClient.Fields.RemoveAt(aClient.Fields.Count-1);
1667 /// Check our type of data field and create corresponding control
1669 /// <param name="aField"></param>
1670 private Control CreateControlForDataField(DataField aField)
1672 Control control=null;
1673 if (!aField.IsBitmap)
1675 MarqueeLabel label = new SharpDisplayManager.MarqueeLabel();
1676 label.AutoEllipsis = true;
1677 label.AutoSize = true;
1678 label.BackColor = System.Drawing.Color.Transparent;
1679 label.Dock = System.Windows.Forms.DockStyle.Fill;
1680 label.Location = new System.Drawing.Point(1, 1);
1681 label.Margin = new System.Windows.Forms.Padding(0);
1682 label.Name = "marqueeLabel" + aField.Index;
1683 label.OwnTimer = false;
1684 label.PixelsPerSecond = cds.ScrollingSpeedInPixelsPerSecond;
1685 label.Separator = cds.Separator;
1686 label.MinFontSize = cds.MinFontSize;
1687 label.ScaleToFit = cds.ScaleToFit;
1688 //control.Size = new System.Drawing.Size(254, 30);
1689 //control.TabIndex = 2;
1690 label.Font = cds.Font;
1692 label.TextAlign = aField.Alignment;
1693 label.UseCompatibleTextRendering = true;
1694 label.Text = aField.Text;
1700 //Create picture box
1701 PictureBox picture = new PictureBox();
1702 picture.AutoSize = true;
1703 picture.BackColor = System.Drawing.Color.Transparent;
1704 picture.Dock = System.Windows.Forms.DockStyle.Fill;
1705 picture.Location = new System.Drawing.Point(1, 1);
1706 picture.Margin = new System.Windows.Forms.Padding(0);
1707 picture.Name = "pictureBox" + aField;
1709 picture.Image = aField.Bitmap;
1718 /// Called when the user selected a new display type.
1720 /// <param name="sender"></param>
1721 /// <param name="e"></param>
1722 private void comboBoxDisplayType_SelectedIndexChanged(object sender, EventArgs e)
1724 //Store the selected display type in our settings
1725 Properties.Settings.Default.CurrentDisplayIndex = comboBoxDisplayType.SelectedIndex;
1726 cds.DisplayType = comboBoxDisplayType.SelectedIndex;
1727 Properties.Settings.Default.Save();
1729 //Try re-opening the display connection if we were already connected.
1730 //Otherwise just update our status to reflect display type change.
1731 if (iDisplay.IsOpen())
1733 OpenDisplayConnection();
1741 private void maskedTextBoxTimerInterval_TextChanged(object sender, EventArgs e)
1743 if (maskedTextBoxTimerInterval.Text != "")
1745 int interval = Convert.ToInt32(maskedTextBoxTimerInterval.Text);
1749 timer.Interval = interval;
1750 cds.TimerInterval = timer.Interval;
1751 Properties.Settings.Default.Save();
1756 private void maskedTextBoxMinFontSize_TextChanged(object sender, EventArgs e)
1758 if (maskedTextBoxMinFontSize.Text != "")
1760 int minFontSize = Convert.ToInt32(maskedTextBoxMinFontSize.Text);
1762 if (minFontSize > 0)
1764 cds.MinFontSize = minFontSize;
1765 Properties.Settings.Default.Save();
1766 //We need to recreate our layout for that change to take effect
1767 UpdateTableLayoutPanel(iCurrentClientData);
1773 private void maskedTextBoxScrollingSpeed_TextChanged(object sender, EventArgs e)
1775 if (maskedTextBoxScrollingSpeed.Text != "")
1777 int scrollingSpeed = Convert.ToInt32(maskedTextBoxScrollingSpeed.Text);
1779 if (scrollingSpeed > 0)
1781 cds.ScrollingSpeedInPixelsPerSecond = scrollingSpeed;
1782 Properties.Settings.Default.Save();
1783 //We need to recreate our layout for that change to take effect
1784 UpdateTableLayoutPanel(iCurrentClientData);
1789 private void textBoxScrollLoopSeparator_TextChanged(object sender, EventArgs e)
1791 cds.Separator = textBoxScrollLoopSeparator.Text;
1792 Properties.Settings.Default.Save();
1794 //Update our text fields
1795 foreach (MarqueeLabel ctrl in tableLayoutPanel.Controls)
1797 ctrl.Separator = cds.Separator;
1802 private void buttonPowerOn_Click(object sender, EventArgs e)
1807 private void buttonPowerOff_Click(object sender, EventArgs e)
1809 iDisplay.PowerOff();
1812 private void buttonShowClock_Click(object sender, EventArgs e)
1814 iDisplay.ShowClock();
1817 private void buttonHideClock_Click(object sender, EventArgs e)
1819 iDisplay.HideClock();
1822 private void buttonUpdate_Click(object sender, EventArgs e)
1824 InstallUpdateSyncWithInfo();
1828 private void InstallUpdateSyncWithInfo()
1830 UpdateCheckInfo info = null;
1832 if (ApplicationDeployment.IsNetworkDeployed)
1834 ApplicationDeployment ad = ApplicationDeployment.CurrentDeployment;
1838 info = ad.CheckForDetailedUpdate();
1841 catch (DeploymentDownloadException dde)
1843 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);
1846 catch (InvalidDeploymentException ide)
1848 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);
1851 catch (InvalidOperationException ioe)
1853 MessageBox.Show("This application cannot be updated. It is likely not a ClickOnce application. Error: " + ioe.Message);
1857 if (info.UpdateAvailable)
1859 Boolean doUpdate = true;
1861 if (!info.IsUpdateRequired)
1863 DialogResult dr = MessageBox.Show("An update is available. Would you like to update the application now?", "Update Available", MessageBoxButtons.OKCancel);
1864 if (!(DialogResult.OK == dr))
1871 // Display a message that the app MUST reboot. Display the minimum required version.
1872 MessageBox.Show("This application has detected a mandatory update from your current " +
1873 "version to version " + info.MinimumRequiredVersion.ToString() +
1874 ". The application will now install the update and restart.",
1875 "Update Available", MessageBoxButtons.OK,
1876 MessageBoxIcon.Information);
1884 MessageBox.Show("The application has been upgraded, and will now restart.");
1885 Application.Restart();
1887 catch (DeploymentDownloadException dde)
1889 MessageBox.Show("Cannot install the latest version of the application. \n\nPlease check your network connection, or try again later. Error: " + dde);
1896 MessageBox.Show("You are already running the latest version.", "Application up-to-date");
1905 private void SysTrayHideShow()
1911 WindowState = FormWindowState.Normal;
1916 /// Use to handle minimize events.
1918 /// <param name="sender"></param>
1919 /// <param name="e"></param>
1920 private void MainForm_SizeChanged(object sender, EventArgs e)
1922 if (WindowState == FormWindowState.Minimized && Properties.Settings.Default.MinimizeToTray)
1935 /// <param name="sender"></param>
1936 /// <param name="e"></param>
1937 private void tableLayoutPanel_SizeChanged(object sender, EventArgs e)
1939 //Our table layout size has changed which means our display size has changed.
1940 //We need to re-create our bitmap.
1941 iCreateBitmap = true;
1946 /// A UI thread copy of a client relevant data.
1947 /// Keeping this copy in the UI thread helps us deal with threading issues.
1949 public class ClientData
1951 public ClientData(string aSessionId, ICallback aCallback)
1953 SessionId = aSessionId;
1955 Fields = new List<DataField>();
1956 Layout = new TableLayout(1, 2); //Default to one column and two rows
1957 Callback = aCallback;
1960 public string SessionId { get; set; }
1961 public string Name { get; set; }
1962 public List<DataField> Fields { get; set; }
1963 public TableLayout Layout { get; set; }
1964 public ICallback Callback { get; set; }