Make sure action edit combo box for enums is long enough.
2 // Copyright (C) 2014-2015 Stéphane Lenclud.
4 // This file is part of SharpDisplayManager.
6 // SharpDisplayManager is free software: you can redistribute it and/or modify
7 // it under the terms of the GNU General Public License as published by
8 // the Free Software Foundation, either version 3 of the License, or
9 // (at your option) any later version.
11 // SharpDisplayManager is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
16 // You should have received a copy of the GNU General Public License
17 // along with SharpDisplayManager. If not, see <http://www.gnu.org/licenses/>.
21 using System.Collections.Generic;
22 using System.ComponentModel;
27 using System.Threading.Tasks;
28 using System.Windows.Forms;
30 using CodeProject.Dialog;
31 using System.Drawing.Imaging;
32 using System.ServiceModel;
33 using System.Threading;
34 using System.Diagnostics;
35 using System.Deployment.Application;
36 using System.Reflection;
38 using NAudio.CoreAudioApi;
39 using NAudio.CoreAudioApi.Interfaces;
40 using System.Runtime.InteropServices;
45 using SharpDisplayClient;
47 using MiniDisplayInterop;
48 using SharpLib.Display;
51 namespace SharpDisplayManager
54 public delegate uint ColorProcessingDelegate(int aX, int aY, uint aPixel);
56 public delegate int CoordinateTranslationDelegate(System.Drawing.Bitmap aBmp, int aInt);
58 //Delegates are used for our thread safe method
59 public delegate void AddClientDelegate(string aSessionId, ICallback aCallback);
61 public delegate void RemoveClientDelegate(string aSessionId);
63 public delegate void SetFieldDelegate(string SessionId, DataField aField);
65 public delegate void SetFieldsDelegate(string SessionId, System.Collections.Generic.IList<DataField> aFields);
67 public delegate void SetLayoutDelegate(string SessionId, TableLayout aLayout);
69 public delegate void SetClientNameDelegate(string aSessionId, string aName);
71 public delegate void SetClientPriorityDelegate(string aSessionId, uint aPriority);
73 public delegate void PlainUpdateDelegate();
75 public delegate void WndProcDelegate(ref Message aMessage);
78 /// Our Display manager main form
80 [System.ComponentModel.DesignerCategory("Form")]
81 public partial class MainForm : MainFormHid, IMMNotificationClient
83 //public ManagerEventAction iManager = new ManagerEventAction();
84 DateTime LastTickTime;
86 System.Drawing.Bitmap iBmp;
87 bool iCreateBitmap; //Workaround render to bitmap issues when minimized
88 ServiceHost iServiceHost;
89 // Our collection of clients sorted by session id.
90 public Dictionary<string, ClientData> iClients;
91 // The name of the client which informations are currently displayed.
92 public string iCurrentClientSessionId;
93 ClientData iCurrentClientData;
97 public bool iSkipFrameRendering;
98 //Function pointer for pixel color filtering
99 ColorProcessingDelegate iColorFx;
100 //Function pointer for pixel X coordinate intercept
101 CoordinateTranslationDelegate iScreenX;
102 //Function pointer for pixel Y coordinate intercept
103 CoordinateTranslationDelegate iScreenY;
105 private MMDeviceEnumerator iMultiMediaDeviceEnumerator;
106 private MMDevice iMultiMediaDevice;
108 private NetworkManager iNetworkManager;
111 /// CEC - Consumer Electronic Control.
112 /// Notably used to turn TV on and off as Windows broadcast monitor on and off notifications.
114 private ConsumerElectronicControl iCecManager;
117 /// Manage run when Windows startup option
119 private StartupManager iStartupManager;
122 /// System notification icon used to hide our application from the task bar.
124 private SharpLib.Notification.Control iNotifyIcon;
127 /// System recording notification icon.
129 private SharpLib.Notification.Control iRecordingNotification;
134 RichTextBoxTextWriter iWriter;
138 /// Allow user to receive window messages;
140 public event WndProcDelegate OnWndProc;
144 ManagerEventAction.Current = Properties.Settings.Default.Actions;
145 if (ManagerEventAction.Current == null)
147 //No actions in our settings yet
148 ManagerEventAction.Current = new ManagerEventAction();
149 Properties.Settings.Default.Actions = ManagerEventAction.Current;
153 //We loaded actions from our settings
154 //We need to hook them with corresponding events
155 ManagerEventAction.Current.Init();
157 iSkipFrameRendering = false;
159 iCurrentClientSessionId = "";
160 iCurrentClientData = null;
161 LastTickTime = DateTime.Now;
162 //Instantiate our display and register for events notifications
163 iDisplay = new Display();
164 iDisplay.OnOpened += OnDisplayOpened;
165 iDisplay.OnClosed += OnDisplayClosed;
167 iClients = new Dictionary<string, ClientData>();
168 iStartupManager = new StartupManager();
169 iNotifyIcon = new SharpLib.Notification.Control();
170 iRecordingNotification = new SharpLib.Notification.Control();
172 //Have our designer initialize its controls
173 InitializeComponent();
175 //Redirect console output
176 iWriter = new RichTextBoxTextWriter(richTextBoxLogs);
177 Console.SetOut(iWriter);
179 //Populate device types
180 PopulateDeviceTypes();
182 //Populate optical drives
183 PopulateOpticalDrives();
185 //Initial status update
188 //We have a bug when drawing minimized and reusing our bitmap
189 //Though I could not reproduce it on Windows 10
190 iBmp = new System.Drawing.Bitmap(iTableLayoutPanel.Width, iTableLayoutPanel.Height,
191 PixelFormat.Format32bppArgb);
192 iCreateBitmap = false;
194 //Minimize our window if desired
195 if (Properties.Settings.Default.StartMinimized)
197 WindowState = FormWindowState.Minimized;
205 /// <param name="sender"></param>
206 /// <param name="e"></param>
207 private void MainForm_Load(object sender, EventArgs e)
209 //Check if we are running a Click Once deployed application
210 if (ApplicationDeployment.IsNetworkDeployed)
212 //This is a proper Click Once installation, fetch and show our version number
213 this.Text += " - v" + ApplicationDeployment.CurrentDeployment.CurrentVersion;
217 //Not a proper Click Once installation, assuming development build then
218 this.Text += " - development";
222 iMultiMediaDeviceEnumerator = new MMDeviceEnumerator();
223 iMultiMediaDeviceEnumerator.RegisterEndpointNotificationCallback(this);
224 UpdateAudioDeviceAndMasterVolumeThreadSafe();
227 iNetworkManager = new NetworkManager();
228 iNetworkManager.OnConnectivityChanged += OnConnectivityChanged;
229 UpdateNetworkStatus();
232 iCecManager = new ConsumerElectronicControl();
233 OnWndProc += iCecManager.OnWndProc;
237 PopulateEventsTreeView();
239 //Setup notification icon
242 //Setup recording notification
243 SetupRecordingNotification();
245 // To make sure start up with minimize to tray works
246 if (WindowState == FormWindowState.Minimized && Properties.Settings.Default.MinimizeToTray)
252 //When not debugging we want the screen to be empty until a client takes over
255 //When developing we want at least one client for testing
256 StartNewClient("abcdefghijklmnopqrst-0123456789", "ABCDEFGHIJKLMNOPQRST-0123456789");
259 //Open display connection on start-up if needed
260 if (Properties.Settings.Default.DisplayConnectOnStartup)
262 OpenDisplayConnection();
265 //Start our server so that we can get client requests
268 //Register for HID events
269 RegisterHidDevices();
271 //Start Idle client if needed
272 if (Properties.Settings.Default.StartIdleClient)
279 /// Called when our display is opened.
281 /// <param name="aDisplay"></param>
282 private void OnDisplayOpened(Display aDisplay)
284 //Make sure we resume frame rendering
285 iSkipFrameRendering = false;
287 //Set our screen size now that our display is connected
288 //Our panelDisplay is the container of our tableLayoutPanel
289 //tableLayoutPanel will resize itself to fit the client size of our panelDisplay
290 //panelDisplay needs an extra 2 pixels for borders on each sides
291 //tableLayoutPanel will eventually be the exact size of our display
292 Size size = new Size(iDisplay.WidthInPixels() + 2, iDisplay.HeightInPixels() + 2);
293 panelDisplay.Size = size;
295 //Our display was just opened, update our UI
297 //Initiate asynchronous request
298 iDisplay.RequestFirmwareRevision();
301 UpdateMasterVolumeThreadSafe();
303 UpdateNetworkStatus();
306 //Testing icon in debug, no arm done if icon not supported
307 //iDisplay.SetIconStatus(Display.TMiniDisplayIconType.EMiniDisplayIconRecording, 0, 1);
308 //iDisplay.SetAllIconsStatus(2);
314 /// Populate tree view with events and actions
316 private void PopulateEventsTreeView()
318 //Disable action buttons
319 buttonAddAction.Enabled = false;
320 buttonDeleteAction.Enabled = false;
322 Event currentEvent = CurrentEvent();
323 SharpLib.Ear.Action currentAction = CurrentAction();
324 TreeNode treeNodeToSelect = null;
327 iTreeViewEvents.Nodes.Clear();
328 //Populate registered events
329 foreach (string key in ManagerEventAction.Current.Events.Keys)
331 Event e = ManagerEventAction.Current.Events[key];
332 TreeNode eventNode = iTreeViewEvents.Nodes.Add(key, e.Name);
334 eventNode.Nodes.Add(key + ".Description", e.Description);
335 TreeNode actionsNodes = eventNode.Nodes.Add(key + ".Actions", "Actions");
337 // Add our actions for that event
338 foreach (SharpLib.Ear.Action a in e.Actions)
340 TreeNode actionNode = actionsNodes.Nodes.Add(a.Brief());
342 if (a == currentAction)
344 treeNodeToSelect = actionNode;
349 iTreeViewEvents.ExpandAll();
350 SelectEvent(currentEvent);
352 if (treeNodeToSelect != null)
354 iTreeViewEvents.SelectedNode = treeNodeToSelect;
356 else if (iTreeViewEvents.SelectedNode != null && iTreeViewEvents.SelectedNode.Nodes[1].GetNodeCount(false) > 0)
358 //Select the last action if any
359 iTreeViewEvents.SelectedNode =
360 iTreeViewEvents.SelectedNode.Nodes[1].Nodes[
361 iTreeViewEvents.SelectedNode.Nodes[1].GetNodeCount(false) - 1];
367 /// Called when our display is closed.
369 /// <param name="aDisplay"></param>
370 private void OnDisplayClosed(Display aDisplay)
372 //Our display was just closed, update our UI consequently
376 public void OnConnectivityChanged(NetworkManager aNetwork, NLM_CONNECTIVITY newConnectivity)
378 //Update network status
379 UpdateNetworkStatus();
383 /// Update our Network Status
385 private void UpdateNetworkStatus()
387 if (iDisplay.IsOpen())
389 iDisplay.SetIconOnOff(MiniDisplay.IconType.Internet,
390 iNetworkManager.NetworkListManager.IsConnectedToInternet);
391 iDisplay.SetIconOnOff(MiniDisplay.IconType.NetworkSignal, iNetworkManager.NetworkListManager.IsConnected);
396 int iLastNetworkIconIndex = 0;
397 int iUpdateCountSinceLastNetworkAnimation = 0;
402 private void UpdateNetworkSignal(DateTime aLastTickTime, DateTime aNewTickTime)
404 iUpdateCountSinceLastNetworkAnimation++;
405 iUpdateCountSinceLastNetworkAnimation = iUpdateCountSinceLastNetworkAnimation%4;
407 if (iDisplay.IsOpen() && iNetworkManager.NetworkListManager.IsConnected &&
408 iUpdateCountSinceLastNetworkAnimation == 0)
410 int iconCount = iDisplay.IconCount(MiniDisplay.IconType.NetworkSignal);
413 //Prevents div by zero and other undefined behavior
416 iLastNetworkIconIndex++;
417 iLastNetworkIconIndex = iLastNetworkIconIndex%(iconCount*2);
418 for (int i = 0; i < iconCount; i++)
420 if (i < iLastNetworkIconIndex && !(i == 0 && iLastNetworkIconIndex > 3) &&
421 !(i == 1 && iLastNetworkIconIndex > 4))
423 iDisplay.SetIconOn(MiniDisplay.IconType.NetworkSignal, i);
427 iDisplay.SetIconOff(MiniDisplay.IconType.NetworkSignal, i);
436 /// Receive volume change notification and reflect changes on our slider.
438 /// <param name="data"></param>
439 public void OnVolumeNotificationThreadSafe(AudioVolumeNotificationData data)
441 UpdateMasterVolumeThreadSafe();
445 /// Update master volume when user moves our slider.
447 /// <param name="sender"></param>
448 /// <param name="e"></param>
449 private void trackBarMasterVolume_Scroll(object sender, EventArgs e)
451 //Just like Windows Volume Mixer we unmute if the volume is adjusted
452 iMultiMediaDevice.AudioEndpointVolume.Mute = false;
453 //Set volume level according to our volume slider new position
454 iMultiMediaDevice.AudioEndpointVolume.MasterVolumeLevelScalar = trackBarMasterVolume.Value/100.0f;
459 /// Mute check box changed.
461 /// <param name="sender"></param>
462 /// <param name="e"></param>
463 private void checkBoxMute_CheckedChanged(object sender, EventArgs e)
465 iMultiMediaDevice.AudioEndpointVolume.Mute = checkBoxMute.Checked;
469 /// Device State Changed
471 public void OnDeviceStateChanged([MarshalAs(UnmanagedType.LPWStr)] string deviceId,
472 [MarshalAs(UnmanagedType.I4)] DeviceState newState)
479 public void OnDeviceAdded([MarshalAs(UnmanagedType.LPWStr)] string pwstrDeviceId)
486 public void OnDeviceRemoved([MarshalAs(UnmanagedType.LPWStr)] string deviceId)
491 /// Default Device Changed
493 public void OnDefaultDeviceChanged(DataFlow flow, Role role,
494 [MarshalAs(UnmanagedType.LPWStr)] string defaultDeviceId)
496 if (role == Role.Multimedia && flow == DataFlow.Render)
498 UpdateAudioDeviceAndMasterVolumeThreadSafe();
503 /// Property Value Changed
505 /// <param name="pwstrDeviceId"></param>
506 /// <param name="key"></param>
507 public void OnPropertyValueChanged([MarshalAs(UnmanagedType.LPWStr)] string pwstrDeviceId, PropertyKey key)
515 /// Update master volume indicators based our current system states.
516 /// This typically includes volume levels and mute status.
518 private void UpdateMasterVolumeThreadSafe()
520 if (this.InvokeRequired)
522 //Not in the proper thread, invoke ourselves
523 PlainUpdateDelegate d = new PlainUpdateDelegate(UpdateMasterVolumeThreadSafe);
524 this.Invoke(d, new object[] {});
528 //Update volume slider
529 float volumeLevelScalar = iMultiMediaDevice.AudioEndpointVolume.MasterVolumeLevelScalar;
530 trackBarMasterVolume.Value = Convert.ToInt32(volumeLevelScalar*100);
531 //Update mute checkbox
532 checkBoxMute.Checked = iMultiMediaDevice.AudioEndpointVolume.Mute;
534 //If our display connection is open we need to update its icons
535 if (iDisplay.IsOpen())
537 //First take care our our volume level icons
538 int volumeIconCount = iDisplay.IconCount(MiniDisplay.IconType.Volume);
539 if (volumeIconCount > 0)
541 //Compute current volume level from system level and the number of segments in our display volume bar.
542 //That tells us how many segments in our volume bar needs to be turned on.
543 float currentVolume = volumeLevelScalar*volumeIconCount;
544 int segmentOnCount = Convert.ToInt32(currentVolume);
545 //Check if our segment count was rounded up, this will later be used for half brightness segment
546 bool roundedUp = segmentOnCount > currentVolume;
548 for (int i = 0; i < volumeIconCount; i++)
550 if (i < segmentOnCount)
552 //If we are dealing with our last segment and our segment count was rounded up then we will use half brightness.
553 if (i == segmentOnCount - 1 && roundedUp)
556 iDisplay.SetIconStatus(MiniDisplay.IconType.Volume, i,
557 (iDisplay.IconStatusCount(MiniDisplay.IconType.Volume) - 1)/2);
562 iDisplay.SetIconStatus(MiniDisplay.IconType.Volume, i,
563 iDisplay.IconStatusCount(MiniDisplay.IconType.Volume) - 1);
568 iDisplay.SetIconStatus(MiniDisplay.IconType.Volume, i, 0);
573 //Take care of our mute icon
574 iDisplay.SetIconOnOff(MiniDisplay.IconType.Mute, iMultiMediaDevice.AudioEndpointVolume.Mute);
582 private void UpdateAudioDeviceAndMasterVolumeThreadSafe()
584 if (this.InvokeRequired)
586 //Not in the proper thread, invoke ourselves
587 PlainUpdateDelegate d = new PlainUpdateDelegate(UpdateAudioDeviceAndMasterVolumeThreadSafe);
588 this.Invoke(d, new object[] {});
592 //We are in the correct thread just go ahead.
595 //Get our master volume
596 iMultiMediaDevice = iMultiMediaDeviceEnumerator.GetDefaultAudioEndpoint(DataFlow.Render, Role.Multimedia);
598 labelDefaultAudioDevice.Text = iMultiMediaDevice.FriendlyName;
600 //Show our volume in our track bar
601 UpdateMasterVolumeThreadSafe();
603 //Register to get volume modifications
604 iMultiMediaDevice.AudioEndpointVolume.OnVolumeNotification += OnVolumeNotificationThreadSafe;
606 trackBarMasterVolume.Enabled = true;
610 Debug.WriteLine("Exception thrown in UpdateAudioDeviceAndMasterVolume");
611 Debug.WriteLine(ex.ToString());
612 //Something went wrong S/PDIF device ca throw exception I guess
613 trackBarMasterVolume.Enabled = false;
620 private void PopulateDeviceTypes()
622 int count = Display.TypeCount();
624 for (int i = 0; i < count; i++)
626 comboBoxDisplayType.Items.Add(Display.TypeName((MiniDisplay.Type) i));
633 private void PopulateOpticalDrives()
635 //Reset our list of drives
636 comboBoxOpticalDrives.Items.Clear();
637 comboBoxOpticalDrives.Items.Add("None");
639 //Go through each drives on our system and collected the optical ones in our list
640 DriveInfo[] allDrives = DriveInfo.GetDrives();
641 foreach (DriveInfo d in allDrives)
643 Debug.WriteLine("Drive " + d.Name);
644 Debug.WriteLine(" Drive type: {0}", d.DriveType);
646 if (d.DriveType == DriveType.CDRom)
648 //This is an optical drive, add it now
649 comboBoxOpticalDrives.Items.Add(d.Name.Substring(0, 2));
657 /// <returns></returns>
658 public string OpticalDriveToEject()
660 return comboBoxOpticalDrives.SelectedItem.ToString();
668 private void SetupTrayIcon()
670 iNotifyIcon.Icon = GetIcon("vfd.ico");
671 iNotifyIcon.Text = "Sharp Display Manager";
672 iNotifyIcon.Visible = true;
674 //Double click toggles visibility - typically brings up the application
675 iNotifyIcon.DoubleClick += delegate(object obj, EventArgs args)
680 //Adding a context menu, useful to be able to exit the application
681 ContextMenu contextMenu = new ContextMenu();
682 //Context menu item to toggle visibility
683 MenuItem hideShowItem = new MenuItem("Hide/Show");
684 hideShowItem.Click += delegate(object obj, EventArgs args)
688 contextMenu.MenuItems.Add(hideShowItem);
690 //Context menu item separator
691 contextMenu.MenuItems.Add(new MenuItem("-"));
693 //Context menu exit item
694 MenuItem exitItem = new MenuItem("Exit");
695 exitItem.Click += delegate(object obj, EventArgs args)
699 contextMenu.MenuItems.Add(exitItem);
701 iNotifyIcon.ContextMenu = contextMenu;
707 private void SetupRecordingNotification()
709 iRecordingNotification.Icon = GetIcon("record.ico");
710 iRecordingNotification.Text = "No recording";
711 iRecordingNotification.Visible = false;
715 /// Access icons from embedded resources.
717 /// <param name="aName"></param>
718 /// <returns></returns>
719 public static Icon GetIcon(string aName)
721 string[] names = Assembly.GetExecutingAssembly().GetManifestResourceNames();
722 foreach (string name in names)
724 //Find a resource name that ends with the given name
725 if (name.EndsWith(aName))
727 using (Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(name))
729 return new Icon(stream);
739 /// Set our current client.
740 /// This will take care of applying our client layout and set data fields.
742 /// <param name="aSessionId"></param>
743 void SetCurrentClient(string aSessionId, bool aForce = false)
745 if (aSessionId == iCurrentClientSessionId)
747 //Given client is already the current one.
748 //Don't bother changing anything then.
752 ClientData requestedClientData = iClients[aSessionId];
754 //Check when was the last time we switched to that client
755 if (iCurrentClientData != null)
757 //Do not switch client if priority of current client is higher
758 if (!aForce && requestedClientData.Priority < iCurrentClientData.Priority)
764 double lastSwitchToClientSecondsAgo = (DateTime.Now - iCurrentClientData.LastSwitchTime).TotalSeconds;
765 //TODO: put that hard coded value as a client property
766 //Clients should be able to define how often they can be interrupted
767 //Thus a background client can set this to zero allowing any other client to interrupt at any time
768 //We could also compute this delay by looking at the requests frequencies?
770 requestedClientData.Priority == iCurrentClientData.Priority &&
771 //Time sharing is only if clients have the same priority
772 (lastSwitchToClientSecondsAgo < 30)) //Make sure a client is on for at least 30 seconds
774 //Don't switch clients too often
779 //Set current client ID.
780 iCurrentClientSessionId = aSessionId;
781 //Set the time we last switched to that client
782 iClients[aSessionId].LastSwitchTime = DateTime.Now;
783 //Fetch and set current client data.
784 iCurrentClientData = requestedClientData;
785 //Apply layout and set data fields.
786 UpdateTableLayoutPanel(iCurrentClientData);
789 private void buttonFont_Click(object sender, EventArgs e)
791 //fontDialog.ShowColor = true;
792 //fontDialog.ShowApply = true;
793 fontDialog.ShowEffects = true;
794 fontDialog.Font = cds.Font;
796 fontDialog.FixedPitchOnly = checkBoxFixedPitchFontOnly.Checked;
798 //fontDialog.ShowHelp = true;
800 //fontDlg.MaxSize = 40;
801 //fontDlg.MinSize = 22;
803 //fontDialog.Parent = this;
804 //fontDialog.StartPosition = FormStartPosition.CenterParent;
806 //DlgBox.ShowDialog(fontDialog);
808 //if (fontDialog.ShowDialog(this) != DialogResult.Cancel)
809 if (DlgBox.ShowDialog(fontDialog) != DialogResult.Cancel)
811 //Set the fonts to all our labels in our layout
812 foreach (Control ctrl in iTableLayoutPanel.Controls)
814 if (ctrl is MarqueeLabel)
816 ((MarqueeLabel) ctrl).Font = fontDialog.Font;
821 cds.Font = fontDialog.Font;
822 Properties.Settings.Default.Save();
831 void CheckFontHeight()
833 //Show font height and width
834 labelFontHeight.Text = "Font height: " + cds.Font.Height;
835 float charWidth = IsFixedWidth(cds.Font);
836 if (charWidth == 0.0f)
838 labelFontWidth.Visible = false;
842 labelFontWidth.Visible = true;
843 labelFontWidth.Text = "Font width: " + charWidth;
846 MarqueeLabel label = null;
847 //Get the first label control we can find
848 foreach (Control ctrl in iTableLayoutPanel.Controls)
850 if (ctrl is MarqueeLabel)
852 label = (MarqueeLabel) ctrl;
857 //Now check font height and show a warning if needed.
858 if (label != null && label.Font.Height > label.Height)
860 labelWarning.Text = "WARNING: Selected font is too height by " + (label.Font.Height - label.Height) +
862 labelWarning.Visible = true;
866 labelWarning.Visible = false;
871 private void buttonCapture_Click(object sender, EventArgs e)
873 System.Drawing.Bitmap bmp = new System.Drawing.Bitmap(iTableLayoutPanel.Width, iTableLayoutPanel.Height);
874 iTableLayoutPanel.DrawToBitmap(bmp, iTableLayoutPanel.ClientRectangle);
875 //Bitmap bmpToSave = new Bitmap(bmp);
876 bmp.Save("D:\\capture.png");
878 ((MarqueeLabel) iTableLayoutPanel.Controls[0]).Text = "Captured";
881 string outputFileName = "d:\\capture.png";
882 using (MemoryStream memory = new MemoryStream())
884 using (FileStream fs = new FileStream(outputFileName, FileMode.OpenOrCreate, FileAccess.ReadWrite))
886 bmp.Save(memory, System.Drawing.Imaging.ImageFormat.Png);
887 byte[] bytes = memory.ToArray();
888 fs.Write(bytes, 0, bytes.Length);
895 private void CheckForRequestResults()
897 if (iDisplay.IsRequestPending())
899 switch (iDisplay.AttemptRequestCompletion())
901 case MiniDisplay.Request.FirmwareRevision:
902 toolStripStatusLabelConnect.Text += " v" + iDisplay.FirmwareRevision();
903 //Issue next request then
904 iDisplay.RequestPowerSupplyStatus();
907 case MiniDisplay.Request.PowerSupplyStatus:
908 if (iDisplay.PowerSupplyStatus())
910 toolStripStatusLabelPower.Text = "ON";
914 toolStripStatusLabelPower.Text = "OFF";
916 //Issue next request then
917 iDisplay.RequestDeviceId();
920 case MiniDisplay.Request.DeviceId:
921 toolStripStatusLabelConnect.Text += " - " + iDisplay.DeviceId();
922 //No more request to issue
928 public static uint ColorWhiteIsOn(int aX, int aY, uint aPixel)
930 if ((aPixel & 0x00FFFFFF) == 0x00FFFFFF)
937 public static uint ColorUntouched(int aX, int aY, uint aPixel)
942 public static uint ColorInversed(int aX, int aY, uint aPixel)
947 public static uint ColorChessboard(int aX, int aY, uint aPixel)
949 if ((aX%2 == 0) && (aY%2 == 0))
953 else if ((aX%2 != 0) && (aY%2 != 0))
961 public static int ScreenReversedX(System.Drawing.Bitmap aBmp, int aX)
963 return aBmp.Width - aX - 1;
966 public int ScreenReversedY(System.Drawing.Bitmap aBmp, int aY)
968 return iBmp.Height - aY - 1;
971 public int ScreenX(System.Drawing.Bitmap aBmp, int aX)
976 public int ScreenY(System.Drawing.Bitmap aBmp, int aY)
982 /// Select proper pixel delegates according to our current settings.
984 private void SetupPixelDelegates()
986 //Select our pixel processing routine
987 if (cds.InverseColors)
989 //iColorFx = ColorChessboard;
990 iColorFx = ColorInversed;
994 iColorFx = ColorWhiteIsOn;
997 //Select proper coordinate translation functions
998 //We used delegate/function pointer to support reverse screen without doing an extra test on each pixels
999 if (cds.ReverseScreen)
1001 iScreenX = ScreenReversedX;
1002 iScreenY = ScreenReversedY;
1012 //This is our timer tick responsible to perform our render
1013 private void timer_Tick(object sender, EventArgs e)
1015 //Not ideal cause this has nothing to do with display render
1018 //Update our animations
1019 DateTime NewTickTime = DateTime.Now;
1021 UpdateNetworkSignal(LastTickTime, NewTickTime);
1023 //Update animation for all our marquees
1024 foreach (Control ctrl in iTableLayoutPanel.Controls)
1026 if (ctrl is MarqueeLabel)
1028 ((MarqueeLabel) ctrl).UpdateAnimation(LastTickTime, NewTickTime);
1032 //Update our display
1033 if (iDisplay.IsOpen())
1035 CheckForRequestResults();
1037 //Check if frame rendering is needed
1038 //Typically used when showing clock
1039 if (!iSkipFrameRendering)
1044 iBmp = new System.Drawing.Bitmap(iTableLayoutPanel.Width, iTableLayoutPanel.Height,
1045 PixelFormat.Format32bppArgb);
1046 iCreateBitmap = false;
1048 iTableLayoutPanel.DrawToBitmap(iBmp, iTableLayoutPanel.ClientRectangle);
1049 //iBmp.Save("D:\\capture.png");
1051 //Send it to our display
1052 for (int i = 0; i < iBmp.Width; i++)
1054 for (int j = 0; j < iBmp.Height; j++)
1058 //Get our processed pixel coordinates
1059 int x = iScreenX(iBmp, i);
1060 int y = iScreenY(iBmp, j);
1062 uint color = (uint) iBmp.GetPixel(i, j).ToArgb();
1063 //Apply color effects
1064 color = iColorFx(x, y, color);
1066 iDisplay.SetPixel(x, y, color);
1071 iDisplay.SwapBuffers();
1075 //Compute instant FPS
1076 toolStripStatusLabelFps.Text = (1.0/NewTickTime.Subtract(LastTickTime).TotalSeconds).ToString("F0") + " / " +
1077 (1000/timer.Interval).ToString() + " FPS";
1079 LastTickTime = NewTickTime;
1084 /// Attempt to establish connection with our display hardware.
1086 private void OpenDisplayConnection()
1088 CloseDisplayConnection();
1090 if (!iDisplay.Open((MiniDisplay.Type) cds.DisplayType))
1093 toolStripStatusLabelConnect.Text = "Connection error";
1097 private void CloseDisplayConnection()
1099 //Status will be updated upon receiving the closed event
1101 if (iDisplay == null || !iDisplay.IsOpen())
1106 //Do not clear if we gave up on rendering already.
1107 //This means we will keep on displaying clock on MDM166AA for instance.
1108 if (!iSkipFrameRendering)
1111 iDisplay.SwapBuffers();
1114 iDisplay.SetAllIconsStatus(0); //Turn off all icons
1118 private void buttonOpen_Click(object sender, EventArgs e)
1120 OpenDisplayConnection();
1123 private void buttonClose_Click(object sender, EventArgs e)
1125 CloseDisplayConnection();
1128 private void buttonClear_Click(object sender, EventArgs e)
1131 iDisplay.SwapBuffers();
1134 private void buttonFill_Click(object sender, EventArgs e)
1137 iDisplay.SwapBuffers();
1140 private void trackBarBrightness_Scroll(object sender, EventArgs e)
1142 cds.Brightness = trackBarBrightness.Value;
1143 Properties.Settings.Default.Save();
1144 iDisplay.SetBrightness(trackBarBrightness.Value);
1150 /// CDS stands for Current Display Settings
1152 private DisplaySettings cds
1156 DisplaysSettings settings = Properties.Settings.Default.DisplaysSettings;
1157 if (settings == null)
1159 settings = new DisplaysSettings();
1161 Properties.Settings.Default.DisplaysSettings = settings;
1164 //Make sure all our settings have been created
1165 while (settings.Displays.Count <= Properties.Settings.Default.CurrentDisplayIndex)
1167 settings.Displays.Add(new DisplaySettings());
1170 DisplaySettings displaySettings = settings.Displays[Properties.Settings.Default.CurrentDisplayIndex];
1171 return displaySettings;
1176 /// Check if the given font has a fixed character pitch.
1178 /// <param name="ft"></param>
1179 /// <returns>0.0f if this is not a monospace font, otherwise returns the character width.</returns>
1180 public float IsFixedWidth(Font ft)
1182 Graphics g = CreateGraphics();
1183 char[] charSizes = new char[] {'i', 'a', 'Z', '%', '#', 'a', 'B', 'l', 'm', ',', '.'};
1184 float charWidth = g.MeasureString("I", ft, Int32.MaxValue, StringFormat.GenericTypographic).Width;
1186 bool fixedWidth = true;
1188 foreach (char c in charSizes)
1189 if (g.MeasureString(c.ToString(), ft, Int32.MaxValue, StringFormat.GenericTypographic).Width !=
1202 /// Synchronize UI with settings
1204 private void UpdateStatus()
1207 checkBoxShowBorders.Checked = cds.ShowBorders;
1208 iTableLayoutPanel.CellBorderStyle = (cds.ShowBorders
1209 ? TableLayoutPanelCellBorderStyle.Single
1210 : TableLayoutPanelCellBorderStyle.None);
1212 //Set the proper font to each of our labels
1213 foreach (MarqueeLabel ctrl in iTableLayoutPanel.Controls)
1215 ctrl.Font = cds.Font;
1219 //Check if "run on Windows startup" is enabled
1220 checkBoxAutoStart.Checked = iStartupManager.Startup;
1222 checkBoxConnectOnStartup.Checked = Properties.Settings.Default.DisplayConnectOnStartup;
1223 checkBoxMinimizeToTray.Checked = Properties.Settings.Default.MinimizeToTray;
1224 checkBoxStartMinimized.Checked = Properties.Settings.Default.StartMinimized;
1225 iCheckBoxStartIdleClient.Checked = Properties.Settings.Default.StartIdleClient;
1226 labelStartFileName.Text = Properties.Settings.Default.StartFileName;
1229 //Try find our drive in our drive list
1230 int opticalDriveItemIndex = 0;
1231 bool driveNotFound = true;
1232 string opticalDriveToEject = Properties.Settings.Default.OpticalDriveToEject;
1233 foreach (object item in comboBoxOpticalDrives.Items)
1235 if (opticalDriveToEject == item.ToString())
1237 comboBoxOpticalDrives.SelectedIndex = opticalDriveItemIndex;
1238 driveNotFound = false;
1241 opticalDriveItemIndex++;
1246 //We could not find the drive we had saved.
1247 //Select "None" then.
1248 comboBoxOpticalDrives.SelectedIndex = 0;
1252 checkBoxCecEnabled.Checked = Properties.Settings.Default.CecEnabled;
1253 comboBoxHdmiPort.SelectedIndex = Properties.Settings.Default.CecHdmiPort - 1;
1255 //Mini Display settings
1256 checkBoxReverseScreen.Checked = cds.ReverseScreen;
1257 checkBoxInverseColors.Checked = cds.InverseColors;
1258 checkBoxShowVolumeLabel.Checked = cds.ShowVolumeLabel;
1259 checkBoxScaleToFit.Checked = cds.ScaleToFit;
1260 maskedTextBoxMinFontSize.Enabled = cds.ScaleToFit;
1261 labelMinFontSize.Enabled = cds.ScaleToFit;
1262 maskedTextBoxMinFontSize.Text = cds.MinFontSize.ToString();
1263 maskedTextBoxScrollingSpeed.Text = cds.ScrollingSpeedInPixelsPerSecond.ToString();
1264 comboBoxDisplayType.SelectedIndex = cds.DisplayType;
1265 timer.Interval = cds.TimerInterval;
1266 maskedTextBoxTimerInterval.Text = cds.TimerInterval.ToString();
1267 textBoxScrollLoopSeparator.Text = cds.Separator;
1269 SetupPixelDelegates();
1271 if (iDisplay.IsOpen())
1273 //We have a display connection
1274 //Reflect that in our UI
1277 iTableLayoutPanel.Enabled = true;
1278 panelDisplay.Enabled = true;
1280 //Only setup brightness if display is open
1281 trackBarBrightness.Minimum = iDisplay.MinBrightness();
1282 trackBarBrightness.Maximum = iDisplay.MaxBrightness();
1283 if (cds.Brightness < iDisplay.MinBrightness() || cds.Brightness > iDisplay.MaxBrightness())
1285 //Brightness out of range, this can occur when using auto-detect
1286 //Use max brightness instead
1287 trackBarBrightness.Value = iDisplay.MaxBrightness();
1288 iDisplay.SetBrightness(iDisplay.MaxBrightness());
1292 trackBarBrightness.Value = cds.Brightness;
1293 iDisplay.SetBrightness(cds.Brightness);
1296 //Try compute the steps to something that makes sense
1297 trackBarBrightness.LargeChange = Math.Max(1, (iDisplay.MaxBrightness() - iDisplay.MinBrightness())/5);
1298 trackBarBrightness.SmallChange = 1;
1301 buttonFill.Enabled = true;
1302 buttonClear.Enabled = true;
1303 buttonOpen.Enabled = false;
1304 buttonClose.Enabled = true;
1305 trackBarBrightness.Enabled = true;
1306 toolStripStatusLabelConnect.Text = "Connected - " + iDisplay.Vendor() + " - " + iDisplay.Product();
1307 //+ " - " + iDisplay.SerialNumber();
1309 if (iDisplay.SupportPowerOnOff())
1311 buttonPowerOn.Enabled = true;
1312 buttonPowerOff.Enabled = true;
1316 buttonPowerOn.Enabled = false;
1317 buttonPowerOff.Enabled = false;
1320 if (iDisplay.SupportClock())
1322 buttonShowClock.Enabled = true;
1323 buttonHideClock.Enabled = true;
1327 buttonShowClock.Enabled = false;
1328 buttonHideClock.Enabled = false;
1332 //Check if Volume Label is supported. To date only MDM166AA supports that crap :)
1333 checkBoxShowVolumeLabel.Enabled = iDisplay.IconCount(MiniDisplay.IconType.VolumeLabel) > 0;
1335 if (cds.ShowVolumeLabel)
1337 iDisplay.SetIconOn(MiniDisplay.IconType.VolumeLabel);
1341 iDisplay.SetIconOff(MiniDisplay.IconType.VolumeLabel);
1346 //Display connection not available
1347 //Reflect that in our UI
1349 //In debug start our timer even if we don't have a display connection
1352 //In production environment we don't need our timer if no display connection
1355 checkBoxShowVolumeLabel.Enabled = false;
1356 iTableLayoutPanel.Enabled = false;
1357 panelDisplay.Enabled = false;
1358 buttonFill.Enabled = false;
1359 buttonClear.Enabled = false;
1360 buttonOpen.Enabled = true;
1361 buttonClose.Enabled = false;
1362 trackBarBrightness.Enabled = false;
1363 buttonPowerOn.Enabled = false;
1364 buttonPowerOff.Enabled = false;
1365 buttonShowClock.Enabled = false;
1366 buttonHideClock.Enabled = false;
1367 toolStripStatusLabelConnect.Text = "Disconnected";
1368 toolStripStatusLabelPower.Text = "N/A";
1377 /// <param name="sender"></param>
1378 /// <param name="e"></param>
1379 private void checkBoxShowVolumeLabel_CheckedChanged(object sender, EventArgs e)
1381 cds.ShowVolumeLabel = checkBoxShowVolumeLabel.Checked;
1382 Properties.Settings.Default.Save();
1386 private void checkBoxShowBorders_CheckedChanged(object sender, EventArgs e)
1388 //Save our show borders setting
1389 iTableLayoutPanel.CellBorderStyle = (checkBoxShowBorders.Checked
1390 ? TableLayoutPanelCellBorderStyle.Single
1391 : TableLayoutPanelCellBorderStyle.None);
1392 cds.ShowBorders = checkBoxShowBorders.Checked;
1393 Properties.Settings.Default.Save();
1397 private void checkBoxConnectOnStartup_CheckedChanged(object sender, EventArgs e)
1399 //Save our connect on startup setting
1400 Properties.Settings.Default.DisplayConnectOnStartup = checkBoxConnectOnStartup.Checked;
1401 Properties.Settings.Default.Save();
1404 private void checkBoxMinimizeToTray_CheckedChanged(object sender, EventArgs e)
1406 //Save our "Minimize to tray" setting
1407 Properties.Settings.Default.MinimizeToTray = checkBoxMinimizeToTray.Checked;
1408 Properties.Settings.Default.Save();
1412 private void checkBoxStartMinimized_CheckedChanged(object sender, EventArgs e)
1414 //Save our "Start minimized" setting
1415 Properties.Settings.Default.StartMinimized = checkBoxStartMinimized.Checked;
1416 Properties.Settings.Default.Save();
1419 private void checkBoxStartIdleClient_CheckedChanged(object sender, EventArgs e)
1421 Properties.Settings.Default.StartIdleClient = iCheckBoxStartIdleClient.Checked;
1422 Properties.Settings.Default.Save();
1425 private void checkBoxAutoStart_CheckedChanged(object sender, EventArgs e)
1427 iStartupManager.Startup = checkBoxAutoStart.Checked;
1431 private void checkBoxReverseScreen_CheckedChanged(object sender, EventArgs e)
1433 //Save our reverse screen setting
1434 cds.ReverseScreen = checkBoxReverseScreen.Checked;
1435 Properties.Settings.Default.Save();
1436 SetupPixelDelegates();
1439 private void checkBoxInverseColors_CheckedChanged(object sender, EventArgs e)
1441 //Save our inverse colors setting
1442 cds.InverseColors = checkBoxInverseColors.Checked;
1443 Properties.Settings.Default.Save();
1444 SetupPixelDelegates();
1447 private void checkBoxScaleToFit_CheckedChanged(object sender, EventArgs e)
1449 //Save our scale to fit setting
1450 cds.ScaleToFit = checkBoxScaleToFit.Checked;
1451 Properties.Settings.Default.Save();
1453 labelMinFontSize.Enabled = cds.ScaleToFit;
1454 maskedTextBoxMinFontSize.Enabled = cds.ScaleToFit;
1457 private void MainForm_Resize(object sender, EventArgs e)
1459 if (WindowState == FormWindowState.Minimized)
1461 // To workaround our empty bitmap bug on Windows 7 we need to recreate our bitmap when the application is minimized
1462 // That's apparently not needed on Windows 10 but we better leave it in place.
1463 iCreateBitmap = true;
1467 private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
1470 iNetworkManager.Dispose();
1471 CloseDisplayConnection();
1473 e.Cancel = iClosing;
1476 public void StartServer()
1478 iServiceHost = new ServiceHost
1481 new Uri[] {new Uri("net.tcp://localhost:8001/")}
1484 iServiceHost.AddServiceEndpoint(typeof(IService), new NetTcpBinding(SecurityMode.None, true),
1486 iServiceHost.Open();
1489 public void StopServer()
1491 if (iClients.Count > 0 && !iClosing)
1495 BroadcastCloseEvent();
1500 MessageBox.Show("Force exit?", "Waiting for clients...", MessageBoxButtons.YesNo,
1501 MessageBoxIcon.Warning) == DialogResult.Yes)
1503 iClosing = false; //We make sure we force close if asked twice
1508 //We removed that as it often lags for some reason
1509 //iServiceHost.Close();
1513 public void BroadcastCloseEvent()
1515 Trace.TraceInformation("BroadcastCloseEvent - start");
1517 var inactiveClients = new List<string>();
1518 foreach (var client in iClients)
1520 //if (client.Key != eventData.ClientName)
1524 Trace.TraceInformation("BroadcastCloseEvent - " + client.Key);
1525 client.Value.Callback.OnCloseOrder( /*eventData*/);
1527 catch (Exception ex)
1529 inactiveClients.Add(client.Key);
1534 if (inactiveClients.Count > 0)
1536 foreach (var client in inactiveClients)
1538 iClients.Remove(client);
1539 Program.iMainForm.iTreeViewClients.Nodes.Remove(
1540 Program.iMainForm.iTreeViewClients.Nodes.Find(client, false)[0]);
1544 if (iClients.Count == 0)
1551 /// Just remove all our fields.
1553 private void ClearLayout()
1555 iTableLayoutPanel.Controls.Clear();
1556 iTableLayoutPanel.RowStyles.Clear();
1557 iTableLayoutPanel.ColumnStyles.Clear();
1558 iCurrentClientData = null;
1562 /// Just launch a demo client.
1564 private void StartNewClient(string aTopText = "", string aBottomText = "")
1566 Thread clientThread = new Thread(SharpDisplayClient.Program.MainWithParams);
1567 SharpDisplayClient.StartParams myParams = new SharpDisplayClient.StartParams(
1568 new Point(this.Right, this.Top), aTopText, aBottomText);
1569 clientThread.Start(myParams);
1574 /// Just launch our idle client.
1576 private void StartIdleClient(string aTopText = "", string aBottomText = "")
1578 Thread clientThread = new Thread(SharpDisplayIdleClient.Program.MainWithParams);
1579 SharpDisplayIdleClient.StartParams myParams =
1580 new SharpDisplayIdleClient.StartParams(new Point(this.Right, this.Top), aTopText, aBottomText);
1581 clientThread.Start(myParams);
1586 private void buttonStartClient_Click(object sender, EventArgs e)
1591 private void buttonSuspend_Click(object sender, EventArgs e)
1596 private void StartTimer()
1598 LastTickTime = DateTime.Now; //Reset timer to prevent jump
1599 timer.Enabled = true;
1600 UpdateSuspendButton();
1603 private void StopTimer()
1605 LastTickTime = DateTime.Now; //Reset timer to prevent jump
1606 timer.Enabled = false;
1607 UpdateSuspendButton();
1610 private void ToggleTimer()
1612 LastTickTime = DateTime.Now; //Reset timer to prevent jump
1613 timer.Enabled = !timer.Enabled;
1614 UpdateSuspendButton();
1617 private void UpdateSuspendButton()
1621 buttonSuspend.Text = "Run";
1625 buttonSuspend.Text = "Pause";
1630 private void buttonCloseClients_Click(object sender, EventArgs e)
1632 BroadcastCloseEvent();
1635 private void treeViewClients_AfterSelect(object sender, TreeViewEventArgs e)
1637 //Root node must have at least one child
1638 if (e.Node.Nodes.Count == 0)
1643 //If the selected node is the root node of a client then switch to it
1644 string sessionId = e.Node.Nodes[0].Text; //First child of a root node is the sessionId
1645 if (iClients.ContainsKey(sessionId)) //Check that's actually what we are looking at
1647 //We have a valid session just switch to that client
1648 SetCurrentClient(sessionId, true);
1657 /// <param name="aSessionId"></param>
1658 /// <param name="aCallback"></param>
1659 public void AddClientThreadSafe(string aSessionId, ICallback aCallback)
1661 if (this.InvokeRequired)
1663 //Not in the proper thread, invoke ourselves
1664 AddClientDelegate d = new AddClientDelegate(AddClientThreadSafe);
1665 this.Invoke(d, new object[] {aSessionId, aCallback});
1669 //We are in the proper thread
1670 //Add this session to our collection of clients
1671 ClientData newClient = new ClientData(aSessionId, aCallback);
1672 Program.iMainForm.iClients.Add(aSessionId, newClient);
1673 //Add this session to our UI
1674 UpdateClientTreeViewNode(newClient);
1680 /// Find the client with the highest priority if any.
1682 /// <returns>Our highest priority client or null if not a single client is connected.</returns>
1683 public ClientData FindHighestPriorityClient()
1685 ClientData highestPriorityClient = null;
1686 foreach (var client in iClients)
1688 if (highestPriorityClient == null || client.Value.Priority > highestPriorityClient.Priority)
1690 highestPriorityClient = client.Value;
1694 return highestPriorityClient;
1700 /// <param name="aSessionId"></param>
1701 public void RemoveClientThreadSafe(string aSessionId)
1703 if (this.InvokeRequired)
1705 //Not in the proper thread, invoke ourselves
1706 RemoveClientDelegate d = new RemoveClientDelegate(RemoveClientThreadSafe);
1707 this.Invoke(d, new object[] {aSessionId});
1711 //We are in the proper thread
1712 //Remove this session from both client collection and UI tree view
1713 if (Program.iMainForm.iClients.Keys.Contains(aSessionId))
1715 Program.iMainForm.iClients.Remove(aSessionId);
1716 Program.iMainForm.iTreeViewClients.Nodes.Remove(
1717 Program.iMainForm.iTreeViewClients.Nodes.Find(aSessionId, false)[0]);
1718 //Update recording status too whenever a client is removed
1719 UpdateRecordingNotification();
1722 if (iCurrentClientSessionId == aSessionId)
1724 //The current client is closing
1725 iCurrentClientData = null;
1726 //Find the client with the highest priority and set it as current
1727 ClientData newCurrentClient = FindHighestPriorityClient();
1728 if (newCurrentClient != null)
1730 SetCurrentClient(newCurrentClient.SessionId, true);
1734 if (iClients.Count == 0)
1736 //Clear our screen when last client disconnects
1741 //We were closing our form
1742 //All clients are now closed
1743 //Just resume our close operation
1754 /// <param name="aSessionId"></param>
1755 /// <param name="aLayout"></param>
1756 public void SetClientLayoutThreadSafe(string aSessionId, TableLayout aLayout)
1758 if (this.InvokeRequired)
1760 //Not in the proper thread, invoke ourselves
1761 SetLayoutDelegate d = new SetLayoutDelegate(SetClientLayoutThreadSafe);
1762 this.Invoke(d, new object[] {aSessionId, aLayout});
1766 ClientData client = iClients[aSessionId];
1769 //Don't change a thing if the layout is the same
1770 if (!client.Layout.IsSameAs(aLayout))
1772 Debug.Print("SetClientLayoutThreadSafe: Layout updated.");
1773 //Set our client layout then
1774 client.Layout = aLayout;
1775 //So that next time we update all our fields at ones
1776 client.HasNewLayout = true;
1777 //Layout has changed clear our fields then
1778 client.Fields.Clear();
1780 UpdateClientTreeViewNode(client);
1784 Debug.Print("SetClientLayoutThreadSafe: Layout has not changed.");
1793 /// <param name="aSessionId"></param>
1794 /// <param name="aField"></param>
1795 public void SetClientFieldThreadSafe(string aSessionId, DataField aField)
1797 if (this.InvokeRequired)
1799 //Not in the proper thread, invoke ourselves
1800 SetFieldDelegate d = new SetFieldDelegate(SetClientFieldThreadSafe);
1801 this.Invoke(d, new object[] {aSessionId, aField});
1805 //We are in the proper thread
1806 //Call the non-thread-safe variant
1807 SetClientField(aSessionId, aField);
1815 /// Set a data field in the given client.
1817 /// <param name="aSessionId"></param>
1818 /// <param name="aField"></param>
1819 private void SetClientField(string aSessionId, DataField aField)
1821 //TODO: should check if the field actually changed?
1823 ClientData client = iClients[aSessionId];
1824 bool layoutChanged = false;
1825 bool contentChanged = true;
1827 //Fetch our field index
1828 int fieldIndex = client.FindSameFieldIndex(aField);
1832 //No corresponding field, just bail out
1836 //Keep our previous field in there
1837 DataField previousField = client.Fields[fieldIndex];
1838 //Just update that field then
1839 client.Fields[fieldIndex] = aField;
1841 if (!aField.IsTableField)
1843 //We are done then if that field is not in our table layout
1847 TableField tableField = (TableField) aField;
1849 if (previousField.IsSameLayout(aField))
1851 //If we are updating a field in our current client we need to update it in our panel
1852 if (aSessionId == iCurrentClientSessionId)
1854 Control ctrl = iTableLayoutPanel.GetControlFromPosition(tableField.Column, tableField.Row);
1855 if (aField.IsTextField && ctrl is MarqueeLabel)
1857 TextField textField = (TextField) aField;
1858 //Text field control already in place, just change the text
1859 MarqueeLabel label = (MarqueeLabel) ctrl;
1860 contentChanged = (label.Text != textField.Text || label.TextAlign != textField.Alignment);
1861 label.Text = textField.Text;
1862 label.TextAlign = textField.Alignment;
1864 else if (aField.IsBitmapField && ctrl is PictureBox)
1866 BitmapField bitmapField = (BitmapField) aField;
1867 contentChanged = true; //TODO: Bitmap comp or should we leave that to clients?
1868 //Bitmap field control already in place just change the bitmap
1869 PictureBox pictureBox = (PictureBox) ctrl;
1870 pictureBox.Image = bitmapField.Bitmap;
1874 layoutChanged = true;
1880 layoutChanged = true;
1883 //If either content or layout changed we need to update our tree view to reflect the changes
1884 if (contentChanged || layoutChanged)
1886 UpdateClientTreeViewNode(client);
1890 Debug.Print("Layout changed");
1891 //Our layout has changed, if we are already the current client we need to update our panel
1892 if (aSessionId == iCurrentClientSessionId)
1894 //Apply layout and set data fields.
1895 UpdateTableLayoutPanel(iCurrentClientData);
1900 Debug.Print("Layout has not changed.");
1905 Debug.Print("WARNING: content and layout have not changed!");
1908 //When a client field is set we try switching to this client to present the new information to our user
1909 SetCurrentClient(aSessionId);
1915 /// <param name="aTexts"></param>
1916 public void SetClientFieldsThreadSafe(string aSessionId, System.Collections.Generic.IList<DataField> aFields)
1918 if (this.InvokeRequired)
1920 //Not in the proper thread, invoke ourselves
1921 SetFieldsDelegate d = new SetFieldsDelegate(SetClientFieldsThreadSafe);
1922 this.Invoke(d, new object[] {aSessionId, aFields});
1926 ClientData client = iClients[aSessionId];
1928 if (client.HasNewLayout)
1930 //TODO: Assert client.Count == 0
1931 //Our layout was just changed
1932 //Do some special handling to avoid re-creating our panel N times, once for each fields
1933 client.HasNewLayout = false;
1934 //Just set all our fields then
1935 client.Fields.AddRange(aFields);
1936 //Try switch to that client
1937 SetCurrentClient(aSessionId);
1939 //If we are updating the current client update our panel
1940 if (aSessionId == iCurrentClientSessionId)
1942 //Apply layout and set data fields.
1943 UpdateTableLayoutPanel(iCurrentClientData);
1946 UpdateClientTreeViewNode(client);
1950 //Put each our text fields in a label control
1951 foreach (DataField field in aFields)
1953 SetClientField(aSessionId, field);
1962 /// <param name="aSessionId"></param>
1963 /// <param name="aName"></param>
1964 public void SetClientNameThreadSafe(string aSessionId, string aName)
1966 if (this.InvokeRequired)
1968 //Not in the proper thread, invoke ourselves
1969 SetClientNameDelegate d = new SetClientNameDelegate(SetClientNameThreadSafe);
1970 this.Invoke(d, new object[] {aSessionId, aName});
1974 //We are in the proper thread
1976 ClientData client = iClients[aSessionId];
1980 client.Name = aName;
1981 //Update our tree-view
1982 UpdateClientTreeViewNode(client);
1988 public void SetClientPriorityThreadSafe(string aSessionId, uint aPriority)
1990 if (this.InvokeRequired)
1992 //Not in the proper thread, invoke ourselves
1993 SetClientPriorityDelegate d = new SetClientPriorityDelegate(SetClientPriorityThreadSafe);
1994 this.Invoke(d, new object[] {aSessionId, aPriority});
1998 //We are in the proper thread
2000 ClientData client = iClients[aSessionId];
2004 client.Priority = aPriority;
2005 //Update our tree-view
2006 UpdateClientTreeViewNode(client);
2007 //Change our current client as per new priority
2008 ClientData newCurrentClient = FindHighestPriorityClient();
2009 if (newCurrentClient != null)
2011 SetCurrentClient(newCurrentClient.SessionId);
2020 /// <param name="value"></param>
2021 /// <param name="maxChars"></param>
2022 /// <returns></returns>
2023 public static string Truncate(string value, int maxChars)
2025 return value.Length <= maxChars ? value : value.Substring(0, maxChars - 3) + "...";
2029 /// Update our recording notification.
2031 private void UpdateRecordingNotification()
2034 bool activeRecording = false;
2036 RecordingField recField = new RecordingField();
2037 foreach (var client in iClients)
2039 RecordingField rec = (RecordingField) client.Value.FindSameFieldAs(recField);
2040 if (rec != null && rec.IsActive)
2042 activeRecording = true;
2043 //Don't break cause we are collecting the names/texts.
2044 if (!String.IsNullOrEmpty(rec.Text))
2046 text += (rec.Text + "\n");
2050 //Not text for that recording, use client name instead
2051 text += client.Value.Name + " recording\n";
2057 //Update our text no matter what, can't have more than 63 characters otherwise it throws an exception.
2058 iRecordingNotification.Text = Truncate(text, 63);
2060 //Change visibility of notification if needed
2061 if (iRecordingNotification.Visible != activeRecording)
2063 iRecordingNotification.Visible = activeRecording;
2064 //Assuming the notification icon is in sync with our display icon
2065 //Take care of our REC icon
2066 if (iDisplay.IsOpen())
2068 iDisplay.SetIconOnOff(MiniDisplay.IconType.Recording, activeRecording);
2076 /// <param name="aClient"></param>
2077 private void UpdateClientTreeViewNode(ClientData aClient)
2079 Debug.Print("UpdateClientTreeViewNode");
2081 if (aClient == null)
2086 //Hook in record icon update too
2087 UpdateRecordingNotification();
2089 TreeNode node = null;
2090 //Check that our client node already exists
2091 //Get our client root node using its key which is our session ID
2092 TreeNode[] nodes = iTreeViewClients.Nodes.Find(aClient.SessionId, false);
2093 if (nodes.Count() > 0)
2095 //We already have a node for that client
2097 //Clear children as we are going to recreate them below
2102 //Client node does not exists create a new one
2103 iTreeViewClients.Nodes.Add(aClient.SessionId, aClient.SessionId);
2104 node = iTreeViewClients.Nodes.Find(aClient.SessionId, false)[0];
2110 if (!String.IsNullOrEmpty(aClient.Name))
2112 //We have a name, use it as text for our root node
2113 node.Text = aClient.Name;
2114 //Add a child with SessionId
2115 node.Nodes.Add(new TreeNode(aClient.SessionId));
2119 //No name, use session ID instead
2120 node.Text = aClient.SessionId;
2123 //Display client priority
2124 node.Nodes.Add(new TreeNode("Priority: " + aClient.Priority));
2126 if (aClient.Fields.Count > 0)
2128 //Create root node for our texts
2129 TreeNode textsRoot = new TreeNode("Fields");
2130 node.Nodes.Add(textsRoot);
2131 //For each text add a new entry
2132 foreach (DataField field in aClient.Fields)
2134 if (field.IsTextField)
2136 TextField textField = (TextField) field;
2137 textsRoot.Nodes.Add(new TreeNode("[Text]" + textField.Text));
2139 else if (field.IsBitmapField)
2141 textsRoot.Nodes.Add(new TreeNode("[Bitmap]"));
2143 else if (field.IsRecordingField)
2145 RecordingField recordingField = (RecordingField) field;
2146 textsRoot.Nodes.Add(new TreeNode("[Recording]" + recordingField.IsActive));
2156 /// Update our table layout row styles to make sure each rows have similar height
2158 private void UpdateTableLayoutRowStyles()
2160 foreach (RowStyle rowStyle in iTableLayoutPanel.RowStyles)
2162 rowStyle.SizeType = SizeType.Percent;
2163 rowStyle.Height = 100/iTableLayoutPanel.RowCount;
2168 /// Update our display table layout.
2169 /// Will instanciated every field control as defined by our client.
2170 /// Fields must be specified by rows from the left.
2172 /// <param name="aLayout"></param>
2173 private void UpdateTableLayoutPanel(ClientData aClient)
2175 Debug.Print("UpdateTableLayoutPanel");
2177 if (aClient == null)
2184 TableLayout layout = aClient.Layout;
2186 //First clean our current panel
2187 iTableLayoutPanel.Controls.Clear();
2188 iTableLayoutPanel.RowStyles.Clear();
2189 iTableLayoutPanel.ColumnStyles.Clear();
2190 iTableLayoutPanel.RowCount = 0;
2191 iTableLayoutPanel.ColumnCount = 0;
2193 //Then recreate our rows...
2194 while (iTableLayoutPanel.RowCount < layout.Rows.Count)
2196 iTableLayoutPanel.RowCount++;
2200 while (iTableLayoutPanel.ColumnCount < layout.Columns.Count)
2202 iTableLayoutPanel.ColumnCount++;
2206 for (int i = 0; i < iTableLayoutPanel.ColumnCount; i++)
2208 //Create our column styles
2209 this.iTableLayoutPanel.ColumnStyles.Add(layout.Columns[i]);
2212 for (int j = 0; j < iTableLayoutPanel.RowCount; j++)
2216 //Create our row styles
2217 this.iTableLayoutPanel.RowStyles.Add(layout.Rows[j]);
2227 foreach (DataField field in aClient.Fields)
2229 if (!field.IsTableField)
2231 //That field is not taking part in our table layout skip it
2235 TableField tableField = (TableField) field;
2237 //Create a control corresponding to the field specified for that cell
2238 Control control = CreateControlForDataField(tableField);
2240 //Add newly created control to our table layout at the specified row and column
2241 iTableLayoutPanel.Controls.Add(control, tableField.Column, tableField.Row);
2242 //Make sure we specify column and row span for that new control
2243 iTableLayoutPanel.SetColumnSpan(control, tableField.ColumnSpan);
2244 iTableLayoutPanel.SetRowSpan(control, tableField.RowSpan);
2252 /// Check our type of data field and create corresponding control
2254 /// <param name="aField"></param>
2255 private Control CreateControlForDataField(DataField aField)
2257 Control control = null;
2258 if (aField.IsTextField)
2260 MarqueeLabel label = new SharpDisplayManager.MarqueeLabel();
2261 label.AutoEllipsis = true;
2262 label.AutoSize = true;
2263 label.BackColor = System.Drawing.Color.Transparent;
2264 label.Dock = System.Windows.Forms.DockStyle.Fill;
2265 label.Location = new System.Drawing.Point(1, 1);
2266 label.Margin = new System.Windows.Forms.Padding(0);
2267 label.Name = "marqueeLabel" + aField;
2268 label.OwnTimer = false;
2269 label.PixelsPerSecond = cds.ScrollingSpeedInPixelsPerSecond;
2270 label.Separator = cds.Separator;
2271 label.MinFontSize = cds.MinFontSize;
2272 label.ScaleToFit = cds.ScaleToFit;
2273 //control.Size = new System.Drawing.Size(254, 30);
2274 //control.TabIndex = 2;
2275 label.Font = cds.Font;
2277 TextField field = (TextField) aField;
2278 label.TextAlign = field.Alignment;
2279 label.UseCompatibleTextRendering = true;
2280 label.Text = field.Text;
2284 else if (aField.IsBitmapField)
2286 //Create picture box
2287 PictureBox picture = new PictureBox();
2288 picture.AutoSize = true;
2289 picture.BackColor = System.Drawing.Color.Transparent;
2290 picture.Dock = System.Windows.Forms.DockStyle.Fill;
2291 picture.Location = new System.Drawing.Point(1, 1);
2292 picture.Margin = new System.Windows.Forms.Padding(0);
2293 picture.Name = "pictureBox" + aField;
2295 BitmapField field = (BitmapField) aField;
2296 picture.Image = field.Bitmap;
2300 //TODO: Handle recording field?
2306 /// Called when the user selected a new display type.
2308 /// <param name="sender"></param>
2309 /// <param name="e"></param>
2310 private void comboBoxDisplayType_SelectedIndexChanged(object sender, EventArgs e)
2312 //Store the selected display type in our settings
2313 Properties.Settings.Default.CurrentDisplayIndex = comboBoxDisplayType.SelectedIndex;
2314 cds.DisplayType = comboBoxDisplayType.SelectedIndex;
2315 Properties.Settings.Default.Save();
2317 //Try re-opening the display connection if we were already connected.
2318 //Otherwise just update our status to reflect display type change.
2319 if (iDisplay.IsOpen())
2321 OpenDisplayConnection();
2329 private void maskedTextBoxTimerInterval_TextChanged(object sender, EventArgs e)
2331 if (maskedTextBoxTimerInterval.Text != "")
2333 int interval = Convert.ToInt32(maskedTextBoxTimerInterval.Text);
2337 timer.Interval = interval;
2338 cds.TimerInterval = timer.Interval;
2339 Properties.Settings.Default.Save();
2344 private void maskedTextBoxMinFontSize_TextChanged(object sender, EventArgs e)
2346 if (maskedTextBoxMinFontSize.Text != "")
2348 int minFontSize = Convert.ToInt32(maskedTextBoxMinFontSize.Text);
2350 if (minFontSize > 0)
2352 cds.MinFontSize = minFontSize;
2353 Properties.Settings.Default.Save();
2354 //We need to recreate our layout for that change to take effect
2355 UpdateTableLayoutPanel(iCurrentClientData);
2361 private void maskedTextBoxScrollingSpeed_TextChanged(object sender, EventArgs e)
2363 if (maskedTextBoxScrollingSpeed.Text != "")
2365 int scrollingSpeed = Convert.ToInt32(maskedTextBoxScrollingSpeed.Text);
2367 if (scrollingSpeed > 0)
2369 cds.ScrollingSpeedInPixelsPerSecond = scrollingSpeed;
2370 Properties.Settings.Default.Save();
2371 //We need to recreate our layout for that change to take effect
2372 UpdateTableLayoutPanel(iCurrentClientData);
2377 private void textBoxScrollLoopSeparator_TextChanged(object sender, EventArgs e)
2379 cds.Separator = textBoxScrollLoopSeparator.Text;
2380 Properties.Settings.Default.Save();
2382 //Update our text fields
2383 foreach (MarqueeLabel ctrl in iTableLayoutPanel.Controls)
2385 ctrl.Separator = cds.Separator;
2390 private void buttonPowerOn_Click(object sender, EventArgs e)
2395 private void buttonPowerOff_Click(object sender, EventArgs e)
2397 iDisplay.PowerOff();
2400 private void buttonShowClock_Click(object sender, EventArgs e)
2405 private void buttonHideClock_Click(object sender, EventArgs e)
2410 private void buttonUpdate_Click(object sender, EventArgs e)
2412 InstallUpdateSyncWithInfo();
2420 if (!iDisplay.IsOpen())
2425 //Devices like MDM166AA don't support windowing and frame rendering must be stopped while showing our clock
2426 iSkipFrameRendering = true;
2429 iDisplay.SwapBuffers();
2430 //Then show our clock
2431 iDisplay.ShowClock();
2439 if (!iDisplay.IsOpen())
2444 //Devices like MDM166AA don't support windowing and frame rendering must be stopped while showing our clock
2445 iSkipFrameRendering = false;
2446 iDisplay.HideClock();
2449 private void InstallUpdateSyncWithInfo()
2451 UpdateCheckInfo info = null;
2453 if (ApplicationDeployment.IsNetworkDeployed)
2455 ApplicationDeployment ad = ApplicationDeployment.CurrentDeployment;
2459 info = ad.CheckForDetailedUpdate();
2462 catch (DeploymentDownloadException dde)
2465 "The new version of the application cannot be downloaded at this time. \n\nPlease check your network connection, or try again later. Error: " +
2469 catch (InvalidDeploymentException ide)
2472 "Cannot check for a new version of the application. The ClickOnce deployment is corrupt. Please redeploy the application and try again. Error: " +
2476 catch (InvalidOperationException ioe)
2479 "This application cannot be updated. It is likely not a ClickOnce application. Error: " +
2484 if (info.UpdateAvailable)
2486 Boolean doUpdate = true;
2488 if (!info.IsUpdateRequired)
2491 MessageBox.Show("An update is available. Would you like to update the application now?",
2492 "Update Available", MessageBoxButtons.OKCancel);
2493 if (!(DialogResult.OK == dr))
2500 // Display a message that the application MUST reboot. Display the minimum required version.
2501 MessageBox.Show("This application has detected a mandatory update from your current " +
2502 "version to version " + info.MinimumRequiredVersion.ToString() +
2503 ". The application will now install the update and restart.",
2504 "Update Available", MessageBoxButtons.OK,
2505 MessageBoxIcon.Information);
2513 MessageBox.Show("The application has been upgraded, and will now restart.");
2514 Application.Restart();
2516 catch (DeploymentDownloadException dde)
2519 "Cannot install the latest version of the application. \n\nPlease check your network connection, or try again later. Error: " +
2527 MessageBox.Show("You are already running the latest version.", "Application up-to-date");
2536 private void SysTrayHideShow()
2542 WindowState = FormWindowState.Normal;
2547 /// Use to handle minimize events.
2549 /// <param name="sender"></param>
2550 /// <param name="e"></param>
2551 private void MainForm_SizeChanged(object sender, EventArgs e)
2553 if (WindowState == FormWindowState.Minimized && Properties.Settings.Default.MinimizeToTray)
2565 /// <param name="sender"></param>
2566 /// <param name="e"></param>
2567 private void tableLayoutPanel_SizeChanged(object sender, EventArgs e)
2569 //Our table layout size has changed which means our display size has changed.
2570 //We need to re-create our bitmap.
2571 iCreateBitmap = true;
2577 /// <param name="sender"></param>
2578 /// <param name="e"></param>
2579 private void buttonSelectFile_Click(object sender, EventArgs e)
2581 //openFileDialog1.InitialDirectory = "c:\\";
2582 //openFileDialog.Filter = "EXE files (*.exe)|*.exe|All files (*.*)|*.*";
2583 //openFileDialog.FilterIndex = 1;
2584 openFileDialog.RestoreDirectory = true;
2586 if (DlgBox.ShowDialog(openFileDialog) == DialogResult.OK)
2588 labelStartFileName.Text = openFileDialog.FileName;
2589 Properties.Settings.Default.StartFileName = openFileDialog.FileName;
2590 Properties.Settings.Default.Save();
2597 /// <param name="sender"></param>
2598 /// <param name="e"></param>
2599 private void comboBoxOpticalDrives_SelectedIndexChanged(object sender, EventArgs e)
2601 //Save the optical drive the user selected for ejection
2602 Properties.Settings.Default.OpticalDriveToEject = comboBoxOpticalDrives.SelectedItem.ToString();
2603 Properties.Settings.Default.Save();
2610 private void LogsUpdate()
2612 if (iWriter != null)
2620 /// Broadcast messages to subscribers.
2622 /// <param name="message"></param>
2623 protected override void WndProc(ref Message aMessage)
2627 if (OnWndProc != null)
2629 OnWndProc(ref aMessage);
2632 base.WndProc(ref aMessage);
2635 private void checkBoxCecEnabled_CheckedChanged(object sender, EventArgs e)
2637 //Save CEC enabled status
2638 Properties.Settings.Default.CecEnabled = checkBoxCecEnabled.Checked;
2639 Properties.Settings.Default.Save();
2644 private void comboBoxHdmiPort_SelectedIndexChanged(object sender, EventArgs e)
2646 //Save CEC HDMI port
2647 Properties.Settings.Default.CecHdmiPort = Convert.ToByte(comboBoxHdmiPort.SelectedIndex);
2648 Properties.Settings.Default.CecHdmiPort++;
2649 Properties.Settings.Default.Save();
2657 private void ResetCec()
2659 if (iCecManager == null)
2661 //Thus skipping initial UI setup
2667 if (Properties.Settings.Default.CecEnabled)
2669 iCecManager.Start(Handle, "CEC",
2670 Properties.Settings.Default.CecHdmiPort);
2679 private void SetupCecLogLevel()
2682 iCecManager.Client.LogLevel = 0;
2684 if (checkBoxCecLogError.Checked)
2685 iCecManager.Client.LogLevel |= (int) CecLogLevel.Error;
2687 if (checkBoxCecLogWarning.Checked)
2688 iCecManager.Client.LogLevel |= (int) CecLogLevel.Warning;
2690 if (checkBoxCecLogNotice.Checked)
2691 iCecManager.Client.LogLevel |= (int) CecLogLevel.Notice;
2693 if (checkBoxCecLogTraffic.Checked)
2694 iCecManager.Client.LogLevel |= (int) CecLogLevel.Traffic;
2696 if (checkBoxCecLogDebug.Checked)
2697 iCecManager.Client.LogLevel |= (int) CecLogLevel.Debug;
2699 iCecManager.Client.FilterOutPollLogs = checkBoxCecLogNoPoll.Checked;
2703 private void ButtonStartIdleClient_Click(object sender, EventArgs e)
2708 private void buttonClearLogs_Click(object sender, EventArgs e)
2710 richTextBoxLogs.Clear();
2713 private void checkBoxCecLogs_CheckedChanged(object sender, EventArgs e)
2722 /// <param name="aEvent"></param>
2723 private void SelectEvent(Event aEvent)
2730 string key = aEvent.GetType().Name;
2731 TreeNode[] res = iTreeViewEvents.Nodes.Find(key, false);
2734 iTreeViewEvents.SelectedNode = res[0];
2735 iTreeViewEvents.Focus();
2742 /// Get the current event based on event tree view selection.
2744 /// <returns></returns>
2745 private Event CurrentEvent()
2747 //Walk up the tree from the selected node to find our event
2748 TreeNode node = iTreeViewEvents.SelectedNode;
2749 Event selectedEvent = null;
2750 while (node != null)
2752 if (node.Tag is Event)
2754 selectedEvent = (Event) node.Tag;
2760 return selectedEvent;
2764 /// Get the current action based on event tree view selection
2766 /// <returns></returns>
2767 private SharpLib.Ear.Action CurrentAction()
2769 TreeNode node = iTreeViewEvents.SelectedNode;
2770 if (node != null && node.Tag is SharpLib.Ear.Action)
2772 return (SharpLib.Ear.Action) node.Tag;
2781 /// <param name="sender"></param>
2782 /// <param name="e"></param>
2783 private void buttonAddAction_Click(object sender, EventArgs e)
2785 Event selectedEvent = CurrentEvent();
2786 if (selectedEvent == null)
2788 //We did not find a corresponding event
2792 FormEditAction ea = new FormEditAction();
2793 DialogResult res = CodeProject.Dialog.DlgBox.ShowDialog(ea);
2794 if (res == DialogResult.OK)
2796 selectedEvent.Actions.Add(ea.Action);
2797 Properties.Settings.Default.Actions = ManagerEventAction.Current;
2798 Properties.Settings.Default.Save();
2799 PopulateEventsTreeView();
2806 /// <param name="sender"></param>
2807 /// <param name="e"></param>
2808 private void buttonDeleteAction_Click(object sender, EventArgs e)
2811 SharpLib.Ear.Action action = CurrentAction();
2814 //Must select action node
2818 ManagerEventAction.Current.RemoveAction(action);
2819 Properties.Settings.Default.Actions = ManagerEventAction.Current;
2820 Properties.Settings.Default.Save();
2821 PopulateEventsTreeView();
2824 private void iTreeViewEvents_AfterSelect(object sender, TreeViewEventArgs e)
2826 //Enable buttons according to selected item
2827 buttonAddAction.Enabled = CurrentEvent() != null;
2829 SharpLib.Ear.Action currentAction = CurrentAction();
2830 //If an action is selected enable the following buttons
2831 buttonTestAction.Enabled =
2832 buttonDeleteAction.Enabled =
2833 buttonActionMoveUp.Enabled =
2834 buttonActionMoveDown.Enabled =
2835 currentAction != null;
2837 if (currentAction != null)
2839 //If an action is selected enable move buttons if needed
2840 buttonActionMoveUp.Enabled = iTreeViewEvents.SelectedNode.Index != 0;
2841 buttonActionMoveDown.Enabled = iTreeViewEvents.SelectedNode.Index <
2842 iTreeViewEvents.SelectedNode.Parent.Nodes.Count - 1;
2850 /// <param name="sender"></param>
2851 /// <param name="e"></param>
2852 private void buttonTestAction_Click(object sender, EventArgs e)
2854 SharpLib.Ear.Action a = CurrentAction();
2857 Console.WriteLine("Action test run");
2860 iTreeViewEvents.Focus();
2866 /// <param name="sender"></param>
2867 /// <param name="e"></param>
2868 private void buttonActionMoveUp_Click(object sender, EventArgs e)
2870 SharpLib.Ear.Action a = CurrentAction();
2872 //Action already at the top of the list
2873 iTreeViewEvents.SelectedNode.Index == 0)
2878 //Swap actions in event's action list
2879 Event currentEvent = CurrentEvent();
2880 int currentIndex = iTreeViewEvents.SelectedNode.Index;
2881 SharpLib.Ear.Action movingUp = currentEvent.Actions[currentIndex];
2882 SharpLib.Ear.Action movingDown = currentEvent.Actions[currentIndex-1];
2883 currentEvent.Actions[currentIndex] = movingDown;
2884 currentEvent.Actions[currentIndex-1] = movingUp;
2886 //Save and populate our tree again
2887 Properties.Settings.Default.Actions = ManagerEventAction.Current;
2888 Properties.Settings.Default.Save();
2889 PopulateEventsTreeView();
2896 /// <param name="sender"></param>
2897 /// <param name="e"></param>
2898 private void buttonActionMoveDown_Click(object sender, EventArgs e)
2900 SharpLib.Ear.Action a = CurrentAction();
2902 //Action already at the bottom of the list
2903 iTreeViewEvents.SelectedNode.Index == iTreeViewEvents.SelectedNode.Parent.Nodes.Count - 1)
2908 //Swap actions in event's action list
2909 Event currentEvent = CurrentEvent();
2910 int currentIndex = iTreeViewEvents.SelectedNode.Index;
2911 SharpLib.Ear.Action movingDown = currentEvent.Actions[currentIndex];
2912 SharpLib.Ear.Action movingUp = currentEvent.Actions[currentIndex + 1];
2913 currentEvent.Actions[currentIndex] = movingUp;
2914 currentEvent.Actions[currentIndex + 1] = movingDown;
2916 //Save and populate our tree again
2917 Properties.Settings.Default.Actions = ManagerEventAction.Current;
2918 Properties.Settings.Default.Save();
2919 PopulateEventsTreeView();