diff --git a/Greenshot/Destinations/FileDestination.cs b/Greenshot/Destinations/FileDestination.cs index 331ba6c89..f821e4bd6 100644 --- a/Greenshot/Destinations/FileDestination.cs +++ b/Greenshot/Destinations/FileDestination.cs @@ -116,6 +116,7 @@ namespace Greenshot.Destinations { pattern = "greenshot ${capturetime}"; } string filename = FilenameHelper.GetFilenameFromPattern(pattern, CoreConfig.OutputFileFormat, captureDetails); + CoreConfig.ValidateAndCorrectOutputFilePath(); string filepath = FilenameHelper.FillVariables(CoreConfig.OutputFilePath, false); try { fullPath = Path.Combine(filepath, filename); diff --git a/Greenshot/Drawing/Fields/FieldAggregator.cs b/Greenshot/Drawing/Fields/FieldAggregator.cs index cbf5377d7..3eb2f8bac 100644 --- a/Greenshot/Drawing/Fields/FieldAggregator.cs +++ b/Greenshot/Drawing/Fields/FieldAggregator.cs @@ -27,6 +27,7 @@ using Greenshot.Plugin.Drawing; using GreenshotPlugin.Interfaces.Drawing; using System.Collections.Generic; using System.ComponentModel; +using System.Linq; namespace Greenshot.Drawing.Fields { @@ -203,7 +204,7 @@ namespace Greenshot.Drawing.Fields { return; } - foreach (var drawableContainer1 in _boundContainers) + foreach (var drawableContainer1 in _boundContainers.ToList()) { var drawableContainer = (DrawableContainer) drawableContainer1; if (!drawableContainer.HasField(field.FieldType)) diff --git a/Greenshot/Drawing/SpeechbubbleContainer.cs b/Greenshot/Drawing/SpeechbubbleContainer.cs index f90643ae9..0cdfb91c1 100644 --- a/Greenshot/Drawing/SpeechbubbleContainer.cs +++ b/Greenshot/Drawing/SpeechbubbleContainer.cs @@ -56,9 +56,10 @@ namespace Greenshot.Drawing /// /// Restore the target gripper /// - /// - protected override void OnDeserialized(StreamingContext context) + /// StreamingContext + protected override void OnDeserialized(StreamingContext streamingContext) { + base.OnDeserialized(streamingContext); InitAdorner(Color.Green, _storedTargetGripperLocation); } #endregion @@ -303,7 +304,6 @@ namespace Greenshot.Drawing tail.Dispose(); // Draw the text - UpdateFormat(); DrawText(graphics, rect, lineThickness, lineColor, shadow, StringFormat, Text, Font); } diff --git a/Greenshot/Drawing/TextContainer.cs b/Greenshot/Drawing/TextContainer.cs index 2761bfe9c..0c72b464b 100644 --- a/Greenshot/Drawing/TextContainer.cs +++ b/Greenshot/Drawing/TextContainer.cs @@ -272,8 +272,11 @@ namespace Greenshot.Drawing _parent.Controls.Add(_textBox); } EnsureTextBoxContrast(); - _textBox.Show(); - _textBox.Focus(); + if (_textBox != null) + { + _textBox.Show(); + _textBox.Focus(); + } } /// @@ -281,6 +284,10 @@ namespace Greenshot.Drawing /// private void EnsureTextBoxContrast() { + if (_textBox == null) + { + return; + } Color lc = GetFieldValueAsColor(FieldType.LINE_COLOR); if (lc.R > 203 && lc.G > 203 && lc.B > 203) { @@ -295,7 +302,7 @@ namespace Greenshot.Drawing private void HideTextBox() { _parent.Focus(); - _textBox.Hide(); + _textBox?.Hide(); _parent.KeysLocked = false; _parent.Controls.Remove(_textBox); } @@ -424,6 +431,10 @@ namespace Greenshot.Drawing /// private void UpdateTextBoxPosition() { + if (_textBox == null) + { + return; + } int lineThickness = GetFieldValueAsInt(FieldType.LINE_THICKNESS); int lineWidth = (int)Math.Floor(lineThickness / 2d); diff --git a/Greenshot/Forms/ImageEditorForm.cs b/Greenshot/Forms/ImageEditorForm.cs index 24ef53bc7..c5de9937e 100644 --- a/Greenshot/Forms/ImageEditorForm.cs +++ b/Greenshot/Forms/ImageEditorForm.cs @@ -860,13 +860,15 @@ namespace Greenshot { case Keys.Oemcomma: // Rotate CCW Ctrl + , RotateCcwToolstripButtonClick(sender, e); break; - case Keys.OemPeriod: // Rotate CW Ctrl + . + case Keys.OemPeriod: // Rotate CW Ctrl + . RotateCwToolstripButtonClick(sender, e); break; case Keys.Add: // Ctrl + + + case Keys.Oemplus: // Ctrl + + EnlargeCanvasToolStripMenuItemClick(sender, e); break; case Keys.Subtract: // Ctrl + - + case Keys.OemMinus: // Ctrl + - ShrinkCanvasToolStripMenuItemClick(sender, e); break; } diff --git a/Greenshot/Forms/MainForm.cs b/Greenshot/Forms/MainForm.cs index b8164fba4..23bb8960f 100644 --- a/Greenshot/Forms/MainForm.cs +++ b/Greenshot/Forms/MainForm.cs @@ -1307,46 +1307,11 @@ namespace Greenshot { private void NotifyIconClick(ClickActions clickAction) { switch (clickAction) { case ClickActions.OPEN_LAST_IN_EXPLORER: - string path = _conf.OutputFileAsFullpath; - if (!File.Exists(path)) { - string lastFilePath = Path.GetDirectoryName(_conf.OutputFileAsFullpath); - if (!string.IsNullOrEmpty(lastFilePath) && Directory.Exists(lastFilePath)) { - path = lastFilePath; - } - } - if (path == null) { - string configPath = FilenameHelper.FillVariables(_conf.OutputFilePath, false); - if (Directory.Exists(configPath)) { - path = configPath; - } - } - - if (path != null) { - try { - // Check if path is a directory - if (Directory.Exists(path)) - { - using (Process.Start(path)) - { - } - } - // Check if path is a file - else if (File.Exists(path)) - { - // Start the explorer process and select the file - using (var explorer = Process.Start("explorer.exe", $"/select,\"{path}\"")) - { - explorer?.WaitForInputIdle(500); - } - } - } catch (Exception ex) { - // Make sure we show what we tried to open in the exception - ex.Data.Add("path", path); - throw; - } - } + Contextmenu_OpenRecent(this, null); break; case ClickActions.OPEN_LAST_IN_EDITOR: + _conf.ValidateAndCorrectOutputFileAsFullpath(); + if (File.Exists(_conf.OutputFileAsFullpath)) { CaptureHelper.CaptureFile(_conf.OutputFileAsFullpath, DestinationHelper.GetDestination(EditorDestination.DESIGNATION)); } @@ -1364,25 +1329,40 @@ namespace Greenshot { /// /// The Contextmenu_OpenRecent currently opens the last know save location /// - private void Contextmenu_OpenRecent(object sender, EventArgs eventArgs) { - string path = FilenameHelper.FillVariables(_conf.OutputFilePath, false); - // Fix for #1470, problems with a drive which is no longer available - try { - string lastFilePath = Path.GetDirectoryName(_conf.OutputFileAsFullpath); + private void Contextmenu_OpenRecent(object sender, EventArgs eventArgs) + { + _conf.ValidateAndCorrectOutputFilePath(); + _conf.ValidateAndCorrectOutputFileAsFullpath(); + string path = _conf.OutputFileAsFullpath; + if (!File.Exists(path)) + { + path = FilenameHelper.FillVariables(_conf.OutputFilePath, false); + // Fix for #1470, problems with a drive which is no longer available + try + { + string lastFilePath = Path.GetDirectoryName(_conf.OutputFileAsFullpath); - if (lastFilePath != null && Directory.Exists(lastFilePath)) { - path = lastFilePath; - } else if (!Directory.Exists(path)) { - // What do I open when nothing can be found? Right, nothing... - return; + if (lastFilePath != null && Directory.Exists(lastFilePath)) + { + path = lastFilePath; + } + else if (!Directory.Exists(path)) + { + // What do I open when nothing can be found? Right, nothing... + return; + } + } + catch (Exception ex) + { + LOG.Warn("Couldn't open the path to the last exported file, taking default.", ex); } - } catch (Exception ex) { - LOG.Warn("Couldn't open the path to the last exported file, taking default.", ex); } - LOG.Debug("DoubleClick was called! Starting: " + path); - try { - Process.Start(path); - } catch (Exception ex) { + try + { + ExplorerHelper.OpenInExplorer(path); + } + catch (Exception ex) + { // Make sure we show what we tried to open in the exception ex.Data.Add("path", path); LOG.Warn("Couldn't open the path to the last exported file", ex); diff --git a/Greenshot/Helpers/CaptureHelper.cs b/Greenshot/Helpers/CaptureHelper.cs index e62afabe7..3351db1d8 100644 --- a/Greenshot/Helpers/CaptureHelper.cs +++ b/Greenshot/Helpers/CaptureHelper.cs @@ -512,22 +512,14 @@ namespace Greenshot.Helpers { string errorMessage = null; var path = Path.GetDirectoryName(surface.LastSaveFullPath); try { - if (path != null) - { - var processStartInfo = new ProcessStartInfo("explorer.exe") - { - Arguments = path, - UseShellExecute = false - }; - using (var process = new Process()) { - process.StartInfo = processStartInfo; - process.Start(); - } - } - } catch (Exception ex) { + ExplorerHelper.OpenInExplorer(path); + } + catch (Exception ex) + { errorMessage = ex.Message; } // Added fallback for when the explorer can't be found + // TODO: Check if this makes sense if (errorMessage != null) { try { string windowsPath = Environment.GetEnvironmentVariable("SYSTEMROOT"); @@ -852,7 +844,7 @@ namespace Greenshot.Helpers { // Restore the window making sure it's visible! windowToCapture.Restore(); } else { - windowToCapture.ToForeground(); + windowToCapture.ToForeground(false); } tmpCapture = windowToCapture.CaptureGdiWindow(captureForWindow); if (tmpCapture != null) { @@ -954,7 +946,7 @@ namespace Greenshot.Helpers { _capture.CaptureDetails.DpiY = graphics.DpiY; } // Set previouslyActiveWindow as foreground window - previouslyActiveWindow?.ToForeground(); + previouslyActiveWindow?.ToForeground(false); if (_capture.CaptureDetails != null) { ((Bitmap) _capture.Image)?.SetResolution(_capture.CaptureDetails.DpiX, _capture.CaptureDetails.DpiY); } diff --git a/Greenshot/Helpers/UpdateHelper.cs b/Greenshot/Helpers/UpdateHelper.cs index 68d5d5d9f..28c522c18 100644 --- a/Greenshot/Helpers/UpdateHelper.cs +++ b/Greenshot/Helpers/UpdateHelper.cs @@ -36,8 +36,8 @@ namespace Greenshot.Experimental { public static class UpdateHelper { private static readonly ILog Log = LogManager.GetLogger(typeof(UpdateHelper)); private static readonly CoreConfiguration CoreConfig = IniConfig.GetIniSection(); - private const string StableDownloadLink = "http://getgreenshot.org/downloads/"; - private const string VersionHistoryLink = "http://getgreenshot.org/version-history/"; + private const string StableDownloadLink = "https://getgreenshot.org/downloads/"; + private const string VersionHistoryLink = "https://getgreenshot.org/version-history/"; private static readonly object LockObject = new object(); private static RssFile _latestGreenshot; private static string _downloadLink = StableDownloadLink; diff --git a/Greenshot/releases/additional_files/readme.txt.template b/Greenshot/releases/additional_files/readme.txt.template index 4c85447db..984044afb 100644 --- a/Greenshot/releases/additional_files/readme.txt.template +++ b/Greenshot/releases/additional_files/readme.txt.template @@ -9,6 +9,36 @@ All details to our tickets can be found here: https://greenshot.atlassian.net @DETAILVERSION@ +This is Greenshot 1.2.9BF2, the second bugfix release for Greenshot 1.2.9 + +The following tickets are on the list for the bugfix release, the current state is not represented in UNSTABLE builds: +* BUG-2051 Scroll-lock button not usable as hotkey +* BUG-2055 Cannot paste Greenshot capture to Skype +* BUG-2056 Cannot export to external command Paint.NET if .greenshot format is used +* BUG-2068 Export to Confluence caused System.NullReferenceException +* BUG-2081 Canvas resize (Ctrl + / Ctrl -) only works via numpad keys +* BUG-2093 Shadow effects not renderingWindows 10 window border frame is not capture correctly on Windows 10 +* BUG-2095 'Save as' doesn't remember last saved directory (after restart) +* BUG-2097 Window border is not captured on Windows 10 +* BUG-2098 Greenshot opens Explorer when clicking tray while notification is still showing +* BUG-2100 ArgumentException when changing the icon size from 16 to 32 +* BUG-2101 Update to version 1.2.9.112 release -> Error 5 Access is denied +* BUG-2102 InvalidOperationException when selecting a color +* BUG-2103 ArgumentException when changing the icon size from 16 to 32 +* BUG-2104 Speechbubble can't be used after copy/paste +* BUG-2105 Window border is not captured on Windows 10 +* BUG-2108 Capture last region doesn't work +* BUG-2109 Double-click on textbox causes NullReferenceException +* BUG-2110 Missing annotations when opening .greenshot files +* BUG-2111 Drag and Drop image file on editor doesn't work +* BUG-2114 Storage location reset to default if not available during start +* BUG-2115 Error while trying to upload screenshot to Jira +* FEATURE-998 The feature "Opening last capture in explorer should select/jump to the file" wasn't available everywhere + + +1.2.9.112-9bc62ac RELEASE + + This is a bugfix release for the Greenshot 1.2.9.104-3721c10 RELEASE Bugs fixed: diff --git a/GreenshotExternalCommandPlugin/ExternalCommandDestination.cs b/GreenshotExternalCommandPlugin/ExternalCommandDestination.cs index 654c2051a..93c0e8096 100644 --- a/GreenshotExternalCommandPlugin/ExternalCommandDestination.cs +++ b/GreenshotExternalCommandPlugin/ExternalCommandDestination.cs @@ -43,33 +43,21 @@ namespace ExternalCommand { _presetCommand = commando; } - public override string Designation { - get { - return "External " + _presetCommand.Replace(',','_'); - } - } + public override string Designation => "External " + _presetCommand.Replace(',','_'); - public override string Description { - get { - return _presetCommand; - } - } + public override string Description => _presetCommand; public override IEnumerable DynamicDestinations() { yield break; } - public override Image DisplayIcon { - get { - return IconCache.IconForCommand(_presetCommand); - } - } + public override Image DisplayIcon => IconCache.IconForCommand(_presetCommand); public override ExportInformation ExportCapture(bool manuallyInitiated, ISurface surface, ICaptureDetails captureDetails) { ExportInformation exportInformation = new ExportInformation(Designation, Description); SurfaceOutputSettings outputSettings = new SurfaceOutputSettings(); + outputSettings.PreventGreenshotFormat(); - if (_presetCommand != null) { if (!config.RunInbackground.ContainsKey(_presetCommand)) { config.RunInbackground.Add(_presetCommand, true); @@ -154,12 +142,12 @@ namespace ExternalCommand { private int CallExternalCommand(string commando, string fullPath, out string output, out string error) { try { return CallExternalCommand(commando, fullPath, null, out output, out error); - } catch (Win32Exception w32ex) { + } catch (Win32Exception w32Ex) { try { return CallExternalCommand(commando, fullPath, "runas", out output, out error); } catch { - w32ex.Data.Add("commandline", config.Commandline[_presetCommand]); - w32ex.Data.Add("arguments", config.Argument[_presetCommand]); + w32Ex.Data.Add("commandline", config.Commandline[_presetCommand]); + w32Ex.Data.Add("arguments", config.Argument[_presetCommand]); throw; } } catch (Exception ex) { diff --git a/GreenshotFlickrPlugin/FlickrUtils.cs b/GreenshotFlickrPlugin/FlickrUtils.cs index 00cb56ed4..39c423cdf 100644 --- a/GreenshotFlickrPlugin/FlickrUtils.cs +++ b/GreenshotFlickrPlugin/FlickrUtils.cs @@ -42,7 +42,7 @@ namespace GreenshotFlickrPlugin { private const string FLICKR_ACCESS_TOKEN_URL = FLICKR_OAUTH_BASE_URL + "access_token"; private const string FLICKR_AUTHORIZE_URL = FLICKR_OAUTH_BASE_URL + "authorize"; private const string FLICKR_REQUEST_TOKEN_URL = FLICKR_OAUTH_BASE_URL + "request_token"; - private const string FLICKR_FARM_URL = "https://farm{0}.staticflickr.com/{1}/{2}_{3}.jpg"; + private const string FLICKR_FARM_URL = "https://farm{0}.staticflickr.com/{1}/{2}_{3}_o.{4}"; // REST private const string FLICKR_REST_URL = FLICKR_API_BASE_URL + "rest/"; private const string FLICKR_GET_INFO_URL = FLICKR_REST_URL + "?method=flickr.photos.getInfo"; @@ -131,8 +131,9 @@ namespace GreenshotFlickrPlugin { string farmId = item.Attributes["farm"].Value; string serverId = item.Attributes["server"].Value; string photoId = item.Attributes["id"].Value; - string secret = item.Attributes["secret"].Value; - return string.Format(FLICKR_FARM_URL, farmId, serverId, photoId, secret); + string originalsecret = item.Attributes["originalsecret"].Value; + string originalFormat = item.Attributes["originalformat"].Value; + return string.Format(FLICKR_FARM_URL, farmId, serverId, photoId, originalsecret, originalFormat); } } } diff --git a/GreenshotJiraPlugin/AsyncMemoryCache.cs b/GreenshotJiraPlugin/AsyncMemoryCache.cs index f7d029750..479bbbd20 100644 --- a/GreenshotJiraPlugin/AsyncMemoryCache.cs +++ b/GreenshotJiraPlugin/AsyncMemoryCache.cs @@ -29,7 +29,7 @@ using System; using System.Runtime.Caching; using System.Threading; using System.Threading.Tasks; -using Dapplo.Log.Facade; +using Dapplo.Log; #endregion diff --git a/GreenshotJiraPlugin/Forms/JiraForm.cs b/GreenshotJiraPlugin/Forms/JiraForm.cs index cfd5183e6..f4a47a347 100644 --- a/GreenshotJiraPlugin/Forms/JiraForm.cs +++ b/GreenshotJiraPlugin/Forms/JiraForm.cs @@ -36,7 +36,6 @@ namespace GreenshotJiraPlugin.Forms { private readonly JiraConnector _jiraConnector; private Issue _selectedIssue; private readonly GreenshotColumnSorter _columnSorter; - private static readonly JiraConfiguration JiraConfig = IniConfig.GetIniSection(); private static readonly CoreConfiguration CoreConfig = IniConfig.GetIniSection(); public JiraForm(JiraConnector jiraConnector) { @@ -161,14 +160,21 @@ namespace GreenshotJiraPlugin.Forms { jiraListView.LargeImageList = imageList; foreach (var issue in issues) { - var issueIcon = await _jiraConnector.GetIssueTypeBitmapAsync(issue); - imageList.Images.Add(issueIcon); - var item = new ListViewItem { - Tag = issue, - ImageIndex = imageList.Images.Count - 1 + Tag = issue }; + try + { + var issueIcon = await _jiraConnector.GetIssueTypeBitmapAsync(issue); + imageList.Images.Add(issueIcon); + item.ImageIndex = imageList.Images.Count - 1; + } + catch (Exception ex) + { + Log.Warn("Problem loading issue type, ignoring", ex); + } + item.SubItems.Add(issue.Key); item.SubItems.Add(issue.Fields.Created.ToString("d", DateTimeFormatInfo.InvariantInfo)); item.SubItems.Add(issue.Fields.Assignee?.DisplayName); diff --git a/GreenshotJiraPlugin/GreenshotJiraPlugin.csproj b/GreenshotJiraPlugin/GreenshotJiraPlugin.csproj index fe5838095..fb1e8108b 100644 --- a/GreenshotJiraPlugin/GreenshotJiraPlugin.csproj +++ b/GreenshotJiraPlugin/GreenshotJiraPlugin.csproj @@ -34,32 +34,42 @@ - - ..\packages\Dapplo.HttpExtensions.0.5.43\lib\net45\Dapplo.HttpExtensions.dll + + ..\packages\Dapplo.HttpExtensions.0.6.17\lib\net45\Dapplo.HttpExtensions.dll True - - ..\packages\Dapplo.Jira.0.1.65\lib\net45\Dapplo.Jira.dll + + ..\packages\Dapplo.Jira.0.5.8\lib\net45\Dapplo.Jira.dll True - - ..\packages\Dapplo.Log.Facade.0.5.4\lib\net45\Dapplo.Log.Facade.dll + + ..\packages\Dapplo.Log.1.0.23\lib\net45\Dapplo.Log.dll True ..\Greenshot\Lib\log4net.dll - - ..\packages\Svg.2.2.2\lib\net35\Svg.dll + + + + + ..\packages\Svg.2.3.0\lib\net35\Svg.dll True + + + + + + + @@ -94,7 +104,6 @@ - Always diff --git a/GreenshotJiraPlugin/IssueTypeBitmapCache.cs b/GreenshotJiraPlugin/IssueTypeBitmapCache.cs index b48173e8f..782d2e9ed 100644 --- a/GreenshotJiraPlugin/IssueTypeBitmapCache.cs +++ b/GreenshotJiraPlugin/IssueTypeBitmapCache.cs @@ -33,11 +33,11 @@ namespace GreenshotJiraPlugin /// public class IssueTypeBitmapCache : AsyncMemoryCache { - private readonly JiraApi _jiraApi; + private readonly IJiraClient _jiraClient; - public IssueTypeBitmapCache(JiraApi jiraApi) + public IssueTypeBitmapCache(IJiraClient jiraClient) { - _jiraApi = jiraApi; + _jiraClient = jiraClient; // Set the expire timeout to an hour ExpireTimeSpan = TimeSpan.FromHours(4); } @@ -49,7 +49,7 @@ namespace GreenshotJiraPlugin protected override async Task CreateAsync(IssueType issueType, CancellationToken cancellationToken = new CancellationToken()) { - return await _jiraApi.GetUriContentAsync(issueType.IconUri, cancellationToken).ConfigureAwait(false); + return await _jiraClient.Server.GetUriContentAsync(issueType.IconUri, cancellationToken).ConfigureAwait(false); } } } diff --git a/GreenshotJiraPlugin/JiraConnector.cs b/GreenshotJiraPlugin/JiraConnector.cs index ee9e98a2f..6d9c933f3 100644 --- a/GreenshotJiraPlugin/JiraConnector.cs +++ b/GreenshotJiraPlugin/JiraConnector.cs @@ -24,19 +24,20 @@ using System; using System.Collections.Generic; using System.Drawing; using System.IO; -using System.Linq; using System.Threading; using System.Threading.Tasks; using System.Windows.Forms; using Dapplo.HttpExtensions; +using Dapplo.HttpExtensions.Extensions; using Dapplo.Jira; +using Dapplo.Jira.Converters; using Dapplo.Jira.Entities; using Greenshot.IniFile; using GreenshotPlugin.Core; namespace GreenshotJiraPlugin { /// - /// This encapsulates the JiraApi to make it possible to change as less old Greenshot code as needed + /// This encapsulates the JiraClient to make it possible to change as less old Greenshot code as needed /// public class JiraConnector : IDisposable { private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(typeof(JiraConnector)); @@ -47,27 +48,19 @@ namespace GreenshotJiraPlugin { private DateTimeOffset _loggedInTime = DateTimeOffset.MinValue; private bool _loggedIn; private readonly int _timeout; - private JiraApi _jiraApi; + private IJiraClient _jiraClient; private IssueTypeBitmapCache _issueTypeBitmapCache; - private static readonly SvgBitmapHttpContentConverter SvgBitmapHttpContentConverterInstance = new SvgBitmapHttpContentConverter(); /// /// Initialize some basic stuff, in the case the SVG to bitmap converter /// static JiraConnector() { - if (HttpExtensionsGlobals.HttpContentConverters.All(x => x.GetType() != typeof(SvgBitmapHttpContentConverter))) - { - HttpExtensionsGlobals.HttpContentConverters.Add(SvgBitmapHttpContentConverterInstance); - } - SvgBitmapHttpContentConverterInstance.Width = CoreConfig.IconSize.Width; - SvgBitmapHttpContentConverterInstance.Height = CoreConfig.IconSize.Height; CoreConfig.PropertyChanged += (sender, args) => { if (args.PropertyName == nameof(CoreConfig.IconSize)) { - SvgBitmapHttpContentConverterInstance.Width = CoreConfig.IconSize.Width; - SvgBitmapHttpContentConverterInstance.Height = CoreConfig.IconSize.Height; + JiraPlugin.Instance.JiraConnector._jiraClient?.Behaviour.SetConfig(new SvgConfiguration { Width = CoreConfig.IconSize.Width, Height = CoreConfig.IconSize.Height }); } }; @@ -77,7 +70,7 @@ namespace GreenshotJiraPlugin { /// Dispose, logout the users /// public void Dispose() { - if (_jiraApi != null) + if (_jiraClient != null) { Task.Run(async () => await LogoutAsync()).Wait(); } @@ -104,25 +97,27 @@ namespace GreenshotJiraPlugin { /// Internal login which catches the exceptions /// /// true if login was done sucessfully - private async Task DoLoginAsync(string user, string password) + private async Task DoLoginAsync(string user, string password, CancellationToken cancellationToken = default(CancellationToken)) { if (string.IsNullOrEmpty(user) || string.IsNullOrEmpty(password)) { return false; } - _jiraApi = new JiraApi(new Uri(JiraConfig.Url)); - _issueTypeBitmapCache = new IssueTypeBitmapCache(_jiraApi); + _jiraClient = JiraClient.Create(new Uri(JiraConfig.Url)); + _jiraClient.Behaviour.SetConfig(new SvgConfiguration { Width = CoreConfig.IconSize.Width, Height = CoreConfig.IconSize.Height }); + + _issueTypeBitmapCache = new IssueTypeBitmapCache(_jiraClient); LoginInfo loginInfo; try { - loginInfo = await _jiraApi.StartSessionAsync(user, password); + loginInfo = await _jiraClient.Session.StartAsync(user, password, cancellationToken); Monitor = new JiraMonitor(); - await Monitor.AddJiraInstanceAsync(_jiraApi); + await Monitor.AddJiraInstanceAsync(_jiraClient, cancellationToken); - var favIconUri = _jiraApi.JiraBaseUri.AppendSegments("favicon.ico"); + var favIconUri = _jiraClient.JiraBaseUri.AppendSegments("favicon.ico"); try { - FavIcon = await _jiraApi.GetUriContentAsync(favIconUri); + FavIcon = await _jiraClient.Server.GetUriContentAsync(favIconUri, cancellationToken); } catch (Exception ex) { @@ -142,8 +137,8 @@ namespace GreenshotJiraPlugin { /// If there are credentials, call the real login. /// /// Task - public async Task LoginAsync() { - await LogoutAsync(); + public async Task LoginAsync(CancellationToken cancellationToken = default(CancellationToken)) { + await LogoutAsync(cancellationToken); try { // Get the system name, so the user knows where to login to var credentialsDialog = new CredentialsDialog(JiraConfig.Url) @@ -151,7 +146,7 @@ namespace GreenshotJiraPlugin { Name = null }; while (credentialsDialog.Show(credentialsDialog.Name) == DialogResult.OK) { - if (await DoLoginAsync(credentialsDialog.Name, credentialsDialog.Password)) { + if (await DoLoginAsync(credentialsDialog.Name, credentialsDialog.Password, cancellationToken)) { if (credentialsDialog.SaveChecked) { credentialsDialog.Confirm(true); } @@ -181,11 +176,11 @@ namespace GreenshotJiraPlugin { /// /// End the session, if there was one /// - public async Task LogoutAsync() { - if (_jiraApi != null && _loggedIn) + public async Task LogoutAsync(CancellationToken cancellationToken = default(CancellationToken)) { + if (_jiraClient != null && _loggedIn) { Monitor.Dispose(); - await _jiraApi.EndSessionAsync(); + await _jiraClient.Session.EndAsync(cancellationToken); _loggedIn = false; } } @@ -195,14 +190,14 @@ namespace GreenshotJiraPlugin { /// Do not use ConfigureAwait to call this, as it will move await from the UI thread. /// /// - private async Task CheckCredentialsAsync() { + private async Task CheckCredentialsAsync(CancellationToken cancellationToken = default(CancellationToken)) { if (_loggedIn) { if (_loggedInTime.AddMinutes(_timeout-1).CompareTo(DateTime.Now) < 0) { - await LogoutAsync(); - await LoginAsync(); + await LogoutAsync(cancellationToken); + await LoginAsync(cancellationToken); } } else { - await LoginAsync(); + await LoginAsync(cancellationToken); } } @@ -210,23 +205,24 @@ namespace GreenshotJiraPlugin { /// Get the favourite filters /// /// List with filters - public async Task> GetFavoriteFiltersAsync() + public async Task> GetFavoriteFiltersAsync(CancellationToken cancellationToken = default(CancellationToken)) { - await CheckCredentialsAsync(); - return await _jiraApi.GetFavoriteFiltersAsync().ConfigureAwait(false); + await CheckCredentialsAsync(cancellationToken); + return await _jiraClient.Filter.GetFavoritesAsync(cancellationToken).ConfigureAwait(false); } /// /// Get the issue for a key /// /// Jira issue key + /// CancellationToken /// Issue - public async Task GetIssueAsync(string issueKey) + public async Task GetIssueAsync(string issueKey, CancellationToken cancellationToken = default(CancellationToken)) { - await CheckCredentialsAsync(); + await CheckCredentialsAsync(cancellationToken); try { - return await _jiraApi.GetIssueAsync(issueKey).ConfigureAwait(false); + return await _jiraClient.Issue.GetAsync(issueKey, cancellationToken).ConfigureAwait(false); } catch { @@ -243,12 +239,12 @@ namespace GreenshotJiraPlugin { /// public async Task AttachAsync(string issueKey, IBinaryContainer content, CancellationToken cancellationToken = default(CancellationToken)) { - await CheckCredentialsAsync(); + await CheckCredentialsAsync(cancellationToken); using (var memoryStream = new MemoryStream()) { content.WriteToStream(memoryStream); memoryStream.Seek(0, SeekOrigin.Begin); - await _jiraApi.AttachAsync(issueKey, memoryStream, content.Filename, content.ContentType, cancellationToken).ConfigureAwait(false); + await _jiraClient.Attachment.AttachAsync(issueKey, memoryStream, content.Filename, content.ContentType, cancellationToken).ConfigureAwait(false); } } @@ -261,8 +257,8 @@ namespace GreenshotJiraPlugin { /// CancellationToken public async Task AddCommentAsync(string issueKey, string body, string visibility = null, CancellationToken cancellationToken = default(CancellationToken)) { - await CheckCredentialsAsync(); - await _jiraApi.AddCommentAsync(issueKey, body, visibility, cancellationToken).ConfigureAwait(false); + await CheckCredentialsAsync(cancellationToken); + await _jiraClient.Issue.AddCommentAsync(issueKey, body, visibility, cancellationToken).ConfigureAwait(false); } /// @@ -273,8 +269,8 @@ namespace GreenshotJiraPlugin { /// public async Task> SearchAsync(Filter filter, CancellationToken cancellationToken = default(CancellationToken)) { - await CheckCredentialsAsync(); - var searchResult = await _jiraApi.SearchAsync(filter.Jql, 20, new[] { "summary", "reporter", "assignee", "created", "issuetype" }, cancellationToken).ConfigureAwait(false); + await CheckCredentialsAsync(cancellationToken); + var searchResult = await _jiraClient.Issue.SearchAsync(filter.Jql, 20, new[] { "summary", "reporter", "assignee", "created", "issuetype" }, cancellationToken).ConfigureAwait(false); return searchResult.Issues; } @@ -292,7 +288,7 @@ namespace GreenshotJiraPlugin { /// /// Get the base uri /// - public Uri JiraBaseUri => _jiraApi.JiraBaseUri; + public Uri JiraBaseUri => _jiraClient.JiraBaseUri; /// /// Is the user "logged in? diff --git a/GreenshotJiraPlugin/JiraDestination.cs b/GreenshotJiraPlugin/JiraDestination.cs index 1b35e6fde..e6f60cd46 100644 --- a/GreenshotJiraPlugin/JiraDestination.cs +++ b/GreenshotJiraPlugin/JiraDestination.cs @@ -78,9 +78,17 @@ namespace GreenshotJiraPlugin { { if (_jiraIssue != null) { - displayIcon = jiraConnector.GetIssueTypeBitmapAsync(_jiraIssue).Result; + // Try to get the issue type as icon + try + { + displayIcon = jiraConnector.GetIssueTypeBitmapAsync(_jiraIssue).Result; + } + catch (Exception ex) + { + Log.Warn($"Problem loading issue type for {_jiraIssue.Key}, ignoring", ex); + } } - else + if (displayIcon == null) { displayIcon = jiraConnector.FavIcon; } diff --git a/GreenshotJiraPlugin/JiraMonitor.cs b/GreenshotJiraPlugin/JiraMonitor.cs index 81605bddf..562bf6c4c 100644 --- a/GreenshotJiraPlugin/JiraMonitor.cs +++ b/GreenshotJiraPlugin/JiraMonitor.cs @@ -26,7 +26,7 @@ using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; using Dapplo.Jira; -using Dapplo.Log.Facade; +using Dapplo.Log; using GreenshotJiraPlugin.Hooking; namespace GreenshotJiraPlugin @@ -42,9 +42,10 @@ namespace GreenshotJiraPlugin private static readonly LogSource Log = new LogSource(); private readonly Regex _jiraKeyPattern = new Regex(@"[A-Z][A-Z0-9]+\-[0-9]+"); private readonly WindowsTitleMonitor _monitor; - private readonly IList _jiraInstances = new List(); - private readonly IDictionary _projectJiraApiMap = new Dictionary(); + private readonly IList _jiraInstances = new List(); + private readonly IDictionary _projectJiraClientMap = new Dictionary(); private readonly int _maxEntries; + // TODO: Add issues from issueHistory (JQL -> Where.IssueKey.InIssueHistory()) private IDictionary _recentJiras = new Dictionary(); /// @@ -92,10 +93,10 @@ namespace GreenshotJiraPlugin /// Retrieve the API belonging to a JiraDetails /// /// - /// JiraAPI - public JiraApi GetJiraApiForKey(JiraDetails jiraDetails) + /// IJiraClient + public IJiraClient GetJiraClientForKey(JiraDetails jiraDetails) { - return _projectJiraApiMap[jiraDetails.ProjectKey]; + return _projectJiraClientMap[jiraDetails.ProjectKey]; } /// @@ -116,17 +117,17 @@ namespace GreenshotJiraPlugin /// /// /// - public async Task AddJiraInstanceAsync(JiraApi jiraInstance, CancellationToken token = default(CancellationToken)) + public async Task AddJiraInstanceAsync(IJiraClient jiraInstance, CancellationToken token = default(CancellationToken)) { _jiraInstances.Add(jiraInstance); - var projects = await jiraInstance.GetProjectsAsync(token).ConfigureAwait(false); + var projects = await jiraInstance.Project.GetAllAsync(cancellationToken: token).ConfigureAwait(false); if (projects != null) { foreach (var project in projects) { - if (!_projectJiraApiMap.ContainsKey(project.Key)) + if (!_projectJiraClientMap.ContainsKey(project.Key)) { - _projectJiraApiMap.Add(project.Key, jiraInstance); + _projectJiraClientMap.Add(project.Key, jiraInstance); } } } @@ -141,10 +142,10 @@ namespace GreenshotJiraPlugin { try { - JiraApi jiraApi; - if (_projectJiraApiMap.TryGetValue(jiraDetails.ProjectKey, out jiraApi)) + IJiraClient jiraClient; + if (_projectJiraClientMap.TryGetValue(jiraDetails.ProjectKey, out jiraClient)) { - var issue = await jiraApi.GetIssueAsync(jiraDetails.JiraKey).ConfigureAwait(false); + var issue = await jiraClient.Issue.GetAsync(jiraDetails.JiraKey).ConfigureAwait(false); jiraDetails.JiraIssue = issue; } // Send event @@ -178,9 +179,9 @@ namespace GreenshotJiraPlugin var projectKey = jiraKeyParts[0]; var jiraId = jiraKeyParts[1]; - JiraApi jiraApi; + IJiraClient jiraClient; // Check if we have a JIRA instance with a project for this key - if (_projectJiraApiMap.TryGetValue(projectKey, out jiraApi)) + if (_projectJiraClientMap.TryGetValue(projectKey, out jiraClient)) { // We have found a project for this _jira key, so it must be a valid & known JIRA JiraDetails currentJiraDetails; @@ -207,8 +208,7 @@ namespace GreenshotJiraPlugin if (_recentJiras.Count > _maxEntries) { // Add it to the list of recent Jiras - IList clonedList = new List(_recentJiras.Values); - _recentJiras = (from jiraDetails in clonedList + _recentJiras = (from jiraDetails in _recentJiras.Values.ToList() orderby jiraDetails.SeenAt descending select jiraDetails).Take(_maxEntries).ToDictionary(jd => jd.JiraKey, jd => jd); } diff --git a/GreenshotJiraPlugin/JiraPlugin.cs b/GreenshotJiraPlugin/JiraPlugin.cs index a7240293e..ed26d888d 100644 --- a/GreenshotJiraPlugin/JiraPlugin.cs +++ b/GreenshotJiraPlugin/JiraPlugin.cs @@ -24,7 +24,9 @@ using Greenshot.IniFile; using Greenshot.Plugin; using System; using System.Threading.Tasks; -using Dapplo.Log.Facade; +using Dapplo.Log; +using Greenshot.Forms; +using Greenshot.Helpers; using GreenshotJiraPlugin.Forms; using GreenshotPlugin.Core; using log4net; @@ -57,6 +59,22 @@ namespace GreenshotJiraPlugin { public JiraPlugin() { _instance = this; + // Added to prevent Greenshot from shutting down when there was an exception in a Task + TaskScheduler.UnobservedTaskException += (sender, args) => + { + try + { + Exception exceptionToLog = args.Exception; + string exceptionText = EnvironmentInfo.BuildReport(exceptionToLog); + Log.Error("Exception caught in the UnobservedTaskException handler."); + Log.Error(exceptionText); + new BugReportForm(exceptionText).ShowDialog(); + } + finally + { + args.SetObserved(); + } + }; } public IEnumerable Destinations() { diff --git a/GreenshotJiraPlugin/Log4NetLogger.cs b/GreenshotJiraPlugin/Log4NetLogger.cs index 1b7ce2661..3018a8823 100644 --- a/GreenshotJiraPlugin/Log4NetLogger.cs +++ b/GreenshotJiraPlugin/Log4NetLogger.cs @@ -19,7 +19,7 @@ * along with this program. If not, see . */ -using Dapplo.Log.Facade; +using Dapplo.Log; using log4net; namespace GreenshotJiraPlugin diff --git a/GreenshotJiraPlugin/SvgBitmapHttpContentConverter.cs b/GreenshotJiraPlugin/SvgBitmapHttpContentConverter.cs deleted file mode 100644 index 47b3a24d1..000000000 --- a/GreenshotJiraPlugin/SvgBitmapHttpContentConverter.cs +++ /dev/null @@ -1,126 +0,0 @@ -/* - * Greenshot - a free and open source screenshot tool - * Copyright (C) 2007-2016 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.Collections.Generic; -using System.Drawing; -using System.IO; -using System.Threading; -using System.Threading.Tasks; -using Dapplo.HttpExtensions; -using Dapplo.HttpExtensions.ContentConverter; -using Dapplo.HttpExtensions.Extensions; -using Dapplo.HttpExtensions.Support; -using Dapplo.Log.Facade; -using System.Net.Http; -using System.Net.Http.Headers; - -namespace GreenshotJiraPlugin -{ - /// - /// This adds SVG image support for the Jira Plugin - /// - public class SvgBitmapHttpContentConverter : IHttpContentConverter - { - private static readonly LogSource Log = new LogSource(); - private static readonly IList SupportedContentTypes = new List(); - - static SvgBitmapHttpContentConverter() - { - SupportedContentTypes.Add(MediaTypes.Svg.EnumValueOf()); - } - - /// - public int Order => 0; - - public int Width { get; set; } - - public int Height { get; set; } - - /// - /// This checks if the HttpContent can be converted to a Bitmap and is assignable to the specified Type - /// - /// This should be something we can assign Bitmap to - /// HttpContent to process - /// true if it can convert - public bool CanConvertFromHttpContent(Type typeToConvertTo, HttpContent httpContent) - { - if (typeToConvertTo == typeof(object) || !typeToConvertTo.IsAssignableFrom(typeof(Bitmap))) - { - return false; - } - var httpBehaviour = HttpBehaviour.Current; - return !httpBehaviour.ValidateResponseContentType || SupportedContentTypes.Contains(httpContent.GetContentType()); - } - - /// - public async Task ConvertFromHttpContentAsync(Type resultType, HttpContent httpContent, CancellationToken cancellationToken = default(CancellationToken)) - { - if (!CanConvertFromHttpContent(resultType, httpContent)) - { - var exMessage = "CanConvertFromHttpContent resulted in false, ConvertFromHttpContentAsync is not supposed to be called."; - Log.Error().WriteLine(exMessage); - throw new NotSupportedException(exMessage); - } - using (var memoryStream = (MemoryStream) await StreamHttpContentConverter.Instance.ConvertFromHttpContentAsync(typeof(MemoryStream), httpContent, cancellationToken).ConfigureAwait(false)) - { - Log.Debug().WriteLine("Creating a Bitmap from the SVG."); - var svgImage = new SvgImage(memoryStream) - { - Height = Height, - Width = Width - }; - return svgImage.Image; - } - } - - /// - public bool CanConvertToHttpContent(Type typeToConvert, object content) - { - return false; - } - - /// - public HttpContent ConvertToHttpContent(Type typeToConvert, object content) - { - return null; - } - - /// - public void AddAcceptHeadersForType(Type resultType, HttpRequestMessage httpRequestMessage) - { - if (resultType == null) - { - throw new ArgumentNullException(nameof(resultType)); - } - if (httpRequestMessage == null) - { - throw new ArgumentNullException(nameof(httpRequestMessage)); - } - if (resultType == typeof(object) || !resultType.IsAssignableFrom(typeof(Bitmap))) - { - return; - } - httpRequestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(MediaTypes.Svg.EnumValueOf())); - Log.Debug().WriteLine("Modified the header(s) of the HttpRequestMessage: Accept: {0}", httpRequestMessage.Headers.Accept); - } - } -} diff --git a/GreenshotJiraPlugin/packages.config b/GreenshotJiraPlugin/packages.config index fade6b5dd..5c071995c 100644 --- a/GreenshotJiraPlugin/packages.config +++ b/GreenshotJiraPlugin/packages.config @@ -1,8 +1,8 @@  - - - + + + - + \ No newline at end of file diff --git a/GreenshotPlugin/Controls/HotkeyControl.cs b/GreenshotPlugin/Controls/HotkeyControl.cs index af12f6d93..590f6090a 100644 --- a/GreenshotPlugin/Controls/HotkeyControl.cs +++ b/GreenshotPlugin/Controls/HotkeyControl.cs @@ -20,7 +20,6 @@ */ using System; -using System.Collections; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Runtime.InteropServices; @@ -48,15 +47,6 @@ namespace GreenshotPlugin.Controls { private static int _hotKeyCounter = 1; private const uint WM_HOTKEY = 0x312; private static IntPtr _hotkeyHwnd; - -// static HotkeyControl() { -// StringBuilder keyName = new StringBuilder(); -// for(uint sc = 0; sc < 500; sc++) { -// if (GetKeyNameText(sc << 16, keyName, 100) != 0) { -// LOG.DebugFormat("SC {0} = {1}", sc, keyName); -// } -// } -// } [SuppressMessage("ReSharper", "InconsistentNaming")] public enum Modifiers : uint { @@ -96,8 +86,8 @@ namespace GreenshotPlugin.Controls { // ArrayLists used to enforce the use of proper modifiers. // Shift+A isn't a valid hotkey, for instance, as it would screw up when the user is typing. - private readonly ArrayList _needNonShiftModifier; - private readonly ArrayList _needNonAltGrModifier; + private readonly IList _needNonShiftModifier = new List(); + private readonly IList _needNonAltGrModifier = new List(); private readonly ContextMenu _dummy = new ContextMenu(); @@ -138,9 +128,6 @@ namespace GreenshotPlugin.Controls { KeyUp += HotkeyControl_KeyUp; KeyDown += HotkeyControl_KeyDown; - // Fill the ArrayLists that contain all invalid hotkey combinations - _needNonShiftModifier = new ArrayList(); - _needNonAltGrModifier = new ArrayList(); PopulateModifierLists(); } @@ -180,8 +167,6 @@ namespace GreenshotPlugin.Controls { _needNonShiftModifier.Add((int)Keys.Return); _needNonShiftModifier.Add((int)Keys.Escape); _needNonShiftModifier.Add((int)Keys.NumLock); - _needNonShiftModifier.Add((int)Keys.Scroll); - _needNonShiftModifier.Add((int)Keys.Pause); // Ctrl+Alt + 0 - 9 for (Keys k = Keys.D0; k <= Keys.D9; k++) { diff --git a/GreenshotPlugin/Controls/SaveImageFileDialog.cs b/GreenshotPlugin/Controls/SaveImageFileDialog.cs index 10621c323..c077b4093 100644 --- a/GreenshotPlugin/Controls/SaveImageFileDialog.cs +++ b/GreenshotPlugin/Controls/SaveImageFileDialog.cs @@ -68,6 +68,7 @@ namespace GreenshotPlugin.Controls { ApplyFilterOptions(); string initialDirectory = null; try { + conf.ValidateAndCorrectOutputFileAsFullpath(); initialDirectory = Path.GetDirectoryName(conf.OutputFileAsFullpath); } catch { LOG.WarnFormat("OutputFileAsFullpath was set to {0}, ignoring due to problem in path.", conf.OutputFileAsFullpath); @@ -90,12 +91,12 @@ namespace GreenshotPlugin.Controls { PrepareFilterOptions(); string fdf = ""; int preselect = 0; - var outputFileFormatAsString = Enum.GetName(typeof(OutputFormat), conf.OutputFileFormat); + var outputFileFormatAsString = Enum.GetName(typeof(OutputFormat), conf.OutputFileFormat); for(int i=0; i<_filterOptions.Length; i++){ FilterOption fo = _filterOptions[i]; fdf += fo.Label + "|*." + fo.Extension + "|"; - if(outputFileFormatAsString == fo.Extension) - preselect = i; + if(outputFileFormatAsString == fo.Extension) + preselect = i; } fdf = fdf.Substring(0, fdf.Length-1); SaveFileDialog.Filter = fdf; diff --git a/GreenshotPlugin/Core/ClipboardHelper.cs b/GreenshotPlugin/Core/ClipboardHelper.cs index a2462708d..e46821b22 100644 --- a/GreenshotPlugin/Core/ClipboardHelper.cs +++ b/GreenshotPlugin/Core/ClipboardHelper.cs @@ -813,10 +813,10 @@ EndSelection:<<<<<<<4 string[] dropFileNames = (string[]) dataObject.GetData(DataFormats.FileDrop); if (dropFileNames != null && dropFileNames.Length > 0) { - return dropFileNames.Where(filename => !string.IsNullOrEmpty(filename)) + return dropFileNames + .Where(filename => !string.IsNullOrEmpty(filename)) .Where(Path.HasExtension) - .Select(filename => Path.GetExtension(filename).ToLowerInvariant()) - .Where(ext => ImageHelper.StreamConverters.Keys.Contains(ext)); + .Where(filename => ImageHelper.StreamConverters.Keys.Contains(Path.GetExtension(filename).ToLowerInvariant().Substring(1))); } return Enumerable.Empty(); } diff --git a/GreenshotPlugin/Core/CoreConfiguration.cs b/GreenshotPlugin/Core/CoreConfiguration.cs index b89c7a5f2..56958d64f 100644 --- a/GreenshotPlugin/Core/CoreConfiguration.cs +++ b/GreenshotPlugin/Core/CoreConfiguration.cs @@ -274,6 +274,9 @@ namespace GreenshotPlugin.Core { [IniProperty("LastCapturedRegion", Description = "The last used region, for reuse in the capture last region")] public Rectangle LastCapturedRegion { get; set; } + [IniProperty("Win10BorderCrop", Description = "The capture is cropped with these settings, e.g. when you don't want to color around it -1,-1"), DefaultValue("0,0")] + public Size Win10BorderCrop { get; set; } + private Size _iconSize; [IniProperty("IconSize", Description = "Defines the size of the icons (e.g. for the buttons in the editor), default value 16,16 anything bigger will cause scaling", DefaultValue = "16,16")] public Size IconSize { @@ -350,9 +353,8 @@ namespace GreenshotPlugin.Core { case "OutputFileAsFullpath": if (IniConfig.IsPortable) { return Path.Combine(Application.StartupPath, @"..\..\Documents\Pictures\Greenshots\dummy.png"); - } else { - return Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop),"dummy.png"); } + return Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop),"dummy.png"); case "OutputFilePath": if (IniConfig.IsPortable) { string pafOutputFilePath = Path.Combine(Application.StartupPath, @"..\..\Documents\Pictures\Greenshots"); @@ -523,13 +525,27 @@ namespace GreenshotPlugin.Core { if (WebRequestReadWriteTimeout < 1) { WebRequestReadWriteTimeout = 100; } + } - // Added for BUG-1992, reset the OutputFilePath / OutputFileAsFullpath if they don't exist (e.g. the configuration is used on a different PC) + /// + /// Validate the OutputFilePath, and if this is not correct it will be set to the default + /// Added for BUG-1992, reset the OutputFilePath / OutputFileAsFullpath if they don't exist (e.g. the configuration is used on a different PC) + /// + public void ValidateAndCorrectOutputFilePath() + { if (!Directory.Exists(OutputFilePath)) { OutputFilePath = GetDefault(nameof(OutputFilePath)) as string; } - if (!File.Exists(OutputFileAsFullpath)) + } + /// + /// Validate the OutputFileAsFullpath, and if this is not correct it will be set to the default + /// Added for BUG-1992, reset the OutputFilePath / OutputFileAsFullpath if they don't exist (e.g. the configuration is used on a different PC) + /// + public void ValidateAndCorrectOutputFileAsFullpath() + { + var outputFilePath = Path.GetDirectoryName(OutputFileAsFullpath); + if (outputFilePath == null || (!File.Exists(OutputFileAsFullpath) && !Directory.Exists(outputFilePath))) { OutputFileAsFullpath = GetDefault(nameof(OutputFileAsFullpath)) as string; } diff --git a/GreenshotPlugin/Core/ExplorerHelper.cs b/GreenshotPlugin/Core/ExplorerHelper.cs new file mode 100644 index 000000000..7f65294f4 --- /dev/null +++ b/GreenshotPlugin/Core/ExplorerHelper.cs @@ -0,0 +1,54 @@ +using System; +using System.Diagnostics; +using System.IO; + +namespace GreenshotPlugin.Core +{ + /// + /// Simple utility for the explorer + /// + public static class ExplorerHelper + { + /// + /// Open the path in the windows explorer. + /// If the path is a directory, it will just open the explorer with that directory. + /// If the path is a file, the explorer is opened with the directory and the file is selected. + /// + /// Path to file or directory + public static bool OpenInExplorer(string path) + { + if (path == null) + { + return false; + } + try + { + // Check if path is a directory + if (Directory.Exists(path)) + { + using (Process.Start(path)) + { + return true; + } + } + // Check if path is a file + else if (File.Exists(path)) + { + // Start the explorer process and select the file + using (var explorer = Process.Start("explorer.exe", $"/select,\"{path}\"")) + { + explorer?.WaitForInputIdle(500); + return true; + } + } + } + catch (Exception ex) + { + // Make sure we show what we tried to open in the exception + ex.Data.Add("path", path); + throw; + } + return false; + } + } +} diff --git a/GreenshotPlugin/Core/ImageOutput.cs b/GreenshotPlugin/Core/ImageOutput.cs index 7a2e40f37..cbd4cec9b 100644 --- a/GreenshotPlugin/Core/ImageOutput.cs +++ b/GreenshotPlugin/Core/ImageOutput.cs @@ -105,7 +105,7 @@ namespace GreenshotPlugin.Core { bool useMemoryStream = false; MemoryStream memoryStream = null; if (outputSettings.Format == OutputFormat.greenshot && surface == null) { - throw new ArgumentException("Surface needs to be se when using OutputFormat.Greenshot"); + throw new ArgumentException("Surface needs to be set when using OutputFormat.Greenshot"); } try { diff --git a/GreenshotPlugin/Core/WindowsHelper.cs b/GreenshotPlugin/Core/WindowsHelper.cs index 1a6db39a1..90e6e6767 100644 --- a/GreenshotPlugin/Core/WindowsHelper.cs +++ b/GreenshotPlugin/Core/WindowsHelper.cs @@ -770,7 +770,7 @@ namespace GreenshotPlugin.Core { { // Somehow DWM doesn't calculate it corectly, there is a 1 pixel border around the capture // Remove this border, currently it's fixed but TODO: Make it depend on the OS? - windowRect.Inflate(-1, -1); + windowRect.Inflate(Conf.Win10BorderCrop); _previousWindowRectangle = windowRect; _lastWindowRectangleRetrieveTime = now; return windowRect; @@ -1001,7 +1001,7 @@ namespace GreenshotPlugin.Core { // TODO: Also 8.x? if (Environment.OSVersion.IsWindows10()) { - captureRectangle.Inflate(-1, -1); + captureRectangle.Inflate(Conf.Win10BorderCrop); } if (autoMode) { @@ -1250,11 +1250,13 @@ namespace GreenshotPlugin.Core { size = result ? new Size((int)windowInfo.cxWindowBorders, (int)windowInfo.cyWindowBorders) : Size.Empty; return result; } - + /// /// Set the window as foreground window /// - public static void ToForeground(IntPtr handle) + /// hWnd of the window to bring to the foreground + /// bool with true to use a trick to really bring the window to the foreground + public static void ToForeground(IntPtr handle, bool workaround = true) { // Do nothing if the window is already in the foreground if (User32.GetForegroundWindow() == handle) @@ -1266,10 +1268,14 @@ namespace GreenshotPlugin.Core { const int EXTENDEDKEY = 0x1; const int KEYUP = 0x2; - // Simulate an "ALT" key press. - User32.keybd_event(ALT, 0x45, EXTENDEDKEY | 0, 0); - // Simulate an "ALT" key release. - User32.keybd_event(ALT, 0x45, EXTENDEDKEY | KEYUP, 0); + // See https://msdn.microsoft.com/en-us/library/windows/desktop/ms633539(v=vs.85).aspx + if (workaround) + { + // Simulate an "ALT" key press. + User32.keybd_event(ALT, 0x45, EXTENDEDKEY | 0, 0); + // Simulate an "ALT" key release. + User32.keybd_event(ALT, 0x45, EXTENDEDKEY | KEYUP, 0); + } // Show window in forground. User32.SetForegroundWindow(handle); @@ -1278,8 +1284,9 @@ namespace GreenshotPlugin.Core { /// /// Set the window as foreground window /// - public void ToForeground() { - ToForeground(Handle); + /// true to use a workaround, otherwise the window might only flash + public void ToForeground(bool workaround = true) { + ToForeground(Handle, workaround); } /// diff --git a/GreenshotPlugin/GreenshotPlugin.csproj b/GreenshotPlugin/GreenshotPlugin.csproj index 3629db74c..e9f52382f 100644 --- a/GreenshotPlugin/GreenshotPlugin.csproj +++ b/GreenshotPlugin/GreenshotPlugin.csproj @@ -47,6 +47,7 @@ Component + diff --git a/GreenshotPlugin/Interfaces/Plugin/PluginInterfaces.cs b/GreenshotPlugin/Interfaces/Plugin/PluginInterfaces.cs index 63ad027cb..4eb8c918d 100644 --- a/GreenshotPlugin/Interfaces/Plugin/PluginInterfaces.cs +++ b/GreenshotPlugin/Interfaces/Plugin/PluginInterfaces.cs @@ -98,8 +98,15 @@ namespace Greenshot.Plugin { ReduceColors = reduceColors; } - public SurfaceOutputSettings PreventGreenshotFormat() { - if (Format == OutputFormat.greenshot) { + /// + /// BUG-2056 reported a logical issue, using greenshot format as the default causes issues with the external commands. + /// + /// this for fluent API usage + public SurfaceOutputSettings PreventGreenshotFormat() + { + // If OutputFormat is Greenshot, use PNG instead. + if (Format == OutputFormat.greenshot) + { Format = OutputFormat.png; } return this; diff --git a/GreenshotPlugin/UnmanagedHelpers/GDIplus.cs b/GreenshotPlugin/UnmanagedHelpers/GDIplus.cs index f8671f335..05b202105 100644 --- a/GreenshotPlugin/UnmanagedHelpers/GDIplus.cs +++ b/GreenshotPlugin/UnmanagedHelpers/GDIplus.cs @@ -171,11 +171,16 @@ namespace GreenshotPlugin.UnmanagedHelpers { /// /// /// false if blur is not possible - public static bool IsBlurPossible(int radius) { + public static bool IsBlurPossible(int radius) + { if (!_isBlurEnabled) { return false; } - return Environment.OSVersion.Version.Minor < 2 || radius >= 20; + if (Environment.OSVersion.Version.Major == 6 && Environment.OSVersion.Version.Minor < 2) + { + return true; + } + return Environment.OSVersion.Version.Major > 6 && radius >= 20; } /// diff --git a/GreenshotWin10Plugin/Win10ShareDestination.cs b/GreenshotWin10Plugin/Win10ShareDestination.cs index 780f568cd..8933b9b7a 100644 --- a/GreenshotWin10Plugin/Win10ShareDestination.cs +++ b/GreenshotWin10Plugin/Win10ShareDestination.cs @@ -70,8 +70,11 @@ namespace GreenshotWin10Plugin using (var logoStream = new MemoryRandomAccessStream()) using (var thumbnailStream = new MemoryRandomAccessStream()) { + var outputSettings = new SurfaceOutputSettings(); + outputSettings.PreventGreenshotFormat(); + // Create capture for export - ImageOutput.SaveToStream(surface, imageStream, new SurfaceOutputSettings()); + ImageOutput.SaveToStream(surface, imageStream, outputSettings); imageStream.Position = 0; Log.Info("Created RandomAccessStreamReference for the image"); var imageRandomAccessStreamReference = RandomAccessStreamReference.CreateFromStream(imageStream); @@ -83,7 +86,7 @@ namespace GreenshotWin10Plugin { using (var thumbnail = ImageHelper.CreateThumbnail(tmpImageForThumbnail, 240, 160)) { - ImageOutput.SaveToStream(thumbnail, null, thumbnailStream, new SurfaceOutputSettings()); + ImageOutput.SaveToStream(thumbnail, null, thumbnailStream, outputSettings); thumbnailStream.Position = 0; thumbnailRandomAccessStreamReference = RandomAccessStreamReference.CreateFromStream(thumbnailStream); Log.Info("Created RandomAccessStreamReference for the thumbnail"); @@ -94,7 +97,7 @@ namespace GreenshotWin10Plugin { using (var logoThumbnail = ImageHelper.CreateThumbnail(logo, 30, 30)) { - ImageOutput.SaveToStream(logoThumbnail, null, logoStream, new SurfaceOutputSettings()); + ImageOutput.SaveToStream(logoThumbnail, null, logoStream, outputSettings); logoStream.Position = 0; logoRandomAccessStreamReference = RandomAccessStreamReference.CreateFromStream(logoStream); Log.Info("Created RandomAccessStreamReference for the logo"); diff --git a/appveyor12.yml b/appveyor12.yml index fd16f2741..0e7ad7491 100644 --- a/appveyor12.yml +++ b/appveyor12.yml @@ -1,7 +1,7 @@ version: 1.2.9.{build} branches: only: - - release/1.2.9 + - release/1.2.9BF2 skip_tags: true configuration: Release platform: Any CPU