diff --git a/CFixer/AppManagerService.cs b/CFixer/AppManagerService.cs index b3497b3..02ca5a8 100644 --- a/CFixer/AppManagerService.cs +++ b/CFixer/AppManagerService.cs @@ -9,7 +9,6 @@ using Windows.Management.Deployment; namespace CrapFixer { - public class AppAnalysisResult { public string AppName { get; set; } @@ -43,13 +42,18 @@ namespace CrapFixer } /// - /// Analyzes the apps based on predefined apps (from resources) and logs the results in MainForm. + /// Analyzes the installed apps against provided bloatware patterns and whitelist, + /// logs the results, and returns the matches. /// - /// - /// - public async Task> AnalyzeAndLogAppsAsync(string[] predefinedApps) + /// List of bloatware keywords to check against + /// List of app names to ignore + /// If true, scans all apps regardless of bloatware patterns + public async Task> AnalyzeAndLogAppsAsync( + string[] bloatwarePatterns, + string[] whitelistPatterns, + bool scanAll) { - var apps = await AnalyzeAppsAsync(predefinedApps); + var apps = await AnalyzeAppsAsync(bloatwarePatterns, whitelistPatterns, scanAll); if (apps.Count > 0) { @@ -64,37 +68,56 @@ namespace CrapFixer Logger.Log("✅ No Microsoft Store bloatware apps found.", LogLevel.Info); } + Logger.Log(""); // Add a blank line for spacing + return apps; } - /// /// Analyzes the apps based on predefined apps (from resources) and returns matching apps. /// /// /// - public async Task> AnalyzeAppsAsync(string[] predefinedApps) + public async Task> AnalyzeAppsAsync(string[] bloatwarePatterns, string[] whitelistPatterns, bool scanAll = false) { Logger.Log("\n🧩 APPS ANALYSIS", LogLevel.Info); Logger.Log(new string('=', 50), LogLevel.Info); - await LoadAppsAsync(); // Load all apps before analysis + await LoadAppsAsync(); // Load all installed apps var result = new List(); - // Check each app's name against predefined patterns foreach (var app in _appDirectory) { - foreach (string pattern in predefinedApps) + string appName = app.Key.ToLower(); + + // Always skip whitelisted apps + if (whitelistPatterns.Any(w => appName.Contains(w))) + continue; + + if (scanAll) { - if (app.Key.ToLower().Contains(pattern.ToLower())) + // If wildcard is set, include everything not whitelisted + result.Add(new AppAnalysisResult { - result.Add(new AppAnalysisResult + AppName = app.Key, + FullName = app.Value + }); + } + else + { + // Only match against provided patterns + foreach (var pattern in bloatwarePatterns) + { + if (appName.Contains(pattern)) { - AppName = app.Key, - FullName = app.Value // Store the full name - }); - break; + result.Add(new AppAnalysisResult + { + AppName = app.Key, + FullName = app.Value + }); + break; + } } } } @@ -171,39 +194,71 @@ namespace CrapFixer return removedApps; // Return removed apps to update the UI } + /// - /// Loads an external bloatware list from the CFEnhancer text file (comma-separated). - /// Falls back to an empty array if the file doesn't exist or fails to load. + /// Loads external bloatware and whitelist patterns from a text file (e.g., CFEnhancer.txt). + /// Also checks if wildcard (*) is set to scan all apps. /// - public string[] LoadExternalBloatwareList(string fileName = "CFEnhancer.txt") + /// Name of the file to load from (must be in Plugins folder) + /// + /// A tuple containing: + /// - bloatwarePatterns: List of apps to flag as bloatware + /// - whitelistPatterns: List of apps to ignore/exclude from detection + /// - scanAll: Whether all apps should be shown regardless of matching patterns + /// + public (string[] bloatwarePatterns, string[] whitelistPatterns, bool scanAll) LoadExternalBloatwarePatterns(string fileName = "CFEnhancer.txt") { try { string exeDir = AppDomain.CurrentDomain.BaseDirectory; - string pluginsDir = Path.Combine(exeDir, "plugins"); - string fullPath = Path.Combine(pluginsDir, fileName); + string fullPath = Path.Combine(exeDir, "Plugins", fileName); if (!File.Exists(fullPath)) { - Logger.Log($"⚠️ The bloatware radar stays basic for now 🧠. Get the CFEnhancer detection list from Options > Plugins ", LogLevel.Warning); - return Array.Empty(); + Logger.Log($"⚠️ The bloatware radar stays basic for now 🧠. Get the enhanced detection list from Options > Plugins > CFEnhancer plugin", LogLevel.Warning); + return (Array.Empty(), Array.Empty(), false); } - var content = File.ReadAllText(fullPath); - return content.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries) - .Select(s => s.Trim()) - .ToArray(); + var lines = File.ReadAllLines(fullPath); + var bloatware = new List(); // Apps to detect as bloatware + var whitelist = new List(); // Apps to ignore completely + bool scanAll = false; // Set to true if wildcard (*) is present + + foreach (var line in lines) + { + // Strip comments after "#" and trim whitespace + var entry = line.Split('#')[0].Trim(); + + // Skip empty lines or lines with only comments + if (string.IsNullOrWhiteSpace(entry)) + continue; + + // Wildcard entry means: show all installed apps + if (entry == "*" || entry == "*.*") + { + scanAll = true; + continue; + } + + // Entries starting with "!" go to the whitelist (excluded apps) + if (entry.StartsWith("!")) + whitelist.Add(entry.Substring(1).Trim().ToLower()); + else + bloatware.Add(entry.ToLower()); // All other entries are bloatware patterns + } + + return (bloatware.ToArray(), whitelist.ToArray(), scanAll); } catch (Exception ex) { Logger.Log($"Error reading external bloatware file: {ex.Message}", LogLevel.Warning); - return Array.Empty(); + return (Array.Empty(), Array.Empty(), false); } } /// - /// OPTIONALLY!Returns all installed apps in the system. + /// OPTIONALLY!Returns all installed apps in the system. /// /// public async Task> GetAllInstalledAppsAsync() diff --git a/CFixer/CFixer.csproj b/CFixer/CFixer.csproj index 1750e2b..4648efc 100644 --- a/CFixer/CFixer.csproj +++ b/CFixer/CFixer.csproj @@ -7,7 +7,7 @@ {FA7D5C89-63D3-4AF0-80C2-D650D35E575B} WinExe CFixer - CFixer + Crap Fixer v4.8.1 512 true @@ -57,7 +57,7 @@ - + @@ -78,6 +78,7 @@ + @@ -128,6 +129,11 @@ + + True + True + Resources.resx + UserControl @@ -152,6 +158,12 @@ OptionsView.cs + + UserControl + + + ViveView.cs + MainForm.cs @@ -162,6 +174,7 @@ AboutView.cs + Designer PluginsView.cs @@ -172,17 +185,15 @@ OptionsView.cs + + ViveView.cs + SettingsSingleFileGenerator Settings.Designer.cs - - True - True - Resources.resx - True Settings.settings diff --git a/CFixer/Features/AI/ClickToDo.cs b/CFixer/Features/AI/ClickToDo.cs new file mode 100644 index 0000000..e930188 --- /dev/null +++ b/CFixer/Features/AI/ClickToDo.cs @@ -0,0 +1,70 @@ +using Microsoft.Win32; +using System; +using System.Threading.Tasks; +using CrapFixer; + +namespace Settings.UI +{ + /// + /// Disables the Click to Do feature, which also removes its entry from the right-click context menu. + /// Only available on Copilot+ PCs running Windows 11 24H2 or newer. + /// Requires a PC with an NPU (Neural Processing Unit). + /// + internal class ClickToDo: FeatureBase + { + private const string keyName = @"HKEY_CURRENT_USER\Software\Microsoft\Windows\Shell\ClickToDo"; + private const string valueName = "DisableClickToDo"; + private const int recommendedValue = 1; // 1 = fully disabled, including context menu + + public override string GetFeatureDetails() + { + return $"{keyName} | Value: {valueName} | Set to: {recommendedValue} (disables Click to Do, removing it from context menus). " + + "Note: This setting only applies on Copilot+ PCs with Windows 11 24H2 or newer."; + } + + + + public override string ID() + { + return "Disable Click to Do (Only Copilot+ PCs)"; + } + + public override string Info() + { + return "Disables Click to Do entirely, including its context menu entry which uses on-device AI to suggest actions based on screen content. Only available on Copilot+ PCs with Windows 11 24H2 or newer."; + } + + public override Task CheckFeature() + { + return Task.FromResult(Utils.IntEquals(keyName, valueName, recommendedValue)); + } + + public override Task DoFeature() + { + try + { + Registry.SetValue(keyName, valueName, recommendedValue, RegistryValueKind.DWord); + return Task.FromResult(true); + } + catch (Exception ex) + { + Logger.Log("Error disabling Click to Do: " + ex.Message, LogLevel.Error); + return Task.FromResult(false); + } + } + + public override bool UndoFeature() + { + try + { + Registry.SetValue(keyName, valueName, 0, RegistryValueKind.DWord); + return true; + } + catch (Exception ex) + { + Logger.Log("Error re-enabling Click to Do: " + ex.Message, LogLevel.Error); + return false; + } + } + } +} \ No newline at end of file diff --git a/CFixer/Features/FeatureLoader.cs b/CFixer/Features/FeatureLoader.cs index fa6c31f..1719563 100644 --- a/CFixer/Features/FeatureLoader.cs +++ b/CFixer/Features/FeatureLoader.cs @@ -1,13 +1,13 @@ -using Settings.Issues; -using Settings.Ads; +using Settings.Ads; using Settings.AI; using Settings.Edge; using Settings.Gaming; +using Settings.Issues; using Settings.Personalization; using Settings.Privacy; using Settings.System; -using System.Collections.Generic; using Settings.UI; +using System.Collections.Generic; namespace Features { @@ -69,6 +69,7 @@ namespace Features new FeatureNode(new ShowOrHideMostUsedApps()), new FeatureNode(new ShowTaskViewButton()), new FeatureNode(new DisableSearchBoxSuggestions()), + new FeatureNode(new DisableBingSearch()), new FeatureNode(new StartLayout()), new FeatureNode(new TaskbarAlignment()), new FeatureNode(new Transparency()), @@ -121,7 +122,7 @@ namespace Features { new FeatureNode(new CopilotTaskbar()), new FeatureNode(new Recall()), - new FeatureNode(new AskCopilot()), + new FeatureNode(new ClickToDo()) { DefaultChecked = false }, } }, }; diff --git a/CFixer/Features/FeatureManager.cs b/CFixer/Features/FeatureManager.cs index ca63ef0..2e9ac18 100644 --- a/CFixer/Features/FeatureManager.cs +++ b/CFixer/Features/FeatureManager.cs @@ -1,5 +1,6 @@ using Features; using System; +using System.Diagnostics; using System.Drawing; using System.Threading.Tasks; using System.Windows.Forms; @@ -16,12 +17,14 @@ namespace CrapFixer // Public properties to access the analysis results public static int TotalChecked => totalChecked; + public static int IssuesFound => issuesFound; public static void ResetAnalysis() { totalChecked = 0; issuesFound = 0; + Logger.Clear(); } /// @@ -29,6 +32,9 @@ namespace CrapFixer /// public static void LoadFeatures(TreeView tree) { + // Hide the TreeView to avoid flickering and visible scroll jump + tree.Visible = false; + var features = FeatureLoader.Load(); tree.Nodes.Clear(); @@ -43,6 +49,14 @@ namespace CrapFixer } tree.ExpandAll(); // expand all nodes + + // Set scroll to top and make TreeView visible + tree.BeginInvoke(new Action(() => + { + // Ensure the first node is shown at the top (prevents auto-scroll to bottom) + if (tree.Nodes.Count > 0) tree.TopNode = tree.Nodes[0]; + tree.Visible = true; + })); } /// @@ -166,52 +180,75 @@ namespace CrapFixer RestoreChecked(child); } } - - /// - /// Analyzes a selected feature and logs its status. + /// Analyzes a selected feature or, if it's a category, analyzes only checked child features. /// public static async void AnalyzeFeature(TreeNode node) { - // no && node.Checked + // Analyze this node if it's a leaf node (not a category) if (node.Tag is FeatureNode fn && !fn.IsCategory && fn.Feature != null) { bool isOk = await fn.Feature.CheckFeature(); node.ForeColor = isOk ? Color.Gray : Color.Red; - Logger.Log(isOk - ? $"✅ Feature: {fn.Name} is properly configured." - : $"❌ Feature: {fn.Name} requires attention.\n ➤ {fn.Feature.GetFeatureDetails()}", - isOk ? LogLevel.Info : LogLevel.Warning); - - if (!isOk) + if (isOk) + { + Logger.Log($"✅ Feature: {fn.Name} is properly configured.", LogLevel.Info); + } + else + { + string category = node.Parent?.Text ?? "General"; + Logger.Log($"❌ Feature: {fn.Name} requires attention.", LogLevel.Warning); + Logger.Log($" ➤ {fn.Feature.GetFeatureDetails()}"); Logger.Log(new string('-', 50), LogLevel.Info); + } + } + else + { + // If it's a category node, analyze only checked child nodes + foreach (TreeNode child in node.Nodes) + { + if (child.Checked) + AnalyzeFeature(child); + } } } /// - /// Attempts to fix the selected feature and logs the result. + /// Attempts to fix the selected feature or, if it is a category, fixes only checked child features. /// public static async Task FixFeature(TreeNode node) { - // no && node.Checked + // Try to fix this node if it is NOT a category (i.e., a leaf node) if (node.Tag is FeatureNode fn && !fn.IsCategory && fn.Feature != null) { + // Always fix the selected leaf node, regardless of Checked bool result = await fn.Feature.DoFeature(); Logger.Log(result ? $"🔧 {fn.Name} - Fixed" : $"❌ {fn.Name} - ⚠️ Fix failed (This feature may require admin privileges)", result ? LogLevel.Info : LogLevel.Error); } + else + { + // If it's a category node, fix only checked child nodes (recursively) + foreach (TreeNode child in node.Nodes) + { + if (child.Checked) + await FixFeature(child); + } + } } + /// - /// Restores the selected feature to its original state and logs the result. + /// Restores a selected feature (always) or, if it's a category, only restores checked child features. + /// Logs success or failure. /// public static void RestoreFeature(TreeNode node) { - // no && node.Checked + // Restore feature node regardless of Checked state if (node.Tag is FeatureNode fn && !fn.IsCategory && fn.Feature != null) { bool ok = fn.Feature.UndoFeature(); @@ -220,38 +257,26 @@ namespace CrapFixer : $"❌ {fn.Name} - Restore failed", ok ? LogLevel.Info : LogLevel.Error); } - } - - /// - /// Recursively previews changes for all checked features. - /// - /// - public static void PreviewChanges(TreeNode node) - { - if (node.Tag is FeatureNode fn) + else { - if (!fn.IsCategory && fn.Feature != null) - { - string details = fn.Feature.GetFeatureDetails(); - - string category = node.Parent?.Text ?? "General"; - Logger.Log($"🛈 [PREVIEW] [{category}] {fn.Name}", LogLevel.Info); - Logger.Log($" ➤ {details}"); - Logger.Log(new string('-', 50), LogLevel.Info); - } - + // For category nodes, only restore checked children foreach (TreeNode child in node.Nodes) - PreviewChanges(child); + { + if (child.Checked) + RestoreFeature(child); + } } } /// - /// Displays help information for the selected feature. + /// Displays help information for the selected feature or plugin. + /// If a feature is selected, also offers to search online. /// public static void ShowHelp(TreeNode node) { - if (node.Tag is FeatureNode fn && fn.Feature != null) + // Show help for features + if (node?.Tag is FeatureNode fn && fn.Feature != null) { string info = fn.Feature.Info(); MessageBox.Show( @@ -259,27 +284,36 @@ namespace CrapFixer $"Help: {fn.Name}", MessageBoxButtons.OK, MessageBoxIcon.Information); + + // Optional online help + var result = MessageBox.Show( + "Would you like to search online for more information about this feature?", + "Online Help", + MessageBoxButtons.YesNo, + MessageBoxIcon.Question); + + if (result == DialogResult.Yes) + { + string searchQuery = Uri.EscapeDataString(fn.Feature.GetFeatureDetails()); + string webUrl = $"https://www.google.com/search?q={searchQuery}"; + System.Diagnostics.Process.Start(new ProcessStartInfo + { + FileName = webUrl, + UseShellExecute = true + }); + } + + return; } - else + + // Show help for plugins + if (!PluginManager.ShowHelp(node)) { - MessageBox.Show("⚠️ No feature selected or feature is invalid.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); + MessageBox.Show("⚠️ No feature or plugin selected, or help info unavailable.", + "Help", + MessageBoxButtons.OK, + MessageBoxIcon.Warning); } } - - /// - /// Opens the default web browser to search for help online for the selected feature (Extended ShowHelp). - /// - /// - public static void ShowHelpOnline(TreeNode node) - { - if (node?.Tag is FeatureNode fn) - { - string searchQuery = Uri.EscapeDataString( fn.Feature.GetFeatureDetails()); - string webUrl = $"microsoft-edge:https://www.google.com/search?q={searchQuery}"; - - System.Diagnostics.Process.Start(webUrl); - } - } - } } \ No newline at end of file diff --git a/CFixer/Features/Issues/BasicCleanup.cs b/CFixer/Features/Issues/BasicCleanup.cs index 66a9dbf..d138577 100644 --- a/CFixer/Features/Issues/BasicCleanup.cs +++ b/CFixer/Features/Issues/BasicCleanup.cs @@ -23,7 +23,6 @@ namespace Settings.Issues var totalSize = GetDirectorySize(tempPath); bool isOk = totalSize <= 50; - return Task.FromResult(isOk); } @@ -34,13 +33,12 @@ namespace Settings.Issues } } - public override string GetFeatureDetails() { try { var totalSize = GetDirectorySize(tempPath); - return $"Temp folder size: {totalSize} MB (We need also to include cleanmgr in the next run)"; + return $"Temp folder size: {totalSize} MB (including cleanmgr /sagerun:1 in the next run)"; } catch (Exception ex) { @@ -53,7 +51,7 @@ namespace Settings.Issues { try { - await CleanTempFolderAsync(); + await CleanTempFolderAsync(); await RunDiskCleanup(); Logger.Log("Basic Cleanup completed successfully.", LogLevel.Info); return true; @@ -65,7 +63,6 @@ namespace Settings.Issues } } - private async Task CleanTempFolderAsync() { var files = await Task.Run(() => Directory.GetFiles(tempPath, "*", SearchOption.AllDirectories)); @@ -75,7 +72,7 @@ namespace Settings.Issues { try { - await Task.Run(() => File.Delete(file)); + await Task.Run(() => File.Delete(file)); Logger.Log($"Deleted file: {file}", LogLevel.Info); } catch (Exception ex) @@ -98,7 +95,6 @@ namespace Settings.Issues } } - // calculate the size of the directory in MB private long GetDirectorySize(string directory) { @@ -110,7 +106,6 @@ namespace Settings.Issues // Calculate size of all files size += directoryInfo.GetFiles("*", SearchOption.AllDirectories).Sum(file => file.Length); - return size / (1024 * 1024); // return size in MB } catch (Exception ex) @@ -144,13 +139,13 @@ namespace Settings.Issues using (var process1 = Process.Start(startInfo1)) { if (process1 != null) - await Task.Run(() => process1.WaitForExit()); + await Task.Run(() => process1.WaitForExit()); } using (var process2 = Process.Start(startInfo2)) { if (process2 != null) - await Task.Run(() => process2.WaitForExit()); + await Task.Run(() => process2.WaitForExit()); } } catch (Exception ex) @@ -159,8 +154,7 @@ namespace Settings.Issues } } - // Undo method: Cleanup cannot be undone, so return false public override bool UndoFeature() => false; } -} +} \ No newline at end of file diff --git a/CFixer/Features/Issues/WingetUpgrade.cs b/CFixer/Features/Issues/WingetUpgrade.cs index 3ca547d..578f6ac 100644 --- a/CFixer/Features/Issues/WingetUpgrade.cs +++ b/CFixer/Features/Issues/WingetUpgrade.cs @@ -1,7 +1,8 @@ -using System; +using CrapFixer; +using System; using System.Diagnostics; +using System.Drawing; using System.Threading.Tasks; -using CrapFixer; namespace Settings.System { @@ -29,9 +30,9 @@ namespace Settings.System try { - string output = await ExecuteCommand("winget upgrade --include-unknown"); + string output = await ExecuteCommand("winget upgrade --include-unknown" ); - Logger.Log("Winget upgrade check:\n" + output, LogLevel.Info); + Logger.Log("Winget upgrade check:\n" + output, LogLevel.Info, new Font("Cascadia Mono", 8.25f)); return output.ToLower().Contains("available"); } @@ -122,4 +123,4 @@ namespace Settings.System return tcs.Task; } } -} +} \ No newline at end of file diff --git a/CFixer/Features/UI/BingSearch.cs b/CFixer/Features/UI/BingSearch.cs new file mode 100644 index 0000000..568a251 --- /dev/null +++ b/CFixer/Features/UI/BingSearch.cs @@ -0,0 +1,62 @@ +using Microsoft.Win32; +using System; +using System.Threading.Tasks; +using CrapFixer; + +namespace Settings.UI +{ + internal class DisableBingSearch : FeatureBase + { + private const string keyName = @"HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Search"; + private const string valueName = "BingSearchEnabled"; + private const int recommendedValue = 0; + + public override string GetFeatureDetails() + { + return $"{keyName} | Value: {valueName} | Recommended Value: {recommendedValue}"; + } + + public override string ID() + { + return "Disable Bing Search"; + } + + public override string Info() + { + return "This feature disables Bing integration in Windows Search."; + } + + public override Task CheckFeature() + { + return Task.FromResult(Utils.IntEquals(keyName, valueName, recommendedValue)); + } + + public override Task DoFeature() + { + try + { + Registry.SetValue(keyName, valueName, recommendedValue, RegistryValueKind.DWord); + return Task.FromResult(true); + } + catch (Exception ex) + { + Logger.Log("Error in DisableBingSearch: " + ex.Message, LogLevel.Error); + return Task.FromResult(false); + } + } + + public override bool UndoFeature() + { + try + { + Registry.SetValue(keyName, valueName, 1, RegistryValueKind.DWord); + return true; + } + catch (Exception ex) + { + Logger.Log("Error undoing DisableBingSearch: " + ex.Message, LogLevel.Error); + return false; + } + } + } +} diff --git a/CFixer/Helpers/Logger.cs b/CFixer/Helpers/Logger.cs index 7c983fc..4ce2dc2 100644 --- a/CFixer/Helpers/Logger.cs +++ b/CFixer/Helpers/Logger.cs @@ -7,25 +7,37 @@ using System.Windows.Forms; /// public static class Logger { + /// + /// The RichTextBox control to which log messages are written. + /// public static RichTextBox OutputBox; - private static readonly Font DefaultFont = new Font("Consolas", 8.25f, FontStyle.Regular); + private static readonly Font DefaultFont = new Font("Tahoma", 8.25f, FontStyle.Regular); - public static void Log(string message, LogLevel level = LogLevel.Info) + /// + /// Writes a message with an optional log level and custom font. + /// + /// The message to display. + /// The log level (e.g., Info, Warning, Error). + /// An optional font for the message. + public static void Log(string message, LogLevel level = LogLevel.Info, Font customFont = null) { if (OutputBox == null) return; if (OutputBox.InvokeRequired) { - OutputBox.Invoke(new Action(() => LogInternal(message, level))); + OutputBox.Invoke(new Action(() => LogInternal(message, level, customFont))); } else { - LogInternal(message, level); + LogInternal(message, level, customFont); } } - private static void LogInternal(string message, LogLevel level) + /// + /// Internal method to append text to the RichTextBox with formatting. + /// + private static void LogInternal(string message, LogLevel level, Font customFont = null) { // string prefix = $"[{DateTime.Now:HH:mm:ss}] [{level}] "; string fullMessage = message + Environment.NewLine; @@ -51,10 +63,11 @@ public static class Logger break; } - // Append text with color + // Append formatted message OutputBox.SelectionStart = OutputBox.TextLength; OutputBox.SelectionLength = 0; OutputBox.SelectionColor = color; + OutputBox.SelectionFont = customFont ?? DefaultFont; // use custom font if provided, otherwise use default OutputBox.AppendText(fullMessage); // Reset selection to default @@ -62,8 +75,29 @@ public static class Logger OutputBox.SelectionFont = DefaultFont; // reset font OutputBox.ScrollToCaret(); // scroll to the end } + + /// + /// Clears the log output. + /// + public static void Clear() + { + if (OutputBox == null || OutputBox.IsDisposed) + return; + + if (OutputBox.InvokeRequired) + { + OutputBox.Invoke(new Action(Clear)); + return; + } + + OutputBox.Clear(); + OutputBox.SelectionColor = Color.Black; + } } +/// +/// Log level types used to define message severity. +/// public enum LogLevel { Info, diff --git a/CFixer/Helpers/OSHelper.cs b/CFixer/Helpers/OSHelper.cs index 83d705c..0453df6 100644 --- a/CFixer/Helpers/OSHelper.cs +++ b/CFixer/Helpers/OSHelper.cs @@ -1,9 +1,9 @@ -using System.Collections.ObjectModel; +using System; +using System.Collections.ObjectModel; using System.Management.Automation; using System.Threading.Tasks; using Microsoft.Win32; -// This file is part of CFixer. namespace OSHelper { internal class OSHelper @@ -14,37 +14,63 @@ namespace OSHelper { try { - using (PowerShell powerShellInstance = PowerShell.Create()) + using (PowerShell ps = PowerShell.Create()) { - powerShellInstance.AddScript("Get-CimInstance -ClassName Win32_OperatingSystem"); - Collection psOutput = powerShellInstance.Invoke(); + ps.AddScript("Get-CimInstance -ClassName Win32_OperatingSystem"); + var results = ps.Invoke(); - foreach (PSObject outputItem in psOutput) + foreach (var result in results) { - if (outputItem != null) - { - string productName = outputItem.Properties["Caption"]?.Value?.ToString(); - if (!string.IsNullOrEmpty(productName)) - { - string osVersion = productName.Contains("Windows 10") ? "Windows 10" : "Windows 11"; + if (result == null) continue; - using (RegistryKey displayVersionKey = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows NT\CurrentVersion")) + string caption = result.Properties["Caption"]?.Value?.ToString(); + string version = result.Properties["Version"]?.Value?.ToString(); + string build = result.Properties["BuildNumber"]?.Value?.ToString(); + + string displayVersion = Registry.GetValue( + @"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion", + "DisplayVersion", "")?.ToString(); + + // UBR = Update Build Revision + string ubr = Registry.GetValue( + @"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion", + "UBR", 0)?.ToString(); + + bool isInsider = false; + string ring = null; + + using (var insiderKey = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\UpdateOrchestrator")) + { + if (insiderKey != null) + { + object enabled = insiderKey.GetValue("EnableInsiderBuilds"); + if (enabled != null && Convert.ToInt32(enabled) == 1) { - if (displayVersionKey != null) - { - string displayVersion = displayVersionKey.GetValue("DisplayVersion")?.ToString(); - return $"{osVersion} ({displayVersion})"; - } + isInsider = true; + ring = insiderKey.GetValue("Ring")?.ToString(); } - return osVersion; } } + + string osName = caption?.Contains("Windows 11") == true ? "Windows 11" : + caption?.Contains("Windows 10") == true ? "Windows 10" : + caption ?? "Unknown OS"; + + string fullBuild = !string.IsNullOrEmpty(build) && !string.IsNullOrEmpty(ubr) + ? $"{build}.{ubr}" + : build ?? "unknown"; + + string insiderInfo = isInsider ? $" (Insider: {ring})" : ""; + + return $"{osName} {displayVersion}{insiderInfo} (Build {fullBuild})"; } } } - catch + catch (Exception ex) { + return $"OS info unavailable: {ex.Message}"; } + return "OS not supported"; }); } diff --git a/CFixer/Helpers/Utils.cs b/CFixer/Helpers/Utils.cs index ea4b320..05510a2 100644 --- a/CFixer/Helpers/Utils.cs +++ b/CFixer/Helpers/Utils.cs @@ -3,55 +3,65 @@ using System; using System.Diagnostics; using System.Windows.Forms; -// This file is part of CFixer. namespace CrapFixer { - internal class Utils + internal static class Utils { + private const string GitHubUrl = "https://github.com/builtbybel/CrapFixer"; + /// - /// Checks if a registry value is equal to a specified integer. + /// Checks if a registry value equals a specified integer. /// - /// - /// - /// - /// public static bool IntEquals(string keyName, string valueName, int expectedValue) { try { - var value = Registry.GetValue(keyName, valueName, null); - return (value != null && (int)value == expectedValue); + object value = Registry.GetValue(keyName, valueName, null); + return value is int intValue && intValue == expectedValue; } catch (Exception ex) - { - MessageBox.Show(keyName, ex.Message, MessageBoxButtons.OK); + Logger.Log($"Registry check failed for {keyName}\\{valueName}: {ex.Message}", LogLevel.Error); return false; } } /// - /// Checks if a registry value is equal to a specified string. + /// Checks if a registry value equals a specified string. /// - /// - /// - /// - /// public static bool StringEquals(string keyName, string valueName, string expectedValue) { try { - var value = Registry.GetValue(keyName, valueName, null); - - return (value != null && (string)value == expectedValue); + object value = Registry.GetValue(keyName, valueName, null); + return value is string strValue && strValue == expectedValue; } catch (Exception ex) { - MessageBox.Show(keyName, ex.Message, MessageBoxButtons.OK); + Logger.Log($"Registry check failed for {keyName}\\{valueName}: {ex.Message}", LogLevel.Error); return false; } } + /// + /// Opens the GitHub project page in the default browser. + /// + public static void OpenGitHubPage(object sender, EventArgs e) + { + try + { + Process.Start(new ProcessStartInfo + { + FileName = GitHubUrl, + UseShellExecute = true + }); + } + catch (Exception ex) + { + Logger.Log($"Failed to open GitHub page: {ex.Message}", LogLevel.Error); + } + } + /// /// Restarts Windows Explorer to apply UI changes. /// @@ -61,22 +71,19 @@ namespace CrapFixer { Logger.Log("Restarting Windows Explorer to apply UI changes...", LogLevel.Info); - // Kill all explorer instances foreach (var process in Process.GetProcessesByName("explorer")) { process.Kill(); process.WaitForExit(); } - // Restart explorer Process.Start("explorer.exe"); - - Logger.Log("Explorer restarted successfully. Changes should now be visible.", LogLevel.Info); + Logger.Log("Explorer restarted successfully.", LogLevel.Info); } catch (Exception ex) { - Logger.Log("Failed to restart Explorer: " + ex.Message, LogLevel.Error); + Logger.Log($"Failed to restart Explorer: {ex.Message}", LogLevel.Error); } } } -} \ No newline at end of file +} diff --git a/CFixer/IniStateManager.cs b/CFixer/IniStateManager.cs index 7daf5e8..a53f864 100644 --- a/CFixer/IniStateManager.cs +++ b/CFixer/IniStateManager.cs @@ -27,7 +27,9 @@ public static class IniStateManager lines.Add("[APP]"); lines.Add($"Width={form.Width}"); lines.Add($"Height={form.Height}"); - lines.Add(""); // empty line for spacing + lines.Add($"Top={form.Top}"); + lines.Add($"Left={form.Left}"); + // lines.Add(""); // empty line for spacing // Append new FEATURES section (TreeView nodes states) lines.Add("[FEATURES]"); @@ -62,8 +64,75 @@ public static class IniStateManager } } - // Loads global states (App size, TreeView nodes states) from the INI file - public static void Load(TreeView tree, Form form) + // Loads the global states (App size, TreeView nodes states) if enabled + public static void LoadFeaturesIfEnabled(TreeView tree) + { + if (IsViewSettingEnabled("SETTINGS", "checkSaveToINI")) + LoadFeatureStates(tree); + } + + public static void ApplyWindowState(Form form) + { + if (File.Exists(IniPath)) + { + // Load window size and position from INI file + LoadWindowState(form); + form.StartPosition = FormStartPosition.Manual; + } + else + { + // If INI file doesn't exist, set default position + form.StartPosition = FormStartPosition.CenterScreen; + } + } + + // Loads only the window size and position from the INI file + public static void LoadWindowState(Form form) + { + if (!File.Exists(IniPath)) return; + + var lines = File.ReadAllLines(IniPath); + var section = ""; + + foreach (var line in lines) + { + var trimmed = line.Trim(); + if (string.IsNullOrWhiteSpace(trimmed)) continue; + + // Check if line is a section header + if (trimmed.StartsWith("[") && trimmed.EndsWith("]")) + { + section = trimmed; + continue; + } + + if (section == "[APP]") + { + // Split line into key and value + var parts = trimmed.Split(new[] { '=' }, 2); + if (parts.Length != 2) continue; + + var key = parts[0].Trim(); + var value = parts[1].Trim(); + + // Parse and apply window size and position + if (int.TryParse(value, out int size)) + { + if (key.Equals("Width", StringComparison.OrdinalIgnoreCase)) + form.Width = size; + else if (key.Equals("Height", StringComparison.OrdinalIgnoreCase)) + form.Height = size; + else if (key.Equals("Top", StringComparison.OrdinalIgnoreCase)) + form.Top = size; + else if (key.Equals("Left", StringComparison.OrdinalIgnoreCase)) + form.Left = size; + } + } + } + } + + // Loads only the feature states (TreeView node checked states) from the INI file + public static void LoadFeatureStates(TreeView tree) { if (!File.Exists(IniPath)) return; @@ -76,37 +145,28 @@ public static class IniStateManager var trimmed = line.Trim(); if (string.IsNullOrWhiteSpace(trimmed)) continue; - // Check if line is a section + // Check if line is a section header if (trimmed.StartsWith("[") && trimmed.EndsWith("]")) { section = trimmed; continue; } - // Split line into key and value - var parts = trimmed.Split(new[] { '=' }, 2); - if (parts.Length != 2) continue; - - var key = parts[0].Trim(); - var value = parts[1].Trim(); - if (section == "[FEATURES]") { + // Split line into key and value + var parts = trimmed.Split(new[] { '=' }, 2); + if (parts.Length != 2) continue; + + var key = parts[0].Trim(); + var value = parts[1].Trim(); + + // Save checked state for each feature node states[key] = value.ToLower() == "true"; } - else if (section == "[APP]") - { - if (int.TryParse(value, out int size)) - { - if (key.Equals("Width", StringComparison.OrdinalIgnoreCase)) - form.Width = size; - else if (key.Equals("Height", StringComparison.OrdinalIgnoreCase)) - form.Height = size; - } - } } - // Apply saved feature states to tree nodes + // Apply saved feature states to the TreeView nodes recursively ApplyStates(tree.Nodes, states); } diff --git a/CFixer/MainForm.Designer.cs b/CFixer/MainForm.Designer.cs index 31702f1..b228ef0 100644 --- a/CFixer/MainForm.Designer.cs +++ b/CFixer/MainForm.Designer.cs @@ -29,7 +29,6 @@ private void InitializeComponent() { this.components = new System.ComponentModel.Container(); - System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(MainForm)); this.panelContainer = new System.Windows.Forms.Panel(); this.panelContent = new System.Windows.Forms.Panel(); this.btnAnalyze = new System.Windows.Forms.Button(); @@ -38,10 +37,9 @@ this.treeFeatures = new System.Windows.Forms.TreeView(); this.contextMenuStrip = new System.Windows.Forms.ContextMenuStrip(this.components); this.analyzeMarkedFeatureToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.previewMarkedFeatureToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.fixMarkedFeatureToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.seperatorToolStripMenuItem = new System.Windows.Forms.ToolStripSeparator(); this.restoreMarkedFeatureToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.seperatorToolStripMenuItem = new System.Windows.Forms.ToolStripSeparator(); this.helpMarkedFeatureToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.Apps = new System.Windows.Forms.TabPage(); this.checkedListBoxApps = new System.Windows.Forms.CheckedListBox(); @@ -50,15 +48,17 @@ this.rtbLogger = new System.Windows.Forms.RichTextBox(); this.btnFix = new System.Windows.Forms.Button(); this.panelHeader = new System.Windows.Forms.Panel(); + this.btnGitHub = new System.Windows.Forms.Button(); this.lblOSInfo = new System.Windows.Forms.Label(); this.lblVersionInfo = new System.Windows.Forms.Label(); this.lblHeader = new System.Windows.Forms.Label(); this.pictureHeader = new System.Windows.Forms.PictureBox(); this.btnRestore = new System.Windows.Forms.Button(); this.linkUpdateCheck = new System.Windows.Forms.LinkLabel(); - this.btnSettings = new System.Windows.Forms.Button(); - this.btnHome = new System.Windows.Forms.Button(); + this.btnTools = new System.Windows.Forms.Button(); + this.btnFixer = new System.Windows.Forms.Button(); this.linkSelection = new System.Windows.Forms.LinkLabel(); + this.toolTip = new System.Windows.Forms.ToolTip(this.components); this.panelContainer.SuspendLayout(); this.panelContent.SuspendLayout(); this.tabControl.SuspendLayout(); @@ -77,15 +77,15 @@ | System.Windows.Forms.AnchorStyles.Right))); this.panelContainer.BackColor = System.Drawing.SystemColors.Control; this.panelContainer.Controls.Add(this.panelContent); - this.panelContainer.Location = new System.Drawing.Point(99, 62); + this.panelContainer.Location = new System.Drawing.Point(90, 69); this.panelContainer.Name = "panelContainer"; - this.panelContainer.Size = new System.Drawing.Size(611, 382); + this.panelContainer.Size = new System.Drawing.Size(613, 375); this.panelContainer.TabIndex = 198; // // panelContent // this.panelContent.AutoScroll = true; - this.panelContent.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(252)))), ((int)(((byte)(252)))), ((int)(((byte)(252))))); + this.panelContent.BackColor = System.Drawing.Color.White; this.panelContent.Controls.Add(this.btnAnalyze); this.panelContent.Controls.Add(this.tabControl); this.panelContent.Controls.Add(this.groupBox); @@ -93,7 +93,7 @@ this.panelContent.Dock = System.Windows.Forms.DockStyle.Fill; this.panelContent.Location = new System.Drawing.Point(0, 0); this.panelContent.Name = "panelContent"; - this.panelContent.Size = new System.Drawing.Size(611, 382); + this.panelContent.Size = new System.Drawing.Size(613, 375); this.panelContent.TabIndex = 205; // // btnAnalyze @@ -102,7 +102,7 @@ this.btnAnalyze.AutoEllipsis = true; this.btnAnalyze.FlatStyle = System.Windows.Forms.FlatStyle.System; this.btnAnalyze.Font = new System.Drawing.Font("Tahoma", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.btnAnalyze.Location = new System.Drawing.Point(280, 346); + this.btnAnalyze.Location = new System.Drawing.Point(268, 339); this.btnAnalyze.Name = "btnAnalyze"; this.btnAnalyze.Size = new System.Drawing.Size(121, 29); this.btnAnalyze.TabIndex = 1; @@ -116,45 +116,42 @@ | System.Windows.Forms.AnchorStyles.Left))); this.tabControl.Controls.Add(this.Windows); this.tabControl.Controls.Add(this.Apps); - this.tabControl.HotTrack = true; - this.tabControl.Location = new System.Drawing.Point(0, 0); + this.tabControl.ItemSize = new System.Drawing.Size(68, 21); + this.tabControl.Location = new System.Drawing.Point(7, 5); this.tabControl.Name = "tabControl"; - this.tabControl.Padding = new System.Drawing.Point(20, 8); this.tabControl.SelectedIndex = 0; this.tabControl.ShowToolTips = true; - this.tabControl.Size = new System.Drawing.Size(270, 376); - this.tabControl.SizeMode = System.Windows.Forms.TabSizeMode.Fixed; + this.tabControl.Size = new System.Drawing.Size(255, 363); + this.tabControl.SizeMode = System.Windows.Forms.TabSizeMode.FillToRight; this.tabControl.TabIndex = 199; // // Windows // + this.Windows.AutoScroll = true; this.Windows.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(240)))), ((int)(((byte)(240)))), ((int)(((byte)(240))))); this.Windows.Controls.Add(this.treeFeatures); - this.Windows.Location = new System.Drawing.Point(4, 32); + this.Windows.Location = new System.Drawing.Point(4, 25); this.Windows.Name = "Windows"; - this.Windows.Padding = new System.Windows.Forms.Padding(3); - this.Windows.Size = new System.Drawing.Size(262, 340); + this.Windows.Size = new System.Drawing.Size(247, 334); this.Windows.TabIndex = 0; this.Windows.Text = "Windows"; // // treeFeatures // - this.treeFeatures.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(242)))), ((int)(((byte)(242)))), ((int)(((byte)(242))))); + this.treeFeatures.BackColor = System.Drawing.Color.White; this.treeFeatures.BorderStyle = System.Windows.Forms.BorderStyle.None; this.treeFeatures.CheckBoxes = true; this.treeFeatures.ContextMenuStrip = this.contextMenuStrip; this.treeFeatures.Dock = System.Windows.Forms.DockStyle.Fill; this.treeFeatures.Font = new System.Drawing.Font("Tahoma", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.treeFeatures.Indent = 20; - this.treeFeatures.ItemHeight = 23; - this.treeFeatures.Location = new System.Drawing.Point(3, 3); + this.treeFeatures.Location = new System.Drawing.Point(0, 0); this.treeFeatures.Name = "treeFeatures"; this.treeFeatures.ShowLines = false; this.treeFeatures.ShowNodeToolTips = true; this.treeFeatures.ShowPlusMinus = false; this.treeFeatures.ShowRootLines = false; - this.treeFeatures.Size = new System.Drawing.Size(256, 334); - this.treeFeatures.TabIndex = 196; + this.treeFeatures.Size = new System.Drawing.Size(247, 334); + this.treeFeatures.TabIndex = 0; this.treeFeatures.AfterCheck += new System.Windows.Forms.TreeViewEventHandler(this.treeFeatures_AfterCheck); this.treeFeatures.MouseDown += new System.Windows.Forms.MouseEventHandler(this.treeFeatures_MouseDown); // @@ -162,14 +159,13 @@ // this.contextMenuStrip.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { this.analyzeMarkedFeatureToolStripMenuItem, - this.previewMarkedFeatureToolStripMenuItem, this.fixMarkedFeatureToolStripMenuItem, - this.seperatorToolStripMenuItem, this.restoreMarkedFeatureToolStripMenuItem, + this.seperatorToolStripMenuItem, this.helpMarkedFeatureToolStripMenuItem}); this.contextMenuStrip.Name = "contextManualMenu"; this.contextMenuStrip.RenderMode = System.Windows.Forms.ToolStripRenderMode.System; - this.contextMenuStrip.Size = new System.Drawing.Size(119, 120); + this.contextMenuStrip.Size = new System.Drawing.Size(119, 98); // // analyzeMarkedFeatureToolStripMenuItem // @@ -178,13 +174,6 @@ this.analyzeMarkedFeatureToolStripMenuItem.Text = "Analyze"; this.analyzeMarkedFeatureToolStripMenuItem.Click += new System.EventHandler(this.analyzeMarkedFeatureToolStripMenuItem_Click); // - // previewMarkedFeatureToolStripMenuItem - // - this.previewMarkedFeatureToolStripMenuItem.Name = "previewMarkedFeatureToolStripMenuItem"; - this.previewMarkedFeatureToolStripMenuItem.Size = new System.Drawing.Size(118, 22); - this.previewMarkedFeatureToolStripMenuItem.Text = "Preview"; - this.previewMarkedFeatureToolStripMenuItem.Click += new System.EventHandler(this.previewMarkedFeatureToolStripMenuItem_Click); - // // fixMarkedFeatureToolStripMenuItem // this.fixMarkedFeatureToolStripMenuItem.Name = "fixMarkedFeatureToolStripMenuItem"; @@ -192,11 +181,6 @@ this.fixMarkedFeatureToolStripMenuItem.Text = "Fix"; this.fixMarkedFeatureToolStripMenuItem.Click += new System.EventHandler(this.fixMarkedFeatureToolStripMenuItem_Click); // - // seperatorToolStripMenuItem - // - this.seperatorToolStripMenuItem.Name = "seperatorToolStripMenuItem"; - this.seperatorToolStripMenuItem.Size = new System.Drawing.Size(115, 6); - // // restoreMarkedFeatureToolStripMenuItem // this.restoreMarkedFeatureToolStripMenuItem.Name = "restoreMarkedFeatureToolStripMenuItem"; @@ -204,6 +188,11 @@ this.restoreMarkedFeatureToolStripMenuItem.Text = "Restore"; this.restoreMarkedFeatureToolStripMenuItem.Click += new System.EventHandler(this.restoreMarkedFeatureToolStripMenuItem_Click); // + // seperatorToolStripMenuItem + // + this.seperatorToolStripMenuItem.Name = "seperatorToolStripMenuItem"; + this.seperatorToolStripMenuItem.Size = new System.Drawing.Size(115, 6); + // // helpMarkedFeatureToolStripMenuItem // this.helpMarkedFeatureToolStripMenuItem.Name = "helpMarkedFeatureToolStripMenuItem"; @@ -214,30 +203,28 @@ // // Apps // - this.Apps.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(242)))), ((int)(((byte)(242)))), ((int)(((byte)(242))))); + this.Apps.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(240)))), ((int)(((byte)(240)))), ((int)(((byte)(240))))); this.Apps.Controls.Add(this.checkedListBoxApps); - this.Apps.Location = new System.Drawing.Point(4, 32); + this.Apps.Location = new System.Drawing.Point(4, 25); this.Apps.Name = "Apps"; - this.Apps.Padding = new System.Windows.Forms.Padding(3); - this.Apps.Size = new System.Drawing.Size(262, 340); + this.Apps.Size = new System.Drawing.Size(247, 334); this.Apps.TabIndex = 1; - this.Apps.Text = "Apps"; + this.Apps.Text = "Applications"; // // checkedListBoxApps // - this.checkedListBoxApps.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(242)))), ((int)(((byte)(242)))), ((int)(((byte)(242))))); + this.checkedListBoxApps.BackColor = System.Drawing.Color.White; this.checkedListBoxApps.BorderStyle = System.Windows.Forms.BorderStyle.None; this.checkedListBoxApps.Dock = System.Windows.Forms.DockStyle.Fill; this.checkedListBoxApps.Font = new System.Drawing.Font("Tahoma", 8F); this.checkedListBoxApps.FormattingEnabled = true; this.checkedListBoxApps.Items.AddRange(new object[] { "No analysis yet"}); - this.checkedListBoxApps.Location = new System.Drawing.Point(3, 3); + this.checkedListBoxApps.Location = new System.Drawing.Point(0, 0); this.checkedListBoxApps.Name = "checkedListBoxApps"; - this.checkedListBoxApps.Size = new System.Drawing.Size(256, 334); + this.checkedListBoxApps.Size = new System.Drawing.Size(247, 334); this.checkedListBoxApps.Sorted = true; this.checkedListBoxApps.TabIndex = 336; - this.checkedListBoxApps.ThreeDCheckBoxes = true; // // groupBox // @@ -247,9 +234,10 @@ this.groupBox.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(252)))), ((int)(((byte)(252)))), ((int)(((byte)(252))))); this.groupBox.Controls.Add(this.comboLogActions); this.groupBox.Controls.Add(this.rtbLogger); - this.groupBox.Location = new System.Drawing.Point(280, 21); + this.groupBox.ForeColor = System.Drawing.SystemColors.ControlDarkDark; + this.groupBox.Location = new System.Drawing.Point(268, 21); this.groupBox.Name = "groupBox"; - this.groupBox.Size = new System.Drawing.Size(320, 317); + this.groupBox.Size = new System.Drawing.Size(334, 310); this.groupBox.TabIndex = 200; this.groupBox.TabStop = false; // @@ -257,13 +245,14 @@ // this.comboLogActions.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); + this.comboLogActions.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(240)))), ((int)(((byte)(240)))), ((int)(((byte)(240))))); this.comboLogActions.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; this.comboLogActions.FlatStyle = System.Windows.Forms.FlatStyle.Flat; this.comboLogActions.Font = new System.Drawing.Font("Tahoma", 8.25F); this.comboLogActions.FormattingEnabled = true; - this.comboLogActions.Location = new System.Drawing.Point(7, 292); + this.comboLogActions.Location = new System.Drawing.Point(7, 285); this.comboLogActions.Name = "comboLogActions"; - this.comboLogActions.Size = new System.Drawing.Size(305, 21); + this.comboLogActions.Size = new System.Drawing.Size(319, 21); this.comboLogActions.TabIndex = 210; this.comboLogActions.Visible = false; // @@ -274,13 +263,13 @@ | System.Windows.Forms.AnchorStyles.Right))); this.rtbLogger.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(252)))), ((int)(((byte)(252)))), ((int)(((byte)(252))))); this.rtbLogger.BorderStyle = System.Windows.Forms.BorderStyle.None; - this.rtbLogger.Font = new System.Drawing.Font("Consolas", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.rtbLogger.Location = new System.Drawing.Point(7, 12); + this.rtbLogger.Font = new System.Drawing.Font("Tahoma", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.rtbLogger.Location = new System.Drawing.Point(7, 19); this.rtbLogger.Name = "rtbLogger"; - this.rtbLogger.ScrollBars = System.Windows.Forms.RichTextBoxScrollBars.Vertical; - this.rtbLogger.Size = new System.Drawing.Size(305, 273); + this.rtbLogger.Size = new System.Drawing.Size(319, 263); this.rtbLogger.TabIndex = 195; this.rtbLogger.Text = ""; + this.rtbLogger.WordWrap = false; // // btnFix // @@ -288,7 +277,7 @@ this.btnFix.AutoEllipsis = true; this.btnFix.FlatStyle = System.Windows.Forms.FlatStyle.System; this.btnFix.Font = new System.Drawing.Font("Tahoma", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.btnFix.Location = new System.Drawing.Point(480, 346); + this.btnFix.Location = new System.Drawing.Point(482, 339); this.btnFix.Name = "btnFix"; this.btnFix.Size = new System.Drawing.Size(121, 29); this.btnFix.TabIndex = 2; @@ -299,6 +288,7 @@ // panelHeader // this.panelHeader.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(77)))), ((int)(((byte)(77)))), ((int)(((byte)(77))))); + this.panelHeader.Controls.Add(this.btnGitHub); this.panelHeader.Controls.Add(this.lblOSInfo); this.panelHeader.Controls.Add(this.lblVersionInfo); this.panelHeader.Controls.Add(this.lblHeader); @@ -310,21 +300,42 @@ this.panelHeader.TabIndex = 204; this.panelHeader.Paint += new System.Windows.Forms.PaintEventHandler(this.panelHeader_Paint); // + // btnGitHub + // + this.btnGitHub.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.btnGitHub.AutoSize = true; + this.btnGitHub.Cursor = System.Windows.Forms.Cursors.Hand; + this.btnGitHub.FlatAppearance.BorderColor = System.Drawing.Color.FromArgb(((int)(((byte)(120)))), ((int)(((byte)(170)))), ((int)(((byte)(210))))); + this.btnGitHub.FlatAppearance.BorderSize = 0; + this.btnGitHub.FlatStyle = System.Windows.Forms.FlatStyle.Flat; + this.btnGitHub.Font = new System.Drawing.Font("Tahoma", 8F); + this.btnGitHub.ForeColor = System.Drawing.Color.Gainsboro; + this.btnGitHub.Location = new System.Drawing.Point(657, 7); + this.btnGitHub.Name = "btnGitHub"; + this.btnGitHub.Size = new System.Drawing.Size(40, 40); + this.btnGitHub.TabIndex = 201; + this.btnGitHub.TabStop = false; + this.toolTip.SetToolTip(this.btnGitHub, "Love CrapFixer? It’s open source — but your support keeps it alive! 💖"); + this.btnGitHub.UseVisualStyleBackColor = true; + this.btnGitHub.Click += new System.EventHandler(this.btnGitHub_Click); + // // lblOSInfo // this.lblOSInfo.AutoSize = true; - this.lblOSInfo.Font = new System.Drawing.Font("Microsoft Sans Serif", 8F); + this.lblOSInfo.BackColor = System.Drawing.Color.Transparent; + this.lblOSInfo.Font = new System.Drawing.Font("Tahoma", 7.6F); this.lblOSInfo.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(230)))), ((int)(((byte)(230)))), ((int)(((byte)(230))))); this.lblOSInfo.Location = new System.Drawing.Point(90, 37); this.lblOSInfo.Name = "lblOSInfo"; - this.lblOSInfo.Size = new System.Drawing.Size(116, 13); + this.lblOSInfo.Size = new System.Drawing.Size(120, 13); this.lblOSInfo.TabIndex = 200; this.lblOSInfo.Text = "Checking your system.."; // // lblVersionInfo // this.lblVersionInfo.AutoSize = true; - this.lblVersionInfo.Font = new System.Drawing.Font("Microsoft Sans Serif", 7.5F); + this.lblVersionInfo.BackColor = System.Drawing.Color.Transparent; + this.lblVersionInfo.Font = new System.Drawing.Font("Tahoma", 7.6F); this.lblVersionInfo.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(230)))), ((int)(((byte)(230)))), ((int)(((byte)(230))))); this.lblVersionInfo.Location = new System.Drawing.Point(198, 15); this.lblVersionInfo.Name = "lblVersionInfo"; @@ -335,6 +346,8 @@ // lblHeader // this.lblHeader.AutoEllipsis = true; + this.lblHeader.BackColor = System.Drawing.Color.Transparent; + this.lblHeader.Cursor = System.Windows.Forms.Cursors.Hand; this.lblHeader.Font = new System.Drawing.Font("Tahoma", 14F, System.Drawing.FontStyle.Bold); this.lblHeader.ForeColor = System.Drawing.Color.White; this.lblHeader.Location = new System.Drawing.Point(89, 9); @@ -342,29 +355,35 @@ this.lblHeader.Size = new System.Drawing.Size(117, 25); this.lblHeader.TabIndex = 1; this.lblHeader.Text = "CrapFixer"; + this.toolTip.SetToolTip(this.lblHeader, "Click here to visit the CrapFixer website at github.com/builtbybel/crapfixer"); this.lblHeader.UseCompatibleTextRendering = true; // // pictureHeader // - this.pictureHeader.Image = ((System.Drawing.Image)(resources.GetObject("pictureHeader.Image"))); + this.pictureHeader.BackColor = System.Drawing.Color.Transparent; + this.pictureHeader.Cursor = System.Windows.Forms.Cursors.Hand; + this.pictureHeader.Image = global::CFixer.Properties.Resources.AppIcon; + this.pictureHeader.InitialImage = null; this.pictureHeader.Location = new System.Drawing.Point(30, 9); this.pictureHeader.Name = "pictureHeader"; this.pictureHeader.Size = new System.Drawing.Size(44, 41); this.pictureHeader.SizeMode = System.Windows.Forms.PictureBoxSizeMode.Zoom; this.pictureHeader.TabIndex = 0; this.pictureHeader.TabStop = false; + this.toolTip.SetToolTip(this.pictureHeader, "Click here to visit the CrapFixer website at github.com/builtbybel/crapfixer"); // // btnRestore // this.btnRestore.AutoEllipsis = true; - this.btnRestore.Cursor = System.Windows.Forms.Cursors.Hand; - this.btnRestore.FlatAppearance.BorderColor = System.Drawing.Color.FromArgb(((int)(((byte)(114)))), ((int)(((byte)(114)))), ((int)(((byte)(114))))); + this.btnRestore.Cursor = System.Windows.Forms.Cursors.Default; + this.btnRestore.FlatAppearance.BorderColor = System.Drawing.Color.FromArgb(((int)(((byte)(120)))), ((int)(((byte)(170)))), ((int)(((byte)(210))))); + this.btnRestore.FlatAppearance.BorderSize = 0; this.btnRestore.FlatStyle = System.Windows.Forms.FlatStyle.Flat; - this.btnRestore.Font = new System.Drawing.Font("Tahoma", 8.25F); - this.btnRestore.ForeColor = System.Drawing.Color.White; - this.btnRestore.Location = new System.Drawing.Point(0, 129); + this.btnRestore.Font = new System.Drawing.Font("Tahoma", 8F); + this.btnRestore.ForeColor = System.Drawing.Color.Gainsboro; + this.btnRestore.Location = new System.Drawing.Point(10, 142); this.btnRestore.Name = "btnRestore"; - this.btnRestore.Size = new System.Drawing.Size(99, 69); + this.btnRestore.Size = new System.Drawing.Size(80, 60); this.btnRestore.TabIndex = 198; this.btnRestore.TabStop = false; this.btnRestore.Text = "&Restore"; @@ -379,46 +398,48 @@ this.linkUpdateCheck.AutoSize = true; this.linkUpdateCheck.Font = new System.Drawing.Font("Tahoma", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.linkUpdateCheck.LinkColor = System.Drawing.Color.White; - this.linkUpdateCheck.Location = new System.Drawing.Point(595, 447); + this.linkUpdateCheck.Location = new System.Drawing.Point(590, 447); this.linkUpdateCheck.Name = "linkUpdateCheck"; - this.linkUpdateCheck.Size = new System.Drawing.Size(95, 13); + this.linkUpdateCheck.Size = new System.Drawing.Size(107, 13); this.linkUpdateCheck.TabIndex = 203; this.linkUpdateCheck.TabStop = true; - this.linkUpdateCheck.Text = "Check for updates"; - this.linkUpdateCheck.TextAlign = System.Drawing.ContentAlignment.TopCenter; + this.linkUpdateCheck.Text = "Check for updates..."; + this.linkUpdateCheck.TextAlign = System.Drawing.ContentAlignment.TopCenter; + this.linkUpdateCheck.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.linkUpdateCheck_LinkClicked); // - // btnSettings + // btnTools // - this.btnSettings.AutoEllipsis = true; - this.btnSettings.Cursor = System.Windows.Forms.Cursors.Hand; - this.btnSettings.FlatAppearance.BorderColor = System.Drawing.Color.FromArgb(((int)(((byte)(114)))), ((int)(((byte)(114)))), ((int)(((byte)(114))))); - this.btnSettings.FlatStyle = System.Windows.Forms.FlatStyle.Flat; - this.btnSettings.Font = new System.Drawing.Font("Tahoma", 8.25F); - this.btnSettings.ForeColor = System.Drawing.Color.White; - this.btnSettings.Location = new System.Drawing.Point(0, 197); - this.btnSettings.Name = "btnSettings"; - this.btnSettings.Size = new System.Drawing.Size(99, 69); - this.btnSettings.TabIndex = 205; - this.btnSettings.TabStop = false; - this.btnSettings.Text = "&Options"; - this.btnSettings.UseVisualStyleBackColor = true; + this.btnTools.AutoEllipsis = true; + this.btnTools.Cursor = System.Windows.Forms.Cursors.Default; + this.btnTools.FlatAppearance.BorderColor = System.Drawing.Color.FromArgb(((int)(((byte)(120)))), ((int)(((byte)(170)))), ((int)(((byte)(210))))); + this.btnTools.FlatAppearance.BorderSize = 0; + this.btnTools.FlatStyle = System.Windows.Forms.FlatStyle.Flat; + this.btnTools.Font = new System.Drawing.Font("Tahoma", 8F); + this.btnTools.ForeColor = System.Drawing.Color.Gainsboro; + this.btnTools.Location = new System.Drawing.Point(10, 210); + this.btnTools.Name = "btnTools"; + this.btnTools.Size = new System.Drawing.Size(80, 60); + this.btnTools.TabIndex = 205; + this.btnTools.TabStop = false; + this.btnTools.Text = "&Tools"; + this.btnTools.UseVisualStyleBackColor = true; // - // btnHome + // btnFixer // - this.btnHome.AutoEllipsis = true; - this.btnHome.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(76)))), ((int)(((byte)(145)))), ((int)(((byte)(235))))); - this.btnHome.Cursor = System.Windows.Forms.Cursors.Hand; - this.btnHome.FlatAppearance.BorderColor = System.Drawing.Color.FromArgb(((int)(((byte)(66)))), ((int)(((byte)(131)))), ((int)(((byte)(222))))); - this.btnHome.FlatStyle = System.Windows.Forms.FlatStyle.Flat; - this.btnHome.Font = new System.Drawing.Font("Tahoma", 8.25F); - this.btnHome.ForeColor = System.Drawing.Color.White; - this.btnHome.Location = new System.Drawing.Point(0, 61); - this.btnHome.Name = "btnHome"; - this.btnHome.Size = new System.Drawing.Size(99, 69); - this.btnHome.TabIndex = 206; - this.btnHome.TabStop = false; - this.btnHome.Text = "&Fixer"; - this.btnHome.UseVisualStyleBackColor = false; + this.btnFixer.AutoEllipsis = true; + this.btnFixer.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(180)))), ((int)(((byte)(150)))), ((int)(((byte)(200)))), ((int)(((byte)(240))))); + this.btnFixer.Cursor = System.Windows.Forms.Cursors.Default; + this.btnFixer.FlatAppearance.BorderColor = System.Drawing.Color.FromArgb(((int)(((byte)(120)))), ((int)(((byte)(170)))), ((int)(((byte)(210))))); + this.btnFixer.FlatStyle = System.Windows.Forms.FlatStyle.Flat; + this.btnFixer.Font = new System.Drawing.Font("Tahoma", 8F); + this.btnFixer.ForeColor = System.Drawing.Color.Gainsboro; + this.btnFixer.Location = new System.Drawing.Point(10, 74); + this.btnFixer.Name = "btnFixer"; + this.btnFixer.Size = new System.Drawing.Size(80, 60); + this.btnFixer.TabIndex = 206; + this.btnFixer.TabStop = false; + this.btnFixer.Text = "&Fixer"; + this.btnFixer.UseVisualStyleBackColor = false; // // linkSelection // @@ -441,15 +462,17 @@ // this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi; - this.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(104)))), ((int)(((byte)(104)))), ((int)(((byte)(104))))); + this.AutoScroll = true; + this.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(103)))), ((int)(((byte)(103)))), ((int)(((byte)(103))))); this.ClientSize = new System.Drawing.Size(710, 466); this.Controls.Add(this.linkSelection); - this.Controls.Add(this.btnHome); - this.Controls.Add(this.btnSettings); + this.Controls.Add(this.btnFixer); + this.Controls.Add(this.btnTools); this.Controls.Add(this.panelHeader); this.Controls.Add(this.btnRestore); this.Controls.Add(this.linkUpdateCheck); this.Controls.Add(this.panelContainer); + this.MinimumSize = new System.Drawing.Size(642, 337); this.Name = "MainForm"; this.ShowIcon = false; this.SizeGripStyle = System.Windows.Forms.SizeGripStyle.Show; @@ -495,13 +518,14 @@ private System.Windows.Forms.Panel panelContent; private System.Windows.Forms.LinkLabel linkUpdateCheck; private System.Windows.Forms.ToolStripMenuItem helpMarkedFeatureToolStripMenuItem; - private System.Windows.Forms.TreeView treeFeatures; private System.Windows.Forms.Label lblOSInfo; - private System.Windows.Forms.Button btnSettings; - private System.Windows.Forms.Button btnHome; + private System.Windows.Forms.Button btnTools; + private System.Windows.Forms.Button btnFixer; private System.Windows.Forms.LinkLabel linkSelection; - private System.Windows.Forms.ToolStripMenuItem previewMarkedFeatureToolStripMenuItem; private System.Windows.Forms.ComboBox comboLogActions; + private System.Windows.Forms.TreeView treeFeatures; + private System.Windows.Forms.Button btnGitHub; + private System.Windows.Forms.ToolTip toolTip; } } diff --git a/CFixer/MainForm.cs b/CFixer/MainForm.cs index c1aa126..954c3f1 100644 --- a/CFixer/MainForm.cs +++ b/CFixer/MainForm.cs @@ -4,6 +4,7 @@ using CFixer.Views; using System; using System.Diagnostics; using System.Drawing; +using System.Drawing.Drawing2D; using System.Linq; using System.Threading.Tasks; using System.Windows.Forms; @@ -21,6 +22,7 @@ namespace CrapFixer public MainForm() { InitializeComponent(); + IniStateManager.ApplyWindowState(this); // Set up the main navigation manager and logger _navigationManager = new NavigationManager(panelContainer); @@ -28,66 +30,67 @@ namespace CrapFixer // Set up log actions controller _logActions = new LogActions(rtbLogger); - _logActionsController = new LogActionsController(comboLogActions, _logActions); + } - // Load features and plugins, and restore INI state if enabled + private async void MainForm_Shown(object sender, EventArgs e) + { + await InitializeUI(); // _ = InitializeUI(); + InitializeAppState(); + } + + private void InitializeAppState() + { + // Load features and plugins into the tree view FeatureNodeManager.LoadFeatures(treeFeatures); PluginManager.LoadPlugins(treeFeatures); - if (IniStateManager.IsViewSettingEnabled("SETTINGS", "checkSaveToINI")) - { - IniStateManager.Load(treeFeatures, this); - } + + // Load settings from INI file if enabled + IniStateManager.LoadFeaturesIfEnabled(treeFeatures); } - private void MainForm_Shown(object sender, EventArgs e) + private async Task InitializeUI() { - InitializeUI(); - } + // Initialize the navigation handler with buttons + _navigationHandler = new NavigationHandler(btnFixer, btnRestore, btnTools, btnGitHub); - private void InitializeUI() - { - // Set the default active button - _navigationHandler = new NavigationHandler(btnHome, btnSettings); - _navigationHandler.NavigationButtonClicked += button => - { - if (button == btnHome) - { - _navigationManager.GoToMain(); - } - else if (button == btnSettings) - { - _navigationManager.SwitchView(new OptionsView()); - } - }; + // Load navigation icons + await _navigationHandler.LoadNavigationIcons(); - // Set up link label for update check - linkUpdateCheck.LinkClicked += (_, __) => - Process.Start($"https://builtbybel.github.io/CrapFixer/update-check.html?version={Program.GetAppVersion()}"); + // Register navigation handler + _navigationHandler.NavigationButtonClicked += NavigationHandler_NavigationButtonClicked; - // Set up log actions controller + // Register click handlers for GitHub links + pictureHeader.Click += PictureHeader_Click; + lblHeader.Click += PictureHeader_Click; + + // Re-initialize log actions controller (optional if not changed) _logActionsController = new LogActionsController(comboLogActions, _logActions); - // Set up version labels - async Task SetVersionLabels() + // Set version and OS info + lblVersionInfo.Text = $"v{Program.GetAppVersion()} "; + lblOSInfo.Text = await OSHelper.OSHelper.GetWindowsVersion(); + } + + // Handles navigation button clicks and switches views accordingly + private void NavigationHandler_NavigationButtonClicked(Button button) + { + if (button == btnFixer) { - lblVersionInfo.Text = $"v{Program.GetAppVersion()} "; - lblOSInfo.Text = await OSHelper.OSHelper.GetWindowsVersion(); + _navigationManager.GoToMain(); + } + else if (button == btnTools) + { + _navigationManager.SwitchView(new OptionsView()); } - _ = SetVersionLabels(); } private async void btnAnalyze_Click(object sender, EventArgs e) { - rtbLogger.Clear(); - // Analyze features await FeatureNodeManager.AnalyzeAll(treeFeatures.Nodes); // Analyze plugins - foreach (TreeNode node in treeFeatures.Nodes) - { - PluginManager.AnalyzeAll(node); - } + await PluginManager.AnalyzeAllPlugins(treeFeatures.Nodes); // Analyze apps await AnalyzeApps(); @@ -99,40 +102,35 @@ namespace CrapFixer /// /// Analyzes the apps and logs the results. /// - /// private async Task AnalyzeApps() { checkedListBoxApps.Items.Clear(); - // Try loading external bloatware list from file - string[] predefined = _appManager.LoadExternalBloatwareList(); + // Try loading patterns from CFEnhancer.txt (located in Plugins folder) + var (bloatwarePatterns, whitelistPatterns, scanAll) = _appManager.LoadExternalBloatwarePatterns(); - if (predefined.Length == 0) + if (bloatwarePatterns.Length == 0 && !scanAll) { - // Fallback to internal resource if external file not found or empty - predefined = Resources.PredefinedApps? + // Fallback to internal resource if external file not found or empty and scanAll is not enabled + bloatwarePatterns = Resources.PredefinedApps? .Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries) - .Select(s => s.Trim()).ToArray() ?? Array.Empty(); + .Select(s => s.Trim().ToLower()).ToArray() ?? Array.Empty(); - // Logger.Log("Using built-in bloatware list.", LogLevel.Info); + whitelistPatterns = Array.Empty(); + Logger.Log("Using built-in bloatware list.", LogLevel.Info); } else { - Logger.Log("Using external bloatware list via CFEnhancer", LogLevel.Info); + Logger.Log("🔎 Plugin ready: CFEnhancer (external bloatware list)", LogLevel.Info); } - // Analyze installed apps against the predefined list - var apps = await _appManager.AnalyzeAndLogAppsAsync(predefined); + // Analyze installed apps based on patterns and whitelist, and optionally scan all + var apps = await _appManager.AnalyzeAndLogAppsAsync(bloatwarePatterns, whitelistPatterns, scanAll); foreach (var app in apps) { checkedListBoxApps.Items.Add(app.FullName); } - - if (!apps.Any()) - { - Logger.Log("✅ No bloatware apps found.", LogLevel.Info); - } } private async void btnFix_Click(object sender, EventArgs e) @@ -182,53 +180,64 @@ namespace CrapFixer } } + /// + /// Analyzes all plugins and features starting from the selected node from the context menu. + /// private async void analyzeMarkedFeatureToolStripMenuItem_Click(object sender, EventArgs e) { if (treeFeatures.SelectedNode is TreeNode selectedNode) { - if (PluginManager.IsPluginNode(selectedNode)) + Logger.Log($"🔎 Analyzing Feature: {selectedNode.Text}", LogLevel.Info); + + // If a single node is selected (leaf node with no children), + // always analyze this node regardless of its Checked state. + if (selectedNode.Nodes.Count == 0) + { await PluginManager.AnalyzePlugin(selectedNode); + } else - Logger.Log($"🔎 Analyzing Feature: {selectedNode.Text}", LogLevel.Info); + { + // If a parent node is selected (has children), + // recursively analyze only the checked plugin nodes. + await PluginManager.AnalyzeAll(selectedNode); + } + + // Perform feature-specific analysis (non-plugin) FeatureNodeManager.AnalyzeFeature(selectedNode); } } - private async void previewMarkedFeatureToolStripMenuItem_Click(object sender, EventArgs e) - { - if (treeFeatures.SelectedNode is TreeNode selectedNode) - { - if (PluginManager.IsPluginNode(selectedNode)) - { - await PluginManager.AnalyzePlugin(selectedNode); - } - else - { - FeatureNodeManager.PreviewChanges(selectedNode); - } - } - } - + /// + /// Fixes all checked plugin and feature nodes starting from the selected node from the context menu. + /// private async void fixMarkedFeatureToolStripMenuItem_Click(object sender, EventArgs e) { if (treeFeatures.SelectedNode is TreeNode selectedNode) { - if (PluginManager.IsPluginNode(selectedNode)) - await PluginManager.FixPlugin(selectedNode); - else - Logger.Log($"🔧 Fixing Feature: {selectedNode.Text}", LogLevel.Info); + Logger.Log($"🔧 Fixing Feature: {selectedNode.Text}", LogLevel.Info); + + // Recursively fix all checked feature nodes (non-plugin) await FeatureNodeManager.FixFeature(selectedNode); + + // Recursively fix all checked plugin nodes starting from the selected node + await PluginManager.FixPlugin(selectedNode); } } - private void restoreMarkedFeatureToolStripMenuItem_Click(object sender, EventArgs e) + /// + /// Restores the selected plugin or feature to its previous state from the context menu. + /// + private async void restoreMarkedFeatureToolStripMenuItem_Click(object sender, EventArgs e) { if (treeFeatures.SelectedNode is TreeNode selectedNode) { if (PluginManager.IsPluginNode(selectedNode)) - PluginManager.RestorePlugin(selectedNode); + // Restore the plugin using its Undo command if available! + await PluginManager.RestorePlugin(selectedNode); else Logger.Log($"↩️ Restoring Feature: {selectedNode.Text}", LogLevel.Info); + + // Perform feature-specific restore (non-plugin) FeatureNodeManager.RestoreFeature(selectedNode); } } @@ -238,18 +247,6 @@ namespace CrapFixer if (treeFeatures.SelectedNode is TreeNode selectedNode) { FeatureNodeManager.ShowHelp(selectedNode); - - // Prompt for online search - var result = MessageBox.Show( - "Would you like to search online for more information about this feature?", - "Online Help", - MessageBoxButtons.YesNo, - MessageBoxIcon.Question); - - if (result == DialogResult.Yes) - { - FeatureNodeManager.ShowHelpOnline(selectedNode); - } } } @@ -322,26 +319,56 @@ namespace CrapFixer private void panelHeader_Paint(object sender, PaintEventArgs e) { - Panel panel = sender as Panel; - Graphics g = e.Graphics; + var panel = sender as Panel; + var g = e.Graphics; - // Fill background + // Solid background: #4D4D4D g.Clear(Color.FromArgb(77, 77, 77)); - // Draw a single magenta line at the bottom - using (Pen pen = new Pen(Color.FromArgb(100, 255, 0, 255))) // Magenta with transparency + // Inset line effect (3D-like): light line + shadow line + Color baseColor = Color.FromArgb(80, 80, 80); // inset base + + using (var topLine = new Pen(ControlPaint.Light(baseColor, 0.0f))) + using (var bottomLine = new Pen(ControlPaint.Dark(baseColor, 0.2f))) { - int y = panel.Height - 1; // Bottom-most pixel - g.DrawLine(pen, 0, y, panel.Width, y); + g.SmoothingMode = SmoothingMode.None; + g.DrawLine(topLine, 0, panel.Height - 2, panel.Width, panel.Height - 2); + g.DrawLine(bottomLine, 0, panel.Height - 1, panel.Width, panel.Height - 1); } } + // Handles click on the header image to open the GitHub page + private void PictureHeader_Click(object sender, EventArgs e) + { + Utils.OpenGitHubPage(sender, e); + } + + // Handles link click to check for updates + private void linkUpdateCheck_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e) + { + var updateUrl = $"https://builtbybel.github.io/CrapFixer/update-check.html?version={Program.GetAppVersion()}"; + + var psi = new ProcessStartInfo + { + FileName = updateUrl, + UseShellExecute = true + }; + Process.Start(psi); + } + private void MainForm_FormClosing(object sender, FormClosingEventArgs e) { if (IniStateManager.IsViewSettingEnabled("SETTINGS", "checkSaveToINI")) { IniStateManager.Save(treeFeatures, this); } + + Logger.OutputBox = null; // Remove reference + } + + private void btnGitHub_Click(object sender, EventArgs e) + { + _navigationManager.SwitchView(new OptionsView()); } } } \ No newline at end of file diff --git a/CFixer/MainForm.resx b/CFixer/MainForm.resx index bfbb24f..3639ac6 100644 --- a/CFixer/MainForm.resx +++ b/CFixer/MainForm.resx @@ -120,339 +120,10 @@ 23, 15 - - - - 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 - AABJRU5ErkJggg== - - + + 171, 15 + + + 171, 15 + \ No newline at end of file diff --git a/CFixer/NavigationHandler.cs b/CFixer/NavigationHandler.cs index 8010d36..7b9477c 100644 --- a/CFixer/NavigationHandler.cs +++ b/CFixer/NavigationHandler.cs @@ -1,6 +1,8 @@ using System; using System.Collections.Generic; using System.Drawing; +using System.IO; +using System.Threading.Tasks; using System.Windows.Forms; namespace CFixer @@ -13,14 +15,14 @@ namespace CFixer // List of all buttons managed by the navigation handler private readonly List