diff --git a/Greenshot/Drawing/Adorners/AbstractAdorner.cs b/Greenshot/Drawing/Adorners/AbstractAdorner.cs new file mode 100644 index 000000000..685ec779f --- /dev/null +++ b/Greenshot/Drawing/Adorners/AbstractAdorner.cs @@ -0,0 +1,147 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2015 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 . + */ + +using Greenshot.Plugin.Drawing; +using Greenshot.Plugin.Drawing.Adorners; +using System.Drawing; +using System.Drawing.Drawing2D; +using System.Windows.Forms; +using System; + +namespace Greenshot.Drawing.Adorners +{ + public class AbstractAdorner : IAdorner + { + public virtual EditStatus EditStatus { get; protected set; } = EditStatus.IDLE; + + protected Size _size = new Size(4, 4); + + public AbstractAdorner(IDrawableContainer owner) + { + Owner = owner; + } + + /// + /// Returns the cursor for when the mouse is over the adorner + /// + public virtual Cursor Cursor + { + get + { + return Cursors.SizeAll; + } + } + + public virtual IDrawableContainer Owner + { + get; + set; + } + + /// + /// Test if the point is inside the adorner + /// + /// + /// + public virtual bool HitTest(Point point) + { + Rectangle hitBounds = Bounds; + hitBounds.Inflate(3, 3); + return hitBounds.Contains(point); + } + + /// + /// Handle the mouse down + /// + /// + /// + public virtual void MouseDown(object sender, MouseEventArgs mouseEventArgs) + { + } + + /// + /// Handle the mouse move + /// + /// + /// + public virtual void MouseMove(object sender, MouseEventArgs mouseEventArgs) + { + } + + /// + /// Handle the mouse up + /// + /// + /// + public virtual void MouseUp(object sender, MouseEventArgs mouseEventArgs) + { + EditStatus = EditStatus.IDLE; + } + + /// + /// Return the location of the adorner + /// + public virtual Point Location + { + get; + set; + } + + /// + /// Return the bounds of the Adorner + /// + public virtual Rectangle Bounds + { + get + { + Point location = Location; + return new Rectangle(location.X - (_size.Width / 2), location.Y - (_size.Height / 2), _size.Width, _size.Height); + } + } + + /// + /// The adorner is active if the edit status is not idle or undrawn + /// + public virtual bool IsActive + { + get + { + return EditStatus != EditStatus.IDLE && EditStatus != EditStatus.UNDRAWN; + } + } + + /// + /// Draw the adorner + /// + /// PaintEventArgs + public virtual void Paint(PaintEventArgs paintEventArgs) + { + } + + /// + /// We ignore the Transform, as the coordinates are directly bound to those of the owner + /// + /// + public virtual void Transform(Matrix matrix) + { + } + } +} diff --git a/Greenshot/Drawing/Adorners/MoveAdorner.cs b/Greenshot/Drawing/Adorners/MoveAdorner.cs new file mode 100644 index 000000000..78e00e2dd --- /dev/null +++ b/Greenshot/Drawing/Adorners/MoveAdorner.cs @@ -0,0 +1,165 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2015 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 . + */ + +using Greenshot.Helpers; +using Greenshot.Plugin.Drawing; +using Greenshot.Plugin.Drawing.Adorners; +using System.Drawing; +using System.Drawing.Drawing2D; +using System.Windows.Forms; + +namespace Greenshot.Drawing.Adorners +{ + /// + /// This is the adorner for the line based containers + /// + public class MoveAdorner : AbstractAdorner + { + private Rectangle _boundsBeforeResize = Rectangle.Empty; + private RectangleF _boundsAfterResize = RectangleF.Empty; + + public Positions Position { get; private set; } + + public MoveAdorner(IDrawableContainer owner, Positions position) : base(owner) + { + Position = position; + } + + /// + /// Returns the cursor for when the mouse is over the adorner + /// + public override Cursor Cursor + { + get + { + return Cursors.SizeAll; + } + } + + /// + /// Handle the mouse down + /// + /// + /// + public override void MouseDown(object sender, MouseEventArgs mouseEventArgs) + { + EditStatus = EditStatus.RESIZING; + _boundsBeforeResize = new Rectangle(Owner.Left, Owner.Top, Owner.Width, Owner.Height); + _boundsAfterResize = _boundsBeforeResize; + } + + /// + /// Handle the mouse move + /// + /// + /// + 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(); + } + + /// + /// Return the location of the adorner + /// + public override Point Location { + get + { + int x = 0,y = 0; + switch (Position) + { + case Positions.TopLeft: + x = Owner.Left; + y = Owner.Top; + break; + case Positions.BottomLeft: + x = Owner.Left; + y = Owner.Top + Owner.Height; + break; + case Positions.MiddleLeft: + x = Owner.Left; + y = Owner.Top + (Owner.Height / 2); + break; + case Positions.TopCenter: + x = Owner.Left + (Owner.Width / 2); + y = Owner.Top; + break; + case Positions.BottomCenter: + x = Owner.Left + (Owner.Width / 2); + y = Owner.Top + Owner.Height; + break; + case Positions.TopRight: + x = Owner.Left + Owner.Width; + y = Owner.Top; + break; + case Positions.BottomRight: + x = Owner.Left + Owner.Width; + y = Owner.Top + Owner.Height; + break; + case Positions.MiddleRight: + x = Owner.Left + Owner.Width; + y = Owner.Top + (Owner.Height / 2); + break; + } + return new Point(x, y); + } + } + + /// + /// Draw the adorner + /// + /// PaintEventArgs + public override void Paint(PaintEventArgs paintEventArgs) + { + Graphics targetGraphics = paintEventArgs.Graphics; + Rectangle clipRectangle = paintEventArgs.ClipRectangle; + + var bounds = Bounds; + GraphicsState state = targetGraphics.Save(); + + targetGraphics.SmoothingMode = SmoothingMode.None; + targetGraphics.CompositingMode = CompositingMode.SourceCopy; + targetGraphics.PixelOffsetMode = PixelOffsetMode.Half; + targetGraphics.InterpolationMode = InterpolationMode.NearestNeighbor; + + targetGraphics.FillRectangle(Brushes.Black, bounds.X, bounds.Y, bounds.Width , bounds.Height); + targetGraphics.Restore(state); + } + } +} diff --git a/Greenshot/Drawing/Adorners/ResizeAdorner.cs b/Greenshot/Drawing/Adorners/ResizeAdorner.cs new file mode 100644 index 000000000..da536b1f5 --- /dev/null +++ b/Greenshot/Drawing/Adorners/ResizeAdorner.cs @@ -0,0 +1,186 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2015 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 . + */ + +using Greenshot.Helpers; +using Greenshot.Plugin.Drawing; +using Greenshot.Plugin.Drawing.Adorners; +using System.Drawing; +using System.Drawing.Drawing2D; +using System.Windows.Forms; + +namespace Greenshot.Drawing.Adorners +{ + /// + /// This is the default "legacy" gripper adorner, not the one used for the tail in the speech-bubble + /// + public class ResizeAdorner : AbstractAdorner + { + private Rectangle _boundsBeforeResize = Rectangle.Empty; + private RectangleF _boundsAfterResize = RectangleF.Empty; + + public Positions Position { get; private set; } + + public ResizeAdorner(IDrawableContainer owner, Positions position) : base(owner) + { + Position = position; + } + + /// + /// Returns the cursor for when the mouse is over the adorner + /// + public override Cursor Cursor + { + get + { + bool isNotSwitched = Owner.Width >= 0; + if (Owner.Height < 0) + { + isNotSwitched = !isNotSwitched; + } + switch (Position) + { + case Positions.TopLeft: + case Positions.BottomRight: + return isNotSwitched ? Cursors.SizeNWSE : Cursors.SizeNESW; + case Positions.TopRight: + case Positions.BottomLeft: + return isNotSwitched ? Cursors.SizeNESW : Cursors.SizeNWSE; + case Positions.MiddleLeft: + case Positions.MiddleRight: + return Cursors.SizeWE; + case Positions.TopCenter: + case Positions.BottomCenter: + return Cursors.SizeNS; + default: + return Cursors.SizeAll; + } + } + } + + /// + /// Handle the mouse down + /// + /// + /// + public override void MouseDown(object sender, MouseEventArgs mouseEventArgs) + { + EditStatus = EditStatus.RESIZING; + _boundsBeforeResize = new Rectangle(Owner.Left, Owner.Top, Owner.Width, Owner.Height); + _boundsAfterResize = _boundsBeforeResize; + } + + /// + /// Handle the mouse move + /// + /// + /// + public override void MouseMove(object sender, MouseEventArgs mouseEventArgs) + { + if (EditStatus != EditStatus.RESIZING) + { + return; + } + Owner.Invalidate(); + Owner.MakeBoundsChangeUndoable(false); + + // 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(); + } + + /// + /// Return the location of the adorner + /// + public override Point Location { + get + { + int x = 0,y = 0; + switch (Position) + { + case Positions.TopLeft: + x = Owner.Left; + y = Owner.Top; + break; + case Positions.BottomLeft: + x = Owner.Left; + y = Owner.Top + Owner.Height; + break; + case Positions.MiddleLeft: + x = Owner.Left; + y = Owner.Top + (Owner.Height / 2); + break; + case Positions.TopCenter: + x = Owner.Left + (Owner.Width / 2); + y = Owner.Top; + break; + case Positions.BottomCenter: + x = Owner.Left + (Owner.Width / 2); + y = Owner.Top + Owner.Height; + break; + case Positions.TopRight: + x = Owner.Left + Owner.Width; + y = Owner.Top; + break; + case Positions.BottomRight: + x = Owner.Left + Owner.Width; + y = Owner.Top + Owner.Height; + break; + case Positions.MiddleRight: + x = Owner.Left + Owner.Width; + y = Owner.Top + (Owner.Height / 2); + break; + } + return new Point(x, y); + } + } + + /// + /// Draw the adorner + /// + /// PaintEventArgs + public override void Paint(PaintEventArgs paintEventArgs) + { + Graphics targetGraphics = paintEventArgs.Graphics; + Rectangle clipRectangle = paintEventArgs.ClipRectangle; + + var bounds = Bounds; + GraphicsState state = targetGraphics.Save(); + + targetGraphics.SmoothingMode = SmoothingMode.None; + targetGraphics.CompositingMode = CompositingMode.SourceCopy; + targetGraphics.PixelOffsetMode = PixelOffsetMode.Half; + targetGraphics.InterpolationMode = InterpolationMode.NearestNeighbor; + + targetGraphics.FillRectangle(Brushes.Black, bounds.X, bounds.Y, bounds.Width , bounds.Height); + targetGraphics.Restore(state); + } + } +} diff --git a/Greenshot/Drawing/Adorners/TargetAdorner.cs b/Greenshot/Drawing/Adorners/TargetAdorner.cs new file mode 100644 index 000000000..27e499bac --- /dev/null +++ b/Greenshot/Drawing/Adorners/TargetAdorner.cs @@ -0,0 +1,119 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2015 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 . + */ + +using Greenshot.Plugin.Drawing; +using System.Drawing; +using System.Drawing.Drawing2D; +using System.Windows.Forms; + +namespace Greenshot.Drawing.Adorners +{ + /// + /// This implements the special "gripper" for the Speech-Bubble tail + /// + public class TargetAdorner : AbstractAdorner + { + + public TargetAdorner(IDrawableContainer owner, Point location) : base(owner) + { + Location = location; + } + + /// + /// Handle the mouse down + /// + /// + /// + public override void MouseDown(object sender, MouseEventArgs mouseEventArgs) + { + EditStatus = EditStatus.MOVING; + } + + /// + /// Handle the mouse move + /// + /// + /// + public override void MouseMove(object sender, MouseEventArgs mouseEventArgs) + { + if (EditStatus != EditStatus.MOVING) + { + return; + } + + Owner.Invalidate(); + Point newGripperLocation = new Point(mouseEventArgs.X, mouseEventArgs.Y); + Rectangle surfaceBounds = new Rectangle(0, 0, Owner.Parent.Width, Owner.Parent.Height); + // Check if gripper inside the parent (surface), if not we need to move it inside + // This was made for BUG-1682 + if (!surfaceBounds.Contains(newGripperLocation)) + { + if (newGripperLocation.X > surfaceBounds.Right) + { + newGripperLocation.X = surfaceBounds.Right - 5; + } + if (newGripperLocation.X < surfaceBounds.Left) + { + newGripperLocation.X = surfaceBounds.Left; + } + if (newGripperLocation.Y > surfaceBounds.Bottom) + { + newGripperLocation.Y = surfaceBounds.Bottom - 5; + } + if (newGripperLocation.Y < surfaceBounds.Top) + { + newGripperLocation.Y = surfaceBounds.Top; + } + } + + Location = newGripperLocation; + Owner.Invalidate(); + } + + /// + /// Draw the adorner + /// + /// PaintEventArgs + public override void Paint(PaintEventArgs paintEventArgs) + { + Graphics targetGraphics = paintEventArgs.Graphics; + Rectangle clipRectangle = paintEventArgs.ClipRectangle; + + var bounds = Bounds; + targetGraphics.FillRectangle(Brushes.Green, bounds.X, bounds.Y, bounds.Width, bounds.Height); + } + + /// + /// Made sure this adorner is transformed + /// + /// Matrix + public override void Transform(Matrix matrix) + { + if (matrix == null) + { + return; + } + Point[] points = new[] { Location }; + matrix.TransformPoints(points); + Location = points[0]; + } + } +} diff --git a/Greenshot/Drawing/CropContainer.cs b/Greenshot/Drawing/CropContainer.cs index 676836f93..e1bf7a78a 100644 --- a/Greenshot/Drawing/CropContainer.cs +++ b/Greenshot/Drawing/CropContainer.cs @@ -20,6 +20,7 @@ */ using System.Drawing; +using System.Runtime.Serialization; using Greenshot.Drawing.Fields; using Greenshot.Helpers; using Greenshot.Plugin.Drawing; @@ -30,8 +31,19 @@ namespace Greenshot.Drawing { /// 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, FieldType.Flag.CONFIRMABLE); } diff --git a/Greenshot/Drawing/CursorContainer.cs b/Greenshot/Drawing/CursorContainer.cs index 2b5969c92..98bc77358 100644 --- a/Greenshot/Drawing/CursorContainer.cs +++ b/Greenshot/Drawing/CursorContainer.cs @@ -26,6 +26,7 @@ using System.Windows.Forms; using Greenshot.Plugin.Drawing; using System.Drawing.Drawing2D; using log4net; +using System.Runtime.Serialization; namespace Greenshot.Drawing { /// @@ -38,9 +39,21 @@ namespace Greenshot.Drawing { protected Cursor cursor; public CursorContainer(Surface parent) : base(parent) { + Init(); } - public CursorContainer(Surface parent, string filename) : base(parent) { + protected override void OnDeserialized(StreamingContext streamingContext) + { + base.OnDeserialized(streamingContext); + Init(); + } + + private void Init() + { + CreateDefaultAdorners(); + } + + public CursorContainer(Surface parent, string filename) : this(parent) { Load(filename); } diff --git a/Greenshot/Drawing/DrawableContainer.cs b/Greenshot/Drawing/DrawableContainer.cs index 9b3a10952..f65480b2b 100644 --- a/Greenshot/Drawing/DrawableContainer.cs +++ b/Greenshot/Drawing/DrawableContainer.cs @@ -20,6 +20,7 @@ */ using Greenshot.Configuration; +using Greenshot.Drawing.Adorners; using Greenshot.Drawing.Fields; using Greenshot.Drawing.Filters; using Greenshot.Helpers; @@ -27,15 +28,18 @@ using Greenshot.IniFile; using Greenshot.Memento; using Greenshot.Plugin; using Greenshot.Plugin.Drawing; +using Greenshot.Plugin.Drawing.Adorners; using log4net; using System; using System.Collections.Generic; using System.ComponentModel; using System.Drawing; using System.Drawing.Drawing2D; +using System.Runtime.Serialization; using System.Windows.Forms; -namespace Greenshot.Drawing { +namespace Greenshot.Drawing +{ /// /// represents a rectangle, ellipse, label or whatever. Can contain filters, too. /// serializable for clipboard support @@ -46,10 +50,23 @@ namespace Greenshot.Drawing { public abstract class DrawableContainer : AbstractFieldHolderWithChildren, IDrawableContainer { private static readonly ILog LOG = LogManager.GetLogger(typeof(DrawableContainer)); protected static readonly EditorConfiguration EditorConfig = IniConfig.GetIniSection(); - private bool _isMadeUndoable; private const int M11 = 0; private const int M22 = 3; + [OnDeserialized] + private void OnDeserializedInit(StreamingContext context) + { + _adorners = new List(); + OnDeserialized(context); + } + + /// + /// Override to implement your own deserialization logic, like initializing properties which are not serialized + /// + /// + protected virtual void OnDeserialized(StreamingContext streamingContext) + { + } protected EditStatus _defaultEditMode = EditStatus.DRAWING; public EditStatus DefaultEditMode { @@ -71,16 +88,6 @@ namespace Greenshot.Drawing { if (!disposing) { return; } - if (_grippers != null) { - for (int i = 0; i < _grippers.Length; i++) { - if (_grippers[i] == null) { - continue; - } - _grippers[i].Dispose(); - _grippers[i] = null; - } - _grippers = null; - } FieldAggregator aggProps = _parent.FieldAggregator; aggProps.UnbindElement(this); @@ -115,14 +122,10 @@ namespace Greenshot.Drawing { get { return _parent; } set { SwitchParent((Surface)value); } } - [NonSerialized] - protected Gripper[] _grippers; - private bool layoutSuspended; [NonSerialized] - private Gripper _targetGripper; - - public Gripper TargetGripper { + private TargetAdorner _targetGripper; + public TargetAdorner TargetGripper { get { return _targetGripper; } @@ -158,7 +161,6 @@ namespace Greenshot.Drawing { return; } left = value; - DoLayout(); } } @@ -170,7 +172,6 @@ namespace Greenshot.Drawing { return; } top = value; - DoLayout(); } } @@ -182,7 +183,6 @@ namespace Greenshot.Drawing { return; } width = value; - DoLayout(); } } @@ -194,7 +194,6 @@ namespace Greenshot.Drawing { return; } height = value; - DoLayout(); } } @@ -218,6 +217,19 @@ namespace Greenshot.Drawing { } } + /// + /// List of available Adorners + /// + [NonSerialized] + private IList _adorners = new List(); + public IList Adorners + { + get + { + return _adorners; + } + } + [NonSerialized] // will store current bounds of this DrawableContainer before starting a resize protected Rectangle _boundsBeforeResize = Rectangle.Empty; @@ -246,7 +258,6 @@ namespace Greenshot.Drawing { public DrawableContainer(Surface parent) { InitializeFields(); _parent = parent; - InitControls(); } public void Add(IFilter filter) { @@ -303,7 +314,7 @@ namespace Greenshot.Drawing { Left = _parent.Width - Width - lineThickness/2; } if (horizontalAlignment == HorizontalAlignment.Center) { - Left = _parent.Width / 2 - Width / 2 - lineThickness/2; + Left = (_parent.Width / 2) - (Width / 2) - lineThickness/2; } if (verticalAlignment == VerticalAlignment.TOP) { @@ -313,195 +324,42 @@ namespace Greenshot.Drawing { Top = _parent.Height - Height - lineThickness/2; } if (verticalAlignment == VerticalAlignment.CENTER) { - Top = _parent.Height / 2 - Height / 2 - lineThickness/2; + Top = (_parent.Height / 2) - (Height / 2) - lineThickness/2; } } public virtual bool InitContent() { return true; } public virtual void OnDoubleClick() {} - - private void InitControls() { - InitGrippers(); - - DoLayout(); - } - - /// - /// Move the TargetGripper around, confined to the surface to solve BUG-1682 - /// - /// - /// - protected virtual void TargetGripperMove(int newX, int newY) { - Point newGripperLocation = new Point(newX, newY); - Rectangle surfaceBounds = new Rectangle(0, 0, _parent.Width, _parent.Height); - // Check if gripper inside the parent (surface), if not we need to move it inside - // This was made for BUG-1682 - if (!surfaceBounds.Contains(newGripperLocation)) { - if (newGripperLocation.X > surfaceBounds.Right) { - newGripperLocation.X = surfaceBounds.Right - 5; - } - if (newGripperLocation.X < surfaceBounds.Left) { - newGripperLocation.X = surfaceBounds.Left; - } - if (newGripperLocation.Y > surfaceBounds.Bottom) { - newGripperLocation.Y = surfaceBounds.Bottom - 5; - } - if (newGripperLocation.Y < surfaceBounds.Top) { - newGripperLocation.Y = surfaceBounds.Top; - } - } - - _targetGripper.Left = newGripperLocation.X; - _targetGripper.Top = newGripperLocation.Y; - } /// /// Initialize a target gripper /// protected void InitTargetGripper(Color gripperColor, Point location) { - _targetGripper = new Gripper { - Cursor = Cursors.SizeAll, - BackColor = gripperColor, - Visible = false, - Parent = _parent, - Location = location - }; - _targetGripper.MouseDown += GripperMouseDown; - _targetGripper.MouseUp += GripperMouseUp; - _targetGripper.MouseMove += GripperMouseMove; - if (_parent != null) { - _parent.Controls.Add(_targetGripper); // otherwise we'll attach them in switchParent - } + _targetGripper = new TargetAdorner(this, location); + Adorners.Add(_targetGripper); } - protected void InitGrippers() { - - _grippers = new Gripper[8]; - for(int i=0; i<_grippers.Length; i++) { - _grippers[i] = new Gripper(); - _grippers[i].Position = i; - _grippers[i].MouseDown += GripperMouseDown; - _grippers[i].MouseUp += GripperMouseUp; - _grippers[i].MouseMove += GripperMouseMove; - _grippers[i].Visible = false; - _grippers[i].Parent = _parent; - } - _grippers[Gripper.POSITION_TOP_CENTER].Cursor = Cursors.SizeNS; - _grippers[Gripper.POSITION_MIDDLE_RIGHT].Cursor = Cursors.SizeWE; - _grippers[Gripper.POSITION_BOTTOM_CENTER].Cursor = Cursors.SizeNS; - _grippers[Gripper.POSITION_MIDDLE_LEFT].Cursor = Cursors.SizeWE; - if (_parent != null) { - _parent.Controls.AddRange(_grippers); // otherwise we'll attach them in switchParent - } - } - - public void SuspendLayout() { - layoutSuspended = true; - } - - public void ResumeLayout() { - layoutSuspended = false; - DoLayout(); - } - - protected virtual void DoLayout() { - if (_grippers == null) { - return; - } - if (!layoutSuspended) { - int[] xChoords = {Left-2,Left+Width/2-2,Left+Width-2}; - int[] yChoords = {Top-2,Top+Height/2-2,Top+Height-2}; + /// + /// Create the default adorners for a rectangle based container + /// - _grippers[Gripper.POSITION_TOP_LEFT].Left = xChoords[0]; _grippers[Gripper.POSITION_TOP_LEFT].Top = yChoords[0]; - _grippers[Gripper.POSITION_TOP_CENTER].Left = xChoords[1]; _grippers[Gripper.POSITION_TOP_CENTER].Top = yChoords[0]; - _grippers[Gripper.POSITION_TOP_RIGHT].Left = xChoords[2]; _grippers[Gripper.POSITION_TOP_RIGHT].Top = yChoords[0]; - _grippers[Gripper.POSITION_MIDDLE_RIGHT].Left = xChoords[2]; _grippers[Gripper.POSITION_MIDDLE_RIGHT].Top = yChoords[1]; - _grippers[Gripper.POSITION_BOTTOM_RIGHT].Left = xChoords[2]; _grippers[Gripper.POSITION_BOTTOM_RIGHT].Top = yChoords[2]; - _grippers[Gripper.POSITION_BOTTOM_CENTER].Left = xChoords[1]; _grippers[Gripper.POSITION_BOTTOM_CENTER].Top = yChoords[2]; - _grippers[Gripper.POSITION_BOTTOM_LEFT].Left = xChoords[0]; _grippers[Gripper.POSITION_BOTTOM_LEFT].Top = yChoords[2]; - _grippers[Gripper.POSITION_MIDDLE_LEFT].Left = xChoords[0]; _grippers[Gripper.POSITION_MIDDLE_LEFT].Top = yChoords[1]; - - if((_grippers[Gripper.POSITION_TOP_LEFT].Left < _grippers[Gripper.POSITION_BOTTOM_RIGHT].Left && _grippers[Gripper.POSITION_TOP_LEFT].Top < _grippers[Gripper.POSITION_BOTTOM_RIGHT].Top) || - _grippers[Gripper.POSITION_TOP_LEFT].Left > _grippers[Gripper.POSITION_BOTTOM_RIGHT].Left && _grippers[Gripper.POSITION_TOP_LEFT].Top > _grippers[Gripper.POSITION_BOTTOM_RIGHT].Top) { - _grippers[Gripper.POSITION_TOP_LEFT].Cursor = Cursors.SizeNWSE; - _grippers[Gripper.POSITION_TOP_RIGHT].Cursor = Cursors.SizeNESW; - _grippers[Gripper.POSITION_BOTTOM_RIGHT].Cursor = Cursors.SizeNWSE; - _grippers[Gripper.POSITION_BOTTOM_LEFT].Cursor = Cursors.SizeNESW; - } else if((_grippers[Gripper.POSITION_TOP_LEFT].Left > _grippers[Gripper.POSITION_BOTTOM_RIGHT].Left && _grippers[Gripper.POSITION_TOP_LEFT].Top < _grippers[Gripper.POSITION_BOTTOM_RIGHT].Top) || - _grippers[Gripper.POSITION_TOP_LEFT].Left < _grippers[Gripper.POSITION_BOTTOM_RIGHT].Left && _grippers[Gripper.POSITION_TOP_LEFT].Top > _grippers[Gripper.POSITION_BOTTOM_RIGHT].Top) { - _grippers[Gripper.POSITION_TOP_LEFT].Cursor = Cursors.SizeNESW; - _grippers[Gripper.POSITION_TOP_RIGHT].Cursor = Cursors.SizeNWSE; - _grippers[Gripper.POSITION_BOTTOM_RIGHT].Cursor = Cursors.SizeNESW; - _grippers[Gripper.POSITION_BOTTOM_LEFT].Cursor = Cursors.SizeNWSE; - } else if (_grippers[Gripper.POSITION_TOP_LEFT].Left == _grippers[Gripper.POSITION_BOTTOM_RIGHT].Left) { - _grippers[Gripper.POSITION_TOP_LEFT].Cursor = Cursors.SizeNS; - _grippers[Gripper.POSITION_BOTTOM_RIGHT].Cursor = Cursors.SizeNS; - } else if (_grippers[Gripper.POSITION_TOP_LEFT].Top == _grippers[Gripper.POSITION_BOTTOM_RIGHT].Top) { - _grippers[Gripper.POSITION_TOP_LEFT].Cursor = Cursors.SizeWE; - _grippers[Gripper.POSITION_BOTTOM_RIGHT].Cursor = Cursors.SizeWE; - } + protected void CreateDefaultAdorners() { + if (Adorners.Count > 0) + { + LOG.Warn("Adorners are already defined!"); } - } - - private void GripperMouseDown(object sender, MouseEventArgs e) { - Gripper originatingGripper = (Gripper)sender; - if (originatingGripper != _targetGripper) { - Status = EditStatus.RESIZING; - _boundsBeforeResize = new Rectangle(left, top, width, height); - _boundsAfterResize = new RectangleF(_boundsBeforeResize.Left, _boundsBeforeResize.Top, _boundsBeforeResize.Width, _boundsBeforeResize.Height); - } else { - Status = EditStatus.MOVING; - } - _isMadeUndoable = false; + // 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)); } - private void GripperMouseUp(object sender, MouseEventArgs e) { - Gripper originatingGripper = (Gripper)sender; - if (originatingGripper != _targetGripper) { - _boundsBeforeResize = Rectangle.Empty; - _boundsAfterResize = RectangleF.Empty; - _isMadeUndoable = false; - } - Status = EditStatus.IDLE; - Invalidate(); - } - - private void GripperMouseMove(object sender, MouseEventArgs e) { - Invalidate(); - Gripper originatingGripper = (Gripper)sender; - int absX = originatingGripper.Left + e.X; - int absY = originatingGripper.Top + e.Y; - if (originatingGripper == _targetGripper && Status.Equals(EditStatus.MOVING)) { - TargetGripperMove(absX, absY); - } else if (Status.Equals(EditStatus.RESIZING)) { - // check if we already made this undoable - if (!_isMadeUndoable) { - // don't allow another undo until we are finished with this move - _isMadeUndoable = true; - // Make undo-able - MakeBoundsChangeUndoable(false); - } - - SuspendLayout(); - - // 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, originatingGripper.Position, new PointF(absX, absY), ScaleHelper.GetScaleOptions()); - - // apply scaled bounds to this DrawableContainer - ApplyBounds(_boundsAfterResize); - - ResumeLayout(); - } - Invalidate(); - } - public bool hasFilters { get { return Filters.Count > 0; @@ -556,43 +414,10 @@ namespace Greenshot.Drawing { } } - public virtual void ShowGrippers() { - if (_grippers != null) { - for (int i = 0; i < _grippers.Length; i++) { - if (_grippers[i].Enabled) { - _grippers[i].Show(); - } else { - _grippers[i].Hide(); - } - } - } - if (_targetGripper != null) { - if (_targetGripper.Enabled) { - _targetGripper.Show(); - } else { - _targetGripper.Hide(); - } - } - ResumeLayout(); - } - - public virtual void HideGrippers() { - SuspendLayout(); - if (_grippers != null) { - for (int i = 0; i < _grippers.Length; i++) { - _grippers[i].Hide(); - } - } - if (_targetGripper != null) { - _targetGripper.Hide(); - } - } - + public void ResizeTo(int width, int height, int anchorPosition) { - SuspendLayout(); Width = width; Height = height; - ResumeLayout(); } /// @@ -604,10 +429,8 @@ namespace Greenshot.Drawing { } public void MoveBy(int dx, int dy) { - SuspendLayout(); Left += dx; Top += dy; - ResumeLayout(); } /// @@ -630,7 +453,6 @@ namespace Greenshot.Drawing { /// true if the event is handled, false if the surface needs to handle it public virtual bool HandleMouseMove(int x, int y) { Invalidate(); - SuspendLayout(); // reset "workrbench" rectangle to current bounds _boundsAfterResize.X = _boundsBeforeResize.Left; @@ -643,7 +465,6 @@ namespace Greenshot.Drawing { // apply scaled bounds to this DrawableContainer ApplyBounds(_boundsAfterResize); - ResumeLayout(); Invalidate(); return true; } @@ -657,28 +478,8 @@ namespace Greenshot.Drawing { } protected virtual void SwitchParent(Surface newParent) { - // Target gripper - if (_parent != null && _targetGripper != null) { - _parent.Controls.Remove(_targetGripper); - } - // Normal grippers - if (_parent != null && _grippers != null) { - for (int i=0; i<_grippers.Length; i++) { - _parent.Controls.Remove(_grippers[i]); - } - } else if (_grippers == null) { - InitControls(); - } - _parent = newParent; - // Target gripper - if (_parent != null && _targetGripper != null) { - _parent.Controls.Add(_targetGripper); - } - // Normal grippers - if (_grippers != null) { - _parent.Controls.AddRange(_grippers); - } + _parent = newParent; foreach(IFilter filter in Filters) { filter.Parent = this; } @@ -775,26 +576,16 @@ namespace Greenshot.Drawing { if (matrix == null) { return; } - SuspendLayout(); Point topLeft = new Point(Left, Top); Point bottomRight = new Point(Left + Width, Top + Height); - Point[] points; - if (TargetGripper != null) { - points = new[] {topLeft, bottomRight, TargetGripper.Location}; - - } else { - points = new[] { topLeft, bottomRight }; - } + 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; - if (TargetGripper != null) { - TargetGripper.Location = points[points.Length-1]; - } - ResumeLayout(); + } protected virtual ScaleHelper.IDoubleProcessor GetAngleRoundProcessor() { diff --git a/Greenshot/Drawing/DrawableContainerList.cs b/Greenshot/Drawing/DrawableContainerList.cs index 46daf9773..692295966 100644 --- a/Greenshot/Drawing/DrawableContainerList.cs +++ b/Greenshot/Drawing/DrawableContainerList.cs @@ -171,26 +171,6 @@ namespace Greenshot.Drawing { } } - /// - /// Hides the grippers of all elements in the list. - /// - public void HideGrippers() { - foreach(var dc in this) { - dc.HideGrippers(); - dc.Invalidate(); - } - } - - /// - /// Shows the grippers of all elements in the list. - /// - public void ShowGrippers() { - foreach(var dc in this) { - dc.ShowGrippers(); - dc.Invalidate(); - } - } - /// /// Indicates whether on of the elements is clickable at the given location /// diff --git a/Greenshot/Drawing/EllipseContainer.cs b/Greenshot/Drawing/EllipseContainer.cs index c4fe278e1..9ccec37f3 100644 --- a/Greenshot/Drawing/EllipseContainer.cs +++ b/Greenshot/Drawing/EllipseContainer.cs @@ -33,6 +33,7 @@ namespace Greenshot.Drawing { [Serializable()] public class EllipseContainer : DrawableContainer { public EllipseContainer(Surface parent) : base(parent) { + CreateDefaultAdorners(); } protected override void InitializeFields() { diff --git a/Greenshot/Drawing/Fields/AbstractFieldHolder.cs b/Greenshot/Drawing/Fields/AbstractFieldHolder.cs index 4bb0316fb..1c4a98037 100644 --- a/Greenshot/Drawing/Fields/AbstractFieldHolder.cs +++ b/Greenshot/Drawing/Fields/AbstractFieldHolder.cs @@ -53,7 +53,7 @@ namespace Greenshot.Drawing.Fields { private readonly List fields = new List(); [OnDeserialized] - private void OnDeserialized(StreamingContext context) { + private void OnFieldHolderDeserialized(StreamingContext context) { fieldsByType = new Dictionary(); // listen to changing properties foreach(Field field in fields) { diff --git a/Greenshot/Drawing/Fields/AbstractFieldHolderWithChildren.cs b/Greenshot/Drawing/Fields/AbstractFieldHolderWithChildren.cs index 3114ffc46..a81308d0e 100644 --- a/Greenshot/Drawing/Fields/AbstractFieldHolderWithChildren.cs +++ b/Greenshot/Drawing/Fields/AbstractFieldHolderWithChildren.cs @@ -45,8 +45,8 @@ namespace Greenshot.Drawing.Fields { fieldChangedEventHandler = OnFieldChanged; } - [OnDeserialized()] - private void OnDeserialized(StreamingContext context) { + [OnDeserialized] + private void OnFieldHolderWithChildrenDeserialized(StreamingContext context) { // listen to changing properties foreach(IFieldHolder fieldHolder in Children) { fieldHolder.FieldChanged += fieldChangedEventHandler; diff --git a/Greenshot/Drawing/FilterContainer.cs b/Greenshot/Drawing/FilterContainer.cs index 7e6b2336e..1607fbdf4 100644 --- a/Greenshot/Drawing/FilterContainer.cs +++ b/Greenshot/Drawing/FilterContainer.cs @@ -24,6 +24,7 @@ using Greenshot.Drawing.Fields; using Greenshot.Helpers; using Greenshot.Plugin.Drawing; using System.Drawing.Drawing2D; +using System.Runtime.Serialization; namespace Greenshot.Drawing { /// @@ -40,6 +41,18 @@ namespace Greenshot.Drawing { } 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() { diff --git a/Greenshot/Drawing/FreehandContainer.cs b/Greenshot/Drawing/FreehandContainer.cs index 5294f5bd5..ec927017b 100644 --- a/Greenshot/Drawing/FreehandContainer.cs +++ b/Greenshot/Drawing/FreehandContainer.cs @@ -47,7 +47,6 @@ namespace Greenshot.Drawing { /// Constructor /// public FreehandContainer(Surface parent) : base(parent) { - Init(); Width = parent.Width; Height = parent.Height; Top = 0; @@ -59,16 +58,6 @@ namespace Greenshot.Drawing { AddField(GetType(), FieldType.LINE_COLOR, Color.Red); } - - protected void Init() { - if (_grippers != null) { - for (int i = 0; i < _grippers.Length; i++) { - _grippers[i].Enabled = false; - _grippers[i].Visible = false; - } - } - } - public override void Transform(Matrix matrix) { Point[] points = capturePoints.ToArray(); @@ -78,11 +67,7 @@ namespace Greenshot.Drawing { RecalculatePath(); } - [OnDeserialized] - private void OnDeserialized(StreamingContext context) { - InitGrippers(); - DoLayout(); - Init(); + protected override void OnDeserialized(StreamingContext context) { RecalculatePath(); } @@ -256,17 +241,6 @@ namespace Greenshot.Drawing { return freehandPath.GetHashCode(); } - /// - /// This is overriden to prevent the grippers to be modified. - /// Might not be the best way... - /// - protected override void DoLayout() { - } - - public override void ShowGrippers() { - ResumeLayout(); - } - public override bool ClickableAt(int x, int y) { bool returnValue = base.ClickableAt(x, y); if (returnValue) { diff --git a/Greenshot/Drawing/HighlightContainer.cs b/Greenshot/Drawing/HighlightContainer.cs index 701592539..5b8b783e2 100644 --- a/Greenshot/Drawing/HighlightContainer.cs +++ b/Greenshot/Drawing/HighlightContainer.cs @@ -42,8 +42,8 @@ namespace Greenshot.Drawing { AddField(GetType(), FieldType.PREPARED_FILTER_HIGHLIGHT, PreparedFilter.TEXT_HIGHTLIGHT); } - [OnDeserialized] - private void OnDeserialized(StreamingContext context) { + protected override void OnDeserialized(StreamingContext context) + { Init(); } diff --git a/Greenshot/Drawing/IconContainer.cs b/Greenshot/Drawing/IconContainer.cs index 846ee3cde..a72b39496 100644 --- a/Greenshot/Drawing/IconContainer.cs +++ b/Greenshot/Drawing/IconContainer.cs @@ -24,6 +24,7 @@ using System.IO; using Greenshot.Plugin.Drawing; using System.Drawing.Drawing2D; using log4net; +using System.Runtime.Serialization; namespace Greenshot.Drawing { /// @@ -36,6 +37,18 @@ namespace Greenshot.Drawing { 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) { diff --git a/Greenshot/Drawing/ImageContainer.cs b/Greenshot/Drawing/ImageContainer.cs index 25894af74..ca1742195 100644 --- a/Greenshot/Drawing/ImageContainer.cs +++ b/Greenshot/Drawing/ImageContainer.cs @@ -27,6 +27,7 @@ using GreenshotPlugin.Core; using System.Drawing.Drawing2D; using Greenshot.Core; using log4net; +using System.Runtime.Serialization; namespace Greenshot.Drawing { /// @@ -58,6 +59,18 @@ namespace Greenshot.Drawing { 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() { diff --git a/Greenshot/Drawing/LineContainer.cs b/Greenshot/Drawing/LineContainer.cs index b11486901..47776ff00 100644 --- a/Greenshot/Drawing/LineContainer.cs +++ b/Greenshot/Drawing/LineContainer.cs @@ -26,6 +26,7 @@ using System.Runtime.Serialization; using Greenshot.Drawing.Fields; using Greenshot.Helpers; using Greenshot.Plugin.Drawing; +using Greenshot.Drawing.Adorners; namespace Greenshot.Drawing { /// @@ -45,19 +46,14 @@ namespace Greenshot.Drawing { AddField(GetType(), FieldType.SHADOW, true); } - [OnDeserialized()] - private void OnDeserialized(StreamingContext context) { - InitGrippers(); - DoLayout(); + protected override void OnDeserialized(StreamingContext context) + { Init(); } protected void Init() { - if (_grippers != null) { - foreach (int index in new[] { 1, 2, 3, 5, 6, 7 }) { - _grippers[index].Enabled = false; - } - } + Adorners.Add(new MoveAdorner(this, Positions.TopLeft)); + Adorners.Add(new MoveAdorner(this, Positions.BottomRight)); } public override void Draw(Graphics graphics, RenderMode rm) { @@ -113,8 +109,6 @@ namespace Greenshot.Drawing { protected override ScaleHelper.IDoubleProcessor GetAngleRoundProcessor() { return ScaleHelper.LineAngleRoundBehavior.Instance; - } - - + } } } diff --git a/Greenshot/Drawing/ObfuscateContainer.cs b/Greenshot/Drawing/ObfuscateContainer.cs index 80f395c31..032291527 100644 --- a/Greenshot/Drawing/ObfuscateContainer.cs +++ b/Greenshot/Drawing/ObfuscateContainer.cs @@ -37,15 +37,16 @@ namespace Greenshot.Drawing { base.InitializeFields(); AddField(GetType(), FieldType.PREPARED_FILTER_OBFUSCATE, PreparedFilter.PIXELIZE); } - - [OnDeserialized] - private void OnDeserialized(StreamingContext context) { + + protected override void OnDeserialized(StreamingContext context) + { Init(); } private void Init() { FieldChanged += ObfuscateContainer_OnFieldChanged; ConfigurePreparedFilters(); + CreateDefaultAdorners(); } protected void ObfuscateContainer_OnFieldChanged(object sender, FieldChangedEventArgs e) { diff --git a/Greenshot/Drawing/Gripper.cs b/Greenshot/Drawing/Positions.cs similarity index 54% rename from Greenshot/Drawing/Gripper.cs rename to Greenshot/Drawing/Positions.cs index 7a65fc57a..63d7cb759 100644 --- a/Greenshot/Drawing/Gripper.cs +++ b/Greenshot/Drawing/Positions.cs @@ -19,38 +19,20 @@ * along with this program. If not, see . */ -using System.Drawing; -using System.Windows.Forms; - -namespace Greenshot.Drawing { +namespace Greenshot.Drawing +{ /// - /// Grippers are the dragable edges of our containers + /// Position /// - public class Gripper : Label { - /// - /// Constants for anchor/gripper position: - /// 0 1 2 - /// 7 3 - /// 6 5 4 - /// - public const int POSITION_TOP_LEFT = 0; - public const int POSITION_TOP_CENTER = 1; - public const int POSITION_TOP_RIGHT = 2; - public const int POSITION_MIDDLE_RIGHT = 3; - public const int POSITION_BOTTOM_RIGHT = 4; - public const int POSITION_BOTTOM_CENTER = 5; - public const int POSITION_BOTTOM_LEFT = 6; - public const int POSITION_MIDDLE_LEFT = 7; - - public int Position { - get; - set; - } - - public Gripper() { - Width = 5; - Height = 5; - BackColor = Color.Black; - } + public enum Positions : int + { + TopLeft = 0, + TopCenter = 1, + TopRight = 2, + MiddleRight = 3, + BottomRight = 4, + BottomCenter = 5, + BottomLeft = 6, + MiddleLeft = 7 } } diff --git a/Greenshot/Drawing/RectangleContainer.cs b/Greenshot/Drawing/RectangleContainer.cs index 933adb180..d12f215eb 100644 --- a/Greenshot/Drawing/RectangleContainer.cs +++ b/Greenshot/Drawing/RectangleContainer.cs @@ -25,6 +25,7 @@ using System.Drawing.Drawing2D; using Greenshot.Drawing.Fields; using Greenshot.Helpers; using Greenshot.Plugin.Drawing; +using System.Runtime.Serialization; namespace Greenshot.Drawing { /// @@ -34,6 +35,22 @@ namespace Greenshot.Drawing { public class RectangleContainer : DrawableContainer { public RectangleContainer(Surface parent) : base(parent) { + Init(); + } + + /// + /// Do some logic to make sure all field are initiated correctly + /// + /// StreamingContext + protected override void OnDeserialized(StreamingContext streamingContext) + { + base.OnDeserialized(streamingContext); + Init(); + } + + private void Init() + { + CreateDefaultAdorners(); } protected override void InitializeFields() { diff --git a/Greenshot/Drawing/SpeechbubbleContainer.cs b/Greenshot/Drawing/SpeechbubbleContainer.cs index 4bbc91c29..f49768329 100644 --- a/Greenshot/Drawing/SpeechbubbleContainer.cs +++ b/Greenshot/Drawing/SpeechbubbleContainer.cs @@ -28,7 +28,8 @@ using System.Drawing.Drawing2D; using System.Drawing.Text; using System.Runtime.Serialization; -namespace Greenshot.Drawing { +namespace Greenshot.Drawing +{ /// /// Description of SpeechbubbleContainer. /// @@ -56,8 +57,8 @@ namespace Greenshot.Drawing { /// Restore the target gripper /// /// - [OnDeserialized] - private void SetValuesOnDeserialized(StreamingContext context) { + protected override void OnDeserialized(StreamingContext context) + { InitTargetGripper(Color.Green, _storedTargetGripperLocation); } #endregion @@ -115,7 +116,7 @@ namespace Greenshot.Drawing { if (TargetGripper.Location != newGripperLocation) { Invalidate(); - TargetGripperMove(newGripperLocation.X, newGripperLocation.Y); + TargetGripper.Location = newGripperLocation; Invalidate(); } return returnValue; @@ -177,8 +178,7 @@ namespace Greenshot.Drawing { 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, TargetGripper.Left, TargetGripper.Top); - int tailWidth = (Math.Abs(rect.Width) + Math.Abs(rect.Height)) / 20; + int tailLength = GeometryHelper.Distance2D(rect.Left + (rect.Width / 2), rect.Top + (rect.Height / 2), TargetGripper.Left, TargetGripper.Top); 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); @@ -189,7 +189,6 @@ namespace Greenshot.Drawing { tail.AddLine(tailWidth, 0, 0, -tailLength); tail.CloseFigure(); - int tailAngle = 90 + (int)GeometryHelper.Angle2D(rect.Left + rect.Width / 2, rect.Top + rect.Height / 2, TargetGripper.Left, TargetGripper.Top); using (Matrix tailMatrix = new Matrix()) { tailMatrix.Translate(rect.Left + rect.Width / 2, rect.Top + rect.Height / 2); diff --git a/Greenshot/Drawing/StepLabelContainer.cs b/Greenshot/Drawing/StepLabelContainer.cs index e42076c82..636fd4540 100644 --- a/Greenshot/Drawing/StepLabelContainer.cs +++ b/Greenshot/Drawing/StepLabelContainer.cs @@ -45,6 +45,12 @@ namespace Greenshot.Drawing { public StepLabelContainer(Surface parent) : base(parent) { parent.AddStepLabel(this); InitContent(); + Init(); + } + + private void Init() + { + CreateDefaultAdorners(); } #region Number serializing @@ -75,8 +81,9 @@ namespace Greenshot.Drawing { /// Restore values that don't serialize /// /// - [OnDeserialized] - private void SetValuesOnDeserialized(StreamingContext context) { + protected override void OnDeserialized(StreamingContext context) + { + Init(); _stringFormat = new StringFormat(); _stringFormat.Alignment = StringAlignment.Center; _stringFormat.LineAlignment = StringAlignment.Center; diff --git a/Greenshot/Drawing/Surface.cs b/Greenshot/Drawing/Surface.cs index 22c1aea93..13dec90c0 100644 --- a/Greenshot/Drawing/Surface.cs +++ b/Greenshot/Drawing/Surface.cs @@ -27,6 +27,7 @@ using Greenshot.IniFile; using Greenshot.Memento; using Greenshot.Plugin; using Greenshot.Plugin.Drawing; +using Greenshot.Plugin.Drawing.Adorners; using GreenshotPlugin.Controls; using GreenshotPlugin.Core; using log4net; @@ -40,7 +41,8 @@ using System.IO; using System.Runtime.Serialization.Formatters.Binary; using System.Windows.Forms; -namespace Greenshot.Drawing { +namespace Greenshot.Drawing +{ /// /// Description of Surface. @@ -991,12 +993,46 @@ namespace Greenshot.Drawing { Invalidate(); } + /// + /// Check if an adorner was "hit", and change the cursor if so + /// + /// MouseEventArgs + /// IAdorner + private IAdorner FindActiveAdorner(MouseEventArgs mouseEventArgs) + { + foreach(IDrawableContainer drawableContainer in selectedElements) + { + foreach(IAdorner adorner in drawableContainer.Adorners) + { + + if (adorner.IsActive || adorner.HitTest(mouseEventArgs.Location)) + { + if (adorner.Cursor != null) + { + Cursor = adorner.Cursor; + } + return adorner; + } + } + } + return null; + } + /// /// This event handler is called when someone presses the mouse on a surface. /// /// /// void SurfaceMouseDown(object sender, MouseEventArgs e) { + + // Handle Adorners + var adorner = FindActiveAdorner(e); + if (adorner != null) + { + adorner.MouseDown(sender, e); + return; + } + _mouseStart = e.Location; // check contextmenu @@ -1068,6 +1104,15 @@ namespace Greenshot.Drawing { /// /// void SurfaceMouseUp(object sender, MouseEventArgs e) { + + // Handle Adorners + var adorner = FindActiveAdorner(e); + if (adorner != null) + { + adorner.MouseUp(sender, e); + return; + } + Point currentMouse = new Point(e.X, e.Y); _elements.Status = EditStatus.IDLE; @@ -1101,7 +1146,7 @@ namespace Greenshot.Drawing { } if (selectedElements.Count > 0) { - selectedElements.ShowGrippers(); + selectedElements.Invalidate(); selectedElements.Selected = true; } @@ -1129,6 +1174,14 @@ namespace Greenshot.Drawing { /// /// void SurfaceMouseMove(object sender, MouseEventArgs e) { + // Handle Adorners + var adorner = FindActiveAdorner(e); + if (adorner != null) + { + adorner.MouseMove(sender, e); + return; + } + Point currentMouse = e.Location; if (DrawingMode != DrawingModes.None) { @@ -1140,7 +1193,7 @@ namespace Greenshot.Drawing { if (_mouseDown) { if (_mouseDownElement != null) { // an element is currently dragged _mouseDownElement.Invalidate(); - selectedElements.HideGrippers(); + selectedElements.Invalidate(); // Move the element if (_mouseDownElement.Selected) { if (!_isSurfaceMoveMadeUndoable) { @@ -1206,15 +1259,15 @@ namespace Greenshot.Drawing { public Image GetImageForExport() { return GetImage(RenderMode.EXPORT); } - + /// /// This is the event handler for the Paint Event, try to draw as little as possible! /// /// - /// - void SurfacePaint(object sender, PaintEventArgs e) { - Graphics targetGraphics = e.Graphics; - Rectangle clipRectangle = e.ClipRectangle; + /// PaintEventArgs + void SurfacePaint(object sender, PaintEventArgs paintEventArgs) { + Graphics targetGraphics = paintEventArgs.Graphics; + Rectangle clipRectangle = paintEventArgs.ClipRectangle; if (Rectangle.Empty.Equals(clipRectangle)) { LOG.Debug("Empty cliprectangle??"); return; @@ -1249,6 +1302,17 @@ namespace Greenshot.Drawing { targetGraphics.DrawImage(Image, clipRectangle, clipRectangle, GraphicsUnit.Pixel); _elements.Draw(targetGraphics, null, RenderMode.EDIT, clipRectangle); } + + // No clipping for the adorners + targetGraphics.ResetClip(); + // Draw adorners last + foreach (var drawableContainer in selectedElements) + { + foreach(var adorner in drawableContainer.Adorners) + { + adorner.Paint(paintEventArgs); + } + } } private void DrawBackground(Graphics targetGraphics, Rectangle clipRectangle) { @@ -1539,9 +1603,9 @@ namespace Greenshot.Drawing { /// /// public void DeselectElement(IDrawableContainer container) { - container.HideGrippers(); container.Selected = false; selectedElements.Remove(container); + container.Invalidate(); FieldAggregator.UnbindElement(container); if (_movingElementChanged != null) { SurfaceElementEventArgs eventArgs = new SurfaceElementEventArgs(); @@ -1557,10 +1621,9 @@ namespace Greenshot.Drawing { if (HasSelectedElements) { while(selectedElements.Count > 0) { IDrawableContainer element = selectedElements[0]; - element.Invalidate(); - element.HideGrippers(); element.Selected = false; selectedElements.Remove(element); + element.Invalidate(); FieldAggregator.UnbindElement(element); } if (_movingElementChanged != null) { @@ -1578,7 +1641,6 @@ namespace Greenshot.Drawing { public void SelectElement(IDrawableContainer container) { if (!selectedElements.Contains(container)) { selectedElements.Add(container); - container.ShowGrippers(); container.Selected = true; FieldAggregator.BindElement(container); if (_movingElementChanged != null) { diff --git a/Greenshot/Drawing/TextContainer.cs b/Greenshot/Drawing/TextContainer.cs index 6c58e2683..1b369ea78 100644 --- a/Greenshot/Drawing/TextContainer.cs +++ b/Greenshot/Drawing/TextContainer.cs @@ -97,9 +97,13 @@ namespace Greenshot.Drawing { AddField(GetType(), FieldType.TEXT_HORIZONTAL_ALIGNMENT, StringAlignment.Center); AddField(GetType(), FieldType.TEXT_VERTICAL_ALIGNMENT, StringAlignment.Center); } - - [OnDeserialized] - private void OnDeserialized(StreamingContext context) { + + /// + /// Do some logic to make sure all field are initiated correctly + /// + /// StreamingContext + protected override void OnDeserialized(StreamingContext streamingContext) { + base.OnDeserialized(streamingContext); Init(); } diff --git a/Greenshot/Greenshot.csproj b/Greenshot/Greenshot.csproj index ee50aa76a..0fc6043a0 100644 --- a/Greenshot/Greenshot.csproj +++ b/Greenshot/Greenshot.csproj @@ -75,7 +75,12 @@ + + + + + @@ -101,9 +106,6 @@ - - Component - diff --git a/Greenshot/Helpers/ScaleHelper.cs b/Greenshot/Helpers/ScaleHelper.cs index 7f348b932..2af56cb2f 100644 --- a/Greenshot/Helpers/ScaleHelper.cs +++ b/Greenshot/Helpers/ScaleHelper.cs @@ -116,15 +116,15 @@ namespace Greenshot.Helpers { return GetAlignedRectangle(newRect, targetRect, alignment); } - public static void RationalScale(ref RectangleF originalRectangle, int resizeHandlePosition, PointF resizeHandleCoords) { + public static void RationalScale(ref RectangleF originalRectangle, Positions resizeHandlePosition, PointF resizeHandleCoords) { Scale(ref originalRectangle, resizeHandlePosition, resizeHandleCoords, ScaleOptions.Rational); } - public static void CenteredScale(ref RectangleF originalRectangle, int resizeHandlePosition, PointF resizeHandleCoords) { + public static void CenteredScale(ref RectangleF originalRectangle, Positions resizeHandlePosition, PointF resizeHandleCoords) { Scale(ref originalRectangle, resizeHandlePosition, resizeHandleCoords, ScaleOptions.Centered); } - public static void Scale(ref RectangleF originalRectangle, int resizeHandlePosition, PointF resizeHandleCoords) { + public static void Scale(ref RectangleF originalRectangle, Positions resizeHandlePosition, PointF resizeHandleCoords) { Scale(ref originalRectangle, resizeHandlePosition, resizeHandleCoords, null); } @@ -135,7 +135,7 @@ namespace Greenshot.Helpers { /// position of the handle/gripper being used for resized, see constants in Gripper.cs, e.g. Gripper.POSITION_TOP_LEFT /// coordinates of the used handle/gripper /// ScaleOptions to use when scaling - public static void Scale(ref RectangleF originalRectangle, int resizeHandlePosition, PointF resizeHandleCoords, ScaleOptions? options) { + public static void Scale(ref RectangleF originalRectangle, Positions resizeHandlePosition, PointF resizeHandleCoords, ScaleOptions? options) { if(options == null) { options = GetScaleOptions(); } @@ -154,7 +154,7 @@ namespace Greenshot.Helpers { resizeHandleCoords.X -= 2 * (resizeHandleCoords.X - rectCenterX); resizeHandleCoords.Y -= 2 * (resizeHandleCoords.Y - rectCenterY); // scale again with opposing handle and mirrored coordinates - resizeHandlePosition = (resizeHandlePosition + 4) % 8; + resizeHandlePosition = (Positions)((((int)resizeHandlePosition) + 4) % 8); scale(ref originalRectangle, resizeHandlePosition, resizeHandleCoords); } else { scale(ref originalRectangle, resizeHandlePosition, resizeHandleCoords); @@ -168,47 +168,47 @@ namespace Greenshot.Helpers { /// bounds of the current rectangle, scaled values will be written to this reference /// position of the handle/gripper being used for resized, see constants in Gripper.cs, e.g. Gripper.POSITION_TOP_LEFT /// coordinates of the used handle/gripper - private static void scale(ref RectangleF originalRectangle, int resizeHandlePosition, PointF resizeHandleCoords) { + private static void scale(ref RectangleF originalRectangle, Positions resizeHandlePosition, PointF resizeHandleCoords) { switch(resizeHandlePosition) { - case Gripper.POSITION_TOP_LEFT: + 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 Gripper.POSITION_TOP_CENTER: + case Positions.TopCenter: originalRectangle.Height = originalRectangle.Top + originalRectangle.Height - resizeHandleCoords.Y; originalRectangle.Y = resizeHandleCoords.Y; break; - case Gripper.POSITION_TOP_RIGHT: + case Positions.TopRight: originalRectangle.Width = resizeHandleCoords.X - originalRectangle.Left; originalRectangle.Height = originalRectangle.Top + originalRectangle.Height - resizeHandleCoords.Y; originalRectangle.Y = resizeHandleCoords.Y; break; - case Gripper.POSITION_MIDDLE_LEFT: + case Positions.MiddleLeft: originalRectangle.Width = originalRectangle.Left + originalRectangle.Width - resizeHandleCoords.X; originalRectangle.X = resizeHandleCoords.X; break; - case Gripper.POSITION_MIDDLE_RIGHT: + case Positions.MiddleRight: originalRectangle.Width = resizeHandleCoords.X - originalRectangle.Left; break; - case Gripper.POSITION_BOTTOM_LEFT: + case Positions.BottomLeft: originalRectangle.Width = originalRectangle.Left + originalRectangle.Width - resizeHandleCoords.X; originalRectangle.Height = resizeHandleCoords.Y - originalRectangle.Top; originalRectangle.X = resizeHandleCoords.X; break; - case Gripper.POSITION_BOTTOM_CENTER: + case Positions.BottomCenter: originalRectangle.Height = resizeHandleCoords.Y - originalRectangle.Top; break; - case Gripper.POSITION_BOTTOM_RIGHT: + case Positions.BottomRight: originalRectangle.Width = resizeHandleCoords.X - originalRectangle.Left; originalRectangle.Height = resizeHandleCoords.Y - originalRectangle.Top; break; @@ -218,19 +218,19 @@ namespace Greenshot.Helpers { } } - + /// /// Adjusts resizeHandleCoords so that aspect ratio is kept after resizing a given rectangle with provided arguments /// /// bounds of the current rectangle - /// position of the handle/gripper being used for resized, see constants in Gripper.cs, e.g. Gripper.POSITION_TOP_LEFT + /// position of the handle/gripper being used for resized, see Position /// coordinates of the used handle/gripper, adjusted coordinates will be written to this reference - private static void adjustCoordsForRationalScale(RectangleF originalRectangle, int resizeHandlePosition, ref PointF resizeHandleCoords) { + private static void adjustCoordsForRationalScale(RectangleF originalRectangle, Positions resizeHandlePosition, ref PointF resizeHandleCoords) { float originalRatio = originalRectangle.Width / originalRectangle.Height; float newWidth, newHeight, newRatio; switch(resizeHandlePosition) { - case Gripper.POSITION_TOP_LEFT: + case Positions.TopLeft: newWidth = originalRectangle.Right - resizeHandleCoords.X; newHeight = originalRectangle.Bottom - resizeHandleCoords.Y; newRatio = newWidth / newHeight; @@ -241,7 +241,7 @@ namespace Greenshot.Helpers { } break; - case Gripper.POSITION_TOP_RIGHT: + case Positions.TopRight: newWidth = resizeHandleCoords.X - originalRectangle.Left; newHeight = originalRectangle.Bottom - resizeHandleCoords.Y; newRatio = newWidth / newHeight; @@ -252,7 +252,7 @@ namespace Greenshot.Helpers { } break; - case Gripper.POSITION_BOTTOM_LEFT: + case Positions.BottomLeft: newWidth = originalRectangle.Right - resizeHandleCoords.X; newHeight = resizeHandleCoords.Y - originalRectangle.Top; newRatio = newWidth / newHeight; @@ -263,7 +263,7 @@ namespace Greenshot.Helpers { } break; - case Gripper.POSITION_BOTTOM_RIGHT: + case Positions.BottomRight: newWidth = resizeHandleCoords.X - originalRectangle.Left; newHeight = resizeHandleCoords.Y - originalRectangle.Top; newRatio = newWidth / newHeight; @@ -282,10 +282,10 @@ namespace Greenshot.Helpers { public static void Scale(Rectangle boundsBeforeResize, int cursorX, int cursorY, ref RectangleF boundsAfterResize, IDoubleProcessor angleRoundBehavior) { - Scale(boundsBeforeResize, Gripper.POSITION_TOP_LEFT, cursorX, cursorY, ref boundsAfterResize, angleRoundBehavior); + Scale(boundsBeforeResize, Positions.TopLeft, cursorX, cursorY, ref boundsAfterResize, angleRoundBehavior); } - public static void Scale(Rectangle boundsBeforeResize, int gripperPosition, int cursorX, int cursorY, ref RectangleF boundsAfterResize, IDoubleProcessor angleRoundBehavior) { + public static void Scale(Rectangle boundsBeforeResize, Positions gripperPosition, int cursorX, int cursorY, ref RectangleF boundsAfterResize, IDoubleProcessor angleRoundBehavior) { ScaleOptions opts = GetScaleOptions(); diff --git a/GreenshotPlugin/GreenshotPlugin.csproj b/GreenshotPlugin/GreenshotPlugin.csproj index f29b3b79b..8bc85a253 100644 --- a/GreenshotPlugin/GreenshotPlugin.csproj +++ b/GreenshotPlugin/GreenshotPlugin.csproj @@ -67,6 +67,7 @@ + diff --git a/GreenshotPlugin/Interfaces/Drawing/Adorners/IAdorner.cs b/GreenshotPlugin/Interfaces/Drawing/Adorners/IAdorner.cs new file mode 100644 index 000000000..27f825de4 --- /dev/null +++ b/GreenshotPlugin/Interfaces/Drawing/Adorners/IAdorner.cs @@ -0,0 +1,91 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2015 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 . + */ + +using System.Drawing; +using System.Drawing.Drawing2D; +using System.Windows.Forms; + +namespace Greenshot.Plugin.Drawing.Adorners +{ + public interface IAdorner + { + /// + /// Returns if this adorner is active + /// + bool IsActive { get; } + + /// + /// The current edit status, this is needed to locate the adorner to send events to + /// + EditStatus EditStatus { get; } + + /// + /// The owner of this adorner + /// + IDrawableContainer Owner { get; } + + /// + /// Is the current point "over" the Adorner? + /// If this is the case, the + /// + /// Point to test + /// true if so + bool HitTest(Point point); + + /// + /// Handle the MouseDown event + /// + /// + /// MouseEventArgs + void MouseDown(object sender, MouseEventArgs mouseEventArgs); + + /// + /// Handle the MouseUp event + /// + /// + /// MouseEventArgs + void MouseUp(object sender, MouseEventArgs mouseEventArgs); + + /// + /// Handle the MouseMove event + /// + /// + /// MouseEventArgs + void MouseMove(object sender, MouseEventArgs mouseEventArgs); + + /// + /// Gets the cursor that should be displayed for this behavior. + /// + Cursor Cursor { get; } + + /// + /// Draw the adorner + /// + /// PaintEventArgs + void Paint(PaintEventArgs paintEventArgs); + + /// + /// Called if the owner is transformed + /// + /// Matrix + void Transform(Matrix matrix); + } +} diff --git a/GreenshotPlugin/Interfaces/Drawing/Container.cs b/GreenshotPlugin/Interfaces/Drawing/Container.cs index 7be49e852..afae80489 100644 --- a/GreenshotPlugin/Interfaces/Drawing/Container.cs +++ b/GreenshotPlugin/Interfaces/Drawing/Container.cs @@ -24,6 +24,9 @@ using System.Drawing.Drawing2D; using System.Drawing.Imaging; using System.Windows.Forms; using System.ComponentModel; +using System.Collections.Generic; +using Greenshot.Plugin.Drawing.Adorners; +using System.Runtime.Serialization; namespace Greenshot.Plugin.Drawing { public enum RenderMode {EDIT, EXPORT}; @@ -73,7 +76,9 @@ namespace Greenshot.Plugin.Drawing { Rectangle DrawingBounds { get; } - + + void ApplyBounds(RectangleF newBounds); + bool hasFilters { get; } @@ -85,8 +90,6 @@ namespace Greenshot.Plugin.Drawing { void AlignToParent(HorizontalAlignment horizontalAlignment, VerticalAlignment verticalAlignment); void Invalidate(); bool ClickableAt(int x, int y); - void HideGrippers(); - void ShowGrippers(); void MoveBy(int x, int y); void Transform(Matrix matrix); bool HandleMouseDown(int x, int y); @@ -97,6 +100,11 @@ namespace Greenshot.Plugin.Drawing { EditStatus DefaultEditMode { get; } + + /// + /// Available adorners for the DrawableContainer + /// + IList Adorners { get; } } public interface ITextContainer: IDrawableContainer { diff --git a/GreenshotPlugin/Interfaces/Generic.cs b/GreenshotPlugin/Interfaces/Generic.cs index 96283027a..29a66597f 100644 --- a/GreenshotPlugin/Interfaces/Generic.cs +++ b/GreenshotPlugin/Interfaces/Generic.cs @@ -197,5 +197,7 @@ namespace Greenshot.Plugin { get; set; } + int Width { get; } + int Height { get; } } } \ No newline at end of file