diff --git a/installer/innosetup/setup.iss b/installer/innosetup/setup.iss
index 977f4cf8b..f8bd75b1d 100644
--- a/installer/innosetup/setup.iss
+++ b/installer/innosetup/setup.iss
@@ -20,7 +20,7 @@
[Files]
Source: {#ReleaseDir}\Greenshot.exe; DestDir: {app}; Components: greenshot; Flags: overwritereadonly ignoreversion replacesameversion
-Source: {#ReleaseDir}\GreenshotPlugin.dll; DestDir: {app}; Components: greenshot; Flags: overwritereadonly ignoreversion replacesameversion
+Source: {#ReleaseDir}\Greenshot.Base.dll; DestDir: {app}; Components: greenshot; Flags: overwritereadonly ignoreversion replacesameversion
Source: {#ReleaseDir}\Greenshot.exe.config; DestDir: {app}; Components: greenshot; Flags: overwritereadonly ignoreversion replacesameversion
Source: {#ReleaseDir}\log4net.dll; DestDir: {app}; Components: greenshot; Flags: overwritereadonly ignoreversion replacesameversion
Source: {#ReleaseDir}\Dapplo.Http*.dll; DestDir: {app}; Components: greenshot; Flags: overwritereadonly ignoreversion replacesameversion
diff --git a/src/GreenshotPlugin/Controls/AnimatingForm.cs b/src/Greenshot.Base/Controls/AnimatingForm.cs
similarity index 94%
rename from src/GreenshotPlugin/Controls/AnimatingForm.cs
rename to src/Greenshot.Base/Controls/AnimatingForm.cs
index 2bfb06a52..c5a5e3e7e 100644
--- a/src/GreenshotPlugin/Controls/AnimatingForm.cs
+++ b/src/Greenshot.Base/Controls/AnimatingForm.cs
@@ -1,140 +1,140 @@
-/*
- * Greenshot - a free and open source screenshot tool
- * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
- *
- * For more information see: http://getgreenshot.org/
- * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 1 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-using System;
-using System.Windows.Forms;
-using GreenshotPlugin.UnmanagedHelpers;
-using GreenshotPlugin.UnmanagedHelpers.Enums;
-using log4net;
-
-namespace GreenshotPlugin.Controls
-{
- ///
- /// Extend this Form to have the possibility for animations on your form
- ///
- public class AnimatingForm : GreenshotForm
- {
- private static readonly ILog Log = LogManager.GetLogger(typeof(AnimatingForm));
- private const int DEFAULT_VREFRESH = 60;
- private int _vRefresh;
- private Timer _timer;
-
- ///
- /// This flag specifies if any animation is used
- ///
- protected bool EnableAnimation { get; set; }
-
- ///
- /// Vertical Refresh Rate
- ///
- protected int VRefresh
- {
- get
- {
- if (_vRefresh == 0)
- {
- // get te hDC of the desktop to get the VREFRESH
- using SafeWindowDcHandle desktopHandle = SafeWindowDcHandle.FromDesktop();
- _vRefresh = GDI32.GetDeviceCaps(desktopHandle, DeviceCaps.VREFRESH);
- }
-
- // A vertical refresh rate value of 0 or 1 represents the display hardware's default refresh rate.
- // As there is currently no know way to get the default, we guess it.
- if (_vRefresh <= 1)
- {
- _vRefresh = DEFAULT_VREFRESH;
- }
-
- return _vRefresh;
- }
- }
-
- ///
- /// Check if we are in a Terminal Server session OR need to optimize for RDP / remote desktop connections
- ///
- protected bool IsTerminalServerSession => !coreConfiguration.DisableRDPOptimizing && (coreConfiguration.OptimizeForRDP || SystemInformation.TerminalServerSession);
-
- ///
- /// Calculate the amount of frames that an animation takes
- ///
- ///
- /// Number of frames, 1 if in Terminal Server Session
- protected int FramesForMillis(int milliseconds)
- {
- // If we are in a Terminal Server Session we return 1
- if (IsTerminalServerSession)
- {
- return 1;
- }
-
- return milliseconds / VRefresh;
- }
-
- ///
- /// Initialize the animation
- ///
- protected AnimatingForm()
- {
- Load += delegate
- {
- if (!EnableAnimation)
- {
- return;
- }
-
- _timer = new Timer
- {
- Interval = 1000 / VRefresh
- };
- _timer.Tick += timer_Tick;
- _timer.Start();
- };
-
- // Unregister at close
- FormClosing += delegate { _timer?.Stop(); };
- }
-
- ///
- /// The tick handler initiates the animation.
- ///
- ///
- ///
- private void timer_Tick(object sender, EventArgs e)
- {
- try
- {
- Animate();
- }
- catch (Exception ex)
- {
- Log.Warn("An exception occured while animating:", ex);
- }
- }
-
- ///
- /// This method will be called every frame, so implement your animation/redraw logic here.
- ///
- protected virtual void Animate()
- {
- throw new NotImplementedException();
- }
- }
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+using System;
+using System.Windows.Forms;
+using Greenshot.Base.UnmanagedHelpers;
+using Greenshot.Base.UnmanagedHelpers.Enums;
+using log4net;
+
+namespace Greenshot.Base.Controls
+{
+ ///
+ /// Extend this Form to have the possibility for animations on your form
+ ///
+ public class AnimatingForm : GreenshotForm
+ {
+ private static readonly ILog Log = LogManager.GetLogger(typeof(AnimatingForm));
+ private const int DEFAULT_VREFRESH = 60;
+ private int _vRefresh;
+ private Timer _timer;
+
+ ///
+ /// This flag specifies if any animation is used
+ ///
+ protected bool EnableAnimation { get; set; }
+
+ ///
+ /// Vertical Refresh Rate
+ ///
+ protected int VRefresh
+ {
+ get
+ {
+ if (_vRefresh == 0)
+ {
+ // get te hDC of the desktop to get the VREFRESH
+ using SafeWindowDcHandle desktopHandle = SafeWindowDcHandle.FromDesktop();
+ _vRefresh = GDI32.GetDeviceCaps(desktopHandle, DeviceCaps.VREFRESH);
+ }
+
+ // A vertical refresh rate value of 0 or 1 represents the display hardware's default refresh rate.
+ // As there is currently no know way to get the default, we guess it.
+ if (_vRefresh <= 1)
+ {
+ _vRefresh = DEFAULT_VREFRESH;
+ }
+
+ return _vRefresh;
+ }
+ }
+
+ ///
+ /// Check if we are in a Terminal Server session OR need to optimize for RDP / remote desktop connections
+ ///
+ protected bool IsTerminalServerSession => !coreConfiguration.DisableRDPOptimizing && (coreConfiguration.OptimizeForRDP || SystemInformation.TerminalServerSession);
+
+ ///
+ /// Calculate the amount of frames that an animation takes
+ ///
+ ///
+ /// Number of frames, 1 if in Terminal Server Session
+ protected int FramesForMillis(int milliseconds)
+ {
+ // If we are in a Terminal Server Session we return 1
+ if (IsTerminalServerSession)
+ {
+ return 1;
+ }
+
+ return milliseconds / VRefresh;
+ }
+
+ ///
+ /// Initialize the animation
+ ///
+ protected AnimatingForm()
+ {
+ Load += delegate
+ {
+ if (!EnableAnimation)
+ {
+ return;
+ }
+
+ _timer = new Timer
+ {
+ Interval = 1000 / VRefresh
+ };
+ _timer.Tick += timer_Tick;
+ _timer.Start();
+ };
+
+ // Unregister at close
+ FormClosing += delegate { _timer?.Stop(); };
+ }
+
+ ///
+ /// The tick handler initiates the animation.
+ ///
+ ///
+ ///
+ private void timer_Tick(object sender, EventArgs e)
+ {
+ try
+ {
+ Animate();
+ }
+ catch (Exception ex)
+ {
+ Log.Warn("An exception occured while animating:", ex);
+ }
+ }
+
+ ///
+ /// This method will be called every frame, so implement your animation/redraw logic here.
+ ///
+ protected virtual void Animate()
+ {
+ throw new NotImplementedException();
+ }
+ }
}
\ No newline at end of file
diff --git a/src/GreenshotPlugin/Controls/BackgroundForm.Designer.cs b/src/Greenshot.Base/Controls/BackgroundForm.Designer.cs
similarity index 96%
rename from src/GreenshotPlugin/Controls/BackgroundForm.Designer.cs
rename to src/Greenshot.Base/Controls/BackgroundForm.Designer.cs
index cafbb6e18..1bbde20ad 100644
--- a/src/GreenshotPlugin/Controls/BackgroundForm.Designer.cs
+++ b/src/Greenshot.Base/Controls/BackgroundForm.Designer.cs
@@ -1,96 +1,96 @@
-/*
- * Greenshot - a free and open source screenshot tool
- * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
- *
- * For more information see: http://getgreenshot.org/
- * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 1 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-namespace GreenshotPlugin.Controls
-{
- partial class BackgroundForm
- {
- ///
- /// Designer variable used to keep track of non-visual components.
- ///
- private System.ComponentModel.IContainer components = null;
-
- ///
- /// Disposes resources used by the form.
- ///
- /// true if managed resources should be disposed; otherwise, false.
- protected override void Dispose(bool disposing)
- {
- if (disposing) {
- if (components != null) {
- components.Dispose();
- }
- }
- base.Dispose(disposing);
- }
-
- ///
- /// This method is required for Windows Forms designer support.
- /// Do not change the method contents inside the source code editor. The Forms designer might
- /// not be able to load this method if it was changed manually.
- ///
- private void InitializeComponent()
- {
- this.components = new System.ComponentModel.Container();
- this.label_pleasewait = new System.Windows.Forms.Label();
- this.timer_checkforclose = new System.Windows.Forms.Timer(this.components);
- this.SuspendLayout();
- //
- // label_pleasewait
- //
- this.label_pleasewait.Dock = System.Windows.Forms.DockStyle.Fill;
- this.label_pleasewait.Location = new System.Drawing.Point(0, 0);
- this.label_pleasewait.Name = "label_pleasewait";
- this.label_pleasewait.Padding = new System.Windows.Forms.Padding(10);
- this.label_pleasewait.Size = new System.Drawing.Size(90, 33);
- this.label_pleasewait.TabIndex = 0;
- this.label_pleasewait.Text = "Please wait...";
- this.label_pleasewait.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
- this.label_pleasewait.UseWaitCursor = true;
- //
- // timer_checkforclose
- //
- this.timer_checkforclose.Interval = 200;
- this.timer_checkforclose.Tick += new System.EventHandler(this.Timer_checkforcloseTick);
- //
- // BackgroundForm
- //
- this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F);
- this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi;
- this.ClientSize = new System.Drawing.Size(169, 52);
- this.ControlBox = true;
- this.Controls.Add(this.label_pleasewait);
- this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
- this.MaximizeBox = false;
- this.MinimizeBox = false;
- this.Name = "BackgroundForm";
- this.SizeGripStyle = System.Windows.Forms.SizeGripStyle.Hide;
- this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
- this.Text = "Greenshot";
- this.TopMost = true;
- this.UseWaitCursor = true;
- this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.BackgroundFormFormClosing);
- this.ResumeLayout(false);
- this.PerformLayout();
- }
- private System.Windows.Forms.Timer timer_checkforclose;
- private System.Windows.Forms.Label label_pleasewait;
- }
-}
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+namespace Greenshot.Base.Controls
+{
+ partial class BackgroundForm
+ {
+ ///
+ /// Designer variable used to keep track of non-visual components.
+ ///
+ private System.ComponentModel.IContainer components = null;
+
+ ///
+ /// Disposes resources used by the form.
+ ///
+ /// true if managed resources should be disposed; otherwise, false.
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing) {
+ if (components != null) {
+ components.Dispose();
+ }
+ }
+ base.Dispose(disposing);
+ }
+
+ ///
+ /// This method is required for Windows Forms designer support.
+ /// Do not change the method contents inside the source code editor. The Forms designer might
+ /// not be able to load this method if it was changed manually.
+ ///
+ private void InitializeComponent()
+ {
+ this.components = new System.ComponentModel.Container();
+ this.label_pleasewait = new System.Windows.Forms.Label();
+ this.timer_checkforclose = new System.Windows.Forms.Timer(this.components);
+ this.SuspendLayout();
+ //
+ // label_pleasewait
+ //
+ this.label_pleasewait.Dock = System.Windows.Forms.DockStyle.Fill;
+ this.label_pleasewait.Location = new System.Drawing.Point(0, 0);
+ this.label_pleasewait.Name = "label_pleasewait";
+ this.label_pleasewait.Padding = new System.Windows.Forms.Padding(10);
+ this.label_pleasewait.Size = new System.Drawing.Size(90, 33);
+ this.label_pleasewait.TabIndex = 0;
+ this.label_pleasewait.Text = "Please wait...";
+ this.label_pleasewait.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
+ this.label_pleasewait.UseWaitCursor = true;
+ //
+ // timer_checkforclose
+ //
+ this.timer_checkforclose.Interval = 200;
+ this.timer_checkforclose.Tick += new System.EventHandler(this.Timer_checkforcloseTick);
+ //
+ // BackgroundForm
+ //
+ this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi;
+ this.ClientSize = new System.Drawing.Size(169, 52);
+ this.ControlBox = true;
+ this.Controls.Add(this.label_pleasewait);
+ this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
+ this.MaximizeBox = false;
+ this.MinimizeBox = false;
+ this.Name = "BackgroundForm";
+ this.SizeGripStyle = System.Windows.Forms.SizeGripStyle.Hide;
+ this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
+ this.Text = "Greenshot";
+ this.TopMost = true;
+ this.UseWaitCursor = true;
+ this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.BackgroundFormFormClosing);
+ this.ResumeLayout(false);
+ this.PerformLayout();
+ }
+ private System.Windows.Forms.Timer timer_checkforclose;
+ private System.Windows.Forms.Label label_pleasewait;
+ }
+}
diff --git a/src/GreenshotPlugin/Controls/BackgroundForm.cs b/src/Greenshot.Base/Controls/BackgroundForm.cs
similarity index 95%
rename from src/GreenshotPlugin/Controls/BackgroundForm.cs
rename to src/Greenshot.Base/Controls/BackgroundForm.cs
index beda8b73b..45691c8e3 100644
--- a/src/GreenshotPlugin/Controls/BackgroundForm.cs
+++ b/src/Greenshot.Base/Controls/BackgroundForm.cs
@@ -1,99 +1,99 @@
-/*
- * Greenshot - a free and open source screenshot tool
- * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
- *
- * For more information see: http://getgreenshot.org/
- * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 1 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-using System;
-using System.Drawing;
-using System.Windows.Forms;
-using GreenshotPlugin.Core;
-
-namespace GreenshotPlugin.Controls
-{
- ///
- /// Description of PleaseWaitForm.
- ///
- public sealed partial class BackgroundForm : Form
- {
- private volatile bool _shouldClose;
-
- public BackgroundForm(string title, string text)
- {
- //
- // The InitializeComponent() call is required for Windows Forms designer support.
- //
- InitializeComponent();
- Icon = GreenshotResources.GetGreenshotIcon();
- _shouldClose = false;
- Text = title;
- label_pleasewait.Text = text;
- FormClosing += PreventFormClose;
- timer_checkforclose.Start();
- }
-
- // Can be used instead of ShowDialog
- public new void Show()
- {
- base.Show();
- bool positioned = false;
- foreach (Screen screen in Screen.AllScreens)
- {
- if (screen.Bounds.Contains(Cursor.Position))
- {
- positioned = true;
- Location = new Point(screen.Bounds.X + (screen.Bounds.Width / 2) - (Width / 2), screen.Bounds.Y + (screen.Bounds.Height / 2) - (Height / 2));
- break;
- }
- }
-
- if (!positioned)
- {
- Location = new Point(Cursor.Position.X - Width / 2, Cursor.Position.Y - Height / 2);
- }
- }
-
- private void PreventFormClose(object sender, FormClosingEventArgs e)
- {
- if (!_shouldClose)
- {
- e.Cancel = true;
- }
- }
-
- private void Timer_checkforcloseTick(object sender, EventArgs e)
- {
- if (_shouldClose)
- {
- timer_checkforclose.Stop();
- BeginInvoke(new EventHandler(delegate { Close(); }));
- }
- }
-
- public void CloseDialog()
- {
- _shouldClose = true;
- Application.DoEvents();
- }
-
- private void BackgroundFormFormClosing(object sender, FormClosingEventArgs e)
- {
- timer_checkforclose.Stop();
- }
- }
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+using System;
+using System.Drawing;
+using System.Windows.Forms;
+using Greenshot.Base.Core;
+
+namespace Greenshot.Base.Controls
+{
+ ///
+ /// Description of PleaseWaitForm.
+ ///
+ public sealed partial class BackgroundForm : Form
+ {
+ private volatile bool _shouldClose;
+
+ public BackgroundForm(string title, string text)
+ {
+ //
+ // The InitializeComponent() call is required for Windows Forms designer support.
+ //
+ InitializeComponent();
+ Icon = GreenshotResources.GetGreenshotIcon();
+ _shouldClose = false;
+ Text = title;
+ label_pleasewait.Text = text;
+ FormClosing += PreventFormClose;
+ timer_checkforclose.Start();
+ }
+
+ // Can be used instead of ShowDialog
+ public new void Show()
+ {
+ base.Show();
+ bool positioned = false;
+ foreach (Screen screen in Screen.AllScreens)
+ {
+ if (screen.Bounds.Contains(Cursor.Position))
+ {
+ positioned = true;
+ Location = new Point(screen.Bounds.X + (screen.Bounds.Width / 2) - (Width / 2), screen.Bounds.Y + (screen.Bounds.Height / 2) - (Height / 2));
+ break;
+ }
+ }
+
+ if (!positioned)
+ {
+ Location = new Point(Cursor.Position.X - Width / 2, Cursor.Position.Y - Height / 2);
+ }
+ }
+
+ private void PreventFormClose(object sender, FormClosingEventArgs e)
+ {
+ if (!_shouldClose)
+ {
+ e.Cancel = true;
+ }
+ }
+
+ private void Timer_checkforcloseTick(object sender, EventArgs e)
+ {
+ if (_shouldClose)
+ {
+ timer_checkforclose.Stop();
+ BeginInvoke(new EventHandler(delegate { Close(); }));
+ }
+ }
+
+ public void CloseDialog()
+ {
+ _shouldClose = true;
+ Application.DoEvents();
+ }
+
+ private void BackgroundFormFormClosing(object sender, FormClosingEventArgs e)
+ {
+ timer_checkforclose.Stop();
+ }
+ }
}
\ No newline at end of file
diff --git a/src/GreenshotPlugin/Controls/ExtendedWebBrowser.cs b/src/Greenshot.Base/Controls/ExtendedWebBrowser.cs
similarity index 94%
rename from src/GreenshotPlugin/Controls/ExtendedWebBrowser.cs
rename to src/Greenshot.Base/Controls/ExtendedWebBrowser.cs
index e5bc1c622..0e4d439ae 100644
--- a/src/GreenshotPlugin/Controls/ExtendedWebBrowser.cs
+++ b/src/Greenshot.Base/Controls/ExtendedWebBrowser.cs
@@ -1,68 +1,68 @@
-/*
- * Greenshot - a free and open source screenshot tool
- * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
- *
- * For more information see: http://getgreenshot.org/
- * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 1 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-using System;
-using System.Windows.Forms;
-using GreenshotPlugin.Interop;
-
-namespace GreenshotPlugin.Controls
-{
- public class ExtendedWebBrowser : WebBrowser
- {
- protected class ExtendedWebBrowserSite : WebBrowserSite, IOleCommandTarget
- {
- private const int OLECMDID_SHOWSCRIPTERROR = 40;
-
- private static readonly Guid CGID_DocHostCommandHandler = new Guid("F38BC242-B950-11D1-8918-00C04FC2C836");
-
- private const int S_OK = 0;
- private const int OLECMDERR_E_NOTSUPPORTED = (-2147221248);
-
- public ExtendedWebBrowserSite(WebBrowser wb) : base(wb)
- {
- }
-
- public int QueryStatus(Guid pguidCmdGroup, int cCmds, IntPtr prgCmds, IntPtr pCmdText)
- {
- return OLECMDERR_E_NOTSUPPORTED;
- }
-
- public int Exec(Guid pguidCmdGroup, int nCmdID, int nCmdexecopt, IntPtr pvaIn, IntPtr pvaOut)
- {
- if (pguidCmdGroup == CGID_DocHostCommandHandler)
- {
- if (nCmdID == OLECMDID_SHOWSCRIPTERROR)
- {
- // do not need to alter pvaOut as the docs says, enough to return S_OK here
- return S_OK;
- }
- }
-
- return OLECMDERR_E_NOTSUPPORTED;
- }
- }
-
- protected override WebBrowserSiteBase CreateWebBrowserSiteBase()
- {
- return new ExtendedWebBrowserSite(this);
- }
- }
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+using System;
+using System.Windows.Forms;
+using Greenshot.Base.Interop;
+
+namespace Greenshot.Base.Controls
+{
+ public class ExtendedWebBrowser : WebBrowser
+ {
+ protected class ExtendedWebBrowserSite : WebBrowserSite, IOleCommandTarget
+ {
+ private const int OLECMDID_SHOWSCRIPTERROR = 40;
+
+ private static readonly Guid CGID_DocHostCommandHandler = new Guid("F38BC242-B950-11D1-8918-00C04FC2C836");
+
+ private const int S_OK = 0;
+ private const int OLECMDERR_E_NOTSUPPORTED = (-2147221248);
+
+ public ExtendedWebBrowserSite(WebBrowser wb) : base(wb)
+ {
+ }
+
+ public int QueryStatus(Guid pguidCmdGroup, int cCmds, IntPtr prgCmds, IntPtr pCmdText)
+ {
+ return OLECMDERR_E_NOTSUPPORTED;
+ }
+
+ public int Exec(Guid pguidCmdGroup, int nCmdID, int nCmdexecopt, IntPtr pvaIn, IntPtr pvaOut)
+ {
+ if (pguidCmdGroup == CGID_DocHostCommandHandler)
+ {
+ if (nCmdID == OLECMDID_SHOWSCRIPTERROR)
+ {
+ // do not need to alter pvaOut as the docs says, enough to return S_OK here
+ return S_OK;
+ }
+ }
+
+ return OLECMDERR_E_NOTSUPPORTED;
+ }
+ }
+
+ protected override WebBrowserSiteBase CreateWebBrowserSiteBase()
+ {
+ return new ExtendedWebBrowserSite(this);
+ }
+ }
}
\ No newline at end of file
diff --git a/src/GreenshotPlugin/Controls/FormWithoutActivation.cs b/src/Greenshot.Base/Controls/FormWithoutActivation.cs
similarity index 94%
rename from src/GreenshotPlugin/Controls/FormWithoutActivation.cs
rename to src/Greenshot.Base/Controls/FormWithoutActivation.cs
index 06cde98a2..f9fee5ef5 100644
--- a/src/GreenshotPlugin/Controls/FormWithoutActivation.cs
+++ b/src/Greenshot.Base/Controls/FormWithoutActivation.cs
@@ -1,36 +1,36 @@
-/*
- * Greenshot - a free and open source screenshot tool
- * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
- *
- * For more information see: http://getgreenshot.org/
- * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 1 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-using System.Windows.Forms;
-
-namespace GreenshotPlugin.Controls
-{
- ///
- /// FormWithoutActivation is exactly like a normal form, but doesn't activate (steal focus)
- ///
- public class FormWithoutActivation : Form
- {
- protected override bool ShowWithoutActivation
- {
- get { return true; }
- }
- }
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+using System.Windows.Forms;
+
+namespace Greenshot.Base.Controls
+{
+ ///
+ /// FormWithoutActivation is exactly like a normal form, but doesn't activate (steal focus)
+ ///
+ public class FormWithoutActivation : Form
+ {
+ protected override bool ShowWithoutActivation
+ {
+ get { return true; }
+ }
+ }
}
\ No newline at end of file
diff --git a/src/GreenshotPlugin/Controls/GreenshotButton.cs b/src/Greenshot.Base/Controls/GreenshotButton.cs
similarity index 94%
rename from src/GreenshotPlugin/Controls/GreenshotButton.cs
rename to src/Greenshot.Base/Controls/GreenshotButton.cs
index 9849f2c50..703be11e5 100644
--- a/src/GreenshotPlugin/Controls/GreenshotButton.cs
+++ b/src/Greenshot.Base/Controls/GreenshotButton.cs
@@ -1,32 +1,32 @@
-/*
- * Greenshot - a free and open source screenshot tool
- * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
- *
- * For more information see: http://getgreenshot.org/
- * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 1 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-using System.ComponentModel;
-using System.Windows.Forms;
-
-namespace GreenshotPlugin.Controls
-{
- public class GreenshotButton : Button, IGreenshotLanguageBindable
- {
- [Category("Greenshot"), DefaultValue(null), Description("Specifies key of the language file to use when displaying the text.")]
- public string LanguageKey { get; set; }
- }
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+using System.ComponentModel;
+using System.Windows.Forms;
+
+namespace Greenshot.Base.Controls
+{
+ public class GreenshotButton : Button, IGreenshotLanguageBindable
+ {
+ [Category("Greenshot"), DefaultValue(null), Description("Specifies key of the language file to use when displaying the text.")]
+ public string LanguageKey { get; set; }
+ }
}
\ No newline at end of file
diff --git a/src/GreenshotPlugin/Controls/GreenshotCheckBox.cs b/src/Greenshot.Base/Controls/GreenshotCheckBox.cs
similarity index 95%
rename from src/GreenshotPlugin/Controls/GreenshotCheckBox.cs
rename to src/Greenshot.Base/Controls/GreenshotCheckBox.cs
index f0e4563b9..c7552fb74 100644
--- a/src/GreenshotPlugin/Controls/GreenshotCheckBox.cs
+++ b/src/Greenshot.Base/Controls/GreenshotCheckBox.cs
@@ -1,41 +1,41 @@
-/*
- * Greenshot - a free and open source screenshot tool
- * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
- *
- * For more information see: http://getgreenshot.org/
- * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 1 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-using System.ComponentModel;
-using System.Windows.Forms;
-
-namespace GreenshotPlugin.Controls
-{
- ///
- /// Description of GreenshotCheckbox.
- ///
- public class GreenshotCheckBox : CheckBox, IGreenshotLanguageBindable, IGreenshotConfigBindable
- {
- [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("Core"), Description("Specifies the Ini-Section to map this control with.")]
- public string SectionName { get; set; } = "Core";
-
- [Category("Greenshot"), DefaultValue(null), Description("Specifies the property name to map the configuration.")]
- public string PropertyName { get; set; }
- }
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+using System.ComponentModel;
+using System.Windows.Forms;
+
+namespace Greenshot.Base.Controls
+{
+ ///
+ /// Description of GreenshotCheckbox.
+ ///
+ public class GreenshotCheckBox : CheckBox, IGreenshotLanguageBindable, IGreenshotConfigBindable
+ {
+ [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("Core"), Description("Specifies the Ini-Section to map this control with.")]
+ public string SectionName { get; set; } = "Core";
+
+ [Category("Greenshot"), DefaultValue(null), Description("Specifies the property name to map the configuration.")]
+ public string PropertyName { get; set; }
+ }
}
\ No newline at end of file
diff --git a/src/GreenshotPlugin/Controls/GreenshotColumnSorter.cs b/src/Greenshot.Base/Controls/GreenshotColumnSorter.cs
similarity index 99%
rename from src/GreenshotPlugin/Controls/GreenshotColumnSorter.cs
rename to src/Greenshot.Base/Controls/GreenshotColumnSorter.cs
index 02c8f16ef..f33379cda 100644
--- a/src/GreenshotPlugin/Controls/GreenshotColumnSorter.cs
+++ b/src/Greenshot.Base/Controls/GreenshotColumnSorter.cs
@@ -22,7 +22,7 @@
using System.Collections;
using System.Windows.Forms;
-namespace GreenshotPlugin.Controls
+namespace Greenshot.Base.Controls
{
///
/// This class is an implementation of the 'IComparer' interface.
diff --git a/src/GreenshotPlugin/Controls/GreenshotComboBox.cs b/src/Greenshot.Base/Controls/GreenshotComboBox.cs
similarity index 95%
rename from src/GreenshotPlugin/Controls/GreenshotComboBox.cs
rename to src/Greenshot.Base/Controls/GreenshotComboBox.cs
index 5fc0437b6..4c464544c 100644
--- a/src/GreenshotPlugin/Controls/GreenshotComboBox.cs
+++ b/src/Greenshot.Base/Controls/GreenshotComboBox.cs
@@ -1,116 +1,116 @@
-/*
- * Greenshot - a free and open source screenshot tool
- * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
- *
- * For more information see: http://getgreenshot.org/
- * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 1 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-using System;
-using System.ComponentModel;
-using System.Windows.Forms;
-using GreenshotPlugin.Core;
-
-namespace GreenshotPlugin.Controls
-{
- public class GreenshotComboBox : ComboBox, IGreenshotConfigBindable
- {
- private Type _enumType;
- private Enum _selectedEnum;
-
- [Category("Greenshot"), DefaultValue("Core"), Description("Specifies the Ini-Section to map this control with.")]
- public string SectionName { get; set; } = "Core";
-
- [Category("Greenshot"), DefaultValue(null), Description("Specifies the property name to map the configuration.")]
- public string PropertyName { get; set; }
-
- public GreenshotComboBox()
- {
- SelectedIndexChanged += delegate { StoreSelectedEnum(); };
- }
-
- public void SetValue(Enum currentValue)
- {
- if (currentValue != null)
- {
- _selectedEnum = currentValue;
- SelectedItem = Language.Translate(currentValue);
- }
- }
-
- ///
- /// This is a method to popululate the ComboBox
- /// with the items from the enumeration
- ///
- /// TEnum to populate with
- public void Populate(Type enumType)
- {
- // Store the enum-type, so we can work with it
- _enumType = enumType;
-
- var availableValues = Enum.GetValues(enumType);
- Items.Clear();
- foreach (var enumValue in availableValues)
- {
- Items.Add(Language.Translate((Enum) enumValue));
- }
- }
-
- ///
- /// Store the selected value internally
- ///
- private void StoreSelectedEnum()
- {
- string enumTypeName = _enumType.Name;
- string selectedValue = SelectedItem as string;
- var availableValues = Enum.GetValues(_enumType);
- object returnValue = null;
-
- try
- {
- returnValue = Enum.Parse(_enumType, selectedValue);
- }
- catch (Exception)
- {
- // Ignore
- }
-
- foreach (Enum enumValue in availableValues)
- {
- string enumKey = enumTypeName + "." + enumValue;
- if (Language.HasKey(enumKey))
- {
- string translation = Language.GetString(enumTypeName + "." + enumValue);
- if (translation.Equals(selectedValue))
- {
- returnValue = enumValue;
- }
- }
- }
-
- _selectedEnum = (Enum) returnValue;
- }
-
- ///
- /// Get the selected enum value from the combobox, uses generics
- ///
- /// The enum value of the combobox
- public Enum GetSelectedEnum()
- {
- return _selectedEnum;
- }
- }
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+using System;
+using System.ComponentModel;
+using System.Windows.Forms;
+using Greenshot.Base.Core;
+
+namespace Greenshot.Base.Controls
+{
+ public class GreenshotComboBox : ComboBox, IGreenshotConfigBindable
+ {
+ private Type _enumType;
+ private Enum _selectedEnum;
+
+ [Category("Greenshot"), DefaultValue("Core"), Description("Specifies the Ini-Section to map this control with.")]
+ public string SectionName { get; set; } = "Core";
+
+ [Category("Greenshot"), DefaultValue(null), Description("Specifies the property name to map the configuration.")]
+ public string PropertyName { get; set; }
+
+ public GreenshotComboBox()
+ {
+ SelectedIndexChanged += delegate { StoreSelectedEnum(); };
+ }
+
+ public void SetValue(Enum currentValue)
+ {
+ if (currentValue != null)
+ {
+ _selectedEnum = currentValue;
+ SelectedItem = Language.Translate(currentValue);
+ }
+ }
+
+ ///
+ /// This is a method to popululate the ComboBox
+ /// with the items from the enumeration
+ ///
+ /// TEnum to populate with
+ public void Populate(Type enumType)
+ {
+ // Store the enum-type, so we can work with it
+ _enumType = enumType;
+
+ var availableValues = Enum.GetValues(enumType);
+ Items.Clear();
+ foreach (var enumValue in availableValues)
+ {
+ Items.Add(Language.Translate((Enum) enumValue));
+ }
+ }
+
+ ///
+ /// Store the selected value internally
+ ///
+ private void StoreSelectedEnum()
+ {
+ string enumTypeName = _enumType.Name;
+ string selectedValue = SelectedItem as string;
+ var availableValues = Enum.GetValues(_enumType);
+ object returnValue = null;
+
+ try
+ {
+ returnValue = Enum.Parse(_enumType, selectedValue);
+ }
+ catch (Exception)
+ {
+ // Ignore
+ }
+
+ foreach (Enum enumValue in availableValues)
+ {
+ string enumKey = enumTypeName + "." + enumValue;
+ if (Language.HasKey(enumKey))
+ {
+ string translation = Language.GetString(enumTypeName + "." + enumValue);
+ if (translation.Equals(selectedValue))
+ {
+ returnValue = enumValue;
+ }
+ }
+ }
+
+ _selectedEnum = (Enum) returnValue;
+ }
+
+ ///
+ /// Get the selected enum value from the combobox, uses generics
+ ///
+ /// The enum value of the combobox
+ public Enum GetSelectedEnum()
+ {
+ return _selectedEnum;
+ }
+ }
}
\ No newline at end of file
diff --git a/src/GreenshotPlugin/Controls/GreenshotForm.cs b/src/Greenshot.Base/Controls/GreenshotForm.cs
similarity index 96%
rename from src/GreenshotPlugin/Controls/GreenshotForm.cs
rename to src/Greenshot.Base/Controls/GreenshotForm.cs
index c91a48ddc..9850e71ed 100644
--- a/src/GreenshotPlugin/Controls/GreenshotForm.cs
+++ b/src/Greenshot.Base/Controls/GreenshotForm.cs
@@ -1,643 +1,643 @@
-/*
- * Greenshot - a free and open source screenshot tool
- * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
- *
- * For more information see: http://getgreenshot.org/
- * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 1 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-using System;
-using System.Collections.Generic;
-using System.Windows.Forms;
-using System.Reflection;
-using GreenshotPlugin.Core;
-using System.ComponentModel;
-using System.ComponentModel.Design;
-using System.IO;
-using GreenshotPlugin.IniFile;
-using log4net;
-
-namespace GreenshotPlugin.Controls
-{
- ///
- /// This form is used for automatically binding the elements of the form to the language
- ///
- public class GreenshotForm : Form, IGreenshotLanguageBindable
- {
- private static readonly ILog LOG = LogManager.GetLogger(typeof(GreenshotForm));
- protected static CoreConfiguration coreConfiguration;
- private static readonly IDictionary reflectionCache = new Dictionary();
- private IComponentChangeService m_changeService;
- private bool _isDesignModeLanguageSet;
- private bool _applyLanguageManually;
- private bool _storeFieldsManually;
- private IDictionary _designTimeControls;
- private IDictionary _designTimeToolStripItems;
-
- static GreenshotForm()
- {
- if (!IsInDesignMode)
- {
- coreConfiguration = IniConfig.GetIniSection();
- }
- }
-
- [Category("Greenshot"), DefaultValue(null), Description("Specifies key of the language file to use when displaying the text.")]
- public string LanguageKey { get; set; }
-
- ///
- /// Used to check the designmode during a constructor
- ///
- ///
- protected static bool IsInDesignMode
- {
- get
- {
- return (Application.ExecutablePath.IndexOf("devenv.exe", StringComparison.OrdinalIgnoreCase) > -1) ||
- (Application.ExecutablePath.IndexOf("sharpdevelop.exe", StringComparison.OrdinalIgnoreCase) > -1 ||
- (Application.ExecutablePath.IndexOf("wdexpress.exe", StringComparison.OrdinalIgnoreCase) > -1));
- }
- }
-
- protected bool ManualLanguageApply
- {
- get { return _applyLanguageManually; }
- set { _applyLanguageManually = value; }
- }
-
- protected bool ManualStoreFields
- {
- get { return _storeFieldsManually; }
- set { _storeFieldsManually = value; }
- }
-
- ///
- /// When this is set, the form will be brought to the foreground as soon as it is shown.
- ///
- protected bool ToFront { get; set; }
-
- ///
- /// Code to initialize the language etc during design time
- ///
- protected void InitializeForDesigner()
- {
- if (!DesignMode) return;
- _designTimeControls = new Dictionary();
- _designTimeToolStripItems = new Dictionary();
- try
- {
- ITypeResolutionService typeResService = GetService(typeof(ITypeResolutionService)) as ITypeResolutionService;
-
- // Add a hard-path if you are using SharpDevelop
- // Language.AddLanguageFilePath(@"C:\Greenshot\Greenshot\Languages");
-
- // this "type"
- Assembly currentAssembly = GetType().Assembly;
- if (typeResService != null)
- {
- string assemblyPath = typeResService.GetPathOfAssembly(currentAssembly.GetName());
- string assemblyDirectory = Path.GetDirectoryName(assemblyPath);
- if (assemblyDirectory != null && !Language.AddLanguageFilePath(Path.Combine(assemblyDirectory, @"..\..\Greenshot\Languages\")))
- {
- Language.AddLanguageFilePath(Path.Combine(assemblyDirectory, @"..\..\..\Greenshot\Languages\"));
- }
-
- if (assemblyDirectory != null && !Language.AddLanguageFilePath(Path.Combine(assemblyDirectory, @"..\..\Languages\")))
- {
- Language.AddLanguageFilePath(Path.Combine(assemblyDirectory, @"..\..\..\Languages\"));
- }
- }
- }
- catch (Exception ex)
- {
- MessageBox.Show(ex.Message);
- }
- }
-
- ///
- /// This override is only for the design-time of the form
- ///
- ///
- protected override void OnPaint(PaintEventArgs e)
- {
- if (DesignMode)
- {
- if (!_isDesignModeLanguageSet)
- {
- _isDesignModeLanguageSet = true;
- try
- {
- ApplyLanguage();
- }
- catch (Exception)
- {
- // ignored
- }
- }
- }
-
- base.OnPaint(e);
- }
-
- protected override void OnLoad(EventArgs e)
- {
- // Every GreenshotForm should have it's default icon
- // And it might not ne needed for a Tool Window, but still for the task manager / switcher it's important
- Icon = GreenshotResources.GetGreenshotIcon();
- if (!DesignMode)
- {
- if (!_applyLanguageManually)
- {
- ApplyLanguage();
- }
-
- FillFields();
- base.OnLoad(e);
- }
- else
- {
- LOG.Info("OnLoad called from designer.");
- InitializeForDesigner();
- base.OnLoad(e);
- ApplyLanguage();
- }
- }
-
- ///
- /// Make sure the form is visible, if this is wanted
- ///
- /// EventArgs
- protected override void OnShown(EventArgs e)
- {
- base.OnShown(e);
- if (ToFront)
- {
- WindowDetails.ToForeground(Handle);
- }
- }
-
- ///
- /// check if the form was closed with an OK, if so store the values in the GreenshotControls
- ///
- ///
- protected override void OnClosed(EventArgs e)
- {
- if (!DesignMode && !_storeFieldsManually)
- {
- if (DialogResult == DialogResult.OK)
- {
- LOG.Info("Form was closed with OK: storing field values.");
- StoreFields();
- }
- }
-
- base.OnClosed(e);
- }
-
- ///
- /// This override allows the control to register event handlers for IComponentChangeService events
- /// at the time the control is sited, which happens only in design mode.
- ///
- public override ISite Site
- {
- get { return base.Site; }
- set
- {
- // Clear any component change event handlers.
- ClearChangeNotifications();
-
- // Set the new Site value.
- base.Site = value;
-
- m_changeService = (IComponentChangeService) GetService(typeof(IComponentChangeService));
-
- // Register event handlers for component change events.
- RegisterChangeNotifications();
- }
- }
-
- private void ClearChangeNotifications()
- {
- // The m_changeService value is null when not in design mode,
- // as the IComponentChangeService is only available at design time.
- m_changeService = (IComponentChangeService) GetService(typeof(IComponentChangeService));
-
- // Clear our the component change events to prepare for re-siting.
- if (m_changeService != null)
- {
- m_changeService.ComponentChanged -= OnComponentChanged;
- m_changeService.ComponentAdded -= OnComponentAdded;
- }
- }
-
- private void RegisterChangeNotifications()
- {
- // Register the event handlers for the IComponentChangeService events
- if (m_changeService != null)
- {
- m_changeService.ComponentChanged += OnComponentChanged;
- m_changeService.ComponentAdded += OnComponentAdded;
- }
- }
-
- ///
- /// This method handles the OnComponentChanged event to display a notification.
- ///
- ///
- ///
- private void OnComponentChanged(object sender, ComponentChangedEventArgs ce)
- {
- if (((IComponent) ce.Component)?.Site == null || ce.Member == null) return;
- if (!"LanguageKey".Equals(ce.Member.Name)) return;
- if (ce.Component is Control control)
- {
- LOG.InfoFormat("Changing LanguageKey for {0} to {1}", control.Name, ce.NewValue);
- ApplyLanguage(control, (string) ce.NewValue);
- }
- else
- {
- if (ce.Component is ToolStripItem item)
- {
- LOG.InfoFormat("Changing LanguageKey for {0} to {1}", item.Name, ce.NewValue);
- ApplyLanguage(item, (string) ce.NewValue);
- }
- else
- {
- LOG.InfoFormat("Not possible to changing LanguageKey for {0} to {1}", ce.Component.GetType(), ce.NewValue);
- }
- }
- }
-
- private void OnComponentAdded(object sender, ComponentEventArgs ce)
- {
- if (ce.Component?.Site == null) return;
- if (ce.Component is Control control)
- {
- if (!_designTimeControls.ContainsKey(control.Name))
- {
- _designTimeControls.Add(control.Name, control);
- }
- else
- {
- _designTimeControls[control.Name] = control;
- }
- }
- else
- {
- if (ce.Component is ToolStripItem stripItem)
- {
- ToolStripItem item = stripItem;
- if (!_designTimeControls.ContainsKey(item.Name))
- {
- _designTimeToolStripItems.Add(item.Name, item);
- }
- else
- {
- _designTimeToolStripItems[item.Name] = item;
- }
- }
- }
- }
-
- // Clean up any resources being used.
- protected override void Dispose(bool disposing)
- {
- if (disposing)
- {
- ClearChangeNotifications();
- }
-
- base.Dispose(disposing);
- }
-
- protected void ApplyLanguage(ToolStripItem applyTo, string languageKey)
- {
- string langString;
- if (!string.IsNullOrEmpty(languageKey))
- {
- if (!Language.TryGetString(languageKey, out langString))
- {
- LOG.WarnFormat("Unknown language key '{0}' configured for control '{1}', this might be okay.", languageKey, applyTo.Name);
- return;
- }
-
- applyTo.Text = langString;
- }
- else
- {
- // Fallback to control name!
- if (Language.TryGetString(applyTo.Name, out langString))
- {
- applyTo.Text = langString;
- return;
- }
-
- if (!DesignMode)
- {
- LOG.DebugFormat("Greenshot control without language key: {0}", applyTo.Name);
- }
- }
- }
-
- protected void ApplyLanguage(ToolStripItem applyTo)
- {
- if (applyTo is IGreenshotLanguageBindable languageBindable)
- {
- ApplyLanguage(applyTo, languageBindable.LanguageKey);
- }
- }
-
- protected void ApplyLanguage(Control applyTo)
- {
- if (!(applyTo is IGreenshotLanguageBindable languageBindable))
- {
- // check if it's a menu!
- if (!(applyTo is ToolStrip toolStrip))
- {
- return;
- }
-
- foreach (ToolStripItem item in toolStrip.Items)
- {
- ApplyLanguage(item);
- }
-
- return;
- }
-
- // Apply language text to the control
- ApplyLanguage(applyTo, languageBindable.LanguageKey);
-
- // Repopulate the combox boxes
- if (applyTo is IGreenshotConfigBindable configBindable && applyTo is GreenshotComboBox comboxBox)
- {
- if (!string.IsNullOrEmpty(configBindable.SectionName) && !string.IsNullOrEmpty(configBindable.PropertyName))
- {
- IniSection section = IniConfig.GetIniSection(configBindable.SectionName);
- if (section != null)
- {
- // Only update the language, so get the actual value and than repopulate
- Enum currentValue = comboxBox.GetSelectedEnum();
- comboxBox.Populate(section.Values[configBindable.PropertyName].ValueType);
- comboxBox.SetValue(currentValue);
- }
- }
- }
- }
-
- ///
- /// Helper method to cache the fieldinfo values, so we don't need to reflect all the time!
- ///
- ///
- ///
- private static FieldInfo[] GetCachedFields(Type typeToGetFieldsFor)
- {
- if (!reflectionCache.TryGetValue(typeToGetFieldsFor, out var fields))
- {
- fields = typeToGetFieldsFor.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
- reflectionCache.Add(typeToGetFieldsFor, fields);
- }
-
- return fields;
- }
-
- ///
- /// Apply all the language settings to the "Greenshot" Controls on this form
- ///
- protected void ApplyLanguage()
- {
- SuspendLayout();
- try
- {
- // Set title of the form
- if (!string.IsNullOrEmpty(LanguageKey) && Language.TryGetString(LanguageKey, out var langString))
- {
- Text = langString;
- }
-
- // Reset the text values for all GreenshotControls
- foreach (FieldInfo field in GetCachedFields(GetType()))
- {
- object controlObject = field.GetValue(this);
- if (controlObject == null)
- {
- LOG.DebugFormat("No value: {0}", field.Name);
- continue;
- }
-
- if (!(controlObject is Control applyToControl))
- {
- ToolStripItem applyToItem = controlObject as ToolStripItem;
- if (applyToItem == null)
- {
- LOG.DebugFormat("No Control or ToolStripItem: {0}", field.Name);
- continue;
- }
-
- ApplyLanguage(applyToItem);
- }
- else
- {
- ApplyLanguage(applyToControl);
- }
- }
-
- if (DesignMode)
- {
- foreach (Control designControl in _designTimeControls.Values)
- {
- ApplyLanguage(designControl);
- }
-
- foreach (ToolStripItem designToolStripItem in _designTimeToolStripItems.Values)
- {
- ApplyLanguage(designToolStripItem);
- }
- }
- }
- finally
- {
- ResumeLayout();
- }
- }
-
- ///
- /// Apply the language text to supplied control
- ///
- protected void ApplyLanguage(Control applyTo, string languageKey)
- {
- string langString;
- if (!string.IsNullOrEmpty(languageKey))
- {
- if (!Language.TryGetString(languageKey, out langString))
- {
- LOG.WarnFormat("Wrong language key '{0}' configured for control '{1}'", languageKey, applyTo.Name);
- return;
- }
-
- applyTo.Text = langString;
- }
- else
- {
- // Fallback to control name!
- if (Language.TryGetString(applyTo.Name, out langString))
- {
- applyTo.Text = langString;
- return;
- }
-
- if (!DesignMode)
- {
- LOG.DebugFormat("Greenshot control without language key: {0}", applyTo.Name);
- }
- }
- }
-
- ///
- /// Fill all GreenshotControls with the values from the configuration
- ///
- protected void FillFields()
- {
- foreach (FieldInfo field in GetCachedFields(GetType()))
- {
- var controlObject = field.GetValue(this);
- IGreenshotConfigBindable configBindable = controlObject as IGreenshotConfigBindable;
- if (!string.IsNullOrEmpty(configBindable?.SectionName) && !string.IsNullOrEmpty(configBindable.PropertyName))
- {
- IniSection section = IniConfig.GetIniSection(configBindable.SectionName);
- if (section != null)
- {
- if (!section.Values.TryGetValue(configBindable.PropertyName, out var iniValue))
- {
- LOG.DebugFormat("Wrong property '{0}' configured for field '{1}'", configBindable.PropertyName, field.Name);
- continue;
- }
-
- if (controlObject is CheckBox checkBox)
- {
- checkBox.Checked = (bool) iniValue.Value;
- checkBox.Enabled = !iniValue.IsFixed;
- continue;
- }
-
- if (controlObject is RadioButton radíoButton)
- {
- radíoButton.Checked = (bool) iniValue.Value;
- radíoButton.Enabled = !iniValue.IsFixed;
- continue;
- }
-
- if (controlObject is TextBox textBox)
- {
- if (controlObject is HotkeyControl hotkeyControl)
- {
- string hotkeyValue = (string) iniValue.Value;
- if (!string.IsNullOrEmpty(hotkeyValue))
- {
- hotkeyControl.SetHotkey(hotkeyValue);
- hotkeyControl.Enabled = !iniValue.IsFixed;
- }
-
- continue;
- }
-
- textBox.Text = iniValue.ToString();
- textBox.Enabled = !iniValue.IsFixed;
- continue;
- }
-
- if (controlObject is GreenshotComboBox comboxBox)
- {
- comboxBox.Populate(iniValue.ValueType);
- comboxBox.SetValue((Enum) iniValue.Value);
- comboxBox.Enabled = !iniValue.IsFixed;
- }
- }
- }
- }
-
- OnFieldsFilled();
- }
-
- protected virtual void OnFieldsFilled()
- {
- }
-
- ///
- /// Store all GreenshotControl values to the configuration
- ///
- protected void StoreFields()
- {
- bool iniDirty = false;
- foreach (FieldInfo field in GetCachedFields(GetType()))
- {
- var controlObject = field.GetValue(this);
- IGreenshotConfigBindable configBindable = controlObject as IGreenshotConfigBindable;
-
- if (!string.IsNullOrEmpty(configBindable?.SectionName) && !string.IsNullOrEmpty(configBindable.PropertyName))
- {
- IniSection section = IniConfig.GetIniSection(configBindable.SectionName);
- if (section != null)
- {
- if (!section.Values.TryGetValue(configBindable.PropertyName, out var iniValue))
- {
- continue;
- }
-
- if (controlObject is CheckBox checkBox)
- {
- iniValue.Value = checkBox.Checked;
- iniDirty = true;
- continue;
- }
-
- if (controlObject is RadioButton radioButton)
- {
- iniValue.Value = radioButton.Checked;
- iniDirty = true;
- continue;
- }
-
- if (controlObject is TextBox textBox)
- {
- if (controlObject is HotkeyControl hotkeyControl)
- {
- iniValue.Value = hotkeyControl.ToString();
- iniDirty = true;
- continue;
- }
-
- iniValue.UseValueOrDefault(textBox.Text);
- iniDirty = true;
- continue;
- }
-
- if (controlObject is GreenshotComboBox comboxBox)
- {
- iniValue.Value = comboxBox.GetSelectedEnum();
- iniDirty = true;
- }
- }
- }
- }
-
- if (iniDirty)
- {
- IniConfig.Save();
- }
- }
- }
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.ComponentModel.Design;
+using System.IO;
+using System.Reflection;
+using System.Windows.Forms;
+using Greenshot.Base.Core;
+using Greenshot.Base.IniFile;
+using log4net;
+
+namespace Greenshot.Base.Controls
+{
+ ///
+ /// This form is used for automatically binding the elements of the form to the language
+ ///
+ public class GreenshotForm : Form, IGreenshotLanguageBindable
+ {
+ private static readonly ILog LOG = LogManager.GetLogger(typeof(GreenshotForm));
+ protected static CoreConfiguration coreConfiguration;
+ private static readonly IDictionary reflectionCache = new Dictionary();
+ private IComponentChangeService m_changeService;
+ private bool _isDesignModeLanguageSet;
+ private bool _applyLanguageManually;
+ private bool _storeFieldsManually;
+ private IDictionary _designTimeControls;
+ private IDictionary _designTimeToolStripItems;
+
+ static GreenshotForm()
+ {
+ if (!IsInDesignMode)
+ {
+ coreConfiguration = IniConfig.GetIniSection();
+ }
+ }
+
+ [Category("Greenshot"), DefaultValue(null), Description("Specifies key of the language file to use when displaying the text.")]
+ public string LanguageKey { get; set; }
+
+ ///
+ /// Used to check the designmode during a constructor
+ ///
+ ///
+ protected static bool IsInDesignMode
+ {
+ get
+ {
+ return (Application.ExecutablePath.IndexOf("devenv.exe", StringComparison.OrdinalIgnoreCase) > -1) ||
+ (Application.ExecutablePath.IndexOf("sharpdevelop.exe", StringComparison.OrdinalIgnoreCase) > -1 ||
+ (Application.ExecutablePath.IndexOf("wdexpress.exe", StringComparison.OrdinalIgnoreCase) > -1));
+ }
+ }
+
+ protected bool ManualLanguageApply
+ {
+ get { return _applyLanguageManually; }
+ set { _applyLanguageManually = value; }
+ }
+
+ protected bool ManualStoreFields
+ {
+ get { return _storeFieldsManually; }
+ set { _storeFieldsManually = value; }
+ }
+
+ ///
+ /// When this is set, the form will be brought to the foreground as soon as it is shown.
+ ///
+ protected bool ToFront { get; set; }
+
+ ///
+ /// Code to initialize the language etc during design time
+ ///
+ protected void InitializeForDesigner()
+ {
+ if (!DesignMode) return;
+ _designTimeControls = new Dictionary();
+ _designTimeToolStripItems = new Dictionary();
+ try
+ {
+ ITypeResolutionService typeResService = GetService(typeof(ITypeResolutionService)) as ITypeResolutionService;
+
+ // Add a hard-path if you are using SharpDevelop
+ // Language.AddLanguageFilePath(@"C:\Greenshot\Greenshot\Languages");
+
+ // this "type"
+ Assembly currentAssembly = GetType().Assembly;
+ if (typeResService != null)
+ {
+ string assemblyPath = typeResService.GetPathOfAssembly(currentAssembly.GetName());
+ string assemblyDirectory = Path.GetDirectoryName(assemblyPath);
+ if (assemblyDirectory != null && !Language.AddLanguageFilePath(Path.Combine(assemblyDirectory, @"..\..\Greenshot\Languages\")))
+ {
+ Language.AddLanguageFilePath(Path.Combine(assemblyDirectory, @"..\..\..\Greenshot\Languages\"));
+ }
+
+ if (assemblyDirectory != null && !Language.AddLanguageFilePath(Path.Combine(assemblyDirectory, @"..\..\Languages\")))
+ {
+ Language.AddLanguageFilePath(Path.Combine(assemblyDirectory, @"..\..\..\Languages\"));
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ MessageBox.Show(ex.Message);
+ }
+ }
+
+ ///
+ /// This override is only for the design-time of the form
+ ///
+ ///
+ protected override void OnPaint(PaintEventArgs e)
+ {
+ if (DesignMode)
+ {
+ if (!_isDesignModeLanguageSet)
+ {
+ _isDesignModeLanguageSet = true;
+ try
+ {
+ ApplyLanguage();
+ }
+ catch (Exception)
+ {
+ // ignored
+ }
+ }
+ }
+
+ base.OnPaint(e);
+ }
+
+ protected override void OnLoad(EventArgs e)
+ {
+ // Every GreenshotForm should have it's default icon
+ // And it might not ne needed for a Tool Window, but still for the task manager / switcher it's important
+ Icon = GreenshotResources.GetGreenshotIcon();
+ if (!DesignMode)
+ {
+ if (!_applyLanguageManually)
+ {
+ ApplyLanguage();
+ }
+
+ FillFields();
+ base.OnLoad(e);
+ }
+ else
+ {
+ LOG.Info("OnLoad called from designer.");
+ InitializeForDesigner();
+ base.OnLoad(e);
+ ApplyLanguage();
+ }
+ }
+
+ ///
+ /// Make sure the form is visible, if this is wanted
+ ///
+ /// EventArgs
+ protected override void OnShown(EventArgs e)
+ {
+ base.OnShown(e);
+ if (ToFront)
+ {
+ WindowDetails.ToForeground(Handle);
+ }
+ }
+
+ ///
+ /// check if the form was closed with an OK, if so store the values in the GreenshotControls
+ ///
+ ///
+ protected override void OnClosed(EventArgs e)
+ {
+ if (!DesignMode && !_storeFieldsManually)
+ {
+ if (DialogResult == DialogResult.OK)
+ {
+ LOG.Info("Form was closed with OK: storing field values.");
+ StoreFields();
+ }
+ }
+
+ base.OnClosed(e);
+ }
+
+ ///
+ /// This override allows the control to register event handlers for IComponentChangeService events
+ /// at the time the control is sited, which happens only in design mode.
+ ///
+ public override ISite Site
+ {
+ get { return base.Site; }
+ set
+ {
+ // Clear any component change event handlers.
+ ClearChangeNotifications();
+
+ // Set the new Site value.
+ base.Site = value;
+
+ m_changeService = (IComponentChangeService) GetService(typeof(IComponentChangeService));
+
+ // Register event handlers for component change events.
+ RegisterChangeNotifications();
+ }
+ }
+
+ private void ClearChangeNotifications()
+ {
+ // The m_changeService value is null when not in design mode,
+ // as the IComponentChangeService is only available at design time.
+ m_changeService = (IComponentChangeService) GetService(typeof(IComponentChangeService));
+
+ // Clear our the component change events to prepare for re-siting.
+ if (m_changeService != null)
+ {
+ m_changeService.ComponentChanged -= OnComponentChanged;
+ m_changeService.ComponentAdded -= OnComponentAdded;
+ }
+ }
+
+ private void RegisterChangeNotifications()
+ {
+ // Register the event handlers for the IComponentChangeService events
+ if (m_changeService != null)
+ {
+ m_changeService.ComponentChanged += OnComponentChanged;
+ m_changeService.ComponentAdded += OnComponentAdded;
+ }
+ }
+
+ ///
+ /// This method handles the OnComponentChanged event to display a notification.
+ ///
+ ///
+ ///
+ private void OnComponentChanged(object sender, ComponentChangedEventArgs ce)
+ {
+ if (((IComponent) ce.Component)?.Site == null || ce.Member == null) return;
+ if (!"LanguageKey".Equals(ce.Member.Name)) return;
+ if (ce.Component is Control control)
+ {
+ LOG.InfoFormat("Changing LanguageKey for {0} to {1}", control.Name, ce.NewValue);
+ ApplyLanguage(control, (string) ce.NewValue);
+ }
+ else
+ {
+ if (ce.Component is ToolStripItem item)
+ {
+ LOG.InfoFormat("Changing LanguageKey for {0} to {1}", item.Name, ce.NewValue);
+ ApplyLanguage(item, (string) ce.NewValue);
+ }
+ else
+ {
+ LOG.InfoFormat("Not possible to changing LanguageKey for {0} to {1}", ce.Component.GetType(), ce.NewValue);
+ }
+ }
+ }
+
+ private void OnComponentAdded(object sender, ComponentEventArgs ce)
+ {
+ if (ce.Component?.Site == null) return;
+ if (ce.Component is Control control)
+ {
+ if (!_designTimeControls.ContainsKey(control.Name))
+ {
+ _designTimeControls.Add(control.Name, control);
+ }
+ else
+ {
+ _designTimeControls[control.Name] = control;
+ }
+ }
+ else
+ {
+ if (ce.Component is ToolStripItem stripItem)
+ {
+ ToolStripItem item = stripItem;
+ if (!_designTimeControls.ContainsKey(item.Name))
+ {
+ _designTimeToolStripItems.Add(item.Name, item);
+ }
+ else
+ {
+ _designTimeToolStripItems[item.Name] = item;
+ }
+ }
+ }
+ }
+
+ // Clean up any resources being used.
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing)
+ {
+ ClearChangeNotifications();
+ }
+
+ base.Dispose(disposing);
+ }
+
+ protected void ApplyLanguage(ToolStripItem applyTo, string languageKey)
+ {
+ string langString;
+ if (!string.IsNullOrEmpty(languageKey))
+ {
+ if (!Language.TryGetString(languageKey, out langString))
+ {
+ LOG.WarnFormat("Unknown language key '{0}' configured for control '{1}', this might be okay.", languageKey, applyTo.Name);
+ return;
+ }
+
+ applyTo.Text = langString;
+ }
+ else
+ {
+ // Fallback to control name!
+ if (Language.TryGetString(applyTo.Name, out langString))
+ {
+ applyTo.Text = langString;
+ return;
+ }
+
+ if (!DesignMode)
+ {
+ LOG.DebugFormat("Greenshot control without language key: {0}", applyTo.Name);
+ }
+ }
+ }
+
+ protected void ApplyLanguage(ToolStripItem applyTo)
+ {
+ if (applyTo is IGreenshotLanguageBindable languageBindable)
+ {
+ ApplyLanguage(applyTo, languageBindable.LanguageKey);
+ }
+ }
+
+ protected void ApplyLanguage(Control applyTo)
+ {
+ if (!(applyTo is IGreenshotLanguageBindable languageBindable))
+ {
+ // check if it's a menu!
+ if (!(applyTo is ToolStrip toolStrip))
+ {
+ return;
+ }
+
+ foreach (ToolStripItem item in toolStrip.Items)
+ {
+ ApplyLanguage(item);
+ }
+
+ return;
+ }
+
+ // Apply language text to the control
+ ApplyLanguage(applyTo, languageBindable.LanguageKey);
+
+ // Repopulate the combox boxes
+ if (applyTo is IGreenshotConfigBindable configBindable && applyTo is GreenshotComboBox comboxBox)
+ {
+ if (!string.IsNullOrEmpty(configBindable.SectionName) && !string.IsNullOrEmpty(configBindable.PropertyName))
+ {
+ IniSection section = IniConfig.GetIniSection(configBindable.SectionName);
+ if (section != null)
+ {
+ // Only update the language, so get the actual value and than repopulate
+ Enum currentValue = comboxBox.GetSelectedEnum();
+ comboxBox.Populate(section.Values[configBindable.PropertyName].ValueType);
+ comboxBox.SetValue(currentValue);
+ }
+ }
+ }
+ }
+
+ ///
+ /// Helper method to cache the fieldinfo values, so we don't need to reflect all the time!
+ ///
+ ///
+ ///
+ private static FieldInfo[] GetCachedFields(Type typeToGetFieldsFor)
+ {
+ if (!reflectionCache.TryGetValue(typeToGetFieldsFor, out var fields))
+ {
+ fields = typeToGetFieldsFor.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
+ reflectionCache.Add(typeToGetFieldsFor, fields);
+ }
+
+ return fields;
+ }
+
+ ///
+ /// Apply all the language settings to the "Greenshot" Controls on this form
+ ///
+ protected void ApplyLanguage()
+ {
+ SuspendLayout();
+ try
+ {
+ // Set title of the form
+ if (!string.IsNullOrEmpty(LanguageKey) && Language.TryGetString(LanguageKey, out var langString))
+ {
+ Text = langString;
+ }
+
+ // Reset the text values for all GreenshotControls
+ foreach (FieldInfo field in GetCachedFields(GetType()))
+ {
+ object controlObject = field.GetValue(this);
+ if (controlObject == null)
+ {
+ LOG.DebugFormat("No value: {0}", field.Name);
+ continue;
+ }
+
+ if (!(controlObject is Control applyToControl))
+ {
+ ToolStripItem applyToItem = controlObject as ToolStripItem;
+ if (applyToItem == null)
+ {
+ LOG.DebugFormat("No Control or ToolStripItem: {0}", field.Name);
+ continue;
+ }
+
+ ApplyLanguage(applyToItem);
+ }
+ else
+ {
+ ApplyLanguage(applyToControl);
+ }
+ }
+
+ if (DesignMode)
+ {
+ foreach (Control designControl in _designTimeControls.Values)
+ {
+ ApplyLanguage(designControl);
+ }
+
+ foreach (ToolStripItem designToolStripItem in _designTimeToolStripItems.Values)
+ {
+ ApplyLanguage(designToolStripItem);
+ }
+ }
+ }
+ finally
+ {
+ ResumeLayout();
+ }
+ }
+
+ ///
+ /// Apply the language text to supplied control
+ ///
+ protected void ApplyLanguage(Control applyTo, string languageKey)
+ {
+ string langString;
+ if (!string.IsNullOrEmpty(languageKey))
+ {
+ if (!Language.TryGetString(languageKey, out langString))
+ {
+ LOG.WarnFormat("Wrong language key '{0}' configured for control '{1}'", languageKey, applyTo.Name);
+ return;
+ }
+
+ applyTo.Text = langString;
+ }
+ else
+ {
+ // Fallback to control name!
+ if (Language.TryGetString(applyTo.Name, out langString))
+ {
+ applyTo.Text = langString;
+ return;
+ }
+
+ if (!DesignMode)
+ {
+ LOG.DebugFormat("Greenshot control without language key: {0}", applyTo.Name);
+ }
+ }
+ }
+
+ ///
+ /// Fill all GreenshotControls with the values from the configuration
+ ///
+ protected void FillFields()
+ {
+ foreach (FieldInfo field in GetCachedFields(GetType()))
+ {
+ var controlObject = field.GetValue(this);
+ IGreenshotConfigBindable configBindable = controlObject as IGreenshotConfigBindable;
+ if (!string.IsNullOrEmpty(configBindable?.SectionName) && !string.IsNullOrEmpty(configBindable.PropertyName))
+ {
+ IniSection section = IniConfig.GetIniSection(configBindable.SectionName);
+ if (section != null)
+ {
+ if (!section.Values.TryGetValue(configBindable.PropertyName, out var iniValue))
+ {
+ LOG.DebugFormat("Wrong property '{0}' configured for field '{1}'", configBindable.PropertyName, field.Name);
+ continue;
+ }
+
+ if (controlObject is CheckBox checkBox)
+ {
+ checkBox.Checked = (bool) iniValue.Value;
+ checkBox.Enabled = !iniValue.IsFixed;
+ continue;
+ }
+
+ if (controlObject is RadioButton radíoButton)
+ {
+ radíoButton.Checked = (bool) iniValue.Value;
+ radíoButton.Enabled = !iniValue.IsFixed;
+ continue;
+ }
+
+ if (controlObject is TextBox textBox)
+ {
+ if (controlObject is HotkeyControl hotkeyControl)
+ {
+ string hotkeyValue = (string) iniValue.Value;
+ if (!string.IsNullOrEmpty(hotkeyValue))
+ {
+ hotkeyControl.SetHotkey(hotkeyValue);
+ hotkeyControl.Enabled = !iniValue.IsFixed;
+ }
+
+ continue;
+ }
+
+ textBox.Text = iniValue.ToString();
+ textBox.Enabled = !iniValue.IsFixed;
+ continue;
+ }
+
+ if (controlObject is GreenshotComboBox comboxBox)
+ {
+ comboxBox.Populate(iniValue.ValueType);
+ comboxBox.SetValue((Enum) iniValue.Value);
+ comboxBox.Enabled = !iniValue.IsFixed;
+ }
+ }
+ }
+ }
+
+ OnFieldsFilled();
+ }
+
+ protected virtual void OnFieldsFilled()
+ {
+ }
+
+ ///
+ /// Store all GreenshotControl values to the configuration
+ ///
+ protected void StoreFields()
+ {
+ bool iniDirty = false;
+ foreach (FieldInfo field in GetCachedFields(GetType()))
+ {
+ var controlObject = field.GetValue(this);
+ IGreenshotConfigBindable configBindable = controlObject as IGreenshotConfigBindable;
+
+ if (!string.IsNullOrEmpty(configBindable?.SectionName) && !string.IsNullOrEmpty(configBindable.PropertyName))
+ {
+ IniSection section = IniConfig.GetIniSection(configBindable.SectionName);
+ if (section != null)
+ {
+ if (!section.Values.TryGetValue(configBindable.PropertyName, out var iniValue))
+ {
+ continue;
+ }
+
+ if (controlObject is CheckBox checkBox)
+ {
+ iniValue.Value = checkBox.Checked;
+ iniDirty = true;
+ continue;
+ }
+
+ if (controlObject is RadioButton radioButton)
+ {
+ iniValue.Value = radioButton.Checked;
+ iniDirty = true;
+ continue;
+ }
+
+ if (controlObject is TextBox textBox)
+ {
+ if (controlObject is HotkeyControl hotkeyControl)
+ {
+ iniValue.Value = hotkeyControl.ToString();
+ iniDirty = true;
+ continue;
+ }
+
+ iniValue.UseValueOrDefault(textBox.Text);
+ iniDirty = true;
+ continue;
+ }
+
+ if (controlObject is GreenshotComboBox comboxBox)
+ {
+ iniValue.Value = comboxBox.GetSelectedEnum();
+ iniDirty = true;
+ }
+ }
+ }
+ }
+
+ if (iniDirty)
+ {
+ IniConfig.Save();
+ }
+ }
+ }
}
\ No newline at end of file
diff --git a/src/GreenshotPlugin/Controls/GreenshotGroupBox.cs b/src/Greenshot.Base/Controls/GreenshotGroupBox.cs
similarity index 95%
rename from src/GreenshotPlugin/Controls/GreenshotGroupBox.cs
rename to src/Greenshot.Base/Controls/GreenshotGroupBox.cs
index 7f4886122..a213f1139 100644
--- a/src/GreenshotPlugin/Controls/GreenshotGroupBox.cs
+++ b/src/Greenshot.Base/Controls/GreenshotGroupBox.cs
@@ -1,32 +1,32 @@
-/*
- * Greenshot - a free and open source screenshot tool
- * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
- *
- * For more information see: http://getgreenshot.org/
- * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 1 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-using System.Windows.Forms;
-using System.ComponentModel;
-
-namespace GreenshotPlugin.Controls
-{
- public class GreenshotGroupBox : GroupBox, IGreenshotLanguageBindable
- {
- [Category("Greenshot"), DefaultValue(null), Description("Specifies key of the language file to use when displaying the text.")]
- public string LanguageKey { get; set; }
- }
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+using System.ComponentModel;
+using System.Windows.Forms;
+
+namespace Greenshot.Base.Controls
+{
+ public class GreenshotGroupBox : GroupBox, IGreenshotLanguageBindable
+ {
+ [Category("Greenshot"), DefaultValue(null), Description("Specifies key of the language file to use when displaying the text.")]
+ public string LanguageKey { get; set; }
+ }
}
\ No newline at end of file
diff --git a/src/GreenshotPlugin/Controls/GreenshotLabel.cs b/src/Greenshot.Base/Controls/GreenshotLabel.cs
similarity index 94%
rename from src/GreenshotPlugin/Controls/GreenshotLabel.cs
rename to src/Greenshot.Base/Controls/GreenshotLabel.cs
index 904980f9f..152bf5fb1 100644
--- a/src/GreenshotPlugin/Controls/GreenshotLabel.cs
+++ b/src/Greenshot.Base/Controls/GreenshotLabel.cs
@@ -1,32 +1,32 @@
-/*
- * Greenshot - a free and open source screenshot tool
- * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
- *
- * For more information see: http://getgreenshot.org/
- * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 1 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-using System.Windows.Forms;
-using System.ComponentModel;
-
-namespace GreenshotPlugin.Controls
-{
- public class GreenshotLabel : Label, IGreenshotLanguageBindable
- {
- [Category("Greenshot"), DefaultValue(null), Description("Specifies key of the language file to use when displaying the text.")]
- public string LanguageKey { get; set; }
- }
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+using System.ComponentModel;
+using System.Windows.Forms;
+
+namespace Greenshot.Base.Controls
+{
+ public class GreenshotLabel : Label, IGreenshotLanguageBindable
+ {
+ [Category("Greenshot"), DefaultValue(null), Description("Specifies key of the language file to use when displaying the text.")]
+ public string LanguageKey { get; set; }
+ }
}
\ No newline at end of file
diff --git a/src/GreenshotPlugin/Controls/GreenshotRadioButton.cs b/src/Greenshot.Base/Controls/GreenshotRadioButton.cs
similarity index 95%
rename from src/GreenshotPlugin/Controls/GreenshotRadioButton.cs
rename to src/Greenshot.Base/Controls/GreenshotRadioButton.cs
index 9fdc172d4..0e0e87d58 100644
--- a/src/GreenshotPlugin/Controls/GreenshotRadioButton.cs
+++ b/src/Greenshot.Base/Controls/GreenshotRadioButton.cs
@@ -1,41 +1,41 @@
-/*
- * Greenshot - a free and open source screenshot tool
- * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
- *
- * For more information see: http://getgreenshot.org/
- * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 1 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-using System.ComponentModel;
-using System.Windows.Forms;
-
-namespace GreenshotPlugin.Controls
-{
- ///
- /// Description of GreenshotCheckbox.
- ///
- public class GreenshotRadioButton : RadioButton, IGreenshotLanguageBindable, IGreenshotConfigBindable
- {
- [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("Core"), Description("Specifies the Ini-Section to map this control with.")]
- public string SectionName { get; set; } = "Core";
-
- [Category("Greenshot"), DefaultValue(null), Description("Specifies the property name to map the configuration.")]
- public string PropertyName { get; set; }
- }
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+using System.ComponentModel;
+using System.Windows.Forms;
+
+namespace Greenshot.Base.Controls
+{
+ ///
+ /// Description of GreenshotCheckbox.
+ ///
+ public class GreenshotRadioButton : RadioButton, IGreenshotLanguageBindable, IGreenshotConfigBindable
+ {
+ [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("Core"), Description("Specifies the Ini-Section to map this control with.")]
+ public string SectionName { get; set; } = "Core";
+
+ [Category("Greenshot"), DefaultValue(null), Description("Specifies the property name to map the configuration.")]
+ public string PropertyName { get; set; }
+ }
}
\ No newline at end of file
diff --git a/src/GreenshotPlugin/Controls/GreenshotTabPage.cs b/src/Greenshot.Base/Controls/GreenshotTabPage.cs
similarity index 95%
rename from src/GreenshotPlugin/Controls/GreenshotTabPage.cs
rename to src/Greenshot.Base/Controls/GreenshotTabPage.cs
index 40b53b30f..8577368c9 100644
--- a/src/GreenshotPlugin/Controls/GreenshotTabPage.cs
+++ b/src/Greenshot.Base/Controls/GreenshotTabPage.cs
@@ -1,32 +1,32 @@
-/*
- * Greenshot - a free and open source screenshot tool
- * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
- *
- * For more information see: http://getgreenshot.org/
- * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 1 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-using System.Windows.Forms;
-using System.ComponentModel;
-
-namespace GreenshotPlugin.Controls
-{
- public class GreenshotTabPage : TabPage, IGreenshotLanguageBindable
- {
- [Category("Greenshot"), DefaultValue(null), Description("Specifies key of the language file to use when displaying the text.")]
- public string LanguageKey { get; set; }
- }
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+using System.ComponentModel;
+using System.Windows.Forms;
+
+namespace Greenshot.Base.Controls
+{
+ public class GreenshotTabPage : TabPage, IGreenshotLanguageBindable
+ {
+ [Category("Greenshot"), DefaultValue(null), Description("Specifies key of the language file to use when displaying the text.")]
+ public string LanguageKey { get; set; }
+ }
}
\ No newline at end of file
diff --git a/src/GreenshotPlugin/Controls/GreenshotTextBox.cs b/src/Greenshot.Base/Controls/GreenshotTextBox.cs
similarity index 95%
rename from src/GreenshotPlugin/Controls/GreenshotTextBox.cs
rename to src/Greenshot.Base/Controls/GreenshotTextBox.cs
index 49c7a4d2e..744a479d1 100644
--- a/src/GreenshotPlugin/Controls/GreenshotTextBox.cs
+++ b/src/Greenshot.Base/Controls/GreenshotTextBox.cs
@@ -1,35 +1,35 @@
-/*
- * Greenshot - a free and open source screenshot tool
- * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
- *
- * For more information see: http://getgreenshot.org/
- * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 1 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-using System.ComponentModel;
-using System.Windows.Forms;
-
-namespace GreenshotPlugin.Controls
-{
- public class GreenshotTextBox : TextBox, IGreenshotConfigBindable
- {
- [Category("Greenshot"), DefaultValue("Core"), Description("Specifies the Ini-Section to map this control with.")]
- public string SectionName { get; set; } = "Core";
-
- [Category("Greenshot"), DefaultValue(null), Description("Specifies the property name to map the configuration.")]
- public string PropertyName { get; set; }
- }
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+using System.ComponentModel;
+using System.Windows.Forms;
+
+namespace Greenshot.Base.Controls
+{
+ public class GreenshotTextBox : TextBox, IGreenshotConfigBindable
+ {
+ [Category("Greenshot"), DefaultValue("Core"), Description("Specifies the Ini-Section to map this control with.")]
+ public string SectionName { get; set; } = "Core";
+
+ [Category("Greenshot"), DefaultValue(null), Description("Specifies the property name to map the configuration.")]
+ public string PropertyName { get; set; }
+ }
}
\ No newline at end of file
diff --git a/src/GreenshotPlugin/Controls/GreenshotToolDropDownButton.cs b/src/Greenshot.Base/Controls/GreenshotToolDropDownButton.cs
similarity index 95%
rename from src/GreenshotPlugin/Controls/GreenshotToolDropDownButton.cs
rename to src/Greenshot.Base/Controls/GreenshotToolDropDownButton.cs
index e719187c7..81f5b7c2f 100644
--- a/src/GreenshotPlugin/Controls/GreenshotToolDropDownButton.cs
+++ b/src/Greenshot.Base/Controls/GreenshotToolDropDownButton.cs
@@ -1,32 +1,32 @@
-/*
- * Greenshot - a free and open source screenshot tool
- * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
- *
- * For more information see: http://getgreenshot.org/
- * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 1 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-using System.ComponentModel;
-using System.Windows.Forms;
-
-namespace GreenshotPlugin.Controls
-{
- public class GreenshotToolStripDropDownButton : ToolStripDropDownButton, IGreenshotLanguageBindable
- {
- [Category("Greenshot"), DefaultValue(null), Description("Specifies key of the language file to use when displaying the text.")]
- public string LanguageKey { get; set; }
- }
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+using System.ComponentModel;
+using System.Windows.Forms;
+
+namespace Greenshot.Base.Controls
+{
+ public class GreenshotToolStripDropDownButton : ToolStripDropDownButton, IGreenshotLanguageBindable
+ {
+ [Category("Greenshot"), DefaultValue(null), Description("Specifies key of the language file to use when displaying the text.")]
+ public string LanguageKey { get; set; }
+ }
}
\ No newline at end of file
diff --git a/src/GreenshotPlugin/Controls/GreenshotToolStripButton.cs b/src/Greenshot.Base/Controls/GreenshotToolStripButton.cs
similarity index 95%
rename from src/GreenshotPlugin/Controls/GreenshotToolStripButton.cs
rename to src/Greenshot.Base/Controls/GreenshotToolStripButton.cs
index e95bd1803..68dee4ba6 100644
--- a/src/GreenshotPlugin/Controls/GreenshotToolStripButton.cs
+++ b/src/Greenshot.Base/Controls/GreenshotToolStripButton.cs
@@ -1,32 +1,32 @@
-/*
- * Greenshot - a free and open source screenshot tool
- * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
- *
- * For more information see: http://getgreenshot.org/
- * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 1 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-using System.ComponentModel;
-using System.Windows.Forms;
-
-namespace GreenshotPlugin.Controls
-{
- public class GreenshotToolStripButton : ToolStripButton, IGreenshotLanguageBindable
- {
- [Category("Greenshot"), DefaultValue(null), Description("Specifies key of the language file to use when displaying the text.")]
- public string LanguageKey { get; set; }
- }
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+using System.ComponentModel;
+using System.Windows.Forms;
+
+namespace Greenshot.Base.Controls
+{
+ public class GreenshotToolStripButton : ToolStripButton, IGreenshotLanguageBindable
+ {
+ [Category("Greenshot"), DefaultValue(null), Description("Specifies key of the language file to use when displaying the text.")]
+ public string LanguageKey { get; set; }
+ }
}
\ No newline at end of file
diff --git a/src/GreenshotPlugin/Controls/GreenshotToolStripLabel.cs b/src/Greenshot.Base/Controls/GreenshotToolStripLabel.cs
similarity index 95%
rename from src/GreenshotPlugin/Controls/GreenshotToolStripLabel.cs
rename to src/Greenshot.Base/Controls/GreenshotToolStripLabel.cs
index 07443d894..e47de2955 100644
--- a/src/GreenshotPlugin/Controls/GreenshotToolStripLabel.cs
+++ b/src/Greenshot.Base/Controls/GreenshotToolStripLabel.cs
@@ -1,32 +1,32 @@
-/*
- * Greenshot - a free and open source screenshot tool
- * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
- *
- * For more information see: http://getgreenshot.org/
- * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 1 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-using System.Windows.Forms;
-using System.ComponentModel;
-
-namespace GreenshotPlugin.Controls
-{
- public class GreenshotToolStripLabel : ToolStripLabel, IGreenshotLanguageBindable
- {
- [Category("Greenshot"), DefaultValue(null), Description("Specifies key of the language file to use when displaying the text.")]
- public string LanguageKey { get; set; }
- }
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+using System.ComponentModel;
+using System.Windows.Forms;
+
+namespace Greenshot.Base.Controls
+{
+ public class GreenshotToolStripLabel : ToolStripLabel, IGreenshotLanguageBindable
+ {
+ [Category("Greenshot"), DefaultValue(null), Description("Specifies key of the language file to use when displaying the text.")]
+ public string LanguageKey { get; set; }
+ }
}
\ No newline at end of file
diff --git a/src/GreenshotPlugin/Controls/GreenshotToolStripMenuItem.cs b/src/Greenshot.Base/Controls/GreenshotToolStripMenuItem.cs
similarity index 95%
rename from src/GreenshotPlugin/Controls/GreenshotToolStripMenuItem.cs
rename to src/Greenshot.Base/Controls/GreenshotToolStripMenuItem.cs
index c7a64c743..4feec806b 100644
--- a/src/GreenshotPlugin/Controls/GreenshotToolStripMenuItem.cs
+++ b/src/Greenshot.Base/Controls/GreenshotToolStripMenuItem.cs
@@ -1,32 +1,32 @@
-/*
- * Greenshot - a free and open source screenshot tool
- * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
- *
- * For more information see: http://getgreenshot.org/
- * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 1 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-using System.ComponentModel;
-using System.Windows.Forms;
-
-namespace GreenshotPlugin.Controls
-{
- public class GreenshotToolStripMenuItem : ToolStripMenuItem, IGreenshotLanguageBindable
- {
- [Category("Greenshot"), DefaultValue(null), Description("Specifies key of the language file to use when displaying the text.")]
- public string LanguageKey { get; set; }
- }
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+using System.ComponentModel;
+using System.Windows.Forms;
+
+namespace Greenshot.Base.Controls
+{
+ public class GreenshotToolStripMenuItem : ToolStripMenuItem, IGreenshotLanguageBindable
+ {
+ [Category("Greenshot"), DefaultValue(null), Description("Specifies key of the language file to use when displaying the text.")]
+ public string LanguageKey { get; set; }
+ }
}
\ No newline at end of file
diff --git a/src/GreenshotPlugin/Controls/HotkeyControl.cs b/src/Greenshot.Base/Controls/HotkeyControl.cs
similarity index 96%
rename from src/GreenshotPlugin/Controls/HotkeyControl.cs
rename to src/Greenshot.Base/Controls/HotkeyControl.cs
index af59a27dd..da1ae6696 100644
--- a/src/GreenshotPlugin/Controls/HotkeyControl.cs
+++ b/src/Greenshot.Base/Controls/HotkeyControl.cs
@@ -1,686 +1,686 @@
-/*
- * Greenshot - a free and open source screenshot tool
- * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
- *
- * For more information see: http://getgreenshot.org/
- * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 1 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-using System;
-using System.Collections.Generic;
-using System.Diagnostics.CodeAnalysis;
-using System.Runtime.InteropServices;
-using System.Text;
-using System.Windows.Forms;
-using log4net;
-using GreenshotPlugin.Core;
-using GreenshotPlugin.Interfaces.Plugin;
-
-namespace GreenshotPlugin.Controls
-{
- ///
- /// A simple control that allows the user to select pretty much any valid hotkey combination
- /// See: http://www.codeproject.com/KB/buttons/hotkeycontrol.aspx
- /// But is modified to fit in Greenshot, and have localized support
- ///
- public sealed class HotkeyControl : GreenshotTextBox
- {
- private static readonly ILog Log = LogManager.GetLogger(typeof(HotkeyControl));
-
- private static readonly EventDelay EventDelay = new EventDelay(TimeSpan.FromMilliseconds(600).Ticks);
- private static readonly bool IsWindows7OrOlder = Environment.OSVersion.Version.Major >= 6 && Environment.OSVersion.Version.Minor >= 1;
-
- // Holds the list of hotkeys
- private static readonly IDictionary KeyHandlers = new Dictionary();
- private static int _hotKeyCounter = 1;
- private const uint WM_HOTKEY = 0x312;
- private static IntPtr _hotkeyHwnd;
-
- [SuppressMessage("ReSharper", "InconsistentNaming")]
- public enum Modifiers : uint
- {
- NONE = 0,
- ALT = 1,
- CTRL = 2,
- SHIFT = 4,
- WIN = 8,
- NO_REPEAT = 0x4000
- }
-
- [SuppressMessage("ReSharper", "InconsistentNaming")]
- private enum MapType : uint
- {
- MAPVK_VK_TO_VSC =
- 0, //The uCode parameter is a virtual-key code and is translated into a scan code. If it is a virtual-key code that does not distinguish between left- and right-hand keys, the left-hand scan code is returned. If there is no translation, the function returns 0.
-
- MAPVK_VSC_TO_VK =
- 1, //The uCode parameter is a scan code and is translated into a virtual-key code that does not distinguish between left- and right-hand keys. If there is no translation, the function returns 0.
-
- MAPVK_VK_TO_CHAR =
- 2, //The uCode parameter is a virtual-key code and is translated into an unshifted character value in the low order word of the return value. Dead keys (diacritics) are indicated by setting the top bit of the return value. If there is no translation, the function returns 0.
-
- MAPVK_VSC_TO_VK_EX =
- 3, //The uCode parameter is a scan code and is translated into a virtual-key code that distinguishes between left- and right-hand keys. If there is no translation, the function returns 0.
-
- MAPVK_VK_TO_VSC_EX =
- 4 //The uCode parameter is a virtual-key code and is translated into a scan code. If it is a virtual-key code that does not distinguish between left- and right-hand keys, the left-hand scan code is returned. If the scan code is an extended scan code, the high byte of the uCode value can contain either 0xe0 or 0xe1 to specify the extended scan code. If there is no translation, the function returns 0.
- }
-
- [DllImport("user32.dll", SetLastError = true)]
- [return: MarshalAs(UnmanagedType.Bool)]
- private static extern bool RegisterHotKey(IntPtr hWnd, int id, uint fsModifiers, uint virtualKeyCode);
-
- [DllImport("user32.dll", SetLastError = true)]
- [return: MarshalAs(UnmanagedType.Bool)]
- private static extern bool UnregisterHotKey(IntPtr hWnd, int id);
-
- [DllImport("user32.dll", SetLastError = true)]
- private static extern uint MapVirtualKey(uint uCode, uint uMapType);
-
- [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
- private static extern int GetKeyNameText(uint lParam, [Out] StringBuilder lpString, int nSize);
-
- // These variables store the current hotkey and modifier(s)
- private Keys _hotkey = Keys.None;
- private Keys _modifiers = Keys.None;
-
- // ArrayLists used to enforce the use of proper modifiers.
- // Shift+A isn't a valid hotkey, for instance, as it would screw up when the user is typing.
- private readonly IList _needNonShiftModifier = new List();
- private readonly IList _needNonAltGrModifier = new List();
-
- private readonly ContextMenuStrip _dummy = new ContextMenuStrip();
-
- ///
- /// Used to make sure that there is no right-click menu available
- ///
- public override ContextMenuStrip ContextMenuStrip
- {
- get { return _dummy; }
- set { base.ContextMenuStrip = _dummy; }
- }
-
- ///
- /// Forces the control to be non-multiline
- ///
- public override bool Multiline
- {
- get { return base.Multiline; }
- set
- {
- // Ignore what the user wants; force Multiline to false
- base.Multiline = false;
- }
- }
-
- ///
- /// Creates a new HotkeyControl
- ///
- public HotkeyControl()
- {
- ContextMenuStrip = _dummy; // Disable right-clicking
- Text = "None";
-
- // Handle events that occurs when keys are pressed
- KeyPress += HotkeyControl_KeyPress;
- KeyUp += HotkeyControl_KeyUp;
- KeyDown += HotkeyControl_KeyDown;
-
- PopulateModifierLists();
- }
-
- ///
- /// Populates the ArrayLists specifying disallowed hotkeys
- /// such as Shift+A, Ctrl+Alt+4 (would produce a dollar sign) etc
- ///
- private void PopulateModifierLists()
- {
- // Shift + 0 - 9, A - Z
- for (Keys k = Keys.D0; k <= Keys.Z; k++)
- {
- _needNonShiftModifier.Add((int) k);
- }
-
- // Shift + Numpad keys
- for (Keys k = Keys.NumPad0; k <= Keys.NumPad9; k++)
- {
- _needNonShiftModifier.Add((int) k);
- }
-
- // Shift + Misc (,;<./ etc)
- for (Keys k = Keys.Oem1; k <= Keys.OemBackslash; k++)
- {
- _needNonShiftModifier.Add((int) k);
- }
-
- // Shift + Space, PgUp, PgDn, End, Home
- for (Keys k = Keys.Space; k <= Keys.Home; k++)
- {
- _needNonShiftModifier.Add((int) k);
- }
-
- // Misc keys that we can't loop through
- _needNonShiftModifier.Add((int) Keys.Insert);
- _needNonShiftModifier.Add((int) Keys.Help);
- _needNonShiftModifier.Add((int) Keys.Multiply);
- _needNonShiftModifier.Add((int) Keys.Add);
- _needNonShiftModifier.Add((int) Keys.Subtract);
- _needNonShiftModifier.Add((int) Keys.Divide);
- _needNonShiftModifier.Add((int) Keys.Decimal);
- _needNonShiftModifier.Add((int) Keys.Return);
- _needNonShiftModifier.Add((int) Keys.Escape);
- _needNonShiftModifier.Add((int) Keys.NumLock);
-
- // Ctrl+Alt + 0 - 9
- for (Keys k = Keys.D0; k <= Keys.D9; k++)
- {
- _needNonAltGrModifier.Add((int) k);
- }
- }
-
- ///
- /// Resets this hotkey control to None
- ///
- public new void Clear()
- {
- Hotkey = Keys.None;
- HotkeyModifiers = Keys.None;
- }
-
- ///
- /// Fires when a key is pushed down. Here, we'll want to update the text in the box
- /// to notify the user what combination is currently pressed.
- ///
- private void HotkeyControl_KeyDown(object sender, KeyEventArgs e)
- {
- // Clear the current hotkey
- if (e.KeyCode == Keys.Back || e.KeyCode == Keys.Delete)
- {
- ResetHotkey();
- }
- else
- {
- _modifiers = e.Modifiers;
- _hotkey = e.KeyCode;
- Redraw();
- }
- }
-
- ///
- /// Fires when all keys are released. If the current hotkey isn't valid, reset it.
- /// Otherwise, do nothing and keep the text and hotkey as it was.
- ///
- private void HotkeyControl_KeyUp(object sender, KeyEventArgs e)
- {
- // Somehow the PrintScreen only comes as a keyup, therefore we handle it here.
- if (e.KeyCode == Keys.PrintScreen)
- {
- _modifiers = e.Modifiers;
- _hotkey = e.KeyCode;
- Redraw();
- }
-
- if (_hotkey == Keys.None && ModifierKeys == Keys.None)
- {
- ResetHotkey();
- }
- }
-
- ///
- /// Prevents the letter/whatever entered to show up in the TextBox
- /// Without this, a "A" key press would appear as "aControl, Alt + A"
- ///
- private void HotkeyControl_KeyPress(object sender, KeyPressEventArgs e)
- {
- e.Handled = true;
- }
-
- ///
- /// Handles some misc keys, such as Ctrl+Delete and Shift+Insert
- ///
- protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
- {
- if (keyData == Keys.Delete || keyData == (Keys.Control | Keys.Delete))
- {
- ResetHotkey();
- return true;
- }
-
- // Paste
- if (keyData == (Keys.Shift | Keys.Insert))
- {
- return true; // Don't allow
- }
-
- // Allow the rest
- return base.ProcessCmdKey(ref msg, keyData);
- }
-
- ///
- /// Clears the current hotkey and resets the TextBox
- ///
- public void ResetHotkey()
- {
- _hotkey = Keys.None;
- _modifiers = Keys.None;
- Redraw();
- }
-
- ///
- /// Used to get/set the hotkey (e.g. Keys.A)
- ///
- public Keys Hotkey
- {
- get { return _hotkey; }
- set
- {
- _hotkey = value;
- Redraw(true);
- }
- }
-
- ///
- /// Used to get/set the hotkey (e.g. Keys.A)
- ///
- public void SetHotkey(string hotkey)
- {
- _hotkey = HotkeyFromString(hotkey);
- _modifiers = HotkeyModifiersFromString(hotkey);
- Redraw(true);
- }
-
- ///
- /// Used to get/set the modifier keys (e.g. Keys.Alt | Keys.Control)
- ///
- public Keys HotkeyModifiers
- {
- get { return _modifiers; }
- set
- {
- _modifiers = value;
- Redraw(true);
- }
- }
-
- ///
- /// Redraws the TextBox when necessary.
- ///
- /// Specifies whether this function was called by the Hotkey/HotkeyModifiers properties or by the user.
- private void Redraw(bool bCalledProgramatically = false)
- {
- // No hotkey set
- if (_hotkey == Keys.None)
- {
- Text = string.Empty;
- return;
- }
-
- // LWin/RWin doesn't work as hotkeys (neither do they work as modifier keys in .NET 2.0)
- if (_hotkey == Keys.LWin || _hotkey == Keys.RWin)
- {
- Text = string.Empty;
- return;
- }
-
- // Only validate input if it comes from the user
- if (bCalledProgramatically == false)
- {
- // No modifier or shift only, AND a hotkey that needs another modifier
- if ((_modifiers == Keys.Shift || _modifiers == Keys.None) && _needNonShiftModifier.Contains((int) _hotkey))
- {
- if (_modifiers == Keys.None)
- {
- // Set Ctrl+Alt as the modifier unless Ctrl+Alt+ won't work...
- if (_needNonAltGrModifier.Contains((int) _hotkey) == false)
- {
- _modifiers = Keys.Alt | Keys.Control;
- }
- else
- {
- // ... in that case, use Shift+Alt instead.
- _modifiers = Keys.Alt | Keys.Shift;
- }
- }
- else
- {
- // User pressed Shift and an invalid key (e.g. a letter or a number),
- // that needs another set of modifier keys
- _hotkey = Keys.None;
- Text = string.Empty;
- return;
- }
- }
-
- // Check all Ctrl+Alt keys
- if ((_modifiers == (Keys.Alt | Keys.Control)) && _needNonAltGrModifier.Contains((int) _hotkey))
- {
- // Ctrl+Alt+4 etc won't work; reset hotkey and tell the user
- _hotkey = Keys.None;
- Text = string.Empty;
- return;
- }
- }
-
- // I have no idea why this is needed, but it is. Without this code, pressing only Ctrl
- // will show up as "Control + ControlKey", etc.
- if (_hotkey == Keys.Menu /* Alt */ || _hotkey == Keys.ShiftKey || _hotkey == Keys.ControlKey)
- {
- _hotkey = Keys.None;
- }
-
- Text = HotkeyToLocalizedString(_modifiers, _hotkey);
- }
-
- public override string ToString()
- {
- return HotkeyToString(HotkeyModifiers, Hotkey);
- }
-
- public static string GetLocalizedHotkeyStringFromString(string hotkeyString)
- {
- Keys virtualKeyCode = HotkeyFromString(hotkeyString);
- Keys modifiers = HotkeyModifiersFromString(hotkeyString);
- return HotkeyToLocalizedString(modifiers, virtualKeyCode);
- }
-
- public static string HotkeyToString(Keys modifierKeyCode, Keys virtualKeyCode)
- {
- return HotkeyModifiersToString(modifierKeyCode) + virtualKeyCode;
- }
-
- public static string HotkeyModifiersToString(Keys modifierKeyCode)
- {
- StringBuilder hotkeyString = new StringBuilder();
- if ((modifierKeyCode & Keys.Alt) > 0)
- {
- hotkeyString.Append("Alt").Append(" + ");
- }
-
- if ((modifierKeyCode & Keys.Control) > 0)
- {
- hotkeyString.Append("Ctrl").Append(" + ");
- }
-
- if ((modifierKeyCode & Keys.Shift) > 0)
- {
- hotkeyString.Append("Shift").Append(" + ");
- }
-
- if (modifierKeyCode == Keys.LWin || modifierKeyCode == Keys.RWin)
- {
- hotkeyString.Append("Win").Append(" + ");
- }
-
- return hotkeyString.ToString();
- }
-
-
- public static string HotkeyToLocalizedString(Keys modifierKeyCode, Keys virtualKeyCode)
- {
- return HotkeyModifiersToLocalizedString(modifierKeyCode) + GetKeyName(virtualKeyCode);
- }
-
- public static string HotkeyModifiersToLocalizedString(Keys modifierKeyCode)
- {
- StringBuilder hotkeyString = new StringBuilder();
- if ((modifierKeyCode & Keys.Alt) > 0)
- {
- hotkeyString.Append(GetKeyName(Keys.Alt)).Append(" + ");
- }
-
- if ((modifierKeyCode & Keys.Control) > 0)
- {
- hotkeyString.Append(GetKeyName(Keys.Control)).Append(" + ");
- }
-
- if ((modifierKeyCode & Keys.Shift) > 0)
- {
- hotkeyString.Append(GetKeyName(Keys.Shift)).Append(" + ");
- }
-
- if (modifierKeyCode == Keys.LWin || modifierKeyCode == Keys.RWin)
- {
- hotkeyString.Append("Win").Append(" + ");
- }
-
- return hotkeyString.ToString();
- }
-
-
- public static Keys HotkeyModifiersFromString(string modifiersString)
- {
- Keys modifiers = Keys.None;
- if (!string.IsNullOrEmpty(modifiersString))
- {
- if (modifiersString.ToLower().Contains("alt"))
- {
- modifiers |= Keys.Alt;
- }
-
- if (modifiersString.ToLower().Contains("ctrl"))
- {
- modifiers |= Keys.Control;
- }
-
- if (modifiersString.ToLower().Contains("shift"))
- {
- modifiers |= Keys.Shift;
- }
-
- if (modifiersString.ToLower().Contains("win"))
- {
- modifiers |= Keys.LWin;
- }
- }
-
- return modifiers;
- }
-
- public static Keys HotkeyFromString(string hotkey)
- {
- Keys key = Keys.None;
- if (!string.IsNullOrEmpty(hotkey))
- {
- if (hotkey.LastIndexOf('+') > 0)
- {
- hotkey = hotkey.Remove(0, hotkey.LastIndexOf('+') + 1).Trim();
- }
-
- key = (Keys) Enum.Parse(typeof(Keys), hotkey);
- }
-
- return key;
- }
-
- public static void RegisterHotkeyHwnd(IntPtr hWnd)
- {
- _hotkeyHwnd = hWnd;
- }
-
- ///
- /// Register a hotkey
- ///
- /// The modifier, e.g.: Modifiers.CTRL, Modifiers.NONE or Modifiers.ALT
- /// The virtual key code
- /// A HotKeyHandler, this will be called to handle the hotkey press
- /// the hotkey number, -1 if failed
- public static int RegisterHotKey(Keys modifierKeyCode, Keys virtualKeyCode, HotKeyHandler handler)
- {
- if (virtualKeyCode == Keys.None)
- {
- Log.Warn("Trying to register a Keys.none hotkey, ignoring");
- return 0;
- }
-
- // Convert Modifiers to fit HKM_SETHOTKEY
- uint modifiers = 0;
- if ((modifierKeyCode & Keys.Alt) > 0)
- {
- modifiers |= (uint) Modifiers.ALT;
- }
-
- if ((modifierKeyCode & Keys.Control) > 0)
- {
- modifiers |= (uint) Modifiers.CTRL;
- }
-
- if ((modifierKeyCode & Keys.Shift) > 0)
- {
- modifiers |= (uint) Modifiers.SHIFT;
- }
-
- if (modifierKeyCode == Keys.LWin || modifierKeyCode == Keys.RWin)
- {
- modifiers |= (uint) Modifiers.WIN;
- }
-
- // Disable repeating hotkey for Windows 7 and beyond, as described in #1559
- if (IsWindows7OrOlder)
- {
- modifiers |= (uint) Modifiers.NO_REPEAT;
- }
-
- if (RegisterHotKey(_hotkeyHwnd, _hotKeyCounter, modifiers, (uint) virtualKeyCode))
- {
- KeyHandlers.Add(_hotKeyCounter, handler);
- return _hotKeyCounter++;
- }
-
- Log.Warn($"Couldn't register hotkey modifier {modifierKeyCode} virtualKeyCode {virtualKeyCode}");
- return -1;
- }
-
- public static void UnregisterHotkeys()
- {
- foreach (int hotkey in KeyHandlers.Keys)
- {
- UnregisterHotKey(_hotkeyHwnd, hotkey);
- }
-
- // Remove all key handlers
- KeyHandlers.Clear();
- }
-
- ///
- /// Handle WndProc messages for the hotkey
- ///
- ///
- /// true if the message was handled
- public static bool HandleMessages(ref Message m)
- {
- if (m.Msg != WM_HOTKEY)
- {
- return false;
- }
-
- // Call handler
- if (!IsWindows7OrOlder && !EventDelay.Check())
- {
- return true;
- }
-
- if (KeyHandlers.TryGetValue((int) m.WParam, out var handler))
- {
- handler();
- }
-
- return true;
- }
-
- public static string GetKeyName(Keys givenKey)
- {
- StringBuilder keyName = new StringBuilder();
- const uint numpad = 55;
-
- Keys virtualKey = givenKey;
- string keyString;
- // Make VC's to real keys
- switch (virtualKey)
- {
- case Keys.Alt:
- virtualKey = Keys.LMenu;
- break;
- case Keys.Control:
- virtualKey = Keys.ControlKey;
- break;
- case Keys.Shift:
- virtualKey = Keys.LShiftKey;
- break;
- case Keys.Multiply:
- GetKeyNameText(numpad << 16, keyName, 100);
- keyString = keyName.ToString().Replace("*", string.Empty).Trim().ToLower();
- if (keyString.IndexOf("(", StringComparison.Ordinal) >= 0)
- {
- return "* " + keyString;
- }
-
- keyString = keyString.Substring(0, 1).ToUpper() + keyString.Substring(1).ToLower();
- return keyString + " *";
- case Keys.Divide:
- GetKeyNameText(numpad << 16, keyName, 100);
- keyString = keyName.ToString().Replace("*", string.Empty).Trim().ToLower();
- if (keyString.IndexOf("(", StringComparison.Ordinal) >= 0)
- {
- return "/ " + keyString;
- }
-
- keyString = keyString.Substring(0, 1).ToUpper() + keyString.Substring(1).ToLower();
- return keyString + " /";
- }
-
- uint scanCode = MapVirtualKey((uint) virtualKey, (uint) MapType.MAPVK_VK_TO_VSC);
-
- // because MapVirtualKey strips the extended bit for some keys
- switch (virtualKey)
- {
- case Keys.Left:
- case Keys.Up:
- case Keys.Right:
- case Keys.Down: // arrow keys
- case Keys.Prior:
- case Keys.Next: // page up and page down
- case Keys.End:
- case Keys.Home:
- case Keys.Insert:
- case Keys.Delete:
- case Keys.NumLock:
- Log.Debug("Modifying Extended bit");
- scanCode |= 0x100; // set extended bit
- break;
- case Keys.PrintScreen: // PrintScreen
- scanCode = 311;
- break;
- case Keys.Pause: // PrintScreen
- scanCode = 69;
- break;
- }
-
- scanCode |= 0x200;
- if (GetKeyNameText(scanCode << 16, keyName, 100) != 0)
- {
- string visibleName = keyName.ToString();
- if (visibleName.Length > 1)
- {
- visibleName = visibleName.Substring(0, 1) + visibleName.Substring(1).ToLower();
- }
-
- return visibleName;
- }
-
- return givenKey.ToString();
- }
- }
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+using System;
+using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
+using System.Runtime.InteropServices;
+using System.Text;
+using System.Windows.Forms;
+using Greenshot.Base.Core;
+using Greenshot.Base.Interfaces.Plugin;
+using log4net;
+
+namespace Greenshot.Base.Controls
+{
+ ///
+ /// A simple control that allows the user to select pretty much any valid hotkey combination
+ /// See: http://www.codeproject.com/KB/buttons/hotkeycontrol.aspx
+ /// But is modified to fit in Greenshot, and have localized support
+ ///
+ public sealed class HotkeyControl : GreenshotTextBox
+ {
+ private static readonly ILog Log = LogManager.GetLogger(typeof(HotkeyControl));
+
+ private static readonly EventDelay EventDelay = new EventDelay(TimeSpan.FromMilliseconds(600).Ticks);
+ private static readonly bool IsWindows7OrOlder = Environment.OSVersion.Version.Major >= 6 && Environment.OSVersion.Version.Minor >= 1;
+
+ // Holds the list of hotkeys
+ private static readonly IDictionary KeyHandlers = new Dictionary();
+ private static int _hotKeyCounter = 1;
+ private const uint WM_HOTKEY = 0x312;
+ private static IntPtr _hotkeyHwnd;
+
+ [SuppressMessage("ReSharper", "InconsistentNaming")]
+ public enum Modifiers : uint
+ {
+ NONE = 0,
+ ALT = 1,
+ CTRL = 2,
+ SHIFT = 4,
+ WIN = 8,
+ NO_REPEAT = 0x4000
+ }
+
+ [SuppressMessage("ReSharper", "InconsistentNaming")]
+ private enum MapType : uint
+ {
+ MAPVK_VK_TO_VSC =
+ 0, //The uCode parameter is a virtual-key code and is translated into a scan code. If it is a virtual-key code that does not distinguish between left- and right-hand keys, the left-hand scan code is returned. If there is no translation, the function returns 0.
+
+ MAPVK_VSC_TO_VK =
+ 1, //The uCode parameter is a scan code and is translated into a virtual-key code that does not distinguish between left- and right-hand keys. If there is no translation, the function returns 0.
+
+ MAPVK_VK_TO_CHAR =
+ 2, //The uCode parameter is a virtual-key code and is translated into an unshifted character value in the low order word of the return value. Dead keys (diacritics) are indicated by setting the top bit of the return value. If there is no translation, the function returns 0.
+
+ MAPVK_VSC_TO_VK_EX =
+ 3, //The uCode parameter is a scan code and is translated into a virtual-key code that distinguishes between left- and right-hand keys. If there is no translation, the function returns 0.
+
+ MAPVK_VK_TO_VSC_EX =
+ 4 //The uCode parameter is a virtual-key code and is translated into a scan code. If it is a virtual-key code that does not distinguish between left- and right-hand keys, the left-hand scan code is returned. If the scan code is an extended scan code, the high byte of the uCode value can contain either 0xe0 or 0xe1 to specify the extended scan code. If there is no translation, the function returns 0.
+ }
+
+ [DllImport("user32.dll", SetLastError = true)]
+ [return: MarshalAs(UnmanagedType.Bool)]
+ private static extern bool RegisterHotKey(IntPtr hWnd, int id, uint fsModifiers, uint virtualKeyCode);
+
+ [DllImport("user32.dll", SetLastError = true)]
+ [return: MarshalAs(UnmanagedType.Bool)]
+ private static extern bool UnregisterHotKey(IntPtr hWnd, int id);
+
+ [DllImport("user32.dll", SetLastError = true)]
+ private static extern uint MapVirtualKey(uint uCode, uint uMapType);
+
+ [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
+ private static extern int GetKeyNameText(uint lParam, [Out] StringBuilder lpString, int nSize);
+
+ // These variables store the current hotkey and modifier(s)
+ private Keys _hotkey = Keys.None;
+ private Keys _modifiers = Keys.None;
+
+ // ArrayLists used to enforce the use of proper modifiers.
+ // Shift+A isn't a valid hotkey, for instance, as it would screw up when the user is typing.
+ private readonly IList _needNonShiftModifier = new List();
+ private readonly IList _needNonAltGrModifier = new List();
+
+ private readonly ContextMenuStrip _dummy = new ContextMenuStrip();
+
+ ///
+ /// Used to make sure that there is no right-click menu available
+ ///
+ public override ContextMenuStrip ContextMenuStrip
+ {
+ get { return _dummy; }
+ set { base.ContextMenuStrip = _dummy; }
+ }
+
+ ///
+ /// Forces the control to be non-multiline
+ ///
+ public override bool Multiline
+ {
+ get { return base.Multiline; }
+ set
+ {
+ // Ignore what the user wants; force Multiline to false
+ base.Multiline = false;
+ }
+ }
+
+ ///
+ /// Creates a new HotkeyControl
+ ///
+ public HotkeyControl()
+ {
+ ContextMenuStrip = _dummy; // Disable right-clicking
+ Text = "None";
+
+ // Handle events that occurs when keys are pressed
+ KeyPress += HotkeyControl_KeyPress;
+ KeyUp += HotkeyControl_KeyUp;
+ KeyDown += HotkeyControl_KeyDown;
+
+ PopulateModifierLists();
+ }
+
+ ///
+ /// Populates the ArrayLists specifying disallowed hotkeys
+ /// such as Shift+A, Ctrl+Alt+4 (would produce a dollar sign) etc
+ ///
+ private void PopulateModifierLists()
+ {
+ // Shift + 0 - 9, A - Z
+ for (Keys k = Keys.D0; k <= Keys.Z; k++)
+ {
+ _needNonShiftModifier.Add((int) k);
+ }
+
+ // Shift + Numpad keys
+ for (Keys k = Keys.NumPad0; k <= Keys.NumPad9; k++)
+ {
+ _needNonShiftModifier.Add((int) k);
+ }
+
+ // Shift + Misc (,;<./ etc)
+ for (Keys k = Keys.Oem1; k <= Keys.OemBackslash; k++)
+ {
+ _needNonShiftModifier.Add((int) k);
+ }
+
+ // Shift + Space, PgUp, PgDn, End, Home
+ for (Keys k = Keys.Space; k <= Keys.Home; k++)
+ {
+ _needNonShiftModifier.Add((int) k);
+ }
+
+ // Misc keys that we can't loop through
+ _needNonShiftModifier.Add((int) Keys.Insert);
+ _needNonShiftModifier.Add((int) Keys.Help);
+ _needNonShiftModifier.Add((int) Keys.Multiply);
+ _needNonShiftModifier.Add((int) Keys.Add);
+ _needNonShiftModifier.Add((int) Keys.Subtract);
+ _needNonShiftModifier.Add((int) Keys.Divide);
+ _needNonShiftModifier.Add((int) Keys.Decimal);
+ _needNonShiftModifier.Add((int) Keys.Return);
+ _needNonShiftModifier.Add((int) Keys.Escape);
+ _needNonShiftModifier.Add((int) Keys.NumLock);
+
+ // Ctrl+Alt + 0 - 9
+ for (Keys k = Keys.D0; k <= Keys.D9; k++)
+ {
+ _needNonAltGrModifier.Add((int) k);
+ }
+ }
+
+ ///
+ /// Resets this hotkey control to None
+ ///
+ public new void Clear()
+ {
+ Hotkey = Keys.None;
+ HotkeyModifiers = Keys.None;
+ }
+
+ ///
+ /// Fires when a key is pushed down. Here, we'll want to update the text in the box
+ /// to notify the user what combination is currently pressed.
+ ///
+ private void HotkeyControl_KeyDown(object sender, KeyEventArgs e)
+ {
+ // Clear the current hotkey
+ if (e.KeyCode == Keys.Back || e.KeyCode == Keys.Delete)
+ {
+ ResetHotkey();
+ }
+ else
+ {
+ _modifiers = e.Modifiers;
+ _hotkey = e.KeyCode;
+ Redraw();
+ }
+ }
+
+ ///
+ /// Fires when all keys are released. If the current hotkey isn't valid, reset it.
+ /// Otherwise, do nothing and keep the text and hotkey as it was.
+ ///
+ private void HotkeyControl_KeyUp(object sender, KeyEventArgs e)
+ {
+ // Somehow the PrintScreen only comes as a keyup, therefore we handle it here.
+ if (e.KeyCode == Keys.PrintScreen)
+ {
+ _modifiers = e.Modifiers;
+ _hotkey = e.KeyCode;
+ Redraw();
+ }
+
+ if (_hotkey == Keys.None && ModifierKeys == Keys.None)
+ {
+ ResetHotkey();
+ }
+ }
+
+ ///
+ /// Prevents the letter/whatever entered to show up in the TextBox
+ /// Without this, a "A" key press would appear as "aControl, Alt + A"
+ ///
+ private void HotkeyControl_KeyPress(object sender, KeyPressEventArgs e)
+ {
+ e.Handled = true;
+ }
+
+ ///
+ /// Handles some misc keys, such as Ctrl+Delete and Shift+Insert
+ ///
+ protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
+ {
+ if (keyData == Keys.Delete || keyData == (Keys.Control | Keys.Delete))
+ {
+ ResetHotkey();
+ return true;
+ }
+
+ // Paste
+ if (keyData == (Keys.Shift | Keys.Insert))
+ {
+ return true; // Don't allow
+ }
+
+ // Allow the rest
+ return base.ProcessCmdKey(ref msg, keyData);
+ }
+
+ ///
+ /// Clears the current hotkey and resets the TextBox
+ ///
+ public void ResetHotkey()
+ {
+ _hotkey = Keys.None;
+ _modifiers = Keys.None;
+ Redraw();
+ }
+
+ ///
+ /// Used to get/set the hotkey (e.g. Keys.A)
+ ///
+ public Keys Hotkey
+ {
+ get { return _hotkey; }
+ set
+ {
+ _hotkey = value;
+ Redraw(true);
+ }
+ }
+
+ ///
+ /// Used to get/set the hotkey (e.g. Keys.A)
+ ///
+ public void SetHotkey(string hotkey)
+ {
+ _hotkey = HotkeyFromString(hotkey);
+ _modifiers = HotkeyModifiersFromString(hotkey);
+ Redraw(true);
+ }
+
+ ///
+ /// Used to get/set the modifier keys (e.g. Keys.Alt | Keys.Control)
+ ///
+ public Keys HotkeyModifiers
+ {
+ get { return _modifiers; }
+ set
+ {
+ _modifiers = value;
+ Redraw(true);
+ }
+ }
+
+ ///
+ /// Redraws the TextBox when necessary.
+ ///
+ /// Specifies whether this function was called by the Hotkey/HotkeyModifiers properties or by the user.
+ private void Redraw(bool bCalledProgramatically = false)
+ {
+ // No hotkey set
+ if (_hotkey == Keys.None)
+ {
+ Text = string.Empty;
+ return;
+ }
+
+ // LWin/RWin doesn't work as hotkeys (neither do they work as modifier keys in .NET 2.0)
+ if (_hotkey == Keys.LWin || _hotkey == Keys.RWin)
+ {
+ Text = string.Empty;
+ return;
+ }
+
+ // Only validate input if it comes from the user
+ if (bCalledProgramatically == false)
+ {
+ // No modifier or shift only, AND a hotkey that needs another modifier
+ if ((_modifiers == Keys.Shift || _modifiers == Keys.None) && _needNonShiftModifier.Contains((int) _hotkey))
+ {
+ if (_modifiers == Keys.None)
+ {
+ // Set Ctrl+Alt as the modifier unless Ctrl+Alt+ won't work...
+ if (_needNonAltGrModifier.Contains((int) _hotkey) == false)
+ {
+ _modifiers = Keys.Alt | Keys.Control;
+ }
+ else
+ {
+ // ... in that case, use Shift+Alt instead.
+ _modifiers = Keys.Alt | Keys.Shift;
+ }
+ }
+ else
+ {
+ // User pressed Shift and an invalid key (e.g. a letter or a number),
+ // that needs another set of modifier keys
+ _hotkey = Keys.None;
+ Text = string.Empty;
+ return;
+ }
+ }
+
+ // Check all Ctrl+Alt keys
+ if ((_modifiers == (Keys.Alt | Keys.Control)) && _needNonAltGrModifier.Contains((int) _hotkey))
+ {
+ // Ctrl+Alt+4 etc won't work; reset hotkey and tell the user
+ _hotkey = Keys.None;
+ Text = string.Empty;
+ return;
+ }
+ }
+
+ // I have no idea why this is needed, but it is. Without this code, pressing only Ctrl
+ // will show up as "Control + ControlKey", etc.
+ if (_hotkey == Keys.Menu /* Alt */ || _hotkey == Keys.ShiftKey || _hotkey == Keys.ControlKey)
+ {
+ _hotkey = Keys.None;
+ }
+
+ Text = HotkeyToLocalizedString(_modifiers, _hotkey);
+ }
+
+ public override string ToString()
+ {
+ return HotkeyToString(HotkeyModifiers, Hotkey);
+ }
+
+ public static string GetLocalizedHotkeyStringFromString(string hotkeyString)
+ {
+ Keys virtualKeyCode = HotkeyFromString(hotkeyString);
+ Keys modifiers = HotkeyModifiersFromString(hotkeyString);
+ return HotkeyToLocalizedString(modifiers, virtualKeyCode);
+ }
+
+ public static string HotkeyToString(Keys modifierKeyCode, Keys virtualKeyCode)
+ {
+ return HotkeyModifiersToString(modifierKeyCode) + virtualKeyCode;
+ }
+
+ public static string HotkeyModifiersToString(Keys modifierKeyCode)
+ {
+ StringBuilder hotkeyString = new StringBuilder();
+ if ((modifierKeyCode & Keys.Alt) > 0)
+ {
+ hotkeyString.Append("Alt").Append(" + ");
+ }
+
+ if ((modifierKeyCode & Keys.Control) > 0)
+ {
+ hotkeyString.Append("Ctrl").Append(" + ");
+ }
+
+ if ((modifierKeyCode & Keys.Shift) > 0)
+ {
+ hotkeyString.Append("Shift").Append(" + ");
+ }
+
+ if (modifierKeyCode == Keys.LWin || modifierKeyCode == Keys.RWin)
+ {
+ hotkeyString.Append("Win").Append(" + ");
+ }
+
+ return hotkeyString.ToString();
+ }
+
+
+ public static string HotkeyToLocalizedString(Keys modifierKeyCode, Keys virtualKeyCode)
+ {
+ return HotkeyModifiersToLocalizedString(modifierKeyCode) + GetKeyName(virtualKeyCode);
+ }
+
+ public static string HotkeyModifiersToLocalizedString(Keys modifierKeyCode)
+ {
+ StringBuilder hotkeyString = new StringBuilder();
+ if ((modifierKeyCode & Keys.Alt) > 0)
+ {
+ hotkeyString.Append(GetKeyName(Keys.Alt)).Append(" + ");
+ }
+
+ if ((modifierKeyCode & Keys.Control) > 0)
+ {
+ hotkeyString.Append(GetKeyName(Keys.Control)).Append(" + ");
+ }
+
+ if ((modifierKeyCode & Keys.Shift) > 0)
+ {
+ hotkeyString.Append(GetKeyName(Keys.Shift)).Append(" + ");
+ }
+
+ if (modifierKeyCode == Keys.LWin || modifierKeyCode == Keys.RWin)
+ {
+ hotkeyString.Append("Win").Append(" + ");
+ }
+
+ return hotkeyString.ToString();
+ }
+
+
+ public static Keys HotkeyModifiersFromString(string modifiersString)
+ {
+ Keys modifiers = Keys.None;
+ if (!string.IsNullOrEmpty(modifiersString))
+ {
+ if (modifiersString.ToLower().Contains("alt"))
+ {
+ modifiers |= Keys.Alt;
+ }
+
+ if (modifiersString.ToLower().Contains("ctrl"))
+ {
+ modifiers |= Keys.Control;
+ }
+
+ if (modifiersString.ToLower().Contains("shift"))
+ {
+ modifiers |= Keys.Shift;
+ }
+
+ if (modifiersString.ToLower().Contains("win"))
+ {
+ modifiers |= Keys.LWin;
+ }
+ }
+
+ return modifiers;
+ }
+
+ public static Keys HotkeyFromString(string hotkey)
+ {
+ Keys key = Keys.None;
+ if (!string.IsNullOrEmpty(hotkey))
+ {
+ if (hotkey.LastIndexOf('+') > 0)
+ {
+ hotkey = hotkey.Remove(0, hotkey.LastIndexOf('+') + 1).Trim();
+ }
+
+ key = (Keys) Enum.Parse(typeof(Keys), hotkey);
+ }
+
+ return key;
+ }
+
+ public static void RegisterHotkeyHwnd(IntPtr hWnd)
+ {
+ _hotkeyHwnd = hWnd;
+ }
+
+ ///
+ /// Register a hotkey
+ ///
+ /// The modifier, e.g.: Modifiers.CTRL, Modifiers.NONE or Modifiers.ALT
+ /// The virtual key code
+ /// A HotKeyHandler, this will be called to handle the hotkey press
+ /// the hotkey number, -1 if failed
+ public static int RegisterHotKey(Keys modifierKeyCode, Keys virtualKeyCode, HotKeyHandler handler)
+ {
+ if (virtualKeyCode == Keys.None)
+ {
+ Log.Warn("Trying to register a Keys.none hotkey, ignoring");
+ return 0;
+ }
+
+ // Convert Modifiers to fit HKM_SETHOTKEY
+ uint modifiers = 0;
+ if ((modifierKeyCode & Keys.Alt) > 0)
+ {
+ modifiers |= (uint) Modifiers.ALT;
+ }
+
+ if ((modifierKeyCode & Keys.Control) > 0)
+ {
+ modifiers |= (uint) Modifiers.CTRL;
+ }
+
+ if ((modifierKeyCode & Keys.Shift) > 0)
+ {
+ modifiers |= (uint) Modifiers.SHIFT;
+ }
+
+ if (modifierKeyCode == Keys.LWin || modifierKeyCode == Keys.RWin)
+ {
+ modifiers |= (uint) Modifiers.WIN;
+ }
+
+ // Disable repeating hotkey for Windows 7 and beyond, as described in #1559
+ if (IsWindows7OrOlder)
+ {
+ modifiers |= (uint) Modifiers.NO_REPEAT;
+ }
+
+ if (RegisterHotKey(_hotkeyHwnd, _hotKeyCounter, modifiers, (uint) virtualKeyCode))
+ {
+ KeyHandlers.Add(_hotKeyCounter, handler);
+ return _hotKeyCounter++;
+ }
+
+ Log.Warn($"Couldn't register hotkey modifier {modifierKeyCode} virtualKeyCode {virtualKeyCode}");
+ return -1;
+ }
+
+ public static void UnregisterHotkeys()
+ {
+ foreach (int hotkey in KeyHandlers.Keys)
+ {
+ UnregisterHotKey(_hotkeyHwnd, hotkey);
+ }
+
+ // Remove all key handlers
+ KeyHandlers.Clear();
+ }
+
+ ///
+ /// Handle WndProc messages for the hotkey
+ ///
+ ///
+ /// true if the message was handled
+ public static bool HandleMessages(ref Message m)
+ {
+ if (m.Msg != WM_HOTKEY)
+ {
+ return false;
+ }
+
+ // Call handler
+ if (!IsWindows7OrOlder && !EventDelay.Check())
+ {
+ return true;
+ }
+
+ if (KeyHandlers.TryGetValue((int) m.WParam, out var handler))
+ {
+ handler();
+ }
+
+ return true;
+ }
+
+ public static string GetKeyName(Keys givenKey)
+ {
+ StringBuilder keyName = new StringBuilder();
+ const uint numpad = 55;
+
+ Keys virtualKey = givenKey;
+ string keyString;
+ // Make VC's to real keys
+ switch (virtualKey)
+ {
+ case Keys.Alt:
+ virtualKey = Keys.LMenu;
+ break;
+ case Keys.Control:
+ virtualKey = Keys.ControlKey;
+ break;
+ case Keys.Shift:
+ virtualKey = Keys.LShiftKey;
+ break;
+ case Keys.Multiply:
+ GetKeyNameText(numpad << 16, keyName, 100);
+ keyString = keyName.ToString().Replace("*", string.Empty).Trim().ToLower();
+ if (keyString.IndexOf("(", StringComparison.Ordinal) >= 0)
+ {
+ return "* " + keyString;
+ }
+
+ keyString = keyString.Substring(0, 1).ToUpper() + keyString.Substring(1).ToLower();
+ return keyString + " *";
+ case Keys.Divide:
+ GetKeyNameText(numpad << 16, keyName, 100);
+ keyString = keyName.ToString().Replace("*", string.Empty).Trim().ToLower();
+ if (keyString.IndexOf("(", StringComparison.Ordinal) >= 0)
+ {
+ return "/ " + keyString;
+ }
+
+ keyString = keyString.Substring(0, 1).ToUpper() + keyString.Substring(1).ToLower();
+ return keyString + " /";
+ }
+
+ uint scanCode = MapVirtualKey((uint) virtualKey, (uint) MapType.MAPVK_VK_TO_VSC);
+
+ // because MapVirtualKey strips the extended bit for some keys
+ switch (virtualKey)
+ {
+ case Keys.Left:
+ case Keys.Up:
+ case Keys.Right:
+ case Keys.Down: // arrow keys
+ case Keys.Prior:
+ case Keys.Next: // page up and page down
+ case Keys.End:
+ case Keys.Home:
+ case Keys.Insert:
+ case Keys.Delete:
+ case Keys.NumLock:
+ Log.Debug("Modifying Extended bit");
+ scanCode |= 0x100; // set extended bit
+ break;
+ case Keys.PrintScreen: // PrintScreen
+ scanCode = 311;
+ break;
+ case Keys.Pause: // PrintScreen
+ scanCode = 69;
+ break;
+ }
+
+ scanCode |= 0x200;
+ if (GetKeyNameText(scanCode << 16, keyName, 100) != 0)
+ {
+ string visibleName = keyName.ToString();
+ if (visibleName.Length > 1)
+ {
+ visibleName = visibleName.Substring(0, 1) + visibleName.Substring(1).ToLower();
+ }
+
+ return visibleName;
+ }
+
+ return givenKey.ToString();
+ }
+ }
}
\ No newline at end of file
diff --git a/src/GreenshotPlugin/Controls/IGreenshotConfigBindable.cs b/src/Greenshot.Base/Controls/IGreenshotConfigBindable.cs
similarity index 94%
rename from src/GreenshotPlugin/Controls/IGreenshotConfigBindable.cs
rename to src/Greenshot.Base/Controls/IGreenshotConfigBindable.cs
index e02358718..7a4865031 100644
--- a/src/GreenshotPlugin/Controls/IGreenshotConfigBindable.cs
+++ b/src/Greenshot.Base/Controls/IGreenshotConfigBindable.cs
@@ -1,36 +1,36 @@
-/*
- * Greenshot - a free and open source screenshot tool
- * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
- *
- * For more information see: http://getgreenshot.org/
- * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 1 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-namespace GreenshotPlugin.Controls
-{
- public interface IGreenshotConfigBindable
- {
- ///
- /// The class where the property-value is stored
- ///
- string SectionName { get; set; }
-
- ///
- /// Path to the property value which will be mapped with this control
- ///
- string PropertyName { get; set; }
- }
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+namespace Greenshot.Base.Controls
+{
+ public interface IGreenshotConfigBindable
+ {
+ ///
+ /// The class where the property-value is stored
+ ///
+ string SectionName { get; set; }
+
+ ///
+ /// Path to the property value which will be mapped with this control
+ ///
+ string PropertyName { get; set; }
+ }
}
\ No newline at end of file
diff --git a/src/GreenshotPlugin/Controls/IGreenshotLanguageBindable.cs b/src/Greenshot.Base/Controls/IGreenshotLanguageBindable.cs
similarity index 94%
rename from src/GreenshotPlugin/Controls/IGreenshotLanguageBindable.cs
rename to src/Greenshot.Base/Controls/IGreenshotLanguageBindable.cs
index c83c50d57..2a06d4f7e 100644
--- a/src/GreenshotPlugin/Controls/IGreenshotLanguageBindable.cs
+++ b/src/Greenshot.Base/Controls/IGreenshotLanguageBindable.cs
@@ -1,34 +1,34 @@
-/*
- * Greenshot - a free and open source screenshot tool
- * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
- *
- * For more information see: http://getgreenshot.org/
- * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 1 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-namespace GreenshotPlugin.Controls
-{
- ///
- /// This interface describes the designer fields that need to be implemented for Greenshot controls
- ///
- public interface IGreenshotLanguageBindable
- {
- ///
- /// Language key to use to fill the Text value with
- ///
- string LanguageKey { get; set; }
- }
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+namespace Greenshot.Base.Controls
+{
+ ///
+ /// This interface describes the designer fields that need to be implemented for Greenshot controls
+ ///
+ public interface IGreenshotLanguageBindable
+ {
+ ///
+ /// Language key to use to fill the Text value with
+ ///
+ string LanguageKey { get; set; }
+ }
}
\ No newline at end of file
diff --git a/src/GreenshotPlugin/Controls/OAuthLoginForm.Designer.cs b/src/Greenshot.Base/Controls/OAuthLoginForm.Designer.cs
similarity index 96%
rename from src/GreenshotPlugin/Controls/OAuthLoginForm.Designer.cs
rename to src/Greenshot.Base/Controls/OAuthLoginForm.Designer.cs
index abf92060c..f930762af 100644
--- a/src/GreenshotPlugin/Controls/OAuthLoginForm.Designer.cs
+++ b/src/Greenshot.Base/Controls/OAuthLoginForm.Designer.cs
@@ -1,92 +1,92 @@
-/*
- * Greenshot - a free and open source screenshot tool
- * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
- *
- * For more information see: http://getgreenshot.org/
- * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 1 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-namespace GreenshotPlugin.Controls {
- partial class OAuthLoginForm {
- ///
- /// Required designer variable.
- ///
- private System.ComponentModel.IContainer components = null;
-
- ///
- /// Clean up any resources being used.
- ///
- /// true if managed resources should be disposed; otherwise, false.
- protected override void Dispose(bool disposing)
- {
- if (disposing && (components != null)) {
- components.Dispose();
- }
- base.Dispose(disposing);
- }
-
- #region Windows Form Designer generated code
-
- ///
- /// Required method for Designer support - do not modify
- /// the contents of this method with the code editor.
- ///
- private void InitializeComponent() {
- this._addressTextBox = new System.Windows.Forms.TextBox();
- this._browser = new ExtendedWebBrowser();
- this.SuspendLayout();
- //
- // _addressTextBox
- //
- this._addressTextBox.Cursor = System.Windows.Forms.Cursors.Arrow;
- this._addressTextBox.Dock = System.Windows.Forms.DockStyle.Top;
- this._addressTextBox.Enabled = false;
- this._addressTextBox.Location = new System.Drawing.Point(0, 0);
- this._addressTextBox.Name = "addressTextBox";
- this._addressTextBox.Size = new System.Drawing.Size(595, 20);
- this._addressTextBox.TabIndex = 3;
- this._addressTextBox.TabStop = false;
- //
- // _browser
- //
- this._browser.Dock = System.Windows.Forms.DockStyle.Fill;
- this._browser.Location = new System.Drawing.Point(0, 20);
- this._browser.MinimumSize = new System.Drawing.Size(100, 100);
- this._browser.Name = "browser";
- this._browser.Size = new System.Drawing.Size(595, 295);
- this._browser.TabIndex = 4;
- //
- // OAuthLoginForm
- //
- this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F);
- this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi;
- this.ClientSize = new System.Drawing.Size(595, 315);
- this.Controls.Add(this._browser);
- this.Controls.Add(this._addressTextBox);
- this.MaximizeBox = false;
- this.MinimizeBox = false;
- this.Name = "OAuthLoginForm";
- this.ResumeLayout(false);
- this.PerformLayout();
-
- }
-
- #endregion
-
- private System.Windows.Forms.TextBox _addressTextBox;
- private ExtendedWebBrowser _browser;
-
- }
-}
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+namespace Greenshot.Base.Controls {
+ partial class OAuthLoginForm {
+ ///
+ /// Required designer variable.
+ ///
+ private System.ComponentModel.IContainer components = null;
+
+ ///
+ /// Clean up any resources being used.
+ ///
+ /// true if managed resources should be disposed; otherwise, false.
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing && (components != null)) {
+ components.Dispose();
+ }
+ base.Dispose(disposing);
+ }
+
+ #region Windows Form Designer generated code
+
+ ///
+ /// Required method for Designer support - do not modify
+ /// the contents of this method with the code editor.
+ ///
+ private void InitializeComponent() {
+ this._addressTextBox = new System.Windows.Forms.TextBox();
+ this._browser = new ExtendedWebBrowser();
+ this.SuspendLayout();
+ //
+ // _addressTextBox
+ //
+ this._addressTextBox.Cursor = System.Windows.Forms.Cursors.Arrow;
+ this._addressTextBox.Dock = System.Windows.Forms.DockStyle.Top;
+ this._addressTextBox.Enabled = false;
+ this._addressTextBox.Location = new System.Drawing.Point(0, 0);
+ this._addressTextBox.Name = "addressTextBox";
+ this._addressTextBox.Size = new System.Drawing.Size(595, 20);
+ this._addressTextBox.TabIndex = 3;
+ this._addressTextBox.TabStop = false;
+ //
+ // _browser
+ //
+ this._browser.Dock = System.Windows.Forms.DockStyle.Fill;
+ this._browser.Location = new System.Drawing.Point(0, 20);
+ this._browser.MinimumSize = new System.Drawing.Size(100, 100);
+ this._browser.Name = "browser";
+ this._browser.Size = new System.Drawing.Size(595, 295);
+ this._browser.TabIndex = 4;
+ //
+ // OAuthLoginForm
+ //
+ this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi;
+ this.ClientSize = new System.Drawing.Size(595, 315);
+ this.Controls.Add(this._browser);
+ this.Controls.Add(this._addressTextBox);
+ this.MaximizeBox = false;
+ this.MinimizeBox = false;
+ this.Name = "OAuthLoginForm";
+ this.ResumeLayout(false);
+ this.PerformLayout();
+
+ }
+
+ #endregion
+
+ private System.Windows.Forms.TextBox _addressTextBox;
+ private ExtendedWebBrowser _browser;
+
+ }
+}
diff --git a/src/GreenshotPlugin/Controls/OAuthLoginForm.cs b/src/Greenshot.Base/Controls/OAuthLoginForm.cs
similarity index 95%
rename from src/GreenshotPlugin/Controls/OAuthLoginForm.cs
rename to src/Greenshot.Base/Controls/OAuthLoginForm.cs
index 59a1ea9ca..4cbc1dc9c 100644
--- a/src/GreenshotPlugin/Controls/OAuthLoginForm.cs
+++ b/src/Greenshot.Base/Controls/OAuthLoginForm.cs
@@ -1,121 +1,121 @@
-/*
- * Greenshot - a free and open source screenshot tool
- * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
- *
- * For more information see: http://getgreenshot.org/
- * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 1 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-using System;
-using System.Collections.Generic;
-using System.Drawing;
-using System.Windows.Forms;
-using GreenshotPlugin.Core;
-using log4net;
-
-namespace GreenshotPlugin.Controls
-{
- ///
- /// The OAuthLoginForm is used to allow the user to authorize Greenshot with an "Oauth" application
- ///
- public sealed partial class OAuthLoginForm : Form
- {
- private static readonly ILog LOG = LogManager.GetLogger(typeof(OAuthLoginForm));
- private readonly string _callbackUrl;
-
- public IDictionary CallbackParameters { get; private set; }
-
- public bool IsOk => DialogResult == DialogResult.OK;
-
- public OAuthLoginForm(string browserTitle, Size size, string authorizationLink, string callbackUrl)
- {
- // Make sure Greenshot uses the correct browser version
- IEHelper.FixBrowserVersion(false);
-
- _callbackUrl = callbackUrl;
- // Fix for BUG-2071
- if (callbackUrl.EndsWith("/"))
- {
- _callbackUrl = callbackUrl.Substring(0, callbackUrl.Length - 1);
- }
-
- InitializeComponent();
- ClientSize = size;
- Icon = GreenshotResources.GetGreenshotIcon();
- Text = browserTitle;
- _addressTextBox.Text = authorizationLink;
-
- // The script errors are suppressed by using the ExtendedWebBrowser
- _browser.ScriptErrorsSuppressed = false;
- _browser.DocumentCompleted += Browser_DocumentCompleted;
- _browser.Navigated += Browser_Navigated;
- _browser.Navigating += Browser_Navigating;
- _browser.Navigate(new Uri(authorizationLink));
- }
-
- ///
- /// Make sure the form is visible
- ///
- /// EventArgs
- protected override void OnShown(EventArgs e)
- {
- base.OnShown(e);
- WindowDetails.ToForeground(Handle);
- }
-
- private void Browser_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
- {
- LOG.DebugFormat("document completed with url: {0}", _browser.Url);
- CheckUrl();
- }
-
- private void Browser_Navigating(object sender, WebBrowserNavigatingEventArgs e)
- {
- LOG.DebugFormat("Navigating to url: {0}", _browser.Url);
- _addressTextBox.Text = e.Url.ToString();
- }
-
- private void Browser_Navigated(object sender, WebBrowserNavigatedEventArgs e)
- {
- LOG.DebugFormat("Navigated to url: {0}", _browser.Url);
- CheckUrl();
- }
-
- private void CheckUrl()
- {
- if (_browser.Url.ToString().StartsWith(_callbackUrl))
- {
- var correctedUri = new Uri(_browser.Url.AbsoluteUri.Replace("#", "&"));
-
- string queryParams = correctedUri.Query;
- if (queryParams.Length > 0)
- {
- queryParams = NetworkHelper.UrlDecode(queryParams);
- //Store the Token and Token Secret
- CallbackParameters = NetworkHelper.ParseQueryString(queryParams);
- }
-
- DialogResult = DialogResult.OK;
- }
- }
-
- private void AddressTextBox_KeyPress(object sender, KeyPressEventArgs e)
- {
- //Cancel the key press so the user can't enter a new url
- e.Handled = true;
- }
- }
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+using System;
+using System.Collections.Generic;
+using System.Drawing;
+using System.Windows.Forms;
+using Greenshot.Base.Core;
+using log4net;
+
+namespace Greenshot.Base.Controls
+{
+ ///
+ /// The OAuthLoginForm is used to allow the user to authorize Greenshot with an "Oauth" application
+ ///
+ public sealed partial class OAuthLoginForm : Form
+ {
+ private static readonly ILog LOG = LogManager.GetLogger(typeof(OAuthLoginForm));
+ private readonly string _callbackUrl;
+
+ public IDictionary CallbackParameters { get; private set; }
+
+ public bool IsOk => DialogResult == DialogResult.OK;
+
+ public OAuthLoginForm(string browserTitle, Size size, string authorizationLink, string callbackUrl)
+ {
+ // Make sure Greenshot uses the correct browser version
+ IEHelper.FixBrowserVersion(false);
+
+ _callbackUrl = callbackUrl;
+ // Fix for BUG-2071
+ if (callbackUrl.EndsWith("/"))
+ {
+ _callbackUrl = callbackUrl.Substring(0, callbackUrl.Length - 1);
+ }
+
+ InitializeComponent();
+ ClientSize = size;
+ Icon = GreenshotResources.GetGreenshotIcon();
+ Text = browserTitle;
+ _addressTextBox.Text = authorizationLink;
+
+ // The script errors are suppressed by using the ExtendedWebBrowser
+ _browser.ScriptErrorsSuppressed = false;
+ _browser.DocumentCompleted += Browser_DocumentCompleted;
+ _browser.Navigated += Browser_Navigated;
+ _browser.Navigating += Browser_Navigating;
+ _browser.Navigate(new Uri(authorizationLink));
+ }
+
+ ///
+ /// Make sure the form is visible
+ ///
+ /// EventArgs
+ protected override void OnShown(EventArgs e)
+ {
+ base.OnShown(e);
+ WindowDetails.ToForeground(Handle);
+ }
+
+ private void Browser_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
+ {
+ LOG.DebugFormat("document completed with url: {0}", _browser.Url);
+ CheckUrl();
+ }
+
+ private void Browser_Navigating(object sender, WebBrowserNavigatingEventArgs e)
+ {
+ LOG.DebugFormat("Navigating to url: {0}", _browser.Url);
+ _addressTextBox.Text = e.Url.ToString();
+ }
+
+ private void Browser_Navigated(object sender, WebBrowserNavigatedEventArgs e)
+ {
+ LOG.DebugFormat("Navigated to url: {0}", _browser.Url);
+ CheckUrl();
+ }
+
+ private void CheckUrl()
+ {
+ if (_browser.Url.ToString().StartsWith(_callbackUrl))
+ {
+ var correctedUri = new Uri(_browser.Url.AbsoluteUri.Replace("#", "&"));
+
+ string queryParams = correctedUri.Query;
+ if (queryParams.Length > 0)
+ {
+ queryParams = NetworkHelper.UrlDecode(queryParams);
+ //Store the Token and Token Secret
+ CallbackParameters = NetworkHelper.ParseQueryString(queryParams);
+ }
+
+ DialogResult = DialogResult.OK;
+ }
+ }
+
+ private void AddressTextBox_KeyPress(object sender, KeyPressEventArgs e)
+ {
+ //Cancel the key press so the user can't enter a new url
+ e.Handled = true;
+ }
+ }
}
\ No newline at end of file
diff --git a/src/GreenshotPlugin/Controls/PleaseWaitForm.Designer.cs b/src/Greenshot.Base/Controls/PleaseWaitForm.Designer.cs
similarity index 96%
rename from src/GreenshotPlugin/Controls/PleaseWaitForm.Designer.cs
rename to src/Greenshot.Base/Controls/PleaseWaitForm.Designer.cs
index a3ab2832b..3083b7bf2 100644
--- a/src/GreenshotPlugin/Controls/PleaseWaitForm.Designer.cs
+++ b/src/Greenshot.Base/Controls/PleaseWaitForm.Designer.cs
@@ -1,99 +1,99 @@
-/*
- * Greenshot - a free and open source screenshot tool
- * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
- *
- * For more information see: http://getgreenshot.org/
- * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 1 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-namespace GreenshotPlugin.Controls {
- partial class PleaseWaitForm {
- ///
- /// Designer variable used to keep track of non-visual components.
- ///
- private System.ComponentModel.IContainer components = null;
-
- ///
- /// Disposes resources used by the form.
- ///
- /// true if managed resources should be disposed; otherwise, false.
- protected override void Dispose(bool disposing) {
- if (disposing) {
- if (components != null) {
- components.Dispose();
- }
- }
- base.Dispose(disposing);
- }
-
- ///
- /// This method is required for Windows Forms designer support.
- /// Do not change the method contents inside the source code editor. The Forms designer might
- /// not be able to load this method if it was changed manually.
- ///
- private void InitializeComponent() {
- this.label_pleasewait = new System.Windows.Forms.Label();
- this.cancelButton = new System.Windows.Forms.Button();
- this.SuspendLayout();
- //
- // label_pleasewait
- //
- this.label_pleasewait.Dock = System.Windows.Forms.DockStyle.Fill;
- this.label_pleasewait.Location = new System.Drawing.Point(0, 0);
- this.label_pleasewait.Name = "label_pleasewait";
- this.label_pleasewait.Padding = new System.Windows.Forms.Padding(10);
- this.label_pleasewait.Size = new System.Drawing.Size(90, 33);
- this.label_pleasewait.TabIndex = 0;
- this.label_pleasewait.Text = "Please wait...";
- this.label_pleasewait.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
- this.label_pleasewait.UseWaitCursor = true;
- //
- // cancelButton
- //
- this.cancelButton.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)
- | System.Windows.Forms.AnchorStyles.Right)));
- this.cancelButton.DialogResult = System.Windows.Forms.DialogResult.Cancel;
- this.cancelButton.Location = new System.Drawing.Point(38, 41);
- this.cancelButton.Name = "cancelButton";
- this.cancelButton.Size = new System.Drawing.Size(94, 23);
- this.cancelButton.TabIndex = 1;
- this.cancelButton.Text = "Cancel";
- this.cancelButton.UseVisualStyleBackColor = true;
- this.cancelButton.UseWaitCursor = true;
- this.cancelButton.Click += new System.EventHandler(this.CancelButtonClick);
- //
- // PleaseWaitForm
- //
- this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F);
- this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi;
- this.CancelButton = this.cancelButton;
- this.ClientSize = new System.Drawing.Size(169, 76);
- this.Controls.Add(this.cancelButton);
- this.Controls.Add(this.label_pleasewait);
- this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
- this.MaximizeBox = false;
- this.MinimizeBox = false;
- this.Name = "PleaseWaitForm";
- this.SizeGripStyle = System.Windows.Forms.SizeGripStyle.Hide;
- this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
- this.Text = "Greenshot";
- this.UseWaitCursor = true;
- this.ResumeLayout(false);
- this.PerformLayout();
- }
- private System.Windows.Forms.Button cancelButton;
- private System.Windows.Forms.Label label_pleasewait;
- }
-}
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+namespace Greenshot.Base.Controls {
+ partial class PleaseWaitForm {
+ ///
+ /// Designer variable used to keep track of non-visual components.
+ ///
+ private System.ComponentModel.IContainer components = null;
+
+ ///
+ /// Disposes resources used by the form.
+ ///
+ /// true if managed resources should be disposed; otherwise, false.
+ protected override void Dispose(bool disposing) {
+ if (disposing) {
+ if (components != null) {
+ components.Dispose();
+ }
+ }
+ base.Dispose(disposing);
+ }
+
+ ///
+ /// This method is required for Windows Forms designer support.
+ /// Do not change the method contents inside the source code editor. The Forms designer might
+ /// not be able to load this method if it was changed manually.
+ ///
+ private void InitializeComponent() {
+ this.label_pleasewait = new System.Windows.Forms.Label();
+ this.cancelButton = new System.Windows.Forms.Button();
+ this.SuspendLayout();
+ //
+ // label_pleasewait
+ //
+ this.label_pleasewait.Dock = System.Windows.Forms.DockStyle.Fill;
+ this.label_pleasewait.Location = new System.Drawing.Point(0, 0);
+ this.label_pleasewait.Name = "label_pleasewait";
+ this.label_pleasewait.Padding = new System.Windows.Forms.Padding(10);
+ this.label_pleasewait.Size = new System.Drawing.Size(90, 33);
+ this.label_pleasewait.TabIndex = 0;
+ this.label_pleasewait.Text = "Please wait...";
+ this.label_pleasewait.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
+ this.label_pleasewait.UseWaitCursor = true;
+ //
+ // cancelButton
+ //
+ this.cancelButton.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.cancelButton.DialogResult = System.Windows.Forms.DialogResult.Cancel;
+ this.cancelButton.Location = new System.Drawing.Point(38, 41);
+ this.cancelButton.Name = "cancelButton";
+ this.cancelButton.Size = new System.Drawing.Size(94, 23);
+ this.cancelButton.TabIndex = 1;
+ this.cancelButton.Text = "Cancel";
+ this.cancelButton.UseVisualStyleBackColor = true;
+ this.cancelButton.UseWaitCursor = true;
+ this.cancelButton.Click += new System.EventHandler(this.CancelButtonClick);
+ //
+ // PleaseWaitForm
+ //
+ this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi;
+ this.CancelButton = this.cancelButton;
+ this.ClientSize = new System.Drawing.Size(169, 76);
+ this.Controls.Add(this.cancelButton);
+ this.Controls.Add(this.label_pleasewait);
+ this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
+ this.MaximizeBox = false;
+ this.MinimizeBox = false;
+ this.Name = "PleaseWaitForm";
+ this.SizeGripStyle = System.Windows.Forms.SizeGripStyle.Hide;
+ this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
+ this.Text = "Greenshot";
+ this.UseWaitCursor = true;
+ this.ResumeLayout(false);
+ this.PerformLayout();
+ }
+ private System.Windows.Forms.Button cancelButton;
+ private System.Windows.Forms.Label label_pleasewait;
+ }
+}
diff --git a/src/GreenshotPlugin/Controls/PleaseWaitForm.cs b/src/Greenshot.Base/Controls/PleaseWaitForm.cs
similarity index 95%
rename from src/GreenshotPlugin/Controls/PleaseWaitForm.cs
rename to src/Greenshot.Base/Controls/PleaseWaitForm.cs
index 8d0ced972..e87ab38ef 100644
--- a/src/GreenshotPlugin/Controls/PleaseWaitForm.cs
+++ b/src/Greenshot.Base/Controls/PleaseWaitForm.cs
@@ -1,143 +1,143 @@
-/*
- * Greenshot - a free and open source screenshot tool
- * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
- *
- * For more information see: http://getgreenshot.org/
- * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 1 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-using System;
-using System.Windows.Forms;
-using System.Threading;
-using GreenshotPlugin.Core;
-using log4net;
-
-namespace GreenshotPlugin.Controls
-{
- ///
- /// Description of PleaseWaitForm.
- ///
- public partial class PleaseWaitForm : Form
- {
- private static readonly ILog LOG = LogManager.GetLogger(typeof(PleaseWaitForm));
- private Thread _waitFor;
- private string _title;
-
- public PleaseWaitForm()
- {
- //
- // The InitializeComponent() call is required for Windows Forms designer support.
- //
- InitializeComponent();
- Icon = GreenshotResources.GetGreenshotIcon();
- }
-
- ///
- /// Prevent the close-window button showing
- ///
- private const int CP_NOCLOSE_BUTTON = 0x200;
-
- protected override CreateParams CreateParams
- {
- get
- {
- CreateParams createParams = base.CreateParams;
- createParams.ClassStyle |= CP_NOCLOSE_BUTTON;
- return createParams;
- }
- }
-
- ///
- /// Show the "please wait" form, execute the code from the delegate and wait until execution finishes.
- /// The supplied delegate will be wrapped with a try/catch so this method can return any exception that was thrown.
- ///
- /// The title of the form (and Thread)
- /// The text in the form
- /// delegate { with your code }
- public void ShowAndWait(string title, string text, ThreadStart waitDelegate)
- {
- _title = title;
- Text = title;
- label_pleasewait.Text = text;
- cancelButton.Text = Language.GetString("CANCEL");
-
- // Make sure the form is shown.
- Show();
-
- // Variable to store the exception, if one is generated, from inside the thread.
- Exception threadException = null;
- try
- {
- // Wrap the passed delegate in a try/catch which makes it possible to save the exception
- _waitFor = new Thread(new ThreadStart(
- delegate
- {
- try
- {
- waitDelegate.Invoke();
- }
- catch (Exception ex)
- {
- LOG.Error("invoke error:", ex);
- threadException = ex;
- }
- })
- )
- {
- Name = title,
- IsBackground = true
- };
- _waitFor.SetApartmentState(ApartmentState.STA);
- _waitFor.Start();
-
- // Wait until finished
- while (!_waitFor.Join(TimeSpan.FromMilliseconds(100)))
- {
- Application.DoEvents();
- }
-
- LOG.DebugFormat("Finished {0}", title);
- }
- catch (Exception ex)
- {
- LOG.Error(ex);
- throw;
- }
- finally
- {
- Close();
- }
-
- // Check if an exception occured, if so throw it
- if (threadException != null)
- {
- throw threadException;
- }
- }
-
- ///
- /// Called if the cancel button is clicked, will use Thread.Abort()
- ///
- ///
- ///
- private void CancelButtonClick(object sender, EventArgs e)
- {
- LOG.DebugFormat("Cancel clicked on {0}", _title);
- cancelButton.Enabled = false;
- _waitFor.Abort();
- }
- }
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+using System;
+using System.Threading;
+using System.Windows.Forms;
+using Greenshot.Base.Core;
+using log4net;
+
+namespace Greenshot.Base.Controls
+{
+ ///
+ /// Description of PleaseWaitForm.
+ ///
+ public partial class PleaseWaitForm : Form
+ {
+ private static readonly ILog LOG = LogManager.GetLogger(typeof(PleaseWaitForm));
+ private Thread _waitFor;
+ private string _title;
+
+ public PleaseWaitForm()
+ {
+ //
+ // The InitializeComponent() call is required for Windows Forms designer support.
+ //
+ InitializeComponent();
+ Icon = GreenshotResources.GetGreenshotIcon();
+ }
+
+ ///
+ /// Prevent the close-window button showing
+ ///
+ private const int CP_NOCLOSE_BUTTON = 0x200;
+
+ protected override CreateParams CreateParams
+ {
+ get
+ {
+ CreateParams createParams = base.CreateParams;
+ createParams.ClassStyle |= CP_NOCLOSE_BUTTON;
+ return createParams;
+ }
+ }
+
+ ///
+ /// Show the "please wait" form, execute the code from the delegate and wait until execution finishes.
+ /// The supplied delegate will be wrapped with a try/catch so this method can return any exception that was thrown.
+ ///
+ /// The title of the form (and Thread)
+ /// The text in the form
+ /// delegate { with your code }
+ public void ShowAndWait(string title, string text, ThreadStart waitDelegate)
+ {
+ _title = title;
+ Text = title;
+ label_pleasewait.Text = text;
+ cancelButton.Text = Language.GetString("CANCEL");
+
+ // Make sure the form is shown.
+ Show();
+
+ // Variable to store the exception, if one is generated, from inside the thread.
+ Exception threadException = null;
+ try
+ {
+ // Wrap the passed delegate in a try/catch which makes it possible to save the exception
+ _waitFor = new Thread(new ThreadStart(
+ delegate
+ {
+ try
+ {
+ waitDelegate.Invoke();
+ }
+ catch (Exception ex)
+ {
+ LOG.Error("invoke error:", ex);
+ threadException = ex;
+ }
+ })
+ )
+ {
+ Name = title,
+ IsBackground = true
+ };
+ _waitFor.SetApartmentState(ApartmentState.STA);
+ _waitFor.Start();
+
+ // Wait until finished
+ while (!_waitFor.Join(TimeSpan.FromMilliseconds(100)))
+ {
+ Application.DoEvents();
+ }
+
+ LOG.DebugFormat("Finished {0}", title);
+ }
+ catch (Exception ex)
+ {
+ LOG.Error(ex);
+ throw;
+ }
+ finally
+ {
+ Close();
+ }
+
+ // Check if an exception occured, if so throw it
+ if (threadException != null)
+ {
+ throw threadException;
+ }
+ }
+
+ ///
+ /// Called if the cancel button is clicked, will use Thread.Abort()
+ ///
+ ///
+ ///
+ private void CancelButtonClick(object sender, EventArgs e)
+ {
+ LOG.DebugFormat("Cancel clicked on {0}", _title);
+ cancelButton.Enabled = false;
+ _waitFor.Abort();
+ }
+ }
}
\ No newline at end of file
diff --git a/src/GreenshotPlugin/Controls/QualityDialog.Designer.cs b/src/Greenshot.Base/Controls/QualityDialog.Designer.cs
similarity index 90%
rename from src/GreenshotPlugin/Controls/QualityDialog.Designer.cs
rename to src/Greenshot.Base/Controls/QualityDialog.Designer.cs
index 5f1c03fce..dd67f3358 100644
--- a/src/GreenshotPlugin/Controls/QualityDialog.Designer.cs
+++ b/src/Greenshot.Base/Controls/QualityDialog.Designer.cs
@@ -1,147 +1,147 @@
-/*
- * Greenshot - a free and open source screenshot tool
- * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
- *
- * For more information see: http://getgreenshot.org/
- * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 1 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-namespace GreenshotPlugin.Controls {
- partial class QualityDialog {
- ///
- /// Designer variable used to keep track of non-visual components.
- ///
- private System.ComponentModel.IContainer components = null;
-
- ///
- /// Disposes resources used by the form.
- ///
- /// true if managed resources should be disposed; otherwise, false.
- protected override void Dispose(bool disposing)
- {
- if (disposing) {
- if (components != null) {
- components.Dispose();
- }
- }
- base.Dispose(disposing);
- }
-
- ///
- /// This method is required for Windows Forms designer support.
- /// Do not change the method contents inside the source code editor. The Forms designer might
- /// not be able to load this method if it was changed manually.
- ///
- private void InitializeComponent()
- {
- this.label_choosejpegquality = new GreenshotPlugin.Controls.GreenshotLabel();
- this.textBoxJpegQuality = new System.Windows.Forms.TextBox();
- this.trackBarJpegQuality = new System.Windows.Forms.TrackBar();
- this.checkbox_dontaskagain = new GreenshotPlugin.Controls.GreenshotCheckBox();
- this.button_ok = new GreenshotPlugin.Controls.GreenshotButton();
- this.checkBox_reduceColors = new System.Windows.Forms.CheckBox();
- ((System.ComponentModel.ISupportInitialize)(this.trackBarJpegQuality)).BeginInit();
- this.SuspendLayout();
- //
- // label_choosejpegquality
- //
- this.label_choosejpegquality.Location = new System.Drawing.Point(12, 47);
- this.label_choosejpegquality.Name = "label_choosejpegquality";
- this.label_choosejpegquality.Size = new System.Drawing.Size(268, 19);
- this.label_choosejpegquality.TabIndex = 15;
- this.label_choosejpegquality.LanguageKey = "jpegqualitydialog_choosejpegquality";
- //
- // textBoxJpegQuality
- //
- this.textBoxJpegQuality.Location = new System.Drawing.Point(245, 69);
- this.textBoxJpegQuality.Name = "textBoxJpegQuality";
- this.textBoxJpegQuality.ReadOnly = true;
- this.textBoxJpegQuality.Size = new System.Drawing.Size(35, 20);
- this.textBoxJpegQuality.TabIndex = 4;
- this.textBoxJpegQuality.TextAlign = System.Windows.Forms.HorizontalAlignment.Right;
- //
- // trackBarJpegQuality
- //
- this.trackBarJpegQuality.LargeChange = 10;
- this.trackBarJpegQuality.Location = new System.Drawing.Point(12, 69);
- this.trackBarJpegQuality.Maximum = 100;
- this.trackBarJpegQuality.Name = "trackBarJpegQuality";
- this.trackBarJpegQuality.Size = new System.Drawing.Size(233, 45);
- this.trackBarJpegQuality.TabIndex = 3;
- this.trackBarJpegQuality.TickFrequency = 10;
- this.trackBarJpegQuality.Scroll += new System.EventHandler(this.TrackBarJpegQualityScroll);
- //
- // checkbox_dontaskagain
- //
- this.checkbox_dontaskagain.CheckAlign = System.Drawing.ContentAlignment.TopLeft;
- this.checkbox_dontaskagain.ImageAlign = System.Drawing.ContentAlignment.TopLeft;
- this.checkbox_dontaskagain.Location = new System.Drawing.Point(12, 106);
- this.checkbox_dontaskagain.Name = "checkbox_dontaskagain";
- this.checkbox_dontaskagain.LanguageKey = "qualitydialog_dontaskagain";
- this.checkbox_dontaskagain.Size = new System.Drawing.Size(268, 37);
- this.checkbox_dontaskagain.TabIndex = 5;
- this.checkbox_dontaskagain.TextAlign = System.Drawing.ContentAlignment.TopLeft;
- this.checkbox_dontaskagain.UseVisualStyleBackColor = true;
- //
- // button_ok
- //
- this.button_ok.DialogResult = System.Windows.Forms.DialogResult.Cancel;
- this.button_ok.Location = new System.Drawing.Point(205, 149);
- this.button_ok.Name = "button_ok";
- this.button_ok.Size = new System.Drawing.Size(75, 23);
- this.button_ok.TabIndex = 1;
- this.button_ok.LanguageKey = "OK";
- this.button_ok.UseVisualStyleBackColor = true;
- this.button_ok.Click += new System.EventHandler(this.Button_okClick);
- //
- // checkBox_reduceColors
- //
- this.checkBox_reduceColors.Location = new System.Drawing.Point(12, 11);
- this.checkBox_reduceColors.Name = "checkBox_reduceColors";
- this.checkBox_reduceColors.Size = new System.Drawing.Size(95, 17);
- this.checkBox_reduceColors.TabIndex = 2;
- this.checkBox_reduceColors.Text = "settings_reducecolors";
- this.checkBox_reduceColors.UseVisualStyleBackColor = true;
- //
- // QualityDialog
- //
- this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F);
- this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi;
- this.ClientSize = new System.Drawing.Size(299, 184);
- this.ControlBox = false;
- this.Controls.Add(this.checkBox_reduceColors);
- this.Controls.Add(this.button_ok);
- this.Controls.Add(this.checkbox_dontaskagain);
- this.Controls.Add(this.label_choosejpegquality);
- this.Controls.Add(this.textBoxJpegQuality);
- this.Controls.Add(this.trackBarJpegQuality);
- this.MaximizeBox = false;
- this.MinimizeBox = false;
- this.Name = "QualityDialog";
- this.SizeGripStyle = System.Windows.Forms.SizeGripStyle.Hide;
- this.LanguageKey = "qualitydialog_title";
- ((System.ComponentModel.ISupportInitialize)(this.trackBarJpegQuality)).EndInit();
- this.ResumeLayout(false);
- this.PerformLayout();
-
- }
- private GreenshotPlugin.Controls.GreenshotButton button_ok;
- private GreenshotPlugin.Controls.GreenshotCheckBox checkbox_dontaskagain;
- private System.Windows.Forms.TrackBar trackBarJpegQuality;
- private System.Windows.Forms.TextBox textBoxJpegQuality;
- private GreenshotPlugin.Controls.GreenshotLabel label_choosejpegquality;
- private System.Windows.Forms.CheckBox checkBox_reduceColors;
- }
-}
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+namespace Greenshot.Base.Controls {
+ partial class QualityDialog {
+ ///
+ /// Designer variable used to keep track of non-visual components.
+ ///
+ private System.ComponentModel.IContainer components = null;
+
+ ///
+ /// Disposes resources used by the form.
+ ///
+ /// true if managed resources should be disposed; otherwise, false.
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing) {
+ if (components != null) {
+ components.Dispose();
+ }
+ }
+ base.Dispose(disposing);
+ }
+
+ ///
+ /// This method is required for Windows Forms designer support.
+ /// Do not change the method contents inside the source code editor. The Forms designer might
+ /// not be able to load this method if it was changed manually.
+ ///
+ private void InitializeComponent()
+ {
+ this.label_choosejpegquality = new GreenshotLabel();
+ this.textBoxJpegQuality = new System.Windows.Forms.TextBox();
+ this.trackBarJpegQuality = new System.Windows.Forms.TrackBar();
+ this.checkbox_dontaskagain = new GreenshotCheckBox();
+ this.button_ok = new GreenshotButton();
+ this.checkBox_reduceColors = new System.Windows.Forms.CheckBox();
+ ((System.ComponentModel.ISupportInitialize)(this.trackBarJpegQuality)).BeginInit();
+ this.SuspendLayout();
+ //
+ // label_choosejpegquality
+ //
+ this.label_choosejpegquality.Location = new System.Drawing.Point(12, 47);
+ this.label_choosejpegquality.Name = "label_choosejpegquality";
+ this.label_choosejpegquality.Size = new System.Drawing.Size(268, 19);
+ this.label_choosejpegquality.TabIndex = 15;
+ this.label_choosejpegquality.LanguageKey = "jpegqualitydialog_choosejpegquality";
+ //
+ // textBoxJpegQuality
+ //
+ this.textBoxJpegQuality.Location = new System.Drawing.Point(245, 69);
+ this.textBoxJpegQuality.Name = "textBoxJpegQuality";
+ this.textBoxJpegQuality.ReadOnly = true;
+ this.textBoxJpegQuality.Size = new System.Drawing.Size(35, 20);
+ this.textBoxJpegQuality.TabIndex = 4;
+ this.textBoxJpegQuality.TextAlign = System.Windows.Forms.HorizontalAlignment.Right;
+ //
+ // trackBarJpegQuality
+ //
+ this.trackBarJpegQuality.LargeChange = 10;
+ this.trackBarJpegQuality.Location = new System.Drawing.Point(12, 69);
+ this.trackBarJpegQuality.Maximum = 100;
+ this.trackBarJpegQuality.Name = "trackBarJpegQuality";
+ this.trackBarJpegQuality.Size = new System.Drawing.Size(233, 45);
+ this.trackBarJpegQuality.TabIndex = 3;
+ this.trackBarJpegQuality.TickFrequency = 10;
+ this.trackBarJpegQuality.Scroll += new System.EventHandler(this.TrackBarJpegQualityScroll);
+ //
+ // checkbox_dontaskagain
+ //
+ this.checkbox_dontaskagain.CheckAlign = System.Drawing.ContentAlignment.TopLeft;
+ this.checkbox_dontaskagain.ImageAlign = System.Drawing.ContentAlignment.TopLeft;
+ this.checkbox_dontaskagain.Location = new System.Drawing.Point(12, 106);
+ this.checkbox_dontaskagain.Name = "checkbox_dontaskagain";
+ this.checkbox_dontaskagain.LanguageKey = "qualitydialog_dontaskagain";
+ this.checkbox_dontaskagain.Size = new System.Drawing.Size(268, 37);
+ this.checkbox_dontaskagain.TabIndex = 5;
+ this.checkbox_dontaskagain.TextAlign = System.Drawing.ContentAlignment.TopLeft;
+ this.checkbox_dontaskagain.UseVisualStyleBackColor = true;
+ //
+ // button_ok
+ //
+ this.button_ok.DialogResult = System.Windows.Forms.DialogResult.Cancel;
+ this.button_ok.Location = new System.Drawing.Point(205, 149);
+ this.button_ok.Name = "button_ok";
+ this.button_ok.Size = new System.Drawing.Size(75, 23);
+ this.button_ok.TabIndex = 1;
+ this.button_ok.LanguageKey = "OK";
+ this.button_ok.UseVisualStyleBackColor = true;
+ this.button_ok.Click += new System.EventHandler(this.Button_okClick);
+ //
+ // checkBox_reduceColors
+ //
+ this.checkBox_reduceColors.Location = new System.Drawing.Point(12, 11);
+ this.checkBox_reduceColors.Name = "checkBox_reduceColors";
+ this.checkBox_reduceColors.Size = new System.Drawing.Size(95, 17);
+ this.checkBox_reduceColors.TabIndex = 2;
+ this.checkBox_reduceColors.Text = "settings_reducecolors";
+ this.checkBox_reduceColors.UseVisualStyleBackColor = true;
+ //
+ // QualityDialog
+ //
+ this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi;
+ this.ClientSize = new System.Drawing.Size(299, 184);
+ this.ControlBox = false;
+ this.Controls.Add(this.checkBox_reduceColors);
+ this.Controls.Add(this.button_ok);
+ this.Controls.Add(this.checkbox_dontaskagain);
+ this.Controls.Add(this.label_choosejpegquality);
+ this.Controls.Add(this.textBoxJpegQuality);
+ this.Controls.Add(this.trackBarJpegQuality);
+ this.MaximizeBox = false;
+ this.MinimizeBox = false;
+ this.Name = "QualityDialog";
+ this.SizeGripStyle = System.Windows.Forms.SizeGripStyle.Hide;
+ this.LanguageKey = "qualitydialog_title";
+ ((System.ComponentModel.ISupportInitialize)(this.trackBarJpegQuality)).EndInit();
+ this.ResumeLayout(false);
+ this.PerformLayout();
+
+ }
+ private GreenshotButton button_ok;
+ private GreenshotCheckBox checkbox_dontaskagain;
+ private System.Windows.Forms.TrackBar trackBarJpegQuality;
+ private System.Windows.Forms.TextBox textBoxJpegQuality;
+ private GreenshotLabel label_choosejpegquality;
+ private System.Windows.Forms.CheckBox checkBox_reduceColors;
+ }
+}
diff --git a/src/GreenshotPlugin/Controls/QualityDialog.cs b/src/Greenshot.Base/Controls/QualityDialog.cs
similarity index 92%
rename from src/GreenshotPlugin/Controls/QualityDialog.cs
rename to src/Greenshot.Base/Controls/QualityDialog.cs
index ae8ae07f2..1da67eb2b 100644
--- a/src/GreenshotPlugin/Controls/QualityDialog.cs
+++ b/src/Greenshot.Base/Controls/QualityDialog.cs
@@ -1,71 +1,71 @@
-/*
- * Greenshot - a free and open source screenshot tool
- * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
- *
- * For more information see: http://getgreenshot.org/
- * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 1 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-using System;
-using GreenshotPlugin.Core;
-using GreenshotPlugin.IniFile;
-using GreenshotPlugin.Interfaces.Plugin;
-
-namespace GreenshotPlugin.Controls
-{
- ///
- /// Description of JpegQualityDialog.
- ///
- public partial class QualityDialog : GreenshotForm
- {
- private static readonly CoreConfiguration conf = IniConfig.GetIniSection();
- public SurfaceOutputSettings Settings { get; set; }
-
- public QualityDialog(SurfaceOutputSettings outputSettings)
- {
- Settings = outputSettings;
- //
- // The InitializeComponent() call is required for Windows Forms designer support.
- //
- InitializeComponent();
-
- checkBox_reduceColors.Checked = Settings.ReduceColors;
- trackBarJpegQuality.Enabled = OutputFormat.jpg.Equals(outputSettings.Format);
- trackBarJpegQuality.Value = Settings.JPGQuality;
- textBoxJpegQuality.Enabled = OutputFormat.jpg.Equals(outputSettings.Format);
- textBoxJpegQuality.Text = Settings.JPGQuality.ToString();
- ToFront = true;
- }
-
- private void Button_okClick(object sender, EventArgs e)
- {
- Settings.JPGQuality = trackBarJpegQuality.Value;
- Settings.ReduceColors = checkBox_reduceColors.Checked;
- if (checkbox_dontaskagain.Checked)
- {
- conf.OutputFileJpegQuality = Settings.JPGQuality;
- conf.OutputFilePromptQuality = false;
- conf.OutputFileReduceColors = Settings.ReduceColors;
- IniConfig.Save();
- }
- }
-
- private void TrackBarJpegQualityScroll(object sender, EventArgs e)
- {
- textBoxJpegQuality.Text = trackBarJpegQuality.Value.ToString();
- }
- }
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+using System;
+using Greenshot.Base.Core;
+using Greenshot.Base.IniFile;
+using Greenshot.Base.Interfaces.Plugin;
+
+namespace Greenshot.Base.Controls
+{
+ ///
+ /// Description of JpegQualityDialog.
+ ///
+ public partial class QualityDialog : GreenshotForm
+ {
+ private static readonly CoreConfiguration conf = IniConfig.GetIniSection();
+ public SurfaceOutputSettings Settings { get; set; }
+
+ public QualityDialog(SurfaceOutputSettings outputSettings)
+ {
+ Settings = outputSettings;
+ //
+ // The InitializeComponent() call is required for Windows Forms designer support.
+ //
+ InitializeComponent();
+
+ checkBox_reduceColors.Checked = Settings.ReduceColors;
+ trackBarJpegQuality.Enabled = OutputFormat.jpg.Equals(outputSettings.Format);
+ trackBarJpegQuality.Value = Settings.JPGQuality;
+ textBoxJpegQuality.Enabled = OutputFormat.jpg.Equals(outputSettings.Format);
+ textBoxJpegQuality.Text = Settings.JPGQuality.ToString();
+ ToFront = true;
+ }
+
+ private void Button_okClick(object sender, EventArgs e)
+ {
+ Settings.JPGQuality = trackBarJpegQuality.Value;
+ Settings.ReduceColors = checkBox_reduceColors.Checked;
+ if (checkbox_dontaskagain.Checked)
+ {
+ conf.OutputFileJpegQuality = Settings.JPGQuality;
+ conf.OutputFilePromptQuality = false;
+ conf.OutputFileReduceColors = Settings.ReduceColors;
+ IniConfig.Save();
+ }
+ }
+
+ private void TrackBarJpegQualityScroll(object sender, EventArgs e)
+ {
+ textBoxJpegQuality.Text = trackBarJpegQuality.Value.ToString();
+ }
+ }
}
\ No newline at end of file
diff --git a/src/GreenshotPlugin/Controls/SaveImageFileDialog.cs b/src/Greenshot.Base/Controls/SaveImageFileDialog.cs
similarity index 95%
rename from src/GreenshotPlugin/Controls/SaveImageFileDialog.cs
rename to src/Greenshot.Base/Controls/SaveImageFileDialog.cs
index 49caedb07..d95702547 100644
--- a/src/GreenshotPlugin/Controls/SaveImageFileDialog.cs
+++ b/src/Greenshot.Base/Controls/SaveImageFileDialog.cs
@@ -1,235 +1,235 @@
-/*
- * Greenshot - a free and open source screenshot tool
- * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
- *
- * For more information see: http://getgreenshot.org/
- * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 1 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-using System;
-using System.IO;
-using System.Windows.Forms;
-using GreenshotPlugin.Core;
-using GreenshotPlugin.IniFile;
-using GreenshotPlugin.Interfaces;
-using log4net;
-
-namespace GreenshotPlugin.Controls
-{
- ///
- /// Custom dialog for saving images, wraps SaveFileDialog.
- /// For some reason SFD is sealed :(
- ///
- public class SaveImageFileDialog : IDisposable
- {
- private static readonly ILog LOG = LogManager.GetLogger(typeof(SaveImageFileDialog));
- private static readonly CoreConfiguration conf = IniConfig.GetIniSection();
- protected SaveFileDialog SaveFileDialog;
- private FilterOption[] _filterOptions;
- private DirectoryInfo _eagerlyCreatedDirectory;
- private readonly ICaptureDetails _captureDetails;
-
- public void Dispose()
- {
- Dispose(true);
- GC.SuppressFinalize(this);
- }
-
- protected virtual void Dispose(bool disposing)
- {
- if (disposing)
- {
- if (SaveFileDialog != null)
- {
- SaveFileDialog.Dispose();
- SaveFileDialog = null;
- }
- }
- }
-
- public SaveImageFileDialog(ICaptureDetails captureDetails)
- {
- _captureDetails = captureDetails;
- Init();
- }
-
- private void Init()
- {
- SaveFileDialog = new SaveFileDialog();
- ApplyFilterOptions();
- string initialDirectory = null;
- try
- {
- conf.ValidateAndCorrectOutputFileAsFullpath();
- initialDirectory = Path.GetDirectoryName(conf.OutputFileAsFullpath);
- }
- catch
- {
- LOG.WarnFormat("OutputFileAsFullpath was set to {0}, ignoring due to problem in path.", conf.OutputFileAsFullpath);
- }
-
- if (!string.IsNullOrEmpty(initialDirectory) && Directory.Exists(initialDirectory))
- {
- SaveFileDialog.InitialDirectory = initialDirectory;
- }
- else if (Directory.Exists(conf.OutputFilePath))
- {
- SaveFileDialog.InitialDirectory = conf.OutputFilePath;
- }
-
- // The following property fixes a problem that the directory where we save is locked (bug #2899790)
- SaveFileDialog.RestoreDirectory = true;
- SaveFileDialog.OverwritePrompt = true;
- SaveFileDialog.CheckPathExists = false;
- SaveFileDialog.AddExtension = true;
- ApplySuggestedValues();
- }
-
- private void ApplyFilterOptions()
- {
- PrepareFilterOptions();
- string fdf = string.Empty;
- int preselect = 0;
- var outputFileFormatAsString = Enum.GetName(typeof(OutputFormat), conf.OutputFileFormat);
- for (int i = 0; i < _filterOptions.Length; i++)
- {
- FilterOption fo = _filterOptions[i];
- fdf += fo.Label + "|*." + fo.Extension + "|";
- if (outputFileFormatAsString == fo.Extension)
- preselect = i;
- }
-
- fdf = fdf.Substring(0, fdf.Length - 1);
- SaveFileDialog.Filter = fdf;
- SaveFileDialog.FilterIndex = preselect + 1;
- }
-
- private void PrepareFilterOptions()
- {
- OutputFormat[] supportedImageFormats = (OutputFormat[]) Enum.GetValues(typeof(OutputFormat));
- _filterOptions = new FilterOption[supportedImageFormats.Length];
- for (int i = 0; i < _filterOptions.Length; i++)
- {
- string ifo = supportedImageFormats[i].ToString();
- if (ifo.ToLower().Equals("jpeg")) ifo = "Jpg"; // we dont want no jpeg files, so let the dialog check for jpg
- FilterOption fo = new FilterOption
- {
- Label = ifo.ToUpper(),
- Extension = ifo.ToLower()
- };
- _filterOptions.SetValue(fo, i);
- }
- }
-
- ///
- /// filename exactly as typed in the filename field
- ///
- public string FileName
- {
- get { return SaveFileDialog.FileName; }
- set { SaveFileDialog.FileName = value; }
- }
-
- ///
- /// initial directory of the dialog
- ///
- public string InitialDirectory
- {
- get { return SaveFileDialog.InitialDirectory; }
- set { SaveFileDialog.InitialDirectory = value; }
- }
-
- ///
- /// returns filename as typed in the filename field with extension.
- /// if filename field value ends with selected extension, the value is just returned.
- /// otherwise, the selected extension is appended to the filename.
- ///
- public string FileNameWithExtension
- {
- get
- {
- string fn = SaveFileDialog.FileName;
- // if the filename contains a valid extension, which is the same like the selected filter item's extension, the filename is okay
- if (fn.EndsWith(Extension, StringComparison.CurrentCultureIgnoreCase)) return fn;
- // otherwise we just add the selected filter item's extension
- else return fn + "." + Extension;
- }
- set
- {
- FileName = Path.GetFileNameWithoutExtension(value);
- Extension = Path.GetExtension(value);
- }
- }
-
- ///
- /// gets or sets selected extension
- ///
- public string Extension
- {
- get { return _filterOptions[SaveFileDialog.FilterIndex - 1].Extension; }
- set
- {
- for (int i = 0; i < _filterOptions.Length; i++)
- {
- if (value.Equals(_filterOptions[i].Extension, StringComparison.CurrentCultureIgnoreCase))
- {
- SaveFileDialog.FilterIndex = i + 1;
- }
- }
- }
- }
-
- public DialogResult ShowDialog()
- {
- DialogResult ret = SaveFileDialog.ShowDialog();
- CleanUp();
- return ret;
- }
-
- ///
- /// sets InitialDirectory and FileName property of a SaveFileDialog smartly, considering default pattern and last used path
- ///
- private void ApplySuggestedValues()
- {
- // build the full path and set dialog properties
- FileName = FilenameHelper.GetFilenameWithoutExtensionFromPattern(conf.OutputFileFilenamePattern, _captureDetails);
- }
-
- private class FilterOption
- {
- public string Label;
- public string Extension;
- }
-
- private void CleanUp()
- {
- // fix for bug #3379053
- try
- {
- if (_eagerlyCreatedDirectory != null && _eagerlyCreatedDirectory.GetFiles().Length == 0 && _eagerlyCreatedDirectory.GetDirectories().Length == 0)
- {
- _eagerlyCreatedDirectory.Delete();
- _eagerlyCreatedDirectory = null;
- }
- }
- catch (Exception e)
- {
- LOG.WarnFormat("Couldn't cleanup directory due to: {0}", e.Message);
- _eagerlyCreatedDirectory = null;
- }
- }
- }
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+using System;
+using System.IO;
+using System.Windows.Forms;
+using Greenshot.Base.Core;
+using Greenshot.Base.IniFile;
+using Greenshot.Base.Interfaces;
+using log4net;
+
+namespace Greenshot.Base.Controls
+{
+ ///
+ /// Custom dialog for saving images, wraps SaveFileDialog.
+ /// For some reason SFD is sealed :(
+ ///
+ public class SaveImageFileDialog : IDisposable
+ {
+ private static readonly ILog LOG = LogManager.GetLogger(typeof(SaveImageFileDialog));
+ private static readonly CoreConfiguration conf = IniConfig.GetIniSection();
+ protected SaveFileDialog SaveFileDialog;
+ private FilterOption[] _filterOptions;
+ private DirectoryInfo _eagerlyCreatedDirectory;
+ private readonly ICaptureDetails _captureDetails;
+
+ public void Dispose()
+ {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+
+ protected virtual void Dispose(bool disposing)
+ {
+ if (disposing)
+ {
+ if (SaveFileDialog != null)
+ {
+ SaveFileDialog.Dispose();
+ SaveFileDialog = null;
+ }
+ }
+ }
+
+ public SaveImageFileDialog(ICaptureDetails captureDetails)
+ {
+ _captureDetails = captureDetails;
+ Init();
+ }
+
+ private void Init()
+ {
+ SaveFileDialog = new SaveFileDialog();
+ ApplyFilterOptions();
+ string initialDirectory = null;
+ try
+ {
+ conf.ValidateAndCorrectOutputFileAsFullpath();
+ initialDirectory = Path.GetDirectoryName(conf.OutputFileAsFullpath);
+ }
+ catch
+ {
+ LOG.WarnFormat("OutputFileAsFullpath was set to {0}, ignoring due to problem in path.", conf.OutputFileAsFullpath);
+ }
+
+ if (!string.IsNullOrEmpty(initialDirectory) && Directory.Exists(initialDirectory))
+ {
+ SaveFileDialog.InitialDirectory = initialDirectory;
+ }
+ else if (Directory.Exists(conf.OutputFilePath))
+ {
+ SaveFileDialog.InitialDirectory = conf.OutputFilePath;
+ }
+
+ // The following property fixes a problem that the directory where we save is locked (bug #2899790)
+ SaveFileDialog.RestoreDirectory = true;
+ SaveFileDialog.OverwritePrompt = true;
+ SaveFileDialog.CheckPathExists = false;
+ SaveFileDialog.AddExtension = true;
+ ApplySuggestedValues();
+ }
+
+ private void ApplyFilterOptions()
+ {
+ PrepareFilterOptions();
+ string fdf = string.Empty;
+ int preselect = 0;
+ var outputFileFormatAsString = Enum.GetName(typeof(OutputFormat), conf.OutputFileFormat);
+ for (int i = 0; i < _filterOptions.Length; i++)
+ {
+ FilterOption fo = _filterOptions[i];
+ fdf += fo.Label + "|*." + fo.Extension + "|";
+ if (outputFileFormatAsString == fo.Extension)
+ preselect = i;
+ }
+
+ fdf = fdf.Substring(0, fdf.Length - 1);
+ SaveFileDialog.Filter = fdf;
+ SaveFileDialog.FilterIndex = preselect + 1;
+ }
+
+ private void PrepareFilterOptions()
+ {
+ OutputFormat[] supportedImageFormats = (OutputFormat[]) Enum.GetValues(typeof(OutputFormat));
+ _filterOptions = new FilterOption[supportedImageFormats.Length];
+ for (int i = 0; i < _filterOptions.Length; i++)
+ {
+ string ifo = supportedImageFormats[i].ToString();
+ if (ifo.ToLower().Equals("jpeg")) ifo = "Jpg"; // we dont want no jpeg files, so let the dialog check for jpg
+ FilterOption fo = new FilterOption
+ {
+ Label = ifo.ToUpper(),
+ Extension = ifo.ToLower()
+ };
+ _filterOptions.SetValue(fo, i);
+ }
+ }
+
+ ///
+ /// filename exactly as typed in the filename field
+ ///
+ public string FileName
+ {
+ get { return SaveFileDialog.FileName; }
+ set { SaveFileDialog.FileName = value; }
+ }
+
+ ///
+ /// initial directory of the dialog
+ ///
+ public string InitialDirectory
+ {
+ get { return SaveFileDialog.InitialDirectory; }
+ set { SaveFileDialog.InitialDirectory = value; }
+ }
+
+ ///
+ /// returns filename as typed in the filename field with extension.
+ /// if filename field value ends with selected extension, the value is just returned.
+ /// otherwise, the selected extension is appended to the filename.
+ ///
+ public string FileNameWithExtension
+ {
+ get
+ {
+ string fn = SaveFileDialog.FileName;
+ // if the filename contains a valid extension, which is the same like the selected filter item's extension, the filename is okay
+ if (fn.EndsWith(Extension, StringComparison.CurrentCultureIgnoreCase)) return fn;
+ // otherwise we just add the selected filter item's extension
+ else return fn + "." + Extension;
+ }
+ set
+ {
+ FileName = Path.GetFileNameWithoutExtension(value);
+ Extension = Path.GetExtension(value);
+ }
+ }
+
+ ///
+ /// gets or sets selected extension
+ ///
+ public string Extension
+ {
+ get { return _filterOptions[SaveFileDialog.FilterIndex - 1].Extension; }
+ set
+ {
+ for (int i = 0; i < _filterOptions.Length; i++)
+ {
+ if (value.Equals(_filterOptions[i].Extension, StringComparison.CurrentCultureIgnoreCase))
+ {
+ SaveFileDialog.FilterIndex = i + 1;
+ }
+ }
+ }
+ }
+
+ public DialogResult ShowDialog()
+ {
+ DialogResult ret = SaveFileDialog.ShowDialog();
+ CleanUp();
+ return ret;
+ }
+
+ ///
+ /// sets InitialDirectory and FileName property of a SaveFileDialog smartly, considering default pattern and last used path
+ ///
+ private void ApplySuggestedValues()
+ {
+ // build the full path and set dialog properties
+ FileName = FilenameHelper.GetFilenameWithoutExtensionFromPattern(conf.OutputFileFilenamePattern, _captureDetails);
+ }
+
+ private class FilterOption
+ {
+ public string Label;
+ public string Extension;
+ }
+
+ private void CleanUp()
+ {
+ // fix for bug #3379053
+ try
+ {
+ if (_eagerlyCreatedDirectory != null && _eagerlyCreatedDirectory.GetFiles().Length == 0 && _eagerlyCreatedDirectory.GetDirectories().Length == 0)
+ {
+ _eagerlyCreatedDirectory.Delete();
+ _eagerlyCreatedDirectory = null;
+ }
+ }
+ catch (Exception e)
+ {
+ LOG.WarnFormat("Couldn't cleanup directory due to: {0}", e.Message);
+ _eagerlyCreatedDirectory = null;
+ }
+ }
+ }
}
\ No newline at end of file
diff --git a/src/GreenshotPlugin/Controls/ThumbnailForm.cs b/src/Greenshot.Base/Controls/ThumbnailForm.cs
similarity index 92%
rename from src/GreenshotPlugin/Controls/ThumbnailForm.cs
rename to src/Greenshot.Base/Controls/ThumbnailForm.cs
index 2b6b349b4..16d597f3c 100644
--- a/src/GreenshotPlugin/Controls/ThumbnailForm.cs
+++ b/src/Greenshot.Base/Controls/ThumbnailForm.cs
@@ -1,157 +1,156 @@
-/*
- * Greenshot - a free and open source screenshot tool
- * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
- *
- * For more information see: http://getgreenshot.org/
- * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 1 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-using System;
-using System.Windows.Forms;
-using GreenshotPlugin.Core;
-using System.Drawing;
-using GreenshotPlugin.Core.Enums;
-using GreenshotPlugin.IniFile;
-using GreenshotPlugin.UnmanagedHelpers;
-using GreenshotPlugin.UnmanagedHelpers.Enums;
-using GreenshotPlugin.UnmanagedHelpers.Structs;
-
-namespace GreenshotPlugin.Controls
-{
- ///
- /// This form allows us to show a Thumbnail preview of a window near the context menu when selecting a window to capture.
- /// Didn't make it completely "generic" yet, but at least most logic is in here so we don't have it in the mainform.
- ///
- public sealed class ThumbnailForm : FormWithoutActivation
- {
- private static readonly CoreConfiguration conf = IniConfig.GetIniSection();
-
- private IntPtr _thumbnailHandle = IntPtr.Zero;
-
- public ThumbnailForm()
- {
- ShowInTaskbar = false;
- FormBorderStyle = FormBorderStyle.None;
- TopMost = false;
- Enabled = false;
- if (conf.WindowCaptureMode == WindowCaptureMode.Auto || conf.WindowCaptureMode == WindowCaptureMode.Aero)
- {
- BackColor = Color.FromArgb(255, conf.DWMBackgroundColor.R, conf.DWMBackgroundColor.G, conf.DWMBackgroundColor.B);
- }
- else
- {
- BackColor = Color.White;
- }
-
- // cleanup at close
- FormClosing += delegate { UnregisterThumbnail(); };
- }
-
- public new void Hide()
- {
- UnregisterThumbnail();
- base.Hide();
- }
-
- private void UnregisterThumbnail()
- {
- if (_thumbnailHandle == IntPtr.Zero) return;
-
- DWM.DwmUnregisterThumbnail(_thumbnailHandle);
- _thumbnailHandle = IntPtr.Zero;
- }
-
- ///
- /// Show the thumbnail of the supplied window above (or under) the parent Control
- ///
- /// WindowDetails
- /// Control
- public void ShowThumbnail(WindowDetails window, Control parentControl)
- {
- UnregisterThumbnail();
-
- DWM.DwmRegisterThumbnail(Handle, window.Handle, out _thumbnailHandle);
- if (_thumbnailHandle == IntPtr.Zero) return;
-
- var result = DWM.DwmQueryThumbnailSourceSize(_thumbnailHandle, out var sourceSize);
- if (result.Failed())
- {
- DWM.DwmUnregisterThumbnail(_thumbnailHandle);
- return;
- }
-
- if (sourceSize.IsEmpty)
- {
- DWM.DwmUnregisterThumbnail(_thumbnailHandle);
- return;
- }
-
- int thumbnailHeight = 200;
- int thumbnailWidth = (int) (thumbnailHeight * (sourceSize.Width / (float) sourceSize.Height));
- if (parentControl != null && thumbnailWidth > parentControl.Width)
- {
- thumbnailWidth = parentControl.Width;
- thumbnailHeight = (int) (thumbnailWidth * (sourceSize.Height / (float) sourceSize.Width));
- }
-
- Width = thumbnailWidth;
- Height = thumbnailHeight;
- // Prepare the displaying of the Thumbnail
- var dwmThumbnailProperties = new DWM_THUMBNAIL_PROPERTIES
- {
- Opacity = 255,
- Visible = true,
- SourceClientAreaOnly = false,
- Destination = new RECT(0, 0, thumbnailWidth, thumbnailHeight)
- };
- result = DWM.DwmUpdateThumbnailProperties(_thumbnailHandle, ref dwmThumbnailProperties);
- if (result.Failed())
- {
- DWM.DwmUnregisterThumbnail(_thumbnailHandle);
- return;
- }
-
- if (parentControl != null)
- {
- AlignToControl(parentControl);
- }
-
- if (!Visible)
- {
- Show();
- }
-
- // Make sure it's on "top"!
- if (parentControl != null)
- {
- User32.SetWindowPos(Handle, parentControl.Handle, 0, 0, 0, 0, WindowPos.SWP_NOMOVE | WindowPos.SWP_NOSIZE | WindowPos.SWP_NOACTIVATE);
- }
- }
-
- public void AlignToControl(Control alignTo)
- {
- var screenBounds = WindowCapture.GetScreenBounds();
- if (screenBounds.Contains(alignTo.Left, alignTo.Top - Height))
- {
- Location = new Point(alignTo.Left + (alignTo.Width / 2) - (Width / 2), alignTo.Top - Height);
- }
- else
- {
- Location = new Point(alignTo.Left + (alignTo.Width / 2) - (Width / 2), alignTo.Bottom);
- }
- }
- }
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+using System;
+using System.Drawing;
+using System.Windows.Forms;
+using Greenshot.Base.Core;
+using Greenshot.Base.IniFile;
+using Greenshot.Base.UnmanagedHelpers;
+using Greenshot.Base.UnmanagedHelpers.Enums;
+using Greenshot.Base.UnmanagedHelpers.Structs;
+
+namespace Greenshot.Base.Controls
+{
+ ///
+ /// This form allows us to show a Thumbnail preview of a window near the context menu when selecting a window to capture.
+ /// Didn't make it completely "generic" yet, but at least most logic is in here so we don't have it in the mainform.
+ ///
+ public sealed class ThumbnailForm : FormWithoutActivation
+ {
+ private static readonly CoreConfiguration conf = IniConfig.GetIniSection();
+
+ private IntPtr _thumbnailHandle = IntPtr.Zero;
+
+ public ThumbnailForm()
+ {
+ ShowInTaskbar = false;
+ FormBorderStyle = FormBorderStyle.None;
+ TopMost = false;
+ Enabled = false;
+ if (conf.WindowCaptureMode == WindowCaptureMode.Auto || conf.WindowCaptureMode == WindowCaptureMode.Aero)
+ {
+ BackColor = Color.FromArgb(255, conf.DWMBackgroundColor.R, conf.DWMBackgroundColor.G, conf.DWMBackgroundColor.B);
+ }
+ else
+ {
+ BackColor = Color.White;
+ }
+
+ // cleanup at close
+ FormClosing += delegate { UnregisterThumbnail(); };
+ }
+
+ public new void Hide()
+ {
+ UnregisterThumbnail();
+ base.Hide();
+ }
+
+ private void UnregisterThumbnail()
+ {
+ if (_thumbnailHandle == IntPtr.Zero) return;
+
+ DWM.DwmUnregisterThumbnail(_thumbnailHandle);
+ _thumbnailHandle = IntPtr.Zero;
+ }
+
+ ///
+ /// Show the thumbnail of the supplied window above (or under) the parent Control
+ ///
+ /// WindowDetails
+ /// Control
+ public void ShowThumbnail(WindowDetails window, Control parentControl)
+ {
+ UnregisterThumbnail();
+
+ DWM.DwmRegisterThumbnail(Handle, window.Handle, out _thumbnailHandle);
+ if (_thumbnailHandle == IntPtr.Zero) return;
+
+ var result = DWM.DwmQueryThumbnailSourceSize(_thumbnailHandle, out var sourceSize);
+ if (result.Failed())
+ {
+ DWM.DwmUnregisterThumbnail(_thumbnailHandle);
+ return;
+ }
+
+ if (sourceSize.IsEmpty)
+ {
+ DWM.DwmUnregisterThumbnail(_thumbnailHandle);
+ return;
+ }
+
+ int thumbnailHeight = 200;
+ int thumbnailWidth = (int) (thumbnailHeight * (sourceSize.Width / (float) sourceSize.Height));
+ if (parentControl != null && thumbnailWidth > parentControl.Width)
+ {
+ thumbnailWidth = parentControl.Width;
+ thumbnailHeight = (int) (thumbnailWidth * (sourceSize.Height / (float) sourceSize.Width));
+ }
+
+ Width = thumbnailWidth;
+ Height = thumbnailHeight;
+ // Prepare the displaying of the Thumbnail
+ var dwmThumbnailProperties = new DWM_THUMBNAIL_PROPERTIES
+ {
+ Opacity = 255,
+ Visible = true,
+ SourceClientAreaOnly = false,
+ Destination = new RECT(0, 0, thumbnailWidth, thumbnailHeight)
+ };
+ result = DWM.DwmUpdateThumbnailProperties(_thumbnailHandle, ref dwmThumbnailProperties);
+ if (result.Failed())
+ {
+ DWM.DwmUnregisterThumbnail(_thumbnailHandle);
+ return;
+ }
+
+ if (parentControl != null)
+ {
+ AlignToControl(parentControl);
+ }
+
+ if (!Visible)
+ {
+ Show();
+ }
+
+ // Make sure it's on "top"!
+ if (parentControl != null)
+ {
+ User32.SetWindowPos(Handle, parentControl.Handle, 0, 0, 0, 0, WindowPos.SWP_NOMOVE | WindowPos.SWP_NOSIZE | WindowPos.SWP_NOACTIVATE);
+ }
+ }
+
+ public void AlignToControl(Control alignTo)
+ {
+ var screenBounds = WindowCapture.GetScreenBounds();
+ if (screenBounds.Contains(alignTo.Left, alignTo.Top - Height))
+ {
+ Location = new Point(alignTo.Left + (alignTo.Width / 2) - (Width / 2), alignTo.Top - Height);
+ }
+ else
+ {
+ Location = new Point(alignTo.Left + (alignTo.Width / 2) - (Width / 2), alignTo.Bottom);
+ }
+ }
+ }
}
\ No newline at end of file
diff --git a/src/GreenshotPlugin/Core/AbstractDestination.cs b/src/Greenshot.Base/Core/AbstractDestination.cs
similarity index 96%
rename from src/GreenshotPlugin/Core/AbstractDestination.cs
rename to src/Greenshot.Base/Core/AbstractDestination.cs
index 27c6abbb4..c9864f99b 100644
--- a/src/GreenshotPlugin/Core/AbstractDestination.cs
+++ b/src/Greenshot.Base/Core/AbstractDestination.cs
@@ -1,416 +1,416 @@
-/*
- * Greenshot - a free and open source screenshot tool
- * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
- *
- * For more information see: http://getgreenshot.org/
- * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 1 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-using System;
-using System.Collections.Generic;
-using System.Drawing;
-using System.Threading;
-using System.Windows.Forms;
-using GreenshotPlugin.IniFile;
-using GreenshotPlugin.Interfaces;
-using GreenshotPlugin.UnmanagedHelpers;
-using log4net;
-
-namespace GreenshotPlugin.Core
-{
- ///
- /// Description of AbstractDestination.
- ///
- public abstract class AbstractDestination : IDestination
- {
- private static readonly ILog Log = LogManager.GetLogger(typeof(AbstractDestination));
- private static readonly CoreConfiguration CoreConfig = IniConfig.GetIniSection();
-
- public virtual int CompareTo(object obj)
- {
- if (!(obj is IDestination other))
- {
- return 1;
- }
-
- if (Priority == other.Priority)
- {
- return string.Compare(Description, other.Description, StringComparison.Ordinal);
- }
-
- return Priority - other.Priority;
- }
-
- public abstract string Designation { get; }
-
- public abstract string Description { get; }
-
- public virtual int Priority => 10;
-
- public virtual Image DisplayIcon => null;
-
- public virtual Keys EditorShortcutKeys => Keys.None;
-
- public virtual IEnumerable DynamicDestinations()
- {
- yield break;
- }
-
- public void Dispose()
- {
- Dispose(true);
- GC.SuppressFinalize(this);
- }
-
- protected virtual void Dispose(bool disposing)
- {
- //if (disposing) {}
- }
-
- public virtual bool IsDynamic => false;
-
- public virtual bool UseDynamicsOnly => false;
-
- public virtual bool IsLinkable => false;
-
- public virtual bool IsActive
- {
- get
- {
- if (CoreConfig.ExcludeDestinations != null && CoreConfig.ExcludeDestinations.Contains(Designation))
- {
- return false;
- }
-
- return true;
- }
- }
-
- public abstract ExportInformation ExportCapture(bool manuallyInitiated, ISurface surface, ICaptureDetails captureDetails);
-
- ///
- /// A small helper method to perform some default destination actions, like inform the surface of the export
- ///
- ///
- ///
- public void ProcessExport(ExportInformation exportInformation, ISurface surface)
- {
- if (exportInformation != null && exportInformation.ExportMade)
- {
- if (!string.IsNullOrEmpty(exportInformation.Uri))
- {
- surface.UploadUrl = exportInformation.Uri;
- surface.SendMessageEvent(this, SurfaceMessageTyp.UploadedUri, Language.GetFormattedString("exported_to", exportInformation.DestinationDescription));
- }
- else if (!string.IsNullOrEmpty(exportInformation.Filepath))
- {
- surface.LastSaveFullPath = exportInformation.Filepath;
- surface.SendMessageEvent(this, SurfaceMessageTyp.FileSaved, Language.GetFormattedString("exported_to", exportInformation.DestinationDescription));
- }
- else
- {
- surface.SendMessageEvent(this, SurfaceMessageTyp.Info, Language.GetFormattedString("exported_to", exportInformation.DestinationDescription));
- }
-
- surface.Modified = false;
- }
- else if (!string.IsNullOrEmpty(exportInformation?.ErrorMessage))
- {
- surface.SendMessageEvent(this, SurfaceMessageTyp.Error,
- Language.GetFormattedString("exported_to_error", exportInformation.DestinationDescription) + " " + exportInformation.ErrorMessage);
- }
- }
-
- public override string ToString()
- {
- return Description;
- }
-
- ///
- /// Helper method to add events which set the tag, this way we can see why there might be a close.
- ///
- /// Item to add the events to
- /// Menu to set the tag
- /// Value for the tag
- private void AddTagEvents(ToolStripMenuItem menuItem, ContextMenuStrip menu, string tagValue)
- {
- if (menuItem != null && menu != null)
- {
- menuItem.MouseDown += delegate
- {
- Log.DebugFormat("Setting tag to '{0}'", tagValue);
- menu.Tag = tagValue;
- };
- menuItem.MouseUp += delegate
- {
- Log.Debug("Deleting tag");
- menu.Tag = null;
- };
- }
- }
-
- ///
- /// This method will create and show the destination picker menu
- ///
- /// Boolean if the dynamic values also need to be added
- /// The surface which can be exported
- /// Details for the surface
- /// The list of destinations to show
- ///
- public ExportInformation ShowPickerMenu(bool addDynamics, ISurface surface, ICaptureDetails captureDetails, IEnumerable destinations)
- {
- // Generate an empty ExportInformation object, for when nothing was selected.
- ExportInformation exportInformation = new ExportInformation(Designation, Language.GetString("settings_destination_picker"));
- var menu = new ContextMenuStrip
- {
- ImageScalingSize = CoreConfig.ScaledIconSize,
- Tag = null,
- TopLevel = true
- };
-
- menu.Opening += (sender, args) =>
- {
- // find the DPI settings for the screen where this is going to land
- var screenDpi = DpiHelper.GetDpi(menu.Location);
- var scaledIconSize = DpiHelper.ScaleWithDpi(CoreConfig.IconSize, screenDpi);
- menu.SuspendLayout();
- var fontSize = DpiHelper.ScaleWithDpi(12f, screenDpi);
- menu.Font = new Font(FontFamily.GenericSansSerif, fontSize, FontStyle.Regular, GraphicsUnit.Pixel);
- menu.ImageScalingSize = scaledIconSize;
- menu.ResumeLayout();
- };
-
- menu.Closing += delegate(object source, ToolStripDropDownClosingEventArgs eventArgs)
- {
- Log.DebugFormat("Close reason: {0}", eventArgs.CloseReason);
- switch (eventArgs.CloseReason)
- {
- case ToolStripDropDownCloseReason.AppFocusChange:
- if (menu.Tag == null)
- {
- // Do not allow the close if the tag is not set, this means the user clicked somewhere else.
- eventArgs.Cancel = true;
- }
- else
- {
- Log.DebugFormat("Letting the menu 'close' as the tag is set to '{0}'", menu.Tag);
- }
-
- break;
- case ToolStripDropDownCloseReason.ItemClicked:
- case ToolStripDropDownCloseReason.CloseCalled:
- // The ContextMenuStrip can be "closed" for these reasons.
- break;
- case ToolStripDropDownCloseReason.Keyboard:
- // Dispose as the close is clicked
- if (!captureDetails.HasDestination("Editor"))
- {
- surface.Dispose();
- surface = null;
- }
-
- break;
- default:
- eventArgs.Cancel = true;
- break;
- }
- };
- menu.MouseEnter += delegate
- {
- // in case the menu has been unfocused, focus again so that dropdown menus will still open on mouseenter
- if (!menu.ContainsFocus)
- {
- menu.Focus();
- }
- };
- foreach (IDestination destination in destinations)
- {
- // Fix foreach loop variable for the delegate
- ToolStripMenuItem item = destination.GetMenuItem(addDynamics, menu,
- delegate(object sender, EventArgs e)
- {
- ToolStripMenuItem toolStripMenuItem = sender as ToolStripMenuItem;
- IDestination clickedDestination = (IDestination) toolStripMenuItem?.Tag;
- if (clickedDestination == null)
- {
- return;
- }
-
- menu.Tag = clickedDestination.Designation;
- // Export
- exportInformation = clickedDestination.ExportCapture(true, surface, captureDetails);
- if (exportInformation != null && exportInformation.ExportMade)
- {
- Log.InfoFormat("Export to {0} success, closing menu", exportInformation.DestinationDescription);
- // close menu if the destination wasn't the editor
- menu.Close();
-
- // Cleanup surface, only if there is no editor in the destinations and we didn't export to the editor
- if (!captureDetails.HasDestination("Editor") && !"Editor".Equals(clickedDestination.Designation))
- {
- surface.Dispose();
- surface = null;
- }
- }
- else
- {
- Log.Info("Export cancelled or failed, showing menu again");
-
- // Make sure a click besides the menu don't close it.
- menu.Tag = null;
-
- // This prevents the problem that the context menu shows in the task-bar
- ShowMenuAtCursor(menu);
- }
- }
- );
- if (item != null)
- {
- menu.Items.Add(item);
- }
- }
-
- // Close
- menu.Items.Add(new ToolStripSeparator());
- ToolStripMenuItem closeItem = new ToolStripMenuItem(Language.GetString("editor_close"))
- {
- Image = GreenshotResources.GetImage("Close.Image")
- };
- closeItem.Click += delegate
- {
- // This menu entry is the close itself, we can dispose the surface
- menu.Close();
- if (!captureDetails.HasDestination("Editor"))
- {
- surface.Dispose();
- surface = null;
- }
- };
- menu.Items.Add(closeItem);
-
- ShowMenuAtCursor(menu);
- return exportInformation;
- }
-
- ///
- /// This method will show the supplied context menu at the mouse cursor, also makes sure it has focus and it's not visible in the taskbar.
- ///
- ///
- private static void ShowMenuAtCursor(ContextMenuStrip menu)
- {
- // find a suitable location
- Point location = Cursor.Position;
- Rectangle menuRectangle = new Rectangle(location, menu.Size);
-
- menuRectangle.Intersect(WindowCapture.GetScreenBounds());
- if (menuRectangle.Height < menu.Height)
- {
- location.Offset(-40, -(menuRectangle.Height - menu.Height));
- }
- else
- {
- location.Offset(-40, -10);
- }
-
- // This prevents the problem that the context menu shows in the task-bar
- User32.SetForegroundWindow(SimpleServiceProvider.Current.GetInstance().ContextMenuStrip.Handle);
- menu.Show(location);
- menu.Focus();
-
- // Wait for the menu to close, so we can dispose it.
- while (true)
- {
- if (menu.Visible)
- {
- Application.DoEvents();
- Thread.Sleep(100);
- }
- else
- {
- menu.Dispose();
- break;
- }
- }
- }
-
- ///
- /// Return a menu item
- ///
- ///
- ///
- ///
- /// ToolStripMenuItem
- public virtual ToolStripMenuItem GetMenuItem(bool addDynamics, ContextMenuStrip menu, EventHandler destinationClickHandler)
- {
- var basisMenuItem = new ToolStripMenuItem(Description)
- {
- Image = DisplayIcon,
- Tag = this,
- Text = Description
- };
- AddTagEvents(basisMenuItem, menu, Description);
- basisMenuItem.Click -= destinationClickHandler;
- basisMenuItem.Click += destinationClickHandler;
-
- if (IsDynamic && addDynamics)
- {
- basisMenuItem.DropDownOpening += delegate
- {
- if (basisMenuItem.DropDownItems.Count == 0)
- {
- List subDestinations = new List();
- // Fixing Bug #3536968 by catching the COMException (every exception) and not displaying the "subDestinations"
- try
- {
- subDestinations.AddRange(DynamicDestinations());
- }
- catch (Exception ex)
- {
- Log.ErrorFormat("Skipping {0}, due to the following error: {1}", Description, ex.Message);
- }
-
- if (subDestinations.Count > 0)
- {
- if (UseDynamicsOnly && subDestinations.Count == 1)
- {
- basisMenuItem.Tag = subDestinations[0];
- basisMenuItem.Text = subDestinations[0].Description;
- basisMenuItem.Click -= destinationClickHandler;
- basisMenuItem.Click += destinationClickHandler;
- }
- else
- {
- foreach (IDestination subDestination in subDestinations)
- {
- var destinationMenuItem = new ToolStripMenuItem(subDestination.Description)
- {
- Tag = subDestination,
- Image = subDestination.DisplayIcon
- };
- destinationMenuItem.Click += destinationClickHandler;
- AddTagEvents(destinationMenuItem, menu, subDestination.Description);
- basisMenuItem.DropDownItems.Add(destinationMenuItem);
- }
- }
- }
- }
- };
- }
-
- return basisMenuItem;
- }
- }
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+using System;
+using System.Collections.Generic;
+using System.Drawing;
+using System.Threading;
+using System.Windows.Forms;
+using Greenshot.Base.IniFile;
+using Greenshot.Base.Interfaces;
+using Greenshot.Base.UnmanagedHelpers;
+using log4net;
+
+namespace Greenshot.Base.Core
+{
+ ///
+ /// Description of AbstractDestination.
+ ///
+ public abstract class AbstractDestination : IDestination
+ {
+ private static readonly ILog Log = LogManager.GetLogger(typeof(AbstractDestination));
+ private static readonly CoreConfiguration CoreConfig = IniConfig.GetIniSection();
+
+ public virtual int CompareTo(object obj)
+ {
+ if (!(obj is IDestination other))
+ {
+ return 1;
+ }
+
+ if (Priority == other.Priority)
+ {
+ return string.Compare(Description, other.Description, StringComparison.Ordinal);
+ }
+
+ return Priority - other.Priority;
+ }
+
+ public abstract string Designation { get; }
+
+ public abstract string Description { get; }
+
+ public virtual int Priority => 10;
+
+ public virtual Image DisplayIcon => null;
+
+ public virtual Keys EditorShortcutKeys => Keys.None;
+
+ public virtual IEnumerable DynamicDestinations()
+ {
+ yield break;
+ }
+
+ public void Dispose()
+ {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+
+ protected virtual void Dispose(bool disposing)
+ {
+ //if (disposing) {}
+ }
+
+ public virtual bool IsDynamic => false;
+
+ public virtual bool UseDynamicsOnly => false;
+
+ public virtual bool IsLinkable => false;
+
+ public virtual bool IsActive
+ {
+ get
+ {
+ if (CoreConfig.ExcludeDestinations != null && CoreConfig.ExcludeDestinations.Contains(Designation))
+ {
+ return false;
+ }
+
+ return true;
+ }
+ }
+
+ public abstract ExportInformation ExportCapture(bool manuallyInitiated, ISurface surface, ICaptureDetails captureDetails);
+
+ ///
+ /// A small helper method to perform some default destination actions, like inform the surface of the export
+ ///
+ ///
+ ///
+ public void ProcessExport(ExportInformation exportInformation, ISurface surface)
+ {
+ if (exportInformation != null && exportInformation.ExportMade)
+ {
+ if (!string.IsNullOrEmpty(exportInformation.Uri))
+ {
+ surface.UploadUrl = exportInformation.Uri;
+ surface.SendMessageEvent(this, SurfaceMessageTyp.UploadedUri, Language.GetFormattedString("exported_to", exportInformation.DestinationDescription));
+ }
+ else if (!string.IsNullOrEmpty(exportInformation.Filepath))
+ {
+ surface.LastSaveFullPath = exportInformation.Filepath;
+ surface.SendMessageEvent(this, SurfaceMessageTyp.FileSaved, Language.GetFormattedString("exported_to", exportInformation.DestinationDescription));
+ }
+ else
+ {
+ surface.SendMessageEvent(this, SurfaceMessageTyp.Info, Language.GetFormattedString("exported_to", exportInformation.DestinationDescription));
+ }
+
+ surface.Modified = false;
+ }
+ else if (!string.IsNullOrEmpty(exportInformation?.ErrorMessage))
+ {
+ surface.SendMessageEvent(this, SurfaceMessageTyp.Error,
+ Language.GetFormattedString("exported_to_error", exportInformation.DestinationDescription) + " " + exportInformation.ErrorMessage);
+ }
+ }
+
+ public override string ToString()
+ {
+ return Description;
+ }
+
+ ///
+ /// Helper method to add events which set the tag, this way we can see why there might be a close.
+ ///
+ /// Item to add the events to
+ /// Menu to set the tag
+ /// Value for the tag
+ private void AddTagEvents(ToolStripMenuItem menuItem, ContextMenuStrip menu, string tagValue)
+ {
+ if (menuItem != null && menu != null)
+ {
+ menuItem.MouseDown += delegate
+ {
+ Log.DebugFormat("Setting tag to '{0}'", tagValue);
+ menu.Tag = tagValue;
+ };
+ menuItem.MouseUp += delegate
+ {
+ Log.Debug("Deleting tag");
+ menu.Tag = null;
+ };
+ }
+ }
+
+ ///
+ /// This method will create and show the destination picker menu
+ ///
+ /// Boolean if the dynamic values also need to be added
+ /// The surface which can be exported
+ /// Details for the surface
+ /// The list of destinations to show
+ ///
+ public ExportInformation ShowPickerMenu(bool addDynamics, ISurface surface, ICaptureDetails captureDetails, IEnumerable destinations)
+ {
+ // Generate an empty ExportInformation object, for when nothing was selected.
+ ExportInformation exportInformation = new ExportInformation(Designation, Language.GetString("settings_destination_picker"));
+ var menu = new ContextMenuStrip
+ {
+ ImageScalingSize = CoreConfig.ScaledIconSize,
+ Tag = null,
+ TopLevel = true
+ };
+
+ menu.Opening += (sender, args) =>
+ {
+ // find the DPI settings for the screen where this is going to land
+ var screenDpi = DpiHelper.GetDpi(menu.Location);
+ var scaledIconSize = DpiHelper.ScaleWithDpi(CoreConfig.IconSize, screenDpi);
+ menu.SuspendLayout();
+ var fontSize = DpiHelper.ScaleWithDpi(12f, screenDpi);
+ menu.Font = new Font(FontFamily.GenericSansSerif, fontSize, FontStyle.Regular, GraphicsUnit.Pixel);
+ menu.ImageScalingSize = scaledIconSize;
+ menu.ResumeLayout();
+ };
+
+ menu.Closing += delegate(object source, ToolStripDropDownClosingEventArgs eventArgs)
+ {
+ Log.DebugFormat("Close reason: {0}", eventArgs.CloseReason);
+ switch (eventArgs.CloseReason)
+ {
+ case ToolStripDropDownCloseReason.AppFocusChange:
+ if (menu.Tag == null)
+ {
+ // Do not allow the close if the tag is not set, this means the user clicked somewhere else.
+ eventArgs.Cancel = true;
+ }
+ else
+ {
+ Log.DebugFormat("Letting the menu 'close' as the tag is set to '{0}'", menu.Tag);
+ }
+
+ break;
+ case ToolStripDropDownCloseReason.ItemClicked:
+ case ToolStripDropDownCloseReason.CloseCalled:
+ // The ContextMenuStrip can be "closed" for these reasons.
+ break;
+ case ToolStripDropDownCloseReason.Keyboard:
+ // Dispose as the close is clicked
+ if (!captureDetails.HasDestination("Editor"))
+ {
+ surface.Dispose();
+ surface = null;
+ }
+
+ break;
+ default:
+ eventArgs.Cancel = true;
+ break;
+ }
+ };
+ menu.MouseEnter += delegate
+ {
+ // in case the menu has been unfocused, focus again so that dropdown menus will still open on mouseenter
+ if (!menu.ContainsFocus)
+ {
+ menu.Focus();
+ }
+ };
+ foreach (IDestination destination in destinations)
+ {
+ // Fix foreach loop variable for the delegate
+ ToolStripMenuItem item = destination.GetMenuItem(addDynamics, menu,
+ delegate(object sender, EventArgs e)
+ {
+ ToolStripMenuItem toolStripMenuItem = sender as ToolStripMenuItem;
+ IDestination clickedDestination = (IDestination) toolStripMenuItem?.Tag;
+ if (clickedDestination == null)
+ {
+ return;
+ }
+
+ menu.Tag = clickedDestination.Designation;
+ // Export
+ exportInformation = clickedDestination.ExportCapture(true, surface, captureDetails);
+ if (exportInformation != null && exportInformation.ExportMade)
+ {
+ Log.InfoFormat("Export to {0} success, closing menu", exportInformation.DestinationDescription);
+ // close menu if the destination wasn't the editor
+ menu.Close();
+
+ // Cleanup surface, only if there is no editor in the destinations and we didn't export to the editor
+ if (!captureDetails.HasDestination("Editor") && !"Editor".Equals(clickedDestination.Designation))
+ {
+ surface.Dispose();
+ surface = null;
+ }
+ }
+ else
+ {
+ Log.Info("Export cancelled or failed, showing menu again");
+
+ // Make sure a click besides the menu don't close it.
+ menu.Tag = null;
+
+ // This prevents the problem that the context menu shows in the task-bar
+ ShowMenuAtCursor(menu);
+ }
+ }
+ );
+ if (item != null)
+ {
+ menu.Items.Add(item);
+ }
+ }
+
+ // Close
+ menu.Items.Add(new ToolStripSeparator());
+ ToolStripMenuItem closeItem = new ToolStripMenuItem(Language.GetString("editor_close"))
+ {
+ Image = GreenshotResources.GetImage("Close.Image")
+ };
+ closeItem.Click += delegate
+ {
+ // This menu entry is the close itself, we can dispose the surface
+ menu.Close();
+ if (!captureDetails.HasDestination("Editor"))
+ {
+ surface.Dispose();
+ surface = null;
+ }
+ };
+ menu.Items.Add(closeItem);
+
+ ShowMenuAtCursor(menu);
+ return exportInformation;
+ }
+
+ ///
+ /// This method will show the supplied context menu at the mouse cursor, also makes sure it has focus and it's not visible in the taskbar.
+ ///
+ ///
+ private static void ShowMenuAtCursor(ContextMenuStrip menu)
+ {
+ // find a suitable location
+ Point location = Cursor.Position;
+ Rectangle menuRectangle = new Rectangle(location, menu.Size);
+
+ menuRectangle.Intersect(WindowCapture.GetScreenBounds());
+ if (menuRectangle.Height < menu.Height)
+ {
+ location.Offset(-40, -(menuRectangle.Height - menu.Height));
+ }
+ else
+ {
+ location.Offset(-40, -10);
+ }
+
+ // This prevents the problem that the context menu shows in the task-bar
+ User32.SetForegroundWindow(SimpleServiceProvider.Current.GetInstance().ContextMenuStrip.Handle);
+ menu.Show(location);
+ menu.Focus();
+
+ // Wait for the menu to close, so we can dispose it.
+ while (true)
+ {
+ if (menu.Visible)
+ {
+ Application.DoEvents();
+ Thread.Sleep(100);
+ }
+ else
+ {
+ menu.Dispose();
+ break;
+ }
+ }
+ }
+
+ ///
+ /// Return a menu item
+ ///
+ ///
+ ///
+ ///
+ /// ToolStripMenuItem
+ public virtual ToolStripMenuItem GetMenuItem(bool addDynamics, ContextMenuStrip menu, EventHandler destinationClickHandler)
+ {
+ var basisMenuItem = new ToolStripMenuItem(Description)
+ {
+ Image = DisplayIcon,
+ Tag = this,
+ Text = Description
+ };
+ AddTagEvents(basisMenuItem, menu, Description);
+ basisMenuItem.Click -= destinationClickHandler;
+ basisMenuItem.Click += destinationClickHandler;
+
+ if (IsDynamic && addDynamics)
+ {
+ basisMenuItem.DropDownOpening += delegate
+ {
+ if (basisMenuItem.DropDownItems.Count == 0)
+ {
+ List subDestinations = new List();
+ // Fixing Bug #3536968 by catching the COMException (every exception) and not displaying the "subDestinations"
+ try
+ {
+ subDestinations.AddRange(DynamicDestinations());
+ }
+ catch (Exception ex)
+ {
+ Log.ErrorFormat("Skipping {0}, due to the following error: {1}", Description, ex.Message);
+ }
+
+ if (subDestinations.Count > 0)
+ {
+ if (UseDynamicsOnly && subDestinations.Count == 1)
+ {
+ basisMenuItem.Tag = subDestinations[0];
+ basisMenuItem.Text = subDestinations[0].Description;
+ basisMenuItem.Click -= destinationClickHandler;
+ basisMenuItem.Click += destinationClickHandler;
+ }
+ else
+ {
+ foreach (IDestination subDestination in subDestinations)
+ {
+ var destinationMenuItem = new ToolStripMenuItem(subDestination.Description)
+ {
+ Tag = subDestination,
+ Image = subDestination.DisplayIcon
+ };
+ destinationMenuItem.Click += destinationClickHandler;
+ AddTagEvents(destinationMenuItem, menu, subDestination.Description);
+ basisMenuItem.DropDownItems.Add(destinationMenuItem);
+ }
+ }
+ }
+ }
+ };
+ }
+
+ return basisMenuItem;
+ }
+ }
}
\ No newline at end of file
diff --git a/src/GreenshotPlugin/Core/AbstractProcessor.cs b/src/Greenshot.Base/Core/AbstractProcessor.cs
similarity index 93%
rename from src/GreenshotPlugin/Core/AbstractProcessor.cs
rename to src/Greenshot.Base/Core/AbstractProcessor.cs
index 6e016e857..b82d11e46 100644
--- a/src/GreenshotPlugin/Core/AbstractProcessor.cs
+++ b/src/Greenshot.Base/Core/AbstractProcessor.cs
@@ -1,68 +1,68 @@
-/*
- * Greenshot - a free and open source screenshot tool
- * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
- *
- * For more information see: http://getgreenshot.org/
- * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 1 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-using System;
-using GreenshotPlugin.Interfaces;
-
-namespace GreenshotPlugin.Core
-{
- ///
- /// Description of AbstractProcessor.
- ///
- public abstract class AbstractProcessor : IProcessor
- {
- public virtual int CompareTo(object obj)
- {
- if (!(obj is IProcessor other))
- {
- return 1;
- }
-
- if (Priority == other.Priority)
- {
- return string.Compare(Description, other.Description, StringComparison.Ordinal);
- }
-
- return Priority - other.Priority;
- }
-
- public abstract string Designation { get; }
-
- public abstract string Description { get; }
-
- public virtual int Priority => 10;
-
- public void Dispose()
- {
- Dispose(true);
- GC.SuppressFinalize(this);
- }
-
- protected virtual void Dispose(bool disposing)
- {
- //if (disposing) {}
- }
-
- public virtual bool isActive => true;
-
- public abstract bool ProcessCapture(ISurface surface, ICaptureDetails captureDetails);
- }
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+using System;
+using Greenshot.Base.Interfaces;
+
+namespace Greenshot.Base.Core
+{
+ ///
+ /// Description of AbstractProcessor.
+ ///
+ public abstract class AbstractProcessor : IProcessor
+ {
+ public virtual int CompareTo(object obj)
+ {
+ if (!(obj is IProcessor other))
+ {
+ return 1;
+ }
+
+ if (Priority == other.Priority)
+ {
+ return string.Compare(Description, other.Description, StringComparison.Ordinal);
+ }
+
+ return Priority - other.Priority;
+ }
+
+ public abstract string Designation { get; }
+
+ public abstract string Description { get; }
+
+ public virtual int Priority => 10;
+
+ public void Dispose()
+ {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+
+ protected virtual void Dispose(bool disposing)
+ {
+ //if (disposing) {}
+ }
+
+ public virtual bool isActive => true;
+
+ public abstract bool ProcessCapture(ISurface surface, ICaptureDetails captureDetails);
+ }
}
\ No newline at end of file
diff --git a/src/GreenshotPlugin/Core/AccessibleHelper.cs b/src/Greenshot.Base/Core/AccessibleHelper.cs
similarity index 96%
rename from src/GreenshotPlugin/Core/AccessibleHelper.cs
rename to src/Greenshot.Base/Core/AccessibleHelper.cs
index 9a88f68b8..31fca4e83 100644
--- a/src/GreenshotPlugin/Core/AccessibleHelper.cs
+++ b/src/Greenshot.Base/Core/AccessibleHelper.cs
@@ -1,237 +1,237 @@
-/*
- * Greenshot - a free and open source screenshot tool
- * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
- *
- * For more information see: http://getgreenshot.org/
- * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 1 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-using System;
-using System.Collections.Generic;
-using System.Runtime.InteropServices;
-using Accessibility;
-
-namespace GreenshotPlugin.Core
-{
- ///
- /// See: http://social.msdn.microsoft.com/Forums/en-US/ieextensiondevelopment/thread/03a8c835-e9e4-405b-8345-6c3d36bc8941
- /// This should really be cleaned up, there is little OO behind this class!
- /// Maybe move the basic Accessible functions to WindowDetails!?
- ///
- public class Accessible
- {
- private static int AccessibleObjectFromWindow(IntPtr hWnd, OBJID idObject, ref IAccessible acc)
- {
- var guid = new Guid("{618736e0-3c3d-11cf-810c-00aa00389b71}"); // IAccessible
- object obj = null;
- int num = AccessibleObjectFromWindow(hWnd, (uint) idObject, ref guid, ref obj);
- acc = (IAccessible) obj;
- return num;
- }
-
- [DllImport("oleacc.dll")]
- private static extern int AccessibleObjectFromWindow(IntPtr hWnd, uint id, ref Guid iid, [In, Out, MarshalAs(UnmanagedType.IUnknown)]
- ref object ppvObject);
-
- [DllImport("oleacc.dll")]
- private static extern int AccessibleChildren(IAccessible paccContainer, int iChildStart, int cChildren, [In, Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 2)]
- object[] rgvarChildren, out int pcObtained);
-
- [DllImport("oleacc.dll", PreserveSig = false)]
- [return: MarshalAs(UnmanagedType.Interface)]
- public static extern object ObjectFromLresult(UIntPtr lResult, [MarshalAs(UnmanagedType.LPStruct)] Guid refiid, IntPtr wParam);
-
- private enum OBJID : uint
- {
- OBJID_WINDOW = 0x00000000,
- }
-
- private const int IE_ACTIVE_TAB = 2097154;
- private const int CHILDID_SELF = 0;
- private readonly IAccessible accessible;
-
- private Accessible[] Children
- {
- get
- {
- object[] res = GetAccessibleChildren(accessible, out var num);
- if (res == null)
- {
- return new Accessible[0];
- }
-
- List list = new List(res.Length);
- foreach (object obj in res)
- {
- if (obj is IAccessible acc)
- {
- list.Add(new Accessible(acc));
- }
- }
-
- return list.ToArray();
- }
- }
-
- private string Name
- {
- get { return accessible.get_accName(CHILDID_SELF); }
- }
-
- private int ChildCount
- {
- get { return accessible.accChildCount; }
- }
-
- public Accessible(IntPtr hWnd)
- {
- AccessibleObjectFromWindow(hWnd, OBJID.OBJID_WINDOW, ref accessible);
- if (accessible == null)
- {
- throw new Exception();
- }
- }
-
- public void ActivateIETab(int tabIndexToActivate)
- {
- var index = 0;
- foreach (Accessible accessor in Children)
- {
- foreach (var child in accessor.Children)
- {
- foreach (var tab in child.Children)
- {
- if (tabIndexToActivate >= child.ChildCount - 1)
- {
- return;
- }
-
- if (index == tabIndexToActivate)
- {
- tab.Activate();
- return;
- }
-
- index++;
- }
- }
- }
- }
-
- public string IEActiveTabCaption
- {
- get
- {
- foreach (Accessible accessor in Children)
- {
- foreach (var child in accessor.Children)
- {
- foreach (var tab in child.Children)
- {
- object tabIndex = tab.accessible.get_accState(0);
-
- if ((int) tabIndex == IE_ACTIVE_TAB)
- {
- return tab.Name;
- }
- }
- }
- }
-
- return string.Empty;
- }
- }
-
- public List IETabCaptions
- {
- get
- {
- var captionList = new List();
-
- foreach (Accessible accessor in Children)
- {
- foreach (var child in accessor.Children)
- {
- foreach (var tab in child.Children)
- {
- captionList.Add(tab.Name);
- }
- }
- }
-
- // TODO: Why again?
- if (captionList.Count > 0)
- {
- captionList.RemoveAt(captionList.Count - 1);
- }
-
- return captionList;
- }
- }
-
-
- public IEnumerable IETabUrls
- {
- get
- {
- foreach (Accessible accessor in Children)
- {
- foreach (var child in accessor.Children)
- {
- foreach (var tab in child.Children)
- {
- object tabIndex = tab.accessible.get_accState(CHILDID_SELF);
- var description = tab.accessible.get_accDescription(CHILDID_SELF);
- if (!string.IsNullOrEmpty(description))
- {
- if (description.Contains(Environment.NewLine))
- {
- var url = description.Substring(description.IndexOf(Environment.NewLine)).Trim();
- yield return url;
- }
- }
- }
- }
- }
- }
- }
-
- private Accessible(IAccessible acc)
- {
- accessible = acc ?? throw new Exception();
- }
-
- private void Activate()
- {
- accessible.accDoDefaultAction(CHILDID_SELF);
- }
-
- private static object[] GetAccessibleChildren(IAccessible ao, out int childs)
- {
- childs = 0;
- object[] ret = null;
- int count = ao.accChildCount;
-
- if (count > 0)
- {
- ret = new object[count];
- AccessibleChildren(ao, 0, count, ret, out childs);
- }
-
- return ret;
- }
- }
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+using System;
+using System.Collections.Generic;
+using System.Runtime.InteropServices;
+using Accessibility;
+
+namespace Greenshot.Base.Core
+{
+ ///
+ /// See: http://social.msdn.microsoft.com/Forums/en-US/ieextensiondevelopment/thread/03a8c835-e9e4-405b-8345-6c3d36bc8941
+ /// This should really be cleaned up, there is little OO behind this class!
+ /// Maybe move the basic Accessible functions to WindowDetails!?
+ ///
+ public class Accessible
+ {
+ private static int AccessibleObjectFromWindow(IntPtr hWnd, OBJID idObject, ref IAccessible acc)
+ {
+ var guid = new Guid("{618736e0-3c3d-11cf-810c-00aa00389b71}"); // IAccessible
+ object obj = null;
+ int num = AccessibleObjectFromWindow(hWnd, (uint) idObject, ref guid, ref obj);
+ acc = (IAccessible) obj;
+ return num;
+ }
+
+ [DllImport("oleacc.dll")]
+ private static extern int AccessibleObjectFromWindow(IntPtr hWnd, uint id, ref Guid iid, [In, Out, MarshalAs(UnmanagedType.IUnknown)]
+ ref object ppvObject);
+
+ [DllImport("oleacc.dll")]
+ private static extern int AccessibleChildren(IAccessible paccContainer, int iChildStart, int cChildren, [In, Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 2)]
+ object[] rgvarChildren, out int pcObtained);
+
+ [DllImport("oleacc.dll", PreserveSig = false)]
+ [return: MarshalAs(UnmanagedType.Interface)]
+ public static extern object ObjectFromLresult(UIntPtr lResult, [MarshalAs(UnmanagedType.LPStruct)] Guid refiid, IntPtr wParam);
+
+ private enum OBJID : uint
+ {
+ OBJID_WINDOW = 0x00000000,
+ }
+
+ private const int IE_ACTIVE_TAB = 2097154;
+ private const int CHILDID_SELF = 0;
+ private readonly IAccessible accessible;
+
+ private Accessible[] Children
+ {
+ get
+ {
+ object[] res = GetAccessibleChildren(accessible, out var num);
+ if (res == null)
+ {
+ return new Accessible[0];
+ }
+
+ List list = new List(res.Length);
+ foreach (object obj in res)
+ {
+ if (obj is IAccessible acc)
+ {
+ list.Add(new Accessible(acc));
+ }
+ }
+
+ return list.ToArray();
+ }
+ }
+
+ private string Name
+ {
+ get { return accessible.get_accName(CHILDID_SELF); }
+ }
+
+ private int ChildCount
+ {
+ get { return accessible.accChildCount; }
+ }
+
+ public Accessible(IntPtr hWnd)
+ {
+ AccessibleObjectFromWindow(hWnd, OBJID.OBJID_WINDOW, ref accessible);
+ if (accessible == null)
+ {
+ throw new Exception();
+ }
+ }
+
+ public void ActivateIETab(int tabIndexToActivate)
+ {
+ var index = 0;
+ foreach (Accessible accessor in Children)
+ {
+ foreach (var child in accessor.Children)
+ {
+ foreach (var tab in child.Children)
+ {
+ if (tabIndexToActivate >= child.ChildCount - 1)
+ {
+ return;
+ }
+
+ if (index == tabIndexToActivate)
+ {
+ tab.Activate();
+ return;
+ }
+
+ index++;
+ }
+ }
+ }
+ }
+
+ public string IEActiveTabCaption
+ {
+ get
+ {
+ foreach (Accessible accessor in Children)
+ {
+ foreach (var child in accessor.Children)
+ {
+ foreach (var tab in child.Children)
+ {
+ object tabIndex = tab.accessible.get_accState(0);
+
+ if ((int) tabIndex == IE_ACTIVE_TAB)
+ {
+ return tab.Name;
+ }
+ }
+ }
+ }
+
+ return string.Empty;
+ }
+ }
+
+ public List IETabCaptions
+ {
+ get
+ {
+ var captionList = new List();
+
+ foreach (Accessible accessor in Children)
+ {
+ foreach (var child in accessor.Children)
+ {
+ foreach (var tab in child.Children)
+ {
+ captionList.Add(tab.Name);
+ }
+ }
+ }
+
+ // TODO: Why again?
+ if (captionList.Count > 0)
+ {
+ captionList.RemoveAt(captionList.Count - 1);
+ }
+
+ return captionList;
+ }
+ }
+
+
+ public IEnumerable IETabUrls
+ {
+ get
+ {
+ foreach (Accessible accessor in Children)
+ {
+ foreach (var child in accessor.Children)
+ {
+ foreach (var tab in child.Children)
+ {
+ object tabIndex = tab.accessible.get_accState(CHILDID_SELF);
+ var description = tab.accessible.get_accDescription(CHILDID_SELF);
+ if (!string.IsNullOrEmpty(description))
+ {
+ if (description.Contains(Environment.NewLine))
+ {
+ var url = description.Substring(description.IndexOf(Environment.NewLine)).Trim();
+ yield return url;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ private Accessible(IAccessible acc)
+ {
+ accessible = acc ?? throw new Exception();
+ }
+
+ private void Activate()
+ {
+ accessible.accDoDefaultAction(CHILDID_SELF);
+ }
+
+ private static object[] GetAccessibleChildren(IAccessible ao, out int childs)
+ {
+ childs = 0;
+ object[] ret = null;
+ int count = ao.accChildCount;
+
+ if (count > 0)
+ {
+ ret = new object[count];
+ AccessibleChildren(ao, 0, count, ret, out childs);
+ }
+
+ return ret;
+ }
+ }
}
\ No newline at end of file
diff --git a/src/GreenshotPlugin/Core/AnimationHelpers.cs b/src/Greenshot.Base/Core/AnimationHelpers.cs
similarity index 96%
rename from src/GreenshotPlugin/Core/AnimationHelpers.cs
rename to src/Greenshot.Base/Core/AnimationHelpers.cs
index ed8282ac6..0d96016ab 100644
--- a/src/GreenshotPlugin/Core/AnimationHelpers.cs
+++ b/src/Greenshot.Base/Core/AnimationHelpers.cs
@@ -1,610 +1,610 @@
-/*
- * Greenshot - a free and open source screenshot tool
- * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
- *
- * For more information see: http://getgreenshot.org/
- * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 1 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-using System;
-using System.Drawing;
-using System.Collections.Generic;
-
-namespace GreenshotPlugin.Core
-{
- ///
- /// Helper interface for passing base type
- ///
- public interface IAnimator
- {
- ///
- /// Is there a next frame?
- ///
- bool HasNext { get; }
-
- ///
- /// The amount of frames
- ///
- int Frames { get; }
-
- ///
- /// Current frame number
- ///
- int CurrentFrameNr { get; }
- }
-
- ///
- /// This class is used to store a animation leg
- ///
- internal class AnimationLeg
- {
- public T Destination { get; set; }
-
- public int Frames { get; set; }
-
- public EasingType EasingType { get; set; }
-
- public EasingMode EasingMode { get; set; }
- }
-
- ///
- /// Base class for the animation logic, this only implements Properties and a constructor
- ///
- /// Type for the animation, like Point/Rectangle/Size
- public abstract class AnimatorBase : IAnimator
- {
- private readonly Queue> _queue = new Queue>();
-
- ///
- /// Constructor
- ///
- ///
- ///
- ///
- ///
- ///
- public AnimatorBase(T first, T last, int frames, EasingType easingType, EasingMode easingMode)
- {
- First = first;
- Last = last;
- Frames = frames;
- Current = first;
- EasingType = easingType;
- EasingMode = easingMode;
- }
-
- ///
- /// The amount of frames
- ///
- public int Frames { get; private set; }
-
- ///
- /// Current frame number
- ///
- public int CurrentFrameNr { get; private set; }
-
- ///
- /// First animation value
- ///
- public T First { get; private set; }
-
- ///
- /// Last animation value, of this "leg"
- ///
- public T Last { get; private set; }
-
- ///
- /// Final animation value, this is including the legs
- ///
- public T Final
- {
- get
- {
- if (_queue.Count == 0)
- {
- return Last;
- }
-
- return _queue.ToArray()[_queue.Count - 1].Destination;
- }
- }
-
- ///
- /// This restarts the current animation and changes the last frame
- ///
- ///
- public void ChangeDestination(T newDestination)
- {
- ChangeDestination(newDestination, Frames);
- }
-
- ///
- /// This restarts the current animation and changes the last frame
- ///
- ///
- ///
- public void ChangeDestination(T newDestination, int frames)
- {
- _queue.Clear();
- First = Current;
- CurrentFrameNr = 0;
- Frames = frames;
- Last = newDestination;
- }
-
- ///
- /// Queue the destination, it will be used to continue at the last frame
- /// All values will stay the same
- ///
- ///
- public void QueueDestinationLeg(T queuedDestination)
- {
- QueueDestinationLeg(queuedDestination, Frames, EasingType, EasingMode);
- }
-
- ///
- /// Queue the destination, it will be used to continue at the last frame
- ///
- ///
- ///
- public void QueueDestinationLeg(T queuedDestination, int frames)
- {
- QueueDestinationLeg(queuedDestination, frames, EasingType, EasingMode);
- }
-
- ///
- /// Queue the destination, it will be used to continue at the last frame
- ///
- ///
- ///
- /// EasingType
- public void QueueDestinationLeg(T queuedDestination, int frames, EasingType easingType)
- {
- QueueDestinationLeg(queuedDestination, frames, easingType, EasingMode);
- }
-
- ///
- /// Queue the destination, it will be used to continue at the last frame
- ///
- ///
- ///
- ///
- ///
- public void QueueDestinationLeg(T queuedDestination, int frames, EasingType easingType, EasingMode easingMode)
- {
- AnimationLeg leg = new AnimationLeg
- {
- Destination = queuedDestination,
- Frames = frames,
- EasingType = easingType,
- EasingMode = easingMode
- };
- _queue.Enqueue(leg);
- }
-
- ///
- /// The EasingType to use for the animation
- ///
- public EasingType EasingType { get; set; }
-
- ///
- /// The EasingMode to use for the animation
- ///
- public EasingMode EasingMode { get; set; }
-
- ///
- /// Get the easing value, which is from 0-1 and depends on the frame
- ///
- protected double EasingValue
- {
- get =>
- EasingMode switch
- {
- EasingMode.EaseOut => Easing.EaseOut(CurrentFrameNr / (double) Frames, EasingType),
- EasingMode.EaseInOut => Easing.EaseInOut(CurrentFrameNr / (double) Frames, EasingType),
- EasingMode.EaseIn => Easing.EaseIn(CurrentFrameNr / (double) Frames, EasingType),
- _ => Easing.EaseIn(CurrentFrameNr / (double) Frames, EasingType)
- };
- }
-
- ///
- /// Get the current (previous) frame object
- ///
- public virtual T Current { get; set; }
-
- ///
- /// Returns if there are any frame left, and if this is the case than the frame is increased.
- ///
- public virtual bool NextFrame
- {
- get
- {
- if (CurrentFrameNr < Frames)
- {
- CurrentFrameNr++;
- return true;
- }
-
- if (_queue.Count > 0)
- {
- First = Current;
- CurrentFrameNr = 0;
- AnimationLeg nextLeg = _queue.Dequeue();
- Last = nextLeg.Destination;
- Frames = nextLeg.Frames;
- EasingType = nextLeg.EasingType;
- EasingMode = nextLeg.EasingMode;
- return true;
- }
-
- return false;
- }
- }
-
- ///
- /// Are there more frames to animate?
- ///
- public virtual bool HasNext
- {
- get
- {
- if (CurrentFrameNr < Frames)
- {
- return true;
- }
-
- return _queue.Count > 0;
- }
- }
-
- ///
- /// Get the next animation frame value object
- ///
- ///
- public abstract T Next();
- }
-
- ///
- /// Implementation of the RectangleAnimator
- ///
- public class RectangleAnimator : AnimatorBase
- {
- public RectangleAnimator(Rectangle first, Rectangle last, int frames)
- : base(first, last, frames, EasingType.Linear, EasingMode.EaseIn)
- {
- }
-
- public RectangleAnimator(Rectangle first, Rectangle last, int frames, EasingType easingType)
- : base(first, last, frames, easingType, EasingMode.EaseIn)
- {
- }
-
- public RectangleAnimator(Rectangle first, Rectangle last, int frames, EasingType easingType, EasingMode easingMode)
- : base(first, last, frames, easingType, easingMode)
- {
- }
-
- ///
- /// Calculate the next frame object
- ///
- /// Rectangle
- public override Rectangle Next()
- {
- if (NextFrame)
- {
- double easingValue = EasingValue;
- double dx = Last.X - First.X;
- double dy = Last.Y - First.Y;
-
- int x = First.X + (int) (easingValue * dx);
- int y = First.Y + (int) (easingValue * dy);
- double dw = Last.Width - First.Width;
- double dh = Last.Height - First.Height;
- int width = First.Width + (int) (easingValue * dw);
- int height = First.Height + (int) (easingValue * dh);
- Current = new Rectangle(x, y, width, height);
- }
-
- return Current;
- }
- }
-
- ///
- /// Implementation of the PointAnimator
- ///
- public class PointAnimator : AnimatorBase
- {
- public PointAnimator(Point first, Point last, int frames)
- : base(first, last, frames, EasingType.Linear, EasingMode.EaseIn)
- {
- }
-
- public PointAnimator(Point first, Point last, int frames, EasingType easingType)
- : base(first, last, frames, easingType, EasingMode.EaseIn)
- {
- }
-
- public PointAnimator(Point first, Point last, int frames, EasingType easingType, EasingMode easingMode)
- : base(first, last, frames, easingType, easingMode)
- {
- }
-
- ///
- /// Calculate the next frame value
- ///
- /// Point
- public override Point Next()
- {
- if (NextFrame)
- {
- double easingValue = EasingValue;
- double dx = Last.X - First.X;
- double dy = Last.Y - First.Y;
-
- int x = First.X + (int) (easingValue * dx);
- int y = First.Y + (int) (easingValue * dy);
- Current = new Point(x, y);
- }
-
- return Current;
- }
- }
-
- ///
- /// Implementation of the SizeAnimator
- ///
- public class SizeAnimator : AnimatorBase
- {
- public SizeAnimator(Size first, Size last, int frames)
- : base(first, last, frames, EasingType.Linear, EasingMode.EaseIn)
- {
- }
-
- public SizeAnimator(Size first, Size last, int frames, EasingType easingType)
- : base(first, last, frames, easingType, EasingMode.EaseIn)
- {
- }
-
- public SizeAnimator(Size first, Size last, int frames, EasingType easingType, EasingMode easingMode)
- : base(first, last, frames, easingType, easingMode)
- {
- }
-
- ///
- /// Calculate the next frame values
- ///
- /// Size
- public override Size Next()
- {
- if (NextFrame)
- {
- double easingValue = EasingValue;
- double dw = Last.Width - First.Width;
- double dh = Last.Height - First.Height;
- int width = First.Width + (int) (easingValue * dw);
- int height = First.Height + (int) (easingValue * dh);
- Current = new Size(width, height);
- }
-
- return Current;
- }
- }
-
- ///
- /// Implementation of the ColorAnimator
- ///
- public class ColorAnimator : AnimatorBase
- {
- public ColorAnimator(Color first, Color last, int frames)
- : base(first, last, frames, EasingType.Linear, EasingMode.EaseIn)
- {
- }
-
- public ColorAnimator(Color first, Color last, int frames, EasingType easingType)
- : base(first, last, frames, easingType, EasingMode.EaseIn)
- {
- }
-
- public ColorAnimator(Color first, Color last, int frames, EasingType easingType, EasingMode easingMode)
- : base(first, last, frames, easingType, easingMode)
- {
- }
-
- ///
- /// Calculate the next frame values
- ///
- /// Color
- public override Color Next()
- {
- if (NextFrame)
- {
- double easingValue = EasingValue;
- double da = Last.A - First.A;
- double dr = Last.R - First.R;
- double dg = Last.G - First.G;
- double db = Last.B - First.B;
- int a = First.A + (int) (easingValue * da);
- int r = First.R + (int) (easingValue * dr);
- int g = First.G + (int) (easingValue * dg);
- int b = First.B + (int) (easingValue * db);
- Current = Color.FromArgb(a, r, g, b);
- }
-
- return Current;
- }
- }
-
- ///
- /// Implementation of the IntAnimator
- ///
- public class IntAnimator : AnimatorBase
- {
- public IntAnimator(int first, int last, int frames)
- : base(first, last, frames, EasingType.Linear, EasingMode.EaseIn)
- {
- }
-
- public IntAnimator(int first, int last, int frames, EasingType easingType)
- : base(first, last, frames, easingType, EasingMode.EaseIn)
- {
- }
-
- public IntAnimator(int first, int last, int frames, EasingType easingType, EasingMode easingMode)
- : base(first, last, frames, easingType, easingMode)
- {
- }
-
- ///
- /// Calculate the next frame values
- ///
- /// int
- public override int Next()
- {
- if (NextFrame)
- {
- double easingValue = EasingValue;
- double delta = Last - First;
- Current = First + (int) (easingValue * delta);
- }
-
- return Current;
- }
- }
-
- ///
- /// Easing logic, to make the animations more "fluent"
- ///
- public static class Easing
- {
- // Adapted from http://www.robertpenner.com/easing/penner_chapter7_tweening.pdf
-
- public static double Ease(double linearStep, double acceleration, EasingType type)
- {
- double easedStep = acceleration > 0 ? EaseIn(linearStep, type) : acceleration < 0 ? EaseOut(linearStep, type) : linearStep;
- // Lerp:
- return ((easedStep - linearStep) * Math.Abs(acceleration) + linearStep);
- }
-
- public static double EaseIn(double linearStep, EasingType type) =>
- type switch
- {
- EasingType.Step => (linearStep < 0.5 ? 0 : 1),
- EasingType.Linear => linearStep,
- EasingType.Sine => Sine.EaseIn(linearStep),
- EasingType.Quadratic => Power.EaseIn(linearStep, 2),
- EasingType.Cubic => Power.EaseIn(linearStep, 3),
- EasingType.Quartic => Power.EaseIn(linearStep, 4),
- EasingType.Quintic => Power.EaseIn(linearStep, 5),
- _ => throw new NotImplementedException()
- };
-
- public static double EaseOut(double linearStep, EasingType type) =>
- type switch
- {
- EasingType.Step => (linearStep < 0.5 ? 0 : 1),
- EasingType.Linear => linearStep,
- EasingType.Sine => Sine.EaseOut(linearStep),
- EasingType.Quadratic => Power.EaseOut(linearStep, 2),
- EasingType.Cubic => Power.EaseOut(linearStep, 3),
- EasingType.Quartic => Power.EaseOut(linearStep, 4),
- EasingType.Quintic => Power.EaseOut(linearStep, 5),
- _ => throw new NotImplementedException()
- };
-
- public static double EaseInOut(double linearStep, EasingType easeInType, EasingType easeOutType)
- {
- return linearStep < 0.5 ? EaseInOut(linearStep, easeInType) : EaseInOut(linearStep, easeOutType);
- }
-
- public static double EaseInOut(double linearStep, EasingType type) =>
- type switch
- {
- EasingType.Step => (linearStep < 0.5 ? 0 : 1),
- EasingType.Linear => linearStep,
- EasingType.Sine => Sine.EaseInOut(linearStep),
- EasingType.Quadratic => Power.EaseInOut(linearStep, 2),
- EasingType.Cubic => Power.EaseInOut(linearStep, 3),
- EasingType.Quartic => Power.EaseInOut(linearStep, 4),
- EasingType.Quintic => Power.EaseInOut(linearStep, 5),
- _ => throw new NotImplementedException()
- };
-
- private static class Sine
- {
- public static double EaseIn(double s)
- {
- return Math.Sin(s * (Math.PI / 2) - (Math.PI / 2)) + 1;
- }
-
- public static double EaseOut(double s)
- {
- return Math.Sin(s * (Math.PI / 2));
- }
-
- public static double EaseInOut(double s)
- {
- return Math.Sin(s * Math.PI - (Math.PI / 2) + 1) / 2;
- }
- }
-
- private static class Power
- {
- public static double EaseIn(double s, int power)
- {
- return Math.Pow(s, power);
- }
-
- public static double EaseOut(double s, int power)
- {
- var sign = power % 2 == 0 ? -1 : 1;
- return sign * (Math.Pow(s - 1, power) + sign);
- }
-
- public static double EaseInOut(double s, int power)
- {
- s *= 2;
- if (s < 1)
- {
- return EaseIn(s, power) / 2;
- }
-
- var sign = power % 2 == 0 ? -1 : 1;
- return (sign / 2.0 * (Math.Pow(s - 2, power) + sign * 2));
- }
- }
- }
-
- ///
- /// This defines the way the animation works
- ///
- public enum EasingType
- {
- Step,
- Linear,
- Sine,
- Quadratic,
- Cubic,
- Quartic,
- Quintic
- }
-
- public enum EasingMode
- {
- EaseIn,
- EaseOut,
- EaseInOut
- }
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+using System;
+using System.Collections.Generic;
+using System.Drawing;
+
+namespace Greenshot.Base.Core
+{
+ ///
+ /// Helper interface for passing base type
+ ///
+ public interface IAnimator
+ {
+ ///
+ /// Is there a next frame?
+ ///
+ bool HasNext { get; }
+
+ ///
+ /// The amount of frames
+ ///
+ int Frames { get; }
+
+ ///
+ /// Current frame number
+ ///
+ int CurrentFrameNr { get; }
+ }
+
+ ///
+ /// This class is used to store a animation leg
+ ///
+ internal class AnimationLeg
+ {
+ public T Destination { get; set; }
+
+ public int Frames { get; set; }
+
+ public EasingType EasingType { get; set; }
+
+ public EasingMode EasingMode { get; set; }
+ }
+
+ ///
+ /// Base class for the animation logic, this only implements Properties and a constructor
+ ///
+ /// Type for the animation, like Point/Rectangle/Size
+ public abstract class AnimatorBase : IAnimator
+ {
+ private readonly Queue> _queue = new Queue>();
+
+ ///
+ /// Constructor
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public AnimatorBase(T first, T last, int frames, EasingType easingType, EasingMode easingMode)
+ {
+ First = first;
+ Last = last;
+ Frames = frames;
+ Current = first;
+ EasingType = easingType;
+ EasingMode = easingMode;
+ }
+
+ ///
+ /// The amount of frames
+ ///
+ public int Frames { get; private set; }
+
+ ///
+ /// Current frame number
+ ///
+ public int CurrentFrameNr { get; private set; }
+
+ ///
+ /// First animation value
+ ///
+ public T First { get; private set; }
+
+ ///
+ /// Last animation value, of this "leg"
+ ///
+ public T Last { get; private set; }
+
+ ///
+ /// Final animation value, this is including the legs
+ ///
+ public T Final
+ {
+ get
+ {
+ if (_queue.Count == 0)
+ {
+ return Last;
+ }
+
+ return _queue.ToArray()[_queue.Count - 1].Destination;
+ }
+ }
+
+ ///
+ /// This restarts the current animation and changes the last frame
+ ///
+ ///
+ public void ChangeDestination(T newDestination)
+ {
+ ChangeDestination(newDestination, Frames);
+ }
+
+ ///
+ /// This restarts the current animation and changes the last frame
+ ///
+ ///
+ ///
+ public void ChangeDestination(T newDestination, int frames)
+ {
+ _queue.Clear();
+ First = Current;
+ CurrentFrameNr = 0;
+ Frames = frames;
+ Last = newDestination;
+ }
+
+ ///
+ /// Queue the destination, it will be used to continue at the last frame
+ /// All values will stay the same
+ ///
+ ///
+ public void QueueDestinationLeg(T queuedDestination)
+ {
+ QueueDestinationLeg(queuedDestination, Frames, EasingType, EasingMode);
+ }
+
+ ///
+ /// Queue the destination, it will be used to continue at the last frame
+ ///
+ ///
+ ///
+ public void QueueDestinationLeg(T queuedDestination, int frames)
+ {
+ QueueDestinationLeg(queuedDestination, frames, EasingType, EasingMode);
+ }
+
+ ///
+ /// Queue the destination, it will be used to continue at the last frame
+ ///
+ ///
+ ///
+ /// EasingType
+ public void QueueDestinationLeg(T queuedDestination, int frames, EasingType easingType)
+ {
+ QueueDestinationLeg(queuedDestination, frames, easingType, EasingMode);
+ }
+
+ ///
+ /// Queue the destination, it will be used to continue at the last frame
+ ///
+ ///
+ ///
+ ///
+ ///
+ public void QueueDestinationLeg(T queuedDestination, int frames, EasingType easingType, EasingMode easingMode)
+ {
+ AnimationLeg leg = new AnimationLeg
+ {
+ Destination = queuedDestination,
+ Frames = frames,
+ EasingType = easingType,
+ EasingMode = easingMode
+ };
+ _queue.Enqueue(leg);
+ }
+
+ ///
+ /// The EasingType to use for the animation
+ ///
+ public EasingType EasingType { get; set; }
+
+ ///
+ /// The EasingMode to use for the animation
+ ///
+ public EasingMode EasingMode { get; set; }
+
+ ///
+ /// Get the easing value, which is from 0-1 and depends on the frame
+ ///
+ protected double EasingValue
+ {
+ get =>
+ EasingMode switch
+ {
+ EasingMode.EaseOut => Easing.EaseOut(CurrentFrameNr / (double) Frames, EasingType),
+ EasingMode.EaseInOut => Easing.EaseInOut(CurrentFrameNr / (double) Frames, EasingType),
+ EasingMode.EaseIn => Easing.EaseIn(CurrentFrameNr / (double) Frames, EasingType),
+ _ => Easing.EaseIn(CurrentFrameNr / (double) Frames, EasingType)
+ };
+ }
+
+ ///
+ /// Get the current (previous) frame object
+ ///
+ public virtual T Current { get; set; }
+
+ ///
+ /// Returns if there are any frame left, and if this is the case than the frame is increased.
+ ///
+ public virtual bool NextFrame
+ {
+ get
+ {
+ if (CurrentFrameNr < Frames)
+ {
+ CurrentFrameNr++;
+ return true;
+ }
+
+ if (_queue.Count > 0)
+ {
+ First = Current;
+ CurrentFrameNr = 0;
+ AnimationLeg nextLeg = _queue.Dequeue();
+ Last = nextLeg.Destination;
+ Frames = nextLeg.Frames;
+ EasingType = nextLeg.EasingType;
+ EasingMode = nextLeg.EasingMode;
+ return true;
+ }
+
+ return false;
+ }
+ }
+
+ ///
+ /// Are there more frames to animate?
+ ///
+ public virtual bool HasNext
+ {
+ get
+ {
+ if (CurrentFrameNr < Frames)
+ {
+ return true;
+ }
+
+ return _queue.Count > 0;
+ }
+ }
+
+ ///
+ /// Get the next animation frame value object
+ ///
+ ///
+ public abstract T Next();
+ }
+
+ ///
+ /// Implementation of the RectangleAnimator
+ ///
+ public class RectangleAnimator : AnimatorBase
+ {
+ public RectangleAnimator(Rectangle first, Rectangle last, int frames)
+ : base(first, last, frames, EasingType.Linear, EasingMode.EaseIn)
+ {
+ }
+
+ public RectangleAnimator(Rectangle first, Rectangle last, int frames, EasingType easingType)
+ : base(first, last, frames, easingType, EasingMode.EaseIn)
+ {
+ }
+
+ public RectangleAnimator(Rectangle first, Rectangle last, int frames, EasingType easingType, EasingMode easingMode)
+ : base(first, last, frames, easingType, easingMode)
+ {
+ }
+
+ ///
+ /// Calculate the next frame object
+ ///
+ /// Rectangle
+ public override Rectangle Next()
+ {
+ if (NextFrame)
+ {
+ double easingValue = EasingValue;
+ double dx = Last.X - First.X;
+ double dy = Last.Y - First.Y;
+
+ int x = First.X + (int) (easingValue * dx);
+ int y = First.Y + (int) (easingValue * dy);
+ double dw = Last.Width - First.Width;
+ double dh = Last.Height - First.Height;
+ int width = First.Width + (int) (easingValue * dw);
+ int height = First.Height + (int) (easingValue * dh);
+ Current = new Rectangle(x, y, width, height);
+ }
+
+ return Current;
+ }
+ }
+
+ ///
+ /// Implementation of the PointAnimator
+ ///
+ public class PointAnimator : AnimatorBase
+ {
+ public PointAnimator(Point first, Point last, int frames)
+ : base(first, last, frames, EasingType.Linear, EasingMode.EaseIn)
+ {
+ }
+
+ public PointAnimator(Point first, Point last, int frames, EasingType easingType)
+ : base(first, last, frames, easingType, EasingMode.EaseIn)
+ {
+ }
+
+ public PointAnimator(Point first, Point last, int frames, EasingType easingType, EasingMode easingMode)
+ : base(first, last, frames, easingType, easingMode)
+ {
+ }
+
+ ///
+ /// Calculate the next frame value
+ ///
+ /// Point
+ public override Point Next()
+ {
+ if (NextFrame)
+ {
+ double easingValue = EasingValue;
+ double dx = Last.X - First.X;
+ double dy = Last.Y - First.Y;
+
+ int x = First.X + (int) (easingValue * dx);
+ int y = First.Y + (int) (easingValue * dy);
+ Current = new Point(x, y);
+ }
+
+ return Current;
+ }
+ }
+
+ ///
+ /// Implementation of the SizeAnimator
+ ///
+ public class SizeAnimator : AnimatorBase
+ {
+ public SizeAnimator(Size first, Size last, int frames)
+ : base(first, last, frames, EasingType.Linear, EasingMode.EaseIn)
+ {
+ }
+
+ public SizeAnimator(Size first, Size last, int frames, EasingType easingType)
+ : base(first, last, frames, easingType, EasingMode.EaseIn)
+ {
+ }
+
+ public SizeAnimator(Size first, Size last, int frames, EasingType easingType, EasingMode easingMode)
+ : base(first, last, frames, easingType, easingMode)
+ {
+ }
+
+ ///
+ /// Calculate the next frame values
+ ///
+ /// Size
+ public override Size Next()
+ {
+ if (NextFrame)
+ {
+ double easingValue = EasingValue;
+ double dw = Last.Width - First.Width;
+ double dh = Last.Height - First.Height;
+ int width = First.Width + (int) (easingValue * dw);
+ int height = First.Height + (int) (easingValue * dh);
+ Current = new Size(width, height);
+ }
+
+ return Current;
+ }
+ }
+
+ ///
+ /// Implementation of the ColorAnimator
+ ///
+ public class ColorAnimator : AnimatorBase
+ {
+ public ColorAnimator(Color first, Color last, int frames)
+ : base(first, last, frames, EasingType.Linear, EasingMode.EaseIn)
+ {
+ }
+
+ public ColorAnimator(Color first, Color last, int frames, EasingType easingType)
+ : base(first, last, frames, easingType, EasingMode.EaseIn)
+ {
+ }
+
+ public ColorAnimator(Color first, Color last, int frames, EasingType easingType, EasingMode easingMode)
+ : base(first, last, frames, easingType, easingMode)
+ {
+ }
+
+ ///
+ /// Calculate the next frame values
+ ///
+ /// Color
+ public override Color Next()
+ {
+ if (NextFrame)
+ {
+ double easingValue = EasingValue;
+ double da = Last.A - First.A;
+ double dr = Last.R - First.R;
+ double dg = Last.G - First.G;
+ double db = Last.B - First.B;
+ int a = First.A + (int) (easingValue * da);
+ int r = First.R + (int) (easingValue * dr);
+ int g = First.G + (int) (easingValue * dg);
+ int b = First.B + (int) (easingValue * db);
+ Current = Color.FromArgb(a, r, g, b);
+ }
+
+ return Current;
+ }
+ }
+
+ ///
+ /// Implementation of the IntAnimator
+ ///
+ public class IntAnimator : AnimatorBase
+ {
+ public IntAnimator(int first, int last, int frames)
+ : base(first, last, frames, EasingType.Linear, EasingMode.EaseIn)
+ {
+ }
+
+ public IntAnimator(int first, int last, int frames, EasingType easingType)
+ : base(first, last, frames, easingType, EasingMode.EaseIn)
+ {
+ }
+
+ public IntAnimator(int first, int last, int frames, EasingType easingType, EasingMode easingMode)
+ : base(first, last, frames, easingType, easingMode)
+ {
+ }
+
+ ///
+ /// Calculate the next frame values
+ ///
+ /// int
+ public override int Next()
+ {
+ if (NextFrame)
+ {
+ double easingValue = EasingValue;
+ double delta = Last - First;
+ Current = First + (int) (easingValue * delta);
+ }
+
+ return Current;
+ }
+ }
+
+ ///
+ /// Easing logic, to make the animations more "fluent"
+ ///
+ public static class Easing
+ {
+ // Adapted from http://www.robertpenner.com/easing/penner_chapter7_tweening.pdf
+
+ public static double Ease(double linearStep, double acceleration, EasingType type)
+ {
+ double easedStep = acceleration > 0 ? EaseIn(linearStep, type) : acceleration < 0 ? EaseOut(linearStep, type) : linearStep;
+ // Lerp:
+ return ((easedStep - linearStep) * Math.Abs(acceleration) + linearStep);
+ }
+
+ public static double EaseIn(double linearStep, EasingType type) =>
+ type switch
+ {
+ EasingType.Step => (linearStep < 0.5 ? 0 : 1),
+ EasingType.Linear => linearStep,
+ EasingType.Sine => Sine.EaseIn(linearStep),
+ EasingType.Quadratic => Power.EaseIn(linearStep, 2),
+ EasingType.Cubic => Power.EaseIn(linearStep, 3),
+ EasingType.Quartic => Power.EaseIn(linearStep, 4),
+ EasingType.Quintic => Power.EaseIn(linearStep, 5),
+ _ => throw new NotImplementedException()
+ };
+
+ public static double EaseOut(double linearStep, EasingType type) =>
+ type switch
+ {
+ EasingType.Step => (linearStep < 0.5 ? 0 : 1),
+ EasingType.Linear => linearStep,
+ EasingType.Sine => Sine.EaseOut(linearStep),
+ EasingType.Quadratic => Power.EaseOut(linearStep, 2),
+ EasingType.Cubic => Power.EaseOut(linearStep, 3),
+ EasingType.Quartic => Power.EaseOut(linearStep, 4),
+ EasingType.Quintic => Power.EaseOut(linearStep, 5),
+ _ => throw new NotImplementedException()
+ };
+
+ public static double EaseInOut(double linearStep, EasingType easeInType, EasingType easeOutType)
+ {
+ return linearStep < 0.5 ? EaseInOut(linearStep, easeInType) : EaseInOut(linearStep, easeOutType);
+ }
+
+ public static double EaseInOut(double linearStep, EasingType type) =>
+ type switch
+ {
+ EasingType.Step => (linearStep < 0.5 ? 0 : 1),
+ EasingType.Linear => linearStep,
+ EasingType.Sine => Sine.EaseInOut(linearStep),
+ EasingType.Quadratic => Power.EaseInOut(linearStep, 2),
+ EasingType.Cubic => Power.EaseInOut(linearStep, 3),
+ EasingType.Quartic => Power.EaseInOut(linearStep, 4),
+ EasingType.Quintic => Power.EaseInOut(linearStep, 5),
+ _ => throw new NotImplementedException()
+ };
+
+ private static class Sine
+ {
+ public static double EaseIn(double s)
+ {
+ return Math.Sin(s * (Math.PI / 2) - (Math.PI / 2)) + 1;
+ }
+
+ public static double EaseOut(double s)
+ {
+ return Math.Sin(s * (Math.PI / 2));
+ }
+
+ public static double EaseInOut(double s)
+ {
+ return Math.Sin(s * Math.PI - (Math.PI / 2) + 1) / 2;
+ }
+ }
+
+ private static class Power
+ {
+ public static double EaseIn(double s, int power)
+ {
+ return Math.Pow(s, power);
+ }
+
+ public static double EaseOut(double s, int power)
+ {
+ var sign = power % 2 == 0 ? -1 : 1;
+ return sign * (Math.Pow(s - 1, power) + sign);
+ }
+
+ public static double EaseInOut(double s, int power)
+ {
+ s *= 2;
+ if (s < 1)
+ {
+ return EaseIn(s, power) / 2;
+ }
+
+ var sign = power % 2 == 0 ? -1 : 1;
+ return (sign / 2.0 * (Math.Pow(s - 2, power) + sign * 2));
+ }
+ }
+ }
+
+ ///
+ /// This defines the way the animation works
+ ///
+ public enum EasingType
+ {
+ Step,
+ Linear,
+ Sine,
+ Quadratic,
+ Cubic,
+ Quartic,
+ Quintic
+ }
+
+ public enum EasingMode
+ {
+ EaseIn,
+ EaseOut,
+ EaseInOut
+ }
}
\ No newline at end of file
diff --git a/src/GreenshotPlugin/Core/BinaryStructHelper.cs b/src/Greenshot.Base/Core/BinaryStructHelper.cs
similarity index 96%
rename from src/GreenshotPlugin/Core/BinaryStructHelper.cs
rename to src/Greenshot.Base/Core/BinaryStructHelper.cs
index 1da7254f7..257d8dd2c 100644
--- a/src/GreenshotPlugin/Core/BinaryStructHelper.cs
+++ b/src/Greenshot.Base/Core/BinaryStructHelper.cs
@@ -1,108 +1,108 @@
-/*
- * Greenshot - a free and open source screenshot tool
- * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
- *
- * For more information see: http://getgreenshot.org/
- * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 1 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-using System;
-using System.Runtime.InteropServices;
-
-namespace GreenshotPlugin.Core
-{
- ///
- /// A helper class which does the mashalling for structs
- ///
- public static class BinaryStructHelper
- {
- ///
- /// Get a struct from a byte array
- ///
- /// typeof struct
- /// byte[]
- /// struct
- public static T FromByteArray(byte[] bytes) where T : struct
- {
- IntPtr ptr = IntPtr.Zero;
- try
- {
- int size = Marshal.SizeOf(typeof(T));
- ptr = Marshal.AllocHGlobal(size);
- Marshal.Copy(bytes, 0, ptr, size);
- return FromIntPtr(ptr);
- }
- finally
- {
- if (ptr != IntPtr.Zero)
- {
- Marshal.FreeHGlobal(ptr);
- }
- }
- }
-
- ///
- /// Get a struct from a byte array
- ///
- /// typeof struct
- /// Pointer to the structor to return
- /// struct
- public static T FromIntPtr(IntPtr intPtr) where T : struct
- {
- object obj = Marshal.PtrToStructure(intPtr, typeof(T));
- return (T) obj;
- }
-
- ///
- /// copy a struct to a byte array
- ///
- /// typeof struct
- /// struct
- /// byte[]
- public static byte[] ToByteArray(T obj) where T : struct
- {
- IntPtr ptr = IntPtr.Zero;
- try
- {
- int size = Marshal.SizeOf(typeof(T));
- ptr = Marshal.AllocHGlobal(size);
- Marshal.StructureToPtr(obj, ptr, true);
- return FromPtrToByteArray(ptr);
- }
- finally
- {
- if (ptr != IntPtr.Zero)
- {
- Marshal.FreeHGlobal(ptr);
- }
- }
- }
-
- ///
- /// copy a struct from a pointer to a byte array
- ///
- /// typeof struct
- /// IntPtr to struct
- /// byte[]
- public static byte[] FromPtrToByteArray(IntPtr ptr) where T : struct
- {
- int size = Marshal.SizeOf(typeof(T));
- byte[] bytes = new byte[size];
- Marshal.Copy(ptr, bytes, 0, size);
- return bytes;
- }
- }
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+using System;
+using System.Runtime.InteropServices;
+
+namespace Greenshot.Base.Core
+{
+ ///
+ /// A helper class which does the mashalling for structs
+ ///
+ public static class BinaryStructHelper
+ {
+ ///
+ /// Get a struct from a byte array
+ ///
+ /// typeof struct
+ /// byte[]
+ /// struct
+ public static T FromByteArray(byte[] bytes) where T : struct
+ {
+ IntPtr ptr = IntPtr.Zero;
+ try
+ {
+ int size = Marshal.SizeOf(typeof(T));
+ ptr = Marshal.AllocHGlobal(size);
+ Marshal.Copy(bytes, 0, ptr, size);
+ return FromIntPtr(ptr);
+ }
+ finally
+ {
+ if (ptr != IntPtr.Zero)
+ {
+ Marshal.FreeHGlobal(ptr);
+ }
+ }
+ }
+
+ ///
+ /// Get a struct from a byte array
+ ///
+ /// typeof struct
+ /// Pointer to the structor to return
+ /// struct
+ public static T FromIntPtr(IntPtr intPtr) where T : struct
+ {
+ object obj = Marshal.PtrToStructure(intPtr, typeof(T));
+ return (T) obj;
+ }
+
+ ///
+ /// copy a struct to a byte array
+ ///
+ /// typeof struct
+ /// struct
+ /// byte[]
+ public static byte[] ToByteArray(T obj) where T : struct
+ {
+ IntPtr ptr = IntPtr.Zero;
+ try
+ {
+ int size = Marshal.SizeOf(typeof(T));
+ ptr = Marshal.AllocHGlobal(size);
+ Marshal.StructureToPtr(obj, ptr, true);
+ return FromPtrToByteArray(ptr);
+ }
+ finally
+ {
+ if (ptr != IntPtr.Zero)
+ {
+ Marshal.FreeHGlobal(ptr);
+ }
+ }
+ }
+
+ ///
+ /// copy a struct from a pointer to a byte array
+ ///
+ /// typeof struct
+ /// IntPtr to struct
+ /// byte[]
+ public static byte[] FromPtrToByteArray(IntPtr ptr) where T : struct
+ {
+ int size = Marshal.SizeOf(typeof(T));
+ byte[] bytes = new byte[size];
+ Marshal.Copy(ptr, bytes, 0, size);
+ return bytes;
+ }
+ }
}
\ No newline at end of file
diff --git a/src/GreenshotPlugin/Core/Cache.cs b/src/Greenshot.Base/Core/Cache.cs
similarity index 96%
rename from src/GreenshotPlugin/Core/Cache.cs
rename to src/Greenshot.Base/Core/Cache.cs
index 5707f2465..f761b8b89 100644
--- a/src/GreenshotPlugin/Core/Cache.cs
+++ b/src/Greenshot.Base/Core/Cache.cs
@@ -1,259 +1,259 @@
-/*
- * Greenshot - a free and open source screenshot tool
- * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
- *
- * For more information see: http://getgreenshot.org/
- * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 1 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-using System;
-using System.Collections.Generic;
-using System.Timers;
-using log4net;
-
-namespace GreenshotPlugin.Core
-{
- ///
- /// Cache class
- ///
- /// Type of key
- /// Type of value
- public class Cache
- {
- private static readonly ILog Log = LogManager.GetLogger(typeof(Cache));
- private readonly IDictionary _internalCache = new Dictionary();
- private readonly object _lockObject = new object();
- private readonly int _secondsToExpire = 10;
- private readonly CacheObjectExpired _expiredCallback;
-
- public delegate void CacheObjectExpired(TK key, TV cacheValue);
-
- ///
- /// Initialize the cache
- ///
- public Cache()
- {
- }
-
- ///
- /// Initialize the cache
- ///
- ///
- public Cache(CacheObjectExpired expiredCallback) : this()
- {
- _expiredCallback = expiredCallback;
- }
-
- ///
- /// Initialize the cache with a expire setting
- ///
- ///
- public Cache(int secondsToExpire) : this()
- {
- _secondsToExpire = secondsToExpire;
- }
-
- ///
- /// Initialize the cache with a expire setting
- ///
- ///
- ///
- public Cache(int secondsToExpire, CacheObjectExpired expiredCallback) : this(expiredCallback)
- {
- _secondsToExpire = secondsToExpire;
- }
-
- ///
- /// Enumerable for the values in the cache
- ///
- public IEnumerable Elements
- {
- get
- {
- List elements = new List();
-
- lock (_lockObject)
- {
- foreach (TV element in _internalCache.Values)
- {
- elements.Add(element);
- }
- }
-
- foreach (TV element in elements)
- {
- yield return element;
- }
- }
- }
-
- ///
- /// Get the value by key from the cache
- ///
- ///
- ///
- public TV this[TK key]
- {
- get
- {
- TV result = default;
- lock (_lockObject)
- {
- if (_internalCache.ContainsKey(key))
- {
- result = _internalCache[key];
- }
- }
-
- return result;
- }
- }
-
- ///
- /// Contains
- ///
- ///
- /// true if the cache contains the key
- public bool Contains(TK key)
- {
- lock (_lockObject)
- {
- return _internalCache.ContainsKey(key);
- }
- }
-
- ///
- /// Add a value to the cache
- ///
- ///
- ///
- public void Add(TK key, TV value)
- {
- Add(key, value, null);
- }
-
- ///
- /// Add a value to the cache
- ///
- ///
- ///
- /// optional value for the seconds to expire
- public void Add(TK key, TV value, int? secondsToExpire)
- {
- lock (_lockObject)
- {
- var cachedItem = new CachedItem(key, value, secondsToExpire ?? _secondsToExpire);
- cachedItem.Expired += delegate(TK cacheKey, TV cacheValue)
- {
- if (_internalCache.ContainsKey(cacheKey))
- {
- Log.DebugFormat("Expiring object with Key: {0}", cacheKey);
- _expiredCallback?.Invoke(cacheKey, cacheValue);
- Remove(cacheKey);
- }
- else
- {
- Log.DebugFormat("Expired old object with Key: {0}", cacheKey);
- }
- };
-
- if (_internalCache.ContainsKey(key))
- {
- _internalCache[key] = value;
- Log.DebugFormat("Updated item with Key: {0}", key);
- }
- else
- {
- _internalCache.Add(key, cachedItem);
- Log.DebugFormat("Added item with Key: {0}", key);
- }
- }
- }
-
- ///
- /// Remove item from cache
- ///
- ///
- public void Remove(TK key)
- {
- lock (_lockObject)
- {
- if (!_internalCache.ContainsKey(key))
- {
- throw new ApplicationException($"An object with key ‘{key}’ does not exists in cache");
- }
-
- _internalCache.Remove(key);
- Log.DebugFormat("Removed item with Key: {0}", key);
- }
- }
-
- ///
- /// A cache item
- ///
- private class CachedItem
- {
- public event CacheObjectExpired Expired;
- private readonly int _secondsToExpire;
- private readonly Timer _timerEvent;
-
- public CachedItem(TK key, TV item, int secondsToExpire)
- {
- if (key == null)
- {
- throw new ArgumentNullException(nameof(key));
- }
-
- Key = key;
- Item = item;
- _secondsToExpire = secondsToExpire;
- if (secondsToExpire <= 0)
- {
- return;
- }
-
- _timerEvent = new Timer(secondsToExpire * 1000)
- {
- AutoReset = false
- };
- _timerEvent.Elapsed += timerEvent_Elapsed;
- _timerEvent.Start();
- }
-
- private void ExpireNow()
- {
- _timerEvent.Stop();
- if (_secondsToExpire > 0)
- {
- Expired?.Invoke(Key, Item);
- }
- }
-
- private void timerEvent_Elapsed(object sender, ElapsedEventArgs e)
- {
- ExpireNow();
- }
-
- public TK Key { get; private set; }
- public TV Item { get; private set; }
-
- public static implicit operator TV(CachedItem a)
- {
- return a.Item;
- }
- }
- }
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+using System;
+using System.Collections.Generic;
+using System.Timers;
+using log4net;
+
+namespace Greenshot.Base.Core
+{
+ ///
+ /// Cache class
+ ///
+ /// Type of key
+ /// Type of value
+ public class Cache
+ {
+ private static readonly ILog Log = LogManager.GetLogger(typeof(Cache));
+ private readonly IDictionary _internalCache = new Dictionary();
+ private readonly object _lockObject = new object();
+ private readonly int _secondsToExpire = 10;
+ private readonly CacheObjectExpired _expiredCallback;
+
+ public delegate void CacheObjectExpired(TK key, TV cacheValue);
+
+ ///
+ /// Initialize the cache
+ ///
+ public Cache()
+ {
+ }
+
+ ///
+ /// Initialize the cache
+ ///
+ ///
+ public Cache(CacheObjectExpired expiredCallback) : this()
+ {
+ _expiredCallback = expiredCallback;
+ }
+
+ ///
+ /// Initialize the cache with a expire setting
+ ///
+ ///
+ public Cache(int secondsToExpire) : this()
+ {
+ _secondsToExpire = secondsToExpire;
+ }
+
+ ///
+ /// Initialize the cache with a expire setting
+ ///
+ ///
+ ///
+ public Cache(int secondsToExpire, CacheObjectExpired expiredCallback) : this(expiredCallback)
+ {
+ _secondsToExpire = secondsToExpire;
+ }
+
+ ///
+ /// Enumerable for the values in the cache
+ ///
+ public IEnumerable Elements
+ {
+ get
+ {
+ List elements = new List();
+
+ lock (_lockObject)
+ {
+ foreach (TV element in _internalCache.Values)
+ {
+ elements.Add(element);
+ }
+ }
+
+ foreach (TV element in elements)
+ {
+ yield return element;
+ }
+ }
+ }
+
+ ///
+ /// Get the value by key from the cache
+ ///
+ ///
+ ///
+ public TV this[TK key]
+ {
+ get
+ {
+ TV result = default;
+ lock (_lockObject)
+ {
+ if (_internalCache.ContainsKey(key))
+ {
+ result = _internalCache[key];
+ }
+ }
+
+ return result;
+ }
+ }
+
+ ///
+ /// Contains
+ ///
+ ///
+ /// true if the cache contains the key
+ public bool Contains(TK key)
+ {
+ lock (_lockObject)
+ {
+ return _internalCache.ContainsKey(key);
+ }
+ }
+
+ ///
+ /// Add a value to the cache
+ ///
+ ///
+ ///
+ public void Add(TK key, TV value)
+ {
+ Add(key, value, null);
+ }
+
+ ///
+ /// Add a value to the cache
+ ///
+ ///
+ ///
+ /// optional value for the seconds to expire
+ public void Add(TK key, TV value, int? secondsToExpire)
+ {
+ lock (_lockObject)
+ {
+ var cachedItem = new CachedItem(key, value, secondsToExpire ?? _secondsToExpire);
+ cachedItem.Expired += delegate(TK cacheKey, TV cacheValue)
+ {
+ if (_internalCache.ContainsKey(cacheKey))
+ {
+ Log.DebugFormat("Expiring object with Key: {0}", cacheKey);
+ _expiredCallback?.Invoke(cacheKey, cacheValue);
+ Remove(cacheKey);
+ }
+ else
+ {
+ Log.DebugFormat("Expired old object with Key: {0}", cacheKey);
+ }
+ };
+
+ if (_internalCache.ContainsKey(key))
+ {
+ _internalCache[key] = value;
+ Log.DebugFormat("Updated item with Key: {0}", key);
+ }
+ else
+ {
+ _internalCache.Add(key, cachedItem);
+ Log.DebugFormat("Added item with Key: {0}", key);
+ }
+ }
+ }
+
+ ///
+ /// Remove item from cache
+ ///
+ ///
+ public void Remove(TK key)
+ {
+ lock (_lockObject)
+ {
+ if (!_internalCache.ContainsKey(key))
+ {
+ throw new ApplicationException($"An object with key ‘{key}’ does not exists in cache");
+ }
+
+ _internalCache.Remove(key);
+ Log.DebugFormat("Removed item with Key: {0}", key);
+ }
+ }
+
+ ///
+ /// A cache item
+ ///
+ private class CachedItem
+ {
+ public event CacheObjectExpired Expired;
+ private readonly int _secondsToExpire;
+ private readonly Timer _timerEvent;
+
+ public CachedItem(TK key, TV item, int secondsToExpire)
+ {
+ if (key == null)
+ {
+ throw new ArgumentNullException(nameof(key));
+ }
+
+ Key = key;
+ Item = item;
+ _secondsToExpire = secondsToExpire;
+ if (secondsToExpire <= 0)
+ {
+ return;
+ }
+
+ _timerEvent = new Timer(secondsToExpire * 1000)
+ {
+ AutoReset = false
+ };
+ _timerEvent.Elapsed += timerEvent_Elapsed;
+ _timerEvent.Start();
+ }
+
+ private void ExpireNow()
+ {
+ _timerEvent.Stop();
+ if (_secondsToExpire > 0)
+ {
+ Expired?.Invoke(Key, Item);
+ }
+ }
+
+ private void timerEvent_Elapsed(object sender, ElapsedEventArgs e)
+ {
+ ExpireNow();
+ }
+
+ public TK Key { get; private set; }
+ public TV Item { get; private set; }
+
+ public static implicit operator TV(CachedItem a)
+ {
+ return a.Item;
+ }
+ }
+ }
}
\ No newline at end of file
diff --git a/src/GreenshotPlugin/Core/Capture.cs b/src/Greenshot.Base/Core/Capture.cs
similarity index 98%
rename from src/GreenshotPlugin/Core/Capture.cs
rename to src/Greenshot.Base/Core/Capture.cs
index e5e7d7234..b4d172dc2 100644
--- a/src/GreenshotPlugin/Core/Capture.cs
+++ b/src/Greenshot.Base/Core/Capture.cs
@@ -22,11 +22,11 @@
using System;
using System.Drawing;
using System.Drawing.Imaging;
-using GreenshotPlugin.Interfaces;
-using GreenshotPlugin.Interfaces.Ocr;
+using Greenshot.Base.Interfaces;
+using Greenshot.Base.Interfaces.Ocr;
using log4net;
-namespace GreenshotPlugin.Core
+namespace Greenshot.Base.Core
{
///
/// This class is used to pass an instance of the "Capture" around
diff --git a/src/GreenshotPlugin/Core/CaptureDetails.cs b/src/Greenshot.Base/Core/CaptureDetails.cs
similarity index 97%
rename from src/GreenshotPlugin/Core/CaptureDetails.cs
rename to src/Greenshot.Base/Core/CaptureDetails.cs
index 96c9abb41..58e3f19be 100644
--- a/src/GreenshotPlugin/Core/CaptureDetails.cs
+++ b/src/Greenshot.Base/Core/CaptureDetails.cs
@@ -21,10 +21,10 @@
using System;
using System.Collections.Generic;
-using GreenshotPlugin.Interfaces;
-using GreenshotPlugin.Interfaces.Ocr;
+using Greenshot.Base.Interfaces;
+using Greenshot.Base.Interfaces.Ocr;
-namespace GreenshotPlugin.Core
+namespace Greenshot.Base.Core
{
///
/// This Class is used to pass details about the capture around.
diff --git a/src/GreenshotPlugin/Core/CaptureHandler.cs b/src/Greenshot.Base/Core/CaptureHandler.cs
similarity index 95%
rename from src/GreenshotPlugin/Core/CaptureHandler.cs
rename to src/Greenshot.Base/Core/CaptureHandler.cs
index 952131330..ea11c464d 100644
--- a/src/GreenshotPlugin/Core/CaptureHandler.cs
+++ b/src/Greenshot.Base/Core/CaptureHandler.cs
@@ -1,43 +1,43 @@
-/*
- * Greenshot - a free and open source screenshot tool
- * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
- *
- * For more information see: http://getgreenshot.org/
- * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 1 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-using System.Drawing;
-
-namespace GreenshotPlugin.Core
-{
- ///
- /// This is the method signature which is used to capture a rectangle from the screen.
- ///
- ///
- /// Captured Bitmap
- public delegate Bitmap CaptureScreenRectangleHandler(Rectangle captureBounds);
-
- ///
- /// This is a hack to experiment with different screen capture routines
- ///
- public static class CaptureHandler
- {
- ///
- /// By changing this value, null is default
- ///
- public static CaptureScreenRectangleHandler CaptureScreenRectangle { get; set; }
- }
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+using System.Drawing;
+
+namespace Greenshot.Base.Core
+{
+ ///
+ /// This is the method signature which is used to capture a rectangle from the screen.
+ ///
+ ///
+ /// Captured Bitmap
+ public delegate Bitmap CaptureScreenRectangleHandler(Rectangle captureBounds);
+
+ ///
+ /// This is a hack to experiment with different screen capture routines
+ ///
+ public static class CaptureHandler
+ {
+ ///
+ /// By changing this value, null is default
+ ///
+ public static CaptureScreenRectangleHandler CaptureScreenRectangle { get; set; }
+ }
}
\ No newline at end of file
diff --git a/src/GreenshotPlugin/Core/ClipboardHelper.cs b/src/Greenshot.Base/Core/ClipboardHelper.cs
similarity index 97%
rename from src/GreenshotPlugin/Core/ClipboardHelper.cs
rename to src/Greenshot.Base/Core/ClipboardHelper.cs
index f3c57b3d0..cb10a7be3 100644
--- a/src/GreenshotPlugin/Core/ClipboardHelper.cs
+++ b/src/Greenshot.Base/Core/ClipboardHelper.cs
@@ -1,1077 +1,1076 @@
-/*
- * Greenshot - a free and open source screenshot tool
- * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
- *
- * For more information see: http://getgreenshot.org/
- * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 1 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-using System;
-using System.Collections.Generic;
-using System.Diagnostics;
-using System.Drawing;
-using System.Drawing.Imaging;
-using System.IO;
-using System.Linq;
-using System.Text;
-using System.Threading;
-using System.Windows.Forms;
-using GreenshotPlugin.UnmanagedHelpers;
-using System.Runtime.InteropServices;
-using GreenshotPlugin.IniFile;
-using GreenshotPlugin.Interfaces;
-using GreenshotPlugin.Interfaces.Plugin;
-using GreenshotPlugin.Interop;
-using log4net;
-using HtmlDocument = HtmlAgilityPack.HtmlDocument;
-
-namespace GreenshotPlugin.Core
-{
- ///
- /// Description of ClipboardHelper.
- ///
- public static class ClipboardHelper
- {
- private static readonly ILog Log = LogManager.GetLogger(typeof(ClipboardHelper));
- private static readonly object ClipboardLockObject = new object();
- private static readonly CoreConfiguration CoreConfig = IniConfig.GetIniSection();
- private static readonly string FORMAT_FILECONTENTS = "FileContents";
- private static readonly string FORMAT_HTML = "text/html";
- private static readonly string FORMAT_PNG = "PNG";
- private static readonly string FORMAT_PNG_OFFICEART = "PNG+Office Art";
- private static readonly string FORMAT_17 = "Format17";
- private static readonly string FORMAT_JPG = "JPG";
- private static readonly string FORMAT_JPEG = "JPEG";
- private static readonly string FORMAT_JFIF = "JFIF";
- private static readonly string FORMAT_JFIF_OFFICEART = "JFIF+Office Art";
- private static readonly string FORMAT_GIF = "GIF";
-
- private static readonly string FORMAT_BITMAP = "System.Drawing.Bitmap";
- //private static readonly string FORMAT_HTML = "HTML Format";
-
- // Template for the HTML Text on the clipboard
- // see: http://msdn.microsoft.com/en-us/library/ms649015%28v=vs.85%29.aspx
- // or: http://msdn.microsoft.com/en-us/library/Aa767917.aspx
- private const string HtmlClipboardString = @"Version:0.9
-StartHTML:<<<<<<<1
-EndHTML:<<<<<<<2
-StartFragment:<<<<<<<3
-EndFragment:<<<<<<<4
-StartSelection:<<<<<<<3
-EndSelection:<<<<<<<4
-
-
-
-Greenshot capture
-
-
-
-
-
-
-";
-
- private const string HtmlClipboardBase64String = @"Version:0.9
-StartHTML:<<<<<<<1
-EndHTML:<<<<<<<2
-StartFragment:<<<<<<<3
-EndFragment:<<<<<<<4
-StartSelection:<<<<<<<3
-EndSelection:<<<<<<<4
-
-
-
-Greenshot capture
-
-
-
-
-
-
-";
-
- ///
- /// Get the current "ClipboardOwner" but only if it isn't us!
- ///
- /// current clipboard owner
- private static string GetClipboardOwner()
- {
- string owner = null;
- try
- {
- IntPtr hWnd = User32.GetClipboardOwner();
- if (hWnd != IntPtr.Zero)
- {
- try
- {
- User32.GetWindowThreadProcessId(hWnd, out var pid);
- using Process me = Process.GetCurrentProcess();
- using Process ownerProcess = Process.GetProcessById(pid);
- // Exclude myself
- if (me.Id != ownerProcess.Id)
- {
- // Get Process Name
- owner = ownerProcess.ProcessName;
- // Try to get the starting Process Filename, this might fail.
- try
- {
- owner = ownerProcess.Modules[0].FileName;
- }
- catch (Exception)
- {
- // Ignore
- }
- }
- }
- catch (Exception e)
- {
- Log.Warn("Non critical error: Couldn't get clipboard process, trying to use the title.", e);
- var title = new StringBuilder(260, 260);
- User32.GetWindowText(hWnd, title, title.Capacity);
- owner = title.ToString();
- }
- }
- }
- catch (Exception e)
- {
- Log.Warn("Non critical error: Couldn't get clipboard owner.", e);
- }
-
- return owner;
- }
-
- ///
- /// The SetDataObject will lock/try/catch clipboard operations making it save and not show exceptions.
- /// The bool "copy" is used to decided if the information stays on the clipboard after exit.
- ///
- ///
- ///
- private static void SetDataObject(IDataObject ido, bool copy)
- {
- lock (ClipboardLockObject)
- {
- // Clear first, this seems to solve some issues
- try
- {
- Clipboard.Clear();
- }
- catch (Exception clearException)
- {
- Log.Warn(clearException.Message);
- }
-
- try
- {
- // For BUG-1935 this was changed from looping ourselfs, or letting MS retry...
- Clipboard.SetDataObject(ido, copy, 15, 200);
- }
- catch (Exception clipboardSetException)
- {
- string messageText;
- string clipboardOwner = GetClipboardOwner();
- if (clipboardOwner != null)
- {
- messageText = Language.GetFormattedString("clipboard_inuse", clipboardOwner);
- }
- else
- {
- messageText = Language.GetString("clipboard_error");
- }
-
- Log.Error(messageText, clipboardSetException);
- }
- }
- }
-
- ///
- /// The GetDataObject will lock/try/catch clipboard operations making it save and not show exceptions.
- ///
- public static IDataObject GetDataObject()
- {
- lock (ClipboardLockObject)
- {
- int retryCount = 2;
- while (retryCount >= 0)
- {
- try
- {
- return Clipboard.GetDataObject();
- }
- catch (Exception ee)
- {
- if (retryCount == 0)
- {
- string messageText;
- string clipboardOwner = GetClipboardOwner();
- if (clipboardOwner != null)
- {
- messageText = Language.GetFormattedString("clipboard_inuse", clipboardOwner);
- }
- else
- {
- messageText = Language.GetString("clipboard_error");
- }
-
- Log.Error(messageText, ee);
- }
- else
- {
- Thread.Sleep(100);
- }
- }
- finally
- {
- --retryCount;
- }
- }
- }
-
- return null;
- }
-
- ///
- /// Test if the IDataObject contains Text
- ///
- ///
- ///
- public static bool ContainsText(IDataObject dataObject)
- {
- if (dataObject != null)
- {
- if (dataObject.GetDataPresent(DataFormats.Text) || dataObject.GetDataPresent(DataFormats.UnicodeText))
- {
- return true;
- }
- }
-
- return false;
- }
-
- ///
- /// Wrapper for Clipboard.ContainsImage, specialized for Greenshot, Created for Bug #3432313
- ///
- /// boolean if there is an image on the clipboard
- public static bool ContainsImage()
- {
- IDataObject clipboardData = GetDataObject();
- return ContainsImage(clipboardData);
- }
-
- ///
- /// Check if the IDataObject has an image
- ///
- ///
- /// true if an image is there
- public static bool ContainsImage(IDataObject dataObject)
- {
- if (dataObject == null) return false;
-
- if (dataObject.GetDataPresent(DataFormats.Bitmap)
- || dataObject.GetDataPresent(DataFormats.Dib)
- || dataObject.GetDataPresent(DataFormats.Tiff)
- || dataObject.GetDataPresent(DataFormats.EnhancedMetafile)
- || dataObject.GetDataPresent(FORMAT_PNG)
- || dataObject.GetDataPresent(FORMAT_17)
- || dataObject.GetDataPresent(FORMAT_JPG)
- || dataObject.GetDataPresent(FORMAT_JFIF)
- || dataObject.GetDataPresent(FORMAT_JPEG)
- || dataObject.GetDataPresent(FORMAT_GIF))
- {
- return true;
- }
-
- var imageFiles = GetImageFilenames(dataObject);
- if (imageFiles.Any())
- {
- return true;
- }
-
- var fileDescriptor = (MemoryStream) dataObject.GetData("FileGroupDescriptorW");
- var files = FileDescriptorReader.Read(fileDescriptor);
- var fileIndex = 0;
- foreach (var fileContentFile in files)
- {
- if ((fileContentFile.FileAttributes & FileAttributes.Directory) != 0)
- {
- //Do something with directories?
- //Note that directories do not have FileContents
- //And will throw if we try to read them
- continue;
- }
-
- var fileData = FileDescriptorReader.GetFileContents(dataObject, fileIndex);
- try
- {
- //Do something with the fileContent Stream
- if (IsValidStream(fileData))
- {
- fileData.Position = 0;
- using (ImageHelper.FromStream(fileData))
- {
- // If we get here, there is an image
- return true;
- }
- }
- }
- finally
- {
- fileData?.Dispose();
- }
-
- fileIndex++;
- }
-
- if (dataObject.GetDataPresent(FORMAT_FILECONTENTS))
- {
- try
- {
- var clipboardContent = dataObject.GetData(FORMAT_FILECONTENTS, true);
- var imageStream = clipboardContent as MemoryStream;
- if (IsValidStream(imageStream))
- {
- using (ImageHelper.FromStream(imageStream))
- {
- // If we get here, there is an image
- return true;
- }
- }
- }
- catch (Exception)
- {
- // Ignore
- }
- }
-
- // Try to get the image from the HTML code
- var textObject = ContentAsString(dataObject, FORMAT_HTML, Encoding.UTF8);
- if (textObject != null)
- {
- var doc = new HtmlDocument();
- doc.LoadHtml(textObject);
- var imgNodes = doc.DocumentNode.SelectNodes("//img");
- if (imgNodes != null)
- {
- foreach (var imgNode in imgNodes)
- {
- var srcAttribute = imgNode.Attributes["src"];
- var imageUrl = srcAttribute.Value;
- if (!string.IsNullOrEmpty(imageUrl))
- {
- return true;
- }
- }
- }
- }
-
- return false;
- }
-
- ///
- /// Get the specified IDataObject format as a string
- ///
- /// IDataObject
- /// string
- /// Encoding
- /// sting
- private static string ContentAsString(IDataObject dataObject, string format, Encoding encoding = null)
- {
- encoding ??= Encoding.Unicode;
- var objectAsFormat = dataObject.GetData(format);
- return objectAsFormat switch
- {
- null => null,
- string text => text,
- MemoryStream ms => encoding.GetString(ms.ToArray()),
- _ => null
- };
- }
-
- ///
- /// Simple helper to check the stream
- ///
- ///
- ///
- private static bool IsValidStream(MemoryStream memoryStream)
- {
- return memoryStream != null && memoryStream.Length > 0;
- }
-
- ///
- /// Wrapper for Clipboard.GetImage, Created for Bug #3432313
- ///
- /// Image if there is an image on the clipboard
- public static Image GetImage()
- {
- IDataObject clipboardData = GetDataObject();
- // Return the first image
- foreach (Image clipboardImage in GetImages(clipboardData))
- {
- return clipboardImage;
- }
-
- return null;
- }
-
- ///
- /// Get all images (multiple if filenames are available) from the dataObject
- /// Returned images must be disposed by the calling code!
- ///
- ///
- /// IEnumerable of Image
- public static IEnumerable GetImages(IDataObject dataObject)
- {
- // Get single image, this takes the "best" match
- Image singleImage = GetImage(dataObject);
- if (singleImage != null)
- {
- Log.InfoFormat("Got image from clipboard with size {0} and format {1}", singleImage.Size, singleImage.PixelFormat);
- yield return singleImage;
- }
- else
- {
- // check if files are supplied
- foreach (string imageFile in GetImageFilenames(dataObject))
- {
- Image returnImage = null;
- try
- {
- returnImage = ImageHelper.LoadImage(imageFile);
- }
- catch (Exception streamImageEx)
- {
- Log.Error("Problem retrieving Image from clipboard.", streamImageEx);
- }
-
- if (returnImage != null)
- {
- Log.InfoFormat("Got image from clipboard with size {0} and format {1}", returnImage.Size, returnImage.PixelFormat);
- yield return returnImage;
- }
- }
- }
- }
-
- ///
- /// Get an Image from the IDataObject, don't check for FileDrop
- ///
- ///
- /// Image or null
- private static Image GetImage(IDataObject dataObject)
- {
- Image returnImage = null;
- if (dataObject != null)
- {
- IList formats = GetFormats(dataObject);
- string[] retrieveFormats;
-
- // Found a weird bug, where PNG's from Outlook 2010 are clipped
- // So I build some special logik to get the best format:
- if (formats != null && formats.Contains(FORMAT_PNG_OFFICEART) && formats.Contains(DataFormats.Dib))
- {
- // Outlook ??
- Log.Info("Most likely the current clipboard contents come from Outlook, as this has a problem with PNG and others we place the DIB format to the front...");
- retrieveFormats = new[]
- {
- DataFormats.Dib, FORMAT_BITMAP, FORMAT_FILECONTENTS, FORMAT_PNG_OFFICEART, FORMAT_PNG, FORMAT_JFIF_OFFICEART, FORMAT_JPG, FORMAT_JPEG, FORMAT_JFIF,
- DataFormats.Tiff, FORMAT_GIF, FORMAT_HTML
- };
- }
- else
- {
- retrieveFormats = new[]
- {
- FORMAT_PNG_OFFICEART, FORMAT_PNG, FORMAT_17, FORMAT_JFIF_OFFICEART, FORMAT_JPG, FORMAT_JPEG, FORMAT_JFIF, DataFormats.Tiff, DataFormats.Dib, FORMAT_BITMAP,
- FORMAT_FILECONTENTS, FORMAT_GIF, FORMAT_HTML
- };
- }
-
- foreach (string currentFormat in retrieveFormats)
- {
- if (formats != null && formats.Contains(currentFormat))
- {
- Log.InfoFormat("Found {0}, trying to retrieve.", currentFormat);
- returnImage = GetImageForFormat(currentFormat, dataObject);
- }
- else
- {
- Log.DebugFormat("Couldn't find format {0}.", currentFormat);
- }
-
- if (returnImage != null)
- {
- return returnImage;
- }
- }
- }
-
- return null;
- }
-
- ///
- /// Helper method to try to get an image in the specified format from the dataObject
- /// the DIB reader should solve some issues
- /// It also supports Format17/DibV5, by using the following information: http://stackoverflow.com/a/14335591
- ///
- /// string with the format
- /// IDataObject
- /// Image or null
- private static Image GetImageForFormat(string format, IDataObject dataObject)
- {
- if (format == FORMAT_HTML)
- {
- var textObject = ContentAsString(dataObject, FORMAT_HTML, Encoding.UTF8);
- if (textObject != null)
- {
- var doc = new HtmlDocument();
- doc.LoadHtml(textObject);
- var imgNodes = doc.DocumentNode.SelectNodes("//img");
- if (imgNodes != null)
- {
- foreach (var imgNode in imgNodes)
- {
- var srcAttribute = imgNode.Attributes["src"];
- var imageUrl = srcAttribute.Value;
- Log.Debug(imageUrl);
- var image = NetworkHelper.DownloadImage(imageUrl);
- if (image != null)
- {
- return image;
- }
- }
- }
- }
- }
-
- object clipboardObject = GetFromDataObject(dataObject, format);
- var imageStream = clipboardObject as MemoryStream;
- if (!IsValidStream(imageStream))
- {
- // TODO: add "HTML Format" support here...
- return clipboardObject as Image;
- }
-
- if (CoreConfig.EnableSpecialDIBClipboardReader)
- {
- if (format == FORMAT_17 || format == DataFormats.Dib)
- {
- Log.Info("Found DIB stream, trying to process it.");
- try
- {
- if (imageStream != null)
- {
- byte[] dibBuffer = new byte[imageStream.Length];
- imageStream.Read(dibBuffer, 0, dibBuffer.Length);
- var infoHeader = BinaryStructHelper.FromByteArray(dibBuffer);
- if (!infoHeader.IsDibV5)
- {
- Log.InfoFormat("Using special DIB
- /// Get Text from the DataObject
- ///
- /// string if there is text on the clipboard
- public static string GetText(IDataObject dataObject)
- {
- if (ContainsText(dataObject))
- {
- return (string) dataObject.GetData(DataFormats.Text);
- }
-
- return null;
- }
-
- ///
- /// Set text to the clipboard
- ///
- ///
- public static void SetClipboardData(string text)
- {
- IDataObject ido = new DataObject();
- ido.SetData(DataFormats.Text, true, text);
- SetDataObject(ido, true);
- }
-
- private static string GetHtmlString(ISurface surface, string filename)
- {
- string utf8EncodedHtmlString = Encoding.GetEncoding(0).GetString(Encoding.UTF8.GetBytes(HtmlClipboardString));
- utf8EncodedHtmlString = utf8EncodedHtmlString.Replace("${width}", surface.Image.Width.ToString());
- utf8EncodedHtmlString = utf8EncodedHtmlString.Replace("${height}", surface.Image.Height.ToString());
- utf8EncodedHtmlString = utf8EncodedHtmlString.Replace("${file}", filename.Replace("\\", "/"));
- StringBuilder sb = new StringBuilder();
- sb.Append(utf8EncodedHtmlString);
- sb.Replace("<<<<<<<1", (utf8EncodedHtmlString.IndexOf("", StringComparison.Ordinal) + "".Length).ToString("D8"));
- sb.Replace("<<<<<<<2", (utf8EncodedHtmlString.IndexOf("", StringComparison.Ordinal)).ToString("D8"));
- sb.Replace("<<<<<<<3", (utf8EncodedHtmlString.IndexOf("", StringComparison.Ordinal) + "".Length).ToString("D8"));
- sb.Replace("<<<<<<<4", (utf8EncodedHtmlString.IndexOf("", StringComparison.Ordinal)).ToString("D8"));
- return sb.ToString();
- }
-
- private static string GetHtmlDataUrlString(ISurface surface, MemoryStream pngStream)
- {
- string utf8EncodedHtmlString = Encoding.GetEncoding(0).GetString(Encoding.UTF8.GetBytes(HtmlClipboardBase64String));
- utf8EncodedHtmlString = utf8EncodedHtmlString.Replace("${width}", surface.Image.Width.ToString());
- utf8EncodedHtmlString = utf8EncodedHtmlString.Replace("${height}", surface.Image.Height.ToString());
- utf8EncodedHtmlString = utf8EncodedHtmlString.Replace("${format}", "png");
- utf8EncodedHtmlString = utf8EncodedHtmlString.Replace("${data}", Convert.ToBase64String(pngStream.GetBuffer(), 0, (int) pngStream.Length));
- StringBuilder sb = new StringBuilder();
- sb.Append(utf8EncodedHtmlString);
- sb.Replace("<<<<<<<1", (utf8EncodedHtmlString.IndexOf("", StringComparison.Ordinal) + "".Length).ToString("D8"));
- sb.Replace("<<<<<<<2", (utf8EncodedHtmlString.IndexOf("", StringComparison.Ordinal)).ToString("D8"));
- sb.Replace("<<<<<<<3", (utf8EncodedHtmlString.IndexOf("", StringComparison.Ordinal) + "".Length).ToString("D8"));
- sb.Replace("<<<<<<<4", (utf8EncodedHtmlString.IndexOf("", StringComparison.Ordinal)).ToString("D8"));
- return sb.ToString();
- }
-
- private const int BITMAPFILEHEADER_LENGTH = 14;
-
- ///
- /// Set an Image to the clipboard
- /// This method will place images to the clipboard depending on the ClipboardFormats setting.
- /// e.g. Bitmap which works with pretty much everything and type Dib for e.g. OpenOffice
- /// because OpenOffice has a bug http://qa.openoffice.org/issues/show_bug.cgi?id=85661
- /// The Dib (Device Indenpendend Bitmap) in 32bpp actually won't work with Powerpoint 2003!
- /// When pasting a Dib in PP 2003 the Bitmap is somehow shifted left!
- /// For this problem the user should not use the direct paste (=Dib), but select Bitmap
- ///
- public static void SetClipboardData(ISurface surface)
- {
- DataObject dataObject = new DataObject();
-
- // This will work for Office and most other applications
- //ido.SetData(DataFormats.Bitmap, true, image);
-
- MemoryStream dibStream = null;
- MemoryStream dibV5Stream = null;
- MemoryStream pngStream = null;
- Image imageToSave = null;
- bool disposeImage = false;
- try
- {
- SurfaceOutputSettings outputSettings = new SurfaceOutputSettings(OutputFormat.png, 100, false);
- // Create the image which is going to be saved so we don't create it multiple times
- disposeImage = ImageOutput.CreateImageFromSurface(surface, outputSettings, out imageToSave);
- try
- {
- // Create PNG stream
- if (CoreConfig.ClipboardFormats.Contains(ClipboardFormat.PNG))
- {
- pngStream = new MemoryStream();
- // PNG works for e.g. Powerpoint
- SurfaceOutputSettings pngOutputSettings = new SurfaceOutputSettings(OutputFormat.png, 100, false);
- ImageOutput.SaveToStream(imageToSave, null, pngStream, pngOutputSettings);
- pngStream.Seek(0, SeekOrigin.Begin);
- // Set the PNG stream
- dataObject.SetData(FORMAT_PNG, false, pngStream);
- }
- }
- catch (Exception pngEx)
- {
- Log.Error("Error creating PNG for the Clipboard.", pngEx);
- }
-
- try
- {
- if (CoreConfig.ClipboardFormats.Contains(ClipboardFormat.DIB))
- {
- using (MemoryStream tmpBmpStream = new MemoryStream())
- {
- // Save image as BMP
- SurfaceOutputSettings bmpOutputSettings = new SurfaceOutputSettings(OutputFormat.bmp, 100, false);
- ImageOutput.SaveToStream(imageToSave, null, tmpBmpStream, bmpOutputSettings);
-
- dibStream = new MemoryStream();
- // Copy the source, but skip the "BITMAPFILEHEADER" which has a size of 14
- dibStream.Write(tmpBmpStream.GetBuffer(), BITMAPFILEHEADER_LENGTH, (int) tmpBmpStream.Length - BITMAPFILEHEADER_LENGTH);
- }
-
- // Set the DIB to the clipboard DataObject
- dataObject.SetData(DataFormats.Dib, true, dibStream);
- }
- }
- catch (Exception dibEx)
- {
- Log.Error("Error creating DIB for the Clipboard.", dibEx);
- }
-
- // CF_DibV5
- try
- {
- if (CoreConfig.ClipboardFormats.Contains(ClipboardFormat.DIBV5))
- {
- // Create the stream for the clipboard
- dibV5Stream = new MemoryStream();
-
- // Create the BITMAPINFOHEADER
- BITMAPINFOHEADER header = new BITMAPINFOHEADER(imageToSave.Width, imageToSave.Height, 32)
- {
- // Make sure we have BI_BITFIELDS, this seems to be normal for Format17?
- biCompression = BI_COMPRESSION.BI_BITFIELDS
- };
- // Create a byte[] to write
- byte[] headerBytes = BinaryStructHelper.ToByteArray(header);
- // Write the BITMAPINFOHEADER to the stream
- dibV5Stream.Write(headerBytes, 0, headerBytes.Length);
-
- // As we have specified BI_COMPRESSION.BI_BITFIELDS, the BitfieldColorMask needs to be added
- BitfieldColorMask colorMask = new BitfieldColorMask();
- // Make sure the values are set
- colorMask.InitValues();
- // Create the byte[] from the struct
- byte[] colorMaskBytes = BinaryStructHelper.ToByteArray(colorMask);
- Array.Reverse(colorMaskBytes);
- // Write to the stream
- dibV5Stream.Write(colorMaskBytes, 0, colorMaskBytes.Length);
-
- // Create the raw bytes for the pixels only
- byte[] bitmapBytes = BitmapToByteArray((Bitmap) imageToSave);
- // Write to the stream
- dibV5Stream.Write(bitmapBytes, 0, bitmapBytes.Length);
-
- // Set the DIBv5 to the clipboard DataObject
- dataObject.SetData(FORMAT_17, true, dibV5Stream);
- }
- }
- catch (Exception dibEx)
- {
- Log.Error("Error creating DIB for the Clipboard.", dibEx);
- }
-
- // Set the HTML
- if (CoreConfig.ClipboardFormats.Contains(ClipboardFormat.HTML))
- {
- string tmpFile = ImageOutput.SaveToTmpFile(surface, new SurfaceOutputSettings(OutputFormat.png, 100, false), null);
- string html = GetHtmlString(surface, tmpFile);
- dataObject.SetText(html, TextDataFormat.Html);
- }
- else if (CoreConfig.ClipboardFormats.Contains(ClipboardFormat.HTMLDATAURL))
- {
- string html;
- using (MemoryStream tmpPngStream = new MemoryStream())
- {
- SurfaceOutputSettings pngOutputSettings = new SurfaceOutputSettings(OutputFormat.png, 100, false)
- {
- // Do not allow to reduce the colors, some applications dislike 256 color images
- // reported with bug #3594681
- DisableReduceColors = true
- };
- // Check if we can use the previously used image
- if (imageToSave.PixelFormat != PixelFormat.Format8bppIndexed)
- {
- ImageOutput.SaveToStream(imageToSave, surface, tmpPngStream, pngOutputSettings);
- }
- else
- {
- ImageOutput.SaveToStream(surface, tmpPngStream, pngOutputSettings);
- }
-
- html = GetHtmlDataUrlString(surface, tmpPngStream);
- }
-
- dataObject.SetText(html, TextDataFormat.Html);
- }
- }
- finally
- {
- // we need to use the SetDataOject before the streams are closed otherwise the buffer will be gone!
- // Check if Bitmap is wanted
- if (CoreConfig.ClipboardFormats.Contains(ClipboardFormat.BITMAP))
- {
- dataObject.SetImage(imageToSave);
- // Place the DataObject to the clipboard
- SetDataObject(dataObject, true);
- }
- else
- {
- // Place the DataObject to the clipboard
- SetDataObject(dataObject, true);
- }
-
- pngStream?.Dispose();
- dibStream?.Dispose();
- dibV5Stream?.Dispose();
- // cleanup if needed
- if (disposeImage)
- {
- imageToSave?.Dispose();
- }
- }
- }
-
- ///
- /// Helper method so get the bitmap bytes
- /// See: http://stackoverflow.com/a/6570155
- ///
- /// Bitmap
- /// byte[]
- private static byte[] BitmapToByteArray(Bitmap bitmap)
- {
- // Lock the bitmap's bits.
- Rectangle rect = new Rectangle(0, 0, bitmap.Width, bitmap.Height);
- BitmapData bmpData = bitmap.LockBits(rect, ImageLockMode.ReadOnly, bitmap.PixelFormat);
-
- int absStride = Math.Abs(bmpData.Stride);
- int bytes = absStride * bitmap.Height;
- long ptr = bmpData.Scan0.ToInt32();
- // Declare an array to hold the bytes of the bitmap.
- byte[] rgbValues = new byte[bytes];
-
- for (int i = 0; i < bitmap.Height; i++)
- {
- IntPtr pointer = new IntPtr(ptr + (bmpData.Stride * i));
- Marshal.Copy(pointer, rgbValues, absStride * (bitmap.Height - i - 1), absStride);
- }
-
- // Unlock the bits.
- bitmap.UnlockBits(bmpData);
-
- return rgbValues;
- }
-
- ///
- /// Set Object with type Type to the clipboard
- ///
- /// Type
- /// object
- public static void SetClipboardData(Type type, object obj)
- {
- DataFormats.Format format = DataFormats.GetFormat(type.FullName);
-
- //now copy to clipboard
- IDataObject dataObj = new DataObject();
- dataObj.SetData(format.Name, false, obj);
- // Use false to make the object disappear when the application stops.
- SetDataObject(dataObj, true);
- }
-
- ///
- /// Retrieve a list of all formats currently in the IDataObject
- ///
- /// List of string with the current formats
- public static List GetFormats(IDataObject dataObj)
- {
- string[] formats = null;
-
- if (dataObj != null)
- {
- formats = dataObj.GetFormats();
- }
-
- if (formats != null)
- {
- Log.DebugFormat("Got clipboard formats: {0}", string.Join(",", formats));
- return new List(formats);
- }
-
- return new List();
- }
-
- ///
- /// Check if there is currently something on the clipboard which has the supplied format
- ///
- /// IDataObject
- /// string with format
- /// true if one the format is found
- public static bool ContainsFormat(IDataObject dataObject, string format)
- {
- return ContainsFormat(dataObject, new[]
- {
- format
- });
- }
-
- ///
- /// Check if there is currently something on the clipboard which has one of the supplied formats
- ///
- /// string[] with formats
- /// true if one of the formats was found
- public static bool ContainsFormat(string[] formats)
- {
- return ContainsFormat(GetDataObject(), formats);
- }
-
- ///
- /// Check if there is currently something on the clipboard which has one of the supplied formats
- ///
- /// IDataObject
- /// string[] with formats
- /// true if one of the formats was found
- public static bool ContainsFormat(IDataObject dataObject, string[] formats)
- {
- bool formatFound = false;
- List currentFormats = GetFormats(dataObject);
- if (currentFormats == null || currentFormats.Count == 0 || formats == null || formats.Length == 0)
- {
- return false;
- }
-
- foreach (string format in formats)
- {
- if (currentFormats.Contains(format))
- {
- formatFound = true;
- break;
- }
- }
-
- return formatFound;
- }
-
- ///
- /// Get Object for format from IDataObject
- ///
- /// IDataObject
- /// Type to get
- /// object from IDataObject
- public static object GetFromDataObject(IDataObject dataObj, Type type)
- {
- if (type != null)
- {
- return GetFromDataObject(dataObj, type.FullName);
- }
-
- return null;
- }
-
- ///
- /// Get ImageFilenames from the IDataObject
- ///
- /// IDataObject
- ///
- public static IEnumerable GetImageFilenames(IDataObject dataObject)
- {
- string[] dropFileNames = (string[]) dataObject.GetData(DataFormats.FileDrop);
- if (dropFileNames != null && dropFileNames.Length > 0)
- {
- return dropFileNames
- .Where(filename => !string.IsNullOrEmpty(filename))
- .Where(Path.HasExtension)
- .Where(filename => ImageHelper.StreamConverters.Keys.Contains(Path.GetExtension(filename).ToLowerInvariant().Substring(1)));
- }
-
- return Enumerable.Empty();
- }
-
- ///
- /// Get Object for format from IDataObject
- ///
- /// IDataObject
- /// format to get
- /// object from IDataObject
- public static object GetFromDataObject(IDataObject dataObj, string format)
- {
- if (dataObj != null)
- {
- try
- {
- return dataObj.GetData(format);
- }
- catch (Exception e)
- {
- Log.Error("Error in GetClipboardData.", e);
- }
- }
-
- return null;
- }
- }
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Drawing;
+using System.Drawing.Imaging;
+using System.IO;
+using System.Linq;
+using System.Runtime.InteropServices;
+using System.Text;
+using System.Threading;
+using System.Windows.Forms;
+using Greenshot.Base.IniFile;
+using Greenshot.Base.Interfaces;
+using Greenshot.Base.Interfaces.Plugin;
+using Greenshot.Base.UnmanagedHelpers;
+using log4net;
+using HtmlDocument = HtmlAgilityPack.HtmlDocument;
+
+namespace Greenshot.Base.Core
+{
+ ///
+ /// Description of ClipboardHelper.
+ ///
+ public static class ClipboardHelper
+ {
+ private static readonly ILog Log = LogManager.GetLogger(typeof(ClipboardHelper));
+ private static readonly object ClipboardLockObject = new object();
+ private static readonly CoreConfiguration CoreConfig = IniConfig.GetIniSection();
+ private static readonly string FORMAT_FILECONTENTS = "FileContents";
+ private static readonly string FORMAT_HTML = "text/html";
+ private static readonly string FORMAT_PNG = "PNG";
+ private static readonly string FORMAT_PNG_OFFICEART = "PNG+Office Art";
+ private static readonly string FORMAT_17 = "Format17";
+ private static readonly string FORMAT_JPG = "JPG";
+ private static readonly string FORMAT_JPEG = "JPEG";
+ private static readonly string FORMAT_JFIF = "JFIF";
+ private static readonly string FORMAT_JFIF_OFFICEART = "JFIF+Office Art";
+ private static readonly string FORMAT_GIF = "GIF";
+
+ private static readonly string FORMAT_BITMAP = "System.Drawing.Bitmap";
+ //private static readonly string FORMAT_HTML = "HTML Format";
+
+ // Template for the HTML Text on the clipboard
+ // see: http://msdn.microsoft.com/en-us/library/ms649015%28v=vs.85%29.aspx
+ // or: http://msdn.microsoft.com/en-us/library/Aa767917.aspx
+ private const string HtmlClipboardString = @"Version:0.9
+StartHTML:<<<<<<<1
+EndHTML:<<<<<<<2
+StartFragment:<<<<<<<3
+EndFragment:<<<<<<<4
+StartSelection:<<<<<<<3
+EndSelection:<<<<<<<4
+
+
+
+Greenshot capture
+
+
+
+
+
+
+";
+
+ private const string HtmlClipboardBase64String = @"Version:0.9
+StartHTML:<<<<<<<1
+EndHTML:<<<<<<<2
+StartFragment:<<<<<<<3
+EndFragment:<<<<<<<4
+StartSelection:<<<<<<<3
+EndSelection:<<<<<<<4
+
+
+
+Greenshot capture
+
+
+
+
+
+
+";
+
+ ///
+ /// Get the current "ClipboardOwner" but only if it isn't us!
+ ///
+ /// current clipboard owner
+ private static string GetClipboardOwner()
+ {
+ string owner = null;
+ try
+ {
+ IntPtr hWnd = User32.GetClipboardOwner();
+ if (hWnd != IntPtr.Zero)
+ {
+ try
+ {
+ User32.GetWindowThreadProcessId(hWnd, out var pid);
+ using Process me = Process.GetCurrentProcess();
+ using Process ownerProcess = Process.GetProcessById(pid);
+ // Exclude myself
+ if (me.Id != ownerProcess.Id)
+ {
+ // Get Process Name
+ owner = ownerProcess.ProcessName;
+ // Try to get the starting Process Filename, this might fail.
+ try
+ {
+ owner = ownerProcess.Modules[0].FileName;
+ }
+ catch (Exception)
+ {
+ // Ignore
+ }
+ }
+ }
+ catch (Exception e)
+ {
+ Log.Warn("Non critical error: Couldn't get clipboard process, trying to use the title.", e);
+ var title = new StringBuilder(260, 260);
+ User32.GetWindowText(hWnd, title, title.Capacity);
+ owner = title.ToString();
+ }
+ }
+ }
+ catch (Exception e)
+ {
+ Log.Warn("Non critical error: Couldn't get clipboard owner.", e);
+ }
+
+ return owner;
+ }
+
+ ///
+ /// The SetDataObject will lock/try/catch clipboard operations making it save and not show exceptions.
+ /// The bool "copy" is used to decided if the information stays on the clipboard after exit.
+ ///
+ ///
+ ///
+ private static void SetDataObject(IDataObject ido, bool copy)
+ {
+ lock (ClipboardLockObject)
+ {
+ // Clear first, this seems to solve some issues
+ try
+ {
+ Clipboard.Clear();
+ }
+ catch (Exception clearException)
+ {
+ Log.Warn(clearException.Message);
+ }
+
+ try
+ {
+ // For BUG-1935 this was changed from looping ourselfs, or letting MS retry...
+ Clipboard.SetDataObject(ido, copy, 15, 200);
+ }
+ catch (Exception clipboardSetException)
+ {
+ string messageText;
+ string clipboardOwner = GetClipboardOwner();
+ if (clipboardOwner != null)
+ {
+ messageText = Language.GetFormattedString("clipboard_inuse", clipboardOwner);
+ }
+ else
+ {
+ messageText = Language.GetString("clipboard_error");
+ }
+
+ Log.Error(messageText, clipboardSetException);
+ }
+ }
+ }
+
+ ///
+ /// The GetDataObject will lock/try/catch clipboard operations making it save and not show exceptions.
+ ///
+ public static IDataObject GetDataObject()
+ {
+ lock (ClipboardLockObject)
+ {
+ int retryCount = 2;
+ while (retryCount >= 0)
+ {
+ try
+ {
+ return Clipboard.GetDataObject();
+ }
+ catch (Exception ee)
+ {
+ if (retryCount == 0)
+ {
+ string messageText;
+ string clipboardOwner = GetClipboardOwner();
+ if (clipboardOwner != null)
+ {
+ messageText = Language.GetFormattedString("clipboard_inuse", clipboardOwner);
+ }
+ else
+ {
+ messageText = Language.GetString("clipboard_error");
+ }
+
+ Log.Error(messageText, ee);
+ }
+ else
+ {
+ Thread.Sleep(100);
+ }
+ }
+ finally
+ {
+ --retryCount;
+ }
+ }
+ }
+
+ return null;
+ }
+
+ ///
+ /// Test if the IDataObject contains Text
+ ///
+ ///
+ ///
+ public static bool ContainsText(IDataObject dataObject)
+ {
+ if (dataObject != null)
+ {
+ if (dataObject.GetDataPresent(DataFormats.Text) || dataObject.GetDataPresent(DataFormats.UnicodeText))
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ ///
+ /// Wrapper for Clipboard.ContainsImage, specialized for Greenshot, Created for Bug #3432313
+ ///
+ /// boolean if there is an image on the clipboard
+ public static bool ContainsImage()
+ {
+ IDataObject clipboardData = GetDataObject();
+ return ContainsImage(clipboardData);
+ }
+
+ ///
+ /// Check if the IDataObject has an image
+ ///
+ ///
+ /// true if an image is there
+ public static bool ContainsImage(IDataObject dataObject)
+ {
+ if (dataObject == null) return false;
+
+ if (dataObject.GetDataPresent(DataFormats.Bitmap)
+ || dataObject.GetDataPresent(DataFormats.Dib)
+ || dataObject.GetDataPresent(DataFormats.Tiff)
+ || dataObject.GetDataPresent(DataFormats.EnhancedMetafile)
+ || dataObject.GetDataPresent(FORMAT_PNG)
+ || dataObject.GetDataPresent(FORMAT_17)
+ || dataObject.GetDataPresent(FORMAT_JPG)
+ || dataObject.GetDataPresent(FORMAT_JFIF)
+ || dataObject.GetDataPresent(FORMAT_JPEG)
+ || dataObject.GetDataPresent(FORMAT_GIF))
+ {
+ return true;
+ }
+
+ var imageFiles = GetImageFilenames(dataObject);
+ if (imageFiles.Any())
+ {
+ return true;
+ }
+
+ var fileDescriptor = (MemoryStream) dataObject.GetData("FileGroupDescriptorW");
+ var files = FileDescriptorReader.Read(fileDescriptor);
+ var fileIndex = 0;
+ foreach (var fileContentFile in files)
+ {
+ if ((fileContentFile.FileAttributes & FileAttributes.Directory) != 0)
+ {
+ //Do something with directories?
+ //Note that directories do not have FileContents
+ //And will throw if we try to read them
+ continue;
+ }
+
+ var fileData = FileDescriptorReader.GetFileContents(dataObject, fileIndex);
+ try
+ {
+ //Do something with the fileContent Stream
+ if (IsValidStream(fileData))
+ {
+ fileData.Position = 0;
+ using (ImageHelper.FromStream(fileData))
+ {
+ // If we get here, there is an image
+ return true;
+ }
+ }
+ }
+ finally
+ {
+ fileData?.Dispose();
+ }
+
+ fileIndex++;
+ }
+
+ if (dataObject.GetDataPresent(FORMAT_FILECONTENTS))
+ {
+ try
+ {
+ var clipboardContent = dataObject.GetData(FORMAT_FILECONTENTS, true);
+ var imageStream = clipboardContent as MemoryStream;
+ if (IsValidStream(imageStream))
+ {
+ using (ImageHelper.FromStream(imageStream))
+ {
+ // If we get here, there is an image
+ return true;
+ }
+ }
+ }
+ catch (Exception)
+ {
+ // Ignore
+ }
+ }
+
+ // Try to get the image from the HTML code
+ var textObject = ContentAsString(dataObject, FORMAT_HTML, Encoding.UTF8);
+ if (textObject != null)
+ {
+ var doc = new HtmlDocument();
+ doc.LoadHtml(textObject);
+ var imgNodes = doc.DocumentNode.SelectNodes("//img");
+ if (imgNodes != null)
+ {
+ foreach (var imgNode in imgNodes)
+ {
+ var srcAttribute = imgNode.Attributes["src"];
+ var imageUrl = srcAttribute.Value;
+ if (!string.IsNullOrEmpty(imageUrl))
+ {
+ return true;
+ }
+ }
+ }
+ }
+
+ return false;
+ }
+
+ ///
+ /// Get the specified IDataObject format as a string
+ ///
+ /// IDataObject
+ /// string
+ /// Encoding
+ /// sting
+ private static string ContentAsString(IDataObject dataObject, string format, Encoding encoding = null)
+ {
+ encoding ??= Encoding.Unicode;
+ var objectAsFormat = dataObject.GetData(format);
+ return objectAsFormat switch
+ {
+ null => null,
+ string text => text,
+ MemoryStream ms => encoding.GetString(ms.ToArray()),
+ _ => null
+ };
+ }
+
+ ///
+ /// Simple helper to check the stream
+ ///
+ ///
+ ///
+ private static bool IsValidStream(MemoryStream memoryStream)
+ {
+ return memoryStream != null && memoryStream.Length > 0;
+ }
+
+ ///
+ /// Wrapper for Clipboard.GetImage, Created for Bug #3432313
+ ///
+ /// Image if there is an image on the clipboard
+ public static Image GetImage()
+ {
+ IDataObject clipboardData = GetDataObject();
+ // Return the first image
+ foreach (Image clipboardImage in GetImages(clipboardData))
+ {
+ return clipboardImage;
+ }
+
+ return null;
+ }
+
+ ///
+ /// Get all images (multiple if filenames are available) from the dataObject
+ /// Returned images must be disposed by the calling code!
+ ///
+ ///
+ /// IEnumerable of Image
+ public static IEnumerable GetImages(IDataObject dataObject)
+ {
+ // Get single image, this takes the "best" match
+ Image singleImage = GetImage(dataObject);
+ if (singleImage != null)
+ {
+ Log.InfoFormat("Got image from clipboard with size {0} and format {1}", singleImage.Size, singleImage.PixelFormat);
+ yield return singleImage;
+ }
+ else
+ {
+ // check if files are supplied
+ foreach (string imageFile in GetImageFilenames(dataObject))
+ {
+ Image returnImage = null;
+ try
+ {
+ returnImage = ImageHelper.LoadImage(imageFile);
+ }
+ catch (Exception streamImageEx)
+ {
+ Log.Error("Problem retrieving Image from clipboard.", streamImageEx);
+ }
+
+ if (returnImage != null)
+ {
+ Log.InfoFormat("Got image from clipboard with size {0} and format {1}", returnImage.Size, returnImage.PixelFormat);
+ yield return returnImage;
+ }
+ }
+ }
+ }
+
+ ///
+ /// Get an Image from the IDataObject, don't check for FileDrop
+ ///
+ ///
+ /// Image or null
+ private static Image GetImage(IDataObject dataObject)
+ {
+ Image returnImage = null;
+ if (dataObject != null)
+ {
+ IList formats = GetFormats(dataObject);
+ string[] retrieveFormats;
+
+ // Found a weird bug, where PNG's from Outlook 2010 are clipped
+ // So I build some special logik to get the best format:
+ if (formats != null && formats.Contains(FORMAT_PNG_OFFICEART) && formats.Contains(DataFormats.Dib))
+ {
+ // Outlook ??
+ Log.Info("Most likely the current clipboard contents come from Outlook, as this has a problem with PNG and others we place the DIB format to the front...");
+ retrieveFormats = new[]
+ {
+ DataFormats.Dib, FORMAT_BITMAP, FORMAT_FILECONTENTS, FORMAT_PNG_OFFICEART, FORMAT_PNG, FORMAT_JFIF_OFFICEART, FORMAT_JPG, FORMAT_JPEG, FORMAT_JFIF,
+ DataFormats.Tiff, FORMAT_GIF, FORMAT_HTML
+ };
+ }
+ else
+ {
+ retrieveFormats = new[]
+ {
+ FORMAT_PNG_OFFICEART, FORMAT_PNG, FORMAT_17, FORMAT_JFIF_OFFICEART, FORMAT_JPG, FORMAT_JPEG, FORMAT_JFIF, DataFormats.Tiff, DataFormats.Dib, FORMAT_BITMAP,
+ FORMAT_FILECONTENTS, FORMAT_GIF, FORMAT_HTML
+ };
+ }
+
+ foreach (string currentFormat in retrieveFormats)
+ {
+ if (formats != null && formats.Contains(currentFormat))
+ {
+ Log.InfoFormat("Found {0}, trying to retrieve.", currentFormat);
+ returnImage = GetImageForFormat(currentFormat, dataObject);
+ }
+ else
+ {
+ Log.DebugFormat("Couldn't find format {0}.", currentFormat);
+ }
+
+ if (returnImage != null)
+ {
+ return returnImage;
+ }
+ }
+ }
+
+ return null;
+ }
+
+ ///
+ /// Helper method to try to get an image in the specified format from the dataObject
+ /// the DIB reader should solve some issues
+ /// It also supports Format17/DibV5, by using the following information: http://stackoverflow.com/a/14335591
+ ///
+ /// string with the format
+ /// IDataObject
+ /// Image or null
+ private static Image GetImageForFormat(string format, IDataObject dataObject)
+ {
+ if (format == FORMAT_HTML)
+ {
+ var textObject = ContentAsString(dataObject, FORMAT_HTML, Encoding.UTF8);
+ if (textObject != null)
+ {
+ var doc = new HtmlDocument();
+ doc.LoadHtml(textObject);
+ var imgNodes = doc.DocumentNode.SelectNodes("//img");
+ if (imgNodes != null)
+ {
+ foreach (var imgNode in imgNodes)
+ {
+ var srcAttribute = imgNode.Attributes["src"];
+ var imageUrl = srcAttribute.Value;
+ Log.Debug(imageUrl);
+ var image = NetworkHelper.DownloadImage(imageUrl);
+ if (image != null)
+ {
+ return image;
+ }
+ }
+ }
+ }
+ }
+
+ object clipboardObject = GetFromDataObject(dataObject, format);
+ var imageStream = clipboardObject as MemoryStream;
+ if (!IsValidStream(imageStream))
+ {
+ // TODO: add "HTML Format" support here...
+ return clipboardObject as Image;
+ }
+
+ if (CoreConfig.EnableSpecialDIBClipboardReader)
+ {
+ if (format == FORMAT_17 || format == DataFormats.Dib)
+ {
+ Log.Info("Found DIB stream, trying to process it.");
+ try
+ {
+ if (imageStream != null)
+ {
+ byte[] dibBuffer = new byte[imageStream.Length];
+ imageStream.Read(dibBuffer, 0, dibBuffer.Length);
+ var infoHeader = BinaryStructHelper.FromByteArray(dibBuffer);
+ if (!infoHeader.IsDibV5)
+ {
+ Log.InfoFormat("Using special DIB
+ /// Get Text from the DataObject
+ ///
+ /// string if there is text on the clipboard
+ public static string GetText(IDataObject dataObject)
+ {
+ if (ContainsText(dataObject))
+ {
+ return (string) dataObject.GetData(DataFormats.Text);
+ }
+
+ return null;
+ }
+
+ ///
+ /// Set text to the clipboard
+ ///
+ ///
+ public static void SetClipboardData(string text)
+ {
+ IDataObject ido = new DataObject();
+ ido.SetData(DataFormats.Text, true, text);
+ SetDataObject(ido, true);
+ }
+
+ private static string GetHtmlString(ISurface surface, string filename)
+ {
+ string utf8EncodedHtmlString = Encoding.GetEncoding(0).GetString(Encoding.UTF8.GetBytes(HtmlClipboardString));
+ utf8EncodedHtmlString = utf8EncodedHtmlString.Replace("${width}", surface.Image.Width.ToString());
+ utf8EncodedHtmlString = utf8EncodedHtmlString.Replace("${height}", surface.Image.Height.ToString());
+ utf8EncodedHtmlString = utf8EncodedHtmlString.Replace("${file}", filename.Replace("\\", "/"));
+ StringBuilder sb = new StringBuilder();
+ sb.Append(utf8EncodedHtmlString);
+ sb.Replace("<<<<<<<1", (utf8EncodedHtmlString.IndexOf("", StringComparison.Ordinal) + "".Length).ToString("D8"));
+ sb.Replace("<<<<<<<2", (utf8EncodedHtmlString.IndexOf("", StringComparison.Ordinal)).ToString("D8"));
+ sb.Replace("<<<<<<<3", (utf8EncodedHtmlString.IndexOf("", StringComparison.Ordinal) + "".Length).ToString("D8"));
+ sb.Replace("<<<<<<<4", (utf8EncodedHtmlString.IndexOf("", StringComparison.Ordinal)).ToString("D8"));
+ return sb.ToString();
+ }
+
+ private static string GetHtmlDataUrlString(ISurface surface, MemoryStream pngStream)
+ {
+ string utf8EncodedHtmlString = Encoding.GetEncoding(0).GetString(Encoding.UTF8.GetBytes(HtmlClipboardBase64String));
+ utf8EncodedHtmlString = utf8EncodedHtmlString.Replace("${width}", surface.Image.Width.ToString());
+ utf8EncodedHtmlString = utf8EncodedHtmlString.Replace("${height}", surface.Image.Height.ToString());
+ utf8EncodedHtmlString = utf8EncodedHtmlString.Replace("${format}", "png");
+ utf8EncodedHtmlString = utf8EncodedHtmlString.Replace("${data}", Convert.ToBase64String(pngStream.GetBuffer(), 0, (int) pngStream.Length));
+ StringBuilder sb = new StringBuilder();
+ sb.Append(utf8EncodedHtmlString);
+ sb.Replace("<<<<<<<1", (utf8EncodedHtmlString.IndexOf("", StringComparison.Ordinal) + "".Length).ToString("D8"));
+ sb.Replace("<<<<<<<2", (utf8EncodedHtmlString.IndexOf("", StringComparison.Ordinal)).ToString("D8"));
+ sb.Replace("<<<<<<<3", (utf8EncodedHtmlString.IndexOf("", StringComparison.Ordinal) + "".Length).ToString("D8"));
+ sb.Replace("<<<<<<<4", (utf8EncodedHtmlString.IndexOf("", StringComparison.Ordinal)).ToString("D8"));
+ return sb.ToString();
+ }
+
+ private const int BITMAPFILEHEADER_LENGTH = 14;
+
+ ///
+ /// Set an Image to the clipboard
+ /// This method will place images to the clipboard depending on the ClipboardFormats setting.
+ /// e.g. Bitmap which works with pretty much everything and type Dib for e.g. OpenOffice
+ /// because OpenOffice has a bug http://qa.openoffice.org/issues/show_bug.cgi?id=85661
+ /// The Dib (Device Indenpendend Bitmap) in 32bpp actually won't work with Powerpoint 2003!
+ /// When pasting a Dib in PP 2003 the Bitmap is somehow shifted left!
+ /// For this problem the user should not use the direct paste (=Dib), but select Bitmap
+ ///
+ public static void SetClipboardData(ISurface surface)
+ {
+ DataObject dataObject = new DataObject();
+
+ // This will work for Office and most other applications
+ //ido.SetData(DataFormats.Bitmap, true, image);
+
+ MemoryStream dibStream = null;
+ MemoryStream dibV5Stream = null;
+ MemoryStream pngStream = null;
+ Image imageToSave = null;
+ bool disposeImage = false;
+ try
+ {
+ SurfaceOutputSettings outputSettings = new SurfaceOutputSettings(OutputFormat.png, 100, false);
+ // Create the image which is going to be saved so we don't create it multiple times
+ disposeImage = ImageOutput.CreateImageFromSurface(surface, outputSettings, out imageToSave);
+ try
+ {
+ // Create PNG stream
+ if (CoreConfig.ClipboardFormats.Contains(ClipboardFormat.PNG))
+ {
+ pngStream = new MemoryStream();
+ // PNG works for e.g. Powerpoint
+ SurfaceOutputSettings pngOutputSettings = new SurfaceOutputSettings(OutputFormat.png, 100, false);
+ ImageOutput.SaveToStream(imageToSave, null, pngStream, pngOutputSettings);
+ pngStream.Seek(0, SeekOrigin.Begin);
+ // Set the PNG stream
+ dataObject.SetData(FORMAT_PNG, false, pngStream);
+ }
+ }
+ catch (Exception pngEx)
+ {
+ Log.Error("Error creating PNG for the Clipboard.", pngEx);
+ }
+
+ try
+ {
+ if (CoreConfig.ClipboardFormats.Contains(ClipboardFormat.DIB))
+ {
+ using (MemoryStream tmpBmpStream = new MemoryStream())
+ {
+ // Save image as BMP
+ SurfaceOutputSettings bmpOutputSettings = new SurfaceOutputSettings(OutputFormat.bmp, 100, false);
+ ImageOutput.SaveToStream(imageToSave, null, tmpBmpStream, bmpOutputSettings);
+
+ dibStream = new MemoryStream();
+ // Copy the source, but skip the "BITMAPFILEHEADER" which has a size of 14
+ dibStream.Write(tmpBmpStream.GetBuffer(), BITMAPFILEHEADER_LENGTH, (int) tmpBmpStream.Length - BITMAPFILEHEADER_LENGTH);
+ }
+
+ // Set the DIB to the clipboard DataObject
+ dataObject.SetData(DataFormats.Dib, true, dibStream);
+ }
+ }
+ catch (Exception dibEx)
+ {
+ Log.Error("Error creating DIB for the Clipboard.", dibEx);
+ }
+
+ // CF_DibV5
+ try
+ {
+ if (CoreConfig.ClipboardFormats.Contains(ClipboardFormat.DIBV5))
+ {
+ // Create the stream for the clipboard
+ dibV5Stream = new MemoryStream();
+
+ // Create the BITMAPINFOHEADER
+ BITMAPINFOHEADER header = new BITMAPINFOHEADER(imageToSave.Width, imageToSave.Height, 32)
+ {
+ // Make sure we have BI_BITFIELDS, this seems to be normal for Format17?
+ biCompression = BI_COMPRESSION.BI_BITFIELDS
+ };
+ // Create a byte[] to write
+ byte[] headerBytes = BinaryStructHelper.ToByteArray(header);
+ // Write the BITMAPINFOHEADER to the stream
+ dibV5Stream.Write(headerBytes, 0, headerBytes.Length);
+
+ // As we have specified BI_COMPRESSION.BI_BITFIELDS, the BitfieldColorMask needs to be added
+ BitfieldColorMask colorMask = new BitfieldColorMask();
+ // Make sure the values are set
+ colorMask.InitValues();
+ // Create the byte[] from the struct
+ byte[] colorMaskBytes = BinaryStructHelper.ToByteArray(colorMask);
+ Array.Reverse(colorMaskBytes);
+ // Write to the stream
+ dibV5Stream.Write(colorMaskBytes, 0, colorMaskBytes.Length);
+
+ // Create the raw bytes for the pixels only
+ byte[] bitmapBytes = BitmapToByteArray((Bitmap) imageToSave);
+ // Write to the stream
+ dibV5Stream.Write(bitmapBytes, 0, bitmapBytes.Length);
+
+ // Set the DIBv5 to the clipboard DataObject
+ dataObject.SetData(FORMAT_17, true, dibV5Stream);
+ }
+ }
+ catch (Exception dibEx)
+ {
+ Log.Error("Error creating DIB for the Clipboard.", dibEx);
+ }
+
+ // Set the HTML
+ if (CoreConfig.ClipboardFormats.Contains(ClipboardFormat.HTML))
+ {
+ string tmpFile = ImageOutput.SaveToTmpFile(surface, new SurfaceOutputSettings(OutputFormat.png, 100, false), null);
+ string html = GetHtmlString(surface, tmpFile);
+ dataObject.SetText(html, TextDataFormat.Html);
+ }
+ else if (CoreConfig.ClipboardFormats.Contains(ClipboardFormat.HTMLDATAURL))
+ {
+ string html;
+ using (MemoryStream tmpPngStream = new MemoryStream())
+ {
+ SurfaceOutputSettings pngOutputSettings = new SurfaceOutputSettings(OutputFormat.png, 100, false)
+ {
+ // Do not allow to reduce the colors, some applications dislike 256 color images
+ // reported with bug #3594681
+ DisableReduceColors = true
+ };
+ // Check if we can use the previously used image
+ if (imageToSave.PixelFormat != PixelFormat.Format8bppIndexed)
+ {
+ ImageOutput.SaveToStream(imageToSave, surface, tmpPngStream, pngOutputSettings);
+ }
+ else
+ {
+ ImageOutput.SaveToStream(surface, tmpPngStream, pngOutputSettings);
+ }
+
+ html = GetHtmlDataUrlString(surface, tmpPngStream);
+ }
+
+ dataObject.SetText(html, TextDataFormat.Html);
+ }
+ }
+ finally
+ {
+ // we need to use the SetDataOject before the streams are closed otherwise the buffer will be gone!
+ // Check if Bitmap is wanted
+ if (CoreConfig.ClipboardFormats.Contains(ClipboardFormat.BITMAP))
+ {
+ dataObject.SetImage(imageToSave);
+ // Place the DataObject to the clipboard
+ SetDataObject(dataObject, true);
+ }
+ else
+ {
+ // Place the DataObject to the clipboard
+ SetDataObject(dataObject, true);
+ }
+
+ pngStream?.Dispose();
+ dibStream?.Dispose();
+ dibV5Stream?.Dispose();
+ // cleanup if needed
+ if (disposeImage)
+ {
+ imageToSave?.Dispose();
+ }
+ }
+ }
+
+ ///
+ /// Helper method so get the bitmap bytes
+ /// See: http://stackoverflow.com/a/6570155
+ ///
+ /// Bitmap
+ /// byte[]
+ private static byte[] BitmapToByteArray(Bitmap bitmap)
+ {
+ // Lock the bitmap's bits.
+ Rectangle rect = new Rectangle(0, 0, bitmap.Width, bitmap.Height);
+ BitmapData bmpData = bitmap.LockBits(rect, ImageLockMode.ReadOnly, bitmap.PixelFormat);
+
+ int absStride = Math.Abs(bmpData.Stride);
+ int bytes = absStride * bitmap.Height;
+ long ptr = bmpData.Scan0.ToInt32();
+ // Declare an array to hold the bytes of the bitmap.
+ byte[] rgbValues = new byte[bytes];
+
+ for (int i = 0; i < bitmap.Height; i++)
+ {
+ IntPtr pointer = new IntPtr(ptr + (bmpData.Stride * i));
+ Marshal.Copy(pointer, rgbValues, absStride * (bitmap.Height - i - 1), absStride);
+ }
+
+ // Unlock the bits.
+ bitmap.UnlockBits(bmpData);
+
+ return rgbValues;
+ }
+
+ ///
+ /// Set Object with type Type to the clipboard
+ ///
+ /// Type
+ /// object
+ public static void SetClipboardData(Type type, object obj)
+ {
+ DataFormats.Format format = DataFormats.GetFormat(type.FullName);
+
+ //now copy to clipboard
+ IDataObject dataObj = new DataObject();
+ dataObj.SetData(format.Name, false, obj);
+ // Use false to make the object disappear when the application stops.
+ SetDataObject(dataObj, true);
+ }
+
+ ///
+ /// Retrieve a list of all formats currently in the IDataObject
+ ///
+ /// List of string with the current formats
+ public static List GetFormats(IDataObject dataObj)
+ {
+ string[] formats = null;
+
+ if (dataObj != null)
+ {
+ formats = dataObj.GetFormats();
+ }
+
+ if (formats != null)
+ {
+ Log.DebugFormat("Got clipboard formats: {0}", string.Join(",", formats));
+ return new List(formats);
+ }
+
+ return new List();
+ }
+
+ ///
+ /// Check if there is currently something on the clipboard which has the supplied format
+ ///
+ /// IDataObject
+ /// string with format
+ /// true if one the format is found
+ public static bool ContainsFormat(IDataObject dataObject, string format)
+ {
+ return ContainsFormat(dataObject, new[]
+ {
+ format
+ });
+ }
+
+ ///
+ /// Check if there is currently something on the clipboard which has one of the supplied formats
+ ///
+ /// string[] with formats
+ /// true if one of the formats was found
+ public static bool ContainsFormat(string[] formats)
+ {
+ return ContainsFormat(GetDataObject(), formats);
+ }
+
+ ///
+ /// Check if there is currently something on the clipboard which has one of the supplied formats
+ ///
+ /// IDataObject
+ /// string[] with formats
+ /// true if one of the formats was found
+ public static bool ContainsFormat(IDataObject dataObject, string[] formats)
+ {
+ bool formatFound = false;
+ List currentFormats = GetFormats(dataObject);
+ if (currentFormats == null || currentFormats.Count == 0 || formats == null || formats.Length == 0)
+ {
+ return false;
+ }
+
+ foreach (string format in formats)
+ {
+ if (currentFormats.Contains(format))
+ {
+ formatFound = true;
+ break;
+ }
+ }
+
+ return formatFound;
+ }
+
+ ///
+ /// Get Object for format from IDataObject
+ ///
+ /// IDataObject
+ /// Type to get
+ /// object from IDataObject
+ public static object GetFromDataObject(IDataObject dataObj, Type type)
+ {
+ if (type != null)
+ {
+ return GetFromDataObject(dataObj, type.FullName);
+ }
+
+ return null;
+ }
+
+ ///
+ /// Get ImageFilenames from the IDataObject
+ ///
+ /// IDataObject
+ ///
+ public static IEnumerable GetImageFilenames(IDataObject dataObject)
+ {
+ string[] dropFileNames = (string[]) dataObject.GetData(DataFormats.FileDrop);
+ if (dropFileNames != null && dropFileNames.Length > 0)
+ {
+ return dropFileNames
+ .Where(filename => !string.IsNullOrEmpty(filename))
+ .Where(Path.HasExtension)
+ .Where(filename => ImageHelper.StreamConverters.Keys.Contains(Path.GetExtension(filename).ToLowerInvariant().Substring(1)));
+ }
+
+ return Enumerable.Empty();
+ }
+
+ ///
+ /// Get Object for format from IDataObject
+ ///
+ /// IDataObject
+ /// format to get
+ /// object from IDataObject
+ public static object GetFromDataObject(IDataObject dataObj, string format)
+ {
+ if (dataObj != null)
+ {
+ try
+ {
+ return dataObj.GetData(format);
+ }
+ catch (Exception e)
+ {
+ Log.Error("Error in GetClipboardData.", e);
+ }
+ }
+
+ return null;
+ }
+ }
}
\ No newline at end of file
diff --git a/src/GreenshotPlugin/Core/CoreConfiguration.cs b/src/Greenshot.Base/Core/CoreConfiguration.cs
similarity index 97%
rename from src/GreenshotPlugin/Core/CoreConfiguration.cs
rename to src/Greenshot.Base/Core/CoreConfiguration.cs
index d099770a7..638613011 100644
--- a/src/GreenshotPlugin/Core/CoreConfiguration.cs
+++ b/src/Greenshot.Base/Core/CoreConfiguration.cs
@@ -1,737 +1,737 @@
-/*
- * Greenshot - a free and open source screenshot tool
- * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
- *
- * For more information see: http://getgreenshot.org/
- * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 1 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-using System;
-using System.Collections.Generic;
-using System.ComponentModel;
-using System.Drawing;
-using System.IO;
-using System.Reflection;
-using System.Windows.Forms;
-using GreenshotPlugin.IniFile;
-using GreenshotPlugin.Interfaces;
-
-namespace GreenshotPlugin.Core
-{
- public enum ClipboardFormat
- {
- PNG,
- DIB,
- HTML,
- HTMLDATAURL,
- BITMAP,
- DIBV5
- }
-
- public enum OutputFormat
- {
- bmp,
- gif,
- jpg,
- png,
- tiff,
- greenshot,
- ico
- }
-
- public enum WindowCaptureMode
- {
- Screen,
- GDI,
- Aero,
- AeroTransparent,
- Auto
- }
-
- public enum BuildStates
- {
- UNSTABLE,
- RELEASE_CANDIDATE,
- RELEASE
- }
-
- public enum ClickActions
- {
- DO_NOTHING,
- OPEN_LAST_IN_EXPLORER,
- OPEN_LAST_IN_EDITOR,
- OPEN_SETTINGS,
- SHOW_CONTEXT_MENU
- }
-
- ///
- /// Description of CoreConfiguration.
- ///
- [IniSection("Core", Description = "Greenshot core configuration")]
- public class CoreConfiguration : IniSection, INotifyPropertyChanged
- {
- public event PropertyChangedEventHandler PropertyChanged;
-
- [IniProperty("Language", Description = "The language in IETF format (e.g. en-US)")]
- public string Language { get; set; }
-
- [IniProperty("RegionHotkey", Description = "Hotkey for starting the region capture", DefaultValue = "PrintScreen")]
- public string RegionHotkey { get; set; }
-
- [IniProperty("WindowHotkey", Description = "Hotkey for starting the window capture", DefaultValue = "Alt + PrintScreen")]
- public string WindowHotkey { get; set; }
-
- [IniProperty("FullscreenHotkey", Description = "Hotkey for starting the fullscreen capture", DefaultValue = "Ctrl + PrintScreen")]
- public string FullscreenHotkey { get; set; }
-
- [IniProperty("LastregionHotkey", Description = "Hotkey for starting the last region capture", DefaultValue = "Shift + PrintScreen")]
- public string LastregionHotkey { get; set; }
-
- [IniProperty("IEHotkey", Description = "Hotkey for starting the IE capture", DefaultValue = "Shift + Ctrl + PrintScreen")]
- public string IEHotkey { get; set; }
-
- [IniProperty("ClipboardHotkey", Description = "Hotkey for opening the clipboard contents into the editor")]
- public string ClipboardHotkey { get; set; }
-
- [IniProperty("IsFirstLaunch", Description = "Is this the first time launch?", DefaultValue = "true")]
- public bool IsFirstLaunch { get; set; }
-
- [IniProperty("Destinations", Separator = ",",
- Description = "Which destinations? Possible options (more might be added by plugins) are: Editor, FileDefault, FileWithDialog, Clipboard, Printer, EMail, Picker",
- DefaultValue = "Picker")]
- public List OutputDestinations { get; set; } = new List();
-
- [IniProperty("ClipboardFormats", Separator = ",", Description = "Specify which formats we copy on the clipboard? Options are: PNG, HTML, HTMLDATAURL and DIB",
- DefaultValue = "PNG,DIB")]
- public List ClipboardFormats { get; set; } = new List();
-
- [IniProperty("CaptureMousepointer", Description = "Should the mouse be captured?", DefaultValue = "true")]
- public bool CaptureMousepointer { get; set; }
-
- [IniProperty("CaptureWindowsInteractive", Description = "Use interactive window selection to capture? (false=Capture active window)", DefaultValue = "false")]
- public bool CaptureWindowsInteractive { get; set; }
-
- [IniProperty("CaptureDelay", Description = "Capture delay in millseconds.", DefaultValue = "100")]
- public int CaptureDelay { get; set; }
-
- [IniProperty("ScreenCaptureMode", Description = "The capture mode used to capture a screen. (Auto, FullScreen, Fixed)", DefaultValue = "Auto")]
- public ScreenCaptureMode ScreenCaptureMode { get; set; }
-
- [IniProperty("ScreenToCapture", Description = "The screen number to capture when using ScreenCaptureMode Fixed.", DefaultValue = "1")]
- public int ScreenToCapture { get; set; }
-
- [IniProperty("WindowCaptureMode", Description = "The capture mode used to capture a Window (Screen, GDI, Aero, AeroTransparent, Auto).", DefaultValue = "Auto")]
- public WindowCaptureMode WindowCaptureMode { get; set; }
-
- [IniProperty("WindowCaptureAllChildLocations",
- Description = "Enable/disable capture all children, very slow but will make it possible to use this information in the editor.", DefaultValue = "False")]
- public bool WindowCaptureAllChildLocations { get; set; }
-
- [IniProperty("DWMBackgroundColor", Description = "The background color for a DWM window capture.")]
- public Color DWMBackgroundColor { get; set; }
-
- [IniProperty("PlayCameraSound", LanguageKey = "settings_playsound", Description = "Play a camera sound after taking a capture.", DefaultValue = "false")]
- public bool PlayCameraSound { get; set; } = false;
-
- [IniProperty("ShowTrayNotification", LanguageKey = "settings_shownotify", Description = "Show a notification from the systray when a capture is taken.",
- DefaultValue = "true")]
- public bool ShowTrayNotification { get; set; } = true;
-
- [IniProperty("OutputFilePath", Description = "Output file path.")]
- public string OutputFilePath { get; set; }
-
- [IniProperty("OutputFileAllowOverwrite",
- Description = "If the target file already exists True will make Greenshot always overwrite and False will display a 'Save-As' dialog.", DefaultValue = "true")]
- public bool OutputFileAllowOverwrite { get; set; }
-
- [IniProperty("OutputFileFilenamePattern", Description = "Filename pattern for screenshot.", DefaultValue = "${capturetime:d\"yyyy-MM-dd HH_mm_ss\"}-${title}")]
- public string OutputFileFilenamePattern { get; set; }
-
- [IniProperty("OutputFileFormat", Description = "Default file type for writing screenshots. (bmp, gif, jpg, png, tiff)", DefaultValue = "png")]
- public OutputFormat OutputFileFormat { get; set; } = OutputFormat.png;
-
- [IniProperty("OutputFileReduceColors", Description = "If set to true, than the colors of the output file are reduced to 256 (8-bit) colors", DefaultValue = "false")]
- public bool OutputFileReduceColors { get; set; }
-
- [IniProperty("OutputFileAutoReduceColors",
- Description = "If set to true the amount of colors is counted and if smaller than 256 the color reduction is automatically used.", DefaultValue = "false")]
- public bool OutputFileAutoReduceColors { get; set; }
-
- [IniProperty("OutputFileReduceColorsTo", Description = "Amount of colors to reduce to, when reducing", DefaultValue = "256")]
- public int OutputFileReduceColorsTo { get; set; }
-
- [IniProperty("OutputFileCopyPathToClipboard", Description = "When saving a screenshot, copy the path to the clipboard?", DefaultValue = "true")]
- public bool OutputFileCopyPathToClipboard { get; set; }
-
- [IniProperty("OutputFileAsFullpath", Description = "SaveAs Full path?")]
- public string OutputFileAsFullpath { get; set; }
-
- [IniProperty("OutputFileJpegQuality", Description = "JPEG file save quality in %.", DefaultValue = "80")]
- public int OutputFileJpegQuality { get; set; }
-
- [IniProperty("OutputFilePromptQuality", Description = "Ask for the quality before saving?", DefaultValue = "false")]
- public bool OutputFilePromptQuality { get; set; }
-
- [IniProperty("OutputFileIncrementingNumber", Description = "The number for the ${NUM} in the filename pattern, is increased automatically after each save.",
- DefaultValue = "1")]
- public uint OutputFileIncrementingNumber { get; set; }
-
- [IniProperty("OutputPrintPromptOptions", LanguageKey = "settings_alwaysshowprintoptionsdialog", Description = "Ask for print options when printing?",
- DefaultValue = "true")]
- public bool OutputPrintPromptOptions { get; set; }
-
- [IniProperty("OutputPrintAllowRotate", LanguageKey = "printoptions_allowrotate", Description = "Allow rotating the picture for fitting on paper?", DefaultValue = "false")]
- public bool OutputPrintAllowRotate { get; set; }
-
- [IniProperty("OutputPrintAllowEnlarge", LanguageKey = "printoptions_allowenlarge", Description = "Allow growing the picture for fitting on paper?", DefaultValue = "false")]
- public bool OutputPrintAllowEnlarge { get; set; }
-
- [IniProperty("OutputPrintAllowShrink", LanguageKey = "printoptions_allowshrink", Description = "Allow shrinking the picture for fitting on paper?", DefaultValue = "true")]
- public bool OutputPrintAllowShrink { get; set; }
-
- [IniProperty("OutputPrintCenter", LanguageKey = "printoptions_allowcenter", Description = "Center image when printing?", DefaultValue = "true")]
- public bool OutputPrintCenter { get; set; }
-
- [IniProperty("OutputPrintInverted", LanguageKey = "printoptions_inverted", Description = "Print image inverted (use e.g. for console captures)", DefaultValue = "false")]
- public bool OutputPrintInverted { get; set; }
-
- [IniProperty("OutputPrintGrayscale", LanguageKey = "printoptions_printgrayscale", Description = "Force grayscale printing", DefaultValue = "false")]
- public bool OutputPrintGrayscale { get; set; }
-
- [IniProperty("OutputPrintMonochrome", LanguageKey = "printoptions_printmonochrome", Description = "Force monorchrome printing", DefaultValue = "false")]
- public bool OutputPrintMonochrome { get; set; }
-
- [IniProperty("OutputPrintMonochromeThreshold", Description = "Threshold for monochrome filter (0 - 255), lower value means less black", DefaultValue = "127")]
- public byte OutputPrintMonochromeThreshold { get; set; }
-
- [IniProperty("OutputPrintFooter", LanguageKey = "printoptions_timestamp", Description = "Print footer on print?", DefaultValue = "true")]
- public bool OutputPrintFooter { get; set; }
-
- [IniProperty("OutputPrintFooterPattern", Description = "Footer pattern", DefaultValue = "${capturetime:d\"D\"} ${capturetime:d\"T\"} - ${title}")]
- public string OutputPrintFooterPattern { get; set; }
-
- [IniProperty("NotificationSound", Description = "The wav-file to play when a capture is taken, loaded only once at the Greenshot startup", DefaultValue = "default")]
- public string NotificationSound { get; set; }
-
- [IniProperty("UseProxy", Description = "Use your global proxy?", DefaultValue = "True")]
- public bool UseProxy { get; set; }
-
- [IniProperty("IECapture", Description = "Enable/disable IE capture", DefaultValue = "True")]
- public bool IECapture { get; set; }
-
- [IniProperty("IEFieldCapture", Description = "Enable/disable IE field capture, very slow but will make it possible to annotate the fields of a capture in the editor.",
- DefaultValue = "False")]
- public bool IEFieldCapture { get; set; }
-
- [IniProperty("WindowClassesToCheckForIE", Description = "Comma separated list of Window-Classes which need to be checked for a IE instance!",
- DefaultValue = "AfxFrameOrView70,IMWindowClass")]
- public List WindowClassesToCheckForIE { get; set; }
-
- [IniProperty("AutoCropDifference",
- Description =
- "Sets how to compare the colors for the autocrop detection, the higher the more is 'selected'. Possible values are from 0 to 255, where everything above ~150 doesn't make much sense!",
- DefaultValue = "10")]
- public int AutoCropDifference { get; set; }
-
- [IniProperty("IncludePlugins",
- Description = "Comma separated list of Plugins which are allowed. If something in the list, than every plugin not in the list will not be loaded!")]
- public List IncludePlugins { get; set; }
-
- [IniProperty("ExcludePlugins", Description = "Comma separated list of Plugins which are NOT allowed.")]
- public List ExcludePlugins { get; set; }
-
- [IniProperty("ExcludeDestinations", Description = "Comma separated list of destinations which should be disabled.")]
- public List ExcludeDestinations { get; set; }
-
- [IniProperty("UpdateCheckInterval", Description = "How many days between every update check? (0=no checks)", DefaultValue = "14")]
- public int UpdateCheckInterval { get; set; }
-
- [IniProperty("LastUpdateCheck", Description = "Last update check")]
- public DateTime LastUpdateCheck { get; set; }
-
- [IniProperty("DisableSettings", Description = "Enable/disable the access to the settings, can only be changed manually in this .ini", DefaultValue = "False")]
- public bool DisableSettings { get; set; }
-
- [IniProperty("DisableQuickSettings", Description = "Enable/disable the access to the quick settings, can only be changed manually in this .ini", DefaultValue = "False")]
- public bool DisableQuickSettings { get; set; }
-
- [IniProperty("DisableTrayicon", Description = "Disable the trayicon, can only be changed manually in this .ini", DefaultValue = "False")]
- public bool HideTrayicon { get; set; }
-
- [IniProperty("HideExpertSettings", Description = "Hide expert tab in the settings, can only be changed manually in this .ini", DefaultValue = "False")]
- public bool HideExpertSettings { get; set; }
-
- [IniProperty("ThumnailPreview", Description = "Enable/disable thumbnail previews", DefaultValue = "True")]
- public bool ThumnailPreview { get; set; }
-
- [IniProperty("NoGDICaptureForProduct", Description = "List of productnames for which GDI capturing is skipped (using fallback).", DefaultValue = "IntelliJ IDEA")]
- public List NoGDICaptureForProduct { get; set; }
-
- [IniProperty("NoDWMCaptureForProduct", Description = "List of productnames for which DWM capturing is skipped (using fallback).", DefaultValue = "Citrix ICA Client")]
- public List NoDWMCaptureForProduct { get; set; }
-
- [IniProperty("OptimizeForRDP", Description = "Make some optimizations for usage with remote desktop", DefaultValue = "False")]
- public bool OptimizeForRDP { get; set; }
-
- [IniProperty("DisableRDPOptimizing", Description = "Disable all optimizations for usage with remote desktop", DefaultValue = "False")]
- public bool DisableRDPOptimizing { get; set; }
-
- [IniProperty("MinimizeWorkingSetSize", Description = "Optimize memory footprint, but with a performance penalty!", DefaultValue = "False")]
- public bool MinimizeWorkingSetSize { get; set; }
-
- [IniProperty("WindowCaptureRemoveCorners", Description = "Remove the corners from a window capture", DefaultValue = "True")]
- public bool WindowCaptureRemoveCorners { get; set; }
-
- [IniProperty("CheckForUnstable", Description = "Also check for unstable version updates", DefaultValue = "False")]
- public bool CheckForUnstable { get; set; }
-
- [IniProperty("ActiveTitleFixes", Description = "The fixes that are active.")]
- public List ActiveTitleFixes { get; set; }
-
- [IniProperty("TitleFixMatcher", Description = "The regular expressions to match the title with.")]
- public Dictionary TitleFixMatcher { get; set; }
-
- [IniProperty("TitleFixReplacer", Description = "The replacements for the matchers.")]
- public Dictionary TitleFixReplacer { get; set; }
-
- [IniProperty("ExperimentalFeatures", Description = "A list of experimental features, this allows us to test certain features before releasing them.", ExcludeIfNull = true)]
- public List ExperimentalFeatures { get; set; }
-
- [IniProperty("EnableSpecialDIBClipboardReader", Description = "Enable a special DIB clipboard reader", DefaultValue = "True")]
- public bool EnableSpecialDIBClipboardReader { get; set; }
-
- [IniProperty("WindowCornerCutShape", Description = "The cutshape which is used to remove the window corners, is mirrorred for all corners", DefaultValue = "5,3,2,1,1")]
- public List WindowCornerCutShape { get; set; }
-
- [IniProperty("LeftClickAction",
- Description =
- "Specify what action is made if the tray icon is left clicked, if a double-click action is specified this action is initiated after a delay (configurable via the windows double-click speed)",
- DefaultValue = "SHOW_CONTEXT_MENU")]
- public ClickActions LeftClickAction { get; set; }
-
- [IniProperty("DoubleClickAction", Description = "Specify what action is made if the tray icon is double clicked", DefaultValue = "OPEN_LAST_IN_EXPLORER")]
- public ClickActions DoubleClickAction { get; set; }
-
- [IniProperty("ZoomerEnabled", Description = "Sets if the zoomer is enabled", DefaultValue = "True")]
- public bool ZoomerEnabled { get; set; }
-
- [IniProperty("ZoomerOpacity",
- Description = "Specify the transparency for the zoomer, from 0-1 (where 1 is no transparency and 0 is complete transparent. An usefull setting would be 0.7)",
- DefaultValue = "1")]
- public float ZoomerOpacity { get; set; }
-
- [IniProperty("MaxMenuItemLength",
- Description = "Maximum length of submenu items in the context menu, making this longer might cause context menu issues on dual screen systems.", DefaultValue = "25")]
- public int MaxMenuItemLength { get; set; }
-
- [IniProperty("MailApiTo", Description = "The 'to' field for the email destination (settings for Outlook can be found under the Office section)", DefaultValue = "")]
- public string MailApiTo { get; set; }
-
- [IniProperty("MailApiCC", Description = "The 'CC' field for the email destination (settings for Outlook can be found under the Office section)", DefaultValue = "")]
- public string MailApiCC { get; set; }
-
- [IniProperty("MailApiBCC", Description = "The 'BCC' field for the email destination (settings for Outlook can be found under the Office section)", DefaultValue = "")]
- public string MailApiBCC { get; set; }
-
- [IniProperty("OptimizePNGCommand",
- Description =
- "Optional command to execute on a temporary PNG file, the command should overwrite the file and Greenshot will read it back. Note: this command is also executed when uploading PNG's!",
- DefaultValue = "")]
- public string OptimizePNGCommand { get; set; }
-
- [IniProperty("OptimizePNGCommandArguments",
- Description =
- "Arguments for the optional command to execute on a PNG, {0} is replaced by the temp-filename from Greenshot. Note: Temp-file is deleted afterwards by Greenshot.",
- DefaultValue = "\"{0}\"")]
- public string OptimizePNGCommandArguments { get; set; }
-
- [IniProperty("LastSaveWithVersion", Description = "Version of Greenshot which created this .ini")]
- public string LastSaveWithVersion { get; set; }
-
- [IniProperty("ProcessEXIFOrientation", Description = "When reading images from files or clipboard, use the EXIF information to correct the orientation",
- DefaultValue = "True")]
- public bool ProcessEXIFOrientation { get; set; }
-
- [IniProperty("LastCapturedRegion", Description = "The last used region, for reuse in the capture last region")]
- public Rectangle LastCapturedRegion { get; set; }
-
- [IniProperty("Win10BorderCrop", Description = "The capture is cropped with these settings, e.g. when you don't want to color around it -1,-1"), DefaultValue("0,0")]
- public Size Win10BorderCrop { get; set; }
-
- private Size _iconSize;
-
- [IniProperty("BaseIconSize",
- Description = "Defines the base size of the icons (e.g. for the buttons in the editor), default value 16,16 and it's scaled to the current DPI",
- DefaultValue = "16,16")]
- public Size IconSize
- {
- get { return _iconSize; }
- set
- {
- Size newSize = value;
- if (newSize != Size.Empty)
- {
- if (newSize.Width < 16)
- {
- newSize.Width = 16;
- }
- else if (newSize.Width > 256)
- {
- newSize.Width = 256;
- }
-
- newSize.Width = (newSize.Width / 16) * 16;
- if (newSize.Height < 16)
- {
- newSize.Height = 16;
- }
- else if (IconSize.Height > 256)
- {
- newSize.Height = 256;
- }
-
- newSize.Height = (newSize.Height / 16) * 16;
- }
-
- if (_iconSize != newSize)
- {
- _iconSize = value;
- PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("IconSize"));
- }
- }
- }
-
- public Size ScaledIconSize => DpiHelper.ScaleWithCurrentDpi(_iconSize);
-
- [IniProperty("WebRequestTimeout", Description = "The connect timeout value for webrequets, these are seconds", DefaultValue = "100")]
- public int WebRequestTimeout { get; set; }
-
- [IniProperty("WebRequestReadWriteTimeout", Description = "The read/write timeout value for webrequets, these are seconds", DefaultValue = "100")]
- public int WebRequestReadWriteTimeout { get; set; }
-
- public bool UseLargeIcons => IconSize.Width >= 32 || IconSize.Height >= 32;
-
- ///
- /// A helper method which returns true if the supplied experimental feature is enabled
- ///
- ///
- ///
- public bool IsExperimentalFeatureEnabled(string experimentalFeature)
- {
- return (ExperimentalFeatures != null && ExperimentalFeatures.Contains(experimentalFeature));
- }
-
- ///
- /// 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)
- {
- switch (property)
- {
- case "PluginWhitelist":
- case "PluginBacklist":
- return new List();
- case "OutputFileAsFullpath":
- if (IniConfig.IsPortable)
- {
- return Path.Combine(Application.StartupPath, @"..\..\Documents\Pictures\Greenshots\dummy.png");
- }
-
- return Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "dummy.png");
- case "OutputFilePath":
- if (IniConfig.IsPortable)
- {
- string pafOutputFilePath = Path.Combine(Application.StartupPath, @"..\..\Documents\Pictures\Greenshots");
- if (!Directory.Exists(pafOutputFilePath))
- {
- try
- {
- Directory.CreateDirectory(pafOutputFilePath);
- return pafOutputFilePath;
- }
- catch (Exception ex)
- {
- LOG.Warn(ex);
- // Problem creating directory, fallback to Desktop
- }
- }
- else
- {
- return pafOutputFilePath;
- }
- }
-
- return Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
- case "DWMBackgroundColor":
- return Color.Transparent;
- case "ActiveTitleFixes":
- return new List
- {
- "Firefox",
- "IE",
- "Chrome"
- };
- case "TitleFixMatcher":
- return new Dictionary
- {
- {
- "Firefox", " - Mozilla Firefox.*"
- },
- {
- "IE", " - (Microsoft|Windows) Internet Explorer.*"
- },
- {
- "Chrome", " - Google Chrome.*"
- }
- };
- case "TitleFixReplacer":
- return new Dictionary
- {
- {
- "Firefox", string.Empty
- },
- {
- "IE", string.Empty
- },
- {
- "Chrome", string.Empty
- }
- };
- }
-
- return null;
- }
-
- ///
- /// This method will be called before converting the property, making to possible to correct a certain value
- /// Can be used when migration is needed
- ///
- /// The name of the property
- /// The string value of the property
- /// string with the propertyValue, modified or not...
- public override string PreCheckValue(string propertyName, string propertyValue)
- {
- // Changed the separator, now we need to correct this
- if ("Destinations".Equals(propertyName))
- {
- if (propertyValue != null)
- {
- return propertyValue.Replace('|', ',');
- }
- }
-
- if ("OutputFilePath".Equals(propertyName))
- {
- if (string.IsNullOrEmpty(propertyValue))
- {
- return null;
- }
- }
-
- return base.PreCheckValue(propertyName, propertyValue);
- }
-
- ///
- /// This method will be called before writing the configuration
- ///
- public override void BeforeSave()
- {
- try
- {
- // Store version, this can be used later to fix settings after an update
- LastSaveWithVersion = Assembly.GetEntryAssembly().GetName().Version.ToString();
- }
- catch
- {
- // ignored
- }
- }
-
- ///
- /// This method will be called after reading the configuration, so eventually some corrections can be made
- ///
- public override void AfterLoad()
- {
- // Comment with releases
- // CheckForUnstable = true;
-
- if (string.IsNullOrEmpty(LastSaveWithVersion))
- {
- try
- {
- // Store version, this can be used later to fix settings after an update
- LastSaveWithVersion = Assembly.GetEntryAssembly().GetName().Version.ToString();
- }
- catch
- {
- // ignored
- }
-
- // Disable the AutoReduceColors as it causes issues with Mozzila applications and some others
- OutputFileAutoReduceColors = false;
- }
-
- // Fix for excessive feed checking
- if (UpdateCheckInterval != 0 && UpdateCheckInterval <= 7 && LastSaveWithVersion.StartsWith("1.2"))
- {
- UpdateCheckInterval = 14;
- }
-
- if (UpdateCheckInterval > 365)
- {
- UpdateCheckInterval = 365;
- }
-
- // Enable OneNote if upgrading from 1.1
- if (ExcludeDestinations != null && ExcludeDestinations.Contains("OneNote"))
- {
- if (LastSaveWithVersion != null && LastSaveWithVersion.StartsWith("1.1"))
- {
- ExcludeDestinations.Remove("OneNote");
- }
- else
- {
- // TODO: Remove with the release
- ExcludeDestinations.Remove("OneNote");
- }
- }
-
- if (OutputDestinations == null)
- {
- OutputDestinations = new List();
- }
-
- // Make sure there is an output!
- if (OutputDestinations.Count == 0)
- {
- OutputDestinations.Add("Editor");
- }
-
- // Prevent both settings at once, bug #3435056
- if (OutputDestinations.Contains("Clipboard") && OutputFileCopyPathToClipboard)
- {
- OutputFileCopyPathToClipboard = false;
- }
-
- // Make sure we have clipboard formats, otherwise a paste doesn't make sense!
- if (ClipboardFormats == null || ClipboardFormats.Count == 0)
- {
- ClipboardFormats = new List
- {
- ClipboardFormat.PNG,
- ClipboardFormat.HTML,
- ClipboardFormat.DIB
- };
- }
-
- // Make sure the lists are lowercase, to speedup the check
- if (NoGDICaptureForProduct != null)
- {
- // Fix error in configuration
- if (NoGDICaptureForProduct.Count >= 2)
- {
- if ("intellij".Equals(NoGDICaptureForProduct[0]) && "idea".Equals(NoGDICaptureForProduct[1]))
- {
- NoGDICaptureForProduct.RemoveRange(0, 2);
- NoGDICaptureForProduct.Add("Intellij Idea");
- IsDirty = true;
- }
- }
-
- for (int i = 0; i < NoGDICaptureForProduct.Count; i++)
- {
- NoGDICaptureForProduct[i] = NoGDICaptureForProduct[i].ToLower();
- }
- }
-
- if (NoDWMCaptureForProduct != null)
- {
- // Fix error in configuration
- if (NoDWMCaptureForProduct.Count >= 3)
- {
- if ("citrix".Equals(NoDWMCaptureForProduct[0]) && "ica".Equals(NoDWMCaptureForProduct[1]) && "client".Equals(NoDWMCaptureForProduct[2]))
- {
- NoDWMCaptureForProduct.RemoveRange(0, 3);
- NoDWMCaptureForProduct.Add("Citrix ICA Client");
- IsDirty = true;
- }
- }
-
- for (int i = 0; i < NoDWMCaptureForProduct.Count; i++)
- {
- NoDWMCaptureForProduct[i] = NoDWMCaptureForProduct[i].ToLower();
- }
- }
-
- if (AutoCropDifference < 0)
- {
- AutoCropDifference = 0;
- }
-
- if (AutoCropDifference > 255)
- {
- AutoCropDifference = 255;
- }
-
- if (OutputFileReduceColorsTo < 2)
- {
- OutputFileReduceColorsTo = 2;
- }
-
- if (OutputFileReduceColorsTo > 256)
- {
- OutputFileReduceColorsTo = 256;
- }
-
- if (WebRequestTimeout <= 10)
- {
- WebRequestTimeout = 100;
- }
-
- if (WebRequestReadWriteTimeout < 1)
- {
- WebRequestReadWriteTimeout = 100;
- }
- }
-
- ///
- /// Validate the OutputFilePath, and if this is not correct it will be set to the default
- /// Added for BUG-1992, reset the OutputFilePath / OutputFileAsFullpath if they don't exist (e.g. the configuration is used on a different PC)
- ///
- public void ValidateAndCorrectOutputFilePath()
- {
- if (!Directory.Exists(OutputFilePath))
- {
- OutputFilePath = GetDefault(nameof(OutputFilePath)) as string;
- }
- }
-
- ///
- /// Validate the OutputFileAsFullpath, and if this is not correct it will be set to the default
- /// Added for BUG-1992, reset the OutputFilePath / OutputFileAsFullpath if they don't exist (e.g. the configuration is used on a different PC)
- ///
- public void ValidateAndCorrectOutputFileAsFullpath()
- {
- var outputFilePath = Path.GetDirectoryName(OutputFileAsFullpath);
- if (outputFilePath == null || (!File.Exists(OutputFileAsFullpath) && !Directory.Exists(outputFilePath)))
- {
- OutputFileAsFullpath = GetDefault(nameof(OutputFileAsFullpath)) as string;
- }
- }
- }
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Drawing;
+using System.IO;
+using System.Reflection;
+using System.Windows.Forms;
+using Greenshot.Base.IniFile;
+using Greenshot.Base.Interfaces;
+
+namespace Greenshot.Base.Core
+{
+ public enum ClipboardFormat
+ {
+ PNG,
+ DIB,
+ HTML,
+ HTMLDATAURL,
+ BITMAP,
+ DIBV5
+ }
+
+ public enum OutputFormat
+ {
+ bmp,
+ gif,
+ jpg,
+ png,
+ tiff,
+ greenshot,
+ ico
+ }
+
+ public enum WindowCaptureMode
+ {
+ Screen,
+ GDI,
+ Aero,
+ AeroTransparent,
+ Auto
+ }
+
+ public enum BuildStates
+ {
+ UNSTABLE,
+ RELEASE_CANDIDATE,
+ RELEASE
+ }
+
+ public enum ClickActions
+ {
+ DO_NOTHING,
+ OPEN_LAST_IN_EXPLORER,
+ OPEN_LAST_IN_EDITOR,
+ OPEN_SETTINGS,
+ SHOW_CONTEXT_MENU
+ }
+
+ ///
+ /// Description of CoreConfiguration.
+ ///
+ [IniSection("Core", Description = "Greenshot core configuration")]
+ public class CoreConfiguration : IniSection, INotifyPropertyChanged
+ {
+ public event PropertyChangedEventHandler PropertyChanged;
+
+ [IniProperty("Language", Description = "The language in IETF format (e.g. en-US)")]
+ public string Language { get; set; }
+
+ [IniProperty("RegionHotkey", Description = "Hotkey for starting the region capture", DefaultValue = "PrintScreen")]
+ public string RegionHotkey { get; set; }
+
+ [IniProperty("WindowHotkey", Description = "Hotkey for starting the window capture", DefaultValue = "Alt + PrintScreen")]
+ public string WindowHotkey { get; set; }
+
+ [IniProperty("FullscreenHotkey", Description = "Hotkey for starting the fullscreen capture", DefaultValue = "Ctrl + PrintScreen")]
+ public string FullscreenHotkey { get; set; }
+
+ [IniProperty("LastregionHotkey", Description = "Hotkey for starting the last region capture", DefaultValue = "Shift + PrintScreen")]
+ public string LastregionHotkey { get; set; }
+
+ [IniProperty("IEHotkey", Description = "Hotkey for starting the IE capture", DefaultValue = "Shift + Ctrl + PrintScreen")]
+ public string IEHotkey { get; set; }
+
+ [IniProperty("ClipboardHotkey", Description = "Hotkey for opening the clipboard contents into the editor")]
+ public string ClipboardHotkey { get; set; }
+
+ [IniProperty("IsFirstLaunch", Description = "Is this the first time launch?", DefaultValue = "true")]
+ public bool IsFirstLaunch { get; set; }
+
+ [IniProperty("Destinations", Separator = ",",
+ Description = "Which destinations? Possible options (more might be added by plugins) are: Editor, FileDefault, FileWithDialog, Clipboard, Printer, EMail, Picker",
+ DefaultValue = "Picker")]
+ public List OutputDestinations { get; set; } = new List();
+
+ [IniProperty("ClipboardFormats", Separator = ",", Description = "Specify which formats we copy on the clipboard? Options are: PNG, HTML, HTMLDATAURL and DIB",
+ DefaultValue = "PNG,DIB")]
+ public List ClipboardFormats { get; set; } = new List();
+
+ [IniProperty("CaptureMousepointer", Description = "Should the mouse be captured?", DefaultValue = "true")]
+ public bool CaptureMousepointer { get; set; }
+
+ [IniProperty("CaptureWindowsInteractive", Description = "Use interactive window selection to capture? (false=Capture active window)", DefaultValue = "false")]
+ public bool CaptureWindowsInteractive { get; set; }
+
+ [IniProperty("CaptureDelay", Description = "Capture delay in millseconds.", DefaultValue = "100")]
+ public int CaptureDelay { get; set; }
+
+ [IniProperty("ScreenCaptureMode", Description = "The capture mode used to capture a screen. (Auto, FullScreen, Fixed)", DefaultValue = "Auto")]
+ public ScreenCaptureMode ScreenCaptureMode { get; set; }
+
+ [IniProperty("ScreenToCapture", Description = "The screen number to capture when using ScreenCaptureMode Fixed.", DefaultValue = "1")]
+ public int ScreenToCapture { get; set; }
+
+ [IniProperty("WindowCaptureMode", Description = "The capture mode used to capture a Window (Screen, GDI, Aero, AeroTransparent, Auto).", DefaultValue = "Auto")]
+ public WindowCaptureMode WindowCaptureMode { get; set; }
+
+ [IniProperty("WindowCaptureAllChildLocations",
+ Description = "Enable/disable capture all children, very slow but will make it possible to use this information in the editor.", DefaultValue = "False")]
+ public bool WindowCaptureAllChildLocations { get; set; }
+
+ [IniProperty("DWMBackgroundColor", Description = "The background color for a DWM window capture.")]
+ public Color DWMBackgroundColor { get; set; }
+
+ [IniProperty("PlayCameraSound", LanguageKey = "settings_playsound", Description = "Play a camera sound after taking a capture.", DefaultValue = "false")]
+ public bool PlayCameraSound { get; set; } = false;
+
+ [IniProperty("ShowTrayNotification", LanguageKey = "settings_shownotify", Description = "Show a notification from the systray when a capture is taken.",
+ DefaultValue = "true")]
+ public bool ShowTrayNotification { get; set; } = true;
+
+ [IniProperty("OutputFilePath", Description = "Output file path.")]
+ public string OutputFilePath { get; set; }
+
+ [IniProperty("OutputFileAllowOverwrite",
+ Description = "If the target file already exists True will make Greenshot always overwrite and False will display a 'Save-As' dialog.", DefaultValue = "true")]
+ public bool OutputFileAllowOverwrite { get; set; }
+
+ [IniProperty("OutputFileFilenamePattern", Description = "Filename pattern for screenshot.", DefaultValue = "${capturetime:d\"yyyy-MM-dd HH_mm_ss\"}-${title}")]
+ public string OutputFileFilenamePattern { get; set; }
+
+ [IniProperty("OutputFileFormat", Description = "Default file type for writing screenshots. (bmp, gif, jpg, png, tiff)", DefaultValue = "png")]
+ public OutputFormat OutputFileFormat { get; set; } = OutputFormat.png;
+
+ [IniProperty("OutputFileReduceColors", Description = "If set to true, than the colors of the output file are reduced to 256 (8-bit) colors", DefaultValue = "false")]
+ public bool OutputFileReduceColors { get; set; }
+
+ [IniProperty("OutputFileAutoReduceColors",
+ Description = "If set to true the amount of colors is counted and if smaller than 256 the color reduction is automatically used.", DefaultValue = "false")]
+ public bool OutputFileAutoReduceColors { get; set; }
+
+ [IniProperty("OutputFileReduceColorsTo", Description = "Amount of colors to reduce to, when reducing", DefaultValue = "256")]
+ public int OutputFileReduceColorsTo { get; set; }
+
+ [IniProperty("OutputFileCopyPathToClipboard", Description = "When saving a screenshot, copy the path to the clipboard?", DefaultValue = "true")]
+ public bool OutputFileCopyPathToClipboard { get; set; }
+
+ [IniProperty("OutputFileAsFullpath", Description = "SaveAs Full path?")]
+ public string OutputFileAsFullpath { get; set; }
+
+ [IniProperty("OutputFileJpegQuality", Description = "JPEG file save quality in %.", DefaultValue = "80")]
+ public int OutputFileJpegQuality { get; set; }
+
+ [IniProperty("OutputFilePromptQuality", Description = "Ask for the quality before saving?", DefaultValue = "false")]
+ public bool OutputFilePromptQuality { get; set; }
+
+ [IniProperty("OutputFileIncrementingNumber", Description = "The number for the ${NUM} in the filename pattern, is increased automatically after each save.",
+ DefaultValue = "1")]
+ public uint OutputFileIncrementingNumber { get; set; }
+
+ [IniProperty("OutputPrintPromptOptions", LanguageKey = "settings_alwaysshowprintoptionsdialog", Description = "Ask for print options when printing?",
+ DefaultValue = "true")]
+ public bool OutputPrintPromptOptions { get; set; }
+
+ [IniProperty("OutputPrintAllowRotate", LanguageKey = "printoptions_allowrotate", Description = "Allow rotating the picture for fitting on paper?", DefaultValue = "false")]
+ public bool OutputPrintAllowRotate { get; set; }
+
+ [IniProperty("OutputPrintAllowEnlarge", LanguageKey = "printoptions_allowenlarge", Description = "Allow growing the picture for fitting on paper?", DefaultValue = "false")]
+ public bool OutputPrintAllowEnlarge { get; set; }
+
+ [IniProperty("OutputPrintAllowShrink", LanguageKey = "printoptions_allowshrink", Description = "Allow shrinking the picture for fitting on paper?", DefaultValue = "true")]
+ public bool OutputPrintAllowShrink { get; set; }
+
+ [IniProperty("OutputPrintCenter", LanguageKey = "printoptions_allowcenter", Description = "Center image when printing?", DefaultValue = "true")]
+ public bool OutputPrintCenter { get; set; }
+
+ [IniProperty("OutputPrintInverted", LanguageKey = "printoptions_inverted", Description = "Print image inverted (use e.g. for console captures)", DefaultValue = "false")]
+ public bool OutputPrintInverted { get; set; }
+
+ [IniProperty("OutputPrintGrayscale", LanguageKey = "printoptions_printgrayscale", Description = "Force grayscale printing", DefaultValue = "false")]
+ public bool OutputPrintGrayscale { get; set; }
+
+ [IniProperty("OutputPrintMonochrome", LanguageKey = "printoptions_printmonochrome", Description = "Force monorchrome printing", DefaultValue = "false")]
+ public bool OutputPrintMonochrome { get; set; }
+
+ [IniProperty("OutputPrintMonochromeThreshold", Description = "Threshold for monochrome filter (0 - 255), lower value means less black", DefaultValue = "127")]
+ public byte OutputPrintMonochromeThreshold { get; set; }
+
+ [IniProperty("OutputPrintFooter", LanguageKey = "printoptions_timestamp", Description = "Print footer on print?", DefaultValue = "true")]
+ public bool OutputPrintFooter { get; set; }
+
+ [IniProperty("OutputPrintFooterPattern", Description = "Footer pattern", DefaultValue = "${capturetime:d\"D\"} ${capturetime:d\"T\"} - ${title}")]
+ public string OutputPrintFooterPattern { get; set; }
+
+ [IniProperty("NotificationSound", Description = "The wav-file to play when a capture is taken, loaded only once at the Greenshot startup", DefaultValue = "default")]
+ public string NotificationSound { get; set; }
+
+ [IniProperty("UseProxy", Description = "Use your global proxy?", DefaultValue = "True")]
+ public bool UseProxy { get; set; }
+
+ [IniProperty("IECapture", Description = "Enable/disable IE capture", DefaultValue = "True")]
+ public bool IECapture { get; set; }
+
+ [IniProperty("IEFieldCapture", Description = "Enable/disable IE field capture, very slow but will make it possible to annotate the fields of a capture in the editor.",
+ DefaultValue = "False")]
+ public bool IEFieldCapture { get; set; }
+
+ [IniProperty("WindowClassesToCheckForIE", Description = "Comma separated list of Window-Classes which need to be checked for a IE instance!",
+ DefaultValue = "AfxFrameOrView70,IMWindowClass")]
+ public List WindowClassesToCheckForIE { get; set; }
+
+ [IniProperty("AutoCropDifference",
+ Description =
+ "Sets how to compare the colors for the autocrop detection, the higher the more is 'selected'. Possible values are from 0 to 255, where everything above ~150 doesn't make much sense!",
+ DefaultValue = "10")]
+ public int AutoCropDifference { get; set; }
+
+ [IniProperty("IncludePlugins",
+ Description = "Comma separated list of Plugins which are allowed. If something in the list, than every plugin not in the list will not be loaded!")]
+ public List IncludePlugins { get; set; }
+
+ [IniProperty("ExcludePlugins", Description = "Comma separated list of Plugins which are NOT allowed.")]
+ public List ExcludePlugins { get; set; }
+
+ [IniProperty("ExcludeDestinations", Description = "Comma separated list of destinations which should be disabled.")]
+ public List ExcludeDestinations { get; set; }
+
+ [IniProperty("UpdateCheckInterval", Description = "How many days between every update check? (0=no checks)", DefaultValue = "14")]
+ public int UpdateCheckInterval { get; set; }
+
+ [IniProperty("LastUpdateCheck", Description = "Last update check")]
+ public DateTime LastUpdateCheck { get; set; }
+
+ [IniProperty("DisableSettings", Description = "Enable/disable the access to the settings, can only be changed manually in this .ini", DefaultValue = "False")]
+ public bool DisableSettings { get; set; }
+
+ [IniProperty("DisableQuickSettings", Description = "Enable/disable the access to the quick settings, can only be changed manually in this .ini", DefaultValue = "False")]
+ public bool DisableQuickSettings { get; set; }
+
+ [IniProperty("DisableTrayicon", Description = "Disable the trayicon, can only be changed manually in this .ini", DefaultValue = "False")]
+ public bool HideTrayicon { get; set; }
+
+ [IniProperty("HideExpertSettings", Description = "Hide expert tab in the settings, can only be changed manually in this .ini", DefaultValue = "False")]
+ public bool HideExpertSettings { get; set; }
+
+ [IniProperty("ThumnailPreview", Description = "Enable/disable thumbnail previews", DefaultValue = "True")]
+ public bool ThumnailPreview { get; set; }
+
+ [IniProperty("NoGDICaptureForProduct", Description = "List of productnames for which GDI capturing is skipped (using fallback).", DefaultValue = "IntelliJ IDEA")]
+ public List NoGDICaptureForProduct { get; set; }
+
+ [IniProperty("NoDWMCaptureForProduct", Description = "List of productnames for which DWM capturing is skipped (using fallback).", DefaultValue = "Citrix ICA Client")]
+ public List NoDWMCaptureForProduct { get; set; }
+
+ [IniProperty("OptimizeForRDP", Description = "Make some optimizations for usage with remote desktop", DefaultValue = "False")]
+ public bool OptimizeForRDP { get; set; }
+
+ [IniProperty("DisableRDPOptimizing", Description = "Disable all optimizations for usage with remote desktop", DefaultValue = "False")]
+ public bool DisableRDPOptimizing { get; set; }
+
+ [IniProperty("MinimizeWorkingSetSize", Description = "Optimize memory footprint, but with a performance penalty!", DefaultValue = "False")]
+ public bool MinimizeWorkingSetSize { get; set; }
+
+ [IniProperty("WindowCaptureRemoveCorners", Description = "Remove the corners from a window capture", DefaultValue = "True")]
+ public bool WindowCaptureRemoveCorners { get; set; }
+
+ [IniProperty("CheckForUnstable", Description = "Also check for unstable version updates", DefaultValue = "False")]
+ public bool CheckForUnstable { get; set; }
+
+ [IniProperty("ActiveTitleFixes", Description = "The fixes that are active.")]
+ public List ActiveTitleFixes { get; set; }
+
+ [IniProperty("TitleFixMatcher", Description = "The regular expressions to match the title with.")]
+ public Dictionary TitleFixMatcher { get; set; }
+
+ [IniProperty("TitleFixReplacer", Description = "The replacements for the matchers.")]
+ public Dictionary TitleFixReplacer { get; set; }
+
+ [IniProperty("ExperimentalFeatures", Description = "A list of experimental features, this allows us to test certain features before releasing them.", ExcludeIfNull = true)]
+ public List ExperimentalFeatures { get; set; }
+
+ [IniProperty("EnableSpecialDIBClipboardReader", Description = "Enable a special DIB clipboard reader", DefaultValue = "True")]
+ public bool EnableSpecialDIBClipboardReader { get; set; }
+
+ [IniProperty("WindowCornerCutShape", Description = "The cutshape which is used to remove the window corners, is mirrorred for all corners", DefaultValue = "5,3,2,1,1")]
+ public List WindowCornerCutShape { get; set; }
+
+ [IniProperty("LeftClickAction",
+ Description =
+ "Specify what action is made if the tray icon is left clicked, if a double-click action is specified this action is initiated after a delay (configurable via the windows double-click speed)",
+ DefaultValue = "SHOW_CONTEXT_MENU")]
+ public ClickActions LeftClickAction { get; set; }
+
+ [IniProperty("DoubleClickAction", Description = "Specify what action is made if the tray icon is double clicked", DefaultValue = "OPEN_LAST_IN_EXPLORER")]
+ public ClickActions DoubleClickAction { get; set; }
+
+ [IniProperty("ZoomerEnabled", Description = "Sets if the zoomer is enabled", DefaultValue = "True")]
+ public bool ZoomerEnabled { get; set; }
+
+ [IniProperty("ZoomerOpacity",
+ Description = "Specify the transparency for the zoomer, from 0-1 (where 1 is no transparency and 0 is complete transparent. An usefull setting would be 0.7)",
+ DefaultValue = "1")]
+ public float ZoomerOpacity { get; set; }
+
+ [IniProperty("MaxMenuItemLength",
+ Description = "Maximum length of submenu items in the context menu, making this longer might cause context menu issues on dual screen systems.", DefaultValue = "25")]
+ public int MaxMenuItemLength { get; set; }
+
+ [IniProperty("MailApiTo", Description = "The 'to' field for the email destination (settings for Outlook can be found under the Office section)", DefaultValue = "")]
+ public string MailApiTo { get; set; }
+
+ [IniProperty("MailApiCC", Description = "The 'CC' field for the email destination (settings for Outlook can be found under the Office section)", DefaultValue = "")]
+ public string MailApiCC { get; set; }
+
+ [IniProperty("MailApiBCC", Description = "The 'BCC' field for the email destination (settings for Outlook can be found under the Office section)", DefaultValue = "")]
+ public string MailApiBCC { get; set; }
+
+ [IniProperty("OptimizePNGCommand",
+ Description =
+ "Optional command to execute on a temporary PNG file, the command should overwrite the file and Greenshot will read it back. Note: this command is also executed when uploading PNG's!",
+ DefaultValue = "")]
+ public string OptimizePNGCommand { get; set; }
+
+ [IniProperty("OptimizePNGCommandArguments",
+ Description =
+ "Arguments for the optional command to execute on a PNG, {0} is replaced by the temp-filename from Greenshot. Note: Temp-file is deleted afterwards by Greenshot.",
+ DefaultValue = "\"{0}\"")]
+ public string OptimizePNGCommandArguments { get; set; }
+
+ [IniProperty("LastSaveWithVersion", Description = "Version of Greenshot which created this .ini")]
+ public string LastSaveWithVersion { get; set; }
+
+ [IniProperty("ProcessEXIFOrientation", Description = "When reading images from files or clipboard, use the EXIF information to correct the orientation",
+ DefaultValue = "True")]
+ public bool ProcessEXIFOrientation { get; set; }
+
+ [IniProperty("LastCapturedRegion", Description = "The last used region, for reuse in the capture last region")]
+ public Rectangle LastCapturedRegion { get; set; }
+
+ [IniProperty("Win10BorderCrop", Description = "The capture is cropped with these settings, e.g. when you don't want to color around it -1,-1"), DefaultValue("0,0")]
+ public Size Win10BorderCrop { get; set; }
+
+ private Size _iconSize;
+
+ [IniProperty("BaseIconSize",
+ Description = "Defines the base size of the icons (e.g. for the buttons in the editor), default value 16,16 and it's scaled to the current DPI",
+ DefaultValue = "16,16")]
+ public Size IconSize
+ {
+ get { return _iconSize; }
+ set
+ {
+ Size newSize = value;
+ if (newSize != Size.Empty)
+ {
+ if (newSize.Width < 16)
+ {
+ newSize.Width = 16;
+ }
+ else if (newSize.Width > 256)
+ {
+ newSize.Width = 256;
+ }
+
+ newSize.Width = (newSize.Width / 16) * 16;
+ if (newSize.Height < 16)
+ {
+ newSize.Height = 16;
+ }
+ else if (IconSize.Height > 256)
+ {
+ newSize.Height = 256;
+ }
+
+ newSize.Height = (newSize.Height / 16) * 16;
+ }
+
+ if (_iconSize != newSize)
+ {
+ _iconSize = value;
+ PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("IconSize"));
+ }
+ }
+ }
+
+ public Size ScaledIconSize => DpiHelper.ScaleWithCurrentDpi(_iconSize);
+
+ [IniProperty("WebRequestTimeout", Description = "The connect timeout value for webrequets, these are seconds", DefaultValue = "100")]
+ public int WebRequestTimeout { get; set; }
+
+ [IniProperty("WebRequestReadWriteTimeout", Description = "The read/write timeout value for webrequets, these are seconds", DefaultValue = "100")]
+ public int WebRequestReadWriteTimeout { get; set; }
+
+ public bool UseLargeIcons => IconSize.Width >= 32 || IconSize.Height >= 32;
+
+ ///
+ /// A helper method which returns true if the supplied experimental feature is enabled
+ ///
+ ///
+ ///
+ public bool IsExperimentalFeatureEnabled(string experimentalFeature)
+ {
+ return (ExperimentalFeatures != null && ExperimentalFeatures.Contains(experimentalFeature));
+ }
+
+ ///
+ /// 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)
+ {
+ switch (property)
+ {
+ case "PluginWhitelist":
+ case "PluginBacklist":
+ return new List();
+ case "OutputFileAsFullpath":
+ if (IniConfig.IsPortable)
+ {
+ return Path.Combine(Application.StartupPath, @"..\..\Documents\Pictures\Greenshots\dummy.png");
+ }
+
+ return Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "dummy.png");
+ case "OutputFilePath":
+ if (IniConfig.IsPortable)
+ {
+ string pafOutputFilePath = Path.Combine(Application.StartupPath, @"..\..\Documents\Pictures\Greenshots");
+ if (!Directory.Exists(pafOutputFilePath))
+ {
+ try
+ {
+ Directory.CreateDirectory(pafOutputFilePath);
+ return pafOutputFilePath;
+ }
+ catch (Exception ex)
+ {
+ LOG.Warn(ex);
+ // Problem creating directory, fallback to Desktop
+ }
+ }
+ else
+ {
+ return pafOutputFilePath;
+ }
+ }
+
+ return Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
+ case "DWMBackgroundColor":
+ return Color.Transparent;
+ case "ActiveTitleFixes":
+ return new List
+ {
+ "Firefox",
+ "IE",
+ "Chrome"
+ };
+ case "TitleFixMatcher":
+ return new Dictionary
+ {
+ {
+ "Firefox", " - Mozilla Firefox.*"
+ },
+ {
+ "IE", " - (Microsoft|Windows) Internet Explorer.*"
+ },
+ {
+ "Chrome", " - Google Chrome.*"
+ }
+ };
+ case "TitleFixReplacer":
+ return new Dictionary
+ {
+ {
+ "Firefox", string.Empty
+ },
+ {
+ "IE", string.Empty
+ },
+ {
+ "Chrome", string.Empty
+ }
+ };
+ }
+
+ return null;
+ }
+
+ ///
+ /// This method will be called before converting the property, making to possible to correct a certain value
+ /// Can be used when migration is needed
+ ///
+ /// The name of the property
+ /// The string value of the property
+ /// string with the propertyValue, modified or not...
+ public override string PreCheckValue(string propertyName, string propertyValue)
+ {
+ // Changed the separator, now we need to correct this
+ if ("Destinations".Equals(propertyName))
+ {
+ if (propertyValue != null)
+ {
+ return propertyValue.Replace('|', ',');
+ }
+ }
+
+ if ("OutputFilePath".Equals(propertyName))
+ {
+ if (string.IsNullOrEmpty(propertyValue))
+ {
+ return null;
+ }
+ }
+
+ return base.PreCheckValue(propertyName, propertyValue);
+ }
+
+ ///
+ /// This method will be called before writing the configuration
+ ///
+ public override void BeforeSave()
+ {
+ try
+ {
+ // Store version, this can be used later to fix settings after an update
+ LastSaveWithVersion = Assembly.GetEntryAssembly().GetName().Version.ToString();
+ }
+ catch
+ {
+ // ignored
+ }
+ }
+
+ ///
+ /// This method will be called after reading the configuration, so eventually some corrections can be made
+ ///
+ public override void AfterLoad()
+ {
+ // Comment with releases
+ // CheckForUnstable = true;
+
+ if (string.IsNullOrEmpty(LastSaveWithVersion))
+ {
+ try
+ {
+ // Store version, this can be used later to fix settings after an update
+ LastSaveWithVersion = Assembly.GetEntryAssembly().GetName().Version.ToString();
+ }
+ catch
+ {
+ // ignored
+ }
+
+ // Disable the AutoReduceColors as it causes issues with Mozzila applications and some others
+ OutputFileAutoReduceColors = false;
+ }
+
+ // Fix for excessive feed checking
+ if (UpdateCheckInterval != 0 && UpdateCheckInterval <= 7 && LastSaveWithVersion.StartsWith("1.2"))
+ {
+ UpdateCheckInterval = 14;
+ }
+
+ if (UpdateCheckInterval > 365)
+ {
+ UpdateCheckInterval = 365;
+ }
+
+ // Enable OneNote if upgrading from 1.1
+ if (ExcludeDestinations != null && ExcludeDestinations.Contains("OneNote"))
+ {
+ if (LastSaveWithVersion != null && LastSaveWithVersion.StartsWith("1.1"))
+ {
+ ExcludeDestinations.Remove("OneNote");
+ }
+ else
+ {
+ // TODO: Remove with the release
+ ExcludeDestinations.Remove("OneNote");
+ }
+ }
+
+ if (OutputDestinations == null)
+ {
+ OutputDestinations = new List();
+ }
+
+ // Make sure there is an output!
+ if (OutputDestinations.Count == 0)
+ {
+ OutputDestinations.Add("Editor");
+ }
+
+ // Prevent both settings at once, bug #3435056
+ if (OutputDestinations.Contains("Clipboard") && OutputFileCopyPathToClipboard)
+ {
+ OutputFileCopyPathToClipboard = false;
+ }
+
+ // Make sure we have clipboard formats, otherwise a paste doesn't make sense!
+ if (ClipboardFormats == null || ClipboardFormats.Count == 0)
+ {
+ ClipboardFormats = new List
+ {
+ ClipboardFormat.PNG,
+ ClipboardFormat.HTML,
+ ClipboardFormat.DIB
+ };
+ }
+
+ // Make sure the lists are lowercase, to speedup the check
+ if (NoGDICaptureForProduct != null)
+ {
+ // Fix error in configuration
+ if (NoGDICaptureForProduct.Count >= 2)
+ {
+ if ("intellij".Equals(NoGDICaptureForProduct[0]) && "idea".Equals(NoGDICaptureForProduct[1]))
+ {
+ NoGDICaptureForProduct.RemoveRange(0, 2);
+ NoGDICaptureForProduct.Add("Intellij Idea");
+ IsDirty = true;
+ }
+ }
+
+ for (int i = 0; i < NoGDICaptureForProduct.Count; i++)
+ {
+ NoGDICaptureForProduct[i] = NoGDICaptureForProduct[i].ToLower();
+ }
+ }
+
+ if (NoDWMCaptureForProduct != null)
+ {
+ // Fix error in configuration
+ if (NoDWMCaptureForProduct.Count >= 3)
+ {
+ if ("citrix".Equals(NoDWMCaptureForProduct[0]) && "ica".Equals(NoDWMCaptureForProduct[1]) && "client".Equals(NoDWMCaptureForProduct[2]))
+ {
+ NoDWMCaptureForProduct.RemoveRange(0, 3);
+ NoDWMCaptureForProduct.Add("Citrix ICA Client");
+ IsDirty = true;
+ }
+ }
+
+ for (int i = 0; i < NoDWMCaptureForProduct.Count; i++)
+ {
+ NoDWMCaptureForProduct[i] = NoDWMCaptureForProduct[i].ToLower();
+ }
+ }
+
+ if (AutoCropDifference < 0)
+ {
+ AutoCropDifference = 0;
+ }
+
+ if (AutoCropDifference > 255)
+ {
+ AutoCropDifference = 255;
+ }
+
+ if (OutputFileReduceColorsTo < 2)
+ {
+ OutputFileReduceColorsTo = 2;
+ }
+
+ if (OutputFileReduceColorsTo > 256)
+ {
+ OutputFileReduceColorsTo = 256;
+ }
+
+ if (WebRequestTimeout <= 10)
+ {
+ WebRequestTimeout = 100;
+ }
+
+ if (WebRequestReadWriteTimeout < 1)
+ {
+ WebRequestReadWriteTimeout = 100;
+ }
+ }
+
+ ///
+ /// Validate the OutputFilePath, and if this is not correct it will be set to the default
+ /// Added for BUG-1992, reset the OutputFilePath / OutputFileAsFullpath if they don't exist (e.g. the configuration is used on a different PC)
+ ///
+ public void ValidateAndCorrectOutputFilePath()
+ {
+ if (!Directory.Exists(OutputFilePath))
+ {
+ OutputFilePath = GetDefault(nameof(OutputFilePath)) as string;
+ }
+ }
+
+ ///
+ /// Validate the OutputFileAsFullpath, and if this is not correct it will be set to the default
+ /// Added for BUG-1992, reset the OutputFilePath / OutputFileAsFullpath if they don't exist (e.g. the configuration is used on a different PC)
+ ///
+ public void ValidateAndCorrectOutputFileAsFullpath()
+ {
+ var outputFilePath = Path.GetDirectoryName(OutputFileAsFullpath);
+ if (outputFilePath == null || (!File.Exists(OutputFileAsFullpath) && !Directory.Exists(outputFilePath)))
+ {
+ OutputFileAsFullpath = GetDefault(nameof(OutputFileAsFullpath)) as string;
+ }
+ }
+ }
}
\ No newline at end of file
diff --git a/src/GreenshotPlugin/Core/CredentialsHelper.cs b/src/Greenshot.Base/Core/CredentialsHelper.cs
similarity index 97%
rename from src/GreenshotPlugin/Core/CredentialsHelper.cs
rename to src/Greenshot.Base/Core/CredentialsHelper.cs
index a1140dc46..cc02113a0 100644
--- a/src/GreenshotPlugin/Core/CredentialsHelper.cs
+++ b/src/Greenshot.Base/Core/CredentialsHelper.cs
@@ -1,525 +1,525 @@
-/*
- * Greenshot - a free and open source screenshot tool
- * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
- *
- * For more information see: http://getgreenshot.org/
- * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 1 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-using System;
-using System.Drawing;
-using System.Runtime.InteropServices;
-using System.Text;
-using System.Threading;
-using System.Windows.Forms;
-
-namespace GreenshotPlugin.Core
-{
- ///
- /// The following code comes from: http://www.developerfusion.com/code/4693/using-the-credential-management-api/
- /// and is slightly modified so it works for us.
- /// As the "Stored usernames and passwords" which can be accessed by: Start-> Run and type "Control keymgr.dll"
- /// doesn't show all credentials use the tool here: http://www.microsoft.com/indonesia/msdn/credmgmt.aspx
- /// The following code is an example for a login, it will call the Authenticate with user/password
- /// which should return true if the login worked, false if not.
- /// private static bool Login(string system, string name) {
- /// try {
- /// CredentialsDialog dialog = new CredentialsDialog(system);
- /// dialog.Name = name;
- /// while (dialog.Show(dialog.Name) == DialogResult.OK) {
- /// if (Authenticate(dialog.Name, dialog.Password)) {
- /// if (dialog.SaveChecked) dialog.Confirm(true);
- /// return true;
- /// } else {
- /// try {
- /// dialog.Confirm(false);
- /// } catch (ApplicationException) {
- /// // exception handling ...
- /// }
- /// dialog.IncorrectPassword = true;
- /// }
- /// }
- /// } catch (ApplicationException) {
- /// // exception handling ...
- /// }
- /// return false;
- /// }
- ///
- /// Encapsulates dialog functionality from the Credential Management API.
- public sealed class CredentialsDialog
- {
- [DllImport("gdi32.dll", SetLastError = true)]
- [return: MarshalAs(UnmanagedType.Bool)]
- private static extern bool DeleteObject(IntPtr hObject);
-
- /// The only valid bitmap height (in pixels) of a user-defined banner.
- private const int ValidBannerHeight = 60;
-
- /// The only valid bitmap width (in pixels) of a user-defined banner.
- private const int ValidBannerWidth = 320;
-
- /// Initializes a new instance of the class
- /// with the specified target.
- /// The name of the target for the credentials, typically a server name.
- public CredentialsDialog(string target) : this(target, null)
- {
- }
-
- /// Initializes a new instance of the class
- /// with the specified target and caption.
- /// The name of the target for the credentials, typically a server name.
- /// The caption of the dialog (null will cause a system default title to be used).
- public CredentialsDialog(string target, string caption) : this(target, caption, null)
- {
- }
-
- /// Initializes a new instance of the class
- /// with the specified target, caption and message.
- /// The name of the target for the credentials, typically a server name.
- /// The caption of the dialog (null will cause a system default title to be used).
- /// The message of the dialog (null will cause a system default message to be used).
- public CredentialsDialog(string target, string caption, string message) : this(target, caption, message, null)
- {
- }
-
- /// Initializes a new instance of the class
- /// with the specified target, caption, message and banner.
- /// The name of the target for the credentials, typically a server name.
- /// The caption of the dialog (null will cause a system default title to be used).
- /// The message of the dialog (null will cause a system default message to be used).
- /// The image to display on the dialog (null will cause a system default image to be used).
- public CredentialsDialog(string target, string caption, string message, Image banner)
- {
- Target = target;
- Caption = caption;
- Message = message;
- Banner = banner;
- }
-
- ///
- /// Gets or sets if the dialog will be shown even if the credentials
- /// can be returned from an existing credential in the credential manager.
- ///
- public bool AlwaysDisplay { get; set; }
-
- /// Gets or sets if the dialog is populated with name/password only.
- public bool ExcludeCertificates { get; set; } = true;
-
- /// Gets or sets if the credentials are to be persisted in the credential manager.
- public bool Persist { get; set; } = true;
-
- /// Gets or sets if the incorrect password balloontip needs to be shown. Introduced AFTER Windows XP
- public bool IncorrectPassword { get; set; }
-
- /// Gets or sets if the name is read-only.
- public bool KeepName { get; set; }
-
- private string _name = string.Empty;
-
- /// Gets or sets the name for the credentials.
- public string Name
- {
- get { return _name; }
- set
- {
- if (value?.Length > CredUi.MAX_USERNAME_LENGTH)
- {
- string message = string.Format(
- Thread.CurrentThread.CurrentUICulture,
- "The name has a maximum length of {0} characters.",
- CredUi.MAX_USERNAME_LENGTH);
- throw new ArgumentException(message, nameof(Name));
- }
-
- _name = value;
- }
- }
-
- private string _password = string.Empty;
-
- /// Gets or sets the password for the credentials.
- public string Password
- {
- get { return _password; }
- set
- {
- if (value?.Length > CredUi.MAX_PASSWORD_LENGTH)
- {
- string message = string.Format(
- Thread.CurrentThread.CurrentUICulture,
- "The password has a maximum length of {0} characters.",
- CredUi.MAX_PASSWORD_LENGTH);
- throw new ArgumentException(message, nameof(Password));
- }
-
- _password = value;
- }
- }
-
- /// Gets or sets if the save checkbox status.
- public bool SaveChecked { get; set; }
-
- /// Gets or sets if the save checkbox is displayed.
- /// This value only has effect if Persist is true.
- public bool SaveDisplayed { get; set; } = true;
-
- private string _target = string.Empty;
-
- /// Gets or sets the name of the target for the credentials, typically a server name.
- public string Target
- {
- get { return _target; }
- set
- {
- if (value == null)
- {
- throw new ArgumentException("The target cannot be a null value.", nameof(Target));
- }
-
- if (value.Length > CredUi.MAX_GENERIC_TARGET_LENGTH)
- {
- string message = string.Format(
- Thread.CurrentThread.CurrentUICulture,
- "The target has a maximum length of {0} characters.",
- CredUi.MAX_GENERIC_TARGET_LENGTH);
- throw new ArgumentException(message, nameof(Target));
- }
-
- _target = value;
- }
- }
-
- private string _caption = string.Empty;
-
- /// Gets or sets the caption of the dialog.
- /// A null value will cause a system default caption to be used.
- public string Caption
- {
- get { return _caption; }
- set
- {
- if (value?.Length > CredUi.MAX_CAPTION_LENGTH)
- {
- string message = string.Format(
- Thread.CurrentThread.CurrentUICulture,
- "The caption has a maximum length of {0} characters.",
- CredUi.MAX_CAPTION_LENGTH);
- throw new ArgumentException(message, nameof(Caption));
- }
-
- _caption = value;
- }
- }
-
- private string _message = string.Empty;
-
- /// Gets or sets the message of the dialog.
- /// A null value will cause a system default message to be used.
- public string Message
- {
- get { return _message; }
- set
- {
- if (value?.Length > CredUi.MAX_MESSAGE_LENGTH)
- {
- string message = string.Format(
- Thread.CurrentThread.CurrentUICulture,
- "The message has a maximum length of {0} characters.",
- CredUi.MAX_MESSAGE_LENGTH);
- throw new ArgumentException(message, nameof(Message));
- }
-
- _message = value;
- }
- }
-
- private Image _banner;
-
- /// Gets or sets the image to display on the dialog.
- /// A null value will cause a system default image to be used.
- public Image Banner
- {
- get { return _banner; }
- set
- {
- if (value != null)
- {
- if (value.Width != ValidBannerWidth)
- {
- throw new ArgumentException("The banner image width must be 320 pixels.", nameof(Banner));
- }
-
- if (value.Height != ValidBannerHeight)
- {
- throw new ArgumentException("The banner image height must be 60 pixels.", nameof(Banner));
- }
- }
-
- _banner = value;
- }
- }
-
- /// Shows the credentials dialog with the specified name.
- /// The name for the credentials.
- /// Returns a DialogResult indicating the user action.
- public DialogResult Show(string name)
- {
- return Show(null, name, Password, SaveChecked);
- }
-
- /// Shows the credentials dialog with the specified owner, name, password and save checkbox status.
- /// The System.Windows.Forms.IWin32Window the dialog will display in front of.
- /// The name for the credentials.
- /// The password for the credentials.
- /// True if the save checkbox is checked.
- /// Returns a DialogResult indicating the user action.
- public DialogResult Show(IWin32Window owner, string name, string password, bool saveChecked)
- {
- if ((Environment.OSVersion.Version.Major < 5) || ((Environment.OSVersion.Version.Major == 5) && (Environment.OSVersion.Version.Minor < 1)))
- {
- throw new ApplicationException("The Credential Management API requires Windows XP / Windows Server 2003 or later.");
- }
-
- Name = name;
- Password = password;
- SaveChecked = saveChecked;
-
- return ShowDialog(owner);
- }
-
- /// Confirmation action to be applied.
- /// True if the credentials should be persisted.
- public void Confirm(bool value)
- {
- var confirmResult = CredUi.CredUIConfirmCredentials(Target, value);
- switch (confirmResult)
- {
- case CredUi.ReturnCodes.NO_ERROR:
- break;
- case CredUi.ReturnCodes.ERROR_INVALID_PARAMETER:
- // for some reason, this is encountered when credentials are overwritten
- break;
- default:
- throw new ApplicationException($"Credential confirmation failed: {confirmResult}");
- }
- }
-
- /// Returns a DialogResult indicating the user action.
- /// The System.Windows.Forms.IWin32Window the dialog will display in front of.
- ///
- /// Sets the name, password and SaveChecked accessors to the state of the dialog as it was dismissed by the user.
- ///
- private DialogResult ShowDialog(IWin32Window owner)
- {
- // set the api call parameters
- StringBuilder name = new StringBuilder(CredUi.MAX_USERNAME_LENGTH);
- name.Append(Name);
-
- StringBuilder password = new StringBuilder(CredUi.MAX_PASSWORD_LENGTH);
- password.Append(Password);
-
- int saveChecked = Convert.ToInt32(SaveChecked);
-
- CredUi.INFO info = GetInfo(owner);
- CredUi.CredFlags credFlags = GetFlags();
-
- // make the api call
- CredUi.ReturnCodes code = CredUi.CredUIPromptForCredentials(
- ref info,
- Target,
- IntPtr.Zero, 0,
- name, CredUi.MAX_USERNAME_LENGTH,
- password, CredUi.MAX_PASSWORD_LENGTH,
- ref saveChecked,
- credFlags
- );
-
- // clean up resources
- if (Banner != null)
- {
- DeleteObject(info.hbmBanner);
- }
-
- // set the accessors from the api call parameters
- Name = name.ToString();
- Password = password.ToString();
- SaveChecked = Convert.ToBoolean(saveChecked);
-
- return GetDialogResult(code);
- }
-
- /// Returns the info structure for dialog display settings.
- /// The System.Windows.Forms.IWin32Window the dialog will display in front of.
- private CredUi.INFO GetInfo(IWin32Window owner)
- {
- CredUi.INFO info = new CredUi.INFO();
- if (owner != null) info.hWndParent = owner.Handle;
- info.pszCaptionText = Caption;
- info.pszMessageText = Message;
- if (Banner != null)
- {
- info.hbmBanner = new Bitmap(Banner, ValidBannerWidth, ValidBannerHeight).GetHbitmap();
- }
-
- info.cbSize = Marshal.SizeOf(info);
- return info;
- }
-
- /// Returns the flags for dialog display options.
- private CredUi.CredFlags GetFlags()
- {
- CredUi.CredFlags credFlags = CredUi.CredFlags.GENERIC_CREDENTIALS;
-
- if (IncorrectPassword)
- {
- credFlags |= CredUi.CredFlags.INCORRECT_PASSWORD;
- }
-
- if (AlwaysDisplay)
- {
- credFlags |= CredUi.CredFlags.ALWAYS_SHOW_UI;
- }
-
- if (ExcludeCertificates)
- {
- credFlags |= CredUi.CredFlags.EXCLUDE_CERTIFICATES;
- }
-
- if (Persist)
- {
- credFlags |= CredUi.CredFlags.EXPECT_CONFIRMATION;
- if (SaveDisplayed)
- {
- credFlags |= CredUi.CredFlags.SHOW_SAVE_CHECK_BOX;
- }
- else
- {
- credFlags |= CredUi.CredFlags.PERSIST;
- }
- }
- else
- {
- credFlags |= CredUi.CredFlags.DO_NOT_PERSIST;
- }
-
- if (KeepName)
- {
- credFlags |= CredUi.CredFlags.KEEP_USERNAME;
- }
-
- return credFlags;
- }
-
- /// Returns a DialogResult from the specified code.
- /// The credential return code.
- private DialogResult GetDialogResult(CredUi.ReturnCodes code) =>
- code switch
- {
- CredUi.ReturnCodes.NO_ERROR => DialogResult.OK,
- CredUi.ReturnCodes.ERROR_CANCELLED => DialogResult.Cancel,
- CredUi.ReturnCodes.ERROR_NO_SUCH_LOGON_SESSION => throw new ApplicationException(
- "No such logon session."),
- CredUi.ReturnCodes.ERROR_NOT_FOUND => throw new ApplicationException("Not found."),
- CredUi.ReturnCodes.ERROR_INVALID_ACCOUNT_NAME =>
- throw new ApplicationException("Invalid account name."),
- CredUi.ReturnCodes.ERROR_INSUFFICIENT_BUFFER => throw new ApplicationException("Insufficient buffer."),
- CredUi.ReturnCodes.ERROR_INVALID_PARAMETER => throw new ApplicationException("Invalid parameter."),
- CredUi.ReturnCodes.ERROR_INVALID_FLAGS => throw new ApplicationException("Invalid flags."),
- _ => throw new ApplicationException("Unknown credential result encountered.")
- };
- }
-
- internal static class CredUi
- {
- /// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/secauthn/security/authentication_constants.asp
- public const int MAX_MESSAGE_LENGTH = 100;
-
- public const int MAX_CAPTION_LENGTH = 100;
- public const int MAX_GENERIC_TARGET_LENGTH = 100;
- public const int MAX_USERNAME_LENGTH = 100;
- public const int MAX_PASSWORD_LENGTH = 100;
-
- ///
- /// http://www.pinvoke.net/default.aspx/Enums.CREDUI_FLAGS
- /// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnnetsec/html/dpapiusercredentials.asp
- /// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/secauthn/security/creduipromptforcredentials.asp
- ///
- [Flags]
- public enum CredFlags
- {
- INCORRECT_PASSWORD = 0x1,
- DO_NOT_PERSIST = 0x2,
- EXCLUDE_CERTIFICATES = 0x8,
- SHOW_SAVE_CHECK_BOX = 0x40,
- ALWAYS_SHOW_UI = 0x80,
- PERSIST = 0x1000,
- EXPECT_CONFIRMATION = 0x20000,
- GENERIC_CREDENTIALS = 0x40000,
- KEEP_USERNAME = 0x100000,
- }
-
- /// http://www.pinvoke.net/default.aspx/Enums.CredUIReturnCodes
- public enum ReturnCodes
- {
- NO_ERROR = 0,
- ERROR_INVALID_PARAMETER = 87,
- ERROR_INSUFFICIENT_BUFFER = 122,
- ERROR_INVALID_FLAGS = 1004,
- ERROR_NOT_FOUND = 1168,
- ERROR_CANCELLED = 1223,
- ERROR_NO_SUCH_LOGON_SESSION = 1312,
- ERROR_INVALID_ACCOUNT_NAME = 1315
- }
-
- ///
- /// http://www.pinvoke.net/default.aspx/Structures.CREDUI_INFO
- /// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/secauthn/security/credui_info.asp
- ///
- public struct INFO
- {
- public int cbSize;
- public IntPtr hWndParent;
- [MarshalAs(UnmanagedType.LPWStr)] public string pszMessageText;
- [MarshalAs(UnmanagedType.LPWStr)] public string pszCaptionText;
- public IntPtr hbmBanner;
- }
-
- ///
- /// http://www.pinvoke.net/default.aspx/credui.CredUIPromptForCredentialsW
- /// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/secauthn/security/creduipromptforcredentials.asp
- ///
- [DllImport("credui", CharSet = CharSet.Unicode)]
- public static extern ReturnCodes CredUIPromptForCredentials(
- ref INFO creditUR,
- string targetName,
- IntPtr reserved1,
- int iError,
- StringBuilder userName,
- int maxUserName,
- StringBuilder password,
- int maxPassword,
- ref int iSave,
- CredFlags credFlags
- );
-
- ///
- /// http://www.pinvoke.net/default.aspx/credui.CredUIConfirmCredentials
- /// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/secauthn/security/creduiconfirmcredentials.asp
- ///
- [DllImport("credui.dll", CharSet = CharSet.Unicode)]
- public static extern ReturnCodes CredUIConfirmCredentials(string targetName, [MarshalAs(UnmanagedType.Bool)] bool confirm);
- }
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+using System;
+using System.Drawing;
+using System.Runtime.InteropServices;
+using System.Text;
+using System.Threading;
+using System.Windows.Forms;
+
+namespace Greenshot.Base.Core
+{
+ ///
+ /// The following code comes from: http://www.developerfusion.com/code/4693/using-the-credential-management-api/
+ /// and is slightly modified so it works for us.
+ /// As the "Stored usernames and passwords" which can be accessed by: Start-> Run and type "Control keymgr.dll"
+ /// doesn't show all credentials use the tool here: http://www.microsoft.com/indonesia/msdn/credmgmt.aspx
+ /// The following code is an example for a login, it will call the Authenticate with user/password
+ /// which should return true if the login worked, false if not.
+ /// private static bool Login(string system, string name) {
+ /// try {
+ /// CredentialsDialog dialog = new CredentialsDialog(system);
+ /// dialog.Name = name;
+ /// while (dialog.Show(dialog.Name) == DialogResult.OK) {
+ /// if (Authenticate(dialog.Name, dialog.Password)) {
+ /// if (dialog.SaveChecked) dialog.Confirm(true);
+ /// return true;
+ /// } else {
+ /// try {
+ /// dialog.Confirm(false);
+ /// } catch (ApplicationException) {
+ /// // exception handling ...
+ /// }
+ /// dialog.IncorrectPassword = true;
+ /// }
+ /// }
+ /// } catch (ApplicationException) {
+ /// // exception handling ...
+ /// }
+ /// return false;
+ /// }
+ ///
+ /// Encapsulates dialog functionality from the Credential Management API.
+ public sealed class CredentialsDialog
+ {
+ [DllImport("gdi32.dll", SetLastError = true)]
+ [return: MarshalAs(UnmanagedType.Bool)]
+ private static extern bool DeleteObject(IntPtr hObject);
+
+ /// The only valid bitmap height (in pixels) of a user-defined banner.
+ private const int ValidBannerHeight = 60;
+
+ /// The only valid bitmap width (in pixels) of a user-defined banner.
+ private const int ValidBannerWidth = 320;
+
+ /// Initializes a new instance of the class
+ /// with the specified target.
+ /// The name of the target for the credentials, typically a server name.
+ public CredentialsDialog(string target) : this(target, null)
+ {
+ }
+
+ /// Initializes a new instance of the class
+ /// with the specified target and caption.
+ /// The name of the target for the credentials, typically a server name.
+ /// The caption of the dialog (null will cause a system default title to be used).
+ public CredentialsDialog(string target, string caption) : this(target, caption, null)
+ {
+ }
+
+ /// Initializes a new instance of the class
+ /// with the specified target, caption and message.
+ /// The name of the target for the credentials, typically a server name.
+ /// The caption of the dialog (null will cause a system default title to be used).
+ /// The message of the dialog (null will cause a system default message to be used).
+ public CredentialsDialog(string target, string caption, string message) : this(target, caption, message, null)
+ {
+ }
+
+ /// Initializes a new instance of the class
+ /// with the specified target, caption, message and banner.
+ /// The name of the target for the credentials, typically a server name.
+ /// The caption of the dialog (null will cause a system default title to be used).
+ /// The message of the dialog (null will cause a system default message to be used).
+ /// The image to display on the dialog (null will cause a system default image to be used).
+ public CredentialsDialog(string target, string caption, string message, Image banner)
+ {
+ Target = target;
+ Caption = caption;
+ Message = message;
+ Banner = banner;
+ }
+
+ ///
+ /// Gets or sets if the dialog will be shown even if the credentials
+ /// can be returned from an existing credential in the credential manager.
+ ///
+ public bool AlwaysDisplay { get; set; }
+
+ /// Gets or sets if the dialog is populated with name/password only.
+ public bool ExcludeCertificates { get; set; } = true;
+
+ /// Gets or sets if the credentials are to be persisted in the credential manager.
+ public bool Persist { get; set; } = true;
+
+ /// Gets or sets if the incorrect password balloontip needs to be shown. Introduced AFTER Windows XP
+ public bool IncorrectPassword { get; set; }
+
+ /// Gets or sets if the name is read-only.
+ public bool KeepName { get; set; }
+
+ private string _name = string.Empty;
+
+ /// Gets or sets the name for the credentials.
+ public string Name
+ {
+ get { return _name; }
+ set
+ {
+ if (value?.Length > CredUi.MAX_USERNAME_LENGTH)
+ {
+ string message = string.Format(
+ Thread.CurrentThread.CurrentUICulture,
+ "The name has a maximum length of {0} characters.",
+ CredUi.MAX_USERNAME_LENGTH);
+ throw new ArgumentException(message, nameof(Name));
+ }
+
+ _name = value;
+ }
+ }
+
+ private string _password = string.Empty;
+
+ /// Gets or sets the password for the credentials.
+ public string Password
+ {
+ get { return _password; }
+ set
+ {
+ if (value?.Length > CredUi.MAX_PASSWORD_LENGTH)
+ {
+ string message = string.Format(
+ Thread.CurrentThread.CurrentUICulture,
+ "The password has a maximum length of {0} characters.",
+ CredUi.MAX_PASSWORD_LENGTH);
+ throw new ArgumentException(message, nameof(Password));
+ }
+
+ _password = value;
+ }
+ }
+
+ /// Gets or sets if the save checkbox status.
+ public bool SaveChecked { get; set; }
+
+ /// Gets or sets if the save checkbox is displayed.
+ /// This value only has effect if Persist is true.
+ public bool SaveDisplayed { get; set; } = true;
+
+ private string _target = string.Empty;
+
+ /// Gets or sets the name of the target for the credentials, typically a server name.
+ public string Target
+ {
+ get { return _target; }
+ set
+ {
+ if (value == null)
+ {
+ throw new ArgumentException("The target cannot be a null value.", nameof(Target));
+ }
+
+ if (value.Length > CredUi.MAX_GENERIC_TARGET_LENGTH)
+ {
+ string message = string.Format(
+ Thread.CurrentThread.CurrentUICulture,
+ "The target has a maximum length of {0} characters.",
+ CredUi.MAX_GENERIC_TARGET_LENGTH);
+ throw new ArgumentException(message, nameof(Target));
+ }
+
+ _target = value;
+ }
+ }
+
+ private string _caption = string.Empty;
+
+ /// Gets or sets the caption of the dialog.
+ /// A null value will cause a system default caption to be used.
+ public string Caption
+ {
+ get { return _caption; }
+ set
+ {
+ if (value?.Length > CredUi.MAX_CAPTION_LENGTH)
+ {
+ string message = string.Format(
+ Thread.CurrentThread.CurrentUICulture,
+ "The caption has a maximum length of {0} characters.",
+ CredUi.MAX_CAPTION_LENGTH);
+ throw new ArgumentException(message, nameof(Caption));
+ }
+
+ _caption = value;
+ }
+ }
+
+ private string _message = string.Empty;
+
+ /// Gets or sets the message of the dialog.
+ /// A null value will cause a system default message to be used.
+ public string Message
+ {
+ get { return _message; }
+ set
+ {
+ if (value?.Length > CredUi.MAX_MESSAGE_LENGTH)
+ {
+ string message = string.Format(
+ Thread.CurrentThread.CurrentUICulture,
+ "The message has a maximum length of {0} characters.",
+ CredUi.MAX_MESSAGE_LENGTH);
+ throw new ArgumentException(message, nameof(Message));
+ }
+
+ _message = value;
+ }
+ }
+
+ private Image _banner;
+
+ /// Gets or sets the image to display on the dialog.
+ /// A null value will cause a system default image to be used.
+ public Image Banner
+ {
+ get { return _banner; }
+ set
+ {
+ if (value != null)
+ {
+ if (value.Width != ValidBannerWidth)
+ {
+ throw new ArgumentException("The banner image width must be 320 pixels.", nameof(Banner));
+ }
+
+ if (value.Height != ValidBannerHeight)
+ {
+ throw new ArgumentException("The banner image height must be 60 pixels.", nameof(Banner));
+ }
+ }
+
+ _banner = value;
+ }
+ }
+
+ /// Shows the credentials dialog with the specified name.
+ /// The name for the credentials.
+ /// Returns a DialogResult indicating the user action.
+ public DialogResult Show(string name)
+ {
+ return Show(null, name, Password, SaveChecked);
+ }
+
+ /// Shows the credentials dialog with the specified owner, name, password and save checkbox status.
+ /// The System.Windows.Forms.IWin32Window the dialog will display in front of.
+ /// The name for the credentials.
+ /// The password for the credentials.
+ /// True if the save checkbox is checked.
+ /// Returns a DialogResult indicating the user action.
+ public DialogResult Show(IWin32Window owner, string name, string password, bool saveChecked)
+ {
+ if ((Environment.OSVersion.Version.Major < 5) || ((Environment.OSVersion.Version.Major == 5) && (Environment.OSVersion.Version.Minor < 1)))
+ {
+ throw new ApplicationException("The Credential Management API requires Windows XP / Windows Server 2003 or later.");
+ }
+
+ Name = name;
+ Password = password;
+ SaveChecked = saveChecked;
+
+ return ShowDialog(owner);
+ }
+
+ /// Confirmation action to be applied.
+ /// True if the credentials should be persisted.
+ public void Confirm(bool value)
+ {
+ var confirmResult = CredUi.CredUIConfirmCredentials(Target, value);
+ switch (confirmResult)
+ {
+ case CredUi.ReturnCodes.NO_ERROR:
+ break;
+ case CredUi.ReturnCodes.ERROR_INVALID_PARAMETER:
+ // for some reason, this is encountered when credentials are overwritten
+ break;
+ default:
+ throw new ApplicationException($"Credential confirmation failed: {confirmResult}");
+ }
+ }
+
+ /// Returns a DialogResult indicating the user action.
+ /// The System.Windows.Forms.IWin32Window the dialog will display in front of.
+ ///
+ /// Sets the name, password and SaveChecked accessors to the state of the dialog as it was dismissed by the user.
+ ///
+ private DialogResult ShowDialog(IWin32Window owner)
+ {
+ // set the api call parameters
+ StringBuilder name = new StringBuilder(CredUi.MAX_USERNAME_LENGTH);
+ name.Append(Name);
+
+ StringBuilder password = new StringBuilder(CredUi.MAX_PASSWORD_LENGTH);
+ password.Append(Password);
+
+ int saveChecked = Convert.ToInt32(SaveChecked);
+
+ CredUi.INFO info = GetInfo(owner);
+ CredUi.CredFlags credFlags = GetFlags();
+
+ // make the api call
+ CredUi.ReturnCodes code = CredUi.CredUIPromptForCredentials(
+ ref info,
+ Target,
+ IntPtr.Zero, 0,
+ name, CredUi.MAX_USERNAME_LENGTH,
+ password, CredUi.MAX_PASSWORD_LENGTH,
+ ref saveChecked,
+ credFlags
+ );
+
+ // clean up resources
+ if (Banner != null)
+ {
+ DeleteObject(info.hbmBanner);
+ }
+
+ // set the accessors from the api call parameters
+ Name = name.ToString();
+ Password = password.ToString();
+ SaveChecked = Convert.ToBoolean(saveChecked);
+
+ return GetDialogResult(code);
+ }
+
+ /// Returns the info structure for dialog display settings.
+ /// The System.Windows.Forms.IWin32Window the dialog will display in front of.
+ private CredUi.INFO GetInfo(IWin32Window owner)
+ {
+ CredUi.INFO info = new CredUi.INFO();
+ if (owner != null) info.hWndParent = owner.Handle;
+ info.pszCaptionText = Caption;
+ info.pszMessageText = Message;
+ if (Banner != null)
+ {
+ info.hbmBanner = new Bitmap(Banner, ValidBannerWidth, ValidBannerHeight).GetHbitmap();
+ }
+
+ info.cbSize = Marshal.SizeOf(info);
+ return info;
+ }
+
+ /// Returns the flags for dialog display options.
+ private CredUi.CredFlags GetFlags()
+ {
+ CredUi.CredFlags credFlags = CredUi.CredFlags.GENERIC_CREDENTIALS;
+
+ if (IncorrectPassword)
+ {
+ credFlags |= CredUi.CredFlags.INCORRECT_PASSWORD;
+ }
+
+ if (AlwaysDisplay)
+ {
+ credFlags |= CredUi.CredFlags.ALWAYS_SHOW_UI;
+ }
+
+ if (ExcludeCertificates)
+ {
+ credFlags |= CredUi.CredFlags.EXCLUDE_CERTIFICATES;
+ }
+
+ if (Persist)
+ {
+ credFlags |= CredUi.CredFlags.EXPECT_CONFIRMATION;
+ if (SaveDisplayed)
+ {
+ credFlags |= CredUi.CredFlags.SHOW_SAVE_CHECK_BOX;
+ }
+ else
+ {
+ credFlags |= CredUi.CredFlags.PERSIST;
+ }
+ }
+ else
+ {
+ credFlags |= CredUi.CredFlags.DO_NOT_PERSIST;
+ }
+
+ if (KeepName)
+ {
+ credFlags |= CredUi.CredFlags.KEEP_USERNAME;
+ }
+
+ return credFlags;
+ }
+
+ /// Returns a DialogResult from the specified code.
+ /// The credential return code.
+ private DialogResult GetDialogResult(CredUi.ReturnCodes code) =>
+ code switch
+ {
+ CredUi.ReturnCodes.NO_ERROR => DialogResult.OK,
+ CredUi.ReturnCodes.ERROR_CANCELLED => DialogResult.Cancel,
+ CredUi.ReturnCodes.ERROR_NO_SUCH_LOGON_SESSION => throw new ApplicationException(
+ "No such logon session."),
+ CredUi.ReturnCodes.ERROR_NOT_FOUND => throw new ApplicationException("Not found."),
+ CredUi.ReturnCodes.ERROR_INVALID_ACCOUNT_NAME =>
+ throw new ApplicationException("Invalid account name."),
+ CredUi.ReturnCodes.ERROR_INSUFFICIENT_BUFFER => throw new ApplicationException("Insufficient buffer."),
+ CredUi.ReturnCodes.ERROR_INVALID_PARAMETER => throw new ApplicationException("Invalid parameter."),
+ CredUi.ReturnCodes.ERROR_INVALID_FLAGS => throw new ApplicationException("Invalid flags."),
+ _ => throw new ApplicationException("Unknown credential result encountered.")
+ };
+ }
+
+ internal static class CredUi
+ {
+ /// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/secauthn/security/authentication_constants.asp
+ public const int MAX_MESSAGE_LENGTH = 100;
+
+ public const int MAX_CAPTION_LENGTH = 100;
+ public const int MAX_GENERIC_TARGET_LENGTH = 100;
+ public const int MAX_USERNAME_LENGTH = 100;
+ public const int MAX_PASSWORD_LENGTH = 100;
+
+ ///
+ /// http://www.pinvoke.net/default.aspx/Enums.CREDUI_FLAGS
+ /// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnnetsec/html/dpapiusercredentials.asp
+ /// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/secauthn/security/creduipromptforcredentials.asp
+ ///
+ [Flags]
+ public enum CredFlags
+ {
+ INCORRECT_PASSWORD = 0x1,
+ DO_NOT_PERSIST = 0x2,
+ EXCLUDE_CERTIFICATES = 0x8,
+ SHOW_SAVE_CHECK_BOX = 0x40,
+ ALWAYS_SHOW_UI = 0x80,
+ PERSIST = 0x1000,
+ EXPECT_CONFIRMATION = 0x20000,
+ GENERIC_CREDENTIALS = 0x40000,
+ KEEP_USERNAME = 0x100000,
+ }
+
+ /// http://www.pinvoke.net/default.aspx/Enums.CredUIReturnCodes
+ public enum ReturnCodes
+ {
+ NO_ERROR = 0,
+ ERROR_INVALID_PARAMETER = 87,
+ ERROR_INSUFFICIENT_BUFFER = 122,
+ ERROR_INVALID_FLAGS = 1004,
+ ERROR_NOT_FOUND = 1168,
+ ERROR_CANCELLED = 1223,
+ ERROR_NO_SUCH_LOGON_SESSION = 1312,
+ ERROR_INVALID_ACCOUNT_NAME = 1315
+ }
+
+ ///
+ /// http://www.pinvoke.net/default.aspx/Structures.CREDUI_INFO
+ /// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/secauthn/security/credui_info.asp
+ ///
+ public struct INFO
+ {
+ public int cbSize;
+ public IntPtr hWndParent;
+ [MarshalAs(UnmanagedType.LPWStr)] public string pszMessageText;
+ [MarshalAs(UnmanagedType.LPWStr)] public string pszCaptionText;
+ public IntPtr hbmBanner;
+ }
+
+ ///
+ /// http://www.pinvoke.net/default.aspx/credui.CredUIPromptForCredentialsW
+ /// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/secauthn/security/creduipromptforcredentials.asp
+ ///
+ [DllImport("credui", CharSet = CharSet.Unicode)]
+ public static extern ReturnCodes CredUIPromptForCredentials(
+ ref INFO creditUR,
+ string targetName,
+ IntPtr reserved1,
+ int iError,
+ StringBuilder userName,
+ int maxUserName,
+ StringBuilder password,
+ int maxPassword,
+ ref int iSave,
+ CredFlags credFlags
+ );
+
+ ///
+ /// http://www.pinvoke.net/default.aspx/credui.CredUIConfirmCredentials
+ /// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/secauthn/security/creduiconfirmcredentials.asp
+ ///
+ [DllImport("credui.dll", CharSet = CharSet.Unicode)]
+ public static extern ReturnCodes CredUIConfirmCredentials(string targetName, [MarshalAs(UnmanagedType.Bool)] bool confirm);
+ }
}
\ No newline at end of file
diff --git a/src/GreenshotPlugin/Core/DisplayKeyAttribute.cs b/src/Greenshot.Base/Core/DisplayKeyAttribute.cs
similarity index 94%
rename from src/GreenshotPlugin/Core/DisplayKeyAttribute.cs
rename to src/Greenshot.Base/Core/DisplayKeyAttribute.cs
index 1719e0092..39ac43b13 100644
--- a/src/GreenshotPlugin/Core/DisplayKeyAttribute.cs
+++ b/src/Greenshot.Base/Core/DisplayKeyAttribute.cs
@@ -1,40 +1,40 @@
-/*
- * Greenshot - a free and open source screenshot tool
- * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
- *
- * For more information see: http://getgreenshot.org/
- * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 1 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-using System;
-
-namespace GreenshotPlugin.Core
-{
- [AttributeUsage(AttributeTargets.Field)]
- public sealed class DisplayKeyAttribute : Attribute
- {
- public string Value { get; }
-
- public DisplayKeyAttribute(string v)
- {
- Value = v;
- }
-
- public DisplayKeyAttribute()
- {
- }
- }
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+using System;
+
+namespace Greenshot.Base.Core
+{
+ [AttributeUsage(AttributeTargets.Field)]
+ public sealed class DisplayKeyAttribute : Attribute
+ {
+ public string Value { get; }
+
+ public DisplayKeyAttribute(string v)
+ {
+ Value = v;
+ }
+
+ public DisplayKeyAttribute()
+ {
+ }
+ }
}
\ No newline at end of file
diff --git a/src/GreenshotPlugin/Core/DpiHelper.cs b/src/Greenshot.Base/Core/DpiHelper.cs
similarity index 97%
rename from src/GreenshotPlugin/Core/DpiHelper.cs
rename to src/Greenshot.Base/Core/DpiHelper.cs
index ec9ae8f41..bcb3314b6 100644
--- a/src/GreenshotPlugin/Core/DpiHelper.cs
+++ b/src/Greenshot.Base/Core/DpiHelper.cs
@@ -19,15 +19,15 @@
* along with this program. If not, see .
*/
-using GreenshotPlugin.Core.Enums;
-using GreenshotPlugin.UnmanagedHelpers;
using System;
using System.Drawing;
using System.Runtime.InteropServices;
-using GreenshotPlugin.UnmanagedHelpers.Enums;
-using GreenshotPlugin.UnmanagedHelpers.Structs;
+using Greenshot.Base.Core.Enums;
+using Greenshot.Base.UnmanagedHelpers;
+using Greenshot.Base.UnmanagedHelpers.Enums;
+using Greenshot.Base.UnmanagedHelpers.Structs;
-namespace GreenshotPlugin.Core
+namespace Greenshot.Base.Core
{
///
/// This handles DPI changes see
diff --git a/src/GreenshotPlugin/Core/EffectConverter.cs b/src/Greenshot.Base/Core/EffectConverter.cs
similarity index 96%
rename from src/GreenshotPlugin/Core/EffectConverter.cs
rename to src/Greenshot.Base/Core/EffectConverter.cs
index 1b69dc3ed..40cb1f5ae 100644
--- a/src/GreenshotPlugin/Core/EffectConverter.cs
+++ b/src/Greenshot.Base/Core/EffectConverter.cs
@@ -1,233 +1,233 @@
-using System;
-using System.ComponentModel;
-using System.Drawing;
-using System.Globalization;
-using System.Text;
-using GreenshotPlugin.Effects;
-
-namespace GreenshotPlugin.Core
-{
- public class EffectConverter : TypeConverter
- {
- // Fix to prevent BUG-1753
- private readonly NumberFormatInfo _numberFormatInfo = new NumberFormatInfo();
-
- public EffectConverter()
- {
- _numberFormatInfo.NumberDecimalSeparator = ".";
- _numberFormatInfo.NumberGroupSeparator = ",";
- }
-
- public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
- {
- if (sourceType == typeof(string))
- {
- return true;
- }
-
- return base.CanConvertFrom(context, sourceType);
- }
-
- public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
- {
- if (destinationType == typeof(string))
- {
- return true;
- }
-
- if (destinationType == typeof(DropShadowEffect))
- {
- return true;
- }
-
- if (destinationType == typeof(TornEdgeEffect))
- {
- return true;
- }
-
- return base.CanConvertTo(context, destinationType);
- }
-
- public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
- {
- // to string
- if (destinationType == typeof(string))
- {
- StringBuilder sb = new StringBuilder();
- if (value.GetType() == typeof(DropShadowEffect))
- {
- DropShadowEffect effect = value as DropShadowEffect;
- RetrieveDropShadowEffectValues(effect, sb);
- return sb.ToString();
- }
-
- if (value.GetType() == typeof(TornEdgeEffect))
- {
- TornEdgeEffect effect = value as TornEdgeEffect;
- RetrieveDropShadowEffectValues(effect, sb);
- sb.Append("|");
- RetrieveTornEdgeEffectValues(effect, sb);
- return sb.ToString();
- }
- }
-
- // from string
- if (value is string)
- {
- string settings = value as string;
- if (destinationType == typeof(DropShadowEffect))
- {
- DropShadowEffect effect = new DropShadowEffect();
- ApplyDropShadowEffectValues(settings, effect);
- return effect;
- }
-
- if (destinationType == typeof(TornEdgeEffect))
- {
- TornEdgeEffect effect = new TornEdgeEffect();
- ApplyDropShadowEffectValues(settings, effect);
- ApplyTornEdgeEffectValues(settings, effect);
- return effect;
- }
- }
-
- return base.ConvertTo(context, culture, value, destinationType);
- }
-
- public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
- {
- if (value is string settings)
- {
- if (settings.Contains("ToothHeight"))
- {
- return ConvertTo(context, culture, settings, typeof(TornEdgeEffect));
- }
-
- return ConvertTo(context, culture, settings, typeof(DropShadowEffect));
- }
-
- return base.ConvertFrom(context, culture, value);
- }
-
- private void ApplyDropShadowEffectValues(string valuesString, DropShadowEffect effect)
- {
- string[] values = valuesString.Split('|');
- foreach (string nameValuePair in values)
- {
- string[] pair = nameValuePair.Split(':');
- switch (pair[0])
- {
- case "Darkness":
- // Fix to prevent BUG-1753
- if (pair[1] != null && float.TryParse(pair[1], NumberStyles.Float, _numberFormatInfo, out var darkness))
- {
- if (darkness <= 1.0)
- {
- effect.Darkness = darkness;
- }
- }
-
- break;
- case "ShadowSize":
- if (int.TryParse(pair[1], out var shadowSize))
- {
- effect.ShadowSize = shadowSize;
- }
-
- break;
- case "ShadowOffset":
- Point shadowOffset = new Point();
- string[] coordinates = pair[1].Split(',');
- if (int.TryParse(coordinates[0], out var shadowOffsetX))
- {
- shadowOffset.X = shadowOffsetX;
- }
-
- if (int.TryParse(coordinates[1], out var shadowOffsetY))
- {
- shadowOffset.Y = shadowOffsetY;
- }
-
- effect.ShadowOffset = shadowOffset;
- break;
- }
- }
- }
-
- private void ApplyTornEdgeEffectValues(string valuesString, TornEdgeEffect effect)
- {
- string[] values = valuesString.Split('|');
- foreach (string nameValuePair in values)
- {
- string[] pair = nameValuePair.Split(':');
- switch (pair[0])
- {
- case "GenerateShadow":
- if (bool.TryParse(pair[1], out var generateShadow))
- {
- effect.GenerateShadow = generateShadow;
- }
-
- break;
- case "ToothHeight":
- if (int.TryParse(pair[1], out var toothHeight))
- {
- effect.ToothHeight = toothHeight;
- }
-
- break;
- case "HorizontalToothRange":
- if (int.TryParse(pair[1], out var horizontalToothRange))
- {
- effect.HorizontalToothRange = horizontalToothRange;
- }
-
- break;
- case "VerticalToothRange":
- if (int.TryParse(pair[1], out var verticalToothRange))
- {
- effect.VerticalToothRange = verticalToothRange;
- }
-
- break;
- case "Edges":
- string[] edges = pair[1].Split(',');
- if (bool.TryParse(edges[0], out var edge))
- {
- effect.Edges[0] = edge;
- }
-
- if (bool.TryParse(edges[1], out edge))
- {
- effect.Edges[1] = edge;
- }
-
- if (bool.TryParse(edges[2], out edge))
- {
- effect.Edges[2] = edge;
- }
-
- if (bool.TryParse(edges[3], out edge))
- {
- effect.Edges[3] = edge;
- }
-
- break;
- }
- }
- }
-
- private void RetrieveDropShadowEffectValues(DropShadowEffect effect, StringBuilder sb)
- {
- // Fix to prevent BUG-1753 is to use the numberFormatInfo
- sb.AppendFormat("Darkness:{0}|ShadowSize:{1}|ShadowOffset:{2},{3}", effect.Darkness.ToString("F2", _numberFormatInfo), effect.ShadowSize, effect.ShadowOffset.X,
- effect.ShadowOffset.Y);
- }
-
- private void RetrieveTornEdgeEffectValues(TornEdgeEffect effect, StringBuilder sb)
- {
- sb.AppendFormat("GenerateShadow:{0}|ToothHeight:{1}|HorizontalToothRange:{2}|VerticalToothRange:{3}|Edges:{4},{5},{6},{7}", effect.GenerateShadow, effect.ToothHeight,
- effect.HorizontalToothRange, effect.VerticalToothRange, effect.Edges[0], effect.Edges[1], effect.Edges[2], effect.Edges[3]);
- }
- }
+using System;
+using System.ComponentModel;
+using System.Drawing;
+using System.Globalization;
+using System.Text;
+using Greenshot.Base.Effects;
+
+namespace Greenshot.Base.Core
+{
+ public class EffectConverter : TypeConverter
+ {
+ // Fix to prevent BUG-1753
+ private readonly NumberFormatInfo _numberFormatInfo = new NumberFormatInfo();
+
+ public EffectConverter()
+ {
+ _numberFormatInfo.NumberDecimalSeparator = ".";
+ _numberFormatInfo.NumberGroupSeparator = ",";
+ }
+
+ public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
+ {
+ if (sourceType == typeof(string))
+ {
+ return true;
+ }
+
+ return base.CanConvertFrom(context, sourceType);
+ }
+
+ public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
+ {
+ if (destinationType == typeof(string))
+ {
+ return true;
+ }
+
+ if (destinationType == typeof(DropShadowEffect))
+ {
+ return true;
+ }
+
+ if (destinationType == typeof(TornEdgeEffect))
+ {
+ return true;
+ }
+
+ return base.CanConvertTo(context, destinationType);
+ }
+
+ public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
+ {
+ // to string
+ if (destinationType == typeof(string))
+ {
+ StringBuilder sb = new StringBuilder();
+ if (value.GetType() == typeof(DropShadowEffect))
+ {
+ DropShadowEffect effect = value as DropShadowEffect;
+ RetrieveDropShadowEffectValues(effect, sb);
+ return sb.ToString();
+ }
+
+ if (value.GetType() == typeof(TornEdgeEffect))
+ {
+ TornEdgeEffect effect = value as TornEdgeEffect;
+ RetrieveDropShadowEffectValues(effect, sb);
+ sb.Append("|");
+ RetrieveTornEdgeEffectValues(effect, sb);
+ return sb.ToString();
+ }
+ }
+
+ // from string
+ if (value is string)
+ {
+ string settings = value as string;
+ if (destinationType == typeof(DropShadowEffect))
+ {
+ DropShadowEffect effect = new DropShadowEffect();
+ ApplyDropShadowEffectValues(settings, effect);
+ return effect;
+ }
+
+ if (destinationType == typeof(TornEdgeEffect))
+ {
+ TornEdgeEffect effect = new TornEdgeEffect();
+ ApplyDropShadowEffectValues(settings, effect);
+ ApplyTornEdgeEffectValues(settings, effect);
+ return effect;
+ }
+ }
+
+ return base.ConvertTo(context, culture, value, destinationType);
+ }
+
+ public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
+ {
+ if (value is string settings)
+ {
+ if (settings.Contains("ToothHeight"))
+ {
+ return ConvertTo(context, culture, settings, typeof(TornEdgeEffect));
+ }
+
+ return ConvertTo(context, culture, settings, typeof(DropShadowEffect));
+ }
+
+ return base.ConvertFrom(context, culture, value);
+ }
+
+ private void ApplyDropShadowEffectValues(string valuesString, DropShadowEffect effect)
+ {
+ string[] values = valuesString.Split('|');
+ foreach (string nameValuePair in values)
+ {
+ string[] pair = nameValuePair.Split(':');
+ switch (pair[0])
+ {
+ case "Darkness":
+ // Fix to prevent BUG-1753
+ if (pair[1] != null && float.TryParse(pair[1], NumberStyles.Float, _numberFormatInfo, out var darkness))
+ {
+ if (darkness <= 1.0)
+ {
+ effect.Darkness = darkness;
+ }
+ }
+
+ break;
+ case "ShadowSize":
+ if (int.TryParse(pair[1], out var shadowSize))
+ {
+ effect.ShadowSize = shadowSize;
+ }
+
+ break;
+ case "ShadowOffset":
+ Point shadowOffset = new Point();
+ string[] coordinates = pair[1].Split(',');
+ if (int.TryParse(coordinates[0], out var shadowOffsetX))
+ {
+ shadowOffset.X = shadowOffsetX;
+ }
+
+ if (int.TryParse(coordinates[1], out var shadowOffsetY))
+ {
+ shadowOffset.Y = shadowOffsetY;
+ }
+
+ effect.ShadowOffset = shadowOffset;
+ break;
+ }
+ }
+ }
+
+ private void ApplyTornEdgeEffectValues(string valuesString, TornEdgeEffect effect)
+ {
+ string[] values = valuesString.Split('|');
+ foreach (string nameValuePair in values)
+ {
+ string[] pair = nameValuePair.Split(':');
+ switch (pair[0])
+ {
+ case "GenerateShadow":
+ if (bool.TryParse(pair[1], out var generateShadow))
+ {
+ effect.GenerateShadow = generateShadow;
+ }
+
+ break;
+ case "ToothHeight":
+ if (int.TryParse(pair[1], out var toothHeight))
+ {
+ effect.ToothHeight = toothHeight;
+ }
+
+ break;
+ case "HorizontalToothRange":
+ if (int.TryParse(pair[1], out var horizontalToothRange))
+ {
+ effect.HorizontalToothRange = horizontalToothRange;
+ }
+
+ break;
+ case "VerticalToothRange":
+ if (int.TryParse(pair[1], out var verticalToothRange))
+ {
+ effect.VerticalToothRange = verticalToothRange;
+ }
+
+ break;
+ case "Edges":
+ string[] edges = pair[1].Split(',');
+ if (bool.TryParse(edges[0], out var edge))
+ {
+ effect.Edges[0] = edge;
+ }
+
+ if (bool.TryParse(edges[1], out edge))
+ {
+ effect.Edges[1] = edge;
+ }
+
+ if (bool.TryParse(edges[2], out edge))
+ {
+ effect.Edges[2] = edge;
+ }
+
+ if (bool.TryParse(edges[3], out edge))
+ {
+ effect.Edges[3] = edge;
+ }
+
+ break;
+ }
+ }
+ }
+
+ private void RetrieveDropShadowEffectValues(DropShadowEffect effect, StringBuilder sb)
+ {
+ // Fix to prevent BUG-1753 is to use the numberFormatInfo
+ sb.AppendFormat("Darkness:{0}|ShadowSize:{1}|ShadowOffset:{2},{3}", effect.Darkness.ToString("F2", _numberFormatInfo), effect.ShadowSize, effect.ShadowOffset.X,
+ effect.ShadowOffset.Y);
+ }
+
+ private void RetrieveTornEdgeEffectValues(TornEdgeEffect effect, StringBuilder sb)
+ {
+ sb.AppendFormat("GenerateShadow:{0}|ToothHeight:{1}|HorizontalToothRange:{2}|VerticalToothRange:{3}|Edges:{4},{5},{6},{7}", effect.GenerateShadow, effect.ToothHeight,
+ effect.HorizontalToothRange, effect.VerticalToothRange, effect.Edges[0], effect.Edges[1], effect.Edges[2], effect.Edges[3]);
+ }
+ }
}
\ No newline at end of file
diff --git a/src/GreenshotPlugin/Core/Enums/HResult.cs b/src/Greenshot.Base/Core/Enums/HResult.cs
similarity index 97%
rename from src/GreenshotPlugin/Core/Enums/HResult.cs
rename to src/Greenshot.Base/Core/Enums/HResult.cs
index 96e867c6c..e278162f9 100644
--- a/src/GreenshotPlugin/Core/Enums/HResult.cs
+++ b/src/Greenshot.Base/Core/Enums/HResult.cs
@@ -19,7 +19,7 @@
using System.Diagnostics.CodeAnalysis;
-namespace GreenshotPlugin.Core.Enums
+namespace Greenshot.Base.Core.Enums
{
///
/// The HRESULT represents Windows error codes
diff --git a/src/GreenshotPlugin/Core/Enums/MonitorDpiType.cs b/src/Greenshot.Base/Core/Enums/MonitorDpiType.cs
similarity index 97%
rename from src/GreenshotPlugin/Core/Enums/MonitorDpiType.cs
rename to src/Greenshot.Base/Core/Enums/MonitorDpiType.cs
index c1db8ce18..e434f5bb3 100644
--- a/src/GreenshotPlugin/Core/Enums/MonitorDpiType.cs
+++ b/src/Greenshot.Base/Core/Enums/MonitorDpiType.cs
@@ -3,7 +3,7 @@
using System;
-namespace GreenshotPlugin.Core.Enums
+namespace Greenshot.Base.Core.Enums
{
///
/// See
diff --git a/src/GreenshotPlugin/Core/Enums/MonitorFrom.cs b/src/Greenshot.Base/Core/Enums/MonitorFrom.cs
similarity index 96%
rename from src/GreenshotPlugin/Core/Enums/MonitorFrom.cs
rename to src/Greenshot.Base/Core/Enums/MonitorFrom.cs
index 40f25961b..f9d9fcc2b 100644
--- a/src/GreenshotPlugin/Core/Enums/MonitorFrom.cs
+++ b/src/Greenshot.Base/Core/Enums/MonitorFrom.cs
@@ -3,7 +3,7 @@
using System;
-namespace GreenshotPlugin.Core.Enums
+namespace Greenshot.Base.Core.Enums
{
///
/// Flags for the MonitorFromRect / MonitorFromWindow "flags" field
diff --git a/src/GreenshotPlugin/Core/EnvironmentInfo.cs b/src/Greenshot.Base/Core/EnvironmentInfo.cs
similarity index 99%
rename from src/GreenshotPlugin/Core/EnvironmentInfo.cs
rename to src/Greenshot.Base/Core/EnvironmentInfo.cs
index 27a3eeda1..9c23cd2df 100644
--- a/src/GreenshotPlugin/Core/EnvironmentInfo.cs
+++ b/src/Greenshot.Base/Core/EnvironmentInfo.cs
@@ -23,11 +23,11 @@ using System;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text;
-using GreenshotPlugin.IniFile;
-using GreenshotPlugin.UnmanagedHelpers;
+using Greenshot.Base.IniFile;
+using Greenshot.Base.UnmanagedHelpers;
using Microsoft.Win32;
-namespace GreenshotPlugin.Core
+namespace Greenshot.Base.Core
{
///
/// Description of EnvironmentInfo.
diff --git a/src/GreenshotPlugin/Core/EventDelay.cs b/src/Greenshot.Base/Core/EventDelay.cs
similarity index 97%
rename from src/GreenshotPlugin/Core/EventDelay.cs
rename to src/Greenshot.Base/Core/EventDelay.cs
index 1c6ec235c..c1a41a1be 100644
--- a/src/GreenshotPlugin/Core/EventDelay.cs
+++ b/src/Greenshot.Base/Core/EventDelay.cs
@@ -21,7 +21,7 @@
using System;
-namespace GreenshotPlugin.Core
+namespace Greenshot.Base.Core
{
public class EventDelay
{
diff --git a/src/GreenshotPlugin/Core/ExplorerHelper.cs b/src/Greenshot.Base/Core/ExplorerHelper.cs
similarity index 94%
rename from src/GreenshotPlugin/Core/ExplorerHelper.cs
rename to src/Greenshot.Base/Core/ExplorerHelper.cs
index 934d0ed74..0ebb88db3 100644
--- a/src/GreenshotPlugin/Core/ExplorerHelper.cs
+++ b/src/Greenshot.Base/Core/ExplorerHelper.cs
@@ -1,55 +1,55 @@
-using System;
-using System.Diagnostics;
-using System.IO;
-
-namespace GreenshotPlugin.Core
-{
- ///
- /// Simple utility for the explorer
- ///
- public static class ExplorerHelper
- {
- ///
- /// Open the path in the windows explorer.
- /// If the path is a directory, it will just open the explorer with that directory.
- /// If the path is a file, the explorer is opened with the directory and the file is selected.
- ///
- /// Path to file or directory
- public static bool OpenInExplorer(string path)
- {
- if (path == null)
- {
- return false;
- }
-
- try
- {
- // Check if path is a directory
- if (Directory.Exists(path))
- {
- using (Process.Start(path))
- {
- return true;
- }
- }
-
- // Check if path is a file
- if (File.Exists(path))
- {
- // Start the explorer process and select the file
- using var explorer = Process.Start("explorer.exe", $"/select,\"{path}\"");
- explorer?.WaitForInputIdle(500);
- return true;
- }
- }
- catch (Exception ex)
- {
- // Make sure we show what we tried to open in the exception
- ex.Data.Add("path", path);
- throw;
- }
-
- return false;
- }
- }
+using System;
+using System.Diagnostics;
+using System.IO;
+
+namespace Greenshot.Base.Core
+{
+ ///
+ /// Simple utility for the explorer
+ ///
+ public static class ExplorerHelper
+ {
+ ///
+ /// Open the path in the windows explorer.
+ /// If the path is a directory, it will just open the explorer with that directory.
+ /// If the path is a file, the explorer is opened with the directory and the file is selected.
+ ///
+ /// Path to file or directory
+ public static bool OpenInExplorer(string path)
+ {
+ if (path == null)
+ {
+ return false;
+ }
+
+ try
+ {
+ // Check if path is a directory
+ if (Directory.Exists(path))
+ {
+ using (Process.Start(path))
+ {
+ return true;
+ }
+ }
+
+ // Check if path is a file
+ if (File.Exists(path))
+ {
+ // Start the explorer process and select the file
+ using var explorer = Process.Start("explorer.exe", $"/select,\"{path}\"");
+ explorer?.WaitForInputIdle(500);
+ return true;
+ }
+ }
+ catch (Exception ex)
+ {
+ // Make sure we show what we tried to open in the exception
+ ex.Data.Add("path", path);
+ throw;
+ }
+
+ return false;
+ }
+ }
}
\ No newline at end of file
diff --git a/src/GreenshotPlugin/Core/FastBitmap.cs b/src/Greenshot.Base/Core/FastBitmap.cs
similarity index 97%
rename from src/GreenshotPlugin/Core/FastBitmap.cs
rename to src/Greenshot.Base/Core/FastBitmap.cs
index 2ef28af93..3b6685dd0 100644
--- a/src/GreenshotPlugin/Core/FastBitmap.cs
+++ b/src/Greenshot.Base/Core/FastBitmap.cs
@@ -1,1055 +1,1055 @@
-/*
- * Greenshot - a free and open source screenshot tool
- * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
- *
- * For more information see: http://getgreenshot.org/
- * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 1 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-using System;
-using System.Collections.Generic;
-using System.Drawing;
-using System.Drawing.Imaging;
-
-namespace GreenshotPlugin.Core
-{
- ///
- /// The interface for the FastBitmap
- ///
- public interface IFastBitmap : IDisposable
- {
- ///
- /// Get the color at x,y
- /// The returned Color object depends on the underlying pixel format
- ///
- /// int x
- /// int y
- /// Color
- Color GetColorAt(int x, int y);
-
- ///
- /// Set the color at the specified location
- ///
- /// int x
- /// int y
- /// Color
- void SetColorAt(int x, int y, Color color);
-
- ///
- /// Get the color at x,y
- /// The returned byte[] color depends on the underlying pixel format
- ///
- /// int x
- /// int y
- /// byte array
- void GetColorAt(int x, int y, byte[] color);
-
- ///
- /// Set the color at the specified location
- ///
- /// int x
- /// int y
- /// byte[] color
- void SetColorAt(int x, int y, byte[] color);
-
- ///
- /// Lock the bitmap
- ///
- void Lock();
-
- ///
- /// Unlock the bitmap
- ///
- void Unlock();
-
- ///
- /// Unlock the bitmap and get the underlying bitmap in one call
- ///
- ///
- Bitmap UnlockAndReturnBitmap();
-
- ///
- /// Size of the underlying image
- ///
- Size Size { get; }
-
- ///
- /// Height of the image area that this fastbitmap covers
- ///
- int Height { get; }
-
- ///
- /// Width of the image area that this fastbitmap covers
- ///
- int Width { get; }
-
- ///
- /// Top of the image area that this fastbitmap covers
- ///
- int Top { get; }
-
- ///
- /// Left of the image area that this fastbitmap covers
- ///
- int Left { get; }
-
- ///
- /// Right of the image area that this fastbitmap covers
- ///
- int Right { get; }
-
- ///
- /// Bottom of the image area that this fastbitmap covers
- ///
- int Bottom { get; }
-
- ///
- /// Does the underlying image need to be disposed
- ///
- bool NeedsDispose { get; set; }
-
- ///
- /// Returns if this FastBitmap has an alpha channel
- ///
- bool HasAlphaChannel { get; }
-
- ///
- /// Draw the stored bitmap to the destionation bitmap at the supplied point
- ///
- /// Graphics
- /// Point with location
- void DrawTo(Graphics graphics, Point destination);
-
- ///
- /// Draw the stored Bitmap on the Destination bitmap with the specified rectangle
- /// Be aware that the stored bitmap will be resized to the specified rectangle!!
- ///
- /// Graphics
- /// Rectangle with destination
- void DrawTo(Graphics graphics, Rectangle destinationRect);
-
- ///
- /// Return true if the coordinates are inside the FastBitmap
- ///
- ///
- ///
- ///
- bool Contains(int x, int y);
-
- ///
- /// Set the bitmap resolution
- ///
- ///
- ///
- void SetResolution(float horizontal, float vertical);
- }
-
- ///
- /// This interface can be used for when offsetting is needed
- ///
- public interface IFastBitmapWithOffset : IFastBitmap
- {
- ///
- /// Return true if the coordinates are inside the FastBitmap
- ///
- ///
- ///
- ///
- new bool Contains(int x, int y);
-
- ///
- /// Set the color at the specified location, using offsetting so the original coordinates can be used
- ///
- /// int x
- /// int y
- /// Color color
- new void SetColorAt(int x, int y, Color color);
-
- ///
- /// Set the color at the specified location, using offsetting so the original coordinates can be used
- ///
- /// int x
- /// int y
- /// byte[] color
- new void SetColorAt(int x, int y, byte[] color);
-
- ///
- /// Get the color at x,y
- /// The returned Color object depends on the underlying pixel format
- ///
- /// int x
- /// int y
- /// Color
- new Color GetColorAt(int x, int y);
-
- ///
- /// Get the color at x,y, using offsetting so the original coordinates can be used
- /// The returned byte[] color depends on the underlying pixel format
- ///
- /// int x
- /// int y
- /// byte array
- new void GetColorAt(int x, int y, byte[] color);
-
- new int Left { get; set; }
-
- new int Top { get; set; }
- }
-
- ///
- /// This interface can be used for when clipping is needed
- ///
- public interface IFastBitmapWithClip : IFastBitmap
- {
- Rectangle Clip { get; set; }
-
- bool InvertClip { get; set; }
-
- ///
- /// Set the color at the specified location, this doesn't do anything if the location is excluded due to clipping
- ///
- /// int x
- /// int y
- /// Color color
- new void SetColorAt(int x, int y, Color color);
-
- ///
- /// Set the color at the specified location, this doesn't do anything if the location is excluded due to clipping
- ///
- /// int x
- /// int y
- /// byte[] color
- new void SetColorAt(int x, int y, byte[] color);
-
- ///
- /// Return true if the coordinates are inside the FastBitmap and not clipped
- ///
- ///
- ///
- ///
- new bool Contains(int x, int y);
- }
-
- ///
- /// This interface is implemented when there is a alpha-blending possibility
- ///
- public interface IFastBitmapWithBlend : IFastBitmap
- {
- Color BackgroundBlendColor { get; set; }
- Color GetBlendedColorAt(int x, int y);
- }
-
- ///
- /// The base class for the fast bitmap implementation
- ///
- public abstract unsafe class FastBitmap : IFastBitmapWithClip, IFastBitmapWithOffset
- {
- protected const int PixelformatIndexA = 3;
- protected const int PixelformatIndexR = 2;
- protected const int PixelformatIndexG = 1;
- protected const int PixelformatIndexB = 0;
-
- public const int ColorIndexR = 0;
- public const int ColorIndexG = 1;
- public const int ColorIndexB = 2;
- public const int ColorIndexA = 3;
-
- protected Rectangle Area;
-
- ///
- /// If this is set to true, the bitmap will be disposed when disposing the IFastBitmap
- ///
- public bool NeedsDispose { get; set; }
-
- public Rectangle Clip { get; set; }
-
- public bool InvertClip { get; set; }
-
- ///
- /// The bitmap for which the FastBitmap is creating access
- ///
- protected Bitmap Bitmap;
-
- protected BitmapData BmData;
- protected int Stride; /* bytes per pixel row */
- protected bool BitsLocked;
- protected byte* Pointer;
-
- public static IFastBitmap Create(Bitmap source)
- {
- return Create(source, Rectangle.Empty);
- }
-
- public void SetResolution(float horizontal, float vertical)
- {
- Bitmap.SetResolution(horizontal, vertical);
- }
-
- ///
- /// Factory for creating a FastBitmap depending on the pixelformat of the source
- /// The supplied rectangle specifies the area for which the FastBitmap does its thing
- ///
- /// Bitmap to access
- /// Rectangle which specifies the area to have access to, can be Rectangle.Empty for the whole image
- /// IFastBitmap
- public static IFastBitmap Create(Bitmap source, Rectangle area)
- {
- switch (source.PixelFormat)
- {
- case PixelFormat.Format8bppIndexed:
- return new FastChunkyBitmap(source, area);
- case PixelFormat.Format24bppRgb:
- return new Fast24RgbBitmap(source, area);
- case PixelFormat.Format32bppRgb:
- return new Fast32RgbBitmap(source, area);
- case PixelFormat.Format32bppArgb:
- case PixelFormat.Format32bppPArgb:
- return new Fast32ArgbBitmap(source, area);
- default:
- throw new NotSupportedException($"Not supported Pixelformat {source.PixelFormat}");
- }
- }
-
- ///
- /// Factory for creating a FastBitmap as a destination for the source
- ///
- /// Bitmap to clone
- /// new Pixelformat
- /// IFastBitmap
- public static IFastBitmap CreateCloneOf(Image source, PixelFormat pixelFormat)
- {
- return CreateCloneOf(source, pixelFormat, Rectangle.Empty);
- }
-
- ///
- /// Factory for creating a FastBitmap as a destination for the source
- ///
- /// Bitmap to clone
- /// Area of the bitmap to access, can be Rectangle.Empty for the whole
- /// IFastBitmap
- public static IFastBitmap CreateCloneOf(Image source, Rectangle area)
- {
- return CreateCloneOf(source, PixelFormat.DontCare, area);
- }
-
- ///
- /// Factory for creating a FastBitmap as a destination for the source
- ///
- /// Bitmap to clone
- /// Pixelformat of the cloned bitmap
- /// Area of the bitmap to access, can be Rectangle.Empty for the whole
- /// IFastBitmap
- public static IFastBitmap CreateCloneOf(Image source, PixelFormat pixelFormat, Rectangle area)
- {
- Bitmap destination = ImageHelper.CloneArea(source, area, pixelFormat);
- FastBitmap fastBitmap = Create(destination) as FastBitmap;
- if (fastBitmap != null)
- {
- fastBitmap.NeedsDispose = true;
- fastBitmap.Left = area.Left;
- fastBitmap.Top = area.Top;
- }
-
- return fastBitmap;
- }
-
- ///
- /// Factory for creating a FastBitmap as a destination
- ///
- ///
- ///
- ///
- /// IFastBitmap
- public static IFastBitmap CreateEmpty(Size newSize, PixelFormat pixelFormat, Color backgroundColor)
- {
- Bitmap destination = ImageHelper.CreateEmpty(newSize.Width, newSize.Height, pixelFormat, backgroundColor, 96f, 96f);
- IFastBitmap fastBitmap = Create(destination);
- fastBitmap.NeedsDispose = true;
- return fastBitmap;
- }
-
- ///
- /// Constructor which stores the image and locks it when called
- ///
- /// Bitmap
- /// Rectangle
- protected FastBitmap(Bitmap bitmap, Rectangle area)
- {
- Bitmap = bitmap;
- Rectangle bitmapArea = new Rectangle(Point.Empty, bitmap.Size);
- if (area != Rectangle.Empty)
- {
- area.Intersect(bitmapArea);
- Area = area;
- }
- else
- {
- Area = bitmapArea;
- }
-
- // As the lock takes care that only the specified area is made available we need to calculate the offset
- Left = area.Left;
- Top = area.Top;
- // Default cliping is done to the area without invert
- Clip = Area;
- InvertClip = false;
- // Always lock, so we don't need to do this ourselves
- Lock();
- }
-
- ///
- /// Return the size of the image
- ///
- public Size Size
- {
- get
- {
- if (Area == Rectangle.Empty)
- {
- return Bitmap.Size;
- }
-
- return Area.Size;
- }
- }
-
- ///
- /// Return the width of the image
- ///
- public int Width
- {
- get
- {
- if (Area == Rectangle.Empty)
- {
- return Bitmap.Width;
- }
-
- return Area.Width;
- }
- }
-
- ///
- /// Return the height of the image
- ///
- public int Height
- {
- get
- {
- if (Area == Rectangle.Empty)
- {
- return Bitmap.Height;
- }
-
- return Area.Height;
- }
- }
-
- private int _left;
-
- ///
- /// Return the left of the fastbitmap, this is also used as an offset
- ///
- public int Left
- {
- get { return 0; }
- set { _left = value; }
- }
-
- ///
- /// Return the left of the fastbitmap, this is also used as an offset
- ///
- int IFastBitmapWithOffset.Left
- {
- get { return _left; }
- set { _left = value; }
- }
-
- private int _top;
-
- ///
- /// Return the top of the fastbitmap, this is also used as an offset
- ///
- public int Top
- {
- get { return 0; }
- set { _top = value; }
- }
-
- ///
- /// Return the top of the fastbitmap, this is also used as an offset
- ///
- int IFastBitmapWithOffset.Top
- {
- get { return _top; }
- set { _top = value; }
- }
-
- ///
- /// Return the right of the fastbitmap
- ///
- public int Right => Left + Width;
-
- ///
- /// Return the bottom of the fastbitmap
- ///
- public int Bottom => Top + Height;
-
- ///
- /// Returns the underlying bitmap, unlocks it and prevents that it will be disposed
- ///
- public Bitmap UnlockAndReturnBitmap()
- {
- if (BitsLocked)
- {
- Unlock();
- }
-
- NeedsDispose = false;
- return Bitmap;
- }
-
- public virtual bool HasAlphaChannel => false;
-
- ///
- /// Destructor
- ///
- ~FastBitmap()
- {
- Dispose(false);
- }
-
- ///
- /// The public accessible Dispose
- /// Will call the GarbageCollector to SuppressFinalize, preventing being cleaned twice
- ///
- public void Dispose()
- {
- Dispose(true);
- GC.SuppressFinalize(this);
- }
-
- // 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 virtual void Dispose(bool disposing)
- {
- Unlock();
- if (disposing)
- {
- if (Bitmap != null && NeedsDispose)
- {
- Bitmap.Dispose();
- }
- }
-
- Bitmap = null;
- BmData = null;
- Pointer = null;
- }
-
- ///
- /// Lock the bitmap so we have direct access to the memory
- ///
- public void Lock()
- {
- if (Width <= 0 || Height <= 0 || BitsLocked)
- {
- return;
- }
-
- BmData = Bitmap.LockBits(Area, ImageLockMode.ReadWrite, Bitmap.PixelFormat);
- BitsLocked = true;
-
- IntPtr scan0 = BmData.Scan0;
- Pointer = (byte*) (void*) scan0;
- Stride = BmData.Stride;
- }
-
- ///
- /// Unlock the System Memory
- ///
- public void Unlock()
- {
- if (BitsLocked)
- {
- Bitmap.UnlockBits(BmData);
- BitsLocked = false;
- }
- }
-
- ///
- /// Draw the stored bitmap to the destionation bitmap at the supplied point
- ///
- ///
- ///
- public void DrawTo(Graphics graphics, Point destination)
- {
- DrawTo(graphics, new Rectangle(destination, Area.Size));
- }
-
- ///
- /// Draw the stored Bitmap on the Destination bitmap with the specified rectangle
- /// Be aware that the stored bitmap will be resized to the specified rectangle!!
- ///
- ///
- ///
- public void DrawTo(Graphics graphics, Rectangle destinationRect)
- {
- // Make sure this.bitmap is unlocked, if it was locked
- bool isLocked = BitsLocked;
- if (isLocked)
- {
- Unlock();
- }
-
- graphics.DrawImage(Bitmap, destinationRect, Area, GraphicsUnit.Pixel);
- }
-
- ///
- /// returns true if x & y are inside the FastBitmap
- ///
- ///
- ///
- /// true if x & y are inside the FastBitmap
- public bool Contains(int x, int y)
- {
- return Area.Contains(x - Left, y - Top);
- }
-
- public abstract Color GetColorAt(int x, int y);
- public abstract void SetColorAt(int x, int y, Color color);
- public abstract void GetColorAt(int x, int y, byte[] color);
- public abstract void SetColorAt(int x, int y, byte[] color);
-
- bool IFastBitmapWithClip.Contains(int x, int y)
- {
- bool contains = Clip.Contains(x, y);
- if (InvertClip)
- {
- return !contains;
- }
- else
- {
- return contains;
- }
- }
-
- void IFastBitmapWithClip.SetColorAt(int x, int y, byte[] color)
- {
- bool contains = Clip.Contains(x, y);
- if ((InvertClip && contains) || (!InvertClip && !contains))
- {
- return;
- }
-
- SetColorAt(x, y, color);
- }
-
- void IFastBitmapWithClip.SetColorAt(int x, int y, Color color)
- {
- bool contains = Clip.Contains(x, y);
- if ((InvertClip && contains) || (!InvertClip && !contains))
- {
- return;
- }
-
- SetColorAt(x, y, color);
- }
-
- ///
- /// returns true if x & y are inside the FastBitmap
- ///
- ///
- ///
- /// true if x & y are inside the FastBitmap
- bool IFastBitmapWithOffset.Contains(int x, int y)
- {
- return Area.Contains(x - Left, y - Top);
- }
-
- Color IFastBitmapWithOffset.GetColorAt(int x, int y)
- {
- x -= _left;
- y -= _top;
- return GetColorAt(x, y);
- }
-
- void IFastBitmapWithOffset.GetColorAt(int x, int y, byte[] color)
- {
- x -= _left;
- y -= _top;
- GetColorAt(x, y, color);
- }
-
- void IFastBitmapWithOffset.SetColorAt(int x, int y, byte[] color)
- {
- x -= _left;
- y -= _top;
- SetColorAt(x, y, color);
- }
-
- void IFastBitmapWithOffset.SetColorAt(int x, int y, Color color)
- {
- x -= _left;
- y -= _top;
- SetColorAt(x, y, color);
- }
- }
-
- ///
- /// This is the implementation of the FastBitmat for the 8BPP pixelformat
- ///
- public unsafe class FastChunkyBitmap : FastBitmap
- {
- // Used for indexed images
- private readonly Color[] _colorEntries;
- private readonly Dictionary _colorCache = new Dictionary();
-
- public FastChunkyBitmap(Bitmap source, Rectangle area) : base(source, area)
- {
- _colorEntries = Bitmap.Palette.Entries;
- }
-
- ///
- /// Get the color from the specified location
- ///
- ///
- ///
- /// Color
- public override Color GetColorAt(int x, int y)
- {
- int offset = x + (y * Stride);
- byte colorIndex = Pointer[offset];
- return _colorEntries[colorIndex];
- }
-
- ///
- /// Get the color from the specified location into the specified array
- ///
- ///
- ///
- /// byte[4] as reference
- public override void GetColorAt(int x, int y, byte[] color)
- {
- throw new NotImplementedException("No performance gain!");
- }
-
- ///
- /// Set the color at the specified location from the specified array
- ///
- ///
- ///
- /// byte[4] as reference
- public override void SetColorAt(int x, int y, byte[] color)
- {
- throw new NotImplementedException("No performance gain!");
- }
-
- ///
- /// Get the color-index from the specified location
- ///
- ///
- ///
- /// byte with index
- public byte GetColorIndexAt(int x, int y)
- {
- int offset = x + (y * Stride);
- return Pointer[offset];
- }
-
- ///
- /// Set the color-index at the specified location
- ///
- ///
- ///
- ///
- public void SetColorIndexAt(int x, int y, byte colorIndex)
- {
- int offset = x + (y * Stride);
- Pointer[offset] = colorIndex;
- }
-
- ///
- /// Set the supplied color at the specified location.
- /// Throws an ArgumentException if the color is not in the palette
- ///
- ///
- ///
- /// Color to set
- public override void SetColorAt(int x, int y, Color color)
- {
- int offset = x + (y * Stride);
- if (!_colorCache.TryGetValue(color, out var colorIndex))
- {
- bool foundColor = false;
- for (colorIndex = 0; colorIndex < _colorEntries.Length; colorIndex++)
- {
- if (color == _colorEntries[colorIndex])
- {
- _colorCache.Add(color, colorIndex);
- foundColor = true;
- break;
- }
- }
-
- if (!foundColor)
- {
- throw new ArgumentException("No such color!");
- }
- }
-
- Pointer[offset] = colorIndex;
- }
- }
-
- ///
- /// This is the implementation of the IFastBitmap for 24 bit images (no Alpha)
- ///
- public unsafe class Fast24RgbBitmap : FastBitmap
- {
- public Fast24RgbBitmap(Bitmap source, Rectangle area) : base(source, area)
- {
- }
-
- ///
- /// Retrieve the color at location x,y
- /// Before the first time this is called the Lock() should be called once!
- ///
- /// X coordinate
- /// Y Coordinate
- /// Color
- public override Color GetColorAt(int x, int y)
- {
- int offset = (x * 3) + (y * Stride);
- return Color.FromArgb(255, Pointer[PixelformatIndexR + offset], Pointer[PixelformatIndexG + offset], Pointer[PixelformatIndexB + offset]);
- }
-
- ///
- /// Set the color at location x,y
- /// Before the first time this is called the Lock() should be called once!
- ///
- ///
- ///
- ///
- public override void SetColorAt(int x, int y, Color color)
- {
- int offset = (x * 3) + (y * Stride);
- Pointer[PixelformatIndexR + offset] = color.R;
- Pointer[PixelformatIndexG + offset] = color.G;
- Pointer[PixelformatIndexB + offset] = color.B;
- }
-
- ///
- /// Get the color from the specified location into the specified array
- ///
- ///
- ///
- /// byte[4] as reference (r,g,b)
- public override void GetColorAt(int x, int y, byte[] color)
- {
- int offset = (x * 3) + (y * Stride);
- color[PixelformatIndexR] = Pointer[PixelformatIndexR + offset];
- color[PixelformatIndexG] = Pointer[PixelformatIndexG + offset];
- color[PixelformatIndexB] = Pointer[PixelformatIndexB + offset];
- }
-
- ///
- /// Set the color at the specified location from the specified array
- ///
- ///
- ///
- /// byte[4] as reference (r,g,b)
- public override void SetColorAt(int x, int y, byte[] color)
- {
- int offset = (x * 3) + (y * Stride);
- Pointer[PixelformatIndexR + offset] = color[PixelformatIndexR];
- Pointer[PixelformatIndexG + offset] = color[PixelformatIndexG];
- Pointer[PixelformatIndexB + offset] = color[PixelformatIndexB];
- }
- }
-
- ///
- /// This is the implementation of the IFastBitmap for 32 bit images (no Alpha)
- ///
- public unsafe class Fast32RgbBitmap : FastBitmap
- {
- public Fast32RgbBitmap(Bitmap source, Rectangle area) : base(source, area)
- {
- }
-
- ///
- /// Retrieve the color at location x,y
- /// Before the first time this is called the Lock() should be called once!
- ///
- /// X coordinate
- /// Y Coordinate
- /// Color
- public override Color GetColorAt(int x, int y)
- {
- int offset = (x * 4) + (y * Stride);
- return Color.FromArgb(255, Pointer[PixelformatIndexR + offset], Pointer[PixelformatIndexG + offset], Pointer[PixelformatIndexB + offset]);
- }
-
- ///
- /// Set the color at location x,y
- /// Before the first time this is called the Lock() should be called once!
- ///
- ///
- ///
- ///
- public override void SetColorAt(int x, int y, Color color)
- {
- int offset = (x * 4) + (y * Stride);
- Pointer[PixelformatIndexR + offset] = color.R;
- Pointer[PixelformatIndexG + offset] = color.G;
- Pointer[PixelformatIndexB + offset] = color.B;
- }
-
- ///
- /// Get the color from the specified location into the specified array
- ///
- ///
- ///
- /// byte[4] as reference (a,r,g,b)
- public override void GetColorAt(int x, int y, byte[] color)
- {
- int offset = (x * 4) + (y * Stride);
- color[ColorIndexR] = Pointer[PixelformatIndexR + offset];
- color[ColorIndexG] = Pointer[PixelformatIndexG + offset];
- color[ColorIndexB] = Pointer[PixelformatIndexB + offset];
- }
-
- ///
- /// Set the color at the specified location from the specified array
- ///
- ///
- ///
- /// byte[4] as reference (r,g,b)
- public override void SetColorAt(int x, int y, byte[] color)
- {
- int offset = (x * 4) + (y * Stride);
- Pointer[PixelformatIndexR + offset] = color[ColorIndexR]; // R
- Pointer[PixelformatIndexG + offset] = color[ColorIndexG];
- Pointer[PixelformatIndexB + offset] = color[ColorIndexB];
- }
- }
-
- ///
- /// This is the implementation of the IFastBitmap for 32 bit images with Alpha
- ///
- public unsafe class Fast32ArgbBitmap : FastBitmap, IFastBitmapWithBlend
- {
- public override bool HasAlphaChannel => true;
-
- public Color BackgroundBlendColor { get; set; }
-
- public Fast32ArgbBitmap(Bitmap source, Rectangle area) : base(source, area)
- {
- BackgroundBlendColor = Color.White;
- }
-
- ///
- /// Retrieve the color at location x,y
- ///
- /// X coordinate
- /// Y Coordinate
- /// Color
- public override Color GetColorAt(int x, int y)
- {
- int offset = (x * 4) + (y * Stride);
- return Color.FromArgb(Pointer[PixelformatIndexA + offset], Pointer[PixelformatIndexR + offset], Pointer[PixelformatIndexG + offset],
- Pointer[PixelformatIndexB + offset]);
- }
-
- ///
- /// Set the color at location x,y
- /// Before the first time this is called the Lock() should be called once!
- ///
- ///
- ///
- ///
- public override void SetColorAt(int x, int y, Color color)
- {
- int offset = (x * 4) + (y * Stride);
- Pointer[PixelformatIndexA + offset] = color.A;
- Pointer[PixelformatIndexR + offset] = color.R;
- Pointer[PixelformatIndexG + offset] = color.G;
- Pointer[PixelformatIndexB + offset] = color.B;
- }
-
- ///
- /// Get the color from the specified location into the specified array
- ///
- ///
- ///
- /// byte[4] as reference (r,g,b,a)
- public override void GetColorAt(int x, int y, byte[] color)
- {
- int offset = (x * 4) + (y * Stride);
- color[ColorIndexR] = Pointer[PixelformatIndexR + offset];
- color[ColorIndexG] = Pointer[PixelformatIndexG + offset];
- color[ColorIndexB] = Pointer[PixelformatIndexB + offset];
- color[ColorIndexA] = Pointer[PixelformatIndexA + offset];
- }
-
- ///
- /// Set the color at the specified location from the specified array
- ///
- ///
- ///
- /// byte[4] as reference (r,g,b,a)
- public override void SetColorAt(int x, int y, byte[] color)
- {
- int offset = (x * 4) + (y * Stride);
- Pointer[PixelformatIndexR + offset] = color[ColorIndexR]; // R
- Pointer[PixelformatIndexG + offset] = color[ColorIndexG];
- Pointer[PixelformatIndexB + offset] = color[ColorIndexB];
- Pointer[PixelformatIndexA + offset] = color[ColorIndexA];
- }
-
- ///
- /// Retrieve the color, without alpha (is blended), at location x,y
- /// Before the first time this is called the Lock() should be called once!
- ///
- /// X coordinate
- /// Y Coordinate
- /// Color
- public Color GetBlendedColorAt(int x, int y)
- {
- int offset = (x * 4) + (y * Stride);
- int a = Pointer[PixelformatIndexA + offset];
- int red = Pointer[PixelformatIndexR + offset];
- int green = Pointer[PixelformatIndexG + offset];
- int blue = Pointer[PixelformatIndexB + offset];
-
- if (a < 255)
- {
- // As the request is to get without alpha, we blend.
- int rem = 255 - a;
- red = (red * a + BackgroundBlendColor.R * rem) / 255;
- green = (green * a + BackgroundBlendColor.G * rem) / 255;
- blue = (blue * a + BackgroundBlendColor.B * rem) / 255;
- }
-
- return Color.FromArgb(255, red, green, blue);
- }
- }
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+using System;
+using System.Collections.Generic;
+using System.Drawing;
+using System.Drawing.Imaging;
+
+namespace Greenshot.Base.Core
+{
+ ///
+ /// The interface for the FastBitmap
+ ///
+ public interface IFastBitmap : IDisposable
+ {
+ ///
+ /// Get the color at x,y
+ /// The returned Color object depends on the underlying pixel format
+ ///
+ /// int x
+ /// int y
+ /// Color
+ Color GetColorAt(int x, int y);
+
+ ///
+ /// Set the color at the specified location
+ ///
+ /// int x
+ /// int y
+ /// Color
+ void SetColorAt(int x, int y, Color color);
+
+ ///
+ /// Get the color at x,y
+ /// The returned byte[] color depends on the underlying pixel format
+ ///
+ /// int x
+ /// int y
+ /// byte array
+ void GetColorAt(int x, int y, byte[] color);
+
+ ///
+ /// Set the color at the specified location
+ ///
+ /// int x
+ /// int y
+ /// byte[] color
+ void SetColorAt(int x, int y, byte[] color);
+
+ ///
+ /// Lock the bitmap
+ ///
+ void Lock();
+
+ ///
+ /// Unlock the bitmap
+ ///
+ void Unlock();
+
+ ///
+ /// Unlock the bitmap and get the underlying bitmap in one call
+ ///
+ ///
+ Bitmap UnlockAndReturnBitmap();
+
+ ///
+ /// Size of the underlying image
+ ///
+ Size Size { get; }
+
+ ///
+ /// Height of the image area that this fastbitmap covers
+ ///
+ int Height { get; }
+
+ ///
+ /// Width of the image area that this fastbitmap covers
+ ///
+ int Width { get; }
+
+ ///
+ /// Top of the image area that this fastbitmap covers
+ ///
+ int Top { get; }
+
+ ///
+ /// Left of the image area that this fastbitmap covers
+ ///
+ int Left { get; }
+
+ ///
+ /// Right of the image area that this fastbitmap covers
+ ///
+ int Right { get; }
+
+ ///
+ /// Bottom of the image area that this fastbitmap covers
+ ///
+ int Bottom { get; }
+
+ ///
+ /// Does the underlying image need to be disposed
+ ///
+ bool NeedsDispose { get; set; }
+
+ ///
+ /// Returns if this FastBitmap has an alpha channel
+ ///
+ bool HasAlphaChannel { get; }
+
+ ///
+ /// Draw the stored bitmap to the destionation bitmap at the supplied point
+ ///
+ /// Graphics
+ /// Point with location
+ void DrawTo(Graphics graphics, Point destination);
+
+ ///
+ /// Draw the stored Bitmap on the Destination bitmap with the specified rectangle
+ /// Be aware that the stored bitmap will be resized to the specified rectangle!!
+ ///
+ /// Graphics
+ /// Rectangle with destination
+ void DrawTo(Graphics graphics, Rectangle destinationRect);
+
+ ///
+ /// Return true if the coordinates are inside the FastBitmap
+ ///
+ ///
+ ///
+ ///
+ bool Contains(int x, int y);
+
+ ///
+ /// Set the bitmap resolution
+ ///
+ ///
+ ///
+ void SetResolution(float horizontal, float vertical);
+ }
+
+ ///
+ /// This interface can be used for when offsetting is needed
+ ///
+ public interface IFastBitmapWithOffset : IFastBitmap
+ {
+ ///
+ /// Return true if the coordinates are inside the FastBitmap
+ ///
+ ///
+ ///
+ ///
+ new bool Contains(int x, int y);
+
+ ///
+ /// Set the color at the specified location, using offsetting so the original coordinates can be used
+ ///
+ /// int x
+ /// int y
+ /// Color color
+ new void SetColorAt(int x, int y, Color color);
+
+ ///
+ /// Set the color at the specified location, using offsetting so the original coordinates can be used
+ ///
+ /// int x
+ /// int y
+ /// byte[] color
+ new void SetColorAt(int x, int y, byte[] color);
+
+ ///
+ /// Get the color at x,y
+ /// The returned Color object depends on the underlying pixel format
+ ///
+ /// int x
+ /// int y
+ /// Color
+ new Color GetColorAt(int x, int y);
+
+ ///
+ /// Get the color at x,y, using offsetting so the original coordinates can be used
+ /// The returned byte[] color depends on the underlying pixel format
+ ///
+ /// int x
+ /// int y
+ /// byte array
+ new void GetColorAt(int x, int y, byte[] color);
+
+ new int Left { get; set; }
+
+ new int Top { get; set; }
+ }
+
+ ///
+ /// This interface can be used for when clipping is needed
+ ///
+ public interface IFastBitmapWithClip : IFastBitmap
+ {
+ Rectangle Clip { get; set; }
+
+ bool InvertClip { get; set; }
+
+ ///
+ /// Set the color at the specified location, this doesn't do anything if the location is excluded due to clipping
+ ///
+ /// int x
+ /// int y
+ /// Color color
+ new void SetColorAt(int x, int y, Color color);
+
+ ///
+ /// Set the color at the specified location, this doesn't do anything if the location is excluded due to clipping
+ ///
+ /// int x
+ /// int y
+ /// byte[] color
+ new void SetColorAt(int x, int y, byte[] color);
+
+ ///
+ /// Return true if the coordinates are inside the FastBitmap and not clipped
+ ///
+ ///
+ ///
+ ///
+ new bool Contains(int x, int y);
+ }
+
+ ///
+ /// This interface is implemented when there is a alpha-blending possibility
+ ///
+ public interface IFastBitmapWithBlend : IFastBitmap
+ {
+ Color BackgroundBlendColor { get; set; }
+ Color GetBlendedColorAt(int x, int y);
+ }
+
+ ///
+ /// The base class for the fast bitmap implementation
+ ///
+ public abstract unsafe class FastBitmap : IFastBitmapWithClip, IFastBitmapWithOffset
+ {
+ protected const int PixelformatIndexA = 3;
+ protected const int PixelformatIndexR = 2;
+ protected const int PixelformatIndexG = 1;
+ protected const int PixelformatIndexB = 0;
+
+ public const int ColorIndexR = 0;
+ public const int ColorIndexG = 1;
+ public const int ColorIndexB = 2;
+ public const int ColorIndexA = 3;
+
+ protected Rectangle Area;
+
+ ///
+ /// If this is set to true, the bitmap will be disposed when disposing the IFastBitmap
+ ///
+ public bool NeedsDispose { get; set; }
+
+ public Rectangle Clip { get; set; }
+
+ public bool InvertClip { get; set; }
+
+ ///
+ /// The bitmap for which the FastBitmap is creating access
+ ///
+ protected Bitmap Bitmap;
+
+ protected BitmapData BmData;
+ protected int Stride; /* bytes per pixel row */
+ protected bool BitsLocked;
+ protected byte* Pointer;
+
+ public static IFastBitmap Create(Bitmap source)
+ {
+ return Create(source, Rectangle.Empty);
+ }
+
+ public void SetResolution(float horizontal, float vertical)
+ {
+ Bitmap.SetResolution(horizontal, vertical);
+ }
+
+ ///
+ /// Factory for creating a FastBitmap depending on the pixelformat of the source
+ /// The supplied rectangle specifies the area for which the FastBitmap does its thing
+ ///
+ /// Bitmap to access
+ /// Rectangle which specifies the area to have access to, can be Rectangle.Empty for the whole image
+ /// IFastBitmap
+ public static IFastBitmap Create(Bitmap source, Rectangle area)
+ {
+ switch (source.PixelFormat)
+ {
+ case PixelFormat.Format8bppIndexed:
+ return new FastChunkyBitmap(source, area);
+ case PixelFormat.Format24bppRgb:
+ return new Fast24RgbBitmap(source, area);
+ case PixelFormat.Format32bppRgb:
+ return new Fast32RgbBitmap(source, area);
+ case PixelFormat.Format32bppArgb:
+ case PixelFormat.Format32bppPArgb:
+ return new Fast32ArgbBitmap(source, area);
+ default:
+ throw new NotSupportedException($"Not supported Pixelformat {source.PixelFormat}");
+ }
+ }
+
+ ///
+ /// Factory for creating a FastBitmap as a destination for the source
+ ///
+ /// Bitmap to clone
+ /// new Pixelformat
+ /// IFastBitmap
+ public static IFastBitmap CreateCloneOf(Image source, PixelFormat pixelFormat)
+ {
+ return CreateCloneOf(source, pixelFormat, Rectangle.Empty);
+ }
+
+ ///
+ /// Factory for creating a FastBitmap as a destination for the source
+ ///
+ /// Bitmap to clone
+ /// Area of the bitmap to access, can be Rectangle.Empty for the whole
+ /// IFastBitmap
+ public static IFastBitmap CreateCloneOf(Image source, Rectangle area)
+ {
+ return CreateCloneOf(source, PixelFormat.DontCare, area);
+ }
+
+ ///
+ /// Factory for creating a FastBitmap as a destination for the source
+ ///
+ /// Bitmap to clone
+ /// Pixelformat of the cloned bitmap
+ /// Area of the bitmap to access, can be Rectangle.Empty for the whole
+ /// IFastBitmap
+ public static IFastBitmap CreateCloneOf(Image source, PixelFormat pixelFormat, Rectangle area)
+ {
+ Bitmap destination = ImageHelper.CloneArea(source, area, pixelFormat);
+ FastBitmap fastBitmap = Create(destination) as FastBitmap;
+ if (fastBitmap != null)
+ {
+ fastBitmap.NeedsDispose = true;
+ fastBitmap.Left = area.Left;
+ fastBitmap.Top = area.Top;
+ }
+
+ return fastBitmap;
+ }
+
+ ///
+ /// Factory for creating a FastBitmap as a destination
+ ///
+ ///
+ ///
+ ///
+ /// IFastBitmap
+ public static IFastBitmap CreateEmpty(Size newSize, PixelFormat pixelFormat, Color backgroundColor)
+ {
+ Bitmap destination = ImageHelper.CreateEmpty(newSize.Width, newSize.Height, pixelFormat, backgroundColor, 96f, 96f);
+ IFastBitmap fastBitmap = Create(destination);
+ fastBitmap.NeedsDispose = true;
+ return fastBitmap;
+ }
+
+ ///
+ /// Constructor which stores the image and locks it when called
+ ///
+ /// Bitmap
+ /// Rectangle
+ protected FastBitmap(Bitmap bitmap, Rectangle area)
+ {
+ Bitmap = bitmap;
+ Rectangle bitmapArea = new Rectangle(Point.Empty, bitmap.Size);
+ if (area != Rectangle.Empty)
+ {
+ area.Intersect(bitmapArea);
+ Area = area;
+ }
+ else
+ {
+ Area = bitmapArea;
+ }
+
+ // As the lock takes care that only the specified area is made available we need to calculate the offset
+ Left = area.Left;
+ Top = area.Top;
+ // Default cliping is done to the area without invert
+ Clip = Area;
+ InvertClip = false;
+ // Always lock, so we don't need to do this ourselves
+ Lock();
+ }
+
+ ///
+ /// Return the size of the image
+ ///
+ public Size Size
+ {
+ get
+ {
+ if (Area == Rectangle.Empty)
+ {
+ return Bitmap.Size;
+ }
+
+ return Area.Size;
+ }
+ }
+
+ ///
+ /// Return the width of the image
+ ///
+ public int Width
+ {
+ get
+ {
+ if (Area == Rectangle.Empty)
+ {
+ return Bitmap.Width;
+ }
+
+ return Area.Width;
+ }
+ }
+
+ ///
+ /// Return the height of the image
+ ///
+ public int Height
+ {
+ get
+ {
+ if (Area == Rectangle.Empty)
+ {
+ return Bitmap.Height;
+ }
+
+ return Area.Height;
+ }
+ }
+
+ private int _left;
+
+ ///
+ /// Return the left of the fastbitmap, this is also used as an offset
+ ///
+ public int Left
+ {
+ get { return 0; }
+ set { _left = value; }
+ }
+
+ ///
+ /// Return the left of the fastbitmap, this is also used as an offset
+ ///
+ int IFastBitmapWithOffset.Left
+ {
+ get { return _left; }
+ set { _left = value; }
+ }
+
+ private int _top;
+
+ ///
+ /// Return the top of the fastbitmap, this is also used as an offset
+ ///
+ public int Top
+ {
+ get { return 0; }
+ set { _top = value; }
+ }
+
+ ///
+ /// Return the top of the fastbitmap, this is also used as an offset
+ ///
+ int IFastBitmapWithOffset.Top
+ {
+ get { return _top; }
+ set { _top = value; }
+ }
+
+ ///
+ /// Return the right of the fastbitmap
+ ///
+ public int Right => Left + Width;
+
+ ///
+ /// Return the bottom of the fastbitmap
+ ///
+ public int Bottom => Top + Height;
+
+ ///
+ /// Returns the underlying bitmap, unlocks it and prevents that it will be disposed
+ ///
+ public Bitmap UnlockAndReturnBitmap()
+ {
+ if (BitsLocked)
+ {
+ Unlock();
+ }
+
+ NeedsDispose = false;
+ return Bitmap;
+ }
+
+ public virtual bool HasAlphaChannel => false;
+
+ ///
+ /// Destructor
+ ///
+ ~FastBitmap()
+ {
+ Dispose(false);
+ }
+
+ ///
+ /// The public accessible Dispose
+ /// Will call the GarbageCollector to SuppressFinalize, preventing being cleaned twice
+ ///
+ public void Dispose()
+ {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+
+ // 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 virtual void Dispose(bool disposing)
+ {
+ Unlock();
+ if (disposing)
+ {
+ if (Bitmap != null && NeedsDispose)
+ {
+ Bitmap.Dispose();
+ }
+ }
+
+ Bitmap = null;
+ BmData = null;
+ Pointer = null;
+ }
+
+ ///
+ /// Lock the bitmap so we have direct access to the memory
+ ///
+ public void Lock()
+ {
+ if (Width <= 0 || Height <= 0 || BitsLocked)
+ {
+ return;
+ }
+
+ BmData = Bitmap.LockBits(Area, ImageLockMode.ReadWrite, Bitmap.PixelFormat);
+ BitsLocked = true;
+
+ IntPtr scan0 = BmData.Scan0;
+ Pointer = (byte*) (void*) scan0;
+ Stride = BmData.Stride;
+ }
+
+ ///
+ /// Unlock the System Memory
+ ///
+ public void Unlock()
+ {
+ if (BitsLocked)
+ {
+ Bitmap.UnlockBits(BmData);
+ BitsLocked = false;
+ }
+ }
+
+ ///
+ /// Draw the stored bitmap to the destionation bitmap at the supplied point
+ ///
+ ///
+ ///
+ public void DrawTo(Graphics graphics, Point destination)
+ {
+ DrawTo(graphics, new Rectangle(destination, Area.Size));
+ }
+
+ ///
+ /// Draw the stored Bitmap on the Destination bitmap with the specified rectangle
+ /// Be aware that the stored bitmap will be resized to the specified rectangle!!
+ ///
+ ///
+ ///
+ public void DrawTo(Graphics graphics, Rectangle destinationRect)
+ {
+ // Make sure this.bitmap is unlocked, if it was locked
+ bool isLocked = BitsLocked;
+ if (isLocked)
+ {
+ Unlock();
+ }
+
+ graphics.DrawImage(Bitmap, destinationRect, Area, GraphicsUnit.Pixel);
+ }
+
+ ///
+ /// returns true if x & y are inside the FastBitmap
+ ///
+ ///
+ ///
+ /// true if x & y are inside the FastBitmap
+ public bool Contains(int x, int y)
+ {
+ return Area.Contains(x - Left, y - Top);
+ }
+
+ public abstract Color GetColorAt(int x, int y);
+ public abstract void SetColorAt(int x, int y, Color color);
+ public abstract void GetColorAt(int x, int y, byte[] color);
+ public abstract void SetColorAt(int x, int y, byte[] color);
+
+ bool IFastBitmapWithClip.Contains(int x, int y)
+ {
+ bool contains = Clip.Contains(x, y);
+ if (InvertClip)
+ {
+ return !contains;
+ }
+ else
+ {
+ return contains;
+ }
+ }
+
+ void IFastBitmapWithClip.SetColorAt(int x, int y, byte[] color)
+ {
+ bool contains = Clip.Contains(x, y);
+ if ((InvertClip && contains) || (!InvertClip && !contains))
+ {
+ return;
+ }
+
+ SetColorAt(x, y, color);
+ }
+
+ void IFastBitmapWithClip.SetColorAt(int x, int y, Color color)
+ {
+ bool contains = Clip.Contains(x, y);
+ if ((InvertClip && contains) || (!InvertClip && !contains))
+ {
+ return;
+ }
+
+ SetColorAt(x, y, color);
+ }
+
+ ///
+ /// returns true if x & y are inside the FastBitmap
+ ///
+ ///
+ ///
+ /// true if x & y are inside the FastBitmap
+ bool IFastBitmapWithOffset.Contains(int x, int y)
+ {
+ return Area.Contains(x - Left, y - Top);
+ }
+
+ Color IFastBitmapWithOffset.GetColorAt(int x, int y)
+ {
+ x -= _left;
+ y -= _top;
+ return GetColorAt(x, y);
+ }
+
+ void IFastBitmapWithOffset.GetColorAt(int x, int y, byte[] color)
+ {
+ x -= _left;
+ y -= _top;
+ GetColorAt(x, y, color);
+ }
+
+ void IFastBitmapWithOffset.SetColorAt(int x, int y, byte[] color)
+ {
+ x -= _left;
+ y -= _top;
+ SetColorAt(x, y, color);
+ }
+
+ void IFastBitmapWithOffset.SetColorAt(int x, int y, Color color)
+ {
+ x -= _left;
+ y -= _top;
+ SetColorAt(x, y, color);
+ }
+ }
+
+ ///
+ /// This is the implementation of the FastBitmat for the 8BPP pixelformat
+ ///
+ public unsafe class FastChunkyBitmap : FastBitmap
+ {
+ // Used for indexed images
+ private readonly Color[] _colorEntries;
+ private readonly Dictionary _colorCache = new Dictionary();
+
+ public FastChunkyBitmap(Bitmap source, Rectangle area) : base(source, area)
+ {
+ _colorEntries = Bitmap.Palette.Entries;
+ }
+
+ ///
+ /// Get the color from the specified location
+ ///
+ ///
+ ///
+ /// Color
+ public override Color GetColorAt(int x, int y)
+ {
+ int offset = x + (y * Stride);
+ byte colorIndex = Pointer[offset];
+ return _colorEntries[colorIndex];
+ }
+
+ ///
+ /// Get the color from the specified location into the specified array
+ ///
+ ///
+ ///
+ /// byte[4] as reference
+ public override void GetColorAt(int x, int y, byte[] color)
+ {
+ throw new NotImplementedException("No performance gain!");
+ }
+
+ ///
+ /// Set the color at the specified location from the specified array
+ ///
+ ///
+ ///
+ /// byte[4] as reference
+ public override void SetColorAt(int x, int y, byte[] color)
+ {
+ throw new NotImplementedException("No performance gain!");
+ }
+
+ ///
+ /// Get the color-index from the specified location
+ ///
+ ///
+ ///
+ /// byte with index
+ public byte GetColorIndexAt(int x, int y)
+ {
+ int offset = x + (y * Stride);
+ return Pointer[offset];
+ }
+
+ ///
+ /// Set the color-index at the specified location
+ ///
+ ///
+ ///
+ ///
+ public void SetColorIndexAt(int x, int y, byte colorIndex)
+ {
+ int offset = x + (y * Stride);
+ Pointer[offset] = colorIndex;
+ }
+
+ ///
+ /// Set the supplied color at the specified location.
+ /// Throws an ArgumentException if the color is not in the palette
+ ///
+ ///
+ ///
+ /// Color to set
+ public override void SetColorAt(int x, int y, Color color)
+ {
+ int offset = x + (y * Stride);
+ if (!_colorCache.TryGetValue(color, out var colorIndex))
+ {
+ bool foundColor = false;
+ for (colorIndex = 0; colorIndex < _colorEntries.Length; colorIndex++)
+ {
+ if (color == _colorEntries[colorIndex])
+ {
+ _colorCache.Add(color, colorIndex);
+ foundColor = true;
+ break;
+ }
+ }
+
+ if (!foundColor)
+ {
+ throw new ArgumentException("No such color!");
+ }
+ }
+
+ Pointer[offset] = colorIndex;
+ }
+ }
+
+ ///
+ /// This is the implementation of the IFastBitmap for 24 bit images (no Alpha)
+ ///
+ public unsafe class Fast24RgbBitmap : FastBitmap
+ {
+ public Fast24RgbBitmap(Bitmap source, Rectangle area) : base(source, area)
+ {
+ }
+
+ ///
+ /// Retrieve the color at location x,y
+ /// Before the first time this is called the Lock() should be called once!
+ ///
+ /// X coordinate
+ /// Y Coordinate
+ /// Color
+ public override Color GetColorAt(int x, int y)
+ {
+ int offset = (x * 3) + (y * Stride);
+ return Color.FromArgb(255, Pointer[PixelformatIndexR + offset], Pointer[PixelformatIndexG + offset], Pointer[PixelformatIndexB + offset]);
+ }
+
+ ///
+ /// Set the color at location x,y
+ /// Before the first time this is called the Lock() should be called once!
+ ///
+ ///
+ ///
+ ///
+ public override void SetColorAt(int x, int y, Color color)
+ {
+ int offset = (x * 3) + (y * Stride);
+ Pointer[PixelformatIndexR + offset] = color.R;
+ Pointer[PixelformatIndexG + offset] = color.G;
+ Pointer[PixelformatIndexB + offset] = color.B;
+ }
+
+ ///
+ /// Get the color from the specified location into the specified array
+ ///
+ ///
+ ///
+ /// byte[4] as reference (r,g,b)
+ public override void GetColorAt(int x, int y, byte[] color)
+ {
+ int offset = (x * 3) + (y * Stride);
+ color[PixelformatIndexR] = Pointer[PixelformatIndexR + offset];
+ color[PixelformatIndexG] = Pointer[PixelformatIndexG + offset];
+ color[PixelformatIndexB] = Pointer[PixelformatIndexB + offset];
+ }
+
+ ///
+ /// Set the color at the specified location from the specified array
+ ///
+ ///
+ ///
+ /// byte[4] as reference (r,g,b)
+ public override void SetColorAt(int x, int y, byte[] color)
+ {
+ int offset = (x * 3) + (y * Stride);
+ Pointer[PixelformatIndexR + offset] = color[PixelformatIndexR];
+ Pointer[PixelformatIndexG + offset] = color[PixelformatIndexG];
+ Pointer[PixelformatIndexB + offset] = color[PixelformatIndexB];
+ }
+ }
+
+ ///
+ /// This is the implementation of the IFastBitmap for 32 bit images (no Alpha)
+ ///
+ public unsafe class Fast32RgbBitmap : FastBitmap
+ {
+ public Fast32RgbBitmap(Bitmap source, Rectangle area) : base(source, area)
+ {
+ }
+
+ ///
+ /// Retrieve the color at location x,y
+ /// Before the first time this is called the Lock() should be called once!
+ ///
+ /// X coordinate
+ /// Y Coordinate
+ /// Color
+ public override Color GetColorAt(int x, int y)
+ {
+ int offset = (x * 4) + (y * Stride);
+ return Color.FromArgb(255, Pointer[PixelformatIndexR + offset], Pointer[PixelformatIndexG + offset], Pointer[PixelformatIndexB + offset]);
+ }
+
+ ///
+ /// Set the color at location x,y
+ /// Before the first time this is called the Lock() should be called once!
+ ///
+ ///
+ ///
+ ///
+ public override void SetColorAt(int x, int y, Color color)
+ {
+ int offset = (x * 4) + (y * Stride);
+ Pointer[PixelformatIndexR + offset] = color.R;
+ Pointer[PixelformatIndexG + offset] = color.G;
+ Pointer[PixelformatIndexB + offset] = color.B;
+ }
+
+ ///
+ /// Get the color from the specified location into the specified array
+ ///
+ ///
+ ///
+ /// byte[4] as reference (a,r,g,b)
+ public override void GetColorAt(int x, int y, byte[] color)
+ {
+ int offset = (x * 4) + (y * Stride);
+ color[ColorIndexR] = Pointer[PixelformatIndexR + offset];
+ color[ColorIndexG] = Pointer[PixelformatIndexG + offset];
+ color[ColorIndexB] = Pointer[PixelformatIndexB + offset];
+ }
+
+ ///
+ /// Set the color at the specified location from the specified array
+ ///
+ ///
+ ///
+ /// byte[4] as reference (r,g,b)
+ public override void SetColorAt(int x, int y, byte[] color)
+ {
+ int offset = (x * 4) + (y * Stride);
+ Pointer[PixelformatIndexR + offset] = color[ColorIndexR]; // R
+ Pointer[PixelformatIndexG + offset] = color[ColorIndexG];
+ Pointer[PixelformatIndexB + offset] = color[ColorIndexB];
+ }
+ }
+
+ ///
+ /// This is the implementation of the IFastBitmap for 32 bit images with Alpha
+ ///
+ public unsafe class Fast32ArgbBitmap : FastBitmap, IFastBitmapWithBlend
+ {
+ public override bool HasAlphaChannel => true;
+
+ public Color BackgroundBlendColor { get; set; }
+
+ public Fast32ArgbBitmap(Bitmap source, Rectangle area) : base(source, area)
+ {
+ BackgroundBlendColor = Color.White;
+ }
+
+ ///
+ /// Retrieve the color at location x,y
+ ///
+ /// X coordinate
+ /// Y Coordinate
+ /// Color
+ public override Color GetColorAt(int x, int y)
+ {
+ int offset = (x * 4) + (y * Stride);
+ return Color.FromArgb(Pointer[PixelformatIndexA + offset], Pointer[PixelformatIndexR + offset], Pointer[PixelformatIndexG + offset],
+ Pointer[PixelformatIndexB + offset]);
+ }
+
+ ///
+ /// Set the color at location x,y
+ /// Before the first time this is called the Lock() should be called once!
+ ///
+ ///
+ ///
+ ///
+ public override void SetColorAt(int x, int y, Color color)
+ {
+ int offset = (x * 4) + (y * Stride);
+ Pointer[PixelformatIndexA + offset] = color.A;
+ Pointer[PixelformatIndexR + offset] = color.R;
+ Pointer[PixelformatIndexG + offset] = color.G;
+ Pointer[PixelformatIndexB + offset] = color.B;
+ }
+
+ ///
+ /// Get the color from the specified location into the specified array
+ ///
+ ///
+ ///
+ /// byte[4] as reference (r,g,b,a)
+ public override void GetColorAt(int x, int y, byte[] color)
+ {
+ int offset = (x * 4) + (y * Stride);
+ color[ColorIndexR] = Pointer[PixelformatIndexR + offset];
+ color[ColorIndexG] = Pointer[PixelformatIndexG + offset];
+ color[ColorIndexB] = Pointer[PixelformatIndexB + offset];
+ color[ColorIndexA] = Pointer[PixelformatIndexA + offset];
+ }
+
+ ///
+ /// Set the color at the specified location from the specified array
+ ///
+ ///
+ ///
+ /// byte[4] as reference (r,g,b,a)
+ public override void SetColorAt(int x, int y, byte[] color)
+ {
+ int offset = (x * 4) + (y * Stride);
+ Pointer[PixelformatIndexR + offset] = color[ColorIndexR]; // R
+ Pointer[PixelformatIndexG + offset] = color[ColorIndexG];
+ Pointer[PixelformatIndexB + offset] = color[ColorIndexB];
+ Pointer[PixelformatIndexA + offset] = color[ColorIndexA];
+ }
+
+ ///
+ /// Retrieve the color, without alpha (is blended), at location x,y
+ /// Before the first time this is called the Lock() should be called once!
+ ///
+ /// X coordinate
+ /// Y Coordinate
+ /// Color
+ public Color GetBlendedColorAt(int x, int y)
+ {
+ int offset = (x * 4) + (y * Stride);
+ int a = Pointer[PixelformatIndexA + offset];
+ int red = Pointer[PixelformatIndexR + offset];
+ int green = Pointer[PixelformatIndexG + offset];
+ int blue = Pointer[PixelformatIndexB + offset];
+
+ if (a < 255)
+ {
+ // As the request is to get without alpha, we blend.
+ int rem = 255 - a;
+ red = (red * a + BackgroundBlendColor.R * rem) / 255;
+ green = (green * a + BackgroundBlendColor.G * rem) / 255;
+ blue = (blue * a + BackgroundBlendColor.B * rem) / 255;
+ }
+
+ return Color.FromArgb(255, red, green, blue);
+ }
+ }
}
\ No newline at end of file
diff --git a/src/GreenshotPlugin/Core/FilenameHelper.cs b/src/Greenshot.Base/Core/FilenameHelper.cs
similarity index 97%
rename from src/GreenshotPlugin/Core/FilenameHelper.cs
rename to src/Greenshot.Base/Core/FilenameHelper.cs
index 6d8073843..83a76f438 100644
--- a/src/GreenshotPlugin/Core/FilenameHelper.cs
+++ b/src/Greenshot.Base/Core/FilenameHelper.cs
@@ -1,705 +1,705 @@
-/*
- * Greenshot - a free and open source screenshot tool
- * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
- *
- * For more information see: http://getgreenshot.org/
- * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 1 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-using System;
-using System.Collections;
-using System.IO;
-using System.Text.RegularExpressions;
-using System.Windows.Forms;
-using log4net;
-using System.Collections.Generic;
-using GreenshotPlugin.IniFile;
-using GreenshotPlugin.Interfaces;
-
-namespace GreenshotPlugin.Core
-{
- public static class FilenameHelper
- {
- private static readonly ILog Log = LogManager.GetLogger(typeof(FilenameHelper));
-
- // Specify the regular expression for the filename formatting:
- // Starting with ${
- // than the varname, which ends with a : or }
- // If a parameters needs to be supplied, than a ":" should follow the name... everything from the : until the } is considered to be part of the parameters.
- // The parameter format is a single alpha followed by the value belonging to the parameter, e.g. :
- // ${capturetime:d"yyyy-MM-dd HH_mm_ss"}
- private static readonly Regex VarRegexp = new Regex(@"\${(?[^:}]+)[:]?(?[^}]*)}", RegexOptions.Compiled);
- private static readonly Regex CmdVarRegexp = new Regex(@"%(?[^%]+)%", RegexOptions.Compiled);
-
- private static readonly Regex SplitRegexp = new Regex(";(?=(?:[^\"]*\"[^\"]*\")*[^\"]*$)", RegexOptions.Compiled);
- private const int MaxTitleLength = 80;
- private static readonly CoreConfiguration CoreConfig = IniConfig.GetIniSection();
- private const string UnsafeReplacement = "_";
- private static readonly Random RandomNumberGen = new Random();
- private static readonly Regex RandRegexp = new Regex("^R+$", RegexOptions.Compiled);
-
- ///
- /// Remove invalid characters from the fully qualified filename
- ///
- /// string with the full path to a file
- /// string with the full path to a file, without invalid characters
- public static string MakeFqFilenameSafe(string fullPath)
- {
- string path = MakePathSafe(Path.GetDirectoryName(fullPath));
- string filename = MakeFilenameSafe(Path.GetFileName(fullPath));
- // Make the fullpath again and return
- return Path.Combine(path, filename);
- }
-
- ///
- /// Remove invalid characters from the filename
- ///
- /// string with the full path to a file
- /// string with the full path to a file, without invalid characters
- public static string MakeFilenameSafe(string filename)
- {
- // Make the filename save!
- if (filename != null)
- {
- foreach (char disallowed in Path.GetInvalidFileNameChars())
- {
- filename = filename.Replace(disallowed.ToString(), UnsafeReplacement);
- }
- }
-
- return filename;
- }
-
- ///
- /// Remove invalid characters from the path
- ///
- /// string with the full path to a file
- /// string with the full path to a file, without invalid characters
- public static string MakePathSafe(string path)
- {
- // Make the path save!
- if (path != null)
- {
- foreach (char disallowed in Path.GetInvalidPathChars())
- {
- path = path.Replace(disallowed.ToString(), UnsafeReplacement);
- }
- }
-
- return path;
- }
-
- public static string GetFilenameWithoutExtensionFromPattern(string pattern)
- {
- return GetFilenameWithoutExtensionFromPattern(pattern, null);
- }
-
- public static string GetFilenameWithoutExtensionFromPattern(string pattern, ICaptureDetails captureDetails)
- {
- return FillPattern(pattern, captureDetails, true);
- }
-
- public static string GetFilenameFromPattern(string pattern, OutputFormat imageFormat)
- {
- return GetFilenameFromPattern(pattern, imageFormat, null);
- }
-
- public static string GetFilenameFromPattern(string pattern, OutputFormat imageFormat, ICaptureDetails captureDetails)
- {
- return FillPattern(pattern, captureDetails, true) + "." + imageFormat.ToString().ToLower();
- }
-
- ///
- /// Return a filename for the current image format (png,jpg etc) with the default file pattern
- /// that is specified in the configuration
- ///
- /// A string with the format
- ///
- /// The filename which should be used to save the image
- public static string GetFilename(OutputFormat format, ICaptureDetails captureDetails)
- {
- string pattern = CoreConfig.OutputFileFilenamePattern;
- if (string.IsNullOrEmpty(pattern?.Trim()))
- {
- pattern = "greenshot ${capturetime}";
- }
-
- return GetFilenameFromPattern(pattern, format, captureDetails);
- }
-
-
- ///
- /// This method will be called by the regexp.replace as a MatchEvaluator delegate!
- /// Will delegate this to the MatchVarEvaluatorInternal and catch any exceptions
- ///
- /// What are we matching?
- /// The detail, can be null
- /// Variables from the process
- /// Variables from the user
- /// Variables from the machine
- ///
- /// string with the match replacement
- private static string MatchVarEvaluator(Match match, ICaptureDetails captureDetails, IDictionary processVars, IDictionary userVars, IDictionary machineVars,
- bool filenameSafeMode)
- {
- try
- {
- return MatchVarEvaluatorInternal(match, captureDetails, processVars, userVars, machineVars, filenameSafeMode);
- }
- catch (Exception e)
- {
- Log.Error("Error in MatchVarEvaluatorInternal", e);
- }
-
- return string.Empty;
- }
-
- ///
- /// This method will be called by the regexp.replace as a MatchEvaluator delegate!
- ///
- /// What are we matching?
- /// The detail, can be null
- ///
- ///
- ///
- ///
- ///
- private static string MatchVarEvaluatorInternal(Match match, ICaptureDetails captureDetails, IDictionary processVars, IDictionary userVars, IDictionary machineVars,
- bool filenameSafeMode)
- {
- // some defaults
- int padWidth = 0;
- int startIndex = 0;
- int endIndex = 0;
- char padChar = ' ';
- string dateFormat = "yyyy-MM-dd HH-mm-ss";
- IDictionary replacements = new Dictionary();
- string replaceValue = string.Empty;
- string variable = match.Groups["variable"].Value;
- string parameters = match.Groups["parameters"].Value;
- string randomChars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
-
- if (parameters.Length > 0)
- {
- string[] parms = SplitRegexp.Split(parameters);
- foreach (string parameter in parms)
- {
- switch (parameter.Substring(0, 1))
- {
- // Padding p[,pad-character]
- case "p":
- string[] padParams = parameter.Substring(1).Split(',');
- try
- {
- padWidth = int.Parse(padParams[0]);
- }
- catch
- {
- // ignored
- }
-
- if (padParams.Length > 1)
- {
- padChar = padParams[1][0];
- }
-
- break;
- // replace
- // r,
- case "r":
- string[] replaceParameters = parameter.Substring(1).Split(',');
- if (replaceParameters != null && replaceParameters.Length == 2)
- {
- replacements.Add(replaceParameters[0], replaceParameters[1]);
- }
-
- break;
- // Dateformat d
- // Format can be anything that is used in C# date formatting
- case "d":
- dateFormat = parameter.Substring(1);
- if (dateFormat.StartsWith("\""))
- {
- dateFormat = dateFormat.Substring(1);
- }
-
- if (dateFormat.EndsWith("\""))
- {
- dateFormat = dateFormat.Substring(0, dateFormat.Length - 1);
- }
-
- break;
- // Substring:
- // s[,length]
- case "s":
- string range = parameter.Substring(1);
- string[] rangelist = range.Split(',');
- if (rangelist.Length > 0)
- {
- try
- {
- startIndex = int.Parse(rangelist[0]);
- }
- catch
- {
- // Ignore
- }
- }
-
- if (rangelist.Length > 1)
- {
- try
- {
- endIndex = int.Parse(rangelist[1]);
- }
- catch
- {
- // Ignore
- }
- }
-
- break;
- }
- }
- }
-
- if (processVars != null && processVars.Contains(variable))
- {
- replaceValue = (string) processVars[variable];
- if (filenameSafeMode)
- {
- replaceValue = MakePathSafe(replaceValue);
- }
- }
- else if (userVars != null && userVars.Contains(variable))
- {
- replaceValue = (string) userVars[variable];
- if (filenameSafeMode)
- {
- replaceValue = MakePathSafe(replaceValue);
- }
- }
- else if (machineVars != null && machineVars.Contains(variable))
- {
- replaceValue = (string) machineVars[variable];
- if (filenameSafeMode)
- {
- replaceValue = MakePathSafe(replaceValue);
- }
- }
- else if (captureDetails?.MetaData != null && captureDetails.MetaData.ContainsKey(variable))
- {
- replaceValue = captureDetails.MetaData[variable];
- if (filenameSafeMode)
- {
- replaceValue = MakePathSafe(replaceValue);
- }
- }
- else if (RandRegexp.IsMatch(variable))
- {
- for (int i = 0; i < variable.Length; i++)
- {
- replaceValue += randomChars[RandomNumberGen.Next(randomChars.Length)];
- }
- }
- else
- {
- // Handle other variables
- // Default use "now" for the capture take´n
- DateTime capturetime = DateTime.Now;
- // Use default application name for title
- string title = Application.ProductName;
-
- // Check if we have capture details
- if (captureDetails != null)
- {
- capturetime = captureDetails.DateTime;
- if (captureDetails.Title != null)
- {
- title = captureDetails.Title;
- if (title.Length > MaxTitleLength)
- {
- title = title.Substring(0, MaxTitleLength);
- }
- }
- }
-
- switch (variable)
- {
- case "domain":
- replaceValue = Environment.UserDomainName;
- break;
- case "user":
- replaceValue = Environment.UserName;
- break;
- case "hostname":
- replaceValue = Environment.MachineName;
- break;
- case "YYYY":
- if (padWidth == 0)
- {
- padWidth = -4;
- padChar = '0';
- }
-
- replaceValue = capturetime.Year.ToString();
- break;
- case "MM":
- replaceValue = capturetime.Month.ToString();
- if (padWidth == 0)
- {
- padWidth = -2;
- padChar = '0';
- }
-
- break;
- case "DD":
- replaceValue = capturetime.Day.ToString();
- if (padWidth == 0)
- {
- padWidth = -2;
- padChar = '0';
- }
-
- break;
- case "hh":
- if (padWidth == 0)
- {
- padWidth = -2;
- padChar = '0';
- }
-
- replaceValue = capturetime.Hour.ToString();
- break;
- case "mm":
- if (padWidth == 0)
- {
- padWidth = -2;
- padChar = '0';
- }
-
- replaceValue = capturetime.Minute.ToString();
- break;
- case "ss":
- if (padWidth == 0)
- {
- padWidth = -2;
- padChar = '0';
- }
-
- replaceValue = capturetime.Second.ToString();
- break;
- case "now":
- replaceValue = DateTime.Now.ToString(dateFormat);
- if (filenameSafeMode)
- {
- replaceValue = MakeFilenameSafe(replaceValue);
- }
-
- break;
- case "capturetime":
- replaceValue = capturetime.ToString(dateFormat);
- if (filenameSafeMode)
- {
- replaceValue = MakeFilenameSafe(replaceValue);
- }
-
- break;
- case "NUM":
- CoreConfig.OutputFileIncrementingNumber++;
- IniConfig.Save();
- replaceValue = CoreConfig.OutputFileIncrementingNumber.ToString();
- if (padWidth == 0)
- {
- padWidth = -6;
- padChar = '0';
- }
-
- break;
- case "title":
- replaceValue = title;
- if (filenameSafeMode)
- {
- replaceValue = MakeFilenameSafe(replaceValue);
- }
-
- break;
- case "MyPictures":
- replaceValue = Environment.GetFolderPath(Environment.SpecialFolder.MyPictures);
- break;
- case "MyMusic":
- replaceValue = Environment.GetFolderPath(Environment.SpecialFolder.MyMusic);
- break;
- case "MyDocuments":
- replaceValue = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
- break;
- case "Personal":
- replaceValue = Environment.GetFolderPath(Environment.SpecialFolder.Personal);
- break;
- case "Desktop":
- replaceValue = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
- break;
- case "ApplicationData":
- replaceValue = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
- break;
- case "LocalApplicationData":
- replaceValue = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
- break;
- }
- }
-
- // do padding
- if (padWidth > 0)
- {
- replaceValue = replaceValue.PadRight(padWidth, padChar);
- }
- else if (padWidth < 0)
- {
- replaceValue = replaceValue.PadLeft(-padWidth, padChar);
- }
-
- // do substring
- if (startIndex != 0 || endIndex != 0)
- {
- if (startIndex < 0)
- {
- startIndex = replaceValue.Length + startIndex;
- }
-
- if (endIndex < 0)
- {
- endIndex = replaceValue.Length + endIndex;
- }
-
- if (endIndex != 0)
- {
- try
- {
- replaceValue = replaceValue.Substring(startIndex, endIndex);
- }
- catch
- {
- // Ignore
- }
- }
- else
- {
- try
- {
- replaceValue = replaceValue.Substring(startIndex);
- }
- catch
- {
- // Ignore
- }
- }
- }
-
- // new for feature #697
- if (replacements.Count > 0)
- {
- foreach (string oldValue in replacements.Keys)
- {
- replaceValue = replaceValue.Replace(oldValue, replacements[oldValue]);
- }
- }
-
- return replaceValue;
- }
-
- ///
- /// "Simply" fill the pattern with environment variables
- ///
- /// String with pattern %var%
- /// true to make sure everything is filenamesafe
- /// Filled string
- public static string FillCmdVariables(string pattern, bool filenameSafeMode = true)
- {
- IDictionary processVars = null;
- IDictionary userVars = null;
- IDictionary machineVars = null;
- try
- {
- processVars = Environment.GetEnvironmentVariables(EnvironmentVariableTarget.Process);
- }
- catch (Exception e)
- {
- Log.Error("Error retrieving EnvironmentVariableTarget.Process", e);
- }
-
- try
- {
- userVars = Environment.GetEnvironmentVariables(EnvironmentVariableTarget.User);
- }
- catch (Exception e)
- {
- Log.Error("Error retrieving EnvironmentVariableTarget.User", e);
- }
-
- try
- {
- machineVars = Environment.GetEnvironmentVariables(EnvironmentVariableTarget.Machine);
- }
- catch (Exception e)
- {
- Log.Error("Error retrieving EnvironmentVariableTarget.Machine", e);
- }
-
- return CmdVarRegexp.Replace(pattern,
- m => MatchVarEvaluator(m, null, processVars, userVars, machineVars, filenameSafeMode)
- );
- }
-
- ///
- /// "Simply" fill the pattern with environment variables
- ///
- /// String with pattern ${var}
- /// true to make sure everything is filenamesafe
- /// Filled string
- public static string FillVariables(string pattern, bool filenameSafeMode)
- {
- IDictionary processVars = null;
- IDictionary userVars = null;
- IDictionary machineVars = null;
- try
- {
- processVars = Environment.GetEnvironmentVariables(EnvironmentVariableTarget.Process);
- }
- catch (Exception e)
- {
- Log.Error("Error retrieving EnvironmentVariableTarget.Process", e);
- }
-
- try
- {
- userVars = Environment.GetEnvironmentVariables(EnvironmentVariableTarget.User);
- }
- catch (Exception e)
- {
- Log.Error("Error retrieving EnvironmentVariableTarget.User", e);
- }
-
- try
- {
- machineVars = Environment.GetEnvironmentVariables(EnvironmentVariableTarget.Machine);
- }
- catch (Exception e)
- {
- Log.Error("Error retrieving EnvironmentVariableTarget.Machine", e);
- }
-
- return VarRegexp.Replace(pattern,
- m => MatchVarEvaluator(m, null, processVars, userVars, machineVars, filenameSafeMode)
- );
- }
-
- ///
- /// Fill the pattern wit the supplied details
- ///
- /// Pattern
- /// CaptureDetails, can be null
- /// Should the result be made "filename" safe?
- /// Filled pattern
- public static string FillPattern(string pattern, ICaptureDetails captureDetails, bool filenameSafeMode)
- {
- IDictionary processVars = null;
- IDictionary userVars = null;
- IDictionary machineVars = null;
- try
- {
- processVars = Environment.GetEnvironmentVariables(EnvironmentVariableTarget.Process);
- }
- catch (Exception e)
- {
- Log.Error("Error retrieving EnvironmentVariableTarget.Process", e);
- }
-
- try
- {
- userVars = Environment.GetEnvironmentVariables(EnvironmentVariableTarget.User);
- }
- catch (Exception e)
- {
- Log.Error("Error retrieving EnvironmentVariableTarget.User", e);
- }
-
- try
- {
- machineVars = Environment.GetEnvironmentVariables(EnvironmentVariableTarget.Machine);
- }
- catch (Exception e)
- {
- Log.Error("Error retrieving EnvironmentVariableTarget.Machine", e);
- }
-
- try
- {
- return VarRegexp.Replace(pattern,
- m => MatchVarEvaluator(m, captureDetails, processVars, userVars, machineVars, filenameSafeMode)
- );
- }
- catch (Exception e)
- {
- // adding additional data for bug tracking
- if (captureDetails != null)
- {
- e.Data.Add("title", captureDetails.Title);
- }
-
- e.Data.Add("pattern", pattern);
- throw;
- }
- }
-
- ///
- /// Checks whether a directory name is valid in the current file system
- ///
- /// directory name (not path!)
- /// true if directory name is valid
- public static bool IsDirectoryNameValid(string directoryName)
- {
- var forbiddenChars = Path.GetInvalidPathChars();
- foreach (var forbiddenChar in forbiddenChars)
- {
- if (directoryName == null || directoryName.Contains(forbiddenChar.ToString()))
- {
- return false;
- }
- }
-
- return true;
- }
-
- ///
- /// Checks whether a filename is valid in the current file system
- ///
- /// name of the file
- /// true if filename is valid
- public static bool IsFilenameValid(string filename)
- {
- var forbiddenChars = Path.GetInvalidFileNameChars();
- foreach (var forbiddenChar in forbiddenChars)
- {
- if (filename == null || filename.Contains(forbiddenChar.ToString()))
- {
- return false;
- }
- }
-
- return true;
- }
- }
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.IO;
+using System.Text.RegularExpressions;
+using System.Windows.Forms;
+using Greenshot.Base.IniFile;
+using Greenshot.Base.Interfaces;
+using log4net;
+
+namespace Greenshot.Base.Core
+{
+ public static class FilenameHelper
+ {
+ private static readonly ILog Log = LogManager.GetLogger(typeof(FilenameHelper));
+
+ // Specify the regular expression for the filename formatting:
+ // Starting with ${
+ // than the varname, which ends with a : or }
+ // If a parameters needs to be supplied, than a ":" should follow the name... everything from the : until the } is considered to be part of the parameters.
+ // The parameter format is a single alpha followed by the value belonging to the parameter, e.g. :
+ // ${capturetime:d"yyyy-MM-dd HH_mm_ss"}
+ private static readonly Regex VarRegexp = new Regex(@"\${(?[^:}]+)[:]?(?[^}]*)}", RegexOptions.Compiled);
+ private static readonly Regex CmdVarRegexp = new Regex(@"%(?[^%]+)%", RegexOptions.Compiled);
+
+ private static readonly Regex SplitRegexp = new Regex(";(?=(?:[^\"]*\"[^\"]*\")*[^\"]*$)", RegexOptions.Compiled);
+ private const int MaxTitleLength = 80;
+ private static readonly CoreConfiguration CoreConfig = IniConfig.GetIniSection();
+ private const string UnsafeReplacement = "_";
+ private static readonly Random RandomNumberGen = new Random();
+ private static readonly Regex RandRegexp = new Regex("^R+$", RegexOptions.Compiled);
+
+ ///
+ /// Remove invalid characters from the fully qualified filename
+ ///
+ /// string with the full path to a file
+ /// string with the full path to a file, without invalid characters
+ public static string MakeFqFilenameSafe(string fullPath)
+ {
+ string path = MakePathSafe(Path.GetDirectoryName(fullPath));
+ string filename = MakeFilenameSafe(Path.GetFileName(fullPath));
+ // Make the fullpath again and return
+ return Path.Combine(path, filename);
+ }
+
+ ///
+ /// Remove invalid characters from the filename
+ ///
+ /// string with the full path to a file
+ /// string with the full path to a file, without invalid characters
+ public static string MakeFilenameSafe(string filename)
+ {
+ // Make the filename save!
+ if (filename != null)
+ {
+ foreach (char disallowed in Path.GetInvalidFileNameChars())
+ {
+ filename = filename.Replace(disallowed.ToString(), UnsafeReplacement);
+ }
+ }
+
+ return filename;
+ }
+
+ ///
+ /// Remove invalid characters from the path
+ ///
+ /// string with the full path to a file
+ /// string with the full path to a file, without invalid characters
+ public static string MakePathSafe(string path)
+ {
+ // Make the path save!
+ if (path != null)
+ {
+ foreach (char disallowed in Path.GetInvalidPathChars())
+ {
+ path = path.Replace(disallowed.ToString(), UnsafeReplacement);
+ }
+ }
+
+ return path;
+ }
+
+ public static string GetFilenameWithoutExtensionFromPattern(string pattern)
+ {
+ return GetFilenameWithoutExtensionFromPattern(pattern, null);
+ }
+
+ public static string GetFilenameWithoutExtensionFromPattern(string pattern, ICaptureDetails captureDetails)
+ {
+ return FillPattern(pattern, captureDetails, true);
+ }
+
+ public static string GetFilenameFromPattern(string pattern, OutputFormat imageFormat)
+ {
+ return GetFilenameFromPattern(pattern, imageFormat, null);
+ }
+
+ public static string GetFilenameFromPattern(string pattern, OutputFormat imageFormat, ICaptureDetails captureDetails)
+ {
+ return FillPattern(pattern, captureDetails, true) + "." + imageFormat.ToString().ToLower();
+ }
+
+ ///
+ /// Return a filename for the current image format (png,jpg etc) with the default file pattern
+ /// that is specified in the configuration
+ ///
+ /// A string with the format
+ ///
+ /// The filename which should be used to save the image
+ public static string GetFilename(OutputFormat format, ICaptureDetails captureDetails)
+ {
+ string pattern = CoreConfig.OutputFileFilenamePattern;
+ if (string.IsNullOrEmpty(pattern?.Trim()))
+ {
+ pattern = "greenshot ${capturetime}";
+ }
+
+ return GetFilenameFromPattern(pattern, format, captureDetails);
+ }
+
+
+ ///
+ /// This method will be called by the regexp.replace as a MatchEvaluator delegate!
+ /// Will delegate this to the MatchVarEvaluatorInternal and catch any exceptions
+ ///
+ /// What are we matching?
+ /// The detail, can be null
+ /// Variables from the process
+ /// Variables from the user
+ /// Variables from the machine
+ ///
+ /// string with the match replacement
+ private static string MatchVarEvaluator(Match match, ICaptureDetails captureDetails, IDictionary processVars, IDictionary userVars, IDictionary machineVars,
+ bool filenameSafeMode)
+ {
+ try
+ {
+ return MatchVarEvaluatorInternal(match, captureDetails, processVars, userVars, machineVars, filenameSafeMode);
+ }
+ catch (Exception e)
+ {
+ Log.Error("Error in MatchVarEvaluatorInternal", e);
+ }
+
+ return string.Empty;
+ }
+
+ ///
+ /// This method will be called by the regexp.replace as a MatchEvaluator delegate!
+ ///
+ /// What are we matching?
+ /// The detail, can be null
+ ///
+ ///
+ ///
+ ///
+ ///
+ private static string MatchVarEvaluatorInternal(Match match, ICaptureDetails captureDetails, IDictionary processVars, IDictionary userVars, IDictionary machineVars,
+ bool filenameSafeMode)
+ {
+ // some defaults
+ int padWidth = 0;
+ int startIndex = 0;
+ int endIndex = 0;
+ char padChar = ' ';
+ string dateFormat = "yyyy-MM-dd HH-mm-ss";
+ IDictionary replacements = new Dictionary();
+ string replaceValue = string.Empty;
+ string variable = match.Groups["variable"].Value;
+ string parameters = match.Groups["parameters"].Value;
+ string randomChars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
+
+ if (parameters.Length > 0)
+ {
+ string[] parms = SplitRegexp.Split(parameters);
+ foreach (string parameter in parms)
+ {
+ switch (parameter.Substring(0, 1))
+ {
+ // Padding p[,pad-character]
+ case "p":
+ string[] padParams = parameter.Substring(1).Split(',');
+ try
+ {
+ padWidth = int.Parse(padParams[0]);
+ }
+ catch
+ {
+ // ignored
+ }
+
+ if (padParams.Length > 1)
+ {
+ padChar = padParams[1][0];
+ }
+
+ break;
+ // replace
+ // r,
+ case "r":
+ string[] replaceParameters = parameter.Substring(1).Split(',');
+ if (replaceParameters != null && replaceParameters.Length == 2)
+ {
+ replacements.Add(replaceParameters[0], replaceParameters[1]);
+ }
+
+ break;
+ // Dateformat d
+ // Format can be anything that is used in C# date formatting
+ case "d":
+ dateFormat = parameter.Substring(1);
+ if (dateFormat.StartsWith("\""))
+ {
+ dateFormat = dateFormat.Substring(1);
+ }
+
+ if (dateFormat.EndsWith("\""))
+ {
+ dateFormat = dateFormat.Substring(0, dateFormat.Length - 1);
+ }
+
+ break;
+ // Substring:
+ // s[,length]
+ case "s":
+ string range = parameter.Substring(1);
+ string[] rangelist = range.Split(',');
+ if (rangelist.Length > 0)
+ {
+ try
+ {
+ startIndex = int.Parse(rangelist[0]);
+ }
+ catch
+ {
+ // Ignore
+ }
+ }
+
+ if (rangelist.Length > 1)
+ {
+ try
+ {
+ endIndex = int.Parse(rangelist[1]);
+ }
+ catch
+ {
+ // Ignore
+ }
+ }
+
+ break;
+ }
+ }
+ }
+
+ if (processVars != null && processVars.Contains(variable))
+ {
+ replaceValue = (string) processVars[variable];
+ if (filenameSafeMode)
+ {
+ replaceValue = MakePathSafe(replaceValue);
+ }
+ }
+ else if (userVars != null && userVars.Contains(variable))
+ {
+ replaceValue = (string) userVars[variable];
+ if (filenameSafeMode)
+ {
+ replaceValue = MakePathSafe(replaceValue);
+ }
+ }
+ else if (machineVars != null && machineVars.Contains(variable))
+ {
+ replaceValue = (string) machineVars[variable];
+ if (filenameSafeMode)
+ {
+ replaceValue = MakePathSafe(replaceValue);
+ }
+ }
+ else if (captureDetails?.MetaData != null && captureDetails.MetaData.ContainsKey(variable))
+ {
+ replaceValue = captureDetails.MetaData[variable];
+ if (filenameSafeMode)
+ {
+ replaceValue = MakePathSafe(replaceValue);
+ }
+ }
+ else if (RandRegexp.IsMatch(variable))
+ {
+ for (int i = 0; i < variable.Length; i++)
+ {
+ replaceValue += randomChars[RandomNumberGen.Next(randomChars.Length)];
+ }
+ }
+ else
+ {
+ // Handle other variables
+ // Default use "now" for the capture take´n
+ DateTime capturetime = DateTime.Now;
+ // Use default application name for title
+ string title = Application.ProductName;
+
+ // Check if we have capture details
+ if (captureDetails != null)
+ {
+ capturetime = captureDetails.DateTime;
+ if (captureDetails.Title != null)
+ {
+ title = captureDetails.Title;
+ if (title.Length > MaxTitleLength)
+ {
+ title = title.Substring(0, MaxTitleLength);
+ }
+ }
+ }
+
+ switch (variable)
+ {
+ case "domain":
+ replaceValue = Environment.UserDomainName;
+ break;
+ case "user":
+ replaceValue = Environment.UserName;
+ break;
+ case "hostname":
+ replaceValue = Environment.MachineName;
+ break;
+ case "YYYY":
+ if (padWidth == 0)
+ {
+ padWidth = -4;
+ padChar = '0';
+ }
+
+ replaceValue = capturetime.Year.ToString();
+ break;
+ case "MM":
+ replaceValue = capturetime.Month.ToString();
+ if (padWidth == 0)
+ {
+ padWidth = -2;
+ padChar = '0';
+ }
+
+ break;
+ case "DD":
+ replaceValue = capturetime.Day.ToString();
+ if (padWidth == 0)
+ {
+ padWidth = -2;
+ padChar = '0';
+ }
+
+ break;
+ case "hh":
+ if (padWidth == 0)
+ {
+ padWidth = -2;
+ padChar = '0';
+ }
+
+ replaceValue = capturetime.Hour.ToString();
+ break;
+ case "mm":
+ if (padWidth == 0)
+ {
+ padWidth = -2;
+ padChar = '0';
+ }
+
+ replaceValue = capturetime.Minute.ToString();
+ break;
+ case "ss":
+ if (padWidth == 0)
+ {
+ padWidth = -2;
+ padChar = '0';
+ }
+
+ replaceValue = capturetime.Second.ToString();
+ break;
+ case "now":
+ replaceValue = DateTime.Now.ToString(dateFormat);
+ if (filenameSafeMode)
+ {
+ replaceValue = MakeFilenameSafe(replaceValue);
+ }
+
+ break;
+ case "capturetime":
+ replaceValue = capturetime.ToString(dateFormat);
+ if (filenameSafeMode)
+ {
+ replaceValue = MakeFilenameSafe(replaceValue);
+ }
+
+ break;
+ case "NUM":
+ CoreConfig.OutputFileIncrementingNumber++;
+ IniConfig.Save();
+ replaceValue = CoreConfig.OutputFileIncrementingNumber.ToString();
+ if (padWidth == 0)
+ {
+ padWidth = -6;
+ padChar = '0';
+ }
+
+ break;
+ case "title":
+ replaceValue = title;
+ if (filenameSafeMode)
+ {
+ replaceValue = MakeFilenameSafe(replaceValue);
+ }
+
+ break;
+ case "MyPictures":
+ replaceValue = Environment.GetFolderPath(Environment.SpecialFolder.MyPictures);
+ break;
+ case "MyMusic":
+ replaceValue = Environment.GetFolderPath(Environment.SpecialFolder.MyMusic);
+ break;
+ case "MyDocuments":
+ replaceValue = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
+ break;
+ case "Personal":
+ replaceValue = Environment.GetFolderPath(Environment.SpecialFolder.Personal);
+ break;
+ case "Desktop":
+ replaceValue = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
+ break;
+ case "ApplicationData":
+ replaceValue = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
+ break;
+ case "LocalApplicationData":
+ replaceValue = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
+ break;
+ }
+ }
+
+ // do padding
+ if (padWidth > 0)
+ {
+ replaceValue = replaceValue.PadRight(padWidth, padChar);
+ }
+ else if (padWidth < 0)
+ {
+ replaceValue = replaceValue.PadLeft(-padWidth, padChar);
+ }
+
+ // do substring
+ if (startIndex != 0 || endIndex != 0)
+ {
+ if (startIndex < 0)
+ {
+ startIndex = replaceValue.Length + startIndex;
+ }
+
+ if (endIndex < 0)
+ {
+ endIndex = replaceValue.Length + endIndex;
+ }
+
+ if (endIndex != 0)
+ {
+ try
+ {
+ replaceValue = replaceValue.Substring(startIndex, endIndex);
+ }
+ catch
+ {
+ // Ignore
+ }
+ }
+ else
+ {
+ try
+ {
+ replaceValue = replaceValue.Substring(startIndex);
+ }
+ catch
+ {
+ // Ignore
+ }
+ }
+ }
+
+ // new for feature #697
+ if (replacements.Count > 0)
+ {
+ foreach (string oldValue in replacements.Keys)
+ {
+ replaceValue = replaceValue.Replace(oldValue, replacements[oldValue]);
+ }
+ }
+
+ return replaceValue;
+ }
+
+ ///
+ /// "Simply" fill the pattern with environment variables
+ ///
+ /// String with pattern %var%
+ /// true to make sure everything is filenamesafe
+ /// Filled string
+ public static string FillCmdVariables(string pattern, bool filenameSafeMode = true)
+ {
+ IDictionary processVars = null;
+ IDictionary userVars = null;
+ IDictionary machineVars = null;
+ try
+ {
+ processVars = Environment.GetEnvironmentVariables(EnvironmentVariableTarget.Process);
+ }
+ catch (Exception e)
+ {
+ Log.Error("Error retrieving EnvironmentVariableTarget.Process", e);
+ }
+
+ try
+ {
+ userVars = Environment.GetEnvironmentVariables(EnvironmentVariableTarget.User);
+ }
+ catch (Exception e)
+ {
+ Log.Error("Error retrieving EnvironmentVariableTarget.User", e);
+ }
+
+ try
+ {
+ machineVars = Environment.GetEnvironmentVariables(EnvironmentVariableTarget.Machine);
+ }
+ catch (Exception e)
+ {
+ Log.Error("Error retrieving EnvironmentVariableTarget.Machine", e);
+ }
+
+ return CmdVarRegexp.Replace(pattern,
+ m => MatchVarEvaluator(m, null, processVars, userVars, machineVars, filenameSafeMode)
+ );
+ }
+
+ ///
+ /// "Simply" fill the pattern with environment variables
+ ///
+ /// String with pattern ${var}
+ /// true to make sure everything is filenamesafe
+ /// Filled string
+ public static string FillVariables(string pattern, bool filenameSafeMode)
+ {
+ IDictionary processVars = null;
+ IDictionary userVars = null;
+ IDictionary machineVars = null;
+ try
+ {
+ processVars = Environment.GetEnvironmentVariables(EnvironmentVariableTarget.Process);
+ }
+ catch (Exception e)
+ {
+ Log.Error("Error retrieving EnvironmentVariableTarget.Process", e);
+ }
+
+ try
+ {
+ userVars = Environment.GetEnvironmentVariables(EnvironmentVariableTarget.User);
+ }
+ catch (Exception e)
+ {
+ Log.Error("Error retrieving EnvironmentVariableTarget.User", e);
+ }
+
+ try
+ {
+ machineVars = Environment.GetEnvironmentVariables(EnvironmentVariableTarget.Machine);
+ }
+ catch (Exception e)
+ {
+ Log.Error("Error retrieving EnvironmentVariableTarget.Machine", e);
+ }
+
+ return VarRegexp.Replace(pattern,
+ m => MatchVarEvaluator(m, null, processVars, userVars, machineVars, filenameSafeMode)
+ );
+ }
+
+ ///
+ /// Fill the pattern wit the supplied details
+ ///
+ /// Pattern
+ /// CaptureDetails, can be null
+ /// Should the result be made "filename" safe?
+ /// Filled pattern
+ public static string FillPattern(string pattern, ICaptureDetails captureDetails, bool filenameSafeMode)
+ {
+ IDictionary processVars = null;
+ IDictionary userVars = null;
+ IDictionary machineVars = null;
+ try
+ {
+ processVars = Environment.GetEnvironmentVariables(EnvironmentVariableTarget.Process);
+ }
+ catch (Exception e)
+ {
+ Log.Error("Error retrieving EnvironmentVariableTarget.Process", e);
+ }
+
+ try
+ {
+ userVars = Environment.GetEnvironmentVariables(EnvironmentVariableTarget.User);
+ }
+ catch (Exception e)
+ {
+ Log.Error("Error retrieving EnvironmentVariableTarget.User", e);
+ }
+
+ try
+ {
+ machineVars = Environment.GetEnvironmentVariables(EnvironmentVariableTarget.Machine);
+ }
+ catch (Exception e)
+ {
+ Log.Error("Error retrieving EnvironmentVariableTarget.Machine", e);
+ }
+
+ try
+ {
+ return VarRegexp.Replace(pattern,
+ m => MatchVarEvaluator(m, captureDetails, processVars, userVars, machineVars, filenameSafeMode)
+ );
+ }
+ catch (Exception e)
+ {
+ // adding additional data for bug tracking
+ if (captureDetails != null)
+ {
+ e.Data.Add("title", captureDetails.Title);
+ }
+
+ e.Data.Add("pattern", pattern);
+ throw;
+ }
+ }
+
+ ///
+ /// Checks whether a directory name is valid in the current file system
+ ///
+ /// directory name (not path!)
+ /// true if directory name is valid
+ public static bool IsDirectoryNameValid(string directoryName)
+ {
+ var forbiddenChars = Path.GetInvalidPathChars();
+ foreach (var forbiddenChar in forbiddenChars)
+ {
+ if (directoryName == null || directoryName.Contains(forbiddenChar.ToString()))
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ ///
+ /// Checks whether a filename is valid in the current file system
+ ///
+ /// name of the file
+ /// true if filename is valid
+ public static bool IsFilenameValid(string filename)
+ {
+ var forbiddenChars = Path.GetInvalidFileNameChars();
+ foreach (var forbiddenChar in forbiddenChars)
+ {
+ if (filename == null || filename.Contains(forbiddenChar.ToString()))
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+ }
}
\ No newline at end of file
diff --git a/src/GreenshotPlugin/Core/Fraction.cs b/src/Greenshot.Base/Core/Fraction.cs
similarity index 99%
rename from src/GreenshotPlugin/Core/Fraction.cs
rename to src/Greenshot.Base/Core/Fraction.cs
index 94c4fddf4..e7d999445 100644
--- a/src/GreenshotPlugin/Core/Fraction.cs
+++ b/src/Greenshot.Base/Core/Fraction.cs
@@ -1,7 +1,7 @@
using System;
using System.Text.RegularExpressions;
-namespace GreenshotPlugin.Core
+namespace Greenshot.Base.Core
{
///
/// Basic Fraction (Rational) numbers with features only needed to represent scale factors.
diff --git a/src/GreenshotPlugin/Core/GreenshotResources.cs b/src/Greenshot.Base/Core/GreenshotResources.cs
similarity index 95%
rename from src/GreenshotPlugin/Core/GreenshotResources.cs
rename to src/Greenshot.Base/Core/GreenshotResources.cs
index b4de5d007..29248012e 100644
--- a/src/GreenshotPlugin/Core/GreenshotResources.cs
+++ b/src/Greenshot.Base/Core/GreenshotResources.cs
@@ -1,49 +1,49 @@
-/*
- * Greenshot - a free and open source screenshot tool
- * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
- *
- * For more information see: http://getgreenshot.org/
- * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 1 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-using System.ComponentModel;
-using System.Drawing;
-
-namespace GreenshotPlugin.Core
-{
- ///
- /// Centralized storage of the icons & bitmaps
- ///
- public static class GreenshotResources
- {
- private static readonly ComponentResourceManager GreenshotResourceManager = new ComponentResourceManager(typeof(GreenshotResources));
-
- public static Image GetImage(string imageName)
- {
- return (Image) GreenshotResourceManager.GetObject(imageName);
- }
-
- public static Icon GetIcon(string imageName)
- {
- return (Icon) GreenshotResourceManager.GetObject(imageName);
- }
-
- public static Icon GetGreenshotIcon()
- {
- return GetIcon("Greenshot.Icon");
- }
- }
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+using System.ComponentModel;
+using System.Drawing;
+
+namespace Greenshot.Base.Core
+{
+ ///
+ /// Centralized storage of the icons & bitmaps
+ ///
+ public static class GreenshotResources
+ {
+ private static readonly ComponentResourceManager GreenshotResourceManager = new ComponentResourceManager(typeof(GreenshotResources));
+
+ public static Image GetImage(string imageName)
+ {
+ return (Image) GreenshotResourceManager.GetObject(imageName);
+ }
+
+ public static Icon GetIcon(string imageName)
+ {
+ return (Icon) GreenshotResourceManager.GetObject(imageName);
+ }
+
+ public static Icon GetGreenshotIcon()
+ {
+ return GetIcon("Greenshot.Icon");
+ }
+ }
}
\ No newline at end of file
diff --git a/src/GreenshotPlugin/Core/GreenshotResources.resx b/src/Greenshot.Base/Core/GreenshotResources.resx
similarity index 98%
rename from src/GreenshotPlugin/Core/GreenshotResources.resx
rename to src/Greenshot.Base/Core/GreenshotResources.resx
index 521a6125f..ea1760d58 100644
--- a/src/GreenshotPlugin/Core/GreenshotResources.resx
+++ b/src/Greenshot.Base/Core/GreenshotResources.resx
@@ -1,471 +1,471 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- text/microsoft-resx
-
-
- 2.0
-
-
- System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
-
-
- System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
-
-
-
-
- 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
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPSkpKSisCAAAAAAAAAQAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFTg9JgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAgAAAgABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQABAAEAAQABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAApEIAAAAAAACkQgAAAAAAAKRCAAAAAAAApEIA
- AAAAAACkQgAAAAAAAKRCAAAAAAAApEIAAAAAAACkQgAAAAAAAKRCAAAAAAAApEIAAAAAAACkQgAAAAAA
- AKRCAAAAAAAApEIAAAAAAACkQgAAAAAAAKRCAAAAAAAApEIAAAAAAACkQgAAAAAAAKRCAAAAAAAApEIA
- AAAAAACkQgAAAAAAAKRCAAAAAAAApEIAAAAAAACkQgAAAAAAAKRCAAAAAAAApEIAAAAAAACkQgAAAAAA
- AKRCAAAAAAAApEIAAAAAAACkQgAAAAAAAKRCAAAAAAAApEIAAAAAAACkQgAAAAAAAKRCAAAAAAAApEIA
- AAAAAACkQgAAAAAAAKRCAAAAAAAApEIAAAAAAACkQgAAAAAAAKRCAAAAAAAApEIAAAAAAACkQgAAAAAA
- AKRCAAAAAAAApEIAAAAAAACkQgAAAAAAAKRCAAAAAAAApEIAAAAAAACkQgAAAAAAAKRCKAAAACAAAABA
- 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
- YQUAAAAgY0hSTQAAeiYAAICEAAD6AAAAgOgAAHUwAADqYAAAOpgAABdwnLpRPAAAAwBQTFRFgICA////
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAODgHVgAAAAlwSFlzAAAOvgAA
- Dr4B6kKxwAAAABZJREFUGFdjYAABRhAAs4hlkq4DZDgACywAM12jTsYAAAAASUVORK5CYII=
-
-
-
-
- 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
- mXizAAAAAElFTkSuQmCC
-
-
-
-
- iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
- YQUAAAAgY0hSTQAAeiYAAICEAAD6AAAAgOgAAHUwAADqYAAAOpgAABdwnLpRPAAAAXJJREFUOE+lk0FL
- AkEYhlvwv3jzoiDoQdCbdEnYf6CrqCgoHgRRAk/9EQVLdEGyFiQNMS+dvHnoEkgglGAmCL7NO6RMIZvU
- wsMO3zzzzGk0ACf/+hjQNO1ccKlXKsYx0OUZeflXoFmtVsUS2P4CHboi0FQDrXK5jM12i/VmYwsduiLQ
- UgNmqVTCuzj8tlrZQoeuCJhqoFMsFvG6XmO2WNhCh64IdNRAt1Ao4EXc/jSf20KHrgh01YCVy+Uwnkzw
- vFzaQoeuCFhqoJfJZBCLxY6Crgj01EA/lUrB4/HA7XYfhHs78vk8A301MIzH4/B6vRiNHjAY3H+DM+7p
- ug6fz4dsNsvAUA2Mo9Eo/H4/LOsOTqdTYprXEs64x0AwGEQ6nWZgrAYeDcNAIBBAu30r/6Reb0t2MwbC
- 4TCSySQDj/uAeEyngqnL5fpoNG4QCoUktVpHspsxEIlEkEgk+AKnaoAP8kwwczgcF4fg3g+u9gEu/son
- bfJW/NwRDyIAAAAASUVORK5CYII=
-
-
-
-
- iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
- YQUAAAAgY0hSTQAAeiYAAICEAAD6AAAAgOgAAHUwAADqYAAAOpgAABdwnLpRPAAAAUNJREFUOE+lk79L
- QlEcxW9/gqCrm6vg4uYoOAgOrqLk4ioP0r2Glhp0SSjoF1FE0BIUDU3RdIOGoKBVGlpapaHTObeuCPe6
- 9ITD5fs9n3Pue8JbAWBS/VSQRvPwKR/j3JgaZXVqPv5TzPOXLhYoZDEcQidVWyhw3qzfn3tBAWH7PRjg
- uV7HV5JAM6USyX50u86btlrOCwoOCR7Q+Oz1cFcu473dhmbppdFwu8dq1e3EBgU0zB6NXQJvzSaui0U8
- VCq4LZWwn8vhLJ+HPDFiowUEzITADsGrQgFHmYzTSTYL7eSJiRZs0timRoTGhC956wXDXtrJEyM2eAIt
- t34Be8NgTPLELCuQYe8Z9tK8ZBf+ieuEnxj20rzB26SYF7zCGsGEoVeW6NTMoJFiXlDAkFllqMOwTs2+
- IOYFBf/9oFJ9ibr0B4f94vVG3bWDAAAAAElFTkSuQmCC
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+
+
+ 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
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPSkpKSisCAAAAAAAAAQAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFTg9JgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAgAAAgABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQABAAEAAQABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAApEIAAAAAAACkQgAAAAAAAKRCAAAAAAAApEIA
+ AAAAAACkQgAAAAAAAKRCAAAAAAAApEIAAAAAAACkQgAAAAAAAKRCAAAAAAAApEIAAAAAAACkQgAAAAAA
+ AKRCAAAAAAAApEIAAAAAAACkQgAAAAAAAKRCAAAAAAAApEIAAAAAAACkQgAAAAAAAKRCAAAAAAAApEIA
+ AAAAAACkQgAAAAAAAKRCAAAAAAAApEIAAAAAAACkQgAAAAAAAKRCAAAAAAAApEIAAAAAAACkQgAAAAAA
+ AKRCAAAAAAAApEIAAAAAAACkQgAAAAAAAKRCAAAAAAAApEIAAAAAAACkQgAAAAAAAKRCAAAAAAAApEIA
+ AAAAAACkQgAAAAAAAKRCAAAAAAAApEIAAAAAAACkQgAAAAAAAKRCAAAAAAAApEIAAAAAAACkQgAAAAAA
+ AKRCAAAAAAAApEIAAAAAAACkQgAAAAAAAKRCAAAAAAAApEIAAAAAAACkQgAAAAAAAKRCKAAAACAAAABA
+ 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
+ YQUAAAAgY0hSTQAAeiYAAICEAAD6AAAAgOgAAHUwAADqYAAAOpgAABdwnLpRPAAAAwBQTFRFgICA////
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAODgHVgAAAAlwSFlzAAAOvgAA
+ Dr4B6kKxwAAAABZJREFUGFdjYAABRhAAs4hlkq4DZDgACywAM12jTsYAAAAASUVORK5CYII=
+
+
+
+
+ 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
+ mXizAAAAAElFTkSuQmCC
+
+
+
+
+ iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
+ YQUAAAAgY0hSTQAAeiYAAICEAAD6AAAAgOgAAHUwAADqYAAAOpgAABdwnLpRPAAAAXJJREFUOE+lk0FL
+ AkEYhlvwv3jzoiDoQdCbdEnYf6CrqCgoHgRRAk/9EQVLdEGyFiQNMS+dvHnoEkgglGAmCL7NO6RMIZvU
+ wsMO3zzzzGk0ACf/+hjQNO1ccKlXKsYx0OUZeflXoFmtVsUS2P4CHboi0FQDrXK5jM12i/VmYwsduiLQ
+ UgNmqVTCuzj8tlrZQoeuCJhqoFMsFvG6XmO2WNhCh64IdNRAt1Ao4EXc/jSf20KHrgh01YCVy+Uwnkzw
+ vFzaQoeuCFhqoJfJZBCLxY6Crgj01EA/lUrB4/HA7XYfhHs78vk8A301MIzH4/B6vRiNHjAY3H+DM+7p
+ ug6fz4dsNsvAUA2Mo9Eo/H4/LOsOTqdTYprXEs64x0AwGEQ6nWZgrAYeDcNAIBBAu30r/6Reb0t2MwbC
+ 4TCSySQDj/uAeEyngqnL5fpoNG4QCoUktVpHspsxEIlEkEgk+AKnaoAP8kwwczgcF4fg3g+u9gEu/son
+ bfJW/NwRDyIAAAAASUVORK5CYII=
+
+
+
+
+ iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
+ YQUAAAAgY0hSTQAAeiYAAICEAAD6AAAAgOgAAHUwAADqYAAAOpgAABdwnLpRPAAAAUNJREFUOE+lk79L
+ QlEcxW9/gqCrm6vg4uYoOAgOrqLk4ioP0r2Glhp0SSjoF1FE0BIUDU3RdIOGoKBVGlpapaHTObeuCPe6
+ 9ITD5fs9n3Pue8JbAWBS/VSQRvPwKR/j3JgaZXVqPv5TzPOXLhYoZDEcQidVWyhw3qzfn3tBAWH7PRjg
+ uV7HV5JAM6USyX50u86btlrOCwoOCR7Q+Oz1cFcu473dhmbppdFwu8dq1e3EBgU0zB6NXQJvzSaui0U8
+ VCq4LZWwn8vhLJ+HPDFiowUEzITADsGrQgFHmYzTSTYL7eSJiRZs0timRoTGhC956wXDXtrJEyM2eAIt
+ t34Be8NgTPLELCuQYe8Z9tK8ZBf+ieuEnxj20rzB26SYF7zCGsGEoVeW6NTMoJFiXlDAkFllqMOwTs2+
+ IOYFBf/9oFJ9ibr0B4f94vVG3bWDAAAAAElFTkSuQmCC
+
+
\ No newline at end of file
diff --git a/src/GreenshotPlugin/Core/HResultExtensions.cs b/src/Greenshot.Base/Core/HResultExtensions.cs
similarity index 96%
rename from src/GreenshotPlugin/Core/HResultExtensions.cs
rename to src/Greenshot.Base/Core/HResultExtensions.cs
index 891f48908..9550101fd 100644
--- a/src/GreenshotPlugin/Core/HResultExtensions.cs
+++ b/src/Greenshot.Base/Core/HResultExtensions.cs
@@ -17,10 +17,10 @@
// You should have received a copy of the GNU General Public License
// along with this program. If not, see .
-using GreenshotPlugin.Core.Enums;
using System.Diagnostics.Contracts;
+using Greenshot.Base.Core.Enums;
-namespace GreenshotPlugin.Core
+namespace Greenshot.Base.Core
{
///
/// Extensions to handle the HResult
diff --git a/src/GreenshotPlugin/Core/IEHelper.cs b/src/Greenshot.Base/Core/IEHelper.cs
similarity index 96%
rename from src/GreenshotPlugin/Core/IEHelper.cs
rename to src/Greenshot.Base/Core/IEHelper.cs
index 5fb52032f..7f01b73a2 100644
--- a/src/GreenshotPlugin/Core/IEHelper.cs
+++ b/src/Greenshot.Base/Core/IEHelper.cs
@@ -1,203 +1,203 @@
-/*
- * Greenshot - a free and open source screenshot tool
- * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
- *
- * For more information see: http://getgreenshot.org/
- * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 1 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Reflection;
-using log4net;
-using Microsoft.Win32;
-
-namespace GreenshotPlugin.Core
-{
- ///
- /// Description of IEHelper.
- ///
- public static class IEHelper
- {
- private static readonly ILog Log = LogManager.GetLogger(typeof(IEHelper));
-
- // Internet explorer Registry key
- private const string IeKey = @"Software\Microsoft\Internet Explorer";
-
- ///
- /// Get the current browser version
- ///
- /// int with browser version
- public static int IEVersion
- {
- get
- {
- var maxVer = 7;
- using (var ieKey = Registry.LocalMachine.OpenSubKey(IeKey, false))
- {
- foreach (var value in new[]
- {
- "svcVersion", "svcUpdateVersion", "Version", "W2kVersion"
- })
- {
- var objVal = ieKey.GetValue(value, "0");
- var strVal = Convert.ToString(objVal);
-
- var iPos = strVal.IndexOf('.');
- if (iPos > 0)
- {
- strVal = strVal.Substring(0, iPos);
- }
-
- if (int.TryParse(strVal, out var res))
- {
- maxVer = Math.Max(maxVer, res);
- }
- }
- }
-
- return maxVer;
- }
- }
-
- ///
- /// Get the highest possible version for the embedded browser
- ///
- /// true to ignore the doctype when loading a page
- /// IE Feature
- public static int GetEmbVersion(bool ignoreDoctype = true)
- {
- var ieVersion = IEVersion;
-
- if (ieVersion > 9)
- {
- return ieVersion * 1000 + (ignoreDoctype ? 1 : 0);
- }
-
- if (ieVersion > 7)
- {
- return ieVersion * 1111;
- }
-
- return 7000;
- }
-
- ///
- /// Fix browser version to the highest possible
- ///
- /// true to ignore the doctype when loading a page
- public static void FixBrowserVersion(bool ignoreDoctype = true)
- {
- var applicationName = Path.GetFileNameWithoutExtension(Assembly.GetEntryAssembly().Location);
- FixBrowserVersion(applicationName, ignoreDoctype);
- }
-
- ///
- /// Fix the browser version for the specified application
- ///
- /// Name of the process
- /// true to ignore the doctype when loading a page
- public static void FixBrowserVersion(string applicationName, bool ignoreDoctype = true)
- {
- FixBrowserVersion(applicationName, GetEmbVersion(ignoreDoctype));
- }
-
- ///
- /// Fix the browser version for the specified application
- ///
- /// Name of the process
- ///
- /// Version, see
- /// Browser Emulation
- ///
- public static void FixBrowserVersion(string applicationName, int ieVersion)
- {
- ModifyRegistry("HKEY_CURRENT_USER", applicationName + ".exe", ieVersion);
-#if DEBUG
- ModifyRegistry("HKEY_CURRENT_USER", applicationName + ".vshost.exe", ieVersion);
-#endif
- }
-
- ///
- /// Make the change to the registry
- ///
- /// HKEY_CURRENT_USER or something
- /// Name of the executable
- /// Version to use
- private static void ModifyRegistry(string root, string applicationName, int ieFeatureVersion)
- {
- var regKey = root + @"\Software\Microsoft\Internet Explorer\Main\FeatureControl\FEATURE_BROWSER_EMULATION";
- try
- {
- Registry.SetValue(regKey, applicationName, ieFeatureVersion);
- }
- catch (Exception ex)
- {
- // some config will hit access rights exceptions
- // this is why we try with both LOCAL_MACHINE and CURRENT_USER
- Log.Error(ex);
- Log.ErrorFormat("couldn't modify the registry key {0}", regKey);
- }
- }
-
- ///
- /// Find the DirectUI window for MSAA (Accessible)
- ///
- /// The browser WindowDetails
- /// WindowDetails for the DirectUI window
- public static WindowDetails GetDirectUI(WindowDetails browserWindowDetails)
- {
- if (browserWindowDetails == null)
- {
- return null;
- }
-
- WindowDetails tmpWd = browserWindowDetails;
- // Since IE 9 the TabBandClass is less deep!
- if (IEVersion < 9)
- {
- tmpWd = tmpWd.GetChild("CommandBarClass");
- tmpWd = tmpWd?.GetChild("ReBarWindow32");
- }
-
- tmpWd = tmpWd?.GetChild("TabBandClass");
- tmpWd = tmpWd?.GetChild("DirectUIHWND");
- return tmpWd;
- }
-
- ///
- /// Return an IEnumerable with the currently opened IE urls
- ///
- ///
- public static IEnumerable GetIEUrls()
- {
- // Find the IE window
- foreach (WindowDetails ieWindow in WindowDetails.GetAllWindows("IEFrame"))
- {
- WindowDetails directUIWD = GetDirectUI(ieWindow);
- if (directUIWD != null)
- {
- Accessible ieAccessible = new Accessible(directUIWD.Handle);
- foreach (string url in ieAccessible.IETabUrls)
- {
- yield return url;
- }
- }
- }
- }
- }
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Reflection;
+using log4net;
+using Microsoft.Win32;
+
+namespace Greenshot.Base.Core
+{
+ ///
+ /// Description of IEHelper.
+ ///
+ public static class IEHelper
+ {
+ private static readonly ILog Log = LogManager.GetLogger(typeof(IEHelper));
+
+ // Internet explorer Registry key
+ private const string IeKey = @"Software\Microsoft\Internet Explorer";
+
+ ///
+ /// Get the current browser version
+ ///
+ /// int with browser version
+ public static int IEVersion
+ {
+ get
+ {
+ var maxVer = 7;
+ using (var ieKey = Registry.LocalMachine.OpenSubKey(IeKey, false))
+ {
+ foreach (var value in new[]
+ {
+ "svcVersion", "svcUpdateVersion", "Version", "W2kVersion"
+ })
+ {
+ var objVal = ieKey.GetValue(value, "0");
+ var strVal = Convert.ToString(objVal);
+
+ var iPos = strVal.IndexOf('.');
+ if (iPos > 0)
+ {
+ strVal = strVal.Substring(0, iPos);
+ }
+
+ if (int.TryParse(strVal, out var res))
+ {
+ maxVer = Math.Max(maxVer, res);
+ }
+ }
+ }
+
+ return maxVer;
+ }
+ }
+
+ ///
+ /// Get the highest possible version for the embedded browser
+ ///
+ /// true to ignore the doctype when loading a page
+ /// IE Feature
+ public static int GetEmbVersion(bool ignoreDoctype = true)
+ {
+ var ieVersion = IEVersion;
+
+ if (ieVersion > 9)
+ {
+ return ieVersion * 1000 + (ignoreDoctype ? 1 : 0);
+ }
+
+ if (ieVersion > 7)
+ {
+ return ieVersion * 1111;
+ }
+
+ return 7000;
+ }
+
+ ///
+ /// Fix browser version to the highest possible
+ ///
+ /// true to ignore the doctype when loading a page
+ public static void FixBrowserVersion(bool ignoreDoctype = true)
+ {
+ var applicationName = Path.GetFileNameWithoutExtension(Assembly.GetEntryAssembly().Location);
+ FixBrowserVersion(applicationName, ignoreDoctype);
+ }
+
+ ///
+ /// Fix the browser version for the specified application
+ ///
+ /// Name of the process
+ /// true to ignore the doctype when loading a page
+ public static void FixBrowserVersion(string applicationName, bool ignoreDoctype = true)
+ {
+ FixBrowserVersion(applicationName, GetEmbVersion(ignoreDoctype));
+ }
+
+ ///
+ /// Fix the browser version for the specified application
+ ///
+ /// Name of the process
+ ///
+ /// Version, see
+ /// Browser Emulation
+ ///
+ public static void FixBrowserVersion(string applicationName, int ieVersion)
+ {
+ ModifyRegistry("HKEY_CURRENT_USER", applicationName + ".exe", ieVersion);
+#if DEBUG
+ ModifyRegistry("HKEY_CURRENT_USER", applicationName + ".vshost.exe", ieVersion);
+#endif
+ }
+
+ ///
+ /// Make the change to the registry
+ ///
+ /// HKEY_CURRENT_USER or something
+ /// Name of the executable
+ /// Version to use
+ private static void ModifyRegistry(string root, string applicationName, int ieFeatureVersion)
+ {
+ var regKey = root + @"\Software\Microsoft\Internet Explorer\Main\FeatureControl\FEATURE_BROWSER_EMULATION";
+ try
+ {
+ Registry.SetValue(regKey, applicationName, ieFeatureVersion);
+ }
+ catch (Exception ex)
+ {
+ // some config will hit access rights exceptions
+ // this is why we try with both LOCAL_MACHINE and CURRENT_USER
+ Log.Error(ex);
+ Log.ErrorFormat("couldn't modify the registry key {0}", regKey);
+ }
+ }
+
+ ///
+ /// Find the DirectUI window for MSAA (Accessible)
+ ///
+ /// The browser WindowDetails
+ /// WindowDetails for the DirectUI window
+ public static WindowDetails GetDirectUI(WindowDetails browserWindowDetails)
+ {
+ if (browserWindowDetails == null)
+ {
+ return null;
+ }
+
+ WindowDetails tmpWd = browserWindowDetails;
+ // Since IE 9 the TabBandClass is less deep!
+ if (IEVersion < 9)
+ {
+ tmpWd = tmpWd.GetChild("CommandBarClass");
+ tmpWd = tmpWd?.GetChild("ReBarWindow32");
+ }
+
+ tmpWd = tmpWd?.GetChild("TabBandClass");
+ tmpWd = tmpWd?.GetChild("DirectUIHWND");
+ return tmpWd;
+ }
+
+ ///
+ /// Return an IEnumerable with the currently opened IE urls
+ ///
+ ///
+ public static IEnumerable GetIEUrls()
+ {
+ // Find the IE window
+ foreach (WindowDetails ieWindow in WindowDetails.GetAllWindows("IEFrame"))
+ {
+ WindowDetails directUIWD = GetDirectUI(ieWindow);
+ if (directUIWD != null)
+ {
+ Accessible ieAccessible = new Accessible(directUIWD.Handle);
+ foreach (string url in ieAccessible.IETabUrls)
+ {
+ yield return url;
+ }
+ }
+ }
+ }
+ }
}
\ No newline at end of file
diff --git a/src/GreenshotPlugin/Core/IImage.cs b/src/Greenshot.Base/Core/IImage.cs
similarity index 95%
rename from src/GreenshotPlugin/Core/IImage.cs
rename to src/Greenshot.Base/Core/IImage.cs
index ffeff3d24..2204d5739 100644
--- a/src/GreenshotPlugin/Core/IImage.cs
+++ b/src/Greenshot.Base/Core/IImage.cs
@@ -1,68 +1,68 @@
-/*
- * Greenshot - a free and open source screenshot tool
- * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
- *
- * For more information see: http://getgreenshot.org/
- * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 1 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-using System;
-using System.Drawing;
-using System.Drawing.Imaging;
-
-namespace GreenshotPlugin.Core
-{
- ///
- /// The image interface, this abstracts an image
- ///
- public interface IImage : IDisposable
- {
- ///
- /// Height of the image, can be set to change
- ///
- int Height { get; set; }
-
- ///
- /// Width of the image, can be set to change.
- ///
- int Width { get; set; }
-
- ///
- /// Size of the image
- ///
- Size Size { get; }
-
- ///
- /// Pixelformat of the underlying image
- ///
- PixelFormat PixelFormat { get; }
-
- ///
- /// Vertical resolution of the underlying image
- ///
- float VerticalResolution { get; }
-
- ///
- /// Horizontal resolution of the underlying image
- ///
- float HorizontalResolution { get; }
-
- ///
- /// Unterlying image, or an on demand rendered version with different attributes as the original
- ///
- Image Image { get; }
- }
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+using System;
+using System.Drawing;
+using System.Drawing.Imaging;
+
+namespace Greenshot.Base.Core
+{
+ ///
+ /// The image interface, this abstracts an image
+ ///
+ public interface IImage : IDisposable
+ {
+ ///
+ /// Height of the image, can be set to change
+ ///
+ int Height { get; set; }
+
+ ///
+ /// Width of the image, can be set to change.
+ ///
+ int Width { get; set; }
+
+ ///
+ /// Size of the image
+ ///
+ Size Size { get; }
+
+ ///
+ /// Pixelformat of the underlying image
+ ///
+ PixelFormat PixelFormat { get; }
+
+ ///
+ /// Vertical resolution of the underlying image
+ ///
+ float VerticalResolution { get; }
+
+ ///
+ /// Horizontal resolution of the underlying image
+ ///
+ float HorizontalResolution { get; }
+
+ ///
+ /// Unterlying image, or an on demand rendered version with different attributes as the original
+ ///
+ Image Image { get; }
+ }
}
\ No newline at end of file
diff --git a/src/GreenshotPlugin/Core/ImageHelper.cs b/src/Greenshot.Base/Core/ImageHelper.cs
similarity index 97%
rename from src/GreenshotPlugin/Core/ImageHelper.cs
rename to src/Greenshot.Base/Core/ImageHelper.cs
index d6263813c..e93307717 100644
--- a/src/GreenshotPlugin/Core/ImageHelper.cs
+++ b/src/Greenshot.Base/Core/ImageHelper.cs
@@ -1,1807 +1,1807 @@
-/*
- * Greenshot - a free and open source screenshot tool
- * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
- *
- * For more information see: http://getgreenshot.org/
- * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 1 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-using System;
-using System.Collections.Generic;
-using System.Drawing;
-using System.Drawing.Drawing2D;
-using System.Drawing.Imaging;
-using System.IO;
-using GreenshotPlugin.UnmanagedHelpers;
-using GreenshotPlugin.Effects;
-using GreenshotPlugin.IniFile;
-using GreenshotPlugin.Interfaces;
-using log4net;
-
-namespace GreenshotPlugin.Core
-{
- internal enum ExifOrientations : byte
- {
- Unknown = 0,
- TopLeft = 1,
- TopRight = 2,
- BottomRight = 3,
- BottomLeft = 4,
- LeftTop = 5,
- RightTop = 6,
- RightBottom = 7,
- LeftBottom = 8,
- }
-
- ///
- /// Description of ImageHelper.
- ///
- public static class ImageHelper
- {
- private static readonly ILog Log = LogManager.GetLogger(typeof(ImageHelper));
- private static readonly CoreConfiguration CoreConfig = IniConfig.GetIniSection();
- private const int ExifOrientationId = 0x0112;
-
- static ImageHelper()
- {
- StreamConverters["greenshot"] = (stream, s) =>
- {
- var surface = SimpleServiceProvider.Current.GetInstance>().Invoke();
- return surface.GetImageForExport();
- };
-
- // Add a SVG converter
- StreamConverters["svg"] = (stream, s) =>
- {
- stream.Position = 0;
- try
- {
- return SvgImage.FromStream(stream).Image;
- }
- catch (Exception ex)
- {
- Log.Error("Can't load SVG", ex);
- }
-
- return null;
- };
-
- static Image DefaultConverter(Stream stream, string s)
- {
- stream.Position = 0;
- using var tmpImage = Image.FromStream(stream, true, true);
- Log.DebugFormat("Loaded bitmap with Size {0}x{1} and PixelFormat {2}", tmpImage.Width, tmpImage.Height, tmpImage.PixelFormat);
- return Clone(tmpImage, PixelFormat.Format32bppArgb);
- }
-
- // Fallback
- StreamConverters[string.Empty] = DefaultConverter;
- StreamConverters["gif"] = DefaultConverter;
- StreamConverters["bmp"] = DefaultConverter;
- StreamConverters["jpg"] = DefaultConverter;
- StreamConverters["jpeg"] = DefaultConverter;
- StreamConverters["png"] = DefaultConverter;
- StreamConverters["wmf"] = DefaultConverter;
-
- StreamConverters["ico"] = (stream, extension) =>
- {
- // Icon logic, try to get the Vista icon, else the biggest possible
- try
- {
- using Image tmpImage = ExtractVistaIcon(stream);
- if (tmpImage != null)
- {
- return Clone(tmpImage, PixelFormat.Format32bppArgb);
- }
- }
- catch (Exception vistaIconException)
- {
- Log.Warn("Can't read icon", vistaIconException);
- }
-
- try
- {
- // No vista icon, try normal icon
- stream.Position = 0;
- // We create a copy of the bitmap, so everything else can be disposed
- using Icon tmpIcon = new Icon(stream, new Size(1024, 1024));
- using Image tmpImage = tmpIcon.ToBitmap();
- return Clone(tmpImage, PixelFormat.Format32bppArgb);
- }
- catch (Exception iconException)
- {
- Log.Warn("Can't read icon", iconException);
- }
-
- stream.Position = 0;
- return DefaultConverter(stream, extension);
- };
- }
-
- public static IDictionary> StreamConverters { get; } = new Dictionary>();
-
- ///
- /// Make sure the image is orientated correctly
- ///
- ///
- public static void Orientate(Image image)
- {
- if (!CoreConfig.ProcessEXIFOrientation)
- {
- return;
- }
-
- try
- {
- // Get the index of the orientation property.
- int orientationIndex = Array.IndexOf(image.PropertyIdList, ExifOrientationId);
- // If there is no such property, return Unknown.
- if (orientationIndex < 0)
- {
- return;
- }
-
- PropertyItem item = image.GetPropertyItem(ExifOrientationId);
-
- ExifOrientations orientation = (ExifOrientations) item.Value[0];
- // Orient the image.
- switch (orientation)
- {
- case ExifOrientations.Unknown:
- case ExifOrientations.TopLeft:
- break;
- case ExifOrientations.TopRight:
- image.RotateFlip(RotateFlipType.RotateNoneFlipX);
- break;
- case ExifOrientations.BottomRight:
- image.RotateFlip(RotateFlipType.Rotate180FlipNone);
- break;
- case ExifOrientations.BottomLeft:
- image.RotateFlip(RotateFlipType.RotateNoneFlipY);
- break;
- case ExifOrientations.LeftTop:
- image.RotateFlip(RotateFlipType.Rotate90FlipX);
- break;
- case ExifOrientations.RightTop:
- image.RotateFlip(RotateFlipType.Rotate90FlipNone);
- break;
- case ExifOrientations.RightBottom:
- image.RotateFlip(RotateFlipType.Rotate90FlipY);
- break;
- case ExifOrientations.LeftBottom:
- image.RotateFlip(RotateFlipType.Rotate270FlipNone);
- break;
- }
-
- // Set the orientation to be normal, as we rotated the image.
- item.Value[0] = (byte) ExifOrientations.TopLeft;
- image.SetPropertyItem(item);
- }
- catch (Exception orientEx)
- {
- Log.Warn("Problem orientating the image: ", orientEx);
- }
- }
-
- ///
- /// Create a Thumbnail
- ///
- ///
- ///
- ///
- ///
- ///
- ///
- public static Image CreateThumbnail(Image image, int thumbWidth, int thumbHeight, int maxWidth = -1, int maxHeight = -1)
- {
- int srcWidth = image.Width;
- int srcHeight = image.Height;
- if (thumbHeight < 0)
- {
- thumbHeight = (int) (thumbWidth * (srcHeight / (float) srcWidth));
- }
-
- if (thumbWidth < 0)
- {
- thumbWidth = (int) (thumbHeight * (srcWidth / (float) srcHeight));
- }
-
- if (maxWidth > 0 && thumbWidth > maxWidth)
- {
- thumbWidth = Math.Min(thumbWidth, maxWidth);
- thumbHeight = (int) (thumbWidth * (srcHeight / (float) srcWidth));
- }
-
- if (maxHeight > 0 && thumbHeight > maxHeight)
- {
- thumbHeight = Math.Min(thumbHeight, maxHeight);
- thumbWidth = (int) (thumbHeight * (srcWidth / (float) srcHeight));
- }
-
- Bitmap bmp = new Bitmap(thumbWidth, thumbHeight);
- using (Graphics graphics = Graphics.FromImage(bmp))
- {
- graphics.SmoothingMode = SmoothingMode.HighQuality;
- graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
- graphics.CompositingQuality = CompositingQuality.HighQuality;
- graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
- Rectangle rectDestination = new Rectangle(0, 0, thumbWidth, thumbHeight);
- graphics.DrawImage(image, rectDestination, 0, 0, srcWidth, srcHeight, GraphicsUnit.Pixel);
- }
-
- return bmp;
- }
-
- ///
- /// Crops the image to the specified rectangle
- ///
- /// Image to crop
- /// Rectangle with bitmap coordinates, will be "intersected" to the bitmap
- public static bool Crop(ref Image image, ref Rectangle cropRectangle)
- {
- if (image is Bitmap && (image.Width * image.Height > 0))
- {
- cropRectangle.Intersect(new Rectangle(0, 0, image.Width, image.Height));
- if (cropRectangle.Width != 0 || cropRectangle.Height != 0)
- {
- Image returnImage = CloneArea(image, cropRectangle, PixelFormat.DontCare);
- image.Dispose();
- image = returnImage;
- return true;
- }
- }
-
- Log.Warn("Can't crop a null/zero size image!");
- return false;
- }
-
- ///
- /// Private helper method for the FindAutoCropRectangle
- ///
- ///
- ///
- ///
- /// Rectangle
- private static Rectangle FindAutoCropRectangle(IFastBitmap fastBitmap, Point colorPoint, int cropDifference)
- {
- Rectangle cropRectangle = Rectangle.Empty;
- Color referenceColor = fastBitmap.GetColorAt(colorPoint.X, colorPoint.Y);
- Point min = new Point(int.MaxValue, int.MaxValue);
- Point max = new Point(int.MinValue, int.MinValue);
-
- if (cropDifference > 0)
- {
- for (int y = 0; y < fastBitmap.Height; y++)
- {
- for (int x = 0; x < fastBitmap.Width; x++)
- {
- Color currentColor = fastBitmap.GetColorAt(x, y);
- int diffR = Math.Abs(currentColor.R - referenceColor.R);
- int diffG = Math.Abs(currentColor.G - referenceColor.G);
- int diffB = Math.Abs(currentColor.B - referenceColor.B);
- if ((diffR + diffG + diffB) / 3 <= cropDifference)
- {
- continue;
- }
-
- if (x < min.X) min.X = x;
- if (y < min.Y) min.Y = y;
- if (x > max.X) max.X = x;
- if (y > max.Y) max.Y = y;
- }
- }
- }
- else
- {
- for (int y = 0; y < fastBitmap.Height; y++)
- {
- for (int x = 0; x < fastBitmap.Width; x++)
- {
- Color currentColor = fastBitmap.GetColorAt(x, y);
- if (!referenceColor.Equals(currentColor))
- {
- continue;
- }
-
- if (x < min.X) min.X = x;
- if (y < min.Y) min.Y = y;
- if (x > max.X) max.X = x;
- if (y > max.Y) max.Y = y;
- }
- }
- }
-
- if (!(Point.Empty.Equals(min) && max.Equals(new Point(fastBitmap.Width - 1, fastBitmap.Height - 1))))
- {
- if (!(min.X == int.MaxValue || min.Y == int.MaxValue || max.X == int.MinValue || min.X == int.MinValue))
- {
- cropRectangle = new Rectangle(min.X, min.Y, max.X - min.X + 1, max.Y - min.Y + 1);
- }
- }
-
- return cropRectangle;
- }
-
- ///
- /// Get a rectangle for the image which crops the image of all colors equal to that on 0,0
- ///
- ///
- ///
- /// Rectangle
- public static Rectangle FindAutoCropRectangle(Image image, int cropDifference)
- {
- Rectangle cropRectangle = Rectangle.Empty;
- var checkPoints = new List
- {
- new Point(0, 0),
- new Point(0, image.Height - 1),
- new Point(image.Width - 1, 0),
- new Point(image.Width - 1, image.Height - 1)
- };
- // Top Left
- // Bottom Left
- // Top Right
- // Bottom Right
- using (IFastBitmap fastBitmap = FastBitmap.Create((Bitmap) image))
- {
- // find biggest area
- foreach (Point checkPoint in checkPoints)
- {
- var currentRectangle = FindAutoCropRectangle(fastBitmap, checkPoint, cropDifference);
- if (currentRectangle.Width * currentRectangle.Height > cropRectangle.Width * cropRectangle.Height)
- {
- cropRectangle = currentRectangle;
- }
- }
- }
-
- return cropRectangle;
- }
-
- ///
- /// Load an image from file
- ///
- ///
- ///
- public static Image LoadImage(string filename)
- {
- if (string.IsNullOrEmpty(filename))
- {
- return null;
- }
-
- if (!File.Exists(filename))
- {
- return null;
- }
-
- Image fileImage;
- Log.InfoFormat("Loading image from file {0}", filename);
- // Fixed lock problem Bug #3431881
- using (Stream imageFileStream = File.OpenRead(filename))
- {
- fileImage = FromStream(imageFileStream, Path.GetExtension(filename));
- }
-
- if (fileImage != null)
- {
- Log.InfoFormat("Information about file {0}: {1}x{2}-{3} Resolution {4}x{5}", filename, fileImage.Width, fileImage.Height, fileImage.PixelFormat,
- fileImage.HorizontalResolution, fileImage.VerticalResolution);
- }
-
- return fileImage;
- }
-
- ///
- /// Based on: http://www.codeproject.com/KB/cs/IconExtractor.aspx
- /// And a hint from: http://www.codeproject.com/KB/cs/IconLib.aspx
- ///
- /// Stream with the icon information
- /// Bitmap with the Vista Icon (256x256)
- private static Bitmap ExtractVistaIcon(Stream iconStream)
- {
- const int sizeIconDir = 6;
- const int sizeIconDirEntry = 16;
- Bitmap bmpPngExtracted = null;
- try
- {
- byte[] srcBuf = new byte[iconStream.Length];
- iconStream.Read(srcBuf, 0, (int) iconStream.Length);
- int iCount = BitConverter.ToInt16(srcBuf, 4);
- for (int iIndex = 0; iIndex < iCount; iIndex++)
- {
- int iWidth = srcBuf[sizeIconDir + sizeIconDirEntry * iIndex];
- int iHeight = srcBuf[sizeIconDir + sizeIconDirEntry * iIndex + 1];
- if (iWidth == 0 && iHeight == 0)
- {
- int iImageSize = BitConverter.ToInt32(srcBuf, sizeIconDir + sizeIconDirEntry * iIndex + 8);
- int iImageOffset = BitConverter.ToInt32(srcBuf, sizeIconDir + sizeIconDirEntry * iIndex + 12);
- using MemoryStream destStream = new MemoryStream();
- destStream.Write(srcBuf, iImageOffset, iImageSize);
- destStream.Seek(0, SeekOrigin.Begin);
- bmpPngExtracted = new Bitmap(destStream); // This is PNG! :)
- break;
- }
- }
- }
- catch
- {
- return null;
- }
-
- return bmpPngExtracted;
- }
-
- ///
- /// See: http://msdn.microsoft.com/en-us/library/windows/desktop/ms648069%28v=vs.85%29.aspx
- ///
- /// The file (EXE or DLL) to get the icon from
- /// Index of the icon
- /// true if the large icon is wanted
- /// Icon
- public static Icon ExtractAssociatedIcon(string location, int index, bool takeLarge)
- {
- Shell32.ExtractIconEx(location, index, out var large, out var small, 1);
- Icon returnIcon = null;
- bool isLarge = false;
- bool isSmall = false;
- try
- {
- if (takeLarge && !IntPtr.Zero.Equals(large))
- {
- returnIcon = Icon.FromHandle(large);
- isLarge = true;
- }
- else if (!IntPtr.Zero.Equals(small))
- {
- returnIcon = Icon.FromHandle(small);
- isSmall = true;
- }
- else if (!IntPtr.Zero.Equals(large))
- {
- returnIcon = Icon.FromHandle(large);
- isLarge = true;
- }
- }
- finally
- {
- if (isLarge && !IntPtr.Zero.Equals(small))
- {
- User32.DestroyIcon(small);
- }
-
- if (isSmall && !IntPtr.Zero.Equals(large))
- {
- User32.DestroyIcon(large);
- }
- }
-
- return returnIcon;
- }
-
- ///
- /// Apply the effect to the bitmap
- ///
- /// Bitmap
- /// IEffect
- ///
- /// Bitmap
- public static Image ApplyEffect(Image sourceImage, IEffect effect, Matrix matrix)
- {
- var effects = new List
- {
- effect
- };
- return ApplyEffects(sourceImage, effects, matrix);
- }
-
- ///
- /// Apply the effects in the supplied order to the bitmap
- ///
- /// Bitmap
- /// List of IEffect
- ///
- /// Bitmap
- public static Image ApplyEffects(Image sourceImage, IEnumerable effects, Matrix matrix)
- {
- var currentImage = sourceImage;
- bool disposeImage = false;
- foreach (var effect in effects)
- {
- var tmpImage = effect.Apply(currentImage, matrix);
- if (tmpImage != null)
- {
- if (disposeImage)
- {
- currentImage.Dispose();
- }
-
- currentImage = tmpImage;
- // Make sure the "new" image is disposed
- disposeImage = true;
- }
- }
-
- return currentImage;
- }
-
- ///
- /// Helper method for the tornedge
- ///
- /// Path to draw to
- /// Points for the lines to draw
- private static void DrawLines(GraphicsPath path, List points)
- {
- path.AddLine(points[0], points[1]);
- for (int i = 0; i < points.Count - 1; i++)
- {
- path.AddLine(points[i], points[i + 1]);
- }
- }
-
- ///
- /// Make the picture look like it's torn
- ///
- /// Bitmap to make torn edge off
- /// How large (height) is each tooth
- /// How wide is a horizontal tooth
- /// How wide is a vertical tooth
- /// bool[] with information on if the edge needs torn or not. Order is clockwise: 0=top,1=right,2=bottom,3=left
- /// Changed bitmap
- public static Image CreateTornEdge(Image sourceImage, int toothHeight, int horizontalToothRange, int verticalToothRange, bool[] edges)
- {
- Image returnImage = CreateEmpty(sourceImage.Width, sourceImage.Height, PixelFormat.Format32bppArgb, Color.Empty, sourceImage.HorizontalResolution,
- sourceImage.VerticalResolution);
- using (var path = new GraphicsPath())
- {
- Random random = new Random();
- int horizontalRegions = (int) Math.Round((float) sourceImage.Width / horizontalToothRange);
- int verticalRegions = (int) Math.Round((float) sourceImage.Height / verticalToothRange);
-
- Point topLeft = new Point(0, 0);
- Point topRight = new Point(sourceImage.Width, 0);
- Point bottomLeft = new Point(0, sourceImage.Height);
- Point bottomRight = new Point(sourceImage.Width, sourceImage.Height);
-
- List points = new List();
-
- if (edges[0])
- {
- // calculate starting point only if the left edge is torn
- if (!edges[3])
- {
- points.Add(topLeft);
- }
- else
- {
- points.Add(new Point(random.Next(1, toothHeight), random.Next(1, toothHeight)));
- }
-
- for (int i = 1; i < horizontalRegions - 1; i++)
- {
- points.Add(new Point(i * horizontalToothRange, random.Next(1, toothHeight)));
- }
-
- points.Add(new Point(sourceImage.Width - random.Next(1, toothHeight), random.Next(1, toothHeight)));
- }
- else
- {
- // set start & endpoint to be the default "whole-line"
- points.Add(topLeft);
- points.Add(topRight);
- }
-
- // Right
- if (edges[1])
- {
- for (int i = 1; i < verticalRegions - 1; i++)
- {
- points.Add(new Point(sourceImage.Width - random.Next(1, toothHeight), i * verticalToothRange));
- }
-
- points.Add(new Point(sourceImage.Width - random.Next(1, toothHeight), sourceImage.Height - random.Next(1, toothHeight)));
- }
- else
- {
- // correct previous ending point
- points[points.Count - 1] = topRight;
- // set endpoint to be the default "whole-line"
- points.Add(bottomRight);
- }
-
- // Bottom
- if (edges[2])
- {
- for (int i = 1; i < horizontalRegions - 1; i++)
- {
- points.Add(new Point(sourceImage.Width - i * horizontalToothRange, sourceImage.Height - random.Next(1, toothHeight)));
- }
-
- points.Add(new Point(random.Next(1, toothHeight), sourceImage.Height - random.Next(1, toothHeight)));
- }
- else
- {
- // correct previous ending point
- points[points.Count - 1] = bottomRight;
- // set endpoint to be the default "whole-line"
- points.Add(bottomLeft);
- }
-
- // Left
- if (edges[3])
- {
- // One fewer as the end point is the starting point
- for (int i = 1; i < verticalRegions - 1; i++)
- {
- points.Add(new Point(random.Next(1, toothHeight), points[points.Count - 1].Y - verticalToothRange));
- }
- }
- else
- {
- // correct previous ending point
- points[points.Count - 1] = bottomLeft;
- // set endpoint to be the default "whole-line"
- points.Add(topLeft);
- }
-
- // End point always is the starting point
- points[points.Count - 1] = points[0];
-
- DrawLines(path, points);
-
- path.CloseFigure();
-
- // Draw the created figure with the original image by using a TextureBrush so we have anti-aliasing
- using Graphics graphics = Graphics.FromImage(returnImage);
- graphics.SmoothingMode = SmoothingMode.HighQuality;
- graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
- graphics.CompositingQuality = CompositingQuality.HighQuality;
- graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
- using Brush brush = new TextureBrush(sourceImage);
- // Important note: If the target wouldn't be at 0,0 we need to translate-transform!!
- graphics.FillPath(brush, path);
- }
-
- return returnImage;
- }
-
- ///
- /// Apply BoxBlur to the destinationBitmap
- ///
- /// Bitmap to blur
- /// Must be ODD!
- public static void ApplyBoxBlur(Bitmap destinationBitmap, int range)
- {
- // We only need one fastbitmap as we use it as source and target (the reading is done for one line H/V, writing after "parsing" one line H/V)
- using IFastBitmap fastBitmap = FastBitmap.Create(destinationBitmap);
- ApplyBoxBlur(fastBitmap, range);
- }
-
- ///
- /// Apply BoxBlur to the fastBitmap
- ///
- /// IFastBitmap to blur
- /// Must be ODD!
- public static void ApplyBoxBlur(IFastBitmap fastBitmap, int range)
- {
- // Range must be odd!
- if ((range & 1) == 0)
- {
- range++;
- }
-
- if (range <= 1)
- {
- return;
- }
-
- // Box blurs are frequently used to approximate a Gaussian blur.
- // By the central limit theorem, if applied 3 times on the same image, a box blur approximates the Gaussian kernel to within about 3%, yielding the same result as a quadratic convolution kernel.
- // This might be true, but the GDI+ BlurEffect doesn't look the same, a 2x blur is more simular and we only make 2x Box-Blur.
- // (Might also be a mistake in our blur, but for now it looks great)
- if (fastBitmap.HasAlphaChannel)
- {
- BoxBlurHorizontalAlpha(fastBitmap, range);
- BoxBlurVerticalAlpha(fastBitmap, range);
- BoxBlurHorizontalAlpha(fastBitmap, range);
- BoxBlurVerticalAlpha(fastBitmap, range);
- }
- else
- {
- BoxBlurHorizontal(fastBitmap, range);
- BoxBlurVertical(fastBitmap, range);
- BoxBlurHorizontal(fastBitmap, range);
- BoxBlurVertical(fastBitmap, range);
- }
- }
-
- ///
- /// BoxBlurHorizontal is a private helper method for the BoxBlur
- ///
- /// Target BitmapBuffer
- /// Range must be odd!
- private static void BoxBlurHorizontal(IFastBitmap targetFastBitmap, int range)
- {
- if (targetFastBitmap.HasAlphaChannel)
- {
- throw new NotSupportedException("BoxBlurHorizontal should NOT be called for bitmaps with alpha channel");
- }
-
- int halfRange = range / 2;
- Color[] newColors = new Color[targetFastBitmap.Width];
- byte[] tmpColor = new byte[3];
- for (int y = targetFastBitmap.Top; y < targetFastBitmap.Bottom; y++)
- {
- int hits = 0;
- int r = 0;
- int g = 0;
- int b = 0;
- for (int x = targetFastBitmap.Left - halfRange; x < targetFastBitmap.Right; x++)
- {
- int oldPixel = x - halfRange - 1;
- if (oldPixel >= targetFastBitmap.Left)
- {
- targetFastBitmap.GetColorAt(oldPixel, y, tmpColor);
- r -= tmpColor[FastBitmap.ColorIndexR];
- g -= tmpColor[FastBitmap.ColorIndexG];
- b -= tmpColor[FastBitmap.ColorIndexB];
- hits--;
- }
-
- int newPixel = x + halfRange;
- if (newPixel < targetFastBitmap.Right)
- {
- targetFastBitmap.GetColorAt(newPixel, y, tmpColor);
- r += tmpColor[FastBitmap.ColorIndexR];
- g += tmpColor[FastBitmap.ColorIndexG];
- b += tmpColor[FastBitmap.ColorIndexB];
- hits++;
- }
-
- if (x >= targetFastBitmap.Left)
- {
- newColors[x - targetFastBitmap.Left] = Color.FromArgb(255, (byte) (r / hits), (byte) (g / hits), (byte) (b / hits));
- }
- }
-
- for (int x = targetFastBitmap.Left; x < targetFastBitmap.Right; x++)
- {
- targetFastBitmap.SetColorAt(x, y, newColors[x - targetFastBitmap.Left]);
- }
- }
- }
-
- ///
- /// BoxBlurHorizontal is a private helper method for the BoxBlur, only for IFastBitmaps with alpha channel
- ///
- /// Target BitmapBuffer
- /// Range must be odd!
- private static void BoxBlurHorizontalAlpha(IFastBitmap targetFastBitmap, int range)
- {
- if (!targetFastBitmap.HasAlphaChannel)
- {
- throw new NotSupportedException("BoxBlurHorizontalAlpha should be called for bitmaps with alpha channel");
- }
-
- int halfRange = range / 2;
- Color[] newColors = new Color[targetFastBitmap.Width];
- byte[] tmpColor = new byte[4];
- for (int y = targetFastBitmap.Top; y < targetFastBitmap.Bottom; y++)
- {
- int hits = 0;
- int a = 0;
- int r = 0;
- int g = 0;
- int b = 0;
- for (int x = targetFastBitmap.Left - halfRange; x < targetFastBitmap.Right; x++)
- {
- int oldPixel = x - halfRange - 1;
- if (oldPixel >= targetFastBitmap.Left)
- {
- targetFastBitmap.GetColorAt(oldPixel, y, tmpColor);
- a -= tmpColor[FastBitmap.ColorIndexA];
- r -= tmpColor[FastBitmap.ColorIndexR];
- g -= tmpColor[FastBitmap.ColorIndexG];
- b -= tmpColor[FastBitmap.ColorIndexB];
- hits--;
- }
-
- int newPixel = x + halfRange;
- if (newPixel < targetFastBitmap.Right)
- {
- targetFastBitmap.GetColorAt(newPixel, y, tmpColor);
- a += tmpColor[FastBitmap.ColorIndexA];
- r += tmpColor[FastBitmap.ColorIndexR];
- g += tmpColor[FastBitmap.ColorIndexG];
- b += tmpColor[FastBitmap.ColorIndexB];
- hits++;
- }
-
- if (x >= targetFastBitmap.Left)
- {
- newColors[x - targetFastBitmap.Left] = Color.FromArgb((byte) (a / hits), (byte) (r / hits), (byte) (g / hits), (byte) (b / hits));
- }
- }
-
- for (int x = targetFastBitmap.Left; x < targetFastBitmap.Right; x++)
- {
- targetFastBitmap.SetColorAt(x, y, newColors[x - targetFastBitmap.Left]);
- }
- }
- }
-
- ///
- /// BoxBlurVertical is a private helper method for the BoxBlur
- ///
- /// BitmapBuffer which previously was created with BoxBlurHorizontal
- /// Range must be odd!
- private static void BoxBlurVertical(IFastBitmap targetFastBitmap, int range)
- {
- if (targetFastBitmap.HasAlphaChannel)
- {
- throw new NotSupportedException("BoxBlurVertical should NOT be called for bitmaps with alpha channel");
- }
-
- int halfRange = range / 2;
- Color[] newColors = new Color[targetFastBitmap.Height];
- byte[] tmpColor = new byte[4];
- for (int x = targetFastBitmap.Left; x < targetFastBitmap.Right; x++)
- {
- int hits = 0;
- int r = 0;
- int g = 0;
- int b = 0;
- for (int y = targetFastBitmap.Top - halfRange; y < targetFastBitmap.Bottom; y++)
- {
- int oldPixel = y - halfRange - 1;
- if (oldPixel >= targetFastBitmap.Top)
- {
- targetFastBitmap.GetColorAt(x, oldPixel, tmpColor);
- r -= tmpColor[FastBitmap.ColorIndexR];
- g -= tmpColor[FastBitmap.ColorIndexG];
- b -= tmpColor[FastBitmap.ColorIndexB];
- hits--;
- }
-
- int newPixel = y + halfRange;
- if (newPixel < targetFastBitmap.Bottom)
- {
- targetFastBitmap.GetColorAt(x, newPixel, tmpColor);
- r += tmpColor[FastBitmap.ColorIndexR];
- g += tmpColor[FastBitmap.ColorIndexG];
- b += tmpColor[FastBitmap.ColorIndexB];
- hits++;
- }
-
- if (y >= targetFastBitmap.Top)
- {
- newColors[y - targetFastBitmap.Top] = Color.FromArgb(255, (byte) (r / hits), (byte) (g / hits), (byte) (b / hits));
- }
- }
-
- for (int y = targetFastBitmap.Top; y < targetFastBitmap.Bottom; y++)
- {
- targetFastBitmap.SetColorAt(x, y, newColors[y - targetFastBitmap.Top]);
- }
- }
- }
-
- ///
- /// BoxBlurVertical is a private helper method for the BoxBlur
- ///
- /// BitmapBuffer which previously was created with BoxBlurHorizontal
- /// Range must be odd!
- private static void BoxBlurVerticalAlpha(IFastBitmap targetFastBitmap, int range)
- {
- if (!targetFastBitmap.HasAlphaChannel)
- {
- throw new NotSupportedException("BoxBlurVerticalAlpha should be called for bitmaps with alpha channel");
- }
-
- int halfRange = range / 2;
- Color[] newColors = new Color[targetFastBitmap.Height];
- byte[] tmpColor = new byte[4];
- for (int x = targetFastBitmap.Left; x < targetFastBitmap.Right; x++)
- {
- int hits = 0;
- int a = 0;
- int r = 0;
- int g = 0;
- int b = 0;
- for (int y = targetFastBitmap.Top - halfRange; y < targetFastBitmap.Bottom; y++)
- {
- int oldPixel = y - halfRange - 1;
- if (oldPixel >= targetFastBitmap.Top)
- {
- targetFastBitmap.GetColorAt(x, oldPixel, tmpColor);
- a -= tmpColor[FastBitmap.ColorIndexA];
- r -= tmpColor[FastBitmap.ColorIndexR];
- g -= tmpColor[FastBitmap.ColorIndexG];
- b -= tmpColor[FastBitmap.ColorIndexB];
- hits--;
- }
-
- int newPixel = y + halfRange;
- if (newPixel < targetFastBitmap.Bottom)
- {
- //int colorg = pixels[index + newPixelOffset];
- targetFastBitmap.GetColorAt(x, newPixel, tmpColor);
- a += tmpColor[FastBitmap.ColorIndexA];
- r += tmpColor[FastBitmap.ColorIndexR];
- g += tmpColor[FastBitmap.ColorIndexG];
- b += tmpColor[FastBitmap.ColorIndexB];
- hits++;
- }
-
- if (y >= targetFastBitmap.Top)
- {
- newColors[y - targetFastBitmap.Top] = Color.FromArgb((byte) (a / hits), (byte) (r / hits), (byte) (g / hits), (byte) (b / hits));
- }
- }
-
- for (int y = targetFastBitmap.Top; y < targetFastBitmap.Bottom; y++)
- {
- targetFastBitmap.SetColorAt(x, y, newColors[y - targetFastBitmap.Top]);
- }
- }
- }
-
- ///
- /// This method fixes the problem that we can't apply a filter outside the target bitmap,
- /// therefor the filtered-bitmap will be shifted if we try to draw it outside the target bitmap.
- /// It will also account for the Invert flag.
- ///
- ///
- ///
- ///
- ///
- public static Rectangle CreateIntersectRectangle(Size applySize, Rectangle rect, bool invert)
- {
- Rectangle myRect;
- if (invert)
- {
- myRect = new Rectangle(0, 0, applySize.Width, applySize.Height);
- }
- else
- {
- Rectangle applyRect = new Rectangle(0, 0, applySize.Width, applySize.Height);
- myRect = new Rectangle(rect.X, rect.Y, rect.Width, rect.Height);
- myRect.Intersect(applyRect);
- }
-
- return myRect;
- }
-
- ///
- /// Create a new bitmap where the sourceBitmap has a shadow
- ///
- /// Bitmap to make a shadow on
- /// How dark is the shadow
- /// Size of the shadow
- /// What pixel format must the returning bitmap have
- ///
- /// The transform matrix which describes how the elements need to be transformed to stay at the same location
- /// Bitmap with the shadow, is bigger than the sourceBitmap!!
- public static Bitmap CreateShadow(Image sourceBitmap, float darkness, int shadowSize, Point shadowOffset, Matrix matrix, PixelFormat targetPixelformat)
- {
- Point offset = shadowOffset;
- offset.X += shadowSize - 1;
- offset.Y += shadowSize - 1;
- matrix.Translate(offset.X, offset.Y, MatrixOrder.Append);
- // Create a new "clean" image
- Bitmap returnImage = CreateEmpty(sourceBitmap.Width + shadowSize * 2, sourceBitmap.Height + shadowSize * 2, targetPixelformat, Color.Empty,
- sourceBitmap.HorizontalResolution, sourceBitmap.VerticalResolution);
- // Make sure the shadow is odd, there is no reason for an even blur!
- if ((shadowSize & 1) == 0)
- {
- shadowSize++;
- }
-
- bool useGdiBlur = GDIplus.IsBlurPossible(shadowSize);
- // Create "mask" for the shadow
- ColorMatrix maskMatrix = new ColorMatrix
- {
- Matrix00 = 0,
- Matrix11 = 0,
- Matrix22 = 0
- };
- if (useGdiBlur)
- {
- maskMatrix.Matrix33 = darkness + 0.1f;
- }
- else
- {
- maskMatrix.Matrix33 = darkness;
- }
-
- Rectangle shadowRectangle = new Rectangle(new Point(shadowSize, shadowSize), sourceBitmap.Size);
- ApplyColorMatrix((Bitmap) sourceBitmap, Rectangle.Empty, returnImage, shadowRectangle, maskMatrix);
-
- // blur "shadow", apply to whole new image
- if (useGdiBlur)
- {
- // Use GDI Blur
- Rectangle newImageRectangle = new Rectangle(0, 0, returnImage.Width, returnImage.Height);
- GDIplus.ApplyBlur(returnImage, newImageRectangle, shadowSize + 1, false);
- }
- else
- {
- // try normal software blur
- //returnImage = CreateBlur(returnImage, newImageRectangle, true, shadowSize, 1d, false, newImageRectangle);
- ApplyBoxBlur(returnImage, shadowSize);
- }
-
- // Draw the original image over the shadow
- using (Graphics graphics = Graphics.FromImage(returnImage))
- {
- // Make sure we draw with the best quality!
- graphics.SmoothingMode = SmoothingMode.HighQuality;
- graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
- graphics.CompositingQuality = CompositingQuality.HighQuality;
- graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
- // draw original with a TextureBrush so we have nice antialiasing!
- using Brush textureBrush = new TextureBrush(sourceBitmap, WrapMode.Clamp);
- // We need to do a translate-transform otherwise the image is wrapped
- graphics.TranslateTransform(offset.X, offset.Y);
- graphics.FillRectangle(textureBrush, 0, 0, sourceBitmap.Width, sourceBitmap.Height);
- }
-
- return returnImage;
- }
-
- ///
- /// Return negative of Bitmap
- ///
- /// Bitmap to create a negative off
- /// Negative bitmap
- public static Bitmap CreateNegative(Image sourceImage)
- {
- Bitmap clone = (Bitmap) Clone(sourceImage);
- ColorMatrix invertMatrix = new ColorMatrix(new[]
- {
- new float[]
- {
- -1, 0, 0, 0, 0
- },
- new float[]
- {
- 0, -1, 0, 0, 0
- },
- new float[]
- {
- 0, 0, -1, 0, 0
- },
- new float[]
- {
- 0, 0, 0, 1, 0
- },
- new float[]
- {
- 1, 1, 1, 1, 1
- }
- });
- ApplyColorMatrix(clone, invertMatrix);
- return clone;
- }
-
- ///
- /// Apply a color matrix to the image
- ///
- /// Image to apply matrix to
- /// ColorMatrix to apply
- public static void ApplyColorMatrix(Bitmap source, ColorMatrix colorMatrix)
- {
- ApplyColorMatrix(source, Rectangle.Empty, source, Rectangle.Empty, colorMatrix);
- }
-
- ///
- /// Apply a color matrix by copying from the source to the destination
- ///
- /// Image to copy from
- /// Rectangle to copy from
- /// Rectangle to copy to
- /// Image to copy to
- /// ColorMatrix to apply
- public static void ApplyColorMatrix(Bitmap source, Rectangle sourceRect, Bitmap dest, Rectangle destRect, ColorMatrix colorMatrix)
- {
- using ImageAttributes imageAttributes = new ImageAttributes();
- imageAttributes.ClearColorMatrix();
- imageAttributes.SetColorMatrix(colorMatrix);
- ApplyImageAttributes(source, sourceRect, dest, destRect, imageAttributes);
- }
-
- ///
- /// Apply a color matrix by copying from the source to the destination
- ///
- /// Image to copy from
- /// Rectangle to copy from
- /// Rectangle to copy to
- /// Image to copy to
- /// ImageAttributes to apply
- public static void ApplyImageAttributes(Bitmap source, Rectangle sourceRect, Bitmap dest, Rectangle destRect, ImageAttributes imageAttributes)
- {
- if (sourceRect == Rectangle.Empty)
- {
- sourceRect = new Rectangle(0, 0, source.Width, source.Height);
- }
-
- if (dest == null)
- {
- dest = source;
- }
-
- if (destRect == Rectangle.Empty)
- {
- destRect = new Rectangle(0, 0, dest.Width, dest.Height);
- }
-
- using Graphics graphics = Graphics.FromImage(dest);
- // Make sure we draw with the best quality!
- graphics.SmoothingMode = SmoothingMode.HighQuality;
- graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
- graphics.CompositingQuality = CompositingQuality.HighQuality;
- graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
- graphics.CompositingMode = CompositingMode.SourceCopy;
-
- graphics.DrawImage(source, destRect, sourceRect.X, sourceRect.Y, sourceRect.Width, sourceRect.Height, GraphicsUnit.Pixel, imageAttributes);
- }
-
- ///
- /// Returns a b/w of Bitmap
- ///
- /// Bitmap to create a b/w of
- /// Threshold for monochrome filter (0 - 255), lower value means less black
- /// b/w bitmap
- public static Bitmap CreateMonochrome(Image sourceImage, byte threshold)
- {
- using IFastBitmap fastBitmap = FastBitmap.CreateCloneOf(sourceImage, sourceImage.PixelFormat);
- for (int y = 0; y < fastBitmap.Height; y++)
- {
- for (int x = 0; x < fastBitmap.Width; x++)
- {
- Color color = fastBitmap.GetColorAt(x, y);
- int colorBrightness = (color.R + color.G + color.B) / 3 > threshold ? 255 : 0;
- Color monoColor = Color.FromArgb(color.A, colorBrightness, colorBrightness, colorBrightness);
- fastBitmap.SetColorAt(x, y, monoColor);
- }
- }
-
- return fastBitmap.UnlockAndReturnBitmap();
- }
-
- ///
- /// Create a new bitmap where the sourceBitmap has a Simple border around it
- ///
- /// Bitmap to make a border on
- /// Size of the border
- /// Color of the border
- /// What pixel format must the returning bitmap have
- /// The transform matrix which describes how the elements need to be transformed to stay at the same location
- /// Bitmap with the shadow, is bigger than the sourceBitmap!!
- public static Image CreateBorder(Image sourceImage, int borderSize, Color borderColor, PixelFormat targetPixelformat, Matrix matrix)
- {
- // "return" the shifted offset, so the caller can e.g. move elements
- Point offset = new Point(borderSize, borderSize);
- matrix.Translate(offset.X, offset.Y, MatrixOrder.Append);
-
- // Create a new "clean" image
- Bitmap newImage = CreateEmpty(sourceImage.Width + borderSize * 2, sourceImage.Height + borderSize * 2, targetPixelformat, Color.Empty, sourceImage.HorizontalResolution,
- sourceImage.VerticalResolution);
- using (Graphics graphics = Graphics.FromImage(newImage))
- {
- // Make sure we draw with the best quality!
- graphics.SmoothingMode = SmoothingMode.HighQuality;
- graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
- graphics.CompositingQuality = CompositingQuality.HighQuality;
- graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
- using (GraphicsPath path = new GraphicsPath())
- {
- path.AddRectangle(new Rectangle(borderSize >> 1, borderSize >> 1, newImage.Width - borderSize, newImage.Height - borderSize));
- using Pen pen = new Pen(borderColor, borderSize)
- {
- LineJoin = LineJoin.Round,
- StartCap = LineCap.Round,
- EndCap = LineCap.Round
- };
- graphics.DrawPath(pen, path);
- }
-
- // draw original with a TextureBrush so we have nice antialiasing!
- using Brush textureBrush = new TextureBrush(sourceImage, WrapMode.Clamp);
- // We need to do a translate-tranform otherwise the image is wrapped
- graphics.TranslateTransform(offset.X, offset.Y);
- graphics.FillRectangle(textureBrush, 0, 0, sourceImage.Width, sourceImage.Height);
- }
-
- return newImage;
- }
-
- ///
- /// Create ImageAttributes to modify
- ///
- ///
- ///
- ///
- /// ImageAttributes
- public static ImageAttributes CreateAdjustAttributes(float brightness, float contrast, float gamma)
- {
- float adjustedBrightness = brightness - 1.0f;
- ColorMatrix applyColorMatrix = new ColorMatrix(
- new[]
- {
- new[]
- {
- contrast, 0, 0, 0, 0
- }, // scale red
- new[]
- {
- 0, contrast, 0, 0, 0
- }, // scale green
- new[]
- {
- 0, 0, contrast, 0, 0
- }, // scale blue
- new[]
- {
- 0, 0, 0, 1.0f, 0
- }, // don't scale alpha
- new[]
- {
- adjustedBrightness, adjustedBrightness, adjustedBrightness, 0, 1
- }
- });
-
- //create some image attributes
- ImageAttributes attributes = new ImageAttributes();
- attributes.ClearColorMatrix();
- attributes.SetColorMatrix(applyColorMatrix, ColorMatrixFlag.Default, ColorAdjustType.Bitmap);
- attributes.SetGamma(gamma, ColorAdjustType.Bitmap);
- return attributes;
- }
-
- ///
- /// Adjust the brightness, contract or gamma of an image.
- /// Use the value "1.0f" for no changes.
- ///
- /// Original bitmap
- ///
- ///
- ///
- /// Bitmap with grayscale
- public static Image Adjust(Image sourceImage, float brightness, float contrast, float gamma)
- {
- //create a blank bitmap the same size as original
- // If using 8bpp than the following exception comes: A Graphics object cannot be created from an image that has an indexed pixel format.
- Bitmap newBitmap = CreateEmpty(sourceImage.Width, sourceImage.Height, PixelFormat.Format24bppRgb, Color.Empty, sourceImage.HorizontalResolution,
- sourceImage.VerticalResolution);
- using (ImageAttributes adjustAttributes = CreateAdjustAttributes(brightness, contrast, gamma))
- {
- ApplyImageAttributes((Bitmap) sourceImage, Rectangle.Empty, newBitmap, Rectangle.Empty, adjustAttributes);
- }
-
- return newBitmap;
- }
-
- ///
- /// Create a new bitmap where the sourceBitmap is in grayscale
- ///
- /// Original bitmap
- /// Bitmap with grayscale
- public static Image CreateGrayscale(Image sourceImage)
- {
- Bitmap clone = (Bitmap) Clone(sourceImage);
- 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
- }
- });
- ApplyColorMatrix(clone, grayscaleMatrix);
- return clone;
- }
-
- ///
- /// Checks if we support the pixel format
- ///
- /// PixelFormat to check
- /// bool if we support it
- public static bool SupportsPixelFormat(PixelFormat pixelformat)
- {
- return pixelformat.Equals(PixelFormat.Format32bppArgb) ||
- pixelformat.Equals(PixelFormat.Format32bppPArgb) ||
- pixelformat.Equals(PixelFormat.Format32bppRgb) ||
- pixelformat.Equals(PixelFormat.Format24bppRgb);
- }
-
- ///
- /// Wrapper for just cloning which calls the CloneArea
- ///
- /// Image to clone
- /// Bitmap with clone image data
- public static Image Clone(Image sourceImage)
- {
- if (sourceImage is Metafile)
- {
- return (Image) sourceImage.Clone();
- }
-
- return CloneArea(sourceImage, Rectangle.Empty, PixelFormat.DontCare);
- }
-
- ///
- /// Wrapper for just cloning & TargetFormat which calls the CloneArea
- ///
- /// Image to clone
- /// Target Format, use PixelFormat.DontCare if you want the original (or a default if the source PixelFormat is not supported)
- /// Bitmap with clone image data
- public static Bitmap Clone(Image sourceBitmap, PixelFormat targetFormat)
- {
- return CloneArea(sourceBitmap, Rectangle.Empty, targetFormat);
- }
-
- ///
- /// Clone an image, taking some rules into account:
- /// 1) When sourceRect is the whole bitmap there is a GDI+ bug in Clone
- /// Clone will than return the same PixelFormat as the source
- /// a quick workaround is using new Bitmap which uses a default of Format32bppArgb
- /// 2) When going from a transparent to a non transparent bitmap, we draw the background white!
- ///
- /// Source bitmap to clone
- /// Rectangle to copy from the source, use Rectangle.Empty for all
- /// Target Format, use PixelFormat.DontCare if you want the original (or a default if the source PixelFormat is not supported)
- ///
- public static Bitmap CloneArea(Image sourceImage, Rectangle sourceRect, PixelFormat targetFormat)
- {
- Bitmap newImage;
- Rectangle bitmapRect = new Rectangle(0, 0, sourceImage.Width, sourceImage.Height);
-
- // Make sure the source is not Rectangle.Empty
- if (Rectangle.Empty.Equals(sourceRect))
- {
- sourceRect = new Rectangle(0, 0, sourceImage.Width, sourceImage.Height);
- }
- else
- {
- sourceRect.Intersect(bitmapRect);
- }
-
- // If no pixelformat is supplied
- if (PixelFormat.DontCare == targetFormat || PixelFormat.Undefined == targetFormat)
- {
- if (SupportsPixelFormat(sourceImage.PixelFormat))
- {
- targetFormat = sourceImage.PixelFormat;
- }
- else if (Image.IsAlphaPixelFormat(sourceImage.PixelFormat))
- {
- targetFormat = PixelFormat.Format32bppArgb;
- }
- else
- {
- targetFormat = PixelFormat.Format24bppRgb;
- }
- }
-
- // check the target format
- if (!SupportsPixelFormat(targetFormat))
- {
- targetFormat = Image.IsAlphaPixelFormat(targetFormat) ? PixelFormat.Format32bppArgb : PixelFormat.Format24bppRgb;
- }
-
- bool destinationIsTransparent = Image.IsAlphaPixelFormat(targetFormat);
- bool sourceIsTransparent = Image.IsAlphaPixelFormat(sourceImage.PixelFormat);
- bool fromTransparentToNon = !destinationIsTransparent && sourceIsTransparent;
- bool isBitmap = sourceImage is Bitmap;
- bool isAreaEqual = sourceRect.Equals(bitmapRect);
- if (isAreaEqual || fromTransparentToNon || !isBitmap)
- {
- // Rule 1: if the areas are equal, always copy ourselves
- newImage = new Bitmap(bitmapRect.Width, bitmapRect.Height, targetFormat);
- // Make sure both images have the same resolution
- newImage.SetResolution(sourceImage.HorizontalResolution, sourceImage.VerticalResolution);
-
- using Graphics graphics = Graphics.FromImage(newImage);
- if (fromTransparentToNon)
- {
- // Rule 2: Make sure the background color is white
- graphics.Clear(Color.White);
- }
-
- // decide fastest copy method
- if (isAreaEqual)
- {
- graphics.DrawImageUnscaled(sourceImage, 0, 0);
- }
- else
- {
- graphics.DrawImage(sourceImage, 0, 0, sourceRect, GraphicsUnit.Pixel);
- }
- }
- else
- {
- // Let GDI+ decide how to convert, need to test what is quicker...
- newImage = (sourceImage as Bitmap).Clone(sourceRect, targetFormat);
- // Make sure both images have the same resolution
- newImage.SetResolution(sourceImage.HorizontalResolution, sourceImage.VerticalResolution);
- }
-
- // In WINE someone getting the PropertyItems doesn't work
- try
- {
- // Clone property items (EXIF information etc)
- foreach (var propertyItem in sourceImage.PropertyItems)
- {
- try
- {
- newImage.SetPropertyItem(propertyItem);
- }
- catch (Exception innerEx)
- {
- Log.Warn("Problem cloning a propertyItem.", innerEx);
- }
- }
- }
- catch (Exception ex)
- {
- Log.Warn("Problem cloning a propertyItem.", ex);
- }
-
- return newImage;
- }
-
- ///
- /// Rotate the bitmap
- ///
- ///
- ///
- ///
- public static Image RotateFlip(Image sourceImage, RotateFlipType rotateFlipType)
- {
- Image returnImage = Clone(sourceImage);
- returnImage.RotateFlip(rotateFlipType);
- return returnImage;
- }
-
- ///
- /// A generic way to create an empty image
- ///
- /// the source bitmap as the specifications for the new bitmap
- /// The color to fill with, or Color.Empty to take the default depending on the pixel format
- ///
- public static Bitmap CreateEmptyLike(Image sourceImage, Color backgroundColor)
- {
- PixelFormat pixelFormat = sourceImage.PixelFormat;
- if (backgroundColor.A < 255)
- {
- pixelFormat = PixelFormat.Format32bppArgb;
- }
-
- return CreateEmpty(sourceImage.Width, sourceImage.Height, pixelFormat, backgroundColor, sourceImage.HorizontalResolution, sourceImage.VerticalResolution);
- }
-
- ///
- /// A generic way to create an empty image
- ///
- ///
- ///
- ///
- /// The color to fill with, or Color.Empty to take the default depending on the pixel format
- ///
- ///
- /// Bitmap
- public static Bitmap CreateEmpty(int width, int height, PixelFormat format, Color backgroundColor, float horizontalResolution, float verticalResolution)
- {
- // Create a new "clean" image
- Bitmap newImage = new Bitmap(width, height, format);
- newImage.SetResolution(horizontalResolution, verticalResolution);
- if (format != PixelFormat.Format8bppIndexed)
- {
- using Graphics graphics = Graphics.FromImage(newImage);
- // Make sure the background color is what we want (transparent or white, depending on the pixel format)
- if (!Color.Empty.Equals(backgroundColor))
- {
- graphics.Clear(backgroundColor);
- }
- else if (Image.IsAlphaPixelFormat(format))
- {
- graphics.Clear(Color.Transparent);
- }
- else
- {
- graphics.Clear(Color.White);
- }
- }
-
- return newImage;
- }
-
- ///
- /// Resize canvas with pixel to the left, right, top and bottom
- ///
- ///
- /// The color to fill with, or Color.Empty to take the default depending on the pixel format
- ///
- ///
- ///
- ///
- ///
- /// a new bitmap with the source copied on it
- public static Image ResizeCanvas(Image sourceImage, Color backgroundColor, int left, int right, int top, int bottom, Matrix matrix)
- {
- matrix.Translate(left, top, MatrixOrder.Append);
- Bitmap newBitmap = CreateEmpty(sourceImage.Width + left + right, sourceImage.Height + top + bottom, sourceImage.PixelFormat, backgroundColor,
- sourceImage.HorizontalResolution, sourceImage.VerticalResolution);
- using (Graphics graphics = Graphics.FromImage(newBitmap))
- {
- graphics.DrawImageUnscaled(sourceImage, left, top);
- }
-
- return newBitmap;
- }
-
- ///
- /// Wrapper for the more complex Resize, this resize could be used for e.g. Thumbnails
- ///
- ///
- /// true to maintain the aspect ratio
- ///
- ///
- ///
- ///
- public static Image ResizeImage(Image sourceImage, bool maintainAspectRatio, int newWidth, int newHeight, Matrix matrix)
- {
- return ResizeImage(sourceImage, maintainAspectRatio, false, Color.Empty, newWidth, newHeight, matrix);
- }
-
- ///
- /// Count how many times the supplied color exists
- ///
- /// Image to count the pixels of
- /// Color to count
- /// true if Alpha needs to be checked
- /// int with the number of pixels which have colorToCount
- public static int CountColor(Image sourceImage, Color colorToCount, bool includeAlpha)
- {
- int colors = 0;
- int toCount = colorToCount.ToArgb();
- if (!includeAlpha)
- {
- toCount &= 0xffffff;
- }
-
- using IFastBitmap bb = FastBitmap.Create((Bitmap) sourceImage);
- for (int y = 0; y < bb.Height; y++)
- {
- for (int x = 0; x < bb.Width; x++)
- {
- int bitmapcolor = bb.GetColorAt(x, y).ToArgb();
- if (!includeAlpha)
- {
- bitmapcolor &= 0xffffff;
- }
-
- if (bitmapcolor == toCount)
- {
- colors++;
- }
- }
- }
-
- return colors;
- }
-
- ///
- /// Scale the bitmap, keeping aspect ratio, but the canvas will always have the specified size.
- ///
- /// Image to scale
- /// true to maintain the aspect ratio
- /// Makes the image maintain aspect ratio, but the canvas get's the specified size
- /// The color to fill with, or Color.Empty to take the default depending on the pixel format
- /// new width
- /// new height
- ///
- /// a new bitmap with the specified size, the source-Image scaled to fit with aspect ratio locked
- public static Image ResizeImage(Image sourceImage, bool maintainAspectRatio, bool canvasUseNewSize, Color backgroundColor, int newWidth, int newHeight, Matrix matrix)
- {
- int destX = 0;
- int destY = 0;
-
- var nPercentW = newWidth / (float) sourceImage.Width;
- var nPercentH = newHeight / (float) sourceImage.Height;
- if (maintainAspectRatio)
- {
- if ((int) nPercentW == 1)
- {
- nPercentW = nPercentH;
- if (canvasUseNewSize)
- {
- destX = Math.Max(0, Convert.ToInt32((newWidth - sourceImage.Width * nPercentW) / 2));
- }
- }
- else if ((int) nPercentH == 1)
- {
- nPercentH = nPercentW;
- if (canvasUseNewSize)
- {
- destY = Math.Max(0, Convert.ToInt32((newHeight - sourceImage.Height * nPercentH) / 2));
- }
- }
- else if ((int) nPercentH != 0 && nPercentH < nPercentW)
- {
- nPercentW = nPercentH;
- if (canvasUseNewSize)
- {
- destX = Math.Max(0, Convert.ToInt32((newWidth - sourceImage.Width * nPercentW) / 2));
- }
- }
- else
- {
- nPercentH = nPercentW;
- if (canvasUseNewSize)
- {
- destY = Math.Max(0, Convert.ToInt32((newHeight - sourceImage.Height * nPercentH) / 2));
- }
- }
- }
-
- int destWidth = (int) (sourceImage.Width * nPercentW);
- int destHeight = (int) (sourceImage.Height * nPercentH);
- if (newWidth == 0)
- {
- newWidth = destWidth;
- }
-
- if (newHeight == 0)
- {
- newHeight = destHeight;
- }
-
- Image newImage;
- if (maintainAspectRatio && canvasUseNewSize)
- {
- newImage = CreateEmpty(newWidth, newHeight, sourceImage.PixelFormat, backgroundColor, sourceImage.HorizontalResolution, sourceImage.VerticalResolution);
- matrix?.Scale((float) newWidth / sourceImage.Width, (float) newHeight / sourceImage.Height, MatrixOrder.Append);
- }
- else
- {
- newImage = CreateEmpty(destWidth, destHeight, sourceImage.PixelFormat, backgroundColor, sourceImage.HorizontalResolution, sourceImage.VerticalResolution);
- matrix?.Scale((float) destWidth / sourceImage.Width, (float) destHeight / sourceImage.Height, MatrixOrder.Append);
- }
-
- using (Graphics graphics = Graphics.FromImage(newImage))
- {
- graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
- using ImageAttributes wrapMode = new ImageAttributes();
- wrapMode.SetWrapMode(WrapMode.TileFlipXY);
- graphics.DrawImage(sourceImage, new Rectangle(destX, destY, destWidth, destHeight), 0, 0, sourceImage.Width, sourceImage.Height, GraphicsUnit.Pixel, wrapMode);
- }
-
- return newImage;
- }
-
- ///
- /// Load a Greenshot surface from a stream
- ///
- /// Stream
- ///
- /// ISurface
- public static ISurface LoadGreenshotSurface(Stream surfaceFileStream, ISurface returnSurface)
- {
- Image fileImage;
- // Fixed problem that the bitmap stream is disposed... by Cloning the image
- // This also ensures the bitmap is correctly created
-
- // We create a copy of the bitmap, so everything else can be disposed
- surfaceFileStream.Position = 0;
- using (Image tmpImage = Image.FromStream(surfaceFileStream, true, true))
- {
- Log.DebugFormat("Loaded .greenshot file with Size {0}x{1} and PixelFormat {2}", tmpImage.Width, tmpImage.Height, tmpImage.PixelFormat);
- fileImage = Clone(tmpImage);
- }
-
- // Start at -14 read "GreenshotXX.YY" (XX=Major, YY=Minor)
- const int markerSize = 14;
- surfaceFileStream.Seek(-markerSize, SeekOrigin.End);
- using (StreamReader streamReader = new StreamReader(surfaceFileStream))
- {
- var greenshotMarker = streamReader.ReadToEnd();
- if (!greenshotMarker.StartsWith("Greenshot"))
- {
- throw new ArgumentException("Stream is not a Greenshot file!");
- }
-
- Log.InfoFormat("Greenshot file format: {0}", greenshotMarker);
- const int filesizeLocation = 8 + markerSize;
- surfaceFileStream.Seek(-filesizeLocation, SeekOrigin.End);
- using BinaryReader reader = new BinaryReader(surfaceFileStream);
- long bytesWritten = reader.ReadInt64();
- surfaceFileStream.Seek(-(bytesWritten + filesizeLocation), SeekOrigin.End);
- returnSurface.LoadElementsFromStream(surfaceFileStream);
- }
-
- if (fileImage != null)
- {
- returnSurface.Image = fileImage;
- Log.InfoFormat("Information about .greenshot file: {0}x{1}-{2} Resolution {3}x{4}", fileImage.Width, fileImage.Height, fileImage.PixelFormat,
- fileImage.HorizontalResolution, fileImage.VerticalResolution);
- }
-
- return returnSurface;
- }
-
- ///
- /// Create an image from a stream, if an extension is supplied more formats are supported.
- ///
- /// Stream
- ///
- /// Image
- public static Image FromStream(Stream stream, string extension = null)
- {
- if (stream == null)
- {
- return null;
- }
-
- if (!string.IsNullOrEmpty(extension))
- {
- extension = extension.Replace(".", string.Empty);
- }
-
- // Make sure we can try multiple times
- if (!stream.CanSeek)
- {
- var memoryStream = new MemoryStream();
- stream.CopyTo(memoryStream);
- stream = memoryStream;
- }
-
- Image returnImage = null;
- if (StreamConverters.TryGetValue(extension ?? string.Empty, out var converter))
- {
- returnImage = converter(stream, extension);
- }
-
- // Fallback
- if (returnImage == null)
- {
- // We create a copy of the bitmap, so everything else can be disposed
- stream.Position = 0;
- using var tmpImage = Image.FromStream(stream, true, true);
- Log.DebugFormat("Loaded bitmap with Size {0}x{1} and PixelFormat {2}", tmpImage.Width, tmpImage.Height, tmpImage.PixelFormat);
- returnImage = Clone(tmpImage, PixelFormat.Format32bppArgb);
- }
-
- return returnImage;
- }
- }
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+using System;
+using System.Collections.Generic;
+using System.Drawing;
+using System.Drawing.Drawing2D;
+using System.Drawing.Imaging;
+using System.IO;
+using Greenshot.Base.Effects;
+using Greenshot.Base.IniFile;
+using Greenshot.Base.Interfaces;
+using Greenshot.Base.UnmanagedHelpers;
+using log4net;
+
+namespace Greenshot.Base.Core
+{
+ internal enum ExifOrientations : byte
+ {
+ Unknown = 0,
+ TopLeft = 1,
+ TopRight = 2,
+ BottomRight = 3,
+ BottomLeft = 4,
+ LeftTop = 5,
+ RightTop = 6,
+ RightBottom = 7,
+ LeftBottom = 8,
+ }
+
+ ///
+ /// Description of ImageHelper.
+ ///
+ public static class ImageHelper
+ {
+ private static readonly ILog Log = LogManager.GetLogger(typeof(ImageHelper));
+ private static readonly CoreConfiguration CoreConfig = IniConfig.GetIniSection();
+ private const int ExifOrientationId = 0x0112;
+
+ static ImageHelper()
+ {
+ StreamConverters["greenshot"] = (stream, s) =>
+ {
+ var surface = SimpleServiceProvider.Current.GetInstance>().Invoke();
+ return surface.GetImageForExport();
+ };
+
+ // Add a SVG converter
+ StreamConverters["svg"] = (stream, s) =>
+ {
+ stream.Position = 0;
+ try
+ {
+ return SvgImage.FromStream(stream).Image;
+ }
+ catch (Exception ex)
+ {
+ Log.Error("Can't load SVG", ex);
+ }
+
+ return null;
+ };
+
+ static Image DefaultConverter(Stream stream, string s)
+ {
+ stream.Position = 0;
+ using var tmpImage = Image.FromStream(stream, true, true);
+ Log.DebugFormat("Loaded bitmap with Size {0}x{1} and PixelFormat {2}", tmpImage.Width, tmpImage.Height, tmpImage.PixelFormat);
+ return Clone(tmpImage, PixelFormat.Format32bppArgb);
+ }
+
+ // Fallback
+ StreamConverters[string.Empty] = DefaultConverter;
+ StreamConverters["gif"] = DefaultConverter;
+ StreamConverters["bmp"] = DefaultConverter;
+ StreamConverters["jpg"] = DefaultConverter;
+ StreamConverters["jpeg"] = DefaultConverter;
+ StreamConverters["png"] = DefaultConverter;
+ StreamConverters["wmf"] = DefaultConverter;
+
+ StreamConverters["ico"] = (stream, extension) =>
+ {
+ // Icon logic, try to get the Vista icon, else the biggest possible
+ try
+ {
+ using Image tmpImage = ExtractVistaIcon(stream);
+ if (tmpImage != null)
+ {
+ return Clone(tmpImage, PixelFormat.Format32bppArgb);
+ }
+ }
+ catch (Exception vistaIconException)
+ {
+ Log.Warn("Can't read icon", vistaIconException);
+ }
+
+ try
+ {
+ // No vista icon, try normal icon
+ stream.Position = 0;
+ // We create a copy of the bitmap, so everything else can be disposed
+ using Icon tmpIcon = new Icon(stream, new Size(1024, 1024));
+ using Image tmpImage = tmpIcon.ToBitmap();
+ return Clone(tmpImage, PixelFormat.Format32bppArgb);
+ }
+ catch (Exception iconException)
+ {
+ Log.Warn("Can't read icon", iconException);
+ }
+
+ stream.Position = 0;
+ return DefaultConverter(stream, extension);
+ };
+ }
+
+ public static IDictionary> StreamConverters { get; } = new Dictionary>();
+
+ ///
+ /// Make sure the image is orientated correctly
+ ///
+ ///
+ public static void Orientate(Image image)
+ {
+ if (!CoreConfig.ProcessEXIFOrientation)
+ {
+ return;
+ }
+
+ try
+ {
+ // Get the index of the orientation property.
+ int orientationIndex = Array.IndexOf(image.PropertyIdList, ExifOrientationId);
+ // If there is no such property, return Unknown.
+ if (orientationIndex < 0)
+ {
+ return;
+ }
+
+ PropertyItem item = image.GetPropertyItem(ExifOrientationId);
+
+ ExifOrientations orientation = (ExifOrientations) item.Value[0];
+ // Orient the image.
+ switch (orientation)
+ {
+ case ExifOrientations.Unknown:
+ case ExifOrientations.TopLeft:
+ break;
+ case ExifOrientations.TopRight:
+ image.RotateFlip(RotateFlipType.RotateNoneFlipX);
+ break;
+ case ExifOrientations.BottomRight:
+ image.RotateFlip(RotateFlipType.Rotate180FlipNone);
+ break;
+ case ExifOrientations.BottomLeft:
+ image.RotateFlip(RotateFlipType.RotateNoneFlipY);
+ break;
+ case ExifOrientations.LeftTop:
+ image.RotateFlip(RotateFlipType.Rotate90FlipX);
+ break;
+ case ExifOrientations.RightTop:
+ image.RotateFlip(RotateFlipType.Rotate90FlipNone);
+ break;
+ case ExifOrientations.RightBottom:
+ image.RotateFlip(RotateFlipType.Rotate90FlipY);
+ break;
+ case ExifOrientations.LeftBottom:
+ image.RotateFlip(RotateFlipType.Rotate270FlipNone);
+ break;
+ }
+
+ // Set the orientation to be normal, as we rotated the image.
+ item.Value[0] = (byte) ExifOrientations.TopLeft;
+ image.SetPropertyItem(item);
+ }
+ catch (Exception orientEx)
+ {
+ Log.Warn("Problem orientating the image: ", orientEx);
+ }
+ }
+
+ ///
+ /// Create a Thumbnail
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static Image CreateThumbnail(Image image, int thumbWidth, int thumbHeight, int maxWidth = -1, int maxHeight = -1)
+ {
+ int srcWidth = image.Width;
+ int srcHeight = image.Height;
+ if (thumbHeight < 0)
+ {
+ thumbHeight = (int) (thumbWidth * (srcHeight / (float) srcWidth));
+ }
+
+ if (thumbWidth < 0)
+ {
+ thumbWidth = (int) (thumbHeight * (srcWidth / (float) srcHeight));
+ }
+
+ if (maxWidth > 0 && thumbWidth > maxWidth)
+ {
+ thumbWidth = Math.Min(thumbWidth, maxWidth);
+ thumbHeight = (int) (thumbWidth * (srcHeight / (float) srcWidth));
+ }
+
+ if (maxHeight > 0 && thumbHeight > maxHeight)
+ {
+ thumbHeight = Math.Min(thumbHeight, maxHeight);
+ thumbWidth = (int) (thumbHeight * (srcWidth / (float) srcHeight));
+ }
+
+ Bitmap bmp = new Bitmap(thumbWidth, thumbHeight);
+ using (Graphics graphics = Graphics.FromImage(bmp))
+ {
+ graphics.SmoothingMode = SmoothingMode.HighQuality;
+ graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
+ graphics.CompositingQuality = CompositingQuality.HighQuality;
+ graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
+ Rectangle rectDestination = new Rectangle(0, 0, thumbWidth, thumbHeight);
+ graphics.DrawImage(image, rectDestination, 0, 0, srcWidth, srcHeight, GraphicsUnit.Pixel);
+ }
+
+ return bmp;
+ }
+
+ ///
+ /// Crops the image to the specified rectangle
+ ///
+ /// Image to crop
+ /// Rectangle with bitmap coordinates, will be "intersected" to the bitmap
+ public static bool Crop(ref Image image, ref Rectangle cropRectangle)
+ {
+ if (image is Bitmap && (image.Width * image.Height > 0))
+ {
+ cropRectangle.Intersect(new Rectangle(0, 0, image.Width, image.Height));
+ if (cropRectangle.Width != 0 || cropRectangle.Height != 0)
+ {
+ Image returnImage = CloneArea(image, cropRectangle, PixelFormat.DontCare);
+ image.Dispose();
+ image = returnImage;
+ return true;
+ }
+ }
+
+ Log.Warn("Can't crop a null/zero size image!");
+ return false;
+ }
+
+ ///
+ /// Private helper method for the FindAutoCropRectangle
+ ///
+ ///
+ ///
+ ///
+ /// Rectangle
+ private static Rectangle FindAutoCropRectangle(IFastBitmap fastBitmap, Point colorPoint, int cropDifference)
+ {
+ Rectangle cropRectangle = Rectangle.Empty;
+ Color referenceColor = fastBitmap.GetColorAt(colorPoint.X, colorPoint.Y);
+ Point min = new Point(int.MaxValue, int.MaxValue);
+ Point max = new Point(int.MinValue, int.MinValue);
+
+ if (cropDifference > 0)
+ {
+ for (int y = 0; y < fastBitmap.Height; y++)
+ {
+ for (int x = 0; x < fastBitmap.Width; x++)
+ {
+ Color currentColor = fastBitmap.GetColorAt(x, y);
+ int diffR = Math.Abs(currentColor.R - referenceColor.R);
+ int diffG = Math.Abs(currentColor.G - referenceColor.G);
+ int diffB = Math.Abs(currentColor.B - referenceColor.B);
+ if ((diffR + diffG + diffB) / 3 <= cropDifference)
+ {
+ continue;
+ }
+
+ if (x < min.X) min.X = x;
+ if (y < min.Y) min.Y = y;
+ if (x > max.X) max.X = x;
+ if (y > max.Y) max.Y = y;
+ }
+ }
+ }
+ else
+ {
+ for (int y = 0; y < fastBitmap.Height; y++)
+ {
+ for (int x = 0; x < fastBitmap.Width; x++)
+ {
+ Color currentColor = fastBitmap.GetColorAt(x, y);
+ if (!referenceColor.Equals(currentColor))
+ {
+ continue;
+ }
+
+ if (x < min.X) min.X = x;
+ if (y < min.Y) min.Y = y;
+ if (x > max.X) max.X = x;
+ if (y > max.Y) max.Y = y;
+ }
+ }
+ }
+
+ if (!(Point.Empty.Equals(min) && max.Equals(new Point(fastBitmap.Width - 1, fastBitmap.Height - 1))))
+ {
+ if (!(min.X == int.MaxValue || min.Y == int.MaxValue || max.X == int.MinValue || min.X == int.MinValue))
+ {
+ cropRectangle = new Rectangle(min.X, min.Y, max.X - min.X + 1, max.Y - min.Y + 1);
+ }
+ }
+
+ return cropRectangle;
+ }
+
+ ///
+ /// Get a rectangle for the image which crops the image of all colors equal to that on 0,0
+ ///
+ ///
+ ///
+ /// Rectangle
+ public static Rectangle FindAutoCropRectangle(Image image, int cropDifference)
+ {
+ Rectangle cropRectangle = Rectangle.Empty;
+ var checkPoints = new List
+ {
+ new Point(0, 0),
+ new Point(0, image.Height - 1),
+ new Point(image.Width - 1, 0),
+ new Point(image.Width - 1, image.Height - 1)
+ };
+ // Top Left
+ // Bottom Left
+ // Top Right
+ // Bottom Right
+ using (IFastBitmap fastBitmap = FastBitmap.Create((Bitmap) image))
+ {
+ // find biggest area
+ foreach (Point checkPoint in checkPoints)
+ {
+ var currentRectangle = FindAutoCropRectangle(fastBitmap, checkPoint, cropDifference);
+ if (currentRectangle.Width * currentRectangle.Height > cropRectangle.Width * cropRectangle.Height)
+ {
+ cropRectangle = currentRectangle;
+ }
+ }
+ }
+
+ return cropRectangle;
+ }
+
+ ///
+ /// Load an image from file
+ ///
+ ///
+ ///
+ public static Image LoadImage(string filename)
+ {
+ if (string.IsNullOrEmpty(filename))
+ {
+ return null;
+ }
+
+ if (!File.Exists(filename))
+ {
+ return null;
+ }
+
+ Image fileImage;
+ Log.InfoFormat("Loading image from file {0}", filename);
+ // Fixed lock problem Bug #3431881
+ using (Stream imageFileStream = File.OpenRead(filename))
+ {
+ fileImage = FromStream(imageFileStream, Path.GetExtension(filename));
+ }
+
+ if (fileImage != null)
+ {
+ Log.InfoFormat("Information about file {0}: {1}x{2}-{3} Resolution {4}x{5}", filename, fileImage.Width, fileImage.Height, fileImage.PixelFormat,
+ fileImage.HorizontalResolution, fileImage.VerticalResolution);
+ }
+
+ return fileImage;
+ }
+
+ ///
+ /// Based on: http://www.codeproject.com/KB/cs/IconExtractor.aspx
+ /// And a hint from: http://www.codeproject.com/KB/cs/IconLib.aspx
+ ///
+ /// Stream with the icon information
+ /// Bitmap with the Vista Icon (256x256)
+ private static Bitmap ExtractVistaIcon(Stream iconStream)
+ {
+ const int sizeIconDir = 6;
+ const int sizeIconDirEntry = 16;
+ Bitmap bmpPngExtracted = null;
+ try
+ {
+ byte[] srcBuf = new byte[iconStream.Length];
+ iconStream.Read(srcBuf, 0, (int) iconStream.Length);
+ int iCount = BitConverter.ToInt16(srcBuf, 4);
+ for (int iIndex = 0; iIndex < iCount; iIndex++)
+ {
+ int iWidth = srcBuf[sizeIconDir + sizeIconDirEntry * iIndex];
+ int iHeight = srcBuf[sizeIconDir + sizeIconDirEntry * iIndex + 1];
+ if (iWidth == 0 && iHeight == 0)
+ {
+ int iImageSize = BitConverter.ToInt32(srcBuf, sizeIconDir + sizeIconDirEntry * iIndex + 8);
+ int iImageOffset = BitConverter.ToInt32(srcBuf, sizeIconDir + sizeIconDirEntry * iIndex + 12);
+ using MemoryStream destStream = new MemoryStream();
+ destStream.Write(srcBuf, iImageOffset, iImageSize);
+ destStream.Seek(0, SeekOrigin.Begin);
+ bmpPngExtracted = new Bitmap(destStream); // This is PNG! :)
+ break;
+ }
+ }
+ }
+ catch
+ {
+ return null;
+ }
+
+ return bmpPngExtracted;
+ }
+
+ ///
+ /// See: http://msdn.microsoft.com/en-us/library/windows/desktop/ms648069%28v=vs.85%29.aspx
+ ///
+ /// The file (EXE or DLL) to get the icon from
+ /// Index of the icon
+ /// true if the large icon is wanted
+ /// Icon
+ public static Icon ExtractAssociatedIcon(string location, int index, bool takeLarge)
+ {
+ Shell32.ExtractIconEx(location, index, out var large, out var small, 1);
+ Icon returnIcon = null;
+ bool isLarge = false;
+ bool isSmall = false;
+ try
+ {
+ if (takeLarge && !IntPtr.Zero.Equals(large))
+ {
+ returnIcon = Icon.FromHandle(large);
+ isLarge = true;
+ }
+ else if (!IntPtr.Zero.Equals(small))
+ {
+ returnIcon = Icon.FromHandle(small);
+ isSmall = true;
+ }
+ else if (!IntPtr.Zero.Equals(large))
+ {
+ returnIcon = Icon.FromHandle(large);
+ isLarge = true;
+ }
+ }
+ finally
+ {
+ if (isLarge && !IntPtr.Zero.Equals(small))
+ {
+ User32.DestroyIcon(small);
+ }
+
+ if (isSmall && !IntPtr.Zero.Equals(large))
+ {
+ User32.DestroyIcon(large);
+ }
+ }
+
+ return returnIcon;
+ }
+
+ ///
+ /// Apply the effect to the bitmap
+ ///
+ /// Bitmap
+ /// IEffect
+ ///
+ /// Bitmap
+ public static Image ApplyEffect(Image sourceImage, IEffect effect, Matrix matrix)
+ {
+ var effects = new List
+ {
+ effect
+ };
+ return ApplyEffects(sourceImage, effects, matrix);
+ }
+
+ ///
+ /// Apply the effects in the supplied order to the bitmap
+ ///
+ /// Bitmap
+ /// List of IEffect
+ ///
+ /// Bitmap
+ public static Image ApplyEffects(Image sourceImage, IEnumerable effects, Matrix matrix)
+ {
+ var currentImage = sourceImage;
+ bool disposeImage = false;
+ foreach (var effect in effects)
+ {
+ var tmpImage = effect.Apply(currentImage, matrix);
+ if (tmpImage != null)
+ {
+ if (disposeImage)
+ {
+ currentImage.Dispose();
+ }
+
+ currentImage = tmpImage;
+ // Make sure the "new" image is disposed
+ disposeImage = true;
+ }
+ }
+
+ return currentImage;
+ }
+
+ ///
+ /// Helper method for the tornedge
+ ///
+ /// Path to draw to
+ /// Points for the lines to draw
+ private static void DrawLines(GraphicsPath path, List points)
+ {
+ path.AddLine(points[0], points[1]);
+ for (int i = 0; i < points.Count - 1; i++)
+ {
+ path.AddLine(points[i], points[i + 1]);
+ }
+ }
+
+ ///
+ /// Make the picture look like it's torn
+ ///
+ /// Bitmap to make torn edge off
+ /// How large (height) is each tooth
+ /// How wide is a horizontal tooth
+ /// How wide is a vertical tooth
+ /// bool[] with information on if the edge needs torn or not. Order is clockwise: 0=top,1=right,2=bottom,3=left
+ /// Changed bitmap
+ public static Image CreateTornEdge(Image sourceImage, int toothHeight, int horizontalToothRange, int verticalToothRange, bool[] edges)
+ {
+ Image returnImage = CreateEmpty(sourceImage.Width, sourceImage.Height, PixelFormat.Format32bppArgb, Color.Empty, sourceImage.HorizontalResolution,
+ sourceImage.VerticalResolution);
+ using (var path = new GraphicsPath())
+ {
+ Random random = new Random();
+ int horizontalRegions = (int) Math.Round((float) sourceImage.Width / horizontalToothRange);
+ int verticalRegions = (int) Math.Round((float) sourceImage.Height / verticalToothRange);
+
+ Point topLeft = new Point(0, 0);
+ Point topRight = new Point(sourceImage.Width, 0);
+ Point bottomLeft = new Point(0, sourceImage.Height);
+ Point bottomRight = new Point(sourceImage.Width, sourceImage.Height);
+
+ List points = new List();
+
+ if (edges[0])
+ {
+ // calculate starting point only if the left edge is torn
+ if (!edges[3])
+ {
+ points.Add(topLeft);
+ }
+ else
+ {
+ points.Add(new Point(random.Next(1, toothHeight), random.Next(1, toothHeight)));
+ }
+
+ for (int i = 1; i < horizontalRegions - 1; i++)
+ {
+ points.Add(new Point(i * horizontalToothRange, random.Next(1, toothHeight)));
+ }
+
+ points.Add(new Point(sourceImage.Width - random.Next(1, toothHeight), random.Next(1, toothHeight)));
+ }
+ else
+ {
+ // set start & endpoint to be the default "whole-line"
+ points.Add(topLeft);
+ points.Add(topRight);
+ }
+
+ // Right
+ if (edges[1])
+ {
+ for (int i = 1; i < verticalRegions - 1; i++)
+ {
+ points.Add(new Point(sourceImage.Width - random.Next(1, toothHeight), i * verticalToothRange));
+ }
+
+ points.Add(new Point(sourceImage.Width - random.Next(1, toothHeight), sourceImage.Height - random.Next(1, toothHeight)));
+ }
+ else
+ {
+ // correct previous ending point
+ points[points.Count - 1] = topRight;
+ // set endpoint to be the default "whole-line"
+ points.Add(bottomRight);
+ }
+
+ // Bottom
+ if (edges[2])
+ {
+ for (int i = 1; i < horizontalRegions - 1; i++)
+ {
+ points.Add(new Point(sourceImage.Width - i * horizontalToothRange, sourceImage.Height - random.Next(1, toothHeight)));
+ }
+
+ points.Add(new Point(random.Next(1, toothHeight), sourceImage.Height - random.Next(1, toothHeight)));
+ }
+ else
+ {
+ // correct previous ending point
+ points[points.Count - 1] = bottomRight;
+ // set endpoint to be the default "whole-line"
+ points.Add(bottomLeft);
+ }
+
+ // Left
+ if (edges[3])
+ {
+ // One fewer as the end point is the starting point
+ for (int i = 1; i < verticalRegions - 1; i++)
+ {
+ points.Add(new Point(random.Next(1, toothHeight), points[points.Count - 1].Y - verticalToothRange));
+ }
+ }
+ else
+ {
+ // correct previous ending point
+ points[points.Count - 1] = bottomLeft;
+ // set endpoint to be the default "whole-line"
+ points.Add(topLeft);
+ }
+
+ // End point always is the starting point
+ points[points.Count - 1] = points[0];
+
+ DrawLines(path, points);
+
+ path.CloseFigure();
+
+ // Draw the created figure with the original image by using a TextureBrush so we have anti-aliasing
+ using Graphics graphics = Graphics.FromImage(returnImage);
+ graphics.SmoothingMode = SmoothingMode.HighQuality;
+ graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
+ graphics.CompositingQuality = CompositingQuality.HighQuality;
+ graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
+ using Brush brush = new TextureBrush(sourceImage);
+ // Important note: If the target wouldn't be at 0,0 we need to translate-transform!!
+ graphics.FillPath(brush, path);
+ }
+
+ return returnImage;
+ }
+
+ ///
+ /// Apply BoxBlur to the destinationBitmap
+ ///
+ /// Bitmap to blur
+ /// Must be ODD!
+ public static void ApplyBoxBlur(Bitmap destinationBitmap, int range)
+ {
+ // We only need one fastbitmap as we use it as source and target (the reading is done for one line H/V, writing after "parsing" one line H/V)
+ using IFastBitmap fastBitmap = FastBitmap.Create(destinationBitmap);
+ ApplyBoxBlur(fastBitmap, range);
+ }
+
+ ///
+ /// Apply BoxBlur to the fastBitmap
+ ///
+ /// IFastBitmap to blur
+ /// Must be ODD!
+ public static void ApplyBoxBlur(IFastBitmap fastBitmap, int range)
+ {
+ // Range must be odd!
+ if ((range & 1) == 0)
+ {
+ range++;
+ }
+
+ if (range <= 1)
+ {
+ return;
+ }
+
+ // Box blurs are frequently used to approximate a Gaussian blur.
+ // By the central limit theorem, if applied 3 times on the same image, a box blur approximates the Gaussian kernel to within about 3%, yielding the same result as a quadratic convolution kernel.
+ // This might be true, but the GDI+ BlurEffect doesn't look the same, a 2x blur is more simular and we only make 2x Box-Blur.
+ // (Might also be a mistake in our blur, but for now it looks great)
+ if (fastBitmap.HasAlphaChannel)
+ {
+ BoxBlurHorizontalAlpha(fastBitmap, range);
+ BoxBlurVerticalAlpha(fastBitmap, range);
+ BoxBlurHorizontalAlpha(fastBitmap, range);
+ BoxBlurVerticalAlpha(fastBitmap, range);
+ }
+ else
+ {
+ BoxBlurHorizontal(fastBitmap, range);
+ BoxBlurVertical(fastBitmap, range);
+ BoxBlurHorizontal(fastBitmap, range);
+ BoxBlurVertical(fastBitmap, range);
+ }
+ }
+
+ ///
+ /// BoxBlurHorizontal is a private helper method for the BoxBlur
+ ///
+ /// Target BitmapBuffer
+ /// Range must be odd!
+ private static void BoxBlurHorizontal(IFastBitmap targetFastBitmap, int range)
+ {
+ if (targetFastBitmap.HasAlphaChannel)
+ {
+ throw new NotSupportedException("BoxBlurHorizontal should NOT be called for bitmaps with alpha channel");
+ }
+
+ int halfRange = range / 2;
+ Color[] newColors = new Color[targetFastBitmap.Width];
+ byte[] tmpColor = new byte[3];
+ for (int y = targetFastBitmap.Top; y < targetFastBitmap.Bottom; y++)
+ {
+ int hits = 0;
+ int r = 0;
+ int g = 0;
+ int b = 0;
+ for (int x = targetFastBitmap.Left - halfRange; x < targetFastBitmap.Right; x++)
+ {
+ int oldPixel = x - halfRange - 1;
+ if (oldPixel >= targetFastBitmap.Left)
+ {
+ targetFastBitmap.GetColorAt(oldPixel, y, tmpColor);
+ r -= tmpColor[FastBitmap.ColorIndexR];
+ g -= tmpColor[FastBitmap.ColorIndexG];
+ b -= tmpColor[FastBitmap.ColorIndexB];
+ hits--;
+ }
+
+ int newPixel = x + halfRange;
+ if (newPixel < targetFastBitmap.Right)
+ {
+ targetFastBitmap.GetColorAt(newPixel, y, tmpColor);
+ r += tmpColor[FastBitmap.ColorIndexR];
+ g += tmpColor[FastBitmap.ColorIndexG];
+ b += tmpColor[FastBitmap.ColorIndexB];
+ hits++;
+ }
+
+ if (x >= targetFastBitmap.Left)
+ {
+ newColors[x - targetFastBitmap.Left] = Color.FromArgb(255, (byte) (r / hits), (byte) (g / hits), (byte) (b / hits));
+ }
+ }
+
+ for (int x = targetFastBitmap.Left; x < targetFastBitmap.Right; x++)
+ {
+ targetFastBitmap.SetColorAt(x, y, newColors[x - targetFastBitmap.Left]);
+ }
+ }
+ }
+
+ ///
+ /// BoxBlurHorizontal is a private helper method for the BoxBlur, only for IFastBitmaps with alpha channel
+ ///
+ /// Target BitmapBuffer
+ /// Range must be odd!
+ private static void BoxBlurHorizontalAlpha(IFastBitmap targetFastBitmap, int range)
+ {
+ if (!targetFastBitmap.HasAlphaChannel)
+ {
+ throw new NotSupportedException("BoxBlurHorizontalAlpha should be called for bitmaps with alpha channel");
+ }
+
+ int halfRange = range / 2;
+ Color[] newColors = new Color[targetFastBitmap.Width];
+ byte[] tmpColor = new byte[4];
+ for (int y = targetFastBitmap.Top; y < targetFastBitmap.Bottom; y++)
+ {
+ int hits = 0;
+ int a = 0;
+ int r = 0;
+ int g = 0;
+ int b = 0;
+ for (int x = targetFastBitmap.Left - halfRange; x < targetFastBitmap.Right; x++)
+ {
+ int oldPixel = x - halfRange - 1;
+ if (oldPixel >= targetFastBitmap.Left)
+ {
+ targetFastBitmap.GetColorAt(oldPixel, y, tmpColor);
+ a -= tmpColor[FastBitmap.ColorIndexA];
+ r -= tmpColor[FastBitmap.ColorIndexR];
+ g -= tmpColor[FastBitmap.ColorIndexG];
+ b -= tmpColor[FastBitmap.ColorIndexB];
+ hits--;
+ }
+
+ int newPixel = x + halfRange;
+ if (newPixel < targetFastBitmap.Right)
+ {
+ targetFastBitmap.GetColorAt(newPixel, y, tmpColor);
+ a += tmpColor[FastBitmap.ColorIndexA];
+ r += tmpColor[FastBitmap.ColorIndexR];
+ g += tmpColor[FastBitmap.ColorIndexG];
+ b += tmpColor[FastBitmap.ColorIndexB];
+ hits++;
+ }
+
+ if (x >= targetFastBitmap.Left)
+ {
+ newColors[x - targetFastBitmap.Left] = Color.FromArgb((byte) (a / hits), (byte) (r / hits), (byte) (g / hits), (byte) (b / hits));
+ }
+ }
+
+ for (int x = targetFastBitmap.Left; x < targetFastBitmap.Right; x++)
+ {
+ targetFastBitmap.SetColorAt(x, y, newColors[x - targetFastBitmap.Left]);
+ }
+ }
+ }
+
+ ///
+ /// BoxBlurVertical is a private helper method for the BoxBlur
+ ///
+ /// BitmapBuffer which previously was created with BoxBlurHorizontal
+ /// Range must be odd!
+ private static void BoxBlurVertical(IFastBitmap targetFastBitmap, int range)
+ {
+ if (targetFastBitmap.HasAlphaChannel)
+ {
+ throw new NotSupportedException("BoxBlurVertical should NOT be called for bitmaps with alpha channel");
+ }
+
+ int halfRange = range / 2;
+ Color[] newColors = new Color[targetFastBitmap.Height];
+ byte[] tmpColor = new byte[4];
+ for (int x = targetFastBitmap.Left; x < targetFastBitmap.Right; x++)
+ {
+ int hits = 0;
+ int r = 0;
+ int g = 0;
+ int b = 0;
+ for (int y = targetFastBitmap.Top - halfRange; y < targetFastBitmap.Bottom; y++)
+ {
+ int oldPixel = y - halfRange - 1;
+ if (oldPixel >= targetFastBitmap.Top)
+ {
+ targetFastBitmap.GetColorAt(x, oldPixel, tmpColor);
+ r -= tmpColor[FastBitmap.ColorIndexR];
+ g -= tmpColor[FastBitmap.ColorIndexG];
+ b -= tmpColor[FastBitmap.ColorIndexB];
+ hits--;
+ }
+
+ int newPixel = y + halfRange;
+ if (newPixel < targetFastBitmap.Bottom)
+ {
+ targetFastBitmap.GetColorAt(x, newPixel, tmpColor);
+ r += tmpColor[FastBitmap.ColorIndexR];
+ g += tmpColor[FastBitmap.ColorIndexG];
+ b += tmpColor[FastBitmap.ColorIndexB];
+ hits++;
+ }
+
+ if (y >= targetFastBitmap.Top)
+ {
+ newColors[y - targetFastBitmap.Top] = Color.FromArgb(255, (byte) (r / hits), (byte) (g / hits), (byte) (b / hits));
+ }
+ }
+
+ for (int y = targetFastBitmap.Top; y < targetFastBitmap.Bottom; y++)
+ {
+ targetFastBitmap.SetColorAt(x, y, newColors[y - targetFastBitmap.Top]);
+ }
+ }
+ }
+
+ ///
+ /// BoxBlurVertical is a private helper method for the BoxBlur
+ ///
+ /// BitmapBuffer which previously was created with BoxBlurHorizontal
+ /// Range must be odd!
+ private static void BoxBlurVerticalAlpha(IFastBitmap targetFastBitmap, int range)
+ {
+ if (!targetFastBitmap.HasAlphaChannel)
+ {
+ throw new NotSupportedException("BoxBlurVerticalAlpha should be called for bitmaps with alpha channel");
+ }
+
+ int halfRange = range / 2;
+ Color[] newColors = new Color[targetFastBitmap.Height];
+ byte[] tmpColor = new byte[4];
+ for (int x = targetFastBitmap.Left; x < targetFastBitmap.Right; x++)
+ {
+ int hits = 0;
+ int a = 0;
+ int r = 0;
+ int g = 0;
+ int b = 0;
+ for (int y = targetFastBitmap.Top - halfRange; y < targetFastBitmap.Bottom; y++)
+ {
+ int oldPixel = y - halfRange - 1;
+ if (oldPixel >= targetFastBitmap.Top)
+ {
+ targetFastBitmap.GetColorAt(x, oldPixel, tmpColor);
+ a -= tmpColor[FastBitmap.ColorIndexA];
+ r -= tmpColor[FastBitmap.ColorIndexR];
+ g -= tmpColor[FastBitmap.ColorIndexG];
+ b -= tmpColor[FastBitmap.ColorIndexB];
+ hits--;
+ }
+
+ int newPixel = y + halfRange;
+ if (newPixel < targetFastBitmap.Bottom)
+ {
+ //int colorg = pixels[index + newPixelOffset];
+ targetFastBitmap.GetColorAt(x, newPixel, tmpColor);
+ a += tmpColor[FastBitmap.ColorIndexA];
+ r += tmpColor[FastBitmap.ColorIndexR];
+ g += tmpColor[FastBitmap.ColorIndexG];
+ b += tmpColor[FastBitmap.ColorIndexB];
+ hits++;
+ }
+
+ if (y >= targetFastBitmap.Top)
+ {
+ newColors[y - targetFastBitmap.Top] = Color.FromArgb((byte) (a / hits), (byte) (r / hits), (byte) (g / hits), (byte) (b / hits));
+ }
+ }
+
+ for (int y = targetFastBitmap.Top; y < targetFastBitmap.Bottom; y++)
+ {
+ targetFastBitmap.SetColorAt(x, y, newColors[y - targetFastBitmap.Top]);
+ }
+ }
+ }
+
+ ///
+ /// This method fixes the problem that we can't apply a filter outside the target bitmap,
+ /// therefor the filtered-bitmap will be shifted if we try to draw it outside the target bitmap.
+ /// It will also account for the Invert flag.
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static Rectangle CreateIntersectRectangle(Size applySize, Rectangle rect, bool invert)
+ {
+ Rectangle myRect;
+ if (invert)
+ {
+ myRect = new Rectangle(0, 0, applySize.Width, applySize.Height);
+ }
+ else
+ {
+ Rectangle applyRect = new Rectangle(0, 0, applySize.Width, applySize.Height);
+ myRect = new Rectangle(rect.X, rect.Y, rect.Width, rect.Height);
+ myRect.Intersect(applyRect);
+ }
+
+ return myRect;
+ }
+
+ ///
+ /// Create a new bitmap where the sourceBitmap has a shadow
+ ///
+ /// Bitmap to make a shadow on
+ /// How dark is the shadow
+ /// Size of the shadow
+ /// What pixel format must the returning bitmap have
+ ///
+ /// The transform matrix which describes how the elements need to be transformed to stay at the same location
+ /// Bitmap with the shadow, is bigger than the sourceBitmap!!
+ public static Bitmap CreateShadow(Image sourceBitmap, float darkness, int shadowSize, Point shadowOffset, Matrix matrix, PixelFormat targetPixelformat)
+ {
+ Point offset = shadowOffset;
+ offset.X += shadowSize - 1;
+ offset.Y += shadowSize - 1;
+ matrix.Translate(offset.X, offset.Y, MatrixOrder.Append);
+ // Create a new "clean" image
+ Bitmap returnImage = CreateEmpty(sourceBitmap.Width + shadowSize * 2, sourceBitmap.Height + shadowSize * 2, targetPixelformat, Color.Empty,
+ sourceBitmap.HorizontalResolution, sourceBitmap.VerticalResolution);
+ // Make sure the shadow is odd, there is no reason for an even blur!
+ if ((shadowSize & 1) == 0)
+ {
+ shadowSize++;
+ }
+
+ bool useGdiBlur = GDIplus.IsBlurPossible(shadowSize);
+ // Create "mask" for the shadow
+ ColorMatrix maskMatrix = new ColorMatrix
+ {
+ Matrix00 = 0,
+ Matrix11 = 0,
+ Matrix22 = 0
+ };
+ if (useGdiBlur)
+ {
+ maskMatrix.Matrix33 = darkness + 0.1f;
+ }
+ else
+ {
+ maskMatrix.Matrix33 = darkness;
+ }
+
+ Rectangle shadowRectangle = new Rectangle(new Point(shadowSize, shadowSize), sourceBitmap.Size);
+ ApplyColorMatrix((Bitmap) sourceBitmap, Rectangle.Empty, returnImage, shadowRectangle, maskMatrix);
+
+ // blur "shadow", apply to whole new image
+ if (useGdiBlur)
+ {
+ // Use GDI Blur
+ Rectangle newImageRectangle = new Rectangle(0, 0, returnImage.Width, returnImage.Height);
+ GDIplus.ApplyBlur(returnImage, newImageRectangle, shadowSize + 1, false);
+ }
+ else
+ {
+ // try normal software blur
+ //returnImage = CreateBlur(returnImage, newImageRectangle, true, shadowSize, 1d, false, newImageRectangle);
+ ApplyBoxBlur(returnImage, shadowSize);
+ }
+
+ // Draw the original image over the shadow
+ using (Graphics graphics = Graphics.FromImage(returnImage))
+ {
+ // Make sure we draw with the best quality!
+ graphics.SmoothingMode = SmoothingMode.HighQuality;
+ graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
+ graphics.CompositingQuality = CompositingQuality.HighQuality;
+ graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
+ // draw original with a TextureBrush so we have nice antialiasing!
+ using Brush textureBrush = new TextureBrush(sourceBitmap, WrapMode.Clamp);
+ // We need to do a translate-transform otherwise the image is wrapped
+ graphics.TranslateTransform(offset.X, offset.Y);
+ graphics.FillRectangle(textureBrush, 0, 0, sourceBitmap.Width, sourceBitmap.Height);
+ }
+
+ return returnImage;
+ }
+
+ ///
+ /// Return negative of Bitmap
+ ///
+ /// Bitmap to create a negative off
+ /// Negative bitmap
+ public static Bitmap CreateNegative(Image sourceImage)
+ {
+ Bitmap clone = (Bitmap) Clone(sourceImage);
+ ColorMatrix invertMatrix = new ColorMatrix(new[]
+ {
+ new float[]
+ {
+ -1, 0, 0, 0, 0
+ },
+ new float[]
+ {
+ 0, -1, 0, 0, 0
+ },
+ new float[]
+ {
+ 0, 0, -1, 0, 0
+ },
+ new float[]
+ {
+ 0, 0, 0, 1, 0
+ },
+ new float[]
+ {
+ 1, 1, 1, 1, 1
+ }
+ });
+ ApplyColorMatrix(clone, invertMatrix);
+ return clone;
+ }
+
+ ///
+ /// Apply a color matrix to the image
+ ///
+ /// Image to apply matrix to
+ /// ColorMatrix to apply
+ public static void ApplyColorMatrix(Bitmap source, ColorMatrix colorMatrix)
+ {
+ ApplyColorMatrix(source, Rectangle.Empty, source, Rectangle.Empty, colorMatrix);
+ }
+
+ ///
+ /// Apply a color matrix by copying from the source to the destination
+ ///
+ /// Image to copy from
+ /// Rectangle to copy from
+ /// Rectangle to copy to
+ /// Image to copy to
+ /// ColorMatrix to apply
+ public static void ApplyColorMatrix(Bitmap source, Rectangle sourceRect, Bitmap dest, Rectangle destRect, ColorMatrix colorMatrix)
+ {
+ using ImageAttributes imageAttributes = new ImageAttributes();
+ imageAttributes.ClearColorMatrix();
+ imageAttributes.SetColorMatrix(colorMatrix);
+ ApplyImageAttributes(source, sourceRect, dest, destRect, imageAttributes);
+ }
+
+ ///
+ /// Apply a color matrix by copying from the source to the destination
+ ///
+ /// Image to copy from
+ /// Rectangle to copy from
+ /// Rectangle to copy to
+ /// Image to copy to
+ /// ImageAttributes to apply
+ public static void ApplyImageAttributes(Bitmap source, Rectangle sourceRect, Bitmap dest, Rectangle destRect, ImageAttributes imageAttributes)
+ {
+ if (sourceRect == Rectangle.Empty)
+ {
+ sourceRect = new Rectangle(0, 0, source.Width, source.Height);
+ }
+
+ if (dest == null)
+ {
+ dest = source;
+ }
+
+ if (destRect == Rectangle.Empty)
+ {
+ destRect = new Rectangle(0, 0, dest.Width, dest.Height);
+ }
+
+ using Graphics graphics = Graphics.FromImage(dest);
+ // Make sure we draw with the best quality!
+ graphics.SmoothingMode = SmoothingMode.HighQuality;
+ graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
+ graphics.CompositingQuality = CompositingQuality.HighQuality;
+ graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
+ graphics.CompositingMode = CompositingMode.SourceCopy;
+
+ graphics.DrawImage(source, destRect, sourceRect.X, sourceRect.Y, sourceRect.Width, sourceRect.Height, GraphicsUnit.Pixel, imageAttributes);
+ }
+
+ ///
+ /// Returns a b/w of Bitmap
+ ///
+ /// Bitmap to create a b/w of
+ /// Threshold for monochrome filter (0 - 255), lower value means less black
+ /// b/w bitmap
+ public static Bitmap CreateMonochrome(Image sourceImage, byte threshold)
+ {
+ using IFastBitmap fastBitmap = FastBitmap.CreateCloneOf(sourceImage, sourceImage.PixelFormat);
+ for (int y = 0; y < fastBitmap.Height; y++)
+ {
+ for (int x = 0; x < fastBitmap.Width; x++)
+ {
+ Color color = fastBitmap.GetColorAt(x, y);
+ int colorBrightness = (color.R + color.G + color.B) / 3 > threshold ? 255 : 0;
+ Color monoColor = Color.FromArgb(color.A, colorBrightness, colorBrightness, colorBrightness);
+ fastBitmap.SetColorAt(x, y, monoColor);
+ }
+ }
+
+ return fastBitmap.UnlockAndReturnBitmap();
+ }
+
+ ///
+ /// Create a new bitmap where the sourceBitmap has a Simple border around it
+ ///
+ /// Bitmap to make a border on
+ /// Size of the border
+ /// Color of the border
+ /// What pixel format must the returning bitmap have
+ /// The transform matrix which describes how the elements need to be transformed to stay at the same location
+ /// Bitmap with the shadow, is bigger than the sourceBitmap!!
+ public static Image CreateBorder(Image sourceImage, int borderSize, Color borderColor, PixelFormat targetPixelformat, Matrix matrix)
+ {
+ // "return" the shifted offset, so the caller can e.g. move elements
+ Point offset = new Point(borderSize, borderSize);
+ matrix.Translate(offset.X, offset.Y, MatrixOrder.Append);
+
+ // Create a new "clean" image
+ Bitmap newImage = CreateEmpty(sourceImage.Width + borderSize * 2, sourceImage.Height + borderSize * 2, targetPixelformat, Color.Empty, sourceImage.HorizontalResolution,
+ sourceImage.VerticalResolution);
+ using (Graphics graphics = Graphics.FromImage(newImage))
+ {
+ // Make sure we draw with the best quality!
+ graphics.SmoothingMode = SmoothingMode.HighQuality;
+ graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
+ graphics.CompositingQuality = CompositingQuality.HighQuality;
+ graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
+ using (GraphicsPath path = new GraphicsPath())
+ {
+ path.AddRectangle(new Rectangle(borderSize >> 1, borderSize >> 1, newImage.Width - borderSize, newImage.Height - borderSize));
+ using Pen pen = new Pen(borderColor, borderSize)
+ {
+ LineJoin = LineJoin.Round,
+ StartCap = LineCap.Round,
+ EndCap = LineCap.Round
+ };
+ graphics.DrawPath(pen, path);
+ }
+
+ // draw original with a TextureBrush so we have nice antialiasing!
+ using Brush textureBrush = new TextureBrush(sourceImage, WrapMode.Clamp);
+ // We need to do a translate-tranform otherwise the image is wrapped
+ graphics.TranslateTransform(offset.X, offset.Y);
+ graphics.FillRectangle(textureBrush, 0, 0, sourceImage.Width, sourceImage.Height);
+ }
+
+ return newImage;
+ }
+
+ ///
+ /// Create ImageAttributes to modify
+ ///
+ ///
+ ///
+ ///
+ /// ImageAttributes
+ public static ImageAttributes CreateAdjustAttributes(float brightness, float contrast, float gamma)
+ {
+ float adjustedBrightness = brightness - 1.0f;
+ ColorMatrix applyColorMatrix = new ColorMatrix(
+ new[]
+ {
+ new[]
+ {
+ contrast, 0, 0, 0, 0
+ }, // scale red
+ new[]
+ {
+ 0, contrast, 0, 0, 0
+ }, // scale green
+ new[]
+ {
+ 0, 0, contrast, 0, 0
+ }, // scale blue
+ new[]
+ {
+ 0, 0, 0, 1.0f, 0
+ }, // don't scale alpha
+ new[]
+ {
+ adjustedBrightness, adjustedBrightness, adjustedBrightness, 0, 1
+ }
+ });
+
+ //create some image attributes
+ ImageAttributes attributes = new ImageAttributes();
+ attributes.ClearColorMatrix();
+ attributes.SetColorMatrix(applyColorMatrix, ColorMatrixFlag.Default, ColorAdjustType.Bitmap);
+ attributes.SetGamma(gamma, ColorAdjustType.Bitmap);
+ return attributes;
+ }
+
+ ///
+ /// Adjust the brightness, contract or gamma of an image.
+ /// Use the value "1.0f" for no changes.
+ ///
+ /// Original bitmap
+ ///
+ ///
+ ///
+ /// Bitmap with grayscale
+ public static Image Adjust(Image sourceImage, float brightness, float contrast, float gamma)
+ {
+ //create a blank bitmap the same size as original
+ // If using 8bpp than the following exception comes: A Graphics object cannot be created from an image that has an indexed pixel format.
+ Bitmap newBitmap = CreateEmpty(sourceImage.Width, sourceImage.Height, PixelFormat.Format24bppRgb, Color.Empty, sourceImage.HorizontalResolution,
+ sourceImage.VerticalResolution);
+ using (ImageAttributes adjustAttributes = CreateAdjustAttributes(brightness, contrast, gamma))
+ {
+ ApplyImageAttributes((Bitmap) sourceImage, Rectangle.Empty, newBitmap, Rectangle.Empty, adjustAttributes);
+ }
+
+ return newBitmap;
+ }
+
+ ///
+ /// Create a new bitmap where the sourceBitmap is in grayscale
+ ///
+ /// Original bitmap
+ /// Bitmap with grayscale
+ public static Image CreateGrayscale(Image sourceImage)
+ {
+ Bitmap clone = (Bitmap) Clone(sourceImage);
+ 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
+ }
+ });
+ ApplyColorMatrix(clone, grayscaleMatrix);
+ return clone;
+ }
+
+ ///
+ /// Checks if we support the pixel format
+ ///
+ /// PixelFormat to check
+ /// bool if we support it
+ public static bool SupportsPixelFormat(PixelFormat pixelformat)
+ {
+ return pixelformat.Equals(PixelFormat.Format32bppArgb) ||
+ pixelformat.Equals(PixelFormat.Format32bppPArgb) ||
+ pixelformat.Equals(PixelFormat.Format32bppRgb) ||
+ pixelformat.Equals(PixelFormat.Format24bppRgb);
+ }
+
+ ///
+ /// Wrapper for just cloning which calls the CloneArea
+ ///
+ /// Image to clone
+ /// Bitmap with clone image data
+ public static Image Clone(Image sourceImage)
+ {
+ if (sourceImage is Metafile)
+ {
+ return (Image) sourceImage.Clone();
+ }
+
+ return CloneArea(sourceImage, Rectangle.Empty, PixelFormat.DontCare);
+ }
+
+ ///
+ /// Wrapper for just cloning & TargetFormat which calls the CloneArea
+ ///
+ /// Image to clone
+ /// Target Format, use PixelFormat.DontCare if you want the original (or a default if the source PixelFormat is not supported)
+ /// Bitmap with clone image data
+ public static Bitmap Clone(Image sourceBitmap, PixelFormat targetFormat)
+ {
+ return CloneArea(sourceBitmap, Rectangle.Empty, targetFormat);
+ }
+
+ ///
+ /// Clone an image, taking some rules into account:
+ /// 1) When sourceRect is the whole bitmap there is a GDI+ bug in Clone
+ /// Clone will than return the same PixelFormat as the source
+ /// a quick workaround is using new Bitmap which uses a default of Format32bppArgb
+ /// 2) When going from a transparent to a non transparent bitmap, we draw the background white!
+ ///
+ /// Source bitmap to clone
+ /// Rectangle to copy from the source, use Rectangle.Empty for all
+ /// Target Format, use PixelFormat.DontCare if you want the original (or a default if the source PixelFormat is not supported)
+ ///
+ public static Bitmap CloneArea(Image sourceImage, Rectangle sourceRect, PixelFormat targetFormat)
+ {
+ Bitmap newImage;
+ Rectangle bitmapRect = new Rectangle(0, 0, sourceImage.Width, sourceImage.Height);
+
+ // Make sure the source is not Rectangle.Empty
+ if (Rectangle.Empty.Equals(sourceRect))
+ {
+ sourceRect = new Rectangle(0, 0, sourceImage.Width, sourceImage.Height);
+ }
+ else
+ {
+ sourceRect.Intersect(bitmapRect);
+ }
+
+ // If no pixelformat is supplied
+ if (PixelFormat.DontCare == targetFormat || PixelFormat.Undefined == targetFormat)
+ {
+ if (SupportsPixelFormat(sourceImage.PixelFormat))
+ {
+ targetFormat = sourceImage.PixelFormat;
+ }
+ else if (Image.IsAlphaPixelFormat(sourceImage.PixelFormat))
+ {
+ targetFormat = PixelFormat.Format32bppArgb;
+ }
+ else
+ {
+ targetFormat = PixelFormat.Format24bppRgb;
+ }
+ }
+
+ // check the target format
+ if (!SupportsPixelFormat(targetFormat))
+ {
+ targetFormat = Image.IsAlphaPixelFormat(targetFormat) ? PixelFormat.Format32bppArgb : PixelFormat.Format24bppRgb;
+ }
+
+ bool destinationIsTransparent = Image.IsAlphaPixelFormat(targetFormat);
+ bool sourceIsTransparent = Image.IsAlphaPixelFormat(sourceImage.PixelFormat);
+ bool fromTransparentToNon = !destinationIsTransparent && sourceIsTransparent;
+ bool isBitmap = sourceImage is Bitmap;
+ bool isAreaEqual = sourceRect.Equals(bitmapRect);
+ if (isAreaEqual || fromTransparentToNon || !isBitmap)
+ {
+ // Rule 1: if the areas are equal, always copy ourselves
+ newImage = new Bitmap(bitmapRect.Width, bitmapRect.Height, targetFormat);
+ // Make sure both images have the same resolution
+ newImage.SetResolution(sourceImage.HorizontalResolution, sourceImage.VerticalResolution);
+
+ using Graphics graphics = Graphics.FromImage(newImage);
+ if (fromTransparentToNon)
+ {
+ // Rule 2: Make sure the background color is white
+ graphics.Clear(Color.White);
+ }
+
+ // decide fastest copy method
+ if (isAreaEqual)
+ {
+ graphics.DrawImageUnscaled(sourceImage, 0, 0);
+ }
+ else
+ {
+ graphics.DrawImage(sourceImage, 0, 0, sourceRect, GraphicsUnit.Pixel);
+ }
+ }
+ else
+ {
+ // Let GDI+ decide how to convert, need to test what is quicker...
+ newImage = (sourceImage as Bitmap).Clone(sourceRect, targetFormat);
+ // Make sure both images have the same resolution
+ newImage.SetResolution(sourceImage.HorizontalResolution, sourceImage.VerticalResolution);
+ }
+
+ // In WINE someone getting the PropertyItems doesn't work
+ try
+ {
+ // Clone property items (EXIF information etc)
+ foreach (var propertyItem in sourceImage.PropertyItems)
+ {
+ try
+ {
+ newImage.SetPropertyItem(propertyItem);
+ }
+ catch (Exception innerEx)
+ {
+ Log.Warn("Problem cloning a propertyItem.", innerEx);
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ Log.Warn("Problem cloning a propertyItem.", ex);
+ }
+
+ return newImage;
+ }
+
+ ///
+ /// Rotate the bitmap
+ ///
+ ///
+ ///
+ ///
+ public static Image RotateFlip(Image sourceImage, RotateFlipType rotateFlipType)
+ {
+ Image returnImage = Clone(sourceImage);
+ returnImage.RotateFlip(rotateFlipType);
+ return returnImage;
+ }
+
+ ///
+ /// A generic way to create an empty image
+ ///
+ /// the source bitmap as the specifications for the new bitmap
+ /// The color to fill with, or Color.Empty to take the default depending on the pixel format
+ ///
+ public static Bitmap CreateEmptyLike(Image sourceImage, Color backgroundColor)
+ {
+ PixelFormat pixelFormat = sourceImage.PixelFormat;
+ if (backgroundColor.A < 255)
+ {
+ pixelFormat = PixelFormat.Format32bppArgb;
+ }
+
+ return CreateEmpty(sourceImage.Width, sourceImage.Height, pixelFormat, backgroundColor, sourceImage.HorizontalResolution, sourceImage.VerticalResolution);
+ }
+
+ ///
+ /// A generic way to create an empty image
+ ///
+ ///
+ ///
+ ///
+ /// The color to fill with, or Color.Empty to take the default depending on the pixel format
+ ///
+ ///
+ /// Bitmap
+ public static Bitmap CreateEmpty(int width, int height, PixelFormat format, Color backgroundColor, float horizontalResolution, float verticalResolution)
+ {
+ // Create a new "clean" image
+ Bitmap newImage = new Bitmap(width, height, format);
+ newImage.SetResolution(horizontalResolution, verticalResolution);
+ if (format != PixelFormat.Format8bppIndexed)
+ {
+ using Graphics graphics = Graphics.FromImage(newImage);
+ // Make sure the background color is what we want (transparent or white, depending on the pixel format)
+ if (!Color.Empty.Equals(backgroundColor))
+ {
+ graphics.Clear(backgroundColor);
+ }
+ else if (Image.IsAlphaPixelFormat(format))
+ {
+ graphics.Clear(Color.Transparent);
+ }
+ else
+ {
+ graphics.Clear(Color.White);
+ }
+ }
+
+ return newImage;
+ }
+
+ ///
+ /// Resize canvas with pixel to the left, right, top and bottom
+ ///
+ ///
+ /// The color to fill with, or Color.Empty to take the default depending on the pixel format
+ ///
+ ///
+ ///
+ ///
+ ///
+ /// a new bitmap with the source copied on it
+ public static Image ResizeCanvas(Image sourceImage, Color backgroundColor, int left, int right, int top, int bottom, Matrix matrix)
+ {
+ matrix.Translate(left, top, MatrixOrder.Append);
+ Bitmap newBitmap = CreateEmpty(sourceImage.Width + left + right, sourceImage.Height + top + bottom, sourceImage.PixelFormat, backgroundColor,
+ sourceImage.HorizontalResolution, sourceImage.VerticalResolution);
+ using (Graphics graphics = Graphics.FromImage(newBitmap))
+ {
+ graphics.DrawImageUnscaled(sourceImage, left, top);
+ }
+
+ return newBitmap;
+ }
+
+ ///
+ /// Wrapper for the more complex Resize, this resize could be used for e.g. Thumbnails
+ ///
+ ///
+ /// true to maintain the aspect ratio
+ ///
+ ///
+ ///
+ ///
+ public static Image ResizeImage(Image sourceImage, bool maintainAspectRatio, int newWidth, int newHeight, Matrix matrix)
+ {
+ return ResizeImage(sourceImage, maintainAspectRatio, false, Color.Empty, newWidth, newHeight, matrix);
+ }
+
+ ///
+ /// Count how many times the supplied color exists
+ ///
+ /// Image to count the pixels of
+ /// Color to count
+ /// true if Alpha needs to be checked
+ /// int with the number of pixels which have colorToCount
+ public static int CountColor(Image sourceImage, Color colorToCount, bool includeAlpha)
+ {
+ int colors = 0;
+ int toCount = colorToCount.ToArgb();
+ if (!includeAlpha)
+ {
+ toCount &= 0xffffff;
+ }
+
+ using IFastBitmap bb = FastBitmap.Create((Bitmap) sourceImage);
+ for (int y = 0; y < bb.Height; y++)
+ {
+ for (int x = 0; x < bb.Width; x++)
+ {
+ int bitmapcolor = bb.GetColorAt(x, y).ToArgb();
+ if (!includeAlpha)
+ {
+ bitmapcolor &= 0xffffff;
+ }
+
+ if (bitmapcolor == toCount)
+ {
+ colors++;
+ }
+ }
+ }
+
+ return colors;
+ }
+
+ ///
+ /// Scale the bitmap, keeping aspect ratio, but the canvas will always have the specified size.
+ ///
+ /// Image to scale
+ /// true to maintain the aspect ratio
+ /// Makes the image maintain aspect ratio, but the canvas get's the specified size
+ /// The color to fill with, or Color.Empty to take the default depending on the pixel format
+ /// new width
+ /// new height
+ ///
+ /// a new bitmap with the specified size, the source-Image scaled to fit with aspect ratio locked
+ public static Image ResizeImage(Image sourceImage, bool maintainAspectRatio, bool canvasUseNewSize, Color backgroundColor, int newWidth, int newHeight, Matrix matrix)
+ {
+ int destX = 0;
+ int destY = 0;
+
+ var nPercentW = newWidth / (float) sourceImage.Width;
+ var nPercentH = newHeight / (float) sourceImage.Height;
+ if (maintainAspectRatio)
+ {
+ if ((int) nPercentW == 1)
+ {
+ nPercentW = nPercentH;
+ if (canvasUseNewSize)
+ {
+ destX = Math.Max(0, Convert.ToInt32((newWidth - sourceImage.Width * nPercentW) / 2));
+ }
+ }
+ else if ((int) nPercentH == 1)
+ {
+ nPercentH = nPercentW;
+ if (canvasUseNewSize)
+ {
+ destY = Math.Max(0, Convert.ToInt32((newHeight - sourceImage.Height * nPercentH) / 2));
+ }
+ }
+ else if ((int) nPercentH != 0 && nPercentH < nPercentW)
+ {
+ nPercentW = nPercentH;
+ if (canvasUseNewSize)
+ {
+ destX = Math.Max(0, Convert.ToInt32((newWidth - sourceImage.Width * nPercentW) / 2));
+ }
+ }
+ else
+ {
+ nPercentH = nPercentW;
+ if (canvasUseNewSize)
+ {
+ destY = Math.Max(0, Convert.ToInt32((newHeight - sourceImage.Height * nPercentH) / 2));
+ }
+ }
+ }
+
+ int destWidth = (int) (sourceImage.Width * nPercentW);
+ int destHeight = (int) (sourceImage.Height * nPercentH);
+ if (newWidth == 0)
+ {
+ newWidth = destWidth;
+ }
+
+ if (newHeight == 0)
+ {
+ newHeight = destHeight;
+ }
+
+ Image newImage;
+ if (maintainAspectRatio && canvasUseNewSize)
+ {
+ newImage = CreateEmpty(newWidth, newHeight, sourceImage.PixelFormat, backgroundColor, sourceImage.HorizontalResolution, sourceImage.VerticalResolution);
+ matrix?.Scale((float) newWidth / sourceImage.Width, (float) newHeight / sourceImage.Height, MatrixOrder.Append);
+ }
+ else
+ {
+ newImage = CreateEmpty(destWidth, destHeight, sourceImage.PixelFormat, backgroundColor, sourceImage.HorizontalResolution, sourceImage.VerticalResolution);
+ matrix?.Scale((float) destWidth / sourceImage.Width, (float) destHeight / sourceImage.Height, MatrixOrder.Append);
+ }
+
+ using (Graphics graphics = Graphics.FromImage(newImage))
+ {
+ graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
+ using ImageAttributes wrapMode = new ImageAttributes();
+ wrapMode.SetWrapMode(WrapMode.TileFlipXY);
+ graphics.DrawImage(sourceImage, new Rectangle(destX, destY, destWidth, destHeight), 0, 0, sourceImage.Width, sourceImage.Height, GraphicsUnit.Pixel, wrapMode);
+ }
+
+ return newImage;
+ }
+
+ ///
+ /// Load a Greenshot surface from a stream
+ ///
+ /// Stream
+ ///
+ /// ISurface
+ public static ISurface LoadGreenshotSurface(Stream surfaceFileStream, ISurface returnSurface)
+ {
+ Image fileImage;
+ // Fixed problem that the bitmap stream is disposed... by Cloning the image
+ // This also ensures the bitmap is correctly created
+
+ // We create a copy of the bitmap, so everything else can be disposed
+ surfaceFileStream.Position = 0;
+ using (Image tmpImage = Image.FromStream(surfaceFileStream, true, true))
+ {
+ Log.DebugFormat("Loaded .greenshot file with Size {0}x{1} and PixelFormat {2}", tmpImage.Width, tmpImage.Height, tmpImage.PixelFormat);
+ fileImage = Clone(tmpImage);
+ }
+
+ // Start at -14 read "GreenshotXX.YY" (XX=Major, YY=Minor)
+ const int markerSize = 14;
+ surfaceFileStream.Seek(-markerSize, SeekOrigin.End);
+ using (StreamReader streamReader = new StreamReader(surfaceFileStream))
+ {
+ var greenshotMarker = streamReader.ReadToEnd();
+ if (!greenshotMarker.StartsWith("Greenshot"))
+ {
+ throw new ArgumentException("Stream is not a Greenshot file!");
+ }
+
+ Log.InfoFormat("Greenshot file format: {0}", greenshotMarker);
+ const int filesizeLocation = 8 + markerSize;
+ surfaceFileStream.Seek(-filesizeLocation, SeekOrigin.End);
+ using BinaryReader reader = new BinaryReader(surfaceFileStream);
+ long bytesWritten = reader.ReadInt64();
+ surfaceFileStream.Seek(-(bytesWritten + filesizeLocation), SeekOrigin.End);
+ returnSurface.LoadElementsFromStream(surfaceFileStream);
+ }
+
+ if (fileImage != null)
+ {
+ returnSurface.Image = fileImage;
+ Log.InfoFormat("Information about .greenshot file: {0}x{1}-{2} Resolution {3}x{4}", fileImage.Width, fileImage.Height, fileImage.PixelFormat,
+ fileImage.HorizontalResolution, fileImage.VerticalResolution);
+ }
+
+ return returnSurface;
+ }
+
+ ///
+ /// Create an image from a stream, if an extension is supplied more formats are supported.
+ ///
+ /// Stream
+ ///
+ /// Image
+ public static Image FromStream(Stream stream, string extension = null)
+ {
+ if (stream == null)
+ {
+ return null;
+ }
+
+ if (!string.IsNullOrEmpty(extension))
+ {
+ extension = extension.Replace(".", string.Empty);
+ }
+
+ // Make sure we can try multiple times
+ if (!stream.CanSeek)
+ {
+ var memoryStream = new MemoryStream();
+ stream.CopyTo(memoryStream);
+ stream = memoryStream;
+ }
+
+ Image returnImage = null;
+ if (StreamConverters.TryGetValue(extension ?? string.Empty, out var converter))
+ {
+ returnImage = converter(stream, extension);
+ }
+
+ // Fallback
+ if (returnImage == null)
+ {
+ // We create a copy of the bitmap, so everything else can be disposed
+ stream.Position = 0;
+ using var tmpImage = Image.FromStream(stream, true, true);
+ Log.DebugFormat("Loaded bitmap with Size {0}x{1} and PixelFormat {2}", tmpImage.Width, tmpImage.Height, tmpImage.PixelFormat);
+ returnImage = Clone(tmpImage, PixelFormat.Format32bppArgb);
+ }
+
+ return returnImage;
+ }
+ }
}
\ No newline at end of file
diff --git a/src/GreenshotPlugin/Core/ImageOutput.cs b/src/Greenshot.Base/Core/ImageOutput.cs
similarity index 97%
rename from src/GreenshotPlugin/Core/ImageOutput.cs
rename to src/Greenshot.Base/Core/ImageOutput.cs
index 233ffe377..d6a42a7d9 100644
--- a/src/GreenshotPlugin/Core/ImageOutput.cs
+++ b/src/Greenshot.Base/Core/ImageOutput.cs
@@ -1,798 +1,798 @@
-/*
- * Greenshot - a free and open source screenshot tool
- * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
- *
- * For more information see: http://getgreenshot.org/
- * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 1 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-using GreenshotPlugin.Controls;
-using log4net;
-using System;
-using System.Collections.Generic;
-using System.Diagnostics;
-using System.Drawing;
-using System.Drawing.Drawing2D;
-using System.Drawing.Imaging;
-using System.IO;
-using System.Reflection;
-using System.Runtime.InteropServices;
-using System.Text;
-using System.Text.RegularExpressions;
-using System.Windows.Forms;
-using GreenshotPlugin.IniFile;
-using GreenshotPlugin.Interfaces;
-using GreenshotPlugin.Interfaces.Plugin;
-using Encoder = System.Drawing.Imaging.Encoder;
-
-namespace GreenshotPlugin.Core
-{
- ///
- /// Description of ImageOutput.
- ///
- public static class ImageOutput
- {
- private static readonly ILog Log = LogManager.GetLogger(typeof(ImageOutput));
- private static readonly CoreConfiguration CoreConfig = IniConfig.GetIniSection();
- private static readonly int PROPERTY_TAG_SOFTWARE_USED = 0x0131;
- private static readonly Cache TmpFileCache = new Cache(10 * 60 * 60, RemoveExpiredTmpFile);
-
- ///
- /// Creates a PropertyItem (Metadata) to store with the image.
- /// For the possible ID's see: http://msdn.microsoft.com/de-de/library/system.drawing.imaging.propertyitem.id(v=vs.80).aspx
- /// This code uses Reflection to create a PropertyItem, although it's not adviced it's not as stupid as having a image in the project so we can read a PropertyItem from that!
- ///
- /// ID
- /// Text
- ///
- private static PropertyItem CreatePropertyItem(int id, string text)
- {
- PropertyItem propertyItem = null;
- try
- {
- ConstructorInfo ci = typeof(PropertyItem).GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public, null, new Type[]
- {
- }, null);
- propertyItem = (PropertyItem) ci.Invoke(null);
- // Make sure it's of type string
- propertyItem.Type = 2;
- // Set the ID
- propertyItem.Id = id;
- // Set the text
- byte[] byteString = Encoding.ASCII.GetBytes(text + " ");
- // Set Zero byte for String end.
- byteString[byteString.Length - 1] = 0;
- propertyItem.Value = byteString;
- propertyItem.Len = text.Length + 1;
- }
- catch (Exception e)
- {
- Log.WarnFormat("Error creating a PropertyItem: {0}", e.Message);
- }
-
- return propertyItem;
- }
-
- ///
- /// Saves ISurface to stream with specified output settings
- ///
- /// ISurface to save
- /// Stream to save to
- /// SurfaceOutputSettings
- public static void SaveToStream(ISurface surface, Stream stream, SurfaceOutputSettings outputSettings)
- {
- bool disposeImage = CreateImageFromSurface(surface, outputSettings, out var imageToSave);
- SaveToStream(imageToSave, surface, stream, outputSettings);
- // cleanup if needed
- if (disposeImage)
- {
- imageToSave?.Dispose();
- }
- }
-
- ///
- /// Saves image to stream with specified quality
- /// To prevent problems with GDI version of before Windows 7:
- /// the stream is checked if it's seekable and if needed a MemoryStream as "cache" is used.
- ///
- /// image to save
- /// surface for the elements, needed if the greenshot format is used
- /// Stream to save to
- /// SurfaceOutputSettings
- public static void SaveToStream(Image imageToSave, ISurface surface, Stream stream, SurfaceOutputSettings outputSettings)
- {
- bool useMemoryStream = false;
- MemoryStream memoryStream = null;
- if (outputSettings.Format == OutputFormat.greenshot && surface == null)
- {
- throw new ArgumentException("Surface needs to be set when using OutputFormat.Greenshot");
- }
-
- try
- {
- var imageFormat = outputSettings.Format switch
- {
- OutputFormat.bmp => ImageFormat.Bmp,
- OutputFormat.gif => ImageFormat.Gif,
- OutputFormat.jpg => ImageFormat.Jpeg,
- OutputFormat.tiff => ImageFormat.Tiff,
- OutputFormat.ico => ImageFormat.Icon,
- _ => ImageFormat.Png
- };
- Log.DebugFormat("Saving image to stream with Format {0} and PixelFormat {1}", imageFormat, imageToSave.PixelFormat);
-
- // Check if we want to use a memory stream, to prevent issues with non seakable streams
- // The save is made to the targetStream, this is directed to either the MemoryStream or the original
- Stream targetStream = stream;
- if (!stream.CanSeek)
- {
- useMemoryStream = true;
- Log.Warn("Using memorystream prevent an issue with saving to a non seekable stream.");
- memoryStream = new MemoryStream();
- targetStream = memoryStream;
- }
-
- if (Equals(imageFormat, ImageFormat.Jpeg))
- {
- bool foundEncoder = false;
- foreach (ImageCodecInfo imageCodec in ImageCodecInfo.GetImageEncoders())
- {
- if (imageCodec.FormatID == imageFormat.Guid)
- {
- EncoderParameters parameters = new EncoderParameters(1)
- {
- Param =
- {
- [0] = new EncoderParameter(Encoder.Quality, outputSettings.JPGQuality)
- }
- };
- // Removing transparency if it's not supported in the output
- if (Image.IsAlphaPixelFormat(imageToSave.PixelFormat))
- {
- Image nonAlphaImage = ImageHelper.Clone(imageToSave, PixelFormat.Format24bppRgb);
- AddTag(nonAlphaImage);
- nonAlphaImage.Save(targetStream, imageCodec, parameters);
- nonAlphaImage.Dispose();
- }
- else
- {
- AddTag(imageToSave);
- imageToSave.Save(targetStream, imageCodec, parameters);
- }
-
- foundEncoder = true;
- break;
- }
- }
-
- if (!foundEncoder)
- {
- throw new ApplicationException("No JPG encoder found, this should not happen.");
- }
- }
- else if (Equals(imageFormat, ImageFormat.Icon))
- {
- // FEATURE-916: Added Icon support
- IList images = new List
- {
- imageToSave
- };
- WriteIcon(stream, images);
- }
- else
- {
- bool needsDispose = false;
- // Removing transparency if it's not supported in the output
- if (!Equals(imageFormat, ImageFormat.Png) && Image.IsAlphaPixelFormat(imageToSave.PixelFormat))
- {
- imageToSave = ImageHelper.Clone(imageToSave, PixelFormat.Format24bppRgb);
- needsDispose = true;
- }
-
- AddTag(imageToSave);
- // Added for OptiPNG
- bool processed = false;
- if (Equals(imageFormat, ImageFormat.Png) && !string.IsNullOrEmpty(CoreConfig.OptimizePNGCommand))
- {
- processed = ProcessPngImageExternally(imageToSave, targetStream);
- }
-
- if (!processed)
- {
- imageToSave.Save(targetStream, imageFormat);
- }
-
- if (needsDispose)
- {
- imageToSave.Dispose();
- }
- }
-
- // If we used a memory stream, we need to stream the memory stream to the original stream.
- if (useMemoryStream)
- {
- memoryStream.WriteTo(stream);
- }
-
- // Output the surface elements, size and marker to the stream
- if (outputSettings.Format != OutputFormat.greenshot)
- {
- return;
- }
-
- using MemoryStream tmpStream = new MemoryStream();
- long bytesWritten = surface.SaveElementsToStream(tmpStream);
- using BinaryWriter writer = new BinaryWriter(tmpStream);
- writer.Write(bytesWritten);
- Version v = Assembly.GetExecutingAssembly().GetName().Version;
- byte[] marker = Encoding.ASCII.GetBytes($"Greenshot{v.Major:00}.{v.Minor:00}");
- writer.Write(marker);
- tmpStream.WriteTo(stream);
- }
- finally
- {
- memoryStream?.Dispose();
- }
- }
-
- ///
- /// Write the passed Image to a tmp-file and call an external process, than read the file back and write it to the targetStream
- ///
- /// Image to pass to the external process
- /// stream to write the processed image to
- ///
- private static bool ProcessPngImageExternally(Image imageToProcess, Stream targetStream)
- {
- if (string.IsNullOrEmpty(CoreConfig.OptimizePNGCommand))
- {
- return false;
- }
-
- if (!File.Exists(CoreConfig.OptimizePNGCommand))
- {
- Log.WarnFormat("Can't find 'OptimizePNGCommand' {0}", CoreConfig.OptimizePNGCommand);
- return false;
- }
-
- string tmpFileName = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName() + ".png");
- try
- {
- using (FileStream tmpStream = File.Create(tmpFileName))
- {
- Log.DebugFormat("Writing png to tmp file: {0}", tmpFileName);
- imageToProcess.Save(tmpStream, ImageFormat.Png);
- if (Log.IsDebugEnabled)
- {
- Log.DebugFormat("File size before processing {0}", new FileInfo(tmpFileName).Length);
- }
- }
-
- if (Log.IsDebugEnabled)
- {
- Log.DebugFormat("Starting : {0}", CoreConfig.OptimizePNGCommand);
- }
-
- ProcessStartInfo processStartInfo = new ProcessStartInfo(CoreConfig.OptimizePNGCommand)
- {
- Arguments = string.Format(CoreConfig.OptimizePNGCommandArguments, tmpFileName),
- CreateNoWindow = true,
- RedirectStandardOutput = true,
- RedirectStandardError = true,
- UseShellExecute = false
- };
- using Process process = Process.Start(processStartInfo);
- if (process != null)
- {
- process.WaitForExit();
- if (process.ExitCode == 0)
- {
- if (Log.IsDebugEnabled)
- {
- Log.DebugFormat("File size after processing {0}", new FileInfo(tmpFileName).Length);
- Log.DebugFormat("Reading back tmp file: {0}", tmpFileName);
- }
-
- byte[] processedImage = File.ReadAllBytes(tmpFileName);
- targetStream.Write(processedImage, 0, processedImage.Length);
- return true;
- }
-
- Log.ErrorFormat("Error while processing PNG image: {0}", process.ExitCode);
- Log.ErrorFormat("Output: {0}", process.StandardOutput.ReadToEnd());
- Log.ErrorFormat("Error: {0}", process.StandardError.ReadToEnd());
- }
- }
- catch (Exception e)
- {
- Log.Error("Error while processing PNG image: ", e);
- }
- finally
- {
- if (File.Exists(tmpFileName))
- {
- Log.DebugFormat("Cleaning up tmp file: {0}", tmpFileName);
- File.Delete(tmpFileName);
- }
- }
-
- return false;
- }
-
- ///
- /// Create an image from a surface with the settings from the output settings applied
- ///
- ///
- ///
- ///
- /// true if the image must be disposed
- public static bool CreateImageFromSurface(ISurface surface, SurfaceOutputSettings outputSettings, out Image imageToSave)
- {
- bool disposeImage = false;
-
- if (outputSettings.Format == OutputFormat.greenshot || outputSettings.SaveBackgroundOnly)
- {
- // We save the image of the surface, this should not be disposed
- imageToSave = surface.Image;
- }
- else
- {
- // We create the export image of the surface to save
- imageToSave = surface.GetImageForExport();
- disposeImage = true;
- }
-
- // The following block of modifications should be skipped when saving the greenshot format, no effects or otherwise!
- if (outputSettings.Format == OutputFormat.greenshot)
- {
- return disposeImage;
- }
-
- Image tmpImage;
- if (outputSettings.Effects != null && outputSettings.Effects.Count > 0)
- {
- // apply effects, if there are any
- using (Matrix matrix = new Matrix())
- {
- tmpImage = ImageHelper.ApplyEffects(imageToSave, outputSettings.Effects, matrix);
- }
-
- if (tmpImage != null)
- {
- if (disposeImage)
- {
- imageToSave.Dispose();
- }
-
- imageToSave = tmpImage;
- disposeImage = true;
- }
- }
-
- // check for color reduction, forced or automatically, only when the DisableReduceColors is false
- if (outputSettings.DisableReduceColors || (!CoreConfig.OutputFileAutoReduceColors && !outputSettings.ReduceColors))
- {
- return disposeImage;
- }
-
- bool isAlpha = Image.IsAlphaPixelFormat(imageToSave.PixelFormat);
- if (outputSettings.ReduceColors || (!isAlpha && CoreConfig.OutputFileAutoReduceColors))
- {
- using var quantizer = new WuQuantizer((Bitmap) imageToSave);
- int colorCount = quantizer.GetColorCount();
- Log.InfoFormat("Image with format {0} has {1} colors", imageToSave.PixelFormat, colorCount);
- if (!outputSettings.ReduceColors && colorCount >= 256)
- {
- return disposeImage;
- }
-
- try
- {
- Log.Info("Reducing colors on bitmap to 256.");
- tmpImage = quantizer.GetQuantizedImage(CoreConfig.OutputFileReduceColorsTo);
- if (disposeImage)
- {
- imageToSave.Dispose();
- }
-
- imageToSave = tmpImage;
- // Make sure the "new" image is disposed
- disposeImage = true;
- }
- catch (Exception e)
- {
- Log.Warn("Error occurred while Quantizing the image, ignoring and using original. Error: ", e);
- }
- }
- else if (isAlpha && !outputSettings.ReduceColors)
- {
- Log.Info("Skipping 'optional' color reduction as the image has alpha");
- }
-
- return disposeImage;
- }
-
- ///
- /// Add the greenshot property!
- ///
- ///
- private static void AddTag(Image imageToSave)
- {
- // Create meta-data
- PropertyItem softwareUsedPropertyItem = CreatePropertyItem(PROPERTY_TAG_SOFTWARE_USED, "Greenshot");
- if (softwareUsedPropertyItem != null)
- {
- try
- {
- imageToSave.SetPropertyItem(softwareUsedPropertyItem);
- }
- catch (Exception)
- {
- Log.WarnFormat("Couldn't set property {0}", softwareUsedPropertyItem.Id);
- }
- }
- }
-
- ///
- /// Load a Greenshot surface
- ///
- ///
- ///
- ///
- public static ISurface LoadGreenshotSurface(string fullPath, ISurface returnSurface)
- {
- if (string.IsNullOrEmpty(fullPath))
- {
- return null;
- }
-
- Log.InfoFormat("Loading image from file {0}", fullPath);
- // Fixed lock problem Bug #3431881
- using (Stream surfaceFileStream = File.OpenRead(fullPath))
- {
- returnSurface = ImageHelper.LoadGreenshotSurface(surfaceFileStream, returnSurface);
- }
-
- if (returnSurface != null)
- {
- Log.InfoFormat("Information about file {0}: {1}x{2}-{3} Resolution {4}x{5}", fullPath, returnSurface.Image.Width, returnSurface.Image.Height,
- returnSurface.Image.PixelFormat, returnSurface.Image.HorizontalResolution, returnSurface.Image.VerticalResolution);
- }
-
- return returnSurface;
- }
-
- ///
- /// Saves image to specific path with specified quality
- ///
- public static void Save(ISurface surface, string fullPath, bool allowOverwrite, SurfaceOutputSettings outputSettings, bool copyPathToClipboard)
- {
- fullPath = FilenameHelper.MakeFqFilenameSafe(fullPath);
- string path = Path.GetDirectoryName(fullPath);
-
- // check whether path exists - if not create it
- if (path != null)
- {
- DirectoryInfo di = new DirectoryInfo(path);
- if (!di.Exists)
- {
- Directory.CreateDirectory(di.FullName);
- }
- }
-
- if (!allowOverwrite && File.Exists(fullPath))
- {
- ArgumentException throwingException = new ArgumentException("File '" + fullPath + "' already exists.");
- throwingException.Data.Add("fullPath", fullPath);
- throw throwingException;
- }
-
- Log.DebugFormat("Saving surface to {0}", fullPath);
- // Create the stream and call SaveToStream
- using (FileStream stream = new FileStream(fullPath, FileMode.Create, FileAccess.Write))
- {
- SaveToStream(surface, stream, outputSettings);
- }
-
- if (copyPathToClipboard)
- {
- ClipboardHelper.SetClipboardData(fullPath);
- }
- }
-
- ///
- /// Get the OutputFormat for a filename
- ///
- /// filename (can be a complete path)
- /// OutputFormat
- public static OutputFormat FormatForFilename(string fullPath)
- {
- // Fix for bug 2912959
- string extension = fullPath.Substring(fullPath.LastIndexOf(".", StringComparison.Ordinal) + 1);
- OutputFormat format = OutputFormat.png;
- try
- {
- format = (OutputFormat) Enum.Parse(typeof(OutputFormat), extension.ToLower());
- }
- catch (ArgumentException ae)
- {
- Log.Warn("Couldn't parse extension: " + extension, ae);
- }
-
- return format;
- }
-
- ///
- /// Save with showing a dialog
- ///
- ///
- ///
- /// Path to filename
- public static string SaveWithDialog(ISurface surface, ICaptureDetails captureDetails)
- {
- string returnValue = null;
- using (SaveImageFileDialog saveImageFileDialog = new SaveImageFileDialog(captureDetails))
- {
- DialogResult dialogResult = saveImageFileDialog.ShowDialog();
- if (dialogResult.Equals(DialogResult.OK))
- {
- try
- {
- string fileNameWithExtension = saveImageFileDialog.FileNameWithExtension;
- SurfaceOutputSettings outputSettings = new SurfaceOutputSettings(FormatForFilename(fileNameWithExtension));
- if (CoreConfig.OutputFilePromptQuality)
- {
- QualityDialog qualityDialog = new QualityDialog(outputSettings);
- qualityDialog.ShowDialog();
- }
-
- // TODO: For now we always overwrite, should be changed
- Save(surface, fileNameWithExtension, true, outputSettings, CoreConfig.OutputFileCopyPathToClipboard);
- returnValue = fileNameWithExtension;
- IniConfig.Save();
- }
- catch (ExternalException)
- {
- MessageBox.Show(Language.GetFormattedString("error_nowriteaccess", saveImageFileDialog.FileName).Replace(@"\\", @"\"), Language.GetString("error"));
- }
- }
- }
-
- return returnValue;
- }
-
- ///
- /// Create a tmpfile which has the name like in the configured pattern.
- /// Used e.g. by the email export
- ///
- ///
- ///
- ///
- /// Path to image file
- public static string SaveNamedTmpFile(ISurface surface, ICaptureDetails captureDetails, SurfaceOutputSettings outputSettings)
- {
- string pattern = CoreConfig.OutputFileFilenamePattern;
- if (string.IsNullOrEmpty(pattern?.Trim()))
- {
- pattern = "greenshot ${capturetime}";
- }
-
- string filename = FilenameHelper.GetFilenameFromPattern(pattern, outputSettings.Format, captureDetails);
- // Prevent problems with "other characters", which causes a problem in e.g. Outlook 2007 or break our HTML
- filename = Regex.Replace(filename, @"[^\d\w\.]", "_");
- // Remove multiple "_"
- filename = Regex.Replace(filename, @"_+", "_");
- string tmpFile = Path.Combine(Path.GetTempPath(), filename);
-
- Log.Debug("Creating TMP File: " + tmpFile);
-
- // 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
- try
- {
- Save(surface, tmpFile, true, outputSettings, false);
- TmpFileCache.Add(tmpFile, tmpFile);
- }
- catch (Exception e)
- {
- // Show the problem
- MessageBox.Show(e.Message, "Error");
- // when save failed we present a SaveWithDialog
- tmpFile = SaveWithDialog(surface, captureDetails);
- }
-
- return tmpFile;
- }
-
- ///
- /// Remove a tmpfile which was created by SaveNamedTmpFile
- /// Used e.g. by the email export
- ///
- ///
- /// true if it worked
- public static bool DeleteNamedTmpFile(string tmpfile)
- {
- Log.Debug("Deleting TMP File: " + tmpfile);
- try
- {
- if (File.Exists(tmpfile))
- {
- File.Delete(tmpfile);
- TmpFileCache.Remove(tmpfile);
- }
-
- return true;
- }
- catch (Exception ex)
- {
- Log.Warn("Error deleting tmp file: ", ex);
- }
-
- return false;
- }
-
- ///
- /// Helper method to create a temp image file
- ///
- ///
- ///
- ///
- ///
- public static string SaveToTmpFile(ISurface surface, SurfaceOutputSettings outputSettings, string destinationPath)
- {
- string tmpFile = Path.GetRandomFileName() + "." + outputSettings.Format;
- // Prevent problems with "other characters", which could cause problems
- tmpFile = Regex.Replace(tmpFile, @"[^\d\w\.]", string.Empty);
- if (destinationPath == null)
- {
- destinationPath = Path.GetTempPath();
- }
-
- string tmpPath = Path.Combine(destinationPath, tmpFile);
- Log.Debug("Creating TMP File : " + tmpPath);
-
- try
- {
- Save(surface, tmpPath, true, outputSettings, false);
- TmpFileCache.Add(tmpPath, tmpPath);
- }
- catch (Exception)
- {
- return null;
- }
-
- return tmpPath;
- }
-
- ///
- /// Cleanup all created tmpfiles
- ///
- public static void RemoveTmpFiles()
- {
- foreach (string tmpFile in TmpFileCache.Elements)
- {
- if (File.Exists(tmpFile))
- {
- Log.DebugFormat("Removing old temp file {0}", tmpFile);
- File.Delete(tmpFile);
- }
-
- TmpFileCache.Remove(tmpFile);
- }
- }
-
- ///
- /// Cleanup handler for expired tempfiles
- ///
- ///
- ///
- private static void RemoveExpiredTmpFile(string filekey, object filename)
- {
- if (filename is string path && File.Exists(path))
- {
- Log.DebugFormat("Removing expired file {0}", path);
- File.Delete(path);
- }
- }
-
- ///
- /// Write the images to the stream as icon
- /// Every image is resized to 256x256 (but the content maintains the aspect ratio)
- ///
- /// Stream to write to
- /// List of images
- public static void WriteIcon(Stream stream, IList images)
- {
- var binaryWriter = new BinaryWriter(stream);
- //
- // ICONDIR structure
- //
- binaryWriter.Write((short) 0); // reserved
- binaryWriter.Write((short) 1); // image type (icon)
- binaryWriter.Write((short) images.Count); // number of images
-
- IList imageSizes = new List();
- IList encodedImages = new List();
- foreach (var image in images)
- {
- // Pick the best fit
- var sizes = new[]
- {
- 16, 32, 48
- };
- int size = 256;
- foreach (var possibleSize in sizes)
- {
- if (image.Width <= possibleSize && image.Height <= possibleSize)
- {
- size = possibleSize;
- break;
- }
- }
-
- var imageStream = new MemoryStream();
- if (image.Width == size && image.Height == size)
- {
- using var clonedImage = ImageHelper.Clone(image, PixelFormat.Format32bppArgb);
- clonedImage.Save(imageStream, ImageFormat.Png);
- imageSizes.Add(new Size(size, size));
- }
- else
- {
- // Resize to the specified size, first make sure the image is 32bpp
- using var clonedImage = ImageHelper.Clone(image, PixelFormat.Format32bppArgb);
- using var resizedImage = ImageHelper.ResizeImage(clonedImage, true, true, Color.Empty, size, size, null);
- resizedImage.Save(imageStream, ImageFormat.Png);
- imageSizes.Add(resizedImage.Size);
- }
-
- imageStream.Seek(0, SeekOrigin.Begin);
- encodedImages.Add(imageStream);
- }
-
- //
- // ICONDIRENTRY structure
- //
- const int iconDirSize = 6;
- const int iconDirEntrySize = 16;
-
- var offset = iconDirSize + (images.Count * iconDirEntrySize);
- for (int i = 0; i < images.Count; i++)
- {
- var imageSize = imageSizes[i];
- // Write the width / height, 0 means 256
- binaryWriter.Write(imageSize.Width == 256 ? (byte) 0 : (byte) imageSize.Width);
- binaryWriter.Write(imageSize.Height == 256 ? (byte) 0 : (byte) imageSize.Height);
- binaryWriter.Write((byte) 0); // no pallete
- binaryWriter.Write((byte) 0); // reserved
- binaryWriter.Write((short) 0); // no color planes
- binaryWriter.Write((short) 32); // 32 bpp
- binaryWriter.Write((int) encodedImages[i].Length); // image data length
- binaryWriter.Write(offset);
- offset += (int) encodedImages[i].Length;
- }
-
- binaryWriter.Flush();
- //
- // Write image data
- //
- foreach (var encodedImage in encodedImages)
- {
- encodedImage.WriteTo(stream);
- encodedImage.Dispose();
- }
- }
- }
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Drawing;
+using System.Drawing.Drawing2D;
+using System.Drawing.Imaging;
+using System.IO;
+using System.Reflection;
+using System.Runtime.InteropServices;
+using System.Text;
+using System.Text.RegularExpressions;
+using System.Windows.Forms;
+using Greenshot.Base.Controls;
+using Greenshot.Base.IniFile;
+using Greenshot.Base.Interfaces;
+using Greenshot.Base.Interfaces.Plugin;
+using log4net;
+using Encoder = System.Drawing.Imaging.Encoder;
+
+namespace Greenshot.Base.Core
+{
+ ///
+ /// Description of ImageOutput.
+ ///
+ public static class ImageOutput
+ {
+ private static readonly ILog Log = LogManager.GetLogger(typeof(ImageOutput));
+ private static readonly CoreConfiguration CoreConfig = IniConfig.GetIniSection();
+ private static readonly int PROPERTY_TAG_SOFTWARE_USED = 0x0131;
+ private static readonly Cache TmpFileCache = new Cache(10 * 60 * 60, RemoveExpiredTmpFile);
+
+ ///
+ /// Creates a PropertyItem (Metadata) to store with the image.
+ /// For the possible ID's see: http://msdn.microsoft.com/de-de/library/system.drawing.imaging.propertyitem.id(v=vs.80).aspx
+ /// This code uses Reflection to create a PropertyItem, although it's not adviced it's not as stupid as having a image in the project so we can read a PropertyItem from that!
+ ///
+ /// ID
+ /// Text
+ ///
+ private static PropertyItem CreatePropertyItem(int id, string text)
+ {
+ PropertyItem propertyItem = null;
+ try
+ {
+ ConstructorInfo ci = typeof(PropertyItem).GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public, null, new Type[]
+ {
+ }, null);
+ propertyItem = (PropertyItem) ci.Invoke(null);
+ // Make sure it's of type string
+ propertyItem.Type = 2;
+ // Set the ID
+ propertyItem.Id = id;
+ // Set the text
+ byte[] byteString = Encoding.ASCII.GetBytes(text + " ");
+ // Set Zero byte for String end.
+ byteString[byteString.Length - 1] = 0;
+ propertyItem.Value = byteString;
+ propertyItem.Len = text.Length + 1;
+ }
+ catch (Exception e)
+ {
+ Log.WarnFormat("Error creating a PropertyItem: {0}", e.Message);
+ }
+
+ return propertyItem;
+ }
+
+ ///
+ /// Saves ISurface to stream with specified output settings
+ ///
+ /// ISurface to save
+ /// Stream to save to
+ /// SurfaceOutputSettings
+ public static void SaveToStream(ISurface surface, Stream stream, SurfaceOutputSettings outputSettings)
+ {
+ bool disposeImage = CreateImageFromSurface(surface, outputSettings, out var imageToSave);
+ SaveToStream(imageToSave, surface, stream, outputSettings);
+ // cleanup if needed
+ if (disposeImage)
+ {
+ imageToSave?.Dispose();
+ }
+ }
+
+ ///
+ /// Saves image to stream with specified quality
+ /// To prevent problems with GDI version of before Windows 7:
+ /// the stream is checked if it's seekable and if needed a MemoryStream as "cache" is used.
+ ///
+ /// image to save
+ /// surface for the elements, needed if the greenshot format is used
+ /// Stream to save to
+ /// SurfaceOutputSettings
+ public static void SaveToStream(Image imageToSave, ISurface surface, Stream stream, SurfaceOutputSettings outputSettings)
+ {
+ bool useMemoryStream = false;
+ MemoryStream memoryStream = null;
+ if (outputSettings.Format == OutputFormat.greenshot && surface == null)
+ {
+ throw new ArgumentException("Surface needs to be set when using OutputFormat.Greenshot");
+ }
+
+ try
+ {
+ var imageFormat = outputSettings.Format switch
+ {
+ OutputFormat.bmp => ImageFormat.Bmp,
+ OutputFormat.gif => ImageFormat.Gif,
+ OutputFormat.jpg => ImageFormat.Jpeg,
+ OutputFormat.tiff => ImageFormat.Tiff,
+ OutputFormat.ico => ImageFormat.Icon,
+ _ => ImageFormat.Png
+ };
+ Log.DebugFormat("Saving image to stream with Format {0} and PixelFormat {1}", imageFormat, imageToSave.PixelFormat);
+
+ // Check if we want to use a memory stream, to prevent issues with non seakable streams
+ // The save is made to the targetStream, this is directed to either the MemoryStream or the original
+ Stream targetStream = stream;
+ if (!stream.CanSeek)
+ {
+ useMemoryStream = true;
+ Log.Warn("Using memorystream prevent an issue with saving to a non seekable stream.");
+ memoryStream = new MemoryStream();
+ targetStream = memoryStream;
+ }
+
+ if (Equals(imageFormat, ImageFormat.Jpeg))
+ {
+ bool foundEncoder = false;
+ foreach (ImageCodecInfo imageCodec in ImageCodecInfo.GetImageEncoders())
+ {
+ if (imageCodec.FormatID == imageFormat.Guid)
+ {
+ EncoderParameters parameters = new EncoderParameters(1)
+ {
+ Param =
+ {
+ [0] = new EncoderParameter(Encoder.Quality, outputSettings.JPGQuality)
+ }
+ };
+ // Removing transparency if it's not supported in the output
+ if (Image.IsAlphaPixelFormat(imageToSave.PixelFormat))
+ {
+ Image nonAlphaImage = ImageHelper.Clone(imageToSave, PixelFormat.Format24bppRgb);
+ AddTag(nonAlphaImage);
+ nonAlphaImage.Save(targetStream, imageCodec, parameters);
+ nonAlphaImage.Dispose();
+ }
+ else
+ {
+ AddTag(imageToSave);
+ imageToSave.Save(targetStream, imageCodec, parameters);
+ }
+
+ foundEncoder = true;
+ break;
+ }
+ }
+
+ if (!foundEncoder)
+ {
+ throw new ApplicationException("No JPG encoder found, this should not happen.");
+ }
+ }
+ else if (Equals(imageFormat, ImageFormat.Icon))
+ {
+ // FEATURE-916: Added Icon support
+ IList images = new List
+ {
+ imageToSave
+ };
+ WriteIcon(stream, images);
+ }
+ else
+ {
+ bool needsDispose = false;
+ // Removing transparency if it's not supported in the output
+ if (!Equals(imageFormat, ImageFormat.Png) && Image.IsAlphaPixelFormat(imageToSave.PixelFormat))
+ {
+ imageToSave = ImageHelper.Clone(imageToSave, PixelFormat.Format24bppRgb);
+ needsDispose = true;
+ }
+
+ AddTag(imageToSave);
+ // Added for OptiPNG
+ bool processed = false;
+ if (Equals(imageFormat, ImageFormat.Png) && !string.IsNullOrEmpty(CoreConfig.OptimizePNGCommand))
+ {
+ processed = ProcessPngImageExternally(imageToSave, targetStream);
+ }
+
+ if (!processed)
+ {
+ imageToSave.Save(targetStream, imageFormat);
+ }
+
+ if (needsDispose)
+ {
+ imageToSave.Dispose();
+ }
+ }
+
+ // If we used a memory stream, we need to stream the memory stream to the original stream.
+ if (useMemoryStream)
+ {
+ memoryStream.WriteTo(stream);
+ }
+
+ // Output the surface elements, size and marker to the stream
+ if (outputSettings.Format != OutputFormat.greenshot)
+ {
+ return;
+ }
+
+ using MemoryStream tmpStream = new MemoryStream();
+ long bytesWritten = surface.SaveElementsToStream(tmpStream);
+ using BinaryWriter writer = new BinaryWriter(tmpStream);
+ writer.Write(bytesWritten);
+ Version v = Assembly.GetExecutingAssembly().GetName().Version;
+ byte[] marker = Encoding.ASCII.GetBytes($"Greenshot{v.Major:00}.{v.Minor:00}");
+ writer.Write(marker);
+ tmpStream.WriteTo(stream);
+ }
+ finally
+ {
+ memoryStream?.Dispose();
+ }
+ }
+
+ ///
+ /// Write the passed Image to a tmp-file and call an external process, than read the file back and write it to the targetStream
+ ///
+ /// Image to pass to the external process
+ /// stream to write the processed image to
+ ///
+ private static bool ProcessPngImageExternally(Image imageToProcess, Stream targetStream)
+ {
+ if (string.IsNullOrEmpty(CoreConfig.OptimizePNGCommand))
+ {
+ return false;
+ }
+
+ if (!File.Exists(CoreConfig.OptimizePNGCommand))
+ {
+ Log.WarnFormat("Can't find 'OptimizePNGCommand' {0}", CoreConfig.OptimizePNGCommand);
+ return false;
+ }
+
+ string tmpFileName = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName() + ".png");
+ try
+ {
+ using (FileStream tmpStream = File.Create(tmpFileName))
+ {
+ Log.DebugFormat("Writing png to tmp file: {0}", tmpFileName);
+ imageToProcess.Save(tmpStream, ImageFormat.Png);
+ if (Log.IsDebugEnabled)
+ {
+ Log.DebugFormat("File size before processing {0}", new FileInfo(tmpFileName).Length);
+ }
+ }
+
+ if (Log.IsDebugEnabled)
+ {
+ Log.DebugFormat("Starting : {0}", CoreConfig.OptimizePNGCommand);
+ }
+
+ ProcessStartInfo processStartInfo = new ProcessStartInfo(CoreConfig.OptimizePNGCommand)
+ {
+ Arguments = string.Format(CoreConfig.OptimizePNGCommandArguments, tmpFileName),
+ CreateNoWindow = true,
+ RedirectStandardOutput = true,
+ RedirectStandardError = true,
+ UseShellExecute = false
+ };
+ using Process process = Process.Start(processStartInfo);
+ if (process != null)
+ {
+ process.WaitForExit();
+ if (process.ExitCode == 0)
+ {
+ if (Log.IsDebugEnabled)
+ {
+ Log.DebugFormat("File size after processing {0}", new FileInfo(tmpFileName).Length);
+ Log.DebugFormat("Reading back tmp file: {0}", tmpFileName);
+ }
+
+ byte[] processedImage = File.ReadAllBytes(tmpFileName);
+ targetStream.Write(processedImage, 0, processedImage.Length);
+ return true;
+ }
+
+ Log.ErrorFormat("Error while processing PNG image: {0}", process.ExitCode);
+ Log.ErrorFormat("Output: {0}", process.StandardOutput.ReadToEnd());
+ Log.ErrorFormat("Error: {0}", process.StandardError.ReadToEnd());
+ }
+ }
+ catch (Exception e)
+ {
+ Log.Error("Error while processing PNG image: ", e);
+ }
+ finally
+ {
+ if (File.Exists(tmpFileName))
+ {
+ Log.DebugFormat("Cleaning up tmp file: {0}", tmpFileName);
+ File.Delete(tmpFileName);
+ }
+ }
+
+ return false;
+ }
+
+ ///
+ /// Create an image from a surface with the settings from the output settings applied
+ ///
+ ///
+ ///
+ ///
+ /// true if the image must be disposed
+ public static bool CreateImageFromSurface(ISurface surface, SurfaceOutputSettings outputSettings, out Image imageToSave)
+ {
+ bool disposeImage = false;
+
+ if (outputSettings.Format == OutputFormat.greenshot || outputSettings.SaveBackgroundOnly)
+ {
+ // We save the image of the surface, this should not be disposed
+ imageToSave = surface.Image;
+ }
+ else
+ {
+ // We create the export image of the surface to save
+ imageToSave = surface.GetImageForExport();
+ disposeImage = true;
+ }
+
+ // The following block of modifications should be skipped when saving the greenshot format, no effects or otherwise!
+ if (outputSettings.Format == OutputFormat.greenshot)
+ {
+ return disposeImage;
+ }
+
+ Image tmpImage;
+ if (outputSettings.Effects != null && outputSettings.Effects.Count > 0)
+ {
+ // apply effects, if there are any
+ using (Matrix matrix = new Matrix())
+ {
+ tmpImage = ImageHelper.ApplyEffects(imageToSave, outputSettings.Effects, matrix);
+ }
+
+ if (tmpImage != null)
+ {
+ if (disposeImage)
+ {
+ imageToSave.Dispose();
+ }
+
+ imageToSave = tmpImage;
+ disposeImage = true;
+ }
+ }
+
+ // check for color reduction, forced or automatically, only when the DisableReduceColors is false
+ if (outputSettings.DisableReduceColors || (!CoreConfig.OutputFileAutoReduceColors && !outputSettings.ReduceColors))
+ {
+ return disposeImage;
+ }
+
+ bool isAlpha = Image.IsAlphaPixelFormat(imageToSave.PixelFormat);
+ if (outputSettings.ReduceColors || (!isAlpha && CoreConfig.OutputFileAutoReduceColors))
+ {
+ using var quantizer = new WuQuantizer((Bitmap) imageToSave);
+ int colorCount = quantizer.GetColorCount();
+ Log.InfoFormat("Image with format {0} has {1} colors", imageToSave.PixelFormat, colorCount);
+ if (!outputSettings.ReduceColors && colorCount >= 256)
+ {
+ return disposeImage;
+ }
+
+ try
+ {
+ Log.Info("Reducing colors on bitmap to 256.");
+ tmpImage = quantizer.GetQuantizedImage(CoreConfig.OutputFileReduceColorsTo);
+ if (disposeImage)
+ {
+ imageToSave.Dispose();
+ }
+
+ imageToSave = tmpImage;
+ // Make sure the "new" image is disposed
+ disposeImage = true;
+ }
+ catch (Exception e)
+ {
+ Log.Warn("Error occurred while Quantizing the image, ignoring and using original. Error: ", e);
+ }
+ }
+ else if (isAlpha && !outputSettings.ReduceColors)
+ {
+ Log.Info("Skipping 'optional' color reduction as the image has alpha");
+ }
+
+ return disposeImage;
+ }
+
+ ///
+ /// Add the greenshot property!
+ ///
+ ///
+ private static void AddTag(Image imageToSave)
+ {
+ // Create meta-data
+ PropertyItem softwareUsedPropertyItem = CreatePropertyItem(PROPERTY_TAG_SOFTWARE_USED, "Greenshot");
+ if (softwareUsedPropertyItem != null)
+ {
+ try
+ {
+ imageToSave.SetPropertyItem(softwareUsedPropertyItem);
+ }
+ catch (Exception)
+ {
+ Log.WarnFormat("Couldn't set property {0}", softwareUsedPropertyItem.Id);
+ }
+ }
+ }
+
+ ///
+ /// Load a Greenshot surface
+ ///
+ ///
+ ///
+ ///
+ public static ISurface LoadGreenshotSurface(string fullPath, ISurface returnSurface)
+ {
+ if (string.IsNullOrEmpty(fullPath))
+ {
+ return null;
+ }
+
+ Log.InfoFormat("Loading image from file {0}", fullPath);
+ // Fixed lock problem Bug #3431881
+ using (Stream surfaceFileStream = File.OpenRead(fullPath))
+ {
+ returnSurface = ImageHelper.LoadGreenshotSurface(surfaceFileStream, returnSurface);
+ }
+
+ if (returnSurface != null)
+ {
+ Log.InfoFormat("Information about file {0}: {1}x{2}-{3} Resolution {4}x{5}", fullPath, returnSurface.Image.Width, returnSurface.Image.Height,
+ returnSurface.Image.PixelFormat, returnSurface.Image.HorizontalResolution, returnSurface.Image.VerticalResolution);
+ }
+
+ return returnSurface;
+ }
+
+ ///
+ /// Saves image to specific path with specified quality
+ ///
+ public static void Save(ISurface surface, string fullPath, bool allowOverwrite, SurfaceOutputSettings outputSettings, bool copyPathToClipboard)
+ {
+ fullPath = FilenameHelper.MakeFqFilenameSafe(fullPath);
+ string path = Path.GetDirectoryName(fullPath);
+
+ // check whether path exists - if not create it
+ if (path != null)
+ {
+ DirectoryInfo di = new DirectoryInfo(path);
+ if (!di.Exists)
+ {
+ Directory.CreateDirectory(di.FullName);
+ }
+ }
+
+ if (!allowOverwrite && File.Exists(fullPath))
+ {
+ ArgumentException throwingException = new ArgumentException("File '" + fullPath + "' already exists.");
+ throwingException.Data.Add("fullPath", fullPath);
+ throw throwingException;
+ }
+
+ Log.DebugFormat("Saving surface to {0}", fullPath);
+ // Create the stream and call SaveToStream
+ using (FileStream stream = new FileStream(fullPath, FileMode.Create, FileAccess.Write))
+ {
+ SaveToStream(surface, stream, outputSettings);
+ }
+
+ if (copyPathToClipboard)
+ {
+ ClipboardHelper.SetClipboardData(fullPath);
+ }
+ }
+
+ ///
+ /// Get the OutputFormat for a filename
+ ///
+ /// filename (can be a complete path)
+ /// OutputFormat
+ public static OutputFormat FormatForFilename(string fullPath)
+ {
+ // Fix for bug 2912959
+ string extension = fullPath.Substring(fullPath.LastIndexOf(".", StringComparison.Ordinal) + 1);
+ OutputFormat format = OutputFormat.png;
+ try
+ {
+ format = (OutputFormat) Enum.Parse(typeof(OutputFormat), extension.ToLower());
+ }
+ catch (ArgumentException ae)
+ {
+ Log.Warn("Couldn't parse extension: " + extension, ae);
+ }
+
+ return format;
+ }
+
+ ///
+ /// Save with showing a dialog
+ ///
+ ///
+ ///
+ /// Path to filename
+ public static string SaveWithDialog(ISurface surface, ICaptureDetails captureDetails)
+ {
+ string returnValue = null;
+ using (SaveImageFileDialog saveImageFileDialog = new SaveImageFileDialog(captureDetails))
+ {
+ DialogResult dialogResult = saveImageFileDialog.ShowDialog();
+ if (dialogResult.Equals(DialogResult.OK))
+ {
+ try
+ {
+ string fileNameWithExtension = saveImageFileDialog.FileNameWithExtension;
+ SurfaceOutputSettings outputSettings = new SurfaceOutputSettings(FormatForFilename(fileNameWithExtension));
+ if (CoreConfig.OutputFilePromptQuality)
+ {
+ QualityDialog qualityDialog = new QualityDialog(outputSettings);
+ qualityDialog.ShowDialog();
+ }
+
+ // TODO: For now we always overwrite, should be changed
+ Save(surface, fileNameWithExtension, true, outputSettings, CoreConfig.OutputFileCopyPathToClipboard);
+ returnValue = fileNameWithExtension;
+ IniConfig.Save();
+ }
+ catch (ExternalException)
+ {
+ MessageBox.Show(Language.GetFormattedString("error_nowriteaccess", saveImageFileDialog.FileName).Replace(@"\\", @"\"), Language.GetString("error"));
+ }
+ }
+ }
+
+ return returnValue;
+ }
+
+ ///
+ /// Create a tmpfile which has the name like in the configured pattern.
+ /// Used e.g. by the email export
+ ///
+ ///
+ ///
+ ///
+ /// Path to image file
+ public static string SaveNamedTmpFile(ISurface surface, ICaptureDetails captureDetails, SurfaceOutputSettings outputSettings)
+ {
+ string pattern = CoreConfig.OutputFileFilenamePattern;
+ if (string.IsNullOrEmpty(pattern?.Trim()))
+ {
+ pattern = "greenshot ${capturetime}";
+ }
+
+ string filename = FilenameHelper.GetFilenameFromPattern(pattern, outputSettings.Format, captureDetails);
+ // Prevent problems with "other characters", which causes a problem in e.g. Outlook 2007 or break our HTML
+ filename = Regex.Replace(filename, @"[^\d\w\.]", "_");
+ // Remove multiple "_"
+ filename = Regex.Replace(filename, @"_+", "_");
+ string tmpFile = Path.Combine(Path.GetTempPath(), filename);
+
+ Log.Debug("Creating TMP File: " + tmpFile);
+
+ // 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
+ try
+ {
+ Save(surface, tmpFile, true, outputSettings, false);
+ TmpFileCache.Add(tmpFile, tmpFile);
+ }
+ catch (Exception e)
+ {
+ // Show the problem
+ MessageBox.Show(e.Message, "Error");
+ // when save failed we present a SaveWithDialog
+ tmpFile = SaveWithDialog(surface, captureDetails);
+ }
+
+ return tmpFile;
+ }
+
+ ///
+ /// Remove a tmpfile which was created by SaveNamedTmpFile
+ /// Used e.g. by the email export
+ ///
+ ///
+ /// true if it worked
+ public static bool DeleteNamedTmpFile(string tmpfile)
+ {
+ Log.Debug("Deleting TMP File: " + tmpfile);
+ try
+ {
+ if (File.Exists(tmpfile))
+ {
+ File.Delete(tmpfile);
+ TmpFileCache.Remove(tmpfile);
+ }
+
+ return true;
+ }
+ catch (Exception ex)
+ {
+ Log.Warn("Error deleting tmp file: ", ex);
+ }
+
+ return false;
+ }
+
+ ///
+ /// Helper method to create a temp image file
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static string SaveToTmpFile(ISurface surface, SurfaceOutputSettings outputSettings, string destinationPath)
+ {
+ string tmpFile = Path.GetRandomFileName() + "." + outputSettings.Format;
+ // Prevent problems with "other characters", which could cause problems
+ tmpFile = Regex.Replace(tmpFile, @"[^\d\w\.]", string.Empty);
+ if (destinationPath == null)
+ {
+ destinationPath = Path.GetTempPath();
+ }
+
+ string tmpPath = Path.Combine(destinationPath, tmpFile);
+ Log.Debug("Creating TMP File : " + tmpPath);
+
+ try
+ {
+ Save(surface, tmpPath, true, outputSettings, false);
+ TmpFileCache.Add(tmpPath, tmpPath);
+ }
+ catch (Exception)
+ {
+ return null;
+ }
+
+ return tmpPath;
+ }
+
+ ///
+ /// Cleanup all created tmpfiles
+ ///
+ public static void RemoveTmpFiles()
+ {
+ foreach (string tmpFile in TmpFileCache.Elements)
+ {
+ if (File.Exists(tmpFile))
+ {
+ Log.DebugFormat("Removing old temp file {0}", tmpFile);
+ File.Delete(tmpFile);
+ }
+
+ TmpFileCache.Remove(tmpFile);
+ }
+ }
+
+ ///
+ /// Cleanup handler for expired tempfiles
+ ///
+ ///
+ ///
+ private static void RemoveExpiredTmpFile(string filekey, object filename)
+ {
+ if (filename is string path && File.Exists(path))
+ {
+ Log.DebugFormat("Removing expired file {0}", path);
+ File.Delete(path);
+ }
+ }
+
+ ///
+ /// Write the images to the stream as icon
+ /// Every image is resized to 256x256 (but the content maintains the aspect ratio)
+ ///
+ /// Stream to write to
+ /// List of images
+ public static void WriteIcon(Stream stream, IList images)
+ {
+ var binaryWriter = new BinaryWriter(stream);
+ //
+ // ICONDIR structure
+ //
+ binaryWriter.Write((short) 0); // reserved
+ binaryWriter.Write((short) 1); // image type (icon)
+ binaryWriter.Write((short) images.Count); // number of images
+
+ IList imageSizes = new List();
+ IList encodedImages = new List();
+ foreach (var image in images)
+ {
+ // Pick the best fit
+ var sizes = new[]
+ {
+ 16, 32, 48
+ };
+ int size = 256;
+ foreach (var possibleSize in sizes)
+ {
+ if (image.Width <= possibleSize && image.Height <= possibleSize)
+ {
+ size = possibleSize;
+ break;
+ }
+ }
+
+ var imageStream = new MemoryStream();
+ if (image.Width == size && image.Height == size)
+ {
+ using var clonedImage = ImageHelper.Clone(image, PixelFormat.Format32bppArgb);
+ clonedImage.Save(imageStream, ImageFormat.Png);
+ imageSizes.Add(new Size(size, size));
+ }
+ else
+ {
+ // Resize to the specified size, first make sure the image is 32bpp
+ using var clonedImage = ImageHelper.Clone(image, PixelFormat.Format32bppArgb);
+ using var resizedImage = ImageHelper.ResizeImage(clonedImage, true, true, Color.Empty, size, size, null);
+ resizedImage.Save(imageStream, ImageFormat.Png);
+ imageSizes.Add(resizedImage.Size);
+ }
+
+ imageStream.Seek(0, SeekOrigin.Begin);
+ encodedImages.Add(imageStream);
+ }
+
+ //
+ // ICONDIRENTRY structure
+ //
+ const int iconDirSize = 6;
+ const int iconDirEntrySize = 16;
+
+ var offset = iconDirSize + (images.Count * iconDirEntrySize);
+ for (int i = 0; i < images.Count; i++)
+ {
+ var imageSize = imageSizes[i];
+ // Write the width / height, 0 means 256
+ binaryWriter.Write(imageSize.Width == 256 ? (byte) 0 : (byte) imageSize.Width);
+ binaryWriter.Write(imageSize.Height == 256 ? (byte) 0 : (byte) imageSize.Height);
+ binaryWriter.Write((byte) 0); // no pallete
+ binaryWriter.Write((byte) 0); // reserved
+ binaryWriter.Write((short) 0); // no color planes
+ binaryWriter.Write((short) 32); // 32 bpp
+ binaryWriter.Write((int) encodedImages[i].Length); // image data length
+ binaryWriter.Write(offset);
+ offset += (int) encodedImages[i].Length;
+ }
+
+ binaryWriter.Flush();
+ //
+ // Write image data
+ //
+ foreach (var encodedImage in encodedImages)
+ {
+ encodedImage.WriteTo(stream);
+ encodedImage.Dispose();
+ }
+ }
+ }
}
\ No newline at end of file
diff --git a/src/GreenshotPlugin/Core/ImageWrapper.cs b/src/Greenshot.Base/Core/ImageWrapper.cs
similarity index 95%
rename from src/GreenshotPlugin/Core/ImageWrapper.cs
rename to src/Greenshot.Base/Core/ImageWrapper.cs
index 720d2ce50..517249534 100644
--- a/src/GreenshotPlugin/Core/ImageWrapper.cs
+++ b/src/Greenshot.Base/Core/ImageWrapper.cs
@@ -1,77 +1,77 @@
-using System.Drawing;
-using System.Drawing.Imaging;
-
-namespace GreenshotPlugin.Core
-{
- ///
- /// Wrap an image, make it resizeable
- ///
- public class ImageWrapper : IImage
- {
- // Underlying image, is used to generate a resized version of it when needed
- private readonly Image _image;
- private Image _imageClone;
-
- public ImageWrapper(Image image)
- {
- // Make sure the orientation is set correctly so Greenshot can process the image correctly
- ImageHelper.Orientate(image);
- _image = image;
- Width = _image.Width;
- Height = _image.Height;
- }
-
- public void Dispose()
- {
- _image.Dispose();
- _imageClone?.Dispose();
- }
-
- ///
- /// Height of the image, can be set to change
- ///
- public int Height { get; set; }
-
- ///
- /// Width of the image, can be set to change.
- ///
- public int Width { get; set; }
-
- ///
- /// Size of the image
- ///
- public Size Size => new Size(Width, Height);
-
- ///
- /// Pixelformat of the underlying image
- ///
- public PixelFormat PixelFormat => Image.PixelFormat;
-
- public float HorizontalResolution => Image.HorizontalResolution;
- public float VerticalResolution => Image.VerticalResolution;
-
- public Image Image
- {
- get
- {
- if (_imageClone == null)
- {
- if (_image.Height == Height && _image.Width == Width)
- {
- return _image;
- }
- }
-
- if (_imageClone?.Height == Height && _imageClone?.Width == Width)
- {
- return _imageClone;
- }
-
- // Calculate new image clone
- _imageClone?.Dispose();
- _imageClone = ImageHelper.ResizeImage(_image, false, Width, Height, null);
- return _imageClone;
- }
- }
- }
+using System.Drawing;
+using System.Drawing.Imaging;
+
+namespace Greenshot.Base.Core
+{
+ ///
+ /// Wrap an image, make it resizeable
+ ///
+ public class ImageWrapper : IImage
+ {
+ // Underlying image, is used to generate a resized version of it when needed
+ private readonly Image _image;
+ private Image _imageClone;
+
+ public ImageWrapper(Image image)
+ {
+ // Make sure the orientation is set correctly so Greenshot can process the image correctly
+ ImageHelper.Orientate(image);
+ _image = image;
+ Width = _image.Width;
+ Height = _image.Height;
+ }
+
+ public void Dispose()
+ {
+ _image.Dispose();
+ _imageClone?.Dispose();
+ }
+
+ ///
+ /// Height of the image, can be set to change
+ ///
+ public int Height { get; set; }
+
+ ///
+ /// Width of the image, can be set to change.
+ ///
+ public int Width { get; set; }
+
+ ///
+ /// Size of the image
+ ///
+ public Size Size => new Size(Width, Height);
+
+ ///
+ /// Pixelformat of the underlying image
+ ///
+ public PixelFormat PixelFormat => Image.PixelFormat;
+
+ public float HorizontalResolution => Image.HorizontalResolution;
+ public float VerticalResolution => Image.VerticalResolution;
+
+ public Image Image
+ {
+ get
+ {
+ if (_imageClone == null)
+ {
+ if (_image.Height == Height && _image.Width == Width)
+ {
+ return _image;
+ }
+ }
+
+ if (_imageClone?.Height == Height && _imageClone?.Width == Width)
+ {
+ return _imageClone;
+ }
+
+ // Calculate new image clone
+ _imageClone?.Dispose();
+ _imageClone = ImageHelper.ResizeImage(_image, false, Width, Height, null);
+ return _imageClone;
+ }
+ }
+ }
}
\ No newline at end of file
diff --git a/src/GreenshotPlugin/Core/InterfaceUtils.cs b/src/Greenshot.Base/Core/InterfaceUtils.cs
similarity index 96%
rename from src/GreenshotPlugin/Core/InterfaceUtils.cs
rename to src/Greenshot.Base/Core/InterfaceUtils.cs
index c71c7b6ad..c93f24b56 100644
--- a/src/GreenshotPlugin/Core/InterfaceUtils.cs
+++ b/src/Greenshot.Base/Core/InterfaceUtils.cs
@@ -1,73 +1,73 @@
-/*
- * Greenshot - a free and open source screenshot tool
- * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
- *
- * For more information see: http://getgreenshot.org/
- * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 1 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-using System;
-using System.Collections.Generic;
-using System.Threading;
-using log4net;
-
-namespace GreenshotPlugin.Core
-{
- ///
- /// Description of InterfaceUtils.
- ///
- public static class InterfaceUtils
- {
- private static readonly ILog LOG = LogManager.GetLogger(typeof(InterfaceUtils));
-
- public static List GetSubclassesOf(Type type, bool excludeSystemTypes)
- {
- var list = new List();
- foreach (var currentAssembly in Thread.GetDomain().GetAssemblies())
- {
- try
- {
- Type[] types = currentAssembly.GetTypes();
- if (excludeSystemTypes && (!excludeSystemTypes || currentAssembly.FullName.StartsWith("System.")))
- {
- continue;
- }
-
- foreach (var currentType in types)
- {
- if (type.IsInterface)
- {
- if (currentType.GetInterface(type.FullName) != null)
- {
- list.Add(currentType);
- }
- }
- else if (currentType.IsSubclassOf(type))
- {
- list.Add(currentType);
- }
- }
- }
- catch (Exception ex)
- {
- LOG.WarnFormat("Problem getting subclasses of type: {0}, message: {1}", type.FullName, ex.Message);
- }
- }
-
- return list;
- }
- }
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: http://getgreenshot.org/
+ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+using System;
+using System.Collections.Generic;
+using System.Threading;
+using log4net;
+
+namespace Greenshot.Base.Core
+{
+ ///
+ /// Description of InterfaceUtils.
+ ///
+ public static class InterfaceUtils
+ {
+ private static readonly ILog LOG = LogManager.GetLogger(typeof(InterfaceUtils));
+
+ public static List GetSubclassesOf(Type type, bool excludeSystemTypes)
+ {
+ var list = new List();
+ foreach (var currentAssembly in Thread.GetDomain().GetAssemblies())
+ {
+ try
+ {
+ Type[] types = currentAssembly.GetTypes();
+ if (excludeSystemTypes && (!excludeSystemTypes || currentAssembly.FullName.StartsWith("System.")))
+ {
+ continue;
+ }
+
+ foreach (var currentType in types)
+ {
+ if (type.IsInterface)
+ {
+ if (currentType.GetInterface(type.FullName) != null)
+ {
+ list.Add(currentType);
+ }
+ }
+ else if (currentType.IsSubclassOf(type))
+ {
+ list.Add(currentType);
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ LOG.WarnFormat("Problem getting subclasses of type: {0}, message: {1}", type.FullName, ex.Message);
+ }
+ }
+
+ return list;
+ }
+ }
}
\ No newline at end of file
diff --git a/src/GreenshotPlugin/Core/JSONHelper.cs b/src/Greenshot.Base/Core/JSONHelper.cs
similarity index 96%
rename from src/GreenshotPlugin/Core/JSONHelper.cs
rename to src/Greenshot.Base/Core/JSONHelper.cs
index 28a76a71d..37da4972b 100644
--- a/src/GreenshotPlugin/Core/JSONHelper.cs
+++ b/src/Greenshot.Base/Core/JSONHelper.cs
@@ -1,436 +1,436 @@
-/*
-Copyright (C) 2008 Patrick van Bergen
-
-Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- */
-
-using System.Collections.Generic;
-using System.Globalization;
-using System.Text;
-
-namespace GreenshotPlugin.Core
-{
- ///
- /// This parses a JSON response, a modified version of the code found at:
- /// See: http://techblog.procurios.nl/k/n618/news/view/14605/14863/How-do-I-write-my-own-parser-for-JSON.html
- ///
- /// This file is under the MIT License, which is GPL Compatible and according to: http://en.wikipedia.org/wiki/MIT_License
- /// can be used under the GPL "umbrella".
- ///
- /// TODO: code should be replaced when upgrading to .NET 3.5 or higher!!
- ///
- public class JSONHelper
- {
- public const int TOKEN_NONE = 0;
- public const int TOKEN_CURLY_OPEN = 1;
- public const int TOKEN_CURLY_CLOSE = 2;
- public const int TOKEN_SQUARED_OPEN = 3;
- public const int TOKEN_SQUARED_CLOSE = 4;
- public const int TOKEN_COLON = 5;
- public const int TOKEN_COMMA = 6;
- public const int TOKEN_STRING = 7;
- public const int TOKEN_NUMBER = 8;
- public const int TOKEN_TRUE = 9;
- public const int TOKEN_FALSE = 10;
- public const int TOKEN_NULL = 11;
-
- private const int BUILDER_CAPACITY = 2000;
-
- ///
- /// Parses the string json into a value
- ///
- /// A JSON string.
- /// An ArrayList, a Hashtable, a double, a string, null, true, or false
- public static IDictionary JsonDecode(string json)
- {
- bool success = true;
-
- return JsonDecode(json, ref success);
- }
-
- ///
- /// Parses the string json into a value; and fills 'success' with the successfullness of the parse.
- ///
- /// A JSON string.
- /// Successful parse?
- /// An ArrayList, a Hashtable, a double, a string, null, true, or false
- public static IDictionary JsonDecode(string json, ref bool success)
- {
- success = true;
- if (json != null)
- {
- char[] charArray = json.ToCharArray();
- int index = 0;
- IDictionary value = ParseValue(charArray, ref index, ref success) as IDictionary;
- return value;
- }
-
- return null;
- }
-
- protected static IDictionary ParseObject(char[] json, ref int index, ref bool success)
- {
- IDictionary table = new Dictionary();
- int token;
-
- // {
- NextToken(json, ref index);
-
- bool done = false;
- while (!done)
- {
- token = LookAhead(json, index);
- if (token == TOKEN_NONE)
- {
- success = false;
- return null;
- }
- else if (token == TOKEN_COMMA)
- {
- NextToken(json, ref index);
- }
- else if (token == TOKEN_CURLY_CLOSE)
- {
- NextToken(json, ref index);
- return table;
- }
- else
- {
- // name
- string name = ParseString(json, ref index, ref success);
- if (!success)
- {
- success = false;
- return null;
- }
-
- // :
- token = NextToken(json, ref index);
- if (token != TOKEN_COLON)
- {
- success = false;
- return null;
- }
-
- // value
- object value = ParseValue(json, ref index, ref success);
- if (!success)
- {
- success = false;
- return null;
- }
-
- table.Add(name, value);
- }
- }
-
- return table;
- }
-
- protected static IList