StephaneLenclud@123: //
StephaneLenclud@123: // Copyright (C) 2014-2015 Stéphane Lenclud.
StephaneLenclud@123: //
StephaneLenclud@123: // This file is part of SharpDisplayManager.
StephaneLenclud@123: //
StephaneLenclud@123: // SharpDisplayManager is free software: you can redistribute it and/or modify
StephaneLenclud@123: // it under the terms of the GNU General Public License as published by
StephaneLenclud@123: // the Free Software Foundation, either version 3 of the License, or
StephaneLenclud@123: // (at your option) any later version.
StephaneLenclud@123: //
StephaneLenclud@123: // SharpDisplayManager is distributed in the hope that it will be useful,
StephaneLenclud@123: // but WITHOUT ANY WARRANTY; without even the implied warranty of
StephaneLenclud@123: // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
StephaneLenclud@123: // GNU General Public License for more details.
StephaneLenclud@123: //
StephaneLenclud@123: // You should have received a copy of the GNU General Public License
StephaneLenclud@123: // along with SharpDisplayManager. If not, see .
StephaneLenclud@123: //
StephaneLenclud@123:
StephaneLenclud@123: using System;
sl@0: using System.Collections.Generic;
sl@0: using System.ComponentModel;
sl@0: using System.Data;
sl@0: using System.Drawing;
sl@0: using System.Linq;
sl@0: using System.Text;
sl@0: using System.Threading.Tasks;
sl@0: using System.Windows.Forms;
sl@14: using System.IO;
sl@0: using CodeProject.Dialog;
sl@14: using System.Drawing.Imaging;
sl@17: using System.ServiceModel;
sl@25: using System.Threading;
sl@31: using System.Diagnostics;
sl@88: using System.Deployment.Application;
sl@94: using System.Reflection;
StephaneLenclud@112: //NAudio
StephaneLenclud@112: using NAudio.CoreAudioApi;
StephaneLenclud@112: using NAudio.CoreAudioApi.Interfaces;
StephaneLenclud@112: using System.Runtime.InteropServices;
StephaneLenclud@206: using CecSharp;
StephaneLenclud@117: //Network
StephaneLenclud@117: using NETWORKLIST;
sl@25: //
sl@25: using SharpDisplayClient;
sl@55: using SharpDisplay;
StephaneLenclud@135: using MiniDisplayInterop;
StephaneLenclud@171: using SharpLib.Display;
StephaneLenclud@234: using Ear = SharpLib.Ear;
StephaneLenclud@124:
sl@0: namespace SharpDisplayManager
sl@0: {
sl@58: //Types declarations
sl@58: public delegate uint ColorProcessingDelegate(int aX, int aY, uint aPixel);
StephaneLenclud@223:
sl@58: public delegate int CoordinateTranslationDelegate(System.Drawing.Bitmap aBmp, int aInt);
StephaneLenclud@223:
sl@62: //Delegates are used for our thread safe method
sl@62: public delegate void AddClientDelegate(string aSessionId, ICallback aCallback);
StephaneLenclud@223:
sl@62: public delegate void RemoveClientDelegate(string aSessionId);
StephaneLenclud@223:
sl@79: public delegate void SetFieldDelegate(string SessionId, DataField aField);
StephaneLenclud@223:
sl@79: public delegate void SetFieldsDelegate(string SessionId, System.Collections.Generic.IList aFields);
StephaneLenclud@223:
sl@62: public delegate void SetLayoutDelegate(string SessionId, TableLayout aLayout);
StephaneLenclud@223:
sl@62: public delegate void SetClientNameDelegate(string aSessionId, string aName);
StephaneLenclud@223:
StephaneLenclud@184: public delegate void SetClientPriorityDelegate(string aSessionId, uint aPriority);
StephaneLenclud@223:
StephaneLenclud@184: public delegate void PlainUpdateDelegate();
StephaneLenclud@223:
StephaneLenclud@167: public delegate void WndProcDelegate(ref Message aMessage);
sl@58:
sl@58: ///
sl@58: /// Our Display manager main form
sl@58: ///
StephaneLenclud@223: [System.ComponentModel.DesignerCategory("Form")]
StephaneLenclud@226: public partial class FormMain : FormMainHid, IMMNotificationClient
sl@0: {
StephaneLenclud@234: //public Manager iManager = new Manager();
sl@2: DateTime LastTickTime;
sl@3: Display iDisplay;
sl@14: System.Drawing.Bitmap iBmp;
sl@14: bool iCreateBitmap; //Workaround render to bitmap issues when minimized
sl@17: ServiceHost iServiceHost;
sl@65: // Our collection of clients sorted by session id.
sl@33: public Dictionary iClients;
sl@65: // The name of the client which informations are currently displayed.
sl@65: public string iCurrentClientSessionId;
sl@65: ClientData iCurrentClientData;
sl@65: //
sl@29: public bool iClosing;
StephaneLenclud@223: //
StephaneLenclud@223: public bool iSkipFrameRendering;
sl@65: //Function pointer for pixel color filtering
sl@58: ColorProcessingDelegate iColorFx;
sl@65: //Function pointer for pixel X coordinate intercept
sl@58: CoordinateTranslationDelegate iScreenX;
sl@65: //Function pointer for pixel Y coordinate intercept
sl@58: CoordinateTranslationDelegate iScreenY;
StephaneLenclud@223: //NAudio
StephaneLenclud@223: private MMDeviceEnumerator iMultiMediaDeviceEnumerator;
StephaneLenclud@223: private MMDevice iMultiMediaDevice;
StephaneLenclud@223: //Network
StephaneLenclud@223: private NetworkManager iNetworkManager;
StephaneLenclud@167:
StephaneLenclud@167: ///
StephaneLenclud@167: /// CEC - Consumer Electronic Control.
StephaneLenclud@167: /// Notably used to turn TV on and off as Windows broadcast monitor on and off notifications.
StephaneLenclud@167: ///
StephaneLenclud@167: private ConsumerElectronicControl iCecManager;
StephaneLenclud@223:
StephaneLenclud@223: ///
StephaneLenclud@223: /// Manage run when Windows startup option
StephaneLenclud@223: ///
StephaneLenclud@223: private StartupManager iStartupManager;
StephaneLenclud@223:
StephaneLenclud@223: ///
StephaneLenclud@223: /// System notification icon used to hide our application from the task bar.
StephaneLenclud@223: ///
StephaneLenclud@223: private SharpLib.Notification.Control iNotifyIcon;
sl@94:
StephaneLenclud@167: ///
StephaneLenclud@194: /// System recording notification icon.
StephaneLenclud@179: ///
StephaneLenclud@179: private SharpLib.Notification.Control iRecordingNotification;
StephaneLenclud@179:
Stephane@202: ///
Stephane@202: ///
Stephane@202: ///
Stephane@202: RichTextBoxTextWriter iWriter;
Stephane@202:
StephaneLenclud@179:
StephaneLenclud@179: ///
StephaneLenclud@167: /// Allow user to receive window messages;
StephaneLenclud@167: ///
StephaneLenclud@167: public event WndProcDelegate OnWndProc;
StephaneLenclud@167:
StephaneLenclud@226: public FormMain()
sl@0: {
StephaneLenclud@235: if (Properties.Settings.Default.EarManager == null)
Stephane@212: {
Stephane@212: //No actions in our settings yet
StephaneLenclud@235: Properties.Settings.Default.EarManager = new EarManager();
Stephane@212: }
Stephane@212: else
Stephane@212: {
StephaneLenclud@235: // We loaded events and actions from our settings
StephaneLenclud@235: // Internalizer apparently skips constructor so we need to initialize it here
StephaneLenclud@235: // Though I reckon that should only be needed when loading an empty EAR manager I guess.
StephaneLenclud@235: Properties.Settings.Default.EarManager.Init();
Stephane@212: }
StephaneLenclud@210: iSkipFrameRendering = false;
StephaneLenclud@223: iClosing = false;
sl@65: iCurrentClientSessionId = "";
sl@65: iCurrentClientData = null;
sl@2: LastTickTime = DateTime.Now;
StephaneLenclud@223: //Instantiate our display and register for events notifications
sl@3: iDisplay = new Display();
StephaneLenclud@223: iDisplay.OnOpened += OnDisplayOpened;
StephaneLenclud@223: iDisplay.OnClosed += OnDisplayClosed;
StephaneLenclud@223: //
StephaneLenclud@223: iClients = new Dictionary();
StephaneLenclud@223: iStartupManager = new StartupManager();
StephaneLenclud@223: iNotifyIcon = new SharpLib.Notification.Control();
StephaneLenclud@179: iRecordingNotification = new SharpLib.Notification.Control();
sl@2:
StephaneLenclud@179: //Have our designer initialize its controls
sl@0: InitializeComponent();
StephaneLenclud@104:
StephaneLenclud@201: //Redirect console output
Stephane@202: iWriter = new RichTextBoxTextWriter(richTextBoxLogs);
Stephane@202: Console.SetOut(iWriter);
StephaneLenclud@201:
StephaneLenclud@201: //Populate device types
StephaneLenclud@201: PopulateDeviceTypes();
StephaneLenclud@104:
StephaneLenclud@152: //Populate optical drives
StephaneLenclud@152: PopulateOpticalDrives();
StephaneLenclud@152:
StephaneLenclud@223: //Initial status update
sl@7: UpdateStatus();
StephaneLenclud@104:
sl@14: //We have a bug when drawing minimized and reusing our bitmap
StephaneLenclud@158: //Though I could not reproduce it on Windows 10
StephaneLenclud@223: iBmp = new System.Drawing.Bitmap(iTableLayoutPanel.Width, iTableLayoutPanel.Height,
StephaneLenclud@223: PixelFormat.Format32bppArgb);
sl@14: iCreateBitmap = false;
sl@94:
StephaneLenclud@223: //Minimize our window if desired
StephaneLenclud@223: if (Properties.Settings.Default.StartMinimized)
StephaneLenclud@223: {
StephaneLenclud@223: WindowState = FormWindowState.Minimized;
StephaneLenclud@223: }
sl@94:
sl@0: }
sl@0:
StephaneLenclud@223: ///
StephaneLenclud@223: ///
StephaneLenclud@223: ///
StephaneLenclud@223: ///
StephaneLenclud@223: ///
StephaneLenclud@106: private void MainForm_Load(object sender, EventArgs e)
StephaneLenclud@106: {
StephaneLenclud@223: //Check if we are running a Click Once deployed application
StephaneLenclud@223: if (ApplicationDeployment.IsNetworkDeployed)
StephaneLenclud@223: {
StephaneLenclud@223: //This is a proper Click Once installation, fetch and show our version number
StephaneLenclud@223: this.Text += " - v" + ApplicationDeployment.CurrentDeployment.CurrentVersion;
StephaneLenclud@223: }
StephaneLenclud@223: else
StephaneLenclud@223: {
StephaneLenclud@223: //Not a proper Click Once installation, assuming development build then
StephaneLenclud@223: this.Text += " - development";
StephaneLenclud@223: }
StephaneLenclud@223:
StephaneLenclud@223: //NAudio
StephaneLenclud@223: iMultiMediaDeviceEnumerator = new MMDeviceEnumerator();
StephaneLenclud@223: iMultiMediaDeviceEnumerator.RegisterEndpointNotificationCallback(this);
StephaneLenclud@223: UpdateAudioDeviceAndMasterVolumeThreadSafe();
StephaneLenclud@223:
StephaneLenclud@223: //Network
StephaneLenclud@223: iNetworkManager = new NetworkManager();
StephaneLenclud@223: iNetworkManager.OnConnectivityChanged += OnConnectivityChanged;
StephaneLenclud@223: UpdateNetworkStatus();
StephaneLenclud@117:
StephaneLenclud@167: //CEC
StephaneLenclud@167: iCecManager = new ConsumerElectronicControl();
StephaneLenclud@167: OnWndProc += iCecManager.OnWndProc;
StephaneLenclud@168: ResetCec();
StephaneLenclud@168:
StephaneLenclud@233: //Harmony
StephaneLenclud@233: ResetHarmony();
StephaneLenclud@233:
StephaneLenclud@211: //Setup Events
StephaneLenclud@219: PopulateEventsTreeView();
StephaneLenclud@167:
StephaneLenclud@167: //Setup notification icon
StephaneLenclud@167: SetupTrayIcon();
StephaneLenclud@106:
StephaneLenclud@179: //Setup recording notification
StephaneLenclud@179: SetupRecordingNotification();
StephaneLenclud@179:
StephaneLenclud@179: // To make sure start up with minimize to tray works
StephaneLenclud@179: if (WindowState == FormWindowState.Minimized && Properties.Settings.Default.MinimizeToTray)
StephaneLenclud@223: {
StephaneLenclud@223: Visible = false;
StephaneLenclud@223: }
StephaneLenclud@106:
StephaneLenclud@106: #if !DEBUG
StephaneLenclud@223: //When not debugging we want the screen to be empty until a client takes over
StephaneLenclud@106: ClearLayout();
StephaneLenclud@106: #else
StephaneLenclud@223: //When developing we want at least one client for testing
StephaneLenclud@223: StartNewClient("abcdefghijklmnopqrst-0123456789", "ABCDEFGHIJKLMNOPQRST-0123456789");
StephaneLenclud@106: #endif
StephaneLenclud@106:
StephaneLenclud@223: //Open display connection on start-up if needed
StephaneLenclud@223: if (Properties.Settings.Default.DisplayConnectOnStartup)
StephaneLenclud@223: {
StephaneLenclud@223: OpenDisplayConnection();
StephaneLenclud@223: }
StephaneLenclud@223:
StephaneLenclud@223: //Start our server so that we can get client requests
StephaneLenclud@223: StartServer();
StephaneLenclud@223:
StephaneLenclud@223: //Register for HID events
StephaneLenclud@223: RegisterHidDevices();
StephaneLenclud@194:
StephaneLenclud@194: //Start Idle client if needed
StephaneLenclud@194: if (Properties.Settings.Default.StartIdleClient)
StephaneLenclud@194: {
StephaneLenclud@194: StartIdleClient();
StephaneLenclud@194: }
StephaneLenclud@106: }
StephaneLenclud@106:
StephaneLenclud@223: ///
StephaneLenclud@223: /// Called when our display is opened.
StephaneLenclud@223: ///
StephaneLenclud@223: ///
StephaneLenclud@223: private void OnDisplayOpened(Display aDisplay)
StephaneLenclud@223: {
StephaneLenclud@187: //Make sure we resume frame rendering
StephaneLenclud@187: iSkipFrameRendering = false;
StephaneLenclud@122:
StephaneLenclud@223: //Set our screen size now that our display is connected
StephaneLenclud@223: //Our panelDisplay is the container of our tableLayoutPanel
StephaneLenclud@223: //tableLayoutPanel will resize itself to fit the client size of our panelDisplay
StephaneLenclud@223: //panelDisplay needs an extra 2 pixels for borders on each sides
StephaneLenclud@223: //tableLayoutPanel will eventually be the exact size of our display
StephaneLenclud@223: Size size = new Size(iDisplay.WidthInPixels() + 2, iDisplay.HeightInPixels() + 2);
StephaneLenclud@223: panelDisplay.Size = size;
StephaneLenclud@223:
StephaneLenclud@223: //Our display was just opened, update our UI
StephaneLenclud@223: UpdateStatus();
StephaneLenclud@223: //Initiate asynchronous request
StephaneLenclud@223: iDisplay.RequestFirmwareRevision();
StephaneLenclud@223:
StephaneLenclud@223: //Audio
StephaneLenclud@223: UpdateMasterVolumeThreadSafe();
StephaneLenclud@223: //Network
StephaneLenclud@223: UpdateNetworkStatus();
StephaneLenclud@112:
StephaneLenclud@108: #if DEBUG
StephaneLenclud@223: //Testing icon in debug, no arm done if icon not supported
StephaneLenclud@223: //iDisplay.SetIconStatus(Display.TMiniDisplayIconType.EMiniDisplayIconRecording, 0, 1);
StephaneLenclud@223: //iDisplay.SetAllIconsStatus(2);
StephaneLenclud@108: #endif
StephaneLenclud@108:
StephaneLenclud@223: }
StephaneLenclud@104:
StephaneLenclud@211: ///
StephaneLenclud@218: /// Populate tree view with events and actions
StephaneLenclud@211: ///
StephaneLenclud@219: private void PopulateEventsTreeView()
StephaneLenclud@211: {
StephaneLenclud@217: //Disable action buttons
StephaneLenclud@227: buttonActionAdd.Enabled = false;
StephaneLenclud@227: buttonActionDelete.Enabled = false;
StephaneLenclud@217:
StephaneLenclud@234: Ear.Event currentEvent = CurrentEvent();
StephaneLenclud@234: Ear.Action currentAction = CurrentAction();
StephaneLenclud@223: TreeNode treeNodeToSelect = null;
StephaneLenclud@231:
StephaneLenclud@211: //Reset our tree
StephaneLenclud@211: iTreeViewEvents.Nodes.Clear();
StephaneLenclud@211: //Populate registered events
StephaneLenclud@235: foreach (Ear.Event e in Properties.Settings.Default.EarManager.Events)
StephaneLenclud@211: {
StephaneLenclud@231: //Create our event node
StephaneLenclud@237: TreeNode eventNode = iTreeViewEvents.Nodes.Add(e.Brief());
StephaneLenclud@231: eventNode.Tag = e; //For easy access to our event
StephaneLenclud@231: if (!e.Enabled)
StephaneLenclud@231: {
StephaneLenclud@231: //Dim our nodes if disabled
StephaneLenclud@231: eventNode.ForeColor = Color.DimGray;
StephaneLenclud@231: }
StephaneLenclud@231:
StephaneLenclud@231: //Add event description as child node
StephaneLenclud@231: eventNode.Nodes.Add(e.Description).ForeColor = eventNode.ForeColor;
StephaneLenclud@231: //Create child node for actions root
StephaneLenclud@231: TreeNode actionsNodes = eventNode.Nodes.Add("Actions");
StephaneLenclud@231: actionsNodes.ForeColor = eventNode.ForeColor;
StephaneLenclud@211:
StephaneLenclud@214: // Add our actions for that event
StephaneLenclud@234: foreach (Ear.Action a in e.Actions)
StephaneLenclud@211: {
StephaneLenclud@220: TreeNode actionNode = actionsNodes.Nodes.Add(a.Brief());
StephaneLenclud@214: actionNode.Tag = a;
StephaneLenclud@231: actionNode.ForeColor = eventNode.ForeColor;
StephaneLenclud@223: if (a == currentAction)
StephaneLenclud@223: {
StephaneLenclud@223: treeNodeToSelect = actionNode;
StephaneLenclud@223: }
StephaneLenclud@211: }
StephaneLenclud@211: }
StephaneLenclud@211:
StephaneLenclud@214: iTreeViewEvents.ExpandAll();
StephaneLenclud@218: SelectEvent(currentEvent);
StephaneLenclud@223:
StephaneLenclud@223: if (treeNodeToSelect != null)
StephaneLenclud@219: {
StephaneLenclud@223: iTreeViewEvents.SelectedNode = treeNodeToSelect;
StephaneLenclud@219: }
StephaneLenclud@223: else if (iTreeViewEvents.SelectedNode != null && iTreeViewEvents.SelectedNode.Nodes[1].GetNodeCount(false) > 0)
StephaneLenclud@223: {
StephaneLenclud@223: //Select the last action if any
StephaneLenclud@223: iTreeViewEvents.SelectedNode =
StephaneLenclud@223: iTreeViewEvents.SelectedNode.Nodes[1].Nodes[
StephaneLenclud@223: iTreeViewEvents.SelectedNode.Nodes[1].GetNodeCount(false) - 1];
StephaneLenclud@223: }
StephaneLenclud@231: else if (iTreeViewEvents.SelectedNode == null && iTreeViewEvents.Nodes.Count > 0)
StephaneLenclud@231: {
StephaneLenclud@231: //Still no selected node select the first one then
StephaneLenclud@231: iTreeViewEvents.SelectedNode = iTreeViewEvents.Nodes[0];
StephaneLenclud@231: }
StephaneLenclud@231:
StephaneLenclud@231:
StephaneLenclud@231: UpdateEventView();
StephaneLenclud@211: }
StephaneLenclud@211:
StephaneLenclud@223: ///
StephaneLenclud@223: /// Called when our display is closed.
StephaneLenclud@223: ///
StephaneLenclud@223: ///
StephaneLenclud@223: private void OnDisplayClosed(Display aDisplay)
StephaneLenclud@223: {
StephaneLenclud@187: //Our display was just closed, update our UI consequently
StephaneLenclud@187: UpdateStatus();
StephaneLenclud@223: }
StephaneLenclud@223:
StephaneLenclud@223: public void OnConnectivityChanged(NetworkManager aNetwork, NLM_CONNECTIVITY newConnectivity)
StephaneLenclud@223: {
StephaneLenclud@223: //Update network status
StephaneLenclud@223: UpdateNetworkStatus();
StephaneLenclud@223: }
StephaneLenclud@223:
StephaneLenclud@223: ///
StephaneLenclud@223: /// Update our Network Status
StephaneLenclud@223: ///
StephaneLenclud@223: private void UpdateNetworkStatus()
StephaneLenclud@223: {
StephaneLenclud@223: if (iDisplay.IsOpen())
StephaneLenclud@223: {
StephaneLenclud@223: iDisplay.SetIconOnOff(MiniDisplay.IconType.Internet,
StephaneLenclud@223: iNetworkManager.NetworkListManager.IsConnectedToInternet);
StephaneLenclud@135: iDisplay.SetIconOnOff(MiniDisplay.IconType.NetworkSignal, iNetworkManager.NetworkListManager.IsConnected);
StephaneLenclud@223: }
StephaneLenclud@223: }
StephaneLenclud@223:
StephaneLenclud@223:
StephaneLenclud@223: int iLastNetworkIconIndex = 0;
StephaneLenclud@223: int iUpdateCountSinceLastNetworkAnimation = 0;
StephaneLenclud@223:
StephaneLenclud@223: ///
StephaneLenclud@223: ///
StephaneLenclud@223: ///
StephaneLenclud@223: private void UpdateNetworkSignal(DateTime aLastTickTime, DateTime aNewTickTime)
StephaneLenclud@223: {
StephaneLenclud@223: iUpdateCountSinceLastNetworkAnimation++;
StephaneLenclud@223: iUpdateCountSinceLastNetworkAnimation = iUpdateCountSinceLastNetworkAnimation%4;
StephaneLenclud@223:
StephaneLenclud@223: if (iDisplay.IsOpen() && iNetworkManager.NetworkListManager.IsConnected &&
StephaneLenclud@223: iUpdateCountSinceLastNetworkAnimation == 0)
StephaneLenclud@223: {
StephaneLenclud@135: int iconCount = iDisplay.IconCount(MiniDisplay.IconType.NetworkSignal);
StephaneLenclud@223: if (iconCount <= 0)
StephaneLenclud@223: {
StephaneLenclud@223: //Prevents div by zero and other undefined behavior
StephaneLenclud@223: return;
StephaneLenclud@223: }
StephaneLenclud@223: iLastNetworkIconIndex++;
StephaneLenclud@223: iLastNetworkIconIndex = iLastNetworkIconIndex%(iconCount*2);
StephaneLenclud@223: for (int i = 0; i < iconCount; i++)
StephaneLenclud@223: {
StephaneLenclud@223: if (i < iLastNetworkIconIndex && !(i == 0 && iLastNetworkIconIndex > 3) &&
StephaneLenclud@223: !(i == 1 && iLastNetworkIconIndex > 4))
StephaneLenclud@223: {
StephaneLenclud@135: iDisplay.SetIconOn(MiniDisplay.IconType.NetworkSignal, i);
StephaneLenclud@223: }
StephaneLenclud@223: else
StephaneLenclud@223: {
StephaneLenclud@135: iDisplay.SetIconOff(MiniDisplay.IconType.NetworkSignal, i);
StephaneLenclud@223: }
StephaneLenclud@223: }
StephaneLenclud@223: }
StephaneLenclud@223: }
StephaneLenclud@118:
StephaneLenclud@118:
StephaneLenclud@118:
StephaneLenclud@112: ///
StephaneLenclud@112: /// Receive volume change notification and reflect changes on our slider.
StephaneLenclud@112: ///
StephaneLenclud@112: ///
StephaneLenclud@112: public void OnVolumeNotificationThreadSafe(AudioVolumeNotificationData data)
StephaneLenclud@112: {
StephaneLenclud@223: UpdateMasterVolumeThreadSafe();
StephaneLenclud@112: }
StephaneLenclud@112:
StephaneLenclud@112: ///
StephaneLenclud@112: /// Update master volume when user moves our slider.
StephaneLenclud@112: ///
StephaneLenclud@112: ///
StephaneLenclud@112: ///
StephaneLenclud@112: private void trackBarMasterVolume_Scroll(object sender, EventArgs e)
StephaneLenclud@112: {
StephaneLenclud@223: //Just like Windows Volume Mixer we unmute if the volume is adjusted
StephaneLenclud@223: iMultiMediaDevice.AudioEndpointVolume.Mute = false;
StephaneLenclud@223: //Set volume level according to our volume slider new position
StephaneLenclud@223: iMultiMediaDevice.AudioEndpointVolume.MasterVolumeLevelScalar = trackBarMasterVolume.Value/100.0f;
StephaneLenclud@112: }
StephaneLenclud@112:
StephaneLenclud@113:
StephaneLenclud@223: ///
StephaneLenclud@223: /// Mute check box changed.
StephaneLenclud@223: ///
StephaneLenclud@223: ///
StephaneLenclud@223: ///
StephaneLenclud@223: private void checkBoxMute_CheckedChanged(object sender, EventArgs e)
StephaneLenclud@223: {
StephaneLenclud@223: iMultiMediaDevice.AudioEndpointVolume.Mute = checkBoxMute.Checked;
StephaneLenclud@223: }
StephaneLenclud@113:
StephaneLenclud@112: ///
StephaneLenclud@112: /// Device State Changed
StephaneLenclud@112: ///
StephaneLenclud@223: public void OnDeviceStateChanged([MarshalAs(UnmanagedType.LPWStr)] string deviceId,
StephaneLenclud@223: [MarshalAs(UnmanagedType.I4)] DeviceState newState)
StephaneLenclud@223: {
StephaneLenclud@223: }
StephaneLenclud@112:
StephaneLenclud@112: ///
StephaneLenclud@112: /// Device Added
StephaneLenclud@112: ///
StephaneLenclud@223: public void OnDeviceAdded([MarshalAs(UnmanagedType.LPWStr)] string pwstrDeviceId)
StephaneLenclud@223: {
StephaneLenclud@223: }
StephaneLenclud@112:
StephaneLenclud@112: ///
StephaneLenclud@112: /// Device Removed
StephaneLenclud@112: ///
StephaneLenclud@223: public void OnDeviceRemoved([MarshalAs(UnmanagedType.LPWStr)] string deviceId)
StephaneLenclud@223: {
StephaneLenclud@223: }
StephaneLenclud@112:
StephaneLenclud@112: ///
StephaneLenclud@112: /// Default Device Changed
StephaneLenclud@112: ///
StephaneLenclud@223: public void OnDefaultDeviceChanged(DataFlow flow, Role role,
StephaneLenclud@223: [MarshalAs(UnmanagedType.LPWStr)] string defaultDeviceId)
StephaneLenclud@112: {
StephaneLenclud@112: if (role == Role.Multimedia && flow == DataFlow.Render)
StephaneLenclud@112: {
StephaneLenclud@112: UpdateAudioDeviceAndMasterVolumeThreadSafe();
StephaneLenclud@112: }
StephaneLenclud@112: }
StephaneLenclud@112:
StephaneLenclud@112: ///
StephaneLenclud@112: /// Property Value Changed
StephaneLenclud@112: ///
StephaneLenclud@112: ///
StephaneLenclud@112: ///
StephaneLenclud@223: public void OnPropertyValueChanged([MarshalAs(UnmanagedType.LPWStr)] string pwstrDeviceId, PropertyKey key)
StephaneLenclud@223: {
StephaneLenclud@223: }
StephaneLenclud@223:
StephaneLenclud@223:
StephaneLenclud@223:
StephaneLenclud@223:
StephaneLenclud@223: ///
StephaneLenclud@223: /// Update master volume indicators based our current system states.
StephaneLenclud@223: /// This typically includes volume levels and mute status.
StephaneLenclud@223: ///
StephaneLenclud@223: private void UpdateMasterVolumeThreadSafe()
StephaneLenclud@223: {
StephaneLenclud@223: if (this.InvokeRequired)
StephaneLenclud@223: {
StephaneLenclud@223: //Not in the proper thread, invoke ourselves
StephaneLenclud@223: PlainUpdateDelegate d = new PlainUpdateDelegate(UpdateMasterVolumeThreadSafe);
StephaneLenclud@223: this.Invoke(d, new object[] {});
StephaneLenclud@223: return;
StephaneLenclud@223: }
StephaneLenclud@223:
StephaneLenclud@223: //Update volume slider
StephaneLenclud@223: float volumeLevelScalar = iMultiMediaDevice.AudioEndpointVolume.MasterVolumeLevelScalar;
StephaneLenclud@223: trackBarMasterVolume.Value = Convert.ToInt32(volumeLevelScalar*100);
StephaneLenclud@223: //Update mute checkbox
StephaneLenclud@223: checkBoxMute.Checked = iMultiMediaDevice.AudioEndpointVolume.Mute;
StephaneLenclud@223:
StephaneLenclud@223: //If our display connection is open we need to update its icons
StephaneLenclud@223: if (iDisplay.IsOpen())
StephaneLenclud@223: {
StephaneLenclud@223: //First take care our our volume level icons
StephaneLenclud@135: int volumeIconCount = iDisplay.IconCount(MiniDisplay.IconType.Volume);
StephaneLenclud@223: if (volumeIconCount > 0)
StephaneLenclud@223: {
StephaneLenclud@223: //Compute current volume level from system level and the number of segments in our display volume bar.
StephaneLenclud@223: //That tells us how many segments in our volume bar needs to be turned on.
StephaneLenclud@223: float currentVolume = volumeLevelScalar*volumeIconCount;
StephaneLenclud@223: int segmentOnCount = Convert.ToInt32(currentVolume);
StephaneLenclud@223: //Check if our segment count was rounded up, this will later be used for half brightness segment
StephaneLenclud@223: bool roundedUp = segmentOnCount > currentVolume;
StephaneLenclud@223:
StephaneLenclud@223: for (int i = 0; i < volumeIconCount; i++)
StephaneLenclud@223: {
StephaneLenclud@223: if (i < segmentOnCount)
StephaneLenclud@223: {
StephaneLenclud@223: //If we are dealing with our last segment and our segment count was rounded up then we will use half brightness.
StephaneLenclud@223: if (i == segmentOnCount - 1 && roundedUp)
StephaneLenclud@223: {
StephaneLenclud@223: //Half brightness
StephaneLenclud@223: iDisplay.SetIconStatus(MiniDisplay.IconType.Volume, i,
StephaneLenclud@223: (iDisplay.IconStatusCount(MiniDisplay.IconType.Volume) - 1)/2);
StephaneLenclud@223: }
StephaneLenclud@223: else
StephaneLenclud@223: {
StephaneLenclud@223: //Full brightness
StephaneLenclud@223: iDisplay.SetIconStatus(MiniDisplay.IconType.Volume, i,
StephaneLenclud@223: iDisplay.IconStatusCount(MiniDisplay.IconType.Volume) - 1);
StephaneLenclud@223: }
StephaneLenclud@223: }
StephaneLenclud@223: else
StephaneLenclud@223: {
StephaneLenclud@135: iDisplay.SetIconStatus(MiniDisplay.IconType.Volume, i, 0);
StephaneLenclud@223: }
StephaneLenclud@223: }
StephaneLenclud@223: }
StephaneLenclud@223:
StephaneLenclud@223: //Take care of our mute icon
StephaneLenclud@135: iDisplay.SetIconOnOff(MiniDisplay.IconType.Mute, iMultiMediaDevice.AudioEndpointVolume.Mute);
StephaneLenclud@223: }
StephaneLenclud@223:
StephaneLenclud@223: }
StephaneLenclud@112:
StephaneLenclud@112: ///
StephaneLenclud@112: ///
StephaneLenclud@112: ///
StephaneLenclud@112: private void UpdateAudioDeviceAndMasterVolumeThreadSafe()
StephaneLenclud@112: {
StephaneLenclud@112: if (this.InvokeRequired)
StephaneLenclud@112: {
StephaneLenclud@112: //Not in the proper thread, invoke ourselves
StephaneLenclud@223: PlainUpdateDelegate d = new PlainUpdateDelegate(UpdateAudioDeviceAndMasterVolumeThreadSafe);
StephaneLenclud@223: this.Invoke(d, new object[] {});
StephaneLenclud@112: return;
StephaneLenclud@112: }
StephaneLenclud@223:
StephaneLenclud@112: //We are in the correct thread just go ahead.
StephaneLenclud@112: try
StephaneLenclud@223: {
StephaneLenclud@112: //Get our master volume
StephaneLenclud@223: iMultiMediaDevice = iMultiMediaDeviceEnumerator.GetDefaultAudioEndpoint(DataFlow.Render, Role.Multimedia);
StephaneLenclud@223: //Update our label
StephaneLenclud@223: labelDefaultAudioDevice.Text = iMultiMediaDevice.FriendlyName;
StephaneLenclud@116:
StephaneLenclud@112: //Show our volume in our track bar
StephaneLenclud@223: UpdateMasterVolumeThreadSafe();
StephaneLenclud@112:
StephaneLenclud@112: //Register to get volume modifications
StephaneLenclud@223: iMultiMediaDevice.AudioEndpointVolume.OnVolumeNotification += OnVolumeNotificationThreadSafe;
StephaneLenclud@112: //
StephaneLenclud@223: trackBarMasterVolume.Enabled = true;
StephaneLenclud@112: }
StephaneLenclud@112: catch (Exception ex)
StephaneLenclud@112: {
StephaneLenclud@112: Debug.WriteLine("Exception thrown in UpdateAudioDeviceAndMasterVolume");
StephaneLenclud@112: Debug.WriteLine(ex.ToString());
StephaneLenclud@112: //Something went wrong S/PDIF device ca throw exception I guess
StephaneLenclud@223: trackBarMasterVolume.Enabled = false;
StephaneLenclud@112: }
StephaneLenclud@112: }
StephaneLenclud@104:
StephaneLenclud@223: ///
StephaneLenclud@223: ///
StephaneLenclud@223: ///
StephaneLenclud@223: private void PopulateDeviceTypes()
StephaneLenclud@223: {
StephaneLenclud@223: int count = Display.TypeCount();
StephaneLenclud@223:
StephaneLenclud@223: for (int i = 0; i < count; i++)
StephaneLenclud@223: {
StephaneLenclud@223: comboBoxDisplayType.Items.Add(Display.TypeName((MiniDisplay.Type) i));
StephaneLenclud@223: }
StephaneLenclud@223: }
StephaneLenclud@104:
StephaneLenclud@152: ///
StephaneLenclud@152: ///
StephaneLenclud@152: ///
StephaneLenclud@152: private void PopulateOpticalDrives()
StephaneLenclud@152: {
StephaneLenclud@152: //Reset our list of drives
StephaneLenclud@152: comboBoxOpticalDrives.Items.Clear();
StephaneLenclud@153: comboBoxOpticalDrives.Items.Add("None");
StephaneLenclud@152:
StephaneLenclud@152: //Go through each drives on our system and collected the optical ones in our list
StephaneLenclud@152: DriveInfo[] allDrives = DriveInfo.GetDrives();
StephaneLenclud@152: foreach (DriveInfo d in allDrives)
StephaneLenclud@152: {
StephaneLenclud@157: Debug.WriteLine("Drive " + d.Name);
StephaneLenclud@152: Debug.WriteLine(" Drive type: {0}", d.DriveType);
StephaneLenclud@152:
StephaneLenclud@223: if (d.DriveType == DriveType.CDRom)
StephaneLenclud@152: {
StephaneLenclud@152: //This is an optical drive, add it now
StephaneLenclud@223: comboBoxOpticalDrives.Items.Add(d.Name.Substring(0, 2));
StephaneLenclud@223: }
StephaneLenclud@223: }
StephaneLenclud@152: }
StephaneLenclud@152:
StephaneLenclud@152: ///
StephaneLenclud@152: ///
StephaneLenclud@152: ///
StephaneLenclud@152: ///
StephaneLenclud@152: public string OpticalDriveToEject()
StephaneLenclud@152: {
StephaneLenclud@153: return comboBoxOpticalDrives.SelectedItem.ToString();
StephaneLenclud@152: }
StephaneLenclud@152:
StephaneLenclud@152:
StephaneLenclud@152:
StephaneLenclud@223: ///
StephaneLenclud@223: ///
StephaneLenclud@223: ///
StephaneLenclud@223: private void SetupTrayIcon()
StephaneLenclud@223: {
StephaneLenclud@223: iNotifyIcon.Icon = GetIcon("vfd.ico");
StephaneLenclud@223: iNotifyIcon.Text = "Sharp Display Manager";
StephaneLenclud@223: iNotifyIcon.Visible = true;
StephaneLenclud@223:
StephaneLenclud@223: //Double click toggles visibility - typically brings up the application
StephaneLenclud@223: iNotifyIcon.DoubleClick += delegate(object obj, EventArgs args)
StephaneLenclud@223: {
StephaneLenclud@223: SysTrayHideShow();
StephaneLenclud@223: };
StephaneLenclud@223:
StephaneLenclud@223: //Adding a context menu, useful to be able to exit the application
StephaneLenclud@223: ContextMenu contextMenu = new ContextMenu();
StephaneLenclud@223: //Context menu item to toggle visibility
StephaneLenclud@223: MenuItem hideShowItem = new MenuItem("Hide/Show");
StephaneLenclud@223: hideShowItem.Click += delegate(object obj, EventArgs args)
StephaneLenclud@223: {
StephaneLenclud@223: SysTrayHideShow();
StephaneLenclud@223: };
StephaneLenclud@223: contextMenu.MenuItems.Add(hideShowItem);
StephaneLenclud@223:
StephaneLenclud@223: //Context menu item separator
StephaneLenclud@223: contextMenu.MenuItems.Add(new MenuItem("-"));
StephaneLenclud@223:
StephaneLenclud@223: //Context menu exit item
StephaneLenclud@223: MenuItem exitItem = new MenuItem("Exit");
StephaneLenclud@223: exitItem.Click += delegate(object obj, EventArgs args)
StephaneLenclud@223: {
StephaneLenclud@223: Application.Exit();
StephaneLenclud@223: };
StephaneLenclud@223: contextMenu.MenuItems.Add(exitItem);
StephaneLenclud@223:
StephaneLenclud@223: iNotifyIcon.ContextMenu = contextMenu;
StephaneLenclud@223: }
sl@95:
StephaneLenclud@178: ///
StephaneLenclud@179: ///
StephaneLenclud@179: ///
StephaneLenclud@179: private void SetupRecordingNotification()
StephaneLenclud@179: {
StephaneLenclud@179: iRecordingNotification.Icon = GetIcon("record.ico");
StephaneLenclud@179: iRecordingNotification.Text = "No recording";
StephaneLenclud@180: iRecordingNotification.Visible = false;
StephaneLenclud@179: }
StephaneLenclud@179:
StephaneLenclud@179: ///
StephaneLenclud@178: /// Access icons from embedded resources.
StephaneLenclud@178: ///
StephaneLenclud@178: ///
StephaneLenclud@178: ///
StephaneLenclud@178: public static Icon GetIcon(string aName)
StephaneLenclud@223: {
StephaneLenclud@223: string[] names = Assembly.GetExecutingAssembly().GetManifestResourceNames();
StephaneLenclud@223: foreach (string name in names)
StephaneLenclud@223: {
StephaneLenclud@178: //Find a resource name that ends with the given name
StephaneLenclud@223: if (name.EndsWith(aName))
StephaneLenclud@223: {
StephaneLenclud@223: using (Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(name))
StephaneLenclud@223: {
StephaneLenclud@223: return new Icon(stream);
StephaneLenclud@223: }
StephaneLenclud@223: }
StephaneLenclud@223: }
StephaneLenclud@223:
StephaneLenclud@223: return null;
StephaneLenclud@223: }
sl@94:
sl@94:
sl@65: ///
sl@65: /// Set our current client.
sl@65: /// This will take care of applying our client layout and set data fields.
sl@65: ///
sl@65: ///
StephaneLenclud@223: void SetCurrentClient(string aSessionId, bool aForce = false)
sl@57: {
sl@65: if (aSessionId == iCurrentClientSessionId)
sl@57: {
sl@65: //Given client is already the current one.
sl@65: //Don't bother changing anything then.
sl@65: return;
sl@65: }
sl@57:
StephaneLenclud@185: ClientData requestedClientData = iClients[aSessionId];
StephaneLenclud@141:
StephaneLenclud@141: //Check when was the last time we switched to that client
StephaneLenclud@142: if (iCurrentClientData != null)
StephaneLenclud@141: {
StephaneLenclud@185: //Do not switch client if priority of current client is higher
StephaneLenclud@185: if (!aForce && requestedClientData.Priority < iCurrentClientData.Priority)
StephaneLenclud@185: {
StephaneLenclud@185: return;
StephaneLenclud@185: }
StephaneLenclud@185:
StephaneLenclud@185:
StephaneLenclud@142: double lastSwitchToClientSecondsAgo = (DateTime.Now - iCurrentClientData.LastSwitchTime).TotalSeconds;
StephaneLenclud@142: //TODO: put that hard coded value as a client property
StephaneLenclud@142: //Clients should be able to define how often they can be interrupted
StephaneLenclud@142: //Thus a background client can set this to zero allowing any other client to interrupt at any time
StephaneLenclud@142: //We could also compute this delay by looking at the requests frequencies?
StephaneLenclud@185: if (!aForce &&
StephaneLenclud@223: requestedClientData.Priority == iCurrentClientData.Priority &&
StephaneLenclud@223: //Time sharing is only if clients have the same priority
StephaneLenclud@185: (lastSwitchToClientSecondsAgo < 30)) //Make sure a client is on for at least 30 seconds
StephaneLenclud@142: {
StephaneLenclud@142: //Don't switch clients too often
StephaneLenclud@142: return;
StephaneLenclud@142: }
StephaneLenclud@141: }
StephaneLenclud@141:
sl@65: //Set current client ID.
sl@65: iCurrentClientSessionId = aSessionId;
StephaneLenclud@141: //Set the time we last switched to that client
StephaneLenclud@141: iClients[aSessionId].LastSwitchTime = DateTime.Now;
sl@65: //Fetch and set current client data.
StephaneLenclud@185: iCurrentClientData = requestedClientData;
sl@65: //Apply layout and set data fields.
sl@65: UpdateTableLayoutPanel(iCurrentClientData);
sl@57: }
sl@57:
sl@0: private void buttonFont_Click(object sender, EventArgs e)
sl@0: {
sl@0: //fontDialog.ShowColor = true;
sl@0: //fontDialog.ShowApply = true;
sl@0: fontDialog.ShowEffects = true;
sl@99: fontDialog.Font = cds.Font;
sl@28:
sl@28: fontDialog.FixedPitchOnly = checkBoxFixedPitchFontOnly.Checked;
sl@28:
sl@0: //fontDialog.ShowHelp = true;
sl@0:
sl@0: //fontDlg.MaxSize = 40;
sl@0: //fontDlg.MinSize = 22;
sl@0:
sl@0: //fontDialog.Parent = this;
sl@0: //fontDialog.StartPosition = FormStartPosition.CenterParent;
sl@0:
sl@0: //DlgBox.ShowDialog(fontDialog);
sl@0:
sl@0: //if (fontDialog.ShowDialog(this) != DialogResult.Cancel)
sl@0: if (DlgBox.ShowDialog(fontDialog) != DialogResult.Cancel)
sl@0: {
sl@99: //Set the fonts to all our labels in our layout
StephaneLenclud@175: foreach (Control ctrl in iTableLayoutPanel.Controls)
sl@99: {
sl@99: if (ctrl is MarqueeLabel)
sl@99: {
StephaneLenclud@223: ((MarqueeLabel) ctrl).Font = fontDialog.Font;
sl@99: }
sl@99: }
sl@0:
sl@99: //Save font settings
sl@48: cds.Font = fontDialog.Font;
sl@8: Properties.Settings.Default.Save();
sl@36: //
sl@37: CheckFontHeight();
sl@37: }
sl@37: }
sl@36:
sl@37: ///
sl@38: ///
sl@37: ///
sl@37: void CheckFontHeight()
sl@37: {
sl@54: //Show font height and width
sl@54: labelFontHeight.Text = "Font height: " + cds.Font.Height;
sl@54: float charWidth = IsFixedWidth(cds.Font);
sl@54: if (charWidth == 0.0f)
sl@54: {
sl@54: labelFontWidth.Visible = false;
sl@54: }
sl@54: else
sl@54: {
sl@54: labelFontWidth.Visible = true;
sl@54: labelFontWidth.Text = "Font width: " + charWidth;
sl@54: }
sl@54:
sl@70: MarqueeLabel label = null;
sl@68: //Get the first label control we can find
StephaneLenclud@175: foreach (Control ctrl in iTableLayoutPanel.Controls)
sl@68: {
sl@68: if (ctrl is MarqueeLabel)
sl@68: {
StephaneLenclud@223: label = (MarqueeLabel) ctrl;
sl@68: break;
sl@68: }
sl@68: }
sl@68:
sl@54: //Now check font height and show a warning if needed.
sl@68: if (label != null && label.Font.Height > label.Height)
sl@37: {
StephaneLenclud@223: labelWarning.Text = "WARNING: Selected font is too height by " + (label.Font.Height - label.Height) +
StephaneLenclud@223: " pixels!";
sl@37: labelWarning.Visible = true;
sl@0: }
sl@37: else
sl@37: {
sl@37: labelWarning.Visible = false;
sl@37: }
sl@37:
sl@0: }
sl@0:
sl@0: private void buttonCapture_Click(object sender, EventArgs e)
sl@0: {
StephaneLenclud@175: System.Drawing.Bitmap bmp = new System.Drawing.Bitmap(iTableLayoutPanel.Width, iTableLayoutPanel.Height);
StephaneLenclud@175: iTableLayoutPanel.DrawToBitmap(bmp, iTableLayoutPanel.ClientRectangle);
sl@14: //Bitmap bmpToSave = new Bitmap(bmp);
sl@14: bmp.Save("D:\\capture.png");
sl@14:
StephaneLenclud@223: ((MarqueeLabel) iTableLayoutPanel.Controls[0]).Text = "Captured";
sl@17:
sl@14: /*
sl@14: string outputFileName = "d:\\capture.png";
sl@14: using (MemoryStream memory = new MemoryStream())
sl@14: {
sl@14: using (FileStream fs = new FileStream(outputFileName, FileMode.OpenOrCreate, FileAccess.ReadWrite))
sl@14: {
sl@14: bmp.Save(memory, System.Drawing.Imaging.ImageFormat.Png);
sl@14: byte[] bytes = memory.ToArray();
sl@14: fs.Write(bytes, 0, bytes.Length);
sl@14: }
sl@14: }
sl@14: */
sl@14:
sl@0: }
sl@2:
sl@12: private void CheckForRequestResults()
sl@12: {
sl@12: if (iDisplay.IsRequestPending())
sl@12: {
sl@12: switch (iDisplay.AttemptRequestCompletion())
sl@12: {
StephaneLenclud@135: case MiniDisplay.Request.FirmwareRevision:
sl@51: toolStripStatusLabelConnect.Text += " v" + iDisplay.FirmwareRevision();
sl@51: //Issue next request then
sl@51: iDisplay.RequestPowerSupplyStatus();
sl@51: break;
sl@51:
StephaneLenclud@135: case MiniDisplay.Request.PowerSupplyStatus:
sl@12: if (iDisplay.PowerSupplyStatus())
sl@12: {
sl@12: toolStripStatusLabelPower.Text = "ON";
sl@12: }
sl@12: else
sl@12: {
sl@12: toolStripStatusLabelPower.Text = "OFF";
sl@12: }
sl@12: //Issue next request then
sl@12: iDisplay.RequestDeviceId();
sl@12: break;
sl@12:
StephaneLenclud@135: case MiniDisplay.Request.DeviceId:
sl@12: toolStripStatusLabelConnect.Text += " - " + iDisplay.DeviceId();
sl@12: //No more request to issue
sl@12: break;
sl@12: }
sl@12: }
sl@12: }
sl@12:
sl@58: public static uint ColorWhiteIsOn(int aX, int aY, uint aPixel)
sl@58: {
sl@58: if ((aPixel & 0x00FFFFFF) == 0x00FFFFFF)
sl@58: {
sl@58: return 0xFFFFFFFF;
sl@58: }
sl@58: return 0x00000000;
sl@58: }
sl@16:
sl@58: public static uint ColorUntouched(int aX, int aY, uint aPixel)
sl@57: {
sl@57: return aPixel;
sl@57: }
sl@57:
sl@58: public static uint ColorInversed(int aX, int aY, uint aPixel)
sl@57: {
sl@57: return ~aPixel;
sl@57: }
sl@57:
sl@58: public static uint ColorChessboard(int aX, int aY, uint aPixel)
sl@58: {
StephaneLenclud@223: if ((aX%2 == 0) && (aY%2 == 0))
sl@58: {
sl@58: return ~aPixel;
sl@58: }
StephaneLenclud@223: else if ((aX%2 != 0) && (aY%2 != 0))
sl@58: {
sl@58: return ~aPixel;
sl@58: }
sl@58: return 0x00000000;
sl@58: }
sl@58:
sl@16:
sl@16: public static int ScreenReversedX(System.Drawing.Bitmap aBmp, int aX)
sl@16: {
sl@16: return aBmp.Width - aX - 1;
sl@16: }
sl@16:
sl@16: public int ScreenReversedY(System.Drawing.Bitmap aBmp, int aY)
sl@16: {
sl@16: return iBmp.Height - aY - 1;
sl@16: }
sl@16:
sl@16: public int ScreenX(System.Drawing.Bitmap aBmp, int aX)
sl@16: {
sl@16: return aX;
sl@16: }
sl@16:
sl@16: public int ScreenY(System.Drawing.Bitmap aBmp, int aY)
sl@16: {
sl@16: return aY;
sl@16: }
sl@16:
sl@58: ///
sl@58: /// Select proper pixel delegates according to our current settings.
sl@58: ///
sl@58: private void SetupPixelDelegates()
sl@58: {
sl@59: //Select our pixel processing routine
sl@58: if (cds.InverseColors)
sl@58: {
sl@58: //iColorFx = ColorChessboard;
sl@58: iColorFx = ColorInversed;
sl@58: }
sl@58: else
sl@58: {
sl@58: iColorFx = ColorWhiteIsOn;
sl@58: }
sl@58:
sl@58: //Select proper coordinate translation functions
sl@58: //We used delegate/function pointer to support reverse screen without doing an extra test on each pixels
sl@58: if (cds.ReverseScreen)
sl@58: {
sl@58: iScreenX = ScreenReversedX;
sl@58: iScreenY = ScreenReversedY;
sl@58: }
sl@58: else
sl@58: {
sl@58: iScreenX = ScreenX;
sl@58: iScreenY = ScreenY;
sl@58: }
sl@58:
sl@58: }
sl@16:
sl@16: //This is our timer tick responsible to perform our render
sl@2: private void timer_Tick(object sender, EventArgs e)
sl@14: {
StephaneLenclud@206: //Not ideal cause this has nothing to do with display render
StephaneLenclud@206: LogsUpdate();
StephaneLenclud@206:
sl@2: //Update our animations
sl@2: DateTime NewTickTime = DateTime.Now;
sl@2:
StephaneLenclud@223: UpdateNetworkSignal(LastTickTime, NewTickTime);
StephaneLenclud@118:
sl@60: //Update animation for all our marquees
StephaneLenclud@175: foreach (Control ctrl in iTableLayoutPanel.Controls)
sl@60: {
sl@68: if (ctrl is MarqueeLabel)
sl@68: {
StephaneLenclud@223: ((MarqueeLabel) ctrl).UpdateAnimation(LastTickTime, NewTickTime);
sl@68: }
sl@60: }
sl@60:
sl@4: //Update our display
sl@4: if (iDisplay.IsOpen())
sl@4: {
sl@12: CheckForRequestResults();
sl@12:
StephaneLenclud@223: //Check if frame rendering is needed
StephaneLenclud@223: //Typically used when showing clock
StephaneLenclud@223: if (!iSkipFrameRendering)
StephaneLenclud@223: {
StephaneLenclud@223: //Draw to bitmap
StephaneLenclud@223: if (iCreateBitmap)
StephaneLenclud@223: {
StephaneLenclud@223: iBmp = new System.Drawing.Bitmap(iTableLayoutPanel.Width, iTableLayoutPanel.Height,
StephaneLenclud@223: PixelFormat.Format32bppArgb);
StephaneLenclud@158: iCreateBitmap = false;
StephaneLenclud@158: }
StephaneLenclud@223: iTableLayoutPanel.DrawToBitmap(iBmp, iTableLayoutPanel.ClientRectangle);
StephaneLenclud@223: //iBmp.Save("D:\\capture.png");
StephaneLenclud@223:
StephaneLenclud@223: //Send it to our display
StephaneLenclud@223: for (int i = 0; i < iBmp.Width; i++)
StephaneLenclud@223: {
StephaneLenclud@223: for (int j = 0; j < iBmp.Height; j++)
StephaneLenclud@223: {
StephaneLenclud@223: unchecked
StephaneLenclud@223: {
StephaneLenclud@223: //Get our processed pixel coordinates
StephaneLenclud@223: int x = iScreenX(iBmp, i);
StephaneLenclud@223: int y = iScreenY(iBmp, j);
StephaneLenclud@223: //Get pixel color
StephaneLenclud@223: uint color = (uint) iBmp.GetPixel(i, j).ToArgb();
StephaneLenclud@223: //Apply color effects
StephaneLenclud@223: color = iColorFx(x, y, color);
StephaneLenclud@223: //Now set our pixel
StephaneLenclud@223: iDisplay.SetPixel(x, y, color);
StephaneLenclud@223: }
StephaneLenclud@223: }
StephaneLenclud@223: }
StephaneLenclud@223:
StephaneLenclud@223: iDisplay.SwapBuffers();
StephaneLenclud@223: }
sl@4: }
sl@8:
sl@8: //Compute instant FPS
StephaneLenclud@223: toolStripStatusLabelFps.Text = (1.0/NewTickTime.Subtract(LastTickTime).TotalSeconds).ToString("F0") + " / " +
StephaneLenclud@223: (1000/timer.Interval).ToString() + " FPS";
sl@8:
sl@8: LastTickTime = NewTickTime;
sl@8:
sl@2: }
sl@3:
StephaneLenclud@223: ///
StephaneLenclud@223: /// Attempt to establish connection with our display hardware.
StephaneLenclud@223: ///
sl@46: private void OpenDisplayConnection()
sl@3: {
sl@46: CloseDisplayConnection();
sl@46:
StephaneLenclud@223: if (!iDisplay.Open((MiniDisplay.Type) cds.DisplayType))
StephaneLenclud@223: {
StephaneLenclud@223: UpdateStatus();
StephaneLenclud@223: toolStripStatusLabelConnect.Text = "Connection error";
sl@7: }
sl@46: }
sl@7:
sl@46: private void CloseDisplayConnection()
sl@46: {
StephaneLenclud@223: //Status will be updated upon receiving the closed event
StephaneLenclud@223:
StephaneLenclud@223: if (iDisplay == null || !iDisplay.IsOpen())
StephaneLenclud@223: {
StephaneLenclud@223: return;
StephaneLenclud@223: }
StephaneLenclud@223:
StephaneLenclud@223: //Do not clear if we gave up on rendering already.
StephaneLenclud@223: //This means we will keep on displaying clock on MDM166AA for instance.
StephaneLenclud@223: if (!iSkipFrameRendering)
StephaneLenclud@223: {
StephaneLenclud@223: iDisplay.Clear();
StephaneLenclud@223: iDisplay.SwapBuffers();
StephaneLenclud@223: }
StephaneLenclud@223:
StephaneLenclud@223: iDisplay.SetAllIconsStatus(0); //Turn off all icons
sl@46: iDisplay.Close();
sl@46: }
sl@46:
sl@46: private void buttonOpen_Click(object sender, EventArgs e)
sl@46: {
sl@46: OpenDisplayConnection();
sl@3: }
sl@3:
sl@3: private void buttonClose_Click(object sender, EventArgs e)
sl@3: {
sl@46: CloseDisplayConnection();
sl@3: }
sl@3:
sl@3: private void buttonClear_Click(object sender, EventArgs e)
sl@3: {
sl@3: iDisplay.Clear();
sl@3: iDisplay.SwapBuffers();
sl@3: }
sl@3:
sl@3: private void buttonFill_Click(object sender, EventArgs e)
sl@3: {
sl@3: iDisplay.Fill();
sl@3: iDisplay.SwapBuffers();
sl@3: }
sl@3:
sl@3: private void trackBarBrightness_Scroll(object sender, EventArgs e)
sl@3: {
sl@48: cds.Brightness = trackBarBrightness.Value;
sl@9: Properties.Settings.Default.Save();
sl@3: iDisplay.SetBrightness(trackBarBrightness.Value);
sl@9:
sl@3: }
sl@7:
sl@48:
sl@48: ///
sl@48: /// CDS stands for Current Display Settings
sl@48: ///
sl@50: private DisplaySettings cds
sl@48: {
sl@48: get
sl@48: {
sl@65: DisplaysSettings settings = Properties.Settings.Default.DisplaysSettings;
sl@51: if (settings == null)
sl@51: {
sl@51: settings = new DisplaysSettings();
sl@51: settings.Init();
sl@65: Properties.Settings.Default.DisplaysSettings = settings;
sl@51: }
sl@48:
sl@48: //Make sure all our settings have been created
sl@48: while (settings.Displays.Count <= Properties.Settings.Default.CurrentDisplayIndex)
sl@48: {
sl@50: settings.Displays.Add(new DisplaySettings());
sl@48: }
sl@48:
sl@50: DisplaySettings displaySettings = settings.Displays[Properties.Settings.Default.CurrentDisplayIndex];
sl@48: return displaySettings;
sl@48: }
sl@48: }
sl@48:
sl@54: ///
sl@54: /// Check if the given font has a fixed character pitch.
sl@54: ///
sl@54: ///
sl@54: /// 0.0f if this is not a monospace font, otherwise returns the character width.
sl@54: public float IsFixedWidth(Font ft)
sl@54: {
sl@54: Graphics g = CreateGraphics();
StephaneLenclud@223: char[] charSizes = new char[] {'i', 'a', 'Z', '%', '#', 'a', 'B', 'l', 'm', ',', '.'};
sl@54: float charWidth = g.MeasureString("I", ft, Int32.MaxValue, StringFormat.GenericTypographic).Width;
sl@54:
sl@54: bool fixedWidth = true;
sl@54:
sl@54: foreach (char c in charSizes)
StephaneLenclud@223: if (g.MeasureString(c.ToString(), ft, Int32.MaxValue, StringFormat.GenericTypographic).Width !=
StephaneLenclud@223: charWidth)
sl@54: fixedWidth = false;
sl@54:
sl@54: if (fixedWidth)
sl@54: {
sl@54: return charWidth;
sl@54: }
sl@54:
sl@54: return 0.0f;
sl@54: }
sl@54:
StephaneLenclud@223: ///
StephaneLenclud@223: /// Synchronize UI with settings
StephaneLenclud@223: ///
sl@7: private void UpdateStatus()
StephaneLenclud@223: {
sl@48: //Load settings
sl@54: checkBoxShowBorders.Checked = cds.ShowBorders;
StephaneLenclud@223: iTableLayoutPanel.CellBorderStyle = (cds.ShowBorders
StephaneLenclud@223: ? TableLayoutPanelCellBorderStyle.Single
StephaneLenclud@223: : TableLayoutPanelCellBorderStyle.None);
sl@60:
sl@60: //Set the proper font to each of our labels
StephaneLenclud@175: foreach (MarqueeLabel ctrl in iTableLayoutPanel.Controls)
sl@60: {
sl@60: ctrl.Font = cds.Font;
sl@60: }
sl@60:
sl@54: CheckFontHeight();
StephaneLenclud@223: //Check if "run on Windows startup" is enabled
StephaneLenclud@223: checkBoxAutoStart.Checked = iStartupManager.Startup;
StephaneLenclud@235: //
StephaneLenclud@194: labelStartFileName.Text = Properties.Settings.Default.StartFileName;
StephaneLenclud@194:
StephaneLenclud@153:
StephaneLenclud@153: //Try find our drive in our drive list
StephaneLenclud@223: int opticalDriveItemIndex = 0;
StephaneLenclud@153: bool driveNotFound = true;
StephaneLenclud@223: string opticalDriveToEject = Properties.Settings.Default.OpticalDriveToEject;
StephaneLenclud@153: foreach (object item in comboBoxOpticalDrives.Items)
StephaneLenclud@153: {
StephaneLenclud@154: if (opticalDriveToEject == item.ToString())
StephaneLenclud@153: {
StephaneLenclud@153: comboBoxOpticalDrives.SelectedIndex = opticalDriveItemIndex;
StephaneLenclud@153: driveNotFound = false;
StephaneLenclud@153: break;
StephaneLenclud@153: }
StephaneLenclud@153: opticalDriveItemIndex++;
StephaneLenclud@153: }
StephaneLenclud@153:
StephaneLenclud@153: if (driveNotFound)
StephaneLenclud@153: {
StephaneLenclud@153: //We could not find the drive we had saved.
StephaneLenclud@153: //Select "None" then.
StephaneLenclud@153: comboBoxOpticalDrives.SelectedIndex = 0;
StephaneLenclud@153: }
StephaneLenclud@153:
StephaneLenclud@233: //Harmony settings
StephaneLenclud@233: iTextBoxHarmonyHubAddress.Text = Properties.Settings.Default.HarmonyHubAddress;
StephaneLenclud@233:
StephaneLenclud@168: //CEC settings
StephaneLenclud@168: comboBoxHdmiPort.SelectedIndex = Properties.Settings.Default.CecHdmiPort - 1;
StephaneLenclud@153:
StephaneLenclud@168: //Mini Display settings
sl@48: checkBoxReverseScreen.Checked = cds.ReverseScreen;
sl@57: checkBoxInverseColors.Checked = cds.InverseColors;
StephaneLenclud@223: checkBoxShowVolumeLabel.Checked = cds.ShowVolumeLabel;
sl@100: checkBoxScaleToFit.Checked = cds.ScaleToFit;
sl@100: maskedTextBoxMinFontSize.Enabled = cds.ScaleToFit;
sl@100: labelMinFontSize.Enabled = cds.ScaleToFit;
sl@100: maskedTextBoxMinFontSize.Text = cds.MinFontSize.ToString();
StephaneLenclud@223: maskedTextBoxScrollingSpeed.Text = cds.ScrollingSpeedInPixelsPerSecond.ToString();
sl@48: comboBoxDisplayType.SelectedIndex = cds.DisplayType;
sl@48: timer.Interval = cds.TimerInterval;
sl@48: maskedTextBoxTimerInterval.Text = cds.TimerInterval.ToString();
sl@100: textBoxScrollLoopSeparator.Text = cds.Separator;
sl@58: //
sl@58: SetupPixelDelegates();
sl@48:
sl@7: if (iDisplay.IsOpen())
sl@7: {
StephaneLenclud@187: //We have a display connection
StephaneLenclud@187: //Reflect that in our UI
StephaneLenclud@187: StartTimer();
StephaneLenclud@103:
StephaneLenclud@223: iTableLayoutPanel.Enabled = true;
StephaneLenclud@223: panelDisplay.Enabled = true;
StephaneLenclud@103:
sl@48: //Only setup brightness if display is open
sl@48: trackBarBrightness.Minimum = iDisplay.MinBrightness();
sl@48: trackBarBrightness.Maximum = iDisplay.MaxBrightness();
StephaneLenclud@223: if (cds.Brightness < iDisplay.MinBrightness() || cds.Brightness > iDisplay.MaxBrightness())
StephaneLenclud@223: {
StephaneLenclud@223: //Brightness out of range, this can occur when using auto-detect
StephaneLenclud@223: //Use max brightness instead
StephaneLenclud@223: trackBarBrightness.Value = iDisplay.MaxBrightness();
StephaneLenclud@223: iDisplay.SetBrightness(iDisplay.MaxBrightness());
StephaneLenclud@223: }
StephaneLenclud@223: else
StephaneLenclud@223: {
StephaneLenclud@223: trackBarBrightness.Value = cds.Brightness;
StephaneLenclud@223: iDisplay.SetBrightness(cds.Brightness);
StephaneLenclud@223: }
StephaneLenclud@223:
StephaneLenclud@223: //Try compute the steps to something that makes sense
StephaneLenclud@223: trackBarBrightness.LargeChange = Math.Max(1, (iDisplay.MaxBrightness() - iDisplay.MinBrightness())/5);
sl@48: trackBarBrightness.SmallChange = 1;
StephaneLenclud@223:
sl@48: //
sl@7: buttonFill.Enabled = true;
sl@7: buttonClear.Enabled = true;
sl@7: buttonOpen.Enabled = false;
sl@7: buttonClose.Enabled = true;
sl@7: trackBarBrightness.Enabled = true;
sl@10: toolStripStatusLabelConnect.Text = "Connected - " + iDisplay.Vendor() + " - " + iDisplay.Product();
sl@10: //+ " - " + iDisplay.SerialNumber();
sl@52:
sl@52: if (iDisplay.SupportPowerOnOff())
sl@52: {
sl@52: buttonPowerOn.Enabled = true;
sl@52: buttonPowerOff.Enabled = true;
sl@52: }
sl@52: else
sl@52: {
sl@52: buttonPowerOn.Enabled = false;
sl@52: buttonPowerOff.Enabled = false;
sl@52: }
sl@53:
sl@53: if (iDisplay.SupportClock())
sl@53: {
sl@53: buttonShowClock.Enabled = true;
sl@53: buttonHideClock.Enabled = true;
sl@53: }
sl@53: else
sl@53: {
sl@53: buttonShowClock.Enabled = false;
sl@53: buttonHideClock.Enabled = false;
sl@53: }
StephaneLenclud@115:
StephaneLenclud@223:
StephaneLenclud@223: //Check if Volume Label is supported. To date only MDM166AA supports that crap :)
StephaneLenclud@223: checkBoxShowVolumeLabel.Enabled = iDisplay.IconCount(MiniDisplay.IconType.VolumeLabel) > 0;
StephaneLenclud@223:
StephaneLenclud@223: if (cds.ShowVolumeLabel)
StephaneLenclud@223: {
StephaneLenclud@135: iDisplay.SetIconOn(MiniDisplay.IconType.VolumeLabel);
StephaneLenclud@223: }
StephaneLenclud@223: else
StephaneLenclud@223: {
StephaneLenclud@135: iDisplay.SetIconOff(MiniDisplay.IconType.VolumeLabel);
StephaneLenclud@223: }
sl@7: }
sl@7: else
sl@7: {
StephaneLenclud@187: //Display connection not available
StephaneLenclud@187: //Reflect that in our UI
StephaneLenclud@187: #if DEBUG
StephaneLenclud@187: //In debug start our timer even if we don't have a display connection
StephaneLenclud@187: StartTimer();
StephaneLenclud@187: #else
StephaneLenclud@223: //In production environment we don't need our timer if no display connection
StephaneLenclud@187: StopTimer();
StephaneLenclud@187: #endif
StephaneLenclud@187: checkBoxShowVolumeLabel.Enabled = false;
StephaneLenclud@223: iTableLayoutPanel.Enabled = false;
StephaneLenclud@223: panelDisplay.Enabled = false;
sl@7: buttonFill.Enabled = false;
sl@7: buttonClear.Enabled = false;
sl@7: buttonOpen.Enabled = true;
sl@7: buttonClose.Enabled = false;
sl@7: trackBarBrightness.Enabled = false;
sl@52: buttonPowerOn.Enabled = false;
sl@52: buttonPowerOff.Enabled = false;
sl@53: buttonShowClock.Enabled = false;
sl@53: buttonHideClock.Enabled = false;
sl@9: toolStripStatusLabelConnect.Text = "Disconnected";
sl@48: toolStripStatusLabelPower.Text = "N/A";
sl@7: }
StephaneLenclud@106:
sl@7: }
sl@9:
sl@13:
StephaneLenclud@223: ///
StephaneLenclud@223: ///
StephaneLenclud@223: ///
StephaneLenclud@223: ///
StephaneLenclud@223: ///
StephaneLenclud@223: private void checkBoxShowVolumeLabel_CheckedChanged(object sender, EventArgs e)
StephaneLenclud@223: {
StephaneLenclud@223: cds.ShowVolumeLabel = checkBoxShowVolumeLabel.Checked;
StephaneLenclud@223: Properties.Settings.Default.Save();
StephaneLenclud@223: UpdateStatus();
StephaneLenclud@223: }
sl@13:
sl@9: private void checkBoxShowBorders_CheckedChanged(object sender, EventArgs e)
sl@9: {
sl@16: //Save our show borders setting
StephaneLenclud@223: iTableLayoutPanel.CellBorderStyle = (checkBoxShowBorders.Checked
StephaneLenclud@223: ? TableLayoutPanelCellBorderStyle.Single
StephaneLenclud@223: : TableLayoutPanelCellBorderStyle.None);
sl@48: cds.ShowBorders = checkBoxShowBorders.Checked;
sl@9: Properties.Settings.Default.Save();
sl@57: CheckFontHeight();
sl@9: }
sl@13:
StephaneLenclud@194: private void checkBoxAutoStart_CheckedChanged(object sender, EventArgs e)
StephaneLenclud@223: {
StephaneLenclud@223: iStartupManager.Startup = checkBoxAutoStart.Checked;
StephaneLenclud@223: }
sl@94:
sl@94:
sl@16: private void checkBoxReverseScreen_CheckedChanged(object sender, EventArgs e)
sl@16: {
sl@16: //Save our reverse screen setting
sl@48: cds.ReverseScreen = checkBoxReverseScreen.Checked;
sl@16: Properties.Settings.Default.Save();
sl@58: SetupPixelDelegates();
sl@16: }
sl@16:
sl@57: private void checkBoxInverseColors_CheckedChanged(object sender, EventArgs e)
sl@57: {
sl@57: //Save our inverse colors setting
sl@57: cds.InverseColors = checkBoxInverseColors.Checked;
sl@57: Properties.Settings.Default.Save();
sl@58: SetupPixelDelegates();
sl@57: }
sl@57:
sl@100: private void checkBoxScaleToFit_CheckedChanged(object sender, EventArgs e)
sl@100: {
sl@100: //Save our scale to fit setting
sl@100: cds.ScaleToFit = checkBoxScaleToFit.Checked;
sl@100: Properties.Settings.Default.Save();
sl@100: //
sl@100: labelMinFontSize.Enabled = cds.ScaleToFit;
sl@100: maskedTextBoxMinFontSize.Enabled = cds.ScaleToFit;
sl@100: }
sl@100:
sl@14: private void MainForm_Resize(object sender, EventArgs e)
sl@14: {
sl@14: if (WindowState == FormWindowState.Minimized)
sl@14: {
StephaneLenclud@158: // To workaround our empty bitmap bug on Windows 7 we need to recreate our bitmap when the application is minimized
StephaneLenclud@158: // That's apparently not needed on Windows 10 but we better leave it in place.
sl@14: iCreateBitmap = true;
sl@14: }
sl@14: }
sl@14:
sl@17: private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
sl@17: {
StephaneLenclud@167: iCecManager.Stop();
StephaneLenclud@223: iNetworkManager.Dispose();
StephaneLenclud@223: CloseDisplayConnection();
sl@17: StopServer();
sl@32: e.Cancel = iClosing;
sl@17: }
sl@17:
sl@17: public void StartServer()
sl@17: {
sl@17: iServiceHost = new ServiceHost
sl@17: (
StephaneLenclud@223: typeof(Session),
StephaneLenclud@223: new Uri[] {new Uri("net.tcp://localhost:8001/")}
sl@17: );
sl@17:
StephaneLenclud@223: iServiceHost.AddServiceEndpoint(typeof(IService), new NetTcpBinding(SecurityMode.None, true),
StephaneLenclud@223: "DisplayService");
sl@17: iServiceHost.Open();
sl@17: }
sl@17:
sl@17: public void StopServer()
sl@17: {
sl@32: if (iClients.Count > 0 && !iClosing)
sl@29: {
sl@29: //Tell our clients
sl@32: iClosing = true;
sl@29: BroadcastCloseEvent();
sl@29: }
sl@32: else if (iClosing)
sl@32: {
StephaneLenclud@223: if (
StephaneLenclud@223: MessageBox.Show("Force exit?", "Waiting for clients...", MessageBoxButtons.YesNo,
StephaneLenclud@223: MessageBoxIcon.Warning) == DialogResult.Yes)
sl@32: {
sl@32: iClosing = false; //We make sure we force close if asked twice
sl@32: }
sl@32: }
sl@32: else
sl@36: {
sl@32: //We removed that as it often lags for some reason
sl@32: //iServiceHost.Close();
sl@32: }
sl@17: }
sl@17:
sl@21: public void BroadcastCloseEvent()
sl@21: {
sl@31: Trace.TraceInformation("BroadcastCloseEvent - start");
sl@31:
sl@21: var inactiveClients = new List();
sl@21: foreach (var client in iClients)
sl@21: {
sl@21: //if (client.Key != eventData.ClientName)
sl@21: {
sl@21: try
sl@21: {
sl@31: Trace.TraceInformation("BroadcastCloseEvent - " + client.Key);
StephaneLenclud@223: client.Value.Callback.OnCloseOrder( /*eventData*/);
sl@21: }
sl@21: catch (Exception ex)
sl@21: {
sl@21: inactiveClients.Add(client.Key);
sl@21: }
sl@21: }
sl@21: }
sl@21:
sl@21: if (inactiveClients.Count > 0)
sl@21: {
sl@21: foreach (var client in inactiveClients)
sl@21: {
sl@21: iClients.Remove(client);
StephaneLenclud@226: Program.iFormMain.iTreeViewClients.Nodes.Remove(
StephaneLenclud@226: Program.iFormMain.iTreeViewClients.Nodes.Find(client, false)[0]);
sl@21: }
sl@21: }
sl@97:
StephaneLenclud@223: if (iClients.Count == 0)
StephaneLenclud@223: {
StephaneLenclud@223: ClearLayout();
StephaneLenclud@223: }
sl@21: }
sl@21:
StephaneLenclud@223: ///
StephaneLenclud@223: /// Just remove all our fields.
StephaneLenclud@223: ///
StephaneLenclud@223: private void ClearLayout()
StephaneLenclud@223: {
StephaneLenclud@223: iTableLayoutPanel.Controls.Clear();
StephaneLenclud@223: iTableLayoutPanel.RowStyles.Clear();
StephaneLenclud@223: iTableLayoutPanel.ColumnStyles.Clear();
StephaneLenclud@223: iCurrentClientData = null;
StephaneLenclud@223: }
StephaneLenclud@223:
StephaneLenclud@223: ///
StephaneLenclud@223: /// Just launch a demo client.
StephaneLenclud@223: ///
StephaneLenclud@223: private void StartNewClient(string aTopText = "", string aBottomText = "")
StephaneLenclud@223: {
StephaneLenclud@223: Thread clientThread = new Thread(SharpDisplayClient.Program.MainWithParams);
StephaneLenclud@223: SharpDisplayClient.StartParams myParams = new SharpDisplayClient.StartParams(
StephaneLenclud@223: new Point(this.Right, this.Top), aTopText, aBottomText);
StephaneLenclud@223: clientThread.Start(myParams);
StephaneLenclud@223: BringToFront();
StephaneLenclud@223: }
StephaneLenclud@106:
StephaneLenclud@189: ///
StephaneLenclud@189: /// Just launch our idle client.
StephaneLenclud@189: ///
StephaneLenclud@189: private void StartIdleClient(string aTopText = "", string aBottomText = "")
StephaneLenclud@189: {
StephaneLenclud@225: Thread clientThread = new Thread(SharpDisplayClientIdle.Program.MainWithParams);
StephaneLenclud@225: SharpDisplayClientIdle.StartParams myParams =
StephaneLenclud@225: new SharpDisplayClientIdle.StartParams(new Point(this.Right, this.Top), aTopText, aBottomText);
StephaneLenclud@189: clientThread.Start(myParams);
StephaneLenclud@189: BringToFront();
StephaneLenclud@189: }
StephaneLenclud@189:
StephaneLenclud@189:
sl@25: private void buttonStartClient_Click(object sender, EventArgs e)
sl@25: {
StephaneLenclud@223: StartNewClient();
sl@25: }
sl@25:
sl@27: private void buttonSuspend_Click(object sender, EventArgs e)
sl@27: {
StephaneLenclud@187: ToggleTimer();
StephaneLenclud@187: }
StephaneLenclud@187:
StephaneLenclud@187: private void StartTimer()
StephaneLenclud@187: {
StephaneLenclud@187: LastTickTime = DateTime.Now; //Reset timer to prevent jump
StephaneLenclud@187: timer.Enabled = true;
StephaneLenclud@187: UpdateSuspendButton();
StephaneLenclud@187: }
StephaneLenclud@187:
StephaneLenclud@187: private void StopTimer()
StephaneLenclud@187: {
StephaneLenclud@187: LastTickTime = DateTime.Now; //Reset timer to prevent jump
StephaneLenclud@187: timer.Enabled = false;
StephaneLenclud@187: UpdateSuspendButton();
StephaneLenclud@187: }
StephaneLenclud@187:
StephaneLenclud@187: private void ToggleTimer()
StephaneLenclud@187: {
sl@52: LastTickTime = DateTime.Now; //Reset timer to prevent jump
sl@27: timer.Enabled = !timer.Enabled;
StephaneLenclud@187: UpdateSuspendButton();
StephaneLenclud@187: }
StephaneLenclud@187:
StephaneLenclud@187: private void UpdateSuspendButton()
StephaneLenclud@187: {
sl@27: if (!timer.Enabled)
sl@27: {
sl@52: buttonSuspend.Text = "Run";
sl@27: }
sl@27: else
sl@27: {
sl@27: buttonSuspend.Text = "Pause";
sl@27: }
sl@27: }
sl@27:
StephaneLenclud@187:
sl@29: private void buttonCloseClients_Click(object sender, EventArgs e)
sl@29: {
sl@29: BroadcastCloseEvent();
sl@29: }
sl@29:
sl@30: private void treeViewClients_AfterSelect(object sender, TreeViewEventArgs e)
sl@30: {
StephaneLenclud@141: //Root node must have at least one child
StephaneLenclud@141: if (e.Node.Nodes.Count == 0)
StephaneLenclud@141: {
StephaneLenclud@141: return;
StephaneLenclud@141: }
sl@21:
StephaneLenclud@141: //If the selected node is the root node of a client then switch to it
StephaneLenclud@223: string sessionId = e.Node.Nodes[0].Text; //First child of a root node is the sessionId
StephaneLenclud@141: if (iClients.ContainsKey(sessionId)) //Check that's actually what we are looking at
StephaneLenclud@141: {
StephaneLenclud@141: //We have a valid session just switch to that client
StephaneLenclud@223: SetCurrentClient(sessionId, true);
StephaneLenclud@141: }
StephaneLenclud@223:
sl@30: }
sl@30:
sl@36:
sl@30: ///
sl@36: ///
sl@30: ///
sl@30: ///
sl@30: ///
sl@55: public void AddClientThreadSafe(string aSessionId, ICallback aCallback)
sl@30: {
sl@33: if (this.InvokeRequired)
sl@30: {
sl@30: //Not in the proper thread, invoke ourselves
sl@30: AddClientDelegate d = new AddClientDelegate(AddClientThreadSafe);
StephaneLenclud@223: this.Invoke(d, new object[] {aSessionId, aCallback});
sl@30: }
sl@30: else
sl@30: {
sl@30: //We are in the proper thread
sl@30: //Add this session to our collection of clients
sl@33: ClientData newClient = new ClientData(aSessionId, aCallback);
StephaneLenclud@226: Program.iFormMain.iClients.Add(aSessionId, newClient);
sl@30: //Add this session to our UI
sl@33: UpdateClientTreeViewNode(newClient);
sl@30: }
sl@30: }
sl@30:
Stephane@186:
Stephane@186: ///
Stephane@186: /// Find the client with the highest priority if any.
Stephane@186: ///
Stephane@186: /// Our highest priority client or null if not a single client is connected.
Stephane@186: public ClientData FindHighestPriorityClient()
Stephane@186: {
Stephane@186: ClientData highestPriorityClient = null;
Stephane@186: foreach (var client in iClients)
Stephane@186: {
Stephane@186: if (highestPriorityClient == null || client.Value.Priority > highestPriorityClient.Priority)
Stephane@186: {
Stephane@186: highestPriorityClient = client.Value;
Stephane@186: }
Stephane@186: }
Stephane@186:
Stephane@186: return highestPriorityClient;
Stephane@186: }
Stephane@186:
sl@30: ///
sl@36: ///
sl@30: ///
sl@30: ///
sl@30: public void RemoveClientThreadSafe(string aSessionId)
sl@30: {
sl@33: if (this.InvokeRequired)
sl@30: {
sl@30: //Not in the proper thread, invoke ourselves
sl@30: RemoveClientDelegate d = new RemoveClientDelegate(RemoveClientThreadSafe);
StephaneLenclud@223: this.Invoke(d, new object[] {aSessionId});
sl@30: }
sl@30: else
sl@30: {
sl@30: //We are in the proper thread
sl@33: //Remove this session from both client collection and UI tree view
StephaneLenclud@226: if (Program.iFormMain.iClients.Keys.Contains(aSessionId))
sl@30: {
StephaneLenclud@226: Program.iFormMain.iClients.Remove(aSessionId);
StephaneLenclud@226: Program.iFormMain.iTreeViewClients.Nodes.Remove(
StephaneLenclud@226: Program.iFormMain.iTreeViewClients.Nodes.Find(aSessionId, false)[0]);
StephaneLenclud@188: //Update recording status too whenever a client is removed
StephaneLenclud@188: UpdateRecordingNotification();
sl@32: }
sl@32:
Stephane@186: if (iCurrentClientSessionId == aSessionId)
Stephane@186: {
Stephane@186: //The current client is closing
Stephane@186: iCurrentClientData = null;
Stephane@186: //Find the client with the highest priority and set it as current
Stephane@186: ClientData newCurrentClient = FindHighestPriorityClient();
StephaneLenclud@223: if (newCurrentClient != null)
Stephane@186: {
Stephane@186: SetCurrentClient(newCurrentClient.SessionId, true);
StephaneLenclud@223: }
Stephane@186: }
Stephane@186:
Stephane@186: if (iClients.Count == 0)
StephaneLenclud@223: {
StephaneLenclud@223: //Clear our screen when last client disconnects
StephaneLenclud@223: ClearLayout();
StephaneLenclud@223:
StephaneLenclud@223: if (iClosing)
StephaneLenclud@223: {
StephaneLenclud@223: //We were closing our form
StephaneLenclud@223: //All clients are now closed
StephaneLenclud@223: //Just resume our close operation
StephaneLenclud@223: iClosing = false;
StephaneLenclud@223: Close();
StephaneLenclud@223: }
StephaneLenclud@223: }
sl@30: }
sl@30: }
sl@30:
sl@30: ///
sl@36: ///
sl@30: ///
sl@62: ///
sl@72: ///
sl@62: public void SetClientLayoutThreadSafe(string aSessionId, TableLayout aLayout)
sl@62: {
sl@62: if (this.InvokeRequired)
sl@62: {
sl@62: //Not in the proper thread, invoke ourselves
sl@62: SetLayoutDelegate d = new SetLayoutDelegate(SetClientLayoutThreadSafe);
StephaneLenclud@223: this.Invoke(d, new object[] {aSessionId, aLayout});
sl@62: }
sl@62: else
sl@62: {
sl@62: ClientData client = iClients[aSessionId];
sl@62: if (client != null)
sl@62: {
StephaneLenclud@148: //Don't change a thing if the layout is the same
StephaneLenclud@148: if (!client.Layout.IsSameAs(aLayout))
StephaneLenclud@148: {
StephaneLenclud@158: Debug.Print("SetClientLayoutThreadSafe: Layout updated.");
StephaneLenclud@148: //Set our client layout then
StephaneLenclud@148: client.Layout = aLayout;
StephaneLenclud@175: //So that next time we update all our fields at ones
StephaneLenclud@175: client.HasNewLayout = true;
StephaneLenclud@158: //Layout has changed clear our fields then
StephaneLenclud@158: client.Fields.Clear();
StephaneLenclud@148: //
StephaneLenclud@148: UpdateClientTreeViewNode(client);
StephaneLenclud@148: }
StephaneLenclud@158: else
StephaneLenclud@158: {
StephaneLenclud@158: Debug.Print("SetClientLayoutThreadSafe: Layout has not changed.");
StephaneLenclud@158: }
sl@62: }
sl@62: }
sl@62: }
sl@62:
sl@62: ///
sl@62: ///
sl@62: ///
sl@67: ///
sl@72: ///
sl@75: public void SetClientFieldThreadSafe(string aSessionId, DataField aField)
sl@30: {
sl@33: if (this.InvokeRequired)
sl@30: {
sl@30: //Not in the proper thread, invoke ourselves
sl@79: SetFieldDelegate d = new SetFieldDelegate(SetClientFieldThreadSafe);
StephaneLenclud@223: this.Invoke(d, new object[] {aSessionId, aField});
sl@30: }
sl@30: else
sl@30: {
sl@75: //We are in the proper thread
sl@75: //Call the non-thread-safe variant
sl@75: SetClientField(aSessionId, aField);
sl@75: }
sl@75: }
sl@75:
StephaneLenclud@176:
StephaneLenclud@176:
StephaneLenclud@176:
sl@75: ///
StephaneLenclud@175: /// Set a data field in the given client.
sl@75: ///
sl@75: ///
sl@75: ///
sl@75: private void SetClientField(string aSessionId, DataField aField)
StephaneLenclud@223: {
StephaneLenclud@141: //TODO: should check if the field actually changed?
StephaneLenclud@141:
sl@75: ClientData client = iClients[aSessionId];
StephaneLenclud@141: bool layoutChanged = false;
StephaneLenclud@148: bool contentChanged = true;
StephaneLenclud@141:
StephaneLenclud@176: //Fetch our field index
StephaneLenclud@176: int fieldIndex = client.FindSameFieldIndex(aField);
StephaneLenclud@176:
StephaneLenclud@176: if (fieldIndex < 0)
sl@75: {
StephaneLenclud@176: //No corresponding field, just bail out
StephaneLenclud@176: return;
StephaneLenclud@141: }
sl@76:
StephaneLenclud@175: //Keep our previous field in there
StephaneLenclud@176: DataField previousField = client.Fields[fieldIndex];
StephaneLenclud@176: //Just update that field then
StephaneLenclud@176: client.Fields[fieldIndex] = aField;
StephaneLenclud@148:
StephaneLenclud@176: if (!aField.IsTableField)
StephaneLenclud@176: {
StephaneLenclud@176: //We are done then if that field is not in our table layout
StephaneLenclud@176: return;
StephaneLenclud@176: }
StephaneLenclud@176:
StephaneLenclud@176: TableField tableField = (TableField) aField;
StephaneLenclud@172:
StephaneLenclud@175: if (previousField.IsSameLayout(aField))
StephaneLenclud@141: {
StephaneLenclud@141: //If we are updating a field in our current client we need to update it in our panel
StephaneLenclud@141: if (aSessionId == iCurrentClientSessionId)
sl@30: {
StephaneLenclud@223: Control ctrl = iTableLayoutPanel.GetControlFromPosition(tableField.Column, tableField.Row);
StephaneLenclud@176: if (aField.IsTextField && ctrl is MarqueeLabel)
sl@79: {
StephaneLenclud@223: TextField textField = (TextField) aField;
sl@75: //Text field control already in place, just change the text
StephaneLenclud@223: MarqueeLabel label = (MarqueeLabel) ctrl;
StephaneLenclud@172: contentChanged = (label.Text != textField.Text || label.TextAlign != textField.Alignment);
StephaneLenclud@172: label.Text = textField.Text;
StephaneLenclud@172: label.TextAlign = textField.Alignment;
sl@68: }
StephaneLenclud@176: else if (aField.IsBitmapField && ctrl is PictureBox)
sl@75: {
StephaneLenclud@223: BitmapField bitmapField = (BitmapField) aField;
StephaneLenclud@148: contentChanged = true; //TODO: Bitmap comp or should we leave that to clients?
sl@75: //Bitmap field control already in place just change the bitmap
StephaneLenclud@223: PictureBox pictureBox = (PictureBox) ctrl;
StephaneLenclud@172: pictureBox.Image = bitmapField.Bitmap;
sl@75: }
sl@68: else
sl@68: {
StephaneLenclud@141: layoutChanged = true;
sl@68: }
sl@30: }
StephaneLenclud@141: }
StephaneLenclud@141: else
StephaneLenclud@223: {
StephaneLenclud@141: layoutChanged = true;
StephaneLenclud@141: }
StephaneLenclud@141:
StephaneLenclud@148: //If either content or layout changed we need to update our tree view to reflect the changes
StephaneLenclud@148: if (contentChanged || layoutChanged)
StephaneLenclud@141: {
StephaneLenclud@141: UpdateClientTreeViewNode(client);
StephaneLenclud@148: //
StephaneLenclud@148: if (layoutChanged)
sl@75: {
StephaneLenclud@148: Debug.Print("Layout changed");
StephaneLenclud@148: //Our layout has changed, if we are already the current client we need to update our panel
StephaneLenclud@148: if (aSessionId == iCurrentClientSessionId)
StephaneLenclud@148: {
StephaneLenclud@148: //Apply layout and set data fields.
StephaneLenclud@148: UpdateTableLayoutPanel(iCurrentClientData);
StephaneLenclud@148: }
sl@75: }
StephaneLenclud@158: else
StephaneLenclud@158: {
StephaneLenclud@158: Debug.Print("Layout has not changed.");
StephaneLenclud@158: }
StephaneLenclud@158: }
StephaneLenclud@158: else
StephaneLenclud@158: {
StephaneLenclud@158: Debug.Print("WARNING: content and layout have not changed!");
StephaneLenclud@141: }
sl@75:
StephaneLenclud@141: //When a client field is set we try switching to this client to present the new information to our user
StephaneLenclud@141: SetCurrentClient(aSessionId);
sl@30: }
sl@30:
sl@30: ///
sl@36: ///
sl@30: ///
sl@30: ///
sl@75: public void SetClientFieldsThreadSafe(string aSessionId, System.Collections.Generic.IList aFields)
sl@30: {
sl@33: if (this.InvokeRequired)
sl@30: {
sl@30: //Not in the proper thread, invoke ourselves
sl@75: SetFieldsDelegate d = new SetFieldsDelegate(SetClientFieldsThreadSafe);
StephaneLenclud@223: this.Invoke(d, new object[] {aSessionId, aFields});
sl@30: }
sl@30: else
sl@30: {
StephaneLenclud@175: ClientData client = iClients[aSessionId];
StephaneLenclud@175:
StephaneLenclud@175: if (client.HasNewLayout)
sl@30: {
StephaneLenclud@175: //TODO: Assert client.Count == 0
StephaneLenclud@175: //Our layout was just changed
StephaneLenclud@175: //Do some special handling to avoid re-creating our panel N times, once for each fields
StephaneLenclud@175: client.HasNewLayout = false;
StephaneLenclud@175: //Just set all our fields then
StephaneLenclud@175: client.Fields.AddRange(aFields);
StephaneLenclud@175: //Try switch to that client
StephaneLenclud@175: SetCurrentClient(aSessionId);
StephaneLenclud@175:
StephaneLenclud@175: //If we are updating the current client update our panel
StephaneLenclud@175: if (aSessionId == iCurrentClientSessionId)
StephaneLenclud@175: {
StephaneLenclud@175: //Apply layout and set data fields.
StephaneLenclud@175: UpdateTableLayoutPanel(iCurrentClientData);
StephaneLenclud@175: }
StephaneLenclud@176:
StephaneLenclud@176: UpdateClientTreeViewNode(client);
StephaneLenclud@175: }
StephaneLenclud@175: else
StephaneLenclud@175: {
StephaneLenclud@175: //Put each our text fields in a label control
StephaneLenclud@175: foreach (DataField field in aFields)
StephaneLenclud@175: {
StephaneLenclud@175: SetClientField(aSessionId, field);
StephaneLenclud@175: }
sl@30: }
sl@30: }
sl@32: }
sl@30:
sl@67: ///
sl@67: ///
sl@67: ///
sl@67: ///
sl@32: ///
sl@32: public void SetClientNameThreadSafe(string aSessionId, string aName)
sl@32: {
sl@32: if (this.InvokeRequired)
sl@32: {
sl@32: //Not in the proper thread, invoke ourselves
sl@32: SetClientNameDelegate d = new SetClientNameDelegate(SetClientNameThreadSafe);
StephaneLenclud@223: this.Invoke(d, new object[] {aSessionId, aName});
sl@32: }
sl@32: else
sl@32: {
sl@32: //We are in the proper thread
sl@33: //Get our client
sl@33: ClientData client = iClients[aSessionId];
sl@33: if (client != null)
sl@32: {
sl@33: //Set its name
sl@33: client.Name = aName;
sl@33: //Update our tree-view
sl@33: UpdateClientTreeViewNode(client);
sl@33: }
sl@33: }
sl@33: }
sl@33:
StephaneLenclud@184: ///
StephaneLenclud@184: public void SetClientPriorityThreadSafe(string aSessionId, uint aPriority)
StephaneLenclud@184: {
StephaneLenclud@184: if (this.InvokeRequired)
StephaneLenclud@184: {
StephaneLenclud@184: //Not in the proper thread, invoke ourselves
StephaneLenclud@184: SetClientPriorityDelegate d = new SetClientPriorityDelegate(SetClientPriorityThreadSafe);
StephaneLenclud@223: this.Invoke(d, new object[] {aSessionId, aPriority});
StephaneLenclud@184: }
StephaneLenclud@184: else
StephaneLenclud@184: {
StephaneLenclud@184: //We are in the proper thread
StephaneLenclud@184: //Get our client
StephaneLenclud@184: ClientData client = iClients[aSessionId];
StephaneLenclud@184: if (client != null)
StephaneLenclud@184: {
StephaneLenclud@184: //Set its name
StephaneLenclud@184: client.Priority = aPriority;
StephaneLenclud@184: //Update our tree-view
StephaneLenclud@184: UpdateClientTreeViewNode(client);
Stephane@186: //Change our current client as per new priority
Stephane@186: ClientData newCurrentClient = FindHighestPriorityClient();
StephaneLenclud@223: if (newCurrentClient != null)
Stephane@186: {
Stephane@186: SetCurrentClient(newCurrentClient.SessionId);
Stephane@186: }
StephaneLenclud@184: }
StephaneLenclud@184: }
StephaneLenclud@184: }
StephaneLenclud@184:
sl@33: ///
StephaneLenclud@183: ///
StephaneLenclud@183: ///
StephaneLenclud@183: ///
StephaneLenclud@183: ///
StephaneLenclud@183: ///
StephaneLenclud@183: public static string Truncate(string value, int maxChars)
StephaneLenclud@183: {
StephaneLenclud@223: return value.Length <= maxChars ? value : value.Substring(0, maxChars - 3) + "...";
StephaneLenclud@183: }
StephaneLenclud@183:
StephaneLenclud@183: ///
StephaneLenclud@180: /// Update our recording notification.
StephaneLenclud@180: ///
StephaneLenclud@180: private void UpdateRecordingNotification()
StephaneLenclud@180: {
StephaneLenclud@180: //Go through each
StephaneLenclud@180: bool activeRecording = false;
StephaneLenclud@223: string text = "";
StephaneLenclud@223: RecordingField recField = new RecordingField();
StephaneLenclud@180: foreach (var client in iClients)
StephaneLenclud@180: {
StephaneLenclud@223: RecordingField rec = (RecordingField) client.Value.FindSameFieldAs(recField);
StephaneLenclud@223: if (rec != null && rec.IsActive)
StephaneLenclud@180: {
StephaneLenclud@180: activeRecording = true;
StephaneLenclud@183: //Don't break cause we are collecting the names/texts.
StephaneLenclud@183: if (!String.IsNullOrEmpty(rec.Text))
StephaneLenclud@183: {
StephaneLenclud@183: text += (rec.Text + "\n");
StephaneLenclud@183: }
StephaneLenclud@183: else
StephaneLenclud@183: {
StephaneLenclud@183: //Not text for that recording, use client name instead
StephaneLenclud@183: text += client.Value.Name + " recording\n";
StephaneLenclud@183: }
StephaneLenclud@223:
StephaneLenclud@180: }
StephaneLenclud@180: }
StephaneLenclud@180:
StephaneLenclud@183: //Update our text no matter what, can't have more than 63 characters otherwise it throws an exception.
StephaneLenclud@223: iRecordingNotification.Text = Truncate(text, 63);
StephaneLenclud@183:
StephaneLenclud@180: //Change visibility of notification if needed
StephaneLenclud@180: if (iRecordingNotification.Visible != activeRecording)
StephaneLenclud@223: {
StephaneLenclud@180: iRecordingNotification.Visible = activeRecording;
Stephane@182: //Assuming the notification icon is in sync with our display icon
Stephane@182: //Take care of our REC icon
StephaneLenclud@183: if (iDisplay.IsOpen())
StephaneLenclud@183: {
StephaneLenclud@183: iDisplay.SetIconOnOff(MiniDisplay.IconType.Recording, activeRecording);
StephaneLenclud@223: }
StephaneLenclud@180: }
StephaneLenclud@180: }
StephaneLenclud@180:
StephaneLenclud@180: ///
sl@36: ///
sl@33: ///
sl@33: ///
sl@33: private void UpdateClientTreeViewNode(ClientData aClient)
sl@33: {
StephaneLenclud@148: Debug.Print("UpdateClientTreeViewNode");
StephaneLenclud@148:
sl@33: if (aClient == null)
sl@33: {
sl@33: return;
sl@33: }
sl@33:
StephaneLenclud@180: //Hook in record icon update too
StephaneLenclud@180: UpdateRecordingNotification();
StephaneLenclud@180:
sl@33: TreeNode node = null;
sl@33: //Check that our client node already exists
sl@33: //Get our client root node using its key which is our session ID
StephaneLenclud@188: TreeNode[] nodes = iTreeViewClients.Nodes.Find(aClient.SessionId, false);
StephaneLenclud@223: if (nodes.Count() > 0)
sl@33: {
sl@33: //We already have a node for that client
sl@33: node = nodes[0];
sl@33: //Clear children as we are going to recreate them below
sl@33: node.Nodes.Clear();
sl@33: }
sl@33: else
sl@33: {
sl@33: //Client node does not exists create a new one
StephaneLenclud@188: iTreeViewClients.Nodes.Add(aClient.SessionId, aClient.SessionId);
StephaneLenclud@188: node = iTreeViewClients.Nodes.Find(aClient.SessionId, false)[0];
sl@33: }
sl@33:
sl@33: if (node != null)
sl@33: {
sl@33: //Change its name
StephaneLenclud@184: if (!String.IsNullOrEmpty(aClient.Name))
sl@33: {
StephaneLenclud@184: //We have a name, use it as text for our root node
sl@33: node.Text = aClient.Name;
sl@32: //Add a child with SessionId
sl@33: node.Nodes.Add(new TreeNode(aClient.SessionId));
sl@33: }
sl@33: else
sl@33: {
sl@33: //No name, use session ID instead
sl@33: node.Text = aClient.SessionId;
sl@33: }
sl@36:
StephaneLenclud@184: //Display client priority
StephaneLenclud@184: node.Nodes.Add(new TreeNode("Priority: " + aClient.Priority));
StephaneLenclud@184:
sl@67: if (aClient.Fields.Count > 0)
sl@33: {
sl@33: //Create root node for our texts
sl@70: TreeNode textsRoot = new TreeNode("Fields");
sl@33: node.Nodes.Add(textsRoot);
sl@33: //For each text add a new entry
sl@67: foreach (DataField field in aClient.Fields)
sl@33: {
StephaneLenclud@172: if (field.IsTextField)
sl@67: {
StephaneLenclud@223: TextField textField = (TextField) field;
sl@70: textsRoot.Nodes.Add(new TreeNode("[Text]" + textField.Text));
sl@67: }
StephaneLenclud@172: else if (field.IsBitmapField)
sl@67: {
sl@72: textsRoot.Nodes.Add(new TreeNode("[Bitmap]"));
sl@70: }
StephaneLenclud@172: else if (field.IsRecordingField)
StephaneLenclud@172: {
StephaneLenclud@223: RecordingField recordingField = (RecordingField) field;
StephaneLenclud@177: textsRoot.Nodes.Add(new TreeNode("[Recording]" + recordingField.IsActive));
StephaneLenclud@172: }
sl@33: }
sl@32: }
sl@34:
sl@34: node.ExpandAll();
sl@32: }
sl@30: }
sl@17:
sl@60: ///
sl@60: /// Update our table layout row styles to make sure each rows have similar height
sl@60: ///
sl@60: private void UpdateTableLayoutRowStyles()
sl@60: {
StephaneLenclud@175: foreach (RowStyle rowStyle in iTableLayoutPanel.RowStyles)
sl@60: {
sl@60: rowStyle.SizeType = SizeType.Percent;
StephaneLenclud@223: rowStyle.Height = 100/iTableLayoutPanel.RowCount;
sl@60: }
sl@60: }
sl@60:
sl@63: ///
sl@63: /// Update our display table layout.
StephaneLenclud@175: /// Will instanciated every field control as defined by our client.
StephaneLenclud@175: /// Fields must be specified by rows from the left.
sl@63: ///
sl@63: ///
sl@65: private void UpdateTableLayoutPanel(ClientData aClient)
sl@63: {
StephaneLenclud@175: Debug.Print("UpdateTableLayoutPanel");
StephaneLenclud@148:
StephaneLenclud@223: if (aClient == null)
StephaneLenclud@223: {
StephaneLenclud@223: //Just drop it
StephaneLenclud@223: return;
StephaneLenclud@223: }
StephaneLenclud@106:
StephaneLenclud@106:
sl@65: TableLayout layout = aClient.Layout;
sl@70:
StephaneLenclud@141: //First clean our current panel
StephaneLenclud@175: iTableLayoutPanel.Controls.Clear();
StephaneLenclud@175: iTableLayoutPanel.RowStyles.Clear();
StephaneLenclud@175: iTableLayoutPanel.ColumnStyles.Clear();
StephaneLenclud@175: iTableLayoutPanel.RowCount = 0;
StephaneLenclud@175: iTableLayoutPanel.ColumnCount = 0;
sl@63:
StephaneLenclud@175: //Then recreate our rows...
StephaneLenclud@175: while (iTableLayoutPanel.RowCount < layout.Rows.Count)
sl@63: {
StephaneLenclud@175: iTableLayoutPanel.RowCount++;
sl@63: }
sl@63:
StephaneLenclud@175: // ...and columns
StephaneLenclud@175: while (iTableLayoutPanel.ColumnCount < layout.Columns.Count)
sl@63: {
StephaneLenclud@175: iTableLayoutPanel.ColumnCount++;
sl@63: }
sl@63:
StephaneLenclud@175: //For each column
StephaneLenclud@175: for (int i = 0; i < iTableLayoutPanel.ColumnCount; i++)
sl@63: {
sl@63: //Create our column styles
StephaneLenclud@175: this.iTableLayoutPanel.ColumnStyles.Add(layout.Columns[i]);
sl@63:
StephaneLenclud@175: //For each rows
StephaneLenclud@175: for (int j = 0; j < iTableLayoutPanel.RowCount; j++)
sl@63: {
sl@63: if (i == 0)
sl@63: {
sl@63: //Create our row styles
StephaneLenclud@175: this.iTableLayoutPanel.RowStyles.Add(layout.Rows[j]);
sl@63: }
StephaneLenclud@176: else
sl@70: {
sl@70: continue;
sl@70: }
sl@63: }
sl@63: }
sl@63:
StephaneLenclud@176: //For each field
StephaneLenclud@176: foreach (DataField field in aClient.Fields)
sl@70: {
StephaneLenclud@176: if (!field.IsTableField)
StephaneLenclud@176: {
StephaneLenclud@176: //That field is not taking part in our table layout skip it
StephaneLenclud@176: continue;
StephaneLenclud@176: }
StephaneLenclud@176:
StephaneLenclud@223: TableField tableField = (TableField) field;
StephaneLenclud@176:
StephaneLenclud@176: //Create a control corresponding to the field specified for that cell
StephaneLenclud@176: Control control = CreateControlForDataField(tableField);
StephaneLenclud@176:
StephaneLenclud@176: //Add newly created control to our table layout at the specified row and column
StephaneLenclud@176: iTableLayoutPanel.Controls.Add(control, tableField.Column, tableField.Row);
StephaneLenclud@176: //Make sure we specify column and row span for that new control
StephaneLenclud@176: iTableLayoutPanel.SetColumnSpan(control, tableField.ColumnSpan);
StephaneLenclud@176: iTableLayoutPanel.SetRowSpan(control, tableField.RowSpan);
sl@70: }
sl@70:
StephaneLenclud@176:
sl@63: CheckFontHeight();
sl@63: }
sl@63:
sl@68: ///
sl@70: /// Check our type of data field and create corresponding control
sl@68: ///
sl@68: ///
sl@69: private Control CreateControlForDataField(DataField aField)
sl@68: {
StephaneLenclud@223: Control control = null;
StephaneLenclud@172: if (aField.IsTextField)
sl@68: {
sl@68: MarqueeLabel label = new SharpDisplayManager.MarqueeLabel();
sl@68: label.AutoEllipsis = true;
sl@68: label.AutoSize = true;
sl@68: label.BackColor = System.Drawing.Color.Transparent;
sl@68: label.Dock = System.Windows.Forms.DockStyle.Fill;
sl@68: label.Location = new System.Drawing.Point(1, 1);
sl@68: label.Margin = new System.Windows.Forms.Padding(0);
StephaneLenclud@176: label.Name = "marqueeLabel" + aField;
sl@68: label.OwnTimer = false;
StephaneLenclud@106: label.PixelsPerSecond = cds.ScrollingSpeedInPixelsPerSecond;
sl@100: label.Separator = cds.Separator;
sl@100: label.MinFontSize = cds.MinFontSize;
sl@100: label.ScaleToFit = cds.ScaleToFit;
sl@68: //control.Size = new System.Drawing.Size(254, 30);
sl@68: //control.TabIndex = 2;
sl@68: label.Font = cds.Font;
sl@68:
StephaneLenclud@223: TextField field = (TextField) aField;
StephaneLenclud@172: label.TextAlign = field.Alignment;
sl@68: label.UseCompatibleTextRendering = true;
StephaneLenclud@172: label.Text = field.Text;
sl@68: //
sl@68: control = label;
sl@68: }
StephaneLenclud@172: else if (aField.IsBitmapField)
sl@68: {
sl@68: //Create picture box
sl@68: PictureBox picture = new PictureBox();
sl@68: picture.AutoSize = true;
sl@68: picture.BackColor = System.Drawing.Color.Transparent;
sl@68: picture.Dock = System.Windows.Forms.DockStyle.Fill;
sl@68: picture.Location = new System.Drawing.Point(1, 1);
sl@68: picture.Margin = new System.Windows.Forms.Padding(0);
sl@68: picture.Name = "pictureBox" + aField;
sl@68: //Set our image
StephaneLenclud@223: BitmapField field = (BitmapField) aField;
StephaneLenclud@172: picture.Image = field.Bitmap;
sl@68: //
sl@68: control = picture;
sl@68: }
StephaneLenclud@172: //TODO: Handle recording field?
sl@68:
sl@69: return control;
sl@68: }
sl@68:
StephaneLenclud@223: ///
StephaneLenclud@223: /// Called when the user selected a new display type.
StephaneLenclud@223: ///
StephaneLenclud@223: ///
StephaneLenclud@223: ///
sl@46: private void comboBoxDisplayType_SelectedIndexChanged(object sender, EventArgs e)
sl@46: {
StephaneLenclud@223: //Store the selected display type in our settings
sl@48: Properties.Settings.Default.CurrentDisplayIndex = comboBoxDisplayType.SelectedIndex;
sl@48: cds.DisplayType = comboBoxDisplayType.SelectedIndex;
sl@46: Properties.Settings.Default.Save();
StephaneLenclud@103:
StephaneLenclud@223: //Try re-opening the display connection if we were already connected.
StephaneLenclud@223: //Otherwise just update our status to reflect display type change.
sl@51: if (iDisplay.IsOpen())
sl@51: {
sl@51: OpenDisplayConnection();
sl@51: }
sl@51: else
sl@51: {
sl@51: UpdateStatus();
sl@51: }
sl@46: }
sl@46:
sl@47: private void maskedTextBoxTimerInterval_TextChanged(object sender, EventArgs e)
sl@47: {
sl@47: if (maskedTextBoxTimerInterval.Text != "")
sl@47: {
sl@51: int interval = Convert.ToInt32(maskedTextBoxTimerInterval.Text);
sl@51:
sl@51: if (interval > 0)
sl@51: {
sl@51: timer.Interval = interval;
sl@51: cds.TimerInterval = timer.Interval;
sl@51: Properties.Settings.Default.Save();
sl@51: }
sl@47: }
sl@47: }
sl@47:
sl@100: private void maskedTextBoxMinFontSize_TextChanged(object sender, EventArgs e)
sl@100: {
sl@100: if (maskedTextBoxMinFontSize.Text != "")
sl@100: {
sl@100: int minFontSize = Convert.ToInt32(maskedTextBoxMinFontSize.Text);
sl@100:
sl@100: if (minFontSize > 0)
sl@100: {
sl@100: cds.MinFontSize = minFontSize;
sl@100: Properties.Settings.Default.Save();
StephaneLenclud@223: //We need to recreate our layout for that change to take effect
StephaneLenclud@223: UpdateTableLayoutPanel(iCurrentClientData);
sl@100: }
sl@100: }
sl@100: }
sl@100:
StephaneLenclud@106:
StephaneLenclud@223: private void maskedTextBoxScrollingSpeed_TextChanged(object sender, EventArgs e)
StephaneLenclud@223: {
StephaneLenclud@223: if (maskedTextBoxScrollingSpeed.Text != "")
StephaneLenclud@223: {
StephaneLenclud@223: int scrollingSpeed = Convert.ToInt32(maskedTextBoxScrollingSpeed.Text);
StephaneLenclud@223:
StephaneLenclud@223: if (scrollingSpeed > 0)
StephaneLenclud@223: {
StephaneLenclud@223: cds.ScrollingSpeedInPixelsPerSecond = scrollingSpeed;
StephaneLenclud@223: Properties.Settings.Default.Save();
StephaneLenclud@223: //We need to recreate our layout for that change to take effect
StephaneLenclud@223: UpdateTableLayoutPanel(iCurrentClientData);
StephaneLenclud@223: }
StephaneLenclud@223: }
StephaneLenclud@223: }
StephaneLenclud@106:
sl@100: private void textBoxScrollLoopSeparator_TextChanged(object sender, EventArgs e)
sl@100: {
sl@100: cds.Separator = textBoxScrollLoopSeparator.Text;
sl@100: Properties.Settings.Default.Save();
StephaneLenclud@110:
StephaneLenclud@223: //Update our text fields
StephaneLenclud@223: foreach (MarqueeLabel ctrl in iTableLayoutPanel.Controls)
StephaneLenclud@223: {
StephaneLenclud@223: ctrl.Separator = cds.Separator;
StephaneLenclud@223: }
StephaneLenclud@110:
sl@100: }
sl@100:
sl@52: private void buttonPowerOn_Click(object sender, EventArgs e)
sl@52: {
sl@52: iDisplay.PowerOn();
sl@52: }
sl@52:
sl@52: private void buttonPowerOff_Click(object sender, EventArgs e)
sl@52: {
sl@52: iDisplay.PowerOff();
sl@52: }
sl@52:
sl@53: private void buttonShowClock_Click(object sender, EventArgs e)
sl@53: {
StephaneLenclud@223: ShowClock();
sl@53: }
sl@53:
sl@53: private void buttonHideClock_Click(object sender, EventArgs e)
sl@53: {
StephaneLenclud@223: HideClock();
sl@53: }
sl@88:
sl@88: private void buttonUpdate_Click(object sender, EventArgs e)
sl@88: {
sl@88: InstallUpdateSyncWithInfo();
sl@88: }
sl@88:
StephaneLenclud@223: ///
StephaneLenclud@223: ///
StephaneLenclud@223: ///
StephaneLenclud@223: void ShowClock()
StephaneLenclud@223: {
StephaneLenclud@223: if (!iDisplay.IsOpen())
StephaneLenclud@223: {
StephaneLenclud@223: return;
StephaneLenclud@223: }
StephaneLenclud@223:
StephaneLenclud@223: //Devices like MDM166AA don't support windowing and frame rendering must be stopped while showing our clock
StephaneLenclud@223: iSkipFrameRendering = true;
StephaneLenclud@223: //Clear our screen
StephaneLenclud@223: iDisplay.Clear();
StephaneLenclud@223: iDisplay.SwapBuffers();
StephaneLenclud@223: //Then show our clock
StephaneLenclud@223: iDisplay.ShowClock();
StephaneLenclud@223: }
StephaneLenclud@223:
StephaneLenclud@223: ///
StephaneLenclud@223: ///
StephaneLenclud@223: ///
StephaneLenclud@223: void HideClock()
StephaneLenclud@223: {
StephaneLenclud@223: if (!iDisplay.IsOpen())
StephaneLenclud@223: {
StephaneLenclud@223: return;
StephaneLenclud@223: }
StephaneLenclud@223:
StephaneLenclud@223: //Devices like MDM166AA don't support windowing and frame rendering must be stopped while showing our clock
StephaneLenclud@223: iSkipFrameRendering = false;
StephaneLenclud@223: iDisplay.HideClock();
StephaneLenclud@223: }
sl@88:
sl@88: private void InstallUpdateSyncWithInfo()
sl@88: {
sl@88: UpdateCheckInfo info = null;
sl@88:
sl@88: if (ApplicationDeployment.IsNetworkDeployed)
sl@88: {
sl@88: ApplicationDeployment ad = ApplicationDeployment.CurrentDeployment;
sl@88:
sl@88: try
sl@88: {
sl@88: info = ad.CheckForDetailedUpdate();
sl@88:
sl@88: }
sl@88: catch (DeploymentDownloadException dde)
sl@88: {
StephaneLenclud@223: MessageBox.Show(
StephaneLenclud@223: "The new version of the application cannot be downloaded at this time. \n\nPlease check your network connection, or try again later. Error: " +
StephaneLenclud@223: dde.Message);
sl@88: return;
sl@88: }
sl@88: catch (InvalidDeploymentException ide)
sl@88: {
StephaneLenclud@223: MessageBox.Show(
StephaneLenclud@223: "Cannot check for a new version of the application. The ClickOnce deployment is corrupt. Please redeploy the application and try again. Error: " +
StephaneLenclud@223: ide.Message);
sl@88: return;
sl@88: }
sl@88: catch (InvalidOperationException ioe)
sl@88: {
StephaneLenclud@223: MessageBox.Show(
StephaneLenclud@223: "This application cannot be updated. It is likely not a ClickOnce application. Error: " +
StephaneLenclud@223: ioe.Message);
sl@88: return;
sl@88: }
sl@88:
StephaneLenclud@223: if (info.UpdateAvailable)
StephaneLenclud@223: {
StephaneLenclud@223: Boolean doUpdate = true;
StephaneLenclud@223:
StephaneLenclud@223: if (!info.IsUpdateRequired)
StephaneLenclud@223: {
StephaneLenclud@223: DialogResult dr =
StephaneLenclud@223: MessageBox.Show("An update is available. Would you like to update the application now?",
StephaneLenclud@223: "Update Available", MessageBoxButtons.OKCancel);
StephaneLenclud@223: if (!(DialogResult.OK == dr))
StephaneLenclud@223: {
StephaneLenclud@223: doUpdate = false;
StephaneLenclud@223: }
StephaneLenclud@223: }
StephaneLenclud@223: else
StephaneLenclud@223: {
StephaneLenclud@223: // Display a message that the application MUST reboot. Display the minimum required version.
StephaneLenclud@223: MessageBox.Show("This application has detected a mandatory update from your current " +
StephaneLenclud@223: "version to version " + info.MinimumRequiredVersion.ToString() +
StephaneLenclud@223: ". The application will now install the update and restart.",
StephaneLenclud@223: "Update Available", MessageBoxButtons.OK,
StephaneLenclud@223: MessageBoxIcon.Information);
StephaneLenclud@223: }
StephaneLenclud@223:
StephaneLenclud@223: if (doUpdate)
StephaneLenclud@223: {
StephaneLenclud@223: try
StephaneLenclud@223: {
StephaneLenclud@223: ad.Update();
StephaneLenclud@223: MessageBox.Show("The application has been upgraded, and will now restart.");
StephaneLenclud@223: Application.Restart();
StephaneLenclud@223: }
StephaneLenclud@223: catch (DeploymentDownloadException dde)
StephaneLenclud@223: {
StephaneLenclud@223: MessageBox.Show(
StephaneLenclud@223: "Cannot install the latest version of the application. \n\nPlease check your network connection, or try again later. Error: " +
StephaneLenclud@223: dde);
StephaneLenclud@223: return;
StephaneLenclud@223: }
StephaneLenclud@223: }
StephaneLenclud@223: }
StephaneLenclud@223: else
StephaneLenclud@223: {
StephaneLenclud@223: MessageBox.Show("You are already running the latest version.", "Application up-to-date");
StephaneLenclud@223: }
sl@88: }
sl@88: }
sl@92:
sl@94:
StephaneLenclud@223: ///
StephaneLenclud@223: /// Used to
StephaneLenclud@223: ///
StephaneLenclud@223: private void SysTrayHideShow()
StephaneLenclud@223: {
StephaneLenclud@223: Visible = !Visible;
StephaneLenclud@223: if (Visible)
StephaneLenclud@223: {
StephaneLenclud@223: Activate();
StephaneLenclud@223: WindowState = FormWindowState.Normal;
StephaneLenclud@223: }
StephaneLenclud@223: }
StephaneLenclud@223:
StephaneLenclud@223: ///
StephaneLenclud@223: /// Use to handle minimize events.
StephaneLenclud@223: ///
StephaneLenclud@223: ///
StephaneLenclud@223: ///
StephaneLenclud@223: private void MainForm_SizeChanged(object sender, EventArgs e)
StephaneLenclud@223: {
StephaneLenclud@223: if (WindowState == FormWindowState.Minimized && Properties.Settings.Default.MinimizeToTray)
StephaneLenclud@223: {
StephaneLenclud@223: if (Visible)
StephaneLenclud@223: {
StephaneLenclud@223: SysTrayHideShow();
StephaneLenclud@223: }
StephaneLenclud@223: }
StephaneLenclud@223: }
StephaneLenclud@223:
StephaneLenclud@223: ///
StephaneLenclud@223: ///
StephaneLenclud@223: ///
StephaneLenclud@223: ///
StephaneLenclud@223: ///
StephaneLenclud@223: private void tableLayoutPanel_SizeChanged(object sender, EventArgs e)
StephaneLenclud@223: {
StephaneLenclud@223: //Our table layout size has changed which means our display size has changed.
StephaneLenclud@223: //We need to re-create our bitmap.
StephaneLenclud@223: iCreateBitmap = true;
StephaneLenclud@223: }
StephaneLenclud@223:
StephaneLenclud@223: ///
StephaneLenclud@223: ///
StephaneLenclud@223: ///
StephaneLenclud@223: ///
StephaneLenclud@223: ///
StephaneLenclud@223: private void buttonSelectFile_Click(object sender, EventArgs e)
StephaneLenclud@223: {
StephaneLenclud@223: //openFileDialog1.InitialDirectory = "c:\\";
StephaneLenclud@223: //openFileDialog.Filter = "EXE files (*.exe)|*.exe|All files (*.*)|*.*";
StephaneLenclud@223: //openFileDialog.FilterIndex = 1;
StephaneLenclud@223: openFileDialog.RestoreDirectory = true;
StephaneLenclud@223:
StephaneLenclud@223: if (DlgBox.ShowDialog(openFileDialog) == DialogResult.OK)
StephaneLenclud@223: {
StephaneLenclud@223: labelStartFileName.Text = openFileDialog.FileName;
StephaneLenclud@223: Properties.Settings.Default.StartFileName = openFileDialog.FileName;
StephaneLenclud@223: Properties.Settings.Default.Save();
StephaneLenclud@223: }
StephaneLenclud@223: }
StephaneLenclud@153:
StephaneLenclud@153: ///
StephaneLenclud@153: ///
StephaneLenclud@153: ///
StephaneLenclud@153: ///
StephaneLenclud@153: ///
StephaneLenclud@153: private void comboBoxOpticalDrives_SelectedIndexChanged(object sender, EventArgs e)
StephaneLenclud@153: {
StephaneLenclud@153: //Save the optical drive the user selected for ejection
StephaneLenclud@153: Properties.Settings.Default.OpticalDriveToEject = comboBoxOpticalDrives.SelectedItem.ToString();
StephaneLenclud@153: Properties.Settings.Default.Save();
StephaneLenclud@153: }
StephaneLenclud@167:
StephaneLenclud@206:
StephaneLenclud@206: ///
StephaneLenclud@206: ///
StephaneLenclud@206: ///
StephaneLenclud@206: private void LogsUpdate()
StephaneLenclud@206: {
StephaneLenclud@206: if (iWriter != null)
StephaneLenclud@206: {
StephaneLenclud@206: iWriter.Flush();
StephaneLenclud@206: }
StephaneLenclud@206:
StephaneLenclud@206: }
StephaneLenclud@206:
StephaneLenclud@167: ///
StephaneLenclud@167: /// Broadcast messages to subscribers.
StephaneLenclud@167: ///
StephaneLenclud@167: ///
StephaneLenclud@167: protected override void WndProc(ref Message aMessage)
StephaneLenclud@167: {
StephaneLenclud@206: LogsUpdate();
Stephane@202:
StephaneLenclud@223: if (OnWndProc != null)
StephaneLenclud@167: {
StephaneLenclud@167: OnWndProc(ref aMessage);
StephaneLenclud@167: }
StephaneLenclud@223:
StephaneLenclud@167: base.WndProc(ref aMessage);
StephaneLenclud@167: }
StephaneLenclud@168:
StephaneLenclud@168: private void checkBoxCecEnabled_CheckedChanged(object sender, EventArgs e)
StephaneLenclud@168: {
StephaneLenclud@168: //
StephaneLenclud@168: ResetCec();
StephaneLenclud@168: }
StephaneLenclud@168:
StephaneLenclud@168: private void comboBoxHdmiPort_SelectedIndexChanged(object sender, EventArgs e)
StephaneLenclud@168: {
StephaneLenclud@168: //Save CEC HDMI port
StephaneLenclud@168: Properties.Settings.Default.CecHdmiPort = Convert.ToByte(comboBoxHdmiPort.SelectedIndex);
StephaneLenclud@168: Properties.Settings.Default.CecHdmiPort++;
StephaneLenclud@168: Properties.Settings.Default.Save();
StephaneLenclud@168: //
StephaneLenclud@168: ResetCec();
StephaneLenclud@168: }
StephaneLenclud@168:
StephaneLenclud@168: ///
StephaneLenclud@168: ///
StephaneLenclud@168: ///
StephaneLenclud@168: private void ResetCec()
StephaneLenclud@168: {
StephaneLenclud@223: if (iCecManager == null)
StephaneLenclud@168: {
StephaneLenclud@168: //Thus skipping initial UI setup
StephaneLenclud@168: return;
StephaneLenclud@168: }
StephaneLenclud@168:
StephaneLenclud@168: iCecManager.Stop();
StephaneLenclud@168: //
StephaneLenclud@168: if (Properties.Settings.Default.CecEnabled)
StephaneLenclud@168: {
StephaneLenclud@168: iCecManager.Start(Handle, "CEC",
StephaneLenclud@223: Properties.Settings.Default.CecHdmiPort);
StephaneLenclud@206:
Stephane@207: SetupCecLogLevel();
Stephane@207: }
Stephane@207: }
StephaneLenclud@206:
Stephane@207: ///
Stephane@207: ///
Stephane@207: ///
StephaneLenclud@233: private async void ResetHarmony()
StephaneLenclud@233: {
StephaneLenclud@233: // ConnectAsync already if we have an existing session cookie
StephaneLenclud@233: if (Properties.Settings.Default.HarmonyEnabled && File.Exists("SessionToken"))
StephaneLenclud@233: {
StephaneLenclud@233:
StephaneLenclud@233: iButtonHarmonyConnect.Enabled = false;
StephaneLenclud@233: try
StephaneLenclud@233: {
StephaneLenclud@233: await ConnectHarmonyAsync();
StephaneLenclud@233: }
StephaneLenclud@233: finally
StephaneLenclud@233: {
StephaneLenclud@233: iButtonHarmonyConnect.Enabled = true;
StephaneLenclud@233: }
StephaneLenclud@233: }
StephaneLenclud@233: }
StephaneLenclud@233:
StephaneLenclud@233: ///
StephaneLenclud@233: ///
StephaneLenclud@233: ///
Stephane@207: private void SetupCecLogLevel()
Stephane@207: {
Stephane@207: //Setup log level
Stephane@207: iCecManager.Client.LogLevel = 0;
StephaneLenclud@206:
Stephane@207: if (checkBoxCecLogError.Checked)
StephaneLenclud@223: iCecManager.Client.LogLevel |= (int) CecLogLevel.Error;
StephaneLenclud@206:
Stephane@207: if (checkBoxCecLogWarning.Checked)
StephaneLenclud@223: iCecManager.Client.LogLevel |= (int) CecLogLevel.Warning;
StephaneLenclud@206:
Stephane@207: if (checkBoxCecLogNotice.Checked)
StephaneLenclud@223: iCecManager.Client.LogLevel |= (int) CecLogLevel.Notice;
StephaneLenclud@206:
Stephane@207: if (checkBoxCecLogTraffic.Checked)
StephaneLenclud@223: iCecManager.Client.LogLevel |= (int) CecLogLevel.Traffic;
StephaneLenclud@206:
Stephane@207: if (checkBoxCecLogDebug.Checked)
StephaneLenclud@223: iCecManager.Client.LogLevel |= (int) CecLogLevel.Debug;
StephaneLenclud@206:
Stephane@207: iCecManager.Client.FilterOutPollLogs = checkBoxCecLogNoPoll.Checked;
Stephane@207:
StephaneLenclud@168: }
StephaneLenclud@189:
StephaneLenclud@189: private void ButtonStartIdleClient_Click(object sender, EventArgs e)
StephaneLenclud@189: {
StephaneLenclud@189: StartIdleClient();
StephaneLenclud@189: }
StephaneLenclud@204:
StephaneLenclud@206: private void buttonClearLogs_Click(object sender, EventArgs e)
StephaneLenclud@206: {
StephaneLenclud@206: richTextBoxLogs.Clear();
StephaneLenclud@206: }
StephaneLenclud@204:
StephaneLenclud@206: private void checkBoxCecLogs_CheckedChanged(object sender, EventArgs e)
StephaneLenclud@206: {
Stephane@207: SetupCecLogLevel();
StephaneLenclud@206: }
StephaneLenclud@211:
StephaneLenclud@223:
StephaneLenclud@218: ///
StephaneLenclud@218: ///
StephaneLenclud@218: ///
StephaneLenclud@218: ///
StephaneLenclud@234: private void SelectEvent(Ear.Event aEvent)
StephaneLenclud@218: {
StephaneLenclud@218: if (aEvent == null)
StephaneLenclud@218: {
StephaneLenclud@218: return;
StephaneLenclud@218: }
StephaneLenclud@218:
StephaneLenclud@231: foreach (TreeNode node in iTreeViewEvents.Nodes)
StephaneLenclud@223: {
StephaneLenclud@231: if (node.Tag == aEvent)
StephaneLenclud@231: {
StephaneLenclud@231: iTreeViewEvents.SelectedNode = node;
StephaneLenclud@231: iTreeViewEvents.Focus();
StephaneLenclud@231: }
StephaneLenclud@218: }
StephaneLenclud@218: }
StephaneLenclud@218:
StephaneLenclud@219:
StephaneLenclud@219:
StephaneLenclud@217: ///
StephaneLenclud@219: /// Get the current event based on event tree view selection.
StephaneLenclud@217: ///
StephaneLenclud@217: ///
StephaneLenclud@234: private Ear.Event CurrentEvent()
StephaneLenclud@217: {
StephaneLenclud@217: //Walk up the tree from the selected node to find our event
StephaneLenclud@217: TreeNode node = iTreeViewEvents.SelectedNode;
StephaneLenclud@234: Ear.Event selectedEvent = null;
StephaneLenclud@217: while (node != null)
StephaneLenclud@217: {
StephaneLenclud@234: if (node.Tag is Ear.Event)
StephaneLenclud@217: {
StephaneLenclud@234: selectedEvent = (Ear.Event) node.Tag;
StephaneLenclud@217: break;
StephaneLenclud@217: }
StephaneLenclud@217: node = node.Parent;
StephaneLenclud@217: }
StephaneLenclud@217:
StephaneLenclud@217: return selectedEvent;
StephaneLenclud@217: }
StephaneLenclud@217:
StephaneLenclud@217: ///
StephaneLenclud@219: /// Get the current action based on event tree view selection
StephaneLenclud@217: ///
StephaneLenclud@217: ///
StephaneLenclud@234: private Ear.Action CurrentAction()
StephaneLenclud@217: {
StephaneLenclud@217: TreeNode node = iTreeViewEvents.SelectedNode;
StephaneLenclud@234: if (node != null && node.Tag is Ear.Action)
StephaneLenclud@217: {
StephaneLenclud@234: return (Ear.Action) node.Tag;
StephaneLenclud@217: }
StephaneLenclud@217:
StephaneLenclud@217: return null;
StephaneLenclud@217: }
StephaneLenclud@217:
StephaneLenclud@217: ///
StephaneLenclud@217: ///
StephaneLenclud@217: ///
StephaneLenclud@217: ///
StephaneLenclud@217: ///
StephaneLenclud@227: private void buttonActionAdd_Click(object sender, EventArgs e)
StephaneLenclud@211: {
StephaneLenclud@234: Ear.Event selectedEvent = CurrentEvent();
StephaneLenclud@217: if (selectedEvent == null)
Stephane@212: {
StephaneLenclud@217: //We did not find a corresponding event
StephaneLenclud@211: return;
StephaneLenclud@211: }
StephaneLenclud@211:
StephaneLenclud@234: FormEditObject ea = new FormEditObject();
StephaneLenclud@228: ea.Text = "Add action";
StephaneLenclud@211: DialogResult res = CodeProject.Dialog.DlgBox.ShowDialog(ea);
StephaneLenclud@211: if (res == DialogResult.OK)
StephaneLenclud@211: {
StephaneLenclud@235: selectedEvent.Actions.Add(ea.Object);
Stephane@212: Properties.Settings.Default.Save();
StephaneLenclud@219: PopulateEventsTreeView();
StephaneLenclud@211: }
StephaneLenclud@211: }
StephaneLenclud@214:
StephaneLenclud@217: ///
StephaneLenclud@217: ///
StephaneLenclud@217: ///
StephaneLenclud@217: ///
StephaneLenclud@217: ///
StephaneLenclud@227: private void buttonActionEdit_Click(object sender, EventArgs e)
StephaneLenclud@227: {
StephaneLenclud@234: Ear.Event selectedEvent = CurrentEvent();
StephaneLenclud@234: Ear.Action selectedAction = CurrentAction();
StephaneLenclud@227: if (selectedEvent == null || selectedAction == null)
StephaneLenclud@227: {
StephaneLenclud@227: //We did not find a corresponding event
StephaneLenclud@227: return;
StephaneLenclud@227: }
StephaneLenclud@227:
StephaneLenclud@234: FormEditObject ea = new FormEditObject();
StephaneLenclud@228: ea.Text = "Edit action";
StephaneLenclud@231: ea.Object = selectedAction;
StephaneLenclud@227: int actionIndex = iTreeViewEvents.SelectedNode.Index;
StephaneLenclud@227: DialogResult res = CodeProject.Dialog.DlgBox.ShowDialog(ea);
StephaneLenclud@227: if (res == DialogResult.OK)
StephaneLenclud@227: {
StephaneLenclud@227: //Update our action
StephaneLenclud@231: selectedEvent.Actions[actionIndex]=ea.Object;
StephaneLenclud@227: //Save and rebuild our event tree view
StephaneLenclud@227: Properties.Settings.Default.Save();
StephaneLenclud@227: PopulateEventsTreeView();
StephaneLenclud@227: }
StephaneLenclud@227: }
StephaneLenclud@227:
StephaneLenclud@227: ///
StephaneLenclud@227: ///
StephaneLenclud@227: ///
StephaneLenclud@227: ///
StephaneLenclud@227: ///
StephaneLenclud@227: private void buttonActionDelete_Click(object sender, EventArgs e)
StephaneLenclud@214: {
StephaneLenclud@214:
StephaneLenclud@234: Ear.Action action = CurrentAction();
StephaneLenclud@214: if (action == null)
StephaneLenclud@214: {
StephaneLenclud@214: //Must select action node
StephaneLenclud@214: return;
StephaneLenclud@214: }
StephaneLenclud@214:
StephaneLenclud@235: Properties.Settings.Default.EarManager.RemoveAction(action);
StephaneLenclud@214: Properties.Settings.Default.Save();
StephaneLenclud@219: PopulateEventsTreeView();
StephaneLenclud@214: }
StephaneLenclud@217:
StephaneLenclud@223: ///
StephaneLenclud@223: ///
StephaneLenclud@223: ///
StephaneLenclud@223: ///
StephaneLenclud@223: ///
StephaneLenclud@227: private void buttonActionTest_Click(object sender, EventArgs e)
StephaneLenclud@223: {
StephaneLenclud@234: Ear.Action a = CurrentAction();
StephaneLenclud@223: if (a != null)
StephaneLenclud@223: {
StephaneLenclud@231: a.Test();
StephaneLenclud@223: }
StephaneLenclud@223: iTreeViewEvents.Focus();
StephaneLenclud@223: }
StephaneLenclud@223:
StephaneLenclud@223: ///
StephaneLenclud@223: ///
StephaneLenclud@223: ///
StephaneLenclud@223: ///
StephaneLenclud@223: ///
StephaneLenclud@223: private void buttonActionMoveUp_Click(object sender, EventArgs e)
StephaneLenclud@223: {
StephaneLenclud@234: Ear.Action a = CurrentAction();
StephaneLenclud@223: if (a == null ||
StephaneLenclud@223: //Action already at the top of the list
StephaneLenclud@223: iTreeViewEvents.SelectedNode.Index == 0)
StephaneLenclud@223: {
StephaneLenclud@223: return;
StephaneLenclud@223: }
StephaneLenclud@223:
StephaneLenclud@223: //Swap actions in event's action list
StephaneLenclud@234: Ear.Event currentEvent = CurrentEvent();
StephaneLenclud@223: int currentIndex = iTreeViewEvents.SelectedNode.Index;
StephaneLenclud@234: Ear.Action movingUp = currentEvent.Actions[currentIndex];
StephaneLenclud@234: Ear.Action movingDown = currentEvent.Actions[currentIndex-1];
StephaneLenclud@223: currentEvent.Actions[currentIndex] = movingDown;
StephaneLenclud@223: currentEvent.Actions[currentIndex-1] = movingUp;
StephaneLenclud@223:
StephaneLenclud@223: //Save and populate our tree again
StephaneLenclud@223: Properties.Settings.Default.Save();
StephaneLenclud@223: PopulateEventsTreeView();
StephaneLenclud@223:
StephaneLenclud@223: }
StephaneLenclud@223:
StephaneLenclud@223: ///
StephaneLenclud@223: ///
StephaneLenclud@223: ///
StephaneLenclud@223: ///
StephaneLenclud@223: ///
StephaneLenclud@223: private void buttonActionMoveDown_Click(object sender, EventArgs e)
StephaneLenclud@223: {
StephaneLenclud@234: Ear.Action a = CurrentAction();
StephaneLenclud@223: if (a == null ||
StephaneLenclud@223: //Action already at the bottom of the list
StephaneLenclud@223: iTreeViewEvents.SelectedNode.Index == iTreeViewEvents.SelectedNode.Parent.Nodes.Count - 1)
StephaneLenclud@223: {
StephaneLenclud@223: return;
StephaneLenclud@223: }
StephaneLenclud@223:
StephaneLenclud@223: //Swap actions in event's action list
StephaneLenclud@234: Ear.Event currentEvent = CurrentEvent();
StephaneLenclud@223: int currentIndex = iTreeViewEvents.SelectedNode.Index;
StephaneLenclud@234: Ear.Action movingDown = currentEvent.Actions[currentIndex];
StephaneLenclud@234: Ear.Action movingUp = currentEvent.Actions[currentIndex + 1];
StephaneLenclud@223: currentEvent.Actions[currentIndex] = movingUp;
StephaneLenclud@223: currentEvent.Actions[currentIndex + 1] = movingDown;
StephaneLenclud@223:
StephaneLenclud@223: //Save and populate our tree again
StephaneLenclud@223: Properties.Settings.Default.Save();
StephaneLenclud@223: PopulateEventsTreeView();
StephaneLenclud@217: }
StephaneLenclud@227:
StephaneLenclud@227:
StephaneLenclud@227: ///
StephaneLenclud@229: ///
StephaneLenclud@229: ///
StephaneLenclud@229: ///
StephaneLenclud@229: ///
StephaneLenclud@229: private void buttonEventTest_Click(object sender, EventArgs e)
StephaneLenclud@229: {
StephaneLenclud@234: Ear.Event earEvent = CurrentEvent();
StephaneLenclud@229: if (earEvent != null)
StephaneLenclud@229: {
StephaneLenclud@231: earEvent.Test();
StephaneLenclud@229: }
StephaneLenclud@229: }
StephaneLenclud@229:
StephaneLenclud@229: ///
StephaneLenclud@227: /// Manages events and actions buttons according to selected item in event tree.
StephaneLenclud@227: ///
StephaneLenclud@227: ///
StephaneLenclud@227: ///
StephaneLenclud@227: private void iTreeViewEvents_AfterSelect(object sender, TreeViewEventArgs e)
StephaneLenclud@227: {
StephaneLenclud@231: UpdateEventView();
StephaneLenclud@231: }
StephaneLenclud@231:
StephaneLenclud@231: ///
StephaneLenclud@231: ///
StephaneLenclud@231: ///
StephaneLenclud@231: private void UpdateEventView()
StephaneLenclud@231: {
StephaneLenclud@231: //One can always add an event
StephaneLenclud@231: buttonEventAdd.Enabled = true;
StephaneLenclud@231:
StephaneLenclud@227: //Enable buttons according to selected item
StephaneLenclud@229: buttonActionAdd.Enabled =
StephaneLenclud@229: buttonEventTest.Enabled =
StephaneLenclud@231: buttonEventDelete.Enabled =
StephaneLenclud@231: buttonEventEdit.Enabled =
StephaneLenclud@229: CurrentEvent() != null;
StephaneLenclud@227:
StephaneLenclud@234: Ear.Action currentAction = CurrentAction();
StephaneLenclud@227: //If an action is selected enable the following buttons
StephaneLenclud@227: buttonActionTest.Enabled =
StephaneLenclud@227: buttonActionDelete.Enabled =
StephaneLenclud@227: buttonActionMoveUp.Enabled =
StephaneLenclud@227: buttonActionMoveDown.Enabled =
StephaneLenclud@231: buttonActionEdit.Enabled =
StephaneLenclud@227: currentAction != null;
StephaneLenclud@227:
StephaneLenclud@227: if (currentAction != null)
StephaneLenclud@227: {
StephaneLenclud@227: //If an action is selected enable move buttons if needed
StephaneLenclud@227: buttonActionMoveUp.Enabled = iTreeViewEvents.SelectedNode.Index != 0;
StephaneLenclud@227: buttonActionMoveDown.Enabled = iTreeViewEvents.SelectedNode.Index <
StephaneLenclud@227: iTreeViewEvents.SelectedNode.Parent.Nodes.Count - 1;
StephaneLenclud@227: }
StephaneLenclud@227: }
StephaneLenclud@227:
StephaneLenclud@231: private void buttonEventAdd_Click(object sender, EventArgs e)
StephaneLenclud@231: {
StephaneLenclud@234: FormEditObject ea = new FormEditObject();
StephaneLenclud@231: ea.Text = "Add event";
StephaneLenclud@231: DialogResult res = CodeProject.Dialog.DlgBox.ShowDialog(ea);
StephaneLenclud@231: if (res == DialogResult.OK)
StephaneLenclud@231: {
StephaneLenclud@235: Properties.Settings.Default.EarManager.Events.Add(ea.Object);
StephaneLenclud@231: Properties.Settings.Default.Save();
StephaneLenclud@231: PopulateEventsTreeView();
StephaneLenclud@231: SelectEvent(ea.Object);
StephaneLenclud@231: }
StephaneLenclud@231: }
StephaneLenclud@231:
StephaneLenclud@231: private void buttonEventDelete_Click(object sender, EventArgs e)
StephaneLenclud@231: {
StephaneLenclud@234: Ear.Event currentEvent = CurrentEvent();
StephaneLenclud@231: if (currentEvent == null)
StephaneLenclud@231: {
StephaneLenclud@231: //Must select action node
StephaneLenclud@231: return;
StephaneLenclud@231: }
StephaneLenclud@231:
StephaneLenclud@235: Properties.Settings.Default.EarManager.Events.Remove(currentEvent);
StephaneLenclud@231: Properties.Settings.Default.Save();
StephaneLenclud@231: PopulateEventsTreeView();
StephaneLenclud@231: }
StephaneLenclud@231:
StephaneLenclud@231: private void buttonEventEdit_Click(object sender, EventArgs e)
StephaneLenclud@231: {
StephaneLenclud@234: Ear.Event selectedEvent = CurrentEvent();
StephaneLenclud@231: if (selectedEvent == null)
StephaneLenclud@231: {
StephaneLenclud@231: //We did not find a corresponding event
StephaneLenclud@231: return;
StephaneLenclud@231: }
StephaneLenclud@231:
StephaneLenclud@234: FormEditObject ea = new FormEditObject();
StephaneLenclud@231: ea.Text = "Edit event";
StephaneLenclud@231: ea.Object = selectedEvent;
StephaneLenclud@231: int actionIndex = iTreeViewEvents.SelectedNode.Index;
StephaneLenclud@231: DialogResult res = CodeProject.Dialog.DlgBox.ShowDialog(ea);
StephaneLenclud@231: if (res == DialogResult.OK)
StephaneLenclud@231: {
StephaneLenclud@231: //Save and rebuild our event tree view
StephaneLenclud@231: Properties.Settings.Default.Save();
StephaneLenclud@231: PopulateEventsTreeView();
StephaneLenclud@231: }
StephaneLenclud@231: }
StephaneLenclud@231:
StephaneLenclud@231: private void iTreeViewEvents_Leave(object sender, EventArgs e)
StephaneLenclud@231: {
StephaneLenclud@231: //Make sure our event tree never looses focus
StephaneLenclud@231: ((TreeView) sender).Focus();
StephaneLenclud@231: }
StephaneLenclud@233:
StephaneLenclud@233: private async void iButtonHarmonyConnect_Click(object sender, EventArgs e)
StephaneLenclud@233: {
StephaneLenclud@233: //Save hub address
StephaneLenclud@233: Properties.Settings.Default.HarmonyHubAddress = iTextBoxHarmonyHubAddress.Text;
StephaneLenclud@233: Properties.Settings.Default.Save();
StephaneLenclud@233:
StephaneLenclud@233: iButtonHarmonyConnect.Enabled = false;
StephaneLenclud@233: try
StephaneLenclud@233: {
StephaneLenclud@233: await ConnectHarmonyAsync();
StephaneLenclud@233: }
StephaneLenclud@233: catch (Exception)
StephaneLenclud@233: {
StephaneLenclud@233: iButtonHarmonyConnect.Enabled = true;
StephaneLenclud@233: }
StephaneLenclud@233:
StephaneLenclud@233: }
StephaneLenclud@233:
StephaneLenclud@233:
StephaneLenclud@233: private async Task ConnectHarmonyAsync()
StephaneLenclud@233: {
StephaneLenclud@236: if (Program.HarmonyClient != null)
StephaneLenclud@236: {
StephaneLenclud@236: Program.HarmonyClient.Close();
StephaneLenclud@236: }
StephaneLenclud@236:
StephaneLenclud@236: //Reset Harmony client & config
StephaneLenclud@236: Program.HarmonyClient = null;
StephaneLenclud@236: Program.HarmonyConfig = null;
StephaneLenclud@236:
StephaneLenclud@233: Console.WriteLine("Harmony: Connecting... ");
StephaneLenclud@236: Program.HarmonyClient = new HarmonyHub.Client(iTextBoxHarmonyHubAddress.Text);
StephaneLenclud@233: //First create our client and login
StephaneLenclud@233: if (File.Exists("SessionToken"))
StephaneLenclud@233: {
StephaneLenclud@233: var sessionToken = File.ReadAllText("SessionToken");
StephaneLenclud@233: Console.WriteLine("Harmony: Reusing token: {0}", sessionToken);
StephaneLenclud@236: Program.HarmonyClient.Open(sessionToken);
StephaneLenclud@233: }
StephaneLenclud@233: else
StephaneLenclud@233: {
StephaneLenclud@233: if (string.IsNullOrEmpty(iTextBoxLogitechPassword.Text))
StephaneLenclud@233: {
StephaneLenclud@233: Console.WriteLine("Harmony: Credentials missing!");
StephaneLenclud@233: return;
StephaneLenclud@233: }
StephaneLenclud@233:
StephaneLenclud@233: Console.WriteLine("Harmony: Authenticating with Logitech servers...");
StephaneLenclud@236: await Program.HarmonyClient.Open(iTextBoxLogitechUserName.Text, iTextBoxLogitechPassword.Text);
StephaneLenclud@233: File.WriteAllText("SessionToken", Program.HarmonyClient.Token);
StephaneLenclud@233: }
StephaneLenclud@233:
StephaneLenclud@233: Console.WriteLine("Harmony: Fetching Harmony Hub configuration...");
StephaneLenclud@233:
StephaneLenclud@233: //Fetch our config
StephaneLenclud@236: Program.HarmonyConfig = await Program.HarmonyClient.GetConfigAsync();
StephaneLenclud@236: PopulateTreeViewHarmony(Program.HarmonyConfig);
StephaneLenclud@233:
StephaneLenclud@233: Console.WriteLine("Harmony: Ready");
StephaneLenclud@233: }
StephaneLenclud@233:
StephaneLenclud@233: ///
StephaneLenclud@233: ///
StephaneLenclud@233: ///
StephaneLenclud@233: ///
StephaneLenclud@236: private void PopulateTreeViewHarmony(HarmonyHub.Config aConfig)
StephaneLenclud@233: {
StephaneLenclud@233: iTreeViewHarmony.Nodes.Clear();
StephaneLenclud@233: //Add our devices
StephaneLenclud@236: foreach (HarmonyHub.Device device in aConfig.Devices)
StephaneLenclud@233: {
StephaneLenclud@233: TreeNode deviceNode = iTreeViewHarmony.Nodes.Add(device.Id, $"{device.Label} ({device.DeviceTypeDisplayName}/{device.Model})");
StephaneLenclud@233: deviceNode.Tag = device;
StephaneLenclud@233:
StephaneLenclud@236: foreach (HarmonyHub.ControlGroup cg in device.ControlGroups)
StephaneLenclud@233: {
StephaneLenclud@233: TreeNode cgNode = deviceNode.Nodes.Add(cg.Name);
StephaneLenclud@233: cgNode.Tag = cg;
StephaneLenclud@233:
StephaneLenclud@236: foreach (HarmonyHub.Function f in cg.Functions)
StephaneLenclud@233: {
StephaneLenclud@233: TreeNode fNode = cgNode.Nodes.Add(f.Name);
StephaneLenclud@233: fNode.Tag = f;
StephaneLenclud@233: }
StephaneLenclud@233: }
StephaneLenclud@233: }
StephaneLenclud@233:
StephaneLenclud@233: //treeViewConfig.ExpandAll();
StephaneLenclud@233: }
StephaneLenclud@233:
StephaneLenclud@233: private async void iTreeViewHarmony_NodeMouseDoubleClick(object sender, TreeNodeMouseClickEventArgs e)
StephaneLenclud@233: {
StephaneLenclud@233: //Upon function node double click we execute it
StephaneLenclud@236: var tag = e.Node.Tag as HarmonyHub.Function;
StephaneLenclud@236: if (tag != null && e.Node.Parent.Parent.Tag is HarmonyHub.Device)
StephaneLenclud@233: {
StephaneLenclud@236: HarmonyHub.Function f = tag;
StephaneLenclud@236: HarmonyHub.Device d = (HarmonyHub.Device)e.Node.Parent.Parent.Tag;
StephaneLenclud@233:
StephaneLenclud@233: Console.WriteLine($"Harmony: Sending {f.Name} to {d.Label}...");
StephaneLenclud@233:
StephaneLenclud@233: await Program.HarmonyClient.SendCommandAsync(d.Id, f.Name);
StephaneLenclud@233: }
StephaneLenclud@233: }
sl@0: }
sl@0: }