diff --git a/src/Greenshot.Base/Interfaces/Drawing/IDrawableContainer.cs b/src/Greenshot.Base/Interfaces/Drawing/IDrawableContainer.cs index 24abc2d25..3d420bfa3 100644 --- a/src/Greenshot.Base/Interfaces/Drawing/IDrawableContainer.cs +++ b/src/Greenshot.Base/Interfaces/Drawing/IDrawableContainer.cs @@ -78,6 +78,11 @@ namespace Greenshot.Base.Interfaces.Drawing bool InitContent(); + /// + /// Defines if the drawable container participates in undo / redo + /// + bool IsUndoable { get; } + void MakeBoundsChangeUndoable(bool allowMerge); EditStatus DefaultEditMode { get; } diff --git a/src/Greenshot.Base/Interfaces/Drawing/IFieldAggregator.cs b/src/Greenshot.Base/Interfaces/Drawing/IFieldAggregator.cs index fdbf72847..5af83b39e 100644 --- a/src/Greenshot.Base/Interfaces/Drawing/IFieldAggregator.cs +++ b/src/Greenshot.Base/Interfaces/Drawing/IFieldAggregator.cs @@ -21,6 +21,9 @@ namespace Greenshot.Base.Interfaces.Drawing { + /// + /// The IFieldAggregator defines the connections between fields and containers + /// public interface IFieldAggregator { void UnbindElement(IDrawableContainer dc); diff --git a/src/Greenshot.Base/Interfaces/ISurface.cs b/src/Greenshot.Base/Interfaces/ISurface.cs index 85cefe452..396a5702f 100644 --- a/src/Greenshot.Base/Interfaces/ISurface.cs +++ b/src/Greenshot.Base/Interfaces/ISurface.cs @@ -201,8 +201,16 @@ namespace Greenshot.Base.Interfaces /// A rectangle in the coordinate space of the surface. Rectangle ToImageCoordinates(Rectangle rc); + /// + /// Make it possible to undo the specified IMemento + /// + /// IMemento + /// bool to specify if the action can be merged, e.g. we do not want an undo for every part of a resize void MakeUndoable(IMemento memento, bool allowMerge); + /// + /// The IFieldAggregator + /// IFieldAggregator FieldAggregator { get; } /// diff --git a/src/Greenshot.Editor/Drawing/CropContainer.cs b/src/Greenshot.Editor/Drawing/CropContainer.cs index cd924369a..936a1a2c8 100644 --- a/src/Greenshot.Editor/Drawing/CropContainer.cs +++ b/src/Greenshot.Editor/Drawing/CropContainer.cs @@ -36,9 +36,9 @@ namespace Greenshot.Editor.Drawing public class CropContainer : DrawableContainer { /// - /// awailable modes + /// Available Crop modes /// - public enum CropMode + public enum CropModes { /// /// 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; } + + /// + /// Make sure this container is not undoable + public override bool IsUndoable => false; } } diff --git a/src/Greenshot.Editor/Drawing/DrawableContainer.cs b/src/Greenshot.Editor/Drawing/DrawableContainer.cs index 08abba3e2..b07974e61 100644 --- a/src/Greenshot.Editor/Drawing/DrawableContainer.cs +++ b/src/Greenshot.Editor/Drawing/DrawableContainer.cs @@ -472,13 +472,20 @@ namespace Greenshot.Editor.Drawing g.DrawRectangle(pen, rect); } + /// + public virtual bool IsUndoable => true; + /// /// Make a following bounds change on this drawablecontainer undoable! /// /// true means allow the moves to be merged - 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,11 +559,10 @@ namespace Greenshot.Editor.Drawing protected void OnPropertyChanged(string propertyName) { - if (_propertyChanged != null) - { - _propertyChanged(this, new PropertyChangedEventArgs(propertyName)); - Invalidate(); - } + if (_propertyChanged == null) return; + + _propertyChanged(this, new PropertyChangedEventArgs(propertyName)); + Invalidate(); } /// @@ -567,7 +573,10 @@ namespace Greenshot.Editor.Drawing /// The new value public virtual void BeforeFieldChange(IField fieldToBeChanged, object newValue) { - _parent?.MakeUndoable(new ChangeFieldHolderMemento(this, fieldToBeChanged), true); + if (IsUndoable) + { + _parent?.MakeUndoable(new ChangeFieldHolderMemento(this, fieldToBeChanged), true); + } Invalidate(); } diff --git a/src/Greenshot.Editor/Drawing/DrawableContainerList.cs b/src/Greenshot.Editor/Drawing/DrawableContainerList.cs index e89b78b70..1a31de881 100644 --- a/src/Greenshot.Editor/Drawing/DrawableContainerList.cs +++ b/src/Greenshot.Editor/Drawing/DrawableContainerList.cs @@ -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 elements) + { + AddRange(elements); + } + public DrawableContainerList(Guid parentId) { ParentID = parentId; @@ -135,12 +141,16 @@ namespace Greenshot.Editor.Drawing /// true means allow the moves to be merged 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); } /// diff --git a/src/Greenshot.Editor/Drawing/Surface.cs b/src/Greenshot.Editor/Drawing/Surface.cs index 92ffe50ae..c6b5ffe5b 100644 --- a/src/Greenshot.Editor/Drawing/Surface.cs +++ b/src/Greenshot.Editor/Drawing/Surface.cs @@ -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 /// Rectangle adapted to the dimensions of the image /// /// true if this is possible - 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 /// 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 /// 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 /// 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; diff --git a/src/Greenshot.Editor/Drawing/TextContainer.cs b/src/Greenshot.Editor/Drawing/TextContainer.cs index fdb718cbe..5a1f755ef 100644 --- a/src/Greenshot.Editor/Drawing/TextContainer.cs +++ b/src/Greenshot.Editor/Drawing/TextContainer.cs @@ -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); diff --git a/src/Greenshot.Editor/Forms/ImageEditorForm.Designer.cs b/src/Greenshot.Editor/Forms/ImageEditorForm.Designer.cs index c92136179..8d8162fd9 100644 --- a/src/Greenshot.Editor/Forms/ImageEditorForm.Designer.cs +++ b/src/Greenshot.Editor/Forms/ImageEditorForm.Designer.cs @@ -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 diff --git a/src/Greenshot.Editor/Forms/ImageEditorForm.cs b/src/Greenshot.Editor/Forms/ImageEditorForm.cs index 13e630727..e8a1b5bf7 100644 --- a/src/Greenshot.Editor/Forms/ImageEditorForm.cs +++ b/src/Greenshot.Editor/Forms/ImageEditorForm.cs @@ -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();