Server/MainForm.cs
author StephaneLenclud
Thu, 24 Sep 2015 14:35:50 +0200
changeset 158 e22bf44c4300
parent 157 0f3e7b21c663
child 167 d2295c186ce1
permissions -rw-r--r--
Fixing issues where layout change would not be detected beccause of partial
field similarity between new and older layout.
Therefore we now clear our fields whenever our layout is changed.
Now resetting our create bitmap flag, hoping it will fix our rather large memory
usage when minimized.
StephaneLenclud@123
     1
//
StephaneLenclud@123
     2
// Copyright (C) 2014-2015 Stéphane Lenclud.
StephaneLenclud@123
     3
//
StephaneLenclud@123
     4
// This file is part of SharpDisplayManager.
StephaneLenclud@123
     5
//
StephaneLenclud@123
     6
// SharpDisplayManager is free software: you can redistribute it and/or modify
StephaneLenclud@123
     7
// it under the terms of the GNU General Public License as published by
StephaneLenclud@123
     8
// the Free Software Foundation, either version 3 of the License, or
StephaneLenclud@123
     9
// (at your option) any later version.
StephaneLenclud@123
    10
//
StephaneLenclud@123
    11
// SharpDisplayManager is distributed in the hope that it will be useful,
StephaneLenclud@123
    12
// but WITHOUT ANY WARRANTY; without even the implied warranty of
StephaneLenclud@123
    13
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
StephaneLenclud@123
    14
// GNU General Public License for more details.
StephaneLenclud@123
    15
//
StephaneLenclud@123
    16
// You should have received a copy of the GNU General Public License
StephaneLenclud@123
    17
// along with SharpDisplayManager.  If not, see <http://www.gnu.org/licenses/>.
StephaneLenclud@123
    18
//
StephaneLenclud@123
    19
StephaneLenclud@123
    20
using System;
sl@0
    21
using System.Collections.Generic;
sl@0
    22
using System.ComponentModel;
sl@0
    23
using System.Data;
sl@0
    24
using System.Drawing;
sl@0
    25
using System.Linq;
sl@0
    26
using System.Text;
sl@0
    27
using System.Threading.Tasks;
sl@0
    28
using System.Windows.Forms;
sl@14
    29
using System.IO;
sl@0
    30
using CodeProject.Dialog;
sl@14
    31
using System.Drawing.Imaging;
sl@17
    32
using System.ServiceModel;
sl@25
    33
using System.Threading;
sl@31
    34
using System.Diagnostics;
sl@88
    35
using System.Deployment.Application;
sl@94
    36
using System.Reflection;
StephaneLenclud@112
    37
//NAudio
StephaneLenclud@112
    38
using NAudio.CoreAudioApi;
StephaneLenclud@112
    39
using NAudio.CoreAudioApi.Interfaces;
StephaneLenclud@112
    40
using System.Runtime.InteropServices;
StephaneLenclud@117
    41
//Network
StephaneLenclud@117
    42
using NETWORKLIST;
sl@25
    43
//
sl@25
    44
using SharpDisplayClient;
sl@55
    45
using SharpDisplay;
StephaneLenclud@135
    46
using MiniDisplayInterop;
sl@0
    47
StephaneLenclud@124
    48
sl@0
    49
namespace SharpDisplayManager
sl@0
    50
{
sl@58
    51
    //Types declarations
sl@58
    52
    public delegate uint ColorProcessingDelegate(int aX, int aY, uint aPixel);
sl@58
    53
    public delegate int CoordinateTranslationDelegate(System.Drawing.Bitmap aBmp, int aInt);
sl@62
    54
    //Delegates are used for our thread safe method
sl@62
    55
    public delegate void AddClientDelegate(string aSessionId, ICallback aCallback);
sl@62
    56
    public delegate void RemoveClientDelegate(string aSessionId);
sl@79
    57
    public delegate void SetFieldDelegate(string SessionId, DataField aField);
sl@79
    58
    public delegate void SetFieldsDelegate(string SessionId, System.Collections.Generic.IList<DataField> aFields);
sl@62
    59
    public delegate void SetLayoutDelegate(string SessionId, TableLayout aLayout);
sl@62
    60
    public delegate void SetClientNameDelegate(string aSessionId, string aName);
StephaneLenclud@112
    61
	public delegate void PlainUpdateDelegate();
sl@62
    62
sl@58
    63
sl@58
    64
    /// <summary>
sl@58
    65
    /// Our Display manager main form
sl@58
    66
    /// </summary>
StephaneLenclud@126
    67
	[System.ComponentModel.DesignerCategory("Form")]
StephaneLenclud@126
    68
	public partial class MainForm : MainFormHid, IMMNotificationClient
sl@0
    69
    {
sl@2
    70
        DateTime LastTickTime;
sl@3
    71
        Display iDisplay;
sl@14
    72
        System.Drawing.Bitmap iBmp;
sl@14
    73
        bool iCreateBitmap; //Workaround render to bitmap issues when minimized
sl@17
    74
        ServiceHost iServiceHost;
sl@65
    75
        // Our collection of clients sorted by session id.
sl@33
    76
        public Dictionary<string, ClientData> iClients;
sl@65
    77
        // The name of the client which informations are currently displayed.
sl@65
    78
        public string iCurrentClientSessionId;
sl@65
    79
        ClientData iCurrentClientData;
sl@65
    80
        //
sl@29
    81
        public bool iClosing;
StephaneLenclud@122
    82
		//
StephaneLenclud@122
    83
		public bool iSkipFrameRendering;
sl@65
    84
        //Function pointer for pixel color filtering
sl@58
    85
        ColorProcessingDelegate iColorFx;
sl@65
    86
        //Function pointer for pixel X coordinate intercept
sl@58
    87
        CoordinateTranslationDelegate iScreenX;
sl@65
    88
        //Function pointer for pixel Y coordinate intercept
sl@58
    89
        CoordinateTranslationDelegate iScreenY;
StephaneLenclud@112
    90
		//NAudio
StephaneLenclud@112
    91
		private MMDeviceEnumerator iMultiMediaDeviceEnumerator;
StephaneLenclud@112
    92
		private MMDevice iMultiMediaDevice;
StephaneLenclud@117
    93
		//Network
StephaneLenclud@117
    94
		private NetworkManager iNetworkManager;
StephaneLenclud@112
    95
		
sl@2
    96
sl@94
    97
		/// <summary>
sl@94
    98
		/// Manage run when Windows startup option
sl@94
    99
		/// </summary>
sl@92
   100
		private StartupManager iStartupManager;
sl@92
   101
sl@94
   102
		/// <summary>
sl@94
   103
		/// System tray icon.
sl@94
   104
		/// </summary>
sl@94
   105
		private NotifyIconAdv iNotifyIcon;
sl@94
   106
sl@0
   107
        public MainForm()
sl@0
   108
        {
StephaneLenclud@122
   109
			iSkipFrameRendering = false;
StephaneLenclud@122
   110
			iClosing = false;
sl@65
   111
            iCurrentClientSessionId = "";
sl@65
   112
            iCurrentClientData = null;
sl@2
   113
            LastTickTime = DateTime.Now;
StephaneLenclud@104
   114
			//Instantiate our display and register for events notifications
sl@3
   115
            iDisplay = new Display();
StephaneLenclud@104
   116
			iDisplay.OnOpened += OnDisplayOpened;
StephaneLenclud@104
   117
			iDisplay.OnClosed += OnDisplayClosed;
StephaneLenclud@104
   118
			//
StephaneLenclud@104
   119
			iClients = new Dictionary<string, ClientData>();
sl@92
   120
			iStartupManager = new StartupManager();
sl@94
   121
			iNotifyIcon = new NotifyIconAdv();
sl@2
   122
StephaneLenclud@104
   123
			//Have our designer initialize its controls
sl@0
   124
            InitializeComponent();
StephaneLenclud@104
   125
StephaneLenclud@104
   126
			//Populate device types
StephaneLenclud@104
   127
			PopulateDeviceTypes();
StephaneLenclud@104
   128
StephaneLenclud@152
   129
            //Populate optical drives
StephaneLenclud@152
   130
            PopulateOpticalDrives();
StephaneLenclud@152
   131
StephaneLenclud@104
   132
			//Initial status update 
sl@7
   133
            UpdateStatus();
StephaneLenclud@104
   134
sl@14
   135
            //We have a bug when drawing minimized and reusing our bitmap
StephaneLenclud@158
   136
            //Though I could not reproduce it on Windows 10
sl@14
   137
            iBmp = new System.Drawing.Bitmap(tableLayoutPanel.Width, tableLayoutPanel.Height, PixelFormat.Format32bppArgb);
sl@14
   138
            iCreateBitmap = false;
sl@94
   139
StephaneLenclud@104
   140
			//Minimize our window if desired
sl@94
   141
			if (Properties.Settings.Default.StartMinimized)
sl@94
   142
			{
sl@94
   143
				WindowState = FormWindowState.Minimized;
sl@94
   144
			}
sl@94
   145
sl@0
   146
        }
sl@0
   147
sl@94
   148
		/// <summary>
StephaneLenclud@106
   149
		///
StephaneLenclud@106
   150
		/// </summary>
StephaneLenclud@106
   151
		/// <param name="sender"></param>
StephaneLenclud@106
   152
		/// <param name="e"></param>
StephaneLenclud@106
   153
        private void MainForm_Load(object sender, EventArgs e)
StephaneLenclud@106
   154
        {
StephaneLenclud@106
   155
			//Check if we are running a Click Once deployed application
StephaneLenclud@106
   156
			if (ApplicationDeployment.IsNetworkDeployed)
StephaneLenclud@106
   157
			{
StephaneLenclud@106
   158
				//This is a proper Click Once installation, fetch and show our version number
StephaneLenclud@106
   159
				this.Text += " - v" + ApplicationDeployment.CurrentDeployment.CurrentVersion;
StephaneLenclud@106
   160
			}
StephaneLenclud@106
   161
			else
StephaneLenclud@106
   162
			{
StephaneLenclud@106
   163
				//Not a proper Click Once installation, assuming development build then
StephaneLenclud@106
   164
				this.Text += " - development";
StephaneLenclud@106
   165
			}
StephaneLenclud@106
   166
StephaneLenclud@112
   167
			//NAudio
StephaneLenclud@112
   168
			iMultiMediaDeviceEnumerator = new MMDeviceEnumerator();
StephaneLenclud@117
   169
			iMultiMediaDeviceEnumerator.RegisterEndpointNotificationCallback(this);			
StephaneLenclud@112
   170
			UpdateAudioDeviceAndMasterVolumeThreadSafe();
StephaneLenclud@112
   171
StephaneLenclud@117
   172
			//Network
StephaneLenclud@117
   173
			iNetworkManager = new NetworkManager();
StephaneLenclud@117
   174
			iNetworkManager.OnConnectivityChanged += OnConnectivityChanged;
StephaneLenclud@117
   175
			UpdateNetworkStatus();
StephaneLenclud@117
   176
StephaneLenclud@106
   177
			//Setup notification icon
StephaneLenclud@106
   178
			SetupTrayIcon();
StephaneLenclud@106
   179
StephaneLenclud@106
   180
			// To make sure start up with minimize to tray works
StephaneLenclud@106
   181
			if (WindowState == FormWindowState.Minimized && Properties.Settings.Default.MinimizeToTray)
StephaneLenclud@106
   182
			{
StephaneLenclud@106
   183
				Visible = false;
StephaneLenclud@106
   184
			}
StephaneLenclud@106
   185
StephaneLenclud@106
   186
#if !DEBUG
StephaneLenclud@106
   187
			//When not debugging we want the screen to be empty until a client takes over
StephaneLenclud@106
   188
			ClearLayout();
StephaneLenclud@106
   189
#else
StephaneLenclud@106
   190
			//When developing we want at least one client for testing
StephaneLenclud@106
   191
			StartNewClient("abcdefghijklmnopqrst-0123456789","ABCDEFGHIJKLMNOPQRST-0123456789");
StephaneLenclud@106
   192
#endif
StephaneLenclud@106
   193
StephaneLenclud@106
   194
			//Open display connection on start-up if needed
StephaneLenclud@106
   195
			if (Properties.Settings.Default.DisplayConnectOnStartup)
StephaneLenclud@106
   196
			{
StephaneLenclud@106
   197
				OpenDisplayConnection();
StephaneLenclud@106
   198
			}
StephaneLenclud@106
   199
StephaneLenclud@106
   200
			//Start our server so that we can get client requests
StephaneLenclud@106
   201
			StartServer();
StephaneLenclud@124
   202
StephaneLenclud@124
   203
			//Register for HID events
StephaneLenclud@124
   204
			RegisterHidDevices();
StephaneLenclud@106
   205
        }
StephaneLenclud@106
   206
StephaneLenclud@106
   207
		/// <summary>
StephaneLenclud@104
   208
		/// Called when our display is opened.
StephaneLenclud@104
   209
		/// </summary>
StephaneLenclud@104
   210
		/// <param name="aDisplay"></param>
StephaneLenclud@104
   211
		private void OnDisplayOpened(Display aDisplay)
StephaneLenclud@104
   212
		{
StephaneLenclud@122
   213
			//Make sure we resume frame rendering
StephaneLenclud@122
   214
			iSkipFrameRendering = false;
StephaneLenclud@122
   215
StephaneLenclud@105
   216
			//Set our screen size now that our display is connected
StephaneLenclud@105
   217
			//Our panelDisplay is the container of our tableLayoutPanel
StephaneLenclud@105
   218
			//tableLayoutPanel will resize itself to fit the client size of our panelDisplay
StephaneLenclud@105
   219
			//panelDisplay needs an extra 2 pixels for borders on each sides
StephaneLenclud@105
   220
			//tableLayoutPanel will eventually be the exact size of our display
StephaneLenclud@105
   221
			Size size = new Size(iDisplay.WidthInPixels() + 2, iDisplay.HeightInPixels() + 2);
StephaneLenclud@105
   222
			panelDisplay.Size = size;
StephaneLenclud@105
   223
StephaneLenclud@104
   224
			//Our display was just opened, update our UI
StephaneLenclud@104
   225
			UpdateStatus();
StephaneLenclud@104
   226
			//Initiate asynchronous request
StephaneLenclud@104
   227
			iDisplay.RequestFirmwareRevision();
StephaneLenclud@108
   228
StephaneLenclud@117
   229
			//Audio
StephaneLenclud@112
   230
			UpdateMasterVolumeThreadSafe();
StephaneLenclud@117
   231
			//Network
StephaneLenclud@117
   232
			UpdateNetworkStatus();
StephaneLenclud@112
   233
StephaneLenclud@108
   234
#if DEBUG
StephaneLenclud@108
   235
			//Testing icon in debug, no arm done if icon not supported
StephaneLenclud@109
   236
			//iDisplay.SetIconStatus(Display.TMiniDisplayIconType.EMiniDisplayIconRecording, 0, 1);
StephaneLenclud@112
   237
			//iDisplay.SetAllIconsStatus(2);
StephaneLenclud@108
   238
#endif
StephaneLenclud@108
   239
StephaneLenclud@104
   240
		}
StephaneLenclud@104
   241
StephaneLenclud@104
   242
		/// <summary>
StephaneLenclud@104
   243
		/// Called when our display is closed.
StephaneLenclud@104
   244
		/// </summary>
StephaneLenclud@104
   245
		/// <param name="aDisplay"></param>
StephaneLenclud@104
   246
		private void OnDisplayClosed(Display aDisplay)
StephaneLenclud@104
   247
		{
StephaneLenclud@104
   248
			//Our display was just closed, update our UI consequently
StephaneLenclud@104
   249
			UpdateStatus();
StephaneLenclud@104
   250
		}
StephaneLenclud@117
   251
StephaneLenclud@117
   252
		public void OnConnectivityChanged(NetworkManager aNetwork, NLM_CONNECTIVITY newConnectivity)
StephaneLenclud@117
   253
		{
StephaneLenclud@117
   254
			//Update network status
StephaneLenclud@117
   255
			UpdateNetworkStatus();			
StephaneLenclud@117
   256
		}
StephaneLenclud@117
   257
StephaneLenclud@117
   258
		/// <summary>
StephaneLenclud@117
   259
		/// Update our Network Status
StephaneLenclud@117
   260
		/// </summary>
StephaneLenclud@117
   261
		private void UpdateNetworkStatus()
StephaneLenclud@117
   262
		{
StephaneLenclud@117
   263
			if (iDisplay.IsOpen())
StephaneLenclud@117
   264
			{
StephaneLenclud@135
   265
                iDisplay.SetIconOnOff(MiniDisplay.IconType.Internet, iNetworkManager.NetworkListManager.IsConnectedToInternet);
StephaneLenclud@135
   266
                iDisplay.SetIconOnOff(MiniDisplay.IconType.NetworkSignal, iNetworkManager.NetworkListManager.IsConnected);
StephaneLenclud@117
   267
			}
StephaneLenclud@117
   268
		}
StephaneLenclud@117
   269
StephaneLenclud@117
   270
StephaneLenclud@118
   271
		int iLastNetworkIconIndex = 0;
StephaneLenclud@118
   272
		int iUpdateCountSinceLastNetworkAnimation = 0;
StephaneLenclud@118
   273
StephaneLenclud@118
   274
		/// <summary>
StephaneLenclud@118
   275
		/// 
StephaneLenclud@118
   276
		/// </summary>
StephaneLenclud@118
   277
		private void UpdateNetworkSignal(DateTime aLastTickTime, DateTime aNewTickTime)
StephaneLenclud@118
   278
		{
StephaneLenclud@118
   279
			iUpdateCountSinceLastNetworkAnimation++;
StephaneLenclud@118
   280
			iUpdateCountSinceLastNetworkAnimation = iUpdateCountSinceLastNetworkAnimation % 4;
StephaneLenclud@118
   281
StephaneLenclud@118
   282
			if (iDisplay.IsOpen() && iNetworkManager.NetworkListManager.IsConnected && iUpdateCountSinceLastNetworkAnimation==0)
StephaneLenclud@135
   283
			{
StephaneLenclud@135
   284
                int iconCount = iDisplay.IconCount(MiniDisplay.IconType.NetworkSignal);
StephaneLenclud@120
   285
				if (iconCount <= 0)
StephaneLenclud@120
   286
				{
StephaneLenclud@120
   287
					//Prevents div by zero and other undefined behavior
StephaneLenclud@120
   288
					return;
StephaneLenclud@120
   289
				}
StephaneLenclud@118
   290
				iLastNetworkIconIndex++;
StephaneLenclud@119
   291
				iLastNetworkIconIndex = iLastNetworkIconIndex % (iconCount*2);
StephaneLenclud@118
   292
				for (int i=0;i<iconCount;i++)
StephaneLenclud@118
   293
				{
StephaneLenclud@119
   294
					if (i < iLastNetworkIconIndex && !(i == 0 && iLastNetworkIconIndex > 3) && !(i == 1 && iLastNetworkIconIndex > 4))
StephaneLenclud@118
   295
					{
StephaneLenclud@135
   296
                        iDisplay.SetIconOn(MiniDisplay.IconType.NetworkSignal, i);
StephaneLenclud@118
   297
					}
StephaneLenclud@118
   298
					else
StephaneLenclud@118
   299
					{
StephaneLenclud@135
   300
                        iDisplay.SetIconOff(MiniDisplay.IconType.NetworkSignal, i);
StephaneLenclud@118
   301
					}
StephaneLenclud@118
   302
				}				
StephaneLenclud@118
   303
			}
StephaneLenclud@118
   304
		}
StephaneLenclud@118
   305
StephaneLenclud@118
   306
StephaneLenclud@118
   307
StephaneLenclud@112
   308
        /// <summary>
StephaneLenclud@112
   309
        /// Receive volume change notification and reflect changes on our slider.
StephaneLenclud@112
   310
        /// </summary>
StephaneLenclud@112
   311
        /// <param name="data"></param>
StephaneLenclud@112
   312
        public void OnVolumeNotificationThreadSafe(AudioVolumeNotificationData data)
StephaneLenclud@112
   313
        {
StephaneLenclud@112
   314
			UpdateMasterVolumeThreadSafe();
StephaneLenclud@112
   315
        }
StephaneLenclud@112
   316
StephaneLenclud@112
   317
        /// <summary>
StephaneLenclud@112
   318
        /// Update master volume when user moves our slider.
StephaneLenclud@112
   319
        /// </summary>
StephaneLenclud@112
   320
        /// <param name="sender"></param>
StephaneLenclud@112
   321
        /// <param name="e"></param>
StephaneLenclud@112
   322
        private void trackBarMasterVolume_Scroll(object sender, EventArgs e)
StephaneLenclud@112
   323
        {
StephaneLenclud@116
   324
			//Just like Windows Volume Mixer we unmute if the volume is adjusted
StephaneLenclud@116
   325
			iMultiMediaDevice.AudioEndpointVolume.Mute = false;
StephaneLenclud@116
   326
			//Set volume level according to our volume slider new position
StephaneLenclud@112
   327
			iMultiMediaDevice.AudioEndpointVolume.MasterVolumeLevelScalar = trackBarMasterVolume.Value / 100.0f;
StephaneLenclud@112
   328
        }
StephaneLenclud@112
   329
StephaneLenclud@113
   330
StephaneLenclud@113
   331
		/// <summary>
StephaneLenclud@113
   332
		/// Mute check box changed.
StephaneLenclud@113
   333
		/// </summary>
StephaneLenclud@113
   334
		/// <param name="sender"></param>
StephaneLenclud@113
   335
		/// <param name="e"></param>
StephaneLenclud@113
   336
		private void checkBoxMute_CheckedChanged(object sender, EventArgs e)
StephaneLenclud@113
   337
		{
StephaneLenclud@113
   338
			iMultiMediaDevice.AudioEndpointVolume.Mute = checkBoxMute.Checked;
StephaneLenclud@113
   339
		}
StephaneLenclud@113
   340
StephaneLenclud@112
   341
        /// <summary>
StephaneLenclud@112
   342
        /// Device State Changed
StephaneLenclud@112
   343
        /// </summary>
StephaneLenclud@112
   344
        public void OnDeviceStateChanged([MarshalAs(UnmanagedType.LPWStr)] string deviceId, [MarshalAs(UnmanagedType.I4)] DeviceState newState){}
StephaneLenclud@112
   345
StephaneLenclud@112
   346
        /// <summary>
StephaneLenclud@112
   347
        /// Device Added
StephaneLenclud@112
   348
        /// </summary>
StephaneLenclud@112
   349
        public void OnDeviceAdded([MarshalAs(UnmanagedType.LPWStr)] string pwstrDeviceId) { }
StephaneLenclud@112
   350
StephaneLenclud@112
   351
        /// <summary>
StephaneLenclud@112
   352
        /// Device Removed
StephaneLenclud@112
   353
        /// </summary>
StephaneLenclud@112
   354
        public void OnDeviceRemoved([MarshalAs(UnmanagedType.LPWStr)] string deviceId) { }
StephaneLenclud@112
   355
StephaneLenclud@112
   356
        /// <summary>
StephaneLenclud@112
   357
        /// Default Device Changed
StephaneLenclud@112
   358
        /// </summary>
StephaneLenclud@112
   359
        public void OnDefaultDeviceChanged(DataFlow flow, Role role, [MarshalAs(UnmanagedType.LPWStr)] string defaultDeviceId)
StephaneLenclud@112
   360
        {
StephaneLenclud@112
   361
            if (role == Role.Multimedia && flow == DataFlow.Render)
StephaneLenclud@112
   362
            {
StephaneLenclud@112
   363
                UpdateAudioDeviceAndMasterVolumeThreadSafe();
StephaneLenclud@112
   364
            }
StephaneLenclud@112
   365
        }
StephaneLenclud@112
   366
StephaneLenclud@112
   367
        /// <summary>
StephaneLenclud@112
   368
        /// Property Value Changed
StephaneLenclud@112
   369
        /// </summary>
StephaneLenclud@112
   370
        /// <param name="pwstrDeviceId"></param>
StephaneLenclud@112
   371
        /// <param name="key"></param>
StephaneLenclud@112
   372
        public void OnPropertyValueChanged([MarshalAs(UnmanagedType.LPWStr)] string pwstrDeviceId, PropertyKey key){}
StephaneLenclud@112
   373
StephaneLenclud@112
   374
StephaneLenclud@112
   375
        
StephaneLenclud@112
   376
StephaneLenclud@112
   377
		/// <summary>
StephaneLenclud@116
   378
		/// Update master volume indicators based our current system states.
StephaneLenclud@116
   379
		/// This typically includes volume levels and mute status.
StephaneLenclud@112
   380
		/// </summary>
StephaneLenclud@112
   381
		private void UpdateMasterVolumeThreadSafe()
StephaneLenclud@112
   382
		{
StephaneLenclud@112
   383
			if (this.InvokeRequired)
StephaneLenclud@112
   384
			{
StephaneLenclud@112
   385
				//Not in the proper thread, invoke ourselves
StephaneLenclud@112
   386
				PlainUpdateDelegate d = new PlainUpdateDelegate(UpdateMasterVolumeThreadSafe);
StephaneLenclud@112
   387
				this.Invoke(d, new object[] { });
StephaneLenclud@112
   388
				return;
StephaneLenclud@112
   389
			}
StephaneLenclud@112
   390
StephaneLenclud@113
   391
			//Update volume slider
StephaneLenclud@112
   392
			float volumeLevelScalar = iMultiMediaDevice.AudioEndpointVolume.MasterVolumeLevelScalar;
StephaneLenclud@112
   393
			trackBarMasterVolume.Value = Convert.ToInt32(volumeLevelScalar * 100);
StephaneLenclud@113
   394
			//Update mute checkbox
StephaneLenclud@113
   395
			checkBoxMute.Checked = iMultiMediaDevice.AudioEndpointVolume.Mute;
StephaneLenclud@112
   396
StephaneLenclud@116
   397
			//If our display connection is open we need to update its icons
StephaneLenclud@112
   398
			if (iDisplay.IsOpen())
StephaneLenclud@112
   399
			{
StephaneLenclud@116
   400
				//First take care our our volume level icons
StephaneLenclud@135
   401
                int volumeIconCount = iDisplay.IconCount(MiniDisplay.IconType.Volume);
StephaneLenclud@112
   402
				if (volumeIconCount > 0)
StephaneLenclud@114
   403
				{					
StephaneLenclud@116
   404
					//Compute current volume level from system level and the number of segments in our display volume bar.
StephaneLenclud@116
   405
					//That tells us how many segments in our volume bar needs to be turned on.
StephaneLenclud@116
   406
					float currentVolume = volumeLevelScalar * volumeIconCount;
StephaneLenclud@116
   407
					int segmentOnCount = Convert.ToInt32(currentVolume);
StephaneLenclud@116
   408
					//Check if our segment count was rounded up, this will later be used for half brightness segment
StephaneLenclud@116
   409
					bool roundedUp = segmentOnCount > currentVolume;
StephaneLenclud@114
   410
StephaneLenclud@112
   411
					for (int i = 0; i < volumeIconCount; i++)
StephaneLenclud@112
   412
					{
StephaneLenclud@116
   413
						if (i < segmentOnCount)
StephaneLenclud@112
   414
						{
StephaneLenclud@116
   415
							//If we are dealing with our last segment and our segment count was rounded up then we will use half brightness.
StephaneLenclud@116
   416
							if (i == segmentOnCount - 1 && roundedUp)
StephaneLenclud@114
   417
							{
StephaneLenclud@114
   418
								//Half brightness
StephaneLenclud@135
   419
                                iDisplay.SetIconStatus(MiniDisplay.IconType.Volume, i, (iDisplay.IconStatusCount(MiniDisplay.IconType.Volume) - 1) / 2);
StephaneLenclud@114
   420
							}
StephaneLenclud@114
   421
							else
StephaneLenclud@114
   422
							{
StephaneLenclud@114
   423
								//Full brightness
StephaneLenclud@135
   424
                                iDisplay.SetIconStatus(MiniDisplay.IconType.Volume, i, iDisplay.IconStatusCount(MiniDisplay.IconType.Volume) - 1);
StephaneLenclud@114
   425
							}
StephaneLenclud@112
   426
						}
StephaneLenclud@112
   427
						else
StephaneLenclud@112
   428
						{
StephaneLenclud@135
   429
                            iDisplay.SetIconStatus(MiniDisplay.IconType.Volume, i, 0);
StephaneLenclud@112
   430
						}
StephaneLenclud@112
   431
					}
StephaneLenclud@112
   432
				}
StephaneLenclud@113
   433
StephaneLenclud@116
   434
				//Take care our our mute icon
StephaneLenclud@135
   435
                iDisplay.SetIconOnOff(MiniDisplay.IconType.Mute, iMultiMediaDevice.AudioEndpointVolume.Mute);
StephaneLenclud@112
   436
			}
StephaneLenclud@112
   437
StephaneLenclud@112
   438
		}
StephaneLenclud@112
   439
StephaneLenclud@112
   440
        /// <summary>
StephaneLenclud@112
   441
        /// 
StephaneLenclud@112
   442
        /// </summary>
StephaneLenclud@112
   443
        private void UpdateAudioDeviceAndMasterVolumeThreadSafe()
StephaneLenclud@112
   444
        {
StephaneLenclud@112
   445
            if (this.InvokeRequired)
StephaneLenclud@112
   446
            {
StephaneLenclud@112
   447
                //Not in the proper thread, invoke ourselves
StephaneLenclud@112
   448
				PlainUpdateDelegate d = new PlainUpdateDelegate(UpdateAudioDeviceAndMasterVolumeThreadSafe);
StephaneLenclud@112
   449
                this.Invoke(d, new object[] { });
StephaneLenclud@112
   450
                return;
StephaneLenclud@112
   451
            }
StephaneLenclud@112
   452
            
StephaneLenclud@112
   453
            //We are in the correct thread just go ahead.
StephaneLenclud@112
   454
            try
StephaneLenclud@112
   455
            {                
StephaneLenclud@112
   456
                //Get our master volume            
StephaneLenclud@112
   457
				iMultiMediaDevice = iMultiMediaDeviceEnumerator.GetDefaultAudioEndpoint(DataFlow.Render, Role.Multimedia);
StephaneLenclud@116
   458
				//Update our label
StephaneLenclud@116
   459
				labelDefaultAudioDevice.Text = iMultiMediaDevice.FriendlyName;
StephaneLenclud@116
   460
StephaneLenclud@112
   461
                //Show our volume in our track bar
StephaneLenclud@112
   462
				UpdateMasterVolumeThreadSafe();
StephaneLenclud@112
   463
StephaneLenclud@112
   464
                //Register to get volume modifications
StephaneLenclud@112
   465
				iMultiMediaDevice.AudioEndpointVolume.OnVolumeNotification += OnVolumeNotificationThreadSafe;
StephaneLenclud@112
   466
                //
StephaneLenclud@112
   467
				trackBarMasterVolume.Enabled = true;
StephaneLenclud@112
   468
            }
StephaneLenclud@112
   469
            catch (Exception ex)
StephaneLenclud@112
   470
            {
StephaneLenclud@112
   471
                Debug.WriteLine("Exception thrown in UpdateAudioDeviceAndMasterVolume");
StephaneLenclud@112
   472
                Debug.WriteLine(ex.ToString());
StephaneLenclud@112
   473
                //Something went wrong S/PDIF device ca throw exception I guess
StephaneLenclud@112
   474
				trackBarMasterVolume.Enabled = false;
StephaneLenclud@112
   475
            }
StephaneLenclud@112
   476
        }
StephaneLenclud@104
   477
StephaneLenclud@104
   478
		/// <summary>
StephaneLenclud@104
   479
		/// 
StephaneLenclud@104
   480
		/// </summary>
StephaneLenclud@104
   481
		private void PopulateDeviceTypes()
StephaneLenclud@104
   482
		{
StephaneLenclud@104
   483
			int count = Display.TypeCount();
StephaneLenclud@104
   484
StephaneLenclud@106
   485
			for (int i = 0; i < count; i++)
StephaneLenclud@104
   486
			{
StephaneLenclud@135
   487
				comboBoxDisplayType.Items.Add(Display.TypeName((MiniDisplay.Type)i));
StephaneLenclud@106
   488
			}
StephaneLenclud@104
   489
		}
StephaneLenclud@104
   490
StephaneLenclud@152
   491
        /// <summary>
StephaneLenclud@152
   492
        /// 
StephaneLenclud@152
   493
        /// </summary>
StephaneLenclud@152
   494
        private void PopulateOpticalDrives()
StephaneLenclud@152
   495
        {
StephaneLenclud@152
   496
            //Reset our list of drives
StephaneLenclud@152
   497
            comboBoxOpticalDrives.Items.Clear();
StephaneLenclud@153
   498
            comboBoxOpticalDrives.Items.Add("None");
StephaneLenclud@152
   499
StephaneLenclud@152
   500
            //Go through each drives on our system and collected the optical ones in our list
StephaneLenclud@152
   501
            DriveInfo[] allDrives = DriveInfo.GetDrives();
StephaneLenclud@152
   502
            foreach (DriveInfo d in allDrives)
StephaneLenclud@152
   503
            {
StephaneLenclud@157
   504
                Debug.WriteLine("Drive " + d.Name);
StephaneLenclud@152
   505
                Debug.WriteLine("  Drive type: {0}", d.DriveType);
StephaneLenclud@152
   506
StephaneLenclud@152
   507
                if (d.DriveType==DriveType.CDRom)
StephaneLenclud@152
   508
                {
StephaneLenclud@152
   509
                    //This is an optical drive, add it now
StephaneLenclud@152
   510
                    comboBoxOpticalDrives.Items.Add(d.Name.Substring(0,2));
StephaneLenclud@152
   511
                }                
StephaneLenclud@153
   512
            }           
StephaneLenclud@152
   513
        }
StephaneLenclud@152
   514
StephaneLenclud@152
   515
        /// <summary>
StephaneLenclud@152
   516
        /// 
StephaneLenclud@152
   517
        /// </summary>
StephaneLenclud@152
   518
        /// <returns></returns>
StephaneLenclud@152
   519
        public string OpticalDriveToEject()
StephaneLenclud@152
   520
        {
StephaneLenclud@153
   521
            return comboBoxOpticalDrives.SelectedItem.ToString();
StephaneLenclud@152
   522
        }
StephaneLenclud@152
   523
StephaneLenclud@152
   524
StephaneLenclud@152
   525
StephaneLenclud@104
   526
		/// <summary>
sl@99
   527
		///
sl@94
   528
		/// </summary>
sl@95
   529
		private void SetupTrayIcon()
sl@95
   530
		{
sl@95
   531
			iNotifyIcon.Icon = GetIcon("vfd.ico");
sl@95
   532
			iNotifyIcon.Text = "Sharp Display Manager";
sl@95
   533
			iNotifyIcon.Visible = true;
sl@95
   534
sl@95
   535
			//Double click toggles visibility - typically brings up the application
sl@95
   536
			iNotifyIcon.DoubleClick += delegate(object obj, EventArgs args)
sl@95
   537
			{
sl@95
   538
				SysTrayHideShow();
sl@95
   539
			};
sl@95
   540
sl@95
   541
			//Adding a context menu, useful to be able to exit the application
sl@95
   542
			ContextMenu contextMenu = new ContextMenu();
sl@95
   543
			//Context menu item to toggle visibility
sl@95
   544
			MenuItem hideShowItem = new MenuItem("Hide/Show");
sl@95
   545
			hideShowItem.Click += delegate(object obj, EventArgs args)
sl@95
   546
			{
sl@95
   547
				SysTrayHideShow();
sl@95
   548
			};
sl@95
   549
			contextMenu.MenuItems.Add(hideShowItem);
sl@95
   550
sl@95
   551
			//Context menu item separator
sl@95
   552
			contextMenu.MenuItems.Add(new MenuItem("-"));
sl@95
   553
sl@95
   554
			//Context menu exit item
sl@95
   555
			MenuItem exitItem = new MenuItem("Exit");
sl@95
   556
			exitItem.Click += delegate(object obj, EventArgs args)
sl@95
   557
			{
sl@95
   558
				Application.Exit();
sl@95
   559
			};
sl@95
   560
			contextMenu.MenuItems.Add(exitItem);
sl@95
   561
sl@95
   562
			iNotifyIcon.ContextMenu = contextMenu;
sl@95
   563
		}
sl@95
   564
sl@95
   565
		/// <summary>
sl@94
   566
		/// Access icons from embedded resources.
sl@94
   567
		/// </summary>
sl@94
   568
		/// <param name="name"></param>
sl@94
   569
		/// <returns></returns>
sl@94
   570
		public static Icon GetIcon(string name)
sl@94
   571
		{
sl@94
   572
			name = "SharpDisplayManager.Resources." + name;
sl@94
   573
sl@94
   574
			string[] names =
sl@94
   575
			  Assembly.GetExecutingAssembly().GetManifestResourceNames();
sl@94
   576
			for (int i = 0; i < names.Length; i++)
sl@94
   577
			{
sl@94
   578
				if (names[i].Replace('\\', '.') == name)
sl@94
   579
				{
sl@94
   580
					using (Stream stream = Assembly.GetExecutingAssembly().
sl@94
   581
					  GetManifestResourceStream(names[i]))
sl@94
   582
					{
sl@94
   583
						return new Icon(stream);
sl@94
   584
					}
sl@94
   585
				}
sl@94
   586
			}
sl@94
   587
sl@94
   588
			return null;
sl@94
   589
		}
sl@94
   590
sl@94
   591
sl@65
   592
        /// <summary>
sl@65
   593
        /// Set our current client.
sl@65
   594
        /// This will take care of applying our client layout and set data fields.
sl@65
   595
        /// </summary>
sl@65
   596
        /// <param name="aSessionId"></param>
StephaneLenclud@141
   597
        void SetCurrentClient(string aSessionId, bool aForce=false)
sl@57
   598
        {
sl@65
   599
            if (aSessionId == iCurrentClientSessionId)
sl@57
   600
            {
sl@65
   601
                //Given client is already the current one.
sl@65
   602
                //Don't bother changing anything then.
sl@65
   603
                return;
sl@65
   604
            }
sl@57
   605
StephaneLenclud@141
   606
StephaneLenclud@141
   607
            //Check when was the last time we switched to that client
StephaneLenclud@142
   608
            if (iCurrentClientData != null)
StephaneLenclud@141
   609
            {
StephaneLenclud@142
   610
                double lastSwitchToClientSecondsAgo = (DateTime.Now - iCurrentClientData.LastSwitchTime).TotalSeconds;
StephaneLenclud@142
   611
                //TODO: put that hard coded value as a client property
StephaneLenclud@142
   612
                //Clients should be able to define how often they can be interrupted
StephaneLenclud@142
   613
                //Thus a background client can set this to zero allowing any other client to interrupt at any time
StephaneLenclud@142
   614
                //We could also compute this delay by looking at the requests frequencies?
StephaneLenclud@142
   615
                if (!aForce && (lastSwitchToClientSecondsAgo < 30)) //Make sure a client is on for at least 30 seconds
StephaneLenclud@142
   616
                {
StephaneLenclud@142
   617
                    //Don't switch clients too often
StephaneLenclud@142
   618
                    return;
StephaneLenclud@142
   619
                }
StephaneLenclud@141
   620
            }
StephaneLenclud@141
   621
sl@65
   622
            //Set current client ID.
sl@65
   623
            iCurrentClientSessionId = aSessionId;
StephaneLenclud@141
   624
            //Set the time we last switched to that client
StephaneLenclud@141
   625
            iClients[aSessionId].LastSwitchTime = DateTime.Now;
sl@65
   626
            //Fetch and set current client data.
sl@65
   627
            iCurrentClientData = iClients[aSessionId];
sl@65
   628
            //Apply layout and set data fields.
sl@65
   629
            UpdateTableLayoutPanel(iCurrentClientData);
sl@57
   630
        }
sl@57
   631
sl@0
   632
        private void buttonFont_Click(object sender, EventArgs e)
sl@0
   633
        {
sl@0
   634
            //fontDialog.ShowColor = true;
sl@0
   635
            //fontDialog.ShowApply = true;
sl@0
   636
            fontDialog.ShowEffects = true;
sl@99
   637
            fontDialog.Font = cds.Font;
sl@28
   638
sl@28
   639
            fontDialog.FixedPitchOnly = checkBoxFixedPitchFontOnly.Checked;
sl@28
   640
sl@0
   641
            //fontDialog.ShowHelp = true;
sl@0
   642
sl@0
   643
            //fontDlg.MaxSize = 40;
sl@0
   644
            //fontDlg.MinSize = 22;
sl@0
   645
sl@0
   646
            //fontDialog.Parent = this;
sl@0
   647
            //fontDialog.StartPosition = FormStartPosition.CenterParent;
sl@0
   648
sl@0
   649
            //DlgBox.ShowDialog(fontDialog);
sl@0
   650
sl@0
   651
            //if (fontDialog.ShowDialog(this) != DialogResult.Cancel)
sl@0
   652
            if (DlgBox.ShowDialog(fontDialog) != DialogResult.Cancel)
sl@0
   653
            {
sl@99
   654
                //Set the fonts to all our labels in our layout
sl@99
   655
                foreach (Control ctrl in tableLayoutPanel.Controls)
sl@99
   656
                {
sl@99
   657
                    if (ctrl is MarqueeLabel)
sl@99
   658
                    {
sl@99
   659
                        ((MarqueeLabel)ctrl).Font = fontDialog.Font;
sl@99
   660
                    }
sl@99
   661
                }
sl@0
   662
sl@99
   663
                //Save font settings
sl@48
   664
                cds.Font = fontDialog.Font;
sl@8
   665
                Properties.Settings.Default.Save();
sl@36
   666
                //
sl@37
   667
                CheckFontHeight();
sl@37
   668
            }
sl@37
   669
        }
sl@36
   670
sl@37
   671
        /// <summary>
sl@38
   672
        ///
sl@37
   673
        /// </summary>
sl@37
   674
        void CheckFontHeight()
sl@37
   675
        {
sl@54
   676
            //Show font height and width
sl@54
   677
            labelFontHeight.Text = "Font height: " + cds.Font.Height;
sl@54
   678
            float charWidth = IsFixedWidth(cds.Font);
sl@54
   679
            if (charWidth == 0.0f)
sl@54
   680
            {
sl@54
   681
                labelFontWidth.Visible = false;
sl@54
   682
            }
sl@54
   683
            else
sl@54
   684
            {
sl@54
   685
                labelFontWidth.Visible = true;
sl@54
   686
                labelFontWidth.Text = "Font width: " + charWidth;
sl@54
   687
            }
sl@54
   688
sl@70
   689
            MarqueeLabel label = null;
sl@68
   690
            //Get the first label control we can find
sl@68
   691
            foreach (Control ctrl in tableLayoutPanel.Controls)
sl@68
   692
            {
sl@68
   693
                if (ctrl is MarqueeLabel)
sl@68
   694
                {
sl@68
   695
                    label = (MarqueeLabel)ctrl;
sl@68
   696
                    break;
sl@68
   697
                }
sl@68
   698
            }
sl@68
   699
sl@54
   700
            //Now check font height and show a warning if needed.
sl@68
   701
            if (label != null && label.Font.Height > label.Height)
sl@37
   702
            {
sl@60
   703
                labelWarning.Text = "WARNING: Selected font is too height by " + (label.Font.Height - label.Height) + " pixels!";
sl@37
   704
                labelWarning.Visible = true;
sl@0
   705
            }
sl@37
   706
            else
sl@37
   707
            {
sl@37
   708
                labelWarning.Visible = false;
sl@37
   709
            }
sl@37
   710
sl@0
   711
        }
sl@0
   712
sl@0
   713
        private void buttonCapture_Click(object sender, EventArgs e)
sl@0
   714
        {
sl@0
   715
            System.Drawing.Bitmap bmp = new System.Drawing.Bitmap(tableLayoutPanel.Width, tableLayoutPanel.Height);
sl@0
   716
            tableLayoutPanel.DrawToBitmap(bmp, tableLayoutPanel.ClientRectangle);
sl@14
   717
            //Bitmap bmpToSave = new Bitmap(bmp);
sl@14
   718
            bmp.Save("D:\\capture.png");
sl@14
   719
sl@60
   720
            ((MarqueeLabel)tableLayoutPanel.Controls[0]).Text = "Captured";
sl@17
   721
sl@14
   722
            /*
sl@14
   723
            string outputFileName = "d:\\capture.png";
sl@14
   724
            using (MemoryStream memory = new MemoryStream())
sl@14
   725
            {
sl@14
   726
                using (FileStream fs = new FileStream(outputFileName, FileMode.OpenOrCreate, FileAccess.ReadWrite))
sl@14
   727
                {
sl@14
   728
                    bmp.Save(memory, System.Drawing.Imaging.ImageFormat.Png);
sl@14
   729
                    byte[] bytes = memory.ToArray();
sl@14
   730
                    fs.Write(bytes, 0, bytes.Length);
sl@14
   731
                }
sl@14
   732
            }
sl@14
   733
             */
sl@14
   734
sl@0
   735
        }
sl@2
   736
sl@12
   737
        private void CheckForRequestResults()
sl@12
   738
        {
sl@12
   739
            if (iDisplay.IsRequestPending())
sl@12
   740
            {
sl@12
   741
                switch (iDisplay.AttemptRequestCompletion())
sl@12
   742
                {
StephaneLenclud@135
   743
                    case MiniDisplay.Request.FirmwareRevision:
sl@51
   744
                        toolStripStatusLabelConnect.Text += " v" + iDisplay.FirmwareRevision();
sl@51
   745
                        //Issue next request then
sl@51
   746
                        iDisplay.RequestPowerSupplyStatus();
sl@51
   747
                        break;
sl@51
   748
StephaneLenclud@135
   749
                    case MiniDisplay.Request.PowerSupplyStatus:
sl@12
   750
                        if (iDisplay.PowerSupplyStatus())
sl@12
   751
                        {
sl@12
   752
                            toolStripStatusLabelPower.Text = "ON";
sl@12
   753
                        }
sl@12
   754
                        else
sl@12
   755
                        {
sl@12
   756
                            toolStripStatusLabelPower.Text = "OFF";
sl@12
   757
                        }
sl@12
   758
                        //Issue next request then
sl@12
   759
                        iDisplay.RequestDeviceId();
sl@12
   760
                        break;
sl@12
   761
StephaneLenclud@135
   762
                    case MiniDisplay.Request.DeviceId:
sl@12
   763
                        toolStripStatusLabelConnect.Text += " - " + iDisplay.DeviceId();
sl@12
   764
                        //No more request to issue
sl@12
   765
                        break;
sl@12
   766
                }
sl@12
   767
            }
sl@12
   768
        }
sl@12
   769
sl@58
   770
        public static uint ColorWhiteIsOn(int aX, int aY, uint aPixel)
sl@58
   771
        {
sl@58
   772
            if ((aPixel & 0x00FFFFFF) == 0x00FFFFFF)
sl@58
   773
            {
sl@58
   774
                return 0xFFFFFFFF;
sl@58
   775
            }
sl@58
   776
            return 0x00000000;
sl@58
   777
        }
sl@16
   778
sl@58
   779
        public static uint ColorUntouched(int aX, int aY, uint aPixel)
sl@57
   780
        {
sl@57
   781
            return aPixel;
sl@57
   782
        }
sl@57
   783
sl@58
   784
        public static uint ColorInversed(int aX, int aY, uint aPixel)
sl@57
   785
        {
sl@57
   786
            return ~aPixel;
sl@57
   787
        }
sl@57
   788
sl@58
   789
        public static uint ColorChessboard(int aX, int aY, uint aPixel)
sl@58
   790
        {
sl@58
   791
            if ((aX % 2 == 0) && (aY % 2 == 0))
sl@58
   792
            {
sl@58
   793
                return ~aPixel;
sl@58
   794
            }
sl@58
   795
            else if ((aX % 2 != 0) && (aY % 2 != 0))
sl@58
   796
            {
sl@58
   797
                return ~aPixel;
sl@58
   798
            }
sl@58
   799
            return 0x00000000;
sl@58
   800
        }
sl@58
   801
sl@16
   802
sl@16
   803
        public static int ScreenReversedX(System.Drawing.Bitmap aBmp, int aX)
sl@16
   804
        {
sl@16
   805
            return aBmp.Width - aX - 1;
sl@16
   806
        }
sl@16
   807
sl@16
   808
        public int ScreenReversedY(System.Drawing.Bitmap aBmp, int aY)
sl@16
   809
        {
sl@16
   810
            return iBmp.Height - aY - 1;
sl@16
   811
        }
sl@16
   812
sl@16
   813
        public int ScreenX(System.Drawing.Bitmap aBmp, int aX)
sl@16
   814
        {
sl@16
   815
            return aX;
sl@16
   816
        }
sl@16
   817
sl@16
   818
        public int ScreenY(System.Drawing.Bitmap aBmp, int aY)
sl@16
   819
        {
sl@16
   820
            return aY;
sl@16
   821
        }
sl@16
   822
sl@58
   823
        /// <summary>
sl@58
   824
        /// Select proper pixel delegates according to our current settings.
sl@58
   825
        /// </summary>
sl@58
   826
        private void SetupPixelDelegates()
sl@58
   827
        {
sl@59
   828
            //Select our pixel processing routine
sl@58
   829
            if (cds.InverseColors)
sl@58
   830
            {
sl@58
   831
                //iColorFx = ColorChessboard;
sl@58
   832
                iColorFx = ColorInversed;
sl@58
   833
            }
sl@58
   834
            else
sl@58
   835
            {
sl@58
   836
                iColorFx = ColorWhiteIsOn;
sl@58
   837
            }
sl@58
   838
sl@58
   839
            //Select proper coordinate translation functions
sl@58
   840
            //We used delegate/function pointer to support reverse screen without doing an extra test on each pixels
sl@58
   841
            if (cds.ReverseScreen)
sl@58
   842
            {
sl@58
   843
                iScreenX = ScreenReversedX;
sl@58
   844
                iScreenY = ScreenReversedY;
sl@58
   845
            }
sl@58
   846
            else
sl@58
   847
            {
sl@58
   848
                iScreenX = ScreenX;
sl@58
   849
                iScreenY = ScreenY;
sl@58
   850
            }
sl@58
   851
sl@58
   852
        }
sl@16
   853
sl@16
   854
        //This is our timer tick responsible to perform our render
sl@2
   855
        private void timer_Tick(object sender, EventArgs e)
sl@14
   856
        {
sl@2
   857
            //Update our animations
sl@2
   858
            DateTime NewTickTime = DateTime.Now;
sl@2
   859
StephaneLenclud@118
   860
			UpdateNetworkSignal(LastTickTime, NewTickTime);
StephaneLenclud@118
   861
sl@60
   862
            //Update animation for all our marquees
sl@68
   863
            foreach (Control ctrl in tableLayoutPanel.Controls)
sl@60
   864
            {
sl@68
   865
                if (ctrl is MarqueeLabel)
sl@68
   866
                {
sl@68
   867
                    ((MarqueeLabel)ctrl).UpdateAnimation(LastTickTime, NewTickTime);
sl@68
   868
                }
sl@60
   869
            }
sl@60
   870
sl@4
   871
            //Update our display
sl@4
   872
            if (iDisplay.IsOpen())
sl@4
   873
            {
sl@12
   874
                CheckForRequestResults();
sl@12
   875
StephaneLenclud@122
   876
				//Check if frame rendering is needed
StephaneLenclud@122
   877
				//Typically used when showing clock
StephaneLenclud@122
   878
				if (!iSkipFrameRendering)
StephaneLenclud@122
   879
				{
StephaneLenclud@122
   880
					//Draw to bitmap
StephaneLenclud@122
   881
					if (iCreateBitmap)
StephaneLenclud@122
   882
					{
StephaneLenclud@158
   883
                        iBmp = new System.Drawing.Bitmap(tableLayoutPanel.Width, tableLayoutPanel.Height, PixelFormat.Format32bppArgb);
StephaneLenclud@158
   884
                        iCreateBitmap = false;
StephaneLenclud@158
   885
                    }
StephaneLenclud@122
   886
					tableLayoutPanel.DrawToBitmap(iBmp, tableLayoutPanel.ClientRectangle);
StephaneLenclud@122
   887
					//iBmp.Save("D:\\capture.png");
sl@16
   888
StephaneLenclud@122
   889
					//Send it to our display
StephaneLenclud@122
   890
					for (int i = 0; i < iBmp.Width; i++)
StephaneLenclud@122
   891
					{
StephaneLenclud@122
   892
						for (int j = 0; j < iBmp.Height; j++)
StephaneLenclud@122
   893
						{
StephaneLenclud@122
   894
							unchecked
StephaneLenclud@122
   895
							{
StephaneLenclud@122
   896
								//Get our processed pixel coordinates
StephaneLenclud@122
   897
								int x = iScreenX(iBmp, i);
StephaneLenclud@122
   898
								int y = iScreenY(iBmp, j);
StephaneLenclud@122
   899
								//Get pixel color
StephaneLenclud@122
   900
								uint color = (uint)iBmp.GetPixel(i, j).ToArgb();
StephaneLenclud@122
   901
								//Apply color effects
StephaneLenclud@122
   902
								color = iColorFx(x, y, color);
StephaneLenclud@122
   903
								//Now set our pixel
StephaneLenclud@122
   904
								iDisplay.SetPixel(x, y, color);
StephaneLenclud@122
   905
							}
StephaneLenclud@122
   906
						}
StephaneLenclud@122
   907
					}
sl@4
   908
StephaneLenclud@122
   909
					iDisplay.SwapBuffers();
StephaneLenclud@122
   910
				}
sl@4
   911
            }
sl@8
   912
sl@8
   913
            //Compute instant FPS
sl@47
   914
            toolStripStatusLabelFps.Text = (1.0/NewTickTime.Subtract(LastTickTime).TotalSeconds).ToString("F0") + " / " + (1000/timer.Interval).ToString() + " FPS";
sl@8
   915
sl@8
   916
            LastTickTime = NewTickTime;
sl@8
   917
sl@2
   918
        }
sl@3
   919
StephaneLenclud@103
   920
		/// <summary>
StephaneLenclud@103
   921
		/// Attempt to establish connection with our display hardware.
StephaneLenclud@103
   922
		/// </summary>
sl@46
   923
        private void OpenDisplayConnection()
sl@3
   924
        {
sl@46
   925
            CloseDisplayConnection();
sl@46
   926
StephaneLenclud@135
   927
            if (!iDisplay.Open((MiniDisplay.Type)cds.DisplayType))
StephaneLenclud@104
   928
            {   
StephaneLenclud@104
   929
				UpdateStatus();               
StephaneLenclud@104
   930
				toolStripStatusLabelConnect.Text = "Connection error";
sl@7
   931
            }
sl@46
   932
        }
sl@7
   933
sl@46
   934
        private void CloseDisplayConnection()
sl@46
   935
        {
StephaneLenclud@104
   936
			//Status will be updated upon receiving the closed event
StephaneLenclud@122
   937
StephaneLenclud@122
   938
			if (iDisplay == null || !iDisplay.IsOpen())
StephaneLenclud@122
   939
			{
StephaneLenclud@122
   940
				return;
StephaneLenclud@122
   941
			}
StephaneLenclud@122
   942
StephaneLenclud@122
   943
			//Do not clear if we gave up on rendering already.
StephaneLenclud@122
   944
			//This means we will keep on displaying clock on MDM166AA for instance.
StephaneLenclud@122
   945
			if (!iSkipFrameRendering)
StephaneLenclud@122
   946
			{
StephaneLenclud@122
   947
				iDisplay.Clear();
StephaneLenclud@122
   948
				iDisplay.SwapBuffers();
StephaneLenclud@122
   949
			}
StephaneLenclud@122
   950
StephaneLenclud@122
   951
			iDisplay.SetAllIconsStatus(0); //Turn off all icons
sl@46
   952
            iDisplay.Close();
sl@46
   953
        }
sl@46
   954
sl@46
   955
        private void buttonOpen_Click(object sender, EventArgs e)
sl@46
   956
        {
sl@46
   957
            OpenDisplayConnection();
sl@3
   958
        }
sl@3
   959
sl@3
   960
        private void buttonClose_Click(object sender, EventArgs e)
sl@3
   961
        {
sl@46
   962
            CloseDisplayConnection();
sl@3
   963
        }
sl@3
   964
sl@3
   965
        private void buttonClear_Click(object sender, EventArgs e)
sl@3
   966
        {
sl@3
   967
            iDisplay.Clear();
sl@3
   968
            iDisplay.SwapBuffers();
sl@3
   969
        }
sl@3
   970
sl@3
   971
        private void buttonFill_Click(object sender, EventArgs e)
sl@3
   972
        {
sl@3
   973
            iDisplay.Fill();
sl@3
   974
            iDisplay.SwapBuffers();
sl@3
   975
        }
sl@3
   976
sl@3
   977
        private void trackBarBrightness_Scroll(object sender, EventArgs e)
sl@3
   978
        {
sl@48
   979
            cds.Brightness = trackBarBrightness.Value;
sl@9
   980
            Properties.Settings.Default.Save();
sl@3
   981
            iDisplay.SetBrightness(trackBarBrightness.Value);
sl@9
   982
sl@3
   983
        }
sl@7
   984
sl@48
   985
sl@48
   986
        /// <summary>
sl@48
   987
        /// CDS stands for Current Display Settings
sl@48
   988
        /// </summary>
sl@50
   989
        private DisplaySettings cds
sl@48
   990
        {
sl@48
   991
            get
sl@48
   992
            {
sl@65
   993
                DisplaysSettings settings = Properties.Settings.Default.DisplaysSettings;
sl@51
   994
                if (settings == null)
sl@51
   995
                {
sl@51
   996
                    settings = new DisplaysSettings();
sl@51
   997
                    settings.Init();
sl@65
   998
                    Properties.Settings.Default.DisplaysSettings = settings;
sl@51
   999
                }
sl@48
  1000
sl@48
  1001
                //Make sure all our settings have been created
sl@48
  1002
                while (settings.Displays.Count <= Properties.Settings.Default.CurrentDisplayIndex)
sl@48
  1003
                {
sl@50
  1004
                    settings.Displays.Add(new DisplaySettings());
sl@48
  1005
                }
sl@48
  1006
sl@50
  1007
                DisplaySettings displaySettings = settings.Displays[Properties.Settings.Default.CurrentDisplayIndex];
sl@48
  1008
                return displaySettings;
sl@48
  1009
            }
sl@48
  1010
        }
sl@48
  1011
sl@54
  1012
        /// <summary>
sl@54
  1013
        /// Check if the given font has a fixed character pitch.
sl@54
  1014
        /// </summary>
sl@54
  1015
        /// <param name="ft"></param>
sl@54
  1016
        /// <returns>0.0f if this is not a monospace font, otherwise returns the character width.</returns>
sl@54
  1017
        public float IsFixedWidth(Font ft)
sl@54
  1018
        {
sl@54
  1019
            Graphics g = CreateGraphics();
sl@54
  1020
            char[] charSizes = new char[] { 'i', 'a', 'Z', '%', '#', 'a', 'B', 'l', 'm', ',', '.' };
sl@54
  1021
            float charWidth = g.MeasureString("I", ft, Int32.MaxValue, StringFormat.GenericTypographic).Width;
sl@54
  1022
sl@54
  1023
            bool fixedWidth = true;
sl@54
  1024
sl@54
  1025
            foreach (char c in charSizes)
sl@54
  1026
                if (g.MeasureString(c.ToString(), ft, Int32.MaxValue, StringFormat.GenericTypographic).Width != charWidth)
sl@54
  1027
                    fixedWidth = false;
sl@54
  1028
sl@54
  1029
            if (fixedWidth)
sl@54
  1030
            {
sl@54
  1031
                return charWidth;
sl@54
  1032
            }
sl@54
  1033
sl@54
  1034
            return 0.0f;
sl@54
  1035
        }
sl@54
  1036
StephaneLenclud@103
  1037
		/// <summary>
StephaneLenclud@103
  1038
		/// Synchronize UI with settings
StephaneLenclud@103
  1039
		/// </summary>
sl@7
  1040
        private void UpdateStatus()
StephaneLenclud@103
  1041
        {            
sl@48
  1042
            //Load settings
sl@54
  1043
            checkBoxShowBorders.Checked = cds.ShowBorders;
sl@54
  1044
            tableLayoutPanel.CellBorderStyle = (cds.ShowBorders ? TableLayoutPanelCellBorderStyle.Single : TableLayoutPanelCellBorderStyle.None);
sl@60
  1045
sl@60
  1046
            //Set the proper font to each of our labels
sl@60
  1047
            foreach (MarqueeLabel ctrl in tableLayoutPanel.Controls)
sl@60
  1048
            {
sl@60
  1049
                ctrl.Font = cds.Font;
sl@60
  1050
            }
sl@60
  1051
sl@54
  1052
            CheckFontHeight();
sl@96
  1053
			//Check if "run on Windows startup" is enabled
sl@96
  1054
			checkBoxAutoStart.Checked = iStartupManager.Startup;
sl@96
  1055
			//
sl@48
  1056
            checkBoxConnectOnStartup.Checked = Properties.Settings.Default.DisplayConnectOnStartup;
sl@94
  1057
			checkBoxMinimizeToTray.Checked = Properties.Settings.Default.MinimizeToTray;
sl@94
  1058
			checkBoxStartMinimized.Checked = Properties.Settings.Default.StartMinimized;
StephaneLenclud@126
  1059
			labelStartFileName.Text = Properties.Settings.Default.StartFileName;
StephaneLenclud@153
  1060
StephaneLenclud@153
  1061
            //Try find our drive in our drive list
StephaneLenclud@153
  1062
            int opticalDriveItemIndex=0;
StephaneLenclud@153
  1063
            bool driveNotFound = true;
StephaneLenclud@153
  1064
            string opticalDriveToEject=Properties.Settings.Default.OpticalDriveToEject;
StephaneLenclud@153
  1065
            foreach (object item in comboBoxOpticalDrives.Items)
StephaneLenclud@153
  1066
            {
StephaneLenclud@154
  1067
                if (opticalDriveToEject == item.ToString())
StephaneLenclud@153
  1068
                {
StephaneLenclud@153
  1069
                    comboBoxOpticalDrives.SelectedIndex = opticalDriveItemIndex;
StephaneLenclud@153
  1070
                    driveNotFound = false;
StephaneLenclud@153
  1071
                    break;
StephaneLenclud@153
  1072
                }
StephaneLenclud@153
  1073
                opticalDriveItemIndex++;
StephaneLenclud@153
  1074
            }
StephaneLenclud@153
  1075
StephaneLenclud@153
  1076
            if (driveNotFound)
StephaneLenclud@153
  1077
            {
StephaneLenclud@153
  1078
                //We could not find the drive we had saved.
StephaneLenclud@153
  1079
                //Select "None" then.
StephaneLenclud@153
  1080
                comboBoxOpticalDrives.SelectedIndex = 0;
StephaneLenclud@153
  1081
            }
StephaneLenclud@153
  1082
StephaneLenclud@153
  1083
StephaneLenclud@153
  1084
sl@48
  1085
            checkBoxReverseScreen.Checked = cds.ReverseScreen;
sl@57
  1086
            checkBoxInverseColors.Checked = cds.InverseColors;
StephaneLenclud@115
  1087
			checkBoxShowVolumeLabel.Checked = cds.ShowVolumeLabel;
sl@100
  1088
            checkBoxScaleToFit.Checked = cds.ScaleToFit;
sl@100
  1089
            maskedTextBoxMinFontSize.Enabled = cds.ScaleToFit;
sl@100
  1090
            labelMinFontSize.Enabled = cds.ScaleToFit;
sl@100
  1091
            maskedTextBoxMinFontSize.Text = cds.MinFontSize.ToString();
StephaneLenclud@106
  1092
			maskedTextBoxScrollingSpeed.Text = cds.ScrollingSpeedInPixelsPerSecond.ToString();
sl@48
  1093
            comboBoxDisplayType.SelectedIndex = cds.DisplayType;
sl@48
  1094
            timer.Interval = cds.TimerInterval;
sl@48
  1095
            maskedTextBoxTimerInterval.Text = cds.TimerInterval.ToString();
sl@100
  1096
            textBoxScrollLoopSeparator.Text = cds.Separator;
sl@58
  1097
            //
sl@58
  1098
            SetupPixelDelegates();
sl@48
  1099
sl@7
  1100
            if (iDisplay.IsOpen())
sl@7
  1101
            {
StephaneLenclud@103
  1102
				//We have a display connection
StephaneLenclud@103
  1103
				//Reflect that in our UI
StephaneLenclud@103
  1104
StephaneLenclud@103
  1105
				tableLayoutPanel.Enabled = true;
StephaneLenclud@105
  1106
				panelDisplay.Enabled = true;
StephaneLenclud@103
  1107
sl@48
  1108
                //Only setup brightness if display is open
sl@48
  1109
                trackBarBrightness.Minimum = iDisplay.MinBrightness();
sl@48
  1110
                trackBarBrightness.Maximum = iDisplay.MaxBrightness();
StephaneLenclud@105
  1111
				if (cds.Brightness < iDisplay.MinBrightness() || cds.Brightness > iDisplay.MaxBrightness())
StephaneLenclud@105
  1112
				{
StephaneLenclud@105
  1113
					//Brightness out of range, this can occur when using auto-detect
StephaneLenclud@105
  1114
					//Use max brightness instead
StephaneLenclud@105
  1115
					trackBarBrightness.Value = iDisplay.MaxBrightness();
StephaneLenclud@105
  1116
					iDisplay.SetBrightness(iDisplay.MaxBrightness());
StephaneLenclud@105
  1117
				}
StephaneLenclud@105
  1118
				else
StephaneLenclud@105
  1119
				{
StephaneLenclud@105
  1120
					trackBarBrightness.Value = cds.Brightness;
StephaneLenclud@105
  1121
					iDisplay.SetBrightness(cds.Brightness);
StephaneLenclud@105
  1122
				}
StephaneLenclud@105
  1123
StephaneLenclud@105
  1124
				//Try compute the steps to something that makes sense
sl@48
  1125
                trackBarBrightness.LargeChange = Math.Max(1, (iDisplay.MaxBrightness() - iDisplay.MinBrightness()) / 5);
sl@48
  1126
                trackBarBrightness.SmallChange = 1;
StephaneLenclud@105
  1127
                
sl@48
  1128
                //
sl@7
  1129
                buttonFill.Enabled = true;
sl@7
  1130
                buttonClear.Enabled = true;
sl@7
  1131
                buttonOpen.Enabled = false;
sl@7
  1132
                buttonClose.Enabled = true;
sl@7
  1133
                trackBarBrightness.Enabled = true;
sl@10
  1134
                toolStripStatusLabelConnect.Text = "Connected - " + iDisplay.Vendor() + " - " + iDisplay.Product();
sl@10
  1135
                //+ " - " + iDisplay.SerialNumber();
sl@52
  1136
sl@52
  1137
                if (iDisplay.SupportPowerOnOff())
sl@52
  1138
                {
sl@52
  1139
                    buttonPowerOn.Enabled = true;
sl@52
  1140
                    buttonPowerOff.Enabled = true;
sl@52
  1141
                }
sl@52
  1142
                else
sl@52
  1143
                {
sl@52
  1144
                    buttonPowerOn.Enabled = false;
sl@52
  1145
                    buttonPowerOff.Enabled = false;
sl@52
  1146
                }
sl@53
  1147
sl@53
  1148
                if (iDisplay.SupportClock())
sl@53
  1149
                {
sl@53
  1150
                    buttonShowClock.Enabled = true;
sl@53
  1151
                    buttonHideClock.Enabled = true;
sl@53
  1152
                }
sl@53
  1153
                else
sl@53
  1154
                {
sl@53
  1155
                    buttonShowClock.Enabled = false;
sl@53
  1156
                    buttonHideClock.Enabled = false;
sl@53
  1157
                }
StephaneLenclud@115
  1158
StephaneLenclud@115
  1159
				
StephaneLenclud@115
  1160
				//Check if Volume Label is supported. To date only MDM166AA supports that crap :)
StephaneLenclud@135
  1161
				checkBoxShowVolumeLabel.Enabled = iDisplay.IconCount(MiniDisplay.IconType.VolumeLabel)>0;
StephaneLenclud@115
  1162
StephaneLenclud@115
  1163
				if (cds.ShowVolumeLabel)
StephaneLenclud@115
  1164
				{
StephaneLenclud@135
  1165
                    iDisplay.SetIconOn(MiniDisplay.IconType.VolumeLabel);
StephaneLenclud@115
  1166
				}
StephaneLenclud@115
  1167
				else
StephaneLenclud@115
  1168
				{
StephaneLenclud@135
  1169
                    iDisplay.SetIconOff(MiniDisplay.IconType.VolumeLabel);
StephaneLenclud@115
  1170
				}
sl@7
  1171
            }
sl@7
  1172
            else
sl@7
  1173
            {
StephaneLenclud@103
  1174
				//Display is connection not available
StephaneLenclud@103
  1175
				//Reflect that in our UI
StephaneLenclud@115
  1176
				checkBoxShowVolumeLabel.Enabled = false;
StephaneLenclud@103
  1177
				tableLayoutPanel.Enabled = false;
StephaneLenclud@105
  1178
				panelDisplay.Enabled = false;
sl@7
  1179
                buttonFill.Enabled = false;
sl@7
  1180
                buttonClear.Enabled = false;
sl@7
  1181
                buttonOpen.Enabled = true;
sl@7
  1182
                buttonClose.Enabled = false;
sl@7
  1183
                trackBarBrightness.Enabled = false;
sl@52
  1184
                buttonPowerOn.Enabled = false;
sl@52
  1185
                buttonPowerOff.Enabled = false;
sl@53
  1186
                buttonShowClock.Enabled = false;
sl@53
  1187
                buttonHideClock.Enabled = false;
sl@9
  1188
                toolStripStatusLabelConnect.Text = "Disconnected";
sl@48
  1189
                toolStripStatusLabelPower.Text = "N/A";
sl@7
  1190
            }
StephaneLenclud@106
  1191
sl@7
  1192
        }
sl@9
  1193
sl@13
  1194
StephaneLenclud@115
  1195
		/// <summary>
StephaneLenclud@115
  1196
		/// 
StephaneLenclud@115
  1197
		/// </summary>
StephaneLenclud@115
  1198
		/// <param name="sender"></param>
StephaneLenclud@115
  1199
		/// <param name="e"></param>
StephaneLenclud@115
  1200
		private void checkBoxShowVolumeLabel_CheckedChanged(object sender, EventArgs e)
StephaneLenclud@115
  1201
		{
StephaneLenclud@115
  1202
			cds.ShowVolumeLabel = checkBoxShowVolumeLabel.Checked;
StephaneLenclud@115
  1203
			Properties.Settings.Default.Save();
StephaneLenclud@115
  1204
			UpdateStatus();
StephaneLenclud@115
  1205
		}
sl@13
  1206
sl@9
  1207
        private void checkBoxShowBorders_CheckedChanged(object sender, EventArgs e)
sl@9
  1208
        {
sl@16
  1209
            //Save our show borders setting
sl@13
  1210
            tableLayoutPanel.CellBorderStyle = (checkBoxShowBorders.Checked ? TableLayoutPanelCellBorderStyle.Single : TableLayoutPanelCellBorderStyle.None);
sl@48
  1211
            cds.ShowBorders = checkBoxShowBorders.Checked;
sl@9
  1212
            Properties.Settings.Default.Save();
sl@57
  1213
            CheckFontHeight();
sl@9
  1214
        }
sl@13
  1215
sl@13
  1216
        private void checkBoxConnectOnStartup_CheckedChanged(object sender, EventArgs e)
sl@13
  1217
        {
sl@16
  1218
            //Save our connect on startup setting
sl@13
  1219
            Properties.Settings.Default.DisplayConnectOnStartup = checkBoxConnectOnStartup.Checked;
sl@13
  1220
            Properties.Settings.Default.Save();
sl@13
  1221
        }
sl@13
  1222
sl@94
  1223
		private void checkBoxMinimizeToTray_CheckedChanged(object sender, EventArgs e)
sl@94
  1224
		{
sl@94
  1225
			//Save our "Minimize to tray" setting
sl@94
  1226
			Properties.Settings.Default.MinimizeToTray = checkBoxMinimizeToTray.Checked;
sl@94
  1227
			Properties.Settings.Default.Save();
sl@94
  1228
sl@94
  1229
		}
sl@94
  1230
sl@94
  1231
		private void checkBoxStartMinimized_CheckedChanged(object sender, EventArgs e)
sl@94
  1232
		{
sl@94
  1233
			//Save our "Start minimized" setting
sl@94
  1234
			Properties.Settings.Default.StartMinimized = checkBoxStartMinimized.Checked;
sl@94
  1235
			Properties.Settings.Default.Save();
sl@94
  1236
		}
sl@94
  1237
sl@94
  1238
		private void checkBoxAutoStart_CheckedChanged(object sender, EventArgs e)
sl@94
  1239
		{
sl@94
  1240
			iStartupManager.Startup = checkBoxAutoStart.Checked;
sl@94
  1241
		}
sl@94
  1242
sl@94
  1243
sl@16
  1244
        private void checkBoxReverseScreen_CheckedChanged(object sender, EventArgs e)
sl@16
  1245
        {
sl@16
  1246
            //Save our reverse screen setting
sl@48
  1247
            cds.ReverseScreen = checkBoxReverseScreen.Checked;
sl@16
  1248
            Properties.Settings.Default.Save();
sl@58
  1249
            SetupPixelDelegates();
sl@16
  1250
        }
sl@16
  1251
sl@57
  1252
        private void checkBoxInverseColors_CheckedChanged(object sender, EventArgs e)
sl@57
  1253
        {
sl@57
  1254
            //Save our inverse colors setting
sl@57
  1255
            cds.InverseColors = checkBoxInverseColors.Checked;
sl@57
  1256
            Properties.Settings.Default.Save();
sl@58
  1257
            SetupPixelDelegates();
sl@57
  1258
        }
sl@57
  1259
sl@100
  1260
        private void checkBoxScaleToFit_CheckedChanged(object sender, EventArgs e)
sl@100
  1261
        {
sl@100
  1262
            //Save our scale to fit setting
sl@100
  1263
            cds.ScaleToFit = checkBoxScaleToFit.Checked;
sl@100
  1264
            Properties.Settings.Default.Save();
sl@100
  1265
            //
sl@100
  1266
            labelMinFontSize.Enabled = cds.ScaleToFit;
sl@100
  1267
            maskedTextBoxMinFontSize.Enabled = cds.ScaleToFit;
sl@100
  1268
        }
sl@100
  1269
sl@14
  1270
        private void MainForm_Resize(object sender, EventArgs e)
sl@14
  1271
        {
sl@14
  1272
            if (WindowState == FormWindowState.Minimized)
sl@14
  1273
            {
StephaneLenclud@158
  1274
                // To workaround our empty bitmap bug on Windows 7 we need to recreate our bitmap when the application is minimized
StephaneLenclud@158
  1275
                // That's apparently not needed on Windows 10 but we better leave it in place.
sl@14
  1276
                iCreateBitmap = true;
sl@14
  1277
            }
sl@14
  1278
        }
sl@14
  1279
sl@17
  1280
        private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
sl@17
  1281
        {
StephaneLenclud@117
  1282
			iNetworkManager.Dispose();
StephaneLenclud@105
  1283
			CloseDisplayConnection();
sl@17
  1284
            StopServer();
sl@32
  1285
            e.Cancel = iClosing;
sl@17
  1286
        }
sl@17
  1287
sl@17
  1288
        public void StartServer()
sl@17
  1289
        {
sl@17
  1290
            iServiceHost = new ServiceHost
sl@17
  1291
                (
sl@55
  1292
                    typeof(Session),
sl@20
  1293
                    new Uri[] { new Uri("net.tcp://localhost:8001/") }
sl@17
  1294
                );
sl@17
  1295
sl@55
  1296
            iServiceHost.AddServiceEndpoint(typeof(IService), new NetTcpBinding(SecurityMode.None, true), "DisplayService");
sl@17
  1297
            iServiceHost.Open();
sl@17
  1298
        }
sl@17
  1299
sl@17
  1300
        public void StopServer()
sl@17
  1301
        {
sl@32
  1302
            if (iClients.Count > 0 && !iClosing)
sl@29
  1303
            {
sl@29
  1304
                //Tell our clients
sl@32
  1305
                iClosing = true;
sl@29
  1306
                BroadcastCloseEvent();
sl@29
  1307
            }
sl@32
  1308
            else if (iClosing)
sl@32
  1309
            {
sl@32
  1310
                if (MessageBox.Show("Force exit?", "Waiting for clients...", MessageBoxButtons.YesNo, MessageBoxIcon.Warning) == DialogResult.Yes)
sl@32
  1311
                {
sl@32
  1312
                    iClosing = false; //We make sure we force close if asked twice
sl@32
  1313
                }
sl@32
  1314
            }
sl@32
  1315
            else
sl@36
  1316
            {
sl@32
  1317
                //We removed that as it often lags for some reason
sl@32
  1318
                //iServiceHost.Close();
sl@32
  1319
            }
sl@17
  1320
        }
sl@17
  1321
sl@21
  1322
        public void BroadcastCloseEvent()
sl@21
  1323
        {
sl@31
  1324
            Trace.TraceInformation("BroadcastCloseEvent - start");
sl@31
  1325
sl@21
  1326
            var inactiveClients = new List<string>();
sl@21
  1327
            foreach (var client in iClients)
sl@21
  1328
            {
sl@21
  1329
                //if (client.Key != eventData.ClientName)
sl@21
  1330
                {
sl@21
  1331
                    try
sl@21
  1332
                    {
sl@31
  1333
                        Trace.TraceInformation("BroadcastCloseEvent - " + client.Key);
sl@33
  1334
                        client.Value.Callback.OnCloseOrder(/*eventData*/);
sl@21
  1335
                    }
sl@21
  1336
                    catch (Exception ex)
sl@21
  1337
                    {
sl@21
  1338
                        inactiveClients.Add(client.Key);
sl@21
  1339
                    }
sl@21
  1340
                }
sl@21
  1341
            }
sl@21
  1342
sl@21
  1343
            if (inactiveClients.Count > 0)
sl@21
  1344
            {
sl@21
  1345
                foreach (var client in inactiveClients)
sl@21
  1346
                {
sl@21
  1347
                    iClients.Remove(client);
sl@30
  1348
                    Program.iMainForm.treeViewClients.Nodes.Remove(Program.iMainForm.treeViewClients.Nodes.Find(client, false)[0]);
sl@21
  1349
                }
sl@21
  1350
            }
sl@97
  1351
sl@97
  1352
			if (iClients.Count==0)
sl@97
  1353
			{
sl@97
  1354
				ClearLayout();
sl@97
  1355
			}
sl@21
  1356
        }
sl@21
  1357
sl@97
  1358
		/// <summary>
sl@97
  1359
		/// Just remove all our fields.
sl@97
  1360
		/// </summary>
sl@97
  1361
		private void ClearLayout()
sl@97
  1362
		{
sl@97
  1363
			tableLayoutPanel.Controls.Clear();
sl@97
  1364
			tableLayoutPanel.RowStyles.Clear();
sl@97
  1365
			tableLayoutPanel.ColumnStyles.Clear();
StephaneLenclud@122
  1366
			iCurrentClientData = null;
sl@97
  1367
		}
sl@97
  1368
StephaneLenclud@106
  1369
		/// <summary>
StephaneLenclud@106
  1370
		/// Just launch a demo client.
StephaneLenclud@106
  1371
		/// </summary>
StephaneLenclud@106
  1372
		private void StartNewClient(string aTopText = "", string aBottomText = "")
StephaneLenclud@106
  1373
		{
StephaneLenclud@106
  1374
			Thread clientThread = new Thread(SharpDisplayClient.Program.MainWithParams);
StephaneLenclud@106
  1375
			SharpDisplayClient.StartParams myParams = new SharpDisplayClient.StartParams(new Point(this.Right, this.Top),aTopText,aBottomText);
StephaneLenclud@106
  1376
			clientThread.Start(myParams);
StephaneLenclud@106
  1377
			BringToFront();
StephaneLenclud@106
  1378
		}
StephaneLenclud@106
  1379
sl@25
  1380
        private void buttonStartClient_Click(object sender, EventArgs e)
sl@25
  1381
        {
StephaneLenclud@106
  1382
			StartNewClient();
sl@25
  1383
        }
sl@25
  1384
sl@27
  1385
        private void buttonSuspend_Click(object sender, EventArgs e)
sl@27
  1386
        {
sl@52
  1387
            LastTickTime = DateTime.Now; //Reset timer to prevent jump
sl@27
  1388
            timer.Enabled = !timer.Enabled;
sl@27
  1389
            if (!timer.Enabled)
sl@27
  1390
            {
sl@52
  1391
                buttonSuspend.Text = "Run";
sl@27
  1392
            }
sl@27
  1393
            else
sl@27
  1394
            {
sl@27
  1395
                buttonSuspend.Text = "Pause";
sl@27
  1396
            }
sl@27
  1397
        }
sl@27
  1398
sl@29
  1399
        private void buttonCloseClients_Click(object sender, EventArgs e)
sl@29
  1400
        {
sl@29
  1401
            BroadcastCloseEvent();
sl@29
  1402
        }
sl@29
  1403
sl@30
  1404
        private void treeViewClients_AfterSelect(object sender, TreeViewEventArgs e)
sl@30
  1405
        {
StephaneLenclud@141
  1406
            //Root node must have at least one child
StephaneLenclud@141
  1407
            if (e.Node.Nodes.Count == 0)
StephaneLenclud@141
  1408
            {
StephaneLenclud@141
  1409
                return;
StephaneLenclud@141
  1410
            }
sl@21
  1411
StephaneLenclud@141
  1412
            //If the selected node is the root node of a client then switch to it
StephaneLenclud@141
  1413
            string sessionId=e.Node.Nodes[0].Text; //First child of a root node is the sessionId
StephaneLenclud@141
  1414
            if (iClients.ContainsKey(sessionId)) //Check that's actually what we are looking at
StephaneLenclud@141
  1415
            {
StephaneLenclud@141
  1416
                //We have a valid session just switch to that client
StephaneLenclud@141
  1417
                SetCurrentClient(sessionId,true);
StephaneLenclud@141
  1418
            }
StephaneLenclud@141
  1419
            
sl@30
  1420
        }
sl@30
  1421
sl@36
  1422
sl@30
  1423
        /// <summary>
sl@36
  1424
        ///
sl@30
  1425
        /// </summary>
sl@30
  1426
        /// <param name="aSessionId"></param>
sl@30
  1427
        /// <param name="aCallback"></param>
sl@55
  1428
        public void AddClientThreadSafe(string aSessionId, ICallback aCallback)
sl@30
  1429
        {
sl@33
  1430
            if (this.InvokeRequired)
sl@30
  1431
            {
sl@30
  1432
                //Not in the proper thread, invoke ourselves
sl@30
  1433
                AddClientDelegate d = new AddClientDelegate(AddClientThreadSafe);
sl@30
  1434
                this.Invoke(d, new object[] { aSessionId, aCallback });
sl@30
  1435
            }
sl@30
  1436
            else
sl@30
  1437
            {
sl@30
  1438
                //We are in the proper thread
sl@30
  1439
                //Add this session to our collection of clients
sl@33
  1440
                ClientData newClient = new ClientData(aSessionId, aCallback);
sl@33
  1441
                Program.iMainForm.iClients.Add(aSessionId, newClient);
sl@30
  1442
                //Add this session to our UI
sl@33
  1443
                UpdateClientTreeViewNode(newClient);
sl@30
  1444
            }
sl@30
  1445
        }
sl@30
  1446
sl@30
  1447
        /// <summary>
sl@36
  1448
        ///
sl@30
  1449
        /// </summary>
sl@30
  1450
        /// <param name="aSessionId"></param>
sl@30
  1451
        public void RemoveClientThreadSafe(string aSessionId)
sl@30
  1452
        {
sl@33
  1453
            if (this.InvokeRequired)
sl@30
  1454
            {
sl@30
  1455
                //Not in the proper thread, invoke ourselves
sl@30
  1456
                RemoveClientDelegate d = new RemoveClientDelegate(RemoveClientThreadSafe);
sl@30
  1457
                this.Invoke(d, new object[] { aSessionId });
sl@30
  1458
            }
sl@30
  1459
            else
sl@30
  1460
            {
sl@30
  1461
                //We are in the proper thread
sl@33
  1462
                //Remove this session from both client collection and UI tree view
sl@30
  1463
                if (Program.iMainForm.iClients.Keys.Contains(aSessionId))
sl@30
  1464
                {
sl@30
  1465
                    Program.iMainForm.iClients.Remove(aSessionId);
sl@30
  1466
                    Program.iMainForm.treeViewClients.Nodes.Remove(Program.iMainForm.treeViewClients.Nodes.Find(aSessionId, false)[0]);
sl@32
  1467
                }
sl@32
  1468
sl@97
  1469
				if (iClients.Count == 0)
sl@97
  1470
				{
sl@97
  1471
					//Clear our screen when last client disconnects
sl@97
  1472
					ClearLayout();
sl@97
  1473
sl@97
  1474
					if (iClosing)
sl@97
  1475
					{
sl@97
  1476
						//We were closing our form
sl@97
  1477
						//All clients are now closed
sl@97
  1478
						//Just resume our close operation
sl@97
  1479
						iClosing = false;
sl@97
  1480
						Close();
sl@97
  1481
					}
sl@97
  1482
				}
sl@30
  1483
            }
sl@30
  1484
        }
sl@30
  1485
sl@30
  1486
        /// <summary>
sl@36
  1487
        ///
sl@30
  1488
        /// </summary>
sl@62
  1489
        /// <param name="aSessionId"></param>
sl@72
  1490
        /// <param name="aLayout"></param>
sl@62
  1491
        public void SetClientLayoutThreadSafe(string aSessionId, TableLayout aLayout)
sl@62
  1492
        {
sl@62
  1493
            if (this.InvokeRequired)
sl@62
  1494
            {
sl@62
  1495
                //Not in the proper thread, invoke ourselves
sl@62
  1496
                SetLayoutDelegate d = new SetLayoutDelegate(SetClientLayoutThreadSafe);
sl@62
  1497
                this.Invoke(d, new object[] { aSessionId, aLayout });
sl@62
  1498
            }
sl@62
  1499
            else
sl@62
  1500
            {
sl@62
  1501
                ClientData client = iClients[aSessionId];
sl@62
  1502
                if (client != null)
sl@62
  1503
                {
StephaneLenclud@148
  1504
                    //Don't change a thing if the layout is the same
StephaneLenclud@148
  1505
                    if (!client.Layout.IsSameAs(aLayout))
StephaneLenclud@148
  1506
                    {
StephaneLenclud@158
  1507
                        Debug.Print("SetClientLayoutThreadSafe: Layout updated.");
StephaneLenclud@148
  1508
                        //Set our client layout then
StephaneLenclud@148
  1509
                        client.Layout = aLayout;
StephaneLenclud@158
  1510
                        //Layout has changed clear our fields then
StephaneLenclud@158
  1511
                        client.Fields.Clear();
StephaneLenclud@148
  1512
                        //
StephaneLenclud@148
  1513
                        UpdateClientTreeViewNode(client);
StephaneLenclud@148
  1514
                    }
StephaneLenclud@158
  1515
                    else
StephaneLenclud@158
  1516
                    {
StephaneLenclud@158
  1517
                        Debug.Print("SetClientLayoutThreadSafe: Layout has not changed.");
StephaneLenclud@158
  1518
                    }
sl@62
  1519
                }
sl@62
  1520
            }
sl@62
  1521
        }
sl@62
  1522
sl@62
  1523
        /// <summary>
sl@62
  1524
        ///
sl@62
  1525
        /// </summary>
sl@67
  1526
        /// <param name="aSessionId"></param>
sl@72
  1527
        /// <param name="aField"></param>
sl@75
  1528
        public void SetClientFieldThreadSafe(string aSessionId, DataField aField)
sl@30
  1529
        {
sl@33
  1530
            if (this.InvokeRequired)
sl@30
  1531
            {
sl@30
  1532
                //Not in the proper thread, invoke ourselves
sl@79
  1533
                SetFieldDelegate d = new SetFieldDelegate(SetClientFieldThreadSafe);
sl@72
  1534
                this.Invoke(d, new object[] { aSessionId, aField });
sl@30
  1535
            }
sl@30
  1536
            else
sl@30
  1537
            {
sl@75
  1538
                //We are in the proper thread
sl@75
  1539
                //Call the non-thread-safe variant
sl@75
  1540
                SetClientField(aSessionId, aField);
sl@75
  1541
            }
sl@75
  1542
        }
sl@75
  1543
sl@75
  1544
        /// <summary>
sl@79
  1545
        ///
sl@75
  1546
        /// </summary>
sl@75
  1547
        /// <param name="aSessionId"></param>
sl@75
  1548
        /// <param name="aField"></param>
sl@75
  1549
        private void SetClientField(string aSessionId, DataField aField)
StephaneLenclud@141
  1550
        {   
StephaneLenclud@141
  1551
            //TODO: should check if the field actually changed?
StephaneLenclud@141
  1552
sl@75
  1553
            ClientData client = iClients[aSessionId];
StephaneLenclud@141
  1554
            bool layoutChanged = false;
StephaneLenclud@148
  1555
            bool contentChanged = true;
StephaneLenclud@141
  1556
StephaneLenclud@141
  1557
            //Make sure all our fields are in place
StephaneLenclud@141
  1558
            while (client.Fields.Count < (aField.Index + 1))
sl@75
  1559
            {
StephaneLenclud@148
  1560
                //Add a data field with proper index
StephaneLenclud@141
  1561
                client.Fields.Add(new DataField(client.Fields.Count));
StephaneLenclud@141
  1562
                layoutChanged = true;
StephaneLenclud@141
  1563
            }
sl@76
  1564
StephaneLenclud@158
  1565
            //Now that we know our fields are in place just update that one
StephaneLenclud@148
  1566
            client.Fields[aField.Index] = aField;
StephaneLenclud@148
  1567
StephaneLenclud@141
  1568
            if (client.Fields[aField.Index].IsSameLayout(aField))
StephaneLenclud@141
  1569
            {
StephaneLenclud@141
  1570
                //If we are updating a field in our current client we need to update it in our panel
StephaneLenclud@141
  1571
                if (aSessionId == iCurrentClientSessionId)
sl@30
  1572
                {
StephaneLenclud@141
  1573
                    if (aField.IsText && aField.Index < tableLayoutPanel.Controls.Count && tableLayoutPanel.Controls[aField.Index] is MarqueeLabel)
sl@79
  1574
                    {
sl@75
  1575
                        //Text field control already in place, just change the text
sl@72
  1576
                        MarqueeLabel label = (MarqueeLabel)tableLayoutPanel.Controls[aField.Index];
StephaneLenclud@148
  1577
                        contentChanged = (label.Text != aField.Text || label.TextAlign != aField.Alignment);
sl@72
  1578
                        label.Text = aField.Text;
sl@72
  1579
                        label.TextAlign = aField.Alignment;
sl@68
  1580
                    }
StephaneLenclud@141
  1581
                    else if (aField.IsBitmap && aField.Index < tableLayoutPanel.Controls.Count && tableLayoutPanel.Controls[aField.Index] is PictureBox)
sl@75
  1582
                    {
StephaneLenclud@148
  1583
                        contentChanged = true; //TODO: Bitmap comp or should we leave that to clients?
sl@75
  1584
                        //Bitmap field control already in place just change the bitmap
sl@75
  1585
                        PictureBox pictureBox = (PictureBox)tableLayoutPanel.Controls[aField.Index];
sl@75
  1586
                        pictureBox.Image = aField.Bitmap;
sl@75
  1587
                    }
sl@68
  1588
                    else
sl@68
  1589
                    {
StephaneLenclud@141
  1590
                        layoutChanged = true;
sl@68
  1591
                    }
sl@30
  1592
                }
StephaneLenclud@141
  1593
            }
StephaneLenclud@141
  1594
            else
StephaneLenclud@148
  1595
            {                
StephaneLenclud@141
  1596
                layoutChanged = true;
StephaneLenclud@141
  1597
            }
StephaneLenclud@141
  1598
StephaneLenclud@148
  1599
            //If either content or layout changed we need to update our tree view to reflect the changes
StephaneLenclud@148
  1600
            if (contentChanged || layoutChanged)
StephaneLenclud@141
  1601
            {
StephaneLenclud@141
  1602
                UpdateClientTreeViewNode(client);
StephaneLenclud@148
  1603
                //
StephaneLenclud@148
  1604
                if (layoutChanged)
sl@75
  1605
                {
StephaneLenclud@148
  1606
                    Debug.Print("Layout changed");
StephaneLenclud@148
  1607
                    //Our layout has changed, if we are already the current client we need to update our panel
StephaneLenclud@148
  1608
                    if (aSessionId == iCurrentClientSessionId)
StephaneLenclud@148
  1609
                    {
StephaneLenclud@148
  1610
                        //Apply layout and set data fields.
StephaneLenclud@148
  1611
                        UpdateTableLayoutPanel(iCurrentClientData);
StephaneLenclud@148
  1612
                    }
sl@75
  1613
                }
StephaneLenclud@158
  1614
                else
StephaneLenclud@158
  1615
                {
StephaneLenclud@158
  1616
                    Debug.Print("Layout has not changed.");
StephaneLenclud@158
  1617
                }
StephaneLenclud@158
  1618
            }
StephaneLenclud@158
  1619
            else
StephaneLenclud@158
  1620
            {
StephaneLenclud@158
  1621
                Debug.Print("WARNING: content and layout have not changed!");
StephaneLenclud@141
  1622
            }
sl@75
  1623
StephaneLenclud@141
  1624
            //When a client field is set we try switching to this client to present the new information to our user
StephaneLenclud@141
  1625
            SetCurrentClient(aSessionId);
sl@30
  1626
        }
sl@30
  1627
sl@30
  1628
        /// <summary>
sl@36
  1629
        ///
sl@30
  1630
        /// </summary>
sl@30
  1631
        /// <param name="aTexts"></param>
sl@75
  1632
        public void SetClientFieldsThreadSafe(string aSessionId, System.Collections.Generic.IList<DataField> aFields)
sl@30
  1633
        {
sl@33
  1634
            if (this.InvokeRequired)
sl@30
  1635
            {
sl@30
  1636
                //Not in the proper thread, invoke ourselves
sl@75
  1637
                SetFieldsDelegate d = new SetFieldsDelegate(SetClientFieldsThreadSafe);
sl@72
  1638
                this.Invoke(d, new object[] { aSessionId, aFields });
sl@30
  1639
            }
sl@30
  1640
            else
sl@30
  1641
            {
sl@75
  1642
                //Put each our text fields in a label control
sl@75
  1643
                foreach (DataField field in aFields)
sl@30
  1644
                {
sl@75
  1645
                    SetClientField(aSessionId, field);
sl@30
  1646
                }
sl@30
  1647
            }
sl@32
  1648
        }
sl@30
  1649
sl@67
  1650
        /// <summary>
sl@67
  1651
        ///
sl@67
  1652
        /// </summary>
sl@67
  1653
        /// <param name="aSessionId"></param>
sl@32
  1654
        /// <param name="aName"></param>
sl@32
  1655
        public void SetClientNameThreadSafe(string aSessionId, string aName)
sl@32
  1656
        {
sl@32
  1657
            if (this.InvokeRequired)
sl@32
  1658
            {
sl@32
  1659
                //Not in the proper thread, invoke ourselves
sl@32
  1660
                SetClientNameDelegate d = new SetClientNameDelegate(SetClientNameThreadSafe);
sl@32
  1661
                this.Invoke(d, new object[] { aSessionId, aName });
sl@32
  1662
            }
sl@32
  1663
            else
sl@32
  1664
            {
sl@32
  1665
                //We are in the proper thread
sl@33
  1666
                //Get our client
sl@33
  1667
                ClientData client = iClients[aSessionId];
sl@33
  1668
                if (client != null)
sl@32
  1669
                {
sl@33
  1670
                    //Set its name
sl@33
  1671
                    client.Name = aName;
sl@33
  1672
                    //Update our tree-view
sl@33
  1673
                    UpdateClientTreeViewNode(client);
sl@33
  1674
                }
sl@33
  1675
            }
sl@33
  1676
        }
sl@33
  1677
sl@33
  1678
        /// <summary>
sl@36
  1679
        ///
sl@33
  1680
        /// </summary>
sl@33
  1681
        /// <param name="aClient"></param>
sl@33
  1682
        private void UpdateClientTreeViewNode(ClientData aClient)
sl@33
  1683
        {
StephaneLenclud@148
  1684
            Debug.Print("UpdateClientTreeViewNode");
StephaneLenclud@148
  1685
sl@33
  1686
            if (aClient == null)
sl@33
  1687
            {
sl@33
  1688
                return;
sl@33
  1689
            }
sl@33
  1690
sl@33
  1691
            TreeNode node = null;
sl@33
  1692
            //Check that our client node already exists
sl@33
  1693
            //Get our client root node using its key which is our session ID
sl@33
  1694
            TreeNode[] nodes = treeViewClients.Nodes.Find(aClient.SessionId, false);
sl@33
  1695
            if (nodes.Count()>0)
sl@33
  1696
            {
sl@33
  1697
                //We already have a node for that client
sl@33
  1698
                node = nodes[0];
sl@33
  1699
                //Clear children as we are going to recreate them below
sl@33
  1700
                node.Nodes.Clear();
sl@33
  1701
            }
sl@33
  1702
            else
sl@33
  1703
            {
sl@33
  1704
                //Client node does not exists create a new one
sl@33
  1705
                treeViewClients.Nodes.Add(aClient.SessionId, aClient.SessionId);
sl@33
  1706
                node = treeViewClients.Nodes.Find(aClient.SessionId, false)[0];
sl@33
  1707
            }
sl@33
  1708
sl@33
  1709
            if (node != null)
sl@33
  1710
            {
sl@33
  1711
                //Change its name
sl@33
  1712
                if (aClient.Name != "")
sl@33
  1713
                {
sl@33
  1714
                    //We have a name, us it as text for our root node
sl@33
  1715
                    node.Text = aClient.Name;
sl@32
  1716
                    //Add a child with SessionId
sl@33
  1717
                    node.Nodes.Add(new TreeNode(aClient.SessionId));
sl@33
  1718
                }
sl@33
  1719
                else
sl@33
  1720
                {
sl@33
  1721
                    //No name, use session ID instead
sl@33
  1722
                    node.Text = aClient.SessionId;
sl@33
  1723
                }
sl@36
  1724
sl@67
  1725
                if (aClient.Fields.Count > 0)
sl@33
  1726
                {
sl@33
  1727
                    //Create root node for our texts
sl@70
  1728
                    TreeNode textsRoot = new TreeNode("Fields");
sl@33
  1729
                    node.Nodes.Add(textsRoot);
sl@33
  1730
                    //For each text add a new entry
sl@67
  1731
                    foreach (DataField field in aClient.Fields)
sl@33
  1732
                    {
sl@75
  1733
                        if (!field.IsBitmap)
sl@67
  1734
                        {
sl@72
  1735
                            DataField textField = (DataField)field;
sl@70
  1736
                            textsRoot.Nodes.Add(new TreeNode("[Text]" + textField.Text));
sl@67
  1737
                        }
sl@67
  1738
                        else
sl@67
  1739
                        {
sl@72
  1740
                            textsRoot.Nodes.Add(new TreeNode("[Bitmap]"));
sl@70
  1741
                        }
sl@33
  1742
                    }
sl@32
  1743
                }
sl@34
  1744
sl@34
  1745
                node.ExpandAll();
sl@32
  1746
            }
sl@30
  1747
        }
sl@17
  1748
sl@60
  1749
        /// <summary>
sl@60
  1750
        /// Update our table layout row styles to make sure each rows have similar height
sl@60
  1751
        /// </summary>
sl@60
  1752
        private void UpdateTableLayoutRowStyles()
sl@60
  1753
        {
sl@60
  1754
            foreach (RowStyle rowStyle in tableLayoutPanel.RowStyles)
sl@60
  1755
            {
sl@60
  1756
                rowStyle.SizeType = SizeType.Percent;
sl@60
  1757
                rowStyle.Height = 100 / tableLayoutPanel.RowCount;
sl@60
  1758
            }
sl@60
  1759
        }
sl@60
  1760
sl@63
  1761
        /// <summary>
sl@63
  1762
        /// Update our display table layout.
sl@63
  1763
        /// </summary>
sl@63
  1764
        /// <param name="aLayout"></param>
sl@65
  1765
        private void UpdateTableLayoutPanel(ClientData aClient)
sl@63
  1766
        {
StephaneLenclud@148
  1767
            Debug.Print("UpdateClientTreeViewNode");
StephaneLenclud@148
  1768
StephaneLenclud@106
  1769
			if (aClient == null)
StephaneLenclud@106
  1770
			{
StephaneLenclud@106
  1771
				//Just drop it
StephaneLenclud@106
  1772
				return;
StephaneLenclud@106
  1773
			}
StephaneLenclud@106
  1774
StephaneLenclud@106
  1775
sl@65
  1776
            TableLayout layout = aClient.Layout;
sl@70
  1777
            int fieldCount = 0;
sl@70
  1778
StephaneLenclud@141
  1779
            //First clean our current panel
sl@63
  1780
            tableLayoutPanel.Controls.Clear();
sl@63
  1781
            tableLayoutPanel.RowStyles.Clear();
sl@63
  1782
            tableLayoutPanel.ColumnStyles.Clear();
sl@63
  1783
            tableLayoutPanel.RowCount = 0;
sl@63
  1784
            tableLayoutPanel.ColumnCount = 0;
sl@63
  1785
sl@65
  1786
            while (tableLayoutPanel.RowCount < layout.Rows.Count)
sl@63
  1787
            {
sl@63
  1788
                tableLayoutPanel.RowCount++;
sl@63
  1789
            }
sl@63
  1790
sl@65
  1791
            while (tableLayoutPanel.ColumnCount < layout.Columns.Count)
sl@63
  1792
            {
sl@63
  1793
                tableLayoutPanel.ColumnCount++;
sl@63
  1794
            }
sl@63
  1795
sl@63
  1796
            for (int i = 0; i < tableLayoutPanel.ColumnCount; i++)
sl@63
  1797
            {
sl@63
  1798
                //Create our column styles
sl@65
  1799
                this.tableLayoutPanel.ColumnStyles.Add(layout.Columns[i]);
sl@63
  1800
sl@63
  1801
                for (int j = 0; j < tableLayoutPanel.RowCount; j++)
sl@63
  1802
                {
sl@63
  1803
                    if (i == 0)
sl@63
  1804
                    {
sl@63
  1805
                        //Create our row styles
sl@65
  1806
                        this.tableLayoutPanel.RowStyles.Add(layout.Rows[j]);
sl@63
  1807
                    }
sl@63
  1808
sl@70
  1809
                    //Check if we already have a control
sl@70
  1810
                    Control existingControl = tableLayoutPanel.GetControlFromPosition(i,j);
sl@70
  1811
                    if (existingControl!=null)
sl@70
  1812
                    {
sl@70
  1813
                        //We already have a control in that cell as a results of row/col spanning
sl@70
  1814
                        //Move on to next cell then
sl@70
  1815
                        continue;
sl@70
  1816
                    }
sl@70
  1817
sl@70
  1818
                    fieldCount++;
sl@70
  1819
sl@69
  1820
                    //Check if a client field already exists for that cell
sl@69
  1821
                    if (aClient.Fields.Count <= tableLayoutPanel.Controls.Count)
sl@65
  1822
                    {
sl@69
  1823
                        //No client field specified, create a text field by default
sl@72
  1824
                        aClient.Fields.Add(new DataField(aClient.Fields.Count));
sl@65
  1825
                    }
sl@68
  1826
sl@69
  1827
                    //Create a control corresponding to the field specified for that cell
sl@70
  1828
                    DataField field = aClient.Fields[tableLayoutPanel.Controls.Count];
sl@70
  1829
                    Control control = CreateControlForDataField(field);
sl@70
  1830
sl@69
  1831
                    //Add newly created control to our table layout at the specified row and column
sl@63
  1832
                    tableLayoutPanel.Controls.Add(control, i, j);
sl@70
  1833
                    //Make sure we specify row and column span for that new control
sl@70
  1834
                    tableLayoutPanel.SetRowSpan(control,field.RowSpan);
sl@70
  1835
                    tableLayoutPanel.SetColumnSpan(control, field.ColumnSpan);
sl@63
  1836
                }
sl@63
  1837
            }
sl@63
  1838
sl@70
  1839
            //
sl@70
  1840
            while (aClient.Fields.Count > fieldCount)
sl@70
  1841
            {
sl@70
  1842
                //We have too much fields for this layout
sl@70
  1843
                //Just discard them until we get there
sl@70
  1844
                aClient.Fields.RemoveAt(aClient.Fields.Count-1);
sl@70
  1845
            }
sl@70
  1846
sl@63
  1847
            CheckFontHeight();
sl@63
  1848
        }
sl@63
  1849
sl@68
  1850
        /// <summary>
sl@70
  1851
        /// Check our type of data field and create corresponding control
sl@68
  1852
        /// </summary>
sl@68
  1853
        /// <param name="aField"></param>
sl@69
  1854
        private Control CreateControlForDataField(DataField aField)
sl@68
  1855
        {
sl@68
  1856
            Control control=null;
sl@75
  1857
            if (!aField.IsBitmap)
sl@68
  1858
            {
sl@68
  1859
                MarqueeLabel label = new SharpDisplayManager.MarqueeLabel();
sl@68
  1860
                label.AutoEllipsis = true;
sl@68
  1861
                label.AutoSize = true;
sl@68
  1862
                label.BackColor = System.Drawing.Color.Transparent;
sl@68
  1863
                label.Dock = System.Windows.Forms.DockStyle.Fill;
sl@68
  1864
                label.Location = new System.Drawing.Point(1, 1);
sl@68
  1865
                label.Margin = new System.Windows.Forms.Padding(0);
sl@68
  1866
                label.Name = "marqueeLabel" + aField.Index;
sl@68
  1867
                label.OwnTimer = false;
StephaneLenclud@106
  1868
                label.PixelsPerSecond = cds.ScrollingSpeedInPixelsPerSecond;
sl@100
  1869
                label.Separator = cds.Separator;
sl@100
  1870
                label.MinFontSize = cds.MinFontSize;
sl@100
  1871
                label.ScaleToFit = cds.ScaleToFit;
sl@68
  1872
                //control.Size = new System.Drawing.Size(254, 30);
sl@68
  1873
                //control.TabIndex = 2;
sl@68
  1874
                label.Font = cds.Font;
sl@68
  1875
StephaneLenclud@106
  1876
				label.TextAlign = aField.Alignment;
sl@68
  1877
                label.UseCompatibleTextRendering = true;
sl@72
  1878
                label.Text = aField.Text;
sl@68
  1879
                //
sl@68
  1880
                control = label;
sl@68
  1881
            }
sl@72
  1882
            else
sl@68
  1883
            {
sl@68
  1884
                //Create picture box
sl@68
  1885
                PictureBox picture = new PictureBox();
sl@68
  1886
                picture.AutoSize = true;
sl@68
  1887
                picture.BackColor = System.Drawing.Color.Transparent;
sl@68
  1888
                picture.Dock = System.Windows.Forms.DockStyle.Fill;
sl@68
  1889
                picture.Location = new System.Drawing.Point(1, 1);
sl@68
  1890
                picture.Margin = new System.Windows.Forms.Padding(0);
sl@68
  1891
                picture.Name = "pictureBox" + aField;
sl@68
  1892
                //Set our image
sl@72
  1893
                picture.Image = aField.Bitmap;
sl@68
  1894
                //
sl@68
  1895
                control = picture;
sl@68
  1896
            }
sl@68
  1897
sl@69
  1898
            return control;
sl@68
  1899
        }
sl@68
  1900
StephaneLenclud@103
  1901
		/// <summary>
StephaneLenclud@103
  1902
		/// Called when the user selected a new display type.
StephaneLenclud@103
  1903
		/// </summary>
StephaneLenclud@103
  1904
		/// <param name="sender"></param>
StephaneLenclud@103
  1905
		/// <param name="e"></param>
sl@46
  1906
        private void comboBoxDisplayType_SelectedIndexChanged(object sender, EventArgs e)
sl@46
  1907
        {
StephaneLenclud@103
  1908
			//Store the selected display type in our settings
sl@48
  1909
            Properties.Settings.Default.CurrentDisplayIndex = comboBoxDisplayType.SelectedIndex;
sl@48
  1910
            cds.DisplayType = comboBoxDisplayType.SelectedIndex;
sl@46
  1911
            Properties.Settings.Default.Save();
StephaneLenclud@103
  1912
StephaneLenclud@103
  1913
			//Try re-opening the display connection if we were already connected.
StephaneLenclud@103
  1914
			//Otherwise just update our status to reflect display type change.
sl@51
  1915
            if (iDisplay.IsOpen())
sl@51
  1916
            {
sl@51
  1917
                OpenDisplayConnection();
sl@51
  1918
            }
sl@51
  1919
            else
sl@51
  1920
            {
sl@51
  1921
                UpdateStatus();
sl@51
  1922
            }
sl@46
  1923
        }
sl@46
  1924
sl@47
  1925
        private void maskedTextBoxTimerInterval_TextChanged(object sender, EventArgs e)
sl@47
  1926
        {
sl@47
  1927
            if (maskedTextBoxTimerInterval.Text != "")
sl@47
  1928
            {
sl@51
  1929
                int interval = Convert.ToInt32(maskedTextBoxTimerInterval.Text);
sl@51
  1930
sl@51
  1931
                if (interval > 0)
sl@51
  1932
                {
sl@51
  1933
                    timer.Interval = interval;
sl@51
  1934
                    cds.TimerInterval = timer.Interval;
sl@51
  1935
                    Properties.Settings.Default.Save();
sl@51
  1936
                }
sl@47
  1937
            }
sl@47
  1938
        }
sl@47
  1939
sl@100
  1940
        private void maskedTextBoxMinFontSize_TextChanged(object sender, EventArgs e)
sl@100
  1941
        {
sl@100
  1942
            if (maskedTextBoxMinFontSize.Text != "")
sl@100
  1943
            {
sl@100
  1944
                int minFontSize = Convert.ToInt32(maskedTextBoxMinFontSize.Text);
sl@100
  1945
sl@100
  1946
                if (minFontSize > 0)
sl@100
  1947
                {
sl@100
  1948
                    cds.MinFontSize = minFontSize;
sl@100
  1949
                    Properties.Settings.Default.Save();
StephaneLenclud@106
  1950
					//We need to recreate our layout for that change to take effect
StephaneLenclud@106
  1951
					UpdateTableLayoutPanel(iCurrentClientData);
sl@100
  1952
                }
sl@100
  1953
            }
sl@100
  1954
        }
sl@100
  1955
StephaneLenclud@106
  1956
StephaneLenclud@106
  1957
		private void maskedTextBoxScrollingSpeed_TextChanged(object sender, EventArgs e)
StephaneLenclud@106
  1958
		{
StephaneLenclud@106
  1959
			if (maskedTextBoxScrollingSpeed.Text != "")
StephaneLenclud@106
  1960
			{
StephaneLenclud@106
  1961
				int scrollingSpeed = Convert.ToInt32(maskedTextBoxScrollingSpeed.Text);
StephaneLenclud@106
  1962
StephaneLenclud@106
  1963
				if (scrollingSpeed > 0)
StephaneLenclud@106
  1964
				{
StephaneLenclud@106
  1965
					cds.ScrollingSpeedInPixelsPerSecond = scrollingSpeed;
StephaneLenclud@106
  1966
					Properties.Settings.Default.Save();
StephaneLenclud@106
  1967
					//We need to recreate our layout for that change to take effect
StephaneLenclud@106
  1968
					UpdateTableLayoutPanel(iCurrentClientData);
StephaneLenclud@106
  1969
				}
StephaneLenclud@106
  1970
			}
StephaneLenclud@106
  1971
		}
StephaneLenclud@106
  1972
sl@100
  1973
        private void textBoxScrollLoopSeparator_TextChanged(object sender, EventArgs e)
sl@100
  1974
        {
sl@100
  1975
            cds.Separator = textBoxScrollLoopSeparator.Text;
sl@100
  1976
            Properties.Settings.Default.Save();
StephaneLenclud@110
  1977
StephaneLenclud@110
  1978
			//Update our text fields
StephaneLenclud@110
  1979
			foreach (MarqueeLabel ctrl in tableLayoutPanel.Controls)
StephaneLenclud@110
  1980
			{
StephaneLenclud@110
  1981
				ctrl.Separator = cds.Separator;
StephaneLenclud@110
  1982
			}
StephaneLenclud@110
  1983
sl@100
  1984
        }
sl@100
  1985
sl@52
  1986
        private void buttonPowerOn_Click(object sender, EventArgs e)
sl@52
  1987
        {
sl@52
  1988
            iDisplay.PowerOn();
sl@52
  1989
        }
sl@52
  1990
sl@52
  1991
        private void buttonPowerOff_Click(object sender, EventArgs e)
sl@52
  1992
        {
sl@52
  1993
            iDisplay.PowerOff();
sl@52
  1994
        }
sl@52
  1995
sl@53
  1996
        private void buttonShowClock_Click(object sender, EventArgs e)
sl@53
  1997
        {
StephaneLenclud@122
  1998
			ShowClock();
sl@53
  1999
        }
sl@53
  2000
sl@53
  2001
        private void buttonHideClock_Click(object sender, EventArgs e)
sl@53
  2002
        {
StephaneLenclud@122
  2003
			HideClock();
sl@53
  2004
        }
sl@88
  2005
sl@88
  2006
        private void buttonUpdate_Click(object sender, EventArgs e)
sl@88
  2007
        {
sl@88
  2008
            InstallUpdateSyncWithInfo();
sl@88
  2009
        }
sl@88
  2010
StephaneLenclud@122
  2011
		/// <summary>
StephaneLenclud@122
  2012
		/// 
StephaneLenclud@122
  2013
		/// </summary>
StephaneLenclud@122
  2014
		void ShowClock()
StephaneLenclud@122
  2015
		{
StephaneLenclud@122
  2016
			if (!iDisplay.IsOpen())
StephaneLenclud@122
  2017
			{
StephaneLenclud@122
  2018
				return;
StephaneLenclud@122
  2019
			}
StephaneLenclud@122
  2020
StephaneLenclud@122
  2021
			//Devices like MDM166AA don't support windowing and frame rendering must be stopped while showing our clock
StephaneLenclud@122
  2022
			iSkipFrameRendering = true;
StephaneLenclud@122
  2023
			//Clear our screen 
StephaneLenclud@122
  2024
			iDisplay.Clear();
StephaneLenclud@122
  2025
			iDisplay.SwapBuffers();
StephaneLenclud@122
  2026
			//Then show our clock
StephaneLenclud@122
  2027
			iDisplay.ShowClock();
StephaneLenclud@122
  2028
		}
StephaneLenclud@122
  2029
StephaneLenclud@122
  2030
		/// <summary>
StephaneLenclud@122
  2031
		/// 
StephaneLenclud@122
  2032
		/// </summary>
StephaneLenclud@122
  2033
		void HideClock()
StephaneLenclud@122
  2034
		{
StephaneLenclud@122
  2035
			if (!iDisplay.IsOpen())
StephaneLenclud@122
  2036
			{
StephaneLenclud@122
  2037
				return;
StephaneLenclud@122
  2038
			}
StephaneLenclud@122
  2039
StephaneLenclud@122
  2040
			//Devices like MDM166AA don't support windowing and frame rendering must be stopped while showing our clock
StephaneLenclud@122
  2041
			iSkipFrameRendering = false;
StephaneLenclud@122
  2042
			iDisplay.HideClock();
StephaneLenclud@122
  2043
		}
sl@88
  2044
sl@88
  2045
        private void InstallUpdateSyncWithInfo()
sl@88
  2046
        {
sl@88
  2047
            UpdateCheckInfo info = null;
sl@88
  2048
sl@88
  2049
            if (ApplicationDeployment.IsNetworkDeployed)
sl@88
  2050
            {
sl@88
  2051
                ApplicationDeployment ad = ApplicationDeployment.CurrentDeployment;
sl@88
  2052
sl@88
  2053
                try
sl@88
  2054
                {
sl@88
  2055
                    info = ad.CheckForDetailedUpdate();
sl@88
  2056
sl@88
  2057
                }
sl@88
  2058
                catch (DeploymentDownloadException dde)
sl@88
  2059
                {
sl@88
  2060
                    MessageBox.Show("The new version of the application cannot be downloaded at this time. \n\nPlease check your network connection, or try again later. Error: " + dde.Message);
sl@88
  2061
                    return;
sl@88
  2062
                }
sl@88
  2063
                catch (InvalidDeploymentException ide)
sl@88
  2064
                {
sl@88
  2065
                    MessageBox.Show("Cannot check for a new version of the application. The ClickOnce deployment is corrupt. Please redeploy the application and try again. Error: " + ide.Message);
sl@88
  2066
                    return;
sl@88
  2067
                }
sl@88
  2068
                catch (InvalidOperationException ioe)
sl@88
  2069
                {
sl@88
  2070
                    MessageBox.Show("This application cannot be updated. It is likely not a ClickOnce application. Error: " + ioe.Message);
sl@88
  2071
                    return;
sl@88
  2072
                }
sl@88
  2073
sl@90
  2074
				if (info.UpdateAvailable)
sl@90
  2075
				{
sl@90
  2076
					Boolean doUpdate = true;
sl@88
  2077
sl@90
  2078
					if (!info.IsUpdateRequired)
sl@90
  2079
					{
sl@90
  2080
						DialogResult dr = MessageBox.Show("An update is available. Would you like to update the application now?", "Update Available", MessageBoxButtons.OKCancel);
sl@90
  2081
						if (!(DialogResult.OK == dr))
sl@90
  2082
						{
sl@90
  2083
							doUpdate = false;
sl@90
  2084
						}
sl@90
  2085
					}
sl@90
  2086
					else
sl@90
  2087
					{
sl@90
  2088
						// Display a message that the app MUST reboot. Display the minimum required version.
sl@90
  2089
						MessageBox.Show("This application has detected a mandatory update from your current " +
sl@90
  2090
							"version to version " + info.MinimumRequiredVersion.ToString() +
sl@90
  2091
							". The application will now install the update and restart.",
sl@90
  2092
							"Update Available", MessageBoxButtons.OK,
sl@90
  2093
							MessageBoxIcon.Information);
sl@90
  2094
					}
sl@88
  2095
sl@90
  2096
					if (doUpdate)
sl@90
  2097
					{
sl@90
  2098
						try
sl@90
  2099
						{
sl@90
  2100
							ad.Update();
sl@90
  2101
							MessageBox.Show("The application has been upgraded, and will now restart.");
sl@90
  2102
							Application.Restart();
sl@90
  2103
						}
sl@90
  2104
						catch (DeploymentDownloadException dde)
sl@90
  2105
						{
sl@90
  2106
							MessageBox.Show("Cannot install the latest version of the application. \n\nPlease check your network connection, or try again later. Error: " + dde);
sl@90
  2107
							return;
sl@90
  2108
						}
sl@90
  2109
					}
sl@90
  2110
				}
sl@90
  2111
				else
sl@90
  2112
				{
sl@90
  2113
					MessageBox.Show("You are already running the latest version.", "Application up-to-date");
sl@90
  2114
				}
sl@88
  2115
            }
sl@88
  2116
        }
sl@92
  2117
sl@94
  2118
sl@94
  2119
		/// <summary>
sl@99
  2120
		/// Used to
sl@94
  2121
		/// </summary>
sl@94
  2122
		private void SysTrayHideShow()
sl@92
  2123
		{
sl@94
  2124
			Visible = !Visible;
sl@94
  2125
			if (Visible)
sl@94
  2126
			{
sl@94
  2127
				Activate();
sl@94
  2128
				WindowState = FormWindowState.Normal;
sl@94
  2129
			}
sl@92
  2130
		}
sl@94
  2131
sl@94
  2132
		/// <summary>
sl@94
  2133
		/// Use to handle minimize events.
sl@94
  2134
		/// </summary>
sl@94
  2135
		/// <param name="sender"></param>
sl@94
  2136
		/// <param name="e"></param>
sl@94
  2137
		private void MainForm_SizeChanged(object sender, EventArgs e)
sl@94
  2138
		{
sl@94
  2139
			if (WindowState == FormWindowState.Minimized && Properties.Settings.Default.MinimizeToTray)
sl@94
  2140
			{
sl@94
  2141
				if (Visible)
sl@94
  2142
				{
sl@94
  2143
					SysTrayHideShow();
sl@94
  2144
				}
sl@94
  2145
			}
sl@94
  2146
		}
sl@94
  2147
StephaneLenclud@105
  2148
		/// <summary>
StephaneLenclud@105
  2149
		/// 
StephaneLenclud@105
  2150
		/// </summary>
StephaneLenclud@105
  2151
		/// <param name="sender"></param>
StephaneLenclud@105
  2152
		/// <param name="e"></param>
StephaneLenclud@105
  2153
		private void tableLayoutPanel_SizeChanged(object sender, EventArgs e)
StephaneLenclud@105
  2154
		{
StephaneLenclud@105
  2155
			//Our table layout size has changed which means our display size has changed.
StephaneLenclud@105
  2156
			//We need to re-create our bitmap.
StephaneLenclud@105
  2157
			iCreateBitmap = true;
StephaneLenclud@105
  2158
		}
StephaneLenclud@126
  2159
StephaneLenclud@126
  2160
		/// <summary>
StephaneLenclud@126
  2161
		/// 
StephaneLenclud@126
  2162
		/// </summary>
StephaneLenclud@126
  2163
		/// <param name="sender"></param>
StephaneLenclud@126
  2164
		/// <param name="e"></param>
StephaneLenclud@126
  2165
		private void buttonSelectFile_Click(object sender, EventArgs e)
StephaneLenclud@126
  2166
		{
StephaneLenclud@126
  2167
			//openFileDialog1.InitialDirectory = "c:\\";
StephaneLenclud@126
  2168
			//openFileDialog.Filter = "EXE files (*.exe)|*.exe|All files (*.*)|*.*";
StephaneLenclud@126
  2169
			//openFileDialog.FilterIndex = 1;
StephaneLenclud@126
  2170
			openFileDialog.RestoreDirectory = true;
StephaneLenclud@126
  2171
StephaneLenclud@126
  2172
			if (DlgBox.ShowDialog(openFileDialog) == DialogResult.OK)
StephaneLenclud@126
  2173
			{
StephaneLenclud@126
  2174
				labelStartFileName.Text = openFileDialog.FileName;
StephaneLenclud@126
  2175
				Properties.Settings.Default.StartFileName = openFileDialog.FileName;
StephaneLenclud@126
  2176
				Properties.Settings.Default.Save();
StephaneLenclud@126
  2177
			}
StephaneLenclud@126
  2178
		}
StephaneLenclud@153
  2179
StephaneLenclud@153
  2180
        /// <summary>
StephaneLenclud@153
  2181
        /// 
StephaneLenclud@153
  2182
        /// </summary>
StephaneLenclud@153
  2183
        /// <param name="sender"></param>
StephaneLenclud@153
  2184
        /// <param name="e"></param>
StephaneLenclud@153
  2185
        private void comboBoxOpticalDrives_SelectedIndexChanged(object sender, EventArgs e)
StephaneLenclud@153
  2186
        {
StephaneLenclud@153
  2187
            //Save the optical drive the user selected for ejection
StephaneLenclud@153
  2188
            Properties.Settings.Default.OpticalDriveToEject = comboBoxOpticalDrives.SelectedItem.ToString();
StephaneLenclud@153
  2189
            Properties.Settings.Default.Save();
StephaneLenclud@153
  2190
        }
sl@0
  2191
    }
sl@0
  2192
}