WingMan – Blame information for rev 36

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 office 1 using System;
2 using System.Collections.Generic;
14 office 3 using System.Collections.ObjectModel;
24 office 4 using System.ComponentModel;
1 office 5 using System.Drawing;
9 office 6 using System.IO;
1 office 7 using System.Linq;
8 using System.Net;
7 office 9 using System.Threading;
6 office 10 using System.Threading.Tasks;
1 office 11 using System.Windows.Forms;
4 office 12 using Gma.System.MouseKeyHook;
9 office 13 using MQTTnet.Extensions.ManagedClient;
14 using MQTTnet.Server;
23 office 15 using WingMan.AutoCompletion;
14 office 16 using WingMan.Bindings;
1 office 17 using WingMan.Communication;
32 office 18 using WingMan.Discovery;
7 office 19 using WingMan.Lobby;
5 office 20 using WingMan.Properties;
8 office 21 using WingMan.Utilities;
1 office 22  
23 namespace WingMan
24 {
2 office 25 public partial class WingManForm : Form
1 office 26 {
36 office 27 private const int TabControlDetachPixelOffset = 20;
33 office 28  
5 office 29 public WingManForm()
30 {
31 InitializeComponent();
1 office 32  
6 office 33 FormTaskScheduler = TaskScheduler.FromCurrentSynchronizationContext();
7 office 34 FormCancellationTokenSource = new CancellationTokenSource();
1 office 35  
32 office 36 // Set up discovery.
37 Discovery = new Discovery.Discovery(FormCancellationTokenSource, FormTaskScheduler);
38 Discovery.OnPortMapFailed += OnDiscoveryPortMapFailed;
39  
23 office 40 // Set up autocompletion.
41 AutoCompletion = new AutoCompletion.AutoCompletion(FormTaskScheduler, FormCancellationTokenSource.Token);
42 AutoCompletion.OnSaveFailed += AutoCompletionOnSaveFailed;
43 AutoCompletion.OnLoadFailed += AutoCompletionOnLoadFailed;
44  
45 Task.Run(() => AutoCompletion.Load(Address.Name, Address.AutoCompleteCustomSource));
46 Task.Run(() => AutoCompletion.Load(Port.Name, Address.AutoCompleteCustomSource));
47 Task.Run(() => AutoCompletion.Load(Nick.Name, Nick.AutoCompleteCustomSource));
36 office 48 Task.Run(() => AutoCompletion.Load(LocalNameTextBox.Name, LocalNameTextBox.AutoCompleteCustomSource));
23 office 49  
9 office 50 MqttCommunication = new MqttCommunication(FormTaskScheduler, FormCancellationTokenSource.Token);
51 MqttCommunication.OnClientAuthenticationFailed += OnMqttClientAuthenticationFailed;
52 MqttCommunication.OnClientConnectionFailed += OnMqttClientConnectionFailed;
53 MqttCommunication.OnClientDisconnected += OnMqttClientDisconnected;
54 MqttCommunication.OnClientConnected += OnMqttClientConnected;
55 MqttCommunication.OnServerAuthenticationFailed += OnMqttServerAuthenticationFailed;
56 MqttCommunication.OnServerStopped += OnMqttServerStopped;
57 MqttCommunication.OnServerStarted += OnMqttServerStarted;
58 MqttCommunication.OnServerClientConnected += OnMqttServerClientConnected;
59 MqttCommunication.OnServerClientDisconnected += OnMqttServerClientDisconnected;
6 office 60  
10 office 61 LocalKeyBindings = new LocalKeyBindings(new List<KeyBinding>());
14 office 62 RemoteKeyBindings = new RemoteKeyBindings(new ObservableCollection<RemoteKeyBinding>());
1 office 63  
24 office 64 LocalCheckedListBoxBindingSource = new BindingSource
5 office 65 {
10 office 66 DataSource = LocalKeyBindings.Bindings
5 office 67 };
24 office 68 LocalCheckedListBoxBindingSource.ListChanged += LocalCheckedListBoxBindingSourceOnListChanged;
36 office 69  
24 office 70 LocalBindingsCheckedListBox.DisplayMember = "DisplayName";
71 LocalBindingsCheckedListBox.ValueMember = "Keys";
72 LocalBindingsCheckedListBox.DataSource = LocalCheckedListBoxBindingSource;
1 office 73  
10 office 74 KeyBindingsExchange = new KeyBindingsExchange
5 office 75 {
10 office 76 ExchangeBindings = new List<KeyBindingExchange>()
7 office 77 };
78  
9 office 79 RemoteBindingsComboBoxSource = new BindingSource
7 office 80 {
10 office 81 DataSource = KeyBindingsExchange.ExchangeBindings
5 office 82 };
36 office 83 RemoteBindingsComboBoxSource.ListChanged += RemoteBindingsComboBoxSourceOnListChanged;
84  
9 office 85 RemoteBindingsComboBox.DisplayMember = "Nick";
10 office 86 RemoteBindingsComboBox.ValueMember = "KeyBindings";
9 office 87 RemoteBindingsComboBox.DataSource = RemoteBindingsComboBoxSource;
4 office 88  
36 office 89  
5 office 90 // Start lobby message synchronizer.
9 office 91 LobbyMessageSynchronizer = new LobbyMessageSynchronizer(MqttCommunication, FormTaskScheduler,
7 office 92 FormCancellationTokenSource.Token);
5 office 93 LobbyMessageSynchronizer.OnLobbyMessageReceived += OnLobbyMessageReceived;
4 office 94  
5 office 95 // Start mouse key bindings synchronizer.
10 office 96 KeyBindingsSynchronizer = new KeyBindingsSynchronizer(LocalKeyBindings, MqttCommunication,
7 office 97 FormTaskScheduler, FormCancellationTokenSource.Token);
10 office 98 KeyBindingsSynchronizer.OnMouseKeyBindingsSynchronized += OnMouseKeyBindingsSynchronized;
9 office 99  
10 office 100 // Start mouse key interceptor.
101 KeyInterceptor = new KeyInterceptor(RemoteKeyBindings, MqttCommunication, FormTaskScheduler,
102 FormCancellationTokenSource.Token);
103 KeyInterceptor.OnMouseKeyBindingMatched += OnMouseKeyBindingMatched;
104  
105 // Start mouse key simulator.
106 KeySimulator = new KeySimulator(LocalKeyBindings, MqttCommunication, FormTaskScheduler,
107 FormCancellationTokenSource.Token);
108 KeySimulator.OnMouseKeyBindingExecuting += OnMouseKeyBindingExecuting;
5 office 109 }
4 office 110  
32 office 111 private static Discovery.Discovery Discovery { get; set; }
23 office 112 private static AutoCompletion.AutoCompletion AutoCompletion { get; set; }
7 office 113 private static CancellationTokenSource FormCancellationTokenSource { get; set; }
4 office 114  
7 office 115 private static TaskScheduler FormTaskScheduler { get; set; }
4 office 116  
7 office 117 private static IKeyboardMouseEvents MouseKeyApplicationHook { get; set; }
4 office 118  
7 office 119 private List<string> MouseKeyCombo { get; set; }
2 office 120  
36 office 121 public LocalKeyBindings LocalKeyBindings { get; set; }
4 office 122  
10 office 123 private RemoteKeyBindings RemoteKeyBindings { get; }
4 office 124  
24 office 125 private BindingSource LocalCheckedListBoxBindingSource { get; }
4 office 126  
9 office 127 private BindingSource RemoteBindingsComboBoxSource { get; }
128  
10 office 129 private KeyBindingsExchange KeyBindingsExchange { get; }
7 office 130  
9 office 131 public MqttCommunication MqttCommunication { get; set; }
7 office 132  
133 public LobbyMessageSynchronizer LobbyMessageSynchronizer { get; set; }
134  
10 office 135 public KeyBindingsSynchronizer KeyBindingsSynchronizer { get; set; }
7 office 136  
10 office 137 public KeyInterceptor KeyInterceptor { get; set; }
138  
139 public KeySimulator KeySimulator { get; set; }
140  
36 office 141 private static Point TabControlClickStartPosition { get; set; }
33 office 142 private static TabControl DetachedTabControl { get; set; }
143 private static Form DetachedForm { get; set; }
144  
36 office 145 private void RemoteBindingsComboBoxSourceOnListChanged(object sender, ListChangedEventArgs e)
146 {
147 UpdateRemoteBindingsListBox();
148 }
149  
32 office 150 public void OnDiscoveryPortMapFailed(object sender, DiscoveryFailedEventArgs args)
151 {
152 switch (args.Type)
153 {
36 office 154 case DiscoveryType.Upnp:
32 office 155 ActivityTextBox.AppendText(
156 $"{Strings.Failed_to_create_UPnP_port_mapping}{Environment.NewLine}");
157 break;
36 office 158 case DiscoveryType.Pmp:
32 office 159 ActivityTextBox.AppendText(
160 $"{Strings.Failed_to_create_PMP_port_mapping}{Environment.NewLine}");
161 break;
162 }
163 }
164  
24 office 165 private void LocalCheckedListBoxBindingSourceOnListChanged(object sender, ListChangedEventArgs e)
166 {
167 // Check items
168 LocalBindingsCheckedListBox.ItemCheck -= LocalBindingsCheckedListBoxItemCheck;
169  
170 for (var i = 0; i < LocalKeyBindings.Bindings.Count; ++i)
171 switch (LocalKeyBindings.Bindings[i].Enabled)
172 {
173 case true:
174 LocalBindingsCheckedListBox.SetItemCheckState(i, CheckState.Checked);
175 break;
176 default:
177 LocalBindingsCheckedListBox.SetItemCheckState(i, CheckState.Unchecked);
178 break;
179 }
180  
181 LocalBindingsCheckedListBox.ItemCheck += LocalBindingsCheckedListBoxItemCheck;
182 }
183  
23 office 184 private void AutoCompletionOnLoadFailed(object sender, AutoCompletionFailedEventArgs args)
185 {
186 ActivityTextBox.AppendText(
187 $"{Strings.Failed_loading_autocomplete_source} : {args.Name} : {args.Exception.Message}{Environment.NewLine}");
188 }
189  
190 private void AutoCompletionOnSaveFailed(object sender, AutoCompletionFailedEventArgs args)
191 {
192 ActivityTextBox.AppendText(
193 $"{Strings.Failed_saving_autocomplete_source} : {args.Name} : {args.Exception.Message}{Environment.NewLine}");
194 }
195  
21 office 196 /// <inheritdoc />
10 office 197 /// <summary>
198 /// Clean up any resources being used.
199 /// </summary>
200 /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
201 protected override void Dispose(bool disposing)
8 office 202 {
10 office 203 FormCancellationTokenSource?.Dispose();
204 FormCancellationTokenSource = null;
9 office 205  
10 office 206 if (disposing && components != null)
9 office 207 {
10 office 208 components.Dispose();
209 components = null;
9 office 210 }
10 office 211  
212 base.Dispose(disposing);
9 office 213 }
214  
10 office 215 private void OnMouseKeyBindingExecuting(object sender, KeyBindingExecutingEventArgs args)
216 {
217 ActivityTextBox.AppendText(
218 $"{Strings.Executing_binding_from_remote_client} : {args.Nick} : {args.Name}{Environment.NewLine}");
219 }
220  
221 private void OnMouseKeyBindingMatched(object sender, KeyBindingMatchedEventArgs args)
222 {
223 ActivityTextBox.AppendText(
224 $"{Strings.Matched_remote_key_binding} : {args.Nick} : {args.Name} : {string.Join(" + ", args.KeyCombo)}{Environment.NewLine}");
225 }
226  
9 office 227 private void OnMqttServerClientDisconnected(object sender, MqttClientDisconnectedEventArgs e)
228 {
8 office 229 ActivityTextBox.AppendText(
9 office 230 $"{Strings.Client_disconnected}{Environment.NewLine}");
231 }
232  
233 private void OnMqttServerClientConnected(object sender, MqttClientConnectedEventArgs e)
234 {
235 ActivityTextBox.AppendText(
236 $"{Strings.Client_connected}{Environment.NewLine}");
237 }
238  
239 private void OnMqttServerStarted(object sender, EventArgs e)
240 {
241 ActivityTextBox.AppendText(
242 $"{Strings.Server_started}{Environment.NewLine}");
243 }
244  
245 private void OnMqttServerStopped(object sender, EventArgs e)
246 {
247 ActivityTextBox.AppendText(
248 $"{Strings.Server_stopped}{Environment.NewLine}");
249 }
250  
251 private void OnMqttClientConnected(object sender, MQTTnet.Client.MqttClientConnectedEventArgs e)
252 {
253 ActivityTextBox.AppendText(
254 $"{Strings.Client_connected}{Environment.NewLine}");
255 }
256  
257 private void OnMqttClientDisconnected(object sender, MQTTnet.Client.MqttClientDisconnectedEventArgs e)
258 {
259 ActivityTextBox.AppendText(
260 $"{Strings.Client_disconnected}{Environment.NewLine}");
261 }
262  
263 private void OnMqttClientConnectionFailed(object sender, MqttManagedProcessFailedEventArgs e)
264 {
265 ActivityTextBox.AppendText(
266 $"{Strings.Client_connection_failed}{Environment.NewLine}");
267 }
268  
10 office 269 private void OnMqttServerAuthenticationFailed(object sender, MqttAuthenticationFailureEventArgs e)
9 office 270 {
271 ActivityTextBox.AppendText(
10 office 272 $"{Strings.Failed_to_authenticate_client} : {e.Exception}{Environment.NewLine}");
8 office 273 }
274  
10 office 275 private void OnMqttClientAuthenticationFailed(object sender, MqttAuthenticationFailureEventArgs e)
8 office 276 {
277 ActivityTextBox.AppendText(
10 office 278 $"{Strings.Failed_to_authenticate_with_server} : {e.Exception}{Environment.NewLine}");
8 office 279 }
280  
10 office 281 private void OnMouseKeyBindingsSynchronized(object sender, KeyBindingsSynchronizerEventArgs e)
7 office 282 {
283 ActivityTextBox.AppendText(
10 office 284 $"{Strings.Synchronized_bindings_with_client} : {e.Bindings.Nick} : {e.Bindings.KeyBindings.Count}{Environment.NewLine}");
6 office 285  
10 office 286 var exchangeBindings = KeyBindingsExchange.ExchangeBindings.FirstOrDefault(exchangeBinding =>
7 office 287 string.Equals(exchangeBinding.Nick, e.Bindings.Nick, StringComparison.Ordinal));
5 office 288  
7 office 289 // If the nick does not exist then add it.
290 if (exchangeBindings == null)
291 {
10 office 292 KeyBindingsExchange.ExchangeBindings.Add(e.Bindings);
9 office 293 RemoteBindingsComboBoxSource.ResetBindings(false);
7 office 294 return;
295 }
5 office 296  
7 office 297 // If the bindings for the nick have not changed then do not update.
36 office 298 if (e.Bindings.KeyBindings.Count == exchangeBindings.KeyBindings.Count &&
299 e.Bindings.KeyBindings.All(exchangeBindings.KeyBindings.Contains))
7 office 300 {
9 office 301 RemoteBindingsComboBoxSource.ResetBindings(false);
7 office 302 return;
303 }
5 office 304  
7 office 305 // Update the bindings.
36 office 306 exchangeBindings.KeyBindings.RemoveAll(binding => !e.Bindings.KeyBindings.Contains(binding));
307 exchangeBindings.KeyBindings.AddRange(
308 e.Bindings.KeyBindings.Where(binding => !exchangeBindings.KeyBindings.Contains(binding)));
9 office 309 RemoteBindingsComboBoxSource.ResetBindings(false);
7 office 310 }
5 office 311  
36 office 312 private void UpdateRemoteBindingsListBox()
7 office 313 {
36 office 314 var keyBindingExchange = (KeyBindingExchange) RemoteBindingsComboBox.SelectedItem;
315 if (keyBindingExchange == null)
7 office 316 return;
5 office 317  
36 office 318 RemoteBindingsListBox.Items.Clear();
319 var bindings = KeyBindingsExchange.ExchangeBindings
320 .Where(binding => binding.Nick == keyBindingExchange.Nick)
321 .SelectMany(binding => binding.KeyBindings.Select(keyBinding => keyBinding))
322 .Select(binding => (object) binding).ToArray();
10 office 323  
324 if (bindings.Length == 0)
7 office 325 return;
5 office 326  
10 office 327 RemoteBindingsListBox.Items.AddRange(bindings);
7 office 328 }
5 office 329  
330 private void OnLobbyMessageReceived(object sender, LobbyMessageReceivedEventArgs e)
331 {
36 office 332 WingManNotifyIcon.BalloonTipTitle = Strings.Lobby_message;
333 WingManNotifyIcon.BalloonTipText = $"{e.Nick} : {e.Message}{Environment.NewLine}";
334 WingManNotifyIcon.ShowBalloonTip(1000);
25 office 335  
9 office 336 LobbyTextBox.AppendText($"{e.Nick} : {e.Message}{Environment.NewLine}");
5 office 337 }
338  
1 office 339 private void AddressTextBoxClick(object sender, EventArgs e)
340 {
341 Address.BackColor = Color.Empty;
342 }
343  
344 private void PortTextBoxClick(object sender, EventArgs e)
345 {
346 Port.BackColor = Color.Empty;
347 }
348  
349 private async void HostButtonClickAsync(object sender, EventArgs e)
350 {
351 // Stop the MQTT server if it is running.
9 office 352 if (MqttCommunication.Running)
1 office 353 {
36 office 354 // Remote UPnP and Pmp mappings.
355 await Task.Delay(0, FormCancellationTokenSource.Token).ContinueWith(async _ =>
356 {
357 await Discovery.DeleteMapping(DiscoveryType.Upnp, MqttCommunication.Port);
358 await Discovery.DeleteMapping(DiscoveryType.Pmp, MqttCommunication.Port);
359 },
360 FormCancellationTokenSource.Token, TaskContinuationOptions.LongRunning, FormTaskScheduler);
361  
10 office 362 await MqttCommunication.Stop();
36 office 363  
1 office 364 HostButton.BackColor = Color.Empty;
365  
3 office 366 // Enable controls.
1 office 367 ConnectButton.Enabled = true;
5 office 368 Address.Enabled = true;
369 Port.Enabled = true;
370 Nick.Enabled = true;
10 office 371 Password.Enabled = true;
1 office 372 return;
373 }
374  
21 office 375 if (!ValidateConnectionParameters(out var ipAddress, out var port, out var nick, out var password))
1 office 376 return;
377  
23 office 378 StoreConnectionAutocomplete();
379  
32 office 380 // Try to reserve port: try UPnP followed by PMP.
36 office 381 await Task.Delay(0, FormCancellationTokenSource.Token).ContinueWith(async _ =>
382 {
383 if (!await Discovery.CreateMapping(DiscoveryType.Upnp, port) &&
384 !await Discovery.CreateMapping(DiscoveryType.Pmp, port))
385 ActivityTextBox.AppendText(
386 $"{Strings.Failed_creating_automatic_port_mapping_please_make_sure_the_port_is_routed_properly}{Environment.NewLine}");
387 },
388 FormCancellationTokenSource.Token, TaskContinuationOptions.LongRunning, FormTaskScheduler);
32 office 389  
1 office 390 // Start the MQTT server.
10 office 391 if (!await MqttCommunication
392 .Start(MqttCommunicationType.Server, ipAddress, port, nick, password))
9 office 393 {
394 ActivityTextBox.AppendText(
395 $"{Strings.Failed_starting_server}{Environment.NewLine}");
396 return;
397 }
398  
1 office 399 HostButton.BackColor = Color.Aquamarine;
400  
3 office 401 // Disable controls
1 office 402 ConnectButton.Enabled = false;
3 office 403 Address.Enabled = false;
404 Port.Enabled = false;
405 Nick.Enabled = false;
10 office 406 Password.Enabled = false;
3 office 407 }
408  
23 office 409 private async void StoreConnectionAutocomplete()
410 {
411 Address.AutoCompleteCustomSource.Add(Address.Text);
412  
413 await AutoCompletion.Save(Address.Name, Address.AutoCompleteCustomSource);
414  
415 Port.AutoCompleteCustomSource.Add(Port.Text);
416  
417 await AutoCompletion.Save(Port.Name, Port.AutoCompleteCustomSource);
418  
419 Nick.AutoCompleteCustomSource.Add(Nick.Text);
420  
421 await AutoCompletion.Save(Nick.Name, Nick.AutoCompleteCustomSource);
422 }
423  
21 office 424 private bool ValidateConnectionParameters(
425 out IPAddress address,
426 out int port,
427 out string nick,
428 out string password)
1 office 429 {
430 address = IPAddress.Any;
431 port = 0;
2 office 432 nick = string.Empty;
8 office 433 password = string.Empty;
1 office 434  
435 if (string.IsNullOrEmpty(Address.Text) &&
2 office 436 string.IsNullOrEmpty(Port.Text) &&
8 office 437 string.IsNullOrEmpty(Nick.Text) &&
438 string.IsNullOrEmpty(Password.Text))
1 office 439 {
440 Address.BackColor = Color.LightPink;
441 Port.BackColor = Color.LightPink;
2 office 442 Nick.BackColor = Color.LightPink;
8 office 443 Password.BackColor = Color.LightPink;
1 office 444 return false;
445 }
446  
447 if (!IPAddress.TryParse(Address.Text, out address))
21 office 448 try
449 {
450 address = Dns.GetHostAddresses(Address.Text).FirstOrDefault();
451 }
452 catch (Exception ex)
453 {
454 ActivityTextBox.AppendText(
455 $"{Strings.Could_not_resolve_hostname} : {ex.Message}{Environment.NewLine}");
1 office 456  
21 office 457 Address.BackColor = Color.LightPink;
458 return false;
459 }
460  
1 office 461 if (!uint.TryParse(Port.Text, out var uPort))
462 {
463 Port.BackColor = Color.LightPink;
464 return false;
465 }
466  
467 port = (int) uPort;
468  
2 office 469 if (string.IsNullOrEmpty(Nick.Text))
470 {
471 Nick.BackColor = Color.LightPink;
472 return false;
473 }
474  
475 nick = Nick.Text;
476  
8 office 477 if (string.IsNullOrEmpty(Password.Text))
478 {
479 Password.BackColor = Color.LightPink;
480 return false;
481 }
482  
36 office 483 password = Aes.ExpandKey(Password.Text);
8 office 484  
1 office 485 Address.BackColor = Color.Empty;
486 Port.BackColor = Color.Empty;
2 office 487 Nick.BackColor = Color.Empty;
8 office 488 Password.BackColor = Color.Empty;
1 office 489  
490 return true;
491 }
492  
493 private async void ConnectButtonClickAsync(object sender, EventArgs e)
494 {
9 office 495 if (MqttCommunication.Running)
1 office 496 {
10 office 497 await MqttCommunication.Stop();
5 office 498 ConnectButton.Text = Strings.Connect;
1 office 499 ConnectButton.BackColor = Color.Empty;
500  
5 office 501 Address.Enabled = true;
502 Port.Enabled = true;
503 Nick.Enabled = true;
10 office 504 Password.Enabled = true;
1 office 505 HostButton.Enabled = true;
506 return;
507 }
508  
21 office 509 if (!ValidateConnectionParameters(out var ipAddress, out var port, out var nick, out var password))
1 office 510 return;
511  
23 office 512 StoreConnectionAutocomplete();
513  
10 office 514 if (!await MqttCommunication
515 .Start(MqttCommunicationType.Client, ipAddress, port, nick, password))
9 office 516 {
517 ActivityTextBox.AppendText(
518 $"{Strings.Failed_starting_client}{Environment.NewLine}");
519 return;
520 }
1 office 521  
5 office 522 ConnectButton.Text = Strings.Disconnect;
1 office 523 ConnectButton.BackColor = Color.Aquamarine;
524  
525 HostButton.Enabled = false;
5 office 526 Address.Enabled = false;
527 Port.Enabled = false;
528 Nick.Enabled = false;
10 office 529 Password.Enabled = false;
1 office 530 }
531  
2 office 532 private async void LobbySayTextBoxKeyDown(object sender, KeyEventArgs e)
533 {
25 office 534 // Do not send messages if the communication is not running.
535 if (!MqttCommunication.Running)
536 return;
537  
2 office 538 if (e.KeyCode != Keys.Enter)
539 return;
540  
10 office 541 await LobbyMessageSynchronizer.Broadcast(LobbySayTextBox.Text);
2 office 542  
5 office 543 LobbySayTextBox.Text = string.Empty;
2 office 544 }
4 office 545  
36 office 546 private async void LocalAddBindingButtonClick(object sender, EventArgs e)
4 office 547 {
9 office 548 if (string.IsNullOrEmpty(LocalNameTextBox.Text))
4 office 549 {
9 office 550 LocalNameTextBox.BackColor = Color.LightPink;
4 office 551 return;
552 }
553  
10 office 554 // Only unique names allowed.
555 if (LocalKeyBindings.Bindings.Any(binding =>
556 string.Equals(binding.Name, LocalNameTextBox.Text, StringComparison.Ordinal)))
557 {
558 LocalNameTextBox.BackColor = Color.LightPink;
24 office 559 LocalBindingsCheckedListBox.BackColor = Color.LightPink;
10 office 560 return;
561 }
562  
563 LocalNameTextBox.BackColor = Color.Empty;
24 office 564 LocalBindingsCheckedListBox.BackColor = Color.Empty;
10 office 565  
36 office 566 LocalNameTextBox.AutoCompleteCustomSource.Add(LocalNameTextBox.Text);
567  
568 await AutoCompletion.Save(LocalNameTextBox.Name, LocalNameTextBox.AutoCompleteCustomSource);
569  
8 office 570 ShowOverlayPanel();
4 office 571  
572 MouseKeyCombo = new List<string>();
573  
10 office 574 MouseKeyApplicationHook = Hook.GlobalEvents();
9 office 575 MouseKeyApplicationHook.KeyUp += LocalMouseKeyHookOnKeyUp;
576 MouseKeyApplicationHook.KeyDown += LocalMouseKeyHookOnKeyDown;
4 office 577 }
578  
8 office 579 private void ShowOverlayPanel()
580 {
581 OverlayPanel.BringToFront();
582 OverlayPanel.Visible = true;
583 OverlayPanel.Invalidate();
584 }
585  
14 office 586 private async void LocalMouseKeyHookOnKeyUp(object sender, KeyEventArgs e)
4 office 587 {
10 office 588 LocalKeyBindings.Bindings.Add(new KeyBinding(LocalNameTextBox.Text, MouseKeyCombo));
4 office 589  
24 office 590 LocalCheckedListBoxBindingSource.ResetBindings(false);
4 office 591  
9 office 592 MouseKeyApplicationHook.KeyDown -= LocalMouseKeyHookOnKeyDown;
593 MouseKeyApplicationHook.KeyUp -= LocalMouseKeyHookOnKeyUp;
4 office 594  
595 MouseKeyApplicationHook.Dispose();
596  
9 office 597 LocalNameTextBox.Text = string.Empty;
8 office 598 HideOverlayPanel();
9 office 599  
14 office 600 await SaveLocalMouseKeyBindings();
4 office 601 }
602  
8 office 603 private void HideOverlayPanel()
604 {
605 OverlayPanel.SendToBack();
606 OverlayPanel.Visible = false;
607 OverlayPanel.Invalidate();
608 }
609  
9 office 610 private void LocalMouseKeyHookOnKeyDown(object sender, KeyEventArgs e)
4 office 611 {
612 e.SuppressKeyPress = true;
613  
10 office 614 KeyConversion.KeysToString.TryGetValue((byte) e.KeyCode, out var key);
615  
616 MouseKeyCombo.Add(key);
4 office 617 }
618  
10 office 619 private void LocalNameTextBoxClick(object sender, EventArgs e)
4 office 620 {
9 office 621 LocalNameTextBox.BackColor = Color.Empty;
4 office 622 }
623  
14 office 624 private async void LocalBindingsRemoveButtonClick(object sender, EventArgs e)
4 office 625 {
36 office 626 var localBinding = (KeyBinding) LocalBindingsCheckedListBox.SelectedItem;
627 if (localBinding == null)
5 office 628 return;
4 office 629  
36 office 630 LocalKeyBindings.Bindings.Remove(localBinding);
24 office 631 LocalCheckedListBoxBindingSource.ResetBindings(false);
9 office 632  
14 office 633 await SaveLocalMouseKeyBindings();
4 office 634 }
635  
5 office 636 private async void LobbySayButtonClick(object sender, EventArgs e)
4 office 637 {
25 office 638 // Do not send messages if the communication is not running.
639 if (!MqttCommunication.Running)
640 return;
641  
10 office 642 await LobbyMessageSynchronizer.Broadcast(LobbySayTextBox.Text);
4 office 643  
5 office 644 LobbySayTextBox.Text = string.Empty;
4 office 645 }
646  
9 office 647 private void RemoteBindingsComboBoxSelectionChangeCompleted(object sender, EventArgs e)
4 office 648 {
36 office 649 UpdateRemoteBindingsListBox();
4 office 650 }
8 office 651  
14 office 652 private async void WingManFormOnLoad(object sender, EventArgs e)
8 office 653 {
14 office 654 await LoadLocalMouseKeyBindings();
10 office 655  
14 office 656 await LoadRemoteMouseKeyBindings();
8 office 657 }
9 office 658  
10 office 659 private void RemoteBindingsBindButtonClicked(object sender, EventArgs e)
9 office 660 {
10 office 661 if (string.IsNullOrEmpty((string) RemoteBindingsListBox.SelectedItem))
9 office 662 {
10 office 663 RemoteBindingsListBox.BackColor = Color.LightPink;
664 return;
665 }
9 office 666  
10 office 667 RemoteBindingsListBox.BackColor = Color.Empty;
9 office 668  
10 office 669 ShowOverlayPanel();
9 office 670  
10 office 671 MouseKeyCombo = new List<string>();
9 office 672  
10 office 673 MouseKeyApplicationHook = Hook.GlobalEvents();
21 office 674 MouseKeyApplicationHook.KeyUp += RemoteKeyHookOnKeyUp;
675 MouseKeyApplicationHook.KeyDown += RemoteKeyHookOnKeyDown;
9 office 676 }
677  
10 office 678 private void RemoteBindingsUnbindButtonClicked(object sender, EventArgs e)
9 office 679 {
10 office 680 var item = (string) RemoteBindingsListBox.SelectedItem;
681 if (string.IsNullOrEmpty(item))
682 {
683 RemoteBindingsListBox.BackColor = Color.LightPink;
684 return;
685 }
9 office 686  
10 office 687 RemoteBindingsListBox.BackColor = Color.Empty;
9 office 688  
10 office 689 var remoteKeyBinding = RemoteKeyBindings.Bindings.FirstOrDefault(binding =>
690 string.Equals(binding.Name, item, StringComparison.Ordinal));
691  
692 if (remoteKeyBinding == null)
693 return;
694  
695 RemoteKeyBindings.Bindings.Remove(remoteKeyBinding);
696 RemoteBindingsBindToBox.Text = string.Empty;
9 office 697 }
698  
21 office 699 private void RemoteKeyHookOnKeyDown(object sender, KeyEventArgs e)
9 office 700 {
701 e.SuppressKeyPress = true;
702  
21 office 703 if (!KeyConversion.KeysToString.TryGetValue((byte) e.KeyCode, out var key))
704 return;
9 office 705  
10 office 706 MouseKeyCombo.Add(key);
9 office 707 }
708  
21 office 709 private async void RemoteKeyHookOnKeyUp(object sender, KeyEventArgs e)
9 office 710 {
10 office 711 RemoteKeyBindings.Bindings.Add(new RemoteKeyBinding(RemoteBindingsComboBox.Text,
9 office 712 (string) RemoteBindingsListBox.SelectedItem, MouseKeyCombo));
713  
21 office 714 MouseKeyApplicationHook.KeyDown -= RemoteKeyHookOnKeyDown;
715 MouseKeyApplicationHook.KeyUp -= RemoteKeyHookOnKeyUp;
9 office 716  
717 MouseKeyApplicationHook.Dispose();
718  
719 RemoteBindingsBindToBox.Text = string.Join(" + ", MouseKeyCombo);
720 HideOverlayPanel();
721  
14 office 722 await SaveRemoteMouseKeyBindings();
9 office 723 }
724  
10 office 725 private void RemoteBindingsListBoxSelectedValueChanged(object sender, EventArgs e)
9 office 726 {
10 office 727 RemoteBindingsBindToBox.Text = "";
9 office 728  
10 office 729 var name = (string) RemoteBindingsListBox.SelectedItem;
730 if (string.IsNullOrEmpty(name))
731 return;
9 office 732  
14 office 733 var nick = RemoteBindingsComboBox.Text;
734 if (string.IsNullOrEmpty(nick))
735 return;
736  
10 office 737 foreach (var binding in RemoteKeyBindings.Bindings)
738 {
14 office 739 if (!string.Equals(binding.Nick, nick) || !string.Equals(binding.Name, name))
10 office 740 continue;
9 office 741  
10 office 742 RemoteBindingsBindToBox.Text = string.Join(" + ", binding.Keys);
743 break;
744 }
745 }
9 office 746  
23 office 747 private void WingManFormResized(object sender, EventArgs e)
748 {
27 office 749 if (WindowState == FormWindowState.Minimized) Hide();
23 office 750 }
751  
752 private void NotifyIconDoubleClick(object sender, EventArgs e)
753 {
754 Show();
755 WindowState = FormWindowState.Normal;
756 }
757  
24 office 758 private async void LocalBindingsCheckedListBoxItemCheck(object sender, ItemCheckEventArgs e)
759 {
760 var helmBinding = (KeyBinding) LocalBindingsCheckedListBox.Items[e.Index];
761 if (helmBinding == null)
762 return;
763  
764 switch (e.NewValue)
765 {
766 case CheckState.Checked:
767 helmBinding.Enabled = true;
768 break;
769 case CheckState.Unchecked:
770 helmBinding.Enabled = false;
771 break;
772 }
773  
774 await SaveLocalMouseKeyBindings();
775 }
776  
33 office 777 private void WingManTabControlMouseDown(object sender, MouseEventArgs e)
778 {
779 if (e.Button != MouseButtons.Left) return;
780  
36 office 781 TabControlClickStartPosition = e.Location;
33 office 782 }
783  
784 private void WingManTabControlMouseMove(object sender, MouseEventArgs e)
785 {
786 if (e.Button == MouseButtons.Left)
787 {
36 office 788 var mouseOffsetX = TabControlClickStartPosition.X - e.X;
789 var mouseOffsetY = TabControlClickStartPosition.Y - e.Y;
33 office 790  
36 office 791 if (mouseOffsetX <= TabControlDetachPixelOffset && mouseOffsetY <= TabControlDetachPixelOffset)
33 office 792 return;
793  
794 tabControl1.DoDragDrop(tabControl1.SelectedTab, DragDropEffects.Move);
36 office 795 TabControlClickStartPosition = new Point();
33 office 796  
797 return;
798 }
799  
36 office 800 TabControlClickStartPosition = new Point();
33 office 801 }
802  
803 private void WingManTabControlGiveFeedback(object sender, GiveFeedbackEventArgs e)
804 {
805 e.UseDefaultCursors = false;
806 }
807  
808 private void WingManTabControlQueryContinueDrag(object sender, QueryContinueDragEventArgs e)
809 {
810 if (MouseButtons != MouseButtons.Left)
811 {
812 e.Action = DragAction.Cancel;
813  
814 DetachedForm = new Form
815 {
816 Size = new Size(
817 // Width = tab control width + tab control left and right margins + x-padding
818 tabControl1.Width +
819 tabControl1.Margin.Right +
820 tabControl1.Margin.Left +
821 tabControl1.Padding.X,
822 // Tab Height = tab control height - tab control tab page height
823 // Height = tab control height + Tab Height + top and bottom margin + x-padding
824 2 * tabControl1.Height -
825 tabControl1.SelectedTab.Height +
826 tabControl1.Margin.Top +
827 tabControl1.Margin.Bottom +
828 tabControl1.Padding.Y),
829 StartPosition = FormStartPosition.Manual,
830 Location = MousePosition,
831 MaximizeBox = false,
832 SizeGripStyle = SizeGripStyle.Hide,
36 office 833 FormBorderStyle = FormBorderStyle.FixedSingle,
834 Name = Name,
835 Text = Text,
836 Icon = Icon
33 office 837 };
838  
839 DetachedTabControl = new TabControl
840 {
841 Dock = DockStyle.Fill,
842 SizeMode = TabSizeMode.Fixed
843 };
844 DetachedTabControl.TabPages.Add(tabControl1.SelectedTab);
845 DetachedForm.Controls.Add(DetachedTabControl);
846 DetachedForm.FormClosing += DetachedFormOnFormClosing;
847 DetachedForm.Show();
848 Cursor = Cursors.Default;
849 return;
850 }
851  
852 e.Action = DragAction.Continue;
853 Cursor = Cursors.SizeAll;
854 }
855  
856 private void DetachedFormOnFormClosing(object sender, FormClosingEventArgs e)
857 {
858 tabControl1.TabPages.Insert(DetachedTabControl.SelectedTab.TabIndex, DetachedTabControl.SelectedTab);
859 DetachedForm.FormClosing -= DetachedFormOnFormClosing;
860 }
861  
10 office 862 #region Saving and loading
863  
864 private async Task SaveLocalMouseKeyBindings()
865 {
866 try
867 {
868 using (var memoryStream = new MemoryStream())
869 {
870 LocalKeyBindings.XmlSerializer.Serialize(memoryStream, LocalKeyBindings);
871  
872 memoryStream.Position = 0L;
873  
874 using (var fileStream = new FileStream("LocalKeyBindings.xml", FileMode.Create))
875 {
876 await memoryStream.CopyToAsync(fileStream);
877 }
878 }
879 }
880 catch (Exception)
881 {
882 ActivityTextBox.AppendText(
883 $"{Strings.Failed_saving_local_bindings}{Environment.NewLine}");
884 }
9 office 885 }
886  
10 office 887 private async Task LoadLocalMouseKeyBindings()
888 {
889 try
890 {
891 using (var fileStream = new FileStream("LocalKeyBindings.xml", FileMode.Open))
892 {
893 using (var memoryStream = new MemoryStream())
894 {
895 await fileStream.CopyToAsync(memoryStream);
896  
897 memoryStream.Position = 0L;
898  
899 var loadedBindings =
900 (LocalKeyBindings) LocalKeyBindings.XmlSerializer.Deserialize(memoryStream);
901  
21 office 902 foreach (var binding in loadedBindings.Bindings)
903 LocalKeyBindings.Bindings.Add(binding);
10 office 904  
24 office 905  
906 LocalCheckedListBoxBindingSource.ResetBindings(false);
10 office 907 }
908 }
909 }
910 catch (Exception)
911 {
912 ActivityTextBox.AppendText(
913 $"{Strings.Failed_loading_local_bindings}{Environment.NewLine}");
914 }
915 }
916  
9 office 917 private async Task SaveRemoteMouseKeyBindings()
918 {
919 try
920 {
921 using (var memoryStream = new MemoryStream())
922 {
10 office 923 RemoteKeyBindings.XmlSerializer.Serialize(memoryStream, RemoteKeyBindings);
9 office 924  
925 memoryStream.Position = 0L;
926  
10 office 927 using (var fileStream = new FileStream("RemoteKeyBindings.xml", FileMode.Create))
9 office 928 {
10 office 929 await memoryStream.CopyToAsync(fileStream);
9 office 930 }
931 }
932 }
933 catch (Exception)
934 {
935 ActivityTextBox.AppendText(
936 $"{Strings.Failed_saving_remote_bindings}{Environment.NewLine}");
937 }
938 }
939  
940 private async Task LoadRemoteMouseKeyBindings()
941 {
942 try
943 {
10 office 944 using (var fileStream = new FileStream("RemoteKeyBindings.xml", FileMode.Open))
9 office 945 {
946 using (var memoryStream = new MemoryStream())
947 {
10 office 948 await fileStream.CopyToAsync(memoryStream);
9 office 949  
950 memoryStream.Position = 0L;
951  
952 var loadedBindings =
10 office 953 (RemoteKeyBindings) RemoteKeyBindings.XmlSerializer.Deserialize(memoryStream);
9 office 954  
21 office 955 foreach (var binding in loadedBindings.Bindings)
956 RemoteKeyBindings.Bindings.Add(binding);
9 office 957 }
958 }
959 }
960 catch (Exception)
961 {
962 ActivityTextBox.AppendText(
963 $"{Strings.Failed_loading_remote_bindings}{Environment.NewLine}");
964 }
965 }
10 office 966  
967 #endregion
1 office 968 }
969 }