From 5260d8a08714616fc1b7e22d0ef1c6f9fccf48b0 Mon Sep 17 00:00:00 2001 From: Terry MacDonald Date: Sun, 7 Mar 2021 13:45:49 +1300 Subject: [PATCH] Refactor shortcut validity Moved the logic to the right places. Removed the need for the separate error and warning lookup arrays. --- DisplayMagician/ShortcutItem.cs | 168 +++++++++++++++--- DisplayMagician/ShortcutRepository.cs | 38 ++-- .../UIForms/ImageListViewRenderers.cs | 19 +- .../UIForms/ShortcutLibraryForm.cs | 88 +++++---- 4 files changed, 218 insertions(+), 95 deletions(-) diff --git a/DisplayMagician/ShortcutItem.cs b/DisplayMagician/ShortcutItem.cs index 0f2b509..a3dba3e 100644 --- a/DisplayMagician/ShortcutItem.cs +++ b/DisplayMagician/ShortcutItem.cs @@ -67,6 +67,12 @@ namespace DisplayMagician public bool GameArgumentsRequired; } + public struct ShortcutError + { + public string Name; + public ShortcutValidity Validity; + public string Message; + } public class ShortcutItem : IComparable { @@ -99,7 +105,8 @@ namespace DisplayMagician private ShortcutPermanence _audioPermanence = ShortcutPermanence.Temporary; private ShortcutPermanence _capturePermanence = ShortcutPermanence.Temporary; private bool _autoName = true; - private bool _isPossible; + private ShortcutValidity _isValid; + private List _shortcutErrors = new List(); private List _startPrograms; private Bitmap _shortcutBitmap, _originalLargeBitmap, _originalSmallBitmap; [JsonIgnore] @@ -1142,16 +1149,30 @@ namespace DisplayMagician } } + [JsonIgnore] - public bool IsPossible + public ShortcutValidity IsValid { get { - return _isPossible; + return _isValid; } set { - _isPossible = value; + _isValid = value; + } + } + + [JsonIgnore] + public List Errors + { + get + { + return _shortcutErrors; + } + set + { + _shortcutErrors = value; } } @@ -1741,7 +1762,8 @@ namespace DisplayMagician shortcut.OriginalLargeBitmap = OriginalLargeBitmap; shortcut.ShortcutBitmap = ShortcutBitmap; shortcut.SavedShortcutIconCacheFilename = SavedShortcutIconCacheFilename; - shortcut.IsPossible = IsPossible; + shortcut.IsValid = IsValid; + shortcut.Errors.AddRange(Errors); shortcut.StartPrograms = StartPrograms; shortcut.ChangeAudioDevice = ChangeAudioDevice; shortcut.AudioDevice = AudioDevice; @@ -1935,21 +1957,35 @@ namespace DisplayMagician return multiIcon; } - public (ShortcutValidity, string) IsValid() + public void RefreshValidity() { // Do some validation checks to make sure the shortcut is sensible // And that we have enough to try and action within the shortcut // (in other words check everything in the shortcut is still valid) + ShortcutValidity worstError = ShortcutValidity.Valid; + // Does the profile we want to Use still exist? - // Is the profile still valid right now? i.e. are all the screens available? - if (ProfileToUse == null) + if (!ProfileRepository.ContainsProfile(ProfileUUID)) { - return (ShortcutValidity.Error, string.Format("The profile does not exist (probably deleted) and cannot be used.")); + ShortcutError error = new ShortcutError(); + error.Name = "ProfileNotExist"; + error.Validity = ShortcutValidity.Error; + error.Message = $"The profile does not exist (probably deleted) and cannot be used."; + _shortcutErrors.Add(error); + if (worstError != ShortcutValidity.Error) + worstError = ShortcutValidity.Error; } - if (!ProfileToUse.IsPossible) + // Is the profile still valid right now? i.e. are all the screens available? + if (ProfileToUse != null && !ProfileToUse.IsPossible) { - return (ShortcutValidity.Warning, string.Format("The profile '{0}' is not valid right now and cannot be used.", ProfileToUse.Name)); + ShortcutError error = new ShortcutError(); + error.Name = "InvalidProfile"; + error.Validity = ShortcutValidity.Warning; + error.Message = $"The profile '{ProfileToUse.Name}' is not valid right now and cannot be used."; + _shortcutErrors.Add(error); + if (worstError != ShortcutValidity.Error) + worstError = ShortcutValidity.Warning; } // Is the main application still installed? if (Category.Equals(ShortcutCategory.Application)) @@ -1957,7 +1993,13 @@ namespace DisplayMagician // We need to check if the Application still exists if (!System.IO.File.Exists(ExecutableNameAndPath)) { - return (ShortcutValidity.Warning, string.Format("The application executable '{0}' does not exist, or cannot be accessed by DisplayMagician.", ExecutableNameAndPath)); + ShortcutError error = new ShortcutError(); + error.Name = "InvalidExecutableNameAndPath"; + error.Validity = ShortcutValidity.Error; + error.Message = $"The application executable '{ExecutableNameAndPath}' does not exist, or cannot be accessed by DisplayMagician."; + _shortcutErrors.Add(error); + if (worstError != ShortcutValidity.Error) + worstError = ShortcutValidity.Error; } } @@ -1971,34 +2013,55 @@ namespace DisplayMagician // Check if Steam is installed and error if it isn't if (!SteamLibrary.IsSteamInstalled) { - return (ShortcutValidity.Error, Language.Steam_executable_file_not_found); + ShortcutError error = new ShortcutError(); + error.Name = "SteamNotInstalled"; + error.Validity = ShortcutValidity.Error; + error.Message = $"Steam is not installed on this computer."; + _shortcutErrors.Add(error); + if (worstError != ShortcutValidity.Error) + worstError = ShortcutValidity.Error; } // We need to look up details about the game if (!SteamLibrary.ContainsSteamGame(GameAppId)) { - return (ShortcutValidity.Error, string.Format("The Steam Game with AppID '{0}' is not installed on this computer.", GameAppId)); + ShortcutError error = new ShortcutError(); + error.Name = "SteamGameNotInstalled"; + error.Validity = ShortcutValidity.Error; + error.Message = $"The Steam Game with AppID '{GameAppId}' is not installed on this computer."; + _shortcutErrors.Add(error); + if (worstError != ShortcutValidity.Error) + worstError = ShortcutValidity.Error; } } // If the game is a Uplay Game we check for that else if (GameLibrary.Equals(SupportedGameLibrary.Uplay)) { - // First check if Steam is installed - // Check if Steam is installed and error if it isn't + // First check if Uplay is installed + // Check if Uplay is installed and error if it isn't if (!UplayLibrary.IsUplayInstalled) { - return (ShortcutValidity.Error, "Cannot find the Uplay executable! Uplay doesn't appear to be installed"); + ShortcutError error = new ShortcutError(); + error.Name = "UplayNotInstalled"; + error.Validity = ShortcutValidity.Error; + error.Message = $"Uplay is not installed on this computer."; + _shortcutErrors.Add(error); + if (worstError != ShortcutValidity.Error) + worstError = ShortcutValidity.Error; } // We need to look up details about the game if (!UplayLibrary.ContainsUplayGame(GameAppId)) { - return (ShortcutValidity.Error, string.Format("The Uplay Game with AppID '{0}' is not installed on this computer.", GameAppId)); + ShortcutError error = new ShortcutError(); + error.Name = "UplayGameNotInstalled"; + error.Validity = ShortcutValidity.Error; + error.Message = $"The Uplay Game with AppID '{GameAppId}' is not installed on this computer."; + _shortcutErrors.Add(error); + if (worstError != ShortcutValidity.Error) + worstError = ShortcutValidity.Error; } - } - - } // Check the Audio Device is still valid (if one is specified) if (ChangeAudioDevice) @@ -2010,11 +2073,35 @@ namespace DisplayMagician if (audioDevice.FullName.Equals(AudioDevice)) { if (audioDevice.State == AudioSwitcher.AudioApi.DeviceState.Disabled) - return (ShortcutValidity.Warning, $"The Audio Device {AudioDevice} is disabled, so the shortcut '{Name}' cannot be used. You need to enable the audio device to use this shortcut, or edit the shortcut to change the audio device."); + { + ShortcutError error = new ShortcutError(); + error.Name = "AudioDeviceDisabled"; + error.Validity = ShortcutValidity.Warning; + error.Message = $"The Audio Device { AudioDevice} is disabled, so the shortcut '{Name}' cannot be used.You need to enable the audio device to use this shortcut, or edit the shortcut to change the audio device."; + _shortcutErrors.Add(error); + if (worstError != ShortcutValidity.Error) + worstError = ShortcutValidity.Warning; + } if (audioDevice.State == AudioSwitcher.AudioApi.DeviceState.NotPresent) - return (ShortcutValidity.Warning, $"The Audio Device {AudioDevice} is not present, so the shortcut '{Name}' cannot be used."); + { + ShortcutError error = new ShortcutError(); + error.Name = "AudioDeviceNotPresent"; + error.Validity = ShortcutValidity.Error; + error.Message = $"The Audio Device {AudioDevice} is not present, so the shortcut '{Name}' cannot be used."; + _shortcutErrors.Add(error); + if (worstError != ShortcutValidity.Error) + worstError = ShortcutValidity.Error; + } if (audioDevice.State == AudioSwitcher.AudioApi.DeviceState.Unplugged) - return (ShortcutValidity.Warning, $"The Audio Device {AudioDevice} is unplugged, so the shortcut '{Name}' cannot be used. You need to plug in the audio device to use this shortcut, or edit the shortcut to change the audio device."); + { + ShortcutError error = new ShortcutError(); + error.Name = "AudioDeviceUnplugged"; + error.Validity = ShortcutValidity.Warning; + error.Message = $"The Audio Device {AudioDevice} is unplugged, so the shortcut '{Name}' cannot be used. You need to plug in the audio device to use this shortcut, or edit the shortcut to change the audio device."; + _shortcutErrors.Add(error); + if (worstError != ShortcutValidity.Error) + worstError = ShortcutValidity.Warning; + } } } } @@ -2028,18 +2115,43 @@ namespace DisplayMagician if (captureDevice.FullName.Equals(CaptureDevice)) { if (captureDevice.State == AudioSwitcher.AudioApi.DeviceState.Disabled) - return (ShortcutValidity.Warning, $"The Capture Device {CaptureDevice} is disabled, so the shortcut '{Name}' cannot be used. You need to enable the capture device to use this shortcut, or edit the shortcut to change the capture device."); + { + ShortcutError error = new ShortcutError(); + error.Name = "CaptureDeviceDisabled"; + error.Validity = ShortcutValidity.Warning; + error.Message = $"The Capture Device {CaptureDevice} is disabled, so the shortcut '{Name}' cannot be used. You need to enable the capture device to use this shortcut, or edit the shortcut to change the capture device."; + _shortcutErrors.Add(error); + if (worstError != ShortcutValidity.Error) + worstError = ShortcutValidity.Warning; + } if (captureDevice.State == AudioSwitcher.AudioApi.DeviceState.NotPresent) - return (ShortcutValidity.Warning, $"The Capture Device {CaptureDevice} is not present, so the shortcut '{Name}' cannot be used."); + { + ShortcutError error = new ShortcutError(); + error.Name = "CaptureDeviceNotPresent"; + error.Validity = ShortcutValidity.Error; + error.Message = $"The Capture Device {CaptureDevice} is not present, so the shortcut '{Name}' cannot be used."; + _shortcutErrors.Add(error); + if (worstError != ShortcutValidity.Error) + worstError = ShortcutValidity.Error; + } if (captureDevice.State == AudioSwitcher.AudioApi.DeviceState.Unplugged) - return (ShortcutValidity.Warning, $"The Capture Device {CaptureDevice} is unplugged, so the shortcut '{Name}' cannot be used. You need to plug in the capture device to use this shortcut, or edit the shortcut to change the capture device."); + { + ShortcutError error = new ShortcutError(); + error.Name = "CaptureDeviceUnplugged"; + error.Validity = ShortcutValidity.Warning; + error.Message = $"The Capture Device {CaptureDevice} is unplugged, so the shortcut '{Name}' cannot be used. You need to plug in the capture device to use this shortcut, or edit the shortcut to change the capture device."; + _shortcutErrors.Add(error); + if (worstError != ShortcutValidity.Error) + worstError = ShortcutValidity.Warning; + } } } } // TODO Do all the specified pre-start apps still exist? - return (ShortcutValidity.Valid, "Shortcut is valid"); + // Save the worst error level to IsValid property + IsValid = worstError; } diff --git a/DisplayMagician/ShortcutRepository.cs b/DisplayMagician/ShortcutRepository.cs index 6aba7ad..b1c0dde 100644 --- a/DisplayMagician/ShortcutRepository.cs +++ b/DisplayMagician/ShortcutRepository.cs @@ -26,8 +26,8 @@ namespace DisplayMagician #region Class Variables // Common items to the class private static List _allShortcuts = new List(); - public static Dictionary _shortcutWarningLookup = new Dictionary(); - public static Dictionary _shortcutErrorLookup = new Dictionary(); + //public static Dictionary _shortcutWarningLookup = new Dictionary(); + //public static Dictionary _shortcutErrorLookup = new Dictionary(); private static bool _shortcutsLoaded = false; // Other constants that are useful private static string AppShortcutStoragePath = Path.Combine(Program.AppDataPath, $"Shortcuts"); @@ -76,7 +76,6 @@ namespace DisplayMagician // Load the Shortcuts from storage LoadShortcuts(); - IsValidRefresh(); } #endregion @@ -94,7 +93,7 @@ namespace DisplayMagician } } - public static Dictionary ShortcutWarningLookup + /*public static Dictionary ShortcutWarningLookup { get { @@ -116,7 +115,7 @@ namespace DisplayMagician return _shortcutErrorLookup; } - } + }*/ public static int ShortcutCount { @@ -163,7 +162,7 @@ namespace DisplayMagician { // Save the shortcuts JSON as it's different SaveShortcuts(); - + IsValidRefresh(); return true; } else @@ -199,6 +198,7 @@ namespace DisplayMagician if (numRemoved == 1) { SaveShortcuts(); + IsValidRefresh(); logger.Debug($"ShortcutRepository/RemoveShortcut: Our shortcut repository does contain a shortcut we were looking for"); return true; } @@ -254,6 +254,7 @@ namespace DisplayMagician if (numRemoved == 1) { SaveShortcuts(); + IsValidRefresh(); logger.Debug($"ShortcutRepository/RemoveShortcut2: Our shortcut repository does contain a shortcut with Name or UUID {shortcutNameOrUuid}"); return true; } @@ -392,7 +393,7 @@ namespace DisplayMagician } SaveShortcuts(); - + IsValidRefresh(); return true; } @@ -447,7 +448,6 @@ namespace DisplayMagician { // And assign the matching Profile if we find it. updatedShortcut.ProfileToUse = profile; - updatedShortcut.IsPossible = true; break; } } @@ -455,7 +455,6 @@ namespace DisplayMagician // We should only get here if there isn't a profile to match to. logger.Debug($"ShortcutRepository/LoadShortcuts: Couldn't find the profile with UUID {updatedShortcut.ProfileUUID} so couldn't link it to a profile! We can't use this shortcut."); updatedShortcut.ProfileToUse = null; - updatedShortcut.IsPossible = false; } // Sort the shortcuts alphabetically @@ -544,19 +543,9 @@ namespace DisplayMagician // We need to refresh the cached answer // Get the list of connected devices - _shortcutWarningLookup.Clear(); - _shortcutErrorLookup.Clear(); - foreach (ShortcutItem loadedShortcut in AllShortcuts) { - _shortcutWarningLookup[loadedShortcut.Name] = false; - _shortcutErrorLookup[loadedShortcut.Name] = false; - - (ShortcutValidity result, string thing) = (loadedShortcut.IsValid()); - if (result == ShortcutValidity.Warning) - ShortcutWarningLookup[loadedShortcut.Name] = true; - if (result == ShortcutValidity.Error) - ShortcutErrorLookup[loadedShortcut.Name] = true; + loadedShortcut.RefreshValidity(); } } @@ -573,12 +562,15 @@ namespace DisplayMagician if (!(shortcutToUse is ShortcutItem)) return; - (ShortcutValidity valid, string reason) = shortcutToUse.IsValid(); - if (valid == ShortcutValidity.Error || valid == ShortcutValidity.Warning) + // Check the shortcut is still valid. + shortcutToUse.RefreshValidity(); + + if (shortcutToUse.IsValid == ShortcutValidity.Error || shortcutToUse.IsValid == ShortcutValidity.Warning) { logger.Error($"ShortcutRepository/RunShortcut: Cannot run the shortcut {shortcutToUse.Name} as it isn't valid"); + string errorReasons = String.Join(", ", (from error in shortcutToUse.Errors select error.Message)); MessageBox.Show( - $"Unable to run the shortcut '{shortcutToUse.Name}': {reason}", + $"Unable to run the shortcut '{shortcutToUse.Name}': {errorReasons}", @"Cannot run the Shortcut", MessageBoxButtons.OK, MessageBoxIcon.Exclamation); diff --git a/DisplayMagician/UIForms/ImageListViewRenderers.cs b/DisplayMagician/UIForms/ImageListViewRenderers.cs index 3e67343..9689c67 100644 --- a/DisplayMagician/UIForms/ImageListViewRenderers.cs +++ b/DisplayMagician/UIForms/ImageListViewRenderers.cs @@ -96,7 +96,8 @@ namespace DisplayMagician.UIForms { Rectangle pos = Utility.GetSizedImageBounds(img, new Rectangle(bounds.Location + itemPadding, ImageListView.ThumbnailSize)); - if (ShortcutRepository.ShortcutErrorLookup[item.Text]) + ShortcutItem shortcutToRender = ShortcutRepository.GetShortcut(item.Text); + if (shortcutToRender.IsValid == ShortcutValidity.Error) { // The shortcut is permanently invalid (game removed or profile deleted) // so we make the image grayscale @@ -107,7 +108,7 @@ namespace DisplayMagician.UIForms // right in the centre g.DrawImage(Properties.Resources.Error, pos.X + 30, pos.Y + 30, 40, 40); } - else if (ShortcutRepository.ShortcutWarningLookup[item.Text]) + else if (shortcutToRender.IsValid == ShortcutValidity.Warning) { // The shortcut is temporaily invalid (e.g. screens aren't right at the moment) // so we make the image grayscale @@ -266,7 +267,13 @@ namespace DisplayMagician.UIForms { Rectangle pos = Utility.GetSizedImageBounds(img, new Rectangle(bounds.Location + itemPadding, ImageListView.ThumbnailSize)); - if (ProfileRepository.ProfileWarningLookup[item.Text]) + ProfileItem profileToRender = ProfileRepository.GetProfile(item.Text); + if (profileToRender.IsPossible) + { + // Draw the full color image as the shortcuts is not invalid + g.DrawImage(img, pos); + } + else { // THe shortcut is invalid // so we make the image grayscale @@ -277,11 +284,7 @@ namespace DisplayMagician.UIForms // right in the centre g.DrawImage(Properties.Resources.Warning, pos.X + 30, pos.Y + 30, 40, 40); } - else - { - // Draw the full color image as the shortcuts is not invalid - g.DrawImage(img, pos); - } + // Draw image border if (Math.Min(pos.Width, pos.Height) > 32) diff --git a/DisplayMagician/UIForms/ShortcutLibraryForm.cs b/DisplayMagician/UIForms/ShortcutLibraryForm.cs index 3b6665c..807cbd9 100644 --- a/DisplayMagician/UIForms/ShortcutLibraryForm.cs +++ b/DisplayMagician/UIForms/ShortcutLibraryForm.cs @@ -38,6 +38,12 @@ namespace DisplayMagician.UIForms private void ShortcutLibraryForm_Load(object sender, EventArgs e) { + // Refresh the profiles and the shortcut validity to start + // The rest of the refreshing happens as the shortcuts are added + // and deleted. + ProfileRepository.IsPossibleRefresh(); + ShortcutRepository.IsValidRefresh(); + // Refresh the Shortcut Library UI RefreshShortcutLibraryUI(); @@ -52,15 +58,11 @@ namespace DisplayMagician.UIForms if (ShortcutRepository.ShortcutCount == 0) return; - ProfileRepository.IsPossibleRefresh(); - - // Temporarily stop updating the saved_profiles listview ilv_saved_shortcuts.SuspendLayout(); ImageListViewItem newItem = null; ilv_saved_shortcuts.Items.Clear(); - ShortcutRepository.IsValidRefresh(); foreach (ShortcutItem loadedShortcut in ShortcutRepository.AllShortcuts.OrderBy(s => s.Name)) { @@ -68,7 +70,21 @@ namespace DisplayMagician.UIForms // Select it if its the selectedProfile if (_selectedShortcut is ShortcutItem && _selectedShortcut.Equals(loadedShortcut)) + { newItem.Selected = true; + // Hide the run button if the shortcut isn't valid + if (_selectedShortcut.IsValid == ShortcutValidity.Warning || _selectedShortcut.IsValid == ShortcutValidity.Error) + { + btn_run.Visible = false; + cms_shortcuts.Items[1].Enabled = false; + } + + else + { + btn_run.Visible = true; + cms_shortcuts.Items[1].Enabled = true; + } + } //ilv_saved_profiles.Items.Add(newItem); ilv_saved_shortcuts.Items.Add(newItem, _shortcutAdaptor); @@ -95,8 +111,7 @@ namespace DisplayMagician.UIForms // if shortcut is not valid then ask if the user // really wants to save it to desktop - (ShortcutValidity valid, string reason) = _selectedShortcut.IsValid(); - if (valid == ShortcutValidity.Error || valid == ShortcutValidity.Warning) + if (_selectedShortcut.IsValid == ShortcutValidity.Error || _selectedShortcut.IsValid == ShortcutValidity.Warning) { // We ask the user of they still want to save the desktop shortcut if (MessageBox.Show($"The shortcut '{_selectedShortcut.Name}' isn't valid for some reason so a desktop shortcut wouldn't work until the shortcut is fixed. Has your hardware or screen layout changed from when the shortcut was made? We recommend that you edit the shortcut to make it valid again, or reverse the hardware changes you made. Do you still want to save the desktop shortcut?", $"Still save the '{_selectedShortcut.Name}' Desktop Shortcut?", MessageBoxButtons.YesNo, MessageBoxIcon.Error) == DialogResult.No) @@ -166,35 +181,11 @@ namespace DisplayMagician.UIForms private void ilv_saved_shortcuts_ItemClick(object sender, ItemClickEventArgs e) { + // This is the single click to select _selectedShortcut = GetShortcutFromName(e.Item.Text); - SetRunOption(); - - if (e.Buttons == MouseButtons.Right) - { - cms_shortcuts.Show(ilv_saved_shortcuts,e.Location); - } - } - - private void ilv_saved_shortcuts_ItemDoubleClick(object sender, ItemClickEventArgs e) - { - _selectedShortcut = GetShortcutFromName(e.Item.Text); - - SetRunOption(); - - if (_selectedShortcut == null) - return; - - if (!ShortcutRepository.ShortcutWarningLookup[_selectedShortcut.Name]) - return; - - // Run the selected shortcut - btn_run.PerformClick(); - } - - private void SetRunOption() - { - if (ShortcutRepository.ShortcutWarningLookup[_selectedShortcut.Name] || ShortcutRepository.ShortcutErrorLookup[_selectedShortcut.Name]) + // Hide the run button if the shortcut isn't valid + if (_selectedShortcut.IsValid == ShortcutValidity.Warning || _selectedShortcut.IsValid == ShortcutValidity.Error) { btn_run.Visible = false; cms_shortcuts.Items[1].Enabled = false; @@ -205,7 +196,33 @@ namespace DisplayMagician.UIForms btn_run.Visible = true; cms_shortcuts.Items[1].Enabled = true; } - + + if (e.Buttons == MouseButtons.Right) + { + cms_shortcuts.Show(ilv_saved_shortcuts,e.Location); + } + } + + private void ilv_saved_shortcuts_ItemDoubleClick(object sender, ItemClickEventArgs e) + { + // This is the double click to run + _selectedShortcut = GetShortcutFromName(e.Item.Text); + + // Hide the run button if the shortcut isn't valid + if (_selectedShortcut.IsValid == ShortcutValidity.Warning || _selectedShortcut.IsValid == ShortcutValidity.Error) + { + btn_run.Visible = false; + cms_shortcuts.Items[1].Enabled = false; + } + + else + { + btn_run.Visible = true; + cms_shortcuts.Items[1].Enabled = true; + } + + // Run the selected shortcut + btn_run.PerformClick(); } private void btn_new_Click(object sender, EventArgs e) @@ -273,8 +290,7 @@ namespace DisplayMagician.UIForms return; // Only run the if shortcut is valid - (ShortcutValidity valid, string reason) = _selectedShortcut.IsValid(); - if (valid == ShortcutValidity.Error || valid == ShortcutValidity.Warning) + if (_selectedShortcut.IsValid == ShortcutValidity.Warning || _selectedShortcut.IsValid == ShortcutValidity.Error) { // We tell the user the reason that we couldnt run the shortcut if (MessageBox.Show($"The shortcut '{_selectedShortcut.Name}' isn't valid for some reason so we cannot run the application or game. Has your hardware or screen layout changed from when the shortcut was made? We recommend that you edit the shortcut to make it valid again, or reverse the hardware changes you made. Do you want to do that now?", $"Edit the '{_selectedShortcut.Name}' Shortcut?", MessageBoxButtons.YesNo, MessageBoxIcon.Error) == DialogResult.No)