From d0846b0f09a70c9b943eed59d43d04dbce7342ba Mon Sep 17 00:00:00 2001 From: Robin Krom Date: Mon, 9 Mar 2020 21:50:37 +0100 Subject: [PATCH] First step in moving towards replaceable notification messages, extracted the logic to do so into it's own module. [skip ci] --- Greenshot/Forms/MainForm.cs | 33 ++--- Greenshot/Helpers/CaptureHelper.cs | 69 ++++------ .../Helpers/NotifyIconNotificationService.cs | 125 ++++++++++++++++++ GreenshotPlugin/Core/WindowsVersion.cs | 6 + 4 files changed, 168 insertions(+), 65 deletions(-) create mode 100644 Greenshot/Helpers/NotifyIconNotificationService.cs diff --git a/Greenshot/Forms/MainForm.cs b/Greenshot/Forms/MainForm.cs index dcdee013b..917553305 100644 --- a/Greenshot/Forms/MainForm.cs +++ b/Greenshot/Forms/MainForm.cs @@ -46,7 +46,6 @@ using System.Threading.Tasks; using GreenshotPlugin.IniFile; using GreenshotPlugin.Interfaces; using GreenshotPlugin.Interfaces.Plugin; -using GreenshotPlugin.UnmanagedHelpers.Enums; namespace Greenshot { /// @@ -350,6 +349,12 @@ namespace Greenshot { // Make the notify icon available SimpleServiceProvider.Current.AddService(notifyIcon); + // TODO: Enable check if the Windows 10 notification service is available + //if (WindowsVersion.IsBeforeWindows10) + //{ + SimpleServiceProvider.Current.AddService(new NotifyIconNotificationService()); + //} + // Disable access to the settings, for feature #3521446 contextmenu_settings.Visible = !_conf.DisableSettings; @@ -428,20 +433,7 @@ namespace Greenshot { HandleDataTransport(dataTransport); } - private void BalloonTipClicked(object sender, EventArgs e) { - try { - ShowSetting(); - } finally { - BalloonTipClosed(sender, e); - } - } - - private void BalloonTipClosed(object sender, EventArgs e) { - notifyIcon.BalloonTipClicked -= BalloonTipClicked; - notifyIcon.BalloonTipClosed -= BalloonTipClosed; - } - - private void HandleDataTransport(CopyDataTransport dataTransport) { + private void HandleDataTransport(CopyDataTransport dataTransport) { foreach(KeyValuePair command in dataTransport.Commands) { LOG.Debug("Data received, Command = " + command.Key + ", Data: " + command.Value); switch(command.Key) { @@ -451,14 +443,9 @@ namespace Greenshot { break; case CommandEnum.FirstLaunch: LOG.Info("FirstLaunch: Created new configuration, showing balloon."); - try { - notifyIcon.BalloonTipClicked += BalloonTipClicked; - notifyIcon.BalloonTipClosed += BalloonTipClosed; - notifyIcon.ShowBalloonTip(2000, "Greenshot", Language.GetFormattedString(LangKey.tooltip_firststart, HotkeyControl.GetLocalizedHotkeyStringFromString(_conf.RegionHotkey)), ToolTipIcon.Info); - } catch (Exception ex) { - LOG.Warn("Exception while showing first launch: ", ex); - } - break; + var notifyIconClassicMessageHandler = SimpleServiceProvider.Current.GetInstance(); + notifyIconClassicMessageHandler.ShowInfoMessage(Language.GetFormattedString(LangKey.tooltip_firststart, HotkeyControl.GetLocalizedHotkeyStringFromString(_conf.RegionHotkey)), 2000, ShowSetting); + break; case CommandEnum.ReloadConfig: LOG.Info("Reload requested"); try { diff --git a/Greenshot/Helpers/CaptureHelper.cs b/Greenshot/Helpers/CaptureHelper.cs index c2d1f2aab..761b2ec11 100644 --- a/Greenshot/Helpers/CaptureHelper.cs +++ b/Greenshot/Helpers/CaptureHelper.cs @@ -1,20 +1,20 @@ /* * Greenshot - a free and open source screenshot tool * Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom - * + * * For more information see: http://getgreenshot.org/ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot - * + * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 1 of the License, or * (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ @@ -41,7 +41,7 @@ using GreenshotPlugin.UnmanagedHelpers.Enums; namespace Greenshot.Helpers { /// - /// CaptureHelper contains all the capture logic + /// CaptureHelper contains all the capture logic /// public class CaptureHelper : IDisposable { private static readonly ILog Log = LogManager.GetLogger(typeof(CaptureHelper)); @@ -177,7 +177,7 @@ namespace Greenshot.Helpers { public CaptureHelper(CaptureMode captureMode) { _captureMode = captureMode; - _capture = new Capture(); + _capture = new Capture(); } public CaptureHelper(CaptureMode captureMode, bool captureMouseCursor) : this(captureMode) { @@ -192,7 +192,7 @@ namespace Greenshot.Helpers { public CaptureHelper(CaptureMode captureMode, bool captureMouseCursor, IDestination destination) : this(captureMode, captureMouseCursor) { _capture.CaptureDetails.AddDestination(destination); } - + public WindowDetails SelectedCaptureWindow { get { return _selectedCaptureWindow; @@ -201,7 +201,7 @@ namespace Greenshot.Helpers { _selectedCaptureWindow = value; } } - + private void DoCaptureFeedback() { if (CoreConfig.PlayCameraSound) { SoundHelper.Play(); @@ -459,13 +459,13 @@ namespace Greenshot.Helpers { _capture.Dispose(); } } - + /// /// Pre-Initialization for CaptureWithFeedback, this will get all the windows before we change anything /// private Thread PrepareForCaptureWithFeedback() { _windows = new List(); - + // If the App Launcher is visisble, no other windows are active WindowDetails appLauncherWindow = WindowDetails.GetAppLauncher(); if (appLauncherWindow != null && appLauncherWindow.Visible) { @@ -514,14 +514,12 @@ namespace Greenshot.Helpers { /// /// If a balloon tip is show for a taken capture, this handles the click on it /// - /// - /// - private void OpenCaptureOnClick(object sender, EventArgs e) { + /// SurfaceMessageEventArgs + private void OpenCaptureOnClick(SurfaceMessageEventArgs e) { var notifyIcon = SimpleServiceProvider.Current.GetInstance(); if (!(notifyIcon.Tag is SurfaceMessageEventArgs eventArgs)) { Log.Warn("OpenCaptureOnClick called without SurfaceMessageEventArgs"); - RemoveEventHandler(sender, e); - return; + return; } ISurface surface = eventArgs.Surface; if (surface != null) @@ -537,17 +535,9 @@ namespace Greenshot.Helpers { } } Log.DebugFormat("Deregistering the BalloonTipClicked"); - RemoveEventHandler(sender, e); - } + } - private void RemoveEventHandler(object sender, EventArgs e) { - var notifyIcon = SimpleServiceProvider.Current.GetInstance(); - notifyIcon.BalloonTipClicked -= OpenCaptureOnClick; - notifyIcon.BalloonTipClosed -= RemoveEventHandler; - notifyIcon.Tag = null; - } - - /// + /// /// This is the SurfaceMessageEvent receiver /// /// object @@ -556,27 +546,22 @@ namespace Greenshot.Helpers { if (string.IsNullOrEmpty(eventArgs?.Message)) { return; } - var notifyIcon = SimpleServiceProvider.Current.GetInstance(); + var notifyIconClassicMessageHandler = SimpleServiceProvider.Current.GetInstance(); switch (eventArgs.MessageType) { case SurfaceMessageTyp.Error: - notifyIcon.ShowBalloonTip(10000, "Greenshot", eventArgs.Message, ToolTipIcon.Error); + notifyIconClassicMessageHandler.ShowErrorMessage(eventArgs.Message, 10000); break; case SurfaceMessageTyp.Info: - notifyIcon.ShowBalloonTip(10000, "Greenshot", eventArgs.Message, ToolTipIcon.Info); + notifyIconClassicMessageHandler.ShowInfoMessage(eventArgs.Message, 10000); break; case SurfaceMessageTyp.FileSaved: case SurfaceMessageTyp.UploadedUri: // Show a balloon and register an event handler to open the "capture" for if someone clicks the balloon. - notifyIcon.BalloonTipClicked += OpenCaptureOnClick; - notifyIcon.BalloonTipClosed += RemoveEventHandler; - // Store for later usage - notifyIcon.Tag = eventArgs; - notifyIcon.ShowBalloonTip(10000, "Greenshot", eventArgs.Message, ToolTipIcon.Info); + notifyIconClassicMessageHandler.ShowInfoMessage(eventArgs.Message, 10000, () => OpenCaptureOnClick(eventArgs)); break; } } - private void HandleCapture() { // Flag to see if the image was "exported" so the FileEditor doesn't // ask to save the file as long as nothing is done. @@ -587,7 +572,7 @@ namespace Greenshot.Helpers { var selectionRectangle = new Rectangle(Point.Empty, _capture.Image.Size); var ocrInfo = _capture.CaptureDetails.OcrInformation; if (ocrInfo != null) - { + { var textResult = new StringBuilder(); foreach (var line in ocrInfo.Lines) { @@ -666,10 +651,10 @@ namespace Greenshot.Helpers { Modified = !outputMade }; - // Register notify events if this is wanted + // Register notify events if this is wanted if (CoreConfig.ShowTrayNotification && !CoreConfig.HideTrayicon) { surface.SurfaceMessage += SurfaceMessageReceived; - + } // Let the processors do their job foreach(var processor in SimpleServiceProvider.Current.GetAllInstances()) { @@ -677,7 +662,7 @@ namespace Greenshot.Helpers { Log.InfoFormat("Calling processor {0}", processor.Description); processor.ProcessCapture(surface, _capture.CaptureDetails); } - + // As the surfaces copies the reference to the image, make sure the image is not being disposed (a trick to save memory) _capture.Image = null; @@ -749,7 +734,7 @@ namespace Greenshot.Helpers { // Nothing to capture, code up in the stack will capture the full screen return false; } - // Fix for Bug #3430560 + // Fix for Bug #3430560 CoreConfig.LastCapturedRegion = _selectedCaptureWindow.WindowRectangle; bool returnValue = CaptureWindow(_selectedCaptureWindow, _capture, CoreConfig.WindowCaptureMode) != null; return returnValue; @@ -775,7 +760,7 @@ namespace Greenshot.Helpers { } return windowToCapture; } - + /// /// Check if Process uses PresentationFramework.dll -> meaning it uses WPF /// @@ -992,9 +977,9 @@ namespace Greenshot.Helpers { private void CaptureWithFeedback() { - // The following, to be precise the HideApp, causes the app to close as described in BUG-1620 + // The following, to be precise the HideApp, causes the app to close as described in BUG-1620 // Added check for metro (Modern UI) apps, which might be maximized and cover the screen. - + //foreach(WindowDetails app in WindowDetails.GetMetroApps()) { // if (app.Maximised) { // app.HideApp(); diff --git a/Greenshot/Helpers/NotifyIconNotificationService.cs b/Greenshot/Helpers/NotifyIconNotificationService.cs new file mode 100644 index 000000000..bba6b1517 --- /dev/null +++ b/Greenshot/Helpers/NotifyIconNotificationService.cs @@ -0,0 +1,125 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: http://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using System; +using System.Windows.Forms; +using GreenshotPlugin.Core; +using log4net; + +namespace Greenshot.Helpers +{ + /// + /// Notify the user with messages via the NotifyIcon + /// + public class NotifyIconNotificationService + { + private static readonly ILog Log = LogManager.GetLogger(typeof(NotifyIconNotificationService)); + private readonly NotifyIcon _notifyIcon; + + public NotifyIconNotificationService() + { + _notifyIcon = SimpleServiceProvider.Current.GetInstance(); + } + + /// + /// This will show a warning message to the user + /// + /// string + /// + /// Action called if the user clicks the notification + /// Action + public void ShowWarningMessage(string message, int timeout, Action onClickAction = null, Action onClosedAction = null) + { + ShowMessage(message, timeout, ToolTipIcon.Warning, onClickAction, onClosedAction); + } + + /// + /// This will show an error message to the user + /// + /// string + /// + /// Action called if the user clicks the notification + /// Action + public void ShowErrorMessage(string message, int timeout, Action onClickAction = null, Action onClosedAction = null) + { + ShowMessage(message, timeout, ToolTipIcon.Error, onClickAction, onClosedAction); + } + + /// + /// This will show an info message to the user + /// + /// string + /// int + /// Action called if the user clicks the notification + /// Action + public void ShowInfoMessage(string message, int timeout, Action onClickAction = null, Action onClosedAction = null) + { + ShowMessage(message, timeout, ToolTipIcon.Info, onClickAction, onClosedAction); + } + + /// + /// This will show a message to the user + /// + /// string + /// int + /// ToolTipIcon + /// Action + /// Action + public void ShowMessage(string message, int timeout, ToolTipIcon level, Action onClickAction = null, Action onClosedAction = null) { + void BalloonClickedHandler(object s, EventArgs e) + { + try + { + onClickAction?.Invoke(); + } + catch (Exception ex) + { + Log.Warn("Exception while handling the onclick action: ", ex); + } + + _notifyIcon.BalloonTipClicked -= BalloonClickedHandler; + } + + if (onClickAction != null) + { + _notifyIcon.BalloonTipClicked += BalloonClickedHandler; + } + + void BalloonClosedHandler(object s, EventArgs e) + { + try + { + onClosedAction?.Invoke(); + } + catch (Exception ex) + { + Log.Warn("Exception while handling the onClosed action: ", ex); + } + + _notifyIcon.BalloonTipClosed -= BalloonClosedHandler; + // Remove the other handler too + _notifyIcon.BalloonTipClicked -= BalloonClickedHandler; + } + _notifyIcon.BalloonTipClosed += BalloonClosedHandler; + _notifyIcon.ShowBalloonTip(timeout, @"Greenshot", message, level); + } + } +} diff --git a/GreenshotPlugin/Core/WindowsVersion.cs b/GreenshotPlugin/Core/WindowsVersion.cs index 3337fe5be..ae9600f25 100644 --- a/GreenshotPlugin/Core/WindowsVersion.cs +++ b/GreenshotPlugin/Core/WindowsVersion.cs @@ -21,6 +21,12 @@ namespace GreenshotPlugin.Core /// true if we are running on Windows 10 public static bool IsWindows10 { get; } = WinVersion.Major == 10; + /// + /// Test if the current OS is before Windows 10 + /// + /// true if we are running on Windows before 10 + public static bool IsBeforeWindows10 { get; } = WinVersion.Major < 10; + /// /// Test if the current OS is Windows 10 or later ///