Spring – Blame information for rev 2

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 office 1 using System;
2 using System.Collections.Generic;
3 using System.Collections.Specialized;
4 using System.ComponentModel;
5 using System.Drawing;
6 using System.IO;
7 using System.Linq;
8 using System.Net;
9 using System.Reflection;
10 using System.Threading.Tasks;
11 using System.Threading.Tasks.Dataflow;
12 using System.Windows.Forms;
13 using Gma.System.MouseKeyHook;
14 using NetSparkleUpdater;
15 using NetSparkleUpdater.Enums;
16 using NetSparkleUpdater.SignatureVerifiers;
17 using NetSparkleUpdater.UI.WinForms;
18 using QRCoder;
19 using Spring.About;
20 using Spring.Charging;
21 using Spring.Discharging;
22 using Spring.Editing;
23 using Spring.HUD;
24 using Spring.MouseKeyboard;
25 using Spring.Properties;
26 using Spring.Serialization;
27 using Spring.Serialization.Combos;
28 using Spring.Serialization.Configuration;
29 using Spring.Settings;
30 using Spring.Utilities;
31 using Spring.Utilities.Collections.RangeTree;
32 using SpringCombos;
33  
34 namespace Spring.Main
35 {
36 public partial class MainForm : Form
37 {
38 #region Public Events & Delegates
39  
40 public event EventHandler<EventArgs> SpringKeyButtonClicked;
41  
42 public event EventHandler<EventArgs> SpringSlowButtonClicked;
43  
44 public event EventHandler<EventArgs> SpringSpeedButtonClicked;
45  
46 public event EventHandler<SpeedbarValueChangedEventArgs> SpeedbarValueChanged;
47  
48 public event EventHandler<ConfigurationRestoredEventArgs> ConfigurationRestored;
49  
50 #endregion
51  
52 #region Private Delegates, Events, Enums, Properties, Indexers and Fields
53  
54 private Configuration.Configuration Configuration { get; set; }
55  
56 private OSD OSD { get; set; }
57  
58 private Key Key { get; set; }
59  
60 private KeyWatch WatchKey { get; set; }
61  
62 private Charge Charge { get; set; }
63  
64 private Discharge Discharge { get; set; }
65  
66 private IKeyboardMouseEvents KeyboardMouseEvents { get; }
67  
68 private EditorForm EditorForm { get; set; }
69  
70 private SettingsForm SettingsForm { get; set; }
71  
72 private BufferBlock<ComboQueueItem> ComboQueue { get; }
73  
74 private ActionBlock<ComboQueueItem> ComboQueueAction { get; }
75  
76 private static QRCodeGenerator QrGenerator { get; set; }
77  
78 private readonly IDisposable _comboQueueLink;
79  
80 private RangeTreeAction<TimeSpan, int> _rangeTree;
81 private readonly SparkleUpdater _sparkle;
82  
83 #endregion
84  
85 #region Constructors, Destructors and Finalizers
86  
87 public MainForm()
88 {
89 InitializeComponent();
90  
91 // Start application update.
92 var manifestModuleName = Assembly.GetEntryAssembly().ManifestModule.FullyQualifiedName;
93 var icon = Icon.ExtractAssociatedIcon(manifestModuleName);
94  
95 _sparkle = new SparkleUpdater("https://spring.grimore.org/update/appcast.xml",
96 new Ed25519Checker(SecurityMode.Strict, "LonrgxVjSF0GnY4hzwlRJnLkaxnDn2ikdmOifILzLJY="))
97 {
98 UIFactory = new UIFactory(icon),
99 RelaunchAfterUpdate = true,
100 SecurityProtocolType = SecurityProtocolType.Tls12
101 };
102 _sparkle.StartLoop(true, true);
103  
104 Directory.CreateDirectory(Constants.UserApplicationDirectory);
105  
106 QrGenerator = new QRCodeGenerator();
107  
108 ComboQueue =
109 new BufferBlock<ComboQueueItem>(new DataflowBlockOptions
110 { BoundedCapacity = 4, EnsureOrdered = true });
111  
112 ComboQueueAction = new ActionBlock<ComboQueueItem>(DoComboQueueAction,
113 new ExecutionDataflowBlockOptions { EnsureOrdered = true, BoundedCapacity = 4 });
114  
115 _comboQueueLink = ComboQueue.LinkTo(ComboQueueAction);
116  
117 toolStripStatusLabel1.Text = Strings.Welcome_to_Spring;
118  
119 ConfigurationRestored += MainForm_ConfigurationRestored;
120  
121 KeyboardMouseEvents = Hook.GlobalEvents();
122 }
123  
124 /// <summary>
125 /// Clean up any resources being used.
126 /// </summary>
127 /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
128 protected override void Dispose(bool disposing)
129 {
130 if (disposing && components != null)
131 {
132 ConfigurationRestored -= MainForm_ConfigurationRestored;
133  
134 _comboQueueLink?.Dispose();
135 Discharge?.Dispose();
136 Charge?.Dispose();
137 WatchKey?.Dispose();
138 Key?.Dispose();
139  
140 components.Dispose();
141 }
142  
143 OSD?.Dispose();
144 OSD = null;
145  
146 base.Dispose(disposing);
147 }
148  
149 #endregion
150  
151 #region Event Handlers
152  
153 private void ComboQueuePictureBox_Click(object sender, EventArgs e)
154 {
155 var pictureBox = (PictureBox) sender;
156  
157 Charge.Load((Combos) pictureBox.Tag);
158 }
159  
160 private void AboutToolStripMenuItem_Click(object sender, EventArgs e)
161 {
162 var aboutForm = new AboutForm();
163 aboutForm.Show();
164 }
165  
166 private void TutorialToolStripMenuItem_Click(object sender, EventArgs e)
167 {
168 new Tutorial.Tutorial(this, KeyboardMouseEvents).Show();
169 }
170  
171 private async void MainForm_ConfigurationRestored(object sender, ConfigurationRestoredEventArgs e)
172 {
173 await Initialize(e.Combos);
174 }
175  
176 private void SpeedBar_ValueChanged(object sender, EventArgs e)
177 {
178 var trackBar = (TrackBar) sender;
179 SpeedbarValueChanged?.Invoke(sender, new SpeedbarValueChangedEventArgs(trackBar.Value));
180 }
181  
182 private void QuitToolStripMenuItem_Click(object sender, EventArgs e)
183 {
184 Application.Exit();
185 }
186  
187 private void BindSpeedButton_Click(object sender, EventArgs e)
188 {
189 SpringSpeedButtonClicked?.Invoke(this, e);
190 }
191  
192 private void BindSlowButton_Click(object sender, EventArgs e)
193 {
194 SpringSlowButtonClicked?.Invoke(this, e);
195 }
196  
197 private void BindKeyButton_Click(object sender, EventArgs e)
198 {
199 SpringKeyButtonClicked?.Invoke(this, e);
200 }
201  
202 private async void SaveFileDialog1_FileOk(object sender, CancelEventArgs e)
203 {
204 switch (await Charge.Combos.Serialize(saveFileDialog1.FileName))
205 {
206 case SerializationSuccess<Combos> serializationSuccess:
207 toolStripStatusLabel1.Text = $"{Strings.Combos_wrote_successfully}";
208  
209 Charge.StartCharge(serializationSuccess.Result);
210  
211 Configuration.Spring.Store = saveFileDialog1.FileName;
212  
213 if (!Configuration.Spring.History.Contains(saveFileDialog1.FileName))
214 {
215 Configuration.Spring.History.Add(saveFileDialog1.FileName);
216 }
217  
218 break;
219  
220 case SerializationFailure serializationFailed:
221 toolStripStatusLabel1.Text =
222 $"{Strings.Failed_to_write_combos}: {serializationFailed.Exception.Message}";
223  
224 break;
225 }
226 }
227  
228 private async void OpenFileDialog1_FileOk(object sender, CancelEventArgs e)
229 {
230 var fileInfo = new FileInfo(openFileDialog1.FileName);
231  
232 var combos = await LoadCombos(fileInfo);
233  
234 if (combos == null)
235 {
236 return;
237 }
238  
239 Charge.StartCharge(combos);
240  
241 Configuration.Spring.Store = openFileDialog1.FileName;
242  
243 if (!Configuration.Spring.History.Contains(openFileDialog1.FileName))
244 {
245 Configuration.Spring.History.Add(openFileDialog1.FileName);
246 }
247 }
248  
249 private void ExportToolStripMenuItem_Click(object sender, EventArgs e)
250 {
251 saveFileDialog1.ShowDialog();
252 }
253  
254 private void ImportToolStripMenuItem_Click(object sender, EventArgs e)
255 {
256 openFileDialog1.ShowDialog();
257 }
258  
259 private async void MainForm_Load(object sender, EventArgs e)
260 {
2 office 261 Utilities.WindowState.FormTracker.Track(this);
262  
1 office 263 var configurationFileInfo = new FileInfo(Constants.ConfigurationFilePath);
264  
265 // Load configuration.
266 switch (ConfigurationSerialization.Deserialize(configurationFileInfo))
267 {
268 case SerializationSuccess<Configuration.Configuration> serializationSuccess:
269 Configuration = serializationSuccess.Result;
270 toolStripStatusLabel1.Text = $"{Strings.Read_configuration_successfully}";
271  
272 break;
273  
274 case SerializationFailure serializationFailed:
275 toolStripStatusLabel1.Text =
276 $"{Strings.Failed_to_read_configuration}: {serializationFailed.Exception.Message}";
277  
278 // Attempt to load default configuration
279 var fileInfo = new FileInfo("Configuration.xml.default");
280  
281 switch (ConfigurationSerialization.Deserialize(fileInfo))
282 {
283 case SerializationSuccess<Configuration.Configuration> serializationSuccess:
284 toolStripStatusLabel1.Text =
285 $"{Strings.Read_default_configuration_successfully}";
286  
287 Configuration = serializationSuccess.Result;
288  
289 break;
290  
291 case SerializationFailure defaultSerializationFailure:
292 toolStripStatusLabel1.Text =
293 $"{Strings.Failed_to_read_default_configuration}: {defaultSerializationFailure.Exception.Message}";
294  
295 Configuration = new Configuration.Configuration();
296  
297 break;
298 }
299  
300 break;
301 }
302  
303 // Load combos.
304 if (string.IsNullOrEmpty(Configuration.Spring.Store))
305 {
306 ConfigurationRestored?.Invoke(this, new ConfigurationRestoredEventArgs(new Combos()));
307  
308 return;
309 }
310  
311 var storeFileInfo = new FileInfo(Configuration.Spring.Store);
312  
313 var combos = await LoadCombos(storeFileInfo);
314  
315 if (combos == null)
316 {
317 ConfigurationRestored?.Invoke(this, new ConfigurationRestoredEventArgs(new Combos()));
318  
319 return;
320 }
321  
322 await ComboQueue.SendAsync(new ComboQueueItem(comboQueuePictureBox1, combos));
323  
324 ConfigurationRestored?.Invoke(this, new ConfigurationRestoredEventArgs(combos));
325 }
326  
327 private void WatchKey_SpeedAdjusted(object sender, KeySpeedAdjustedEventArgs e)
328 {
329 this.InvokeIfRequired(form =>
330 {
331 switch (e.Multiplier)
332 {
333 case -1:
334 form.SpeedBar.Value = Math.Max(form.SpeedBar.Value - 1, form.SpeedBar.Minimum);
335  
336 break;
337  
338 case 1:
339 form.SpeedBar.Value = Math.Min(form.SpeedBar.Value + 1, form.SpeedBar.Maximum);
340  
341 break;
342 }
343 });
344 }
345  
346 private void Key_KeyMessage(object sender, KeyMessageEventArgs e)
347 {
348 this.InvokeIfRequired(form => { form.toolStripStatusLabel1.Text = e.Message; });
349 }
350  
351 private void Loading_PropertyChanged(object sender, PropertyChangedEventArgs e)
352 {
353 _rangeTree = new RangeTreeAction<TimeSpan, int>
354 {
355 {
356 TimeSpan.FromSeconds(0),
357 TimeSpan.FromSeconds(Configuration.Spring.Charge.ChargeSeconds),
358 ActionNoCharge
359 },
360 {
361 TimeSpan.FromSeconds(Configuration.Spring.Charge.ChargeSeconds),
362 TimeSpan.FromSeconds(Configuration.Spring.Charge.ChargeSeconds),
363 ActionNoCharge
364 },
365 {
366 TimeSpan.FromSeconds(Configuration.Spring.Charge.ChargeSeconds),
367 TimeSpan.FromSeconds(Configuration.Spring.Charge.MaximumRepeats +
368 Configuration.Spring.Charge.ChargeSeconds),
369 ActionCharge
370 },
371 {
372 TimeSpan.FromSeconds(Configuration.Spring.Charge.MaximumRepeats +
373 Configuration.Spring.Charge.ChargeSeconds),
374 TimeSpan.MaxValue, ActionNoCharge
375 }
376 };
377 }
378  
379 private void WatchKey_KeyUp(object sender, KeyUpEventArgs e)
380 {
381 OSD.Text = OSD.Symbols.KeyReleased();
382 }
383  
384 private void Charge_ChargeLoaded(object sender, ChargeLoadedEventArgs e)
385 {
386 this.InvokeIfRequired(form => { form.toolStripStatusLabel1.Text = Strings.Spring_charged; });
387  
388 if (e.Combos == null)
389 {
390 return;
391 }
392  
393 switch (e.Combos.ComboRepeat)
394 {
395 case 0:
396 OSD.Text = OSD.Symbols.ChargeStop(Resources.Infinity);
397  
398 break;
399  
400 default:
401 OSD.Text = OSD.Symbols.ChargeStop(e.Combos.ComboRepeat.ToString());
402  
403 break;
404 }
405 }
406  
407 private async void ChargeChanged(object sender, ChargeChangedEventArgs e)
408 {
409 this.InvokeIfRequired(form => { form.toolStripStatusLabel1.Text = Strings.Spring_charged; });
410  
411 if (e.Combos == null)
412 {
413 return;
414 }
415  
416 var combos = Charge.Combos;
417  
418 await ComboQueue.SendAsync(new ComboQueueItem(comboQueuePictureBox1, combos));
419  
420 switch (e.Combos.ComboRepeat)
421 {
422 case 0:
423 OSD.Text = OSD.Symbols.ChargeStop(Resources.Infinity);
424  
425 break;
426  
427 default:
428 OSD.Text = OSD.Symbols.ChargeStop(e.Combos.ComboRepeat.ToString());
429  
430 break;
431 }
432 }
433  
434 private void ChargeStart(object sender, ChargeStartEventArgs e)
435 {
436 if (Charge.Combos == null)
437 {
438 return;
439 }
440  
441 this.InvokeIfRequired(form => { form.toolStripStatusLabel1.Text = Strings.Spring_is_charging; });
442  
443 OSD.Text = OSD.Symbols.ChargeStart();
444 }
445  
446 private void Discharge_DischargeStart(object sender, DischargeStartEventArgs e)
447 {
448 this.InvokeIfRequired(form =>
449 {
450 form.toolStripStatusLabel1.Text = Strings.Spring_is_discharging;
451 form.toolStripProgressBar1.Value = 0;
452  
453 switch (e.Repeat)
454 {
455 case 0:
456 form.label5.Text = Resources.Infinity;
457 form.toolStripProgressBar1.Visible = false;
458  
459 OSD.Text = OSD.Symbols.DischargeProgress(Resources.Infinity);
460  
461 break;
462  
463 default:
464 form.label5.Text = e.Repeat.ToString();
465 form.toolStripProgressBar1.Visible = true;
466  
467 OSD.Text = OSD.Symbols.DischargeProgress(e.Repeat.ToString());
468  
469 break;
470 }
471 });
472 }
473  
474 private void Discharge_DischargeStop(object sender, DischargeStopEventArgs e)
475 {
476 OSD.Text = string.Empty;
477  
478 this.InvokeIfRequired(form =>
479 {
480 form.toolStripStatusLabel1.Text = Strings.Spring_discharge_cancelled;
481 form.toolStripProgressBar1.Visible = false;
482  
483 switch (e.Repeat)
484 {
485 case 0:
486 form.label5.Text = Resources.Infinity;
487  
488 OSD.Text = OSD.Symbols.DischargeStop(Resources.Infinity);
489  
490 break;
491  
492 default:
493 form.label5.Text = e.Repeat.ToString();
494  
495 OSD.Text = OSD.Symbols.DischargeStop(e.Repeat.ToString());
496  
497 break;
498 }
499 });
500 }
501  
502 private void Discharge_DischargeProgress(object sender, DischargeProgressEventArgs e)
503 {
504 this.InvokeIfRequired(form =>
505 {
506 switch (e.Repeat)
507 {
508 case 0:
509 break;
510  
511 default:
512 var progress = (int) (100f - 100f * e.Current / (e.Total * e.Repeat));
513  
514 if (progress < 0 || progress > 100)
515 {
516 break;
517 }
518  
519 form.toolStripProgressBar1.Value = progress;
520  
521 var repeats = (int) (1f * e.Current / e.Total);
522  
523 form.label5.Text = $@"{repeats}";
524  
525 OSD.Text = OSD.Symbols.DischargeProgress(repeats.ToString());
526  
527 break;
528 }
529 });
530 }
531  
532 private void WatchKey_KeyDown(object sender, KeyDownEventArgs e)
533 {
534 _rangeTree.Query(e.Elapsed);
535 }
536  
537 private async void MainForm_FormClosing(object sender, FormClosingEventArgs e)
538 {
539 // Save combos.
540 await SaveCombos(Configuration.Spring.Store);
541  
542 // Save configuration file.
543 switch (await Configuration.Serialize(Constants.ConfigurationFilePath))
544 {
545 case SerializationSuccess _:
546 toolStripStatusLabel1.Text = $"{Strings.Wrote_configuration_successfully}";
547  
548 break;
549  
550 case SerializationFailure serializationFailed:
551 toolStripStatusLabel1.Text =
552 $"{Strings.Failed_to_write_configuration}: {serializationFailed.Exception.Message}";
553  
554 break;
555 }
556 }
557  
558 private void EditorButton_Click(object sender, EventArgs e)
559 {
560 if (EditorForm != null)
561 {
562 return;
563 }
564  
565 EditorForm = new EditorForm(Configuration, Charge.Combos);
566 EditorForm.Closing += EditorForm_Closing;
567 EditorForm.Edited += EditorForm_Edited;
568 EditorForm.Show();
569 }
570  
571 private void EditorForm_Edited(object sender, EditEventArgs e)
572 {
573 switch (e)
574 {
575 case EditSuccessEventArgs editSuccessEventArgs:
576 Charge.StartCharge(editSuccessEventArgs.Combos);
577  
578 break;
579  
580 case EditFailureEventArgs editFailureEventArgs:
581 break;
582 }
583 }
584  
585 private void EditorForm_Closing(object sender, CancelEventArgs e)
586 {
587 if (EditorForm == null)
588 {
589 return;
590 }
591  
592 EditorForm.Closing -= EditorForm_Closing;
593 EditorForm.Edited -= EditorForm_Edited;
594 EditorForm.Dispose();
595 EditorForm = null;
596  
597 toolStripStatusLabel1.Text = $"{Strings.Combos_read_successfully}";
598 }
599  
600 private void SettingsButton_Click(object sender, EventArgs e)
601 {
602 if (SettingsForm != null)
603 {
604 return;
605 }
606  
607 SettingsForm = new SettingsForm(Configuration);
608 SettingsForm.FormClosing += SettingsForm_FormClosing;
609 SettingsForm.Show();
610 }
611  
612 private void SettingsForm_FormClosing(object sender, FormClosingEventArgs e)
613 {
614 if (SettingsForm == null)
615 {
616 return;
617 }
618  
619 SettingsForm.FormClosing -= SettingsForm_FormClosing;
620 SettingsForm.Dispose();
621 SettingsForm = null;
622 }
623  
624 private void History_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
625 {
626 RecreateHistoryMenu(e.NewItems.OfType<string>());
627 }
628  
629 private void Spring_PropertyChanged(object sender, PropertyChangedEventArgs e)
630 {
631 var springHistory = ((Configuration.Spring) sender).History;
632  
633 RecreateHistoryMenu(springHistory);
634 }
635  
636 private async void HistoryToolStripMenu_Click(object sender, EventArgs e)
637 {
638 var selectedFile = ((ToolStripMenuItem) sender).Text;
639  
640 if (string.IsNullOrEmpty(selectedFile))
641 {
642 return;
643 }
644  
645 var fileInfo = new FileInfo(selectedFile);
646  
647 var combos = await LoadCombos(fileInfo);
648  
649 if (combos == null)
650 {
651 return;
652 }
653  
654 Charge.StartCharge(combos);
655  
656 Configuration.Spring.Store = openFileDialog1.FileName;
657  
658 if (!Configuration.Spring.History.Contains(openFileDialog1.FileName))
659 {
660 Configuration.Spring.History.Add(openFileDialog1.FileName);
661 }
662 }
663  
664 private async void CheckForUpdatesToolStripMenuItem_Click(object sender, EventArgs e)
665 {
666 // Manually check for updates, this will not show a ui
667 var result = await _sparkle.CheckForUpdatesQuietly();
668 var updates = result.Updates;
669 if (result.Status == UpdateStatus.UpdateAvailable)
670 {
671 // if update(s) are found, then we have to trigger the UI to show it gracefully
672 _sparkle.ShowUpdateNeededUI();
673 return;
674 }
675  
676 MessageBox.Show("No updates available at this time.", "Spring", MessageBoxButtons.OK,
677 MessageBoxIcon.Asterisk,
678 MessageBoxDefaultButton.Button1, MessageBoxOptions.DefaultDesktopOnly, false);
679 }
680  
681 #endregion
682  
683 #region Private Methods
684  
685 private async void DoComboQueueAction(ComboQueueItem springComboQueueItem)
686 {
687 var serialization = await springComboQueueItem.Combos.Serialize();
688  
689 switch (serialization)
690 {
691 case SerializationSuccess<Combos> serializationSuccess:
692 var comboText = serializationSuccess.Text;
693  
694 var qrCodeData = QrGenerator.CreateQrCode(Helpers.Sha1Hash(comboText),
695 QRCodeGenerator.ECCLevel.L,
696 false,
697 false,
698 QRCodeGenerator.EciMode.Default,
699 1);
700  
701 var qrCode = new QRCode(qrCodeData);
702 var qrCodeImage = qrCode.GetGraphic(20);
703 var image = Helpers.Crop(qrCodeImage);
704  
705 // Set the QR code hash and the combos.
706 comboQueuePictureBox4.BackgroundImage = comboQueuePictureBox3.BackgroundImage;
707 comboQueuePictureBox4.Tag = comboQueuePictureBox3.Tag;
708 comboQueuePictureBox3.BackgroundImage = comboQueuePictureBox2.BackgroundImage;
709 comboQueuePictureBox3.Tag = comboQueuePictureBox2.Tag;
710 comboQueuePictureBox2.BackgroundImage = comboQueuePictureBox1.BackgroundImage;
711 comboQueuePictureBox2.Tag = comboQueuePictureBox1.Tag;
712 comboQueuePictureBox1.BackgroundImage = image;
713 comboQueuePictureBox1.Tag = serializationSuccess.Result;
714  
715 break;
716  
717 case SerializationFailure serializationFailure:
718 break;
719 }
720 }
721  
722 private async Task<Combos> LoadCombos(FileInfo file)
723 {
724 switch (await ComboSerialization.Deserialize(file, 0))
725 {
726 case SerializationSuccess<Combos> serializationSuccess:
727 toolStripStatusLabel1.Text = $"{Strings.Combos_read_successfully}";
728  
729 return serializationSuccess.Result;
730  
731 case SerializationFailure deserializationFailure:
732 toolStripStatusLabel1.Text =
733 $"{Strings.Failed_to_read_combos}: {deserializationFailure.Exception.Message}";
734  
735 break;
736 }
737  
738 return null;
739 }
740  
741 /// <summary>
742 /// Serialize combos to file.
743 /// </summary>
744 /// <param name="file">the file to which the combos will be serialized</param>
745 /// <returns>an awaitable task</returns>
746 private async Task SaveCombos(string file)
747 {
748 switch (await Charge.Combos.Serialize(file))
749 {
750 case SerializationSuccess<Combos> serializationSuccess:
751 toolStripStatusLabel1.Text = $"{Strings.Combos_wrote_successfully}";
752  
753 break;
754  
755 case SerializationFailure deserializationFailure:
756 toolStripStatusLabel1.Text =
757 $"{Strings.Failed_to_write_combos}: {deserializationFailure.Exception.Message}";
758  
759 break;
760 }
761 }
762  
763 private void ActionCharge(TimeSpan x)
764 {
765 this.InvokeIfRequired(form =>
766 {
767 switch (x.Seconds - Configuration.Spring.Charge.ChargeSeconds)
768 {
769 case 0:
770 form.label5.Text = Resources.Infinity;
771 OSD.Text = OSD.Symbols.KeyDown(Resources.Infinity);
772  
773 break;
774  
775 default:
776 var comboText = (x.Seconds - Configuration.Spring.Charge.ChargeSeconds).ToString();
777  
778 form.label5.Text = comboText;
779  
780 OSD.Text = OSD.Symbols.KeyDown(comboText);
781  
782 break;
783 }
784 });
785 }
786  
787 private void ActionNoCharge(TimeSpan x)
788 {
789 this.InvokeIfRequired(form =>
790 {
791 switch (x.Seconds < Configuration.Spring.Charge.ChargeSeconds)
792 {
793 case true:
794 form.label5.Text = string.Empty;
795 OSD.Text = OSD.Symbols.KeyPressed();
796  
797 break;
798  
799 default:
800 form.label5.Text = Resources.Infinity;
801 OSD.Text = OSD.Symbols.KeyDown(Resources.Infinity);
802  
803 break;
804 }
805 });
806 }
807  
808 private async Task Initialize(Combos springCombos)
809 {
810 Key = new Key(this, Configuration, KeyboardMouseEvents);
811 Key.KeyMessage += Key_KeyMessage;
812 WatchKey = new KeyWatch(KeyboardMouseEvents, Key);
813 WatchKey.SpeedAdjusted += WatchKey_SpeedAdjusted;
814 WatchKey.KeyDown += WatchKey_KeyDown;
815 WatchKey.KeyUp += WatchKey_KeyUp;
816  
817 Charge = new Charge(springCombos, Configuration, KeyboardMouseEvents, Key, WatchKey);
818 Charge.ChargeStart += ChargeStart;
819 Charge.ChargeChanged += ChargeChanged;
820 Charge.ChargeLoaded += Charge_ChargeLoaded;
821  
822 Discharge = new Discharge(this, Configuration, Key, WatchKey, Charge, SpeedBar.Value);
823 Discharge.SpringDischargeStart += Discharge_DischargeStart;
824 Discharge.SpringDischargeStop += Discharge_DischargeStop;
825 Discharge.SpringDischargeProgress += Discharge_DischargeProgress;
826  
827 Configuration.Spring.Charge.PropertyChanged += Loading_PropertyChanged;
828 Configuration.Spring.PropertyChanged += Spring_PropertyChanged;
829 Configuration.Spring.History.CollectionChanged += History_CollectionChanged;
830  
831 OSD = new OSD(Configuration, KeyboardMouseEvents);
832  
833 if (OSD.Configuration.HUD.Enable)
834 {
835 OSD.Text = OSD.Symbols.HUDShown();
836 }
837  
838 _rangeTree = new RangeTreeAction<TimeSpan, int>
839 {
840 {
841 TimeSpan.FromSeconds(0),
842 TimeSpan.FromSeconds(Configuration.Spring.Charge.ChargeSeconds),
843 ActionNoCharge
844 },
845 {
846 TimeSpan.FromSeconds(Configuration.Spring.Charge.ChargeSeconds),
847 TimeSpan.FromSeconds(Configuration.Spring.Charge.ChargeSeconds),
848 ActionNoCharge
849 },
850 {
851 TimeSpan.FromSeconds(Configuration.Spring.Charge.ChargeSeconds),
852 TimeSpan.FromSeconds(Configuration.Spring.Charge.MaximumRepeats +
853 Configuration.Spring.Charge.ChargeSeconds),
854 ActionCharge
855 },
856 {
857 TimeSpan.FromSeconds(Configuration.Spring.Charge.MaximumRepeats +
858 Configuration.Spring.Charge.ChargeSeconds),
859 TimeSpan.MaxValue, ActionNoCharge
860 }
861 };
862  
863 RecreateHistoryMenu(Configuration.Spring.History);
864  
865 await Key.Load();
866 }
867  
868 private void RecreateHistoryMenu(IEnumerable<string> springHistory)
869 {
870 var history = springHistory.ToList();
871  
872 foreach (var toolStripMenuItem in
873 historyToolStripMenuItem.DropDownItems.OfType<ToolStripMenuItem>())
874 {
875 toolStripMenuItem.Click -= HistoryToolStripMenu_Click;
876  
877 history.Add(toolStripMenuItem.Text);
878 }
879  
880 historyToolStripMenuItem.DropDownItems.Clear();
881  
882 foreach (var item in history.Distinct())
883 {
884 if (string.IsNullOrEmpty(item))
885 {
886 continue;
887 }
888  
889 historyToolStripMenuItem.DropDownItems.Add(item, null, HistoryToolStripMenu_Click);
890 }
891 }
892  
893 #endregion
894 }
895 }