WingMan – Blame information for rev 36
?pathlinks?
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 | } |