mirror of
https://github.com/greenshot/greenshot
synced 2025-07-16 10:03:44 -07:00
Merge remote-tracking branch 'remotes/origin/feature/BUG-1887_removed_grippers'
# Conflicts: # Greenshot/Drawing/DrawableContainer.cs # Greenshot/Drawing/SpeechbubbleContainer.cs
This commit is contained in:
commit
7872f2f7e4
30 changed files with 1016 additions and 419 deletions
147
Greenshot/Drawing/Adorners/AbstractAdorner.cs
Normal file
147
Greenshot/Drawing/Adorners/AbstractAdorner.cs
Normal file
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/// <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>
|
||||
/// 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>
|
||||
/// 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)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
165
Greenshot/Drawing/Adorners/MoveAdorner.cs
Normal file
165
Greenshot/Drawing/Adorners/MoveAdorner.cs
Normal file
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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
|
||||
{
|
||||
/// <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
|
||||
{
|
||||
get
|
||||
{
|
||||
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;
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
186
Greenshot/Drawing/Adorners/ResizeAdorner.cs
Normal file
186
Greenshot/Drawing/Adorners/ResizeAdorner.cs
Normal file
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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
|
||||
{
|
||||
/// <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;
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
119
Greenshot/Drawing/Adorners/TargetAdorner.cs
Normal file
119
Greenshot/Drawing/Adorners/TargetAdorner.cs
Normal file
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
using Greenshot.Plugin.Drawing;
|
||||
using System.Drawing;
|
||||
using System.Drawing.Drawing2D;
|
||||
using System.Windows.Forms;
|
||||
|
||||
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 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();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Draw the adorner
|
||||
/// </summary>
|
||||
/// <param name="paintEventArgs">PaintEventArgs</param>
|
||||
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);
|
||||
}
|
||||
|
||||
/// <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];
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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 {
|
|||
/// </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, FieldType.Flag.CONFIRMABLE);
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
/// <summary>
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// 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<EditorConfiguration>();
|
||||
private bool _isMadeUndoable;
|
||||
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 {
|
||||
|
@ -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 {
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// List of available Adorners
|
||||
/// </summary>
|
||||
[NonSerialized]
|
||||
private IList<IAdorner> _adorners = new List<IAdorner>();
|
||||
public IList<IAdorner> 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();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Move the TargetGripper around, confined to the surface to solve BUG-1682
|
||||
/// </summary>
|
||||
/// <param name="newX"></param>
|
||||
/// <param name="newY"></param>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initialize a target gripper
|
||||
/// </summary>
|
||||
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};
|
||||
/// <summary>
|
||||
/// Create the default adorners for a rectangle based container
|
||||
/// </summary>
|
||||
|
||||
_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();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -604,10 +429,8 @@ namespace Greenshot.Drawing {
|
|||
}
|
||||
|
||||
public void MoveBy(int dx, int dy) {
|
||||
SuspendLayout();
|
||||
Left += dx;
|
||||
Top += dy;
|
||||
ResumeLayout();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -630,7 +453,6 @@ namespace Greenshot.Drawing {
|
|||
/// <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();
|
||||
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() {
|
||||
|
|
|
@ -171,26 +171,6 @@ namespace Greenshot.Drawing {
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Hides the grippers of all elements in the list.
|
||||
/// </summary>
|
||||
public void HideGrippers() {
|
||||
foreach(var dc in this) {
|
||||
dc.HideGrippers();
|
||||
dc.Invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Shows the grippers of all elements in the list.
|
||||
/// </summary>
|
||||
public void ShowGrippers() {
|
||||
foreach(var dc in this) {
|
||||
dc.ShowGrippers();
|
||||
dc.Invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether on of the elements is clickable at the given location
|
||||
/// </summary>
|
||||
|
|
|
@ -33,6 +33,7 @@ namespace Greenshot.Drawing {
|
|||
[Serializable()]
|
||||
public class EllipseContainer : DrawableContainer {
|
||||
public EllipseContainer(Surface parent) : base(parent) {
|
||||
CreateDefaultAdorners();
|
||||
}
|
||||
|
||||
protected override void InitializeFields() {
|
||||
|
|
|
@ -53,7 +53,7 @@ namespace Greenshot.Drawing.Fields {
|
|||
private readonly List<Field> fields = new List<Field>();
|
||||
|
||||
[OnDeserialized]
|
||||
private void OnDeserialized(StreamingContext context) {
|
||||
private void OnFieldHolderDeserialized(StreamingContext context) {
|
||||
fieldsByType = new Dictionary<FieldType, Field>();
|
||||
// listen to changing properties
|
||||
foreach(Field field in fields) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 {
|
||||
/// <summary>
|
||||
|
@ -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() {
|
||||
|
|
|
@ -47,7 +47,6 @@ namespace Greenshot.Drawing {
|
|||
/// Constructor
|
||||
/// </summary>
|
||||
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();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This is overriden to prevent the grippers to be modified.
|
||||
/// Might not be the best way...
|
||||
/// </summary>
|
||||
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) {
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -24,6 +24,7 @@ using System.IO;
|
|||
using Greenshot.Plugin.Drawing;
|
||||
using System.Drawing.Drawing2D;
|
||||
using log4net;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace Greenshot.Drawing {
|
||||
/// <summary>
|
||||
|
@ -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) {
|
||||
|
|
|
@ -27,6 +27,7 @@ using GreenshotPlugin.Core;
|
|||
using System.Drawing.Drawing2D;
|
||||
using Greenshot.Core;
|
||||
using log4net;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace Greenshot.Drawing {
|
||||
/// <summary>
|
||||
|
@ -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() {
|
||||
|
|
|
@ -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 {
|
||||
/// <summary>
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -19,38 +19,20 @@
|
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
using System.Drawing;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace Greenshot.Drawing {
|
||||
namespace Greenshot.Drawing
|
||||
{
|
||||
/// <summary>
|
||||
/// Grippers are the dragable edges of our containers
|
||||
/// Position
|
||||
/// </summary>
|
||||
public class Gripper : Label {
|
||||
/// <summary>
|
||||
/// Constants for anchor/gripper position:
|
||||
/// 0 1 2
|
||||
/// 7 3
|
||||
/// 6 5 4
|
||||
/// </summary>
|
||||
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
|
||||
}
|
||||
}
|
|
@ -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 {
|
||||
/// <summary>
|
||||
|
@ -34,6 +35,22 @@ namespace Greenshot.Drawing {
|
|||
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() {
|
||||
|
|
|
@ -28,7 +28,8 @@ using System.Drawing.Drawing2D;
|
|||
using System.Drawing.Text;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace Greenshot.Drawing {
|
||||
namespace Greenshot.Drawing
|
||||
{
|
||||
/// <summary>
|
||||
/// Description of SpeechbubbleContainer.
|
||||
/// </summary>
|
||||
|
@ -56,8 +57,8 @@ namespace Greenshot.Drawing {
|
|||
/// Restore the target gripper
|
||||
/// </summary>
|
||||
/// <param name="context"></param>
|
||||
[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);
|
||||
|
|
|
@ -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
|
||||
/// </summary>
|
||||
/// <param name="context"></param>
|
||||
[OnDeserialized]
|
||||
private void SetValuesOnDeserialized(StreamingContext context) {
|
||||
protected override void OnDeserialized(StreamingContext context)
|
||||
{
|
||||
Init();
|
||||
_stringFormat = new StringFormat();
|
||||
_stringFormat.Alignment = StringAlignment.Center;
|
||||
_stringFormat.LineAlignment = StringAlignment.Center;
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Description of Surface.
|
||||
|
@ -991,12 +993,46 @@ namespace Greenshot.Drawing {
|
|||
Invalidate();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check if an adorner was "hit", and change the cursor if so
|
||||
/// </summary>
|
||||
/// <param name="mouseEventArgs">MouseEventArgs</param>
|
||||
/// <returns>IAdorner</returns>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This event handler is called when someone presses the mouse on a surface.
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
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 {
|
|||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
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 {
|
|||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// This is the event handler for the Paint Event, try to draw as little as possible!
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
void SurfacePaint(object sender, PaintEventArgs e) {
|
||||
Graphics targetGraphics = e.Graphics;
|
||||
Rectangle clipRectangle = e.ClipRectangle;
|
||||
/// <param name="paintEventArgs">PaintEventArgs</param>
|
||||
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 {
|
|||
/// </summary>
|
||||
/// <param name="container"></param>
|
||||
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) {
|
||||
|
|
|
@ -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) {
|
||||
|
||||
/// <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();
|
||||
}
|
||||
|
||||
|
|
|
@ -75,7 +75,12 @@
|
|||
<Compile Include="Destinations\FileWithDialogDestination.cs" />
|
||||
<Compile Include="Destinations\PickerDestination.cs" />
|
||||
<Compile Include="Destinations\PrinterDestination.cs" />
|
||||
<Compile Include="Drawing\Adorners\AbstractAdorner.cs" />
|
||||
<Compile Include="Drawing\Adorners\MoveAdorner.cs" />
|
||||
<Compile Include="Drawing\Adorners\ResizeAdorner.cs" />
|
||||
<Compile Include="Drawing\Adorners\TargetAdorner.cs" />
|
||||
<Compile Include="Drawing\ArrowContainer.cs" />
|
||||
<Compile Include="Drawing\Positions.cs" />
|
||||
<Compile Include="Drawing\StepLabelContainer.cs" />
|
||||
<Compile Include="Drawing\ImageContainer.cs" />
|
||||
<Compile Include="Drawing\CropContainer.cs" />
|
||||
|
@ -101,9 +106,6 @@
|
|||
<Compile Include="Drawing\Filters\MagnifierFilter.cs" />
|
||||
<Compile Include="Drawing\Filters\PixelizationFilter.cs" />
|
||||
<Compile Include="Drawing\Filters\BlurFilter.cs" />
|
||||
<Compile Include="Drawing\Gripper.cs">
|
||||
<SubType>Component</SubType>
|
||||
</Compile>
|
||||
<Compile Include="Drawing\HighlightContainer.cs" />
|
||||
<Compile Include="Drawing\IconContainer.cs" />
|
||||
<Compile Include="Drawing\LineContainer.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 {
|
|||
/// <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, 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 {
|
|||
/// <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, 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 {
|
|||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Adjusts resizeHandleCoords so that aspect ratio is kept after resizing a given rectangle with provided arguments
|
||||
/// </summary>
|
||||
/// <param name="originalRectangle">bounds of the current rectangle</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="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, 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();
|
||||
|
||||
|
|
|
@ -67,6 +67,7 @@
|
|||
<Compile Include="IEInterop\IHTMLWindow3.cs" />
|
||||
<Compile Include="IEInterop\IHTMLWindow4.cs" />
|
||||
<Compile Include="IEInterop\IWebBrowser2.cs" />
|
||||
<Compile Include="Interfaces\Drawing\Adorners\IAdorner.cs" />
|
||||
<Compile Include="Interop\Base.cs" />
|
||||
<Compile Include="Interop\ComProgIdAttribute.cs" />
|
||||
<Compile Include="Interop\COMWrapper.cs" />
|
||||
|
|
91
GreenshotPlugin/Interfaces/Drawing/Adorners/IAdorner.cs
Normal file
91
GreenshotPlugin/Interfaces/Drawing/Adorners/IAdorner.cs
Normal file
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
using System.Drawing;
|
||||
using System.Drawing.Drawing2D;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace Greenshot.Plugin.Drawing.Adorners
|
||||
{
|
||||
public interface IAdorner
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns if this adorner is active
|
||||
/// </summary>
|
||||
bool IsActive { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The current edit status, this is needed to locate the adorner to send events to
|
||||
/// </summary>
|
||||
EditStatus EditStatus { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The owner of this adorner
|
||||
/// </summary>
|
||||
IDrawableContainer Owner { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Is the current point "over" the Adorner?
|
||||
/// If this is the case, the
|
||||
/// </summary>
|
||||
/// <param name="point">Point to test</param>
|
||||
/// <returns>true if so</returns>
|
||||
bool HitTest(Point point);
|
||||
|
||||
/// <summary>
|
||||
/// Handle the MouseDown event
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="mouseEventArgs">MouseEventArgs</param>
|
||||
void MouseDown(object sender, MouseEventArgs mouseEventArgs);
|
||||
|
||||
/// <summary>
|
||||
/// Handle the MouseUp event
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="mouseEventArgs">MouseEventArgs</param>
|
||||
void MouseUp(object sender, MouseEventArgs mouseEventArgs);
|
||||
|
||||
/// <summary>
|
||||
/// Handle the MouseMove event
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="mouseEventArgs">MouseEventArgs</param>
|
||||
void MouseMove(object sender, MouseEventArgs mouseEventArgs);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the cursor that should be displayed for this behavior.
|
||||
/// </summary>
|
||||
Cursor Cursor { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Draw the adorner
|
||||
/// </summary>
|
||||
/// <param name="paintEventArgs">PaintEventArgs</param>
|
||||
void Paint(PaintEventArgs paintEventArgs);
|
||||
|
||||
/// <summary>
|
||||
/// Called if the owner is transformed
|
||||
/// </summary>
|
||||
/// <param name="matrix">Matrix</param>
|
||||
void Transform(Matrix matrix);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Available adorners for the DrawableContainer
|
||||
/// </summary>
|
||||
IList<IAdorner> Adorners { get; }
|
||||
}
|
||||
|
||||
public interface ITextContainer: IDrawableContainer {
|
||||
|
|
|
@ -197,5 +197,7 @@ namespace Greenshot.Plugin {
|
|||
get;
|
||||
set;
|
||||
}
|
||||
int Width { get; }
|
||||
int Height { get; }
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue