#region License Information (GPL v3) /* ShareX - A program that allows you to take screenshots and share any file type Copyright (c) 2007-2018 ShareX Team 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 2 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, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Optionally you can also view the license at . */ #endregion License Information (GPL v3) using ShareX.HelpersLib; using ShareX.ImageEffectsLib; using System; using System.Collections.Generic; using System.Drawing; using System.Drawing.Drawing2D; using System.Linq; using System.Threading; using System.Windows.Forms; namespace ShareX.ScreenCaptureLib { internal partial class ShapeManager : IDisposable { public List Shapes { get; private set; } = new List(); private BaseShape currentShape; public BaseShape CurrentShape { get { return currentShape; } private set { currentShape = value; if (currentShape != null) { currentShape.OnConfigSave(); } OnCurrentShapeChanged(currentShape); } } private ShapeType currentTool; public ShapeType CurrentTool { get { return currentTool; } private set { currentTool = value; if (Form.IsAnnotationMode) { if (IsCurrentShapeTypeRegion) { Options.LastRegionTool = CurrentTool; } else if (Form.IsEditorMode) { Options.LastEditorTool = CurrentTool; } else { Options.LastAnnotationTool = CurrentTool; } ClearTools(); } DeselectCurrentShape(); OnCurrentShapeTypeChanged(currentTool); } } public Rectangle CurrentRectangle { get { if (CurrentShape != null) { return CurrentShape.Rectangle; } return Rectangle.Empty; } } public bool IsCurrentShapeValid => CurrentShape != null && CurrentShape.IsValidShape; public BaseShape[] Regions => Shapes.OfType().ToArray(); public BaseShape[] ValidRegions => Regions.Where(x => x.IsValidShape).ToArray(); public BaseDrawingShape[] DrawingShapes => Shapes.OfType().ToArray(); public BaseEffectShape[] EffectShapes => Shapes.OfType().ToArray(); public BaseTool[] ToolShapes => Shapes.OfType().ToArray(); private BaseShape currentHoverShape; public BaseShape CurrentHoverShape { get { return currentHoverShape; } private set { if (currentHoverShape != null) { if (PreviousHoverRectangle == Rectangle.Empty || PreviousHoverRectangle != currentHoverShape.Rectangle) { PreviousHoverRectangle = currentHoverShape.Rectangle; } } else { PreviousHoverRectangle = Rectangle.Empty; } currentHoverShape = value; } } public Rectangle PreviousHoverRectangle { get; private set; } public bool IsCurrentHoverShapeValid => CurrentHoverShape != null && CurrentHoverShape.IsValidShape; public bool IsCurrentShapeTypeRegion => IsShapeTypeRegion(CurrentTool); public bool IsCreating { get; set; } public bool IsMoving { get; set; } public bool IsPanning { get; set; } public bool IsResizing { get; set; } // Is holding Ctrl? public bool IsCornerMoving { get; private set; } // Is holding Shift? public bool IsProportionalResizing { get; private set; } // Is holding Alt? public bool IsSnapResizing { get; private set; } public bool IsRenderingOutput { get; private set; } private bool isAnnotated; public bool IsAnnotated => isAnnotated || DrawingShapes.Where(x => x.ShapeType != ShapeType.DrawingCursor).Count() > 0 || EffectShapes.Length > 0; public InputManager InputManager { get; private set; } = new InputManager(); public List Windows { get; set; } public bool WindowCaptureMode { get; set; } public bool IncludeControls { get; set; } public RegionCaptureOptions Options { get; private set; } public AnnotationOptions AnnotationOptions => Options.AnnotationOptions; internal List DrawableObjects { get; private set; } internal ResizeNode[] ResizeNodes { get; private set; } private bool nodesVisible; public bool NodesVisible { get { return nodesVisible; } set { nodesVisible = value; if (!nodesVisible) { foreach (ResizeNode node in ResizeNodes) { node.Visible = false; } } else { BaseShape shape = CurrentShape; if (shape != null) { shape.OnNodePositionUpdate(); shape.OnNodeVisible(); } } } } public bool IsCursorOnObject => DrawableObjects.Any(x => x.HandleMouseInput && x.IsCursorHover); public event Action CurrentShapeChanged; public event Action CurrentShapeTypeChanged; public event Action ShapeCreated; internal RegionCaptureForm Form { get; private set; } private bool isLeftPressed, isRightPressed, isUpPressed, isDownPressed; public ShapeManager(RegionCaptureForm form) { Form = form; Options = form.Options; DrawableObjects = new List(); ResizeNodes = new ResizeNode[9]; for (int i = 0; i < ResizeNodes.Length; i++) { ResizeNode node = new ResizeNode(); node.SetCustomNode(form.CustomNodeImage); DrawableObjects.Add(node); ResizeNodes[i] = node; } ResizeNodes[(int)NodePosition.BottomRight].Order = 10; form.Shown += form_Shown; form.LostFocus += form_LostFocus; form.MouseDown += form_MouseDown; form.MouseUp += form_MouseUp; form.MouseDoubleClick += form_MouseDoubleClick; form.MouseWheel += form_MouseWheel; form.KeyDown += form_KeyDown; form.KeyUp += form_KeyUp; CurrentShape = null; if (form.Mode == RegionCaptureMode.Annotation) { CurrentTool = Options.LastRegionTool; } else if (form.IsEditorMode) { CurrentTool = Options.LastEditorTool; } else { CurrentTool = ShapeType.RegionRectangle; } } private void form_Shown(object sender, EventArgs e) { if (Form.IsAnnotationMode) { CreateToolbar(); } } private void form_LostFocus(object sender, EventArgs e) { IsCornerMoving = IsProportionalResizing = IsSnapResizing = false; } private void form_MouseDown(object sender, MouseEventArgs e) { if (e.Button == MouseButtons.Left) { if (!IsCreating) { StartRegionSelection(); } } else if (e.Button == MouseButtons.Middle) { if (Form.IsEditorMode) { StartPanning(); } } } private void form_MouseUp(object sender, MouseEventArgs e) { if (e.Button == MouseButtons.Left) { if (IsMoving || IsCreating) { EndRegionSelection(); } } else if (e.Button == MouseButtons.Right) { if (IsCreating) { DeleteCurrentShape(); EndRegionSelection(); } else if (Form.IsAnnotationMode) { RunAction(Options.RegionCaptureActionRightClick); } else if (IsShapeIntersect()) { DeleteIntersectShape(); } else { Form.Close(); } } else if (e.Button == MouseButtons.Middle) { if (Form.IsEditorMode) { EndPanning(); } else { RunAction(Options.RegionCaptureActionMiddleClick); } } else if (e.Button == MouseButtons.XButton1) { RunAction(Options.RegionCaptureActionX1Click); } else if (e.Button == MouseButtons.XButton2) { RunAction(Options.RegionCaptureActionX2Click); } } private void form_MouseDoubleClick(object sender, MouseEventArgs e) { if (e.Button == MouseButtons.Left) { if (IsCurrentShapeTypeRegion && ValidRegions.Length > 0) { Form.UpdateRegionPath(); Form.Close(RegionResult.Region); } else if (CurrentShape != null && !IsCreating) { CurrentShape.OnDoubleClicked(); } } } private void form_MouseWheel(object sender, MouseEventArgs e) { if (Control.ModifierKeys.HasFlag(Keys.Control) && Form.Mode == RegionCaptureMode.Annotation) { if (e.Delta > 0) { CurrentTool = CurrentTool.Previous(); if (!Form.IsEditorMode && CurrentTool == ShapeType.ToolCrop) { CurrentTool = CurrentTool.Previous(); } } else if (e.Delta < 0) { CurrentTool = CurrentTool.Next(); if (!Form.IsEditorMode && CurrentTool == ShapeType.ToolCrop) { CurrentTool = CurrentTool.Next(); } } } else { if (e.Delta > 0) { Options.MagnifierPixelCount = Math.Min(Options.MagnifierPixelCount + 2, RegionCaptureOptions.MagnifierPixelCountMaximum); } else if (e.Delta < 0) { Options.MagnifierPixelCount = Math.Max(Options.MagnifierPixelCount - 2, RegionCaptureOptions.MagnifierPixelCountMinimum); } } } private void form_KeyDown(object sender, KeyEventArgs e) { switch (e.KeyCode) { case Keys.ControlKey: IsCornerMoving = true; break; case Keys.ShiftKey: IsProportionalResizing = true; break; case Keys.Menu: IsSnapResizing = true; break; case Keys.Left: case Keys.A: isLeftPressed = true; break; case Keys.Right: case Keys.D: isRightPressed = true; break; case Keys.Up: case Keys.W: isUpPressed = true; break; case Keys.Down: case Keys.S: isDownPressed = true; break; } switch (e.KeyData) { case Keys.Insert: if (IsCreating) { EndRegionSelection(); } else { StartRegionSelection(); } break; case Keys.Delete: DeleteCurrentShape(); if (IsCreating) { EndRegionSelection(); } break; case Keys.Shift | Keys.Delete: DeleteAllShapes(); break; case Keys.F1: Options.ShowHotkeys = !Options.ShowHotkeys; if (tsmiTips != null) { tsmiTips.Checked = Options.ShowHotkeys; } break; } if (!IsCreating) { if (Form.Mode == RegionCaptureMode.Annotation) { switch (e.KeyData) { case Keys.Tab: SwapShapeType(); break; case Keys.NumPad0: CurrentTool = ShapeType.RegionRectangle; break; } } if (Form.IsAnnotationMode) { switch (e.KeyData) { case Keys.R: case Keys.NumPad1: CurrentTool = ShapeType.DrawingRectangle; break; case Keys.E: case Keys.NumPad2: CurrentTool = ShapeType.DrawingEllipse; break; case Keys.F: case Keys.NumPad3: CurrentTool = ShapeType.DrawingFreehand; break; case Keys.L: case Keys.NumPad4: CurrentTool = ShapeType.DrawingLine; break; case Keys.A: case Keys.NumPad5: CurrentTool = ShapeType.DrawingArrow; break; case Keys.O: case Keys.NumPad6: CurrentTool = ShapeType.DrawingTextOutline; break; case Keys.I: case Keys.NumPad7: CurrentTool = ShapeType.DrawingStep; break; case Keys.B: case Keys.NumPad8: CurrentTool = ShapeType.EffectBlur; break; case Keys.P: case Keys.NumPad9: CurrentTool = ShapeType.EffectPixelate; break; case Keys.H: CurrentTool = ShapeType.EffectHighlight; break; case Keys.T: CurrentTool = ShapeType.DrawingTextBackground; break; case Keys.S: CurrentTool = ShapeType.DrawingSpeechBalloon; break; case Keys.Control | Keys.V: PasteFromClipboard(true); break; case Keys.Control | Keys.Z: UndoShape(); break; case Keys.Home: MoveCurrentShapeTop(); break; case Keys.End: MoveCurrentShapeBottom(); break; case Keys.PageUp: MoveCurrentShapeUp(); break; case Keys.PageDown: MoveCurrentShapeDown(); break; case Keys.Q: Options.QuickCrop = !Options.QuickCrop; tsmiQuickCrop.Checked = !Options.QuickCrop; break; } } if (Form.IsEditorMode) { switch (e.KeyData) { case Keys.C: CurrentTool = ShapeType.ToolCrop; break; } } } int speed; if (e.Shift) { speed = RegionCaptureOptions.MoveSpeedMaximum; } else { speed = RegionCaptureOptions.MoveSpeedMinimum; } int x = 0; if (isLeftPressed) { x -= speed; } if (isRightPressed) { x += speed; } int y = 0; if (isUpPressed) { y -= speed; } if (isDownPressed) { y += speed; } if (x != 0 || y != 0) { BaseShape shape = CurrentShape; if (shape == null || IsCreating) { Cursor.Position = Cursor.Position.Add(x, y); } else { if (e.Control) { shape.OnMoving(); shape.Move(x, y); } else { shape.OnResizing(); shape.Resize(x, y, !e.Alt); } } } } private void form_KeyUp(object sender, KeyEventArgs e) { bool wasMoving = isLeftPressed || isRightPressed || isUpPressed || isDownPressed; switch (e.KeyCode) { case Keys.ControlKey: IsCornerMoving = false; break; case Keys.ShiftKey: IsProportionalResizing = false; break; case Keys.Menu: IsSnapResizing = false; break; case Keys.Left: case Keys.A: isLeftPressed = false; break; case Keys.Right: case Keys.D: isRightPressed = false; break; case Keys.Up: case Keys.W: isUpPressed = false; break; case Keys.Down: case Keys.S: isDownPressed = false; break; } if (!IsCreating && !IsMoving && wasMoving) { bool isMoving = isLeftPressed || isRightPressed || isUpPressed || isDownPressed; if (!isMoving) { ShapeMoved(); } } } private void ShapeMoved() { if (!IsCreating) { BaseShape shape = CurrentShape; if (shape != null) { shape.OnMoved(); } } } private void RunAction(RegionCaptureAction action) { switch (action) { case RegionCaptureAction.CancelCapture: if (Form.Mode == RegionCaptureMode.TaskEditor) { Form.Close(RegionResult.AnnotateContinueTask); } else { Form.Close(); } break; case RegionCaptureAction.RemoveShapeCancelCapture: if (IsShapeIntersect()) { DeleteIntersectShape(); } else if (Form.Mode == RegionCaptureMode.TaskEditor) { Form.Close(RegionResult.AnnotateContinueTask); } else { Form.Close(); } break; case RegionCaptureAction.RemoveShape: DeleteIntersectShape(); break; case RegionCaptureAction.SwapToolType: SwapShapeType(); break; case RegionCaptureAction.CaptureFullscreen: Form.Close(RegionResult.Fullscreen); break; case RegionCaptureAction.CaptureActiveMonitor: Form.Close(RegionResult.ActiveMonitor); break; } } public void Update() { OrderStepShapes(); BaseShape shape = CurrentShape; if (shape != null) { shape.OnUpdate(); } UpdateCurrentHoverShape(); UpdateNodes(); } private void StartRegionSelection() { if (IsCursorOnObject) { return; } InputManager.Update(Form); // If it's a touch event we don't have the correct point yet, so refresh it now BaseShape shape = GetIntersectShape(); if (shape != null && shape.ShapeType == CurrentTool) // Select shape { IsMoving = true; shape.OnMoving(); Form.Cursor = Cursors.SizeAll; CurrentShape = shape; SelectCurrentShape(); } else if (!IsCreating) // Create new shape { ClearTools(); DeselectCurrentShape(); shape = AddShape(); shape.OnCreating(); } } private void EndRegionSelection() { bool wasCreating = IsCreating; bool wasMoving = IsMoving; IsCreating = false; IsMoving = false; Form.SetDefaultCursor(); BaseShape shape = CurrentShape; if (shape != null) { if (!shape.IsValidShape) { shape.Rectangle = Rectangle.Empty; UpdateCurrentHoverShape(); if (IsCurrentHoverShapeValid) { shape.Rectangle = CurrentHoverShape.Rectangle; } else { DeleteCurrentShape(); shape = null; } } if (shape != null) { if (Options.QuickCrop && IsCurrentShapeTypeRegion) { Form.UpdateRegionPath(); Form.Close(RegionResult.Region); } else { if (wasCreating) { shape.OnCreated(); OnShapeCreated(shape); } else if (wasMoving) { shape.OnMoved(); } SelectCurrentShape(); } } } } private void StartPanning() { IsPanning = true; Form.PanningStrech = new Point(0, 0); Form.Cursor = Cursors.SizeAll; Options.ShowEditorPanTip = false; } private void EndPanning() { IsPanning = false; Form.SetDefaultCursor(); } internal void UpdateObjects() { DrawableObject[] objects = DrawableObjects.OrderByDescending(x => x.Order).ToArray(); Point position = InputManager.ClientMousePosition; if (objects.All(x => !x.IsDragging)) { for (int i = 0; i < objects.Length; i++) { DrawableObject obj = objects[i]; if (obj.Visible) { obj.IsCursorHover = obj.Rectangle.Contains(position); if (obj.IsCursorHover) { if (InputManager.IsMousePressed(MouseButtons.Left)) { obj.OnMouseDown(position); } for (int j = i + 1; j < objects.Length; j++) { objects[j].IsCursorHover = false; } break; } } else { obj.IsCursorHover = false; } } } else { if (InputManager.IsMouseReleased(MouseButtons.Left)) { foreach (DrawableObject obj in objects) { if (obj.IsDragging) { obj.OnMouseUp(position); } } } } } internal void DrawObjects(Graphics g) { foreach (DrawableObject obj in DrawableObjects) { if (obj.Visible) { obj.OnDraw(g); } } } private BaseShape AddShape() { BaseShape shape = CreateShape(); AddShape(shape); return shape; } private void AddShape(BaseShape shape) { Shapes.Add(shape); CurrentShape = shape; } private BaseShape CreateShape() { return CreateShape(CurrentTool); } private BaseShape CreateShape(ShapeType shapeType) { BaseShape shape; switch (shapeType) { default: case ShapeType.RegionRectangle: shape = new RectangleRegionShape(); break; case ShapeType.RegionEllipse: shape = new EllipseRegionShape(); break; case ShapeType.RegionFreehand: shape = new FreehandRegionShape(); break; case ShapeType.DrawingRectangle: shape = new RectangleDrawingShape(); break; case ShapeType.DrawingEllipse: shape = new EllipseDrawingShape(); break; case ShapeType.DrawingFreehand: shape = new FreehandDrawingShape(); break; case ShapeType.DrawingLine: shape = new LineDrawingShape(); break; case ShapeType.DrawingArrow: shape = new ArrowDrawingShape(); break; case ShapeType.DrawingTextOutline: shape = new TextOutlineDrawingShape(); break; case ShapeType.DrawingTextBackground: shape = new TextDrawingShape(); break; case ShapeType.DrawingSpeechBalloon: shape = new SpeechBalloonDrawingShape(); break; case ShapeType.DrawingStep: shape = new StepDrawingShape(); break; case ShapeType.DrawingImage: shape = new ImageFileDrawingShape(); break; case ShapeType.DrawingImageScreen: shape = new ImageScreenDrawingShape(); break; case ShapeType.DrawingSticker: shape = new StickerDrawingShape(); break; case ShapeType.DrawingCursor: shape = new CursorDrawingShape(); break; case ShapeType.EffectBlur: shape = new BlurEffectShape(); break; case ShapeType.EffectPixelate: shape = new PixelateEffectShape(); break; case ShapeType.EffectHighlight: shape = new HighlightEffectShape(); break; case ShapeType.ToolCrop: shape = new CropTool(); break; } shape.Manager = this; shape.OnConfigLoad(); return shape; } private void UpdateCurrentShape() { BaseShape shape = CurrentShape; if (shape != null) { shape.OnConfigLoad(); shape.OnMoved(); } } private void SwapShapeType() { if (Form.Mode == RegionCaptureMode.Annotation) { if (IsCurrentShapeTypeRegion) { CurrentTool = Options.LastAnnotationTool; } else { CurrentTool = Options.LastRegionTool; } } } public Point SnapPosition(Point posOnClick, Point posCurrent) { Size currentSize = CaptureHelpers.CreateRectangle(posOnClick, posCurrent).Size; Vector2 vector = new Vector2(currentSize.Width, currentSize.Height); SnapSize snapSize = (from size in Options.SnapSizes let distance = MathHelpers.Distance(vector, new Vector2(size.Width, size.Height)) where distance > 0 && distance < RegionCaptureOptions.SnapDistance orderby distance select size).FirstOrDefault(); if (snapSize != null) { Point posNew = CaptureHelpers.CalculateNewPosition(posOnClick, posCurrent, snapSize); Rectangle newRect = CaptureHelpers.CreateRectangle(posOnClick, posNew); if (Form.ClientArea.Contains(newRect)) { return posNew; } } return posCurrent; } private void UpdateCurrentHoverShape() { CurrentHoverShape = CheckHover(); } private BaseShape CheckHover() { if (!IsCursorOnObject && !IsCreating && !IsMoving && !IsResizing) { BaseShape shape = GetIntersectShape(); if (shape != null && shape.IsValidShape) { return shape; } else { switch (CurrentTool) { case ShapeType.RegionFreehand: case ShapeType.DrawingFreehand: case ShapeType.DrawingLine: case ShapeType.DrawingArrow: case ShapeType.DrawingTextOutline: case ShapeType.DrawingTextBackground: case ShapeType.DrawingSpeechBalloon: case ShapeType.DrawingStep: case ShapeType.DrawingImage: case ShapeType.DrawingSticker: case ShapeType.DrawingCursor: return null; } if (Options.IsFixedSize && IsCurrentShapeTypeRegion) { Point location = InputManager.ClientMousePosition; return new RectangleRegionShape() { Rectangle = new Rectangle(new Point(location.X - Options.FixedSize.Width / 2, location.Y - Options.FixedSize.Height / 2), Options.FixedSize) }; } else { SimpleWindowInfo window = FindSelectedWindow(); if (window != null && !window.Rectangle.IsEmpty) { Rectangle hoverArea = CaptureHelpers.ScreenToClient(window.Rectangle); return new RectangleRegionShape() { Rectangle = Rectangle.Intersect(Form.ClientArea, hoverArea) }; } } } } return null; } public SimpleWindowInfo FindSelectedWindow() { if (Windows != null) { return Windows.FirstOrDefault(x => x.Rectangle.Contains(InputManager.MousePosition)); } return null; } public WindowInfo FindSelectedWindowInfo(Point position) { if (Windows != null) { SimpleWindowInfo windowInfo = Windows.FirstOrDefault(x => x.IsWindow && x.Rectangle.Contains(position)); if (windowInfo != null) { return windowInfo.WindowInfo; } } return null; } public Image RenderOutputImage(Image img) { return RenderOutputImage(img, Point.Empty); } public Image RenderOutputImage(Image img, Point offset) { Bitmap bmp = new Bitmap(img); if (DrawingShapes.Length > 0 || EffectShapes.Length > 0) { IsRenderingOutput = true; MoveAll(-offset.X, -offset.Y); using (Graphics g = Graphics.FromImage(bmp)) { foreach (BaseEffectShape shape in EffectShapes) { if (shape != null) { shape.OnDrawFinal(g, bmp); } } foreach (BaseDrawingShape shape in DrawingShapes) { if (shape != null) { shape.OnDraw(g); } } } MoveAll(offset); IsRenderingOutput = false; } return bmp; } private void SelectShape(BaseShape shape) { if (shape != null) { shape.ShowNodes(); } } private void SelectCurrentShape() { SelectShape(CurrentShape); } private void SelectIntersectShape() { BaseShape shape = GetIntersectShape(); if (shape != null) { CurrentShape = shape; SelectShape(shape); } } private void DeselectShape(BaseShape shape) { if (shape == CurrentShape) { CurrentShape = null; NodesVisible = false; } } private void DeselectCurrentShape() { DeselectShape(CurrentShape); } public void DeleteShape(BaseShape shape) { if (shape != null) { shape.Dispose(); Shapes.Remove(shape); DeselectShape(shape); UpdateMenu(); } } private void DeleteCurrentShape() { DeleteShape(CurrentShape); } private void DeleteIntersectShape() { DeleteShape(GetIntersectShape()); } private void DeleteAllShapes() { foreach (BaseShape shape in Shapes) { shape.Dispose(); } Shapes.Clear(); DeselectCurrentShape(); } private void ClearTools() { foreach (BaseTool tool in ToolShapes) { tool.Dispose(); Shapes.Remove(tool); } } public BaseShape GetIntersectShape() { return GetIntersectShape(InputManager.ClientMousePosition); } public BaseShape GetIntersectShape(Point position) { for (int i = Shapes.Count - 1; i >= 0; i--) { BaseShape shape = Shapes[i]; if (shape.ShapeType == CurrentTool && shape.Intersects(position)) { return shape; } } return null; } public bool IsShapeIntersect() { return GetIntersectShape() != null; } public void UndoShape() { if (Shapes.Count > 0) { DeleteShape(Shapes[Shapes.Count - 1]); } } public void MoveShapeBottom(BaseShape shape) { if (shape != null) { for (int i = 0; i < Shapes.Count; i++) { if (Shapes[i] == shape) { Shapes.Move(i, 0); return; } } } } public void MoveCurrentShapeBottom() { MoveShapeBottom(CurrentShape); } public void MoveShapeTop(BaseShape shape) { if (shape != null) { for (int i = 0; i < Shapes.Count; i++) { if (Shapes[i] == shape) { Shapes.Move(i, Shapes.Count - 1); return; } } } } public void MoveCurrentShapeTop() { MoveShapeTop(CurrentShape); } public void MoveShapeDown(BaseShape shape) { if (shape != null) { for (int i = 1; i < Shapes.Count; i++) { if (Shapes[i] == shape) { Shapes.Move(i, --i); return; } } } } public void MoveCurrentShapeDown() { MoveShapeDown(CurrentShape); } public void MoveShapeUp(BaseShape shape) { if (shape != null) { for (int i = 0; i < Shapes.Count - 1; i++) { if (Shapes[i] == shape) { Shapes.Move(i, ++i); return; } } } } public void MoveCurrentShapeUp() { MoveShapeUp(CurrentShape); } public void MoveAll(int x, int y) { if (x != 0 || y != 0) { foreach (BaseShape shape in Shapes) { shape.Move(x, y); } } } public void MoveAll(Point offset) { MoveAll(offset.X, offset.Y); } public void RemoveOutsideShapes() { foreach (BaseShape shape in Shapes.ToArray()) { if (!Form.CanvasRectangle.IntersectsWith(shape.Rectangle)) { shape.Remove(); } } } private bool IsShapeTypeRegion(ShapeType shapeType) { switch (shapeType) { case ShapeType.RegionRectangle: case ShapeType.RegionEllipse: case ShapeType.RegionFreehand: return true; } return false; } private void UpdateNodes() { BaseShape shape = CurrentShape; if (shape != null && NodesVisible) { if (InputManager.IsMouseDown(MouseButtons.Left)) { shape.OnNodeUpdate(); } else if (IsResizing) { IsResizing = false; shape.OnResized(); } shape.OnNodePositionUpdate(); } } public void OrderStepShapes() { int i = 1; foreach (StepDrawingShape shape in Shapes.OfType()) { shape.Number = i++; } } private void PasteFromClipboard(bool insertMousePosition) { Point pos; if (insertMousePosition) { pos = InputManager.ClientMousePosition; } else { pos = Form.ClientArea.Center(); } if (Clipboard.ContainsImage()) { Image img = ClipboardHelpers.GetImage(); InsertImage(img, pos); } else if (Clipboard.ContainsFileDropList()) { string[] files = Clipboard.GetFileDropList().OfType().Where(x => Helpers.IsImageFile(x)).ToArray(); if (files.Length > 0 && !string.IsNullOrEmpty(files[0])) { Image img = ImageHelpers.LoadImage(files[0]); InsertImage(img, pos); } } else if (Clipboard.ContainsText()) { string text = Clipboard.GetText(); if (!string.IsNullOrEmpty(text)) { CurrentTool = ShapeType.DrawingTextBackground; TextDrawingShape shape = (TextDrawingShape)CreateShape(ShapeType.DrawingTextBackground); shape.Rectangle = new Rectangle(pos.X, pos.Y, 1, 1); shape.Text = text.Trim(); shape.OnCreated(); AddShape(shape); SelectCurrentShape(); } } } public void AddCursor(IntPtr cursorHandle, Point position) { CursorDrawingShape shape = (CursorDrawingShape)CreateShape(ShapeType.DrawingCursor); shape.UpdateCursor(cursorHandle, position); Shapes.Add(shape); } public void DrawRegionArea(Graphics g, Rectangle rect, bool isAnimated, bool showAreaInfo = false) { Form.DrawRegionArea(g, rect, isAnimated); if (showAreaInfo) { string areaText = Form.GetAreaText(rect); Form.DrawAreaText(g, areaText, rect); } } private void UpdateCanvas(Image img) { Form.InitBackground(img); foreach (BaseEffectShape effect in EffectShapes) { effect.OnMoved(); } isAnnotated = true; } public void CropArea(Rectangle rect) { Image img = CropImage(rect, true); if (img != null) { MoveAll(Form.CanvasRectangle.X - rect.X, Form.CanvasRectangle.Y - rect.Y); UpdateCanvas(img); } } public Image CropImage(Rectangle rect, bool onlyIfSizeDifferent = false) { rect = CaptureHelpers.ScreenToClient(rect); Point offset = CaptureHelpers.ScreenToClient(Form.CanvasRectangle.Location); rect.X -= offset.X; rect.Y -= offset.Y; rect.Intersect(new Rectangle(0, 0, Form.Canvas.Width, Form.Canvas.Height)); if (rect.IsValid() && (!onlyIfSizeDifferent || rect.Size != Form.Canvas.Size)) { return ImageHelpers.CropBitmap((Bitmap)Form.Canvas, rect); } return null; } public void NewImage() { Form.Pause(); Image img = NewImageForm.CreateNewImage(Options, Form); Form.Resume(); if (img != null) { Form.ImageFilePath = ""; DeleteAllShapes(); UpdateMenu(); UpdateCanvas(img); } } private void OpenImageFile() { Form.Pause(); string filePath = ImageHelpers.OpenImageFileDialog(Form); Form.Resume(); LoadImageFile(filePath); } private void LoadImageFile(string filePath) { if (!string.IsNullOrEmpty(filePath)) { Image img = ImageHelpers.LoadImage(filePath); if (img != null) { Form.ImageFilePath = filePath; DeleteAllShapes(); UpdateMenu(); UpdateCanvas(img); } } } private void InsertImageFile() { Form.Pause(); string filePath = ImageHelpers.OpenImageFileDialog(Form); Form.Resume(); if (!string.IsNullOrEmpty(filePath)) { Image img = ImageHelpers.LoadImage(filePath); InsertImage(img, Form.ClientArea.Center()); } } private void InsertImageFromScreen() { Image img; try { Form.Pause(); Form.Hide(); menuForm.Hide(); Thread.Sleep(250); img = RegionCaptureTasks.GetRegionImage(Options); } finally { Form.Show(); menuForm.Show(); Form.Resume(); } InsertImage(img, Form.ClientArea.Center()); } private void InsertImage(Image img, Point pos) { if (img != null) { CurrentTool = ShapeType.DrawingImage; ImageDrawingShape shape = (ImageDrawingShape)CreateShape(ShapeType.DrawingImage); shape.Rectangle = new Rectangle(pos.X, pos.Y, 1, 1); shape.SetImage(img, true); shape.OnCreated(); AddShape(shape); SelectCurrentShape(); } } private void ChangeImageSize() { Form.Pause(); Size oldSize = Form.Canvas.Size; using (ImageSizeForm imageSizeForm = new ImageSizeForm(oldSize, Options.ImageEditorResizeInterpolationMode)) { if (imageSizeForm.ShowDialog(Form) == DialogResult.OK) { Size size = imageSizeForm.ImageSize; Options.ImageEditorResizeInterpolationMode = imageSizeForm.InterpolationMode; if (size != oldSize) { InterpolationMode interpolationMode = GetInterpolationMode(Options.ImageEditorResizeInterpolationMode); Image img = ImageHelpers.ResizeImage(Form.Canvas, size, interpolationMode); if (img != null) { UpdateCanvas(img); } } } } Form.Resume(); } internal InterpolationMode GetInterpolationMode(ImageEditorInterpolationMode interpolationMode) { switch (interpolationMode) { default: case ImageEditorInterpolationMode.HighQualityBicubic: return InterpolationMode.HighQualityBicubic; case ImageEditorInterpolationMode.Bicubic: return InterpolationMode.Bicubic; case ImageEditorInterpolationMode.HighQualityBilinear: return InterpolationMode.HighQualityBilinear; case ImageEditorInterpolationMode.Bilinear: return InterpolationMode.Bilinear; case ImageEditorInterpolationMode.NearestNeighbor: return InterpolationMode.NearestNeighbor; } } private void ChangeCanvasSize() { Form.Pause(); using (CanvasSizeForm canvasSizeForm = new CanvasSizeForm()) { if (canvasSizeForm.ShowDialog(Form) == DialogResult.OK) { Padding canvas = canvasSizeForm.Canvas; Image img = ImageHelpers.AddCanvas(Form.Canvas, canvas); if (img != null) { MoveAll(canvas.Left, canvas.Top); UpdateCanvas(img); } } } Form.Resume(); } private void AddCropTool() { CurrentTool = ShapeType.ToolCrop; CropTool tool = (CropTool)CreateShape(ShapeType.ToolCrop); tool.Rectangle = Form.CanvasRectangle; tool.OnCreated(); AddShape(tool); SelectCurrentShape(); } private void AutoCropImage() { Rectangle source = new Rectangle(0, 0, Form.Canvas.Width, Form.Canvas.Height); Rectangle rect = ImageHelpers.FindAutoCropRectangle((Bitmap)Form.Canvas); if (source != rect && rect.X >= 0 && rect.Y >= 0 && rect.Width > 0 && rect.Height > 0) { CurrentTool = ShapeType.ToolCrop; CropTool tool = (CropTool)CreateShape(ShapeType.ToolCrop); tool.Rectangle = rect.LocationOffset(Form.CanvasRectangle.Location); tool.OnCreated(); AddShape(tool); SelectCurrentShape(); } } private void RotateImage(RotateFlipType type) { Image img = (Image)Form.Canvas.Clone(); img.RotateFlip(type); UpdateCanvas(img); } private void AddImageEffects() { Form.Pause(); using (ImageEffectsForm imageEffectsForm = new ImageEffectsForm(Form.Canvas, Options.ImageEffectPresets, Options.SelectedImageEffectPreset)) { imageEffectsForm.EditorMode(); bool applyEffect = imageEffectsForm.ShowDialog(Form) == DialogResult.OK; Options.SelectedImageEffectPreset = imageEffectsForm.SelectedPresetIndex; if (applyEffect) { ImageEffectPreset preset = imageEffectsForm.Presets.ReturnIfValidIndex(Options.SelectedImageEffectPreset); if (preset != null) { Image img = preset.ApplyEffects(Form.Canvas); if (img != null) { UpdateCanvas(img); } } } } Form.Resume(); } private void OnCurrentShapeChanged(BaseShape shape) { if (CurrentShapeChanged != null) { CurrentShapeChanged(shape); } } private void OnCurrentShapeTypeChanged(ShapeType shapeType) { if (CurrentShapeTypeChanged != null) { CurrentShapeTypeChanged(shapeType); } } private void OnShapeCreated(BaseShape shape) { if (ShapeCreated != null) { ShapeCreated(shape); } } public void Dispose() { DeleteAllShapes(); } } }