greenshot/Greenshot/Forms/ImageEditorForm.cs
Robin Krom 94c778d82c
Improve DPI support (#254)
* Improving the DPI handling for most forms, there are still issues with:
* the AboutForm.Designer.cs where the title with the version scales differently.
* the destination picker doesn't seem to scale the font correctly.
Some parts are not tested yet...

* Solved the issue with the destination picker font, and some other small issues.
There still is an issue when using Powertoys (the feature which is experimental), that the capture is somehow skipping.
2020-10-23 00:28:50 +02:00

1645 lines
59 KiB
C#

/*
* Greenshot - a free and open source screenshot tool
* Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom
*
* For more information see: http://getgreenshot.org/
* The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 1 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.IO;
using System.Threading;
using System.Windows.Forms;
using Greenshot.Configuration;
using Greenshot.Destinations;
using Greenshot.Drawing;
using Greenshot.Drawing.Fields;
using Greenshot.Drawing.Fields.Binding;
using Greenshot.Forms;
using Greenshot.Help;
using Greenshot.Helpers;
using GreenshotPlugin.Controls;
using GreenshotPlugin.Core;
using GreenshotPlugin.Effects;
using GreenshotPlugin.IniFile;
using GreenshotPlugin.Interfaces;
using GreenshotPlugin.Interfaces.Drawing;
using GreenshotPlugin.Interfaces.Forms;
using GreenshotPlugin.UnmanagedHelpers;
using GreenshotPlugin.UnmanagedHelpers.Structs;
using log4net;
namespace Greenshot {
/// <summary>
/// Description of ImageEditorForm.
/// </summary>
public partial class ImageEditorForm : BaseForm, IImageEditor {
private static readonly ILog Log = LogManager.GetLogger(typeof(ImageEditorForm));
private static readonly EditorConfiguration EditorConfiguration = IniConfig.GetIniSection<EditorConfiguration>();
private static readonly List<string> IgnoreDestinations = new List<string> { PickerDestination.DESIGNATION, EditorDestination.DESIGNATION };
private static readonly List<IImageEditor> EditorList = new List<IImageEditor>();
private Surface _surface;
private GreenshotToolStripButton[] _toolbarButtons;
private static readonly string[] SupportedClipboardFormats = {typeof(string).FullName, "Text", typeof(IDrawableContainerList).FullName};
private bool _originalBoldCheckState;
private bool _originalItalicCheckState;
// whether part of the editor controls are disabled depending on selected item(s)
private bool _controlsDisabledDueToConfirmable;
/// <summary>
/// All provided zoom values (in percents) in ascending order.
/// </summary>
private readonly Fraction[] ZOOM_VALUES = new Fraction[] { (1, 4), (1, 2), (2, 3), (3, 4), (1 ,1), (2, 1), (3, 1), (4, 1), (6, 1) };
/// <summary>
/// An Implementation for the IImageEditor, this way Plugins have access to the HWND handles wich can be used with Win32 API calls.
/// </summary>
public IWin32Window WindowHandle => this;
public static List<IImageEditor> Editors {
get {
try {
EditorList.Sort((e1, e2) => string.Compare(e1.Surface.CaptureDetails.Title, e2.Surface.CaptureDetails.Title, StringComparison.Ordinal));
} catch(Exception ex) {
Log.Warn("Sorting of editors failed.", ex);
}
return EditorList;
}
}
/// <summary>
/// Adjust the icons etc to the supplied DPI settings
/// </summary>
/// <param name="sender"></param>
/// <param name="dpiChangedEventArgs">DpiChangedEventArgs</param>
private void AdjustToDpi(object sender, DpiChangedEventArgs dpiChangedEventArgs)
{
var dpi = DpiHelper.GetDpi(Handle);
var newSize = DpiHelper.ScaleWithDpi(coreConfiguration.IconSize, dpi);
toolsToolStrip.ImageScalingSize = newSize;
menuStrip1.ImageScalingSize = newSize;
destinationsToolStrip.ImageScalingSize = newSize;
propertiesToolStrip.ImageScalingSize = newSize;
propertiesToolStrip.MinimumSize = new Size(150, newSize.Height + 10);
_surface?.AdjustToDpi(dpi);
UpdateUi();
}
public ImageEditorForm(ISurface iSurface, bool outputMade)
{
EditorList.Add(this);
//
// The InitializeComponent() call is required for Windows Forms designer support.
//
ManualLanguageApply = true;
InitializeComponent();
// Make sure we change the icon size depending on the scaling
DpiChanged += AdjustToDpi;
Load += delegate {
var thread = new Thread(AddDestinations)
{
Name = "add destinations"
};
thread.Start();
AdjustToDpi(null, null);
};
// Make sure the editor is placed on the same location as the last editor was on close
// But only if this still exists, else it will be reset (BUG-1812)
WindowPlacement editorWindowPlacement = EditorConfiguration.GetEditorPlacement();
Rectangle screenBounds = WindowCapture.GetScreenBounds();
if (!screenBounds.Contains(editorWindowPlacement.NormalPosition))
{
EditorConfiguration.ResetEditorPlacement();
}
// ReSharper disable once UnusedVariable
WindowDetails thisForm = new WindowDetails(Handle)
{
WindowPlacement = EditorConfiguration.GetEditorPlacement()
};
// init surface
Surface = iSurface;
// Initial "saved" flag for asking if the image needs to be save
_surface.Modified = !outputMade;
UpdateUi();
// Workaround: As the cursor is (mostly) selected on the surface a funny artifact is visible, this fixes it.
HideToolstripItems();
}
/// <summary>
/// Remove the current surface
/// </summary>
private void RemoveSurface() {
if (_surface == null)
{
return;
}
panel1.Controls.Remove(_surface);
_surface.Dispose();
_surface = null;
}
/// <summary>
/// Change the surface
/// </summary>
/// <param name="newSurface"></param>
private void SetSurface(ISurface newSurface) {
if (Surface != null && Surface.Modified) {
throw new ApplicationException("Surface modified");
}
RemoveSurface();
panel1.Height = 10;
panel1.Width = 10;
_surface = newSurface as Surface;
if (_surface != null)
{
panel1.Controls.Add(_surface);
}
Image backgroundForTransparency = GreenshotResources.GetImage("Checkerboard.Image");
if (_surface != null)
{
_surface.TransparencyBackgroundBrush = new TextureBrush(backgroundForTransparency, WrapMode.Tile);
_surface.MovingElementChanged += delegate {
RefreshEditorControls();
};
_surface.DrawingModeChanged += Surface_DrawingModeChanged;
_surface.SurfaceSizeChanged += SurfaceSizeChanged;
_surface.SurfaceMessage += SurfaceMessageReceived;
_surface.FieldAggregator.FieldChanged += FieldAggregatorFieldChanged;
SurfaceSizeChanged(Surface, null);
BindFieldControls();
RefreshEditorControls();
// Fix title
if (_surface?.CaptureDetails?.Title != null) {
Text = _surface.CaptureDetails.Title + " - " + Language.GetString(LangKey.editor_title);
}
}
Activate();
WindowDetails.ToForeground(Handle);
}
private void UpdateUi() {
// Disable access to the settings, for feature #3521446
preferencesToolStripMenuItem.Visible = !coreConfiguration.DisableSettings;
toolStripSeparator12.Visible = !coreConfiguration.DisableSettings;
toolStripSeparator11.Visible = !coreConfiguration.DisableSettings;
btnSettings.Visible = !coreConfiguration.DisableSettings;
// Make sure Double-buffer is enabled
SetStyle(ControlStyles.DoubleBuffer | ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint, true);
// resizing the panel is futile, since it is docked. however, it seems
// to fix the bug (?) with the vscrollbar not being able to shrink to
// a smaller size than the initial panel size (as set by the forms designer)
panel1.Height = 10;
fontFamilyComboBox.PropertyChanged += FontPropertyChanged;
obfuscateModeButton.DropDownItemClicked += FilterPresetDropDownItemClicked;
highlightModeButton.DropDownItemClicked += FilterPresetDropDownItemClicked;
_toolbarButtons = new[] { btnCursor, btnRect, btnEllipse, btnText, btnLine, btnArrow, btnFreehand, btnHighlight, btnObfuscate, btnCrop, btnStepLabel, btnSpeechBubble };
//toolbarDropDownButtons = new ToolStripDropDownButton[]{btnBlur, btnPixeliate, btnTextHighlighter, btnAreaHighlighter, btnMagnifier};
pluginToolStripMenuItem.Visible = pluginToolStripMenuItem.DropDownItems.Count > 0;
// Workaround: for the MouseWheel event which doesn't get to the panel
MouseWheel += PanelMouseWheel;
// Make sure the value is set correctly when starting
if (Surface != null)
{
counterUpDown.Value = Surface.CounterStart;
}
ApplyLanguage();
}
/// <summary>
/// Workaround for having a border around the dropdown
/// See: http://stackoverflow.com/questions/9560812/change-border-of-toolstripcombobox-with-flat-style
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void PropertiesToolStrip_Paint(object sender, PaintEventArgs e)
{
using Pen cbBorderPen = new Pen(SystemColors.ActiveBorder);
// Loop over all items in the propertiesToolStrip
foreach (ToolStripItem item in propertiesToolStrip.Items) {
var cb = item as ToolStripComboBox;
// Only ToolStripComboBox that are visible
if (cb == null || !cb.Visible) {
continue;
}
if (cb.ComboBox == null) continue;
// Calculate the rectangle
Rectangle r = new Rectangle(cb.ComboBox.Location.X - 1, cb.ComboBox.Location.Y - 1, cb.ComboBox.Size.Width + 1, cb.ComboBox.Size.Height + 1);
// Draw the rectangle
e.Graphics.DrawRectangle(cbBorderPen, r);
}
}
/// <summary>
/// Get all the destinations and display them in the file menu and the buttons
/// </summary>
private void AddDestinations() {
Invoke((MethodInvoker)delegate {
// Create export buttons
foreach(IDestination destination in DestinationHelper.GetAllDestinations()) {
if (destination.Priority <= 2) {
continue;
}
if (!destination.IsActive) {
continue;
}
if (destination.DisplayIcon == null) {
continue;
}
try {
AddDestinationButton(destination);
} catch (Exception addingException) {
Log.WarnFormat("Problem adding destination {0}", destination.Designation);
Log.Warn("Exception: ", addingException);
}
}
});
}
private void AddDestinationButton(IDestination toolstripDestination) {
if (toolstripDestination.IsDynamic) {
ToolStripSplitButton destinationButton = new ToolStripSplitButton
{
DisplayStyle = ToolStripItemDisplayStyle.Image,
Size = new Size(23, 22),
Text = toolstripDestination.Description,
Image = toolstripDestination.DisplayIcon
};
//ToolStripDropDownButton destinationButton = new ToolStripDropDownButton();
ToolStripMenuItem defaultItem = new ToolStripMenuItem(toolstripDestination.Description)
{
Tag = toolstripDestination,
Image = toolstripDestination.DisplayIcon
};
defaultItem.Click += delegate {
toolstripDestination.ExportCapture(true, _surface, _surface.CaptureDetails);
};
// The ButtonClick, this is for the icon, gets the current default item
destinationButton.ButtonClick += delegate {
toolstripDestination.ExportCapture(true, _surface, _surface.CaptureDetails);
};
// Generate the entries for the drop down
destinationButton.DropDownOpening += delegate
{
ClearItems(destinationButton.DropDownItems);
destinationButton.DropDownItems.Add(defaultItem);
List<IDestination> subDestinations = new List<IDestination>();
subDestinations.AddRange(toolstripDestination.DynamicDestinations());
if (subDestinations.Count > 0) {
subDestinations.Sort();
foreach(IDestination subDestination in subDestinations) {
IDestination closureFixedDestination = subDestination;
ToolStripMenuItem destinationMenuItem = new ToolStripMenuItem(closureFixedDestination.Description)
{
Tag = closureFixedDestination,
Image = closureFixedDestination.DisplayIcon
};
destinationMenuItem.Click += delegate {
closureFixedDestination.ExportCapture(true, _surface, _surface.CaptureDetails);
};
destinationButton.DropDownItems.Add(destinationMenuItem);
}
}
};
destinationsToolStrip.Items.Insert(destinationsToolStrip.Items.IndexOf(toolStripSeparator16), destinationButton);
} else {
ToolStripButton destinationButton = new ToolStripButton();
destinationsToolStrip.Items.Insert(destinationsToolStrip.Items.IndexOf(toolStripSeparator16), destinationButton);
destinationButton.DisplayStyle = ToolStripItemDisplayStyle.Image;
destinationButton.Size = new Size(23, 22);
destinationButton.Text = toolstripDestination.Description;
destinationButton.Image = toolstripDestination.DisplayIcon;
destinationButton.Click += delegate {
toolstripDestination.ExportCapture(true, _surface, _surface.CaptureDetails);
};
}
}
/// <summary>
/// According to some information I found, the clear doesn't work correctly when the shortcutkeys are set?
/// This helper method takes care of this.
/// </summary>
/// <param name="items"></param>
private void ClearItems(ToolStripItemCollection items) {
foreach(var item in items) {
if (item is ToolStripMenuItem menuItem && menuItem.ShortcutKeys != Keys.None) {
menuItem.ShortcutKeys = Keys.None;
}
}
items.Clear();
}
private void FileMenuDropDownOpening(object sender, EventArgs eventArgs) {
ClearItems(fileStripMenuItem.DropDownItems);
// Add the destinations
foreach(IDestination destination in DestinationHelper.GetAllDestinations()) {
if (IgnoreDestinations.Contains(destination.Designation)) {
continue;
}
if (!destination.IsActive) {
continue;
}
ToolStripMenuItem item = destination.GetMenuItem(true, null, DestinationToolStripMenuItemClick);
if (item != null) {
item.ShortcutKeys = destination.EditorShortcutKeys;
fileStripMenuItem.DropDownItems.Add(item);
}
}
// add the elements after the destinations
fileStripMenuItem.DropDownItems.Add(toolStripSeparator9);
fileStripMenuItem.DropDownItems.Add(closeToolStripMenuItem);
}
private delegate void SurfaceMessageReceivedThreadSafeDelegate(object sender, SurfaceMessageEventArgs eventArgs);
/// <summary>
/// This is the SufraceMessageEvent receiver which display a message in the status bar if the
/// surface is exported. It also updates the title to represent the filename, if there is one.
/// </summary>
/// <param name="sender"></param>
/// <param name="eventArgs"></param>
private void SurfaceMessageReceived(object sender, SurfaceMessageEventArgs eventArgs) {
if (InvokeRequired) {
Invoke(new SurfaceMessageReceivedThreadSafeDelegate(SurfaceMessageReceived), sender, eventArgs);
} else {
string dateTime = DateTime.Now.ToLongTimeString();
// TODO: Fix that we only open files, like in the tooltip
switch (eventArgs.MessageType) {
case SurfaceMessageTyp.FileSaved:
// Put the event message on the status label and attach the context menu
UpdateStatusLabel(dateTime + " - " + eventArgs.Message, fileSavedStatusContextMenu);
// Change title
Text = eventArgs.Surface.LastSaveFullPath + " - " + Language.GetString(LangKey.editor_title);
break;
default:
// Put the event message on the status label
UpdateStatusLabel(dateTime + " - " + eventArgs.Message);
break;
}
}
}
/// <summary>
/// This is called when the size of the surface chances, used for resizing and displaying the size information
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void SurfaceSizeChanged(object sender, EventArgs e)
{
if (EditorConfiguration.MatchSizeToCapture)
{
Size = GetOptimalWindowSize();
}
dimensionsLabel.Text = Surface.Image.Width + "x" + Surface.Image.Height;
AlignCanvasPositionAfterResize();
}
public ISurface Surface {
get {
return _surface;
}
set {
SetSurface(value);
}
}
public void SetImagePath(string fullpath) {
// Check if the editor supports the format
if (fullpath != null && (fullpath.EndsWith(".ico") || fullpath.EndsWith(".wmf"))) {
fullpath = null;
}
_surface.LastSaveFullPath = fullpath;
if (fullpath == null) {
return;
}
UpdateStatusLabel(Language.GetFormattedString(LangKey.editor_imagesaved, fullpath), fileSavedStatusContextMenu);
Text = Path.GetFileName(fullpath) + " - " + Language.GetString(LangKey.editor_title);
}
private void Surface_DrawingModeChanged(object source, SurfaceDrawingModeEventArgs eventArgs) {
switch (eventArgs.DrawingMode) {
case DrawingModes.None:
SetButtonChecked(btnCursor);
break;
case DrawingModes.Ellipse:
SetButtonChecked(btnEllipse);
break;
case DrawingModes.Rect:
SetButtonChecked(btnRect);
break;
case DrawingModes.Text:
SetButtonChecked(btnText);
break;
case DrawingModes.SpeechBubble:
SetButtonChecked(btnSpeechBubble);
break;
case DrawingModes.StepLabel:
SetButtonChecked(btnStepLabel);
break;
case DrawingModes.Line:
SetButtonChecked(btnLine);
break;
case DrawingModes.Arrow:
SetButtonChecked(btnArrow);
break;
case DrawingModes.Crop:
SetButtonChecked(btnCrop);
break;
case DrawingModes.Highlight:
SetButtonChecked(btnHighlight);
break;
case DrawingModes.Obfuscate:
SetButtonChecked(btnObfuscate);
break;
case DrawingModes.Path:
SetButtonChecked(btnFreehand);
break;
}
}
/**
* Interfaces for plugins, see GreenshotInterface for more details!
*/
public Image GetImageForExport() {
return _surface.GetImageForExport();
}
public ICaptureDetails CaptureDetails => _surface.CaptureDetails;
public ToolStripMenuItem GetPluginMenuItem() {
return pluginToolStripMenuItem;
}
public ToolStripMenuItem GetFileMenuItem() {
return fileStripMenuItem;
}
private void BtnSaveClick(object sender, EventArgs e) {
string destinationDesignation = FileDestination.DESIGNATION;
if (_surface.LastSaveFullPath == null) {
destinationDesignation = FileWithDialogDestination.DESIGNATION;
}
DestinationHelper.ExportCapture(true, destinationDesignation, _surface, _surface.CaptureDetails);
}
private void BtnClipboardClick(object sender, EventArgs e) {
DestinationHelper.ExportCapture(true, ClipboardDestination.DESIGNATION, _surface, _surface.CaptureDetails);
}
private void BtnPrintClick(object sender, EventArgs e) {
// The BeginInvoke is a solution for the printdialog not having focus
BeginInvoke((MethodInvoker) delegate {
DestinationHelper.ExportCapture(true, PrinterDestination.DESIGNATION, _surface, _surface.CaptureDetails);
});
}
private void CloseToolStripMenuItemClick(object sender, EventArgs e) {
Close();
}
private void BtnEllipseClick(object sender, EventArgs e) {
_surface.DrawingMode = DrawingModes.Ellipse;
RefreshFieldControls();
}
private void BtnCursorClick(object sender, EventArgs e) {
_surface.DrawingMode = DrawingModes.None;
RefreshFieldControls();
}
private void BtnRectClick(object sender, EventArgs e) {
_surface.DrawingMode = DrawingModes.Rect;
RefreshFieldControls();
}
private void BtnTextClick(object sender, EventArgs e) {
_surface.DrawingMode = DrawingModes.Text;
RefreshFieldControls();
}
private void BtnSpeechBubbleClick(object sender, EventArgs e) {
_surface.DrawingMode = DrawingModes.SpeechBubble;
RefreshFieldControls();
}
private void BtnStepLabelClick(object sender, EventArgs e) {
_surface.DrawingMode = DrawingModes.StepLabel;
RefreshFieldControls();
}
private void BtnLineClick(object sender, EventArgs e) {
_surface.DrawingMode = DrawingModes.Line;
RefreshFieldControls();
}
private void BtnArrowClick(object sender, EventArgs e) {
_surface.DrawingMode = DrawingModes.Arrow;
RefreshFieldControls();
}
private void BtnCropClick(object sender, EventArgs e) {
_surface.DrawingMode = DrawingModes.Crop;
RefreshFieldControls();
}
private void BtnHighlightClick(object sender, EventArgs e) {
_surface.DrawingMode = DrawingModes.Highlight;
RefreshFieldControls();
}
private void BtnObfuscateClick(object sender, EventArgs e) {
_surface.DrawingMode = DrawingModes.Obfuscate;
RefreshFieldControls();
}
private void BtnFreehandClick(object sender, EventArgs e) {
_surface.DrawingMode = DrawingModes.Path;
RefreshFieldControls();
}
private void SetButtonChecked(ToolStripButton btn) {
UncheckAllToolButtons();
btn.Checked = true;
}
private void UncheckAllToolButtons() {
if (_toolbarButtons != null) {
foreach (GreenshotToolStripButton butt in _toolbarButtons) {
butt.Checked = false;
}
}
}
private void AddRectangleToolStripMenuItemClick(object sender, EventArgs e) {
BtnRectClick(sender, e);
}
private void DrawFreehandToolStripMenuItemClick(object sender, EventArgs e) {
BtnFreehandClick(sender, e);
}
private void AddEllipseToolStripMenuItemClick(object sender, EventArgs e) {
BtnEllipseClick(sender, e);
}
private void AddTextBoxToolStripMenuItemClick(object sender, EventArgs e) {
BtnTextClick(sender, e);
}
private void AddSpeechBubbleToolStripMenuItemClick(object sender, EventArgs e) {
BtnSpeechBubbleClick(sender, e);
}
private void AddCounterToolStripMenuItemClick(object sender, EventArgs e) {
BtnStepLabelClick(sender, e);
}
private void DrawLineToolStripMenuItemClick(object sender, EventArgs e) {
BtnLineClick(sender, e);
}
private void DrawArrowToolStripMenuItemClick(object sender, EventArgs e) {
BtnArrowClick(sender, e);
}
private void RemoveObjectToolStripMenuItemClick(object sender, EventArgs e) {
_surface.RemoveSelectedElements();
}
private void BtnDeleteClick(object sender, EventArgs e) {
RemoveObjectToolStripMenuItemClick(sender, e);
}
private void CutToolStripMenuItemClick(object sender, EventArgs e) {
_surface.CutSelectedElements();
UpdateClipboardSurfaceDependencies();
}
private void BtnCutClick(object sender, EventArgs e) {
CutToolStripMenuItemClick(sender, e);
}
private void CopyToolStripMenuItemClick(object sender, EventArgs e) {
_surface.CopySelectedElements();
UpdateClipboardSurfaceDependencies();
}
private void BtnCopyClick(object sender, EventArgs e) {
CopyToolStripMenuItemClick(sender, e);
}
private void PasteToolStripMenuItemClick(object sender, EventArgs e) {
_surface.PasteElementFromClipboard();
UpdateClipboardSurfaceDependencies();
}
private void BtnPasteClick(object sender, EventArgs e) {
PasteToolStripMenuItemClick(sender, e);
}
private void UndoToolStripMenuItemClick(object sender, EventArgs e) {
_surface.Undo();
UpdateUndoRedoSurfaceDependencies();
}
private void BtnUndoClick(object sender, EventArgs e) {
UndoToolStripMenuItemClick(sender, e);
}
private void RedoToolStripMenuItemClick(object sender, EventArgs e) {
_surface.Redo();
UpdateUndoRedoSurfaceDependencies();
}
private void BtnRedoClick(object sender, EventArgs e) {
RedoToolStripMenuItemClick(sender, e);
}
private void DuplicateToolStripMenuItemClick(object sender, EventArgs e) {
_surface.DuplicateSelectedElements();
UpdateClipboardSurfaceDependencies();
}
private void UpOneLevelToolStripMenuItemClick(object sender, EventArgs e) {
_surface.PullElementsUp();
}
private void DownOneLevelToolStripMenuItemClick(object sender, EventArgs e) {
_surface.PushElementsDown();
}
private void UpToTopToolStripMenuItemClick(object sender, EventArgs e) {
_surface.PullElementsToTop();
}
private void DownToBottomToolStripMenuItemClick(object sender, EventArgs e) {
_surface.PushElementsToBottom();
}
private void HelpToolStripMenuItem1Click(object sender, EventArgs e) {
HelpFileLoader.LoadHelp();
}
private void AboutToolStripMenuItemClick(object sender, EventArgs e)
{
var mainForm = SimpleServiceProvider.Current.GetInstance<MainForm>();
mainForm.ShowAbout();
}
private void PreferencesToolStripMenuItemClick(object sender, EventArgs e) {
var mainForm = SimpleServiceProvider.Current.GetInstance<MainForm>();
mainForm.ShowSetting();
}
private void BtnSettingsClick(object sender, EventArgs e) {
PreferencesToolStripMenuItemClick(sender, e);
}
private void BtnHelpClick(object sender, EventArgs e) {
HelpToolStripMenuItem1Click(sender, e);
}
private void ImageEditorFormActivated(object sender, EventArgs e) {
UpdateClipboardSurfaceDependencies();
UpdateUndoRedoSurfaceDependencies();
}
private void ImageEditorFormFormClosing(object sender, FormClosingEventArgs e) {
if (_surface.Modified && !EditorConfiguration.SuppressSaveDialogAtClose) {
// Make sure the editor is visible
WindowDetails.ToForeground(Handle);
MessageBoxButtons buttons = MessageBoxButtons.YesNoCancel;
// Dissallow "CANCEL" if the application needs to shutdown
if (e.CloseReason == CloseReason.ApplicationExitCall || e.CloseReason == CloseReason.WindowsShutDown || e.CloseReason == CloseReason.TaskManagerClosing) {
buttons = MessageBoxButtons.YesNo;
}
DialogResult result = MessageBox.Show(Language.GetString(LangKey.editor_close_on_save), Language.GetString(LangKey.editor_close_on_save_title), buttons, MessageBoxIcon.Question);
if (result.Equals(DialogResult.Cancel)) {
e.Cancel = true;
return;
}
if (result.Equals(DialogResult.Yes)) {
BtnSaveClick(sender,e);
// Check if the save was made, if not it was cancelled so we cancel the closing
if (_surface.Modified) {
e.Cancel = true;
return;
}
}
}
// persist our geometry string.
EditorConfiguration.SetEditorPlacement(new WindowDetails(Handle).WindowPlacement);
IniConfig.Save();
// remove from the editor list
EditorList.Remove(this);
_surface.Dispose();
GC.Collect();
if (coreConfiguration.MinimizeWorkingSetSize) {
PsAPI.EmptyWorkingSet();
}
}
private void ImageEditorFormKeyDown(object sender, KeyEventArgs e) {
// LOG.Debug("Got key event "+e.KeyCode + ", " + e.Modifiers);
// avoid conflict with other shortcuts and
// make sure there's no selected element claiming input focus
if(e.Modifiers.Equals(Keys.None) && !_surface.KeysLocked) {
switch(e.KeyCode) {
case Keys.Escape:
BtnCursorClick(sender, e);
break;
case Keys.R:
BtnRectClick(sender, e);
break;
case Keys.E:
BtnEllipseClick(sender, e);
break;
case Keys.L:
BtnLineClick(sender, e);
break;
case Keys.F:
BtnFreehandClick(sender, e);
break;
case Keys.A:
BtnArrowClick(sender, e);
break;
case Keys.T:
BtnTextClick(sender, e);
break;
case Keys.S:
BtnSpeechBubbleClick(sender, e);
break;
case Keys.I:
BtnStepLabelClick(sender, e);
break;
case Keys.H:
BtnHighlightClick(sender, e);
break;
case Keys.O:
BtnObfuscateClick(sender, e);
break;
case Keys.C:
BtnCropClick(sender, e);
break;
}
} else if (e.Modifiers.Equals(Keys.Control)) {
switch (e.KeyCode) {
case Keys.Z:
UndoToolStripMenuItemClick(sender, e);
break;
case Keys.Y:
RedoToolStripMenuItemClick(sender, e);
break;
case Keys.Q: // Dropshadow Ctrl + Q
AddDropshadowToolStripMenuItemMouseUp(sender, new MouseEventArgs(MouseButtons.Left, 0, 0, 0, 0));
break;
case Keys.B: // Border Ctrl + B
AddBorderToolStripMenuItemClick(sender, e);
break;
case Keys.T: // Torn edge Ctrl + T
TornEdgesToolStripMenuItemMouseUp(sender, new MouseEventArgs(MouseButtons.Left, 0, 0, 0, 0));
break;
case Keys.I: // Invert Ctrl + I
InvertToolStripMenuItemClick(sender, e);
break;
case Keys.G: // Grayscale Ctrl + G
GrayscaleToolStripMenuItemClick(sender, e);
break;
case Keys.Delete: // Grayscale Ctrl + Delete
ClearToolStripMenuItemClick(sender, e);
break;
case Keys.Oemcomma: // Rotate CCW Ctrl + ,
RotateCcwToolstripButtonClick(sender, e);
break;
case Keys.OemPeriod: // Rotate CW Ctrl + .
RotateCwToolstripButtonClick(sender, e);
break;
case Keys.Add: // Ctrl + Num+
case Keys.Oemplus: // Ctrl + +
ZoomInMenuItemClick(sender, e);
break;
case Keys.Subtract: // Ctrl + Num-
case Keys.OemMinus: // Ctrl + -
ZoomOutMenuItemClick(sender, e);
break;
case Keys.NumPad0: // Ctrl + Num0
case Keys.D0: // Ctrl + 0
ZoomSetValueMenuItemClick(zoomActualSizeMenuItem, e);
break;
case Keys.NumPad9: // Ctrl + Num9
case Keys.D9: // Ctrl + 9
ZoomBestFitMenuItemClick(sender, e);
break;
}
} else if (e.Modifiers.Equals(Keys.Control | Keys.Shift)) {
switch (e.KeyCode) {
case Keys.Add: // Ctrl + Shift + Num+
case Keys.Oemplus: // Ctrl + Shift + +
EnlargeCanvasToolStripMenuItemClick(sender, e);
break;
case Keys.Subtract: // Ctrl + Shift + Num-
case Keys.OemMinus: // Ctrl + Shift + -
ShrinkCanvasToolStripMenuItemClick(sender, e);
break;
}
}
}
/// <summary>
/// This is a "work-around" for the MouseWheel event which doesn't get to the panel
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void PanelMouseWheel(object sender, MouseEventArgs e) {
panel1.Focus();
}
protected override bool ProcessKeyPreview(ref Message msg) {
// disable default key handling if surface has requested a lock
if (!_surface.KeysLocked) {
return base.ProcessKeyPreview(ref msg);
}
return false;
}
protected override bool ProcessCmdKey(ref Message msg, Keys keys) {
// disable default key handling if surface has requested a lock
if (!_surface.KeysLocked) {
// Go through the destinations to check the EditorShortcut Keys
// this way the menu entries don't need to be enabled.
// This also fixes bugs #3526974 & #3527020
foreach (IDestination destination in DestinationHelper.GetAllDestinations()) {
if (IgnoreDestinations.Contains(destination.Designation)) {
continue;
}
if (!destination.IsActive) {
continue;
}
if (destination.EditorShortcutKeys == keys) {
destination.ExportCapture(true, _surface, _surface.CaptureDetails);
return true;
}
}
if (!_surface.ProcessCmdKey(keys)) {
return base.ProcessCmdKey(ref msg, keys);
}
}
return false;
}
private void UpdateUndoRedoSurfaceDependencies() {
if (_surface == null) {
return;
}
bool canUndo = _surface.CanUndo;
btnUndo.Enabled = canUndo;
undoToolStripMenuItem.Enabled = canUndo;
string undoAction = string.Empty;
if (canUndo) {
if (_surface.UndoActionLanguageKey != LangKey.none) {
undoAction = Language.GetString(_surface.UndoActionLanguageKey);
}
}
string undoText = Language.GetFormattedString(LangKey.editor_undo, undoAction);
btnUndo.Text = undoText;
undoToolStripMenuItem.Text = undoText;
bool canRedo = _surface.CanRedo;
btnRedo.Enabled = canRedo;
redoToolStripMenuItem.Enabled = canRedo;
string redoAction = string.Empty;
if (canRedo) {
if (_surface.RedoActionLanguageKey != LangKey.none) {
redoAction = Language.GetString(_surface.RedoActionLanguageKey);
}
}
string redoText = Language.GetFormattedString(LangKey.editor_redo, redoAction);
btnRedo.Text = redoText;
redoToolStripMenuItem.Text = redoText;
}
private void UpdateClipboardSurfaceDependencies() {
if (_surface == null) {
return;
}
// check dependencies for the Surface
bool hasItems = _surface.HasSelectedElements;
bool actionAllowedForSelection = hasItems && !_controlsDisabledDueToConfirmable;
// buttons
btnCut.Enabled = actionAllowedForSelection;
btnCopy.Enabled = actionAllowedForSelection;
btnDelete.Enabled = actionAllowedForSelection;
// menus
removeObjectToolStripMenuItem.Enabled = actionAllowedForSelection;
copyToolStripMenuItem.Enabled = actionAllowedForSelection;
cutToolStripMenuItem.Enabled = actionAllowedForSelection;
duplicateToolStripMenuItem.Enabled = actionAllowedForSelection;
// check dependencies for the Clipboard
bool hasClipboard = ClipboardHelper.ContainsFormat(SupportedClipboardFormats) || ClipboardHelper.ContainsImage();
btnPaste.Enabled = hasClipboard && !_controlsDisabledDueToConfirmable;
pasteToolStripMenuItem.Enabled = hasClipboard && !_controlsDisabledDueToConfirmable;
}
private void UpdateStatusLabel(string text, ContextMenuStrip contextMenu = null) {
statusLabel.Text = text;
statusStrip1.ContextMenuStrip = contextMenu;
}
private void StatusLabelClicked(object sender, MouseEventArgs e) {
ToolStrip ss = (StatusStrip)((ToolStripStatusLabel)sender).Owner;
ss.ContextMenuStrip?.Show(ss, e.X, e.Y);
}
private void CopyPathMenuItemClick(object sender, EventArgs e) {
ClipboardHelper.SetClipboardData(_surface.LastSaveFullPath);
}
private void OpenDirectoryMenuItemClick(object sender, EventArgs e) {
ExplorerHelper.OpenInExplorer(_surface.LastSaveFullPath);
}
private void BindFieldControls() {
// TODO: This is actually risky, if there are no references than the objects may be garbage collected
new BidirectionalBinding(btnFillColor, "SelectedColor", _surface.FieldAggregator.GetField(FieldType.FILL_COLOR), "Value", NotNullValidator.GetInstance());
new BidirectionalBinding(btnLineColor, "SelectedColor", _surface.FieldAggregator.GetField(FieldType.LINE_COLOR), "Value", NotNullValidator.GetInstance());
new BidirectionalBinding(lineThicknessUpDown, "Value", _surface.FieldAggregator.GetField(FieldType.LINE_THICKNESS), "Value", DecimalIntConverter.GetInstance(), NotNullValidator.GetInstance());
new BidirectionalBinding(blurRadiusUpDown, "Value", _surface.FieldAggregator.GetField(FieldType.BLUR_RADIUS), "Value", DecimalIntConverter.GetInstance(), NotNullValidator.GetInstance());
new BidirectionalBinding(magnificationFactorUpDown, "Value", _surface.FieldAggregator.GetField(FieldType.MAGNIFICATION_FACTOR), "Value", DecimalIntConverter.GetInstance(), NotNullValidator.GetInstance());
new BidirectionalBinding(pixelSizeUpDown, "Value", _surface.FieldAggregator.GetField(FieldType.PIXEL_SIZE), "Value", DecimalIntConverter.GetInstance(), NotNullValidator.GetInstance());
new BidirectionalBinding(brightnessUpDown, "Value", _surface.FieldAggregator.GetField(FieldType.BRIGHTNESS), "Value", DecimalDoublePercentageConverter.GetInstance(), NotNullValidator.GetInstance());
new BidirectionalBinding(fontFamilyComboBox, "Text", _surface.FieldAggregator.GetField(FieldType.FONT_FAMILY), "Value", NotNullValidator.GetInstance());
new BidirectionalBinding(fontSizeUpDown, "Value", _surface.FieldAggregator.GetField(FieldType.FONT_SIZE), "Value", DecimalFloatConverter.GetInstance(), NotNullValidator.GetInstance());
new BidirectionalBinding(fontBoldButton, "Checked", _surface.FieldAggregator.GetField(FieldType.FONT_BOLD), "Value", NotNullValidator.GetInstance());
new BidirectionalBinding(fontItalicButton, "Checked", _surface.FieldAggregator.GetField(FieldType.FONT_ITALIC), "Value", NotNullValidator.GetInstance());
new BidirectionalBinding(textHorizontalAlignmentButton, "SelectedTag", _surface.FieldAggregator.GetField(FieldType.TEXT_HORIZONTAL_ALIGNMENT), "Value", NotNullValidator.GetInstance());
new BidirectionalBinding(textVerticalAlignmentButton, "SelectedTag", _surface.FieldAggregator.GetField(FieldType.TEXT_VERTICAL_ALIGNMENT), "Value", NotNullValidator.GetInstance());
new BidirectionalBinding(shadowButton, "Checked", _surface.FieldAggregator.GetField(FieldType.SHADOW), "Value", NotNullValidator.GetInstance());
new BidirectionalBinding(previewQualityUpDown, "Value", _surface.FieldAggregator.GetField(FieldType.PREVIEW_QUALITY), "Value", DecimalDoublePercentageConverter.GetInstance(), NotNullValidator.GetInstance());
new BidirectionalBinding(obfuscateModeButton, "SelectedTag", _surface.FieldAggregator.GetField(FieldType.PREPARED_FILTER_OBFUSCATE), "Value");
new BidirectionalBinding(highlightModeButton, "SelectedTag", _surface.FieldAggregator.GetField(FieldType.PREPARED_FILTER_HIGHLIGHT), "Value");
new BidirectionalBinding(counterUpDown, "Value", _surface, "CounterStart", DecimalIntConverter.GetInstance(), NotNullValidator.GetInstance());
}
/// <summary>
/// shows/hides field controls (2nd toolbar on top) depending on fields of selected elements
/// </summary>
private void RefreshFieldControls() {
propertiesToolStrip.SuspendLayout();
if(_surface.HasSelectedElements || _surface.DrawingMode != DrawingModes.None) {
FieldAggregator props = _surface.FieldAggregator;
btnFillColor.Visible = props.HasFieldValue(FieldType.FILL_COLOR);
btnLineColor.Visible = props.HasFieldValue(FieldType.LINE_COLOR);
lineThicknessLabel.Visible = lineThicknessUpDown.Visible = props.HasFieldValue(FieldType.LINE_THICKNESS);
blurRadiusLabel.Visible = blurRadiusUpDown.Visible = props.HasFieldValue(FieldType.BLUR_RADIUS);
previewQualityLabel.Visible = previewQualityUpDown.Visible = props.HasFieldValue(FieldType.PREVIEW_QUALITY);
magnificationFactorLabel.Visible = magnificationFactorUpDown.Visible = props.HasFieldValue(FieldType.MAGNIFICATION_FACTOR);
pixelSizeLabel.Visible = pixelSizeUpDown.Visible = props.HasFieldValue(FieldType.PIXEL_SIZE);
brightnessLabel.Visible = brightnessUpDown.Visible = props.HasFieldValue(FieldType.BRIGHTNESS);
arrowHeadsLabel.Visible = arrowHeadsDropDownButton.Visible = props.HasFieldValue(FieldType.ARROWHEADS);
fontFamilyComboBox.Visible = props.HasFieldValue(FieldType.FONT_FAMILY);
fontSizeLabel.Visible = fontSizeUpDown.Visible = props.HasFieldValue(FieldType.FONT_SIZE);
fontBoldButton.Visible = props.HasFieldValue(FieldType.FONT_BOLD);
fontItalicButton.Visible = props.HasFieldValue(FieldType.FONT_ITALIC);
textHorizontalAlignmentButton.Visible = props.HasFieldValue(FieldType.TEXT_HORIZONTAL_ALIGNMENT);
textVerticalAlignmentButton.Visible = props.HasFieldValue(FieldType.TEXT_VERTICAL_ALIGNMENT);
shadowButton.Visible = props.HasFieldValue(FieldType.SHADOW);
counterLabel.Visible = counterUpDown.Visible = props.HasFieldValue(FieldType.FLAGS)
&& ((FieldFlag)props.GetFieldValue(FieldType.FLAGS) & FieldFlag.COUNTER) == FieldFlag.COUNTER;
btnConfirm.Visible = btnCancel.Visible = props.HasFieldValue(FieldType.FLAGS)
&& ((FieldFlag)props.GetFieldValue(FieldType.FLAGS) & FieldFlag.CONFIRMABLE) == FieldFlag.CONFIRMABLE;
obfuscateModeButton.Visible = props.HasFieldValue(FieldType.PREPARED_FILTER_OBFUSCATE);
highlightModeButton.Visible = props.HasFieldValue(FieldType.PREPARED_FILTER_HIGHLIGHT);
} else {
HideToolstripItems();
}
propertiesToolStrip.ResumeLayout();
}
private void HideToolstripItems() {
foreach(ToolStripItem toolStripItem in propertiesToolStrip.Items) {
toolStripItem.Visible = false;
}
}
/// <summary>
/// refreshes all editor controls depending on selected elements and their fields
/// </summary>
private void RefreshEditorControls() {
int stepLabels = _surface.CountStepLabels(null);
Image icon;
if (stepLabels <= 20) {
icon = (Image)resources.GetObject($"btnStepLabel{stepLabels:00}.Image");
} else {
icon = (Image)resources.GetObject("btnStepLabel20+.Image");
}
btnStepLabel.Image = icon;
addCounterToolStripMenuItem.Image = icon;
FieldAggregator props = _surface.FieldAggregator;
// if a confirmable element is selected, we must disable most of the controls
// since we demand confirmation or cancel for confirmable element
if (props.HasFieldValue(FieldType.FLAGS) && ((FieldFlag)props.GetFieldValue(FieldType.FLAGS) & FieldFlag.CONFIRMABLE) == FieldFlag.CONFIRMABLE)
{
// disable most controls
if (!_controlsDisabledDueToConfirmable) {
ToolStripItemEndisabler.Disable(menuStrip1);
ToolStripItemEndisabler.Disable(destinationsToolStrip);
ToolStripItemEndisabler.Disable(toolsToolStrip);
ToolStripItemEndisabler.Enable(closeToolStripMenuItem);
ToolStripItemEndisabler.Enable(helpToolStripMenuItem);
ToolStripItemEndisabler.Enable(aboutToolStripMenuItem);
ToolStripItemEndisabler.Enable(preferencesToolStripMenuItem);
_controlsDisabledDueToConfirmable = true;
}
} else if(_controlsDisabledDueToConfirmable) {
// re-enable disabled controls, confirmable element has either been confirmed or cancelled
ToolStripItemEndisabler.Enable(menuStrip1);
ToolStripItemEndisabler.Enable(destinationsToolStrip);
ToolStripItemEndisabler.Enable(toolsToolStrip);
_controlsDisabledDueToConfirmable = false;
}
// en/disable controls depending on whether an element is selected at all
UpdateClipboardSurfaceDependencies();
UpdateUndoRedoSurfaceDependencies();
// en/disablearrage controls depending on hierarchy of selected elements
bool actionAllowedForSelection = _surface.HasSelectedElements && !_controlsDisabledDueToConfirmable;
bool push = actionAllowedForSelection && _surface.CanPushSelectionDown();
bool pull = actionAllowedForSelection && _surface.CanPullSelectionUp();
arrangeToolStripMenuItem.Enabled = push || pull;
if (arrangeToolStripMenuItem.Enabled) {
upToTopToolStripMenuItem.Enabled = pull;
upOneLevelToolStripMenuItem.Enabled = pull;
downToBottomToolStripMenuItem.Enabled = push;
downOneLevelToolStripMenuItem.Enabled = push;
}
// finally show/hide field controls depending on the fields of selected elements
RefreshFieldControls();
}
private void ArrowHeadsToolStripMenuItemClick(object sender, EventArgs e) {
_surface.FieldAggregator.GetField(FieldType.ARROWHEADS).Value = (ArrowContainer.ArrowHeadCombination)((ToolStripMenuItem)sender).Tag;
}
private void EditToolStripMenuItemClick(object sender, EventArgs e) {
UpdateClipboardSurfaceDependencies();
UpdateUndoRedoSurfaceDependencies();
}
private void FontPropertyChanged(object sender, EventArgs e) {
// in case we forced another FontStyle before, reset it first.
if (fontBoldButton != null && _originalBoldCheckState != fontBoldButton.Checked)
{
fontBoldButton.Checked = _originalBoldCheckState;
}
if (fontItalicButton != null && _originalItalicCheckState != fontItalicButton.Checked)
{
fontItalicButton.Checked = _originalItalicCheckState;
}
var fontFamily = fontFamilyComboBox.FontFamily;
bool boldAvailable = fontFamily.IsStyleAvailable(FontStyle.Bold);
if (fontBoldButton != null)
{
if (!boldAvailable)
{
_originalBoldCheckState = fontBoldButton.Checked;
fontBoldButton.Checked = false;
}
fontBoldButton.Enabled = boldAvailable;
}
bool italicAvailable = fontFamily.IsStyleAvailable(FontStyle.Italic);
if (fontItalicButton != null)
{
if (!italicAvailable)
{
fontItalicButton.Checked = false;
}
fontItalicButton.Enabled = italicAvailable;
}
bool regularAvailable = fontFamily.IsStyleAvailable(FontStyle.Regular);
if (regularAvailable)
{
return;
}
if (boldAvailable) {
if (fontBoldButton != null)
{
fontBoldButton.Checked = true;
}
} else if(italicAvailable) {
if (fontItalicButton != null)
{
fontItalicButton.Checked = true;
}
}
}
private void FieldAggregatorFieldChanged(object sender, FieldChangedEventArgs e) {
// in addition to selection, deselection of elements, we need to
// refresh toolbar if prepared filter mode is changed
if(Equals(e.Field.FieldType, FieldType.PREPARED_FILTER_HIGHLIGHT)) {
RefreshFieldControls();
}
}
private void FontBoldButtonClick(object sender, EventArgs e) {
_originalBoldCheckState = fontBoldButton.Checked;
}
private void FontItalicButtonClick(object sender, EventArgs e) {
_originalItalicCheckState = fontItalicButton.Checked;
}
private void ToolBarFocusableElementGotFocus(object sender, EventArgs e) {
_surface.KeysLocked = true;
}
private void ToolBarFocusableElementLostFocus(object sender, EventArgs e) {
_surface.KeysLocked = false;
}
private void SaveElementsToolStripMenuItemClick(object sender, EventArgs e) {
SaveFileDialog saveFileDialog = new SaveFileDialog
{
Filter = "Greenshot templates (*.gst)|*.gst",
FileName = FilenameHelper.GetFilenameWithoutExtensionFromPattern(coreConfiguration.OutputFileFilenamePattern, _surface.CaptureDetails)
};
DialogResult dialogResult = saveFileDialog.ShowDialog();
if(dialogResult.Equals(DialogResult.OK))
{
using Stream streamWrite = File.OpenWrite(saveFileDialog.FileName);
_surface.SaveElementsToStream(streamWrite);
}
}
private void LoadElementsToolStripMenuItemClick(object sender, EventArgs e) {
OpenFileDialog openFileDialog = new OpenFileDialog
{
Filter = "Greenshot templates (*.gst)|*.gst"
};
if (openFileDialog.ShowDialog() == DialogResult.OK) {
using (Stream streamRead = File.OpenRead(openFileDialog.FileName)) {
_surface.LoadElementsFromStream(streamRead);
}
_surface.Refresh();
}
}
private void DestinationToolStripMenuItemClick(object sender, EventArgs e) {
IDestination clickedDestination = null;
if (sender is Control control) {
Control clickedControl = control;
if (clickedControl.ContextMenuStrip != null) {
clickedControl.ContextMenuStrip.Show(Cursor.Position);
return;
}
clickedDestination = (IDestination)clickedControl.Tag;
}
else
{
if (sender is ToolStripMenuItem item) {
ToolStripMenuItem clickedMenuItem = item;
clickedDestination = (IDestination)clickedMenuItem.Tag;
}
}
ExportInformation exportInformation = clickedDestination?.ExportCapture(true, _surface, _surface.CaptureDetails);
if (exportInformation != null && exportInformation.ExportMade) {
_surface.Modified = false;
}
}
protected void FilterPresetDropDownItemClicked(object sender, ToolStripItemClickedEventArgs e) {
RefreshFieldControls();
Invalidate(true);
}
private void SelectAllToolStripMenuItemClick(object sender, EventArgs e) {
_surface.SelectAllElements();
}
private void BtnConfirmClick(object sender, EventArgs e) {
_surface.ConfirmSelectedConfirmableElements(true);
RefreshFieldControls();
}
private void BtnCancelClick(object sender, EventArgs e) {
_surface.ConfirmSelectedConfirmableElements(false);
RefreshFieldControls();
}
private void Insert_window_toolstripmenuitemMouseEnter(object sender, EventArgs e) {
ToolStripMenuItem captureWindowMenuItem = (ToolStripMenuItem)sender;
var mainForm = SimpleServiceProvider.Current.GetInstance<MainForm>();
mainForm.AddCaptureWindowMenuItems(captureWindowMenuItem, Contextmenu_window_Click);
}
private void Contextmenu_window_Click(object sender, EventArgs e) {
ToolStripMenuItem clickedItem = (ToolStripMenuItem)sender;
try {
WindowDetails windowToCapture = (WindowDetails)clickedItem.Tag;
ICapture capture = new Capture();
using (Graphics graphics = Graphics.FromHwnd(Handle)) {
capture.CaptureDetails.DpiX = graphics.DpiY;
capture.CaptureDetails.DpiY = graphics.DpiY;
}
windowToCapture = CaptureHelper.SelectCaptureWindow(windowToCapture);
if (windowToCapture != null) {
capture = CaptureHelper.CaptureWindow(windowToCapture, capture, coreConfiguration.WindowCaptureMode);
if (capture?.CaptureDetails != null && capture.Image != null) {
((Bitmap)capture.Image).SetResolution(capture.CaptureDetails.DpiX, capture.CaptureDetails.DpiY);
_surface.AddImageContainer((Bitmap)capture.Image, 100, 100);
}
Activate();
WindowDetails.ToForeground(Handle);
}
capture?.Dispose();
} catch (Exception exception) {
Log.Error(exception);
}
}
private void AutoCropToolStripMenuItemClick(object sender, EventArgs e) {
if (_surface.AutoCrop()) {
RefreshFieldControls();
}
}
private void AddBorderToolStripMenuItemClick(object sender, EventArgs e) {
_surface.ApplyBitmapEffect(new BorderEffect());
UpdateUndoRedoSurfaceDependencies();
}
/// <summary>
/// Added for FEATURE-919, increasing the canvas by 25 pixels in every direction.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void EnlargeCanvasToolStripMenuItemClick(object sender, EventArgs e)
{
_surface.ApplyBitmapEffect(new ResizeCanvasEffect(25, 25, 25, 25));
UpdateUndoRedoSurfaceDependencies();
}
/// <summary>
/// Added for FEATURE-919, to make the capture as small as possible again.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void ShrinkCanvasToolStripMenuItemClick(object sender, EventArgs e)
{
Rectangle cropRectangle;
using (Image tmpImage = GetImageForExport())
{
cropRectangle = ImageHelper.FindAutoCropRectangle(tmpImage, coreConfiguration.AutoCropDifference);
}
if (_surface.IsCropPossible(ref cropRectangle))
{
_surface.ApplyCrop(cropRectangle);
UpdateUndoRedoSurfaceDependencies();
}
}
/// <summary>
/// This is used when the dropshadow button is used
/// </summary>
/// <param name="sender"></param>
/// <param name="e">MouseEventArgs</param>
private void AddDropshadowToolStripMenuItemMouseUp(object sender, MouseEventArgs e)
{
var dropShadowEffect = EditorConfiguration.DropShadowEffectSettings;
bool apply;
switch (e.Button)
{
case MouseButtons.Left:
apply = true;
break;
case MouseButtons.Right:
var result = new DropShadowSettingsForm(dropShadowEffect).ShowDialog(this);
apply = result == DialogResult.OK;
break;
default:
return;
}
if (apply)
{
_surface.ApplyBitmapEffect(dropShadowEffect);
UpdateUndoRedoSurfaceDependencies();
}
}
/// <summary>
/// Open the resize settings from, and resize if ok was pressed
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void BtnResizeClick(object sender, EventArgs e) {
var resizeEffect = new ResizeEffect(_surface.Image.Width, _surface.Image.Height, true);
var result = new ResizeSettingsForm(resizeEffect).ShowDialog(this);
if (result == DialogResult.OK) {
_surface.ApplyBitmapEffect(resizeEffect);
UpdateUndoRedoSurfaceDependencies();
}
}
/// <summary>
/// This is used when the torn-edge button is used
/// </summary>
/// <param name="sender"></param>
/// <param name="e">MouseEventArgs</param>
private void TornEdgesToolStripMenuItemMouseUp(object sender, MouseEventArgs e)
{
var tornEdgeEffect = EditorConfiguration.TornEdgeEffectSettings;
bool apply;
switch (e.Button)
{
case MouseButtons.Left:
apply = true;
break;
case MouseButtons.Right:
var result = new TornEdgeSettingsForm(tornEdgeEffect).ShowDialog(this);
apply = result == DialogResult.OK;
break;
default:
return;
}
if (apply)
{
_surface.ApplyBitmapEffect(tornEdgeEffect);
UpdateUndoRedoSurfaceDependencies();
}
}
private void GrayscaleToolStripMenuItemClick(object sender, EventArgs e) {
_surface.ApplyBitmapEffect(new GrayscaleEffect());
UpdateUndoRedoSurfaceDependencies();
}
private void ClearToolStripMenuItemClick(object sender, EventArgs e) {
_surface.Clear(Color.Transparent);
UpdateUndoRedoSurfaceDependencies();
}
private void RotateCwToolstripButtonClick(object sender, EventArgs e) {
_surface.ApplyBitmapEffect(new RotateEffect(90));
UpdateUndoRedoSurfaceDependencies();
}
private void RotateCcwToolstripButtonClick(object sender, EventArgs e) {
_surface.ApplyBitmapEffect(new RotateEffect(270));
UpdateUndoRedoSurfaceDependencies();
}
private void InvertToolStripMenuItemClick(object sender, EventArgs e) {
_surface.ApplyBitmapEffect(new InvertEffect());
UpdateUndoRedoSurfaceDependencies();
}
private void ImageEditorFormResize(object sender, EventArgs e) {
AlignCanvasPositionAfterResize();
}
private void AlignCanvasPositionAfterResize() {
if (Surface?.Image == null || panel1 == null) {
return;
}
var canvas = Surface as Control;
Size canvasSize = canvas.Size;
Size currentClientSize = panel1.ClientSize;
Panel panel = (Panel) canvas?.Parent;
if (panel == null) {
return;
}
int offsetX = -panel.HorizontalScroll.Value;
int offsetY = -panel.VerticalScroll.Value;
if (currentClientSize.Width > canvasSize.Width) {
canvas.Left = offsetX + (currentClientSize.Width - canvasSize.Width) / 2;
} else {
canvas.Left = offsetX + 0;
}
if (currentClientSize.Height > canvasSize.Height) {
canvas.Top = offsetY + (currentClientSize.Height - canvasSize.Height) / 2;
} else {
canvas.Top = offsetY + 0;
}
}
/// <summary>
/// Compute a size as a sum of surface size and chrome.
/// Upper bound is working area of the screen. Lower bound is fixed value.
/// </summary>
private Size GetOptimalWindowSize() {
var surfaceSize = (Surface as Control).Size;
var chromeSize = GetChromeSize();
var newWidth = chromeSize.Width + surfaceSize.Width;
var newHeight = chromeSize.Height + surfaceSize.Height;
// Upper bound. Don't make it bigger than the available working area.
var maxWindowSize = GetAvailableScreenSpace();
newWidth = Math.Min(newWidth, maxWindowSize.Width);
newHeight = Math.Min(newHeight, maxWindowSize.Height);
// Lower bound. Don't make it smaller than a fixed value.
int minimumFormWidth = 650;
int minimumFormHeight = 530;
newWidth = Math.Max(minimumFormWidth, newWidth);
newHeight = Math.Max(minimumFormHeight, newHeight);
return new Size(newWidth, newHeight);
}
private Size GetChromeSize()
=> Size - panel1.ClientSize;
/// <summary>
/// Compute a size that the form can take without getting out of working area of the screen.
/// </summary>
private Size GetAvailableScreenSpace() {
var screen = Screen.FromControl(this);
var screenBounds = screen.Bounds;
var workingArea = screen.WorkingArea;
if (Left > screenBounds.Left && Top > screenBounds.Top) {
return new Size(workingArea.Right - Left, workingArea.Bottom - Top);
} else {
return workingArea.Size;
}
}
private void ZoomInMenuItemClick(object sender, EventArgs e) {
var zoomValue = Surface.ZoomFactor;
var nextIndex = Array.FindIndex(ZOOM_VALUES, v => v > zoomValue);
var nextValue = nextIndex < 0 ? ZOOM_VALUES[ZOOM_VALUES.Length - 1] : ZOOM_VALUES[nextIndex];
ZoomSetValue(nextValue);
}
private void ZoomOutMenuItemClick(object sender, EventArgs e) {
var zoomValue = Surface.ZoomFactor;
var nextIndex = Array.FindLastIndex(ZOOM_VALUES, v => v < zoomValue);
var nextValue = nextIndex < 0 ? ZOOM_VALUES[0] : ZOOM_VALUES[nextIndex];
ZoomSetValue(nextValue);
}
private void ZoomSetValueMenuItemClick(object sender, EventArgs e) {
var senderMenuItem = (ToolStripMenuItem)sender;
var nextValue = Fraction.Parse((string)senderMenuItem.Tag);
ZoomSetValue(nextValue);
}
private void ZoomBestFitMenuItemClick(object sender, EventArgs e) {
var maxWindowSize = GetAvailableScreenSpace();
var chromeSize = GetChromeSize();
var maxImageSize = maxWindowSize - chromeSize;
var imageSize = Surface.Image.Size;
static bool isFit(Fraction scale, int source, int boundary)
=> (int)(source * scale) <= boundary;
var nextIndex = Array.FindLastIndex(
ZOOM_VALUES,
zoom => isFit(zoom, imageSize.Width, maxImageSize.Width)
&& isFit(zoom, imageSize.Height, maxImageSize.Height)
);
var nextValue = nextIndex < 0 ? ZOOM_VALUES[0] : ZOOM_VALUES[nextIndex];
ZoomSetValue(nextValue);
}
private void ZoomSetValue(Fraction value) {
var surface = Surface as Surface;
var panel = surface?.Parent as Panel;
if (panel == null)
{
return;
}
if (value == Surface.ZoomFactor)
{
return;
}
// Store scroll position
var rc = surface.GetVisibleRectangle(); // use visible rc by default
var size = surface.Size;
if (value > Surface.ZoomFactor) // being smart on zoom-in
{
var selection = surface.GetSelectionRectangle();
selection.Intersect(rc);
if (selection != Rectangle.Empty)
{
rc = selection; // zoom to visible part of selection
}
else
{
// if image fits completely to currently visible rc and there are no things to focus on
// - prefer top left corner to zoom-in as less disorienting for screenshots
if (size.Width < rc.Width)
{
rc.Width = 0;
}
if (size.Height < rc.Height)
{
rc.Height = 0;
}
}
}
var horizontalCenter = 1.0 * (rc.Left + rc.Width / 2) / size.Width;
var verticalCenter = 1.0 * (rc.Top + rc.Height / 2) / size.Height;
// Set the new zoom value
Surface.ZoomFactor = value;
Size = GetOptimalWindowSize();
AlignCanvasPositionAfterResize();
// Update zoom controls
zoomStatusDropDownBtn.Text = ((int)(100 * (double)value)).ToString() + "%";
var valueString = value.ToString();
foreach (var item in zoomMenuStrip.Items) {
if (item is ToolStripMenuItem menuItem) {
menuItem.Checked = menuItem.Tag as string == valueString;
}
}
// Restore scroll position
rc = surface.GetVisibleRectangle();
size = surface.Size;
panel.AutoScrollPosition = new Point(
(int)(horizontalCenter * size.Width) - rc.Width / 2,
(int)(verticalCenter * size.Height) - rc.Height / 2
);
}
}
}