diff --git a/src/Greenshot.Plugin.Box/BoxConfiguration.cs b/src/Greenshot.Plugin.Box/BoxConfiguration.cs index a940b319e..a4d83ef68 100644 --- a/src/Greenshot.Plugin.Box/BoxConfiguration.cs +++ b/src/Greenshot.Plugin.Box/BoxConfiguration.cs @@ -25,56 +25,55 @@ using Greenshot.Plugin.Box.Forms; using GreenshotPlugin.Core; using GreenshotPlugin.IniFile; -namespace Greenshot.Plugin.Box { - /// - /// Description of ImgurConfiguration. - /// - [IniSection("Box", Description = "Greenshot Box Plugin configuration")] - public class BoxConfiguration : IniSection { - [IniProperty("UploadFormat", Description="What file type to use for uploading", DefaultValue="png")] - public OutputFormat UploadFormat { get; set; } +namespace Greenshot.Plugin.Box +{ + /// + /// Description of ImgurConfiguration. + /// + [IniSection("Box", Description = "Greenshot Box Plugin configuration")] + public class BoxConfiguration : IniSection + { + [IniProperty("UploadFormat", Description = "What file type to use for uploading", DefaultValue = "png")] + public OutputFormat UploadFormat { get; set; } - [IniProperty("UploadJpegQuality", Description="JPEG file save quality in %.", DefaultValue="80")] - public int UploadJpegQuality { get; set; } + [IniProperty("UploadJpegQuality", Description = "JPEG file save quality in %.", DefaultValue = "80")] + public int UploadJpegQuality { get; set; } - [IniProperty("AfterUploadLinkToClipBoard", Description = "After upload send Box link to clipboard.", DefaultValue = "true")] - public bool AfterUploadLinkToClipBoard { get; set; } + [IniProperty("AfterUploadLinkToClipBoard", Description = "After upload send Box link to clipboard.", DefaultValue = "true")] + public bool AfterUploadLinkToClipBoard { get; set; } - [IniProperty("UseSharedLink", Description = "Use the shared link, instead of the private, on the clipboard", DefaultValue = "True")] - public bool UseSharedLink { get; set; } - [IniProperty("FolderId", Description = "Folder ID to upload to, only change if you know what you are doing!", DefaultValue = "0")] - public string FolderId { get; set; } + [IniProperty("UseSharedLink", Description = "Use the shared link, instead of the private, on the clipboard", DefaultValue = "True")] + public bool UseSharedLink { get; set; } - [IniProperty("RefreshToken", Description = "Box authorization refresh Token", Encrypted = true)] - public string RefreshToken { get; set; } + [IniProperty("FolderId", Description = "Folder ID to upload to, only change if you know what you are doing!", DefaultValue = "0")] + public string FolderId { get; set; } - /// - /// Not stored - /// - public string AccessToken { - get; - set; - } + [IniProperty("RefreshToken", Description = "Box authorization refresh Token", Encrypted = true)] + public string RefreshToken { get; set; } - /// - /// Not stored - /// - public DateTimeOffset AccessTokenExpires { - get; - set; - } + /// + /// Not stored + /// + public string AccessToken { get; set; } - /// - /// A form for token - /// - /// bool true if OK was pressed, false if cancel - public bool ShowConfigDialog() { - DialogResult result = new SettingsForm().ShowDialog(); - if (result == DialogResult.OK) { - return true; - } - return false; - } + /// + /// Not stored + /// + public DateTimeOffset AccessTokenExpires { get; set; } - } -} + /// + /// A form for token + /// + /// bool true if OK was pressed, false if cancel + public bool ShowConfigDialog() + { + DialogResult result = new SettingsForm().ShowDialog(); + if (result == DialogResult.OK) + { + return true; + } + + return false; + } + } +} \ No newline at end of file diff --git a/src/Greenshot.Plugin.Box/BoxDestination.cs b/src/Greenshot.Plugin.Box/BoxDestination.cs index 3c1a0622d..0bc547b88 100644 --- a/src/Greenshot.Plugin.Box/BoxDestination.cs +++ b/src/Greenshot.Plugin.Box/BoxDestination.cs @@ -24,33 +24,42 @@ using System.Drawing; using GreenshotPlugin.Core; using GreenshotPlugin.Interfaces; -namespace Greenshot.Plugin.Box { - public class BoxDestination : AbstractDestination { - private readonly BoxPlugin _plugin; - public BoxDestination(BoxPlugin plugin) { - _plugin = plugin; - } - - public override string Designation => "Box"; +namespace Greenshot.Plugin.Box +{ + public class BoxDestination : AbstractDestination + { + private readonly BoxPlugin _plugin; - public override string Description => Language.GetString("box", LangKey.upload_menu_item); + public BoxDestination(BoxPlugin plugin) + { + _plugin = plugin; + } - public override Image DisplayIcon { - get { - ComponentResourceManager resources = new ComponentResourceManager(typeof(BoxPlugin)); - return (Image)resources.GetObject("Box"); - } - } + public override string Designation => "Box"; - public override ExportInformation ExportCapture(bool manuallyInitiated, ISurface surface, ICaptureDetails captureDetails) { - ExportInformation exportInformation = new ExportInformation(Designation, Description); - string uploadUrl = _plugin.Upload(captureDetails, surface); - if (uploadUrl != null) { - exportInformation.ExportMade = true; - exportInformation.Uri = uploadUrl; - } - ProcessExport(exportInformation, surface); - return exportInformation; - } - } -} + public override string Description => Language.GetString("box", LangKey.upload_menu_item); + + public override Image DisplayIcon + { + get + { + ComponentResourceManager resources = new ComponentResourceManager(typeof(BoxPlugin)); + return (Image) resources.GetObject("Box"); + } + } + + public override ExportInformation ExportCapture(bool manuallyInitiated, ISurface surface, ICaptureDetails captureDetails) + { + ExportInformation exportInformation = new ExportInformation(Designation, Description); + string uploadUrl = _plugin.Upload(captureDetails, surface); + if (uploadUrl != null) + { + exportInformation.ExportMade = true; + exportInformation.Uri = uploadUrl; + } + + ProcessExport(exportInformation, surface); + return exportInformation; + } + } +} \ No newline at end of file diff --git a/src/Greenshot.Plugin.Box/BoxEntities.cs b/src/Greenshot.Plugin.Box/BoxEntities.cs index bf5476f4e..d99612377 100644 --- a/src/Greenshot.Plugin.Box/BoxEntities.cs +++ b/src/Greenshot.Plugin.Box/BoxEntities.cs @@ -22,39 +22,35 @@ using System.Collections.Generic; using System.Runtime.Serialization; -namespace Greenshot.Plugin.Box { - [DataContract] - public class Authorization { - [DataMember(Name = "access_token")] - public string AccessToken { get; set; } - [DataMember(Name = "expires_in")] - public int ExpiresIn { get; set; } - [DataMember(Name = "refresh_token")] - public string RefreshToken { get; set; } - [DataMember(Name = "token_type")] - public string TokenType { get; set; } - } - [DataContract] - public class SharedLink { - [DataMember(Name = "url")] - public string Url { get; set; } - [DataMember(Name = "download_url")] - public string DownloadUrl { get; set; } - } +namespace Greenshot.Plugin.Box +{ + [DataContract] + public class Authorization + { + [DataMember(Name = "access_token")] public string AccessToken { get; set; } + [DataMember(Name = "expires_in")] public int ExpiresIn { get; set; } + [DataMember(Name = "refresh_token")] public string RefreshToken { get; set; } + [DataMember(Name = "token_type")] public string TokenType { get; set; } + } - [DataContract] - public class FileEntry { - [DataMember(Name = "id")] - public string Id { get; set; } - [DataMember(Name = "name")] - public string Name { get; set; } - [DataMember(Name = "shared_link")] - public SharedLink SharedLink { get; set; } - } + [DataContract] + public class SharedLink + { + [DataMember(Name = "url")] public string Url { get; set; } + [DataMember(Name = "download_url")] public string DownloadUrl { get; set; } + } - [DataContract] - public class Upload { - [DataMember(Name = "entries")] - public List Entries { get; set; } - } -} + [DataContract] + public class FileEntry + { + [DataMember(Name = "id")] public string Id { get; set; } + [DataMember(Name = "name")] public string Name { get; set; } + [DataMember(Name = "shared_link")] public SharedLink SharedLink { get; set; } + } + + [DataContract] + public class Upload + { + [DataMember(Name = "entries")] public List Entries { get; set; } + } +} \ No newline at end of file diff --git a/src/Greenshot.Plugin.Box/BoxPlugin.cs b/src/Greenshot.Plugin.Box/BoxPlugin.cs index 6a01dda9e..9918b5e93 100644 --- a/src/Greenshot.Plugin.Box/BoxPlugin.cs +++ b/src/Greenshot.Plugin.Box/BoxPlugin.cs @@ -30,99 +30,111 @@ using GreenshotPlugin.IniFile; using GreenshotPlugin.Interfaces; using GreenshotPlugin.Interfaces.Plugin; -namespace Greenshot.Plugin.Box { - /// - /// This is the Box base code - /// +namespace Greenshot.Plugin.Box +{ + /// + /// This is the Box base code + /// [Plugin("Box", true)] - public class BoxPlugin : IGreenshotPlugin { - private static readonly log4net.ILog LOG = log4net.LogManager.GetLogger(typeof(BoxPlugin)); - private static BoxConfiguration _config; - private ComponentResourceManager _resources; - private ToolStripMenuItem _itemPlugInConfig; + public class BoxPlugin : IGreenshotPlugin + { + private static readonly log4net.ILog LOG = log4net.LogManager.GetLogger(typeof(BoxPlugin)); + private static BoxConfiguration _config; + private ComponentResourceManager _resources; + private ToolStripMenuItem _itemPlugInConfig; - public void Dispose() { - Dispose(true); - GC.SuppressFinalize(this); - } + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } - protected void Dispose(bool disposing) - { - if (!disposing) return; + protected void Dispose(bool disposing) + { + if (!disposing) return; - if (_itemPlugInConfig == null) return; - - _itemPlugInConfig.Dispose(); - _itemPlugInConfig = null; - } + if (_itemPlugInConfig == null) return; - /// - /// Implementation of the IGreenshotPlugin.Initialize - /// - public bool Initialize() { + _itemPlugInConfig.Dispose(); + _itemPlugInConfig = null; + } - // Register configuration (don't need the configuration itself) - _config = IniConfig.GetIniSection(); - _resources = new ComponentResourceManager(typeof(BoxPlugin)); + /// + /// Implementation of the IGreenshotPlugin.Initialize + /// + public bool Initialize() + { + // Register configuration (don't need the configuration itself) + _config = IniConfig.GetIniSection(); + _resources = new ComponentResourceManager(typeof(BoxPlugin)); SimpleServiceProvider.Current.AddService(new BoxDestination(this)); - _itemPlugInConfig = new ToolStripMenuItem { - Image = (Image) _resources.GetObject("Box"), - Text = Language.GetString("box", LangKey.Configure) - }; - _itemPlugInConfig.Click += ConfigMenuClick; + _itemPlugInConfig = new ToolStripMenuItem + { + Image = (Image) _resources.GetObject("Box"), + Text = Language.GetString("box", LangKey.Configure) + }; + _itemPlugInConfig.Click += ConfigMenuClick; - PluginUtils.AddToContextMenu(_itemPlugInConfig); - Language.LanguageChanged += OnLanguageChanged; - return true; - } + PluginUtils.AddToContextMenu(_itemPlugInConfig); + Language.LanguageChanged += OnLanguageChanged; + return true; + } - public void OnLanguageChanged(object sender, EventArgs e) { - if (_itemPlugInConfig != null) { - _itemPlugInConfig.Text = Language.GetString("box", LangKey.Configure); - } - } + public void OnLanguageChanged(object sender, EventArgs e) + { + if (_itemPlugInConfig != null) + { + _itemPlugInConfig.Text = Language.GetString("box", LangKey.Configure); + } + } - public void Shutdown() { - LOG.Debug("Box Plugin shutdown."); - } + public void Shutdown() + { + LOG.Debug("Box Plugin shutdown."); + } - /// - /// Implementation of the IPlugin.Configure - /// - public void Configure() { - _config.ShowConfigDialog(); - } + /// + /// Implementation of the IPlugin.Configure + /// + public void Configure() + { + _config.ShowConfigDialog(); + } - public void ConfigMenuClick(object sender, EventArgs eventArgs) { - _config.ShowConfigDialog(); - } + public void ConfigMenuClick(object sender, EventArgs eventArgs) + { + _config.ShowConfigDialog(); + } - /// - /// This will be called when the menu item in the Editor is clicked - /// - public string Upload(ICaptureDetails captureDetails, ISurface surfaceToUpload) { - SurfaceOutputSettings outputSettings = new SurfaceOutputSettings(_config.UploadFormat, _config.UploadJpegQuality, false); - try { - string url = null; - string filename = Path.GetFileName(FilenameHelper.GetFilename(_config.UploadFormat, captureDetails)); - SurfaceContainer imageToUpload = new SurfaceContainer(surfaceToUpload, outputSettings, filename); - - new PleaseWaitForm().ShowAndWait("Box", Language.GetString("box", LangKey.communication_wait), - delegate { - url = BoxUtils.UploadToBox(imageToUpload, captureDetails.Title, filename); - } - ); + /// + /// This will be called when the menu item in the Editor is clicked + /// + public string Upload(ICaptureDetails captureDetails, ISurface surfaceToUpload) + { + SurfaceOutputSettings outputSettings = new SurfaceOutputSettings(_config.UploadFormat, _config.UploadJpegQuality, false); + try + { + string url = null; + string filename = Path.GetFileName(FilenameHelper.GetFilename(_config.UploadFormat, captureDetails)); + SurfaceContainer imageToUpload = new SurfaceContainer(surfaceToUpload, outputSettings, filename); - if (url != null && _config.AfterUploadLinkToClipBoard) { - ClipboardHelper.SetClipboardData(url); - } + new PleaseWaitForm().ShowAndWait("Box", Language.GetString("box", LangKey.communication_wait), + delegate { url = BoxUtils.UploadToBox(imageToUpload, captureDetails.Title, filename); } + ); - return url; - } catch (Exception ex) { - LOG.Error("Error uploading.", ex); - MessageBox.Show(Language.GetString("box", LangKey.upload_failure) + " " + ex.Message); - return null; - } - } - } -} + if (url != null && _config.AfterUploadLinkToClipBoard) + { + ClipboardHelper.SetClipboardData(url); + } + + return url; + } + catch (Exception ex) + { + LOG.Error("Error uploading.", ex); + MessageBox.Show(Language.GetString("box", LangKey.upload_failure) + " " + ex.Message); + return null; + } + } + } +} \ No newline at end of file diff --git a/src/Greenshot.Plugin.Box/BoxUtils.cs b/src/Greenshot.Plugin.Box/BoxUtils.cs index 22e8acf49..37a0eac53 100644 --- a/src/Greenshot.Plugin.Box/BoxUtils.cs +++ b/src/Greenshot.Plugin.Box/BoxUtils.cs @@ -27,112 +27,128 @@ using GreenshotPlugin.Core; using GreenshotPlugin.Core.OAuth; using GreenshotPlugin.IniFile; -namespace Greenshot.Plugin.Box { - +namespace Greenshot.Plugin.Box +{ /// /// Description of BoxUtils. /// - public static class BoxUtils { - private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(typeof(BoxUtils)); - private static readonly BoxConfiguration Config = IniConfig.GetIniSection(); - private const string UploadFileUri = "https://upload.box.com/api/2.0/files/content"; - private const string FilesUri = "https://www.box.com/api/2.0/files/{0}"; + public static class BoxUtils + { + private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(typeof(BoxUtils)); + private static readonly BoxConfiguration Config = IniConfig.GetIniSection(); + private const string UploadFileUri = "https://upload.box.com/api/2.0/files/content"; + private const string FilesUri = "https://www.box.com/api/2.0/files/{0}"; - /// - /// Put string - /// - /// - /// - /// OAuth2Settings - /// response - public static string HttpPut(string url, string content, OAuth2Settings settings) { - var webRequest= OAuth2Helper.CreateOAuth2WebRequest(HTTPMethod.PUT, url, settings); - - byte[] data = Encoding.UTF8.GetBytes(content); - using (var requestStream = webRequest.GetRequestStream()) { - requestStream.Write(data, 0, data.Length); - } - return NetworkHelper.GetResponseAsString(webRequest); - } - - /// - /// Do the actual upload to Box - /// For more details on the available parameters, see: http://developers.box.net/w/page/12923951/ApiFunction_Upload%20and%20Download - /// - /// Image for box upload - /// Title of box upload - /// Filename of box upload - /// url to uploaded image - public static string UploadToBox(SurfaceContainer image, string title, string filename) { - - // Fill the OAuth2Settings - var settings = new OAuth2Settings - { - AuthUrlPattern = "https://app.box.com/api/oauth2/authorize?client_id={ClientId}&response_type=code&state={State}&redirect_uri={RedirectUrl}", - TokenUrl = "https://api.box.com/oauth2/token", - CloudServiceName = "Box", - ClientId = BoxCredentials.ClientId, - ClientSecret = BoxCredentials.ClientSecret, - RedirectUrl = "https://getgreenshot.org/authorize/box", - AuthorizeMode = OAuth2AuthorizeMode.JsonReceiver, - RefreshToken = Config.RefreshToken, - AccessToken = Config.AccessToken, - AccessTokenExpires = Config.AccessTokenExpires - }; - - - // Copy the settings from the config, which is kept in memory and on the disk - - try { - var webRequest = OAuth2Helper.CreateOAuth2WebRequest(HTTPMethod.POST, UploadFileUri, settings); - IDictionary parameters = new Dictionary - { - { "file", image }, - { "parent_id", Config.FolderId } - }; - - NetworkHelper.WriteMultipartFormData(webRequest, parameters); - - var response = NetworkHelper.GetResponseAsString(webRequest); - - Log.DebugFormat("Box response: {0}", response); - - var upload = JsonSerializer.Deserialize(response); - if (upload?.Entries == null || upload.Entries.Count == 0) return null; - - if (Config.UseSharedLink) { - string filesResponse = HttpPut(string.Format(FilesUri, upload.Entries[0].Id), "{\"shared_link\": {\"access\": \"open\"}}", settings); - var file = JsonSerializer.Deserialize(filesResponse); - return file.SharedLink.Url; - } - return $"http://www.box.com/files/0/f/0/1/f_{upload.Entries[0].Id}"; - } finally { - // Copy the settings back to the config, so they are stored. - Config.RefreshToken = settings.RefreshToken; - Config.AccessToken = settings.AccessToken; - Config.AccessTokenExpires = settings.AccessTokenExpires; - Config.IsDirty = true; - IniConfig.Save(); - } - } - } - /// - /// A simple helper class for the DataContractJsonSerializer - /// - internal static class JsonSerializer { /// - /// Helper method to parse JSON to object - /// - /// - /// - /// - public static T Deserialize(string jsonString) { - var deserializer = new DataContractJsonSerializer(typeof(T)); + /// Put string + /// + /// + /// + /// OAuth2Settings + /// response + public static string HttpPut(string url, string content, OAuth2Settings settings) + { + var webRequest = OAuth2Helper.CreateOAuth2WebRequest(HTTPMethod.PUT, url, settings); + + byte[] data = Encoding.UTF8.GetBytes(content); + using (var requestStream = webRequest.GetRequestStream()) + { + requestStream.Write(data, 0, data.Length); + } + + return NetworkHelper.GetResponseAsString(webRequest); + } + + /// + /// Do the actual upload to Box + /// For more details on the available parameters, see: http://developers.box.net/w/page/12923951/ApiFunction_Upload%20and%20Download + /// + /// Image for box upload + /// Title of box upload + /// Filename of box upload + /// url to uploaded image + public static string UploadToBox(SurfaceContainer image, string title, string filename) + { + // Fill the OAuth2Settings + var settings = new OAuth2Settings + { + AuthUrlPattern = "https://app.box.com/api/oauth2/authorize?client_id={ClientId}&response_type=code&state={State}&redirect_uri={RedirectUrl}", + TokenUrl = "https://api.box.com/oauth2/token", + CloudServiceName = "Box", + ClientId = BoxCredentials.ClientId, + ClientSecret = BoxCredentials.ClientSecret, + RedirectUrl = "https://getgreenshot.org/authorize/box", + AuthorizeMode = OAuth2AuthorizeMode.JsonReceiver, + RefreshToken = Config.RefreshToken, + AccessToken = Config.AccessToken, + AccessTokenExpires = Config.AccessTokenExpires + }; + + + // Copy the settings from the config, which is kept in memory and on the disk + + try + { + var webRequest = OAuth2Helper.CreateOAuth2WebRequest(HTTPMethod.POST, UploadFileUri, settings); + IDictionary parameters = new Dictionary + { + { + "file", image + }, + { + "parent_id", Config.FolderId + } + }; + + NetworkHelper.WriteMultipartFormData(webRequest, parameters); + + var response = NetworkHelper.GetResponseAsString(webRequest); + + Log.DebugFormat("Box response: {0}", response); + + var upload = JsonSerializer.Deserialize(response); + if (upload?.Entries == null || upload.Entries.Count == 0) return null; + + if (Config.UseSharedLink) + { + string filesResponse = HttpPut(string.Format(FilesUri, upload.Entries[0].Id), "{\"shared_link\": {\"access\": \"open\"}}", settings); + var file = JsonSerializer.Deserialize(filesResponse); + return file.SharedLink.Url; + } + + return $"http://www.box.com/files/0/f/0/1/f_{upload.Entries[0].Id}"; + } + finally + { + // Copy the settings back to the config, so they are stored. + Config.RefreshToken = settings.RefreshToken; + Config.AccessToken = settings.AccessToken; + Config.AccessTokenExpires = settings.AccessTokenExpires; + Config.IsDirty = true; + IniConfig.Save(); + } + } + } + + /// + /// A simple helper class for the DataContractJsonSerializer + /// + internal static class JsonSerializer + { + /// + /// Helper method to parse JSON to object + /// + /// + /// + /// + public static T Deserialize(string jsonString) + { + var deserializer = new DataContractJsonSerializer(typeof(T)); using var stream = new MemoryStream(); byte[] content = Encoding.UTF8.GetBytes(jsonString); stream.Write(content, 0, content.Length); stream.Seek(0, SeekOrigin.Begin); - return (T)deserializer.ReadObject(stream); + return (T) deserializer.ReadObject(stream); } - } -} + } +} \ No newline at end of file diff --git a/src/Greenshot.Plugin.Box/Forms/BoxForm.cs b/src/Greenshot.Plugin.Box/Forms/BoxForm.cs index 5bb895823..646bdf87b 100644 --- a/src/Greenshot.Plugin.Box/Forms/BoxForm.cs +++ b/src/Greenshot.Plugin.Box/Forms/BoxForm.cs @@ -21,7 +21,9 @@ using GreenshotPlugin.Controls; -namespace Greenshot.Plugin.Box.Forms { - public class BoxForm : GreenshotForm { - } +namespace Greenshot.Plugin.Box.Forms +{ + public class BoxForm : GreenshotForm + { + } } \ No newline at end of file diff --git a/src/Greenshot.Plugin.Box/Forms/SettingsForm.cs b/src/Greenshot.Plugin.Box/Forms/SettingsForm.cs index 1de1e7b32..588350c76 100644 --- a/src/Greenshot.Plugin.Box/Forms/SettingsForm.cs +++ b/src/Greenshot.Plugin.Box/Forms/SettingsForm.cs @@ -19,18 +19,21 @@ * along with this program. If not, see . */ -namespace Greenshot.Plugin.Box.Forms { - /// - /// Description of PasswordRequestForm. - /// - public partial class SettingsForm : BoxForm { - public SettingsForm() { - // - // The InitializeComponent() call is required for Windows Forms designer support. - // - InitializeComponent(); - AcceptButton = buttonOK; - CancelButton = buttonCancel; - } - } -} +namespace Greenshot.Plugin.Box.Forms +{ + /// + /// Description of PasswordRequestForm. + /// + public partial class SettingsForm : BoxForm + { + public SettingsForm() + { + // + // The InitializeComponent() call is required for Windows Forms designer support. + // + InitializeComponent(); + AcceptButton = buttonOK; + CancelButton = buttonCancel; + } + } +} \ No newline at end of file diff --git a/src/Greenshot.Plugin.Box/LanguageKeys.cs b/src/Greenshot.Plugin.Box/LanguageKeys.cs index 32230ebd1..c6c64bfdf 100644 --- a/src/Greenshot.Plugin.Box/LanguageKeys.cs +++ b/src/Greenshot.Plugin.Box/LanguageKeys.cs @@ -18,11 +18,14 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -namespace Greenshot.Plugin.Box { - public enum LangKey { - upload_menu_item, - upload_failure, - communication_wait, - Configure - } -} + +namespace Greenshot.Plugin.Box +{ + public enum LangKey + { + upload_menu_item, + upload_failure, + communication_wait, + Configure + } +} \ No newline at end of file diff --git a/src/Greenshot.Plugin.Box/Properties/AssemblyInfo.cs b/src/Greenshot.Plugin.Box/Properties/AssemblyInfo.cs index b6b19f1c8..8af36d000 100644 --- a/src/Greenshot.Plugin.Box/Properties/AssemblyInfo.cs +++ b/src/Greenshot.Plugin.Box/Properties/AssemblyInfo.cs @@ -29,4 +29,4 @@ using System.Runtime.InteropServices; // This sets the default COM visibility of types in the assembly to invisible. // If you need to expose a type to COM, use [ComVisible(true)] on that type. -[assembly: ComVisible(false)] +[assembly: ComVisible(false)] \ No newline at end of file diff --git a/src/Greenshot.Plugin.Confluence/Confluence.cs b/src/Greenshot.Plugin.Confluence/Confluence.cs index 13f5d0ab3..79d11a034 100644 --- a/src/Greenshot.Plugin.Confluence/Confluence.cs +++ b/src/Greenshot.Plugin.Confluence/Confluence.cs @@ -26,283 +26,333 @@ using GreenshotConfluencePlugin.confluence; using GreenshotPlugin.Core; using GreenshotPlugin.IniFile; -namespace Greenshot.Plugin.Confluence { - public class Page { - public Page(RemotePage page) { - Id = page.id; - Title = page.title; - SpaceKey = page.space; - Url = page.url; - Content = page.content; - } - public Page(RemoteSearchResult searchResult, string space) { - Id = searchResult.id; - Title = searchResult.title; - SpaceKey = space; - Url = searchResult.url; - Content = searchResult.excerpt; - } - - public Page(RemotePageSummary pageSummary) { - Id = pageSummary.id; - Title = pageSummary.title; - SpaceKey = pageSummary.space; - Url =pageSummary.url; - } - public long Id { - get; - set; - } - public string Title { - get; - set; - } - public string Url { - get; - set; - } - public string Content { - get; - set; - } - public string SpaceKey { - get; - set; - } - } - public class Space { - public Space(RemoteSpaceSummary space) { - Key = space.key; - Name = space.name; - } - public string Key { - get; - set; - } - public string Name { - get; - set; - } - } +namespace Greenshot.Plugin.Confluence +{ + public class Page + { + public Page(RemotePage page) + { + Id = page.id; + Title = page.title; + SpaceKey = page.space; + Url = page.url; + Content = page.content; + } + + public Page(RemoteSearchResult searchResult, string space) + { + Id = searchResult.id; + Title = searchResult.title; + SpaceKey = space; + Url = searchResult.url; + Content = searchResult.excerpt; + } + + public Page(RemotePageSummary pageSummary) + { + Id = pageSummary.id; + Title = pageSummary.title; + SpaceKey = pageSummary.space; + Url = pageSummary.url; + } + + public long Id { get; set; } + public string Title { get; set; } + public string Url { get; set; } + public string Content { get; set; } + public string SpaceKey { get; set; } + } + + public class Space + { + public Space(RemoteSpaceSummary space) + { + Key = space.key; + Name = space.name; + } + + public string Key { get; set; } + public string Name { get; set; } + } /// - /// For details see the Confluence API site - /// See: http://confluence.atlassian.com/display/CONFDEV/Remote+API+Specification - /// - public class ConfluenceConnector : IDisposable { - private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(typeof(ConfluenceConnector)); - private const string AuthFailedExceptionName = "com.atlassian.confluence.rpc.AuthenticationFailedException"; - private const string V2Failed = "AXIS"; - private static readonly ConfluenceConfiguration Config = IniConfig.GetIniSection(); - private string _credentials; - private DateTime _loggedInTime = DateTime.Now; - private bool _loggedIn; - private ConfluenceSoapServiceService _confluence; - private readonly int _timeout; - private string _url; - private readonly Cache _pageCache = new Cache(60 * Config.Timeout); + /// For details see the Confluence API site + /// See: http://confluence.atlassian.com/display/CONFDEV/Remote+API+Specification + /// + public class ConfluenceConnector : IDisposable + { + private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(typeof(ConfluenceConnector)); + private const string AuthFailedExceptionName = "com.atlassian.confluence.rpc.AuthenticationFailedException"; + private const string V2Failed = "AXIS"; + private static readonly ConfluenceConfiguration Config = IniConfig.GetIniSection(); + private string _credentials; + private DateTime _loggedInTime = DateTime.Now; + private bool _loggedIn; + private ConfluenceSoapServiceService _confluence; + private readonly int _timeout; + private string _url; + private readonly Cache _pageCache = new Cache(60 * Config.Timeout); - public void Dispose() { - Dispose(true); - GC.SuppressFinalize(this); - } + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } - protected void Dispose(bool disposing) { - if (_confluence != null) { - Logout(); - } - if (disposing) { - if (_confluence != null) { - _confluence.Dispose(); - _confluence = null; - } - } - } + protected void Dispose(bool disposing) + { + if (_confluence != null) + { + Logout(); + } - public ConfluenceConnector(string url, int timeout) { - _timeout = timeout; - Init(url); - } + if (disposing) + { + if (_confluence != null) + { + _confluence.Dispose(); + _confluence = null; + } + } + } - private void Init(string url) { - _url = url; - _confluence = new ConfluenceSoapServiceService - { - Url = url, - Proxy = NetworkHelper.CreateProxy(new Uri(url)) - }; - } + public ConfluenceConnector(string url, int timeout) + { + _timeout = timeout; + Init(url); + } - ~ConfluenceConnector() { - Dispose(false); - } + private void Init(string url) + { + _url = url; + _confluence = new ConfluenceSoapServiceService + { + Url = url, + Proxy = NetworkHelper.CreateProxy(new Uri(url)) + }; + } - /// - /// Internal login which catches the exceptions - /// - /// true if login was done sucessfully - private bool DoLogin(string user, string password) { - try { - _credentials = _confluence.login(user, password); - _loggedInTime = DateTime.Now; - _loggedIn = true; - } catch (Exception e) { - // Check if confluence-v2 caused an error, use v1 instead - if (e.Message.Contains(V2Failed) && _url.Contains("v2")) { - Init(_url.Replace("v2", "v1")); - return DoLogin(user, password); - } - // check if auth failed - if (e.Message.Contains(AuthFailedExceptionName)) { - return false; - } - // Not an authentication issue - _loggedIn = false; - _credentials = null; - e.Data.Add("user", user); - e.Data.Add("url", _url); - throw; - } - return true; - } + ~ConfluenceConnector() + { + Dispose(false); + } - public void Login() { - Logout(); - try { - // Get the system name, so the user knows where to login to - string systemName = _url.Replace(ConfluenceConfiguration.DEFAULT_POSTFIX1,""); - systemName = systemName.Replace(ConfluenceConfiguration.DEFAULT_POSTFIX2, ""); - CredentialsDialog dialog = new CredentialsDialog(systemName) - { - Name = null - }; - while (dialog.Show(dialog.Name) == DialogResult.OK) { - if (DoLogin(dialog.Name, dialog.Password)) { - if (dialog.SaveChecked) { - dialog.Confirm(true); - } - return; - } else { - try { - dialog.Confirm(false); - } catch (ApplicationException e) { - // exception handling ... - Log.Error("Problem using the credentials dialog", e); - } - // For every windows version after XP show an incorrect password baloon - dialog.IncorrectPassword = true; - // Make sure the dialog is display, the password was false! - dialog.AlwaysDisplay = true; - } - } - } catch (ApplicationException e) { - // exception handling ... - Log.Error("Problem using the credentials dialog", e); - } - } + /// + /// Internal login which catches the exceptions + /// + /// true if login was done sucessfully + private bool DoLogin(string user, string password) + { + try + { + _credentials = _confluence.login(user, password); + _loggedInTime = DateTime.Now; + _loggedIn = true; + } + catch (Exception e) + { + // Check if confluence-v2 caused an error, use v1 instead + if (e.Message.Contains(V2Failed) && _url.Contains("v2")) + { + Init(_url.Replace("v2", "v1")); + return DoLogin(user, password); + } - public void Logout() { - if (_credentials != null) { - _confluence.logout(_credentials); - _credentials = null; - _loggedIn = false; - } - } + // check if auth failed + if (e.Message.Contains(AuthFailedExceptionName)) + { + return false; + } - private void CheckCredentials() { - if (_loggedIn) { - if (_loggedInTime.AddMinutes(_timeout-1).CompareTo(DateTime.Now) < 0) { - Logout(); - Login(); - } - } else { - Login(); - } - } + // Not an authentication issue + _loggedIn = false; + _credentials = null; + e.Data.Add("user", user); + e.Data.Add("url", _url); + throw; + } - public bool IsLoggedIn => _loggedIn; + return true; + } - public void AddAttachment(long pageId, string mime, string comment, string filename, IBinaryContainer image) { - CheckCredentials(); - // Comment is ignored, see: http://jira.atlassian.com/browse/CONF-9395 - var attachment = new RemoteAttachment - { - comment = comment, - fileName = filename, - contentType = mime - }; - _confluence.addAttachment(_credentials, pageId, attachment, image.ToByteArray()); - } - - public Page GetPage(string spaceKey, string pageTitle) { - RemotePage page = null; - string cacheKey = spaceKey + pageTitle; - if (_pageCache.Contains(cacheKey)) { - page = _pageCache[cacheKey]; - } - if (page == null) { - CheckCredentials(); - page = _confluence.getPage(_credentials, spaceKey, pageTitle); - _pageCache.Add(cacheKey, page); - } - return new Page(page); - } + public void Login() + { + Logout(); + try + { + // Get the system name, so the user knows where to login to + string systemName = _url.Replace(ConfluenceConfiguration.DEFAULT_POSTFIX1, ""); + systemName = systemName.Replace(ConfluenceConfiguration.DEFAULT_POSTFIX2, ""); + CredentialsDialog dialog = new CredentialsDialog(systemName) + { + Name = null + }; + while (dialog.Show(dialog.Name) == DialogResult.OK) + { + if (DoLogin(dialog.Name, dialog.Password)) + { + if (dialog.SaveChecked) + { + dialog.Confirm(true); + } - public Page GetPage(long pageId) { - RemotePage page = null; - string cacheKey = pageId.ToString(); - - if (_pageCache.Contains(cacheKey)) { - page = _pageCache[cacheKey]; - } - if (page == null) { - CheckCredentials(); - page = _confluence.getPage(_credentials, pageId); - _pageCache.Add(cacheKey, page); - } - return new Page(page); - } + return; + } + else + { + try + { + dialog.Confirm(false); + } + catch (ApplicationException e) + { + // exception handling ... + Log.Error("Problem using the credentials dialog", e); + } - public Page GetSpaceHomepage(Space spaceSummary) { - CheckCredentials(); - RemoteSpace spaceDetail = _confluence.getSpace(_credentials, spaceSummary.Key); - RemotePage page = _confluence.getPage(_credentials, spaceDetail.homePage); - return new Page(page); - } + // For every windows version after XP show an incorrect password baloon + dialog.IncorrectPassword = true; + // Make sure the dialog is display, the password was false! + dialog.AlwaysDisplay = true; + } + } + } + catch (ApplicationException e) + { + // exception handling ... + Log.Error("Problem using the credentials dialog", e); + } + } - public IEnumerable GetSpaceSummaries() { - CheckCredentials(); - RemoteSpaceSummary [] spaces = _confluence.getSpaces(_credentials); - foreach(RemoteSpaceSummary space in spaces) { - yield return new Space(space); - } - } - - public IEnumerable GetPageChildren(Page parentPage) { - CheckCredentials(); - RemotePageSummary[] pages = _confluence.getChildren(_credentials, parentPage.Id); - foreach(RemotePageSummary page in pages) { - yield return new Page(page); - } - } + public void Logout() + { + if (_credentials != null) + { + _confluence.logout(_credentials); + _credentials = null; + _loggedIn = false; + } + } - public IEnumerable GetPageSummaries(Space space) { - CheckCredentials(); - RemotePageSummary[] pages = _confluence.getPages(_credentials, space.Key); - foreach(RemotePageSummary page in pages) { - yield return new Page(page); - } - } - - public IEnumerable SearchPages(string query, string space) { - CheckCredentials(); - foreach(var searchResult in _confluence.search(_credentials, query, 20)) { - Log.DebugFormat("Got result of type {0}", searchResult.type); - if ("page".Equals(searchResult.type)) - { - yield return new Page(searchResult, space); - } - } - } - } -} + private void CheckCredentials() + { + if (_loggedIn) + { + if (_loggedInTime.AddMinutes(_timeout - 1).CompareTo(DateTime.Now) < 0) + { + Logout(); + Login(); + } + } + else + { + Login(); + } + } + + public bool IsLoggedIn => _loggedIn; + + public void AddAttachment(long pageId, string mime, string comment, string filename, IBinaryContainer image) + { + CheckCredentials(); + // Comment is ignored, see: http://jira.atlassian.com/browse/CONF-9395 + var attachment = new RemoteAttachment + { + comment = comment, + fileName = filename, + contentType = mime + }; + _confluence.addAttachment(_credentials, pageId, attachment, image.ToByteArray()); + } + + public Page GetPage(string spaceKey, string pageTitle) + { + RemotePage page = null; + string cacheKey = spaceKey + pageTitle; + if (_pageCache.Contains(cacheKey)) + { + page = _pageCache[cacheKey]; + } + + if (page == null) + { + CheckCredentials(); + page = _confluence.getPage(_credentials, spaceKey, pageTitle); + _pageCache.Add(cacheKey, page); + } + + return new Page(page); + } + + public Page GetPage(long pageId) + { + RemotePage page = null; + string cacheKey = pageId.ToString(); + + if (_pageCache.Contains(cacheKey)) + { + page = _pageCache[cacheKey]; + } + + if (page == null) + { + CheckCredentials(); + page = _confluence.getPage(_credentials, pageId); + _pageCache.Add(cacheKey, page); + } + + return new Page(page); + } + + public Page GetSpaceHomepage(Space spaceSummary) + { + CheckCredentials(); + RemoteSpace spaceDetail = _confluence.getSpace(_credentials, spaceSummary.Key); + RemotePage page = _confluence.getPage(_credentials, spaceDetail.homePage); + return new Page(page); + } + + public IEnumerable GetSpaceSummaries() + { + CheckCredentials(); + RemoteSpaceSummary[] spaces = _confluence.getSpaces(_credentials); + foreach (RemoteSpaceSummary space in spaces) + { + yield return new Space(space); + } + } + + public IEnumerable GetPageChildren(Page parentPage) + { + CheckCredentials(); + RemotePageSummary[] pages = _confluence.getChildren(_credentials, parentPage.Id); + foreach (RemotePageSummary page in pages) + { + yield return new Page(page); + } + } + + public IEnumerable GetPageSummaries(Space space) + { + CheckCredentials(); + RemotePageSummary[] pages = _confluence.getPages(_credentials, space.Key); + foreach (RemotePageSummary page in pages) + { + yield return new Page(page); + } + } + + public IEnumerable SearchPages(string query, string space) + { + CheckCredentials(); + foreach (var searchResult in _confluence.search(_credentials, query, 20)) + { + Log.DebugFormat("Got result of type {0}", searchResult.type); + if ("page".Equals(searchResult.type)) + { + yield return new Page(searchResult, space); + } + } + } + } +} \ No newline at end of file diff --git a/src/Greenshot.Plugin.Confluence/ConfluenceConfiguration.cs b/src/Greenshot.Plugin.Confluence/ConfluenceConfiguration.cs index a742fc45b..df23e7768 100644 --- a/src/Greenshot.Plugin.Confluence/ConfluenceConfiguration.cs +++ b/src/Greenshot.Plugin.Confluence/ConfluenceConfiguration.cs @@ -23,63 +23,45 @@ using System; using GreenshotPlugin.Core; using GreenshotPlugin.IniFile; -namespace Greenshot.Plugin.Confluence { - /// - /// Description of ConfluenceConfiguration. - /// - [Serializable] - [IniSection("Confluence", Description="Greenshot Confluence Plugin configuration")] - public class ConfluenceConfiguration : IniSection { - public const string DEFAULT_POSTFIX1 = "/rpc/soap-axis/confluenceservice-v1?wsdl"; - public const string DEFAULT_POSTFIX2 = "/rpc/soap-axis/confluenceservice-v2?wsdl"; - public const string DEFAULT_PREFIX = "http://"; - private const string DEFAULT_URL = DEFAULT_PREFIX + "confluence"; +namespace Greenshot.Plugin.Confluence +{ + /// + /// Description of ConfluenceConfiguration. + /// + [Serializable] + [IniSection("Confluence", Description = "Greenshot Confluence Plugin configuration")] + public class ConfluenceConfiguration : IniSection + { + public const string DEFAULT_POSTFIX1 = "/rpc/soap-axis/confluenceservice-v1?wsdl"; + public const string DEFAULT_POSTFIX2 = "/rpc/soap-axis/confluenceservice-v2?wsdl"; + public const string DEFAULT_PREFIX = "http://"; + private const string DEFAULT_URL = DEFAULT_PREFIX + "confluence"; - [IniProperty("Url", Description="Url to Confluence system, including wsdl.", DefaultValue=DEFAULT_URL)] - public string Url { - get; - set; - } - [IniProperty("Timeout", Description="Session timeout in minutes", DefaultValue="30")] - public int Timeout { - get; - set; - } + [IniProperty("Url", Description = "Url to Confluence system, including wsdl.", DefaultValue = DEFAULT_URL)] + public string Url { get; set; } - [IniProperty("UploadFormat", Description="What file type to use for uploading", DefaultValue="png")] - public OutputFormat UploadFormat { - get; - set; - } - [IniProperty("UploadJpegQuality", Description="JPEG file save quality in %.", DefaultValue="80")] - public int UploadJpegQuality { - get; - set; - } - [IniProperty("UploadReduceColors", Description="Reduce color amount of the uploaded image to 256", DefaultValue="False")] - public bool UploadReduceColors { - get; - set; - } - [IniProperty("OpenPageAfterUpload", Description="Open the page where the picture is uploaded after upload", DefaultValue="True")] - public bool OpenPageAfterUpload { - get; - set; - } - [IniProperty("CopyWikiMarkupForImageToClipboard", Description="Copy the Wikimarkup for the recently uploaded image to the Clipboard", DefaultValue="True")] - public bool CopyWikiMarkupForImageToClipboard { - get; - set; - } - [IniProperty("SearchSpaceKey", Description="Key of last space that was searched for")] - public string SearchSpaceKey { - get; - set; - } - [IniProperty("IncludePersonSpaces", Description = "Include personal spaces in the search & browse spaces list", DefaultValue = "False")] - public bool IncludePersonSpaces { - get; - set; - } - } -} + [IniProperty("Timeout", Description = "Session timeout in minutes", DefaultValue = "30")] + public int Timeout { get; set; } + + [IniProperty("UploadFormat", Description = "What file type to use for uploading", DefaultValue = "png")] + public OutputFormat UploadFormat { get; set; } + + [IniProperty("UploadJpegQuality", Description = "JPEG file save quality in %.", DefaultValue = "80")] + public int UploadJpegQuality { get; set; } + + [IniProperty("UploadReduceColors", Description = "Reduce color amount of the uploaded image to 256", DefaultValue = "False")] + public bool UploadReduceColors { get; set; } + + [IniProperty("OpenPageAfterUpload", Description = "Open the page where the picture is uploaded after upload", DefaultValue = "True")] + public bool OpenPageAfterUpload { get; set; } + + [IniProperty("CopyWikiMarkupForImageToClipboard", Description = "Copy the Wikimarkup for the recently uploaded image to the Clipboard", DefaultValue = "True")] + public bool CopyWikiMarkupForImageToClipboard { get; set; } + + [IniProperty("SearchSpaceKey", Description = "Key of last space that was searched for")] + public string SearchSpaceKey { get; set; } + + [IniProperty("IncludePersonSpaces", Description = "Include personal spaces in the search & browse spaces list", DefaultValue = "False")] + public bool IncludePersonSpaces { get; set; } + } +} \ No newline at end of file diff --git a/src/Greenshot.Plugin.Confluence/ConfluenceDestination.cs b/src/Greenshot.Plugin.Confluence/ConfluenceDestination.cs index d2661d141..275ace4d1 100644 --- a/src/Greenshot.Plugin.Confluence/ConfluenceDestination.cs +++ b/src/Greenshot.Plugin.Confluence/ConfluenceDestination.cs @@ -32,174 +32,221 @@ using GreenshotPlugin.IniFile; using GreenshotPlugin.Interfaces; using GreenshotPlugin.Interfaces.Plugin; -namespace Greenshot.Plugin.Confluence { - /// - /// Description of ConfluenceDestination. - /// - public class ConfluenceDestination : AbstractDestination { - private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(typeof(ConfluenceDestination)); - private static readonly ConfluenceConfiguration ConfluenceConfig = IniConfig.GetIniSection(); - private static readonly CoreConfiguration CoreConfig = IniConfig.GetIniSection(); - private static readonly Image ConfluenceIcon; - private readonly Page _page; +namespace Greenshot.Plugin.Confluence +{ + /// + /// Description of ConfluenceDestination. + /// + public class ConfluenceDestination : AbstractDestination + { + private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(typeof(ConfluenceDestination)); + private static readonly ConfluenceConfiguration ConfluenceConfig = IniConfig.GetIniSection(); + private static readonly CoreConfiguration CoreConfig = IniConfig.GetIniSection(); + private static readonly Image ConfluenceIcon; + private readonly Page _page; - static ConfluenceDestination() { - IsInitialized = false; - try { - Uri confluenceIconUri = new Uri("/GreenshotConfluencePlugin;component/Images/Confluence.ico", UriKind.Relative); - using (Stream iconStream = Application.GetResourceStream(confluenceIconUri)?.Stream) - { - // TODO: Check what to do with the IImage - ConfluenceIcon = ImageHelper.FromStream(iconStream); - } - IsInitialized = true; - } catch (Exception ex) { - Log.ErrorFormat("Problem in the confluence static initializer: {0}", ex.Message); - } - } - - public static bool IsInitialized + static ConfluenceDestination() { - get; - private set; + IsInitialized = false; + try + { + Uri confluenceIconUri = new Uri("/GreenshotConfluencePlugin;component/Images/Confluence.ico", UriKind.Relative); + using (Stream iconStream = Application.GetResourceStream(confluenceIconUri)?.Stream) + { + // TODO: Check what to do with the IImage + ConfluenceIcon = ImageHelper.FromStream(iconStream); + } + + IsInitialized = true; + } + catch (Exception ex) + { + Log.ErrorFormat("Problem in the confluence static initializer: {0}", ex.Message); + } } - public ConfluenceDestination() { - } + public static bool IsInitialized { get; private set; } - public ConfluenceDestination(Page page) { - _page = page; - } - - public override string Designation { - get { - return "Confluence"; - } - } + public ConfluenceDestination() + { + } - public override string Description { - get { - if (_page == null) { - return Language.GetString("confluence", LangKey.upload_menu_item); - } else { - return Language.GetString("confluence", LangKey.upload_menu_item) + ": \"" + _page.Title + "\""; - } - } - } + public ConfluenceDestination(Page page) + { + _page = page; + } - public override bool IsDynamic { - get { - return true; - } - } - - public override bool IsActive { - get { - return base.IsActive && !string.IsNullOrEmpty(ConfluenceConfig.Url); - } - } + public override string Designation + { + get { return "Confluence"; } + } - public override Image DisplayIcon { - get { - return ConfluenceIcon; - } - } - - public override IEnumerable DynamicDestinations() { - if (ConfluencePlugin.ConfluenceConnectorNoLogin == null || !ConfluencePlugin.ConfluenceConnectorNoLogin.IsLoggedIn) { - yield break; - } - List currentPages = ConfluenceUtils.GetCurrentPages(); - if (currentPages == null || currentPages.Count == 0) { - yield break; - } - foreach(Page currentPage in currentPages) { - yield return new ConfluenceDestination(currentPage); - } - } + public override string Description + { + get + { + if (_page == null) + { + return Language.GetString("confluence", LangKey.upload_menu_item); + } + else + { + return Language.GetString("confluence", LangKey.upload_menu_item) + ": \"" + _page.Title + "\""; + } + } + } - public override ExportInformation ExportCapture(bool manuallyInitiated, ISurface surface, ICaptureDetails captureDetails) { - ExportInformation exportInformation = new ExportInformation(Designation, Description); - // force password check to take place before the pages load - if (!ConfluencePlugin.ConfluenceConnector.IsLoggedIn) { - return exportInformation; - } + public override bool IsDynamic + { + get { return true; } + } - Page selectedPage = _page; - bool openPage = (_page == null) && ConfluenceConfig.OpenPageAfterUpload; - string filename = FilenameHelper.GetFilenameWithoutExtensionFromPattern(CoreConfig.OutputFileFilenamePattern, captureDetails); - if (selectedPage == null) { - Forms.ConfluenceUpload confluenceUpload = new Forms.ConfluenceUpload(filename); - bool? dialogResult = confluenceUpload.ShowDialog(); - if (dialogResult.HasValue && dialogResult.Value) { - selectedPage = confluenceUpload.SelectedPage; - if (confluenceUpload.IsOpenPageSelected) { - openPage = false; - } - filename = confluenceUpload.Filename; - } - } - string extension = "." + ConfluenceConfig.UploadFormat; - if (!filename.ToLower().EndsWith(extension)) { - filename += extension; - } - if (selectedPage != null) { + public override bool IsActive + { + get { return base.IsActive && !string.IsNullOrEmpty(ConfluenceConfig.Url); } + } + + public override Image DisplayIcon + { + get { return ConfluenceIcon; } + } + + public override IEnumerable DynamicDestinations() + { + if (ConfluencePlugin.ConfluenceConnectorNoLogin == null || !ConfluencePlugin.ConfluenceConnectorNoLogin.IsLoggedIn) + { + yield break; + } + + List currentPages = ConfluenceUtils.GetCurrentPages(); + if (currentPages == null || currentPages.Count == 0) + { + yield break; + } + + foreach (Page currentPage in currentPages) + { + yield return new ConfluenceDestination(currentPage); + } + } + + public override ExportInformation ExportCapture(bool manuallyInitiated, ISurface surface, ICaptureDetails captureDetails) + { + ExportInformation exportInformation = new ExportInformation(Designation, Description); + // force password check to take place before the pages load + if (!ConfluencePlugin.ConfluenceConnector.IsLoggedIn) + { + return exportInformation; + } + + Page selectedPage = _page; + bool openPage = (_page == null) && ConfluenceConfig.OpenPageAfterUpload; + string filename = FilenameHelper.GetFilenameWithoutExtensionFromPattern(CoreConfig.OutputFileFilenamePattern, captureDetails); + if (selectedPage == null) + { + Forms.ConfluenceUpload confluenceUpload = new Forms.ConfluenceUpload(filename); + bool? dialogResult = confluenceUpload.ShowDialog(); + if (dialogResult.HasValue && dialogResult.Value) + { + selectedPage = confluenceUpload.SelectedPage; + if (confluenceUpload.IsOpenPageSelected) + { + openPage = false; + } + + filename = confluenceUpload.Filename; + } + } + + string extension = "." + ConfluenceConfig.UploadFormat; + if (!filename.ToLower().EndsWith(extension)) + { + filename += extension; + } + + if (selectedPage != null) + { bool uploaded = Upload(surface, selectedPage, filename, out var errorMessage); - if (uploaded) { - if (openPage) { - try - { - Process.Start(selectedPage.Url); - } - catch - { - // Ignore - } - } - exportInformation.ExportMade = true; - exportInformation.Uri = selectedPage.Url; - } else { - exportInformation.ErrorMessage = errorMessage; - } - } - ProcessExport(exportInformation, surface); - return exportInformation; - } + if (uploaded) + { + if (openPage) + { + try + { + Process.Start(selectedPage.Url); + } + catch + { + // Ignore + } + } - private bool Upload(ISurface surfaceToUpload, Page page, string filename, out string errorMessage) { - SurfaceOutputSettings outputSettings = new SurfaceOutputSettings(ConfluenceConfig.UploadFormat, ConfluenceConfig.UploadJpegQuality, ConfluenceConfig.UploadReduceColors); - errorMessage = null; - try { - new PleaseWaitForm().ShowAndWait(Description, Language.GetString("confluence", LangKey.communication_wait), - delegate { - ConfluencePlugin.ConfluenceConnector.AddAttachment(page.Id, "image/" + ConfluenceConfig.UploadFormat.ToString().ToLower(), null, filename, new SurfaceContainer(surfaceToUpload, outputSettings, filename)); - } - ); - Log.Debug("Uploaded to Confluence."); - if (!ConfluenceConfig.CopyWikiMarkupForImageToClipboard) - { - return true; - } - int retryCount = 2; - while (retryCount >= 0) { - try { - Clipboard.SetText("!" + filename + "!"); - break; - } catch (Exception ee) { - if (retryCount == 0) { - Log.Error(ee); - } else { - Thread.Sleep(100); - } - } finally { - --retryCount; - } - } - return true; - } catch(Exception e) { - errorMessage = e.Message; - } - return false; - } - } -} + exportInformation.ExportMade = true; + exportInformation.Uri = selectedPage.Url; + } + else + { + exportInformation.ErrorMessage = errorMessage; + } + } + + ProcessExport(exportInformation, surface); + return exportInformation; + } + + private bool Upload(ISurface surfaceToUpload, Page page, string filename, out string errorMessage) + { + SurfaceOutputSettings outputSettings = + new SurfaceOutputSettings(ConfluenceConfig.UploadFormat, ConfluenceConfig.UploadJpegQuality, ConfluenceConfig.UploadReduceColors); + errorMessage = null; + try + { + new PleaseWaitForm().ShowAndWait(Description, Language.GetString("confluence", LangKey.communication_wait), + delegate + { + ConfluencePlugin.ConfluenceConnector.AddAttachment(page.Id, "image/" + ConfluenceConfig.UploadFormat.ToString().ToLower(), null, filename, + new SurfaceContainer(surfaceToUpload, outputSettings, filename)); + } + ); + Log.Debug("Uploaded to Confluence."); + if (!ConfluenceConfig.CopyWikiMarkupForImageToClipboard) + { + return true; + } + + int retryCount = 2; + while (retryCount >= 0) + { + try + { + Clipboard.SetText("!" + filename + "!"); + break; + } + catch (Exception ee) + { + if (retryCount == 0) + { + Log.Error(ee); + } + else + { + Thread.Sleep(100); + } + } + finally + { + --retryCount; + } + } + + return true; + } + catch (Exception e) + { + errorMessage = e.Message; + } + + return false; + } + } +} \ No newline at end of file diff --git a/src/Greenshot.Plugin.Confluence/ConfluencePlugin.cs b/src/Greenshot.Plugin.Confluence/ConfluencePlugin.cs index 4cca23c8f..fbe852691 100644 --- a/src/Greenshot.Plugin.Confluence/ConfluencePlugin.cs +++ b/src/Greenshot.Plugin.Confluence/ConfluencePlugin.cs @@ -28,109 +28,142 @@ using GreenshotPlugin.IniFile; using GreenshotPlugin.Interfaces; using GreenshotPlugin.Interfaces.Plugin; -namespace Greenshot.Plugin.Confluence { - /// - /// This is the ConfluencePlugin base code - /// - [Plugin("Confluence", true)] - public class ConfluencePlugin : IGreenshotPlugin { - private static readonly log4net.ILog LOG = log4net.LogManager.GetLogger(typeof(ConfluencePlugin)); - private static ConfluenceConnector _confluenceConnector; - private static ConfluenceConfiguration _config; +namespace Greenshot.Plugin.Confluence +{ + /// + /// This is the ConfluencePlugin base code + /// + [Plugin("Confluence", true)] + public class ConfluencePlugin : IGreenshotPlugin + { + private static readonly log4net.ILog LOG = log4net.LogManager.GetLogger(typeof(ConfluencePlugin)); + private static ConfluenceConnector _confluenceConnector; + private static ConfluenceConfiguration _config; - public void Dispose() { - Dispose(true); - GC.SuppressFinalize(this); - } + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } - protected void Dispose(bool disposing) { - //if (disposing) {} - } + protected void Dispose(bool disposing) + { + //if (disposing) {} + } - private static void CreateConfluenceConnector() { - if (_confluenceConnector == null) { - if (_config.Url.Contains("soap-axis")) { - _confluenceConnector = new ConfluenceConnector(_config.Url, _config.Timeout); - } else { - _confluenceConnector = new ConfluenceConnector(_config.Url + ConfluenceConfiguration.DEFAULT_POSTFIX2, _config.Timeout); - } - } - } + private static void CreateConfluenceConnector() + { + if (_confluenceConnector == null) + { + if (_config.Url.Contains("soap-axis")) + { + _confluenceConnector = new ConfluenceConnector(_config.Url, _config.Timeout); + } + else + { + _confluenceConnector = new ConfluenceConnector(_config.Url + ConfluenceConfiguration.DEFAULT_POSTFIX2, _config.Timeout); + } + } + } - public static ConfluenceConnector ConfluenceConnectorNoLogin { - get { - return _confluenceConnector; - } - } + public static ConfluenceConnector ConfluenceConnectorNoLogin + { + get { return _confluenceConnector; } + } - public static ConfluenceConnector ConfluenceConnector { - get { - if (_confluenceConnector == null) { - CreateConfluenceConnector(); - } - try { - if (_confluenceConnector != null && !_confluenceConnector.IsLoggedIn) { - _confluenceConnector.Login(); - } - } catch (Exception e) { - MessageBox.Show(Language.GetFormattedString("confluence", LangKey.login_error, e.Message)); - } - return _confluenceConnector; - } - } + public static ConfluenceConnector ConfluenceConnector + { + get + { + if (_confluenceConnector == null) + { + CreateConfluenceConnector(); + } + + try + { + if (_confluenceConnector != null && !_confluenceConnector.IsLoggedIn) + { + _confluenceConnector.Login(); + } + } + catch (Exception e) + { + MessageBox.Show(Language.GetFormattedString("confluence", LangKey.login_error, e.Message)); + } + + return _confluenceConnector; + } + } + + /// + /// Implementation of the IGreenshotPlugin.Initialize + /// + public bool Initialize() + { + // Register configuration (don't need the configuration itself) + _config = IniConfig.GetIniSection(); + if (_config.IsDirty) + { + IniConfig.Save(); + } + + try + { + TranslationManager.Instance.TranslationProvider = new LanguageXMLTranslationProvider(); + //resources = new ComponentResourceManager(typeof(ConfluencePlugin)); + } + catch (Exception ex) + { + LOG.ErrorFormat("Problem in ConfluencePlugin.Initialize: {0}", ex.Message); + return false; + } - /// - /// Implementation of the IGreenshotPlugin.Initialize - /// - public bool Initialize() { - // Register configuration (don't need the configuration itself) - _config = IniConfig.GetIniSection(); - if(_config.IsDirty) { - IniConfig.Save(); - } - try { - TranslationManager.Instance.TranslationProvider = new LanguageXMLTranslationProvider(); - //resources = new ComponentResourceManager(typeof(ConfluencePlugin)); - } catch (Exception ex) { - LOG.ErrorFormat("Problem in ConfluencePlugin.Initialize: {0}", ex.Message); - return false; - } if (ConfluenceDestination.IsInitialized) { SimpleServiceProvider.Current.AddService(new ConfluenceDestination()); } - return true; - } - public void Shutdown() { - LOG.Debug("Confluence Plugin shutdown."); - if (_confluenceConnector != null) { - _confluenceConnector.Logout(); - _confluenceConnector = null; - } - } + return true; + } - /// - /// Implementation of the IPlugin.Configure - /// - public void Configure() { - ConfluenceConfiguration clonedConfig = _config.Clone(); - ConfluenceConfigurationForm configForm = new ConfluenceConfigurationForm(clonedConfig); - string url = _config.Url; - bool? dialogResult = configForm.ShowDialog(); - if (dialogResult.HasValue && dialogResult.Value) { - // copy the new object to the old... - clonedConfig.CloneTo(_config); - IniConfig.Save(); - if (_confluenceConnector != null) { - if (!url.Equals(_config.Url)) { - if (_confluenceConnector.IsLoggedIn) { - _confluenceConnector.Logout(); - } - _confluenceConnector = null; - } - } - } - } - } -} + public void Shutdown() + { + LOG.Debug("Confluence Plugin shutdown."); + if (_confluenceConnector != null) + { + _confluenceConnector.Logout(); + _confluenceConnector = null; + } + } + + /// + /// Implementation of the IPlugin.Configure + /// + public void Configure() + { + ConfluenceConfiguration clonedConfig = _config.Clone(); + ConfluenceConfigurationForm configForm = new ConfluenceConfigurationForm(clonedConfig); + string url = _config.Url; + bool? dialogResult = configForm.ShowDialog(); + if (dialogResult.HasValue && dialogResult.Value) + { + // copy the new object to the old... + clonedConfig.CloneTo(_config); + IniConfig.Save(); + if (_confluenceConnector != null) + { + if (!url.Equals(_config.Url)) + { + if (_confluenceConnector.IsLoggedIn) + { + _confluenceConnector.Logout(); + } + + _confluenceConnector = null; + } + } + } + } + } +} \ No newline at end of file diff --git a/src/Greenshot.Plugin.Confluence/ConfluenceUtils.cs b/src/Greenshot.Plugin.Confluence/ConfluenceUtils.cs index 55a40fa8c..2c878d6d9 100644 --- a/src/Greenshot.Plugin.Confluence/ConfluenceUtils.cs +++ b/src/Greenshot.Plugin.Confluence/ConfluenceUtils.cs @@ -26,126 +26,172 @@ using System.Text.RegularExpressions; using System.Windows.Automation; using GreenshotPlugin.Core; -namespace Greenshot.Plugin.Confluence { - /// - /// Description of ConfluenceUtils. - /// - public class ConfluenceUtils { - private static readonly log4net.ILog LOG = log4net.LogManager.GetLogger(typeof(ConfluenceUtils)); +namespace Greenshot.Plugin.Confluence +{ + /// + /// Description of ConfluenceUtils. + /// + public class ConfluenceUtils + { + private static readonly log4net.ILog LOG = log4net.LogManager.GetLogger(typeof(ConfluenceUtils)); - public static List GetCurrentPages() { - List pages = new List(); - Regex pageIdRegex = new Regex(@"pageId=(\d+)"); - Regex spacePageRegex = new Regex(@"\/display\/([^\/]+)\/([^#]+)"); - foreach(string browserurl in GetBrowserUrls()) { - string url; - try { - url = Uri.UnescapeDataString(browserurl).Replace("+", " "); - } catch { - LOG.WarnFormat("Error processing URL: {0}", browserurl); - continue; - } - MatchCollection pageIdMatch = pageIdRegex.Matches(url); - if (pageIdMatch != null && pageIdMatch.Count > 0) { - long pageId = long.Parse(pageIdMatch[0].Groups[1].Value); - try { - bool pageDouble = false; - foreach(Page page in pages) { - if (page.Id == pageId) { - pageDouble = true; - LOG.DebugFormat("Skipping double page with ID {0}", pageId); - break; - } - } - if (!pageDouble) { - Page page = ConfluencePlugin.ConfluenceConnector.GetPage(pageId); - LOG.DebugFormat("Adding page {0}", page.Title); - pages.Add(page); - } - continue; - } catch (Exception ex) { - // Preventing security problems - LOG.DebugFormat("Couldn't get page details for PageID {0}", pageId); - LOG.Warn(ex); - } - } - MatchCollection spacePageMatch = spacePageRegex.Matches(url); - if (spacePageMatch != null && spacePageMatch.Count > 0) { - if (spacePageMatch[0].Groups.Count >= 2) { - string space = spacePageMatch[0].Groups[1].Value; - string title = spacePageMatch[0].Groups[2].Value; - if (string.IsNullOrEmpty(title) || string.IsNullOrEmpty(space)) { - continue; - } - if (title.EndsWith("#")) { - title = title.Substring(0, title.Length-1); - } - try { - bool pageDouble = false; - foreach(Page page in pages) { - if (page.Title.Equals(title)) { - LOG.DebugFormat("Skipping double page with title {0}", title); - pageDouble = true; - break; - } - } - if (!pageDouble) { - Page page = ConfluencePlugin.ConfluenceConnector.GetPage(space, title); - LOG.DebugFormat("Adding page {0}", page.Title); - pages.Add(page); - - } - } catch (Exception ex) { - // Preventing security problems - LOG.DebugFormat("Couldn't get page details for space {0} / title {1}", space, title); - LOG.Warn(ex); - } - } - } - } - return pages; - } + public static List GetCurrentPages() + { + List pages = new List(); + Regex pageIdRegex = new Regex(@"pageId=(\d+)"); + Regex spacePageRegex = new Regex(@"\/display\/([^\/]+)\/([^#]+)"); + foreach (string browserurl in GetBrowserUrls()) + { + string url; + try + { + url = Uri.UnescapeDataString(browserurl).Replace("+", " "); + } + catch + { + LOG.WarnFormat("Error processing URL: {0}", browserurl); + continue; + } - private static IEnumerable GetBrowserUrls() { - HashSet urls = new HashSet(); + MatchCollection pageIdMatch = pageIdRegex.Matches(url); + if (pageIdMatch != null && pageIdMatch.Count > 0) + { + long pageId = long.Parse(pageIdMatch[0].Groups[1].Value); + try + { + bool pageDouble = false; + foreach (Page page in pages) + { + if (page.Id == pageId) + { + pageDouble = true; + LOG.DebugFormat("Skipping double page with ID {0}", pageId); + break; + } + } - // FireFox - foreach (WindowDetails window in WindowDetails.GetAllWindows("MozillaWindowClass")) { - if (window.Text.Length == 0) { - continue; - } - AutomationElement currentElement = AutomationElement.FromHandle(window.Handle); - Condition conditionCustom = new AndCondition(new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Custom), new PropertyCondition(AutomationElement.IsOffscreenProperty, false)); - for (int i = 5; i > 0 && currentElement != null; i--) { - currentElement = currentElement.FindFirst(TreeScope.Children, conditionCustom); - } - if (currentElement == null) { - continue; - } + if (!pageDouble) + { + Page page = ConfluencePlugin.ConfluenceConnector.GetPage(pageId); + LOG.DebugFormat("Adding page {0}", page.Title); + pages.Add(page); + } - Condition conditionDocument = new AndCondition(new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Document), new PropertyCondition(AutomationElement.IsOffscreenProperty, false)); - AutomationElement docElement = currentElement.FindFirst(TreeScope.Children, conditionDocument); - if (docElement == null) { - continue; - } - foreach (AutomationPattern pattern in docElement.GetSupportedPatterns()) { - if (pattern.ProgrammaticName != "ValuePatternIdentifiers.Pattern") { - continue; - } - string url = (docElement.GetCurrentPattern(pattern) as ValuePattern).Current.Value; - if (!string.IsNullOrEmpty(url)) { - urls.Add(url); - break; - } - } - } + continue; + } + catch (Exception ex) + { + // Preventing security problems + LOG.DebugFormat("Couldn't get page details for PageID {0}", pageId); + LOG.Warn(ex); + } + } - foreach(string url in IEHelper.GetIEUrls().Distinct()) { - urls.Add(url); - } + MatchCollection spacePageMatch = spacePageRegex.Matches(url); + if (spacePageMatch != null && spacePageMatch.Count > 0) + { + if (spacePageMatch[0].Groups.Count >= 2) + { + string space = spacePageMatch[0].Groups[1].Value; + string title = spacePageMatch[0].Groups[2].Value; + if (string.IsNullOrEmpty(title) || string.IsNullOrEmpty(space)) + { + continue; + } - return urls; - } + if (title.EndsWith("#")) + { + title = title.Substring(0, title.Length - 1); + } - } -} + try + { + bool pageDouble = false; + foreach (Page page in pages) + { + if (page.Title.Equals(title)) + { + LOG.DebugFormat("Skipping double page with title {0}", title); + pageDouble = true; + break; + } + } + + if (!pageDouble) + { + Page page = ConfluencePlugin.ConfluenceConnector.GetPage(space, title); + LOG.DebugFormat("Adding page {0}", page.Title); + pages.Add(page); + } + } + catch (Exception ex) + { + // Preventing security problems + LOG.DebugFormat("Couldn't get page details for space {0} / title {1}", space, title); + LOG.Warn(ex); + } + } + } + } + + return pages; + } + + private static IEnumerable GetBrowserUrls() + { + HashSet urls = new HashSet(); + + // FireFox + foreach (WindowDetails window in WindowDetails.GetAllWindows("MozillaWindowClass")) + { + if (window.Text.Length == 0) + { + continue; + } + + AutomationElement currentElement = AutomationElement.FromHandle(window.Handle); + Condition conditionCustom = new AndCondition(new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Custom), + new PropertyCondition(AutomationElement.IsOffscreenProperty, false)); + for (int i = 5; i > 0 && currentElement != null; i--) + { + currentElement = currentElement.FindFirst(TreeScope.Children, conditionCustom); + } + + if (currentElement == null) + { + continue; + } + + Condition conditionDocument = new AndCondition(new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Document), + new PropertyCondition(AutomationElement.IsOffscreenProperty, false)); + AutomationElement docElement = currentElement.FindFirst(TreeScope.Children, conditionDocument); + if (docElement == null) + { + continue; + } + + foreach (AutomationPattern pattern in docElement.GetSupportedPatterns()) + { + if (pattern.ProgrammaticName != "ValuePatternIdentifiers.Pattern") + { + continue; + } + + string url = (docElement.GetCurrentPattern(pattern) as ValuePattern).Current.Value; + if (!string.IsNullOrEmpty(url)) + { + urls.Add(url); + break; + } + } + } + + foreach (string url in IEHelper.GetIEUrls().Distinct()) + { + urls.Add(url); + } + + return urls; + } + } +} \ No newline at end of file diff --git a/src/Greenshot.Plugin.Confluence/EnumDisplayer.cs b/src/Greenshot.Plugin.Confluence/EnumDisplayer.cs index 185593c87..c4023a828 100644 --- a/src/Greenshot.Plugin.Confluence/EnumDisplayer.cs +++ b/src/Greenshot.Plugin.Confluence/EnumDisplayer.cs @@ -28,70 +28,87 @@ using System.Reflection; using System.Windows.Data; using GreenshotPlugin.Core; -namespace Greenshot.Plugin.Confluence { - public class EnumDisplayer : IValueConverter { - private Type _type; - private IDictionary _displayValues; - private IDictionary _reverseValues; +namespace Greenshot.Plugin.Confluence +{ + public class EnumDisplayer : IValueConverter + { + private Type _type; + private IDictionary _displayValues; + private IDictionary _reverseValues; - public Type Type { - get { return _type; } - set { - if (!value.IsEnum) { - throw new ArgumentException("parameter is not an Enumerated type", nameof(value)); - } - _type = value; - } - } - - public ReadOnlyCollection DisplayNames { - get { - var genericTypeDefinition = typeof(Dictionary<,>).GetGenericTypeDefinition(); - if (genericTypeDefinition != null) - { - _reverseValues = (IDictionary) Activator.CreateInstance(genericTypeDefinition.MakeGenericType(typeof(string),_type)); - } + public Type Type + { + get { return _type; } + set + { + if (!value.IsEnum) + { + throw new ArgumentException("parameter is not an Enumerated type", nameof(value)); + } - var typeDefinition = typeof(Dictionary<,>).GetGenericTypeDefinition(); - if (typeDefinition != null) - { - _displayValues = (IDictionary)Activator.CreateInstance(typeDefinition.MakeGenericType(_type, typeof(string))); - } + _type = value; + } + } - var fields = _type.GetFields(BindingFlags.Public | BindingFlags.Static); - foreach (var field in fields) { - DisplayKeyAttribute[] a = (DisplayKeyAttribute[])field.GetCustomAttributes(typeof(DisplayKeyAttribute), false); - - string displayKey = GetDisplayKeyValue(a); - object enumValue = field.GetValue(null); - - string displayString; - if (displayKey != null && Language.HasKey(displayKey)) { - displayString = Language.GetString(displayKey); - } - displayString = displayKey ?? enumValue.ToString(); + public ReadOnlyCollection DisplayNames + { + get + { + var genericTypeDefinition = typeof(Dictionary<,>).GetGenericTypeDefinition(); + if (genericTypeDefinition != null) + { + _reverseValues = (IDictionary) Activator.CreateInstance(genericTypeDefinition.MakeGenericType(typeof(string), _type)); + } - _displayValues.Add(enumValue, displayString); - _reverseValues.Add(displayString, enumValue); - } - return new List((IEnumerable)_displayValues.Values).AsReadOnly(); - } - } - - private static string GetDisplayKeyValue(DisplayKeyAttribute[] a) { - if (a == null || a.Length == 0) { - return null; - } - DisplayKeyAttribute dka = a[0]; - return dka.Value; - } - - object IValueConverter.Convert(object value, Type targetType, object parameter, CultureInfo culture) { - return _displayValues[value]; - } - - object IValueConverter.ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { - return _reverseValues[value]; - } - } -} + var typeDefinition = typeof(Dictionary<,>).GetGenericTypeDefinition(); + if (typeDefinition != null) + { + _displayValues = (IDictionary) Activator.CreateInstance(typeDefinition.MakeGenericType(_type, typeof(string))); + } + + var fields = _type.GetFields(BindingFlags.Public | BindingFlags.Static); + foreach (var field in fields) + { + DisplayKeyAttribute[] a = (DisplayKeyAttribute[]) field.GetCustomAttributes(typeof(DisplayKeyAttribute), false); + + string displayKey = GetDisplayKeyValue(a); + object enumValue = field.GetValue(null); + + string displayString; + if (displayKey != null && Language.HasKey(displayKey)) + { + displayString = Language.GetString(displayKey); + } + + displayString = displayKey ?? enumValue.ToString(); + + _displayValues.Add(enumValue, displayString); + _reverseValues.Add(displayString, enumValue); + } + + return new List((IEnumerable) _displayValues.Values).AsReadOnly(); + } + } + + private static string GetDisplayKeyValue(DisplayKeyAttribute[] a) + { + if (a == null || a.Length == 0) + { + return null; + } + + DisplayKeyAttribute dka = a[0]; + return dka.Value; + } + + object IValueConverter.Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + return _displayValues[value]; + } + + object IValueConverter.ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + return _reverseValues[value]; + } + } +} \ No newline at end of file diff --git a/src/Greenshot.Plugin.Confluence/Forms/ConfluenceConfigurationForm.xaml.cs b/src/Greenshot.Plugin.Confluence/Forms/ConfluenceConfigurationForm.xaml.cs index cd31165ba..dd9d46a1b 100644 --- a/src/Greenshot.Plugin.Confluence/Forms/ConfluenceConfigurationForm.xaml.cs +++ b/src/Greenshot.Plugin.Confluence/Forms/ConfluenceConfigurationForm.xaml.cs @@ -21,21 +21,25 @@ using System.Windows; -namespace Greenshot.Plugin.Confluence.Forms { - /// - /// Interaction logic for ConfluenceConfigurationForm.xaml - /// - public partial class ConfluenceConfigurationForm : Window { - public ConfluenceConfiguration Config { get; } +namespace Greenshot.Plugin.Confluence.Forms +{ + /// + /// Interaction logic for ConfluenceConfigurationForm.xaml + /// + public partial class ConfluenceConfigurationForm : Window + { + public ConfluenceConfiguration Config { get; } - public ConfluenceConfigurationForm(ConfluenceConfiguration config) { - DataContext = config; - Config = config; - InitializeComponent(); - } + public ConfluenceConfigurationForm(ConfluenceConfiguration config) + { + DataContext = config; + Config = config; + InitializeComponent(); + } - private void Button_OK_Click(object sender, RoutedEventArgs e) { - DialogResult = true; - } - } + private void Button_OK_Click(object sender, RoutedEventArgs e) + { + DialogResult = true; + } + } } \ No newline at end of file diff --git a/src/Greenshot.Plugin.Confluence/Forms/ConfluencePagePicker.xaml.cs b/src/Greenshot.Plugin.Confluence/Forms/ConfluencePagePicker.xaml.cs index 55ca76659..de3ffc934 100644 --- a/src/Greenshot.Plugin.Confluence/Forms/ConfluencePagePicker.xaml.cs +++ b/src/Greenshot.Plugin.Confluence/Forms/ConfluencePagePicker.xaml.cs @@ -21,36 +21,44 @@ using System.Collections.Generic; -namespace Greenshot.Plugin.Confluence.Forms { - /// - /// Interaction logic for ConfluencePagePicker.xaml - /// - public partial class ConfluencePagePicker - { - private readonly ConfluenceUpload _confluenceUpload; +namespace Greenshot.Plugin.Confluence.Forms +{ + /// + /// Interaction logic for ConfluencePagePicker.xaml + /// + public partial class ConfluencePagePicker + { + private readonly ConfluenceUpload _confluenceUpload; - public ConfluencePagePicker(ConfluenceUpload confluenceUpload, List pagesToPick) { - _confluenceUpload = confluenceUpload; - DataContext = pagesToPick; - InitializeComponent(); - } + public ConfluencePagePicker(ConfluenceUpload confluenceUpload, List pagesToPick) + { + _confluenceUpload = confluenceUpload; + DataContext = pagesToPick; + InitializeComponent(); + } - private void PageListView_SelectionChanged(object sender, System.Windows.Controls.SelectionChangedEventArgs e) { - SelectionChanged(); - } + private void PageListView_SelectionChanged(object sender, System.Windows.Controls.SelectionChangedEventArgs e) + { + SelectionChanged(); + } - private void SelectionChanged() { - if (PageListView.HasItems && PageListView.SelectedItems.Count > 0) { - _confluenceUpload.SelectedPage = (Page)PageListView.SelectedItem; - // Make sure the uploader knows we selected an already opened page - _confluenceUpload.IsOpenPageSelected = true; - } else { - _confluenceUpload.SelectedPage = null; - } - } + private void SelectionChanged() + { + if (PageListView.HasItems && PageListView.SelectedItems.Count > 0) + { + _confluenceUpload.SelectedPage = (Page) PageListView.SelectedItem; + // Make sure the uploader knows we selected an already opened page + _confluenceUpload.IsOpenPageSelected = true; + } + else + { + _confluenceUpload.SelectedPage = null; + } + } - private void Page_Loaded(object sender, System.Windows.RoutedEventArgs e) { - SelectionChanged(); - } - } + private void Page_Loaded(object sender, System.Windows.RoutedEventArgs e) + { + SelectionChanged(); + } + } } \ No newline at end of file diff --git a/src/Greenshot.Plugin.Confluence/Forms/ConfluenceSearch.xaml.cs b/src/Greenshot.Plugin.Confluence/Forms/ConfluenceSearch.xaml.cs index a39c36a39..2b296f5f0 100644 --- a/src/Greenshot.Plugin.Confluence/Forms/ConfluenceSearch.xaml.cs +++ b/src/Greenshot.Plugin.Confluence/Forms/ConfluenceSearch.xaml.cs @@ -25,69 +25,88 @@ using System.Linq; using System.Windows; using GreenshotPlugin.IniFile; -namespace Greenshot.Plugin.Confluence.Forms { - public partial class ConfluenceSearch - { - private static readonly ConfluenceConfiguration ConfluenceConfig = IniConfig.GetIniSection(); - private readonly ConfluenceUpload _confluenceUpload; - - public IEnumerable Spaces => _confluenceUpload.Spaces; +namespace Greenshot.Plugin.Confluence.Forms +{ + public partial class ConfluenceSearch + { + private static readonly ConfluenceConfiguration ConfluenceConfig = IniConfig.GetIniSection(); + private readonly ConfluenceUpload _confluenceUpload; - public ObservableCollection Pages { get; } = new ObservableCollection(); + public IEnumerable Spaces => _confluenceUpload.Spaces; - public ConfluenceSearch(ConfluenceUpload confluenceUpload) { - _confluenceUpload = confluenceUpload; - DataContext = this; - InitializeComponent(); - if (ConfluenceConfig.SearchSpaceKey == null) { - SpaceComboBox.SelectedItem = Spaces.FirstOrDefault(); - } else { - foreach(var space in Spaces) { - if (space.Key.Equals(ConfluenceConfig.SearchSpaceKey)) { - SpaceComboBox.SelectedItem = space; - } - } - } - } + public ObservableCollection Pages { get; } = new ObservableCollection(); - private void PageListView_SelectionChanged(object sender, System.Windows.Controls.SelectionChangedEventArgs e) { - SelectionChanged(); - } + public ConfluenceSearch(ConfluenceUpload confluenceUpload) + { + _confluenceUpload = confluenceUpload; + DataContext = this; + InitializeComponent(); + if (ConfluenceConfig.SearchSpaceKey == null) + { + SpaceComboBox.SelectedItem = Spaces.FirstOrDefault(); + } + else + { + foreach (var space in Spaces) + { + if (space.Key.Equals(ConfluenceConfig.SearchSpaceKey)) + { + SpaceComboBox.SelectedItem = space; + } + } + } + } - private void SelectionChanged() { - if (PageListView.HasItems && PageListView.SelectedItems.Count > 0) { - _confluenceUpload.SelectedPage = (Page)PageListView.SelectedItem; - } else { - _confluenceUpload.SelectedPage = null; - } - } + private void PageListView_SelectionChanged(object sender, System.Windows.Controls.SelectionChangedEventArgs e) + { + SelectionChanged(); + } - private void Search_Click(object sender, RoutedEventArgs e) { - DoSearch(); - } + private void SelectionChanged() + { + if (PageListView.HasItems && PageListView.SelectedItems.Count > 0) + { + _confluenceUpload.SelectedPage = (Page) PageListView.SelectedItem; + } + else + { + _confluenceUpload.SelectedPage = null; + } + } - private void DoSearch() { - string spaceKey = (string)SpaceComboBox.SelectedValue; - ConfluenceConfig.SearchSpaceKey = spaceKey; - Pages.Clear(); - foreach(var page in ConfluencePlugin.ConfluenceConnector.SearchPages(searchText.Text, spaceKey).OrderBy(p => p.Title)) { - Pages.Add(page); - } - } + private void Search_Click(object sender, RoutedEventArgs e) + { + DoSearch(); + } - private void SearchText_KeyDown(object sender, System.Windows.Input.KeyEventArgs e) { - if (e.Key == System.Windows.Input.Key.Return && Search.IsEnabled) { - DoSearch(); - e.Handled = true; - } - } + private void DoSearch() + { + string spaceKey = (string) SpaceComboBox.SelectedValue; + ConfluenceConfig.SearchSpaceKey = spaceKey; + Pages.Clear(); + foreach (var page in ConfluencePlugin.ConfluenceConnector.SearchPages(searchText.Text, spaceKey).OrderBy(p => p.Title)) + { + Pages.Add(page); + } + } - private void Page_Loaded(object sender, RoutedEventArgs e) { - SelectionChanged(); - } + private void SearchText_KeyDown(object sender, System.Windows.Input.KeyEventArgs e) + { + if (e.Key == System.Windows.Input.Key.Return && Search.IsEnabled) + { + DoSearch(); + e.Handled = true; + } + } - private void SearchText_TextChanged(object sender, System.Windows.Controls.TextChangedEventArgs e) { - Search.IsEnabled = !string.IsNullOrEmpty(searchText.Text); - } - } + private void Page_Loaded(object sender, RoutedEventArgs e) + { + SelectionChanged(); + } + + private void SearchText_TextChanged(object sender, System.Windows.Controls.TextChangedEventArgs e) + { + Search.IsEnabled = !string.IsNullOrEmpty(searchText.Text); + } + } } \ No newline at end of file diff --git a/src/Greenshot.Plugin.Confluence/Forms/ConfluenceTreePicker.xaml.cs b/src/Greenshot.Plugin.Confluence/Forms/ConfluenceTreePicker.xaml.cs index cff4878be..62089b4ed 100644 --- a/src/Greenshot.Plugin.Confluence/Forms/ConfluenceTreePicker.xaml.cs +++ b/src/Greenshot.Plugin.Confluence/Forms/ConfluenceTreePicker.xaml.cs @@ -27,101 +27,131 @@ using System.Windows.Controls; using System.Windows.Input; using System.Windows.Threading; -namespace Greenshot.Plugin.Confluence.Forms { - /// - /// Interaction logic for ConfluenceTreePicker.xaml - /// - public partial class ConfluenceTreePicker - { - private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(typeof(ConfluenceTreePicker)); - private readonly ConfluenceConnector _confluenceConnector; - private readonly ConfluenceUpload _confluenceUpload; - private bool _isInitDone; - - public ConfluenceTreePicker(ConfluenceUpload confluenceUpload) { - _confluenceConnector = ConfluencePlugin.ConfluenceConnector; - _confluenceUpload = confluenceUpload; - InitializeComponent(); - } +namespace Greenshot.Plugin.Confluence.Forms +{ + /// + /// Interaction logic for ConfluenceTreePicker.xaml + /// + public partial class ConfluenceTreePicker + { + private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(typeof(ConfluenceTreePicker)); + private readonly ConfluenceConnector _confluenceConnector; + private readonly ConfluenceUpload _confluenceUpload; + private bool _isInitDone; - private void PageTreeViewItem_DoubleClick(object sender, MouseButtonEventArgs eventArgs) { - Log.Debug("spaceTreeViewItem_MouseLeftButtonDown is called!"); - TreeViewItem clickedItem = eventArgs.Source as TreeViewItem; - if (clickedItem?.Tag is not Page page) { - return; - } - if (clickedItem.HasItems) - { - return; - } - Log.Debug("Loading pages for page: " + page.Title); - new Thread(() => { - Dispatcher.BeginInvoke(DispatcherPriority.Normal, (ThreadStart)(() => {ShowBusy.Visibility = Visibility.Visible;})); - var pages = _confluenceConnector.GetPageChildren(page).OrderBy(p => p.Title); - Dispatcher.BeginInvoke(DispatcherPriority.Normal, (ThreadStart)(() => { - foreach(var childPage in pages) { - Log.Debug("Adding page: " + childPage.Title); - var pageTreeViewItem = new TreeViewItem - { - Header = childPage.Title, - Tag = childPage - }; - clickedItem.Items.Add(pageTreeViewItem); - pageTreeViewItem.PreviewMouseDoubleClick += PageTreeViewItem_DoubleClick; - pageTreeViewItem.PreviewMouseLeftButtonDown += PageTreeViewItem_Click; - } - ShowBusy.Visibility = Visibility.Collapsed; - })); - }) { Name = "Loading childpages for confluence page " + page.Title }.Start(); - } + public ConfluenceTreePicker(ConfluenceUpload confluenceUpload) + { + _confluenceConnector = ConfluencePlugin.ConfluenceConnector; + _confluenceUpload = confluenceUpload; + InitializeComponent(); + } - private void PageTreeViewItem_Click(object sender, MouseButtonEventArgs eventArgs) { - Log.Debug("pageTreeViewItem_PreviewMouseDoubleClick is called!"); - if (eventArgs.Source is not TreeViewItem clickedItem) { - return; - } - Page page = clickedItem.Tag as Page; - _confluenceUpload.SelectedPage = page; - if (page != null) { - Log.Debug("Page selected: " + page.Title); - } - } + private void PageTreeViewItem_DoubleClick(object sender, MouseButtonEventArgs eventArgs) + { + Log.Debug("spaceTreeViewItem_MouseLeftButtonDown is called!"); + TreeViewItem clickedItem = eventArgs.Source as TreeViewItem; + if (clickedItem?.Tag is not Page page) + { + return; + } - private void Page_Loaded(object sender, RoutedEventArgs e) { - _confluenceUpload.SelectedPage = null; - if (_isInitDone) { - return; - } - ShowBusy.Visibility = Visibility.Visible; - new Thread(() => { - Dispatcher.BeginInvoke(DispatcherPriority.Normal, (ThreadStart)(() => { - foreach (Space space in _confluenceUpload.Spaces) { - TreeViewItem spaceTreeViewItem = new TreeViewItem - { - Header = space.Name, - Tag = space - }; + if (clickedItem.HasItems) + { + return; + } - // Get homepage - try { - Page page = _confluenceConnector.GetSpaceHomepage(space); - TreeViewItem pageTreeViewItem = new TreeViewItem - { - Header = page.Title, - Tag = page - }; - pageTreeViewItem.PreviewMouseDoubleClick += PageTreeViewItem_DoubleClick; - pageTreeViewItem.PreviewMouseLeftButtonDown += PageTreeViewItem_Click; - spaceTreeViewItem.Items.Add(pageTreeViewItem); - ConfluenceTreeView.Items.Add(spaceTreeViewItem); - } catch (Exception ex) { - Log.Error("Can't get homepage for space : " + space.Name + " (" + ex.Message + ")"); - } - } - ShowBusy.Visibility = Visibility.Collapsed; - _isInitDone = true; - })); - }) { Name = "Loading spaces for confluence"}.Start(); - } - } + Log.Debug("Loading pages for page: " + page.Title); + new Thread(() => + { + Dispatcher.BeginInvoke(DispatcherPriority.Normal, (ThreadStart) (() => { ShowBusy.Visibility = Visibility.Visible; })); + var pages = _confluenceConnector.GetPageChildren(page).OrderBy(p => p.Title); + Dispatcher.BeginInvoke(DispatcherPriority.Normal, (ThreadStart) (() => + { + foreach (var childPage in pages) + { + Log.Debug("Adding page: " + childPage.Title); + var pageTreeViewItem = new TreeViewItem + { + Header = childPage.Title, + Tag = childPage + }; + clickedItem.Items.Add(pageTreeViewItem); + pageTreeViewItem.PreviewMouseDoubleClick += PageTreeViewItem_DoubleClick; + pageTreeViewItem.PreviewMouseLeftButtonDown += PageTreeViewItem_Click; + } + + ShowBusy.Visibility = Visibility.Collapsed; + })); + }) + { + Name = "Loading childpages for confluence page " + page.Title + }.Start(); + } + + private void PageTreeViewItem_Click(object sender, MouseButtonEventArgs eventArgs) + { + Log.Debug("pageTreeViewItem_PreviewMouseDoubleClick is called!"); + if (eventArgs.Source is not TreeViewItem clickedItem) + { + return; + } + + Page page = clickedItem.Tag as Page; + _confluenceUpload.SelectedPage = page; + if (page != null) + { + Log.Debug("Page selected: " + page.Title); + } + } + + private void Page_Loaded(object sender, RoutedEventArgs e) + { + _confluenceUpload.SelectedPage = null; + if (_isInitDone) + { + return; + } + + ShowBusy.Visibility = Visibility.Visible; + new Thread(() => + { + Dispatcher.BeginInvoke(DispatcherPriority.Normal, (ThreadStart) (() => + { + foreach (Space space in _confluenceUpload.Spaces) + { + TreeViewItem spaceTreeViewItem = new TreeViewItem + { + Header = space.Name, + Tag = space + }; + + // Get homepage + try + { + Page page = _confluenceConnector.GetSpaceHomepage(space); + TreeViewItem pageTreeViewItem = new TreeViewItem + { + Header = page.Title, + Tag = page + }; + pageTreeViewItem.PreviewMouseDoubleClick += PageTreeViewItem_DoubleClick; + pageTreeViewItem.PreviewMouseLeftButtonDown += PageTreeViewItem_Click; + spaceTreeViewItem.Items.Add(pageTreeViewItem); + ConfluenceTreeView.Items.Add(spaceTreeViewItem); + } + catch (Exception ex) + { + Log.Error("Can't get homepage for space : " + space.Name + " (" + ex.Message + ")"); + } + } + + ShowBusy.Visibility = Visibility.Collapsed; + _isInitDone = true; + })); + }) + { + Name = "Loading spaces for confluence" + }.Start(); + } + } } \ No newline at end of file diff --git a/src/Greenshot.Plugin.Confluence/Forms/ConfluenceUpload.xaml.cs b/src/Greenshot.Plugin.Confluence/Forms/ConfluenceUpload.xaml.cs index 276788579..494036c24 100644 --- a/src/Greenshot.Plugin.Confluence/Forms/ConfluenceUpload.xaml.cs +++ b/src/Greenshot.Plugin.Confluence/Forms/ConfluenceUpload.xaml.cs @@ -25,92 +25,117 @@ using System.Linq; using System.Threading; using System.Windows; -namespace Greenshot.Plugin.Confluence.Forms { - /// - /// Interaction logic for ConfluenceUpload.xaml - /// - public partial class ConfluenceUpload : Window { - private System.Windows.Controls.Page _pickerPage; - public System.Windows.Controls.Page PickerPage { - get { - if (_pickerPage == null) { - List pages = ConfluenceUtils.GetCurrentPages(); - if (pages != null && pages.Count > 0) { - _pickerPage = new ConfluencePagePicker(this, pages); - } - } - return _pickerPage; - } - } +namespace Greenshot.Plugin.Confluence.Forms +{ + /// + /// Interaction logic for ConfluenceUpload.xaml + /// + public partial class ConfluenceUpload : Window + { + private System.Windows.Controls.Page _pickerPage; - private System.Windows.Controls.Page _searchPage; - public System.Windows.Controls.Page SearchPage { - get { return _searchPage ??= new ConfluenceSearch(this); } - } + public System.Windows.Controls.Page PickerPage + { + get + { + if (_pickerPage == null) + { + List pages = ConfluenceUtils.GetCurrentPages(); + if (pages != null && pages.Count > 0) + { + _pickerPage = new ConfluencePagePicker(this, pages); + } + } - private System.Windows.Controls.Page _browsePage; - public System.Windows.Controls.Page BrowsePage { - get { return _browsePage ??= new ConfluenceTreePicker(this); } - } + return _pickerPage; + } + } - private Page _selectedPage; - public Page SelectedPage { - get => _selectedPage; - set { - _selectedPage = value; - Upload.IsEnabled = _selectedPage != null; - IsOpenPageSelected = false; - } - } + private System.Windows.Controls.Page _searchPage; - public bool IsOpenPageSelected { - get; - set; - } - public string Filename { - get; - set; - } - - private static DateTime _lastLoad = DateTime.Now; - private static IList _spaces; - public IList Spaces { - get { - UpdateSpaces(); - while (_spaces == null) { - Thread.Sleep(300); - } - return _spaces; - } - } + public System.Windows.Controls.Page SearchPage + { + get { return _searchPage ??= new ConfluenceSearch(this); } + } - public ConfluenceUpload(string filename) { - Filename = filename; - InitializeComponent(); - DataContext = this; - UpdateSpaces(); - if (PickerPage == null) { - PickerTab.Visibility = Visibility.Collapsed; - SearchTab.IsSelected = true; - } - } + private System.Windows.Controls.Page _browsePage; - private void UpdateSpaces() { - if (_spaces != null && DateTime.Now.AddMinutes(-60).CompareTo(_lastLoad) > 0) { - // Reset - _spaces = null; - } - // Check if load is needed - if (_spaces == null) { - (new Thread(() => { - _spaces = ConfluencePlugin.ConfluenceConnector.GetSpaceSummaries().OrderBy(s => s.Name).ToList(); - _lastLoad = DateTime.Now; - }) { Name = "Loading spaces for confluence"}).Start(); - } - } + public System.Windows.Controls.Page BrowsePage + { + get { return _browsePage ??= new ConfluenceTreePicker(this); } + } - private void Upload_Click(object sender, RoutedEventArgs e) { - DialogResult = true; - } - } + private Page _selectedPage; + + public Page SelectedPage + { + get => _selectedPage; + set + { + _selectedPage = value; + Upload.IsEnabled = _selectedPage != null; + IsOpenPageSelected = false; + } + } + + public bool IsOpenPageSelected { get; set; } + public string Filename { get; set; } + + private static DateTime _lastLoad = DateTime.Now; + private static IList _spaces; + + public IList Spaces + { + get + { + UpdateSpaces(); + while (_spaces == null) + { + Thread.Sleep(300); + } + + return _spaces; + } + } + + public ConfluenceUpload(string filename) + { + Filename = filename; + InitializeComponent(); + DataContext = this; + UpdateSpaces(); + if (PickerPage == null) + { + PickerTab.Visibility = Visibility.Collapsed; + SearchTab.IsSelected = true; + } + } + + private void UpdateSpaces() + { + if (_spaces != null && DateTime.Now.AddMinutes(-60).CompareTo(_lastLoad) > 0) + { + // Reset + _spaces = null; + } + + // Check if load is needed + if (_spaces == null) + { + (new Thread(() => + { + _spaces = ConfluencePlugin.ConfluenceConnector.GetSpaceSummaries().OrderBy(s => s.Name).ToList(); + _lastLoad = DateTime.Now; + }) + { + Name = "Loading spaces for confluence" + }).Start(); + } + } + + private void Upload_Click(object sender, RoutedEventArgs e) + { + DialogResult = true; + } + } } \ No newline at end of file diff --git a/src/Greenshot.Plugin.Confluence/LanguageKeys.cs b/src/Greenshot.Plugin.Confluence/LanguageKeys.cs index bdbd039a7..bfee1cea8 100644 --- a/src/Greenshot.Plugin.Confluence/LanguageKeys.cs +++ b/src/Greenshot.Plugin.Confluence/LanguageKeys.cs @@ -19,10 +19,12 @@ * along with this program. If not, see . */ -namespace Greenshot.Plugin.Confluence { - public enum LangKey { - login_error, - upload_menu_item, - communication_wait - } -} +namespace Greenshot.Plugin.Confluence +{ + public enum LangKey + { + login_error, + upload_menu_item, + communication_wait + } +} \ No newline at end of file diff --git a/src/Greenshot.Plugin.Confluence/Support/ITranslationProvider.cs b/src/Greenshot.Plugin.Confluence/Support/ITranslationProvider.cs index bbabf3a0b..d189db670 100644 --- a/src/Greenshot.Plugin.Confluence/Support/ITranslationProvider.cs +++ b/src/Greenshot.Plugin.Confluence/Support/ITranslationProvider.cs @@ -1,5 +1,7 @@ -namespace Greenshot.Plugin.Confluence.Support { - public interface ITranslationProvider { +namespace Greenshot.Plugin.Confluence.Support +{ + public interface ITranslationProvider + { /// /// Translates the specified key. /// @@ -7,4 +9,4 @@ /// object Translate(string key); } -} +} \ No newline at end of file diff --git a/src/Greenshot.Plugin.Confluence/Support/LanguageChangedEventManager.cs b/src/Greenshot.Plugin.Confluence/Support/LanguageChangedEventManager.cs index 64cc4b0a3..6ca4ec384 100644 --- a/src/Greenshot.Plugin.Confluence/Support/LanguageChangedEventManager.cs +++ b/src/Greenshot.Plugin.Confluence/Support/LanguageChangedEventManager.cs @@ -22,13 +22,13 @@ namespace Greenshot.Plugin.Confluence.Support protected override void StartListening(object source) { - var manager = (TranslationManager)source; + var manager = (TranslationManager) source; manager.LanguageChanged += OnLanguageChanged; } protected override void StopListening(object source) { - var manager = (TranslationManager)source; + var manager = (TranslationManager) source; manager.LanguageChanged -= OnLanguageChanged; } @@ -37,15 +37,15 @@ namespace Greenshot.Plugin.Confluence.Support get { Type managerType = typeof(LanguageChangedEventManager); - var manager = (LanguageChangedEventManager)GetCurrentManager(managerType); + var manager = (LanguageChangedEventManager) GetCurrentManager(managerType); if (manager == null) { manager = new LanguageChangedEventManager(); SetCurrentManager(managerType, manager); } + return manager; } - } - + } } -} +} \ No newline at end of file diff --git a/src/Greenshot.Plugin.Confluence/Support/LanguageXMLTranslationProvider.cs b/src/Greenshot.Plugin.Confluence/Support/LanguageXMLTranslationProvider.cs index 4c98fd1b9..926e7ad8e 100644 --- a/src/Greenshot.Plugin.Confluence/Support/LanguageXMLTranslationProvider.cs +++ b/src/Greenshot.Plugin.Confluence/Support/LanguageXMLTranslationProvider.cs @@ -1,18 +1,23 @@ using GreenshotPlugin.Core; -namespace Greenshot.Plugin.Confluence.Support { - /// - /// - /// - public class LanguageXMLTranslationProvider : ITranslationProvider { +namespace Greenshot.Plugin.Confluence.Support +{ + /// + /// + /// + public class LanguageXMLTranslationProvider : ITranslationProvider + { /// - /// See - /// - public object Translate(string key) { - if (Language.HasKey("confluence", key)) { - return Language.GetString("confluence", key); - } - return key; - } + /// See + /// + public object Translate(string key) + { + if (Language.HasKey("confluence", key)) + { + return Language.GetString("confluence", key); + } + + return key; + } } -} +} \ No newline at end of file diff --git a/src/Greenshot.Plugin.Confluence/Support/TranslateExtension.cs b/src/Greenshot.Plugin.Confluence/Support/TranslateExtension.cs index 8d9a6e230..012f0c552 100644 --- a/src/Greenshot.Plugin.Confluence/Support/TranslateExtension.cs +++ b/src/Greenshot.Plugin.Confluence/Support/TranslateExtension.cs @@ -25,7 +25,7 @@ namespace Greenshot.Plugin.Confluence.Support public string Key { get { return _key; } - set { _key = value;} + set { _key = value; } } /// @@ -34,10 +34,10 @@ namespace Greenshot.Plugin.Confluence.Support public override object ProvideValue(IServiceProvider serviceProvider) { var binding = new Binding("Value") - { - Source = new TranslationData(_key) - }; + { + Source = new TranslationData(_key) + }; return binding.ProvideValue(serviceProvider); } } -} +} \ No newline at end of file diff --git a/src/Greenshot.Plugin.Confluence/Support/TranslationData.cs b/src/Greenshot.Plugin.Confluence/Support/TranslationData.cs index 0f920efc4..0ebce8300 100644 --- a/src/Greenshot.Plugin.Confluence/Support/TranslationData.cs +++ b/src/Greenshot.Plugin.Confluence/Support/TranslationData.cs @@ -2,44 +2,49 @@ using System.ComponentModel; using System.Windows; -namespace Greenshot.Plugin.Confluence.Support { - public class TranslationData : IWeakEventListener, INotifyPropertyChanged { +namespace Greenshot.Plugin.Confluence.Support +{ + public class TranslationData : IWeakEventListener, INotifyPropertyChanged + { private readonly string _key; /// - /// Initializes a new instance of the class. - /// - /// The key. - public TranslationData( string key) { - _key = key; - LanguageChangedEventManager.AddListener(TranslationManager.Instance, this); - } + /// Initializes a new instance of the class. + /// + /// The key. + public TranslationData(string key) + { + _key = key; + LanguageChangedEventManager.AddListener(TranslationManager.Instance, this); + } - /// - /// Releases unmanaged resources and performs other cleanup operations before the - /// is reclaimed by garbage collection. - /// - ~TranslationData() { - LanguageChangedEventManager.RemoveListener(TranslationManager.Instance, this); - } + /// + /// Releases unmanaged resources and performs other cleanup operations before the + /// is reclaimed by garbage collection. + /// + ~TranslationData() + { + LanguageChangedEventManager.RemoveListener(TranslationManager.Instance, this); + } - public object Value => TranslationManager.Instance.Translate(_key); + public object Value => TranslationManager.Instance.Translate(_key); public bool ReceiveWeakEvent(Type managerType, object sender, EventArgs e) - { - if (managerType == typeof(LanguageChangedEventManager)) - { - OnLanguageChanged(sender, e); - return true; - } - return false; - } + { + if (managerType == typeof(LanguageChangedEventManager)) + { + OnLanguageChanged(sender, e); + return true; + } - private void OnLanguageChanged(object sender, EventArgs e) - { - PropertyChanged?.Invoke( this, new PropertyChangedEventArgs("Value")); - } + return false; + } + + private void OnLanguageChanged(object sender, EventArgs e) + { + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Value")); + } public event PropertyChangedEventHandler PropertyChanged; } -} +} \ No newline at end of file diff --git a/src/Greenshot.Plugin.Confluence/Support/TranslationManager.cs b/src/Greenshot.Plugin.Confluence/Support/TranslationManager.cs index c9af13a4f..825d1aae7 100644 --- a/src/Greenshot.Plugin.Confluence/Support/TranslationManager.cs +++ b/src/Greenshot.Plugin.Confluence/Support/TranslationManager.cs @@ -1,40 +1,45 @@ using System; -namespace Greenshot.Plugin.Confluence.Support { - public class TranslationManager { - private static TranslationManager _translationManager; +namespace Greenshot.Plugin.Confluence.Support +{ + public class TranslationManager + { + private static TranslationManager _translationManager; - public event EventHandler LanguageChanged; + public event EventHandler LanguageChanged; - /*public CultureInfo CurrentLanguage { - get { return Thread.CurrentThread.CurrentUICulture; } - set { - if( value != Thread.CurrentThread.CurrentUICulture) { - Thread.CurrentThread.CurrentUICulture = value; - OnLanguageChanged(); - } - } - } + /*public CultureInfo CurrentLanguage { + get { return Thread.CurrentThread.CurrentUICulture; } + set { + if( value != Thread.CurrentThread.CurrentUICulture) { + Thread.CurrentThread.CurrentUICulture = value; + OnLanguageChanged(); + } + } + } - public IEnumerable Languages { - get { - if( TranslationProvider != null) { - return TranslationProvider.Languages; - } - return Enumerable.Empty(); - } - }*/ + public IEnumerable Languages { + get { + if( TranslationProvider != null) { + return TranslationProvider.Languages; + } + return Enumerable.Empty(); + } + }*/ - public static TranslationManager Instance => _translationManager ??= new TranslationManager(); + public static TranslationManager Instance => _translationManager ??= new TranslationManager(); - public ITranslationProvider TranslationProvider { get; set; } + public ITranslationProvider TranslationProvider { get; set; } - public object Translate(string key) { - object translatedValue = TranslationProvider?.Translate(key); - if( translatedValue != null) { - return translatedValue; - } - return $"!{key}!"; - } - } -} + public object Translate(string key) + { + object translatedValue = TranslationProvider?.Translate(key); + if (translatedValue != null) + { + return translatedValue; + } + + return $"!{key}!"; + } + } +} \ No newline at end of file diff --git a/src/Greenshot.Plugin.Dropbox/DropboxDestination.cs b/src/Greenshot.Plugin.Dropbox/DropboxDestination.cs index 7de974307..61f7a8705 100644 --- a/src/Greenshot.Plugin.Dropbox/DropboxDestination.cs +++ b/src/Greenshot.Plugin.Dropbox/DropboxDestination.cs @@ -25,38 +25,48 @@ using GreenshotPlugin.Core; using GreenshotPlugin.IniFile; using GreenshotPlugin.Interfaces; -namespace Greenshot.Plugin.Dropbox { - internal class DropboxDestination : AbstractDestination { - private static readonly DropboxPluginConfiguration DropboxConfig = IniConfig.GetIniSection(); +namespace Greenshot.Plugin.Dropbox +{ + internal class DropboxDestination : AbstractDestination + { + private static readonly DropboxPluginConfiguration DropboxConfig = IniConfig.GetIniSection(); - private readonly DropboxPlugin _plugin; - public DropboxDestination(DropboxPlugin plugin) { - _plugin = plugin; - } + private readonly DropboxPlugin _plugin; - public override string Designation => "Dropbox"; + public DropboxDestination(DropboxPlugin plugin) + { + _plugin = plugin; + } - public override string Description => Language.GetString("dropbox", LangKey.upload_menu_item); + public override string Designation => "Dropbox"; - public override Image DisplayIcon { - get { - ComponentResourceManager resources = new ComponentResourceManager(typeof(DropboxPlugin)); - return (Image)resources.GetObject("Dropbox"); - } - } + public override string Description => Language.GetString("dropbox", LangKey.upload_menu_item); - public override ExportInformation ExportCapture(bool manually, ISurface surface, ICaptureDetails captureDetails) { - ExportInformation exportInformation = new ExportInformation(Designation, Description); - bool uploaded = _plugin.Upload(captureDetails, surface, out var uploadUrl); - if (uploaded) { - exportInformation.Uri = uploadUrl; - exportInformation.ExportMade = true; - if (DropboxConfig.AfterUploadLinkToClipBoard) { - ClipboardHelper.SetClipboardData(uploadUrl); - } - } - ProcessExport(exportInformation, surface); - return exportInformation; - } - } -} + public override Image DisplayIcon + { + get + { + ComponentResourceManager resources = new ComponentResourceManager(typeof(DropboxPlugin)); + return (Image) resources.GetObject("Dropbox"); + } + } + + public override ExportInformation ExportCapture(bool manually, ISurface surface, ICaptureDetails captureDetails) + { + ExportInformation exportInformation = new ExportInformation(Designation, Description); + bool uploaded = _plugin.Upload(captureDetails, surface, out var uploadUrl); + if (uploaded) + { + exportInformation.Uri = uploadUrl; + exportInformation.ExportMade = true; + if (DropboxConfig.AfterUploadLinkToClipBoard) + { + ClipboardHelper.SetClipboardData(uploadUrl); + } + } + + ProcessExport(exportInformation, surface); + return exportInformation; + } + } +} \ No newline at end of file diff --git a/src/Greenshot.Plugin.Dropbox/DropboxPlugin.cs b/src/Greenshot.Plugin.Dropbox/DropboxPlugin.cs index abd83112f..1ec893423 100644 --- a/src/Greenshot.Plugin.Dropbox/DropboxPlugin.cs +++ b/src/Greenshot.Plugin.Dropbox/DropboxPlugin.cs @@ -29,93 +29,101 @@ using GreenshotPlugin.IniFile; using GreenshotPlugin.Interfaces; using GreenshotPlugin.Interfaces.Plugin; -namespace Greenshot.Plugin.Dropbox { - /// - /// This is the Dropbox base code - /// +namespace Greenshot.Plugin.Dropbox +{ + /// + /// This is the Dropbox base code + /// [Plugin("Dropbox", true)] - public class DropboxPlugin : IGreenshotPlugin { - private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(typeof(DropboxPlugin)); - private static DropboxPluginConfiguration _config; - private ComponentResourceManager _resources; - private ToolStripMenuItem _itemPlugInConfig; + public class DropboxPlugin : IGreenshotPlugin + { + private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(typeof(DropboxPlugin)); + private static DropboxPluginConfiguration _config; + private ComponentResourceManager _resources; + private ToolStripMenuItem _itemPlugInConfig; - public void Dispose() { - Dispose(true); - GC.SuppressFinalize(this); - } + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } - private void Dispose(bool disposing) - { - if (!disposing) return; - if (_itemPlugInConfig == null) return; - _itemPlugInConfig.Dispose(); - _itemPlugInConfig = null; - } + private void Dispose(bool disposing) + { + if (!disposing) return; + if (_itemPlugInConfig == null) return; + _itemPlugInConfig.Dispose(); + _itemPlugInConfig = null; + } - /// - /// Implementation of the IGreenshotPlugin.Initialize - /// - public bool Initialize() { - - // Register configuration (don't need the configuration itself) - _config = IniConfig.GetIniSection(); - _resources = new ComponentResourceManager(typeof(DropboxPlugin)); + /// + /// Implementation of the IGreenshotPlugin.Initialize + /// + public bool Initialize() + { + // Register configuration (don't need the configuration itself) + _config = IniConfig.GetIniSection(); + _resources = new ComponentResourceManager(typeof(DropboxPlugin)); SimpleServiceProvider.Current.AddService(new DropboxDestination(this)); - _itemPlugInConfig = new ToolStripMenuItem - { - Text = Language.GetString("dropbox", LangKey.Configure), - Image = (Image)_resources.GetObject("Dropbox") - }; - _itemPlugInConfig.Click += ConfigMenuClick; + _itemPlugInConfig = new ToolStripMenuItem + { + Text = Language.GetString("dropbox", LangKey.Configure), + Image = (Image) _resources.GetObject("Dropbox") + }; + _itemPlugInConfig.Click += ConfigMenuClick; - PluginUtils.AddToContextMenu(_itemPlugInConfig); - Language.LanguageChanged += OnLanguageChanged; - return true; - } + PluginUtils.AddToContextMenu(_itemPlugInConfig); + Language.LanguageChanged += OnLanguageChanged; + return true; + } - public void OnLanguageChanged(object sender, EventArgs e) { - if (_itemPlugInConfig != null) { - _itemPlugInConfig.Text = Language.GetString("dropbox", LangKey.Configure); - } - } + public void OnLanguageChanged(object sender, EventArgs e) + { + if (_itemPlugInConfig != null) + { + _itemPlugInConfig.Text = Language.GetString("dropbox", LangKey.Configure); + } + } - public void Shutdown() { - Log.Debug("Dropbox Plugin shutdown."); - } + public void Shutdown() + { + Log.Debug("Dropbox Plugin shutdown."); + } - /// - /// Implementation of the IPlugin.Configure - /// - public void Configure() { - _config.ShowConfigDialog(); - } + /// + /// Implementation of the IPlugin.Configure + /// + public void Configure() + { + _config.ShowConfigDialog(); + } - public void ConfigMenuClick(object sender, EventArgs eventArgs) { - _config.ShowConfigDialog(); - } + public void ConfigMenuClick(object sender, EventArgs eventArgs) + { + _config.ShowConfigDialog(); + } - /// - /// This will be called when the menu item in the Editor is clicked - /// - public bool Upload(ICaptureDetails captureDetails, ISurface surfaceToUpload, out string uploadUrl) { - uploadUrl = null; - SurfaceOutputSettings outputSettings = new SurfaceOutputSettings(_config.UploadFormat, _config.UploadJpegQuality, false); - try + /// + /// This will be called when the menu item in the Editor is clicked + /// + public bool Upload(ICaptureDetails captureDetails, ISurface surfaceToUpload, out string uploadUrl) + { + uploadUrl = null; + SurfaceOutputSettings outputSettings = new SurfaceOutputSettings(_config.UploadFormat, _config.UploadJpegQuality, false); + try { bool result = false; - new PleaseWaitForm().ShowAndWait("Dropbox", Language.GetString("dropbox", LangKey.communication_wait), - delegate - { - result = DropboxUtils.UploadToDropbox(surfaceToUpload, outputSettings, captureDetails); - } - ); + new PleaseWaitForm().ShowAndWait("Dropbox", Language.GetString("dropbox", LangKey.communication_wait), + delegate { result = DropboxUtils.UploadToDropbox(surfaceToUpload, outputSettings, captureDetails); } + ); return result; - } catch (Exception e) { - Log.Error(e); - MessageBox.Show(Language.GetString("dropbox", LangKey.upload_failure) + " " + e.Message); - return false; - } - } - } -} + } + catch (Exception e) + { + Log.Error(e); + MessageBox.Show(Language.GetString("dropbox", LangKey.upload_failure) + " " + e.Message); + return false; + } + } + } +} \ No newline at end of file diff --git a/src/Greenshot.Plugin.Dropbox/DropboxPluginConfiguration.cs b/src/Greenshot.Plugin.Dropbox/DropboxPluginConfiguration.cs index 1b738cdeb..4a2bd95d0 100644 --- a/src/Greenshot.Plugin.Dropbox/DropboxPluginConfiguration.cs +++ b/src/Greenshot.Plugin.Dropbox/DropboxPluginConfiguration.cs @@ -25,25 +25,27 @@ using Greenshot.Plugin.Dropbox.Forms; using GreenshotPlugin.Core; using GreenshotPlugin.IniFile; -namespace Greenshot.Plugin.Dropbox { - /// - /// Description of ImgurConfiguration. - /// - [IniSection("Dropbox", Description = "Greenshot Dropbox Plugin configuration")] - public class DropboxPluginConfiguration : IniSection { - [IniProperty("UploadFormat", Description="What file type to use for uploading", DefaultValue="png")] - public OutputFormat UploadFormat { get; set; } +namespace Greenshot.Plugin.Dropbox +{ + /// + /// Description of ImgurConfiguration. + /// + [IniSection("Dropbox", Description = "Greenshot Dropbox Plugin configuration")] + public class DropboxPluginConfiguration : IniSection + { + [IniProperty("UploadFormat", Description = "What file type to use for uploading", DefaultValue = "png")] + public OutputFormat UploadFormat { get; set; } - [IniProperty("UploadJpegQuality", Description="JPEG file save quality in %.", DefaultValue="80")] - public int UploadJpegQuality { get; set; } + [IniProperty("UploadJpegQuality", Description = "JPEG file save quality in %.", DefaultValue = "80")] + public int UploadJpegQuality { get; set; } - [IniProperty("AfterUploadLinkToClipBoard", Description = "After upload send Dropbox link to clipboard.", DefaultValue = "true")] - public bool AfterUploadLinkToClipBoard { get; set; } + [IniProperty("AfterUploadLinkToClipBoard", Description = "After upload send Dropbox link to clipboard.", DefaultValue = "true")] + public bool AfterUploadLinkToClipBoard { get; set; } [IniProperty("RefreshToken", Description = "Dropbox refresh Token", Encrypted = true, ExcludeIfNull = true)] public string RefreshToken { get; set; } - /// + /// /// AccessToken, not stored /// public string AccessToken { get; set; } @@ -53,16 +55,19 @@ namespace Greenshot.Plugin.Dropbox { /// public DateTimeOffset AccessTokenExpires { get; set; } - /// - /// A form for token - /// - /// bool true if OK was pressed, false if cancel - public bool ShowConfigDialog() { - DialogResult result = new SettingsForm().ShowDialog(); - if (result == DialogResult.OK) { - return true; - } - return false; - } - } -} + /// + /// A form for token + /// + /// bool true if OK was pressed, false if cancel + public bool ShowConfigDialog() + { + DialogResult result = new SettingsForm().ShowDialog(); + if (result == DialogResult.OK) + { + return true; + } + + return false; + } + } +} \ No newline at end of file diff --git a/src/Greenshot.Plugin.Dropbox/DropboxUtils.cs b/src/Greenshot.Plugin.Dropbox/DropboxUtils.cs index dd130c9bc..ebb7b2685 100644 --- a/src/Greenshot.Plugin.Dropbox/DropboxUtils.cs +++ b/src/Greenshot.Plugin.Dropbox/DropboxUtils.cs @@ -29,19 +29,22 @@ using GreenshotPlugin.Interfaces; using GreenshotPlugin.Interfaces.Plugin; using Newtonsoft.Json; -namespace Greenshot.Plugin.Dropbox { - /// - /// Description of DropboxUtils. - /// - public class DropboxUtils { - private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(typeof(DropboxUtils)); - private static readonly DropboxPluginConfiguration DropboxConfig = IniConfig.GetIniSection(); +namespace Greenshot.Plugin.Dropbox +{ + /// + /// Description of DropboxUtils. + /// + public class DropboxUtils + { + private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(typeof(DropboxUtils)); + private static readonly DropboxPluginConfiguration DropboxConfig = IniConfig.GetIniSection(); - private DropboxUtils() { - } + private DropboxUtils() + { + } - public static bool UploadToDropbox(ISurface surfaceToUpload, SurfaceOutputSettings outputSettings, ICaptureDetails captureDetails) - { + public static bool UploadToDropbox(ISurface surfaceToUpload, SurfaceOutputSettings outputSettings, ICaptureDetails captureDetails) + { var oauth2Settings = new OAuth2Settings { AuthUrlPattern = "https://api.dropbox.com/oauth2/authorize?response_type=token&client_id={ClientId}&state={State}&redirect_uri={RedirectUrl}", @@ -51,24 +54,32 @@ namespace Greenshot.Plugin.Dropbox { ClientId = DropBoxCredentials.CONSUMER_KEY, ClientSecret = DropBoxCredentials.CONSUMER_SECRET, AuthorizeMode = OAuth2AuthorizeMode.JsonReceiver, - RefreshToken = DropboxConfig.RefreshToken, + RefreshToken = DropboxConfig.RefreshToken, AccessToken = DropboxConfig.AccessToken, AccessTokenExpires = DropboxConfig.AccessTokenExpires - }; + }; try - { + { string filename = Path.GetFileName(FilenameHelper.GetFilename(DropboxConfig.UploadFormat, captureDetails)); SurfaceContainer image = new SurfaceContainer(surfaceToUpload, outputSettings, filename); IDictionary arguments = new Dictionary { - { "autorename", true }, - { "mute", true }, - { "path", "/" + filename.Replace(Path.DirectorySeparatorChar, '\\')} + { + "autorename", true + }, + { + "mute", true + }, + { + "path", "/" + filename.Replace(Path.DirectorySeparatorChar, '\\') + } }; IDictionary headers = new Dictionary { - { "Dropbox-API-Arg", JsonConvert.SerializeObject(arguments)} + { + "Dropbox-API-Arg", JsonConvert.SerializeObject(arguments) + } }; var webRequest = OAuth2Helper.CreateOAuth2WebRequest(HTTPMethod.POST, "https://content.dropboxapi.com/2/files/upload", oauth2Settings); @@ -78,16 +89,19 @@ namespace Greenshot.Plugin.Dropbox { var response = JsonConvert.DeserializeObject>(responseString); return response.ContainsKey("id"); } - catch (Exception ex) { - Log.Error("Upload error: ", ex); - throw; - } finally { + catch (Exception ex) + { + Log.Error("Upload error: ", ex); + throw; + } + finally + { DropboxConfig.RefreshToken = oauth2Settings.RefreshToken; DropboxConfig.AccessToken = oauth2Settings.AccessToken; DropboxConfig.AccessTokenExpires = oauth2Settings.AccessTokenExpires; DropboxConfig.IsDirty = true; IniConfig.Save(); } - } - } -} + } + } +} \ No newline at end of file diff --git a/src/Greenshot.Plugin.Dropbox/Forms/DropboxForm.cs b/src/Greenshot.Plugin.Dropbox/Forms/DropboxForm.cs index c7aaecb1d..b39d1e14d 100644 --- a/src/Greenshot.Plugin.Dropbox/Forms/DropboxForm.cs +++ b/src/Greenshot.Plugin.Dropbox/Forms/DropboxForm.cs @@ -21,7 +21,9 @@ using GreenshotPlugin.Controls; -namespace Greenshot.Plugin.Dropbox.Forms { - public class DropboxForm : GreenshotForm { - } -} +namespace Greenshot.Plugin.Dropbox.Forms +{ + public class DropboxForm : GreenshotForm + { + } +} \ No newline at end of file diff --git a/src/Greenshot.Plugin.Dropbox/Forms/SettingsForm.cs b/src/Greenshot.Plugin.Dropbox/Forms/SettingsForm.cs index 9586c9ca7..c79844e6d 100644 --- a/src/Greenshot.Plugin.Dropbox/Forms/SettingsForm.cs +++ b/src/Greenshot.Plugin.Dropbox/Forms/SettingsForm.cs @@ -19,19 +19,21 @@ * along with this program. If not, see . */ -namespace Greenshot.Plugin.Dropbox.Forms { - /// - /// Description of PasswordRequestForm. - /// - public partial class SettingsForm : DropboxForm { - public SettingsForm() { - // - // The InitializeComponent() call is required for Windows Forms designer support. - // - InitializeComponent(); - AcceptButton = buttonOK; - CancelButton = buttonCancel; - } - - } -} +namespace Greenshot.Plugin.Dropbox.Forms +{ + /// + /// Description of PasswordRequestForm. + /// + public partial class SettingsForm : DropboxForm + { + public SettingsForm() + { + // + // The InitializeComponent() call is required for Windows Forms designer support. + // + InitializeComponent(); + AcceptButton = buttonOK; + CancelButton = buttonCancel; + } + } +} \ No newline at end of file diff --git a/src/Greenshot.Plugin.Dropbox/LanguageKeys.cs b/src/Greenshot.Plugin.Dropbox/LanguageKeys.cs index 9b42f6c75..37d6b50e3 100644 --- a/src/Greenshot.Plugin.Dropbox/LanguageKeys.cs +++ b/src/Greenshot.Plugin.Dropbox/LanguageKeys.cs @@ -18,11 +18,14 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -namespace Greenshot.Plugin.Dropbox { - public enum LangKey { - upload_menu_item, - upload_failure, - communication_wait, - Configure + +namespace Greenshot.Plugin.Dropbox +{ + public enum LangKey + { + upload_menu_item, + upload_failure, + communication_wait, + Configure } -} +} \ No newline at end of file diff --git a/src/Greenshot.Plugin.Dropbox/Properties/AssemblyInfo.cs b/src/Greenshot.Plugin.Dropbox/Properties/AssemblyInfo.cs index 9e9b18e04..5870d89ea 100644 --- a/src/Greenshot.Plugin.Dropbox/Properties/AssemblyInfo.cs +++ b/src/Greenshot.Plugin.Dropbox/Properties/AssemblyInfo.cs @@ -29,4 +29,4 @@ using System.Runtime.InteropServices; // This sets the default COM visibility of types in the assembly to invisible. // If you need to expose a type to COM, use [ComVisible(true)] on that type. -[assembly: ComVisible(false)] +[assembly: ComVisible(false)] \ No newline at end of file diff --git a/src/Greenshot.Plugin.ExternalCommand/ExternalCommandConfiguration.cs b/src/Greenshot.Plugin.ExternalCommand/ExternalCommandConfiguration.cs index e4f345f47..8d677e200 100644 --- a/src/Greenshot.Plugin.ExternalCommand/ExternalCommandConfiguration.cs +++ b/src/Greenshot.Plugin.ExternalCommand/ExternalCommandConfiguration.cs @@ -25,122 +25,140 @@ using System.IO; using GreenshotPlugin.Core; using GreenshotPlugin.IniFile; -namespace Greenshot.Plugin.ExternalCommand { - /// - /// Description of FlickrConfiguration. - /// - [IniSection("ExternalCommand", Description="Greenshot ExternalCommand Plugin configuration")] - public class ExternalCommandConfiguration : IniSection { - [IniProperty("Commands", Description="The commands that are available.")] - public List Commands { get; set; } +namespace Greenshot.Plugin.ExternalCommand +{ + /// + /// Description of FlickrConfiguration. + /// + [IniSection("ExternalCommand", Description = "Greenshot ExternalCommand Plugin configuration")] + public class ExternalCommandConfiguration : IniSection + { + [IniProperty("Commands", Description = "The commands that are available.")] + public List Commands { get; set; } - [IniProperty("RedirectStandardError", Description = "Redirect the standard error of all external commands, used to output as warning to the greenshot.log.", DefaultValue = "true")] - public bool RedirectStandardError { get; set; } + [IniProperty("RedirectStandardError", Description = "Redirect the standard error of all external commands, used to output as warning to the greenshot.log.", + DefaultValue = "true")] + public bool RedirectStandardError { get; set; } - [IniProperty("RedirectStandardOutput", Description = "Redirect the standard output of all external commands, used for different other functions (more below).", DefaultValue = "true")] - public bool RedirectStandardOutput { get; set; } + [IniProperty("RedirectStandardOutput", Description = "Redirect the standard output of all external commands, used for different other functions (more below).", + DefaultValue = "true")] + public bool RedirectStandardOutput { get; set; } - [IniProperty("ShowStandardOutputInLog", Description = "Depends on 'RedirectStandardOutput': Show standard output of all external commands to the Greenshot log, this can be usefull for debugging.", DefaultValue = "false")] - public bool ShowStandardOutputInLog { get; set; } + [IniProperty("ShowStandardOutputInLog", + Description = "Depends on 'RedirectStandardOutput': Show standard output of all external commands to the Greenshot log, this can be usefull for debugging.", + DefaultValue = "false")] + public bool ShowStandardOutputInLog { get; set; } - [IniProperty("ParseForUri", Description = "Depends on 'RedirectStandardOutput': Parse the output and take the first found URI, if a URI is found than clicking on the notify bubble goes there.", DefaultValue = "true")] - public bool ParseOutputForUri { get; set; } + [IniProperty("ParseForUri", + Description = "Depends on 'RedirectStandardOutput': Parse the output and take the first found URI, if a URI is found than clicking on the notify bubble goes there.", + DefaultValue = "true")] + public bool ParseOutputForUri { get; set; } - [IniProperty("OutputToClipboard", Description = "Depends on 'RedirectStandardOutput': Place the standard output on the clipboard.", DefaultValue = "false")] - public bool OutputToClipboard { get; set; } + [IniProperty("OutputToClipboard", Description = "Depends on 'RedirectStandardOutput': Place the standard output on the clipboard.", DefaultValue = "false")] + public bool OutputToClipboard { get; set; } - [IniProperty("UriToClipboard", Description = "Depends on 'RedirectStandardOutput' & 'ParseForUri': If an URI is found in the standard input, place it on the clipboard. (This overwrites the output from OutputToClipboard setting.)", DefaultValue = "true")] - public bool UriToClipboard { get; set; } + [IniProperty("UriToClipboard", + Description = + "Depends on 'RedirectStandardOutput' & 'ParseForUri': If an URI is found in the standard input, place it on the clipboard. (This overwrites the output from OutputToClipboard setting.)", + DefaultValue = "true")] + public bool UriToClipboard { get; set; } - [IniProperty("Commandline", Description="The commandline for the output command.")] - public Dictionary Commandline { get; set; } + [IniProperty("Commandline", Description = "The commandline for the output command.")] + public Dictionary Commandline { get; set; } - [IniProperty("Argument", Description="The arguments for the output command.")] - public Dictionary Argument { get; set; } + [IniProperty("Argument", Description = "The arguments for the output command.")] + public Dictionary Argument { get; set; } - [IniProperty("RunInbackground", Description = "Should the command be started in the background.")] - public Dictionary RunInbackground { get; set; } + [IniProperty("RunInbackground", Description = "Should the command be started in the background.")] + public Dictionary RunInbackground { get; set; } - [IniProperty("DeletedBuildInCommands", Description = "If a build in command was deleted manually, it should not be recreated.")] - public List DeletedBuildInCommands { get; set; } + [IniProperty("DeletedBuildInCommands", Description = "If a build in command was deleted manually, it should not be recreated.")] + public List DeletedBuildInCommands { get; set; } - private const string MsPaint = "MS Paint"; - private static readonly string PaintPath; - private static readonly bool HasPaint; + private const string MsPaint = "MS Paint"; + private static readonly string PaintPath; + private static readonly bool HasPaint; - private const string PaintDotNet = "Paint.NET"; - private static readonly string PaintDotNetPath; - private static readonly bool HasPaintDotNet; - static ExternalCommandConfiguration() { - try { - PaintPath = PluginUtils.GetExePath("pbrush.exe"); - HasPaint = !string.IsNullOrEmpty(PaintPath) && File.Exists(PaintPath); - } catch { - // Ignore - } - try - { - PaintDotNetPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), @"Paint.NET\PaintDotNet.exe"); - HasPaintDotNet = !string.IsNullOrEmpty(PaintDotNetPath) && File.Exists(PaintDotNetPath); - } - catch - { - // Ignore - } - } + private const string PaintDotNet = "Paint.NET"; + private static readonly string PaintDotNetPath; + private static readonly bool HasPaintDotNet; - /// - /// Delete the configuration for the specified command - /// - /// string with command - public void Delete(string command) - { - if (string.IsNullOrEmpty(command)) - { - return; - } - Commands.Remove(command); - Commandline.Remove(command); - Argument.Remove(command); - RunInbackground.Remove(command); - if (MsPaint.Equals(command) || PaintDotNet.Equals(command)) - { - if (!DeletedBuildInCommands.Contains(command)) - { - DeletedBuildInCommands.Add(command); - } - } - } + static ExternalCommandConfiguration() + { + try + { + PaintPath = PluginUtils.GetExePath("pbrush.exe"); + HasPaint = !string.IsNullOrEmpty(PaintPath) && File.Exists(PaintPath); + } + catch + { + // Ignore + } - public override void AfterLoad() - { - base.AfterLoad(); + try + { + PaintDotNetPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), @"Paint.NET\PaintDotNet.exe"); + HasPaintDotNet = !string.IsNullOrEmpty(PaintDotNetPath) && File.Exists(PaintDotNetPath); + } + catch + { + // Ignore + } + } - // Check if we need to add MsPaint - if (HasPaint && !Commands.Contains(MsPaint) && !DeletedBuildInCommands.Contains(MsPaint)) - { - Commands.Add(MsPaint); - Commandline.Add(MsPaint, PaintPath); - Argument.Add(MsPaint, "\"{0}\""); - RunInbackground.Add(MsPaint, true); - } + /// + /// Delete the configuration for the specified command + /// + /// string with command + public void Delete(string command) + { + if (string.IsNullOrEmpty(command)) + { + return; + } - // Check if we need to add Paint.NET - if (HasPaintDotNet && !Commands.Contains(PaintDotNet) && !DeletedBuildInCommands.Contains(PaintDotNet)) - { - Commands.Add(PaintDotNet); - Commandline.Add(PaintDotNet, PaintDotNetPath); - Argument.Add(PaintDotNet, "\"{0}\""); - RunInbackground.Add(PaintDotNet, true); - } - } + Commands.Remove(command); + Commandline.Remove(command); + Argument.Remove(command); + RunInbackground.Remove(command); + if (MsPaint.Equals(command) || PaintDotNet.Equals(command)) + { + if (!DeletedBuildInCommands.Contains(command)) + { + DeletedBuildInCommands.Add(command); + } + } + } - /// - /// Supply values we can't put as defaults - /// - /// The property to return a default for - /// object with the default value for the supplied property - public override object GetDefault(string property) => + public override void AfterLoad() + { + base.AfterLoad(); + + // Check if we need to add MsPaint + if (HasPaint && !Commands.Contains(MsPaint) && !DeletedBuildInCommands.Contains(MsPaint)) + { + Commands.Add(MsPaint); + Commandline.Add(MsPaint, PaintPath); + Argument.Add(MsPaint, "\"{0}\""); + RunInbackground.Add(MsPaint, true); + } + + // Check if we need to add Paint.NET + if (HasPaintDotNet && !Commands.Contains(PaintDotNet) && !DeletedBuildInCommands.Contains(PaintDotNet)) + { + Commands.Add(PaintDotNet); + Commandline.Add(PaintDotNet, PaintDotNetPath); + Argument.Add(PaintDotNet, "\"{0}\""); + RunInbackground.Add(PaintDotNet, true); + } + } + + /// + /// Supply values we can't put as defaults + /// + /// The property to return a default for + /// object with the default value for the supplied property + public override object GetDefault(string property) => property switch { nameof(DeletedBuildInCommands) => (object) new List(), @@ -151,4 +169,4 @@ namespace Greenshot.Plugin.ExternalCommand { _ => null }; } -} +} \ No newline at end of file diff --git a/src/Greenshot.Plugin.ExternalCommand/ExternalCommandDestination.cs b/src/Greenshot.Plugin.ExternalCommand/ExternalCommandDestination.cs index df9b25352..c7ab3b957 100644 --- a/src/Greenshot.Plugin.ExternalCommand/ExternalCommandDestination.cs +++ b/src/Greenshot.Plugin.ExternalCommand/ExternalCommandDestination.cs @@ -31,146 +31,184 @@ using GreenshotPlugin.IniFile; using GreenshotPlugin.Interfaces; using GreenshotPlugin.Interfaces.Plugin; -namespace Greenshot.Plugin.ExternalCommand { - /// - /// Description of OCRDestination. - /// - public class ExternalCommandDestination : AbstractDestination { - private static readonly log4net.ILog LOG = log4net.LogManager.GetLogger(typeof(ExternalCommandDestination)); - private static readonly Regex URI_REGEXP = new Regex(@"((([A-Za-z]{3,9}:(?:\/\/)?)(?:[\-;:&=\+\$,\w]+@)?[A-Za-z0-9\.\-]+|(?:www\.|[\-;:&=\+\$,\w]+@)[A-Za-z0-9\.\-]+)((?:\/[\+~%\/\.\w\-_]*)?\??(?:[\-\+=&;%@\.\w_]*)#?(?:[\.\!\/\\\w]*))?)"); - private static readonly ExternalCommandConfiguration config = IniConfig.GetIniSection(); - private readonly string _presetCommand; - - public ExternalCommandDestination(string commando) { - _presetCommand = commando; - } +namespace Greenshot.Plugin.ExternalCommand +{ + /// + /// Description of OCRDestination. + /// + public class ExternalCommandDestination : AbstractDestination + { + private static readonly log4net.ILog LOG = log4net.LogManager.GetLogger(typeof(ExternalCommandDestination)); - public override string Designation => "External " + _presetCommand.Replace(',','_'); + private static readonly Regex URI_REGEXP = + new Regex( + @"((([A-Za-z]{3,9}:(?:\/\/)?)(?:[\-;:&=\+\$,\w]+@)?[A-Za-z0-9\.\-]+|(?:www\.|[\-;:&=\+\$,\w]+@)[A-Za-z0-9\.\-]+)((?:\/[\+~%\/\.\w\-_]*)?\??(?:[\-\+=&;%@\.\w_]*)#?(?:[\.\!\/\\\w]*))?)"); - public override string Description => _presetCommand; + private static readonly ExternalCommandConfiguration config = IniConfig.GetIniSection(); + private readonly string _presetCommand; - public override IEnumerable DynamicDestinations() { - yield break; - } + public ExternalCommandDestination(string commando) + { + _presetCommand = commando; + } - public override Image DisplayIcon => IconCache.IconForCommand(_presetCommand); + public override string Designation => "External " + _presetCommand.Replace(',', '_'); - public override ExportInformation ExportCapture(bool manuallyInitiated, ISurface surface, ICaptureDetails captureDetails) { - ExportInformation exportInformation = new ExportInformation(Designation, Description); - SurfaceOutputSettings outputSettings = new SurfaceOutputSettings(); - outputSettings.PreventGreenshotFormat(); + public override string Description => _presetCommand; - if (_presetCommand != null) { - if (!config.RunInbackground.ContainsKey(_presetCommand)) { - config.RunInbackground.Add(_presetCommand, true); - } - bool runInBackground = config.RunInbackground[_presetCommand]; - string fullPath = captureDetails.Filename ?? ImageOutput.SaveNamedTmpFile(surface, captureDetails, outputSettings); + public override IEnumerable DynamicDestinations() + { + yield break; + } + + 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); + } + + bool runInBackground = config.RunInbackground[_presetCommand]; + string fullPath = captureDetails.Filename ?? ImageOutput.SaveNamedTmpFile(surface, captureDetails, outputSettings); string output; - string error; - if (runInBackground) { - Thread commandThread = new Thread(delegate() - { - CallExternalCommand(exportInformation, fullPath, out output, out error); - ProcessExport(exportInformation, surface); - }) - { - Name = "Running " + _presetCommand, - IsBackground = true - }; - commandThread.SetApartmentState(ApartmentState.STA); - commandThread.Start(); - exportInformation.ExportMade = true; - } else { - CallExternalCommand(exportInformation, fullPath, out output, out error); - ProcessExport(exportInformation, surface); - } - } - return exportInformation; - } + string error; + if (runInBackground) + { + Thread commandThread = new Thread(delegate() + { + CallExternalCommand(exportInformation, fullPath, out output, out error); + ProcessExport(exportInformation, surface); + }) + { + Name = "Running " + _presetCommand, + IsBackground = true + }; + commandThread.SetApartmentState(ApartmentState.STA); + commandThread.Start(); + exportInformation.ExportMade = true; + } + else + { + CallExternalCommand(exportInformation, fullPath, out output, out error); + ProcessExport(exportInformation, surface); + } + } - /// - /// Wrapper method for the background and normal call, this does all the logic: - /// Call the external command, parse for URI, place to clipboard and set the export information - /// - /// - /// - /// - /// - private void CallExternalCommand(ExportInformation exportInformation, string fullPath, out string output, out string error) { - output = null; - error = null; - try { - if (CallExternalCommand(_presetCommand, fullPath, out output, out error) == 0) { - exportInformation.ExportMade = true; - if (!string.IsNullOrEmpty(output)) { - MatchCollection uriMatches = URI_REGEXP.Matches(output); - // Place output on the clipboard before the URI, so if one is found this overwrites - if (config.OutputToClipboard) { - ClipboardHelper.SetClipboardData(output); - } - if (uriMatches.Count > 0) { - exportInformation.Uri = uriMatches[0].Groups[1].Value; - LOG.InfoFormat("Got URI : {0} ", exportInformation.Uri); - if (config.UriToClipboard) { - ClipboardHelper.SetClipboardData(exportInformation.Uri); - } - } - } - } else { - LOG.WarnFormat("Error calling external command: {0} ", output); - exportInformation.ExportMade = false; - exportInformation.ErrorMessage = error; - } - } catch (Exception ex) { - exportInformation.ExportMade = false; - exportInformation.ErrorMessage = ex.Message; - LOG.WarnFormat("Error calling external command: {0} ", exportInformation.ErrorMessage); - } - } + return exportInformation; + } - /// - /// Wrapper to retry with a runas - /// - /// - /// - /// - /// - /// - 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) { - 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]); - throw; - } - } catch (Exception ex) { - ex.Data.Add("commandline", config.Commandline[_presetCommand]); - ex.Data.Add("arguments", config.Argument[_presetCommand]); - throw; - } - } + /// + /// Wrapper method for the background and normal call, this does all the logic: + /// Call the external command, parse for URI, place to clipboard and set the export information + /// + /// + /// + /// + /// + private void CallExternalCommand(ExportInformation exportInformation, string fullPath, out string output, out string error) + { + output = null; + error = null; + try + { + if (CallExternalCommand(_presetCommand, fullPath, out output, out error) == 0) + { + exportInformation.ExportMade = true; + if (!string.IsNullOrEmpty(output)) + { + MatchCollection uriMatches = URI_REGEXP.Matches(output); + // Place output on the clipboard before the URI, so if one is found this overwrites + if (config.OutputToClipboard) + { + ClipboardHelper.SetClipboardData(output); + } - /// - /// The actual executing code for the external command - /// - /// - /// - /// - /// - /// - /// - private int CallExternalCommand(string commando, string fullPath, string verb, out string output, out string error) { - string commandline = config.Commandline[commando]; - string arguments = config.Argument[commando]; - output = null; - error = null; - if (!string.IsNullOrEmpty(commandline)) + if (uriMatches.Count > 0) + { + exportInformation.Uri = uriMatches[0].Groups[1].Value; + LOG.InfoFormat("Got URI : {0} ", exportInformation.Uri); + if (config.UriToClipboard) + { + ClipboardHelper.SetClipboardData(exportInformation.Uri); + } + } + } + } + else + { + LOG.WarnFormat("Error calling external command: {0} ", output); + exportInformation.ExportMade = false; + exportInformation.ErrorMessage = error; + } + } + catch (Exception ex) + { + exportInformation.ExportMade = false; + exportInformation.ErrorMessage = ex.Message; + LOG.WarnFormat("Error calling external command: {0} ", exportInformation.ErrorMessage); + } + } + + /// + /// Wrapper to retry with a runas + /// + /// + /// + /// + /// + /// + 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) + { + 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]); + throw; + } + } + catch (Exception ex) + { + ex.Data.Add("commandline", config.Commandline[_presetCommand]); + ex.Data.Add("arguments", config.Argument[_presetCommand]); + throw; + } + } + + /// + /// The actual executing code for the external command + /// + /// + /// + /// + /// + /// + /// + private int CallExternalCommand(string commando, string fullPath, string verb, out string output, out string error) + { + string commandline = config.Commandline[commando]; + string arguments = config.Argument[commando]; + output = null; + error = null; + if (!string.IsNullOrEmpty(commandline)) { using Process process = new Process(); // Fix variables @@ -183,39 +221,52 @@ namespace Greenshot.Plugin.ExternalCommand { process.StartInfo.FileName = FilenameHelper.FillCmdVariables(commandline, true); process.StartInfo.Arguments = FormatArguments(arguments, fullPath); process.StartInfo.UseShellExecute = false; - if (config.RedirectStandardOutput) { + if (config.RedirectStandardOutput) + { process.StartInfo.RedirectStandardOutput = true; } - if (config.RedirectStandardError) { + + if (config.RedirectStandardError) + { process.StartInfo.RedirectStandardError = true; } - if (verb != null) { + + if (verb != null) + { process.StartInfo.Verb = verb; } + LOG.InfoFormat("Starting : {0} {1}", process.StartInfo.FileName, process.StartInfo.Arguments); process.Start(); process.WaitForExit(); - if (config.RedirectStandardOutput) { + if (config.RedirectStandardOutput) + { output = process.StandardOutput.ReadToEnd(); - if (config.ShowStandardOutputInLog && output.Trim().Length > 0) { + if (config.ShowStandardOutputInLog && output.Trim().Length > 0) + { LOG.InfoFormat("Output:\n{0}", output); } } - if (config.RedirectStandardError) { + + if (config.RedirectStandardError) + { error = process.StandardError.ReadToEnd(); - if (error.Trim().Length > 0) { + if (error.Trim().Length > 0) + { LOG.WarnFormat("Error:\n{0}", error); } } + LOG.InfoFormat("Finished : {0} {1}", process.StartInfo.FileName, process.StartInfo.Arguments); return process.ExitCode; } - return -1; - } - public static string FormatArguments(string arguments, string fullpath) - { - return string.Format(arguments, fullpath); - } - } -} + return -1; + } + + public static string FormatArguments(string arguments, string fullpath) + { + return string.Format(arguments, fullpath); + } + } +} \ No newline at end of file diff --git a/src/Greenshot.Plugin.ExternalCommand/ExternalCommandForm.cs b/src/Greenshot.Plugin.ExternalCommand/ExternalCommandForm.cs index 7167b954b..7fdfffa2a 100644 --- a/src/Greenshot.Plugin.ExternalCommand/ExternalCommandForm.cs +++ b/src/Greenshot.Plugin.ExternalCommand/ExternalCommandForm.cs @@ -21,10 +21,12 @@ using GreenshotPlugin.Controls; -namespace Greenshot.Plugin.ExternalCommand { - /// - /// This class is needed for design-time resolving of the language files - /// - public class ExternalCommandForm : GreenshotForm { - } +namespace Greenshot.Plugin.ExternalCommand +{ + /// + /// This class is needed for design-time resolving of the language files + /// + public class ExternalCommandForm : GreenshotForm + { + } } \ No newline at end of file diff --git a/src/Greenshot.Plugin.ExternalCommand/ExternalCommandPlugin.cs b/src/Greenshot.Plugin.ExternalCommand/ExternalCommandPlugin.cs index eeb99ed41..43ae03f41 100644 --- a/src/Greenshot.Plugin.ExternalCommand/ExternalCommandPlugin.cs +++ b/src/Greenshot.Plugin.ExternalCommand/ExternalCommandPlugin.cs @@ -55,6 +55,7 @@ namespace Greenshot.Plugin.ExternalCommand _itemPlugInRoot.Dispose(); _itemPlugInRoot = null; } + private IEnumerable Destinations() { foreach (string command in ExternalCommandConfig.Commands) @@ -77,17 +78,20 @@ namespace Greenshot.Plugin.ExternalCommand // Fix it ExternalCommandConfig.RunInbackground.Add(command, true); } + if (!ExternalCommandConfig.Argument.ContainsKey(command)) { Log.WarnFormat("Found missing argument for {0}", command); // Fix it ExternalCommandConfig.Argument.Add(command, "{0}"); } + if (!ExternalCommandConfig.Commandline.ContainsKey(command)) { Log.WarnFormat("Found missing commandline for {0}", command); return false; } + string commandline = FilenameHelper.FillVariables(ExternalCommandConfig.Commandline[command], true); commandline = FilenameHelper.FillCmdVariables(commandline, true); @@ -96,8 +100,10 @@ namespace Greenshot.Plugin.ExternalCommand Log.WarnFormat("Found 'invalid' commandline {0} for command {1}", ExternalCommandConfig.Commandline[command], command); return false; } + return true; } + /// /// Implementation of the IGreenshotPlugin.Initialize /// @@ -114,11 +120,13 @@ namespace Greenshot.Plugin.ExternalCommand commandsToDelete.Add(command); } } + // cleanup foreach (string command in commandsToDelete) { ExternalCommandConfig.Delete(command); } + SimpleServiceProvider.Current.AddService(Destinations()); _itemPlugInRoot = new ToolStripMenuItem(); diff --git a/src/Greenshot.Plugin.ExternalCommand/IconCache.cs b/src/Greenshot.Plugin.ExternalCommand/IconCache.cs index 44ea0a84d..f1b854164 100644 --- a/src/Greenshot.Plugin.ExternalCommand/IconCache.cs +++ b/src/Greenshot.Plugin.ExternalCommand/IconCache.cs @@ -25,23 +25,32 @@ using System.IO; using GreenshotPlugin.Core; using GreenshotPlugin.IniFile; -namespace Greenshot.Plugin.ExternalCommand { - public static class IconCache { - private static readonly ExternalCommandConfiguration config = IniConfig.GetIniSection(); - private static readonly log4net.ILog LOG = log4net.LogManager.GetLogger(typeof(IconCache)); +namespace Greenshot.Plugin.ExternalCommand +{ + public static class IconCache + { + private static readonly ExternalCommandConfiguration config = IniConfig.GetIniSection(); + private static readonly log4net.ILog LOG = log4net.LogManager.GetLogger(typeof(IconCache)); - public static Image IconForCommand(string commandName) { - Image icon = null; - if (commandName != null) { - if (config.Commandline.ContainsKey(commandName) && File.Exists(config.Commandline[commandName])) { - try { - icon = PluginUtils.GetCachedExeIcon(config.Commandline[commandName], 0); - } catch (Exception ex) { - LOG.Warn("Problem loading icon for " + config.Commandline[commandName], ex); - } - } - } - return icon; - } - } -} + public static Image IconForCommand(string commandName) + { + Image icon = null; + if (commandName != null) + { + if (config.Commandline.ContainsKey(commandName) && File.Exists(config.Commandline[commandName])) + { + try + { + icon = PluginUtils.GetCachedExeIcon(config.Commandline[commandName], 0); + } + catch (Exception ex) + { + LOG.Warn("Problem loading icon for " + config.Commandline[commandName], ex); + } + } + } + + return icon; + } + } +} \ No newline at end of file diff --git a/src/Greenshot.Plugin.ExternalCommand/SettingsForm.cs b/src/Greenshot.Plugin.ExternalCommand/SettingsForm.cs index c4cf9ca2b..49e872d7f 100644 --- a/src/Greenshot.Plugin.ExternalCommand/SettingsForm.cs +++ b/src/Greenshot.Plugin.ExternalCommand/SettingsForm.cs @@ -24,103 +24,129 @@ using System.Drawing; using System.Windows.Forms; using GreenshotPlugin.IniFile; -namespace Greenshot.Plugin.ExternalCommand { - /// - /// Description of SettingsForm. - /// - public partial class SettingsForm : ExternalCommandForm { - private static readonly ExternalCommandConfiguration ExternalCommandConfig = IniConfig.GetIniSection(); +namespace Greenshot.Plugin.ExternalCommand +{ + /// + /// Description of SettingsForm. + /// + public partial class SettingsForm : ExternalCommandForm + { + private static readonly ExternalCommandConfiguration ExternalCommandConfig = IniConfig.GetIniSection(); - public SettingsForm() { - // - // The InitializeComponent() call is required for Windows Forms designer support. - // - InitializeComponent(); - AcceptButton = buttonOk; - CancelButton = buttonCancel; - UpdateView(); - } + public SettingsForm() + { + // + // The InitializeComponent() call is required for Windows Forms designer support. + // + InitializeComponent(); + AcceptButton = buttonOk; + CancelButton = buttonCancel; + UpdateView(); + } - private void ButtonOkClick(object sender, EventArgs e) { - IniConfig.Save(); - } + private void ButtonOkClick(object sender, EventArgs e) + { + IniConfig.Save(); + } - private void ButtonAddClick(object sender, EventArgs e) { - var form = new SettingsFormDetail(null); - form.ShowDialog(); + private void ButtonAddClick(object sender, EventArgs e) + { + var form = new SettingsFormDetail(null); + form.ShowDialog(); - UpdateView(); - } + UpdateView(); + } - private void ButtonDeleteClick(object sender, EventArgs e) { - foreach(ListViewItem item in listView1.SelectedItems) { - string commando = item.Tag as string; + private void ButtonDeleteClick(object sender, EventArgs e) + { + foreach (ListViewItem item in listView1.SelectedItems) + { + string commando = item.Tag as string; - ExternalCommandConfig.Delete(commando); - } - UpdateView(); - } + ExternalCommandConfig.Delete(commando); + } - private void UpdateView() { - listView1.Items.Clear(); - if(ExternalCommandConfig.Commands != null) { - listView1.ListViewItemSorter = new ListviewComparer(); - ImageList imageList = new ImageList(); - listView1.SmallImageList = imageList; - int imageNr = 0; - foreach(string commando in ExternalCommandConfig.Commands) { - ListViewItem item; - Image iconForExe = IconCache.IconForCommand(commando); - if(iconForExe != null) { - imageList.Images.Add(iconForExe); - item = new ListViewItem(commando, imageNr++); - } else { - item = new ListViewItem(commando); - } - item.Tag = commando; - listView1.Items.Add(item); - } - } - // Fix for bug #1484, getting an ArgumentOutOfRangeException as there is nothing selected but the edit button was still active. - button_edit.Enabled = listView1.SelectedItems.Count > 0; - } + UpdateView(); + } - private void ListView1ItemSelectionChanged(object sender, EventArgs e) { - button_edit.Enabled = listView1.SelectedItems.Count > 0; - } + private void UpdateView() + { + listView1.Items.Clear(); + if (ExternalCommandConfig.Commands != null) + { + listView1.ListViewItemSorter = new ListviewComparer(); + ImageList imageList = new ImageList(); + listView1.SmallImageList = imageList; + int imageNr = 0; + foreach (string commando in ExternalCommandConfig.Commands) + { + ListViewItem item; + Image iconForExe = IconCache.IconForCommand(commando); + if (iconForExe != null) + { + imageList.Images.Add(iconForExe); + item = new ListViewItem(commando, imageNr++); + } + else + { + item = new ListViewItem(commando); + } - private void ButtonEditClick(object sender, EventArgs e) { - ListView1DoubleClick(sender, e); - } + item.Tag = commando; + listView1.Items.Add(item); + } + } - private void ListView1DoubleClick(object sender, EventArgs e) { - // Safety check for bug #1484 - bool selectionActive = listView1.SelectedItems.Count > 0; - if(!selectionActive) { - button_edit.Enabled = false; - return; - } - string commando = listView1.SelectedItems[0].Tag as string; + // Fix for bug #1484, getting an ArgumentOutOfRangeException as there is nothing selected but the edit button was still active. + button_edit.Enabled = listView1.SelectedItems.Count > 0; + } - var form = new SettingsFormDetail(commando); - form.ShowDialog(); + private void ListView1ItemSelectionChanged(object sender, EventArgs e) + { + button_edit.Enabled = listView1.SelectedItems.Count > 0; + } - UpdateView(); - } - } + private void ButtonEditClick(object sender, EventArgs e) + { + ListView1DoubleClick(sender, e); + } - public class ListviewComparer : System.Collections.IComparer { - public int Compare(object x, object y) { - if(!(x is ListViewItem)) { - return (0); - } - if(!(y is ListViewItem)) { - return (0); - } + private void ListView1DoubleClick(object sender, EventArgs e) + { + // Safety check for bug #1484 + bool selectionActive = listView1.SelectedItems.Count > 0; + if (!selectionActive) + { + button_edit.Enabled = false; + return; + } - var l1 = (ListViewItem)x; - var l2 = (ListViewItem)y; - return string.Compare(l1.Text, l2.Text, StringComparison.Ordinal); - } - } -} + string commando = listView1.SelectedItems[0].Tag as string; + + var form = new SettingsFormDetail(commando); + form.ShowDialog(); + + UpdateView(); + } + } + + public class ListviewComparer : System.Collections.IComparer + { + public int Compare(object x, object y) + { + if (!(x is ListViewItem)) + { + return (0); + } + + if (!(y is ListViewItem)) + { + return (0); + } + + var l1 = (ListViewItem) x; + var l2 = (ListViewItem) y; + return string.Compare(l1.Text, l2.Text, StringComparison.Ordinal); + } + } +} \ No newline at end of file diff --git a/src/Greenshot.Plugin.ExternalCommand/SettingsFormDetail.cs b/src/Greenshot.Plugin.ExternalCommand/SettingsFormDetail.cs index 9c353b197..8aa235a67 100644 --- a/src/Greenshot.Plugin.ExternalCommand/SettingsFormDetail.cs +++ b/src/Greenshot.Plugin.ExternalCommand/SettingsFormDetail.cs @@ -26,141 +26,167 @@ using System.Windows.Forms; using GreenshotPlugin.Core; using GreenshotPlugin.IniFile; -namespace Greenshot.Plugin.ExternalCommand { - /// - /// Description of SettingsFormDetail. - /// - public partial class SettingsFormDetail : ExternalCommandForm { - private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(typeof(SettingsFormDetail)); - private static readonly ExternalCommandConfiguration ExternalCommandConfig = IniConfig.GetIniSection(); +namespace Greenshot.Plugin.ExternalCommand +{ + /// + /// Description of SettingsFormDetail. + /// + public partial class SettingsFormDetail : ExternalCommandForm + { + private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(typeof(SettingsFormDetail)); + private static readonly ExternalCommandConfiguration ExternalCommandConfig = IniConfig.GetIniSection(); - private readonly string _commando; - private readonly int _commandIndex; + private readonly string _commando; + private readonly int _commandIndex; - public SettingsFormDetail(string commando) { - InitializeComponent(); - AcceptButton = buttonOk; - CancelButton = buttonCancel; - _commando = commando; + public SettingsFormDetail(string commando) + { + InitializeComponent(); + AcceptButton = buttonOk; + CancelButton = buttonCancel; + _commando = commando; - if(commando != null) { - textBox_name.Text = commando; - textBox_commandline.Text = ExternalCommandConfig.Commandline[commando]; - textBox_arguments.Text = ExternalCommandConfig.Argument[commando]; - _commandIndex = ExternalCommandConfig.Commands.FindIndex(s => s == commando); - } else { - textBox_arguments.Text = "\"{0}\""; - } - OkButtonState(); - } + if (commando != null) + { + textBox_name.Text = commando; + textBox_commandline.Text = ExternalCommandConfig.Commandline[commando]; + textBox_arguments.Text = ExternalCommandConfig.Argument[commando]; + _commandIndex = ExternalCommandConfig.Commands.FindIndex(s => s == commando); + } + else + { + textBox_arguments.Text = "\"{0}\""; + } - private void ButtonOkClick(object sender, EventArgs e) { - string commandName = textBox_name.Text; - string commandLine = textBox_commandline.Text; - string arguments = textBox_arguments.Text; - if(_commando != null) { - ExternalCommandConfig.Commands[_commandIndex] = commandName; - ExternalCommandConfig.Commandline.Remove(_commando); - ExternalCommandConfig.Commandline.Add(commandName, commandLine); - ExternalCommandConfig.Argument.Remove(_commando); - ExternalCommandConfig.Argument.Add(commandName, arguments); - } else { - ExternalCommandConfig.Commands.Add(commandName); - ExternalCommandConfig.Commandline.Add(commandName, commandLine); - ExternalCommandConfig.Argument.Add(commandName, arguments); - } - } + OkButtonState(); + } - private void Button3Click(object sender, EventArgs e) { - var openFileDialog = new OpenFileDialog - { - Filter = "Executables (*.exe, *.bat, *.com)|*.exe; *.bat; *.com|All files (*)|*", - FilterIndex = 1, - CheckFileExists = true, - Multiselect = false - }; - string initialPath = null; - try - { - initialPath = Path.GetDirectoryName(textBox_commandline.Text); - } - catch (Exception ex) - { - Log.WarnFormat("Can't get the initial path via {0}", textBox_commandline.Text); - Log.Warn("Exception: ", ex); - } - if(initialPath != null && Directory.Exists(initialPath)) { - openFileDialog.InitialDirectory = initialPath; - } else { - initialPath = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles); - openFileDialog.InitialDirectory = initialPath; - } - Log.DebugFormat("Starting OpenFileDialog at {0}", initialPath); - if(openFileDialog.ShowDialog() == DialogResult.OK) { - textBox_commandline.Text = openFileDialog.FileName; - } - } + private void ButtonOkClick(object sender, EventArgs e) + { + string commandName = textBox_name.Text; + string commandLine = textBox_commandline.Text; + string arguments = textBox_arguments.Text; + if (_commando != null) + { + ExternalCommandConfig.Commands[_commandIndex] = commandName; + ExternalCommandConfig.Commandline.Remove(_commando); + ExternalCommandConfig.Commandline.Add(commandName, commandLine); + ExternalCommandConfig.Argument.Remove(_commando); + ExternalCommandConfig.Argument.Add(commandName, arguments); + } + else + { + ExternalCommandConfig.Commands.Add(commandName); + ExternalCommandConfig.Commandline.Add(commandName, commandLine); + ExternalCommandConfig.Argument.Add(commandName, arguments); + } + } - private void OkButtonState() { - // Assume OK - buttonOk.Enabled = true; - textBox_name.BackColor = Color.White; - textBox_commandline.BackColor = Color.White; - textBox_arguments.BackColor = Color.White; - // Is there a text in the name field - if(string.IsNullOrEmpty(textBox_name.Text)) { - buttonOk.Enabled = false; - } - // Check if commandname is unique - if(_commando == null && !string.IsNullOrEmpty(textBox_name.Text) && ExternalCommandConfig.Commands.Contains(textBox_name.Text)) { - buttonOk.Enabled = false; - textBox_name.BackColor = Color.Red; - } - // Is there a text in the commandline field - if(string.IsNullOrEmpty(textBox_commandline.Text)) { - buttonOk.Enabled = false; - } + private void Button3Click(object sender, EventArgs e) + { + var openFileDialog = new OpenFileDialog + { + Filter = "Executables (*.exe, *.bat, *.com)|*.exe; *.bat; *.com|All files (*)|*", + FilterIndex = 1, + CheckFileExists = true, + Multiselect = false + }; + string initialPath = null; + try + { + initialPath = Path.GetDirectoryName(textBox_commandline.Text); + } + catch (Exception ex) + { + Log.WarnFormat("Can't get the initial path via {0}", textBox_commandline.Text); + Log.Warn("Exception: ", ex); + } - if (!string.IsNullOrEmpty(textBox_commandline.Text)) - { - // Added this to be more flexible, using the Greenshot var format - string cmdPath = FilenameHelper.FillVariables(textBox_commandline.Text, true); - // And also replace the "DOS" Variables - cmdPath = FilenameHelper.FillCmdVariables(cmdPath, true); - // Is the command available? - if (!File.Exists(cmdPath)) - { - buttonOk.Enabled = false; - textBox_commandline.BackColor = Color.Red; - } - } - // Are the arguments in a valid format? - try - { - string arguments = FilenameHelper.FillVariables(textBox_arguments.Text, false); - arguments = FilenameHelper.FillCmdVariables(arguments, false); + if (initialPath != null && Directory.Exists(initialPath)) + { + openFileDialog.InitialDirectory = initialPath; + } + else + { + initialPath = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles); + openFileDialog.InitialDirectory = initialPath; + } - ExternalCommandDestination.FormatArguments(arguments, string.Empty); - } - catch - { - buttonOk.Enabled = false; - textBox_arguments.BackColor = Color.Red; - } - } + Log.DebugFormat("Starting OpenFileDialog at {0}", initialPath); + if (openFileDialog.ShowDialog() == DialogResult.OK) + { + textBox_commandline.Text = openFileDialog.FileName; + } + } - private void textBox_name_TextChanged(object sender, EventArgs e) { - OkButtonState(); - } + private void OkButtonState() + { + // Assume OK + buttonOk.Enabled = true; + textBox_name.BackColor = Color.White; + textBox_commandline.BackColor = Color.White; + textBox_arguments.BackColor = Color.White; + // Is there a text in the name field + if (string.IsNullOrEmpty(textBox_name.Text)) + { + buttonOk.Enabled = false; + } - private void textBox_commandline_TextChanged(object sender, EventArgs e) { - OkButtonState(); - } + // Check if commandname is unique + if (_commando == null && !string.IsNullOrEmpty(textBox_name.Text) && ExternalCommandConfig.Commands.Contains(textBox_name.Text)) + { + buttonOk.Enabled = false; + textBox_name.BackColor = Color.Red; + } - private void textBox_arguments_TextChanged(object sender, EventArgs e) - { - OkButtonState(); - } + // Is there a text in the commandline field + if (string.IsNullOrEmpty(textBox_commandline.Text)) + { + buttonOk.Enabled = false; + } - } -} + if (!string.IsNullOrEmpty(textBox_commandline.Text)) + { + // Added this to be more flexible, using the Greenshot var format + string cmdPath = FilenameHelper.FillVariables(textBox_commandline.Text, true); + // And also replace the "DOS" Variables + cmdPath = FilenameHelper.FillCmdVariables(cmdPath, true); + // Is the command available? + if (!File.Exists(cmdPath)) + { + buttonOk.Enabled = false; + textBox_commandline.BackColor = Color.Red; + } + } + + // Are the arguments in a valid format? + try + { + string arguments = FilenameHelper.FillVariables(textBox_arguments.Text, false); + arguments = FilenameHelper.FillCmdVariables(arguments, false); + + ExternalCommandDestination.FormatArguments(arguments, string.Empty); + } + catch + { + buttonOk.Enabled = false; + textBox_arguments.BackColor = Color.Red; + } + } + + private void textBox_name_TextChanged(object sender, EventArgs e) + { + OkButtonState(); + } + + private void textBox_commandline_TextChanged(object sender, EventArgs e) + { + OkButtonState(); + } + + private void textBox_arguments_TextChanged(object sender, EventArgs e) + { + OkButtonState(); + } + } +} \ No newline at end of file diff --git a/src/Greenshot.Plugin.Flickr/FlickrConfiguration.cs b/src/Greenshot.Plugin.Flickr/FlickrConfiguration.cs index 600e61a47..68fc72a3e 100644 --- a/src/Greenshot.Plugin.Flickr/FlickrConfiguration.cs +++ b/src/Greenshot.Plugin.Flickr/FlickrConfiguration.cs @@ -24,59 +24,67 @@ using Greenshot.Plugin.Flickr.Forms; using GreenshotPlugin.Core; using GreenshotPlugin.IniFile; -namespace Greenshot.Plugin.Flickr { - public enum SafetyLevel { - Safe = 1, - Moderate = 2, - Restricted = 3 - } - /// - /// Description of FlickrConfiguration. - /// - [IniSection("Flickr", Description = "Greenshot Flickr Plugin configuration")] - public class FlickrConfiguration : IniSection { - [IniProperty("flickrIsPublic", Description = "IsPublic.", DefaultValue = "true")] - public bool IsPublic { get; set; } +namespace Greenshot.Plugin.Flickr +{ + public enum SafetyLevel + { + Safe = 1, + Moderate = 2, + Restricted = 3 + } - [IniProperty("flickrIsFamily", Description = "IsFamily.", DefaultValue = "true")] - public bool IsFamily { get; set; } + /// + /// Description of FlickrConfiguration. + /// + [IniSection("Flickr", Description = "Greenshot Flickr Plugin configuration")] + public class FlickrConfiguration : IniSection + { + [IniProperty("flickrIsPublic", Description = "IsPublic.", DefaultValue = "true")] + public bool IsPublic { get; set; } - [IniProperty("flickrIsFriend", Description = "IsFriend.", DefaultValue = "true")] - public bool IsFriend { get; set; } + [IniProperty("flickrIsFamily", Description = "IsFamily.", DefaultValue = "true")] + public bool IsFamily { get; set; } - [IniProperty("SafetyLevel", Description = "Safety level", DefaultValue = "Safe")] - public SafetyLevel SafetyLevel { get; set; } + [IniProperty("flickrIsFriend", Description = "IsFriend.", DefaultValue = "true")] + public bool IsFriend { get; set; } - [IniProperty("HiddenFromSearch", Description = "Hidden from search", DefaultValue = "false")] - public bool HiddenFromSearch { get; set; } + [IniProperty("SafetyLevel", Description = "Safety level", DefaultValue = "Safe")] + public SafetyLevel SafetyLevel { get; set; } - [IniProperty("UploadFormat", Description="What file type to use for uploading", DefaultValue="png")] - public OutputFormat UploadFormat { get; set; } + [IniProperty("HiddenFromSearch", Description = "Hidden from search", DefaultValue = "false")] + public bool HiddenFromSearch { get; set; } - [IniProperty("UploadJpegQuality", Description="JPEG file save quality in %.", DefaultValue="80")] - public int UploadJpegQuality { get; set; } + [IniProperty("UploadFormat", Description = "What file type to use for uploading", DefaultValue = "png")] + public OutputFormat UploadFormat { get; set; } - [IniProperty("AfterUploadLinkToClipBoard", Description = "After upload send flickr link to clipboard.", DefaultValue = "true")] - public bool AfterUploadLinkToClipBoard { get; set; } + [IniProperty("UploadJpegQuality", Description = "JPEG file save quality in %.", DefaultValue = "80")] + public int UploadJpegQuality { get; set; } - [IniProperty("UsePageLink", Description = "Use pagelink instead of direct link on the clipboard", DefaultValue = "False")] - public bool UsePageLink { get; set; } + [IniProperty("AfterUploadLinkToClipBoard", Description = "After upload send flickr link to clipboard.", DefaultValue = "true")] + public bool AfterUploadLinkToClipBoard { get; set; } - [IniProperty("FlickrToken", Description = "The Flickr token", Encrypted = true, ExcludeIfNull = true)] - public string FlickrToken { get; set; } - [IniProperty("FlickrTokenSecret", Description = "The Flickr token secret", Encrypted = true, ExcludeIfNull = true)] - public string FlickrTokenSecret { get; set; } + [IniProperty("UsePageLink", Description = "Use pagelink instead of direct link on the clipboard", DefaultValue = "False")] + public bool UsePageLink { get; set; } - /// - /// A form for token - /// - /// bool true if OK was pressed, false if cancel - public bool ShowConfigDialog() { - DialogResult result = new SettingsForm().ShowDialog(); - if (result == DialogResult.OK) { - return true; - } - return false; - } - } -} + [IniProperty("FlickrToken", Description = "The Flickr token", Encrypted = true, ExcludeIfNull = true)] + public string FlickrToken { get; set; } + + [IniProperty("FlickrTokenSecret", Description = "The Flickr token secret", Encrypted = true, ExcludeIfNull = true)] + public string FlickrTokenSecret { get; set; } + + /// + /// A form for token + /// + /// bool true if OK was pressed, false if cancel + public bool ShowConfigDialog() + { + DialogResult result = new SettingsForm().ShowDialog(); + if (result == DialogResult.OK) + { + return true; + } + + return false; + } + } +} \ No newline at end of file diff --git a/src/Greenshot.Plugin.Flickr/FlickrDestination.cs b/src/Greenshot.Plugin.Flickr/FlickrDestination.cs index 340cf3c09..ec61f3b62 100644 --- a/src/Greenshot.Plugin.Flickr/FlickrDestination.cs +++ b/src/Greenshot.Plugin.Flickr/FlickrDestination.cs @@ -24,33 +24,42 @@ using System.Drawing; using GreenshotPlugin.Core; using GreenshotPlugin.Interfaces; -namespace Greenshot.Plugin.Flickr { - public class FlickrDestination : AbstractDestination { - private readonly FlickrPlugin _plugin; - public FlickrDestination(FlickrPlugin plugin) { - _plugin = plugin; - } +namespace Greenshot.Plugin.Flickr +{ + public class FlickrDestination : AbstractDestination + { + private readonly FlickrPlugin _plugin; - public override string Designation => "Flickr"; + public FlickrDestination(FlickrPlugin plugin) + { + _plugin = plugin; + } - public override string Description => Language.GetString("flickr", LangKey.upload_menu_item); + public override string Designation => "Flickr"; - public override Image DisplayIcon { - get { - ComponentResourceManager resources = new ComponentResourceManager(typeof(FlickrPlugin)); - return (Image)resources.GetObject("flickr"); - } - } + public override string Description => Language.GetString("flickr", LangKey.upload_menu_item); - public override ExportInformation ExportCapture(bool manuallyInitiated, ISurface surface, ICaptureDetails captureDetails) { - ExportInformation exportInformation = new ExportInformation(Designation, Description); + public override Image DisplayIcon + { + get + { + ComponentResourceManager resources = new ComponentResourceManager(typeof(FlickrPlugin)); + return (Image) resources.GetObject("flickr"); + } + } + + public override ExportInformation ExportCapture(bool manuallyInitiated, ISurface surface, ICaptureDetails captureDetails) + { + ExportInformation exportInformation = new ExportInformation(Designation, Description); bool uploaded = _plugin.Upload(captureDetails, surface, out var uploadUrl); - if (uploaded) { - exportInformation.ExportMade = true; - exportInformation.Uri = uploadUrl; - } - ProcessExport(exportInformation, surface); - return exportInformation; - } - } -} + if (uploaded) + { + exportInformation.ExportMade = true; + exportInformation.Uri = uploadUrl; + } + + ProcessExport(exportInformation, surface); + return exportInformation; + } + } +} \ No newline at end of file diff --git a/src/Greenshot.Plugin.Flickr/FlickrPlugin.cs b/src/Greenshot.Plugin.Flickr/FlickrPlugin.cs index 62f21a509..0ce2afaad 100644 --- a/src/Greenshot.Plugin.Flickr/FlickrPlugin.cs +++ b/src/Greenshot.Plugin.Flickr/FlickrPlugin.cs @@ -33,99 +33,122 @@ using log4net; namespace Greenshot.Plugin.Flickr { - /// - /// This is the Flickr base code - /// + /// + /// This is the Flickr base code + /// [Plugin("Flickr", true)] - public class FlickrPlugin : IGreenshotPlugin { - private static readonly ILog Log = LogManager.GetLogger(typeof(FlickrPlugin)); - private static FlickrConfiguration _config; - private ComponentResourceManager _resources; - private ToolStripMenuItem _itemPlugInConfig; + public class FlickrPlugin : IGreenshotPlugin + { + private static readonly ILog Log = LogManager.GetLogger(typeof(FlickrPlugin)); + private static FlickrConfiguration _config; + private ComponentResourceManager _resources; + private ToolStripMenuItem _itemPlugInConfig; - public void Dispose() { - Dispose(true); - GC.SuppressFinalize(this); - } + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } - protected void Dispose(bool disposing) { - if (!disposing) { - return; - } - if (_itemPlugInConfig == null) { - return; - } - _itemPlugInConfig.Dispose(); - _itemPlugInConfig = null; - } + protected void Dispose(bool disposing) + { + if (!disposing) + { + return; + } - /// - /// Implementation of the IGreenshotPlugin.Initialize - /// - public bool Initialize() { - // Register configuration (don't need the configuration itself) - _config = IniConfig.GetIniSection(); - _resources = new ComponentResourceManager(typeof(FlickrPlugin)); + if (_itemPlugInConfig == null) + { + return; + } - _itemPlugInConfig = new ToolStripMenuItem - { - Text = Language.GetString("flickr", LangKey.Configure), - Image = (Image) _resources.GetObject("flickr") - }; - _itemPlugInConfig.Click += ConfigMenuClick; + _itemPlugInConfig.Dispose(); + _itemPlugInConfig = null; + } + + /// + /// Implementation of the IGreenshotPlugin.Initialize + /// + public bool Initialize() + { + // Register configuration (don't need the configuration itself) + _config = IniConfig.GetIniSection(); + _resources = new ComponentResourceManager(typeof(FlickrPlugin)); + + _itemPlugInConfig = new ToolStripMenuItem + { + Text = Language.GetString("flickr", LangKey.Configure), + Image = (Image) _resources.GetObject("flickr") + }; + _itemPlugInConfig.Click += ConfigMenuClick; SimpleServiceProvider.Current.AddService(new FlickrDestination(this)); - PluginUtils.AddToContextMenu(_itemPlugInConfig); - Language.LanguageChanged += OnLanguageChanged; - return true; - } + PluginUtils.AddToContextMenu(_itemPlugInConfig); + Language.LanguageChanged += OnLanguageChanged; + return true; + } - public void OnLanguageChanged(object sender, EventArgs e) { - if (_itemPlugInConfig != null) { - _itemPlugInConfig.Text = Language.GetString("flickr", LangKey.Configure); - } - } + public void OnLanguageChanged(object sender, EventArgs e) + { + if (_itemPlugInConfig != null) + { + _itemPlugInConfig.Text = Language.GetString("flickr", LangKey.Configure); + } + } - public void Shutdown() { - Log.Debug("Flickr Plugin shutdown."); - } + public void Shutdown() + { + Log.Debug("Flickr Plugin shutdown."); + } - /// - /// Implementation of the IPlugin.Configure - /// - public void Configure() { - _config.ShowConfigDialog(); - } + /// + /// Implementation of the IPlugin.Configure + /// + public void Configure() + { + _config.ShowConfigDialog(); + } - public void ConfigMenuClick(object sender, EventArgs eventArgs) { - _config.ShowConfigDialog(); - } + public void ConfigMenuClick(object sender, EventArgs eventArgs) + { + _config.ShowConfigDialog(); + } - public bool Upload(ICaptureDetails captureDetails, ISurface surface, out string uploadUrl) { - SurfaceOutputSettings outputSettings = new SurfaceOutputSettings(_config.UploadFormat, _config.UploadJpegQuality, false); - uploadUrl = null; - try { - string flickrUrl = null; - new PleaseWaitForm().ShowAndWait("Flickr", Language.GetString("flickr", LangKey.communication_wait), - delegate { - string filename = Path.GetFileName(FilenameHelper.GetFilename(_config.UploadFormat, captureDetails)); - flickrUrl = FlickrUtils.UploadToFlickr(surface, outputSettings, captureDetails.Title, filename); - } - ); - - if (flickrUrl == null) { - return false; - } - uploadUrl = flickrUrl; + public bool Upload(ICaptureDetails captureDetails, ISurface surface, out string uploadUrl) + { + SurfaceOutputSettings outputSettings = new SurfaceOutputSettings(_config.UploadFormat, _config.UploadJpegQuality, false); + uploadUrl = null; + try + { + string flickrUrl = null; + new PleaseWaitForm().ShowAndWait("Flickr", Language.GetString("flickr", LangKey.communication_wait), + delegate + { + string filename = Path.GetFileName(FilenameHelper.GetFilename(_config.UploadFormat, captureDetails)); + flickrUrl = FlickrUtils.UploadToFlickr(surface, outputSettings, captureDetails.Title, filename); + } + ); - if (_config.AfterUploadLinkToClipBoard) { - ClipboardHelper.SetClipboardData(flickrUrl); - } - return true; - } catch (Exception e) { - Log.Error("Error uploading.", e); - MessageBox.Show(Language.GetString("flickr", LangKey.upload_failure) + " " + e.Message); - } - return false; - } - } -} + if (flickrUrl == null) + { + return false; + } + + uploadUrl = flickrUrl; + + if (_config.AfterUploadLinkToClipBoard) + { + ClipboardHelper.SetClipboardData(flickrUrl); + } + + return true; + } + catch (Exception e) + { + Log.Error("Error uploading.", e); + MessageBox.Show(Language.GetString("flickr", LangKey.upload_failure) + " " + e.Message); + } + + return false; + } + } +} \ No newline at end of file diff --git a/src/Greenshot.Plugin.Flickr/FlickrUtils.cs b/src/Greenshot.Plugin.Flickr/FlickrUtils.cs index c6978a17d..582484433 100644 --- a/src/Greenshot.Plugin.Flickr/FlickrUtils.cs +++ b/src/Greenshot.Plugin.Flickr/FlickrUtils.cs @@ -30,140 +30,203 @@ using GreenshotPlugin.Interfaces; using GreenshotPlugin.Interfaces.Plugin; using log4net; -namespace Greenshot.Plugin.Flickr { - /// - /// Description of FlickrUtils. - /// - public static class FlickrUtils { - private static readonly ILog LOG = LogManager.GetLogger(typeof(FlickrUtils)); - private static readonly FlickrConfiguration config = IniConfig.GetIniSection(); - private const string FLICKR_API_BASE_URL = "https://api.flickr.com/services/"; - private const string FLICKR_UPLOAD_URL = FLICKR_API_BASE_URL + "upload/"; - // OAUTH - private const string FLICKR_OAUTH_BASE_URL = FLICKR_API_BASE_URL + "oauth/"; - 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}_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"; +namespace Greenshot.Plugin.Flickr +{ + /// + /// Description of FlickrUtils. + /// + public static class FlickrUtils + { + private static readonly ILog LOG = LogManager.GetLogger(typeof(FlickrUtils)); + private static readonly FlickrConfiguration config = IniConfig.GetIniSection(); + private const string FLICKR_API_BASE_URL = "https://api.flickr.com/services/"; - /// - /// Do the actual upload to Flickr - /// For more details on the available parameters, see: http://flickrnet.codeplex.com - /// - /// - /// - /// - /// - /// url to image - public static string UploadToFlickr(ISurface surfaceToUpload, SurfaceOutputSettings outputSettings, string title, string filename) { - var oAuth = new OAuthSession(FlickrCredentials.ConsumerKey, FlickrCredentials.ConsumerSecret) - { - BrowserSize = new Size(520, 800), - CheckVerifier = false, - AccessTokenUrl = FLICKR_ACCESS_TOKEN_URL, - AuthorizeUrl = FLICKR_AUTHORIZE_URL, - RequestTokenUrl = FLICKR_REQUEST_TOKEN_URL, - LoginTitle = "Flickr authorization", - Token = config.FlickrToken, - TokenSecret = config.FlickrTokenSecret - }; - if (string.IsNullOrEmpty(oAuth.Token)) { - if (!oAuth.Authorize()) { - return null; - } - if (!string.IsNullOrEmpty(oAuth.Token)) { - config.FlickrToken = oAuth.Token; - } - if (!string.IsNullOrEmpty(oAuth.TokenSecret)) { - config.FlickrTokenSecret = oAuth.TokenSecret; - } - IniConfig.Save(); - } - try { - IDictionary signedParameters = new Dictionary - { - { "content_type", "2" }, // Screenshot - { "tags", "Greenshot" }, - { "is_public", config.IsPublic ? "1" : "0" }, - { "is_friend", config.IsFriend ? "1" : "0" }, - { "is_family", config.IsFamily ? "1" : "0" }, - { "safety_level", $"{(int)config.SafetyLevel}" }, - { "hidden", config.HiddenFromSearch ? "1" : "2" } - }; - IDictionary otherParameters = new Dictionary - { - { "photo", new SurfaceContainer(surfaceToUpload, outputSettings, filename) } - }; - string response = oAuth.MakeOAuthRequest(HTTPMethod.POST, FLICKR_UPLOAD_URL, signedParameters, otherParameters, null); - string photoId = GetPhotoId(response); + private const string FLICKR_UPLOAD_URL = FLICKR_API_BASE_URL + "upload/"; - // Get Photo Info - signedParameters = new Dictionary { { "photo_id", photoId } }; - string photoInfo = oAuth.MakeOAuthRequest(HTTPMethod.POST, FLICKR_GET_INFO_URL, signedParameters, null, null); - return GetUrl(photoInfo); - } catch (Exception ex) { - LOG.Error("Upload error: ", ex); - throw; - } finally { - if (!string.IsNullOrEmpty(oAuth.Token)) { - config.FlickrToken = oAuth.Token; - } - if (!string.IsNullOrEmpty(oAuth.TokenSecret)) { - config.FlickrTokenSecret = oAuth.TokenSecret; - } - } - } + // OAUTH + private const string FLICKR_OAUTH_BASE_URL = FLICKR_API_BASE_URL + "oauth/"; + 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 static string GetUrl(string response) { - try { - XmlDocument doc = new XmlDocument(); - doc.LoadXml(response); - if (config.UsePageLink) { - XmlNodeList nodes = doc.GetElementsByTagName("url"); - if (nodes.Count > 0) { - var xmlNode = nodes.Item(0); - if (xmlNode != null) { - return xmlNode.InnerText; - } - } - } else { - XmlNodeList nodes = doc.GetElementsByTagName("photo"); - if (nodes.Count > 0) { - var item = nodes.Item(0); - if (item?.Attributes != null) { - string farmId = item.Attributes["farm"].Value; - string serverId = item.Attributes["server"].Value; - string photoId = item.Attributes["id"].Value; - string originalsecret = item.Attributes["originalsecret"].Value; - string originalFormat = item.Attributes["originalformat"].Value; - return string.Format(FLICKR_FARM_URL, farmId, serverId, photoId, originalsecret, originalFormat); - } - } - } - } catch (Exception ex) { - LOG.Error("Error parsing Flickr Response.", ex); - } - return null; - } + private const string FLICKR_FARM_URL = "https://farm{0}.staticflickr.com/{1}/{2}_{3}_o.{4}"; - private static string GetPhotoId(string response) { - try { - XmlDocument doc = new XmlDocument(); - doc.LoadXml(response); - XmlNodeList nodes = doc.GetElementsByTagName("photoid"); - if (nodes.Count > 0) { - var xmlNode = nodes.Item(0); - if (xmlNode != null) { - return xmlNode.InnerText; - } - } - } catch (Exception ex) { - LOG.Error("Error parsing Flickr Response.", ex); - } - return null; - } - } -} + // 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"; + + /// + /// Do the actual upload to Flickr + /// For more details on the available parameters, see: http://flickrnet.codeplex.com + /// + /// + /// + /// + /// + /// url to image + public static string UploadToFlickr(ISurface surfaceToUpload, SurfaceOutputSettings outputSettings, string title, string filename) + { + var oAuth = new OAuthSession(FlickrCredentials.ConsumerKey, FlickrCredentials.ConsumerSecret) + { + BrowserSize = new Size(520, 800), + CheckVerifier = false, + AccessTokenUrl = FLICKR_ACCESS_TOKEN_URL, + AuthorizeUrl = FLICKR_AUTHORIZE_URL, + RequestTokenUrl = FLICKR_REQUEST_TOKEN_URL, + LoginTitle = "Flickr authorization", + Token = config.FlickrToken, + TokenSecret = config.FlickrTokenSecret + }; + if (string.IsNullOrEmpty(oAuth.Token)) + { + if (!oAuth.Authorize()) + { + return null; + } + + if (!string.IsNullOrEmpty(oAuth.Token)) + { + config.FlickrToken = oAuth.Token; + } + + if (!string.IsNullOrEmpty(oAuth.TokenSecret)) + { + config.FlickrTokenSecret = oAuth.TokenSecret; + } + + IniConfig.Save(); + } + + try + { + IDictionary signedParameters = new Dictionary + { + { + "content_type", "2" + }, // Screenshot + { + "tags", "Greenshot" + }, + { + "is_public", config.IsPublic ? "1" : "0" + }, + { + "is_friend", config.IsFriend ? "1" : "0" + }, + { + "is_family", config.IsFamily ? "1" : "0" + }, + { + "safety_level", $"{(int) config.SafetyLevel}" + }, + { + "hidden", config.HiddenFromSearch ? "1" : "2" + } + }; + IDictionary otherParameters = new Dictionary + { + { + "photo", new SurfaceContainer(surfaceToUpload, outputSettings, filename) + } + }; + string response = oAuth.MakeOAuthRequest(HTTPMethod.POST, FLICKR_UPLOAD_URL, signedParameters, otherParameters, null); + string photoId = GetPhotoId(response); + + // Get Photo Info + signedParameters = new Dictionary + { + { + "photo_id", photoId + } + }; + string photoInfo = oAuth.MakeOAuthRequest(HTTPMethod.POST, FLICKR_GET_INFO_URL, signedParameters, null, null); + return GetUrl(photoInfo); + } + catch (Exception ex) + { + LOG.Error("Upload error: ", ex); + throw; + } + finally + { + if (!string.IsNullOrEmpty(oAuth.Token)) + { + config.FlickrToken = oAuth.Token; + } + + if (!string.IsNullOrEmpty(oAuth.TokenSecret)) + { + config.FlickrTokenSecret = oAuth.TokenSecret; + } + } + } + + private static string GetUrl(string response) + { + try + { + XmlDocument doc = new XmlDocument(); + doc.LoadXml(response); + if (config.UsePageLink) + { + XmlNodeList nodes = doc.GetElementsByTagName("url"); + if (nodes.Count > 0) + { + var xmlNode = nodes.Item(0); + if (xmlNode != null) + { + return xmlNode.InnerText; + } + } + } + else + { + XmlNodeList nodes = doc.GetElementsByTagName("photo"); + if (nodes.Count > 0) + { + var item = nodes.Item(0); + if (item?.Attributes != null) + { + string farmId = item.Attributes["farm"].Value; + string serverId = item.Attributes["server"].Value; + string photoId = item.Attributes["id"].Value; + string originalsecret = item.Attributes["originalsecret"].Value; + string originalFormat = item.Attributes["originalformat"].Value; + return string.Format(FLICKR_FARM_URL, farmId, serverId, photoId, originalsecret, originalFormat); + } + } + } + } + catch (Exception ex) + { + LOG.Error("Error parsing Flickr Response.", ex); + } + + return null; + } + + private static string GetPhotoId(string response) + { + try + { + XmlDocument doc = new XmlDocument(); + doc.LoadXml(response); + XmlNodeList nodes = doc.GetElementsByTagName("photoid"); + if (nodes.Count > 0) + { + var xmlNode = nodes.Item(0); + if (xmlNode != null) + { + return xmlNode.InnerText; + } + } + } + catch (Exception ex) + { + LOG.Error("Error parsing Flickr Response.", ex); + } + + return null; + } + } +} \ No newline at end of file diff --git a/src/Greenshot.Plugin.Flickr/Forms/FlickrForm.cs b/src/Greenshot.Plugin.Flickr/Forms/FlickrForm.cs index 74e032708..0c8f4130e 100644 --- a/src/Greenshot.Plugin.Flickr/Forms/FlickrForm.cs +++ b/src/Greenshot.Plugin.Flickr/Forms/FlickrForm.cs @@ -21,7 +21,9 @@ using GreenshotPlugin.Controls; -namespace Greenshot.Plugin.Flickr.Forms { - public class FlickrForm : GreenshotForm { - } +namespace Greenshot.Plugin.Flickr.Forms +{ + public class FlickrForm : GreenshotForm + { + } } \ No newline at end of file diff --git a/src/Greenshot.Plugin.Flickr/Forms/SettingsForm.cs b/src/Greenshot.Plugin.Flickr/Forms/SettingsForm.cs index eba638160..332f28664 100644 --- a/src/Greenshot.Plugin.Flickr/Forms/SettingsForm.cs +++ b/src/Greenshot.Plugin.Flickr/Forms/SettingsForm.cs @@ -19,19 +19,21 @@ * along with this program. If not, see . */ -namespace Greenshot.Plugin.Flickr.Forms { - /// - /// Description of PasswordRequestForm. - /// - public partial class SettingsForm : FlickrForm { - public SettingsForm() { - // - // The InitializeComponent() call is required for Windows Forms designer support. - // - InitializeComponent(); - CancelButton = buttonCancel; - AcceptButton = buttonOK; - } - - } -} +namespace Greenshot.Plugin.Flickr.Forms +{ + /// + /// Description of PasswordRequestForm. + /// + public partial class SettingsForm : FlickrForm + { + public SettingsForm() + { + // + // The InitializeComponent() call is required for Windows Forms designer support. + // + InitializeComponent(); + CancelButton = buttonCancel; + AcceptButton = buttonOK; + } + } +} \ No newline at end of file diff --git a/src/Greenshot.Plugin.Flickr/LanguageKeys.cs b/src/Greenshot.Plugin.Flickr/LanguageKeys.cs index d347faa0a..da634aabe 100644 --- a/src/Greenshot.Plugin.Flickr/LanguageKeys.cs +++ b/src/Greenshot.Plugin.Flickr/LanguageKeys.cs @@ -18,11 +18,14 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -namespace Greenshot.Plugin.Flickr { - public enum LangKey { - upload_menu_item, - upload_failure, - communication_wait, - Configure + +namespace Greenshot.Plugin.Flickr +{ + public enum LangKey + { + upload_menu_item, + upload_failure, + communication_wait, + Configure } -} +} \ No newline at end of file diff --git a/src/Greenshot.Plugin.Flickr/Properties/AssemblyInfo.cs b/src/Greenshot.Plugin.Flickr/Properties/AssemblyInfo.cs index 7cc8b4a3a..84938a6ab 100644 --- a/src/Greenshot.Plugin.Flickr/Properties/AssemblyInfo.cs +++ b/src/Greenshot.Plugin.Flickr/Properties/AssemblyInfo.cs @@ -29,4 +29,4 @@ using System.Runtime.InteropServices; // This sets the default COM visibility of types in the assembly to invisible. // If you need to expose a type to COM, use [ComVisible(true)] on that type. -[assembly: ComVisible(false)] +[assembly: ComVisible(false)] \ No newline at end of file diff --git a/src/Greenshot.Plugin.GooglePhotos/Forms/GooglePhotosForm.cs b/src/Greenshot.Plugin.GooglePhotos/Forms/GooglePhotosForm.cs index 1b3c76816..616398591 100644 --- a/src/Greenshot.Plugin.GooglePhotos/Forms/GooglePhotosForm.cs +++ b/src/Greenshot.Plugin.GooglePhotos/Forms/GooglePhotosForm.cs @@ -20,7 +20,9 @@ using GreenshotPlugin.Controls; -namespace Greenshot.Plugin.GooglePhotos.Forms { - public class GooglePhotosForm : GreenshotForm { - } -} +namespace Greenshot.Plugin.GooglePhotos.Forms +{ + public class GooglePhotosForm : GreenshotForm + { + } +} \ No newline at end of file diff --git a/src/Greenshot.Plugin.GooglePhotos/Forms/SettingsForm.cs b/src/Greenshot.Plugin.GooglePhotos/Forms/SettingsForm.cs index 3946baf1b..6029212f9 100644 --- a/src/Greenshot.Plugin.GooglePhotos/Forms/SettingsForm.cs +++ b/src/Greenshot.Plugin.GooglePhotos/Forms/SettingsForm.cs @@ -18,21 +18,21 @@ * along with this program. If not, see . */ -namespace Greenshot.Plugin.GooglePhotos.Forms { - /// - /// Description of PasswordRequestForm. - /// - public partial class SettingsForm : GooglePhotosForm { - - public SettingsForm() - { - // - // The InitializeComponent() call is required for Windows Forms designer support. - // - InitializeComponent(); - CancelButton = buttonCancel; - AcceptButton = buttonOK; - } - - } -} +namespace Greenshot.Plugin.GooglePhotos.Forms +{ + /// + /// Description of PasswordRequestForm. + /// + public partial class SettingsForm : GooglePhotosForm + { + public SettingsForm() + { + // + // The InitializeComponent() call is required for Windows Forms designer support. + // + InitializeComponent(); + CancelButton = buttonCancel; + AcceptButton = buttonOK; + } + } +} \ No newline at end of file diff --git a/src/Greenshot.Plugin.GooglePhotos/GooglePhotosConfiguration.cs b/src/Greenshot.Plugin.GooglePhotos/GooglePhotosConfiguration.cs index d11d797d1..411cf6ba9 100644 --- a/src/Greenshot.Plugin.GooglePhotos/GooglePhotosConfiguration.cs +++ b/src/Greenshot.Plugin.GooglePhotos/GooglePhotosConfiguration.cs @@ -24,71 +24,58 @@ using Greenshot.Plugin.GooglePhotos.Forms; using GreenshotPlugin.Core; using GreenshotPlugin.IniFile; -namespace Greenshot.Plugin.GooglePhotos { - /// - /// Description of GooglePhotosConfiguration. - /// - [IniSection("GooglePhotos", Description = "Greenshot GooglePhotos Plugin configuration")] - public class GooglePhotosConfiguration : IniSection { - [IniProperty("UploadFormat", Description="What file type to use for uploading", DefaultValue="png")] - public OutputFormat UploadFormat { get; set; } +namespace Greenshot.Plugin.GooglePhotos +{ + /// + /// Description of GooglePhotosConfiguration. + /// + [IniSection("GooglePhotos", Description = "Greenshot GooglePhotos Plugin configuration")] + public class GooglePhotosConfiguration : IniSection + { + [IniProperty("UploadFormat", Description = "What file type to use for uploading", DefaultValue = "png")] + public OutputFormat UploadFormat { get; set; } - [IniProperty("UploadJpegQuality", Description="JPEG file save quality in %.", DefaultValue="80")] - public int UploadJpegQuality { get; set; } + [IniProperty("UploadJpegQuality", Description = "JPEG file save quality in %.", DefaultValue = "80")] + public int UploadJpegQuality { get; set; } - [IniProperty("AfterUploadLinkToClipBoard", Description = "After upload send GooglePhotos link to clipboard.", DefaultValue = "true")] - public bool AfterUploadLinkToClipBoard { get; set; } - [IniProperty("AddFilename", Description = "Is the filename passed on to GooglePhotos", DefaultValue = "False")] - public bool AddFilename { - get; - set; - } + [IniProperty("AfterUploadLinkToClipBoard", Description = "After upload send GooglePhotos link to clipboard.", DefaultValue = "true")] + public bool AfterUploadLinkToClipBoard { get; set; } - [IniProperty("UploadUser", Description = "The GooglePhotos user to upload to", DefaultValue = "default")] - public string UploadUser { - get; - set; - } + [IniProperty("AddFilename", Description = "Is the filename passed on to GooglePhotos", DefaultValue = "False")] + public bool AddFilename { get; set; } - [IniProperty("UploadAlbum", Description = "The GooglePhotos album to upload to", DefaultValue = "default")] - public string UploadAlbum { - get; - set; - } + [IniProperty("UploadUser", Description = "The GooglePhotos user to upload to", DefaultValue = "default")] + public string UploadUser { get; set; } - [IniProperty("RefreshToken", Description = "GooglePhotos authorization refresh Token", Encrypted = true)] - public string RefreshToken { - get; - set; - } + [IniProperty("UploadAlbum", Description = "The GooglePhotos album to upload to", DefaultValue = "default")] + public string UploadAlbum { get; set; } - /// - /// Not stored - /// - public string AccessToken { - get; - set; - } + [IniProperty("RefreshToken", Description = "GooglePhotos authorization refresh Token", Encrypted = true)] + public string RefreshToken { get; set; } - /// - /// Not stored - /// - public DateTimeOffset AccessTokenExpires { - get; - set; - } + /// + /// Not stored + /// + public string AccessToken { get; set; } - /// - /// A form for token - /// - /// bool true if OK was pressed, false if cancel - public bool ShowConfigDialog() { - DialogResult result = new SettingsForm().ShowDialog(); - if (result == DialogResult.OK) { - return true; - } - return false; - } + /// + /// Not stored + /// + public DateTimeOffset AccessTokenExpires { get; set; } - } -} + /// + /// A form for token + /// + /// bool true if OK was pressed, false if cancel + public bool ShowConfigDialog() + { + DialogResult result = new SettingsForm().ShowDialog(); + if (result == DialogResult.OK) + { + return true; + } + + return false; + } + } +} \ No newline at end of file diff --git a/src/Greenshot.Plugin.GooglePhotos/GooglePhotosDestination.cs b/src/Greenshot.Plugin.GooglePhotos/GooglePhotosDestination.cs index 2948773ce..8a66faef7 100644 --- a/src/Greenshot.Plugin.GooglePhotos/GooglePhotosDestination.cs +++ b/src/Greenshot.Plugin.GooglePhotos/GooglePhotosDestination.cs @@ -23,33 +23,42 @@ using System.Drawing; using GreenshotPlugin.Core; using GreenshotPlugin.Interfaces; -namespace Greenshot.Plugin.GooglePhotos { - public class GooglePhotosDestination : AbstractDestination { - private readonly GooglePhotosPlugin _plugin; - public GooglePhotosDestination(GooglePhotosPlugin plugin) { - _plugin = plugin; - } - - public override string Designation => "GooglePhotos"; +namespace Greenshot.Plugin.GooglePhotos +{ + public class GooglePhotosDestination : AbstractDestination + { + private readonly GooglePhotosPlugin _plugin; - public override string Description => Language.GetString("googlephotos", LangKey.upload_menu_item); + public GooglePhotosDestination(GooglePhotosPlugin plugin) + { + _plugin = plugin; + } - public override Image DisplayIcon { - get { - ComponentResourceManager resources = new ComponentResourceManager(typeof(GooglePhotosPlugin)); - return (Image)resources.GetObject("GooglePhotos"); - } - } + public override string Designation => "GooglePhotos"; - public override ExportInformation ExportCapture(bool manuallyInitiated, ISurface surface, ICaptureDetails captureDetails) { - ExportInformation exportInformation = new ExportInformation(Designation, Description); + public override string Description => Language.GetString("googlephotos", LangKey.upload_menu_item); + + public override Image DisplayIcon + { + get + { + ComponentResourceManager resources = new ComponentResourceManager(typeof(GooglePhotosPlugin)); + return (Image) resources.GetObject("GooglePhotos"); + } + } + + public override ExportInformation ExportCapture(bool manuallyInitiated, ISurface surface, ICaptureDetails captureDetails) + { + ExportInformation exportInformation = new ExportInformation(Designation, Description); bool uploaded = _plugin.Upload(captureDetails, surface, out var uploadUrl); - if (uploaded) { - exportInformation.ExportMade = true; - exportInformation.Uri = uploadUrl; - } - ProcessExport(exportInformation, surface); - return exportInformation; - } - } -} + if (uploaded) + { + exportInformation.ExportMade = true; + exportInformation.Uri = uploadUrl; + } + + ProcessExport(exportInformation, surface); + return exportInformation; + } + } +} \ No newline at end of file diff --git a/src/Greenshot.Plugin.GooglePhotos/GooglePhotosPlugin.cs b/src/Greenshot.Plugin.GooglePhotos/GooglePhotosPlugin.cs index ed1727c05..91978b8ef 100644 --- a/src/Greenshot.Plugin.GooglePhotos/GooglePhotosPlugin.cs +++ b/src/Greenshot.Plugin.GooglePhotos/GooglePhotosPlugin.cs @@ -29,97 +29,113 @@ using GreenshotPlugin.IniFile; using GreenshotPlugin.Interfaces; using GreenshotPlugin.Interfaces.Plugin; -namespace Greenshot.Plugin.GooglePhotos { - /// - /// This is the GooglePhotos base code - /// - [Plugin("GooglePhotos", true)] - public class GooglePhotosPlugin : IGreenshotPlugin { - private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(typeof(GooglePhotosPlugin)); - private static GooglePhotosConfiguration _config; - private ComponentResourceManager _resources; - private ToolStripMenuItem _itemPlugInRoot; +namespace Greenshot.Plugin.GooglePhotos +{ + /// + /// This is the GooglePhotos base code + /// + [Plugin("GooglePhotos", true)] + public class GooglePhotosPlugin : IGreenshotPlugin + { + private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(typeof(GooglePhotosPlugin)); + private static GooglePhotosConfiguration _config; + private ComponentResourceManager _resources; + private ToolStripMenuItem _itemPlugInRoot; - public void Dispose() { - Dispose(true); - GC.SuppressFinalize(this); - } + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } - private void Dispose(bool disposing) - { - if (!disposing) return; - if (_itemPlugInRoot == null) return; - _itemPlugInRoot.Dispose(); - _itemPlugInRoot = null; - } + private void Dispose(bool disposing) + { + if (!disposing) return; + if (_itemPlugInRoot == null) return; + _itemPlugInRoot.Dispose(); + _itemPlugInRoot = null; + } - /// - /// Implementation of the IGreenshotPlugin.Initialize - /// - public bool Initialize() { - SimpleServiceProvider.Current.AddService(new GooglePhotosDestination(this)); + /// + /// Implementation of the IGreenshotPlugin.Initialize + /// + public bool Initialize() + { + SimpleServiceProvider.Current.AddService(new GooglePhotosDestination(this)); - // Get configuration - _config = IniConfig.GetIniSection(); - _resources = new ComponentResourceManager(typeof(GooglePhotosPlugin)); + // Get configuration + _config = IniConfig.GetIniSection(); + _resources = new ComponentResourceManager(typeof(GooglePhotosPlugin)); - _itemPlugInRoot = new ToolStripMenuItem - { - Text = Language.GetString("googlephotos", LangKey.Configure), - Image = (Image) _resources.GetObject("GooglePhotos") - }; - _itemPlugInRoot.Click += ConfigMenuClick; - PluginUtils.AddToContextMenu(_itemPlugInRoot); - Language.LanguageChanged += OnLanguageChanged; - return true; - } + _itemPlugInRoot = new ToolStripMenuItem + { + Text = Language.GetString("googlephotos", LangKey.Configure), + Image = (Image) _resources.GetObject("GooglePhotos") + }; + _itemPlugInRoot.Click += ConfigMenuClick; + PluginUtils.AddToContextMenu(_itemPlugInRoot); + Language.LanguageChanged += OnLanguageChanged; + return true; + } - public void OnLanguageChanged(object sender, EventArgs e) { - if (_itemPlugInRoot != null) { - _itemPlugInRoot.Text = Language.GetString("googlephotos", LangKey.Configure); - } - } + public void OnLanguageChanged(object sender, EventArgs e) + { + if (_itemPlugInRoot != null) + { + _itemPlugInRoot.Text = Language.GetString("googlephotos", LangKey.Configure); + } + } - public void Shutdown() { - Log.Debug("GooglePhotos Plugin shutdown."); - Language.LanguageChanged -= OnLanguageChanged; - //host.OnImageEditorOpen -= new OnImageEditorOpenHandler(ImageEditorOpened); - } + public void Shutdown() + { + Log.Debug("GooglePhotos Plugin shutdown."); + Language.LanguageChanged -= OnLanguageChanged; + //host.OnImageEditorOpen -= new OnImageEditorOpenHandler(ImageEditorOpened); + } - /// - /// Implementation of the IPlugin.Configure - /// - public void Configure() { - _config.ShowConfigDialog(); - } + /// + /// Implementation of the IPlugin.Configure + /// + public void Configure() + { + _config.ShowConfigDialog(); + } - public void ConfigMenuClick(object sender, EventArgs eventArgs) { - Configure(); - } + public void ConfigMenuClick(object sender, EventArgs eventArgs) + { + Configure(); + } - public bool Upload(ICaptureDetails captureDetails, ISurface surfaceToUpload, out string uploadUrl) { - SurfaceOutputSettings outputSettings = new SurfaceOutputSettings(_config.UploadFormat, _config.UploadJpegQuality); - try { - string url = null; - new PleaseWaitForm().ShowAndWait("GooglePhotos", Language.GetString("googlephotos", LangKey.communication_wait), - delegate - { - string filename = Path.GetFileName(FilenameHelper.GetFilename(_config.UploadFormat, captureDetails)); - url = GooglePhotosUtils.UploadToGooglePhotos(surfaceToUpload, outputSettings, captureDetails.Title, filename); - } - ); - uploadUrl = url; + public bool Upload(ICaptureDetails captureDetails, ISurface surfaceToUpload, out string uploadUrl) + { + SurfaceOutputSettings outputSettings = new SurfaceOutputSettings(_config.UploadFormat, _config.UploadJpegQuality); + try + { + string url = null; + new PleaseWaitForm().ShowAndWait("GooglePhotos", Language.GetString("googlephotos", LangKey.communication_wait), + delegate + { + string filename = Path.GetFileName(FilenameHelper.GetFilename(_config.UploadFormat, captureDetails)); + url = GooglePhotosUtils.UploadToGooglePhotos(surfaceToUpload, outputSettings, captureDetails.Title, filename); + } + ); + uploadUrl = url; - if (uploadUrl != null && _config.AfterUploadLinkToClipBoard) { - ClipboardHelper.SetClipboardData(uploadUrl); - } - return true; - } catch (Exception e) { - Log.Error("Error uploading.", e); - MessageBox.Show(Language.GetString("googlephotos", LangKey.upload_failure) + " " + e.Message); - } - uploadUrl = null; - return false; - } - } -} + if (uploadUrl != null && _config.AfterUploadLinkToClipBoard) + { + ClipboardHelper.SetClipboardData(uploadUrl); + } + + return true; + } + catch (Exception e) + { + Log.Error("Error uploading.", e); + MessageBox.Show(Language.GetString("googlephotos", LangKey.upload_failure) + " " + e.Message); + } + + uploadUrl = null; + return false; + } + } +} \ No newline at end of file diff --git a/src/Greenshot.Plugin.GooglePhotos/GooglePhotosUtils.cs b/src/Greenshot.Plugin.GooglePhotos/GooglePhotosUtils.cs index a11edf090..2b4638839 100644 --- a/src/Greenshot.Plugin.GooglePhotos/GooglePhotosUtils.cs +++ b/src/Greenshot.Plugin.GooglePhotos/GooglePhotosUtils.cs @@ -26,95 +26,118 @@ using GreenshotPlugin.IniFile; using GreenshotPlugin.Interfaces; using GreenshotPlugin.Interfaces.Plugin; -namespace Greenshot.Plugin.GooglePhotos { - /// - /// Description of GooglePhotosUtils. - /// - public static class GooglePhotosUtils { - private const string GooglePhotosScope = "https://picasaweb.google.com/data/"; - private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(typeof(GooglePhotosUtils)); - private static readonly GooglePhotosConfiguration Config = IniConfig.GetIniSection(); - private const string AuthUrl = "https://accounts.google.com/o/oauth2/auth?response_type=code&client_id={ClientId}&redirect_uri={RedirectUrl}&state={State}&scope=" + GooglePhotosScope; - private const string TokenUrl = "https://www.googleapis.com/oauth2/v3/token"; - private const string UploadUrl = "https://picasaweb.google.com/data/feed/api/user/{0}/albumid/{1}"; +namespace Greenshot.Plugin.GooglePhotos +{ + /// + /// Description of GooglePhotosUtils. + /// + public static class GooglePhotosUtils + { + private const string GooglePhotosScope = "https://picasaweb.google.com/data/"; + private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(typeof(GooglePhotosUtils)); + private static readonly GooglePhotosConfiguration Config = IniConfig.GetIniSection(); - /// - /// Do the actual upload to GooglePhotos - /// - /// Image to upload - /// SurfaceOutputSettings - /// string - /// string - /// GooglePhotosResponse - public static string UploadToGooglePhotos(ISurface surfaceToUpload, SurfaceOutputSettings outputSettings, string title, string filename) { - // Fill the OAuth2Settings - var settings = new OAuth2Settings - { - AuthUrlPattern = AuthUrl, - TokenUrl = TokenUrl, - CloudServiceName = "GooglePhotos", - ClientId = GooglePhotosCredentials.ClientId, - ClientSecret = GooglePhotosCredentials.ClientSecret, - AuthorizeMode = OAuth2AuthorizeMode.JsonReceiver, - RefreshToken = Config.RefreshToken, - AccessToken = Config.AccessToken, - AccessTokenExpires = Config.AccessTokenExpires - }; + private const string AuthUrl = "https://accounts.google.com/o/oauth2/auth?response_type=code&client_id={ClientId}&redirect_uri={RedirectUrl}&state={State}&scope=" + + GooglePhotosScope; - // Copy the settings from the config, which is kept in memory and on the disk + private const string TokenUrl = "https://www.googleapis.com/oauth2/v3/token"; + private const string UploadUrl = "https://picasaweb.google.com/data/feed/api/user/{0}/albumid/{1}"; - try { - var webRequest = OAuth2Helper.CreateOAuth2WebRequest(HTTPMethod.POST, string.Format(UploadUrl, Config.UploadUser, Config.UploadAlbum), settings); - if (Config.AddFilename) { - webRequest.Headers.Add("Slug", NetworkHelper.EscapeDataString(filename)); - } - SurfaceContainer container = new SurfaceContainer(surfaceToUpload, outputSettings, filename); - container.Upload(webRequest); - - string response = NetworkHelper.GetResponseAsString(webRequest); + /// + /// Do the actual upload to GooglePhotos + /// + /// Image to upload + /// SurfaceOutputSettings + /// string + /// string + /// GooglePhotosResponse + public static string UploadToGooglePhotos(ISurface surfaceToUpload, SurfaceOutputSettings outputSettings, string title, string filename) + { + // Fill the OAuth2Settings + var settings = new OAuth2Settings + { + AuthUrlPattern = AuthUrl, + TokenUrl = TokenUrl, + CloudServiceName = "GooglePhotos", + ClientId = GooglePhotosCredentials.ClientId, + ClientSecret = GooglePhotosCredentials.ClientSecret, + AuthorizeMode = OAuth2AuthorizeMode.JsonReceiver, + RefreshToken = Config.RefreshToken, + AccessToken = Config.AccessToken, + AccessTokenExpires = Config.AccessTokenExpires + }; - return ParseResponse(response); - } finally { - // Copy the settings back to the config, so they are stored. - Config.RefreshToken = settings.RefreshToken; - Config.AccessToken = settings.AccessToken; - Config.AccessTokenExpires = settings.AccessTokenExpires; - Config.IsDirty = true; - IniConfig.Save(); - } - } - - /// - /// Parse the upload URL from the response - /// - /// - /// - public static string ParseResponse(string response) { - if (response == null) { - return null; - } - try { - XmlDocument doc = new XmlDocument(); - doc.LoadXml(response); - XmlNodeList nodes = doc.GetElementsByTagName("link", "*"); - if(nodes.Count > 0) { - string url = null; - foreach(XmlNode node in nodes) { - if (node.Attributes != null) { - url = node.Attributes["href"].Value; - string rel = node.Attributes["rel"].Value; - // Pictures with rel="http://schemas.google.com/photos/2007#canonical" are the direct link - if (rel != null && rel.EndsWith("canonical")) { - break; - } - } - } - return url; - } - } catch(Exception e) { - Log.ErrorFormat("Could not parse GooglePhotos response due to error {0}, response was: {1}", e.Message, response); - } - return null; - } - } -} + // Copy the settings from the config, which is kept in memory and on the disk + + try + { + var webRequest = OAuth2Helper.CreateOAuth2WebRequest(HTTPMethod.POST, string.Format(UploadUrl, Config.UploadUser, Config.UploadAlbum), settings); + if (Config.AddFilename) + { + webRequest.Headers.Add("Slug", NetworkHelper.EscapeDataString(filename)); + } + + SurfaceContainer container = new SurfaceContainer(surfaceToUpload, outputSettings, filename); + container.Upload(webRequest); + + string response = NetworkHelper.GetResponseAsString(webRequest); + + return ParseResponse(response); + } + finally + { + // Copy the settings back to the config, so they are stored. + Config.RefreshToken = settings.RefreshToken; + Config.AccessToken = settings.AccessToken; + Config.AccessTokenExpires = settings.AccessTokenExpires; + Config.IsDirty = true; + IniConfig.Save(); + } + } + + /// + /// Parse the upload URL from the response + /// + /// + /// + public static string ParseResponse(string response) + { + if (response == null) + { + return null; + } + + try + { + XmlDocument doc = new XmlDocument(); + doc.LoadXml(response); + XmlNodeList nodes = doc.GetElementsByTagName("link", "*"); + if (nodes.Count > 0) + { + string url = null; + foreach (XmlNode node in nodes) + { + if (node.Attributes != null) + { + url = node.Attributes["href"].Value; + string rel = node.Attributes["rel"].Value; + // Pictures with rel="http://schemas.google.com/photos/2007#canonical" are the direct link + if (rel != null && rel.EndsWith("canonical")) + { + break; + } + } + } + + return url; + } + } + catch (Exception e) + { + Log.ErrorFormat("Could not parse GooglePhotos response due to error {0}, response was: {1}", e.Message, response); + } + + return null; + } + } +} \ No newline at end of file diff --git a/src/Greenshot.Plugin.GooglePhotos/LanguageKeys.cs b/src/Greenshot.Plugin.GooglePhotos/LanguageKeys.cs index 34feda885..7eaab1020 100644 --- a/src/Greenshot.Plugin.GooglePhotos/LanguageKeys.cs +++ b/src/Greenshot.Plugin.GooglePhotos/LanguageKeys.cs @@ -18,12 +18,13 @@ * along with this program. If not, see . */ -namespace Greenshot.Plugin.GooglePhotos { - public enum LangKey - { - upload_menu_item, - upload_failure, - communication_wait, - Configure - } -} +namespace Greenshot.Plugin.GooglePhotos +{ + public enum LangKey + { + upload_menu_item, + upload_failure, + communication_wait, + Configure + } +} \ No newline at end of file diff --git a/src/Greenshot.Plugin.GooglePhotos/Properties/AssemblyInfo.cs b/src/Greenshot.Plugin.GooglePhotos/Properties/AssemblyInfo.cs index d4fa2878a..d04662538 100644 --- a/src/Greenshot.Plugin.GooglePhotos/Properties/AssemblyInfo.cs +++ b/src/Greenshot.Plugin.GooglePhotos/Properties/AssemblyInfo.cs @@ -29,4 +29,4 @@ using System.Runtime.InteropServices; // This sets the default COM visibility of types in the assembly to invisible. // If you need to expose a type to COM, use [ComVisible(true)] on that type. -[assembly: ComVisible(false)] +[assembly: ComVisible(false)] \ No newline at end of file diff --git a/src/Greenshot.Plugin.Imgur/Forms/ImgurForm.cs b/src/Greenshot.Plugin.Imgur/Forms/ImgurForm.cs index c4fb88791..c94f6d1eb 100644 --- a/src/Greenshot.Plugin.Imgur/Forms/ImgurForm.cs +++ b/src/Greenshot.Plugin.Imgur/Forms/ImgurForm.cs @@ -21,10 +21,12 @@ using GreenshotPlugin.Controls; -namespace Greenshot.Plugin.Imgur.Forms { - /// - /// This class is needed for design-time resolving of the language files - /// - public class ImgurForm : GreenshotForm { - } -} +namespace Greenshot.Plugin.Imgur.Forms +{ + /// + /// This class is needed for design-time resolving of the language files + /// + public class ImgurForm : GreenshotForm + { + } +} \ No newline at end of file diff --git a/src/Greenshot.Plugin.Imgur/Forms/ImgurHistory.cs b/src/Greenshot.Plugin.Imgur/Forms/ImgurHistory.cs index 3b2efcd48..417f5e7d5 100644 --- a/src/Greenshot.Plugin.Imgur/Forms/ImgurHistory.cs +++ b/src/Greenshot.Plugin.Imgur/Forms/ImgurHistory.cs @@ -27,195 +27,237 @@ using GreenshotPlugin.Controls; using GreenshotPlugin.Core; using GreenshotPlugin.IniFile; -namespace Greenshot.Plugin.Imgur.Forms { - /// - /// Imgur history form - /// - public sealed partial class ImgurHistory : ImgurForm { - private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(typeof(ImgurHistory)); - private readonly GreenshotColumnSorter _columnSorter; - private static readonly object Lock = new object(); - private static readonly ImgurConfiguration Config = IniConfig.GetIniSection(); - private static ImgurHistory _instance; - - public static void ShowHistory() { - lock (Lock) - { - if (ImgurUtils.IsHistoryLoadingNeeded()) - { - // Run upload in the background - new PleaseWaitForm().ShowAndWait("Imgur " + Language.GetString("imgur", LangKey.history), Language.GetString("imgur", LangKey.communication_wait), - ImgurUtils.LoadHistory - ); - } +namespace Greenshot.Plugin.Imgur.Forms +{ + /// + /// Imgur history form + /// + public sealed partial class ImgurHistory : ImgurForm + { + private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(typeof(ImgurHistory)); + private readonly GreenshotColumnSorter _columnSorter; + private static readonly object Lock = new object(); + private static readonly ImgurConfiguration Config = IniConfig.GetIniSection(); + private static ImgurHistory _instance; - // Make sure the history is loaded, will be done only once - if (_instance == null) - { - _instance = new ImgurHistory(); - } - if (!_instance.Visible) - { - _instance.Show(); - } - _instance.Redraw(); - } - } - - private ImgurHistory() { - ManualLanguageApply = true; - // - // The InitializeComponent() call is required for Windows Forms designer support. - // - InitializeComponent(); - AcceptButton = finishedButton; - CancelButton = finishedButton; - // Init sorting - _columnSorter = new GreenshotColumnSorter(); - listview_imgur_uploads.ListViewItemSorter = _columnSorter; - _columnSorter.SortColumn = 3; - _columnSorter.Order = SortOrder.Descending; - Redraw(); - if (listview_imgur_uploads.Items.Count > 0) { - listview_imgur_uploads.Items[0].Selected = true; - } - ApplyLanguage(); - if (Config.Credits > 0) { - Text = Text + " (" + Config.Credits + " credits)"; - } - } + public static void ShowHistory() + { + lock (Lock) + { + if (ImgurUtils.IsHistoryLoadingNeeded()) + { + // Run upload in the background + new PleaseWaitForm().ShowAndWait("Imgur " + Language.GetString("imgur", LangKey.history), Language.GetString("imgur", LangKey.communication_wait), + ImgurUtils.LoadHistory + ); + } - private void Redraw() { - // Should fix Bug #3378699 - pictureBox1.Image = pictureBox1.ErrorImage; - listview_imgur_uploads.BeginUpdate(); - listview_imgur_uploads.Items.Clear(); - listview_imgur_uploads.Columns.Clear(); - string[] columns = { "hash", "title", "deleteHash", "Date"}; - foreach (string column in columns) { - listview_imgur_uploads.Columns.Add(column); - } - foreach (ImgurInfo imgurInfo in Config.runtimeImgurHistory.Values) { - var item = new ListViewItem(imgurInfo.Hash) - { - Tag = imgurInfo - }; - item.SubItems.Add(imgurInfo.Title); - item.SubItems.Add(imgurInfo.DeleteHash); - item.SubItems.Add(imgurInfo.Timestamp.ToString("yyyy-MM-dd HH:mm:ss", DateTimeFormatInfo.InvariantInfo)); - listview_imgur_uploads.Items.Add(item); - } - for (int i = 0; i < columns.Length; i++) { - listview_imgur_uploads.AutoResizeColumn(i, ColumnHeaderAutoResizeStyle.ColumnContent); - } - - listview_imgur_uploads.EndUpdate(); - listview_imgur_uploads.Refresh(); - deleteButton.Enabled = false; - openButton.Enabled = false; - clipboardButton.Enabled = false; - } + // Make sure the history is loaded, will be done only once + if (_instance == null) + { + _instance = new ImgurHistory(); + } - private void Listview_imgur_uploadsSelectedIndexChanged(object sender, EventArgs e) { - pictureBox1.Image = pictureBox1.ErrorImage; - if (listview_imgur_uploads.SelectedItems.Count > 0) { - deleteButton.Enabled = true; - openButton.Enabled = true; - clipboardButton.Enabled = true; - if (listview_imgur_uploads.SelectedItems.Count == 1) { - ImgurInfo imgurInfo = (ImgurInfo)listview_imgur_uploads.SelectedItems[0].Tag; - pictureBox1.Image = imgurInfo.Image; - } - } else { - pictureBox1.Image = pictureBox1.ErrorImage; - deleteButton.Enabled = false; - openButton.Enabled = false; - clipboardButton.Enabled = false; - } - } + if (!_instance.Visible) + { + _instance.Show(); + } - private void DeleteButtonClick(object sender, EventArgs e) { - if (listview_imgur_uploads.SelectedItems.Count > 0) { - for (int i = 0; i < listview_imgur_uploads.SelectedItems.Count; i++) { - ImgurInfo imgurInfo = (ImgurInfo)listview_imgur_uploads.SelectedItems[i].Tag; - DialogResult result = MessageBox.Show(Language.GetFormattedString("imgur", LangKey.delete_question, imgurInfo.Title), Language.GetFormattedString("imgur", LangKey.delete_title, imgurInfo.Hash), MessageBoxButtons.YesNo, MessageBoxIcon.Question); - if (result != DialogResult.Yes) - { - continue; - } - // Should fix Bug #3378699 - pictureBox1.Image = pictureBox1.ErrorImage; - try { - new PleaseWaitForm().ShowAndWait("Imgur", Language.GetString("imgur", LangKey.communication_wait), - delegate { - ImgurUtils.DeleteImgurImage(imgurInfo); - } - ); - } catch (Exception ex) { - Log.Warn("Problem communicating with Imgur: ", ex); - } + _instance.Redraw(); + } + } - imgurInfo.Dispose(); - } - } - Redraw(); - } + private ImgurHistory() + { + ManualLanguageApply = true; + // + // The InitializeComponent() call is required for Windows Forms designer support. + // + InitializeComponent(); + AcceptButton = finishedButton; + CancelButton = finishedButton; + // Init sorting + _columnSorter = new GreenshotColumnSorter(); + listview_imgur_uploads.ListViewItemSorter = _columnSorter; + _columnSorter.SortColumn = 3; + _columnSorter.Order = SortOrder.Descending; + Redraw(); + if (listview_imgur_uploads.Items.Count > 0) + { + listview_imgur_uploads.Items[0].Selected = true; + } - private void ClipboardButtonClick(object sender, EventArgs e) { - StringBuilder links = new StringBuilder(); - if (listview_imgur_uploads.SelectedItems.Count > 0) { - for (int i = 0; i < listview_imgur_uploads.SelectedItems.Count; i++) - { - ImgurInfo imgurInfo = (ImgurInfo)listview_imgur_uploads.SelectedItems[i].Tag; - links.AppendLine(Config.UsePageLink ? imgurInfo.Page : imgurInfo.Original); - } - } - ClipboardHelper.SetClipboardData(links.ToString()); - } + ApplyLanguage(); + if (Config.Credits > 0) + { + Text = Text + " (" + Config.Credits + " credits)"; + } + } - private void ClearHistoryButtonClick(object sender, EventArgs e) { - DialogResult result = MessageBox.Show(Language.GetString("imgur", LangKey.clear_question), "Imgur", MessageBoxButtons.YesNo, MessageBoxIcon.Question); - if (result == DialogResult.Yes) { - Config.runtimeImgurHistory.Clear(); - Config.ImgurUploadHistory.Clear(); - IniConfig.Save(); - Redraw(); - } - } + private void Redraw() + { + // Should fix Bug #3378699 + pictureBox1.Image = pictureBox1.ErrorImage; + listview_imgur_uploads.BeginUpdate(); + listview_imgur_uploads.Items.Clear(); + listview_imgur_uploads.Columns.Clear(); + string[] columns = + { + "hash", "title", "deleteHash", "Date" + }; + foreach (string column in columns) + { + listview_imgur_uploads.Columns.Add(column); + } - private void FinishedButtonClick(object sender, EventArgs e) - { - Hide(); - } + foreach (ImgurInfo imgurInfo in Config.runtimeImgurHistory.Values) + { + var item = new ListViewItem(imgurInfo.Hash) + { + Tag = imgurInfo + }; + item.SubItems.Add(imgurInfo.Title); + item.SubItems.Add(imgurInfo.DeleteHash); + item.SubItems.Add(imgurInfo.Timestamp.ToString("yyyy-MM-dd HH:mm:ss", DateTimeFormatInfo.InvariantInfo)); + listview_imgur_uploads.Items.Add(item); + } - private void OpenButtonClick(object sender, EventArgs e) { - if (listview_imgur_uploads.SelectedItems.Count > 0) { - for (int i = 0; i < listview_imgur_uploads.SelectedItems.Count; i++) { - ImgurInfo imgurInfo = (ImgurInfo)listview_imgur_uploads.SelectedItems[i].Tag; - System.Diagnostics.Process.Start(imgurInfo.Page); - } - } - } - - private void listview_imgur_uploads_ColumnClick(object sender, ColumnClickEventArgs e) { - // Determine if clicked column is already the column that is being sorted. - if (e.Column == _columnSorter.SortColumn) { - // Reverse the current sort direction for this column. - _columnSorter.Order = _columnSorter.Order == SortOrder.Ascending ? SortOrder.Descending : SortOrder.Ascending; - } else { - // Set the column number that is to be sorted; default to ascending. - _columnSorter.SortColumn = e.Column; - _columnSorter.Order = SortOrder.Ascending; - } + for (int i = 0; i < columns.Length; i++) + { + listview_imgur_uploads.AutoResizeColumn(i, ColumnHeaderAutoResizeStyle.ColumnContent); + } - // Perform the sort with these new sort options. - listview_imgur_uploads.Sort(); - } + listview_imgur_uploads.EndUpdate(); + listview_imgur_uploads.Refresh(); + deleteButton.Enabled = false; + openButton.Enabled = false; + clipboardButton.Enabled = false; + } + + private void Listview_imgur_uploadsSelectedIndexChanged(object sender, EventArgs e) + { + pictureBox1.Image = pictureBox1.ErrorImage; + if (listview_imgur_uploads.SelectedItems.Count > 0) + { + deleteButton.Enabled = true; + openButton.Enabled = true; + clipboardButton.Enabled = true; + if (listview_imgur_uploads.SelectedItems.Count == 1) + { + ImgurInfo imgurInfo = (ImgurInfo) listview_imgur_uploads.SelectedItems[0].Tag; + pictureBox1.Image = imgurInfo.Image; + } + } + else + { + pictureBox1.Image = pictureBox1.ErrorImage; + deleteButton.Enabled = false; + openButton.Enabled = false; + clipboardButton.Enabled = false; + } + } + + private void DeleteButtonClick(object sender, EventArgs e) + { + if (listview_imgur_uploads.SelectedItems.Count > 0) + { + for (int i = 0; i < listview_imgur_uploads.SelectedItems.Count; i++) + { + ImgurInfo imgurInfo = (ImgurInfo) listview_imgur_uploads.SelectedItems[i].Tag; + DialogResult result = MessageBox.Show(Language.GetFormattedString("imgur", LangKey.delete_question, imgurInfo.Title), + Language.GetFormattedString("imgur", LangKey.delete_title, imgurInfo.Hash), MessageBoxButtons.YesNo, MessageBoxIcon.Question); + if (result != DialogResult.Yes) + { + continue; + } + + // Should fix Bug #3378699 + pictureBox1.Image = pictureBox1.ErrorImage; + try + { + new PleaseWaitForm().ShowAndWait("Imgur", Language.GetString("imgur", LangKey.communication_wait), + delegate { ImgurUtils.DeleteImgurImage(imgurInfo); } + ); + } + catch (Exception ex) + { + Log.Warn("Problem communicating with Imgur: ", ex); + } + + imgurInfo.Dispose(); + } + } + + Redraw(); + } + + private void ClipboardButtonClick(object sender, EventArgs e) + { + StringBuilder links = new StringBuilder(); + if (listview_imgur_uploads.SelectedItems.Count > 0) + { + for (int i = 0; i < listview_imgur_uploads.SelectedItems.Count; i++) + { + ImgurInfo imgurInfo = (ImgurInfo) listview_imgur_uploads.SelectedItems[i].Tag; + links.AppendLine(Config.UsePageLink ? imgurInfo.Page : imgurInfo.Original); + } + } + + ClipboardHelper.SetClipboardData(links.ToString()); + } + + private void ClearHistoryButtonClick(object sender, EventArgs e) + { + DialogResult result = MessageBox.Show(Language.GetString("imgur", LangKey.clear_question), "Imgur", MessageBoxButtons.YesNo, MessageBoxIcon.Question); + if (result == DialogResult.Yes) + { + Config.runtimeImgurHistory.Clear(); + Config.ImgurUploadHistory.Clear(); + IniConfig.Save(); + Redraw(); + } + } + + private void FinishedButtonClick(object sender, EventArgs e) + { + Hide(); + } + + private void OpenButtonClick(object sender, EventArgs e) + { + if (listview_imgur_uploads.SelectedItems.Count > 0) + { + for (int i = 0; i < listview_imgur_uploads.SelectedItems.Count; i++) + { + ImgurInfo imgurInfo = (ImgurInfo) listview_imgur_uploads.SelectedItems[i].Tag; + System.Diagnostics.Process.Start(imgurInfo.Page); + } + } + } + + private void listview_imgur_uploads_ColumnClick(object sender, ColumnClickEventArgs e) + { + // Determine if clicked column is already the column that is being sorted. + if (e.Column == _columnSorter.SortColumn) + { + // Reverse the current sort direction for this column. + _columnSorter.Order = _columnSorter.Order == SortOrder.Ascending ? SortOrder.Descending : SortOrder.Ascending; + } + else + { + // Set the column number that is to be sorted; default to ascending. + _columnSorter.SortColumn = e.Column; + _columnSorter.Order = SortOrder.Ascending; + } + + // Perform the sort with these new sort options. + listview_imgur_uploads.Sort(); + } - private void ImgurHistoryFormClosing(object sender, FormClosingEventArgs e) - { - _instance = null; - } - } -} + private void ImgurHistoryFormClosing(object sender, FormClosingEventArgs e) + { + _instance = null; + } + } +} \ No newline at end of file diff --git a/src/Greenshot.Plugin.Imgur/Forms/SettingsForm.cs b/src/Greenshot.Plugin.Imgur/Forms/SettingsForm.cs index 1ece15fb9..4958f897c 100644 --- a/src/Greenshot.Plugin.Imgur/Forms/SettingsForm.cs +++ b/src/Greenshot.Plugin.Imgur/Forms/SettingsForm.cs @@ -21,25 +21,28 @@ using System; -namespace Greenshot.Plugin.Imgur.Forms { - /// - /// Description of PasswordRequestForm. - /// - public partial class SettingsForm : ImgurForm { - public SettingsForm() - { - // - // The InitializeComponent() call is required for Windows Forms designer support. - // - InitializeComponent(); - CancelButton = buttonCancel; - AcceptButton = buttonOK; - - historyButton.Enabled = ImgurUtils.IsHistoryLoadingNeeded(); - } +namespace Greenshot.Plugin.Imgur.Forms +{ + /// + /// Description of PasswordRequestForm. + /// + public partial class SettingsForm : ImgurForm + { + public SettingsForm() + { + // + // The InitializeComponent() call is required for Windows Forms designer support. + // + InitializeComponent(); + CancelButton = buttonCancel; + AcceptButton = buttonOK; - private void ButtonHistoryClick(object sender, EventArgs e) { - ImgurHistory.ShowHistory(); - } - } -} + historyButton.Enabled = ImgurUtils.IsHistoryLoadingNeeded(); + } + + private void ButtonHistoryClick(object sender, EventArgs e) + { + ImgurHistory.ShowHistory(); + } + } +} \ No newline at end of file diff --git a/src/Greenshot.Plugin.Imgur/ImgurConfiguration.cs b/src/Greenshot.Plugin.Imgur/ImgurConfiguration.cs index e9725a2d2..fdac54ee3 100644 --- a/src/Greenshot.Plugin.Imgur/ImgurConfiguration.cs +++ b/src/Greenshot.Plugin.Imgur/ImgurConfiguration.cs @@ -26,64 +26,70 @@ using Greenshot.Plugin.Imgur.Forms; using GreenshotPlugin.Core; using GreenshotPlugin.IniFile; -namespace Greenshot.Plugin.Imgur { - /// - /// Description of ImgurConfiguration. - /// - [IniSection("Imgur", Description="Greenshot Imgur Plugin configuration")] - public class ImgurConfiguration : IniSection { - [IniProperty("ImgurApi3Url", Description = "Url to Imgur system.", DefaultValue = "https://api.imgur.com/3")] - public string ImgurApi3Url { get; set; } +namespace Greenshot.Plugin.Imgur +{ + /// + /// Description of ImgurConfiguration. + /// + [IniSection("Imgur", Description = "Greenshot Imgur Plugin configuration")] + public class ImgurConfiguration : IniSection + { + [IniProperty("ImgurApi3Url", Description = "Url to Imgur system.", DefaultValue = "https://api.imgur.com/3")] + public string ImgurApi3Url { get; set; } - [IniProperty("UploadFormat", Description="What file type to use for uploading", DefaultValue="png")] - public OutputFormat UploadFormat { get; set; } - [IniProperty("UploadJpegQuality", Description="JPEG file save quality in %.", DefaultValue="80")] - public int UploadJpegQuality { get; set; } - [IniProperty("UploadReduceColors", Description="Reduce color amount of the uploaded image to 256", DefaultValue="False")] - public bool UploadReduceColors { get; set; } - [IniProperty("CopyLinkToClipboard", Description = "Copy the link, which one is controlled by the UsePageLink, on the clipboard", DefaultValue = "True")] - public bool CopyLinkToClipboard { get; set; } - [IniProperty("UsePageLink", Description = "Use pagelink instead of direct link on the clipboard", DefaultValue = "False")] - public bool UsePageLink { get; set; } - [IniProperty("AnonymousAccess", Description = "Use anonymous access to Imgur", DefaultValue="true")] - public bool AnonymousAccess { get; set; } + [IniProperty("UploadFormat", Description = "What file type to use for uploading", DefaultValue = "png")] + public OutputFormat UploadFormat { get; set; } - [IniProperty("RefreshToken", Description = "Imgur refresh Token", Encrypted = true, ExcludeIfNull = true)] - public string RefreshToken { get; set; } + [IniProperty("UploadJpegQuality", Description = "JPEG file save quality in %.", DefaultValue = "80")] + public int UploadJpegQuality { get; set; } - /// - /// AccessToken, not stored - /// - public string AccessToken { get; set; } + [IniProperty("UploadReduceColors", Description = "Reduce color amount of the uploaded image to 256", DefaultValue = "False")] + public bool UploadReduceColors { get; set; } - /// - /// AccessTokenExpires, not stored - /// - public DateTimeOffset AccessTokenExpires { get; set; } + [IniProperty("CopyLinkToClipboard", Description = "Copy the link, which one is controlled by the UsePageLink, on the clipboard", DefaultValue = "True")] + public bool CopyLinkToClipboard { get; set; } - [IniProperty("AddTitle", Description = "Is the title passed on to Imgur", DefaultValue = "False")] - public bool AddTitle { get; set; } - [IniProperty("AddFilename", Description = "Is the filename passed on to Imgur", DefaultValue = "False")] - public bool AddFilename { get; set; } - [IniProperty("FilenamePattern", Description = "Filename for the Imgur upload", DefaultValue = "${capturetime:d\"yyyyMMdd-HHmm\"}")] - public string FilenamePattern { get; set; } + [IniProperty("UsePageLink", Description = "Use pagelink instead of direct link on the clipboard", DefaultValue = "False")] + public bool UsePageLink { get; set; } - [IniProperty("ImgurUploadHistory", Description="Imgur upload history (ImgurUploadHistory.hash=deleteHash)")] - public Dictionary ImgurUploadHistory { get; set; } + [IniProperty("AnonymousAccess", Description = "Use anonymous access to Imgur", DefaultValue = "true")] + public bool AnonymousAccess { get; set; } - // Not stored, only run-time! - public Dictionary runtimeImgurHistory = new Dictionary(); - public int Credits { - get; - set; - } + [IniProperty("RefreshToken", Description = "Imgur refresh Token", Encrypted = true, ExcludeIfNull = true)] + public string RefreshToken { get; set; } - /// - /// Supply values we can't put as defaults - /// - /// The property to return a default for - /// object with the default value for the supplied property - public override object GetDefault(string property) => + /// + /// AccessToken, not stored + /// + public string AccessToken { get; set; } + + /// + /// AccessTokenExpires, not stored + /// + public DateTimeOffset AccessTokenExpires { get; set; } + + [IniProperty("AddTitle", Description = "Is the title passed on to Imgur", DefaultValue = "False")] + public bool AddTitle { get; set; } + + [IniProperty("AddFilename", Description = "Is the filename passed on to Imgur", DefaultValue = "False")] + public bool AddFilename { get; set; } + + [IniProperty("FilenamePattern", Description = "Filename for the Imgur upload", DefaultValue = "${capturetime:d\"yyyyMMdd-HHmm\"}")] + public string FilenamePattern { get; set; } + + [IniProperty("ImgurUploadHistory", Description = "Imgur upload history (ImgurUploadHistory.hash=deleteHash)")] + public Dictionary ImgurUploadHistory { get; set; } + + // Not stored, only run-time! + public Dictionary runtimeImgurHistory = new Dictionary(); + public int Credits { get; set; } + + /// + /// Supply values we can't put as defaults + /// + /// The property to return a default for + /// object with the default value for the supplied property + public override object GetDefault(string property) => property switch { "ImgurUploadHistory" => new Dictionary(), @@ -91,13 +97,14 @@ namespace Greenshot.Plugin.Imgur { }; /// - /// A form for username/password - /// - /// bool true if OK was pressed, false if cancel - public bool ShowConfigDialog() { - SettingsForm settingsForm = new SettingsForm(); - DialogResult result = settingsForm.ShowDialog(); - return result == DialogResult.OK; - } - } -} + /// A form for username/password + /// + /// bool true if OK was pressed, false if cancel + public bool ShowConfigDialog() + { + SettingsForm settingsForm = new SettingsForm(); + DialogResult result = settingsForm.ShowDialog(); + return result == DialogResult.OK; + } + } +} \ No newline at end of file diff --git a/src/Greenshot.Plugin.Imgur/ImgurDestination.cs b/src/Greenshot.Plugin.Imgur/ImgurDestination.cs index aa24ad68c..a0d069f1a 100644 --- a/src/Greenshot.Plugin.Imgur/ImgurDestination.cs +++ b/src/Greenshot.Plugin.Imgur/ImgurDestination.cs @@ -24,35 +24,42 @@ using System.Drawing; using GreenshotPlugin.Core; using GreenshotPlugin.Interfaces; -namespace Greenshot.Plugin.Imgur { - /// - /// Description of ImgurDestination. - /// - public class ImgurDestination : AbstractDestination { - private readonly ImgurPlugin _plugin; +namespace Greenshot.Plugin.Imgur +{ + /// + /// Description of ImgurDestination. + /// + public class ImgurDestination : AbstractDestination + { + private readonly ImgurPlugin _plugin; - public ImgurDestination(ImgurPlugin plugin) { - _plugin = plugin; - } - - public override string Designation => "Imgur"; + public ImgurDestination(ImgurPlugin plugin) + { + _plugin = plugin; + } - public override string Description => Language.GetString("imgur", LangKey.upload_menu_item); + public override string Designation => "Imgur"; - public override Image DisplayIcon { - get { - ComponentResourceManager resources = new ComponentResourceManager(typeof(ImgurPlugin)); - return (Image)resources.GetObject("Imgur"); - } - } + public override string Description => Language.GetString("imgur", LangKey.upload_menu_item); - public override ExportInformation ExportCapture(bool manuallyInitiated, ISurface surface, ICaptureDetails captureDetails) { + public override Image DisplayIcon + { + get + { + ComponentResourceManager resources = new ComponentResourceManager(typeof(ImgurPlugin)); + return (Image) resources.GetObject("Imgur"); + } + } + + public override ExportInformation ExportCapture(bool manuallyInitiated, ISurface surface, ICaptureDetails captureDetails) + { ExportInformation exportInformation = new ExportInformation(Designation, Description) { - ExportMade = _plugin.Upload(captureDetails, surface, out var uploadUrl), Uri = uploadUrl + ExportMade = _plugin.Upload(captureDetails, surface, out var uploadUrl), + Uri = uploadUrl }; ProcessExport(exportInformation, surface); - return exportInformation; - } - } -} + return exportInformation; + } + } +} \ No newline at end of file diff --git a/src/Greenshot.Plugin.Imgur/ImgurInfo.cs b/src/Greenshot.Plugin.Imgur/ImgurInfo.cs index 5dc82ec2a..4fdfb7a86 100644 --- a/src/Greenshot.Plugin.Imgur/ImgurInfo.cs +++ b/src/Greenshot.Plugin.Imgur/ImgurInfo.cs @@ -25,194 +25,172 @@ using System.Xml; namespace Greenshot.Plugin.Imgur { - /// - /// Description of ImgurInfo. - /// - public class ImgurInfo : IDisposable - { - private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(typeof(ImgurInfo)); + /// + /// Description of ImgurInfo. + /// + public class ImgurInfo : IDisposable + { + private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(typeof(ImgurInfo)); - public string Hash - { - get; - set; - } + public string Hash { get; set; } - private string _deleteHash; - public string DeleteHash - { - get { return _deleteHash; } - set - { - _deleteHash = value; - DeletePage = "https://imgur.com/delete/" + value; - } - } + private string _deleteHash; - public string Title - { - get; - set; - } + public string DeleteHash + { + get { return _deleteHash; } + set + { + _deleteHash = value; + DeletePage = "https://imgur.com/delete/" + value; + } + } - public string ImageType - { - get; - set; - } + public string Title { get; set; } - public DateTime Timestamp - { - get; - set; - } + public string ImageType { get; set; } - public string Original - { - get; - set; - } + public DateTime Timestamp { get; set; } - public string Page - { - get; - set; - } + public string Original { get; set; } - public string SmallSquare - { - get; - set; - } + public string Page { get; set; } - public string LargeThumbnail - { - get; - set; - } + public string SmallSquare { get; set; } - public string DeletePage - { - get; - set; - } + public string LargeThumbnail { get; set; } - private Image _image; - public Image Image - { - get { return _image; } - set - { - _image?.Dispose(); - _image = value; - } - } + public string DeletePage { get; set; } - /// - /// The public accessible Dispose - /// Will call the GarbageCollector to SuppressFinalize, preventing being cleaned twice - /// - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } + private Image _image; - /// - /// This Dispose is called from the Dispose and the Destructor. - /// When disposing==true all non-managed resources should be freed too! - /// - /// - protected virtual void Dispose(bool disposing) - { - if (disposing) - { - _image?.Dispose(); - } - _image = null; - } - public static ImgurInfo ParseResponse(string response) - { - Log.Debug(response); - // This is actually a hack for BUG-1695 - // The problem is the (C) sign, we send it HTML encoded "®" to Imgur and get it HTML encoded in the XML back - // Added all the encodings I found quickly, I guess these are not all... but it should fix the issue for now. - response = response.Replace("¢", "¢"); - response = response.Replace("£", "£"); - response = response.Replace("¥", "¥"); - response = response.Replace("€", "€"); - response = response.Replace("©", "©"); - response = response.Replace("®", "®"); + public Image Image + { + get { return _image; } + set + { + _image?.Dispose(); + _image = value; + } + } - ImgurInfo imgurInfo = new ImgurInfo(); - try - { - XmlDocument doc = new XmlDocument(); - doc.LoadXml(response); - XmlNodeList nodes = doc.GetElementsByTagName("id"); - if (nodes.Count > 0) - { - imgurInfo.Hash = nodes.Item(0)?.InnerText; - } - nodes = doc.GetElementsByTagName("hash"); - if (nodes.Count > 0) - { - imgurInfo.Hash = nodes.Item(0)?.InnerText; - } - nodes = doc.GetElementsByTagName("deletehash"); - if (nodes.Count > 0) - { - imgurInfo.DeleteHash = nodes.Item(0)?.InnerText; - } - nodes = doc.GetElementsByTagName("type"); - if (nodes.Count > 0) - { - imgurInfo.ImageType = nodes.Item(0)?.InnerText; - } - nodes = doc.GetElementsByTagName("title"); - if (nodes.Count > 0) - { - imgurInfo.Title = nodes.Item(0)?.InnerText; - } - nodes = doc.GetElementsByTagName("datetime"); - if (nodes.Count > 0) - { - // Version 3 has seconds since Epoch + /// + /// The public accessible Dispose + /// Will call the GarbageCollector to SuppressFinalize, preventing being cleaned twice + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + /// + /// This Dispose is called from the Dispose and the Destructor. + /// When disposing==true all non-managed resources should be freed too! + /// + /// + protected virtual void Dispose(bool disposing) + { + if (disposing) + { + _image?.Dispose(); + } + + _image = null; + } + + public static ImgurInfo ParseResponse(string response) + { + Log.Debug(response); + // This is actually a hack for BUG-1695 + // The problem is the (C) sign, we send it HTML encoded "®" to Imgur and get it HTML encoded in the XML back + // Added all the encodings I found quickly, I guess these are not all... but it should fix the issue for now. + response = response.Replace("¢", "¢"); + response = response.Replace("£", "£"); + response = response.Replace("¥", "¥"); + response = response.Replace("€", "€"); + response = response.Replace("©", "©"); + response = response.Replace("®", "®"); + + ImgurInfo imgurInfo = new ImgurInfo(); + try + { + XmlDocument doc = new XmlDocument(); + doc.LoadXml(response); + XmlNodeList nodes = doc.GetElementsByTagName("id"); + if (nodes.Count > 0) + { + imgurInfo.Hash = nodes.Item(0)?.InnerText; + } + + nodes = doc.GetElementsByTagName("hash"); + if (nodes.Count > 0) + { + imgurInfo.Hash = nodes.Item(0)?.InnerText; + } + + nodes = doc.GetElementsByTagName("deletehash"); + if (nodes.Count > 0) + { + imgurInfo.DeleteHash = nodes.Item(0)?.InnerText; + } + + nodes = doc.GetElementsByTagName("type"); + if (nodes.Count > 0) + { + imgurInfo.ImageType = nodes.Item(0)?.InnerText; + } + + nodes = doc.GetElementsByTagName("title"); + if (nodes.Count > 0) + { + imgurInfo.Title = nodes.Item(0)?.InnerText; + } + + nodes = doc.GetElementsByTagName("datetime"); + if (nodes.Count > 0) + { + // Version 3 has seconds since Epoch if (double.TryParse(nodes.Item(0)?.InnerText, out var secondsSince)) - { - var epoch = new DateTimeOffset(1970, 1, 1, 0, 0, 0, TimeSpan.Zero); - imgurInfo.Timestamp = epoch.AddSeconds(secondsSince).DateTime; - } - } - nodes = doc.GetElementsByTagName("original"); - if (nodes.Count > 0) - { - imgurInfo.Original = nodes.Item(0)?.InnerText.Replace("http:", "https:"); - } - // Version 3 API only has Link - nodes = doc.GetElementsByTagName("link"); - if (nodes.Count > 0) - { - imgurInfo.Original = nodes.Item(0)?.InnerText.Replace("http:", "https:"); - } - nodes = doc.GetElementsByTagName("imgur_page"); - if (nodes.Count > 0) - { - imgurInfo.Page = nodes.Item(0)?.InnerText.Replace("http:", "https:"); - } - else - { - // Version 3 doesn't have a page link in the response - imgurInfo.Page = $"https://imgur.com/{imgurInfo.Hash}"; - } - nodes = doc.GetElementsByTagName("small_square"); - imgurInfo.SmallSquare = nodes.Count > 0 ? nodes.Item(0)?.InnerText : $"http://i.imgur.com/{imgurInfo.Hash}s.png"; - } - catch (Exception e) - { - Log.ErrorFormat("Could not parse Imgur response due to error {0}, response was: {1}", e.Message, response); - } - return imgurInfo; - } - } -} + { + var epoch = new DateTimeOffset(1970, 1, 1, 0, 0, 0, TimeSpan.Zero); + imgurInfo.Timestamp = epoch.AddSeconds(secondsSince).DateTime; + } + } + + nodes = doc.GetElementsByTagName("original"); + if (nodes.Count > 0) + { + imgurInfo.Original = nodes.Item(0)?.InnerText.Replace("http:", "https:"); + } + + // Version 3 API only has Link + nodes = doc.GetElementsByTagName("link"); + if (nodes.Count > 0) + { + imgurInfo.Original = nodes.Item(0)?.InnerText.Replace("http:", "https:"); + } + + nodes = doc.GetElementsByTagName("imgur_page"); + if (nodes.Count > 0) + { + imgurInfo.Page = nodes.Item(0)?.InnerText.Replace("http:", "https:"); + } + else + { + // Version 3 doesn't have a page link in the response + imgurInfo.Page = $"https://imgur.com/{imgurInfo.Hash}"; + } + + nodes = doc.GetElementsByTagName("small_square"); + imgurInfo.SmallSquare = nodes.Count > 0 ? nodes.Item(0)?.InnerText : $"http://i.imgur.com/{imgurInfo.Hash}s.png"; + } + catch (Exception e) + { + Log.ErrorFormat("Could not parse Imgur response due to error {0}, response was: {1}", e.Message, response); + } + + return imgurInfo; + } + } +} \ No newline at end of file diff --git a/src/Greenshot.Plugin.Imgur/ImgurPlugin.cs b/src/Greenshot.Plugin.Imgur/ImgurPlugin.cs index ef2edfe4b..a936c80d7 100644 --- a/src/Greenshot.Plugin.Imgur/ImgurPlugin.cs +++ b/src/Greenshot.Plugin.Imgur/ImgurPlugin.cs @@ -32,186 +32,216 @@ using GreenshotPlugin.IniFile; using GreenshotPlugin.Interfaces; using GreenshotPlugin.Interfaces.Plugin; -namespace Greenshot.Plugin.Imgur { - /// - /// This is the ImgurPlugin code - /// +namespace Greenshot.Plugin.Imgur +{ + /// + /// This is the ImgurPlugin code + /// [Plugin("Imgur", true)] - public class ImgurPlugin : IGreenshotPlugin { - private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(typeof(ImgurPlugin)); - private static ImgurConfiguration _config; - private ComponentResourceManager _resources; - private ToolStripMenuItem _historyMenuItem; - private ToolStripMenuItem _itemPlugInConfig; + public class ImgurPlugin : IGreenshotPlugin + { + private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(typeof(ImgurPlugin)); + private static ImgurConfiguration _config; + private ComponentResourceManager _resources; + private ToolStripMenuItem _historyMenuItem; + private ToolStripMenuItem _itemPlugInConfig; - public void Dispose() { - Dispose(true); - GC.SuppressFinalize(this); - } + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } - protected virtual void Dispose(bool disposing) { - if (disposing) { - if (_historyMenuItem != null) { - _historyMenuItem.Dispose(); - _historyMenuItem = null; - } - if (_itemPlugInConfig != null) { - _itemPlugInConfig.Dispose(); - _itemPlugInConfig = null; - } - } - } + protected virtual void Dispose(bool disposing) + { + if (disposing) + { + if (_historyMenuItem != null) + { + _historyMenuItem.Dispose(); + _historyMenuItem = null; + } - private IEnumerable Destinations() { - yield return new ImgurDestination(this); - } + if (_itemPlugInConfig != null) + { + _itemPlugInConfig.Dispose(); + _itemPlugInConfig = null; + } + } + } - /// - /// Implementation of the IGreenshotPlugin.Initialize - /// - /// true if plugin is initialized, false if not (doesn't show) - public bool Initialize() { - // Get configuration - _config = IniConfig.GetIniSection(); - _resources = new ComponentResourceManager(typeof(ImgurPlugin)); + private IEnumerable Destinations() + { + yield return new ImgurDestination(this); + } - ToolStripMenuItem itemPlugInRoot = new ToolStripMenuItem("Imgur") - { - Image = (Image) _resources.GetObject("Imgur") - }; + /// + /// Implementation of the IGreenshotPlugin.Initialize + /// + /// true if plugin is initialized, false if not (doesn't show) + public bool Initialize() + { + // Get configuration + _config = IniConfig.GetIniSection(); + _resources = new ComponentResourceManager(typeof(ImgurPlugin)); + + ToolStripMenuItem itemPlugInRoot = new ToolStripMenuItem("Imgur") + { + Image = (Image) _resources.GetObject("Imgur") + }; // Provide the IDestination SimpleServiceProvider.Current.AddService(Destinations()); _historyMenuItem = new ToolStripMenuItem(Language.GetString("imgur", LangKey.history)); - _historyMenuItem.Click += delegate { - ImgurHistory.ShowHistory(); - }; - itemPlugInRoot.DropDownItems.Add(_historyMenuItem); + _historyMenuItem.Click += delegate { ImgurHistory.ShowHistory(); }; + itemPlugInRoot.DropDownItems.Add(_historyMenuItem); _itemPlugInConfig = new ToolStripMenuItem(Language.GetString("imgur", LangKey.configure)); - _itemPlugInConfig.Click += delegate { - _config.ShowConfigDialog(); - }; - itemPlugInRoot.DropDownItems.Add(_itemPlugInConfig); + _itemPlugInConfig.Click += delegate { _config.ShowConfigDialog(); }; + itemPlugInRoot.DropDownItems.Add(_itemPlugInConfig); - PluginUtils.AddToContextMenu(itemPlugInRoot); - Language.LanguageChanged += OnLanguageChanged; + PluginUtils.AddToContextMenu(itemPlugInRoot); + Language.LanguageChanged += OnLanguageChanged; - // Enable history if there are items available - UpdateHistoryMenuItem(); - return true; - } + // Enable history if there are items available + UpdateHistoryMenuItem(); + return true; + } - public void OnLanguageChanged(object sender, EventArgs e) { - if (_itemPlugInConfig != null) { - _itemPlugInConfig.Text = Language.GetString("imgur", LangKey.configure); - } - if (_historyMenuItem != null) { - _historyMenuItem.Text = Language.GetString("imgur", LangKey.history); - } - } + public void OnLanguageChanged(object sender, EventArgs e) + { + if (_itemPlugInConfig != null) + { + _itemPlugInConfig.Text = Language.GetString("imgur", LangKey.configure); + } - private void UpdateHistoryMenuItem() { - if (_historyMenuItem == null) - { - return; - } - try + if (_historyMenuItem != null) + { + _historyMenuItem.Text = Language.GetString("imgur", LangKey.history); + } + } + + private void UpdateHistoryMenuItem() + { + if (_historyMenuItem == null) + { + return; + } + + try { var form = SimpleServiceProvider.Current.GetInstance
(); - form.BeginInvoke((MethodInvoker)delegate + form.BeginInvoke((MethodInvoker) delegate { var historyMenuItem = _historyMenuItem; - if (historyMenuItem == null) + if (historyMenuItem == null) { return; } - if (_config?.ImgurUploadHistory != null && _config.ImgurUploadHistory.Count > 0) { + + if (_config?.ImgurUploadHistory != null && _config.ImgurUploadHistory.Count > 0) + { historyMenuItem.Enabled = true; - } else { + } + else + { historyMenuItem.Enabled = false; - } - }); - } catch (Exception ex) { - Log.Error("Error loading history", ex); - } - } + } + }); + } + catch (Exception ex) + { + Log.Error("Error loading history", ex); + } + } - public virtual void Shutdown() { - Log.Debug("Imgur Plugin shutdown."); - Language.LanguageChanged -= OnLanguageChanged; - } + public virtual void Shutdown() + { + Log.Debug("Imgur Plugin shutdown."); + Language.LanguageChanged -= OnLanguageChanged; + } - /// - /// Implementation of the IPlugin.Configure - /// - public virtual void Configure() { - _config.ShowConfigDialog(); - } + /// + /// Implementation of the IPlugin.Configure + /// + public virtual void Configure() + { + _config.ShowConfigDialog(); + } - /// - /// Upload the capture to imgur - /// - /// ICaptureDetails - /// ISurface - /// out string for the url - /// true if the upload succeeded - public bool Upload(ICaptureDetails captureDetails, ISurface surfaceToUpload, out string uploadUrl) { - SurfaceOutputSettings outputSettings = new SurfaceOutputSettings(_config.UploadFormat, _config.UploadJpegQuality, _config.UploadReduceColors); - try { - string filename = Path.GetFileName(FilenameHelper.GetFilenameFromPattern(_config.FilenamePattern, _config.UploadFormat, captureDetails)); - ImgurInfo imgurInfo = null; + /// + /// Upload the capture to imgur + /// + /// ICaptureDetails + /// ISurface + /// out string for the url + /// true if the upload succeeded + public bool Upload(ICaptureDetails captureDetails, ISurface surfaceToUpload, out string uploadUrl) + { + SurfaceOutputSettings outputSettings = new SurfaceOutputSettings(_config.UploadFormat, _config.UploadJpegQuality, _config.UploadReduceColors); + try + { + string filename = Path.GetFileName(FilenameHelper.GetFilenameFromPattern(_config.FilenamePattern, _config.UploadFormat, captureDetails)); + ImgurInfo imgurInfo = null; - // Run upload in the background - new PleaseWaitForm().ShowAndWait("Imgur plug-in", Language.GetString("imgur", LangKey.communication_wait), - delegate - { - imgurInfo = ImgurUtils.UploadToImgur(surfaceToUpload, outputSettings, captureDetails.Title, filename); - if (imgurInfo != null && _config.AnonymousAccess) { - Log.InfoFormat("Storing imgur upload for hash {0} and delete hash {1}", imgurInfo.Hash, imgurInfo.DeleteHash); - _config.ImgurUploadHistory.Add(imgurInfo.Hash, imgurInfo.DeleteHash); - _config.runtimeImgurHistory.Add(imgurInfo.Hash, imgurInfo); - UpdateHistoryMenuItem(); - } - } - ); + // Run upload in the background + new PleaseWaitForm().ShowAndWait("Imgur plug-in", Language.GetString("imgur", LangKey.communication_wait), + delegate + { + imgurInfo = ImgurUtils.UploadToImgur(surfaceToUpload, outputSettings, captureDetails.Title, filename); + if (imgurInfo != null && _config.AnonymousAccess) + { + Log.InfoFormat("Storing imgur upload for hash {0} and delete hash {1}", imgurInfo.Hash, imgurInfo.DeleteHash); + _config.ImgurUploadHistory.Add(imgurInfo.Hash, imgurInfo.DeleteHash); + _config.runtimeImgurHistory.Add(imgurInfo.Hash, imgurInfo); + UpdateHistoryMenuItem(); + } + } + ); - if (imgurInfo != null) { - // TODO: Optimize a second call for export - using (Image tmpImage = surfaceToUpload.GetImageForExport()) { - imgurInfo.Image = ImageHelper.CreateThumbnail(tmpImage, 90, 90); - } - IniConfig.Save(); + if (imgurInfo != null) + { + // TODO: Optimize a second call for export + using (Image tmpImage = surfaceToUpload.GetImageForExport()) + { + imgurInfo.Image = ImageHelper.CreateThumbnail(tmpImage, 90, 90); + } - if (_config.UsePageLink) - { - uploadUrl = imgurInfo.Page; - } - else - { - uploadUrl = imgurInfo.Original; - } - if (!string.IsNullOrEmpty(uploadUrl) && _config.CopyLinkToClipboard) - { - try - { - ClipboardHelper.SetClipboardData(uploadUrl); + IniConfig.Save(); - } - catch (Exception ex) - { - Log.Error("Can't write to clipboard: ", ex); - uploadUrl = null; - } - } - return true; - } - } catch (Exception e) { - Log.Error("Error uploading.", e); - MessageBox.Show(Language.GetString("imgur", LangKey.upload_failure) + " " + e.Message); - } - uploadUrl = null; - return false; - } - } -} + if (_config.UsePageLink) + { + uploadUrl = imgurInfo.Page; + } + else + { + uploadUrl = imgurInfo.Original; + } + + if (!string.IsNullOrEmpty(uploadUrl) && _config.CopyLinkToClipboard) + { + try + { + ClipboardHelper.SetClipboardData(uploadUrl); + } + catch (Exception ex) + { + Log.Error("Can't write to clipboard: ", ex); + uploadUrl = null; + } + } + + return true; + } + } + catch (Exception e) + { + Log.Error("Error uploading.", e); + MessageBox.Show(Language.GetString("imgur", LangKey.upload_failure) + " " + e.Message); + } + + uploadUrl = null; + return false; + } + } +} \ No newline at end of file diff --git a/src/Greenshot.Plugin.Imgur/ImgurUtils.cs b/src/Greenshot.Plugin.Imgur/ImgurUtils.cs index e80aa0f49..32b83d6e3 100644 --- a/src/Greenshot.Plugin.Imgur/ImgurUtils.cs +++ b/src/Greenshot.Plugin.Imgur/ImgurUtils.cs @@ -30,127 +30,157 @@ using GreenshotPlugin.IniFile; using GreenshotPlugin.Interfaces; using GreenshotPlugin.Interfaces.Plugin; -namespace Greenshot.Plugin.Imgur { - /// - /// A collection of Imgur helper methods - /// - public static class ImgurUtils { - private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(typeof(ImgurUtils)); - private const string SmallUrlPattern = "http://i.imgur.com/{0}s.jpg"; - private static readonly ImgurConfiguration Config = IniConfig.GetIniSection(); +namespace Greenshot.Plugin.Imgur +{ + /// + /// A collection of Imgur helper methods + /// + public static class ImgurUtils + { + private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(typeof(ImgurUtils)); + private const string SmallUrlPattern = "http://i.imgur.com/{0}s.jpg"; + private static readonly ImgurConfiguration Config = IniConfig.GetIniSection(); /// - /// Check if we need to load the history - /// - /// - public static bool IsHistoryLoadingNeeded() - { - Log.InfoFormat("Checking if imgur cache loading needed, configuration has {0} imgur hashes, loaded are {1} hashes.", Config.ImgurUploadHistory.Count, Config.runtimeImgurHistory.Count); - return Config.runtimeImgurHistory.Count != Config.ImgurUploadHistory.Count; - } + /// Check if we need to load the history + /// + /// + public static bool IsHistoryLoadingNeeded() + { + Log.InfoFormat("Checking if imgur cache loading needed, configuration has {0} imgur hashes, loaded are {1} hashes.", Config.ImgurUploadHistory.Count, + Config.runtimeImgurHistory.Count); + return Config.runtimeImgurHistory.Count != Config.ImgurUploadHistory.Count; + } - /// - /// Load the complete history of the imgur uploads, with the corresponding information - /// - public static void LoadHistory() { - if (!IsHistoryLoadingNeeded()) - { - return; - } + /// + /// Load the complete history of the imgur uploads, with the corresponding information + /// + public static void LoadHistory() + { + if (!IsHistoryLoadingNeeded()) + { + return; + } - bool saveNeeded = false; + bool saveNeeded = false; - // Load the ImUr history - foreach (string hash in Config.ImgurUploadHistory.Keys.ToList()) { - if (Config.runtimeImgurHistory.ContainsKey(hash)) { - // Already loaded - continue; - } + // Load the ImUr history + foreach (string hash in Config.ImgurUploadHistory.Keys.ToList()) + { + if (Config.runtimeImgurHistory.ContainsKey(hash)) + { + // Already loaded + continue; + } - try - { - var deleteHash = Config.ImgurUploadHistory[hash]; - ImgurInfo imgurInfo = RetrieveImgurInfo(hash, deleteHash); - if (imgurInfo != null) { - RetrieveImgurThumbnail(imgurInfo); - Config.runtimeImgurHistory[hash] = imgurInfo; - } else { - Log.InfoFormat("Deleting unknown ImgUr {0} from config, delete hash was {1}.", hash, deleteHash); - Config.ImgurUploadHistory.Remove(hash); - Config.runtimeImgurHistory.Remove(hash); - saveNeeded = true; - } - } catch (WebException wE) { - bool redirected = false; - if (wE.Status == WebExceptionStatus.ProtocolError) { - HttpWebResponse response = (HttpWebResponse)wE.Response; + try + { + var deleteHash = Config.ImgurUploadHistory[hash]; + ImgurInfo imgurInfo = RetrieveImgurInfo(hash, deleteHash); + if (imgurInfo != null) + { + RetrieveImgurThumbnail(imgurInfo); + Config.runtimeImgurHistory[hash] = imgurInfo; + } + else + { + Log.InfoFormat("Deleting unknown ImgUr {0} from config, delete hash was {1}.", hash, deleteHash); + Config.ImgurUploadHistory.Remove(hash); + Config.runtimeImgurHistory.Remove(hash); + saveNeeded = true; + } + } + catch (WebException wE) + { + bool redirected = false; + if (wE.Status == WebExceptionStatus.ProtocolError) + { + HttpWebResponse response = (HttpWebResponse) wE.Response; - if (response.StatusCode == HttpStatusCode.Forbidden) - { - Log.Error("Imgur loading forbidden", wE); - break; - } - // Image no longer available? - if (response.StatusCode == HttpStatusCode.Redirect) { - Log.InfoFormat("ImgUr image for hash {0} is no longer available, removing it from the history", hash); - Config.ImgurUploadHistory.Remove(hash); - Config.runtimeImgurHistory.Remove(hash); - redirected = true; - } - } - if (!redirected) { - Log.Error("Problem loading ImgUr history for hash " + hash, wE); - } - } catch (Exception e) { - Log.Error("Problem loading ImgUr history for hash " + hash, e); - } - } - if (saveNeeded) { - // Save needed changes - IniConfig.Save(); - } - } + if (response.StatusCode == HttpStatusCode.Forbidden) + { + Log.Error("Imgur loading forbidden", wE); + break; + } - /// - /// Use this to make sure Imgur knows from where the upload comes. - /// - /// - private static void SetClientId(HttpWebRequest webRequest) { - webRequest.Headers.Add("Authorization", "Client-ID " + ImgurCredentials.CONSUMER_KEY); - } + // Image no longer available? + if (response.StatusCode == HttpStatusCode.Redirect) + { + Log.InfoFormat("ImgUr image for hash {0} is no longer available, removing it from the history", hash); + Config.ImgurUploadHistory.Remove(hash); + Config.runtimeImgurHistory.Remove(hash); + redirected = true; + } + } - /// - /// Do the actual upload to Imgur - /// For more details on the available parameters, see: http://api.imgur.com/resources_anon - /// - /// ISurface to upload - /// OutputSettings for the image file format - /// Title - /// Filename - /// ImgurInfo with details - public static ImgurInfo UploadToImgur(ISurface surfaceToUpload, SurfaceOutputSettings outputSettings, string title, string filename) { - IDictionary otherParameters = new Dictionary(); - // add title - if (title != null && Config.AddTitle) { - otherParameters["title"]= title; - } - // add filename - if (filename != null && Config.AddFilename) { - otherParameters["name"] = filename; - } - string responseString = null; - if (Config.AnonymousAccess) { - // add key, we only use the other parameters for the AnonymousAccess - //otherParameters.Add("key", IMGUR_ANONYMOUS_API_KEY); - HttpWebRequest webRequest = NetworkHelper.CreateWebRequest(Config.ImgurApi3Url + "/upload.xml?" + NetworkHelper.GenerateQueryParameters(otherParameters), HTTPMethod.POST); - webRequest.ContentType = "image/" + outputSettings.Format; - webRequest.ServicePoint.Expect100Continue = false; + if (!redirected) + { + Log.Error("Problem loading ImgUr history for hash " + hash, wE); + } + } + catch (Exception e) + { + Log.Error("Problem loading ImgUr history for hash " + hash, e); + } + } - SetClientId(webRequest); - try { - using (var requestStream = webRequest.GetRequestStream()) { - ImageOutput.SaveToStream(surfaceToUpload, requestStream, outputSettings); - } + if (saveNeeded) + { + // Save needed changes + IniConfig.Save(); + } + } + + /// + /// Use this to make sure Imgur knows from where the upload comes. + /// + /// + private static void SetClientId(HttpWebRequest webRequest) + { + webRequest.Headers.Add("Authorization", "Client-ID " + ImgurCredentials.CONSUMER_KEY); + } + + /// + /// Do the actual upload to Imgur + /// For more details on the available parameters, see: http://api.imgur.com/resources_anon + /// + /// ISurface to upload + /// OutputSettings for the image file format + /// Title + /// Filename + /// ImgurInfo with details + public static ImgurInfo UploadToImgur(ISurface surfaceToUpload, SurfaceOutputSettings outputSettings, string title, string filename) + { + IDictionary otherParameters = new Dictionary(); + // add title + if (title != null && Config.AddTitle) + { + otherParameters["title"] = title; + } + + // add filename + if (filename != null && Config.AddFilename) + { + otherParameters["name"] = filename; + } + + string responseString = null; + if (Config.AnonymousAccess) + { + // add key, we only use the other parameters for the AnonymousAccess + //otherParameters.Add("key", IMGUR_ANONYMOUS_API_KEY); + HttpWebRequest webRequest = + NetworkHelper.CreateWebRequest(Config.ImgurApi3Url + "/upload.xml?" + NetworkHelper.GenerateQueryParameters(otherParameters), HTTPMethod.POST); + webRequest.ContentType = "image/" + outputSettings.Format; + webRequest.ServicePoint.Expect100Continue = false; + + SetClientId(webRequest); + try + { + using (var requestStream = webRequest.GetRequestStream()) + { + ImageOutput.SaveToStream(surfaceToUpload, requestStream, outputSettings); + } using WebResponse response = webRequest.GetResponse(); LogRateLimitInfo(response); @@ -160,68 +190,76 @@ namespace Greenshot.Plugin.Imgur { using StreamReader reader = new StreamReader(responseStream, true); responseString = reader.ReadToEnd(); } - } catch (Exception ex) { - Log.Error("Upload to imgur gave an exception: ", ex); - throw; - } - } else { + } + catch (Exception ex) + { + Log.Error("Upload to imgur gave an exception: ", ex); + throw; + } + } + else + { + var oauth2Settings = new OAuth2Settings + { + AuthUrlPattern = "https://api.imgur.com/oauth2/authorize?response_type=token&client_id={ClientId}&state={State}", + TokenUrl = "https://api.imgur.com/oauth2/token", + RedirectUrl = "https://getgreenshot.org/authorize/imgur", + CloudServiceName = "Imgur", + ClientId = ImgurCredentials.CONSUMER_KEY, + ClientSecret = ImgurCredentials.CONSUMER_SECRET, + AuthorizeMode = OAuth2AuthorizeMode.JsonReceiver, + RefreshToken = Config.RefreshToken, + AccessToken = Config.AccessToken, + AccessTokenExpires = Config.AccessTokenExpires + }; - var oauth2Settings = new OAuth2Settings - { - AuthUrlPattern = "https://api.imgur.com/oauth2/authorize?response_type=token&client_id={ClientId}&state={State}", - TokenUrl = "https://api.imgur.com/oauth2/token", - RedirectUrl = "https://getgreenshot.org/authorize/imgur", - CloudServiceName = "Imgur", - ClientId = ImgurCredentials.CONSUMER_KEY, - ClientSecret = ImgurCredentials.CONSUMER_SECRET, - AuthorizeMode = OAuth2AuthorizeMode.JsonReceiver, - RefreshToken = Config.RefreshToken, - AccessToken = Config.AccessToken, - AccessTokenExpires = Config.AccessTokenExpires - }; + // Copy the settings from the config, which is kept in memory and on the disk - // Copy the settings from the config, which is kept in memory and on the disk + try + { + var webRequest = OAuth2Helper.CreateOAuth2WebRequest(HTTPMethod.POST, Config.ImgurApi3Url + "/upload.xml", oauth2Settings); + otherParameters["image"] = new SurfaceContainer(surfaceToUpload, outputSettings, filename); - try - { - var webRequest = OAuth2Helper.CreateOAuth2WebRequest(HTTPMethod.POST, Config.ImgurApi3Url + "/upload.xml", oauth2Settings); - otherParameters["image"] = new SurfaceContainer(surfaceToUpload, outputSettings, filename); + NetworkHelper.WriteMultipartFormData(webRequest, otherParameters); - NetworkHelper.WriteMultipartFormData(webRequest, otherParameters); + responseString = NetworkHelper.GetResponseAsString(webRequest); + } + finally + { + // Copy the settings back to the config, so they are stored. + Config.RefreshToken = oauth2Settings.RefreshToken; + Config.AccessToken = oauth2Settings.AccessToken; + Config.AccessTokenExpires = oauth2Settings.AccessTokenExpires; + Config.IsDirty = true; + IniConfig.Save(); + } + } - responseString = NetworkHelper.GetResponseAsString(webRequest); - } - finally - { - // Copy the settings back to the config, so they are stored. - Config.RefreshToken = oauth2Settings.RefreshToken; - Config.AccessToken = oauth2Settings.AccessToken; - Config.AccessTokenExpires = oauth2Settings.AccessTokenExpires; - Config.IsDirty = true; - IniConfig.Save(); - } - } - if (string.IsNullOrEmpty(responseString)) - { - return null; - } - return ImgurInfo.ParseResponse(responseString); - } + if (string.IsNullOrEmpty(responseString)) + { + return null; + } - /// - /// Retrieve the thumbnail of an imgur image - /// - /// - public static void RetrieveImgurThumbnail(ImgurInfo imgurInfo) { - if (imgurInfo.SmallSquare == null) { - Log.Warn("Imgur URL was null, not retrieving thumbnail."); - return; - } - Log.InfoFormat("Retrieving Imgur image for {0} with url {1}", imgurInfo.Hash, imgurInfo.SmallSquare); - HttpWebRequest webRequest = NetworkHelper.CreateWebRequest(string.Format(SmallUrlPattern, imgurInfo.Hash), HTTPMethod.GET); - webRequest.ServicePoint.Expect100Continue = false; - // Not for getting the thumbnail, in anonymous mode - //SetClientId(webRequest); + return ImgurInfo.ParseResponse(responseString); + } + + /// + /// Retrieve the thumbnail of an imgur image + /// + /// + public static void RetrieveImgurThumbnail(ImgurInfo imgurInfo) + { + if (imgurInfo.SmallSquare == null) + { + Log.Warn("Imgur URL was null, not retrieving thumbnail."); + return; + } + + Log.InfoFormat("Retrieving Imgur image for {0} with url {1}", imgurInfo.Hash, imgurInfo.SmallSquare); + HttpWebRequest webRequest = NetworkHelper.CreateWebRequest(string.Format(SmallUrlPattern, imgurInfo.Hash), HTTPMethod.GET); + webRequest.ServicePoint.Expect100Continue = false; + // Not for getting the thumbnail, in anonymous mode + //SetClientId(webRequest); using WebResponse response = webRequest.GetResponse(); LogRateLimitInfo(response); Stream responseStream = response.GetResponseStream(); @@ -231,20 +269,21 @@ namespace Greenshot.Plugin.Imgur { } } - /// - /// Retrieve information on an imgur image - /// - /// - /// - /// ImgurInfo - public static ImgurInfo RetrieveImgurInfo(string hash, string deleteHash) { - string url = Config.ImgurApi3Url + "/image/" + hash + ".xml"; - Log.InfoFormat("Retrieving Imgur info for {0} with url {1}", hash, url); - HttpWebRequest webRequest = NetworkHelper.CreateWebRequest(url, HTTPMethod.GET); - webRequest.ServicePoint.Expect100Continue = false; - SetClientId(webRequest); - string responseString = null; - try + /// + /// Retrieve information on an imgur image + /// + /// + /// + /// ImgurInfo + public static ImgurInfo RetrieveImgurInfo(string hash, string deleteHash) + { + string url = Config.ImgurApi3Url + "/image/" + hash + ".xml"; + Log.InfoFormat("Retrieving Imgur info for {0} with url {1}", hash, url); + HttpWebRequest webRequest = NetworkHelper.CreateWebRequest(url, HTTPMethod.GET); + webRequest.ServicePoint.Expect100Continue = false; + SetClientId(webRequest); + string responseString = null; + try { using WebResponse response = webRequest.GetResponse(); LogRateLimitInfo(response); @@ -254,95 +293,118 @@ namespace Greenshot.Plugin.Imgur { using StreamReader reader = new StreamReader(responseStream, true); responseString = reader.ReadToEnd(); } - } catch (WebException wE) { - if (wE.Status == WebExceptionStatus.ProtocolError) { - if (((HttpWebResponse)wE.Response).StatusCode == HttpStatusCode.NotFound) { - return null; - } - } - throw; - } - ImgurInfo imgurInfo = null; - if (responseString != null) - { - Log.Debug(responseString); - imgurInfo = ImgurInfo.ParseResponse(responseString); - imgurInfo.DeleteHash = deleteHash; - } - return imgurInfo; - } + } + catch (WebException wE) + { + if (wE.Status == WebExceptionStatus.ProtocolError) + { + if (((HttpWebResponse) wE.Response).StatusCode == HttpStatusCode.NotFound) + { + return null; + } + } - /// - /// Delete an imgur image, this is done by specifying the delete hash - /// - /// - public static void DeleteImgurImage(ImgurInfo imgurInfo) { - Log.InfoFormat("Deleting Imgur image for {0}", imgurInfo.DeleteHash); + throw; + } - try { - string url = Config.ImgurApi3Url + "/image/" + imgurInfo.DeleteHash + ".xml"; - HttpWebRequest webRequest = NetworkHelper.CreateWebRequest(url, HTTPMethod.DELETE); - webRequest.ServicePoint.Expect100Continue = false; - SetClientId(webRequest); - string responseString = null; - using (WebResponse response = webRequest.GetResponse()) { - LogRateLimitInfo(response); - var responseStream = response.GetResponseStream(); - if (responseStream != null) + ImgurInfo imgurInfo = null; + if (responseString != null) + { + Log.Debug(responseString); + imgurInfo = ImgurInfo.ParseResponse(responseString); + imgurInfo.DeleteHash = deleteHash; + } + + return imgurInfo; + } + + /// + /// Delete an imgur image, this is done by specifying the delete hash + /// + /// + public static void DeleteImgurImage(ImgurInfo imgurInfo) + { + Log.InfoFormat("Deleting Imgur image for {0}", imgurInfo.DeleteHash); + + try + { + string url = Config.ImgurApi3Url + "/image/" + imgurInfo.DeleteHash + ".xml"; + HttpWebRequest webRequest = NetworkHelper.CreateWebRequest(url, HTTPMethod.DELETE); + webRequest.ServicePoint.Expect100Continue = false; + SetClientId(webRequest); + string responseString = null; + using (WebResponse response = webRequest.GetResponse()) + { + LogRateLimitInfo(response); + var responseStream = response.GetResponseStream(); + if (responseStream != null) { using StreamReader reader = new StreamReader(responseStream, true); responseString = reader.ReadToEnd(); } - } - Log.InfoFormat("Delete result: {0}", responseString); - } catch (WebException wE) { - // Allow "Bad request" this means we already deleted it - if (wE.Status == WebExceptionStatus.ProtocolError) { - if (((HttpWebResponse)wE.Response).StatusCode != HttpStatusCode.BadRequest) { - throw ; - } - } - } - // Make sure we remove it from the history, if no error occurred - Config.runtimeImgurHistory.Remove(imgurInfo.Hash); - Config.ImgurUploadHistory.Remove(imgurInfo.Hash); - imgurInfo.Image = null; - } + } - /// - /// Helper for logging - /// - /// - /// - private static void LogHeader(IDictionary nameValues, string key) { - if (nameValues.ContainsKey(key)) { - Log.InfoFormat("{0}={1}", key, nameValues[key]); - } - } + Log.InfoFormat("Delete result: {0}", responseString); + } + catch (WebException wE) + { + // Allow "Bad request" this means we already deleted it + if (wE.Status == WebExceptionStatus.ProtocolError) + { + if (((HttpWebResponse) wE.Response).StatusCode != HttpStatusCode.BadRequest) + { + throw; + } + } + } - /// - /// Log the current rate-limit information - /// - /// - private static void LogRateLimitInfo(WebResponse response) { - IDictionary nameValues = new Dictionary(); - foreach (string key in response.Headers.AllKeys) { - if (!nameValues.ContainsKey(key)) { - nameValues.Add(key, response.Headers[key]); - } - } - LogHeader(nameValues, "X-RateLimit-Limit"); - LogHeader(nameValues, "X-RateLimit-Remaining"); - LogHeader(nameValues, "X-RateLimit-UserLimit"); - LogHeader(nameValues, "X-RateLimit-UserRemaining"); - LogHeader(nameValues, "X-RateLimit-UserReset"); - LogHeader(nameValues, "X-RateLimit-ClientLimit"); - LogHeader(nameValues, "X-RateLimit-ClientRemaining"); + // Make sure we remove it from the history, if no error occurred + Config.runtimeImgurHistory.Remove(imgurInfo.Hash); + Config.ImgurUploadHistory.Remove(imgurInfo.Hash); + imgurInfo.Image = null; + } - // Update the credits in the config, this is shown in a form - if (nameValues.ContainsKey("X-RateLimit-Remaining") && int.TryParse(nameValues["X-RateLimit-Remaining"], out var credits)) { - Config.Credits = credits; - } - } - } -} + /// + /// Helper for logging + /// + /// + /// + private static void LogHeader(IDictionary nameValues, string key) + { + if (nameValues.ContainsKey(key)) + { + Log.InfoFormat("{0}={1}", key, nameValues[key]); + } + } + + /// + /// Log the current rate-limit information + /// + /// + private static void LogRateLimitInfo(WebResponse response) + { + IDictionary nameValues = new Dictionary(); + foreach (string key in response.Headers.AllKeys) + { + if (!nameValues.ContainsKey(key)) + { + nameValues.Add(key, response.Headers[key]); + } + } + + LogHeader(nameValues, "X-RateLimit-Limit"); + LogHeader(nameValues, "X-RateLimit-Remaining"); + LogHeader(nameValues, "X-RateLimit-UserLimit"); + LogHeader(nameValues, "X-RateLimit-UserRemaining"); + LogHeader(nameValues, "X-RateLimit-UserReset"); + LogHeader(nameValues, "X-RateLimit-ClientLimit"); + LogHeader(nameValues, "X-RateLimit-ClientRemaining"); + + // Update the credits in the config, this is shown in a form + if (nameValues.ContainsKey("X-RateLimit-Remaining") && int.TryParse(nameValues["X-RateLimit-Remaining"], out var credits)) + { + Config.Credits = credits; + } + } + } +} \ No newline at end of file diff --git a/src/Greenshot.Plugin.Imgur/LanguageKeys.cs b/src/Greenshot.Plugin.Imgur/LanguageKeys.cs index 9643fb92c..7b8a0ee3e 100644 --- a/src/Greenshot.Plugin.Imgur/LanguageKeys.cs +++ b/src/Greenshot.Plugin.Imgur/LanguageKeys.cs @@ -19,15 +19,17 @@ * along with this program. If not, see . */ -namespace Greenshot.Plugin.Imgur { - public enum LangKey { - upload_menu_item, - upload_failure, - communication_wait, - delete_question, - clear_question, - delete_title, - history, - configure - } -} +namespace Greenshot.Plugin.Imgur +{ + public enum LangKey + { + upload_menu_item, + upload_failure, + communication_wait, + delete_question, + clear_question, + delete_title, + history, + configure + } +} \ No newline at end of file diff --git a/src/Greenshot.Plugin.Imgur/Properties/AssemblyInfo.cs b/src/Greenshot.Plugin.Imgur/Properties/AssemblyInfo.cs index 6818a420b..31ecce89a 100644 --- a/src/Greenshot.Plugin.Imgur/Properties/AssemblyInfo.cs +++ b/src/Greenshot.Plugin.Imgur/Properties/AssemblyInfo.cs @@ -29,4 +29,4 @@ using System.Runtime.InteropServices; // This sets the default COM visibility of types in the assembly to invisible. // If you need to expose a type to COM, use [ComVisible(true)] on that type. -[assembly: ComVisible(false)] +[assembly: ComVisible(false)] \ No newline at end of file diff --git a/src/Greenshot.Plugin.Jira/AsyncMemoryCache.cs b/src/Greenshot.Plugin.Jira/AsyncMemoryCache.cs index 382e61c87..5445fafde 100644 --- a/src/Greenshot.Plugin.Jira/AsyncMemoryCache.cs +++ b/src/Greenshot.Plugin.Jira/AsyncMemoryCache.cs @@ -27,164 +27,165 @@ using Dapplo.Log; namespace Greenshot.Plugin.Jira { - /// - /// This abstract class builds a base for a simple async memory cache. - /// - /// Type for the key - /// Type for the stored value - public abstract class AsyncMemoryCache where TResult : class - { - private static readonly Task EmptyValueTask = Task.FromResult(null); - private readonly SemaphoreSlim _semaphoreSlim = new SemaphoreSlim(1, 1); - private readonly MemoryCache _cache = new MemoryCache(Guid.NewGuid().ToString()); - private readonly LogSource _log = new LogSource(); - - /// - /// Set the timespan for items to expire. - /// - public TimeSpan? ExpireTimeSpan { get; set; } - - /// - /// Set the timespan for items to slide. - /// - public TimeSpan? SlidingTimeSpan { get; set; } - - /// - /// Specifies if the RemovedCallback needs to be called - /// If this is active, ActivateUpdateCallback should be false - /// - protected bool ActivateRemovedCallback { get; set; } = true; - - /// - /// Specifies if the UpdateCallback needs to be called. - /// If this is active, ActivateRemovedCallback should be false - /// - protected bool ActivateUpdateCallback { get; set; } = false; - - /// - /// Implement this method, it should create an instance of TResult via the supplied TKey. - /// - /// TKey - /// CancellationToken - /// TResult - protected abstract Task CreateAsync(TKey key, CancellationToken cancellationToken = default); - - /// - /// Creates a key under which the object is stored or retrieved, default is a toString on the object. - /// - /// TKey - /// string - protected virtual string CreateKey(TKey keyObject) - { - return keyObject.ToString(); - } + /// + /// This abstract class builds a base for a simple async memory cache. + /// + /// Type for the key + /// Type for the stored value + public abstract class AsyncMemoryCache where TResult : class + { + private static readonly Task EmptyValueTask = Task.FromResult(null); + private readonly SemaphoreSlim _semaphoreSlim = new SemaphoreSlim(1, 1); + private readonly MemoryCache _cache = new MemoryCache(Guid.NewGuid().ToString()); + private readonly LogSource _log = new LogSource(); /// - /// Get a task element from the cache, if this is not available call the create function. - /// - /// object for the key - /// CancellationToken - /// Task with TResult - public Task GetOrCreateAsync(TKey keyObject, CancellationToken cancellationToken = default) - { - var key = CreateKey(keyObject); - return _cache.Get(key) as Task ?? GetOrCreateInternalAsync(keyObject, null, cancellationToken); - } + /// Set the timespan for items to expire. + /// + public TimeSpan? ExpireTimeSpan { get; set; } /// - /// This takes care of the real async part of the code. - /// - /// - /// CacheItemPolicy for when you want more control over the item - /// CancellationToken - /// TResult - private async Task GetOrCreateInternalAsync(TKey keyObject, CacheItemPolicy cacheItemPolicy = null, CancellationToken cancellationToken = default) - { - var key = CreateKey(keyObject); - var completionSource = new TaskCompletionSource(); + /// Set the timespan for items to slide. + /// + public TimeSpan? SlidingTimeSpan { get; set; } - if (cacheItemPolicy == null) - { - cacheItemPolicy = new CacheItemPolicy - { - AbsoluteExpiration = ExpireTimeSpan.HasValue ? DateTimeOffset.Now.Add(ExpireTimeSpan.Value) : ObjectCache.InfiniteAbsoluteExpiration, - SlidingExpiration = SlidingTimeSpan ?? ObjectCache.NoSlidingExpiration - }; - if (ActivateUpdateCallback) - { - cacheItemPolicy.UpdateCallback = UpdateCallback; - } - if (ActivateRemovedCallback) - { - cacheItemPolicy.RemovedCallback = RemovedCallback; - } - } + /// + /// Specifies if the RemovedCallback needs to be called + /// If this is active, ActivateUpdateCallback should be false + /// + protected bool ActivateRemovedCallback { get; set; } = true; + + /// + /// Specifies if the UpdateCallback needs to be called. + /// If this is active, ActivateRemovedCallback should be false + /// + protected bool ActivateUpdateCallback { get; set; } = false; + + /// + /// Implement this method, it should create an instance of TResult via the supplied TKey. + /// + /// TKey + /// CancellationToken + /// TResult + protected abstract Task CreateAsync(TKey key, CancellationToken cancellationToken = default); + + /// + /// Creates a key under which the object is stored or retrieved, default is a toString on the object. + /// + /// TKey + /// string + protected virtual string CreateKey(TKey keyObject) + { + return keyObject.ToString(); + } + + /// + /// Get a task element from the cache, if this is not available call the create function. + /// + /// object for the key + /// CancellationToken + /// Task with TResult + public Task GetOrCreateAsync(TKey keyObject, CancellationToken cancellationToken = default) + { + var key = CreateKey(keyObject); + return _cache.Get(key) as Task ?? GetOrCreateInternalAsync(keyObject, null, cancellationToken); + } + + /// + /// This takes care of the real async part of the code. + /// + /// + /// CacheItemPolicy for when you want more control over the item + /// CancellationToken + /// TResult + private async Task GetOrCreateInternalAsync(TKey keyObject, CacheItemPolicy cacheItemPolicy = null, CancellationToken cancellationToken = default) + { + var key = CreateKey(keyObject); + var completionSource = new TaskCompletionSource(); + + if (cacheItemPolicy == null) + { + cacheItemPolicy = new CacheItemPolicy + { + AbsoluteExpiration = ExpireTimeSpan.HasValue ? DateTimeOffset.Now.Add(ExpireTimeSpan.Value) : ObjectCache.InfiniteAbsoluteExpiration, + SlidingExpiration = SlidingTimeSpan ?? ObjectCache.NoSlidingExpiration + }; + if (ActivateUpdateCallback) + { + cacheItemPolicy.UpdateCallback = UpdateCallback; + } + + if (ActivateRemovedCallback) + { + cacheItemPolicy.RemovedCallback = RemovedCallback; + } + } // Test if we got an existing object or our own - if (_cache.AddOrGetExisting(key, completionSource.Task, cacheItemPolicy) is Task result && !completionSource.Task.Equals(result)) - { - return await result.ConfigureAwait(false); - } + if (_cache.AddOrGetExisting(key, completionSource.Task, cacheItemPolicy) is Task result && !completionSource.Task.Equals(result)) + { + return await result.ConfigureAwait(false); + } - await _semaphoreSlim.WaitAsync(cancellationToken).ConfigureAwait(false); - try - { - result = _cache.AddOrGetExisting(key, completionSource.Task, cacheItemPolicy) as Task; - if (result != null && !completionSource.Task.Equals(result)) - { - return await result.ConfigureAwait(false); - } + await _semaphoreSlim.WaitAsync(cancellationToken).ConfigureAwait(false); + try + { + result = _cache.AddOrGetExisting(key, completionSource.Task, cacheItemPolicy) as Task; + if (result != null && !completionSource.Task.Equals(result)) + { + return await result.ConfigureAwait(false); + } - // Now, start the background task, which will set the completionSource with the correct response - // ReSharper disable once MethodSupportsCancellation - // ReSharper disable once UnusedVariable - var ignoreBackgroundTask = Task.Run(async () => - { - try - { - var backgroundResult = await CreateAsync(keyObject, cancellationToken).ConfigureAwait(false); - completionSource.TrySetResult(backgroundResult); - } - catch (TaskCanceledException) - { - completionSource.TrySetCanceled(); - } - catch (Exception ex) - { - completionSource.TrySetException(ex); - } - }); - } - finally - { - _semaphoreSlim.Release(); - } + // Now, start the background task, which will set the completionSource with the correct response + // ReSharper disable once MethodSupportsCancellation + // ReSharper disable once UnusedVariable + var ignoreBackgroundTask = Task.Run(async () => + { + try + { + var backgroundResult = await CreateAsync(keyObject, cancellationToken).ConfigureAwait(false); + completionSource.TrySetResult(backgroundResult); + } + catch (TaskCanceledException) + { + completionSource.TrySetCanceled(); + } + catch (Exception ex) + { + completionSource.TrySetException(ex); + } + }); + } + finally + { + _semaphoreSlim.Release(); + } - return await completionSource.Task.ConfigureAwait(false); - } + return await completionSource.Task.ConfigureAwait(false); + } - /// - /// Override to know when an item is removed, make sure to configure ActivateUpdateCallback / ActivateRemovedCallback - /// - /// CacheEntryRemovedArguments - protected void RemovedCallback(CacheEntryRemovedArguments cacheEntryRemovedArguments) - { - _log.Verbose().WriteLine("Item {0} removed due to {1}.", cacheEntryRemovedArguments.CacheItem.Key, cacheEntryRemovedArguments.RemovedReason); + /// + /// Override to know when an item is removed, make sure to configure ActivateUpdateCallback / ActivateRemovedCallback + /// + /// CacheEntryRemovedArguments + protected void RemovedCallback(CacheEntryRemovedArguments cacheEntryRemovedArguments) + { + _log.Verbose().WriteLine("Item {0} removed due to {1}.", cacheEntryRemovedArguments.CacheItem.Key, cacheEntryRemovedArguments.RemovedReason); if (cacheEntryRemovedArguments.CacheItem.Value is IDisposable disposable) - { - _log.Debug().WriteLine("Disposed cached item."); - disposable.Dispose(); - } - } + { + _log.Debug().WriteLine("Disposed cached item."); + disposable.Dispose(); + } + } - /// - /// Override to modify the cache behaviour when an item is about to be removed, make sure to configure - /// ActivateUpdateCallback / ActivateRemovedCallback - /// - /// CacheEntryUpdateArguments - protected void UpdateCallback(CacheEntryUpdateArguments cacheEntryUpdateArguments) - { - _log.Verbose().WriteLine("Update request for {0} due to {1}.", cacheEntryUpdateArguments.Key, cacheEntryUpdateArguments.RemovedReason); - } - } -} + /// + /// Override to modify the cache behaviour when an item is about to be removed, make sure to configure + /// ActivateUpdateCallback / ActivateRemovedCallback + /// + /// CacheEntryUpdateArguments + protected void UpdateCallback(CacheEntryUpdateArguments cacheEntryUpdateArguments) + { + _log.Verbose().WriteLine("Update request for {0} due to {1}.", cacheEntryUpdateArguments.Key, cacheEntryUpdateArguments.RemovedReason); + } + } +} \ No newline at end of file diff --git a/src/Greenshot.Plugin.Jira/Forms/JiraForm.cs b/src/Greenshot.Plugin.Jira/Forms/JiraForm.cs index 3c3cad44d..6eb143889 100644 --- a/src/Greenshot.Plugin.Jira/Forms/JiraForm.cs +++ b/src/Greenshot.Plugin.Jira/Forms/JiraForm.cs @@ -30,207 +30,242 @@ using GreenshotPlugin.Controls; using GreenshotPlugin.Core; using GreenshotPlugin.IniFile; -namespace Greenshot.Plugin.Jira.Forms { - public partial class JiraForm : Form { - private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(typeof(JiraForm)); - private readonly JiraConnector _jiraConnector; - private Issue _selectedIssue; - private readonly GreenshotColumnSorter _columnSorter; - private static readonly CoreConfiguration CoreConfig = IniConfig.GetIniSection(); +namespace Greenshot.Plugin.Jira.Forms +{ + public partial class JiraForm : Form + { + private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(typeof(JiraForm)); + private readonly JiraConnector _jiraConnector; + private Issue _selectedIssue; + private readonly GreenshotColumnSorter _columnSorter; + private static readonly CoreConfiguration CoreConfig = IniConfig.GetIniSection(); - public JiraForm(JiraConnector jiraConnector) { - InitializeComponent(); - Icon = GreenshotResources.GetGreenshotIcon(); - AcceptButton = uploadButton; - CancelButton = cancelButton; + public JiraForm(JiraConnector jiraConnector) + { + InitializeComponent(); + Icon = GreenshotResources.GetGreenshotIcon(); + AcceptButton = uploadButton; + CancelButton = cancelButton; - InitializeComponentText(); + InitializeComponentText(); - _columnSorter = new GreenshotColumnSorter(); - jiraListView.ListViewItemSorter = _columnSorter; + _columnSorter = new GreenshotColumnSorter(); + jiraListView.ListViewItemSorter = _columnSorter; - _jiraConnector = jiraConnector; + _jiraConnector = jiraConnector; - ChangeModus(false); + ChangeModus(false); - uploadButton.Enabled = false; - Load += OnLoad; - } + uploadButton.Enabled = false; + Load += OnLoad; + } - private async void OnLoad(object sender, EventArgs eventArgs) - { - try - { - if (!_jiraConnector.IsLoggedIn) - { - await _jiraConnector.LoginAsync(); - } - } - catch (Exception e) - { - MessageBox.Show(Language.GetFormattedString("jira", LangKey.login_error, e.Message)); - } - if (_jiraConnector.IsLoggedIn) - { - var filters = await _jiraConnector.GetFavoriteFiltersAsync(); - if (filters.Count > 0) - { - foreach (var filter in filters) - { - jiraFilterBox.Items.Add(filter); - } - jiraFilterBox.SelectedIndex = 0; - } - ChangeModus(true); - if (_jiraConnector.Monitor.RecentJiras.Any()) - { - _selectedIssue = _jiraConnector.Monitor.RecentJiras.First().JiraIssue; - jiraKey.Text = _selectedIssue.Key; - uploadButton.Enabled = true; - } - } - } + private async void OnLoad(object sender, EventArgs eventArgs) + { + try + { + if (!_jiraConnector.IsLoggedIn) + { + await _jiraConnector.LoginAsync(); + } + } + catch (Exception e) + { + MessageBox.Show(Language.GetFormattedString("jira", LangKey.login_error, e.Message)); + } - private void InitializeComponentText() { - label_jirafilter.Text = Language.GetString("jira", LangKey.label_jirafilter); - label_comment.Text = Language.GetString("jira", LangKey.label_comment); - label_filename.Text = Language.GetString("jira", LangKey.label_filename); - } + if (_jiraConnector.IsLoggedIn) + { + var filters = await _jiraConnector.GetFavoriteFiltersAsync(); + if (filters.Count > 0) + { + foreach (var filter in filters) + { + jiraFilterBox.Items.Add(filter); + } - private void ChangeModus(bool enabled) { - jiraFilterBox.Enabled = enabled; - jiraListView.Enabled = enabled; - jiraFilenameBox.Enabled = enabled; - jiraCommentBox.Enabled = enabled; - } + jiraFilterBox.SelectedIndex = 0; + } - public void SetFilename(string filename) { - jiraFilenameBox.Text = filename; - } + ChangeModus(true); + if (_jiraConnector.Monitor.RecentJiras.Any()) + { + _selectedIssue = _jiraConnector.Monitor.RecentJiras.First().JiraIssue; + jiraKey.Text = _selectedIssue.Key; + uploadButton.Enabled = true; + } + } + } - public Issue GetJiraIssue() { - return _selectedIssue; - } + private void InitializeComponentText() + { + label_jirafilter.Text = Language.GetString("jira", LangKey.label_jirafilter); + label_comment.Text = Language.GetString("jira", LangKey.label_comment); + label_filename.Text = Language.GetString("jira", LangKey.label_filename); + } - public async Task UploadAsync(IBinaryContainer attachment) { - attachment.Filename = jiraFilenameBox.Text; - await _jiraConnector.AttachAsync(_selectedIssue.Key, attachment); + private void ChangeModus(bool enabled) + { + jiraFilterBox.Enabled = enabled; + jiraListView.Enabled = enabled; + jiraFilenameBox.Enabled = enabled; + jiraCommentBox.Enabled = enabled; + } - if (!string.IsNullOrEmpty(jiraCommentBox.Text)) { - await _jiraConnector.AddCommentAsync(_selectedIssue.Key, jiraCommentBox.Text); - } - } + public void SetFilename(string filename) + { + jiraFilenameBox.Text = filename; + } - private async void JiraFilterBox_SelectedIndexChanged(object sender, EventArgs e) { - if (_jiraConnector.IsLoggedIn) { + public Issue GetJiraIssue() + { + return _selectedIssue; + } - uploadButton.Enabled = false; - var filter = (Filter)jiraFilterBox.SelectedItem; - if (filter == null) { - return; - } - IList issues = null; - try - { - issues = await _jiraConnector.SearchAsync(filter); - } - catch (Exception ex) - { - Log.Error(ex); - MessageBox.Show(this, ex.Message, "Error in filter", MessageBoxButtons.OK, MessageBoxIcon.Error); - } + public async Task UploadAsync(IBinaryContainer attachment) + { + attachment.Filename = jiraFilenameBox.Text; + await _jiraConnector.AttachAsync(_selectedIssue.Key, attachment); - jiraListView.Items.Clear(); - if (issues?.Count > 0) { - jiraListView.Columns.Clear(); - LangKey[] columns = { LangKey.column_issueType, LangKey.column_id, LangKey.column_created, LangKey.column_assignee, LangKey.column_reporter, LangKey.column_summary }; - foreach (LangKey column in columns) - { - if (!Language.TryGetString("jira", column, out var translation)) - { - translation = string.Empty; - } - jiraListView.Columns.Add(translation); - } - var scaledIconSize = DpiHelper.ScaleWithDpi(CoreConfig.IconSize, DpiHelper.GetDpi(Handle)); - var imageList = new ImageList { - ImageSize = scaledIconSize - }; - jiraListView.SmallImageList = imageList; - jiraListView.LargeImageList = imageList; + if (!string.IsNullOrEmpty(jiraCommentBox.Text)) + { + await _jiraConnector.AddCommentAsync(_selectedIssue.Key, jiraCommentBox.Text); + } + } - foreach (var issue in issues) { - var item = new ListViewItem - { - 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); - } + private async void JiraFilterBox_SelectedIndexChanged(object sender, EventArgs e) + { + if (_jiraConnector.IsLoggedIn) + { + uploadButton.Enabled = false; + var filter = (Filter) jiraFilterBox.SelectedItem; + if (filter == null) + { + return; + } - item.SubItems.Add(issue.Key); + IList issues = null; + try + { + issues = await _jiraConnector.SearchAsync(filter); + } + catch (Exception ex) + { + Log.Error(ex); + MessageBox.Show(this, ex.Message, "Error in filter", MessageBoxButtons.OK, MessageBoxIcon.Error); + } + + jiraListView.Items.Clear(); + if (issues?.Count > 0) + { + jiraListView.Columns.Clear(); + LangKey[] columns = + { + LangKey.column_issueType, LangKey.column_id, LangKey.column_created, LangKey.column_assignee, LangKey.column_reporter, LangKey.column_summary + }; + foreach (LangKey column in columns) + { + if (!Language.TryGetString("jira", column, out var translation)) + { + translation = string.Empty; + } + + jiraListView.Columns.Add(translation); + } + + var scaledIconSize = DpiHelper.ScaleWithDpi(CoreConfig.IconSize, DpiHelper.GetDpi(Handle)); + var imageList = new ImageList + { + ImageSize = scaledIconSize + }; + jiraListView.SmallImageList = imageList; + jiraListView.LargeImageList = imageList; + + foreach (var issue in issues) + { + var item = new ListViewItem + { + 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.HasValue ? issue.Fields.Created.Value.ToString("d", DateTimeFormatInfo.InvariantInfo) : string.Empty); item.SubItems.Add(issue.Fields.Assignee?.DisplayName); - item.SubItems.Add(issue.Fields.Reporter?.DisplayName); - item.SubItems.Add(issue.Fields.Summary); - jiraListView.Items.Add(item); - for (int i = 0; i < columns.Length; i++) - { - jiraListView.AutoResizeColumn(i, ColumnHeaderAutoResizeStyle.ColumnContent); - } - jiraListView.Invalidate(); - jiraListView.Update(); - } + item.SubItems.Add(issue.Fields.Reporter?.DisplayName); + item.SubItems.Add(issue.Fields.Summary); + jiraListView.Items.Add(item); + for (int i = 0; i < columns.Length; i++) + { + jiraListView.AutoResizeColumn(i, ColumnHeaderAutoResizeStyle.ColumnContent); + } - jiraListView.Refresh(); - } - } - } + jiraListView.Invalidate(); + jiraListView.Update(); + } - private void JiraListView_SelectedIndexChanged(object sender, EventArgs e) { - if (jiraListView.SelectedItems.Count > 0) { - _selectedIssue = (Issue)jiraListView.SelectedItems[0].Tag; - jiraKey.Text = _selectedIssue.Key; - uploadButton.Enabled = true; - } else { - uploadButton.Enabled = false; - } - } + jiraListView.Refresh(); + } + } + } - private void JiraListView_ColumnClick(object sender, ColumnClickEventArgs e) { - // Determine if clicked column is already the column that is being sorted. - if (e.Column == _columnSorter.SortColumn) { - // Reverse the current sort direction for this column. - _columnSorter.Order = _columnSorter.Order == SortOrder.Ascending ? SortOrder.Descending : SortOrder.Ascending; - } else { - // Set the column number that is to be sorted; default to ascending. - _columnSorter.SortColumn = e.Column; - _columnSorter.Order = SortOrder.Ascending; - } + private void JiraListView_SelectedIndexChanged(object sender, EventArgs e) + { + if (jiraListView.SelectedItems.Count > 0) + { + _selectedIssue = (Issue) jiraListView.SelectedItems[0].Tag; + jiraKey.Text = _selectedIssue.Key; + uploadButton.Enabled = true; + } + else + { + uploadButton.Enabled = false; + } + } - // Perform the sort with these new sort options. - jiraListView.Sort(); - } + private void JiraListView_ColumnClick(object sender, ColumnClickEventArgs e) + { + // Determine if clicked column is already the column that is being sorted. + if (e.Column == _columnSorter.SortColumn) + { + // Reverse the current sort direction for this column. + _columnSorter.Order = _columnSorter.Order == SortOrder.Ascending ? SortOrder.Descending : SortOrder.Ascending; + } + else + { + // Set the column number that is to be sorted; default to ascending. + _columnSorter.SortColumn = e.Column; + _columnSorter.Order = SortOrder.Ascending; + } - private async void JiraKeyTextChanged(object sender, EventArgs e) { - string jiranumber = jiraKey.Text; - uploadButton.Enabled = false; - int dashIndex = jiranumber.IndexOf('-'); - if (dashIndex > 0 && jiranumber.Length > dashIndex+1) { - _selectedIssue = await _jiraConnector.GetIssueAsync(jiraKey.Text); - if (_selectedIssue != null) { - uploadButton.Enabled = true; - } - } - } - } -} + // Perform the sort with these new sort options. + jiraListView.Sort(); + } + + private async void JiraKeyTextChanged(object sender, EventArgs e) + { + string jiranumber = jiraKey.Text; + uploadButton.Enabled = false; + int dashIndex = jiranumber.IndexOf('-'); + if (dashIndex > 0 && jiranumber.Length > dashIndex + 1) + { + _selectedIssue = await _jiraConnector.GetIssueAsync(jiraKey.Text); + if (_selectedIssue != null) + { + uploadButton.Enabled = true; + } + } + } + } +} \ No newline at end of file diff --git a/src/Greenshot.Plugin.Jira/Forms/JiraFormBase.cs b/src/Greenshot.Plugin.Jira/Forms/JiraFormBase.cs index caf947779..21ab33509 100644 --- a/src/Greenshot.Plugin.Jira/Forms/JiraFormBase.cs +++ b/src/Greenshot.Plugin.Jira/Forms/JiraFormBase.cs @@ -21,7 +21,9 @@ using GreenshotPlugin.Controls; -namespace Greenshot.Plugin.Jira.Forms { - public class JiraFormBase : GreenshotForm { - } -} +namespace Greenshot.Plugin.Jira.Forms +{ + public class JiraFormBase : GreenshotForm + { + } +} \ No newline at end of file diff --git a/src/Greenshot.Plugin.Jira/Forms/SettingsForm.cs b/src/Greenshot.Plugin.Jira/Forms/SettingsForm.cs index 9871fd74f..9009b70e1 100644 --- a/src/Greenshot.Plugin.Jira/Forms/SettingsForm.cs +++ b/src/Greenshot.Plugin.Jira/Forms/SettingsForm.cs @@ -19,19 +19,21 @@ * along with this program. If not, see . */ -namespace Greenshot.Plugin.Jira.Forms { - /// - /// Description of PasswordRequestForm. - /// - public partial class SettingsForm : JiraFormBase { - public SettingsForm() - { - // - // The InitializeComponent() call is required for Windows Forms designer support. - // - InitializeComponent(); - AcceptButton = buttonOK; - CancelButton = buttonCancel; - } - } -} +namespace Greenshot.Plugin.Jira.Forms +{ + /// + /// Description of PasswordRequestForm. + /// + public partial class SettingsForm : JiraFormBase + { + public SettingsForm() + { + // + // The InitializeComponent() call is required for Windows Forms designer support. + // + InitializeComponent(); + AcceptButton = buttonOK; + CancelButton = buttonCancel; + } + } +} \ No newline at end of file diff --git a/src/Greenshot.Plugin.Jira/IssueTypeBitmapCache.cs b/src/Greenshot.Plugin.Jira/IssueTypeBitmapCache.cs index a1eab6112..26d63e7c0 100644 --- a/src/Greenshot.Plugin.Jira/IssueTypeBitmapCache.cs +++ b/src/Greenshot.Plugin.Jira/IssueTypeBitmapCache.cs @@ -28,28 +28,28 @@ using Dapplo.Jira.Entities; namespace Greenshot.Plugin.Jira { - /// - /// This is the bach for the IssueType bitmaps - /// - public class IssueTypeBitmapCache : AsyncMemoryCache - { - private readonly IJiraClient _jiraClient; + /// + /// This is the bach for the IssueType bitmaps + /// + public class IssueTypeBitmapCache : AsyncMemoryCache + { + private readonly IJiraClient _jiraClient; - public IssueTypeBitmapCache(IJiraClient jiraClient) - { - _jiraClient = jiraClient; - // Set the expire timeout to an hour - ExpireTimeSpan = TimeSpan.FromHours(4); - } + public IssueTypeBitmapCache(IJiraClient jiraClient) + { + _jiraClient = jiraClient; + // Set the expire timeout to an hour + ExpireTimeSpan = TimeSpan.FromHours(4); + } - protected override string CreateKey(IssueType keyObject) - { - return keyObject.Name; - } + protected override string CreateKey(IssueType keyObject) + { + return keyObject.Name; + } - protected override async Task CreateAsync(IssueType issueType, CancellationToken cancellationToken = new CancellationToken()) - { - return await _jiraClient.Server.GetUriContentAsync(issueType.IconUri, cancellationToken).ConfigureAwait(false); - } - } -} + protected override async Task CreateAsync(IssueType issueType, CancellationToken cancellationToken = new CancellationToken()) + { + return await _jiraClient.Server.GetUriContentAsync(issueType.IconUri, cancellationToken).ConfigureAwait(false); + } + } +} \ No newline at end of file diff --git a/src/Greenshot.Plugin.Jira/JiraConfiguration.cs b/src/Greenshot.Plugin.Jira/JiraConfiguration.cs index 50a521bef..cce59453b 100644 --- a/src/Greenshot.Plugin.Jira/JiraConfiguration.cs +++ b/src/Greenshot.Plugin.Jira/JiraConfiguration.cs @@ -22,25 +22,27 @@ using GreenshotPlugin.Core; using GreenshotPlugin.IniFile; -namespace Greenshot.Plugin.Jira { - /// - /// Description of JiraConfiguration. - /// - [IniSection("Jira", Description="Greenshot Jira Plugin configuration")] - public class JiraConfiguration : IniSection { - public const string DefaultPrefix = "http://"; - private const string DefaultUrl = DefaultPrefix + "jira"; +namespace Greenshot.Plugin.Jira +{ + /// + /// Description of JiraConfiguration. + /// + [IniSection("Jira", Description = "Greenshot Jira Plugin configuration")] + public class JiraConfiguration : IniSection + { + public const string DefaultPrefix = "http://"; + private const string DefaultUrl = DefaultPrefix + "jira"; - [IniProperty("Url", Description="Base url to Jira system, without anything else", DefaultValue=DefaultUrl)] - public string Url { get; set; } + [IniProperty("Url", Description = "Base url to Jira system, without anything else", DefaultValue = DefaultUrl)] + public string Url { get; set; } - [IniProperty("UploadFormat", Description="What file type to use for uploading", DefaultValue="png")] - public OutputFormat UploadFormat { get; set; } + [IniProperty("UploadFormat", Description = "What file type to use for uploading", DefaultValue = "png")] + public OutputFormat UploadFormat { get; set; } - [IniProperty("UploadJpegQuality", Description="JPEG file save quality in %.", DefaultValue="80")] - public int UploadJpegQuality { get; set; } + [IniProperty("UploadJpegQuality", Description = "JPEG file save quality in %.", DefaultValue = "80")] + public int UploadJpegQuality { get; set; } - [IniProperty("UploadReduceColors", Description="Reduce color amount of the uploaded image to 256", DefaultValue="False")] - public bool UploadReduceColors { get; set; } - } -} + [IniProperty("UploadReduceColors", Description = "Reduce color amount of the uploaded image to 256", DefaultValue = "False")] + public bool UploadReduceColors { get; set; } + } +} \ No newline at end of file diff --git a/src/Greenshot.Plugin.Jira/JiraConnector.cs b/src/Greenshot.Plugin.Jira/JiraConnector.cs index d16a4b235..e25c4fb75 100644 --- a/src/Greenshot.Plugin.Jira/JiraConnector.cs +++ b/src/Greenshot.Plugin.Jira/JiraConnector.cs @@ -34,247 +34,280 @@ using Dapplo.Jira.SvgWinForms.Converters; using GreenshotPlugin.Core; using GreenshotPlugin.IniFile; -namespace Greenshot.Plugin.Jira { - /// - /// This encapsulates the JiraClient to make it possible to change as less old Greenshot code as needed - /// - public sealed class JiraConnector : IDisposable { - private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(typeof(JiraConnector)); - private static readonly JiraConfiguration JiraConfig = IniConfig.GetIniSection(); - private static readonly CoreConfiguration CoreConfig = IniConfig.GetIniSection(); - // Used to remove the wsdl information from the old SOAP Uri - public const string DefaultPostfix = "/rpc/soap/jirasoapservice-v2?wsdl"; - private IJiraClient _jiraClient; - private IssueTypeBitmapCache _issueTypeBitmapCache; +namespace Greenshot.Plugin.Jira +{ + /// + /// This encapsulates the JiraClient to make it possible to change as less old Greenshot code as needed + /// + public sealed class JiraConnector : IDisposable + { + private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(typeof(JiraConnector)); + private static readonly JiraConfiguration JiraConfig = IniConfig.GetIniSection(); - /// - /// Initialize some basic stuff, in the case the SVG to bitmap converter - /// - static JiraConnector() - { - CoreConfig.PropertyChanged += (sender, args) => - { - if (args.PropertyName == nameof(CoreConfig.IconSize)) + private static readonly CoreConfiguration CoreConfig = IniConfig.GetIniSection(); + + // Used to remove the wsdl information from the old SOAP Uri + public const string DefaultPostfix = "/rpc/soap/jirasoapservice-v2?wsdl"; + private IJiraClient _jiraClient; + private IssueTypeBitmapCache _issueTypeBitmapCache; + + /// + /// Initialize some basic stuff, in the case the SVG to bitmap converter + /// + static JiraConnector() + { + CoreConfig.PropertyChanged += (sender, args) => + { + if (args.PropertyName == nameof(CoreConfig.IconSize)) { var jiraConnector = SimpleServiceProvider.Current.GetInstance(); - jiraConnector._jiraClient?.Behaviour.SetConfig(new SvgConfiguration { Width = CoreConfig.ScaledIconSize.Width, Height = CoreConfig.ScaledIconSize.Height }); - } - }; + jiraConnector._jiraClient?.Behaviour.SetConfig(new SvgConfiguration + { + Width = CoreConfig.ScaledIconSize.Width, + Height = CoreConfig.ScaledIconSize.Height + }); + } + }; + } - } - - /// - /// Dispose, logout the users - /// - public void Dispose() { - if (_jiraClient != null) - { + /// + /// Dispose, logout the users + /// + public void Dispose() + { + if (_jiraClient != null) + { Logout(); - } - FavIcon?.Dispose(); - } + } - /// - /// Constructor - /// - public JiraConnector() - { - JiraConfig.Url = JiraConfig.Url.Replace(DefaultPostfix, string.Empty); - } + FavIcon?.Dispose(); + } - /// - /// Access the jira monitor - /// - public JiraMonitor Monitor { get; private set; } + /// + /// Constructor + /// + public JiraConnector() + { + JiraConfig.Url = JiraConfig.Url.Replace(DefaultPostfix, string.Empty); + } - public Bitmap FavIcon { get; private set; } + /// + /// Access the jira monitor + /// + public JiraMonitor Monitor { get; private set; } - /// - /// Internal login which catches the exceptions - /// - /// true if login was done successfully - private async Task DoLoginAsync(string user, string password, CancellationToken cancellationToken = default) - { - if (string.IsNullOrEmpty(user) || string.IsNullOrEmpty(password)) - { - return false; - } - _jiraClient = JiraClient.Create(new Uri(JiraConfig.Url)); - _jiraClient.Behaviour.SetConfig(new SvgConfiguration { Width = CoreConfig.ScaledIconSize.Width, Height = CoreConfig.ScaledIconSize.Height }); + public Bitmap FavIcon { get; private set; } + + /// + /// Internal login which catches the exceptions + /// + /// true if login was done successfully + private async Task DoLoginAsync(string user, string password, CancellationToken cancellationToken = default) + { + if (string.IsNullOrEmpty(user) || string.IsNullOrEmpty(password)) + { + return false; + } + + _jiraClient = JiraClient.Create(new Uri(JiraConfig.Url)); + _jiraClient.Behaviour.SetConfig(new SvgConfiguration + { + Width = CoreConfig.ScaledIconSize.Width, + Height = CoreConfig.ScaledIconSize.Height + }); _jiraClient.SetBasicAuthentication(user, password); - _issueTypeBitmapCache = new IssueTypeBitmapCache(_jiraClient); - try - { - Monitor = new JiraMonitor(); - await Monitor.AddJiraInstanceAsync(_jiraClient, cancellationToken); + _issueTypeBitmapCache = new IssueTypeBitmapCache(_jiraClient); + try + { + Monitor = new JiraMonitor(); + await Monitor.AddJiraInstanceAsync(_jiraClient, cancellationToken); - var favIconUri = _jiraClient.JiraBaseUri.AppendSegments("favicon.ico"); - try - { - FavIcon = await _jiraClient.Server.GetUriContentAsync(favIconUri, cancellationToken); - } - catch (Exception ex) - { - Log.WarnFormat("Couldn't load favicon from {0}", favIconUri); - Log.Warn("Exception details: ", ex); - } - } - catch (Exception ex2) - { + var favIconUri = _jiraClient.JiraBaseUri.AppendSegments("favicon.ico"); + try + { + FavIcon = await _jiraClient.Server.GetUriContentAsync(favIconUri, cancellationToken); + } + catch (Exception ex) + { + Log.WarnFormat("Couldn't load favicon from {0}", favIconUri); + Log.Warn("Exception details: ", ex); + } + } + catch (Exception ex2) + { Log.WarnFormat("Couldn't connect to JIRA {0}", JiraConfig.Url); Log.Warn("Exception details: ", ex2); - return false; - } - return true; - } + return false; + } - /// - /// Use the credentials dialog, this will show if there are not correct credentials. - /// If there are credentials, call the real login. - /// - /// Task - public async Task LoginAsync(CancellationToken cancellationToken = default) { - Logout(); - try { - // Get the system name, so the user knows where to login to - var credentialsDialog = new CredentialsDialog(JiraConfig.Url) - { - Name = null - }; - while (credentialsDialog.Show(credentialsDialog.Name) == DialogResult.OK) { - if (await DoLoginAsync(credentialsDialog.Name, credentialsDialog.Password, cancellationToken)) { - if (credentialsDialog.SaveChecked) { - credentialsDialog.Confirm(true); - } - IsLoggedIn = true; - return; - } - // Login failed, confirm this - try { - credentialsDialog.Confirm(false); - } catch (ApplicationException e) { - // exception handling ... - Log.Error("Problem using the credentials dialog", e); - } - // For every windows version after XP show an incorrect password baloon - credentialsDialog.IncorrectPassword = true; - // Make sure the dialog is display, the password was false! - credentialsDialog.AlwaysDisplay = true; - } - } catch (ApplicationException e) { - // exception handling ... - Log.Error("Problem using the credentials dialog", e); - } + return true; + } - } + /// + /// Use the credentials dialog, this will show if there are not correct credentials. + /// If there are credentials, call the real login. + /// + /// Task + public async Task LoginAsync(CancellationToken cancellationToken = default) + { + Logout(); + try + { + // Get the system name, so the user knows where to login to + var credentialsDialog = new CredentialsDialog(JiraConfig.Url) + { + Name = null + }; + while (credentialsDialog.Show(credentialsDialog.Name) == DialogResult.OK) + { + if (await DoLoginAsync(credentialsDialog.Name, credentialsDialog.Password, cancellationToken)) + { + if (credentialsDialog.SaveChecked) + { + credentialsDialog.Confirm(true); + } - /// - /// End the session, if there was one - /// - public void Logout() { + IsLoggedIn = true; + return; + } + + // Login failed, confirm this + try + { + credentialsDialog.Confirm(false); + } + catch (ApplicationException e) + { + // exception handling ... + Log.Error("Problem using the credentials dialog", e); + } + + // For every windows version after XP show an incorrect password baloon + credentialsDialog.IncorrectPassword = true; + // Make sure the dialog is display, the password was false! + credentialsDialog.AlwaysDisplay = true; + } + } + catch (ApplicationException e) + { + // exception handling ... + Log.Error("Problem using the credentials dialog", e); + } + } + + /// + /// End the session, if there was one + /// + public void Logout() + { if (_jiraClient == null || !IsLoggedIn) return; Monitor.Dispose(); IsLoggedIn = false; } - /// - /// check the login credentials, to prevent timeouts of the session, or makes a login - /// Do not use ConfigureAwait to call this, as it will move await from the UI thread. - /// - /// - private async Task CheckCredentialsAsync(CancellationToken cancellationToken = default) { - if (!IsLoggedIn) { - await LoginAsync(cancellationToken); - } - } + /// + /// check the login credentials, to prevent timeouts of the session, or makes a login + /// Do not use ConfigureAwait to call this, as it will move await from the UI thread. + /// + /// + private async Task CheckCredentialsAsync(CancellationToken cancellationToken = default) + { + if (!IsLoggedIn) + { + await LoginAsync(cancellationToken); + } + } - /// - /// Get the favorite filters - /// - /// List with filters - public async Task> GetFavoriteFiltersAsync(CancellationToken cancellationToken = default) - { - await CheckCredentialsAsync(cancellationToken); - return await _jiraClient.Filter.GetFavoritesAsync(cancellationToken).ConfigureAwait(false); - } + /// + /// Get the favorite filters + /// + /// List with filters + public async Task> GetFavoriteFiltersAsync(CancellationToken cancellationToken = default) + { + 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, CancellationToken cancellationToken = default) - { - await CheckCredentialsAsync(cancellationToken); - try - { - return await _jiraClient.Issue.GetAsync(issueKey, cancellationToken: cancellationToken).ConfigureAwait(false); - } - catch - { - return null; - } - } + /// + /// Get the issue for a key + /// + /// Jira issue key + /// CancellationToken + /// Issue + public async Task GetIssueAsync(string issueKey, CancellationToken cancellationToken = default) + { + await CheckCredentialsAsync(cancellationToken); + try + { + return await _jiraClient.Issue.GetAsync(issueKey, cancellationToken: cancellationToken).ConfigureAwait(false); + } + catch + { + return null; + } + } - /// - /// Attach the content to the jira - /// - /// - /// IBinaryContainer - /// - /// - public async Task AttachAsync(string issueKey, IBinaryContainer content, CancellationToken cancellationToken = default) - { - await CheckCredentialsAsync(cancellationToken); + /// + /// Attach the content to the jira + /// + /// + /// IBinaryContainer + /// + /// + public async Task AttachAsync(string issueKey, IBinaryContainer content, CancellationToken cancellationToken = default) + { + await CheckCredentialsAsync(cancellationToken); using var memoryStream = new MemoryStream(); content.WriteToStream(memoryStream); memoryStream.Seek(0, SeekOrigin.Begin); await _jiraClient.Attachment.AttachAsync(issueKey, memoryStream, content.Filename, content.ContentType, cancellationToken).ConfigureAwait(false); } - /// - /// Add a comment to the supplied issue - /// - /// Jira issue key - /// text - /// the visibility role - /// CancellationToken - public async Task AddCommentAsync(string issueKey, string body, Visibility visibility = null, CancellationToken cancellationToken = default) - { - await CheckCredentialsAsync(cancellationToken); - await _jiraClient.Issue.AddCommentAsync(issueKey, body, visibility, cancellationToken).ConfigureAwait(false); - } + /// + /// Add a comment to the supplied issue + /// + /// Jira issue key + /// text + /// the visibility role + /// CancellationToken + public async Task AddCommentAsync(string issueKey, string body, Visibility visibility = null, CancellationToken cancellationToken = default) + { + await CheckCredentialsAsync(cancellationToken); + await _jiraClient.Issue.AddCommentAsync(issueKey, body, visibility, cancellationToken).ConfigureAwait(false); + } - /// - /// Get the search results for the specified filter - /// - /// Filter - /// - /// - public async Task> SearchAsync(Filter filter, CancellationToken cancellationToken = default) - { - await CheckCredentialsAsync(cancellationToken); - var searchResult = await _jiraClient.Issue.SearchAsync(filter.Jql, null, new[] { "summary", "reporter", "assignee", "created", "issuetype" }, null, cancellationToken).ConfigureAwait(false); - return searchResult.Issues; - } + /// + /// Get the search results for the specified filter + /// + /// Filter + /// + /// + public async Task> SearchAsync(Filter filter, CancellationToken cancellationToken = default) + { + await CheckCredentialsAsync(cancellationToken); + var searchResult = await _jiraClient.Issue.SearchAsync(filter.Jql, null, new[] + { + "summary", "reporter", "assignee", "created", "issuetype" + }, null, cancellationToken).ConfigureAwait(false); + return searchResult.Issues; + } - /// - /// Get the bitmap representing the issue type of an issue, from cache. - /// - /// Issue - /// CancellationToken - /// Bitmap - public async Task GetIssueTypeBitmapAsync(Issue issue, CancellationToken cancellationToken = default) - { - return await _issueTypeBitmapCache.GetOrCreateAsync(issue.Fields.IssueType, cancellationToken).ConfigureAwait(false); - } + /// + /// Get the bitmap representing the issue type of an issue, from cache. + /// + /// Issue + /// CancellationToken + /// Bitmap + public async Task GetIssueTypeBitmapAsync(Issue issue, CancellationToken cancellationToken = default) + { + return await _issueTypeBitmapCache.GetOrCreateAsync(issue.Fields.IssueType, cancellationToken).ConfigureAwait(false); + } - /// - /// Get the base uri - /// - public Uri JiraBaseUri => _jiraClient.JiraBaseUri; + /// + /// Get the base uri + /// + public Uri JiraBaseUri => _jiraClient.JiraBaseUri; /// /// Is the user "logged in? diff --git a/src/Greenshot.Plugin.Jira/JiraDestination.cs b/src/Greenshot.Plugin.Jira/JiraDestination.cs index 9bc66dd1b..2dd871cf3 100644 --- a/src/Greenshot.Plugin.Jira/JiraDestination.cs +++ b/src/Greenshot.Plugin.Jira/JiraDestination.cs @@ -34,127 +34,148 @@ using GreenshotPlugin.IniFile; using GreenshotPlugin.Interfaces; using GreenshotPlugin.Interfaces.Plugin; -namespace Greenshot.Plugin.Jira { - /// - /// Description of JiraDestination. - /// - public class JiraDestination : AbstractDestination { - private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(typeof(JiraDestination)); - private static readonly JiraConfiguration Config = IniConfig.GetIniSection(); - private readonly Issue _jiraIssue; - - public JiraDestination(Issue jiraIssue = null) { +namespace Greenshot.Plugin.Jira +{ + /// + /// Description of JiraDestination. + /// + public class JiraDestination : AbstractDestination + { + private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(typeof(JiraDestination)); + private static readonly JiraConfiguration Config = IniConfig.GetIniSection(); + private readonly Issue _jiraIssue; + + public JiraDestination(Issue jiraIssue = null) + { _jiraIssue = jiraIssue; - } + } - public override string Designation => "Jira"; + public override string Designation => "Jira"; - public override string Description { - get - { - if (_jiraIssue?.Fields?.Summary == null) { - return Language.GetString("jira", LangKey.upload_menu_item); - } - // Format the title of this destination - return _jiraIssue.Key + ": " + _jiraIssue.Fields.Summary.Substring(0, Math.Min(20, _jiraIssue.Fields.Summary.Length)); - } - } - - public override bool IsActive => base.IsActive && !string.IsNullOrEmpty(Config.Url); + public override string Description + { + get + { + if (_jiraIssue?.Fields?.Summary == null) + { + return Language.GetString("jira", LangKey.upload_menu_item); + } - public override bool IsDynamic => true; + // Format the title of this destination + return _jiraIssue.Key + ": " + _jiraIssue.Fields.Summary.Substring(0, Math.Min(20, _jiraIssue.Fields.Summary.Length)); + } + } - public override Image DisplayIcon { - get - { - Image displayIcon = null; + public override bool IsActive => base.IsActive && !string.IsNullOrEmpty(Config.Url); + + public override bool IsDynamic => true; + + public override Image DisplayIcon + { + get + { + Image displayIcon = null; var jiraConnector = SimpleServiceProvider.Current.GetInstance(); - if (jiraConnector != null) - { - if (_jiraIssue != null) - { - // 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); - } - } - if (displayIcon == null) - { - displayIcon = jiraConnector.FavIcon; - } - } - if (displayIcon == null) - { - var resources = new ComponentResourceManager(typeof(JiraPlugin)); - displayIcon = (Image)resources.GetObject("Jira"); - } - return displayIcon; - } - } + if (jiraConnector != null) + { + if (_jiraIssue != null) + { + // 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); + } + } - public override IEnumerable DynamicDestinations() - { - var jiraConnector = SimpleServiceProvider.Current.GetInstance(); - if (jiraConnector == null || !jiraConnector.IsLoggedIn) { - yield break; - } - foreach(var jiraDetails in jiraConnector.Monitor.RecentJiras) - { - yield return new JiraDestination(jiraDetails.JiraIssue); - } - } + if (displayIcon == null) + { + displayIcon = jiraConnector.FavIcon; + } + } - public override ExportInformation ExportCapture(bool manuallyInitiated, ISurface surfaceToUpload, ICaptureDetails captureDetails) { - ExportInformation exportInformation = new ExportInformation(Designation, Description); - string filename = Path.GetFileName(FilenameHelper.GetFilename(Config.UploadFormat, captureDetails)); - SurfaceOutputSettings outputSettings = new SurfaceOutputSettings(Config.UploadFormat, Config.UploadJpegQuality, Config.UploadReduceColors); + if (displayIcon == null) + { + var resources = new ComponentResourceManager(typeof(JiraPlugin)); + displayIcon = (Image) resources.GetObject("Jira"); + } + + return displayIcon; + } + } + + public override IEnumerable DynamicDestinations() + { var jiraConnector = SimpleServiceProvider.Current.GetInstance(); - if (_jiraIssue != null) { - try { - // Run upload in the background - new PleaseWaitForm().ShowAndWait(Description, Language.GetString("jira", LangKey.communication_wait), - async () => - { - var surfaceContainer = new SurfaceContainer(surfaceToUpload, outputSettings, filename); - await jiraConnector.AttachAsync(_jiraIssue.Key, surfaceContainer); - surfaceToUpload.UploadUrl = jiraConnector.JiraBaseUri.AppendSegments("browse", _jiraIssue.Key).AbsoluteUri; - } - ); - Log.DebugFormat("Uploaded to Jira {0}", _jiraIssue.Key); - exportInformation.ExportMade = true; - exportInformation.Uri = surfaceToUpload.UploadUrl; - } catch (Exception e) { - MessageBox.Show(Language.GetString("jira", LangKey.upload_failure) + " " + e.Message); - } - } else { - var jiraForm = new JiraForm(jiraConnector); - jiraForm.SetFilename(filename); - var dialogResult = jiraForm.ShowDialog(); - if (dialogResult == DialogResult.OK) { - try { - surfaceToUpload.UploadUrl = jiraConnector.JiraBaseUri.AppendSegments("browse", jiraForm.GetJiraIssue().Key).AbsoluteUri; - // Run upload in the background - new PleaseWaitForm().ShowAndWait(Description, Language.GetString("jira", LangKey.communication_wait), - async () => - { - await jiraForm.UploadAsync(new SurfaceContainer(surfaceToUpload, outputSettings, filename)); - } - ); - Log.DebugFormat("Uploaded to Jira {0}", jiraForm.GetJiraIssue().Key); - exportInformation.ExportMade = true; - exportInformation.Uri = surfaceToUpload.UploadUrl; - } catch(Exception e) { - MessageBox.Show(Language.GetString("jira", LangKey.upload_failure) + " " + e.Message); - } - } - } - ProcessExport(exportInformation, surfaceToUpload); - return exportInformation; - } - } -} + if (jiraConnector == null || !jiraConnector.IsLoggedIn) + { + yield break; + } + + foreach (var jiraDetails in jiraConnector.Monitor.RecentJiras) + { + yield return new JiraDestination(jiraDetails.JiraIssue); + } + } + + public override ExportInformation ExportCapture(bool manuallyInitiated, ISurface surfaceToUpload, ICaptureDetails captureDetails) + { + ExportInformation exportInformation = new ExportInformation(Designation, Description); + string filename = Path.GetFileName(FilenameHelper.GetFilename(Config.UploadFormat, captureDetails)); + SurfaceOutputSettings outputSettings = new SurfaceOutputSettings(Config.UploadFormat, Config.UploadJpegQuality, Config.UploadReduceColors); + var jiraConnector = SimpleServiceProvider.Current.GetInstance(); + if (_jiraIssue != null) + { + try + { + // Run upload in the background + new PleaseWaitForm().ShowAndWait(Description, Language.GetString("jira", LangKey.communication_wait), + async () => + { + var surfaceContainer = new SurfaceContainer(surfaceToUpload, outputSettings, filename); + await jiraConnector.AttachAsync(_jiraIssue.Key, surfaceContainer); + surfaceToUpload.UploadUrl = jiraConnector.JiraBaseUri.AppendSegments("browse", _jiraIssue.Key).AbsoluteUri; + } + ); + Log.DebugFormat("Uploaded to Jira {0}", _jiraIssue.Key); + exportInformation.ExportMade = true; + exportInformation.Uri = surfaceToUpload.UploadUrl; + } + catch (Exception e) + { + MessageBox.Show(Language.GetString("jira", LangKey.upload_failure) + " " + e.Message); + } + } + else + { + var jiraForm = new JiraForm(jiraConnector); + jiraForm.SetFilename(filename); + var dialogResult = jiraForm.ShowDialog(); + if (dialogResult == DialogResult.OK) + { + try + { + surfaceToUpload.UploadUrl = jiraConnector.JiraBaseUri.AppendSegments("browse", jiraForm.GetJiraIssue().Key).AbsoluteUri; + // Run upload in the background + new PleaseWaitForm().ShowAndWait(Description, Language.GetString("jira", LangKey.communication_wait), + async () => { await jiraForm.UploadAsync(new SurfaceContainer(surfaceToUpload, outputSettings, filename)); } + ); + Log.DebugFormat("Uploaded to Jira {0}", jiraForm.GetJiraIssue().Key); + exportInformation.ExportMade = true; + exportInformation.Uri = surfaceToUpload.UploadUrl; + } + catch (Exception e) + { + MessageBox.Show(Language.GetString("jira", LangKey.upload_failure) + " " + e.Message); + } + } + } + + ProcessExport(exportInformation, surfaceToUpload); + return exportInformation; + } + } +} \ No newline at end of file diff --git a/src/Greenshot.Plugin.Jira/JiraDetails.cs b/src/Greenshot.Plugin.Jira/JiraDetails.cs index c0ce97a50..d3ebf0255 100644 --- a/src/Greenshot.Plugin.Jira/JiraDetails.cs +++ b/src/Greenshot.Plugin.Jira/JiraDetails.cs @@ -24,48 +24,28 @@ using Dapplo.Jira.Entities; namespace Greenshot.Plugin.Jira { - public class JiraDetails : IComparable - { - public JiraDetails() - { - FirstSeenAt = SeenAt = DateTimeOffset.Now; - } + public class JiraDetails : IComparable + { + public JiraDetails() + { + FirstSeenAt = SeenAt = DateTimeOffset.Now; + } - public string ProjectKey - { - get; - set; - } + public string ProjectKey { get; set; } - public string Id - { - get; - set; - } + public string Id { get; set; } - public string JiraKey => ProjectKey + "-" + Id; + public string JiraKey => ProjectKey + "-" + Id; - public Issue JiraIssue - { - get; - set; - } + public Issue JiraIssue { get; set; } - public DateTimeOffset FirstSeenAt - { - get; - private set; - } + public DateTimeOffset FirstSeenAt { get; private set; } - public DateTimeOffset SeenAt - { - get; - set; - } + public DateTimeOffset SeenAt { get; set; } - public int CompareTo(JiraDetails other) - { - return SeenAt.CompareTo(other.SeenAt); - } - } + public int CompareTo(JiraDetails other) + { + return SeenAt.CompareTo(other.SeenAt); + } + } } \ No newline at end of file diff --git a/src/Greenshot.Plugin.Jira/JiraEventArgs.cs b/src/Greenshot.Plugin.Jira/JiraEventArgs.cs index a3b5bdeb9..203194fca 100644 --- a/src/Greenshot.Plugin.Jira/JiraEventArgs.cs +++ b/src/Greenshot.Plugin.Jira/JiraEventArgs.cs @@ -23,18 +23,10 @@ using System; namespace Greenshot.Plugin.Jira { - public class JiraEventArgs : EventArgs - { - public JiraEventTypes EventType - { - get; - set; - } + public class JiraEventArgs : EventArgs + { + public JiraEventTypes EventType { get; set; } - public JiraDetails Details - { - get; - set; - } - } -} + public JiraDetails Details { get; set; } + } +} \ No newline at end of file diff --git a/src/Greenshot.Plugin.Jira/JiraEventTypes.cs b/src/Greenshot.Plugin.Jira/JiraEventTypes.cs index 704fa182a..3e3c80093 100644 --- a/src/Greenshot.Plugin.Jira/JiraEventTypes.cs +++ b/src/Greenshot.Plugin.Jira/JiraEventTypes.cs @@ -21,9 +21,9 @@ namespace Greenshot.Plugin.Jira { - public enum JiraEventTypes - { - OrderChanged, - DetectedNewJiraIssue - } + public enum JiraEventTypes + { + OrderChanged, + DetectedNewJiraIssue + } } \ No newline at end of file diff --git a/src/Greenshot.Plugin.Jira/JiraMonitor.cs b/src/Greenshot.Plugin.Jira/JiraMonitor.cs index 9a4ff1c7e..43a5e5487 100644 --- a/src/Greenshot.Plugin.Jira/JiraMonitor.cs +++ b/src/Greenshot.Plugin.Jira/JiraMonitor.cs @@ -31,188 +31,203 @@ using GreenshotPlugin.Hooking; namespace Greenshot.Plugin.Jira { - /// /// This class will monitor all _jira activity by registering for title changes /// It keeps a list of the last "accessed" jiras, and makes it easy to upload to one. /// Make sure this is instanciated on the UI thread! /// public class JiraMonitor : IDisposable - { - 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 _projectJiraClientMap = new Dictionary(); - private readonly int _maxEntries; - // TODO: Add issues from issueHistory (JQL -> Where.IssueKey.InIssueHistory()) - private IDictionary _recentJiras = new Dictionary(); + { + 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 _projectJiraClientMap = new Dictionary(); - /// - /// Register to this event to get events when new jira issues are detected - /// - public event EventHandler JiraEvent; + private readonly int _maxEntries; - public JiraMonitor(int maxEntries = 40) - { - _maxEntries = maxEntries; - _monitor = new WindowsTitleMonitor(); - _monitor.TitleChangeEvent += MonitorTitleChangeEvent; - } + // TODO: Add issues from issueHistory (JQL -> Where.IssueKey.InIssueHistory()) + private IDictionary _recentJiras = new Dictionary(); /// - /// Dispose - /// - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } + /// Register to this event to get events when new jira issues are detected + /// + public event EventHandler JiraEvent; - /// - /// Dispose all managed resources - /// - /// when true is passed all managed resources are disposed. - protected void Dispose(bool disposing) - { - if (!disposing) - { - return; - } - // free managed resources - _monitor.TitleChangeEvent -= MonitorTitleChangeEvent; - _monitor.Dispose(); - // free native resources if there are any. - } + public JiraMonitor(int maxEntries = 40) + { + _maxEntries = maxEntries; + _monitor = new WindowsTitleMonitor(); + _monitor.TitleChangeEvent += MonitorTitleChangeEvent; + } /// - /// Retrieve the API belonging to a JiraDetails - /// - /// - /// IJiraClient - public IJiraClient GetJiraClientForKey(JiraDetails jiraDetails) - { - return _projectJiraClientMap[jiraDetails.ProjectKey]; - } + /// Dispose + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } - /// - /// Get the "list" of recently seen Jiras - /// - public IEnumerable RecentJiras => - (from jiraDetails in _recentJiras.Values - orderby jiraDetails.SeenAt descending - select jiraDetails); + /// + /// Dispose all managed resources + /// + /// when true is passed all managed resources are disposed. + protected void Dispose(bool disposing) + { + if (!disposing) + { + return; + } - /// - /// Check if this monitor has active instances - /// - public bool HasJiraInstances => _jiraInstances.Count > 0; + // free managed resources + _monitor.TitleChangeEvent -= MonitorTitleChangeEvent; + _monitor.Dispose(); + // free native resources if there are any. + } - /// - /// Add an instance of a JIRA system - /// - /// IJiraClient - /// CancellationToken - public async Task AddJiraInstanceAsync(IJiraClient jiraInstance, CancellationToken token = default) - { - _jiraInstances.Add(jiraInstance); - var projects = await jiraInstance.Project.GetAllAsync(cancellationToken: token).ConfigureAwait(false); - if (projects != null) - { - foreach (var project in projects) - { - if (!_projectJiraClientMap.ContainsKey(project.Key)) - { - _projectJiraClientMap.Add(project.Key, jiraInstance); - } - } - } - } + /// + /// Retrieve the API belonging to a JiraDetails + /// + /// + /// IJiraClient + public IJiraClient GetJiraClientForKey(JiraDetails jiraDetails) + { + return _projectJiraClientMap[jiraDetails.ProjectKey]; + } - /// - /// This method will update details, like the title, and send an event to registed listeners of the JiraEvent - /// - /// Contains the jira key to retrieve the title (XYZ-1234) - /// Task - private async Task DetectedNewJiraIssueAsync(JiraDetails jiraDetails) - { - try - { + /// + /// Get the "list" of recently seen Jiras + /// + public IEnumerable RecentJiras => + (from jiraDetails in _recentJiras.Values + orderby jiraDetails.SeenAt descending + select jiraDetails); + + /// + /// Check if this monitor has active instances + /// + public bool HasJiraInstances => _jiraInstances.Count > 0; + + /// + /// Add an instance of a JIRA system + /// + /// IJiraClient + /// CancellationToken + public async Task AddJiraInstanceAsync(IJiraClient jiraInstance, CancellationToken token = default) + { + _jiraInstances.Add(jiraInstance); + var projects = await jiraInstance.Project.GetAllAsync(cancellationToken: token).ConfigureAwait(false); + if (projects != null) + { + foreach (var project in projects) + { + if (!_projectJiraClientMap.ContainsKey(project.Key)) + { + _projectJiraClientMap.Add(project.Key, jiraInstance); + } + } + } + } + + /// + /// This method will update details, like the title, and send an event to registed listeners of the JiraEvent + /// + /// Contains the jira key to retrieve the title (XYZ-1234) + /// Task + private async Task DetectedNewJiraIssueAsync(JiraDetails jiraDetails) + { + try + { if (_projectJiraClientMap.TryGetValue(jiraDetails.ProjectKey, out var jiraClient)) - { - var issue = await jiraClient.Issue.GetAsync(jiraDetails.JiraKey).ConfigureAwait(false); - jiraDetails.JiraIssue = issue; - } - // Send event - JiraEvent?.Invoke(this, new JiraEventArgs { Details = jiraDetails, EventType = JiraEventTypes.DetectedNewJiraIssue }); - } - catch (Exception ex) - { - Log.Warn().WriteLine("Couldn't retrieve JIRA title: {0}", ex.Message); - } - } + { + var issue = await jiraClient.Issue.GetAsync(jiraDetails.JiraKey).ConfigureAwait(false); + jiraDetails.JiraIssue = issue; + } - /// - /// Handle title changes, check for JIRA - /// - /// - private void MonitorTitleChangeEvent(TitleChangeEventArgs eventArgs) - { - string windowTitle = eventArgs.Title; - if (string.IsNullOrEmpty(windowTitle)) - { - return; - } - var jiraKeyMatch = _jiraKeyPattern.Match(windowTitle); - if (!jiraKeyMatch.Success) - { - return; - } - // Found a possible JIRA title - var jiraKey = jiraKeyMatch.Value; - var jiraKeyParts = jiraKey.Split('-'); - var projectKey = jiraKeyParts[0]; - var jiraId = jiraKeyParts[1]; + // Send event + JiraEvent?.Invoke(this, new JiraEventArgs + { + Details = jiraDetails, + EventType = JiraEventTypes.DetectedNewJiraIssue + }); + } + catch (Exception ex) + { + Log.Warn().WriteLine("Couldn't retrieve JIRA title: {0}", ex.Message); + } + } + + /// + /// Handle title changes, check for JIRA + /// + /// + private void MonitorTitleChangeEvent(TitleChangeEventArgs eventArgs) + { + string windowTitle = eventArgs.Title; + if (string.IsNullOrEmpty(windowTitle)) + { + return; + } + + var jiraKeyMatch = _jiraKeyPattern.Match(windowTitle); + if (!jiraKeyMatch.Success) + { + return; + } + + // Found a possible JIRA title + var jiraKey = jiraKeyMatch.Value; + var jiraKeyParts = jiraKey.Split('-'); + var projectKey = jiraKeyParts[0]; + var jiraId = jiraKeyParts[1]; // Check if we have a JIRA instance with a project for this key - if (_projectJiraClientMap.TryGetValue(projectKey, out var jiraClient)) - { - // We have found a project for this _jira key, so it must be a valid & known JIRA + if (_projectJiraClientMap.TryGetValue(projectKey, out var jiraClient)) + { + // We have found a project for this _jira key, so it must be a valid & known JIRA if (_recentJiras.TryGetValue(jiraKey, out var currentJiraDetails)) - { - // update - currentJiraDetails.SeenAt = DateTimeOffset.Now; + { + // update + currentJiraDetails.SeenAt = DateTimeOffset.Now; - // Notify the order change - JiraEvent?.Invoke(this, new JiraEventArgs { Details = currentJiraDetails, EventType = JiraEventTypes.OrderChanged }); - // Nothing else to do + // Notify the order change + JiraEvent?.Invoke(this, new JiraEventArgs + { + Details = currentJiraDetails, + EventType = JiraEventTypes.OrderChanged + }); + // Nothing else to do - return; - } - // We detected an unknown JIRA, so add it to our list - currentJiraDetails = new JiraDetails - { - Id = jiraId, - ProjectKey = projectKey - }; - _recentJiras.Add(currentJiraDetails.JiraKey, currentJiraDetails); + return; + } - // Make sure we don't collect _jira's until the memory is full - if (_recentJiras.Count > _maxEntries) - { - // Add it to the list of recent Jiras - _recentJiras = (from jiraDetails in _recentJiras.Values.ToList() - orderby jiraDetails.SeenAt descending - select jiraDetails).Take(_maxEntries).ToDictionary(jd => jd.JiraKey, jd => jd); - } - // Now we can get the title from JIRA itself - // ReSharper disable once UnusedVariable - var updateTitleTask = DetectedNewJiraIssueAsync(currentJiraDetails); - } - else - { - Log.Info().WriteLine("Couldn't match possible JIRA key {0} to projects in a configured JIRA instance, ignoring", projectKey); - } - } - } + // We detected an unknown JIRA, so add it to our list + currentJiraDetails = new JiraDetails + { + Id = jiraId, + ProjectKey = projectKey + }; + _recentJiras.Add(currentJiraDetails.JiraKey, currentJiraDetails); + + // Make sure we don't collect _jira's until the memory is full + if (_recentJiras.Count > _maxEntries) + { + // Add it to the list of recent Jiras + _recentJiras = (from jiraDetails in _recentJiras.Values.ToList() + orderby jiraDetails.SeenAt descending + select jiraDetails).Take(_maxEntries).ToDictionary(jd => jd.JiraKey, jd => jd); + } + + // Now we can get the title from JIRA itself + // ReSharper disable once UnusedVariable + var updateTitleTask = DetectedNewJiraIssueAsync(currentJiraDetails); + } + else + { + Log.Info().WriteLine("Couldn't match possible JIRA key {0} to projects in a configured JIRA instance, ignoring", projectKey); + } + } + } } \ No newline at end of file diff --git a/src/Greenshot.Plugin.Jira/JiraPlugin.cs b/src/Greenshot.Plugin.Jira/JiraPlugin.cs index 8ab273768..e00f0263d 100644 --- a/src/Greenshot.Plugin.Jira/JiraPlugin.cs +++ b/src/Greenshot.Plugin.Jira/JiraPlugin.cs @@ -30,109 +30,117 @@ using GreenshotPlugin.Interfaces; using GreenshotPlugin.Interfaces.Plugin; using log4net; -namespace Greenshot.Plugin.Jira { - /// - /// This is the JiraPlugin base code - /// +namespace Greenshot.Plugin.Jira +{ + /// + /// This is the JiraPlugin base code + /// [Plugin("Jira", true)] - public class JiraPlugin : IGreenshotPlugin { - private static readonly ILog Log = LogManager.GetLogger(typeof(JiraPlugin)); - private JiraConfiguration _config; + public class JiraPlugin : IGreenshotPlugin + { + private static readonly ILog Log = LogManager.GetLogger(typeof(JiraPlugin)); + private JiraConfiguration _config; - public void Dispose() { - Dispose(true); - GC.SuppressFinalize(this); - } + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } - protected void Dispose(bool disposing) { - if (disposing) + protected void Dispose(bool disposing) + { + if (disposing) { var jiraConnector = SimpleServiceProvider.Current.GetInstance(); jiraConnector?.Dispose(); - } - } + } + } - /// - /// Implementation of the IGreenshotPlugin.Initialize - /// - /// true if plugin is initialized, false if not (doesn't show) - public bool Initialize() { - // Register configuration (don't need the configuration itself) - _config = IniConfig.GetIniSection(); + /// + /// Implementation of the IGreenshotPlugin.Initialize + /// + /// true if plugin is initialized, false if not (doesn't show) + public bool Initialize() + { + // Register configuration (don't need the configuration itself) + _config = IniConfig.GetIniSection(); - // Provide the JiraConnector - SimpleServiceProvider.Current.AddService(new JiraConnector()); - // Provide the IDestination - SimpleServiceProvider.Current.AddService(new JiraDestination()); + // Provide the JiraConnector + SimpleServiceProvider.Current.AddService(new JiraConnector()); + // Provide the IDestination + SimpleServiceProvider.Current.AddService(new JiraDestination()); - // Make sure the log is enabled for the correct level. - if (Log.IsDebugEnabled) - { - LogSettings.RegisterDefaultLogger(LogLevels.Verbose); - } - else if (Log.IsInfoEnabled) - { - LogSettings.RegisterDefaultLogger(LogLevels.Info); - } - else if (Log.IsWarnEnabled) - { - LogSettings.RegisterDefaultLogger(LogLevels.Warn); - } - else if (Log.IsErrorEnabled) - { - LogSettings.RegisterDefaultLogger(LogLevels.Error); - } - else if (Log.IsErrorEnabled) - { - LogSettings.RegisterDefaultLogger(LogLevels.Error); - } - else - { - LogSettings.RegisterDefaultLogger(LogLevels.Fatal); - } + // Make sure the log is enabled for the correct level. + if (Log.IsDebugEnabled) + { + LogSettings.RegisterDefaultLogger(LogLevels.Verbose); + } + else if (Log.IsInfoEnabled) + { + LogSettings.RegisterDefaultLogger(LogLevels.Info); + } + else if (Log.IsWarnEnabled) + { + LogSettings.RegisterDefaultLogger(LogLevels.Warn); + } + else if (Log.IsErrorEnabled) + { + LogSettings.RegisterDefaultLogger(LogLevels.Error); + } + else if (Log.IsErrorEnabled) + { + LogSettings.RegisterDefaultLogger(LogLevels.Error); + } + else + { + LogSettings.RegisterDefaultLogger(LogLevels.Fatal); + } return true; - } + } - public void Shutdown() { - Log.Debug("Jira Plugin shutdown."); + public void Shutdown() + { + Log.Debug("Jira Plugin shutdown."); var jiraConnector = SimpleServiceProvider.Current.GetInstance(); jiraConnector?.Logout(); } - /// - /// Implementation of the IPlugin.Configure - /// - public void Configure() { - string url = _config.Url; - if (ShowConfigDialog()) { - // check for re-login + /// + /// Implementation of the IPlugin.Configure + /// + public void Configure() + { + string url = _config.Url; + if (ShowConfigDialog()) + { + // check for re-login var jiraConnector = SimpleServiceProvider.Current.GetInstance(); - if (jiraConnector != null && jiraConnector.IsLoggedIn && !string.IsNullOrEmpty(url)) { - if (!url.Equals(_config.Url)) { + if (jiraConnector != null && jiraConnector.IsLoggedIn && !string.IsNullOrEmpty(url)) + { + if (!url.Equals(_config.Url)) + { jiraConnector.Logout(); - Task.Run(async () => - { - await jiraConnector.LoginAsync(); - }); - } - } - } - } + Task.Run(async () => { await jiraConnector.LoginAsync(); }); + } + } + } + } - /// - /// A form for username/password - /// - /// bool true if OK was pressed, false if cancel - private bool ShowConfigDialog() - { - var settingsForm = new SettingsForm(); - var result = settingsForm.ShowDialog(); - if (result == DialogResult.OK) - { - return true; - } - return false; - } - } -} + /// + /// A form for username/password + /// + /// bool true if OK was pressed, false if cancel + private bool ShowConfigDialog() + { + var settingsForm = new SettingsForm(); + var result = settingsForm.ShowDialog(); + if (result == DialogResult.OK) + { + return true; + } + + return false; + } + } +} \ No newline at end of file diff --git a/src/Greenshot.Plugin.Jira/LanguageKeys.cs b/src/Greenshot.Plugin.Jira/LanguageKeys.cs index a387b44d0..fd3fae242 100644 --- a/src/Greenshot.Plugin.Jira/LanguageKeys.cs +++ b/src/Greenshot.Plugin.Jira/LanguageKeys.cs @@ -19,20 +19,22 @@ * along with this program. If not, see . */ -namespace Greenshot.Plugin.Jira { - public enum LangKey { - upload_menu_item, - column_assignee, - column_created, - column_issueType, - column_id, - column_reporter, - column_summary, - label_comment, - label_filename, - label_jirafilter, - login_error, - upload_failure, - communication_wait, - } -} +namespace Greenshot.Plugin.Jira +{ + public enum LangKey + { + upload_menu_item, + column_assignee, + column_created, + column_issueType, + column_id, + column_reporter, + column_summary, + label_comment, + label_filename, + label_jirafilter, + login_error, + upload_failure, + communication_wait, + } +} \ No newline at end of file diff --git a/src/Greenshot.Plugin.Jira/Log4NetLogger.cs b/src/Greenshot.Plugin.Jira/Log4NetLogger.cs index 12a4ac01a..125fbcbcc 100644 --- a/src/Greenshot.Plugin.Jira/Log4NetLogger.cs +++ b/src/Greenshot.Plugin.Jira/Log4NetLogger.cs @@ -24,97 +24,98 @@ using log4net; namespace Greenshot.Plugin.Jira { - /// - /// Used to make Dapplo.Log, used in Dapplo.Jira, write to Log4net - /// - public class Log4NetLogger : AbstractLogger - { - private ILog GetLogger(LogSource logSource) - { - return logSource.SourceType != null ? LogManager.GetLogger(logSource.SourceType) : LogManager.GetLogger(logSource.Source); - } + /// + /// Used to make Dapplo.Log, used in Dapplo.Jira, write to Log4net + /// + public class Log4NetLogger : AbstractLogger + { + private ILog GetLogger(LogSource logSource) + { + return logSource.SourceType != null ? LogManager.GetLogger(logSource.SourceType) : LogManager.GetLogger(logSource.Source); + } - /// - /// Write the supplied information to a log4net.ILog - /// - /// LogInfo - /// string - /// params object[] - public override void Write(LogInfo logInfo, string messageTemplate, params object[] propertyValues) - { - var log = GetLogger(logInfo.Source); + /// + /// Write the supplied information to a log4net.ILog + /// + /// LogInfo + /// string + /// params object[] + public override void Write(LogInfo logInfo, string messageTemplate, params object[] propertyValues) + { + var log = GetLogger(logInfo.Source); - switch (logInfo.LogLevel) - { - case LogLevels.Verbose: - case LogLevels.Debug: - if (propertyValues != null) - log.DebugFormat(messageTemplate, propertyValues); - else - log.Debug(messageTemplate); - break; - case LogLevels.Error: - if (propertyValues != null) - log.ErrorFormat(messageTemplate, propertyValues); - else - log.Error(messageTemplate); - break; - case LogLevels.Fatal: - if (propertyValues != null) - log.FatalFormat(messageTemplate, propertyValues); - else - log.Fatal(messageTemplate); - break; - case LogLevels.Info: - if (propertyValues != null) - log.InfoFormat(messageTemplate, propertyValues); - else - log.Info(messageTemplate); - break; - case LogLevels.Warn: - if (propertyValues != null) - log.WarnFormat(messageTemplate, propertyValues); - else - log.Warn(messageTemplate); - break; - } - } + switch (logInfo.LogLevel) + { + case LogLevels.Verbose: + case LogLevels.Debug: + if (propertyValues != null) + log.DebugFormat(messageTemplate, propertyValues); + else + log.Debug(messageTemplate); + break; + case LogLevels.Error: + if (propertyValues != null) + log.ErrorFormat(messageTemplate, propertyValues); + else + log.Error(messageTemplate); + break; + case LogLevels.Fatal: + if (propertyValues != null) + log.FatalFormat(messageTemplate, propertyValues); + else + log.Fatal(messageTemplate); + break; + case LogLevels.Info: + if (propertyValues != null) + log.InfoFormat(messageTemplate, propertyValues); + else + log.Info(messageTemplate); + break; + case LogLevels.Warn: + if (propertyValues != null) + log.WarnFormat(messageTemplate, propertyValues); + else + log.Warn(messageTemplate); + break; + } + } - /// - /// Make sure there are no newlines passed - /// - /// - /// - /// - public override void WriteLine(LogInfo logInfo, string messageTemplate, params object[] logParameters) - { - Write(logInfo, messageTemplate, logParameters); - } + /// + /// Make sure there are no newlines passed + /// + /// + /// + /// + public override void WriteLine(LogInfo logInfo, string messageTemplate, params object[] logParameters) + { + Write(logInfo, messageTemplate, logParameters); + } - /// - /// Test if a certain LogLevels enum is enabled - /// - /// LogLevels value - /// LogSource to check for - /// bool true if the LogLevels enum is enabled - public override bool IsLogLevelEnabled(LogLevels level, LogSource logSource = null) - { - var log = GetLogger(logSource); - switch (level) - { - case LogLevels.Verbose: - case LogLevels.Debug: - return log.IsDebugEnabled; - case LogLevels.Error: - return log.IsErrorEnabled; - case LogLevels.Fatal: - return log.IsFatalEnabled; - case LogLevels.Info: - return log.IsInfoEnabled; - case LogLevels.Warn: - return log.IsWarnEnabled; - } - return false; - } - } -} + /// + /// Test if a certain LogLevels enum is enabled + /// + /// LogLevels value + /// LogSource to check for + /// bool true if the LogLevels enum is enabled + public override bool IsLogLevelEnabled(LogLevels level, LogSource logSource = null) + { + var log = GetLogger(logSource); + switch (level) + { + case LogLevels.Verbose: + case LogLevels.Debug: + return log.IsDebugEnabled; + case LogLevels.Error: + return log.IsErrorEnabled; + case LogLevels.Fatal: + return log.IsFatalEnabled; + case LogLevels.Info: + return log.IsInfoEnabled; + case LogLevels.Warn: + return log.IsWarnEnabled; + } + + return false; + } + } +} \ No newline at end of file diff --git a/src/Greenshot.Plugin.Office/Com/DisposableCom.cs b/src/Greenshot.Plugin.Office/Com/DisposableCom.cs index 5d969d0be..e722f48e3 100644 --- a/src/Greenshot.Plugin.Office/Com/DisposableCom.cs +++ b/src/Greenshot.Plugin.Office/Com/DisposableCom.cs @@ -1,5 +1,6 @@ // Copyright (c) Dapplo and contributors. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. + namespace Greenshot.Plugin.Office.Com { /// diff --git a/src/Greenshot.Plugin.Office/Com/DisposableComImplementation.cs b/src/Greenshot.Plugin.Office/Com/DisposableComImplementation.cs index 3f721fc38..572de52d8 100644 --- a/src/Greenshot.Plugin.Office/Com/DisposableComImplementation.cs +++ b/src/Greenshot.Plugin.Office/Com/DisposableComImplementation.cs @@ -38,6 +38,7 @@ namespace Greenshot.Plugin.Office.Com { return; } + // Do not catch an exception from this. // You may want to remove these guards depending on // what you think the semantics should be. @@ -45,6 +46,7 @@ namespace Greenshot.Plugin.Office.Com { Marshal.ReleaseComObject(ComObject); } + ComObject = default; } } diff --git a/src/Greenshot.Plugin.Office/Com/Ole32Api.cs b/src/Greenshot.Plugin.Office/Com/Ole32Api.cs index 60167897e..efbf3614b 100644 --- a/src/Greenshot.Plugin.Office/Com/Ole32Api.cs +++ b/src/Greenshot.Plugin.Office/Com/Ole32Api.cs @@ -24,6 +24,7 @@ namespace Greenshot.Plugin.Office.Com { return clsId; } + return clsId; } @@ -36,4 +37,4 @@ namespace Greenshot.Plugin.Office.Com [DllImport("ole32.dll", ExactSpelling = true)] private static extern HResult CLSIDFromProgID([In] [MarshalAs(UnmanagedType.LPWStr)] string progId, [Out] out Guid clsId); } -} +} \ No newline at end of file diff --git a/src/Greenshot.Plugin.Office/Com/OleAut32Api.cs b/src/Greenshot.Plugin.Office/Com/OleAut32Api.cs index 1c38477b9..534f8a274 100644 --- a/src/Greenshot.Plugin.Office/Com/OleAut32Api.cs +++ b/src/Greenshot.Plugin.Office/Com/OleAut32Api.cs @@ -23,7 +23,7 @@ namespace Greenshot.Plugin.Office.Com { if (GetActiveObject(ref clsId, IntPtr.Zero, out object comObject).Succeeded()) { - return DisposableCom.Create((T)comObject); + return DisposableCom.Create((T) comObject); } return null; @@ -51,4 +51,4 @@ namespace Greenshot.Plugin.Office.Com [DllImport("oleaut32.dll")] private static extern HResult GetActiveObject(ref Guid rclsId, IntPtr pvReserved, [MarshalAs(UnmanagedType.IUnknown)] out object ppunk); } -} +} \ No newline at end of file diff --git a/src/Greenshot.Plugin.Office/Destinations/ExcelDestination.cs b/src/Greenshot.Plugin.Office/Destinations/ExcelDestination.cs index 571ebc7c4..5df550775 100644 --- a/src/Greenshot.Plugin.Office/Destinations/ExcelDestination.cs +++ b/src/Greenshot.Plugin.Office/Destinations/ExcelDestination.cs @@ -28,70 +28,89 @@ using GreenshotPlugin.Core; using GreenshotPlugin.Interfaces; using GreenshotPlugin.Interfaces.Plugin; -namespace Greenshot.Plugin.Office.Destinations { - /// - /// Description of PowerpointDestination. - /// - public class ExcelDestination : AbstractDestination { - private const int IconApplication = 0; - private const int IconWorkbook = 1; - private static readonly string ExePath; - private readonly string _workbookName; +namespace Greenshot.Plugin.Office.Destinations +{ + /// + /// Description of PowerpointDestination. + /// + public class ExcelDestination : AbstractDestination + { + private const int IconApplication = 0; + private const int IconWorkbook = 1; + private static readonly string ExePath; + private readonly string _workbookName; - static ExcelDestination() { - ExePath = PluginUtils.GetExePath("EXCEL.EXE"); - if (ExePath != null && File.Exists(ExePath)) { - WindowDetails.AddProcessToExcludeFromFreeze("excel"); - } else { - ExePath = null; - } - } + static ExcelDestination() + { + ExePath = PluginUtils.GetExePath("EXCEL.EXE"); + if (ExePath != null && File.Exists(ExePath)) + { + WindowDetails.AddProcessToExcludeFromFreeze("excel"); + } + else + { + ExePath = null; + } + } - public ExcelDestination() { - } + public ExcelDestination() + { + } - public ExcelDestination(string workbookName) { - _workbookName = workbookName; - } + public ExcelDestination(string workbookName) + { + _workbookName = workbookName; + } - public override string Designation => "Excel"; + public override string Designation => "Excel"; - public override string Description => _workbookName ?? "Microsoft Excel"; + public override string Description => _workbookName ?? "Microsoft Excel"; - public override int Priority => 5; + public override int Priority => 5; - public override bool IsDynamic => true; + public override bool IsDynamic => true; - public override bool IsActive => base.IsActive && ExePath != null; + public override bool IsActive => base.IsActive && ExePath != null; - public override Image DisplayIcon => PluginUtils.GetCachedExeIcon(ExePath, !string.IsNullOrEmpty(_workbookName) ? IconWorkbook : IconApplication); + public override Image DisplayIcon => PluginUtils.GetCachedExeIcon(ExePath, !string.IsNullOrEmpty(_workbookName) ? IconWorkbook : IconApplication); - public override IEnumerable DynamicDestinations() { - foreach (string workbookName in ExcelExporter.GetWorkbooks()) { - yield return new ExcelDestination(workbookName); - } - } + public override IEnumerable DynamicDestinations() + { + foreach (string workbookName in ExcelExporter.GetWorkbooks()) + { + yield return new ExcelDestination(workbookName); + } + } - public override ExportInformation ExportCapture(bool manuallyInitiated, ISurface surface, ICaptureDetails captureDetails) { - ExportInformation exportInformation = new ExportInformation(Designation, Description); - bool createdFile = false; - string imageFile = captureDetails.Filename; - if (imageFile == null || surface.Modified || !Regex.IsMatch(imageFile, @".*(\.png|\.gif|\.jpg|\.jpeg|\.tiff|\.bmp)$")) { - imageFile = ImageOutput.SaveNamedTmpFile(surface, captureDetails, new SurfaceOutputSettings().PreventGreenshotFormat()); - createdFile = true; - } - if (_workbookName != null) { - ExcelExporter.InsertIntoExistingWorkbook(_workbookName, imageFile, surface.Image.Size); - } else { - ExcelExporter.InsertIntoNewWorkbook(imageFile, surface.Image.Size); - } - exportInformation.ExportMade = true; - ProcessExport(exportInformation, surface); - // Cleanup imageFile if we created it here, so less tmp-files are generated and left - if (createdFile) { - ImageOutput.DeleteNamedTmpFile(imageFile); - } - return exportInformation; - } - } -} + public override ExportInformation ExportCapture(bool manuallyInitiated, ISurface surface, ICaptureDetails captureDetails) + { + ExportInformation exportInformation = new ExportInformation(Designation, Description); + bool createdFile = false; + string imageFile = captureDetails.Filename; + if (imageFile == null || surface.Modified || !Regex.IsMatch(imageFile, @".*(\.png|\.gif|\.jpg|\.jpeg|\.tiff|\.bmp)$")) + { + imageFile = ImageOutput.SaveNamedTmpFile(surface, captureDetails, new SurfaceOutputSettings().PreventGreenshotFormat()); + createdFile = true; + } + + if (_workbookName != null) + { + ExcelExporter.InsertIntoExistingWorkbook(_workbookName, imageFile, surface.Image.Size); + } + else + { + ExcelExporter.InsertIntoNewWorkbook(imageFile, surface.Image.Size); + } + + exportInformation.ExportMade = true; + ProcessExport(exportInformation, surface); + // Cleanup imageFile if we created it here, so less tmp-files are generated and left + if (createdFile) + { + ImageOutput.DeleteNamedTmpFile(imageFile); + } + + return exportInformation; + } + } +} \ No newline at end of file diff --git a/src/Greenshot.Plugin.Office/Destinations/OneNoteDestination.cs b/src/Greenshot.Plugin.Office/Destinations/OneNoteDestination.cs index d638682f7..578f357fb 100644 --- a/src/Greenshot.Plugin.Office/Destinations/OneNoteDestination.cs +++ b/src/Greenshot.Plugin.Office/Destinations/OneNoteDestination.cs @@ -28,97 +28,117 @@ using Greenshot.Plugin.Office.OfficeExport.Entities; using GreenshotPlugin.Core; using GreenshotPlugin.Interfaces; -namespace Greenshot.Plugin.Office.Destinations { - public class OneNoteDestination : AbstractDestination { - private static readonly log4net.ILog LOG = log4net.LogManager.GetLogger(typeof(WordDestination)); - private const int ICON_APPLICATION = 0; - public const string DESIGNATION = "OneNote"; - private static readonly string exePath; - private readonly OneNotePage page; - private readonly OneNoteExporter _oneNoteExporter = new OneNoteExporter(); +namespace Greenshot.Plugin.Office.Destinations +{ + public class OneNoteDestination : AbstractDestination + { + private static readonly log4net.ILog LOG = log4net.LogManager.GetLogger(typeof(WordDestination)); + private const int ICON_APPLICATION = 0; + public const string DESIGNATION = "OneNote"; + private static readonly string exePath; + private readonly OneNotePage page; + private readonly OneNoteExporter _oneNoteExporter = new OneNoteExporter(); - static OneNoteDestination() { - exePath = PluginUtils.GetExePath("ONENOTE.EXE"); - if (exePath != null && File.Exists(exePath)) { - WindowDetails.AddProcessToExcludeFromFreeze("onenote"); - } else { - exePath = null; - } - } + static OneNoteDestination() + { + exePath = PluginUtils.GetExePath("ONENOTE.EXE"); + if (exePath != null && File.Exists(exePath)) + { + WindowDetails.AddProcessToExcludeFromFreeze("onenote"); + } + else + { + exePath = null; + } + } - public OneNoteDestination() { + public OneNoteDestination() + { + } - } + public OneNoteDestination(OneNotePage page) + { + this.page = page; + } - public OneNoteDestination(OneNotePage page) { - this.page = page; - } + public override string Designation + { + get { return DESIGNATION; } + } - public override string Designation { - get { - return DESIGNATION; - } - } + public override string Description + { + get + { + if (page == null) + { + return "Microsoft OneNote"; + } + else + { + return page.DisplayName; + } + } + } - public override string Description { - get { - if (page == null) { - return "Microsoft OneNote"; - } else { - return page.DisplayName; - } - } - } + public override int Priority + { + get { return 4; } + } - public override int Priority { - get { - return 4; - } - } + public override bool IsDynamic + { + get { return true; } + } - public override bool IsDynamic { - get { - return true; - } - } + public override bool IsActive + { + get { return base.IsActive && exePath != null; } + } - public override bool IsActive { - get { - return base.IsActive && exePath != null; - } - } + public override Image DisplayIcon + { + get { return PluginUtils.GetCachedExeIcon(exePath, ICON_APPLICATION); } + } - public override Image DisplayIcon { - get { - return PluginUtils.GetCachedExeIcon(exePath, ICON_APPLICATION); - } - } + public override IEnumerable DynamicDestinations() + { + foreach (OneNotePage page in _oneNoteExporter.GetPages()) + { + yield return new OneNoteDestination(page); + } + } - public override IEnumerable DynamicDestinations() { - foreach (OneNotePage page in _oneNoteExporter.GetPages()) { - yield return new OneNoteDestination(page); - } - } + public override ExportInformation ExportCapture(bool manuallyInitiated, ISurface surface, ICaptureDetails captureDetails) + { + ExportInformation exportInformation = new ExportInformation(Designation, Description); - public override ExportInformation ExportCapture(bool manuallyInitiated, ISurface surface, ICaptureDetails captureDetails) { - ExportInformation exportInformation = new ExportInformation(Designation, Description); + if (page == null) + { + try + { + exportInformation.ExportMade = _oneNoteExporter.ExportToNewPage(surface); + } + catch (Exception ex) + { + exportInformation.ErrorMessage = ex.Message; + LOG.Error(ex); + } + } + else + { + try + { + exportInformation.ExportMade = _oneNoteExporter.ExportToPage(surface, page); + } + catch (Exception ex) + { + exportInformation.ErrorMessage = ex.Message; + LOG.Error(ex); + } + } - if (page == null) { - try { - exportInformation.ExportMade = _oneNoteExporter.ExportToNewPage(surface); - } catch(Exception ex) { - exportInformation.ErrorMessage = ex.Message; - LOG.Error(ex); - } - } else { - try { - exportInformation.ExportMade = _oneNoteExporter.ExportToPage(surface, page); - } catch(Exception ex) { - exportInformation.ErrorMessage = ex.Message; - LOG.Error(ex); - } - } - return exportInformation; - } - } -} + return exportInformation; + } + } +} \ No newline at end of file diff --git a/src/Greenshot.Plugin.Office/Destinations/OutlookDestination.cs b/src/Greenshot.Plugin.Office/Destinations/OutlookDestination.cs index 5f5182235..ae80b6f1e 100644 --- a/src/Greenshot.Plugin.Office/Destinations/OutlookDestination.cs +++ b/src/Greenshot.Plugin.Office/Destinations/OutlookDestination.cs @@ -32,152 +32,192 @@ using GreenshotPlugin.Interfaces.Plugin; using Microsoft.Office.Interop.Outlook; using Microsoft.Win32; -namespace Greenshot.Plugin.Office.Destinations { - /// - /// Description of OutlookDestination. - /// - public class OutlookDestination : AbstractDestination { - private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(typeof(OutlookDestination)); - private const int IconApplication = 0; - private const int IconMeeting = 2; +namespace Greenshot.Plugin.Office.Destinations +{ + /// + /// Description of OutlookDestination. + /// + public class OutlookDestination : AbstractDestination + { + private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(typeof(OutlookDestination)); + private const int IconApplication = 0; + private const int IconMeeting = 2; - private static readonly Image MailIcon = GreenshotResources.GetImage("Email.Image"); - private static readonly OfficeConfiguration OfficeConfig = IniConfig.GetIniSection(); - private static readonly string ExePath; - private static readonly bool IsActiveFlag; - private const string MapiClient = "Microsoft Outlook"; - private readonly string _outlookInspectorCaption; - private readonly OlObjectClass _outlookInspectorType; - private readonly OutlookEmailExporter _outlookEmailExporter = new(); + private static readonly Image MailIcon = GreenshotResources.GetImage("Email.Image"); + private static readonly OfficeConfiguration OfficeConfig = IniConfig.GetIniSection(); + private static readonly string ExePath; + private static readonly bool IsActiveFlag; + private const string MapiClient = "Microsoft Outlook"; + private readonly string _outlookInspectorCaption; + private readonly OlObjectClass _outlookInspectorType; + private readonly OutlookEmailExporter _outlookEmailExporter = new(); - static OutlookDestination() { - if (HasOutlook()) { - IsActiveFlag = true; - } - ExePath = PluginUtils.GetExePath("OUTLOOK.EXE"); - if (ExePath != null && File.Exists(ExePath)) { - WindowDetails.AddProcessToExcludeFromFreeze("outlook"); - } else { - ExePath = GetOutlookExePath(); - } - if (ExePath == null) { - IsActiveFlag = false; - } - } + static OutlookDestination() + { + if (HasOutlook()) + { + IsActiveFlag = true; + } + + ExePath = PluginUtils.GetExePath("OUTLOOK.EXE"); + if (ExePath != null && File.Exists(ExePath)) + { + WindowDetails.AddProcessToExcludeFromFreeze("outlook"); + } + else + { + ExePath = GetOutlookExePath(); + } + + if (ExePath == null) + { + IsActiveFlag = false; + } + } - private static string GetOutlookExePath() => RegistryHive.LocalMachine.ReadKey64Or32(@"Microsoft\Windows\CurrentVersion\App Paths\OUTLOOK.EXE"); + private static string GetOutlookExePath() => RegistryHive.LocalMachine.ReadKey64Or32(@"Microsoft\Windows\CurrentVersion\App Paths\OUTLOOK.EXE"); - /// - /// Check if Outlook is installed - /// - /// Returns true if outlook is installed - private static bool HasOutlook() - { - string outlookPath = GetOutlookExePath(); - if (outlookPath == null) - { - return false; - } - return File.Exists(outlookPath); - } + /// + /// Check if Outlook is installed + /// + /// Returns true if outlook is installed + private static bool HasOutlook() + { + string outlookPath = GetOutlookExePath(); + if (outlookPath == null) + { + return false; + } - public OutlookDestination() { - } + return File.Exists(outlookPath); + } - public OutlookDestination(string outlookInspectorCaption, OlObjectClass outlookInspectorType) { - _outlookInspectorCaption = outlookInspectorCaption; - _outlookInspectorType = outlookInspectorType; - } + public OutlookDestination() + { + } - public override string Designation => "Outlook"; + public OutlookDestination(string outlookInspectorCaption, OlObjectClass outlookInspectorType) + { + _outlookInspectorCaption = outlookInspectorCaption; + _outlookInspectorType = outlookInspectorType; + } - public override string Description => _outlookInspectorCaption ?? MapiClient; + public override string Designation => "Outlook"; - public override int Priority => 3; + public override string Description => _outlookInspectorCaption ?? MapiClient; - public override bool IsActive => base.IsActive && IsActiveFlag; + public override int Priority => 3; - public override bool IsDynamic => true; + public override bool IsActive => base.IsActive && IsActiveFlag; - public override Keys EditorShortcutKeys => Keys.Control | Keys.E; + public override bool IsDynamic => true; - public override Image DisplayIcon { - get - { - if (_outlookInspectorCaption == null) - { - return PluginUtils.GetCachedExeIcon(ExePath, IconApplication); - } - if (OlObjectClass.olAppointment.Equals(_outlookInspectorType)) { - // Make sure we loaded the icon, maybe the configuration has been changed! - return PluginUtils.GetCachedExeIcon(ExePath, IconMeeting); - } - return MailIcon; - } - } - - public override IEnumerable DynamicDestinations() { - IDictionary inspectorCaptions = _outlookEmailExporter.RetrievePossibleTargets(); - if (inspectorCaptions != null) { - foreach (string inspectorCaption in inspectorCaptions.Keys) { - yield return new OutlookDestination(inspectorCaption, inspectorCaptions[inspectorCaption]); - } - } - } + public override Keys EditorShortcutKeys => Keys.Control | Keys.E; - /// - /// Export the capture to outlook - /// - /// - /// - /// - /// - public override ExportInformation ExportCapture(bool manuallyInitiated, ISurface surface, ICaptureDetails captureDetails) { - ExportInformation exportInformation = new ExportInformation(Designation, Description); - // Outlook logic - string tmpFile = captureDetails.Filename; - if (tmpFile == null || surface.Modified || !Regex.IsMatch(tmpFile, @".*(\.png|\.gif|\.jpg|\.jpeg|\.tiff|\.bmp)$")) { - tmpFile = ImageOutput.SaveNamedTmpFile(surface, captureDetails, new SurfaceOutputSettings().PreventGreenshotFormat()); - } else { - Log.InfoFormat("Using already available file: {0}", tmpFile); - } + public override Image DisplayIcon + { + get + { + if (_outlookInspectorCaption == null) + { + return PluginUtils.GetCachedExeIcon(ExePath, IconApplication); + } - // Create a attachment name for the image - string attachmentName = captureDetails.Title; - if (!string.IsNullOrEmpty(attachmentName)) { - attachmentName = attachmentName.Trim(); - } - // Set default if non is set - if (string.IsNullOrEmpty(attachmentName)) { - attachmentName = "Greenshot Capture"; - } - // Make sure it's "clean" so it doesn't corrupt the header - attachmentName = Regex.Replace(attachmentName, @"[^\x20\d\w]", string.Empty); + if (OlObjectClass.olAppointment.Equals(_outlookInspectorType)) + { + // Make sure we loaded the icon, maybe the configuration has been changed! + return PluginUtils.GetCachedExeIcon(ExePath, IconMeeting); + } - if (_outlookInspectorCaption != null) { + return MailIcon; + } + } + + public override IEnumerable DynamicDestinations() + { + IDictionary inspectorCaptions = _outlookEmailExporter.RetrievePossibleTargets(); + if (inspectorCaptions != null) + { + foreach (string inspectorCaption in inspectorCaptions.Keys) + { + yield return new OutlookDestination(inspectorCaption, inspectorCaptions[inspectorCaption]); + } + } + } + + /// + /// Export the capture to outlook + /// + /// + /// + /// + /// + public override ExportInformation ExportCapture(bool manuallyInitiated, ISurface surface, ICaptureDetails captureDetails) + { + ExportInformation exportInformation = new ExportInformation(Designation, Description); + // Outlook logic + string tmpFile = captureDetails.Filename; + if (tmpFile == null || surface.Modified || !Regex.IsMatch(tmpFile, @".*(\.png|\.gif|\.jpg|\.jpeg|\.tiff|\.bmp)$")) + { + tmpFile = ImageOutput.SaveNamedTmpFile(surface, captureDetails, new SurfaceOutputSettings().PreventGreenshotFormat()); + } + else + { + Log.InfoFormat("Using already available file: {0}", tmpFile); + } + + // Create a attachment name for the image + string attachmentName = captureDetails.Title; + if (!string.IsNullOrEmpty(attachmentName)) + { + attachmentName = attachmentName.Trim(); + } + + // Set default if non is set + if (string.IsNullOrEmpty(attachmentName)) + { + attachmentName = "Greenshot Capture"; + } + + // Make sure it's "clean" so it doesn't corrupt the header + attachmentName = Regex.Replace(attachmentName, @"[^\x20\d\w]", string.Empty); + + if (_outlookInspectorCaption != null) + { _outlookEmailExporter.ExportToInspector(_outlookInspectorCaption, tmpFile, attachmentName); - exportInformation.ExportMade = true; - } else { - if (!manuallyInitiated) { - var inspectorCaptions = _outlookEmailExporter.RetrievePossibleTargets(); - if (inspectorCaptions != null && inspectorCaptions.Count > 0) { - var destinations = new List - { - new OutlookDestination() - }; - foreach (string inspectorCaption in inspectorCaptions.Keys) { - destinations.Add(new OutlookDestination(inspectorCaption, inspectorCaptions[inspectorCaption])); - } - // Return the ExportInformation from the picker without processing, as this indirectly comes from us self - return ShowPickerMenu(false, surface, captureDetails, destinations); - } - } else { - exportInformation.ExportMade = _outlookEmailExporter.ExportToOutlook(OfficeConfig.OutlookEmailFormat, tmpFile, FilenameHelper.FillPattern(OfficeConfig.EmailSubjectPattern, captureDetails, false), attachmentName, OfficeConfig.EmailTo, OfficeConfig.EmailCC, OfficeConfig.EmailBCC, null); - } - } - ProcessExport(exportInformation, surface); - return exportInformation; - } - } + exportInformation.ExportMade = true; + } + else + { + if (!manuallyInitiated) + { + var inspectorCaptions = _outlookEmailExporter.RetrievePossibleTargets(); + if (inspectorCaptions != null && inspectorCaptions.Count > 0) + { + var destinations = new List + { + new OutlookDestination() + }; + foreach (string inspectorCaption in inspectorCaptions.Keys) + { + destinations.Add(new OutlookDestination(inspectorCaption, inspectorCaptions[inspectorCaption])); + } + + // Return the ExportInformation from the picker without processing, as this indirectly comes from us self + return ShowPickerMenu(false, surface, captureDetails, destinations); + } + } + else + { + exportInformation.ExportMade = _outlookEmailExporter.ExportToOutlook(OfficeConfig.OutlookEmailFormat, tmpFile, + FilenameHelper.FillPattern(OfficeConfig.EmailSubjectPattern, captureDetails, false), attachmentName, OfficeConfig.EmailTo, OfficeConfig.EmailCC, + OfficeConfig.EmailBCC, null); + } + } + + ProcessExport(exportInformation, surface); + return exportInformation; + } + } } \ No newline at end of file diff --git a/src/Greenshot.Plugin.Office/Destinations/PowerpointDestination.cs b/src/Greenshot.Plugin.Office/Destinations/PowerpointDestination.cs index 1382a316a..bb5a0c453 100644 --- a/src/Greenshot.Plugin.Office/Destinations/PowerpointDestination.cs +++ b/src/Greenshot.Plugin.Office/Destinations/PowerpointDestination.cs @@ -29,95 +29,127 @@ using GreenshotPlugin.Core; using GreenshotPlugin.Interfaces; using GreenshotPlugin.Interfaces.Plugin; -namespace Greenshot.Plugin.Office.Destinations { - /// - /// Description of PowerpointDestination. - /// - public class PowerpointDestination : AbstractDestination { - private const int IconApplication = 0; - private const int IconPresentation = 1; +namespace Greenshot.Plugin.Office.Destinations +{ + /// + /// Description of PowerpointDestination. + /// + public class PowerpointDestination : AbstractDestination + { + private const int IconApplication = 0; + private const int IconPresentation = 1; - private static readonly string ExePath; - private readonly string _presentationName; - private readonly PowerpointExporter _powerpointExporter = new PowerpointExporter(); - - static PowerpointDestination() { - ExePath = PluginUtils.GetExePath("POWERPNT.EXE"); - if (ExePath != null && File.Exists(ExePath)) { - WindowDetails.AddProcessToExcludeFromFreeze("powerpnt"); - } else { - ExePath = null; - } - } + private static readonly string ExePath; + private readonly string _presentationName; + private readonly PowerpointExporter _powerpointExporter = new PowerpointExporter(); - public PowerpointDestination() { - } + static PowerpointDestination() + { + ExePath = PluginUtils.GetExePath("POWERPNT.EXE"); + if (ExePath != null && File.Exists(ExePath)) + { + WindowDetails.AddProcessToExcludeFromFreeze("powerpnt"); + } + else + { + ExePath = null; + } + } - public PowerpointDestination(string presentationName) { - _presentationName = presentationName; - } + public PowerpointDestination() + { + } - public override string Designation => "Powerpoint"; + public PowerpointDestination(string presentationName) + { + _presentationName = presentationName; + } - public override string Description { - get - { - if (_presentationName == null) { - return "Microsoft Powerpoint"; - } - return _presentationName; - } - } + public override string Designation => "Powerpoint"; - public override int Priority => 4; + public override string Description + { + get + { + if (_presentationName == null) + { + return "Microsoft Powerpoint"; + } - public override bool IsDynamic => true; + return _presentationName; + } + } - public override bool IsActive => base.IsActive && ExePath != null; + public override int Priority => 4; - public override Image DisplayIcon { - get { - if (!string.IsNullOrEmpty(_presentationName)) { - return PluginUtils.GetCachedExeIcon(ExePath, IconPresentation); - } + public override bool IsDynamic => true; - return PluginUtils.GetCachedExeIcon(ExePath, IconApplication); - } - } + public override bool IsActive => base.IsActive && ExePath != null; - public override IEnumerable DynamicDestinations() { - foreach (string presentationName in _powerpointExporter.GetPowerpointPresentations()) { - yield return new PowerpointDestination(presentationName); - } - } + public override Image DisplayIcon + { + get + { + if (!string.IsNullOrEmpty(_presentationName)) + { + return PluginUtils.GetCachedExeIcon(ExePath, IconPresentation); + } - public override ExportInformation ExportCapture(bool manuallyInitiated, ISurface surface, ICaptureDetails captureDetails) { - ExportInformation exportInformation = new ExportInformation(Designation, Description); - string tmpFile = captureDetails.Filename; - Size imageSize = Size.Empty; - if (tmpFile == null || surface.Modified || !Regex.IsMatch(tmpFile, @".*(\.png|\.gif|\.jpg|\.jpeg|\.tiff|\.bmp)$")) { - tmpFile = ImageOutput.SaveNamedTmpFile(surface, captureDetails, new SurfaceOutputSettings().PreventGreenshotFormat()); - imageSize = surface.Image.Size; - } - if (_presentationName != null) { - exportInformation.ExportMade = _powerpointExporter.ExportToPresentation(_presentationName, tmpFile, imageSize, captureDetails.Title); - } else { - if (!manuallyInitiated) { - var presentations = _powerpointExporter.GetPowerpointPresentations().ToList(); - if (presentations != null && presentations.Count > 0) { - var destinations = new List {new PowerpointDestination()}; - foreach (string presentation in presentations) { - destinations.Add(new PowerpointDestination(presentation)); - } - // Return the ExportInformation from the picker without processing, as this indirectly comes from us self - return ShowPickerMenu(false, surface, captureDetails, destinations); - } - } else if (!exportInformation.ExportMade) { - exportInformation.ExportMade = _powerpointExporter.InsertIntoNewPresentation(tmpFile, imageSize, captureDetails.Title); - } - } - ProcessExport(exportInformation, surface); - return exportInformation; - } - } -} + return PluginUtils.GetCachedExeIcon(ExePath, IconApplication); + } + } + + public override IEnumerable DynamicDestinations() + { + foreach (string presentationName in _powerpointExporter.GetPowerpointPresentations()) + { + yield return new PowerpointDestination(presentationName); + } + } + + public override ExportInformation ExportCapture(bool manuallyInitiated, ISurface surface, ICaptureDetails captureDetails) + { + ExportInformation exportInformation = new ExportInformation(Designation, Description); + string tmpFile = captureDetails.Filename; + Size imageSize = Size.Empty; + if (tmpFile == null || surface.Modified || !Regex.IsMatch(tmpFile, @".*(\.png|\.gif|\.jpg|\.jpeg|\.tiff|\.bmp)$")) + { + tmpFile = ImageOutput.SaveNamedTmpFile(surface, captureDetails, new SurfaceOutputSettings().PreventGreenshotFormat()); + imageSize = surface.Image.Size; + } + + if (_presentationName != null) + { + exportInformation.ExportMade = _powerpointExporter.ExportToPresentation(_presentationName, tmpFile, imageSize, captureDetails.Title); + } + else + { + if (!manuallyInitiated) + { + var presentations = _powerpointExporter.GetPowerpointPresentations().ToList(); + if (presentations != null && presentations.Count > 0) + { + var destinations = new List + { + new PowerpointDestination() + }; + foreach (string presentation in presentations) + { + destinations.Add(new PowerpointDestination(presentation)); + } + + // Return the ExportInformation from the picker without processing, as this indirectly comes from us self + return ShowPickerMenu(false, surface, captureDetails, destinations); + } + } + else if (!exportInformation.ExportMade) + { + exportInformation.ExportMade = _powerpointExporter.InsertIntoNewPresentation(tmpFile, imageSize, captureDetails.Title); + } + } + + ProcessExport(exportInformation, surface); + return exportInformation; + } + } +} \ No newline at end of file diff --git a/src/Greenshot.Plugin.Office/Destinations/WordDestination.cs b/src/Greenshot.Plugin.Office/Destinations/WordDestination.cs index 4f85dd99e..ff9945824 100644 --- a/src/Greenshot.Plugin.Office/Destinations/WordDestination.cs +++ b/src/Greenshot.Plugin.Office/Destinations/WordDestination.cs @@ -30,102 +30,134 @@ using GreenshotPlugin.Core; using GreenshotPlugin.Interfaces; using GreenshotPlugin.Interfaces.Plugin; -namespace Greenshot.Plugin.Office.Destinations { - /// - /// Description of EmailDestination. - /// - public class WordDestination : AbstractDestination { - private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(typeof(WordDestination)); - private const int IconApplication = 0; - private const int IconDocument = 1; - private static readonly string ExePath; - private readonly string _documentCaption; +namespace Greenshot.Plugin.Office.Destinations +{ + /// + /// Description of EmailDestination. + /// + public class WordDestination : AbstractDestination + { + private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(typeof(WordDestination)); + private const int IconApplication = 0; + private const int IconDocument = 1; + private static readonly string ExePath; + private readonly string _documentCaption; private readonly WordExporter _wordExporter = new WordExporter(); - static WordDestination() { - ExePath = PluginUtils.GetExePath("WINWORD.EXE"); - if (ExePath != null && !File.Exists(ExePath)) { - ExePath = null; - } - } - - public WordDestination() { - - } - public WordDestination(string wordCaption) { - _documentCaption = wordCaption; - } + static WordDestination() + { + ExePath = PluginUtils.GetExePath("WINWORD.EXE"); + if (ExePath != null && !File.Exists(ExePath)) + { + ExePath = null; + } + } - public override string Designation => "Word"; + public WordDestination() + { + } - public override string Description => _documentCaption ?? "Microsoft Word"; + public WordDestination(string wordCaption) + { + _documentCaption = wordCaption; + } - public override int Priority => 4; + public override string Designation => "Word"; - public override bool IsDynamic => true; + public override string Description => _documentCaption ?? "Microsoft Word"; - public override bool IsActive => base.IsActive && ExePath != null; + public override int Priority => 4; - public override Image DisplayIcon => PluginUtils.GetCachedExeIcon(ExePath, !string.IsNullOrEmpty(_documentCaption) ? IconDocument : IconApplication); + public override bool IsDynamic => true; - public override IEnumerable DynamicDestinations() { - foreach (string wordCaption in _wordExporter.GetWordDocuments()) { - yield return new WordDestination(wordCaption); - } - } + public override bool IsActive => base.IsActive && ExePath != null; - public override ExportInformation ExportCapture(bool manuallyInitiated, ISurface surface, ICaptureDetails captureDetails) { - ExportInformation exportInformation = new ExportInformation(Designation, Description); - string tmpFile = captureDetails.Filename; - if (tmpFile == null || surface.Modified || !Regex.IsMatch(tmpFile, @".*(\.png|\.gif|\.jpg|\.jpeg|\.tiff|\.bmp)$")) { - tmpFile = ImageOutput.SaveNamedTmpFile(surface, captureDetails, new SurfaceOutputSettings().PreventGreenshotFormat()); - } - if (_documentCaption != null) { - try { + public override Image DisplayIcon => PluginUtils.GetCachedExeIcon(ExePath, !string.IsNullOrEmpty(_documentCaption) ? IconDocument : IconApplication); + + public override IEnumerable DynamicDestinations() + { + foreach (string wordCaption in _wordExporter.GetWordDocuments()) + { + yield return new WordDestination(wordCaption); + } + } + + public override ExportInformation ExportCapture(bool manuallyInitiated, ISurface surface, ICaptureDetails captureDetails) + { + ExportInformation exportInformation = new ExportInformation(Designation, Description); + string tmpFile = captureDetails.Filename; + if (tmpFile == null || surface.Modified || !Regex.IsMatch(tmpFile, @".*(\.png|\.gif|\.jpg|\.jpeg|\.tiff|\.bmp)$")) + { + tmpFile = ImageOutput.SaveNamedTmpFile(surface, captureDetails, new SurfaceOutputSettings().PreventGreenshotFormat()); + } + + if (_documentCaption != null) + { + try + { _wordExporter.InsertIntoExistingDocument(_documentCaption, tmpFile); - exportInformation.ExportMade = true; - } catch (Exception) { - try { + exportInformation.ExportMade = true; + } + catch (Exception) + { + try + { _wordExporter.InsertIntoExistingDocument(_documentCaption, tmpFile); - exportInformation.ExportMade = true; - } catch (Exception ex) { - Log.Error(ex); - // TODO: Change to general logic in ProcessExport - surface.SendMessageEvent(this, SurfaceMessageTyp.Error, Language.GetFormattedString("destination_exportfailed", Description)); - } - } - } else { - if (!manuallyInitiated) { - var documents = _wordExporter.GetWordDocuments().ToList(); - if (documents != null && documents.Count > 0) { - var destinations = new List - { - new WordDestination() - }; - foreach (string document in documents) { - destinations.Add(new WordDestination(document)); - } - // Return the ExportInformation from the picker without processing, as this indirectly comes from us self - return ShowPickerMenu(false, surface, captureDetails, destinations); - } - } - try { + exportInformation.ExportMade = true; + } + catch (Exception ex) + { + Log.Error(ex); + // TODO: Change to general logic in ProcessExport + surface.SendMessageEvent(this, SurfaceMessageTyp.Error, Language.GetFormattedString("destination_exportfailed", Description)); + } + } + } + else + { + if (!manuallyInitiated) + { + var documents = _wordExporter.GetWordDocuments().ToList(); + if (documents != null && documents.Count > 0) + { + var destinations = new List + { + new WordDestination() + }; + foreach (string document in documents) + { + destinations.Add(new WordDestination(document)); + } + + // Return the ExportInformation from the picker without processing, as this indirectly comes from us self + return ShowPickerMenu(false, surface, captureDetails, destinations); + } + } + + try + { _wordExporter.InsertIntoNewDocument(tmpFile, null, null); - exportInformation.ExportMade = true; - } catch(Exception) { - // Retry once, just in case - try { + exportInformation.ExportMade = true; + } + catch (Exception) + { + // Retry once, just in case + try + { _wordExporter.InsertIntoNewDocument(tmpFile, null, null); - exportInformation.ExportMade = true; - } catch (Exception ex) { - Log.Error(ex); - // TODO: Change to general logic in ProcessExport - surface.SendMessageEvent(this, SurfaceMessageTyp.Error, Language.GetFormattedString("destination_exportfailed", Description)); - } - } - } - ProcessExport(exportInformation, surface); - return exportInformation; - } - } -} + exportInformation.ExportMade = true; + } + catch (Exception ex) + { + Log.Error(ex); + // TODO: Change to general logic in ProcessExport + surface.SendMessageEvent(this, SurfaceMessageTyp.Error, Language.GetFormattedString("destination_exportfailed", Description)); + } + } + } + + ProcessExport(exportInformation, surface); + return exportInformation; + } + } +} \ No newline at end of file diff --git a/src/Greenshot.Plugin.Office/GlobalSuppressions.cs b/src/Greenshot.Plugin.Office/GlobalSuppressions.cs index 882507f6b..0adf57f14 100644 Binary files a/src/Greenshot.Plugin.Office/GlobalSuppressions.cs and b/src/Greenshot.Plugin.Office/GlobalSuppressions.cs differ diff --git a/src/Greenshot.Plugin.Office/OfficeConfiguration.cs b/src/Greenshot.Plugin.Office/OfficeConfiguration.cs index eaf118d6a..f2959aa34 100644 --- a/src/Greenshot.Plugin.Office/OfficeConfiguration.cs +++ b/src/Greenshot.Plugin.Office/OfficeConfiguration.cs @@ -23,32 +23,40 @@ using Greenshot.Plugin.Office.OfficeInterop; using GreenshotPlugin.IniFile; using Microsoft.Office.Interop.PowerPoint; -namespace Greenshot.Plugin.Office { +namespace Greenshot.Plugin.Office +{ + /// + /// Description of CoreConfiguration. + /// + [IniSection("Office", Description = "Greenshot Office configuration")] + public class OfficeConfiguration : IniSection + { + [IniProperty("OutlookEmailFormat", Description = "Default type for emails. (Text, HTML)", DefaultValue = "HTML")] + public EmailFormat OutlookEmailFormat { get; set; } - /// - /// Description of CoreConfiguration. - /// - [IniSection("Office", Description="Greenshot Office configuration")] - public class OfficeConfiguration : IniSection { - [IniProperty("OutlookEmailFormat", Description = "Default type for emails. (Text, HTML)", DefaultValue = "HTML")] - public EmailFormat OutlookEmailFormat { get; set; } + [IniProperty("EmailSubjectPattern", Description = "Email subject pattern, works like the OutputFileFilenamePattern", DefaultValue = "${title}")] + public string EmailSubjectPattern { get; set; } - [IniProperty("EmailSubjectPattern", Description = "Email subject pattern, works like the OutputFileFilenamePattern", DefaultValue = "${title}")] - public string EmailSubjectPattern { get; set; } - [IniProperty("EmailTo", Description = "Default value for the to in emails that are created", DefaultValue = "")] - public string EmailTo { get; set; } - [IniProperty("EmailCC", Description = "Default value for the CC in emails that are created", DefaultValue = "")] - public string EmailCC { get; set; } - [IniProperty("EmailBCC", Description = "Default value for the BCC in emails that are created", DefaultValue = "")] - public string EmailBCC { get; set; } - [IniProperty("OutlookAllowExportInMeetings", Description = "For Outlook: Allow export in meeting items", DefaultValue = "False")] - public bool OutlookAllowExportInMeetings { get; set; } - [IniProperty("WordLockAspectRatio", Description = "For Word: Lock the aspect ratio of the image", DefaultValue = "True")] - public bool WordLockAspectRatio { get; set; } - [IniProperty("PowerpointLockAspectRatio", Description = "For Powerpoint: Lock the aspect ratio of the image", DefaultValue = "True")] - public bool PowerpointLockAspectRatio { get; set; } - [IniProperty("PowerpointSlideLayout", Description = "For Powerpoint: Slide layout, changing this to a wrong value will fallback on ppLayoutBlank!!", DefaultValue = "ppLayoutPictureWithCaption")] - public PpSlideLayout PowerpointSlideLayout { get; set; } + [IniProperty("EmailTo", Description = "Default value for the to in emails that are created", DefaultValue = "")] + public string EmailTo { get; set; } - } + [IniProperty("EmailCC", Description = "Default value for the CC in emails that are created", DefaultValue = "")] + public string EmailCC { get; set; } + + [IniProperty("EmailBCC", Description = "Default value for the BCC in emails that are created", DefaultValue = "")] + public string EmailBCC { get; set; } + + [IniProperty("OutlookAllowExportInMeetings", Description = "For Outlook: Allow export in meeting items", DefaultValue = "False")] + public bool OutlookAllowExportInMeetings { get; set; } + + [IniProperty("WordLockAspectRatio", Description = "For Word: Lock the aspect ratio of the image", DefaultValue = "True")] + public bool WordLockAspectRatio { get; set; } + + [IniProperty("PowerpointLockAspectRatio", Description = "For Powerpoint: Lock the aspect ratio of the image", DefaultValue = "True")] + public bool PowerpointLockAspectRatio { get; set; } + + [IniProperty("PowerpointSlideLayout", Description = "For Powerpoint: Slide layout, changing this to a wrong value will fallback on ppLayoutBlank!!", + DefaultValue = "ppLayoutPictureWithCaption")] + public PpSlideLayout PowerpointSlideLayout { get; set; } + } } \ No newline at end of file diff --git a/src/Greenshot.Plugin.Office/OfficeExport/Entities/OneNotePage.cs b/src/Greenshot.Plugin.Office/OfficeExport/Entities/OneNotePage.cs index 685fc4e18..2a0904870 100644 --- a/src/Greenshot.Plugin.Office/OfficeExport/Entities/OneNotePage.cs +++ b/src/Greenshot.Plugin.Office/OfficeExport/Entities/OneNotePage.cs @@ -34,6 +34,7 @@ namespace Greenshot.Plugin.Office.OfficeExport.Entities { return string.Format("{0} / {1}", Parent.Name, Name); } + return string.Format("{0} / {1} / {2}", Parent.Parent.Name, Parent.Name, Name); } } diff --git a/src/Greenshot.Plugin.Office/OfficeExport/ExcelExporter.cs b/src/Greenshot.Plugin.Office/OfficeExport/ExcelExporter.cs index f508208f7..4455bfd73 100644 --- a/src/Greenshot.Plugin.Office/OfficeExport/ExcelExporter.cs +++ b/src/Greenshot.Plugin.Office/OfficeExport/ExcelExporter.cs @@ -53,10 +53,12 @@ namespace Greenshot.Plugin.Office.OfficeExport // Ignore, probably no excel running return null; } + if (excelApplication?.ComObject != null) { InitializeVariables(excelApplication); } + return excelApplication; } @@ -71,6 +73,7 @@ namespace Greenshot.Plugin.Office.OfficeExport { excelApplication = DisposableCom.Create(new Application()); } + InitializeVariables(excelApplication); return excelApplication; } @@ -108,10 +111,11 @@ namespace Greenshot.Plugin.Office.OfficeExport { return; } + if (!Version.TryParse(excelApplication.ComObject.Version, out _excelVersion)) { LOG.Warn("Assuming Excel version 1997."); - _excelVersion = new Version((int)OfficeVersions.Office97, 0, 0, 0); + _excelVersion = new Version((int) OfficeVersions.Office97, 0, 0, 0); } } @@ -132,7 +136,7 @@ namespace Greenshot.Plugin.Office.OfficeExport using var workbooks = DisposableCom.Create(excelApplication.ComObject.Workbooks); for (int i = 1; i <= workbooks.ComObject.Count; i++) { - using var workbook = DisposableCom.Create((_Workbook)workbooks.ComObject[i]); + using var workbook = DisposableCom.Create((_Workbook) workbooks.ComObject[i]); if ((workbook != null) && workbook.ComObject.Name.StartsWith(workbookName)) { InsertIntoExistingWorkbook(workbook, tmpFile, imageSize); @@ -191,9 +195,8 @@ namespace Greenshot.Plugin.Office.OfficeExport excelApplication.ComObject.Visible = true; using var workbooks = DisposableCom.Create(excelApplication.ComObject.Workbooks); - using var workbook = DisposableCom.Create((_Workbook)workbooks.ComObject.Add()); + using var workbook = DisposableCom.Create((_Workbook) workbooks.ComObject.Add()); InsertIntoExistingWorkbook(workbook, tmpFile, imageSize); } } - } \ No newline at end of file diff --git a/src/Greenshot.Plugin.Office/OfficeExport/OneNoteExporter.cs b/src/Greenshot.Plugin.Office/OfficeExport/OneNoteExporter.cs index 4936315bc..f42622356 100644 --- a/src/Greenshot.Plugin.Office/OfficeExport/OneNoteExporter.cs +++ b/src/Greenshot.Plugin.Office/OfficeExport/OneNoteExporter.cs @@ -38,7 +38,10 @@ namespace Greenshot.Plugin.Office.OfficeExport public class OneNoteExporter { private const string XmlImageContent = "{0}"; - private const string XmlOutline = "{0}"; + + private const string XmlOutline = + "{0}"; + private const string OnenoteNamespace2010 = "http://schemas.microsoft.com/office/onenote/2010/onenote"; private static readonly log4net.ILog LOG = log4net.LogManager.GetLogger(typeof(OneNoteExporter)); @@ -107,6 +110,7 @@ namespace Greenshot.Plugin.Office.OfficeExport { LOG.Warn("Unable to navigate to the target page", ex); } + return true; } @@ -126,6 +130,7 @@ namespace Greenshot.Plugin.Office.OfficeExport // Ignore, probably no OneNote running return null; } + return oneNoteApplication; } @@ -140,6 +145,7 @@ namespace Greenshot.Plugin.Office.OfficeExport { oneNoteApplication = DisposableCom.Create(new Application()); } + return oneNoteApplication; } @@ -183,6 +189,7 @@ namespace Greenshot.Plugin.Office.OfficeExport }; } } + if ("one:Section".Equals(xmlReader.Name)) { string id = xmlReader.GetAttribute("ID"); @@ -196,6 +203,7 @@ namespace Greenshot.Plugin.Office.OfficeExport }; } } + if ("one:Page".Equals(xmlReader.Name)) { // Skip deleted items @@ -214,6 +222,7 @@ namespace Greenshot.Plugin.Office.OfficeExport { continue; } + page.IsCurrentlyViewed = "true".Equals(xmlReader.GetAttribute("isCurrentlyViewed")); pages.Add(page); } @@ -231,22 +240,26 @@ namespace Greenshot.Plugin.Office.OfficeExport } catch (COMException cEx) { - if (cEx.ErrorCode == unchecked((int)0x8002801D)) + if (cEx.ErrorCode == unchecked((int) 0x8002801D)) { - LOG.Warn("Wrong registry keys, to solve this remove the OneNote key as described here: http://microsoftmercenary.com/wp/outlook-excel-interop-calls-breaking-solved/"); + LOG.Warn( + "Wrong registry keys, to solve this remove the OneNote key as described here: http://microsoftmercenary.com/wp/outlook-excel-interop-calls-breaking-solved/"); } + LOG.Warn("Problem retrieving onenote destinations, ignoring: ", cEx); } catch (Exception ex) { LOG.Warn("Problem retrieving onenote destinations, ignoring: ", ex); } + pages.Sort((page1, page2) => { if (page1.IsCurrentlyViewed || page2.IsCurrentlyViewed) { return page2.IsCurrentlyViewed.CompareTo(page1.IsCurrentlyViewed); } + return string.Compare(page1.DisplayName, page2.DisplayName, StringComparison.Ordinal); }); return pages; @@ -264,6 +277,7 @@ namespace Greenshot.Plugin.Office.OfficeExport { return null; } + // ReSharper disable once RedundantAssignment string unfiledNotesPath = ""; oneNoteApplication.ComObject.GetSpecialLocation(specialLocation, out unfiledNotesPath); @@ -285,6 +299,7 @@ namespace Greenshot.Plugin.Office.OfficeExport { continue; } + string id = xmlReader.GetAttribute("ID"); string path = xmlReader.GetAttribute("path"); if (unfiledNotesPath.Equals(path)) @@ -301,6 +316,7 @@ namespace Greenshot.Plugin.Office.OfficeExport } } } + return null; } } diff --git a/src/Greenshot.Plugin.Office/OfficeExport/OutlookEmailExporter.cs b/src/Greenshot.Plugin.Office/OfficeExport/OutlookEmailExporter.cs index 93fc68567..55cad605e 100644 --- a/src/Greenshot.Plugin.Office/OfficeExport/OutlookEmailExporter.cs +++ b/src/Greenshot.Plugin.Office/OfficeExport/OutlookEmailExporter.cs @@ -47,7 +47,9 @@ namespace Greenshot.Plugin.Office.OfficeExport private const string ProfilesKey = @"Software\Microsoft\Windows NT\CurrentVersion\Windows Messaging Subsystem\Profiles\"; private const string AccountKey = "9375CFF0413111d3B88A00104B2A6676"; private const string NewSignatureValue = "New Signature"; + private const string DefaultProfileValue = "DefaultProfile"; + // Schema definitions for the MAPI properties, see: http://msdn.microsoft.com/en-us/library/aa454438.aspx and: http://msdn.microsoft.com/en-us/library/bb446117.aspx private const string AttachmentContentId = @"http://schemas.microsoft.com/mapi/proptag/0x3712001E"; @@ -73,10 +75,10 @@ namespace Greenshot.Plugin.Office.OfficeExport } // The activeexplorer inline response only works with >= 2013, Microsoft Outlook 15.0 Object Library - if (_outlookVersion.Major >= (int)OfficeVersions.Office2013) + if (_outlookVersion.Major >= (int) OfficeVersions.Office2013) { // Check inline "panel" for Outlook 2013 - using var activeExplorer = DisposableCom.Create((_Explorer)outlookApplication.ComObject.ActiveExplorer()); + using var activeExplorer = DisposableCom.Create((_Explorer) outlookApplication.ComObject.ActiveExplorer()); // Only if we have one and if the capture is the one we selected if ((activeExplorer != null) && activeExplorer.ComObject.Caption.StartsWith(inspectorCaption)) { @@ -90,15 +92,17 @@ namespace Greenshot.Plugin.Office.OfficeExport { return ExportToInspector(null, activeExplorer, mailItem.Class, mailItem, tmpFile, attachmentName); } + break; case AppointmentItem appointmentItem: - if ((_outlookVersion.Major >= (int)OfficeVersions.Office2010) && _officeConfiguration.OutlookAllowExportInMeetings) + if ((_outlookVersion.Major >= (int) OfficeVersions.Office2010) && _officeConfiguration.OutlookAllowExportInMeetings) { if (!string.IsNullOrEmpty(appointmentItem.Organizer) && appointmentItem.Organizer.Equals(_currentUser)) { return ExportToInspector(null, activeExplorer, appointmentItem.Class, null, tmpFile, attachmentName); } } + break; } } @@ -110,10 +114,11 @@ namespace Greenshot.Plugin.Office.OfficeExport { return false; } + LOG.DebugFormat("Got {0} inspectors to check", inspectors.ComObject.Count); for (int i = 1; i <= inspectors.ComObject.Count; i++) { - using var inspector = DisposableCom.Create((_Inspector)inspectors.ComObject[i]); + using var inspector = DisposableCom.Create((_Inspector) inspectors.ComObject[i]); string currentCaption = inspector.ComObject.Caption; if (!currentCaption.StartsWith(inspectorCaption)) { @@ -130,6 +135,7 @@ namespace Greenshot.Plugin.Office.OfficeExport { continue; } + try { return ExportToInspector(inspector, null, mailItem.Class, mailItem, tmpFile, attachmentName); @@ -138,9 +144,10 @@ namespace Greenshot.Plugin.Office.OfficeExport { LOG.Error($"Export to {currentCaption} failed.", exExport); } + break; case AppointmentItem appointmentItem: - if ((_outlookVersion.Major >= (int)OfficeVersions.Office2010) && _officeConfiguration.OutlookAllowExportInMeetings) + if ((_outlookVersion.Major >= (int) OfficeVersions.Office2010) && _officeConfiguration.OutlookAllowExportInMeetings) { if (!string.IsNullOrEmpty(appointmentItem.Organizer) && !appointmentItem.Organizer.Equals(_currentUser)) { @@ -153,6 +160,7 @@ namespace Greenshot.Plugin.Office.OfficeExport // skip, can't export to olAppointment continue; } + try { return ExportToInspector(inspector, null, appointmentItem.Class, null, tmpFile, attachmentName); @@ -161,6 +169,7 @@ namespace Greenshot.Plugin.Office.OfficeExport { LOG.Error($"Export to {currentCaption} failed.", exExport); } + break; default: continue; @@ -168,6 +177,7 @@ namespace Greenshot.Plugin.Office.OfficeExport } } } + return false; } @@ -181,7 +191,8 @@ namespace Greenshot.Plugin.Office.OfficeExport /// /// /// - private bool ExportToInspector(IDisposableCom<_Inspector> inspector, IDisposableCom<_Explorer> explorer, OlObjectClass itemClass, MailItem mailItem, string tmpFile, string attachmentName) + private bool ExportToInspector(IDisposableCom<_Inspector> inspector, IDisposableCom<_Explorer> explorer, OlObjectClass itemClass, MailItem mailItem, string tmpFile, + string attachmentName) { bool isMail = OlObjectClass.olMail.Equals(itemClass); bool isAppointment = OlObjectClass.olAppointment.Equals(itemClass); @@ -190,6 +201,7 @@ namespace Greenshot.Plugin.Office.OfficeExport LOG.Warn("Item is no mail or appointment."); return false; } + try { // Make sure the inspector is activated, only this way the word editor is active! @@ -200,25 +212,27 @@ namespace Greenshot.Plugin.Office.OfficeExport { isTextFormat = OlBodyFormat.olFormatPlain.Equals(mailItem.BodyFormat); } + if (isAppointment || !isTextFormat) { // Check for wordmail, if so use the wordexporter // http://msdn.microsoft.com/en-us/library/dd492012%28v=office.12%29.aspx // Earlier versions of Outlook also supported an Inspector.HTMLEditor object property, but since Internet Explorer is no longer the rendering engine for HTML messages and posts, HTMLEditor is no longer supported. IDisposableCom<_Document> wordDocument = null; - if ((explorer != null) && (_outlookVersion.Major >= (int)OfficeVersions.Office2013)) + if ((explorer != null) && (_outlookVersion.Major >= (int) OfficeVersions.Office2013)) { // TODO: Needs to have the Microsoft Outlook 15.0 Object Library installed - wordDocument = DisposableCom.Create((_Document)explorer.ComObject.ActiveInlineResponseWordEditor); + wordDocument = DisposableCom.Create((_Document) explorer.ComObject.ActiveInlineResponseWordEditor); } else if (inspector != null) { if (inspector.ComObject.IsWordMail() && (inspector.ComObject.EditorType == OlEditorType.olEditorWord)) { - var tmpWordDocument = (_Document)inspector.ComObject.WordEditor; + var tmpWordDocument = (_Document) inspector.ComObject.WordEditor; wordDocument = DisposableCom.Create(tmpWordDocument); } } + if (wordDocument != null) { using (wordDocument) @@ -248,6 +262,7 @@ namespace Greenshot.Plugin.Office.OfficeExport LOG.Info("Trying export for outlook < 2007."); } } + // Only use mailitem as it should be filled!! if (mailItem != null) { @@ -255,7 +270,7 @@ namespace Greenshot.Plugin.Office.OfficeExport } string contentId; - if (_outlookVersion.Major >= (int)OfficeVersions.Office2007) + if (_outlookVersion.Major >= (int) OfficeVersions.Office2007) { contentId = Guid.NewGuid().ToString(); } @@ -283,7 +298,7 @@ namespace Greenshot.Plugin.Office.OfficeExport var selection = document2.selection; if (selection != null) { - var range = (IHTMLTxtRange)selection.createRange(); + var range = (IHTMLTxtRange) selection.createRange(); if (range != null) { // First paste, than attach (otherwise the range is wrong!) @@ -315,7 +330,7 @@ namespace Greenshot.Plugin.Office.OfficeExport // Create the attachment (if inlined the attachment isn't visible as attachment!) using var attachments = DisposableCom.Create(mailItem.Attachments); using var attachment = DisposableCom.Create(attachments.ComObject.Add(tmpFile, OlAttachmentType.olByValue, inlinePossible ? 0 : 1, attachmentName)); - if (_outlookVersion.Major >= (int)OfficeVersions.Office2007) + if (_outlookVersion.Major >= (int) OfficeVersions.Office2007) { // Add the content id to the attachment, this only works for Outlook >= 2007 try @@ -341,9 +356,11 @@ namespace Greenshot.Plugin.Office.OfficeExport { caption = explorer.ComObject.Caption; } + LOG.Warn($"Problem while trying to add attachment to Item '{caption}'", ex); return false; } + try { if (inspector != null) @@ -360,6 +377,7 @@ namespace Greenshot.Plugin.Office.OfficeExport LOG.Warn("Problem activating inspector/explorer: ", ex); return false; } + LOG.Debug("Finished!"); return true; } @@ -376,27 +394,32 @@ namespace Greenshot.Plugin.Office.OfficeExport /// /// /// - private void ExportToNewEmail(IDisposableCom outlookApplication, EmailFormat format, string tmpFile, string subject, string attachmentName, string to, string cc, string bcc, string url) + private void ExportToNewEmail(IDisposableCom outlookApplication, EmailFormat format, string tmpFile, string subject, string attachmentName, string to, + string cc, string bcc, string url) { - using var newItem = DisposableCom.Create((MailItem)outlookApplication.ComObject.CreateItem(OlItemType.olMailItem)); + using var newItem = DisposableCom.Create((MailItem) outlookApplication.ComObject.CreateItem(OlItemType.olMailItem)); if (newItem == null) { return; } + var newMail = newItem.ComObject; newMail.Subject = subject; if (!string.IsNullOrEmpty(to)) { newMail.To = to; } + if (!string.IsNullOrEmpty(cc)) { newMail.CC = cc; } + if (!string.IsNullOrEmpty(bcc)) { newMail.BCC = bcc; } + newMail.BodyFormat = OlBodyFormat.olFormatHTML; string bodyString = null; // Read the default signature, if nothing found use empty email @@ -408,6 +431,7 @@ namespace Greenshot.Plugin.Office.OfficeExport { LOG.Error("Problem reading signature!", e); } + switch (format) { case EmailFormat.Text: @@ -421,9 +445,11 @@ namespace Greenshot.Plugin.Office.OfficeExport { bodyString = ""; } + newMail.Body = bodyString; } } + break; default: string contentId = Path.GetFileName(tmpFile); @@ -432,7 +458,7 @@ namespace Greenshot.Plugin.Office.OfficeExport { using var attachment = DisposableCom.Create(attachments.ComObject.Add(tmpFile, OlAttachmentType.olByValue, 0, attachmentName)); // add content ID to the attachment - if (_outlookVersion.Major >= (int)OfficeVersions.Office2007) + if (_outlookVersion.Major >= (int) OfficeVersions.Office2007) { try { @@ -456,6 +482,7 @@ namespace Greenshot.Plugin.Office.OfficeExport href = $""; hrefEnd = ""; } + string htmlImgEmbedded = $"
{href}\"{attachmentName}\"{hrefEnd}
"; string fallbackBody = $"{htmlImgEmbedded}"; if (bodyString == null) @@ -482,13 +509,15 @@ namespace Greenshot.Plugin.Office.OfficeExport bodyString = fallbackBody; } } + newMail.HTMLBody = bodyString; break; } + // So not save, otherwise the email is always stored in Draft folder.. (newMail.Save();) newMail.Display(false); - using var inspector = DisposableCom.Create((_Inspector)newMail.GetInspector); + using var inspector = DisposableCom.Create((_Inspector) newMail.GetInspector); if (inspector != null) { try @@ -528,12 +557,14 @@ namespace Greenshot.Plugin.Office.OfficeExport exported = true; } } + return exported; } catch (Exception e) { LOG.Error("Error while creating an outlook mail item: ", e); } + return exported; } @@ -548,6 +579,7 @@ namespace Greenshot.Plugin.Office.OfficeExport { outlookApplication = DisposableCom.Create(new Application()); } + InitializeVariables(outlookApplication); return outlookApplication; } @@ -568,10 +600,12 @@ namespace Greenshot.Plugin.Office.OfficeExport // Ignore, probably no outlook running return null; } + if ((outlookApplication != null) && (outlookApplication.ComObject != null)) { InitializeVariables(outlookApplication); } + return outlookApplication; } @@ -587,7 +621,8 @@ namespace Greenshot.Plugin.Office.OfficeExport { return null; } - string defaultProfile = (string)profilesKey.GetValue(DefaultProfileValue); + + string defaultProfile = (string) profilesKey.GetValue(DefaultProfileValue); LOG.DebugFormat("defaultProfile={0}", defaultProfile); using var profileKey = profilesKey.OpenSubKey(defaultProfile + @"\" + AccountKey, false); if (profileKey != null) @@ -599,19 +634,21 @@ namespace Greenshot.Plugin.Office.OfficeExport using var numberKey = profileKey.OpenSubKey(number, false); if (numberKey != null) { - byte[] val = (byte[])numberKey.GetValue(NewSignatureValue); + byte[] val = (byte[]) numberKey.GetValue(NewSignatureValue); if (val == null) { continue; } + string signatureName = ""; foreach (byte b in val) { if (b != 0) { - signatureName += (char)b; + signatureName += (char) b; } } + LOG.DebugFormat("Found email signature: {0}", signatureName); var extension = format switch { @@ -628,6 +665,7 @@ namespace Greenshot.Plugin.Office.OfficeExport } } } + return null; } @@ -642,13 +680,15 @@ namespace Greenshot.Plugin.Office.OfficeExport { return; } + if (!Version.TryParse(outlookApplication.ComObject.Version, out _outlookVersion)) { LOG.Warn("Assuming outlook version 1997."); - _outlookVersion = new Version((int)OfficeVersions.Office97, 0, 0, 0); + _outlookVersion = new Version((int) OfficeVersions.Office97, 0, 0, 0); } + // Preventing retrieval of currentUser if Outlook is older than 2007 - if (_outlookVersion.Major >= (int)OfficeVersions.Office2007) + if (_outlookVersion.Major >= (int) OfficeVersions.Office2007) { try { @@ -657,6 +697,7 @@ namespace Greenshot.Plugin.Office.OfficeExport using var currentUser = DisposableCom.Create(mapiNamespace.ComObject.CurrentUser); _currentUser = currentUser.ComObject.Name; } + LOG.InfoFormat("Current user: {0}", _currentUser); } catch (Exception exNs) @@ -682,7 +723,7 @@ namespace Greenshot.Plugin.Office.OfficeExport } // The activeexplorer inline response only works with >= 2013, Microsoft Outlook 15.0 Object Library - if (_outlookVersion.Major >= (int)OfficeVersions.Office2013) + if (_outlookVersion.Major >= (int) OfficeVersions.Office2013) { // Check inline "panel" for Outlook 2013 using var activeExplorer = DisposableCom.Create(outlookApplication.ComObject.ActiveExplorer()); @@ -701,15 +742,17 @@ namespace Greenshot.Plugin.Office.OfficeExport { inspectorCaptions.Add(caption, mailItem.Class); } + break; case AppointmentItem appointmentItem: - if ((_outlookVersion.Major >= (int)OfficeVersions.Office2010) && _officeConfiguration.OutlookAllowExportInMeetings) + if ((_outlookVersion.Major >= (int) OfficeVersions.Office2010) && _officeConfiguration.OutlookAllowExportInMeetings) { if (!string.IsNullOrEmpty(appointmentItem.Organizer) && appointmentItem.Organizer.Equals(_currentUser)) { inspectorCaptions.Add(caption, appointmentItem.Class); } } + break; } } @@ -740,10 +783,11 @@ namespace Greenshot.Plugin.Office.OfficeExport { continue; } + inspectorCaptions.Add(caption, mailItem.Class); break; case AppointmentItem appointmentItem: - if ((_outlookVersion.Major >= (int)OfficeVersions.Office2010) && _officeConfiguration.OutlookAllowExportInMeetings) + if ((_outlookVersion.Major >= (int) OfficeVersions.Office2010) && _officeConfiguration.OutlookAllowExportInMeetings) { if (!string.IsNullOrEmpty(appointmentItem.Organizer) && !appointmentItem.Organizer.Equals(_currentUser)) { @@ -756,6 +800,7 @@ namespace Greenshot.Plugin.Office.OfficeExport // skip, can't export to olAppointment continue; } + inspectorCaptions.Add(caption, appointmentItem.Class); break; default: @@ -769,6 +814,7 @@ namespace Greenshot.Plugin.Office.OfficeExport { LOG.Warn("Problem retrieving word destinations, ignoring: ", ex); } + return inspectorCaptions; } } diff --git a/src/Greenshot.Plugin.Office/OfficeExport/PowerpointExporter.cs b/src/Greenshot.Plugin.Office/OfficeExport/PowerpointExporter.cs index aaa473ddf..c2bc7e2b3 100644 --- a/src/Greenshot.Plugin.Office/OfficeExport/PowerpointExporter.cs +++ b/src/Greenshot.Plugin.Office/OfficeExport/PowerpointExporter.cs @@ -60,6 +60,7 @@ namespace Greenshot.Plugin.Office.OfficeExport left = pageSetup.ComObject.SlideWidth / 2 - imageSize.Width / 2f; top = pageSetup.ComObject.SlideHeight / 2 - imageSize.Height / 2f; } + float width = imageSize.Width; float height = imageSize.Height; IDisposableCom shapeForCaption = null; @@ -86,6 +87,7 @@ namespace Greenshot.Plugin.Office.OfficeExport { shapeForLocation.ComObject.Left = left; } + shapeForLocation.ComObject.Width = imageSize.Width; if (height > shapeForLocation.ComObject.Height) @@ -98,6 +100,7 @@ namespace Greenshot.Plugin.Office.OfficeExport { top = shapeForLocation.ComObject.Top + shapeForLocation.ComObject.Height / 2 - imageSize.Height / 2f; } + shapeForLocation.ComObject.Height = imageSize.Height; } catch (Exception e) @@ -106,6 +109,7 @@ namespace Greenshot.Plugin.Office.OfficeExport using var slides = DisposableCom.Create(presentation.ComObject.Slides); slide = DisposableCom.Create(slides.ComObject.Add(slides.ComObject.Count + 1, PpSlideLayout.ppLayoutBlank)); } + using (var shapes = DisposableCom.Create(slide.ComObject.Shapes)) { using var shape = DisposableCom.Create(shapes.ComObject.AddPicture(tmpFile, MsoTriState.msoFalse, MsoTriState.msoTrue, 0, 0, width, height)); @@ -117,20 +121,24 @@ namespace Greenshot.Plugin.Office.OfficeExport { shape.ComObject.LockAspectRatio = MsoTriState.msoFalse; } + shape.ComObject.ScaleHeight(1, MsoTriState.msoTrue, MsoScaleFrom.msoScaleFromMiddle); shape.ComObject.ScaleWidth(1, MsoTriState.msoTrue, MsoScaleFrom.msoScaleFromMiddle); if (hasScaledWidth) { shape.ComObject.Width = width; } + if (hasScaledHeight) { shape.ComObject.Height = height; } + shape.ComObject.Left = left; shape.ComObject.Top = top; shape.ComObject.AlternativeText = title; } + if (shapeForCaption != null) { try @@ -148,6 +156,7 @@ namespace Greenshot.Plugin.Office.OfficeExport LOG.Warn("Problem setting the title to a text-range", ex); } } + // Activate/Goto the slide try { @@ -194,10 +203,12 @@ namespace Greenshot.Plugin.Office.OfficeExport { continue; } + if (!presentation.ComObject.Name.StartsWith(presentationName)) { continue; } + try { AddPictureToPresentation(presentation, tmpFile, imageSize, title); @@ -209,6 +220,7 @@ namespace Greenshot.Plugin.Office.OfficeExport } } } + return false; } @@ -223,6 +235,7 @@ namespace Greenshot.Plugin.Office.OfficeExport { powerPointApplication = DisposableCom.Create(new Application()); } + InitializeVariables(powerPointApplication); return powerPointApplication; } @@ -243,10 +256,12 @@ namespace Greenshot.Plugin.Office.OfficeExport // Ignore, probably no PowerPoint running return null; } + if (powerPointApplication?.ComObject != null) { InitializeVariables(powerPointApplication); } + return powerPointApplication; } @@ -271,10 +286,12 @@ namespace Greenshot.Plugin.Office.OfficeExport { continue; } + if (presentation.ComObject.ReadOnly == MsoTriState.msoTrue) { continue; } + if (IsAfter2003()) { if (presentation.ComObject.Final) @@ -282,6 +299,7 @@ namespace Greenshot.Plugin.Office.OfficeExport continue; } } + yield return presentation.ComObject.Name; } } @@ -296,10 +314,11 @@ namespace Greenshot.Plugin.Office.OfficeExport { return; } + if (!Version.TryParse(powerpointApplication.ComObject.Version, out _powerpointVersion)) { LOG.Warn("Assuming Powerpoint version 1997."); - _powerpointVersion = new Version((int)OfficeVersions.Office97, 0, 0, 0); + _powerpointVersion = new Version((int) OfficeVersions.Office97, 0, 0, 0); } } @@ -332,13 +351,13 @@ namespace Greenshot.Plugin.Office.OfficeExport } } } + return isPictureAdded; } private bool IsAfter2003() { - return _powerpointVersion.Major > (int)OfficeVersions.Office2003; + return _powerpointVersion.Major > (int) OfficeVersions.Office2003; } } - } \ No newline at end of file diff --git a/src/Greenshot.Plugin.Office/OfficeExport/WordExporter.cs b/src/Greenshot.Plugin.Office/OfficeExport/WordExporter.cs index ce4da64d2..0cef0c696 100644 --- a/src/Greenshot.Plugin.Office/OfficeExport/WordExporter.cs +++ b/src/Greenshot.Plugin.Office/OfficeExport/WordExporter.cs @@ -53,6 +53,7 @@ namespace Greenshot.Plugin.Office.OfficeExport { shape.ComObject.LockAspectRatio = MsoTriState.msoTrue; } + selection.ComObject.InsertAfter("\r\n"); selection.ComObject.MoveDown(WdUnits.wdLine, 1, Type.Missing); return shape; @@ -69,6 +70,7 @@ namespace Greenshot.Plugin.Office.OfficeExport { wordApplication = DisposableCom.Create(new Application()); } + InitializeVariables(wordApplication); return wordApplication; } @@ -89,10 +91,12 @@ namespace Greenshot.Plugin.Office.OfficeExport // Ignore, probably no word running return null; } + if ((wordApplication != null) && (wordApplication.ComObject != null)) { InitializeVariables(wordApplication); } + return wordApplication; } @@ -116,6 +120,7 @@ namespace Greenshot.Plugin.Office.OfficeExport { continue; } + if (IsAfter2003()) { if (document.ComObject.Final) @@ -139,10 +144,11 @@ namespace Greenshot.Plugin.Office.OfficeExport { return; } + if (!Version.TryParse(wordApplication.ComObject.Version, out _wordVersion)) { LOG.Warn("Assuming Word version 1997."); - _wordVersion = new Version((int)OfficeVersions.Office97, 0, 0, 0); + _wordVersion = new Version((int) OfficeVersions.Office97, 0, 0, 0); } } @@ -164,7 +170,7 @@ namespace Greenshot.Plugin.Office.OfficeExport using var documents = DisposableCom.Create(wordApplication.ComObject.Documents); for (int i = 1; i <= documents.ComObject.Count; i++) { - using var wordDocument = DisposableCom.Create((_Document)documents.ComObject[i]); + using var wordDocument = DisposableCom.Create((_Document) documents.ComObject[i]); using var activeWindow = DisposableCom.Create(wordDocument.ComObject.ActiveWindow); if (activeWindow.ComObject.Caption.StartsWith(wordCaption)) { @@ -172,6 +178,7 @@ namespace Greenshot.Plugin.Office.OfficeExport } } } + return false; } @@ -184,7 +191,8 @@ namespace Greenshot.Plugin.Office.OfficeExport /// string /// string with the tooltip of the image /// bool - internal bool InsertIntoExistingDocument(IDisposableCom wordApplication, IDisposableCom<_Document> wordDocument, string tmpFile, string address, string tooltip) + internal bool InsertIntoExistingDocument(IDisposableCom wordApplication, IDisposableCom<_Document> wordDocument, string tmpFile, string address, + string tooltip) { // Bug #1517: image will be inserted into that document, where the focus was last. It will not inserted into the chosen one. // Solution: Make sure the selected document is active, otherwise the insert will be made in a different document! @@ -204,6 +212,7 @@ namespace Greenshot.Plugin.Office.OfficeExport LOG.InfoFormat("No selection to insert {0} into found.", tmpFile); return false; } + // Add Picture using (var shape = AddPictureToSelection(selection, tmpFile)) { @@ -214,6 +223,7 @@ namespace Greenshot.Plugin.Office.OfficeExport { screentip = tooltip; } + try { using var hyperlinks = DisposableCom.Create(wordDocument.ComObject.Hyperlinks); @@ -225,6 +235,7 @@ namespace Greenshot.Plugin.Office.OfficeExport } } } + try { // When called for Outlook, the follow error is created: This object model command is not available in e-mail @@ -245,6 +256,7 @@ namespace Greenshot.Plugin.Office.OfficeExport LOG.WarnFormat("Couldn't set zoom to 100, error: {0}", e.Message); } } + try { wordApplication.ComObject.Activate(); @@ -254,6 +266,7 @@ namespace Greenshot.Plugin.Office.OfficeExport { LOG.Warn("Error activating word application", ex); } + try { wordDocument.ComObject.Activate(); @@ -263,6 +276,7 @@ namespace Greenshot.Plugin.Office.OfficeExport { LOG.Warn("Error activating word document", ex); } + return true; } @@ -279,6 +293,7 @@ namespace Greenshot.Plugin.Office.OfficeExport { return; } + wordApplication.ComObject.Visible = true; wordApplication.ComObject.Activate(); // Create new Document @@ -299,6 +314,7 @@ namespace Greenshot.Plugin.Office.OfficeExport { screentip = tooltip; } + try { using var hyperlinks = DisposableCom.Create(wordDocument.ComObject.Hyperlinks); @@ -313,6 +329,7 @@ namespace Greenshot.Plugin.Office.OfficeExport } } } + try { wordDocument.ComObject.Activate(); @@ -322,6 +339,7 @@ namespace Greenshot.Plugin.Office.OfficeExport { LOG.Warn("Error activating word document", ex); } + try { using var activeWindow = DisposableCom.Create(wordDocument.ComObject.ActiveWindow); @@ -340,7 +358,7 @@ namespace Greenshot.Plugin.Office.OfficeExport /// private bool IsAfter2003() { - return _wordVersion.Major > (int)OfficeVersions.Office2003; + return _wordVersion.Major > (int) OfficeVersions.Office2003; } } } \ No newline at end of file diff --git a/src/Greenshot.Plugin.Office/OfficeInterop/EmailFormat.cs b/src/Greenshot.Plugin.Office/OfficeInterop/EmailFormat.cs index 55c347c5e..ea78fce8b 100644 --- a/src/Greenshot.Plugin.Office/OfficeInterop/EmailFormat.cs +++ b/src/Greenshot.Plugin.Office/OfficeInterop/EmailFormat.cs @@ -19,18 +19,19 @@ namespace Greenshot.Plugin.Office.OfficeInterop { - /// - /// Specifies which EmailFormat the email needs to use - /// - public enum EmailFormat - { + /// + /// Specifies which EmailFormat the email needs to use + /// + public enum EmailFormat + { /// /// Use the plain text format /// - Text, + Text, + /// /// Use HTML format /// - Html - } + Html + } } \ No newline at end of file diff --git a/src/Greenshot.Plugin.Office/OfficeInterop/OfficeVersions.cs b/src/Greenshot.Plugin.Office/OfficeInterop/OfficeVersions.cs index ef74d6f9a..321e6de2f 100644 --- a/src/Greenshot.Plugin.Office/OfficeInterop/OfficeVersions.cs +++ b/src/Greenshot.Plugin.Office/OfficeInterop/OfficeVersions.cs @@ -19,38 +19,44 @@ namespace Greenshot.Plugin.Office.OfficeInterop { - /// - /// A mapping between the version and a usable name - /// - public enum OfficeVersions - { + /// + /// A mapping between the version and a usable name + /// + public enum OfficeVersions + { /// /// Office 97 /// - Office97 = 8, + Office97 = 8, + /// /// Office 2003 /// - Office2003 = 11, + Office2003 = 11, + /// /// Office 2007 /// - Office2007 = 12, + Office2007 = 12, + /// /// Office 2010 /// - Office2010 = 14, + Office2010 = 14, + /// /// Office 2013 /// - Office2013 = 15, + Office2013 = 15, + /// /// Office 2016 /// Office2016 = 16, + /// /// Office 2019 /// Office2019 = 17 - } + } } \ No newline at end of file diff --git a/src/Greenshot.Plugin.Office/OfficePlugin.cs b/src/Greenshot.Plugin.Office/OfficePlugin.cs index 7abdecf02..c7e75afcd 100644 --- a/src/Greenshot.Plugin.Office/OfficePlugin.cs +++ b/src/Greenshot.Plugin.Office/OfficePlugin.cs @@ -26,91 +26,123 @@ using GreenshotPlugin.Core; using GreenshotPlugin.Interfaces; using GreenshotPlugin.Interfaces.Plugin; -namespace Greenshot.Plugin.Office { - /// - /// This is the OfficePlugin base code - /// +namespace Greenshot.Plugin.Office +{ + /// + /// This is the OfficePlugin base code + /// [Plugin("Office", false)] public class OfficePlugin : IGreenshotPlugin { - private static readonly log4net.ILog LOG = log4net.LogManager.GetLogger(typeof(OfficePlugin)); + private static readonly log4net.ILog LOG = log4net.LogManager.GetLogger(typeof(OfficePlugin)); - public void Dispose() { - Dispose(true); - GC.SuppressFinalize(this); - } + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } - protected void Dispose(bool disposing) { - // Do nothing - } + protected void Dispose(bool disposing) + { + // Do nothing + } - private IEnumerable Destinations() { - IDestination destination; - try { - destination = new ExcelDestination(); - } catch { - destination = null; - } - if (destination != null) { - yield return destination; - } + private IEnumerable Destinations() + { + IDestination destination; + try + { + destination = new ExcelDestination(); + } + catch + { + destination = null; + } - try { - destination = new PowerpointDestination(); - } catch { - destination = null; - } - if (destination != null) { - yield return destination; - } + if (destination != null) + { + yield return destination; + } - try { - destination = new WordDestination(); - } catch { - destination = null; - } - if (destination != null) { - yield return destination; - } + try + { + destination = new PowerpointDestination(); + } + catch + { + destination = null; + } - try { - destination = new OutlookDestination(); - } catch { - destination = null; - } - if (destination != null) { - yield return destination; - } + if (destination != null) + { + yield return destination; + } - try { - destination = new OneNoteDestination(); - } catch { - destination = null; - } - if (destination != null) { - yield return destination; - } - } + try + { + destination = new WordDestination(); + } + catch + { + destination = null; + } + + if (destination != null) + { + yield return destination; + } + + try + { + destination = new OutlookDestination(); + } + catch + { + destination = null; + } + + if (destination != null) + { + yield return destination; + } + + try + { + destination = new OneNoteDestination(); + } + catch + { + destination = null; + } + + if (destination != null) + { + yield return destination; + } + } - /// - /// Implementation of the IGreenshotPlugin.Initialize - /// - /// true if plugin is initialized, false if not (doesn't show) - public bool Initialize() { + /// + /// Implementation of the IGreenshotPlugin.Initialize + /// + /// true if plugin is initialized, false if not (doesn't show) + public bool Initialize() + { SimpleServiceProvider.Current.AddService(Destinations()); - return true; - } - - public void Shutdown() { - LOG.Debug("Office Plugin shutdown."); - } + return true; + } - /// - /// Implementation of the IPlugin.Configure - /// - public void Configure() { + public void Shutdown() + { + LOG.Debug("Office Plugin shutdown."); + } + + /// + /// Implementation of the IPlugin.Configure + /// + public void Configure() + { throw new NotImplementedException(); - } - } -} + } + } +} \ No newline at end of file diff --git a/src/Greenshot.Plugin.Office/Properties/AssemblyInfo.cs b/src/Greenshot.Plugin.Office/Properties/AssemblyInfo.cs index 3e2563a07..a65ac387f 100644 --- a/src/Greenshot.Plugin.Office/Properties/AssemblyInfo.cs +++ b/src/Greenshot.Plugin.Office/Properties/AssemblyInfo.cs @@ -29,4 +29,4 @@ using System.Runtime.InteropServices; // This sets the default COM visibility of types in the assembly to invisible. // If you need to expose a type to COM, use [ComVisible(true)] on that type. -[assembly: ComVisible(false)] +[assembly: ComVisible(false)] \ No newline at end of file diff --git a/src/Greenshot.Plugin.Photobucket/Forms/PhotobucketForm.cs b/src/Greenshot.Plugin.Photobucket/Forms/PhotobucketForm.cs index 09e3bc3ca..6bf170ca5 100644 --- a/src/Greenshot.Plugin.Photobucket/Forms/PhotobucketForm.cs +++ b/src/Greenshot.Plugin.Photobucket/Forms/PhotobucketForm.cs @@ -19,10 +19,12 @@ * along with this program. If not, see . */ -namespace Greenshot.Plugin.Photobucket.Forms { - /// - /// This class is needed for design-time resolving of the language files - /// - public class PhotobucketForm : GreenshotPlugin.Controls.GreenshotForm { - } -} +namespace Greenshot.Plugin.Photobucket.Forms +{ + /// + /// This class is needed for design-time resolving of the language files + /// + public class PhotobucketForm : GreenshotPlugin.Controls.GreenshotForm + { + } +} \ No newline at end of file diff --git a/src/Greenshot.Plugin.Photobucket/Forms/SettingsForm.cs b/src/Greenshot.Plugin.Photobucket/Forms/SettingsForm.cs index 9d380bdd8..b6a51f8ae 100644 --- a/src/Greenshot.Plugin.Photobucket/Forms/SettingsForm.cs +++ b/src/Greenshot.Plugin.Photobucket/Forms/SettingsForm.cs @@ -19,19 +19,21 @@ * along with this program. If not, see . */ -namespace Greenshot.Plugin.Photobucket.Forms { - /// - /// Description of PasswordRequestForm. - /// - public partial class SettingsForm : PhotobucketForm { - public SettingsForm() - { - // - // The InitializeComponent() call is required for Windows Forms designer support. - // - InitializeComponent(); - AcceptButton = buttonOK; - CancelButton = buttonCancel; - } - } -} +namespace Greenshot.Plugin.Photobucket.Forms +{ + /// + /// Description of PasswordRequestForm. + /// + public partial class SettingsForm : PhotobucketForm + { + public SettingsForm() + { + // + // The InitializeComponent() call is required for Windows Forms designer support. + // + InitializeComponent(); + AcceptButton = buttonOK; + CancelButton = buttonCancel; + } + } +} \ No newline at end of file diff --git a/src/Greenshot.Plugin.Photobucket/LanguageKeys.cs b/src/Greenshot.Plugin.Photobucket/LanguageKeys.cs index 8795b92f0..e9ac6058b 100644 --- a/src/Greenshot.Plugin.Photobucket/LanguageKeys.cs +++ b/src/Greenshot.Plugin.Photobucket/LanguageKeys.cs @@ -19,11 +19,13 @@ * along with this program. If not, see . */ -namespace Greenshot.Plugin.Photobucket { - public enum LangKey { - upload_menu_item, - upload_failure, - communication_wait, - configure - } -} +namespace Greenshot.Plugin.Photobucket +{ + public enum LangKey + { + upload_menu_item, + upload_failure, + communication_wait, + configure + } +} \ No newline at end of file diff --git a/src/Greenshot.Plugin.Photobucket/PhotobucketConfiguration.cs b/src/Greenshot.Plugin.Photobucket/PhotobucketConfiguration.cs index cf2501cd0..4c1c976d2 100644 --- a/src/Greenshot.Plugin.Photobucket/PhotobucketConfiguration.cs +++ b/src/Greenshot.Plugin.Photobucket/PhotobucketConfiguration.cs @@ -25,46 +25,56 @@ using GreenshotPlugin.Controls; using GreenshotPlugin.Core; using GreenshotPlugin.IniFile; -namespace Greenshot.Plugin.Photobucket { - /// - /// Description of PhotobucketConfiguration. - /// - [IniSection("Photobucket", Description="Greenshot Photobucket Plugin configuration")] - public class PhotobucketConfiguration : IniSection { - [IniProperty("UploadFormat", Description="What file type to use for uploading", DefaultValue="png")] - public OutputFormat UploadFormat { get; set; } - [IniProperty("UploadJpegQuality", Description="JPEG file save quality in %.", DefaultValue="80")] - public int UploadJpegQuality { get; set; } - [IniProperty("UploadReduceColors", Description="Reduce color amount of the uploaded image to 256", DefaultValue="False")] - public bool UploadReduceColors { get; set; } - [IniProperty("UsePageLink", Description = "Use pagelink instead of direct link on the clipboard", DefaultValue = "False")] - public bool UsePageLink { get; set; } - [IniProperty("Token", Description = "The Photobucket token", Encrypted=true, ExcludeIfNull=true)] - public string Token { get; set; } - [IniProperty("TokenSecret", Description = "The Photobucket token secret", Encrypted=true, ExcludeIfNull=true)] - public string TokenSecret { get; set; } - [IniProperty("SubDomain", Description = "The Photobucket api subdomain", Encrypted = true, ExcludeIfNull = true)] - public string SubDomain { get; set; } - [IniProperty("Username", Description = "The Photobucket api username", ExcludeIfNull = true)] - public string Username { get; set; } +namespace Greenshot.Plugin.Photobucket +{ + /// + /// Description of PhotobucketConfiguration. + /// + [IniSection("Photobucket", Description = "Greenshot Photobucket Plugin configuration")] + public class PhotobucketConfiguration : IniSection + { + [IniProperty("UploadFormat", Description = "What file type to use for uploading", DefaultValue = "png")] + public OutputFormat UploadFormat { get; set; } + + [IniProperty("UploadJpegQuality", Description = "JPEG file save quality in %.", DefaultValue = "80")] + public int UploadJpegQuality { get; set; } + + [IniProperty("UploadReduceColors", Description = "Reduce color amount of the uploaded image to 256", DefaultValue = "False")] + public bool UploadReduceColors { get; set; } + + [IniProperty("UsePageLink", Description = "Use pagelink instead of direct link on the clipboard", DefaultValue = "False")] + public bool UsePageLink { get; set; } + + [IniProperty("Token", Description = "The Photobucket token", Encrypted = true, ExcludeIfNull = true)] + public string Token { get; set; } + + [IniProperty("TokenSecret", Description = "The Photobucket token secret", Encrypted = true, ExcludeIfNull = true)] + public string TokenSecret { get; set; } + + [IniProperty("SubDomain", Description = "The Photobucket api subdomain", Encrypted = true, ExcludeIfNull = true)] + public string SubDomain { get; set; } + + [IniProperty("Username", Description = "The Photobucket api username", ExcludeIfNull = true)] + public string Username { get; set; } /// - /// A form for username/password - /// - /// bool true if OK was pressed, false if cancel - public bool ShowConfigDialog() { - SettingsForm settingsForm = null; + /// A form for username/password + ///
+ /// bool true if OK was pressed, false if cancel + public bool ShowConfigDialog() + { + SettingsForm settingsForm = null; - new PleaseWaitForm().ShowAndWait("Photobucket", Language.GetString("photobucket", LangKey.communication_wait), - delegate { - settingsForm = new SettingsForm(); - } - ); - DialogResult result = settingsForm.ShowDialog(); - if (result == DialogResult.OK) { - return true; - } - return false; - } - } -} + new PleaseWaitForm().ShowAndWait("Photobucket", Language.GetString("photobucket", LangKey.communication_wait), + delegate { settingsForm = new SettingsForm(); } + ); + DialogResult result = settingsForm.ShowDialog(); + if (result == DialogResult.OK) + { + return true; + } + + return false; + } + } +} \ No newline at end of file diff --git a/src/Greenshot.Plugin.Photobucket/PhotobucketDestination.cs b/src/Greenshot.Plugin.Photobucket/PhotobucketDestination.cs index 564e64511..34bffa89b 100644 --- a/src/Greenshot.Plugin.Photobucket/PhotobucketDestination.cs +++ b/src/Greenshot.Plugin.Photobucket/PhotobucketDestination.cs @@ -25,78 +25,95 @@ using System.Drawing; using GreenshotPlugin.Core; using GreenshotPlugin.Interfaces; -namespace Greenshot.Plugin.Photobucket { - /// - /// Description of PhotobucketDestination. - /// - public class PhotobucketDestination : AbstractDestination { - private readonly PhotobucketPlugin _plugin; - private readonly string _albumPath; +namespace Greenshot.Plugin.Photobucket +{ + /// + /// Description of PhotobucketDestination. + /// + public class PhotobucketDestination : AbstractDestination + { + private readonly PhotobucketPlugin _plugin; + private readonly string _albumPath; - /// - /// Create a Photobucket destination, which also has the path to the album in it - /// - /// - /// path to the album, null for default - public PhotobucketDestination(PhotobucketPlugin plugin, string albumPath = null) { - _plugin = plugin; - _albumPath = albumPath; - } - - public override string Designation => "Photobucket"; + /// + /// Create a Photobucket destination, which also has the path to the album in it + /// + /// + /// path to the album, null for default + public PhotobucketDestination(PhotobucketPlugin plugin, string albumPath = null) + { + _plugin = plugin; + _albumPath = albumPath; + } - public override string Description { - get { - if (_albumPath != null) { - return _albumPath; - } - return Language.GetString("photobucket", LangKey.upload_menu_item); - } - } + public override string Designation => "Photobucket"; - public override Image DisplayIcon { - get { - ComponentResourceManager resources = new ComponentResourceManager(typeof(PhotobucketPlugin)); - return (Image)resources.GetObject("Photobucket"); - } - } - - public override bool IsDynamic => true; + public override string Description + { + get + { + if (_albumPath != null) + { + return _albumPath; + } - public override IEnumerable DynamicDestinations() { - IList albums = null; - try { - albums = PhotobucketUtils.RetrievePhotobucketAlbums(); - } - catch - { - // ignored - } + return Language.GetString("photobucket", LangKey.upload_menu_item); + } + } - if (albums == null || albums.Count == 0) { - yield break; - } - foreach (string album in albums) { - yield return new PhotobucketDestination(_plugin, album); - } - } + public override Image DisplayIcon + { + get + { + ComponentResourceManager resources = new ComponentResourceManager(typeof(PhotobucketPlugin)); + return (Image) resources.GetObject("Photobucket"); + } + } - /// - /// Export the capture to Photobucket - /// - /// - /// - /// - /// - public override ExportInformation ExportCapture(bool manuallyInitiated, ISurface surface, ICaptureDetails captureDetails) { - ExportInformation exportInformation = new ExportInformation(Designation, Description); + public override bool IsDynamic => true; + + public override IEnumerable DynamicDestinations() + { + IList albums = null; + try + { + albums = PhotobucketUtils.RetrievePhotobucketAlbums(); + } + catch + { + // ignored + } + + if (albums == null || albums.Count == 0) + { + yield break; + } + + foreach (string album in albums) + { + yield return new PhotobucketDestination(_plugin, album); + } + } + + /// + /// Export the capture to Photobucket + /// + /// + /// + /// + /// + public override ExportInformation ExportCapture(bool manuallyInitiated, ISurface surface, ICaptureDetails captureDetails) + { + ExportInformation exportInformation = new ExportInformation(Designation, Description); bool uploaded = _plugin.Upload(captureDetails, surface, _albumPath, out var uploadUrl); - if (uploaded) { - exportInformation.ExportMade = true; - exportInformation.Uri = uploadUrl; - } - ProcessExport(exportInformation, surface); - return exportInformation; - } - } -} + if (uploaded) + { + exportInformation.ExportMade = true; + exportInformation.Uri = uploadUrl; + } + + ProcessExport(exportInformation, surface); + return exportInformation; + } + } +} \ No newline at end of file diff --git a/src/Greenshot.Plugin.Photobucket/PhotobucketInfo.cs b/src/Greenshot.Plugin.Photobucket/PhotobucketInfo.cs index 98b3b7517..7cb584b72 100644 --- a/src/Greenshot.Plugin.Photobucket/PhotobucketInfo.cs +++ b/src/Greenshot.Plugin.Photobucket/PhotobucketInfo.cs @@ -24,45 +24,56 @@ using System.Xml; namespace Greenshot.Plugin.Photobucket { - /// - /// Description of PhotobucketInfo. - /// - public class PhotobucketInfo { - private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(typeof(PhotobucketInfo)); + /// + /// Description of PhotobucketInfo. + /// + public class PhotobucketInfo + { + private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(typeof(PhotobucketInfo)); - public string Original { get; set; } + public string Original { get; set; } - public string Page { get; set; } + public string Page { get; set; } - public string Thumbnail { get; set; } + public string Thumbnail { get; set; } - /// - /// Parse the upload response - /// - /// XML - /// PhotobucketInfo object - public static PhotobucketInfo FromUploadResponse(string response) { - Log.Debug(response); - PhotobucketInfo photobucketInfo = new PhotobucketInfo(); - try { - XmlDocument doc = new XmlDocument(); - doc.LoadXml(response); - var nodes = doc.GetElementsByTagName("url"); - if(nodes.Count > 0) { - photobucketInfo.Original = nodes.Item(0)?.InnerText; - } - nodes = doc.GetElementsByTagName("browseurl"); - if(nodes.Count > 0) { - photobucketInfo.Page = nodes.Item(0)?.InnerText; - } - nodes = doc.GetElementsByTagName("thumb"); - if(nodes.Count > 0) { - photobucketInfo.Thumbnail = nodes.Item(0)?.InnerText; - } - } catch(Exception e) { - Log.ErrorFormat("Could not parse Photobucket response due to error {0}, response was: {1}", e.Message, response); - } - return photobucketInfo; - } - } -} + /// + /// Parse the upload response + /// + /// XML + /// PhotobucketInfo object + public static PhotobucketInfo FromUploadResponse(string response) + { + Log.Debug(response); + PhotobucketInfo photobucketInfo = new PhotobucketInfo(); + try + { + XmlDocument doc = new XmlDocument(); + doc.LoadXml(response); + var nodes = doc.GetElementsByTagName("url"); + if (nodes.Count > 0) + { + photobucketInfo.Original = nodes.Item(0)?.InnerText; + } + + nodes = doc.GetElementsByTagName("browseurl"); + if (nodes.Count > 0) + { + photobucketInfo.Page = nodes.Item(0)?.InnerText; + } + + nodes = doc.GetElementsByTagName("thumb"); + if (nodes.Count > 0) + { + photobucketInfo.Thumbnail = nodes.Item(0)?.InnerText; + } + } + catch (Exception e) + { + Log.ErrorFormat("Could not parse Photobucket response due to error {0}, response was: {1}", e.Message, response); + } + + return photobucketInfo; + } + } +} \ No newline at end of file diff --git a/src/Greenshot.Plugin.Photobucket/PhotobucketPlugin.cs b/src/Greenshot.Plugin.Photobucket/PhotobucketPlugin.cs index b45f563e0..394707897 100644 --- a/src/Greenshot.Plugin.Photobucket/PhotobucketPlugin.cs +++ b/src/Greenshot.Plugin.Photobucket/PhotobucketPlugin.cs @@ -30,23 +30,26 @@ using GreenshotPlugin.IniFile; using GreenshotPlugin.Interfaces; using GreenshotPlugin.Interfaces.Plugin; -namespace Greenshot.Plugin.Photobucket { - /// - /// This is the GreenshotPhotobucketPlugin base code - /// - [Plugin("Photobucket", true)] - public class PhotobucketPlugin : IGreenshotPlugin { - private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(typeof(PhotobucketPlugin)); - private static PhotobucketConfiguration _config; - private ComponentResourceManager _resources; - private ToolStripMenuItem _itemPlugInConfig; +namespace Greenshot.Plugin.Photobucket +{ + /// + /// This is the GreenshotPhotobucketPlugin base code + /// + [Plugin("Photobucket", true)] + public class PhotobucketPlugin : IGreenshotPlugin + { + private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(typeof(PhotobucketPlugin)); + private static PhotobucketConfiguration _config; + private ComponentResourceManager _resources; + private ToolStripMenuItem _itemPlugInConfig; - public void Dispose() { - Dispose(true); - GC.SuppressFinalize(this); - } + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } - protected void Dispose(bool disposing) + protected void Dispose(bool disposing) { if (!disposing) return; if (_itemPlugInConfig == null) return; @@ -54,89 +57,102 @@ namespace Greenshot.Plugin.Photobucket { _itemPlugInConfig = null; } - /// - /// Implementation of the IGreenshotPlugin.Initialize - /// - /// true if plugin is initialized, false if not (doesn't show) - public bool Initialize() { + /// + /// Implementation of the IGreenshotPlugin.Initialize + /// + /// true if plugin is initialized, false if not (doesn't show) + public bool Initialize() + { SimpleServiceProvider.Current.AddService(new PhotobucketDestination(this)); - // Get configuration - _config = IniConfig.GetIniSection(); - _resources = new ComponentResourceManager(typeof(PhotobucketPlugin)); + // Get configuration + _config = IniConfig.GetIniSection(); + _resources = new ComponentResourceManager(typeof(PhotobucketPlugin)); - _itemPlugInConfig = new ToolStripMenuItem(Language.GetString("photobucket", LangKey.configure)) - { - Image = (Image)_resources.GetObject("Photobucket") - }; - _itemPlugInConfig.Click += delegate { - _config.ShowConfigDialog(); - }; + _itemPlugInConfig = new ToolStripMenuItem(Language.GetString("photobucket", LangKey.configure)) + { + Image = (Image) _resources.GetObject("Photobucket") + }; + _itemPlugInConfig.Click += delegate { _config.ShowConfigDialog(); }; - PluginUtils.AddToContextMenu(_itemPlugInConfig); - Language.LanguageChanged += OnLanguageChanged; - return true; - } + PluginUtils.AddToContextMenu(_itemPlugInConfig); + Language.LanguageChanged += OnLanguageChanged; + return true; + } - public void OnLanguageChanged(object sender, EventArgs e) { - if (_itemPlugInConfig != null) { - _itemPlugInConfig.Text = Language.GetString("photobucket", LangKey.configure); - } - } + public void OnLanguageChanged(object sender, EventArgs e) + { + if (_itemPlugInConfig != null) + { + _itemPlugInConfig.Text = Language.GetString("photobucket", LangKey.configure); + } + } - public void Shutdown() { - Log.Debug("Photobucket Plugin shutdown."); - Language.LanguageChanged -= OnLanguageChanged; - } + public void Shutdown() + { + Log.Debug("Photobucket Plugin shutdown."); + Language.LanguageChanged -= OnLanguageChanged; + } - /// - /// Implementation of the IPlugin.Configure - /// - public void Configure() { - _config.ShowConfigDialog(); - } + /// + /// Implementation of the IPlugin.Configure + /// + public void Configure() + { + _config.ShowConfigDialog(); + } - /// - /// Upload the capture to Photobucket - /// - /// - /// ISurface - /// Path to the album - /// out string for the url - /// true if the upload succeeded - public bool Upload(ICaptureDetails captureDetails, ISurface surfaceToUpload, string albumPath, out string uploadUrl) { - SurfaceOutputSettings outputSettings = new SurfaceOutputSettings(_config.UploadFormat, _config.UploadJpegQuality, _config.UploadReduceColors); - try { - string filename = Path.GetFileName(FilenameHelper.GetFilename(_config.UploadFormat, captureDetails)); - PhotobucketInfo photobucketInfo = null; - - // Run upload in the background - new PleaseWaitForm().ShowAndWait("Photobucket", Language.GetString("photobucket", LangKey.communication_wait), - delegate { - photobucketInfo = PhotobucketUtils.UploadToPhotobucket(surfaceToUpload, outputSettings, albumPath, captureDetails.Title, filename); - } - ); - // This causes an exeption if the upload failed :) - Log.DebugFormat("Uploaded to Photobucket page: " + photobucketInfo.Page); - uploadUrl = null; - try { - if (_config.UsePageLink) { - uploadUrl = photobucketInfo.Page; - Clipboard.SetText(photobucketInfo.Page); - } else { - uploadUrl = photobucketInfo.Original; - Clipboard.SetText(photobucketInfo.Original); - } - } catch (Exception ex) { - Log.Error("Can't write to clipboard: ", ex); - } - return true; - } catch (Exception e) { - Log.Error(e); - MessageBox.Show(Language.GetString("photobucket", LangKey.upload_failure) + " " + e.Message); - } - uploadUrl = null; - return false; - } - } -} + /// + /// Upload the capture to Photobucket + /// + /// + /// ISurface + /// Path to the album + /// out string for the url + /// true if the upload succeeded + public bool Upload(ICaptureDetails captureDetails, ISurface surfaceToUpload, string albumPath, out string uploadUrl) + { + SurfaceOutputSettings outputSettings = new SurfaceOutputSettings(_config.UploadFormat, _config.UploadJpegQuality, _config.UploadReduceColors); + try + { + string filename = Path.GetFileName(FilenameHelper.GetFilename(_config.UploadFormat, captureDetails)); + PhotobucketInfo photobucketInfo = null; + + // Run upload in the background + new PleaseWaitForm().ShowAndWait("Photobucket", Language.GetString("photobucket", LangKey.communication_wait), + delegate { photobucketInfo = PhotobucketUtils.UploadToPhotobucket(surfaceToUpload, outputSettings, albumPath, captureDetails.Title, filename); } + ); + // This causes an exeption if the upload failed :) + Log.DebugFormat("Uploaded to Photobucket page: " + photobucketInfo.Page); + uploadUrl = null; + try + { + if (_config.UsePageLink) + { + uploadUrl = photobucketInfo.Page; + Clipboard.SetText(photobucketInfo.Page); + } + else + { + uploadUrl = photobucketInfo.Original; + Clipboard.SetText(photobucketInfo.Original); + } + } + catch (Exception ex) + { + Log.Error("Can't write to clipboard: ", ex); + } + + return true; + } + catch (Exception e) + { + Log.Error(e); + MessageBox.Show(Language.GetString("photobucket", LangKey.upload_failure) + " " + e.Message); + } + + uploadUrl = null; + return false; + } + } +} \ No newline at end of file diff --git a/src/Greenshot.Plugin.Photobucket/PhotobucketUtils.cs b/src/Greenshot.Plugin.Photobucket/PhotobucketUtils.cs index eeda1ae74..61a61f8a3 100644 --- a/src/Greenshot.Plugin.Photobucket/PhotobucketUtils.cs +++ b/src/Greenshot.Plugin.Photobucket/PhotobucketUtils.cs @@ -30,197 +30,264 @@ using GreenshotPlugin.IniFile; using GreenshotPlugin.Interfaces; using GreenshotPlugin.Interfaces.Plugin; -namespace Greenshot.Plugin.Photobucket { - /// - /// Description of PhotobucketUtils. - /// - public static class PhotobucketUtils { - private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(typeof(PhotobucketUtils)); - private static readonly PhotobucketConfiguration PhotobucketConfig = IniConfig.GetIniSection(); - private static List _albumsCache; +namespace Greenshot.Plugin.Photobucket +{ + /// + /// Description of PhotobucketUtils. + /// + public static class PhotobucketUtils + { + private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(typeof(PhotobucketUtils)); + private static readonly PhotobucketConfiguration PhotobucketConfig = IniConfig.GetIniSection(); + private static List _albumsCache; - /// - /// Do the actual upload to Photobucket - /// For more details on the available parameters, see: http://api.Photobucket.com/resources_anon - /// - /// PhotobucketResponse - public static PhotobucketInfo UploadToPhotobucket(ISurface surfaceToUpload, SurfaceOutputSettings outputSettings, string albumPath, string title, string filename) { - string responseString; - - var oAuth = CreateSession(true); - if (oAuth == null) { - return null; - } - IDictionary signedParameters = new Dictionary(); - // add album - if (string.IsNullOrEmpty(albumPath)) - { - signedParameters.Add("id", string.IsNullOrEmpty(PhotobucketConfig.Username) ? "!" : PhotobucketConfig.Username); - } else { - signedParameters.Add("id", albumPath); - } - // add type - signedParameters.Add("type", "image"); - // add title - if (title != null) { - signedParameters.Add("title", title); - } - // add filename - if (filename != null) { - signedParameters.Add("filename", filename); - } - IDictionary unsignedParameters = new Dictionary - { - // Add image - { "uploadfile", new SurfaceContainer(surfaceToUpload, outputSettings, filename) } - }; - try { - string apiUrl = "http://api.photobucket.com/album/!/upload"; - responseString = oAuth.MakeOAuthRequest(HTTPMethod.POST, apiUrl, apiUrl.Replace("api.photobucket.com", PhotobucketConfig.SubDomain), signedParameters, unsignedParameters, null); - } catch (Exception ex) { - Log.Error("Error uploading to Photobucket.", ex); - throw; - } finally { - if (!string.IsNullOrEmpty(oAuth.Token)) { - PhotobucketConfig.Token = oAuth.Token; - } - if (!string.IsNullOrEmpty(oAuth.TokenSecret)) { - PhotobucketConfig.TokenSecret = oAuth.TokenSecret; - } - } - if (responseString == null) { - return null; - } - Log.Info(responseString); - var photobucketInfo = PhotobucketInfo.FromUploadResponse(responseString); - Log.Debug("Upload to Photobucket was finished"); - return photobucketInfo; - } - - /// - /// Helper method to create an OAuth session object for contacting the Photobucket API - /// - /// OAuthSession - private static OAuthSession CreateSession(bool autoLogin) { - var oAuth = new OAuthSession(PhotobucketCredentials.ConsumerKey, PhotobucketCredentials.ConsumerSecret) - { - AutoLogin = autoLogin, - CheckVerifier = false, - CallbackUrl = "http://getgreenshot.org", - AccessTokenUrl = "http://api.photobucket.com/login/access", - AuthorizeUrl = "http://photobucket.com/apilogin/login", - RequestTokenUrl = "http://api.photobucket.com/login/request", - BrowserSize = new Size(1010, 400), - RequestTokenMethod = HTTPMethod.POST, - AccessTokenMethod = HTTPMethod.POST, - LoginTitle = "Photobucket authorization" - }; - // This url is configured in the Photobucket API settings in the Photobucket site!! - // Photobucket is very particular about the used methods! + /// + /// Do the actual upload to Photobucket + /// For more details on the available parameters, see: http://api.Photobucket.com/resources_anon + /// + /// PhotobucketResponse + public static PhotobucketInfo UploadToPhotobucket(ISurface surfaceToUpload, SurfaceOutputSettings outputSettings, string albumPath, string title, string filename) + { + string responseString; - if (string.IsNullOrEmpty(PhotobucketConfig.SubDomain) || string.IsNullOrEmpty(PhotobucketConfig.Token) || string.IsNullOrEmpty(PhotobucketConfig.Username)) { - if (!autoLogin) { - return null; - } - if (!oAuth.Authorize()) { - return null; - } - if (!string.IsNullOrEmpty(oAuth.Token)) { - PhotobucketConfig.Token = oAuth.Token; - } - if (!string.IsNullOrEmpty(oAuth.TokenSecret)) { - PhotobucketConfig.TokenSecret = oAuth.TokenSecret; - } - if (oAuth.AccessTokenResponseParameters?["subdomain"] != null) { - PhotobucketConfig.SubDomain = oAuth.AccessTokenResponseParameters["subdomain"]; - } - if (oAuth.AccessTokenResponseParameters?["username"] != null) { - PhotobucketConfig.Username = oAuth.AccessTokenResponseParameters["username"]; - } - IniConfig.Save(); - } - oAuth.Token = PhotobucketConfig.Token; - oAuth.TokenSecret = PhotobucketConfig.TokenSecret; - return oAuth; - } + var oAuth = CreateSession(true); + if (oAuth == null) + { + return null; + } - /// - /// Get list of photobucket albums - /// - /// List of string - public static IList RetrievePhotobucketAlbums() { - if (_albumsCache != null) { - return _albumsCache; - } - string responseString; + IDictionary signedParameters = new Dictionary(); + // add album + if (string.IsNullOrEmpty(albumPath)) + { + signedParameters.Add("id", string.IsNullOrEmpty(PhotobucketConfig.Username) ? "!" : PhotobucketConfig.Username); + } + else + { + signedParameters.Add("id", albumPath); + } - OAuthSession oAuth = CreateSession(false); - if (oAuth == null) { - return null; - } - IDictionary signedParameters = new Dictionary(); - try { - string apiUrl = $"http://api.photobucket.com/album/{PhotobucketConfig.Username}"; - responseString = oAuth.MakeOAuthRequest(HTTPMethod.GET, apiUrl, apiUrl.Replace("api.photobucket.com", PhotobucketConfig.SubDomain), signedParameters, null, null); - } catch (Exception ex) { - Log.Error("Error uploading to Photobucket.", ex); - throw; - } finally { - if (!string.IsNullOrEmpty(oAuth.Token)) { - PhotobucketConfig.Token = oAuth.Token; - } - if (!string.IsNullOrEmpty(oAuth.TokenSecret)) { - PhotobucketConfig.TokenSecret = oAuth.TokenSecret; - } - } - if (responseString == null) { - return null; - } - try { - XmlDocument doc = new XmlDocument(); - doc.LoadXml(responseString); - List albums = new List(); - var xmlNode = doc.GetElementsByTagName("content").Item(0); - if (xmlNode != null) - { - RecurseAlbums(albums, null, xmlNode.ChildNodes); - } - Log.DebugFormat("Albums: {0}", string.Join(",", albums.ToArray())); - _albumsCache = albums; - return albums; - } catch(Exception e) { - Log.Error("Error while Reading albums: ", e); - } + // add type + signedParameters.Add("type", "image"); + // add title + if (title != null) + { + signedParameters.Add("title", title); + } - Log.Debug("Upload to Photobucket was finished"); - return null; - } - - /// - /// Parse the album nodes recursively - /// - /// - /// - /// - private static void RecurseAlbums(ICollection albums, string path, IEnumerable nodes) { - foreach(XmlNode node in nodes) { - if (node.Name != "album") { - continue; - } - if (node.Attributes != null) - { - string currentAlbum = node.Attributes["name"].Value; - string currentPath = currentAlbum; - if (!string.IsNullOrEmpty(path)) { - currentPath = $"{path}/{currentAlbum}"; - } - - albums.Add(currentPath); - if (node.Attributes["subalbum_count"] != null && node.Attributes["subalbum_count"].Value != "0") { - RecurseAlbums(albums, currentPath, node.ChildNodes); - } - } - } - } - } -} + // add filename + if (filename != null) + { + signedParameters.Add("filename", filename); + } + + IDictionary unsignedParameters = new Dictionary + { + // Add image + { + "uploadfile", new SurfaceContainer(surfaceToUpload, outputSettings, filename) + } + }; + try + { + string apiUrl = "http://api.photobucket.com/album/!/upload"; + responseString = oAuth.MakeOAuthRequest(HTTPMethod.POST, apiUrl, apiUrl.Replace("api.photobucket.com", PhotobucketConfig.SubDomain), signedParameters, + unsignedParameters, null); + } + catch (Exception ex) + { + Log.Error("Error uploading to Photobucket.", ex); + throw; + } + finally + { + if (!string.IsNullOrEmpty(oAuth.Token)) + { + PhotobucketConfig.Token = oAuth.Token; + } + + if (!string.IsNullOrEmpty(oAuth.TokenSecret)) + { + PhotobucketConfig.TokenSecret = oAuth.TokenSecret; + } + } + + if (responseString == null) + { + return null; + } + + Log.Info(responseString); + var photobucketInfo = PhotobucketInfo.FromUploadResponse(responseString); + Log.Debug("Upload to Photobucket was finished"); + return photobucketInfo; + } + + /// + /// Helper method to create an OAuth session object for contacting the Photobucket API + /// + /// OAuthSession + private static OAuthSession CreateSession(bool autoLogin) + { + var oAuth = new OAuthSession(PhotobucketCredentials.ConsumerKey, PhotobucketCredentials.ConsumerSecret) + { + AutoLogin = autoLogin, + CheckVerifier = false, + CallbackUrl = "http://getgreenshot.org", + AccessTokenUrl = "http://api.photobucket.com/login/access", + AuthorizeUrl = "http://photobucket.com/apilogin/login", + RequestTokenUrl = "http://api.photobucket.com/login/request", + BrowserSize = new Size(1010, 400), + RequestTokenMethod = HTTPMethod.POST, + AccessTokenMethod = HTTPMethod.POST, + LoginTitle = "Photobucket authorization" + }; + // This url is configured in the Photobucket API settings in the Photobucket site!! + // Photobucket is very particular about the used methods! + + if (string.IsNullOrEmpty(PhotobucketConfig.SubDomain) || string.IsNullOrEmpty(PhotobucketConfig.Token) || string.IsNullOrEmpty(PhotobucketConfig.Username)) + { + if (!autoLogin) + { + return null; + } + + if (!oAuth.Authorize()) + { + return null; + } + + if (!string.IsNullOrEmpty(oAuth.Token)) + { + PhotobucketConfig.Token = oAuth.Token; + } + + if (!string.IsNullOrEmpty(oAuth.TokenSecret)) + { + PhotobucketConfig.TokenSecret = oAuth.TokenSecret; + } + + if (oAuth.AccessTokenResponseParameters?["subdomain"] != null) + { + PhotobucketConfig.SubDomain = oAuth.AccessTokenResponseParameters["subdomain"]; + } + + if (oAuth.AccessTokenResponseParameters?["username"] != null) + { + PhotobucketConfig.Username = oAuth.AccessTokenResponseParameters["username"]; + } + + IniConfig.Save(); + } + + oAuth.Token = PhotobucketConfig.Token; + oAuth.TokenSecret = PhotobucketConfig.TokenSecret; + return oAuth; + } + + /// + /// Get list of photobucket albums + /// + /// List of string + public static IList RetrievePhotobucketAlbums() + { + if (_albumsCache != null) + { + return _albumsCache; + } + + string responseString; + + OAuthSession oAuth = CreateSession(false); + if (oAuth == null) + { + return null; + } + + IDictionary signedParameters = new Dictionary(); + try + { + string apiUrl = $"http://api.photobucket.com/album/{PhotobucketConfig.Username}"; + responseString = oAuth.MakeOAuthRequest(HTTPMethod.GET, apiUrl, apiUrl.Replace("api.photobucket.com", PhotobucketConfig.SubDomain), signedParameters, null, null); + } + catch (Exception ex) + { + Log.Error("Error uploading to Photobucket.", ex); + throw; + } + finally + { + if (!string.IsNullOrEmpty(oAuth.Token)) + { + PhotobucketConfig.Token = oAuth.Token; + } + + if (!string.IsNullOrEmpty(oAuth.TokenSecret)) + { + PhotobucketConfig.TokenSecret = oAuth.TokenSecret; + } + } + + if (responseString == null) + { + return null; + } + + try + { + XmlDocument doc = new XmlDocument(); + doc.LoadXml(responseString); + List albums = new List(); + var xmlNode = doc.GetElementsByTagName("content").Item(0); + if (xmlNode != null) + { + RecurseAlbums(albums, null, xmlNode.ChildNodes); + } + + Log.DebugFormat("Albums: {0}", string.Join(",", albums.ToArray())); + _albumsCache = albums; + return albums; + } + catch (Exception e) + { + Log.Error("Error while Reading albums: ", e); + } + + Log.Debug("Upload to Photobucket was finished"); + return null; + } + + /// + /// Parse the album nodes recursively + /// + /// + /// + /// + private static void RecurseAlbums(ICollection albums, string path, IEnumerable nodes) + { + foreach (XmlNode node in nodes) + { + if (node.Name != "album") + { + continue; + } + + if (node.Attributes != null) + { + string currentAlbum = node.Attributes["name"].Value; + string currentPath = currentAlbum; + if (!string.IsNullOrEmpty(path)) + { + currentPath = $"{path}/{currentAlbum}"; + } + + albums.Add(currentPath); + if (node.Attributes["subalbum_count"] != null && node.Attributes["subalbum_count"].Value != "0") + { + RecurseAlbums(albums, currentPath, node.ChildNodes); + } + } + } + } + } +} \ No newline at end of file diff --git a/src/Greenshot.Plugin.Photobucket/Properties/AssemblyInfo.cs b/src/Greenshot.Plugin.Photobucket/Properties/AssemblyInfo.cs index 99952c5c0..470ed7174 100644 --- a/src/Greenshot.Plugin.Photobucket/Properties/AssemblyInfo.cs +++ b/src/Greenshot.Plugin.Photobucket/Properties/AssemblyInfo.cs @@ -30,5 +30,4 @@ using System.Runtime.InteropServices; // This sets the default COM visibility of types in the assembly to invisible. // If you need to expose a type to COM, use [ComVisible(true)] on that type. -[assembly: ComVisible(false)] - +[assembly: ComVisible(false)] \ No newline at end of file diff --git a/src/Greenshot.Plugin.Win10/Destinations/Win10OcrDestination.cs b/src/Greenshot.Plugin.Win10/Destinations/Win10OcrDestination.cs index 616960615..cc119b70d 100644 --- a/src/Greenshot.Plugin.Win10/Destinations/Win10OcrDestination.cs +++ b/src/Greenshot.Plugin.Win10/Destinations/Win10OcrDestination.cs @@ -29,70 +29,70 @@ using GreenshotPlugin.Interfaces.Ocr; namespace Greenshot.Plugin.Win10.Destinations { - /// - /// This uses the OcrEngine from Windows 10 to perform OCR on the captured image. - /// - public class Win10OcrDestination : AbstractDestination - { - private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(typeof(Win10OcrDestination)); + /// + /// This uses the OcrEngine from Windows 10 to perform OCR on the captured image. + /// + public class Win10OcrDestination : AbstractDestination + { + private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(typeof(Win10OcrDestination)); - public override string Designation { get; } = "Windows10OCR"; - public override string Description { get; } = "Windows 10 OCR"; + public override string Designation { get; } = "Windows10OCR"; + public override string Description { get; } = "Windows 10 OCR"; - /// - /// Icon for the OCR function, the icon was found via: http://help4windows.com/windows_8_imageres_dll.shtml - /// - public override Image DisplayIcon => PluginUtils.GetCachedExeIcon(FilenameHelper.FillCmdVariables(@"%windir%\system32\imageres.dll"), 97); + /// + /// Icon for the OCR function, the icon was found via: http://help4windows.com/windows_8_imageres_dll.shtml + /// + public override Image DisplayIcon => PluginUtils.GetCachedExeIcon(FilenameHelper.FillCmdVariables(@"%windir%\system32\imageres.dll"), 97); - /// - /// Constructor, this is only debug information - /// - public Win10OcrDestination() - { - var languages = OcrEngine.AvailableRecognizerLanguages; - foreach (var language in languages) - { - Log.DebugFormat("Found language {0} {1}", language.NativeName, language.LanguageTag); - } - } - - /// - /// Run the Windows 10 OCR engine to process the text on the captured image - /// - /// - /// - /// - /// ExportInformation - public override ExportInformation ExportCapture(bool manuallyInitiated, ISurface surface, ICaptureDetails captureDetails) - { - var exportInformation = new ExportInformation(Designation, Description); - try + /// + /// Constructor, this is only debug information + /// + public Win10OcrDestination() + { + var languages = OcrEngine.AvailableRecognizerLanguages; + foreach (var language in languages) { - // TODO: Check if the OcrInformation is for the selected surface... otherwise discard & do it again + Log.DebugFormat("Found language {0} {1}", language.NativeName, language.LanguageTag); + } + } + + /// + /// Run the Windows 10 OCR engine to process the text on the captured image + /// + /// + /// + /// + /// ExportInformation + public override ExportInformation ExportCapture(bool manuallyInitiated, ISurface surface, ICaptureDetails captureDetails) + { + var exportInformation = new ExportInformation(Designation, Description); + try + { + // TODO: Check if the OcrInformation is for the selected surface... otherwise discard & do it again var ocrInformation = captureDetails.OcrInformation; - if (captureDetails.OcrInformation == null) + if (captureDetails.OcrInformation == null) { var ocrProvider = SimpleServiceProvider.Current.GetInstance(); - ocrInformation = Task.Run(async () => await ocrProvider.DoOcrAsync(surface)).Result; - } + ocrInformation = Task.Run(async () => await ocrProvider.DoOcrAsync(surface)).Result; + } - // Check if we found text - if (!string.IsNullOrWhiteSpace(ocrInformation.Text)) - { - // Place the OCR text on the - ClipboardHelper.SetClipboardData(ocrInformation.Text); - } - exportInformation.ExportMade = true; - } - catch (Exception ex) - { - exportInformation.ExportMade = false; - exportInformation.ErrorMessage = ex.Message; - } + // Check if we found text + if (!string.IsNullOrWhiteSpace(ocrInformation.Text)) + { + // Place the OCR text on the + ClipboardHelper.SetClipboardData(ocrInformation.Text); + } - ProcessExport(exportInformation, surface); - return exportInformation; + exportInformation.ExportMade = true; + } + catch (Exception ex) + { + exportInformation.ExportMade = false; + exportInformation.ErrorMessage = ex.Message; + } - } - } -} + ProcessExport(exportInformation, surface); + return exportInformation; + } + } +} \ No newline at end of file diff --git a/src/Greenshot.Plugin.Win10/Destinations/Win10ShareDestination.cs b/src/Greenshot.Plugin.Win10/Destinations/Win10ShareDestination.cs index 2581dc586..e8178a8cf 100644 --- a/src/Greenshot.Plugin.Win10/Destinations/Win10ShareDestination.cs +++ b/src/Greenshot.Plugin.Win10/Destinations/Win10ShareDestination.cs @@ -61,7 +61,7 @@ namespace Greenshot.Plugin.Win10.Destinations /// ISurface /// ICaptureDetails /// ExportInformation - public override ExportInformation ExportCapture(bool manuallyInitiated, ISurface surface, ICaptureDetails captureDetails) + public override ExportInformation ExportCapture(bool manuallyInitiated, ISurface surface, ICaptureDetails captureDetails) { var exportInformation = new ExportInformation(Designation, Description); try @@ -88,13 +88,13 @@ namespace Greenshot.Plugin.Win10.Destinations // Wait for the focus to return, and depending on the state close the window! focusMonitor.WindowOpenCloseChangeEvent += e => { - if (e.IsOpen) { if ("Windows Shell Experience Host" == e.Title) { shareInfo.SharingHwnd = e.HWnd; } + return; } @@ -104,6 +104,7 @@ namespace Greenshot.Plugin.Win10.Destinations { return; } + shareInfo.ShareTask.TrySetResult(false); } }; @@ -130,8 +131,8 @@ namespace Greenshot.Plugin.Win10.Destinations ProcessExport(exportInformation, surface); return exportInformation; - } + /// /// Share the surface by using the Share-UI of Windows 10 /// @@ -191,8 +192,8 @@ namespace Greenshot.Plugin.Win10.Destinations var storageFile = await StorageFile.CreateStreamedFileAsync(filename, async streamedFileDataRequest => { shareInfo.IsDeferredFileCreated = true; - // Information on the "how" was found here: https://socialeboladev.wordpress.com/2013/03/15/how-to-use-createstreamedfileasync/ - Log.DebugFormat("Creating deferred file {0}", filename); + // Information on the "how" was found here: https://socialeboladev.wordpress.com/2013/03/15/how-to-use-createstreamedfileasync/ + Log.DebugFormat("Creating deferred file {0}", filename); try { using (var deferredStream = streamedFileDataRequest.AsStreamForWrite()) @@ -200,6 +201,7 @@ namespace Greenshot.Plugin.Win10.Destinations await imageStream.CopyToAsync(deferredStream).ConfigureAwait(false); await imageStream.FlushAsync().ConfigureAwait(false); } + // Signal that the stream is ready streamedFileDataRequest.Dispose(); // Signal that the action is ready, bitmap was exported @@ -245,7 +247,10 @@ namespace Greenshot.Plugin.Win10.Destinations dataPackage.Properties.Thumbnail = thumbnailRandomAccessStreamReference; dataPackage.Properties.Square30x30Logo = logoRandomAccessStreamReference; dataPackage.Properties.LogoBackgroundColor = Color.FromArgb(0xff, 0x3d, 0x3d, 0x3d); - dataPackage.SetStorageItems(new[] { storageFile }); + dataPackage.SetStorageItems(new[] + { + storageFile + }); dataPackage.SetBitmap(imageRandomAccessStreamReference); } finally @@ -259,4 +264,4 @@ namespace Greenshot.Plugin.Win10.Destinations await shareInfo.ShareTask.Task.ConfigureAwait(false); } } -} +} \ No newline at end of file diff --git a/src/Greenshot.Plugin.Win10/Internal/MemoryRandomAccessStream.cs b/src/Greenshot.Plugin.Win10/Internal/MemoryRandomAccessStream.cs index e4a49c64e..230c76ed7 100644 --- a/src/Greenshot.Plugin.Win10/Internal/MemoryRandomAccessStream.cs +++ b/src/Greenshot.Plugin.Win10/Internal/MemoryRandomAccessStream.cs @@ -27,67 +27,67 @@ namespace Greenshot.Plugin.Win10.Internal /// /// This is an IRandomAccessStream implementation which uses a MemoryStream /// - internal sealed class MemoryRandomAccessStream : MemoryStream, IRandomAccessStream - { + internal sealed class MemoryRandomAccessStream : MemoryStream, IRandomAccessStream + { /// public IInputStream GetInputStreamAt(ulong position) - { - Seek((long)position, SeekOrigin.Begin); + { + Seek((long) position, SeekOrigin.Begin); - return this.AsInputStream(); - } + return this.AsInputStream(); + } /// public IOutputStream GetOutputStreamAt(ulong position) - { - Seek((long)position, SeekOrigin.Begin); + { + Seek((long) position, SeekOrigin.Begin); - return this.AsOutputStream(); - } + return this.AsOutputStream(); + } /// - ulong IRandomAccessStream.Position => (ulong)Position; + ulong IRandomAccessStream.Position => (ulong) Position; /// public ulong Size - { - get { return (ulong)Length; } - set { SetLength((long)value); } - } + { + get { return (ulong) Length; } + set { SetLength((long) value); } + } /// public IRandomAccessStream CloneStream() - { - var cloned = new MemoryRandomAccessStream(); - CopyTo(cloned); - return cloned; - } + { + var cloned = new MemoryRandomAccessStream(); + CopyTo(cloned); + return cloned; + } /// public void Seek(ulong position) - { - Seek((long)position, SeekOrigin.Begin); - } + { + Seek((long) position, SeekOrigin.Begin); + } /// public Windows.Foundation.IAsyncOperationWithProgress ReadAsync(IBuffer buffer, uint count, InputStreamOptions options) - { - var inputStream = GetInputStreamAt(0); - return inputStream.ReadAsync(buffer, count, options); - } + { + var inputStream = GetInputStreamAt(0); + return inputStream.ReadAsync(buffer, count, options); + } /// Windows.Foundation.IAsyncOperation IOutputStream.FlushAsync() - { - var outputStream = GetOutputStreamAt(0); - return outputStream.FlushAsync(); - } + { + var outputStream = GetOutputStreamAt(0); + return outputStream.FlushAsync(); + } /// public Windows.Foundation.IAsyncOperationWithProgress WriteAsync(IBuffer buffer) - { - var outputStream = GetOutputStreamAt(0); - return outputStream.WriteAsync(buffer); - } - } -} + { + var outputStream = GetOutputStreamAt(0); + return outputStream.WriteAsync(buffer); + } + } +} \ No newline at end of file diff --git a/src/Greenshot.Plugin.Win10/Internal/ShareInfo.cs b/src/Greenshot.Plugin.Win10/Internal/ShareInfo.cs index 44b0d529b..f28370e06 100644 --- a/src/Greenshot.Plugin.Win10/Internal/ShareInfo.cs +++ b/src/Greenshot.Plugin.Win10/Internal/ShareInfo.cs @@ -40,4 +40,4 @@ namespace Greenshot.Plugin.Win10.Internal public IntPtr SharingHwnd { get; set; } } -} +} \ No newline at end of file diff --git a/src/Greenshot.Plugin.Win10/Native/DataTransferManagerHelper.cs b/src/Greenshot.Plugin.Win10/Native/DataTransferManagerHelper.cs index 192e818ec..9f2540694 100644 --- a/src/Greenshot.Plugin.Win10/Native/DataTransferManagerHelper.cs +++ b/src/Greenshot.Plugin.Win10/Native/DataTransferManagerHelper.cs @@ -26,62 +26,59 @@ using GreenshotPlugin.Core; namespace Greenshot.Plugin.Win10.Native { - /// - /// Wraps the interop for calling the ShareUI - /// - public class DataTransferManagerHelper - { - private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(typeof(DataTransferManagerHelper)); + /// + /// Wraps the interop for calling the ShareUI + /// + public class DataTransferManagerHelper + { + private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(typeof(DataTransferManagerHelper)); - private const string DataTransferManagerId = "a5caee9b-8708-49d1-8d36-67d25a8da00c"; - private readonly IDataTransferManagerInterOp _dataTransferManagerInterOp; - private readonly IntPtr _windowHandle; + private const string DataTransferManagerId = "a5caee9b-8708-49d1-8d36-67d25a8da00c"; + private readonly IDataTransferManagerInterOp _dataTransferManagerInterOp; + private readonly IntPtr _windowHandle; /// /// The DataTransferManager /// - public DataTransferManager DataTransferManager - { - get; - private set; - } + public DataTransferManager DataTransferManager { get; private set; } + /// /// Constructor which takes a handle to initialize /// /// - public DataTransferManagerHelper(IntPtr handle) - { - //TODO: Add a check for failure here. This will fail for versions of Windows below Windows 10 - IActivationFactory activationFactory = WindowsRuntimeMarshal.GetActivationFactory(typeof(DataTransferManager)); + public DataTransferManagerHelper(IntPtr handle) + { + //TODO: Add a check for failure here. This will fail for versions of Windows below Windows 10 + IActivationFactory activationFactory = WindowsRuntimeMarshal.GetActivationFactory(typeof(DataTransferManager)); - // ReSharper disable once SuspiciousTypeConversion.Global - _dataTransferManagerInterOp = (IDataTransferManagerInterOp)activationFactory; + // ReSharper disable once SuspiciousTypeConversion.Global + _dataTransferManagerInterOp = (IDataTransferManagerInterOp) activationFactory; - _windowHandle = handle; - var riid = new Guid(DataTransferManagerId); - var hresult = _dataTransferManagerInterOp.GetForWindow(_windowHandle, riid, out var dataTransferManager); - if (hresult.Failed()) - { - Log.WarnFormat("HResult for GetForWindow: {0}", hresult); - } - DataTransferManager = dataTransferManager; - } + _windowHandle = handle; + var riid = new Guid(DataTransferManagerId); + var hresult = _dataTransferManagerInterOp.GetForWindow(_windowHandle, riid, out var dataTransferManager); + if (hresult.Failed()) + { + Log.WarnFormat("HResult for GetForWindow: {0}", hresult); + } - /// - /// Show the share UI - /// - public void ShowShareUi() - { - var hresult = _dataTransferManagerInterOp.ShowShareUIForWindow(_windowHandle); - if (hresult.Failed()) - { + DataTransferManager = dataTransferManager; + } + + /// + /// Show the share UI + /// + public void ShowShareUi() + { + var hresult = _dataTransferManagerInterOp.ShowShareUIForWindow(_windowHandle); + if (hresult.Failed()) + { Log.WarnFormat("HResult for ShowShareUIForWindow: {0}", hresult); - } - else - { - Log.Debug("ShowShareUIForWindow called"); - } - } - } - -} + } + else + { + Log.Debug("ShowShareUIForWindow called"); + } + } + } +} \ No newline at end of file diff --git a/src/Greenshot.Plugin.Win10/Native/IDataTransferManagerInterOp.cs b/src/Greenshot.Plugin.Win10/Native/IDataTransferManagerInterOp.cs index dc477aac8..8bdba3366 100644 --- a/src/Greenshot.Plugin.Win10/Native/IDataTransferManagerInterOp.cs +++ b/src/Greenshot.Plugin.Win10/Native/IDataTransferManagerInterOp.cs @@ -30,27 +30,26 @@ namespace Greenshot.Plugin.Win10.Native /// window using a window handle. Useful for Win32 apps. /// [ComImport, Guid("3A3DCD6C-3EAB-43DC-BCDE-45671CE800C8")] - [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] - public interface IDataTransferManagerInterOp - { - /// - /// Get an instance of Windows.ApplicationModel.DataTransfer.DataTransferManager - /// for the window identified by a window handle - /// - /// The window handle - /// ID of the DataTransferManager interface - /// The DataTransferManager instance for this window handle - /// HRESULT - [PreserveSig] - HResult GetForWindow([In] IntPtr appWindow, [In] ref Guid riid, [Out] out DataTransferManager pDataTransferManager); + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + public interface IDataTransferManagerInterOp + { + /// + /// Get an instance of Windows.ApplicationModel.DataTransfer.DataTransferManager + /// for the window identified by a window handle + /// + /// The window handle + /// ID of the DataTransferManager interface + /// The DataTransferManager instance for this window handle + /// HRESULT + [PreserveSig] + HResult GetForWindow([In] IntPtr appWindow, [In] ref Guid riid, [Out] out DataTransferManager pDataTransferManager); - /// - /// Show the share flyout for the window identified by a window handle - /// - /// The window handle - /// HRESULT - [PreserveSig] - HResult ShowShareUIForWindow(IntPtr appWindow); - } - -} + /// + /// Show the share flyout for the window identified by a window handle + /// + /// The window handle + /// HRESULT + [PreserveSig] + HResult ShowShareUIForWindow(IntPtr appWindow); + } +} \ No newline at end of file diff --git a/src/Greenshot.Plugin.Win10/Processors/Win10OcrProcessor.cs b/src/Greenshot.Plugin.Win10/Processors/Win10OcrProcessor.cs index c70c6fb27..cfbf88d18 100644 --- a/src/Greenshot.Plugin.Win10/Processors/Win10OcrProcessor.cs +++ b/src/Greenshot.Plugin.Win10/Processors/Win10OcrProcessor.cs @@ -25,11 +25,13 @@ using GreenshotPlugin.IniFile; using GreenshotPlugin.Interfaces; using GreenshotPlugin.Interfaces.Ocr; -namespace Greenshot.Plugin.Win10.Processors { - /// - /// This processor processes a capture to see if there is text on it - /// - public class Win10OcrProcessor : AbstractProcessor { +namespace Greenshot.Plugin.Win10.Processors +{ + /// + /// This processor processes a capture to see if there is text on it + /// + public class Win10OcrProcessor : AbstractProcessor + { private static readonly Win10Configuration Win10Configuration = IniConfig.GetIniSection(); public override string Designation => "Windows10OcrProcessor"; @@ -46,10 +48,12 @@ namespace Greenshot.Plugin.Win10.Processors { { return false; } + if (captureDetails == null || captureDetails.OcrInformation != null) { return false; } + var ocrProvider = SimpleServiceProvider.Current.GetInstance(); if (ocrProvider == null) @@ -68,5 +72,5 @@ namespace Greenshot.Plugin.Win10.Processors { return true; } - } -} + } +} \ No newline at end of file diff --git a/src/Greenshot.Plugin.Win10/Properties/AssemblyInfo.cs b/src/Greenshot.Plugin.Win10/Properties/AssemblyInfo.cs index 7c2dc1d2c..d1b6cbd1b 100644 --- a/src/Greenshot.Plugin.Win10/Properties/AssemblyInfo.cs +++ b/src/Greenshot.Plugin.Win10/Properties/AssemblyInfo.cs @@ -12,4 +12,4 @@ using System.Runtime.InteropServices; [assembly: ComVisible(false)] // The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("9801f62c-540f-4bfe-9211-6405dede563b")] +[assembly: Guid("9801f62c-540f-4bfe-9211-6405dede563b")] \ No newline at end of file diff --git a/src/Greenshot.Plugin.Win10/ToastNotificationService.cs b/src/Greenshot.Plugin.Win10/ToastNotificationService.cs index 444d397ef..eea987802 100644 --- a/src/Greenshot.Plugin.Win10/ToastNotificationService.cs +++ b/src/Greenshot.Plugin.Win10/ToastNotificationService.cs @@ -42,12 +42,14 @@ namespace Greenshot.Plugin.Win10 private static readonly CoreConfiguration CoreConfiguration = IniConfig.GetIniSection(); private readonly string _imageFilePath; + public ToastNotificationService() { if (ToastNotificationManagerCompat.WasCurrentProcessToastActivated()) { Log.Info("Greenshot was activated due to a toast."); } + // Listen to notification activation ToastNotificationManagerCompat.OnActivated += toastArgs => { @@ -65,6 +67,7 @@ namespace Greenshot.Plugin.Win10 { Directory.CreateDirectory(localAppData); } + _imageFilePath = Path.Combine(localAppData, "greenshot.png"); if (File.Exists(_imageFilePath)) @@ -90,6 +93,7 @@ namespace Greenshot.Plugin.Win10 { return; } + // Prepare the toast notifier. Be sure to specify the AppUserModelId on your application's shortcut! var toastNotifier = ToastNotificationManagerCompat.CreateToastNotifier(); @@ -109,7 +113,6 @@ namespace Greenshot.Plugin.Win10 // Generate the toast and send it off new ToastContentBuilder() - .AddArgument("ToastID", 100) // Inline image .AddText(message) @@ -119,6 +122,7 @@ namespace Greenshot.Plugin.Win10 { // Windows 10 first with 1903: ExpiresOnReboot = true toast.ExpirationTime = timeout.HasValue ? DateTimeOffset.Now.Add(timeout.Value) : (DateTimeOffset?) null; + void ToastActivatedHandler(ToastNotification toastNotification, object sender) { try @@ -145,6 +149,7 @@ namespace Greenshot.Plugin.Win10 { return; } + try { onClosedAction?.Invoke(); @@ -158,8 +163,8 @@ namespace Greenshot.Plugin.Win10 // Remove the other handler too toast.Activated -= ToastActivatedHandler; toast.Failed -= ToastOnFailed; - } + toast.Dismissed += ToastDismissedHandler; toast.Failed += ToastOnFailed; }); @@ -196,9 +201,10 @@ namespace Greenshot.Plugin.Win10 { return new ToastNotificationService(); } + Log.Warn("ToastNotificationActionTrigger not available."); return null; } } -} +} \ No newline at end of file diff --git a/src/Greenshot.Plugin.Win10/Win10Configuration.cs b/src/Greenshot.Plugin.Win10/Win10Configuration.cs index 9c24787bf..bbd1ff35e 100644 --- a/src/Greenshot.Plugin.Win10/Win10Configuration.cs +++ b/src/Greenshot.Plugin.Win10/Win10Configuration.cs @@ -23,12 +23,13 @@ using GreenshotPlugin.IniFile; namespace Greenshot.Plugin.Win10 { - /// - /// Description of Win10Configuration. - /// - [IniSection("Win10", Description = "Greenshot Win10 Plugin configuration")] - public class Win10Configuration : IniSection { - [IniProperty("AlwaysRunOCROnCapture", Description="Determines if OCR is run automatically on every capture", DefaultValue="False")] - public bool AlwaysRunOCROnCapture { get; set; } - } -} + /// + /// Description of Win10Configuration. + /// + [IniSection("Win10", Description = "Greenshot Win10 Plugin configuration")] + public class Win10Configuration : IniSection + { + [IniProperty("AlwaysRunOCROnCapture", Description = "Determines if OCR is run automatically on every capture", DefaultValue = "False")] + public bool AlwaysRunOCROnCapture { get; set; } + } +} \ No newline at end of file diff --git a/src/Greenshot.Plugin.Win10/Win10OcrProvider.cs b/src/Greenshot.Plugin.Win10/Win10OcrProvider.cs index 5ade2f329..e6feb8193 100644 --- a/src/Greenshot.Plugin.Win10/Win10OcrProvider.cs +++ b/src/Greenshot.Plugin.Win10/Win10OcrProvider.cs @@ -33,24 +33,24 @@ using GreenshotPlugin.Interfaces.Plugin; namespace Greenshot.Plugin.Win10 { - /// - /// This uses the OcrEngine from Windows 10 to perform OCR on the captured image. - /// - public class Win10OcrProvider : IOcrProvider - { - private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(typeof(Win10OcrProvider)); + /// + /// This uses the OcrEngine from Windows 10 to perform OCR on the captured image. + /// + public class Win10OcrProvider : IOcrProvider + { + private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(typeof(Win10OcrProvider)); - /// - /// Constructor, this is only debug information - /// - public Win10OcrProvider() - { - var languages = OcrEngine.AvailableRecognizerLanguages; - foreach (var language in languages) - { - Log.DebugFormat("Found language {0} {1}", language.NativeName, language.LanguageTag); - } - } + /// + /// Constructor, this is only debug information + /// + public Win10OcrProvider() + { + var languages = OcrEngine.AvailableRecognizerLanguages; + foreach (var language in languages) + { + Log.DebugFormat("Found language {0} {1}", language.NativeName, language.LanguageTag); + } + } /// /// Scan the surface bitmap for text, and get the OcrResult @@ -68,15 +68,16 @@ namespace Greenshot.Plugin.Win10 result = await DoOcrAsync(randomAccessStream); } + return result; } - /// - /// Scan the Image for text, and get the OcrResult - /// - /// Image - /// OcrResult sync - public async Task DoOcrAsync(Image image) + /// + /// Scan the Image for text, and get the OcrResult + /// + /// Image + /// OcrResult sync + public async Task DoOcrAsync(Image image) { OcrInformation result; using (var imageStream = new MemoryStream()) @@ -86,29 +87,31 @@ namespace Greenshot.Plugin.Win10 var randomAccessStream = imageStream.AsRandomAccessStream(); result = await DoOcrAsync(randomAccessStream); - } - return result; + } + + return result; } - /// - /// Scan the surface bitmap for text, and get the OcrResult - /// - /// IRandomAccessStream - /// OcrResult sync - public async Task DoOcrAsync(IRandomAccessStream randomAccessStream) + /// + /// Scan the surface bitmap for text, and get the OcrResult + /// + /// IRandomAccessStream + /// OcrResult sync + public async Task DoOcrAsync(IRandomAccessStream randomAccessStream) { var ocrEngine = OcrEngine.TryCreateFromUserProfileLanguages(); if (ocrEngine is null) { return null; } - var decoder = await BitmapDecoder.CreateAsync(randomAccessStream); - var softwareBitmap = await decoder.GetSoftwareBitmapAsync(); - var ocrResult = await ocrEngine.RecognizeAsync(softwareBitmap); + var decoder = await BitmapDecoder.CreateAsync(randomAccessStream); + var softwareBitmap = await decoder.GetSoftwareBitmapAsync(); + + var ocrResult = await ocrEngine.RecognizeAsync(softwareBitmap); return CreateOcrInformation(ocrResult); - } + } /// /// Create the OcrInformation @@ -131,8 +134,8 @@ namespace Greenshot.Plugin.Win10 for (var index = 0; index < ocrLine.Words.Count; index++) { var ocrWord = ocrLine.Words[index]; - var location = new Rectangle((int)ocrWord.BoundingRect.X, (int)ocrWord.BoundingRect.Y, - (int)ocrWord.BoundingRect.Width, (int)ocrWord.BoundingRect.Height); + var location = new Rectangle((int) ocrWord.BoundingRect.X, (int) ocrWord.BoundingRect.Y, + (int) ocrWord.BoundingRect.Width, (int) ocrWord.BoundingRect.Height); var word = line.Words[index]; word.Text = ocrWord.Text; @@ -143,4 +146,4 @@ namespace Greenshot.Plugin.Win10 return result; } } -} +} \ No newline at end of file diff --git a/src/Greenshot.Plugin.Win10/Win10Plugin.cs b/src/Greenshot.Plugin.Win10/Win10Plugin.cs index b70a417cb..25d8d048e 100644 --- a/src/Greenshot.Plugin.Win10/Win10Plugin.cs +++ b/src/Greenshot.Plugin.Win10/Win10Plugin.cs @@ -29,53 +29,52 @@ using GreenshotPlugin.Interfaces.Plugin; namespace Greenshot.Plugin.Win10 { - /// - /// This is the Win10Plugin - /// - [Plugin("Win10", false)] - public sealed class Win10Plugin : IGreenshotPlugin - { + /// + /// This is the Win10Plugin + /// + [Plugin("Win10", false)] + public sealed class Win10Plugin : IGreenshotPlugin + { private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(typeof(Win10Plugin)); public void Dispose() - { - // Nothing to dispose - } + { + // Nothing to dispose + } public void Configure() - { - throw new NotImplementedException(); - } + { + throw new NotImplementedException(); + } /// - /// Implementation of the IGreenshotPlugin.Initialize - /// - /// true if plugin is initialized, false if not (doesn't show) - public bool Initialize() - { - // Here we check if the build version of Windows is actually what we support + /// Implementation of the IGreenshotPlugin.Initialize + /// + /// true if plugin is initialized, false if not (doesn't show) + public bool Initialize() + { + // Here we check if the build version of Windows is actually what we support if (!WindowsVersion.IsWindows10BuildOrLater(17763)) { - Log.WarnFormat("No support for Windows build {0}", WindowsVersion.BuildVersion); + Log.WarnFormat("No support for Windows build {0}", WindowsVersion.BuildVersion); return false; } SimpleServiceProvider.Current.AddService(ToastNotificationService.Create()); - // Set this as IOcrProvider - SimpleServiceProvider.Current.AddService(new Win10OcrProvider()); + // Set this as IOcrProvider + SimpleServiceProvider.Current.AddService(new Win10OcrProvider()); // Add the processor SimpleServiceProvider.Current.AddService(new Win10OcrProcessor()); // Add the destinations - SimpleServiceProvider.Current.AddService(new Win10OcrDestination()); + SimpleServiceProvider.Current.AddService(new Win10OcrDestination()); SimpleServiceProvider.Current.AddService(new Win10ShareDestination()); - return true; - } + return true; + } - public void Shutdown() - { - // Nothing to shutdown - } - } - -} + public void Shutdown() + { + // Nothing to shutdown + } + } +} \ No newline at end of file diff --git a/src/Greenshot/App.config b/src/Greenshot/App.config index f15ae706e..3b81b058d 100644 --- a/src/Greenshot/App.config +++ b/src/Greenshot/App.config @@ -1,18 +1,19 @@ + - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Greenshot/Configuration/EditorConfiguration.cs b/src/Greenshot/Configuration/EditorConfiguration.cs index 019ea9ef6..97856f5d0 100644 --- a/src/Greenshot/Configuration/EditorConfiguration.cs +++ b/src/Greenshot/Configuration/EditorConfiguration.cs @@ -18,10 +18,10 @@ * 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 Greenshot.Drawing.Fields; using GreenshotPlugin.Effects; using GreenshotPlugin.IniFile; @@ -29,123 +29,153 @@ using GreenshotPlugin.Interfaces.Drawing; using GreenshotPlugin.UnmanagedHelpers.Enums; using GreenshotPlugin.UnmanagedHelpers.Structs; -namespace Greenshot.Configuration { - /// - /// Description of CoreConfiguration. - /// - [IniSection("Editor", Description="Greenshot editor configuration")] - public class EditorConfiguration : IniSection { - [IniProperty("RecentColors", Separator="|", Description="Last used colors")] - public List RecentColors { get; set; } +namespace Greenshot.Configuration +{ + /// + /// Description of CoreConfiguration. + /// + [IniSection("Editor", Description = "Greenshot editor configuration")] + public class EditorConfiguration : IniSection + { + [IniProperty("RecentColors", Separator = "|", Description = "Last used colors")] + public List RecentColors { get; set; } - [IniProperty("LastFieldValue", Separator="|", Description="Field values, make sure the last used settings are re-used")] - public Dictionary LastUsedFieldValues { get; set; } + [IniProperty("LastFieldValue", Separator = "|", Description = "Field values, make sure the last used settings are re-used")] + public Dictionary LastUsedFieldValues { get; set; } - [IniProperty("MatchSizeToCapture", Description="Match the editor window size to the capture", DefaultValue="True")] - public bool MatchSizeToCapture { get; set; } - [IniProperty("WindowPlacementFlags", Description="Placement flags", DefaultValue="0")] - public WindowPlacementFlags WindowPlacementFlags { get; set; } - [IniProperty("WindowShowCommand", Description="Show command", DefaultValue="Normal")] - public ShowWindowCommand ShowWindowCommand { get; set; } - [IniProperty("WindowMinPosition", Description="Position of minimized window", DefaultValue="-1,-1")] - public Point WindowMinPosition { get; set; } - [IniProperty("WindowMaxPosition", Description="Position of maximized window", DefaultValue="-1,-1")] - public Point WindowMaxPosition { get; set; } - [IniProperty("WindowNormalPosition", Description="Position of normal window", DefaultValue="100,100,400,400")] - public Rectangle WindowNormalPosition { get; set; } - [IniProperty("ReuseEditor", Description = "Reuse already open editor", DefaultValue = "false")] - public bool ReuseEditor { get; set; } - [IniProperty("FreehandSensitivity", Description = "The smaller this number, the less smoothing is used. Decrease for detailed drawing, e.g. when using a pen. Increase for smoother lines. e.g. when you want to draw a smooth line.", DefaultValue = "3")] - public int FreehandSensitivity { get; set; } - [IniProperty("SuppressSaveDialogAtClose", Description="Suppressed the 'do you want to save' dialog when closing the editor.", DefaultValue="False")] - public bool SuppressSaveDialogAtClose { get; set; } + [IniProperty("MatchSizeToCapture", Description = "Match the editor window size to the capture", DefaultValue = "True")] + public bool MatchSizeToCapture { get; set; } - [IniProperty("DropShadowEffectSettings", Description = "Settings for the drop shadow effect.")] - public DropShadowEffect DropShadowEffectSettings { get; set; } + [IniProperty("WindowPlacementFlags", Description = "Placement flags", DefaultValue = "0")] + public WindowPlacementFlags WindowPlacementFlags { get; set; } - [IniProperty("TornEdgeEffectSettings", Description = "Settings for the torn edge effect.")] - public TornEdgeEffect TornEdgeEffectSettings { get; set; } + [IniProperty("WindowShowCommand", Description = "Show command", DefaultValue = "Normal")] + public ShowWindowCommand ShowWindowCommand { get; set; } - public override void AfterLoad() { - base.AfterLoad(); - if (RecentColors == null) { - RecentColors = new List(); - } - } + [IniProperty("WindowMinPosition", Description = "Position of minimized window", DefaultValue = "-1,-1")] + public Point WindowMinPosition { get; set; } - /// Type of the class for which to create the field - /// FieldType of the field to construct - /// - /// a new Field of the given fieldType, with the scope of it's value being restricted to the Type scope - public IField CreateField(Type requestingType, IFieldType fieldType, object preferredDefaultValue) - { - string requestingTypeName = requestingType.Name; - string requestedField = requestingTypeName + "." + fieldType.Name; - object fieldValue = preferredDefaultValue; - - // Check if the configuration exists - if (LastUsedFieldValues == null) { - LastUsedFieldValues = new Dictionary(); - } - - // Check if settings for the requesting type exist, if not create! - if (LastUsedFieldValues.ContainsKey(requestedField)) { - // Check if a value is set (not null)! - if (LastUsedFieldValues[requestedField] != null) { - fieldValue = LastUsedFieldValues[requestedField]; - } else { - // Overwrite null value - LastUsedFieldValues[requestedField] = fieldValue; - } - } else { - LastUsedFieldValues.Add(requestedField, fieldValue); - } - return new Field(fieldType, requestingType) - { - Value = fieldValue - }; - } + [IniProperty("WindowMaxPosition", Description = "Position of maximized window", DefaultValue = "-1,-1")] + public Point WindowMaxPosition { get; set; } - public void UpdateLastFieldValue(IField field) - { - string requestedField = field.Scope + "." + field.FieldType.Name; - // Check if the configuration exists - if (LastUsedFieldValues == null) { - LastUsedFieldValues = new Dictionary(); - } - // check if settings for the requesting type exist, if not create! - if (LastUsedFieldValues.ContainsKey(requestedField)) { - LastUsedFieldValues[requestedField] = field.Value; - } else { - LastUsedFieldValues.Add(requestedField, field.Value); - } - } + [IniProperty("WindowNormalPosition", Description = "Position of normal window", DefaultValue = "100,100,400,400")] + public Rectangle WindowNormalPosition { get; set; } - public void ResetEditorPlacement() - { - WindowNormalPosition = new Rectangle(100, 100, 400, 400); - WindowMaxPosition = new Point(-1,-1); - WindowMinPosition = new Point(-1, -1); - WindowPlacementFlags = 0; - ShowWindowCommand = ShowWindowCommand.Normal; - } + [IniProperty("ReuseEditor", Description = "Reuse already open editor", DefaultValue = "false")] + public bool ReuseEditor { get; set; } - public WindowPlacement GetEditorPlacement() { - WindowPlacement placement = WindowPlacement.Default; - placement.NormalPosition = new RECT(WindowNormalPosition); - placement.MaxPosition = new POINT(WindowMaxPosition); - placement.MinPosition = new POINT(WindowMinPosition); - placement.ShowCmd = ShowWindowCommand; - placement.Flags = WindowPlacementFlags; - return placement; - } + [IniProperty("FreehandSensitivity", + Description = + "The smaller this number, the less smoothing is used. Decrease for detailed drawing, e.g. when using a pen. Increase for smoother lines. e.g. when you want to draw a smooth line.", + DefaultValue = "3")] + public int FreehandSensitivity { get; set; } - public void SetEditorPlacement(WindowPlacement placement) { - WindowNormalPosition = placement.NormalPosition.ToRectangle(); - WindowMaxPosition = placement.MaxPosition.ToPoint(); - WindowMinPosition = placement.MinPosition.ToPoint(); - ShowWindowCommand = placement.ShowCmd; - WindowPlacementFlags = placement.Flags; - } - } -} + [IniProperty("SuppressSaveDialogAtClose", Description = "Suppressed the 'do you want to save' dialog when closing the editor.", DefaultValue = "False")] + public bool SuppressSaveDialogAtClose { get; set; } + + [IniProperty("DropShadowEffectSettings", Description = "Settings for the drop shadow effect.")] + public DropShadowEffect DropShadowEffectSettings { get; set; } + + [IniProperty("TornEdgeEffectSettings", Description = "Settings for the torn edge effect.")] + public TornEdgeEffect TornEdgeEffectSettings { get; set; } + + public override void AfterLoad() + { + base.AfterLoad(); + if (RecentColors == null) + { + RecentColors = new List(); + } + } + + /// Type of the class for which to create the field + /// FieldType of the field to construct + /// + /// a new Field of the given fieldType, with the scope of it's value being restricted to the Type scope + public IField CreateField(Type requestingType, IFieldType fieldType, object preferredDefaultValue) + { + string requestingTypeName = requestingType.Name; + string requestedField = requestingTypeName + "." + fieldType.Name; + object fieldValue = preferredDefaultValue; + + // Check if the configuration exists + if (LastUsedFieldValues == null) + { + LastUsedFieldValues = new Dictionary(); + } + + // Check if settings for the requesting type exist, if not create! + if (LastUsedFieldValues.ContainsKey(requestedField)) + { + // Check if a value is set (not null)! + if (LastUsedFieldValues[requestedField] != null) + { + fieldValue = LastUsedFieldValues[requestedField]; + } + else + { + // Overwrite null value + LastUsedFieldValues[requestedField] = fieldValue; + } + } + else + { + LastUsedFieldValues.Add(requestedField, fieldValue); + } + + return new Field(fieldType, requestingType) + { + Value = fieldValue + }; + } + + public void UpdateLastFieldValue(IField field) + { + string requestedField = field.Scope + "." + field.FieldType.Name; + // Check if the configuration exists + if (LastUsedFieldValues == null) + { + LastUsedFieldValues = new Dictionary(); + } + + // check if settings for the requesting type exist, if not create! + if (LastUsedFieldValues.ContainsKey(requestedField)) + { + LastUsedFieldValues[requestedField] = field.Value; + } + else + { + LastUsedFieldValues.Add(requestedField, field.Value); + } + } + + public void ResetEditorPlacement() + { + WindowNormalPosition = new Rectangle(100, 100, 400, 400); + WindowMaxPosition = new Point(-1, -1); + WindowMinPosition = new Point(-1, -1); + WindowPlacementFlags = 0; + ShowWindowCommand = ShowWindowCommand.Normal; + } + + public WindowPlacement GetEditorPlacement() + { + WindowPlacement placement = WindowPlacement.Default; + placement.NormalPosition = new RECT(WindowNormalPosition); + placement.MaxPosition = new POINT(WindowMaxPosition); + placement.MinPosition = new POINT(WindowMinPosition); + placement.ShowCmd = ShowWindowCommand; + placement.Flags = WindowPlacementFlags; + return placement; + } + + public void SetEditorPlacement(WindowPlacement placement) + { + WindowNormalPosition = placement.NormalPosition.ToRectangle(); + WindowMaxPosition = placement.MaxPosition.ToPoint(); + WindowMinPosition = placement.MinPosition.ToPoint(); + ShowWindowCommand = placement.ShowCmd; + WindowPlacementFlags = placement.Flags; + } + } +} \ No newline at end of file diff --git a/src/Greenshot/Configuration/LanguageKeys.cs b/src/Greenshot/Configuration/LanguageKeys.cs index a668218eb..e156261b7 100644 --- a/src/Greenshot/Configuration/LanguageKeys.cs +++ b/src/Greenshot/Configuration/LanguageKeys.cs @@ -21,60 +21,62 @@ using System.Diagnostics.CodeAnalysis; -namespace Greenshot.Configuration { +namespace Greenshot.Configuration +{ [SuppressMessage("ReSharper", "InconsistentNaming")] - public enum LangKey { - none, + public enum LangKey + { + none, contextmenu_capturefullscreen_all, - contextmenu_capturefullscreen_left, - contextmenu_capturefullscreen_top, - contextmenu_capturefullscreen_right, - contextmenu_capturefullscreen_bottom, + contextmenu_capturefullscreen_left, + contextmenu_capturefullscreen_top, + contextmenu_capturefullscreen_right, + contextmenu_capturefullscreen_bottom, contextmenu_captureie, editor_clipboardfailed, - editor_close_on_save, - editor_close_on_save_title, - editor_copytoclipboard, - editor_cuttoclipboard, - editor_deleteelement, - editor_downonelevel, - editor_downtobottom, - editor_duplicate, - editor_email, - editor_imagesaved, - editor_title, - editor_uponelevel, - editor_uptotop, - editor_undo, - editor_redo, - editor_resetsize, - error, - error_multipleinstances, - error_openfile, - error_openlink, - error_save, - error_save_invalid_chars, - print_error, - quicksettings_destination_file, - settings_destination, - settings_destination_clipboard, - settings_destination_editor, - settings_destination_fileas, - settings_destination_printer, - settings_destination_picker, - settings_filenamepattern, - settings_message_filenamepattern, - settings_printoptions, - settings_tooltip_filenamepattern, - settings_tooltip_language, - settings_tooltip_primaryimageformat, - settings_tooltip_storagelocation, - settings_visualization, - settings_window_capture_mode, - tooltip_firststart, - warning, - warning_hotkeys, - wait_ie_capture, - update_found + editor_close_on_save, + editor_close_on_save_title, + editor_copytoclipboard, + editor_cuttoclipboard, + editor_deleteelement, + editor_downonelevel, + editor_downtobottom, + editor_duplicate, + editor_email, + editor_imagesaved, + editor_title, + editor_uponelevel, + editor_uptotop, + editor_undo, + editor_redo, + editor_resetsize, + error, + error_multipleinstances, + error_openfile, + error_openlink, + error_save, + error_save_invalid_chars, + print_error, + quicksettings_destination_file, + settings_destination, + settings_destination_clipboard, + settings_destination_editor, + settings_destination_fileas, + settings_destination_printer, + settings_destination_picker, + settings_filenamepattern, + settings_message_filenamepattern, + settings_printoptions, + settings_tooltip_filenamepattern, + settings_tooltip_language, + settings_tooltip_primaryimageformat, + settings_tooltip_storagelocation, + settings_visualization, + settings_window_capture_mode, + tooltip_firststart, + warning, + warning_hotkeys, + wait_ie_capture, + update_found } -} +} \ No newline at end of file diff --git a/src/Greenshot/Controls/BindableToolStripButton.cs b/src/Greenshot/Controls/BindableToolStripButton.cs index b44a1a31c..37088476a 100644 --- a/src/Greenshot/Controls/BindableToolStripButton.cs +++ b/src/Greenshot/Controls/BindableToolStripButton.cs @@ -18,32 +18,32 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ + using System; using System.ComponentModel; using System.Windows.Forms; using GreenshotPlugin.Controls; -namespace Greenshot.Controls { - /// - /// Description of BindableToolStripButton. - /// - public class BindableToolStripButton : ToolStripButton, INotifyPropertyChanged, IGreenshotLanguageBindable { - public event PropertyChangedEventHandler PropertyChanged; +namespace Greenshot.Controls +{ + /// + /// Description of BindableToolStripButton. + /// + public class BindableToolStripButton : ToolStripButton, INotifyPropertyChanged, IGreenshotLanguageBindable + { + public event PropertyChangedEventHandler PropertyChanged; - [Category("Greenshot"), DefaultValue(null), Description("Specifies key of the language file to use when displaying the text.")] - public string LanguageKey { - get; - set; - } + [Category("Greenshot"), DefaultValue(null), Description("Specifies key of the language file to use when displaying the text.")] + public string LanguageKey { get; set; } - public BindableToolStripButton() - { - CheckedChanged += BindableToolStripButton_CheckedChanged; - } + public BindableToolStripButton() + { + CheckedChanged += BindableToolStripButton_CheckedChanged; + } - private void BindableToolStripButton_CheckedChanged(object sender, EventArgs e) - { - PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Checked")); - } - } -} + private void BindableToolStripButton_CheckedChanged(object sender, EventArgs e) + { + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Checked")); + } + } +} \ No newline at end of file diff --git a/src/Greenshot/Controls/BindableToolStripComboBox.cs b/src/Greenshot/Controls/BindableToolStripComboBox.cs index 3637f2380..b0f87cff8 100644 --- a/src/Greenshot/Controls/BindableToolStripComboBox.cs +++ b/src/Greenshot/Controls/BindableToolStripComboBox.cs @@ -18,32 +18,32 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ + using System; using System.ComponentModel; using System.Windows.Forms; using GreenshotPlugin.Controls; -namespace Greenshot.Controls { - /// - /// A simple ToolStripComboBox implementing INotifyPropertyChanged for data binding - /// - public class BindableToolStripComboBox : ToolStripComboBox, INotifyPropertyChanged, IGreenshotLanguageBindable { - public event PropertyChangedEventHandler PropertyChanged; +namespace Greenshot.Controls +{ + /// + /// A simple ToolStripComboBox implementing INotifyPropertyChanged for data binding + /// + public class BindableToolStripComboBox : ToolStripComboBox, INotifyPropertyChanged, IGreenshotLanguageBindable + { + public event PropertyChangedEventHandler PropertyChanged; - [Category("Greenshot"), DefaultValue(null), Description("Specifies key of the language file to use when displaying the text.")] - public string LanguageKey { - get; - set; - } + [Category("Greenshot"), DefaultValue(null), Description("Specifies key of the language file to use when displaying the text.")] + public string LanguageKey { get; set; } - public BindableToolStripComboBox() - { - SelectedIndexChanged += BindableToolStripComboBox_SelectedIndexChanged; - } + public BindableToolStripComboBox() + { + SelectedIndexChanged += BindableToolStripComboBox_SelectedIndexChanged; + } - private void BindableToolStripComboBox_SelectedIndexChanged(object sender, EventArgs e) - { - PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("SelectedItem")); - } - } -} + private void BindableToolStripComboBox_SelectedIndexChanged(object sender, EventArgs e) + { + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("SelectedItem")); + } + } +} \ No newline at end of file diff --git a/src/Greenshot/Controls/BindableToolStripDropDownButton.cs b/src/Greenshot/Controls/BindableToolStripDropDownButton.cs index 4071d6b81..428b66bb3 100644 --- a/src/Greenshot/Controls/BindableToolStripDropDownButton.cs +++ b/src/Greenshot/Controls/BindableToolStripDropDownButton.cs @@ -18,53 +18,65 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ + using System.ComponentModel; using System.Windows.Forms; using GreenshotPlugin.Controls; -namespace Greenshot.Controls { - /// - /// A simple ToolStripDropDownButton implementing INotifyPropertyChanged for data binding. - /// Also, when a DropDownItem is selected, the DropDownButton adopts its Tag and Image. - /// The selected tag can be accessed via SelectedTag property. - /// - public class BindableToolStripDropDownButton : ToolStripDropDownButton, INotifyPropertyChanged, IGreenshotLanguageBindable { - - public event PropertyChangedEventHandler PropertyChanged; +namespace Greenshot.Controls +{ + /// + /// A simple ToolStripDropDownButton implementing INotifyPropertyChanged for data binding. + /// Also, when a DropDownItem is selected, the DropDownButton adopts its Tag and Image. + /// The selected tag can be accessed via SelectedTag property. + /// + public class BindableToolStripDropDownButton : ToolStripDropDownButton, INotifyPropertyChanged, IGreenshotLanguageBindable + { + public event PropertyChangedEventHandler PropertyChanged; - [Category("Greenshot"), DefaultValue(null), Description("Specifies key of the language file to use when displaying the text.")] - public string LanguageKey { - get; - set; - } + [Category("Greenshot"), DefaultValue(null), Description("Specifies key of the language file to use when displaying the text.")] + public string LanguageKey { get; set; } - public object SelectedTag { - get { if(Tag==null && DropDownItems.Count>0) Tag=DropDownItems[0].Tag; return Tag; } - set { AdoptFromTag(value); } - } - - protected override void OnDropDownItemClicked(ToolStripItemClickedEventArgs e) { - ToolStripItem clickedItem = e.ClickedItem; - if(Tag == null || !Tag.Equals(clickedItem.Tag)) { - Tag = clickedItem.Tag; - Image = clickedItem.Image; - PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("SelectedTag")); - } - base.OnDropDownItemClicked(e); - } - - private void AdoptFromTag(object tag) { - if(Tag == null || !Tag.Equals(tag)) { - Tag = tag; - foreach(ToolStripItem item in DropDownItems) { - if(item.Tag != null && item.Tag.Equals(tag)) { - Image = item.Image; - break; - } - } - Tag = tag; - PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("SelectedTag")); - } - } - } -} + public object SelectedTag + { + get + { + if (Tag == null && DropDownItems.Count > 0) Tag = DropDownItems[0].Tag; + return Tag; + } + set { AdoptFromTag(value); } + } + + protected override void OnDropDownItemClicked(ToolStripItemClickedEventArgs e) + { + ToolStripItem clickedItem = e.ClickedItem; + if (Tag == null || !Tag.Equals(clickedItem.Tag)) + { + Tag = clickedItem.Tag; + Image = clickedItem.Image; + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("SelectedTag")); + } + + base.OnDropDownItemClicked(e); + } + + private void AdoptFromTag(object tag) + { + if (Tag == null || !Tag.Equals(tag)) + { + Tag = tag; + foreach (ToolStripItem item in DropDownItems) + { + if (item.Tag != null && item.Tag.Equals(tag)) + { + Image = item.Image; + break; + } + } + + Tag = tag; + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("SelectedTag")); + } + } + } +} \ No newline at end of file diff --git a/src/Greenshot/Controls/ColorButton.cs b/src/Greenshot/Controls/ColorButton.cs index fe31d2e11..068033522 100644 --- a/src/Greenshot/Controls/ColorButton.cs +++ b/src/Greenshot/Controls/ColorButton.cs @@ -18,6 +18,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ + using System; using System.ComponentModel; using System.Drawing; @@ -26,65 +27,73 @@ using System.Windows.Forms; using GreenshotPlugin.Controls; using ColorDialog = Greenshot.Forms.ColorDialog; -namespace Greenshot.Controls { - /// - /// Description of ColorButton. - /// - public class ColorButton : Button, IGreenshotLanguageBindable { - public event PropertyChangedEventHandler PropertyChanged; - private Color _selectedColor = Color.White; +namespace Greenshot.Controls +{ + /// + /// Description of ColorButton. + /// + public class ColorButton : Button, IGreenshotLanguageBindable + { + public event PropertyChangedEventHandler PropertyChanged; + private Color _selectedColor = Color.White; - [Category("Greenshot"), DefaultValue(null), Description("Specifies key of the language file to use when displaying the text.")] - public string LanguageKey { - get; - set; - } + [Category("Greenshot"), DefaultValue(null), Description("Specifies key of the language file to use when displaying the text.")] + public string LanguageKey { get; set; } - public ColorButton() { - Click += ColorButtonClick; - } + public ColorButton() + { + Click += ColorButtonClick; + } - public Color SelectedColor { - get {return _selectedColor;} - set { - _selectedColor = value; + public Color SelectedColor + { + get { return _selectedColor; } + set + { + _selectedColor = value; - Brush brush; - if(value != Color.Transparent) { - brush = new SolidBrush(value); - } else { - brush = new HatchBrush(HatchStyle.Percent50, Color.White, Color.Gray); - } - - if (Image != null) + Brush brush; + if (value != Color.Transparent) { - using Graphics graphics = Graphics.FromImage(Image); - graphics.FillRectangle(brush, new Rectangle(4,17,16,3)); + brush = new SolidBrush(value); + } + else + { + brush = new HatchBrush(HatchStyle.Percent50, Color.White, Color.Gray); } - // cleanup GDI Object - brush.Dispose(); - Invalidate(); - } - } + if (Image != null) + { + using Graphics graphics = Graphics.FromImage(Image); + graphics.FillRectangle(brush, new Rectangle(4, 17, 16, 3)); + } - private void ColorButtonClick(object sender, EventArgs e) { - var colorDialog = new ColorDialog - { - Color = SelectedColor - }; - // Using the parent to make sure the dialog doesn't show on another window - colorDialog.ShowDialog(Parent.Parent); - if (colorDialog.DialogResult == DialogResult.Cancel) - { - return; - } - if (colorDialog.Color.Equals(SelectedColor)) - { - return; - } - SelectedColor = colorDialog.Color; - PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("SelectedColor")); - } - } -} + // cleanup GDI Object + brush.Dispose(); + Invalidate(); + } + } + + private void ColorButtonClick(object sender, EventArgs e) + { + var colorDialog = new ColorDialog + { + Color = SelectedColor + }; + // Using the parent to make sure the dialog doesn't show on another window + colorDialog.ShowDialog(Parent.Parent); + if (colorDialog.DialogResult == DialogResult.Cancel) + { + return; + } + + if (colorDialog.Color.Equals(SelectedColor)) + { + return; + } + + SelectedColor = colorDialog.Color; + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("SelectedColor")); + } + } +} \ No newline at end of file diff --git a/src/Greenshot/Controls/ContextMenuToolStripProfessionalRenderer.cs b/src/Greenshot/Controls/ContextMenuToolStripProfessionalRenderer.cs index e5ebf5478..e85817d7f 100644 --- a/src/Greenshot/Controls/ContextMenuToolStripProfessionalRenderer.cs +++ b/src/Greenshot/Controls/ContextMenuToolStripProfessionalRenderer.cs @@ -24,22 +24,27 @@ using System.Drawing; using System.Windows.Forms; using GreenshotPlugin.IniFile; -namespace Greenshot.Controls { - /// - /// ToolStripProfessionalRenderer which draws the Check correctly when the icons are larger - /// - public class ContextMenuToolStripProfessionalRenderer : ToolStripProfessionalRenderer { - private static readonly CoreConfiguration CoreConfig = IniConfig.GetIniSection(); - private static Image _scaledCheckbox; +namespace Greenshot.Controls +{ + /// + /// ToolStripProfessionalRenderer which draws the Check correctly when the icons are larger + /// + public class ContextMenuToolStripProfessionalRenderer : ToolStripProfessionalRenderer + { + private static readonly CoreConfiguration CoreConfig = IniConfig.GetIniSection(); + private static Image _scaledCheckbox; - protected override void OnRenderItemCheck(ToolStripItemImageRenderEventArgs e) { - if (_scaledCheckbox == null || _scaledCheckbox.Size != CoreConfig.ScaledIconSize) { - _scaledCheckbox?.Dispose(); - _scaledCheckbox = ImageHelper.ResizeImage(e.Image, true, CoreConfig.ScaledIconSize.Width, CoreConfig.ScaledIconSize.Height, null); - } - Rectangle old = e.ImageRectangle; - ToolStripItemImageRenderEventArgs clone = new ToolStripItemImageRenderEventArgs(e.Graphics, e.Item, _scaledCheckbox, new Rectangle(old.X, 0, old.Width, old.Height)); - base.OnRenderItemCheck(clone); - } - } -} + protected override void OnRenderItemCheck(ToolStripItemImageRenderEventArgs e) + { + if (_scaledCheckbox == null || _scaledCheckbox.Size != CoreConfig.ScaledIconSize) + { + _scaledCheckbox?.Dispose(); + _scaledCheckbox = ImageHelper.ResizeImage(e.Image, true, CoreConfig.ScaledIconSize.Width, CoreConfig.ScaledIconSize.Height, null); + } + + Rectangle old = e.ImageRectangle; + ToolStripItemImageRenderEventArgs clone = new ToolStripItemImageRenderEventArgs(e.Graphics, e.Item, _scaledCheckbox, new Rectangle(old.X, 0, old.Width, old.Height)); + base.OnRenderItemCheck(clone); + } + } +} \ No newline at end of file diff --git a/src/Greenshot/Controls/CustomToolStripProfessionalRenderer.cs b/src/Greenshot/Controls/CustomToolStripProfessionalRenderer.cs index d559d771b..90f3a7818 100644 --- a/src/Greenshot/Controls/CustomToolStripProfessionalRenderer.cs +++ b/src/Greenshot/Controls/CustomToolStripProfessionalRenderer.cs @@ -22,46 +22,63 @@ using System.Drawing; using System.Windows.Forms; -namespace Greenshot.Controls { - /// - /// Prevent having a gradient background in the toolstrip, and the overflow button - /// See: http://stackoverflow.com/a/16926979 - /// - internal class CustomProfessionalColorTable : ProfessionalColorTable { - public override Color ToolStripGradientBegin { - get { return SystemColors.Control; } - } - public override Color ToolStripGradientMiddle { - get { return SystemColors.Control; } - } - public override Color ToolStripGradientEnd { - get { return SystemColors.Control; } - } - public override Color OverflowButtonGradientBegin { - get { return SystemColors.Control; } - } - public override Color OverflowButtonGradientMiddle { - get { return SystemColors.Control; } - } - public override Color OverflowButtonGradientEnd { - get { return SystemColors.Control; } - } - } +namespace Greenshot.Controls +{ + /// + /// Prevent having a gradient background in the toolstrip, and the overflow button + /// See: http://stackoverflow.com/a/16926979 + /// + internal class CustomProfessionalColorTable : ProfessionalColorTable + { + public override Color ToolStripGradientBegin + { + get { return SystemColors.Control; } + } - /// - /// ToolStripProfessionalRenderer without having a visual artifact - /// See: http://stackoverflow.com/a/16926979 and http://stackoverflow.com/a/13418840 - /// - public class CustomToolStripProfessionalRenderer : ToolStripProfessionalRenderer { - public CustomToolStripProfessionalRenderer() : base(new CustomProfessionalColorTable()) { - RoundedEdges = false; - } - /// - /// By overriding the OnRenderToolStripBorder we can make the ToolStrip without border - /// - /// - protected override void OnRenderToolStripBorder(ToolStripRenderEventArgs e) { - // Don't draw a border - } - } -} + public override Color ToolStripGradientMiddle + { + get { return SystemColors.Control; } + } + + public override Color ToolStripGradientEnd + { + get { return SystemColors.Control; } + } + + public override Color OverflowButtonGradientBegin + { + get { return SystemColors.Control; } + } + + public override Color OverflowButtonGradientMiddle + { + get { return SystemColors.Control; } + } + + public override Color OverflowButtonGradientEnd + { + get { return SystemColors.Control; } + } + } + + /// + /// ToolStripProfessionalRenderer without having a visual artifact + /// See: http://stackoverflow.com/a/16926979 and http://stackoverflow.com/a/13418840 + /// + public class CustomToolStripProfessionalRenderer : ToolStripProfessionalRenderer + { + public CustomToolStripProfessionalRenderer() : base(new CustomProfessionalColorTable()) + { + RoundedEdges = false; + } + + /// + /// By overriding the OnRenderToolStripBorder we can make the ToolStrip without border + /// + /// + protected override void OnRenderToolStripBorder(ToolStripRenderEventArgs e) + { + // Don't draw a border + } + } +} \ No newline at end of file diff --git a/src/Greenshot/Controls/FontFamilyComboBox.cs b/src/Greenshot/Controls/FontFamilyComboBox.cs index d37244eab..db1cef03e 100644 --- a/src/Greenshot/Controls/FontFamilyComboBox.cs +++ b/src/Greenshot/Controls/FontFamilyComboBox.cs @@ -24,97 +24,118 @@ using System.ComponentModel; using System.Drawing; using System.Windows.Forms; -namespace Greenshot.Controls { - /// - /// ToolStripComboBox containing installed font families, - /// implementing INotifyPropertyChanged for data binding - /// - public class FontFamilyComboBox : ToolStripComboBox, INotifyPropertyChanged { - public event PropertyChangedEventHandler PropertyChanged; +namespace Greenshot.Controls +{ + /// + /// ToolStripComboBox containing installed font families, + /// implementing INotifyPropertyChanged for data binding + /// + public class FontFamilyComboBox : ToolStripComboBox, INotifyPropertyChanged + { + public event PropertyChangedEventHandler PropertyChanged; - public FontFamily FontFamily { - get { return (FontFamily)SelectedItem; } - set { - if (!SelectedItem.Equals(value)) { - SelectedItem = value; - } - } - } - - public FontFamilyComboBox() - { - if (ComboBox != null) - { - ComboBox.DataSource = FontFamily.Families; - ComboBox.DisplayMember = "Name"; - SelectedIndexChanged += BindableToolStripComboBox_SelectedIndexChanged; - ComboBox.DrawMode = DrawMode.OwnerDrawFixed; - ComboBox.DrawItem += ComboBox_DrawItem; - } - } - - private void ComboBox_DrawItem(object sender, DrawItemEventArgs e) { - // DrawBackground handles drawing the background (i.e,. hot-tracked v. not) - // It uses the system colors (Bluish, and and white, by default) - // same as calling e.Graphics.FillRectangle ( SystemBrushes.Highlight, e.Bounds ); - e.DrawBackground(); - - if (e.Index > -1) { - FontFamily fontFamily = Items[e.Index] as FontFamily; - FontStyle fontStyle = FontStyle.Regular; - if (fontFamily != null && !fontFamily.IsStyleAvailable(FontStyle.Regular)) { - if (fontFamily.IsStyleAvailable(FontStyle.Bold)) { - fontStyle = FontStyle.Bold; - } else if (fontFamily.IsStyleAvailable(FontStyle.Italic)) { - fontStyle = FontStyle.Italic; - } else if (fontFamily.IsStyleAvailable(FontStyle.Strikeout)) { - fontStyle = FontStyle.Strikeout; - } else if (fontFamily.IsStyleAvailable(FontStyle.Underline)) { - fontStyle = FontStyle.Underline; - } - } - try { - if (fontFamily != null) - { - DrawText(e.Graphics, fontFamily, fontStyle, e.Bounds, fontFamily.Name); - } - } catch { - // If the drawing failed, BUG-1770 seems to have a weird case that causes: Font 'Lucida Sans Typewriter' does not support style 'Regular' - if (fontFamily != null) - { - DrawText(e.Graphics, FontFamily.GenericSansSerif, FontStyle.Regular, e.Bounds, fontFamily.Name); - } - } - } - // Uncomment this if you actually like the way the focus rectangle looks - //e.DrawFocusRectangle (); - } - - /// - /// Helper method to draw the string - /// - /// - /// - /// - /// - /// - private void DrawText(Graphics graphics, FontFamily fontFamily, FontStyle fontStyle, Rectangle bounds, string text) + public FontFamily FontFamily { - using Font font = new Font(fontFamily, Font.Size + 5, fontStyle, GraphicsUnit.Pixel); - // Make sure the text is visible by centering it in the line - using StringFormat stringFormat = new StringFormat - { - LineAlignment = StringAlignment.Center - }; - graphics.DrawString(text, font, Brushes.Black, bounds, stringFormat); + get { return (FontFamily) SelectedItem; } + set + { + if (!SelectedItem.Equals(value)) + { + SelectedItem = value; + } + } } - private void BindableToolStripComboBox_SelectedIndexChanged(object sender, EventArgs e) { + public FontFamilyComboBox() + { + if (ComboBox != null) + { + ComboBox.DataSource = FontFamily.Families; + ComboBox.DisplayMember = "Name"; + SelectedIndexChanged += BindableToolStripComboBox_SelectedIndexChanged; + ComboBox.DrawMode = DrawMode.OwnerDrawFixed; + ComboBox.DrawItem += ComboBox_DrawItem; + } + } + + private void ComboBox_DrawItem(object sender, DrawItemEventArgs e) + { + // DrawBackground handles drawing the background (i.e,. hot-tracked v. not) + // It uses the system colors (Bluish, and and white, by default) + // same as calling e.Graphics.FillRectangle ( SystemBrushes.Highlight, e.Bounds ); + e.DrawBackground(); + + if (e.Index > -1) + { + FontFamily fontFamily = Items[e.Index] as FontFamily; + FontStyle fontStyle = FontStyle.Regular; + if (fontFamily != null && !fontFamily.IsStyleAvailable(FontStyle.Regular)) + { + if (fontFamily.IsStyleAvailable(FontStyle.Bold)) + { + fontStyle = FontStyle.Bold; + } + else if (fontFamily.IsStyleAvailable(FontStyle.Italic)) + { + fontStyle = FontStyle.Italic; + } + else if (fontFamily.IsStyleAvailable(FontStyle.Strikeout)) + { + fontStyle = FontStyle.Strikeout; + } + else if (fontFamily.IsStyleAvailable(FontStyle.Underline)) + { + fontStyle = FontStyle.Underline; + } + } + + try + { + if (fontFamily != null) + { + DrawText(e.Graphics, fontFamily, fontStyle, e.Bounds, fontFamily.Name); + } + } + catch + { + // If the drawing failed, BUG-1770 seems to have a weird case that causes: Font 'Lucida Sans Typewriter' does not support style 'Regular' + if (fontFamily != null) + { + DrawText(e.Graphics, FontFamily.GenericSansSerif, FontStyle.Regular, e.Bounds, fontFamily.Name); + } + } + } + + // Uncomment this if you actually like the way the focus rectangle looks + //e.DrawFocusRectangle (); + } + + /// + /// Helper method to draw the string + /// + /// + /// + /// + /// + /// + private void DrawText(Graphics graphics, FontFamily fontFamily, FontStyle fontStyle, Rectangle bounds, string text) + { + using Font font = new Font(fontFamily, Font.Size + 5, fontStyle, GraphicsUnit.Pixel); + // Make sure the text is visible by centering it in the line + using StringFormat stringFormat = new StringFormat + { + LineAlignment = StringAlignment.Center + }; + graphics.DrawString(text, font, Brushes.Black, bounds, stringFormat); + } + + private void BindableToolStripComboBox_SelectedIndexChanged(object sender, EventArgs e) + { if (PropertyChanged == null) return; PropertyChanged(this, new PropertyChangedEventArgs("Text")); PropertyChanged(this, new PropertyChangedEventArgs("FontFamily")); PropertyChanged(this, new PropertyChangedEventArgs("SelectedIndex")); PropertyChanged(this, new PropertyChangedEventArgs("SelectedItem")); } - } -} + } +} \ No newline at end of file diff --git a/src/Greenshot/Controls/MenuStripEx.cs b/src/Greenshot/Controls/MenuStripEx.cs index 7dda22931..34f1d168a 100644 --- a/src/Greenshot/Controls/MenuStripEx.cs +++ b/src/Greenshot/Controls/MenuStripEx.cs @@ -18,45 +18,48 @@ * 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.UnmanagedHelpers.Enums; -namespace Greenshot.Controls { - /// - /// This is an extension of the default MenuStrip and allows us to click it even when the form doesn't have focus. - /// See: http://blogs.msdn.com/b/rickbrew/archive/2006/01/09/511003.aspx - /// - public class MenuStripEx : MenuStrip { - private enum NativeConstants : uint { - MA_ACTIVATE = 1, - MA_ACTIVATEANDEAT = 2, - } +namespace Greenshot.Controls +{ + /// + /// This is an extension of the default MenuStrip and allows us to click it even when the form doesn't have focus. + /// See: http://blogs.msdn.com/b/rickbrew/archive/2006/01/09/511003.aspx + /// + public class MenuStripEx : MenuStrip + { + private enum NativeConstants : uint + { + MA_ACTIVATE = 1, + MA_ACTIVATEANDEAT = 2, + } - private bool _clickThrough; - /// - /// Gets or sets whether the ToolStripEx honors item clicks when its containing form does not have input focus. - /// - /// - /// Default value is false, which is the same behavior provided by the base ToolStrip class. - /// - public bool ClickThrough { - get { - return _clickThrough; - } + private bool _clickThrough; - set { - _clickThrough = value; - } - } + /// + /// Gets or sets whether the ToolStripEx honors item clicks when its containing form does not have input focus. + /// + /// + /// Default value is false, which is the same behavior provided by the base ToolStrip class. + /// + public bool ClickThrough + { + get { return _clickThrough; } - protected override void WndProc(ref Message m) { - base.WndProc(ref m); + set { _clickThrough = value; } + } + + protected override void WndProc(ref Message m) + { + base.WndProc(ref m); var windowsMessage = (WindowsMessages) m.Msg; - if (_clickThrough && windowsMessage == WindowsMessages.WM_MOUSEACTIVATE && m.Result == (IntPtr)NativeConstants.MA_ACTIVATEANDEAT) + if (_clickThrough && windowsMessage == WindowsMessages.WM_MOUSEACTIVATE && m.Result == (IntPtr) NativeConstants.MA_ACTIVATEANDEAT) { - m.Result = (IntPtr)NativeConstants.MA_ACTIVATE; - } - } - } -} + m.Result = (IntPtr) NativeConstants.MA_ACTIVATE; + } + } + } +} \ No newline at end of file diff --git a/src/Greenshot/Controls/NonJumpingPanel.cs b/src/Greenshot/Controls/NonJumpingPanel.cs index d5565c637..8f14303a8 100644 --- a/src/Greenshot/Controls/NonJumpingPanel.cs +++ b/src/Greenshot/Controls/NonJumpingPanel.cs @@ -67,4 +67,4 @@ namespace Greenshot.Controls } } } -} +} \ No newline at end of file diff --git a/src/Greenshot/Controls/Pipette.cs b/src/Greenshot/Controls/Pipette.cs index ca7333d81..d4636b017 100644 --- a/src/Greenshot/Controls/Pipette.cs +++ b/src/Greenshot/Controls/Pipette.cs @@ -18,6 +18,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ + using System; using System.ComponentModel; using System.Drawing; @@ -27,40 +28,43 @@ using GreenshotPlugin.UnmanagedHelpers; using GreenshotPlugin.UnmanagedHelpers.Enums; using ColorDialog = Greenshot.Forms.ColorDialog; -namespace Greenshot.Controls { - /// - /// This code was supplied by Hi-Coder as a patch for Greenshot - /// Needed some modifications to be stable. - /// - public sealed class Pipette : Label, IMessageFilter, IDisposable { - private MovableShowColorForm _movableShowColorForm; - private bool _dragging; - private Cursor _cursor; - private readonly Bitmap _image; - private const int VkEsc = 27; +namespace Greenshot.Controls +{ + /// + /// This code was supplied by Hi-Coder as a patch for Greenshot + /// Needed some modifications to be stable. + /// + public sealed class Pipette : Label, IMessageFilter, IDisposable + { + private MovableShowColorForm _movableShowColorForm; + private bool _dragging; + private Cursor _cursor; + private readonly Bitmap _image; + private const int VkEsc = 27; - public event EventHandler PipetteUsed; + public event EventHandler PipetteUsed; - public Pipette() { - BorderStyle = BorderStyle.FixedSingle; - _dragging = false; - _image = (Bitmap)new ComponentResourceManager(typeof(ColorDialog)).GetObject("pipette.Image"); - Image = _image; - _cursor = CreateCursor(_image, 1, 14); - _movableShowColorForm = new MovableShowColorForm(); - Application.AddMessageFilter(this); - } - - /// - /// Create a cursor from the supplied bitmap & hotspot coordinates - /// - /// Bitmap to create an icon from - /// Hotspot X coordinate - /// Hotspot Y coordinate - /// Cursor - private static Cursor CreateCursor(Bitmap bitmap, int hotspotX, int hotspotY) + public Pipette() { - using SafeIconHandle iconHandle = new SafeIconHandle( bitmap.GetHicon()); + BorderStyle = BorderStyle.FixedSingle; + _dragging = false; + _image = (Bitmap) new ComponentResourceManager(typeof(ColorDialog)).GetObject("pipette.Image"); + Image = _image; + _cursor = CreateCursor(_image, 1, 14); + _movableShowColorForm = new MovableShowColorForm(); + Application.AddMessageFilter(this); + } + + /// + /// Create a cursor from the supplied bitmap & hotspot coordinates + /// + /// Bitmap to create an icon from + /// Hotspot X coordinate + /// Hotspot Y coordinate + /// Cursor + private static Cursor CreateCursor(Bitmap bitmap, int hotspotX, int hotspotY) + { + using SafeIconHandle iconHandle = new SafeIconHandle(bitmap.GetHicon()); User32.GetIconInfo(iconHandle, out var iconInfo); iconInfo.xHotspot = hotspotX; iconInfo.yHotspot = hotspotY; @@ -69,106 +73,132 @@ namespace Greenshot.Controls { return new Cursor(icon); } - /// - /// The bulk of the clean-up code is implemented in Dispose(bool) - /// - public new void Dispose() { - Dispose(true); - } + /// + /// The bulk of the clean-up code is implemented in Dispose(bool) + /// + public new void Dispose() + { + Dispose(true); + } - /// - /// This Dispose is called from the Dispose and the Destructor. - /// - /// When disposing==true all non-managed resources should be freed too! - protected override void Dispose(bool disposing) { - if (disposing) { - if (_cursor != null) { - _cursor.Dispose(); - } - _movableShowColorForm?.Dispose(); - } - _movableShowColorForm = null; - _cursor = null; - base.Dispose(disposing); - } + /// + /// This Dispose is called from the Dispose and the Destructor. + /// + /// When disposing==true all non-managed resources should be freed too! + protected override void Dispose(bool disposing) + { + if (disposing) + { + if (_cursor != null) + { + _cursor.Dispose(); + } - /// - /// Handle the mouse down on the Pipette "label", we take the capture and move the zoomer to the current location - /// - /// MouseEventArgs - protected override void OnMouseDown(MouseEventArgs e) { - if (e.Button == MouseButtons.Left) { - User32.SetCapture(Handle); - _movableShowColorForm.MoveTo(PointToScreen(new Point(e.X, e.Y))); - } - base.OnMouseDown(e); - } + _movableShowColorForm?.Dispose(); + } - /// - /// Handle the mouse up on the Pipette "label", we release the capture and fire the PipetteUsed event - /// - /// MouseEventArgs - protected override void OnMouseUp(MouseEventArgs e) { - if (e.Button == MouseButtons.Left) - { - //Release Capture should consume MouseUp when canceled with the escape key - User32.ReleaseCapture(); - PipetteUsed?.Invoke(this, new PipetteUsedArgs(_movableShowColorForm.color)); - } - base.OnMouseUp(e); - } + _movableShowColorForm = null; + _cursor = null; + base.Dispose(disposing); + } - /// - /// Handle the mouse Move event, we move the ColorUnderCursor to the current location. - /// - /// MouseEventArgs - protected override void OnMouseMove(MouseEventArgs e) { - if (_dragging) { - //display the form on the right side of the cursor by default; - Point zp = PointToScreen(new Point(e.X, e.Y)); - _movableShowColorForm.MoveTo(zp); - } - base.OnMouseMove(e); - } + /// + /// Handle the mouse down on the Pipette "label", we take the capture and move the zoomer to the current location + /// + /// MouseEventArgs + protected override void OnMouseDown(MouseEventArgs e) + { + if (e.Button == MouseButtons.Left) + { + User32.SetCapture(Handle); + _movableShowColorForm.MoveTo(PointToScreen(new Point(e.X, e.Y))); + } - /// - /// Handle the MouseCaptureChanged event - /// - /// - protected override void OnMouseCaptureChanged(EventArgs e) { - if (Capture) { - _dragging = true; - Image = null; - Cursor c = _cursor; - Cursor = c; - _movableShowColorForm.Visible = true; - } else { - _dragging = false; - Image = _image; - Cursor = Cursors.Arrow; - _movableShowColorForm.Visible = false; - } - Update(); - base.OnMouseCaptureChanged(e); - } + base.OnMouseDown(e); + } - public bool PreFilterMessage(ref Message m) { - if (_dragging) { - if (m.Msg == (int)WindowsMessages.WM_CHAR) { - if ((int)m.WParam == VkEsc) { - User32.ReleaseCapture(); - } - } - } - return false; - } + /// + /// Handle the mouse up on the Pipette "label", we release the capture and fire the PipetteUsed event + /// + /// MouseEventArgs + protected override void OnMouseUp(MouseEventArgs e) + { + if (e.Button == MouseButtons.Left) + { + //Release Capture should consume MouseUp when canceled with the escape key + User32.ReleaseCapture(); + PipetteUsed?.Invoke(this, new PipetteUsedArgs(_movableShowColorForm.color)); + } + + base.OnMouseUp(e); + } + + /// + /// Handle the mouse Move event, we move the ColorUnderCursor to the current location. + /// + /// MouseEventArgs + protected override void OnMouseMove(MouseEventArgs e) + { + if (_dragging) + { + //display the form on the right side of the cursor by default; + Point zp = PointToScreen(new Point(e.X, e.Y)); + _movableShowColorForm.MoveTo(zp); + } + + base.OnMouseMove(e); + } + + /// + /// Handle the MouseCaptureChanged event + /// + /// + protected override void OnMouseCaptureChanged(EventArgs e) + { + if (Capture) + { + _dragging = true; + Image = null; + Cursor c = _cursor; + Cursor = c; + _movableShowColorForm.Visible = true; + } + else + { + _dragging = false; + Image = _image; + Cursor = Cursors.Arrow; + _movableShowColorForm.Visible = false; + } + + Update(); + base.OnMouseCaptureChanged(e); + } + + public bool PreFilterMessage(ref Message m) + { + if (_dragging) + { + if (m.Msg == (int) WindowsMessages.WM_CHAR) + { + if ((int) m.WParam == VkEsc) + { + User32.ReleaseCapture(); + } + } + } + + return false; + } } - public class PipetteUsedArgs : EventArgs { - public Color Color; + public class PipetteUsedArgs : EventArgs + { + public Color Color; - public PipetteUsedArgs(Color c) { - Color = c; - } - } -} + public PipetteUsedArgs(Color c) + { + Color = c; + } + } +} \ No newline at end of file diff --git a/src/Greenshot/Controls/ToolStripColorButton.cs b/src/Greenshot/Controls/ToolStripColorButton.cs index 951e117fd..ffc2606f5 100644 --- a/src/Greenshot/Controls/ToolStripColorButton.cs +++ b/src/Greenshot/Controls/ToolStripColorButton.cs @@ -18,6 +18,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ + using System; using System.ComponentModel; using System.Drawing; @@ -26,63 +27,71 @@ using System.Windows.Forms; using GreenshotPlugin.Controls; using ColorDialog = Greenshot.Forms.ColorDialog; -namespace Greenshot.Controls { - public class ToolStripColorButton : ToolStripButton, INotifyPropertyChanged, IGreenshotLanguageBindable { - public event PropertyChangedEventHandler PropertyChanged; +namespace Greenshot.Controls +{ + public class ToolStripColorButton : ToolStripButton, INotifyPropertyChanged, IGreenshotLanguageBindable + { + public event PropertyChangedEventHandler PropertyChanged; - [Category("Greenshot"), DefaultValue(null), Description("Specifies key of the language file to use when displaying the text.")] - public string LanguageKey { - get; - set; - } + [Category("Greenshot"), DefaultValue(null), Description("Specifies key of the language file to use when displaying the text.")] + public string LanguageKey { get; set; } - private Color _selectedColor = Color.Transparent; - - public ToolStripColorButton() { - Click+= ColorButtonClick; - } + private Color _selectedColor = Color.Transparent; - public Color SelectedColor { - get {return _selectedColor;} - set { - _selectedColor = value; + public ToolStripColorButton() + { + Click += ColorButtonClick; + } - Brush brush; - if(value != Color.Transparent) { - brush = new SolidBrush(value); - } else { - brush = new HatchBrush(HatchStyle.Percent50, Color.White, Color.Gray); - } + public Color SelectedColor + { + get { return _selectedColor; } + set + { + _selectedColor = value; - if (Image != null) + Brush brush; + if (value != Color.Transparent) { - using Graphics graphics = Graphics.FromImage(Image); - graphics.FillRectangle(brush, new Rectangle(0,13,16,3)); + brush = new SolidBrush(value); + } + else + { + brush = new HatchBrush(HatchStyle.Percent50, Color.White, Color.Gray); } - // cleanup GDI Object - brush.Dispose(); - Invalidate(); - } - } + if (Image != null) + { + using Graphics graphics = Graphics.FromImage(Image); + graphics.FillRectangle(brush, new Rectangle(0, 13, 16, 3)); + } - private void ColorButtonClick(object sender, EventArgs e) { - var colorDialog = new ColorDialog - { - Color = SelectedColor - }; - // Using the parent to make sure the dialog doesn't show on another window - colorDialog.ShowDialog(Parent.Parent); - if (colorDialog.DialogResult == DialogResult.Cancel) - { - return; - } - if (colorDialog.Color.Equals(SelectedColor)) - { - return; - } - SelectedColor = colorDialog.Color; - PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("SelectedColor")); - } - } -} + // cleanup GDI Object + brush.Dispose(); + Invalidate(); + } + } + + private void ColorButtonClick(object sender, EventArgs e) + { + var colorDialog = new ColorDialog + { + Color = SelectedColor + }; + // Using the parent to make sure the dialog doesn't show on another window + colorDialog.ShowDialog(Parent.Parent); + if (colorDialog.DialogResult == DialogResult.Cancel) + { + return; + } + + if (colorDialog.Color.Equals(SelectedColor)) + { + return; + } + + SelectedColor = colorDialog.Color; + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("SelectedColor")); + } + } +} \ No newline at end of file diff --git a/src/Greenshot/Controls/ToolStripEx.cs b/src/Greenshot/Controls/ToolStripEx.cs index 7d2ca63ed..c7ff79048 100644 --- a/src/Greenshot/Controls/ToolStripEx.cs +++ b/src/Greenshot/Controls/ToolStripEx.cs @@ -18,45 +18,49 @@ * 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; -namespace Greenshot.Controls { - /// - /// This is an extension of the default ToolStrip and allows us to click it even when the form doesn't have focus. - /// See: http://blogs.msdn.com/b/rickbrew/archive/2006/01/09/511003.aspx - /// - internal class ToolStripEx : ToolStrip { - private const int WM_MOUSEACTIVATE = 0x21; +namespace Greenshot.Controls +{ + /// + /// This is an extension of the default ToolStrip and allows us to click it even when the form doesn't have focus. + /// See: http://blogs.msdn.com/b/rickbrew/archive/2006/01/09/511003.aspx + /// + internal class ToolStripEx : ToolStrip + { + private const int WM_MOUSEACTIVATE = 0x21; - private enum NativeConstants : uint { - MA_ACTIVATE = 1, - MA_ACTIVATEANDEAT = 2, - } + private enum NativeConstants : uint + { + MA_ACTIVATE = 1, + MA_ACTIVATEANDEAT = 2, + } - private bool _clickThrough; - /// - /// Gets or sets whether the ToolStripEx honors item clicks when its containing form does not have input focus. - /// - /// - /// Default value is false, which is the same behavior provided by the base ToolStrip class. - /// + private bool _clickThrough; - public bool ClickThrough { - get { - return _clickThrough; - } + /// + /// Gets or sets whether the ToolStripEx honors item clicks when its containing form does not have input focus. + /// + /// + /// Default value is false, which is the same behavior provided by the base ToolStrip class. + /// - set { - _clickThrough = value; - } - } + public bool ClickThrough + { + get { return _clickThrough; } - protected override void WndProc(ref Message m) { - base.WndProc(ref m); - if (_clickThrough && m.Msg == WM_MOUSEACTIVATE && m.Result == (IntPtr)NativeConstants.MA_ACTIVATEANDEAT) { - m.Result = (IntPtr)NativeConstants.MA_ACTIVATE; - } - } - } -} + set { _clickThrough = value; } + } + + protected override void WndProc(ref Message m) + { + base.WndProc(ref m); + if (_clickThrough && m.Msg == WM_MOUSEACTIVATE && m.Result == (IntPtr) NativeConstants.MA_ACTIVATEANDEAT) + { + m.Result = (IntPtr) NativeConstants.MA_ACTIVATE; + } + } + } +} \ No newline at end of file diff --git a/src/Greenshot/Controls/ToolStripNumericUpDown.cs b/src/Greenshot/Controls/ToolStripNumericUpDown.cs index 13652fc41..ecbc470ca 100644 --- a/src/Greenshot/Controls/ToolStripNumericUpDown.cs +++ b/src/Greenshot/Controls/ToolStripNumericUpDown.cs @@ -18,62 +18,71 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ + using System; using System.ComponentModel; using System.Windows.Forms; using System.Windows.Forms.Design; -namespace Greenshot.Controls { +namespace Greenshot.Controls +{ + [ToolStripItemDesignerAvailability(ToolStripItemDesignerAvailability.ToolStrip | ToolStripItemDesignerAvailability.StatusStrip)] + public class ToolStripNumericUpDown : ToolStripControlHost, INotifyPropertyChanged + { + public event PropertyChangedEventHandler PropertyChanged; - [ToolStripItemDesignerAvailability(ToolStripItemDesignerAvailability.ToolStrip | ToolStripItemDesignerAvailability.StatusStrip)] - public class ToolStripNumericUpDown : ToolStripControlHost, INotifyPropertyChanged - { - public event PropertyChangedEventHandler PropertyChanged; - - public ToolStripNumericUpDown() : base(new NumericUpDown()) - { - } - - public NumericUpDown NumericUpDown => Control as NumericUpDown; + public ToolStripNumericUpDown() : base(new NumericUpDown()) + { + } - public decimal Value - { - get { return NumericUpDown.Value; } - set { NumericUpDown.Value = value;} - } - public decimal Minimum { - get { return NumericUpDown.Minimum; } - set { NumericUpDown.Minimum = value; } - } - - public decimal Maximum { - get { return NumericUpDown.Maximum; } - set { NumericUpDown.Maximum = value; } - } - - public decimal Increment { - get { return NumericUpDown.Increment; } - set { NumericUpDown.Increment = value; } - } - - public int DecimalPlaces { - get { return NumericUpDown.DecimalPlaces; } - set { NumericUpDown.DecimalPlaces = value; } - } - - - protected override void OnSubscribeControlEvents(Control control) { - base.OnSubscribeControlEvents(control); - NumericUpDown.ValueChanged += _valueChanged; - } - protected override void OnUnsubscribeControlEvents(Control control) { - base.OnUnsubscribeControlEvents(control); - NumericUpDown.ValueChanged -= _valueChanged; - } - - private void _valueChanged(object sender, EventArgs e) - { - PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Value")); - } - } -} + public NumericUpDown NumericUpDown => Control as NumericUpDown; + + public decimal Value + { + get { return NumericUpDown.Value; } + set { NumericUpDown.Value = value; } + } + + public decimal Minimum + { + get { return NumericUpDown.Minimum; } + set { NumericUpDown.Minimum = value; } + } + + public decimal Maximum + { + get { return NumericUpDown.Maximum; } + set { NumericUpDown.Maximum = value; } + } + + public decimal Increment + { + get { return NumericUpDown.Increment; } + set { NumericUpDown.Increment = value; } + } + + public int DecimalPlaces + { + get { return NumericUpDown.DecimalPlaces; } + set { NumericUpDown.DecimalPlaces = value; } + } + + + protected override void OnSubscribeControlEvents(Control control) + { + base.OnSubscribeControlEvents(control); + NumericUpDown.ValueChanged += _valueChanged; + } + + protected override void OnUnsubscribeControlEvents(Control control) + { + base.OnUnsubscribeControlEvents(control); + NumericUpDown.ValueChanged -= _valueChanged; + } + + private void _valueChanged(object sender, EventArgs e) + { + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Value")); + } + } +} \ No newline at end of file diff --git a/src/Greenshot/Destinations/ClipboardDestination.cs b/src/Greenshot/Destinations/ClipboardDestination.cs index a1172ede4..88a2e2ecb 100644 --- a/src/Greenshot/Destinations/ClipboardDestination.cs +++ b/src/Greenshot/Destinations/ClipboardDestination.cs @@ -18,61 +18,64 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ + using System; using System.Drawing; using System.Windows.Forms; - using Greenshot.Configuration; using GreenshotPlugin.Core; using GreenshotPlugin.Interfaces; -namespace Greenshot.Destinations { - /// - /// Description of ClipboardDestination. - /// - public class ClipboardDestination : AbstractDestination { - public const string DESIGNATION = "Clipboard"; +namespace Greenshot.Destinations +{ + /// + /// Description of ClipboardDestination. + /// + public class ClipboardDestination : AbstractDestination + { + public const string DESIGNATION = "Clipboard"; - public override string Designation { - get { - return DESIGNATION; - } - } + public override string Designation + { + get { return DESIGNATION; } + } - public override string Description { - get { - return Language.GetString(LangKey.settings_destination_clipboard); - } - } - public override int Priority { - get { - return 2; - } - } - - public override Keys EditorShortcutKeys { - get { - return Keys.Control | Keys.Shift | Keys.C; - } - } + public override string Description + { + get { return Language.GetString(LangKey.settings_destination_clipboard); } + } - public override Image DisplayIcon { - get { - return GreenshotResources.GetImage("Clipboard.Image"); - } - } + public override int Priority + { + get { return 2; } + } - public override ExportInformation ExportCapture(bool manuallyInitiated, ISurface surface, ICaptureDetails captureDetails) { - ExportInformation exportInformation = new ExportInformation(Designation, Description); - try { - ClipboardHelper.SetClipboardData(surface); - exportInformation.ExportMade = true; - } catch (Exception) { - // TODO: Change to general logic in ProcessExport - surface.SendMessageEvent(this, SurfaceMessageTyp.Error, Language.GetString(LangKey.editor_clipboardfailed)); - } - ProcessExport(exportInformation, surface); - return exportInformation; - } - } -} + public override Keys EditorShortcutKeys + { + get { return Keys.Control | Keys.Shift | Keys.C; } + } + + public override Image DisplayIcon + { + get { return GreenshotResources.GetImage("Clipboard.Image"); } + } + + public override ExportInformation ExportCapture(bool manuallyInitiated, ISurface surface, ICaptureDetails captureDetails) + { + ExportInformation exportInformation = new ExportInformation(Designation, Description); + try + { + ClipboardHelper.SetClipboardData(surface); + exportInformation.ExportMade = true; + } + catch (Exception) + { + // TODO: Change to general logic in ProcessExport + surface.SendMessageEvent(this, SurfaceMessageTyp.Error, Language.GetString(LangKey.editor_clipboardfailed)); + } + + ProcessExport(exportInformation, surface); + return exportInformation; + } + } +} \ No newline at end of file diff --git a/src/Greenshot/Destinations/EditorDestination.cs b/src/Greenshot/Destinations/EditorDestination.cs index 220359238..b22cc0894 100644 --- a/src/Greenshot/Destinations/EditorDestination.cs +++ b/src/Greenshot/Destinations/EditorDestination.cs @@ -18,6 +18,7 @@ * 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; @@ -29,97 +30,125 @@ using GreenshotPlugin.Interfaces; using GreenshotPlugin.Interfaces.Forms; using log4net; -namespace Greenshot.Destinations { - /// - /// Description of EditorDestination. - /// - public class EditorDestination : AbstractDestination { - private static readonly ILog LOG = LogManager.GetLogger(typeof(EditorDestination)); - private static readonly EditorConfiguration editorConfiguration = IniConfig.GetIniSection(); - public const string DESIGNATION = "Editor"; - private readonly IImageEditor editor; - private static readonly Image greenshotIcon = GreenshotResources.GetGreenshotIcon().ToBitmap(); +namespace Greenshot.Destinations +{ + /// + /// Description of EditorDestination. + /// + public class EditorDestination : AbstractDestination + { + private static readonly ILog LOG = LogManager.GetLogger(typeof(EditorDestination)); + private static readonly EditorConfiguration editorConfiguration = IniConfig.GetIniSection(); + public const string DESIGNATION = "Editor"; + private readonly IImageEditor editor; + private static readonly Image greenshotIcon = GreenshotResources.GetGreenshotIcon().ToBitmap(); public EditorDestination() { // Do not remove, is needed for the framework } - public EditorDestination(IImageEditor editor) { - this.editor = editor; - } + public EditorDestination(IImageEditor editor) + { + this.editor = editor; + } - public override string Designation => DESIGNATION; + public override string Designation => DESIGNATION; - public override string Description { - get { - if (editor == null) { - return Language.GetString(LangKey.settings_destination_editor); - } - return Language.GetString(LangKey.settings_destination_editor) + " - " + editor.CaptureDetails.Title; - } - } + public override string Description + { + get + { + if (editor == null) + { + return Language.GetString(LangKey.settings_destination_editor); + } - public override int Priority => 1; + return Language.GetString(LangKey.settings_destination_editor) + " - " + editor.CaptureDetails.Title; + } + } + + public override int Priority => 1; public override bool IsDynamic => true; public override Image DisplayIcon => greenshotIcon; - public override IEnumerable DynamicDestinations() { - foreach (IImageEditor someEditor in ImageEditorForm.Editors) { - yield return new EditorDestination(someEditor); - } - } + public override IEnumerable DynamicDestinations() + { + foreach (IImageEditor someEditor in ImageEditorForm.Editors) + { + yield return new EditorDestination(someEditor); + } + } - public override ExportInformation ExportCapture(bool manuallyInitiated, ISurface surface, ICaptureDetails captureDetails) { - ExportInformation exportInformation = new ExportInformation(Designation, Description); - // Make sure we collect the garbage before opening the screenshot - GC.Collect(); - GC.WaitForPendingFinalizers(); + public override ExportInformation ExportCapture(bool manuallyInitiated, ISurface surface, ICaptureDetails captureDetails) + { + ExportInformation exportInformation = new ExportInformation(Designation, Description); + // Make sure we collect the garbage before opening the screenshot + GC.Collect(); + GC.WaitForPendingFinalizers(); - bool modified = surface.Modified; - if (editor == null) { - if (editorConfiguration.ReuseEditor) { - foreach(IImageEditor openedEditor in ImageEditorForm.Editors) { + bool modified = surface.Modified; + if (editor == null) + { + if (editorConfiguration.ReuseEditor) + { + foreach (IImageEditor openedEditor in ImageEditorForm.Editors) + { if (openedEditor.Surface.Modified) continue; - + openedEditor.Surface = surface; exportInformation.ExportMade = true; break; } - } - if (!exportInformation.ExportMade) { - try { - ImageEditorForm editorForm = new ImageEditorForm(surface, !surface.Modified); // Output made?? + } - if (!string.IsNullOrEmpty(captureDetails.Filename)) { - editorForm.SetImagePath(captureDetails.Filename); - } - editorForm.Show(); - editorForm.Activate(); - LOG.Debug("Finished opening Editor"); - exportInformation.ExportMade = true; - } catch (Exception e) { - LOG.Error(e); - exportInformation.ErrorMessage = e.Message; - } - } - } else { - try { - using (Image image = surface.GetImageForExport()) { - editor.Surface.AddImageContainer(image, 10, 10); - } - exportInformation.ExportMade = true; - } catch (Exception e) { - LOG.Error(e); - exportInformation.ErrorMessage = e.Message; - } - } - ProcessExport(exportInformation, surface); - // Workaround for the modified flag when using the editor. - surface.Modified = modified; - return exportInformation; - } - } -} + if (!exportInformation.ExportMade) + { + try + { + ImageEditorForm editorForm = new ImageEditorForm(surface, !surface.Modified); // Output made?? + + if (!string.IsNullOrEmpty(captureDetails.Filename)) + { + editorForm.SetImagePath(captureDetails.Filename); + } + + editorForm.Show(); + editorForm.Activate(); + LOG.Debug("Finished opening Editor"); + exportInformation.ExportMade = true; + } + catch (Exception e) + { + LOG.Error(e); + exportInformation.ErrorMessage = e.Message; + } + } + } + else + { + try + { + using (Image image = surface.GetImageForExport()) + { + editor.Surface.AddImageContainer(image, 10, 10); + } + + exportInformation.ExportMade = true; + } + catch (Exception e) + { + LOG.Error(e); + exportInformation.ErrorMessage = e.Message; + } + } + + ProcessExport(exportInformation, surface); + // Workaround for the modified flag when using the editor. + surface.Modified = modified; + return exportInformation; + } + } +} \ No newline at end of file diff --git a/src/Greenshot/Destinations/EmailDestination.cs b/src/Greenshot/Destinations/EmailDestination.cs index 326e27e9c..c8e99b870 100644 --- a/src/Greenshot/Destinations/EmailDestination.cs +++ b/src/Greenshot/Destinations/EmailDestination.cs @@ -18,71 +18,82 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ + using System; using System.Drawing; using System.Windows.Forms; - using Greenshot.Configuration; using Greenshot.Helpers; using GreenshotPlugin.Core; using GreenshotPlugin.Interfaces; using Microsoft.Win32; -namespace Greenshot.Destinations { - /// - /// Description of EmailDestination. - /// - public class EmailDestination : AbstractDestination { - private static readonly Image MailIcon = GreenshotResources.GetImage("Email.Image"); - private static bool _isActiveFlag; - private static string _mapiClient; - public const string DESIGNATION = "EMail"; +namespace Greenshot.Destinations +{ + /// + /// Description of EmailDestination. + /// + public class EmailDestination : AbstractDestination + { + private static readonly Image MailIcon = GreenshotResources.GetImage("Email.Image"); + private static bool _isActiveFlag; + private static string _mapiClient; + public const string DESIGNATION = "EMail"; - static EmailDestination() { - // Logic to decide what email implementation we use - _mapiClient = RegistryHive.LocalMachine.ReadKey64Or32(@"Clients\Mail"); - if (!string.IsNullOrEmpty(_mapiClient)) { + static EmailDestination() + { + // Logic to decide what email implementation we use + _mapiClient = RegistryHive.LocalMachine.ReadKey64Or32(@"Clients\Mail"); + if (!string.IsNullOrEmpty(_mapiClient)) + { // Active as we have a MAPI client, can be disabled later _isActiveFlag = true; } } - public override string Designation => DESIGNATION; + public override string Designation => DESIGNATION; - public override string Description { - get + public override string Description + { + get { // Make sure there is some kind of "mail" name return _mapiClient ??= Language.GetString(LangKey.editor_email); } - } + } - public override int Priority => 3; + public override int Priority => 3; - public override bool IsActive { - get { - if (_isActiveFlag) { - // Disable if the office plugin is installed and the client is outlook - // TODO: Change this! It always creates an exception, as the plugin has not been loaded the type is not there :( - var outlookDestination = Type.GetType("GreenshotOfficePlugin.OutlookDestination,GreenshotOfficePlugin", false); - if (outlookDestination != null && _mapiClient.ToLower().Contains("microsoft outlook")) { - _isActiveFlag = false; - } - } - return base.IsActive && _isActiveFlag; - } - } + public override bool IsActive + { + get + { + if (_isActiveFlag) + { + // Disable if the office plugin is installed and the client is outlook + // TODO: Change this! It always creates an exception, as the plugin has not been loaded the type is not there :( + var outlookDestination = Type.GetType("GreenshotOfficePlugin.OutlookDestination,GreenshotOfficePlugin", false); + if (outlookDestination != null && _mapiClient.ToLower().Contains("microsoft outlook")) + { + _isActiveFlag = false; + } + } - public override Keys EditorShortcutKeys => Keys.Control | Keys.E; + return base.IsActive && _isActiveFlag; + } + } - public override Image DisplayIcon => MailIcon; + public override Keys EditorShortcutKeys => Keys.Control | Keys.E; - public override ExportInformation ExportCapture(bool manuallyInitiated, ISurface surface, ICaptureDetails captureDetails) { - ExportInformation exportInformation = new ExportInformation(Designation, Description); - MapiMailMessage.SendImage(surface, captureDetails); - exportInformation.ExportMade = true; - ProcessExport(exportInformation, surface); - return exportInformation; - } - } + public override Image DisplayIcon => MailIcon; + + public override ExportInformation ExportCapture(bool manuallyInitiated, ISurface surface, ICaptureDetails captureDetails) + { + ExportInformation exportInformation = new ExportInformation(Designation, Description); + MapiMailMessage.SendImage(surface, captureDetails); + exportInformation.ExportMade = true; + ProcessExport(exportInformation, surface); + return exportInformation; + } + } } \ No newline at end of file diff --git a/src/Greenshot/Destinations/FileDestination.cs b/src/Greenshot/Destinations/FileDestination.cs index d6e89c0d5..95365677a 100644 --- a/src/Greenshot/Destinations/FileDestination.cs +++ b/src/Greenshot/Destinations/FileDestination.cs @@ -18,11 +18,11 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ + using System; using System.Drawing; using System.IO; using System.Windows.Forms; - using Greenshot.Configuration; using Greenshot.Forms; using GreenshotPlugin.Core; @@ -32,112 +32,137 @@ using GreenshotPlugin.Interfaces; using GreenshotPlugin.Interfaces.Plugin; using log4net; -namespace Greenshot.Destinations { - /// - /// Description of FileSaveAsDestination. - /// - public class FileDestination : AbstractDestination { - private static readonly ILog Log = LogManager.GetLogger(typeof(FileDestination)); - private static readonly CoreConfiguration CoreConfig = IniConfig.GetIniSection(); - public const string DESIGNATION = "FileNoDialog"; +namespace Greenshot.Destinations +{ + /// + /// Description of FileSaveAsDestination. + /// + public class FileDestination : AbstractDestination + { + private static readonly ILog Log = LogManager.GetLogger(typeof(FileDestination)); + private static readonly CoreConfiguration CoreConfig = IniConfig.GetIniSection(); + public const string DESIGNATION = "FileNoDialog"; - public override string Designation => DESIGNATION; + public override string Designation => DESIGNATION; - public override string Description => Language.GetString(LangKey.quicksettings_destination_file); + public override string Description => Language.GetString(LangKey.quicksettings_destination_file); - public override int Priority => 0; + public override int Priority => 0; - public override Keys EditorShortcutKeys => Keys.Control | Keys.S; + public override Keys EditorShortcutKeys => Keys.Control | Keys.S; - public override Image DisplayIcon => GreenshotResources.GetImage("Save.Image"); + public override Image DisplayIcon => GreenshotResources.GetImage("Save.Image"); - public override ExportInformation ExportCapture(bool manuallyInitiated, ISurface surface, ICaptureDetails captureDetails) { - ExportInformation exportInformation = new ExportInformation(Designation, Description); - bool outputMade; - bool overwrite; - string fullPath; - // Get output settings from the configuration - SurfaceOutputSettings outputSettings = new SurfaceOutputSettings(); + public override ExportInformation ExportCapture(bool manuallyInitiated, ISurface surface, ICaptureDetails captureDetails) + { + ExportInformation exportInformation = new ExportInformation(Designation, Description); + bool outputMade; + bool overwrite; + string fullPath; + // Get output settings from the configuration + SurfaceOutputSettings outputSettings = new SurfaceOutputSettings(); - if (captureDetails?.Filename != null) { - // As we save a pre-selected file, allow to overwrite. - overwrite = true; - Log.InfoFormat("Using previous filename"); - fullPath = captureDetails.Filename; - outputSettings.Format = ImageOutput.FormatForFilename(fullPath); - } else { - fullPath = CreateNewFilename(captureDetails); - // As we generate a file, the configuration tells us if we allow to overwrite - overwrite = CoreConfig.OutputFileAllowOverwrite; - } - if (CoreConfig.OutputFilePromptQuality) { - QualityDialog qualityDialog = new QualityDialog(outputSettings); - qualityDialog.ShowDialog(); - } + if (captureDetails?.Filename != null) + { + // As we save a pre-selected file, allow to overwrite. + overwrite = true; + Log.InfoFormat("Using previous filename"); + fullPath = captureDetails.Filename; + outputSettings.Format = ImageOutput.FormatForFilename(fullPath); + } + else + { + fullPath = CreateNewFilename(captureDetails); + // As we generate a file, the configuration tells us if we allow to overwrite + overwrite = CoreConfig.OutputFileAllowOverwrite; + } - // Catching any exception to prevent that the user can't write in the directory. - // This is done for e.g. bugs #2974608, #2963943, #2816163, #2795317, #2789218, #3004642 - try { - ImageOutput.Save(surface, fullPath, overwrite, outputSettings, CoreConfig.OutputFileCopyPathToClipboard); - outputMade = true; - } catch (ArgumentException ex1) { - // Our generated filename exists, display 'save-as' - Log.InfoFormat("Not overwriting: {0}", ex1.Message); - // when we don't allow to overwrite present a new SaveWithDialog - fullPath = ImageOutput.SaveWithDialog(surface, captureDetails); - outputMade = fullPath != null; - } catch (Exception ex2) { - Log.Error("Error saving screenshot!", ex2); - // Show the problem - MessageBox.Show(Language.GetString(LangKey.error_save), Language.GetString(LangKey.error)); - // when save failed we present a SaveWithDialog - fullPath = ImageOutput.SaveWithDialog(surface, captureDetails); - outputMade = fullPath != null; - } - // Don't overwrite filename if no output is made - if (outputMade) { - exportInformation.ExportMade = true; - exportInformation.Filepath = fullPath; - if (captureDetails != null) - { - captureDetails.Filename = fullPath; - } - CoreConfig.OutputFileAsFullpath = fullPath; - } + if (CoreConfig.OutputFilePromptQuality) + { + QualityDialog qualityDialog = new QualityDialog(outputSettings); + qualityDialog.ShowDialog(); + } - ProcessExport(exportInformation, surface); - return exportInformation; - } + // Catching any exception to prevent that the user can't write in the directory. + // This is done for e.g. bugs #2974608, #2963943, #2816163, #2795317, #2789218, #3004642 + try + { + ImageOutput.Save(surface, fullPath, overwrite, outputSettings, CoreConfig.OutputFileCopyPathToClipboard); + outputMade = true; + } + catch (ArgumentException ex1) + { + // Our generated filename exists, display 'save-as' + Log.InfoFormat("Not overwriting: {0}", ex1.Message); + // when we don't allow to overwrite present a new SaveWithDialog + fullPath = ImageOutput.SaveWithDialog(surface, captureDetails); + outputMade = fullPath != null; + } + catch (Exception ex2) + { + Log.Error("Error saving screenshot!", ex2); + // Show the problem + MessageBox.Show(Language.GetString(LangKey.error_save), Language.GetString(LangKey.error)); + // when save failed we present a SaveWithDialog + fullPath = ImageOutput.SaveWithDialog(surface, captureDetails); + outputMade = fullPath != null; + } - private static string CreateNewFilename(ICaptureDetails captureDetails) { - string fullPath; - Log.InfoFormat("Creating new filename"); - string pattern = CoreConfig.OutputFileFilenamePattern; - if (string.IsNullOrEmpty(pattern)) { - 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); - } catch (ArgumentException) { - // configured filename or path not valid, show error message... - Log.InfoFormat("Generated path or filename not valid: {0}, {1}", filepath, filename); + // Don't overwrite filename if no output is made + if (outputMade) + { + exportInformation.ExportMade = true; + exportInformation.Filepath = fullPath; + if (captureDetails != null) + { + captureDetails.Filename = fullPath; + } - MessageBox.Show(Language.GetString(LangKey.error_save_invalid_chars), Language.GetString(LangKey.error)); - // ... lets get the pattern fixed.... - var dialogResult = new SettingsForm().ShowDialog(); - if (dialogResult == DialogResult.OK) { - // ... OK -> then try again: - fullPath = CreateNewFilename(captureDetails); - } else { - // ... cancelled. - fullPath = null; - } - - } - return fullPath; - } - } -} + CoreConfig.OutputFileAsFullpath = fullPath; + } + + ProcessExport(exportInformation, surface); + return exportInformation; + } + + private static string CreateNewFilename(ICaptureDetails captureDetails) + { + string fullPath; + Log.InfoFormat("Creating new filename"); + string pattern = CoreConfig.OutputFileFilenamePattern; + if (string.IsNullOrEmpty(pattern)) + { + 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); + } + catch (ArgumentException) + { + // configured filename or path not valid, show error message... + Log.InfoFormat("Generated path or filename not valid: {0}, {1}", filepath, filename); + + MessageBox.Show(Language.GetString(LangKey.error_save_invalid_chars), Language.GetString(LangKey.error)); + // ... lets get the pattern fixed.... + var dialogResult = new SettingsForm().ShowDialog(); + if (dialogResult == DialogResult.OK) + { + // ... OK -> then try again: + fullPath = CreateNewFilename(captureDetails); + } + else + { + // ... cancelled. + fullPath = null; + } + } + + return fullPath; + } + } +} \ No newline at end of file diff --git a/src/Greenshot/Destinations/FileWithDialogDestination.cs b/src/Greenshot/Destinations/FileWithDialogDestination.cs index b378f5f34..02c5e9bd4 100644 --- a/src/Greenshot/Destinations/FileWithDialogDestination.cs +++ b/src/Greenshot/Destinations/FileWithDialogDestination.cs @@ -21,62 +21,61 @@ using System.Drawing; using System.Windows.Forms; - using Greenshot.Configuration; using GreenshotPlugin.Core; using GreenshotPlugin.IniFile; using GreenshotPlugin.Interfaces; -namespace Greenshot.Destinations { - /// - /// Description of FileWithDialog. - /// - public class FileWithDialogDestination : AbstractDestination { - private static readonly CoreConfiguration conf = IniConfig.GetIniSection(); - public const string DESIGNATION = "FileDialog"; +namespace Greenshot.Destinations +{ + /// + /// Description of FileWithDialog. + /// + public class FileWithDialogDestination : AbstractDestination + { + private static readonly CoreConfiguration conf = IniConfig.GetIniSection(); + public const string DESIGNATION = "FileDialog"; - public override string Designation { - get { - return DESIGNATION; - } - } + public override string Designation + { + get { return DESIGNATION; } + } - public override string Description { - get { - return Language.GetString(LangKey.settings_destination_fileas); - } - } + public override string Description + { + get { return Language.GetString(LangKey.settings_destination_fileas); } + } - public override int Priority { - get { - return 0; - } - } - - public override Keys EditorShortcutKeys { - get { - return Keys.Control | Keys.Shift | Keys.S; - } - } + public override int Priority + { + get { return 0; } + } - public override Image DisplayIcon { - get { - return GreenshotResources.GetImage("Save.Image"); - } - } + public override Keys EditorShortcutKeys + { + get { return Keys.Control | Keys.Shift | Keys.S; } + } - public override ExportInformation ExportCapture(bool manuallyInitiated, ISurface surface, ICaptureDetails captureDetails) { - ExportInformation exportInformation = new ExportInformation(Designation, Description); - // Bug #2918756 don't overwrite path if SaveWithDialog returns null! - var savedTo = ImageOutput.SaveWithDialog(surface, captureDetails); - if (savedTo != null) { - exportInformation.ExportMade = true; - exportInformation.Filepath = savedTo; - captureDetails.Filename = savedTo; - conf.OutputFileAsFullpath = savedTo; - } - ProcessExport(exportInformation, surface); - return exportInformation; - } - } -} + public override Image DisplayIcon + { + get { return GreenshotResources.GetImage("Save.Image"); } + } + + public override ExportInformation ExportCapture(bool manuallyInitiated, ISurface surface, ICaptureDetails captureDetails) + { + ExportInformation exportInformation = new ExportInformation(Designation, Description); + // Bug #2918756 don't overwrite path if SaveWithDialog returns null! + var savedTo = ImageOutput.SaveWithDialog(surface, captureDetails); + if (savedTo != null) + { + exportInformation.ExportMade = true; + exportInformation.Filepath = savedTo; + captureDetails.Filename = savedTo; + conf.OutputFileAsFullpath = savedTo; + } + + ProcessExport(exportInformation, surface); + return exportInformation; + } + } +} \ No newline at end of file diff --git a/src/Greenshot/Destinations/PickerDestination.cs b/src/Greenshot/Destinations/PickerDestination.cs index e7ea94c83..2195e1d6b 100644 --- a/src/Greenshot/Destinations/PickerDestination.cs +++ b/src/Greenshot/Destinations/PickerDestination.cs @@ -24,42 +24,50 @@ using Greenshot.Configuration; using GreenshotPlugin.Core; using GreenshotPlugin.Interfaces; -namespace Greenshot.Destinations { - /// - /// The PickerDestination shows a context menu with all possible destinations, so the user can "pick" one - /// - public class PickerDestination : AbstractDestination { - public const string DESIGNATION = "Picker"; +namespace Greenshot.Destinations +{ + /// + /// The PickerDestination shows a context menu with all possible destinations, so the user can "pick" one + /// + public class PickerDestination : AbstractDestination + { + public const string DESIGNATION = "Picker"; - public override string Designation => DESIGNATION; + public override string Designation => DESIGNATION; - public override string Description => Language.GetString(LangKey.settings_destination_picker); + public override string Description => Language.GetString(LangKey.settings_destination_picker); - public override int Priority => 1; + public override int Priority => 1; - /// - /// Export the capture with the destination picker - /// - /// Did the user select this destination? - /// Surface to export - /// Details of the capture - /// true if export was made - public override ExportInformation ExportCapture(bool manuallyInitiated, ISurface surface, ICaptureDetails captureDetails) { - List destinations = new List(); + /// + /// Export the capture with the destination picker + /// + /// Did the user select this destination? + /// Surface to export + /// Details of the capture + /// true if export was made + public override ExportInformation ExportCapture(bool manuallyInitiated, ISurface surface, ICaptureDetails captureDetails) + { + List destinations = new List(); - foreach(var destination in SimpleServiceProvider.Current.GetAllInstances()) { - if ("Picker".Equals(destination.Designation)) { - continue; - } - if (!destination.IsActive) { - continue; - } - destinations.Add(destination); - } + foreach (var destination in SimpleServiceProvider.Current.GetAllInstances()) + { + if ("Picker".Equals(destination.Designation)) + { + continue; + } - // No Processing, this is done in the selected destination (if anything was selected) - return ShowPickerMenu(true, surface, captureDetails, destinations); - } - } -} + if (!destination.IsActive) + { + continue; + } + + destinations.Add(destination); + } + + // No Processing, this is done in the selected destination (if anything was selected) + return ShowPickerMenu(true, surface, captureDetails, destinations); + } + } +} \ No newline at end of file diff --git a/src/Greenshot/Destinations/PrinterDestination.cs b/src/Greenshot/Destinations/PrinterDestination.cs index 3a45268e2..09b51880c 100644 --- a/src/Greenshot/Destinations/PrinterDestination.cs +++ b/src/Greenshot/Destinations/PrinterDestination.cs @@ -24,38 +24,46 @@ using System.Collections.Generic; using System.Drawing; using System.Drawing.Printing; using System.Windows.Forms; - using Greenshot.Configuration; using GreenshotPlugin.Core; using Greenshot.Helpers; using GreenshotPlugin.Interfaces; -namespace Greenshot.Destinations { - /// - /// Description of PrinterDestination. - /// - public class PrinterDestination : AbstractDestination { - public const string DESIGNATION = "Printer"; +namespace Greenshot.Destinations +{ + /// + /// Description of PrinterDestination. + /// + public class PrinterDestination : AbstractDestination + { + public const string DESIGNATION = "Printer"; private readonly string _printerName; - public PrinterDestination() { - } + public PrinterDestination() + { + } - public PrinterDestination(string printerName) { - _printerName = printerName; - } - public override string Designation => DESIGNATION; + public PrinterDestination(string printerName) + { + _printerName = printerName; + } - public override string Description { - get { - if (_printerName != null) { - return Language.GetString(LangKey.settings_destination_printer) + " - " + _printerName; - } - return Language.GetString(LangKey.settings_destination_printer); - } - } + public override string Designation => DESIGNATION; - public override int Priority => 2; + public override string Description + { + get + { + if (_printerName != null) + { + return Language.GetString(LangKey.settings_destination_printer) + " - " + _printerName; + } + + return Language.GetString(LangKey.settings_destination_printer); + } + } + + public override int Priority => 2; public override Keys EditorShortcutKeys => Keys.Control | Keys.P; @@ -64,60 +72,75 @@ namespace Greenshot.Destinations { public override bool IsDynamic => true; /// - /// Create destinations for all the installed printers - /// - /// IEnumerable of IDestination - public override IEnumerable DynamicDestinations() { - PrinterSettings settings = new PrinterSettings(); - string defaultPrinter = settings.PrinterName; - List printers = new List(); + /// Create destinations for all the installed printers + /// + /// IEnumerable of IDestination + public override IEnumerable DynamicDestinations() + { + PrinterSettings settings = new PrinterSettings(); + string defaultPrinter = settings.PrinterName; + List printers = new List(); - foreach (string printer in PrinterSettings.InstalledPrinters) { - printers.Add(printer); - } - printers.Sort(delegate(string p1, string p2) { - if(defaultPrinter.Equals(p1)) { - return -1; - } - if(defaultPrinter.Equals(p2)) { - return 1; - } - return string.Compare(p1, p2, StringComparison.Ordinal); - }); - foreach(string printer in printers) { - yield return new PrinterDestination(printer); - } - } + foreach (string printer in PrinterSettings.InstalledPrinters) + { + printers.Add(printer); + } - /// - /// Export the capture to the printer - /// - /// - /// - /// - /// ExportInformation - public override ExportInformation ExportCapture(bool manuallyInitiated, ISurface surface, ICaptureDetails captureDetails) { - ExportInformation exportInformation = new ExportInformation(Designation, Description); - PrinterSettings printerSettings; - if (!string.IsNullOrEmpty(_printerName)) + printers.Sort(delegate(string p1, string p2) + { + if (defaultPrinter.Equals(p1)) + { + return -1; + } + + if (defaultPrinter.Equals(p2)) + { + return 1; + } + + return string.Compare(p1, p2, StringComparison.Ordinal); + }); + foreach (string printer in printers) + { + yield return new PrinterDestination(printer); + } + } + + /// + /// Export the capture to the printer + /// + /// + /// + /// + /// ExportInformation + public override ExportInformation ExportCapture(bool manuallyInitiated, ISurface surface, ICaptureDetails captureDetails) + { + ExportInformation exportInformation = new ExportInformation(Designation, Description); + PrinterSettings printerSettings; + if (!string.IsNullOrEmpty(_printerName)) { using PrintHelper printHelper = new PrintHelper(surface, captureDetails); printerSettings = printHelper.PrintTo(_printerName); - } else if (!manuallyInitiated) { - PrinterSettings settings = new PrinterSettings(); + } + else if (!manuallyInitiated) + { + PrinterSettings settings = new PrinterSettings(); using PrintHelper printHelper = new PrintHelper(surface, captureDetails); printerSettings = printHelper.PrintTo(settings.PrinterName); - } else + } + else { using PrintHelper printHelper = new PrintHelper(surface, captureDetails); printerSettings = printHelper.PrintWithDialog(); } - if (printerSettings != null) { - exportInformation.ExportMade = true; - } - ProcessExport(exportInformation, surface); - return exportInformation; - } - } -} + if (printerSettings != null) + { + exportInformation.ExportMade = true; + } + + ProcessExport(exportInformation, surface); + return exportInformation; + } + } +} \ No newline at end of file diff --git a/src/Greenshot/Drawing/Adorners/AbstractAdorner.cs b/src/Greenshot/Drawing/Adorners/AbstractAdorner.cs index 5461b4df4..19f531a5c 100644 --- a/src/Greenshot/Drawing/Adorners/AbstractAdorner.cs +++ b/src/Greenshot/Drawing/Adorners/AbstractAdorner.cs @@ -28,143 +28,129 @@ using GreenshotPlugin.Interfaces.Drawing.Adorners; namespace Greenshot.Drawing.Adorners { - public class AbstractAdorner : IAdorner - { - public virtual EditStatus EditStatus { get; protected set; } = EditStatus.IDLE; + public class AbstractAdorner : IAdorner + { + public virtual EditStatus EditStatus { get; protected set; } = EditStatus.IDLE; - private static readonly Size DefaultSize = new Size(6, 6); - protected Size _size; + private static readonly Size DefaultSize = new Size(6, 6); + protected Size _size; - public AbstractAdorner(IDrawableContainer owner) - { - _size = DpiHelper.ScaleWithDpi(DefaultSize, 0); - Owner = owner; - } + public AbstractAdorner(IDrawableContainer owner) + { + _size = DpiHelper.ScaleWithDpi(DefaultSize, 0); + Owner = owner; + } - /// - /// Returns the cursor for when the mouse is over the adorner - /// - public virtual Cursor Cursor - { - get - { - return Cursors.SizeAll; - } - } + /// + /// Returns the cursor for when the mouse is over the adorner + /// + public virtual Cursor Cursor + { + get { return Cursors.SizeAll; } + } - public virtual IDrawableContainer Owner - { - get; - set; - } + public virtual IDrawableContainer Owner { get; set; } - /// - /// Test if the point is inside the adorner - /// - /// - /// - public virtual bool HitTest(Point point) - { - Rectangle hitBounds = Bounds; - hitBounds.Inflate(3, 3); - return hitBounds.Contains(point); - } + /// + /// Test if the point is inside the adorner + /// + /// + /// + public virtual bool HitTest(Point point) + { + Rectangle hitBounds = Bounds; + hitBounds.Inflate(3, 3); + return hitBounds.Contains(point); + } - /// - /// Handle the mouse down - /// - /// - /// - public virtual void MouseDown(object sender, MouseEventArgs mouseEventArgs) - { - } + /// + /// Handle the mouse down + /// + /// + /// + public virtual void MouseDown(object sender, MouseEventArgs mouseEventArgs) + { + } - /// - /// Handle the mouse move - /// - /// - /// - public virtual void MouseMove(object sender, MouseEventArgs mouseEventArgs) - { - } + /// + /// Handle the mouse move + /// + /// + /// + public virtual void MouseMove(object sender, MouseEventArgs mouseEventArgs) + { + } - /// - /// Handle the mouse up - /// - /// - /// - public virtual void MouseUp(object sender, MouseEventArgs mouseEventArgs) - { - EditStatus = EditStatus.IDLE; - } + /// + /// Handle the mouse up + /// + /// + /// + public virtual void MouseUp(object sender, MouseEventArgs mouseEventArgs) + { + EditStatus = EditStatus.IDLE; + } - /// - /// Return the location of the adorner - /// - public virtual Point Location - { - get; - set; - } + /// + /// Return the location of the adorner + /// + public virtual Point Location { get; set; } - /// - /// Return the bounds of the Adorner - /// - public virtual Rectangle Bounds - { - get - { - Point location = Location; - return new Rectangle(location.X - (_size.Width / 2), location.Y - (_size.Height / 2), _size.Width, _size.Height); - } - } + /// + /// Return the bounds of the Adorner + /// + public virtual Rectangle Bounds + { + get + { + Point location = Location; + return new Rectangle(location.X - (_size.Width / 2), location.Y - (_size.Height / 2), _size.Width, _size.Height); + } + } - /// - /// Return the bounds of the Adorner as displayed on the parent Surface - /// - protected virtual Rectangle BoundsOnSurface - { - get - { - Point displayLocation = Owner.Parent.ToSurfaceCoordinates(Location); - return new Rectangle(displayLocation.X - _size.Width / 2, displayLocation.Y - _size.Height / 2, _size.Width, _size.Height); - } - } + /// + /// Return the bounds of the Adorner as displayed on the parent Surface + /// + protected virtual Rectangle BoundsOnSurface + { + get + { + Point displayLocation = Owner.Parent.ToSurfaceCoordinates(Location); + return new Rectangle(displayLocation.X - _size.Width / 2, displayLocation.Y - _size.Height / 2, _size.Width, _size.Height); + } + } - /// - /// The adorner is active if the edit status is not idle or undrawn - /// - public virtual bool IsActive - { - get - { - return EditStatus != EditStatus.IDLE && EditStatus != EditStatus.UNDRAWN; - } - } + /// + /// The adorner is active if the edit status is not idle or undrawn + /// + public virtual bool IsActive + { + get { return EditStatus != EditStatus.IDLE && EditStatus != EditStatus.UNDRAWN; } + } - /// - /// Adjust UI elements to the supplied DPI settings - /// - /// uint - public void AdjustToDpi(uint dpi) - { - _size = DpiHelper.ScaleWithDpi(DefaultSize, dpi); - } + /// + /// Adjust UI elements to the supplied DPI settings + /// + /// uint + public void AdjustToDpi(uint dpi) + { + _size = DpiHelper.ScaleWithDpi(DefaultSize, dpi); + } - /// - /// Draw the adorner - /// - /// PaintEventArgs - public virtual void Paint(PaintEventArgs paintEventArgs) - { - } + /// + /// Draw the adorner + /// + /// PaintEventArgs + public virtual void Paint(PaintEventArgs paintEventArgs) + { + } - /// - /// We ignore the Transform, as the coordinates are directly bound to those of the owner - /// - /// - public virtual void Transform(Matrix matrix) - { - } - } -} + /// + /// We ignore the Transform, as the coordinates are directly bound to those of the owner + /// + /// + public virtual void Transform(Matrix matrix) + { + } + } +} \ No newline at end of file diff --git a/src/Greenshot/Drawing/Adorners/MoveAdorner.cs b/src/Greenshot/Drawing/Adorners/MoveAdorner.cs index c8ef3ea22..73c75141c 100644 --- a/src/Greenshot/Drawing/Adorners/MoveAdorner.cs +++ b/src/Greenshot/Drawing/Adorners/MoveAdorner.cs @@ -27,136 +27,140 @@ using GreenshotPlugin.Interfaces.Drawing; namespace Greenshot.Drawing.Adorners { - /// - /// This is the adorner for the line based containers - /// - public class MoveAdorner : AbstractAdorner - { - private Rectangle _boundsBeforeResize = Rectangle.Empty; - private RectangleF _boundsAfterResize = RectangleF.Empty; + /// + /// This is the adorner for the line based containers + /// + public class MoveAdorner : AbstractAdorner + { + private Rectangle _boundsBeforeResize = Rectangle.Empty; + private RectangleF _boundsAfterResize = RectangleF.Empty; - public Positions Position { get; private set; } + public Positions Position { get; private set; } - public MoveAdorner(IDrawableContainer owner, Positions position) : base(owner) - { - Position = position; - } + public MoveAdorner(IDrawableContainer owner, Positions position) : base(owner) + { + Position = position; + } - /// - /// Returns the cursor for when the mouse is over the adorner - /// - public override Cursor Cursor => Cursors.SizeAll; + /// + /// Returns the cursor for when the mouse is over the adorner + /// + public override Cursor Cursor => Cursors.SizeAll; - /// - /// Handle the mouse down - /// - /// - /// - public override void MouseDown(object sender, MouseEventArgs mouseEventArgs) - { - EditStatus = EditStatus.RESIZING; - _boundsBeforeResize = new Rectangle(Owner.Left, Owner.Top, Owner.Width, Owner.Height); - _boundsAfterResize = _boundsBeforeResize; - } + /// + /// Handle the mouse down + /// + /// + /// + public override void MouseDown(object sender, MouseEventArgs mouseEventArgs) + { + EditStatus = EditStatus.RESIZING; + _boundsBeforeResize = new Rectangle(Owner.Left, Owner.Top, Owner.Width, Owner.Height); + _boundsAfterResize = _boundsBeforeResize; + } - /// - /// Handle the mouse move - /// - /// - /// - public override void MouseMove(object sender, MouseEventArgs mouseEventArgs) - { - if (EditStatus != EditStatus.RESIZING) - { - return; - } - Owner.Invalidate(); - Owner.MakeBoundsChangeUndoable(false); + /// + /// Handle the mouse move + /// + /// + /// + public override void MouseMove(object sender, MouseEventArgs mouseEventArgs) + { + if (EditStatus != EditStatus.RESIZING) + { + return; + } - // reset "workbench" rectangle to current bounds - _boundsAfterResize.X = _boundsBeforeResize.X; - _boundsAfterResize.Y = _boundsBeforeResize.Y; - _boundsAfterResize.Width = _boundsBeforeResize.Width; - _boundsAfterResize.Height = _boundsBeforeResize.Height; + Owner.Invalidate(); + Owner.MakeBoundsChangeUndoable(false); - // calculate scaled rectangle - ScaleHelper.Scale(ref _boundsAfterResize, Position, new PointF(mouseEventArgs.X, mouseEventArgs.Y), ScaleHelper.GetScaleOptions()); + // reset "workbench" rectangle to current bounds + _boundsAfterResize.X = _boundsBeforeResize.X; + _boundsAfterResize.Y = _boundsBeforeResize.Y; + _boundsAfterResize.Width = _boundsBeforeResize.Width; + _boundsAfterResize.Height = _boundsBeforeResize.Height; - // apply scaled bounds to this DrawableContainer - Owner.ApplyBounds(_boundsAfterResize); + // calculate scaled rectangle + ScaleHelper.Scale(ref _boundsAfterResize, Position, new PointF(mouseEventArgs.X, mouseEventArgs.Y), ScaleHelper.GetScaleOptions()); - Owner.Invalidate(); - } + // apply scaled bounds to this DrawableContainer + Owner.ApplyBounds(_boundsAfterResize); - /// - /// Return the location of the adorner - /// - public override Point Location { - get - { - int x = 0,y = 0; - switch (Position) - { - case Positions.TopLeft: - x = Owner.Left; - y = Owner.Top; - break; - case Positions.BottomLeft: - x = Owner.Left; - y = Owner.Top + Owner.Height; - break; - case Positions.MiddleLeft: - x = Owner.Left; - y = Owner.Top + (Owner.Height / 2); - break; - case Positions.TopCenter: - x = Owner.Left + (Owner.Width / 2); - y = Owner.Top; - break; - case Positions.BottomCenter: - x = Owner.Left + (Owner.Width / 2); - y = Owner.Top + Owner.Height; - break; - case Positions.TopRight: - x = Owner.Left + Owner.Width; - y = Owner.Top; - break; - case Positions.BottomRight: - x = Owner.Left + Owner.Width; - y = Owner.Top + Owner.Height; - break; - case Positions.MiddleRight: - x = Owner.Left + Owner.Width; - y = Owner.Top + (Owner.Height / 2); - break; - } - return new Point(x, y); - } - } + Owner.Invalidate(); + } - /// - /// Draw the adorner - /// - /// PaintEventArgs - public override void Paint(PaintEventArgs paintEventArgs) - { - Graphics targetGraphics = paintEventArgs.Graphics; + /// + /// Return the location of the adorner + /// + public override Point Location + { + get + { + int x = 0, y = 0; + switch (Position) + { + case Positions.TopLeft: + x = Owner.Left; + y = Owner.Top; + break; + case Positions.BottomLeft: + x = Owner.Left; + y = Owner.Top + Owner.Height; + break; + case Positions.MiddleLeft: + x = Owner.Left; + y = Owner.Top + (Owner.Height / 2); + break; + case Positions.TopCenter: + x = Owner.Left + (Owner.Width / 2); + y = Owner.Top; + break; + case Positions.BottomCenter: + x = Owner.Left + (Owner.Width / 2); + y = Owner.Top + Owner.Height; + break; + case Positions.TopRight: + x = Owner.Left + Owner.Width; + y = Owner.Top; + break; + case Positions.BottomRight: + x = Owner.Left + Owner.Width; + y = Owner.Top + Owner.Height; + break; + case Positions.MiddleRight: + x = Owner.Left + Owner.Width; + y = Owner.Top + (Owner.Height / 2); + break; + } - var bounds = BoundsOnSurface; - GraphicsState state = targetGraphics.Save(); + return new Point(x, y); + } + } - targetGraphics.CompositingMode = CompositingMode.SourceCopy; + /// + /// Draw the adorner + /// + /// PaintEventArgs + public override void Paint(PaintEventArgs paintEventArgs) + { + Graphics targetGraphics = paintEventArgs.Graphics; - try - { - targetGraphics.FillRectangle(Brushes.Black, bounds); - targetGraphics.DrawRectangle(new Pen(Brushes.White), bounds); - } - catch - { - // Ignore, BUG-2065 - } - targetGraphics.Restore(state); - } - } -} + var bounds = BoundsOnSurface; + GraphicsState state = targetGraphics.Save(); + + targetGraphics.CompositingMode = CompositingMode.SourceCopy; + + try + { + targetGraphics.FillRectangle(Brushes.Black, bounds); + targetGraphics.DrawRectangle(new Pen(Brushes.White), bounds); + } + catch + { + // Ignore, BUG-2065 + } + + targetGraphics.Restore(state); + } + } +} \ No newline at end of file diff --git a/src/Greenshot/Drawing/Adorners/ResizeAdorner.cs b/src/Greenshot/Drawing/Adorners/ResizeAdorner.cs index b2e7261a9..b6480180d 100644 --- a/src/Greenshot/Drawing/Adorners/ResizeAdorner.cs +++ b/src/Greenshot/Drawing/Adorners/ResizeAdorner.cs @@ -27,156 +27,160 @@ using GreenshotPlugin.Interfaces.Drawing; namespace Greenshot.Drawing.Adorners { - /// - /// This is the default "legacy" gripper adorner, not the one used for the tail in the speech-bubble - /// - public class ResizeAdorner : AbstractAdorner - { - private Rectangle _boundsBeforeResize = Rectangle.Empty; - private RectangleF _boundsAfterResize = RectangleF.Empty; + /// + /// This is the default "legacy" gripper adorner, not the one used for the tail in the speech-bubble + /// + public class ResizeAdorner : AbstractAdorner + { + private Rectangle _boundsBeforeResize = Rectangle.Empty; + private RectangleF _boundsAfterResize = RectangleF.Empty; - public Positions Position { get; private set; } + public Positions Position { get; private set; } - public ResizeAdorner(IDrawableContainer owner, Positions position) : base(owner) - { - Position = position; - } + public ResizeAdorner(IDrawableContainer owner, Positions position) : base(owner) + { + Position = position; + } - /// - /// Returns the cursor for when the mouse is over the adorner - /// - public override Cursor Cursor - { - get - { - bool isNotSwitched = Owner.Width >= 0; - if (Owner.Height < 0) - { - isNotSwitched = !isNotSwitched; - } - switch (Position) - { - case Positions.TopLeft: - case Positions.BottomRight: - return isNotSwitched ? Cursors.SizeNWSE : Cursors.SizeNESW; - case Positions.TopRight: - case Positions.BottomLeft: - return isNotSwitched ? Cursors.SizeNESW : Cursors.SizeNWSE; - case Positions.MiddleLeft: - case Positions.MiddleRight: - return Cursors.SizeWE; - case Positions.TopCenter: - case Positions.BottomCenter: - return Cursors.SizeNS; - default: - return Cursors.SizeAll; - } - } - } + /// + /// Returns the cursor for when the mouse is over the adorner + /// + public override Cursor Cursor + { + get + { + bool isNotSwitched = Owner.Width >= 0; + if (Owner.Height < 0) + { + isNotSwitched = !isNotSwitched; + } - /// - /// Handle the mouse down - /// - /// - /// - public override void MouseDown(object sender, MouseEventArgs mouseEventArgs) - { - EditStatus = EditStatus.RESIZING; - _boundsBeforeResize = new Rectangle(Owner.Left, Owner.Top, Owner.Width, Owner.Height); - _boundsAfterResize = _boundsBeforeResize; - } + switch (Position) + { + case Positions.TopLeft: + case Positions.BottomRight: + return isNotSwitched ? Cursors.SizeNWSE : Cursors.SizeNESW; + case Positions.TopRight: + case Positions.BottomLeft: + return isNotSwitched ? Cursors.SizeNESW : Cursors.SizeNWSE; + case Positions.MiddleLeft: + case Positions.MiddleRight: + return Cursors.SizeWE; + case Positions.TopCenter: + case Positions.BottomCenter: + return Cursors.SizeNS; + default: + return Cursors.SizeAll; + } + } + } - /// - /// Handle the mouse move - /// - /// - /// - public override void MouseMove(object sender, MouseEventArgs mouseEventArgs) - { - if (EditStatus != EditStatus.RESIZING) - { - return; - } - Owner.Invalidate(); - Owner.MakeBoundsChangeUndoable(false); + /// + /// Handle the mouse down + /// + /// + /// + public override void MouseDown(object sender, MouseEventArgs mouseEventArgs) + { + EditStatus = EditStatus.RESIZING; + _boundsBeforeResize = new Rectangle(Owner.Left, Owner.Top, Owner.Width, Owner.Height); + _boundsAfterResize = _boundsBeforeResize; + } - // reset "workbench" rectangle to current bounds - _boundsAfterResize.X = _boundsBeforeResize.X; - _boundsAfterResize.Y = _boundsBeforeResize.Y; - _boundsAfterResize.Width = _boundsBeforeResize.Width; - _boundsAfterResize.Height = _boundsBeforeResize.Height; + /// + /// Handle the mouse move + /// + /// + /// + public override void MouseMove(object sender, MouseEventArgs mouseEventArgs) + { + if (EditStatus != EditStatus.RESIZING) + { + return; + } - // calculate scaled rectangle - ScaleHelper.Scale(ref _boundsAfterResize, Position, new PointF(mouseEventArgs.X, mouseEventArgs.Y), ScaleHelper.GetScaleOptions()); + Owner.Invalidate(); + Owner.MakeBoundsChangeUndoable(false); - // apply scaled bounds to this DrawableContainer - Owner.ApplyBounds(_boundsAfterResize); + // reset "workbench" rectangle to current bounds + _boundsAfterResize.X = _boundsBeforeResize.X; + _boundsAfterResize.Y = _boundsBeforeResize.Y; + _boundsAfterResize.Width = _boundsBeforeResize.Width; + _boundsAfterResize.Height = _boundsBeforeResize.Height; - Owner.Invalidate(); - } + // calculate scaled rectangle + ScaleHelper.Scale(ref _boundsAfterResize, Position, new PointF(mouseEventArgs.X, mouseEventArgs.Y), ScaleHelper.GetScaleOptions()); - /// - /// Return the location of the adorner - /// - public override Point Location { - get - { - int x = 0,y = 0; - switch (Position) - { - case Positions.TopLeft: - x = Owner.Left; - y = Owner.Top; - break; - case Positions.BottomLeft: - x = Owner.Left; - y = Owner.Top + Owner.Height; - break; - case Positions.MiddleLeft: - x = Owner.Left; - y = Owner.Top + (Owner.Height / 2); - break; - case Positions.TopCenter: - x = Owner.Left + (Owner.Width / 2); - y = Owner.Top; - break; - case Positions.BottomCenter: - x = Owner.Left + (Owner.Width / 2); - y = Owner.Top + Owner.Height; - break; - case Positions.TopRight: - x = Owner.Left + Owner.Width; - y = Owner.Top; - break; - case Positions.BottomRight: - x = Owner.Left + Owner.Width; - y = Owner.Top + Owner.Height; - break; - case Positions.MiddleRight: - x = Owner.Left + Owner.Width; - y = Owner.Top + (Owner.Height / 2); - break; - } - return new Point(x, y); - } - } + // apply scaled bounds to this DrawableContainer + Owner.ApplyBounds(_boundsAfterResize); - /// - /// Draw the adorner - /// - /// PaintEventArgs - public override void Paint(PaintEventArgs paintEventArgs) - { - Graphics targetGraphics = paintEventArgs.Graphics; + Owner.Invalidate(); + } - var bounds = BoundsOnSurface; - GraphicsState state = targetGraphics.Save(); + /// + /// Return the location of the adorner + /// + public override Point Location + { + get + { + int x = 0, y = 0; + switch (Position) + { + case Positions.TopLeft: + x = Owner.Left; + y = Owner.Top; + break; + case Positions.BottomLeft: + x = Owner.Left; + y = Owner.Top + Owner.Height; + break; + case Positions.MiddleLeft: + x = Owner.Left; + y = Owner.Top + (Owner.Height / 2); + break; + case Positions.TopCenter: + x = Owner.Left + (Owner.Width / 2); + y = Owner.Top; + break; + case Positions.BottomCenter: + x = Owner.Left + (Owner.Width / 2); + y = Owner.Top + Owner.Height; + break; + case Positions.TopRight: + x = Owner.Left + Owner.Width; + y = Owner.Top; + break; + case Positions.BottomRight: + x = Owner.Left + Owner.Width; + y = Owner.Top + Owner.Height; + break; + case Positions.MiddleRight: + x = Owner.Left + Owner.Width; + y = Owner.Top + (Owner.Height / 2); + break; + } - targetGraphics.CompositingMode = CompositingMode.SourceCopy; + return new Point(x, y); + } + } - targetGraphics.FillRectangle(Brushes.Black, bounds); - targetGraphics.DrawRectangle(new Pen(Brushes.White), bounds); - targetGraphics.Restore(state); - } - } -} + /// + /// Draw the adorner + /// + /// PaintEventArgs + public override void Paint(PaintEventArgs paintEventArgs) + { + Graphics targetGraphics = paintEventArgs.Graphics; + + var bounds = BoundsOnSurface; + GraphicsState state = targetGraphics.Save(); + + targetGraphics.CompositingMode = CompositingMode.SourceCopy; + + targetGraphics.FillRectangle(Brushes.Black, bounds); + targetGraphics.DrawRectangle(new Pen(Brushes.White), bounds); + targetGraphics.Restore(state); + } + } +} \ No newline at end of file diff --git a/src/Greenshot/Drawing/Adorners/TargetAdorner.cs b/src/Greenshot/Drawing/Adorners/TargetAdorner.cs index e0d0bbf13..546606b66 100644 --- a/src/Greenshot/Drawing/Adorners/TargetAdorner.cs +++ b/src/Greenshot/Drawing/Adorners/TargetAdorner.cs @@ -26,94 +26,100 @@ using GreenshotPlugin.Interfaces.Drawing; namespace Greenshot.Drawing.Adorners { - /// - /// This implements the special "gripper" for the Speech-Bubble tail - /// - public class TargetAdorner : AbstractAdorner - { + /// + /// This implements the special "gripper" for the Speech-Bubble tail + /// + public class TargetAdorner : AbstractAdorner + { + public TargetAdorner(IDrawableContainer owner, Point location) : base(owner) + { + Location = location; + } - public TargetAdorner(IDrawableContainer owner, Point location) : base(owner) - { - Location = location; - } + /// + /// Handle the mouse down + /// + /// + /// + public override void MouseDown(object sender, MouseEventArgs mouseEventArgs) + { + EditStatus = EditStatus.MOVING; + } - /// - /// Handle the mouse down - /// - /// - /// - public override void MouseDown(object sender, MouseEventArgs mouseEventArgs) - { - EditStatus = EditStatus.MOVING; - } + /// + /// Handle the mouse move + /// + /// + /// + public override void MouseMove(object sender, MouseEventArgs mouseEventArgs) + { + if (EditStatus != EditStatus.MOVING) + { + return; + } - /// - /// Handle the mouse move - /// - /// - /// - public override void MouseMove(object sender, MouseEventArgs mouseEventArgs) - { - if (EditStatus != EditStatus.MOVING) - { - return; - } + Owner.Invalidate(); + Point newGripperLocation = new Point(mouseEventArgs.X, mouseEventArgs.Y); + Rectangle imageBounds = new Rectangle(0, 0, Owner.Parent.Image.Width, Owner.Parent.Image.Height); + // Check if gripper inside the parent (surface), if not we need to move it inside + // This was made for BUG-1682 + if (!imageBounds.Contains(newGripperLocation)) + { + if (newGripperLocation.X > imageBounds.Right) + { + newGripperLocation.X = imageBounds.Right - 5; + } - Owner.Invalidate(); - Point newGripperLocation = new Point(mouseEventArgs.X, mouseEventArgs.Y); - Rectangle imageBounds = new Rectangle(0, 0, Owner.Parent.Image.Width, Owner.Parent.Image.Height); - // Check if gripper inside the parent (surface), if not we need to move it inside - // This was made for BUG-1682 - if (!imageBounds.Contains(newGripperLocation)) - { - if (newGripperLocation.X > imageBounds.Right) - { - newGripperLocation.X = imageBounds.Right - 5; - } - if (newGripperLocation.X < imageBounds.Left) - { - newGripperLocation.X = imageBounds.Left; - } - if (newGripperLocation.Y > imageBounds.Bottom) - { - newGripperLocation.Y = imageBounds.Bottom - 5; - } - if (newGripperLocation.Y < imageBounds.Top) - { - newGripperLocation.Y = imageBounds.Top; - } - } + if (newGripperLocation.X < imageBounds.Left) + { + newGripperLocation.X = imageBounds.Left; + } - Location = newGripperLocation; - Owner.Invalidate(); - } + if (newGripperLocation.Y > imageBounds.Bottom) + { + newGripperLocation.Y = imageBounds.Bottom - 5; + } - /// - /// Draw the adorner - /// - /// PaintEventArgs - public override void Paint(PaintEventArgs paintEventArgs) - { - Graphics targetGraphics = paintEventArgs.Graphics; + if (newGripperLocation.Y < imageBounds.Top) + { + newGripperLocation.Y = imageBounds.Top; + } + } - var bounds = BoundsOnSurface; + Location = newGripperLocation; + Owner.Invalidate(); + } + + /// + /// Draw the adorner + /// + /// PaintEventArgs + public override void Paint(PaintEventArgs paintEventArgs) + { + Graphics targetGraphics = paintEventArgs.Graphics; + + var bounds = BoundsOnSurface; targetGraphics.FillRectangle(Brushes.Green, bounds); - targetGraphics.DrawRectangle(new Pen(Brushes.White), bounds); - } + targetGraphics.DrawRectangle(new Pen(Brushes.White), bounds); + } - /// - /// Made sure this adorner is transformed - /// - /// Matrix - public override void Transform(Matrix matrix) - { - if (matrix == null) - { - return; - } - Point[] points = new[] { Location }; - matrix.TransformPoints(points); - Location = points[0]; - } - } -} + /// + /// Made sure this adorner is transformed + /// + /// Matrix + public override void Transform(Matrix matrix) + { + if (matrix == null) + { + return; + } + + Point[] points = new[] + { + Location + }; + matrix.TransformPoints(points); + Location = points[0]; + } + } +} \ No newline at end of file diff --git a/src/Greenshot/Drawing/ArrowContainer.cs b/src/Greenshot/Drawing/ArrowContainer.cs index acfd1ad03..a49ffe863 100644 --- a/src/Greenshot/Drawing/ArrowContainer.cs +++ b/src/Greenshot/Drawing/ArrowContainer.cs @@ -18,57 +18,71 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ + using System; using System.Drawing; using System.Drawing.Drawing2D; - using Greenshot.Drawing.Fields; using GreenshotPlugin.Interfaces.Drawing; -namespace Greenshot.Drawing { - /// - /// Description of LineContainer. - /// - [Serializable()] - public class ArrowContainer : LineContainer { - public enum ArrowHeadCombination { NONE, START_POINT, END_POINT, BOTH }; - - private static readonly AdjustableArrowCap ARROW_CAP = new AdjustableArrowCap(4, 6); +namespace Greenshot.Drawing +{ + /// + /// Description of LineContainer. + /// + [Serializable()] + public class ArrowContainer : LineContainer + { + public enum ArrowHeadCombination + { + NONE, + START_POINT, + END_POINT, + BOTH + }; - public ArrowContainer(Surface parent) : base(parent) { - } + private static readonly AdjustableArrowCap ARROW_CAP = new AdjustableArrowCap(4, 6); - /// - /// Do not use the base, just override so we have our own defaults - /// - protected override void InitializeFields() { - AddField(GetType(), FieldType.LINE_THICKNESS, 2); - AddField(GetType(), FieldType.ARROWHEADS, 2); - AddField(GetType(), FieldType.LINE_COLOR, Color.Red); - AddField(GetType(), FieldType.FILL_COLOR, Color.Transparent); - AddField(GetType(), FieldType.SHADOW, true); - AddField(GetType(), FieldType.ARROWHEADS, ArrowHeadCombination.END_POINT); - } + public ArrowContainer(Surface parent) : base(parent) + { + } - public override void Draw(Graphics graphics, RenderMode rm) { - int lineThickness = GetFieldValueAsInt(FieldType.LINE_THICKNESS); - bool shadow = GetFieldValueAsBool(FieldType.SHADOW); + /// + /// Do not use the base, just override so we have our own defaults + /// + protected override void InitializeFields() + { + AddField(GetType(), FieldType.LINE_THICKNESS, 2); + AddField(GetType(), FieldType.ARROWHEADS, 2); + AddField(GetType(), FieldType.LINE_COLOR, Color.Red); + AddField(GetType(), FieldType.FILL_COLOR, Color.Transparent); + AddField(GetType(), FieldType.SHADOW, true); + AddField(GetType(), FieldType.ARROWHEADS, ArrowHeadCombination.END_POINT); + } - if (lineThickness > 0 ) { - graphics.SmoothingMode = SmoothingMode.HighQuality; - graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; - graphics.CompositingQuality = CompositingQuality.HighQuality; - graphics.PixelOffsetMode = PixelOffsetMode.None; - Color lineColor = GetFieldValueAsColor(FieldType.LINE_COLOR); - ArrowHeadCombination heads = (ArrowHeadCombination)GetFieldValue(FieldType.ARROWHEADS); - if (lineThickness > 0) { - if (shadow) { - //draw shadow first - int basealpha = 100; - int alpha = basealpha; - int steps = 5; - int currentStep = 1; - while (currentStep <= steps) + public override void Draw(Graphics graphics, RenderMode rm) + { + int lineThickness = GetFieldValueAsInt(FieldType.LINE_THICKNESS); + bool shadow = GetFieldValueAsBool(FieldType.SHADOW); + + if (lineThickness > 0) + { + graphics.SmoothingMode = SmoothingMode.HighQuality; + graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; + graphics.CompositingQuality = CompositingQuality.HighQuality; + graphics.PixelOffsetMode = PixelOffsetMode.None; + Color lineColor = GetFieldValueAsColor(FieldType.LINE_COLOR); + ArrowHeadCombination heads = (ArrowHeadCombination) GetFieldValue(FieldType.ARROWHEADS); + if (lineThickness > 0) + { + if (shadow) + { + //draw shadow first + int basealpha = 100; + int alpha = basealpha; + int steps = 5; + int currentStep = 1; + while (currentStep <= steps) { using Pen shadowCapPen = new Pen(Color.FromArgb(alpha, 100, 100, 100), lineThickness); SetArrowHeads(heads, shadowCapPen); @@ -82,35 +96,40 @@ namespace Greenshot.Drawing { currentStep++; alpha -= basealpha / steps; } - - } + } using Pen pen = new Pen(lineColor, lineThickness); SetArrowHeads(heads, pen); graphics.DrawLine(pen, Left, Top, Left + Width, Top + Height); } - } - } - - private void SetArrowHeads(ArrowHeadCombination heads, Pen pen) { - if ( heads == ArrowHeadCombination.BOTH || heads == ArrowHeadCombination.START_POINT ) { - pen.CustomStartCap = ARROW_CAP; - } - if ( heads == ArrowHeadCombination.BOTH || heads == ArrowHeadCombination.END_POINT ) { - pen.CustomEndCap = ARROW_CAP; - } - } - - public override Rectangle DrawingBounds { - get { - int lineThickness = GetFieldValueAsInt(FieldType.LINE_THICKNESS); - if (lineThickness > 0) + } + } + + private void SetArrowHeads(ArrowHeadCombination heads, Pen pen) + { + if (heads == ArrowHeadCombination.BOTH || heads == ArrowHeadCombination.START_POINT) + { + pen.CustomStartCap = ARROW_CAP; + } + + if (heads == ArrowHeadCombination.BOTH || heads == ArrowHeadCombination.END_POINT) + { + pen.CustomEndCap = ARROW_CAP; + } + } + + public override Rectangle DrawingBounds + { + get + { + int lineThickness = GetFieldValueAsInt(FieldType.LINE_THICKNESS); + if (lineThickness > 0) { - using Pen pen = new Pen(Color.White) - { - Width = lineThickness - }; - SetArrowHeads((ArrowHeadCombination)GetFieldValue(FieldType.ARROWHEADS), pen); + using Pen pen = new Pen(Color.White) + { + Width = lineThickness + }; + SetArrowHeads((ArrowHeadCombination) GetFieldValue(FieldType.ARROWHEADS), pen); using GraphicsPath path = new GraphicsPath(); path.AddLine(Left, Top, Left + Width, Top + Height); using Matrix matrix = new Matrix(); @@ -118,24 +137,27 @@ namespace Greenshot.Drawing { drawingBounds.Inflate(2, 2); return drawingBounds; } - return Rectangle.Empty; - } - } - - public override bool ClickableAt(int x, int y) { - int lineThickness = GetFieldValueAsInt(FieldType.LINE_THICKNESS) + 10; - if (lineThickness > 0) + + return Rectangle.Empty; + } + } + + public override bool ClickableAt(int x, int y) + { + int lineThickness = GetFieldValueAsInt(FieldType.LINE_THICKNESS) + 10; + if (lineThickness > 0) { - using Pen pen = new Pen(Color.White) - { - Width = lineThickness - }; - SetArrowHeads((ArrowHeadCombination)GetFieldValue(FieldType.ARROWHEADS), pen); + using Pen pen = new Pen(Color.White) + { + Width = lineThickness + }; + SetArrowHeads((ArrowHeadCombination) GetFieldValue(FieldType.ARROWHEADS), pen); using GraphicsPath path = new GraphicsPath(); path.AddLine(Left, Top, Left + Width, Top + Height); return path.IsOutlineVisible(x, y, pen); } - return false; - } - } -} + + return false; + } + } +} \ No newline at end of file diff --git a/src/Greenshot/Drawing/CropContainer.cs b/src/Greenshot/Drawing/CropContainer.cs index da160f11e..164fd791e 100644 --- a/src/Greenshot/Drawing/CropContainer.cs +++ b/src/Greenshot/Drawing/CropContainer.cs @@ -25,53 +25,62 @@ using Greenshot.Drawing.Fields; using Greenshot.Helpers; using GreenshotPlugin.Interfaces.Drawing; -namespace Greenshot.Drawing { - /// - /// Description of CropContainer. - /// - public class CropContainer : DrawableContainer { - public CropContainer(Surface parent) : base(parent) { - Init(); - } +namespace Greenshot.Drawing +{ + /// + /// Description of CropContainer. + /// + public class CropContainer : DrawableContainer + { + public CropContainer(Surface parent) : base(parent) + { + Init(); + } - protected override void OnDeserialized(StreamingContext streamingContext) - { - base.OnDeserialized(streamingContext); - Init(); - } + protected override void OnDeserialized(StreamingContext streamingContext) + { + base.OnDeserialized(streamingContext); + Init(); + } - private void Init() - { - CreateDefaultAdorners(); - } - protected override void InitializeFields() { - AddField(GetType(), FieldType.FLAGS, FieldFlag.CONFIRMABLE); - } + private void Init() + { + CreateDefaultAdorners(); + } - public override void Invalidate() { - _parent?.Invalidate(); - } + protected override void InitializeFields() + { + AddField(GetType(), FieldType.FLAGS, FieldFlag.CONFIRMABLE); + } - /// - /// We need to override the DrawingBound, return a rectangle in the size of the image, to make sure this element is always draw - /// (we create a transparent brown over the complete picture) - /// - public override Rectangle DrawingBounds { - get - { - if (_parent?.Image is { } image) { - return new Rectangle(0, 0, image.Width, image.Height); - } + public override void Invalidate() + { + _parent?.Invalidate(); + } - return Rectangle.Empty; - } - } + /// + /// We need to override the DrawingBound, return a rectangle in the size of the image, to make sure this element is always draw + /// (we create a transparent brown over the complete picture) + /// + public override Rectangle DrawingBounds + { + get + { + if (_parent?.Image is { } image) + { + return new Rectangle(0, 0, image.Width, image.Height); + } - public override void Draw(Graphics g, RenderMode rm) { - if (_parent == null) - { - return; - } + return Rectangle.Empty; + } + } + + public override void Draw(Graphics g, RenderMode rm) + { + if (_parent == null) + { + return; + } using Brush cropBrush = new SolidBrush(Color.FromArgb(100, 150, 150, 100)); Rectangle cropRectangle = GuiRectangle.GetGuiRectangle(Left, Top, Width, Height); @@ -79,20 +88,21 @@ namespace Greenshot.Drawing { Size imageSize = _parent.Image.Size; DrawSelectionBorder(g, selectionRect); - + // top g.FillRectangle(cropBrush, new Rectangle(0, 0, imageSize.Width, cropRectangle.Top)); // left g.FillRectangle(cropBrush, new Rectangle(0, cropRectangle.Top, cropRectangle.Left, cropRectangle.Height)); // right - g.FillRectangle(cropBrush, new Rectangle(cropRectangle.Left + cropRectangle.Width, cropRectangle.Top, imageSize.Width - (cropRectangle.Left + cropRectangle.Width), cropRectangle.Height)); + g.FillRectangle(cropBrush, + new Rectangle(cropRectangle.Left + cropRectangle.Width, cropRectangle.Top, imageSize.Width - (cropRectangle.Left + cropRectangle.Width), cropRectangle.Height)); // bottom g.FillRectangle(cropBrush, new Rectangle(0, cropRectangle.Top + cropRectangle.Height, imageSize.Width, imageSize.Height - (cropRectangle.Top + cropRectangle.Height))); } - - /// - /// No context menu for the CropContainer - /// - public override bool HasContextMenu => false; - } -} + + /// + /// No context menu for the CropContainer + /// + public override bool HasContextMenu => false; + } +} \ No newline at end of file diff --git a/src/Greenshot/Drawing/CursorContainer.cs b/src/Greenshot/Drawing/CursorContainer.cs index 7b3078510..d22b5d672 100644 --- a/src/Greenshot/Drawing/CursorContainer.cs +++ b/src/Greenshot/Drawing/CursorContainer.cs @@ -18,6 +18,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ + using System; using System.Drawing; using System.IO; @@ -27,84 +28,101 @@ using log4net; using System.Runtime.Serialization; using GreenshotPlugin.Interfaces.Drawing; -namespace Greenshot.Drawing { - /// - /// Description of CursorContainer. - /// - [Serializable] - public class CursorContainer : DrawableContainer, ICursorContainer { - private static readonly ILog LOG = LogManager.GetLogger(typeof(CursorContainer)); +namespace Greenshot.Drawing +{ + /// + /// Description of CursorContainer. + /// + [Serializable] + public class CursorContainer : DrawableContainer, ICursorContainer + { + private static readonly ILog LOG = LogManager.GetLogger(typeof(CursorContainer)); - protected Cursor cursor; + protected Cursor cursor; - public CursorContainer(Surface parent) : base(parent) { - Init(); - } + public CursorContainer(Surface parent) : base(parent) + { + Init(); + } - protected override void OnDeserialized(StreamingContext streamingContext) - { - base.OnDeserialized(streamingContext); - Init(); - } + protected override void OnDeserialized(StreamingContext streamingContext) + { + base.OnDeserialized(streamingContext); + Init(); + } - private void Init() - { - CreateDefaultAdorners(); - } + private void Init() + { + CreateDefaultAdorners(); + } - public CursorContainer(Surface parent, string filename) : this(parent) { - Load(filename); - } + public CursorContainer(Surface parent, string filename) : this(parent) + { + Load(filename); + } - public Cursor Cursor { - set { - if (cursor != null) { - cursor.Dispose(); - } - // Clone cursor (is this correct??) - cursor = new Cursor(value.CopyHandle()); - Width = value.Size.Width; - Height = value.Size.Height; - } - get { return cursor; } - } + public Cursor Cursor + { + set + { + if (cursor != null) + { + cursor.Dispose(); + } - /// - /// This Dispose is called from the Dispose and the Destructor. - /// When disposing==true all non-managed resources should be freed too! - /// - /// - protected override void Dispose(bool disposing) { - if (disposing) { - if (cursor != null) { - cursor.Dispose(); - } - } - cursor = null; - base.Dispose(disposing); - } + // Clone cursor (is this correct??) + cursor = new Cursor(value.CopyHandle()); + Width = value.Size.Width; + Height = value.Size.Height; + } + get { return cursor; } + } - public void Load(string filename) { - if (!File.Exists(filename)) { - return; - } + /// + /// This Dispose is called from the Dispose and the Destructor. + /// When disposing==true all non-managed resources should be freed too! + /// + /// + protected override void Dispose(bool disposing) + { + if (disposing) + { + if (cursor != null) + { + cursor.Dispose(); + } + } + + cursor = null; + base.Dispose(disposing); + } + + public void Load(string filename) + { + if (!File.Exists(filename)) + { + return; + } using Cursor fileCursor = new Cursor(filename); Cursor = fileCursor; LOG.Debug("Loaded file: " + filename + " with resolution: " + Height + "," + Width); } - public override void Draw(Graphics graphics, RenderMode rm) { - if (cursor == null) { - return; - } - graphics.SmoothingMode = SmoothingMode.HighQuality; - graphics.InterpolationMode = InterpolationMode.NearestNeighbor; - graphics.CompositingQuality = CompositingQuality.Default; - graphics.PixelOffsetMode = PixelOffsetMode.None; - cursor.DrawStretched(graphics, Bounds); - } + public override void Draw(Graphics graphics, RenderMode rm) + { + if (cursor == null) + { + return; + } - public override Size DefaultSize => cursor?.Size ?? new Size(16, 16); - } -} + graphics.SmoothingMode = SmoothingMode.HighQuality; + graphics.InterpolationMode = InterpolationMode.NearestNeighbor; + graphics.CompositingQuality = CompositingQuality.Default; + graphics.PixelOffsetMode = PixelOffsetMode.None; + cursor.DrawStretched(graphics, Bounds); + } + + public override Size DefaultSize => cursor?.Size ?? new Size(16, 16); + } +} \ No newline at end of file diff --git a/src/Greenshot/Drawing/DrawableContainer.cs b/src/Greenshot/Drawing/DrawableContainer.cs index bc0973300..96ef93b9c 100644 --- a/src/Greenshot/Drawing/DrawableContainer.cs +++ b/src/Greenshot/Drawing/DrawableContainer.cs @@ -40,524 +40,631 @@ using GreenshotPlugin.Interfaces.Drawing.Adorners; namespace Greenshot.Drawing { - /// - /// represents a rectangle, ellipse, label or whatever. Can contain filters, too. - /// serializable for clipboard support - /// Subclasses should fulfill INotifyPropertyChanged contract, i.e. call - /// OnPropertyChanged whenever a public property has been changed. - /// - [Serializable] - public abstract class DrawableContainer : AbstractFieldHolderWithChildren, IDrawableContainer { - private static readonly ILog LOG = LogManager.GetLogger(typeof(DrawableContainer)); - protected static readonly EditorConfiguration EditorConfig = IniConfig.GetIniSection(); - private const int M11 = 0; - private const int M22 = 3; + /// + /// represents a rectangle, ellipse, label or whatever. Can contain filters, too. + /// serializable for clipboard support + /// Subclasses should fulfill INotifyPropertyChanged contract, i.e. call + /// OnPropertyChanged whenever a public property has been changed. + /// + [Serializable] + public abstract class DrawableContainer : AbstractFieldHolderWithChildren, IDrawableContainer + { + private static readonly ILog LOG = LogManager.GetLogger(typeof(DrawableContainer)); + protected static readonly EditorConfiguration EditorConfig = IniConfig.GetIniSection(); + private const int M11 = 0; + private const int M22 = 3; - [OnDeserialized] - private void OnDeserializedInit(StreamingContext context) - { - _adorners = new List(); - OnDeserialized(context); - } + [OnDeserialized] + private void OnDeserializedInit(StreamingContext context) + { + _adorners = new List(); + OnDeserialized(context); + } - /// - /// Override to implement your own deserialization logic, like initializing properties which are not serialized - /// - /// - protected virtual void OnDeserialized(StreamingContext streamingContext) - { - } + /// + /// Override to implement your own deserialization logic, like initializing properties which are not serialized + /// + /// + protected virtual void OnDeserialized(StreamingContext streamingContext) + { + } - protected EditStatus _defaultEditMode = EditStatus.DRAWING; - public EditStatus DefaultEditMode { - get { - return _defaultEditMode; - } - } + protected EditStatus _defaultEditMode = EditStatus.DRAWING; - /// - /// The public accessible Dispose - /// Will call the GarbageCollector to SuppressFinalize, preventing being cleaned twice - /// - public void Dispose() { - Dispose(true); - GC.SuppressFinalize(this); - } + public EditStatus DefaultEditMode + { + get { return _defaultEditMode; } + } - protected virtual void Dispose(bool disposing) { - if (!disposing) { - return; - } - _parent?.FieldAggregator?.UnbindElement(this); - } + /// + /// The public accessible Dispose + /// Will call the GarbageCollector to SuppressFinalize, preventing being cleaned twice + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } - ~DrawableContainer() { - Dispose(false); - } + protected virtual void Dispose(bool disposing) + { + if (!disposing) + { + return; + } - [NonSerialized] - private PropertyChangedEventHandler _propertyChanged; - public event PropertyChangedEventHandler PropertyChanged { - add => _propertyChanged += value; - remove => _propertyChanged -= value; - } + _parent?.FieldAggregator?.UnbindElement(this); + } - public IList Filters { - get { - List ret = new List(); - foreach(IFieldHolder c in Children) { - if (c is IFilter) { - ret.Add(c as IFilter); - } - } - return ret; - } - } + ~DrawableContainer() + { + Dispose(false); + } - [NonSerialized] - internal Surface _parent; - public ISurface Parent { - get => _parent; - set => SwitchParent((Surface)value); - } + [NonSerialized] private PropertyChangedEventHandler _propertyChanged; - [NonSerialized] - private TargetAdorner _targetAdorner; - public TargetAdorner TargetAdorner => _targetAdorner; + public event PropertyChangedEventHandler PropertyChanged + { + add => _propertyChanged += value; + remove => _propertyChanged -= value; + } - [NonSerialized] - private bool _selected; - public bool Selected { - get => _selected; - set { - _selected = value; - OnPropertyChanged("Selected"); - } - } + public IList Filters + { + get + { + List ret = new List(); + foreach (IFieldHolder c in Children) + { + if (c is IFilter) + { + ret.Add(c as IFilter); + } + } - [NonSerialized] - private EditStatus _status = EditStatus.UNDRAWN; - public EditStatus Status { - get => _status; - set => _status = value; - } + return ret; + } + } + + [NonSerialized] internal Surface _parent; + + public ISurface Parent + { + get => _parent; + set => SwitchParent((Surface) value); + } + + [NonSerialized] private TargetAdorner _targetAdorner; + public TargetAdorner TargetAdorner => _targetAdorner; + + [NonSerialized] private bool _selected; + + public bool Selected + { + get => _selected; + set + { + _selected = value; + OnPropertyChanged("Selected"); + } + } + + [NonSerialized] private EditStatus _status = EditStatus.UNDRAWN; + + public EditStatus Status + { + get => _status; + set => _status = value; + } - private int left; - public int Left { - get => left; - set { - if (value == left) { - return; - } - left = value; - } - } + private int left; - private int top; - public int Top { - get => top; - set { - if (value == top) { - return; - } - top = value; - } - } + public int Left + { + get => left; + set + { + if (value == left) + { + return; + } - private int width; - public int Width { - get => width; - set { - if (value == width) { - return; - } - width = value; - } - } + left = value; + } + } - private int height; - public int Height { - get => height; - set { - if (value == height) { - return; - } - height = value; - } - } + private int top; - public Point Location { - get => new Point(left, top); - set { - left = value.X; - top = value.Y; - } - } + public int Top + { + get => top; + set + { + if (value == top) + { + return; + } - public Size Size { - get => new Size(width, height); - set { - width = value.Width; - height = value.Height; - } - } + top = value; + } + } - /// - /// List of available Adorners - /// - [NonSerialized] - private IList _adorners = new List(); - public IList Adorners => _adorners; + private int width; + + public int Width + { + get => width; + set + { + if (value == width) + { + return; + } + + width = value; + } + } + + private int height; + + public int Height + { + get => height; + set + { + if (value == height) + { + return; + } + + height = value; + } + } + + public Point Location + { + get => new Point(left, top); + set + { + left = value.X; + top = value.Y; + } + } + + public Size Size + { + get => new Size(width, height); + set + { + width = value.Width; + height = value.Height; + } + } + + /// + /// List of available Adorners + /// + [NonSerialized] private IList _adorners = new List(); + + public IList Adorners => _adorners; [NonSerialized] - // will store current bounds of this DrawableContainer before starting a resize - protected Rectangle _boundsBeforeResize = Rectangle.Empty; + // will store current bounds of this DrawableContainer before starting a resize + protected Rectangle _boundsBeforeResize = Rectangle.Empty; - [NonSerialized] - // "workbench" rectangle - used for calculating bounds during resizing (to be applied to this DrawableContainer afterwards) - protected RectangleF _boundsAfterResize = RectangleF.Empty; + [NonSerialized] + // "workbench" rectangle - used for calculating bounds during resizing (to be applied to this DrawableContainer afterwards) + protected RectangleF _boundsAfterResize = RectangleF.Empty; - public Rectangle Bounds { - get => GuiRectangle.GetGuiRectangle(Left, Top, Width, Height); - set { - Left = Round(value.Left); - Top = Round(value.Top); - Width = Round(value.Width); - Height = Round(value.Height); - } - } + public Rectangle Bounds + { + get => GuiRectangle.GetGuiRectangle(Left, Top, Width, Height); + set + { + Left = Round(value.Left); + Top = Round(value.Top); + Width = Round(value.Width); + Height = Round(value.Height); + } + } - public virtual void ApplyBounds(RectangleF newBounds) { - Left = Round(newBounds.Left); - Top = Round(newBounds.Top); - Width = Round(newBounds.Width); - Height = Round(newBounds.Height); - } + public virtual void ApplyBounds(RectangleF newBounds) + { + Left = Round(newBounds.Left); + Top = Round(newBounds.Top); + Width = Round(newBounds.Width); + Height = Round(newBounds.Height); + } - public DrawableContainer(Surface parent) { - InitializeFields(); - _parent = parent; - } + public DrawableContainer(Surface parent) + { + InitializeFields(); + _parent = parent; + } - public void Add(IFilter filter) { - AddChild(filter); - } + public void Add(IFilter filter) + { + AddChild(filter); + } - public void Remove(IFilter filter) { - RemoveChild(filter); - } + public void Remove(IFilter filter) + { + RemoveChild(filter); + } - private static int Round(float f) { - if(float.IsPositiveInfinity(f) || f>int.MaxValue/2) return int.MaxValue/2; - if (float.IsNegativeInfinity(f) || f int.MaxValue / 2) return int.MaxValue / 2; + if (float.IsNegativeInfinity(f) || f < int.MinValue / 2) return int.MinValue / 2; + return (int) Math.Round(f); + } - private bool accountForShadowChange; - public virtual Rectangle DrawingBounds { - get { - foreach(IFilter filter in Filters) { - if (filter.Invert) { - return new Rectangle(Point.Empty, _parent.Image.Size); - } - } - // Take a base safety margin - int lineThickness = 5; + private bool accountForShadowChange; + + public virtual Rectangle DrawingBounds + { + get + { + foreach (IFilter filter in Filters) + { + if (filter.Invert) + { + return new Rectangle(Point.Empty, _parent.Image.Size); + } + } + + // Take a base safety margin + int lineThickness = 5; // add adorner size lineThickness += Adorners.Max(adorner => Math.Max(adorner.Bounds.Width, adorner.Bounds.Height)); - if (HasField(FieldType.LINE_THICKNESS)) { - lineThickness += GetFieldValueAsInt(FieldType.LINE_THICKNESS); - } - int offset = lineThickness/2; + if (HasField(FieldType.LINE_THICKNESS)) + { + lineThickness += GetFieldValueAsInt(FieldType.LINE_THICKNESS); + } - int shadow = 0; - if (accountForShadowChange || (HasField(FieldType.SHADOW) && GetFieldValueAsBool(FieldType.SHADOW))){ - accountForShadowChange = false; - shadow += 10; - } - return new Rectangle(Bounds.Left-offset, Bounds.Top-offset, Bounds.Width+lineThickness+shadow, Bounds.Height+lineThickness+shadow); - } - } + int offset = lineThickness / 2; - public virtual void Invalidate() { - if (Status != EditStatus.UNDRAWN) { - _parent?.InvalidateElements(DrawingBounds); - } - } + int shadow = 0; + if (accountForShadowChange || (HasField(FieldType.SHADOW) && GetFieldValueAsBool(FieldType.SHADOW))) + { + accountForShadowChange = false; + shadow += 10; + } - public virtual bool InitContent() { return true; } + return new Rectangle(Bounds.Left - offset, Bounds.Top - offset, Bounds.Width + lineThickness + shadow, Bounds.Height + lineThickness + shadow); + } + } - public virtual void OnDoubleClick() {} - - /// - /// Initialize a target gripper - /// - protected void InitAdorner(Color gripperColor, Point location) { - _targetAdorner = new TargetAdorner(this, location); - Adorners.Add(_targetAdorner); - } - - /// - /// Create the default adorners for a rectangle based container - /// - - protected void CreateDefaultAdorners() { - if (Adorners.Count > 0) - { - LOG.Warn("Adorners are already defined!"); - } - // Create the GripperAdorners - Adorners.Add(new ResizeAdorner(this, Positions.TopLeft)); - Adorners.Add(new ResizeAdorner(this, Positions.TopCenter)); - Adorners.Add(new ResizeAdorner(this, Positions.TopRight)); - Adorners.Add(new ResizeAdorner(this, Positions.BottomLeft)); - Adorners.Add(new ResizeAdorner(this, Positions.BottomCenter)); - Adorners.Add(new ResizeAdorner(this, Positions.BottomRight)); - Adorners.Add(new ResizeAdorner(this, Positions.MiddleLeft)); - Adorners.Add(new ResizeAdorner(this, Positions.MiddleRight)); - } - - public bool hasFilters => Filters.Count > 0; - - public abstract void Draw(Graphics graphics, RenderMode renderMode); - - public virtual void DrawContent(Graphics graphics, Bitmap bmp, RenderMode renderMode, Rectangle clipRectangle) { - if (Children.Count > 0) { - if (Status != EditStatus.IDLE) { - DrawSelectionBorder(graphics, Bounds); - } else { - if (clipRectangle.Width != 0 && clipRectangle.Height != 0) { - foreach(IFilter filter in Filters) { - if (filter.Invert) { - filter.Apply(graphics, bmp, Bounds, renderMode); - } else { - Rectangle drawingRect = new Rectangle(Bounds.Location, Bounds.Size); - drawingRect.Intersect(clipRectangle); - if(filter is MagnifierFilter) { - // quick&dirty bugfix, because MagnifierFilter behaves differently when drawn only partially - // what we should actually do to resolve this is add a better magnifier which is not that special - filter.Apply(graphics, bmp, Bounds, renderMode); - } else { - filter.Apply(graphics, bmp, drawingRect, renderMode); - } - } - } - } - - } - } - Draw(graphics, renderMode); - } - - /// - /// Adjust UI elements to the supplied DPI settings - /// - /// uint with dpi value - public void AdjustToDpi(uint dpi) - { - foreach(var adorner in Adorners) - { - adorner.AdjustToDpi(dpi); - } - } - - public virtual bool Contains(int x, int y) { - return Bounds.Contains(x , y); - } - - public virtual bool ClickableAt(int x, int y) { - Rectangle r = GuiRectangle.GetGuiRectangle(Left, Top, Width, Height); - r.Inflate(5, 5); - return r.Contains(x, y); - } - - protected void DrawSelectionBorder(Graphics g, Rectangle rect) + public virtual void Invalidate() { - using Pen pen = new Pen(Color.MediumSeaGreen) - { - DashPattern = new float[] { 1, 2 }, - Width = 1 - }; - g.DrawRectangle(pen, rect); + if (Status != EditStatus.UNDRAWN) + { + _parent?.InvalidateElements(DrawingBounds); + } + } + + public virtual bool InitContent() + { + return true; + } + + public virtual void OnDoubleClick() + { + } + + /// + /// Initialize a target gripper + /// + protected void InitAdorner(Color gripperColor, Point location) + { + _targetAdorner = new TargetAdorner(this, location); + Adorners.Add(_targetAdorner); + } + + /// + /// Create the default adorners for a rectangle based container + /// + protected void CreateDefaultAdorners() + { + if (Adorners.Count > 0) + { + LOG.Warn("Adorners are already defined!"); + } + + // Create the GripperAdorners + Adorners.Add(new ResizeAdorner(this, Positions.TopLeft)); + Adorners.Add(new ResizeAdorner(this, Positions.TopCenter)); + Adorners.Add(new ResizeAdorner(this, Positions.TopRight)); + Adorners.Add(new ResizeAdorner(this, Positions.BottomLeft)); + Adorners.Add(new ResizeAdorner(this, Positions.BottomCenter)); + Adorners.Add(new ResizeAdorner(this, Positions.BottomRight)); + Adorners.Add(new ResizeAdorner(this, Positions.MiddleLeft)); + Adorners.Add(new ResizeAdorner(this, Positions.MiddleRight)); + } + + public bool hasFilters => Filters.Count > 0; + + public abstract void Draw(Graphics graphics, RenderMode renderMode); + + public virtual void DrawContent(Graphics graphics, Bitmap bmp, RenderMode renderMode, Rectangle clipRectangle) + { + if (Children.Count > 0) + { + if (Status != EditStatus.IDLE) + { + DrawSelectionBorder(graphics, Bounds); + } + else + { + if (clipRectangle.Width != 0 && clipRectangle.Height != 0) + { + foreach (IFilter filter in Filters) + { + if (filter.Invert) + { + filter.Apply(graphics, bmp, Bounds, renderMode); + } + else + { + Rectangle drawingRect = new Rectangle(Bounds.Location, Bounds.Size); + drawingRect.Intersect(clipRectangle); + if (filter is MagnifierFilter) + { + // quick&dirty bugfix, because MagnifierFilter behaves differently when drawn only partially + // what we should actually do to resolve this is add a better magnifier which is not that special + filter.Apply(graphics, bmp, Bounds, renderMode); + } + else + { + filter.Apply(graphics, bmp, drawingRect, renderMode); + } + } + } + } + } + } + + Draw(graphics, renderMode); + } + + /// + /// Adjust UI elements to the supplied DPI settings + /// + /// uint with dpi value + public void AdjustToDpi(uint dpi) + { + foreach (var adorner in Adorners) + { + adorner.AdjustToDpi(dpi); + } + } + + public virtual bool Contains(int x, int y) + { + return Bounds.Contains(x, y); + } + + public virtual bool ClickableAt(int x, int y) + { + Rectangle r = GuiRectangle.GetGuiRectangle(Left, Top, Width, Height); + r.Inflate(5, 5); + return r.Contains(x, y); + } + + protected void DrawSelectionBorder(Graphics g, Rectangle rect) + { + using Pen pen = new Pen(Color.MediumSeaGreen) + { + DashPattern = new float[] + { + 1, 2 + }, + Width = 1 + }; + g.DrawRectangle(pen, rect); } - public void ResizeTo(int width, int height, int anchorPosition) { - Width = width; - Height = height; - } + public void ResizeTo(int width, int height, int anchorPosition) + { + Width = width; + Height = height; + } - /// - /// Make a following bounds change on this drawablecontainer undoable! - /// - /// true means allow the moves to be merged - public void MakeBoundsChangeUndoable(bool allowMerge) { - _parent.MakeUndoable(new DrawableContainerBoundsChangeMemento(this), allowMerge); - } + /// + /// Make a following bounds change on this drawablecontainer undoable! + /// + /// true means allow the moves to be merged + public void MakeBoundsChangeUndoable(bool allowMerge) + { + _parent.MakeUndoable(new DrawableContainerBoundsChangeMemento(this), allowMerge); + } - public void MoveBy(int dx, int dy) { - Left += dx; - Top += dy; - } + public void MoveBy(int dx, int dy) + { + Left += dx; + Top += dy; + } - /// - /// A handler for the MouseDown, used if you don't want the surface to handle this for you - /// - /// current mouse x - /// current mouse y - /// true if the event is handled, false if the surface needs to handle it - public virtual bool HandleMouseDown(int x, int y) { - Left = _boundsBeforeResize.X = x; - Top = _boundsBeforeResize.Y = y; - return true; - } + /// + /// A handler for the MouseDown, used if you don't want the surface to handle this for you + /// + /// current mouse x + /// current mouse y + /// true if the event is handled, false if the surface needs to handle it + public virtual bool HandleMouseDown(int x, int y) + { + Left = _boundsBeforeResize.X = x; + Top = _boundsBeforeResize.Y = y; + return true; + } - /// - /// A handler for the MouseMove, used if you don't want the surface to handle this for you - /// - /// current mouse x - /// current mouse y - /// true if the event is handled, false if the surface needs to handle it - public virtual bool HandleMouseMove(int x, int y) { - Invalidate(); + /// + /// A handler for the MouseMove, used if you don't want the surface to handle this for you + /// + /// current mouse x + /// current mouse y + /// true if the event is handled, false if the surface needs to handle it + public virtual bool HandleMouseMove(int x, int y) + { + Invalidate(); - // reset "workrbench" rectangle to current bounds - _boundsAfterResize.X = _boundsBeforeResize.Left; - _boundsAfterResize.Y = _boundsBeforeResize.Top; - _boundsAfterResize.Width = x - _boundsAfterResize.Left; - _boundsAfterResize.Height = y - _boundsAfterResize.Top; + // reset "workrbench" rectangle to current bounds + _boundsAfterResize.X = _boundsBeforeResize.Left; + _boundsAfterResize.Y = _boundsBeforeResize.Top; + _boundsAfterResize.Width = x - _boundsAfterResize.Left; + _boundsAfterResize.Height = y - _boundsAfterResize.Top; - ScaleHelper.Scale(_boundsBeforeResize, x, y, ref _boundsAfterResize, GetAngleRoundProcessor()); + ScaleHelper.Scale(_boundsBeforeResize, x, y, ref _boundsAfterResize, GetAngleRoundProcessor()); - // apply scaled bounds to this DrawableContainer - ApplyBounds(_boundsAfterResize); + // apply scaled bounds to this DrawableContainer + ApplyBounds(_boundsAfterResize); - Invalidate(); - return true; - } + Invalidate(); + return true; + } - /// - /// A handler for the MouseUp - /// - /// current mouse x - /// current mouse y - public virtual void HandleMouseUp(int x, int y) { - } + /// + /// A handler for the MouseUp + /// + /// current mouse x + /// current mouse y + public virtual void HandleMouseUp(int x, int y) + { + } - protected virtual void SwitchParent(Surface newParent) { - if (newParent == Parent) - { - return; - } - _parent?.FieldAggregator?.UnbindElement(this); + protected virtual void SwitchParent(Surface newParent) + { + if (newParent == Parent) + { + return; + } - _parent = newParent; - foreach(IFilter filter in Filters) { - filter.Parent = this; - } - } + _parent?.FieldAggregator?.UnbindElement(this); - protected void OnPropertyChanged(string propertyName) { - if (_propertyChanged != null) { - _propertyChanged(this, new PropertyChangedEventArgs(propertyName)); - Invalidate(); - } - } + _parent = newParent; + foreach (IFilter filter in Filters) + { + filter.Parent = this; + } + } - /// - /// This method will be called before a field is changes. - /// Using this makes it possible to invalidate the object as is before changing. - /// - /// The field to be changed - /// The new value - public virtual void BeforeFieldChange(IField fieldToBeChanged, object newValue) { - _parent?.MakeUndoable(new ChangeFieldHolderMemento(this, fieldToBeChanged), true); - Invalidate(); - } + protected void OnPropertyChanged(string propertyName) + { + if (_propertyChanged != null) + { + _propertyChanged(this, new PropertyChangedEventArgs(propertyName)); + Invalidate(); + } + } - /// - /// Handle the field changed event, this should invalidate the correct bounds (e.g. when shadow comes or goes more pixels!) - /// - /// - /// - public void HandleFieldChanged(object sender, FieldChangedEventArgs e) { - LOG.DebugFormat("Field {0} changed", e.Field.FieldType); - if (Equals(e.Field.FieldType, FieldType.SHADOW)) { - accountForShadowChange = true; - } - } + /// + /// This method will be called before a field is changes. + /// Using this makes it possible to invalidate the object as is before changing. + /// + /// The field to be changed + /// The new value + public virtual void BeforeFieldChange(IField fieldToBeChanged, object newValue) + { + _parent?.MakeUndoable(new ChangeFieldHolderMemento(this, fieldToBeChanged), true); + Invalidate(); + } - /// - /// Retrieve the Y scale from the matrix - /// - /// - /// - public static float CalculateScaleY(Matrix matrix) { - return matrix.Elements[M22]; - } + /// + /// Handle the field changed event, this should invalidate the correct bounds (e.g. when shadow comes or goes more pixels!) + /// + /// + /// + public void HandleFieldChanged(object sender, FieldChangedEventArgs e) + { + LOG.DebugFormat("Field {0} changed", e.Field.FieldType); + if (Equals(e.Field.FieldType, FieldType.SHADOW)) + { + accountForShadowChange = true; + } + } - /// - /// Retrieve the X scale from the matrix - /// - /// - /// - public static float CalculateScaleX(Matrix matrix) { - return matrix.Elements[M11]; - } + /// + /// Retrieve the Y scale from the matrix + /// + /// + /// + public static float CalculateScaleY(Matrix matrix) + { + return matrix.Elements[M22]; + } - /// - /// Retrieve the rotation angle from the matrix - /// - /// - /// - public static int CalculateAngle(Matrix matrix) { - const int M11 = 0; - const int M21 = 2; - var radians = Math.Atan2(matrix.Elements[M21], matrix.Elements[M11]); - return (int)-Math.Round(radians * 180 / Math.PI); - } + /// + /// Retrieve the X scale from the matrix + /// + /// + /// + public static float CalculateScaleX(Matrix matrix) + { + return matrix.Elements[M11]; + } - /// - /// This method is called on a DrawableContainers when: - /// 1) The capture on the surface is modified in such a way, that the elements would not be placed correctly. - /// 2) Currently not implemented: an element needs to be moved, scaled or rotated. - /// This basis implementation makes sure the coordinates of the element, including the TargetGripper, is correctly rotated/scaled/translated. - /// But this implementation doesn't take care of any changes to the content!! - /// - /// - public virtual void Transform(Matrix matrix) { - if (matrix == null) { - return; - } - Point topLeft = new Point(Left, Top); - Point bottomRight = new Point(Left + Width, Top + Height); - Point[] points = new[] { topLeft, bottomRight }; - matrix.TransformPoints(points); + /// + /// Retrieve the rotation angle from the matrix + /// + /// + /// + public static int CalculateAngle(Matrix matrix) + { + const int M11 = 0; + const int M21 = 2; + var radians = Math.Atan2(matrix.Elements[M21], matrix.Elements[M11]); + return (int) -Math.Round(radians * 180 / Math.PI); + } - Left = points[0].X; - Top = points[0].Y; - Width = points[1].X - points[0].X; - Height = points[1].Y - points[0].Y; + /// + /// This method is called on a DrawableContainers when: + /// 1) The capture on the surface is modified in such a way, that the elements would not be placed correctly. + /// 2) Currently not implemented: an element needs to be moved, scaled or rotated. + /// This basis implementation makes sure the coordinates of the element, including the TargetGripper, is correctly rotated/scaled/translated. + /// But this implementation doesn't take care of any changes to the content!! + /// + /// + public virtual void Transform(Matrix matrix) + { + if (matrix == null) + { + return; + } - } + Point topLeft = new Point(Left, Top); + Point bottomRight = new Point(Left + Width, Top + Height); + Point[] points = new[] + { + topLeft, bottomRight + }; + matrix.TransformPoints(points); - protected virtual ScaleHelper.IDoubleProcessor GetAngleRoundProcessor() { - return ScaleHelper.ShapeAngleRoundBehavior.Instance; - } + Left = points[0].X; + Top = points[0].Y; + Width = points[1].X - points[0].X; + Height = points[1].Y - points[0].Y; + } - public virtual bool HasContextMenu => true; + protected virtual ScaleHelper.IDoubleProcessor GetAngleRoundProcessor() + { + return ScaleHelper.ShapeAngleRoundBehavior.Instance; + } - public virtual bool HasDefaultSize => false; + public virtual bool HasContextMenu => true; - public virtual Size DefaultSize => throw new NotSupportedException("Object doesn't have a default size"); + public virtual bool HasDefaultSize => false; - /// - /// Allows to override the initializing of the fields, so we can actually have our own defaults - /// - protected virtual void InitializeFields() { - } - } + public virtual Size DefaultSize => throw new NotSupportedException("Object doesn't have a default size"); + + /// + /// Allows to override the initializing of the fields, so we can actually have our own defaults + /// + protected virtual void InitializeFields() + { + } + } } \ No newline at end of file diff --git a/src/Greenshot/Drawing/DrawableContainerList.cs b/src/Greenshot/Drawing/DrawableContainerList.cs index a7107543e..c450ebad4 100644 --- a/src/Greenshot/Drawing/DrawableContainerList.cs +++ b/src/Greenshot/Drawing/DrawableContainerList.cs @@ -33,588 +33,696 @@ using System.Windows.Forms; using Greenshot.Forms; using GreenshotPlugin.Interfaces; -namespace Greenshot.Drawing { - /// - /// Dispatches most of a DrawableContainer's public properties and methods to a list of DrawableContainers. - /// - [Serializable] - public class DrawableContainerList : List, IDrawableContainerList - { - private static readonly ComponentResourceManager EditorFormResources = new ComponentResourceManager(typeof(ImageEditorForm)); +namespace Greenshot.Drawing +{ + /// + /// Dispatches most of a DrawableContainer's public properties and methods to a list of DrawableContainers. + /// + [Serializable] + public class DrawableContainerList : List, IDrawableContainerList + { + private static readonly ComponentResourceManager EditorFormResources = new ComponentResourceManager(typeof(ImageEditorForm)); - public Guid ParentID { - get; - private set; - } + public Guid ParentID { get; private set; } - public DrawableContainerList() { - } + public DrawableContainerList() + { + } - public DrawableContainerList(Guid parentId) { - ParentID = parentId; - } - - public EditStatus Status { - get { - return this[Count-1].Status; - } - set { - foreach (var dc in this) { - dc.Status = value; - } - } - } - - public List AsIDrawableContainerList() { - List interfaceList = new List(); - foreach(IDrawableContainer container in this) { - interfaceList.Add(container); - } - return interfaceList; - } - - /// - /// Gets or sets the selection status of the elements. - /// If several elements are in the list, true is only returned when all elements are selected. - /// - public bool Selected { - get { - bool ret = true; - foreach(var dc in this) { - ret &= dc.Selected; - } - return ret; - } - set { - foreach(var dc in this) { - dc.Selected = value; - } - } - } - - /// - /// Gets or sets the parent control of the elements in the list. - /// If there are several elements, the parent control of the last added is returned. - /// - public ISurface Parent { - get { - if (Count > 0) { - return this[Count-1].Parent; - } - return null; - } - set { - ParentID = value?.ID ?? Guid.NewGuid(); - foreach (var drawableContainer in this) { - var dc = (DrawableContainer) drawableContainer; - dc.Parent = value; - } - } - } - - /// - /// Make a following bounds change on this containerlist undoable! - /// - /// true means allow the moves to be merged - public void MakeBoundsChangeUndoable(bool allowMerge) { - if (Count > 0 && Parent != null) - { - var clone = new DrawableContainerList(); - clone.AddRange(this); - Parent.MakeUndoable(new DrawableContainerBoundsChangeMemento(clone), allowMerge); - } - } + public DrawableContainerList(Guid parentId) + { + ParentID = parentId; + } - /// - /// Apply matrix to all elements - /// - public void Transform(Matrix matrix) { - // Track modifications - bool modified = false; - Invalidate(); - foreach (var dc in this) { - dc.Transform(matrix); - modified = true; - } - // Invalidate after - Invalidate(); - // If we moved something, tell the surface it's modified! - if (modified) { - Parent.Modified = true; - } - } + public EditStatus Status + { + get { return this[Count - 1].Status; } + set + { + foreach (var dc in this) + { + dc.Status = value; + } + } + } - /// - /// Moves all elements in the list by the given amount of pixels. - /// - /// pixels to move horizontally - /// pixels to move vertically - public void MoveBy(int dx, int dy) { - // Track modifications - bool modified = false; + public List AsIDrawableContainerList() + { + List interfaceList = new List(); + foreach (IDrawableContainer container in this) + { + interfaceList.Add(container); + } - // Invalidate before moving, otherwise the old locations aren't refreshed - Invalidate(); - foreach(var dc in this) { - dc.Left += dx; - dc.Top += dy; - modified = true; - } - // Invalidate after - Invalidate(); + return interfaceList; + } - // If we moved something, tell the surface it's modified! - if (modified) { - Parent.Modified = true; - } - } - - /// - /// Indicates whether on of the elements is clickable at the given location - /// - /// x coordinate to be checked - /// y coordinate to be checked - /// true if one of the elements in the list is clickable at the given location, false otherwise - public bool ClickableAt(int x, int y) { - bool ret = false; - foreach(var dc in this) { - ret |= dc.ClickableAt(x, y); - } - return ret; - } - - /// - /// retrieves the topmost element being clickable at the given location - /// - /// x coordinate to be checked - /// y coordinate to be checked - /// the topmost element from the list being clickable at the given location, null if there is no clickable element - public IDrawableContainer ClickableElementAt(int x, int y) { - for (int i=Count-1; i>=0; i--) { - if (this[i].ClickableAt(x,y)) { - return this[i]; - } - } - return null; - } - - /// - /// Dispatches OnDoubleClick to all elements in the list. - /// - public void OnDoubleClick() { - foreach(var drawableContainer in this) { - var dc = (DrawableContainer) drawableContainer; - dc.OnDoubleClick(); - } - } + /// + /// Gets or sets the selection status of the elements. + /// If several elements are in the list, true is only returned when all elements are selected. + /// + public bool Selected + { + get + { + bool ret = true; + foreach (var dc in this) + { + ret &= dc.Selected; + } - /// - /// Check if there are any intersecting filters, if so we need to redraw more - /// - /// - /// true if an filter intersects - public bool HasIntersectingFilters(Rectangle clipRectangle) { - foreach(var dc in this) { - if (dc.DrawingBounds.IntersectsWith(clipRectangle) && dc.hasFilters && dc.Status == EditStatus.IDLE) { - return true; - } - } - return false; - } + return ret; + } + set + { + foreach (var dc in this) + { + dc.Selected = value; + } + } + } - /// - /// Check if any of the drawableContainers are inside the rectangle - /// - /// - /// - public bool IntersectsWith(Rectangle clipRectangle) { - foreach(var dc in this) { - if (dc.DrawingBounds.IntersectsWith(clipRectangle)) { - return true; - } - } - return false; - } + /// + /// Gets or sets the parent control of the elements in the list. + /// If there are several elements, the parent control of the last added is returned. + /// + public ISurface Parent + { + get + { + if (Count > 0) + { + return this[Count - 1].Parent; + } - /// - /// A rectangle containing DrawingBounds of all drawableContainers in this list, - /// or empty rectangle if nothing is there. - /// - public Rectangle DrawingBounds - { - get - { - if (Count == 0) - { - return Rectangle.Empty; - } - else - { - var result = this[0].DrawingBounds; - for (int i = 1; i < Count; i++) - { - result = Rectangle.Union(result, this[i].DrawingBounds); - } - return result; - } - } - } + return null; + } + set + { + ParentID = value?.ID ?? Guid.NewGuid(); + foreach (var drawableContainer in this) + { + var dc = (DrawableContainer) drawableContainer; + dc.Parent = value; + } + } + } - /// - /// Triggers all elements in the list ot be redrawn. - /// - /// the to the bitmap related Graphics object - /// Bitmap to draw - /// the rendermode in which the element is to be drawn - /// - public void Draw(Graphics g, Bitmap bitmap, RenderMode renderMode, Rectangle clipRectangle) { - if (Parent == null) - { - return; - } - foreach (var drawableContainer in this) - { - var dc = (DrawableContainer)drawableContainer; - if (dc.Parent == null) - { - continue; - } - if (dc.DrawingBounds.IntersectsWith(clipRectangle)) - { - dc.DrawContent(g, bitmap, renderMode, clipRectangle); - } - } - } - - /// - /// Pass the field changed event to all elements in the list - /// - /// - /// - public void HandleFieldChangedEvent(object sender, FieldChangedEventArgs e) { - foreach(var drawableContainer in this) { - var dc = (DrawableContainer) drawableContainer; - dc.HandleFieldChanged(sender, e); - } - } + /// + /// Make a following bounds change on this containerlist undoable! + /// + /// true means allow the moves to be merged + public void MakeBoundsChangeUndoable(bool allowMerge) + { + if (Count > 0 && Parent != null) + { + var clone = new DrawableContainerList(); + clone.AddRange(this); + Parent.MakeUndoable(new DrawableContainerBoundsChangeMemento(clone), allowMerge); + } + } - /// - /// Invalidate the bounds of all the DC's in this list - /// - public void Invalidate() { - if (Parent == null) - { - return; - } - Rectangle region = Rectangle.Empty; - foreach (var dc in this) - { - region = Rectangle.Union(region, dc.DrawingBounds); - } - Parent.InvalidateElements(region); - } - /// - /// Indicates whether the given list of elements can be pulled up, - /// i.e. whether there is at least one unselected element higher in hierarchy - /// - /// list of elements to pull up - /// true if the elements could be pulled up - public bool CanPullUp(IDrawableContainerList elements) { - if (elements.Count == 0 || elements.Count == Count) { - return false; - } - foreach(var element in elements) { - if (IndexOf(element) < Count - elements.Count) { - return true; - } - } - return false; - } - - /// - /// Pulls one or several elements up one level in hierarchy (z-index). - /// - /// list of elements to pull up - public void PullElementsUp(IDrawableContainerList elements) { - for(int i=Count-1; i>=0; i--) { - var dc = this[i]; - if (!elements.Contains(dc)) { - continue; - } - if (Count > i+1 && !elements.Contains(this[i+1])) { - SwapElements(i,i+1); - } - } - } - - /// - /// Pulls one or several elements up to the topmost level(s) in hierarchy (z-index). - /// - /// of elements to pull to top - public void PullElementsToTop(IDrawableContainerList elements) - { - var dcs = ToArray(); - foreach (var dc in dcs) - { - if (!elements.Contains(dc)) { - continue; - } - Remove(dc); - Add(dc); - Parent.Modified = true; - } - } + /// + /// Apply matrix to all elements + /// + public void Transform(Matrix matrix) + { + // Track modifications + bool modified = false; + Invalidate(); + foreach (var dc in this) + { + dc.Transform(matrix); + modified = true; + } - /// - /// Indicates whether the given list of elements can be pushed down, - /// i.e. whether there is at least one unselected element lower in hierarchy - /// - /// list of elements to push down - /// true if the elements could be pushed down - public bool CanPushDown(IDrawableContainerList elements) { - if (elements.Count == 0 || elements.Count == Count) { - return false; - } - foreach(var element in elements) { - if (IndexOf(element) >= elements.Count) { - return true; - } - } - return false; - } - - /// - /// Pushes one or several elements down one level in hierarchy (z-index). - /// - /// list of elements to push down - public void PushElementsDown(IDrawableContainerList elements) { - for(int i=0; i0) && !elements.Contains(this[i-1])) { - SwapElements(i,i-1); - } - } - } - - /// - /// Pushes one or several elements down to the bottommost level(s) in hierarchy (z-index). - /// - /// of elements to push to bottom - public void PushElementsToBottom(IDrawableContainerList elements) { - var dcs = ToArray(); - for(int i=dcs.Length-1; i>=0; i--) { - var dc = dcs[i]; - if (!elements.Contains(dc)) { - continue; - } - Remove(dc); - Insert(0, dc); - Parent.Modified = true; - } - } - - /// - /// swaps two elements in hierarchy (z-index), - /// checks both indices to be in range - /// - /// index of the 1st element - /// index of the 2nd element - private void SwapElements(int index1, int index2) { - if (index1 < 0 || index1 >= Count || index2 < 0 || index2 >= Count || index1 == index2) { - return; - } - var dc = this[index1]; - this[index1] = this[index2]; - this[index2] = dc; - Parent.Modified = true; - } + // Invalidate after + Invalidate(); + // If we moved something, tell the surface it's modified! + if (modified) + { + Parent.Modified = true; + } + } - /// - /// Add items to a context menu for the selected item - /// - /// - /// - public virtual void AddContextMenuItems(ContextMenuStrip menu, ISurface surface) { - bool push = surface.Elements.CanPushDown(this); - bool pull = surface.Elements.CanPullUp(this); + /// + /// Moves all elements in the list by the given amount of pixels. + /// + /// pixels to move horizontally + /// pixels to move vertically + public void MoveBy(int dx, int dy) + { + // Track modifications + bool modified = false; - ToolStripMenuItem item; + // Invalidate before moving, otherwise the old locations aren't refreshed + Invalidate(); + foreach (var dc in this) + { + dc.Left += dx; + dc.Top += dy; + modified = true; + } - // Pull "up" - if (pull) { - item = new ToolStripMenuItem(Language.GetString(LangKey.editor_uptotop)); - item.Click += delegate { - surface.Elements.PullElementsToTop(this); - surface.Elements.Invalidate(); - }; - menu.Items.Add(item); - item = new ToolStripMenuItem(Language.GetString(LangKey.editor_uponelevel)); - item.Click += delegate { - surface.Elements.PullElementsUp(this); - surface.Elements.Invalidate(); - }; - menu.Items.Add(item); - } - // Push "down" - if (push) { - item = new ToolStripMenuItem(Language.GetString(LangKey.editor_downtobottom)); - item.Click += delegate { - surface.Elements.PushElementsToBottom(this); - surface.Elements.Invalidate(); - }; - menu.Items.Add(item); - item = new ToolStripMenuItem(Language.GetString(LangKey.editor_downonelevel)); - item.Click += delegate { - surface.Elements.PushElementsDown(this); - surface.Elements.Invalidate(); - }; - menu.Items.Add(item); - } + // Invalidate after + Invalidate(); - // Duplicate - item = new ToolStripMenuItem(Language.GetString(LangKey.editor_duplicate)); - item.Click += delegate { - IDrawableContainerList dcs = this.Clone(); - dcs.Parent = surface; - dcs.MoveBy(10, 10); - surface.AddElements(dcs); - surface.DeselectAllElements(); - surface.SelectElements(dcs); - }; - menu.Items.Add(item); + // If we moved something, tell the surface it's modified! + if (modified) + { + Parent.Modified = true; + } + } - // Copy - item = new ToolStripMenuItem(Language.GetString(LangKey.editor_copytoclipboard)) - { - Image = (Image) EditorFormResources.GetObject("copyToolStripMenuItem.Image") - }; - item.Click += delegate { - ClipboardHelper.SetClipboardData(typeof(IDrawableContainerList), this); - }; - menu.Items.Add(item); + /// + /// Indicates whether on of the elements is clickable at the given location + /// + /// x coordinate to be checked + /// y coordinate to be checked + /// true if one of the elements in the list is clickable at the given location, false otherwise + public bool ClickableAt(int x, int y) + { + bool ret = false; + foreach (var dc in this) + { + ret |= dc.ClickableAt(x, y); + } - // Cut - item = new ToolStripMenuItem(Language.GetString(LangKey.editor_cuttoclipboard)) - { - Image = (Image) EditorFormResources.GetObject("btnCut.Image") - }; - item.Click += delegate { - ClipboardHelper.SetClipboardData(typeof(IDrawableContainerList), this); - surface.RemoveElements(this); - }; - menu.Items.Add(item); + return ret; + } + + /// + /// retrieves the topmost element being clickable at the given location + /// + /// x coordinate to be checked + /// y coordinate to be checked + /// the topmost element from the list being clickable at the given location, null if there is no clickable element + public IDrawableContainer ClickableElementAt(int x, int y) + { + for (int i = Count - 1; i >= 0; i--) + { + if (this[i].ClickableAt(x, y)) + { + return this[i]; + } + } + + return null; + } + + /// + /// Dispatches OnDoubleClick to all elements in the list. + /// + public void OnDoubleClick() + { + foreach (var drawableContainer in this) + { + var dc = (DrawableContainer) drawableContainer; + dc.OnDoubleClick(); + } + } + + /// + /// Check if there are any intersecting filters, if so we need to redraw more + /// + /// + /// true if an filter intersects + public bool HasIntersectingFilters(Rectangle clipRectangle) + { + foreach (var dc in this) + { + if (dc.DrawingBounds.IntersectsWith(clipRectangle) && dc.hasFilters && dc.Status == EditStatus.IDLE) + { + return true; + } + } + + return false; + } + + /// + /// Check if any of the drawableContainers are inside the rectangle + /// + /// + /// + public bool IntersectsWith(Rectangle clipRectangle) + { + foreach (var dc in this) + { + if (dc.DrawingBounds.IntersectsWith(clipRectangle)) + { + return true; + } + } + + return false; + } + + /// + /// A rectangle containing DrawingBounds of all drawableContainers in this list, + /// or empty rectangle if nothing is there. + /// + public Rectangle DrawingBounds + { + get + { + if (Count == 0) + { + return Rectangle.Empty; + } + else + { + var result = this[0].DrawingBounds; + for (int i = 1; i < Count; i++) + { + result = Rectangle.Union(result, this[i].DrawingBounds); + } + + return result; + } + } + } + + /// + /// Triggers all elements in the list ot be redrawn. + /// + /// the to the bitmap related Graphics object + /// Bitmap to draw + /// the rendermode in which the element is to be drawn + /// + public void Draw(Graphics g, Bitmap bitmap, RenderMode renderMode, Rectangle clipRectangle) + { + if (Parent == null) + { + return; + } + + foreach (var drawableContainer in this) + { + var dc = (DrawableContainer) drawableContainer; + if (dc.Parent == null) + { + continue; + } + + if (dc.DrawingBounds.IntersectsWith(clipRectangle)) + { + dc.DrawContent(g, bitmap, renderMode, clipRectangle); + } + } + } + + /// + /// Pass the field changed event to all elements in the list + /// + /// + /// + public void HandleFieldChangedEvent(object sender, FieldChangedEventArgs e) + { + foreach (var drawableContainer in this) + { + var dc = (DrawableContainer) drawableContainer; + dc.HandleFieldChanged(sender, e); + } + } + + /// + /// Invalidate the bounds of all the DC's in this list + /// + public void Invalidate() + { + if (Parent == null) + { + return; + } + + Rectangle region = Rectangle.Empty; + foreach (var dc in this) + { + region = Rectangle.Union(region, dc.DrawingBounds); + } + + Parent.InvalidateElements(region); + } + + /// + /// Indicates whether the given list of elements can be pulled up, + /// i.e. whether there is at least one unselected element higher in hierarchy + /// + /// list of elements to pull up + /// true if the elements could be pulled up + public bool CanPullUp(IDrawableContainerList elements) + { + if (elements.Count == 0 || elements.Count == Count) + { + return false; + } + + foreach (var element in elements) + { + if (IndexOf(element) < Count - elements.Count) + { + return true; + } + } + + return false; + } + + /// + /// Pulls one or several elements up one level in hierarchy (z-index). + /// + /// list of elements to pull up + public void PullElementsUp(IDrawableContainerList elements) + { + for (int i = Count - 1; i >= 0; i--) + { + var dc = this[i]; + if (!elements.Contains(dc)) + { + continue; + } + + if (Count > i + 1 && !elements.Contains(this[i + 1])) + { + SwapElements(i, i + 1); + } + } + } + + /// + /// Pulls one or several elements up to the topmost level(s) in hierarchy (z-index). + /// + /// of elements to pull to top + public void PullElementsToTop(IDrawableContainerList elements) + { + var dcs = ToArray(); + foreach (var dc in dcs) + { + if (!elements.Contains(dc)) + { + continue; + } + + Remove(dc); + Add(dc); + Parent.Modified = true; + } + } + + /// + /// Indicates whether the given list of elements can be pushed down, + /// i.e. whether there is at least one unselected element lower in hierarchy + /// + /// list of elements to push down + /// true if the elements could be pushed down + public bool CanPushDown(IDrawableContainerList elements) + { + if (elements.Count == 0 || elements.Count == Count) + { + return false; + } + + foreach (var element in elements) + { + if (IndexOf(element) >= elements.Count) + { + return true; + } + } + + return false; + } + + /// + /// Pushes one or several elements down one level in hierarchy (z-index). + /// + /// list of elements to push down + public void PushElementsDown(IDrawableContainerList elements) + { + for (int i = 0; i < Count; i++) + { + var dc = this[i]; + if (!elements.Contains(dc)) + { + continue; + } + + if ((i > 0) && !elements.Contains(this[i - 1])) + { + SwapElements(i, i - 1); + } + } + } + + /// + /// Pushes one or several elements down to the bottommost level(s) in hierarchy (z-index). + /// + /// of elements to push to bottom + public void PushElementsToBottom(IDrawableContainerList elements) + { + var dcs = ToArray(); + for (int i = dcs.Length - 1; i >= 0; i--) + { + var dc = dcs[i]; + if (!elements.Contains(dc)) + { + continue; + } + + Remove(dc); + Insert(0, dc); + Parent.Modified = true; + } + } + + /// + /// swaps two elements in hierarchy (z-index), + /// checks both indices to be in range + /// + /// index of the 1st element + /// index of the 2nd element + private void SwapElements(int index1, int index2) + { + if (index1 < 0 || index1 >= Count || index2 < 0 || index2 >= Count || index1 == index2) + { + return; + } + + var dc = this[index1]; + this[index1] = this[index2]; + this[index2] = dc; + Parent.Modified = true; + } + + /// + /// Add items to a context menu for the selected item + /// + /// + /// + public virtual void AddContextMenuItems(ContextMenuStrip menu, ISurface surface) + { + bool push = surface.Elements.CanPushDown(this); + bool pull = surface.Elements.CanPullUp(this); + + ToolStripMenuItem item; + + // Pull "up" + if (pull) + { + item = new ToolStripMenuItem(Language.GetString(LangKey.editor_uptotop)); + item.Click += delegate + { + surface.Elements.PullElementsToTop(this); + surface.Elements.Invalidate(); + }; + menu.Items.Add(item); + item = new ToolStripMenuItem(Language.GetString(LangKey.editor_uponelevel)); + item.Click += delegate + { + surface.Elements.PullElementsUp(this); + surface.Elements.Invalidate(); + }; + menu.Items.Add(item); + } + + // Push "down" + if (push) + { + item = new ToolStripMenuItem(Language.GetString(LangKey.editor_downtobottom)); + item.Click += delegate + { + surface.Elements.PushElementsToBottom(this); + surface.Elements.Invalidate(); + }; + menu.Items.Add(item); + item = new ToolStripMenuItem(Language.GetString(LangKey.editor_downonelevel)); + item.Click += delegate + { + surface.Elements.PushElementsDown(this); + surface.Elements.Invalidate(); + }; + menu.Items.Add(item); + } + + // Duplicate + item = new ToolStripMenuItem(Language.GetString(LangKey.editor_duplicate)); + item.Click += delegate + { + IDrawableContainerList dcs = this.Clone(); + dcs.Parent = surface; + dcs.MoveBy(10, 10); + surface.AddElements(dcs); + surface.DeselectAllElements(); + surface.SelectElements(dcs); + }; + menu.Items.Add(item); + + // Copy + item = new ToolStripMenuItem(Language.GetString(LangKey.editor_copytoclipboard)) + { + Image = (Image) EditorFormResources.GetObject("copyToolStripMenuItem.Image") + }; + item.Click += delegate { ClipboardHelper.SetClipboardData(typeof(IDrawableContainerList), this); }; + menu.Items.Add(item); + + // Cut + item = new ToolStripMenuItem(Language.GetString(LangKey.editor_cuttoclipboard)) + { + Image = (Image) EditorFormResources.GetObject("btnCut.Image") + }; + item.Click += delegate + { + ClipboardHelper.SetClipboardData(typeof(IDrawableContainerList), this); + surface.RemoveElements(this); + }; + menu.Items.Add(item); // Delete item = new ToolStripMenuItem(Language.GetString(LangKey.editor_deleteelement)) { - Image = (Image)EditorFormResources.GetObject("removeObjectToolStripMenuItem.Image") + Image = (Image) EditorFormResources.GetObject("removeObjectToolStripMenuItem.Image") }; - item.Click += delegate { - surface.RemoveElements(this); - }; - menu.Items.Add(item); + item.Click += delegate { surface.RemoveElements(this); }; + menu.Items.Add(item); - // Reset - bool canReset = false; - foreach (var drawableContainer in this) - { - var container = (DrawableContainer)drawableContainer; - if (container.HasDefaultSize) - { - canReset = true; - } - } - if (canReset) { - item = new ToolStripMenuItem(Language.GetString(LangKey.editor_resetsize)); - //item.Image = ((System.Drawing.Image)(editorFormResources.GetObject("removeObjectToolStripMenuItem.Image"))); - item.Click += delegate { - MakeBoundsChangeUndoable(false); - foreach (var drawableContainer in this) { - var container = (DrawableContainer) drawableContainer; - if (!container.HasDefaultSize) { - continue; - } - Size defaultSize = container.DefaultSize; - container.MakeBoundsChangeUndoable(false); - container.Width = defaultSize.Width; - container.Height = defaultSize.Height; - } - surface.Invalidate(); - }; - menu.Items.Add(item); - } - } + // Reset + bool canReset = false; + foreach (var drawableContainer in this) + { + var container = (DrawableContainer) drawableContainer; + if (container.HasDefaultSize) + { + canReset = true; + } + } - public virtual void ShowContextMenu(MouseEventArgs e, ISurface iSurface) - { - if (!(iSurface is Surface surface)) - { - return; - } - bool hasMenu = false; - foreach (var drawableContainer in this) { - var container = (DrawableContainer) drawableContainer; - if (!container.HasContextMenu) { - continue; - } - hasMenu = true; - break; - } - if (hasMenu) { - ContextMenuStrip menu = new ContextMenuStrip(); - AddContextMenuItems(menu, surface); - if (menu.Items.Count > 0) { - menu.Show(surface, surface.ToSurfaceCoordinates(e.Location)); - while (true) { - if (menu.Visible) { - Application.DoEvents(); - Thread.Sleep(100); - } else { - menu.Dispose(); - break; - } - } - } - } - } + if (canReset) + { + item = new ToolStripMenuItem(Language.GetString(LangKey.editor_resetsize)); + //item.Image = ((System.Drawing.Image)(editorFormResources.GetObject("removeObjectToolStripMenuItem.Image"))); + item.Click += delegate + { + MakeBoundsChangeUndoable(false); + foreach (var drawableContainer in this) + { + var container = (DrawableContainer) drawableContainer; + if (!container.HasDefaultSize) + { + continue; + } + + Size defaultSize = container.DefaultSize; + container.MakeBoundsChangeUndoable(false); + container.Width = defaultSize.Width; + container.Height = defaultSize.Height; + } + + surface.Invalidate(); + }; + menu.Items.Add(item); + } + } + + public virtual void ShowContextMenu(MouseEventArgs e, ISurface iSurface) + { + if (!(iSurface is Surface surface)) + { + return; + } + + bool hasMenu = false; + foreach (var drawableContainer in this) + { + var container = (DrawableContainer) drawableContainer; + if (!container.HasContextMenu) + { + continue; + } + + hasMenu = true; + break; + } + + if (hasMenu) + { + ContextMenuStrip menu = new ContextMenuStrip(); + AddContextMenuItems(menu, surface); + if (menu.Items.Count > 0) + { + menu.Show(surface, surface.ToSurfaceCoordinates(e.Location)); + while (true) + { + if (menu.Visible) + { + Application.DoEvents(); + Thread.Sleep(100); + } + else + { + menu.Dispose(); + break; + } + } + } + } + } private bool _disposedValue; // To detect redundant calls - protected virtual void Dispose(bool disposing) - { - if (!_disposedValue) - { - if (disposing) - { - foreach (var drawableContainer in this) - { - drawableContainer.Dispose(); - } - } + protected virtual void Dispose(bool disposing) + { + if (!_disposedValue) + { + if (disposing) + { + foreach (var drawableContainer in this) + { + drawableContainer.Dispose(); + } + } - _disposedValue = true; - } - } + _disposedValue = true; + } + } - // This code added to correctly implement the disposable pattern. - public void Dispose() - { - // Do not change this code. Put cleanup code in Dispose(bool disposing) above. - Dispose(true); - } + // This code added to correctly implement the disposable pattern. + public void Dispose() + { + // Do not change this code. Put cleanup code in Dispose(bool disposing) above. + Dispose(true); + } - /// - /// Adjust UI elements to the supplied DPI settings - /// - /// - public void AdjustToDpi(uint dpi) - { - foreach (var drawableContainer in this) { - drawableContainer.AdjustToDpi(dpi); - } - } - } -} + /// + /// Adjust UI elements to the supplied DPI settings + /// + /// + public void AdjustToDpi(uint dpi) + { + foreach (var drawableContainer in this) + { + drawableContainer.AdjustToDpi(dpi); + } + } + } +} \ No newline at end of file diff --git a/src/Greenshot/Drawing/EllipseContainer.cs b/src/Greenshot/Drawing/EllipseContainer.cs index b7d03ecfe..091c03b17 100644 --- a/src/Greenshot/Drawing/EllipseContainer.cs +++ b/src/Greenshot/Drawing/EllipseContainer.cs @@ -18,64 +18,71 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ + using System; using System.Drawing; using System.Drawing.Drawing2D; - using Greenshot.Drawing.Fields; using Greenshot.Helpers; using GreenshotPlugin.Interfaces.Drawing; -namespace Greenshot.Drawing { - /// - /// Description of EllipseContainer. - /// - [Serializable()] - public class EllipseContainer : DrawableContainer { - public EllipseContainer(Surface parent) : base(parent) { - CreateDefaultAdorners(); - } +namespace Greenshot.Drawing +{ + /// + /// Description of EllipseContainer. + /// + [Serializable()] + public class EllipseContainer : DrawableContainer + { + public EllipseContainer(Surface parent) : base(parent) + { + CreateDefaultAdorners(); + } - protected override void InitializeFields() { - AddField(GetType(), FieldType.LINE_THICKNESS, 2); - AddField(GetType(), FieldType.LINE_COLOR, Color.Red); - AddField(GetType(), FieldType.FILL_COLOR, Color.Transparent); - AddField(GetType(), FieldType.SHADOW, true); - } + protected override void InitializeFields() + { + AddField(GetType(), FieldType.LINE_THICKNESS, 2); + AddField(GetType(), FieldType.LINE_COLOR, Color.Red); + AddField(GetType(), FieldType.FILL_COLOR, Color.Transparent); + AddField(GetType(), FieldType.SHADOW, true); + } - public override void Draw(Graphics graphics, RenderMode renderMode) { - graphics.SmoothingMode = SmoothingMode.HighQuality; - graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; - graphics.CompositingQuality = CompositingQuality.HighQuality; - graphics.PixelOffsetMode = PixelOffsetMode.None; - - int lineThickness = GetFieldValueAsInt(FieldType.LINE_THICKNESS); - Color lineColor = GetFieldValueAsColor(FieldType.LINE_COLOR); - Color fillColor = GetFieldValueAsColor(FieldType.FILL_COLOR); - bool shadow = GetFieldValueAsBool(FieldType.SHADOW); - Rectangle rect = GuiRectangle.GetGuiRectangle(Left, Top, Width, Height); - DrawEllipse(rect, graphics, renderMode, lineThickness, lineColor, fillColor, shadow); - } + public override void Draw(Graphics graphics, RenderMode renderMode) + { + graphics.SmoothingMode = SmoothingMode.HighQuality; + graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; + graphics.CompositingQuality = CompositingQuality.HighQuality; + graphics.PixelOffsetMode = PixelOffsetMode.None; - /// - /// This allows another container to draw an ellipse - /// - /// - /// - /// - /// - /// - /// - /// - public static void DrawEllipse(Rectangle rect, Graphics graphics, RenderMode renderMode, int lineThickness, Color lineColor, Color fillColor, bool shadow) { - bool lineVisible = lineThickness > 0 && Colors.IsVisible(lineColor); - // draw shadow before anything else - if (shadow && (lineVisible || Colors.IsVisible(fillColor))) { - int basealpha = 100; - int alpha = basealpha; - int steps = 5; - int currentStep = lineVisible ? 1 : 0; - while (currentStep <= steps) + int lineThickness = GetFieldValueAsInt(FieldType.LINE_THICKNESS); + Color lineColor = GetFieldValueAsColor(FieldType.LINE_COLOR); + Color fillColor = GetFieldValueAsColor(FieldType.FILL_COLOR); + bool shadow = GetFieldValueAsBool(FieldType.SHADOW); + Rectangle rect = GuiRectangle.GetGuiRectangle(Left, Top, Width, Height); + DrawEllipse(rect, graphics, renderMode, lineThickness, lineColor, fillColor, shadow); + } + + /// + /// This allows another container to draw an ellipse + /// + /// + /// + /// + /// + /// + /// + /// + public static void DrawEllipse(Rectangle rect, Graphics graphics, RenderMode renderMode, int lineThickness, Color lineColor, Color fillColor, bool shadow) + { + bool lineVisible = lineThickness > 0 && Colors.IsVisible(lineColor); + // draw shadow before anything else + if (shadow && (lineVisible || Colors.IsVisible(fillColor))) + { + int basealpha = 100; + int alpha = basealpha; + int steps = 5; + int currentStep = lineVisible ? 1 : 0; + while (currentStep <= steps) { using Pen shadowPen = new Pen(Color.FromArgb(alpha, 100, 100, 100)) { @@ -86,63 +93,71 @@ namespace Greenshot.Drawing { currentStep++; alpha -= basealpha / steps; } - } - //draw the original shape - if (Colors.IsVisible(fillColor)) + } + + //draw the original shape + if (Colors.IsVisible(fillColor)) { using Brush brush = new SolidBrush(fillColor); graphics.FillEllipse(brush, rect); } - if (lineVisible) + + if (lineVisible) { using Pen pen = new Pen(lineColor, lineThickness); graphics.DrawEllipse(pen, rect); } - } - - public override bool Contains(int x, int y) { - return EllipseContains(this, x, y); - } + } - /// - /// Allow the code to be used externally - /// - /// - /// - /// - /// - public static bool EllipseContains(DrawableContainer caller, int x, int y) { - double xDistanceFromCenter = x - (caller.Left + caller.Width / 2); - double yDistanceFromCenter = y - (caller.Top + caller.Height / 2); - // ellipse: x^2/a^2 + y^2/b^2 = 1 - return Math.Pow(xDistanceFromCenter, 2) / Math.Pow(caller.Width / 2, 2) + Math.Pow(yDistanceFromCenter, 2) / Math.Pow(caller.Height / 2, 2) < 1; - } + public override bool Contains(int x, int y) + { + return EllipseContains(this, x, y); + } - public override bool ClickableAt(int x, int y) { + /// + /// Allow the code to be used externally + /// + /// + /// + /// + /// + public static bool EllipseContains(DrawableContainer caller, int x, int y) + { + double xDistanceFromCenter = x - (caller.Left + caller.Width / 2); + double yDistanceFromCenter = y - (caller.Top + caller.Height / 2); + // ellipse: x^2/a^2 + y^2/b^2 = 1 + return Math.Pow(xDistanceFromCenter, 2) / Math.Pow(caller.Width / 2, 2) + Math.Pow(yDistanceFromCenter, 2) / Math.Pow(caller.Height / 2, 2) < 1; + } - int lineThickness = GetFieldValueAsInt(FieldType.LINE_THICKNESS) + 10; - Color fillColor = GetFieldValueAsColor(FieldType.FILL_COLOR); - Rectangle rect = GuiRectangle.GetGuiRectangle(Left, Top, Width, Height); - return EllipseClickableAt(rect, lineThickness, fillColor, x, y); - } + public override bool ClickableAt(int x, int y) + { + int lineThickness = GetFieldValueAsInt(FieldType.LINE_THICKNESS) + 10; + Color fillColor = GetFieldValueAsColor(FieldType.FILL_COLOR); + Rectangle rect = GuiRectangle.GetGuiRectangle(Left, Top, Width, Height); + return EllipseClickableAt(rect, lineThickness, fillColor, x, y); + } - public static bool EllipseClickableAt(Rectangle rect, int lineThickness, Color fillColor, int x, int y) { - // If we clicked inside the rectangle and it's visible we are clickable at. - if (!Color.Transparent.Equals(fillColor)) { - if (rect.Contains(x, y)) { - return true; - } - } + public static bool EllipseClickableAt(Rectangle rect, int lineThickness, Color fillColor, int x, int y) + { + // If we clicked inside the rectangle and it's visible we are clickable at. + if (!Color.Transparent.Equals(fillColor)) + { + if (rect.Contains(x, y)) + { + return true; + } + } - // check the rest of the lines - if (lineThickness > 0) + // check the rest of the lines + if (lineThickness > 0) { using Pen pen = new Pen(Color.White, lineThickness); using GraphicsPath path = new GraphicsPath(); path.AddEllipse(rect); return path.IsOutlineVisible(x, y, pen); } - return false; - } - } -} + + return false; + } + } +} \ No newline at end of file diff --git a/src/Greenshot/Drawing/Fields/AbstractFieldHolder.cs b/src/Greenshot/Drawing/Fields/AbstractFieldHolder.cs index 48dfd95f9..f4bc0377f 100644 --- a/src/Greenshot/Drawing/Fields/AbstractFieldHolder.cs +++ b/src/Greenshot/Drawing/Fields/AbstractFieldHolder.cs @@ -18,12 +18,12 @@ * 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.ComponentModel; using System.Drawing; using System.Runtime.Serialization; - using Greenshot.Configuration; using GreenshotPlugin.IniFile; using log4net; @@ -31,170 +31,162 @@ using GreenshotPlugin.Interfaces.Drawing; namespace Greenshot.Drawing.Fields { - /// - /// Basic IFieldHolder implementation, providing access to a set of fields - /// - [Serializable] - public abstract class AbstractFieldHolder : IFieldHolder - { - private static readonly ILog LOG = LogManager.GetLogger(typeof(AbstractFieldHolder)); - private static readonly EditorConfiguration EditorConfig = IniConfig.GetIniSection(); - [NonSerialized] - private readonly IDictionary _handlers = new Dictionary(); + /// + /// Basic IFieldHolder implementation, providing access to a set of fields + /// + [Serializable] + public abstract class AbstractFieldHolder : IFieldHolder + { + private static readonly ILog LOG = LogManager.GetLogger(typeof(AbstractFieldHolder)); + private static readonly EditorConfiguration EditorConfig = IniConfig.GetIniSection(); + [NonSerialized] private readonly IDictionary _handlers = new Dictionary(); - /// - /// called when a field's value has changed - /// - [NonSerialized] - private FieldChangedEventHandler _fieldChanged; + /// + /// called when a field's value has changed + /// + [NonSerialized] private FieldChangedEventHandler _fieldChanged; - public event FieldChangedEventHandler FieldChanged - { - add { _fieldChanged += value; } - remove { _fieldChanged -= value; } - } + public event FieldChangedEventHandler FieldChanged + { + add { _fieldChanged += value; } + remove { _fieldChanged -= value; } + } - // we keep two Collections of our fields, dictionary for quick access, list for serialization - // this allows us to use default serialization - [NonSerialized] - private IDictionary _fieldsByType = new Dictionary(); - private readonly IList fields = new List(); + // we keep two Collections of our fields, dictionary for quick access, list for serialization + // this allows us to use default serialization + [NonSerialized] private IDictionary _fieldsByType = new Dictionary(); + private readonly IList fields = new List(); - [OnDeserialized] - private void OnDeserialized(StreamingContext context) - { - _fieldsByType = new Dictionary(); - // listen to changing properties - foreach (var field in fields) - { - field.PropertyChanged += delegate { - _fieldChanged?.Invoke(this, new FieldChangedEventArgs(field)); - }; - _fieldsByType[field.FieldType] = field; - } - } + [OnDeserialized] + private void OnDeserialized(StreamingContext context) + { + _fieldsByType = new Dictionary(); + // listen to changing properties + foreach (var field in fields) + { + field.PropertyChanged += delegate { _fieldChanged?.Invoke(this, new FieldChangedEventArgs(field)); }; + _fieldsByType[field.FieldType] = field; + } + } - public void AddField(Type requestingType, IFieldType fieldType, object fieldValue) - { - AddField(EditorConfig.CreateField(requestingType, fieldType, fieldValue)); - } + public void AddField(Type requestingType, IFieldType fieldType, object fieldValue) + { + AddField(EditorConfig.CreateField(requestingType, fieldType, fieldValue)); + } - public virtual void AddField(IField field) - { - fields.Add(field); - if (_fieldsByType == null) - { - return; - } + public virtual void AddField(IField field) + { + fields.Add(field); + if (_fieldsByType == null) + { + return; + } - if (_fieldsByType.ContainsKey(field.FieldType)) - { - if (LOG.IsDebugEnabled) - { - LOG.DebugFormat("A field with of type '{0}' already exists in this {1}, will overwrite.", field.FieldType, GetType()); - } - } + if (_fieldsByType.ContainsKey(field.FieldType)) + { + if (LOG.IsDebugEnabled) + { + LOG.DebugFormat("A field with of type '{0}' already exists in this {1}, will overwrite.", field.FieldType, GetType()); + } + } - _fieldsByType[field.FieldType] = field; + _fieldsByType[field.FieldType] = field; - _handlers[field] = (sender, args) => - { - _fieldChanged?.Invoke(this, new FieldChangedEventArgs(field)); - }; - field.PropertyChanged += _handlers[field]; - } + _handlers[field] = (sender, args) => { _fieldChanged?.Invoke(this, new FieldChangedEventArgs(field)); }; + field.PropertyChanged += _handlers[field]; + } - public void RemoveField(IField field) - { - fields.Remove(field); - _fieldsByType.Remove(field.FieldType); - field.PropertyChanged -= _handlers[field]; - _handlers.Remove(field); - } + public void RemoveField(IField field) + { + fields.Remove(field); + _fieldsByType.Remove(field.FieldType); + field.PropertyChanged -= _handlers[field]; + _handlers.Remove(field); + } - public IList GetFields() - { - return fields; - } + public IList GetFields() + { + return fields; + } - public IField GetField(IFieldType fieldType) - { - try - { - return _fieldsByType[fieldType]; - } - catch (KeyNotFoundException e) - { - throw new ArgumentException("Field '" + fieldType + "' does not exist in " + GetType(), e); - } - } + public IField GetField(IFieldType fieldType) + { + try + { + return _fieldsByType[fieldType]; + } + catch (KeyNotFoundException e) + { + throw new ArgumentException("Field '" + fieldType + "' does not exist in " + GetType(), e); + } + } - public object GetFieldValue(IFieldType fieldType) - { - return GetField(fieldType)?.Value; - } + public object GetFieldValue(IFieldType fieldType) + { + return GetField(fieldType)?.Value; + } public string GetFieldValueAsString(IFieldType fieldType) - { - return Convert.ToString(GetFieldValue(fieldType)); - } + { + return Convert.ToString(GetFieldValue(fieldType)); + } - public int GetFieldValueAsInt(IFieldType fieldType) - { - return Convert.ToInt32(GetFieldValue(fieldType)); - } + public int GetFieldValueAsInt(IFieldType fieldType) + { + return Convert.ToInt32(GetFieldValue(fieldType)); + } - public decimal GetFieldValueAsDecimal(IFieldType fieldType) - { - return Convert.ToDecimal(GetFieldValue(fieldType)); - } + public decimal GetFieldValueAsDecimal(IFieldType fieldType) + { + return Convert.ToDecimal(GetFieldValue(fieldType)); + } - public double GetFieldValueAsDouble(IFieldType fieldType) - { - return Convert.ToDouble(GetFieldValue(fieldType)); - } + public double GetFieldValueAsDouble(IFieldType fieldType) + { + return Convert.ToDouble(GetFieldValue(fieldType)); + } - public float GetFieldValueAsFloat(IFieldType fieldType) - { - return Convert.ToSingle(GetFieldValue(fieldType)); - } + public float GetFieldValueAsFloat(IFieldType fieldType) + { + return Convert.ToSingle(GetFieldValue(fieldType)); + } - public bool GetFieldValueAsBool(IFieldType fieldType) - { - return Convert.ToBoolean(GetFieldValue(fieldType)); - } + public bool GetFieldValueAsBool(IFieldType fieldType) + { + return Convert.ToBoolean(GetFieldValue(fieldType)); + } - public Color GetFieldValueAsColor(IFieldType fieldType, Color defaultColor = default) - { - return (Color)(GetFieldValue(fieldType) ?? defaultColor); - } + public Color GetFieldValueAsColor(IFieldType fieldType, Color defaultColor = default) + { + return (Color) (GetFieldValue(fieldType) ?? defaultColor); + } public bool HasField(IFieldType fieldType) - { - return _fieldsByType.ContainsKey(fieldType); - } + { + return _fieldsByType.ContainsKey(fieldType); + } - public bool HasFieldValue(IFieldType fieldType) - { - return HasField(fieldType) && _fieldsByType[fieldType].HasValue; - } + public bool HasFieldValue(IFieldType fieldType) + { + return HasField(fieldType) && _fieldsByType[fieldType].HasValue; + } - public void SetFieldValue(IFieldType fieldType, object value) - { - try - { - _fieldsByType[fieldType].Value = value; - } - catch (KeyNotFoundException e) - { - throw new ArgumentException("Field '" + fieldType + "' does not exist in " + GetType(), e); - } - } + public void SetFieldValue(IFieldType fieldType, object value) + { + try + { + _fieldsByType[fieldType].Value = value; + } + catch (KeyNotFoundException e) + { + throw new ArgumentException("Field '" + fieldType + "' does not exist in " + GetType(), e); + } + } - protected void OnFieldChanged(object sender, FieldChangedEventArgs e) - { - _fieldChanged?.Invoke(sender, e); - } - } -} + protected void OnFieldChanged(object sender, FieldChangedEventArgs e) + { + _fieldChanged?.Invoke(sender, e); + } + } +} \ No newline at end of file diff --git a/src/Greenshot/Drawing/Fields/AbstractFieldHolderWithChildren.cs b/src/Greenshot/Drawing/Fields/AbstractFieldHolderWithChildren.cs index bb6ae068c..979a80168 100644 --- a/src/Greenshot/Drawing/Fields/AbstractFieldHolderWithChildren.cs +++ b/src/Greenshot/Drawing/Fields/AbstractFieldHolderWithChildren.cs @@ -26,121 +26,124 @@ using System.Runtime.Serialization; namespace Greenshot.Drawing.Fields { - /// - /// Basic IFieldHolderWithChildren implementation. Similar to IFieldHolder, - /// but has a List of IFieldHolder for children. - /// Field values are passed to and from children as well. - /// - [Serializable] - public abstract class AbstractFieldHolderWithChildren : AbstractFieldHolder - { - [NonSerialized] - private readonly FieldChangedEventHandler _fieldChangedEventHandler; + /// + /// Basic IFieldHolderWithChildren implementation. Similar to IFieldHolder, + /// but has a List of IFieldHolder for children. + /// Field values are passed to and from children as well. + /// + [Serializable] + public abstract class AbstractFieldHolderWithChildren : AbstractFieldHolder + { + [NonSerialized] private readonly FieldChangedEventHandler _fieldChangedEventHandler; - [NonSerialized] - private EventHandler childrenChanged; - public event EventHandler ChildrenChanged - { - add { childrenChanged += value; } - remove { childrenChanged -= value; } - } + [NonSerialized] private EventHandler childrenChanged; - public IList Children = new List(); + public event EventHandler ChildrenChanged + { + add { childrenChanged += value; } + remove { childrenChanged -= value; } + } - public AbstractFieldHolderWithChildren() - { - _fieldChangedEventHandler = OnFieldChanged; - } + public IList Children = new List(); - [OnDeserialized()] - private void OnDeserialized(StreamingContext context) - { - // listen to changing properties - foreach (IFieldHolder fieldHolder in Children) - { - fieldHolder.FieldChanged += _fieldChangedEventHandler; - } - childrenChanged?.Invoke(this, EventArgs.Empty); - } + public AbstractFieldHolderWithChildren() + { + _fieldChangedEventHandler = OnFieldChanged; + } - public void AddChild(IFieldHolder fieldHolder) - { - Children.Add(fieldHolder); - fieldHolder.FieldChanged += _fieldChangedEventHandler; - childrenChanged?.Invoke(this, EventArgs.Empty); - } + [OnDeserialized()] + private void OnDeserialized(StreamingContext context) + { + // listen to changing properties + foreach (IFieldHolder fieldHolder in Children) + { + fieldHolder.FieldChanged += _fieldChangedEventHandler; + } - public void RemoveChild(IFieldHolder fieldHolder) - { - Children.Remove(fieldHolder); - fieldHolder.FieldChanged -= _fieldChangedEventHandler; - childrenChanged?.Invoke(this, EventArgs.Empty); - } + childrenChanged?.Invoke(this, EventArgs.Empty); + } - public new IList GetFields() - { - var ret = new List(); - ret.AddRange(base.GetFields()); - foreach (IFieldHolder fh in Children) - { - ret.AddRange(fh.GetFields()); - } - return ret; - } + public void AddChild(IFieldHolder fieldHolder) + { + Children.Add(fieldHolder); + fieldHolder.FieldChanged += _fieldChangedEventHandler; + childrenChanged?.Invoke(this, EventArgs.Empty); + } - public new IField GetField(IFieldType fieldType) - { - IField ret = null; - if (base.HasField(fieldType)) - { - ret = base.GetField(fieldType); - } - else - { - foreach (IFieldHolder fh in Children) - { - if (fh.HasField(fieldType)) - { - ret = fh.GetField(fieldType); - break; - } - } - } - if (ret == null) - { - throw new ArgumentException("Field '" + fieldType + "' does not exist in " + GetType()); - } - return ret; - } + public void RemoveChild(IFieldHolder fieldHolder) + { + Children.Remove(fieldHolder); + fieldHolder.FieldChanged -= _fieldChangedEventHandler; + childrenChanged?.Invoke(this, EventArgs.Empty); + } - public new bool HasField(IFieldType fieldType) - { - bool ret = base.HasField(fieldType); - if (!ret) - { - foreach (IFieldHolder fh in Children) - { - if (fh.HasField(fieldType)) - { - ret = true; - break; - } - } - } - return ret; - } + public new IList GetFields() + { + var ret = new List(); + ret.AddRange(base.GetFields()); + foreach (IFieldHolder fh in Children) + { + ret.AddRange(fh.GetFields()); + } - public new bool HasFieldValue(IFieldType fieldType) - { - IField f = GetField(fieldType); - return f != null && f.HasValue; - } + return ret; + } - public new void SetFieldValue(IFieldType fieldType, object value) - { - IField f = GetField(fieldType); - if (f != null) f.Value = value; - } + public new IField GetField(IFieldType fieldType) + { + IField ret = null; + if (base.HasField(fieldType)) + { + ret = base.GetField(fieldType); + } + else + { + foreach (IFieldHolder fh in Children) + { + if (fh.HasField(fieldType)) + { + ret = fh.GetField(fieldType); + break; + } + } + } - } -} + if (ret == null) + { + throw new ArgumentException("Field '" + fieldType + "' does not exist in " + GetType()); + } + + return ret; + } + + public new bool HasField(IFieldType fieldType) + { + bool ret = base.HasField(fieldType); + if (!ret) + { + foreach (IFieldHolder fh in Children) + { + if (fh.HasField(fieldType)) + { + ret = true; + break; + } + } + } + + return ret; + } + + public new bool HasFieldValue(IFieldType fieldType) + { + IField f = GetField(fieldType); + return f != null && f.HasValue; + } + + public new void SetFieldValue(IFieldType fieldType, object value) + { + IField f = GetField(fieldType); + if (f != null) f.Value = value; + } + } +} \ No newline at end of file diff --git a/src/Greenshot/Drawing/Fields/Binding/AbstractBindingConverter.cs b/src/Greenshot/Drawing/Fields/Binding/AbstractBindingConverter.cs index a1ffd80c1..9870db505 100644 --- a/src/Greenshot/Drawing/Fields/Binding/AbstractBindingConverter.cs +++ b/src/Greenshot/Drawing/Fields/Binding/AbstractBindingConverter.cs @@ -18,29 +18,37 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ + using System; -namespace Greenshot.Drawing.Fields.Binding { - /// - /// Basic IBindingConverter implementation - /// - public abstract class AbstractBindingConverter : IBindingConverter - { - public object convert(object o) { - if(o == null) { - return null; - } - if(o is T1) { - return convert((T1)o); - } - if(o is T2) { - return convert((T2)o); - } - throw new ArgumentException("Cannot handle argument of type "+o.GetType()); - } +namespace Greenshot.Drawing.Fields.Binding +{ + /// + /// Basic IBindingConverter implementation + /// + public abstract class AbstractBindingConverter : IBindingConverter + { + public object convert(object o) + { + if (o == null) + { + return null; + } - protected abstract T2 convert(T1 o); - protected abstract T1 convert(T2 o); - - } -} + if (o is T1) + { + return convert((T1) o); + } + + if (o is T2) + { + return convert((T2) o); + } + + throw new ArgumentException("Cannot handle argument of type " + o.GetType()); + } + + protected abstract T2 convert(T1 o); + protected abstract T1 convert(T2 o); + } +} \ No newline at end of file diff --git a/src/Greenshot/Drawing/Fields/Binding/BidirectionalBinding.cs b/src/Greenshot/Drawing/Fields/Binding/BidirectionalBinding.cs index 5a9652d5c..d9a69140d 100644 --- a/src/Greenshot/Drawing/Fields/Binding/BidirectionalBinding.cs +++ b/src/Greenshot/Drawing/Fields/Binding/BidirectionalBinding.cs @@ -18,141 +18,167 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ + using System; using System.ComponentModel; using System.Reflection; -namespace Greenshot.Drawing.Fields.Binding { - /// - /// Bidirectional binding of properties of two INotifyPropertyChanged instances. - /// This implementation synchronizes null values, too. If you do not want this - /// behavior (e.g. when binding to a - /// - public class BidirectionalBinding { - private readonly INotifyPropertyChanged _controlObject; - private readonly INotifyPropertyChanged _fieldObject; - private readonly string _controlPropertyName; - private readonly string _fieldPropertyName; - private bool _updatingControl; - private bool _updatingField; - private IBindingConverter _converter; - private readonly IBindingValidator _validator; - - /// - /// Whether or not null values are passed on to the other object. - /// - protected bool AllowSynchronizeNull = true; +namespace Greenshot.Drawing.Fields.Binding +{ + /// + /// Bidirectional binding of properties of two INotifyPropertyChanged instances. + /// This implementation synchronizes null values, too. If you do not want this + /// behavior (e.g. when binding to a + /// + public class BidirectionalBinding + { + private readonly INotifyPropertyChanged _controlObject; + private readonly INotifyPropertyChanged _fieldObject; + private readonly string _controlPropertyName; + private readonly string _fieldPropertyName; + private bool _updatingControl; + private bool _updatingField; + private IBindingConverter _converter; + private readonly IBindingValidator _validator; - /// - /// Bind properties of two objects bidirectionally - /// - /// Object containing 1st property to bind - /// Property of 1st object to bind - /// Object containing 2nd property to bind - /// Property of 2nd object to bind - public BidirectionalBinding(INotifyPropertyChanged controlObject, string controlPropertyName, INotifyPropertyChanged fieldObject, string fieldPropertyName) { - _controlObject = controlObject; - _fieldObject = fieldObject; - _controlPropertyName = controlPropertyName; - _fieldPropertyName = fieldPropertyName; - - _controlObject.PropertyChanged += ControlPropertyChanged; - _fieldObject.PropertyChanged += FieldPropertyChanged; - } - - /// - /// Bind properties of two objects bidirectionally, converting the values using a converter - /// - /// Object containing 1st property to bind - /// Property of 1st object to bind - /// Object containing 2nd property to bind - /// Property of 2nd object to bind - /// taking care of converting the synchronized value to the correct target format and back - public BidirectionalBinding(INotifyPropertyChanged controlObject, string controlPropertyName, INotifyPropertyChanged fieldObject, string fieldPropertyName, IBindingConverter converter) : this(controlObject, controlPropertyName, fieldObject, fieldPropertyName) { - _converter = converter; - } - - /// - /// Bind properties of two objects bidirectionally, converting the values using a converter. - /// Synchronization can be intercepted by adding a validator. - /// - /// Object containing 1st property to bind - /// Property of 1st object to bind - /// Object containing 2nd property to bind - /// Property of 2nd object to bind - /// validator to intercept synchronization if the value does not match certain criteria - public BidirectionalBinding(INotifyPropertyChanged controlObject, string controlPropertyName, INotifyPropertyChanged fieldObject, string fieldPropertyName, IBindingValidator validator) : this(controlObject, controlPropertyName, fieldObject, fieldPropertyName) { - _validator = validator; - } - - /// - /// Bind properties of two objects bidirectionally, converting the values using a converter. - /// Synchronization can be intercepted by adding a validator. - /// - /// Object containing 1st property to bind - /// Property of 1st object to bind - /// Object containing 2nd property to bind - /// Property of 2nd object to bind - /// taking care of converting the synchronized value to the correct target format and back - /// validator to intercept synchronization if the value does not match certain criteria - public BidirectionalBinding(INotifyPropertyChanged controlObject, string controlPropertyName, INotifyPropertyChanged fieldObject, string fieldPropertyName, IBindingConverter converter, IBindingValidator validator) : this(controlObject, controlPropertyName, fieldObject, fieldPropertyName, converter) { - _validator = validator; - } - - public void ControlPropertyChanged(object sender, PropertyChangedEventArgs e) { - if (!_updatingControl && e.PropertyName.Equals(_controlPropertyName)) { - _updatingField = true; - Synchronize(_controlObject, _controlPropertyName, _fieldObject, _fieldPropertyName); - _updatingField = false; - } - } - - public void FieldPropertyChanged(object sender, PropertyChangedEventArgs e) { - if (!_updatingField && e.PropertyName.Equals(_fieldPropertyName)) { - _updatingControl = true; - Synchronize(_fieldObject, _fieldPropertyName, _controlObject, _controlPropertyName); - _updatingControl = false; - } - } - - private void Synchronize(INotifyPropertyChanged sourceObject, string sourceProperty, INotifyPropertyChanged targetObject, string targetProperty) { - PropertyInfo targetPropertyInfo = ResolvePropertyInfo(targetObject, targetProperty); - PropertyInfo sourcePropertyInfo = ResolvePropertyInfo(sourceObject, sourceProperty); - - if (sourcePropertyInfo != null && targetPropertyInfo != null && targetPropertyInfo.CanWrite) { - object bValue = sourcePropertyInfo.GetValue(sourceObject, null); - if (_converter != null && bValue != null) { - bValue = _converter.convert(bValue); - } - try { - if (_validator == null || _validator.validate(bValue)) { - targetPropertyInfo.SetValue(targetObject, bValue, null); - } - } catch (Exception e) { - throw new MemberAccessException("Could not set property '"+targetProperty+"' to '"+bValue+"' ["+(bValue?.GetType().Name ?? string.Empty)+"] on "+targetObject+". Probably other type than expected, IBindingCoverter to the rescue.", e); - } - - } - } - - private static PropertyInfo ResolvePropertyInfo(object obj, string property) { - PropertyInfo ret = null; - string[] properties = property.Split(".".ToCharArray()); - for(int i=0; i + /// Whether or not null values are passed on to the other object. + /// + protected bool AllowSynchronizeNull = true; + + /// + /// Bind properties of two objects bidirectionally + /// + /// Object containing 1st property to bind + /// Property of 1st object to bind + /// Object containing 2nd property to bind + /// Property of 2nd object to bind + public BidirectionalBinding(INotifyPropertyChanged controlObject, string controlPropertyName, INotifyPropertyChanged fieldObject, string fieldPropertyName) + { + _controlObject = controlObject; + _fieldObject = fieldObject; + _controlPropertyName = controlPropertyName; + _fieldPropertyName = fieldPropertyName; + + _controlObject.PropertyChanged += ControlPropertyChanged; + _fieldObject.PropertyChanged += FieldPropertyChanged; + } + + /// + /// Bind properties of two objects bidirectionally, converting the values using a converter + /// + /// Object containing 1st property to bind + /// Property of 1st object to bind + /// Object containing 2nd property to bind + /// Property of 2nd object to bind + /// taking care of converting the synchronized value to the correct target format and back + public BidirectionalBinding(INotifyPropertyChanged controlObject, string controlPropertyName, INotifyPropertyChanged fieldObject, string fieldPropertyName, + IBindingConverter converter) : this(controlObject, controlPropertyName, fieldObject, fieldPropertyName) + { + _converter = converter; + } + + /// + /// Bind properties of two objects bidirectionally, converting the values using a converter. + /// Synchronization can be intercepted by adding a validator. + /// + /// Object containing 1st property to bind + /// Property of 1st object to bind + /// Object containing 2nd property to bind + /// Property of 2nd object to bind + /// validator to intercept synchronization if the value does not match certain criteria + public BidirectionalBinding(INotifyPropertyChanged controlObject, string controlPropertyName, INotifyPropertyChanged fieldObject, string fieldPropertyName, + IBindingValidator validator) : this(controlObject, controlPropertyName, fieldObject, fieldPropertyName) + { + _validator = validator; + } + + /// + /// Bind properties of two objects bidirectionally, converting the values using a converter. + /// Synchronization can be intercepted by adding a validator. + /// + /// Object containing 1st property to bind + /// Property of 1st object to bind + /// Object containing 2nd property to bind + /// Property of 2nd object to bind + /// taking care of converting the synchronized value to the correct target format and back + /// validator to intercept synchronization if the value does not match certain criteria + public BidirectionalBinding(INotifyPropertyChanged controlObject, string controlPropertyName, INotifyPropertyChanged fieldObject, string fieldPropertyName, + IBindingConverter converter, IBindingValidator validator) : this(controlObject, controlPropertyName, fieldObject, fieldPropertyName, converter) + { + _validator = validator; + } + + public void ControlPropertyChanged(object sender, PropertyChangedEventArgs e) + { + if (!_updatingControl && e.PropertyName.Equals(_controlPropertyName)) + { + _updatingField = true; + Synchronize(_controlObject, _controlPropertyName, _fieldObject, _fieldPropertyName); + _updatingField = false; + } + } + + public void FieldPropertyChanged(object sender, PropertyChangedEventArgs e) + { + if (!_updatingField && e.PropertyName.Equals(_fieldPropertyName)) + { + _updatingControl = true; + Synchronize(_fieldObject, _fieldPropertyName, _controlObject, _controlPropertyName); + _updatingControl = false; + } + } + + private void Synchronize(INotifyPropertyChanged sourceObject, string sourceProperty, INotifyPropertyChanged targetObject, string targetProperty) + { + PropertyInfo targetPropertyInfo = ResolvePropertyInfo(targetObject, targetProperty); + PropertyInfo sourcePropertyInfo = ResolvePropertyInfo(sourceObject, sourceProperty); + + if (sourcePropertyInfo != null && targetPropertyInfo != null && targetPropertyInfo.CanWrite) + { + object bValue = sourcePropertyInfo.GetValue(sourceObject, null); + if (_converter != null && bValue != null) + { + bValue = _converter.convert(bValue); + } + + try + { + if (_validator == null || _validator.validate(bValue)) + { + targetPropertyInfo.SetValue(targetObject, bValue, null); + } + } + catch (Exception e) + { + throw new MemberAccessException( + "Could not set property '" + targetProperty + "' to '" + bValue + "' [" + (bValue?.GetType().Name ?? string.Empty) + "] on " + targetObject + + ". Probably other type than expected, IBindingCoverter to the rescue.", e); + } + } + } + + private static PropertyInfo ResolvePropertyInfo(object obj, string property) + { + PropertyInfo ret = null; + string[] properties = property.Split(".".ToCharArray()); + for (int i = 0; i < properties.Length; i++) + { + string prop = properties[i]; + ret = obj.GetType().GetProperty(prop); + if (ret != null && ret.CanRead && i < prop.Length - 1) + { + obj = ret.GetValue(obj, null); + } + } + + return ret; + } + + public IBindingConverter Converter + { + get { return _converter; } + set { _converter = value; } + } + } +} \ No newline at end of file diff --git a/src/Greenshot/Drawing/Fields/Binding/DecimalDoublePercentageConverter.cs b/src/Greenshot/Drawing/Fields/Binding/DecimalDoublePercentageConverter.cs index 0ac7d02bb..b7e1849f0 100644 --- a/src/Greenshot/Drawing/Fields/Binding/DecimalDoublePercentageConverter.cs +++ b/src/Greenshot/Drawing/Fields/Binding/DecimalDoublePercentageConverter.cs @@ -18,30 +18,35 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ + using System; -namespace Greenshot.Drawing.Fields.Binding { - /// - /// Converts decimal to double (%) and vice versa, e.g. 95f to 0.95d - /// - public class DecimalDoublePercentageConverter : AbstractBindingConverter - { - private static DecimalDoublePercentageConverter _uniqueInstance; - - private DecimalDoublePercentageConverter() {} - - protected override decimal convert(double o) { - return Convert.ToDecimal(o)*100; - } - - protected override double convert(decimal o) { - return Convert.ToDouble(o)/100; - } - - public static DecimalDoublePercentageConverter GetInstance() +namespace Greenshot.Drawing.Fields.Binding +{ + /// + /// Converts decimal to double (%) and vice versa, e.g. 95f to 0.95d + /// + public class DecimalDoublePercentageConverter : AbstractBindingConverter + { + private static DecimalDoublePercentageConverter _uniqueInstance; + + private DecimalDoublePercentageConverter() + { + } + + protected override decimal convert(double o) + { + return Convert.ToDecimal(o) * 100; + } + + protected override double convert(decimal o) + { + return Convert.ToDouble(o) / 100; + } + + public static DecimalDoublePercentageConverter GetInstance() { return _uniqueInstance ??= new DecimalDoublePercentageConverter(); } - - } -} + } +} \ No newline at end of file diff --git a/src/Greenshot/Drawing/Fields/Binding/DecimalFloatConverter.cs b/src/Greenshot/Drawing/Fields/Binding/DecimalFloatConverter.cs index 528af1c2e..0c2cbe5e5 100644 --- a/src/Greenshot/Drawing/Fields/Binding/DecimalFloatConverter.cs +++ b/src/Greenshot/Drawing/Fields/Binding/DecimalFloatConverter.cs @@ -18,30 +18,35 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ + using System; -namespace Greenshot.Drawing.Fields.Binding { - /// - /// Converts decimal to float and vice versa. - /// - public class DecimalFloatConverter : AbstractBindingConverter - { - private static DecimalFloatConverter _uniqueInstance; - - private DecimalFloatConverter() {} - - protected override decimal convert(float o) { - return Convert.ToDecimal(o); - } - - protected override float convert(decimal o) { - return Convert.ToSingle(o); - } - - public static DecimalFloatConverter GetInstance() - { - return _uniqueInstance ??= new DecimalFloatConverter(); - } - - } -} +namespace Greenshot.Drawing.Fields.Binding +{ + /// + /// Converts decimal to float and vice versa. + /// + public class DecimalFloatConverter : AbstractBindingConverter + { + private static DecimalFloatConverter _uniqueInstance; + + private DecimalFloatConverter() + { + } + + protected override decimal convert(float o) + { + return Convert.ToDecimal(o); + } + + protected override float convert(decimal o) + { + return Convert.ToSingle(o); + } + + public static DecimalFloatConverter GetInstance() + { + return _uniqueInstance ??= new DecimalFloatConverter(); + } + } +} \ No newline at end of file diff --git a/src/Greenshot/Drawing/Fields/Binding/DecimalIntConverter.cs b/src/Greenshot/Drawing/Fields/Binding/DecimalIntConverter.cs index 6a8ff7e84..8e1b4e42f 100644 --- a/src/Greenshot/Drawing/Fields/Binding/DecimalIntConverter.cs +++ b/src/Greenshot/Drawing/Fields/Binding/DecimalIntConverter.cs @@ -18,30 +18,35 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ + using System; -namespace Greenshot.Drawing.Fields.Binding { - /// - /// Converts decimal to int and vice versa. - /// - public class DecimalIntConverter : AbstractBindingConverter - { - private static DecimalIntConverter _uniqueInstance; - - private DecimalIntConverter() {} - - protected override decimal convert(int o) { - return Convert.ToDecimal(o); - } - - protected override int convert(decimal o) { - return Convert.ToInt32(o); - } - - public static DecimalIntConverter GetInstance() - { - return _uniqueInstance ??= new DecimalIntConverter(); - } - - } -} +namespace Greenshot.Drawing.Fields.Binding +{ + /// + /// Converts decimal to int and vice versa. + /// + public class DecimalIntConverter : AbstractBindingConverter + { + private static DecimalIntConverter _uniqueInstance; + + private DecimalIntConverter() + { + } + + protected override decimal convert(int o) + { + return Convert.ToDecimal(o); + } + + protected override int convert(decimal o) + { + return Convert.ToInt32(o); + } + + public static DecimalIntConverter GetInstance() + { + return _uniqueInstance ??= new DecimalIntConverter(); + } + } +} \ No newline at end of file diff --git a/src/Greenshot/Drawing/Fields/Binding/IBindingConverter.cs b/src/Greenshot/Drawing/Fields/Binding/IBindingConverter.cs index cdb573470..9c45dd044 100644 --- a/src/Greenshot/Drawing/Fields/Binding/IBindingConverter.cs +++ b/src/Greenshot/Drawing/Fields/Binding/IBindingConverter.cs @@ -19,15 +19,15 @@ * along with this program. If not, see . */ -namespace Greenshot.Drawing.Fields.Binding { - - /// - /// Interface for a bidirectional converter, for use with BidirectionalBinding. - /// convert(object) implementation must deal with both directions. - /// see DecimalIntConverter - /// - public interface IBindingConverter { - object convert(object o); - } - -} +namespace Greenshot.Drawing.Fields.Binding +{ + /// + /// Interface for a bidirectional converter, for use with BidirectionalBinding. + /// convert(object) implementation must deal with both directions. + /// see DecimalIntConverter + /// + public interface IBindingConverter + { + object convert(object o); + } +} \ No newline at end of file diff --git a/src/Greenshot/Drawing/Fields/Binding/IBindingValidator.cs b/src/Greenshot/Drawing/Fields/Binding/IBindingValidator.cs index d21b575a4..6406e80ab 100644 --- a/src/Greenshot/Drawing/Fields/Binding/IBindingValidator.cs +++ b/src/Greenshot/Drawing/Fields/Binding/IBindingValidator.cs @@ -19,16 +19,16 @@ * along with this program. If not, see . */ -namespace Greenshot.Drawing.Fields.Binding { - - /// - /// Interface for a bidirectional validator, for use with BidirectionalBinding. - /// Useful if you do not want to synchronize values which would be illegal on - /// one of the bound objects (e.g. null value on some form components) - /// see NotNullValidator - /// - public interface IBindingValidator { - bool validate(object o); - } - -} +namespace Greenshot.Drawing.Fields.Binding +{ + /// + /// Interface for a bidirectional validator, for use with BidirectionalBinding. + /// Useful if you do not want to synchronize values which would be illegal on + /// one of the bound objects (e.g. null value on some form components) + /// see NotNullValidator + /// + public interface IBindingValidator + { + bool validate(object o); + } +} \ No newline at end of file diff --git a/src/Greenshot/Drawing/Fields/Binding/NotNullValidator.cs b/src/Greenshot/Drawing/Fields/Binding/NotNullValidator.cs index 5d15b6699..e2089ab05 100644 --- a/src/Greenshot/Drawing/Fields/Binding/NotNullValidator.cs +++ b/src/Greenshot/Drawing/Fields/Binding/NotNullValidator.cs @@ -19,23 +19,27 @@ * along with this program. If not, see . */ -namespace Greenshot.Drawing.Fields.Binding { - /// - /// Validates a value not to be null. - /// - public class NotNullValidator : IBindingValidator { - private static NotNullValidator _uniqueInstance; - - private NotNullValidator() { - } - - public bool validate(object o) { - return o != null; - } - - public static NotNullValidator GetInstance() +namespace Greenshot.Drawing.Fields.Binding +{ + /// + /// Validates a value not to be null. + /// + public class NotNullValidator : IBindingValidator + { + private static NotNullValidator _uniqueInstance; + + private NotNullValidator() + { + } + + public bool validate(object o) + { + return o != null; + } + + public static NotNullValidator GetInstance() { return _uniqueInstance ??= new NotNullValidator(); } - } -} + } +} \ No newline at end of file diff --git a/src/Greenshot/Drawing/Fields/Field.cs b/src/Greenshot/Drawing/Fields/Field.cs index f08033fd0..e58de2122 100644 --- a/src/Greenshot/Drawing/Fields/Field.cs +++ b/src/Greenshot/Drawing/Fields/Field.cs @@ -25,106 +25,104 @@ using System.ComponentModel; namespace Greenshot.Drawing.Fields { - /// - /// Represents a single field of a drawable element, i.e. - /// line thickness of a rectangle. - /// - [Serializable] - public class Field : IField - { - [field: NonSerialized] - public event PropertyChangedEventHandler PropertyChanged; + /// + /// Represents a single field of a drawable element, i.e. + /// line thickness of a rectangle. + /// + [Serializable] + public class Field : IField + { + [field: NonSerialized] public event PropertyChangedEventHandler PropertyChanged; - private object _myValue; - public object Value - { - get - { - return _myValue; - } - set - { - if (!Equals(_myValue, value)) - { - _myValue = value; - PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Value")); - } - } - } - public IFieldType FieldType - { - get; - set; - } - public string Scope - { - get; - set; - } + private object _myValue; - /// - /// Constructs a new Field instance, usually you should be using FieldFactory - /// to create Fields. - /// - /// FieldType of the Field to be created - /// The scope to which the value of this Field is relevant. - /// Depending on the scope the Field's value may be shared for other elements - /// containing the same FieldType for defaulting to the last used value. - /// When scope is set to a Type (e.g. typeof(RectangleContainer)), its value - /// should not be reused for FieldHolders of another Type (e.g. typeof(EllipseContainer)) - /// - public Field(IFieldType fieldType, Type scope) - { - FieldType = fieldType; - Scope = scope.Name; - } - public Field(IFieldType fieldType, string scope) - { - FieldType = fieldType; - Scope = scope; - } - public Field(IFieldType fieldType) - { - FieldType = fieldType; - } - /// - /// Returns true if this field holds a value other than null. - /// - public bool HasValue => Value != null; + public object Value + { + get { return _myValue; } + set + { + if (!Equals(_myValue, value)) + { + _myValue = value; + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Value")); + } + } + } - /// - /// Creates a flat clone of this Field. The fields value itself is not cloned. - /// - /// - public Field Clone() - { - return new Field(FieldType, Scope) {Value = Value}; - } + public IFieldType FieldType { get; set; } + public string Scope { get; set; } - public override int GetHashCode() - { - int hashCode = 0; - unchecked - { - hashCode += 1000000009 * FieldType.GetHashCode(); - if (Scope != null) - hashCode += 1000000021 * Scope.GetHashCode(); - } - return hashCode; - } + /// + /// Constructs a new Field instance, usually you should be using FieldFactory + /// to create Fields. + /// + /// FieldType of the Field to be created + /// The scope to which the value of this Field is relevant. + /// Depending on the scope the Field's value may be shared for other elements + /// containing the same FieldType for defaulting to the last used value. + /// When scope is set to a Type (e.g. typeof(RectangleContainer)), its value + /// should not be reused for FieldHolders of another Type (e.g. typeof(EllipseContainer)) + /// + public Field(IFieldType fieldType, Type scope) + { + FieldType = fieldType; + Scope = scope.Name; + } - public override bool Equals(object obj) - { + public Field(IFieldType fieldType, string scope) + { + FieldType = fieldType; + Scope = scope; + } + + public Field(IFieldType fieldType) + { + FieldType = fieldType; + } + + /// + /// Returns true if this field holds a value other than null. + /// + public bool HasValue => Value != null; + + /// + /// Creates a flat clone of this Field. The fields value itself is not cloned. + /// + /// + public Field Clone() + { + return new Field(FieldType, Scope) + { + Value = Value + }; + } + + public override int GetHashCode() + { + int hashCode = 0; + unchecked + { + hashCode += 1000000009 * FieldType.GetHashCode(); + if (Scope != null) + hashCode += 1000000021 * Scope.GetHashCode(); + } + + return hashCode; + } + + public override bool Equals(object obj) + { if (!(obj is Field other)) - { - return false; - } - return FieldType == other.FieldType && Equals(Scope, other.Scope); - } + { + return false; + } - public override string ToString() - { - return string.Format("[Field FieldType={1} Value={0} Scope={2}]", _myValue, FieldType, Scope); - } - } -} + return FieldType == other.FieldType && Equals(Scope, other.Scope); + } + + public override string ToString() + { + return string.Format("[Field FieldType={1} Value={0} Scope={2}]", _myValue, FieldType, Scope); + } + } +} \ No newline at end of file diff --git a/src/Greenshot/Drawing/Fields/FieldAggregator.cs b/src/Greenshot/Drawing/Fields/FieldAggregator.cs index 6606a96b1..39f859503 100644 --- a/src/Greenshot/Drawing/Fields/FieldAggregator.cs +++ b/src/Greenshot/Drawing/Fields/FieldAggregator.cs @@ -30,146 +30,149 @@ using GreenshotPlugin.Interfaces; namespace Greenshot.Drawing.Fields { - /// - /// Represents the current set of properties for the editor. - /// When one of EditorProperties' properties is updated, the change will be promoted - /// to all bound elements. - /// * If an element is selected: - /// This class represents the element's properties - /// * I n>1 elements are selected: - /// This class represents the properties of all elements. - /// Properties that do not apply for ALL selected elements are null (or 0 respectively) - /// If the property values of the selected elements differ, the value of the last bound element wins. - /// - [Serializable] - public sealed class FieldAggregator : AbstractFieldHolder - { + /// + /// Represents the current set of properties for the editor. + /// When one of EditorProperties' properties is updated, the change will be promoted + /// to all bound elements. + /// * If an element is selected: + /// This class represents the element's properties + /// * I n>1 elements are selected: + /// This class represents the properties of all elements. + /// Properties that do not apply for ALL selected elements are null (or 0 respectively) + /// If the property values of the selected elements differ, the value of the last bound element wins. + /// + [Serializable] + public sealed class FieldAggregator : AbstractFieldHolder + { + private readonly IDrawableContainerList _boundContainers; + private bool _internalUpdateRunning; - private readonly IDrawableContainerList _boundContainers; - private bool _internalUpdateRunning; + private static readonly EditorConfiguration EditorConfig = IniConfig.GetIniSection(); - private static readonly EditorConfiguration EditorConfig = IniConfig.GetIniSection(); + public FieldAggregator(ISurface parent) + { + foreach (var fieldType in FieldType.Values) + { + var field = new Field(fieldType, GetType()); + AddField(field); + } - public FieldAggregator(ISurface parent) - { - foreach (var fieldType in FieldType.Values) - { - var field = new Field(fieldType, GetType()); - AddField(field); - } - _boundContainers = new DrawableContainerList - { - Parent = parent - }; - } + _boundContainers = new DrawableContainerList + { + Parent = parent + }; + } - public override void AddField(IField field) - { - base.AddField(field); - field.PropertyChanged += OwnPropertyChanged; - } + public override void AddField(IField field) + { + base.AddField(field); + field.PropertyChanged += OwnPropertyChanged; + } - public void BindElements(IDrawableContainerList dcs) - { - foreach (var dc in dcs) - { - BindElement(dc); - } - } + public void BindElements(IDrawableContainerList dcs) + { + foreach (var dc in dcs) + { + BindElement(dc); + } + } - public void BindElement(IDrawableContainer dc) - { + public void BindElement(IDrawableContainer dc) + { if (!(dc is DrawableContainer container) || _boundContainers.Contains(container)) - { - return; - } - _boundContainers.Add(container); - container.ChildrenChanged += delegate { - UpdateFromBoundElements(); - }; - UpdateFromBoundElements(); - } + { + return; + } - public void BindAndUpdateElement(IDrawableContainer dc) - { - UpdateElement(dc); - BindElement(dc); - } + _boundContainers.Add(container); + container.ChildrenChanged += delegate { UpdateFromBoundElements(); }; + UpdateFromBoundElements(); + } - public void UpdateElement(IDrawableContainer dc) - { + public void BindAndUpdateElement(IDrawableContainer dc) + { + UpdateElement(dc); + BindElement(dc); + } + + public void UpdateElement(IDrawableContainer dc) + { if (!(dc is DrawableContainer container)) - { - return; - } - _internalUpdateRunning = true; - foreach (var field in GetFields()) - { - if (container.HasField(field.FieldType) && field.HasValue) - { - //if(LOG.IsDebugEnabled) LOG.Debug(" "+field+ ": "+field.Value); - container.SetFieldValue(field.FieldType, field.Value); - } - } - _internalUpdateRunning = false; - } + { + return; + } - public void UnbindElement(IDrawableContainer dc) - { - if (_boundContainers.Contains(dc)) - { - _boundContainers.Remove(dc); - UpdateFromBoundElements(); - } - } + _internalUpdateRunning = true; + foreach (var field in GetFields()) + { + if (container.HasField(field.FieldType) && field.HasValue) + { + //if(LOG.IsDebugEnabled) LOG.Debug(" "+field+ ": "+field.Value); + container.SetFieldValue(field.FieldType, field.Value); + } + } - public void Clear() - { - ClearFields(); - _boundContainers.Clear(); - UpdateFromBoundElements(); - } + _internalUpdateRunning = false; + } - /// - /// sets all field values to null, however does not remove fields - /// - private void ClearFields() - { - _internalUpdateRunning = true; - foreach (var field in GetFields()) - { - field.Value = null; - } - _internalUpdateRunning = false; - } + public void UnbindElement(IDrawableContainer dc) + { + if (_boundContainers.Contains(dc)) + { + _boundContainers.Remove(dc); + UpdateFromBoundElements(); + } + } - /// - /// Updates this instance using the respective fields from the bound elements. - /// Fields that do not apply to every bound element are set to null, or 0 respectively. - /// All other fields will be set to the field value of the least bound element. - /// - private void UpdateFromBoundElements() - { - ClearFields(); - _internalUpdateRunning = true; - foreach (var field in FindCommonFields()) - { - SetFieldValue(field.FieldType, field.Value); - } - _internalUpdateRunning = false; - } + public void Clear() + { + ClearFields(); + _boundContainers.Clear(); + UpdateFromBoundElements(); + } - private IList FindCommonFields() - { - IList returnFields = null; - if (_boundContainers.Count > 0) - { - // take all fields from the least selected container... + /// + /// sets all field values to null, however does not remove fields + /// + private void ClearFields() + { + _internalUpdateRunning = true; + foreach (var field in GetFields()) + { + field.Value = null; + } + + _internalUpdateRunning = false; + } + + /// + /// Updates this instance using the respective fields from the bound elements. + /// Fields that do not apply to every bound element are set to null, or 0 respectively. + /// All other fields will be set to the field value of the least bound element. + /// + private void UpdateFromBoundElements() + { + ClearFields(); + _internalUpdateRunning = true; + foreach (var field in FindCommonFields()) + { + SetFieldValue(field.FieldType, field.Value); + } + + _internalUpdateRunning = false; + } + + private IList FindCommonFields() + { + IList returnFields = null; + if (_boundContainers.Count > 0) + { + // take all fields from the least selected container... if (_boundContainers[_boundContainers.Count - 1] is DrawableContainer leastSelectedContainer) - { - returnFields = leastSelectedContainer.GetFields(); - for (int i = 0; i < _boundContainers.Count - 1; i++) - { + { + returnFields = leastSelectedContainer.GetFields(); + for (int i = 0; i < _boundContainers.Count - 1; i++) + { if (!(_boundContainers[i] is DrawableContainer dc)) continue; IList fieldsToRemove = new List(); foreach (IField field in returnFields) @@ -180,39 +183,42 @@ namespace Greenshot.Drawing.Fields fieldsToRemove.Add(field); } } + foreach (var field in fieldsToRemove) { returnFields.Remove(field); } } - } - } - return returnFields ?? new List(); - } + } + } - public void OwnPropertyChanged(object sender, PropertyChangedEventArgs ea) - { - IField field = (IField)sender; - if (_internalUpdateRunning || field.Value == null) - { - return; - } - foreach (var drawableContainer1 in _boundContainers.ToList()) - { - var drawableContainer = (DrawableContainer) drawableContainer1; - if (!drawableContainer.HasField(field.FieldType)) - { - continue; - } - IField drawableContainerField = drawableContainer.GetField(field.FieldType); - // Notify before change, so we can e.g. invalidate the area - drawableContainer.BeforeFieldChange(drawableContainerField, field.Value); + return returnFields ?? new List(); + } - drawableContainerField.Value = field.Value; - // update last used from DC field, so that scope is honored - EditorConfig.UpdateLastFieldValue(drawableContainerField); - } - } + public void OwnPropertyChanged(object sender, PropertyChangedEventArgs ea) + { + IField field = (IField) sender; + if (_internalUpdateRunning || field.Value == null) + { + return; + } - } -} + foreach (var drawableContainer1 in _boundContainers.ToList()) + { + var drawableContainer = (DrawableContainer) drawableContainer1; + if (!drawableContainer.HasField(field.FieldType)) + { + continue; + } + + IField drawableContainerField = drawableContainer.GetField(field.FieldType); + // Notify before change, so we can e.g. invalidate the area + drawableContainer.BeforeFieldChange(drawableContainerField, field.Value); + + drawableContainerField.Value = field.Value; + // update last used from DC field, so that scope is honored + EditorConfig.UpdateLastFieldValue(drawableContainerField); + } + } + } +} \ No newline at end of file diff --git a/src/Greenshot/Drawing/Fields/FieldType.cs b/src/Greenshot/Drawing/Fields/FieldType.cs index 7531e5b27..3e6711898 100644 --- a/src/Greenshot/Drawing/Fields/FieldType.cs +++ b/src/Greenshot/Drawing/Fields/FieldType.cs @@ -18,106 +18,89 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ + using GreenshotPlugin.Interfaces.Drawing; using System; namespace Greenshot.Drawing.Fields { - /// - /// Defines all FieldTypes + their default value. - /// (The additional value is why this is not an enum) - /// - [Serializable] - public class FieldType : IFieldType - { + /// + /// Defines all FieldTypes + their default value. + /// (The additional value is why this is not an enum) + /// + [Serializable] + public class FieldType : IFieldType + { + public static readonly IFieldType ARROWHEADS = new FieldType("ARROWHEADS"); + public static readonly IFieldType BLUR_RADIUS = new FieldType("BLUR_RADIUS"); + public static readonly IFieldType BRIGHTNESS = new FieldType("BRIGHTNESS"); + public static readonly IFieldType FILL_COLOR = new FieldType("FILL_COLOR"); + public static readonly IFieldType FONT_BOLD = new FieldType("FONT_BOLD"); + public static readonly IFieldType FONT_FAMILY = new FieldType("FONT_FAMILY"); + public static readonly IFieldType FONT_ITALIC = new FieldType("FONT_ITALIC"); + public static readonly IFieldType FONT_SIZE = new FieldType("FONT_SIZE"); + public static readonly IFieldType TEXT_HORIZONTAL_ALIGNMENT = new FieldType("TEXT_HORIZONTAL_ALIGNMENT"); + public static readonly IFieldType TEXT_VERTICAL_ALIGNMENT = new FieldType("TEXT_VERTICAL_ALIGNMENT"); + public static readonly IFieldType HIGHLIGHT_COLOR = new FieldType("HIGHLIGHT_COLOR"); + public static readonly IFieldType LINE_COLOR = new FieldType("LINE_COLOR"); + public static readonly IFieldType LINE_THICKNESS = new FieldType("LINE_THICKNESS"); + public static readonly IFieldType MAGNIFICATION_FACTOR = new FieldType("MAGNIFICATION_FACTOR"); + public static readonly IFieldType PIXEL_SIZE = new FieldType("PIXEL_SIZE"); + public static readonly IFieldType PREVIEW_QUALITY = new FieldType("PREVIEW_QUALITY"); + public static readonly IFieldType SHADOW = new FieldType("SHADOW"); + public static readonly IFieldType PREPARED_FILTER_OBFUSCATE = new FieldType("PREPARED_FILTER_OBFUSCATE"); + public static readonly IFieldType PREPARED_FILTER_HIGHLIGHT = new FieldType("PREPARED_FILTER_HIGHLIGHT"); + public static readonly IFieldType FLAGS = new FieldType("FLAGS"); - public static readonly IFieldType ARROWHEADS = new FieldType("ARROWHEADS"); - public static readonly IFieldType BLUR_RADIUS = new FieldType("BLUR_RADIUS"); - public static readonly IFieldType BRIGHTNESS = new FieldType("BRIGHTNESS"); - public static readonly IFieldType FILL_COLOR = new FieldType("FILL_COLOR"); - public static readonly IFieldType FONT_BOLD = new FieldType("FONT_BOLD"); - public static readonly IFieldType FONT_FAMILY = new FieldType("FONT_FAMILY"); - public static readonly IFieldType FONT_ITALIC = new FieldType("FONT_ITALIC"); - public static readonly IFieldType FONT_SIZE = new FieldType("FONT_SIZE"); - public static readonly IFieldType TEXT_HORIZONTAL_ALIGNMENT = new FieldType("TEXT_HORIZONTAL_ALIGNMENT"); - public static readonly IFieldType TEXT_VERTICAL_ALIGNMENT = new FieldType("TEXT_VERTICAL_ALIGNMENT"); - public static readonly IFieldType HIGHLIGHT_COLOR = new FieldType("HIGHLIGHT_COLOR"); - public static readonly IFieldType LINE_COLOR = new FieldType("LINE_COLOR"); - public static readonly IFieldType LINE_THICKNESS = new FieldType("LINE_THICKNESS"); - public static readonly IFieldType MAGNIFICATION_FACTOR = new FieldType("MAGNIFICATION_FACTOR"); - public static readonly IFieldType PIXEL_SIZE = new FieldType("PIXEL_SIZE"); - public static readonly IFieldType PREVIEW_QUALITY = new FieldType("PREVIEW_QUALITY"); - public static readonly IFieldType SHADOW = new FieldType("SHADOW"); - public static readonly IFieldType PREPARED_FILTER_OBFUSCATE = new FieldType("PREPARED_FILTER_OBFUSCATE"); - public static readonly IFieldType PREPARED_FILTER_HIGHLIGHT = new FieldType("PREPARED_FILTER_HIGHLIGHT"); - public static readonly IFieldType FLAGS = new FieldType("FLAGS"); - - public static IFieldType[] Values = { - ARROWHEADS, - BLUR_RADIUS, - BRIGHTNESS, - FILL_COLOR, - FONT_BOLD, - FONT_FAMILY, - FONT_ITALIC, - FONT_SIZE, - TEXT_HORIZONTAL_ALIGNMENT, - TEXT_VERTICAL_ALIGNMENT, - HIGHLIGHT_COLOR, - LINE_COLOR, - LINE_THICKNESS, - MAGNIFICATION_FACTOR, - PIXEL_SIZE, - PREVIEW_QUALITY, - SHADOW, - PREPARED_FILTER_OBFUSCATE, - PREPARED_FILTER_HIGHLIGHT, - FLAGS - }; + public static IFieldType[] Values = + { + ARROWHEADS, BLUR_RADIUS, BRIGHTNESS, FILL_COLOR, FONT_BOLD, FONT_FAMILY, FONT_ITALIC, FONT_SIZE, TEXT_HORIZONTAL_ALIGNMENT, TEXT_VERTICAL_ALIGNMENT, HIGHLIGHT_COLOR, + LINE_COLOR, LINE_THICKNESS, MAGNIFICATION_FACTOR, PIXEL_SIZE, PREVIEW_QUALITY, SHADOW, PREPARED_FILTER_OBFUSCATE, PREPARED_FILTER_HIGHLIGHT, FLAGS + }; - public string Name - { - get; - set; - } + public string Name { get; set; } - private FieldType(string name) - { - Name = name; - } - public override string ToString() - { - return Name; - } - public override int GetHashCode() - { - int hashCode = 0; - unchecked - { - if (Name != null) - hashCode += 1000000009 * Name.GetHashCode(); - } - return hashCode; - } + private FieldType(string name) + { + Name = name; + } - public override bool Equals(object obj) - { - FieldType other = obj as FieldType; - if (other == null) - { - return false; - } - return Equals(Name, other.Name); - } + public override string ToString() + { + return Name; + } - public static bool operator ==(FieldType a, FieldType b) - { - return Equals(a, b); - } + public override int GetHashCode() + { + int hashCode = 0; + unchecked + { + if (Name != null) + hashCode += 1000000009 * Name.GetHashCode(); + } - public static bool operator !=(FieldType a, FieldType b) - { - return !Equals(a, b); - } - } -} + return hashCode; + } + + public override bool Equals(object obj) + { + FieldType other = obj as FieldType; + if (other == null) + { + return false; + } + + return Equals(Name, other.Name); + } + + public static bool operator ==(FieldType a, FieldType b) + { + return Equals(a, b); + } + + public static bool operator !=(FieldType a, FieldType b) + { + return !Equals(a, b); + } + } +} \ No newline at end of file diff --git a/src/Greenshot/Drawing/FilterContainer.cs b/src/Greenshot/Drawing/FilterContainer.cs index 631cbed33..93e3352c9 100644 --- a/src/Greenshot/Drawing/FilterContainer.cs +++ b/src/Greenshot/Drawing/FilterContainer.cs @@ -18,6 +18,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ + using System; using System.Drawing; using Greenshot.Drawing.Fields; @@ -26,54 +27,73 @@ using System.Drawing.Drawing2D; using System.Runtime.Serialization; using GreenshotPlugin.Interfaces.Drawing; -namespace Greenshot.Drawing { - /// - /// empty container for filter-only elements - /// - [Serializable] - public abstract class FilterContainer : DrawableContainer { - - public enum PreparedFilterMode {OBFUSCATE, HIGHLIGHT}; - public enum PreparedFilter {BLUR, PIXELIZE, TEXT_HIGHTLIGHT, AREA_HIGHLIGHT, GRAYSCALE, MAGNIFICATION}; +namespace Greenshot.Drawing +{ + /// + /// empty container for filter-only elements + /// + [Serializable] + public abstract class FilterContainer : DrawableContainer + { + public enum PreparedFilterMode + { + OBFUSCATE, + HIGHLIGHT + }; - public FilterContainer(Surface parent) : base(parent) { - Init(); - } + public enum PreparedFilter + { + BLUR, + PIXELIZE, + TEXT_HIGHTLIGHT, + AREA_HIGHLIGHT, + GRAYSCALE, + MAGNIFICATION + }; - protected override void OnDeserialized(StreamingContext streamingContext) - { - base.OnDeserialized(streamingContext); - Init(); - } + public FilterContainer(Surface parent) : base(parent) + { + Init(); + } - private void Init() - { - CreateDefaultAdorners(); - } + protected override void OnDeserialized(StreamingContext streamingContext) + { + base.OnDeserialized(streamingContext); + Init(); + } - protected override void InitializeFields() { - AddField(GetType(), FieldType.LINE_THICKNESS, 0); - AddField(GetType(), FieldType.LINE_COLOR, Color.Red); - AddField(GetType(), FieldType.SHADOW, false); - } - - public override void Draw(Graphics graphics, RenderMode rm) { - int lineThickness = GetFieldValueAsInt(FieldType.LINE_THICKNESS); - Color lineColor = GetFieldValueAsColor(FieldType.LINE_COLOR); - bool shadow = GetFieldValueAsBool(FieldType.SHADOW); - bool lineVisible = lineThickness > 0 && Colors.IsVisible(lineColor); - if (lineVisible) { - graphics.SmoothingMode = SmoothingMode.HighSpeed; - graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; - graphics.CompositingQuality = CompositingQuality.HighQuality; - graphics.PixelOffsetMode = PixelOffsetMode.None; - //draw shadow first - if (shadow) { - int basealpha = 100; - int alpha = basealpha; - int steps = 5; - int currentStep = lineVisible ? 1 : 0; - while (currentStep <= steps) + private void Init() + { + CreateDefaultAdorners(); + } + + protected override void InitializeFields() + { + AddField(GetType(), FieldType.LINE_THICKNESS, 0); + AddField(GetType(), FieldType.LINE_COLOR, Color.Red); + AddField(GetType(), FieldType.SHADOW, false); + } + + public override void Draw(Graphics graphics, RenderMode rm) + { + int lineThickness = GetFieldValueAsInt(FieldType.LINE_THICKNESS); + Color lineColor = GetFieldValueAsColor(FieldType.LINE_COLOR); + bool shadow = GetFieldValueAsBool(FieldType.SHADOW); + bool lineVisible = lineThickness > 0 && Colors.IsVisible(lineColor); + if (lineVisible) + { + graphics.SmoothingMode = SmoothingMode.HighSpeed; + graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; + graphics.CompositingQuality = CompositingQuality.HighQuality; + graphics.PixelOffsetMode = PixelOffsetMode.None; + //draw shadow first + if (shadow) + { + int basealpha = 100; + int alpha = basealpha; + int steps = 5; + int currentStep = lineVisible ? 1 : 0; + while (currentStep <= steps) { using Pen shadowPen = new Pen(Color.FromArgb(alpha, 100, 100, 100), lineThickness); Rectangle shadowRect = GuiRectangle.GetGuiRectangle(Left + currentStep, Top + currentStep, Width, Height); @@ -81,14 +101,15 @@ namespace Greenshot.Drawing { currentStep++; alpha -= basealpha / steps; } - } - Rectangle rect = GuiRectangle.GetGuiRectangle(Left, Top, Width, Height); - if (lineThickness > 0) + } + + Rectangle rect = GuiRectangle.GetGuiRectangle(Left, Top, Width, Height); + if (lineThickness > 0) { using Pen pen = new Pen(lineColor, lineThickness); graphics.DrawRectangle(pen, rect); } - } - } - } -} + } + } + } +} \ No newline at end of file diff --git a/src/Greenshot/Drawing/Filters/AbstractFilter.cs b/src/Greenshot/Drawing/Filters/AbstractFilter.cs index 419038d40..1608fd866 100644 --- a/src/Greenshot/Drawing/Filters/AbstractFilter.cs +++ b/src/Greenshot/Drawing/Filters/AbstractFilter.cs @@ -18,63 +18,66 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ + using System; using System.ComponentModel; using System.Drawing; - using Greenshot.Drawing.Fields; using GreenshotPlugin.Interfaces.Drawing; -namespace Greenshot.Drawing.Filters { - /// - /// Graphical filter which can be added to DrawableContainer. - /// Subclasses should fulfill INotifyPropertyChanged contract, i.e. call - /// OnPropertyChanged whenever a public property has been changed. - /// - [Serializable] - public abstract class AbstractFilter : AbstractFieldHolder, IFilter { - - [NonSerialized] - private PropertyChangedEventHandler propertyChanged; - public event PropertyChangedEventHandler PropertyChanged { - add { propertyChanged += value; } - remove{ propertyChanged -= value; } - } - - private bool invert; - public bool Invert { - get { - return invert; - } - set { - invert = value; - OnPropertyChanged("Invert"); - } - } +namespace Greenshot.Drawing.Filters +{ + /// + /// Graphical filter which can be added to DrawableContainer. + /// Subclasses should fulfill INotifyPropertyChanged contract, i.e. call + /// OnPropertyChanged whenever a public property has been changed. + /// + [Serializable] + public abstract class AbstractFilter : AbstractFieldHolder, IFilter + { + [NonSerialized] private PropertyChangedEventHandler propertyChanged; - protected DrawableContainer parent; - public DrawableContainer Parent { - get { - return parent; - } - set { - parent = value; - } - } - - public AbstractFilter(DrawableContainer parent) { - this.parent = parent; - } - - public DrawableContainer GetParent() { - return parent; - } + public event PropertyChangedEventHandler PropertyChanged + { + add { propertyChanged += value; } + remove { propertyChanged -= value; } + } - public abstract void Apply(Graphics graphics, Bitmap applyBitmap, Rectangle rect, RenderMode renderMode); - - protected void OnPropertyChanged(string propertyName) - { - propertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); - } - } -} + private bool invert; + + public bool Invert + { + get { return invert; } + set + { + invert = value; + OnPropertyChanged("Invert"); + } + } + + protected DrawableContainer parent; + + public DrawableContainer Parent + { + get { return parent; } + set { parent = value; } + } + + public AbstractFilter(DrawableContainer parent) + { + this.parent = parent; + } + + public DrawableContainer GetParent() + { + return parent; + } + + public abstract void Apply(Graphics graphics, Bitmap applyBitmap, Rectangle rect, RenderMode renderMode); + + protected void OnPropertyChanged(string propertyName) + { + propertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); + } + } +} \ No newline at end of file diff --git a/src/Greenshot/Drawing/Filters/BlurFilter.cs b/src/Greenshot/Drawing/Filters/BlurFilter.cs index 79f617ac6..9b8c5d0f9 100644 --- a/src/Greenshot/Drawing/Filters/BlurFilter.cs +++ b/src/Greenshot/Drawing/Filters/BlurFilter.cs @@ -27,40 +27,57 @@ using GreenshotPlugin.UnmanagedHelpers; using System.Drawing.Drawing2D; using GreenshotPlugin.Interfaces.Drawing; -namespace Greenshot.Drawing.Filters { - [Serializable] - public class BlurFilter : AbstractFilter { - public double previewQuality; - public double PreviewQuality { - get { return previewQuality; } - set { previewQuality = value; OnPropertyChanged("PreviewQuality"); } - } - - public BlurFilter(DrawableContainer parent) : base(parent) { - AddField(GetType(), FieldType.BLUR_RADIUS, 3); - AddField(GetType(), FieldType.PREVIEW_QUALITY, 1.0d); - } +namespace Greenshot.Drawing.Filters +{ + [Serializable] + public class BlurFilter : AbstractFilter + { + public double previewQuality; - public override void Apply(Graphics graphics, Bitmap applyBitmap, Rectangle rect, RenderMode renderMode) { - int blurRadius = GetFieldValueAsInt(FieldType.BLUR_RADIUS); - Rectangle applyRect = ImageHelper.CreateIntersectRectangle(applyBitmap.Size, rect, Invert); - if (applyRect.Width == 0 || applyRect.Height == 0) { - return; - } - GraphicsState state = graphics.Save(); - if (Invert) { - graphics.SetClip(applyRect); - graphics.ExcludeClip(rect); - } - if (GDIplus.IsBlurPossible(blurRadius)) { - GDIplus.DrawWithBlur(graphics, applyBitmap, applyRect, null, null, blurRadius, false); - } else + public double PreviewQuality + { + get { return previewQuality; } + set + { + previewQuality = value; + OnPropertyChanged("PreviewQuality"); + } + } + + public BlurFilter(DrawableContainer parent) : base(parent) + { + AddField(GetType(), FieldType.BLUR_RADIUS, 3); + AddField(GetType(), FieldType.PREVIEW_QUALITY, 1.0d); + } + + public override void Apply(Graphics graphics, Bitmap applyBitmap, Rectangle rect, RenderMode renderMode) + { + int blurRadius = GetFieldValueAsInt(FieldType.BLUR_RADIUS); + Rectangle applyRect = ImageHelper.CreateIntersectRectangle(applyBitmap.Size, rect, Invert); + if (applyRect.Width == 0 || applyRect.Height == 0) + { + return; + } + + GraphicsState state = graphics.Save(); + if (Invert) + { + graphics.SetClip(applyRect); + graphics.ExcludeClip(rect); + } + + if (GDIplus.IsBlurPossible(blurRadius)) + { + GDIplus.DrawWithBlur(graphics, applyBitmap, applyRect, null, null, blurRadius, false); + } + else { using IFastBitmap fastBitmap = FastBitmap.CreateCloneOf(applyBitmap, applyRect); ImageHelper.ApplyBoxBlur(fastBitmap, blurRadius); fastBitmap.DrawTo(graphics, applyRect); } - graphics.Restore(state); - } - } -} + + graphics.Restore(state); + } + } +} \ No newline at end of file diff --git a/src/Greenshot/Drawing/Filters/BrightnessFilter.cs b/src/Greenshot/Drawing/Filters/BrightnessFilter.cs index a43a68f67..cba712bbe 100644 --- a/src/Greenshot/Drawing/Filters/BrightnessFilter.cs +++ b/src/Greenshot/Drawing/Filters/BrightnessFilter.cs @@ -18,6 +18,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ + using System; using System.Drawing; using Greenshot.Drawing.Fields; @@ -26,39 +27,47 @@ using System.Drawing.Imaging; using System.Drawing.Drawing2D; using GreenshotPlugin.Interfaces.Drawing; -namespace Greenshot.Drawing.Filters { - [Serializable()] - public class BrightnessFilter : AbstractFilter { - - public BrightnessFilter(DrawableContainer parent) : base(parent) { - AddField(GetType(), FieldType.BRIGHTNESS, 0.9d); - } +namespace Greenshot.Drawing.Filters +{ + [Serializable()] + public class BrightnessFilter : AbstractFilter + { + public BrightnessFilter(DrawableContainer parent) : base(parent) + { + AddField(GetType(), FieldType.BRIGHTNESS, 0.9d); + } - /// - /// Implements the Apply code for the Brightness Filet - /// - /// - /// - /// - /// - public override void Apply(Graphics graphics, Bitmap applyBitmap, Rectangle rect, RenderMode renderMode) { - Rectangle applyRect = ImageHelper.CreateIntersectRectangle(applyBitmap.Size, rect, Invert); + /// + /// Implements the Apply code for the Brightness Filet + /// + /// + /// + /// + /// + public override void Apply(Graphics graphics, Bitmap applyBitmap, Rectangle rect, RenderMode renderMode) + { + Rectangle applyRect = ImageHelper.CreateIntersectRectangle(applyBitmap.Size, rect, Invert); - if (applyRect.Width == 0 || applyRect.Height == 0) { - // nothing to do - return; - } + if (applyRect.Width == 0 || applyRect.Height == 0) + { + // nothing to do + return; + } - GraphicsState state = graphics.Save(); - if (Invert) { - graphics.SetClip(applyRect); - graphics.ExcludeClip(rect); - } - float brightness = GetFieldValueAsFloat(FieldType.BRIGHTNESS); - using (ImageAttributes ia = ImageHelper.CreateAdjustAttributes(brightness, 1f, 1f)) { - graphics.DrawImage(applyBitmap, applyRect, applyRect.X, applyRect.Y, applyRect.Width, applyRect.Height, GraphicsUnit.Pixel, ia); - } - graphics.Restore(state); - } - } -} + GraphicsState state = graphics.Save(); + if (Invert) + { + graphics.SetClip(applyRect); + graphics.ExcludeClip(rect); + } + + float brightness = GetFieldValueAsFloat(FieldType.BRIGHTNESS); + using (ImageAttributes ia = ImageHelper.CreateAdjustAttributes(brightness, 1f, 1f)) + { + graphics.DrawImage(applyBitmap, applyRect, applyRect.X, applyRect.Y, applyRect.Width, applyRect.Height, GraphicsUnit.Pixel, ia); + } + + graphics.Restore(state); + } + } +} \ No newline at end of file diff --git a/src/Greenshot/Drawing/Filters/GrayscaleFilter.cs b/src/Greenshot/Drawing/Filters/GrayscaleFilter.cs index f66b29fba..2565999a9 100644 --- a/src/Greenshot/Drawing/Filters/GrayscaleFilter.cs +++ b/src/Greenshot/Drawing/Filters/GrayscaleFilter.cs @@ -18,6 +18,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ + using System; using System.Drawing; using GreenshotPlugin.Core; @@ -25,40 +26,65 @@ using System.Drawing.Drawing2D; using System.Drawing.Imaging; using GreenshotPlugin.Interfaces.Drawing; -namespace Greenshot.Drawing.Filters { - /// - /// Description of GrayscaleFilter. - /// - [Serializable()] - public class GrayscaleFilter : AbstractFilter { - public GrayscaleFilter(DrawableContainer parent) : base(parent) { - } +namespace Greenshot.Drawing.Filters +{ + /// + /// Description of GrayscaleFilter. + /// + [Serializable()] + public class GrayscaleFilter : AbstractFilter + { + public GrayscaleFilter(DrawableContainer parent) : base(parent) + { + } - public override void Apply(Graphics graphics, Bitmap applyBitmap, Rectangle rect, RenderMode renderMode) { - Rectangle applyRect = ImageHelper.CreateIntersectRectangle(applyBitmap.Size, rect, Invert); + public override void Apply(Graphics graphics, Bitmap applyBitmap, Rectangle rect, RenderMode renderMode) + { + Rectangle applyRect = ImageHelper.CreateIntersectRectangle(applyBitmap.Size, rect, Invert); - if (applyRect.Width == 0 || applyRect.Height == 0) { - // nothing to do - return; - } - GraphicsState state = graphics.Save(); - if (Invert) { - graphics.SetClip(applyRect); - graphics.ExcludeClip(rect); - } - ColorMatrix grayscaleMatrix = new ColorMatrix(new[] { - new[] {.3f, .3f, .3f, 0, 0}, - new[] {.59f, .59f, .59f, 0, 0}, - new[] {.11f, .11f, .11f, 0, 0}, - new float[] {0, 0, 0, 1, 0}, - new float[] {0, 0, 0, 0, 1} - }); - using (ImageAttributes ia = new ImageAttributes()) { - ia.SetColorMatrix(grayscaleMatrix); - graphics.DrawImage(applyBitmap, applyRect, applyRect.X, applyRect.Y, applyRect.Width, applyRect.Height, GraphicsUnit.Pixel, ia); - } - graphics.Restore(state); + if (applyRect.Width == 0 || applyRect.Height == 0) + { + // nothing to do + return; + } - } - } -} + GraphicsState state = graphics.Save(); + if (Invert) + { + graphics.SetClip(applyRect); + graphics.ExcludeClip(rect); + } + + ColorMatrix grayscaleMatrix = new ColorMatrix(new[] + { + new[] + { + .3f, .3f, .3f, 0, 0 + }, + new[] + { + .59f, .59f, .59f, 0, 0 + }, + new[] + { + .11f, .11f, .11f, 0, 0 + }, + new float[] + { + 0, 0, 0, 1, 0 + }, + new float[] + { + 0, 0, 0, 0, 1 + } + }); + using (ImageAttributes ia = new ImageAttributes()) + { + ia.SetColorMatrix(grayscaleMatrix); + graphics.DrawImage(applyBitmap, applyRect, applyRect.X, applyRect.Y, applyRect.Width, applyRect.Height, GraphicsUnit.Pixel, ia); + } + + graphics.Restore(state); + } + } +} \ No newline at end of file diff --git a/src/Greenshot/Drawing/Filters/HighlightFilter.cs b/src/Greenshot/Drawing/Filters/HighlightFilter.cs index 38b6a8ee7..e311293c5 100644 --- a/src/Greenshot/Drawing/Filters/HighlightFilter.cs +++ b/src/Greenshot/Drawing/Filters/HighlightFilter.cs @@ -18,6 +18,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ + using System; using System.Drawing; using Greenshot.Drawing.Fields; @@ -25,44 +26,57 @@ using GreenshotPlugin.Core; using System.Drawing.Drawing2D; using GreenshotPlugin.Interfaces.Drawing; -namespace Greenshot.Drawing.Filters { - [Serializable()] - public class HighlightFilter : AbstractFilter { - public HighlightFilter(DrawableContainer parent) : base(parent) { - AddField(GetType(), FieldType.FILL_COLOR, Color.Yellow); - } +namespace Greenshot.Drawing.Filters +{ + [Serializable()] + public class HighlightFilter : AbstractFilter + { + public HighlightFilter(DrawableContainer parent) : base(parent) + { + AddField(GetType(), FieldType.FILL_COLOR, Color.Yellow); + } - /// - /// Implements the Apply code for the Brightness Filet - /// - /// - /// - /// - /// - public override void Apply(Graphics graphics, Bitmap applyBitmap, Rectangle rect, RenderMode renderMode) { - Rectangle applyRect = ImageHelper.CreateIntersectRectangle(applyBitmap.Size, rect, Invert); + /// + /// Implements the Apply code for the Brightness Filet + /// + /// + /// + /// + /// + public override void Apply(Graphics graphics, Bitmap applyBitmap, Rectangle rect, RenderMode renderMode) + { + Rectangle applyRect = ImageHelper.CreateIntersectRectangle(applyBitmap.Size, rect, Invert); - if (applyRect.Width == 0 || applyRect.Height == 0) { - // nothing to do - return; - } - GraphicsState state = graphics.Save(); - if (Invert) { - graphics.SetClip(applyRect); - graphics.ExcludeClip(rect); - } - using (IFastBitmap fastBitmap = FastBitmap.CreateCloneOf(applyBitmap, applyRect)) { - Color highlightColor = GetFieldValueAsColor(FieldType.FILL_COLOR); - for (int y = fastBitmap.Top; y < fastBitmap.Bottom; y++) { - for (int x = fastBitmap.Left; x < fastBitmap.Right; x++) { - Color color = fastBitmap.GetColorAt(x, y); - color = Color.FromArgb(color.A, Math.Min(highlightColor.R, color.R), Math.Min(highlightColor.G, color.G), Math.Min(highlightColor.B, color.B)); - fastBitmap.SetColorAt(x, y, color); - } - } - fastBitmap.DrawTo(graphics, applyRect.Location); - } - graphics.Restore(state); - } - } -} + if (applyRect.Width == 0 || applyRect.Height == 0) + { + // nothing to do + return; + } + + GraphicsState state = graphics.Save(); + if (Invert) + { + graphics.SetClip(applyRect); + graphics.ExcludeClip(rect); + } + + using (IFastBitmap fastBitmap = FastBitmap.CreateCloneOf(applyBitmap, applyRect)) + { + Color highlightColor = GetFieldValueAsColor(FieldType.FILL_COLOR); + for (int y = fastBitmap.Top; y < fastBitmap.Bottom; y++) + { + for (int x = fastBitmap.Left; x < fastBitmap.Right; x++) + { + Color color = fastBitmap.GetColorAt(x, y); + color = Color.FromArgb(color.A, Math.Min(highlightColor.R, color.R), Math.Min(highlightColor.G, color.G), Math.Min(highlightColor.B, color.B)); + fastBitmap.SetColorAt(x, y, color); + } + } + + fastBitmap.DrawTo(graphics, applyRect.Location); + } + + graphics.Restore(state); + } + } +} \ No newline at end of file diff --git a/src/Greenshot/Drawing/Filters/IFilter.cs b/src/Greenshot/Drawing/Filters/IFilter.cs index 8c3b5b23e..bbf255dd1 100644 --- a/src/Greenshot/Drawing/Filters/IFilter.cs +++ b/src/Greenshot/Drawing/Filters/IFilter.cs @@ -25,10 +25,11 @@ using GreenshotPlugin.Interfaces.Drawing; namespace Greenshot.Drawing.Filters { - public interface IFilter : INotifyPropertyChanged, IFieldHolder { - DrawableContainer Parent {get; set; } - void Apply(Graphics graphics, Bitmap bmp, Rectangle rect, RenderMode renderMode); - DrawableContainer GetParent(); - bool Invert {get; set;} - } -} + public interface IFilter : INotifyPropertyChanged, IFieldHolder + { + DrawableContainer Parent { get; set; } + void Apply(Graphics graphics, Bitmap bmp, Rectangle rect, RenderMode renderMode); + DrawableContainer GetParent(); + bool Invert { get; set; } + } +} \ No newline at end of file diff --git a/src/Greenshot/Drawing/Filters/MagnifierFilter.cs b/src/Greenshot/Drawing/Filters/MagnifierFilter.cs index 68b033ef5..c3f9835fa 100644 --- a/src/Greenshot/Drawing/Filters/MagnifierFilter.cs +++ b/src/Greenshot/Drawing/Filters/MagnifierFilter.cs @@ -18,6 +18,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ + using System; using System.Drawing; using Greenshot.Drawing.Fields; @@ -25,37 +26,45 @@ using GreenshotPlugin.Core; using System.Drawing.Drawing2D; using GreenshotPlugin.Interfaces.Drawing; -namespace Greenshot.Drawing.Filters { - [Serializable] - public class MagnifierFilter : AbstractFilter { - public MagnifierFilter(DrawableContainer parent) : base(parent) { - AddField(GetType(), FieldType.MAGNIFICATION_FACTOR, 2); - } +namespace Greenshot.Drawing.Filters +{ + [Serializable] + public class MagnifierFilter : AbstractFilter + { + public MagnifierFilter(DrawableContainer parent) : base(parent) + { + AddField(GetType(), FieldType.MAGNIFICATION_FACTOR, 2); + } - public override void Apply(Graphics graphics, Bitmap applyBitmap, Rectangle rect, RenderMode renderMode) { - Rectangle applyRect = ImageHelper.CreateIntersectRectangle(applyBitmap.Size, rect, Invert); + public override void Apply(Graphics graphics, Bitmap applyBitmap, Rectangle rect, RenderMode renderMode) + { + Rectangle applyRect = ImageHelper.CreateIntersectRectangle(applyBitmap.Size, rect, Invert); - if (applyRect.Width == 0 || applyRect.Height == 0) { - // nothing to do - return; - } - int magnificationFactor = GetFieldValueAsInt(FieldType.MAGNIFICATION_FACTOR); - GraphicsState state = graphics.Save(); - if (Invert) { - graphics.SetClip(applyRect); - graphics.ExcludeClip(rect); - } - graphics.SmoothingMode = SmoothingMode.None; - graphics.InterpolationMode = InterpolationMode.NearestNeighbor; - graphics.CompositingQuality = CompositingQuality.HighQuality; - graphics.PixelOffsetMode = PixelOffsetMode.None; - int halfWidth = rect.Width / 2; - int halfHeight = rect.Height / 2; - int newWidth = rect.Width / magnificationFactor; - int newHeight = rect.Height / magnificationFactor; - Rectangle source = new Rectangle(rect.X + halfWidth - newWidth / 2, rect.Y + halfHeight - newHeight / 2, newWidth, newHeight); - graphics.DrawImage(applyBitmap, rect, source, GraphicsUnit.Pixel); - graphics.Restore(state); - } - } -} + if (applyRect.Width == 0 || applyRect.Height == 0) + { + // nothing to do + return; + } + + int magnificationFactor = GetFieldValueAsInt(FieldType.MAGNIFICATION_FACTOR); + GraphicsState state = graphics.Save(); + if (Invert) + { + graphics.SetClip(applyRect); + graphics.ExcludeClip(rect); + } + + graphics.SmoothingMode = SmoothingMode.None; + graphics.InterpolationMode = InterpolationMode.NearestNeighbor; + graphics.CompositingQuality = CompositingQuality.HighQuality; + graphics.PixelOffsetMode = PixelOffsetMode.None; + int halfWidth = rect.Width / 2; + int halfHeight = rect.Height / 2; + int newWidth = rect.Width / magnificationFactor; + int newHeight = rect.Height / magnificationFactor; + Rectangle source = new Rectangle(rect.X + halfWidth - newWidth / 2, rect.Y + halfHeight - newHeight / 2, newWidth, newHeight); + graphics.DrawImage(applyBitmap, rect, source, GraphicsUnit.Pixel); + graphics.Restore(state); + } + } +} \ No newline at end of file diff --git a/src/Greenshot/Drawing/Filters/PixelizationFilter.cs b/src/Greenshot/Drawing/Filters/PixelizationFilter.cs index dbf19d3f7..00a3e2f11 100644 --- a/src/Greenshot/Drawing/Filters/PixelizationFilter.cs +++ b/src/Greenshot/Drawing/Filters/PixelizationFilter.cs @@ -18,58 +18,78 @@ * 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 Greenshot.Drawing.Fields; using Greenshot.Helpers; using GreenshotPlugin.Core; using GreenshotPlugin.Interfaces.Drawing; -namespace Greenshot.Drawing.Filters { - [Serializable()] - public class PixelizationFilter : AbstractFilter { - - public PixelizationFilter(DrawableContainer parent) : base(parent) { - AddField(GetType(), FieldType.PIXEL_SIZE, 5); - } - - public override void Apply(Graphics graphics, Bitmap applyBitmap, Rectangle rect, RenderMode renderMode) { - int pixelSize = GetFieldValueAsInt(FieldType.PIXEL_SIZE); - ImageHelper.CreateIntersectRectangle(applyBitmap.Size, rect, Invert); - if (pixelSize <= 1 || rect.Width == 0 || rect.Height == 0) { - // Nothing to do - return; - } - if (rect.Width < pixelSize) { - pixelSize = rect.Width; - } - if (rect.Height < pixelSize) { - pixelSize = rect.Height; - } +namespace Greenshot.Drawing.Filters +{ + [Serializable()] + public class PixelizationFilter : AbstractFilter + { + public PixelizationFilter(DrawableContainer parent) : base(parent) + { + AddField(GetType(), FieldType.PIXEL_SIZE, 5); + } + + public override void Apply(Graphics graphics, Bitmap applyBitmap, Rectangle rect, RenderMode renderMode) + { + int pixelSize = GetFieldValueAsInt(FieldType.PIXEL_SIZE); + ImageHelper.CreateIntersectRectangle(applyBitmap.Size, rect, Invert); + if (pixelSize <= 1 || rect.Width == 0 || rect.Height == 0) + { + // Nothing to do + return; + } + + if (rect.Width < pixelSize) + { + pixelSize = rect.Width; + } + + if (rect.Height < pixelSize) + { + pixelSize = rect.Height; + } using IFastBitmap dest = FastBitmap.CreateCloneOf(applyBitmap, rect); - using (IFastBitmap src = FastBitmap.Create(applyBitmap, rect)) { + using (IFastBitmap src = FastBitmap.Create(applyBitmap, rect)) + { List colors = new List(); int halbPixelSize = pixelSize / 2; - for (int y = src.Top - halbPixelSize; y < src.Bottom + halbPixelSize; y += pixelSize) { - for (int x = src.Left - halbPixelSize; x <= src.Right + halbPixelSize; x += pixelSize) { + for (int y = src.Top - halbPixelSize; y < src.Bottom + halbPixelSize; y += pixelSize) + { + for (int x = src.Left - halbPixelSize; x <= src.Right + halbPixelSize; x += pixelSize) + { colors.Clear(); - for (int yy = y; yy < y + pixelSize; yy++) { - if (yy >= src.Top && yy < src.Bottom) { - for (int xx = x; xx < x + pixelSize; xx++) { - if (xx >= src.Left && xx < src.Right) { + for (int yy = y; yy < y + pixelSize; yy++) + { + if (yy >= src.Top && yy < src.Bottom) + { + for (int xx = x; xx < x + pixelSize; xx++) + { + if (xx >= src.Left && xx < src.Right) + { colors.Add(src.GetColorAt(xx, yy)); } } } } + Color currentAvgColor = Colors.Mix(colors); - for (int yy = y; yy <= y + pixelSize; yy++) { - if (yy >= src.Top && yy < src.Bottom) { - for (int xx = x; xx <= x + pixelSize; xx++) { - if (xx >= src.Left && xx < src.Right) { + for (int yy = y; yy <= y + pixelSize; yy++) + { + if (yy >= src.Top && yy < src.Bottom) + { + for (int xx = x; xx <= x + pixelSize; xx++) + { + if (xx >= src.Left && xx < src.Right) + { dest.SetColorAt(xx, yy, currentAvgColor); } } @@ -78,7 +98,8 @@ namespace Greenshot.Drawing.Filters { } } } + dest.DrawTo(graphics, rect.Location); } - } -} + } +} \ No newline at end of file diff --git a/src/Greenshot/Drawing/FreehandContainer.cs b/src/Greenshot/Drawing/FreehandContainer.cs index 0485f2ae5..a493ee670 100644 --- a/src/Greenshot/Drawing/FreehandContainer.cs +++ b/src/Greenshot/Drawing/FreehandContainer.cs @@ -28,155 +28,176 @@ using System.Drawing.Drawing2D; using System.Runtime.Serialization; using GreenshotPlugin.Interfaces.Drawing; -namespace Greenshot.Drawing { - /// - /// Description of PathContainer. - /// - [Serializable] - public class FreehandContainer : DrawableContainer { - private static readonly float [] PointOffset = {0.5f, 0.25f, 0.75f}; +namespace Greenshot.Drawing +{ + /// + /// Description of PathContainer. + /// + [Serializable] + public class FreehandContainer : DrawableContainer + { + private static readonly float[] PointOffset = + { + 0.5f, 0.25f, 0.75f + }; - [NonSerialized] - private GraphicsPath freehandPath = new GraphicsPath(); - private Rectangle myBounds = Rectangle.Empty; - private Point lastMouse = Point.Empty; - private readonly List capturePoints = new List(); - private bool isRecalculated; - - /// - /// Constructor - /// - public FreehandContainer(Surface parent) : base(parent) { - Width = parent.Image.Width; - Height = parent.Image.Height; - Top = 0; - Left = 0; - } + [NonSerialized] private GraphicsPath freehandPath = new GraphicsPath(); + private Rectangle myBounds = Rectangle.Empty; + private Point lastMouse = Point.Empty; + private readonly List capturePoints = new List(); + private bool isRecalculated; - protected override void InitializeFields() { - AddField(GetType(), FieldType.LINE_THICKNESS, 3); - AddField(GetType(), FieldType.LINE_COLOR, Color.Red); - } + /// + /// Constructor + /// + public FreehandContainer(Surface parent) : base(parent) + { + Width = parent.Image.Width; + Height = parent.Image.Height; + Top = 0; + Left = 0; + } - public override void Transform(Matrix matrix) { - Point[] points = capturePoints.ToArray(); + protected override void InitializeFields() + { + AddField(GetType(), FieldType.LINE_THICKNESS, 3); + AddField(GetType(), FieldType.LINE_COLOR, Color.Red); + } - matrix.TransformPoints(points); - capturePoints.Clear(); - capturePoints.AddRange(points); - RecalculatePath(); - } - - protected override void OnDeserialized(StreamingContext context) { - RecalculatePath(); - } + public override void Transform(Matrix matrix) + { + Point[] points = capturePoints.ToArray(); - /// - /// This Dispose is called from the Dispose and the Destructor. - /// - /// When disposing==true all non-managed resources should be freed too! - protected override void Dispose(bool disposing) { - base.Dispose(disposing); - if (disposing) - { - freehandPath?.Dispose(); - } - freehandPath = null; - } - - /// - /// Called from Surface (the parent) when the drawing begins (mouse-down) - /// - /// true if the surface doesn't need to handle the event - public override bool HandleMouseDown(int mouseX, int mouseY) { - lastMouse = new Point(mouseX, mouseY); - capturePoints.Add(lastMouse); - return true; - } + matrix.TransformPoints(points); + capturePoints.Clear(); + capturePoints.AddRange(points); + RecalculatePath(); + } - /// - /// Called from Surface (the parent) if a mouse move is made while drawing - /// - /// true if the surface doesn't need to handle the event - public override bool HandleMouseMove(int mouseX, int mouseY) { - Point previousPoint = capturePoints[capturePoints.Count-1]; + protected override void OnDeserialized(StreamingContext context) + { + RecalculatePath(); + } - if (GeometryHelper.Distance2D(previousPoint.X, previousPoint.Y, mouseX, mouseY) >= 2*EditorConfig.FreehandSensitivity) { - capturePoints.Add(new Point(mouseX, mouseY)); - } - if (GeometryHelper.Distance2D(lastMouse.X, lastMouse.Y, mouseX, mouseY) < EditorConfig.FreehandSensitivity) - { - return true; - } - //path.AddCurve(new Point[]{lastMouse, new Point(mouseX, mouseY)}); - lastMouse = new Point(mouseX, mouseY); - freehandPath.AddLine(lastMouse, new Point(mouseX, mouseY)); - // Only re-calculate the bounds & redraw when we added something to the path - myBounds = Rectangle.Round(freehandPath.GetBounds()); + /// + /// This Dispose is called from the Dispose and the Destructor. + /// + /// When disposing==true all non-managed resources should be freed too! + protected override void Dispose(bool disposing) + { + base.Dispose(disposing); + if (disposing) + { + freehandPath?.Dispose(); + } + + freehandPath = null; + } + + /// + /// Called from Surface (the parent) when the drawing begins (mouse-down) + /// + /// true if the surface doesn't need to handle the event + public override bool HandleMouseDown(int mouseX, int mouseY) + { + lastMouse = new Point(mouseX, mouseY); + capturePoints.Add(lastMouse); + return true; + } + + /// + /// Called from Surface (the parent) if a mouse move is made while drawing + /// + /// true if the surface doesn't need to handle the event + public override bool HandleMouseMove(int mouseX, int mouseY) + { + Point previousPoint = capturePoints[capturePoints.Count - 1]; + + if (GeometryHelper.Distance2D(previousPoint.X, previousPoint.Y, mouseX, mouseY) >= 2 * EditorConfig.FreehandSensitivity) + { + capturePoints.Add(new Point(mouseX, mouseY)); + } + + if (GeometryHelper.Distance2D(lastMouse.X, lastMouse.Y, mouseX, mouseY) < EditorConfig.FreehandSensitivity) + { + return true; + } + + //path.AddCurve(new Point[]{lastMouse, new Point(mouseX, mouseY)}); + lastMouse = new Point(mouseX, mouseY); + freehandPath.AddLine(lastMouse, new Point(mouseX, mouseY)); + // Only re-calculate the bounds & redraw when we added something to the path + myBounds = Rectangle.Round(freehandPath.GetBounds()); Invalidate(); - return true; - } + return true; + } - /// - /// Called when the surface finishes drawing the element - /// - public override void HandleMouseUp(int mouseX, int mouseY) { - // Make sure we don't loose the ending point - if (GeometryHelper.Distance2D(lastMouse.X, lastMouse.Y, mouseX, mouseY) >= EditorConfig.FreehandSensitivity) { - capturePoints.Add(new Point(mouseX, mouseY)); - } - RecalculatePath(); - } - - /// - /// Here we recalculate the freehand path by smoothing out the lines with Beziers. - /// - private void RecalculatePath() { - // Store the previous path, to dispose it later when we are finished + /// + /// Called when the surface finishes drawing the element + /// + public override void HandleMouseUp(int mouseX, int mouseY) + { + // Make sure we don't loose the ending point + if (GeometryHelper.Distance2D(lastMouse.X, lastMouse.Y, mouseX, mouseY) >= EditorConfig.FreehandSensitivity) + { + capturePoints.Add(new Point(mouseX, mouseY)); + } + + RecalculatePath(); + } + + /// + /// Here we recalculate the freehand path by smoothing out the lines with Beziers. + /// + private void RecalculatePath() + { + // Store the previous path, to dispose it later when we are finished var previousFreehandPath = freehandPath; - var newFreehandPath = new GraphicsPath(); + var newFreehandPath = new GraphicsPath(); + + // Here we can put some cleanup... like losing all the uninteresting points. + if (capturePoints.Count >= 3) + { + int index = 0; + while ((capturePoints.Count - 1) % 3 != 0) + { + // duplicate points, first at 50% than 25% than 75% + capturePoints.Insert((int) (capturePoints.Count * PointOffset[index]), capturePoints[(int) (capturePoints.Count * PointOffset[index++])]); + } - // Here we can put some cleanup... like losing all the uninteresting points. - if (capturePoints.Count >= 3) - { - int index = 0; - while ((capturePoints.Count - 1) % 3 != 0) - { - // duplicate points, first at 50% than 25% than 75% - capturePoints.Insert((int)(capturePoints.Count * PointOffset[index]), capturePoints[(int)(capturePoints.Count * PointOffset[index++])]); - } newFreehandPath.AddBeziers(capturePoints.ToArray()); - } - else if (capturePoints.Count == 2) - { + } + else if (capturePoints.Count == 2) + { newFreehandPath.AddLine(capturePoints[0], capturePoints[1]); - } + } - // Recalculate the bounds - myBounds = Rectangle.Round(newFreehandPath.GetBounds()); + // Recalculate the bounds + myBounds = Rectangle.Round(newFreehandPath.GetBounds()); - // assign + // assign isRecalculated = true; freehandPath = newFreehandPath; - // dispose previous - previousFreehandPath?.Dispose(); - } + // dispose previous + previousFreehandPath?.Dispose(); + } - /// - /// Do the drawing of the freehand "stroke" - /// - /// - /// - public override void Draw(Graphics graphics, RenderMode renderMode) { - graphics.SmoothingMode = SmoothingMode.HighQuality; - graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; - graphics.CompositingQuality = CompositingQuality.HighQuality; - graphics.PixelOffsetMode = PixelOffsetMode.HighQuality; - - int lineThickness = GetFieldValueAsInt(FieldType.LINE_THICKNESS); - Color lineColor = GetFieldValueAsColor(FieldType.LINE_COLOR); + /// + /// Do the drawing of the freehand "stroke" + /// + /// + /// + public override void Draw(Graphics graphics, RenderMode renderMode) + { + graphics.SmoothingMode = SmoothingMode.HighQuality; + graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; + graphics.CompositingQuality = CompositingQuality.HighQuality; + graphics.PixelOffsetMode = PixelOffsetMode.HighQuality; + + int lineThickness = GetFieldValueAsInt(FieldType.LINE_THICKNESS); + Color lineColor = GetFieldValueAsColor(FieldType.LINE_COLOR); using var pen = new Pen(lineColor) { Width = lineThickness @@ -185,6 +206,7 @@ namespace Greenshot.Drawing { { return; } + // Make sure the lines are nicely rounded pen.EndCap = LineCap.Round; pen.StartCap = LineCap.Round; @@ -199,11 +221,12 @@ namespace Greenshot.Drawing { isRecalculated = false; DrawSelectionBorder(graphics, pen, currentFreehandPath); } + graphics.DrawPath(pen, currentFreehandPath); - } + } // Move back, otherwise everything is shifted - graphics.TranslateTransform(-Left,-Top); + graphics.TranslateTransform(-Left, -Top); } /// @@ -215,71 +238,86 @@ namespace Greenshot.Drawing { protected static void DrawSelectionBorder(Graphics graphics, Pen linePen, GraphicsPath path) { using var selectionPen = (Pen) linePen.Clone(); - using var selectionPath = (GraphicsPath)path.Clone(); + using var selectionPath = (GraphicsPath) path.Clone(); selectionPen.Width += 5; selectionPen.Color = Color.FromArgb(120, Color.LightSeaGreen); graphics.DrawPath(selectionPen, selectionPath); selectionPath.Widen(selectionPen); - selectionPen.DashPattern = new float[]{2,2}; + selectionPen.DashPattern = new float[] + { + 2, 2 + }; selectionPen.Color = Color.LightSeaGreen; selectionPen.Width = 1; graphics.DrawPath(selectionPen, selectionPath); } - - /// - /// Get the bounds in which we have something drawn, plus safety margin, these are not the normal bounds... - /// - public override Rectangle DrawingBounds { - get { - if (!myBounds.IsEmpty) { - int lineThickness = Math.Max(10, GetFieldValueAsInt(FieldType.LINE_THICKNESS)); - int safetymargin = 10; - return new Rectangle(myBounds.Left + Left - (safetymargin+lineThickness), myBounds.Top + Top - (safetymargin+lineThickness), myBounds.Width + 2*(lineThickness+safetymargin), myBounds.Height + 2*(lineThickness+safetymargin)); - } - if (_parent?.Image is Image image) - { - return new Rectangle(0, 0, image.Width, image.Height); - } - else - { - return Rectangle.Empty; - } - } - } + + /// + /// Get the bounds in which we have something drawn, plus safety margin, these are not the normal bounds... + /// + public override Rectangle DrawingBounds + { + get + { + if (!myBounds.IsEmpty) + { + int lineThickness = Math.Max(10, GetFieldValueAsInt(FieldType.LINE_THICKNESS)); + int safetymargin = 10; + return new Rectangle(myBounds.Left + Left - (safetymargin + lineThickness), myBounds.Top + Top - (safetymargin + lineThickness), + myBounds.Width + 2 * (lineThickness + safetymargin), myBounds.Height + 2 * (lineThickness + safetymargin)); + } + + if (_parent?.Image is Image image) + { + return new Rectangle(0, 0, image.Width, image.Height); + } + else + { + return Rectangle.Empty; + } + } + } /// /// FreehandContainer are regarded equal if they are of the same type and their paths are equal. /// /// object /// bool - public override bool Equals(object obj) { - bool ret = false; + public override bool Equals(object obj) + { + bool ret = false; if (obj == null || GetType() != obj.GetType()) { return false; } - if (obj is FreehandContainer other && Equals(freehandPath, other.freehandPath)) { + if (obj is FreehandContainer other && Equals(freehandPath, other.freehandPath)) + { ret = true; } + return ret; - } + } - public override int GetHashCode() { - return freehandPath?.GetHashCode() ?? 0; - } + public override int GetHashCode() + { + return freehandPath?.GetHashCode() ?? 0; + } - public override bool ClickableAt(int x, int y) { - bool returnValue = base.ClickableAt(x, y); - if (returnValue) { - int lineThickness = GetFieldValueAsInt(FieldType.LINE_THICKNESS); + public override bool ClickableAt(int x, int y) + { + bool returnValue = base.ClickableAt(x, y); + if (returnValue) + { + int lineThickness = GetFieldValueAsInt(FieldType.LINE_THICKNESS); using var pen = new Pen(Color.White) { Width = lineThickness + 10 }; returnValue = freehandPath.IsOutlineVisible(x - Left, y - Top, pen); } - return returnValue; - } - } -} + + return returnValue; + } + } +} \ No newline at end of file diff --git a/src/Greenshot/Drawing/HighlightContainer.cs b/src/Greenshot/Drawing/HighlightContainer.cs index 788360004..267e2784b 100644 --- a/src/Greenshot/Drawing/HighlightContainer.cs +++ b/src/Greenshot/Drawing/HighlightContainer.cs @@ -18,82 +18,95 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ + using System; using System.Runtime.Serialization; - using Greenshot.Drawing.Fields; using Greenshot.Drawing.Filters; using GreenshotPlugin.Interfaces.Drawing; -namespace Greenshot.Drawing { - /// - /// Description of ObfuscateContainer. - /// - [Serializable] - public class HighlightContainer : FilterContainer { - public HighlightContainer(Surface parent) : base(parent) { - Init(); - } +namespace Greenshot.Drawing +{ + /// + /// Description of ObfuscateContainer. + /// + [Serializable] + public class HighlightContainer : FilterContainer + { + public HighlightContainer(Surface parent) : base(parent) + { + Init(); + } - /// - /// Use settings from base, extend with our own field - /// - protected override void InitializeFields() { - base.InitializeFields(); - AddField(GetType(), FieldType.PREPARED_FILTER_HIGHLIGHT, PreparedFilter.TEXT_HIGHTLIGHT); - } + /// + /// Use settings from base, extend with our own field + /// + protected override void InitializeFields() + { + base.InitializeFields(); + AddField(GetType(), FieldType.PREPARED_FILTER_HIGHLIGHT, PreparedFilter.TEXT_HIGHTLIGHT); + } - protected override void OnDeserialized(StreamingContext context) - { - Init(); - } - - private void Init() { - FieldChanged += HighlightContainer_OnFieldChanged; - ConfigurePreparedFilters(); - } - - protected void HighlightContainer_OnFieldChanged(object sender, FieldChangedEventArgs e) { - if (!sender.Equals(this)) { - return; - } - if (Equals(e.Field.FieldType, FieldType.PREPARED_FILTER_HIGHLIGHT)) { - ConfigurePreparedFilters(); - } - } + protected override void OnDeserialized(StreamingContext context) + { + Init(); + } - private void ConfigurePreparedFilters() { - PreparedFilter preset = (PreparedFilter)GetFieldValue(FieldType.PREPARED_FILTER_HIGHLIGHT); - while(Filters.Count>0) { - Remove(Filters[0]); - } - switch(preset) { - case PreparedFilter.TEXT_HIGHTLIGHT: - Add(new HighlightFilter(this)); - break; - case PreparedFilter.AREA_HIGHLIGHT: - var brightnessFilter = new BrightnessFilter(this) - { - Invert = true - }; - Add(brightnessFilter); - var blurFilter = new BlurFilter(this) - { - Invert = true - }; - Add(blurFilter); - break; - case PreparedFilter.GRAYSCALE: + private void Init() + { + FieldChanged += HighlightContainer_OnFieldChanged; + ConfigurePreparedFilters(); + } + + protected void HighlightContainer_OnFieldChanged(object sender, FieldChangedEventArgs e) + { + if (!sender.Equals(this)) + { + return; + } + + if (Equals(e.Field.FieldType, FieldType.PREPARED_FILTER_HIGHLIGHT)) + { + ConfigurePreparedFilters(); + } + } + + private void ConfigurePreparedFilters() + { + PreparedFilter preset = (PreparedFilter) GetFieldValue(FieldType.PREPARED_FILTER_HIGHLIGHT); + while (Filters.Count > 0) + { + Remove(Filters[0]); + } + + switch (preset) + { + case PreparedFilter.TEXT_HIGHTLIGHT: + Add(new HighlightFilter(this)); + break; + case PreparedFilter.AREA_HIGHLIGHT: + var brightnessFilter = new BrightnessFilter(this) + { + Invert = true + }; + Add(brightnessFilter); + var blurFilter = new BlurFilter(this) + { + Invert = true + }; + Add(blurFilter); + break; + case PreparedFilter.GRAYSCALE: AbstractFilter f = new GrayscaleFilter(this) { Invert = true }; Add(f); - break; - case PreparedFilter.MAGNIFICATION: - Add(new MagnifierFilter(this)); - break; - } - } - } -} + break; + case PreparedFilter.MAGNIFICATION: + Add(new MagnifierFilter(this)); + break; + } + } + } +} \ No newline at end of file diff --git a/src/Greenshot/Drawing/IconContainer.cs b/src/Greenshot/Drawing/IconContainer.cs index 6d5c90986..a89e0a258 100644 --- a/src/Greenshot/Drawing/IconContainer.cs +++ b/src/Greenshot/Drawing/IconContainer.cs @@ -18,6 +18,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ + using System; using System.Drawing; using System.IO; @@ -26,84 +27,94 @@ using log4net; using System.Runtime.Serialization; using GreenshotPlugin.Interfaces.Drawing; -namespace Greenshot.Drawing { - /// - /// Description of IconContainer. - /// - [Serializable] - public class IconContainer : DrawableContainer, IIconContainer { - private static readonly ILog Log = LogManager.GetLogger(typeof(IconContainer)); +namespace Greenshot.Drawing +{ + /// + /// Description of IconContainer. + /// + [Serializable] + public class IconContainer : DrawableContainer, IIconContainer + { + private static readonly ILog Log = LogManager.GetLogger(typeof(IconContainer)); - protected Icon icon; + protected Icon icon; - public IconContainer(Surface parent) : base(parent) { - Init(); - } + public IconContainer(Surface parent) : base(parent) + { + Init(); + } - protected override void OnDeserialized(StreamingContext streamingContext) - { - base.OnDeserialized(streamingContext); - Init(); - } + protected override void OnDeserialized(StreamingContext streamingContext) + { + base.OnDeserialized(streamingContext); + Init(); + } - private void Init() - { - CreateDefaultAdorners(); - } + private void Init() + { + CreateDefaultAdorners(); + } - public IconContainer(Surface parent, string filename) : base(parent) { - Load(filename); - } + public IconContainer(Surface parent, string filename) : base(parent) + { + Load(filename); + } - public Icon Icon { - set { - icon?.Dispose(); - icon = (Icon)value.Clone(); - Width = value.Width; - Height = value.Height; - } - get => icon; - } + public Icon Icon + { + set + { + icon?.Dispose(); + icon = (Icon) value.Clone(); + Width = value.Width; + Height = value.Height; + } + get => icon; + } - /** + /** * This Dispose is called from the Dispose and the Destructor. * When disposing==true all non-managed resources should be freed too! */ - protected override void Dispose(bool disposing) { - if (disposing) - { - icon?.Dispose(); - } - icon = null; - base.Dispose(disposing); - } + protected override void Dispose(bool disposing) + { + if (disposing) + { + icon?.Dispose(); + } - public void Load(string filename) - { - if (!File.Exists(filename)) - { - return; - } - using Icon fileIcon = new Icon(filename); - Icon = fileIcon; - Log.Debug("Loaded file: " + filename + " with resolution: " + Height + "," + Width); - } - - public override void Draw(Graphics graphics, RenderMode rm) - { - if (icon == null) - { - return; - } - graphics.SmoothingMode = SmoothingMode.HighQuality; - graphics.InterpolationMode = InterpolationMode.NearestNeighbor; - graphics.CompositingQuality = CompositingQuality.Default; - graphics.PixelOffsetMode = PixelOffsetMode.HighQuality; - graphics.DrawIcon(icon, Bounds); - } + icon = null; + base.Dispose(disposing); + } - public override bool HasDefaultSize => true; + public void Load(string filename) + { + if (!File.Exists(filename)) + { + return; + } - public override Size DefaultSize => icon?.Size ?? new Size(16,16); - } -} + using Icon fileIcon = new Icon(filename); + Icon = fileIcon; + Log.Debug("Loaded file: " + filename + " with resolution: " + Height + "," + Width); + } + + public override void Draw(Graphics graphics, RenderMode rm) + { + if (icon == null) + { + return; + } + + graphics.SmoothingMode = SmoothingMode.HighQuality; + graphics.InterpolationMode = InterpolationMode.NearestNeighbor; + graphics.CompositingQuality = CompositingQuality.Default; + graphics.PixelOffsetMode = PixelOffsetMode.HighQuality; + graphics.DrawIcon(icon, Bounds); + } + + public override bool HasDefaultSize => true; + + public override Size DefaultSize => icon?.Size ?? new Size(16, 16); + } +} \ No newline at end of file diff --git a/src/Greenshot/Drawing/ImageContainer.cs b/src/Greenshot/Drawing/ImageContainer.cs index 5840c9507..3b99e36af 100644 --- a/src/Greenshot/Drawing/ImageContainer.cs +++ b/src/Greenshot/Drawing/ImageContainer.cs @@ -18,6 +18,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ + using System; using System.Drawing; using System.IO; @@ -29,206 +30,238 @@ using System.Runtime.Serialization; using GreenshotPlugin.Effects; using GreenshotPlugin.Interfaces.Drawing; -namespace Greenshot.Drawing { - /// - /// Description of BitmapContainer. - /// - [Serializable] - public class ImageContainer : DrawableContainer, IImageContainer { - private static readonly ILog Log = LogManager.GetLogger(typeof(ImageContainer)); +namespace Greenshot.Drawing +{ + /// + /// Description of BitmapContainer. + /// + [Serializable] + public class ImageContainer : DrawableContainer, IImageContainer + { + private static readonly ILog Log = LogManager.GetLogger(typeof(ImageContainer)); - private Image image; + private Image image; - /// - /// This is the shadow version of the bitmap, rendered once to save performance - /// Do not serialize, as the shadow is recreated from the original bitmap if it's not available - /// - [NonSerialized] - private Image _shadowBitmap; + /// + /// This is the shadow version of the bitmap, rendered once to save performance + /// Do not serialize, as the shadow is recreated from the original bitmap if it's not available + /// + [NonSerialized] private Image _shadowBitmap; - /// - /// This is the offset for the shadow version of the bitmap - /// Do not serialize, as the offset is recreated - /// - [NonSerialized] - private Point _shadowOffset = new Point(-1, -1); + /// + /// This is the offset for the shadow version of the bitmap + /// Do not serialize, as the offset is recreated + /// + [NonSerialized] private Point _shadowOffset = new Point(-1, -1); - public ImageContainer(Surface parent, string filename) : this(parent) { - Load(filename); - } + public ImageContainer(Surface parent, string filename) : this(parent) + { + Load(filename); + } - public ImageContainer(Surface parent) : base(parent) { - FieldChanged += BitmapContainer_OnFieldChanged; - Init(); - } + public ImageContainer(Surface parent) : base(parent) + { + FieldChanged += BitmapContainer_OnFieldChanged; + Init(); + } - protected override void OnDeserialized(StreamingContext streamingContext) - { - base.OnDeserialized(streamingContext); - Init(); - } + protected override void OnDeserialized(StreamingContext streamingContext) + { + base.OnDeserialized(streamingContext); + Init(); + } - private void Init() - { - CreateDefaultAdorners(); - } + private void Init() + { + CreateDefaultAdorners(); + } - protected override void InitializeFields() { - AddField(GetType(), FieldType.SHADOW, false); - } + protected override void InitializeFields() + { + AddField(GetType(), FieldType.SHADOW, false); + } - protected void BitmapContainer_OnFieldChanged(object sender, FieldChangedEventArgs e) - { - if (!sender.Equals(this)) - { - return; - } - if (FieldType.SHADOW.Equals(e.Field.FieldType)) { - ChangeShadowField(); - } - } + protected void BitmapContainer_OnFieldChanged(object sender, FieldChangedEventArgs e) + { + if (!sender.Equals(this)) + { + return; + } - public void ChangeShadowField() { - bool shadow = GetFieldValueAsBool(FieldType.SHADOW); - if (shadow) { - CheckShadow(true); - Width = _shadowBitmap.Width; - Height = _shadowBitmap.Height; - Left -= _shadowOffset.X; - Top -= _shadowOffset.Y; - } else { - Width = image.Width; - Height = image.Height; - if (_shadowBitmap != null) { - Left += _shadowOffset.X; - Top += _shadowOffset.Y; - } - } - } + if (FieldType.SHADOW.Equals(e.Field.FieldType)) + { + ChangeShadowField(); + } + } - public Image Image { - set { - // Remove all current bitmaps - DisposeImage(); - DisposeShadow(); - image = ImageHelper.Clone(value); - bool shadow = GetFieldValueAsBool(FieldType.SHADOW); - CheckShadow(shadow); - if (!shadow) { - Width = image.Width; - Height = image.Height; - } else { - Width = _shadowBitmap.Width; - Height = _shadowBitmap.Height; - Left -= _shadowOffset.X; - Top -= _shadowOffset.Y; - } - } - get { return image; } - } + public void ChangeShadowField() + { + bool shadow = GetFieldValueAsBool(FieldType.SHADOW); + if (shadow) + { + CheckShadow(true); + Width = _shadowBitmap.Width; + Height = _shadowBitmap.Height; + Left -= _shadowOffset.X; + Top -= _shadowOffset.Y; + } + else + { + Width = image.Width; + Height = image.Height; + if (_shadowBitmap != null) + { + Left += _shadowOffset.X; + Top += _shadowOffset.Y; + } + } + } - /// - /// The bulk of the clean-up code is implemented in Dispose(bool) - /// This Dispose is called from the Dispose and the Destructor. - /// When disposing==true all non-managed resources should be freed too! - /// - /// - protected override void Dispose(bool disposing) { - if (disposing) { - DisposeImage(); - DisposeShadow(); - } - base.Dispose(disposing); - } + public Image Image + { + set + { + // Remove all current bitmaps + DisposeImage(); + DisposeShadow(); + image = ImageHelper.Clone(value); + bool shadow = GetFieldValueAsBool(FieldType.SHADOW); + CheckShadow(shadow); + if (!shadow) + { + Width = image.Width; + Height = image.Height; + } + else + { + Width = _shadowBitmap.Width; + Height = _shadowBitmap.Height; + Left -= _shadowOffset.X; + Top -= _shadowOffset.Y; + } + } + get { return image; } + } - private void DisposeImage() { - image?.Dispose(); - image = null; - } - private void DisposeShadow() { - _shadowBitmap?.Dispose(); - _shadowBitmap = null; - } + /// + /// The bulk of the clean-up code is implemented in Dispose(bool) + /// This Dispose is called from the Dispose and the Destructor. + /// When disposing==true all non-managed resources should be freed too! + /// + /// + protected override void Dispose(bool disposing) + { + if (disposing) + { + DisposeImage(); + DisposeShadow(); + } + + base.Dispose(disposing); + } + + private void DisposeImage() + { + image?.Dispose(); + image = null; + } + + private void DisposeShadow() + { + _shadowBitmap?.Dispose(); + _shadowBitmap = null; + } - - /// - /// Make sure the content is also transformed. - /// - /// - public override void Transform(Matrix matrix) { - int rotateAngle = CalculateAngle(matrix); - // we currently assume only one transformation has been made. - if (rotateAngle != 0) { - Log.DebugFormat("Rotating element with {0} degrees.", rotateAngle); - DisposeShadow(); + /// + /// Make sure the content is also transformed. + /// + /// + public override void Transform(Matrix matrix) + { + int rotateAngle = CalculateAngle(matrix); + // we currently assume only one transformation has been made. + if (rotateAngle != 0) + { + Log.DebugFormat("Rotating element with {0} degrees.", rotateAngle); + DisposeShadow(); using var tmpMatrix = new Matrix(); using (image) { image = ImageHelper.ApplyEffect(image, new RotateEffect(rotateAngle), tmpMatrix); } } - base.Transform(matrix); - } - /// - /// - /// - /// - public void Load(string filename) { - if (!File.Exists(filename)) - { - return; - } - // Always make sure ImageHelper.LoadBitmap results are disposed some time, - // as we close the bitmap internally, we need to do it afterwards - using (var tmpImage = ImageHelper.LoadImage(filename)) { - Image = tmpImage; - } - Log.Debug("Loaded file: " + filename + " with resolution: " + Height + "," + Width); - } + base.Transform(matrix); + } - /// - /// This checks if a shadow is already generated - /// - /// - private void CheckShadow(bool shadow) - { - if (!shadow || _shadowBitmap != null) - { - return; - } - using var matrix = new Matrix(); - _shadowBitmap = ImageHelper.ApplyEffect(image, new DropShadowEffect(), matrix); - } + /// + /// + /// + /// + public void Load(string filename) + { + if (!File.Exists(filename)) + { + return; + } - /// - /// Draw the actual container to the graphics object - /// - /// - /// - public override void Draw(Graphics graphics, RenderMode rm) - { - if (image == null) - { - return; - } - bool shadow = GetFieldValueAsBool(FieldType.SHADOW); - graphics.SmoothingMode = SmoothingMode.HighQuality; - graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; - graphics.CompositingQuality = CompositingQuality.HighQuality; - graphics.PixelOffsetMode = PixelOffsetMode.HighQuality; + // Always make sure ImageHelper.LoadBitmap results are disposed some time, + // as we close the bitmap internally, we need to do it afterwards + using (var tmpImage = ImageHelper.LoadImage(filename)) + { + Image = tmpImage; + } - if (shadow) { - CheckShadow(true); - graphics.DrawImage(_shadowBitmap, Bounds); - } else { - graphics.DrawImage(image, Bounds); - } - } + Log.Debug("Loaded file: " + filename + " with resolution: " + Height + "," + Width); + } - public override bool HasDefaultSize => true; + /// + /// This checks if a shadow is already generated + /// + /// + private void CheckShadow(bool shadow) + { + if (!shadow || _shadowBitmap != null) + { + return; + } - public override Size DefaultSize => image?.Size ?? new Size(32, 32); - } -} + using var matrix = new Matrix(); + _shadowBitmap = ImageHelper.ApplyEffect(image, new DropShadowEffect(), matrix); + } + + /// + /// Draw the actual container to the graphics object + /// + /// + /// + public override void Draw(Graphics graphics, RenderMode rm) + { + if (image == null) + { + return; + } + + bool shadow = GetFieldValueAsBool(FieldType.SHADOW); + graphics.SmoothingMode = SmoothingMode.HighQuality; + graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; + graphics.CompositingQuality = CompositingQuality.HighQuality; + graphics.PixelOffsetMode = PixelOffsetMode.HighQuality; + + if (shadow) + { + CheckShadow(true); + graphics.DrawImage(_shadowBitmap, Bounds); + } + else + { + graphics.DrawImage(image, Bounds); + } + } + + public override bool HasDefaultSize => true; + + public override Size DefaultSize => image?.Size ?? new Size(32, 32); + } +} \ No newline at end of file diff --git a/src/Greenshot/Drawing/LineContainer.cs b/src/Greenshot/Drawing/LineContainer.cs index 93b76ad89..6a5229107 100644 --- a/src/Greenshot/Drawing/LineContainer.cs +++ b/src/Greenshot/Drawing/LineContainer.cs @@ -18,60 +18,68 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ + using System; using System.Drawing; using System.Drawing.Drawing2D; using System.Runtime.Serialization; - using Greenshot.Drawing.Fields; using Greenshot.Helpers; using Greenshot.Drawing.Adorners; using GreenshotPlugin.Interfaces.Drawing; -namespace Greenshot.Drawing { - /// - /// Description of LineContainer. - /// - [Serializable()] - public class LineContainer : DrawableContainer { - public LineContainer(Surface parent) : base(parent) { - Init(); - } +namespace Greenshot.Drawing +{ + /// + /// Description of LineContainer. + /// + [Serializable()] + public class LineContainer : DrawableContainer + { + public LineContainer(Surface parent) : base(parent) + { + Init(); + } - protected override void InitializeFields() { - AddField(GetType(), FieldType.LINE_THICKNESS, 2); - AddField(GetType(), FieldType.LINE_COLOR, Color.Red); - AddField(GetType(), FieldType.SHADOW, true); - } + protected override void InitializeFields() + { + AddField(GetType(), FieldType.LINE_THICKNESS, 2); + AddField(GetType(), FieldType.LINE_COLOR, Color.Red); + AddField(GetType(), FieldType.SHADOW, true); + } - protected override void OnDeserialized(StreamingContext context) - { - Init(); - } + protected override void OnDeserialized(StreamingContext context) + { + Init(); + } - protected void Init() { - Adorners.Add(new MoveAdorner(this, Positions.TopLeft)); - Adorners.Add(new MoveAdorner(this, Positions.BottomRight)); - } + protected void Init() + { + Adorners.Add(new MoveAdorner(this, Positions.TopLeft)); + Adorners.Add(new MoveAdorner(this, Positions.BottomRight)); + } - public override void Draw(Graphics graphics, RenderMode rm) { - graphics.SmoothingMode = SmoothingMode.HighQuality; - graphics.InterpolationMode = InterpolationMode.HighQualityBilinear; - graphics.CompositingQuality = CompositingQuality.HighQuality; - graphics.PixelOffsetMode = PixelOffsetMode.None; + public override void Draw(Graphics graphics, RenderMode rm) + { + graphics.SmoothingMode = SmoothingMode.HighQuality; + graphics.InterpolationMode = InterpolationMode.HighQualityBilinear; + graphics.CompositingQuality = CompositingQuality.HighQuality; + graphics.PixelOffsetMode = PixelOffsetMode.None; - int lineThickness = GetFieldValueAsInt(FieldType.LINE_THICKNESS); - Color lineColor = GetFieldValueAsColor(FieldType.LINE_COLOR); - bool shadow = GetFieldValueAsBool(FieldType.SHADOW); + int lineThickness = GetFieldValueAsInt(FieldType.LINE_THICKNESS); + Color lineColor = GetFieldValueAsColor(FieldType.LINE_COLOR); + bool shadow = GetFieldValueAsBool(FieldType.SHADOW); - if (lineThickness > 0) { - if (shadow) { - //draw shadow first - int basealpha = 100; - int alpha = basealpha; - int steps = 5; - int currentStep = 1; - while (currentStep <= steps) + if (lineThickness > 0) + { + if (shadow) + { + //draw shadow first + int basealpha = 100; + int alpha = basealpha; + int steps = 5; + int currentStep = 1; + while (currentStep <= steps) { using Pen shadowCapPen = new Pen(Color.FromArgb(alpha, 100, 100, 100), lineThickness); graphics.DrawLine(shadowCapPen, @@ -83,16 +91,17 @@ namespace Greenshot.Drawing { currentStep++; alpha -= basealpha / steps; } - } + } using Pen pen = new Pen(lineColor, lineThickness); graphics.DrawLine(pen, Left, Top, Left + Width, Top + Height); } - } + } - public override bool ClickableAt(int x, int y) { - int lineThickness = GetFieldValueAsInt(FieldType.LINE_THICKNESS) +5; - if (lineThickness > 0) + public override bool ClickableAt(int x, int y) + { + int lineThickness = GetFieldValueAsInt(FieldType.LINE_THICKNESS) + 5; + if (lineThickness > 0) { using Pen pen = new Pen(Color.White) { @@ -100,13 +109,15 @@ namespace Greenshot.Drawing { }; using GraphicsPath path = new GraphicsPath(); path.AddLine(Left, Top, Left + Width, Top + Height); - return path.IsOutlineVisible(x, y, pen); + return path.IsOutlineVisible(x, y, pen); } - return false; - } - protected override ScaleHelper.IDoubleProcessor GetAngleRoundProcessor() { - return ScaleHelper.LineAngleRoundBehavior.Instance; - } - } -} + return false; + } + + protected override ScaleHelper.IDoubleProcessor GetAngleRoundProcessor() + { + return ScaleHelper.LineAngleRoundBehavior.Instance; + } + } +} \ No newline at end of file diff --git a/src/Greenshot/Drawing/ObfuscateContainer.cs b/src/Greenshot/Drawing/ObfuscateContainer.cs index f1fdbf156..127ff2b61 100644 --- a/src/Greenshot/Drawing/ObfuscateContainer.cs +++ b/src/Greenshot/Drawing/ObfuscateContainer.cs @@ -18,59 +18,72 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ + using System; using System.Runtime.Serialization; using Greenshot.Drawing.Fields; using Greenshot.Drawing.Filters; using GreenshotPlugin.Interfaces.Drawing; -namespace Greenshot.Drawing { - /// - /// Description of ObfuscateContainer. - /// - [Serializable] - public class ObfuscateContainer : FilterContainer { - public ObfuscateContainer(Surface parent) : base(parent) { - Init(); - } +namespace Greenshot.Drawing +{ + /// + /// Description of ObfuscateContainer. + /// + [Serializable] + public class ObfuscateContainer : FilterContainer + { + public ObfuscateContainer(Surface parent) : base(parent) + { + Init(); + } - protected override void InitializeFields() { - base.InitializeFields(); - AddField(GetType(), FieldType.PREPARED_FILTER_OBFUSCATE, PreparedFilter.PIXELIZE); - } + protected override void InitializeFields() + { + base.InitializeFields(); + AddField(GetType(), FieldType.PREPARED_FILTER_OBFUSCATE, PreparedFilter.PIXELIZE); + } - protected override void OnDeserialized(StreamingContext context) - { - Init(); - } - - private void Init() { - FieldChanged += ObfuscateContainer_OnFieldChanged; - ConfigurePreparedFilters(); - CreateDefaultAdorners(); - } - - protected void ObfuscateContainer_OnFieldChanged(object sender, FieldChangedEventArgs e) { - if(sender.Equals(this)) { - if(Equals(e.Field.FieldType, FieldType.PREPARED_FILTER_OBFUSCATE)) { - ConfigurePreparedFilters(); - } - } - } - - private void ConfigurePreparedFilters() { - PreparedFilter preset = (PreparedFilter)GetFieldValue(FieldType.PREPARED_FILTER_OBFUSCATE); - while(Filters.Count>0) { - Remove(Filters[0]); - } - switch(preset) { - case PreparedFilter.BLUR: - Add(new BlurFilter(this)); - break; - case PreparedFilter.PIXELIZE: - Add(new PixelizationFilter(this)); - break; - } - } - } -} + protected override void OnDeserialized(StreamingContext context) + { + Init(); + } + + private void Init() + { + FieldChanged += ObfuscateContainer_OnFieldChanged; + ConfigurePreparedFilters(); + CreateDefaultAdorners(); + } + + protected void ObfuscateContainer_OnFieldChanged(object sender, FieldChangedEventArgs e) + { + if (sender.Equals(this)) + { + if (Equals(e.Field.FieldType, FieldType.PREPARED_FILTER_OBFUSCATE)) + { + ConfigurePreparedFilters(); + } + } + } + + private void ConfigurePreparedFilters() + { + PreparedFilter preset = (PreparedFilter) GetFieldValue(FieldType.PREPARED_FILTER_OBFUSCATE); + while (Filters.Count > 0) + { + Remove(Filters[0]); + } + + switch (preset) + { + case PreparedFilter.BLUR: + Add(new BlurFilter(this)); + break; + case PreparedFilter.PIXELIZE: + Add(new PixelizationFilter(this)); + break; + } + } + } +} \ No newline at end of file diff --git a/src/Greenshot/Drawing/Positions.cs b/src/Greenshot/Drawing/Positions.cs index f7061d023..ca42ee728 100644 --- a/src/Greenshot/Drawing/Positions.cs +++ b/src/Greenshot/Drawing/Positions.cs @@ -21,18 +21,18 @@ namespace Greenshot.Drawing { - /// - /// Position - /// - public enum Positions : int - { - TopLeft = 0, - TopCenter = 1, - TopRight = 2, - MiddleRight = 3, - BottomRight = 4, - BottomCenter = 5, - BottomLeft = 6, - MiddleLeft = 7 - } -} + /// + /// Position + /// + public enum Positions : int + { + TopLeft = 0, + TopCenter = 1, + TopRight = 2, + MiddleRight = 3, + BottomRight = 4, + BottomCenter = 5, + BottomLeft = 6, + MiddleLeft = 7 + } +} \ No newline at end of file diff --git a/src/Greenshot/Drawing/RectangleContainer.cs b/src/Greenshot/Drawing/RectangleContainer.cs index 2e86fa509..b26193ee7 100644 --- a/src/Greenshot/Drawing/RectangleContainer.cs +++ b/src/Greenshot/Drawing/RectangleContainer.cs @@ -27,73 +27,79 @@ using Greenshot.Helpers; using System.Runtime.Serialization; using GreenshotPlugin.Interfaces.Drawing; -namespace Greenshot.Drawing { - /// - /// Represents a rectangular shape on the Surface - /// - [Serializable] - public class RectangleContainer : DrawableContainer { +namespace Greenshot.Drawing +{ + /// + /// Represents a rectangular shape on the Surface + /// + [Serializable] + public class RectangleContainer : DrawableContainer + { + public RectangleContainer(Surface parent) : base(parent) + { + Init(); + } - public RectangleContainer(Surface parent) : base(parent) { - Init(); - } + /// + /// Do some logic to make sure all field are initiated correctly + /// + /// StreamingContext + protected override void OnDeserialized(StreamingContext streamingContext) + { + base.OnDeserialized(streamingContext); + Init(); + } - /// - /// Do some logic to make sure all field are initiated correctly - /// - /// StreamingContext - protected override void OnDeserialized(StreamingContext streamingContext) - { - base.OnDeserialized(streamingContext); - Init(); - } + private void Init() + { + CreateDefaultAdorners(); + } - private void Init() - { - CreateDefaultAdorners(); - } + protected override void InitializeFields() + { + AddField(GetType(), FieldType.LINE_THICKNESS, 2); + AddField(GetType(), FieldType.LINE_COLOR, Color.Red); + AddField(GetType(), FieldType.FILL_COLOR, Color.Transparent); + AddField(GetType(), FieldType.SHADOW, true); + } - protected override void InitializeFields() { - AddField(GetType(), FieldType.LINE_THICKNESS, 2); - AddField(GetType(), FieldType.LINE_COLOR, Color.Red); - AddField(GetType(), FieldType.FILL_COLOR, Color.Transparent); - AddField(GetType(), FieldType.SHADOW, true); - } + public override void Draw(Graphics graphics, RenderMode rm) + { + int lineThickness = GetFieldValueAsInt(FieldType.LINE_THICKNESS); + Color lineColor = GetFieldValueAsColor(FieldType.LINE_COLOR, Color.Red); + Color fillColor = GetFieldValueAsColor(FieldType.FILL_COLOR, Color.Transparent); + bool shadow = GetFieldValueAsBool(FieldType.SHADOW); + Rectangle rect = GuiRectangle.GetGuiRectangle(Left, Top, Width, Height); - public override void Draw(Graphics graphics, RenderMode rm) { - int lineThickness = GetFieldValueAsInt(FieldType.LINE_THICKNESS); - Color lineColor = GetFieldValueAsColor(FieldType.LINE_COLOR, Color.Red); - Color fillColor = GetFieldValueAsColor(FieldType.FILL_COLOR, Color.Transparent); - bool shadow = GetFieldValueAsBool(FieldType.SHADOW); - Rectangle rect = GuiRectangle.GetGuiRectangle(Left, Top, Width, Height); + DrawRectangle(rect, graphics, rm, lineThickness, lineColor, fillColor, shadow); + } - DrawRectangle(rect, graphics, rm, lineThickness, lineColor, fillColor, shadow); - } + /// + /// This method can also be used from other containers, if the right values are passed! + /// + /// + /// + /// + /// + /// + /// + /// + public static void DrawRectangle(Rectangle rect, Graphics graphics, RenderMode rm, int lineThickness, Color lineColor, Color fillColor, bool shadow) + { + graphics.SmoothingMode = SmoothingMode.HighQuality; + graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; + graphics.CompositingQuality = CompositingQuality.HighQuality; + graphics.PixelOffsetMode = PixelOffsetMode.None; - /// - /// This method can also be used from other containers, if the right values are passed! - /// - /// - /// - /// - /// - /// - /// - /// - public static void DrawRectangle(Rectangle rect, Graphics graphics, RenderMode rm, int lineThickness, Color lineColor, Color fillColor, bool shadow) { - graphics.SmoothingMode = SmoothingMode.HighQuality; - graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; - graphics.CompositingQuality = CompositingQuality.HighQuality; - graphics.PixelOffsetMode = PixelOffsetMode.None; - - bool lineVisible = lineThickness > 0 && Colors.IsVisible(lineColor); - if (shadow && (lineVisible || Colors.IsVisible(fillColor))) { - //draw shadow first - int basealpha = 100; - int alpha = basealpha; - int steps = 5; - int currentStep = lineVisible ? 1 : 0; - while (currentStep <= steps) + bool lineVisible = lineThickness > 0 && Colors.IsVisible(lineColor); + if (shadow && (lineVisible || Colors.IsVisible(fillColor))) + { + //draw shadow first + int basealpha = 100; + int alpha = basealpha; + int steps = 5; + int currentStep = lineVisible ? 1 : 0; + while (currentStep <= steps) { using Pen shadowPen = new Pen(Color.FromArgb(alpha, 100, 100, 100)) { @@ -108,50 +114,54 @@ namespace Greenshot.Drawing { currentStep++; alpha -= basealpha / steps; } - } + } - if (Colors.IsVisible(fillColor)) + if (Colors.IsVisible(fillColor)) { using Brush brush = new SolidBrush(fillColor); graphics.FillRectangle(brush, rect); } - graphics.SmoothingMode = SmoothingMode.HighSpeed; - if (lineVisible) + graphics.SmoothingMode = SmoothingMode.HighSpeed; + if (lineVisible) { using Pen pen = new Pen(lineColor, lineThickness); graphics.DrawRectangle(pen, rect); } + } - } - public override bool ClickableAt(int x, int y) { - Rectangle rect = GuiRectangle.GetGuiRectangle(Left, Top, Width, Height); - int lineThickness = GetFieldValueAsInt(FieldType.LINE_THICKNESS) + 10; - Color fillColor = GetFieldValueAsColor(FieldType.FILL_COLOR); + public override bool ClickableAt(int x, int y) + { + Rectangle rect = GuiRectangle.GetGuiRectangle(Left, Top, Width, Height); + int lineThickness = GetFieldValueAsInt(FieldType.LINE_THICKNESS) + 10; + Color fillColor = GetFieldValueAsColor(FieldType.FILL_COLOR); - return RectangleClickableAt(rect, lineThickness, fillColor, x, y); - } + return RectangleClickableAt(rect, lineThickness, fillColor, x, y); + } - public static bool RectangleClickableAt(Rectangle rect, int lineThickness, Color fillColor, int x, int y) { + public static bool RectangleClickableAt(Rectangle rect, int lineThickness, Color fillColor, int x, int y) + { + // If we clicked inside the rectangle and it's visible we are clickable at. + if (!Color.Transparent.Equals(fillColor)) + { + if (rect.Contains(x, y)) + { + return true; + } + } - // If we clicked inside the rectangle and it's visible we are clickable at. - if (!Color.Transparent.Equals(fillColor)) { - if (rect.Contains(x,y)) { - return true; - } - } - - // check the rest of the lines - if (lineThickness > 0) + // check the rest of the lines + if (lineThickness > 0) { using Pen pen = new Pen(Color.White, lineThickness); using GraphicsPath path = new GraphicsPath(); path.AddRectangle(rect); return path.IsOutlineVisible(x, y, pen); } - return false; - } - } -} + + return false; + } + } +} \ No newline at end of file diff --git a/src/Greenshot/Drawing/SpeechbubbleContainer.cs b/src/Greenshot/Drawing/SpeechbubbleContainer.cs index ee00714e9..ce11402b9 100644 --- a/src/Greenshot/Drawing/SpeechbubbleContainer.cs +++ b/src/Greenshot/Drawing/SpeechbubbleContainer.cs @@ -30,316 +30,370 @@ using GreenshotPlugin.Interfaces.Drawing; namespace Greenshot.Drawing { - /// - /// Description of SpeechbubbleContainer. - /// - [Serializable] - public class SpeechbubbleContainer : TextContainer { - - private Point _initialGripperPoint; + /// + /// Description of SpeechbubbleContainer. + /// + [Serializable] + public class SpeechbubbleContainer : TextContainer + { + private Point _initialGripperPoint; // Only used for serializing the TargetGripper location - private Point _storedTargetGripperLocation; + private Point _storedTargetGripperLocation; - /// - /// Store the current location of the target gripper - /// - /// - [OnSerializing] - private void SetValuesOnSerializing(StreamingContext context) { - if (TargetAdorner != null) { - _storedTargetGripperLocation = TargetAdorner.Location; - } - } + /// + /// Store the current location of the target gripper + /// + /// + [OnSerializing] + private void SetValuesOnSerializing(StreamingContext context) + { + if (TargetAdorner != null) + { + _storedTargetGripperLocation = TargetAdorner.Location; + } + } - /// - /// Restore the target gripper - /// - /// StreamingContext - protected override void OnDeserialized(StreamingContext streamingContext) - { - base.OnDeserialized(streamingContext); - InitAdorner(Color.Green, _storedTargetGripperLocation); - } + /// + /// Restore the target gripper + /// + /// StreamingContext + protected override void OnDeserialized(StreamingContext streamingContext) + { + base.OnDeserialized(streamingContext); + InitAdorner(Color.Green, _storedTargetGripperLocation); + } public SpeechbubbleContainer(Surface parent) - : base(parent) { - } + : base(parent) + { + } - /// - /// We set our own field values - /// - protected override void InitializeFields() { - AddField(GetType(), FieldType.LINE_THICKNESS, 2); - AddField(GetType(), FieldType.LINE_COLOR, Color.Blue); - AddField(GetType(), FieldType.SHADOW, false); - AddField(GetType(), FieldType.FONT_ITALIC, false); - AddField(GetType(), FieldType.FONT_BOLD, true); - AddField(GetType(), FieldType.FILL_COLOR, Color.White); - AddField(GetType(), FieldType.FONT_FAMILY, FontFamily.GenericSansSerif.Name); - AddField(GetType(), FieldType.FONT_SIZE, 20f); - AddField(GetType(), FieldType.TEXT_HORIZONTAL_ALIGNMENT, StringAlignment.Center); - AddField(GetType(), FieldType.TEXT_VERTICAL_ALIGNMENT, StringAlignment.Center); - } + /// + /// We set our own field values + /// + protected override void InitializeFields() + { + AddField(GetType(), FieldType.LINE_THICKNESS, 2); + AddField(GetType(), FieldType.LINE_COLOR, Color.Blue); + AddField(GetType(), FieldType.SHADOW, false); + AddField(GetType(), FieldType.FONT_ITALIC, false); + AddField(GetType(), FieldType.FONT_BOLD, true); + AddField(GetType(), FieldType.FILL_COLOR, Color.White); + AddField(GetType(), FieldType.FONT_FAMILY, FontFamily.GenericSansSerif.Name); + AddField(GetType(), FieldType.FONT_SIZE, 20f); + AddField(GetType(), FieldType.TEXT_HORIZONTAL_ALIGNMENT, StringAlignment.Center); + AddField(GetType(), FieldType.TEXT_VERTICAL_ALIGNMENT, StringAlignment.Center); + } - /// - /// Called from Surface (the _parent) when the drawing begins (mouse-down) - /// - /// true if the surface doesn't need to handle the event - public override bool HandleMouseDown(int mouseX, int mouseY) { - if (TargetAdorner == null) { - _initialGripperPoint = new Point(mouseX, mouseY); - InitAdorner(Color.Green, new Point(mouseX, mouseY)); - } - return base.HandleMouseDown(mouseX, mouseY); - } + /// + /// Called from Surface (the _parent) when the drawing begins (mouse-down) + /// + /// true if the surface doesn't need to handle the event + public override bool HandleMouseDown(int mouseX, int mouseY) + { + if (TargetAdorner == null) + { + _initialGripperPoint = new Point(mouseX, mouseY); + InitAdorner(Color.Green, new Point(mouseX, mouseY)); + } - /// - /// Overriding the HandleMouseMove will help us to make sure the tail is always visible. - /// Should fix BUG-1682 - /// - /// - /// - /// base.HandleMouseMove - public override bool HandleMouseMove(int x, int y) { - bool returnValue = base.HandleMouseMove(x, y); + return base.HandleMouseDown(mouseX, mouseY); + } - bool leftAligned = _boundsAfterResize.Right - _boundsAfterResize.Left >= 0; - bool topAligned = _boundsAfterResize.Bottom - _boundsAfterResize.Top >= 0; + /// + /// Overriding the HandleMouseMove will help us to make sure the tail is always visible. + /// Should fix BUG-1682 + /// + /// + /// + /// base.HandleMouseMove + public override bool HandleMouseMove(int x, int y) + { + bool returnValue = base.HandleMouseMove(x, y); - int xOffset = leftAligned ? -20 : 20; - int yOffset = topAligned ? -20 : 20; + bool leftAligned = _boundsAfterResize.Right - _boundsAfterResize.Left >= 0; + bool topAligned = _boundsAfterResize.Bottom - _boundsAfterResize.Top >= 0; - Point newGripperLocation = _initialGripperPoint; - newGripperLocation.Offset(xOffset, yOffset); - - if (TargetAdorner.Location != newGripperLocation) { - Invalidate(); - TargetAdorner.Location = newGripperLocation; - Invalidate(); - } - return returnValue; - } + int xOffset = leftAligned ? -20 : 20; + int yOffset = topAligned ? -20 : 20; - /// - /// The DrawingBound should be so close as possible to the shape, so we don't invalidate to much. - /// - public override Rectangle DrawingBounds { - get { - if (Status != EditStatus.UNDRAWN) { - int lineThickness = GetFieldValueAsInt(FieldType.LINE_THICKNESS); - Color lineColor = GetFieldValueAsColor(FieldType.LINE_COLOR); - bool shadow = GetFieldValueAsBool(FieldType.SHADOW); + Point newGripperLocation = _initialGripperPoint; + newGripperLocation.Offset(xOffset, yOffset); + + if (TargetAdorner.Location != newGripperLocation) + { + Invalidate(); + TargetAdorner.Location = newGripperLocation; + Invalidate(); + } + + return returnValue; + } + + /// + /// The DrawingBound should be so close as possible to the shape, so we don't invalidate to much. + /// + public override Rectangle DrawingBounds + { + get + { + if (Status != EditStatus.UNDRAWN) + { + int lineThickness = GetFieldValueAsInt(FieldType.LINE_THICKNESS); + Color lineColor = GetFieldValueAsColor(FieldType.LINE_COLOR); + bool shadow = GetFieldValueAsBool(FieldType.SHADOW); using Pen pen = new Pen(lineColor, lineThickness); int inflateValue = lineThickness + 2 + (shadow ? 6 : 0); using GraphicsPath tailPath = CreateTail(); - return Rectangle.Inflate(Rectangle.Union(Rectangle.Round(tailPath.GetBounds(new Matrix(), pen)), GuiRectangle.GetGuiRectangle(Left, Top, Width, Height)), inflateValue, inflateValue); + return Rectangle.Inflate(Rectangle.Union(Rectangle.Round(tailPath.GetBounds(new Matrix(), pen)), GuiRectangle.GetGuiRectangle(Left, Top, Width, Height)), + inflateValue, inflateValue); } - return Rectangle.Empty; - } - } - /// - /// Helper method to create the bubble GraphicsPath, so we can also calculate the bounds - /// - /// - /// - private GraphicsPath CreateBubble(int lineThickness) { - GraphicsPath bubble = new GraphicsPath(); - Rectangle rect = GuiRectangle.GetGuiRectangle(Left, Top, Width, Height); + return Rectangle.Empty; + } + } - Rectangle bubbleRect = GuiRectangle.GetGuiRectangle(0, 0, rect.Width, rect.Height); - // adapt corner radius to small rectangle dimensions - int smallerSideLength = Math.Min(bubbleRect.Width, bubbleRect.Height); - int cornerRadius = Math.Min(30, smallerSideLength / 2 - lineThickness); - if (cornerRadius > 0) { - bubble.AddArc(bubbleRect.X, bubbleRect.Y, cornerRadius, cornerRadius, 180, 90); - bubble.AddArc(bubbleRect.X + bubbleRect.Width - cornerRadius, bubbleRect.Y, cornerRadius, cornerRadius, 270, 90); - bubble.AddArc(bubbleRect.X + bubbleRect.Width - cornerRadius, bubbleRect.Y + bubbleRect.Height - cornerRadius, cornerRadius, cornerRadius, 0, 90); - bubble.AddArc(bubbleRect.X, bubbleRect.Y + bubbleRect.Height - cornerRadius, cornerRadius, cornerRadius, 90, 90); - } else { - bubble.AddRectangle(bubbleRect); - } - bubble.CloseAllFigures(); - using (Matrix bubbleMatrix = new Matrix()) { - bubbleMatrix.Translate(rect.Left, rect.Top); - bubble.Transform(bubbleMatrix); - } - return bubble; - } + /// + /// Helper method to create the bubble GraphicsPath, so we can also calculate the bounds + /// + /// + /// + private GraphicsPath CreateBubble(int lineThickness) + { + GraphicsPath bubble = new GraphicsPath(); + Rectangle rect = GuiRectangle.GetGuiRectangle(Left, Top, Width, Height); - /// - /// Helper method to create the tail of the bubble, so we can also calculate the bounds - /// - /// - private GraphicsPath CreateTail() { - Rectangle rect = GuiRectangle.GetGuiRectangle(Left, Top, Width, Height); + Rectangle bubbleRect = GuiRectangle.GetGuiRectangle(0, 0, rect.Width, rect.Height); + // adapt corner radius to small rectangle dimensions + int smallerSideLength = Math.Min(bubbleRect.Width, bubbleRect.Height); + int cornerRadius = Math.Min(30, smallerSideLength / 2 - lineThickness); + if (cornerRadius > 0) + { + bubble.AddArc(bubbleRect.X, bubbleRect.Y, cornerRadius, cornerRadius, 180, 90); + bubble.AddArc(bubbleRect.X + bubbleRect.Width - cornerRadius, bubbleRect.Y, cornerRadius, cornerRadius, 270, 90); + bubble.AddArc(bubbleRect.X + bubbleRect.Width - cornerRadius, bubbleRect.Y + bubbleRect.Height - cornerRadius, cornerRadius, cornerRadius, 0, 90); + bubble.AddArc(bubbleRect.X, bubbleRect.Y + bubbleRect.Height - cornerRadius, cornerRadius, cornerRadius, 90, 90); + } + else + { + bubble.AddRectangle(bubbleRect); + } - int tailLength = GeometryHelper.Distance2D(rect.Left + (rect.Width / 2), rect.Top + (rect.Height / 2), TargetAdorner.Location.X, TargetAdorner.Location.Y); - int tailWidth = (Math.Abs(rect.Width) + Math.Abs(rect.Height)) / 20; + bubble.CloseAllFigures(); + using (Matrix bubbleMatrix = new Matrix()) + { + bubbleMatrix.Translate(rect.Left, rect.Top); + bubble.Transform(bubbleMatrix); + } - // This should fix a problem with the tail being to wide - tailWidth = Math.Min(Math.Abs(rect.Width) / 2, tailWidth); - tailWidth = Math.Min(Math.Abs(rect.Height) / 2, tailWidth); + return bubble; + } - GraphicsPath tail = new GraphicsPath(); - tail.AddLine(-tailWidth, 0, tailWidth, 0); - tail.AddLine(tailWidth, 0, 0, -tailLength); - tail.CloseFigure(); + /// + /// Helper method to create the tail of the bubble, so we can also calculate the bounds + /// + /// + private GraphicsPath CreateTail() + { + Rectangle rect = GuiRectangle.GetGuiRectangle(Left, Top, Width, Height); - int tailAngle = 90 + (int)GeometryHelper.Angle2D(rect.Left + rect.Width / 2, rect.Top + rect.Height / 2, TargetAdorner.Location.X, TargetAdorner.Location.Y); + int tailLength = GeometryHelper.Distance2D(rect.Left + (rect.Width / 2), rect.Top + (rect.Height / 2), TargetAdorner.Location.X, TargetAdorner.Location.Y); + int tailWidth = (Math.Abs(rect.Width) + Math.Abs(rect.Height)) / 20; - using (Matrix tailMatrix = new Matrix()) { - tailMatrix.Translate(rect.Left + rect.Width / 2, rect.Top + rect.Height / 2); - tailMatrix.Rotate(tailAngle); - tail.Transform(tailMatrix); - } + // This should fix a problem with the tail being to wide + tailWidth = Math.Min(Math.Abs(rect.Width) / 2, tailWidth); + tailWidth = Math.Min(Math.Abs(rect.Height) / 2, tailWidth); - return tail; - } + GraphicsPath tail = new GraphicsPath(); + tail.AddLine(-tailWidth, 0, tailWidth, 0); + tail.AddLine(tailWidth, 0, 0, -tailLength); + tail.CloseFigure(); - /// - /// This is to draw the actual container - /// - /// - /// - public override void Draw(Graphics graphics, RenderMode renderMode) { - if (TargetAdorner == null) { - return; - } - graphics.SmoothingMode = SmoothingMode.HighQuality; - graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; - graphics.CompositingQuality = CompositingQuality.HighQuality; - graphics.PixelOffsetMode = PixelOffsetMode.None; - graphics.TextRenderingHint = TextRenderingHint.AntiAliasGridFit; + int tailAngle = 90 + (int) GeometryHelper.Angle2D(rect.Left + rect.Width / 2, rect.Top + rect.Height / 2, TargetAdorner.Location.X, TargetAdorner.Location.Y); - Color lineColor = GetFieldValueAsColor(FieldType.LINE_COLOR); - Color fillColor = GetFieldValueAsColor(FieldType.FILL_COLOR); - bool shadow = GetFieldValueAsBool(FieldType.SHADOW); - int lineThickness = GetFieldValueAsInt(FieldType.LINE_THICKNESS); + using (Matrix tailMatrix = new Matrix()) + { + tailMatrix.Translate(rect.Left + rect.Width / 2, rect.Top + rect.Height / 2); + tailMatrix.Rotate(tailAngle); + tail.Transform(tailMatrix); + } - bool lineVisible = lineThickness > 0 && Colors.IsVisible(lineColor); - Rectangle rect = GuiRectangle.GetGuiRectangle(Left, Top, Width, Height); + return tail; + } - if (Selected && renderMode == RenderMode.EDIT) { - DrawSelectionBorder(graphics, rect); - } + /// + /// This is to draw the actual container + /// + /// + /// + public override void Draw(Graphics graphics, RenderMode renderMode) + { + if (TargetAdorner == null) + { + return; + } - GraphicsPath bubble = CreateBubble(lineThickness); + graphics.SmoothingMode = SmoothingMode.HighQuality; + graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; + graphics.CompositingQuality = CompositingQuality.HighQuality; + graphics.PixelOffsetMode = PixelOffsetMode.None; + graphics.TextRenderingHint = TextRenderingHint.AntiAliasGridFit; - GraphicsPath tail = CreateTail(); + Color lineColor = GetFieldValueAsColor(FieldType.LINE_COLOR); + Color fillColor = GetFieldValueAsColor(FieldType.FILL_COLOR); + bool shadow = GetFieldValueAsBool(FieldType.SHADOW); + int lineThickness = GetFieldValueAsInt(FieldType.LINE_THICKNESS); - //draw shadow first - if (shadow && (lineVisible || Colors.IsVisible(fillColor))) { - const int basealpha = 100; - int alpha = basealpha; - const int steps = 5; - int currentStep = lineVisible ? 1 : 0; + bool lineVisible = lineThickness > 0 && Colors.IsVisible(lineColor); + Rectangle rect = GuiRectangle.GetGuiRectangle(Left, Top, Width, Height); + + if (Selected && renderMode == RenderMode.EDIT) + { + DrawSelectionBorder(graphics, rect); + } + + GraphicsPath bubble = CreateBubble(lineThickness); + + GraphicsPath tail = CreateTail(); + + //draw shadow first + if (shadow && (lineVisible || Colors.IsVisible(fillColor))) + { + const int basealpha = 100; + int alpha = basealpha; + const int steps = 5; + int currentStep = lineVisible ? 1 : 0; using Matrix shadowMatrix = new Matrix(); - using GraphicsPath bubbleClone = (GraphicsPath)bubble.Clone(); - using GraphicsPath tailClone = (GraphicsPath)tail.Clone(); + using GraphicsPath bubbleClone = (GraphicsPath) bubble.Clone(); + using GraphicsPath tailClone = (GraphicsPath) tail.Clone(); shadowMatrix.Translate(1, 1); - while (currentStep <= steps) { - using (Pen shadowPen = new Pen(Color.FromArgb(alpha, 100, 100, 100))) { + while (currentStep <= steps) + { + using (Pen shadowPen = new Pen(Color.FromArgb(alpha, 100, 100, 100))) + { shadowPen.Width = lineVisible ? lineThickness : 1; tailClone.Transform(shadowMatrix); graphics.DrawPath(shadowPen, tailClone); bubbleClone.Transform(shadowMatrix); graphics.DrawPath(shadowPen, bubbleClone); } + currentStep++; alpha -= basealpha / steps; } } - GraphicsState state = graphics.Save(); - // draw the tail border where the bubble is not visible - using (Region clipRegion = new Region(bubble)) { - graphics.SetClip(clipRegion, CombineMode.Exclude); + GraphicsState state = graphics.Save(); + // draw the tail border where the bubble is not visible + using (Region clipRegion = new Region(bubble)) + { + graphics.SetClip(clipRegion, CombineMode.Exclude); using Pen pen = new Pen(lineColor, lineThickness); graphics.DrawPath(pen, tail); } - graphics.Restore(state); - if (Colors.IsVisible(fillColor)) { - //draw the bubbleshape - state = graphics.Save(); - using (Brush brush = new SolidBrush(fillColor)) { - graphics.FillPath(brush, bubble); - } - graphics.Restore(state); - } + graphics.Restore(state); - if (lineVisible) { - //draw the bubble border - state = graphics.Save(); - // Draw bubble where the Tail is not visible. - using (Region clipRegion = new Region(tail)) { - graphics.SetClip(clipRegion, CombineMode.Exclude); + if (Colors.IsVisible(fillColor)) + { + //draw the bubbleshape + state = graphics.Save(); + using (Brush brush = new SolidBrush(fillColor)) + { + graphics.FillPath(brush, bubble); + } + + graphics.Restore(state); + } + + if (lineVisible) + { + //draw the bubble border + state = graphics.Save(); + // Draw bubble where the Tail is not visible. + using (Region clipRegion = new Region(tail)) + { + graphics.SetClip(clipRegion, CombineMode.Exclude); using Pen pen = new Pen(lineColor, lineThickness); //pen.EndCap = pen.StartCap = LineCap.Round; graphics.DrawPath(pen, bubble); } - graphics.Restore(state); - } - if (Colors.IsVisible(fillColor)) { - // Draw the tail border - state = graphics.Save(); - using (Brush brush = new SolidBrush(fillColor)) { - graphics.FillPath(brush, tail); - } - graphics.Restore(state); - } + graphics.Restore(state); + } - // cleanup the paths - bubble.Dispose(); - tail.Dispose(); + if (Colors.IsVisible(fillColor)) + { + // Draw the tail border + state = graphics.Save(); + using (Brush brush = new SolidBrush(fillColor)) + { + graphics.FillPath(brush, tail); + } - // Draw the text - DrawText(graphics, rect, lineThickness, lineColor, shadow, StringFormat, Text, Font); - } + graphics.Restore(state); + } - public override bool Contains(int x, int y) { - if (base.Contains(x, y)) { - return true; - } - Point clickedPoint = new Point(x, y); - if (Status != EditStatus.UNDRAWN) { - int lineThickness = GetFieldValueAsInt(FieldType.LINE_THICKNESS); - Color lineColor = GetFieldValueAsColor(FieldType.LINE_COLOR); + // cleanup the paths + bubble.Dispose(); + tail.Dispose(); + + // Draw the text + DrawText(graphics, rect, lineThickness, lineColor, shadow, StringFormat, Text, Font); + } + + public override bool Contains(int x, int y) + { + if (base.Contains(x, y)) + { + return true; + } + + Point clickedPoint = new Point(x, y); + if (Status != EditStatus.UNDRAWN) + { + int lineThickness = GetFieldValueAsInt(FieldType.LINE_THICKNESS); + Color lineColor = GetFieldValueAsColor(FieldType.LINE_COLOR); using Pen pen = new Pen(lineColor, lineThickness); - using (GraphicsPath bubblePath = CreateBubble(lineThickness)) { + using (GraphicsPath bubblePath = CreateBubble(lineThickness)) + { bubblePath.Widen(pen); - if (bubblePath.IsVisible(clickedPoint)) { + if (bubblePath.IsVisible(clickedPoint)) + { return true; } } using GraphicsPath tailPath = CreateTail(); tailPath.Widen(pen); - if (tailPath.IsVisible(clickedPoint)) { + if (tailPath.IsVisible(clickedPoint)) + { return true; } } - return false; - } + return false; + } - public override bool ClickableAt(int x, int y) { - return Contains(x,y); - } + public override bool ClickableAt(int x, int y) + { + return Contains(x, y); + } - /// - /// Additional to the Transform of the TextContainer the bubble tail coordinates also need to be moved - /// - /// Matrix - public override void Transform(Matrix matrix) - { - Point[] points = { TargetAdorner.Location }; - matrix.TransformPoints(points); - TargetAdorner.Location = points[0]; - base.Transform(matrix); - } - } -} + /// + /// Additional to the Transform of the TextContainer the bubble tail coordinates also need to be moved + /// + /// Matrix + public override void Transform(Matrix matrix) + { + Point[] points = + { + TargetAdorner.Location + }; + matrix.TransformPoints(points); + TargetAdorner.Location = points[0]; + base.Transform(matrix); + } + } +} \ No newline at end of file diff --git a/src/Greenshot/Drawing/StepLabelContainer.cs b/src/Greenshot/Drawing/StepLabelContainer.cs index b750fcb20..ff01af1c1 100644 --- a/src/Greenshot/Drawing/StepLabelContainer.cs +++ b/src/Greenshot/Drawing/StepLabelContainer.cs @@ -28,179 +28,198 @@ using System.Drawing.Text; using System.Runtime.Serialization; using GreenshotPlugin.Interfaces.Drawing; -namespace Greenshot.Drawing { - /// - /// This is an enumerated label, every single StepLabelContainer shows the number of the order it was created. - /// To make sure that deleting recalculates, we check the location before every draw. - /// - [Serializable] - public sealed class StepLabelContainer : DrawableContainer { - [NonSerialized] - private StringFormat _stringFormat = new StringFormat(); +namespace Greenshot.Drawing +{ + /// + /// This is an enumerated label, every single StepLabelContainer shows the number of the order it was created. + /// To make sure that deleting recalculates, we check the location before every draw. + /// + [Serializable] + public sealed class StepLabelContainer : DrawableContainer + { + [NonSerialized] private StringFormat _stringFormat = new StringFormat(); - private readonly bool _drawAsRectangle = false; + private readonly bool _drawAsRectangle = false; - public StepLabelContainer(Surface parent) : base(parent) { - parent.AddStepLabel(this); - InitContent(); - Init(); - } + public StepLabelContainer(Surface parent) : base(parent) + { + parent.AddStepLabel(this); + InitContent(); + Init(); + } - private void Init() - { - CreateDefaultAdorners(); - } + private void Init() + { + CreateDefaultAdorners(); + } // Used to store the number of this label, so when deserializing it can be placed back to the StepLabels list in the right location - private int _number; - // Used to store the counter start of the Surface, as the surface is NOT stored. - private int _counterStart = 1; - public int Number { - get { - return _number; - } - set { - _number = value; - } - } + private int _number; - /// - /// Retrieve the counter before serializing - /// - /// - [OnSerializing] - private void SetValuesOnSerializing(StreamingContext context) { - if (Parent != null) { - Number = ((Surface)Parent).CountStepLabels(this); - _counterStart = ((Surface) Parent).CounterStart; - } - } + // Used to store the counter start of the Surface, as the surface is NOT stored. + private int _counterStart = 1; + + public int Number + { + get { return _number; } + set { _number = value; } + } /// - /// Restore values that don't serialize - /// - /// - protected override void OnDeserialized(StreamingContext context) - { - Init(); - _stringFormat = new StringFormat - { - Alignment = StringAlignment.Center, - LineAlignment = StringAlignment.Center - }; + /// Retrieve the counter before serializing + /// + /// + [OnSerializing] + private void SetValuesOnSerializing(StreamingContext context) + { + if (Parent != null) + { + Number = ((Surface) Parent).CountStepLabels(this); + _counterStart = ((Surface) Parent).CounterStart; + } + } - } + /// + /// Restore values that don't serialize + /// + /// + protected override void OnDeserialized(StreamingContext context) + { + Init(); + _stringFormat = new StringFormat + { + Alignment = StringAlignment.Center, + LineAlignment = StringAlignment.Center + }; + } - /// - /// Add the StepLabel to the parent - /// - /// - protected override void SwitchParent(Surface newParent) { - if (newParent == Parent) - { - return; - } - ((Surface) Parent)?.RemoveStepLabel(this); - base.SwitchParent(newParent); - if (newParent == null) - { - return; - } - // Make sure the counter start is restored (this unfortunately happens multiple times... -> hack) - newParent.CounterStart = _counterStart; - newParent.AddStepLabel(this); - } + /// + /// Add the StepLabel to the parent + /// + /// + protected override void SwitchParent(Surface newParent) + { + if (newParent == Parent) + { + return; + } - public override Size DefaultSize => new Size(30, 30); + ((Surface) Parent)?.RemoveStepLabel(this); + base.SwitchParent(newParent); + if (newParent == null) + { + return; + } - public override bool InitContent() { - _defaultEditMode = EditStatus.IDLE; - _stringFormat.Alignment = StringAlignment.Center; - _stringFormat.LineAlignment = StringAlignment.Center; + // Make sure the counter start is restored (this unfortunately happens multiple times... -> hack) + newParent.CounterStart = _counterStart; + newParent.AddStepLabel(this); + } - // Set defaults - Width = DefaultSize.Width; - Height = DefaultSize.Height; + public override Size DefaultSize => new Size(30, 30); - return true; - } + public override bool InitContent() + { + _defaultEditMode = EditStatus.IDLE; + _stringFormat.Alignment = StringAlignment.Center; + _stringFormat.LineAlignment = StringAlignment.Center; - /// - /// This makes it possible for the label to be placed exactly in the middle of the pointer. - /// - public override bool HandleMouseDown(int mouseX, int mouseY) { - return base.HandleMouseDown(mouseX - Width / 2, mouseY - Height / 2); - } + // Set defaults + Width = DefaultSize.Width; + Height = DefaultSize.Height; - /// - /// We set our own field values - /// - protected override void InitializeFields() { - AddField(GetType(), FieldType.FILL_COLOR, Color.DarkRed); - AddField(GetType(), FieldType.LINE_COLOR, Color.White); - AddField(GetType(), FieldType.FLAGS, FieldFlag.COUNTER); - } + return true; + } - /// - /// Make sure this element is no longer referenced from the surface - /// - protected override void Dispose(bool disposing) { - base.Dispose(disposing); - if (!disposing) { - return; - } - ((Surface) Parent)?.RemoveStepLabel(this); - if (_stringFormat == null) - { - return; - } - _stringFormat.Dispose(); - _stringFormat = null; - } + /// + /// This makes it possible for the label to be placed exactly in the middle of the pointer. + /// + public override bool HandleMouseDown(int mouseX, int mouseY) + { + return base.HandleMouseDown(mouseX - Width / 2, mouseY - Height / 2); + } - public override bool HandleMouseMove(int x, int y) { - Invalidate(); - Left = x - Width / 2; - Top = y - Height / 2; - Invalidate(); - return true; - } + /// + /// We set our own field values + /// + protected override void InitializeFields() + { + AddField(GetType(), FieldType.FILL_COLOR, Color.DarkRed); + AddField(GetType(), FieldType.LINE_COLOR, Color.White); + AddField(GetType(), FieldType.FLAGS, FieldFlag.COUNTER); + } - /// - /// Override the parent, calculate the label number, than draw - /// - /// - /// - public override void Draw(Graphics graphics, RenderMode rm) { - graphics.SmoothingMode = SmoothingMode.HighQuality; - graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; - graphics.CompositingQuality = CompositingQuality.HighQuality; - graphics.PixelOffsetMode = PixelOffsetMode.None; - graphics.TextRenderingHint = TextRenderingHint.AntiAliasGridFit; - string text = ((Surface)Parent).CountStepLabels(this).ToString(); - Rectangle rect = GuiRectangle.GetGuiRectangle(Left, Top, Width, Height); - Color fillColor = GetFieldValueAsColor(FieldType.FILL_COLOR); - Color lineColor = GetFieldValueAsColor(FieldType.LINE_COLOR); - if (_drawAsRectangle) { - RectangleContainer.DrawRectangle(rect, graphics, rm, 0, Color.Transparent, fillColor, false); - } else { - EllipseContainer.DrawEllipse(rect, graphics, rm, 0, Color.Transparent, fillColor, false); - } + /// + /// Make sure this element is no longer referenced from the surface + /// + protected override void Dispose(bool disposing) + { + base.Dispose(disposing); + if (!disposing) + { + return; + } - float fontSize = Math.Min(Width,Height) / 1.4f; + ((Surface) Parent)?.RemoveStepLabel(this); + if (_stringFormat == null) + { + return; + } + + _stringFormat.Dispose(); + _stringFormat = null; + } + + public override bool HandleMouseMove(int x, int y) + { + Invalidate(); + Left = x - Width / 2; + Top = y - Height / 2; + Invalidate(); + return true; + } + + /// + /// Override the parent, calculate the label number, than draw + /// + /// + /// + public override void Draw(Graphics graphics, RenderMode rm) + { + graphics.SmoothingMode = SmoothingMode.HighQuality; + graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; + graphics.CompositingQuality = CompositingQuality.HighQuality; + graphics.PixelOffsetMode = PixelOffsetMode.None; + graphics.TextRenderingHint = TextRenderingHint.AntiAliasGridFit; + string text = ((Surface) Parent).CountStepLabels(this).ToString(); + Rectangle rect = GuiRectangle.GetGuiRectangle(Left, Top, Width, Height); + Color fillColor = GetFieldValueAsColor(FieldType.FILL_COLOR); + Color lineColor = GetFieldValueAsColor(FieldType.LINE_COLOR); + if (_drawAsRectangle) + { + RectangleContainer.DrawRectangle(rect, graphics, rm, 0, Color.Transparent, fillColor, false); + } + else + { + EllipseContainer.DrawEllipse(rect, graphics, rm, 0, Color.Transparent, fillColor, false); + } + + float fontSize = Math.Min(Width, Height) / 1.4f; using FontFamily fam = new FontFamily(FontFamily.GenericSansSerif.Name); using Font font = new Font(fam, fontSize, FontStyle.Bold, GraphicsUnit.Pixel); TextContainer.DrawText(graphics, rect, 0, lineColor, false, _stringFormat, text, font); } - public override bool ClickableAt(int x, int y) { - Rectangle rect = GuiRectangle.GetGuiRectangle(Left, Top, Width, Height); - Color fillColor = GetFieldValueAsColor(FieldType.FILL_COLOR); - if (_drawAsRectangle) { - return RectangleContainer.RectangleClickableAt(rect, 0, fillColor, x, y); - } + public override bool ClickableAt(int x, int y) + { + Rectangle rect = GuiRectangle.GetGuiRectangle(Left, Top, Width, Height); + Color fillColor = GetFieldValueAsColor(FieldType.FILL_COLOR); + if (_drawAsRectangle) + { + return RectangleContainer.RectangleClickableAt(rect, 0, fillColor, x, y); + } return EllipseContainer.EllipseClickableAt(rect, 0, fillColor, x, y); } - } -} + } +} \ No newline at end of file diff --git a/src/Greenshot/Drawing/Surface.cs b/src/Greenshot/Drawing/Surface.cs index ed3118db6..4dd3c55bd 100644 --- a/src/Greenshot/Drawing/Surface.cs +++ b/src/Greenshot/Drawing/Surface.cs @@ -43,843 +43,857 @@ using GreenshotPlugin.Interfaces.Drawing.Adorners; namespace Greenshot.Drawing { - /// - /// Description of Surface. - /// - public sealed class Surface : Control, ISurface, INotifyPropertyChanged - { - private static readonly ILog LOG = LogManager.GetLogger(typeof(Surface)); - public static int Count; - private static readonly CoreConfiguration conf = IniConfig.GetIniSection(); + /// + /// Description of Surface. + /// + public sealed class Surface : Control, ISurface, INotifyPropertyChanged + { + private static readonly ILog LOG = LogManager.GetLogger(typeof(Surface)); + public static int Count; + private static readonly CoreConfiguration conf = IniConfig.GetIniSection(); - // Property to identify the Surface ID - private Guid _uniqueId = Guid.NewGuid(); + // Property to identify the Surface ID + private Guid _uniqueId = Guid.NewGuid(); - /// - /// This value is used to start counting the step labels - /// - private int _counterStart = 1; + /// + /// This value is used to start counting the step labels + /// + private int _counterStart = 1; - /// - /// The GUID of the surface - /// - public Guid ID - { - get => _uniqueId; + /// + /// The GUID of the surface + /// + public Guid ID + { + get => _uniqueId; set => _uniqueId = value; } - /// - /// Event handlers (do not serialize!) - /// - [NonSerialized] - private PropertyChangedEventHandler _propertyChanged; - public event PropertyChangedEventHandler PropertyChanged - { - add => _propertyChanged += value; + /// + /// Event handlers (do not serialize!) + /// + [NonSerialized] private PropertyChangedEventHandler _propertyChanged; + + public event PropertyChangedEventHandler PropertyChanged + { + add => _propertyChanged += value; remove => _propertyChanged -= value; } - [NonSerialized] - private SurfaceElementEventHandler _movingElementChanged; - public event SurfaceElementEventHandler MovingElementChanged + [NonSerialized] private SurfaceElementEventHandler _movingElementChanged; + + public event SurfaceElementEventHandler MovingElementChanged { add => _movingElementChanged += value; remove => _movingElementChanged -= value; } - [NonSerialized] - private SurfaceDrawingModeEventHandler _drawingModeChanged; - public event SurfaceDrawingModeEventHandler DrawingModeChanged - { - add => _drawingModeChanged += value; + [NonSerialized] private SurfaceDrawingModeEventHandler _drawingModeChanged; + + public event SurfaceDrawingModeEventHandler DrawingModeChanged + { + add => _drawingModeChanged += value; remove => _drawingModeChanged -= value; } - [NonSerialized] - private SurfaceSizeChangeEventHandler _surfaceSizeChanged; - public event SurfaceSizeChangeEventHandler SurfaceSizeChanged - { - add => _surfaceSizeChanged += value; + + [NonSerialized] private SurfaceSizeChangeEventHandler _surfaceSizeChanged; + + public event SurfaceSizeChangeEventHandler SurfaceSizeChanged + { + add => _surfaceSizeChanged += value; remove => _surfaceSizeChanged -= value; } - [NonSerialized] - private SurfaceMessageEventHandler _surfaceMessage; - public event SurfaceMessageEventHandler SurfaceMessage - { - add => _surfaceMessage += value; + + [NonSerialized] private SurfaceMessageEventHandler _surfaceMessage; + + public event SurfaceMessageEventHandler SurfaceMessage + { + add => _surfaceMessage += value; remove => _surfaceMessage -= value; } - /// - /// inUndoRedo makes sure we don't undo/redo while in a undo/redo action - /// - [NonSerialized] - private bool _inUndoRedo; + /// + /// inUndoRedo makes sure we don't undo/redo while in a undo/redo action + /// + [NonSerialized] private bool _inUndoRedo; - /// - /// Make only one surface move cycle undoable, see SurfaceMouseMove - /// - [NonSerialized] - private bool _isSurfaceMoveMadeUndoable; + /// + /// Make only one surface move cycle undoable, see SurfaceMouseMove + /// + [NonSerialized] private bool _isSurfaceMoveMadeUndoable; - /// - /// Undo/Redo stacks, should not be serialized as the file would be way to big - /// - [NonSerialized] - private readonly Stack _undoStack = new Stack(); - [NonSerialized] - private readonly Stack _redoStack = new Stack(); + /// + /// Undo/Redo stacks, should not be serialized as the file would be way to big + /// + [NonSerialized] private readonly Stack _undoStack = new Stack(); - /// - /// Last save location, do not serialize! - /// - [NonSerialized] - private string _lastSaveFullPath; + [NonSerialized] private readonly Stack _redoStack = new Stack(); - /// - /// current drawing mode, do not serialize! - /// - [NonSerialized] - private DrawingModes _drawingMode = DrawingModes.None; + /// + /// Last save location, do not serialize! + /// + [NonSerialized] private string _lastSaveFullPath; - /// - /// the keys-locked flag helps with focus issues - /// - [NonSerialized] - private bool _keysLocked; + /// + /// current drawing mode, do not serialize! + /// + [NonSerialized] private DrawingModes _drawingMode = DrawingModes.None; - /// - /// Location of the mouse-down (it "starts" here), do not serialize - /// - [NonSerialized] - private Point _mouseStart = Point.Empty; + /// + /// the keys-locked flag helps with focus issues + /// + [NonSerialized] private bool _keysLocked; - /// - /// are we in a mouse down, do not serialize - /// - [NonSerialized] - private bool _mouseDown; + /// + /// Location of the mouse-down (it "starts" here), do not serialize + /// + [NonSerialized] private Point _mouseStart = Point.Empty; - /// - /// The selected element for the mouse down, do not serialize - /// - [NonSerialized] - private IDrawableContainer _mouseDownElement; + /// + /// are we in a mouse down, do not serialize + /// + [NonSerialized] private bool _mouseDown; - /// - /// all selected elements, do not serialize - /// - [NonSerialized] - private readonly IDrawableContainerList selectedElements; + /// + /// The selected element for the mouse down, do not serialize + /// + [NonSerialized] private IDrawableContainer _mouseDownElement; - /// - /// the element we are drawing with, do not serialize - /// - [NonSerialized] - private IDrawableContainer _drawingElement; + /// + /// all selected elements, do not serialize + /// + [NonSerialized] private readonly IDrawableContainerList selectedElements; - /// - /// the element we want to draw with (not yet drawn), do not serialize - /// - [NonSerialized] - private IDrawableContainer _undrawnElement; + /// + /// the element we are drawing with, do not serialize + /// + [NonSerialized] private IDrawableContainer _drawingElement; - /// - /// the cropcontainer, when cropping this is set, do not serialize - /// - [NonSerialized] - private IDrawableContainer _cropContainer; + /// + /// the element we want to draw with (not yet drawn), do not serialize + /// + [NonSerialized] private IDrawableContainer _undrawnElement; - /// - /// the brush which is used for transparent backgrounds, set by the editor, do not serialize - /// - [NonSerialized] - private Brush _transparencyBackgroundBrush; + /// + /// the cropcontainer, when cropping this is set, do not serialize + /// + [NonSerialized] private IDrawableContainer _cropContainer; - /// - /// The buffer is only for drawing on it when using filters (to supply access) - /// This saves a lot of "create new bitmap" commands - /// Should not be serialized, as it's generated. - /// The actual bitmap is in the paintbox... - /// TODO: Check if this buffer is still needed! - /// - [NonSerialized] - private Bitmap _buffer; + /// + /// the brush which is used for transparent backgrounds, set by the editor, do not serialize + /// + [NonSerialized] private Brush _transparencyBackgroundBrush; - /// - /// all stepLabels for the surface, needed with serialization - /// - private readonly List _stepLabels = new List(); + /// + /// The buffer is only for drawing on it when using filters (to supply access) + /// This saves a lot of "create new bitmap" commands + /// Should not be serialized, as it's generated. + /// The actual bitmap is in the paintbox... + /// TODO: Check if this buffer is still needed! + /// + [NonSerialized] private Bitmap _buffer; - public void AddStepLabel(StepLabelContainer stepLabel) - { - if (!_stepLabels.Contains(stepLabel)) - { - _stepLabels.Add(stepLabel); - } - } + /// + /// all stepLabels for the surface, needed with serialization + /// + private readonly List _stepLabels = new List(); - public void RemoveStepLabel(StepLabelContainer stepLabel) - { - _stepLabels.Remove(stepLabel); - } + public void AddStepLabel(StepLabelContainer stepLabel) + { + if (!_stepLabels.Contains(stepLabel)) + { + _stepLabels.Add(stepLabel); + } + } - /// - /// The start value of the counter objects - /// - public int CounterStart - { - get => _counterStart; + public void RemoveStepLabel(StepLabelContainer stepLabel) + { + _stepLabels.Remove(stepLabel); + } + + /// + /// The start value of the counter objects + /// + public int CounterStart + { + get => _counterStart; set - { - if (_counterStart == value) - { - return; - } + { + if (_counterStart == value) + { + return; + } - _counterStart = value; - Invalidate(); - _propertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(CounterStart))); - } - } + _counterStart = value; + Invalidate(); + _propertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(CounterStart))); + } + } - /// - /// Count all the VISIBLE steplabels in the surface, up to the supplied one - /// - /// can be null, if not the counting stops here - /// number of steplabels before the supplied container - public int CountStepLabels(IDrawableContainer stopAtContainer) - { - int number = CounterStart; - foreach (var possibleThis in _stepLabels) - { - if (possibleThis.Equals(stopAtContainer)) - { - break; - } - if (IsOnSurface(possibleThis)) - { - number++; - } - } - return number; - } + /// + /// Count all the VISIBLE steplabels in the surface, up to the supplied one + /// + /// can be null, if not the counting stops here + /// number of steplabels before the supplied container + public int CountStepLabels(IDrawableContainer stopAtContainer) + { + int number = CounterStart; + foreach (var possibleThis in _stepLabels) + { + if (possibleThis.Equals(stopAtContainer)) + { + break; + } - /// - /// all elements on the surface, needed with serialization - /// - private readonly IDrawableContainerList _elements; + if (IsOnSurface(possibleThis)) + { + number++; + } + } - /// - /// all elements on the surface, needed with serialization - /// - private FieldAggregator _fieldAggregator; + return number; + } - /// - /// the cursor container, needed with serialization as we need a direct acces to it. - /// - private IDrawableContainer _cursorContainer; + /// + /// all elements on the surface, needed with serialization + /// + private readonly IDrawableContainerList _elements; - /// - /// the modified flag specifies if the surface has had modifications after the last export. - /// Initial state is modified, as "it's not saved" - /// After serialization this should actually be "false" (the surface came from a stream) - /// For now we just serialize it... - /// - private bool _modified = true; + /// + /// all elements on the surface, needed with serialization + /// + private FieldAggregator _fieldAggregator; - /// - /// The image is the actual captured image, needed with serialization - /// - private Image _image; - public Image Image - { - get => _image; + /// + /// the cursor container, needed with serialization as we need a direct acces to it. + /// + private IDrawableContainer _cursorContainer; + + /// + /// the modified flag specifies if the surface has had modifications after the last export. + /// Initial state is modified, as "it's not saved" + /// After serialization this should actually be "false" (the surface came from a stream) + /// For now we just serialize it... + /// + private bool _modified = true; + + /// + /// The image is the actual captured image, needed with serialization + /// + private Image _image; + + public Image Image + { + get => _image; set - { - _image = value; - UpdateSize(); - } - } + { + _image = value; + UpdateSize(); + } + } - [NonSerialized] - private Matrix _zoomMatrix = new Matrix(1, 0, 0, 1, 0, 0); - [NonSerialized] - private Matrix _inverseZoomMatrix = new Matrix(1, 0, 0, 1, 0, 0); - [NonSerialized] - private Fraction _zoomFactor = Fraction.Identity; - public Fraction ZoomFactor - { - get => _zoomFactor; - set - { - _zoomFactor = value; - var inverse = _zoomFactor.Inverse(); - _zoomMatrix = new Matrix(_zoomFactor, 0, 0, _zoomFactor, 0, 0); - _inverseZoomMatrix = new Matrix(inverse, 0, 0, inverse, 0, 0); - UpdateSize(); - } - } + [NonSerialized] private Matrix _zoomMatrix = new Matrix(1, 0, 0, 1, 0, 0); + [NonSerialized] private Matrix _inverseZoomMatrix = new Matrix(1, 0, 0, 1, 0, 0); + [NonSerialized] private Fraction _zoomFactor = Fraction.Identity; + + public Fraction ZoomFactor + { + get => _zoomFactor; + set + { + _zoomFactor = value; + var inverse = _zoomFactor.Inverse(); + _zoomMatrix = new Matrix(_zoomFactor, 0, 0, _zoomFactor, 0, 0); + _inverseZoomMatrix = new Matrix(inverse, 0, 0, inverse, 0, 0); + UpdateSize(); + } + } - /// - /// Sets the surface size as zoomed image size. - /// - private void UpdateSize() - { - var size = _image.Size; - Size = new Size((int)(size.Width * _zoomFactor), (int)(size.Height * _zoomFactor)); - } + /// + /// Sets the surface size as zoomed image size. + /// + private void UpdateSize() + { + var size = _image.Size; + Size = new Size((int) (size.Width * _zoomFactor), (int) (size.Height * _zoomFactor)); + } - /// - /// The field aggregator is that which is used to have access to all the fields inside the currently selected elements. - /// e.g. used to decided if and which line thickness is shown when multiple elements are selected. - /// - public FieldAggregator FieldAggregator - { - get => _fieldAggregator; + /// + /// The field aggregator is that which is used to have access to all the fields inside the currently selected elements. + /// e.g. used to decided if and which line thickness is shown when multiple elements are selected. + /// + public FieldAggregator FieldAggregator + { + get => _fieldAggregator; set => _fieldAggregator = value; } - /// - /// The cursor container has it's own accessor so we can find and remove this (when needed) - /// - public IDrawableContainer CursorContainer => _cursorContainer; + /// + /// The cursor container has it's own accessor so we can find and remove this (when needed) + /// + public IDrawableContainer CursorContainer => _cursorContainer; - /// - /// A simple getter to ask if this surface has a cursor - /// - public bool HasCursor => _cursorContainer != null; + /// + /// A simple getter to ask if this surface has a cursor + /// + public bool HasCursor => _cursorContainer != null; - /// - /// A simple helper method to remove the cursor from the surface - /// - public void RemoveCursor() - { - RemoveElement(_cursorContainer); - _cursorContainer = null; - } + /// + /// A simple helper method to remove the cursor from the surface + /// + public void RemoveCursor() + { + RemoveElement(_cursorContainer); + _cursorContainer = null; + } - /// - /// The brush which is used to draw the transparent background - /// - public Brush TransparencyBackgroundBrush - { - get => _transparencyBackgroundBrush; + /// + /// The brush which is used to draw the transparent background + /// + public Brush TransparencyBackgroundBrush + { + get => _transparencyBackgroundBrush; set => _transparencyBackgroundBrush = value; } - /// - /// Are the keys on this surface locked? - /// - public bool KeysLocked - { - get => _keysLocked; + /// + /// Are the keys on this surface locked? + /// + public bool KeysLocked + { + get => _keysLocked; set => _keysLocked = value; } - /// - /// Is this surface modified? This is only true if the surface has not been exported. - /// - public bool Modified - { - get => _modified; + /// + /// Is this surface modified? This is only true if the surface has not been exported. + /// + public bool Modified + { + get => _modified; set => _modified = value; } - /// - /// The DrawingMode property specifies the mode for drawing, more or less the element type. - /// - public DrawingModes DrawingMode - { - get => _drawingMode; + /// + /// The DrawingMode property specifies the mode for drawing, more or less the element type. + /// + public DrawingModes DrawingMode + { + get => _drawingMode; set - { - _drawingMode = value; - if (_drawingModeChanged != null) - { - SurfaceDrawingModeEventArgs eventArgs = new SurfaceDrawingModeEventArgs - { - DrawingMode = _drawingMode - }; - _drawingModeChanged.Invoke(this, eventArgs); - } - DeselectAllElements(); - CreateUndrawnElement(); - } - } + { + _drawingMode = value; + if (_drawingModeChanged != null) + { + SurfaceDrawingModeEventArgs eventArgs = new SurfaceDrawingModeEventArgs + { + DrawingMode = _drawingMode + }; + _drawingModeChanged.Invoke(this, eventArgs); + } - /// - /// Property for accessing the last save "full" path - /// - public string LastSaveFullPath - { - get => _lastSaveFullPath; + DeselectAllElements(); + CreateUndrawnElement(); + } + } + + /// + /// Property for accessing the last save "full" path + /// + public string LastSaveFullPath + { + get => _lastSaveFullPath; set => _lastSaveFullPath = value; } - /// - /// Property for accessing the URL to which the surface was recently uploaded - /// - public string UploadUrl - { - get; - set; - } + /// + /// Property for accessing the URL to which the surface was recently uploaded + /// + public string UploadUrl { get; set; } - /// - /// Property for accessing the capture details - /// - public ICaptureDetails CaptureDetails { get; set; } + /// + /// Property for accessing the capture details + /// + public ICaptureDetails CaptureDetails { get; set; } - /// - /// Adjust UI elements to the supplied DPI settings - /// - /// - public void AdjustToDpi(uint dpi) - { - foreach (var element in this._elements) { - element.AdjustToDpi(dpi); - } - } + /// + /// Adjust UI elements to the supplied DPI settings + /// + /// + public void AdjustToDpi(uint dpi) + { + foreach (var element in this._elements) + { + element.AdjustToDpi(dpi); + } + } - /// - /// Base Surface constructor - /// - public Surface() - { - _fieldAggregator = new FieldAggregator(this); - Count++; - _elements = new DrawableContainerList(_uniqueId); - selectedElements = new DrawableContainerList(_uniqueId); - LOG.Debug("Creating surface!"); - MouseDown += SurfaceMouseDown; - MouseUp += SurfaceMouseUp; - MouseMove += SurfaceMouseMove; - MouseDoubleClick += SurfaceDoubleClick; - Paint += SurfacePaint; - AllowDrop = true; - DragDrop += OnDragDrop; - DragEnter += OnDragEnter; - // bind selected & elements to this, otherwise they can't inform of modifications - selectedElements.Parent = this; - _elements.Parent = this; - // Make sure we are visible - Visible = true; - TabStop = false; - // Enable double buffering - DoubleBuffered = true; - SetStyle(ControlStyles.UserPaint | ControlStyles.AllPaintingInWmPaint | ControlStyles.ResizeRedraw | ControlStyles.ContainerControl | ControlStyles.OptimizedDoubleBuffer | ControlStyles.SupportsTransparentBackColor, true); - } + /// + /// Base Surface constructor + /// + public Surface() + { + _fieldAggregator = new FieldAggregator(this); + Count++; + _elements = new DrawableContainerList(_uniqueId); + selectedElements = new DrawableContainerList(_uniqueId); + LOG.Debug("Creating surface!"); + MouseDown += SurfaceMouseDown; + MouseUp += SurfaceMouseUp; + MouseMove += SurfaceMouseMove; + MouseDoubleClick += SurfaceDoubleClick; + Paint += SurfacePaint; + AllowDrop = true; + DragDrop += OnDragDrop; + DragEnter += OnDragEnter; + // bind selected & elements to this, otherwise they can't inform of modifications + selectedElements.Parent = this; + _elements.Parent = this; + // Make sure we are visible + Visible = true; + TabStop = false; + // Enable double buffering + DoubleBuffered = true; + SetStyle( + ControlStyles.UserPaint | ControlStyles.AllPaintingInWmPaint | ControlStyles.ResizeRedraw | ControlStyles.ContainerControl | ControlStyles.OptimizedDoubleBuffer | + ControlStyles.SupportsTransparentBackColor, true); + } - /// - /// Private method, the current image is disposed the new one will stay. - /// - /// The new image - /// true if the old image needs to be disposed, when using undo this should not be true!! - private void SetImage(Image newImage, bool dispose) - { - // Dispose - if (_image != null && dispose) - { - _image.Dispose(); - } + /// + /// Private method, the current image is disposed the new one will stay. + /// + /// The new image + /// true if the old image needs to be disposed, when using undo this should not be true!! + private void SetImage(Image newImage, bool dispose) + { + // Dispose + if (_image != null && dispose) + { + _image.Dispose(); + } - // Set new values - Image = newImage; + // Set new values + Image = newImage; - _modified = true; - } + _modified = true; + } - /// - /// Surface constructor with an image - /// - /// - public Surface(Image newImage) : this() - { - LOG.DebugFormat("Got image with dimensions {0} and format {1}", newImage.Size, newImage.PixelFormat); - SetImage(newImage, true); - } + /// + /// Surface constructor with an image + /// + /// + public Surface(Image newImage) : this() + { + LOG.DebugFormat("Got image with dimensions {0} and format {1}", newImage.Size, newImage.PixelFormat); + SetImage(newImage, true); + } - /// - /// Surface contructor with a capture - /// - /// - public Surface(ICapture capture) : this(capture.Image) - { - // check if cursor is captured, and visible - if (capture.Cursor != null && capture.CursorVisible) - { - Rectangle cursorRect = new Rectangle(capture.CursorLocation, capture.Cursor.Size); - Rectangle captureRect = new Rectangle(Point.Empty, capture.Image.Size); - // check if cursor is on the capture, otherwise we leave it out. - if (cursorRect.IntersectsWith(captureRect)) - { - _cursorContainer = AddIconContainer(capture.Cursor, capture.CursorLocation.X, capture.CursorLocation.Y); - SelectElement(_cursorContainer); - } - } - // Make sure the image is NOT disposed, we took the reference directly into ourselves - ((Capture)capture).NullImage(); + /// + /// Surface contructor with a capture + /// + /// + public Surface(ICapture capture) : this(capture.Image) + { + // check if cursor is captured, and visible + if (capture.Cursor != null && capture.CursorVisible) + { + Rectangle cursorRect = new Rectangle(capture.CursorLocation, capture.Cursor.Size); + Rectangle captureRect = new Rectangle(Point.Empty, capture.Image.Size); + // check if cursor is on the capture, otherwise we leave it out. + if (cursorRect.IntersectsWith(captureRect)) + { + _cursorContainer = AddIconContainer(capture.Cursor, capture.CursorLocation.X, capture.CursorLocation.Y); + SelectElement(_cursorContainer); + } + } - CaptureDetails = capture.CaptureDetails; - } + // Make sure the image is NOT disposed, we took the reference directly into ourselves + ((Capture) capture).NullImage(); - protected override void Dispose(bool disposing) - { - if (disposing) - { - Count--; - LOG.Debug("Disposing surface!"); - if (_buffer != null) - { - _buffer.Dispose(); - _buffer = null; - } - if (_transparencyBackgroundBrush != null) - { - _transparencyBackgroundBrush.Dispose(); - _transparencyBackgroundBrush = null; - } + CaptureDetails = capture.CaptureDetails; + } - // Cleanup undo/redo stacks - while (_undoStack != null && _undoStack.Count > 0) - { - _undoStack.Pop().Dispose(); - } - while (_redoStack != null && _redoStack.Count > 0) - { - _redoStack.Pop().Dispose(); - } - foreach (IDrawableContainer container in _elements) - { - container.Dispose(); - } - if (_undrawnElement != null) - { - _undrawnElement.Dispose(); - _undrawnElement = null; - } - if (_cropContainer != null) - { - _cropContainer.Dispose(); - _cropContainer = null; - } - } - base.Dispose(disposing); - } + protected override void Dispose(bool disposing) + { + if (disposing) + { + Count--; + LOG.Debug("Disposing surface!"); + if (_buffer != null) + { + _buffer.Dispose(); + _buffer = null; + } - /// - /// Undo the last action - /// - public void Undo() - { - if (_undoStack.Count > 0) - { - _inUndoRedo = true; - IMemento top = _undoStack.Pop(); - _redoStack.Push(top.Restore()); - _inUndoRedo = false; - } - } + if (_transparencyBackgroundBrush != null) + { + _transparencyBackgroundBrush.Dispose(); + _transparencyBackgroundBrush = null; + } - /// - /// Undo an undo (=redo) - /// - public void Redo() - { - if (_redoStack.Count > 0) - { - _inUndoRedo = true; - IMemento top = _redoStack.Pop(); - _undoStack.Push(top.Restore()); - _inUndoRedo = false; - } - } + // Cleanup undo/redo stacks + while (_undoStack != null && _undoStack.Count > 0) + { + _undoStack.Pop().Dispose(); + } - /// - /// Returns if the surface can do a undo - /// - public bool CanUndo => _undoStack.Count > 0; + while (_redoStack != null && _redoStack.Count > 0) + { + _redoStack.Pop().Dispose(); + } - /// - /// Returns if the surface can do a redo - /// - public bool CanRedo => _redoStack.Count > 0; + foreach (IDrawableContainer container in _elements) + { + container.Dispose(); + } - /// - /// Get the language key for the undo action - /// - public LangKey UndoActionLanguageKey => LangKey.none; + if (_undrawnElement != null) + { + _undrawnElement.Dispose(); + _undrawnElement = null; + } - /// - /// Get the language key for redo action - /// - public LangKey RedoActionLanguageKey => LangKey.none; + if (_cropContainer != null) + { + _cropContainer.Dispose(); + _cropContainer = null; + } + } - /// - /// Make an action undo-able - /// - /// The memento implementing the undo - /// Allow changes to be merged - public void MakeUndoable(IMemento memento, bool allowMerge) - { - if (_inUndoRedo) - { - throw new InvalidOperationException("Invoking do within an undo/redo action."); - } - if (memento != null) - { - bool allowPush = true; - if (_undoStack.Count > 0 && allowMerge) - { - // Check if merge is possible - allowPush = !_undoStack.Peek().Merge(memento); - } - if (allowPush) - { - // Clear the redo-stack and dispose - while (_redoStack.Count > 0) - { - _redoStack.Pop().Dispose(); - } - _undoStack.Push(memento); - } - } - } + base.Dispose(disposing); + } - /// - /// This saves the elements of this surface to a stream. - /// Is used to save a template of the complete surface - /// - /// - /// - public long SaveElementsToStream(Stream streamWrite) - { - long bytesWritten = 0; - try - { - long lengtBefore = streamWrite.Length; - BinaryFormatter binaryWrite = new BinaryFormatter(); - binaryWrite.Serialize(streamWrite, _elements); - bytesWritten = streamWrite.Length - lengtBefore; - } - catch (Exception e) - { - LOG.Error("Error serializing elements to stream.", e); - } - return bytesWritten; - } + /// + /// Undo the last action + /// + public void Undo() + { + if (_undoStack.Count > 0) + { + _inUndoRedo = true; + IMemento top = _undoStack.Pop(); + _redoStack.Push(top.Restore()); + _inUndoRedo = false; + } + } - /// - /// This loads elements from a stream, among others this is used to load a surface. - /// - /// - public void LoadElementsFromStream(Stream streamRead) - { - try - { - BinaryFormatter binaryRead = new BinaryFormatter(); - IDrawableContainerList loadedElements = (IDrawableContainerList)binaryRead.Deserialize(streamRead); - loadedElements.Parent = this; - // Make sure the steplabels are sorted accoring to their number - _stepLabels.Sort((p1, p2) => p1.Number.CompareTo(p2.Number)); - DeselectAllElements(); - AddElements(loadedElements); - SelectElements(loadedElements); - FieldAggregator.BindElements(loadedElements); - } - catch (Exception e) - { - LOG.Error("Error serializing elements from stream.", e); - } - } + /// + /// Undo an undo (=redo) + /// + public void Redo() + { + if (_redoStack.Count > 0) + { + _inUndoRedo = true; + IMemento top = _redoStack.Pop(); + _undoStack.Push(top.Restore()); + _inUndoRedo = false; + } + } - /// - /// This is called from the DrawingMode setter, which is not very correct... - /// But here an element is created which is not yet draw, thus "undrawnElement". - /// The element is than used while drawing on the surface. - /// - private void CreateUndrawnElement() - { - if (_undrawnElement != null) - { - FieldAggregator.UnbindElement(_undrawnElement); - } - switch (DrawingMode) - { - case DrawingModes.Rect: - _undrawnElement = new RectangleContainer(this); - break; - case DrawingModes.Ellipse: - _undrawnElement = new EllipseContainer(this); - break; - case DrawingModes.Text: - _undrawnElement = new TextContainer(this); - break; - case DrawingModes.SpeechBubble: - _undrawnElement = new SpeechbubbleContainer(this); - break; - case DrawingModes.StepLabel: - _undrawnElement = new StepLabelContainer(this); - break; - case DrawingModes.Line: - _undrawnElement = new LineContainer(this); - break; - case DrawingModes.Arrow: - _undrawnElement = new ArrowContainer(this); - break; - case DrawingModes.Highlight: - _undrawnElement = new HighlightContainer(this); - break; - case DrawingModes.Obfuscate: - _undrawnElement = new ObfuscateContainer(this); - break; - case DrawingModes.Crop: - _cropContainer = new CropContainer(this); - _undrawnElement = _cropContainer; - break; - case DrawingModes.Bitmap: - _undrawnElement = new ImageContainer(this); - break; - case DrawingModes.Path: - _undrawnElement = new FreehandContainer(this); - break; - case DrawingModes.None: - _undrawnElement = null; - break; - } - if (_undrawnElement != null) - { - FieldAggregator.BindElement(_undrawnElement); - } - } + /// + /// Returns if the surface can do a undo + /// + public bool CanUndo => _undoStack.Count > 0; - #region Plugin interface implementations - public IImageContainer AddImageContainer(Image image, int x, int y) - { - ImageContainer bitmapContainer = new ImageContainer(this) - { - Image = image, - Left = x, - Top = y - }; - AddElement(bitmapContainer); - return bitmapContainer; - } + /// + /// Returns if the surface can do a redo + /// + public bool CanRedo => _redoStack.Count > 0; - public IImageContainer AddImageContainer(string filename, int x, int y) - { - ImageContainer bitmapContainer = new ImageContainer(this); - bitmapContainer.Load(filename); - bitmapContainer.Left = x; - bitmapContainer.Top = y; - AddElement(bitmapContainer); - return bitmapContainer; - } - public IIconContainer AddIconContainer(Icon icon, int x, int y) - { - IconContainer iconContainer = new IconContainer(this) - { - Icon = icon, - Left = x, - Top = y - }; - AddElement(iconContainer); - return iconContainer; - } - public IIconContainer AddIconContainer(string filename, int x, int y) - { - IconContainer iconContainer = new IconContainer(this); - iconContainer.Load(filename); - iconContainer.Left = x; - iconContainer.Top = y; - AddElement(iconContainer); - return iconContainer; - } - public ICursorContainer AddCursorContainer(Cursor cursor, int x, int y) - { - CursorContainer cursorContainer = new CursorContainer(this) - { - Cursor = cursor, - Left = x, - Top = y - }; - AddElement(cursorContainer); - return cursorContainer; - } - public ICursorContainer AddCursorContainer(string filename, int x, int y) - { - CursorContainer cursorContainer = new CursorContainer(this); - cursorContainer.Load(filename); - cursorContainer.Left = x; - cursorContainer.Top = y; - AddElement(cursorContainer); - return cursorContainer; - } + /// + /// Get the language key for the undo action + /// + public LangKey UndoActionLanguageKey => LangKey.none; - public ITextContainer AddTextContainer(string text, int x, int y, FontFamily family, float size, bool italic, bool bold, bool shadow, int borderSize, Color color, Color fillColor) - { - TextContainer textContainer = new TextContainer(this) {Text = text, Left = x, Top = y}; - textContainer.SetFieldValue(FieldType.FONT_FAMILY, family.Name); - textContainer.SetFieldValue(FieldType.FONT_BOLD, bold); - textContainer.SetFieldValue(FieldType.FONT_ITALIC, italic); - textContainer.SetFieldValue(FieldType.FONT_SIZE, size); - textContainer.SetFieldValue(FieldType.FILL_COLOR, fillColor); - textContainer.SetFieldValue(FieldType.LINE_COLOR, color); - textContainer.SetFieldValue(FieldType.LINE_THICKNESS, borderSize); - textContainer.SetFieldValue(FieldType.SHADOW, shadow); - // Make sure the Text fits - textContainer.FitToText(); + /// + /// Get the language key for redo action + /// + public LangKey RedoActionLanguageKey => LangKey.none; - //AggregatedProperties.UpdateElement(textContainer); - AddElement(textContainer); - return textContainer; - } - #endregion + /// + /// Make an action undo-able + /// + /// The memento implementing the undo + /// Allow changes to be merged + public void MakeUndoable(IMemento memento, bool allowMerge) + { + if (_inUndoRedo) + { + throw new InvalidOperationException("Invoking do within an undo/redo action."); + } - #region DragDrop + if (memento != null) + { + bool allowPush = true; + if (_undoStack.Count > 0 && allowMerge) + { + // Check if merge is possible + allowPush = !_undoStack.Peek().Merge(memento); + } - private void OnDragEnter(object sender, DragEventArgs e) - { - if (LOG.IsDebugEnabled) - { - LOG.Debug("DragEnter got following formats: "); - foreach (string format in ClipboardHelper.GetFormats(e.Data)) - { - LOG.Debug(format); - } - } - if ((e.AllowedEffect & DragDropEffects.Copy) != DragDropEffects.Copy) - { - e.Effect = DragDropEffects.None; - } - else - { - if (ClipboardHelper.ContainsImage(e.Data) || ClipboardHelper.ContainsFormat(e.Data, "DragImageBits")) - { - e.Effect = DragDropEffects.Copy; - } - else - { - e.Effect = DragDropEffects.None; - } - } - } + if (allowPush) + { + // Clear the redo-stack and dispose + while (_redoStack.Count > 0) + { + _redoStack.Pop().Dispose(); + } - /// - /// Handle the drag/drop - /// - /// - /// - private void OnDragDrop(object sender, DragEventArgs e) - { - Point mouse = PointToClient(new Point(e.X, e.Y)); - if (e.Data.GetDataPresent("Text")) - { - string possibleUrl = ClipboardHelper.GetText(e.Data); - // Test if it's an url and try to download the image so we have it in the original form - if (possibleUrl != null && possibleUrl.StartsWith("http")) + _undoStack.Push(memento); + } + } + } + + /// + /// This saves the elements of this surface to a stream. + /// Is used to save a template of the complete surface + /// + /// + /// + public long SaveElementsToStream(Stream streamWrite) + { + long bytesWritten = 0; + try + { + long lengtBefore = streamWrite.Length; + BinaryFormatter binaryWrite = new BinaryFormatter(); + binaryWrite.Serialize(streamWrite, _elements); + bytesWritten = streamWrite.Length - lengtBefore; + } + catch (Exception e) + { + LOG.Error("Error serializing elements to stream.", e); + } + + return bytesWritten; + } + + /// + /// This loads elements from a stream, among others this is used to load a surface. + /// + /// + public void LoadElementsFromStream(Stream streamRead) + { + try + { + BinaryFormatter binaryRead = new BinaryFormatter(); + IDrawableContainerList loadedElements = (IDrawableContainerList) binaryRead.Deserialize(streamRead); + loadedElements.Parent = this; + // Make sure the steplabels are sorted accoring to their number + _stepLabels.Sort((p1, p2) => p1.Number.CompareTo(p2.Number)); + DeselectAllElements(); + AddElements(loadedElements); + SelectElements(loadedElements); + FieldAggregator.BindElements(loadedElements); + } + catch (Exception e) + { + LOG.Error("Error serializing elements from stream.", e); + } + } + + /// + /// This is called from the DrawingMode setter, which is not very correct... + /// But here an element is created which is not yet draw, thus "undrawnElement". + /// The element is than used while drawing on the surface. + /// + private void CreateUndrawnElement() + { + if (_undrawnElement != null) + { + FieldAggregator.UnbindElement(_undrawnElement); + } + + switch (DrawingMode) + { + case DrawingModes.Rect: + _undrawnElement = new RectangleContainer(this); + break; + case DrawingModes.Ellipse: + _undrawnElement = new EllipseContainer(this); + break; + case DrawingModes.Text: + _undrawnElement = new TextContainer(this); + break; + case DrawingModes.SpeechBubble: + _undrawnElement = new SpeechbubbleContainer(this); + break; + case DrawingModes.StepLabel: + _undrawnElement = new StepLabelContainer(this); + break; + case DrawingModes.Line: + _undrawnElement = new LineContainer(this); + break; + case DrawingModes.Arrow: + _undrawnElement = new ArrowContainer(this); + break; + case DrawingModes.Highlight: + _undrawnElement = new HighlightContainer(this); + break; + case DrawingModes.Obfuscate: + _undrawnElement = new ObfuscateContainer(this); + break; + case DrawingModes.Crop: + _cropContainer = new CropContainer(this); + _undrawnElement = _cropContainer; + break; + case DrawingModes.Bitmap: + _undrawnElement = new ImageContainer(this); + break; + case DrawingModes.Path: + _undrawnElement = new FreehandContainer(this); + break; + case DrawingModes.None: + _undrawnElement = null; + break; + } + + if (_undrawnElement != null) + { + FieldAggregator.BindElement(_undrawnElement); + } + } + + #region Plugin interface implementations + + public IImageContainer AddImageContainer(Image image, int x, int y) + { + ImageContainer bitmapContainer = new ImageContainer(this) + { + Image = image, + Left = x, + Top = y + }; + AddElement(bitmapContainer); + return bitmapContainer; + } + + public IImageContainer AddImageContainer(string filename, int x, int y) + { + ImageContainer bitmapContainer = new ImageContainer(this); + bitmapContainer.Load(filename); + bitmapContainer.Left = x; + bitmapContainer.Top = y; + AddElement(bitmapContainer); + return bitmapContainer; + } + + public IIconContainer AddIconContainer(Icon icon, int x, int y) + { + IconContainer iconContainer = new IconContainer(this) + { + Icon = icon, + Left = x, + Top = y + }; + AddElement(iconContainer); + return iconContainer; + } + + public IIconContainer AddIconContainer(string filename, int x, int y) + { + IconContainer iconContainer = new IconContainer(this); + iconContainer.Load(filename); + iconContainer.Left = x; + iconContainer.Top = y; + AddElement(iconContainer); + return iconContainer; + } + + public ICursorContainer AddCursorContainer(Cursor cursor, int x, int y) + { + CursorContainer cursorContainer = new CursorContainer(this) + { + Cursor = cursor, + Left = x, + Top = y + }; + AddElement(cursorContainer); + return cursorContainer; + } + + public ICursorContainer AddCursorContainer(string filename, int x, int y) + { + CursorContainer cursorContainer = new CursorContainer(this); + cursorContainer.Load(filename); + cursorContainer.Left = x; + cursorContainer.Top = y; + AddElement(cursorContainer); + return cursorContainer; + } + + public ITextContainer AddTextContainer(string text, int x, int y, FontFamily family, float size, bool italic, bool bold, bool shadow, int borderSize, Color color, + Color fillColor) + { + TextContainer textContainer = new TextContainer(this) + { + Text = text, + Left = x, + Top = y + }; + textContainer.SetFieldValue(FieldType.FONT_FAMILY, family.Name); + textContainer.SetFieldValue(FieldType.FONT_BOLD, bold); + textContainer.SetFieldValue(FieldType.FONT_ITALIC, italic); + textContainer.SetFieldValue(FieldType.FONT_SIZE, size); + textContainer.SetFieldValue(FieldType.FILL_COLOR, fillColor); + textContainer.SetFieldValue(FieldType.LINE_COLOR, color); + textContainer.SetFieldValue(FieldType.LINE_THICKNESS, borderSize); + textContainer.SetFieldValue(FieldType.SHADOW, shadow); + // Make sure the Text fits + textContainer.FitToText(); + + //AggregatedProperties.UpdateElement(textContainer); + AddElement(textContainer); + return textContainer; + } + + #endregion + + #region DragDrop + + private void OnDragEnter(object sender, DragEventArgs e) + { + if (LOG.IsDebugEnabled) + { + LOG.Debug("DragEnter got following formats: "); + foreach (string format in ClipboardHelper.GetFormats(e.Data)) + { + LOG.Debug(format); + } + } + + if ((e.AllowedEffect & DragDropEffects.Copy) != DragDropEffects.Copy) + { + e.Effect = DragDropEffects.None; + } + else + { + if (ClipboardHelper.ContainsImage(e.Data) || ClipboardHelper.ContainsFormat(e.Data, "DragImageBits")) + { + e.Effect = DragDropEffects.Copy; + } + else + { + e.Effect = DragDropEffects.None; + } + } + } + + /// + /// Handle the drag/drop + /// + /// + /// + private void OnDragDrop(object sender, DragEventArgs e) + { + Point mouse = PointToClient(new Point(e.X, e.Y)); + if (e.Data.GetDataPresent("Text")) + { + string possibleUrl = ClipboardHelper.GetText(e.Data); + // Test if it's an url and try to download the image so we have it in the original form + if (possibleUrl != null && possibleUrl.StartsWith("http")) { using Image image = NetworkHelper.DownloadImage(possibleUrl); if (image != null) @@ -888,1377 +902,1466 @@ namespace Greenshot.Drawing return; } } - } + } - foreach (Image image in ClipboardHelper.GetImages(e.Data)) - { - AddImageContainer(image, mouse.X, mouse.Y); - mouse.Offset(10, 10); - image.Dispose(); - } - } + foreach (Image image in ClipboardHelper.GetImages(e.Data)) + { + AddImageContainer(image, mouse.X, mouse.Y); + mouse.Offset(10, 10); + image.Dispose(); + } + } - #endregion + #endregion - /// - /// Auto crop the image - /// - /// true if cropped - public bool AutoCrop() - { - Rectangle cropRectangle; - using (Image tmpImage = GetImageForExport()) - { - cropRectangle = ImageHelper.FindAutoCropRectangle(tmpImage, conf.AutoCropDifference); - } - if (!IsCropPossible(ref cropRectangle)) - { - return false; - } - DeselectAllElements(); - // Maybe a bit obscure, but the following line creates a drop container - // It's available as "undrawnElement" - DrawingMode = DrawingModes.Crop; - _undrawnElement.Left = cropRectangle.X; - _undrawnElement.Top = cropRectangle.Y; - _undrawnElement.Width = cropRectangle.Width; - _undrawnElement.Height = cropRectangle.Height; - _undrawnElement.Status = EditStatus.UNDRAWN; - AddElement(_undrawnElement); - SelectElement(_undrawnElement); - _drawingElement = null; - _undrawnElement = null; - return true; - } + /// + /// Auto crop the image + /// + /// true if cropped + public bool AutoCrop() + { + Rectangle cropRectangle; + using (Image tmpImage = GetImageForExport()) + { + cropRectangle = ImageHelper.FindAutoCropRectangle(tmpImage, conf.AutoCropDifference); + } - /// - /// A simple clear - /// - /// The color for the background - public void Clear(Color newColor) - { - //create a blank bitmap the same size as original - Bitmap newBitmap = ImageHelper.CreateEmptyLike(Image, Color.Empty); - if (newBitmap != null) - { - // Make undoable - MakeUndoable(new SurfaceBackgroundChangeMemento(this, null), false); - SetImage(newBitmap, false); - Invalidate(); - } - } + if (!IsCropPossible(ref cropRectangle)) + { + return false; + } - /// - /// Apply a bitmap effect to the surface - /// - /// - public void ApplyBitmapEffect(IEffect effect) - { - BackgroundForm backgroundForm = new BackgroundForm("Effect", "Please wait"); - backgroundForm.Show(); - Application.DoEvents(); - try - { - Rectangle imageRectangle = new Rectangle(Point.Empty, Image.Size); - Matrix matrix = new Matrix(); - Image newImage = ImageHelper.ApplyEffect(Image, effect, matrix); - if (newImage != null) - { - // Make sure the elements move according to the offset the effect made the bitmap move - _elements.Transform(matrix); - // Make undoable - MakeUndoable(new SurfaceBackgroundChangeMemento(this, matrix), false); - SetImage(newImage, false); - Invalidate(); - if (_surfaceSizeChanged != null && !imageRectangle.Equals(new Rectangle(Point.Empty, newImage.Size))) - { - _surfaceSizeChanged(this, null); - } - } - else - { - // clean up matrix, as it hasn't been used in the undo stack. - matrix.Dispose(); - } - } - finally - { - // Always close the background form - backgroundForm.CloseDialog(); - } - } + DeselectAllElements(); + // Maybe a bit obscure, but the following line creates a drop container + // It's available as "undrawnElement" + DrawingMode = DrawingModes.Crop; + _undrawnElement.Left = cropRectangle.X; + _undrawnElement.Top = cropRectangle.Y; + _undrawnElement.Width = cropRectangle.Width; + _undrawnElement.Height = cropRectangle.Height; + _undrawnElement.Status = EditStatus.UNDRAWN; + AddElement(_undrawnElement); + SelectElement(_undrawnElement); + _drawingElement = null; + _undrawnElement = null; + return true; + } - /// - /// check if a crop is possible - /// - /// - /// true if this is possible - public bool IsCropPossible(ref Rectangle cropRectangle) - { - cropRectangle = GuiRectangle.GetGuiRectangle(cropRectangle.Left, cropRectangle.Top, cropRectangle.Width, cropRectangle.Height); - if (cropRectangle.Left < 0) - { - cropRectangle = new Rectangle(0, cropRectangle.Top, cropRectangle.Width + cropRectangle.Left, cropRectangle.Height); - } - if (cropRectangle.Top < 0) - { - cropRectangle = new Rectangle(cropRectangle.Left, 0, cropRectangle.Width, cropRectangle.Height + cropRectangle.Top); - } - if (cropRectangle.Left + cropRectangle.Width > Image.Width) - { - cropRectangle = new Rectangle(cropRectangle.Left, cropRectangle.Top, Image.Width - cropRectangle.Left, cropRectangle.Height); - } - if (cropRectangle.Top + cropRectangle.Height > Image.Height) - { - cropRectangle = new Rectangle(cropRectangle.Left, cropRectangle.Top, cropRectangle.Width, Image.Height - cropRectangle.Top); - } - if (cropRectangle.Height > 0 && cropRectangle.Width > 0) - { - return true; - } - return false; - } + /// + /// A simple clear + /// + /// The color for the background + public void Clear(Color newColor) + { + //create a blank bitmap the same size as original + Bitmap newBitmap = ImageHelper.CreateEmptyLike(Image, Color.Empty); + if (newBitmap != null) + { + // Make undoable + MakeUndoable(new SurfaceBackgroundChangeMemento(this, null), false); + SetImage(newBitmap, false); + Invalidate(); + } + } - /// - /// Use to send any registered SurfaceMessageEventHandler a message, e.g. used for the notification area - /// - /// Who send - /// Type of message - /// Message itself - public void SendMessageEvent(object source, SurfaceMessageTyp messageType, string message) - { - if (_surfaceMessage != null) - { - var eventArgs = new SurfaceMessageEventArgs - { - Message = message, - MessageType = messageType, - Surface = this - }; - _surfaceMessage(source, eventArgs); - } - } + /// + /// Apply a bitmap effect to the surface + /// + /// + public void ApplyBitmapEffect(IEffect effect) + { + BackgroundForm backgroundForm = new BackgroundForm("Effect", "Please wait"); + backgroundForm.Show(); + Application.DoEvents(); + try + { + Rectangle imageRectangle = new Rectangle(Point.Empty, Image.Size); + Matrix matrix = new Matrix(); + Image newImage = ImageHelper.ApplyEffect(Image, effect, matrix); + if (newImage != null) + { + // Make sure the elements move according to the offset the effect made the bitmap move + _elements.Transform(matrix); + // Make undoable + MakeUndoable(new SurfaceBackgroundChangeMemento(this, matrix), false); + SetImage(newImage, false); + Invalidate(); + if (_surfaceSizeChanged != null && !imageRectangle.Equals(new Rectangle(Point.Empty, newImage.Size))) + { + _surfaceSizeChanged(this, null); + } + } + else + { + // clean up matrix, as it hasn't been used in the undo stack. + matrix.Dispose(); + } + } + finally + { + // Always close the background form + backgroundForm.CloseDialog(); + } + } - /// - /// Crop the surface - /// - /// - /// - public bool ApplyCrop(Rectangle cropRectangle) - { - if (IsCropPossible(ref cropRectangle)) - { - Rectangle imageRectangle = new Rectangle(Point.Empty, Image.Size); - Bitmap tmpImage; - // Make sure we have information, this this fails - try - { - tmpImage = ImageHelper.CloneArea(Image, cropRectangle, PixelFormat.DontCare); - } - catch (Exception ex) - { - ex.Data.Add("CropRectangle", cropRectangle); - ex.Data.Add("Width", Image.Width); - ex.Data.Add("Height", Image.Height); - ex.Data.Add("Pixelformat", Image.PixelFormat); - throw; - } + /// + /// check if a crop is possible + /// + /// + /// true if this is possible + public bool IsCropPossible(ref Rectangle cropRectangle) + { + cropRectangle = GuiRectangle.GetGuiRectangle(cropRectangle.Left, cropRectangle.Top, cropRectangle.Width, cropRectangle.Height); + if (cropRectangle.Left < 0) + { + cropRectangle = new Rectangle(0, cropRectangle.Top, cropRectangle.Width + cropRectangle.Left, cropRectangle.Height); + } - Matrix matrix = new Matrix(); - matrix.Translate(-cropRectangle.Left, -cropRectangle.Top, MatrixOrder.Append); - // Make undoable - MakeUndoable(new SurfaceBackgroundChangeMemento(this, matrix), false); + if (cropRectangle.Top < 0) + { + cropRectangle = new Rectangle(cropRectangle.Left, 0, cropRectangle.Width, cropRectangle.Height + cropRectangle.Top); + } - // Do not dispose otherwise we can't undo the image! - SetImage(tmpImage, false); - _elements.Transform(matrix); - if (_surfaceSizeChanged != null && !imageRectangle.Equals(new Rectangle(Point.Empty, tmpImage.Size))) - { - _surfaceSizeChanged(this, null); - } - Invalidate(); - return true; - } - return false; - } + if (cropRectangle.Left + cropRectangle.Width > Image.Width) + { + cropRectangle = new Rectangle(cropRectangle.Left, cropRectangle.Top, Image.Width - cropRectangle.Left, cropRectangle.Height); + } - /// - /// The background here is the captured image. - /// This is called from the SurfaceBackgroundChangeMemento. - /// - /// - /// - public void UndoBackgroundChange(Image previous, Matrix matrix) - { - SetImage(previous, false); - if (matrix != null) - { - _elements.Transform(matrix); - } - _surfaceSizeChanged?.Invoke(this, null); - Invalidate(); - } - /// - /// Check if an adorner was "hit", and change the cursor if so - /// - /// MouseEventArgs - /// IAdorner - private IAdorner FindActiveAdorner(MouseEventArgs mouseEventArgs) - { - foreach (IDrawableContainer drawableContainer in selectedElements) - { - foreach (IAdorner adorner in drawableContainer.Adorners) - { + if (cropRectangle.Top + cropRectangle.Height > Image.Height) + { + cropRectangle = new Rectangle(cropRectangle.Left, cropRectangle.Top, cropRectangle.Width, Image.Height - cropRectangle.Top); + } - if (adorner.IsActive || adorner.HitTest(mouseEventArgs.Location)) - { - if (adorner.Cursor != null) - { - Cursor = adorner.Cursor; - } - return adorner; - } - } - } - return null; - } + if (cropRectangle.Height > 0 && cropRectangle.Width > 0) + { + return true; + } - /// - /// Translate mouse coordinates as if they were applied directly to unscaled image. - /// - private MouseEventArgs InverseZoomMouseCoordinates(MouseEventArgs e) - => new MouseEventArgs(e.Button, e.Clicks, (int)(e.X / _zoomFactor), (int)(e.Y / _zoomFactor), e.Delta); + return false; + } - /// - /// This event handler is called when someone presses the mouse on a surface. - /// - /// - /// - private void SurfaceMouseDown(object sender, MouseEventArgs e) - { - e = InverseZoomMouseCoordinates(e); + /// + /// Use to send any registered SurfaceMessageEventHandler a message, e.g. used for the notification area + /// + /// Who send + /// Type of message + /// Message itself + public void SendMessageEvent(object source, SurfaceMessageTyp messageType, string message) + { + if (_surfaceMessage != null) + { + var eventArgs = new SurfaceMessageEventArgs + { + Message = message, + MessageType = messageType, + Surface = this + }; + _surfaceMessage(source, eventArgs); + } + } - // Handle Adorners - var adorner = FindActiveAdorner(e); - if (adorner != null) - { - adorner.MouseDown(sender, e); - return; - } + /// + /// Crop the surface + /// + /// + /// + public bool ApplyCrop(Rectangle cropRectangle) + { + if (IsCropPossible(ref cropRectangle)) + { + Rectangle imageRectangle = new Rectangle(Point.Empty, Image.Size); + Bitmap tmpImage; + // Make sure we have information, this this fails + try + { + tmpImage = ImageHelper.CloneArea(Image, cropRectangle, PixelFormat.DontCare); + } + catch (Exception ex) + { + ex.Data.Add("CropRectangle", cropRectangle); + ex.Data.Add("Width", Image.Width); + ex.Data.Add("Height", Image.Height); + ex.Data.Add("Pixelformat", Image.PixelFormat); + throw; + } - _mouseStart = e.Location; + Matrix matrix = new Matrix(); + matrix.Translate(-cropRectangle.Left, -cropRectangle.Top, MatrixOrder.Append); + // Make undoable + MakeUndoable(new SurfaceBackgroundChangeMemento(this, matrix), false); - // check contextmenu - if (e.Button == MouseButtons.Right) - { - IDrawableContainerList selectedList = null; - if (selectedElements != null && selectedElements.Count > 0) - { - selectedList = selectedElements; - } - else - { - // Single element - IDrawableContainer rightClickedContainer = _elements.ClickableElementAt(_mouseStart.X, _mouseStart.Y); - if (rightClickedContainer != null) - { - selectedList = new DrawableContainerList(ID) {rightClickedContainer}; - } - } - if (selectedList != null && selectedList.Count > 0) - { - selectedList.ShowContextMenu(e, this); - } - return; - } + // Do not dispose otherwise we can't undo the image! + SetImage(tmpImage, false); + _elements.Transform(matrix); + if (_surfaceSizeChanged != null && !imageRectangle.Equals(new Rectangle(Point.Empty, tmpImage.Size))) + { + _surfaceSizeChanged(this, null); + } - _mouseDown = true; - _isSurfaceMoveMadeUndoable = false; + Invalidate(); + return true; + } - if (_cropContainer != null && ((_undrawnElement == null) || (_undrawnElement != null && DrawingMode != DrawingModes.Crop))) - { - RemoveElement(_cropContainer, false); - _cropContainer = null; - _drawingElement = null; - } + return false; + } - if (_drawingElement == null && DrawingMode != DrawingModes.None) - { - if (_undrawnElement == null) - { - DeselectAllElements(); - if (_undrawnElement == null) - { - CreateUndrawnElement(); - } - } - _drawingElement = _undrawnElement; - // if a new element has been drawn, set location and register it - if (_drawingElement != null) - { - if (_undrawnElement != null) - { - _drawingElement.Status = _undrawnElement.DefaultEditMode; - } - if (!_drawingElement.HandleMouseDown(_mouseStart.X, _mouseStart.Y)) - { - _drawingElement.Left = _mouseStart.X; - _drawingElement.Top = _mouseStart.Y; - } - AddElement(_drawingElement); - _drawingElement.Selected = true; - } - _undrawnElement = null; - } - else - { - // check whether an existing element was clicked - // we save mouse down element separately from selectedElements (checked on mouse up), - // since it could be moved around before it is actually selected - _mouseDownElement = _elements.ClickableElementAt(_mouseStart.X, _mouseStart.Y); + /// + /// The background here is the captured image. + /// This is called from the SurfaceBackgroundChangeMemento. + /// + /// + /// + public void UndoBackgroundChange(Image previous, Matrix matrix) + { + SetImage(previous, false); + if (matrix != null) + { + _elements.Transform(matrix); + } - if (_mouseDownElement != null) - { - _mouseDownElement.Status = EditStatus.MOVING; - } - } - } + _surfaceSizeChanged?.Invoke(this, null); + Invalidate(); + } - /// - /// This event handle is called when the mouse button is unpressed - /// - /// - /// - private void SurfaceMouseUp(object sender, MouseEventArgs e) - { - e = InverseZoomMouseCoordinates(e); + /// + /// Check if an adorner was "hit", and change the cursor if so + /// + /// MouseEventArgs + /// IAdorner + private IAdorner FindActiveAdorner(MouseEventArgs mouseEventArgs) + { + foreach (IDrawableContainer drawableContainer in selectedElements) + { + foreach (IAdorner adorner in drawableContainer.Adorners) + { + if (adorner.IsActive || adorner.HitTest(mouseEventArgs.Location)) + { + if (adorner.Cursor != null) + { + Cursor = adorner.Cursor; + } - // Handle Adorners - var adorner = FindActiveAdorner(e); - if (adorner != null) - { - adorner.MouseUp(sender, e); - return; - } + return adorner; + } + } + } - Point currentMouse = new Point(e.X, e.Y); + return null; + } - _elements.Status = EditStatus.IDLE; - if (_mouseDownElement != null) - { - _mouseDownElement.Status = EditStatus.IDLE; - } - _mouseDown = false; - _mouseDownElement = null; - if (DrawingMode == DrawingModes.None) - { - // check whether an existing element was clicked - IDrawableContainer element = _elements.ClickableElementAt(currentMouse.X, currentMouse.Y); - bool shiftModifier = (ModifierKeys & Keys.Shift) == Keys.Shift; - if (element != null) - { - element.Invalidate(); - bool alreadySelected = selectedElements.Contains(element); - if (shiftModifier) - { - if (alreadySelected) - { - DeselectElement(element); - } - else - { - SelectElement(element); - } - } - else - { - if (!alreadySelected) - { - DeselectAllElements(); - SelectElement(element); - } - } - } - else if (!shiftModifier) - { - DeselectAllElements(); - } - } + /// + /// Translate mouse coordinates as if they were applied directly to unscaled image. + /// + private MouseEventArgs InverseZoomMouseCoordinates(MouseEventArgs e) + => new MouseEventArgs(e.Button, e.Clicks, (int) (e.X / _zoomFactor), (int) (e.Y / _zoomFactor), e.Delta); - if (selectedElements.Count > 0) - { - selectedElements.Invalidate(); - selectedElements.Selected = true; - } + /// + /// This event handler is called when someone presses the mouse on a surface. + /// + /// + /// + private void SurfaceMouseDown(object sender, MouseEventArgs e) + { + e = InverseZoomMouseCoordinates(e); - if (_drawingElement != null) - { - if (!_drawingElement.InitContent()) - { - _elements.Remove(_drawingElement); - _drawingElement.Invalidate(); - } - else - { - _drawingElement.HandleMouseUp(currentMouse.X, currentMouse.Y); - _drawingElement.Invalidate(); - if (Math.Abs(_drawingElement.Width) < 5 && Math.Abs(_drawingElement.Height) < 5) - { - _drawingElement.Width = 25; - _drawingElement.Height = 25; - } - SelectElement(_drawingElement); - _drawingElement.Selected = true; - } - _drawingElement = null; - } - } + // Handle Adorners + var adorner = FindActiveAdorner(e); + if (adorner != null) + { + adorner.MouseDown(sender, e); + return; + } - /// - /// This event handler is called when the mouse moves over the surface - /// - /// - /// - private void SurfaceMouseMove(object sender, MouseEventArgs e) - { - e = InverseZoomMouseCoordinates(e); + _mouseStart = e.Location; - // Handle Adorners - var adorner = FindActiveAdorner(e); - if (adorner != null) - { - adorner.MouseMove(sender, e); - return; - } + // check contextmenu + if (e.Button == MouseButtons.Right) + { + IDrawableContainerList selectedList = null; + if (selectedElements != null && selectedElements.Count > 0) + { + selectedList = selectedElements; + } + else + { + // Single element + IDrawableContainer rightClickedContainer = _elements.ClickableElementAt(_mouseStart.X, _mouseStart.Y); + if (rightClickedContainer != null) + { + selectedList = new DrawableContainerList(ID) + { + rightClickedContainer + }; + } + } - Point currentMouse = e.Location; + if (selectedList != null && selectedList.Count > 0) + { + selectedList.ShowContextMenu(e, this); + } - Cursor = DrawingMode != DrawingModes.None ? Cursors.Cross : Cursors.Default; + return; + } - if (_mouseDown) - { - if (_mouseDownElement != null) - { // an element is currently dragged - _mouseDownElement.Invalidate(); - selectedElements.Invalidate(); - // Move the element - if (_mouseDownElement.Selected) - { - if (!_isSurfaceMoveMadeUndoable) - { - // Only allow one undoable per mouse-down/move/up "cycle" - _isSurfaceMoveMadeUndoable = true; - selectedElements.MakeBoundsChangeUndoable(false); - } - // dragged element has been selected before -> move all - selectedElements.MoveBy(currentMouse.X - _mouseStart.X, currentMouse.Y - _mouseStart.Y); - } - else - { - if (!_isSurfaceMoveMadeUndoable) - { - // Only allow one undoable per mouse-down/move/up "cycle" - _isSurfaceMoveMadeUndoable = true; - _mouseDownElement.MakeBoundsChangeUndoable(false); - } - // dragged element is not among selected elements -> just move dragged one - _mouseDownElement.MoveBy(currentMouse.X - _mouseStart.X, currentMouse.Y - _mouseStart.Y); - } - _mouseStart = currentMouse; - _mouseDownElement.Invalidate(); - _modified = true; - } - else if (_drawingElement != null) - { - _drawingElement.HandleMouseMove(currentMouse.X, currentMouse.Y); - _modified = true; - } - } - } + _mouseDown = true; + _isSurfaceMoveMadeUndoable = false; - /// - /// This event handler is called when the surface is double clicked. - /// - /// - /// - private void SurfaceDoubleClick(object sender, MouseEventArgs e) - { - selectedElements.OnDoubleClick(); - selectedElements.Invalidate(); - } + if (_cropContainer != null && ((_undrawnElement == null) || (_undrawnElement != null && DrawingMode != DrawingModes.Crop))) + { + RemoveElement(_cropContainer, false); + _cropContainer = null; + _drawingElement = null; + } - /// - /// Privately used to get the rendered image with all the elements on it. - /// - /// - /// - private Image GetImage(RenderMode renderMode) - { - // Generate a copy of the original image with a dpi equal to the default... - Bitmap clone = ImageHelper.Clone(_image, PixelFormat.DontCare); - // otherwise we would have a problem drawing the image to the surface... :( - using (Graphics graphics = Graphics.FromImage(clone)) - { - // Do not set the following, the containers need to decide themselves - //graphics.SmoothingMode = SmoothingMode.HighQuality; - //graphics.PixelOffsetMode = PixelOffsetMode.HighQuality; - //graphics.CompositingQuality = CompositingQuality.HighQuality; - //graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; - _elements.Draw(graphics, clone, renderMode, new Rectangle(Point.Empty, clone.Size)); - } - return clone; - } + if (_drawingElement == null && DrawingMode != DrawingModes.None) + { + if (_undrawnElement == null) + { + DeselectAllElements(); + if (_undrawnElement == null) + { + CreateUndrawnElement(); + } + } - /// - /// This returns the image "result" of this surface, with all the elements rendered on it. - /// - /// - public Image GetImageForExport() - { - return GetImage(RenderMode.EXPORT); - } + _drawingElement = _undrawnElement; + // if a new element has been drawn, set location and register it + if (_drawingElement != null) + { + if (_undrawnElement != null) + { + _drawingElement.Status = _undrawnElement.DefaultEditMode; + } - private static Rectangle ZoomClipRectangle(Rectangle rc, double scale, int inflateAmount = 0) - { - rc = new Rectangle( - (int)(rc.X * scale), - (int)(rc.Y * scale), - (int)(rc.Width * scale) + 1, - (int)(rc.Height * scale) + 1 - ); - if (scale > 1) - { - inflateAmount = (int)(inflateAmount * scale); - } - rc.Inflate(inflateAmount, inflateAmount); - return rc; - } + if (!_drawingElement.HandleMouseDown(_mouseStart.X, _mouseStart.Y)) + { + _drawingElement.Left = _mouseStart.X; + _drawingElement.Top = _mouseStart.Y; + } - public void InvalidateElements(Rectangle rc) - => Invalidate(ZoomClipRectangle(rc, _zoomFactor, 1)); + AddElement(_drawingElement); + _drawingElement.Selected = true; + } - /// - /// This is the event handler for the Paint Event, try to draw as little as possible! - /// - /// - /// PaintEventArgs - private void SurfacePaint(object sender, PaintEventArgs paintEventArgs) - { - Graphics targetGraphics = paintEventArgs.Graphics; - Rectangle targetClipRectangle = paintEventArgs.ClipRectangle; - if (Rectangle.Empty.Equals(targetClipRectangle)) - { - LOG.Debug("Empty cliprectangle??"); - return; - } + _undrawnElement = null; + } + else + { + // check whether an existing element was clicked + // we save mouse down element separately from selectedElements (checked on mouse up), + // since it could be moved around before it is actually selected + _mouseDownElement = _elements.ClickableElementAt(_mouseStart.X, _mouseStart.Y); - // Correction to prevent rounding errors at certain zoom levels. - // When zooming to N/M, clip rectangle top and left coordinates should be multiples of N. - if (_zoomFactor.Numerator > 1 && _zoomFactor.Denominator > 1) - { - int horizontalCorrection = targetClipRectangle.Left % (int)_zoomFactor.Numerator; - int verticalCorrection = targetClipRectangle.Top % (int)_zoomFactor.Numerator; - if (horizontalCorrection != 0) - { - targetClipRectangle.X -= horizontalCorrection; - targetClipRectangle.Width += horizontalCorrection; - } - if (verticalCorrection != 0) - { - targetClipRectangle.Y -= verticalCorrection; - targetClipRectangle.Height += verticalCorrection; - } - } + if (_mouseDownElement != null) + { + _mouseDownElement.Status = EditStatus.MOVING; + } + } + } - Rectangle imageClipRectangle = ZoomClipRectangle(targetClipRectangle, _zoomFactor.Inverse(), 2); + /// + /// This event handle is called when the mouse button is unpressed + /// + /// + /// + private void SurfaceMouseUp(object sender, MouseEventArgs e) + { + e = InverseZoomMouseCoordinates(e); - if (_elements.HasIntersectingFilters(imageClipRectangle) || _zoomFactor > Fraction.Identity) - { - if (_buffer != null) - { - if (_buffer.Width != Image.Width || _buffer.Height != Image.Height || _buffer.PixelFormat != Image.PixelFormat) - { - _buffer.Dispose(); - _buffer = null; - } - } - if (_buffer == null) - { - _buffer = ImageHelper.CreateEmpty(Image.Width, Image.Height, Image.PixelFormat, Color.Empty, Image.HorizontalResolution, Image.VerticalResolution); - LOG.DebugFormat("Created buffer with size: {0}x{1}", Image.Width, Image.Height); - } - // Elements might need the bitmap, so we copy the part we need - using (Graphics graphics = Graphics.FromImage(_buffer)) - { - // do not set the following, the containers need to decide this themselves! - //graphics.SmoothingMode = SmoothingMode.HighQuality; - //graphics.PixelOffsetMode = PixelOffsetMode.HighQuality; - //graphics.CompositingQuality = CompositingQuality.HighQuality; - //graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; - DrawBackground(graphics, imageClipRectangle); - graphics.DrawImage(Image, imageClipRectangle, imageClipRectangle, GraphicsUnit.Pixel); - graphics.SetClip(ZoomClipRectangle(Rectangle.Round(targetGraphics.ClipBounds), _zoomFactor.Inverse(), 2)); - _elements.Draw(graphics, _buffer, RenderMode.EDIT, imageClipRectangle); - } - if (_zoomFactor == Fraction.Identity) - { - targetGraphics.DrawImage(_buffer, imageClipRectangle, imageClipRectangle, GraphicsUnit.Pixel); - } - else - { - targetGraphics.ScaleTransform(_zoomFactor, _zoomFactor); - if (_zoomFactor > Fraction.Identity) - { - DrawSharpImage(targetGraphics, _buffer, imageClipRectangle); - } - else - { - DrawSmoothImage(targetGraphics, _buffer, imageClipRectangle); - } - targetGraphics.ResetTransform(); - } - } - else - { - DrawBackground(targetGraphics, targetClipRectangle); - if (_zoomFactor == Fraction.Identity) - { - targetGraphics.DrawImage(Image, imageClipRectangle, imageClipRectangle, GraphicsUnit.Pixel); - _elements.Draw(targetGraphics, null, RenderMode.EDIT, imageClipRectangle); - } - else - { - targetGraphics.ScaleTransform(_zoomFactor, _zoomFactor); - DrawSmoothImage(targetGraphics, Image, imageClipRectangle); - _elements.Draw(targetGraphics, null, RenderMode.EDIT, imageClipRectangle); - targetGraphics.ResetTransform(); - } - } + // Handle Adorners + var adorner = FindActiveAdorner(e); + if (adorner != null) + { + adorner.MouseUp(sender, e); + return; + } - // No clipping for the adorners - targetGraphics.ResetClip(); - // Draw adorners last - foreach (var drawableContainer in selectedElements) - { - foreach (var adorner in drawableContainer.Adorners) - { - adorner.Paint(paintEventArgs); - } - } - } + Point currentMouse = new Point(e.X, e.Y); - private void DrawSmoothImage(Graphics targetGraphics, Image image, Rectangle imageClipRectangle) - { - var state = targetGraphics.Save(); - targetGraphics.SmoothingMode = SmoothingMode.HighQuality; - targetGraphics.InterpolationMode = InterpolationMode.HighQualityBilinear; - targetGraphics.CompositingQuality = CompositingQuality.HighQuality; - targetGraphics.PixelOffsetMode = PixelOffsetMode.None; + _elements.Status = EditStatus.IDLE; + if (_mouseDownElement != null) + { + _mouseDownElement.Status = EditStatus.IDLE; + } - targetGraphics.DrawImage(image, imageClipRectangle, imageClipRectangle, GraphicsUnit.Pixel); + _mouseDown = false; + _mouseDownElement = null; + if (DrawingMode == DrawingModes.None) + { + // check whether an existing element was clicked + IDrawableContainer element = _elements.ClickableElementAt(currentMouse.X, currentMouse.Y); + bool shiftModifier = (ModifierKeys & Keys.Shift) == Keys.Shift; + if (element != null) + { + element.Invalidate(); + bool alreadySelected = selectedElements.Contains(element); + if (shiftModifier) + { + if (alreadySelected) + { + DeselectElement(element); + } + else + { + SelectElement(element); + } + } + else + { + if (!alreadySelected) + { + DeselectAllElements(); + SelectElement(element); + } + } + } + else if (!shiftModifier) + { + DeselectAllElements(); + } + } - targetGraphics.Restore(state); - } + if (selectedElements.Count > 0) + { + selectedElements.Invalidate(); + selectedElements.Selected = true; + } - private void DrawSharpImage(Graphics targetGraphics, Image image, Rectangle imageClipRectangle) - { - var state = targetGraphics.Save(); - targetGraphics.SmoothingMode = SmoothingMode.None; - targetGraphics.InterpolationMode = InterpolationMode.NearestNeighbor; - targetGraphics.CompositingQuality = CompositingQuality.HighQuality; - targetGraphics.PixelOffsetMode = PixelOffsetMode.None; + if (_drawingElement != null) + { + if (!_drawingElement.InitContent()) + { + _elements.Remove(_drawingElement); + _drawingElement.Invalidate(); + } + else + { + _drawingElement.HandleMouseUp(currentMouse.X, currentMouse.Y); + _drawingElement.Invalidate(); + if (Math.Abs(_drawingElement.Width) < 5 && Math.Abs(_drawingElement.Height) < 5) + { + _drawingElement.Width = 25; + _drawingElement.Height = 25; + } - targetGraphics.DrawImage(image, imageClipRectangle, imageClipRectangle, GraphicsUnit.Pixel); + SelectElement(_drawingElement); + _drawingElement.Selected = true; + } - targetGraphics.Restore(state); - } + _drawingElement = null; + } + } - private void DrawBackground(Graphics targetGraphics, Rectangle clipRectangle) - { - // check if we need to draw the checkerboard - if (Image.IsAlphaPixelFormat(Image.PixelFormat) && _transparencyBackgroundBrush != null) - { - targetGraphics.FillRectangle(_transparencyBackgroundBrush, clipRectangle); - } - else - { - targetGraphics.Clear(BackColor); - } - } + /// + /// This event handler is called when the mouse moves over the surface + /// + /// + /// + private void SurfaceMouseMove(object sender, MouseEventArgs e) + { + e = InverseZoomMouseCoordinates(e); - /// - /// Draw a checkboard when capturing with transparency - /// - /// PaintEventArgs - protected override void OnPaintBackground(PaintEventArgs e) - { - } + // Handle Adorners + var adorner = FindActiveAdorner(e); + if (adorner != null) + { + adorner.MouseMove(sender, e); + return; + } - /// - /// Add a new element to the surface - /// - /// the new element - /// true if the adding should be undoable - /// true if invalidate needs to be called - public void AddElement(IDrawableContainer element, bool makeUndoable = true, bool invalidate = true) - { - _elements.Add(element); + Point currentMouse = e.Location; + + Cursor = DrawingMode != DrawingModes.None ? Cursors.Cross : Cursors.Default; + + if (_mouseDown) + { + if (_mouseDownElement != null) + { + // an element is currently dragged + _mouseDownElement.Invalidate(); + selectedElements.Invalidate(); + // Move the element + if (_mouseDownElement.Selected) + { + if (!_isSurfaceMoveMadeUndoable) + { + // Only allow one undoable per mouse-down/move/up "cycle" + _isSurfaceMoveMadeUndoable = true; + selectedElements.MakeBoundsChangeUndoable(false); + } + + // dragged element has been selected before -> move all + selectedElements.MoveBy(currentMouse.X - _mouseStart.X, currentMouse.Y - _mouseStart.Y); + } + else + { + if (!_isSurfaceMoveMadeUndoable) + { + // Only allow one undoable per mouse-down/move/up "cycle" + _isSurfaceMoveMadeUndoable = true; + _mouseDownElement.MakeBoundsChangeUndoable(false); + } + + // dragged element is not among selected elements -> just move dragged one + _mouseDownElement.MoveBy(currentMouse.X - _mouseStart.X, currentMouse.Y - _mouseStart.Y); + } + + _mouseStart = currentMouse; + _mouseDownElement.Invalidate(); + _modified = true; + } + else if (_drawingElement != null) + { + _drawingElement.HandleMouseMove(currentMouse.X, currentMouse.Y); + _modified = true; + } + } + } + + /// + /// This event handler is called when the surface is double clicked. + /// + /// + /// + private void SurfaceDoubleClick(object sender, MouseEventArgs e) + { + selectedElements.OnDoubleClick(); + selectedElements.Invalidate(); + } + + /// + /// Privately used to get the rendered image with all the elements on it. + /// + /// + /// + private Image GetImage(RenderMode renderMode) + { + // Generate a copy of the original image with a dpi equal to the default... + Bitmap clone = ImageHelper.Clone(_image, PixelFormat.DontCare); + // otherwise we would have a problem drawing the image to the surface... :( + using (Graphics graphics = Graphics.FromImage(clone)) + { + // Do not set the following, the containers need to decide themselves + //graphics.SmoothingMode = SmoothingMode.HighQuality; + //graphics.PixelOffsetMode = PixelOffsetMode.HighQuality; + //graphics.CompositingQuality = CompositingQuality.HighQuality; + //graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; + _elements.Draw(graphics, clone, renderMode, new Rectangle(Point.Empty, clone.Size)); + } + + return clone; + } + + /// + /// This returns the image "result" of this surface, with all the elements rendered on it. + /// + /// + public Image GetImageForExport() + { + return GetImage(RenderMode.EXPORT); + } + + private static Rectangle ZoomClipRectangle(Rectangle rc, double scale, int inflateAmount = 0) + { + rc = new Rectangle( + (int) (rc.X * scale), + (int) (rc.Y * scale), + (int) (rc.Width * scale) + 1, + (int) (rc.Height * scale) + 1 + ); + if (scale > 1) + { + inflateAmount = (int) (inflateAmount * scale); + } + + rc.Inflate(inflateAmount, inflateAmount); + return rc; + } + + public void InvalidateElements(Rectangle rc) + => Invalidate(ZoomClipRectangle(rc, _zoomFactor, 1)); + + /// + /// This is the event handler for the Paint Event, try to draw as little as possible! + /// + /// + /// PaintEventArgs + private void SurfacePaint(object sender, PaintEventArgs paintEventArgs) + { + Graphics targetGraphics = paintEventArgs.Graphics; + Rectangle targetClipRectangle = paintEventArgs.ClipRectangle; + if (Rectangle.Empty.Equals(targetClipRectangle)) + { + LOG.Debug("Empty cliprectangle??"); + return; + } + + // Correction to prevent rounding errors at certain zoom levels. + // When zooming to N/M, clip rectangle top and left coordinates should be multiples of N. + if (_zoomFactor.Numerator > 1 && _zoomFactor.Denominator > 1) + { + int horizontalCorrection = targetClipRectangle.Left % (int) _zoomFactor.Numerator; + int verticalCorrection = targetClipRectangle.Top % (int) _zoomFactor.Numerator; + if (horizontalCorrection != 0) + { + targetClipRectangle.X -= horizontalCorrection; + targetClipRectangle.Width += horizontalCorrection; + } + + if (verticalCorrection != 0) + { + targetClipRectangle.Y -= verticalCorrection; + targetClipRectangle.Height += verticalCorrection; + } + } + + Rectangle imageClipRectangle = ZoomClipRectangle(targetClipRectangle, _zoomFactor.Inverse(), 2); + + if (_elements.HasIntersectingFilters(imageClipRectangle) || _zoomFactor > Fraction.Identity) + { + if (_buffer != null) + { + if (_buffer.Width != Image.Width || _buffer.Height != Image.Height || _buffer.PixelFormat != Image.PixelFormat) + { + _buffer.Dispose(); + _buffer = null; + } + } + + if (_buffer == null) + { + _buffer = ImageHelper.CreateEmpty(Image.Width, Image.Height, Image.PixelFormat, Color.Empty, Image.HorizontalResolution, Image.VerticalResolution); + LOG.DebugFormat("Created buffer with size: {0}x{1}", Image.Width, Image.Height); + } + + // Elements might need the bitmap, so we copy the part we need + using (Graphics graphics = Graphics.FromImage(_buffer)) + { + // do not set the following, the containers need to decide this themselves! + //graphics.SmoothingMode = SmoothingMode.HighQuality; + //graphics.PixelOffsetMode = PixelOffsetMode.HighQuality; + //graphics.CompositingQuality = CompositingQuality.HighQuality; + //graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; + DrawBackground(graphics, imageClipRectangle); + graphics.DrawImage(Image, imageClipRectangle, imageClipRectangle, GraphicsUnit.Pixel); + graphics.SetClip(ZoomClipRectangle(Rectangle.Round(targetGraphics.ClipBounds), _zoomFactor.Inverse(), 2)); + _elements.Draw(graphics, _buffer, RenderMode.EDIT, imageClipRectangle); + } + + if (_zoomFactor == Fraction.Identity) + { + targetGraphics.DrawImage(_buffer, imageClipRectangle, imageClipRectangle, GraphicsUnit.Pixel); + } + else + { + targetGraphics.ScaleTransform(_zoomFactor, _zoomFactor); + if (_zoomFactor > Fraction.Identity) + { + DrawSharpImage(targetGraphics, _buffer, imageClipRectangle); + } + else + { + DrawSmoothImage(targetGraphics, _buffer, imageClipRectangle); + } + + targetGraphics.ResetTransform(); + } + } + else + { + DrawBackground(targetGraphics, targetClipRectangle); + if (_zoomFactor == Fraction.Identity) + { + targetGraphics.DrawImage(Image, imageClipRectangle, imageClipRectangle, GraphicsUnit.Pixel); + _elements.Draw(targetGraphics, null, RenderMode.EDIT, imageClipRectangle); + } + else + { + targetGraphics.ScaleTransform(_zoomFactor, _zoomFactor); + DrawSmoothImage(targetGraphics, Image, imageClipRectangle); + _elements.Draw(targetGraphics, null, RenderMode.EDIT, imageClipRectangle); + targetGraphics.ResetTransform(); + } + } + + // No clipping for the adorners + targetGraphics.ResetClip(); + // Draw adorners last + foreach (var drawableContainer in selectedElements) + { + foreach (var adorner in drawableContainer.Adorners) + { + adorner.Paint(paintEventArgs); + } + } + } + + private void DrawSmoothImage(Graphics targetGraphics, Image image, Rectangle imageClipRectangle) + { + var state = targetGraphics.Save(); + targetGraphics.SmoothingMode = SmoothingMode.HighQuality; + targetGraphics.InterpolationMode = InterpolationMode.HighQualityBilinear; + targetGraphics.CompositingQuality = CompositingQuality.HighQuality; + targetGraphics.PixelOffsetMode = PixelOffsetMode.None; + + targetGraphics.DrawImage(image, imageClipRectangle, imageClipRectangle, GraphicsUnit.Pixel); + + targetGraphics.Restore(state); + } + + private void DrawSharpImage(Graphics targetGraphics, Image image, Rectangle imageClipRectangle) + { + var state = targetGraphics.Save(); + targetGraphics.SmoothingMode = SmoothingMode.None; + targetGraphics.InterpolationMode = InterpolationMode.NearestNeighbor; + targetGraphics.CompositingQuality = CompositingQuality.HighQuality; + targetGraphics.PixelOffsetMode = PixelOffsetMode.None; + + targetGraphics.DrawImage(image, imageClipRectangle, imageClipRectangle, GraphicsUnit.Pixel); + + targetGraphics.Restore(state); + } + + private void DrawBackground(Graphics targetGraphics, Rectangle clipRectangle) + { + // check if we need to draw the checkerboard + if (Image.IsAlphaPixelFormat(Image.PixelFormat) && _transparencyBackgroundBrush != null) + { + targetGraphics.FillRectangle(_transparencyBackgroundBrush, clipRectangle); + } + else + { + targetGraphics.Clear(BackColor); + } + } + + /// + /// Draw a checkboard when capturing with transparency + /// + /// PaintEventArgs + protected override void OnPaintBackground(PaintEventArgs e) + { + } + + /// + /// Add a new element to the surface + /// + /// the new element + /// true if the adding should be undoable + /// true if invalidate needs to be called + public void AddElement(IDrawableContainer element, bool makeUndoable = true, bool invalidate = true) + { + _elements.Add(element); if (element is DrawableContainer container) - { - container.FieldChanged += Element_FieldChanged; - } - element.Parent = this; - if (element.Status == EditStatus.UNDRAWN) - { - element.Status = EditStatus.IDLE; - } - if (element.Selected) - { - // Use false, as the element is invalidated when invalidate == true anyway - SelectElement(element, false); - } - if (invalidate) - { - element.Invalidate(); - } - if (makeUndoable) - { - MakeUndoable(new AddElementMemento(this, element), false); - } - _modified = true; - } + { + container.FieldChanged += Element_FieldChanged; + } - /// - /// Remove the list of elements - /// - /// IDrawableContainerList - /// flag specifying if the remove needs to be undoable - public void RemoveElements(IDrawableContainerList elementsToRemove, bool makeUndoable = true) - { - // fix potential issues with iterating a changing list - DrawableContainerList cloned = new DrawableContainerList(); - cloned.AddRange(elementsToRemove); - if (makeUndoable) - { - MakeUndoable(new DeleteElementsMemento(this, cloned), false); - } - SuspendLayout(); - foreach (var drawableContainer in cloned) - { - RemoveElement(drawableContainer, false, false, false); - } - ResumeLayout(); - Invalidate(); - if (_movingElementChanged != null) - { - SurfaceElementEventArgs eventArgs = new SurfaceElementEventArgs {Elements = cloned}; - _movingElementChanged(this, eventArgs); - } - } + element.Parent = this; + if (element.Status == EditStatus.UNDRAWN) + { + element.Status = EditStatus.IDLE; + } - /// - /// Remove an element of the elements list - /// - /// Element to remove - /// flag specifying if the remove needs to be undoable - /// flag specifying if an surface invalidate needs to be called - /// false to skip event generation - public void RemoveElement(IDrawableContainer elementToRemove, bool makeUndoable = true, bool invalidate = true, bool generateEvents = true) - { - DeselectElement(elementToRemove, generateEvents); - _elements.Remove(elementToRemove); + if (element.Selected) + { + // Use false, as the element is invalidated when invalidate == true anyway + SelectElement(element, false); + } + + if (invalidate) + { + element.Invalidate(); + } + + if (makeUndoable) + { + MakeUndoable(new AddElementMemento(this, element), false); + } + + _modified = true; + } + + /// + /// Remove the list of elements + /// + /// IDrawableContainerList + /// flag specifying if the remove needs to be undoable + public void RemoveElements(IDrawableContainerList elementsToRemove, bool makeUndoable = true) + { + // fix potential issues with iterating a changing list + DrawableContainerList cloned = new DrawableContainerList(); + cloned.AddRange(elementsToRemove); + if (makeUndoable) + { + MakeUndoable(new DeleteElementsMemento(this, cloned), false); + } + + SuspendLayout(); + foreach (var drawableContainer in cloned) + { + RemoveElement(drawableContainer, false, false, false); + } + + ResumeLayout(); + Invalidate(); + if (_movingElementChanged != null) + { + SurfaceElementEventArgs eventArgs = new SurfaceElementEventArgs + { + Elements = cloned + }; + _movingElementChanged(this, eventArgs); + } + } + + /// + /// Remove an element of the elements list + /// + /// Element to remove + /// flag specifying if the remove needs to be undoable + /// flag specifying if an surface invalidate needs to be called + /// false to skip event generation + public void RemoveElement(IDrawableContainer elementToRemove, bool makeUndoable = true, bool invalidate = true, bool generateEvents = true) + { + DeselectElement(elementToRemove, generateEvents); + _elements.Remove(elementToRemove); if (elementToRemove is DrawableContainer element) - { - element.FieldChanged -= Element_FieldChanged; - } - if (elementToRemove != null) - { - elementToRemove.Parent = null; - } - // Do not dispose, the memento should!! element.Dispose(); - if (invalidate) - { - Invalidate(); - } - if (makeUndoable) - { - MakeUndoable(new DeleteElementMemento(this, elementToRemove), false); - } - _modified = true; - } + { + element.FieldChanged -= Element_FieldChanged; + } - /// - /// Add the supplied elements to the surface - /// - /// DrawableContainerList - /// true if the adding should be undoable - public void AddElements(IDrawableContainerList elementsToAdd, bool makeUndoable = true) - { - // fix potential issues with iterating a changing list - DrawableContainerList cloned = new DrawableContainerList(); - cloned.AddRange(elementsToAdd); - if (makeUndoable) - { - MakeUndoable(new AddElementsMemento(this, cloned), false); - } - SuspendLayout(); - foreach (var element in cloned) - { - element.Selected = true; - AddElement(element, false, false); - } - ResumeLayout(); - Invalidate(); - } + if (elementToRemove != null) + { + elementToRemove.Parent = null; + } - /// - /// Returns if this surface has selected elements - /// - /// - public bool HasSelectedElements => (selectedElements != null && selectedElements.Count > 0); + // Do not dispose, the memento should!! element.Dispose(); + if (invalidate) + { + Invalidate(); + } - /// - /// Remove all the selected elements - /// - public void RemoveSelectedElements() - { - if (HasSelectedElements) - { - // As RemoveElement will remove the element from the selectedElements list we need to copy the element to another list. - RemoveElements(selectedElements); - if (_movingElementChanged != null) - { - SurfaceElementEventArgs eventArgs = new SurfaceElementEventArgs(); - _movingElementChanged(this, eventArgs); - } - } - } + if (makeUndoable) + { + MakeUndoable(new DeleteElementMemento(this, elementToRemove), false); + } - /// - /// Cut the selected elements from the surface to the clipboard - /// - public void CutSelectedElements() - { - if (HasSelectedElements) - { - ClipboardHelper.SetClipboardData(typeof(IDrawableContainerList), selectedElements); - RemoveSelectedElements(); - } - } + _modified = true; + } - /// - /// Copy the selected elements to the clipboard - /// - public void CopySelectedElements() - { - if (HasSelectedElements) - { - ClipboardHelper.SetClipboardData(typeof(IDrawableContainerList), selectedElements); - } - } + /// + /// Add the supplied elements to the surface + /// + /// DrawableContainerList + /// true if the adding should be undoable + public void AddElements(IDrawableContainerList elementsToAdd, bool makeUndoable = true) + { + // fix potential issues with iterating a changing list + DrawableContainerList cloned = new DrawableContainerList(); + cloned.AddRange(elementsToAdd); + if (makeUndoable) + { + MakeUndoable(new AddElementsMemento(this, cloned), false); + } - /// - /// This method is called to confirm/cancel "confirmable" elements, like the crop-container. - /// Called when pressing enter or using the "check" in the editor. - /// - /// - public void ConfirmSelectedConfirmableElements(bool confirm) - { - // create new collection so that we can iterate safely (selectedElements might change due with confirm/cancel) - List selectedDCs = new List(selectedElements); - foreach (IDrawableContainer dc in selectedDCs) - { - if (dc.Equals(_cropContainer)) - { - DrawingMode = DrawingModes.None; - // No undo memento for the cropcontainer itself, only for the effect - RemoveElement(_cropContainer, false); - if (confirm) - { - ApplyCrop(_cropContainer.Bounds); - } - _cropContainer.Dispose(); - _cropContainer = null; - break; - } - } - } + SuspendLayout(); + foreach (var element in cloned) + { + element.Selected = true; + AddElement(element, false, false); + } - /// - /// Paste all the elements that are on the clipboard - /// - public void PasteElementFromClipboard() - { - IDataObject clipboard = ClipboardHelper.GetDataObject(); + ResumeLayout(); + Invalidate(); + } - var formats = ClipboardHelper.GetFormats(clipboard); - if (formats == null || formats.Count == 0) - { - return; - } - if (LOG.IsDebugEnabled) - { - LOG.Debug("List of clipboard formats available for pasting:"); - foreach (string format in formats) - { - LOG.Debug("\tgot format: " + format); - } - } + /// + /// Returns if this surface has selected elements + /// + /// + public bool HasSelectedElements => (selectedElements != null && selectedElements.Count > 0); - if (formats.Contains(typeof(IDrawableContainerList).FullName)) - { - IDrawableContainerList dcs = (IDrawableContainerList)ClipboardHelper.GetFromDataObject(clipboard, typeof(IDrawableContainerList)); - if (dcs != null) - { - // Make element(s) only move 10,10 if the surface is the same - bool isSameSurface = (dcs.ParentID == _uniqueId); - dcs.Parent = this; - var moveOffset = isSameSurface ? new Point(10, 10) : Point.Empty; - // Here a fix for bug #1475, first calculate the bounds of the complete IDrawableContainerList - Rectangle drawableContainerListBounds = Rectangle.Empty; - foreach (var element in dcs) - { - drawableContainerListBounds = drawableContainerListBounds == Rectangle.Empty ? element.DrawingBounds : Rectangle.Union(drawableContainerListBounds, element.DrawingBounds); - } - // And find a location inside the target surface to paste to - bool containersCanFit = drawableContainerListBounds.Width < Bounds.Width && drawableContainerListBounds.Height < Bounds.Height; - if (!containersCanFit) - { - Point containersLocation = drawableContainerListBounds.Location; - containersLocation.Offset(moveOffset); - if (!Bounds.Contains(containersLocation)) - { - // Easy fix for same surface - moveOffset = isSameSurface ? new Point(-10, -10) : new Point(-drawableContainerListBounds.Location.X + 10, -drawableContainerListBounds.Location.Y + 10); - } - } - else - { - Rectangle moveContainerListBounds = drawableContainerListBounds; - moveContainerListBounds.Offset(moveOffset); - // check if the element is inside - if (!Bounds.Contains(moveContainerListBounds)) - { - // Easy fix for same surface - if (isSameSurface) - { - moveOffset = new Point(-10, -10); - } - else - { - // For different surface, which is most likely smaller - int offsetX = 0; - int offsetY = 0; - if (drawableContainerListBounds.Right > Bounds.Right) - { - offsetX = Bounds.Right - drawableContainerListBounds.Right; - // Correction for the correction - if (drawableContainerListBounds.Left + offsetX < 0) - { - offsetX += Math.Abs(drawableContainerListBounds.Left + offsetX); - } - } - if (drawableContainerListBounds.Bottom > Bounds.Bottom) - { - offsetY = Bounds.Bottom - drawableContainerListBounds.Bottom; - // Correction for the correction - if (drawableContainerListBounds.Top + offsetY < 0) - { - offsetY += Math.Abs(drawableContainerListBounds.Top + offsetY); - } - } - moveOffset = new Point(offsetX, offsetY); - } - } - } - dcs.MoveBy(moveOffset.X, moveOffset.Y); - AddElements(dcs); - FieldAggregator.BindElements(dcs); - DeselectAllElements(); - SelectElements(dcs); - } - } - else if (ClipboardHelper.ContainsImage(clipboard)) - { - Point pasteLocation = GetPasteLocation(0.1f, 0.1f); + /// + /// Remove all the selected elements + /// + public void RemoveSelectedElements() + { + if (HasSelectedElements) + { + // As RemoveElement will remove the element from the selectedElements list we need to copy the element to another list. + RemoveElements(selectedElements); + if (_movingElementChanged != null) + { + SurfaceElementEventArgs eventArgs = new SurfaceElementEventArgs(); + _movingElementChanged(this, eventArgs); + } + } + } - foreach (Image clipboardImage in ClipboardHelper.GetImages(clipboard)) - { - if (clipboardImage != null) - { - DeselectAllElements(); - IImageContainer container = AddImageContainer(clipboardImage as Bitmap, pasteLocation.X, pasteLocation.Y); - SelectElement(container); - clipboardImage.Dispose(); - pasteLocation.X += 10; - pasteLocation.Y += 10; - } - } - } - else if (ClipboardHelper.ContainsText(clipboard)) - { - Point pasteLocation = GetPasteLocation(0.4f, 0.4f); + /// + /// Cut the selected elements from the surface to the clipboard + /// + public void CutSelectedElements() + { + if (HasSelectedElements) + { + ClipboardHelper.SetClipboardData(typeof(IDrawableContainerList), selectedElements); + RemoveSelectedElements(); + } + } - string text = ClipboardHelper.GetText(clipboard); - if (text != null) - { - DeselectAllElements(); - ITextContainer textContainer = AddTextContainer(text, pasteLocation.X, pasteLocation.Y, - FontFamily.GenericSansSerif, 12f, false, false, false, 2, Color.Black, Color.Transparent); - SelectElement(textContainer); - } - } - } + /// + /// Copy the selected elements to the clipboard + /// + public void CopySelectedElements() + { + if (HasSelectedElements) + { + ClipboardHelper.SetClipboardData(typeof(IDrawableContainerList), selectedElements); + } + } - /// - /// Find a location to paste elements. - /// If mouse is over the surface - use it's position, otherwise use the visible area. - /// Return a point in image coordinate space. - /// - /// 0.0f for the left edge of visible area, 1.0f for the right edge of visible area. - /// 0.0f for the top edge of visible area, 1.0f for the bottom edge of visible area. - private Point GetPasteLocation(float horizontalRatio = 0.5f, float verticalRatio = 0.5f) - { - var point = PointToClient(MousePosition); - var rc = GetVisibleRectangle(); - if (!rc.Contains(point)) - { - point = new Point( - rc.Left + (int)(rc.Width * horizontalRatio), - rc.Top + (int)(rc.Height * verticalRatio) - ); - } - return ToImageCoordinates(point); - } + /// + /// This method is called to confirm/cancel "confirmable" elements, like the crop-container. + /// Called when pressing enter or using the "check" in the editor. + /// + /// + public void ConfirmSelectedConfirmableElements(bool confirm) + { + // create new collection so that we can iterate safely (selectedElements might change due with confirm/cancel) + List selectedDCs = new List(selectedElements); + foreach (IDrawableContainer dc in selectedDCs) + { + if (dc.Equals(_cropContainer)) + { + DrawingMode = DrawingModes.None; + // No undo memento for the cropcontainer itself, only for the effect + RemoveElement(_cropContainer, false); + if (confirm) + { + ApplyCrop(_cropContainer.Bounds); + } - /// - /// Get the rectangle bounding the part of this Surface currently visible in the editor (in surface coordinate space). - /// - public Rectangle GetVisibleRectangle() - { - var bounds = Bounds; - var clientArea = Parent.ClientRectangle; - return new Rectangle( - Math.Max(0, -bounds.Left), - Math.Max(0, -bounds.Top), - clientArea.Width, - clientArea.Height - ); - } + _cropContainer.Dispose(); + _cropContainer = null; + break; + } + } + } - /// - /// Get the rectangle bounding all selected elements (in surface coordinates space), - /// or empty rectangle if nothing is selcted. - /// - public Rectangle GetSelectionRectangle() - => ToSurfaceCoordinates(selectedElements.DrawingBounds); + /// + /// Paste all the elements that are on the clipboard + /// + public void PasteElementFromClipboard() + { + IDataObject clipboard = ClipboardHelper.GetDataObject(); - /// - /// Duplicate all the selecteded elements - /// - public void DuplicateSelectedElements() - { - LOG.DebugFormat("Duplicating {0} selected elements", selectedElements.Count); - IDrawableContainerList dcs = selectedElements.Clone(); - dcs.Parent = this; - dcs.MoveBy(10, 10); - AddElements(dcs); - DeselectAllElements(); - SelectElements(dcs); - } + var formats = ClipboardHelper.GetFormats(clipboard); + if (formats == null || formats.Count == 0) + { + return; + } - /// - /// Deselect the specified element - /// - /// IDrawableContainerList - /// false to skip event generation - public void DeselectElement(IDrawableContainer container, bool generateEvents = true) - { - container.Selected = false; - selectedElements.Remove(container); - FieldAggregator.UnbindElement(container); - if (generateEvents && _movingElementChanged != null) - { - var eventArgs = new SurfaceElementEventArgs {Elements = selectedElements}; - _movingElementChanged(this, eventArgs); - } - } + if (LOG.IsDebugEnabled) + { + LOG.Debug("List of clipboard formats available for pasting:"); + foreach (string format in formats) + { + LOG.Debug("\tgot format: " + format); + } + } - /// - /// Deselect the specified elements - /// - /// IDrawableContainerList - public void DeselectElements(IDrawableContainerList elements) - { - if (elements.Count == 0) - { - return; - } - while (elements.Count > 0) - { - var element = elements[0]; - DeselectElement(element, false); - } - if (_movingElementChanged != null) - { - SurfaceElementEventArgs eventArgs = new SurfaceElementEventArgs - { - Elements = selectedElements - }; - _movingElementChanged(this, eventArgs); - } - Invalidate(); - } + if (formats.Contains(typeof(IDrawableContainerList).FullName)) + { + IDrawableContainerList dcs = (IDrawableContainerList) ClipboardHelper.GetFromDataObject(clipboard, typeof(IDrawableContainerList)); + if (dcs != null) + { + // Make element(s) only move 10,10 if the surface is the same + bool isSameSurface = (dcs.ParentID == _uniqueId); + dcs.Parent = this; + var moveOffset = isSameSurface ? new Point(10, 10) : Point.Empty; + // Here a fix for bug #1475, first calculate the bounds of the complete IDrawableContainerList + Rectangle drawableContainerListBounds = Rectangle.Empty; + foreach (var element in dcs) + { + drawableContainerListBounds = drawableContainerListBounds == Rectangle.Empty + ? element.DrawingBounds + : Rectangle.Union(drawableContainerListBounds, element.DrawingBounds); + } - /// - /// Deselect all the selected elements - /// - public void DeselectAllElements() - { - DeselectElements(selectedElements); - } + // And find a location inside the target surface to paste to + bool containersCanFit = drawableContainerListBounds.Width < Bounds.Width && drawableContainerListBounds.Height < Bounds.Height; + if (!containersCanFit) + { + Point containersLocation = drawableContainerListBounds.Location; + containersLocation.Offset(moveOffset); + if (!Bounds.Contains(containersLocation)) + { + // Easy fix for same surface + moveOffset = isSameSurface + ? new Point(-10, -10) + : new Point(-drawableContainerListBounds.Location.X + 10, -drawableContainerListBounds.Location.Y + 10); + } + } + else + { + Rectangle moveContainerListBounds = drawableContainerListBounds; + moveContainerListBounds.Offset(moveOffset); + // check if the element is inside + if (!Bounds.Contains(moveContainerListBounds)) + { + // Easy fix for same surface + if (isSameSurface) + { + moveOffset = new Point(-10, -10); + } + else + { + // For different surface, which is most likely smaller + int offsetX = 0; + int offsetY = 0; + if (drawableContainerListBounds.Right > Bounds.Right) + { + offsetX = Bounds.Right - drawableContainerListBounds.Right; + // Correction for the correction + if (drawableContainerListBounds.Left + offsetX < 0) + { + offsetX += Math.Abs(drawableContainerListBounds.Left + offsetX); + } + } - /// - /// Select the supplied element - /// - /// - /// false to skip invalidation - /// false to skip event generation - public void SelectElement(IDrawableContainer container, bool invalidate = true, bool generateEvents = true) - { - if (!selectedElements.Contains(container)) - { - selectedElements.Add(container); - container.Selected = true; - FieldAggregator.BindElement(container); - if (generateEvents && _movingElementChanged != null) - { - SurfaceElementEventArgs eventArgs = new SurfaceElementEventArgs - { - Elements = selectedElements - }; - _movingElementChanged(this, eventArgs); - } - if (invalidate) - { - container.Invalidate(); - } - } - } + if (drawableContainerListBounds.Bottom > Bounds.Bottom) + { + offsetY = Bounds.Bottom - drawableContainerListBounds.Bottom; + // Correction for the correction + if (drawableContainerListBounds.Top + offsetY < 0) + { + offsetY += Math.Abs(drawableContainerListBounds.Top + offsetY); + } + } - /// - /// Select all elements, this is called when Ctrl+A is pressed - /// - public void SelectAllElements() - { - SelectElements(_elements); - } + moveOffset = new Point(offsetX, offsetY); + } + } + } - /// - /// Select the supplied elements - /// - /// - public void SelectElements(IDrawableContainerList elements) - { - SuspendLayout(); - foreach (var drawableContainer in elements) - { - var element = (DrawableContainer) drawableContainer; - SelectElement(element, false, false); - } - if (_movingElementChanged != null) - { - SurfaceElementEventArgs eventArgs = new SurfaceElementEventArgs {Elements = selectedElements}; - _movingElementChanged(this, eventArgs); - } - ResumeLayout(); - Invalidate(); - } + dcs.MoveBy(moveOffset.X, moveOffset.Y); + AddElements(dcs); + FieldAggregator.BindElements(dcs); + DeselectAllElements(); + SelectElements(dcs); + } + } + else if (ClipboardHelper.ContainsImage(clipboard)) + { + Point pasteLocation = GetPasteLocation(0.1f, 0.1f); - /// - /// Process key presses on the surface, this is called from the editor (and NOT an override from the Control) - /// - /// Keys - /// false if no keys were processed - public bool ProcessCmdKey(Keys k) - { - if (selectedElements.Count > 0) - { - bool shiftModifier = (ModifierKeys & Keys.Shift) == Keys.Shift; - int px = shiftModifier ? 10 : 1; - Point moveBy = Point.Empty; + foreach (Image clipboardImage in ClipboardHelper.GetImages(clipboard)) + { + if (clipboardImage != null) + { + DeselectAllElements(); + IImageContainer container = AddImageContainer(clipboardImage as Bitmap, pasteLocation.X, pasteLocation.Y); + SelectElement(container); + clipboardImage.Dispose(); + pasteLocation.X += 10; + pasteLocation.Y += 10; + } + } + } + else if (ClipboardHelper.ContainsText(clipboard)) + { + Point pasteLocation = GetPasteLocation(0.4f, 0.4f); - switch (k) - { - case Keys.Left: - case Keys.Left | Keys.Shift: - moveBy = new Point(-px, 0); - break; - case Keys.Up: - case Keys.Up | Keys.Shift: - moveBy = new Point(0, -px); - break; - case Keys.Right: - case Keys.Right | Keys.Shift: - moveBy = new Point(px, 0); - break; - case Keys.Down: - case Keys.Down | Keys.Shift: - moveBy = new Point(0, px); - break; - case Keys.PageUp: - PullElementsUp(); - break; - case Keys.PageDown: - PushElementsDown(); - break; - case Keys.Home: - PullElementsToTop(); - break; - case Keys.End: - PushElementsToBottom(); - break; - case Keys.Enter: - ConfirmSelectedConfirmableElements(true); - break; - case Keys.Escape: - ConfirmSelectedConfirmableElements(false); - break; - /*case Keys.Delete: - RemoveSelectedElements(); - break;*/ - default: - return false; - } - if (!Point.Empty.Equals(moveBy)) - { - selectedElements.MakeBoundsChangeUndoable(true); - selectedElements.MoveBy(moveBy.X, moveBy.Y); - } - return true; - } - return false; - } + string text = ClipboardHelper.GetText(clipboard); + if (text != null) + { + DeselectAllElements(); + ITextContainer textContainer = AddTextContainer(text, pasteLocation.X, pasteLocation.Y, + FontFamily.GenericSansSerif, 12f, false, false, false, 2, Color.Black, Color.Transparent); + SelectElement(textContainer); + } + } + } - /// - /// Property for accessing the elements on the surface - /// - public IDrawableContainerList Elements => _elements; + /// + /// Find a location to paste elements. + /// If mouse is over the surface - use it's position, otherwise use the visible area. + /// Return a point in image coordinate space. + /// + /// 0.0f for the left edge of visible area, 1.0f for the right edge of visible area. + /// 0.0f for the top edge of visible area, 1.0f for the bottom edge of visible area. + private Point GetPasteLocation(float horizontalRatio = 0.5f, float verticalRatio = 0.5f) + { + var point = PointToClient(MousePosition); + var rc = GetVisibleRectangle(); + if (!rc.Contains(point)) + { + point = new Point( + rc.Left + (int) (rc.Width * horizontalRatio), + rc.Top + (int) (rc.Height * verticalRatio) + ); + } - /// - /// pulls selected elements up one level in hierarchy - /// - public void PullElementsUp() - { - _elements.PullElementsUp(selectedElements); - _elements.Invalidate(); - } + return ToImageCoordinates(point); + } - /// - /// pushes selected elements up to top in hierarchy - /// - public void PullElementsToTop() - { - _elements.PullElementsToTop(selectedElements); - _elements.Invalidate(); - } + /// + /// Get the rectangle bounding the part of this Surface currently visible in the editor (in surface coordinate space). + /// + public Rectangle GetVisibleRectangle() + { + var bounds = Bounds; + var clientArea = Parent.ClientRectangle; + return new Rectangle( + Math.Max(0, -bounds.Left), + Math.Max(0, -bounds.Top), + clientArea.Width, + clientArea.Height + ); + } - /// - /// pushes selected elements down one level in hierarchy - /// - public void PushElementsDown() - { - _elements.PushElementsDown(selectedElements); - _elements.Invalidate(); - } + /// + /// Get the rectangle bounding all selected elements (in surface coordinates space), + /// or empty rectangle if nothing is selcted. + /// + public Rectangle GetSelectionRectangle() + => ToSurfaceCoordinates(selectedElements.DrawingBounds); - /// - /// pushes selected elements down to bottom in hierarchy - /// - public void PushElementsToBottom() - { - _elements.PushElementsToBottom(selectedElements); - _elements.Invalidate(); - } + /// + /// Duplicate all the selecteded elements + /// + public void DuplicateSelectedElements() + { + LOG.DebugFormat("Duplicating {0} selected elements", selectedElements.Count); + IDrawableContainerList dcs = selectedElements.Clone(); + dcs.Parent = this; + dcs.MoveBy(10, 10); + AddElements(dcs); + DeselectAllElements(); + SelectElements(dcs); + } - /// - /// indicates whether the selected elements could be pulled up in hierarchy - /// - /// true if selected elements could be pulled up, false otherwise - public bool CanPullSelectionUp() - { - return _elements.CanPullUp(selectedElements); - } + /// + /// Deselect the specified element + /// + /// IDrawableContainerList + /// false to skip event generation + public void DeselectElement(IDrawableContainer container, bool generateEvents = true) + { + container.Selected = false; + selectedElements.Remove(container); + FieldAggregator.UnbindElement(container); + if (generateEvents && _movingElementChanged != null) + { + var eventArgs = new SurfaceElementEventArgs + { + Elements = selectedElements + }; + _movingElementChanged(this, eventArgs); + } + } - /// - /// indicates whether the selected elements could be pushed down in hierarchy - /// - /// true if selected elements could be pushed down, false otherwise - public bool CanPushSelectionDown() - { - return _elements.CanPushDown(selectedElements); - } + /// + /// Deselect the specified elements + /// + /// IDrawableContainerList + public void DeselectElements(IDrawableContainerList elements) + { + if (elements.Count == 0) + { + return; + } - public void Element_FieldChanged(object sender, FieldChangedEventArgs e) - { - selectedElements.HandleFieldChangedEvent(sender, e); - } + while (elements.Count > 0) + { + var element = elements[0]; + DeselectElement(element, false); + } - public bool IsOnSurface(IDrawableContainer container) - { - return _elements.Contains(container); - } + if (_movingElementChanged != null) + { + SurfaceElementEventArgs eventArgs = new SurfaceElementEventArgs + { + Elements = selectedElements + }; + _movingElementChanged(this, eventArgs); + } - public Point ToSurfaceCoordinates(Point point) - { - Point[] points = { point }; - _zoomMatrix.TransformPoints(points); - return points[0]; - } + Invalidate(); + } - public Rectangle ToSurfaceCoordinates(Rectangle rc) - { - if (_zoomMatrix.IsIdentity) - { - return rc; - } - else - { - Point[] points = { rc.Location, rc.Location + rc.Size }; - _zoomMatrix.TransformPoints(points); - return new Rectangle( - points[0].X, - points[0].Y, - points[1].X - points[0].X, - points[1].Y - points[0].Y - ); - } - } + /// + /// Deselect all the selected elements + /// + public void DeselectAllElements() + { + DeselectElements(selectedElements); + } - public Point ToImageCoordinates(Point point) - { - Point[] points = { point }; - _inverseZoomMatrix.TransformPoints(points); - return points[0]; - } + /// + /// Select the supplied element + /// + /// + /// false to skip invalidation + /// false to skip event generation + public void SelectElement(IDrawableContainer container, bool invalidate = true, bool generateEvents = true) + { + if (!selectedElements.Contains(container)) + { + selectedElements.Add(container); + container.Selected = true; + FieldAggregator.BindElement(container); + if (generateEvents && _movingElementChanged != null) + { + SurfaceElementEventArgs eventArgs = new SurfaceElementEventArgs + { + Elements = selectedElements + }; + _movingElementChanged(this, eventArgs); + } - public Rectangle ToImageCoordinates(Rectangle rc) - { - if (_inverseZoomMatrix.IsIdentity) - { - return rc; - } - else - { - Point[] points = { rc.Location, rc.Location + rc.Size }; - _inverseZoomMatrix.TransformPoints(points); - return new Rectangle( - points[0].X, - points[0].Y, - points[1].X - points[0].X, - points[1].Y - points[0].Y - ); - } - } - } -} + if (invalidate) + { + container.Invalidate(); + } + } + } + + /// + /// Select all elements, this is called when Ctrl+A is pressed + /// + public void SelectAllElements() + { + SelectElements(_elements); + } + + /// + /// Select the supplied elements + /// + /// + public void SelectElements(IDrawableContainerList elements) + { + SuspendLayout(); + foreach (var drawableContainer in elements) + { + var element = (DrawableContainer) drawableContainer; + SelectElement(element, false, false); + } + + if (_movingElementChanged != null) + { + SurfaceElementEventArgs eventArgs = new SurfaceElementEventArgs + { + Elements = selectedElements + }; + _movingElementChanged(this, eventArgs); + } + + ResumeLayout(); + Invalidate(); + } + + /// + /// Process key presses on the surface, this is called from the editor (and NOT an override from the Control) + /// + /// Keys + /// false if no keys were processed + public bool ProcessCmdKey(Keys k) + { + if (selectedElements.Count > 0) + { + bool shiftModifier = (ModifierKeys & Keys.Shift) == Keys.Shift; + int px = shiftModifier ? 10 : 1; + Point moveBy = Point.Empty; + + switch (k) + { + case Keys.Left: + case Keys.Left | Keys.Shift: + moveBy = new Point(-px, 0); + break; + case Keys.Up: + case Keys.Up | Keys.Shift: + moveBy = new Point(0, -px); + break; + case Keys.Right: + case Keys.Right | Keys.Shift: + moveBy = new Point(px, 0); + break; + case Keys.Down: + case Keys.Down | Keys.Shift: + moveBy = new Point(0, px); + break; + case Keys.PageUp: + PullElementsUp(); + break; + case Keys.PageDown: + PushElementsDown(); + break; + case Keys.Home: + PullElementsToTop(); + break; + case Keys.End: + PushElementsToBottom(); + break; + case Keys.Enter: + ConfirmSelectedConfirmableElements(true); + break; + case Keys.Escape: + ConfirmSelectedConfirmableElements(false); + break; + /*case Keys.Delete: + RemoveSelectedElements(); + break;*/ + default: + return false; + } + + if (!Point.Empty.Equals(moveBy)) + { + selectedElements.MakeBoundsChangeUndoable(true); + selectedElements.MoveBy(moveBy.X, moveBy.Y); + } + + return true; + } + + return false; + } + + /// + /// Property for accessing the elements on the surface + /// + public IDrawableContainerList Elements => _elements; + + /// + /// pulls selected elements up one level in hierarchy + /// + public void PullElementsUp() + { + _elements.PullElementsUp(selectedElements); + _elements.Invalidate(); + } + + /// + /// pushes selected elements up to top in hierarchy + /// + public void PullElementsToTop() + { + _elements.PullElementsToTop(selectedElements); + _elements.Invalidate(); + } + + /// + /// pushes selected elements down one level in hierarchy + /// + public void PushElementsDown() + { + _elements.PushElementsDown(selectedElements); + _elements.Invalidate(); + } + + /// + /// pushes selected elements down to bottom in hierarchy + /// + public void PushElementsToBottom() + { + _elements.PushElementsToBottom(selectedElements); + _elements.Invalidate(); + } + + /// + /// indicates whether the selected elements could be pulled up in hierarchy + /// + /// true if selected elements could be pulled up, false otherwise + public bool CanPullSelectionUp() + { + return _elements.CanPullUp(selectedElements); + } + + /// + /// indicates whether the selected elements could be pushed down in hierarchy + /// + /// true if selected elements could be pushed down, false otherwise + public bool CanPushSelectionDown() + { + return _elements.CanPushDown(selectedElements); + } + + public void Element_FieldChanged(object sender, FieldChangedEventArgs e) + { + selectedElements.HandleFieldChangedEvent(sender, e); + } + + public bool IsOnSurface(IDrawableContainer container) + { + return _elements.Contains(container); + } + + public Point ToSurfaceCoordinates(Point point) + { + Point[] points = + { + point + }; + _zoomMatrix.TransformPoints(points); + return points[0]; + } + + public Rectangle ToSurfaceCoordinates(Rectangle rc) + { + if (_zoomMatrix.IsIdentity) + { + return rc; + } + else + { + Point[] points = + { + rc.Location, rc.Location + rc.Size + }; + _zoomMatrix.TransformPoints(points); + return new Rectangle( + points[0].X, + points[0].Y, + points[1].X - points[0].X, + points[1].Y - points[0].Y + ); + } + } + + public Point ToImageCoordinates(Point point) + { + Point[] points = + { + point + }; + _inverseZoomMatrix.TransformPoints(points); + return points[0]; + } + + public Rectangle ToImageCoordinates(Rectangle rc) + { + if (_inverseZoomMatrix.IsIdentity) + { + return rc; + } + else + { + Point[] points = + { + rc.Location, rc.Location + rc.Size + }; + _inverseZoomMatrix.TransformPoints(points); + return new Rectangle( + points[0].X, + points[0].Y, + points[1].X - points[0].X, + points[1].Y - points[0].Y + ); + } + } + } +} \ No newline at end of file diff --git a/src/Greenshot/Drawing/TextContainer.cs b/src/Greenshot/Drawing/TextContainer.cs index 9d3838580..8f1a819f6 100644 --- a/src/Greenshot/Drawing/TextContainer.cs +++ b/src/Greenshot/Drawing/TextContainer.cs @@ -46,24 +46,22 @@ namespace Greenshot.Drawing // Although the name is wrong, we can't change it due to file serialization // ReSharper disable once InconsistentNaming private bool makeUndoable; - [NonSerialized] - private Font _font; + [NonSerialized] private Font _font; public Font Font => _font; - [NonSerialized] - private TextBox _textBox; + [NonSerialized] private TextBox _textBox; /// /// The StringFormat object is not serializable!! /// - [NonSerialized] - private StringFormat _stringFormat = new StringFormat(); + [NonSerialized] private StringFormat _stringFormat = new StringFormat(); public StringFormat StringFormat => _stringFormat; // Although the name is wrong, we can't change it due to file serialization // ReSharper disable once InconsistentNaming private string text; + // there is a binding on the following property! public string Text { @@ -74,12 +72,13 @@ namespace Greenshot.Drawing internal void ChangeText(string newText, bool allowUndoable) { if ((text != null || newText == null) && string.Equals(text, newText)) return; - + if (makeUndoable && allowUndoable) { makeUndoable = false; _parent.MakeUndoable(new TextChangeMemento(this), false); } + text = newText; OnPropertyChanged("Text"); } @@ -122,17 +121,20 @@ namespace Greenshot.Drawing _font.Dispose(); _font = null; } + if (_stringFormat != null) { _stringFormat.Dispose(); _stringFormat = null; } + if (_textBox != null) { _textBox.Dispose(); _textBox = null; } } + base.Dispose(disposing); } @@ -158,6 +160,7 @@ namespace Greenshot.Drawing { _parent.SizeChanged -= Parent_SizeChanged; } + base.SwitchParent(newParent); if (_parent != null) { @@ -224,6 +227,7 @@ namespace Greenshot.Drawing _parent.KeysLocked = true; } } + if (_textBox.Visible) { _textBox.Invalidate(); @@ -236,10 +240,12 @@ namespace Greenshot.Drawing { return; } + if (_textBox.Visible) { _textBox.Invalidate(); } + // Only dispose the font, and re-create it, when a font field has changed. if (e.Field.FieldType.Name.StartsWith("FONT")) { @@ -248,12 +254,14 @@ namespace Greenshot.Drawing _font.Dispose(); _font = null; } + UpdateFormat(); } else { UpdateAlignment(); } + UpdateTextBoxFormat(); if (_textBox.Visible) @@ -292,6 +300,7 @@ namespace Greenshot.Drawing _parent.KeysLocked = true; _parent.Controls.Add(_textBox); } + EnsureTextBoxContrast(); if (_textBox != null) { @@ -309,6 +318,7 @@ namespace Greenshot.Drawing { return; } + Color lc = GetFieldValueAsColor(FieldType.LINE_COLOR); if (lc.R > 203 && lc.G > 203 && lc.B > 203) { @@ -328,6 +338,7 @@ namespace Greenshot.Drawing { return; } + _parent.KeysLocked = false; _parent.Controls.Remove(_textBox); } @@ -346,7 +357,7 @@ namespace Greenshot.Drawing rect = GuiRectangle.GetGuiRectangle(Left, Top, Width, Height); int pixelsAfter = rect.Width * rect.Height; - float factor = pixelsAfter / (float)pixelsBefore; + float factor = pixelsAfter / (float) pixelsBefore; float fontSize = GetFieldValueAsFloat(FieldType.FONT_SIZE); fontSize *= factor; @@ -393,6 +404,7 @@ namespace Greenshot.Drawing } } } + return new Font(fontFamily, fontSize, fontStyle, GraphicsUnit.Pixel); } @@ -405,6 +417,7 @@ namespace Greenshot.Drawing { return; } + string fontFamily = GetFieldValueAsString(FieldType.FONT_FAMILY); bool fontBold = GetFieldValueAsBool(FieldType.FONT_BOLD); bool fontItalic = GetFieldValueAsBool(FieldType.FONT_ITALIC); @@ -444,8 +457,8 @@ namespace Greenshot.Drawing private void UpdateAlignment() { - _stringFormat.Alignment = (StringAlignment)GetFieldValue(FieldType.TEXT_HORIZONTAL_ALIGNMENT); - _stringFormat.LineAlignment = (StringAlignment)GetFieldValue(FieldType.TEXT_VERTICAL_ALIGNMENT); + _stringFormat.Alignment = (StringAlignment) GetFieldValue(FieldType.TEXT_HORIZONTAL_ALIGNMENT); + _stringFormat.LineAlignment = (StringAlignment) GetFieldValue(FieldType.TEXT_VERTICAL_ALIGNMENT); } /// @@ -465,7 +478,7 @@ namespace Greenshot.Drawing _font.Size * textBoxFontScale, _font.Style, GraphicsUnit.Pixel - ); + ); _textBox.Font.Dispose(); _textBox.Font = newFont; } @@ -480,15 +493,17 @@ namespace Greenshot.Drawing { return; } + int lineThickness = GetFieldValueAsInt(FieldType.LINE_THICKNESS); - int lineWidth = (int)Math.Floor(lineThickness / 2d); + int lineWidth = (int) Math.Floor(lineThickness / 2d); int correction = (lineThickness + 1) % 2; if (lineThickness <= 1) { lineWidth = 1; correction = -1; } + Rectangle absRectangle = GuiRectangle.GetGuiRectangle(Left, Top, Width, Height); Rectangle displayRectangle = Parent.ToSurfaceCoordinates(absRectangle); _textBox.Left = displayRectangle.X + lineWidth; @@ -497,6 +512,7 @@ namespace Greenshot.Drawing { lineWidth = 0; } + _textBox.Width = displayRectangle.Width - 2 * lineWidth + correction; _textBox.Height = displayRectangle.Height - 2 * lineWidth + correction; } @@ -510,7 +526,8 @@ namespace Greenshot.Drawing { return; } - var alignment = (StringAlignment)GetFieldValue(FieldType.TEXT_HORIZONTAL_ALIGNMENT); + + var alignment = (StringAlignment) GetFieldValue(FieldType.TEXT_HORIZONTAL_ALIGNMENT); switch (alignment) { case StringAlignment.Near: @@ -541,6 +558,7 @@ namespace Greenshot.Drawing { _textBox.SelectAll(); } + // Added for FEATURE-1064 if (e.KeyCode == Keys.Back && e.Control) { @@ -550,11 +568,13 @@ namespace Greenshot.Drawing { selStart--; } + int prevSpacePos = -1; if (selStart != 0) { prevSpacePos = _textBox.Text.LastIndexOf(' ', selStart - 1); } + _textBox.Select(prevSpacePos + 1, _textBox.SelectionStart - prevSpacePos - 1); _textBox.SelectedText = string.Empty; } @@ -611,6 +631,7 @@ namespace Greenshot.Drawing { return flags; } + switch (stringFormat.LineAlignment) { case StringAlignment.Center: @@ -623,6 +644,7 @@ namespace Greenshot.Drawing flags |= TextFormatFlags.Top; break; } + switch (stringFormat.Alignment) { case StringAlignment.Center: @@ -650,7 +672,8 @@ namespace Greenshot.Drawing /// /// /// - public static void DrawText(Graphics graphics, Rectangle drawingRectange, int lineThickness, Color fontColor, bool drawShadow, StringFormat stringFormat, string text, Font font) + public static void DrawText(Graphics graphics, Rectangle drawingRectange, int lineThickness, Color fontColor, bool drawShadow, StringFormat stringFormat, string text, + Font font) { #if DEBUG Debug.Assert(font != null); @@ -660,7 +683,7 @@ namespace Greenshot.Drawing return; } #endif - int textOffset = lineThickness > 0 ? (int)Math.Ceiling(lineThickness / 2d) : 0; + int textOffset = lineThickness > 0 ? (int) Math.Ceiling(lineThickness / 2d) : 0; // draw shadow before anything else if (drawShadow) { @@ -699,4 +722,4 @@ namespace Greenshot.Drawing return r.Contains(x, y); } } -} +} \ No newline at end of file diff --git a/src/Greenshot/Forms/AboutForm.cs b/src/Greenshot/Forms/AboutForm.cs index f3c67edd8..6fdf4eddd 100644 --- a/src/Greenshot/Forms/AboutForm.cs +++ b/src/Greenshot/Forms/AboutForm.cs @@ -34,246 +34,280 @@ using GreenshotPlugin.Core; using GreenshotPlugin.IniFile; using log4net; -namespace Greenshot.Forms { - /// - /// The about form - /// - public sealed partial class AboutForm : AnimatingBaseForm { - private static readonly ILog Log = LogManager.GetLogger(typeof(AboutForm)); - private Bitmap _bitmap; - private readonly ColorAnimator _backgroundAnimation; - private readonly List _pixels = new List(); - private readonly List _colorFlow = new List(); - private readonly List _pixelColors = new List(); - private readonly Random _rand = new Random(); - private readonly Color _backColor = Color.FromArgb(61, 61, 61); - private readonly Color _pixelColor = Color.FromArgb(138, 255, 0); +namespace Greenshot.Forms +{ + /// + /// The about form + /// + public sealed partial class AboutForm : AnimatingBaseForm + { + private static readonly ILog Log = LogManager.GetLogger(typeof(AboutForm)); + private Bitmap _bitmap; + private readonly ColorAnimator _backgroundAnimation; + private readonly List _pixels = new List(); + private readonly List _colorFlow = new List(); + private readonly List _pixelColors = new List(); + private readonly Random _rand = new Random(); + private readonly Color _backColor = Color.FromArgb(61, 61, 61); + private readonly Color _pixelColor = Color.FromArgb(138, 255, 0); - // Variables used for the color-cycle - private int _waitFrames; - private int _colorIndex; - private int _scrollCount; - private bool _hasAnimationsLeft; + // Variables used for the color-cycle + private int _waitFrames; + private int _colorIndex; + private int _scrollCount; + private bool _hasAnimationsLeft; - // Variables are used to define the location of the dots - private const int W = 13; - private const int P1 = 7; - private const int P2 = P1 + W; - private const int P3 = P2 + W; - private const int P4 = P3 + W; - private const int P5 = P4 + W; - private const int P6 = P5 + W; - private const int P7 = P6 + W; + // Variables are used to define the location of the dots + private const int W = 13; + private const int P1 = 7; + private const int P2 = P1 + W; + private const int P3 = P2 + W; + private const int P4 = P3 + W; + private const int P5 = P4 + W; + private const int P6 = P5 + W; + private const int P7 = P6 + W; - /// - /// The location of every dot in the "G" - /// - private readonly List _gSpots = new List + /// + /// The location of every dot in the "G" + /// + private readonly List _gSpots = new List { - // Top row - new Point(P2, P1), // 0 - new Point(P3, P1), // 1 - new Point(P4, P1), // 2 - new Point(P5, P1), // 3 - new Point(P6, P1), // 4 + // Top row + new Point(P2, P1), // 0 + new Point(P3, P1), // 1 + new Point(P4, P1), // 2 + new Point(P5, P1), // 3 + new Point(P6, P1), // 4 - // Second row - new Point(P1, P2), // 5 - new Point(P2, P2), // 6 + // Second row + new Point(P1, P2), // 5 + new Point(P2, P2), // 6 - // Third row - new Point(P1, P3), // 7 - new Point(P2, P3), // 8 + // Third row + new Point(P1, P3), // 7 + new Point(P2, P3), // 8 - // Fourth row - new Point(P1, P4), // 9 - new Point(P2, P4), // 10 - new Point(P5, P4), // 11 - new Point(P6, P4), // 12 - new Point(P7, P4), // 13 + // Fourth row + new Point(P1, P4), // 9 + new Point(P2, P4), // 10 + new Point(P5, P4), // 11 + new Point(P6, P4), // 12 + new Point(P7, P4), // 13 - // Fifth row - new Point(P1, P5), // 14 - new Point(P2, P5), // 15 - new Point(P6, P5), // 16 - new Point(P7, P5), // 17 + // Fifth row + new Point(P1, P5), // 14 + new Point(P2, P5), // 15 + new Point(P6, P5), // 16 + new Point(P7, P5), // 17 - // Sixth row - new Point(P1, P6), // 18 - new Point(P2, P6), // 19 - new Point(P3, P6), // 20 - new Point(P4, P6), // 21 - new Point(P5, P6), // 22 - new Point(P6, P6) // 23 - }; + // Sixth row + new Point(P1, P6), // 18 + new Point(P2, P6), // 19 + new Point(P3, P6), // 20 + new Point(P4, P6), // 21 + new Point(P5, P6), // 22 + new Point(P6, P6) // 23 + }; - // 0 1 2 3 4 - // 5 6 - // 7 8 - // 9 10 11 12 13 - // 14 15 16 17 - // 18 19 20 21 22 23 + // 0 1 2 3 4 + // 5 6 + // 7 8 + // 9 10 11 12 13 + // 14 15 16 17 + // 18 19 20 21 22 23 - // The order in which we draw the dots & flow the colors. - private readonly List _flowOrder = new List{ 4, 3, 2, 1, 0, 5, 6, 7, 8, 9, 10, 14, 15, 18, 19, 20, 21, 22, 23, 16, 17, 13, 12, 11 }; + // The order in which we draw the dots & flow the colors. + private readonly List _flowOrder = new List{ 4, 3, 2, 1, 0, 5, 6, 7, 8, 9, 10, 14, 15, 18, 19, 20, 21, 22, 23, 16, 17, 13, 12, 11 }; - /// - /// Cleanup all the allocated resources - /// - private void Cleanup(object sender, EventArgs e) { + /// + /// Cleanup all the allocated resources + /// + private void Cleanup(object sender, EventArgs e) + { if (_bitmap == null) return; _bitmap.Dispose(); _bitmap = null; } - /// - /// Constructor - /// - public AboutForm() { - // Make sure our resources are removed again. - Disposed += Cleanup; - FormClosing += Cleanup; + /// + /// Constructor + /// + public AboutForm() + { + // Make sure our resources are removed again. + Disposed += Cleanup; + FormClosing += Cleanup; - // Enable animation for this form, when we don't set this the timer doesn't start as soon as the form is loaded. - EnableAnimation = true; - // - // The InitializeComponent() call is required for Windows Forms designer support. - // - InitializeComponent(); + // Enable animation for this form, when we don't set this the timer doesn't start as soon as the form is loaded. + EnableAnimation = true; + // + // The InitializeComponent() call is required for Windows Forms designer support. + // + InitializeComponent(); - // Only use double-buffering when we are NOT in a Terminal Server session - DoubleBuffered = !IsTerminalServerSession; + // Only use double-buffering when we are NOT in a Terminal Server session + DoubleBuffered = !IsTerminalServerSession; - // Use the self drawn image, first we create the background to be the back-color (as we animate from this) + // Use the self drawn image, first we create the background to be the back-color (as we animate from this) - _bitmap = ImageHelper.CreateEmpty(90, 90, PixelFormat.Format24bppRgb, BackColor, 96, 96); - pictureBox1.Image = _bitmap; + _bitmap = ImageHelper.CreateEmpty(90, 90, PixelFormat.Format24bppRgb, BackColor, 96, 96); + pictureBox1.Image = _bitmap; lblTitle.Text = $@"Greenshot {EnvironmentInfo.GetGreenshotVersion()} {(IniConfig.IsPortable ? " Portable" : "")} ({OsInfo.Bits}) bit)"; - // Number of frames the pixel animation takes - int frames = FramesForMillis(2000); - // The number of frames the color-cycle waits before it starts - _waitFrames = FramesForMillis(6000); + // Number of frames the pixel animation takes + int frames = FramesForMillis(2000); + // The number of frames the color-cycle waits before it starts + _waitFrames = FramesForMillis(6000); - // Every pixel is created after pixelWaitFrames frames, which is increased in the loop. - int pixelWaitFrames = FramesForMillis(2000); - // Create pixels - for (int index = 0; index < _gSpots.Count; index++) { - // Read the pixels in the order of the flow - Point gSpot = _gSpots[_flowOrder[index]]; - // Create the animation, first we do nothing (on the final destination) - RectangleAnimator pixelAnimation; + // Every pixel is created after pixelWaitFrames frames, which is increased in the loop. + int pixelWaitFrames = FramesForMillis(2000); + // Create pixels + for (int index = 0; index < _gSpots.Count; index++) + { + // Read the pixels in the order of the flow + Point gSpot = _gSpots[_flowOrder[index]]; + // Create the animation, first we do nothing (on the final destination) + RectangleAnimator pixelAnimation; - // Make the pixel grow from the middle, if this offset isn't used it looks like it's shifted - int offset = (W - 2) / 2; + // Make the pixel grow from the middle, if this offset isn't used it looks like it's shifted + int offset = (W - 2) / 2; - // If the optimize for Terminal Server is set we make the animation without much ado - if (IsTerminalServerSession) { - // No animation - pixelAnimation = new RectangleAnimator(new Rectangle(gSpot.X, gSpot.Y, W - 2, W - 2), new Rectangle(gSpot.X, gSpot.Y, W - 2, W - 2), 1, EasingType.Cubic, EasingMode.EaseIn); - } else { - // Create the animation, first we do nothing (on the final destination) - Rectangle standingStill = new Rectangle(gSpot.X + offset, gSpot.Y + offset, 0, 0); - pixelAnimation = new RectangleAnimator(standingStill, standingStill, pixelWaitFrames, EasingType.Quintic, EasingMode.EaseIn); - // And than we size to the wanted size. - pixelAnimation.QueueDestinationLeg(new Rectangle(gSpot.X, gSpot.Y, W - 2, W - 2), frames); - } - // Increase the wait frames - pixelWaitFrames += FramesForMillis(100); - // Add to the list of to be animated pixels - _pixels.Add(pixelAnimation); - // Add a color to the list for this pixel. - _pixelColors.Add(_pixelColor); - } - // Make sure the frame "loop" knows we have to animate - _hasAnimationsLeft = true; + // If the optimize for Terminal Server is set we make the animation without much ado + if (IsTerminalServerSession) + { + // No animation + pixelAnimation = new RectangleAnimator(new Rectangle(gSpot.X, gSpot.Y, W - 2, W - 2), new Rectangle(gSpot.X, gSpot.Y, W - 2, W - 2), 1, EasingType.Cubic, + EasingMode.EaseIn); + } + else + { + // Create the animation, first we do nothing (on the final destination) + Rectangle standingStill = new Rectangle(gSpot.X + offset, gSpot.Y + offset, 0, 0); + pixelAnimation = new RectangleAnimator(standingStill, standingStill, pixelWaitFrames, EasingType.Quintic, EasingMode.EaseIn); + // And than we size to the wanted size. + pixelAnimation.QueueDestinationLeg(new Rectangle(gSpot.X, gSpot.Y, W - 2, W - 2), frames); + } - // Pixel Color cycle colors, here we use a pre-animated loop which stores the values. - ColorAnimator pixelColorAnimator = new ColorAnimator(_pixelColor, Color.FromArgb(255, 255, 255), 6, EasingType.Quadratic, EasingMode.EaseIn); - pixelColorAnimator.QueueDestinationLeg(_pixelColor, 6, EasingType.Quadratic, EasingMode.EaseOut); - do { - _colorFlow.Add(pixelColorAnimator.Current); - pixelColorAnimator.Next(); - } while (pixelColorAnimator.HasNext); + // Increase the wait frames + pixelWaitFrames += FramesForMillis(100); + // Add to the list of to be animated pixels + _pixels.Add(pixelAnimation); + // Add a color to the list for this pixel. + _pixelColors.Add(_pixelColor); + } - // color animation for the background - _backgroundAnimation = new ColorAnimator(BackColor, _backColor, FramesForMillis(5000), EasingType.Linear, EasingMode.EaseIn); - } + // Make sure the frame "loop" knows we have to animate + _hasAnimationsLeft = true; - /// - /// This is called when a link is clicked - /// - /// - /// - private void LinkLabelClicked(object sender, LinkLabelLinkClickedEventArgs e) + // Pixel Color cycle colors, here we use a pre-animated loop which stores the values. + ColorAnimator pixelColorAnimator = new ColorAnimator(_pixelColor, Color.FromArgb(255, 255, 255), 6, EasingType.Quadratic, EasingMode.EaseIn); + pixelColorAnimator.QueueDestinationLeg(_pixelColor, 6, EasingType.Quadratic, EasingMode.EaseOut); + do + { + _colorFlow.Add(pixelColorAnimator.Current); + pixelColorAnimator.Next(); + } while (pixelColorAnimator.HasNext); + + // color animation for the background + _backgroundAnimation = new ColorAnimator(BackColor, _backColor, FramesForMillis(5000), EasingType.Linear, EasingMode.EaseIn); + } + + /// + /// This is called when a link is clicked + /// + /// + /// + private void LinkLabelClicked(object sender, LinkLabelLinkClickedEventArgs e) { if (!(sender is LinkLabel linkLabel)) return; - try { + try + { linkLabel.LinkVisited = true; Process.Start(linkLabel.Text); - } catch (Exception) { + } + catch (Exception) + { MessageBox.Show(Language.GetFormattedString(LangKey.error_openlink, linkLabel.Text), Language.GetString(LangKey.error)); } } - /// - /// Called from the AnimatingForm, for every frame - /// - protected override void Animate() { - if (_bitmap == null) { - return; - } - if (!IsTerminalServerSession) { - // Color cycle - if (_waitFrames != 0) { - _waitFrames--; - // Check if there is something else to do, if not we return so we don't occupy the CPU - if (!_hasAnimationsLeft) { - return; - } - } else if (_scrollCount < _pixelColors.Count + _colorFlow.Count) { - // Scroll colors, the scrollCount is the amount of pixels + the amount of colors to cycle. - for (int index = _pixelColors.Count - 1; index > 0; index--) { - _pixelColors[index] = _pixelColors[index - 1]; - } - // Keep adding from the colors to cycle until there is nothing left - if (_colorIndex < _colorFlow.Count) { - _pixelColors[0] = _colorFlow[_colorIndex++]; - } - _scrollCount++; - } else { - // Reset values, wait X time for the next one - _waitFrames = FramesForMillis(3000 + _rand.Next(35000)); - _colorIndex = 0; - _scrollCount = 0; - // Check if there is something else to do, if not we return so we don't occupy the CPU - if (!_hasAnimationsLeft) { - return; - } - } - } else if (!_hasAnimationsLeft) { - return; - } + /// + /// Called from the AnimatingForm, for every frame + /// + protected override void Animate() + { + if (_bitmap == null) + { + return; + } - // Draw the "G" - using (Graphics graphics = Graphics.FromImage(_bitmap)) { - graphics.SmoothingMode = SmoothingMode.HighQuality; - graphics.InterpolationMode = InterpolationMode.HighQualityBilinear; - graphics.CompositingQuality = CompositingQuality.HighQuality; - graphics.PixelOffsetMode = PixelOffsetMode.HighQuality; + if (!IsTerminalServerSession) + { + // Color cycle + if (_waitFrames != 0) + { + _waitFrames--; + // Check if there is something else to do, if not we return so we don't occupy the CPU + if (!_hasAnimationsLeft) + { + return; + } + } + else if (_scrollCount < _pixelColors.Count + _colorFlow.Count) + { + // Scroll colors, the scrollCount is the amount of pixels + the amount of colors to cycle. + for (int index = _pixelColors.Count - 1; index > 0; index--) + { + _pixelColors[index] = _pixelColors[index - 1]; + } - graphics.Clear(_backgroundAnimation.Next()); + // Keep adding from the colors to cycle until there is nothing left + if (_colorIndex < _colorFlow.Count) + { + _pixelColors[0] = _colorFlow[_colorIndex++]; + } - graphics.TranslateTransform(2, -2); - graphics.RotateTransform(20); + _scrollCount++; + } + else + { + // Reset values, wait X time for the next one + _waitFrames = FramesForMillis(3000 + _rand.Next(35000)); + _colorIndex = 0; + _scrollCount = 0; + // Check if there is something else to do, if not we return so we don't occupy the CPU + if (!_hasAnimationsLeft) + { + return; + } + } + } + else if (!_hasAnimationsLeft) + { + return; + } + + // Draw the "G" + using (Graphics graphics = Graphics.FromImage(_bitmap)) + { + graphics.SmoothingMode = SmoothingMode.HighQuality; + graphics.InterpolationMode = InterpolationMode.HighQualityBilinear; + graphics.CompositingQuality = CompositingQuality.HighQuality; + graphics.PixelOffsetMode = PixelOffsetMode.HighQuality; + + graphics.Clear(_backgroundAnimation.Next()); + + graphics.TranslateTransform(2, -2); + graphics.RotateTransform(20); using SolidBrush brush = new SolidBrush(_pixelColor); int index = 0; // We assume there is nothing to animate in the next Animate loop _hasAnimationsLeft = false; // Pixels of the G - foreach (RectangleAnimator pixel in _pixels) { + foreach (RectangleAnimator pixel in _pixels) + { brush.Color = _pixelColors[index++]; graphics.FillEllipse(brush, pixel.Current); // If a pixel still has frames left, the hasAnimationsLeft will be true @@ -281,53 +315,75 @@ namespace Greenshot.Forms { pixel.Next(); } } - pictureBox1.Invalidate(); - } - /// - /// CmdKey handler - /// - /// - /// - /// - [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)] - protected override bool ProcessCmdKey(ref Message msg, Keys keyData) { - try { - switch (keyData) { - case Keys.Escape: - DialogResult = DialogResult.Cancel; - break; - case Keys.E: - MessageBox.Show(EnvironmentInfo.EnvironmentToString(true)); - break; - case Keys.L: - try { - if (File.Exists(MainForm.LogFileLocation)) { - using (Process.Start("\"" + MainForm.LogFileLocation + "\"")) { - // nothing to do, just using dispose to cleanup - } - } else { - MessageBox.Show(@"Greenshot can't find the logfile, it should have been here: " + MainForm.LogFileLocation); - } - } catch (Exception) { - MessageBox.Show(@"Couldn't open the greenshot.log, it's located here: " + MainForm.LogFileLocation, @"Error opening greenshot.log", MessageBoxButtons.OK, MessageBoxIcon.Asterisk); - } - break; - case Keys.I: - try { - using (Process.Start("\"" + IniConfig.ConfigLocation + "\"")) { - } - } catch (Exception) { - MessageBox.Show(@"Couldn't open the greenshot.ini, it's located here: " + IniConfig.ConfigLocation, @"Error opening greenshot.ini", MessageBoxButtons.OK, MessageBoxIcon.Asterisk); - } - break; - default: - return base.ProcessCmdKey(ref msg, keyData); - } - } catch (Exception ex) { - Log.Error($"Error handling key '{keyData}'", ex); - } - return true; - } - } + pictureBox1.Invalidate(); + } + + /// + /// CmdKey handler + /// + /// + /// + /// + [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)] + protected override bool ProcessCmdKey(ref Message msg, Keys keyData) + { + try + { + switch (keyData) + { + case Keys.Escape: + DialogResult = DialogResult.Cancel; + break; + case Keys.E: + MessageBox.Show(EnvironmentInfo.EnvironmentToString(true)); + break; + case Keys.L: + try + { + if (File.Exists(MainForm.LogFileLocation)) + { + using (Process.Start("\"" + MainForm.LogFileLocation + "\"")) + { + // nothing to do, just using dispose to cleanup + } + } + else + { + MessageBox.Show(@"Greenshot can't find the logfile, it should have been here: " + MainForm.LogFileLocation); + } + } + catch (Exception) + { + MessageBox.Show(@"Couldn't open the greenshot.log, it's located here: " + MainForm.LogFileLocation, @"Error opening greenshot.log", + MessageBoxButtons.OK, MessageBoxIcon.Asterisk); + } + + break; + case Keys.I: + try + { + using (Process.Start("\"" + IniConfig.ConfigLocation + "\"")) + { + } + } + catch (Exception) + { + MessageBox.Show(@"Couldn't open the greenshot.ini, it's located here: " + IniConfig.ConfigLocation, @"Error opening greenshot.ini", + MessageBoxButtons.OK, MessageBoxIcon.Asterisk); + } + + break; + default: + return base.ProcessCmdKey(ref msg, keyData); + } + } + catch (Exception ex) + { + Log.Error($"Error handling key '{keyData}'", ex); + } + + return true; + } + } } \ No newline at end of file diff --git a/src/Greenshot/Forms/AnimatingBaseForm.cs b/src/Greenshot/Forms/AnimatingBaseForm.cs index 5456ba037..7f9fec60e 100644 --- a/src/Greenshot/Forms/AnimatingBaseForm.cs +++ b/src/Greenshot/Forms/AnimatingBaseForm.cs @@ -21,10 +21,12 @@ using GreenshotPlugin.Controls; -namespace Greenshot.Forms { - /// - /// This class is only here to help in the Designer mode, so it's clear where the language files are - /// - public class AnimatingBaseForm : AnimatingForm { - } -} +namespace Greenshot.Forms +{ + /// + /// This class is only here to help in the Designer mode, so it's clear where the language files are + /// + public class AnimatingBaseForm : AnimatingForm + { + } +} \ No newline at end of file diff --git a/src/Greenshot/Forms/BaseForm.cs b/src/Greenshot/Forms/BaseForm.cs index ffc0e86db..e67654df5 100644 --- a/src/Greenshot/Forms/BaseForm.cs +++ b/src/Greenshot/Forms/BaseForm.cs @@ -21,10 +21,12 @@ using GreenshotPlugin.Controls; -namespace Greenshot.Forms { - /// - /// This class is only here to help in the Designer mode, so it's clear where the language files are - /// - public class BaseForm : GreenshotForm { - } -} +namespace Greenshot.Forms +{ + /// + /// This class is only here to help in the Designer mode, so it's clear where the language files are + /// + public class BaseForm : GreenshotForm + { + } +} \ No newline at end of file diff --git a/src/Greenshot/Forms/BugReportForm.cs b/src/Greenshot/Forms/BugReportForm.cs index 7c26e1b54..b42f20d18 100644 --- a/src/Greenshot/Forms/BugReportForm.cs +++ b/src/Greenshot/Forms/BugReportForm.cs @@ -18,37 +18,47 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ + using System; using System.Diagnostics; using System.Windows.Forms; using Greenshot.Configuration; using GreenshotPlugin.Core; -namespace Greenshot.Forms { - public partial class BugReportForm : BaseForm { - private BugReportForm() { - // - // The InitializeComponent() call is required for Windows Forms designer support. - // - InitializeComponent(); - ToFront = true; - } +namespace Greenshot.Forms +{ + public partial class BugReportForm : BaseForm + { + private BugReportForm() + { + // + // The InitializeComponent() call is required for Windows Forms designer support. + // + InitializeComponent(); + ToFront = true; + } - public BugReportForm(string bugText) : this() { - textBoxDescription.Text = bugText; - } + public BugReportForm(string bugText) : this() + { + textBoxDescription.Text = bugText; + } - private void LinkLblBugsLinkClicked(object sender, LinkLabelLinkClickedEventArgs e) { - openLink((LinkLabel)sender); - } + private void LinkLblBugsLinkClicked(object sender, LinkLabelLinkClickedEventArgs e) + { + openLink((LinkLabel) sender); + } - private void openLink(LinkLabel link) { - try { - link.LinkVisited = true; - Process.Start(link.Text); - } catch (Exception) { - MessageBox.Show(Language.GetFormattedString(LangKey.error_openlink, link.Text), Language.GetString(LangKey.error)); - } - } - } -} + private void openLink(LinkLabel link) + { + try + { + link.LinkVisited = true; + Process.Start(link.Text); + } + catch (Exception) + { + MessageBox.Show(Language.GetFormattedString(LangKey.error_openlink, link.Text), Language.GetString(LangKey.error)); + } + } + } +} \ No newline at end of file diff --git a/src/Greenshot/Forms/CaptureForm.cs b/src/Greenshot/Forms/CaptureForm.cs index 3cc04ebf6..efdf7583c 100644 --- a/src/Greenshot/Forms/CaptureForm.cs +++ b/src/Greenshot/Forms/CaptureForm.cs @@ -39,365 +39,416 @@ using GreenshotPlugin.IniFile; using GreenshotPlugin.Interfaces; using GreenshotPlugin.Interfaces.Ocr; -namespace Greenshot.Forms { - /// - /// The capture form is used to select a part of the capture - /// - public sealed partial class CaptureForm : AnimatingForm { - private enum FixMode {None, Initiated, Horizontal, Vertical}; +namespace Greenshot.Forms +{ + /// + /// The capture form is used to select a part of the capture + /// + public sealed partial class CaptureForm : AnimatingForm + { + private enum FixMode + { + None, + Initiated, + Horizontal, + Vertical + }; - private static readonly ILog Log = LogManager.GetLogger(typeof(CaptureForm)); - private static readonly CoreConfiguration Conf = IniConfig.GetIniSection(); - private static readonly Brush GreenOverlayBrush = new SolidBrush(Color.FromArgb(50, Color.MediumSeaGreen)); - private static readonly Pen OverlayPen = new Pen(Color.FromArgb(50, Color.Black)); - private static CaptureForm _currentForm; - private static readonly Brush BackgroundBrush; + private static readonly ILog Log = LogManager.GetLogger(typeof(CaptureForm)); + private static readonly CoreConfiguration Conf = IniConfig.GetIniSection(); + private static readonly Brush GreenOverlayBrush = new SolidBrush(Color.FromArgb(50, Color.MediumSeaGreen)); + private static readonly Pen OverlayPen = new Pen(Color.FromArgb(50, Color.Black)); + private static CaptureForm _currentForm; + private static readonly Brush BackgroundBrush; - /// - /// Initialize the background brush - /// - static CaptureForm() { - Image backgroundForTransparency = GreenshotResources.GetImage("Checkerboard.Image"); - BackgroundBrush = new TextureBrush(backgroundForTransparency, WrapMode.Tile); - } + /// + /// Initialize the background brush + /// + static CaptureForm() + { + Image backgroundForTransparency = GreenshotResources.GetImage("Checkerboard.Image"); + BackgroundBrush = new TextureBrush(backgroundForTransparency, WrapMode.Tile); + } - private int _mX; - private int _mY; - private Point _mouseMovePos = Point.Empty; - private Point _cursorPos; - private CaptureMode _captureMode; - private readonly List _windows; - private WindowDetails _selectedCaptureWindow; - private bool _mouseDown; - private Rectangle _captureRect = Rectangle.Empty; - private readonly ICapture _capture; - private Point _previousMousePos = Point.Empty; - private FixMode _fixMode = FixMode.None; - private RectangleAnimator _windowAnimator; - private RectangleAnimator _zoomAnimator; - private readonly bool _isZoomerTransparent = Conf.ZoomerOpacity < 1; - private bool _isCtrlPressed; - private bool _showDebugInfo; + private int _mX; + private int _mY; + private Point _mouseMovePos = Point.Empty; + private Point _cursorPos; + private CaptureMode _captureMode; + private readonly List _windows; + private WindowDetails _selectedCaptureWindow; + private bool _mouseDown; + private Rectangle _captureRect = Rectangle.Empty; + private readonly ICapture _capture; + private Point _previousMousePos = Point.Empty; + private FixMode _fixMode = FixMode.None; + private RectangleAnimator _windowAnimator; + private RectangleAnimator _zoomAnimator; + private readonly bool _isZoomerTransparent = Conf.ZoomerOpacity < 1; + private bool _isCtrlPressed; + private bool _showDebugInfo; - /// - /// Property to access the selected capture rectangle - /// - public Rectangle CaptureRectangle => _captureRect; + /// + /// Property to access the selected capture rectangle + /// + public Rectangle CaptureRectangle => _captureRect; - /// - /// Property to access the used capture mode - /// - public CaptureMode UsedCaptureMode => _captureMode; + /// + /// Property to access the used capture mode + /// + public CaptureMode UsedCaptureMode => _captureMode; - /// - /// Get the selected window - /// - public WindowDetails SelectedCaptureWindow => _selectedCaptureWindow; + /// + /// Get the selected window + /// + public WindowDetails SelectedCaptureWindow => _selectedCaptureWindow; - /// - /// This should prevent children to draw backgrounds - /// - protected override CreateParams CreateParams { - [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)] - get { - CreateParams createParams = base.CreateParams; - createParams.ExStyle |= 0x02000000; - return createParams; - } - } + /// + /// This should prevent children to draw backgrounds + /// + protected override CreateParams CreateParams + { + [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)] + get + { + CreateParams createParams = base.CreateParams; + createParams.ExStyle |= 0x02000000; + return createParams; + } + } - private void ClosedHandler(object sender, EventArgs e) { - _currentForm = null; - // Change the final mode + private void ClosedHandler(object sender, EventArgs e) + { + _currentForm = null; + // Change the final mode if (_captureMode == CaptureMode.Text) { _capture.CaptureDetails.CaptureMode = CaptureMode.Text; - } - Log.Debug("Remove CaptureForm from currentForm"); - } + } - private void ClosingHandler(object sender, EventArgs e) { - Log.Debug("Closing capture form"); - WindowDetails.UnregisterIgnoreHandle(Handle); - } + Log.Debug("Remove CaptureForm from currentForm"); + } - /// - /// This creates the capture form - /// - /// - /// - public CaptureForm(ICapture capture, List windows) { - if (_currentForm != null) { - Log.Warn("Found currentForm, Closing already opened CaptureForm"); - _currentForm.Close(); - _currentForm = null; - Application.DoEvents(); - } - _currentForm = this; + private void ClosingHandler(object sender, EventArgs e) + { + Log.Debug("Closing capture form"); + WindowDetails.UnregisterIgnoreHandle(Handle); + } - // Enable the AnimatingForm - EnableAnimation = true; + /// + /// This creates the capture form + /// + /// + /// + public CaptureForm(ICapture capture, List windows) + { + if (_currentForm != null) + { + Log.Warn("Found currentForm, Closing already opened CaptureForm"); + _currentForm.Close(); + _currentForm = null; + Application.DoEvents(); + } - // clean up - FormClosed += ClosedHandler; + _currentForm = this; - _capture = capture; - _windows = windows; - _captureMode = capture.CaptureDetails.CaptureMode; + // Enable the AnimatingForm + EnableAnimation = true; - // - // The InitializeComponent() call is required for Windows Forms designer support. - // - InitializeComponent(); - // Only double-buffer when we are not in a TerminalServerSession - DoubleBuffered = !IsTerminalServerSession; - Text = @"Greenshot capture form"; + // clean up + FormClosed += ClosedHandler; - // Make sure we never capture the capture-form - WindowDetails.RegisterIgnoreHandle(Handle); - // Un-register at close - FormClosing += ClosingHandler; + _capture = capture; + _windows = windows; + _captureMode = capture.CaptureDetails.CaptureMode; - // set cursor location - _cursorPos = WindowCapture.GetCursorLocationRelativeToScreenBounds(); + // + // The InitializeComponent() call is required for Windows Forms designer support. + // + InitializeComponent(); + // Only double-buffer when we are not in a TerminalServerSession + DoubleBuffered = !IsTerminalServerSession; + Text = @"Greenshot capture form"; - // Initialize the animations, the window capture zooms out from the cursor to the window under the cursor - if (_captureMode == CaptureMode.Window) { - _windowAnimator = new RectangleAnimator(new Rectangle(_cursorPos, Size.Empty), _captureRect, FramesForMillis(700), EasingType.Quintic, EasingMode.EaseOut); - } + // Make sure we never capture the capture-form + WindowDetails.RegisterIgnoreHandle(Handle); + // Un-register at close + FormClosing += ClosingHandler; - // Set the zoomer animation - InitializeZoomer(Conf.ZoomerEnabled); + // set cursor location + _cursorPos = WindowCapture.GetCursorLocationRelativeToScreenBounds(); - SuspendLayout(); - Bounds = capture.ScreenBounds; - ResumeLayout(); + // Initialize the animations, the window capture zooms out from the cursor to the window under the cursor + if (_captureMode == CaptureMode.Window) + { + _windowAnimator = new RectangleAnimator(new Rectangle(_cursorPos, Size.Empty), _captureRect, FramesForMillis(700), EasingType.Quintic, EasingMode.EaseOut); + } - // Fix missing focus - ToFront = true; - TopMost = true; - } + // Set the zoomer animation + InitializeZoomer(Conf.ZoomerEnabled); - /// - /// Create an animation for the zoomer, depending on if it's active or not. - /// - private void InitializeZoomer(bool isOn) { - if (isOn) { - // Initialize the zoom with a invalid position - _zoomAnimator = new RectangleAnimator(Rectangle.Empty, new Rectangle(int.MaxValue, int.MaxValue, 0, 0), FramesForMillis(1000), EasingType.Quintic, EasingMode.EaseOut); - VerifyZoomAnimation(_cursorPos, false); - } - else - { - _zoomAnimator?.ChangeDestination(new Rectangle(Point.Empty, Size.Empty), FramesForMillis(1000)); - } - } + SuspendLayout(); + Bounds = capture.ScreenBounds; + ResumeLayout(); - private void CaptureFormKeyUp(object sender, KeyEventArgs e) { - switch(e.KeyCode) { - case Keys.ShiftKey: - _fixMode = FixMode.None; - break; - case Keys.ControlKey: - _isCtrlPressed = false; - break; - } - } + // Fix missing focus + ToFront = true; + TopMost = true; + } - /// - /// Handle the key down event - /// - /// - /// - private void CaptureFormKeyDown(object sender, KeyEventArgs e) { - int step = _isCtrlPressed ? 10 : 1; + /// + /// Create an animation for the zoomer, depending on if it's active or not. + /// + private void InitializeZoomer(bool isOn) + { + if (isOn) + { + // Initialize the zoom with a invalid position + _zoomAnimator = new RectangleAnimator(Rectangle.Empty, new Rectangle(int.MaxValue, int.MaxValue, 0, 0), FramesForMillis(1000), EasingType.Quintic, + EasingMode.EaseOut); + VerifyZoomAnimation(_cursorPos, false); + } + else + { + _zoomAnimator?.ChangeDestination(new Rectangle(Point.Empty, Size.Empty), FramesForMillis(1000)); + } + } - switch (e.KeyCode) { - case Keys.Up: - Cursor.Position = new Point(Cursor.Position.X, Cursor.Position.Y - step); - break; - case Keys.Down: - Cursor.Position = new Point(Cursor.Position.X, Cursor.Position.Y + step); - break; - case Keys.Left: - Cursor.Position = new Point(Cursor.Position.X - step, Cursor.Position.Y); - break; - case Keys.Right: - Cursor.Position = new Point(Cursor.Position.X + step, Cursor.Position.Y); - break; - case Keys.ShiftKey: - // Fix mode - if (_fixMode == FixMode.None) { - _fixMode = FixMode.Initiated; - } - break; - case Keys.ControlKey: - _isCtrlPressed = true; - break; - case Keys.Escape: - // Cancel - DialogResult = DialogResult.Cancel; - break; - case Keys.M: - // Toggle mouse cursor - _capture.CursorVisible = !_capture.CursorVisible; - Invalidate(); - break; - //// TODO: Enable when the screen capture code works reliable - //case Keys.V: - // // Video - // if (capture.CaptureDetails.CaptureMode != CaptureMode.Video) { - // capture.CaptureDetails.CaptureMode = CaptureMode.Video; - // } else { - // capture.CaptureDetails.CaptureMode = captureMode; - // } - // Invalidate(); - // break; - case Keys.Z: - if (_captureMode == CaptureMode.Region) { - // Toggle zoom - Conf.ZoomerEnabled = !Conf.ZoomerEnabled; - InitializeZoomer(Conf.ZoomerEnabled); - Invalidate(); - } - break; - case Keys.D: - if (_captureMode == CaptureMode.Window) - { - // Toggle debug - _showDebugInfo = !_showDebugInfo; - Invalidate(); - } - break; - case Keys.Space: - // Toggle capture mode - switch (_captureMode) { - case CaptureMode.Region: - // Set the window capture mode - _captureMode = CaptureMode.Window; - // "Fade out" Zoom - InitializeZoomer(false); - // "Fade in" window - _windowAnimator = new RectangleAnimator(new Rectangle(_cursorPos, Size.Empty), _captureRect, FramesForMillis(700), EasingType.Quintic, EasingMode.EaseOut); - _captureRect = Rectangle.Empty; - Invalidate(); - break; - case CaptureMode.Text: - // Set the region capture mode - _captureMode = CaptureMode.Region; - Invalidate(); - break; - case CaptureMode.Window: - // Set the region capture mode - _captureMode = CaptureMode.Region; - // "Fade out" window - _windowAnimator.ChangeDestination(new Rectangle(_cursorPos, Size.Empty), FramesForMillis(700)); - // Fade in zoom - InitializeZoomer(Conf.ZoomerEnabled); - _captureRect = Rectangle.Empty; - Invalidate(); - break; - } - _selectedCaptureWindow = null; - OnMouseMove(this, new MouseEventArgs(MouseButtons.None, 0, Cursor.Position.X, Cursor.Position.Y, 0)); - break; - case Keys.Return: - // Confirm - if (_captureMode == CaptureMode.Window) { - DialogResult = DialogResult.OK; - } else if (!_mouseDown) { - HandleMouseDown(); - } else if (_mouseDown) { - HandleMouseUp(); - } - break; - case Keys.F: - ToFront = !ToFront; - TopMost = !TopMost; - break; - case Keys.T: + private void CaptureFormKeyUp(object sender, KeyEventArgs e) + { + switch (e.KeyCode) + { + case Keys.ShiftKey: + _fixMode = FixMode.None; + break; + case Keys.ControlKey: + _isCtrlPressed = false; + break; + } + } + + /// + /// Handle the key down event + /// + /// + /// + private void CaptureFormKeyDown(object sender, KeyEventArgs e) + { + int step = _isCtrlPressed ? 10 : 1; + + switch (e.KeyCode) + { + case Keys.Up: + Cursor.Position = new Point(Cursor.Position.X, Cursor.Position.Y - step); + break; + case Keys.Down: + Cursor.Position = new Point(Cursor.Position.X, Cursor.Position.Y + step); + break; + case Keys.Left: + Cursor.Position = new Point(Cursor.Position.X - step, Cursor.Position.Y); + break; + case Keys.Right: + Cursor.Position = new Point(Cursor.Position.X + step, Cursor.Position.Y); + break; + case Keys.ShiftKey: + // Fix mode + if (_fixMode == FixMode.None) + { + _fixMode = FixMode.Initiated; + } + + break; + case Keys.ControlKey: + _isCtrlPressed = true; + break; + case Keys.Escape: + // Cancel + DialogResult = DialogResult.Cancel; + break; + case Keys.M: + // Toggle mouse cursor + _capture.CursorVisible = !_capture.CursorVisible; + Invalidate(); + break; + //// TODO: Enable when the screen capture code works reliable + //case Keys.V: + // // Video + // if (capture.CaptureDetails.CaptureMode != CaptureMode.Video) { + // capture.CaptureDetails.CaptureMode = CaptureMode.Video; + // } else { + // capture.CaptureDetails.CaptureMode = captureMode; + // } + // Invalidate(); + // break; + case Keys.Z: + if (_captureMode == CaptureMode.Region) + { + // Toggle zoom + Conf.ZoomerEnabled = !Conf.ZoomerEnabled; + InitializeZoomer(Conf.ZoomerEnabled); + Invalidate(); + } + + break; + case Keys.D: + if (_captureMode == CaptureMode.Window) + { + // Toggle debug + _showDebugInfo = !_showDebugInfo; + Invalidate(); + } + + break; + case Keys.Space: + // Toggle capture mode + switch (_captureMode) + { + case CaptureMode.Region: + // Set the window capture mode + _captureMode = CaptureMode.Window; + // "Fade out" Zoom + InitializeZoomer(false); + // "Fade in" window + _windowAnimator = new RectangleAnimator(new Rectangle(_cursorPos, Size.Empty), _captureRect, FramesForMillis(700), EasingType.Quintic, + EasingMode.EaseOut); + _captureRect = Rectangle.Empty; + Invalidate(); + break; + case CaptureMode.Text: + // Set the region capture mode + _captureMode = CaptureMode.Region; + Invalidate(); + break; + case CaptureMode.Window: + // Set the region capture mode + _captureMode = CaptureMode.Region; + // "Fade out" window + _windowAnimator.ChangeDestination(new Rectangle(_cursorPos, Size.Empty), FramesForMillis(700)); + // Fade in zoom + InitializeZoomer(Conf.ZoomerEnabled); + _captureRect = Rectangle.Empty; + Invalidate(); + break; + } + + _selectedCaptureWindow = null; + OnMouseMove(this, new MouseEventArgs(MouseButtons.None, 0, Cursor.Position.X, Cursor.Position.Y, 0)); + break; + case Keys.Return: + // Confirm + if (_captureMode == CaptureMode.Window) + { + DialogResult = DialogResult.OK; + } + else if (!_mouseDown) + { + HandleMouseDown(); + } + else if (_mouseDown) + { + HandleMouseUp(); + } + + break; + case Keys.F: + ToFront = !ToFront; + TopMost = !TopMost; + break; + case Keys.T: _captureMode = CaptureMode.Text; - if (_capture.CaptureDetails.OcrInformation is null) - { - var ocrProvider = SimpleServiceProvider.Current.GetInstance(); - if (ocrProvider != null) - { - var uiTaskScheduler = SimpleServiceProvider.Current.GetInstance(); + if (_capture.CaptureDetails.OcrInformation is null) + { + var ocrProvider = SimpleServiceProvider.Current.GetInstance(); + if (ocrProvider != null) + { + var uiTaskScheduler = SimpleServiceProvider.Current.GetInstance(); - Task.Factory.StartNew(async () => - { - _capture.CaptureDetails.OcrInformation = await ocrProvider.DoOcrAsync(_capture.Image); - Invalidate(); - }, CancellationToken.None, TaskCreationOptions.None, uiTaskScheduler); - } - } + Task.Factory.StartNew(async () => + { + _capture.CaptureDetails.OcrInformation = await ocrProvider.DoOcrAsync(_capture.Image); + Invalidate(); + }, CancellationToken.None, TaskCreationOptions.None, uiTaskScheduler); + } + } else { Invalidate(); - } - break; + } + + break; } - } + } /// - /// The mousedown handler of the capture form - /// - /// - /// - private void OnMouseDown(object sender, MouseEventArgs e) { - if (e.Button == MouseButtons.Left) { - HandleMouseDown(); - } - } + /// The mousedown handler of the capture form + /// + /// + /// + private void OnMouseDown(object sender, MouseEventArgs e) + { + if (e.Button == MouseButtons.Left) + { + HandleMouseDown(); + } + } - private void HandleMouseDown() { - Point tmpCursorLocation = WindowCapture.GetCursorLocationRelativeToScreenBounds(); - _mX = tmpCursorLocation.X; - _mY = tmpCursorLocation.Y; - _mouseDown = true; - OnMouseMove(this, null); - Invalidate(); - } + private void HandleMouseDown() + { + Point tmpCursorLocation = WindowCapture.GetCursorLocationRelativeToScreenBounds(); + _mX = tmpCursorLocation.X; + _mY = tmpCursorLocation.Y; + _mouseDown = true; + OnMouseMove(this, null); + Invalidate(); + } - private void HandleMouseUp() { - // If the mouse goes up we set down to false (nice logic!) - _mouseDown = false; - // Check if anything is selected - if (_captureMode == CaptureMode.Window && _selectedCaptureWindow != null) { - // Go and process the capture - DialogResult = DialogResult.OK; - } else if (_captureRect.Height > 0 && _captureRect.Width > 0) { - // correct the GUI width to real width if Region mode - if (_captureMode == CaptureMode.Region || _captureMode == CaptureMode.Text) { - _captureRect.Width += 1; - _captureRect.Height += 1; - } - // Go and process the capture - DialogResult = DialogResult.OK; - } else if (_captureMode == CaptureMode.Text && IsWordUnderCursor(_mouseMovePos)) + private void HandleMouseUp() + { + // If the mouse goes up we set down to false (nice logic!) + _mouseDown = false; + // Check if anything is selected + if (_captureMode == CaptureMode.Window && _selectedCaptureWindow != null) + { + // Go and process the capture + DialogResult = DialogResult.OK; + } + else if (_captureRect.Height > 0 && _captureRect.Width > 0) + { + // correct the GUI width to real width if Region mode + if (_captureMode == CaptureMode.Region || _captureMode == CaptureMode.Text) + { + _captureRect.Width += 1; + _captureRect.Height += 1; + } + + // Go and process the capture + DialogResult = DialogResult.OK; + } + else if (_captureMode == CaptureMode.Text && IsWordUnderCursor(_mouseMovePos)) { // Handle a click on a single word _captureRect = new Rectangle(_mouseMovePos, new Size(1, 1)); // Go and process the capture DialogResult = DialogResult.OK; - } else { - Invalidate(); - } + } + else + { + Invalidate(); + } + } - } - - /// - /// - /// - /// - /// + /// + /// + /// + /// + /// private bool IsWordUnderCursor(Point cursorLocation) { if (_captureMode != CaptureMode.Text || _capture.CaptureDetails.OcrInformation == null) return false; var ocrInfo = _capture.CaptureDetails.OcrInformation; - foreach (var line in ocrInfo.Lines) + foreach (var line in ocrInfo.Lines) { var lineBounds = line.CalculatedBounds; if (lineBounds.IsEmpty) continue; @@ -415,210 +466,255 @@ namespace Greenshot.Forms { return false; } - /// - /// The mouse up handler of the capture form - /// - /// - /// - private void OnMouseUp(object sender, MouseEventArgs e) { - if (_mouseDown) { - HandleMouseUp(); - } - } + /// + /// The mouse up handler of the capture form + /// + /// + /// + private void OnMouseUp(object sender, MouseEventArgs e) + { + if (_mouseDown) + { + HandleMouseUp(); + } + } - /// - /// This method is used to "fix" the mouse coordinates when keeping shift/ctrl pressed - /// - /// - /// - private Point FixMouseCoordinates(Point currentMouse) { - if (_fixMode == FixMode.Initiated) { - if (_previousMousePos.X != currentMouse.X) { - _fixMode = FixMode.Vertical; - } else if (_previousMousePos.Y != currentMouse.Y) { - _fixMode = FixMode.Horizontal; - } - } else if (_fixMode == FixMode.Vertical) { - currentMouse = new Point(currentMouse.X, _previousMousePos.Y); - } else if (_fixMode == FixMode.Horizontal) { - currentMouse = new Point(_previousMousePos.X, currentMouse.Y); - } - _previousMousePos = currentMouse; - return currentMouse; - } + /// + /// This method is used to "fix" the mouse coordinates when keeping shift/ctrl pressed + /// + /// + /// + private Point FixMouseCoordinates(Point currentMouse) + { + if (_fixMode == FixMode.Initiated) + { + if (_previousMousePos.X != currentMouse.X) + { + _fixMode = FixMode.Vertical; + } + else if (_previousMousePos.Y != currentMouse.Y) + { + _fixMode = FixMode.Horizontal; + } + } + else if (_fixMode == FixMode.Vertical) + { + currentMouse = new Point(currentMouse.X, _previousMousePos.Y); + } + else if (_fixMode == FixMode.Horizontal) + { + currentMouse = new Point(_previousMousePos.X, currentMouse.Y); + } - /// - /// The mouse move handler of the capture form - /// - /// object - /// MouseEventArgs - private void OnMouseMove(object sender, MouseEventArgs e) { - // Make sure the mouse coordinates are fixed, when pressing shift - var mouseMovePos = FixMouseCoordinates(User32.GetCursorLocation()); - _mouseMovePos = WindowCapture.GetLocationRelativeToScreenBounds(mouseMovePos); - } + _previousMousePos = currentMouse; + return currentMouse; + } - /// - /// Helper method to simplify check - /// - /// - /// - private bool IsAnimating(IAnimator animator) { - if (animator == null) { - return false; - } - return animator.HasNext; - } + /// + /// The mouse move handler of the capture form + /// + /// object + /// MouseEventArgs + private void OnMouseMove(object sender, MouseEventArgs e) + { + // Make sure the mouse coordinates are fixed, when pressing shift + var mouseMovePos = FixMouseCoordinates(User32.GetCursorLocation()); + _mouseMovePos = WindowCapture.GetLocationRelativeToScreenBounds(mouseMovePos); + } - /// - /// update the frame, this only invalidates - /// - protected override void Animate() { - Point lastPos = _cursorPos; - _cursorPos = _mouseMovePos; + /// + /// Helper method to simplify check + /// + /// + /// + private bool IsAnimating(IAnimator animator) + { + if (animator == null) + { + return false; + } - if (_selectedCaptureWindow != null && lastPos.Equals(_cursorPos) && !IsAnimating(_zoomAnimator) && !IsAnimating(_windowAnimator)) { - return; - } + return animator.HasNext; + } - WindowDetails lastWindow = _selectedCaptureWindow; - bool horizontalMove = false; - bool verticalMove = false; + /// + /// update the frame, this only invalidates + /// + protected override void Animate() + { + Point lastPos = _cursorPos; + _cursorPos = _mouseMovePos; - if (lastPos.X != _cursorPos.X) { - horizontalMove = true; - } - if (lastPos.Y != _cursorPos.Y) { - verticalMove = true; - } + if (_selectedCaptureWindow != null && lastPos.Equals(_cursorPos) && !IsAnimating(_zoomAnimator) && !IsAnimating(_windowAnimator)) + { + return; + } - if ((_captureMode == CaptureMode.Region || _captureMode == CaptureMode.Text) && _mouseDown) { - _captureRect = GuiRectangle.GetGuiRectangle(_cursorPos.X, _cursorPos.Y, _mX - _cursorPos.X, _mY - _cursorPos.Y); - } + WindowDetails lastWindow = _selectedCaptureWindow; + bool horizontalMove = false; + bool verticalMove = false; - // Iterate over the found windows and check if the current location is inside a window - Point cursorPosition = Cursor.Position; - _selectedCaptureWindow = null; - lock (_windows) { - foreach (var window in _windows) { - if (!window.Contains(cursorPosition)) - { - continue; - } - // Only go over the children if we are in window mode - _selectedCaptureWindow = CaptureMode.Window == _captureMode ? window.FindChildUnderPoint(cursorPosition) : window; - break; - } - } + if (lastPos.X != _cursorPos.X) + { + horizontalMove = true; + } - if (_selectedCaptureWindow != null && !_selectedCaptureWindow.Equals(lastWindow)) { - _capture.CaptureDetails.Title = _selectedCaptureWindow.Text; - _capture.CaptureDetails.AddMetaData("windowtitle", _selectedCaptureWindow.Text); - if (_captureMode == CaptureMode.Window) { - // Here we want to capture the window which is under the mouse - _captureRect = _selectedCaptureWindow.WindowRectangle; - // As the ClientRectangle is not in Bitmap coordinates, we need to correct. - _captureRect.Offset(-_capture.ScreenBounds.Location.X, -_capture.ScreenBounds.Location.Y); - } - } + if (lastPos.Y != _cursorPos.Y) + { + verticalMove = true; + } - Rectangle invalidateRectangle; - if (_mouseDown && (_captureMode != CaptureMode.Window)) { - int x1 = Math.Min(_mX, lastPos.X); - int x2 = Math.Max(_mX, lastPos.X); - int y1 = Math.Min(_mY, lastPos.Y); - int y2 = Math.Max(_mY, lastPos.Y); - x1= Math.Min(x1, _cursorPos.X); - x2= Math.Max(x2, _cursorPos.X); - y1= Math.Min(y1, _cursorPos.Y); - y2= Math.Max(y2, _cursorPos.Y); + if ((_captureMode == CaptureMode.Region || _captureMode == CaptureMode.Text) && _mouseDown) + { + _captureRect = GuiRectangle.GetGuiRectangle(_cursorPos.X, _cursorPos.Y, _mX - _cursorPos.X, _mY - _cursorPos.Y); + } - // Safety correction - x2 += 2; - y2 += 2; + // Iterate over the found windows and check if the current location is inside a window + Point cursorPosition = Cursor.Position; + _selectedCaptureWindow = null; + lock (_windows) + { + foreach (var window in _windows) + { + if (!window.Contains(cursorPosition)) + { + continue; + } - // Here we correct for text-size + // Only go over the children if we are in window mode + _selectedCaptureWindow = CaptureMode.Window == _captureMode ? window.FindChildUnderPoint(cursorPosition) : window; + break; + } + } - // Calculate the size - int textForWidth = Math.Max(Math.Abs(_mX - _cursorPos.X), Math.Abs(_mX - lastPos.X)); - int textForHeight = Math.Max(Math.Abs(_mY - _cursorPos.Y), Math.Abs(_mY - lastPos.Y)); + if (_selectedCaptureWindow != null && !_selectedCaptureWindow.Equals(lastWindow)) + { + _capture.CaptureDetails.Title = _selectedCaptureWindow.Text; + _capture.CaptureDetails.AddMetaData("windowtitle", _selectedCaptureWindow.Text); + if (_captureMode == CaptureMode.Window) + { + // Here we want to capture the window which is under the mouse + _captureRect = _selectedCaptureWindow.WindowRectangle; + // As the ClientRectangle is not in Bitmap coordinates, we need to correct. + _captureRect.Offset(-_capture.ScreenBounds.Location.X, -_capture.ScreenBounds.Location.Y); + } + } - using (Font rulerFont = new Font(FontFamily.GenericSansSerif, 8)) { - Size measureWidth = TextRenderer.MeasureText(textForWidth.ToString(CultureInfo.InvariantCulture), rulerFont); - x1 -= measureWidth.Width + 15; + Rectangle invalidateRectangle; + if (_mouseDown && (_captureMode != CaptureMode.Window)) + { + int x1 = Math.Min(_mX, lastPos.X); + int x2 = Math.Max(_mX, lastPos.X); + int y1 = Math.Min(_mY, lastPos.Y); + int y2 = Math.Max(_mY, lastPos.Y); + x1 = Math.Min(x1, _cursorPos.X); + x2 = Math.Max(x2, _cursorPos.X); + y1 = Math.Min(y1, _cursorPos.Y); + y2 = Math.Max(y2, _cursorPos.Y); - Size measureHeight = TextRenderer.MeasureText(textForHeight.ToString(CultureInfo.InvariantCulture), rulerFont); - y1 -= measureHeight.Height + 10; - } - invalidateRectangle = new Rectangle(x1,y1, x2-x1, y2-y1); - Invalidate(invalidateRectangle); - } else if (_captureMode != CaptureMode.Window) { - if (!IsTerminalServerSession) { - Rectangle allScreenBounds = WindowCapture.GetScreenBounds(); - allScreenBounds.Location = WindowCapture.GetLocationRelativeToScreenBounds(allScreenBounds.Location); - if (verticalMove) { - // Before - invalidateRectangle = GuiRectangle.GetGuiRectangle(allScreenBounds.Left, lastPos.Y - 2, Width + 2, 45); - Invalidate(invalidateRectangle); - // After - invalidateRectangle = GuiRectangle.GetGuiRectangle(allScreenBounds.Left, _cursorPos.Y - 2, Width + 2, 45); - Invalidate(invalidateRectangle); - } - if (horizontalMove) { - // Before - invalidateRectangle = GuiRectangle.GetGuiRectangle(lastPos.X - 2, allScreenBounds.Top, 75, Height + 2); - Invalidate(invalidateRectangle); - // After - invalidateRectangle = GuiRectangle.GetGuiRectangle(_cursorPos.X - 2, allScreenBounds.Top, 75, Height + 2); - Invalidate(invalidateRectangle); - } - } - } else { - if (_selectedCaptureWindow != null && !_selectedCaptureWindow.Equals(lastWindow)) { - // Window changes, make new animation from current to target - _windowAnimator.ChangeDestination(_captureRect, FramesForMillis(700)); - } - } - // always animate the Window area through to the last frame, so we see the fade-in/out untill the end - // Using a safety "offset" to make sure the text is invalidated too - const int safetySize = 30; - // Check if the animation needs to be drawn - if (IsAnimating(_windowAnimator)) { - invalidateRectangle = _windowAnimator.Current; - invalidateRectangle.Inflate(safetySize, safetySize); - Invalidate(invalidateRectangle); - invalidateRectangle = _windowAnimator.Next(); - invalidateRectangle.Inflate(safetySize, safetySize); - Invalidate(invalidateRectangle); - // Check if this was the last of the windows animations in the normal region capture. - if (_captureMode != CaptureMode.Window && !IsAnimating(_windowAnimator)) { - Invalidate(); - } - } + // Safety correction + x2 += 2; + y2 += 2; - if (_zoomAnimator != null && (IsAnimating(_zoomAnimator) || _captureMode != CaptureMode.Window)) { - // Make sure we invalidate the old zoom area - invalidateRectangle = _zoomAnimator.Current; - invalidateRectangle.Offset(lastPos); - Invalidate(invalidateRectangle); - // Only verify if we are really showing the zoom, not the outgoing animation - if (Conf.ZoomerEnabled && _captureMode != CaptureMode.Window) { - VerifyZoomAnimation(_cursorPos, false); - } - // The following logic is not needed, next always returns the current if there are no frames left - // but it makes more sense if we want to change something in the logic - invalidateRectangle = IsAnimating(_zoomAnimator) ? _zoomAnimator.Next() : _zoomAnimator.Current; - invalidateRectangle.Offset(_cursorPos); - Invalidate(invalidateRectangle); - } + // Here we correct for text-size - // OCR + // Calculate the size + int textForWidth = Math.Max(Math.Abs(_mX - _cursorPos.X), Math.Abs(_mX - lastPos.X)); + int textForHeight = Math.Max(Math.Abs(_mY - _cursorPos.Y), Math.Abs(_mY - lastPos.Y)); + + using (Font rulerFont = new Font(FontFamily.GenericSansSerif, 8)) + { + Size measureWidth = TextRenderer.MeasureText(textForWidth.ToString(CultureInfo.InvariantCulture), rulerFont); + x1 -= measureWidth.Width + 15; + + Size measureHeight = TextRenderer.MeasureText(textForHeight.ToString(CultureInfo.InvariantCulture), rulerFont); + y1 -= measureHeight.Height + 10; + } + + invalidateRectangle = new Rectangle(x1, y1, x2 - x1, y2 - y1); + Invalidate(invalidateRectangle); + } + else if (_captureMode != CaptureMode.Window) + { + if (!IsTerminalServerSession) + { + Rectangle allScreenBounds = WindowCapture.GetScreenBounds(); + allScreenBounds.Location = WindowCapture.GetLocationRelativeToScreenBounds(allScreenBounds.Location); + if (verticalMove) + { + // Before + invalidateRectangle = GuiRectangle.GetGuiRectangle(allScreenBounds.Left, lastPos.Y - 2, Width + 2, 45); + Invalidate(invalidateRectangle); + // After + invalidateRectangle = GuiRectangle.GetGuiRectangle(allScreenBounds.Left, _cursorPos.Y - 2, Width + 2, 45); + Invalidate(invalidateRectangle); + } + + if (horizontalMove) + { + // Before + invalidateRectangle = GuiRectangle.GetGuiRectangle(lastPos.X - 2, allScreenBounds.Top, 75, Height + 2); + Invalidate(invalidateRectangle); + // After + invalidateRectangle = GuiRectangle.GetGuiRectangle(_cursorPos.X - 2, allScreenBounds.Top, 75, Height + 2); + Invalidate(invalidateRectangle); + } + } + } + else + { + if (_selectedCaptureWindow != null && !_selectedCaptureWindow.Equals(lastWindow)) + { + // Window changes, make new animation from current to target + _windowAnimator.ChangeDestination(_captureRect, FramesForMillis(700)); + } + } + + // always animate the Window area through to the last frame, so we see the fade-in/out untill the end + // Using a safety "offset" to make sure the text is invalidated too + const int safetySize = 30; + // Check if the animation needs to be drawn + if (IsAnimating(_windowAnimator)) + { + invalidateRectangle = _windowAnimator.Current; + invalidateRectangle.Inflate(safetySize, safetySize); + Invalidate(invalidateRectangle); + invalidateRectangle = _windowAnimator.Next(); + invalidateRectangle.Inflate(safetySize, safetySize); + Invalidate(invalidateRectangle); + // Check if this was the last of the windows animations in the normal region capture. + if (_captureMode != CaptureMode.Window && !IsAnimating(_windowAnimator)) + { + Invalidate(); + } + } + + if (_zoomAnimator != null && (IsAnimating(_zoomAnimator) || _captureMode != CaptureMode.Window)) + { + // Make sure we invalidate the old zoom area + invalidateRectangle = _zoomAnimator.Current; + invalidateRectangle.Offset(lastPos); + Invalidate(invalidateRectangle); + // Only verify if we are really showing the zoom, not the outgoing animation + if (Conf.ZoomerEnabled && _captureMode != CaptureMode.Window) + { + VerifyZoomAnimation(_cursorPos, false); + } + + // The following logic is not needed, next always returns the current if there are no frames left + // but it makes more sense if we want to change something in the logic + invalidateRectangle = IsAnimating(_zoomAnimator) ? _zoomAnimator.Next() : _zoomAnimator.Current; + invalidateRectangle.Offset(_cursorPos); + Invalidate(invalidateRectangle); + } + + // OCR if (_captureMode == CaptureMode.Text && _capture.CaptureDetails.OcrInformation != null) { var ocrInfo = _capture.CaptureDetails.OcrInformation; invalidateRectangle = Rectangle.Empty; - foreach (var line in ocrInfo.Lines) + foreach (var line in ocrInfo.Lines) { var lineBounds = line.CalculatedBounds; if (!lineBounds.IsEmpty) @@ -639,8 +735,8 @@ namespace Greenshot.Forms { else { invalidateRectangle = Rectangle.Union(invalidateRectangle, word.Bounds); - } - } + } + } } } } @@ -657,172 +753,207 @@ namespace Greenshot.Forms { { invalidateRectangle = Rectangle.Union(invalidateRectangle, word.Bounds); } + break; } } } } + if (!invalidateRectangle.IsEmpty) { Invalidate(invalidateRectangle); - } - } - // Force update "now" - Update(); - } + } + } - /// - /// This makes sure there is no background painted, as we have complete "paint" control it doesn't make sense to do otherwise. - /// - /// - protected override void OnPaintBackground(PaintEventArgs pevent) { - } + // Force update "now" + Update(); + } - /// - /// Checks if the Zoom area can move there where it wants to go, change direction if not. - /// - /// preferred destination location for the zoom area - /// false to try to find a location which is neither out of screen bounds nor intersects with the selected rectangle - private void VerifyZoomAnimation(Point pos, bool allowZoomOverCaptureRect) { - Rectangle screenBounds = Screen.GetBounds(MousePosition); - // convert to be relative to top left corner of all screen bounds - screenBounds.Location = WindowCapture.GetLocationRelativeToScreenBounds(screenBounds.Location); - int relativeZoomSize = Math.Min(screenBounds.Width, screenBounds.Height) / 5; - // Make sure the final size is a plural of 4, this makes it look better - relativeZoomSize -= relativeZoomSize % 4; - Size zoomSize = new Size(relativeZoomSize, relativeZoomSize); - Point zoomOffset = new Point(20, 20); + /// + /// This makes sure there is no background painted, as we have complete "paint" control it doesn't make sense to do otherwise. + /// + /// + protected override void OnPaintBackground(PaintEventArgs pevent) + { + } - Rectangle targetRectangle = _zoomAnimator.Final; - targetRectangle.Offset(pos); - if (!screenBounds.Contains(targetRectangle) || (!allowZoomOverCaptureRect && _captureRect.IntersectsWith(targetRectangle))) { - Point destinationLocation = Point.Empty; - Rectangle tl = new Rectangle(pos.X - (zoomOffset.X + zoomSize.Width), pos.Y - (zoomOffset.Y + zoomSize.Height), zoomSize.Width, zoomSize.Height); - Rectangle tr = new Rectangle(pos.X + zoomOffset.X, pos.Y - (zoomOffset.Y + zoomSize.Height), zoomSize.Width, zoomSize.Height); - Rectangle bl = new Rectangle(pos.X - (zoomOffset.X + zoomSize.Width), pos.Y + zoomOffset.Y, zoomSize.Width, zoomSize.Height); - Rectangle br = new Rectangle(pos.X + zoomOffset.X, pos.Y + zoomOffset.Y, zoomSize.Width, zoomSize.Height); - if (screenBounds.Contains(br) && (allowZoomOverCaptureRect || !_captureRect.IntersectsWith(br))) { - destinationLocation = new Point(zoomOffset.X, zoomOffset.Y); - } else if (screenBounds.Contains(bl) && (allowZoomOverCaptureRect || !_captureRect.IntersectsWith(bl))) { - destinationLocation = new Point(-zoomOffset.X - zoomSize.Width, zoomOffset.Y); - } else if (screenBounds.Contains(tr) && (allowZoomOverCaptureRect || !_captureRect.IntersectsWith(tr))) { - destinationLocation = new Point(zoomOffset.X, -zoomOffset.Y - zoomSize.Width); - } else if (screenBounds.Contains(tl) && (allowZoomOverCaptureRect || !_captureRect.IntersectsWith(tl))) { - destinationLocation = new Point(-zoomOffset.X - zoomSize.Width, -zoomOffset.Y - zoomSize.Width); - } - if (destinationLocation == Point.Empty && !allowZoomOverCaptureRect) { - VerifyZoomAnimation(pos, true); - } else { - _zoomAnimator.ChangeDestination(new Rectangle(destinationLocation, zoomSize)); - } - } - } + /// + /// Checks if the Zoom area can move there where it wants to go, change direction if not. + /// + /// preferred destination location for the zoom area + /// false to try to find a location which is neither out of screen bounds nor intersects with the selected rectangle + private void VerifyZoomAnimation(Point pos, bool allowZoomOverCaptureRect) + { + Rectangle screenBounds = Screen.GetBounds(MousePosition); + // convert to be relative to top left corner of all screen bounds + screenBounds.Location = WindowCapture.GetLocationRelativeToScreenBounds(screenBounds.Location); + int relativeZoomSize = Math.Min(screenBounds.Width, screenBounds.Height) / 5; + // Make sure the final size is a plural of 4, this makes it look better + relativeZoomSize -= relativeZoomSize % 4; + Size zoomSize = new Size(relativeZoomSize, relativeZoomSize); + Point zoomOffset = new Point(20, 20); - /// - /// Draw the zoomed area - /// - /// - /// - /// - private void DrawZoom(Graphics graphics, Rectangle sourceRectangle, Rectangle destinationRectangle) { - if (_capture.Image == null) { - return; - } - ImageAttributes attributes; + Rectangle targetRectangle = _zoomAnimator.Final; + targetRectangle.Offset(pos); + if (!screenBounds.Contains(targetRectangle) || (!allowZoomOverCaptureRect && _captureRect.IntersectsWith(targetRectangle))) + { + Point destinationLocation = Point.Empty; + Rectangle tl = new Rectangle(pos.X - (zoomOffset.X + zoomSize.Width), pos.Y - (zoomOffset.Y + zoomSize.Height), zoomSize.Width, zoomSize.Height); + Rectangle tr = new Rectangle(pos.X + zoomOffset.X, pos.Y - (zoomOffset.Y + zoomSize.Height), zoomSize.Width, zoomSize.Height); + Rectangle bl = new Rectangle(pos.X - (zoomOffset.X + zoomSize.Width), pos.Y + zoomOffset.Y, zoomSize.Width, zoomSize.Height); + Rectangle br = new Rectangle(pos.X + zoomOffset.X, pos.Y + zoomOffset.Y, zoomSize.Width, zoomSize.Height); + if (screenBounds.Contains(br) && (allowZoomOverCaptureRect || !_captureRect.IntersectsWith(br))) + { + destinationLocation = new Point(zoomOffset.X, zoomOffset.Y); + } + else if (screenBounds.Contains(bl) && (allowZoomOverCaptureRect || !_captureRect.IntersectsWith(bl))) + { + destinationLocation = new Point(-zoomOffset.X - zoomSize.Width, zoomOffset.Y); + } + else if (screenBounds.Contains(tr) && (allowZoomOverCaptureRect || !_captureRect.IntersectsWith(tr))) + { + destinationLocation = new Point(zoomOffset.X, -zoomOffset.Y - zoomSize.Width); + } + else if (screenBounds.Contains(tl) && (allowZoomOverCaptureRect || !_captureRect.IntersectsWith(tl))) + { + destinationLocation = new Point(-zoomOffset.X - zoomSize.Width, -zoomOffset.Y - zoomSize.Width); + } - if (_isZoomerTransparent) { - //create a color matrix object to change the opacy - ColorMatrix opacyMatrix = new ColorMatrix - { - Matrix33 = Conf.ZoomerOpacity - }; - attributes = new ImageAttributes(); - attributes.SetColorMatrix(opacyMatrix, ColorMatrixFlag.Default, ColorAdjustType.Bitmap); - } else { - attributes = null; - } + if (destinationLocation == Point.Empty && !allowZoomOverCaptureRect) + { + VerifyZoomAnimation(pos, true); + } + else + { + _zoomAnimator.ChangeDestination(new Rectangle(destinationLocation, zoomSize)); + } + } + } - graphics.SmoothingMode = SmoothingMode.HighQuality; - graphics.InterpolationMode = InterpolationMode.NearestNeighbor; - graphics.CompositingQuality = CompositingQuality.HighSpeed; - graphics.PixelOffsetMode = PixelOffsetMode.HighQuality; + /// + /// Draw the zoomed area + /// + /// + /// + /// + private void DrawZoom(Graphics graphics, Rectangle sourceRectangle, Rectangle destinationRectangle) + { + if (_capture.Image == null) + { + return; + } - using (GraphicsPath path = new GraphicsPath()) { - path.AddEllipse(destinationRectangle); - graphics.SetClip(path); - if (!_isZoomerTransparent) { - graphics.FillRectangle(BackgroundBrush, destinationRectangle); - graphics.DrawImage(_capture.Image, destinationRectangle, sourceRectangle, GraphicsUnit.Pixel); - } else { - graphics.DrawImage(_capture.Image, destinationRectangle, sourceRectangle.X, sourceRectangle.Y, sourceRectangle.Width, sourceRectangle.Height, GraphicsUnit.Pixel, attributes); - } - } - int alpha = (int)(255 * Conf.ZoomerOpacity); - Color opacyWhite = Color.FromArgb(alpha, 255, 255, 255); - Color opacyBlack = Color.FromArgb(alpha, 0, 0, 0); + ImageAttributes attributes; - // Draw the circle around the zoomer - using (Pen pen = new Pen(opacyWhite, 2)) { - graphics.DrawEllipse(pen, destinationRectangle); - } + if (_isZoomerTransparent) + { + //create a color matrix object to change the opacy + ColorMatrix opacyMatrix = new ColorMatrix + { + Matrix33 = Conf.ZoomerOpacity + }; + attributes = new ImageAttributes(); + attributes.SetColorMatrix(opacyMatrix, ColorMatrixFlag.Default, ColorAdjustType.Bitmap); + } + else + { + attributes = null; + } - // Make sure we don't have a pixeloffsetmode/smoothingmode when drawing the crosshair - graphics.SmoothingMode = SmoothingMode.None; - graphics.PixelOffsetMode = PixelOffsetMode.None; + graphics.SmoothingMode = SmoothingMode.HighQuality; + graphics.InterpolationMode = InterpolationMode.NearestNeighbor; + graphics.CompositingQuality = CompositingQuality.HighSpeed; + graphics.PixelOffsetMode = PixelOffsetMode.HighQuality; - // Calculate some values - int pixelThickness = destinationRectangle.Width / sourceRectangle.Width; - int halfWidth = destinationRectangle.Width / 2; - int halfWidthEnd = destinationRectangle.Width / 2 - pixelThickness / 2; - int halfHeight = destinationRectangle.Height / 2; - int halfHeightEnd = destinationRectangle.Height / 2 - pixelThickness / 2; + using (GraphicsPath path = new GraphicsPath()) + { + path.AddEllipse(destinationRectangle); + graphics.SetClip(path); + if (!_isZoomerTransparent) + { + graphics.FillRectangle(BackgroundBrush, destinationRectangle); + graphics.DrawImage(_capture.Image, destinationRectangle, sourceRectangle, GraphicsUnit.Pixel); + } + else + { + graphics.DrawImage(_capture.Image, destinationRectangle, sourceRectangle.X, sourceRectangle.Y, sourceRectangle.Width, sourceRectangle.Height, + GraphicsUnit.Pixel, attributes); + } + } - int drawAtHeight = destinationRectangle.Y + halfHeight; - int drawAtWidth = destinationRectangle.X + halfWidth; - int padding = pixelThickness; + int alpha = (int) (255 * Conf.ZoomerOpacity); + Color opacyWhite = Color.FromArgb(alpha, 255, 255, 255); + Color opacyBlack = Color.FromArgb(alpha, 0, 0, 0); - // Pen to draw - using (Pen pen = new Pen(opacyBlack, pixelThickness)) { - // Draw the cross-hair-lines - // Vertical top to middle - graphics.DrawLine(pen, drawAtWidth, destinationRectangle.Y + padding, drawAtWidth, destinationRectangle.Y + halfHeightEnd - padding); - // Vertical middle + 1 to bottom - graphics.DrawLine(pen, drawAtWidth, destinationRectangle.Y + halfHeightEnd + 2 * padding, drawAtWidth, destinationRectangle.Y + destinationRectangle.Width - padding); - // Horizontal left to middle - graphics.DrawLine(pen, destinationRectangle.X + padding, drawAtHeight, destinationRectangle.X + halfWidthEnd - padding, drawAtHeight); - // Horizontal middle + 1 to right - graphics.DrawLine(pen, destinationRectangle.X + halfWidthEnd + 2 * padding, drawAtHeight, destinationRectangle.X + destinationRectangle.Width - padding, drawAtHeight); + // Draw the circle around the zoomer + using (Pen pen = new Pen(opacyWhite, 2)) + { + graphics.DrawEllipse(pen, destinationRectangle); + } - // Fix offset for drawing the white rectangle around the cross-hair-lines - drawAtHeight -= pixelThickness / 2; - drawAtWidth -= pixelThickness / 2; - // Fix off by one error with the DrawRectangle - pixelThickness -= 1; - // Change the color and the pen width - pen.Color = opacyWhite; - pen.Width = 1; - // Vertical top to middle - graphics.DrawRectangle(pen, drawAtWidth, destinationRectangle.Y + padding, pixelThickness, halfHeightEnd - 2 * padding - 1); - // Vertical middle + 1 to bottom - graphics.DrawRectangle(pen, drawAtWidth, destinationRectangle.Y + halfHeightEnd + 2 * padding, pixelThickness, halfHeightEnd - 2 * padding - 1); - // Horizontal left to middle - graphics.DrawRectangle(pen, destinationRectangle.X + padding, drawAtHeight, halfWidthEnd - 2 * padding - 1, pixelThickness); - // Horizontal middle + 1 to right - graphics.DrawRectangle(pen, destinationRectangle.X + halfWidthEnd + 2 * padding, drawAtHeight, halfWidthEnd - 2 * padding - 1, pixelThickness); - } - attributes?.Dispose(); - } + // Make sure we don't have a pixeloffsetmode/smoothingmode when drawing the crosshair + graphics.SmoothingMode = SmoothingMode.None; + graphics.PixelOffsetMode = PixelOffsetMode.None; - /// - /// Paint the actual visible parts - /// - /// - /// - private void OnPaint(object sender, PaintEventArgs e) { - Graphics graphics = e.Graphics; - Rectangle clipRectangle = e.ClipRectangle; - //graphics.BitBlt((Bitmap)buffer, Point.Empty); - graphics.DrawImageUnscaled(_capture.Image, Point.Empty); + // Calculate some values + int pixelThickness = destinationRectangle.Width / sourceRectangle.Width; + int halfWidth = destinationRectangle.Width / 2; + int halfWidthEnd = destinationRectangle.Width / 2 - pixelThickness / 2; + int halfHeight = destinationRectangle.Height / 2; + int halfHeightEnd = destinationRectangle.Height / 2 - pixelThickness / 2; + + int drawAtHeight = destinationRectangle.Y + halfHeight; + int drawAtWidth = destinationRectangle.X + halfWidth; + int padding = pixelThickness; + + // Pen to draw + using (Pen pen = new Pen(opacyBlack, pixelThickness)) + { + // Draw the cross-hair-lines + // Vertical top to middle + graphics.DrawLine(pen, drawAtWidth, destinationRectangle.Y + padding, drawAtWidth, destinationRectangle.Y + halfHeightEnd - padding); + // Vertical middle + 1 to bottom + graphics.DrawLine(pen, drawAtWidth, destinationRectangle.Y + halfHeightEnd + 2 * padding, drawAtWidth, + destinationRectangle.Y + destinationRectangle.Width - padding); + // Horizontal left to middle + graphics.DrawLine(pen, destinationRectangle.X + padding, drawAtHeight, destinationRectangle.X + halfWidthEnd - padding, drawAtHeight); + // Horizontal middle + 1 to right + graphics.DrawLine(pen, destinationRectangle.X + halfWidthEnd + 2 * padding, drawAtHeight, destinationRectangle.X + destinationRectangle.Width - padding, + drawAtHeight); + + // Fix offset for drawing the white rectangle around the cross-hair-lines + drawAtHeight -= pixelThickness / 2; + drawAtWidth -= pixelThickness / 2; + // Fix off by one error with the DrawRectangle + pixelThickness -= 1; + // Change the color and the pen width + pen.Color = opacyWhite; + pen.Width = 1; + // Vertical top to middle + graphics.DrawRectangle(pen, drawAtWidth, destinationRectangle.Y + padding, pixelThickness, halfHeightEnd - 2 * padding - 1); + // Vertical middle + 1 to bottom + graphics.DrawRectangle(pen, drawAtWidth, destinationRectangle.Y + halfHeightEnd + 2 * padding, pixelThickness, halfHeightEnd - 2 * padding - 1); + // Horizontal left to middle + graphics.DrawRectangle(pen, destinationRectangle.X + padding, drawAtHeight, halfWidthEnd - 2 * padding - 1, pixelThickness); + // Horizontal middle + 1 to right + graphics.DrawRectangle(pen, destinationRectangle.X + halfWidthEnd + 2 * padding, drawAtHeight, halfWidthEnd - 2 * padding - 1, pixelThickness); + } + + attributes?.Dispose(); + } + + /// + /// Paint the actual visible parts + /// + /// + /// + private void OnPaint(object sender, PaintEventArgs e) + { + Graphics graphics = e.Graphics; + Rectangle clipRectangle = e.ClipRectangle; + //graphics.BitBlt((Bitmap)buffer, Point.Empty); + graphics.DrawImageUnscaled(_capture.Image, Point.Empty); var ocrInfo = _capture.CaptureDetails.OcrInformation; if (ocrInfo != null && _captureMode == CaptureMode.Text) @@ -849,7 +980,7 @@ namespace Greenshot.Forms { } } } - } + } else if (lineBounds.Contains(_mouseMovePos)) { foreach (var word in line.Words) @@ -858,52 +989,59 @@ namespace Greenshot.Forms { graphics.FillRectangle(highlightTextBrush, word.Bounds); break; } - } - } + } + } } - } + } // Only draw Cursor if it's (partly) visible - if (_capture.Cursor != null && _capture.CursorVisible && clipRectangle.IntersectsWith(new Rectangle(_capture.CursorLocation, _capture.Cursor.Size))) { - graphics.DrawIcon(_capture.Cursor, _capture.CursorLocation.X, _capture.CursorLocation.Y); - } + if (_capture.Cursor != null && _capture.CursorVisible && clipRectangle.IntersectsWith(new Rectangle(_capture.CursorLocation, _capture.Cursor.Size))) + { + graphics.DrawIcon(_capture.Cursor, _capture.CursorLocation.X, _capture.CursorLocation.Y); + } - if (_mouseDown || _captureMode == CaptureMode.Window || IsAnimating(_windowAnimator)) { - _captureRect.Intersect(new Rectangle(Point.Empty, _capture.ScreenBounds.Size)); // crop what is outside the screen + if (_mouseDown || _captureMode == CaptureMode.Window || IsAnimating(_windowAnimator)) + { + _captureRect.Intersect(new Rectangle(Point.Empty, _capture.ScreenBounds.Size)); // crop what is outside the screen - var fixedRect = IsAnimating(_windowAnimator) ? _windowAnimator.Current : _captureRect; + var fixedRect = IsAnimating(_windowAnimator) ? _windowAnimator.Current : _captureRect; - // TODO: enable when the screen capture code works reliable - //if (capture.CaptureDetails.CaptureMode == CaptureMode.Video) { - // graphics.FillRectangle(RedOverlayBrush, fixedRect); - //} else { - graphics.FillRectangle(GreenOverlayBrush, fixedRect); - //} - graphics.DrawRectangle(OverlayPen, fixedRect); + // TODO: enable when the screen capture code works reliable + //if (capture.CaptureDetails.CaptureMode == CaptureMode.Video) { + // graphics.FillRectangle(RedOverlayBrush, fixedRect); + //} else { + graphics.FillRectangle(GreenOverlayBrush, fixedRect); + //} + graphics.DrawRectangle(OverlayPen, fixedRect); - // rulers - const int dist = 8; + // rulers + const int dist = 8; - string captureWidth; - string captureHeight; - // The following fixes the very old incorrect size information bug - if (_captureMode == CaptureMode.Window) { - captureWidth = _captureRect.Width.ToString(CultureInfo.InvariantCulture); - captureHeight = _captureRect.Height.ToString(CultureInfo.InvariantCulture); - } else { - captureWidth = (_captureRect.Width + 1).ToString(CultureInfo.InvariantCulture); - captureHeight = (_captureRect.Height + 1).ToString(CultureInfo.InvariantCulture); - } - using (Font rulerFont = new Font(FontFamily.GenericSansSerif, 8)) { - Size measureWidth = TextRenderer.MeasureText(captureWidth, rulerFont); - Size measureHeight = TextRenderer.MeasureText(captureHeight, rulerFont); - int hSpace = measureWidth.Width + 3; - int vSpace = measureHeight.Height + 3; - Brush bgBrush = new SolidBrush(Color.FromArgb(200, 217, 240, 227)); - Pen rulerPen = new Pen(Color.SeaGreen); + string captureWidth; + string captureHeight; + // The following fixes the very old incorrect size information bug + if (_captureMode == CaptureMode.Window) + { + captureWidth = _captureRect.Width.ToString(CultureInfo.InvariantCulture); + captureHeight = _captureRect.Height.ToString(CultureInfo.InvariantCulture); + } + else + { + captureWidth = (_captureRect.Width + 1).ToString(CultureInfo.InvariantCulture); + captureHeight = (_captureRect.Height + 1).ToString(CultureInfo.InvariantCulture); + } - // horizontal ruler - if (fixedRect.Width > hSpace + 3) + using (Font rulerFont = new Font(FontFamily.GenericSansSerif, 8)) + { + Size measureWidth = TextRenderer.MeasureText(captureWidth, rulerFont); + Size measureHeight = TextRenderer.MeasureText(captureHeight, rulerFont); + int hSpace = measureWidth.Width + 3; + int vSpace = measureHeight.Height + 3; + Brush bgBrush = new SolidBrush(Color.FromArgb(200, 217, 240, 227)); + Pen rulerPen = new Pen(Color.SeaGreen); + + // horizontal ruler + if (fixedRect.Width > hSpace + 3) { using GraphicsPath p = CreateRoundedRectangle( fixedRect.X + (fixedRect.Width / 2 - hSpace / 2) + 3, @@ -920,8 +1058,8 @@ namespace Greenshot.Forms { graphics.DrawLine(rulerPen, fixedRect.X + fixedRect.Width, fixedRect.Y - dist - 3, fixedRect.X + fixedRect.Width, fixedRect.Y - dist + 3); } - // vertical ruler - if (fixedRect.Height > vSpace + 3) + // vertical ruler + if (fixedRect.Height > vSpace + 3) { using GraphicsPath p = CreateRoundedRectangle( fixedRect.X - measureHeight.Width + 1, @@ -938,34 +1076,40 @@ namespace Greenshot.Forms { graphics.DrawLine(rulerPen, fixedRect.X - dist - 3, fixedRect.Y + fixedRect.Height, fixedRect.X - dist + 3, fixedRect.Y + fixedRect.Height); } - rulerPen.Dispose(); - bgBrush.Dispose(); - } + rulerPen.Dispose(); + bgBrush.Dispose(); + } - // Display size of selected rectangle - // Prepare the font and text. - using Font sizeFont = new Font( FontFamily.GenericSansSerif, 12 ); + // Display size of selected rectangle + // Prepare the font and text. + using Font sizeFont = new Font(FontFamily.GenericSansSerif, 12); // When capturing a Region we need to add 1 to the height/width for correction string sizeText; - if (_captureMode == CaptureMode.Region || _captureMode == CaptureMode.Text) { + if (_captureMode == CaptureMode.Region || _captureMode == CaptureMode.Text) + { // correct the GUI width to real width for the shown size sizeText = _captureRect.Width + 1 + " x " + (_captureRect.Height + 1); - } else { + } + else + { sizeText = _captureRect.Width + " x " + _captureRect.Height; } // Calculate the scaled font size. - SizeF extent = graphics.MeasureString( sizeText, sizeFont ); + SizeF extent = graphics.MeasureString(sizeText, sizeFont); float hRatio = _captureRect.Height / (extent.Height * 2); float wRatio = _captureRect.Width / (extent.Width * 2); float ratio = hRatio < wRatio ? hRatio : wRatio; float newSize = sizeFont.Size * ratio; - if ( newSize >= 4 ) { + if (newSize >= 4) + { // Only show if 4pt or larger. - if (newSize > 20) { + if (newSize > 20) + { newSize = 20; } + // Draw the size. using Font newSizeFont = new Font(FontFamily.GenericSansSerif, newSize, FontStyle.Bold); PointF sizeLocation = new PointF(fixedRect.X + _captureRect.Width / 2 - extent.Width / 2, fixedRect.Y + _captureRect.Height / 2 - newSizeFont.GetHeight() / 2); @@ -973,47 +1117,56 @@ namespace Greenshot.Forms { if (_showDebugInfo && _selectedCaptureWindow != null) { - string title = $"#{_selectedCaptureWindow.Handle.ToInt64():X} - {(_selectedCaptureWindow.Text.Length > 0 ? _selectedCaptureWindow.Text : _selectedCaptureWindow.Process.ProcessName)}"; + string title = + $"#{_selectedCaptureWindow.Handle.ToInt64():X} - {(_selectedCaptureWindow.Text.Length > 0 ? _selectedCaptureWindow.Text : _selectedCaptureWindow.Process.ProcessName)}"; PointF debugLocation = new PointF(fixedRect.X, fixedRect.Y); graphics.DrawString(title, sizeFont, Brushes.DarkOrange, debugLocation); } } - } else { - if (!IsTerminalServerSession) { - using (Pen pen = new Pen(Color.LightSeaGreen)) { - pen.DashStyle = DashStyle.Dot; - Rectangle screenBounds = _capture.ScreenBounds; - graphics.DrawLine(pen, _cursorPos.X, screenBounds.Y, _cursorPos.X, screenBounds.Height); - graphics.DrawLine(pen, screenBounds.X, _cursorPos.Y, screenBounds.Width, _cursorPos.Y); - } + } + else + { + if (!IsTerminalServerSession) + { + using (Pen pen = new Pen(Color.LightSeaGreen)) + { + pen.DashStyle = DashStyle.Dot; + Rectangle screenBounds = _capture.ScreenBounds; + graphics.DrawLine(pen, _cursorPos.X, screenBounds.Y, _cursorPos.X, screenBounds.Height); + graphics.DrawLine(pen, screenBounds.X, _cursorPos.Y, screenBounds.Width, _cursorPos.Y); + } - string xy = _cursorPos.X + " x " + _cursorPos.Y; + string xy = _cursorPos.X + " x " + _cursorPos.Y; using Font f = new Font(FontFamily.GenericSansSerif, 8); Size xySize = TextRenderer.MeasureText(xy, f); using GraphicsPath gp = CreateRoundedRectangle(_cursorPos.X + 5, _cursorPos.Y + 5, xySize.Width - 3, xySize.Height, 3); - using (Brush bgBrush = new SolidBrush(Color.FromArgb(200, 217, 240, 227))) { + using (Brush bgBrush = new SolidBrush(Color.FromArgb(200, 217, 240, 227))) + { graphics.FillPath(bgBrush, gp); } - using (Pen pen = new Pen(Color.SeaGreen)) { + + using (Pen pen = new Pen(Color.SeaGreen)) + { graphics.DrawPath(pen, gp); Point coordinatePosition = new Point(_cursorPos.X + 5, _cursorPos.Y + 5); graphics.DrawString(xy, f, pen.Brush, coordinatePosition); } } - } + } - // Zoom - if (_zoomAnimator != null && (IsAnimating(_zoomAnimator) || _captureMode != CaptureMode.Window)) { - const int zoomSourceWidth = 25; - const int zoomSourceHeight = 25; + // Zoom + if (_zoomAnimator != null && (IsAnimating(_zoomAnimator) || _captureMode != CaptureMode.Window)) + { + const int zoomSourceWidth = 25; + const int zoomSourceHeight = 25; - Rectangle sourceRectangle = new Rectangle(_cursorPos.X - zoomSourceWidth / 2, _cursorPos.Y - zoomSourceHeight / 2, zoomSourceWidth, zoomSourceHeight); + Rectangle sourceRectangle = new Rectangle(_cursorPos.X - zoomSourceWidth / 2, _cursorPos.Y - zoomSourceHeight / 2, zoomSourceWidth, zoomSourceHeight); - Rectangle destinationRectangle = _zoomAnimator.Current; - destinationRectangle.Offset(_cursorPos); - DrawZoom(graphics, sourceRectangle, destinationRectangle); - } - } + Rectangle destinationRectangle = _zoomAnimator.Current; + destinationRectangle.Offset(_cursorPos); + DrawZoom(graphics, sourceRectangle, destinationRectangle); + } + } private static GraphicsPath CreateRoundedRectangle(int x, int y, int width, int height, int radius) { @@ -1031,4 +1184,4 @@ namespace Greenshot.Forms { return gp; } } -} +} \ No newline at end of file diff --git a/src/Greenshot/Forms/ColorDialog.cs b/src/Greenshot/Forms/ColorDialog.cs index b93c01258..91c314cd9 100644 --- a/src/Greenshot/Forms/ColorDialog.cs +++ b/src/Greenshot/Forms/ColorDialog.cs @@ -29,204 +29,252 @@ using Greenshot.Configuration; using Greenshot.Controls; using GreenshotPlugin.IniFile; -namespace Greenshot.Forms { - /// - /// Description of ColorDialog. - /// - public partial class ColorDialog : BaseForm { - private static readonly EditorConfiguration EditorConfig = IniConfig.GetIniSection(); +namespace Greenshot.Forms +{ + /// + /// Description of ColorDialog. + /// + public partial class ColorDialog : BaseForm + { + private static readonly EditorConfiguration EditorConfig = IniConfig.GetIniSection(); private static ColorDialog _instance; - public ColorDialog() { - SuspendLayout(); - InitializeComponent(); - SuspendLayout(); - CreateColorPalette(5, 5, 15, 15); - CreateLastUsedColorButtonRow(5, 190, 15, 15); - ResumeLayout(); - UpdateRecentColorsButtonRow(); + + public ColorDialog() + { + SuspendLayout(); + InitializeComponent(); + SuspendLayout(); + CreateColorPalette(5, 5, 15, 15); + CreateLastUsedColorButtonRow(5, 190, 15, 15); + ResumeLayout(); + UpdateRecentColorsButtonRow(); _instance = this; } public static ColorDialog GetInstance() => _instance; - private readonly List