diff --git a/README.md b/README.md
index 1ee589ed..6a8caf64 100644
--- a/README.md
+++ b/README.md
@@ -219,7 +219,7 @@ This project exists to serve the users who were left behind by the march of tech
- Portable (Single executable file)
- Right-to-left language support and bidirectional text
- Use of S.O.L.I.D. principles in object-oriented programming (limited due to the legacy .NET 4.0 framework)
-- Use of Windows native methods for memory management
+- Use of Windows API methods for memory management
- Windows retro-compatibility (Windows XP, Server 2003, and later)
### 💭 Where does the app save the settings?
@@ -261,12 +261,12 @@ When new versions require translation updates, we may use AI tools to provide a
| Language | Contributor(s) | Language | Contributor(s) |
|:---|:---|:---|:---|
| 🇦🇱 Albanian | [Omer Rustemi](https://github.com/omerrustemicode) | 🇯🇵 Japanese | [dai](https://github.com/dai) |
-| 🇸🇦 Arabic | [Abdulmajeed Al-Rajhi](https://github.com/Abdulmajeed-Alrajhi) | 🇰🇷 Korean | [VenusGirl](https://github.com/VenusGirl) |
+| 🇸🇦 Arabic | [Abderraouf FELLAHI](https://github.com/flh-raouf), [Abdulmajeed Al-Rajhi](https://github.com/Abdulmajeed-Alrajhi) | 🇰🇷 Korean | [VenusGirl](https://github.com/VenusGirl) |
| 🇧🇬 Bulgarian | [Konstantin](https://github.com/constantinejc) | 🇲🇰 Macedonian | [Dimitrij Gjorgji](https://github.com/Cathadox) |
| 🇨🇳 Chinese (Simplified) | [KaiHuaDou](https://github.com/KaiHuaDou), [Kun Zhao](https://github.com/kzhdev), [Rayden](https://github.com/raydenake22) | 🇳🇴 Norwegian | [Dan](https://github.com/danorse) |
| 🇨🇳 Chinese (Traditional) | [Rayden](https://github.com/raydenake22), [rtyrtyrtyqw](https://github.com/rtyrtyrtyqw) | 🇮🇷 Persian | [Kavian](https://github.com/KavianK) |
| 🇳🇱 Dutch | [Jesse](https://github.com/dragonhuntermc), [hax4dazy](https://github.com/hax4dazy) | 🇵🇱 Polish | [Patryk](https://github.com/Fresta56) |
-| 🇫🇷 French | [William VINCENT](https://github.com/wixaw) | 🇧🇷 Portuguese | [Igor Mundstein](https://github.com/IgorMundstein) |
+| 🇫🇷 French | [William VINCENT](https://github.com/wixaw) | 🇵🇹 Portuguese (PT) | AI |
| 🇩🇪 German | [Calvin](https://github.com/Slluxx), [Niklas Englmeier](https://github.com/iamniklas), [Steve](https://github.com/uDEV2019) | 🇷🇺 Russian | [Ruslan](https://github.com/ruslooob) |
| 🇬🇷 Greek | [Theodoros Katsageorgis](https://github.com/tkatsageorgis) | 🇷🇸 Serbian | [Dragoš Milošević](https://github.com/DragorMilos) |
| 🇮🇱 Hebrew | [Eliezer Bloy](https://github.com/eliezerbloy) | 🇸🇮 Slovenian | [Jadran Rudec](https://github.com/JadranR) |
diff --git a/src/App.xaml b/src/App.xaml
index 80764aab..35abfe63 100644
--- a/src/App.xaml
+++ b/src/App.xaml
@@ -1,10 +1,13 @@
-
+
+
+
+
+
+
+
+
@@ -12,6 +15,7 @@
+
@@ -29,6 +33,32 @@
Calibri
15
Light
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
@@ -354,21 +322,12 @@
-
-
-
+
+
+
-
+
@@ -403,14 +362,8 @@
-
-
+
+
@@ -431,14 +384,7 @@
-
+
@@ -446,41 +392,12 @@
-
-
+
+
-
-
-
+
+
+
@@ -520,20 +437,8 @@
-
-
+
+
@@ -554,17 +459,10 @@
-
-
-
\ No newline at end of file
+
diff --git a/src/App.xaml.cs b/src/App.xaml.cs
index 86f26149..621041fa 100644
--- a/src/App.xaml.cs
+++ b/src/App.xaml.cs
@@ -6,11 +6,14 @@
using System.IO;
using System.Linq;
using System.Reflection;
+using System.Security.Principal;
using System.ServiceProcess;
using System.Threading;
using System.Windows;
using System.Windows.Forms;
+using System.Windows.Input;
using System.Windows.Threading;
+using Microsoft.Win32;
namespace WinMemoryCleaner
{
@@ -54,7 +57,7 @@ public App()
/// Gets a value indicating whether this instance is in debug mode.
///
///
- /// true if this instance is in debug mode; otherwise, false.
+ /// true if this instance is in debug mode; otherwise, false.
///
public static bool IsInDebugMode
{
@@ -113,6 +116,15 @@ protected virtual void Dispose(bool disposing)
{
if (disposing)
{
+ try
+ {
+ SystemEvents.PowerModeChanged -= OnPowerModeChanged;
+ }
+ catch
+ {
+ // ignored
+ }
+
if (_mutex != null)
{
try
@@ -210,6 +222,73 @@ private void OnDispatcherUnhandledException(object sender, DispatcherUnhandledEx
Logger.Error(e.Exception);
}
+ ///
+ /// Handles power mode changes (suspend/resume from hibernation).
+ ///
+ /// The sender.
+ /// The instance containing the event data.
+ private static void OnPowerModeChanged(object sender, PowerModeChangedEventArgs e)
+ {
+ try
+ {
+ switch (e.Mode)
+ {
+ case PowerModes.Resume:
+ ThreadPool.QueueUserWorkItem(_ =>
+ {
+ try
+ {
+ // XP/2003 need more stabilization time before reinitialization
+ Thread.Sleep(Environment.OSVersion.Version.Major < 6 ? 10000 : 5000);
+
+ // Retry logic for handling transient failures during system resume
+ const int maxRetries = 3;
+ var retryCount = 0;
+ Exception lastException = null;
+
+ while (retryCount < maxRetries)
+ {
+ try
+ {
+ var mainViewModel = DependencyInjection.Container.Resolve();
+
+ if (mainViewModel == null)
+ throw new InvalidOperationException("MainViewModel could not be resolved from the DI container");
+
+ mainViewModel.ReinitializeAfterHibernation();
+ return;
+ }
+ catch (Exception ex)
+ {
+ lastException = ex;
+ retryCount++;
+
+ if (retryCount >= maxRetries)
+ {
+ var failureException = new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, "Failed to reinitialize after hibernation after {0} attempts", maxRetries), lastException);
+ Logger.Error(failureException.Message + ": " + failureException.GetMessage());
+ throw;
+ }
+
+ // Wait before retrying
+ Thread.Sleep(5000);
+ }
+ }
+ }
+ catch (Exception threadEx)
+ {
+ Logger.Error("Critical error in power mode resume handling: " + threadEx.GetMessage());
+ }
+ });
+ break;
+ }
+ }
+ catch (Exception ex)
+ {
+ Logger.Error("Error handling power mode change: " + ex.GetMessage());
+ }
+ }
+
///
/// Called when [process exit].
///
@@ -237,47 +316,78 @@ private void OnProcessExit(object sender, EventArgs e)
///
/// The sender.
/// The instance containing the event data.
- private void OnNotifyIconClick(object sender, EventArgs e)
+ private void OnNotifyIconClick(object sender, System.Windows.Forms.MouseEventArgs e)
{
lock (_showHidelock)
{
- var mouseEventArgs = e as MouseEventArgs;
-
- // Show/Hide
- if (mouseEventArgs != null && mouseEventArgs.Button == MouseButtons.Left && MainWindow != null)
+ switch (e.Button)
{
- if (MainWindow.OwnedWindows.Cast().Where(window => window != null && window.IsDialog).Any())
- {
- MainWindow.Activate();
- MainWindow.Focus();
+ // Show/Hide
+ case MouseButtons.Left:
+ if (MainWindow == null)
+ return;
- return;
- }
+ if (MainWindow.OwnedWindows.Cast().Where(window => window != null && window.IsDialog).Any())
+ {
+ MainWindow.Activate();
+ MainWindow.Focus();
- switch (MainWindow.Visibility)
- {
- case Visibility.Collapsed:
- case Visibility.Hidden:
- MainWindow.Show();
+ return;
+ }
- MainWindow.WindowState = WindowState.Normal;
+ switch (MainWindow.Visibility)
+ {
+ case Visibility.Collapsed:
+ case Visibility.Hidden:
+ MainWindow.Show();
- MainWindow.Activate();
- MainWindow.Focus();
+ MainWindow.WindowState = WindowState.Normal;
- MainWindow.Topmost = true;
- MainWindow.Topmost = Settings.AlwaysOnTop;
- MainWindow.ShowInTaskbar = true;
- break;
+ MainWindow.Activate();
+ MainWindow.Focus();
- case Visibility.Visible:
- MainWindow.Hide();
+ MainWindow.Topmost = true;
+ MainWindow.Topmost = Settings.AlwaysOnTop;
+ MainWindow.ShowInTaskbar = true;
- MainWindow.ShowInTaskbar = false;
- break;
- }
+ // Focus the Optimize button when restoring from notification area
+ MainWindow.Dispatcher.BeginInvoke((Action)(() =>
+ {
+ var mainWindow = MainWindow as MainWindow;
+
+ if (mainWindow != null)
+ {
+ var optimizeButton = mainWindow.FindName("Optimize") as UIElement;
+
+ if (optimizeButton != null)
+ {
+ Keyboard.Focus(optimizeButton);
+ FocusManager.SetFocusedElement(mainWindow, optimizeButton);
+ }
+ }
+ }), DispatcherPriority.ApplicationIdle);
+ break;
+
+ case Visibility.Visible:
+ MainWindow.Hide();
+
+ MainWindow.ShowInTaskbar = false;
+ break;
+ }
+
+ ReleaseMemory();
+ return;
+
+ // Optimize
+ case MouseButtons.Middle:
+ if (!Settings.TrayIconOptimizeOnMiddleMouseClick)
+ return;
+
+ var mainViewModel = DependencyInjection.Container.Resolve();
- ReleaseMemory();
+ if (mainViewModel.OptimizeCommand.CanExecute(null))
+ mainViewModel.OptimizeCommand.Execute(null);
+ break;
}
}
}
@@ -321,9 +431,6 @@ protected override void OnStartup(StartupEventArgs startupEvent)
if (argument.Equals(Constants.App.CommandLineArgument.Install, StringComparison.OrdinalIgnoreCase))
startupType = Enums.StartupType.Installation;
- if (argument.Equals(Constants.App.CommandLineArgument.Package, StringComparison.OrdinalIgnoreCase))
- startupType = Enums.StartupType.Package;
-
if (argument.Equals(Constants.App.CommandLineArgument.Service, StringComparison.OrdinalIgnoreCase))
startupType = Enums.StartupType.Service;
@@ -332,13 +439,6 @@ protected override void OnStartup(StartupEventArgs startupEvent)
}
}
- // Update the last executable path when neither installing nor updating via a package manager
- if (startupType != Enums.StartupType.Package)
- {
- Settings.Path = Path;
- Settings.Save();
- }
-
switch (startupType)
{
case Enums.StartupType.App:
@@ -372,7 +472,7 @@ protected override void OnStartup(StartupEventArgs startupEvent)
// Notification Areas
_notifyIcon = new NotifyIcon();
- _notifyIcon.Click += OnNotifyIconClick;
+ _notifyIcon.MouseUp += OnNotifyIconClick;
// DI/IOC
DependencyInjection.Container.Register(_notifyIcon);
@@ -382,6 +482,9 @@ protected override void OnStartup(StartupEventArgs startupEvent)
if (!Settings.StartMinimized)
mainWindow.Show();
+ // Subscribe to power events
+ SystemEvents.PowerModeChanged += OnPowerModeChanged;
+
// Process notifications
foreach (var notification in _notifications)
{
@@ -399,116 +502,6 @@ protected override void OnStartup(StartupEventArgs startupEvent)
Shutdown();
break;
- case Enums.StartupType.Package:
- commandLineArguments.Remove(string.Format(Localizer.Culture, "/{0}", Constants.App.CommandLineArgument.Package));
-
- var exe = AppDomain.CurrentDomain.FriendlyName;
- var isUpdate = false;
- var sourcePath = System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, exe);
- var targetPath = sourcePath;
- var wasRunning = false;
-
- try
- {
- if (Directory.Exists(System.IO.Path.GetDirectoryName(Settings.Path)))
- targetPath = Settings.Path;
- }
- catch
- {
- // Ignored
- }
-
- try
- {
- var runningProcesses = Process.GetProcessesByName(Constants.App.Name)
- .Where(p => p != null && p.Id != Process.GetCurrentProcess().Id)
- .ToList();
-
- wasRunning = runningProcesses.Any();
-
- foreach (var process in runningProcesses)
- {
- try
- {
- var processPath = process.MainModule.FileName;
-
- if (!string.IsNullOrEmpty(processPath) && File.Exists(processPath))
- targetPath = processPath;
- }
- catch
- {
- // Ignored
- }
-
- try
- {
- process.Kill();
- process.WaitForExit();
- }
- catch
- {
- // Ignored
- }
- finally
- {
- process.Dispose();
- }
- }
- }
- catch
- {
- // Ignored
- }
-
- try
- {
- if (File.Exists(targetPath))
- {
- var currentVersion = FileVersionInfo.GetVersionInfo(sourcePath);
- var targetVersion = FileVersionInfo.GetVersionInfo(targetPath);
-
- isUpdate = currentVersion.FileVersion != targetVersion.FileVersion;
- }
- }
- catch
- {
- // Ignored
- }
-
- try
- {
- var targetDirectory = System.IO.Path.GetDirectoryName(targetPath);
-
- if (!string.IsNullOrEmpty(targetDirectory) && !Directory.Exists(targetDirectory))
- Directory.CreateDirectory(targetDirectory);
- }
- catch
- {
- // Ignored
- }
-
- var restart = !isUpdate || wasRunning;
-
- _processes.Add(new ProcessStartInfo
- {
- Arguments = string.Format
- (
- CultureInfo.InvariantCulture,
- @"/c move ""{0}"" ""{1}"" >nul 2>&1 & if {2} equ 1 if exist ""{1}"" start """" ""{1}"" {3}",
- sourcePath,
- targetPath,
- restart ? "1" : "0",
- string.Join(" ", commandLineArguments.Concat(new[] { isUpdate ? string.Format(CultureInfo.InvariantCulture, "/{0}", Version) : string.Empty }).Where(arg => !string.IsNullOrEmpty(arg)))
- ).Trim(),
- CreateNoWindow = true,
- FileName = "cmd",
- UseShellExecute = false,
- WindowStyle = ProcessWindowStyle.Hidden
- });
-
- Shutdown();
- break;
-
case Enums.StartupType.Service:
using (var service = new WinService())
{
@@ -589,57 +582,140 @@ public static void RunOnStartup(bool enable)
{
if (enable)
{
- var runLevelArgument = Environment.OSVersion.Version.Major >= 6 ? " /RL HIGHEST" : string.Empty;
+ var isTaskCreated = false;
- var createStartInfo = new ProcessStartInfo("schtasks")
+ try
{
- Arguments = string.Format(Localizer.Culture, @"/CREATE /F /IT{0} /SC ONLOGON /TN ""{1}"" /TR ""{2}"" /RU ""{3}""", runLevelArgument, Constants.App.Title, Path, Environment.UserName),
- CreateNoWindow = true,
- UseShellExecute = false,
- WindowStyle = ProcessWindowStyle.Hidden,
- RedirectStandardError = true
- };
+ var taskXml = string.Format
+ (
+ CultureInfo.InvariantCulture,
+ @"
+
+
+ {3}
+ Runs {0} at logon.
+ {4}
+
+
+
+ true
+
+
+
+
+ {2}
+ InteractiveToken
+ HighestAvailable
+
+
+
+ IgnoreNew
+ false
+ false
+ true
+ true
+ false
+
+ PT10M
+ false
+ false
+
+ true
+ true
+ false
+ false
+ false
+ PT0S
+ 7
+
+
+
+ ""{1}""
+
+
+ ",
+ Constants.App.Title,
+ Path,
+ WindowsIdentity.GetCurrent().User.Value,
+ string.Format(CultureInfo.InvariantCulture, "WMC {0} ({1})", string.Format(Localizer.Culture, Constants.App.VersionFormat, Version.Major, Version.Minor, Version.Build), Environment.UserName),
+ DateTime.Now.ToString("yyyy-MM-ddTHH:mm:ss", CultureInfo.InvariantCulture)
+ );
+
+ var tempXmlFile = System.IO.Path.GetTempFileName();
+
+ File.WriteAllText(tempXmlFile, taskXml);
+
+ var createStartInfo = new ProcessStartInfo("schtasks")
+ {
+ Arguments = string.Format(CultureInfo.InvariantCulture, @"/CREATE /F /TN ""{0}"" /XML ""{1}""", Constants.App.Title, tempXmlFile),
+ CreateNoWindow = true,
+ UseShellExecute = false,
+ WindowStyle = ProcessWindowStyle.Hidden,
+ RedirectStandardError = true
+ };
- using (var createProcess = Process.Start(createStartInfo))
+ using (var createProcess = Process.Start(createStartInfo))
+ {
+ var errorMessage = createProcess.StandardError.ReadToEnd();
+ createProcess.WaitForExit();
+
+ if (createProcess.ExitCode == Constants.Windows.SystemErrorCode.ErrorSuccess)
+ isTaskCreated = true;
+ else
+ Logger.Error(string.Format(Localizer.Culture, "XML task creation failed (will attempt fallback). Error: {0}", errorMessage));
+ }
+
+ try
+ {
+ File.Delete(tempXmlFile);
+ }
+ catch
+ {
+ // ignored
+ }
+ }
+ catch (Exception ex)
+ {
+ Logger.Error(string.Format(Localizer.Culture, "An exception occurred during XML task creation (will attempt fallback): {0}", ex.GetMessage()));
+ }
+
+ if (!isTaskCreated)
{
- var errorMessage = createProcess.StandardError.ReadToEnd();
- createProcess.WaitForExit();
+ Logger.Information("Attempting basic fallback method to create startup task.");
- if (createProcess.ExitCode != Constants.Windows.SystemErrorCode.ErrorSuccess)
- Logger.Error(string.Format(Localizer.Culture, "Failed to create startup task for '{0}'. Error: {1}", Constants.App.Title, errorMessage));
+ var createStartInfo = new ProcessStartInfo("schtasks")
+ {
+ Arguments = string.Format(CultureInfo.InvariantCulture, @"/CREATE /F /SC ONLOGON /TN ""{0}"" /TR ""{1}"" /RU ""{2}""", Constants.App.Title, Path, Environment.UserName),
+ CreateNoWindow = true,
+ UseShellExecute = false,
+ WindowStyle = ProcessWindowStyle.Hidden,
+ RedirectStandardError = true
+ };
+
+ using (var createProcess = Process.Start(createStartInfo))
+ {
+ var errorMessage = createProcess.StandardError.ReadToEnd();
+ createProcess.WaitForExit();
+
+ if (createProcess.ExitCode != Constants.Windows.SystemErrorCode.ErrorSuccess)
+ Logger.Error(string.Format(Localizer.Culture, "Fallback task creation also failed for '{0}'. Error: {1}", Constants.App.Title, errorMessage));
+ }
}
}
else
{
- var queryStartInfo = new ProcessStartInfo("schtasks")
+ var deleteStartInfo = new ProcessStartInfo("schtasks")
{
- Arguments = string.Format(Localizer.Culture, @"/QUERY /TN ""{0}""", Constants.App.Title),
+ Arguments = string.Format(CultureInfo.InvariantCulture, @"/DELETE /F /TN ""{0}""", Constants.App.Title),
CreateNoWindow = true,
UseShellExecute = false,
- WindowStyle = ProcessWindowStyle.Hidden,
- RedirectStandardError = true,
- RedirectStandardOutput = true
+ WindowStyle = ProcessWindowStyle.Hidden
};
- using (var queryProcess = Process.Start(queryStartInfo))
+ using (var deleteProcess = Process.Start(deleteStartInfo))
{
- queryProcess.WaitForExit();
-
- if (queryProcess.ExitCode == Constants.Windows.SystemErrorCode.ErrorSuccess)
- {
- var deleteStartInfo = new ProcessStartInfo("schtasks")
- {
- Arguments = string.Format(Localizer.Culture, @"/DELETE /F /TN ""{0}""", Constants.App.Title),
- CreateNoWindow = true,
- UseShellExecute = false,
- WindowStyle = ProcessWindowStyle.Hidden
- };
-
- using (var deleteProcess = Process.Start(deleteStartInfo))
- {
- deleteProcess.WaitForExit();
- }
- }
+ deleteProcess.WaitForExit();
}
}
}
diff --git a/src/Converters/PercentageToWidthConverter.cs b/src/Converters/PercentageToWidthConverter.cs
new file mode 100644
index 00000000..e59b9aa9
--- /dev/null
+++ b/src/Converters/PercentageToWidthConverter.cs
@@ -0,0 +1,65 @@
+using System;
+using System.Globalization;
+using System.Windows;
+using System.Windows.Data;
+
+namespace WinMemoryCleaner
+{
+ ///
+ /// Percentage To Width Converter
+ /// Converts a percentage value (0-100) and container width to the actual pixel width
+ ///
+ ///
+ public class PercentageToWidthConverter : IMultiValueConverter
+ {
+ ///
+ /// Converts a percentage and container width to pixel width.
+ ///
+ /// The array of values: [0] = container width, [1] = percentage (0-100)
+ /// The type of the binding target property.
+ /// The converter parameter to use.
+ /// The culture to use in the converter.
+ /// The calculated width in pixels
+ public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
+ {
+ try
+ {
+ if (values == null || values.Length < 2)
+ return 0.0;
+
+ if (values[0] == DependencyProperty.UnsetValue || values[1] == DependencyProperty.UnsetValue)
+ return 0.0;
+
+ var containerWidth = System.Convert.ToDouble(values[0], culture);
+ var percentage = System.Convert.ToDouble(values[1], culture);
+
+ if (containerWidth <= 0 || percentage < 0)
+ return 0.0;
+
+ // Clamp percentage to 0-100
+ if (percentage > 100)
+ percentage = 100;
+
+ return containerWidth * (percentage / 100.0);
+ }
+ catch
+ {
+ return 0.0;
+ }
+ }
+
+ ///
+ /// Converts a width back to percentage (not supported).
+ ///
+ /// The value that the binding target produces.
+ /// The array of types to convert to.
+ /// The converter parameter to use.
+ /// The culture to use in the converter.
+ /// Not supported
+ ///
+ public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
+ {
+ throw new NotSupportedException();
+ }
+ }
+}
diff --git a/src/Core/Constants.cs b/src/Core/Constants.cs
index d491b81f..05c442db 100644
--- a/src/Core/Constants.cs
+++ b/src/Core/Constants.cs
@@ -44,7 +44,6 @@ public static class Test
public static class CommandLineArgument
{
public const string Install = "Install";
- public const string Package = "Package";
public const string Service = "Service";
public const string Uninstall = "Uninstall";
}
@@ -145,6 +144,8 @@ public static class Locale
public static class Name
{
public const string English = "en";
+ public const string PortugueseBrazil = "pt-BR";
+ public const string PortuguesePortugal = "pt-PT";
public const string SimplifiedChinese = "zh-Hans";
public const string TraditionalChinese = "zh-Hant";
}
diff --git a/src/Core/Helper.cs b/src/Core/Helper.cs
index 9f085849..9ccb2591 100644
--- a/src/Core/Helper.cs
+++ b/src/Core/Helper.cs
@@ -3,6 +3,7 @@
using System.IO;
using System.Linq.Expressions;
using System.Reflection;
+using System.Text;
using System.Web.Script.Serialization;
namespace WinMemoryCleaner
@@ -13,26 +14,29 @@ namespace WinMemoryCleaner
public static class Helper
{
///
- /// Determines if the current Windows version supports updates via GitHub TLS/SNI.
- /// Returns false for legacy Windows versions (XP/2003) that cannot reach GitHub.
+ /// Appends indentation to the StringBuilder based on the current nesting level.
///
- /// True if updates are supported; otherwise, false.
- public static bool IsAutoUpdateSupported
+ /// The StringBuilder to append to.
+ /// The current indentation level.
+ /// The string to use for each indentation level.
+ private static void AppendIndent(StringBuilder sb, int level, string indent)
{
- get
- {
- try
- {
- var os = Environment.OSVersion;
-
- if (os.Version != null && os.Version.Major < 6)
- return false; // Windows XP/2003 and earlier
- }
- catch
- {
- }
+ for (var i = 0; i < level; i++)
+ sb.Append(indent);
+ }
- return true;
+ ///
+ /// Creates a directory exists. Ignores failures.
+ ///
+ public static void CreateDirectory(string path)
+ {
+ try
+ {
+ if (!string.IsNullOrEmpty(path) && !Directory.Exists(path))
+ Directory.CreateDirectory(path);
+ }
+ catch
+ {
}
}
@@ -40,26 +44,103 @@ public static bool IsAutoUpdateSupported
/// Converts the specified JSON string to an object of type T
///
///
- /// The input.
+ /// The object.
///
- public static T Deserialize(string input)
+ public static T Deserialize(string obj)
{
- return new JavaScriptSerializer().Deserialize(input);
+ return new JavaScriptSerializer().Deserialize(obj);
}
///
- /// Creates a directory exists. Ignores failures.
+ /// Formats a minified JSON string into a pretty-printed format with proper indentation and line breaks.
///
- public static void CreateDirectory(string path)
+ /// The minified JSON string to format.
+ /// A formatted JSON string with indentation and line breaks.
+ private static string FormatJson(string json)
{
- try
- {
- if (!string.IsNullOrEmpty(path) && !Directory.Exists(path))
- Directory.CreateDirectory(path);
- }
- catch
+ if (string.IsNullOrEmpty(json))
+ return string.Empty;
+
+ var sb = new StringBuilder(json.Length * 2);
+ var indent = " ";
+ var level = 0;
+ var inString = false;
+ var escapeNext = false;
+
+ for (var i = 0; i < json.Length; i++)
{
+ var c = json[i];
+
+ if (escapeNext)
+ {
+ sb.Append(c);
+ escapeNext = false;
+ continue;
+ }
+
+ if (c == '\\')
+ {
+ sb.Append(c);
+ escapeNext = true;
+ continue;
+ }
+
+ if (c == '"')
+ {
+ sb.Append(c);
+ inString = !inString;
+ continue;
+ }
+
+ if (inString)
+ {
+ sb.Append(c);
+ continue;
+ }
+
+ switch (c)
+ {
+ case '{':
+ case '[':
+ sb.Append(c);
+ sb.Append(Environment.NewLine);
+ level++;
+ AppendIndent(sb, level, indent);
+ break;
+
+ case '}':
+ case ']':
+ sb.Append(Environment.NewLine);
+ level--;
+ AppendIndent(sb, level, indent);
+ sb.Append(c);
+ break;
+
+ case ',':
+ sb.Append(c);
+ sb.Append(Environment.NewLine);
+ AppendIndent(sb, level, indent);
+ break;
+
+ case ':':
+ sb.Append(c);
+ sb.Append(' ');
+ break;
+
+ case ' ':
+ case '\t':
+ case '\r':
+ case '\n':
+ // Skip whitespace outside strings
+ break;
+
+ default:
+ sb.Append(c);
+ break;
+ }
}
+
+ return sb.ToString();
}
///
@@ -103,6 +184,30 @@ public static string GetExecutablePath()
}
}
+ ///
+ /// Determines if the current Windows version supports updates via GitHub TLS/SNI.
+ /// Returns false for legacy Windows versions (XP/2003) that cannot reach GitHub.
+ ///
+ /// True if updates are supported; otherwise, false.
+ public static bool IsAutoUpdateSupported
+ {
+ get
+ {
+ try
+ {
+ var os = Environment.OSVersion;
+
+ if (os.Version != null && os.Version.Major < 6)
+ return false; // Windows XP/2003 and earlier
+ }
+ catch
+ {
+ }
+
+ return true;
+ }
+ }
+
///
/// Gets the string name of a property or field.
///
@@ -136,6 +241,22 @@ public static T ReadEmbeddedResource(string name)
}
}
+ ///
+ /// Converts the specified object to a JSON string
+ ///
+ /// The object to serialize.
+ /// If true, produces compact JSON without formatting; otherwise, formats with indentation for readability. Default is false.
+ /// A JSON string representation of the object.
+ public static string Serialize(IJsonSerializable obj, bool minified = false)
+ {
+ if (obj == null)
+ throw new ArgumentNullException("obj");
+
+ var json = new JavaScriptSerializer().Serialize(obj.ToJson());
+
+ return minified ? json : FormatJson(json);
+ }
+
///
/// Converts to hexcode.
///
diff --git a/src/Core/Logger.cs b/src/Core/Logger.cs
index 085f13f2..505dc25f 100644
--- a/src/Core/Logger.cs
+++ b/src/Core/Logger.cs
@@ -245,7 +245,7 @@ public static void Log(Log log)
if (log == null)
throw new ArgumentNullException("log");
- Log(log.Level, log.ToString());
+ Log(log.Level, Helper.Serialize(log));
}
catch (Exception e)
{
diff --git a/src/Core/Migrator.cs b/src/Core/Migrator.cs
index f2c57395..19404c91 100644
--- a/src/Core/Migrator.cs
+++ b/src/Core/Migrator.cs
@@ -19,6 +19,11 @@ public static void Run()
MigrateSettingsFromCurrentUserToLocalMachine();
RemoveStartupRegistry();
}
+ // 3.0+
+ if (App.Version >= new Version(3, 0))
+ {
+ RemoveRegistryPath();
+ }
}
#region Classes
@@ -152,6 +157,29 @@ private static void MigrateSettingsFromCurrentUserToLocalMachine()
}
}
+ ///
+ /// Removes the registry path.
+ ///
+ private static void RemoveRegistryPath()
+ {
+ try
+ {
+ using (var key = Registry.LocalMachine.OpenSubKey(Constants.App.Registry.Key.Settings, true))
+ {
+ if (key != null && key.GetValue(V30.Registry.Path, null) != null)
+ {
+ key.DeleteValue(V30.Registry.Path, false);
+
+ Logger.Information("Migration version update: Removed path registry entry.");
+ }
+ }
+ }
+ catch (Exception e)
+ {
+ Logger.Error(e);
+ }
+ }
+
///
/// Removes the startup registry to avoid UAC warnings. Use a scheduled task instead to run at startup.
///
diff --git a/src/Core/NativeMethods.cs b/src/Core/NativeMethods.cs
index 9d2bbd39..8d1ff4af 100644
--- a/src/Core/NativeMethods.cs
+++ b/src/Core/NativeMethods.cs
@@ -7,7 +7,7 @@
namespace WinMemoryCleaner
{
///
- /// Windows Native Methods
+ /// Windows API
///
internal static class NativeMethods
{
diff --git a/src/Core/Settings.cs b/src/Core/Settings.cs
index 963922bc..fa2540e3 100644
--- a/src/Core/Settings.cs
+++ b/src/Core/Settings.cs
@@ -50,8 +50,6 @@ static Settings()
public static ModifierKeys OptimizationModifiers { get; set; }
- public static string Path { get; set; }
-
public static SortedSet ProcessExclusionList { get; private set; }
public static Enums.Priority RunOnPriority { get; set; }
@@ -70,6 +68,10 @@ static Settings()
public static byte TrayIconDangerLevel { get; set; }
+ public static bool TrayIconOptimizeOnMiddleMouseClick { get; set; }
+
+ public static Brush TrayIconOptimizingColor { get; set; }
+
public static bool TrayIconShowMemoryUsage { get; set; }
public static Brush TrayIconTextColor { get; set; }
@@ -101,7 +103,6 @@ private static void Load(bool loadUserValues = true)
MemoryAreas = Enums.Memory.Areas.CombinedPageList | Enums.Memory.Areas.ModifiedFileCache | Enums.Memory.Areas.ModifiedPageList | Enums.Memory.Areas.RegistryCache | Enums.Memory.Areas.StandbyList | Enums.Memory.Areas.SystemFileCache | Enums.Memory.Areas.WorkingSet;
OptimizationKey = Key.M;
OptimizationModifiers = ModifierKeys.Control | ModifierKeys.Shift;
- Path = string.Empty;
ProcessExclusionList = new SortedSet(StringComparer.OrdinalIgnoreCase);
RunOnPriority = Enums.Priority.Low;
RunOnStartup = false;
@@ -111,12 +112,14 @@ private static void Load(bool loadUserValues = true)
TrayIconBackgroundColor = Brushes.DarkGreen;
TrayIconDangerColor = Brushes.DarkRed;
TrayIconDangerLevel = 90;
+ TrayIconOptimizeOnMiddleMouseClick = false;
+ TrayIconOptimizingColor = Brushes.DimGray;
TrayIconShowMemoryUsage = false;
TrayIconTextColor = Brushes.White;
TrayIconUseTransparentBackground = false;
TrayIconWarningColor = Brushes.DarkGoldenrod;
TrayIconWarningLevel = 80;
- UseHotkey = true;
+ UseHotkey = false;
// User values
try
@@ -169,8 +172,6 @@ private static void Load(bool loadUserValues = true)
if (Enum.TryParse(Convert.ToString(key.GetValue(Helper.NameOf(() => OptimizationModifiers), OptimizationModifiers), _culture), out optimizationModifiers) && optimizationModifiers.IsValid())
OptimizationModifiers = optimizationModifiers;
- Path = Convert.ToString(key.GetValue(Helper.NameOf(() => Path), Path), _culture);
-
Enums.Priority runOnPriority;
if (Enum.TryParse(Convert.ToString(key.GetValue(Helper.NameOf(() => RunOnPriority), RunOnPriority), _culture), out runOnPriority) && runOnPriority.IsValid())
@@ -183,6 +184,8 @@ private static void Load(bool loadUserValues = true)
TrayIconBackgroundColor = Convert.ToString(key.GetValue(Helper.NameOf(() => TrayIconBackgroundColor), TrayIconBackgroundColor), _culture).ToBrush(TrayIconBackgroundColor);
TrayIconDangerColor = Convert.ToString(key.GetValue(Helper.NameOf(() => TrayIconDangerColor), TrayIconDangerColor), _culture).ToBrush(TrayIconDangerColor);
TrayIconDangerLevel = Convert.ToByte(key.GetValue(Helper.NameOf(() => TrayIconDangerLevel), TrayIconDangerLevel), _culture);
+ TrayIconOptimizeOnMiddleMouseClick = Convert.ToBoolean(key.GetValue(Helper.NameOf(() => TrayIconOptimizeOnMiddleMouseClick), TrayIconOptimizeOnMiddleMouseClick), _culture);
+ TrayIconOptimizingColor = Convert.ToString(key.GetValue(Helper.NameOf(() => TrayIconOptimizingColor), TrayIconOptimizingColor), _culture).ToBrush(TrayIconOptimizingColor);
TrayIconShowMemoryUsage = Convert.ToBoolean(key.GetValue(Helper.NameOf(() => TrayIconShowMemoryUsage), TrayIconShowMemoryUsage), _culture);
TrayIconTextColor = Convert.ToString(key.GetValue(Helper.NameOf(() => TrayIconTextColor), TrayIconTextColor), _culture).ToBrush(TrayIconTextColor);
TrayIconUseTransparentBackground = Convert.ToBoolean(key.GetValue(Helper.NameOf(() => TrayIconUseTransparentBackground), TrayIconUseTransparentBackground), _culture);
@@ -265,7 +268,6 @@ public static void Save()
key.SetValue(Helper.NameOf(() => MemoryAreas), (int)MemoryAreas);
key.SetValue(Helper.NameOf(() => OptimizationKey), (int)OptimizationKey);
key.SetValue(Helper.NameOf(() => OptimizationModifiers), (int)OptimizationModifiers);
- key.SetValue(Helper.NameOf(() => Path), Path);
key.SetValue(Helper.NameOf(() => RunOnPriority), (int)RunOnPriority);
key.SetValue(Helper.NameOf(() => RunOnStartup), RunOnStartup ? 1 : 0);
key.SetValue(Helper.NameOf(() => ShowOptimizationNotifications), ShowOptimizationNotifications ? 1 : 0);
@@ -274,6 +276,8 @@ public static void Save()
key.SetValue(Helper.NameOf(() => TrayIconBackgroundColor), TrayIconBackgroundColor.GetHex(true));
key.SetValue(Helper.NameOf(() => TrayIconDangerColor), TrayIconDangerColor.GetHex(true));
key.SetValue(Helper.NameOf(() => TrayIconDangerLevel), TrayIconDangerLevel);
+ key.SetValue(Helper.NameOf(() => TrayIconOptimizeOnMiddleMouseClick), TrayIconOptimizeOnMiddleMouseClick ? 1 : 0);
+ key.SetValue(Helper.NameOf(() => TrayIconOptimizingColor), TrayIconOptimizingColor.GetHex(true));
key.SetValue(Helper.NameOf(() => TrayIconShowMemoryUsage), TrayIconShowMemoryUsage ? 1 : 0);
key.SetValue(Helper.NameOf(() => TrayIconTextColor), TrayIconTextColor.GetHex(true));
key.SetValue(Helper.NameOf(() => TrayIconUseTransparentBackground), TrayIconUseTransparentBackground ? 1 : 0);
diff --git a/src/Core/Updater.cs b/src/Core/Updater.cs
index 594e1f50..8b93500e 100644
--- a/src/Core/Updater.cs
+++ b/src/Core/Updater.cs
@@ -138,7 +138,7 @@ private static void ApplyUpdateAndRelaunch(string targetPath, string backupPath,
}
catch (Exception e)
{
- LogCritical("UPD-APPLY-ROLLBACK", e);
+ Log("UPD-APPLY-ROLLBACK", e);
}
return;
@@ -159,7 +159,7 @@ private static void ApplyUpdateAndRelaunch(string targetPath, string backupPath,
}
catch (Exception e)
{
- LogCritical("UPD-RELAUNCH", e);
+ Log("UPD-RELAUNCH", e);
}
}
@@ -559,7 +559,7 @@ private static bool IsSameVolume(string a, string b)
}
}
- private static void LogCritical(string code, Exception e)
+ private static void Log(string code, Exception e)
{
try
{
@@ -652,7 +652,7 @@ private static bool TryApplyUpdateFromEnvironment()
}
catch (Exception e)
{
- LogCritical("UPD-READ-TOKEN", e);
+ Log("UPD-READ-TOKEN", e);
App.Shutdown(true);
return true;
@@ -838,8 +838,7 @@ public static void Update(List commandLineArguments = null)
{
var commandLineArgument = arg != null ? arg.Replace("/", string.Empty) : null;
- if (string.Equals(commandLineArgument, Constants.App.CommandLineArgument.Package, StringComparison.OrdinalIgnoreCase) ||
- string.Equals(commandLineArgument, Constants.App.CommandLineArgument.Install, StringComparison.OrdinalIgnoreCase) ||
+ if (string.Equals(commandLineArgument, Constants.App.CommandLineArgument.Install, StringComparison.OrdinalIgnoreCase) ||
string.Equals(commandLineArgument, Constants.App.CommandLineArgument.Uninstall, StringComparison.OrdinalIgnoreCase) ||
string.Equals(commandLineArgument, Constants.App.CommandLineArgument.Service, StringComparison.OrdinalIgnoreCase))
return;
@@ -967,10 +966,8 @@ public static void Update(List commandLineArguments = null)
}
}
- var relaunchArgs = BuildArgumentString(relaunchArgsList);
-
var backupPath = BuildBackupPath(exePath);
-
+ var relaunchArgs = BuildArgumentString(relaunchArgsList);
var token = Guid.NewGuid().ToString("N");
var tokenFilePath = BuildTokenFilePath(token);
@@ -986,7 +983,7 @@ public static void Update(List commandLineArguments = null)
}
catch (Exception e)
{
- LogCritical("UPD-WRITE-TOKEN", e);
+ Log("UPD-WRITE-TOKEN", e);
SafeDeleteFile(stagedExePath);
SafeDeleteFile(tokenFilePath);
@@ -1010,7 +1007,7 @@ public static void Update(List commandLineArguments = null)
}
catch (Exception e)
{
- LogCritical("UPD-LAUNCH-STAGED", e);
+ Log("UPD-LAUNCH-STAGED", e);
SafeDeleteFile(stagedExePath);
SafeDeleteFile(tokenFilePath);
@@ -1022,7 +1019,7 @@ public static void Update(List commandLineArguments = null)
}
catch (Exception e)
{
- LogCritical("UPD-UNHANDLED", e);
+ Log("UPD-UNHANDLED", e);
}
}
#endregion
diff --git a/src/Interfaces/IJsonSerializable.cs b/src/Interfaces/IJsonSerializable.cs
new file mode 100644
index 00000000..9f8e60d4
--- /dev/null
+++ b/src/Interfaces/IJsonSerializable.cs
@@ -0,0 +1,14 @@
+namespace WinMemoryCleaner
+{
+ ///
+ /// Represents an object that can be serialized to JSON.
+ ///
+ public interface IJsonSerializable
+ {
+ ///
+ /// Converts the object to a JSON-serializable representation.
+ ///
+ /// An object ready for JSON serialization.
+ object ToJson();
+ }
+}
diff --git a/src/Interfaces/INotificationService.cs b/src/Interfaces/INotificationService.cs
index 0b1f6b80..422b16ef 100644
--- a/src/Interfaces/INotificationService.cs
+++ b/src/Interfaces/INotificationService.cs
@@ -31,6 +31,7 @@ public interface INotificationService : IDisposable
/// Update Info
///
/// The memory.
- void Update(Memory memory);
+ /// if set to true [is optimizing].
+ void Update(Memory memory, bool isOptimizing = false);
}
}
diff --git a/src/Model/Language.cs b/src/Model/Language.cs
index be589146..c3a7e9fd 100644
--- a/src/Model/Language.cs
+++ b/src/Model/Language.cs
@@ -45,6 +45,12 @@ public string DisplayEnglishName
{
switch (Name)
{
+ case Constants.Windows.Locale.Name.PortugueseBrazil:
+ return "Portuguese";
+
+ case Constants.Windows.Locale.Name.PortuguesePortugal:
+ return "Portuguese";
+
case Constants.Windows.Locale.Name.SimplifiedChinese:
return "Chinese (S)";
@@ -57,6 +63,30 @@ public string DisplayEnglishName
}
}
+ ///
+ /// Gets the display name of the native.
+ ///
+ ///
+ /// The display name of the native.
+ ///
+ public string DisplayNativeName
+ {
+ get
+ {
+ switch (Name)
+ {
+ case Constants.Windows.Locale.Name.PortugueseBrazil:
+ return "Português (BR)";
+
+ case Constants.Windows.Locale.Name.PortuguesePortugal:
+ return "Português (PT)";
+
+ default:
+ return NativeName;
+ }
+ }
+ }
+
///
/// Gets the english name.
///
@@ -134,7 +164,7 @@ public override int GetHashCode()
///
public override string ToString()
{
- return EnglishName == NativeName ? EnglishName : string.Format(Localizer.Culture, "{0} ({1})", DisplayEnglishName, NativeName);
+ return EnglishName == NativeName ? EnglishName : string.Format(Localizer.Culture, "{0} ({1})", DisplayEnglishName, DisplayNativeName);
}
}
}
diff --git a/src/Model/Localization.cs b/src/Model/Localization.cs
index 334808da..ac54ff24 100644
--- a/src/Model/Localization.cs
+++ b/src/Model/Localization.cs
@@ -18,12 +18,13 @@ public class Localization
private string _dangerLevel, _donate, _donationMessage, _donationTitle;
private string _error, _errorAdminPrivilegeRequired, _errorCanNotSaveLog, _errorMemoryAreaOptimizationNotSupported, _everyHour, _exit, _expand;
private string _free;
+ private string _garbageCollector;
private string _help, _hotkeyIsInUseByOperatingSystem;
private string _invalid;
private string _lowMemory;
private string _manual, _memoryAreas, _memoryOptimized, _memoryUsage, _minimize, _modifiedFileCache, _modifiedPageList;
private string _no;
- private string _optimizationHotkey, _optimize, _optimized;
+ private string _optimizationHotkey, _optimize, _optimizeOnMiddleMouseClick, _optimizing;
private string _physicalMemory, _processExclusionList;
private string _reason, _registryCache, _remove, _reset, _resetConfirmation, _runOnLowPriority, _runOnStartup;
private string _schedule, _seconds, _securityWarning, _settings, _showMemoryUsage, _showOptimizationNotifications, _showVirtualMemory, _standbyList, _standbyListLowPriority, _startMinimized, _systemFileCache;
@@ -205,6 +206,13 @@ public string Free
private set { _free = value.Capitalize(); }
}
+ [DataMember]
+ public string GarbageCollector
+ {
+ get { return _garbageCollector; }
+ private set { _garbageCollector = value.Capitalize(); }
+ }
+
[DataMember]
public string Help
{
@@ -304,10 +312,17 @@ public string Optimize
}
[DataMember]
- public string Optimized
+ public string OptimizeOnMiddleMouseClick
+ {
+ get { return _optimizeOnMiddleMouseClick; }
+ private set { _optimizeOnMiddleMouseClick = value.Capitalize(); }
+ }
+
+ [DataMember]
+ public string Optimizing
{
- get { return _optimized; }
- private set { _optimized = value.Capitalize(); }
+ get { return _optimizing; }
+ private set { _optimizing = value.Capitalize(); }
}
[DataMember]
diff --git a/src/Model/Log/Log.cs b/src/Model/Log/Log.cs
index d2cfbf9e..96fe19b0 100644
--- a/src/Model/Log/Log.cs
+++ b/src/Model/Log/Log.cs
@@ -1,13 +1,13 @@
using System;
+using System.Globalization;
using System.Runtime.CompilerServices;
-using System.Text;
namespace WinMemoryCleaner
{
///
/// Log Item
///
- public class Log
+ public class Log : IJsonSerializable
{
///
/// Initializes a new instance of the class.
@@ -27,20 +27,20 @@ public Log(Enums.Log.Levels level, string message, ILogData data = null, [Caller
}
///
- /// Gets or sets the level.
+ /// Gets the data.
///
///
- /// The level.
+ /// The data.
///
- public Enums.Log.Levels Level { get; private set; }
+ public ILogData Data { get; private set; }
///
- /// Gets or sets the method.
+ /// Gets or sets the level.
///
///
- /// The method.
+ /// The level.
///
- public string Method { get; private set; }
+ public Enums.Log.Levels Level { get; private set; }
///
/// Gets or sets the message.
@@ -51,12 +51,12 @@ public Log(Enums.Log.Levels level, string message, ILogData data = null, [Caller
public string Message { get; private set; }
///
- /// Gets the data.
+ /// Gets or sets the method.
///
///
- /// The data.
+ /// The method.
///
- public ILogData Data { get; private set; }
+ public string Method { get; private set; }
///
/// Gets or sets the timestamp.
@@ -67,30 +67,29 @@ public Log(Enums.Log.Levels level, string message, ILogData data = null, [Caller
public DateTimeOffset Timestamp { get; private set; }
///
- /// Converts to string.
+ /// Converts the log to a JSON-serializable object.
///
- ///
- /// A that represents this instance.
- ///
- public override string ToString()
+ /// An anonymous object ready for JSON serialization.
+ public object ToJson()
{
- var sb = new StringBuilder();
- var space = " ";
+ var jsonSerializable = Data as IJsonSerializable;
+ var data = jsonSerializable != null ? jsonSerializable.ToJson() : null;
- sb.Append('{').Append(Environment.NewLine);
- sb.Append(space).Append("\"timestamp\": \"").Append(Timestamp.ToString("yyyy-MM-ddTHH:mm:ss.fffK", Localizer.Culture)).Append("\",").Append(Environment.NewLine);
- sb.Append(space).Append("\"level\": \"").Append(Level.ToString()).Append("\",").Append(Environment.NewLine);
- sb.Append(space).Append("\"method\": \"").Append(Method).Append("\",").Append(Environment.NewLine);
- sb.Append(space).Append("\"message\": \"").Append(Message);
+ var level = Level.ToString();
+ var timestamp = Timestamp.ToString("yyyy-MM-dd'T'HH:mm:ss.fffK", CultureInfo.InvariantCulture);
- if (Data != null)
- sb.Append("\",").Append(Environment.NewLine).Append(space).Append("\"data\": ").Append(Data.ToString()).Append(Environment.NewLine);
- else
- sb.Append('\"').Append(Environment.NewLine);
-
- sb.Append('}');
+ return data == null
+ ? new { timestamp, level, method = Method, message = Message, data = (object)null }
+ : (object)new { timestamp, level, method = Method, message = Message, data };
+ }
- return sb.ToString();
+ ///
+ /// Returns a human-readable string representation for debugging.
+ ///
+ /// A formatted string showing timestamp, level, and message.
+ public override string ToString()
+ {
+ return string.Format(Localizer.Culture, "[{0}] {1}: {2}", Timestamp.ToString("HH:mm:ss", Localizer.Culture), Level, Message);
}
}
}
diff --git a/src/Model/Log/LogOptimizationData.cs b/src/Model/Log/LogOptimizationData.cs
index 35021280..f692687b 100644
--- a/src/Model/Log/LogOptimizationData.cs
+++ b/src/Model/Log/LogOptimizationData.cs
@@ -1,14 +1,12 @@
-using System;
-using System.Collections.Generic;
+using System.Collections.Generic;
using System.Linq;
-using System.Text;
namespace WinMemoryCleaner
{
///
/// Log Optimization Data
///
- public class LogOptimizationData : ILogData
+ public class LogOptimizationData : ILogData, IJsonSerializable
{
///
/// Initializes a new instance of the class.
@@ -42,42 +40,36 @@ public LogOptimizationData()
///
public string Reason { get; set; }
- ///
- /// Converts to string.
- ///
- ///
- /// A that represents this instance.
- ///
- public override string ToString()
+ ///
+ /// Converts the log optimization data to a JSON-serializable object.
+ ///
+ /// An anonymous object ready for JSON serialization.
+ public object ToJson()
{
- var sb = new StringBuilder();
- var space = " ";
+ var memoryAreas = MemoryAreas
+ .OrderBy(m => m.Name)
+ .Select(m => string.IsNullOrEmpty(m.Error)
+ ? new { name = m.Name, duration = m.Duration }
+ : (object)new { name = m.Name, duration = m.Duration, error = m.Error })
+ .ToList();
- sb.Append('{').Append(Environment.NewLine);
- sb.Append(space).Append(space).Append("\"reason\": \"").Append(Reason).Append("\",").Append(Environment.NewLine);
- sb.Append(space).Append(space).Append("\"duration\": \"").Append(Duration).Append("\",").Append(Environment.NewLine);
- sb.Append(space).Append(space).Append("\"memoryAreas\": [").Append(Environment.NewLine);
-
- foreach (var memoryArea in MemoryAreas.OrderBy(memoryArea => memoryArea.Name))
+ return new
{
- sb.Append(space).Append(space).Append(space).Append("{ ");
- sb.Append("\"name\": \"").Append(memoryArea.Name);
- sb.Append("\", \"duration\": \"").Append(memoryArea.Duration);
-
- if (memoryArea.Error != null)
- sb.Append("\", \"error\": \"").Append(memoryArea.Error);
-
- sb.Append("\" }").Append(',').Append(Environment.NewLine);
- }
-
- if (MemoryAreas.Count > 0)
- sb.Length -= (1 + Environment.NewLine.Length);
-
- sb.Append(Environment.NewLine);
- sb.Append(space).Append(space).Append(']').Append(Environment.NewLine);
- sb.Append(space).Append('}');
+ reason = Reason,
+ duration = Duration,
+ memoryAreas
+ };
+ }
- return sb.ToString();
+ ///
+ /// Converts to string.
+ ///
+ ///
+ /// A that represents this instance.
+ ///
+ public override string ToString()
+ {
+ return string.Format(Localizer.Culture, "{0} ({1}) - {2} area(s)", Reason, Duration, MemoryAreas != null ? MemoryAreas.Count : 0);
}
}
}
diff --git a/src/Model/ObservableItem.cs b/src/Model/ObservableItem.cs
index 8dd65acf..0b5fe13f 100644
--- a/src/Model/ObservableItem.cs
+++ b/src/Model/ObservableItem.cs
@@ -9,6 +9,8 @@ namespace WinMemoryCleaner
///
public class ObservableItem : ObservableObject
{
+ private bool _isEnabled;
+
///
/// Initializes a new instance of the class.
///
@@ -19,7 +21,7 @@ public class ObservableItem : ObservableObject
public ObservableItem(string name, Func getter, Action setter, bool isEnabled = true)
{
Getter = getter;
- IsEnabled = isEnabled;
+ _isEnabled = isEnabled;
Name = name;
Setter = setter;
}
@@ -33,12 +35,23 @@ public ObservableItem(string name, Func getter, Action setter, bool isEnab
public Func Getter { get; private set; }
///
- /// Gets a value indicating whether this instance is enabled.
+ /// Gets or sets a value indicating whether this instance is enabled.
///
///
/// true if this instance is enabled; otherwise, false.
///
- public bool IsEnabled { get; private set; }
+ public bool IsEnabled
+ {
+ get { return _isEnabled; }
+ set
+ {
+ if (_isEnabled != value)
+ {
+ _isEnabled = value;
+ RaisePropertyChanged();
+ }
+ }
+ }
///
/// Gets the name.
diff --git a/src/Properties/AssemblyInfo.cs b/src/Properties/AssemblyInfo.cs
index 97525ed0..14072e31 100644
--- a/src/Properties/AssemblyInfo.cs
+++ b/src/Properties/AssemblyInfo.cs
@@ -11,7 +11,7 @@
[assembly: AssemblyKeyFile(Constants.App.KeyFile)]
[assembly: AssemblyProduct(Constants.App.Name)]
[assembly: AssemblyTitle(Constants.App.Title)]
-[assembly: AssemblyVersion("3.0.2.0")]
+[assembly: AssemblyVersion("3.0.3.0")]
[assembly: ComVisible(false)]
[assembly: CLSCompliant(true)]
[assembly: Guid(Constants.App.Id)]
diff --git a/src/Resources/Localization/Albanian.json b/src/Resources/Localization/Albanian.json
index 0c869006..b0ebe480 100644
--- a/src/Resources/Localization/Albanian.json
+++ b/src/Resources/Localization/Albanian.json
@@ -23,6 +23,7 @@
"Exit": "dil",
"Expand": "zgjero",
"Free": "falas",
+ "GarbageCollector": "mbledhës mbeturinash",
"Help": "ndihmë",
"HotkeyIsInUseByOperatingSystem": "tasti i shkurtër ({0}) është në përdorim nga sistemi operativ",
"Invalid": "e pavlefshme",
@@ -37,7 +38,8 @@
"No": "jo",
"OptimizationHotkey": "tasti kryesor i optimizimit",
"Optimize": "optimizo",
- "Optimized": "e optimizuar",
+ "OptimizeOnMiddleMouseClick": "optimizo me klikimin e butonit të mesëm të miut",
+ "Optimizing": "duke optimizuar",
"PhysicalMemory": "memorie fizike",
"ProcessExclusionList": "proceset e përjashtuara nga optimizimi",
"Reason": "arsyeja",
diff --git a/src/Resources/Localization/Arabic.json b/src/Resources/Localization/Arabic.json
index f56c731e..9d838697 100644
--- a/src/Resources/Localization/Arabic.json
+++ b/src/Resources/Localization/Arabic.json
@@ -3,13 +3,13 @@
"Add": "إضافة",
"AlwaysOnTop": "دائما في الأعلى",
"AutoOptimization": "التحسين التلقائي",
- "AutoOptimizationInterval": "الفاصل الزمني بين تحسين الذاكرة التلقائي هو {0} دقائق",
+ "AutoOptimizationInterval": "الفاصل الزمني للتحسين التلقائي للذاكرة هو {0} دقيقة",
"AutoUpdate": "التحديث التلقائي",
"Background": "الخلفية",
"Close": "إغلاق",
"CloseAfterOptimization": "إغلاق بعد التحسين",
- "CloseToTheNotificationArea": "بالقرب من منطقة الإعلام",
- "Collapse": "توسيع",
+ "CloseToTheNotificationArea": "الإغلاق إلى منطقة الإشعارات",
+ "Collapse": "طي",
"CombinedPageList": "قائمة الصفحات المجمعة",
"DangerLevel": "مستوى الخطر",
"Donate": "تبرع",
@@ -21,15 +21,16 @@
"ErrorMemoryAreaOptimizationNotSupported": "تحسين {0} غير مدعوم في إصدار نظام التشغيل هذا",
"EveryHour": "كل {0} ساعة",
"Exit": "خروج",
- "Expand": "الانهيار",
- "Free": "تفريغ",
+ "Expand": "توسيع",
+ "Free": "متاحة",
+ "GarbageCollector": "جامع القمامة",
"Help": "مساعدة",
"HotkeyIsInUseByOperatingSystem": "مفتاح التشغيل السريع ({0}) قيد الاستخدام بواسطة نظام التشغيل",
"Invalid": "غير صالح",
"LowMemory": "ذاكرة منخفضة",
"Manual": "يدوي",
"MemoryAreas": "مناطق الذاكرة",
- "MemoryOptimized": "الذاكرة المحسنة",
+ "MemoryOptimized": "تم تحسين الذاكرة",
"MemoryUsage": "استخدام الذاكرة",
"Minimize": "تصغير",
"ModifiedFileCache": "ذاكرة التخزين المؤقت للملفات المعدلة",
@@ -37,7 +38,8 @@
"No": "لا",
"OptimizationHotkey": "مفتاح التشغيل السريع للتحسين",
"Optimize": "تحسين",
- "Optimized": "تم التحسين",
+ "OptimizeOnMiddleMouseClick": "التحسين عند النقر بزر الماوس الأوسط",
+ "Optimizing": "جارٍ التحسين",
"PhysicalMemory": "ذاكرة فعلية",
"ProcessExclusionList": "العمليات المستبعدة من التحسين",
"Reason": "السبب",
@@ -54,17 +56,17 @@
"ShowMemoryUsage": "إظهار استخدام الذاكرة",
"ShowOptimizationNotifications": "إظهار إشعارات التحسين",
"ShowVirtualMemory": "إظهار الذاكرة الافتراضية",
- "StandbyList": "قائمة الانتظار",
- "StandbyListLowPriority": "قائمة الانتظار (أولوية منخفضة)",
- "StartMinimized": "ابدأ مصغرًا",
+ "StandbyList": "قائمة الاستعداد",
+ "StandbyListLowPriority": "قائمة الاستعداد (أولوية منخفضة)",
+ "StartMinimized": "بدء التشغيل مصغّرًا",
"SystemFileCache": "ذاكرة التخزين المؤقت لملفات النظام",
"Text": "نص",
- "TrayIcon": "أيقونة صينية",
+ "TrayIcon": "أيقونة منطقة الإشعارات",
"UpdatedToVersion": "تم التحديث إلى الإصدار {0}",
"Used": "مستخدمة",
"UseTransparentBackground": "استخدم خلفية شفافة",
"VirtualMemory": "ذاكرة افتراضية",
- "WhenFreePhysicalMemoryIsBelow": "عندما تكون الذاكرة الفيزيائية الحرة أقل من {0}%",
+ "WhenFreePhysicalMemoryIsBelow": "عندما تكون الذاكرة الفعلية الحرة أقل من {0}%",
"WarningLevel": "مستوى التحذير",
"WorkingSet": "مجموعة العمل",
"Yes": "نعم"
diff --git a/src/Resources/Localization/Bulgarian.json b/src/Resources/Localization/Bulgarian.json
index 8a3b8054..38025258 100644
--- a/src/Resources/Localization/Bulgarian.json
+++ b/src/Resources/Localization/Bulgarian.json
@@ -23,6 +23,7 @@
"Exit": "изход",
"Expand": "разшири",
"Free": "свободна",
+ "GarbageCollector": "боклукчия",
"Help": "помощ",
"HotkeyIsInUseByOperatingSystem": "клавишът ({0}) е зает от операционната система",
"Invalid": "невалиден",
@@ -37,7 +38,8 @@
"No": "не",
"OptimizationHotkey": "клавиш за оптимизиране",
"Optimize": "оптимизирай",
- "Optimized": "оптимизиран",
+ "OptimizeOnMiddleMouseClick": "оптимизирай при щракване със средния бутон на мишката",
+ "Optimizing": "оптимизиране",
"PhysicalMemory": "физическа памет",
"ProcessExclusionList": "процесът бе изключен от оптимизацията",
"Reason": "причина",
diff --git a/src/Resources/Localization/Chinese (Simplified).json b/src/Resources/Localization/Chinese (Simplified).json
index 697af42f..8320e075 100644
--- a/src/Resources/Localization/Chinese (Simplified).json
+++ b/src/Resources/Localization/Chinese (Simplified).json
@@ -23,6 +23,7 @@
"Exit": "退出",
"Expand": "展开",
"Free": "可用",
+ "GarbageCollector": "垃圾回收器",
"Help": "帮助",
"HotkeyIsInUseByOperatingSystem": "操作系统正在使用热键 ({0})",
"Invalid": "无效的",
@@ -37,7 +38,8 @@
"No": "否",
"OptimizationHotkey": "优化热键",
"Optimize": "优化",
- "Optimized": "已优化",
+ "OptimizeOnMiddleMouseClick": "鼠标中键单击时优化",
+ "Optimizing": "优化中",
"PhysicalMemory": "物理内存",
"ProcessExclusionList": "排除的进程",
"Reason": "原因",
diff --git a/src/Resources/Localization/Chinese (Traditional).json b/src/Resources/Localization/Chinese (Traditional).json
index fbf6012b..4a2a08b3 100644
--- a/src/Resources/Localization/Chinese (Traditional).json
+++ b/src/Resources/Localization/Chinese (Traditional).json
@@ -23,6 +23,7 @@
"Exit": "結束",
"Expand": "展開",
"Free": "可用",
+ "GarbageCollector": "垃圾回收器",
"Help": "幫助",
"HotkeyIsInUseByOperatingSystem": "作業系統正在使用快速鍵({0})",
"Invalid": "無效",
@@ -37,7 +38,8 @@
"No": "否",
"OptimizationHotkey": "最佳化快速鍵",
"Optimize": "最佳化",
- "Optimized": "已最佳化",
+ "OptimizeOnMiddleMouseClick": "滑鼠中鍵點擊時最佳化",
+ "Optimizing": "最佳化中",
"PhysicalMemory": "實體記憶體",
"ProcessExclusionList": "排除的處理程序",
"Reason": "原因",
diff --git a/src/Resources/Localization/Dutch.json b/src/Resources/Localization/Dutch.json
index e67840a7..9ac50902 100644
--- a/src/Resources/Localization/Dutch.json
+++ b/src/Resources/Localization/Dutch.json
@@ -23,6 +23,7 @@
"Exit": "sluiten",
"Expand": "uitvouwen",
"Free": "vrij",
+ "GarbageCollector": "garbage collector",
"Help": "help",
"HotkeyIsInUseByOperatingSystem": "sneltoets ({0}) wordt reeds gebruikt door het besturingssysteem",
"Invalid": "ongeldig",
@@ -37,7 +38,8 @@
"No": "nee",
"OptimizationHotkey": "optimalisatie sneltoets",
"Optimize": "optimaliseren",
- "Optimized": "geoptimaliseerd",
+ "OptimizeOnMiddleMouseClick": "optimaliseren bij middelste muisklik",
+ "Optimizing": "optimaliseren",
"PhysicalMemory": "fysiek geheugen",
"ProcessExclusionList": "processen uitgesloten van optimalisatie",
"Reason": "reden",
diff --git a/src/Resources/Localization/English.json b/src/Resources/Localization/English.json
index 817a1223..9b1ee81a 100644
--- a/src/Resources/Localization/English.json
+++ b/src/Resources/Localization/English.json
@@ -18,13 +18,14 @@
"Error": "error",
"ErrorAdminPrivilegeRequired": "this operation requires administrator privileges ({0})",
"ErrorCanNotSaveLog": "cannot save the log: {0} ({1})",
- "ErrorMemoryAreaOptimizationNotSupported": "the {0} optimization is not supported on this version of the operating system",
+ "ErrorMemoryAreaOptimizationNotSupported": "the memory area {0} optimization is not supported on this version of the operating system",
"EveryHour": "every {0}h",
"Exit": "exit",
"Expand": "expand",
"Free": "free",
+ "GarbageCollector": "garbage collector",
"Help": "help",
- "HotkeyIsInUseByOperatingSystem": "the hotkey ({0}) is currently in use by the operating system",
+ "HotkeyIsInUseByOperatingSystem": "the hotkey ({0}) is in use by the operating system",
"Invalid": "invalid",
"LowMemory": "low memory",
"Manual": "manual",
@@ -37,7 +38,8 @@
"No": "no",
"OptimizationHotkey": "optimization hotkey",
"Optimize": "optimize",
- "Optimized": "optimized",
+ "OptimizeOnMiddleMouseClick": "optimize on middle mouse click",
+ "Optimizing": "optimizing",
"PhysicalMemory": "physical memory",
"ProcessExclusionList": "processes excluded from optimization",
"Reason": "reason",
diff --git a/src/Resources/Localization/French.json b/src/Resources/Localization/French.json
index e0578f32..b679801d 100644
--- a/src/Resources/Localization/French.json
+++ b/src/Resources/Localization/French.json
@@ -23,6 +23,7 @@
"Exit": "quitter",
"Expand": "développer",
"Free": "libre",
+ "GarbageCollector": "collecteur d'ordures",
"Help": "aide",
"HotkeyIsInUseByOperatingSystem": "le raccourci clavier ({0}) est utilisé par le système d'exploitation",
"Invalid": "invalide",
@@ -37,7 +38,8 @@
"No": "non",
"OptimizationHotkey": "raccourci clavier d'optimisation",
"Optimize": "optimiser",
- "Optimized": "optimisé",
+ "OptimizeOnMiddleMouseClick": "optimiser avec le bouton central de la souris",
+ "Optimizing": "optimisation",
"PhysicalMemory": "mémoire physique",
"ProcessExclusionList": "processus exclus de l'optimisation",
"Reason": "raison",
diff --git a/src/Resources/Localization/German.json b/src/Resources/Localization/German.json
index 88c88e73..93607beb 100644
--- a/src/Resources/Localization/German.json
+++ b/src/Resources/Localization/German.json
@@ -23,6 +23,7 @@
"Exit": "beenden",
"Expand": "ausklappen",
"Free": "frei",
+ "GarbageCollector": "garbage collector",
"Help": "hilfe",
"HotkeyIsInUseByOperatingSystem": "hotkey ({0}) wird vom betriebssystem belegt",
"Invalid": "ungültig",
@@ -37,7 +38,8 @@
"No": "nein",
"OptimizationHotkey": "tastaturkürzel für optimierung",
"Optimize": "optimieren",
- "Optimized": "optimiert",
+ "OptimizeOnMiddleMouseClick": "optimieren bei mittlerem Mausklick",
+ "Optimizing": "optimierung läuft",
"PhysicalMemory": "physikalischer speicher",
"ProcessExclusionList": "von optimierung ignorierte prozesse",
"Reason": "grund",
diff --git a/src/Resources/Localization/Greek.json b/src/Resources/Localization/Greek.json
index d42cde98..45ef0201 100644
--- a/src/Resources/Localization/Greek.json
+++ b/src/Resources/Localization/Greek.json
@@ -23,6 +23,7 @@
"Exit": "έξοδος",
"Expand": "ανάπτυξη",
"Free": "ελεύθερη",
+ "GarbageCollector": "συλλέκτης απορριμμάτων",
"Help": "βοήθεια",
"HotkeyIsInUseByOperatingSystem": "το ειδικό πλήκτρο ({0}) χρησιμοποιείται από το λειτουργικό σύστημα",
"Invalid": "άκυρο",
@@ -37,7 +38,8 @@
"No": "όχι",
"OptimizationHotkey": "ειδικό πλήκτρο βελτιστοποίησης",
"Optimize": "βελτιστοποίηση",
- "Optimized": "βελτιστοποιήθηκε",
+ "OptimizeOnMiddleMouseClick": "βελτιστοποίηση με μεσαίο κλικ ποντικιού",
+ "Optimizing": "βελτιστοποίηση",
"PhysicalMemory": "φυσική μνήμη",
"ProcessExclusionList": "διεργασίες που εξαιρέθηκαν από τη βελτιστοποίηση",
"Reason": "αιτία",
diff --git a/src/Resources/Localization/Hebrew.json b/src/Resources/Localization/Hebrew.json
index 0c56f8ff..76d29c9e 100644
--- a/src/Resources/Localization/Hebrew.json
+++ b/src/Resources/Localization/Hebrew.json
@@ -23,6 +23,7 @@
"Exit": "יציאה",
"Expand": "הרחב",
"Free": "פנוי",
+ "GarbageCollector": "אספן זבל",
"Help": "עזרה",
"HotkeyIsInUseByOperatingSystem": "מקש קיצור ({0}) נמצא בשימוש על ידי מערכת ההפעלה",
"Invalid": "לא חוקי",
@@ -37,7 +38,8 @@
"No": "לא",
"OptimizationHotkey": "מקש קיצור אופטימיזציה",
"Optimize": "בצע אופטימיזציה",
- "Optimized": "אופטימיזציה",
+ "OptimizeOnMiddleMouseClick": "אופטימיזציה בלחיצה על לחצן אמצעי של העכבר",
+ "Optimizing": "מבצע אופטימיזציה",
"PhysicalMemory": "זיכרון פיזי",
"ProcessExclusionList": "תהליכים שלא נכללו באופטימיזציה",
"Reason": "סיבה",
diff --git a/src/Resources/Localization/Hungarian.json b/src/Resources/Localization/Hungarian.json
index eb78d178..f5d60a72 100644
--- a/src/Resources/Localization/Hungarian.json
+++ b/src/Resources/Localization/Hungarian.json
@@ -23,6 +23,7 @@
"Exit": "kilépés",
"Expand": "kibontás",
"Free": "szabad memória",
+ "GarbageCollector": "szemétgyűjtő",
"Help": "súgó",
"HotkeyIsInUseByOperatingSystem": "a gyorsbillentyűt ({0}) jelenleg a rendszer használja",
"Invalid": "érvénytelen",
@@ -37,7 +38,8 @@
"No": "nem",
"OptimizationHotkey": "optimalizálás gyorsbillentyű",
"Optimize": "optimalizálás",
- "Optimized": "optimalizált",
+ "OptimizeOnMiddleMouseClick": "optimalizálás középső egérgombra kattintáskor",
+ "Optimizing": "optimalizálás folyamatban",
"PhysicalMemory": "fizikai memória",
"ProcessExclusionList": "az optimalizálásból kizárt folyamatok",
"Reason": "ok",
diff --git a/src/Resources/Localization/Indonesian.json b/src/Resources/Localization/Indonesian.json
index ff4ce52d..adedac7b 100644
--- a/src/Resources/Localization/Indonesian.json
+++ b/src/Resources/Localization/Indonesian.json
@@ -23,6 +23,7 @@
"Exit": "keluar",
"Expand": "perluas",
"Free": "sisa",
+ "GarbageCollector": "pengumpul sampah",
"Help": "bantuan",
"HotkeyIsInUseByOperatingSystem": "kombinasi ({0}) telah digunakan oleh sistem operasi",
"Invalid": "tidak sah",
@@ -37,7 +38,8 @@
"No": "tidak",
"OptimizationHotkey": "tombol pintas optimalisasi",
"Optimize": "optimalkan",
- "Optimized": "teroptimalkan",
+ "OptimizeOnMiddleMouseClick": "optimalkan dengan klik tombol tengah mouse",
+ "Optimizing": "mengoptimalkan",
"PhysicalMemory": "memori fisik",
"ProcessExclusionList": "daftar pengecualian proses",
"Reason": "alasan",
diff --git a/src/Resources/Localization/Irish.json b/src/Resources/Localization/Irish.json
index dd7453e3..5388c63a 100644
--- a/src/Resources/Localization/Irish.json
+++ b/src/Resources/Localization/Irish.json
@@ -23,6 +23,7 @@
"Exit": "scoir",
"Expand": "leathnaigh",
"Free": "saor",
+ "GarbageCollector": "bailitheoir bruscair",
"Help": "cabhair",
"HotkeyIsInUseByOperatingSystem": "tá hotkey ({0}) in úsáid ag an gcóras oibriúcháin",
"Invalid": "neamhbhailí",
@@ -37,7 +38,8 @@
"No": "níl",
"OptimizationHotkey": "optamaithe hotkey",
"Optimize": "optamaigh",
- "Optimized": "optamaithe",
+ "OptimizeOnMiddleMouseClick": "optamaigh ar chliceáil lárionad na luiche",
+ "Optimizing": "ag optamú",
"PhysicalMemory": "cuimhne fhisiciúil",
"ProcessExclusionList": "próisis eisiata ó bharrfheabhsú",
"Reason": "cúis",
diff --git a/src/Resources/Localization/Italian.json b/src/Resources/Localization/Italian.json
index 0eecdb98..80d30243 100644
--- a/src/Resources/Localization/Italian.json
+++ b/src/Resources/Localization/Italian.json
@@ -23,6 +23,7 @@
"Exit": "esci",
"Expand": "espandi",
"Free": "libera",
+ "GarbageCollector": "garbage collector",
"Help": "aiuto",
"HotkeyIsInUseByOperatingSystem": "il tasto di scelta rapida ({0}) è utilizzato dal sistema operativo",
"Invalid": "non valido",
@@ -37,7 +38,8 @@
"No": "no",
"OptimizationHotkey": "tasto di scelta rapida per l'ottimizzazione",
"Optimize": "ottimizza",
- "Optimized": "ottimizzato",
+ "OptimizeOnMiddleMouseClick": "ottimizza al clic del pulsante centrale del mouse",
+ "Optimizing": "ottimizzazione",
"PhysicalMemory": "memoria fisica",
"ProcessExclusionList": "processi esclusi dall'ottimizzazione",
"Reason": "motivo",
diff --git a/src/Resources/Localization/Japanese.json b/src/Resources/Localization/Japanese.json
index a1772c48..194a1de9 100644
--- a/src/Resources/Localization/Japanese.json
+++ b/src/Resources/Localization/Japanese.json
@@ -23,6 +23,7 @@
"Exit": "終了",
"Expand": "展開",
"Free": "フリー",
+ "GarbageCollector": "ガベージ コレクター",
"Help": "ヘルプ",
"HotkeyIsInUseByOperatingSystem": "ホットキー ({0}) は os で使用されています",
"Invalid": "無効",
@@ -37,7 +38,8 @@
"No": "いいえ",
"OptimizationHotkey": "最適化ホットキー",
"Optimize": "最適化",
- "Optimized": "最適化済",
+ "OptimizeOnMiddleMouseClick": "マウスの中央ボタンのクリックで最適化",
+ "Optimizing": "最適化中",
"PhysicalMemory": "物理メモリ",
"ProcessExclusionList": "最適化から除外したプロセス",
"Reason": "理由",
diff --git a/src/Resources/Localization/Korean.json b/src/Resources/Localization/Korean.json
index 278ed26c..d93731b0 100644
--- a/src/Resources/Localization/Korean.json
+++ b/src/Resources/Localization/Korean.json
@@ -19,10 +19,11 @@
"ErrorAdminPrivilegeRequired": "이 작업을 수행하려면 관리자 권한({0})이 필요합니다",
"ErrorCanNotSaveLog": "로그: {0} (예외: {1})를 저장할 수 없습니다)",
"ErrorMemoryAreaOptimizationNotSupported": "이 운영 체제 버전에서는 {0} 최적화가 지원되지 않습니다.",
- "EveryHour": "매번 {0}h",
+ "EveryHour": "매 {0}시간마다",
"Exit": "종료",
"Expand": "펼치기",
"Free": "여유",
+ "GarbageCollector": "가비지 컬렉터",
"Help": "도움말",
"HotkeyIsInUseByOperatingSystem": "운영 체제에서 단축키({0})를 사용하고 있습니다",
"Invalid": "무효",
@@ -37,7 +38,8 @@
"No": "아니요",
"OptimizationHotkey": "최적화 단축키",
"Optimize": "최적화",
- "Optimized": "최적화됨",
+ "OptimizeOnMiddleMouseClick": "마우스 가운데 버튼 클릭 시 최적화",
+ "Optimizing": "최적화 중",
"PhysicalMemory": "물리적 메모리",
"ProcessExclusionList": "최적화에서 제외된 프로세스",
"Reason": "이유",
diff --git a/src/Resources/Localization/Macedonian.json b/src/Resources/Localization/Macedonian.json
index aedffad0..8a50f8cc 100644
--- a/src/Resources/Localization/Macedonian.json
+++ b/src/Resources/Localization/Macedonian.json
@@ -23,6 +23,7 @@
"Exit": "излез",
"Expand": "прошири",
"Free": "слободно",
+ "GarbageCollector": "собирач на ѓубре",
"Help": "помош",
"HotkeyIsInUseByOperatingSystem": "копчето ({0}) се користи од оперативниот систем",
"Invalid": "неважечки",
@@ -37,7 +38,8 @@
"No": "не",
"OptimizationHotkey": "копчето за оптимизација",
"Optimize": "оптимизирајте",
- "Optimized": "оптимизирано",
+ "OptimizeOnMiddleMouseClick": "оптимизирајте со кликнување на средното копче на глушецот",
+ "Optimizing": "оптимизирање",
"PhysicalMemory": "физичка меморија",
"ProcessExclusionList": "процеси исклучени од оптимизација",
"Reason": "причина",
diff --git a/src/Resources/Localization/Norwegian.json b/src/Resources/Localization/Norwegian.json
index 8e021fac..f3cda28b 100644
--- a/src/Resources/Localization/Norwegian.json
+++ b/src/Resources/Localization/Norwegian.json
@@ -23,6 +23,7 @@
"Exit": "avslutt",
"Expand": "utvid",
"Free": "ledig",
+ "GarbageCollector": "søppelsamler",
"Help": "hjelp",
"HotkeyIsInUseByOperatingSystem": "hurtigtast ({0}) er i bruk av operativsystemet",
"Invalid": "ugyldig",
@@ -37,7 +38,8 @@
"No": "nei",
"OptimizationHotkey": "hurtigtast for optimalisering",
"Optimize": "optimaliser",
- "Optimized": "optimalisert",
+ "OptimizeOnMiddleMouseClick": "optimaliser ved klikk på midterste museknapp",
+ "Optimizing": "optimaliserer",
"PhysicalMemory": "fysisk minne",
"ProcessExclusionList": "prosesser ekskludert fra optimalisering",
"Reason": "årsak",
diff --git a/src/Resources/Localization/Persian.json b/src/Resources/Localization/Persian.json
index 9c2ab68f..91582853 100644
--- a/src/Resources/Localization/Persian.json
+++ b/src/Resources/Localization/Persian.json
@@ -19,10 +19,11 @@
"ErrorAdminPrivilegeRequired": "این عملیات به امتیازات مدیر نیاز دارد ({0})",
"ErrorCanNotSaveLog": "امکان ذخیره لاگ وجود ندارد: {0} (استثنا: {1})",
"ErrorMemoryAreaOptimizationNotSupported": "بهینهسازی {0} در این نسخه از سیستم عامل پشتیبانی نمیشود",
- "EveryHour": "هر {0}h",
+ "EveryHour": "هر {0}س",
"Exit": "خروج",
"Expand": "گسترش",
"Free": "آزاد",
+ "GarbageCollector": "جمعکننده زباله",
"Help": "راهنما",
"HotkeyIsInUseByOperatingSystem": "کلید میانبر ({0}) توسط سیستم عامل استفاده میشود",
"Invalid": "نامعتبر",
@@ -37,7 +38,8 @@
"No": "خیر",
"OptimizationHotkey": "کلید میانبر بهینهسازی",
"Optimize": "بهینهسازی",
- "Optimized": "بهینه شد",
+ "OptimizeOnMiddleMouseClick": "بهینهسازی با کلیک دکمه میانی ماوس",
+ "Optimizing": "در حال بهینهسازی",
"PhysicalMemory": "حافظه فیزیکی",
"ProcessExclusionList": "فرآیندهای مستثنی از بهینهسازی",
"Reason": "دلیل",
diff --git a/src/Resources/Localization/Polish.json b/src/Resources/Localization/Polish.json
index 1cfe2224..d41d49cd 100644
--- a/src/Resources/Localization/Polish.json
+++ b/src/Resources/Localization/Polish.json
@@ -23,6 +23,7 @@
"Exit": "wyjdź",
"Expand": "rozwiń",
"Free": "wolna",
+ "GarbageCollector": "odśmiecacz pamięci",
"Help": "pomoc",
"HotkeyIsInUseByOperatingSystem": "skrót ({0}) jest już używany przez system operacyjny",
"Invalid": "nieprawidłowy",
@@ -37,7 +38,8 @@
"No": "nie",
"OptimizationHotkey": "skrót optymalizacji",
"Optimize": "optymalizuj",
- "Optimized": "zoptymalizowano",
+ "OptimizeOnMiddleMouseClick": "optymalizuj kliknięciem środkowego przycisku myszy",
+ "Optimizing": "optymalizacja",
"PhysicalMemory": "pamięć fizyczna",
"ProcessExclusionList": "procesy wyłączone z optymalizacji",
"Reason": "powód",
diff --git a/src/Resources/Localization/Portuguese.json b/src/Resources/Localization/Portuguese (Brazil).json
similarity index 76%
rename from src/Resources/Localization/Portuguese.json
rename to src/Resources/Localization/Portuguese (Brazil).json
index 0af63305..c6862304 100644
--- a/src/Resources/Localization/Portuguese.json
+++ b/src/Resources/Localization/Portuguese (Brazil).json
@@ -1,13 +1,13 @@
{
"About": "sobre",
"Add": "adicionar",
- "AlwaysOnTop": "sempre na frente",
+ "AlwaysOnTop": "sempre visível",
"AutoOptimization": "otimização automática",
- "AutoOptimizationInterval": "o intervalo entre as otimizações automáticas subsequentes de memória livre é de {0} minutos",
+ "AutoOptimizationInterval": "o intervalo entre otimizações automáticas subsequentes de memória livre é de {0} minutos",
"AutoUpdate": "atualização automática",
"Background": "plano de fundo",
"Close": "fechar",
- "CloseAfterOptimization": "fechar após a otimização",
+ "CloseAfterOptimization": "fechar após otimização",
"CloseToTheNotificationArea": "fechar para a área de notificação",
"Collapse": "recolher",
"CombinedPageList": "lista de páginas combinadas",
@@ -18,15 +18,16 @@
"Error": "erro",
"ErrorAdminPrivilegeRequired": "esta operação requer privilégios de administrador ({0})",
"ErrorCanNotSaveLog": "não é possível salvar o log: {0} ({1})",
- "ErrorMemoryAreaOptimizationNotSupported": "a otimização {0} não é suportada nesta versão do sistema operacional",
+ "ErrorMemoryAreaOptimizationNotSupported": "a otimização da área de memória {0} não é suportada nesta versão do sistema operacional",
"EveryHour": "a cada {0}h",
"Exit": "sair",
"Expand": "expandir",
"Free": "livre",
+ "GarbageCollector": "coletor de lixo",
"Help": "ajuda",
"HotkeyIsInUseByOperatingSystem": "a tecla de atalho ({0}) está atualmente em uso pelo sistema operacional",
"Invalid": "inválido",
- "LowMemory": "pouca memória",
+ "LowMemory": "memória baixa",
"Manual": "manual",
"MemoryAreas": "áreas de memória",
"MemoryOptimized": "memória otimizada",
@@ -35,9 +36,10 @@
"ModifiedFileCache": "cache de arquivos modificados",
"ModifiedPageList": "lista de páginas modificadas",
"No": "não",
- "OptimizationHotkey": "atalho de teclado para otimização",
+ "OptimizationHotkey": "tecla de atalho para otimização",
"Optimize": "otimizar",
- "Optimized": "otimizada",
+ "OptimizeOnMiddleMouseClick": "otimizar ao clicar no botão do meio do mouse",
+ "Optimizing": "otimizando",
"PhysicalMemory": "memória física",
"ProcessExclusionList": "processos excluídos da otimização",
"Reason": "motivo",
@@ -55,16 +57,16 @@
"ShowOptimizationNotifications": "mostrar notificações de otimização",
"ShowVirtualMemory": "mostrar memória virtual",
"StandbyList": "lista de espera",
- "StandbyListLowPriority": "lista de espera (baixa prioridade)",
+ "StandbyListLowPriority": "lista de espera (prioridade baixa)",
"StartMinimized": "iniciar minimizado",
"SystemFileCache": "cache de arquivos do sistema",
"Text": "texto",
- "TrayIcon": "ícone da bandeja",
- "UpdatedToVersion": "atualizado para versão {0}",
+ "TrayIcon": "ícone da área de notificação",
+ "UpdatedToVersion": "atualizado para a versão {0}",
"Used": "usada",
"UseTransparentBackground": "usar plano de fundo transparente",
"VirtualMemory": "memória virtual",
- "WhenFreePhysicalMemoryIsBelow": "quando a memória física livre for menor que {0}%",
+ "WhenFreePhysicalMemoryIsBelow": "quando a memória física livre estiver abaixo de {0}%",
"WarningLevel": "nível de aviso",
"WorkingSet": "conjunto de trabalho",
"Yes": "sim"
diff --git a/src/Resources/Localization/Portuguese (Portugal).json b/src/Resources/Localization/Portuguese (Portugal).json
new file mode 100644
index 00000000..bd1e280c
--- /dev/null
+++ b/src/Resources/Localization/Portuguese (Portugal).json
@@ -0,0 +1,73 @@
+{
+ "About": "sobre",
+ "Add": "adicionar",
+ "AlwaysOnTop": "sempre visível",
+ "AutoOptimization": "otimização automática",
+ "AutoOptimizationInterval": "o intervalo entre otimizações automáticas subsequentes de memória é de {0} minutos",
+ "AutoUpdate": "atualização automática",
+ "Background": "plano de fundo",
+ "Close": "fechar",
+ "CloseAfterOptimization": "fechar após otimização",
+ "CloseToTheNotificationArea": "fechar para a área de notificação",
+ "Collapse": "recolher",
+ "CombinedPageList": "lista de páginas combinadas",
+ "DangerLevel": "nível de perigo",
+ "Donate": "doar",
+ "DonationMessage": "se considera esta aplicação útil, por favor, considere fazer um donativo. a sua contribuição ajuda a manter o projeto ativo, otimizado e gratuito para todos.",
+ "DonationTitle": "apoie este projeto",
+ "Error": "erro",
+ "ErrorAdminPrivilegeRequired": "esta operação requer privilégios de administrador ({0})",
+ "ErrorCanNotSaveLog": "não é possível guardar o registo: {0} ({1})",
+ "ErrorMemoryAreaOptimizationNotSupported": "a otimização da área de memória {0} não é suportada nesta versão do sistema operativo",
+ "EveryHour": "a cada {0}h",
+ "Exit": "sair",
+ "Expand": "expandir",
+ "Free": "livre",
+ "GarbageCollector": "coletor de lixo",
+ "Help": "ajuda",
+ "HotkeyIsInUseByOperatingSystem": "a tecla de atalho ({0}) está a ser utilizada pelo sistema operativo",
+ "Invalid": "inválido",
+ "LowMemory": "memória baixa",
+ "Manual": "manual",
+ "MemoryAreas": "áreas de memória",
+ "MemoryOptimized": "memória otimizada",
+ "MemoryUsage": "uso de memória",
+ "Minimize": "minimizar",
+ "ModifiedFileCache": "cache de ficheiros modificados",
+ "ModifiedPageList": "lista de páginas modificadas",
+ "No": "não",
+ "OptimizationHotkey": "tecla de atalho para otimização",
+ "Optimize": "otimizar",
+ "OptimizeOnMiddleMouseClick": "otimizar ao clicar no botão do meio do rato",
+ "Optimizing": "a otimizar",
+ "PhysicalMemory": "memória física",
+ "ProcessExclusionList": "processos excluídos da otimização",
+ "Reason": "motivo",
+ "RegistryCache": "cache do registo",
+ "Remove": "remover",
+ "Reset": "repor",
+ "ResetConfirmation": "tem a certeza que deseja repor a configuração padrão?",
+ "RunOnLowPriority": "executar em baixa prioridade",
+ "RunOnStartup": "executar na inicialização",
+ "Schedule": "agendamento",
+ "Seconds": "segundos",
+ "SecurityWarning": "esta aplicação pode ter sido comprometida. será redirecionado para o repositório oficial para descarregar uma versão segura.",
+ "Settings": "definições",
+ "ShowMemoryUsage": "mostrar uso de memória",
+ "ShowOptimizationNotifications": "mostrar notificações de otimização",
+ "ShowVirtualMemory": "mostrar memória virtual",
+ "StandbyList": "lista de espera",
+ "StandbyListLowPriority": "lista de espera (prioridade baixa)",
+ "StartMinimized": "iniciar minimizado",
+ "SystemFileCache": "cache de ficheiros do sistema",
+ "Text": "texto",
+ "TrayIcon": "ícone da área de notificação",
+ "UpdatedToVersion": "atualizado para a versão {0}",
+ "Used": "usada",
+ "UseTransparentBackground": "usar plano de fundo transparente",
+ "VirtualMemory": "memória virtual",
+ "WhenFreePhysicalMemoryIsBelow": "quando a memória física livre estiver abaixo de {0}%",
+ "WarningLevel": "nível de aviso",
+ "WorkingSet": "conjunto de trabalho",
+ "Yes": "sim"
+}
diff --git a/src/Resources/Localization/Russian.json b/src/Resources/Localization/Russian.json
index 6300c763..7ca1c202 100644
--- a/src/Resources/Localization/Russian.json
+++ b/src/Resources/Localization/Russian.json
@@ -23,6 +23,7 @@
"Exit": "выход",
"Expand": "развернуть",
"Free": "свободно",
+ "GarbageCollector": "сборщик мусора",
"Help": "помощь",
"HotkeyIsInUseByOperatingSystem": "горячая клавиша ({0}) уже используется операционной системой",
"Invalid": "недопустимо",
@@ -37,7 +38,8 @@
"No": "нет",
"OptimizationHotkey": "горячая клавиша оптимизации",
"Optimize": "оптимизировать",
- "Optimized": "оптимизировано",
+ "OptimizeOnMiddleMouseClick": "оптимизация при нажатии средней кнопки мыши",
+ "Optimizing": "оптимизация",
"PhysicalMemory": "физическая память",
"ProcessExclusionList": "процессы, исключённые из оптимизации",
"Reason": "причина",
diff --git a/src/Resources/Localization/Serbian.json b/src/Resources/Localization/Serbian.json
index 3d2c1579..9fd00cf7 100644
--- a/src/Resources/Localization/Serbian.json
+++ b/src/Resources/Localization/Serbian.json
@@ -23,6 +23,7 @@
"Exit": "излаз",
"Expand": "прошири",
"Free": "слободно",
+ "GarbageCollector": "сакупљач смећа",
"Help": "помоћ",
"HotkeyIsInUseByOperatingSystem": "пречица ({0}) је већ у употреби од стране оперативног система",
"Invalid": "неважеће",
@@ -37,7 +38,8 @@
"No": "не",
"OptimizationHotkey": "пречица за оптимизацију",
"Optimize": "оптимизуј",
- "Optimized": "оптимизовано",
+ "OptimizeOnMiddleMouseClick": "оптимизација кликом на средњи тастер миша",
+ "Optimizing": "оптимизација",
"PhysicalMemory": "физичка меморија",
"ProcessExclusionList": "процеси искључени из оптимизације",
"Reason": "разлог",
diff --git a/src/Resources/Localization/Slovenian.json b/src/Resources/Localization/Slovenian.json
index 6521545e..46a4fb9b 100644
--- a/src/Resources/Localization/Slovenian.json
+++ b/src/Resources/Localization/Slovenian.json
@@ -23,6 +23,7 @@
"Exit": "izhod",
"Expand": "razširi",
"Free": "prosto",
+ "GarbageCollector": "zbiralnik smeti",
"Help": "pomoč",
"HotkeyIsInUseByOperatingSystem": "bližnjica ({0}) je že v uporabi v operacijskem sistemu",
"Invalid": "neveljavno",
@@ -37,7 +38,8 @@
"No": "ne",
"OptimizationHotkey": "bližnjica za optimizacijo",
"Optimize": "optimiziraj",
- "Optimized": "optimizirano",
+ "OptimizeOnMiddleMouseClick": "optimiziraj s klikom na srednji gumb miške",
+ "Optimizing": "optimiziranje",
"PhysicalMemory": "fizični pomnilnik",
"ProcessExclusionList": "procesi izključeni iz optimizacije",
"Reason": "razlog",
diff --git a/src/Resources/Localization/Spanish.json b/src/Resources/Localization/Spanish.json
index c0127b73..bb2aa370 100644
--- a/src/Resources/Localization/Spanish.json
+++ b/src/Resources/Localization/Spanish.json
@@ -23,6 +23,7 @@
"Exit": "salir",
"Expand": "expandir",
"Free": "libre",
+ "GarbageCollector": "recolector de basura",
"Help": "ayuda",
"HotkeyIsInUseByOperatingSystem": "la tecla de acceso rápido ({0}) está en uso por el sistema operativo",
"Invalid": "inválido",
@@ -37,7 +38,8 @@
"No": "no",
"OptimizationHotkey": "tecla de acceso rápido de optimización",
"Optimize": "optimizar",
- "Optimized": "optimizado",
+ "OptimizeOnMiddleMouseClick": "optimizar al hacer clic en el botón central del ratón",
+ "Optimizing": "optimizando",
"PhysicalMemory": "memoria física",
"ProcessExclusionList": "procesos excluidos de la optimización",
"Reason": "razón",
diff --git a/src/Resources/Localization/Thai.json b/src/Resources/Localization/Thai.json
index bddd7063..b766327f 100644
--- a/src/Resources/Localization/Thai.json
+++ b/src/Resources/Localization/Thai.json
@@ -23,6 +23,7 @@
"Exit": "ออก",
"Expand": "ขยาย",
"Free": "ว่าง",
+ "GarbageCollector": "ตัวรวบรวมขยะ",
"Help": "ช่วยเหลือ",
"HotkeyIsInUseByOperatingSystem": "ปุ่มลัด ({0}) ถูกใช้งานโดยระบบปฏิบัติการ",
"Invalid": "ไม่ถูกต้อง",
@@ -37,7 +38,8 @@
"No": "ไม่",
"OptimizationHotkey": "ปุ่มลัดสำหรับเพิ่มประสิทธิภาพ",
"Optimize": "เพิ่มประสิทธิภาพ",
- "Optimized": "เพิ่มประสิทธิภาพแล้ว",
+ "OptimizeOnMiddleMouseClick": "เพิ่มประสิทธิภาพเมื่อคลิกปุ่มกลางเมาส์",
+ "Optimizing": "กำลังเพิ่มประสิทธิภาพ",
"PhysicalMemory": "หน่วยความจำจริง",
"ProcessExclusionList": "กระบวนการที่ยกเว้นจากการเพิ่มประสิทธิภาพ",
"Reason": "เหตุผล",
diff --git a/src/Resources/Localization/Turkish.json b/src/Resources/Localization/Turkish.json
index 531d099c..677cc555 100644
--- a/src/Resources/Localization/Turkish.json
+++ b/src/Resources/Localization/Turkish.json
@@ -23,6 +23,7 @@
"Exit": "çıkış",
"Expand": "genişlet",
"Free": "boş",
+ "GarbageCollector": "çöp toplayıcı",
"Help": "yardım",
"HotkeyIsInUseByOperatingSystem": "kısayol tuşu ({0}) işletim sistemi tarafından kullanılıyor",
"Invalid": "geçersiz",
@@ -37,7 +38,8 @@
"No": "hayır",
"OptimizationHotkey": "optimizasyon kısayol tuşu",
"Optimize": "optimize et",
- "Optimized": "optimize edildi",
+ "OptimizeOnMiddleMouseClick": "orta fare düğmesine tıklanınca optimize et",
+ "Optimizing": "optimize ediliyor",
"PhysicalMemory": "fiziksel bellek",
"ProcessExclusionList": "optimizasyondan hariç tutulan işlemler",
"Reason": "sebep",
diff --git a/src/Resources/Localization/Ukrainian.json b/src/Resources/Localization/Ukrainian.json
index edea24dd..48f7f26e 100644
--- a/src/Resources/Localization/Ukrainian.json
+++ b/src/Resources/Localization/Ukrainian.json
@@ -23,6 +23,7 @@
"Exit": "вийти",
"Expand": "розгорнути",
"Free": "вільно",
+ "GarbageCollector": "збирач сміття",
"Help": "допомога",
"HotkeyIsInUseByOperatingSystem": "гаряча клавіша ({0}) вже використовується операційною системою",
"Invalid": "недійсний",
@@ -37,7 +38,8 @@
"No": "ні",
"OptimizationHotkey": "гаряча клавіша оптимізації",
"Optimize": "оптимізувати",
- "Optimized": "оптимізовано",
+ "OptimizeOnMiddleMouseClick": "оптимізувати при натисканні середньої кнопки миші",
+ "Optimizing": "оптимізація",
"PhysicalMemory": "фізична пам'ять",
"ProcessExclusionList": "процеси, виключені з оптимізації",
"Reason": "причина",
diff --git a/src/Service/ComputerService.cs b/src/Service/ComputerService.cs
index eff092ff..a4ca5319 100644
--- a/src/Service/ComputerService.cs
+++ b/src/Service/ComputerService.cs
@@ -413,6 +413,22 @@ public void Optimize(Enums.Memory.Optimization.Reason reason, Enums.Memory.Areas
}
}
+ // Garbage Collector
+ try
+ {
+ if (OnOptimizeProgressUpdate != null)
+ {
+ value++;
+ OnOptimizeProgressUpdate(value, Localizer.String.GarbageCollector);
+ }
+
+ App.ReleaseMemory();
+ }
+ catch
+ {
+ // ignored
+ }
+
// Log
try
{
@@ -435,24 +451,6 @@ public void Optimize(Enums.Memory.Optimization.Reason reason, Enums.Memory.Areas
{
// ignored
}
-
- // App
- try
- {
- App.ReleaseMemory();
- }
- catch
- {
- // ignored
- }
- finally
- {
- if (OnOptimizeProgressUpdate != null)
- {
- value++;
- OnOptimizeProgressUpdate(value, Localizer.String.Optimized);
- }
- }
}
///
diff --git a/src/Service/NotificationService.cs b/src/Service/NotificationService.cs
index f45825c0..69b5aaed 100644
--- a/src/Service/NotificationService.cs
+++ b/src/Service/NotificationService.cs
@@ -2,9 +2,11 @@
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Text;
+using System.Globalization;
using System.Reflection;
using System.Windows.Forms;
using System.Windows.Input;
+using System.Windows.Threading;
using Application = System.Windows.Application;
using Cursors = System.Windows.Input.Cursors;
@@ -17,8 +19,10 @@ public class NotificationService : INotificationService
{
#region Fields
+ private int _currentRotationAngle;
private readonly Icon _imageIcon;
private readonly NotifyIcon _notifyIcon;
+ private DispatcherTimer _rotationTimer;
#endregion
@@ -30,12 +34,46 @@ public class NotificationService : INotificationService
/// Notify Icon
public NotificationService(NotifyIcon notifyIcon)
{
+ _currentRotationAngle = 0;
_imageIcon = Icon.ExtractAssociatedIcon(Assembly.GetExecutingAssembly().Location);
_notifyIcon = notifyIcon;
Initialize();
}
+ ///
+ /// Initializes the notification service
+ ///
+ public void Initialize()
+ {
+ if (_notifyIcon == null)
+ return;
+
+ // Notification Areas (Menu)
+ _notifyIcon.ContextMenuStrip = new TrayIconContextMenuControl();
+
+ // Optimize
+ _notifyIcon.ContextMenuStrip.Items.Add(Localizer.String.Optimize, null, (sender, args) =>
+ {
+ var mainViewModel = DependencyInjection.Container.Resolve();
+
+ if (mainViewModel.OptimizeCommand.CanExecute(null))
+ mainViewModel.OptimizeCommand.Execute(null);
+ });
+
+ _notifyIcon.ContextMenuStrip.Items.Add(new ToolStripSeparator());
+
+ // Exit
+ _notifyIcon.ContextMenuStrip.Items.Add(Localizer.String.Exit, null, (sender, args) =>
+ {
+ App.Shutdown();
+ });
+
+ Update(new Memory());
+
+ _notifyIcon.Visible = true;
+ }
+
#endregion
#region IDisposable
@@ -57,28 +95,34 @@ protected virtual void Dispose(bool disposing)
{
if (disposing)
{
+ try
+ {
+ if (_rotationTimer != null)
+ _rotationTimer = null;
+ }
+ catch (Exception ex)
+ {
+ Logger.Debug(ex);
+ }
+
try
{
if (_imageIcon != null)
- {
_imageIcon.Dispose();
- }
}
- catch
+ catch (Exception ex)
{
- // ignored
+ Logger.Debug(ex);
}
try
{
if (_notifyIcon != null)
- {
_notifyIcon.Dispose();
- }
}
- catch
+ catch (Exception ex)
{
- // ignored
+ Logger.Debug(ex);
}
}
}
@@ -88,42 +132,299 @@ protected virtual void Dispose(bool disposing)
#region Methods
///
- /// Initializes
+ /// Cleans up the rotation timer resources and resets the rotation angle
///
- public void Initialize()
+ private void CleanupRotationTimer()
{
- if (_notifyIcon == null)
- return;
+ try
+ {
+ _currentRotationAngle = 0;
- // Notification Areas (Menu)
- _notifyIcon.ContextMenuStrip = new TrayIconContextMenuControl();
+ if (_rotationTimer != null)
+ {
+ _rotationTimer.Stop();
+ _rotationTimer.Tick -= OnRotationTimerTick;
+ _rotationTimer = null;
+ }
+ }
+ catch (Exception ex)
+ {
+ Logger.Debug(ex);
+ }
+ }
- // Optimize
- _notifyIcon.ContextMenuStrip.Items.Add(Localizer.String.Optimize, null, (sender, args) =>
+ ///
+ /// Gets the background brush color based on memory usage and optimization state
+ ///
+ /// The memory information
+ /// if set to true the system is optimizing
+ /// A solid brush with the appropriate background color
+ private Brush GetBackgroundBrush(Memory memory, bool isOptimizing)
+ {
+ try
{
- var mainViewModel = DependencyInjection.Container.Resolve();
+ if (Settings.TrayIconUseTransparentBackground)
+ return new SolidBrush(Color.Transparent);
- if (mainViewModel.OptimizeCommand.CanExecute(null))
- mainViewModel.OptimizeCommand.Execute(null);
- });
+ if (isOptimizing)
+ {
+ var solidBrush = Settings.TrayIconOptimizingColor as SolidBrush;
+ return new SolidBrush(solidBrush.Color);
+ }
- _notifyIcon.ContextMenuStrip.Items.Add(new ToolStripSeparator());
+ if (memory.Physical.Used.Percentage >= Settings.TrayIconDangerLevel)
+ {
+ var solidBrush = Settings.TrayIconDangerColor as SolidBrush;
+ return new SolidBrush(solidBrush.Color);
+ }
- // Exit
- _notifyIcon.ContextMenuStrip.Items.Add(Localizer.String.Exit, null, (sender, args) =>
+ if (memory.Physical.Used.Percentage >= Settings.TrayIconWarningLevel)
+ {
+ var solidBrush = Settings.TrayIconWarningColor as SolidBrush;
+ return new SolidBrush(solidBrush.Color);
+ }
+
+ var backgroundBrush = Settings.TrayIconBackgroundColor as SolidBrush;
+ return new SolidBrush(backgroundBrush.Color);
+ }
+ catch (Exception)
{
- App.Shutdown();
- });
+ return new SolidBrush(Color.Black);
+ }
+ }
- Update(new Memory());
+ ///
+ /// Gets the appropriate tray icon based on settings and current state
+ ///
+ /// The memory information
+ /// if set to true the system is optimizing
+ /// The tray icon to display
+ private Icon GetIcon(Memory memory, bool isOptimizing)
+ {
+ try
+ {
+ return Settings.TrayIconShowMemoryUsage ? GetMemoryUsageIcon(memory, isOptimizing) : GetImageIcon(isOptimizing);
+ }
+ catch
+ {
+ return _imageIcon;
+ }
+ }
- _notifyIcon.Visible = true;
+ ///
+ /// Gets the static application icon with optional rotation animation during optimization
+ ///
+ /// if set to true the system is optimizing
+ /// The application icon, optionally rotated
+ private Icon GetImageIcon(bool isOptimizing)
+ {
+ try
+ {
+ if (isOptimizing)
+ {
+ StartRotationAnimation();
+
+ if (_currentRotationAngle > 0)
+ return GetRotatedIcon(_imageIcon, _currentRotationAngle);
+ }
+ else
+ {
+ StopRotationAnimation();
+ }
+
+ return _imageIcon;
+ }
+ catch
+ {
+ return _imageIcon;
+ }
+ }
+
+ ///
+ /// Gets a custom icon displaying the current memory usage percentage
+ ///
+ /// The memory information
+ /// if set to true the system is optimizing
+ /// An icon with rendered memory percentage text
+ private Icon GetMemoryUsageIcon(Memory memory, bool isOptimizing)
+ {
+ try
+ {
+ using (var image = new Bitmap(16, 16))
+ using (var graphics = Graphics.FromImage(image))
+ using (var font = new Font("Consolas", 15F, FontStyle.Regular, GraphicsUnit.Pixel))
+ using (var format = new StringFormat())
+ using (var backgroundBrush = GetBackgroundBrush(memory, isOptimizing))
+ using (var textBrush = GetTextBrush(memory, isOptimizing))
+ {
+ // Configure format
+ format.Alignment = StringAlignment.Center;
+ format.LineAlignment = StringAlignment.Center;
+
+ // Configure graphics quality
+ graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
+ graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
+ graphics.SmoothingMode = SmoothingMode.HighQuality;
+ graphics.TextRenderingHint = TextRenderingHint.SingleBitPerPixelGridFit;
+
+ // Draw background
+ if (!Settings.TrayIconUseTransparentBackground)
+ {
+ using (var path = new GraphicsPath())
+ {
+ var radius = 10;
+
+ path.AddArc(0, 0, radius, radius, 180, 90);
+ path.AddArc(16 - radius, 0, radius, radius, 270, 90);
+ path.AddArc(16 - radius, 16 - radius, radius, radius, 0, 90);
+ path.AddArc(0, 16 - radius, radius, radius, 90, 90);
+ path.CloseFigure();
+
+ graphics.FillPath(backgroundBrush, path);
+ }
+ }
+
+ // Draw text
+ graphics.DrawString(string.Format(CultureInfo.InvariantCulture, "{0:00}", memory.Physical.Used.Percentage == 100 ? 99 : memory.Physical.Used.Percentage), font, textBrush, 8F, 9F, format);
+
+ var handle = image.GetHicon();
+
+ using (var icon = Icon.FromHandle(handle))
+ {
+ var clonedIcon = (Icon)icon.Clone();
+
+ NativeMethods.DestroyIcon(handle);
+
+ return clonedIcon;
+ }
+ }
+ }
+ catch
+ {
+ return _imageIcon;
+ }
+ }
+
+ ///
+ /// Gets a rotated version of the specified icon
+ ///
+ /// The icon to rotate
+ /// The rotation angle in degrees
+ /// A new icon rotated by the specified angle
+ private Icon GetRotatedIcon(Icon icon, float angle)
+ {
+ if (icon == null || angle == 0)
+ return icon;
+
+ try
+ {
+ using (var image = icon.ToBitmap())
+ using (var rotatedImage = new Bitmap(image.Width, image.Height))
+ using (var graphics = Graphics.FromImage(rotatedImage))
+ {
+ // Configure graphics quality
+ graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
+ graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
+ graphics.SmoothingMode = SmoothingMode.HighQuality;
+
+ // Rotate around center point
+ var centerX = image.Width / 2f;
+ var centerY = image.Height / 2f;
+
+ graphics.TranslateTransform(centerX, centerY);
+ graphics.RotateTransform(angle);
+ graphics.TranslateTransform(-centerX, -centerY);
+
+ graphics.DrawImage(image, new Point(0, 0));
+
+ var handle = rotatedImage.GetHicon();
+
+ using (var tempIcon = Icon.FromHandle(handle))
+ {
+ var clonedIcon = (Icon)tempIcon.Clone();
+
+ NativeMethods.DestroyIcon(handle);
+
+ return clonedIcon;
+ }
+ }
+ }
+ catch
+ {
+ return icon;
+ }
+ }
+
+ ///
+ /// Gets the tray icon tooltip text based on memory usage and optimization state
+ ///
+ /// The memory information
+ /// if set to true the system is optimizing
+ /// The formatted tooltip text
+ private string GetText(Memory memory, bool isOptimizing)
+ {
+ try
+ {
+ if (isOptimizing)
+ return Localizer.String.Optimizing.ToUpper(Localizer.Culture);
+
+ return Settings.ShowVirtualMemory
+ ? string.Format(Localizer.Culture, "{1}{0}{2}: {3}%{0}{4}: {5}%", Environment.NewLine, Localizer.String.MemoryUsage.ToUpper(Localizer.Culture), Localizer.String.PhysicalMemory, memory.Physical.Used.Percentage, Localizer.String.VirtualMemory, memory.Virtual.Used.Percentage)
+ : string.Format(Localizer.Culture, "{1}{0}{2}: {3}%", Environment.NewLine, Localizer.String.MemoryUsage.ToUpper(Localizer.Culture), Localizer.String.PhysicalMemory, memory.Physical.Used.Percentage);
+ }
+ catch
+ {
+ return Constants.App.Title;
+ }
+ }
+
+ ///
+ /// Gets the text brush color based on memory usage and optimization state
+ ///
+ /// The memory information
+ /// if set to true the system is optimizing
+ /// A solid brush with the appropriate text color
+ private Brush GetTextBrush(Memory memory, bool isOptimizing)
+ {
+ try
+ {
+ if (!Settings.TrayIconUseTransparentBackground)
+ {
+ var solidBrush = Settings.TrayIconTextColor as SolidBrush;
+ return new SolidBrush(solidBrush.Color);
+ }
+
+ if (isOptimizing)
+ {
+ var solidBrush = Settings.TrayIconOptimizingColor as SolidBrush;
+ return new SolidBrush(solidBrush.Color);
+ }
+
+ if (memory.Physical.Used.Percentage >= Settings.TrayIconDangerLevel)
+ {
+ var solidBrush = Settings.TrayIconDangerColor as SolidBrush;
+ return new SolidBrush(solidBrush.Color);
+ }
+
+ if (memory.Physical.Used.Percentage >= Settings.TrayIconWarningLevel)
+ {
+ var solidBrush = Settings.TrayIconWarningColor as SolidBrush;
+ return new SolidBrush(solidBrush.Color);
+ }
+
+ var textBrush = Settings.TrayIconTextColor as SolidBrush;
+ return new SolidBrush(textBrush.Color);
+ }
+ catch (Exception)
+ {
+ return new SolidBrush(Color.White);
+ }
}
///
- /// Show/Hide Loading
+ /// Shows or hides the loading cursor and enables/disables the context menu
///
- /// True (ON) / False (OFF)
+ /// if set to true shows loading cursor and disables menu
public void Loading(bool running)
{
if (Application.Current == null || Application.Current.Dispatcher == null)
@@ -134,136 +435,133 @@ public void Loading(bool running)
{
Mouse.OverrideCursor = running ? Cursors.Wait : null;
- if (_notifyIcon != null && _notifyIcon.ContextMenuStrip != null)
+ if (_notifyIcon.ContextMenuStrip != null)
_notifyIcon.ContextMenuStrip.Enabled = !running;
});
}
///
- /// Displays a Notification
- ///
- /// The text
- /// The title
- /// The time period, in seconds
- /// The icon
+ /// Displays a balloon tip notification from the system tray
+ /// ///
+ /// The notification message text
+ /// The notification title
+ /// The time period in seconds to display the notification
+ /// The notification icon type
public void Notify(string message, string title = null, int timeout = 5, Enums.Icon.Notification icon = Enums.Icon.Notification.None)
{
+ if (_notifyIcon == null)
+ return;
+
try
{
- if (_notifyIcon == null)
- return;
-
_notifyIcon.Visible = false;
_notifyIcon.Visible = true;
_notifyIcon.ShowBalloonTip(timeout * 1000, title, message, (ToolTipIcon)icon);
}
- catch
+ catch (Exception ex)
{
- // ignored
+ Logger.Debug(ex);
}
}
///
- /// Update Info
+ /// Handles the rotation timer tick event to animate the icon rotation
///
- /// The memory.
- public void Update(Memory memory)
+ /// The event sender
+ /// The event arguments
+ private void OnRotationTimerTick(object sender, EventArgs e)
{
- if (memory == null)
- throw new ArgumentNullException("memory");
+ try
+ {
+ _currentRotationAngle = (_currentRotationAngle + 90) % 360;
- if (_notifyIcon == null)
+ _notifyIcon.Icon = GetRotatedIcon(_imageIcon, _currentRotationAngle);
+ }
+ catch (Exception ex)
+ {
+ Logger.Debug(ex);
+ }
+ }
+
+ ///
+ /// Starts the icon rotation animation for the optimization state
+ ///
+ private void StartRotationAnimation()
+ {
+ if (_rotationTimer != null)
+ return;
+
+ if (Application.Current == null || Application.Current.Dispatcher == null)
return;
- // Icon
try
{
- if (Settings.TrayIconShowMemoryUsage)
+ Application.Current.Dispatcher.Invoke((Action)delegate
{
- using (var image = new Bitmap(16, 16))
- using (var graphics = Graphics.FromImage(image))
- using (var font = new Font("Arial", 14F, GraphicsUnit.Pixel))
- using (var format = new StringFormat())
- using (var path = new GraphicsPath())
+ try
{
- format.Alignment = StringAlignment.Center;
- format.LineAlignment = StringAlignment.Center;
-
- graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
- graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
- graphics.SmoothingMode = SmoothingMode.HighQuality;
- graphics.TextRenderingHint = TextRenderingHint.AntiAliasGridFit;
-
- var backgroundBrush = Settings.TrayIconBackgroundColor;
- var textBrush = Settings.TrayIconTextColor;
-
- if (Settings.TrayIconUseTransparentBackground)
- {
- backgroundBrush = Brushes.Transparent;
-
- if (memory.Physical.Used.Percentage >= Settings.TrayIconDangerLevel)
- {
- textBrush = Settings.TrayIconDangerColor;
- }
- else if (memory.Physical.Used.Percentage >= Settings.TrayIconWarningLevel)
- {
- textBrush = Settings.TrayIconWarningColor;
- }
- }
- else
- {
- if (memory.Physical.Used.Percentage >= Settings.TrayIconDangerLevel)
- {
- backgroundBrush = Settings.TrayIconDangerColor;
- }
- else if (memory.Physical.Used.Percentage >= Settings.TrayIconWarningLevel)
- {
- backgroundBrush = Settings.TrayIconWarningColor;
- }
- }
-
- var radius = 3;
-
- path.AddArc(0, 0, radius, radius, 180, 90);
- path.AddArc(16 - radius, 0, radius, radius, 270, 90);
- path.AddArc(16 - radius, 16 - radius, radius, radius, 0, 90);
- path.AddArc(0, 16 - radius, radius, radius, 90, 90);
- path.CloseFigure();
-
- graphics.FillPath(backgroundBrush, path);
- graphics.DrawString(string.Format(Localizer.Culture, "{0:00}", memory.Physical.Used.Percentage == 100 ? 99 : memory.Physical.Used.Percentage), font, textBrush, 9, 9, format);
+ _currentRotationAngle = 0;
- var handle = image.GetHicon();
-
- using (var icon = Icon.FromHandle(handle))
- _notifyIcon.Icon = (Icon)icon.Clone();
-
- NativeMethods.DestroyIcon(handle);
+ _rotationTimer = new DispatcherTimer { Interval = TimeSpan.FromMilliseconds(200) };
+ _rotationTimer.Tick += OnRotationTimerTick;
+ _rotationTimer.Start();
}
- }
- else
- _notifyIcon.Icon = _imageIcon;
+ catch (Exception ex)
+ {
+ Logger.Debug(ex);
+ }
+ });
}
- catch
+ catch (Exception ex)
{
- if (_notifyIcon != null)
- _notifyIcon.Icon = _imageIcon;
+ Logger.Debug(ex);
}
+ }
+
+ ///
+ /// Stops the icon rotation animation
+ ///
+ private void StopRotationAnimation()
+ {
+ if (_rotationTimer == null)
+ return;
- // Text
try
{
- _notifyIcon.Text = Settings.ShowVirtualMemory
- ? string.Format(Localizer.Culture, "{1}{0}{2}: {3}%{0}{4}: {5}%", Environment.NewLine, Localizer.String.MemoryUsage.ToUpper(Localizer.Culture), Localizer.String.PhysicalMemory, memory.Physical.Used.Percentage, Localizer.String.VirtualMemory, memory.Virtual.Used.Percentage)
- : string.Format(Localizer.Culture, "{1}{0}{2}: {3}%", Environment.NewLine, Localizer.String.MemoryUsage.ToUpper(Localizer.Culture), Localizer.String.PhysicalMemory, memory.Physical.Used.Percentage);
+ if (Application.Current == null || Application.Current.Dispatcher == null)
+ {
+ CleanupRotationTimer();
+ return;
+ }
+
+ Application.Current.Dispatcher.Invoke((Action)delegate
+ {
+ CleanupRotationTimer();
+ });
}
- catch
+ catch (Exception ex)
{
- if (_notifyIcon != null)
- _notifyIcon.Text = string.Empty;
+ Logger.Debug(ex);
}
+ }
+
+ ///
+ /// Updates the tray icon and tooltip text based on current memory usage and optimization state
+ ///
+ /// The memory information
+ /// if set to true the system is optimizing
+ /// memory
+ public void Update(Memory memory, bool isOptimizing = false)
+ {
+ if (memory == null)
+ throw new ArgumentNullException("memory");
+
+ if (_notifyIcon == null)
+ return;
+ _notifyIcon.Text = GetText(memory, isOptimizing);
+ _notifyIcon.Icon = GetIcon(memory, isOptimizing);
}
#endregion
diff --git a/src/View/Window/DonationWindow.xaml b/src/View/Window/DonationWindow.xaml
index 4f5815a9..7e70f794 100644
--- a/src/View/Window/DonationWindow.xaml
+++ b/src/View/Window/DonationWindow.xaml
@@ -129,7 +129,7 @@
@@ -209,7 +209,7 @@
diff --git a/src/View/Window/MainWindow.xaml b/src/View/Window/MainWindow.xaml
index 0d5a84de..fec35130 100644
--- a/src/View/Window/MainWindow.xaml
+++ b/src/View/Window/MainWindow.xaml
@@ -281,7 +281,6 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+ {
+ SetFocusTo(Optimize);
+ };
+
IsVisibleChanged += OnWindowVisibleChanged;
_viewModel = DataContext as MainViewModel;
@@ -38,7 +44,7 @@ public MainWindow()
_viewModel.OnLanguageChangeCompleted += Refresh;
_viewModel.OnNavigateUriCommandCompleted += OnNavigateUriCommandCompleted;
_viewModel.OnOptimizeCommandCompleted += OnOptimizeCommandCompleted;
- _viewModel.OnRemoveProcessFromExclusionListCommandCompleted += SetFocusToProcessExclusionList;
+ _viewModel.OnRemoveProcessFromExclusionListCommandCompleted += () => SetFocusTo(ProcessExclusionList);
}
// Slider
@@ -63,6 +69,9 @@ public MainWindow()
FontSizeSlider.AddHandler(PreviewMouseLeftButtonDownEvent, sliderPreviewMouseLeftButtonDownEvent, true);
}
+ ///
+ /// Closes the window to the notification area.
+ ///
private void CloseToTheNotificationArea()
{
if (Visibility == Visibility.Visible)
@@ -72,16 +81,24 @@ private void CloseToTheNotificationArea()
}
}
+ ///
+ /// Called when the add process to exclusion list command is completed.
+ ///
private void OnAddProcessToExclusionListCommandCompleted()
{
ProcessExclusionScrollViewer.ScrollToEnd();
}
+ ///
+ /// Called when the close button is clicked.
+ ///
+ /// The sender.
+ /// The instance containing the event data.
private void OnCloseButtonClick(object sender, RoutedEventArgs e)
{
if (Settings.CloseToTheNotificationArea)
{
- SetFocusToOptimizationButton();
+ SetFocusTo(Optimize);
CloseToTheNotificationArea();
@@ -91,25 +108,45 @@ private void OnCloseButtonClick(object sender, RoutedEventArgs e)
App.Shutdown();
}
+ ///
+ /// Called when a combo box is right-clicked.
+ ///
+ /// The sender.
+ /// The instance containing the event data.
private void OnComboBoxMouseRightButtonUp(object sender, MouseButtonEventArgs e)
{
- SetFocusToOptimizationButton();
+ SetFocusTo(Optimize);
}
+ ///
+ /// Called when the compact mode button is clicked.
+ ///
+ /// The sender.
+ /// The instance containing the event data.
private void OnCompactModeButtonClick(object sender, RoutedEventArgs e)
{
- SetFocusToOptimizationButton();
+ SetFocusTo(Optimize);
}
+ ///
+ /// Called when the donate menu item is clicked.
+ ///
+ /// The sender.
+ /// The instance containing the event data.
private void OnDonateMenuItemClick(object sender, RoutedEventArgs e)
{
var window = new DonationWindow(this);
window.ShowDialog();
- SetFocusToOptimizationButton();
+ SetFocusTo(Optimize);
}
+ ///
+ /// Called when the help button is clicked.
+ ///
+ /// The sender.
+ /// The instance containing the event data.
private void OnHelpButtonClick(object sender, RoutedEventArgs e)
{
var button = (Button)sender;
@@ -123,15 +160,25 @@ private void OnHelpButtonClick(object sender, RoutedEventArgs e)
}
}
+ ///
+ /// Called when the help button receives a right mouse button down event.
+ ///
+ /// The sender.
+ /// The instance containing the event data.
private void OnHelpButtonPreviewMouseRightButtonDown(object sender, MouseButtonEventArgs e)
{
- SetFocusToOptimizationButton();
+ SetFocusTo(Optimize);
e.Handled = true;
}
+ ///
+ /// Called when the minimize button is clicked.
+ ///
+ /// The sender.
+ /// The instance containing the event data.
private void OnMinimizeButtonClick(object sender, RoutedEventArgs e)
{
- SetFocusToOptimizationButton();
+ SetFocusTo(Optimize);
WindowState = WindowState.Minimized;
}
@@ -144,19 +191,24 @@ protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e)
DragMove();
}
+ ///
+ /// Called when the navigate URI command is completed.
+ ///
private void OnNavigateUriCommandCompleted()
{
- SetFocusToOptimizationButton();
+ SetFocusTo(Optimize);
}
+ ///
+ /// Called when the optimize command is completed.
+ ///
private void OnOptimizeCommandCompleted()
{
if (Settings.CloseAfterOptimization)
{
if (Settings.CloseToTheNotificationArea)
{
- SetFocusToOptimizationButton();
-
+ SetFocusTo(Optimize, force: true);
CloseToTheNotificationArea();
}
else
@@ -168,16 +220,31 @@ private void OnOptimizeCommandCompleted()
else
{
_viewModel.IsBusy = false;
-
- SetFocusToOptimizationButton();
- }
+
+ // Wait for the button to be enabled before trying to refocus it
+ // The button is disabled while IsOptimizationRunning = true
+ Dispatcher.BeginInvoke(new Action(() =>
+ {
+ SetFocusTo(Optimize, force: true);
+ }), System.Windows.Threading.DispatcherPriority.Loaded);
+ }
}
+ ///
+ /// Called when the processes dropdown is opened.
+ ///
+ /// The sender.
+ /// The instance containing the event data.
private void OnProcessesDropDownOpened(object sender, EventArgs e)
{
_viewModel.RaisePropertyChanged(() => _viewModel.Processes);
}
+ ///
+ /// Called when the reset settings to default configuration menu item is clicked.
+ ///
+ /// The sender.
+ /// The instance containing the event data.
private void OnResetSettingsToDefaultConfigurationClick(object sender, RoutedEventArgs e)
{
var window = new MessageDialog(this, Localizer.String.ResetConfirmation, Enums.Dialog.Button.No, Enums.Dialog.Button.Yes);
@@ -187,14 +254,52 @@ private void OnResetSettingsToDefaultConfigurationClick(object sender, RoutedEve
if (result == true && _viewModel.ResetSettingsToDefaultConfigurationCommand.CanExecute(null))
_viewModel.ResetSettingsToDefaultConfigurationCommand.Execute(null);
- SetFocusToOptimizationButton();
+ SetFocusTo(Optimize);
}
+ ///
+ /// Called when a tray icon checkbox is checked.
+ ///
+ /// The sender.
+ /// The instance containing the event data.
+ private void OnTrayIconItemCheckBoxChecked(object sender, RoutedEventArgs e)
+ {
+ var checkBox = sender as CheckBox;
+ if (checkBox == null)
+ return;
+
+ SetFocusTo(checkBox);
+ }
+
+ ///
+ /// Called when a tray icon checkbox is unchecked.
+ ///
+ /// The sender.
+ /// The instance containing the event data.
+ private void OnTrayIconItemCheckBoxUnchecked(object sender, RoutedEventArgs e)
+ {
+ var checkBox = sender as CheckBox;
+ if (checkBox == null)
+ return;
+
+ SetFocusTo(checkBox);
+ }
+
+ ///
+ /// Called when the window is clicked.
+ ///
+ /// The sender.
+ /// The instance containing the event data.
private void OnWindowMouseDown(object sender, MouseButtonEventArgs e)
{
- SetFocusToOptimizationButton();
+ SetFocusTo(Optimize);
}
+ ///
+ /// Called when the window visibility changes.
+ ///
+ /// The sender.
+ /// The instance containing the event data.
private void OnWindowVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
{
foreach (var window in OwnedWindows.Cast().Where(window => window != null && !window.IsDialog))
@@ -207,24 +312,46 @@ private void OnWindowVisibleChanged(object sender, DependencyPropertyChangedEven
window.Visibility = Visibility.Hidden;
}
}
-
- SetFocusToOptimizationButton();
}
- private void SetFocusToOptimizationButton()
+ ///
+ /// Sets the focus to the specified element.
+ ///
+ /// The element to focus.
+ /// If set to true, forces focus even if already focused.
+ private void SetFocusTo(object element, bool force = false)
{
- if (Optimize == null)
+ var uiElement = element as UIElement;
+ var inputElement = element as IInputElement;
+
+ if (uiElement == null || inputElement == null)
return;
- Keyboard.ClearFocus();
- FocusManager.SetFocusedElement(this, Optimize);
- Optimize.Focus();
- }
+ if (!uiElement.IsEnabled || !uiElement.IsVisible || !uiElement.Focusable)
+ return;
- private void SetFocusToProcessExclusionList()
- {
- FocusManager.SetFocusedElement(this, ProcessExclusionList);
- ProcessExclusionList.Focus();
+ if (!force && uiElement.IsFocused)
+ return;
+
+ if (force && uiElement.IsFocused)
+ {
+ // Move focus to the window itself to ensure button loses focus
+ Focus();
+
+ // Then re-focus the element after a brief delay to allow animation state to reset
+ Dispatcher.BeginInvoke(new Action(() =>
+ {
+ Keyboard.Focus(uiElement);
+ FocusManager.SetFocusedElement(this, inputElement);
+ }), System.Windows.Threading.DispatcherPriority.Input);
+ }
+ else
+ {
+ // Normal focus - immediate
+ Keyboard.ClearFocus();
+ Keyboard.Focus(uiElement);
+ FocusManager.SetFocusedElement(this, inputElement);
+ }
}
}
}
diff --git a/src/ViewModel/MainViewModel.cs b/src/ViewModel/MainViewModel.cs
index 540eaf06..9d2e8d0d 100644
--- a/src/ViewModel/MainViewModel.cs
+++ b/src/ViewModel/MainViewModel.cs
@@ -24,13 +24,16 @@ public class MainViewModel : ViewModel, IDisposable
private readonly IHotkeyService _hotKeyService;
private bool _isOptimizationKeyValid;
private bool _isOptimizationRunning;
+ private bool _isReiniziliating;
private DateTimeOffset _lastAutoOptimizationByInterval = DateTimeOffset.Now;
private DateTimeOffset _lastAutoOptimizationByMemoryUsage = DateTimeOffset.Now;
+ private readonly object _lockObject = new object();
private byte _optimizationProgressPercentage;
private string _optimizationProgressStep = Localizer.String.Optimize;
private byte _optimizationProgressTotal = byte.MaxValue;
private byte _optimizationProgressValue = byte.MinValue;
private string _selectedProcess;
+ private ObservableCollection> _trayIconItems;
#endregion
@@ -63,6 +66,9 @@ public MainViewModel(IComputerService computerService, IHotkeyService hotKeyServ
if (App.IsInDesignMode)
{
+ _computerService = new ComputerService();
+ _hotKeyService = new HotkeyService();
+
Settings.AutoUpdate = true;
Computer.OperatingSystem.IsWindows81OrGreater = true;
@@ -70,8 +76,6 @@ public MainViewModel(IComputerService computerService, IHotkeyService hotKeyServ
Computer.OperatingSystem.IsWindowsVistaOrGreater = true;
Computer.OperatingSystem.IsWindowsXpOrGreater = true;
IsOptimizationKeyValid = true;
-
- _hotKeyService = new HotkeyService();
}
else
{
@@ -268,7 +272,7 @@ public bool CanAddProcessToExclusionList
///
public bool CanOptimize
{
- get { return MemoryAreas != Enums.Memory.Areas.None; }
+ get { return MemoryAreas != Enums.Memory.Areas.None && !IsOptimizationRunning; }
}
///
@@ -428,6 +432,9 @@ public bool IsOptimizationKeyValid
get { return _isOptimizationKeyValid; }
set
{
+ if (_isReiniziliating)
+ return;
+
_isOptimizationKeyValid = value;
RaisePropertyChanged();
}
@@ -505,8 +512,10 @@ public Language Language
Computer.Memory = _computerService.Memory;
RaisePropertyChanged(() => Computer);
+ _trayIconItems = null;
+
NotificationService.Initialize();
- NotificationService.Update(Computer.Memory);
+ NotificationService.Update(Computer.Memory, IsOptimizationRunning);
}
RaisePropertyChanged(string.Empty);
@@ -953,7 +962,7 @@ public bool ShowVirtualMemory
Settings.ShowVirtualMemory = value;
Settings.Save();
- NotificationService.Update(Computer.Memory);
+ NotificationService.Update(Computer.Memory, IsOptimizationRunning);
RaisePropertyChanged();
}
@@ -1030,11 +1039,12 @@ public string Title
{
get
{
+ var beta = App.IsInDebugMode ? "\u200E (BETA)\u200E" : null;
var version = App.IsInDesignMode ? Assembly.GetExecutingAssembly().GetName().Version : App.Version;
return CompactMode
- ? Constants.App.Title
- : string.Format(Localizer.Culture, "{0} {1}", Constants.App.Title, string.Format(Localizer.Culture, Constants.App.VersionFormat, version.Major, version.Minor, version.Build));
+ ? string.Format(Localizer.Culture, "{0}{1}", Constants.App.Title, beta)
+ : string.Format(Localizer.Culture, "{0} {1}{2}", Constants.App.Title, string.Format(Localizer.Culture, Constants.App.VersionFormat, version.Major, version.Minor, version.Build), beta);
}
}
@@ -1052,12 +1062,13 @@ public Brush TrayIconBackgroundColor
return System.Windows.Media.Brushes.White;
var brush = Settings.TrayIconBackgroundColor as System.Drawing.SolidBrush;
- var fallbackValue = System.Windows.Media.Brushes.White;
if (brush == null)
- return fallbackValue;
+ return Brushes.FirstOrDefault(b => b.Color == Colors.White) ?? Brushes.First();
- return Brushes.FirstOrDefault(mediaBrush => mediaBrush.Color.IsEquals(brush.Color)) ?? fallbackValue;
+ return Brushes.FirstOrDefault(mediaBrush => mediaBrush.Color.IsEquals(brush.Color))
+ ?? Brushes.FirstOrDefault(b => b.Color == Colors.White)
+ ?? Brushes.First();
}
set
{
@@ -1067,7 +1078,7 @@ public Brush TrayIconBackgroundColor
Settings.TrayIconBackgroundColor = value.ToBrush();
Settings.Save();
- NotificationService.Update(Computer.Memory);
+ NotificationService.Update(Computer.Memory, IsOptimizationRunning);
RaisePropertyChanged();
}
@@ -1087,12 +1098,13 @@ public Brush TrayIconDangerColor
return System.Windows.Media.Brushes.White;
var brush = Settings.TrayIconDangerColor as System.Drawing.SolidBrush;
- var fallbackValue = System.Windows.Media.Brushes.DarkRed;
if (brush == null)
- return fallbackValue;
+ return Brushes.FirstOrDefault(b => b.Color == Colors.DarkRed) ?? Brushes.First();
- return Brushes.FirstOrDefault(mediaBrush => mediaBrush.Color.IsEquals(brush.Color)) ?? fallbackValue;
+ return Brushes.FirstOrDefault(mediaBrush => mediaBrush.Color.IsEquals(brush.Color))
+ ?? Brushes.FirstOrDefault(b => b.Color == Colors.DarkRed)
+ ?? Brushes.First();
}
set
{
@@ -1102,7 +1114,7 @@ public Brush TrayIconDangerColor
Settings.TrayIconDangerColor = value.ToBrush();
Settings.Save();
- NotificationService.Update(Computer.Memory);
+ NotificationService.Update(Computer.Memory, IsOptimizationRunning);
RaisePropertyChanged();
}
@@ -1126,7 +1138,7 @@ public byte TrayIconDangerLevel
Settings.TrayIconDangerLevel = value;
Settings.Save();
- NotificationService.Update(Computer.Memory);
+ NotificationService.Update(Computer.Memory, IsOptimizationRunning);
RaisePropertyChanged();
}
@@ -1137,6 +1149,69 @@ public byte TrayIconDangerLevel
}
}
+ ///
+ /// Gets or sets a value indicating whether [tray icon optimize on middle mouse click].
+ ///
+ ///
+ /// true if [tray icon optimize on middle mouse click]; otherwise, false.
+ ///
+ public bool TrayIconOptimizeOnMiddleMouseClick
+ {
+ get { return Settings.TrayIconOptimizeOnMiddleMouseClick; }
+ set
+ {
+ try
+ {
+ IsBusy = true;
+
+ Settings.TrayIconOptimizeOnMiddleMouseClick = value;
+ Settings.Save();
+
+ RaisePropertyChanged();
+ }
+ finally
+ {
+ IsBusy = false;
+ }
+ }
+ }
+
+ ///
+ /// Gets or sets the color of the tray icon optimizing.
+ ///
+ ///
+ /// The color of the tray icon optimizing.
+ ///
+ public Brush TrayIconOptimizingColor
+ {
+ get
+ {
+ if (App.IsInDesignMode)
+ return System.Windows.Media.Brushes.White;
+
+ var brush = Settings.TrayIconOptimizingColor as System.Drawing.SolidBrush;
+
+ if (brush == null)
+ return Brushes.FirstOrDefault(b => b.Color == Colors.DimGray) ?? Brushes.First();
+
+ return Brushes.FirstOrDefault(mediaBrush => mediaBrush.Color.IsEquals(brush.Color))
+ ?? Brushes.FirstOrDefault(b => b.Color == Colors.DimGray)
+ ?? Brushes.First();
+ }
+ set
+ {
+ if (value == null)
+ return;
+
+ Settings.TrayIconOptimizingColor = value.ToBrush();
+ Settings.Save();
+
+ NotificationService.Update(Computer.Memory, IsOptimizationRunning);
+
+ RaisePropertyChanged();
+ }
+ }
+
///
/// Gets the tray icon items.
///
@@ -1147,15 +1222,19 @@ public ObservableCollection> TrayIconItems
{
get
{
- return new ObservableCollection>
- (
- new List>
- {
- new ObservableItem(Localizer.String.ShowMemoryUsage, () => TrayIconShowMemoryUsage, value => TrayIconShowMemoryUsage = value),
- new ObservableItem(Localizer.String.UseTransparentBackground, () => TrayIconUseTransparentBackground, value => TrayIconUseTransparentBackground = value, TrayIconShowMemoryUsage)
- }
- .OrderBy(item => item.Name)
- );
+ if (_trayIconItems == null)
+ {
+ _trayIconItems = new ObservableCollection>
+ (
+ new List>
+ {
+ new ObservableItem(Localizer.String.OptimizeOnMiddleMouseClick, () => TrayIconOptimizeOnMiddleMouseClick, value => TrayIconOptimizeOnMiddleMouseClick = value),
+ new ObservableItem(Localizer.String.ShowMemoryUsage, () => TrayIconShowMemoryUsage, value => TrayIconShowMemoryUsage = value),
+ new ObservableItem(Localizer.String.UseTransparentBackground, () => TrayIconUseTransparentBackground, value => TrayIconUseTransparentBackground = value, TrayIconShowMemoryUsage)
+ }
+ );
+ }
+ return _trayIconItems;
}
}
@@ -1177,10 +1256,14 @@ public bool TrayIconShowMemoryUsage
Settings.TrayIconShowMemoryUsage = value;
Settings.Save();
- NotificationService.Update(Computer.Memory);
+ NotificationService.Update(Computer.Memory, IsOptimizationRunning);
RaisePropertyChanged();
- RaisePropertyChanged(() => TrayIconItems);
+
+ var useTransparentBackgroundItem = TrayIconItems.FirstOrDefault(item => item.Name == Localizer.String.UseTransparentBackground);
+
+ if (useTransparentBackgroundItem != null)
+ useTransparentBackgroundItem.IsEnabled = value;
}
finally
{
@@ -1203,12 +1286,13 @@ public Brush TrayIconTextColor
return System.Windows.Media.Brushes.White;
var brush = Settings.TrayIconTextColor as System.Drawing.SolidBrush;
- var fallbackValue = System.Windows.Media.Brushes.White;
if (brush == null)
- return fallbackValue;
+ return Brushes.FirstOrDefault(b => b.Color == Colors.White) ?? Brushes.First();
- return Brushes.FirstOrDefault(mediaBrush => mediaBrush.Color.IsEquals(brush.Color)) ?? fallbackValue;
+ return Brushes.FirstOrDefault(mediaBrush => mediaBrush.Color.IsEquals(brush.Color))
+ ?? Brushes.FirstOrDefault(b => b.Color == Colors.White)
+ ?? Brushes.First();
}
set
{
@@ -1218,7 +1302,7 @@ public Brush TrayIconTextColor
Settings.TrayIconTextColor = value.ToBrush();
Settings.Save();
- NotificationService.Update(Computer.Memory);
+ NotificationService.Update(Computer.Memory, IsOptimizationRunning);
RaisePropertyChanged();
}
@@ -1242,7 +1326,7 @@ public bool TrayIconUseTransparentBackground
Settings.TrayIconUseTransparentBackground = value;
Settings.Save();
- NotificationService.Update(Computer.Memory);
+ NotificationService.Update(Computer.Memory, IsOptimizationRunning);
RaisePropertyChanged();
}
@@ -1267,12 +1351,13 @@ public Brush TrayIconWarningColor
return System.Windows.Media.Brushes.White;
var brush = Settings.TrayIconWarningColor as System.Drawing.SolidBrush;
- var fallbackValue = System.Windows.Media.Brushes.DarkRed;
if (brush == null)
- return fallbackValue;
+ return Brushes.FirstOrDefault(b => b.Color == Colors.DarkRed) ?? Brushes.First();
- return Brushes.FirstOrDefault(mediaBrush => mediaBrush.Color.IsEquals(brush.Color)) ?? fallbackValue;
+ return Brushes.FirstOrDefault(mediaBrush => mediaBrush.Color.IsEquals(brush.Color))
+ ?? Brushes.FirstOrDefault(b => b.Color == Colors.DarkRed)
+ ?? Brushes.First();
}
set
{
@@ -1282,7 +1367,7 @@ public Brush TrayIconWarningColor
Settings.TrayIconWarningColor = value.ToBrush();
Settings.Save();
- NotificationService.Update(Computer.Memory);
+ NotificationService.Update(Computer.Memory, IsOptimizationRunning);
RaisePropertyChanged();
}
@@ -1306,7 +1391,7 @@ public byte TrayIconWarningLevel
Settings.TrayIconWarningLevel = value;
Settings.Save();
- NotificationService.Update(Computer.Memory);
+ NotificationService.Update(Computer.Memory, IsOptimizationRunning);
RaisePropertyChanged();
}
@@ -1508,27 +1593,30 @@ private void MonitorApp()
App.SetPriority(Settings.RunOnPriority);
// Auto Optimization
- if (CanOptimize)
+ lock (_lockObject)
{
- // Interval
- if (Settings.AutoOptimizationInterval > 0 &&
- DateTimeOffset.Now.Subtract(_lastAutoOptimizationByInterval).TotalHours >= Settings.AutoOptimizationInterval)
+ if (CanOptimize)
{
- OptimizeAsync(Enums.Memory.Optimization.Reason.Schedule);
+ // Interval
+ if (Settings.AutoOptimizationInterval > 0 &&
+ DateTimeOffset.Now.Subtract(_lastAutoOptimizationByInterval).TotalHours >= Settings.AutoOptimizationInterval)
+ {
+ OptimizeAsync(Enums.Memory.Optimization.Reason.Schedule);
- _lastAutoOptimizationByInterval = DateTimeOffset.Now;
- continue;
- }
+ _lastAutoOptimizationByInterval = DateTimeOffset.Now;
+ continue;
+ }
- // Memory usage
- if (Settings.AutoOptimizationMemoryUsage > 0 &&
- Computer.Memory.Physical.Free.Percentage < Settings.AutoOptimizationMemoryUsage &&
- DateTimeOffset.Now.Subtract(_lastAutoOptimizationByMemoryUsage).TotalMinutes >= Constants.App.AutoOptimizationMemoryUsageInterval)
- {
- OptimizeAsync(Enums.Memory.Optimization.Reason.LowMemory);
+ // Memory usage
+ if (Settings.AutoOptimizationMemoryUsage > 0 &&
+ Computer.Memory.Physical.Free.Percentage < Settings.AutoOptimizationMemoryUsage &&
+ DateTimeOffset.Now.Subtract(_lastAutoOptimizationByMemoryUsage).TotalMinutes >= Constants.App.AutoOptimizationMemoryUsageInterval)
+ {
+ OptimizeAsync(Enums.Memory.Optimization.Reason.LowMemory);
- _lastAutoOptimizationByMemoryUsage = DateTimeOffset.Now;
- continue;
+ _lastAutoOptimizationByMemoryUsage = DateTimeOffset.Now;
+ continue;
+ }
}
}
}
@@ -1581,13 +1669,16 @@ private void MonitorComputer()
if (IsBusy)
continue;
- // Update memory info
- Computer.Memory = _computerService.Memory;
+ lock (_lockObject)
+ {
+ // Update memory info
+ Computer.Memory = _computerService.Memory;
- RaisePropertyChanged(() => Computer);
- RaisePropertyChanged(() => VirtualMemoryHeader);
+ RaisePropertyChanged(() => Computer);
+ RaisePropertyChanged(() => VirtualMemoryHeader);
- NotificationService.Update(Computer.Memory);
+ NotificationService.Update(Computer.Memory, IsOptimizationRunning);
+ }
// Delay
if (_cancellationTokenSource.Token.WaitHandle.WaitOne(5000))
@@ -1618,50 +1709,66 @@ private void OnOptimizeProgressUpdate(byte value, string step)
/// Optimization reason
private void Optimize(Enums.Memory.Optimization.Reason reason)
{
- try
+ lock (_lockObject)
{
- IsBusy = true;
- IsOptimizationRunning = true;
+ try
+ {
+ IsBusy = true;
+ IsOptimizationRunning = true;
+
+ NotificationService.Update(Computer.Memory, IsOptimizationRunning);
- // App priority
- App.SetPriority(Settings.RunOnPriority);
+ // App priority
+ App.SetPriority(Settings.RunOnPriority);
- // Memory optimize
- var tempPhysicalAvailable = Computer.Memory.Physical.Free.Bytes;
- var tempVirtualAvailable = Computer.Memory.Virtual.Free.Bytes;
+ // Memory optimize
+ var tempPhysicalAvailable = Computer.Memory.Physical.Free.Bytes;
+ var tempVirtualAvailable = Computer.Memory.Virtual.Free.Bytes;
- _computerService.Optimize(reason, Settings.MemoryAreas);
+ _computerService.Optimize(reason, Settings.MemoryAreas);
- // Update memory info
- Computer.Memory = _computerService.Memory;
- RaisePropertyChanged(() => Computer);
+ // Update memory info
+ Computer.Memory = _computerService.Memory;
+ RaisePropertyChanged(() => Computer);
- // Notification
- if (Settings.ShowOptimizationNotifications)
- {
- var physicalReleased = (Computer.Memory.Physical.Free.Bytes > tempPhysicalAvailable ? Computer.Memory.Physical.Free.Bytes - tempPhysicalAvailable : tempPhysicalAvailable - Computer.Memory.Physical.Free.Bytes).ToMemoryUnit();
- var virtualReleased = (Computer.Memory.Virtual.Free.Bytes > tempVirtualAvailable ? Computer.Memory.Virtual.Free.Bytes - tempVirtualAvailable : tempVirtualAvailable - Computer.Memory.Virtual.Free.Bytes).ToMemoryUnit();
+ // Notification
+ if (Settings.ShowOptimizationNotifications)
+ {
+ var physicalReleased = (Computer.Memory.Physical.Free.Bytes > tempPhysicalAvailable ? Computer.Memory.Physical.Free.Bytes - tempPhysicalAvailable : tempPhysicalAvailable - Computer.Memory.Physical.Free.Bytes).ToMemoryUnit();
+ var virtualReleased = (Computer.Memory.Virtual.Free.Bytes > tempVirtualAvailable ? Computer.Memory.Virtual.Free.Bytes - tempVirtualAvailable : tempVirtualAvailable - Computer.Memory.Virtual.Free.Bytes).ToMemoryUnit();
- var message = Settings.ShowVirtualMemory
- ? string.Format(Localizer.Culture, "{1}{0}{0}{2}: {3}{0}{4}: {5:0.#} {6}{0}{7}: {8:0.#} {9}", Environment.NewLine, Localizer.String.MemoryOptimized.ToUpper(Localizer.Culture), Localizer.String.Reason, reason.GetString(), Localizer.String.PhysicalMemory, physicalReleased.Key, physicalReleased.Value, Localizer.String.VirtualMemory, virtualReleased.Key, virtualReleased.Value)
- : string.Format(Localizer.Culture, "{1}{0}{0}{2}: {3}{0}{4}: {5:0.#} {6}", Environment.NewLine, Localizer.String.MemoryOptimized.ToUpper(Localizer.Culture), Localizer.String.Reason, reason.GetString(), Localizer.String.PhysicalMemory, physicalReleased.Key, physicalReleased.Value);
+ var message = Settings.ShowVirtualMemory
+ ? string.Format(Localizer.Culture, "{1}{0}{0}{2}: {3}{0}{4}: {5:0.#} {6}{0}{7}: {8:0.#} {9}", Environment.NewLine, Localizer.String.MemoryOptimized.ToUpper(Localizer.Culture), Localizer.String.Reason, reason.GetString(), Localizer.String.PhysicalMemory, physicalReleased.Key, physicalReleased.Value, Localizer.String.VirtualMemory, virtualReleased.Key, virtualReleased.Value)
+ : string.Format(Localizer.Culture, "{1}{0}{0}{2}: {3}{0}{4}: {5:0.#} {6}", Environment.NewLine, Localizer.String.MemoryOptimized.ToUpper(Localizer.Culture), Localizer.String.Reason, reason.GetString(), Localizer.String.PhysicalMemory, physicalReleased.Key, physicalReleased.Value);
- Notify(message);
+ Notify(message);
+ }
}
-
- if (OnOptimizeCommandCompleted != null)
+ finally
{
- System.Windows.Application.Current.Dispatcher.Invoke((Action)delegate
+ IsOptimizationRunning = false;
+ IsBusy = false;
+
+ NotificationService.Update(Computer.Memory, IsOptimizationRunning);
+
+ // Raise the event after IsOptimizationRunning is set to false
+ // Use BeginInvoke to ensure it runs after all property changes propagate
+ System.Windows.Application.Current.Dispatcher.BeginInvoke((Action)(() =>
{
- OnOptimizeCommandCompleted();
- });
+ // Force command manager to re-evaluate CanExecute on all commands
+ CommandManager.InvalidateRequerySuggested();
+ }), System.Windows.Threading.DispatcherPriority.Normal);
+
+ // Raise completion event with lower priority to ensure commands are refreshed first
+ if (OnOptimizeCommandCompleted != null)
+ {
+ System.Windows.Application.Current.Dispatcher.BeginInvoke((Action)(() =>
+ {
+ OnOptimizeCommandCompleted();
+ }), System.Windows.Threading.DispatcherPriority.ApplicationIdle);
+ }
}
}
- finally
- {
- IsOptimizationRunning = false;
- IsBusy = false;
- }
}
///
@@ -1672,6 +1779,9 @@ private void OptimizeAsync(Enums.Memory.Optimization.Reason reason)
{
try
{
+ if (IsOptimizationRunning)
+ return;
+
OptimizationProgressStep = Localizer.String.Optimize;
OptimizationProgressValue = 0;
OptimizationProgressTotal = (byte)(new BitArray(new[] { (int)Settings.MemoryAreas }).OfType().Count(x => x) + 1);
@@ -1697,9 +1807,10 @@ private void RegisterOptimizationHotkey(ModifierKeys modifiers, Key key)
Settings.OptimizationModifiers = modifiers;
var hotKey = new Hotkey(Settings.OptimizationModifiers, Settings.OptimizationKey);
+
IsOptimizationKeyValid = _hotKeyService.Register(hotKey, () => OptimizeAsync(Enums.Memory.Optimization.Reason.Manual));
- if (!IsOptimizationKeyValid)
+ if (!_isReiniziliating && !IsOptimizationKeyValid)
{
var message = string.Format(Localizer.Culture, Localizer.String.HotkeyIsInUseByOperatingSystem, hotKey);
@@ -1715,6 +1826,39 @@ private void RegisterOptimizationHotkey(ModifierKeys modifiers, Key key)
RaisePropertyChanged(() => OptimizationModifiers);
}
+ ///
+ /// Reinitializes app after system resume from hibernation.
+ ///
+ public void ReinitializeAfterHibernation()
+ {
+ try
+ {
+ lock (_lockObject)
+ {
+ _isReiniziliating = true;
+
+ if (UseHotkey)
+ RegisterOptimizationHotkey(Settings.OptimizationModifiers, Settings.OptimizationKey);
+
+ Computer.Memory = _computerService.Memory;
+
+ NotificationService.Update(Computer.Memory, IsOptimizationRunning);
+
+ RaisePropertyChanged(string.Empty);
+
+ App.ReleaseMemory();
+ }
+ }
+ catch (Exception e)
+ {
+ Logger.Error("Error after system resume from hibernation: " + e.GetMessage());
+ }
+ finally
+ {
+ _isReiniziliating = false;
+ }
+ }
+
///
/// Removes the process from exclusion list.
///
@@ -1761,8 +1905,10 @@ private void ResetSettingsToDefaultConfiguration()
OptimizationKey = Settings.OptimizationKey;
OptimizationModifiers = Settings.OptimizationModifiers;
+ _trayIconItems = null;
+
NotificationService.Initialize();
- NotificationService.Update(Computer.Memory);
+ NotificationService.Update(Computer.Memory, IsOptimizationRunning);
RaisePropertyChanged(string.Empty);
}
diff --git a/src/WinMemoryCleaner.csproj b/src/WinMemoryCleaner.csproj
index 8ebf7557..2b2ce263 100644
--- a/src/WinMemoryCleaner.csproj
+++ b/src/WinMemoryCleaner.csproj
@@ -1,4 +1,4 @@
-
+
@@ -115,6 +115,7 @@
+
@@ -134,6 +135,7 @@
+
@@ -210,7 +212,8 @@
-
+
+