Make sure that CropContainer is not placed upon the Undo / Redo stack, as this causes all kinds of issues.

This commit is contained in:
Robin Krom 2022-03-04 14:54:16 +01:00
commit 747a51e512
No known key found for this signature in database
GPG key ID: BCC01364F1371490
10 changed files with 106 additions and 54 deletions

View file

@ -78,6 +78,11 @@ namespace Greenshot.Base.Interfaces.Drawing
bool InitContent();
/// <summary>
/// Defines if the drawable container participates in undo / redo
/// </summary>
bool IsUndoable { get; }
void MakeBoundsChangeUndoable(bool allowMerge);
EditStatus DefaultEditMode { get; }

View file

@ -21,6 +21,9 @@
namespace Greenshot.Base.Interfaces.Drawing
{
/// <summary>
/// The IFieldAggregator defines the connections between fields and containers
/// </summary>
public interface IFieldAggregator
{
void UnbindElement(IDrawableContainer dc);

View file

@ -201,8 +201,16 @@ namespace Greenshot.Base.Interfaces
/// <param name="rc">A rectangle in the coordinate space of the surface.</param>
Rectangle ToImageCoordinates(Rectangle rc);
/// <summary>
/// Make it possible to undo the specified IMemento
/// </summary>
/// <param name="memento">IMemento</param>
/// <param name="allowMerge">bool to specify if the action can be merged, e.g. we do not want an undo for every part of a resize</param>
void MakeUndoable(IMemento memento, bool allowMerge);
/// <summary>
/// The IFieldAggregator
/// </summary>
IFieldAggregator FieldAggregator { get; }
/// <summary>

View file

@ -36,9 +36,9 @@ namespace Greenshot.Editor.Drawing
public class CropContainer : DrawableContainer
{
/// <summary>
/// awailable modes
/// Available Crop modes
/// </summary>
public enum CropMode
public enum CropModes
{
/// <summary>
/// crop all outside the selection rectangle
@ -73,12 +73,12 @@ namespace Greenshot.Editor.Drawing
{
switch (GetFieldValue(FieldType.CROPMODE))
{
case CropMode.Horizontal:
case CropModes.Horizontal:
{
InitHorizontalCropOutStyle();
break;
}
case CropMode.Vertical:
case CropModes.Vertical:
{
InitVerticalCropOutStyle();
break;
@ -119,6 +119,7 @@ namespace Greenshot.Editor.Drawing
Adorners.Add(new ResizeAdorner(this, Positions.TopCenter));
Adorners.Add(new ResizeAdorner(this, Positions.BottomCenter));
}
private void CreateLeftRightAdorners()
{
Adorners.Add(new ResizeAdorner(this, Positions.MiddleLeft));
@ -128,7 +129,7 @@ namespace Greenshot.Editor.Drawing
protected override void InitializeFields()
{
AddField(GetType(), FieldType.FLAGS, FieldFlag.CONFIRMABLE);
AddField(GetType(), FieldType.CROPMODE, CropMode.Default);
AddField(GetType(), FieldType.CROPMODE, CropModes.Default);
}
public override void Invalidate()
@ -170,8 +171,8 @@ namespace Greenshot.Editor.Drawing
switch (GetFieldValue(FieldType.CROPMODE))
{
case CropMode.Horizontal:
case CropMode.Vertical:
case CropModes.Horizontal:
case CropModes.Vertical:
{
//draw inside
g.FillRectangle(cropBrush, cropRectangle);
@ -205,9 +206,9 @@ namespace Greenshot.Editor.Drawing
return GetFieldValue(FieldType.CROPMODE) switch
{
//force horizontal crop to left edge
CropMode.Horizontal => base.HandleMouseDown(0, y),
CropModes.Horizontal => base.HandleMouseDown(0, y),
//force vertical crop to top edge
CropMode.Vertical => base.HandleMouseDown(x, 0),
CropModes.Vertical => base.HandleMouseDown(x, 0),
_ => base.HandleMouseDown(x, y),
};
}
@ -218,7 +219,7 @@ namespace Greenshot.Editor.Drawing
switch (GetFieldValue(FieldType.CROPMODE))
{
case CropMode.Horizontal:
case CropModes.Horizontal:
{
//stick on left and right
//allow only horizontal changes
@ -231,7 +232,7 @@ namespace Greenshot.Editor.Drawing
}
break;
}
case CropMode.Vertical:
case CropModes.Vertical:
{
//stick on top and bottom
//allow only vertical changes
@ -263,5 +264,9 @@ namespace Greenshot.Editor.Drawing
Invalidate();
return true;
}
/// <inheritdoc cref="IDrawableContainer"/>
/// Make sure this container is not undoable
public override bool IsUndoable => false;
}
}

View file

@ -472,13 +472,20 @@ namespace Greenshot.Editor.Drawing
g.DrawRectangle(pen, rect);
}
/// <inheritdoc cref="IDrawableContainer"/>
public virtual bool IsUndoable => true;
/// <summary>
/// Make a following bounds change on this drawablecontainer undoable!
/// </summary>
/// <param name="allowMerge">true means allow the moves to be merged</param>
public void MakeBoundsChangeUndoable(bool allowMerge)
public virtual void MakeBoundsChangeUndoable(bool allowMerge)
{
_parent.MakeUndoable(new DrawableContainerBoundsChangeMemento(this), allowMerge);
if (!IsUndoable)
{
return;
}
_parent?.MakeUndoable(new DrawableContainerBoundsChangeMemento(this), allowMerge);
}
public void MoveBy(int dx, int dy)
@ -552,12 +559,11 @@ namespace Greenshot.Editor.Drawing
protected void OnPropertyChanged(string propertyName)
{
if (_propertyChanged != null)
{
if (_propertyChanged == null) return;
_propertyChanged(this, new PropertyChangedEventArgs(propertyName));
Invalidate();
}
}
/// <summary>
/// This method will be called before a field is changes.
@ -566,8 +572,11 @@ namespace Greenshot.Editor.Drawing
/// <param name="fieldToBeChanged">The field to be changed</param>
/// <param name="newValue">The new value</param>
public virtual void BeforeFieldChange(IField fieldToBeChanged, object newValue)
{
if (IsUndoable)
{
_parent?.MakeUndoable(new ChangeFieldHolderMemento(this, fieldToBeChanged), true);
}
Invalidate();
}

View file

@ -24,6 +24,7 @@ using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Linq;
using System.Threading;
using System.Windows.Forms;
using Greenshot.Base.Core;
@ -50,6 +51,11 @@ namespace Greenshot.Editor.Drawing
{
}
public DrawableContainerList(IEnumerable<IDrawableContainer> elements)
{
AddRange(elements);
}
public DrawableContainerList(Guid parentId)
{
ParentID = parentId;
@ -135,12 +141,16 @@ namespace Greenshot.Editor.Drawing
/// <param name="allowMerge">true means allow the moves to be merged</param>
public void MakeBoundsChangeUndoable(bool allowMerge)
{
if (Count > 0 && Parent != null)
if (Count <= 0 || Parent == null) return;
// Take all containers to make undoable
var containersToClone = this.Where(c => c.IsUndoable).ToList();
if (!containersToClone.Any())
{
var clone = new DrawableContainerList();
clone.AddRange(this);
Parent.MakeUndoable(new DrawableContainerBoundsChangeMemento(clone), allowMerge);
return;
}
var clone = new DrawableContainerList();
clone.AddRange(containersToClone);
Parent.MakeUndoable(new DrawableContainerBoundsChangeMemento(clone), allowMerge);
}
/// <summary>

View file

@ -26,6 +26,7 @@ using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using System.Runtime.Serialization.Formatters.Binary;
using System.Windows.Forms;
using Greenshot.Base.Controls;
@ -981,7 +982,7 @@ namespace Greenshot.Editor.Drawing
cropRectangle = ImageHelper.FindAutoCropRectangle(tmpImage, conf.AutoCropDifference);
}
if (!IsCropPossible(ref cropRectangle, CropContainer.CropMode.AutoCrop))
if (!IsCropPossible(ref cropRectangle, CropContainer.CropModes.AutoCrop))
{
return false;
}
@ -1063,7 +1064,7 @@ namespace Greenshot.Editor.Drawing
/// <param name="cropRectangle">Rectangle adapted to the dimensions of the image</param>
/// <param name="cropMode"></param>
/// <returns>true if this is possible</returns>
public bool IsCropPossible(ref Rectangle cropRectangle, CropContainer.CropMode cropMode)
public bool IsCropPossible(ref Rectangle cropRectangle, CropContainer.CropModes cropMode)
{
cropRectangle = GuiRectangle.GetGuiRectangle(cropRectangle.Left, cropRectangle.Top, cropRectangle.Width, cropRectangle.Height);
//Fitting the rectangle to the dimensions of the image
@ -1088,14 +1089,14 @@ namespace Greenshot.Editor.Drawing
}
// special condition for vertical
if(cropMode == CropContainer.CropMode.Vertical && cropRectangle.Width == Image.Width)
if(cropMode == CropContainer.CropModes.Vertical && cropRectangle.Width == Image.Width)
{
//crop out the hole imgage is not allowed
return false;
}
// special condition for vertical
if (cropMode == CropContainer.CropMode.Horizontal && cropRectangle.Height == Image.Height)
if (cropMode == CropContainer.CropModes.Horizontal && cropRectangle.Height == Image.Height)
{
//crop out the hole imgage is not allowed
return false;
@ -1205,7 +1206,7 @@ namespace Greenshot.Editor.Drawing
/// <returns></returns>
public bool ApplyCrop(Rectangle cropRectangle)
{
if (!IsCropPossible(ref cropRectangle, CropContainer.CropMode.Default)) return false;
if (!IsCropPossible(ref cropRectangle, CropContainer.CropModes.Default)) return false;
Rectangle imageRectangle = new Rectangle(Point.Empty, Image.Size);
Bitmap tmpImage;
@ -1248,7 +1249,7 @@ namespace Greenshot.Editor.Drawing
/// <returns></returns>
public bool ApplyHorizontalCrop(Rectangle cropRectangle)
{
if (!IsCropPossible(ref cropRectangle, CropContainer.CropMode.Horizontal)) return false;
if (!IsCropPossible(ref cropRectangle, CropContainer.CropModes.Horizontal)) return false;
var imageRectangle = new Rectangle(Point.Empty, Image.Size);
var topRectangle = new Rectangle(0, 0, Image.Size.Width, cropRectangle.Top);
@ -1315,7 +1316,7 @@ namespace Greenshot.Editor.Drawing
/// <returns></returns>
public bool ApplyVerticalCrop(Rectangle cropRectangle)
{
if (!IsCropPossible(ref cropRectangle, CropContainer.CropMode.Vertical)) return false;
if (!IsCropPossible(ref cropRectangle, CropContainer.CropModes.Vertical)) return false;
var imageRectangle = new Rectangle(Point.Empty, Image.Size);
var leftRectangle = new Rectangle(0, 0, cropRectangle.Left, Image.Size.Height);
@ -1943,7 +1944,7 @@ namespace Greenshot.Editor.Drawing
element.Invalidate();
}
if (makeUndoable)
if (makeUndoable && element.IsUndoable)
{
MakeUndoable(new AddElementMemento(this, element), false);
}
@ -1959,11 +1960,17 @@ namespace Greenshot.Editor.Drawing
public void RemoveElements(IDrawableContainerList elementsToRemove, bool makeUndoable = true)
{
// fix potential issues with iterating a changing list
DrawableContainerList cloned = new DrawableContainerList();
cloned.AddRange(elementsToRemove);
DrawableContainerList cloned = new DrawableContainerList(elementsToRemove);
if (makeUndoable)
{
MakeUndoable(new DeleteElementsMemento(this, cloned), false);
// Take all containers to make undoable
var undoableContainers = elementsToRemove.Where(c => c.IsUndoable).ToList();
if (undoableContainers.Any())
{
var undoableContainerList = new DrawableContainerList(undoableContainers);
MakeUndoable(new DeleteElementsMemento(this, undoableContainerList), false);
}
}
SuspendLayout();
@ -2011,7 +2018,7 @@ namespace Greenshot.Editor.Drawing
Invalidate();
}
if (makeUndoable)
if (makeUndoable && elementToRemove.IsUndoable)
{
MakeUndoable(new DeleteElementMemento(this, elementToRemove), false);
}
@ -2027,11 +2034,16 @@ namespace Greenshot.Editor.Drawing
public void AddElements(IDrawableContainerList elementsToAdd, bool makeUndoable = true)
{
// fix potential issues with iterating a changing list
DrawableContainerList cloned = new DrawableContainerList();
cloned.AddRange(elementsToAdd);
DrawableContainerList cloned = new DrawableContainerList(elementsToAdd);
if (makeUndoable)
{
MakeUndoable(new AddElementsMemento(this, cloned), false);
// Take all containers to make undoable
var undoableContainers = elementsToAdd.Where(c => c.IsUndoable).ToList();
if (undoableContainers.Any())
{
var undoableContainerList = new DrawableContainerList(undoableContainers);
MakeUndoable(new AddElementsMemento(this, undoableContainerList), false);
}
}
SuspendLayout();
@ -2113,12 +2125,12 @@ namespace Greenshot.Editor.Drawing
{
switch (e.GetFieldValue(FieldType.CROPMODE))
{
case CropContainer.CropMode.Horizontal:
case CropContainer.CropModes.Horizontal:
{
ApplyHorizontalCrop(_cropContainer.Bounds);
break;
}
case CropContainer.CropMode.Vertical:
case CropContainer.CropModes.Vertical:
{
ApplyVerticalCrop(_cropContainer.Bounds);
break;

View file

@ -74,7 +74,7 @@ namespace Greenshot.Editor.Drawing
{
if ((text != null || newText == null) && string.Equals(text, newText)) return;
if (makeUndoable && allowUndoable)
if (makeUndoable && allowUndoable && IsUndoable)
{
makeUndoable = false;
_parent.MakeUndoable(new TextChangeMemento(this), false);

View file

@ -1118,8 +1118,8 @@ namespace Greenshot.Editor.Forms {
//TODO translate
this.cropModeButton.LanguageKey = "editor_crop_mode";
this.cropModeButton.Name = "cropModeButton";
this.cropModeButton.SelectedTag = CropContainer.CropMode.Default;
this.cropModeButton.Tag = CropContainer.CropMode.Default;
this.cropModeButton.SelectedTag = CropContainer.CropModes.Default;
this.cropModeButton.Tag = CropContainer.CropModes.Default;
//
// defaultCropStyleToolStripMenuItem
@ -1127,7 +1127,7 @@ namespace Greenshot.Editor.Forms {
this.defaultCropModeToolStripMenuItem.Image = ((System.Drawing.Image)(resources.GetObject("btnCrop.Image")));
this.defaultCropModeToolStripMenuItem.LanguageKey = "editor_cropmode_default";
this.defaultCropModeToolStripMenuItem.Name = "defaultCropModeToolStripMenuItem";
this.defaultCropModeToolStripMenuItem.Tag = CropContainer.CropMode.Default;
this.defaultCropModeToolStripMenuItem.Tag = CropContainer.CropModes.Default;
//
// verticalCropStyleToolStripMenuItem
@ -1135,7 +1135,7 @@ namespace Greenshot.Editor.Forms {
this.verticalCropModeToolStripMenuItem.Image = ((System.Drawing.Image)(resources.GetObject("CropVertical.Image")));
this.verticalCropModeToolStripMenuItem.LanguageKey = "editor_cropmode_vertical";
this.verticalCropModeToolStripMenuItem.Name = "verticalCropModeToolStripMenuItem";
this.verticalCropModeToolStripMenuItem.Tag = CropContainer.CropMode.Vertical;
this.verticalCropModeToolStripMenuItem.Tag = CropContainer.CropModes.Vertical;
//
// horizontalCropStyleToolStripMenuItem
@ -1143,7 +1143,7 @@ namespace Greenshot.Editor.Forms {
this.horizontalCropModeToolStripMenuItem.Image = ((System.Drawing.Image)(resources.GetObject("CropHorizontal.Image")));
this.horizontalCropModeToolStripMenuItem.LanguageKey = "editor_cropmode_horizontal";
this.horizontalCropModeToolStripMenuItem.Name = "horizontalCropModeToolStripMenuItem";
this.horizontalCropModeToolStripMenuItem.Tag = CropContainer.CropMode.Horizontal;
this.horizontalCropModeToolStripMenuItem.Tag = CropContainer.CropModes.Horizontal;
//
// horizontalCropStyleToolStripMenuItem
@ -1151,7 +1151,7 @@ namespace Greenshot.Editor.Forms {
this.autoCropModeToolStripMenuItem.Image = ((System.Drawing.Image)(resources.GetObject("AutoCrop.Image")));
this.autoCropModeToolStripMenuItem.LanguageKey = "editor_cropmode_auto";
this.autoCropModeToolStripMenuItem.Name = "autoCropModeToolStripMenuItem";
this.autoCropModeToolStripMenuItem.Tag = CropContainer.CropMode.AutoCrop;
this.autoCropModeToolStripMenuItem.Tag = CropContainer.CropModes.AutoCrop;
//
// highlightModeButton

View file

@ -731,7 +731,7 @@ namespace Greenshot.Editor.Forms
if (_surface.DrawingMode == DrawingModes.Crop) return;
_surface.DrawingMode = DrawingModes.Crop;
InitCropMode((CropContainer.CropMode)_surface.FieldAggregator.GetField(FieldType.CROPMODE).Value);
InitCropMode((CropContainer.CropModes)_surface.FieldAggregator.GetField(FieldType.CROPMODE).Value);
RefreshFieldControls();
}
@ -1590,25 +1590,25 @@ namespace Greenshot.Editor.Forms
protected void CropStyleDropDownItemClicked(object sender, ToolStripItemClickedEventArgs e)
{
InitCropMode((CropContainer.CropMode)e.ClickedItem.Tag);
InitCropMode((CropContainer.CropModes)e.ClickedItem.Tag);
RefreshFieldControls();
Invalidate(true);
}
private void InitCropMode(CropContainer.CropMode mode)
private void InitCropMode(CropContainer.CropModes mode)
{
_surface.DrawingMode = DrawingModes.None;
_surface.RemoveCropContainer();
if (mode == CropContainer.CropMode.AutoCrop)
if (mode == CropContainer.CropModes.AutoCrop)
{
if (!_surface.AutoCrop())
{
//not AutoCrop possible automatic switch to default crop mode
_surface.DrawingMode = DrawingModes.Crop;
_surface.FieldAggregator.GetField(FieldType.CROPMODE).Value = CropContainer.CropMode.Default;
this.cropModeButton.SelectedTag = CropContainer.CropMode.Default;
_surface.FieldAggregator.GetField(FieldType.CROPMODE).Value = CropContainer.CropModes.Default;
this.cropModeButton.SelectedTag = CropContainer.CropModes.Default;
this.statusLabel.Text = Language.GetString(LangKey.editor_autocrop_not_possible);
}
}
@ -1710,7 +1710,7 @@ namespace Greenshot.Editor.Forms
cropRectangle = ImageHelper.FindAutoCropRectangle(tmpImage, coreConfiguration.AutoCropDifference);
}
if (_surface.IsCropPossible(ref cropRectangle, CropContainer.CropMode.AutoCrop))
if (_surface.IsCropPossible(ref cropRectangle, CropContainer.CropModes.AutoCrop))
{
_surface.ApplyCrop(cropRectangle);
UpdateUndoRedoSurfaceDependencies();