Audio tab now displaying default audio device name.
Consolidating audio management, adding 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 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 //Just like Windows Volume Mixer we unmute if the volume is adjusted
223 iMultiMediaDevice.AudioEndpointVolume.Mute = false;
224 //Set volume level according to our volume slider new position
225 iMultiMediaDevice.AudioEndpointVolume.MasterVolumeLevelScalar = trackBarMasterVolume.Value / 100.0f;
230 /// Mute check box changed.
232 /// <param name="sender"></param>
233 /// <param name="e"></param>
234 private void checkBoxMute_CheckedChanged(object sender, EventArgs e)
236 iMultiMediaDevice.AudioEndpointVolume.Mute = checkBoxMute.Checked;
240 /// Device State Changed
242 public void OnDeviceStateChanged([MarshalAs(UnmanagedType.LPWStr)] string deviceId, [MarshalAs(UnmanagedType.I4)] DeviceState newState){}
247 public void OnDeviceAdded([MarshalAs(UnmanagedType.LPWStr)] string pwstrDeviceId) { }
252 public void OnDeviceRemoved([MarshalAs(UnmanagedType.LPWStr)] string deviceId) { }
255 /// Default Device Changed
257 public void OnDefaultDeviceChanged(DataFlow flow, Role role, [MarshalAs(UnmanagedType.LPWStr)] string defaultDeviceId)
259 if (role == Role.Multimedia && flow == DataFlow.Render)
261 UpdateAudioDeviceAndMasterVolumeThreadSafe();
266 /// Property Value Changed
268 /// <param name="pwstrDeviceId"></param>
269 /// <param name="key"></param>
270 public void OnPropertyValueChanged([MarshalAs(UnmanagedType.LPWStr)] string pwstrDeviceId, PropertyKey key){}
276 /// Update master volume indicators based our current system states.
277 /// This typically includes volume levels and mute status.
279 private void UpdateMasterVolumeThreadSafe()
281 if (this.InvokeRequired)
283 //Not in the proper thread, invoke ourselves
284 PlainUpdateDelegate d = new PlainUpdateDelegate(UpdateMasterVolumeThreadSafe);
285 this.Invoke(d, new object[] { });
289 //Update volume slider
290 float volumeLevelScalar = iMultiMediaDevice.AudioEndpointVolume.MasterVolumeLevelScalar;
291 trackBarMasterVolume.Value = Convert.ToInt32(volumeLevelScalar * 100);
292 //Update mute checkbox
293 checkBoxMute.Checked = iMultiMediaDevice.AudioEndpointVolume.Mute;
295 //If our display connection is open we need to update its icons
296 if (iDisplay.IsOpen())
298 //First take care our our volume level icons
299 int volumeIconCount = iDisplay.IconCount(Display.TMiniDisplayIconType.EMiniDisplayIconVolume);
300 if (volumeIconCount > 0)
302 //Compute current volume level from system level and the number of segments in our display volume bar.
303 //That tells us how many segments in our volume bar needs to be turned on.
304 float currentVolume = volumeLevelScalar * volumeIconCount;
305 int segmentOnCount = Convert.ToInt32(currentVolume);
306 //Check if our segment count was rounded up, this will later be used for half brightness segment
307 bool roundedUp = segmentOnCount > currentVolume;
309 for (int i = 0; i < volumeIconCount; i++)
311 if (i < segmentOnCount)
313 //If we are dealing with our last segment and our segment count was rounded up then we will use half brightness.
314 if (i == segmentOnCount - 1 && roundedUp)
317 iDisplay.SetIconStatus(Display.TMiniDisplayIconType.EMiniDisplayIconVolume, i, (iDisplay.IconStatusCount(Display.TMiniDisplayIconType.EMiniDisplayIconVolume) - 1)/2);
322 iDisplay.SetIconStatus(Display.TMiniDisplayIconType.EMiniDisplayIconVolume, i, iDisplay.IconStatusCount(Display.TMiniDisplayIconType.EMiniDisplayIconVolume) - 1);
327 iDisplay.SetIconStatus(Display.TMiniDisplayIconType.EMiniDisplayIconVolume, i, 0);
332 //Take care our our mute icon
333 iDisplay.SetIconOnOff(Display.TMiniDisplayIconType.EMiniDisplayIconMute, iMultiMediaDevice.AudioEndpointVolume.Mute);
341 private void UpdateAudioDeviceAndMasterVolumeThreadSafe()
343 if (this.InvokeRequired)
345 //Not in the proper thread, invoke ourselves
346 PlainUpdateDelegate d = new PlainUpdateDelegate(UpdateAudioDeviceAndMasterVolumeThreadSafe);
347 this.Invoke(d, new object[] { });
351 //We are in the correct thread just go ahead.
354 //Get our master volume
355 iMultiMediaDevice = iMultiMediaDeviceEnumerator.GetDefaultAudioEndpoint(DataFlow.Render, Role.Multimedia);
357 labelDefaultAudioDevice.Text = iMultiMediaDevice.FriendlyName;
359 //Show our volume in our track bar
360 UpdateMasterVolumeThreadSafe();
362 //Register to get volume modifications
363 iMultiMediaDevice.AudioEndpointVolume.OnVolumeNotification += OnVolumeNotificationThreadSafe;
365 trackBarMasterVolume.Enabled = true;
369 Debug.WriteLine("Exception thrown in UpdateAudioDeviceAndMasterVolume");
370 Debug.WriteLine(ex.ToString());
371 //Something went wrong S/PDIF device ca throw exception I guess
372 trackBarMasterVolume.Enabled = false;
379 private void PopulateDeviceTypes()
381 int count = Display.TypeCount();
383 for (int i = 0; i < count; i++)
385 comboBoxDisplayType.Items.Add(Display.TypeName((Display.TMiniDisplayType)i));
392 private void SetupTrayIcon()
394 iNotifyIcon.Icon = GetIcon("vfd.ico");
395 iNotifyIcon.Text = "Sharp Display Manager";
396 iNotifyIcon.Visible = true;
398 //Double click toggles visibility - typically brings up the application
399 iNotifyIcon.DoubleClick += delegate(object obj, EventArgs args)
404 //Adding a context menu, useful to be able to exit the application
405 ContextMenu contextMenu = new ContextMenu();
406 //Context menu item to toggle visibility
407 MenuItem hideShowItem = new MenuItem("Hide/Show");
408 hideShowItem.Click += delegate(object obj, EventArgs args)
412 contextMenu.MenuItems.Add(hideShowItem);
414 //Context menu item separator
415 contextMenu.MenuItems.Add(new MenuItem("-"));
417 //Context menu exit item
418 MenuItem exitItem = new MenuItem("Exit");
419 exitItem.Click += delegate(object obj, EventArgs args)
423 contextMenu.MenuItems.Add(exitItem);
425 iNotifyIcon.ContextMenu = contextMenu;
429 /// Access icons from embedded resources.
431 /// <param name="name"></param>
432 /// <returns></returns>
433 public static Icon GetIcon(string name)
435 name = "SharpDisplayManager.Resources." + name;
438 Assembly.GetExecutingAssembly().GetManifestResourceNames();
439 for (int i = 0; i < names.Length; i++)
441 if (names[i].Replace('\\', '.') == name)
443 using (Stream stream = Assembly.GetExecutingAssembly().
444 GetManifestResourceStream(names[i]))
446 return new Icon(stream);
456 /// Set our current client.
457 /// This will take care of applying our client layout and set data fields.
459 /// <param name="aSessionId"></param>
460 void SetCurrentClient(string aSessionId)
462 if (aSessionId == iCurrentClientSessionId)
464 //Given client is already the current one.
465 //Don't bother changing anything then.
469 //Set current client ID.
470 iCurrentClientSessionId = aSessionId;
471 //Fetch and set current client data.
472 iCurrentClientData = iClients[aSessionId];
473 //Apply layout and set data fields.
474 UpdateTableLayoutPanel(iCurrentClientData);
477 private void buttonFont_Click(object sender, EventArgs e)
479 //fontDialog.ShowColor = true;
480 //fontDialog.ShowApply = true;
481 fontDialog.ShowEffects = true;
482 fontDialog.Font = cds.Font;
484 fontDialog.FixedPitchOnly = checkBoxFixedPitchFontOnly.Checked;
486 //fontDialog.ShowHelp = true;
488 //fontDlg.MaxSize = 40;
489 //fontDlg.MinSize = 22;
491 //fontDialog.Parent = this;
492 //fontDialog.StartPosition = FormStartPosition.CenterParent;
494 //DlgBox.ShowDialog(fontDialog);
496 //if (fontDialog.ShowDialog(this) != DialogResult.Cancel)
497 if (DlgBox.ShowDialog(fontDialog) != DialogResult.Cancel)
499 //Set the fonts to all our labels in our layout
500 foreach (Control ctrl in tableLayoutPanel.Controls)
502 if (ctrl is MarqueeLabel)
504 ((MarqueeLabel)ctrl).Font = fontDialog.Font;
509 cds.Font = fontDialog.Font;
510 Properties.Settings.Default.Save();
519 void CheckFontHeight()
521 //Show font height and width
522 labelFontHeight.Text = "Font height: " + cds.Font.Height;
523 float charWidth = IsFixedWidth(cds.Font);
524 if (charWidth == 0.0f)
526 labelFontWidth.Visible = false;
530 labelFontWidth.Visible = true;
531 labelFontWidth.Text = "Font width: " + charWidth;
534 MarqueeLabel label = null;
535 //Get the first label control we can find
536 foreach (Control ctrl in tableLayoutPanel.Controls)
538 if (ctrl is MarqueeLabel)
540 label = (MarqueeLabel)ctrl;
545 //Now check font height and show a warning if needed.
546 if (label != null && label.Font.Height > label.Height)
548 labelWarning.Text = "WARNING: Selected font is too height by " + (label.Font.Height - label.Height) + " pixels!";
549 labelWarning.Visible = true;
553 labelWarning.Visible = false;
558 private void buttonCapture_Click(object sender, EventArgs e)
560 System.Drawing.Bitmap bmp = new System.Drawing.Bitmap(tableLayoutPanel.Width, tableLayoutPanel.Height);
561 tableLayoutPanel.DrawToBitmap(bmp, tableLayoutPanel.ClientRectangle);
562 //Bitmap bmpToSave = new Bitmap(bmp);
563 bmp.Save("D:\\capture.png");
565 ((MarqueeLabel)tableLayoutPanel.Controls[0]).Text = "Captured";
568 string outputFileName = "d:\\capture.png";
569 using (MemoryStream memory = new MemoryStream())
571 using (FileStream fs = new FileStream(outputFileName, FileMode.OpenOrCreate, FileAccess.ReadWrite))
573 bmp.Save(memory, System.Drawing.Imaging.ImageFormat.Png);
574 byte[] bytes = memory.ToArray();
575 fs.Write(bytes, 0, bytes.Length);
582 private void CheckForRequestResults()
584 if (iDisplay.IsRequestPending())
586 switch (iDisplay.AttemptRequestCompletion())
588 case Display.TMiniDisplayRequest.EMiniDisplayRequestFirmwareRevision:
589 toolStripStatusLabelConnect.Text += " v" + iDisplay.FirmwareRevision();
590 //Issue next request then
591 iDisplay.RequestPowerSupplyStatus();
594 case Display.TMiniDisplayRequest.EMiniDisplayRequestPowerSupplyStatus:
595 if (iDisplay.PowerSupplyStatus())
597 toolStripStatusLabelPower.Text = "ON";
601 toolStripStatusLabelPower.Text = "OFF";
603 //Issue next request then
604 iDisplay.RequestDeviceId();
607 case Display.TMiniDisplayRequest.EMiniDisplayRequestDeviceId:
608 toolStripStatusLabelConnect.Text += " - " + iDisplay.DeviceId();
609 //No more request to issue
615 public static uint ColorWhiteIsOn(int aX, int aY, uint aPixel)
617 if ((aPixel & 0x00FFFFFF) == 0x00FFFFFF)
624 public static uint ColorUntouched(int aX, int aY, uint aPixel)
629 public static uint ColorInversed(int aX, int aY, uint aPixel)
634 public static uint ColorChessboard(int aX, int aY, uint aPixel)
636 if ((aX % 2 == 0) && (aY % 2 == 0))
640 else if ((aX % 2 != 0) && (aY % 2 != 0))
648 public static int ScreenReversedX(System.Drawing.Bitmap aBmp, int aX)
650 return aBmp.Width - aX - 1;
653 public int ScreenReversedY(System.Drawing.Bitmap aBmp, int aY)
655 return iBmp.Height - aY - 1;
658 public int ScreenX(System.Drawing.Bitmap aBmp, int aX)
663 public int ScreenY(System.Drawing.Bitmap aBmp, int aY)
669 /// Select proper pixel delegates according to our current settings.
671 private void SetupPixelDelegates()
673 //Select our pixel processing routine
674 if (cds.InverseColors)
676 //iColorFx = ColorChessboard;
677 iColorFx = ColorInversed;
681 iColorFx = ColorWhiteIsOn;
684 //Select proper coordinate translation functions
685 //We used delegate/function pointer to support reverse screen without doing an extra test on each pixels
686 if (cds.ReverseScreen)
688 iScreenX = ScreenReversedX;
689 iScreenY = ScreenReversedY;
699 //This is our timer tick responsible to perform our render
700 private void timer_Tick(object sender, EventArgs e)
702 //Update our animations
703 DateTime NewTickTime = DateTime.Now;
705 //Update animation for all our marquees
706 foreach (Control ctrl in tableLayoutPanel.Controls)
708 if (ctrl is MarqueeLabel)
710 ((MarqueeLabel)ctrl).UpdateAnimation(LastTickTime, NewTickTime);
716 if (iDisplay.IsOpen())
718 CheckForRequestResults();
723 iBmp = new System.Drawing.Bitmap(tableLayoutPanel.Width, tableLayoutPanel.Height, PixelFormat.Format32bppArgb);
725 tableLayoutPanel.DrawToBitmap(iBmp, tableLayoutPanel.ClientRectangle);
726 //iBmp.Save("D:\\capture.png");
728 //Send it to our display
729 for (int i = 0; i < iBmp.Width; i++)
731 for (int j = 0; j < iBmp.Height; j++)
735 //Get our processed pixel coordinates
736 int x = iScreenX(iBmp, i);
737 int y = iScreenY(iBmp, j);
739 uint color = (uint)iBmp.GetPixel(i, j).ToArgb();
740 //Apply color effects
741 color = iColorFx(x,y,color);
743 iDisplay.SetPixel(x, y, color);
748 iDisplay.SwapBuffers();
752 //Compute instant FPS
753 toolStripStatusLabelFps.Text = (1.0/NewTickTime.Subtract(LastTickTime).TotalSeconds).ToString("F0") + " / " + (1000/timer.Interval).ToString() + " FPS";
755 LastTickTime = NewTickTime;
760 /// Attempt to establish connection with our display hardware.
762 private void OpenDisplayConnection()
764 CloseDisplayConnection();
766 if (!iDisplay.Open((Display.TMiniDisplayType)cds.DisplayType))
769 toolStripStatusLabelConnect.Text = "Connection error";
773 private void CloseDisplayConnection()
775 //Status will be updated upon receiving the closed event
779 private void buttonOpen_Click(object sender, EventArgs e)
781 OpenDisplayConnection();
784 private void buttonClose_Click(object sender, EventArgs e)
786 CloseDisplayConnection();
789 private void buttonClear_Click(object sender, EventArgs e)
792 iDisplay.SwapBuffers();
795 private void buttonFill_Click(object sender, EventArgs e)
798 iDisplay.SwapBuffers();
801 private void trackBarBrightness_Scroll(object sender, EventArgs e)
803 cds.Brightness = trackBarBrightness.Value;
804 Properties.Settings.Default.Save();
805 iDisplay.SetBrightness(trackBarBrightness.Value);
811 /// CDS stands for Current Display Settings
813 private DisplaySettings cds
817 DisplaysSettings settings = Properties.Settings.Default.DisplaysSettings;
818 if (settings == null)
820 settings = new DisplaysSettings();
822 Properties.Settings.Default.DisplaysSettings = settings;
825 //Make sure all our settings have been created
826 while (settings.Displays.Count <= Properties.Settings.Default.CurrentDisplayIndex)
828 settings.Displays.Add(new DisplaySettings());
831 DisplaySettings displaySettings = settings.Displays[Properties.Settings.Default.CurrentDisplayIndex];
832 return displaySettings;
837 /// Check if the given font has a fixed character pitch.
839 /// <param name="ft"></param>
840 /// <returns>0.0f if this is not a monospace font, otherwise returns the character width.</returns>
841 public float IsFixedWidth(Font ft)
843 Graphics g = CreateGraphics();
844 char[] charSizes = new char[] { 'i', 'a', 'Z', '%', '#', 'a', 'B', 'l', 'm', ',', '.' };
845 float charWidth = g.MeasureString("I", ft, Int32.MaxValue, StringFormat.GenericTypographic).Width;
847 bool fixedWidth = true;
849 foreach (char c in charSizes)
850 if (g.MeasureString(c.ToString(), ft, Int32.MaxValue, StringFormat.GenericTypographic).Width != charWidth)
862 /// Synchronize UI with settings
864 private void UpdateStatus()
867 checkBoxShowBorders.Checked = cds.ShowBorders;
868 tableLayoutPanel.CellBorderStyle = (cds.ShowBorders ? TableLayoutPanelCellBorderStyle.Single : TableLayoutPanelCellBorderStyle.None);
870 //Set the proper font to each of our labels
871 foreach (MarqueeLabel ctrl in tableLayoutPanel.Controls)
873 ctrl.Font = cds.Font;
877 //Check if "run on Windows startup" is enabled
878 checkBoxAutoStart.Checked = iStartupManager.Startup;
880 checkBoxConnectOnStartup.Checked = Properties.Settings.Default.DisplayConnectOnStartup;
881 checkBoxMinimizeToTray.Checked = Properties.Settings.Default.MinimizeToTray;
882 checkBoxStartMinimized.Checked = Properties.Settings.Default.StartMinimized;
883 checkBoxReverseScreen.Checked = cds.ReverseScreen;
884 checkBoxInverseColors.Checked = cds.InverseColors;
885 checkBoxShowVolumeLabel.Checked = cds.ShowVolumeLabel;
886 checkBoxScaleToFit.Checked = cds.ScaleToFit;
887 maskedTextBoxMinFontSize.Enabled = cds.ScaleToFit;
888 labelMinFontSize.Enabled = cds.ScaleToFit;
889 maskedTextBoxMinFontSize.Text = cds.MinFontSize.ToString();
890 maskedTextBoxScrollingSpeed.Text = cds.ScrollingSpeedInPixelsPerSecond.ToString();
891 comboBoxDisplayType.SelectedIndex = cds.DisplayType;
892 timer.Interval = cds.TimerInterval;
893 maskedTextBoxTimerInterval.Text = cds.TimerInterval.ToString();
894 textBoxScrollLoopSeparator.Text = cds.Separator;
896 SetupPixelDelegates();
898 if (iDisplay.IsOpen())
900 //We have a display connection
901 //Reflect that in our UI
903 tableLayoutPanel.Enabled = true;
904 panelDisplay.Enabled = true;
906 //Only setup brightness if display is open
907 trackBarBrightness.Minimum = iDisplay.MinBrightness();
908 trackBarBrightness.Maximum = iDisplay.MaxBrightness();
909 if (cds.Brightness < iDisplay.MinBrightness() || cds.Brightness > iDisplay.MaxBrightness())
911 //Brightness out of range, this can occur when using auto-detect
912 //Use max brightness instead
913 trackBarBrightness.Value = iDisplay.MaxBrightness();
914 iDisplay.SetBrightness(iDisplay.MaxBrightness());
918 trackBarBrightness.Value = cds.Brightness;
919 iDisplay.SetBrightness(cds.Brightness);
922 //Try compute the steps to something that makes sense
923 trackBarBrightness.LargeChange = Math.Max(1, (iDisplay.MaxBrightness() - iDisplay.MinBrightness()) / 5);
924 trackBarBrightness.SmallChange = 1;
927 buttonFill.Enabled = true;
928 buttonClear.Enabled = true;
929 buttonOpen.Enabled = false;
930 buttonClose.Enabled = true;
931 trackBarBrightness.Enabled = true;
932 toolStripStatusLabelConnect.Text = "Connected - " + iDisplay.Vendor() + " - " + iDisplay.Product();
933 //+ " - " + iDisplay.SerialNumber();
935 if (iDisplay.SupportPowerOnOff())
937 buttonPowerOn.Enabled = true;
938 buttonPowerOff.Enabled = true;
942 buttonPowerOn.Enabled = false;
943 buttonPowerOff.Enabled = false;
946 if (iDisplay.SupportClock())
948 buttonShowClock.Enabled = true;
949 buttonHideClock.Enabled = true;
953 buttonShowClock.Enabled = false;
954 buttonHideClock.Enabled = false;
958 //Check if Volume Label is supported. To date only MDM166AA supports that crap :)
959 checkBoxShowVolumeLabel.Enabled = iDisplay.IconCount(Display.TMiniDisplayIconType.EMiniDisplayIconVolumeLabel)>0;
961 if (cds.ShowVolumeLabel)
963 iDisplay.SetIconOn(Display.TMiniDisplayIconType.EMiniDisplayIconVolumeLabel);
967 iDisplay.SetIconOff(Display.TMiniDisplayIconType.EMiniDisplayIconVolumeLabel);
972 //Display is connection not available
973 //Reflect that in our UI
974 checkBoxShowVolumeLabel.Enabled = false;
975 tableLayoutPanel.Enabled = false;
976 panelDisplay.Enabled = false;
977 buttonFill.Enabled = false;
978 buttonClear.Enabled = false;
979 buttonOpen.Enabled = true;
980 buttonClose.Enabled = false;
981 trackBarBrightness.Enabled = false;
982 buttonPowerOn.Enabled = false;
983 buttonPowerOff.Enabled = false;
984 buttonShowClock.Enabled = false;
985 buttonHideClock.Enabled = false;
986 toolStripStatusLabelConnect.Text = "Disconnected";
987 toolStripStatusLabelPower.Text = "N/A";
996 /// <param name="sender"></param>
997 /// <param name="e"></param>
998 private void checkBoxShowVolumeLabel_CheckedChanged(object sender, EventArgs e)
1000 cds.ShowVolumeLabel = checkBoxShowVolumeLabel.Checked;
1001 Properties.Settings.Default.Save();
1005 private void checkBoxShowBorders_CheckedChanged(object sender, EventArgs e)
1007 //Save our show borders setting
1008 tableLayoutPanel.CellBorderStyle = (checkBoxShowBorders.Checked ? TableLayoutPanelCellBorderStyle.Single : TableLayoutPanelCellBorderStyle.None);
1009 cds.ShowBorders = checkBoxShowBorders.Checked;
1010 Properties.Settings.Default.Save();
1014 private void checkBoxConnectOnStartup_CheckedChanged(object sender, EventArgs e)
1016 //Save our connect on startup setting
1017 Properties.Settings.Default.DisplayConnectOnStartup = checkBoxConnectOnStartup.Checked;
1018 Properties.Settings.Default.Save();
1021 private void checkBoxMinimizeToTray_CheckedChanged(object sender, EventArgs e)
1023 //Save our "Minimize to tray" setting
1024 Properties.Settings.Default.MinimizeToTray = checkBoxMinimizeToTray.Checked;
1025 Properties.Settings.Default.Save();
1029 private void checkBoxStartMinimized_CheckedChanged(object sender, EventArgs e)
1031 //Save our "Start minimized" setting
1032 Properties.Settings.Default.StartMinimized = checkBoxStartMinimized.Checked;
1033 Properties.Settings.Default.Save();
1036 private void checkBoxAutoStart_CheckedChanged(object sender, EventArgs e)
1038 iStartupManager.Startup = checkBoxAutoStart.Checked;
1042 private void checkBoxReverseScreen_CheckedChanged(object sender, EventArgs e)
1044 //Save our reverse screen setting
1045 cds.ReverseScreen = checkBoxReverseScreen.Checked;
1046 Properties.Settings.Default.Save();
1047 SetupPixelDelegates();
1050 private void checkBoxInverseColors_CheckedChanged(object sender, EventArgs e)
1052 //Save our inverse colors setting
1053 cds.InverseColors = checkBoxInverseColors.Checked;
1054 Properties.Settings.Default.Save();
1055 SetupPixelDelegates();
1058 private void checkBoxScaleToFit_CheckedChanged(object sender, EventArgs e)
1060 //Save our scale to fit setting
1061 cds.ScaleToFit = checkBoxScaleToFit.Checked;
1062 Properties.Settings.Default.Save();
1064 labelMinFontSize.Enabled = cds.ScaleToFit;
1065 maskedTextBoxMinFontSize.Enabled = cds.ScaleToFit;
1068 private void MainForm_Resize(object sender, EventArgs e)
1070 if (WindowState == FormWindowState.Minimized)
1073 //iBmp = new System.Drawing.Bitmap(tableLayoutPanel.Width, tableLayoutPanel.Height, PixelFormat.Format32bppArgb);
1074 iCreateBitmap = true;
1078 private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
1080 CloseDisplayConnection();
1082 e.Cancel = iClosing;
1085 public void StartServer()
1087 iServiceHost = new ServiceHost
1090 new Uri[] { new Uri("net.tcp://localhost:8001/") }
1093 iServiceHost.AddServiceEndpoint(typeof(IService), new NetTcpBinding(SecurityMode.None, true), "DisplayService");
1094 iServiceHost.Open();
1097 public void StopServer()
1099 if (iClients.Count > 0 && !iClosing)
1103 BroadcastCloseEvent();
1107 if (MessageBox.Show("Force exit?", "Waiting for clients...", MessageBoxButtons.YesNo, MessageBoxIcon.Warning) == DialogResult.Yes)
1109 iClosing = false; //We make sure we force close if asked twice
1114 //We removed that as it often lags for some reason
1115 //iServiceHost.Close();
1119 public void BroadcastCloseEvent()
1121 Trace.TraceInformation("BroadcastCloseEvent - start");
1123 var inactiveClients = new List<string>();
1124 foreach (var client in iClients)
1126 //if (client.Key != eventData.ClientName)
1130 Trace.TraceInformation("BroadcastCloseEvent - " + client.Key);
1131 client.Value.Callback.OnCloseOrder(/*eventData*/);
1133 catch (Exception ex)
1135 inactiveClients.Add(client.Key);
1140 if (inactiveClients.Count > 0)
1142 foreach (var client in inactiveClients)
1144 iClients.Remove(client);
1145 Program.iMainForm.treeViewClients.Nodes.Remove(Program.iMainForm.treeViewClients.Nodes.Find(client, false)[0]);
1149 if (iClients.Count==0)
1156 /// Just remove all our fields.
1158 private void ClearLayout()
1160 tableLayoutPanel.Controls.Clear();
1161 tableLayoutPanel.RowStyles.Clear();
1162 tableLayoutPanel.ColumnStyles.Clear();
1166 /// Just launch a demo client.
1168 private void StartNewClient(string aTopText = "", string aBottomText = "")
1170 Thread clientThread = new Thread(SharpDisplayClient.Program.MainWithParams);
1171 SharpDisplayClient.StartParams myParams = new SharpDisplayClient.StartParams(new Point(this.Right, this.Top),aTopText,aBottomText);
1172 clientThread.Start(myParams);
1176 private void buttonStartClient_Click(object sender, EventArgs e)
1181 private void buttonSuspend_Click(object sender, EventArgs e)
1183 LastTickTime = DateTime.Now; //Reset timer to prevent jump
1184 timer.Enabled = !timer.Enabled;
1187 buttonSuspend.Text = "Run";
1191 buttonSuspend.Text = "Pause";
1195 private void buttonCloseClients_Click(object sender, EventArgs e)
1197 BroadcastCloseEvent();
1200 private void treeViewClients_AfterSelect(object sender, TreeViewEventArgs e)
1209 /// <param name="aSessionId"></param>
1210 /// <param name="aCallback"></param>
1211 public void AddClientThreadSafe(string aSessionId, ICallback aCallback)
1213 if (this.InvokeRequired)
1215 //Not in the proper thread, invoke ourselves
1216 AddClientDelegate d = new AddClientDelegate(AddClientThreadSafe);
1217 this.Invoke(d, new object[] { aSessionId, aCallback });
1221 //We are in the proper thread
1222 //Add this session to our collection of clients
1223 ClientData newClient = new ClientData(aSessionId, aCallback);
1224 Program.iMainForm.iClients.Add(aSessionId, newClient);
1225 //Add this session to our UI
1226 UpdateClientTreeViewNode(newClient);
1233 /// <param name="aSessionId"></param>
1234 public void RemoveClientThreadSafe(string aSessionId)
1236 if (this.InvokeRequired)
1238 //Not in the proper thread, invoke ourselves
1239 RemoveClientDelegate d = new RemoveClientDelegate(RemoveClientThreadSafe);
1240 this.Invoke(d, new object[] { aSessionId });
1244 //We are in the proper thread
1245 //Remove this session from both client collection and UI tree view
1246 if (Program.iMainForm.iClients.Keys.Contains(aSessionId))
1248 Program.iMainForm.iClients.Remove(aSessionId);
1249 Program.iMainForm.treeViewClients.Nodes.Remove(Program.iMainForm.treeViewClients.Nodes.Find(aSessionId, false)[0]);
1252 if (iClients.Count == 0)
1254 //Clear our screen when last client disconnects
1259 //We were closing our form
1260 //All clients are now closed
1261 //Just resume our close operation
1272 /// <param name="aSessionId"></param>
1273 /// <param name="aLayout"></param>
1274 public void SetClientLayoutThreadSafe(string aSessionId, TableLayout aLayout)
1276 if (this.InvokeRequired)
1278 //Not in the proper thread, invoke ourselves
1279 SetLayoutDelegate d = new SetLayoutDelegate(SetClientLayoutThreadSafe);
1280 this.Invoke(d, new object[] { aSessionId, aLayout });
1284 ClientData client = iClients[aSessionId];
1287 client.Layout = aLayout;
1288 UpdateTableLayoutPanel(client);
1290 UpdateClientTreeViewNode(client);
1298 /// <param name="aSessionId"></param>
1299 /// <param name="aField"></param>
1300 public void SetClientFieldThreadSafe(string aSessionId, DataField aField)
1302 if (this.InvokeRequired)
1304 //Not in the proper thread, invoke ourselves
1305 SetFieldDelegate d = new SetFieldDelegate(SetClientFieldThreadSafe);
1306 this.Invoke(d, new object[] { aSessionId, aField });
1310 //We are in the proper thread
1311 //Call the non-thread-safe variant
1312 SetClientField(aSessionId, aField);
1319 /// <param name="aSessionId"></param>
1320 /// <param name="aField"></param>
1321 private void SetClientField(string aSessionId, DataField aField)
1323 SetCurrentClient(aSessionId);
1324 ClientData client = iClients[aSessionId];
1327 bool somethingChanged = false;
1329 //Make sure all our fields are in place
1330 while (client.Fields.Count < (aField.Index + 1))
1332 //Add a text field with proper index
1333 client.Fields.Add(new DataField(client.Fields.Count));
1334 somethingChanged = true;
1337 if (client.Fields[aField.Index].IsSameLayout(aField))
1339 //Same layout just update our field
1340 client.Fields[aField.Index] = aField;
1342 if (aField.IsText && tableLayoutPanel.Controls[aField.Index] is MarqueeLabel)
1344 //Text field control already in place, just change the text
1345 MarqueeLabel label = (MarqueeLabel)tableLayoutPanel.Controls[aField.Index];
1346 somethingChanged = (label.Text != aField.Text || label.TextAlign != aField.Alignment);
1347 label.Text = aField.Text;
1348 label.TextAlign = aField.Alignment;
1350 else if (aField.IsBitmap && tableLayoutPanel.Controls[aField.Index] is PictureBox)
1352 somethingChanged = true; //TODO: Bitmap comp or should we leave that to clients?
1353 //Bitmap field control already in place just change the bitmap
1354 PictureBox pictureBox = (PictureBox)tableLayoutPanel.Controls[aField.Index];
1355 pictureBox.Image = aField.Bitmap;
1359 somethingChanged = true;
1360 //The requested control in our layout it not of the correct type
1361 //Wrong control type, re-create them all
1362 UpdateTableLayoutPanel(iCurrentClientData);
1367 somethingChanged = true;
1368 //Different layout, need to rebuild it
1369 client.Fields[aField.Index] = aField;
1370 UpdateTableLayoutPanel(iCurrentClientData);
1374 if (somethingChanged)
1376 UpdateClientTreeViewNode(client);
1384 /// <param name="aTexts"></param>
1385 public void SetClientFieldsThreadSafe(string aSessionId, System.Collections.Generic.IList<DataField> aFields)
1387 if (this.InvokeRequired)
1389 //Not in the proper thread, invoke ourselves
1390 SetFieldsDelegate d = new SetFieldsDelegate(SetClientFieldsThreadSafe);
1391 this.Invoke(d, new object[] { aSessionId, aFields });
1395 //Put each our text fields in a label control
1396 foreach (DataField field in aFields)
1398 SetClientField(aSessionId, field);
1406 /// <param name="aSessionId"></param>
1407 /// <param name="aName"></param>
1408 public void SetClientNameThreadSafe(string aSessionId, string aName)
1410 if (this.InvokeRequired)
1412 //Not in the proper thread, invoke ourselves
1413 SetClientNameDelegate d = new SetClientNameDelegate(SetClientNameThreadSafe);
1414 this.Invoke(d, new object[] { aSessionId, aName });
1418 //We are in the proper thread
1420 ClientData client = iClients[aSessionId];
1424 client.Name = aName;
1425 //Update our tree-view
1426 UpdateClientTreeViewNode(client);
1434 /// <param name="aClient"></param>
1435 private void UpdateClientTreeViewNode(ClientData aClient)
1437 if (aClient == null)
1442 TreeNode node = null;
1443 //Check that our client node already exists
1444 //Get our client root node using its key which is our session ID
1445 TreeNode[] nodes = treeViewClients.Nodes.Find(aClient.SessionId, false);
1446 if (nodes.Count()>0)
1448 //We already have a node for that client
1450 //Clear children as we are going to recreate them below
1455 //Client node does not exists create a new one
1456 treeViewClients.Nodes.Add(aClient.SessionId, aClient.SessionId);
1457 node = treeViewClients.Nodes.Find(aClient.SessionId, false)[0];
1463 if (aClient.Name != "")
1465 //We have a name, us it as text for our root node
1466 node.Text = aClient.Name;
1467 //Add a child with SessionId
1468 node.Nodes.Add(new TreeNode(aClient.SessionId));
1472 //No name, use session ID instead
1473 node.Text = aClient.SessionId;
1476 if (aClient.Fields.Count > 0)
1478 //Create root node for our texts
1479 TreeNode textsRoot = new TreeNode("Fields");
1480 node.Nodes.Add(textsRoot);
1481 //For each text add a new entry
1482 foreach (DataField field in aClient.Fields)
1484 if (!field.IsBitmap)
1486 DataField textField = (DataField)field;
1487 textsRoot.Nodes.Add(new TreeNode("[Text]" + textField.Text));
1491 textsRoot.Nodes.Add(new TreeNode("[Bitmap]"));
1501 /// Update our table layout row styles to make sure each rows have similar height
1503 private void UpdateTableLayoutRowStyles()
1505 foreach (RowStyle rowStyle in tableLayoutPanel.RowStyles)
1507 rowStyle.SizeType = SizeType.Percent;
1508 rowStyle.Height = 100 / tableLayoutPanel.RowCount;
1514 /// Empty and recreate our table layout with the given number of columns and rows.
1515 /// Sizes of rows and columns are uniform.
1517 /// <param name="aColumn"></param>
1518 /// <param name="aRow"></param>
1519 private void UpdateTableLayoutPanel(int aColumn, int aRow)
1521 tableLayoutPanel.Controls.Clear();
1522 tableLayoutPanel.RowStyles.Clear();
1523 tableLayoutPanel.ColumnStyles.Clear();
1524 tableLayoutPanel.RowCount = 0;
1525 tableLayoutPanel.ColumnCount = 0;
1527 while (tableLayoutPanel.RowCount < aRow)
1529 tableLayoutPanel.RowCount++;
1532 while (tableLayoutPanel.ColumnCount < aColumn)
1534 tableLayoutPanel.ColumnCount++;
1537 for (int i = 0; i < tableLayoutPanel.ColumnCount; i++)
1539 //Create our column styles
1540 this.tableLayoutPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100 / tableLayoutPanel.ColumnCount));
1542 for (int j = 0; j < tableLayoutPanel.RowCount; j++)
1546 //Create our row styles
1547 this.tableLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100 / tableLayoutPanel.RowCount));
1550 MarqueeLabel control = new SharpDisplayManager.MarqueeLabel();
1551 control.AutoEllipsis = true;
1552 control.AutoSize = true;
1553 control.BackColor = System.Drawing.Color.Transparent;
1554 control.Dock = System.Windows.Forms.DockStyle.Fill;
1555 control.Location = new System.Drawing.Point(1, 1);
1556 control.Margin = new System.Windows.Forms.Padding(0);
1557 control.Name = "marqueeLabelCol" + aColumn + "Row" + aRow;
1558 control.OwnTimer = false;
1559 control.PixelsPerSecond = 64;
1560 control.Separator = cds.Separator;
1561 control.MinFontSize = cds.MinFontSize;
1562 control.ScaleToFit = cds.ScaleToFit;
1563 //control.Size = new System.Drawing.Size(254, 30);
1564 //control.TabIndex = 2;
1565 control.Font = cds.Font;
1566 control.Text = "ABCDEFGHIJKLMNOPQRST-0123456789";
1567 control.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
1568 control.UseCompatibleTextRendering = true;
1570 tableLayoutPanel.Controls.Add(control, i, j);
1579 /// Update our display table layout.
1581 /// <param name="aLayout"></param>
1582 private void UpdateTableLayoutPanel(ClientData aClient)
1584 if (aClient == null)
1591 TableLayout layout = aClient.Layout;
1594 tableLayoutPanel.Controls.Clear();
1595 tableLayoutPanel.RowStyles.Clear();
1596 tableLayoutPanel.ColumnStyles.Clear();
1597 tableLayoutPanel.RowCount = 0;
1598 tableLayoutPanel.ColumnCount = 0;
1600 while (tableLayoutPanel.RowCount < layout.Rows.Count)
1602 tableLayoutPanel.RowCount++;
1605 while (tableLayoutPanel.ColumnCount < layout.Columns.Count)
1607 tableLayoutPanel.ColumnCount++;
1610 for (int i = 0; i < tableLayoutPanel.ColumnCount; i++)
1612 //Create our column styles
1613 this.tableLayoutPanel.ColumnStyles.Add(layout.Columns[i]);
1615 for (int j = 0; j < tableLayoutPanel.RowCount; j++)
1619 //Create our row styles
1620 this.tableLayoutPanel.RowStyles.Add(layout.Rows[j]);
1623 //Check if we already have a control
1624 Control existingControl = tableLayoutPanel.GetControlFromPosition(i,j);
1625 if (existingControl!=null)
1627 //We already have a control in that cell as a results of row/col spanning
1628 //Move on to next cell then
1634 //Check if a client field already exists for that cell
1635 if (aClient.Fields.Count <= tableLayoutPanel.Controls.Count)
1637 //No client field specified, create a text field by default
1638 aClient.Fields.Add(new DataField(aClient.Fields.Count));
1641 //Create a control corresponding to the field specified for that cell
1642 DataField field = aClient.Fields[tableLayoutPanel.Controls.Count];
1643 Control control = CreateControlForDataField(field);
1645 //Add newly created control to our table layout at the specified row and column
1646 tableLayoutPanel.Controls.Add(control, i, j);
1647 //Make sure we specify row and column span for that new control
1648 tableLayoutPanel.SetRowSpan(control,field.RowSpan);
1649 tableLayoutPanel.SetColumnSpan(control, field.ColumnSpan);
1654 while (aClient.Fields.Count > fieldCount)
1656 //We have too much fields for this layout
1657 //Just discard them until we get there
1658 aClient.Fields.RemoveAt(aClient.Fields.Count-1);
1665 /// Check our type of data field and create corresponding control
1667 /// <param name="aField"></param>
1668 private Control CreateControlForDataField(DataField aField)
1670 Control control=null;
1671 if (!aField.IsBitmap)
1673 MarqueeLabel label = new SharpDisplayManager.MarqueeLabel();
1674 label.AutoEllipsis = true;
1675 label.AutoSize = true;
1676 label.BackColor = System.Drawing.Color.Transparent;
1677 label.Dock = System.Windows.Forms.DockStyle.Fill;
1678 label.Location = new System.Drawing.Point(1, 1);
1679 label.Margin = new System.Windows.Forms.Padding(0);
1680 label.Name = "marqueeLabel" + aField.Index;
1681 label.OwnTimer = false;
1682 label.PixelsPerSecond = cds.ScrollingSpeedInPixelsPerSecond;
1683 label.Separator = cds.Separator;
1684 label.MinFontSize = cds.MinFontSize;
1685 label.ScaleToFit = cds.ScaleToFit;
1686 //control.Size = new System.Drawing.Size(254, 30);
1687 //control.TabIndex = 2;
1688 label.Font = cds.Font;
1690 label.TextAlign = aField.Alignment;
1691 label.UseCompatibleTextRendering = true;
1692 label.Text = aField.Text;
1698 //Create picture box
1699 PictureBox picture = new PictureBox();
1700 picture.AutoSize = true;
1701 picture.BackColor = System.Drawing.Color.Transparent;
1702 picture.Dock = System.Windows.Forms.DockStyle.Fill;
1703 picture.Location = new System.Drawing.Point(1, 1);
1704 picture.Margin = new System.Windows.Forms.Padding(0);
1705 picture.Name = "pictureBox" + aField;
1707 picture.Image = aField.Bitmap;
1716 /// Called when the user selected a new display type.
1718 /// <param name="sender"></param>
1719 /// <param name="e"></param>
1720 private void comboBoxDisplayType_SelectedIndexChanged(object sender, EventArgs e)
1722 //Store the selected display type in our settings
1723 Properties.Settings.Default.CurrentDisplayIndex = comboBoxDisplayType.SelectedIndex;
1724 cds.DisplayType = comboBoxDisplayType.SelectedIndex;
1725 Properties.Settings.Default.Save();
1727 //Try re-opening the display connection if we were already connected.
1728 //Otherwise just update our status to reflect display type change.
1729 if (iDisplay.IsOpen())
1731 OpenDisplayConnection();
1739 private void maskedTextBoxTimerInterval_TextChanged(object sender, EventArgs e)
1741 if (maskedTextBoxTimerInterval.Text != "")
1743 int interval = Convert.ToInt32(maskedTextBoxTimerInterval.Text);
1747 timer.Interval = interval;
1748 cds.TimerInterval = timer.Interval;
1749 Properties.Settings.Default.Save();
1754 private void maskedTextBoxMinFontSize_TextChanged(object sender, EventArgs e)
1756 if (maskedTextBoxMinFontSize.Text != "")
1758 int minFontSize = Convert.ToInt32(maskedTextBoxMinFontSize.Text);
1760 if (minFontSize > 0)
1762 cds.MinFontSize = minFontSize;
1763 Properties.Settings.Default.Save();
1764 //We need to recreate our layout for that change to take effect
1765 UpdateTableLayoutPanel(iCurrentClientData);
1771 private void maskedTextBoxScrollingSpeed_TextChanged(object sender, EventArgs e)
1773 if (maskedTextBoxScrollingSpeed.Text != "")
1775 int scrollingSpeed = Convert.ToInt32(maskedTextBoxScrollingSpeed.Text);
1777 if (scrollingSpeed > 0)
1779 cds.ScrollingSpeedInPixelsPerSecond = scrollingSpeed;
1780 Properties.Settings.Default.Save();
1781 //We need to recreate our layout for that change to take effect
1782 UpdateTableLayoutPanel(iCurrentClientData);
1787 private void textBoxScrollLoopSeparator_TextChanged(object sender, EventArgs e)
1789 cds.Separator = textBoxScrollLoopSeparator.Text;
1790 Properties.Settings.Default.Save();
1792 //Update our text fields
1793 foreach (MarqueeLabel ctrl in tableLayoutPanel.Controls)
1795 ctrl.Separator = cds.Separator;
1800 private void buttonPowerOn_Click(object sender, EventArgs e)
1805 private void buttonPowerOff_Click(object sender, EventArgs e)
1807 iDisplay.PowerOff();
1810 private void buttonShowClock_Click(object sender, EventArgs e)
1812 iDisplay.ShowClock();
1815 private void buttonHideClock_Click(object sender, EventArgs e)
1817 iDisplay.HideClock();
1820 private void buttonUpdate_Click(object sender, EventArgs e)
1822 InstallUpdateSyncWithInfo();
1826 private void InstallUpdateSyncWithInfo()
1828 UpdateCheckInfo info = null;
1830 if (ApplicationDeployment.IsNetworkDeployed)
1832 ApplicationDeployment ad = ApplicationDeployment.CurrentDeployment;
1836 info = ad.CheckForDetailedUpdate();
1839 catch (DeploymentDownloadException dde)
1841 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);
1844 catch (InvalidDeploymentException ide)
1846 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);
1849 catch (InvalidOperationException ioe)
1851 MessageBox.Show("This application cannot be updated. It is likely not a ClickOnce application. Error: " + ioe.Message);
1855 if (info.UpdateAvailable)
1857 Boolean doUpdate = true;
1859 if (!info.IsUpdateRequired)
1861 DialogResult dr = MessageBox.Show("An update is available. Would you like to update the application now?", "Update Available", MessageBoxButtons.OKCancel);
1862 if (!(DialogResult.OK == dr))
1869 // Display a message that the app MUST reboot. Display the minimum required version.
1870 MessageBox.Show("This application has detected a mandatory update from your current " +
1871 "version to version " + info.MinimumRequiredVersion.ToString() +
1872 ". The application will now install the update and restart.",
1873 "Update Available", MessageBoxButtons.OK,
1874 MessageBoxIcon.Information);
1882 MessageBox.Show("The application has been upgraded, and will now restart.");
1883 Application.Restart();
1885 catch (DeploymentDownloadException dde)
1887 MessageBox.Show("Cannot install the latest version of the application. \n\nPlease check your network connection, or try again later. Error: " + dde);
1894 MessageBox.Show("You are already running the latest version.", "Application up-to-date");
1903 private void SysTrayHideShow()
1909 WindowState = FormWindowState.Normal;
1914 /// Use to handle minimize events.
1916 /// <param name="sender"></param>
1917 /// <param name="e"></param>
1918 private void MainForm_SizeChanged(object sender, EventArgs e)
1920 if (WindowState == FormWindowState.Minimized && Properties.Settings.Default.MinimizeToTray)
1933 /// <param name="sender"></param>
1934 /// <param name="e"></param>
1935 private void tableLayoutPanel_SizeChanged(object sender, EventArgs e)
1937 //Our table layout size has changed which means our display size has changed.
1938 //We need to re-create our bitmap.
1939 iCreateBitmap = true;
1944 /// A UI thread copy of a client relevant data.
1945 /// Keeping this copy in the UI thread helps us deal with threading issues.
1947 public class ClientData
1949 public ClientData(string aSessionId, ICallback aCallback)
1951 SessionId = aSessionId;
1953 Fields = new List<DataField>();
1954 Layout = new TableLayout(1, 2); //Default to one column and two rows
1955 Callback = aCallback;
1958 public string SessionId { get; set; }
1959 public string Name { get; set; }
1960 public List<DataField> Fields { get; set; }
1961 public TableLayout Layout { get; set; }
1962 public ICallback Callback { get; set; }