Project cleanup (#302)

* Renamed the projects
* Refactoring of the namespaces
* Moving the Inno-Setup files away from the application project.
* Code formatting (indents etc.) to get it consistent, simplify it for contributors.
* Renamed the GreenshotPlugin to Greenshot.Base
This commit is contained in:
Robin Krom 2021-03-28 21:04:09 +02:00 committed by GitHub
commit 473ced231f
No known key found for this signature in database
GPG key ID: 3FA160E9E9D6F0AD
1312 changed files with 67150 additions and 59761 deletions

View file

@ -1,18 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<!--<system.windows.forms jitDebugging="true" />-->
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.2" />
</startup>
<System.Windows.Forms.ApplicationConfigurationSection>
<add key="DpiAwareness" value="PerMonitorV2" />
</System.Windows.Forms.ApplicationConfigurationSection>
<runtime>
<loadFromRemoteSources enabled="true" />
<relativeBindForResources enabled="true" />
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<probing privatePath="Addons" />
<probing privatePath="App\Greenshot" />
</assemblyBinding>
</runtime>
</configuration>

View file

@ -1,151 +0,0 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
using System;
using System.Collections.Generic;
using System.Drawing;
using Greenshot.Drawing.Fields;
using GreenshotPlugin.Effects;
using GreenshotPlugin.IniFile;
using GreenshotPlugin.Interfaces.Drawing;
using GreenshotPlugin.UnmanagedHelpers.Enums;
using GreenshotPlugin.UnmanagedHelpers.Structs;
namespace Greenshot.Configuration {
/// <summary>
/// Description of CoreConfiguration.
/// </summary>
[IniSection("Editor", Description="Greenshot editor configuration")]
public class EditorConfiguration : IniSection {
[IniProperty("RecentColors", Separator="|", Description="Last used colors")]
public List<Color> RecentColors { get; set; }
[IniProperty("LastFieldValue", Separator="|", Description="Field values, make sure the last used settings are re-used")]
public Dictionary<string, object> LastUsedFieldValues { get; set; }
[IniProperty("MatchSizeToCapture", Description="Match the editor window size to the capture", DefaultValue="True")]
public bool MatchSizeToCapture { get; set; }
[IniProperty("WindowPlacementFlags", Description="Placement flags", DefaultValue="0")]
public WindowPlacementFlags WindowPlacementFlags { get; set; }
[IniProperty("WindowShowCommand", Description="Show command", DefaultValue="Normal")]
public ShowWindowCommand ShowWindowCommand { get; set; }
[IniProperty("WindowMinPosition", Description="Position of minimized window", DefaultValue="-1,-1")]
public Point WindowMinPosition { get; set; }
[IniProperty("WindowMaxPosition", Description="Position of maximized window", DefaultValue="-1,-1")]
public Point WindowMaxPosition { get; set; }
[IniProperty("WindowNormalPosition", Description="Position of normal window", DefaultValue="100,100,400,400")]
public Rectangle WindowNormalPosition { get; set; }
[IniProperty("ReuseEditor", Description = "Reuse already open editor", DefaultValue = "false")]
public bool ReuseEditor { get; set; }
[IniProperty("FreehandSensitivity", Description = "The smaller this number, the less smoothing is used. Decrease for detailed drawing, e.g. when using a pen. Increase for smoother lines. e.g. when you want to draw a smooth line.", DefaultValue = "3")]
public int FreehandSensitivity { get; set; }
[IniProperty("SuppressSaveDialogAtClose", Description="Suppressed the 'do you want to save' dialog when closing the editor.", DefaultValue="False")]
public bool SuppressSaveDialogAtClose { get; set; }
[IniProperty("DropShadowEffectSettings", Description = "Settings for the drop shadow effect.")]
public DropShadowEffect DropShadowEffectSettings { get; set; }
[IniProperty("TornEdgeEffectSettings", Description = "Settings for the torn edge effect.")]
public TornEdgeEffect TornEdgeEffectSettings { get; set; }
public override void AfterLoad() {
base.AfterLoad();
if (RecentColors == null) {
RecentColors = new List<Color>();
}
}
/// <param name="requestingType">Type of the class for which to create the field</param>
/// <param name="fieldType">FieldType of the field to construct</param>
/// <param name="preferredDefaultValue"></param>
/// <returns>a new Field of the given fieldType, with the scope of it's value being restricted to the Type scope</returns>
public IField CreateField(Type requestingType, IFieldType fieldType, object preferredDefaultValue)
{
string requestingTypeName = requestingType.Name;
string requestedField = requestingTypeName + "." + fieldType.Name;
object fieldValue = preferredDefaultValue;
// Check if the configuration exists
if (LastUsedFieldValues == null) {
LastUsedFieldValues = new Dictionary<string, object>();
}
// Check if settings for the requesting type exist, if not create!
if (LastUsedFieldValues.ContainsKey(requestedField)) {
// Check if a value is set (not null)!
if (LastUsedFieldValues[requestedField] != null) {
fieldValue = LastUsedFieldValues[requestedField];
} else {
// Overwrite null value
LastUsedFieldValues[requestedField] = fieldValue;
}
} else {
LastUsedFieldValues.Add(requestedField, fieldValue);
}
return new Field(fieldType, requestingType)
{
Value = fieldValue
};
}
public void UpdateLastFieldValue(IField field)
{
string requestedField = field.Scope + "." + field.FieldType.Name;
// Check if the configuration exists
if (LastUsedFieldValues == null) {
LastUsedFieldValues = new Dictionary<string, object>();
}
// check if settings for the requesting type exist, if not create!
if (LastUsedFieldValues.ContainsKey(requestedField)) {
LastUsedFieldValues[requestedField] = field.Value;
} else {
LastUsedFieldValues.Add(requestedField, field.Value);
}
}
public void ResetEditorPlacement()
{
WindowNormalPosition = new Rectangle(100, 100, 400, 400);
WindowMaxPosition = new Point(-1,-1);
WindowMinPosition = new Point(-1, -1);
WindowPlacementFlags = 0;
ShowWindowCommand = ShowWindowCommand.Normal;
}
public WindowPlacement GetEditorPlacement() {
WindowPlacement placement = WindowPlacement.Default;
placement.NormalPosition = new RECT(WindowNormalPosition);
placement.MaxPosition = new POINT(WindowMaxPosition);
placement.MinPosition = new POINT(WindowMinPosition);
placement.ShowCmd = ShowWindowCommand;
placement.Flags = WindowPlacementFlags;
return placement;
}
public void SetEditorPlacement(WindowPlacement placement) {
WindowNormalPosition = placement.NormalPosition.ToRectangle();
WindowMaxPosition = placement.MaxPosition.ToPoint();
WindowMinPosition = placement.MinPosition.ToPoint();
ShowWindowCommand = placement.ShowCmd;
WindowPlacementFlags = placement.Flags;
}
}
}

View file

@ -1,80 +0,0 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
using System.Diagnostics.CodeAnalysis;
namespace Greenshot.Configuration {
[SuppressMessage("ReSharper", "InconsistentNaming")]
public enum LangKey {
none,
contextmenu_capturefullscreen_all,
contextmenu_capturefullscreen_left,
contextmenu_capturefullscreen_top,
contextmenu_capturefullscreen_right,
contextmenu_capturefullscreen_bottom,
contextmenu_captureie,
editor_clipboardfailed,
editor_close_on_save,
editor_close_on_save_title,
editor_copytoclipboard,
editor_cuttoclipboard,
editor_deleteelement,
editor_downonelevel,
editor_downtobottom,
editor_duplicate,
editor_email,
editor_imagesaved,
editor_title,
editor_uponelevel,
editor_uptotop,
editor_undo,
editor_redo,
editor_resetsize,
error,
error_multipleinstances,
error_openfile,
error_openlink,
error_save,
error_save_invalid_chars,
print_error,
quicksettings_destination_file,
settings_destination,
settings_destination_clipboard,
settings_destination_editor,
settings_destination_fileas,
settings_destination_printer,
settings_destination_picker,
settings_filenamepattern,
settings_message_filenamepattern,
settings_printoptions,
settings_tooltip_filenamepattern,
settings_tooltip_language,
settings_tooltip_primaryimageformat,
settings_tooltip_storagelocation,
settings_visualization,
settings_window_capture_mode,
tooltip_firststart,
warning,
warning_hotkeys,
wait_ie_capture,
update_found
}
}

View file

@ -1,70 +0,0 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
using System.ComponentModel;
using System.Windows.Forms;
using GreenshotPlugin.Controls;
namespace Greenshot.Controls {
/// <summary>
/// A simple ToolStripDropDownButton implementing INotifyPropertyChanged for data binding.
/// Also, when a DropDownItem is selected, the DropDownButton adopts its Tag and Image.
/// The selected tag can be accessed via SelectedTag property.
/// </summary>
public class BindableToolStripDropDownButton : ToolStripDropDownButton, INotifyPropertyChanged, IGreenshotLanguageBindable {
public event PropertyChangedEventHandler PropertyChanged;
[Category("Greenshot"), DefaultValue(null), Description("Specifies key of the language file to use when displaying the text.")]
public string LanguageKey {
get;
set;
}
public object SelectedTag {
get { if(Tag==null && DropDownItems.Count>0) Tag=DropDownItems[0].Tag; return Tag; }
set { AdoptFromTag(value); }
}
protected override void OnDropDownItemClicked(ToolStripItemClickedEventArgs e) {
ToolStripItem clickedItem = e.ClickedItem;
if(Tag == null || !Tag.Equals(clickedItem.Tag)) {
Tag = clickedItem.Tag;
Image = clickedItem.Image;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("SelectedTag"));
}
base.OnDropDownItemClicked(e);
}
private void AdoptFromTag(object tag) {
if(Tag == null || !Tag.Equals(tag)) {
Tag = tag;
foreach(ToolStripItem item in DropDownItems) {
if(item.Tag != null && item.Tag.Equals(tag)) {
Image = item.Image;
break;
}
}
Tag = tag;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("SelectedTag"));
}
}
}
}

View file

@ -1,90 +0,0 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
using System;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;
using GreenshotPlugin.Controls;
using ColorDialog = Greenshot.Forms.ColorDialog;
namespace Greenshot.Controls {
/// <summary>
/// Description of ColorButton.
/// </summary>
public class ColorButton : Button, IGreenshotLanguageBindable {
public event PropertyChangedEventHandler PropertyChanged;
private Color _selectedColor = Color.White;
[Category("Greenshot"), DefaultValue(null), Description("Specifies key of the language file to use when displaying the text.")]
public string LanguageKey {
get;
set;
}
public ColorButton() {
Click += ColorButtonClick;
}
public Color SelectedColor {
get {return _selectedColor;}
set {
_selectedColor = value;
Brush brush;
if(value != Color.Transparent) {
brush = new SolidBrush(value);
} else {
brush = new HatchBrush(HatchStyle.Percent50, Color.White, Color.Gray);
}
if (Image != null)
{
using Graphics graphics = Graphics.FromImage(Image);
graphics.FillRectangle(brush, new Rectangle(4,17,16,3));
}
// cleanup GDI Object
brush.Dispose();
Invalidate();
}
}
private void ColorButtonClick(object sender, EventArgs e) {
var colorDialog = new ColorDialog
{
Color = SelectedColor
};
// Using the parent to make sure the dialog doesn't show on another window
colorDialog.ShowDialog(Parent.Parent);
if (colorDialog.DialogResult == DialogResult.Cancel)
{
return;
}
if (colorDialog.Color.Equals(SelectedColor))
{
return;
}
SelectedColor = colorDialog.Color;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("SelectedColor"));
}
}
}

View file

@ -1,45 +0,0 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
using GreenshotPlugin.Core;
using System.Drawing;
using System.Windows.Forms;
using GreenshotPlugin.IniFile;
namespace Greenshot.Controls {
/// <summary>
/// ToolStripProfessionalRenderer which draws the Check correctly when the icons are larger
/// </summary>
public class ContextMenuToolStripProfessionalRenderer : ToolStripProfessionalRenderer {
private static readonly CoreConfiguration CoreConfig = IniConfig.GetIniSection<CoreConfiguration>();
private static Image _scaledCheckbox;
protected override void OnRenderItemCheck(ToolStripItemImageRenderEventArgs e) {
if (_scaledCheckbox == null || _scaledCheckbox.Size != CoreConfig.ScaledIconSize) {
_scaledCheckbox?.Dispose();
_scaledCheckbox = ImageHelper.ResizeImage(e.Image, true, CoreConfig.ScaledIconSize.Width, CoreConfig.ScaledIconSize.Height, null);
}
Rectangle old = e.ImageRectangle;
ToolStripItemImageRenderEventArgs clone = new ToolStripItemImageRenderEventArgs(e.Graphics, e.Item, _scaledCheckbox, new Rectangle(old.X, 0, old.Width, old.Height));
base.OnRenderItemCheck(clone);
}
}
}

View file

@ -1,67 +0,0 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
using System.Drawing;
using System.Windows.Forms;
namespace Greenshot.Controls {
/// <summary>
/// Prevent having a gradient background in the toolstrip, and the overflow button
/// See: http://stackoverflow.com/a/16926979
/// </summary>
internal class CustomProfessionalColorTable : ProfessionalColorTable {
public override Color ToolStripGradientBegin {
get { return SystemColors.Control; }
}
public override Color ToolStripGradientMiddle {
get { return SystemColors.Control; }
}
public override Color ToolStripGradientEnd {
get { return SystemColors.Control; }
}
public override Color OverflowButtonGradientBegin {
get { return SystemColors.Control; }
}
public override Color OverflowButtonGradientMiddle {
get { return SystemColors.Control; }
}
public override Color OverflowButtonGradientEnd {
get { return SystemColors.Control; }
}
}
/// <summary>
/// ToolStripProfessionalRenderer without having a visual artifact
/// See: http://stackoverflow.com/a/16926979 and http://stackoverflow.com/a/13418840
/// </summary>
public class CustomToolStripProfessionalRenderer : ToolStripProfessionalRenderer {
public CustomToolStripProfessionalRenderer() : base(new CustomProfessionalColorTable()) {
RoundedEdges = false;
}
/// <summary>
/// By overriding the OnRenderToolStripBorder we can make the ToolStrip without border
/// </summary>
/// <param name="e"></param>
protected override void OnRenderToolStripBorder(ToolStripRenderEventArgs e) {
// Don't draw a border
}
}
}

View file

@ -1,120 +0,0 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
using System;
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;
namespace Greenshot.Controls {
/// <summary>
/// ToolStripComboBox containing installed font families,
/// implementing INotifyPropertyChanged for data binding
/// </summary>
public class FontFamilyComboBox : ToolStripComboBox, INotifyPropertyChanged {
public event PropertyChangedEventHandler PropertyChanged;
public FontFamily FontFamily {
get { return (FontFamily)SelectedItem; }
set {
if (!SelectedItem.Equals(value)) {
SelectedItem = value;
}
}
}
public FontFamilyComboBox()
{
if (ComboBox != null)
{
ComboBox.DataSource = FontFamily.Families;
ComboBox.DisplayMember = "Name";
SelectedIndexChanged += BindableToolStripComboBox_SelectedIndexChanged;
ComboBox.DrawMode = DrawMode.OwnerDrawFixed;
ComboBox.DrawItem += ComboBox_DrawItem;
}
}
private void ComboBox_DrawItem(object sender, DrawItemEventArgs e) {
// DrawBackground handles drawing the background (i.e,. hot-tracked v. not)
// It uses the system colors (Bluish, and and white, by default)
// same as calling e.Graphics.FillRectangle ( SystemBrushes.Highlight, e.Bounds );
e.DrawBackground();
if (e.Index > -1) {
FontFamily fontFamily = Items[e.Index] as FontFamily;
FontStyle fontStyle = FontStyle.Regular;
if (fontFamily != null && !fontFamily.IsStyleAvailable(FontStyle.Regular)) {
if (fontFamily.IsStyleAvailable(FontStyle.Bold)) {
fontStyle = FontStyle.Bold;
} else if (fontFamily.IsStyleAvailable(FontStyle.Italic)) {
fontStyle = FontStyle.Italic;
} else if (fontFamily.IsStyleAvailable(FontStyle.Strikeout)) {
fontStyle = FontStyle.Strikeout;
} else if (fontFamily.IsStyleAvailable(FontStyle.Underline)) {
fontStyle = FontStyle.Underline;
}
}
try {
if (fontFamily != null)
{
DrawText(e.Graphics, fontFamily, fontStyle, e.Bounds, fontFamily.Name);
}
} catch {
// If the drawing failed, BUG-1770 seems to have a weird case that causes: Font 'Lucida Sans Typewriter' does not support style 'Regular'
if (fontFamily != null)
{
DrawText(e.Graphics, FontFamily.GenericSansSerif, FontStyle.Regular, e.Bounds, fontFamily.Name);
}
}
}
// Uncomment this if you actually like the way the focus rectangle looks
//e.DrawFocusRectangle ();
}
/// <summary>
/// Helper method to draw the string
/// </summary>
/// <param name="graphics"></param>
/// <param name="fontFamily"></param>
/// <param name="fontStyle"></param>
/// <param name="bounds"></param>
/// <param name="text"></param>
private void DrawText(Graphics graphics, FontFamily fontFamily, FontStyle fontStyle, Rectangle bounds, string text)
{
using Font font = new Font(fontFamily, Font.Size + 5, fontStyle, GraphicsUnit.Pixel);
// Make sure the text is visible by centering it in the line
using StringFormat stringFormat = new StringFormat
{
LineAlignment = StringAlignment.Center
};
graphics.DrawString(text, font, Brushes.Black, bounds, stringFormat);
}
private void BindableToolStripComboBox_SelectedIndexChanged(object sender, EventArgs e) {
if (PropertyChanged == null) return;
PropertyChanged(this, new PropertyChangedEventArgs("Text"));
PropertyChanged(this, new PropertyChangedEventArgs("FontFamily"));
PropertyChanged(this, new PropertyChangedEventArgs("SelectedIndex"));
PropertyChanged(this, new PropertyChangedEventArgs("SelectedItem"));
}
}
}

View file

@ -1,62 +0,0 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
using System;
using System.Windows.Forms;
using GreenshotPlugin.UnmanagedHelpers.Enums;
namespace Greenshot.Controls {
/// <summary>
/// This is an extension of the default MenuStrip and allows us to click it even when the form doesn't have focus.
/// See: http://blogs.msdn.com/b/rickbrew/archive/2006/01/09/511003.aspx
/// </summary>
public class MenuStripEx : MenuStrip {
private enum NativeConstants : uint {
MA_ACTIVATE = 1,
MA_ACTIVATEANDEAT = 2,
}
private bool _clickThrough;
/// <summary>
/// Gets or sets whether the ToolStripEx honors item clicks when its containing form does not have input focus.
/// </summary>
/// <remarks>
/// Default value is false, which is the same behavior provided by the base ToolStrip class.
/// </remarks>
public bool ClickThrough {
get {
return _clickThrough;
}
set {
_clickThrough = value;
}
}
protected override void WndProc(ref Message m) {
base.WndProc(ref m);
var windowsMessage = (WindowsMessages) m.Msg;
if (_clickThrough && windowsMessage == WindowsMessages.WM_MOUSEACTIVATE && m.Result == (IntPtr)NativeConstants.MA_ACTIVATEANDEAT)
{
m.Result = (IntPtr)NativeConstants.MA_ACTIVATE;
}
}
}
}

View file

@ -1,174 +0,0 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
using System;
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;
using Greenshot.Forms;
using GreenshotPlugin.UnmanagedHelpers;
using GreenshotPlugin.UnmanagedHelpers.Enums;
using ColorDialog = Greenshot.Forms.ColorDialog;
namespace Greenshot.Controls {
/// <summary>
/// This code was supplied by Hi-Coder as a patch for Greenshot
/// Needed some modifications to be stable.
/// </summary>
public sealed class Pipette : Label, IMessageFilter, IDisposable {
private MovableShowColorForm _movableShowColorForm;
private bool _dragging;
private Cursor _cursor;
private readonly Bitmap _image;
private const int VkEsc = 27;
public event EventHandler<PipetteUsedArgs> PipetteUsed;
public Pipette() {
BorderStyle = BorderStyle.FixedSingle;
_dragging = false;
_image = (Bitmap)new ComponentResourceManager(typeof(ColorDialog)).GetObject("pipette.Image");
Image = _image;
_cursor = CreateCursor(_image, 1, 14);
_movableShowColorForm = new MovableShowColorForm();
Application.AddMessageFilter(this);
}
/// <summary>
/// Create a cursor from the supplied bitmap & hotspot coordinates
/// </summary>
/// <param name="bitmap">Bitmap to create an icon from</param>
/// <param name="hotspotX">Hotspot X coordinate</param>
/// <param name="hotspotY">Hotspot Y coordinate</param>
/// <returns>Cursor</returns>
private static Cursor CreateCursor(Bitmap bitmap, int hotspotX, int hotspotY)
{
using SafeIconHandle iconHandle = new SafeIconHandle( bitmap.GetHicon());
User32.GetIconInfo(iconHandle, out var iconInfo);
iconInfo.xHotspot = hotspotX;
iconInfo.yHotspot = hotspotY;
iconInfo.fIcon = false;
var icon = User32.CreateIconIndirect(ref iconInfo);
return new Cursor(icon);
}
/// <summary>
/// The bulk of the clean-up code is implemented in Dispose(bool)
/// </summary>
public new void Dispose() {
Dispose(true);
}
/// <summary>
/// This Dispose is called from the Dispose and the Destructor.
/// </summary>
/// <param name="disposing">When disposing==true all non-managed resources should be freed too!</param>
protected override void Dispose(bool disposing) {
if (disposing) {
if (_cursor != null) {
_cursor.Dispose();
}
_movableShowColorForm?.Dispose();
}
_movableShowColorForm = null;
_cursor = null;
base.Dispose(disposing);
}
/// <summary>
/// Handle the mouse down on the Pipette "label", we take the capture and move the zoomer to the current location
/// </summary>
/// <param name="e">MouseEventArgs</param>
protected override void OnMouseDown(MouseEventArgs e) {
if (e.Button == MouseButtons.Left) {
User32.SetCapture(Handle);
_movableShowColorForm.MoveTo(PointToScreen(new Point(e.X, e.Y)));
}
base.OnMouseDown(e);
}
/// <summary>
/// Handle the mouse up on the Pipette "label", we release the capture and fire the PipetteUsed event
/// </summary>
/// <param name="e">MouseEventArgs</param>
protected override void OnMouseUp(MouseEventArgs e) {
if (e.Button == MouseButtons.Left)
{
//Release Capture should consume MouseUp when canceled with the escape key
User32.ReleaseCapture();
PipetteUsed?.Invoke(this, new PipetteUsedArgs(_movableShowColorForm.color));
}
base.OnMouseUp(e);
}
/// <summary>
/// Handle the mouse Move event, we move the ColorUnderCursor to the current location.
/// </summary>
/// <param name="e">MouseEventArgs</param>
protected override void OnMouseMove(MouseEventArgs e) {
if (_dragging) {
//display the form on the right side of the cursor by default;
Point zp = PointToScreen(new Point(e.X, e.Y));
_movableShowColorForm.MoveTo(zp);
}
base.OnMouseMove(e);
}
/// <summary>
/// Handle the MouseCaptureChanged event
/// </summary>
/// <param name="e"></param>
protected override void OnMouseCaptureChanged(EventArgs e) {
if (Capture) {
_dragging = true;
Image = null;
Cursor c = _cursor;
Cursor = c;
_movableShowColorForm.Visible = true;
} else {
_dragging = false;
Image = _image;
Cursor = Cursors.Arrow;
_movableShowColorForm.Visible = false;
}
Update();
base.OnMouseCaptureChanged(e);
}
public bool PreFilterMessage(ref Message m) {
if (_dragging) {
if (m.Msg == (int)WindowsMessages.WM_CHAR) {
if ((int)m.WParam == VkEsc) {
User32.ReleaseCapture();
}
}
}
return false;
}
}
public class PipetteUsedArgs : EventArgs {
public Color Color;
public PipetteUsedArgs(Color c) {
Color = c;
}
}
}

View file

@ -1,88 +0,0 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
using System;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;
using GreenshotPlugin.Controls;
using ColorDialog = Greenshot.Forms.ColorDialog;
namespace Greenshot.Controls {
public class ToolStripColorButton : ToolStripButton, INotifyPropertyChanged, IGreenshotLanguageBindable {
public event PropertyChangedEventHandler PropertyChanged;
[Category("Greenshot"), DefaultValue(null), Description("Specifies key of the language file to use when displaying the text.")]
public string LanguageKey {
get;
set;
}
private Color _selectedColor = Color.Transparent;
public ToolStripColorButton() {
Click+= ColorButtonClick;
}
public Color SelectedColor {
get {return _selectedColor;}
set {
_selectedColor = value;
Brush brush;
if(value != Color.Transparent) {
brush = new SolidBrush(value);
} else {
brush = new HatchBrush(HatchStyle.Percent50, Color.White, Color.Gray);
}
if (Image != null)
{
using Graphics graphics = Graphics.FromImage(Image);
graphics.FillRectangle(brush, new Rectangle(0,13,16,3));
}
// cleanup GDI Object
brush.Dispose();
Invalidate();
}
}
private void ColorButtonClick(object sender, EventArgs e) {
var colorDialog = new ColorDialog
{
Color = SelectedColor
};
// Using the parent to make sure the dialog doesn't show on another window
colorDialog.ShowDialog(Parent.Parent);
if (colorDialog.DialogResult == DialogResult.Cancel)
{
return;
}
if (colorDialog.Color.Equals(SelectedColor))
{
return;
}
SelectedColor = colorDialog.Color;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("SelectedColor"));
}
}
}

View file

@ -1,62 +0,0 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
using System;
using System.Windows.Forms;
namespace Greenshot.Controls {
/// <summary>
/// This is an extension of the default ToolStrip and allows us to click it even when the form doesn't have focus.
/// See: http://blogs.msdn.com/b/rickbrew/archive/2006/01/09/511003.aspx
/// </summary>
internal class ToolStripEx : ToolStrip {
private const int WM_MOUSEACTIVATE = 0x21;
private enum NativeConstants : uint {
MA_ACTIVATE = 1,
MA_ACTIVATEANDEAT = 2,
}
private bool _clickThrough;
/// <summary>
/// Gets or sets whether the ToolStripEx honors item clicks when its containing form does not have input focus.
/// </summary>
/// <remarks>
/// Default value is false, which is the same behavior provided by the base ToolStrip class.
/// </remarks>
public bool ClickThrough {
get {
return _clickThrough;
}
set {
_clickThrough = value;
}
}
protected override void WndProc(ref Message m) {
base.WndProc(ref m);
if (_clickThrough && m.Msg == WM_MOUSEACTIVATE && m.Result == (IntPtr)NativeConstants.MA_ACTIVATEANDEAT) {
m.Result = (IntPtr)NativeConstants.MA_ACTIVATE;
}
}
}
}

View file

@ -1,79 +0,0 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
using System;
using System.ComponentModel;
using System.Windows.Forms;
using System.Windows.Forms.Design;
namespace Greenshot.Controls {
[ToolStripItemDesignerAvailability(ToolStripItemDesignerAvailability.ToolStrip | ToolStripItemDesignerAvailability.StatusStrip)]
public class ToolStripNumericUpDown : ToolStripControlHost, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public ToolStripNumericUpDown() : base(new NumericUpDown())
{
}
public NumericUpDown NumericUpDown => Control as NumericUpDown;
public decimal Value
{
get { return NumericUpDown.Value; }
set { NumericUpDown.Value = value;}
}
public decimal Minimum {
get { return NumericUpDown.Minimum; }
set { NumericUpDown.Minimum = value; }
}
public decimal Maximum {
get { return NumericUpDown.Maximum; }
set { NumericUpDown.Maximum = value; }
}
public decimal Increment {
get { return NumericUpDown.Increment; }
set { NumericUpDown.Increment = value; }
}
public int DecimalPlaces {
get { return NumericUpDown.DecimalPlaces; }
set { NumericUpDown.DecimalPlaces = value; }
}
protected override void OnSubscribeControlEvents(Control control) {
base.OnSubscribeControlEvents(control);
NumericUpDown.ValueChanged += _valueChanged;
}
protected override void OnUnsubscribeControlEvents(Control control) {
base.OnUnsubscribeControlEvents(control);
NumericUpDown.ValueChanged -= _valueChanged;
}
private void _valueChanged(object sender, EventArgs e)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Value"));
}
}
}

View file

@ -1,78 +0,0 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
using System;
using System.Drawing;
using System.Windows.Forms;
using Greenshot.Configuration;
using GreenshotPlugin.Core;
using GreenshotPlugin.Interfaces;
namespace Greenshot.Destinations {
/// <summary>
/// Description of ClipboardDestination.
/// </summary>
public class ClipboardDestination : AbstractDestination {
public const string DESIGNATION = "Clipboard";
public override string Designation {
get {
return DESIGNATION;
}
}
public override string Description {
get {
return Language.GetString(LangKey.settings_destination_clipboard);
}
}
public override int Priority {
get {
return 2;
}
}
public override Keys EditorShortcutKeys {
get {
return Keys.Control | Keys.Shift | Keys.C;
}
}
public override Image DisplayIcon {
get {
return GreenshotResources.GetImage("Clipboard.Image");
}
}
public override ExportInformation ExportCapture(bool manuallyInitiated, ISurface surface, ICaptureDetails captureDetails) {
ExportInformation exportInformation = new ExportInformation(Designation, Description);
try {
ClipboardHelper.SetClipboardData(surface);
exportInformation.ExportMade = true;
} catch (Exception) {
// TODO: Change to general logic in ProcessExport
surface.SendMessageEvent(this, SurfaceMessageTyp.Error, Language.GetString(LangKey.editor_clipboardfailed));
}
ProcessExport(exportInformation, surface);
return exportInformation;
}
}
}

View file

@ -1,125 +0,0 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
using System;
using System.Collections.Generic;
using System.Drawing;
using Greenshot.Configuration;
using Greenshot.Forms;
using GreenshotPlugin.Core;
using GreenshotPlugin.IniFile;
using GreenshotPlugin.Interfaces;
using GreenshotPlugin.Interfaces.Forms;
using log4net;
namespace Greenshot.Destinations {
/// <summary>
/// Description of EditorDestination.
/// </summary>
public class EditorDestination : AbstractDestination {
private static readonly ILog LOG = LogManager.GetLogger(typeof(EditorDestination));
private static readonly EditorConfiguration editorConfiguration = IniConfig.GetIniSection<EditorConfiguration>();
public const string DESIGNATION = "Editor";
private readonly IImageEditor editor;
private static readonly Image greenshotIcon = GreenshotResources.GetGreenshotIcon().ToBitmap();
public EditorDestination()
{
// Do not remove, is needed for the framework
}
public EditorDestination(IImageEditor editor) {
this.editor = editor;
}
public override string Designation => DESIGNATION;
public override string Description {
get {
if (editor == null) {
return Language.GetString(LangKey.settings_destination_editor);
}
return Language.GetString(LangKey.settings_destination_editor) + " - " + editor.CaptureDetails.Title;
}
}
public override int Priority => 1;
public override bool IsDynamic => true;
public override Image DisplayIcon => greenshotIcon;
public override IEnumerable<IDestination> DynamicDestinations() {
foreach (IImageEditor someEditor in ImageEditorForm.Editors) {
yield return new EditorDestination(someEditor);
}
}
public override ExportInformation ExportCapture(bool manuallyInitiated, ISurface surface, ICaptureDetails captureDetails) {
ExportInformation exportInformation = new ExportInformation(Designation, Description);
// Make sure we collect the garbage before opening the screenshot
GC.Collect();
GC.WaitForPendingFinalizers();
bool modified = surface.Modified;
if (editor == null) {
if (editorConfiguration.ReuseEditor) {
foreach(IImageEditor openedEditor in ImageEditorForm.Editors) {
if (openedEditor.Surface.Modified) continue;
openedEditor.Surface = surface;
exportInformation.ExportMade = true;
break;
}
}
if (!exportInformation.ExportMade) {
try {
ImageEditorForm editorForm = new ImageEditorForm(surface, !surface.Modified); // Output made??
if (!string.IsNullOrEmpty(captureDetails.Filename)) {
editorForm.SetImagePath(captureDetails.Filename);
}
editorForm.Show();
editorForm.Activate();
LOG.Debug("Finished opening Editor");
exportInformation.ExportMade = true;
} catch (Exception e) {
LOG.Error(e);
exportInformation.ErrorMessage = e.Message;
}
}
} else {
try {
using (Image image = surface.GetImageForExport()) {
editor.Surface.AddImageContainer(image, 10, 10);
}
exportInformation.ExportMade = true;
} catch (Exception e) {
LOG.Error(e);
exportInformation.ErrorMessage = e.Message;
}
}
ProcessExport(exportInformation, surface);
// Workaround for the modified flag when using the editor.
surface.Modified = modified;
return exportInformation;
}
}
}

View file

@ -1,88 +0,0 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
using System;
using System.Drawing;
using System.Windows.Forms;
using Greenshot.Configuration;
using Greenshot.Helpers;
using GreenshotPlugin.Core;
using GreenshotPlugin.Interfaces;
using Microsoft.Win32;
namespace Greenshot.Destinations {
/// <summary>
/// Description of EmailDestination.
/// </summary>
public class EmailDestination : AbstractDestination {
private static readonly Image MailIcon = GreenshotResources.GetImage("Email.Image");
private static bool _isActiveFlag;
private static string _mapiClient;
public const string DESIGNATION = "EMail";
static EmailDestination() {
// Logic to decide what email implementation we use
_mapiClient = RegistryHive.LocalMachine.ReadKey64Or32(@"Clients\Mail");
if (!string.IsNullOrEmpty(_mapiClient)) {
// Active as we have a MAPI client, can be disabled later
_isActiveFlag = true;
}
}
public override string Designation => DESIGNATION;
public override string Description {
get
{
// Make sure there is some kind of "mail" name
return _mapiClient ??= Language.GetString(LangKey.editor_email);
}
}
public override int Priority => 3;
public override bool IsActive {
get {
if (_isActiveFlag) {
// Disable if the office plugin is installed and the client is outlook
// TODO: Change this! It always creates an exception, as the plugin has not been loaded the type is not there :(
var outlookDestination = Type.GetType("GreenshotOfficePlugin.OutlookDestination,GreenshotOfficePlugin", false);
if (outlookDestination != null && _mapiClient.ToLower().Contains("microsoft outlook")) {
_isActiveFlag = false;
}
}
return base.IsActive && _isActiveFlag;
}
}
public override Keys EditorShortcutKeys => Keys.Control | Keys.E;
public override Image DisplayIcon => MailIcon;
public override ExportInformation ExportCapture(bool manuallyInitiated, ISurface surface, ICaptureDetails captureDetails) {
ExportInformation exportInformation = new ExportInformation(Designation, Description);
MapiMailMessage.SendImage(surface, captureDetails);
exportInformation.ExportMade = true;
ProcessExport(exportInformation, surface);
return exportInformation;
}
}
}

View file

@ -1,143 +0,0 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
using System;
using System.Drawing;
using System.IO;
using System.Windows.Forms;
using Greenshot.Configuration;
using Greenshot.Forms;
using GreenshotPlugin.Core;
using GreenshotPlugin.Controls;
using GreenshotPlugin.IniFile;
using GreenshotPlugin.Interfaces;
using GreenshotPlugin.Interfaces.Plugin;
using log4net;
namespace Greenshot.Destinations {
/// <summary>
/// Description of FileSaveAsDestination.
/// </summary>
public class FileDestination : AbstractDestination {
private static readonly ILog Log = LogManager.GetLogger(typeof(FileDestination));
private static readonly CoreConfiguration CoreConfig = IniConfig.GetIniSection<CoreConfiguration>();
public const string DESIGNATION = "FileNoDialog";
public override string Designation => DESIGNATION;
public override string Description => Language.GetString(LangKey.quicksettings_destination_file);
public override int Priority => 0;
public override Keys EditorShortcutKeys => Keys.Control | Keys.S;
public override Image DisplayIcon => GreenshotResources.GetImage("Save.Image");
public override ExportInformation ExportCapture(bool manuallyInitiated, ISurface surface, ICaptureDetails captureDetails) {
ExportInformation exportInformation = new ExportInformation(Designation, Description);
bool outputMade;
bool overwrite;
string fullPath;
// Get output settings from the configuration
SurfaceOutputSettings outputSettings = new SurfaceOutputSettings();
if (captureDetails?.Filename != null) {
// As we save a pre-selected file, allow to overwrite.
overwrite = true;
Log.InfoFormat("Using previous filename");
fullPath = captureDetails.Filename;
outputSettings.Format = ImageOutput.FormatForFilename(fullPath);
} else {
fullPath = CreateNewFilename(captureDetails);
// As we generate a file, the configuration tells us if we allow to overwrite
overwrite = CoreConfig.OutputFileAllowOverwrite;
}
if (CoreConfig.OutputFilePromptQuality) {
QualityDialog qualityDialog = new QualityDialog(outputSettings);
qualityDialog.ShowDialog();
}
// Catching any exception to prevent that the user can't write in the directory.
// This is done for e.g. bugs #2974608, #2963943, #2816163, #2795317, #2789218, #3004642
try {
ImageOutput.Save(surface, fullPath, overwrite, outputSettings, CoreConfig.OutputFileCopyPathToClipboard);
outputMade = true;
} catch (ArgumentException ex1) {
// Our generated filename exists, display 'save-as'
Log.InfoFormat("Not overwriting: {0}", ex1.Message);
// when we don't allow to overwrite present a new SaveWithDialog
fullPath = ImageOutput.SaveWithDialog(surface, captureDetails);
outputMade = fullPath != null;
} catch (Exception ex2) {
Log.Error("Error saving screenshot!", ex2);
// Show the problem
MessageBox.Show(Language.GetString(LangKey.error_save), Language.GetString(LangKey.error));
// when save failed we present a SaveWithDialog
fullPath = ImageOutput.SaveWithDialog(surface, captureDetails);
outputMade = fullPath != null;
}
// Don't overwrite filename if no output is made
if (outputMade) {
exportInformation.ExportMade = true;
exportInformation.Filepath = fullPath;
if (captureDetails != null)
{
captureDetails.Filename = fullPath;
}
CoreConfig.OutputFileAsFullpath = fullPath;
}
ProcessExport(exportInformation, surface);
return exportInformation;
}
private static string CreateNewFilename(ICaptureDetails captureDetails) {
string fullPath;
Log.InfoFormat("Creating new filename");
string pattern = CoreConfig.OutputFileFilenamePattern;
if (string.IsNullOrEmpty(pattern)) {
pattern = "greenshot ${capturetime}";
}
string filename = FilenameHelper.GetFilenameFromPattern(pattern, CoreConfig.OutputFileFormat, captureDetails);
CoreConfig.ValidateAndCorrectOutputFilePath();
string filepath = FilenameHelper.FillVariables(CoreConfig.OutputFilePath, false);
try {
fullPath = Path.Combine(filepath, filename);
} catch (ArgumentException) {
// configured filename or path not valid, show error message...
Log.InfoFormat("Generated path or filename not valid: {0}, {1}", filepath, filename);
MessageBox.Show(Language.GetString(LangKey.error_save_invalid_chars), Language.GetString(LangKey.error));
// ... lets get the pattern fixed....
var dialogResult = new SettingsForm().ShowDialog();
if (dialogResult == DialogResult.OK) {
// ... OK -> then try again:
fullPath = CreateNewFilename(captureDetails);
} else {
// ... cancelled.
fullPath = null;
}
}
return fullPath;
}
}
}

View file

@ -1,82 +0,0 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
using System.Drawing;
using System.Windows.Forms;
using Greenshot.Configuration;
using GreenshotPlugin.Core;
using GreenshotPlugin.IniFile;
using GreenshotPlugin.Interfaces;
namespace Greenshot.Destinations {
/// <summary>
/// Description of FileWithDialog.
/// </summary>
public class FileWithDialogDestination : AbstractDestination {
private static readonly CoreConfiguration conf = IniConfig.GetIniSection<CoreConfiguration>();
public const string DESIGNATION = "FileDialog";
public override string Designation {
get {
return DESIGNATION;
}
}
public override string Description {
get {
return Language.GetString(LangKey.settings_destination_fileas);
}
}
public override int Priority {
get {
return 0;
}
}
public override Keys EditorShortcutKeys {
get {
return Keys.Control | Keys.Shift | Keys.S;
}
}
public override Image DisplayIcon {
get {
return GreenshotResources.GetImage("Save.Image");
}
}
public override ExportInformation ExportCapture(bool manuallyInitiated, ISurface surface, ICaptureDetails captureDetails) {
ExportInformation exportInformation = new ExportInformation(Designation, Description);
// Bug #2918756 don't overwrite path if SaveWithDialog returns null!
var savedTo = ImageOutput.SaveWithDialog(surface, captureDetails);
if (savedTo != null) {
exportInformation.ExportMade = true;
exportInformation.Filepath = savedTo;
captureDetails.Filename = savedTo;
conf.OutputFileAsFullpath = savedTo;
}
ProcessExport(exportInformation, surface);
return exportInformation;
}
}
}

View file

@ -1,65 +0,0 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
using System.Collections.Generic;
using Greenshot.Configuration;
using GreenshotPlugin.Core;
using GreenshotPlugin.Interfaces;
namespace Greenshot.Destinations {
/// <summary>
/// The PickerDestination shows a context menu with all possible destinations, so the user can "pick" one
/// </summary>
public class PickerDestination : AbstractDestination {
public const string DESIGNATION = "Picker";
public override string Designation => DESIGNATION;
public override string Description => Language.GetString(LangKey.settings_destination_picker);
public override int Priority => 1;
/// <summary>
/// Export the capture with the destination picker
/// </summary>
/// <param name="manuallyInitiated">Did the user select this destination?</param>
/// <param name="surface">Surface to export</param>
/// <param name="captureDetails">Details of the capture</param>
/// <returns>true if export was made</returns>
public override ExportInformation ExportCapture(bool manuallyInitiated, ISurface surface, ICaptureDetails captureDetails) {
List<IDestination> destinations = new List<IDestination>();
foreach(var destination in SimpleServiceProvider.Current.GetAllInstances<IDestination>()) {
if ("Picker".Equals(destination.Designation)) {
continue;
}
if (!destination.IsActive) {
continue;
}
destinations.Add(destination);
}
// No Processing, this is done in the selected destination (if anything was selected)
return ShowPickerMenu(true, surface, captureDetails, destinations);
}
}
}

View file

@ -1,123 +0,0 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Printing;
using System.Windows.Forms;
using Greenshot.Configuration;
using GreenshotPlugin.Core;
using Greenshot.Helpers;
using GreenshotPlugin.Interfaces;
namespace Greenshot.Destinations {
/// <summary>
/// Description of PrinterDestination.
/// </summary>
public class PrinterDestination : AbstractDestination {
public const string DESIGNATION = "Printer";
private readonly string _printerName;
public PrinterDestination() {
}
public PrinterDestination(string printerName) {
_printerName = printerName;
}
public override string Designation => DESIGNATION;
public override string Description {
get {
if (_printerName != null) {
return Language.GetString(LangKey.settings_destination_printer) + " - " + _printerName;
}
return Language.GetString(LangKey.settings_destination_printer);
}
}
public override int Priority => 2;
public override Keys EditorShortcutKeys => Keys.Control | Keys.P;
public override Image DisplayIcon => GreenshotResources.GetImage("Printer.Image");
public override bool IsDynamic => true;
/// <summary>
/// Create destinations for all the installed printers
/// </summary>
/// <returns>IEnumerable of IDestination</returns>
public override IEnumerable<IDestination> DynamicDestinations() {
PrinterSettings settings = new PrinterSettings();
string defaultPrinter = settings.PrinterName;
List<string> printers = new List<string>();
foreach (string printer in PrinterSettings.InstalledPrinters) {
printers.Add(printer);
}
printers.Sort(delegate(string p1, string p2) {
if(defaultPrinter.Equals(p1)) {
return -1;
}
if(defaultPrinter.Equals(p2)) {
return 1;
}
return string.Compare(p1, p2, StringComparison.Ordinal);
});
foreach(string printer in printers) {
yield return new PrinterDestination(printer);
}
}
/// <summary>
/// Export the capture to the printer
/// </summary>
/// <param name="manuallyInitiated"></param>
/// <param name="surface"></param>
/// <param name="captureDetails"></param>
/// <returns>ExportInformation</returns>
public override ExportInformation ExportCapture(bool manuallyInitiated, ISurface surface, ICaptureDetails captureDetails) {
ExportInformation exportInformation = new ExportInformation(Designation, Description);
PrinterSettings printerSettings;
if (!string.IsNullOrEmpty(_printerName))
{
using PrintHelper printHelper = new PrintHelper(surface, captureDetails);
printerSettings = printHelper.PrintTo(_printerName);
} else if (!manuallyInitiated) {
PrinterSettings settings = new PrinterSettings();
using PrintHelper printHelper = new PrintHelper(surface, captureDetails);
printerSettings = printHelper.PrintTo(settings.PrinterName);
} else
{
using PrintHelper printHelper = new PrintHelper(surface, captureDetails);
printerSettings = printHelper.PrintWithDialog();
}
if (printerSettings != null) {
exportInformation.ExportMade = true;
}
ProcessExport(exportInformation, surface);
return exportInformation;
}
}
}

View file

@ -1,170 +0,0 @@
/*
* 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 Sourceforge: http://sourceforge.net/projects/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.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;
using GreenshotPlugin.Core;
using GreenshotPlugin.Interfaces.Drawing;
using GreenshotPlugin.Interfaces.Drawing.Adorners;
namespace Greenshot.Drawing.Adorners
{
public class AbstractAdorner : IAdorner
{
public virtual EditStatus EditStatus { get; protected set; } = EditStatus.IDLE;
private static readonly Size DefaultSize = new Size(6, 6);
protected Size _size;
public AbstractAdorner(IDrawableContainer owner)
{
_size = DpiHelper.ScaleWithDpi(DefaultSize, 0);
Owner = owner;
}
/// <summary>
/// Returns the cursor for when the mouse is over the adorner
/// </summary>
public virtual Cursor Cursor
{
get
{
return Cursors.SizeAll;
}
}
public virtual IDrawableContainer Owner
{
get;
set;
}
/// <summary>
/// Test if the point is inside the adorner
/// </summary>
/// <param name="point"></param>
/// <returns></returns>
public virtual bool HitTest(Point point)
{
Rectangle hitBounds = Bounds;
hitBounds.Inflate(3, 3);
return hitBounds.Contains(point);
}
/// <summary>
/// Handle the mouse down
/// </summary>
/// <param name="sender"></param>
/// <param name="mouseEventArgs"></param>
public virtual void MouseDown(object sender, MouseEventArgs mouseEventArgs)
{
}
/// <summary>
/// Handle the mouse move
/// </summary>
/// <param name="sender"></param>
/// <param name="mouseEventArgs"></param>
public virtual void MouseMove(object sender, MouseEventArgs mouseEventArgs)
{
}
/// <summary>
/// Handle the mouse up
/// </summary>
/// <param name="sender"></param>
/// <param name="mouseEventArgs"></param>
public virtual void MouseUp(object sender, MouseEventArgs mouseEventArgs)
{
EditStatus = EditStatus.IDLE;
}
/// <summary>
/// Return the location of the adorner
/// </summary>
public virtual Point Location
{
get;
set;
}
/// <summary>
/// Return the bounds of the Adorner
/// </summary>
public virtual Rectangle Bounds
{
get
{
Point location = Location;
return new Rectangle(location.X - (_size.Width / 2), location.Y - (_size.Height / 2), _size.Width, _size.Height);
}
}
/// <summary>
/// Return the bounds of the Adorner as displayed on the parent Surface
/// </summary>
protected virtual Rectangle BoundsOnSurface
{
get
{
Point displayLocation = Owner.Parent.ToSurfaceCoordinates(Location);
return new Rectangle(displayLocation.X - _size.Width / 2, displayLocation.Y - _size.Height / 2, _size.Width, _size.Height);
}
}
/// <summary>
/// The adorner is active if the edit status is not idle or undrawn
/// </summary>
public virtual bool IsActive
{
get
{
return EditStatus != EditStatus.IDLE && EditStatus != EditStatus.UNDRAWN;
}
}
/// <summary>
/// Adjust UI elements to the supplied DPI settings
/// </summary>
/// <param name="dpi">uint</param>
public void AdjustToDpi(uint dpi)
{
_size = DpiHelper.ScaleWithDpi(DefaultSize, dpi);
}
/// <summary>
/// Draw the adorner
/// </summary>
/// <param name="paintEventArgs">PaintEventArgs</param>
public virtual void Paint(PaintEventArgs paintEventArgs)
{
}
/// <summary>
/// We ignore the Transform, as the coordinates are directly bound to those of the owner
/// </summary>
/// <param name="matrix"></param>
public virtual void Transform(Matrix matrix)
{
}
}
}

View file

@ -1,162 +0,0 @@
/*
* 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 Sourceforge: http://sourceforge.net/projects/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 Greenshot.Helpers;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;
using GreenshotPlugin.Interfaces.Drawing;
namespace Greenshot.Drawing.Adorners
{
/// <summary>
/// This is the adorner for the line based containers
/// </summary>
public class MoveAdorner : AbstractAdorner
{
private Rectangle _boundsBeforeResize = Rectangle.Empty;
private RectangleF _boundsAfterResize = RectangleF.Empty;
public Positions Position { get; private set; }
public MoveAdorner(IDrawableContainer owner, Positions position) : base(owner)
{
Position = position;
}
/// <summary>
/// Returns the cursor for when the mouse is over the adorner
/// </summary>
public override Cursor Cursor => Cursors.SizeAll;
/// <summary>
/// Handle the mouse down
/// </summary>
/// <param name="sender"></param>
/// <param name="mouseEventArgs"></param>
public override void MouseDown(object sender, MouseEventArgs mouseEventArgs)
{
EditStatus = EditStatus.RESIZING;
_boundsBeforeResize = new Rectangle(Owner.Left, Owner.Top, Owner.Width, Owner.Height);
_boundsAfterResize = _boundsBeforeResize;
}
/// <summary>
/// Handle the mouse move
/// </summary>
/// <param name="sender"></param>
/// <param name="mouseEventArgs"></param>
public override void MouseMove(object sender, MouseEventArgs mouseEventArgs)
{
if (EditStatus != EditStatus.RESIZING)
{
return;
}
Owner.Invalidate();
Owner.MakeBoundsChangeUndoable(false);
// reset "workbench" rectangle to current bounds
_boundsAfterResize.X = _boundsBeforeResize.X;
_boundsAfterResize.Y = _boundsBeforeResize.Y;
_boundsAfterResize.Width = _boundsBeforeResize.Width;
_boundsAfterResize.Height = _boundsBeforeResize.Height;
// calculate scaled rectangle
ScaleHelper.Scale(ref _boundsAfterResize, Position, new PointF(mouseEventArgs.X, mouseEventArgs.Y), ScaleHelper.GetScaleOptions());
// apply scaled bounds to this DrawableContainer
Owner.ApplyBounds(_boundsAfterResize);
Owner.Invalidate();
}
/// <summary>
/// Return the location of the adorner
/// </summary>
public override Point Location {
get
{
int x = 0,y = 0;
switch (Position)
{
case Positions.TopLeft:
x = Owner.Left;
y = Owner.Top;
break;
case Positions.BottomLeft:
x = Owner.Left;
y = Owner.Top + Owner.Height;
break;
case Positions.MiddleLeft:
x = Owner.Left;
y = Owner.Top + (Owner.Height / 2);
break;
case Positions.TopCenter:
x = Owner.Left + (Owner.Width / 2);
y = Owner.Top;
break;
case Positions.BottomCenter:
x = Owner.Left + (Owner.Width / 2);
y = Owner.Top + Owner.Height;
break;
case Positions.TopRight:
x = Owner.Left + Owner.Width;
y = Owner.Top;
break;
case Positions.BottomRight:
x = Owner.Left + Owner.Width;
y = Owner.Top + Owner.Height;
break;
case Positions.MiddleRight:
x = Owner.Left + Owner.Width;
y = Owner.Top + (Owner.Height / 2);
break;
}
return new Point(x, y);
}
}
/// <summary>
/// Draw the adorner
/// </summary>
/// <param name="paintEventArgs">PaintEventArgs</param>
public override void Paint(PaintEventArgs paintEventArgs)
{
Graphics targetGraphics = paintEventArgs.Graphics;
var bounds = BoundsOnSurface;
GraphicsState state = targetGraphics.Save();
targetGraphics.CompositingMode = CompositingMode.SourceCopy;
try
{
targetGraphics.FillRectangle(Brushes.Black, bounds);
targetGraphics.DrawRectangle(new Pen(Brushes.White), bounds);
}
catch
{
// Ignore, BUG-2065
}
targetGraphics.Restore(state);
}
}
}

View file

@ -1,182 +0,0 @@
/*
* 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 Sourceforge: http://sourceforge.net/projects/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 Greenshot.Helpers;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;
using GreenshotPlugin.Interfaces.Drawing;
namespace Greenshot.Drawing.Adorners
{
/// <summary>
/// This is the default "legacy" gripper adorner, not the one used for the tail in the speech-bubble
/// </summary>
public class ResizeAdorner : AbstractAdorner
{
private Rectangle _boundsBeforeResize = Rectangle.Empty;
private RectangleF _boundsAfterResize = RectangleF.Empty;
public Positions Position { get; private set; }
public ResizeAdorner(IDrawableContainer owner, Positions position) : base(owner)
{
Position = position;
}
/// <summary>
/// Returns the cursor for when the mouse is over the adorner
/// </summary>
public override Cursor Cursor
{
get
{
bool isNotSwitched = Owner.Width >= 0;
if (Owner.Height < 0)
{
isNotSwitched = !isNotSwitched;
}
switch (Position)
{
case Positions.TopLeft:
case Positions.BottomRight:
return isNotSwitched ? Cursors.SizeNWSE : Cursors.SizeNESW;
case Positions.TopRight:
case Positions.BottomLeft:
return isNotSwitched ? Cursors.SizeNESW : Cursors.SizeNWSE;
case Positions.MiddleLeft:
case Positions.MiddleRight:
return Cursors.SizeWE;
case Positions.TopCenter:
case Positions.BottomCenter:
return Cursors.SizeNS;
default:
return Cursors.SizeAll;
}
}
}
/// <summary>
/// Handle the mouse down
/// </summary>
/// <param name="sender"></param>
/// <param name="mouseEventArgs"></param>
public override void MouseDown(object sender, MouseEventArgs mouseEventArgs)
{
EditStatus = EditStatus.RESIZING;
_boundsBeforeResize = new Rectangle(Owner.Left, Owner.Top, Owner.Width, Owner.Height);
_boundsAfterResize = _boundsBeforeResize;
}
/// <summary>
/// Handle the mouse move
/// </summary>
/// <param name="sender"></param>
/// <param name="mouseEventArgs"></param>
public override void MouseMove(object sender, MouseEventArgs mouseEventArgs)
{
if (EditStatus != EditStatus.RESIZING)
{
return;
}
Owner.Invalidate();
Owner.MakeBoundsChangeUndoable(false);
// reset "workbench" rectangle to current bounds
_boundsAfterResize.X = _boundsBeforeResize.X;
_boundsAfterResize.Y = _boundsBeforeResize.Y;
_boundsAfterResize.Width = _boundsBeforeResize.Width;
_boundsAfterResize.Height = _boundsBeforeResize.Height;
// calculate scaled rectangle
ScaleHelper.Scale(ref _boundsAfterResize, Position, new PointF(mouseEventArgs.X, mouseEventArgs.Y), ScaleHelper.GetScaleOptions());
// apply scaled bounds to this DrawableContainer
Owner.ApplyBounds(_boundsAfterResize);
Owner.Invalidate();
}
/// <summary>
/// Return the location of the adorner
/// </summary>
public override Point Location {
get
{
int x = 0,y = 0;
switch (Position)
{
case Positions.TopLeft:
x = Owner.Left;
y = Owner.Top;
break;
case Positions.BottomLeft:
x = Owner.Left;
y = Owner.Top + Owner.Height;
break;
case Positions.MiddleLeft:
x = Owner.Left;
y = Owner.Top + (Owner.Height / 2);
break;
case Positions.TopCenter:
x = Owner.Left + (Owner.Width / 2);
y = Owner.Top;
break;
case Positions.BottomCenter:
x = Owner.Left + (Owner.Width / 2);
y = Owner.Top + Owner.Height;
break;
case Positions.TopRight:
x = Owner.Left + Owner.Width;
y = Owner.Top;
break;
case Positions.BottomRight:
x = Owner.Left + Owner.Width;
y = Owner.Top + Owner.Height;
break;
case Positions.MiddleRight:
x = Owner.Left + Owner.Width;
y = Owner.Top + (Owner.Height / 2);
break;
}
return new Point(x, y);
}
}
/// <summary>
/// Draw the adorner
/// </summary>
/// <param name="paintEventArgs">PaintEventArgs</param>
public override void Paint(PaintEventArgs paintEventArgs)
{
Graphics targetGraphics = paintEventArgs.Graphics;
var bounds = BoundsOnSurface;
GraphicsState state = targetGraphics.Save();
targetGraphics.CompositingMode = CompositingMode.SourceCopy;
targetGraphics.FillRectangle(Brushes.Black, bounds);
targetGraphics.DrawRectangle(new Pen(Brushes.White), bounds);
targetGraphics.Restore(state);
}
}
}

View file

@ -1,119 +0,0 @@
/*
* 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 Sourceforge: http://sourceforge.net/projects/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.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;
using GreenshotPlugin.Interfaces.Drawing;
namespace Greenshot.Drawing.Adorners
{
/// <summary>
/// This implements the special "gripper" for the Speech-Bubble tail
/// </summary>
public class TargetAdorner : AbstractAdorner
{
public TargetAdorner(IDrawableContainer owner, Point location) : base(owner)
{
Location = location;
}
/// <summary>
/// Handle the mouse down
/// </summary>
/// <param name="sender"></param>
/// <param name="mouseEventArgs"></param>
public override void MouseDown(object sender, MouseEventArgs mouseEventArgs)
{
EditStatus = EditStatus.MOVING;
}
/// <summary>
/// Handle the mouse move
/// </summary>
/// <param name="sender"></param>
/// <param name="mouseEventArgs"></param>
public override void MouseMove(object sender, MouseEventArgs mouseEventArgs)
{
if (EditStatus != EditStatus.MOVING)
{
return;
}
Owner.Invalidate();
Point newGripperLocation = new Point(mouseEventArgs.X, mouseEventArgs.Y);
Rectangle imageBounds = new Rectangle(0, 0, Owner.Parent.Image.Width, Owner.Parent.Image.Height);
// Check if gripper inside the parent (surface), if not we need to move it inside
// This was made for BUG-1682
if (!imageBounds.Contains(newGripperLocation))
{
if (newGripperLocation.X > imageBounds.Right)
{
newGripperLocation.X = imageBounds.Right - 5;
}
if (newGripperLocation.X < imageBounds.Left)
{
newGripperLocation.X = imageBounds.Left;
}
if (newGripperLocation.Y > imageBounds.Bottom)
{
newGripperLocation.Y = imageBounds.Bottom - 5;
}
if (newGripperLocation.Y < imageBounds.Top)
{
newGripperLocation.Y = imageBounds.Top;
}
}
Location = newGripperLocation;
Owner.Invalidate();
}
/// <summary>
/// Draw the adorner
/// </summary>
/// <param name="paintEventArgs">PaintEventArgs</param>
public override void Paint(PaintEventArgs paintEventArgs)
{
Graphics targetGraphics = paintEventArgs.Graphics;
var bounds = BoundsOnSurface;
targetGraphics.FillRectangle(Brushes.Green, bounds);
targetGraphics.DrawRectangle(new Pen(Brushes.White), bounds);
}
/// <summary>
/// Made sure this adorner is transformed
/// </summary>
/// <param name="matrix">Matrix</param>
public override void Transform(Matrix matrix)
{
if (matrix == null)
{
return;
}
Point[] points = new[] { Location };
matrix.TransformPoints(points);
Location = points[0];
}
}
}

View file

@ -1,141 +0,0 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using Greenshot.Drawing.Fields;
using GreenshotPlugin.Interfaces.Drawing;
namespace Greenshot.Drawing {
/// <summary>
/// Description of LineContainer.
/// </summary>
[Serializable()]
public class ArrowContainer : LineContainer {
public enum ArrowHeadCombination { NONE, START_POINT, END_POINT, BOTH };
private static readonly AdjustableArrowCap ARROW_CAP = new AdjustableArrowCap(4, 6);
public ArrowContainer(Surface parent) : base(parent) {
}
/// <summary>
/// Do not use the base, just override so we have our own defaults
/// </summary>
protected override void InitializeFields() {
AddField(GetType(), FieldType.LINE_THICKNESS, 2);
AddField(GetType(), FieldType.ARROWHEADS, 2);
AddField(GetType(), FieldType.LINE_COLOR, Color.Red);
AddField(GetType(), FieldType.FILL_COLOR, Color.Transparent);
AddField(GetType(), FieldType.SHADOW, true);
AddField(GetType(), FieldType.ARROWHEADS, ArrowHeadCombination.END_POINT);
}
public override void Draw(Graphics graphics, RenderMode rm) {
int lineThickness = GetFieldValueAsInt(FieldType.LINE_THICKNESS);
bool shadow = GetFieldValueAsBool(FieldType.SHADOW);
if (lineThickness > 0 ) {
graphics.SmoothingMode = SmoothingMode.HighQuality;
graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
graphics.CompositingQuality = CompositingQuality.HighQuality;
graphics.PixelOffsetMode = PixelOffsetMode.None;
Color lineColor = GetFieldValueAsColor(FieldType.LINE_COLOR);
ArrowHeadCombination heads = (ArrowHeadCombination)GetFieldValue(FieldType.ARROWHEADS);
if (lineThickness > 0) {
if (shadow) {
//draw shadow first
int basealpha = 100;
int alpha = basealpha;
int steps = 5;
int currentStep = 1;
while (currentStep <= steps)
{
using Pen shadowCapPen = new Pen(Color.FromArgb(alpha, 100, 100, 100), lineThickness);
SetArrowHeads(heads, shadowCapPen);
graphics.DrawLine(shadowCapPen,
Left + currentStep,
Top + currentStep,
Left + currentStep + Width,
Top + currentStep + Height);
currentStep++;
alpha -= basealpha / steps;
}
}
using Pen pen = new Pen(lineColor, lineThickness);
SetArrowHeads(heads, pen);
graphics.DrawLine(pen, Left, Top, Left + Width, Top + Height);
}
}
}
private void SetArrowHeads(ArrowHeadCombination heads, Pen pen) {
if ( heads == ArrowHeadCombination.BOTH || heads == ArrowHeadCombination.START_POINT ) {
pen.CustomStartCap = ARROW_CAP;
}
if ( heads == ArrowHeadCombination.BOTH || heads == ArrowHeadCombination.END_POINT ) {
pen.CustomEndCap = ARROW_CAP;
}
}
public override Rectangle DrawingBounds {
get {
int lineThickness = GetFieldValueAsInt(FieldType.LINE_THICKNESS);
if (lineThickness > 0)
{
using Pen pen = new Pen(Color.White)
{
Width = lineThickness
};
SetArrowHeads((ArrowHeadCombination)GetFieldValue(FieldType.ARROWHEADS), pen);
using GraphicsPath path = new GraphicsPath();
path.AddLine(Left, Top, Left + Width, Top + Height);
using Matrix matrix = new Matrix();
Rectangle drawingBounds = Rectangle.Round(path.GetBounds(matrix, pen));
drawingBounds.Inflate(2, 2);
return drawingBounds;
}
return Rectangle.Empty;
}
}
public override bool ClickableAt(int x, int y) {
int lineThickness = GetFieldValueAsInt(FieldType.LINE_THICKNESS) + 10;
if (lineThickness > 0)
{
using Pen pen = new Pen(Color.White)
{
Width = lineThickness
};
SetArrowHeads((ArrowHeadCombination)GetFieldValue(FieldType.ARROWHEADS), pen);
using GraphicsPath path = new GraphicsPath();
path.AddLine(Left, Top, Left + Width, Top + Height);
return path.IsOutlineVisible(x, y, pen);
}
return false;
}
}
}

View file

@ -1,98 +0,0 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
using System.Drawing;
using System.Runtime.Serialization;
using Greenshot.Drawing.Fields;
using Greenshot.Helpers;
using GreenshotPlugin.Interfaces.Drawing;
namespace Greenshot.Drawing {
/// <summary>
/// Description of CropContainer.
/// </summary>
public class CropContainer : DrawableContainer {
public CropContainer(Surface parent) : base(parent) {
Init();
}
protected override void OnDeserialized(StreamingContext streamingContext)
{
base.OnDeserialized(streamingContext);
Init();
}
private void Init()
{
CreateDefaultAdorners();
}
protected override void InitializeFields() {
AddField(GetType(), FieldType.FLAGS, FieldFlag.CONFIRMABLE);
}
public override void Invalidate() {
_parent?.Invalidate();
}
/// <summary>
/// We need to override the DrawingBound, return a rectangle in the size of the image, to make sure this element is always draw
/// (we create a transparent brown over the complete picture)
/// </summary>
public override Rectangle DrawingBounds {
get
{
if (_parent?.Image is { } image) {
return new Rectangle(0, 0, image.Width, image.Height);
}
return Rectangle.Empty;
}
}
public override void Draw(Graphics g, RenderMode rm) {
if (_parent == null)
{
return;
}
using Brush cropBrush = new SolidBrush(Color.FromArgb(100, 150, 150, 100));
Rectangle cropRectangle = GuiRectangle.GetGuiRectangle(Left, Top, Width, Height);
Rectangle selectionRect = new Rectangle(cropRectangle.Left - 1, cropRectangle.Top - 1, cropRectangle.Width + 1, cropRectangle.Height + 1);
Size imageSize = _parent.Image.Size;
DrawSelectionBorder(g, selectionRect);
// top
g.FillRectangle(cropBrush, new Rectangle(0, 0, imageSize.Width, cropRectangle.Top));
// left
g.FillRectangle(cropBrush, new Rectangle(0, cropRectangle.Top, cropRectangle.Left, cropRectangle.Height));
// right
g.FillRectangle(cropBrush, new Rectangle(cropRectangle.Left + cropRectangle.Width, cropRectangle.Top, imageSize.Width - (cropRectangle.Left + cropRectangle.Width), cropRectangle.Height));
// bottom
g.FillRectangle(cropBrush, new Rectangle(0, cropRectangle.Top + cropRectangle.Height, imageSize.Width, imageSize.Height - (cropRectangle.Top + cropRectangle.Height)));
}
/// <summary>
/// No context menu for the CropContainer
/// </summary>
public override bool HasContextMenu => false;
}
}

View file

@ -1,110 +0,0 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
using System;
using System.Drawing;
using System.IO;
using System.Windows.Forms;
using System.Drawing.Drawing2D;
using log4net;
using System.Runtime.Serialization;
using GreenshotPlugin.Interfaces.Drawing;
namespace Greenshot.Drawing {
/// <summary>
/// Description of CursorContainer.
/// </summary>
[Serializable]
public class CursorContainer : DrawableContainer, ICursorContainer {
private static readonly ILog LOG = LogManager.GetLogger(typeof(CursorContainer));
protected Cursor cursor;
public CursorContainer(Surface parent) : base(parent) {
Init();
}
protected override void OnDeserialized(StreamingContext streamingContext)
{
base.OnDeserialized(streamingContext);
Init();
}
private void Init()
{
CreateDefaultAdorners();
}
public CursorContainer(Surface parent, string filename) : this(parent) {
Load(filename);
}
public Cursor Cursor {
set {
if (cursor != null) {
cursor.Dispose();
}
// Clone cursor (is this correct??)
cursor = new Cursor(value.CopyHandle());
Width = value.Size.Width;
Height = value.Size.Height;
}
get { return cursor; }
}
/// <summary>
/// This Dispose is called from the Dispose and the Destructor.
/// When disposing==true all non-managed resources should be freed too!
/// </summary>
/// <param name="disposing"></param>
protected override void Dispose(bool disposing) {
if (disposing) {
if (cursor != null) {
cursor.Dispose();
}
}
cursor = null;
base.Dispose(disposing);
}
public void Load(string filename) {
if (!File.Exists(filename)) {
return;
}
using Cursor fileCursor = new Cursor(filename);
Cursor = fileCursor;
LOG.Debug("Loaded file: " + filename + " with resolution: " + Height + "," + Width);
}
public override void Draw(Graphics graphics, RenderMode rm) {
if (cursor == null) {
return;
}
graphics.SmoothingMode = SmoothingMode.HighQuality;
graphics.InterpolationMode = InterpolationMode.NearestNeighbor;
graphics.CompositingQuality = CompositingQuality.Default;
graphics.PixelOffsetMode = PixelOffsetMode.None;
cursor.DrawStretched(graphics, Bounds);
}
public override Size DefaultSize => cursor?.Size ?? new Size(16, 16);
}
}

View file

@ -1,563 +0,0 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
using Greenshot.Configuration;
using Greenshot.Drawing.Adorners;
using Greenshot.Drawing.Fields;
using Greenshot.Drawing.Filters;
using Greenshot.Helpers;
using Greenshot.Memento;
using GreenshotPlugin.Interfaces.Drawing;
using log4net;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Linq;
using System.Runtime.Serialization;
using GreenshotPlugin.IniFile;
using GreenshotPlugin.Interfaces;
using GreenshotPlugin.Interfaces.Drawing.Adorners;
namespace Greenshot.Drawing
{
/// <summary>
/// represents a rectangle, ellipse, label or whatever. Can contain filters, too.
/// serializable for clipboard support
/// Subclasses should fulfill INotifyPropertyChanged contract, i.e. call
/// OnPropertyChanged whenever a public property has been changed.
/// </summary>
[Serializable]
public abstract class DrawableContainer : AbstractFieldHolderWithChildren, IDrawableContainer {
private static readonly ILog LOG = LogManager.GetLogger(typeof(DrawableContainer));
protected static readonly EditorConfiguration EditorConfig = IniConfig.GetIniSection<EditorConfiguration>();
private const int M11 = 0;
private const int M22 = 3;
[OnDeserialized]
private void OnDeserializedInit(StreamingContext context)
{
_adorners = new List<IAdorner>();
OnDeserialized(context);
}
/// <summary>
/// Override to implement your own deserialization logic, like initializing properties which are not serialized
/// </summary>
/// <param name="streamingContext"></param>
protected virtual void OnDeserialized(StreamingContext streamingContext)
{
}
protected EditStatus _defaultEditMode = EditStatus.DRAWING;
public EditStatus DefaultEditMode {
get {
return _defaultEditMode;
}
}
/// <summary>
/// The public accessible Dispose
/// Will call the GarbageCollector to SuppressFinalize, preventing being cleaned twice
/// </summary>
public void Dispose() {
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing) {
if (!disposing) {
return;
}
_parent?.FieldAggregator?.UnbindElement(this);
}
~DrawableContainer() {
Dispose(false);
}
[NonSerialized]
private PropertyChangedEventHandler _propertyChanged;
public event PropertyChangedEventHandler PropertyChanged {
add => _propertyChanged += value;
remove => _propertyChanged -= value;
}
public IList<IFilter> Filters {
get {
List<IFilter> ret = new List<IFilter>();
foreach(IFieldHolder c in Children) {
if (c is IFilter) {
ret.Add(c as IFilter);
}
}
return ret;
}
}
[NonSerialized]
internal Surface _parent;
public ISurface Parent {
get => _parent;
set => SwitchParent((Surface)value);
}
[NonSerialized]
private TargetAdorner _targetAdorner;
public TargetAdorner TargetAdorner => _targetAdorner;
[NonSerialized]
private bool _selected;
public bool Selected {
get => _selected;
set {
_selected = value;
OnPropertyChanged("Selected");
}
}
[NonSerialized]
private EditStatus _status = EditStatus.UNDRAWN;
public EditStatus Status {
get => _status;
set => _status = value;
}
private int left;
public int Left {
get => left;
set {
if (value == left) {
return;
}
left = value;
}
}
private int top;
public int Top {
get => top;
set {
if (value == top) {
return;
}
top = value;
}
}
private int width;
public int Width {
get => width;
set {
if (value == width) {
return;
}
width = value;
}
}
private int height;
public int Height {
get => height;
set {
if (value == height) {
return;
}
height = value;
}
}
public Point Location {
get => new Point(left, top);
set {
left = value.X;
top = value.Y;
}
}
public Size Size {
get => new Size(width, height);
set {
width = value.Width;
height = value.Height;
}
}
/// <summary>
/// List of available Adorners
/// </summary>
[NonSerialized]
private IList<IAdorner> _adorners = new List<IAdorner>();
public IList<IAdorner> Adorners => _adorners;
[NonSerialized]
// will store current bounds of this DrawableContainer before starting a resize
protected Rectangle _boundsBeforeResize = Rectangle.Empty;
[NonSerialized]
// "workbench" rectangle - used for calculating bounds during resizing (to be applied to this DrawableContainer afterwards)
protected RectangleF _boundsAfterResize = RectangleF.Empty;
public Rectangle Bounds {
get => GuiRectangle.GetGuiRectangle(Left, Top, Width, Height);
set {
Left = Round(value.Left);
Top = Round(value.Top);
Width = Round(value.Width);
Height = Round(value.Height);
}
}
public virtual void ApplyBounds(RectangleF newBounds) {
Left = Round(newBounds.Left);
Top = Round(newBounds.Top);
Width = Round(newBounds.Width);
Height = Round(newBounds.Height);
}
public DrawableContainer(Surface parent) {
InitializeFields();
_parent = parent;
}
public void Add(IFilter filter) {
AddChild(filter);
}
public void Remove(IFilter filter) {
RemoveChild(filter);
}
private static int Round(float f) {
if(float.IsPositiveInfinity(f) || f>int.MaxValue/2) return int.MaxValue/2;
if (float.IsNegativeInfinity(f) || f<int.MinValue/2) return int.MinValue/2;
return (int)Math.Round(f);
}
private bool accountForShadowChange;
public virtual Rectangle DrawingBounds {
get {
foreach(IFilter filter in Filters) {
if (filter.Invert) {
return new Rectangle(Point.Empty, _parent.Image.Size);
}
}
// Take a base safety margin
int lineThickness = 5;
// add adorner size
lineThickness += Adorners.Max(adorner => Math.Max(adorner.Bounds.Width, adorner.Bounds.Height));
if (HasField(FieldType.LINE_THICKNESS)) {
lineThickness += GetFieldValueAsInt(FieldType.LINE_THICKNESS);
}
int offset = lineThickness/2;
int shadow = 0;
if (accountForShadowChange || (HasField(FieldType.SHADOW) && GetFieldValueAsBool(FieldType.SHADOW))){
accountForShadowChange = false;
shadow += 10;
}
return new Rectangle(Bounds.Left-offset, Bounds.Top-offset, Bounds.Width+lineThickness+shadow, Bounds.Height+lineThickness+shadow);
}
}
public virtual void Invalidate() {
if (Status != EditStatus.UNDRAWN) {
_parent?.InvalidateElements(DrawingBounds);
}
}
public virtual bool InitContent() { return true; }
public virtual void OnDoubleClick() {}
/// <summary>
/// Initialize a target gripper
/// </summary>
protected void InitAdorner(Color gripperColor, Point location) {
_targetAdorner = new TargetAdorner(this, location);
Adorners.Add(_targetAdorner);
}
/// <summary>
/// Create the default adorners for a rectangle based container
/// </summary>
protected void CreateDefaultAdorners() {
if (Adorners.Count > 0)
{
LOG.Warn("Adorners are already defined!");
}
// Create the GripperAdorners
Adorners.Add(new ResizeAdorner(this, Positions.TopLeft));
Adorners.Add(new ResizeAdorner(this, Positions.TopCenter));
Adorners.Add(new ResizeAdorner(this, Positions.TopRight));
Adorners.Add(new ResizeAdorner(this, Positions.BottomLeft));
Adorners.Add(new ResizeAdorner(this, Positions.BottomCenter));
Adorners.Add(new ResizeAdorner(this, Positions.BottomRight));
Adorners.Add(new ResizeAdorner(this, Positions.MiddleLeft));
Adorners.Add(new ResizeAdorner(this, Positions.MiddleRight));
}
public bool hasFilters => Filters.Count > 0;
public abstract void Draw(Graphics graphics, RenderMode renderMode);
public virtual void DrawContent(Graphics graphics, Bitmap bmp, RenderMode renderMode, Rectangle clipRectangle) {
if (Children.Count > 0) {
if (Status != EditStatus.IDLE) {
DrawSelectionBorder(graphics, Bounds);
} else {
if (clipRectangle.Width != 0 && clipRectangle.Height != 0) {
foreach(IFilter filter in Filters) {
if (filter.Invert) {
filter.Apply(graphics, bmp, Bounds, renderMode);
} else {
Rectangle drawingRect = new Rectangle(Bounds.Location, Bounds.Size);
drawingRect.Intersect(clipRectangle);
if(filter is MagnifierFilter) {
// quick&dirty bugfix, because MagnifierFilter behaves differently when drawn only partially
// what we should actually do to resolve this is add a better magnifier which is not that special
filter.Apply(graphics, bmp, Bounds, renderMode);
} else {
filter.Apply(graphics, bmp, drawingRect, renderMode);
}
}
}
}
}
}
Draw(graphics, renderMode);
}
/// <summary>
/// Adjust UI elements to the supplied DPI settings
/// </summary>
/// <param name="dpi">uint with dpi value</param>
public void AdjustToDpi(uint dpi)
{
foreach(var adorner in Adorners)
{
adorner.AdjustToDpi(dpi);
}
}
public virtual bool Contains(int x, int y) {
return Bounds.Contains(x , y);
}
public virtual bool ClickableAt(int x, int y) {
Rectangle r = GuiRectangle.GetGuiRectangle(Left, Top, Width, Height);
r.Inflate(5, 5);
return r.Contains(x, y);
}
protected void DrawSelectionBorder(Graphics g, Rectangle rect)
{
using Pen pen = new Pen(Color.MediumSeaGreen)
{
DashPattern = new float[] { 1, 2 },
Width = 1
};
g.DrawRectangle(pen, rect);
}
public void ResizeTo(int width, int height, int anchorPosition) {
Width = width;
Height = height;
}
/// <summary>
/// Make a following bounds change on this drawablecontainer undoable!
/// </summary>
/// <param name="allowMerge">true means allow the moves to be merged</param>
public void MakeBoundsChangeUndoable(bool allowMerge) {
_parent.MakeUndoable(new DrawableContainerBoundsChangeMemento(this), allowMerge);
}
public void MoveBy(int dx, int dy) {
Left += dx;
Top += dy;
}
/// <summary>
/// A handler for the MouseDown, used if you don't want the surface to handle this for you
/// </summary>
/// <param name="x">current mouse x</param>
/// <param name="y">current mouse y</param>
/// <returns>true if the event is handled, false if the surface needs to handle it</returns>
public virtual bool HandleMouseDown(int x, int y) {
Left = _boundsBeforeResize.X = x;
Top = _boundsBeforeResize.Y = y;
return true;
}
/// <summary>
/// A handler for the MouseMove, used if you don't want the surface to handle this for you
/// </summary>
/// <param name="x">current mouse x</param>
/// <param name="y">current mouse y</param>
/// <returns>true if the event is handled, false if the surface needs to handle it</returns>
public virtual bool HandleMouseMove(int x, int y) {
Invalidate();
// reset "workrbench" rectangle to current bounds
_boundsAfterResize.X = _boundsBeforeResize.Left;
_boundsAfterResize.Y = _boundsBeforeResize.Top;
_boundsAfterResize.Width = x - _boundsAfterResize.Left;
_boundsAfterResize.Height = y - _boundsAfterResize.Top;
ScaleHelper.Scale(_boundsBeforeResize, x, y, ref _boundsAfterResize, GetAngleRoundProcessor());
// apply scaled bounds to this DrawableContainer
ApplyBounds(_boundsAfterResize);
Invalidate();
return true;
}
/// <summary>
/// A handler for the MouseUp
/// </summary>
/// <param name="x">current mouse x</param>
/// <param name="y">current mouse y</param>
public virtual void HandleMouseUp(int x, int y) {
}
protected virtual void SwitchParent(Surface newParent) {
if (newParent == Parent)
{
return;
}
_parent?.FieldAggregator?.UnbindElement(this);
_parent = newParent;
foreach(IFilter filter in Filters) {
filter.Parent = this;
}
}
protected void OnPropertyChanged(string propertyName) {
if (_propertyChanged != null) {
_propertyChanged(this, new PropertyChangedEventArgs(propertyName));
Invalidate();
}
}
/// <summary>
/// This method will be called before a field is changes.
/// Using this makes it possible to invalidate the object as is before changing.
/// </summary>
/// <param name="fieldToBeChanged">The field to be changed</param>
/// <param name="newValue">The new value</param>
public virtual void BeforeFieldChange(IField fieldToBeChanged, object newValue) {
_parent?.MakeUndoable(new ChangeFieldHolderMemento(this, fieldToBeChanged), true);
Invalidate();
}
/// <summary>
/// Handle the field changed event, this should invalidate the correct bounds (e.g. when shadow comes or goes more pixels!)
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
public void HandleFieldChanged(object sender, FieldChangedEventArgs e) {
LOG.DebugFormat("Field {0} changed", e.Field.FieldType);
if (Equals(e.Field.FieldType, FieldType.SHADOW)) {
accountForShadowChange = true;
}
}
/// <summary>
/// Retrieve the Y scale from the matrix
/// </summary>
/// <param name="matrix"></param>
/// <returns></returns>
public static float CalculateScaleY(Matrix matrix) {
return matrix.Elements[M22];
}
/// <summary>
/// Retrieve the X scale from the matrix
/// </summary>
/// <param name="matrix"></param>
/// <returns></returns>
public static float CalculateScaleX(Matrix matrix) {
return matrix.Elements[M11];
}
/// <summary>
/// Retrieve the rotation angle from the matrix
/// </summary>
/// <param name="matrix"></param>
/// <returns></returns>
public static int CalculateAngle(Matrix matrix) {
const int M11 = 0;
const int M21 = 2;
var radians = Math.Atan2(matrix.Elements[M21], matrix.Elements[M11]);
return (int)-Math.Round(radians * 180 / Math.PI);
}
/// <summary>
/// This method is called on a DrawableContainers when:
/// 1) The capture on the surface is modified in such a way, that the elements would not be placed correctly.
/// 2) Currently not implemented: an element needs to be moved, scaled or rotated.
/// This basis implementation makes sure the coordinates of the element, including the TargetGripper, is correctly rotated/scaled/translated.
/// But this implementation doesn't take care of any changes to the content!!
/// </summary>
/// <param name="matrix"></param>
public virtual void Transform(Matrix matrix) {
if (matrix == null) {
return;
}
Point topLeft = new Point(Left, Top);
Point bottomRight = new Point(Left + Width, Top + Height);
Point[] points = new[] { topLeft, bottomRight };
matrix.TransformPoints(points);
Left = points[0].X;
Top = points[0].Y;
Width = points[1].X - points[0].X;
Height = points[1].Y - points[0].Y;
}
protected virtual ScaleHelper.IDoubleProcessor GetAngleRoundProcessor() {
return ScaleHelper.ShapeAngleRoundBehavior.Instance;
}
public virtual bool HasContextMenu => true;
public virtual bool HasDefaultSize => false;
public virtual Size DefaultSize => throw new NotSupportedException("Object doesn't have a default size");
/// <summary>
/// Allows to override the initializing of the fields, so we can actually have our own defaults
/// </summary>
protected virtual void InitializeFields() {
}
}
}

View file

@ -1,620 +0,0 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
using Greenshot.Configuration;
using Greenshot.Memento;
using GreenshotPlugin.Core;
using GreenshotPlugin.Interfaces.Drawing;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Threading;
using System.Windows.Forms;
using Greenshot.Forms;
using GreenshotPlugin.Interfaces;
namespace Greenshot.Drawing {
/// <summary>
/// Dispatches most of a DrawableContainer's public properties and methods to a list of DrawableContainers.
/// </summary>
[Serializable]
public class DrawableContainerList : List<IDrawableContainer>, IDrawableContainerList
{
private static readonly ComponentResourceManager EditorFormResources = new ComponentResourceManager(typeof(ImageEditorForm));
public Guid ParentID {
get;
private set;
}
public DrawableContainerList() {
}
public DrawableContainerList(Guid parentId) {
ParentID = parentId;
}
public EditStatus Status {
get {
return this[Count-1].Status;
}
set {
foreach (var dc in this) {
dc.Status = value;
}
}
}
public List<IDrawableContainer> AsIDrawableContainerList() {
List<IDrawableContainer> interfaceList = new List<IDrawableContainer>();
foreach(IDrawableContainer container in this) {
interfaceList.Add(container);
}
return interfaceList;
}
/// <summary>
/// Gets or sets the selection status of the elements.
/// If several elements are in the list, true is only returned when all elements are selected.
/// </summary>
public bool Selected {
get {
bool ret = true;
foreach(var dc in this) {
ret &= dc.Selected;
}
return ret;
}
set {
foreach(var dc in this) {
dc.Selected = value;
}
}
}
/// <summary>
/// Gets or sets the parent control of the elements in the list.
/// If there are several elements, the parent control of the last added is returned.
/// </summary>
public ISurface Parent {
get {
if (Count > 0) {
return this[Count-1].Parent;
}
return null;
}
set {
ParentID = value?.ID ?? Guid.NewGuid();
foreach (var drawableContainer in this) {
var dc = (DrawableContainer) drawableContainer;
dc.Parent = value;
}
}
}
/// <summary>
/// Make a following bounds change on this containerlist undoable!
/// </summary>
/// <param name="allowMerge">true means allow the moves to be merged</param>
public void MakeBoundsChangeUndoable(bool allowMerge) {
if (Count > 0 && Parent != null)
{
var clone = new DrawableContainerList();
clone.AddRange(this);
Parent.MakeUndoable(new DrawableContainerBoundsChangeMemento(clone), allowMerge);
}
}
/// <summary>
/// Apply matrix to all elements
/// </summary>
public void Transform(Matrix matrix) {
// Track modifications
bool modified = false;
Invalidate();
foreach (var dc in this) {
dc.Transform(matrix);
modified = true;
}
// Invalidate after
Invalidate();
// If we moved something, tell the surface it's modified!
if (modified) {
Parent.Modified = true;
}
}
/// <summary>
/// Moves all elements in the list by the given amount of pixels.
/// </summary>
/// <param name="dx">pixels to move horizontally</param>
/// <param name="dy">pixels to move vertically</param>
public void MoveBy(int dx, int dy) {
// Track modifications
bool modified = false;
// Invalidate before moving, otherwise the old locations aren't refreshed
Invalidate();
foreach(var dc in this) {
dc.Left += dx;
dc.Top += dy;
modified = true;
}
// Invalidate after
Invalidate();
// If we moved something, tell the surface it's modified!
if (modified) {
Parent.Modified = true;
}
}
/// <summary>
/// Indicates whether on of the elements is clickable at the given location
/// </summary>
/// <param name="x">x coordinate to be checked</param>
/// <param name="y">y coordinate to be checked</param>
/// <returns>true if one of the elements in the list is clickable at the given location, false otherwise</returns>
public bool ClickableAt(int x, int y) {
bool ret = false;
foreach(var dc in this) {
ret |= dc.ClickableAt(x, y);
}
return ret;
}
/// <summary>
/// retrieves the topmost element being clickable at the given location
/// </summary>
/// <param name="x">x coordinate to be checked</param>
/// <param name="y">y coordinate to be checked</param>
/// <returns>the topmost element from the list being clickable at the given location, null if there is no clickable element</returns>
public IDrawableContainer ClickableElementAt(int x, int y) {
for (int i=Count-1; i>=0; i--) {
if (this[i].ClickableAt(x,y)) {
return this[i];
}
}
return null;
}
/// <summary>
/// Dispatches OnDoubleClick to all elements in the list.
/// </summary>
public void OnDoubleClick() {
foreach(var drawableContainer in this) {
var dc = (DrawableContainer) drawableContainer;
dc.OnDoubleClick();
}
}
/// <summary>
/// Check if there are any intersecting filters, if so we need to redraw more
/// </summary>
/// <param name="clipRectangle"></param>
/// <returns>true if an filter intersects</returns>
public bool HasIntersectingFilters(Rectangle clipRectangle) {
foreach(var dc in this) {
if (dc.DrawingBounds.IntersectsWith(clipRectangle) && dc.hasFilters && dc.Status == EditStatus.IDLE) {
return true;
}
}
return false;
}
/// <summary>
/// Check if any of the drawableContainers are inside the rectangle
/// </summary>
/// <param name="clipRectangle"></param>
/// <returns></returns>
public bool IntersectsWith(Rectangle clipRectangle) {
foreach(var dc in this) {
if (dc.DrawingBounds.IntersectsWith(clipRectangle)) {
return true;
}
}
return false;
}
/// <summary>
/// A rectangle containing DrawingBounds of all drawableContainers in this list,
/// or empty rectangle if nothing is there.
/// </summary>
public Rectangle DrawingBounds
{
get
{
if (Count == 0)
{
return Rectangle.Empty;
}
else
{
var result = this[0].DrawingBounds;
for (int i = 1; i < Count; i++)
{
result = Rectangle.Union(result, this[i].DrawingBounds);
}
return result;
}
}
}
/// <summary>
/// Triggers all elements in the list ot be redrawn.
/// </summary>
/// <param name="g">the to the bitmap related Graphics object</param>
/// <param name="bitmap">Bitmap to draw</param>
/// <param name="renderMode">the rendermode in which the element is to be drawn</param>
/// <param name="clipRectangle"></param>
public void Draw(Graphics g, Bitmap bitmap, RenderMode renderMode, Rectangle clipRectangle) {
if (Parent == null)
{
return;
}
foreach (var drawableContainer in this)
{
var dc = (DrawableContainer)drawableContainer;
if (dc.Parent == null)
{
continue;
}
if (dc.DrawingBounds.IntersectsWith(clipRectangle))
{
dc.DrawContent(g, bitmap, renderMode, clipRectangle);
}
}
}
/// <summary>
/// Pass the field changed event to all elements in the list
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
public void HandleFieldChangedEvent(object sender, FieldChangedEventArgs e) {
foreach(var drawableContainer in this) {
var dc = (DrawableContainer) drawableContainer;
dc.HandleFieldChanged(sender, e);
}
}
/// <summary>
/// Invalidate the bounds of all the DC's in this list
/// </summary>
public void Invalidate() {
if (Parent == null)
{
return;
}
Rectangle region = Rectangle.Empty;
foreach (var dc in this)
{
region = Rectangle.Union(region, dc.DrawingBounds);
}
Parent.InvalidateElements(region);
}
/// <summary>
/// Indicates whether the given list of elements can be pulled up,
/// i.e. whether there is at least one unselected element higher in hierarchy
/// </summary>
/// <param name="elements">list of elements to pull up</param>
/// <returns>true if the elements could be pulled up</returns>
public bool CanPullUp(IDrawableContainerList elements) {
if (elements.Count == 0 || elements.Count == Count) {
return false;
}
foreach(var element in elements) {
if (IndexOf(element) < Count - elements.Count) {
return true;
}
}
return false;
}
/// <summary>
/// Pulls one or several elements up one level in hierarchy (z-index).
/// </summary>
/// <param name="elements">list of elements to pull up</param>
public void PullElementsUp(IDrawableContainerList elements) {
for(int i=Count-1; i>=0; i--) {
var dc = this[i];
if (!elements.Contains(dc)) {
continue;
}
if (Count > i+1 && !elements.Contains(this[i+1])) {
SwapElements(i,i+1);
}
}
}
/// <summary>
/// Pulls one or several elements up to the topmost level(s) in hierarchy (z-index).
/// </summary>
/// <param name="elements">of elements to pull to top</param>
public void PullElementsToTop(IDrawableContainerList elements)
{
var dcs = ToArray();
foreach (var dc in dcs)
{
if (!elements.Contains(dc)) {
continue;
}
Remove(dc);
Add(dc);
Parent.Modified = true;
}
}
/// <summary>
/// Indicates whether the given list of elements can be pushed down,
/// i.e. whether there is at least one unselected element lower in hierarchy
/// </summary>
/// <param name="elements">list of elements to push down</param>
/// <returns>true if the elements could be pushed down</returns>
public bool CanPushDown(IDrawableContainerList elements) {
if (elements.Count == 0 || elements.Count == Count) {
return false;
}
foreach(var element in elements) {
if (IndexOf(element) >= elements.Count) {
return true;
}
}
return false;
}
/// <summary>
/// Pushes one or several elements down one level in hierarchy (z-index).
/// </summary>
/// <param name="elements">list of elements to push down</param>
public void PushElementsDown(IDrawableContainerList elements) {
for(int i=0; i<Count; i++) {
var dc = this[i];
if (!elements.Contains(dc)) {
continue;
}
if((i>0) && !elements.Contains(this[i-1])) {
SwapElements(i,i-1);
}
}
}
/// <summary>
/// Pushes one or several elements down to the bottommost level(s) in hierarchy (z-index).
/// </summary>
/// <param name="elements">of elements to push to bottom</param>
public void PushElementsToBottom(IDrawableContainerList elements) {
var dcs = ToArray();
for(int i=dcs.Length-1; i>=0; i--) {
var dc = dcs[i];
if (!elements.Contains(dc)) {
continue;
}
Remove(dc);
Insert(0, dc);
Parent.Modified = true;
}
}
/// <summary>
/// swaps two elements in hierarchy (z-index),
/// checks both indices to be in range
/// </summary>
/// <param name="index1">index of the 1st element</param>
/// <param name="index2">index of the 2nd element</param>
private void SwapElements(int index1, int index2) {
if (index1 < 0 || index1 >= Count || index2 < 0 || index2 >= Count || index1 == index2) {
return;
}
var dc = this[index1];
this[index1] = this[index2];
this[index2] = dc;
Parent.Modified = true;
}
/// <summary>
/// Add items to a context menu for the selected item
/// </summary>
/// <param name="menu"></param>
/// <param name="surface"></param>
public virtual void AddContextMenuItems(ContextMenuStrip menu, ISurface surface) {
bool push = surface.Elements.CanPushDown(this);
bool pull = surface.Elements.CanPullUp(this);
ToolStripMenuItem item;
// Pull "up"
if (pull) {
item = new ToolStripMenuItem(Language.GetString(LangKey.editor_uptotop));
item.Click += delegate {
surface.Elements.PullElementsToTop(this);
surface.Elements.Invalidate();
};
menu.Items.Add(item);
item = new ToolStripMenuItem(Language.GetString(LangKey.editor_uponelevel));
item.Click += delegate {
surface.Elements.PullElementsUp(this);
surface.Elements.Invalidate();
};
menu.Items.Add(item);
}
// Push "down"
if (push) {
item = new ToolStripMenuItem(Language.GetString(LangKey.editor_downtobottom));
item.Click += delegate {
surface.Elements.PushElementsToBottom(this);
surface.Elements.Invalidate();
};
menu.Items.Add(item);
item = new ToolStripMenuItem(Language.GetString(LangKey.editor_downonelevel));
item.Click += delegate {
surface.Elements.PushElementsDown(this);
surface.Elements.Invalidate();
};
menu.Items.Add(item);
}
// Duplicate
item = new ToolStripMenuItem(Language.GetString(LangKey.editor_duplicate));
item.Click += delegate {
IDrawableContainerList dcs = this.Clone();
dcs.Parent = surface;
dcs.MoveBy(10, 10);
surface.AddElements(dcs);
surface.DeselectAllElements();
surface.SelectElements(dcs);
};
menu.Items.Add(item);
// Copy
item = new ToolStripMenuItem(Language.GetString(LangKey.editor_copytoclipboard))
{
Image = (Image) EditorFormResources.GetObject("copyToolStripMenuItem.Image")
};
item.Click += delegate {
ClipboardHelper.SetClipboardData(typeof(IDrawableContainerList), this);
};
menu.Items.Add(item);
// Cut
item = new ToolStripMenuItem(Language.GetString(LangKey.editor_cuttoclipboard))
{
Image = (Image) EditorFormResources.GetObject("btnCut.Image")
};
item.Click += delegate {
ClipboardHelper.SetClipboardData(typeof(IDrawableContainerList), this);
surface.RemoveElements(this);
};
menu.Items.Add(item);
// Delete
item = new ToolStripMenuItem(Language.GetString(LangKey.editor_deleteelement))
{
Image = (Image)EditorFormResources.GetObject("removeObjectToolStripMenuItem.Image")
};
item.Click += delegate {
surface.RemoveElements(this);
};
menu.Items.Add(item);
// Reset
bool canReset = false;
foreach (var drawableContainer in this)
{
var container = (DrawableContainer)drawableContainer;
if (container.HasDefaultSize)
{
canReset = true;
}
}
if (canReset) {
item = new ToolStripMenuItem(Language.GetString(LangKey.editor_resetsize));
//item.Image = ((System.Drawing.Image)(editorFormResources.GetObject("removeObjectToolStripMenuItem.Image")));
item.Click += delegate {
MakeBoundsChangeUndoable(false);
foreach (var drawableContainer in this) {
var container = (DrawableContainer) drawableContainer;
if (!container.HasDefaultSize) {
continue;
}
Size defaultSize = container.DefaultSize;
container.MakeBoundsChangeUndoable(false);
container.Width = defaultSize.Width;
container.Height = defaultSize.Height;
}
surface.Invalidate();
};
menu.Items.Add(item);
}
}
public virtual void ShowContextMenu(MouseEventArgs e, ISurface iSurface)
{
if (!(iSurface is Surface surface))
{
return;
}
bool hasMenu = false;
foreach (var drawableContainer in this) {
var container = (DrawableContainer) drawableContainer;
if (!container.HasContextMenu) {
continue;
}
hasMenu = true;
break;
}
if (hasMenu) {
ContextMenuStrip menu = new ContextMenuStrip();
AddContextMenuItems(menu, surface);
if (menu.Items.Count > 0) {
menu.Show(surface, surface.ToSurfaceCoordinates(e.Location));
while (true) {
if (menu.Visible) {
Application.DoEvents();
Thread.Sleep(100);
} else {
menu.Dispose();
break;
}
}
}
}
}
private bool _disposedValue; // To detect redundant calls
protected virtual void Dispose(bool disposing)
{
if (!_disposedValue)
{
if (disposing)
{
foreach (var drawableContainer in this)
{
drawableContainer.Dispose();
}
}
_disposedValue = true;
}
}
// This code added to correctly implement the disposable pattern.
public void Dispose()
{
// Do not change this code. Put cleanup code in Dispose(bool disposing) above.
Dispose(true);
}
/// <summary>
/// Adjust UI elements to the supplied DPI settings
/// </summary>
/// <param name="dpi"></param>
public void AdjustToDpi(uint dpi)
{
foreach (var drawableContainer in this) {
drawableContainer.AdjustToDpi(dpi);
}
}
}
}

View file

@ -1,148 +0,0 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using Greenshot.Drawing.Fields;
using Greenshot.Helpers;
using GreenshotPlugin.Interfaces.Drawing;
namespace Greenshot.Drawing {
/// <summary>
/// Description of EllipseContainer.
/// </summary>
[Serializable()]
public class EllipseContainer : DrawableContainer {
public EllipseContainer(Surface parent) : base(parent) {
CreateDefaultAdorners();
}
protected override void InitializeFields() {
AddField(GetType(), FieldType.LINE_THICKNESS, 2);
AddField(GetType(), FieldType.LINE_COLOR, Color.Red);
AddField(GetType(), FieldType.FILL_COLOR, Color.Transparent);
AddField(GetType(), FieldType.SHADOW, true);
}
public override void Draw(Graphics graphics, RenderMode renderMode) {
graphics.SmoothingMode = SmoothingMode.HighQuality;
graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
graphics.CompositingQuality = CompositingQuality.HighQuality;
graphics.PixelOffsetMode = PixelOffsetMode.None;
int lineThickness = GetFieldValueAsInt(FieldType.LINE_THICKNESS);
Color lineColor = GetFieldValueAsColor(FieldType.LINE_COLOR);
Color fillColor = GetFieldValueAsColor(FieldType.FILL_COLOR);
bool shadow = GetFieldValueAsBool(FieldType.SHADOW);
Rectangle rect = GuiRectangle.GetGuiRectangle(Left, Top, Width, Height);
DrawEllipse(rect, graphics, renderMode, lineThickness, lineColor, fillColor, shadow);
}
/// <summary>
/// This allows another container to draw an ellipse
/// </summary>
/// <param name="rect"></param>
/// <param name="graphics"></param>
/// <param name="renderMode"></param>
/// <param name="lineThickness"></param>
/// <param name="lineColor"></param>
/// <param name="fillColor"></param>
/// <param name="shadow"></param>
public static void DrawEllipse(Rectangle rect, Graphics graphics, RenderMode renderMode, int lineThickness, Color lineColor, Color fillColor, bool shadow) {
bool lineVisible = lineThickness > 0 && Colors.IsVisible(lineColor);
// draw shadow before anything else
if (shadow && (lineVisible || Colors.IsVisible(fillColor))) {
int basealpha = 100;
int alpha = basealpha;
int steps = 5;
int currentStep = lineVisible ? 1 : 0;
while (currentStep <= steps)
{
using Pen shadowPen = new Pen(Color.FromArgb(alpha, 100, 100, 100))
{
Width = lineVisible ? lineThickness : 1
};
Rectangle shadowRect = GuiRectangle.GetGuiRectangle(rect.Left + currentStep, rect.Top + currentStep, rect.Width, rect.Height);
graphics.DrawEllipse(shadowPen, shadowRect);
currentStep++;
alpha -= basealpha / steps;
}
}
//draw the original shape
if (Colors.IsVisible(fillColor))
{
using Brush brush = new SolidBrush(fillColor);
graphics.FillEllipse(brush, rect);
}
if (lineVisible)
{
using Pen pen = new Pen(lineColor, lineThickness);
graphics.DrawEllipse(pen, rect);
}
}
public override bool Contains(int x, int y) {
return EllipseContains(this, x, y);
}
/// <summary>
/// Allow the code to be used externally
/// </summary>
/// <param name="caller"></param>
/// <param name="x"></param>
/// <param name="y"></param>
/// <returns></returns>
public static bool EllipseContains(DrawableContainer caller, int x, int y) {
double xDistanceFromCenter = x - (caller.Left + caller.Width / 2);
double yDistanceFromCenter = y - (caller.Top + caller.Height / 2);
// ellipse: x^2/a^2 + y^2/b^2 = 1
return Math.Pow(xDistanceFromCenter, 2) / Math.Pow(caller.Width / 2, 2) + Math.Pow(yDistanceFromCenter, 2) / Math.Pow(caller.Height / 2, 2) < 1;
}
public override bool ClickableAt(int x, int y) {
int lineThickness = GetFieldValueAsInt(FieldType.LINE_THICKNESS) + 10;
Color fillColor = GetFieldValueAsColor(FieldType.FILL_COLOR);
Rectangle rect = GuiRectangle.GetGuiRectangle(Left, Top, Width, Height);
return EllipseClickableAt(rect, lineThickness, fillColor, x, y);
}
public static bool EllipseClickableAt(Rectangle rect, int lineThickness, Color fillColor, int x, int y) {
// If we clicked inside the rectangle and it's visible we are clickable at.
if (!Color.Transparent.Equals(fillColor)) {
if (rect.Contains(x, y)) {
return true;
}
}
// check the rest of the lines
if (lineThickness > 0)
{
using Pen pen = new Pen(Color.White, lineThickness);
using GraphicsPath path = new GraphicsPath();
path.AddEllipse(rect);
return path.IsOutlineVisible(x, y, pen);
}
return false;
}
}
}

View file

@ -1,200 +0,0 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Runtime.Serialization;
using Greenshot.Configuration;
using GreenshotPlugin.IniFile;
using log4net;
using GreenshotPlugin.Interfaces.Drawing;
namespace Greenshot.Drawing.Fields
{
/// <summary>
/// Basic IFieldHolder implementation, providing access to a set of fields
/// </summary>
[Serializable]
public abstract class AbstractFieldHolder : IFieldHolder
{
private static readonly ILog LOG = LogManager.GetLogger(typeof(AbstractFieldHolder));
private static readonly EditorConfiguration EditorConfig = IniConfig.GetIniSection<EditorConfiguration>();
[NonSerialized]
private readonly IDictionary<IField, PropertyChangedEventHandler> _handlers = new Dictionary<IField, PropertyChangedEventHandler>();
/// <summary>
/// called when a field's value has changed
/// </summary>
[NonSerialized]
private FieldChangedEventHandler _fieldChanged;
public event FieldChangedEventHandler FieldChanged
{
add { _fieldChanged += value; }
remove { _fieldChanged -= value; }
}
// we keep two Collections of our fields, dictionary for quick access, list for serialization
// this allows us to use default serialization
[NonSerialized]
private IDictionary<IFieldType, IField> _fieldsByType = new Dictionary<IFieldType, IField>();
private readonly IList<IField> fields = new List<IField>();
[OnDeserialized]
private void OnDeserialized(StreamingContext context)
{
_fieldsByType = new Dictionary<IFieldType, IField>();
// listen to changing properties
foreach (var field in fields)
{
field.PropertyChanged += delegate {
_fieldChanged?.Invoke(this, new FieldChangedEventArgs(field));
};
_fieldsByType[field.FieldType] = field;
}
}
public void AddField(Type requestingType, IFieldType fieldType, object fieldValue)
{
AddField(EditorConfig.CreateField(requestingType, fieldType, fieldValue));
}
public virtual void AddField(IField field)
{
fields.Add(field);
if (_fieldsByType == null)
{
return;
}
if (_fieldsByType.ContainsKey(field.FieldType))
{
if (LOG.IsDebugEnabled)
{
LOG.DebugFormat("A field with of type '{0}' already exists in this {1}, will overwrite.", field.FieldType, GetType());
}
}
_fieldsByType[field.FieldType] = field;
_handlers[field] = (sender, args) =>
{
_fieldChanged?.Invoke(this, new FieldChangedEventArgs(field));
};
field.PropertyChanged += _handlers[field];
}
public void RemoveField(IField field)
{
fields.Remove(field);
_fieldsByType.Remove(field.FieldType);
field.PropertyChanged -= _handlers[field];
_handlers.Remove(field);
}
public IList<IField> GetFields()
{
return fields;
}
public IField GetField(IFieldType fieldType)
{
try
{
return _fieldsByType[fieldType];
}
catch (KeyNotFoundException e)
{
throw new ArgumentException("Field '" + fieldType + "' does not exist in " + GetType(), e);
}
}
public object GetFieldValue(IFieldType fieldType)
{
return GetField(fieldType)?.Value;
}
public string GetFieldValueAsString(IFieldType fieldType)
{
return Convert.ToString(GetFieldValue(fieldType));
}
public int GetFieldValueAsInt(IFieldType fieldType)
{
return Convert.ToInt32(GetFieldValue(fieldType));
}
public decimal GetFieldValueAsDecimal(IFieldType fieldType)
{
return Convert.ToDecimal(GetFieldValue(fieldType));
}
public double GetFieldValueAsDouble(IFieldType fieldType)
{
return Convert.ToDouble(GetFieldValue(fieldType));
}
public float GetFieldValueAsFloat(IFieldType fieldType)
{
return Convert.ToSingle(GetFieldValue(fieldType));
}
public bool GetFieldValueAsBool(IFieldType fieldType)
{
return Convert.ToBoolean(GetFieldValue(fieldType));
}
public Color GetFieldValueAsColor(IFieldType fieldType, Color defaultColor = default)
{
return (Color)(GetFieldValue(fieldType) ?? defaultColor);
}
public bool HasField(IFieldType fieldType)
{
return _fieldsByType.ContainsKey(fieldType);
}
public bool HasFieldValue(IFieldType fieldType)
{
return HasField(fieldType) && _fieldsByType[fieldType].HasValue;
}
public void SetFieldValue(IFieldType fieldType, object value)
{
try
{
_fieldsByType[fieldType].Value = value;
}
catch (KeyNotFoundException e)
{
throw new ArgumentException("Field '" + fieldType + "' does not exist in " + GetType(), e);
}
}
protected void OnFieldChanged(object sender, FieldChangedEventArgs e)
{
_fieldChanged?.Invoke(sender, e);
}
}
}

View file

@ -1,146 +0,0 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
using GreenshotPlugin.Interfaces.Drawing;
using System;
using System.Collections.Generic;
using System.Runtime.Serialization;
namespace Greenshot.Drawing.Fields
{
/// <summary>
/// Basic IFieldHolderWithChildren implementation. Similar to IFieldHolder,
/// but has a List of IFieldHolder for children.
/// Field values are passed to and from children as well.
/// </summary>
[Serializable]
public abstract class AbstractFieldHolderWithChildren : AbstractFieldHolder
{
[NonSerialized]
private readonly FieldChangedEventHandler _fieldChangedEventHandler;
[NonSerialized]
private EventHandler childrenChanged;
public event EventHandler ChildrenChanged
{
add { childrenChanged += value; }
remove { childrenChanged -= value; }
}
public IList<IFieldHolder> Children = new List<IFieldHolder>();
public AbstractFieldHolderWithChildren()
{
_fieldChangedEventHandler = OnFieldChanged;
}
[OnDeserialized()]
private void OnDeserialized(StreamingContext context)
{
// listen to changing properties
foreach (IFieldHolder fieldHolder in Children)
{
fieldHolder.FieldChanged += _fieldChangedEventHandler;
}
childrenChanged?.Invoke(this, EventArgs.Empty);
}
public void AddChild(IFieldHolder fieldHolder)
{
Children.Add(fieldHolder);
fieldHolder.FieldChanged += _fieldChangedEventHandler;
childrenChanged?.Invoke(this, EventArgs.Empty);
}
public void RemoveChild(IFieldHolder fieldHolder)
{
Children.Remove(fieldHolder);
fieldHolder.FieldChanged -= _fieldChangedEventHandler;
childrenChanged?.Invoke(this, EventArgs.Empty);
}
public new IList<IField> GetFields()
{
var ret = new List<IField>();
ret.AddRange(base.GetFields());
foreach (IFieldHolder fh in Children)
{
ret.AddRange(fh.GetFields());
}
return ret;
}
public new IField GetField(IFieldType fieldType)
{
IField ret = null;
if (base.HasField(fieldType))
{
ret = base.GetField(fieldType);
}
else
{
foreach (IFieldHolder fh in Children)
{
if (fh.HasField(fieldType))
{
ret = fh.GetField(fieldType);
break;
}
}
}
if (ret == null)
{
throw new ArgumentException("Field '" + fieldType + "' does not exist in " + GetType());
}
return ret;
}
public new bool HasField(IFieldType fieldType)
{
bool ret = base.HasField(fieldType);
if (!ret)
{
foreach (IFieldHolder fh in Children)
{
if (fh.HasField(fieldType))
{
ret = true;
break;
}
}
}
return ret;
}
public new bool HasFieldValue(IFieldType fieldType)
{
IField f = GetField(fieldType);
return f != null && f.HasValue;
}
public new void SetFieldValue(IFieldType fieldType, object value)
{
IField f = GetField(fieldType);
if (f != null) f.Value = value;
}
}
}

View file

@ -1,46 +0,0 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
using System;
namespace Greenshot.Drawing.Fields.Binding {
/// <summary>
/// Basic IBindingConverter implementation
/// </summary>
public abstract class AbstractBindingConverter<T1,T2> : IBindingConverter
{
public object convert(object o) {
if(o == null) {
return null;
}
if(o is T1) {
return convert((T1)o);
}
if(o is T2) {
return convert((T2)o);
}
throw new ArgumentException("Cannot handle argument of type "+o.GetType());
}
protected abstract T2 convert(T1 o);
protected abstract T1 convert(T2 o);
}
}

View file

@ -1,158 +0,0 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
using System;
using System.ComponentModel;
using System.Reflection;
namespace Greenshot.Drawing.Fields.Binding {
/// <summary>
/// Bidirectional binding of properties of two INotifyPropertyChanged instances.
/// This implementation synchronizes null values, too. If you do not want this
/// behavior (e.g. when binding to a
/// </summary>
public class BidirectionalBinding {
private readonly INotifyPropertyChanged _controlObject;
private readonly INotifyPropertyChanged _fieldObject;
private readonly string _controlPropertyName;
private readonly string _fieldPropertyName;
private bool _updatingControl;
private bool _updatingField;
private IBindingConverter _converter;
private readonly IBindingValidator _validator;
/// <summary>
/// Whether or not null values are passed on to the other object.
/// </summary>
protected bool AllowSynchronizeNull = true;
/// <summary>
/// Bind properties of two objects bidirectionally
/// </summary>
/// <param name="controlObject">Object containing 1st property to bind</param>
/// <param name="controlPropertyName">Property of 1st object to bind</param>
/// <param name="fieldObject">Object containing 2nd property to bind</param>
/// <param name="fieldPropertyName">Property of 2nd object to bind</param>
public BidirectionalBinding(INotifyPropertyChanged controlObject, string controlPropertyName, INotifyPropertyChanged fieldObject, string fieldPropertyName) {
_controlObject = controlObject;
_fieldObject = fieldObject;
_controlPropertyName = controlPropertyName;
_fieldPropertyName = fieldPropertyName;
_controlObject.PropertyChanged += ControlPropertyChanged;
_fieldObject.PropertyChanged += FieldPropertyChanged;
}
/// <summary>
/// Bind properties of two objects bidirectionally, converting the values using a converter
/// </summary>
/// <param name="controlObject">Object containing 1st property to bind</param>
/// <param name="controlPropertyName">Property of 1st object to bind</param>
/// <param name="fieldObject">Object containing 2nd property to bind</param>
/// <param name="fieldPropertyName">Property of 2nd object to bind</param>
/// <param name="converter">taking care of converting the synchronized value to the correct target format and back</param>
public BidirectionalBinding(INotifyPropertyChanged controlObject, string controlPropertyName, INotifyPropertyChanged fieldObject, string fieldPropertyName, IBindingConverter converter) : this(controlObject, controlPropertyName, fieldObject, fieldPropertyName) {
_converter = converter;
}
/// <summary>
/// Bind properties of two objects bidirectionally, converting the values using a converter.
/// Synchronization can be intercepted by adding a validator.
/// </summary>
/// <param name="controlObject">Object containing 1st property to bind</param>
/// <param name="controlPropertyName">Property of 1st object to bind</param>
/// <param name="fieldObject">Object containing 2nd property to bind</param>
/// <param name="fieldPropertyName">Property of 2nd object to bind</param>
/// <param name="validator">validator to intercept synchronization if the value does not match certain criteria</param>
public BidirectionalBinding(INotifyPropertyChanged controlObject, string controlPropertyName, INotifyPropertyChanged fieldObject, string fieldPropertyName, IBindingValidator validator) : this(controlObject, controlPropertyName, fieldObject, fieldPropertyName) {
_validator = validator;
}
/// <summary>
/// Bind properties of two objects bidirectionally, converting the values using a converter.
/// Synchronization can be intercepted by adding a validator.
/// </summary>
/// <param name="controlObject">Object containing 1st property to bind</param>
/// <param name="controlPropertyName">Property of 1st object to bind</param>
/// <param name="fieldObject">Object containing 2nd property to bind</param>
/// <param name="fieldPropertyName">Property of 2nd object to bind</param>
/// <param name="converter">taking care of converting the synchronized value to the correct target format and back</param>
/// <param name="validator">validator to intercept synchronization if the value does not match certain criteria</param>
public BidirectionalBinding(INotifyPropertyChanged controlObject, string controlPropertyName, INotifyPropertyChanged fieldObject, string fieldPropertyName, IBindingConverter converter, IBindingValidator validator) : this(controlObject, controlPropertyName, fieldObject, fieldPropertyName, converter) {
_validator = validator;
}
public void ControlPropertyChanged(object sender, PropertyChangedEventArgs e) {
if (!_updatingControl && e.PropertyName.Equals(_controlPropertyName)) {
_updatingField = true;
Synchronize(_controlObject, _controlPropertyName, _fieldObject, _fieldPropertyName);
_updatingField = false;
}
}
public void FieldPropertyChanged(object sender, PropertyChangedEventArgs e) {
if (!_updatingField && e.PropertyName.Equals(_fieldPropertyName)) {
_updatingControl = true;
Synchronize(_fieldObject, _fieldPropertyName, _controlObject, _controlPropertyName);
_updatingControl = false;
}
}
private void Synchronize(INotifyPropertyChanged sourceObject, string sourceProperty, INotifyPropertyChanged targetObject, string targetProperty) {
PropertyInfo targetPropertyInfo = ResolvePropertyInfo(targetObject, targetProperty);
PropertyInfo sourcePropertyInfo = ResolvePropertyInfo(sourceObject, sourceProperty);
if (sourcePropertyInfo != null && targetPropertyInfo != null && targetPropertyInfo.CanWrite) {
object bValue = sourcePropertyInfo.GetValue(sourceObject, null);
if (_converter != null && bValue != null) {
bValue = _converter.convert(bValue);
}
try {
if (_validator == null || _validator.validate(bValue)) {
targetPropertyInfo.SetValue(targetObject, bValue, null);
}
} catch (Exception e) {
throw new MemberAccessException("Could not set property '"+targetProperty+"' to '"+bValue+"' ["+(bValue?.GetType().Name ?? string.Empty)+"] on "+targetObject+". Probably other type than expected, IBindingCoverter to the rescue.", e);
}
}
}
private static PropertyInfo ResolvePropertyInfo(object obj, string property) {
PropertyInfo ret = null;
string[] properties = property.Split(".".ToCharArray());
for(int i=0; i<properties.Length; i++) {
string prop = properties[i];
ret = obj.GetType().GetProperty(prop);
if(ret != null && ret.CanRead && i<prop.Length-1) {
obj = ret.GetValue(obj, null);
}
}
return ret;
}
public IBindingConverter Converter {
get { return _converter; }
set { _converter = value; }
}
}
}

View file

@ -1,47 +0,0 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
using System;
namespace Greenshot.Drawing.Fields.Binding {
/// <summary>
/// Converts decimal to float and vice versa.
/// </summary>
public class DecimalFloatConverter : AbstractBindingConverter<float, decimal>
{
private static DecimalFloatConverter _uniqueInstance;
private DecimalFloatConverter() {}
protected override decimal convert(float o) {
return Convert.ToDecimal(o);
}
protected override float convert(decimal o) {
return Convert.ToSingle(o);
}
public static DecimalFloatConverter GetInstance()
{
return _uniqueInstance ??= new DecimalFloatConverter();
}
}
}

View file

@ -1,47 +0,0 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
using System;
namespace Greenshot.Drawing.Fields.Binding {
/// <summary>
/// Converts decimal to int and vice versa.
/// </summary>
public class DecimalIntConverter : AbstractBindingConverter<int, decimal>
{
private static DecimalIntConverter _uniqueInstance;
private DecimalIntConverter() {}
protected override decimal convert(int o) {
return Convert.ToDecimal(o);
}
protected override int convert(decimal o) {
return Convert.ToInt32(o);
}
public static DecimalIntConverter GetInstance()
{
return _uniqueInstance ??= new DecimalIntConverter();
}
}
}

View file

@ -1,34 +0,0 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
namespace Greenshot.Drawing.Fields.Binding {
/// <summary>
/// Interface for a bidirectional validator, for use with BidirectionalBinding.
/// Useful if you do not want to synchronize values which would be illegal on
/// one of the bound objects (e.g. null value on some form components)
/// see NotNullValidator
/// </summary>
public interface IBindingValidator {
bool validate(object o);
}
}

View file

@ -1,130 +0,0 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
using GreenshotPlugin.Interfaces.Drawing;
using System;
using System.ComponentModel;
namespace Greenshot.Drawing.Fields
{
/// <summary>
/// Represents a single field of a drawable element, i.e.
/// line thickness of a rectangle.
/// </summary>
[Serializable]
public class Field : IField
{
[field: NonSerialized]
public event PropertyChangedEventHandler PropertyChanged;
private object _myValue;
public object Value
{
get
{
return _myValue;
}
set
{
if (!Equals(_myValue, value))
{
_myValue = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Value"));
}
}
}
public IFieldType FieldType
{
get;
set;
}
public string Scope
{
get;
set;
}
/// <summary>
/// Constructs a new Field instance, usually you should be using FieldFactory
/// to create Fields.
/// </summary>
/// <param name="fieldType">FieldType of the Field to be created</param>
/// <param name="scope">The scope to which the value of this Field is relevant.
/// Depending on the scope the Field's value may be shared for other elements
/// containing the same FieldType for defaulting to the last used value.
/// When scope is set to a Type (e.g. typeof(RectangleContainer)), its value
/// should not be reused for FieldHolders of another Type (e.g. typeof(EllipseContainer))
/// </param>
public Field(IFieldType fieldType, Type scope)
{
FieldType = fieldType;
Scope = scope.Name;
}
public Field(IFieldType fieldType, string scope)
{
FieldType = fieldType;
Scope = scope;
}
public Field(IFieldType fieldType)
{
FieldType = fieldType;
}
/// <summary>
/// Returns true if this field holds a value other than null.
/// </summary>
public bool HasValue => Value != null;
/// <summary>
/// Creates a flat clone of this Field. The fields value itself is not cloned.
/// </summary>
/// <returns></returns>
public Field Clone()
{
return new Field(FieldType, Scope) {Value = Value};
}
public override int GetHashCode()
{
int hashCode = 0;
unchecked
{
hashCode += 1000000009 * FieldType.GetHashCode();
if (Scope != null)
hashCode += 1000000021 * Scope.GetHashCode();
}
return hashCode;
}
public override bool Equals(object obj)
{
if (!(obj is Field other))
{
return false;
}
return FieldType == other.FieldType && Equals(Scope, other.Scope);
}
public override string ToString()
{
return string.Format("[Field FieldType={1} Value={0} Scope={2}]", _myValue, FieldType, Scope);
}
}
}

View file

@ -1,218 +0,0 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
using System;
using Greenshot.Configuration;
using GreenshotPlugin.Interfaces.Drawing;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using GreenshotPlugin.IniFile;
using GreenshotPlugin.Interfaces;
namespace Greenshot.Drawing.Fields
{
/// <summary>
/// Represents the current set of properties for the editor.
/// When one of EditorProperties' properties is updated, the change will be promoted
/// to all bound elements.
/// * If an element is selected:
/// This class represents the element's properties
/// * I n>1 elements are selected:
/// This class represents the properties of all elements.
/// Properties that do not apply for ALL selected elements are null (or 0 respectively)
/// If the property values of the selected elements differ, the value of the last bound element wins.
/// </summary>
[Serializable]
public sealed class FieldAggregator : AbstractFieldHolder
{
private readonly IDrawableContainerList _boundContainers;
private bool _internalUpdateRunning;
private static readonly EditorConfiguration EditorConfig = IniConfig.GetIniSection<EditorConfiguration>();
public FieldAggregator(ISurface parent)
{
foreach (var fieldType in FieldType.Values)
{
var field = new Field(fieldType, GetType());
AddField(field);
}
_boundContainers = new DrawableContainerList
{
Parent = parent
};
}
public override void AddField(IField field)
{
base.AddField(field);
field.PropertyChanged += OwnPropertyChanged;
}
public void BindElements(IDrawableContainerList dcs)
{
foreach (var dc in dcs)
{
BindElement(dc);
}
}
public void BindElement(IDrawableContainer dc)
{
if (!(dc is DrawableContainer container) || _boundContainers.Contains(container))
{
return;
}
_boundContainers.Add(container);
container.ChildrenChanged += delegate {
UpdateFromBoundElements();
};
UpdateFromBoundElements();
}
public void BindAndUpdateElement(IDrawableContainer dc)
{
UpdateElement(dc);
BindElement(dc);
}
public void UpdateElement(IDrawableContainer dc)
{
if (!(dc is DrawableContainer container))
{
return;
}
_internalUpdateRunning = true;
foreach (var field in GetFields())
{
if (container.HasField(field.FieldType) && field.HasValue)
{
//if(LOG.IsDebugEnabled) LOG.Debug(" "+field+ ": "+field.Value);
container.SetFieldValue(field.FieldType, field.Value);
}
}
_internalUpdateRunning = false;
}
public void UnbindElement(IDrawableContainer dc)
{
if (_boundContainers.Contains(dc))
{
_boundContainers.Remove(dc);
UpdateFromBoundElements();
}
}
public void Clear()
{
ClearFields();
_boundContainers.Clear();
UpdateFromBoundElements();
}
/// <summary>
/// sets all field values to null, however does not remove fields
/// </summary>
private void ClearFields()
{
_internalUpdateRunning = true;
foreach (var field in GetFields())
{
field.Value = null;
}
_internalUpdateRunning = false;
}
/// <summary>
/// Updates this instance using the respective fields from the bound elements.
/// Fields that do not apply to every bound element are set to null, or 0 respectively.
/// All other fields will be set to the field value of the least bound element.
/// </summary>
private void UpdateFromBoundElements()
{
ClearFields();
_internalUpdateRunning = true;
foreach (var field in FindCommonFields())
{
SetFieldValue(field.FieldType, field.Value);
}
_internalUpdateRunning = false;
}
private IList<IField> FindCommonFields()
{
IList<IField> returnFields = null;
if (_boundContainers.Count > 0)
{
// take all fields from the least selected container...
if (_boundContainers[_boundContainers.Count - 1] is DrawableContainer leastSelectedContainer)
{
returnFields = leastSelectedContainer.GetFields();
for (int i = 0; i < _boundContainers.Count - 1; i++)
{
if (!(_boundContainers[i] is DrawableContainer dc)) continue;
IList<IField> fieldsToRemove = new List<IField>();
foreach (IField field in returnFields)
{
// ... throw out those that do not apply to one of the other containers
if (!dc.HasField(field.FieldType))
{
fieldsToRemove.Add(field);
}
}
foreach (var field in fieldsToRemove)
{
returnFields.Remove(field);
}
}
}
}
return returnFields ?? new List<IField>();
}
public void OwnPropertyChanged(object sender, PropertyChangedEventArgs ea)
{
IField field = (IField)sender;
if (_internalUpdateRunning || field.Value == null)
{
return;
}
foreach (var drawableContainer1 in _boundContainers.ToList())
{
var drawableContainer = (DrawableContainer) drawableContainer1;
if (!drawableContainer.HasField(field.FieldType))
{
continue;
}
IField drawableContainerField = drawableContainer.GetField(field.FieldType);
// Notify before change, so we can e.g. invalidate the area
drawableContainer.BeforeFieldChange(drawableContainerField, field.Value);
drawableContainerField.Value = field.Value;
// update last used from DC field, so that scope is honored
EditorConfig.UpdateLastFieldValue(drawableContainerField);
}
}
}
}

View file

@ -1,123 +0,0 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
using GreenshotPlugin.Interfaces.Drawing;
using System;
namespace Greenshot.Drawing.Fields
{
/// <summary>
/// Defines all FieldTypes + their default value.
/// (The additional value is why this is not an enum)
/// </summary>
[Serializable]
public class FieldType : IFieldType
{
public static readonly IFieldType ARROWHEADS = new FieldType("ARROWHEADS");
public static readonly IFieldType BLUR_RADIUS = new FieldType("BLUR_RADIUS");
public static readonly IFieldType BRIGHTNESS = new FieldType("BRIGHTNESS");
public static readonly IFieldType FILL_COLOR = new FieldType("FILL_COLOR");
public static readonly IFieldType FONT_BOLD = new FieldType("FONT_BOLD");
public static readonly IFieldType FONT_FAMILY = new FieldType("FONT_FAMILY");
public static readonly IFieldType FONT_ITALIC = new FieldType("FONT_ITALIC");
public static readonly IFieldType FONT_SIZE = new FieldType("FONT_SIZE");
public static readonly IFieldType TEXT_HORIZONTAL_ALIGNMENT = new FieldType("TEXT_HORIZONTAL_ALIGNMENT");
public static readonly IFieldType TEXT_VERTICAL_ALIGNMENT = new FieldType("TEXT_VERTICAL_ALIGNMENT");
public static readonly IFieldType HIGHLIGHT_COLOR = new FieldType("HIGHLIGHT_COLOR");
public static readonly IFieldType LINE_COLOR = new FieldType("LINE_COLOR");
public static readonly IFieldType LINE_THICKNESS = new FieldType("LINE_THICKNESS");
public static readonly IFieldType MAGNIFICATION_FACTOR = new FieldType("MAGNIFICATION_FACTOR");
public static readonly IFieldType PIXEL_SIZE = new FieldType("PIXEL_SIZE");
public static readonly IFieldType PREVIEW_QUALITY = new FieldType("PREVIEW_QUALITY");
public static readonly IFieldType SHADOW = new FieldType("SHADOW");
public static readonly IFieldType PREPARED_FILTER_OBFUSCATE = new FieldType("PREPARED_FILTER_OBFUSCATE");
public static readonly IFieldType PREPARED_FILTER_HIGHLIGHT = new FieldType("PREPARED_FILTER_HIGHLIGHT");
public static readonly IFieldType FLAGS = new FieldType("FLAGS");
public static IFieldType[] Values = {
ARROWHEADS,
BLUR_RADIUS,
BRIGHTNESS,
FILL_COLOR,
FONT_BOLD,
FONT_FAMILY,
FONT_ITALIC,
FONT_SIZE,
TEXT_HORIZONTAL_ALIGNMENT,
TEXT_VERTICAL_ALIGNMENT,
HIGHLIGHT_COLOR,
LINE_COLOR,
LINE_THICKNESS,
MAGNIFICATION_FACTOR,
PIXEL_SIZE,
PREVIEW_QUALITY,
SHADOW,
PREPARED_FILTER_OBFUSCATE,
PREPARED_FILTER_HIGHLIGHT,
FLAGS
};
public string Name
{
get;
set;
}
private FieldType(string name)
{
Name = name;
}
public override string ToString()
{
return Name;
}
public override int GetHashCode()
{
int hashCode = 0;
unchecked
{
if (Name != null)
hashCode += 1000000009 * Name.GetHashCode();
}
return hashCode;
}
public override bool Equals(object obj)
{
FieldType other = obj as FieldType;
if (other == null)
{
return false;
}
return Equals(Name, other.Name);
}
public static bool operator ==(FieldType a, FieldType b)
{
return Equals(a, b);
}
public static bool operator !=(FieldType a, FieldType b)
{
return !Equals(a, b);
}
}
}

View file

@ -1,94 +0,0 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
using System;
using System.Drawing;
using Greenshot.Drawing.Fields;
using Greenshot.Helpers;
using System.Drawing.Drawing2D;
using System.Runtime.Serialization;
using GreenshotPlugin.Interfaces.Drawing;
namespace Greenshot.Drawing {
/// <summary>
/// empty container for filter-only elements
/// </summary>
[Serializable]
public abstract class FilterContainer : DrawableContainer {
public enum PreparedFilterMode {OBFUSCATE, HIGHLIGHT};
public enum PreparedFilter {BLUR, PIXELIZE, TEXT_HIGHTLIGHT, AREA_HIGHLIGHT, GRAYSCALE, MAGNIFICATION};
public FilterContainer(Surface parent) : base(parent) {
Init();
}
protected override void OnDeserialized(StreamingContext streamingContext)
{
base.OnDeserialized(streamingContext);
Init();
}
private void Init()
{
CreateDefaultAdorners();
}
protected override void InitializeFields() {
AddField(GetType(), FieldType.LINE_THICKNESS, 0);
AddField(GetType(), FieldType.LINE_COLOR, Color.Red);
AddField(GetType(), FieldType.SHADOW, false);
}
public override void Draw(Graphics graphics, RenderMode rm) {
int lineThickness = GetFieldValueAsInt(FieldType.LINE_THICKNESS);
Color lineColor = GetFieldValueAsColor(FieldType.LINE_COLOR);
bool shadow = GetFieldValueAsBool(FieldType.SHADOW);
bool lineVisible = lineThickness > 0 && Colors.IsVisible(lineColor);
if (lineVisible) {
graphics.SmoothingMode = SmoothingMode.HighSpeed;
graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
graphics.CompositingQuality = CompositingQuality.HighQuality;
graphics.PixelOffsetMode = PixelOffsetMode.None;
//draw shadow first
if (shadow) {
int basealpha = 100;
int alpha = basealpha;
int steps = 5;
int currentStep = lineVisible ? 1 : 0;
while (currentStep <= steps)
{
using Pen shadowPen = new Pen(Color.FromArgb(alpha, 100, 100, 100), lineThickness);
Rectangle shadowRect = GuiRectangle.GetGuiRectangle(Left + currentStep, Top + currentStep, Width, Height);
graphics.DrawRectangle(shadowPen, shadowRect);
currentStep++;
alpha -= basealpha / steps;
}
}
Rectangle rect = GuiRectangle.GetGuiRectangle(Left, Top, Width, Height);
if (lineThickness > 0)
{
using Pen pen = new Pen(lineColor, lineThickness);
graphics.DrawRectangle(pen, rect);
}
}
}
}
}

View file

@ -1,80 +0,0 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
using System;
using System.ComponentModel;
using System.Drawing;
using Greenshot.Drawing.Fields;
using GreenshotPlugin.Interfaces.Drawing;
namespace Greenshot.Drawing.Filters {
/// <summary>
/// Graphical filter which can be added to DrawableContainer.
/// Subclasses should fulfill INotifyPropertyChanged contract, i.e. call
/// OnPropertyChanged whenever a public property has been changed.
/// </summary>
[Serializable]
public abstract class AbstractFilter : AbstractFieldHolder, IFilter {
[NonSerialized]
private PropertyChangedEventHandler propertyChanged;
public event PropertyChangedEventHandler PropertyChanged {
add { propertyChanged += value; }
remove{ propertyChanged -= value; }
}
private bool invert;
public bool Invert {
get {
return invert;
}
set {
invert = value;
OnPropertyChanged("Invert");
}
}
protected DrawableContainer parent;
public DrawableContainer Parent {
get {
return parent;
}
set {
parent = value;
}
}
public AbstractFilter(DrawableContainer parent) {
this.parent = parent;
}
public DrawableContainer GetParent() {
return parent;
}
public abstract void Apply(Graphics graphics, Bitmap applyBitmap, Rectangle rect, RenderMode renderMode);
protected void OnPropertyChanged(string propertyName)
{
propertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}

View file

@ -1,66 +0,0 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
using System;
using System.Drawing;
using Greenshot.Drawing.Fields;
using GreenshotPlugin.Core;
using GreenshotPlugin.UnmanagedHelpers;
using System.Drawing.Drawing2D;
using GreenshotPlugin.Interfaces.Drawing;
namespace Greenshot.Drawing.Filters {
[Serializable]
public class BlurFilter : AbstractFilter {
public double previewQuality;
public double PreviewQuality {
get { return previewQuality; }
set { previewQuality = value; OnPropertyChanged("PreviewQuality"); }
}
public BlurFilter(DrawableContainer parent) : base(parent) {
AddField(GetType(), FieldType.BLUR_RADIUS, 3);
AddField(GetType(), FieldType.PREVIEW_QUALITY, 1.0d);
}
public override void Apply(Graphics graphics, Bitmap applyBitmap, Rectangle rect, RenderMode renderMode) {
int blurRadius = GetFieldValueAsInt(FieldType.BLUR_RADIUS);
Rectangle applyRect = ImageHelper.CreateIntersectRectangle(applyBitmap.Size, rect, Invert);
if (applyRect.Width == 0 || applyRect.Height == 0) {
return;
}
GraphicsState state = graphics.Save();
if (Invert) {
graphics.SetClip(applyRect);
graphics.ExcludeClip(rect);
}
if (GDIplus.IsBlurPossible(blurRadius)) {
GDIplus.DrawWithBlur(graphics, applyBitmap, applyRect, null, null, blurRadius, false);
} else
{
using IFastBitmap fastBitmap = FastBitmap.CreateCloneOf(applyBitmap, applyRect);
ImageHelper.ApplyBoxBlur(fastBitmap, blurRadius);
fastBitmap.DrawTo(graphics, applyRect);
}
graphics.Restore(state);
}
}
}

View file

@ -1,64 +0,0 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
using System;
using System.Drawing;
using Greenshot.Drawing.Fields;
using GreenshotPlugin.Core;
using System.Drawing.Imaging;
using System.Drawing.Drawing2D;
using GreenshotPlugin.Interfaces.Drawing;
namespace Greenshot.Drawing.Filters {
[Serializable()]
public class BrightnessFilter : AbstractFilter {
public BrightnessFilter(DrawableContainer parent) : base(parent) {
AddField(GetType(), FieldType.BRIGHTNESS, 0.9d);
}
/// <summary>
/// Implements the Apply code for the Brightness Filet
/// </summary>
/// <param name="graphics"></param>
/// <param name="applyBitmap"></param>
/// <param name="rect"></param>
/// <param name="renderMode"></param>
public override void Apply(Graphics graphics, Bitmap applyBitmap, Rectangle rect, RenderMode renderMode) {
Rectangle applyRect = ImageHelper.CreateIntersectRectangle(applyBitmap.Size, rect, Invert);
if (applyRect.Width == 0 || applyRect.Height == 0) {
// nothing to do
return;
}
GraphicsState state = graphics.Save();
if (Invert) {
graphics.SetClip(applyRect);
graphics.ExcludeClip(rect);
}
float brightness = GetFieldValueAsFloat(FieldType.BRIGHTNESS);
using (ImageAttributes ia = ImageHelper.CreateAdjustAttributes(brightness, 1f, 1f)) {
graphics.DrawImage(applyBitmap, applyRect, applyRect.X, applyRect.Y, applyRect.Width, applyRect.Height, GraphicsUnit.Pixel, ia);
}
graphics.Restore(state);
}
}
}

View file

@ -1,64 +0,0 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
using System;
using System.Drawing;
using GreenshotPlugin.Core;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using GreenshotPlugin.Interfaces.Drawing;
namespace Greenshot.Drawing.Filters {
/// <summary>
/// Description of GrayscaleFilter.
/// </summary>
[Serializable()]
public class GrayscaleFilter : AbstractFilter {
public GrayscaleFilter(DrawableContainer parent) : base(parent) {
}
public override void Apply(Graphics graphics, Bitmap applyBitmap, Rectangle rect, RenderMode renderMode) {
Rectangle applyRect = ImageHelper.CreateIntersectRectangle(applyBitmap.Size, rect, Invert);
if (applyRect.Width == 0 || applyRect.Height == 0) {
// nothing to do
return;
}
GraphicsState state = graphics.Save();
if (Invert) {
graphics.SetClip(applyRect);
graphics.ExcludeClip(rect);
}
ColorMatrix grayscaleMatrix = new ColorMatrix(new[] {
new[] {.3f, .3f, .3f, 0, 0},
new[] {.59f, .59f, .59f, 0, 0},
new[] {.11f, .11f, .11f, 0, 0},
new float[] {0, 0, 0, 1, 0},
new float[] {0, 0, 0, 0, 1}
});
using (ImageAttributes ia = new ImageAttributes()) {
ia.SetColorMatrix(grayscaleMatrix);
graphics.DrawImage(applyBitmap, applyRect, applyRect.X, applyRect.Y, applyRect.Width, applyRect.Height, GraphicsUnit.Pixel, ia);
}
graphics.Restore(state);
}
}
}

View file

@ -1,68 +0,0 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
using System;
using System.Drawing;
using Greenshot.Drawing.Fields;
using GreenshotPlugin.Core;
using System.Drawing.Drawing2D;
using GreenshotPlugin.Interfaces.Drawing;
namespace Greenshot.Drawing.Filters {
[Serializable()]
public class HighlightFilter : AbstractFilter {
public HighlightFilter(DrawableContainer parent) : base(parent) {
AddField(GetType(), FieldType.FILL_COLOR, Color.Yellow);
}
/// <summary>
/// Implements the Apply code for the Brightness Filet
/// </summary>
/// <param name="graphics"></param>
/// <param name="applyBitmap"></param>
/// <param name="rect"></param>
/// <param name="renderMode"></param>
public override void Apply(Graphics graphics, Bitmap applyBitmap, Rectangle rect, RenderMode renderMode) {
Rectangle applyRect = ImageHelper.CreateIntersectRectangle(applyBitmap.Size, rect, Invert);
if (applyRect.Width == 0 || applyRect.Height == 0) {
// nothing to do
return;
}
GraphicsState state = graphics.Save();
if (Invert) {
graphics.SetClip(applyRect);
graphics.ExcludeClip(rect);
}
using (IFastBitmap fastBitmap = FastBitmap.CreateCloneOf(applyBitmap, applyRect)) {
Color highlightColor = GetFieldValueAsColor(FieldType.FILL_COLOR);
for (int y = fastBitmap.Top; y < fastBitmap.Bottom; y++) {
for (int x = fastBitmap.Left; x < fastBitmap.Right; x++) {
Color color = fastBitmap.GetColorAt(x, y);
color = Color.FromArgb(color.A, Math.Min(highlightColor.R, color.R), Math.Min(highlightColor.G, color.G), Math.Min(highlightColor.B, color.B));
fastBitmap.SetColorAt(x, y, color);
}
}
fastBitmap.DrawTo(graphics, applyRect.Location);
}
graphics.Restore(state);
}
}
}

View file

@ -1,61 +0,0 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
using System;
using System.Drawing;
using Greenshot.Drawing.Fields;
using GreenshotPlugin.Core;
using System.Drawing.Drawing2D;
using GreenshotPlugin.Interfaces.Drawing;
namespace Greenshot.Drawing.Filters {
[Serializable]
public class MagnifierFilter : AbstractFilter {
public MagnifierFilter(DrawableContainer parent) : base(parent) {
AddField(GetType(), FieldType.MAGNIFICATION_FACTOR, 2);
}
public override void Apply(Graphics graphics, Bitmap applyBitmap, Rectangle rect, RenderMode renderMode) {
Rectangle applyRect = ImageHelper.CreateIntersectRectangle(applyBitmap.Size, rect, Invert);
if (applyRect.Width == 0 || applyRect.Height == 0) {
// nothing to do
return;
}
int magnificationFactor = GetFieldValueAsInt(FieldType.MAGNIFICATION_FACTOR);
GraphicsState state = graphics.Save();
if (Invert) {
graphics.SetClip(applyRect);
graphics.ExcludeClip(rect);
}
graphics.SmoothingMode = SmoothingMode.None;
graphics.InterpolationMode = InterpolationMode.NearestNeighbor;
graphics.CompositingQuality = CompositingQuality.HighQuality;
graphics.PixelOffsetMode = PixelOffsetMode.None;
int halfWidth = rect.Width / 2;
int halfHeight = rect.Height / 2;
int newWidth = rect.Width / magnificationFactor;
int newHeight = rect.Height / magnificationFactor;
Rectangle source = new Rectangle(rect.X + halfWidth - newWidth / 2, rect.Y + halfHeight - newHeight / 2, newWidth, newHeight);
graphics.DrawImage(applyBitmap, rect, source, GraphicsUnit.Pixel);
graphics.Restore(state);
}
}
}

View file

@ -1,285 +0,0 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
using Greenshot.Drawing.Fields;
using Greenshot.Helpers;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Runtime.Serialization;
using GreenshotPlugin.Interfaces.Drawing;
namespace Greenshot.Drawing {
/// <summary>
/// Description of PathContainer.
/// </summary>
[Serializable]
public class FreehandContainer : DrawableContainer {
private static readonly float [] PointOffset = {0.5f, 0.25f, 0.75f};
[NonSerialized]
private GraphicsPath freehandPath = new GraphicsPath();
private Rectangle myBounds = Rectangle.Empty;
private Point lastMouse = Point.Empty;
private readonly List<Point> capturePoints = new List<Point>();
private bool isRecalculated;
/// <summary>
/// Constructor
/// </summary>
public FreehandContainer(Surface parent) : base(parent) {
Width = parent.Image.Width;
Height = parent.Image.Height;
Top = 0;
Left = 0;
}
protected override void InitializeFields() {
AddField(GetType(), FieldType.LINE_THICKNESS, 3);
AddField(GetType(), FieldType.LINE_COLOR, Color.Red);
}
public override void Transform(Matrix matrix) {
Point[] points = capturePoints.ToArray();
matrix.TransformPoints(points);
capturePoints.Clear();
capturePoints.AddRange(points);
RecalculatePath();
}
protected override void OnDeserialized(StreamingContext context) {
RecalculatePath();
}
/// <summary>
/// This Dispose is called from the Dispose and the Destructor.
/// </summary>
/// <param name="disposing">When disposing==true all non-managed resources should be freed too!</param>
protected override void Dispose(bool disposing) {
base.Dispose(disposing);
if (disposing)
{
freehandPath?.Dispose();
}
freehandPath = null;
}
/// <summary>
/// Called from Surface (the parent) when the drawing begins (mouse-down)
/// </summary>
/// <returns>true if the surface doesn't need to handle the event</returns>
public override bool HandleMouseDown(int mouseX, int mouseY) {
lastMouse = new Point(mouseX, mouseY);
capturePoints.Add(lastMouse);
return true;
}
/// <summary>
/// Called from Surface (the parent) if a mouse move is made while drawing
/// </summary>
/// <returns>true if the surface doesn't need to handle the event</returns>
public override bool HandleMouseMove(int mouseX, int mouseY) {
Point previousPoint = capturePoints[capturePoints.Count-1];
if (GeometryHelper.Distance2D(previousPoint.X, previousPoint.Y, mouseX, mouseY) >= 2*EditorConfig.FreehandSensitivity) {
capturePoints.Add(new Point(mouseX, mouseY));
}
if (GeometryHelper.Distance2D(lastMouse.X, lastMouse.Y, mouseX, mouseY) < EditorConfig.FreehandSensitivity)
{
return true;
}
//path.AddCurve(new Point[]{lastMouse, new Point(mouseX, mouseY)});
lastMouse = new Point(mouseX, mouseY);
freehandPath.AddLine(lastMouse, new Point(mouseX, mouseY));
// Only re-calculate the bounds & redraw when we added something to the path
myBounds = Rectangle.Round(freehandPath.GetBounds());
Invalidate();
return true;
}
/// <summary>
/// Called when the surface finishes drawing the element
/// </summary>
public override void HandleMouseUp(int mouseX, int mouseY) {
// Make sure we don't loose the ending point
if (GeometryHelper.Distance2D(lastMouse.X, lastMouse.Y, mouseX, mouseY) >= EditorConfig.FreehandSensitivity) {
capturePoints.Add(new Point(mouseX, mouseY));
}
RecalculatePath();
}
/// <summary>
/// Here we recalculate the freehand path by smoothing out the lines with Beziers.
/// </summary>
private void RecalculatePath() {
// Store the previous path, to dispose it later when we are finished
var previousFreehandPath = freehandPath;
var newFreehandPath = new GraphicsPath();
// Here we can put some cleanup... like losing all the uninteresting points.
if (capturePoints.Count >= 3)
{
int index = 0;
while ((capturePoints.Count - 1) % 3 != 0)
{
// duplicate points, first at 50% than 25% than 75%
capturePoints.Insert((int)(capturePoints.Count * PointOffset[index]), capturePoints[(int)(capturePoints.Count * PointOffset[index++])]);
}
newFreehandPath.AddBeziers(capturePoints.ToArray());
}
else if (capturePoints.Count == 2)
{
newFreehandPath.AddLine(capturePoints[0], capturePoints[1]);
}
// Recalculate the bounds
myBounds = Rectangle.Round(newFreehandPath.GetBounds());
// assign
isRecalculated = true;
freehandPath = newFreehandPath;
// dispose previous
previousFreehandPath?.Dispose();
}
/// <summary>
/// Do the drawing of the freehand "stroke"
/// </summary>
/// <param name="graphics"></param>
/// <param name="renderMode"></param>
public override void Draw(Graphics graphics, RenderMode renderMode) {
graphics.SmoothingMode = SmoothingMode.HighQuality;
graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
graphics.CompositingQuality = CompositingQuality.HighQuality;
graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
int lineThickness = GetFieldValueAsInt(FieldType.LINE_THICKNESS);
Color lineColor = GetFieldValueAsColor(FieldType.LINE_COLOR);
using var pen = new Pen(lineColor)
{
Width = lineThickness
};
if (!(pen.Width > 0))
{
return;
}
// Make sure the lines are nicely rounded
pen.EndCap = LineCap.Round;
pen.StartCap = LineCap.Round;
pen.LineJoin = LineJoin.Round;
// Move to where we need to draw
graphics.TranslateTransform(Left, Top);
var currentFreehandPath = freehandPath;
if (currentFreehandPath != null)
{
if (isRecalculated && Selected && renderMode == RenderMode.EDIT)
{
isRecalculated = false;
DrawSelectionBorder(graphics, pen, currentFreehandPath);
}
graphics.DrawPath(pen, currentFreehandPath);
}
// Move back, otherwise everything is shifted
graphics.TranslateTransform(-Left,-Top);
}
/// <summary>
/// Draw a selectionborder around the freehand path
/// </summary>
/// <param name="graphics">Graphics</param>
/// <param name="linePen">Pen</param>
/// <param name="path">GraphicsPath</param>
protected static void DrawSelectionBorder(Graphics graphics, Pen linePen, GraphicsPath path)
{
using var selectionPen = (Pen) linePen.Clone();
using var selectionPath = (GraphicsPath)path.Clone();
selectionPen.Width += 5;
selectionPen.Color = Color.FromArgb(120, Color.LightSeaGreen);
graphics.DrawPath(selectionPen, selectionPath);
selectionPath.Widen(selectionPen);
selectionPen.DashPattern = new float[]{2,2};
selectionPen.Color = Color.LightSeaGreen;
selectionPen.Width = 1;
graphics.DrawPath(selectionPen, selectionPath);
}
/// <summary>
/// Get the bounds in which we have something drawn, plus safety margin, these are not the normal bounds...
/// </summary>
public override Rectangle DrawingBounds {
get {
if (!myBounds.IsEmpty) {
int lineThickness = Math.Max(10, GetFieldValueAsInt(FieldType.LINE_THICKNESS));
int safetymargin = 10;
return new Rectangle(myBounds.Left + Left - (safetymargin+lineThickness), myBounds.Top + Top - (safetymargin+lineThickness), myBounds.Width + 2*(lineThickness+safetymargin), myBounds.Height + 2*(lineThickness+safetymargin));
}
if (_parent?.Image is Image image)
{
return new Rectangle(0, 0, image.Width, image.Height);
}
else
{
return Rectangle.Empty;
}
}
}
/// <summary>
/// FreehandContainer are regarded equal if they are of the same type and their paths are equal.
/// </summary>
/// <param name="obj">object</param>
/// <returns>bool</returns>
public override bool Equals(object obj) {
bool ret = false;
if (obj == null || GetType() != obj.GetType())
{
return false;
}
if (obj is FreehandContainer other && Equals(freehandPath, other.freehandPath)) {
ret = true;
}
return ret;
}
public override int GetHashCode() {
return freehandPath?.GetHashCode() ?? 0;
}
public override bool ClickableAt(int x, int y) {
bool returnValue = base.ClickableAt(x, y);
if (returnValue) {
int lineThickness = GetFieldValueAsInt(FieldType.LINE_THICKNESS);
using var pen = new Pen(Color.White)
{
Width = lineThickness + 10
};
returnValue = freehandPath.IsOutlineVisible(x - Left, y - Top, pen);
}
return returnValue;
}
}
}

View file

@ -1,99 +0,0 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
using System;
using System.Runtime.Serialization;
using Greenshot.Drawing.Fields;
using Greenshot.Drawing.Filters;
using GreenshotPlugin.Interfaces.Drawing;
namespace Greenshot.Drawing {
/// <summary>
/// Description of ObfuscateContainer.
/// </summary>
[Serializable]
public class HighlightContainer : FilterContainer {
public HighlightContainer(Surface parent) : base(parent) {
Init();
}
/// <summary>
/// Use settings from base, extend with our own field
/// </summary>
protected override void InitializeFields() {
base.InitializeFields();
AddField(GetType(), FieldType.PREPARED_FILTER_HIGHLIGHT, PreparedFilter.TEXT_HIGHTLIGHT);
}
protected override void OnDeserialized(StreamingContext context)
{
Init();
}
private void Init() {
FieldChanged += HighlightContainer_OnFieldChanged;
ConfigurePreparedFilters();
}
protected void HighlightContainer_OnFieldChanged(object sender, FieldChangedEventArgs e) {
if (!sender.Equals(this)) {
return;
}
if (Equals(e.Field.FieldType, FieldType.PREPARED_FILTER_HIGHLIGHT)) {
ConfigurePreparedFilters();
}
}
private void ConfigurePreparedFilters() {
PreparedFilter preset = (PreparedFilter)GetFieldValue(FieldType.PREPARED_FILTER_HIGHLIGHT);
while(Filters.Count>0) {
Remove(Filters[0]);
}
switch(preset) {
case PreparedFilter.TEXT_HIGHTLIGHT:
Add(new HighlightFilter(this));
break;
case PreparedFilter.AREA_HIGHLIGHT:
var brightnessFilter = new BrightnessFilter(this)
{
Invert = true
};
Add(brightnessFilter);
var blurFilter = new BlurFilter(this)
{
Invert = true
};
Add(blurFilter);
break;
case PreparedFilter.GRAYSCALE:
AbstractFilter f = new GrayscaleFilter(this)
{
Invert = true
};
Add(f);
break;
case PreparedFilter.MAGNIFICATION:
Add(new MagnifierFilter(this));
break;
}
}
}
}

View file

@ -1,109 +0,0 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
using System;
using System.Drawing;
using System.IO;
using System.Drawing.Drawing2D;
using log4net;
using System.Runtime.Serialization;
using GreenshotPlugin.Interfaces.Drawing;
namespace Greenshot.Drawing {
/// <summary>
/// Description of IconContainer.
/// </summary>
[Serializable]
public class IconContainer : DrawableContainer, IIconContainer {
private static readonly ILog Log = LogManager.GetLogger(typeof(IconContainer));
protected Icon icon;
public IconContainer(Surface parent) : base(parent) {
Init();
}
protected override void OnDeserialized(StreamingContext streamingContext)
{
base.OnDeserialized(streamingContext);
Init();
}
private void Init()
{
CreateDefaultAdorners();
}
public IconContainer(Surface parent, string filename) : base(parent) {
Load(filename);
}
public Icon Icon {
set {
icon?.Dispose();
icon = (Icon)value.Clone();
Width = value.Width;
Height = value.Height;
}
get => icon;
}
/**
* This Dispose is called from the Dispose and the Destructor.
* When disposing==true all non-managed resources should be freed too!
*/
protected override void Dispose(bool disposing) {
if (disposing)
{
icon?.Dispose();
}
icon = null;
base.Dispose(disposing);
}
public void Load(string filename)
{
if (!File.Exists(filename))
{
return;
}
using Icon fileIcon = new Icon(filename);
Icon = fileIcon;
Log.Debug("Loaded file: " + filename + " with resolution: " + Height + "," + Width);
}
public override void Draw(Graphics graphics, RenderMode rm)
{
if (icon == null)
{
return;
}
graphics.SmoothingMode = SmoothingMode.HighQuality;
graphics.InterpolationMode = InterpolationMode.NearestNeighbor;
graphics.CompositingQuality = CompositingQuality.Default;
graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
graphics.DrawIcon(icon, Bounds);
}
public override bool HasDefaultSize => true;
public override Size DefaultSize => icon?.Size ?? new Size(16,16);
}
}

View file

@ -1,234 +0,0 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
using System;
using System.Drawing;
using System.IO;
using Greenshot.Drawing.Fields;
using GreenshotPlugin.Core;
using System.Drawing.Drawing2D;
using log4net;
using System.Runtime.Serialization;
using GreenshotPlugin.Effects;
using GreenshotPlugin.Interfaces.Drawing;
namespace Greenshot.Drawing {
/// <summary>
/// Description of BitmapContainer.
/// </summary>
[Serializable]
public class ImageContainer : DrawableContainer, IImageContainer {
private static readonly ILog Log = LogManager.GetLogger(typeof(ImageContainer));
private Image image;
/// <summary>
/// This is the shadow version of the bitmap, rendered once to save performance
/// Do not serialize, as the shadow is recreated from the original bitmap if it's not available
/// </summary>
[NonSerialized]
private Image _shadowBitmap;
/// <summary>
/// This is the offset for the shadow version of the bitmap
/// Do not serialize, as the offset is recreated
/// </summary>
[NonSerialized]
private Point _shadowOffset = new Point(-1, -1);
public ImageContainer(Surface parent, string filename) : this(parent) {
Load(filename);
}
public ImageContainer(Surface parent) : base(parent) {
FieldChanged += BitmapContainer_OnFieldChanged;
Init();
}
protected override void OnDeserialized(StreamingContext streamingContext)
{
base.OnDeserialized(streamingContext);
Init();
}
private void Init()
{
CreateDefaultAdorners();
}
protected override void InitializeFields() {
AddField(GetType(), FieldType.SHADOW, false);
}
protected void BitmapContainer_OnFieldChanged(object sender, FieldChangedEventArgs e)
{
if (!sender.Equals(this))
{
return;
}
if (FieldType.SHADOW.Equals(e.Field.FieldType)) {
ChangeShadowField();
}
}
public void ChangeShadowField() {
bool shadow = GetFieldValueAsBool(FieldType.SHADOW);
if (shadow) {
CheckShadow(true);
Width = _shadowBitmap.Width;
Height = _shadowBitmap.Height;
Left -= _shadowOffset.X;
Top -= _shadowOffset.Y;
} else {
Width = image.Width;
Height = image.Height;
if (_shadowBitmap != null) {
Left += _shadowOffset.X;
Top += _shadowOffset.Y;
}
}
}
public Image Image {
set {
// Remove all current bitmaps
DisposeImage();
DisposeShadow();
image = ImageHelper.Clone(value);
bool shadow = GetFieldValueAsBool(FieldType.SHADOW);
CheckShadow(shadow);
if (!shadow) {
Width = image.Width;
Height = image.Height;
} else {
Width = _shadowBitmap.Width;
Height = _shadowBitmap.Height;
Left -= _shadowOffset.X;
Top -= _shadowOffset.Y;
}
}
get { return image; }
}
/// <summary>
/// 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!
/// </summary>
/// <param name="disposing"></param>
protected override void Dispose(bool disposing) {
if (disposing) {
DisposeImage();
DisposeShadow();
}
base.Dispose(disposing);
}
private void DisposeImage() {
image?.Dispose();
image = null;
}
private void DisposeShadow() {
_shadowBitmap?.Dispose();
_shadowBitmap = null;
}
/// <summary>
/// Make sure the content is also transformed.
/// </summary>
/// <param name="matrix"></param>
public override void Transform(Matrix matrix) {
int rotateAngle = CalculateAngle(matrix);
// we currently assume only one transformation has been made.
if (rotateAngle != 0) {
Log.DebugFormat("Rotating element with {0} degrees.", rotateAngle);
DisposeShadow();
using var tmpMatrix = new Matrix();
using (image)
{
image = ImageHelper.ApplyEffect(image, new RotateEffect(rotateAngle), tmpMatrix);
}
}
base.Transform(matrix);
}
/// <summary>
///
/// </summary>
/// <param name="filename"></param>
public void Load(string filename) {
if (!File.Exists(filename))
{
return;
}
// Always make sure ImageHelper.LoadBitmap results are disposed some time,
// as we close the bitmap internally, we need to do it afterwards
using (var tmpImage = ImageHelper.LoadImage(filename)) {
Image = tmpImage;
}
Log.Debug("Loaded file: " + filename + " with resolution: " + Height + "," + Width);
}
/// <summary>
/// This checks if a shadow is already generated
/// </summary>
/// <param name="shadow"></param>
private void CheckShadow(bool shadow)
{
if (!shadow || _shadowBitmap != null)
{
return;
}
using var matrix = new Matrix();
_shadowBitmap = ImageHelper.ApplyEffect(image, new DropShadowEffect(), matrix);
}
/// <summary>
/// Draw the actual container to the graphics object
/// </summary>
/// <param name="graphics"></param>
/// <param name="rm"></param>
public override void Draw(Graphics graphics, RenderMode rm)
{
if (image == null)
{
return;
}
bool shadow = GetFieldValueAsBool(FieldType.SHADOW);
graphics.SmoothingMode = SmoothingMode.HighQuality;
graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
graphics.CompositingQuality = CompositingQuality.HighQuality;
graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
if (shadow) {
CheckShadow(true);
graphics.DrawImage(_shadowBitmap, Bounds);
} else {
graphics.DrawImage(image, Bounds);
}
}
public override bool HasDefaultSize => true;
public override Size DefaultSize => image?.Size ?? new Size(32, 32);
}
}

View file

@ -1,112 +0,0 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Runtime.Serialization;
using Greenshot.Drawing.Fields;
using Greenshot.Helpers;
using Greenshot.Drawing.Adorners;
using GreenshotPlugin.Interfaces.Drawing;
namespace Greenshot.Drawing {
/// <summary>
/// Description of LineContainer.
/// </summary>
[Serializable()]
public class LineContainer : DrawableContainer {
public LineContainer(Surface parent) : base(parent) {
Init();
}
protected override void InitializeFields() {
AddField(GetType(), FieldType.LINE_THICKNESS, 2);
AddField(GetType(), FieldType.LINE_COLOR, Color.Red);
AddField(GetType(), FieldType.SHADOW, true);
}
protected override void OnDeserialized(StreamingContext context)
{
Init();
}
protected void Init() {
Adorners.Add(new MoveAdorner(this, Positions.TopLeft));
Adorners.Add(new MoveAdorner(this, Positions.BottomRight));
}
public override void Draw(Graphics graphics, RenderMode rm) {
graphics.SmoothingMode = SmoothingMode.HighQuality;
graphics.InterpolationMode = InterpolationMode.HighQualityBilinear;
graphics.CompositingQuality = CompositingQuality.HighQuality;
graphics.PixelOffsetMode = PixelOffsetMode.None;
int lineThickness = GetFieldValueAsInt(FieldType.LINE_THICKNESS);
Color lineColor = GetFieldValueAsColor(FieldType.LINE_COLOR);
bool shadow = GetFieldValueAsBool(FieldType.SHADOW);
if (lineThickness > 0) {
if (shadow) {
//draw shadow first
int basealpha = 100;
int alpha = basealpha;
int steps = 5;
int currentStep = 1;
while (currentStep <= steps)
{
using Pen shadowCapPen = new Pen(Color.FromArgb(alpha, 100, 100, 100), lineThickness);
graphics.DrawLine(shadowCapPen,
Left + currentStep,
Top + currentStep,
Left + currentStep + Width,
Top + currentStep + Height);
currentStep++;
alpha -= basealpha / steps;
}
}
using Pen pen = new Pen(lineColor, lineThickness);
graphics.DrawLine(pen, Left, Top, Left + Width, Top + Height);
}
}
public override bool ClickableAt(int x, int y) {
int lineThickness = GetFieldValueAsInt(FieldType.LINE_THICKNESS) +5;
if (lineThickness > 0)
{
using Pen pen = new Pen(Color.White)
{
Width = lineThickness
};
using GraphicsPath path = new GraphicsPath();
path.AddLine(Left, Top, Left + Width, Top + Height);
return path.IsOutlineVisible(x, y, pen);
}
return false;
}
protected override ScaleHelper.IDoubleProcessor GetAngleRoundProcessor() {
return ScaleHelper.LineAngleRoundBehavior.Instance;
}
}
}

View file

@ -1,76 +0,0 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
using System;
using System.Runtime.Serialization;
using Greenshot.Drawing.Fields;
using Greenshot.Drawing.Filters;
using GreenshotPlugin.Interfaces.Drawing;
namespace Greenshot.Drawing {
/// <summary>
/// Description of ObfuscateContainer.
/// </summary>
[Serializable]
public class ObfuscateContainer : FilterContainer {
public ObfuscateContainer(Surface parent) : base(parent) {
Init();
}
protected override void InitializeFields() {
base.InitializeFields();
AddField(GetType(), FieldType.PREPARED_FILTER_OBFUSCATE, PreparedFilter.PIXELIZE);
}
protected override void OnDeserialized(StreamingContext context)
{
Init();
}
private void Init() {
FieldChanged += ObfuscateContainer_OnFieldChanged;
ConfigurePreparedFilters();
CreateDefaultAdorners();
}
protected void ObfuscateContainer_OnFieldChanged(object sender, FieldChangedEventArgs e) {
if(sender.Equals(this)) {
if(Equals(e.Field.FieldType, FieldType.PREPARED_FILTER_OBFUSCATE)) {
ConfigurePreparedFilters();
}
}
}
private void ConfigurePreparedFilters() {
PreparedFilter preset = (PreparedFilter)GetFieldValue(FieldType.PREPARED_FILTER_OBFUSCATE);
while(Filters.Count>0) {
Remove(Filters[0]);
}
switch(preset) {
case PreparedFilter.BLUR:
Add(new BlurFilter(this));
break;
case PreparedFilter.PIXELIZE:
Add(new PixelizationFilter(this));
break;
}
}
}
}

View file

@ -1,157 +0,0 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using Greenshot.Drawing.Fields;
using Greenshot.Helpers;
using System.Runtime.Serialization;
using GreenshotPlugin.Interfaces.Drawing;
namespace Greenshot.Drawing {
/// <summary>
/// Represents a rectangular shape on the Surface
/// </summary>
[Serializable]
public class RectangleContainer : DrawableContainer {
public RectangleContainer(Surface parent) : base(parent) {
Init();
}
/// <summary>
/// Do some logic to make sure all field are initiated correctly
/// </summary>
/// <param name="streamingContext">StreamingContext</param>
protected override void OnDeserialized(StreamingContext streamingContext)
{
base.OnDeserialized(streamingContext);
Init();
}
private void Init()
{
CreateDefaultAdorners();
}
protected override void InitializeFields() {
AddField(GetType(), FieldType.LINE_THICKNESS, 2);
AddField(GetType(), FieldType.LINE_COLOR, Color.Red);
AddField(GetType(), FieldType.FILL_COLOR, Color.Transparent);
AddField(GetType(), FieldType.SHADOW, true);
}
public override void Draw(Graphics graphics, RenderMode rm) {
int lineThickness = GetFieldValueAsInt(FieldType.LINE_THICKNESS);
Color lineColor = GetFieldValueAsColor(FieldType.LINE_COLOR, Color.Red);
Color fillColor = GetFieldValueAsColor(FieldType.FILL_COLOR, Color.Transparent);
bool shadow = GetFieldValueAsBool(FieldType.SHADOW);
Rectangle rect = GuiRectangle.GetGuiRectangle(Left, Top, Width, Height);
DrawRectangle(rect, graphics, rm, lineThickness, lineColor, fillColor, shadow);
}
/// <summary>
/// This method can also be used from other containers, if the right values are passed!
/// </summary>
/// <param name="rect"></param>
/// <param name="graphics"></param>
/// <param name="rm"></param>
/// <param name="lineThickness"></param>
/// <param name="lineColor"></param>
/// <param name="fillColor"></param>
/// <param name="shadow"></param>
public static void DrawRectangle(Rectangle rect, Graphics graphics, RenderMode rm, int lineThickness, Color lineColor, Color fillColor, bool shadow) {
graphics.SmoothingMode = SmoothingMode.HighQuality;
graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
graphics.CompositingQuality = CompositingQuality.HighQuality;
graphics.PixelOffsetMode = PixelOffsetMode.None;
bool lineVisible = lineThickness > 0 && Colors.IsVisible(lineColor);
if (shadow && (lineVisible || Colors.IsVisible(fillColor))) {
//draw shadow first
int basealpha = 100;
int alpha = basealpha;
int steps = 5;
int currentStep = lineVisible ? 1 : 0;
while (currentStep <= steps)
{
using Pen shadowPen = new Pen(Color.FromArgb(alpha, 100, 100, 100))
{
Width = lineVisible ? lineThickness : 1
};
Rectangle shadowRect = GuiRectangle.GetGuiRectangle(
rect.Left + currentStep,
rect.Top + currentStep,
rect.Width,
rect.Height);
graphics.DrawRectangle(shadowPen, shadowRect);
currentStep++;
alpha -= basealpha / steps;
}
}
if (Colors.IsVisible(fillColor))
{
using Brush brush = new SolidBrush(fillColor);
graphics.FillRectangle(brush, rect);
}
graphics.SmoothingMode = SmoothingMode.HighSpeed;
if (lineVisible)
{
using Pen pen = new Pen(lineColor, lineThickness);
graphics.DrawRectangle(pen, rect);
}
}
public override bool ClickableAt(int x, int y) {
Rectangle rect = GuiRectangle.GetGuiRectangle(Left, Top, Width, Height);
int lineThickness = GetFieldValueAsInt(FieldType.LINE_THICKNESS) + 10;
Color fillColor = GetFieldValueAsColor(FieldType.FILL_COLOR);
return RectangleClickableAt(rect, lineThickness, fillColor, x, y);
}
public static bool RectangleClickableAt(Rectangle rect, int lineThickness, Color fillColor, int x, int y) {
// If we clicked inside the rectangle and it's visible we are clickable at.
if (!Color.Transparent.Equals(fillColor)) {
if (rect.Contains(x,y)) {
return true;
}
}
// check the rest of the lines
if (lineThickness > 0)
{
using Pen pen = new Pen(Color.White, lineThickness);
using GraphicsPath path = new GraphicsPath();
path.AddRectangle(rect);
return path.IsOutlineVisible(x, y, pen);
}
return false;
}
}
}

View file

@ -1,345 +0,0 @@
/*
* Greenshot - a free and open source screenshot tool
* Copyright (C) 2007-2012 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 Greenshot.Drawing.Fields;
using Greenshot.Helpers;
using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Text;
using System.Runtime.Serialization;
using GreenshotPlugin.Interfaces.Drawing;
namespace Greenshot.Drawing
{
/// <summary>
/// Description of SpeechbubbleContainer.
/// </summary>
[Serializable]
public class SpeechbubbleContainer : TextContainer {
private Point _initialGripperPoint;
// Only used for serializing the TargetGripper location
private Point _storedTargetGripperLocation;
/// <summary>
/// Store the current location of the target gripper
/// </summary>
/// <param name="context"></param>
[OnSerializing]
private void SetValuesOnSerializing(StreamingContext context) {
if (TargetAdorner != null) {
_storedTargetGripperLocation = TargetAdorner.Location;
}
}
/// <summary>
/// Restore the target gripper
/// </summary>
/// <param name="streamingContext">StreamingContext</param>
protected override void OnDeserialized(StreamingContext streamingContext)
{
base.OnDeserialized(streamingContext);
InitAdorner(Color.Green, _storedTargetGripperLocation);
}
public SpeechbubbleContainer(Surface parent)
: base(parent) {
}
/// <summary>
/// We set our own field values
/// </summary>
protected override void InitializeFields() {
AddField(GetType(), FieldType.LINE_THICKNESS, 2);
AddField(GetType(), FieldType.LINE_COLOR, Color.Blue);
AddField(GetType(), FieldType.SHADOW, false);
AddField(GetType(), FieldType.FONT_ITALIC, false);
AddField(GetType(), FieldType.FONT_BOLD, true);
AddField(GetType(), FieldType.FILL_COLOR, Color.White);
AddField(GetType(), FieldType.FONT_FAMILY, FontFamily.GenericSansSerif.Name);
AddField(GetType(), FieldType.FONT_SIZE, 20f);
AddField(GetType(), FieldType.TEXT_HORIZONTAL_ALIGNMENT, StringAlignment.Center);
AddField(GetType(), FieldType.TEXT_VERTICAL_ALIGNMENT, StringAlignment.Center);
}
/// <summary>
/// Called from Surface (the _parent) when the drawing begins (mouse-down)
/// </summary>
/// <returns>true if the surface doesn't need to handle the event</returns>
public override bool HandleMouseDown(int mouseX, int mouseY) {
if (TargetAdorner == null) {
_initialGripperPoint = new Point(mouseX, mouseY);
InitAdorner(Color.Green, new Point(mouseX, mouseY));
}
return base.HandleMouseDown(mouseX, mouseY);
}
/// <summary>
/// Overriding the HandleMouseMove will help us to make sure the tail is always visible.
/// Should fix BUG-1682
/// </summary>
/// <param name="x"></param>
/// <param name="y"></param>
/// <returns>base.HandleMouseMove</returns>
public override bool HandleMouseMove(int x, int y) {
bool returnValue = base.HandleMouseMove(x, y);
bool leftAligned = _boundsAfterResize.Right - _boundsAfterResize.Left >= 0;
bool topAligned = _boundsAfterResize.Bottom - _boundsAfterResize.Top >= 0;
int xOffset = leftAligned ? -20 : 20;
int yOffset = topAligned ? -20 : 20;
Point newGripperLocation = _initialGripperPoint;
newGripperLocation.Offset(xOffset, yOffset);
if (TargetAdorner.Location != newGripperLocation) {
Invalidate();
TargetAdorner.Location = newGripperLocation;
Invalidate();
}
return returnValue;
}
/// <summary>
/// The DrawingBound should be so close as possible to the shape, so we don't invalidate to much.
/// </summary>
public override Rectangle DrawingBounds {
get {
if (Status != EditStatus.UNDRAWN) {
int lineThickness = GetFieldValueAsInt(FieldType.LINE_THICKNESS);
Color lineColor = GetFieldValueAsColor(FieldType.LINE_COLOR);
bool shadow = GetFieldValueAsBool(FieldType.SHADOW);
using Pen pen = new Pen(lineColor, lineThickness);
int inflateValue = lineThickness + 2 + (shadow ? 6 : 0);
using GraphicsPath tailPath = CreateTail();
return Rectangle.Inflate(Rectangle.Union(Rectangle.Round(tailPath.GetBounds(new Matrix(), pen)), GuiRectangle.GetGuiRectangle(Left, Top, Width, Height)), inflateValue, inflateValue);
}
return Rectangle.Empty;
}
}
/// <summary>
/// Helper method to create the bubble GraphicsPath, so we can also calculate the bounds
/// </summary>
/// <param name="lineThickness"></param>
/// <returns></returns>
private GraphicsPath CreateBubble(int lineThickness) {
GraphicsPath bubble = new GraphicsPath();
Rectangle rect = GuiRectangle.GetGuiRectangle(Left, Top, Width, Height);
Rectangle bubbleRect = GuiRectangle.GetGuiRectangle(0, 0, rect.Width, rect.Height);
// adapt corner radius to small rectangle dimensions
int smallerSideLength = Math.Min(bubbleRect.Width, bubbleRect.Height);
int cornerRadius = Math.Min(30, smallerSideLength / 2 - lineThickness);
if (cornerRadius > 0) {
bubble.AddArc(bubbleRect.X, bubbleRect.Y, cornerRadius, cornerRadius, 180, 90);
bubble.AddArc(bubbleRect.X + bubbleRect.Width - cornerRadius, bubbleRect.Y, cornerRadius, cornerRadius, 270, 90);
bubble.AddArc(bubbleRect.X + bubbleRect.Width - cornerRadius, bubbleRect.Y + bubbleRect.Height - cornerRadius, cornerRadius, cornerRadius, 0, 90);
bubble.AddArc(bubbleRect.X, bubbleRect.Y + bubbleRect.Height - cornerRadius, cornerRadius, cornerRadius, 90, 90);
} else {
bubble.AddRectangle(bubbleRect);
}
bubble.CloseAllFigures();
using (Matrix bubbleMatrix = new Matrix()) {
bubbleMatrix.Translate(rect.Left, rect.Top);
bubble.Transform(bubbleMatrix);
}
return bubble;
}
/// <summary>
/// Helper method to create the tail of the bubble, so we can also calculate the bounds
/// </summary>
/// <returns></returns>
private GraphicsPath CreateTail() {
Rectangle rect = GuiRectangle.GetGuiRectangle(Left, Top, Width, Height);
int tailLength = GeometryHelper.Distance2D(rect.Left + (rect.Width / 2), rect.Top + (rect.Height / 2), TargetAdorner.Location.X, TargetAdorner.Location.Y);
int tailWidth = (Math.Abs(rect.Width) + Math.Abs(rect.Height)) / 20;
// This should fix a problem with the tail being to wide
tailWidth = Math.Min(Math.Abs(rect.Width) / 2, tailWidth);
tailWidth = Math.Min(Math.Abs(rect.Height) / 2, tailWidth);
GraphicsPath tail = new GraphicsPath();
tail.AddLine(-tailWidth, 0, tailWidth, 0);
tail.AddLine(tailWidth, 0, 0, -tailLength);
tail.CloseFigure();
int tailAngle = 90 + (int)GeometryHelper.Angle2D(rect.Left + rect.Width / 2, rect.Top + rect.Height / 2, TargetAdorner.Location.X, TargetAdorner.Location.Y);
using (Matrix tailMatrix = new Matrix()) {
tailMatrix.Translate(rect.Left + rect.Width / 2, rect.Top + rect.Height / 2);
tailMatrix.Rotate(tailAngle);
tail.Transform(tailMatrix);
}
return tail;
}
/// <summary>
/// This is to draw the actual container
/// </summary>
/// <param name="graphics"></param>
/// <param name="renderMode"></param>
public override void Draw(Graphics graphics, RenderMode renderMode) {
if (TargetAdorner == null) {
return;
}
graphics.SmoothingMode = SmoothingMode.HighQuality;
graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
graphics.CompositingQuality = CompositingQuality.HighQuality;
graphics.PixelOffsetMode = PixelOffsetMode.None;
graphics.TextRenderingHint = TextRenderingHint.AntiAliasGridFit;
Color lineColor = GetFieldValueAsColor(FieldType.LINE_COLOR);
Color fillColor = GetFieldValueAsColor(FieldType.FILL_COLOR);
bool shadow = GetFieldValueAsBool(FieldType.SHADOW);
int lineThickness = GetFieldValueAsInt(FieldType.LINE_THICKNESS);
bool lineVisible = lineThickness > 0 && Colors.IsVisible(lineColor);
Rectangle rect = GuiRectangle.GetGuiRectangle(Left, Top, Width, Height);
if (Selected && renderMode == RenderMode.EDIT) {
DrawSelectionBorder(graphics, rect);
}
GraphicsPath bubble = CreateBubble(lineThickness);
GraphicsPath tail = CreateTail();
//draw shadow first
if (shadow && (lineVisible || Colors.IsVisible(fillColor))) {
const int basealpha = 100;
int alpha = basealpha;
const int steps = 5;
int currentStep = lineVisible ? 1 : 0;
using Matrix shadowMatrix = new Matrix();
using GraphicsPath bubbleClone = (GraphicsPath)bubble.Clone();
using GraphicsPath tailClone = (GraphicsPath)tail.Clone();
shadowMatrix.Translate(1, 1);
while (currentStep <= steps) {
using (Pen shadowPen = new Pen(Color.FromArgb(alpha, 100, 100, 100))) {
shadowPen.Width = lineVisible ? lineThickness : 1;
tailClone.Transform(shadowMatrix);
graphics.DrawPath(shadowPen, tailClone);
bubbleClone.Transform(shadowMatrix);
graphics.DrawPath(shadowPen, bubbleClone);
}
currentStep++;
alpha -= basealpha / steps;
}
}
GraphicsState state = graphics.Save();
// draw the tail border where the bubble is not visible
using (Region clipRegion = new Region(bubble)) {
graphics.SetClip(clipRegion, CombineMode.Exclude);
using Pen pen = new Pen(lineColor, lineThickness);
graphics.DrawPath(pen, tail);
}
graphics.Restore(state);
if (Colors.IsVisible(fillColor)) {
//draw the bubbleshape
state = graphics.Save();
using (Brush brush = new SolidBrush(fillColor)) {
graphics.FillPath(brush, bubble);
}
graphics.Restore(state);
}
if (lineVisible) {
//draw the bubble border
state = graphics.Save();
// Draw bubble where the Tail is not visible.
using (Region clipRegion = new Region(tail)) {
graphics.SetClip(clipRegion, CombineMode.Exclude);
using Pen pen = new Pen(lineColor, lineThickness);
//pen.EndCap = pen.StartCap = LineCap.Round;
graphics.DrawPath(pen, bubble);
}
graphics.Restore(state);
}
if (Colors.IsVisible(fillColor)) {
// Draw the tail border
state = graphics.Save();
using (Brush brush = new SolidBrush(fillColor)) {
graphics.FillPath(brush, tail);
}
graphics.Restore(state);
}
// cleanup the paths
bubble.Dispose();
tail.Dispose();
// Draw the text
DrawText(graphics, rect, lineThickness, lineColor, shadow, StringFormat, Text, Font);
}
public override bool Contains(int x, int y) {
if (base.Contains(x, y)) {
return true;
}
Point clickedPoint = new Point(x, y);
if (Status != EditStatus.UNDRAWN) {
int lineThickness = GetFieldValueAsInt(FieldType.LINE_THICKNESS);
Color lineColor = GetFieldValueAsColor(FieldType.LINE_COLOR);
using Pen pen = new Pen(lineColor, lineThickness);
using (GraphicsPath bubblePath = CreateBubble(lineThickness)) {
bubblePath.Widen(pen);
if (bubblePath.IsVisible(clickedPoint)) {
return true;
}
}
using GraphicsPath tailPath = CreateTail();
tailPath.Widen(pen);
if (tailPath.IsVisible(clickedPoint)) {
return true;
}
}
return false;
}
public override bool ClickableAt(int x, int y) {
return Contains(x,y);
}
/// <summary>
/// Additional to the Transform of the TextContainer the bubble tail coordinates also need to be moved
/// </summary>
/// <param name="matrix">Matrix</param>
public override void Transform(Matrix matrix)
{
Point[] points = { TargetAdorner.Location };
matrix.TransformPoints(points);
TargetAdorner.Location = points[0];
base.Transform(matrix);
}
}
}

View file

@ -1,206 +0,0 @@
/*
* Greenshot - a free and open source screenshot tool
* Copyright (C) 2007-2012 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 Greenshot.Drawing.Fields;
using Greenshot.Helpers;
using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Text;
using System.Runtime.Serialization;
using GreenshotPlugin.Interfaces.Drawing;
namespace Greenshot.Drawing {
/// <summary>
/// This is an enumerated label, every single StepLabelContainer shows the number of the order it was created.
/// To make sure that deleting recalculates, we check the location before every draw.
/// </summary>
[Serializable]
public sealed class StepLabelContainer : DrawableContainer {
[NonSerialized]
private StringFormat _stringFormat = new StringFormat();
private readonly bool _drawAsRectangle = false;
public StepLabelContainer(Surface parent) : base(parent) {
parent.AddStepLabel(this);
InitContent();
Init();
}
private void Init()
{
CreateDefaultAdorners();
}
// Used to store the number of this label, so when deserializing it can be placed back to the StepLabels list in the right location
private int _number;
// Used to store the counter start of the Surface, as the surface is NOT stored.
private int _counterStart = 1;
public int Number {
get {
return _number;
}
set {
_number = value;
}
}
/// <summary>
/// Retrieve the counter before serializing
/// </summary>
/// <param name="context"></param>
[OnSerializing]
private void SetValuesOnSerializing(StreamingContext context) {
if (Parent != null) {
Number = ((Surface)Parent).CountStepLabels(this);
_counterStart = ((Surface) Parent).CounterStart;
}
}
/// <summary>
/// Restore values that don't serialize
/// </summary>
/// <param name="context"></param>
protected override void OnDeserialized(StreamingContext context)
{
Init();
_stringFormat = new StringFormat
{
Alignment = StringAlignment.Center,
LineAlignment = StringAlignment.Center
};
}
/// <summary>
/// Add the StepLabel to the parent
/// </summary>
/// <param name="newParent"></param>
protected override void SwitchParent(Surface newParent) {
if (newParent == Parent)
{
return;
}
((Surface) Parent)?.RemoveStepLabel(this);
base.SwitchParent(newParent);
if (newParent == null)
{
return;
}
// Make sure the counter start is restored (this unfortunately happens multiple times... -> hack)
newParent.CounterStart = _counterStart;
newParent.AddStepLabel(this);
}
public override Size DefaultSize => new Size(30, 30);
public override bool InitContent() {
_defaultEditMode = EditStatus.IDLE;
_stringFormat.Alignment = StringAlignment.Center;
_stringFormat.LineAlignment = StringAlignment.Center;
// Set defaults
Width = DefaultSize.Width;
Height = DefaultSize.Height;
return true;
}
/// <summary>
/// This makes it possible for the label to be placed exactly in the middle of the pointer.
/// </summary>
public override bool HandleMouseDown(int mouseX, int mouseY) {
return base.HandleMouseDown(mouseX - Width / 2, mouseY - Height / 2);
}
/// <summary>
/// We set our own field values
/// </summary>
protected override void InitializeFields() {
AddField(GetType(), FieldType.FILL_COLOR, Color.DarkRed);
AddField(GetType(), FieldType.LINE_COLOR, Color.White);
AddField(GetType(), FieldType.FLAGS, FieldFlag.COUNTER);
}
/// <summary>
/// Make sure this element is no longer referenced from the surface
/// </summary>
protected override void Dispose(bool disposing) {
base.Dispose(disposing);
if (!disposing) {
return;
}
((Surface) Parent)?.RemoveStepLabel(this);
if (_stringFormat == null)
{
return;
}
_stringFormat.Dispose();
_stringFormat = null;
}
public override bool HandleMouseMove(int x, int y) {
Invalidate();
Left = x - Width / 2;
Top = y - Height / 2;
Invalidate();
return true;
}
/// <summary>
/// Override the parent, calculate the label number, than draw
/// </summary>
/// <param name="graphics"></param>
/// <param name="rm"></param>
public override void Draw(Graphics graphics, RenderMode rm) {
graphics.SmoothingMode = SmoothingMode.HighQuality;
graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
graphics.CompositingQuality = CompositingQuality.HighQuality;
graphics.PixelOffsetMode = PixelOffsetMode.None;
graphics.TextRenderingHint = TextRenderingHint.AntiAliasGridFit;
string text = ((Surface)Parent).CountStepLabels(this).ToString();
Rectangle rect = GuiRectangle.GetGuiRectangle(Left, Top, Width, Height);
Color fillColor = GetFieldValueAsColor(FieldType.FILL_COLOR);
Color lineColor = GetFieldValueAsColor(FieldType.LINE_COLOR);
if (_drawAsRectangle) {
RectangleContainer.DrawRectangle(rect, graphics, rm, 0, Color.Transparent, fillColor, false);
} else {
EllipseContainer.DrawEllipse(rect, graphics, rm, 0, Color.Transparent, fillColor, false);
}
float fontSize = Math.Min(Width,Height) / 1.4f;
using FontFamily fam = new FontFamily(FontFamily.GenericSansSerif.Name);
using Font font = new Font(fam, fontSize, FontStyle.Bold, GraphicsUnit.Pixel);
TextContainer.DrawText(graphics, rect, 0, lineColor, false, _stringFormat, text, font);
}
public override bool ClickableAt(int x, int y) {
Rectangle rect = GuiRectangle.GetGuiRectangle(Left, Top, Width, Height);
Color fillColor = GetFieldValueAsColor(FieldType.FILL_COLOR);
if (_drawAsRectangle) {
return RectangleContainer.RectangleClickableAt(rect, 0, fillColor, x, y);
}
return EllipseContainer.EllipseClickableAt(rect, 0, fillColor, x, y);
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -1,333 +0,0 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
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.Security.Permissions;
using System.Windows.Forms;
using Greenshot.Configuration;
using Greenshot.Helpers;
using GreenshotPlugin.Core;
using GreenshotPlugin.IniFile;
using log4net;
namespace Greenshot.Forms {
/// <summary>
/// The about form
/// </summary>
public sealed partial class AboutForm : AnimatingBaseForm {
private static readonly ILog Log = LogManager.GetLogger(typeof(AboutForm));
private Bitmap _bitmap;
private readonly ColorAnimator _backgroundAnimation;
private readonly List<RectangleAnimator> _pixels = new List<RectangleAnimator>();
private readonly List<Color> _colorFlow = new List<Color>();
private readonly List<Color> _pixelColors = new List<Color>();
private readonly Random _rand = new Random();
private readonly Color _backColor = Color.FromArgb(61, 61, 61);
private readonly Color _pixelColor = Color.FromArgb(138, 255, 0);
// Variables used for the color-cycle
private int _waitFrames;
private int _colorIndex;
private int _scrollCount;
private bool _hasAnimationsLeft;
// Variables are used to define the location of the dots
private const int W = 13;
private const int P1 = 7;
private const int P2 = P1 + W;
private const int P3 = P2 + W;
private const int P4 = P3 + W;
private const int P5 = P4 + W;
private const int P6 = P5 + W;
private const int P7 = P6 + W;
/// <summary>
/// The location of every dot in the "G"
/// </summary>
private readonly List<Point> _gSpots = new List<Point>
{
// Top row
new Point(P2, P1), // 0
new Point(P3, P1), // 1
new Point(P4, P1), // 2
new Point(P5, P1), // 3
new Point(P6, P1), // 4
// Second row
new Point(P1, P2), // 5
new Point(P2, P2), // 6
// Third row
new Point(P1, P3), // 7
new Point(P2, P3), // 8
// Fourth row
new Point(P1, P4), // 9
new Point(P2, P4), // 10
new Point(P5, P4), // 11
new Point(P6, P4), // 12
new Point(P7, P4), // 13
// Fifth row
new Point(P1, P5), // 14
new Point(P2, P5), // 15
new Point(P6, P5), // 16
new Point(P7, P5), // 17
// Sixth row
new Point(P1, P6), // 18
new Point(P2, P6), // 19
new Point(P3, P6), // 20
new Point(P4, P6), // 21
new Point(P5, P6), // 22
new Point(P6, P6) // 23
};
// 0 1 2 3 4
// 5 6
// 7 8
// 9 10 11 12 13
// 14 15 16 17
// 18 19 20 21 22 23
// The order in which we draw the dots & flow the colors.
private readonly List<int> _flowOrder = new List<int>{ 4, 3, 2, 1, 0, 5, 6, 7, 8, 9, 10, 14, 15, 18, 19, 20, 21, 22, 23, 16, 17, 13, 12, 11 };
/// <summary>
/// Cleanup all the allocated resources
/// </summary>
private void Cleanup(object sender, EventArgs e) {
if (_bitmap == null) return;
_bitmap.Dispose();
_bitmap = null;
}
/// <summary>
/// Constructor
/// </summary>
public AboutForm() {
// Make sure our resources are removed again.
Disposed += Cleanup;
FormClosing += Cleanup;
// Enable animation for this form, when we don't set this the timer doesn't start as soon as the form is loaded.
EnableAnimation = true;
//
// The InitializeComponent() call is required for Windows Forms designer support.
//
InitializeComponent();
// Only use double-buffering when we are NOT in a Terminal Server session
DoubleBuffered = !IsTerminalServerSession;
// Use the self drawn image, first we create the background to be the back-color (as we animate from this)
_bitmap = ImageHelper.CreateEmpty(90, 90, PixelFormat.Format24bppRgb, BackColor, 96, 96);
pictureBox1.Image = _bitmap;
lblTitle.Text = $@"Greenshot {EnvironmentInfo.GetGreenshotVersion()} {(IniConfig.IsPortable ? " Portable" : "")} ({OsInfo.Bits}) bit)";
// Number of frames the pixel animation takes
int frames = FramesForMillis(2000);
// The number of frames the color-cycle waits before it starts
_waitFrames = FramesForMillis(6000);
// Every pixel is created after pixelWaitFrames frames, which is increased in the loop.
int pixelWaitFrames = FramesForMillis(2000);
// Create pixels
for (int index = 0; index < _gSpots.Count; index++) {
// Read the pixels in the order of the flow
Point gSpot = _gSpots[_flowOrder[index]];
// Create the animation, first we do nothing (on the final destination)
RectangleAnimator pixelAnimation;
// Make the pixel grow from the middle, if this offset isn't used it looks like it's shifted
int offset = (W - 2) / 2;
// If the optimize for Terminal Server is set we make the animation without much ado
if (IsTerminalServerSession) {
// No animation
pixelAnimation = new RectangleAnimator(new Rectangle(gSpot.X, gSpot.Y, W - 2, W - 2), new Rectangle(gSpot.X, gSpot.Y, W - 2, W - 2), 1, EasingType.Cubic, EasingMode.EaseIn);
} else {
// Create the animation, first we do nothing (on the final destination)
Rectangle standingStill = new Rectangle(gSpot.X + offset, gSpot.Y + offset, 0, 0);
pixelAnimation = new RectangleAnimator(standingStill, standingStill, pixelWaitFrames, EasingType.Quintic, EasingMode.EaseIn);
// And than we size to the wanted size.
pixelAnimation.QueueDestinationLeg(new Rectangle(gSpot.X, gSpot.Y, W - 2, W - 2), frames);
}
// Increase the wait frames
pixelWaitFrames += FramesForMillis(100);
// Add to the list of to be animated pixels
_pixels.Add(pixelAnimation);
// Add a color to the list for this pixel.
_pixelColors.Add(_pixelColor);
}
// Make sure the frame "loop" knows we have to animate
_hasAnimationsLeft = true;
// Pixel Color cycle colors, here we use a pre-animated loop which stores the values.
ColorAnimator pixelColorAnimator = new ColorAnimator(_pixelColor, Color.FromArgb(255, 255, 255), 6, EasingType.Quadratic, EasingMode.EaseIn);
pixelColorAnimator.QueueDestinationLeg(_pixelColor, 6, EasingType.Quadratic, EasingMode.EaseOut);
do {
_colorFlow.Add(pixelColorAnimator.Current);
pixelColorAnimator.Next();
} while (pixelColorAnimator.HasNext);
// color animation for the background
_backgroundAnimation = new ColorAnimator(BackColor, _backColor, FramesForMillis(5000), EasingType.Linear, EasingMode.EaseIn);
}
/// <summary>
/// This is called when a link is clicked
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void LinkLabelClicked(object sender, LinkLabelLinkClickedEventArgs e)
{
if (!(sender is LinkLabel linkLabel)) return;
try {
linkLabel.LinkVisited = true;
Process.Start(linkLabel.Text);
} catch (Exception) {
MessageBox.Show(Language.GetFormattedString(LangKey.error_openlink, linkLabel.Text), Language.GetString(LangKey.error));
}
}
/// <summary>
/// Called from the AnimatingForm, for every frame
/// </summary>
protected override void Animate() {
if (_bitmap == null) {
return;
}
if (!IsTerminalServerSession) {
// Color cycle
if (_waitFrames != 0) {
_waitFrames--;
// Check if there is something else to do, if not we return so we don't occupy the CPU
if (!_hasAnimationsLeft) {
return;
}
} else if (_scrollCount < _pixelColors.Count + _colorFlow.Count) {
// Scroll colors, the scrollCount is the amount of pixels + the amount of colors to cycle.
for (int index = _pixelColors.Count - 1; index > 0; index--) {
_pixelColors[index] = _pixelColors[index - 1];
}
// Keep adding from the colors to cycle until there is nothing left
if (_colorIndex < _colorFlow.Count) {
_pixelColors[0] = _colorFlow[_colorIndex++];
}
_scrollCount++;
} else {
// Reset values, wait X time for the next one
_waitFrames = FramesForMillis(3000 + _rand.Next(35000));
_colorIndex = 0;
_scrollCount = 0;
// Check if there is something else to do, if not we return so we don't occupy the CPU
if (!_hasAnimationsLeft) {
return;
}
}
} else if (!_hasAnimationsLeft) {
return;
}
// Draw the "G"
using (Graphics graphics = Graphics.FromImage(_bitmap)) {
graphics.SmoothingMode = SmoothingMode.HighQuality;
graphics.InterpolationMode = InterpolationMode.HighQualityBilinear;
graphics.CompositingQuality = CompositingQuality.HighQuality;
graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
graphics.Clear(_backgroundAnimation.Next());
graphics.TranslateTransform(2, -2);
graphics.RotateTransform(20);
using SolidBrush brush = new SolidBrush(_pixelColor);
int index = 0;
// We assume there is nothing to animate in the next Animate loop
_hasAnimationsLeft = false;
// Pixels of the G
foreach (RectangleAnimator pixel in _pixels) {
brush.Color = _pixelColors[index++];
graphics.FillEllipse(brush, pixel.Current);
// If a pixel still has frames left, the hasAnimationsLeft will be true
_hasAnimationsLeft |= pixel.HasNext;
pixel.Next();
}
}
pictureBox1.Invalidate();
}
/// <summary>
/// CmdKey handler
/// </summary>
/// <param name="msg"></param>
/// <param name="keyData"></param>
/// <returns></returns>
[SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)]
protected override bool ProcessCmdKey(ref Message msg, Keys keyData) {
try {
switch (keyData) {
case Keys.Escape:
DialogResult = DialogResult.Cancel;
break;
case Keys.E:
MessageBox.Show(EnvironmentInfo.EnvironmentToString(true));
break;
case Keys.L:
try {
if (File.Exists(MainForm.LogFileLocation)) {
using (Process.Start("\"" + MainForm.LogFileLocation + "\"")) {
// nothing to do, just using dispose to cleanup
}
} else {
MessageBox.Show(@"Greenshot can't find the logfile, it should have been here: " + MainForm.LogFileLocation);
}
} catch (Exception) {
MessageBox.Show(@"Couldn't open the greenshot.log, it's located here: " + MainForm.LogFileLocation, @"Error opening greenshot.log", MessageBoxButtons.OK, MessageBoxIcon.Asterisk);
}
break;
case Keys.I:
try {
using (Process.Start("\"" + IniConfig.ConfigLocation + "\"")) {
}
} catch (Exception) {
MessageBox.Show(@"Couldn't open the greenshot.ini, it's located here: " + IniConfig.ConfigLocation, @"Error opening greenshot.ini", MessageBoxButtons.OK, MessageBoxIcon.Asterisk);
}
break;
default:
return base.ProcessCmdKey(ref msg, keyData);
}
} catch (Exception ex) {
Log.Error($"Error handling key '{keyData}'", ex);
}
return true;
}
}
}

View file

@ -1,54 +0,0 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
using System;
using System.Diagnostics;
using System.Windows.Forms;
using Greenshot.Configuration;
using GreenshotPlugin.Core;
namespace Greenshot.Forms {
public partial class BugReportForm : BaseForm {
private BugReportForm() {
//
// The InitializeComponent() call is required for Windows Forms designer support.
//
InitializeComponent();
ToFront = true;
}
public BugReportForm(string bugText) : this() {
textBoxDescription.Text = bugText;
}
private void LinkLblBugsLinkClicked(object sender, LinkLabelLinkClickedEventArgs e) {
openLink((LinkLabel)sender);
}
private void openLink(LinkLabel link) {
try {
link.LinkVisited = true;
Process.Start(link.Text);
} catch (Exception) {
MessageBox.Show(Language.GetFormattedString(LangKey.error_openlink, link.Text), Language.GetString(LangKey.error));
}
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -1,232 +0,0 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Globalization;
using System.Threading;
using System.Windows.Forms;
using Greenshot.Configuration;
using Greenshot.Controls;
using GreenshotPlugin.IniFile;
namespace Greenshot.Forms {
/// <summary>
/// Description of ColorDialog.
/// </summary>
public partial class ColorDialog : BaseForm {
private static readonly EditorConfiguration EditorConfig = IniConfig.GetIniSection<EditorConfiguration>();
private static ColorDialog _instance;
public ColorDialog() {
SuspendLayout();
InitializeComponent();
SuspendLayout();
CreateColorPalette(5, 5, 15, 15);
CreateLastUsedColorButtonRow(5, 190, 15, 15);
ResumeLayout();
UpdateRecentColorsButtonRow();
_instance = this;
}
public static ColorDialog GetInstance() => _instance;
private readonly List<Button> _colorButtons = new List<Button>();
private readonly List<Button> _recentColorButtons = new List<Button>();
private readonly ToolTip _toolTip = new ToolTip();
private bool _updateInProgress;
public Color Color {
get { return colorPanel.BackColor; }
set { PreviewColor(value, this); }
}
private void CreateColorPalette(int x, int y, int w, int h) {
CreateColorButtonColumn(255, 0, 0, x, y, w, h, 11);
x += w;
CreateColorButtonColumn(255, 255 / 2, 0, x, y, w, h, 11);
x += w;
CreateColorButtonColumn(255, 255, 0, x, y, w, h, 11);
x += w;
CreateColorButtonColumn(255 / 2, 255, 0, x, y, w, h, 11);
x += w;
CreateColorButtonColumn(0, 255, 0, x, y, w, h, 11);
x += w;
CreateColorButtonColumn(0, 255, 255 / 2, x, y, w, h, 11);
x += w;
CreateColorButtonColumn(0, 255, 255, x, y, w, h, 11);
x += w;
CreateColorButtonColumn(0, 255 / 2, 255, x, y, w, h, 11);
x += w;
CreateColorButtonColumn(0, 0, 255, x, y, w, h, 11);
x += w;
CreateColorButtonColumn(255 / 2, 0, 255, x, y, w, h, 11);
x += w;
CreateColorButtonColumn(255, 0, 255, x, y, w, h, 11);
x += w;
CreateColorButtonColumn(255, 0, 255 / 2, x, y, w, h, 11);
x += w + 5;
CreateColorButtonColumn(255 / 2, 255 / 2, 255 / 2, x, y, w, h, 11);
Controls.AddRange(_colorButtons.ToArray());
}
private void CreateColorButtonColumn(int red, int green, int blue, int x, int y, int w, int h, int shades) {
int shadedColorsNum = (shades - 1) / 2;
for (int i = 0; i <= shadedColorsNum; i++) {
_colorButtons.Add(CreateColorButton(red * i / shadedColorsNum, green * i / shadedColorsNum, blue * i / shadedColorsNum, x, y + i * h, w, h));
if (i > 0) _colorButtons.Add(CreateColorButton(red + (255 - red) * i / shadedColorsNum, green + (255 - green) * i / shadedColorsNum, blue + (255 - blue) * i / shadedColorsNum, x, y + (i + shadedColorsNum) * h, w, h));
}
}
private Button CreateColorButton(int red, int green, int blue, int x, int y, int w, int h) {
return CreateColorButton(Color.FromArgb(255, red, green, blue), x, y, w, h);
}
private Button CreateColorButton(Color color, int x, int y, int w, int h) {
Button b = new Button
{
BackColor = color,
FlatStyle = FlatStyle.Flat,
Location = new Point(x, y),
Size = new Size(w, h),
TabStop = false
};
b.FlatAppearance.BorderSize = 0;
b.Click += ColorButtonClick;
_toolTip.SetToolTip(b, ColorTranslator.ToHtml(color) + " | R:" + color.R + ", G:" + color.G + ", B:" + color.B);
return b;
}
private void CreateLastUsedColorButtonRow(int x, int y, int w, int h) {
for (int i = 0; i < 12; i++) {
Button b = CreateColorButton(Color.Transparent, x, y, w, h);
b.Enabled = false;
_recentColorButtons.Add(b);
x += w;
}
Controls.AddRange(_recentColorButtons.ToArray());
}
private void UpdateRecentColorsButtonRow() {
for (int i = 0; i < EditorConfig.RecentColors.Count && i < 12; i++) {
_recentColorButtons[i].BackColor = EditorConfig.RecentColors[i];
_recentColorButtons[i].Enabled = true;
}
}
private void PreviewColor(Color colorToPreview, Control trigger) {
_updateInProgress = true;
colorPanel.BackColor = colorToPreview;
if (trigger != textBoxHtmlColor) {
textBoxHtmlColor.Text = ColorTranslator.ToHtml(colorToPreview);
}
if (trigger != textBoxRed && trigger != textBoxGreen && trigger != textBoxBlue && trigger != textBoxAlpha) {
textBoxRed.Text = colorToPreview.R.ToString();
textBoxGreen.Text = colorToPreview.G.ToString();
textBoxBlue.Text = colorToPreview.B.ToString();
textBoxAlpha.Text = colorToPreview.A.ToString();
}
_updateInProgress = false;
}
private void AddToRecentColors(Color c) {
EditorConfig.RecentColors.Remove(c);
EditorConfig.RecentColors.Insert(0, c);
if (EditorConfig.RecentColors.Count > 12) {
EditorConfig.RecentColors.RemoveRange(12, EditorConfig.RecentColors.Count - 12);
}
UpdateRecentColorsButtonRow();
}
private void TextBoxHexadecimalTextChanged(object sender, EventArgs e) {
if (_updateInProgress) {
return;
}
TextBox textBox = (TextBox)sender;
string text = textBox.Text.Replace("#", string.Empty);
Color c;
if (int.TryParse(text, NumberStyles.AllowHexSpecifier, Thread.CurrentThread.CurrentCulture, out var i)) {
c = Color.FromArgb(i);
} else {
try
{
var knownColor = (KnownColor)Enum.Parse(typeof(KnownColor), text, true);
c = Color.FromKnownColor(knownColor);
} catch (Exception) {
return;
}
}
Color opaqueColor = Color.FromArgb(255, c.R, c.G, c.B);
PreviewColor(opaqueColor, textBox);
}
private void TextBoxRgbTextChanged(object sender, EventArgs e) {
if (_updateInProgress) {
return;
}
TextBox textBox = (TextBox)sender;
PreviewColor(Color.FromArgb(GetColorPartIntFromString(textBoxAlpha.Text), GetColorPartIntFromString(textBoxRed.Text), GetColorPartIntFromString(textBoxGreen.Text), GetColorPartIntFromString(textBoxBlue.Text)), textBox);
}
private void TextBoxGotFocus(object sender, EventArgs e) {
textBoxHtmlColor.SelectAll();
}
private void TextBoxKeyDown(object sender, KeyEventArgs e) {
if (e.KeyCode == Keys.Return || e.KeyCode == Keys.Enter) {
AddToRecentColors(colorPanel.BackColor);
}
}
private void ColorButtonClick(object sender, EventArgs e) {
Button b = (Button)sender;
PreviewColor(b.BackColor, b);
}
private void BtnTransparentClick(object sender, EventArgs e) {
ColorButtonClick(sender, e);
}
private void BtnApplyClick(object sender, EventArgs e) {
DialogResult = DialogResult.OK;
Hide();
AddToRecentColors(colorPanel.BackColor);
}
private int GetColorPartIntFromString(string s) {
int.TryParse(s, out var ret);
if (ret < 0)
{
ret = 0;
}
else if (ret > 255)
{
ret = 255;
}
return ret;
}
private void PipetteUsed(object sender, PipetteUsedArgs e) {
Color = e.Color;
}
}
}

View file

@ -1,97 +0,0 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
using System;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Imaging;
using System.Windows.Forms;
namespace Greenshot.Forms
{
public delegate void ColorPickerEventHandler(object o, ColorPickerEventArgs e);
public class ColorPickerToolStripButton : ToolStripButton
{
private Color _color;
public Point Offset = new Point(0,0);
public event ColorPickerEventHandler ColorPicked;
private readonly ColorDialog _cd;
public ColorPickerToolStripButton()
{
_cd = ColorDialog.GetInstance();
Click += ToolStripButton1Click;
}
public Color Color {
set {_color = value;Invalidate();}
get {return _color;}
}
protected override void OnPaint (PaintEventArgs e) {
base.OnPaint(e);
if(_color != null) {
// replace transparent color with selected color
Graphics g = e.Graphics;
//Graphics g = Graphics.FromImage(Image);
ColorMap[] colorMap = new ColorMap[1];
colorMap[0] = new ColorMap
{
OldColor = Color.Magenta,//this.ImageTransparentColor;
NewColor = _color
};
ImageAttributes attr = new ImageAttributes();
attr.SetRemapTable(colorMap);
Rectangle rect = new Rectangle(0, 0, Image.Width, Image.Height);
// todo find a way to retrieve transparency offset automatically
// for now, we use the public variable Offset to define this manually
rect.Offset(Offset.X,Offset.Y);
//Image.
Debug.WriteLine("paint!"+Text+": "+_color);
//ssif(color.Equals(Color.Transparent)) ((Bitmap)Image).MakeTransparent(Color.Magenta);
g.DrawImage(Image, rect, 0, 0, rect.Width, rect.Height, GraphicsUnit.Pixel, attr);
//this.Image.In
}
}
void ToolStripButton1Click(object sender, EventArgs e)
{
_cd.ShowDialog(Owner);
Color = _cd.Color;
ColorPicked?.Invoke(this, new ColorPickerEventArgs(Color));
}
}
public class ColorPickerEventArgs : EventArgs {
public Color Color;
public ColorPickerEventArgs(Color color) {
Color = color;
}
}
}

View file

@ -1,53 +0,0 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
using System;
using System.Drawing;
using System.Windows.Forms;
using GreenshotPlugin.Effects;
namespace Greenshot.Forms {
public partial class DropShadowSettingsForm : BaseForm {
private readonly DropShadowEffect _effect;
public DropShadowSettingsForm(DropShadowEffect effect) {
_effect = effect;
InitializeComponent();
ShowSettings();
}
/// <summary>
/// Apply the settings from the effect to the view
/// </summary>
private void ShowSettings() {
trackBar1.Value = (int)(_effect.Darkness * 40);
offsetX.Value = _effect.ShadowOffset.X;
offsetY.Value = _effect.ShadowOffset.Y;
thickness.Value = _effect.ShadowSize;
}
private void ButtonOK_Click(object sender, EventArgs e) {
_effect.Darkness = trackBar1.Value / (float)40;
_effect.ShadowOffset = new Point((int)offsetX.Value, (int)offsetY.Value);
_effect.ShadowSize = (int)thickness.Value;
DialogResult = DialogResult.OK;
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -1,92 +0,0 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
using System;
using System.Threading;
using System.Windows.Forms;
using GreenshotPlugin.Core;
using log4net;
namespace Greenshot.Forms {
/// <summary>
/// Description of LanguageDialog.
/// </summary>
public partial class LanguageDialog : Form {
private static readonly ILog LOG = LogManager.GetLogger(typeof(LanguageDialog));
private static LanguageDialog _uniqueInstance;
private bool _properOkPressed;
private LanguageDialog() {
//
// The InitializeComponent() call is required for Windows Forms designer support.
//
InitializeComponent();
Icon = GreenshotResources.GetGreenshotIcon();
Load += FormLoad;
FormClosing += PreventFormClose;
}
private void PreventFormClose(object sender, FormClosingEventArgs e) {
if(!_properOkPressed) {
e.Cancel = true;
}
}
public string SelectedLanguage => comboBoxLanguage.SelectedValue.ToString();
protected void FormLoad(object sender, EventArgs e) {
// Initialize the Language ComboBox
comboBoxLanguage.DisplayMember = "Description";
comboBoxLanguage.ValueMember = "Ietf";
// Set datasource last to prevent problems
// See: http://www.codeproject.com/KB/database/scomlistcontrolbinding.aspx?fid=111644
comboBoxLanguage.DataSource = Language.SupportedLanguages;
if (Language.CurrentLanguage != null) {
LOG.DebugFormat("Selecting {0}", Language.CurrentLanguage);
comboBoxLanguage.SelectedValue = Language.CurrentLanguage;
} else {
comboBoxLanguage.SelectedValue = Thread.CurrentThread.CurrentUICulture.Name;
}
// Close again when there is only one language, this shows the form briefly!
// But the use-case is not so interesting, only happens once, to invest a lot of time here.
if (Language.SupportedLanguages.Count == 1) {
comboBoxLanguage.SelectedValue = Language.SupportedLanguages[0].Ietf;
Language.CurrentLanguage = SelectedLanguage;
_properOkPressed = true;
Close();
}
}
private void BtnOKClick(object sender, EventArgs e) {
_properOkPressed = true;
// Fix for Bug #3431100
Language.CurrentLanguage = SelectedLanguage;
Close();
}
public static LanguageDialog GetInstance()
{
return _uniqueInstance ??= new LanguageDialog();
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -1,100 +0,0 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
using System;
using System.Windows.Forms;
using System.Drawing;
using GreenshotPlugin.UnmanagedHelpers;
namespace Greenshot.Forms {
/// <summary>
/// This code was supplied by Hi-Coder as a patch for Greenshot
/// Needed some modifications to be stable.
/// </summary>
public partial class MovableShowColorForm : Form {
public Color color {
get {
return preview.BackColor;
}
}
public MovableShowColorForm() {
InitializeComponent();
}
/// <summary>
/// Move the MovableShowColorForm to the specified location and display the color under the (current mouse) coordinates
/// </summary>
/// <param name="screenCoordinates">Coordinates</param>
public void MoveTo(Point screenCoordinates) {
Color c = GetPixelColor(screenCoordinates);
preview.BackColor = c;
html.Text = "#" + c.Name.Substring(2).ToUpper();
red.Text = string.Empty + c.R;
blue.Text = string.Empty + c.B;
green.Text = string.Empty + c.G;
alpha.Text = string.Empty + c.A;
Size cursorSize = Cursor.Current.Size;
Point hotspot = Cursor.Current.HotSpot;
Point zoomerLocation = new Point(screenCoordinates.X, screenCoordinates.Y);
zoomerLocation.X += cursorSize.Width + 5 - hotspot.X;
zoomerLocation.Y += cursorSize.Height + 5 - hotspot.Y;
foreach (Screen screen in Screen.AllScreens) {
Rectangle screenRectangle = screen.Bounds;
if (screen.Bounds.Contains(screenCoordinates)) {
if (zoomerLocation.X < screenRectangle.X) {
zoomerLocation.X = screenRectangle.X;
} else if (zoomerLocation.X + Width > screenRectangle.X + screenRectangle.Width) {
zoomerLocation.X = screenCoordinates.X - Width - 5 - hotspot.X;
}
if (zoomerLocation.Y < screenRectangle.Y) {
zoomerLocation.Y = screenRectangle.Y;
} else if (zoomerLocation.Y + Height > screenRectangle.Y + screenRectangle.Height) {
zoomerLocation.Y = screenCoordinates.Y - Height - 5 - hotspot.Y;
}
break;
}
}
Location = zoomerLocation;
Update();
}
/// <summary>
/// Get the color from the pixel on the screen at "x,y"
/// </summary>
/// <param name="screenCoordinates">Point with the coordinates</param>
/// <returns>Color at the specified screenCoordinates</returns>
private static Color GetPixelColor(Point screenCoordinates)
{
using SafeWindowDcHandle screenDC = SafeWindowDcHandle.FromDesktop();
try {
uint pixel = GDI32.GetPixel(screenDC, screenCoordinates.X, screenCoordinates.Y);
Color color = Color.FromArgb(255, (int)(pixel & 0xFF), (int)(pixel & 0xFF00) >> 8, (int)(pixel & 0xFF0000) >> 16);
return color;
} catch (Exception) {
return Color.Empty;
}
}
}
}

View file

@ -1,162 +0,0 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
using System;
using System.Drawing;
using System.Windows.Forms;
using GreenshotPlugin.Core;
using GreenshotPlugin.Effects;
namespace Greenshot.Forms {
/// <summary>
/// A form to set the resize settings
/// </summary>
public partial class ResizeSettingsForm : BaseForm {
private readonly ResizeEffect _effect;
private readonly string _valuePercent;
private double _newWidth, _newHeight;
public ResizeSettingsForm(ResizeEffect effect) {
_effect = effect;
InitializeComponent();
var valuePixel = Language.GetString("editor_resize_pixel");
_valuePercent = Language.GetString("editor_resize_percent");
combobox_width.Items.Add(valuePixel);
combobox_width.Items.Add(_valuePercent);
combobox_width.SelectedItem = valuePixel;
combobox_height.Items.Add(valuePixel);
combobox_height.Items.Add(_valuePercent);
combobox_height.SelectedItem = valuePixel;
textbox_width.Text = effect.Width.ToString();
textbox_height.Text = effect.Height.ToString();
_newWidth = effect.Width;
_newHeight = effect.Height;
combobox_width.SelectedIndexChanged += combobox_SelectedIndexChanged;
combobox_height.SelectedIndexChanged += combobox_SelectedIndexChanged;
checkbox_aspectratio.Checked = effect.MaintainAspectRatio;
}
private void buttonOK_Click(object sender, EventArgs e) {
if (_newWidth != _effect.Width || _newHeight != _effect.Height) {
_effect.Width = (int)_newWidth;
_effect.Height = (int)_newHeight;
_effect.MaintainAspectRatio = checkbox_aspectratio.Checked;
DialogResult = DialogResult.OK;
}
}
private static bool Validate(object sender) {
if (sender is TextBox textbox) {
if (!double.TryParse(textbox.Text, out var numberEntered)) {
textbox.BackColor = Color.Red;
return false;
}
textbox.BackColor = Color.White;
}
return true;
}
private void DisplayWidth() {
double displayValue;
if (_valuePercent.Equals(combobox_width.SelectedItem)) {
displayValue = _newWidth / _effect.Width * 100d;
} else {
displayValue = _newWidth;
}
textbox_width.Text = ((int)displayValue).ToString();
}
private void DisplayHeight() {
double displayValue;
if (_valuePercent.Equals(combobox_height.SelectedItem)) {
displayValue = _newHeight / _effect.Height * 100d;
} else {
displayValue = _newHeight;
}
textbox_height.Text = ((int)displayValue).ToString();
}
private void textbox_KeyUp(object sender, KeyEventArgs e) {
if (!Validate(sender)) {
return;
}
TextBox textbox = sender as TextBox;
if (string.IsNullOrEmpty(textbox?.Text)) {
return;
}
bool isWidth = textbox == textbox_width;
if (!checkbox_aspectratio.Checked) {
if (isWidth) {
_newWidth = double.Parse(textbox_width.Text);
} else {
_newHeight = double.Parse(textbox_height.Text);
}
return;
}
var isPercent = _valuePercent.Equals(isWidth ? combobox_width.SelectedItem : combobox_height.SelectedItem);
double percent;
if (isWidth) {
if (isPercent) {
percent = double.Parse(textbox_width.Text);
_newWidth = _effect.Width / 100d * percent;
} else {
_newWidth = double.Parse(textbox_width.Text);
percent = double.Parse(textbox_width.Text) / _effect.Width * 100d;
}
if (checkbox_aspectratio.Checked) {
_newHeight = _effect.Height / 100d * percent;
DisplayHeight();
}
} else {
if (isPercent) {
percent = double.Parse(textbox_height.Text);
_newHeight = _effect.Height / 100d * percent;
} else {
_newHeight = double.Parse(textbox_height.Text);
percent = double.Parse(textbox_height.Text) / _effect.Height * 100d;
}
if (checkbox_aspectratio.Checked) {
_newWidth = _effect.Width / 100d * percent;
DisplayWidth();
}
}
}
private void textbox_Validating(object sender, System.ComponentModel.CancelEventArgs e) {
Validate(sender);
}
/// <summary>
///
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void combobox_SelectedIndexChanged(object sender, EventArgs e) {
if (Validate(textbox_width)) {
DisplayWidth();
}
if (Validate(textbox_height)) {
DisplayHeight();
}
}
}
}

View file

@ -1,698 +0,0 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
using System;
using System.Collections;
using System.Collections.Generic;
using System.Drawing;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using System.Windows.Forms;
using Greenshot.Configuration;
using Greenshot.Destinations;
using Greenshot.Helpers;
using GreenshotPlugin.Controls;
using GreenshotPlugin.Core;
using GreenshotPlugin.IniFile;
using GreenshotPlugin.Interfaces;
using GreenshotPlugin.Interfaces.Plugin;
using GreenshotPlugin.UnmanagedHelpers;
using log4net;
namespace Greenshot.Forms {
/// <summary>
/// Description of SettingsForm.
/// </summary>
public partial class SettingsForm : BaseForm {
private static readonly ILog Log = LogManager.GetLogger(typeof(SettingsForm));
private readonly ToolTip _toolTip = new ToolTip();
private bool _inHotkey;
private int _daysbetweencheckPreviousValue;
public SettingsForm() {
InitializeComponent();
// Make sure we change the icon size depending on the scaling
DpiChanged += AdjustToDpi;
// Make sure the store isn't called to early, that's why we do it manually
ManualStoreFields = true;
}
/// <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)
{
DisplaySettings();
}
protected override void OnLoad(EventArgs e) {
base.OnLoad(e);
// Fix for Vista/XP differences
trackBarJpegQuality.BackColor = Environment.OSVersion.Version.Major >= 6 ? SystemColors.Window : SystemColors.Control;
// This makes it possible to still capture the settings screen
fullscreen_hotkeyControl.Enter += EnterHotkeyControl;
fullscreen_hotkeyControl.Leave += LeaveHotkeyControl;
window_hotkeyControl.Enter += EnterHotkeyControl;
window_hotkeyControl.Leave += LeaveHotkeyControl;
region_hotkeyControl.Enter += EnterHotkeyControl;
region_hotkeyControl.Leave += LeaveHotkeyControl;
ie_hotkeyControl.Enter += EnterHotkeyControl;
ie_hotkeyControl.Leave += LeaveHotkeyControl;
lastregion_hotkeyControl.Enter += EnterHotkeyControl;
lastregion_hotkeyControl.Leave += LeaveHotkeyControl;
// Changes for BUG-2077
numericUpDown_daysbetweencheck.ValueChanged += NumericUpDownDaysbetweencheckOnValueChanged;
_daysbetweencheckPreviousValue = (int) numericUpDown_daysbetweencheck.Value;
DisplayPluginTab();
UpdateUi();
ExpertSettingsEnableState(false);
DisplaySettings();
CheckSettings();
}
/// <summary>
/// This makes sure the check cannot be set to 1-6
/// </summary>
/// <param name="sender">object</param>
/// <param name="eventArgs">EventArgs</param>
private void NumericUpDownDaysbetweencheckOnValueChanged(object sender, EventArgs eventArgs)
{
int currentValue = (int)numericUpDown_daysbetweencheck.Value;
// Check if we can into the forbidden range
if (currentValue > 0 && currentValue < 7)
{
if (_daysbetweencheckPreviousValue <= currentValue)
{
numericUpDown_daysbetweencheck.Value = 7;
}
else
{
numericUpDown_daysbetweencheck.Value = 0;
}
}
if ((int)numericUpDown_daysbetweencheck.Value < 0)
{
numericUpDown_daysbetweencheck.Value = 0;
}
if ((int)numericUpDown_daysbetweencheck.Value > 365)
{
numericUpDown_daysbetweencheck.Value = 365;
}
_daysbetweencheckPreviousValue = (int)numericUpDown_daysbetweencheck.Value;
}
private void EnterHotkeyControl(object sender, EventArgs e) {
HotkeyControl.UnregisterHotkeys();
_inHotkey = true;
}
private void LeaveHotkeyControl(object sender, EventArgs e) {
MainForm.RegisterHotkeys();
_inHotkey = false;
}
protected override bool ProcessCmdKey(ref Message msg, Keys keyData) {
switch (keyData) {
case Keys.Escape:
if (!_inHotkey) {
DialogResult = DialogResult.Cancel;
} else {
return base.ProcessCmdKey(ref msg, keyData);
}
break;
default:
return base.ProcessCmdKey(ref msg, keyData);
}
return true;
}
/// <summary>
/// This is a method to populate the ComboBox
/// with the items from the enumeration
/// </summary>
/// <param name="comboBox">ComboBox to populate</param>
/// <param name="availableValues"></param>
/// <param name="selectedValue"></param>
private void PopulateComboBox<TEnum>(ComboBox comboBox, TEnum[] availableValues, TEnum selectedValue) where TEnum : struct {
comboBox.Items.Clear();
foreach(TEnum enumValue in availableValues) {
comboBox.Items.Add(Language.Translate(enumValue));
}
comboBox.SelectedItem = Language.Translate(selectedValue);
}
/// <summary>
/// Get the selected enum value from the combobox, uses generics
/// </summary>
/// <param name="comboBox">Combobox to get the value from</param>
/// <returns>The generics value of the combobox</returns>
private TEnum GetSelected<TEnum>(ComboBox comboBox) {
string enumTypeName = typeof(TEnum).Name;
string selectedValue = comboBox.SelectedItem as string;
TEnum[] availableValues = (TEnum[])Enum.GetValues(typeof(TEnum));
TEnum returnValue = availableValues[0];
foreach(TEnum enumValue in availableValues) {
string translation = Language.GetString(enumTypeName + "." + enumValue);
if (translation.Equals(selectedValue)) {
returnValue = enumValue;
break;
}
}
return returnValue;
}
private void SetWindowCaptureMode(WindowCaptureMode selectedWindowCaptureMode) {
WindowCaptureMode[] availableModes;
if (!DWM.IsDwmEnabled) {
// Remove DWM from configuration, as DWM is disabled!
if (coreConfiguration.WindowCaptureMode == WindowCaptureMode.Aero || coreConfiguration.WindowCaptureMode == WindowCaptureMode.AeroTransparent) {
coreConfiguration.WindowCaptureMode = WindowCaptureMode.GDI;
}
availableModes = new[]{WindowCaptureMode.Auto, WindowCaptureMode.Screen, WindowCaptureMode.GDI};
} else {
availableModes = new[]{WindowCaptureMode.Auto, WindowCaptureMode.Screen, WindowCaptureMode.GDI, WindowCaptureMode.Aero, WindowCaptureMode.AeroTransparent};
}
PopulateComboBox(combobox_window_capture_mode, availableModes, selectedWindowCaptureMode);
}
private void DisplayPluginTab() {
if (!SimpleServiceProvider.Current.GetAllInstances<IGreenshotPlugin>().Any())
{
tabcontrol.TabPages.Remove(tab_plugins);
return;
}
// Draw the Plugin listview
listview_plugins.BeginUpdate();
listview_plugins.Items.Clear();
listview_plugins.Columns.Clear();
string[] columns = {
Language.GetString("settings_plugins_name"),
Language.GetString("settings_plugins_version"),
Language.GetString("settings_plugins_createdby"),
Language.GetString("settings_plugins_dllpath")};
foreach (string column in columns) {
listview_plugins.Columns.Add(column);
}
PluginHelper.Instance.FillListView(listview_plugins);
// Maximize Column size!
for (int i = 0; i < listview_plugins.Columns.Count; i++) {
listview_plugins.AutoResizeColumn(i, ColumnHeaderAutoResizeStyle.ColumnContent);
int width = listview_plugins.Columns[i].Width;
listview_plugins.AutoResizeColumn(i, ColumnHeaderAutoResizeStyle.HeaderSize);
if (width > listview_plugins.Columns[i].Width) {
listview_plugins.Columns[i].Width = width;
}
}
listview_plugins.EndUpdate();
listview_plugins.Refresh();
// Disable the configure button, it will be enabled when a plugin is selected AND isConfigurable
button_pluginconfigure.Enabled = false;
}
/// <summary>
/// Update the UI to reflect the language and other text settings
/// </summary>
private void UpdateUi() {
if (coreConfiguration.HideExpertSettings) {
tabcontrol.Controls.Remove(tab_expert);
}
_toolTip.SetToolTip(label_language, Language.GetString(LangKey.settings_tooltip_language));
_toolTip.SetToolTip(label_storagelocation, Language.GetString(LangKey.settings_tooltip_storagelocation));
_toolTip.SetToolTip(label_screenshotname, Language.GetString(LangKey.settings_tooltip_filenamepattern));
_toolTip.SetToolTip(label_primaryimageformat, Language.GetString(LangKey.settings_tooltip_primaryimageformat));
// Removing, otherwise we keep getting the event multiple times!
combobox_language.SelectedIndexChanged -= Combobox_languageSelectedIndexChanged;
// Initialize the Language ComboBox
combobox_language.DisplayMember = "Description";
combobox_language.ValueMember = "Ietf";
// Set datasource last to prevent problems
// See: http://www.codeproject.com/KB/database/scomlistcontrolbinding.aspx?fid=111644
combobox_language.DataSource = Language.SupportedLanguages;
if (Language.CurrentLanguage != null) {
combobox_language.SelectedValue = Language.CurrentLanguage;
}
// Delaying the SelectedIndexChanged events untill all is initiated
combobox_language.SelectedIndexChanged += Combobox_languageSelectedIndexChanged;
UpdateDestinationDescriptions();
UpdateClipboardFormatDescriptions();
}
// Check the settings and somehow visibly mark when something is incorrect
private bool CheckSettings() {
return CheckFilenamePattern() && CheckStorageLocationPath();
}
private bool CheckFilenamePattern() {
string filename = FilenameHelper.GetFilenameFromPattern(textbox_screenshotname.Text, coreConfiguration.OutputFileFormat, null);
// we allow dynamically created subfolders, need to check for them, too
string[] pathParts = filename.Split(Path.DirectorySeparatorChar);
string filenamePart = pathParts[pathParts.Length-1];
var settingsOk = FilenameHelper.IsFilenameValid(filenamePart);
for (int i = 0; settingsOk && i<pathParts.Length-1; i++) {
settingsOk = FilenameHelper.IsDirectoryNameValid(pathParts[i]);
}
DisplayTextBoxValidity(textbox_screenshotname, settingsOk);
return settingsOk;
}
private bool CheckStorageLocationPath() {
bool settingsOk = Directory.Exists(FilenameHelper.FillVariables(textbox_storagelocation.Text, false));
DisplayTextBoxValidity(textbox_storagelocation, settingsOk);
return settingsOk;
}
private void DisplayTextBoxValidity(GreenshotTextBox textbox, bool valid) {
if (valid) {
// "Added" feature #3547158
textbox.BackColor = Environment.OSVersion.Version.Major >= 6 ? SystemColors.Window : SystemColors.Control;
} else {
textbox.BackColor = Color.Red;
}
}
private void FilenamePatternChanged(object sender, EventArgs e) {
CheckFilenamePattern();
}
private void StorageLocationChanged(object sender, EventArgs e) {
CheckStorageLocationPath();
}
/// <summary>
/// Show all destination descriptions in the current language
/// </summary>
private void UpdateDestinationDescriptions() {
foreach (ListViewItem item in listview_destinations.Items) {
if (item.Tag is IDestination destinationFromTag) {
item.Text = destinationFromTag.Description;
}
}
}
/// <summary>
/// Show all clipboard format descriptions in the current language
/// </summary>
private void UpdateClipboardFormatDescriptions() {
foreach(ListViewItem item in listview_clipboardformats.Items) {
ClipboardFormat cf = (ClipboardFormat) item.Tag;
item.Text = Language.Translate(cf);
}
}
/// <summary>
/// Build the view with all the destinations
/// </summary>
private void DisplayDestinations() {
bool destinationsEnabled = true;
if (coreConfiguration.Values.ContainsKey("Destinations")) {
destinationsEnabled = !coreConfiguration.Values["Destinations"].IsFixed;
}
checkbox_picker.Checked = false;
listview_destinations.Items.Clear();
var scaledIconSize = DpiHelper.ScaleWithDpi(coreConfiguration.IconSize, DpiHelper.GetDpi(Handle));
listview_destinations.ListViewItemSorter = new ListviewWithDestinationComparer();
ImageList imageList = new ImageList {ImageSize = scaledIconSize};
listview_destinations.SmallImageList = imageList;
int imageNr = -1;
foreach (IDestination currentDestination in DestinationHelper.GetAllDestinations()) {
Image destinationImage = currentDestination.DisplayIcon;
if (destinationImage != null) {
imageList.Images.Add(currentDestination.DisplayIcon);
imageNr++;
}
if (PickerDestination.DESIGNATION.Equals(currentDestination.Designation)) {
checkbox_picker.Checked = coreConfiguration.OutputDestinations.Contains(currentDestination.Designation);
checkbox_picker.Text = currentDestination.Description;
} else {
ListViewItem item;
if (destinationImage != null) {
item = listview_destinations.Items.Add(currentDestination.Description, imageNr);
} else {
item = listview_destinations.Items.Add(currentDestination.Description);
}
item.Tag = currentDestination;
item.Checked = coreConfiguration.OutputDestinations.Contains(currentDestination.Designation);
}
}
if (checkbox_picker.Checked) {
listview_destinations.Enabled = false;
foreach (int index in listview_destinations.CheckedIndices) {
ListViewItem item = listview_destinations.Items[index];
item.Checked = false;
}
}
checkbox_picker.Enabled = destinationsEnabled;
listview_destinations.Enabled = destinationsEnabled;
}
private void DisplaySettings() {
colorButton_window_background.SelectedColor = coreConfiguration.DWMBackgroundColor;
// Expert mode, the clipboard formats
foreach (ClipboardFormat clipboardFormat in Enum.GetValues(typeof(ClipboardFormat))) {
ListViewItem item = listview_clipboardformats.Items.Add(Language.Translate(clipboardFormat));
item.Tag = clipboardFormat;
item.Checked = coreConfiguration.ClipboardFormats.Contains(clipboardFormat);
}
if (Language.CurrentLanguage != null) {
combobox_language.SelectedValue = Language.CurrentLanguage;
}
// Disable editing when the value is fixed
combobox_language.Enabled = !coreConfiguration.Values["Language"].IsFixed;
textbox_storagelocation.Text = FilenameHelper.FillVariables(coreConfiguration.OutputFilePath, false);
// Disable editing when the value is fixed
textbox_storagelocation.Enabled = !coreConfiguration.Values["OutputFilePath"].IsFixed;
SetWindowCaptureMode(coreConfiguration.WindowCaptureMode);
// Disable editing when the value is fixed
combobox_window_capture_mode.Enabled = !coreConfiguration.CaptureWindowsInteractive && !coreConfiguration.Values["WindowCaptureMode"].IsFixed;
radiobuttonWindowCapture.Checked = !coreConfiguration.CaptureWindowsInteractive;
trackBarJpegQuality.Value = coreConfiguration.OutputFileJpegQuality;
trackBarJpegQuality.Enabled = !coreConfiguration.Values["OutputFileJpegQuality"].IsFixed;
textBoxJpegQuality.Text = coreConfiguration.OutputFileJpegQuality+"%";
DisplayDestinations();
numericUpDownWaitTime.Value = coreConfiguration.CaptureDelay >=0?coreConfiguration.CaptureDelay:0;
numericUpDownWaitTime.Enabled = !coreConfiguration.Values["CaptureDelay"].IsFixed;
if (IniConfig.IsPortable) {
checkbox_autostartshortcut.Visible = false;
checkbox_autostartshortcut.Checked = false;
} else {
// Autostart checkbox logic.
if (StartupHelper.HasRunAll()) {
// Remove runUser if we already have a run under all
StartupHelper.DeleteRunUser();
checkbox_autostartshortcut.Enabled = StartupHelper.CanWriteRunAll();
checkbox_autostartshortcut.Checked = true; // We already checked this
} else if (StartupHelper.IsInStartupFolder()) {
checkbox_autostartshortcut.Enabled = false;
checkbox_autostartshortcut.Checked = true; // We already checked this
} else {
// No run for all, enable the checkbox and set it to true if the current user has a key
checkbox_autostartshortcut.Enabled = StartupHelper.CanWriteRunUser();
checkbox_autostartshortcut.Checked = StartupHelper.HasRunUser();
}
}
numericUpDown_daysbetweencheck.Value = coreConfiguration.UpdateCheckInterval;
numericUpDown_daysbetweencheck.Enabled = !coreConfiguration.Values["UpdateCheckInterval"].IsFixed;
var scaledIconSize = DpiHelper.ScaleWithDpi(coreConfiguration.IconSize, DpiHelper.GetDpi(Handle));
numericUpdownIconSize.Value = scaledIconSize.Width / 16 * 16;
CheckDestinationSettings();
}
private void SaveSettings() {
if (combobox_language.SelectedItem != null) {
string newLang = combobox_language.SelectedValue.ToString();
if (!string.IsNullOrEmpty(newLang)) {
coreConfiguration.Language = combobox_language.SelectedValue.ToString();
}
}
// retrieve the set clipboard formats
var clipboardFormats = new List<ClipboardFormat>();
foreach (int index in listview_clipboardformats.CheckedIndices) {
var item = listview_clipboardformats.Items[index];
if (item.Checked) {
clipboardFormats.Add((ClipboardFormat)item.Tag);
}
}
coreConfiguration.ClipboardFormats = clipboardFormats;
coreConfiguration.WindowCaptureMode = GetSelected<WindowCaptureMode>(combobox_window_capture_mode);
if (!FilenameHelper.FillVariables(coreConfiguration.OutputFilePath, false).Equals(textbox_storagelocation.Text)) {
coreConfiguration.OutputFilePath = textbox_storagelocation.Text;
}
coreConfiguration.OutputFileJpegQuality = trackBarJpegQuality.Value;
List<string> destinations = new List<string>();
if (checkbox_picker.Checked) {
destinations.Add(PickerDestination.DESIGNATION);
}
foreach(int index in listview_destinations.CheckedIndices) {
ListViewItem item = listview_destinations.Items[index];
if (item.Checked && item.Tag is IDestination destinationFromTag) {
destinations.Add(destinationFromTag.Designation);
}
}
coreConfiguration.OutputDestinations = destinations;
coreConfiguration.CaptureDelay = (int)numericUpDownWaitTime.Value;
coreConfiguration.DWMBackgroundColor = colorButton_window_background.SelectedColor;
coreConfiguration.UpdateCheckInterval = (int)numericUpDown_daysbetweencheck.Value;
coreConfiguration.IconSize = new Size((int)numericUpdownIconSize.Value, (int)numericUpdownIconSize.Value);
try {
if (checkbox_autostartshortcut.Checked) {
// It's checked, so we set the RunUser if the RunAll isn't set.
// Do this every time, so the executable is correct.
if (!StartupHelper.HasRunAll()) {
StartupHelper.SetRunUser();
}
} else {
// Delete both settings if it's unchecked
if (StartupHelper.HasRunAll()) {
StartupHelper.DeleteRunAll();
}
if (StartupHelper.HasRunUser()) {
StartupHelper.DeleteRunUser();
}
}
} catch (Exception e) {
Log.Warn("Problem checking registry, ignoring for now: ", e);
}
}
private void Settings_cancelClick(object sender, EventArgs e) {
DialogResult = DialogResult.Cancel;
}
private void Settings_okayClick(object sender, EventArgs e) {
if (CheckSettings()) {
HotkeyControl.UnregisterHotkeys();
SaveSettings();
StoreFields();
MainForm.RegisterHotkeys();
// Make sure the current language & settings are reflected in the Main-context menu
var mainForm = SimpleServiceProvider.Current.GetInstance<MainForm>();
mainForm?.UpdateUi();
DialogResult = DialogResult.OK;
} else {
tabcontrol.SelectTab(tab_output);
}
}
private void BrowseClick(object sender, EventArgs e) {
// Get the storage location and replace the environment variables
folderBrowserDialog1.SelectedPath = FilenameHelper.FillVariables(textbox_storagelocation.Text, false);
if (folderBrowserDialog1.ShowDialog() == DialogResult.OK) {
// Only change if there is a change, otherwise we might overwrite the environment variables
if (folderBrowserDialog1.SelectedPath != null && !folderBrowserDialog1.SelectedPath.Equals(FilenameHelper.FillVariables(textbox_storagelocation.Text, false))) {
textbox_storagelocation.Text = folderBrowserDialog1.SelectedPath;
}
}
}
private void TrackBarJpegQualityScroll(object sender, EventArgs e) {
textBoxJpegQuality.Text = trackBarJpegQuality.Value.ToString(CultureInfo.InvariantCulture);
}
private void BtnPatternHelpClick(object sender, EventArgs e) {
string filenamepatternText = Language.GetString(LangKey.settings_message_filenamepattern);
// Convert %NUM% to ${NUM} for old language files!
filenamepatternText = Regex.Replace(filenamepatternText, "%([a-zA-Z_0-9]+)%", @"${$1}");
MessageBox.Show(filenamepatternText, Language.GetString(LangKey.settings_filenamepattern));
}
private void Listview_pluginsSelectedIndexChanged(object sender, EventArgs e) {
button_pluginconfigure.Enabled = PluginHelper.Instance.IsSelectedItemConfigurable(listview_plugins);
}
private void Button_pluginconfigureClick(object sender, EventArgs e) {
PluginHelper.Instance.ConfigureSelectedItem(listview_plugins);
}
private void Combobox_languageSelectedIndexChanged(object sender, EventArgs e) {
// Get the combobox values BEFORE changing the language
//EmailFormat selectedEmailFormat = GetSelected<EmailFormat>(combobox_emailformat);
WindowCaptureMode selectedWindowCaptureMode = GetSelected<WindowCaptureMode>(combobox_window_capture_mode);
if (combobox_language.SelectedItem != null) {
Log.Debug("Setting language to: " + (string)combobox_language.SelectedValue);
Language.CurrentLanguage = (string)combobox_language.SelectedValue;
}
// Reflect language changes to the settings form
UpdateUi();
// Reflect Language changes form
ApplyLanguage();
// Update the email & windows capture mode
//SetEmailFormat(selectedEmailFormat);
SetWindowCaptureMode(selectedWindowCaptureMode);
}
private void Combobox_window_capture_modeSelectedIndexChanged(object sender, EventArgs e) {
int windowsVersion = Environment.OSVersion.Version.Major;
WindowCaptureMode mode = GetSelected<WindowCaptureMode>(combobox_window_capture_mode);
if (windowsVersion >= 6) {
switch (mode) {
case WindowCaptureMode.Aero:
colorButton_window_background.Visible = true;
return;
}
}
colorButton_window_background.Visible = false;
}
/// <summary>
/// Check the destination settings
/// </summary>
private void CheckDestinationSettings() {
bool clipboardDestinationChecked = false;
bool pickerSelected = checkbox_picker.Checked;
bool destinationsEnabled = true;
if (coreConfiguration.Values.ContainsKey("Destinations")) {
destinationsEnabled = !coreConfiguration.Values["Destinations"].IsFixed;
}
listview_destinations.Enabled = destinationsEnabled;
foreach(int index in listview_destinations.CheckedIndices) {
ListViewItem item = listview_destinations.Items[index];
if (item.Tag is IDestination destinationFromTag && destinationFromTag.Designation.Equals(ClipboardDestination.DESIGNATION)) {
clipboardDestinationChecked = true;
break;
}
}
if (pickerSelected) {
listview_destinations.Enabled = false;
foreach(int index in listview_destinations.CheckedIndices) {
ListViewItem item = listview_destinations.Items[index];
item.Checked = false;
}
} else {
// Prevent multiple clipboard settings at once, see bug #3435056
if (clipboardDestinationChecked) {
checkbox_copypathtoclipboard.Checked = false;
checkbox_copypathtoclipboard.Enabled = false;
} else {
checkbox_copypathtoclipboard.Enabled = true;
}
}
}
private void DestinationsCheckStateChanged(object sender, EventArgs e) {
CheckDestinationSettings();
}
protected override void OnFieldsFilled() {
// the color radio button is not actually bound to a setting, but checked when monochrome/grayscale are not checked
if(!radioBtnGrayScale.Checked && !radioBtnMonochrome.Checked) {
radioBtnColorPrint.Checked = true;
}
}
/// <summary>
/// Set the enable state of the expert settings
/// </summary>
/// <param name="state"></param>
private void ExpertSettingsEnableState(bool state) {
listview_clipboardformats.Enabled = state;
checkbox_autoreducecolors.Enabled = state;
checkbox_optimizeforrdp.Enabled = state;
checkbox_thumbnailpreview.Enabled = state;
textbox_footerpattern.Enabled = state;
textbox_counter.Enabled = state;
checkbox_suppresssavedialogatclose.Enabled = state;
checkbox_checkunstableupdates.Enabled = state;
checkbox_minimizememoryfootprint.Enabled = state;
checkbox_reuseeditor.Enabled = state;
}
/// <summary>
/// Called if the "I know what I am doing" on the settings form is changed
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void Checkbox_enableexpert_CheckedChanged(object sender, EventArgs e) {
if (sender is CheckBox checkBox) {
ExpertSettingsEnableState(checkBox.Checked);
}
}
private void Radiobutton_CheckedChanged(object sender, EventArgs e) {
combobox_window_capture_mode.Enabled = radiobuttonWindowCapture.Checked;
}
}
public class ListviewWithDestinationComparer : IComparer {
public int Compare(object x, object y) {
if (!(x is ListViewItem)) {
return 0;
}
if (!(y is ListViewItem)) {
return 0;
}
ListViewItem l1 = (ListViewItem)x;
ListViewItem l2 = (ListViewItem)y;
IDestination firstDestination = l1.Tag as IDestination;
if (!(l2.Tag is IDestination secondDestination)) {
return 1;
}
if (firstDestination != null && firstDestination.Priority == secondDestination.Priority) {
return string.Compare(firstDestination.Description, secondDestination.Description, StringComparison.Ordinal);
}
if (firstDestination != null) {
return firstDestination.Priority - secondDestination.Priority;
}
return 0;
}
}
}

View file

@ -1,156 +0,0 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
using GreenshotPlugin.Core;
using System;
using System.Collections;
using System.Drawing;
using System.Windows.Forms;
using GreenshotPlugin.IniFile;
namespace Greenshot.Forms {
/// <summary>
/// the ToolStripMenuSelectList makes it possible to have a single or multi-check menu
/// </summary>
public sealed class ToolStripMenuSelectList : ToolStripMenuItem {
private static readonly CoreConfiguration CoreConfig = IniConfig.GetIniSection<CoreConfiguration>();
private readonly bool _multiCheckAllowed;
private bool _updateInProgress;
private static Image _defaultImage;
/// <summary>
/// Occurs when one of the list's child element's Checked state changes.
/// </summary>
public new event EventHandler CheckedChanged;
public object Identifier {
get;
private set;
}
public ToolStripMenuSelectList(object identifier, bool allowMultiCheck) {
Identifier = identifier;
CheckOnClick = false;
_multiCheckAllowed = allowMultiCheck;
if (_defaultImage == null || _defaultImage.Size != CoreConfig.ScaledIconSize) {
_defaultImage?.Dispose();
_defaultImage = ImageHelper.CreateEmpty(CoreConfig.ScaledIconSize.Width, CoreConfig.ScaledIconSize.Height, System.Drawing.Imaging.PixelFormat.Format32bppArgb, Color.Transparent, 96f, 96f);
}
Image = _defaultImage;
}
public ToolStripMenuSelectList() : this(null,false) {}
private void ItemCheckStateChanged(object sender, EventArgs e) {
if (_updateInProgress) {
return;
}
var toolStripMenuSelectListItem = (ToolStripMenuSelectListItem)sender;
_updateInProgress = true;
if (toolStripMenuSelectListItem.Checked && !_multiCheckAllowed) {
UncheckAll();
toolStripMenuSelectListItem.Checked = true;
}
_updateInProgress = false;
CheckedChanged?.Invoke(this, new ItemCheckedChangedEventArgs(toolStripMenuSelectListItem));
}
/// <summary>
/// adds an item to the select list
/// </summary>
/// <param name="label">the label to be displayed</param>
/// <param name="image">the icon to be displayed</param>
/// <param name="data">the data to be returned when an item is queried</param>
/// <param name="isChecked">whether the item is initially checked</param>
public void AddItem(string label, Image image, object data, bool isChecked) {
var toolStripMenuSelectListItem = new ToolStripMenuSelectListItem
{
Text = label
};
if (image == null) {
image = _defaultImage;
}
toolStripMenuSelectListItem.DisplayStyle = ToolStripItemDisplayStyle.Text;
toolStripMenuSelectListItem.Image = image;
toolStripMenuSelectListItem.CheckOnClick = true;
toolStripMenuSelectListItem.CheckStateChanged += ItemCheckStateChanged;
toolStripMenuSelectListItem.Data = data;
if (isChecked) {
if (!_multiCheckAllowed) {
_updateInProgress = true;
UncheckAll();
_updateInProgress = false;
}
toolStripMenuSelectListItem.Checked = true;
}
DropDownItems.Add(toolStripMenuSelectListItem);
}
/// <summary>
/// adds an item to the select list
/// </summary>
/// <param name="label">the label to be displayed</param>
/// <param name="data">the data to be returned when an item is queried</param>
/// <param name="isChecked">whether the item is initially checked</param>
public void AddItem(string label, object data, bool isChecked) {
AddItem(label, null, data, isChecked);
}
/// <summary>
/// unchecks all items of the list
/// </summary>
public void UncheckAll() {
IEnumerator items = DropDownItems.GetEnumerator();
while (items.MoveNext())
{
var toolStripMenuSelectListItem = (ToolStripMenuSelectListItem)items.Current;
if (toolStripMenuSelectListItem != null)
{
toolStripMenuSelectListItem.Checked = false;
}
}
}
}
/// <summary>
/// Event class for the CheckedChanged event in the ToolStripMenuSelectList
/// </summary>
public class ItemCheckedChangedEventArgs : EventArgs {
public ToolStripMenuSelectListItem Item {
get;
set;
}
public ItemCheckedChangedEventArgs(ToolStripMenuSelectListItem item) {
Item = item;
}
}
/// <summary>
/// Wrapper around the ToolStripMenuItem, which can contain an object
/// Also the Checked property hides the normal checked property so we can render our own check
/// </summary>
public class ToolStripMenuSelectListItem : ToolStripMenuItem {
public object Data {
get;
set;
}
}
}

View file

@ -1,102 +0,0 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
using System;
using System.Drawing;
using System.Windows.Forms;
using GreenshotPlugin.Effects;
namespace Greenshot.Forms {
public partial class TornEdgeSettingsForm : BaseForm {
private readonly TornEdgeEffect _effect;
public TornEdgeSettingsForm(TornEdgeEffect effect) {
_effect = effect;
InitializeComponent();
ShowSettings();
}
private void ShowSettings() {
shadowCheckbox.Checked = _effect.GenerateShadow;
// Fix to prevent BUG-1753
shadowDarkness.Value = Math.Max(shadowDarkness.Minimum, Math.Min(shadowDarkness.Maximum, (int)(_effect.Darkness * shadowDarkness.Maximum)));
offsetX.Value = _effect.ShadowOffset.X;
offsetY.Value = _effect.ShadowOffset.Y;
toothsize.Value = _effect.ToothHeight;
verticaltoothrange.Value = _effect.VerticalToothRange;
horizontaltoothrange.Value = _effect.HorizontalToothRange;
top.Checked = _effect.Edges[0];
right.Checked = _effect.Edges[1];
bottom.Checked = _effect.Edges[2];
left.Checked = _effect.Edges[3];
}
private void ButtonOK_Click(object sender, EventArgs e) {
_effect.Darkness = shadowDarkness.Value / (float)40;
_effect.ShadowOffset = new Point((int)offsetX.Value, (int)offsetY.Value);
_effect.ShadowSize = (int)thickness.Value;
_effect.ToothHeight = (int)toothsize.Value;
_effect.VerticalToothRange = (int)verticaltoothrange.Value;
_effect.HorizontalToothRange = (int)horizontaltoothrange.Value;
_effect.Edges = new[] { top.Checked, right.Checked, bottom.Checked, left.Checked };
_effect.GenerateShadow = shadowCheckbox.Checked;
DialogResult = DialogResult.OK;
}
private void ShadowCheckbox_CheckedChanged(object sender, EventArgs e) {
thickness.Enabled = shadowCheckbox.Checked;
offsetX.Enabled = shadowCheckbox.Checked;
offsetY.Enabled = shadowCheckbox.Checked;
shadowDarkness.Enabled = shadowCheckbox.Checked;
}
private void all_CheckedChanged(object sender, EventArgs e) {
AnySideChangeChecked(top, all.Checked);
AnySideChangeChecked(right, all.Checked);
AnySideChangeChecked(bottom, all.Checked);
AnySideChangeChecked(left, all.Checked);
}
private void AnySideCheckedChanged(object sender, EventArgs e) {
all.CheckedChanged -= all_CheckedChanged;
all.Checked = top.Checked && right.Checked && bottom.Checked && left.Checked;
all.CheckedChanged += all_CheckedChanged;
}
/// <summary>
/// changes the Checked property of top/right/bottom/left checkboxes without triggering AnySideCheckedChange
/// </summary>
/// <param name="cb">Checkbox to change Checked</param>
/// <param name="status">true to check</param>
private void AnySideChangeChecked(CheckBox cb, bool status) {
if (status != cb.Checked) {
cb.CheckedChanged -= AnySideCheckedChanged;
cb.Checked = status;
cb.CheckedChanged += AnySideCheckedChanged;
}
}
private void TornEdgeSettingsForm_Load(object sender, EventArgs e)
{
}
}
}

View file

@ -1,62 +0,0 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
using System;
using System.Globalization;
using System.Net;
using System.Reflection;
using Greenshot.Forms;
// Remove AppendPrivatePath warning:
#pragma warning disable 0618
namespace Greenshot {
/// <summary>
/// Description of Main.
/// </summary>
public class GreenshotMain {
static GreenshotMain() {
AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;
}
private static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args) {
Assembly ayResult = null;
string sShortAssemblyName = args.Name.Split(',')[0];
Assembly[] ayAssemblies = AppDomain.CurrentDomain.GetAssemblies();
foreach (Assembly ayAssembly in ayAssemblies)
{
if (sShortAssemblyName != ayAssembly.FullName.Split(',')[0]) continue;
ayResult = ayAssembly;
break;
}
return ayResult;
}
[STAThread]
public static void Main(string[] args)
{
// Enable TLS 1.2 support
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12;
CultureInfo.DefaultThreadCurrentCulture = CultureInfo.InvariantCulture;
CultureInfo.DefaultThreadCurrentUICulture = CultureInfo.InvariantCulture;
MainForm.Start(args);
}
}
}

View file

@ -1,86 +0,0 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
using GreenshotPlugin.Core;
using System.Diagnostics;
using System.Net;
using log4net;
namespace Greenshot.Help
{
/// <summary>
/// Description of HelpFileLoader.
/// </summary>
public static class HelpFileLoader
{
private static readonly ILog Log = LogManager.GetLogger(typeof(HelpFileLoader));
private const string ExtHelpUrl = @"http://getgreenshot.org/help/";
public static void LoadHelp() {
string uri = FindOnlineHelpUrl(Language.CurrentLanguage) ?? Language.HelpFilePath;
Process.Start(uri);
}
/// <returns>URL of help file in selected ietf, or (if not present) default ietf, or null (if not present, too. probably indicating that there is no internet connection)</returns>
private static string FindOnlineHelpUrl(string currentIETF) {
string ret = null;
string extHelpUrlForCurrrentIETF = ExtHelpUrl;
if(!currentIETF.Equals("en-US")) {
extHelpUrlForCurrrentIETF += currentIETF.ToLower() + "/";
}
HttpStatusCode? httpStatusCode = GetHttpStatus(extHelpUrlForCurrrentIETF);
if(httpStatusCode == HttpStatusCode.OK) {
ret = extHelpUrlForCurrrentIETF;
} else if(httpStatusCode != null && !extHelpUrlForCurrrentIETF.Equals(ExtHelpUrl)) {
Log.DebugFormat("Localized online help not found at {0}, will try {1} as fallback", extHelpUrlForCurrrentIETF, ExtHelpUrl);
httpStatusCode = GetHttpStatus(ExtHelpUrl);
if(httpStatusCode == HttpStatusCode.OK) {
ret = ExtHelpUrl;
} else {
Log.WarnFormat("{0} returned status {1}", ExtHelpUrl, httpStatusCode);
}
} else if(httpStatusCode == null){
Log.Info("Internet connection does not seem to be available, will load help from file system.");
}
return ret;
}
/// <summary>
/// Retrieves HTTP status for a given url.
/// </summary>
/// <param name="url">URL for which the HTTP status is to be checked</param>
/// <returns>An HTTP status code, or null if there is none (probably indicating that there is no internet connection available</returns>
private static HttpStatusCode? GetHttpStatus(string url) {
try {
HttpWebRequest req = NetworkHelper.CreateWebRequest(url);
using HttpWebResponse res = (HttpWebResponse)req.GetResponse();
return res.StatusCode;
} catch (WebException e) {
return ((HttpWebResponse) e.Response)?.StatusCode;
}
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -1,52 +0,0 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
using System.Collections.Generic;
using System.Drawing;
namespace Greenshot.Helpers {
public static class Colors {
public static bool IsVisible(Color c) {
return c != null && !c.Equals(Color.Empty) && !c.Equals(Color.Transparent) && c.A > 0;
}
public static Color Mix(List<Color> colors) {
int a = 0;
int r = 0;
int g = 0;
int b = 0;
int count = 0;
foreach (Color color in colors) {
if (!color.Equals(Color.Empty)) {
a += color.A;
r += color.R;
g += color.G;
b += color.B;
count++;
}
}
if (count==0) {
return Color.Empty;
}
return Color.FromArgb(a/count, r/count, g/count, b/count);
}
}
}

View file

@ -1,527 +0,0 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Runtime.InteropServices;
using System.Runtime.Serialization.Formatters.Binary;
using System.Windows.Forms;
using GreenshotPlugin.Core;
namespace Greenshot.Helpers {
public enum CommandEnum { OpenFile, Exit, FirstLaunch, ReloadConfig };
/// <summary>
/// Code from vbAccelerator, location:
/// http://www.vbaccelerator.com/home/NET/Code/Libraries/Windows_Messages/Simple_Interprocess_Communication/WM_COPYDATA_Demo_zip_SimpleInterprocessCommunicationsCS_CopyData_cs.asp
/// </summary>
[Serializable()]
public class CopyDataTransport {
private readonly List<KeyValuePair<CommandEnum, string>> _commands;
public List<KeyValuePair<CommandEnum, string>> Commands => _commands;
public CopyDataTransport() {
_commands = new List<KeyValuePair<CommandEnum, string>>();
}
public CopyDataTransport(CommandEnum command) : this() {
AddCommand(command, null);
}
public CopyDataTransport(CommandEnum command, string commandData) : this() {
AddCommand(command, commandData);
}
public void AddCommand(CommandEnum command) {
_commands.Add(new KeyValuePair<CommandEnum, string>(command, null));
}
public void AddCommand(CommandEnum command, string commandData) {
_commands.Add(new KeyValuePair<CommandEnum, string>(command, commandData));
}
}
public delegate void CopyDataReceivedEventHandler(object sender, CopyDataReceivedEventArgs e);
/// <summary>
/// A class which wraps using Windows native WM_COPYDATA
/// message to send interprocess data between applications.
/// This is a simple technique for interprocess data sends
/// using Windows. The alternative to this is to use
/// Remoting, which requires a network card and a way
/// to register the Remoting name of an object so it
/// can be read by other applications.
/// </summary>
public class CopyData : NativeWindow, IDisposable {
/// <summary>
/// Event raised when data is received on any of the channels
/// this class is subscribed to.
/// </summary>
public event CopyDataReceivedEventHandler CopyDataReceived;
[StructLayout(LayoutKind.Sequential)]
private struct COPYDATASTRUCT {
public readonly IntPtr dwData;
public readonly int cbData;
public readonly IntPtr lpData;
}
private const int WM_COPYDATA = 0x4A;
private const int WM_DESTROY = 0x2;
private CopyDataChannels _channels;
/// <summary>
/// Override for a form's Window Procedure to handle WM_COPYDATA
/// messages sent by other instances of this class.
/// </summary>
/// <param name="m">The Windows Message information.</param>
protected override void WndProc (ref Message m ) {
if (m.Msg == WM_COPYDATA)
{
var cds = (COPYDATASTRUCT) Marshal.PtrToStructure(m.LParam, typeof(COPYDATASTRUCT));
if (cds.cbData > 0) {
byte[] data = new byte[cds.cbData];
Marshal.Copy(cds.lpData, data, 0, cds.cbData);
MemoryStream stream = new MemoryStream(data);
BinaryFormatter b = new BinaryFormatter();
CopyDataObjectData cdo = (CopyDataObjectData) b.Deserialize(stream);
if (_channels != null && _channels.Contains(cdo.Channel)) {
CopyDataReceivedEventArgs d = new CopyDataReceivedEventArgs(cdo.Channel, cdo.Data, cdo.Sent);
OnCopyDataReceived(d);
m.Result = (IntPtr) 1;
}
}
} else if (m.Msg == WM_DESTROY) {
// WM_DESTROY fires before OnHandleChanged and is
// a better place to ensure that we've cleared
// everything up.
_channels?.OnHandleChange();
base.OnHandleChange();
}
base.WndProc(ref m);
}
/// <summary>
/// Raises the DataReceived event from this class.
/// </summary>
/// <param name="e">The data which has been received.</param>
protected void OnCopyDataReceived(CopyDataReceivedEventArgs e)
{
CopyDataReceived?.Invoke(this, e);
}
/// <summary>
/// If the form's handle changes, the properties associated
/// with the window need to be cleared up. This override ensures
/// that it is done. Note that the CopyData class will then
/// stop responding to events and it should be recreated once
/// the new handle has been assigned.
/// </summary>
protected override void OnHandleChange () {
// need to clear up everything we had set.
_channels?.OnHandleChange();
base.OnHandleChange();
}
/// <summary>
/// Gets the collection of channels.
/// </summary>
public CopyDataChannels Channels => _channels;
public void Dispose() {
Dispose(true);
GC.SuppressFinalize(this);
}
/// <summary>
/// Clears up any resources associated with this object.
/// </summary>
protected virtual void Dispose(bool disposing)
{
if (!disposing || _channels == null) return;
_channels.Clear();
_channels = null;
}
/// <summary>
/// Constructs a new instance of the CopyData class
/// </summary>
public CopyData() {
_channels = new CopyDataChannels(this);
}
/// <summary>
/// Finalises a CopyData class which has not been disposed.
/// There may be a minor resource leak if this class is finalised
/// after the form it is associated with.
/// </summary>
~CopyData() {
Dispose(false);
}
}
/// <summary>
/// Contains data and other information associated with data
/// which has been sent from another application.
/// </summary>
public class CopyDataReceivedEventArgs : EventArgs {
/// <summary>
/// Gets the channel name that this data was sent on.
/// </summary>
public string ChannelName { get; }
/// <summary>
/// Gets the data object which was sent.
/// </summary>
public object Data { get; }
/// <summary>
/// Gets the date and time which at the data was sent
/// by the sending application.
/// </summary>
public DateTime Sent { get; }
/// <summary>
/// Gets the date and time which this data item as
/// received.
/// </summary>
public DateTime Received { get; }
/// <summary>
/// Constructs an instance of this class.
/// </summary>
/// <param name="channelName">The channel that the data was received from</param>
/// <param name="data">The data which was sent</param>
/// <param name="sent">The date and time the data was sent</param>
internal CopyDataReceivedEventArgs(string channelName, object data, DateTime sent) {
ChannelName = channelName;
Data = data;
Sent = sent;
Received = DateTime.Now;
}
}
/// <summary>
/// A strongly-typed collection of channels associated with the CopyData
/// class.
/// </summary>
public class CopyDataChannels : DictionaryBase {
private readonly NativeWindow _owner;
/// <summary>
/// Returns an enumerator for each of the CopyDataChannel objects
/// within this collection.
/// </summary>
/// <returns>An enumerator for each of the CopyDataChannel objects
/// within this collection.</returns>
public new IEnumerator GetEnumerator ( ) {
return Dictionary.Values.GetEnumerator();
}
/// <summary>
/// Returns the CopyDataChannel at the specified 0-based index.
/// </summary>
public CopyDataChannel this[int index] {
get {
CopyDataChannel ret = null;
int i = 0;
foreach (CopyDataChannel cdc in Dictionary.Values) {
i++;
if (i != index) continue;
ret = cdc;
break;
}
return ret;
}
}
/// <summary>
/// Returns the CopyDataChannel for the specified channelName
/// </summary>
public CopyDataChannel this[string channelName] => (CopyDataChannel) Dictionary[channelName];
/// <summary>
/// Adds a new channel on which this application can send and
/// receive messages.
/// </summary>
public void Add(string channelName) {
CopyDataChannel cdc = new CopyDataChannel(_owner, channelName);
Dictionary.Add(channelName , cdc);
}
/// <summary>
/// Removes an existing channel.
/// </summary>
/// <param name="channelName">The channel to remove</param>
public void Remove(string channelName) {
Dictionary.Remove(channelName);
}
/// <summary>
/// Gets/sets whether this channel contains a CopyDataChannel
/// for the specified channelName.
/// </summary>
public bool Contains(string channelName) {
return Dictionary.Contains(channelName);
}
/// <summary>
/// Ensures the resources associated with a CopyDataChannel
/// object collected by this class are cleared up.
/// </summary>
protected override void OnClear() {
foreach (CopyDataChannel cdc in Dictionary.Values) {
cdc.Dispose();
}
base.OnClear();
}
/// <summary>
/// Ensures any resoures associated with the CopyDataChannel object
/// which has been removed are cleared up.
/// </summary>
/// <param name="key">The channelName</param>
/// <param name="data">The CopyDataChannel object which has
/// just been removed</param>
protected override void OnRemoveComplete ( object key , object data ) {
( (CopyDataChannel) data).Dispose();
OnRemove(key, data);
}
/// <summary>
/// If the form's handle changes, the properties associated
/// with the window need to be cleared up. This override ensures
/// that it is done. Note that the CopyData class will then
/// stop responding to events and it should be recreated once
/// the new handle has been assigned.
/// </summary>
public void OnHandleChange() {
foreach (CopyDataChannel cdc in Dictionary.Values) {
cdc.OnHandleChange();
}
}
/// <summary>
/// Constructs a new instance of the CopyDataChannels collection.
/// Automatically managed by the CopyData class.
/// </summary>
/// <param name="owner">The NativeWindow this collection
/// will be associated with</param>
internal CopyDataChannels(NativeWindow owner) {
_owner = owner;
}
}
/// <summary>
/// A channel on which messages can be sent.
/// </summary>
public class CopyDataChannel : IDisposable {
[DllImport("user32", CharSet = CharSet.Unicode, SetLastError = true)]
private static extern IntPtr GetProp(IntPtr hWnd, string lpString);
[DllImport("user32", CharSet = CharSet.Unicode, SetLastError = true)]
private static extern bool SetProp(IntPtr hWnd, string lpString, IntPtr hData);
[DllImport("user32", CharSet = CharSet.Unicode, SetLastError = true)]
private static extern IntPtr RemoveProp(IntPtr hWnd, string lpString);
[DllImport("user32", CharSet = CharSet.Unicode, SetLastError = true)]
private static extern IntPtr SendMessage(IntPtr hWnd, int wMsg, IntPtr wParam, ref COPYDATASTRUCT lParam);
[StructLayout(LayoutKind.Sequential)]
private struct COPYDATASTRUCT {
public IntPtr dwData;
public int cbData;
public IntPtr lpData;
}
private const int WM_COPYDATA = 0x4A;
private readonly NativeWindow _owner;
private bool _recreateChannel;
/// <summary>
/// Gets the name associated with this channel.
/// </summary>
public string ChannelName { get; private set; }
/// <summary>
/// Sends the specified object on this channel to any other
/// applications which are listening. The object must have the
/// SerializableAttribute set, or must implement ISerializable.
/// </summary>
/// <param name="obj">The object to send</param>
/// <returns>The number of recipients</returns>
public int Send(object obj) {
int recipients = 0;
if (_recreateChannel) {
// handle has changed
AddChannel();
}
CopyDataObjectData cdo = new CopyDataObjectData(obj, ChannelName);
// Try to do a binary serialization on obj.
// This will throw and exception if the object to
// be passed isn't serializable.
BinaryFormatter b = new BinaryFormatter();
MemoryStream stream = new MemoryStream();
b.Serialize(stream, cdo);
stream.Flush();
// Now move the data into a pointer so we can send
// it using WM_COPYDATA:
// Get the length of the data:
int dataSize = (int)stream.Length;
if (dataSize > 0) {
// This isn't very efficient if your data is very large.
// First we copy to a byte array, then copy to a CoTask
// Mem object... And when we use WM_COPYDATA windows will
// make yet another copy! But if you're talking about 4K
// or less of data then it doesn't really matter.
byte[] data = new byte[dataSize];
stream.Seek(0, SeekOrigin.Begin);
stream.Read(data, 0, dataSize);
IntPtr ptrData = Marshal.AllocCoTaskMem(dataSize);
Marshal.Copy(data, 0, ptrData, dataSize);
// Send the data to each window identified on
// the channel:
foreach(WindowDetails window in WindowDetails.GetAllWindows()) {
if (!window.Handle.Equals(_owner.Handle)) {
if (GetProp(window.Handle, ChannelName) != IntPtr.Zero) {
COPYDATASTRUCT cds = new COPYDATASTRUCT
{
cbData = dataSize,
dwData = IntPtr.Zero,
lpData = ptrData
};
SendMessage(window.Handle, WM_COPYDATA, _owner.Handle, ref cds);
recipients += Marshal.GetLastWin32Error() == 0 ? 1 : 0;
}
}
}
// Clear up the data:
Marshal.FreeCoTaskMem(ptrData);
}
stream.Close();
return recipients;
}
private void AddChannel() {
// Tag this window with property "channelName"
SetProp(_owner.Handle, ChannelName, _owner.Handle);
}
private void RemoveChannel() {
// Remove the "channelName" property from this window
RemoveProp(_owner.Handle, ChannelName);
}
/// <summary>
/// If the form's handle changes, the properties associated
/// with the window need to be cleared up. This method ensures
/// that it is done. Note that the CopyData class will then
/// stop responding to events and it should be recreated once
/// the new handle has been assigned.
/// </summary>
public void OnHandleChange() {
RemoveChannel();
_recreateChannel = true;
}
public void Dispose() {
Dispose(true);
GC.SuppressFinalize(this);
}
/// <summary>
/// Clears up any resources associated with this channel.
/// </summary>
protected virtual void Dispose(bool disposing)
{
if (!disposing) return;
if (ChannelName.Length > 0) {
RemoveChannel();
}
ChannelName = string.Empty;
}
/// <summary>
/// Constructs a new instance of a CopyData channel. Called
/// automatically by the CopyDataChannels collection.
/// </summary>
/// <param name="owner">The owning native window</param>
/// <param name="channelName">The name of the channel to
/// send messages on</param>
internal CopyDataChannel(NativeWindow owner, string channelName) {
_owner = owner;
ChannelName = channelName;
AddChannel();
}
~CopyDataChannel() {
Dispose(false);
}
}
/// <summary>
/// A class which wraps the data being copied, used
/// internally within the CopyData class objects.
/// </summary>
[Serializable()]
internal class CopyDataObjectData {
/// <summary>
/// The Object to copy. Must be Serializable.
/// </summary>
public object Data;
/// <summary>
/// The date and time this object was sent.
/// </summary>
public DateTime Sent;
/// <summary>
/// The name of the channel this object is being sent on
/// </summary>
public string Channel;
/// <summary>
/// Constructs a new instance of this object
/// </summary>
/// <param name="data">The data to copy</param>
/// <param name="channel">The channel name to send on</param>
/// <exception cref="ArgumentException">If data is not serializable.</exception>
public CopyDataObjectData(object data, string channel) {
Data = data;
if (!data.GetType().IsSerializable) {
throw new ArgumentException("Data object must be serializable.",
nameof(data));
}
Channel = channel;
Sent = DateTime.Now;
}
}
}

View file

@ -1,111 +0,0 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
using System;
using System.Collections.Generic;
using System.Linq;
using GreenshotPlugin.Core;
using GreenshotPlugin.IniFile;
using GreenshotPlugin.Interfaces;
using log4net;
namespace Greenshot.Helpers {
/// <summary>
/// Description of DestinationHelper.
/// </summary>
public static class DestinationHelper {
private static readonly ILog Log = LogManager.GetLogger(typeof(DestinationHelper));
private static readonly CoreConfiguration CoreConfig = IniConfig.GetIniSection<CoreConfiguration>();
/// <summary>
/// Initialize the internal destinations
/// </summary>
public static void RegisterInternalDestinations() {
foreach(Type destinationType in InterfaceUtils.GetSubclassesOf(typeof(IDestination),true)) {
// Only take our own
if (!"Greenshot.Destinations".Equals(destinationType.Namespace)) {
continue;
}
if (destinationType.IsAbstract) continue;
IDestination destination;
try {
destination = (IDestination)Activator.CreateInstance(destinationType);
} catch (Exception e) {
Log.ErrorFormat("Can't create instance of {0}", destinationType);
Log.Error(e);
continue;
}
if (destination.IsActive) {
Log.DebugFormat("Found destination {0} with designation {1}", destinationType.Name, destination.Designation);
SimpleServiceProvider.Current.AddService(destination);
} else {
Log.DebugFormat("Ignoring destination {0} with designation {1}", destinationType.Name, destination.Designation);
}
}
}
/// <summary>
/// Method to get all the destinations from the plugins
/// </summary>
/// <returns>List of IDestination</returns>
public static IEnumerable<IDestination> GetAllDestinations()
{
return SimpleServiceProvider.Current.GetAllInstances<IDestination>()
.Where(destination => destination.IsActive)
.Where(destination => CoreConfig.ExcludeDestinations == null ||
!CoreConfig.ExcludeDestinations.Contains(destination.Designation)).OrderBy(p => p.Priority).ThenBy(p => p.Description);
}
/// <summary>
/// Get a destination by a designation
/// </summary>
/// <param name="designation">Designation of the destination</param>
/// <returns>IDestination or null</returns>
public static IDestination GetDestination(string designation) {
if (designation == null) {
return null;
}
foreach (IDestination destination in GetAllDestinations()) {
if (designation.Equals(destination.Designation)) {
return destination;
}
}
return null;
}
/// <summary>
/// A simple helper method which will call ExportCapture for the destination with the specified designation
/// </summary>
/// <param name="manuallyInitiated"></param>
/// <param name="designation"></param>
/// <param name="surface"></param>
/// <param name="captureDetails"></param>
public static ExportInformation ExportCapture(bool manuallyInitiated, string designation, ISurface surface, ICaptureDetails captureDetails) {
IDestination destination = GetDestination(designation);
if (destination != null && destination.IsActive) {
return destination.ExportCapture(manuallyInitiated, surface, captureDetails);
}
return null;
}
}
}

View file

@ -1,59 +0,0 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
using System;
namespace Greenshot.Helpers {
/// <summary>
/// Description of GeometryHelper.
/// </summary>
public static class GeometryHelper {
/// <summary>
/// Finds the distance between two points on a 2D surface.
/// </summary>
/// <param name="x1">The point on the x-axis of the first point</param>
/// <param name="x2">The point on the x-axis of the second point</param>
/// <param name="y1">The point on the y-axis of the first point</param>
/// <param name="y2">The point on the y-axis of the second point</param>
/// <returns></returns>
public static int Distance2D(int x1, int y1, int x2, int y2) {
//Take x2-x1, then square it
double part1 = Math.Pow(x2 - x1, 2);
//Take y2-y1, then square it
double part2 = Math.Pow(y2 - y1, 2);
//Add both of the parts together
double underRadical = part1 + part2;
//Get the square root of the parts
return (int)Math.Sqrt(underRadical);
}
/// <summary>
/// Calculates the angle of a line defined by two points on a 2D surface.
/// </summary>
/// <param name="x1">The point on the x-axis of the first point</param>
/// <param name="x2">The point on the x-axis of the second point</param>
/// <param name="y1">The point on the y-axis of the first point</param>
/// <param name="y2">The point on the y-axis of the second point</param>
/// <returns></returns>
public static double Angle2D(int x1, int y1, int x2, int y2) {
return Math.Atan2(y2 - y1, x2 - x1) * 180 / Math.PI;
}
}
}

View file

@ -1,643 +0,0 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.Linq;
using System.Runtime.InteropServices;
using Greenshot.Configuration;
using Greenshot.Helpers.IEInterop;
using GreenshotPlugin.UnmanagedHelpers;
using GreenshotPlugin.Controls;
using GreenshotPlugin.Core;
using GreenshotPlugin.IEInterop;
using GreenshotPlugin.IniFile;
using GreenshotPlugin.Interfaces;
using GreenshotPlugin.Interop;
using GreenshotPlugin.UnmanagedHelpers.Enums;
using log4net;
namespace Greenshot.Helpers {
/// <summary>
/// The code for this helper comes from: http://www.codeproject.com/KB/graphics/IECapture.aspx
/// The code is modified with some of the suggestions in different comments and there still were leaks which I fixed.
/// On top I modified it to use the already available code in Greenshot.
/// Many thanks to all the people who contributed here!
/// </summary>
public static class IeCaptureHelper {
private static readonly ILog Log = LogManager.GetLogger(typeof(IeCaptureHelper));
private static readonly CoreConfiguration CoreConfig = IniConfig.GetIniSection<CoreConfiguration>();
// Helper method to activate a certain IE Tab
public static void ActivateIeTab(WindowDetails ieWindowDetails, int tabIndex) {
WindowDetails directUiWindowDetails = IEHelper.GetDirectUI(ieWindowDetails);
if (directUiWindowDetails == null) return;
// Bring window to the front
ieWindowDetails.Restore();
// Get accessible
Accessible ieAccessible = new Accessible(directUiWindowDetails.Handle);
// Activate Tab
ieAccessible.ActivateIETab(tabIndex);
}
/// <summary>
/// Does the supplied window have a IE part?
/// </summary>
/// <param name="someWindow"></param>
/// <returns></returns>
public static bool IsIeWindow(WindowDetails someWindow) {
if ("IEFrame".Equals(someWindow.ClassName)) {
return true;
}
if (CoreConfig.WindowClassesToCheckForIE != null && CoreConfig.WindowClassesToCheckForIE.Contains(someWindow.ClassName)) {
return someWindow.GetChild("Internet Explorer_Server") != null;
}
return false;
}
/// <summary>
/// Get Windows displaying an IE
/// </summary>
/// <returns>IEnumerable WindowDetails</returns>
public static IEnumerable<WindowDetails> GetIeWindows() {
foreach (var possibleIeWindow in WindowDetails.GetAllWindows()) {
if (possibleIeWindow.Text.Length == 0) {
continue;
}
if (possibleIeWindow.ClientRectangle.IsEmpty) {
continue;
}
if (IsIeWindow(possibleIeWindow)) {
yield return possibleIeWindow;
}
}
}
/// <summary>
/// Simple check if IE is running
/// </summary>
/// <returns>bool</returns>
public static bool IsIeRunning()
{
return GetIeWindows().Any();
}
/// <summary>
/// Gets a list of all IE Windows & tabs with the captions of the instances
/// </summary>
/// <returns>List with KeyValuePair of WindowDetails and string</returns>
public static List<KeyValuePair<WindowDetails, string>> GetBrowserTabs() {
var ieHandleList = new List<IntPtr>();
var browserWindows = new Dictionary<WindowDetails, List<string>>();
// Find the IE windows
foreach (var ieWindow in GetIeWindows()) {
try {
if (ieHandleList.Contains(ieWindow.Handle))
{
continue;
}
if ("IEFrame".Equals(ieWindow.ClassName)) {
var directUiwd = IEHelper.GetDirectUI(ieWindow);
if (directUiwd != null) {
var accessible = new Accessible(directUiwd.Handle);
browserWindows.Add(ieWindow, accessible.IETabCaptions);
}
} else if (CoreConfig.WindowClassesToCheckForIE != null && CoreConfig.WindowClassesToCheckForIE.Contains(ieWindow.ClassName)) {
var singleWindowText = new List<string>();
try {
var document2 = GetHtmlDocument(ieWindow);
string title = document2.title;
Marshal.ReleaseComObject(document2);
if (string.IsNullOrEmpty(title)) {
singleWindowText.Add(ieWindow.Text);
} else {
singleWindowText.Add(ieWindow.Text + " - " + title);
}
} catch {
singleWindowText.Add(ieWindow.Text);
}
browserWindows.Add(ieWindow, singleWindowText);
}
ieHandleList.Add(ieWindow.Handle);
} catch (Exception) {
Log.Warn("Can't get Info from " + ieWindow.ClassName);
}
}
var returnList = new List<KeyValuePair<WindowDetails, string>>();
foreach(var windowDetails in browserWindows.Keys) {
foreach(string tab in browserWindows[windowDetails]) {
returnList.Add(new KeyValuePair<WindowDetails, string>(windowDetails, tab));
}
}
return returnList;
}
/// <summary>
/// Helper Method to get the IHTMLDocument2
/// </summary>
/// <param name="mainWindow"></param>
/// <returns></returns>
private static IHTMLDocument2 GetHtmlDocument(WindowDetails mainWindow) {
var ieServer = "Internet Explorer_Server".Equals(mainWindow.ClassName) ? mainWindow : mainWindow.GetChild("Internet Explorer_Server");
if (ieServer == null) {
Log.WarnFormat("No Internet Explorer_Server for {0}", mainWindow.Text);
return null;
}
uint windowMessage = User32.RegisterWindowMessage("WM_HTML_GETOBJECT");
if (windowMessage == 0) {
Log.WarnFormat("Couldn't register WM_HTML_GETOBJECT");
return null;
}
Log.DebugFormat("Trying WM_HTML_GETOBJECT on {0}", ieServer.ClassName);
User32.SendMessageTimeout(ieServer.Handle, windowMessage, IntPtr.Zero, IntPtr.Zero, SendMessageTimeoutFlags.SMTO_NORMAL, 5000, out var response);
IHTMLDocument2 document2;
if (response != UIntPtr.Zero) {
document2 = (IHTMLDocument2)Accessible.ObjectFromLresult(response, typeof(IHTMLDocument).GUID, IntPtr.Zero);
if (document2 == null) {
Log.Error("No IHTMLDocument2 found");
return null;
}
} else {
Log.Error("No answer on WM_HTML_GETOBJECT.");
return null;
}
return document2;
}
/// <summary>
/// Helper method which will retrieve the IHTMLDocument2 for the supplied window,
/// or return the first if none is supplied.
/// </summary>
/// <param name="browserWindow">The WindowDetails to get the IHTMLDocument2 for</param>
/// <returns>The WindowDetails to which the IHTMLDocument2 belongs</returns>
private static DocumentContainer CreateDocumentContainer(WindowDetails browserWindow) {
DocumentContainer returnDocumentContainer = null;
WindowDetails returnWindow = null;
IHTMLDocument2 returnDocument2 = null;
// alternative if no match
WindowDetails alternativeReturnWindow = null;
IHTMLDocument2 alternativeReturnDocument2 = null;
// Find the IE windows
foreach (WindowDetails ieWindow in GetIeWindows()) {
Log.DebugFormat("Processing {0} - {1}", ieWindow.ClassName, ieWindow.Text);
Accessible ieAccessible = null;
WindowDetails directUiwd = IEHelper.GetDirectUI(ieWindow);
if (directUiwd != null) {
ieAccessible = new Accessible(directUiwd.Handle);
}
if (ieAccessible == null) {
if (browserWindow != null) {
Log.InfoFormat("Active Window is {0}", browserWindow.Text);
}
if (!ieWindow.Equals(browserWindow)) {
Log.WarnFormat("No ieAccessible for {0}", ieWindow.Text);
continue;
}
Log.DebugFormat("No ieAccessible, but the active window is an IE window: {0}, ", ieWindow.Text);
}
try {
// Get the Document
IHTMLDocument2 document2 = GetHtmlDocument(ieWindow);
if (document2 == null) {
continue;
}
// Get the content window handle for the shellWindow.Document
IOleWindow oleWindow = (IOleWindow)document2;
IntPtr contentWindowHandle = IntPtr.Zero;
oleWindow?.GetWindow(out contentWindowHandle);
if (contentWindowHandle != IntPtr.Zero) {
// Get the HTMLDocument to check the hasFocus
// See: http://social.msdn.microsoft.com/Forums/en-US/vbgeneral/thread/60c6c95d-377c-4bf4-860d-390840fce31c/
IHTMLDocument4 document4 = (IHTMLDocument4)document2;
if (document4.hasFocus()) {
Log.DebugFormat("Matched focused document: {0}", document2.title);
// Look no further, we got what we wanted!
returnDocument2 = document2;
returnWindow = new WindowDetails(contentWindowHandle);
break;
}
try {
if (ieWindow.Equals(browserWindow)) {
returnDocument2 = document2;
returnWindow = new WindowDetails(contentWindowHandle);
break;
}
if (ieAccessible != null && returnWindow == null && document2.title.Equals(ieAccessible.IEActiveTabCaption) ) {
Log.DebugFormat("Title: {0}", document2.title);
returnDocument2 = document2;
returnWindow = new WindowDetails(contentWindowHandle);
} else {
alternativeReturnDocument2 = document2;
alternativeReturnWindow = new WindowDetails(contentWindowHandle);
}
} catch (Exception) {
alternativeReturnDocument2 = document2;
alternativeReturnWindow = new WindowDetails(contentWindowHandle);
}
}
} catch (Exception e) {
Log.ErrorFormat("Major problem: Problem retrieving Document from {0}", ieWindow.Text);
Log.Error(e);
}
}
// check if we have something to return
if (returnWindow != null) {
// As it doesn't have focus, make sure it's active
returnWindow.Restore();
returnWindow.GetParent();
// Create the container
try {
returnDocumentContainer = new DocumentContainer(returnDocument2, returnWindow);
} catch (Exception e) {
Log.Error("Major problem: Problem retrieving Document.");
Log.Error(e);
}
}
if (returnDocumentContainer == null && alternativeReturnDocument2 != null) {
// As it doesn't have focus, make sure it's active
alternativeReturnWindow.Restore();
alternativeReturnWindow.GetParent();
// Create the container
try {
returnDocumentContainer = new DocumentContainer(alternativeReturnDocument2, alternativeReturnWindow);
} catch (Exception e) {
Log.Error("Major problem: Problem retrieving Document.");
Log.Error(e);
}
}
return returnDocumentContainer;
}
/// <summary>
/// Here the logic for capturing the IE Content is located
/// </summary>
/// <param name="capture">ICapture where the capture needs to be stored</param>
/// <param name="windowToCapture">window to use</param>
/// <returns>ICapture with the content (if any)</returns>
public static ICapture CaptureIe(ICapture capture, WindowDetails windowToCapture) {
windowToCapture ??= WindowDetails.GetActiveWindow();
// Show backgroundform after retrieving the active window..
BackgroundForm backgroundForm = new BackgroundForm(Language.GetString(LangKey.contextmenu_captureie), Language.GetString(LangKey.wait_ie_capture));
backgroundForm.Show();
//BackgroundForm backgroundForm = BackgroundForm.ShowAndWait(language.GetString(LangKey.contextmenu_captureie), language.GetString(LangKey.wait_ie_capture));
try {
//Get IHTMLDocument2 for the current active window
DocumentContainer documentContainer = CreateDocumentContainer(windowToCapture);
// Nothing found
if (documentContainer == null) {
Log.Debug("Nothing to capture found");
return null;
}
try {
Log.DebugFormat("Window class {0}", documentContainer.ContentWindow.ClassName);
Log.DebugFormat("Window location {0}", documentContainer.ContentWindow.Location);
} catch (Exception ex) {
Log.Warn("Error while logging information.", ex);
}
// bitmap to return
Bitmap returnBitmap = null;
try {
Size pageSize = PrepareCapture(documentContainer, capture);
returnBitmap = CapturePage(documentContainer, pageSize);
} catch (Exception captureException) {
Log.Error("Exception found, ignoring and returning nothing! Error was: ", captureException);
}
// TODO: Enable when the elements are usable again.
// Capture the element on the page
//try {
// if (CoreConfig.IEFieldCapture && capture.CaptureDetails.HasDestination("Editor")) {
// // clear the current elements, as they are for the window itself
// capture.Elements.Clear();
// CaptureElement documentCaptureElement = documentContainer.CreateCaptureElements(pageSize);
// foreach(DocumentContainer frameDocument in documentContainer.Frames) {
// try {
// CaptureElement frameCaptureElement = frameDocument.CreateCaptureElements(Size.Empty);
// if (frameCaptureElement != null) {
// documentCaptureElement.Children.Add(frameCaptureElement);
// }
// } catch (Exception ex) {
// LOG.Warn("An error occurred while creating the capture elements: ", ex);
// }
// }
// capture.AddElement(documentCaptureElement);
// // Offset the elements, as they are "back offseted" later...
// Point windowLocation = documentContainer.ContentWindow.WindowRectangle.Location;
// capture.MoveElements(-(capture.ScreenBounds.Location.X-windowLocation.X), -(capture.ScreenBounds.Location.Y-windowLocation.Y));
// }
//} catch (Exception elementsException) {
// LOG.Warn("An error occurred while creating the capture elements: ", elementsException);
//}
if (returnBitmap == null) {
return null;
}
// Store the bitmap for further processing
capture.Image = returnBitmap;
try {
// Store the location of the window
capture.Location = documentContainer.ContentWindow.Location;
// The URL is available unter "document2.url" and can be used to enhance the meta-data etc.
capture.CaptureDetails.AddMetaData("url", documentContainer.Url);
// Store the title of the page
capture.CaptureDetails.Title = documentContainer.Name ?? windowToCapture.Text;
} catch (Exception ex) {
Log.Warn("Problems getting some attributes...", ex);
}
// Store the URL of the page
if (documentContainer.Url != null) {
try {
Uri uri = new Uri(documentContainer.Url);
capture.CaptureDetails.AddMetaData("URL", uri.OriginalString);
// As the URL can hardly be used in a filename, the following can be used
if (!string.IsNullOrEmpty(uri.Scheme)) {
capture.CaptureDetails.AddMetaData("URL_SCHEME", uri.Scheme);
}
if (!string.IsNullOrEmpty(uri.DnsSafeHost)) {
capture.CaptureDetails.AddMetaData("URL_HOSTNAME", uri.DnsSafeHost);
}
if (!string.IsNullOrEmpty(uri.AbsolutePath)) {
capture.CaptureDetails.AddMetaData("URL_PATH", uri.AbsolutePath);
}
if (!string.IsNullOrEmpty(uri.Query)) {
capture.CaptureDetails.AddMetaData("URL_QUERY", uri.Query);
}
if (!string.IsNullOrEmpty(uri.UserInfo)) {
capture.CaptureDetails.AddMetaData("URL_USER", uri.UserInfo);
}
capture.CaptureDetails.AddMetaData("URL_PORT", uri.Port.ToString());
} catch(Exception e) {
Log.Warn("Exception when trying to use url in metadata "+documentContainer.Url,e);
}
}
try {
// Only move the mouse to correct for the capture offset
capture.MoveMouseLocation(-documentContainer.ViewportRectangle.X, -documentContainer.ViewportRectangle.Y);
// Used to be: capture.MoveMouseLocation(-(capture.Location.X + documentContainer.CaptureOffset.X), -(capture.Location.Y + documentContainer.CaptureOffset.Y));
} catch (Exception ex) {
Log.Warn("Error while correcting the mouse offset.", ex);
}
} finally {
// Always close the background form
backgroundForm.CloseDialog();
}
return capture;
}
/// <summary>
/// Prepare the calculates for all the frames, move and fit...
/// </summary>
/// <param name="documentContainer"></param>
/// <param name="capture"></param>
/// <returns>Size of the complete page</returns>
private static Size PrepareCapture(DocumentContainer documentContainer, ICapture capture) {
// Calculate the page size
int pageWidth = documentContainer.ScrollWidth;
int pageHeight = documentContainer.ScrollHeight;
// Here we loop over all the frames and try to make sure they don't overlap
bool movedFrame;
do {
movedFrame = false;
foreach(DocumentContainer currentFrame in documentContainer.Frames) {
foreach(DocumentContainer otherFrame in documentContainer.Frames) {
if (otherFrame.Id == currentFrame.Id) {
continue;
}
// check if we need to move
if (!otherFrame.DestinationRectangle.IntersectsWith(currentFrame.DestinationRectangle) ||
otherFrame.SourceRectangle.IntersectsWith(currentFrame.SourceRectangle)) continue;
bool horizontalResize = currentFrame.SourceSize.Width < currentFrame.DestinationSize.Width;
bool verticalResize = currentFrame.SourceSize.Width < currentFrame.DestinationSize.Width;
bool horizontalMove = currentFrame.SourceLeft < currentFrame.DestinationLeft;
bool verticalMove = currentFrame.SourceTop < currentFrame.DestinationTop;
bool leftOf = currentFrame.SourceRight <= otherFrame.SourceLeft;
bool belowOf = currentFrame.SourceBottom <= otherFrame.SourceTop;
if ((horizontalResize || horizontalMove) && leftOf) {
// Current frame resized horizontally, so move other horizontally
Log.DebugFormat("Moving Frame {0} horizontally to the right of {1}", otherFrame.Name, currentFrame.Name);
otherFrame.DestinationLeft = currentFrame.DestinationRight;
movedFrame = true;
} else if ((verticalResize || verticalMove) && belowOf){
// Current frame resized vertically, so move other vertically
Log.DebugFormat("Moving Frame {0} vertically to the bottom of {1}", otherFrame.Name, currentFrame.Name);
otherFrame.DestinationTop = currentFrame.DestinationBottom;
movedFrame = true;
} else {
Log.DebugFormat("Frame {0} intersects with {1}", otherFrame.Name, currentFrame.Name);
}
}
}
} while(movedFrame);
bool movedMouse = false;
// Correct cursor location to be inside the window
capture.MoveMouseLocation(-documentContainer.ContentWindow.Location.X, -documentContainer.ContentWindow.Location.Y);
// See if the page has the correct size, as we capture the full frame content AND might have moved them
// the normal pageSize will no longer be enough
foreach(DocumentContainer frameData in documentContainer.Frames) {
if (!movedMouse && frameData.SourceRectangle.Contains(capture.CursorLocation)) {
// Correct mouse cursor location for scrolled position (so it shows on the capture where it really was)
capture.MoveMouseLocation(frameData.ScrollLeft, frameData.ScrollTop);
movedMouse = true;
// Apply any other offset changes
int offsetX = frameData.DestinationLocation.X - frameData.SourceLocation.X;
int offsetY = frameData.DestinationLocation.Y - frameData.SourceLocation.Y;
capture.MoveMouseLocation(offsetX, offsetY);
}
//Get Frame Width & Height
pageWidth = Math.Max(pageWidth, frameData.DestinationRight);
pageHeight = Math.Max(pageHeight, frameData.DestinationBottom);
}
// If the mouse hasn't been moved, it wasn't on a frame. So correct the mouse according to the scroll position of the document
if (!movedMouse) {
// Correct mouse cursor location
capture.MoveMouseLocation(documentContainer.ScrollLeft, documentContainer.ScrollTop);
}
// Limit the size as the editor currently can't work with sizes > short.MaxValue
if (pageWidth > short.MaxValue) {
Log.WarnFormat("Capture has a width of {0} which bigger than the maximum supported {1}, cutting width to the maxium.", pageWidth, short.MaxValue);
pageWidth = Math.Min(pageWidth, short.MaxValue);
}
if (pageHeight > short.MaxValue) {
Log.WarnFormat("Capture has a height of {0} which bigger than the maximum supported {1}, cutting height to the maxium", pageHeight, short.MaxValue);
pageHeight = Math.Min(pageHeight, short.MaxValue);
}
return new Size(pageWidth, pageHeight);
}
/// <summary>
/// Capture the actual page (document)
/// </summary>
/// <param name="documentContainer">The document wrapped in a container</param>
/// <param name="pageSize"></param>
/// <returns>Bitmap with the page content as an image</returns>
private static Bitmap CapturePage(DocumentContainer documentContainer, Size pageSize) {
WindowDetails contentWindowDetails = documentContainer.ContentWindow;
//Create a target bitmap to draw into with the calculated page size
Bitmap returnBitmap = new Bitmap(pageSize.Width, pageSize.Height, PixelFormat.Format24bppRgb);
using (Graphics graphicsTarget = Graphics.FromImage(returnBitmap)) {
// Clear the target with the backgroundcolor
Color clearColor = documentContainer.BackgroundColor;
Log.DebugFormat("Clear color: {0}", clearColor);
graphicsTarget.Clear(clearColor);
// Get the base document & draw it
DrawDocument(documentContainer, contentWindowDetails, graphicsTarget);
// Loop over the frames and clear their source area so we don't see any artefacts
foreach(DocumentContainer frameDocument in documentContainer.Frames)
{
using Brush brush = new SolidBrush(clearColor);
graphicsTarget.FillRectangle(brush, frameDocument.SourceRectangle);
}
// Loop over the frames and capture their content
foreach(DocumentContainer frameDocument in documentContainer.Frames) {
DrawDocument(frameDocument, contentWindowDetails, graphicsTarget);
}
}
return returnBitmap;
}
/// <summary>
/// This method takes the actual capture of the document (frame)
/// </summary>
/// <param name="documentContainer">DocumentContainer</param>
/// <param name="contentWindowDetails">Needed for referencing the location of the frame</param>
/// <param name="graphicsTarget">Graphics</param>
/// <returns>Bitmap with the capture</returns>
private static void DrawDocument(DocumentContainer documentContainer, WindowDetails contentWindowDetails, Graphics graphicsTarget) {
documentContainer.SetAttribute("scroll", 1);
//Get Browser Window Width & Height
int pageWidth = documentContainer.ScrollWidth;
int pageHeight = documentContainer.ScrollHeight;
if (pageWidth * pageHeight == 0) {
Log.WarnFormat("Empty page for DocumentContainer {0}: {1}", documentContainer.Name, documentContainer.Url);
return;
}
//Get Screen Width & Height (this is better as the WindowDetails.ClientRectangle as the real visible parts are there!
int viewportWidth = documentContainer.ClientWidth;
int viewportHeight = documentContainer.ClientHeight;
if (viewportWidth * viewportHeight == 0) {
Log.WarnFormat("Empty viewport for DocumentContainer {0}: {1}", documentContainer.Name, documentContainer.Url);
return;
}
// Store the current location so we can set the browser back and use it for the mouse cursor
int startLeft = documentContainer.ScrollLeft;
int startTop = documentContainer.ScrollTop;
Log.DebugFormat("Capturing {4} with total size {0},{1} displayed with size {2},{3}", pageWidth, pageHeight, viewportWidth, viewportHeight, documentContainer.Name);
// Variable used for looping horizontally
int horizontalPage = 0;
// The location of the browser, used as the destination into the bitmap target
Point targetOffset = new Point();
// Loop of the pages and make a copy of the visible viewport
while (horizontalPage * viewportWidth < pageWidth) {
// Scroll to location
documentContainer.ScrollLeft = viewportWidth * horizontalPage;
targetOffset.X = documentContainer.ScrollLeft;
// Variable used for looping vertically
int verticalPage = 0;
while (verticalPage * viewportHeight < pageHeight) {
// Scroll to location
documentContainer.ScrollTop = viewportHeight * verticalPage;
//Shoot visible window
targetOffset.Y = documentContainer.ScrollTop;
// Draw the captured fragment to the target, but "crop" the scrollbars etc while capturing
Size viewPortSize = new Size(viewportWidth, viewportHeight);
Rectangle clientRectangle = new Rectangle(documentContainer.SourceLocation, viewPortSize);
Image fragment = contentWindowDetails.PrintWindow();
if (fragment != null) {
Log.DebugFormat("Captured fragment size: {0}x{1}", fragment.Width, fragment.Height);
try {
// cut all junk, due to IE "border" we need to remove some parts
Rectangle viewportRect = documentContainer.ViewportRectangle;
if (!viewportRect.IsEmpty) {
Log.DebugFormat("Cropping to viewport: {0}", viewportRect);
ImageHelper.Crop(ref fragment, ref viewportRect);
}
Log.DebugFormat("Cropping to clientRectangle: {0}", clientRectangle);
// Crop to clientRectangle
if (ImageHelper.Crop(ref fragment, ref clientRectangle)) {
Point targetLocation = new Point(documentContainer.DestinationLocation.X, documentContainer.DestinationLocation.Y);
Log.DebugFormat("Fragment targetLocation is {0}", targetLocation);
targetLocation.Offset(targetOffset);
Log.DebugFormat("After offsetting the fragment targetLocation is {0}", targetLocation);
Log.DebugFormat("Drawing fragment of size {0} to {1}", fragment.Size, targetLocation);
graphicsTarget.DrawImage(fragment, targetLocation);
graphicsTarget.Flush();
} else {
// somehow we are capturing nothing!?
Log.WarnFormat("Crop of {0} failed?", documentContainer.Name);
break;
}
} finally {
fragment.Dispose();
}
} else {
Log.WarnFormat("Capture of {0} failed!", documentContainer.Name);
}
verticalPage++;
}
horizontalPage++;
}
// Return to where we were
documentContainer.ScrollLeft = startLeft;
documentContainer.ScrollTop = startTop;
}
}
}

View file

@ -1,502 +0,0 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Globalization;
using System.Runtime.InteropServices;
using GreenshotPlugin.Core;
using GreenshotPlugin.IEInterop;
using log4net;
using IServiceProvider = GreenshotPlugin.Interop.IServiceProvider;
namespace Greenshot.Helpers.IEInterop {
public class DocumentContainer {
private static readonly ILog LOG = LogManager.GetLogger(typeof(DocumentContainer));
private const int E_ACCESSDENIED = unchecked((int)0x80070005L);
private static readonly Guid IID_IWebBrowserApp = new Guid("0002DF05-0000-0000-C000-000000000046");
private static readonly Guid IID_IWebBrowser2 = new Guid("D30C1661-CDAF-11D0-8A3E-00C04FC9E26E");
private static int _counter;
private IHTMLDocument2 _document2;
private IHTMLDocument3 _document3;
private Point _sourceLocation;
private Point _destinationLocation;
private Point _startLocation = Point.Empty;
private Rectangle _viewportRectangle = Rectangle.Empty;
private bool _isDtd;
private DocumentContainer _parent;
private WindowDetails _contentWindow;
private double _zoomLevelX = 1;
private double _zoomLevelY = 1;
private readonly IList<DocumentContainer> _frames = new List<DocumentContainer>();
private DocumentContainer(IHTMLWindow2 frameWindow, WindowDetails contentWindow, DocumentContainer parent) {
//IWebBrowser2 webBrowser2 = frame as IWebBrowser2;
//IHTMLDocument2 document2 = webBrowser2.Document as IHTMLDocument2;
IHTMLDocument2 document2 = GetDocumentFromWindow(frameWindow);
try {
LOG.DebugFormat("frameWindow.name {0}", frameWindow.name);
Name = frameWindow.name;
} catch {
// Ignore
}
try {
LOG.DebugFormat("document2.url {0}",document2.url);
} catch {
// Ignore
}
try {
LOG.DebugFormat("document2.title {0}", document2.title);
} catch {
// Ignore
}
_parent = parent;
// Calculate startLocation for the frames
IHTMLWindow2 window2 = document2.parentWindow;
IHTMLWindow3 window3 = (IHTMLWindow3)window2;
Point contentWindowLocation = contentWindow.WindowRectangle.Location;
int x = window3.screenLeft - contentWindowLocation.X;
int y = window3.screenTop - contentWindowLocation.Y;
// Release IHTMLWindow 2+3 com objects
releaseCom(window2);
releaseCom(window3);
_startLocation = new Point(x, y);
Init(document2, contentWindow);
}
public DocumentContainer(IHTMLDocument2 document2, WindowDetails contentWindow) {
Init(document2, contentWindow);
LOG.DebugFormat("Creating DocumentContainer for Document {0} found in window with rectangle {1}", Name, SourceRectangle);
}
/// <summary>
/// Helper method to release com objects
/// </summary>
/// <param name="comObject"></param>
private void releaseCom(object comObject) {
if (comObject != null) {
Marshal.ReleaseComObject(comObject);
}
}
/// <summary>
/// Private helper method for the constructors
/// </summary>
/// <param name="document2">IHTMLDocument2</param>
/// <param name="contentWindow">WindowDetails</param>
private void Init(IHTMLDocument2 document2, WindowDetails contentWindow) {
_document2 = document2;
_contentWindow = contentWindow;
_document3 = document2 as IHTMLDocument3;
// Check what access method is needed for the document
IHTMLDocument5 document5 = (IHTMLDocument5)document2;
//compatibility mode affects how height is computed
_isDtd = false;
try {
if (_document3?.documentElement != null && !document5.compatMode.Equals("BackCompat")) {
_isDtd = true;
}
} catch (Exception ex) {
LOG.Error("Error checking the compatibility mode:");
LOG.Error(ex);
}
// Do not release IHTMLDocument5 com object, as this also gives problems with the document2!
//Marshal.ReleaseComObject(document5);
Rectangle clientRectangle = contentWindow.WindowRectangle;
try {
IHTMLWindow2 window2 = document2.parentWindow;
//IHTMLWindow3 window3 = (IHTMLWindow3)document2.parentWindow;
IHTMLScreen screen = window2.screen;
IHTMLScreen2 screen2 = (IHTMLScreen2)screen;
if (_parent != null) {
// Copy parent values
_zoomLevelX = _parent._zoomLevelX;
_zoomLevelY = _parent._zoomLevelY;
_viewportRectangle = _parent._viewportRectangle;
} else {
//DisableScrollbars(document2);
// Calculate zoom level
_zoomLevelX = screen2.deviceXDPI/(double)screen2.logicalXDPI;
_zoomLevelY = screen2.deviceYDPI/(double)screen2.logicalYDPI;
// Calculate the viewport rectangle, needed if there is a frame around the html window
LOG.DebugFormat("Screen {0}x{1}", ScaleX(screen.width), ScaleY(screen.height));
//LOG.DebugFormat("Screen location {0},{1}", window3.screenLeft, window3.screenTop);
LOG.DebugFormat("Window rectangle {0}", clientRectangle);
LOG.DebugFormat("Client size {0}x{1}", ClientWidth, ClientHeight);
int diffX = clientRectangle.Width - ClientWidth;
int diffY = clientRectangle.Height - ClientHeight;
// If there is a border around the inner window, the diff == 4
// If there is a border AND a scrollbar the diff == 20
if ((diffX == 4 || diffX >= 20) && (diffY == 4 || diffY >= 20)) {
Point viewportOffset = new Point(2, 2);
Size viewportSize = new Size(ClientWidth, ClientHeight);
_viewportRectangle = new Rectangle(viewportOffset, viewportSize);
LOG.DebugFormat("viewportRect {0}", _viewportRectangle);
}
}
LOG.DebugFormat("Zoomlevel {0}, {1}", _zoomLevelX, _zoomLevelY);
// Release com objects
releaseCom(window2);
releaseCom(screen);
releaseCom(screen2);
} catch (Exception e) {
LOG.Warn("Can't get certain properties for documents, using default. Due to: ", e);
}
try {
LOG.DebugFormat("Calculated location {0} for {1}", _startLocation, document2.title);
if (Name == null) {
Name = document2.title;
}
} catch (Exception e) {
LOG.Warn("Problem while trying to get document title!", e);
}
try {
Url = document2.url;
} catch (Exception e) {
LOG.Warn("Problem while trying to get document url!", e);
}
_sourceLocation = new Point(ScaleX(_startLocation.X), ScaleY(_startLocation.Y));
_destinationLocation = new Point(ScaleX(_startLocation.X), ScaleY(_startLocation.Y));
if (_parent != null) {
return;
}
try {
IHTMLFramesCollection2 frameCollection = (IHTMLFramesCollection2)document2.frames;
for (int frame = 0; frame < frameCollection.length; frame++) {
try {
IHTMLWindow2 frameWindow = frameCollection.item(frame);
DocumentContainer frameData = new DocumentContainer(frameWindow, contentWindow, this);
// check if frame is hidden
if (!frameData.IsHidden) {
LOG.DebugFormat("Creating DocumentContainer for Frame {0} found in window with rectangle {1}", frameData.Name, frameData.SourceRectangle);
_frames.Add(frameData);
} else {
LOG.DebugFormat("Skipping frame {0}", frameData.Name);
}
// Clean up frameWindow
releaseCom(frameWindow);
} catch (Exception e) {
LOG.Warn("Problem while trying to get information from a frame, skipping the frame!", e);
}
}
// Clean up collection
releaseCom(frameCollection);
} catch (Exception ex) {
LOG.Warn("Problem while trying to get the frames, skipping!", ex);
}
try {
// Correct iframe locations
foreach (IHTMLElement frameElement in _document3.getElementsByTagName("IFRAME")) {
try {
CorrectFrameLocations(frameElement);
// Clean up frameElement
releaseCom(frameElement);
} catch (Exception e) {
LOG.Warn("Problem while trying to get information from an iframe, skipping the frame!", e);
}
}
} catch (Exception ex) {
LOG.Warn("Problem while trying to get the iframes, skipping!", ex);
}
}
/// <summary>
/// Corrent the frame locations with the information
/// </summary>
/// <param name="frameElement"></param>
private void CorrectFrameLocations(IHTMLElement frameElement) {
long x = 0;
long y = 0;
IHTMLElement element = frameElement;
IHTMLElement oldElement = null;
do {
x += element.offsetLeft;
y += element.offsetTop;
element = element.offsetParent;
// Release element, but prevent the frameElement to be released
if (oldElement != null) {
releaseCom(oldElement);
}
oldElement = element;
} while (element != null);
Point elementLocation = new Point((int)x, (int)y);
IHTMLElement2 element2 = (IHTMLElement2)frameElement;
IHTMLRect rec = element2.getBoundingClientRect();
Point elementBoundingLocation = new Point(rec.left, rec.top);
// Release IHTMLRect
releaseCom(rec);
LOG.DebugFormat("Looking for iframe to correct at {0}", elementBoundingLocation);
foreach(DocumentContainer foundFrame in _frames) {
Point frameLocation = foundFrame.SourceLocation;
if (frameLocation.Equals(elementBoundingLocation)) {
// Match found, correcting location
LOG.DebugFormat("Correcting frame from {0} to {1}", frameLocation, elementLocation);
foundFrame.SourceLocation = elementLocation;
foundFrame.DestinationLocation = elementLocation;
} else {
LOG.DebugFormat("{0} != {1}", frameLocation, elementBoundingLocation);
}
}
}
/// <summary>
/// A "workaround" for Access Denied when dealing with Frames from different domains
/// </summary>
/// <param name="htmlWindow">The IHTMLWindow2 to get the document from</param>
/// <returns>IHTMLDocument2 or null</returns>
private static IHTMLDocument2 GetDocumentFromWindow(IHTMLWindow2 htmlWindow) {
if (htmlWindow == null) {
LOG.Warn("htmlWindow == null");
return null;
}
// First try the usual way to get the document.
try {
IHTMLDocument2 doc = htmlWindow.document;
return doc;
} catch (COMException comEx) {
// I think COMException won't be ever fired but just to be sure ...
if (comEx.ErrorCode != E_ACCESSDENIED) {
LOG.Warn("comEx.ErrorCode != E_ACCESSDENIED but", comEx);
return null;
}
} catch (UnauthorizedAccessException) {
// This error is okay, ignoring it
} catch (Exception ex1) {
LOG.Warn("Some error: ", ex1);
// Any other error.
return null;
}
// At this point the error was E_ACCESSDENIED because the frame contains a document from another domain.
// IE tries to prevent a cross frame scripting security issue.
try {
// Convert IHTMLWindow2 to IWebBrowser2 using IServiceProvider.
IServiceProvider sp = (IServiceProvider)htmlWindow;
// Use IServiceProvider.QueryService to get IWebBrowser2 object.
Guid webBrowserApp = IID_IWebBrowserApp;
Guid webBrowser2 = IID_IWebBrowser2;
sp.QueryService(ref webBrowserApp, ref webBrowser2, out var brws);
// Get the document from IWebBrowser2.
IWebBrowser2 browser = (IWebBrowser2)brws;
return (IHTMLDocument2)browser.Document;
} catch (Exception ex2) {
LOG.Warn("another error: ", ex2);
}
return null;
}
public Color BackgroundColor {
get {
try {
string bgColor = (string)_document2.bgColor;
if (bgColor != null) {
int rgbInt = int.Parse(bgColor.Substring(1), NumberStyles.HexNumber);
return Color.FromArgb(rgbInt >> 16, (rgbInt >> 8) & 255, rgbInt & 255);
}
} catch (Exception ex) {
LOG.Error("Problem retrieving the background color: ", ex);
}
return Color.White;
}
}
public Rectangle ViewportRectangle => _viewportRectangle;
public WindowDetails ContentWindow => _contentWindow;
public DocumentContainer Parent {
get {
return _parent;
}
set {
_parent = value;
}
}
private int ScaleX(int physicalValue) {
return (int)Math.Round(physicalValue * _zoomLevelX, MidpointRounding.AwayFromZero);
}
private int ScaleY(int physicalValue) {
return (int)Math.Round(physicalValue * _zoomLevelY, MidpointRounding.AwayFromZero);
}
private int UnscaleX(int physicalValue) {
return (int)Math.Round(physicalValue / _zoomLevelX, MidpointRounding.AwayFromZero);
}
private int UnscaleY(int physicalValue) {
return (int)Math.Round(physicalValue / _zoomLevelY, MidpointRounding.AwayFromZero);
}
/// <summary>
/// Set/change an int attribute on a document
/// </summary>
public void SetAttribute(string attribute, int value) {
SetAttribute(attribute, value.ToString());
}
/// <summary>
/// Set/change an attribute on a document
/// </summary>
/// <param name="attribute">Attribute to set</param>
/// <param name="value">Value to set</param>
public void SetAttribute(string attribute, string value) {
var element = !_isDtd ? _document2.body : _document3.documentElement;
element.setAttribute(attribute, value, 1);
// Release IHTMLElement com object
releaseCom(element);
}
/// <summary>
/// Get the attribute from a document
/// </summary>
/// <param name="attribute">Attribute to get</param>
/// <returns>object with the attribute value</returns>
public object GetAttribute(string attribute) {
var element = !_isDtd ? _document2.body : _document3.documentElement;
var retVal = element.getAttribute(attribute, 1);
// Release IHTMLElement com object
releaseCom(element);
return retVal;
}
/// <summary>
/// Get the attribute as int from a document
/// </summary>
public int GetAttributeAsInt(string attribute) {
int retVal = (int)GetAttribute(attribute);
return retVal;
}
public int Id { get; } = _counter++;
public string Name { get; private set; }
public string Url { get; private set; }
public bool IsHidden => ClientWidth == 0 || ClientHeight == 0;
public int ClientWidth => ScaleX(GetAttributeAsInt("clientWidth"));
public int ClientHeight => ScaleY(GetAttributeAsInt("clientHeight"));
public int ScrollWidth => ScaleX(GetAttributeAsInt("scrollWidth"));
public int ScrollHeight => ScaleY(GetAttributeAsInt("scrollHeight"));
public Point SourceLocation {
get {
return _sourceLocation;
}
set {
_sourceLocation = value;
}
}
public Size SourceSize => new Size(ClientWidth, ClientHeight);
public Rectangle SourceRectangle => new Rectangle(SourceLocation, SourceSize);
public int SourceLeft => _sourceLocation.X;
public int SourceTop => _sourceLocation.Y;
public int SourceRight => _sourceLocation.X + ClientWidth;
public int SourceBottom => _sourceLocation.Y + ClientHeight;
public Point DestinationLocation {
get {
return _destinationLocation;
}
set {
_destinationLocation = value;
}
}
public Size DestinationSize => new Size(ScrollWidth, ScrollHeight);
public Rectangle DestinationRectangle => new Rectangle(DestinationLocation, DestinationSize);
public int DestinationLeft {
get {
return _destinationLocation.X;
}
set {
_destinationLocation.X = value;
}
}
public int DestinationTop {
get {
return _destinationLocation.Y;
}
set {
_destinationLocation.Y = value;
}
}
public int DestinationRight => _destinationLocation.X + ScrollWidth;
public int DestinationBottom => _destinationLocation.Y + ScrollHeight;
public int ScrollLeft {
get{
return ScaleX(GetAttributeAsInt("scrollLeft"));
}
set {
SetAttribute("scrollLeft", UnscaleX(value));
}
}
public int ScrollTop {
get{
return ScaleY(GetAttributeAsInt("scrollTop"));
}
set {
SetAttribute("scrollTop", UnscaleY(value));
}
}
public IList<DocumentContainer> Frames => _frames;
}
}

View file

@ -1,535 +0,0 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
using GreenshotPlugin.Core;
using log4net;
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Runtime.InteropServices;
using System.Threading;
using System.Windows.Forms;
using GreenshotPlugin.IniFile;
using GreenshotPlugin.Interfaces;
using GreenshotPlugin.Interfaces.Plugin;
namespace Greenshot.Helpers {
/// <summary>
/// Author: Andrew Baker
/// Datum: 10.03.2006
/// Available from <a href="http://www.vbusers.com/codecsharp/codeget.asp?ThreadID=71&PostID=1">here</a>
/// </summary>
/// <summary>
/// Represents an email message to be sent through MAPI.
/// </summary>
public class MapiMailMessage : IDisposable {
private static readonly ILog Log = LogManager.GetLogger(typeof(MapiMailMessage));
private static readonly CoreConfiguration CoreConfig = IniConfig.GetIniSection<CoreConfiguration>();
/// <summary>
/// Helper Method for creating an Email with Attachment
/// </summary>
/// <param name="fullPath">Path to file</param>
/// <param name="title"></param>
public static void SendImage(string fullPath, string title)
{
using MapiMailMessage message = new MapiMailMessage(title, null);
message.Files.Add(fullPath);
if (!string.IsNullOrEmpty(CoreConfig.MailApiTo)) {
message.Recipients.Add(new Recipient(CoreConfig.MailApiTo, RecipientType.To));
}
if (!string.IsNullOrEmpty(CoreConfig.MailApiCC)) {
message.Recipients.Add(new Recipient(CoreConfig.MailApiCC, RecipientType.CC));
}
if (!string.IsNullOrEmpty(CoreConfig.MailApiBCC)) {
message.Recipients.Add(new Recipient(CoreConfig.MailApiBCC, RecipientType.BCC));
}
message.ShowDialog();
}
/// <summary>
/// Helper Method for creating an Email with Image Attachment
/// </summary>
/// <param name="surface">The image to send</param>
/// <param name="captureDetails">ICaptureDetails</param>
public static void SendImage(ISurface surface, ICaptureDetails captureDetails) {
string tmpFile = ImageOutput.SaveNamedTmpFile(surface, captureDetails, new SurfaceOutputSettings());
if (tmpFile == null) return;
// Store the list of currently active windows, so we can make sure we show the email window later!
var windowsBefore = WindowDetails.GetVisibleWindows();
SendImage(tmpFile, captureDetails.Title);
WindowDetails.ActiveNewerWindows(windowsBefore);
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
private class MapiFileDescriptor {
public int reserved = 0;
public int flags = 0;
public int position;
public string path;
public string name;
public IntPtr type = IntPtr.Zero;
}
/// <summary>
/// Specifies the valid RecipientTypes for a Recipient.
/// </summary>
public enum RecipientType
{
/// <summary>
/// Recipient will be in the TO list.
/// </summary>
To = 1,
/// <summary>
/// Recipient will be in the CC list.
/// </summary>
CC = 2,
/// <summary>
/// Recipient will be in the BCC list.
/// </summary>
BCC = 3
};
private readonly ManualResetEvent _manualResetEvent;
/// <summary>
/// Creates a blank mail message.
/// </summary>
public MapiMailMessage() {
Files = new List<string>();
Recipients = new RecipientCollection();
_manualResetEvent = new ManualResetEvent(false);
}
/// <summary>
/// Creates a new mail message with the specified subject and body.
/// </summary>
public MapiMailMessage(string subject, string body) : this() {
Subject = subject;
Body = body;
}
/// <summary>
/// Gets or sets the subject of this mail message.
/// </summary>
public string Subject { get; set; }
/// <summary>
/// Gets or sets the body of this mail message.
/// </summary>
public string Body { get; set; }
/// <summary>
/// Gets the recipient list for this mail message.
/// </summary>
public RecipientCollection Recipients { get; private set; }
/// <summary>
/// Gets the file list for this mail message.
/// </summary>
public List<string> Files { get; }
/// <summary>
/// Displays the mail message dialog asynchronously.
/// </summary>
public void ShowDialog() {
// Create the mail message in an STA thread
var thread = new Thread(ShowMail)
{
IsBackground = true,
Name = "Create MAPI mail"
};
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
// only return when the new thread has built it's interop representation
_manualResetEvent.WaitOne();
_manualResetEvent.Reset();
}
public void Dispose() {
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing) {
if (!disposing) {
return;
}
_manualResetEvent?.Close();
}
/// <summary>
/// Sends the mail message.
/// </summary>
private void ShowMail()
{
while (true)
{
var message = new MapiHelperInterop.MapiMessage();
using var interopRecipients = Recipients.GetInteropRepresentation();
message.Subject = Subject;
message.NoteText = Body;
message.Recipients = interopRecipients.Handle;
message.RecipientCount = Recipients.Count;
// Check if we need to add attachments
if (Files.Count > 0)
{
// Add attachments
message.Files = AllocAttachments(out message.FileCount);
}
// Signal the creating thread (make the remaining code async)
_manualResetEvent.Set();
const int MAPI_DIALOG = 0x8;
//const int MAPI_LOGON_UI = 0x1;
int error = MapiHelperInterop.MAPISendMail(IntPtr.Zero, IntPtr.Zero, message, MAPI_DIALOG, 0);
if (Files.Count > 0)
{
// Deallocate the files
DeallocFiles(message);
}
MAPI_CODES errorCode = (MAPI_CODES) Enum.ToObject(typeof(MAPI_CODES), error);
// Check for error
if (errorCode == MAPI_CODES.SUCCESS || errorCode == MAPI_CODES.USER_ABORT)
{
return;
}
string errorText = GetMapiError(errorCode);
Log.Error("Error sending MAPI Email. Error: " + errorText + " (code = " + errorCode + ").");
MessageBox.Show(errorText, "Mail (MAPI) destination", MessageBoxButtons.OK, MessageBoxIcon.Error);
// Recover from bad settings, show again
if (errorCode != MAPI_CODES.INVALID_RECIPS)
{
return;
}
Recipients = new RecipientCollection();
}
}
/// <summary>
/// Deallocates the files in a message.
/// </summary>
/// <param name="message">The message to deallocate the files from.</param>
private void DeallocFiles(MapiHelperInterop.MapiMessage message)
{
if (message.Files == IntPtr.Zero) return;
Type fileDescType = typeof(MapiFileDescriptor);
int fsize = Marshal.SizeOf(fileDescType);
// Get the ptr to the files
IntPtr runptr = message.Files;
// Release each file
for (int i = 0; i < message.FileCount; i++) {
Marshal.DestroyStructure(runptr, fileDescType);
runptr = new IntPtr(runptr.ToInt64() + fsize);
}
// Release the file
Marshal.FreeHGlobal(message.Files);
}
/// <summary>
/// Allocates the file attachments
/// </summary>
/// <param name="fileCount"></param>
/// <returns></returns>
private IntPtr AllocAttachments(out int fileCount) {
fileCount = 0;
if (Files == null) {
return IntPtr.Zero;
}
if ((Files.Count <= 0) || (Files.Count > 100)) {
return IntPtr.Zero;
}
Type atype = typeof(MapiFileDescriptor);
int asize = Marshal.SizeOf(atype);
IntPtr ptra = Marshal.AllocHGlobal(Files.Count * asize);
MapiFileDescriptor mfd = new MapiFileDescriptor
{
position = -1
};
IntPtr runptr = ptra;
foreach (string path in Files)
{
mfd.name = Path.GetFileName(path);
mfd.path = path;
Marshal.StructureToPtr(mfd, runptr, false);
runptr = new IntPtr(runptr.ToInt64() + asize);
}
fileCount = Files.Count;
return ptra;
}
private enum MAPI_CODES {
SUCCESS = 0,
USER_ABORT = 1,
FAILURE = 2,
LOGIN_FAILURE = 3,
DISK_FULL = 4,
INSUFFICIENT_MEMORY = 5,
BLK_TOO_SMALL = 6,
TOO_MANY_SESSIONS = 8,
TOO_MANY_FILES = 9,
TOO_MANY_RECIPIENTS = 10,
ATTACHMENT_NOT_FOUND = 11,
ATTACHMENT_OPEN_FAILURE = 12,
ATTACHMENT_WRITE_FAILURE = 13,
UNKNOWN_RECIPIENT = 14,
BAD_RECIPTYPE = 15,
NO_MESSAGES = 16,
INVALID_MESSAGE = 17,
TEXT_TOO_LARGE = 18,
INVALID_SESSION = 19,
TYPE_NOT_SUPPORTED = 20,
AMBIGUOUS_RECIPIENT = 21,
MESSAGE_IN_USE = 22,
NETWORK_FAILURE = 23,
INVALID_EDITFIELDS = 24,
INVALID_RECIPS = 25,
NOT_SUPPORTED = 26,
NO_LIBRARY = 999,
INVALID_PARAMETER = 998
}
/// <summary>
/// Logs any Mapi errors.
/// </summary>
private string GetMapiError(MAPI_CODES errorCode)
{
string error = errorCode switch
{
MAPI_CODES.USER_ABORT => "User Aborted.",
MAPI_CODES.FAILURE => "MAPI Failure.",
MAPI_CODES.LOGIN_FAILURE => "Login Failure.",
MAPI_CODES.DISK_FULL => "MAPI Disk full.",
MAPI_CODES.INSUFFICIENT_MEMORY => "MAPI Insufficient memory.",
MAPI_CODES.BLK_TOO_SMALL => "MAPI Block too small.",
MAPI_CODES.TOO_MANY_SESSIONS => "MAPI Too many sessions.",
MAPI_CODES.TOO_MANY_FILES => "MAPI too many files.",
MAPI_CODES.TOO_MANY_RECIPIENTS => "MAPI too many recipients.",
MAPI_CODES.ATTACHMENT_NOT_FOUND => "MAPI Attachment not found.",
MAPI_CODES.ATTACHMENT_OPEN_FAILURE => "MAPI Attachment open failure.",
MAPI_CODES.ATTACHMENT_WRITE_FAILURE => "MAPI Attachment Write Failure.",
MAPI_CODES.UNKNOWN_RECIPIENT => "MAPI Unknown recipient.",
MAPI_CODES.BAD_RECIPTYPE => "MAPI Bad recipient type.",
MAPI_CODES.NO_MESSAGES => "MAPI No messages.",
MAPI_CODES.INVALID_MESSAGE => "MAPI Invalid message.",
MAPI_CODES.TEXT_TOO_LARGE => "MAPI Text too large.",
MAPI_CODES.INVALID_SESSION => "MAPI Invalid session.",
MAPI_CODES.TYPE_NOT_SUPPORTED => "MAPI Type not supported.",
MAPI_CODES.AMBIGUOUS_RECIPIENT => "MAPI Ambiguous recipient.",
MAPI_CODES.MESSAGE_IN_USE => "MAPI Message in use.",
MAPI_CODES.NETWORK_FAILURE => "MAPI Network failure.",
MAPI_CODES.INVALID_EDITFIELDS => "MAPI Invalid edit fields.",
MAPI_CODES.INVALID_RECIPS => "MAPI Invalid Recipients.",
MAPI_CODES.NOT_SUPPORTED => "MAPI Not supported.",
MAPI_CODES.NO_LIBRARY => "MAPI No Library.",
MAPI_CODES.INVALID_PARAMETER => "MAPI Invalid parameter.",
_ => string.Empty
};
return error;
}
/// <summary>
/// Internal class for calling MAPI APIs
/// </summary>
internal class MapiHelperInterop {
/// <summary>
/// Private constructor.
/// </summary>
private MapiHelperInterop() {
// Intenationally blank
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public class MapiMessage {
public int Reserved = 0;
public string Subject;
public string NoteText;
public string MessageType = null;
public string DateReceived = null;
public string ConversationID = null;
public int Flags = 0;
public IntPtr Originator = IntPtr.Zero;
public int RecipientCount;
public IntPtr Recipients = IntPtr.Zero;
public int FileCount;
public IntPtr Files = IntPtr.Zero;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public class MapiRecipDesc {
public int Reserved = 0;
public int RecipientClass;
public string Name;
public string Address;
public int eIDSize = 0;
public IntPtr EntryID = IntPtr.Zero;
}
[DllImport("MAPI32.DLL", SetLastError = true, CharSet=CharSet.Ansi)]
public static extern int MAPISendMail(IntPtr session, IntPtr hWnd, MapiMessage message, int flg, int rsv);
}
}
/// <summary>
/// Represents a Recipient for a MapiMailMessage.
/// </summary>
public class Recipient {
/// <summary>
/// The email address of this recipient.
/// </summary>
public string Address;
/// <summary>
/// The display name of this recipient.
/// </summary>
public string DisplayName;
/// <summary>
/// How the recipient will receive this message (To, CC, BCC).
/// </summary>
public MapiMailMessage.RecipientType RecipientType = MapiMailMessage.RecipientType.To;
/// <summary>
/// Creates a new recipient with the specified address and recipient type.
/// </summary>
public Recipient(string address, MapiMailMessage.RecipientType recipientType) {
Address = address;
RecipientType = recipientType;
}
/// <summary>
/// Returns an interop representation of a recepient.
/// </summary>
/// <returns></returns>
internal MapiMailMessage.MapiHelperInterop.MapiRecipDesc GetInteropRepresentation() {
MapiMailMessage.MapiHelperInterop.MapiRecipDesc interop = new MapiMailMessage.MapiHelperInterop.MapiRecipDesc();
if (DisplayName == null) {
interop.Name = Address;
} else {
interop.Name = DisplayName;
interop.Address = Address;
}
interop.RecipientClass = (int)RecipientType;
return interop;
}
}
/// <summary>
/// Represents a collection of recipients for a mail message.
/// </summary>
public class RecipientCollection : CollectionBase {
/// <summary>
/// Adds the specified recipient to this collection.
/// </summary>
public void Add(Recipient value) {
List.Add(value);
}
internal InteropRecipientCollection GetInteropRepresentation() {
return new InteropRecipientCollection(this);
}
/// <summary>
/// Struct which contains an interop representation of a colleciton of recipients.
/// </summary>
internal struct InteropRecipientCollection : IDisposable {
private int _count;
/// <summary>
/// Default constructor for creating InteropRecipientCollection.
/// </summary>
/// <param name="outer"></param>
public InteropRecipientCollection(RecipientCollection outer) {
_count = outer.Count;
if (_count == 0) {
Handle = IntPtr.Zero;
return;
}
// allocate enough memory to hold all recipients
int size = Marshal.SizeOf(typeof(MapiMailMessage.MapiHelperInterop.MapiRecipDesc));
Handle = Marshal.AllocHGlobal(_count * size);
// place all interop recipients into the memory just allocated
IntPtr ptr = Handle;
foreach (Recipient native in outer) {
MapiMailMessage.MapiHelperInterop.MapiRecipDesc interop = native.GetInteropRepresentation();
// stick it in the memory block
Marshal.StructureToPtr(interop, ptr, false);
ptr = new IntPtr(ptr.ToInt64() + size);
}
}
public IntPtr Handle { get; private set; }
/// <summary>
/// Disposes of resources.
/// </summary>
public void Dispose()
{
if (Handle == IntPtr.Zero) return;
Type type = typeof(MapiMailMessage.MapiHelperInterop.MapiRecipDesc);
int size = Marshal.SizeOf(type);
// destroy all the structures in the memory area
IntPtr ptr = Handle;
for (int i = 0; i < _count; i++) {
Marshal.DestroyStructure(ptr, type);
ptr = new IntPtr(ptr.ToInt64() + size);
}
// free the memory
Marshal.FreeHGlobal(Handle);
Handle = IntPtr.Zero;
_count = 0;
}
}
}
}

View file

@ -1,245 +0,0 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Windows.Forms;
using GreenshotPlugin.Core;
using GreenshotPlugin.IniFile;
using GreenshotPlugin.Interfaces;
using GreenshotPlugin.Interfaces.Plugin;
using log4net;
namespace Greenshot.Helpers {
/// <summary>
/// The PluginHelper takes care of all plugin related functionality
/// </summary>
[Serializable]
public class PluginHelper : IGreenshotHost {
private static readonly ILog Log = LogManager.GetLogger(typeof(PluginHelper));
private static readonly CoreConfiguration CoreConfig = IniConfig.GetIniSection<CoreConfiguration>();
private static readonly string PluginPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),Application.ProductName);
private static readonly string ApplicationPath = Path.GetDirectoryName(Application.ExecutablePath);
private static readonly string PafPath = Path.Combine(Application.StartupPath, @"App\Greenshot");
public static PluginHelper Instance { get; } = new PluginHelper();
public void Shutdown() {
foreach(var plugin in SimpleServiceProvider.Current.GetAllInstances<IGreenshotPlugin>()) {
plugin.Shutdown();
plugin.Dispose();
}
}
/// <summary>
/// Add plugins to the ListView
/// </summary>
/// <param name="listView"></param>
public void FillListView(ListView listView) {
foreach (var plugin in SimpleServiceProvider.Current.GetAllInstances<IGreenshotPlugin>())
{
var pluginAttribute = plugin.GetType().GetCustomAttribute<PluginAttribute>();
var item = new ListViewItem(pluginAttribute.Name)
{
Tag = pluginAttribute
};
var assembly = plugin.GetType().Assembly;
var company = assembly.GetCustomAttribute<AssemblyCompanyAttribute>();
item.SubItems.Add(assembly.GetName().Version.ToString());
item.SubItems.Add(company.Company);
item.SubItems.Add(assembly.Location);
listView.Items.Add(item);
}
}
public bool IsSelectedItemConfigurable(ListView listView) {
if (listView.SelectedItems.Count <= 0)
{
return false;
}
var pluginAttribute = (PluginAttribute)listView.SelectedItems[0].Tag;
return pluginAttribute != null && pluginAttribute.Configurable;
}
public void ConfigureSelectedItem(ListView listView) {
if (listView.SelectedItems.Count <= 0)
{
return;
}
var pluginAttribute = (PluginAttribute)listView.SelectedItems[0].Tag;
if (pluginAttribute == null)
{
return;
}
var plugin = SimpleServiceProvider.Current.GetAllInstances<IGreenshotPlugin>().FirstOrDefault(p =>
p.GetType().GetCustomAttribute<PluginAttribute>().Name == pluginAttribute.Name);
plugin?.Configure();
}
/// <summary>
/// Create a Thumbnail
/// </summary>
/// <param name="image">Image of which we need a Thumbnail</param>
/// <param name="width">Thumbnail width</param>
/// <param name="height">Thumbnail height</param>
/// <returns>Image with Thumbnail</returns>
public Image GetThumbnail(Image image, int width, int height) {
return image.GetThumbnailImage(width, height, ThumbnailCallback, IntPtr.Zero);
}
/// <summary>
/// Required for GetThumbnail, but not used
/// </summary>
/// <returns>true</returns>
private bool ThumbnailCallback() {
return true;
}
public ExportInformation ExportCapture(bool manuallyInitiated, string designation, ISurface surface, ICaptureDetails captureDetails) {
return DestinationHelper.ExportCapture(manuallyInitiated, designation, surface, captureDetails);
}
/// <summary>
/// Make Capture with specified Handler
/// </summary>
/// <param name="captureMouseCursor">bool false if the mouse should not be captured, true if the configuration should be checked</param>
/// <param name="destination">IDestination</param>
public void CaptureRegion(bool captureMouseCursor, IDestination destination) {
CaptureHelper.CaptureRegion(captureMouseCursor, destination);
}
/// <summary>
/// Use the supplied image, and handle it as if it's captured.
/// </summary>
/// <param name="captureToImport">Image to handle</param>
public void ImportCapture(ICapture captureToImport)
{
var mainForm = SimpleServiceProvider.Current.GetInstance<Form>();
mainForm.BeginInvoke((MethodInvoker)delegate {
CaptureHelper.ImportCapture(captureToImport);
});
}
/// <summary>
/// Get an ICapture object, so the plugin can modify this
/// </summary>
/// <returns></returns>
public ICapture GetCapture(Image imageToCapture) {
var capture = new Capture(imageToCapture)
{
CaptureDetails = new CaptureDetails
{
CaptureMode = CaptureMode.Import,
Title = "Imported"
}
};
return capture;
}
/// <summary>
/// Private helper to find the plugins in the path
/// </summary>
/// <param name="path">string</param>
/// <returns>IEnumerable with plugin files</returns>
private IEnumerable<string> FindPluginsOnPath(string path)
{
var pluginFiles = Enumerable.Empty<string>();
if (!Directory.Exists(path)) return pluginFiles;
try
{
pluginFiles = Directory.GetFiles(path, "*Plugin.dll", SearchOption.AllDirectories)
// Skip the GreenshotPlugin.dll itself
.Where(p => CultureInfo.CurrentCulture.CompareInfo.IndexOf(p, "GreenshotPlugin.dll", CompareOptions.IgnoreCase) < 0);
} catch (Exception ex) {
Log.Error("Error loading plugin: ", ex);
}
return pluginFiles;
}
/// <summary>
/// Load the plugins
/// </summary>
public void LoadPlugins() {
List<string> pluginFiles = new List<string>();
if (IniConfig.IsPortable) {
pluginFiles.AddRange(FindPluginsOnPath(PafPath));
} else {
pluginFiles.AddRange(FindPluginsOnPath(PluginPath));
pluginFiles.AddRange(FindPluginsOnPath(ApplicationPath));
}
// Loop over the list of available files and get the Plugin Attributes
foreach (string pluginFile in pluginFiles) {
try {
Assembly assembly = Assembly.LoadFrom(pluginFile);
var assemblyName = assembly.GetName().Name;
var pluginEntryName = $"{assemblyName}.{assemblyName.Replace("Greenshot", string.Empty)}";
var pluginEntryType = assembly.GetType(pluginEntryName, false, true);
var pluginAttribute = pluginEntryType.GetCustomAttribute<PluginAttribute>();
if (CoreConfig.ExcludePlugins != null && CoreConfig.ExcludePlugins.Contains(pluginAttribute.Name))
{
Log.WarnFormat("Exclude list: {0}", string.Join(",", CoreConfig.ExcludePlugins));
Log.WarnFormat("Skipping the excluded plugin {0} with version {1} from {2}", pluginAttribute.Name, assembly.GetName().Version, pluginFile);
continue;
}
IGreenshotPlugin plugin = (IGreenshotPlugin)Activator.CreateInstance(pluginEntryType);
if (plugin != null)
{
if (plugin.Initialize())
{
SimpleServiceProvider.Current.AddService(plugin);
}
else
{
Log.InfoFormat("Plugin {0} not initialized!", pluginAttribute.Name);
}
}
else
{
Log.ErrorFormat("Can't create an instance of {0} from \"{1}\"", assembly.GetName().Name + ".GreenshotPlugin", pluginFile);
}
}
catch (Exception e)
{
Log.ErrorFormat("Can't load Plugin: {0}", pluginFile);
Log.Error(e);
}
}
}
}
}

View file

@ -1,240 +0,0 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
using System;
using System.Drawing;
using System.Drawing.Printing;
using System.Windows.Forms;
using Greenshot.Configuration;
using Greenshot.Forms;
using GreenshotPlugin.Core;
using GreenshotPlugin.Effects;
using GreenshotPlugin.IniFile;
using GreenshotPlugin.Interfaces;
using GreenshotPlugin.Interfaces.Plugin;
using log4net;
namespace Greenshot.Helpers {
/// <summary>
/// Description of PrintHelper.
/// </summary>
public class PrintHelper : IDisposable {
private static readonly ILog Log = LogManager.GetLogger(typeof(PrintHelper));
private static readonly CoreConfiguration CoreConfig = IniConfig.GetIniSection<CoreConfiguration>();
private ISurface _surface;
private readonly ICaptureDetails _captureDetails;
private PrintDocument _printDocument = new PrintDocument();
private PrintDialog _printDialog = new PrintDialog();
public PrintHelper(ISurface surface, ICaptureDetails captureDetails) {
_surface = surface;
_captureDetails = captureDetails;
_printDialog.UseEXDialog = true;
_printDocument.DocumentName = FilenameHelper.GetFilenameWithoutExtensionFromPattern(CoreConfig.OutputFileFilenamePattern, captureDetails);
_printDocument.PrintPage += DrawImageForPrint;
_printDialog.Document = _printDocument;
}
/**
* Destructor
*/
~PrintHelper() {
Dispose(false);
}
/**
* The public accessible Dispose
* Will call the GarbageCollector to SuppressFinalize, preventing being cleaned twice
*/
public void Dispose() {
Dispose(true);
GC.SuppressFinalize(this);
}
/**
* This Dispose is called from the Dispose and the Destructor.
* When disposing==true all non-managed resources should be freed too!
*/
protected virtual void Dispose(bool disposing) {
if (disposing) {
_printDocument?.Dispose();
_printDialog?.Dispose();
}
_surface = null;
_printDocument = null;
_printDialog = null;
}
/// <summary>
/// displays options dialog (if not disabled via settings) and windows
/// print dialog.
/// </summary>
/// <returns>printer settings if actually printed, or null if print was cancelled or has failed</returns>
public PrinterSettings PrintTo(string printerName) {
PrinterSettings returnPrinterSettings = null;
DialogResult? printOptionsResult = ShowPrintOptionsDialog();
try {
if (printOptionsResult == null || printOptionsResult == DialogResult.OK) {
_printDocument.PrinterSettings.PrinterName = printerName;
if (!IsColorPrint()) {
_printDocument.DefaultPageSettings.Color = false;
}
_printDocument.Print();
returnPrinterSettings = _printDocument.PrinterSettings;
}
} catch (Exception e) {
Log.Error("An error occurred while trying to print", e);
MessageBox.Show(Language.GetString(LangKey.print_error), Language.GetString(LangKey.error));
}
return returnPrinterSettings;
}
/// <summary>
/// displays options dialog (if not disabled via settings) and windows
/// print dialog.
/// </summary>
/// <returns>printer settings if actually printed, or null if print was cancelled or has failed</returns>
public PrinterSettings PrintWithDialog() {
PrinterSettings returnPrinterSettings = null;
if (_printDialog.ShowDialog() != DialogResult.OK) return null;
DialogResult? printOptionsResult = ShowPrintOptionsDialog();
try {
if (printOptionsResult == null || printOptionsResult == DialogResult.OK) {
if (!IsColorPrint()) {
_printDocument.DefaultPageSettings.Color = false;
}
_printDocument.Print();
returnPrinterSettings = _printDialog.PrinterSettings;
}
} catch (Exception e) {
Log.Error("An error occurred while trying to print", e);
MessageBox.Show(Language.GetString(LangKey.print_error), Language.GetString(LangKey.error));
}
return returnPrinterSettings;
}
private bool IsColorPrint() {
return !CoreConfig.OutputPrintGrayscale && !CoreConfig.OutputPrintMonochrome;
}
/// <summary>
/// display print options dialog (if the user has not configured Greenshot not to)
/// </summary>
/// <returns>result of the print dialog, or null if the dialog has not been displayed by config</returns>
private DialogResult? ShowPrintOptionsDialog() {
DialogResult? ret = null;
if (CoreConfig.OutputPrintPromptOptions)
{
using PrintOptionsDialog printOptionsDialog = new PrintOptionsDialog();
ret = printOptionsDialog.ShowDialog();
}
return ret;
}
private void DrawImageForPrint(object sender, PrintPageEventArgs e) {
// Create the output settings
SurfaceOutputSettings printOutputSettings = new SurfaceOutputSettings(OutputFormat.png, 100, false);
ApplyEffects(printOutputSettings);
bool disposeImage = ImageOutput.CreateImageFromSurface(_surface, printOutputSettings, out var image);
try {
ContentAlignment alignment = CoreConfig.OutputPrintCenter ? ContentAlignment.MiddleCenter : ContentAlignment.TopLeft;
// prepare timestamp
float footerStringWidth = 0;
float footerStringHeight = 0;
string footerString = null; //DateTime.Now.ToLongDateString() + " " + DateTime.Now.ToLongTimeString();
if (CoreConfig.OutputPrintFooter) {
footerString = FilenameHelper.FillPattern(CoreConfig.OutputPrintFooterPattern, _captureDetails, false);
using Font f = new Font(FontFamily.GenericSansSerif, 10, FontStyle.Regular);
footerStringWidth = e.Graphics.MeasureString(footerString, f).Width;
footerStringHeight = e.Graphics.MeasureString(footerString, f).Height;
}
// Get a rectangle representing the printable Area
RectangleF pageRect = e.PageSettings.PrintableArea;
if(e.PageSettings.Landscape) {
float origWidth = pageRect.Width;
pageRect.Width = pageRect.Height;
pageRect.Height = origWidth;
}
// Subtract the dateString height from the available area, this way the area stays free
pageRect.Height -= footerStringHeight;
GraphicsUnit gu = GraphicsUnit.Pixel;
RectangleF imageRect = image.GetBounds(ref gu);
// rotate the image if it fits the page better
if (CoreConfig.OutputPrintAllowRotate) {
if (pageRect.Width > pageRect.Height && imageRect.Width < imageRect.Height || pageRect.Width < pageRect.Height && imageRect.Width > imageRect.Height) {
image.RotateFlip(RotateFlipType.Rotate270FlipNone);
imageRect = image.GetBounds(ref gu);
if (alignment.Equals(ContentAlignment.TopLeft)) {
alignment = ContentAlignment.TopRight;
}
}
}
RectangleF printRect = new RectangleF(0, 0, imageRect.Width, imageRect.Height);
// scale the image to fit the page better
if (CoreConfig.OutputPrintAllowEnlarge || CoreConfig.OutputPrintAllowShrink) {
SizeF resizedRect = ScaleHelper.GetScaledSize(imageRect.Size, pageRect.Size, false);
if (CoreConfig.OutputPrintAllowShrink && resizedRect.Width < printRect.Width || CoreConfig.OutputPrintAllowEnlarge && resizedRect.Width > printRect.Width) {
printRect.Size = resizedRect;
}
}
// align the image
printRect = ScaleHelper.GetAlignedRectangle(printRect, new RectangleF(0, 0, pageRect.Width, pageRect.Height), alignment);
if (CoreConfig.OutputPrintFooter)
{
//printRect = new RectangleF(0, 0, printRect.Width, printRect.Height - (dateStringHeight * 2));
using Font f = new Font(FontFamily.GenericSansSerif, 10, FontStyle.Regular);
e.Graphics.DrawString(footerString, f, Brushes.Black, pageRect.Width / 2 - footerStringWidth / 2, pageRect.Height);
}
e.Graphics.DrawImage(image, printRect, imageRect, GraphicsUnit.Pixel);
} finally {
if (disposeImage) {
image?.Dispose();
}
}
}
private void ApplyEffects(SurfaceOutputSettings printOutputSettings) {
// TODO:
// add effects here
if (CoreConfig.OutputPrintMonochrome) {
byte threshold = CoreConfig.OutputPrintMonochromeThreshold;
printOutputSettings.Effects.Add(new MonochromeEffect(threshold));
printOutputSettings.ReduceColors = true;
}
// the invert effect should probably be the last
if (CoreConfig.OutputPrintInverted) {
printOutputSettings.Effects.Add(new InvertEffect());
}
}
}
}

View file

@ -1,65 +0,0 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
using System;
using GreenshotPlugin.Core;
using GreenshotPlugin.Interfaces;
using log4net;
namespace Greenshot.Helpers {
/// <summary>
/// Description of ProcessorHelper.
/// </summary>
public static class ProcessorHelper {
private static readonly ILog LOG = LogManager.GetLogger(typeof(ProcessorHelper));
/// <summary>
/// Register the internal processors
/// </summary>
public static void RegisterInternalProcessors() {
foreach(Type processorType in InterfaceUtils.GetSubclassesOf(typeof(IProcessor),true)) {
// Only take our own
if (!"Greenshot.Processors".Equals(processorType.Namespace)) {
continue;
}
try {
if (!processorType.IsAbstract) {
IProcessor processor;
try {
processor = (IProcessor)Activator.CreateInstance(processorType);
} catch (Exception e) {
LOG.ErrorFormat("Can't create instance of {0}", processorType);
LOG.Error(e);
continue;
}
if (processor.isActive) {
LOG.DebugFormat("Found Processor {0} with designation {1}", processorType.Name, processor.Designation);
SimpleServiceProvider.Current.AddService(processor);
} else {
LOG.DebugFormat("Ignoring Processor {0} with designation {1}", processorType.Name, processor.Designation);
}
}
} catch (Exception ex) {
LOG.ErrorFormat("Error loading processor {0}, message: ", processorType.FullName, ex.Message);
}
}
}
}
}

View file

@ -1,175 +0,0 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
using log4net;
using System;
using System.Security.AccessControl;
using System.Security.Principal;
using System.Threading;
namespace Greenshot.Helpers
{
/// <summary>
/// This protects your resources or application from running more than once
/// Simplifies the usage of the Mutex class, as described here:
/// https://msdn.microsoft.com/en-us/library/System.Threading.Mutex.aspx
/// </summary>
public class ResourceMutex : IDisposable
{
private static readonly ILog Log = LogManager.GetLogger(typeof(ResourceMutex));
private readonly string _mutexId;
private readonly string _resourceName;
private Mutex _applicationMutex;
/// <summary>
/// Private constructor
/// </summary>
/// <param name="mutexId"></param>
/// <param name="resourceName"></param>
private ResourceMutex(string mutexId, string resourceName = null)
{
_mutexId = mutexId;
_resourceName = resourceName ?? "some resource";
}
/// <summary>
/// Test if the Mutex was created and locked.
/// </summary>
public bool IsLocked { get; set; }
/// <summary>
/// Create a ResourceMutex for the specified mutex id and resource-name
/// </summary>
/// <param name="mutexId">ID of the mutex, preferably a Guid as string</param>
/// <param name="resourceName">Name of the resource to lock, e.g your application name, usefull for logs</param>
/// <param name="global">true to have a global mutex see: https://msdn.microsoft.com/en-us/library/bwe34f1k.aspx </param>
public static ResourceMutex Create(string mutexId, string resourceName = null, bool global = false)
{
var applicationMutex = new ResourceMutex((global ? @"Global\" : @"Local\") + mutexId, resourceName);
applicationMutex.Lock();
return applicationMutex;
}
/// <summary>
/// This tries to get the Mutex, which takes care of having multiple instances running
/// </summary>
/// <returns>true if it worked, false if another instance is already running or something went wrong</returns>
public bool Lock()
{
Log.DebugFormat("{0} is trying to get Mutex {1}", _resourceName, _mutexId);
IsLocked = true;
// check whether there's an local instance running already, but use local so this works in a multi-user environment
try
{
// Added Mutex Security, hopefully this prevents the UnauthorizedAccessException more gracefully
var sid = new SecurityIdentifier(WellKnownSidType.WorldSid, null);
var mutexSecurity = new MutexSecurity();
mutexSecurity.AddAccessRule(new MutexAccessRule(sid, MutexRights.FullControl, AccessControlType.Allow));
mutexSecurity.AddAccessRule(new MutexAccessRule(sid, MutexRights.ChangePermissions, AccessControlType.Deny));
mutexSecurity.AddAccessRule(new MutexAccessRule(sid, MutexRights.Delete, AccessControlType.Deny));
// 1) Create Mutex
_applicationMutex = new Mutex(true, _mutexId, out var createdNew, mutexSecurity);
// 2) if the mutex wasn't created new get the right to it, this returns false if it's already locked
if (!createdNew && !_applicationMutex.WaitOne(100, false))
{
Log.InfoFormat("{0} is already in use, mutex {1} is NOT locked for the caller", _resourceName, _mutexId);
IsLocked = false;
// Clean up
_applicationMutex.Close();
_applicationMutex = null;
}
else
{
Log.InfoFormat(createdNew ? "{0} has created & claimed the mutex {1}" : "{0} has claimed the mutex {1}", _resourceName, _mutexId);
}
}
catch (AbandonedMutexException e)
{
// Another instance didn't cleanup correctly!
// we can ignore the exception, it happened on the "waitOne" but still the mutex belongs to us
Log.WarnFormat("{0} didn't cleanup correctly, but we got the mutex {1}.", _resourceName, _mutexId);
Log.Warn(e);
}
catch (UnauthorizedAccessException e)
{
Log.ErrorFormat("{0} is most likely already running for a different user in the same session, can't create/get mutex {1} due to error.", _resourceName, _mutexId);
Log.Error(e);
IsLocked = false;
}
catch (Exception ex)
{
Log.ErrorFormat("Problem obtaining the Mutex {1} for {0}, assuming it was already taken!", _resourceName, _mutexId);
Log.Error(ex);
IsLocked = false;
}
return IsLocked;
}
// To detect redundant Dispose calls
private bool _disposedValue;
/// <summary>
/// The real disposing code
/// </summary>
/// <param name="disposing">true if dispose is called, false when the finalizer is called</param>
protected void Dispose(bool disposing)
{
if (_disposedValue) return;
if (_applicationMutex != null)
{
try
{
_applicationMutex.ReleaseMutex();
_applicationMutex = null;
Log.InfoFormat("Released Mutex {0} for {1}", _mutexId, _resourceName);
}
catch (Exception ex)
{
Log.ErrorFormat("Error releasing Mutex {0} for {1}", _mutexId, _resourceName);
Log.Error(ex);
}
}
_disposedValue = true;
}
/// <summary>
/// Make sure the ApplicationMutex is disposed when the finalizer is called
/// </summary>
~ResourceMutex()
{
// Do not change this code. Put cleanup code in Dispose(bool disposing) above.
Dispose(false);
}
/// <summary>
/// The dispose interface, which calls Dispose(true) to signal that dispose is called.
/// </summary>
public void Dispose()
{
// Do not change this code. Put cleanup code in Dispose(bool disposing) above.
Dispose(true);
GC.SuppressFinalize(this);
}
}
}

View file

@ -1,339 +0,0 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
using System;
using System.Drawing;
using System.Windows.Forms;
using Greenshot.Drawing;
namespace Greenshot.Helpers {
/// <summary>
/// Offers a few helper functions for scaling/aligning an element with another element
/// </summary>
public static class ScaleHelper {
[Flags]
public enum ScaleOptions {
/// <summary>
/// Default scale behavior.
/// </summary>
Default = 0x00,
/// <summary>
/// Scale a rectangle in two our four directions, mirrored at it's center coordinates
/// </summary>
Centered = 0x01,
/// <summary>
/// Scale a rectangle maintaining it's aspect ratio
/// </summary>
Rational = 0x02
}
/// <summary>
/// calculates the Size an element must be resized to, in order to fit another element, keeping aspect ratio
/// </summary>
/// <param name="currentSize">the size of the element to be resized</param>
/// <param name="targetSize">the target size of the element</param>
/// <param name="crop">in case the aspect ratio of currentSize and targetSize differs: shall the scaled size fit into targetSize (i.e. that one of its dimensions is smaller - false) or vice versa (true)</param>
/// <returns>a new SizeF object indicating the width and height the element should be scaled to</returns>
public static SizeF GetScaledSize(SizeF currentSize, SizeF targetSize, bool crop) {
float wFactor = targetSize.Width/currentSize.Width;
float hFactor = targetSize.Height/currentSize.Height;
float factor = crop ? Math.Max(wFactor, hFactor) : Math.Min(wFactor, hFactor);
return new SizeF(currentSize.Width * factor, currentSize.Height * factor);
}
/// <summary>
/// calculates the position of an element depending on the desired alignment within a RectangleF
/// </summary>
/// <param name="currentRect">the bounds of the element to be aligned</param>
/// <param name="targetRect">the rectangle reference for alignment of the element</param>
/// <param name="alignment">the System.Drawing.ContentAlignment value indicating how the element is to be aligned should the width or height differ from targetSize</param>
/// <returns>a new RectangleF object with Location aligned aligned to targetRect</returns>
public static RectangleF GetAlignedRectangle(RectangleF currentRect, RectangleF targetRect, ContentAlignment alignment) {
RectangleF newRect = new RectangleF(targetRect.Location, currentRect.Size);
switch(alignment) {
case ContentAlignment.TopCenter:
newRect.X = (targetRect.Width - currentRect.Width) / 2;
break;
case ContentAlignment.TopRight:
newRect.X = targetRect.Width - currentRect.Width;
break;
case ContentAlignment.MiddleLeft:
newRect.Y = (targetRect.Height - currentRect.Height) / 2;
break;
case ContentAlignment.MiddleCenter:
newRect.Y = (targetRect.Height - currentRect.Height) / 2;
newRect.X = (targetRect.Width - currentRect.Width) / 2;
break;
case ContentAlignment.MiddleRight:
newRect.Y = (targetRect.Height - currentRect.Height) / 2;
newRect.X = targetRect.Width - currentRect.Width;
break;
case ContentAlignment.BottomLeft:
newRect.Y = targetRect.Height - currentRect.Height;
break;
case ContentAlignment.BottomCenter:
newRect.Y = targetRect.Height - currentRect.Height;
newRect.X = (targetRect.Width - currentRect.Width) / 2;
break;
case ContentAlignment.BottomRight:
newRect.Y = targetRect.Height - currentRect.Height;
newRect.X = targetRect.Width - currentRect.Width;
break;
}
return newRect;
}
/// <summary>
/// Calculates target size of a given rectangle scaled by dragging one of its handles (corners)
/// </summary>
/// <param name="originalRectangle">bounds of the current rectangle, scaled values will be written to this reference</param>
/// <param name="resizeHandlePosition">position of the handle/gripper being used for resized, see constants in Gripper.cs, e.g. Gripper.POSITION_TOP_LEFT</param>
/// <param name="resizeHandleCoords">coordinates of the used handle/gripper</param>
/// <param name="options">ScaleOptions to use when scaling</param>
public static void Scale(ref RectangleF originalRectangle, Positions resizeHandlePosition, PointF resizeHandleCoords, ScaleOptions? options) {
options ??= GetScaleOptions();
if ((options & ScaleOptions.Rational) == ScaleOptions.Rational) {
AdjustCoordsForRationalScale(originalRectangle, resizeHandlePosition, ref resizeHandleCoords);
}
if ((options & ScaleOptions.Centered) == ScaleOptions.Centered) {
// store center coordinates of rectangle
float rectCenterX = originalRectangle.Left + originalRectangle.Width / 2;
float rectCenterY = originalRectangle.Top + originalRectangle.Height / 2;
// scale rectangle using handle coordinates
Scale(ref originalRectangle, resizeHandlePosition, resizeHandleCoords);
// mirror handle coordinates via rectangle center coordinates
resizeHandleCoords.X -= 2 * (resizeHandleCoords.X - rectCenterX);
resizeHandleCoords.Y -= 2 * (resizeHandleCoords.Y - rectCenterY);
// scale again with opposing handle and mirrored coordinates
resizeHandlePosition = (Positions)((((int)resizeHandlePosition) + 4) % 8);
Scale(ref originalRectangle, resizeHandlePosition, resizeHandleCoords);
} else {
Scale(ref originalRectangle, resizeHandlePosition, resizeHandleCoords);
}
}
/// <summary>
/// Calculates target size of a given rectangle scaled by dragging one of its handles (corners)
/// </summary>
/// <param name="originalRectangle">bounds of the current rectangle, scaled values will be written to this reference</param>
/// <param name="resizeHandlePosition">position of the handle/gripper being used for resized, see constants in Gripper.cs, e.g. Gripper.POSITION_TOP_LEFT</param>
/// <param name="resizeHandleCoords">coordinates of the used handle/gripper</param>
private static void Scale(ref RectangleF originalRectangle, Positions resizeHandlePosition, PointF resizeHandleCoords) {
switch(resizeHandlePosition) {
case Positions.TopLeft:
originalRectangle.Width = originalRectangle.Left + originalRectangle.Width - resizeHandleCoords.X;
originalRectangle.Height = originalRectangle.Top + originalRectangle.Height - resizeHandleCoords.Y;
originalRectangle.X = resizeHandleCoords.X;
originalRectangle.Y = resizeHandleCoords.Y;
break;
case Positions.TopCenter:
originalRectangle.Height = originalRectangle.Top + originalRectangle.Height - resizeHandleCoords.Y;
originalRectangle.Y = resizeHandleCoords.Y;
break;
case Positions.TopRight:
originalRectangle.Width = resizeHandleCoords.X - originalRectangle.Left;
originalRectangle.Height = originalRectangle.Top + originalRectangle.Height - resizeHandleCoords.Y;
originalRectangle.Y = resizeHandleCoords.Y;
break;
case Positions.MiddleLeft:
originalRectangle.Width = originalRectangle.Left + originalRectangle.Width - resizeHandleCoords.X;
originalRectangle.X = resizeHandleCoords.X;
break;
case Positions.MiddleRight:
originalRectangle.Width = resizeHandleCoords.X - originalRectangle.Left;
break;
case Positions.BottomLeft:
originalRectangle.Width = originalRectangle.Left + originalRectangle.Width - resizeHandleCoords.X;
originalRectangle.Height = resizeHandleCoords.Y - originalRectangle.Top;
originalRectangle.X = resizeHandleCoords.X;
break;
case Positions.BottomCenter:
originalRectangle.Height = resizeHandleCoords.Y - originalRectangle.Top;
break;
case Positions.BottomRight:
originalRectangle.Width = resizeHandleCoords.X - originalRectangle.Left;
originalRectangle.Height = resizeHandleCoords.Y - originalRectangle.Top;
break;
default:
throw new ArgumentException("Position cannot be handled: "+resizeHandlePosition);
}
}
/// <summary>
/// Adjusts resizeHandleCoords so that aspect ratio is kept after resizing a given rectangle with provided arguments.
/// An adjustment can always be done in two ways, e.g. *in*crease width until fit or *de*crease height until fit.
/// To avoid objects growing near infinity unexpectedly in certain combinations, the adjustment will choose the
/// option resulting in the smaller rectangle.
/// </summary>
/// <param name="originalRectangle">bounds of the current rectangle</param>
/// <param name="resizeHandlePosition">position of the handle/gripper being used for resized, see Position</param>
/// <param name="resizeHandleCoords">coordinates of the used handle/gripper, adjusted coordinates will be written to this reference</param>
private static void AdjustCoordsForRationalScale(RectangleF originalRectangle, Positions resizeHandlePosition, ref PointF resizeHandleCoords) {
SizeF selectedRectangle, newSize;
switch(resizeHandlePosition) {
case Positions.TopLeft:
selectedRectangle = new SizeF(originalRectangle.Right - resizeHandleCoords.X, originalRectangle.Bottom - resizeHandleCoords.Y);
newSize = GetNewSizeForRationalScale(originalRectangle.Size, selectedRectangle);
resizeHandleCoords.X = originalRectangle.Right - newSize.Width;
resizeHandleCoords.Y = originalRectangle.Bottom - newSize.Height;
break;
case Positions.TopRight:
selectedRectangle = new SizeF(resizeHandleCoords.X - originalRectangle.Left, originalRectangle.Bottom - resizeHandleCoords.Y);
newSize = GetNewSizeForRationalScale(originalRectangle.Size, selectedRectangle);
resizeHandleCoords.X = originalRectangle.Left + newSize.Width;
resizeHandleCoords.Y = originalRectangle.Bottom - newSize.Height;
break;
case Positions.BottomLeft:
selectedRectangle = new SizeF(originalRectangle.Right - resizeHandleCoords.X, resizeHandleCoords.Y - originalRectangle.Top);
newSize = GetNewSizeForRationalScale(originalRectangle.Size, selectedRectangle);
resizeHandleCoords.X = originalRectangle.Right - newSize.Width;
resizeHandleCoords.Y = originalRectangle.Top + newSize.Height;
break;
case Positions.BottomRight:
selectedRectangle = new SizeF(resizeHandleCoords.X - originalRectangle.Left, resizeHandleCoords.Y - originalRectangle.Top);
newSize = GetNewSizeForRationalScale(originalRectangle.Size, selectedRectangle);
resizeHandleCoords.X = originalRectangle.Left + newSize.Width;
resizeHandleCoords.Y = originalRectangle.Top + newSize.Height;
break;
}
}
/// <summary>
/// For an original size, and a selected size, returns the the largest possible size that
/// * has the same aspect ratio as the original
/// * fits into selected size
/// </summary>
/// <param name="originalSize">size to be considered for keeping aspect ratio</param>
/// <param name="selectedSize">selection size (i.e. the size we'd produce if we wouldn't keep aspect ratio)</param>
/// <returns></returns>
private static SizeF GetNewSizeForRationalScale(SizeF originalSize, SizeF selectedSize)
{
SizeF newSize = selectedSize;
float originalRatio = originalSize.Width / originalSize.Height;
float selectedRatio = selectedSize.Width / selectedSize.Height;
// will fix orientation if the scaling causes size to be flipped in any direction
int flippedRatioSign = Math.Sign(selectedRatio) * Math.Sign(originalRatio);
if (Math.Abs(selectedRatio) > Math.Abs(originalRatio))
{
// scaled rectangle (ratio) would be wider than original
// keep height and tweak width to maintain aspect ratio
newSize.Width = selectedSize.Height * originalRatio * flippedRatioSign;
}
else if (Math.Abs(selectedRatio) < Math.Abs(originalRatio))
{
// scaled rectangle (ratio) would be taller than original
// keep width and tweak height to maintain aspect ratio
newSize.Height = selectedSize.Width / originalRatio * flippedRatioSign;
}
return newSize;
}
public static void Scale(Rectangle boundsBeforeResize, int cursorX, int cursorY, ref RectangleF boundsAfterResize) {
Scale(boundsBeforeResize, cursorX, cursorY, ref boundsAfterResize, null);
}
public static void Scale(Rectangle boundsBeforeResize, int cursorX, int cursorY, ref RectangleF boundsAfterResize, IDoubleProcessor angleRoundBehavior) {
Scale(boundsBeforeResize, Positions.TopLeft, cursorX, cursorY, ref boundsAfterResize, angleRoundBehavior);
}
public static void Scale(Rectangle boundsBeforeResize, Positions gripperPosition, int cursorX, int cursorY, ref RectangleF boundsAfterResize, IDoubleProcessor angleRoundBehavior) {
ScaleOptions opts = GetScaleOptions();
bool rationalScale = (opts & ScaleOptions.Rational) == ScaleOptions.Rational;
bool centeredScale = (opts & ScaleOptions.Centered) == ScaleOptions.Centered;
if(rationalScale) {
double angle = GeometryHelper.Angle2D(boundsBeforeResize.X, boundsBeforeResize.Y, cursorX, cursorY);
if(angleRoundBehavior != null) {
angle = angleRoundBehavior.Process(angle);
}
int dist = GeometryHelper.Distance2D(boundsBeforeResize.X, boundsBeforeResize.Y, cursorX, cursorY);
boundsAfterResize.Width = (int)Math.Round(dist * Math.Cos(angle / 180 * Math.PI));
boundsAfterResize.Height = (int)Math.Round(dist * Math.Sin(angle / 180 * Math.PI));
}
if(centeredScale) {
float wdiff = boundsAfterResize.Width - boundsBeforeResize.Width;
float hdiff = boundsAfterResize.Height - boundsBeforeResize.Height;
boundsAfterResize.Width += wdiff;
boundsAfterResize.Height += hdiff;
boundsAfterResize.X -= wdiff;
boundsAfterResize.Y -= hdiff;
}
}
/// <returns>the current ScaleOptions depending on modifier keys held down</returns>
public static ScaleOptions GetScaleOptions() {
bool anchorAtCenter = (Control.ModifierKeys & Keys.Control) != 0;
bool maintainAspectRatio = (Control.ModifierKeys & Keys.Shift) != 0;
ScaleOptions opts = ScaleOptions.Default;
if(anchorAtCenter) opts |= ScaleOptions.Centered;
if(maintainAspectRatio) opts |= ScaleOptions.Rational;
return opts;
}
public interface IDoubleProcessor {
double Process(double d);
}
public class ShapeAngleRoundBehavior : IDoubleProcessor {
public static ShapeAngleRoundBehavior Instance = new();
private ShapeAngleRoundBehavior() {}
public double Process(double angle) {
return Math.Round((angle+45)/90)*90 - 45;
}
}
public class LineAngleRoundBehavior : IDoubleProcessor {
public static LineAngleRoundBehavior Instance = new();
private LineAngleRoundBehavior() {}
public double Process(double angle) {
return Math.Round(angle/15)*15;
}
}
}
}

View file

@ -1,95 +0,0 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
using System;
using System.Reflection;
using System.Resources;
using System.Runtime.InteropServices;
using GreenshotPlugin.UnmanagedHelpers;
using GreenshotPlugin.Core;
using System.IO;
using GreenshotPlugin.IniFile;
using GreenshotPlugin.UnmanagedHelpers.Enums;
using log4net;
namespace Greenshot.Helpers {
/// <summary>
/// Create to fix the sometimes wrongly played sample, especially after first start from IDE
/// See: http://www.codeproject.com/KB/audio-video/soundplayerbug.aspx?msg=2487569
/// </summary>
public static class SoundHelper {
private static readonly ILog Log = LogManager.GetLogger(typeof(SoundHelper));
private static readonly CoreConfiguration CoreConfig = IniConfig.GetIniSection<CoreConfiguration>();
private static GCHandle? _gcHandle;
private static byte[] _soundBuffer;
public static void Initialize() {
if (_gcHandle == null) {
try {
ResourceManager resources = new ResourceManager("Greenshot.Sounds", Assembly.GetExecutingAssembly());
_soundBuffer = (byte[])resources.GetObject("camera");
if (CoreConfig.NotificationSound != null && CoreConfig.NotificationSound.EndsWith(".wav")) {
try {
if (File.Exists(CoreConfig.NotificationSound)) {
_soundBuffer = File.ReadAllBytes(CoreConfig.NotificationSound);
}
} catch (Exception ex) {
Log.WarnFormat("couldn't load {0}: {1}", CoreConfig.NotificationSound, ex.Message);
}
}
// Pin sound so it can't be moved by the Garbage Collector, this was the cause for the bad sound
_gcHandle = GCHandle.Alloc(_soundBuffer, GCHandleType.Pinned);
} catch (Exception e) {
Log.Error("Error initializing.", e);
}
}
}
public static void Play() {
if (_soundBuffer != null) {
//Thread playSoundThread = new Thread(delegate() {
SoundFlags flags = SoundFlags.SND_ASYNC | SoundFlags.SND_MEMORY | SoundFlags.SND_NOWAIT | SoundFlags.SND_NOSTOP;
try {
if (_gcHandle != null) WinMM.PlaySound(_gcHandle.Value.AddrOfPinnedObject(), (UIntPtr)0, (uint)flags);
} catch (Exception e) {
Log.Error("Error in play.", e);
}
//});
//playSoundThread.Name = "Play camera sound";
//playSoundThread.IsBackground = true;
//playSoundThread.Start();
}
}
public static void Deinitialize() {
try {
if (_gcHandle != null) {
WinMM.PlaySound(null, (UIntPtr)0, 0);
_gcHandle.Value.Free();
_gcHandle = null;
}
} catch (Exception e) {
Log.Error("Error in deinitialize.", e);
}
}
}
}

View file

@ -1,257 +0,0 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
using System;
using System.Windows.Forms;
using log4net;
using Microsoft.Win32;
using System.IO;
namespace Greenshot.Helpers {
/// <summary>
/// A helper class for the startup registry
/// </summary>
public static class StartupHelper {
private static readonly ILog Log = LogManager.GetLogger(typeof(StartupHelper));
private const string RunKey6432 = @"Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Run";
private const string RunKey = @"Software\Microsoft\Windows\CurrentVersion\Run";
private const string ApplicationName = "Greenshot";
private static string GetExecutablePath() {
return "\"" + Application.ExecutablePath + "\"";
}
/// <summary>
/// Return true if the current user can write the RUN key of the local machine.
/// </summary>
/// <returns>true if Greenshot can write key</returns>
public static bool CanWriteRunAll() {
try {
using (Registry.LocalMachine.OpenSubKey(RunKey, true))
{
}
} catch {
return false;
}
return true;
}
/// <summary>
/// Return true if the current user can write the RUN key of the current user.
/// </summary>
/// <returns>true if Greenshot can write key</returns>
public static bool CanWriteRunUser() {
try {
using (Registry.CurrentUser.OpenSubKey(RunKey, true))
{
}
} catch {
return false;
}
return true;
}
/// <summary>
/// Return the RUN key value of the local machine
/// </summary>
/// <returns>the RUN key value of the local machine</returns>
public static object GetRunAllValue()
{
using (var key = Registry.LocalMachine.OpenSubKey(RunKey, false))
{
object runValue = key?.GetValue(ApplicationName);
if (runValue != null)
{
return runValue;
}
}
// for 64-bit systems we need to check the 32-bit keys too
if (IntPtr.Size != 8)
{
return null;
}
using (var key = Registry.LocalMachine.OpenSubKey(RunKey6432, false))
{
object runValue = key?.GetValue(ApplicationName);
if (runValue != null)
{
return runValue;
}
}
return null;
}
/// <summary>
/// Return the RUN key value of the current user
/// </summary>
/// <returns>the RUN key value of the current user</returns>
public static object GetRunUserValue() {
using (var key = Registry.CurrentUser.OpenSubKey(RunKey, false)) {
object runValue = key?.GetValue(ApplicationName);
if (runValue != null) {
return runValue;
}
}
// for 64-bit systems we need to check the 32-bit keys too
if (IntPtr.Size != 8)
{
return null;
}
using (var key = Registry.CurrentUser.OpenSubKey(RunKey6432, false)) {
object runValue = key?.GetValue(ApplicationName);
if (runValue != null) {
return runValue;
}
}
return null;
}
/// <summary>
/// Return true if the local machine has a RUN entry for Greenshot
/// </summary>
/// <returns>true if there is a run key</returns>
public static bool HasRunAll() {
try {
return GetRunAllValue() != null;
} catch (Exception e) {
Log.Error("Error retrieving RunAllValue", e);
}
return false;
}
/// <summary>
/// Return true if the current user has a RUN entry for Greenshot
/// </summary>
/// <returns>true if there is a run key</returns>
public static bool HasRunUser() {
object runValue = null;
try {
runValue = GetRunUserValue();
} catch (Exception e) {
Log.Error("Error retrieving RunUserValue", e);
}
return runValue != null;
}
/// <summary>
/// Delete the RUN key for the localmachine ("ALL")
/// </summary>
public static void DeleteRunAll() {
if (!HasRunAll())
{
return;
}
try
{
using var key = Registry.LocalMachine.OpenSubKey(RunKey, true);
key?.DeleteValue(ApplicationName);
} catch (Exception e) {
Log.Error("Error in deleteRunAll.", e);
}
try
{
// for 64-bit systems we need to delete the 32-bit keys too
if (IntPtr.Size != 8)
{
return;
}
using var key = Registry.LocalMachine.OpenSubKey(RunKey6432, false);
key?.DeleteValue(ApplicationName);
} catch (Exception e) {
Log.Error("Error in deleteRunAll.", e);
}
}
/// <summary>
/// Delete the RUN key for the current user
/// </summary>
public static void DeleteRunUser() {
if (!HasRunUser())
{
return;
}
try
{
using var key = Registry.CurrentUser.OpenSubKey(RunKey, true);
key?.DeleteValue(ApplicationName);
} catch (Exception e) {
Log.Error("Error in deleteRunUser.", e);
}
try
{
// for 64-bit systems we need to delete the 32-bit keys too
if (IntPtr.Size != 8)
{
return;
}
using var key = Registry.CurrentUser.OpenSubKey(RunKey6432, false);
key?.DeleteValue(ApplicationName);
} catch (Exception e) {
Log.Error("Error in deleteRunUser.", e);
}
}
/// <summary>
/// Set the RUN key for the current user
/// </summary>
public static void SetRunUser() {
try
{
using var key = Registry.CurrentUser.OpenSubKey(RunKey, true);
key?.SetValue(ApplicationName, GetExecutablePath());
} catch (Exception e) {
Log.Error("Error in setRunUser.", e);
}
}
/// <summary>
/// Test if there is a link in the Startup folder
/// </summary>
/// <returns>bool</returns>
public static bool IsInStartupFolder() {
try {
string lnkName = Path.GetFileNameWithoutExtension(Application.ExecutablePath) + ".lnk";
string startupPath = Environment.GetFolderPath(Environment.SpecialFolder.Startup);
if (Directory.Exists(startupPath)) {
Log.DebugFormat("Startup path: {0}", startupPath);
if (File.Exists(Path.Combine(startupPath, lnkName))) {
return true;
}
}
string startupAll = Environment.GetEnvironmentVariable("ALLUSERSPROFILE") + @"\Microsoft\Windows\Start Menu\Programs\Startup";
if (Directory.Exists(startupAll)) {
Log.DebugFormat("Startup all path: {0}", startupAll);
if (File.Exists(Path.Combine(startupAll, lnkName))) {
return true;
}
}
}
catch
{
// ignored
}
return false;
}
}
}

View file

@ -1,90 +0,0 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
using System;
using System.Windows.Forms;
namespace Greenshot.Helpers {
/// <summary>
/// Enables or disables toolstrip items, taking care of the hierarchy.
/// (parent) OwnerItems are ENabled with ToolStripItems,
/// (child) DropDownItems are ENabled and DISabled with ToolStripItems.
/// </summary>
public static class ToolStripItemEndisabler {
[Flags]
private enum PropagationMode {NONE=0, CHILDREN=1, ANCESTORS=2};
/// <summary>
/// Enables all of a ToolStrip's children (recursively),
/// but not the ToolStrip itself
/// </summary>
public static void Enable(ToolStrip ts) {
Endisable(ts, true, PropagationMode.CHILDREN);
}
/// <summary>
/// Disables all of a ToolStrip's children (recursively),
/// but not the ToolStrip itself
/// </summary>
public static void Disable(ToolStrip ts) {
Endisable(ts, false, PropagationMode.CHILDREN);
}
/// <summary>
/// Enables the ToolStripItem, including children (ToolStripDropDownItem)
/// and ancestor (OwnerItem)
/// </summary>
public static void Enable(ToolStripItem tsi) {
Endisable(tsi, true, PropagationMode.CHILDREN | PropagationMode.ANCESTORS);
}
private static void Endisable(ToolStrip ts, bool enable, PropagationMode mode)
{
if ((mode & PropagationMode.CHILDREN) != PropagationMode.CHILDREN) return;
foreach(ToolStripItem tsi in ts.Items) {
Endisable(tsi, enable, PropagationMode.CHILDREN);
}
}
private static void Endisable(ToolStripItem tsi, bool enable, PropagationMode mode){
if (tsi is ToolStripDropDownItem item) {
Endisable(item, enable, mode);
} else {
tsi.Enabled = enable;
}
if ((mode & PropagationMode.ANCESTORS) != PropagationMode.ANCESTORS) return;
if (tsi.OwnerItem != null) Endisable(tsi.OwnerItem, enable, PropagationMode.ANCESTORS);
}
private static void Endisable(ToolStripDropDownItem tsddi, bool enable, PropagationMode mode) {
if((mode & PropagationMode.CHILDREN) == PropagationMode.CHILDREN) {
foreach(ToolStripItem tsi in tsddi.DropDownItems) {
Endisable(tsi, enable, PropagationMode.CHILDREN);
}
}
tsddi.Enabled = enable;
}
}
}

View file

@ -1,67 +0,0 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
using System;
using Greenshot.Drawing;
using GreenshotPlugin.Interfaces.Drawing;
namespace Greenshot.Memento {
/// <summary>
/// The AddElementMemento makes it possible to undo adding an element
/// </summary>
public class AddElementMemento : IMemento {
private IDrawableContainer _drawableContainer;
private Surface _surface;
public AddElementMemento(Surface surface, IDrawableContainer drawableContainer) {
_surface = surface;
_drawableContainer = drawableContainer;
}
public void Dispose() {
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing) {
//if (disposing) { }
_drawableContainer = null;
_surface = null;
}
public bool Merge(IMemento otherMemento) {
return false;
}
public IMemento Restore() {
// Before
_drawableContainer.Invalidate();
// Store the selected state, as it's overwritten by the RemoveElement
DeleteElementMemento oldState = new DeleteElementMemento(_surface, _drawableContainer);
_surface.RemoveElement(_drawableContainer, false);
_drawableContainer.Selected = true;
// After
_drawableContainer.Invalidate();
return oldState;
}
}
}

View file

@ -1,71 +0,0 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
using Greenshot.Drawing;
using GreenshotPlugin.Interfaces.Drawing;
namespace Greenshot.Memento
{
/// <summary>
/// The AddElementMemento makes it possible to undo adding an element
/// </summary>
public class AddElementsMemento : IMemento
{
private IDrawableContainerList _containerList;
private Surface _surface;
public AddElementsMemento(Surface surface, IDrawableContainerList containerList)
{
_surface = surface;
_containerList = containerList;
}
public void Dispose()
{
Dispose(true);
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
_containerList?.Dispose();
}
_containerList = null;
_surface = null;
}
public bool Merge(IMemento otherMemento)
{
return false;
}
public IMemento Restore()
{
var oldState = new DeleteElementsMemento(_surface, _containerList);
_surface.RemoveElements(_containerList, false);
// After, so everything is gone
_surface.Invalidate();
return oldState;
}
}
}

View file

@ -1,76 +0,0 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
using GreenshotPlugin.Interfaces.Drawing;
namespace Greenshot.Memento
{
/// <summary>
/// The ChangeFieldHolderMemento makes it possible to undo-redo an IDrawableContainer move
/// </summary>
public class ChangeFieldHolderMemento : IMemento
{
private IDrawableContainer _drawableContainer;
private readonly IField _fieldToBeChanged;
private readonly object _oldValue;
public ChangeFieldHolderMemento(IDrawableContainer drawableContainer, IField fieldToBeChanged)
{
_drawableContainer = drawableContainer;
_fieldToBeChanged = fieldToBeChanged;
_oldValue = fieldToBeChanged.Value;
}
public void Dispose()
{
Dispose(true);
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
_drawableContainer?.Dispose();
}
_drawableContainer = null;
}
public bool Merge(IMemento otherMemento)
{
if (otherMemento is not ChangeFieldHolderMemento other) return false;
if (!other._drawableContainer.Equals(_drawableContainer)) return false;
return other._fieldToBeChanged.Equals(_fieldToBeChanged);
}
public IMemento Restore()
{
// Before
_drawableContainer.Invalidate();
ChangeFieldHolderMemento oldState = new ChangeFieldHolderMemento(_drawableContainer, _fieldToBeChanged);
_fieldToBeChanged.Value = _oldValue;
// After
_drawableContainer.Invalidate();
return oldState;
}
}
}

View file

@ -1,73 +0,0 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
using System;
using Greenshot.Drawing;
using GreenshotPlugin.Interfaces.Drawing;
namespace Greenshot.Memento {
/// <summary>
/// The DeleteElementMemento makes it possible to undo deleting an element
/// </summary>
public class DeleteElementMemento : IMemento {
private IDrawableContainer _drawableContainer;
private readonly Surface _surface;
public DeleteElementMemento(Surface surface, IDrawableContainer drawableContainer) {
_surface = surface;
_drawableContainer = drawableContainer;
}
public void Dispose() {
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (!disposing) return;
if (_drawableContainer != null) {
_drawableContainer.Dispose();
_drawableContainer = null;
}
}
public bool Merge(IMemento otherMemento) {
return false;
}
public IMemento Restore() {
// Before
_drawableContainer.Invalidate();
var oldState = new AddElementMemento(_surface, _drawableContainer);
_surface.AddElement(_drawableContainer, false);
// The container has a selected flag which represents the state at the moment it was deleted.
if (_drawableContainer.Selected) {
_surface.SelectElement(_drawableContainer);
}
// After
_drawableContainer.Invalidate();
return oldState;
}
}
}

View file

@ -1,69 +0,0 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
using Greenshot.Drawing;
using GreenshotPlugin.Interfaces.Drawing;
namespace Greenshot.Memento
{
/// <summary>
/// The DeleteElementMemento makes it possible to undo deleting an element
/// </summary>
public class DeleteElementsMemento : IMemento
{
private IDrawableContainerList _containerList;
private Surface _surface;
public DeleteElementsMemento(Surface surface, IDrawableContainerList containerList)
{
_surface = surface;
_containerList = containerList;
}
public void Dispose()
{
Dispose(true);
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
_containerList?.Dispose();
}
_containerList = null;
_surface = null;
}
public bool Merge(IMemento otherMemento)
{
return false;
}
public IMemento Restore()
{
var oldState = new AddElementsMemento(_surface, _containerList);
_surface.AddElements(_containerList, false);
// After
_surface.Invalidate();
return oldState;
}
}
}

View file

@ -1,108 +0,0 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
using Greenshot.Drawing;
using GreenshotPlugin.Core;
using System.Collections.Generic;
using System.Drawing;
using GreenshotPlugin.Interfaces.Drawing;
namespace Greenshot.Memento
{
/// <summary>
/// The DrawableContainerBoundsChangeMemento makes it possible to undo-redo an IDrawableContainer resize & move
/// </summary>
public class DrawableContainerBoundsChangeMemento : IMemento
{
private readonly List<Point> _points = new();
private readonly List<Size> _sizes = new();
private IDrawableContainerList _listOfdrawableContainer;
private void StoreBounds()
{
foreach (IDrawableContainer drawableContainer in _listOfdrawableContainer)
{
_points.Add(drawableContainer.Location);
_sizes.Add(drawableContainer.Size);
}
}
public DrawableContainerBoundsChangeMemento(IDrawableContainerList listOfdrawableContainer)
{
_listOfdrawableContainer = listOfdrawableContainer;
StoreBounds();
}
public DrawableContainerBoundsChangeMemento(IDrawableContainer drawableContainer)
{
_listOfdrawableContainer = new DrawableContainerList
{
drawableContainer
};
_listOfdrawableContainer.Parent = drawableContainer.Parent;
StoreBounds();
}
public void Dispose()
{
Dispose(true);
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
_listOfdrawableContainer?.Dispose();
}
_listOfdrawableContainer = null;
}
public bool Merge(IMemento otherMemento)
{
if (otherMemento is not DrawableContainerBoundsChangeMemento other) return false;
if (ObjectExtensions.CompareLists(_listOfdrawableContainer, other._listOfdrawableContainer))
{
// Lists are equal, as we have the state already we can ignore the new memento
return true;
}
return false;
}
public IMemento Restore()
{
var oldState = new DrawableContainerBoundsChangeMemento(_listOfdrawableContainer);
for (int index = 0; index < _listOfdrawableContainer.Count; index++)
{
IDrawableContainer drawableContainer = _listOfdrawableContainer[index];
// Before
drawableContainer.Invalidate();
drawableContainer.Left = _points[index].X;
drawableContainer.Top = _points[index].Y;
drawableContainer.Width = _sizes[index].Width;
drawableContainer.Height = _sizes[index].Height;
// After
drawableContainer.Invalidate();
drawableContainer.Parent.Modified = true;
}
return oldState;
}
}
}

View file

@ -1,74 +0,0 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
using Greenshot.Drawing;
using System.Drawing;
using System.Drawing.Drawing2D;
using GreenshotPlugin.Interfaces.Drawing;
namespace Greenshot.Memento {
/// <summary>
/// The SurfaceCropMemento makes it possible to undo-redo an surface crop
/// </summary>
public class SurfaceBackgroundChangeMemento : IMemento {
private Image _image;
private Surface _surface;
private Matrix _matrix;
public SurfaceBackgroundChangeMemento(Surface surface, Matrix matrix) {
_surface = surface;
_image = surface.Image;
_matrix = matrix.Clone();
// Make sure the reverse is applied
_matrix.Invert();
}
public void Dispose() {
Dispose(true);
}
protected virtual void Dispose(bool disposing)
{
if (!disposing) return;
if (_matrix != null) {
_matrix.Dispose();
_matrix = null;
}
if (_image != null) {
_image.Dispose();
_image = null;
}
_surface = null;
}
public bool Merge(IMemento otherMemento) {
return false;
}
public IMemento Restore() {
SurfaceBackgroundChangeMemento oldState = new SurfaceBackgroundChangeMemento(_surface, _matrix);
_surface.UndoBackgroundChange(_image, _matrix);
_surface.Invalidate();
return oldState;
}
}
}

View file

@ -1,64 +0,0 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
using Greenshot.Drawing;
using GreenshotPlugin.Interfaces.Drawing;
namespace Greenshot.Memento {
/// <summary>
/// The TextChangeMemento makes it possible to undo-redo an IDrawableContainer move
/// </summary>
public class TextChangeMemento : IMemento {
private TextContainer _textContainer;
private readonly string _oldText;
public TextChangeMemento(TextContainer textContainer) {
_textContainer = textContainer;
_oldText = textContainer.Text;
}
public void Dispose() {
Dispose(true);
}
protected virtual void Dispose(bool disposing) {
if (disposing) {
_textContainer = null;
}
}
public bool Merge(IMemento otherMemento) {
if (otherMemento is not TextChangeMemento other) return false;
return other._textContainer.Equals(_textContainer);
}
public IMemento Restore() {
// Before
_textContainer.Invalidate();
TextChangeMemento oldState = new TextChangeMemento(_textContainer);
_textContainer.ChangeText(_oldText, false);
// After
_textContainer.Invalidate();
return oldState;
}
}
}

View file

@ -1,106 +0,0 @@
<html>
<head>
<title>Greenshot Hilfe</title>
<style>
h1, h2, h3, p, li {
font-family: Tahoma, Arial, Sans-serif;
}
h1 {font-size:16px;}
h2 {font-size:14px;color:#009900;}
h3 {font-size:12px;}
p, li {font-size:12px;}
a:link, a:hover, a:visited, a:active {color:#009900;}
</style>
</head>
<body>
<a href="http://sourceforge.net"><img src="http://sflogo.sourceforge.net/sflogo.php?group_id=191585&amp;type=2" width="125" height="37" border="0" alt="SourceForge.net Logo" /></a>
<h1>Greenshot Hilfe</h1>
<h2>Modi</h2>
<h3>Bereichsmodus</h3>
<p>
Aktivieren Sie den Bereichsmodus, indem Sie die <em>Druck</em>-Taste auf
Ihrer Tastatur bet&auml;tigen oder <em>Bereich abfotografieren</em> aus dem
Kontextmen&uuml; w&auml;hlen.<br>
Dr&uuml;cken und halten Sie die linke Maustaste gedr&uuml;ckt, um einen rechteckigen
Bereich zu definieren, der abfotografiert werden soll.<br>
Nach dem Loslassen der Maustaste &ouml;ffnet sich das Bildbearbeitungsfenster
zur weiteren Bearbeitung Ihres Screenshots.<br><br>
Um den Bereich sp&auml;ter noch einmal abzufotografieren, w&auml;hlen Sie <em>Zuletzt
gew&auml;hlten Bereich abfotografieren</em>.
</p>
<h3>Fenstermodus</h3>
<p>
Aktivieren Sie den FensterModus, indem Sie <em>Alt + Druck</em> auf
Ihrer Tastatur bet&auml;tigen oder <em>Fenster abfotografieren</em> aus dem
Kontextmen&uuml; w&auml;hlen.<br>
Klicken Sie auf das Fenster, dass abfotografiern werden soll.<br>
Nachdem Sie geklickt haben &ouml;ffnet sich das Bildbearbeitungsfenster
zur weiteren Bearbeitung Ihres Screenshots.
</p>
<h3>Bildschirmmodus</h3>
<p>
Wenn Sie den gesamten Bildschirm abfotografieren wollen, dr&uuml;cken Sie einfach
<em>Ctrl + Print</em> auf Ihrer Tastatur oder w&auml;hlen Sie <em>Kompletten
Bildschirm abfotografieren</em>.<br>
Der komplette Bildschirm wird sofort abfotografiert, das Bildbearbeitungsfenster
&ouml;ffnet sich zur weiteren Bearbeitung Ihres Screenshots.
</p>
<a name="imageeditor"><h2>Bildbearbeitungsfenster</h2></a>
<p>
Wenn Sie das Bildbearbeitungsfenster nicht nutzen wollen können Sie im
Kontextmenü oder im Einstellungsdialog festlegen, dass es nicht angezeigt
werden soll. In diesem Fall wird der Screenshot sofort in eine Datei
gespeichert. Speicherort, Dateiname und Bildformat sind dann abhängig von
den bevorzugten Ausgabedatei-Einstellungen im Einstellungsdialog.
</p>
<h3>Datei-Men&uuml;</h3>
<ul>
<li><em>Speichern</em>: speichert die Grafik unter dem Pfad der beim letzten <em>Speichern unter...</em> Dialog gew&auml;hlt wurde</li>
<li><em>Speichern unter...</em>: &ouml;ffnet einen Dialog zur Auswahl des Pfads und Dateinamens unter dem die Grafik gespeichert werden soll</li>
<li><em>Grafik in die Zwischenablage kopieren</em>: kopiert die Grafik in die Zwischenablage, so dass sie in anderer Software verwendet werden kann</li>
</ul>
<h3>Bearbeiten-Men&uuml;</h3>
<ul>
<li><em>Gew&auml;hltes Element in die Zwischenablage ausschneiden</em>: entfernt das ausgew&auml;hltes Element und kopiert es in die Zwischenablage, so dass es in ein anderes Bildbearbeitungsfenster eingef&uuml;gt werden kann</li>
<li><em>Gew&auml;hltes Element in die Zwischenablage kopieren</em>: kopiert das ausgew&auml;hltes Element in die Zwischenablage, so dass es in ein anderes Bildbearbeitungsfenster eingef&uuml;gt werden kann</li>
<li><em>Element aus der Zwischenablage einf&uuml;gen</em>: f&uuml;gt ein vorher ausgeschnittenes/kopiertes Element in das Bildbearbeitungsfenster ein</li>
<li><em>Gew&auml;hltes Element duplizieren</em>: dupliziert das gew&auml;hlte Element</li>
</ul>
<h3>Objekt-Men&uuml;</h3>
<ul>
<li><em>Rechteck hinzuf&uuml;gen</em>: f&uuml;gt ein Rechteck zur Grafik hinzu</li>
<li><em>Ellipse hinzuf&uuml;gen</em>: f&uuml;gt eine Ellipse zur Grafik hinzu</li>
<li><em>Textfeld hinzuf&uuml;gen</em>: f&uuml;gt ein Textfeld zur Grafik hinzu</li>
<li><em>Gew&auml;hltes Element l&ouml;schen</em>: entfernt das gew&auml;hlte Element aus der Grafik</li>
</ul>
<p>
Klicken Sie ein Element an um es auszuw&auml;hlen. Anschlie&szlig;end k&ouml;nnen Sie die Gr&ouml;&szlig;e oder
Position ver&auml;ndern, oder es kopieren, ausschneiden oder entfernen. Die Gr&ouml;&szlig;e
eines Elements kann durch Klicken und Ziehen der Anfasser (kleine schwarze
Quadrate) an der linken oberen oder der rechten unteren Ecke ge&auml;ndert werden.
</p>
</body>
</html>

View file

@ -1,100 +0,0 @@
<html>
<head>
<title>Greenshot Help</title>
<style>
h1, h2, h3, p, li {
font-family: Tahoma, Arial, Sans-serif;
}
h1 {font-size:16px;}
h2 {font-size:14px;color:#009900;}
h3 {font-size:12px;}
p, li {font-size:12px;}
a:link, a:hover, a:visited, a:active {color:#009900;}
</style>
</head>
<body>
<a href="http://sourceforge.net"><img src="http://sflogo.sourceforge.net/sflogo.php?group_id=191585&amp;type=2" width="125" height="37" border="0" alt="SourceForge.net Logo" /></a>
<h1>Greenshot Help</h1>
<h2>Modes</h2>
<h3>Region mode</h3>
<p>
Activate region mode by hitting the <em>Print</em> key on your keyboard
or by choosing <em>Capture region</em> from the context menu.<br>
Left-click and drag to define a rectangular area you want to be shot.<br>
After releasing the mouse button, the image editor window will open for
further editing of your screenshot.<br><br>
For shooting the region again later, choose <em>Capture last region</em> from
the context menu.
</p>
<h3>Window mode</h3>
<p>
Activate window mode by hitting <em>Alt + Print</em> on your keyboard
or by choosing <em>Capture window</em> from the context menu.<br>
Click the window you want to be shot.<br>
After clicking, the image editor window will open for
further editing of your screenshot.
</p>
<h3>Fullscreen mode</h3>
<p>
If you want to shoot the complete screen, just press <em>Ctrl + Print</em> on
your keyboard or choose <em>Capture full screen</em> from the context menu.<br>
The complete screen will be shot instantly, the image editor window will open for
further editing of your screenshot.
</p>
<a name="imageeditor"><h2>Image Editor</h2></a>
<p>
If you do not want to use the image editor window you can choose to skip
in in the context menu or in the settings dialog. The screenshot will be
saved directly to a file then. Storage location, filename and image format
are defined by your preferred output file settings in the settings dialog.
</p>
<h3>File menu</h3>
<ul>
<li><em>Save</em>: saves the image to the file specified in the last <em>Save as...</em> dialog</li>
<li><em>Save as...</em>: lets you choose a path and filename to save the image to</li>
<li><em>Copy image to clipboard</em>: copies the image to the clipboard, for pasting it to other software</li>
</ul>
<h3>Edit menu</h3>
<ul>
<li><em>Cut selected element to clipboard</em>: removes the selected element and copies it to the clipboard, so that it can be pasted into another image editor window</li>
<li><em>Copy selected element to clipboard</em>: copies it to the clipboard, so that it can be pasted into another image editor window</li>
<li><em>Paste element from clipboard</em>: pastes a previously cut/copied element into the image editor window</li>
<li><em>Duplicate selected element</em>: duplicates the selected element</li>
</ul>
<h3>Object menu</h3>
<ul>
<li><em>Add rectangle</em>: adds a rectangle to the image</li>
<li><em>Add ellipse</em>: adds an ellipse to the image</li>
<li><em>Add textbox</em>: adds a textbox to the image</li>
<li><em>Delete selected element</em>: removes the selected element from the image</li>
</ul>
<p>
Click an element to select it for resizing, moving, copying, cutting, or removal. The size of an
element can be defined by dragging the grippers (small black squares) at the top-left and the
bottom-right corner of the selected element.
</p>
</body>
</html>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

Some files were not shown because too many files have changed in this diff Show more