Published v0.11.0.0.
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 FormMain : FormMainHid, 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.Events;
145 if (ManagerEventAction.Current == null)
147 //No actions in our settings yet
148 ManagerEventAction.Current = new ManagerEventAction();
149 Properties.Settings.Default.Events = 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 buttonActionAdd.Enabled = false;
320 buttonActionDelete.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 (SharpLib.Ear.Event e in ManagerEventAction.Current.Events)
331 //Create our event node
332 TreeNode eventNode = iTreeViewEvents.Nodes.Add(e.Name);
333 eventNode.Tag = e; //For easy access to our event
336 //Dim our nodes if disabled
337 eventNode.ForeColor = Color.DimGray;
340 //Add event description as child node
341 eventNode.Nodes.Add(e.Description).ForeColor = eventNode.ForeColor;
342 //Create child node for actions root
343 TreeNode actionsNodes = eventNode.Nodes.Add("Actions");
344 actionsNodes.ForeColor = eventNode.ForeColor;
346 // Add our actions for that event
347 foreach (SharpLib.Ear.Action a in e.Actions)
349 TreeNode actionNode = actionsNodes.Nodes.Add(a.Brief());
351 actionNode.ForeColor = eventNode.ForeColor;
352 if (a == currentAction)
354 treeNodeToSelect = actionNode;
359 iTreeViewEvents.ExpandAll();
360 SelectEvent(currentEvent);
362 if (treeNodeToSelect != null)
364 iTreeViewEvents.SelectedNode = treeNodeToSelect;
366 else if (iTreeViewEvents.SelectedNode != null && iTreeViewEvents.SelectedNode.Nodes[1].GetNodeCount(false) > 0)
368 //Select the last action if any
369 iTreeViewEvents.SelectedNode =
370 iTreeViewEvents.SelectedNode.Nodes[1].Nodes[
371 iTreeViewEvents.SelectedNode.Nodes[1].GetNodeCount(false) - 1];
373 else if (iTreeViewEvents.SelectedNode == null && iTreeViewEvents.Nodes.Count > 0)
375 //Still no selected node select the first one then
376 iTreeViewEvents.SelectedNode = iTreeViewEvents.Nodes[0];
384 /// Called when our display is closed.
386 /// <param name="aDisplay"></param>
387 private void OnDisplayClosed(Display aDisplay)
389 //Our display was just closed, update our UI consequently
393 public void OnConnectivityChanged(NetworkManager aNetwork, NLM_CONNECTIVITY newConnectivity)
395 //Update network status
396 UpdateNetworkStatus();
400 /// Update our Network Status
402 private void UpdateNetworkStatus()
404 if (iDisplay.IsOpen())
406 iDisplay.SetIconOnOff(MiniDisplay.IconType.Internet,
407 iNetworkManager.NetworkListManager.IsConnectedToInternet);
408 iDisplay.SetIconOnOff(MiniDisplay.IconType.NetworkSignal, iNetworkManager.NetworkListManager.IsConnected);
413 int iLastNetworkIconIndex = 0;
414 int iUpdateCountSinceLastNetworkAnimation = 0;
419 private void UpdateNetworkSignal(DateTime aLastTickTime, DateTime aNewTickTime)
421 iUpdateCountSinceLastNetworkAnimation++;
422 iUpdateCountSinceLastNetworkAnimation = iUpdateCountSinceLastNetworkAnimation%4;
424 if (iDisplay.IsOpen() && iNetworkManager.NetworkListManager.IsConnected &&
425 iUpdateCountSinceLastNetworkAnimation == 0)
427 int iconCount = iDisplay.IconCount(MiniDisplay.IconType.NetworkSignal);
430 //Prevents div by zero and other undefined behavior
433 iLastNetworkIconIndex++;
434 iLastNetworkIconIndex = iLastNetworkIconIndex%(iconCount*2);
435 for (int i = 0; i < iconCount; i++)
437 if (i < iLastNetworkIconIndex && !(i == 0 && iLastNetworkIconIndex > 3) &&
438 !(i == 1 && iLastNetworkIconIndex > 4))
440 iDisplay.SetIconOn(MiniDisplay.IconType.NetworkSignal, i);
444 iDisplay.SetIconOff(MiniDisplay.IconType.NetworkSignal, i);
453 /// Receive volume change notification and reflect changes on our slider.
455 /// <param name="data"></param>
456 public void OnVolumeNotificationThreadSafe(AudioVolumeNotificationData data)
458 UpdateMasterVolumeThreadSafe();
462 /// Update master volume when user moves our slider.
464 /// <param name="sender"></param>
465 /// <param name="e"></param>
466 private void trackBarMasterVolume_Scroll(object sender, EventArgs e)
468 //Just like Windows Volume Mixer we unmute if the volume is adjusted
469 iMultiMediaDevice.AudioEndpointVolume.Mute = false;
470 //Set volume level according to our volume slider new position
471 iMultiMediaDevice.AudioEndpointVolume.MasterVolumeLevelScalar = trackBarMasterVolume.Value/100.0f;
476 /// Mute check box changed.
478 /// <param name="sender"></param>
479 /// <param name="e"></param>
480 private void checkBoxMute_CheckedChanged(object sender, EventArgs e)
482 iMultiMediaDevice.AudioEndpointVolume.Mute = checkBoxMute.Checked;
486 /// Device State Changed
488 public void OnDeviceStateChanged([MarshalAs(UnmanagedType.LPWStr)] string deviceId,
489 [MarshalAs(UnmanagedType.I4)] DeviceState newState)
496 public void OnDeviceAdded([MarshalAs(UnmanagedType.LPWStr)] string pwstrDeviceId)
503 public void OnDeviceRemoved([MarshalAs(UnmanagedType.LPWStr)] string deviceId)
508 /// Default Device Changed
510 public void OnDefaultDeviceChanged(DataFlow flow, Role role,
511 [MarshalAs(UnmanagedType.LPWStr)] string defaultDeviceId)
513 if (role == Role.Multimedia && flow == DataFlow.Render)
515 UpdateAudioDeviceAndMasterVolumeThreadSafe();
520 /// Property Value Changed
522 /// <param name="pwstrDeviceId"></param>
523 /// <param name="key"></param>
524 public void OnPropertyValueChanged([MarshalAs(UnmanagedType.LPWStr)] string pwstrDeviceId, PropertyKey key)
532 /// Update master volume indicators based our current system states.
533 /// This typically includes volume levels and mute status.
535 private void UpdateMasterVolumeThreadSafe()
537 if (this.InvokeRequired)
539 //Not in the proper thread, invoke ourselves
540 PlainUpdateDelegate d = new PlainUpdateDelegate(UpdateMasterVolumeThreadSafe);
541 this.Invoke(d, new object[] {});
545 //Update volume slider
546 float volumeLevelScalar = iMultiMediaDevice.AudioEndpointVolume.MasterVolumeLevelScalar;
547 trackBarMasterVolume.Value = Convert.ToInt32(volumeLevelScalar*100);
548 //Update mute checkbox
549 checkBoxMute.Checked = iMultiMediaDevice.AudioEndpointVolume.Mute;
551 //If our display connection is open we need to update its icons
552 if (iDisplay.IsOpen())
554 //First take care our our volume level icons
555 int volumeIconCount = iDisplay.IconCount(MiniDisplay.IconType.Volume);
556 if (volumeIconCount > 0)
558 //Compute current volume level from system level and the number of segments in our display volume bar.
559 //That tells us how many segments in our volume bar needs to be turned on.
560 float currentVolume = volumeLevelScalar*volumeIconCount;
561 int segmentOnCount = Convert.ToInt32(currentVolume);
562 //Check if our segment count was rounded up, this will later be used for half brightness segment
563 bool roundedUp = segmentOnCount > currentVolume;
565 for (int i = 0; i < volumeIconCount; i++)
567 if (i < segmentOnCount)
569 //If we are dealing with our last segment and our segment count was rounded up then we will use half brightness.
570 if (i == segmentOnCount - 1 && roundedUp)
573 iDisplay.SetIconStatus(MiniDisplay.IconType.Volume, i,
574 (iDisplay.IconStatusCount(MiniDisplay.IconType.Volume) - 1)/2);
579 iDisplay.SetIconStatus(MiniDisplay.IconType.Volume, i,
580 iDisplay.IconStatusCount(MiniDisplay.IconType.Volume) - 1);
585 iDisplay.SetIconStatus(MiniDisplay.IconType.Volume, i, 0);
590 //Take care of our mute icon
591 iDisplay.SetIconOnOff(MiniDisplay.IconType.Mute, iMultiMediaDevice.AudioEndpointVolume.Mute);
599 private void UpdateAudioDeviceAndMasterVolumeThreadSafe()
601 if (this.InvokeRequired)
603 //Not in the proper thread, invoke ourselves
604 PlainUpdateDelegate d = new PlainUpdateDelegate(UpdateAudioDeviceAndMasterVolumeThreadSafe);
605 this.Invoke(d, new object[] {});
609 //We are in the correct thread just go ahead.
612 //Get our master volume
613 iMultiMediaDevice = iMultiMediaDeviceEnumerator.GetDefaultAudioEndpoint(DataFlow.Render, Role.Multimedia);
615 labelDefaultAudioDevice.Text = iMultiMediaDevice.FriendlyName;
617 //Show our volume in our track bar
618 UpdateMasterVolumeThreadSafe();
620 //Register to get volume modifications
621 iMultiMediaDevice.AudioEndpointVolume.OnVolumeNotification += OnVolumeNotificationThreadSafe;
623 trackBarMasterVolume.Enabled = true;
627 Debug.WriteLine("Exception thrown in UpdateAudioDeviceAndMasterVolume");
628 Debug.WriteLine(ex.ToString());
629 //Something went wrong S/PDIF device ca throw exception I guess
630 trackBarMasterVolume.Enabled = false;
637 private void PopulateDeviceTypes()
639 int count = Display.TypeCount();
641 for (int i = 0; i < count; i++)
643 comboBoxDisplayType.Items.Add(Display.TypeName((MiniDisplay.Type) i));
650 private void PopulateOpticalDrives()
652 //Reset our list of drives
653 comboBoxOpticalDrives.Items.Clear();
654 comboBoxOpticalDrives.Items.Add("None");
656 //Go through each drives on our system and collected the optical ones in our list
657 DriveInfo[] allDrives = DriveInfo.GetDrives();
658 foreach (DriveInfo d in allDrives)
660 Debug.WriteLine("Drive " + d.Name);
661 Debug.WriteLine(" Drive type: {0}", d.DriveType);
663 if (d.DriveType == DriveType.CDRom)
665 //This is an optical drive, add it now
666 comboBoxOpticalDrives.Items.Add(d.Name.Substring(0, 2));
674 /// <returns></returns>
675 public string OpticalDriveToEject()
677 return comboBoxOpticalDrives.SelectedItem.ToString();
685 private void SetupTrayIcon()
687 iNotifyIcon.Icon = GetIcon("vfd.ico");
688 iNotifyIcon.Text = "Sharp Display Manager";
689 iNotifyIcon.Visible = true;
691 //Double click toggles visibility - typically brings up the application
692 iNotifyIcon.DoubleClick += delegate(object obj, EventArgs args)
697 //Adding a context menu, useful to be able to exit the application
698 ContextMenu contextMenu = new ContextMenu();
699 //Context menu item to toggle visibility
700 MenuItem hideShowItem = new MenuItem("Hide/Show");
701 hideShowItem.Click += delegate(object obj, EventArgs args)
705 contextMenu.MenuItems.Add(hideShowItem);
707 //Context menu item separator
708 contextMenu.MenuItems.Add(new MenuItem("-"));
710 //Context menu exit item
711 MenuItem exitItem = new MenuItem("Exit");
712 exitItem.Click += delegate(object obj, EventArgs args)
716 contextMenu.MenuItems.Add(exitItem);
718 iNotifyIcon.ContextMenu = contextMenu;
724 private void SetupRecordingNotification()
726 iRecordingNotification.Icon = GetIcon("record.ico");
727 iRecordingNotification.Text = "No recording";
728 iRecordingNotification.Visible = false;
732 /// Access icons from embedded resources.
734 /// <param name="aName"></param>
735 /// <returns></returns>
736 public static Icon GetIcon(string aName)
738 string[] names = Assembly.GetExecutingAssembly().GetManifestResourceNames();
739 foreach (string name in names)
741 //Find a resource name that ends with the given name
742 if (name.EndsWith(aName))
744 using (Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(name))
746 return new Icon(stream);
756 /// Set our current client.
757 /// This will take care of applying our client layout and set data fields.
759 /// <param name="aSessionId"></param>
760 void SetCurrentClient(string aSessionId, bool aForce = false)
762 if (aSessionId == iCurrentClientSessionId)
764 //Given client is already the current one.
765 //Don't bother changing anything then.
769 ClientData requestedClientData = iClients[aSessionId];
771 //Check when was the last time we switched to that client
772 if (iCurrentClientData != null)
774 //Do not switch client if priority of current client is higher
775 if (!aForce && requestedClientData.Priority < iCurrentClientData.Priority)
781 double lastSwitchToClientSecondsAgo = (DateTime.Now - iCurrentClientData.LastSwitchTime).TotalSeconds;
782 //TODO: put that hard coded value as a client property
783 //Clients should be able to define how often they can be interrupted
784 //Thus a background client can set this to zero allowing any other client to interrupt at any time
785 //We could also compute this delay by looking at the requests frequencies?
787 requestedClientData.Priority == iCurrentClientData.Priority &&
788 //Time sharing is only if clients have the same priority
789 (lastSwitchToClientSecondsAgo < 30)) //Make sure a client is on for at least 30 seconds
791 //Don't switch clients too often
796 //Set current client ID.
797 iCurrentClientSessionId = aSessionId;
798 //Set the time we last switched to that client
799 iClients[aSessionId].LastSwitchTime = DateTime.Now;
800 //Fetch and set current client data.
801 iCurrentClientData = requestedClientData;
802 //Apply layout and set data fields.
803 UpdateTableLayoutPanel(iCurrentClientData);
806 private void buttonFont_Click(object sender, EventArgs e)
808 //fontDialog.ShowColor = true;
809 //fontDialog.ShowApply = true;
810 fontDialog.ShowEffects = true;
811 fontDialog.Font = cds.Font;
813 fontDialog.FixedPitchOnly = checkBoxFixedPitchFontOnly.Checked;
815 //fontDialog.ShowHelp = true;
817 //fontDlg.MaxSize = 40;
818 //fontDlg.MinSize = 22;
820 //fontDialog.Parent = this;
821 //fontDialog.StartPosition = FormStartPosition.CenterParent;
823 //DlgBox.ShowDialog(fontDialog);
825 //if (fontDialog.ShowDialog(this) != DialogResult.Cancel)
826 if (DlgBox.ShowDialog(fontDialog) != DialogResult.Cancel)
828 //Set the fonts to all our labels in our layout
829 foreach (Control ctrl in iTableLayoutPanel.Controls)
831 if (ctrl is MarqueeLabel)
833 ((MarqueeLabel) ctrl).Font = fontDialog.Font;
838 cds.Font = fontDialog.Font;
839 Properties.Settings.Default.Save();
848 void CheckFontHeight()
850 //Show font height and width
851 labelFontHeight.Text = "Font height: " + cds.Font.Height;
852 float charWidth = IsFixedWidth(cds.Font);
853 if (charWidth == 0.0f)
855 labelFontWidth.Visible = false;
859 labelFontWidth.Visible = true;
860 labelFontWidth.Text = "Font width: " + charWidth;
863 MarqueeLabel label = null;
864 //Get the first label control we can find
865 foreach (Control ctrl in iTableLayoutPanel.Controls)
867 if (ctrl is MarqueeLabel)
869 label = (MarqueeLabel) ctrl;
874 //Now check font height and show a warning if needed.
875 if (label != null && label.Font.Height > label.Height)
877 labelWarning.Text = "WARNING: Selected font is too height by " + (label.Font.Height - label.Height) +
879 labelWarning.Visible = true;
883 labelWarning.Visible = false;
888 private void buttonCapture_Click(object sender, EventArgs e)
890 System.Drawing.Bitmap bmp = new System.Drawing.Bitmap(iTableLayoutPanel.Width, iTableLayoutPanel.Height);
891 iTableLayoutPanel.DrawToBitmap(bmp, iTableLayoutPanel.ClientRectangle);
892 //Bitmap bmpToSave = new Bitmap(bmp);
893 bmp.Save("D:\\capture.png");
895 ((MarqueeLabel) iTableLayoutPanel.Controls[0]).Text = "Captured";
898 string outputFileName = "d:\\capture.png";
899 using (MemoryStream memory = new MemoryStream())
901 using (FileStream fs = new FileStream(outputFileName, FileMode.OpenOrCreate, FileAccess.ReadWrite))
903 bmp.Save(memory, System.Drawing.Imaging.ImageFormat.Png);
904 byte[] bytes = memory.ToArray();
905 fs.Write(bytes, 0, bytes.Length);
912 private void CheckForRequestResults()
914 if (iDisplay.IsRequestPending())
916 switch (iDisplay.AttemptRequestCompletion())
918 case MiniDisplay.Request.FirmwareRevision:
919 toolStripStatusLabelConnect.Text += " v" + iDisplay.FirmwareRevision();
920 //Issue next request then
921 iDisplay.RequestPowerSupplyStatus();
924 case MiniDisplay.Request.PowerSupplyStatus:
925 if (iDisplay.PowerSupplyStatus())
927 toolStripStatusLabelPower.Text = "ON";
931 toolStripStatusLabelPower.Text = "OFF";
933 //Issue next request then
934 iDisplay.RequestDeviceId();
937 case MiniDisplay.Request.DeviceId:
938 toolStripStatusLabelConnect.Text += " - " + iDisplay.DeviceId();
939 //No more request to issue
945 public static uint ColorWhiteIsOn(int aX, int aY, uint aPixel)
947 if ((aPixel & 0x00FFFFFF) == 0x00FFFFFF)
954 public static uint ColorUntouched(int aX, int aY, uint aPixel)
959 public static uint ColorInversed(int aX, int aY, uint aPixel)
964 public static uint ColorChessboard(int aX, int aY, uint aPixel)
966 if ((aX%2 == 0) && (aY%2 == 0))
970 else if ((aX%2 != 0) && (aY%2 != 0))
978 public static int ScreenReversedX(System.Drawing.Bitmap aBmp, int aX)
980 return aBmp.Width - aX - 1;
983 public int ScreenReversedY(System.Drawing.Bitmap aBmp, int aY)
985 return iBmp.Height - aY - 1;
988 public int ScreenX(System.Drawing.Bitmap aBmp, int aX)
993 public int ScreenY(System.Drawing.Bitmap aBmp, int aY)
999 /// Select proper pixel delegates according to our current settings.
1001 private void SetupPixelDelegates()
1003 //Select our pixel processing routine
1004 if (cds.InverseColors)
1006 //iColorFx = ColorChessboard;
1007 iColorFx = ColorInversed;
1011 iColorFx = ColorWhiteIsOn;
1014 //Select proper coordinate translation functions
1015 //We used delegate/function pointer to support reverse screen without doing an extra test on each pixels
1016 if (cds.ReverseScreen)
1018 iScreenX = ScreenReversedX;
1019 iScreenY = ScreenReversedY;
1029 //This is our timer tick responsible to perform our render
1030 private void timer_Tick(object sender, EventArgs e)
1032 //Not ideal cause this has nothing to do with display render
1035 //Update our animations
1036 DateTime NewTickTime = DateTime.Now;
1038 UpdateNetworkSignal(LastTickTime, NewTickTime);
1040 //Update animation for all our marquees
1041 foreach (Control ctrl in iTableLayoutPanel.Controls)
1043 if (ctrl is MarqueeLabel)
1045 ((MarqueeLabel) ctrl).UpdateAnimation(LastTickTime, NewTickTime);
1049 //Update our display
1050 if (iDisplay.IsOpen())
1052 CheckForRequestResults();
1054 //Check if frame rendering is needed
1055 //Typically used when showing clock
1056 if (!iSkipFrameRendering)
1061 iBmp = new System.Drawing.Bitmap(iTableLayoutPanel.Width, iTableLayoutPanel.Height,
1062 PixelFormat.Format32bppArgb);
1063 iCreateBitmap = false;
1065 iTableLayoutPanel.DrawToBitmap(iBmp, iTableLayoutPanel.ClientRectangle);
1066 //iBmp.Save("D:\\capture.png");
1068 //Send it to our display
1069 for (int i = 0; i < iBmp.Width; i++)
1071 for (int j = 0; j < iBmp.Height; j++)
1075 //Get our processed pixel coordinates
1076 int x = iScreenX(iBmp, i);
1077 int y = iScreenY(iBmp, j);
1079 uint color = (uint) iBmp.GetPixel(i, j).ToArgb();
1080 //Apply color effects
1081 color = iColorFx(x, y, color);
1083 iDisplay.SetPixel(x, y, color);
1088 iDisplay.SwapBuffers();
1092 //Compute instant FPS
1093 toolStripStatusLabelFps.Text = (1.0/NewTickTime.Subtract(LastTickTime).TotalSeconds).ToString("F0") + " / " +
1094 (1000/timer.Interval).ToString() + " FPS";
1096 LastTickTime = NewTickTime;
1101 /// Attempt to establish connection with our display hardware.
1103 private void OpenDisplayConnection()
1105 CloseDisplayConnection();
1107 if (!iDisplay.Open((MiniDisplay.Type) cds.DisplayType))
1110 toolStripStatusLabelConnect.Text = "Connection error";
1114 private void CloseDisplayConnection()
1116 //Status will be updated upon receiving the closed event
1118 if (iDisplay == null || !iDisplay.IsOpen())
1123 //Do not clear if we gave up on rendering already.
1124 //This means we will keep on displaying clock on MDM166AA for instance.
1125 if (!iSkipFrameRendering)
1128 iDisplay.SwapBuffers();
1131 iDisplay.SetAllIconsStatus(0); //Turn off all icons
1135 private void buttonOpen_Click(object sender, EventArgs e)
1137 OpenDisplayConnection();
1140 private void buttonClose_Click(object sender, EventArgs e)
1142 CloseDisplayConnection();
1145 private void buttonClear_Click(object sender, EventArgs e)
1148 iDisplay.SwapBuffers();
1151 private void buttonFill_Click(object sender, EventArgs e)
1154 iDisplay.SwapBuffers();
1157 private void trackBarBrightness_Scroll(object sender, EventArgs e)
1159 cds.Brightness = trackBarBrightness.Value;
1160 Properties.Settings.Default.Save();
1161 iDisplay.SetBrightness(trackBarBrightness.Value);
1167 /// CDS stands for Current Display Settings
1169 private DisplaySettings cds
1173 DisplaysSettings settings = Properties.Settings.Default.DisplaysSettings;
1174 if (settings == null)
1176 settings = new DisplaysSettings();
1178 Properties.Settings.Default.DisplaysSettings = settings;
1181 //Make sure all our settings have been created
1182 while (settings.Displays.Count <= Properties.Settings.Default.CurrentDisplayIndex)
1184 settings.Displays.Add(new DisplaySettings());
1187 DisplaySettings displaySettings = settings.Displays[Properties.Settings.Default.CurrentDisplayIndex];
1188 return displaySettings;
1193 /// Check if the given font has a fixed character pitch.
1195 /// <param name="ft"></param>
1196 /// <returns>0.0f if this is not a monospace font, otherwise returns the character width.</returns>
1197 public float IsFixedWidth(Font ft)
1199 Graphics g = CreateGraphics();
1200 char[] charSizes = new char[] {'i', 'a', 'Z', '%', '#', 'a', 'B', 'l', 'm', ',', '.'};
1201 float charWidth = g.MeasureString("I", ft, Int32.MaxValue, StringFormat.GenericTypographic).Width;
1203 bool fixedWidth = true;
1205 foreach (char c in charSizes)
1206 if (g.MeasureString(c.ToString(), ft, Int32.MaxValue, StringFormat.GenericTypographic).Width !=
1219 /// Synchronize UI with settings
1221 private void UpdateStatus()
1224 checkBoxShowBorders.Checked = cds.ShowBorders;
1225 iTableLayoutPanel.CellBorderStyle = (cds.ShowBorders
1226 ? TableLayoutPanelCellBorderStyle.Single
1227 : TableLayoutPanelCellBorderStyle.None);
1229 //Set the proper font to each of our labels
1230 foreach (MarqueeLabel ctrl in iTableLayoutPanel.Controls)
1232 ctrl.Font = cds.Font;
1236 //Check if "run on Windows startup" is enabled
1237 checkBoxAutoStart.Checked = iStartupManager.Startup;
1239 checkBoxConnectOnStartup.Checked = Properties.Settings.Default.DisplayConnectOnStartup;
1240 checkBoxMinimizeToTray.Checked = Properties.Settings.Default.MinimizeToTray;
1241 checkBoxStartMinimized.Checked = Properties.Settings.Default.StartMinimized;
1242 iCheckBoxStartIdleClient.Checked = Properties.Settings.Default.StartIdleClient;
1243 labelStartFileName.Text = Properties.Settings.Default.StartFileName;
1246 //Try find our drive in our drive list
1247 int opticalDriveItemIndex = 0;
1248 bool driveNotFound = true;
1249 string opticalDriveToEject = Properties.Settings.Default.OpticalDriveToEject;
1250 foreach (object item in comboBoxOpticalDrives.Items)
1252 if (opticalDriveToEject == item.ToString())
1254 comboBoxOpticalDrives.SelectedIndex = opticalDriveItemIndex;
1255 driveNotFound = false;
1258 opticalDriveItemIndex++;
1263 //We could not find the drive we had saved.
1264 //Select "None" then.
1265 comboBoxOpticalDrives.SelectedIndex = 0;
1269 checkBoxCecEnabled.Checked = Properties.Settings.Default.CecEnabled;
1270 comboBoxHdmiPort.SelectedIndex = Properties.Settings.Default.CecHdmiPort - 1;
1272 //Mini Display settings
1273 checkBoxReverseScreen.Checked = cds.ReverseScreen;
1274 checkBoxInverseColors.Checked = cds.InverseColors;
1275 checkBoxShowVolumeLabel.Checked = cds.ShowVolumeLabel;
1276 checkBoxScaleToFit.Checked = cds.ScaleToFit;
1277 maskedTextBoxMinFontSize.Enabled = cds.ScaleToFit;
1278 labelMinFontSize.Enabled = cds.ScaleToFit;
1279 maskedTextBoxMinFontSize.Text = cds.MinFontSize.ToString();
1280 maskedTextBoxScrollingSpeed.Text = cds.ScrollingSpeedInPixelsPerSecond.ToString();
1281 comboBoxDisplayType.SelectedIndex = cds.DisplayType;
1282 timer.Interval = cds.TimerInterval;
1283 maskedTextBoxTimerInterval.Text = cds.TimerInterval.ToString();
1284 textBoxScrollLoopSeparator.Text = cds.Separator;
1286 SetupPixelDelegates();
1288 if (iDisplay.IsOpen())
1290 //We have a display connection
1291 //Reflect that in our UI
1294 iTableLayoutPanel.Enabled = true;
1295 panelDisplay.Enabled = true;
1297 //Only setup brightness if display is open
1298 trackBarBrightness.Minimum = iDisplay.MinBrightness();
1299 trackBarBrightness.Maximum = iDisplay.MaxBrightness();
1300 if (cds.Brightness < iDisplay.MinBrightness() || cds.Brightness > iDisplay.MaxBrightness())
1302 //Brightness out of range, this can occur when using auto-detect
1303 //Use max brightness instead
1304 trackBarBrightness.Value = iDisplay.MaxBrightness();
1305 iDisplay.SetBrightness(iDisplay.MaxBrightness());
1309 trackBarBrightness.Value = cds.Brightness;
1310 iDisplay.SetBrightness(cds.Brightness);
1313 //Try compute the steps to something that makes sense
1314 trackBarBrightness.LargeChange = Math.Max(1, (iDisplay.MaxBrightness() - iDisplay.MinBrightness())/5);
1315 trackBarBrightness.SmallChange = 1;
1318 buttonFill.Enabled = true;
1319 buttonClear.Enabled = true;
1320 buttonOpen.Enabled = false;
1321 buttonClose.Enabled = true;
1322 trackBarBrightness.Enabled = true;
1323 toolStripStatusLabelConnect.Text = "Connected - " + iDisplay.Vendor() + " - " + iDisplay.Product();
1324 //+ " - " + iDisplay.SerialNumber();
1326 if (iDisplay.SupportPowerOnOff())
1328 buttonPowerOn.Enabled = true;
1329 buttonPowerOff.Enabled = true;
1333 buttonPowerOn.Enabled = false;
1334 buttonPowerOff.Enabled = false;
1337 if (iDisplay.SupportClock())
1339 buttonShowClock.Enabled = true;
1340 buttonHideClock.Enabled = true;
1344 buttonShowClock.Enabled = false;
1345 buttonHideClock.Enabled = false;
1349 //Check if Volume Label is supported. To date only MDM166AA supports that crap :)
1350 checkBoxShowVolumeLabel.Enabled = iDisplay.IconCount(MiniDisplay.IconType.VolumeLabel) > 0;
1352 if (cds.ShowVolumeLabel)
1354 iDisplay.SetIconOn(MiniDisplay.IconType.VolumeLabel);
1358 iDisplay.SetIconOff(MiniDisplay.IconType.VolumeLabel);
1363 //Display connection not available
1364 //Reflect that in our UI
1366 //In debug start our timer even if we don't have a display connection
1369 //In production environment we don't need our timer if no display connection
1372 checkBoxShowVolumeLabel.Enabled = false;
1373 iTableLayoutPanel.Enabled = false;
1374 panelDisplay.Enabled = false;
1375 buttonFill.Enabled = false;
1376 buttonClear.Enabled = false;
1377 buttonOpen.Enabled = true;
1378 buttonClose.Enabled = false;
1379 trackBarBrightness.Enabled = false;
1380 buttonPowerOn.Enabled = false;
1381 buttonPowerOff.Enabled = false;
1382 buttonShowClock.Enabled = false;
1383 buttonHideClock.Enabled = false;
1384 toolStripStatusLabelConnect.Text = "Disconnected";
1385 toolStripStatusLabelPower.Text = "N/A";
1394 /// <param name="sender"></param>
1395 /// <param name="e"></param>
1396 private void checkBoxShowVolumeLabel_CheckedChanged(object sender, EventArgs e)
1398 cds.ShowVolumeLabel = checkBoxShowVolumeLabel.Checked;
1399 Properties.Settings.Default.Save();
1403 private void checkBoxShowBorders_CheckedChanged(object sender, EventArgs e)
1405 //Save our show borders setting
1406 iTableLayoutPanel.CellBorderStyle = (checkBoxShowBorders.Checked
1407 ? TableLayoutPanelCellBorderStyle.Single
1408 : TableLayoutPanelCellBorderStyle.None);
1409 cds.ShowBorders = checkBoxShowBorders.Checked;
1410 Properties.Settings.Default.Save();
1414 private void checkBoxConnectOnStartup_CheckedChanged(object sender, EventArgs e)
1416 //Save our connect on startup setting
1417 Properties.Settings.Default.DisplayConnectOnStartup = checkBoxConnectOnStartup.Checked;
1418 Properties.Settings.Default.Save();
1421 private void checkBoxMinimizeToTray_CheckedChanged(object sender, EventArgs e)
1423 //Save our "Minimize to tray" setting
1424 Properties.Settings.Default.MinimizeToTray = checkBoxMinimizeToTray.Checked;
1425 Properties.Settings.Default.Save();
1429 private void checkBoxStartMinimized_CheckedChanged(object sender, EventArgs e)
1431 //Save our "Start minimized" setting
1432 Properties.Settings.Default.StartMinimized = checkBoxStartMinimized.Checked;
1433 Properties.Settings.Default.Save();
1436 private void checkBoxStartIdleClient_CheckedChanged(object sender, EventArgs e)
1438 Properties.Settings.Default.StartIdleClient = iCheckBoxStartIdleClient.Checked;
1439 Properties.Settings.Default.Save();
1442 private void checkBoxAutoStart_CheckedChanged(object sender, EventArgs e)
1444 iStartupManager.Startup = checkBoxAutoStart.Checked;
1448 private void checkBoxReverseScreen_CheckedChanged(object sender, EventArgs e)
1450 //Save our reverse screen setting
1451 cds.ReverseScreen = checkBoxReverseScreen.Checked;
1452 Properties.Settings.Default.Save();
1453 SetupPixelDelegates();
1456 private void checkBoxInverseColors_CheckedChanged(object sender, EventArgs e)
1458 //Save our inverse colors setting
1459 cds.InverseColors = checkBoxInverseColors.Checked;
1460 Properties.Settings.Default.Save();
1461 SetupPixelDelegates();
1464 private void checkBoxScaleToFit_CheckedChanged(object sender, EventArgs e)
1466 //Save our scale to fit setting
1467 cds.ScaleToFit = checkBoxScaleToFit.Checked;
1468 Properties.Settings.Default.Save();
1470 labelMinFontSize.Enabled = cds.ScaleToFit;
1471 maskedTextBoxMinFontSize.Enabled = cds.ScaleToFit;
1474 private void MainForm_Resize(object sender, EventArgs e)
1476 if (WindowState == FormWindowState.Minimized)
1478 // To workaround our empty bitmap bug on Windows 7 we need to recreate our bitmap when the application is minimized
1479 // That's apparently not needed on Windows 10 but we better leave it in place.
1480 iCreateBitmap = true;
1484 private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
1487 iNetworkManager.Dispose();
1488 CloseDisplayConnection();
1490 e.Cancel = iClosing;
1493 public void StartServer()
1495 iServiceHost = new ServiceHost
1498 new Uri[] {new Uri("net.tcp://localhost:8001/")}
1501 iServiceHost.AddServiceEndpoint(typeof(IService), new NetTcpBinding(SecurityMode.None, true),
1503 iServiceHost.Open();
1506 public void StopServer()
1508 if (iClients.Count > 0 && !iClosing)
1512 BroadcastCloseEvent();
1517 MessageBox.Show("Force exit?", "Waiting for clients...", MessageBoxButtons.YesNo,
1518 MessageBoxIcon.Warning) == DialogResult.Yes)
1520 iClosing = false; //We make sure we force close if asked twice
1525 //We removed that as it often lags for some reason
1526 //iServiceHost.Close();
1530 public void BroadcastCloseEvent()
1532 Trace.TraceInformation("BroadcastCloseEvent - start");
1534 var inactiveClients = new List<string>();
1535 foreach (var client in iClients)
1537 //if (client.Key != eventData.ClientName)
1541 Trace.TraceInformation("BroadcastCloseEvent - " + client.Key);
1542 client.Value.Callback.OnCloseOrder( /*eventData*/);
1544 catch (Exception ex)
1546 inactiveClients.Add(client.Key);
1551 if (inactiveClients.Count > 0)
1553 foreach (var client in inactiveClients)
1555 iClients.Remove(client);
1556 Program.iFormMain.iTreeViewClients.Nodes.Remove(
1557 Program.iFormMain.iTreeViewClients.Nodes.Find(client, false)[0]);
1561 if (iClients.Count == 0)
1568 /// Just remove all our fields.
1570 private void ClearLayout()
1572 iTableLayoutPanel.Controls.Clear();
1573 iTableLayoutPanel.RowStyles.Clear();
1574 iTableLayoutPanel.ColumnStyles.Clear();
1575 iCurrentClientData = null;
1579 /// Just launch a demo client.
1581 private void StartNewClient(string aTopText = "", string aBottomText = "")
1583 Thread clientThread = new Thread(SharpDisplayClient.Program.MainWithParams);
1584 SharpDisplayClient.StartParams myParams = new SharpDisplayClient.StartParams(
1585 new Point(this.Right, this.Top), aTopText, aBottomText);
1586 clientThread.Start(myParams);
1591 /// Just launch our idle client.
1593 private void StartIdleClient(string aTopText = "", string aBottomText = "")
1595 Thread clientThread = new Thread(SharpDisplayClientIdle.Program.MainWithParams);
1596 SharpDisplayClientIdle.StartParams myParams =
1597 new SharpDisplayClientIdle.StartParams(new Point(this.Right, this.Top), aTopText, aBottomText);
1598 clientThread.Start(myParams);
1603 private void buttonStartClient_Click(object sender, EventArgs e)
1608 private void buttonSuspend_Click(object sender, EventArgs e)
1613 private void StartTimer()
1615 LastTickTime = DateTime.Now; //Reset timer to prevent jump
1616 timer.Enabled = true;
1617 UpdateSuspendButton();
1620 private void StopTimer()
1622 LastTickTime = DateTime.Now; //Reset timer to prevent jump
1623 timer.Enabled = false;
1624 UpdateSuspendButton();
1627 private void ToggleTimer()
1629 LastTickTime = DateTime.Now; //Reset timer to prevent jump
1630 timer.Enabled = !timer.Enabled;
1631 UpdateSuspendButton();
1634 private void UpdateSuspendButton()
1638 buttonSuspend.Text = "Run";
1642 buttonSuspend.Text = "Pause";
1647 private void buttonCloseClients_Click(object sender, EventArgs e)
1649 BroadcastCloseEvent();
1652 private void treeViewClients_AfterSelect(object sender, TreeViewEventArgs e)
1654 //Root node must have at least one child
1655 if (e.Node.Nodes.Count == 0)
1660 //If the selected node is the root node of a client then switch to it
1661 string sessionId = e.Node.Nodes[0].Text; //First child of a root node is the sessionId
1662 if (iClients.ContainsKey(sessionId)) //Check that's actually what we are looking at
1664 //We have a valid session just switch to that client
1665 SetCurrentClient(sessionId, true);
1674 /// <param name="aSessionId"></param>
1675 /// <param name="aCallback"></param>
1676 public void AddClientThreadSafe(string aSessionId, ICallback aCallback)
1678 if (this.InvokeRequired)
1680 //Not in the proper thread, invoke ourselves
1681 AddClientDelegate d = new AddClientDelegate(AddClientThreadSafe);
1682 this.Invoke(d, new object[] {aSessionId, aCallback});
1686 //We are in the proper thread
1687 //Add this session to our collection of clients
1688 ClientData newClient = new ClientData(aSessionId, aCallback);
1689 Program.iFormMain.iClients.Add(aSessionId, newClient);
1690 //Add this session to our UI
1691 UpdateClientTreeViewNode(newClient);
1697 /// Find the client with the highest priority if any.
1699 /// <returns>Our highest priority client or null if not a single client is connected.</returns>
1700 public ClientData FindHighestPriorityClient()
1702 ClientData highestPriorityClient = null;
1703 foreach (var client in iClients)
1705 if (highestPriorityClient == null || client.Value.Priority > highestPriorityClient.Priority)
1707 highestPriorityClient = client.Value;
1711 return highestPriorityClient;
1717 /// <param name="aSessionId"></param>
1718 public void RemoveClientThreadSafe(string aSessionId)
1720 if (this.InvokeRequired)
1722 //Not in the proper thread, invoke ourselves
1723 RemoveClientDelegate d = new RemoveClientDelegate(RemoveClientThreadSafe);
1724 this.Invoke(d, new object[] {aSessionId});
1728 //We are in the proper thread
1729 //Remove this session from both client collection and UI tree view
1730 if (Program.iFormMain.iClients.Keys.Contains(aSessionId))
1732 Program.iFormMain.iClients.Remove(aSessionId);
1733 Program.iFormMain.iTreeViewClients.Nodes.Remove(
1734 Program.iFormMain.iTreeViewClients.Nodes.Find(aSessionId, false)[0]);
1735 //Update recording status too whenever a client is removed
1736 UpdateRecordingNotification();
1739 if (iCurrentClientSessionId == aSessionId)
1741 //The current client is closing
1742 iCurrentClientData = null;
1743 //Find the client with the highest priority and set it as current
1744 ClientData newCurrentClient = FindHighestPriorityClient();
1745 if (newCurrentClient != null)
1747 SetCurrentClient(newCurrentClient.SessionId, true);
1751 if (iClients.Count == 0)
1753 //Clear our screen when last client disconnects
1758 //We were closing our form
1759 //All clients are now closed
1760 //Just resume our close operation
1771 /// <param name="aSessionId"></param>
1772 /// <param name="aLayout"></param>
1773 public void SetClientLayoutThreadSafe(string aSessionId, TableLayout aLayout)
1775 if (this.InvokeRequired)
1777 //Not in the proper thread, invoke ourselves
1778 SetLayoutDelegate d = new SetLayoutDelegate(SetClientLayoutThreadSafe);
1779 this.Invoke(d, new object[] {aSessionId, aLayout});
1783 ClientData client = iClients[aSessionId];
1786 //Don't change a thing if the layout is the same
1787 if (!client.Layout.IsSameAs(aLayout))
1789 Debug.Print("SetClientLayoutThreadSafe: Layout updated.");
1790 //Set our client layout then
1791 client.Layout = aLayout;
1792 //So that next time we update all our fields at ones
1793 client.HasNewLayout = true;
1794 //Layout has changed clear our fields then
1795 client.Fields.Clear();
1797 UpdateClientTreeViewNode(client);
1801 Debug.Print("SetClientLayoutThreadSafe: Layout has not changed.");
1810 /// <param name="aSessionId"></param>
1811 /// <param name="aField"></param>
1812 public void SetClientFieldThreadSafe(string aSessionId, DataField aField)
1814 if (this.InvokeRequired)
1816 //Not in the proper thread, invoke ourselves
1817 SetFieldDelegate d = new SetFieldDelegate(SetClientFieldThreadSafe);
1818 this.Invoke(d, new object[] {aSessionId, aField});
1822 //We are in the proper thread
1823 //Call the non-thread-safe variant
1824 SetClientField(aSessionId, aField);
1832 /// Set a data field in the given client.
1834 /// <param name="aSessionId"></param>
1835 /// <param name="aField"></param>
1836 private void SetClientField(string aSessionId, DataField aField)
1838 //TODO: should check if the field actually changed?
1840 ClientData client = iClients[aSessionId];
1841 bool layoutChanged = false;
1842 bool contentChanged = true;
1844 //Fetch our field index
1845 int fieldIndex = client.FindSameFieldIndex(aField);
1849 //No corresponding field, just bail out
1853 //Keep our previous field in there
1854 DataField previousField = client.Fields[fieldIndex];
1855 //Just update that field then
1856 client.Fields[fieldIndex] = aField;
1858 if (!aField.IsTableField)
1860 //We are done then if that field is not in our table layout
1864 TableField tableField = (TableField) aField;
1866 if (previousField.IsSameLayout(aField))
1868 //If we are updating a field in our current client we need to update it in our panel
1869 if (aSessionId == iCurrentClientSessionId)
1871 Control ctrl = iTableLayoutPanel.GetControlFromPosition(tableField.Column, tableField.Row);
1872 if (aField.IsTextField && ctrl is MarqueeLabel)
1874 TextField textField = (TextField) aField;
1875 //Text field control already in place, just change the text
1876 MarqueeLabel label = (MarqueeLabel) ctrl;
1877 contentChanged = (label.Text != textField.Text || label.TextAlign != textField.Alignment);
1878 label.Text = textField.Text;
1879 label.TextAlign = textField.Alignment;
1881 else if (aField.IsBitmapField && ctrl is PictureBox)
1883 BitmapField bitmapField = (BitmapField) aField;
1884 contentChanged = true; //TODO: Bitmap comp or should we leave that to clients?
1885 //Bitmap field control already in place just change the bitmap
1886 PictureBox pictureBox = (PictureBox) ctrl;
1887 pictureBox.Image = bitmapField.Bitmap;
1891 layoutChanged = true;
1897 layoutChanged = true;
1900 //If either content or layout changed we need to update our tree view to reflect the changes
1901 if (contentChanged || layoutChanged)
1903 UpdateClientTreeViewNode(client);
1907 Debug.Print("Layout changed");
1908 //Our layout has changed, if we are already the current client we need to update our panel
1909 if (aSessionId == iCurrentClientSessionId)
1911 //Apply layout and set data fields.
1912 UpdateTableLayoutPanel(iCurrentClientData);
1917 Debug.Print("Layout has not changed.");
1922 Debug.Print("WARNING: content and layout have not changed!");
1925 //When a client field is set we try switching to this client to present the new information to our user
1926 SetCurrentClient(aSessionId);
1932 /// <param name="aTexts"></param>
1933 public void SetClientFieldsThreadSafe(string aSessionId, System.Collections.Generic.IList<DataField> aFields)
1935 if (this.InvokeRequired)
1937 //Not in the proper thread, invoke ourselves
1938 SetFieldsDelegate d = new SetFieldsDelegate(SetClientFieldsThreadSafe);
1939 this.Invoke(d, new object[] {aSessionId, aFields});
1943 ClientData client = iClients[aSessionId];
1945 if (client.HasNewLayout)
1947 //TODO: Assert client.Count == 0
1948 //Our layout was just changed
1949 //Do some special handling to avoid re-creating our panel N times, once for each fields
1950 client.HasNewLayout = false;
1951 //Just set all our fields then
1952 client.Fields.AddRange(aFields);
1953 //Try switch to that client
1954 SetCurrentClient(aSessionId);
1956 //If we are updating the current client update our panel
1957 if (aSessionId == iCurrentClientSessionId)
1959 //Apply layout and set data fields.
1960 UpdateTableLayoutPanel(iCurrentClientData);
1963 UpdateClientTreeViewNode(client);
1967 //Put each our text fields in a label control
1968 foreach (DataField field in aFields)
1970 SetClientField(aSessionId, field);
1979 /// <param name="aSessionId"></param>
1980 /// <param name="aName"></param>
1981 public void SetClientNameThreadSafe(string aSessionId, string aName)
1983 if (this.InvokeRequired)
1985 //Not in the proper thread, invoke ourselves
1986 SetClientNameDelegate d = new SetClientNameDelegate(SetClientNameThreadSafe);
1987 this.Invoke(d, new object[] {aSessionId, aName});
1991 //We are in the proper thread
1993 ClientData client = iClients[aSessionId];
1997 client.Name = aName;
1998 //Update our tree-view
1999 UpdateClientTreeViewNode(client);
2005 public void SetClientPriorityThreadSafe(string aSessionId, uint aPriority)
2007 if (this.InvokeRequired)
2009 //Not in the proper thread, invoke ourselves
2010 SetClientPriorityDelegate d = new SetClientPriorityDelegate(SetClientPriorityThreadSafe);
2011 this.Invoke(d, new object[] {aSessionId, aPriority});
2015 //We are in the proper thread
2017 ClientData client = iClients[aSessionId];
2021 client.Priority = aPriority;
2022 //Update our tree-view
2023 UpdateClientTreeViewNode(client);
2024 //Change our current client as per new priority
2025 ClientData newCurrentClient = FindHighestPriorityClient();
2026 if (newCurrentClient != null)
2028 SetCurrentClient(newCurrentClient.SessionId);
2037 /// <param name="value"></param>
2038 /// <param name="maxChars"></param>
2039 /// <returns></returns>
2040 public static string Truncate(string value, int maxChars)
2042 return value.Length <= maxChars ? value : value.Substring(0, maxChars - 3) + "...";
2046 /// Update our recording notification.
2048 private void UpdateRecordingNotification()
2051 bool activeRecording = false;
2053 RecordingField recField = new RecordingField();
2054 foreach (var client in iClients)
2056 RecordingField rec = (RecordingField) client.Value.FindSameFieldAs(recField);
2057 if (rec != null && rec.IsActive)
2059 activeRecording = true;
2060 //Don't break cause we are collecting the names/texts.
2061 if (!String.IsNullOrEmpty(rec.Text))
2063 text += (rec.Text + "\n");
2067 //Not text for that recording, use client name instead
2068 text += client.Value.Name + " recording\n";
2074 //Update our text no matter what, can't have more than 63 characters otherwise it throws an exception.
2075 iRecordingNotification.Text = Truncate(text, 63);
2077 //Change visibility of notification if needed
2078 if (iRecordingNotification.Visible != activeRecording)
2080 iRecordingNotification.Visible = activeRecording;
2081 //Assuming the notification icon is in sync with our display icon
2082 //Take care of our REC icon
2083 if (iDisplay.IsOpen())
2085 iDisplay.SetIconOnOff(MiniDisplay.IconType.Recording, activeRecording);
2093 /// <param name="aClient"></param>
2094 private void UpdateClientTreeViewNode(ClientData aClient)
2096 Debug.Print("UpdateClientTreeViewNode");
2098 if (aClient == null)
2103 //Hook in record icon update too
2104 UpdateRecordingNotification();
2106 TreeNode node = null;
2107 //Check that our client node already exists
2108 //Get our client root node using its key which is our session ID
2109 TreeNode[] nodes = iTreeViewClients.Nodes.Find(aClient.SessionId, false);
2110 if (nodes.Count() > 0)
2112 //We already have a node for that client
2114 //Clear children as we are going to recreate them below
2119 //Client node does not exists create a new one
2120 iTreeViewClients.Nodes.Add(aClient.SessionId, aClient.SessionId);
2121 node = iTreeViewClients.Nodes.Find(aClient.SessionId, false)[0];
2127 if (!String.IsNullOrEmpty(aClient.Name))
2129 //We have a name, use it as text for our root node
2130 node.Text = aClient.Name;
2131 //Add a child with SessionId
2132 node.Nodes.Add(new TreeNode(aClient.SessionId));
2136 //No name, use session ID instead
2137 node.Text = aClient.SessionId;
2140 //Display client priority
2141 node.Nodes.Add(new TreeNode("Priority: " + aClient.Priority));
2143 if (aClient.Fields.Count > 0)
2145 //Create root node for our texts
2146 TreeNode textsRoot = new TreeNode("Fields");
2147 node.Nodes.Add(textsRoot);
2148 //For each text add a new entry
2149 foreach (DataField field in aClient.Fields)
2151 if (field.IsTextField)
2153 TextField textField = (TextField) field;
2154 textsRoot.Nodes.Add(new TreeNode("[Text]" + textField.Text));
2156 else if (field.IsBitmapField)
2158 textsRoot.Nodes.Add(new TreeNode("[Bitmap]"));
2160 else if (field.IsRecordingField)
2162 RecordingField recordingField = (RecordingField) field;
2163 textsRoot.Nodes.Add(new TreeNode("[Recording]" + recordingField.IsActive));
2173 /// Update our table layout row styles to make sure each rows have similar height
2175 private void UpdateTableLayoutRowStyles()
2177 foreach (RowStyle rowStyle in iTableLayoutPanel.RowStyles)
2179 rowStyle.SizeType = SizeType.Percent;
2180 rowStyle.Height = 100/iTableLayoutPanel.RowCount;
2185 /// Update our display table layout.
2186 /// Will instanciated every field control as defined by our client.
2187 /// Fields must be specified by rows from the left.
2189 /// <param name="aLayout"></param>
2190 private void UpdateTableLayoutPanel(ClientData aClient)
2192 Debug.Print("UpdateTableLayoutPanel");
2194 if (aClient == null)
2201 TableLayout layout = aClient.Layout;
2203 //First clean our current panel
2204 iTableLayoutPanel.Controls.Clear();
2205 iTableLayoutPanel.RowStyles.Clear();
2206 iTableLayoutPanel.ColumnStyles.Clear();
2207 iTableLayoutPanel.RowCount = 0;
2208 iTableLayoutPanel.ColumnCount = 0;
2210 //Then recreate our rows...
2211 while (iTableLayoutPanel.RowCount < layout.Rows.Count)
2213 iTableLayoutPanel.RowCount++;
2217 while (iTableLayoutPanel.ColumnCount < layout.Columns.Count)
2219 iTableLayoutPanel.ColumnCount++;
2223 for (int i = 0; i < iTableLayoutPanel.ColumnCount; i++)
2225 //Create our column styles
2226 this.iTableLayoutPanel.ColumnStyles.Add(layout.Columns[i]);
2229 for (int j = 0; j < iTableLayoutPanel.RowCount; j++)
2233 //Create our row styles
2234 this.iTableLayoutPanel.RowStyles.Add(layout.Rows[j]);
2244 foreach (DataField field in aClient.Fields)
2246 if (!field.IsTableField)
2248 //That field is not taking part in our table layout skip it
2252 TableField tableField = (TableField) field;
2254 //Create a control corresponding to the field specified for that cell
2255 Control control = CreateControlForDataField(tableField);
2257 //Add newly created control to our table layout at the specified row and column
2258 iTableLayoutPanel.Controls.Add(control, tableField.Column, tableField.Row);
2259 //Make sure we specify column and row span for that new control
2260 iTableLayoutPanel.SetColumnSpan(control, tableField.ColumnSpan);
2261 iTableLayoutPanel.SetRowSpan(control, tableField.RowSpan);
2269 /// Check our type of data field and create corresponding control
2271 /// <param name="aField"></param>
2272 private Control CreateControlForDataField(DataField aField)
2274 Control control = null;
2275 if (aField.IsTextField)
2277 MarqueeLabel label = new SharpDisplayManager.MarqueeLabel();
2278 label.AutoEllipsis = true;
2279 label.AutoSize = true;
2280 label.BackColor = System.Drawing.Color.Transparent;
2281 label.Dock = System.Windows.Forms.DockStyle.Fill;
2282 label.Location = new System.Drawing.Point(1, 1);
2283 label.Margin = new System.Windows.Forms.Padding(0);
2284 label.Name = "marqueeLabel" + aField;
2285 label.OwnTimer = false;
2286 label.PixelsPerSecond = cds.ScrollingSpeedInPixelsPerSecond;
2287 label.Separator = cds.Separator;
2288 label.MinFontSize = cds.MinFontSize;
2289 label.ScaleToFit = cds.ScaleToFit;
2290 //control.Size = new System.Drawing.Size(254, 30);
2291 //control.TabIndex = 2;
2292 label.Font = cds.Font;
2294 TextField field = (TextField) aField;
2295 label.TextAlign = field.Alignment;
2296 label.UseCompatibleTextRendering = true;
2297 label.Text = field.Text;
2301 else if (aField.IsBitmapField)
2303 //Create picture box
2304 PictureBox picture = new PictureBox();
2305 picture.AutoSize = true;
2306 picture.BackColor = System.Drawing.Color.Transparent;
2307 picture.Dock = System.Windows.Forms.DockStyle.Fill;
2308 picture.Location = new System.Drawing.Point(1, 1);
2309 picture.Margin = new System.Windows.Forms.Padding(0);
2310 picture.Name = "pictureBox" + aField;
2312 BitmapField field = (BitmapField) aField;
2313 picture.Image = field.Bitmap;
2317 //TODO: Handle recording field?
2323 /// Called when the user selected a new display type.
2325 /// <param name="sender"></param>
2326 /// <param name="e"></param>
2327 private void comboBoxDisplayType_SelectedIndexChanged(object sender, EventArgs e)
2329 //Store the selected display type in our settings
2330 Properties.Settings.Default.CurrentDisplayIndex = comboBoxDisplayType.SelectedIndex;
2331 cds.DisplayType = comboBoxDisplayType.SelectedIndex;
2332 Properties.Settings.Default.Save();
2334 //Try re-opening the display connection if we were already connected.
2335 //Otherwise just update our status to reflect display type change.
2336 if (iDisplay.IsOpen())
2338 OpenDisplayConnection();
2346 private void maskedTextBoxTimerInterval_TextChanged(object sender, EventArgs e)
2348 if (maskedTextBoxTimerInterval.Text != "")
2350 int interval = Convert.ToInt32(maskedTextBoxTimerInterval.Text);
2354 timer.Interval = interval;
2355 cds.TimerInterval = timer.Interval;
2356 Properties.Settings.Default.Save();
2361 private void maskedTextBoxMinFontSize_TextChanged(object sender, EventArgs e)
2363 if (maskedTextBoxMinFontSize.Text != "")
2365 int minFontSize = Convert.ToInt32(maskedTextBoxMinFontSize.Text);
2367 if (minFontSize > 0)
2369 cds.MinFontSize = minFontSize;
2370 Properties.Settings.Default.Save();
2371 //We need to recreate our layout for that change to take effect
2372 UpdateTableLayoutPanel(iCurrentClientData);
2378 private void maskedTextBoxScrollingSpeed_TextChanged(object sender, EventArgs e)
2380 if (maskedTextBoxScrollingSpeed.Text != "")
2382 int scrollingSpeed = Convert.ToInt32(maskedTextBoxScrollingSpeed.Text);
2384 if (scrollingSpeed > 0)
2386 cds.ScrollingSpeedInPixelsPerSecond = scrollingSpeed;
2387 Properties.Settings.Default.Save();
2388 //We need to recreate our layout for that change to take effect
2389 UpdateTableLayoutPanel(iCurrentClientData);
2394 private void textBoxScrollLoopSeparator_TextChanged(object sender, EventArgs e)
2396 cds.Separator = textBoxScrollLoopSeparator.Text;
2397 Properties.Settings.Default.Save();
2399 //Update our text fields
2400 foreach (MarqueeLabel ctrl in iTableLayoutPanel.Controls)
2402 ctrl.Separator = cds.Separator;
2407 private void buttonPowerOn_Click(object sender, EventArgs e)
2412 private void buttonPowerOff_Click(object sender, EventArgs e)
2414 iDisplay.PowerOff();
2417 private void buttonShowClock_Click(object sender, EventArgs e)
2422 private void buttonHideClock_Click(object sender, EventArgs e)
2427 private void buttonUpdate_Click(object sender, EventArgs e)
2429 InstallUpdateSyncWithInfo();
2437 if (!iDisplay.IsOpen())
2442 //Devices like MDM166AA don't support windowing and frame rendering must be stopped while showing our clock
2443 iSkipFrameRendering = true;
2446 iDisplay.SwapBuffers();
2447 //Then show our clock
2448 iDisplay.ShowClock();
2456 if (!iDisplay.IsOpen())
2461 //Devices like MDM166AA don't support windowing and frame rendering must be stopped while showing our clock
2462 iSkipFrameRendering = false;
2463 iDisplay.HideClock();
2466 private void InstallUpdateSyncWithInfo()
2468 UpdateCheckInfo info = null;
2470 if (ApplicationDeployment.IsNetworkDeployed)
2472 ApplicationDeployment ad = ApplicationDeployment.CurrentDeployment;
2476 info = ad.CheckForDetailedUpdate();
2479 catch (DeploymentDownloadException dde)
2482 "The new version of the application cannot be downloaded at this time. \n\nPlease check your network connection, or try again later. Error: " +
2486 catch (InvalidDeploymentException ide)
2489 "Cannot check for a new version of the application. The ClickOnce deployment is corrupt. Please redeploy the application and try again. Error: " +
2493 catch (InvalidOperationException ioe)
2496 "This application cannot be updated. It is likely not a ClickOnce application. Error: " +
2501 if (info.UpdateAvailable)
2503 Boolean doUpdate = true;
2505 if (!info.IsUpdateRequired)
2508 MessageBox.Show("An update is available. Would you like to update the application now?",
2509 "Update Available", MessageBoxButtons.OKCancel);
2510 if (!(DialogResult.OK == dr))
2517 // Display a message that the application MUST reboot. Display the minimum required version.
2518 MessageBox.Show("This application has detected a mandatory update from your current " +
2519 "version to version " + info.MinimumRequiredVersion.ToString() +
2520 ". The application will now install the update and restart.",
2521 "Update Available", MessageBoxButtons.OK,
2522 MessageBoxIcon.Information);
2530 MessageBox.Show("The application has been upgraded, and will now restart.");
2531 Application.Restart();
2533 catch (DeploymentDownloadException dde)
2536 "Cannot install the latest version of the application. \n\nPlease check your network connection, or try again later. Error: " +
2544 MessageBox.Show("You are already running the latest version.", "Application up-to-date");
2553 private void SysTrayHideShow()
2559 WindowState = FormWindowState.Normal;
2564 /// Use to handle minimize events.
2566 /// <param name="sender"></param>
2567 /// <param name="e"></param>
2568 private void MainForm_SizeChanged(object sender, EventArgs e)
2570 if (WindowState == FormWindowState.Minimized && Properties.Settings.Default.MinimizeToTray)
2582 /// <param name="sender"></param>
2583 /// <param name="e"></param>
2584 private void tableLayoutPanel_SizeChanged(object sender, EventArgs e)
2586 //Our table layout size has changed which means our display size has changed.
2587 //We need to re-create our bitmap.
2588 iCreateBitmap = true;
2594 /// <param name="sender"></param>
2595 /// <param name="e"></param>
2596 private void buttonSelectFile_Click(object sender, EventArgs e)
2598 //openFileDialog1.InitialDirectory = "c:\\";
2599 //openFileDialog.Filter = "EXE files (*.exe)|*.exe|All files (*.*)|*.*";
2600 //openFileDialog.FilterIndex = 1;
2601 openFileDialog.RestoreDirectory = true;
2603 if (DlgBox.ShowDialog(openFileDialog) == DialogResult.OK)
2605 labelStartFileName.Text = openFileDialog.FileName;
2606 Properties.Settings.Default.StartFileName = openFileDialog.FileName;
2607 Properties.Settings.Default.Save();
2614 /// <param name="sender"></param>
2615 /// <param name="e"></param>
2616 private void comboBoxOpticalDrives_SelectedIndexChanged(object sender, EventArgs e)
2618 //Save the optical drive the user selected for ejection
2619 Properties.Settings.Default.OpticalDriveToEject = comboBoxOpticalDrives.SelectedItem.ToString();
2620 Properties.Settings.Default.Save();
2627 private void LogsUpdate()
2629 if (iWriter != null)
2637 /// Broadcast messages to subscribers.
2639 /// <param name="message"></param>
2640 protected override void WndProc(ref Message aMessage)
2644 if (OnWndProc != null)
2646 OnWndProc(ref aMessage);
2649 base.WndProc(ref aMessage);
2652 private void checkBoxCecEnabled_CheckedChanged(object sender, EventArgs e)
2654 //Save CEC enabled status
2655 Properties.Settings.Default.CecEnabled = checkBoxCecEnabled.Checked;
2656 Properties.Settings.Default.Save();
2661 private void comboBoxHdmiPort_SelectedIndexChanged(object sender, EventArgs e)
2663 //Save CEC HDMI port
2664 Properties.Settings.Default.CecHdmiPort = Convert.ToByte(comboBoxHdmiPort.SelectedIndex);
2665 Properties.Settings.Default.CecHdmiPort++;
2666 Properties.Settings.Default.Save();
2674 private void ResetCec()
2676 if (iCecManager == null)
2678 //Thus skipping initial UI setup
2684 if (Properties.Settings.Default.CecEnabled)
2686 iCecManager.Start(Handle, "CEC",
2687 Properties.Settings.Default.CecHdmiPort);
2696 private void SetupCecLogLevel()
2699 iCecManager.Client.LogLevel = 0;
2701 if (checkBoxCecLogError.Checked)
2702 iCecManager.Client.LogLevel |= (int) CecLogLevel.Error;
2704 if (checkBoxCecLogWarning.Checked)
2705 iCecManager.Client.LogLevel |= (int) CecLogLevel.Warning;
2707 if (checkBoxCecLogNotice.Checked)
2708 iCecManager.Client.LogLevel |= (int) CecLogLevel.Notice;
2710 if (checkBoxCecLogTraffic.Checked)
2711 iCecManager.Client.LogLevel |= (int) CecLogLevel.Traffic;
2713 if (checkBoxCecLogDebug.Checked)
2714 iCecManager.Client.LogLevel |= (int) CecLogLevel.Debug;
2716 iCecManager.Client.FilterOutPollLogs = checkBoxCecLogNoPoll.Checked;
2720 private void ButtonStartIdleClient_Click(object sender, EventArgs e)
2725 private void buttonClearLogs_Click(object sender, EventArgs e)
2727 richTextBoxLogs.Clear();
2730 private void checkBoxCecLogs_CheckedChanged(object sender, EventArgs e)
2739 /// <param name="aEvent"></param>
2740 private void SelectEvent(Event aEvent)
2747 foreach (TreeNode node in iTreeViewEvents.Nodes)
2749 if (node.Tag == aEvent)
2751 iTreeViewEvents.SelectedNode = node;
2752 iTreeViewEvents.Focus();
2760 /// Get the current event based on event tree view selection.
2762 /// <returns></returns>
2763 private Event CurrentEvent()
2765 //Walk up the tree from the selected node to find our event
2766 TreeNode node = iTreeViewEvents.SelectedNode;
2767 Event selectedEvent = null;
2768 while (node != null)
2770 if (node.Tag is Event)
2772 selectedEvent = (Event) node.Tag;
2778 return selectedEvent;
2782 /// Get the current action based on event tree view selection
2784 /// <returns></returns>
2785 private SharpLib.Ear.Action CurrentAction()
2787 TreeNode node = iTreeViewEvents.SelectedNode;
2788 if (node != null && node.Tag is SharpLib.Ear.Action)
2790 return (SharpLib.Ear.Action) node.Tag;
2799 /// <param name="sender"></param>
2800 /// <param name="e"></param>
2801 private void buttonActionAdd_Click(object sender, EventArgs e)
2803 Event selectedEvent = CurrentEvent();
2804 if (selectedEvent == null)
2806 //We did not find a corresponding event
2810 FormEditObject<SharpLib.Ear.Action> ea = new FormEditObject<SharpLib.Ear.Action>();
2811 ea.Text = "Add action";
2812 DialogResult res = CodeProject.Dialog.DlgBox.ShowDialog(ea);
2813 if (res == DialogResult.OK)
2815 selectedEvent.Actions.Add(ea.Object);
2816 Properties.Settings.Default.Events = ManagerEventAction.Current;
2817 Properties.Settings.Default.Save();
2818 PopulateEventsTreeView();
2825 /// <param name="sender"></param>
2826 /// <param name="e"></param>
2827 private void buttonActionEdit_Click(object sender, EventArgs e)
2829 Event selectedEvent = CurrentEvent();
2830 SharpLib.Ear.Action selectedAction = CurrentAction();
2831 if (selectedEvent == null || selectedAction == null)
2833 //We did not find a corresponding event
2837 FormEditObject<SharpLib.Ear.Action> ea = new FormEditObject<SharpLib.Ear.Action>();
2838 ea.Text = "Edit action";
2839 ea.Object = selectedAction;
2840 int actionIndex = iTreeViewEvents.SelectedNode.Index;
2841 DialogResult res = CodeProject.Dialog.DlgBox.ShowDialog(ea);
2842 if (res == DialogResult.OK)
2845 selectedEvent.Actions[actionIndex]=ea.Object;
2846 //Save and rebuild our event tree view
2847 Properties.Settings.Default.Events = ManagerEventAction.Current;
2848 Properties.Settings.Default.Save();
2849 PopulateEventsTreeView();
2856 /// <param name="sender"></param>
2857 /// <param name="e"></param>
2858 private void buttonActionDelete_Click(object sender, EventArgs e)
2861 SharpLib.Ear.Action action = CurrentAction();
2864 //Must select action node
2868 ManagerEventAction.Current.RemoveAction(action);
2869 Properties.Settings.Default.Events = ManagerEventAction.Current;
2870 Properties.Settings.Default.Save();
2871 PopulateEventsTreeView();
2877 /// <param name="sender"></param>
2878 /// <param name="e"></param>
2879 private void buttonActionTest_Click(object sender, EventArgs e)
2881 SharpLib.Ear.Action a = CurrentAction();
2886 iTreeViewEvents.Focus();
2892 /// <param name="sender"></param>
2893 /// <param name="e"></param>
2894 private void buttonActionMoveUp_Click(object sender, EventArgs e)
2896 SharpLib.Ear.Action a = CurrentAction();
2898 //Action already at the top of the list
2899 iTreeViewEvents.SelectedNode.Index == 0)
2904 //Swap actions in event's action list
2905 Event currentEvent = CurrentEvent();
2906 int currentIndex = iTreeViewEvents.SelectedNode.Index;
2907 SharpLib.Ear.Action movingUp = currentEvent.Actions[currentIndex];
2908 SharpLib.Ear.Action movingDown = currentEvent.Actions[currentIndex-1];
2909 currentEvent.Actions[currentIndex] = movingDown;
2910 currentEvent.Actions[currentIndex-1] = movingUp;
2912 //Save and populate our tree again
2913 Properties.Settings.Default.Events = ManagerEventAction.Current;
2914 Properties.Settings.Default.Save();
2915 PopulateEventsTreeView();
2922 /// <param name="sender"></param>
2923 /// <param name="e"></param>
2924 private void buttonActionMoveDown_Click(object sender, EventArgs e)
2926 SharpLib.Ear.Action a = CurrentAction();
2928 //Action already at the bottom of the list
2929 iTreeViewEvents.SelectedNode.Index == iTreeViewEvents.SelectedNode.Parent.Nodes.Count - 1)
2934 //Swap actions in event's action list
2935 Event currentEvent = CurrentEvent();
2936 int currentIndex = iTreeViewEvents.SelectedNode.Index;
2937 SharpLib.Ear.Action movingDown = currentEvent.Actions[currentIndex];
2938 SharpLib.Ear.Action movingUp = currentEvent.Actions[currentIndex + 1];
2939 currentEvent.Actions[currentIndex] = movingUp;
2940 currentEvent.Actions[currentIndex + 1] = movingDown;
2942 //Save and populate our tree again
2943 Properties.Settings.Default.Events = ManagerEventAction.Current;
2944 Properties.Settings.Default.Save();
2945 PopulateEventsTreeView();
2952 /// <param name="sender"></param>
2953 /// <param name="e"></param>
2954 private void buttonEventTest_Click(object sender, EventArgs e)
2956 Event earEvent = CurrentEvent();
2957 if (earEvent != null)
2964 /// Manages events and actions buttons according to selected item in event tree.
2966 /// <param name="sender"></param>
2967 /// <param name="e"></param>
2968 private void iTreeViewEvents_AfterSelect(object sender, TreeViewEventArgs e)
2976 private void UpdateEventView()
2978 //One can always add an event
2979 buttonEventAdd.Enabled = true;
2981 //Enable buttons according to selected item
2982 buttonActionAdd.Enabled =
2983 buttonEventTest.Enabled =
2984 buttonEventDelete.Enabled =
2985 buttonEventEdit.Enabled =
2986 CurrentEvent() != null;
2988 SharpLib.Ear.Action currentAction = CurrentAction();
2989 //If an action is selected enable the following buttons
2990 buttonActionTest.Enabled =
2991 buttonActionDelete.Enabled =
2992 buttonActionMoveUp.Enabled =
2993 buttonActionMoveDown.Enabled =
2994 buttonActionEdit.Enabled =
2995 currentAction != null;
2997 if (currentAction != null)
2999 //If an action is selected enable move buttons if needed
3000 buttonActionMoveUp.Enabled = iTreeViewEvents.SelectedNode.Index != 0;
3001 buttonActionMoveDown.Enabled = iTreeViewEvents.SelectedNode.Index <
3002 iTreeViewEvents.SelectedNode.Parent.Nodes.Count - 1;
3006 private void buttonEventAdd_Click(object sender, EventArgs e)
3008 FormEditObject<SharpLib.Ear.Event> ea = new FormEditObject<SharpLib.Ear.Event>();
3009 ea.Text = "Add event";
3010 DialogResult res = CodeProject.Dialog.DlgBox.ShowDialog(ea);
3011 if (res == DialogResult.OK)
3013 ManagerEventAction.Current.Events.Add(ea.Object);
3014 Properties.Settings.Default.Events = ManagerEventAction.Current;
3015 Properties.Settings.Default.Save();
3016 PopulateEventsTreeView();
3017 SelectEvent(ea.Object);
3021 private void buttonEventDelete_Click(object sender, EventArgs e)
3023 SharpLib.Ear.Event currentEvent = CurrentEvent();
3024 if (currentEvent == null)
3026 //Must select action node
3030 ManagerEventAction.Current.Events.Remove(currentEvent);
3031 Properties.Settings.Default.Events = ManagerEventAction.Current;
3032 Properties.Settings.Default.Save();
3033 PopulateEventsTreeView();
3036 private void buttonEventEdit_Click(object sender, EventArgs e)
3038 Event selectedEvent = CurrentEvent();
3039 if (selectedEvent == null)
3041 //We did not find a corresponding event
3045 FormEditObject<SharpLib.Ear.Event> ea = new FormEditObject<SharpLib.Ear.Event>();
3046 ea.Text = "Edit event";
3047 ea.Object = selectedEvent;
3048 int actionIndex = iTreeViewEvents.SelectedNode.Index;
3049 DialogResult res = CodeProject.Dialog.DlgBox.ShowDialog(ea);
3050 if (res == DialogResult.OK)
3052 //Save and rebuild our event tree view
3053 Properties.Settings.Default.Events = ManagerEventAction.Current;
3054 Properties.Settings.Default.Save();
3055 PopulateEventsTreeView();
3059 private void iTreeViewEvents_Leave(object sender, EventArgs e)
3061 //Make sure our event tree never looses focus
3062 ((TreeView) sender).Focus();