From f084d0e95ed83ad3ef49362735a861c3cfa90420 Mon Sep 17 00:00:00 2001 From: Robin Krom Date: Wed, 14 Apr 2021 23:19:31 +0200 Subject: [PATCH] Refactored the editor to be in it's own project, this is a step up to improve maintainability. --- installer/innosetup/setup.iss | 1 + src/Directory.Build.props | 2 +- src/Greenshot.Base/Controls/AnimatingForm.cs | 20 +- src/Greenshot.Base/Controls/QualityDialog.cs | 1 + .../Controls/SaveImageFileDialog.cs | 1 + src/Greenshot.Base/Controls/ThumbnailForm.cs | 1 + src/Greenshot.Base/Core/AnimationHelpers.cs | 45 +- src/Greenshot.Base/Core/Capture.cs | 2 +- src/Greenshot.Base/Core/ClipboardHelper.cs | 1 + src/Greenshot.Base/Core/CoreConfiguration.cs | 47 +- .../Core}/DestinationHelper.cs | 231 +- src/Greenshot.Base/Core/Enums/ClickActions.cs | 35 + .../Core/Enums/ClipboardFormat.cs | 36 + src/Greenshot.Base/Core/Enums/OutputFormat.cs | 37 + .../Core/Enums/WindowCaptureMode.cs | 35 + src/Greenshot.Base/Core/FilenameHelper.cs | 1 + src/Greenshot.Base/Core/ImageOutput.cs | 1 + src/Greenshot.Base/Core/WindowDetails.cs | 1 + .../Help/HelpFileLoader.cs | 200 +- .../Interfaces/ICaptureHelper.cs | 48 + .../Interfaces/IGreenshotMainForm.cs | 46 + .../Interfaces/IServiceLocator.cs | 46 +- .../Plugin/SurfaceOutputSettings.cs | 1 + .../UnmanagedHelpers/Kernel32.cs | 4 + src/Greenshot.Base/WellKnownDestinations.cs | 36 + .../Configuration/EditorConfiguration.cs | 360 +- .../Configuration/LanguageKeys.cs | 82 + .../Controls/BindableToolStripButton.cs | 96 +- .../Controls/BindableToolStripComboBox.cs | 96 +- .../BindableToolStripDropDownButton.cs | 162 +- .../Controls/ColorButton.cs | 196 +- .../CustomToolStripProfessionalRenderer.cs | 166 +- .../Controls/FontFamilyComboBox.cs | 280 +- .../Controls/MenuStripEx.cs | 128 +- .../Controls/NonJumpingPanel.cs | 138 +- .../Controls/Pipette.cs | 406 +- .../Controls/ToolStripColorButton.cs | 192 +- .../Controls/ToolStripEx.cs | 130 +- .../Controls/ToolStripNumericUpDown.cs | 174 +- .../Destinations/EditorDestination.cs | 306 +- .../Drawing/Adorners/AbstractAdorner.cs | 310 +- .../Drawing/Adorners/MoveAdorner.cs | 330 +- .../Drawing/Adorners/ResizeAdorner.cs | 370 +- .../Drawing/Adorners/TargetAdorner.cs | 2 +- .../Drawing/ArrowContainer.cs | 324 +- .../Drawing/CropContainer.cs | 214 +- .../Drawing/CursorContainer.cs | 254 +- .../Drawing/DrawableContainer.cs | 1338 ++--- .../Drawing/DrawableContainerList.cs | 1454 ++--- .../Drawing/EllipseContainer.cs | 324 +- .../Drawing/Fields/AbstractFieldHolder.cs | 382 +- .../Fields/AbstractFieldHolderWithChildren.cs | 296 +- .../Binding/AbstractBindingConverter.cs | 106 +- .../Fields/Binding/BidirectionalBinding.cs | 366 +- .../DecimalDoublePercentageConverter.cs | 102 +- .../Fields/Binding/DecimalFloatConverter.cs | 102 +- .../Fields/Binding/DecimalIntConverter.cs | 102 +- .../Fields/Binding/IBindingConverter.cs | 64 +- .../Fields/Binding/IBindingValidator.cs | 66 +- .../Fields/Binding/NotNullValidator.cs | 88 +- .../Drawing/Fields/Field.cs | 254 +- .../Drawing/Fields/FieldAggregator.cs | 446 +- .../Drawing/Fields/FieldType.cs | 210 +- .../Drawing/FilterContainer.cs | 228 +- .../Drawing/Filters/AbstractFilter.cs | 164 +- .../Drawing/Filters/BlurFilter.cs | 164 +- .../Drawing/Filters/BrightnessFilter.cs | 144 +- .../Drawing/Filters/GrayscaleFilter.cs | 178 +- .../Drawing/Filters/HighlightFilter.cs | 162 +- .../Drawing/Filters/IFilter.cs | 68 +- .../Drawing/Filters/MagnifierFilter.cs | 138 +- .../Drawing/Filters/PixelizationFilter.cs | 208 +- .../Drawing/FreehandContainer.cs | 644 +-- .../Drawing/HighlightContainer.cs | 222 +- .../Drawing/IconContainer.cs | 238 +- .../Drawing/ImageContainer.cs | 532 +- .../Drawing/LineContainer.cs | 244 +- .../Drawing/ObfuscateContainer.cs | 176 +- .../Drawing/Positions.cs | 74 +- .../Drawing/RectangleContainer.cs | 332 +- .../Drawing/SpeechbubbleContainer.cs | 796 +-- .../Drawing/StepLabelContainer.cs | 448 +- .../Drawing/Surface.cs | 4732 ++++++++--------- .../Drawing/TextContainer.cs | 1380 ++--- .../Forms/ColorDialog.Designer.cs | 575 +- .../Forms/ColorDialog.cs | 558 +- .../Forms/ColorDialog.resx | 268 +- .../Forms/ColorPickerToolStripButton.cs | 200 +- .../Forms/DropShadowSettingsForm.Designer.cs | 476 +- .../Forms/DropShadowSettingsForm.cs | 116 +- src/Greenshot.Editor/Forms/EditorForm.cs | 29 + .../Forms/ImageEditorForm.Designer.cs | 4015 +++++++------- .../Forms/ImageEditorForm.cs | 3852 +++++++------- .../Forms/ImageEditorForm.resx | 2218 ++++---- .../Forms/MovableShowColorForm.Designer.cs | 366 +- .../Forms/MovableShowColorForm.cs | 232 +- .../Forms/ResizeSettingsForm.Designer.cs | 363 +- .../Forms/ResizeSettingsForm.cs | 426 +- .../Forms/TornEdgeSettingsForm.Designer.cs | 877 ++- .../Forms/TornEdgeSettingsForm.cs | 228 +- src/Greenshot.Editor/Greenshot.Editor.csproj | 84 + .../Helpers/Colors.cs | 120 +- .../Helpers/GeometryHelper.cs | 126 +- .../Helpers/GuiRectangle.cs | 104 +- .../Helpers/ScaleHelper.cs | 4 +- .../Helpers/ToolStripItemEndisabler.cs | 214 +- .../Memento/AddElementMemento.cs | 148 +- .../Memento/AddElementsMemento.cs | 144 +- .../Memento/ChangeFieldHolderMemento.cs | 152 +- .../Memento/DeleteElementMemento.cs | 162 +- .../Memento/DeleteElementsMemento.cs | 140 +- .../DrawableContainerBoundsChangeMemento.cs | 222 +- .../Memento/SurfaceBackgroundChangeMemento.cs | 166 +- .../Memento/TextChangeMemento.cs | 142 +- .../icons/balloon.png | Bin .../icons/notification-counter-01.png | Bin .../icons/notification-counter-02.png | Bin .../icons/notification-counter-03.png | Bin .../icons/notification-counter-04.png | Bin .../icons/notification-counter-05.png | Bin .../icons/notification-counter-06.png | Bin .../icons/notification-counter-07.png | Bin .../icons/notification-counter-08.png | Bin .../icons/notification-counter-09.png | Bin .../icons/notification-counter-10.png | Bin .../icons/notification-counter-11.png | Bin .../icons/notification-counter-12.png | Bin .../icons/notification-counter-13.png | Bin .../icons/notification-counter-14.png | Bin .../icons/notification-counter-15.png | Bin .../icons/notification-counter-16.png | Bin .../icons/notification-counter-17.png | Bin .../icons/notification-counter-18.png | Bin .../icons/notification-counter-19.png | Bin .../icons/notification-counter-20-plus.png | Bin .../icons/notification-counter-20.png | Bin .../icons/resize.png | Bin src/Greenshot.Plugin.Box/BoxConfiguration.cs | 1 + .../ConfluenceConfiguration.cs | 1 + .../Forms/ConfluenceConfigurationForm.xaml | 3 +- .../DropboxPluginConfiguration.cs | 1 + .../FlickrConfiguration.cs | 1 + .../GooglePhotosConfiguration.cs | 1 + .../ImgurConfiguration.cs | 1 + .../JiraConfiguration.cs | 1 + .../OfficeExport/OneNoteExporter.cs | 1 + .../PhotobucketConfiguration.cs | 1 + .../Destinations/Win10ShareDestination.cs | 1 + src/Greenshot.sln | 10 + .../Destinations/ClipboardDestination.cs | 8 +- .../Destinations/EmailDestination.cs | 6 +- src/Greenshot/Destinations/FileDestination.cs | 6 +- .../Destinations/FileWithDialogDestination.cs | 20 +- .../Destinations/PickerDestination.cs | 5 +- .../Destinations/PrinterDestination.cs | 4 +- src/Greenshot/Forms/AboutForm.cs | 14 +- src/Greenshot/Forms/CaptureForm.cs | 183 +- src/Greenshot/Forms/MainForm.cs | 129 +- src/Greenshot/Forms/SettingsForm.Designer.cs | 5 +- src/Greenshot/Forms/SettingsForm.cs | 8 +- src/Greenshot/Greenshot.csproj | 7 +- src/Greenshot/GreenshotMain.cs | 1 + src/Greenshot/Helpers/CaptureHelper.cs | 78 +- src/Greenshot/Helpers/PrintHelper.cs | 2 + src/Greenshot/Helpers/ProcessorHelper.cs | 83 - 165 files changed, 20167 insertions(+), 19709 deletions(-) rename src/{Greenshot/Helpers => Greenshot.Base/Core}/DestinationHelper.cs (63%) create mode 100644 src/Greenshot.Base/Core/Enums/ClickActions.cs create mode 100644 src/Greenshot.Base/Core/Enums/ClipboardFormat.cs create mode 100644 src/Greenshot.Base/Core/Enums/OutputFormat.cs create mode 100644 src/Greenshot.Base/Core/Enums/WindowCaptureMode.cs rename src/{Greenshot => Greenshot.Base}/Help/HelpFileLoader.cs (96%) create mode 100644 src/Greenshot.Base/Interfaces/ICaptureHelper.cs create mode 100644 src/Greenshot.Base/Interfaces/IGreenshotMainForm.cs create mode 100644 src/Greenshot.Base/WellKnownDestinations.cs rename src/{Greenshot => Greenshot.Editor}/Configuration/EditorConfiguration.cs (96%) create mode 100644 src/Greenshot.Editor/Configuration/LanguageKeys.cs rename src/{Greenshot => Greenshot.Editor}/Controls/BindableToolStripButton.cs (95%) rename src/{Greenshot => Greenshot.Editor}/Controls/BindableToolStripComboBox.cs (96%) rename src/{Greenshot => Greenshot.Editor}/Controls/BindableToolStripDropDownButton.cs (96%) rename src/{Greenshot => Greenshot.Editor}/Controls/ColorButton.cs (94%) rename src/{Greenshot => Greenshot.Editor}/Controls/CustomToolStripProfessionalRenderer.cs (96%) rename src/{Greenshot => Greenshot.Editor}/Controls/FontFamilyComboBox.cs (97%) rename src/{Greenshot => Greenshot.Editor}/Controls/MenuStripEx.cs (96%) rename src/{Greenshot => Greenshot.Editor}/Controls/NonJumpingPanel.cs (96%) rename src/{Greenshot => Greenshot.Editor}/Controls/Pipette.cs (95%) rename src/{Greenshot => Greenshot.Editor}/Controls/ToolStripColorButton.cs (94%) rename src/{Greenshot => Greenshot.Editor}/Controls/ToolStripEx.cs (95%) rename src/{Greenshot => Greenshot.Editor}/Controls/ToolStripNumericUpDown.cs (96%) rename src/{Greenshot => Greenshot.Editor}/Destinations/EditorDestination.cs (95%) rename src/{Greenshot => Greenshot.Editor}/Drawing/Adorners/AbstractAdorner.cs (96%) rename src/{Greenshot => Greenshot.Editor}/Drawing/Adorners/MoveAdorner.cs (96%) rename src/{Greenshot => Greenshot.Editor}/Drawing/Adorners/ResizeAdorner.cs (96%) rename src/{Greenshot => Greenshot.Editor}/Drawing/Adorners/TargetAdorner.cs (98%) rename src/{Greenshot => Greenshot.Editor}/Drawing/ArrowContainer.cs (96%) rename src/{Greenshot => Greenshot.Editor}/Drawing/CropContainer.cs (95%) rename src/{Greenshot => Greenshot.Editor}/Drawing/CursorContainer.cs (96%) rename src/{Greenshot => Greenshot.Editor}/Drawing/DrawableContainer.cs (95%) rename src/{Greenshot => Greenshot.Editor}/Drawing/DrawableContainerList.cs (96%) rename src/{Greenshot => Greenshot.Editor}/Drawing/EllipseContainer.cs (96%) rename src/{Greenshot => Greenshot.Editor}/Drawing/Fields/AbstractFieldHolder.cs (96%) rename src/{Greenshot => Greenshot.Editor}/Drawing/Fields/AbstractFieldHolderWithChildren.cs (96%) rename src/{Greenshot => Greenshot.Editor}/Drawing/Fields/Binding/AbstractBindingConverter.cs (94%) rename src/{Greenshot => Greenshot.Editor}/Drawing/Fields/Binding/BidirectionalBinding.cs (97%) rename src/{Greenshot => Greenshot.Editor}/Drawing/Fields/Binding/DecimalDoublePercentageConverter.cs (94%) rename src/{Greenshot => Greenshot.Editor}/Drawing/Fields/Binding/DecimalFloatConverter.cs (94%) rename src/{Greenshot => Greenshot.Editor}/Drawing/Fields/Binding/DecimalIntConverter.cs (94%) rename src/{Greenshot => Greenshot.Editor}/Drawing/Fields/Binding/IBindingConverter.cs (94%) rename src/{Greenshot => Greenshot.Editor}/Drawing/Fields/Binding/IBindingValidator.cs (94%) rename src/{Greenshot => Greenshot.Editor}/Drawing/Fields/Binding/NotNullValidator.cs (94%) rename src/{Greenshot => Greenshot.Editor}/Drawing/Fields/Field.cs (96%) rename src/{Greenshot => Greenshot.Editor}/Drawing/Fields/FieldAggregator.cs (96%) rename src/{Greenshot => Greenshot.Editor}/Drawing/Fields/FieldType.cs (96%) rename src/{Greenshot => Greenshot.Editor}/Drawing/FilterContainer.cs (95%) rename src/{Greenshot => Greenshot.Editor}/Drawing/Filters/AbstractFilter.cs (94%) rename src/{Greenshot => Greenshot.Editor}/Drawing/Filters/BlurFilter.cs (94%) rename src/{Greenshot => Greenshot.Editor}/Drawing/Filters/BrightnessFilter.cs (94%) rename src/{Greenshot => Greenshot.Editor}/Drawing/Filters/GrayscaleFilter.cs (95%) rename src/{Greenshot => Greenshot.Editor}/Drawing/Filters/HighlightFilter.cs (95%) rename src/{Greenshot => Greenshot.Editor}/Drawing/Filters/IFilter.cs (94%) rename src/{Greenshot => Greenshot.Editor}/Drawing/Filters/MagnifierFilter.cs (95%) rename src/{Greenshot => Greenshot.Editor}/Drawing/Filters/PixelizationFilter.cs (95%) rename src/{Greenshot => Greenshot.Editor}/Drawing/FreehandContainer.cs (96%) rename src/{Greenshot => Greenshot.Editor}/Drawing/HighlightContainer.cs (94%) rename src/{Greenshot => Greenshot.Editor}/Drawing/IconContainer.cs (95%) rename src/{Greenshot => Greenshot.Editor}/Drawing/ImageContainer.cs (96%) rename src/{Greenshot => Greenshot.Editor}/Drawing/LineContainer.cs (94%) rename src/{Greenshot => Greenshot.Editor}/Drawing/ObfuscateContainer.cs (93%) rename src/{Greenshot => Greenshot.Editor}/Drawing/Positions.cs (94%) rename src/{Greenshot => Greenshot.Editor}/Drawing/RectangleContainer.cs (96%) rename src/{Greenshot => Greenshot.Editor}/Drawing/SpeechbubbleContainer.cs (96%) rename src/{Greenshot => Greenshot.Editor}/Drawing/StepLabelContainer.cs (96%) rename src/{Greenshot => Greenshot.Editor}/Drawing/Surface.cs (97%) rename src/{Greenshot => Greenshot.Editor}/Drawing/TextContainer.cs (96%) rename src/{Greenshot => Greenshot.Editor}/Forms/ColorDialog.Designer.cs (95%) rename src/{Greenshot => Greenshot.Editor}/Forms/ColorDialog.cs (95%) rename src/{Greenshot => Greenshot.Editor}/Forms/ColorDialog.resx (98%) rename src/{Greenshot => Greenshot.Editor}/Forms/ColorPickerToolStripButton.cs (96%) rename src/{Greenshot => Greenshot.Editor}/Forms/DropShadowSettingsForm.Designer.cs (96%) rename src/{Greenshot => Greenshot.Editor}/Forms/DropShadowSettingsForm.cs (93%) create mode 100644 src/Greenshot.Editor/Forms/EditorForm.cs rename src/{Greenshot => Greenshot.Editor}/Forms/ImageEditorForm.Designer.cs (93%) rename src/{Greenshot => Greenshot.Editor}/Forms/ImageEditorForm.cs (96%) rename src/{Greenshot => Greenshot.Editor}/Forms/ImageEditorForm.resx (98%) rename src/{Greenshot => Greenshot.Editor}/Forms/MovableShowColorForm.Designer.cs (97%) rename src/{Greenshot => Greenshot.Editor}/Forms/MovableShowColorForm.cs (96%) rename src/{Greenshot => Greenshot.Editor}/Forms/ResizeSettingsForm.Designer.cs (96%) rename src/{Greenshot => Greenshot.Editor}/Forms/ResizeSettingsForm.cs (95%) rename src/{Greenshot => Greenshot.Editor}/Forms/TornEdgeSettingsForm.Designer.cs (96%) rename src/{Greenshot => Greenshot.Editor}/Forms/TornEdgeSettingsForm.cs (95%) create mode 100644 src/Greenshot.Editor/Greenshot.Editor.csproj rename src/{Greenshot => Greenshot.Editor}/Helpers/Colors.cs (95%) rename src/{Greenshot => Greenshot.Editor}/Helpers/GeometryHelper.cs (96%) rename src/{Greenshot => Greenshot.Editor}/Helpers/GuiRectangle.cs (95%) rename src/{Greenshot => Greenshot.Editor}/Helpers/ScaleHelper.cs (99%) rename src/{Greenshot => Greenshot.Editor}/Helpers/ToolStripItemEndisabler.cs (96%) rename src/{Greenshot => Greenshot.Editor}/Memento/AddElementMemento.cs (94%) rename src/{Greenshot => Greenshot.Editor}/Memento/AddElementsMemento.cs (94%) rename src/{Greenshot => Greenshot.Editor}/Memento/ChangeFieldHolderMemento.cs (96%) rename src/{Greenshot => Greenshot.Editor}/Memento/DeleteElementMemento.cs (94%) rename src/{Greenshot => Greenshot.Editor}/Memento/DeleteElementsMemento.cs (94%) rename src/{Greenshot => Greenshot.Editor}/Memento/DrawableContainerBoundsChangeMemento.cs (95%) rename src/{Greenshot => Greenshot.Editor}/Memento/SurfaceBackgroundChangeMemento.cs (94%) rename src/{Greenshot => Greenshot.Editor}/Memento/TextChangeMemento.cs (94%) rename src/{Greenshot => Greenshot.Editor}/icons/balloon.png (100%) rename src/{Greenshot => Greenshot.Editor}/icons/notification-counter-01.png (100%) rename src/{Greenshot => Greenshot.Editor}/icons/notification-counter-02.png (100%) rename src/{Greenshot => Greenshot.Editor}/icons/notification-counter-03.png (100%) rename src/{Greenshot => Greenshot.Editor}/icons/notification-counter-04.png (100%) rename src/{Greenshot => Greenshot.Editor}/icons/notification-counter-05.png (100%) rename src/{Greenshot => Greenshot.Editor}/icons/notification-counter-06.png (100%) rename src/{Greenshot => Greenshot.Editor}/icons/notification-counter-07.png (100%) rename src/{Greenshot => Greenshot.Editor}/icons/notification-counter-08.png (100%) rename src/{Greenshot => Greenshot.Editor}/icons/notification-counter-09.png (100%) rename src/{Greenshot => Greenshot.Editor}/icons/notification-counter-10.png (100%) rename src/{Greenshot => Greenshot.Editor}/icons/notification-counter-11.png (100%) rename src/{Greenshot => Greenshot.Editor}/icons/notification-counter-12.png (100%) rename src/{Greenshot => Greenshot.Editor}/icons/notification-counter-13.png (100%) rename src/{Greenshot => Greenshot.Editor}/icons/notification-counter-14.png (100%) rename src/{Greenshot => Greenshot.Editor}/icons/notification-counter-15.png (100%) rename src/{Greenshot => Greenshot.Editor}/icons/notification-counter-16.png (100%) rename src/{Greenshot => Greenshot.Editor}/icons/notification-counter-17.png (100%) rename src/{Greenshot => Greenshot.Editor}/icons/notification-counter-18.png (100%) rename src/{Greenshot => Greenshot.Editor}/icons/notification-counter-19.png (100%) rename src/{Greenshot => Greenshot.Editor}/icons/notification-counter-20-plus.png (100%) rename src/{Greenshot => Greenshot.Editor}/icons/notification-counter-20.png (100%) rename src/{Greenshot => Greenshot.Editor}/icons/resize.png (100%) delete mode 100644 src/Greenshot/Helpers/ProcessorHelper.cs diff --git a/installer/innosetup/setup.iss b/installer/innosetup/setup.iss index 91ce63bfe..c08588ca5 100644 --- a/installer/innosetup/setup.iss +++ b/installer/innosetup/setup.iss @@ -21,6 +21,7 @@ [Files] Source: {#ReleaseDir}\Greenshot.exe; DestDir: {app}; Components: greenshot; Flags: overwritereadonly ignoreversion replacesameversion Source: {#ReleaseDir}\Greenshot.Base.dll; DestDir: {app}; Components: greenshot; Flags: overwritereadonly ignoreversion replacesameversion +Source: {#ReleaseDir}\Greenshot.Editor.dll; DestDir: {app}; Components: greenshot; Flags: overwritereadonly ignoreversion replacesameversion Source: {#ReleaseDir}\Greenshot.exe.config; DestDir: {app}; Components: greenshot; Flags: overwritereadonly ignoreversion replacesameversion Source: {#ReleaseDir}\log4net.dll; DestDir: {app}; Components: greenshot; Flags: overwritereadonly ignoreversion replacesameversion Source: {#ReleaseDir}\Dapplo.Http*.dll; DestDir: {app}; Components: greenshot; Flags: overwritereadonly ignoreversion replacesameversion diff --git a/src/Directory.Build.props b/src/Directory.Build.props index 77faddc33..e56ca9d63 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -108,7 +108,7 @@ - + /// - private void timer_Tick(object sender, EventArgs e) + private void Timer_Tick(object sender, EventArgs e) { try { @@ -125,7 +131,7 @@ namespace Greenshot.Base.Controls } catch (Exception ex) { - Log.Warn("An exception occured while animating:", ex); + Log.Warn("An exception occurred while animating:", ex); } } diff --git a/src/Greenshot.Base/Controls/QualityDialog.cs b/src/Greenshot.Base/Controls/QualityDialog.cs index 6482a291d..2212ab35b 100644 --- a/src/Greenshot.Base/Controls/QualityDialog.cs +++ b/src/Greenshot.Base/Controls/QualityDialog.cs @@ -21,6 +21,7 @@ using System; using Greenshot.Base.Core; +using Greenshot.Base.Core.Enums; using Greenshot.Base.IniFile; using Greenshot.Base.Interfaces.Plugin; diff --git a/src/Greenshot.Base/Controls/SaveImageFileDialog.cs b/src/Greenshot.Base/Controls/SaveImageFileDialog.cs index 87185bb55..3c9b2780e 100644 --- a/src/Greenshot.Base/Controls/SaveImageFileDialog.cs +++ b/src/Greenshot.Base/Controls/SaveImageFileDialog.cs @@ -23,6 +23,7 @@ using System; using System.IO; using System.Windows.Forms; using Greenshot.Base.Core; +using Greenshot.Base.Core.Enums; using Greenshot.Base.IniFile; using Greenshot.Base.Interfaces; using log4net; diff --git a/src/Greenshot.Base/Controls/ThumbnailForm.cs b/src/Greenshot.Base/Controls/ThumbnailForm.cs index 2b5882e36..c8f22f1f1 100644 --- a/src/Greenshot.Base/Controls/ThumbnailForm.cs +++ b/src/Greenshot.Base/Controls/ThumbnailForm.cs @@ -23,6 +23,7 @@ using System; using System.Drawing; using System.Windows.Forms; using Greenshot.Base.Core; +using Greenshot.Base.Core.Enums; using Greenshot.Base.IniFile; using Greenshot.Base.UnmanagedHelpers; using Greenshot.Base.UnmanagedHelpers.Enums; diff --git a/src/Greenshot.Base/Core/AnimationHelpers.cs b/src/Greenshot.Base/Core/AnimationHelpers.cs index ba4769ffa..8b58a1c34 100644 --- a/src/Greenshot.Base/Core/AnimationHelpers.cs +++ b/src/Greenshot.Base/Core/AnimationHelpers.cs @@ -238,19 +238,19 @@ namespace Greenshot.Base.Core return true; } - if (_queue.Count > 0) + if (_queue.Count <= 0) { - First = Current; - CurrentFrameNr = 0; - AnimationLeg nextLeg = _queue.Dequeue(); - Last = nextLeg.Destination; - Frames = nextLeg.Frames; - EasingType = nextLeg.EasingType; - EasingMode = nextLeg.EasingMode; - return true; + return false; } + First = Current; + CurrentFrameNr = 0; + AnimationLeg nextLeg = _queue.Dequeue(); + Last = nextLeg.Destination; + Frames = nextLeg.Frames; + EasingType = nextLeg.EasingType; + EasingMode = nextLeg.EasingMode; + return true; - return false; } } @@ -303,20 +303,21 @@ namespace Greenshot.Base.Core /// Rectangle public override Rectangle Next() { - if (NextFrame) + if (!NextFrame) { - double easingValue = EasingValue; - double dx = Last.X - First.X; - double dy = Last.Y - First.Y; - - int x = First.X + (int) (easingValue * dx); - int y = First.Y + (int) (easingValue * dy); - double dw = Last.Width - First.Width; - double dh = Last.Height - First.Height; - int width = First.Width + (int) (easingValue * dw); - int height = First.Height + (int) (easingValue * dh); - Current = new Rectangle(x, y, width, height); + return Current; } + double easingValue = EasingValue; + double dx = Last.X - First.X; + double dy = Last.Y - First.Y; + + int x = First.X + (int) (easingValue * dx); + int y = First.Y + (int) (easingValue * dy); + double dw = Last.Width - First.Width; + double dh = Last.Height - First.Height; + int width = First.Width + (int) (easingValue * dw); + int height = First.Height + (int) (easingValue * dh); + Current = new Rectangle(x, y, width, height); return Current; } diff --git a/src/Greenshot.Base/Core/Capture.cs b/src/Greenshot.Base/Core/Capture.cs index efd0ed6c7..347cd0067 100644 --- a/src/Greenshot.Base/Core/Capture.cs +++ b/src/Greenshot.Base/Core/Capture.cs @@ -80,7 +80,7 @@ namespace Greenshot.Base.Core } finally { - // Always dispose, even when a exception occured + // Always dispose, even when a exception occurred value.Dispose(); } } diff --git a/src/Greenshot.Base/Core/ClipboardHelper.cs b/src/Greenshot.Base/Core/ClipboardHelper.cs index 5275661fc..7da99f216 100644 --- a/src/Greenshot.Base/Core/ClipboardHelper.cs +++ b/src/Greenshot.Base/Core/ClipboardHelper.cs @@ -30,6 +30,7 @@ using System.Runtime.InteropServices; using System.Text; using System.Threading; using System.Windows.Forms; +using Greenshot.Base.Core.Enums; using Greenshot.Base.IniFile; using Greenshot.Base.Interfaces; using Greenshot.Base.Interfaces.Plugin; diff --git a/src/Greenshot.Base/Core/CoreConfiguration.cs b/src/Greenshot.Base/Core/CoreConfiguration.cs index d34a10e33..9c1fbd913 100644 --- a/src/Greenshot.Base/Core/CoreConfiguration.cs +++ b/src/Greenshot.Base/Core/CoreConfiguration.cs @@ -26,57 +26,12 @@ using System.Drawing; using System.IO; using System.Reflection; using System.Windows.Forms; +using Greenshot.Base.Core.Enums; using Greenshot.Base.IniFile; using Greenshot.Base.Interfaces; namespace Greenshot.Base.Core { - public enum ClipboardFormat - { - PNG, - DIB, - HTML, - HTMLDATAURL, - BITMAP, - DIBV5 - } - - public enum OutputFormat - { - bmp, - gif, - jpg, - png, - tiff, - greenshot, - ico - } - - public enum WindowCaptureMode - { - Screen, - GDI, - Aero, - AeroTransparent, - Auto - } - - public enum BuildStates - { - UNSTABLE, - RELEASE_CANDIDATE, - RELEASE - } - - public enum ClickActions - { - DO_NOTHING, - OPEN_LAST_IN_EXPLORER, - OPEN_LAST_IN_EDITOR, - OPEN_SETTINGS, - SHOW_CONTEXT_MENU - } - /// /// Description of CoreConfiguration. /// diff --git a/src/Greenshot/Helpers/DestinationHelper.cs b/src/Greenshot.Base/Core/DestinationHelper.cs similarity index 63% rename from src/Greenshot/Helpers/DestinationHelper.cs rename to src/Greenshot.Base/Core/DestinationHelper.cs index ca4fdc44b..641b29bbc 100644 --- a/src/Greenshot/Helpers/DestinationHelper.cs +++ b/src/Greenshot.Base/Core/DestinationHelper.cs @@ -1,132 +1,101 @@ -/* - * Greenshot - a free and open source screenshot tool - * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom - * - * For more information see: https://getgreenshot.org/ - * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 1 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -using System; -using System.Collections.Generic; -using System.Linq; -using Greenshot.Base.Core; -using Greenshot.Base.IniFile; -using Greenshot.Base.Interfaces; -using log4net; - -namespace Greenshot.Helpers -{ - /// - /// Description of DestinationHelper. - /// - public static class DestinationHelper - { - private static readonly ILog Log = LogManager.GetLogger(typeof(DestinationHelper)); - private static readonly CoreConfiguration CoreConfig = IniConfig.GetIniSection(); - - /// - /// Initialize the internal destinations - /// - public static void RegisterInternalDestinations() - { - foreach (Type destinationType in InterfaceUtils.GetSubclassesOf(typeof(IDestination), true)) - { - // Only take our own - if (!"Greenshot.Destinations".Equals(destinationType.Namespace)) - { - continue; - } - - if (destinationType.IsAbstract) continue; - - IDestination destination; - try - { - destination = (IDestination) Activator.CreateInstance(destinationType); - } - catch (Exception e) - { - Log.ErrorFormat("Can't create instance of {0}", destinationType); - Log.Error(e); - continue; - } - - if (destination.IsActive) - { - Log.DebugFormat("Found destination {0} with designation {1}", destinationType.Name, destination.Designation); - SimpleServiceProvider.Current.AddService(destination); - } - else - { - Log.DebugFormat("Ignoring destination {0} with designation {1}", destinationType.Name, destination.Designation); - } - } - } - - /// - /// Method to get all the destinations from the plugins - /// - /// List of IDestination - public static IEnumerable GetAllDestinations() - { - return SimpleServiceProvider.Current.GetAllInstances() - .Where(destination => destination.IsActive) - .Where(destination => CoreConfig.ExcludeDestinations == null || - !CoreConfig.ExcludeDestinations.Contains(destination.Designation)).OrderBy(p => p.Priority).ThenBy(p => p.Description); - } - - /// - /// Get a destination by a designation - /// - /// Designation of the destination - /// IDestination or null - public static IDestination GetDestination(string designation) - { - if (designation == null) - { - return null; - } - - foreach (IDestination destination in GetAllDestinations()) - { - if (designation.Equals(destination.Designation)) - { - return destination; - } - } - - return null; - } - - /// - /// A simple helper method which will call ExportCapture for the destination with the specified designation - /// - /// - /// - /// - /// - public static ExportInformation ExportCapture(bool manuallyInitiated, string designation, ISurface surface, ICaptureDetails captureDetails) - { - IDestination destination = GetDestination(designation); - if (destination != null && destination.IsActive) - { - return destination.ExportCapture(manuallyInitiated, surface, captureDetails); - } - - return null; - } - } +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using System.Collections.Generic; +using System.Linq; +using Greenshot.Base.IniFile; +using Greenshot.Base.Interfaces; + +namespace Greenshot.Base.Core +{ + /// + /// Helper class to simplify working with destinations. + /// + public static class DestinationHelper + { + private static readonly CoreConfiguration CoreConfig = IniConfig.GetIniSection(); + + /// + /// Method to get all the destinations from the plugins + /// + /// List of IDestination + public static IEnumerable GetAllDestinations() + { + return SimpleServiceProvider.Current.GetAllInstances() + .Where(destination => destination.IsActive) + .Where(destination => CoreConfig.ExcludeDestinations == null || + !CoreConfig.ExcludeDestinations.Contains(destination.Designation)).OrderBy(p => p.Priority).ThenBy(p => p.Description); + } + + /// + /// Get a destination by a designation + /// + /// Designation of the destination + /// IDestination or null + public static IDestination GetDestination(string designation) + { + if (designation == null) + { + return null; + } + + foreach (IDestination destination in GetAllDestinations()) + { + if (designation.Equals(destination.Designation)) + { + return destination; + } + } + + return null; + } + + /// + /// A simple helper method which will call ExportCapture for the destination with the specified designation + /// + /// + /// WellKnownDestinations + /// ISurface + /// ICaptureDetails + public static ExportInformation ExportCapture(bool manuallyInitiated, WellKnownDestinations designation, ISurface surface, ICaptureDetails captureDetails) + { + return ExportCapture(manuallyInitiated, designation.ToString(), surface, captureDetails); + } + + /// + /// A simple helper method which will call ExportCapture for the destination with the specified designation + /// + /// bool + /// string + /// ISurface + /// ICaptureDetails + public static ExportInformation ExportCapture(bool manuallyInitiated, string designation, ISurface surface, ICaptureDetails captureDetails) + { + IDestination destination = GetDestination(designation); + if (destination != null && destination.IsActive) + { + return destination.ExportCapture(manuallyInitiated, surface, captureDetails); + } + + return null; + } + } } \ No newline at end of file diff --git a/src/Greenshot.Base/Core/Enums/ClickActions.cs b/src/Greenshot.Base/Core/Enums/ClickActions.cs new file mode 100644 index 000000000..7c7fb5ee8 --- /dev/null +++ b/src/Greenshot.Base/Core/Enums/ClickActions.cs @@ -0,0 +1,35 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/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 . + */ + +namespace Greenshot.Base.Core.Enums +{ + /// + /// This are the defined actions which can be initiated when the menu is clicked + /// + public enum ClickActions + { + DO_NOTHING, + OPEN_LAST_IN_EXPLORER, + OPEN_LAST_IN_EDITOR, + OPEN_SETTINGS, + SHOW_CONTEXT_MENU + } +} \ No newline at end of file diff --git a/src/Greenshot.Base/Core/Enums/ClipboardFormat.cs b/src/Greenshot.Base/Core/Enums/ClipboardFormat.cs new file mode 100644 index 000000000..56062d820 --- /dev/null +++ b/src/Greenshot.Base/Core/Enums/ClipboardFormat.cs @@ -0,0 +1,36 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/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 . + */ + +namespace Greenshot.Base.Core.Enums +{ + /// + /// These are the supported clipboard formats + /// + public enum ClipboardFormat + { + PNG, + DIB, + HTML, + HTMLDATAURL, + BITMAP, + DIBV5 + } +} \ No newline at end of file diff --git a/src/Greenshot.Base/Core/Enums/OutputFormat.cs b/src/Greenshot.Base/Core/Enums/OutputFormat.cs new file mode 100644 index 000000000..64d8a614b --- /dev/null +++ b/src/Greenshot.Base/Core/Enums/OutputFormat.cs @@ -0,0 +1,37 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/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 . + */ + +namespace Greenshot.Base.Core.Enums +{ + /// + /// These are the supported output formats + /// + public enum OutputFormat + { + bmp, + gif, + jpg, + png, + tiff, + greenshot, + ico + } +} \ No newline at end of file diff --git a/src/Greenshot.Base/Core/Enums/WindowCaptureMode.cs b/src/Greenshot.Base/Core/Enums/WindowCaptureMode.cs new file mode 100644 index 000000000..a350e1b14 --- /dev/null +++ b/src/Greenshot.Base/Core/Enums/WindowCaptureMode.cs @@ -0,0 +1,35 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/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 . + */ + +namespace Greenshot.Base.Core.Enums +{ + /// + /// These are the possible window capture modes + /// + public enum WindowCaptureMode + { + Screen, + GDI, + Aero, + AeroTransparent, + Auto + } +} \ No newline at end of file diff --git a/src/Greenshot.Base/Core/FilenameHelper.cs b/src/Greenshot.Base/Core/FilenameHelper.cs index 411fee30f..4c6864547 100644 --- a/src/Greenshot.Base/Core/FilenameHelper.cs +++ b/src/Greenshot.Base/Core/FilenameHelper.cs @@ -25,6 +25,7 @@ using System.Collections.Generic; using System.IO; using System.Text.RegularExpressions; using System.Windows.Forms; +using Greenshot.Base.Core.Enums; using Greenshot.Base.IniFile; using Greenshot.Base.Interfaces; using log4net; diff --git a/src/Greenshot.Base/Core/ImageOutput.cs b/src/Greenshot.Base/Core/ImageOutput.cs index 22ac769d5..39c4896c1 100644 --- a/src/Greenshot.Base/Core/ImageOutput.cs +++ b/src/Greenshot.Base/Core/ImageOutput.cs @@ -32,6 +32,7 @@ using System.Text; using System.Text.RegularExpressions; using System.Windows.Forms; using Greenshot.Base.Controls; +using Greenshot.Base.Core.Enums; using Greenshot.Base.IniFile; using Greenshot.Base.Interfaces; using Greenshot.Base.Interfaces.Plugin; diff --git a/src/Greenshot.Base/Core/WindowDetails.cs b/src/Greenshot.Base/Core/WindowDetails.cs index a23c938fd..91ee52de1 100644 --- a/src/Greenshot.Base/Core/WindowDetails.cs +++ b/src/Greenshot.Base/Core/WindowDetails.cs @@ -7,6 +7,7 @@ using System.Linq; using System.Runtime.InteropServices; using System.Text; using System.Windows.Forms; +using Greenshot.Base.Core.Enums; using Greenshot.Base.IniFile; using Greenshot.Base.Interfaces; using Greenshot.Base.Interop; diff --git a/src/Greenshot/Help/HelpFileLoader.cs b/src/Greenshot.Base/Help/HelpFileLoader.cs similarity index 96% rename from src/Greenshot/Help/HelpFileLoader.cs rename to src/Greenshot.Base/Help/HelpFileLoader.cs index 0f9d46ac6..2c9b04b4d 100644 --- a/src/Greenshot/Help/HelpFileLoader.cs +++ b/src/Greenshot.Base/Help/HelpFileLoader.cs @@ -1,101 +1,101 @@ -/* - * Greenshot - a free and open source screenshot tool - * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom - * - * For more information see: https://getgreenshot.org/ - * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 1 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -using System.Diagnostics; -using System.Net; -using Greenshot.Base.Core; -using log4net; - -namespace Greenshot.Help -{ - /// - /// Description of HelpFileLoader. - /// - public static class HelpFileLoader - { - private static readonly ILog Log = LogManager.GetLogger(typeof(HelpFileLoader)); - - private const string ExtHelpUrl = @"https://getgreenshot.org/help/"; - - public static void LoadHelp() - { - string uri = FindOnlineHelpUrl(Language.CurrentLanguage) ?? Language.HelpFilePath; - Process.Start(uri); - } - - /// URL of help file in selected ietf, or (if not present) default ietf, or null (if not present, too. probably indicating that there is no internet connection) - private static string FindOnlineHelpUrl(string currentIETF) - { - string ret = null; - - string extHelpUrlForCurrrentIETF = ExtHelpUrl; - - if (!currentIETF.Equals("en-US")) - { - extHelpUrlForCurrrentIETF += currentIETF.ToLower() + "/"; - } - - HttpStatusCode? httpStatusCode = GetHttpStatus(extHelpUrlForCurrrentIETF); - if (httpStatusCode == HttpStatusCode.OK) - { - ret = extHelpUrlForCurrrentIETF; - } - else if (httpStatusCode != null && !extHelpUrlForCurrrentIETF.Equals(ExtHelpUrl)) - { - Log.DebugFormat("Localized online help not found at {0}, will try {1} as fallback", extHelpUrlForCurrrentIETF, ExtHelpUrl); - httpStatusCode = GetHttpStatus(ExtHelpUrl); - if (httpStatusCode == HttpStatusCode.OK) - { - ret = ExtHelpUrl; - } - else - { - Log.WarnFormat("{0} returned status {1}", ExtHelpUrl, httpStatusCode); - } - } - else if (httpStatusCode == null) - { - Log.Info("Internet connection does not seem to be available, will load help from file system."); - } - - return ret; - } - - /// - /// Retrieves HTTP status for a given url. - /// - /// URL for which the HTTP status is to be checked - /// An HTTP status code, or null if there is none (probably indicating that there is no internet connection available - private static HttpStatusCode? GetHttpStatus(string url) - { - try - { - HttpWebRequest req = NetworkHelper.CreateWebRequest(url); - using HttpWebResponse res = (HttpWebResponse) req.GetResponse(); - return res.StatusCode; - } - catch (WebException e) - { - return ((HttpWebResponse) e.Response)?.StatusCode; - } - } - } +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using System.Diagnostics; +using System.Net; +using Greenshot.Base.Core; +using log4net; + +namespace Greenshot.Base.Help +{ + /// + /// Description of HelpFileLoader. + /// + public static class HelpFileLoader + { + private static readonly ILog Log = LogManager.GetLogger(typeof(HelpFileLoader)); + + private const string ExtHelpUrl = @"https://getgreenshot.org/help/"; + + public static void LoadHelp() + { + string uri = FindOnlineHelpUrl(Language.CurrentLanguage) ?? Language.HelpFilePath; + Process.Start(uri); + } + + /// URL of help file in selected ietf, or (if not present) default ietf, or null (if not present, too. probably indicating that there is no internet connection) + private static string FindOnlineHelpUrl(string currentIETF) + { + string ret = null; + + string extHelpUrlForCurrrentIETF = ExtHelpUrl; + + if (!currentIETF.Equals("en-US")) + { + extHelpUrlForCurrrentIETF += currentIETF.ToLower() + "/"; + } + + HttpStatusCode? httpStatusCode = GetHttpStatus(extHelpUrlForCurrrentIETF); + if (httpStatusCode == HttpStatusCode.OK) + { + ret = extHelpUrlForCurrrentIETF; + } + else if (httpStatusCode != null && !extHelpUrlForCurrrentIETF.Equals(ExtHelpUrl)) + { + Log.DebugFormat("Localized online help not found at {0}, will try {1} as fallback", extHelpUrlForCurrrentIETF, ExtHelpUrl); + httpStatusCode = GetHttpStatus(ExtHelpUrl); + if (httpStatusCode == HttpStatusCode.OK) + { + ret = ExtHelpUrl; + } + else + { + Log.WarnFormat("{0} returned status {1}", ExtHelpUrl, httpStatusCode); + } + } + else if (httpStatusCode == null) + { + Log.Info("Internet connection does not seem to be available, will load help from file system."); + } + + return ret; + } + + /// + /// Retrieves HTTP status for a given url. + /// + /// URL for which the HTTP status is to be checked + /// An HTTP status code, or null if there is none (probably indicating that there is no internet connection available + private static HttpStatusCode? GetHttpStatus(string url) + { + try + { + HttpWebRequest req = NetworkHelper.CreateWebRequest(url); + using HttpWebResponse res = (HttpWebResponse) req.GetResponse(); + return res.StatusCode; + } + catch (WebException e) + { + return ((HttpWebResponse) e.Response)?.StatusCode; + } + } + } } \ No newline at end of file diff --git a/src/Greenshot.Base/Interfaces/ICaptureHelper.cs b/src/Greenshot.Base/Interfaces/ICaptureHelper.cs new file mode 100644 index 000000000..9b15032ad --- /dev/null +++ b/src/Greenshot.Base/Interfaces/ICaptureHelper.cs @@ -0,0 +1,48 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using Greenshot.Base.Core; +using Greenshot.Base.Core.Enums; + +namespace Greenshot.Base.Interfaces +{ + /// + /// This is a temporary solution to provide the CaptureHelper functionality via an interface + /// + public interface ICaptureHelper + { + /// + /// + /// + /// + /// + WindowDetails SelectCaptureWindow(WindowDetails windowToCapture); + + /// + /// Capture the specified window + /// + /// WindowDetails + /// ICapture + /// WindowCaptureMode + /// ICapture + ICapture CaptureWindow(WindowDetails windowToCapture, ICapture capture, WindowCaptureMode coreConfigurationWindowCaptureMode); + } +} diff --git a/src/Greenshot.Base/Interfaces/IGreenshotMainForm.cs b/src/Greenshot.Base/Interfaces/IGreenshotMainForm.cs new file mode 100644 index 000000000..cf25d48d8 --- /dev/null +++ b/src/Greenshot.Base/Interfaces/IGreenshotMainForm.cs @@ -0,0 +1,46 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using System; +using System.Windows.Forms; + +namespace Greenshot.Base.Interfaces +{ + public interface IGreenshotMainForm : IWin32Window + { + /// + /// Create the "capture window from list" list + /// + /// ToolStripMenuItem + /// EventHandler + void AddCaptureWindowMenuItems(ToolStripMenuItem menuItem, EventHandler eventHandler); + + /// + /// This is called indirectly from the context menu "Preferences" + /// + void ShowSetting(); + + /// + /// Show the about window + /// + void ShowAbout(); + } +} \ No newline at end of file diff --git a/src/Greenshot.Base/Interfaces/IServiceLocator.cs b/src/Greenshot.Base/Interfaces/IServiceLocator.cs index cd107e707..c22e3463b 100644 --- a/src/Greenshot.Base/Interfaces/IServiceLocator.cs +++ b/src/Greenshot.Base/Interfaces/IServiceLocator.cs @@ -1,14 +1,58 @@ -using System.Collections.Generic; +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +using System.Collections.Generic; namespace Greenshot.Base.Interfaces { + /// + /// This is the interface of the service locator + /// public interface IServiceLocator { + /// + /// Get all instances of the specified service + /// + /// Service to find + /// IEnumerable{TService} IEnumerable GetAllInstances(); + + /// + /// Get the only instance of the specified service + /// + /// Service to find + /// TService TService GetInstance(); + /// + /// Add one of more services to the registry + /// + /// Type of the service + /// One or more services which need to be added void AddService(params TService[] services); + /// + /// Add multiple services to the registry + /// + /// Type of the service + /// IEnumerable{TService} with services to add void AddService(IEnumerable services); } } \ No newline at end of file diff --git a/src/Greenshot.Base/Interfaces/Plugin/SurfaceOutputSettings.cs b/src/Greenshot.Base/Interfaces/Plugin/SurfaceOutputSettings.cs index 82946acdd..ef2a58e2f 100644 --- a/src/Greenshot.Base/Interfaces/Plugin/SurfaceOutputSettings.cs +++ b/src/Greenshot.Base/Interfaces/Plugin/SurfaceOutputSettings.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using Greenshot.Base.Core; +using Greenshot.Base.Core.Enums; using Greenshot.Base.Effects; using Greenshot.Base.IniFile; diff --git a/src/Greenshot.Base/UnmanagedHelpers/Kernel32.cs b/src/Greenshot.Base/UnmanagedHelpers/Kernel32.cs index 878242803..310864c75 100644 --- a/src/Greenshot.Base/UnmanagedHelpers/Kernel32.cs +++ b/src/Greenshot.Base/UnmanagedHelpers/Kernel32.cs @@ -64,6 +64,10 @@ namespace Greenshot.Base.UnmanagedHelpers [return: MarshalAs(UnmanagedType.Bool)] public static extern bool CloseHandle(IntPtr hObject); + [DllImport("kernel32", SetLastError = true, CharSet = CharSet.Auto)] + public static extern int GetPackageFullName(IntPtr hProcess, ref Int32 packageFullNameLength, StringBuilder fullName); + + /// /// Method to get the process path /// diff --git a/src/Greenshot.Base/WellKnownDestinations.cs b/src/Greenshot.Base/WellKnownDestinations.cs new file mode 100644 index 000000000..029305363 --- /dev/null +++ b/src/Greenshot.Base/WellKnownDestinations.cs @@ -0,0 +1,36 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/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 . + */ + +namespace Greenshot.Base +{ + /// + /// This contains all the well known destinations, make it possible to find them via the service locator + /// + public enum WellKnownDestinations + { + Clipboard, + EMail, + FileDialog, + FileNoDialog, + Picker, + Printer + } +} diff --git a/src/Greenshot/Configuration/EditorConfiguration.cs b/src/Greenshot.Editor/Configuration/EditorConfiguration.cs similarity index 96% rename from src/Greenshot/Configuration/EditorConfiguration.cs rename to src/Greenshot.Editor/Configuration/EditorConfiguration.cs index c2a252611..91a5bc335 100644 --- a/src/Greenshot/Configuration/EditorConfiguration.cs +++ b/src/Greenshot.Editor/Configuration/EditorConfiguration.cs @@ -1,181 +1,181 @@ -/* - * Greenshot - a free and open source screenshot tool - * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom - * - * For more information see: https://getgreenshot.org/ - * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 1 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -using System; -using System.Collections.Generic; -using System.Drawing; -using Greenshot.Base.Effects; -using Greenshot.Base.IniFile; -using Greenshot.Base.Interfaces.Drawing; -using Greenshot.Base.UnmanagedHelpers.Enums; -using Greenshot.Base.UnmanagedHelpers.Structs; -using Greenshot.Drawing.Fields; - -namespace Greenshot.Configuration -{ - /// - /// Description of CoreConfiguration. - /// - [IniSection("Editor", Description = "Greenshot editor configuration")] - public class EditorConfiguration : IniSection - { - [IniProperty("RecentColors", Separator = "|", Description = "Last used colors")] - public List RecentColors { get; set; } - - [IniProperty("LastFieldValue", Separator = "|", Description = "Field values, make sure the last used settings are re-used")] - public Dictionary LastUsedFieldValues { get; set; } - - [IniProperty("MatchSizeToCapture", Description = "Match the editor window size to the capture", DefaultValue = "True")] - public bool MatchSizeToCapture { get; set; } - - [IniProperty("WindowPlacementFlags", Description = "Placement flags", DefaultValue = "0")] - public WindowPlacementFlags WindowPlacementFlags { get; set; } - - [IniProperty("WindowShowCommand", Description = "Show command", DefaultValue = "Normal")] - public ShowWindowCommand ShowWindowCommand { get; set; } - - [IniProperty("WindowMinPosition", Description = "Position of minimized window", DefaultValue = "-1,-1")] - public Point WindowMinPosition { get; set; } - - [IniProperty("WindowMaxPosition", Description = "Position of maximized window", DefaultValue = "-1,-1")] - public Point WindowMaxPosition { get; set; } - - [IniProperty("WindowNormalPosition", Description = "Position of normal window", DefaultValue = "100,100,400,400")] - public Rectangle WindowNormalPosition { get; set; } - - [IniProperty("ReuseEditor", Description = "Reuse already open editor", DefaultValue = "false")] - public bool ReuseEditor { get; set; } - - [IniProperty("FreehandSensitivity", - Description = - "The smaller this number, the less smoothing is used. Decrease for detailed drawing, e.g. when using a pen. Increase for smoother lines. e.g. when you want to draw a smooth line.", - DefaultValue = "3")] - public int FreehandSensitivity { get; set; } - - [IniProperty("SuppressSaveDialogAtClose", Description = "Suppressed the 'do you want to save' dialog when closing the editor.", DefaultValue = "False")] - public bool SuppressSaveDialogAtClose { get; set; } - - [IniProperty("DropShadowEffectSettings", Description = "Settings for the drop shadow effect.")] - public DropShadowEffect DropShadowEffectSettings { get; set; } - - [IniProperty("TornEdgeEffectSettings", Description = "Settings for the torn edge effect.")] - public TornEdgeEffect TornEdgeEffectSettings { get; set; } - - public override void AfterLoad() - { - base.AfterLoad(); - if (RecentColors == null) - { - RecentColors = new List(); - } - } - - /// Type of the class for which to create the field - /// FieldType of the field to construct - /// - /// a new Field of the given fieldType, with the scope of it's value being restricted to the Type scope - public IField CreateField(Type requestingType, IFieldType fieldType, object preferredDefaultValue) - { - string requestingTypeName = requestingType.Name; - string requestedField = requestingTypeName + "." + fieldType.Name; - object fieldValue = preferredDefaultValue; - - // Check if the configuration exists - if (LastUsedFieldValues == null) - { - LastUsedFieldValues = new Dictionary(); - } - - // Check if settings for the requesting type exist, if not create! - if (LastUsedFieldValues.ContainsKey(requestedField)) - { - // Check if a value is set (not null)! - if (LastUsedFieldValues[requestedField] != null) - { - fieldValue = LastUsedFieldValues[requestedField]; - } - else - { - // Overwrite null value - LastUsedFieldValues[requestedField] = fieldValue; - } - } - else - { - LastUsedFieldValues.Add(requestedField, fieldValue); - } - - return new Field(fieldType, requestingType) - { - Value = fieldValue - }; - } - - public void UpdateLastFieldValue(IField field) - { - string requestedField = field.Scope + "." + field.FieldType.Name; - // Check if the configuration exists - if (LastUsedFieldValues == null) - { - LastUsedFieldValues = new Dictionary(); - } - - // check if settings for the requesting type exist, if not create! - if (LastUsedFieldValues.ContainsKey(requestedField)) - { - LastUsedFieldValues[requestedField] = field.Value; - } - else - { - LastUsedFieldValues.Add(requestedField, field.Value); - } - } - - public void ResetEditorPlacement() - { - WindowNormalPosition = new Rectangle(100, 100, 400, 400); - WindowMaxPosition = new Point(-1, -1); - WindowMinPosition = new Point(-1, -1); - WindowPlacementFlags = 0; - ShowWindowCommand = ShowWindowCommand.Normal; - } - - public WindowPlacement GetEditorPlacement() - { - WindowPlacement placement = WindowPlacement.Default; - placement.NormalPosition = new RECT(WindowNormalPosition); - placement.MaxPosition = new POINT(WindowMaxPosition); - placement.MinPosition = new POINT(WindowMinPosition); - placement.ShowCmd = ShowWindowCommand; - placement.Flags = WindowPlacementFlags; - return placement; - } - - public void SetEditorPlacement(WindowPlacement placement) - { - WindowNormalPosition = placement.NormalPosition.ToRectangle(); - WindowMaxPosition = placement.MaxPosition.ToPoint(); - WindowMinPosition = placement.MinPosition.ToPoint(); - ShowWindowCommand = placement.ShowCmd; - WindowPlacementFlags = placement.Flags; - } - } +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using System; +using System.Collections.Generic; +using System.Drawing; +using Greenshot.Base.Effects; +using Greenshot.Base.IniFile; +using Greenshot.Base.Interfaces.Drawing; +using Greenshot.Base.UnmanagedHelpers.Enums; +using Greenshot.Base.UnmanagedHelpers.Structs; +using Greenshot.Editor.Drawing.Fields; + +namespace Greenshot.Editor.Configuration +{ + /// + /// Description of CoreConfiguration. + /// + [IniSection("Editor", Description = "Greenshot editor configuration")] + public class EditorConfiguration : IniSection + { + [IniProperty("RecentColors", Separator = "|", Description = "Last used colors")] + public List RecentColors { get; set; } + + [IniProperty("LastFieldValue", Separator = "|", Description = "Field values, make sure the last used settings are re-used")] + public Dictionary LastUsedFieldValues { get; set; } + + [IniProperty("MatchSizeToCapture", Description = "Match the editor window size to the capture", DefaultValue = "True")] + public bool MatchSizeToCapture { get; set; } + + [IniProperty("WindowPlacementFlags", Description = "Placement flags", DefaultValue = "0")] + public WindowPlacementFlags WindowPlacementFlags { get; set; } + + [IniProperty("WindowShowCommand", Description = "Show command", DefaultValue = "Normal")] + public ShowWindowCommand ShowWindowCommand { get; set; } + + [IniProperty("WindowMinPosition", Description = "Position of minimized window", DefaultValue = "-1,-1")] + public Point WindowMinPosition { get; set; } + + [IniProperty("WindowMaxPosition", Description = "Position of maximized window", DefaultValue = "-1,-1")] + public Point WindowMaxPosition { get; set; } + + [IniProperty("WindowNormalPosition", Description = "Position of normal window", DefaultValue = "100,100,400,400")] + public Rectangle WindowNormalPosition { get; set; } + + [IniProperty("ReuseEditor", Description = "Reuse already open editor", DefaultValue = "false")] + public bool ReuseEditor { get; set; } + + [IniProperty("FreehandSensitivity", + Description = + "The smaller this number, the less smoothing is used. Decrease for detailed drawing, e.g. when using a pen. Increase for smoother lines. e.g. when you want to draw a smooth line.", + DefaultValue = "3")] + public int FreehandSensitivity { get; set; } + + [IniProperty("SuppressSaveDialogAtClose", Description = "Suppressed the 'do you want to save' dialog when closing the editor.", DefaultValue = "False")] + public bool SuppressSaveDialogAtClose { get; set; } + + [IniProperty("DropShadowEffectSettings", Description = "Settings for the drop shadow effect.")] + public DropShadowEffect DropShadowEffectSettings { get; set; } + + [IniProperty("TornEdgeEffectSettings", Description = "Settings for the torn edge effect.")] + public TornEdgeEffect TornEdgeEffectSettings { get; set; } + + public override void AfterLoad() + { + base.AfterLoad(); + if (RecentColors == null) + { + RecentColors = new List(); + } + } + + /// Type of the class for which to create the field + /// FieldType of the field to construct + /// + /// a new Field of the given fieldType, with the scope of it's value being restricted to the Type scope + public IField CreateField(Type requestingType, IFieldType fieldType, object preferredDefaultValue) + { + string requestingTypeName = requestingType.Name; + string requestedField = requestingTypeName + "." + fieldType.Name; + object fieldValue = preferredDefaultValue; + + // Check if the configuration exists + if (LastUsedFieldValues == null) + { + LastUsedFieldValues = new Dictionary(); + } + + // Check if settings for the requesting type exist, if not create! + if (LastUsedFieldValues.ContainsKey(requestedField)) + { + // Check if a value is set (not null)! + if (LastUsedFieldValues[requestedField] != null) + { + fieldValue = LastUsedFieldValues[requestedField]; + } + else + { + // Overwrite null value + LastUsedFieldValues[requestedField] = fieldValue; + } + } + else + { + LastUsedFieldValues.Add(requestedField, fieldValue); + } + + return new Field(fieldType, requestingType) + { + Value = fieldValue + }; + } + + public void UpdateLastFieldValue(IField field) + { + string requestedField = field.Scope + "." + field.FieldType.Name; + // Check if the configuration exists + if (LastUsedFieldValues == null) + { + LastUsedFieldValues = new Dictionary(); + } + + // check if settings for the requesting type exist, if not create! + if (LastUsedFieldValues.ContainsKey(requestedField)) + { + LastUsedFieldValues[requestedField] = field.Value; + } + else + { + LastUsedFieldValues.Add(requestedField, field.Value); + } + } + + public void ResetEditorPlacement() + { + WindowNormalPosition = new Rectangle(100, 100, 400, 400); + WindowMaxPosition = new Point(-1, -1); + WindowMinPosition = new Point(-1, -1); + WindowPlacementFlags = 0; + ShowWindowCommand = ShowWindowCommand.Normal; + } + + public WindowPlacement GetEditorPlacement() + { + WindowPlacement placement = WindowPlacement.Default; + placement.NormalPosition = new RECT(WindowNormalPosition); + placement.MaxPosition = new POINT(WindowMaxPosition); + placement.MinPosition = new POINT(WindowMinPosition); + placement.ShowCmd = ShowWindowCommand; + placement.Flags = WindowPlacementFlags; + return placement; + } + + public void SetEditorPlacement(WindowPlacement placement) + { + WindowNormalPosition = placement.NormalPosition.ToRectangle(); + WindowMaxPosition = placement.MaxPosition.ToPoint(); + WindowMinPosition = placement.MinPosition.ToPoint(); + ShowWindowCommand = placement.ShowCmd; + WindowPlacementFlags = placement.Flags; + } + } } \ No newline at end of file diff --git a/src/Greenshot.Editor/Configuration/LanguageKeys.cs b/src/Greenshot.Editor/Configuration/LanguageKeys.cs new file mode 100644 index 000000000..f15b113d4 --- /dev/null +++ b/src/Greenshot.Editor/Configuration/LanguageKeys.cs @@ -0,0 +1,82 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using System.Diagnostics.CodeAnalysis; + +namespace Greenshot.Editor.Configuration +{ + [SuppressMessage("ReSharper", "InconsistentNaming")] + public enum LangKey + { + none, + contextmenu_capturefullscreen_all, + contextmenu_capturefullscreen_left, + contextmenu_capturefullscreen_top, + contextmenu_capturefullscreen_right, + contextmenu_capturefullscreen_bottom, + contextmenu_captureie, + editor_clipboardfailed, + editor_close_on_save, + editor_close_on_save_title, + editor_copytoclipboard, + editor_cuttoclipboard, + editor_deleteelement, + editor_downonelevel, + editor_downtobottom, + editor_duplicate, + editor_email, + editor_imagesaved, + editor_title, + editor_uponelevel, + editor_uptotop, + editor_undo, + editor_redo, + editor_resetsize, + error, + error_multipleinstances, + error_openfile, + error_openlink, + error_save, + error_save_invalid_chars, + print_error, + quicksettings_destination_file, + settings_destination, + settings_destination_clipboard, + settings_destination_editor, + settings_destination_fileas, + settings_destination_printer, + settings_destination_picker, + settings_filenamepattern, + settings_message_filenamepattern, + settings_printoptions, + settings_tooltip_filenamepattern, + settings_tooltip_language, + settings_tooltip_primaryimageformat, + settings_tooltip_storagelocation, + settings_visualization, + settings_window_capture_mode, + tooltip_firststart, + warning, + warning_hotkeys, + wait_ie_capture, + update_found + } +} \ No newline at end of file diff --git a/src/Greenshot/Controls/BindableToolStripButton.cs b/src/Greenshot.Editor/Controls/BindableToolStripButton.cs similarity index 95% rename from src/Greenshot/Controls/BindableToolStripButton.cs rename to src/Greenshot.Editor/Controls/BindableToolStripButton.cs index 603ec3499..a79d6c801 100644 --- a/src/Greenshot/Controls/BindableToolStripButton.cs +++ b/src/Greenshot.Editor/Controls/BindableToolStripButton.cs @@ -1,49 +1,49 @@ -/* - * Greenshot - a free and open source screenshot tool - * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom - * - * For more information see: https://getgreenshot.org/ - * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 1 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -using System; -using System.ComponentModel; -using System.Windows.Forms; -using Greenshot.Base.Controls; - -namespace Greenshot.Controls -{ - /// - /// Description of BindableToolStripButton. - /// - public class BindableToolStripButton : ToolStripButton, INotifyPropertyChanged, IGreenshotLanguageBindable - { - public event PropertyChangedEventHandler PropertyChanged; - - [Category("Greenshot"), DefaultValue(null), Description("Specifies key of the language file to use when displaying the text.")] - public string LanguageKey { get; set; } - - public BindableToolStripButton() - { - CheckedChanged += BindableToolStripButton_CheckedChanged; - } - - private void BindableToolStripButton_CheckedChanged(object sender, EventArgs e) - { - PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Checked")); - } - } +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using System; +using System.ComponentModel; +using System.Windows.Forms; +using Greenshot.Base.Controls; + +namespace Greenshot.Editor.Controls +{ + /// + /// Description of BindableToolStripButton. + /// + public class BindableToolStripButton : ToolStripButton, INotifyPropertyChanged, IGreenshotLanguageBindable + { + public event PropertyChangedEventHandler PropertyChanged; + + [Category("Greenshot"), DefaultValue(null), Description("Specifies key of the language file to use when displaying the text.")] + public string LanguageKey { get; set; } + + public BindableToolStripButton() + { + CheckedChanged += BindableToolStripButton_CheckedChanged; + } + + private void BindableToolStripButton_CheckedChanged(object sender, EventArgs e) + { + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Checked")); + } + } } \ No newline at end of file diff --git a/src/Greenshot/Controls/BindableToolStripComboBox.cs b/src/Greenshot.Editor/Controls/BindableToolStripComboBox.cs similarity index 96% rename from src/Greenshot/Controls/BindableToolStripComboBox.cs rename to src/Greenshot.Editor/Controls/BindableToolStripComboBox.cs index f13cfa4d6..ac56641f1 100644 --- a/src/Greenshot/Controls/BindableToolStripComboBox.cs +++ b/src/Greenshot.Editor/Controls/BindableToolStripComboBox.cs @@ -1,49 +1,49 @@ -/* - * Greenshot - a free and open source screenshot tool - * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom - * - * For more information see: https://getgreenshot.org/ - * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 1 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -using System; -using System.ComponentModel; -using System.Windows.Forms; -using Greenshot.Base.Controls; - -namespace Greenshot.Controls -{ - /// - /// A simple ToolStripComboBox implementing INotifyPropertyChanged for data binding - /// - public class BindableToolStripComboBox : ToolStripComboBox, INotifyPropertyChanged, IGreenshotLanguageBindable - { - public event PropertyChangedEventHandler PropertyChanged; - - [Category("Greenshot"), DefaultValue(null), Description("Specifies key of the language file to use when displaying the text.")] - public string LanguageKey { get; set; } - - public BindableToolStripComboBox() - { - SelectedIndexChanged += BindableToolStripComboBox_SelectedIndexChanged; - } - - private void BindableToolStripComboBox_SelectedIndexChanged(object sender, EventArgs e) - { - PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("SelectedItem")); - } - } +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using System; +using System.ComponentModel; +using System.Windows.Forms; +using Greenshot.Base.Controls; + +namespace Greenshot.Editor.Controls +{ + /// + /// A simple ToolStripComboBox implementing INotifyPropertyChanged for data binding + /// + public class BindableToolStripComboBox : ToolStripComboBox, INotifyPropertyChanged, IGreenshotLanguageBindable + { + public event PropertyChangedEventHandler PropertyChanged; + + [Category("Greenshot"), DefaultValue(null), Description("Specifies key of the language file to use when displaying the text.")] + public string LanguageKey { get; set; } + + public BindableToolStripComboBox() + { + SelectedIndexChanged += BindableToolStripComboBox_SelectedIndexChanged; + } + + private void BindableToolStripComboBox_SelectedIndexChanged(object sender, EventArgs e) + { + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("SelectedItem")); + } + } } \ No newline at end of file diff --git a/src/Greenshot/Controls/BindableToolStripDropDownButton.cs b/src/Greenshot.Editor/Controls/BindableToolStripDropDownButton.cs similarity index 96% rename from src/Greenshot/Controls/BindableToolStripDropDownButton.cs rename to src/Greenshot.Editor/Controls/BindableToolStripDropDownButton.cs index 6a7affcc8..7d0a45676 100644 --- a/src/Greenshot/Controls/BindableToolStripDropDownButton.cs +++ b/src/Greenshot.Editor/Controls/BindableToolStripDropDownButton.cs @@ -1,82 +1,82 @@ -/* - * Greenshot - a free and open source screenshot tool - * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom - * - * For more information see: https://getgreenshot.org/ - * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 1 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -using System.ComponentModel; -using System.Windows.Forms; -using Greenshot.Base.Controls; - -namespace Greenshot.Controls -{ - /// - /// A simple ToolStripDropDownButton implementing INotifyPropertyChanged for data binding. - /// Also, when a DropDownItem is selected, the DropDownButton adopts its Tag and Image. - /// The selected tag can be accessed via SelectedTag property. - /// - public class BindableToolStripDropDownButton : ToolStripDropDownButton, INotifyPropertyChanged, IGreenshotLanguageBindable - { - public event PropertyChangedEventHandler PropertyChanged; - - [Category("Greenshot"), DefaultValue(null), Description("Specifies key of the language file to use when displaying the text.")] - public string LanguageKey { get; set; } - - public object SelectedTag - { - get - { - if (Tag == null && DropDownItems.Count > 0) Tag = DropDownItems[0].Tag; - return Tag; - } - set { AdoptFromTag(value); } - } - - protected override void OnDropDownItemClicked(ToolStripItemClickedEventArgs e) - { - ToolStripItem clickedItem = e.ClickedItem; - if (Tag == null || !Tag.Equals(clickedItem.Tag)) - { - Tag = clickedItem.Tag; - Image = clickedItem.Image; - PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("SelectedTag")); - } - - base.OnDropDownItemClicked(e); - } - - private void AdoptFromTag(object tag) - { - if (Tag == null || !Tag.Equals(tag)) - { - Tag = tag; - foreach (ToolStripItem item in DropDownItems) - { - if (item.Tag != null && item.Tag.Equals(tag)) - { - Image = item.Image; - break; - } - } - - Tag = tag; - PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("SelectedTag")); - } - } - } +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using System.ComponentModel; +using System.Windows.Forms; +using Greenshot.Base.Controls; + +namespace Greenshot.Editor.Controls +{ + /// + /// A simple ToolStripDropDownButton implementing INotifyPropertyChanged for data binding. + /// Also, when a DropDownItem is selected, the DropDownButton adopts its Tag and Image. + /// The selected tag can be accessed via SelectedTag property. + /// + public class BindableToolStripDropDownButton : ToolStripDropDownButton, INotifyPropertyChanged, IGreenshotLanguageBindable + { + public event PropertyChangedEventHandler PropertyChanged; + + [Category("Greenshot"), DefaultValue(null), Description("Specifies key of the language file to use when displaying the text.")] + public string LanguageKey { get; set; } + + public object SelectedTag + { + get + { + if (Tag == null && DropDownItems.Count > 0) Tag = DropDownItems[0].Tag; + return Tag; + } + set { AdoptFromTag(value); } + } + + protected override void OnDropDownItemClicked(ToolStripItemClickedEventArgs e) + { + ToolStripItem clickedItem = e.ClickedItem; + if (Tag == null || !Tag.Equals(clickedItem.Tag)) + { + Tag = clickedItem.Tag; + Image = clickedItem.Image; + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("SelectedTag")); + } + + base.OnDropDownItemClicked(e); + } + + private void AdoptFromTag(object tag) + { + if (Tag == null || !Tag.Equals(tag)) + { + Tag = tag; + foreach (ToolStripItem item in DropDownItems) + { + if (item.Tag != null && item.Tag.Equals(tag)) + { + Image = item.Image; + break; + } + } + + Tag = tag; + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("SelectedTag")); + } + } + } } \ No newline at end of file diff --git a/src/Greenshot/Controls/ColorButton.cs b/src/Greenshot.Editor/Controls/ColorButton.cs similarity index 94% rename from src/Greenshot/Controls/ColorButton.cs rename to src/Greenshot.Editor/Controls/ColorButton.cs index 93cf4edf3..849395330 100644 --- a/src/Greenshot/Controls/ColorButton.cs +++ b/src/Greenshot.Editor/Controls/ColorButton.cs @@ -1,99 +1,99 @@ -/* - * Greenshot - a free and open source screenshot tool - * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom - * - * For more information see: https://getgreenshot.org/ - * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 1 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -using System; -using System.ComponentModel; -using System.Drawing; -using System.Drawing.Drawing2D; -using System.Windows.Forms; -using Greenshot.Base.Controls; -using ColorDialog = Greenshot.Forms.ColorDialog; - -namespace Greenshot.Controls -{ - /// - /// Description of ColorButton. - /// - public class ColorButton : Button, IGreenshotLanguageBindable - { - public event PropertyChangedEventHandler PropertyChanged; - private Color _selectedColor = Color.White; - - [Category("Greenshot"), DefaultValue(null), Description("Specifies key of the language file to use when displaying the text.")] - public string LanguageKey { get; set; } - - public ColorButton() - { - Click += ColorButtonClick; - } - - public Color SelectedColor - { - get { return _selectedColor; } - set - { - _selectedColor = value; - - Brush brush; - if (value != Color.Transparent) - { - brush = new SolidBrush(value); - } - else - { - brush = new HatchBrush(HatchStyle.Percent50, Color.White, Color.Gray); - } - - if (Image != null) - { - using Graphics graphics = Graphics.FromImage(Image); - graphics.FillRectangle(brush, new Rectangle(4, 17, 16, 3)); - } - - // cleanup GDI Object - brush.Dispose(); - Invalidate(); - } - } - - private void ColorButtonClick(object sender, EventArgs e) - { - var colorDialog = new ColorDialog - { - Color = SelectedColor - }; - // Using the parent to make sure the dialog doesn't show on another window - colorDialog.ShowDialog(Parent.Parent); - if (colorDialog.DialogResult == DialogResult.Cancel) - { - return; - } - - if (colorDialog.Color.Equals(SelectedColor)) - { - return; - } - - SelectedColor = colorDialog.Color; - PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("SelectedColor")); - } - } +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using System; +using System.ComponentModel; +using System.Drawing; +using System.Drawing.Drawing2D; +using System.Windows.Forms; +using Greenshot.Base.Controls; +using ColorDialog = Greenshot.Editor.Forms.ColorDialog; + +namespace Greenshot.Editor.Controls +{ + /// + /// Description of ColorButton. + /// + public class ColorButton : Button, IGreenshotLanguageBindable + { + public event PropertyChangedEventHandler PropertyChanged; + private Color _selectedColor = Color.White; + + [Category("Greenshot"), DefaultValue(null), Description("Specifies key of the language file to use when displaying the text.")] + public string LanguageKey { get; set; } + + public ColorButton() + { + Click += ColorButtonClick; + } + + public Color SelectedColor + { + get { return _selectedColor; } + set + { + _selectedColor = value; + + Brush brush; + if (value != Color.Transparent) + { + brush = new SolidBrush(value); + } + else + { + brush = new HatchBrush(HatchStyle.Percent50, Color.White, Color.Gray); + } + + if (Image != null) + { + using Graphics graphics = Graphics.FromImage(Image); + graphics.FillRectangle(brush, new Rectangle(4, 17, 16, 3)); + } + + // cleanup GDI Object + brush.Dispose(); + Invalidate(); + } + } + + private void ColorButtonClick(object sender, EventArgs e) + { + var colorDialog = new ColorDialog + { + Color = SelectedColor + }; + // Using the parent to make sure the dialog doesn't show on another window + colorDialog.ShowDialog(Parent.Parent); + if (colorDialog.DialogResult == DialogResult.Cancel) + { + return; + } + + if (colorDialog.Color.Equals(SelectedColor)) + { + return; + } + + SelectedColor = colorDialog.Color; + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("SelectedColor")); + } + } } \ No newline at end of file diff --git a/src/Greenshot/Controls/CustomToolStripProfessionalRenderer.cs b/src/Greenshot.Editor/Controls/CustomToolStripProfessionalRenderer.cs similarity index 96% rename from src/Greenshot/Controls/CustomToolStripProfessionalRenderer.cs rename to src/Greenshot.Editor/Controls/CustomToolStripProfessionalRenderer.cs index 8c8927508..578aaf655 100644 --- a/src/Greenshot/Controls/CustomToolStripProfessionalRenderer.cs +++ b/src/Greenshot.Editor/Controls/CustomToolStripProfessionalRenderer.cs @@ -1,84 +1,84 @@ -/* - * Greenshot - a free and open source screenshot tool - * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom - * - * For more information see: https://getgreenshot.org/ - * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 1 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -using System.Drawing; -using System.Windows.Forms; - -namespace Greenshot.Controls -{ - /// - /// Prevent having a gradient background in the toolstrip, and the overflow button - /// See: https://stackoverflow.com/a/16926979 - /// - internal class CustomProfessionalColorTable : ProfessionalColorTable - { - public override Color ToolStripGradientBegin - { - get { return SystemColors.Control; } - } - - public override Color ToolStripGradientMiddle - { - get { return SystemColors.Control; } - } - - public override Color ToolStripGradientEnd - { - get { return SystemColors.Control; } - } - - public override Color OverflowButtonGradientBegin - { - get { return SystemColors.Control; } - } - - public override Color OverflowButtonGradientMiddle - { - get { return SystemColors.Control; } - } - - public override Color OverflowButtonGradientEnd - { - get { return SystemColors.Control; } - } - } - - /// - /// ToolStripProfessionalRenderer without having a visual artifact - /// See: https://stackoverflow.com/a/16926979 and https://stackoverflow.com/a/13418840 - /// - public class CustomToolStripProfessionalRenderer : ToolStripProfessionalRenderer - { - public CustomToolStripProfessionalRenderer() : base(new CustomProfessionalColorTable()) - { - RoundedEdges = false; - } - - /// - /// By overriding the OnRenderToolStripBorder we can make the ToolStrip without border - /// - /// - protected override void OnRenderToolStripBorder(ToolStripRenderEventArgs e) - { - // Don't draw a border - } - } +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using System.Drawing; +using System.Windows.Forms; + +namespace Greenshot.Editor.Controls +{ + /// + /// Prevent having a gradient background in the toolstrip, and the overflow button + /// See: https://stackoverflow.com/a/16926979 + /// + internal class CustomProfessionalColorTable : ProfessionalColorTable + { + public override Color ToolStripGradientBegin + { + get { return SystemColors.Control; } + } + + public override Color ToolStripGradientMiddle + { + get { return SystemColors.Control; } + } + + public override Color ToolStripGradientEnd + { + get { return SystemColors.Control; } + } + + public override Color OverflowButtonGradientBegin + { + get { return SystemColors.Control; } + } + + public override Color OverflowButtonGradientMiddle + { + get { return SystemColors.Control; } + } + + public override Color OverflowButtonGradientEnd + { + get { return SystemColors.Control; } + } + } + + /// + /// ToolStripProfessionalRenderer without having a visual artifact + /// See: https://stackoverflow.com/a/16926979 and https://stackoverflow.com/a/13418840 + /// + public class CustomToolStripProfessionalRenderer : ToolStripProfessionalRenderer + { + public CustomToolStripProfessionalRenderer() : base(new CustomProfessionalColorTable()) + { + RoundedEdges = false; + } + + /// + /// By overriding the OnRenderToolStripBorder we can make the ToolStrip without border + /// + /// + protected override void OnRenderToolStripBorder(ToolStripRenderEventArgs e) + { + // Don't draw a border + } + } } \ No newline at end of file diff --git a/src/Greenshot/Controls/FontFamilyComboBox.cs b/src/Greenshot.Editor/Controls/FontFamilyComboBox.cs similarity index 97% rename from src/Greenshot/Controls/FontFamilyComboBox.cs rename to src/Greenshot.Editor/Controls/FontFamilyComboBox.cs index 6369f83e4..b57d52e75 100644 --- a/src/Greenshot/Controls/FontFamilyComboBox.cs +++ b/src/Greenshot.Editor/Controls/FontFamilyComboBox.cs @@ -1,141 +1,141 @@ -/* - * Greenshot - a free and open source screenshot tool - * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom - * - * For more information see: https://getgreenshot.org/ - * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 1 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -using System; -using System.ComponentModel; -using System.Drawing; -using System.Windows.Forms; - -namespace Greenshot.Controls -{ - /// - /// ToolStripComboBox containing installed font families, - /// implementing INotifyPropertyChanged for data binding - /// - public class FontFamilyComboBox : ToolStripComboBox, INotifyPropertyChanged - { - public event PropertyChangedEventHandler PropertyChanged; - - public FontFamily FontFamily - { - get { return (FontFamily) SelectedItem; } - set - { - if (!SelectedItem.Equals(value)) - { - SelectedItem = value; - } - } - } - - public FontFamilyComboBox() - { - if (ComboBox != null) - { - ComboBox.DataSource = FontFamily.Families; - ComboBox.DisplayMember = "Name"; - SelectedIndexChanged += BindableToolStripComboBox_SelectedIndexChanged; - ComboBox.DrawMode = DrawMode.OwnerDrawFixed; - ComboBox.DrawItem += ComboBox_DrawItem; - } - } - - private void ComboBox_DrawItem(object sender, DrawItemEventArgs e) - { - // DrawBackground handles drawing the background (i.e,. hot-tracked v. not) - // It uses the system colors (Bluish, and and white, by default) - // same as calling e.Graphics.FillRectangle ( SystemBrushes.Highlight, e.Bounds ); - e.DrawBackground(); - - if (e.Index > -1) - { - FontFamily fontFamily = Items[e.Index] as FontFamily; - FontStyle fontStyle = FontStyle.Regular; - if (fontFamily != null && !fontFamily.IsStyleAvailable(FontStyle.Regular)) - { - if (fontFamily.IsStyleAvailable(FontStyle.Bold)) - { - fontStyle = FontStyle.Bold; - } - else if (fontFamily.IsStyleAvailable(FontStyle.Italic)) - { - fontStyle = FontStyle.Italic; - } - else if (fontFamily.IsStyleAvailable(FontStyle.Strikeout)) - { - fontStyle = FontStyle.Strikeout; - } - else if (fontFamily.IsStyleAvailable(FontStyle.Underline)) - { - fontStyle = FontStyle.Underline; - } - } - - try - { - if (fontFamily != null) - { - DrawText(e.Graphics, fontFamily, fontStyle, e.Bounds, fontFamily.Name); - } - } - catch - { - // If the drawing failed, BUG-1770 seems to have a weird case that causes: Font 'Lucida Sans Typewriter' does not support style 'Regular' - if (fontFamily != null) - { - DrawText(e.Graphics, FontFamily.GenericSansSerif, FontStyle.Regular, e.Bounds, fontFamily.Name); - } - } - } - - // Uncomment this if you actually like the way the focus rectangle looks - //e.DrawFocusRectangle (); - } - - /// - /// Helper method to draw the string - /// - /// - /// - /// - /// - /// - private void DrawText(Graphics graphics, FontFamily fontFamily, FontStyle fontStyle, Rectangle bounds, string text) - { - using Font font = new Font(fontFamily, Font.Size + 5, fontStyle, GraphicsUnit.Pixel); - // Make sure the text is visible by centering it in the line - using StringFormat stringFormat = new StringFormat - { - LineAlignment = StringAlignment.Center - }; - graphics.DrawString(text, font, Brushes.Black, bounds, stringFormat); - } - - private void BindableToolStripComboBox_SelectedIndexChanged(object sender, EventArgs e) - { - if (PropertyChanged == null) return; - PropertyChanged(this, new PropertyChangedEventArgs("Text")); - PropertyChanged(this, new PropertyChangedEventArgs("FontFamily")); - PropertyChanged(this, new PropertyChangedEventArgs("SelectedIndex")); - PropertyChanged(this, new PropertyChangedEventArgs("SelectedItem")); - } - } +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using System; +using System.ComponentModel; +using System.Drawing; +using System.Windows.Forms; + +namespace Greenshot.Editor.Controls +{ + /// + /// ToolStripComboBox containing installed font families, + /// implementing INotifyPropertyChanged for data binding + /// + public class FontFamilyComboBox : ToolStripComboBox, INotifyPropertyChanged + { + public event PropertyChangedEventHandler PropertyChanged; + + public FontFamily FontFamily + { + get { return (FontFamily) SelectedItem; } + set + { + if (!SelectedItem.Equals(value)) + { + SelectedItem = value; + } + } + } + + public FontFamilyComboBox() + { + if (ComboBox != null) + { + ComboBox.DataSource = FontFamily.Families; + ComboBox.DisplayMember = "Name"; + SelectedIndexChanged += BindableToolStripComboBox_SelectedIndexChanged; + ComboBox.DrawMode = DrawMode.OwnerDrawFixed; + ComboBox.DrawItem += ComboBox_DrawItem; + } + } + + private void ComboBox_DrawItem(object sender, DrawItemEventArgs e) + { + // DrawBackground handles drawing the background (i.e,. hot-tracked v. not) + // It uses the system colors (Bluish, and and white, by default) + // same as calling e.Graphics.FillRectangle ( SystemBrushes.Highlight, e.Bounds ); + e.DrawBackground(); + + if (e.Index > -1) + { + FontFamily fontFamily = Items[e.Index] as FontFamily; + FontStyle fontStyle = FontStyle.Regular; + if (fontFamily != null && !fontFamily.IsStyleAvailable(FontStyle.Regular)) + { + if (fontFamily.IsStyleAvailable(FontStyle.Bold)) + { + fontStyle = FontStyle.Bold; + } + else if (fontFamily.IsStyleAvailable(FontStyle.Italic)) + { + fontStyle = FontStyle.Italic; + } + else if (fontFamily.IsStyleAvailable(FontStyle.Strikeout)) + { + fontStyle = FontStyle.Strikeout; + } + else if (fontFamily.IsStyleAvailable(FontStyle.Underline)) + { + fontStyle = FontStyle.Underline; + } + } + + try + { + if (fontFamily != null) + { + DrawText(e.Graphics, fontFamily, fontStyle, e.Bounds, fontFamily.Name); + } + } + catch + { + // If the drawing failed, BUG-1770 seems to have a weird case that causes: Font 'Lucida Sans Typewriter' does not support style 'Regular' + if (fontFamily != null) + { + DrawText(e.Graphics, FontFamily.GenericSansSerif, FontStyle.Regular, e.Bounds, fontFamily.Name); + } + } + } + + // Uncomment this if you actually like the way the focus rectangle looks + //e.DrawFocusRectangle (); + } + + /// + /// Helper method to draw the string + /// + /// + /// + /// + /// + /// + private void DrawText(Graphics graphics, FontFamily fontFamily, FontStyle fontStyle, Rectangle bounds, string text) + { + using Font font = new Font(fontFamily, Font.Size + 5, fontStyle, GraphicsUnit.Pixel); + // Make sure the text is visible by centering it in the line + using StringFormat stringFormat = new StringFormat + { + LineAlignment = StringAlignment.Center + }; + graphics.DrawString(text, font, Brushes.Black, bounds, stringFormat); + } + + private void BindableToolStripComboBox_SelectedIndexChanged(object sender, EventArgs e) + { + if (PropertyChanged == null) return; + PropertyChanged(this, new PropertyChangedEventArgs("Text")); + PropertyChanged(this, new PropertyChangedEventArgs("FontFamily")); + PropertyChanged(this, new PropertyChangedEventArgs("SelectedIndex")); + PropertyChanged(this, new PropertyChangedEventArgs("SelectedItem")); + } + } } \ No newline at end of file diff --git a/src/Greenshot/Controls/MenuStripEx.cs b/src/Greenshot.Editor/Controls/MenuStripEx.cs similarity index 96% rename from src/Greenshot/Controls/MenuStripEx.cs rename to src/Greenshot.Editor/Controls/MenuStripEx.cs index d6264e787..9af1da2cf 100644 --- a/src/Greenshot/Controls/MenuStripEx.cs +++ b/src/Greenshot.Editor/Controls/MenuStripEx.cs @@ -1,65 +1,65 @@ -/* - * Greenshot - a free and open source screenshot tool - * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom - * - * For more information see: https://getgreenshot.org/ - * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 1 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -using System; -using System.Windows.Forms; -using Greenshot.Base.UnmanagedHelpers.Enums; - -namespace Greenshot.Controls -{ - /// - /// This is an extension of the default MenuStrip and allows us to click it even when the form doesn't have focus. - /// See: https://blogs.msdn.com/b/rickbrew/archive/2006/01/09/511003.aspx - /// - public class MenuStripEx : MenuStrip - { - private enum NativeConstants : uint - { - MA_ACTIVATE = 1, - MA_ACTIVATEANDEAT = 2, - } - - private bool _clickThrough; - - /// - /// Gets or sets whether the ToolStripEx honors item clicks when its containing form does not have input focus. - /// - /// - /// Default value is false, which is the same behavior provided by the base ToolStrip class. - /// - public bool ClickThrough - { - get { return _clickThrough; } - - set { _clickThrough = value; } - } - - protected override void WndProc(ref Message m) - { - base.WndProc(ref m); - var windowsMessage = (WindowsMessages) m.Msg; - if (_clickThrough && windowsMessage == WindowsMessages.WM_MOUSEACTIVATE && m.Result == (IntPtr) NativeConstants.MA_ACTIVATEANDEAT) - { - m.Result = (IntPtr) NativeConstants.MA_ACTIVATE; - } - } - } +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using System; +using System.Windows.Forms; +using Greenshot.Base.UnmanagedHelpers.Enums; + +namespace Greenshot.Editor.Controls +{ + /// + /// This is an extension of the default MenuStrip and allows us to click it even when the form doesn't have focus. + /// See: https://blogs.msdn.com/b/rickbrew/archive/2006/01/09/511003.aspx + /// + public class MenuStripEx : MenuStrip + { + private enum NativeConstants : uint + { + MA_ACTIVATE = 1, + MA_ACTIVATEANDEAT = 2, + } + + private bool _clickThrough; + + /// + /// Gets or sets whether the ToolStripEx honors item clicks when its containing form does not have input focus. + /// + /// + /// Default value is false, which is the same behavior provided by the base ToolStrip class. + /// + public bool ClickThrough + { + get { return _clickThrough; } + + set { _clickThrough = value; } + } + + protected override void WndProc(ref Message m) + { + base.WndProc(ref m); + var windowsMessage = (WindowsMessages) m.Msg; + if (_clickThrough && windowsMessage == WindowsMessages.WM_MOUSEACTIVATE && m.Result == (IntPtr) NativeConstants.MA_ACTIVATEANDEAT) + { + m.Result = (IntPtr) NativeConstants.MA_ACTIVATE; + } + } + } } \ No newline at end of file diff --git a/src/Greenshot/Controls/NonJumpingPanel.cs b/src/Greenshot.Editor/Controls/NonJumpingPanel.cs similarity index 96% rename from src/Greenshot/Controls/NonJumpingPanel.cs rename to src/Greenshot.Editor/Controls/NonJumpingPanel.cs index bb9f46d27..3ce0fbdaf 100644 --- a/src/Greenshot/Controls/NonJumpingPanel.cs +++ b/src/Greenshot.Editor/Controls/NonJumpingPanel.cs @@ -1,70 +1,70 @@ -/* - * Greenshot - a free and open source screenshot tool - * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom - * - * For more information see: https://getgreenshot.org/ - * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 1 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -using System.Drawing; -using System.Windows.Forms; - -namespace Greenshot.Controls -{ - /// - /// See: https://nickstips.wordpress.com/2010/03/03/c-panel-resets-scroll-position-after-focus-is-lost-and-regained/ - /// - public class NonJumpingPanel : Panel - { - protected override Point ScrollToControl(Control activeControl) - { - // Returning the current location prevents the panel from - // scrolling to the active control when the panel loses and regains focus - return DisplayRectangle.Location; - } - - /// - /// Add horizontal scrolling to the panel, when using the wheel and the shift key is pressed - /// - /// MouseEventArgs - protected override void OnMouseWheel(MouseEventArgs e) - { - //Check if Scrollbars available and CTRL key pressed -> Zoom IN OUT - if ((VScroll || HScroll) && (ModifierKeys & Keys.Control) == Keys.Control) - { - VScroll = false; - HScroll = false; - base.OnMouseWheel(e); - VScroll = true; - HScroll = true; - } - else - { - //Vertical Scoll with SHIFT key pressed - if (VScroll && (ModifierKeys & Keys.Shift) == Keys.Shift) - { - VScroll = false; - base.OnMouseWheel(e); - VScroll = true; - } - else - { - base.OnMouseWheel(e); - } - } - } - } +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using System.Drawing; +using System.Windows.Forms; + +namespace Greenshot.Editor.Controls +{ + /// + /// See: https://nickstips.wordpress.com/2010/03/03/c-panel-resets-scroll-position-after-focus-is-lost-and-regained/ + /// + public class NonJumpingPanel : Panel + { + protected override Point ScrollToControl(Control activeControl) + { + // Returning the current location prevents the panel from + // scrolling to the active control when the panel loses and regains focus + return DisplayRectangle.Location; + } + + /// + /// Add horizontal scrolling to the panel, when using the wheel and the shift key is pressed + /// + /// MouseEventArgs + protected override void OnMouseWheel(MouseEventArgs e) + { + //Check if Scrollbars available and CTRL key pressed -> Zoom IN OUT + if ((VScroll || HScroll) && (ModifierKeys & Keys.Control) == Keys.Control) + { + VScroll = false; + HScroll = false; + base.OnMouseWheel(e); + VScroll = true; + HScroll = true; + } + else + { + //Vertical Scoll with SHIFT key pressed + if (VScroll && (ModifierKeys & Keys.Shift) == Keys.Shift) + { + VScroll = false; + base.OnMouseWheel(e); + VScroll = true; + } + else + { + base.OnMouseWheel(e); + } + } + } + } } \ No newline at end of file diff --git a/src/Greenshot/Controls/Pipette.cs b/src/Greenshot.Editor/Controls/Pipette.cs similarity index 95% rename from src/Greenshot/Controls/Pipette.cs rename to src/Greenshot.Editor/Controls/Pipette.cs index f14d02150..d31017b92 100644 --- a/src/Greenshot/Controls/Pipette.cs +++ b/src/Greenshot.Editor/Controls/Pipette.cs @@ -1,204 +1,204 @@ -/* - * Greenshot - a free and open source screenshot tool - * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom - * - * For more information see: https://getgreenshot.org/ - * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 1 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -using System; -using System.ComponentModel; -using System.Drawing; -using System.Windows.Forms; -using Greenshot.Base.UnmanagedHelpers; -using Greenshot.Base.UnmanagedHelpers.Enums; -using Greenshot.Forms; -using ColorDialog = Greenshot.Forms.ColorDialog; - -namespace Greenshot.Controls -{ - /// - /// This code was supplied by Hi-Coder as a patch for Greenshot - /// Needed some modifications to be stable. - /// - public sealed class Pipette : Label, IMessageFilter, IDisposable - { - private MovableShowColorForm _movableShowColorForm; - private bool _dragging; - private Cursor _cursor; - private readonly Bitmap _image; - private const int VkEsc = 27; - - public event EventHandler PipetteUsed; - - public Pipette() - { - BorderStyle = BorderStyle.FixedSingle; - _dragging = false; - _image = (Bitmap) new ComponentResourceManager(typeof(ColorDialog)).GetObject("pipette.Image"); - Image = _image; - _cursor = CreateCursor(_image, 1, 14); - _movableShowColorForm = new MovableShowColorForm(); - Application.AddMessageFilter(this); - } - - /// - /// Create a cursor from the supplied bitmap & hotspot coordinates - /// - /// Bitmap to create an icon from - /// Hotspot X coordinate - /// Hotspot Y coordinate - /// Cursor - private static Cursor CreateCursor(Bitmap bitmap, int hotspotX, int hotspotY) - { - using SafeIconHandle iconHandle = new SafeIconHandle(bitmap.GetHicon()); - User32.GetIconInfo(iconHandle, out var iconInfo); - iconInfo.xHotspot = hotspotX; - iconInfo.yHotspot = hotspotY; - iconInfo.fIcon = false; - var icon = User32.CreateIconIndirect(ref iconInfo); - return new Cursor(icon); - } - - /// - /// The bulk of the clean-up code is implemented in Dispose(bool) - /// - public new void Dispose() - { - Dispose(true); - } - - /// - /// This Dispose is called from the Dispose and the Destructor. - /// - /// When disposing==true all non-managed resources should be freed too! - protected override void Dispose(bool disposing) - { - if (disposing) - { - if (_cursor != null) - { - _cursor.Dispose(); - } - - _movableShowColorForm?.Dispose(); - } - - _movableShowColorForm = null; - _cursor = null; - base.Dispose(disposing); - } - - /// - /// Handle the mouse down on the Pipette "label", we take the capture and move the zoomer to the current location - /// - /// MouseEventArgs - protected override void OnMouseDown(MouseEventArgs e) - { - if (e.Button == MouseButtons.Left) - { - User32.SetCapture(Handle); - _movableShowColorForm.MoveTo(PointToScreen(new Point(e.X, e.Y))); - } - - base.OnMouseDown(e); - } - - /// - /// Handle the mouse up on the Pipette "label", we release the capture and fire the PipetteUsed event - /// - /// MouseEventArgs - protected override void OnMouseUp(MouseEventArgs e) - { - if (e.Button == MouseButtons.Left) - { - //Release Capture should consume MouseUp when canceled with the escape key - User32.ReleaseCapture(); - PipetteUsed?.Invoke(this, new PipetteUsedArgs(_movableShowColorForm.color)); - } - - base.OnMouseUp(e); - } - - /// - /// Handle the mouse Move event, we move the ColorUnderCursor to the current location. - /// - /// MouseEventArgs - protected override void OnMouseMove(MouseEventArgs e) - { - if (_dragging) - { - //display the form on the right side of the cursor by default; - Point zp = PointToScreen(new Point(e.X, e.Y)); - _movableShowColorForm.MoveTo(zp); - } - - base.OnMouseMove(e); - } - - /// - /// Handle the MouseCaptureChanged event - /// - /// - protected override void OnMouseCaptureChanged(EventArgs e) - { - if (Capture) - { - _dragging = true; - Image = null; - Cursor c = _cursor; - Cursor = c; - _movableShowColorForm.Visible = true; - } - else - { - _dragging = false; - Image = _image; - Cursor = Cursors.Arrow; - _movableShowColorForm.Visible = false; - } - - Update(); - base.OnMouseCaptureChanged(e); - } - - public bool PreFilterMessage(ref Message m) - { - if (_dragging) - { - if (m.Msg == (int) WindowsMessages.WM_CHAR) - { - if ((int) m.WParam == VkEsc) - { - User32.ReleaseCapture(); - } - } - } - - return false; - } - } - - public class PipetteUsedArgs : EventArgs - { - public Color Color; - - public PipetteUsedArgs(Color c) - { - Color = c; - } - } +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using System; +using System.ComponentModel; +using System.Drawing; +using System.Windows.Forms; +using Greenshot.Base.UnmanagedHelpers; +using Greenshot.Base.UnmanagedHelpers.Enums; +using Greenshot.Editor.Forms; +using ColorDialog = Greenshot.Editor.Forms.ColorDialog; + +namespace Greenshot.Editor.Controls +{ + /// + /// This code was supplied by Hi-Coder as a patch for Greenshot + /// Needed some modifications to be stable. + /// + public sealed class Pipette : Label, IMessageFilter, IDisposable + { + private MovableShowColorForm _movableShowColorForm; + private bool _dragging; + private Cursor _cursor; + private readonly Bitmap _image; + private const int VkEsc = 27; + + public event EventHandler PipetteUsed; + + public Pipette() + { + BorderStyle = BorderStyle.FixedSingle; + _dragging = false; + _image = (Bitmap) new ComponentResourceManager(typeof(ColorDialog)).GetObject("pipette.Image"); + Image = _image; + _cursor = CreateCursor(_image, 1, 14); + _movableShowColorForm = new MovableShowColorForm(); + Application.AddMessageFilter(this); + } + + /// + /// Create a cursor from the supplied bitmap & hotspot coordinates + /// + /// Bitmap to create an icon from + /// Hotspot X coordinate + /// Hotspot Y coordinate + /// Cursor + private static Cursor CreateCursor(Bitmap bitmap, int hotspotX, int hotspotY) + { + using SafeIconHandle iconHandle = new SafeIconHandle(bitmap.GetHicon()); + User32.GetIconInfo(iconHandle, out var iconInfo); + iconInfo.xHotspot = hotspotX; + iconInfo.yHotspot = hotspotY; + iconInfo.fIcon = false; + var icon = User32.CreateIconIndirect(ref iconInfo); + return new Cursor(icon); + } + + /// + /// The bulk of the clean-up code is implemented in Dispose(bool) + /// + public new void Dispose() + { + Dispose(true); + } + + /// + /// This Dispose is called from the Dispose and the Destructor. + /// + /// When disposing==true all non-managed resources should be freed too! + protected override void Dispose(bool disposing) + { + if (disposing) + { + if (_cursor != null) + { + _cursor.Dispose(); + } + + _movableShowColorForm?.Dispose(); + } + + _movableShowColorForm = null; + _cursor = null; + base.Dispose(disposing); + } + + /// + /// Handle the mouse down on the Pipette "label", we take the capture and move the zoomer to the current location + /// + /// MouseEventArgs + protected override void OnMouseDown(MouseEventArgs e) + { + if (e.Button == MouseButtons.Left) + { + User32.SetCapture(Handle); + _movableShowColorForm.MoveTo(PointToScreen(new Point(e.X, e.Y))); + } + + base.OnMouseDown(e); + } + + /// + /// Handle the mouse up on the Pipette "label", we release the capture and fire the PipetteUsed event + /// + /// MouseEventArgs + protected override void OnMouseUp(MouseEventArgs e) + { + if (e.Button == MouseButtons.Left) + { + //Release Capture should consume MouseUp when canceled with the escape key + User32.ReleaseCapture(); + PipetteUsed?.Invoke(this, new PipetteUsedArgs(_movableShowColorForm.color)); + } + + base.OnMouseUp(e); + } + + /// + /// Handle the mouse Move event, we move the ColorUnderCursor to the current location. + /// + /// MouseEventArgs + protected override void OnMouseMove(MouseEventArgs e) + { + if (_dragging) + { + //display the form on the right side of the cursor by default; + Point zp = PointToScreen(new Point(e.X, e.Y)); + _movableShowColorForm.MoveTo(zp); + } + + base.OnMouseMove(e); + } + + /// + /// Handle the MouseCaptureChanged event + /// + /// + protected override void OnMouseCaptureChanged(EventArgs e) + { + if (Capture) + { + _dragging = true; + Image = null; + Cursor c = _cursor; + Cursor = c; + _movableShowColorForm.Visible = true; + } + else + { + _dragging = false; + Image = _image; + Cursor = Cursors.Arrow; + _movableShowColorForm.Visible = false; + } + + Update(); + base.OnMouseCaptureChanged(e); + } + + public bool PreFilterMessage(ref Message m) + { + if (_dragging) + { + if (m.Msg == (int) WindowsMessages.WM_CHAR) + { + if ((int) m.WParam == VkEsc) + { + User32.ReleaseCapture(); + } + } + } + + return false; + } + } + + public class PipetteUsedArgs : EventArgs + { + public Color Color; + + public PipetteUsedArgs(Color c) + { + Color = c; + } + } } \ No newline at end of file diff --git a/src/Greenshot/Controls/ToolStripColorButton.cs b/src/Greenshot.Editor/Controls/ToolStripColorButton.cs similarity index 94% rename from src/Greenshot/Controls/ToolStripColorButton.cs rename to src/Greenshot.Editor/Controls/ToolStripColorButton.cs index 86bfd7241..91386b4f4 100644 --- a/src/Greenshot/Controls/ToolStripColorButton.cs +++ b/src/Greenshot.Editor/Controls/ToolStripColorButton.cs @@ -1,97 +1,97 @@ -/* - * Greenshot - a free and open source screenshot tool - * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom - * - * For more information see: https://getgreenshot.org/ - * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 1 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -using System; -using System.ComponentModel; -using System.Drawing; -using System.Drawing.Drawing2D; -using System.Windows.Forms; -using Greenshot.Base.Controls; -using ColorDialog = Greenshot.Forms.ColorDialog; - -namespace Greenshot.Controls -{ - public class ToolStripColorButton : ToolStripButton, INotifyPropertyChanged, IGreenshotLanguageBindable - { - public event PropertyChangedEventHandler PropertyChanged; - - [Category("Greenshot"), DefaultValue(null), Description("Specifies key of the language file to use when displaying the text.")] - public string LanguageKey { get; set; } - - private Color _selectedColor = Color.Transparent; - - public ToolStripColorButton() - { - Click += ColorButtonClick; - } - - public Color SelectedColor - { - get { return _selectedColor; } - set - { - _selectedColor = value; - - Brush brush; - if (value != Color.Transparent) - { - brush = new SolidBrush(value); - } - else - { - brush = new HatchBrush(HatchStyle.Percent50, Color.White, Color.Gray); - } - - if (Image != null) - { - using Graphics graphics = Graphics.FromImage(Image); - graphics.FillRectangle(brush, new Rectangle(0, 13, 16, 3)); - } - - // cleanup GDI Object - brush.Dispose(); - Invalidate(); - } - } - - private void ColorButtonClick(object sender, EventArgs e) - { - var colorDialog = new ColorDialog - { - Color = SelectedColor - }; - // Using the parent to make sure the dialog doesn't show on another window - colorDialog.ShowDialog(Parent.Parent); - if (colorDialog.DialogResult == DialogResult.Cancel) - { - return; - } - - if (colorDialog.Color.Equals(SelectedColor)) - { - return; - } - - SelectedColor = colorDialog.Color; - PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("SelectedColor")); - } - } +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using System; +using System.ComponentModel; +using System.Drawing; +using System.Drawing.Drawing2D; +using System.Windows.Forms; +using Greenshot.Base.Controls; +using ColorDialog = Greenshot.Editor.Forms.ColorDialog; + +namespace Greenshot.Editor.Controls +{ + public class ToolStripColorButton : ToolStripButton, INotifyPropertyChanged, IGreenshotLanguageBindable + { + public event PropertyChangedEventHandler PropertyChanged; + + [Category("Greenshot"), DefaultValue(null), Description("Specifies key of the language file to use when displaying the text.")] + public string LanguageKey { get; set; } + + private Color _selectedColor = Color.Transparent; + + public ToolStripColorButton() + { + Click += ColorButtonClick; + } + + public Color SelectedColor + { + get { return _selectedColor; } + set + { + _selectedColor = value; + + Brush brush; + if (value != Color.Transparent) + { + brush = new SolidBrush(value); + } + else + { + brush = new HatchBrush(HatchStyle.Percent50, Color.White, Color.Gray); + } + + if (Image != null) + { + using Graphics graphics = Graphics.FromImage(Image); + graphics.FillRectangle(brush, new Rectangle(0, 13, 16, 3)); + } + + // cleanup GDI Object + brush.Dispose(); + Invalidate(); + } + } + + private void ColorButtonClick(object sender, EventArgs e) + { + var colorDialog = new ColorDialog + { + Color = SelectedColor + }; + // Using the parent to make sure the dialog doesn't show on another window + colorDialog.ShowDialog(Parent.Parent); + if (colorDialog.DialogResult == DialogResult.Cancel) + { + return; + } + + if (colorDialog.Color.Equals(SelectedColor)) + { + return; + } + + SelectedColor = colorDialog.Color; + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("SelectedColor")); + } + } } \ No newline at end of file diff --git a/src/Greenshot/Controls/ToolStripEx.cs b/src/Greenshot.Editor/Controls/ToolStripEx.cs similarity index 95% rename from src/Greenshot/Controls/ToolStripEx.cs rename to src/Greenshot.Editor/Controls/ToolStripEx.cs index 216873481..39e1859cc 100644 --- a/src/Greenshot/Controls/ToolStripEx.cs +++ b/src/Greenshot.Editor/Controls/ToolStripEx.cs @@ -1,66 +1,66 @@ -/* - * Greenshot - a free and open source screenshot tool - * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom - * - * For more information see: https://getgreenshot.org/ - * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 1 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -using System; -using System.Windows.Forms; - -namespace Greenshot.Controls -{ - /// - /// This is an extension of the default ToolStrip and allows us to click it even when the form doesn't have focus. - /// See: https://blogs.msdn.com/b/rickbrew/archive/2006/01/09/511003.aspx - /// - internal class ToolStripEx : ToolStrip - { - private const int WM_MOUSEACTIVATE = 0x21; - - private enum NativeConstants : uint - { - MA_ACTIVATE = 1, - MA_ACTIVATEANDEAT = 2, - } - - private bool _clickThrough; - - /// - /// Gets or sets whether the ToolStripEx honors item clicks when its containing form does not have input focus. - /// - /// - /// Default value is false, which is the same behavior provided by the base ToolStrip class. - /// - - public bool ClickThrough - { - get { return _clickThrough; } - - set { _clickThrough = value; } - } - - protected override void WndProc(ref Message m) - { - base.WndProc(ref m); - if (_clickThrough && m.Msg == WM_MOUSEACTIVATE && m.Result == (IntPtr) NativeConstants.MA_ACTIVATEANDEAT) - { - m.Result = (IntPtr) NativeConstants.MA_ACTIVATE; - } - } - } +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using System; +using System.Windows.Forms; + +namespace Greenshot.Editor.Controls +{ + /// + /// This is an extension of the default ToolStrip and allows us to click it even when the form doesn't have focus. + /// See: https://blogs.msdn.com/b/rickbrew/archive/2006/01/09/511003.aspx + /// + internal class ToolStripEx : ToolStrip + { + private const int WM_MOUSEACTIVATE = 0x21; + + private enum NativeConstants : uint + { + MA_ACTIVATE = 1, + MA_ACTIVATEANDEAT = 2, + } + + private bool _clickThrough; + + /// + /// Gets or sets whether the ToolStripEx honors item clicks when its containing form does not have input focus. + /// + /// + /// Default value is false, which is the same behavior provided by the base ToolStrip class. + /// + + public bool ClickThrough + { + get { return _clickThrough; } + + set { _clickThrough = value; } + } + + protected override void WndProc(ref Message m) + { + base.WndProc(ref m); + if (_clickThrough && m.Msg == WM_MOUSEACTIVATE && m.Result == (IntPtr) NativeConstants.MA_ACTIVATEANDEAT) + { + m.Result = (IntPtr) NativeConstants.MA_ACTIVATE; + } + } + } } \ No newline at end of file diff --git a/src/Greenshot/Controls/ToolStripNumericUpDown.cs b/src/Greenshot.Editor/Controls/ToolStripNumericUpDown.cs similarity index 96% rename from src/Greenshot/Controls/ToolStripNumericUpDown.cs rename to src/Greenshot.Editor/Controls/ToolStripNumericUpDown.cs index a27266152..89caa257c 100644 --- a/src/Greenshot/Controls/ToolStripNumericUpDown.cs +++ b/src/Greenshot.Editor/Controls/ToolStripNumericUpDown.cs @@ -1,88 +1,88 @@ -/* - * Greenshot - a free and open source screenshot tool - * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom - * - * For more information see: https://getgreenshot.org/ - * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 1 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -using System; -using System.ComponentModel; -using System.Windows.Forms; -using System.Windows.Forms.Design; - -namespace Greenshot.Controls -{ - [ToolStripItemDesignerAvailability(ToolStripItemDesignerAvailability.ToolStrip | ToolStripItemDesignerAvailability.StatusStrip)] - public class ToolStripNumericUpDown : ToolStripControlHost, INotifyPropertyChanged - { - public event PropertyChangedEventHandler PropertyChanged; - - public ToolStripNumericUpDown() : base(new NumericUpDown()) - { - } - - public NumericUpDown NumericUpDown => Control as NumericUpDown; - - public decimal Value - { - get { return NumericUpDown.Value; } - set { NumericUpDown.Value = value; } - } - - public decimal Minimum - { - get { return NumericUpDown.Minimum; } - set { NumericUpDown.Minimum = value; } - } - - public decimal Maximum - { - get { return NumericUpDown.Maximum; } - set { NumericUpDown.Maximum = value; } - } - - public decimal Increment - { - get { return NumericUpDown.Increment; } - set { NumericUpDown.Increment = value; } - } - - public int DecimalPlaces - { - get { return NumericUpDown.DecimalPlaces; } - set { NumericUpDown.DecimalPlaces = value; } - } - - - protected override void OnSubscribeControlEvents(Control control) - { - base.OnSubscribeControlEvents(control); - NumericUpDown.ValueChanged += _valueChanged; - } - - protected override void OnUnsubscribeControlEvents(Control control) - { - base.OnUnsubscribeControlEvents(control); - NumericUpDown.ValueChanged -= _valueChanged; - } - - private void _valueChanged(object sender, EventArgs e) - { - PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Value")); - } - } +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using System; +using System.ComponentModel; +using System.Windows.Forms; +using System.Windows.Forms.Design; + +namespace Greenshot.Editor.Controls +{ + [ToolStripItemDesignerAvailability(ToolStripItemDesignerAvailability.ToolStrip | ToolStripItemDesignerAvailability.StatusStrip)] + public class ToolStripNumericUpDown : ToolStripControlHost, INotifyPropertyChanged + { + public event PropertyChangedEventHandler PropertyChanged; + + public ToolStripNumericUpDown() : base(new NumericUpDown()) + { + } + + public NumericUpDown NumericUpDown => Control as NumericUpDown; + + public decimal Value + { + get { return NumericUpDown.Value; } + set { NumericUpDown.Value = value; } + } + + public decimal Minimum + { + get { return NumericUpDown.Minimum; } + set { NumericUpDown.Minimum = value; } + } + + public decimal Maximum + { + get { return NumericUpDown.Maximum; } + set { NumericUpDown.Maximum = value; } + } + + public decimal Increment + { + get { return NumericUpDown.Increment; } + set { NumericUpDown.Increment = value; } + } + + public int DecimalPlaces + { + get { return NumericUpDown.DecimalPlaces; } + set { NumericUpDown.DecimalPlaces = value; } + } + + + protected override void OnSubscribeControlEvents(Control control) + { + base.OnSubscribeControlEvents(control); + NumericUpDown.ValueChanged += _valueChanged; + } + + protected override void OnUnsubscribeControlEvents(Control control) + { + base.OnUnsubscribeControlEvents(control); + NumericUpDown.ValueChanged -= _valueChanged; + } + + private void _valueChanged(object sender, EventArgs e) + { + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Value")); + } + } } \ No newline at end of file diff --git a/src/Greenshot/Destinations/EditorDestination.cs b/src/Greenshot.Editor/Destinations/EditorDestination.cs similarity index 95% rename from src/Greenshot/Destinations/EditorDestination.cs rename to src/Greenshot.Editor/Destinations/EditorDestination.cs index f7cf6d2e8..326397ec9 100644 --- a/src/Greenshot/Destinations/EditorDestination.cs +++ b/src/Greenshot.Editor/Destinations/EditorDestination.cs @@ -1,154 +1,154 @@ -/* - * Greenshot - a free and open source screenshot tool - * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom - * - * For more information see: https://getgreenshot.org/ - * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 1 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -using System; -using System.Collections.Generic; -using System.Drawing; -using Greenshot.Base.Core; -using Greenshot.Base.IniFile; -using Greenshot.Base.Interfaces; -using Greenshot.Base.Interfaces.Forms; -using Greenshot.Configuration; -using Greenshot.Forms; -using log4net; - -namespace Greenshot.Destinations -{ - /// - /// Description of EditorDestination. - /// - public class EditorDestination : AbstractDestination - { - private static readonly ILog LOG = LogManager.GetLogger(typeof(EditorDestination)); - private static readonly EditorConfiguration editorConfiguration = IniConfig.GetIniSection(); - public const string DESIGNATION = "Editor"; - private readonly IImageEditor editor; - private static readonly Image greenshotIcon = GreenshotResources.GetGreenshotIcon().ToBitmap(); - - public EditorDestination() - { - // Do not remove, is needed for the framework - } - - public EditorDestination(IImageEditor editor) - { - this.editor = editor; - } - - public override string Designation => DESIGNATION; - - public override string Description - { - get - { - if (editor == null) - { - return Language.GetString(LangKey.settings_destination_editor); - } - - return Language.GetString(LangKey.settings_destination_editor) + " - " + editor.CaptureDetails.Title; - } - } - - public override int Priority => 1; - - public override bool IsDynamic => true; - - public override Image DisplayIcon => greenshotIcon; - - public override IEnumerable DynamicDestinations() - { - foreach (IImageEditor someEditor in ImageEditorForm.Editors) - { - yield return new EditorDestination(someEditor); - } - } - - public override ExportInformation ExportCapture(bool manuallyInitiated, ISurface surface, ICaptureDetails captureDetails) - { - ExportInformation exportInformation = new ExportInformation(Designation, Description); - // Make sure we collect the garbage before opening the screenshot - GC.Collect(); - GC.WaitForPendingFinalizers(); - - bool modified = surface.Modified; - if (editor == null) - { - if (editorConfiguration.ReuseEditor) - { - foreach (IImageEditor openedEditor in ImageEditorForm.Editors) - { - if (openedEditor.Surface.Modified) continue; - - openedEditor.Surface = surface; - exportInformation.ExportMade = true; - break; - } - } - - if (!exportInformation.ExportMade) - { - try - { - ImageEditorForm editorForm = new ImageEditorForm(surface, !surface.Modified); // Output made?? - - if (!string.IsNullOrEmpty(captureDetails.Filename)) - { - editorForm.SetImagePath(captureDetails.Filename); - } - - editorForm.Show(); - editorForm.Activate(); - LOG.Debug("Finished opening Editor"); - exportInformation.ExportMade = true; - } - catch (Exception e) - { - LOG.Error(e); - exportInformation.ErrorMessage = e.Message; - } - } - } - else - { - try - { - using (Image image = surface.GetImageForExport()) - { - editor.Surface.AddImageContainer(image, 10, 10); - } - - exportInformation.ExportMade = true; - } - catch (Exception e) - { - LOG.Error(e); - exportInformation.ErrorMessage = e.Message; - } - } - - ProcessExport(exportInformation, surface); - // Workaround for the modified flag when using the editor. - surface.Modified = modified; - return exportInformation; - } - } +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using System; +using System.Collections.Generic; +using System.Drawing; +using Greenshot.Base.Core; +using Greenshot.Base.IniFile; +using Greenshot.Base.Interfaces; +using Greenshot.Base.Interfaces.Forms; +using Greenshot.Editor.Configuration; +using Greenshot.Editor.Forms; +using log4net; + +namespace Greenshot.Editor.Destinations +{ + /// + /// Description of EditorDestination. + /// + public class EditorDestination : AbstractDestination + { + private static readonly ILog LOG = LogManager.GetLogger(typeof(EditorDestination)); + private static readonly EditorConfiguration editorConfiguration = IniConfig.GetIniSection(); + public const string DESIGNATION = "Editor"; + private readonly IImageEditor editor; + private static readonly Image greenshotIcon = GreenshotResources.GetGreenshotIcon().ToBitmap(); + + public EditorDestination() + { + // Do not remove, is needed for the framework + } + + public EditorDestination(IImageEditor editor) + { + this.editor = editor; + } + + public override string Designation => DESIGNATION; + + public override string Description + { + get + { + if (editor == null) + { + return Language.GetString(LangKey.settings_destination_editor); + } + + return Language.GetString(LangKey.settings_destination_editor) + " - " + editor.CaptureDetails.Title; + } + } + + public override int Priority => 1; + + public override bool IsDynamic => true; + + public override Image DisplayIcon => greenshotIcon; + + public override IEnumerable DynamicDestinations() + { + foreach (IImageEditor someEditor in ImageEditorForm.Editors) + { + yield return new EditorDestination(someEditor); + } + } + + public override ExportInformation ExportCapture(bool manuallyInitiated, ISurface surface, ICaptureDetails captureDetails) + { + ExportInformation exportInformation = new ExportInformation(Designation, Description); + // Make sure we collect the garbage before opening the screenshot + GC.Collect(); + GC.WaitForPendingFinalizers(); + + bool modified = surface.Modified; + if (editor == null) + { + if (editorConfiguration.ReuseEditor) + { + foreach (IImageEditor openedEditor in ImageEditorForm.Editors) + { + if (openedEditor.Surface.Modified) continue; + + openedEditor.Surface = surface; + exportInformation.ExportMade = true; + break; + } + } + + if (!exportInformation.ExportMade) + { + try + { + ImageEditorForm editorForm = new ImageEditorForm(surface, !surface.Modified); // Output made?? + + if (!string.IsNullOrEmpty(captureDetails.Filename)) + { + editorForm.SetImagePath(captureDetails.Filename); + } + + editorForm.Show(); + editorForm.Activate(); + LOG.Debug("Finished opening Editor"); + exportInformation.ExportMade = true; + } + catch (Exception e) + { + LOG.Error(e); + exportInformation.ErrorMessage = e.Message; + } + } + } + else + { + try + { + using (Image image = surface.GetImageForExport()) + { + editor.Surface.AddImageContainer(image, 10, 10); + } + + exportInformation.ExportMade = true; + } + catch (Exception e) + { + LOG.Error(e); + exportInformation.ErrorMessage = e.Message; + } + } + + ProcessExport(exportInformation, surface); + // Workaround for the modified flag when using the editor. + surface.Modified = modified; + return exportInformation; + } + } } \ No newline at end of file diff --git a/src/Greenshot/Drawing/Adorners/AbstractAdorner.cs b/src/Greenshot.Editor/Drawing/Adorners/AbstractAdorner.cs similarity index 96% rename from src/Greenshot/Drawing/Adorners/AbstractAdorner.cs rename to src/Greenshot.Editor/Drawing/Adorners/AbstractAdorner.cs index a8bd3cd47..028306e24 100644 --- a/src/Greenshot/Drawing/Adorners/AbstractAdorner.cs +++ b/src/Greenshot.Editor/Drawing/Adorners/AbstractAdorner.cs @@ -1,156 +1,156 @@ -/* - * Greenshot - a free and open source screenshot tool - * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom, Francis Noel - * - * For more information see: https://getgreenshot.org/ - * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 1 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -using System.Drawing; -using System.Drawing.Drawing2D; -using System.Windows.Forms; -using Greenshot.Base.Core; -using Greenshot.Base.Interfaces.Drawing; -using Greenshot.Base.Interfaces.Drawing.Adorners; - -namespace Greenshot.Drawing.Adorners -{ - public class AbstractAdorner : IAdorner - { - public virtual EditStatus EditStatus { get; protected set; } = EditStatus.IDLE; - - private static readonly Size DefaultSize = new Size(6, 6); - protected Size _size; - - public AbstractAdorner(IDrawableContainer owner) - { - _size = DpiHelper.ScaleWithDpi(DefaultSize, 0); - Owner = owner; - } - - /// - /// Returns the cursor for when the mouse is over the adorner - /// - public virtual Cursor Cursor - { - get { return Cursors.SizeAll; } - } - - public virtual IDrawableContainer Owner { get; set; } - - /// - /// Test if the point is inside the adorner - /// - /// - /// - public virtual bool HitTest(Point point) - { - Rectangle hitBounds = Bounds; - hitBounds.Inflate(3, 3); - return hitBounds.Contains(point); - } - - /// - /// Handle the mouse down - /// - /// - /// - public virtual void MouseDown(object sender, MouseEventArgs mouseEventArgs) - { - } - - /// - /// Handle the mouse move - /// - /// - /// - public virtual void MouseMove(object sender, MouseEventArgs mouseEventArgs) - { - } - - /// - /// Handle the mouse up - /// - /// - /// - public virtual void MouseUp(object sender, MouseEventArgs mouseEventArgs) - { - EditStatus = EditStatus.IDLE; - } - - /// - /// Return the location of the adorner - /// - public virtual Point Location { get; set; } - - /// - /// Return the bounds of the Adorner - /// - public virtual Rectangle Bounds - { - get - { - Point location = Location; - return new Rectangle(location.X - (_size.Width / 2), location.Y - (_size.Height / 2), _size.Width, _size.Height); - } - } - - /// - /// Return the bounds of the Adorner as displayed on the parent Surface - /// - protected virtual Rectangle BoundsOnSurface - { - get - { - Point displayLocation = Owner.Parent.ToSurfaceCoordinates(Location); - return new Rectangle(displayLocation.X - _size.Width / 2, displayLocation.Y - _size.Height / 2, _size.Width, _size.Height); - } - } - - /// - /// The adorner is active if the edit status is not idle or undrawn - /// - public virtual bool IsActive - { - get { return EditStatus != EditStatus.IDLE && EditStatus != EditStatus.UNDRAWN; } - } - - /// - /// Adjust UI elements to the supplied DPI settings - /// - /// uint - public void AdjustToDpi(uint dpi) - { - _size = DpiHelper.ScaleWithDpi(DefaultSize, dpi); - } - - /// - /// Draw the adorner - /// - /// PaintEventArgs - public virtual void Paint(PaintEventArgs paintEventArgs) - { - } - - /// - /// We ignore the Transform, as the coordinates are directly bound to those of the owner - /// - /// - public virtual void Transform(Matrix matrix) - { - } - } +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom, Francis Noel + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using System.Drawing; +using System.Drawing.Drawing2D; +using System.Windows.Forms; +using Greenshot.Base.Core; +using Greenshot.Base.Interfaces.Drawing; +using Greenshot.Base.Interfaces.Drawing.Adorners; + +namespace Greenshot.Editor.Drawing.Adorners +{ + public class AbstractAdorner : IAdorner + { + public virtual EditStatus EditStatus { get; protected set; } = EditStatus.IDLE; + + private static readonly Size DefaultSize = new Size(6, 6); + protected Size _size; + + public AbstractAdorner(IDrawableContainer owner) + { + _size = DpiHelper.ScaleWithDpi(DefaultSize, 0); + Owner = owner; + } + + /// + /// Returns the cursor for when the mouse is over the adorner + /// + public virtual Cursor Cursor + { + get { return Cursors.SizeAll; } + } + + public virtual IDrawableContainer Owner { get; set; } + + /// + /// Test if the point is inside the adorner + /// + /// + /// + public virtual bool HitTest(Point point) + { + Rectangle hitBounds = Bounds; + hitBounds.Inflate(3, 3); + return hitBounds.Contains(point); + } + + /// + /// Handle the mouse down + /// + /// + /// + public virtual void MouseDown(object sender, MouseEventArgs mouseEventArgs) + { + } + + /// + /// Handle the mouse move + /// + /// + /// + public virtual void MouseMove(object sender, MouseEventArgs mouseEventArgs) + { + } + + /// + /// Handle the mouse up + /// + /// + /// + public virtual void MouseUp(object sender, MouseEventArgs mouseEventArgs) + { + EditStatus = EditStatus.IDLE; + } + + /// + /// Return the location of the adorner + /// + public virtual Point Location { get; set; } + + /// + /// Return the bounds of the Adorner + /// + public virtual Rectangle Bounds + { + get + { + Point location = Location; + return new Rectangle(location.X - (_size.Width / 2), location.Y - (_size.Height / 2), _size.Width, _size.Height); + } + } + + /// + /// Return the bounds of the Adorner as displayed on the parent Surface + /// + protected virtual Rectangle BoundsOnSurface + { + get + { + Point displayLocation = Owner.Parent.ToSurfaceCoordinates(Location); + return new Rectangle(displayLocation.X - _size.Width / 2, displayLocation.Y - _size.Height / 2, _size.Width, _size.Height); + } + } + + /// + /// The adorner is active if the edit status is not idle or undrawn + /// + public virtual bool IsActive + { + get { return EditStatus != EditStatus.IDLE && EditStatus != EditStatus.UNDRAWN; } + } + + /// + /// Adjust UI elements to the supplied DPI settings + /// + /// uint + public void AdjustToDpi(uint dpi) + { + _size = DpiHelper.ScaleWithDpi(DefaultSize, dpi); + } + + /// + /// Draw the adorner + /// + /// PaintEventArgs + public virtual void Paint(PaintEventArgs paintEventArgs) + { + } + + /// + /// We ignore the Transform, as the coordinates are directly bound to those of the owner + /// + /// + public virtual void Transform(Matrix matrix) + { + } + } } \ No newline at end of file diff --git a/src/Greenshot/Drawing/Adorners/MoveAdorner.cs b/src/Greenshot.Editor/Drawing/Adorners/MoveAdorner.cs similarity index 96% rename from src/Greenshot/Drawing/Adorners/MoveAdorner.cs rename to src/Greenshot.Editor/Drawing/Adorners/MoveAdorner.cs index 9ba4d1f46..ad1355ced 100644 --- a/src/Greenshot/Drawing/Adorners/MoveAdorner.cs +++ b/src/Greenshot.Editor/Drawing/Adorners/MoveAdorner.cs @@ -1,166 +1,166 @@ -/* - * Greenshot - a free and open source screenshot tool - * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom, Francis Noel - * - * For more information see: https://getgreenshot.org/ - * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 1 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -using Greenshot.Helpers; -using System.Drawing; -using System.Drawing.Drawing2D; -using System.Windows.Forms; -using Greenshot.Base.Interfaces.Drawing; - -namespace Greenshot.Drawing.Adorners -{ - /// - /// This is the adorner for the line based containers - /// - public class MoveAdorner : AbstractAdorner - { - private Rectangle _boundsBeforeResize = Rectangle.Empty; - private RectangleF _boundsAfterResize = RectangleF.Empty; - - public Positions Position { get; private set; } - - public MoveAdorner(IDrawableContainer owner, Positions position) : base(owner) - { - Position = position; - } - - /// - /// Returns the cursor for when the mouse is over the adorner - /// - public override Cursor Cursor => Cursors.SizeAll; - - /// - /// Handle the mouse down - /// - /// - /// - public override void MouseDown(object sender, MouseEventArgs mouseEventArgs) - { - EditStatus = EditStatus.RESIZING; - _boundsBeforeResize = new Rectangle(Owner.Left, Owner.Top, Owner.Width, Owner.Height); - _boundsAfterResize = _boundsBeforeResize; - } - - /// - /// Handle the mouse move - /// - /// - /// - public override void MouseMove(object sender, MouseEventArgs mouseEventArgs) - { - if (EditStatus != EditStatus.RESIZING) - { - return; - } - - Owner.Invalidate(); - Owner.MakeBoundsChangeUndoable(false); - - // reset "workbench" rectangle to current bounds - _boundsAfterResize.X = _boundsBeforeResize.X; - _boundsAfterResize.Y = _boundsBeforeResize.Y; - _boundsAfterResize.Width = _boundsBeforeResize.Width; - _boundsAfterResize.Height = _boundsBeforeResize.Height; - - // calculate scaled rectangle - ScaleHelper.Scale(ref _boundsAfterResize, Position, new PointF(mouseEventArgs.X, mouseEventArgs.Y), ScaleHelper.GetScaleOptions()); - - // apply scaled bounds to this DrawableContainer - Owner.ApplyBounds(_boundsAfterResize); - - Owner.Invalidate(); - } - - /// - /// Return the location of the adorner - /// - public override Point Location - { - get - { - int x = 0, y = 0; - switch (Position) - { - case Positions.TopLeft: - x = Owner.Left; - y = Owner.Top; - break; - case Positions.BottomLeft: - x = Owner.Left; - y = Owner.Top + Owner.Height; - break; - case Positions.MiddleLeft: - x = Owner.Left; - y = Owner.Top + (Owner.Height / 2); - break; - case Positions.TopCenter: - x = Owner.Left + (Owner.Width / 2); - y = Owner.Top; - break; - case Positions.BottomCenter: - x = Owner.Left + (Owner.Width / 2); - y = Owner.Top + Owner.Height; - break; - case Positions.TopRight: - x = Owner.Left + Owner.Width; - y = Owner.Top; - break; - case Positions.BottomRight: - x = Owner.Left + Owner.Width; - y = Owner.Top + Owner.Height; - break; - case Positions.MiddleRight: - x = Owner.Left + Owner.Width; - y = Owner.Top + (Owner.Height / 2); - break; - } - - return new Point(x, y); - } - } - - /// - /// Draw the adorner - /// - /// PaintEventArgs - public override void Paint(PaintEventArgs paintEventArgs) - { - Graphics targetGraphics = paintEventArgs.Graphics; - - var bounds = BoundsOnSurface; - GraphicsState state = targetGraphics.Save(); - - targetGraphics.CompositingMode = CompositingMode.SourceCopy; - - try - { - targetGraphics.FillRectangle(Brushes.Black, bounds); - targetGraphics.DrawRectangle(new Pen(Brushes.White), bounds); - } - catch - { - // Ignore, BUG-2065 - } - - targetGraphics.Restore(state); - } - } +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom, Francis Noel + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using System.Drawing; +using System.Drawing.Drawing2D; +using System.Windows.Forms; +using Greenshot.Base.Interfaces.Drawing; +using Greenshot.Editor.Helpers; + +namespace Greenshot.Editor.Drawing.Adorners +{ + /// + /// This is the adorner for the line based containers + /// + public class MoveAdorner : AbstractAdorner + { + private Rectangle _boundsBeforeResize = Rectangle.Empty; + private RectangleF _boundsAfterResize = RectangleF.Empty; + + public Positions Position { get; private set; } + + public MoveAdorner(IDrawableContainer owner, Positions position) : base(owner) + { + Position = position; + } + + /// + /// Returns the cursor for when the mouse is over the adorner + /// + public override Cursor Cursor => Cursors.SizeAll; + + /// + /// Handle the mouse down + /// + /// + /// + public override void MouseDown(object sender, MouseEventArgs mouseEventArgs) + { + EditStatus = EditStatus.RESIZING; + _boundsBeforeResize = new Rectangle(Owner.Left, Owner.Top, Owner.Width, Owner.Height); + _boundsAfterResize = _boundsBeforeResize; + } + + /// + /// Handle the mouse move + /// + /// + /// + public override void MouseMove(object sender, MouseEventArgs mouseEventArgs) + { + if (EditStatus != EditStatus.RESIZING) + { + return; + } + + Owner.Invalidate(); + Owner.MakeBoundsChangeUndoable(false); + + // reset "workbench" rectangle to current bounds + _boundsAfterResize.X = _boundsBeforeResize.X; + _boundsAfterResize.Y = _boundsBeforeResize.Y; + _boundsAfterResize.Width = _boundsBeforeResize.Width; + _boundsAfterResize.Height = _boundsBeforeResize.Height; + + // calculate scaled rectangle + ScaleHelper.Scale(ref _boundsAfterResize, Position, new PointF(mouseEventArgs.X, mouseEventArgs.Y), ScaleHelper.GetScaleOptions()); + + // apply scaled bounds to this DrawableContainer + Owner.ApplyBounds(_boundsAfterResize); + + Owner.Invalidate(); + } + + /// + /// Return the location of the adorner + /// + public override Point Location + { + get + { + int x = 0, y = 0; + switch (Position) + { + case Positions.TopLeft: + x = Owner.Left; + y = Owner.Top; + break; + case Positions.BottomLeft: + x = Owner.Left; + y = Owner.Top + Owner.Height; + break; + case Positions.MiddleLeft: + x = Owner.Left; + y = Owner.Top + (Owner.Height / 2); + break; + case Positions.TopCenter: + x = Owner.Left + (Owner.Width / 2); + y = Owner.Top; + break; + case Positions.BottomCenter: + x = Owner.Left + (Owner.Width / 2); + y = Owner.Top + Owner.Height; + break; + case Positions.TopRight: + x = Owner.Left + Owner.Width; + y = Owner.Top; + break; + case Positions.BottomRight: + x = Owner.Left + Owner.Width; + y = Owner.Top + Owner.Height; + break; + case Positions.MiddleRight: + x = Owner.Left + Owner.Width; + y = Owner.Top + (Owner.Height / 2); + break; + } + + return new Point(x, y); + } + } + + /// + /// Draw the adorner + /// + /// PaintEventArgs + public override void Paint(PaintEventArgs paintEventArgs) + { + Graphics targetGraphics = paintEventArgs.Graphics; + + var bounds = BoundsOnSurface; + GraphicsState state = targetGraphics.Save(); + + targetGraphics.CompositingMode = CompositingMode.SourceCopy; + + try + { + targetGraphics.FillRectangle(Brushes.Black, bounds); + targetGraphics.DrawRectangle(new Pen(Brushes.White), bounds); + } + catch + { + // Ignore, BUG-2065 + } + + targetGraphics.Restore(state); + } + } } \ No newline at end of file diff --git a/src/Greenshot/Drawing/Adorners/ResizeAdorner.cs b/src/Greenshot.Editor/Drawing/Adorners/ResizeAdorner.cs similarity index 96% rename from src/Greenshot/Drawing/Adorners/ResizeAdorner.cs rename to src/Greenshot.Editor/Drawing/Adorners/ResizeAdorner.cs index 21757a66a..052abaaa4 100644 --- a/src/Greenshot/Drawing/Adorners/ResizeAdorner.cs +++ b/src/Greenshot.Editor/Drawing/Adorners/ResizeAdorner.cs @@ -1,186 +1,186 @@ -/* - * Greenshot - a free and open source screenshot tool - * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom, Francis Noel - * - * For more information see: https://getgreenshot.org/ - * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 1 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -using Greenshot.Helpers; -using System.Drawing; -using System.Drawing.Drawing2D; -using System.Windows.Forms; -using Greenshot.Base.Interfaces.Drawing; - -namespace Greenshot.Drawing.Adorners -{ - /// - /// This is the default "legacy" gripper adorner, not the one used for the tail in the speech-bubble - /// - public class ResizeAdorner : AbstractAdorner - { - private Rectangle _boundsBeforeResize = Rectangle.Empty; - private RectangleF _boundsAfterResize = RectangleF.Empty; - - public Positions Position { get; private set; } - - public ResizeAdorner(IDrawableContainer owner, Positions position) : base(owner) - { - Position = position; - } - - /// - /// Returns the cursor for when the mouse is over the adorner - /// - public override Cursor Cursor - { - get - { - bool isNotSwitched = Owner.Width >= 0; - if (Owner.Height < 0) - { - isNotSwitched = !isNotSwitched; - } - - switch (Position) - { - case Positions.TopLeft: - case Positions.BottomRight: - return isNotSwitched ? Cursors.SizeNWSE : Cursors.SizeNESW; - case Positions.TopRight: - case Positions.BottomLeft: - return isNotSwitched ? Cursors.SizeNESW : Cursors.SizeNWSE; - case Positions.MiddleLeft: - case Positions.MiddleRight: - return Cursors.SizeWE; - case Positions.TopCenter: - case Positions.BottomCenter: - return Cursors.SizeNS; - default: - return Cursors.SizeAll; - } - } - } - - /// - /// Handle the mouse down - /// - /// - /// - public override void MouseDown(object sender, MouseEventArgs mouseEventArgs) - { - EditStatus = EditStatus.RESIZING; - _boundsBeforeResize = new Rectangle(Owner.Left, Owner.Top, Owner.Width, Owner.Height); - _boundsAfterResize = _boundsBeforeResize; - } - - /// - /// Handle the mouse move - /// - /// - /// - public override void MouseMove(object sender, MouseEventArgs mouseEventArgs) - { - if (EditStatus != EditStatus.RESIZING) - { - return; - } - - Owner.Invalidate(); - Owner.MakeBoundsChangeUndoable(false); - - // reset "workbench" rectangle to current bounds - _boundsAfterResize.X = _boundsBeforeResize.X; - _boundsAfterResize.Y = _boundsBeforeResize.Y; - _boundsAfterResize.Width = _boundsBeforeResize.Width; - _boundsAfterResize.Height = _boundsBeforeResize.Height; - - // calculate scaled rectangle - ScaleHelper.Scale(ref _boundsAfterResize, Position, new PointF(mouseEventArgs.X, mouseEventArgs.Y), ScaleHelper.GetScaleOptions()); - - // apply scaled bounds to this DrawableContainer - Owner.ApplyBounds(_boundsAfterResize); - - Owner.Invalidate(); - } - - /// - /// Return the location of the adorner - /// - public override Point Location - { - get - { - int x = 0, y = 0; - switch (Position) - { - case Positions.TopLeft: - x = Owner.Left; - y = Owner.Top; - break; - case Positions.BottomLeft: - x = Owner.Left; - y = Owner.Top + Owner.Height; - break; - case Positions.MiddleLeft: - x = Owner.Left; - y = Owner.Top + (Owner.Height / 2); - break; - case Positions.TopCenter: - x = Owner.Left + (Owner.Width / 2); - y = Owner.Top; - break; - case Positions.BottomCenter: - x = Owner.Left + (Owner.Width / 2); - y = Owner.Top + Owner.Height; - break; - case Positions.TopRight: - x = Owner.Left + Owner.Width; - y = Owner.Top; - break; - case Positions.BottomRight: - x = Owner.Left + Owner.Width; - y = Owner.Top + Owner.Height; - break; - case Positions.MiddleRight: - x = Owner.Left + Owner.Width; - y = Owner.Top + (Owner.Height / 2); - break; - } - - return new Point(x, y); - } - } - - /// - /// Draw the adorner - /// - /// PaintEventArgs - public override void Paint(PaintEventArgs paintEventArgs) - { - Graphics targetGraphics = paintEventArgs.Graphics; - - var bounds = BoundsOnSurface; - GraphicsState state = targetGraphics.Save(); - - targetGraphics.CompositingMode = CompositingMode.SourceCopy; - - targetGraphics.FillRectangle(Brushes.Black, bounds); - targetGraphics.DrawRectangle(new Pen(Brushes.White), bounds); - targetGraphics.Restore(state); - } - } +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom, Francis Noel + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using System.Drawing; +using System.Drawing.Drawing2D; +using System.Windows.Forms; +using Greenshot.Base.Interfaces.Drawing; +using Greenshot.Editor.Helpers; + +namespace Greenshot.Editor.Drawing.Adorners +{ + /// + /// This is the default "legacy" gripper adorner, not the one used for the tail in the speech-bubble + /// + public class ResizeAdorner : AbstractAdorner + { + private Rectangle _boundsBeforeResize = Rectangle.Empty; + private RectangleF _boundsAfterResize = RectangleF.Empty; + + public Positions Position { get; private set; } + + public ResizeAdorner(IDrawableContainer owner, Positions position) : base(owner) + { + Position = position; + } + + /// + /// Returns the cursor for when the mouse is over the adorner + /// + public override Cursor Cursor + { + get + { + bool isNotSwitched = Owner.Width >= 0; + if (Owner.Height < 0) + { + isNotSwitched = !isNotSwitched; + } + + switch (Position) + { + case Positions.TopLeft: + case Positions.BottomRight: + return isNotSwitched ? Cursors.SizeNWSE : Cursors.SizeNESW; + case Positions.TopRight: + case Positions.BottomLeft: + return isNotSwitched ? Cursors.SizeNESW : Cursors.SizeNWSE; + case Positions.MiddleLeft: + case Positions.MiddleRight: + return Cursors.SizeWE; + case Positions.TopCenter: + case Positions.BottomCenter: + return Cursors.SizeNS; + default: + return Cursors.SizeAll; + } + } + } + + /// + /// Handle the mouse down + /// + /// + /// + public override void MouseDown(object sender, MouseEventArgs mouseEventArgs) + { + EditStatus = EditStatus.RESIZING; + _boundsBeforeResize = new Rectangle(Owner.Left, Owner.Top, Owner.Width, Owner.Height); + _boundsAfterResize = _boundsBeforeResize; + } + + /// + /// Handle the mouse move + /// + /// + /// + public override void MouseMove(object sender, MouseEventArgs mouseEventArgs) + { + if (EditStatus != EditStatus.RESIZING) + { + return; + } + + Owner.Invalidate(); + Owner.MakeBoundsChangeUndoable(false); + + // reset "workbench" rectangle to current bounds + _boundsAfterResize.X = _boundsBeforeResize.X; + _boundsAfterResize.Y = _boundsBeforeResize.Y; + _boundsAfterResize.Width = _boundsBeforeResize.Width; + _boundsAfterResize.Height = _boundsBeforeResize.Height; + + // calculate scaled rectangle + ScaleHelper.Scale(ref _boundsAfterResize, Position, new PointF(mouseEventArgs.X, mouseEventArgs.Y), ScaleHelper.GetScaleOptions()); + + // apply scaled bounds to this DrawableContainer + Owner.ApplyBounds(_boundsAfterResize); + + Owner.Invalidate(); + } + + /// + /// Return the location of the adorner + /// + public override Point Location + { + get + { + int x = 0, y = 0; + switch (Position) + { + case Positions.TopLeft: + x = Owner.Left; + y = Owner.Top; + break; + case Positions.BottomLeft: + x = Owner.Left; + y = Owner.Top + Owner.Height; + break; + case Positions.MiddleLeft: + x = Owner.Left; + y = Owner.Top + (Owner.Height / 2); + break; + case Positions.TopCenter: + x = Owner.Left + (Owner.Width / 2); + y = Owner.Top; + break; + case Positions.BottomCenter: + x = Owner.Left + (Owner.Width / 2); + y = Owner.Top + Owner.Height; + break; + case Positions.TopRight: + x = Owner.Left + Owner.Width; + y = Owner.Top; + break; + case Positions.BottomRight: + x = Owner.Left + Owner.Width; + y = Owner.Top + Owner.Height; + break; + case Positions.MiddleRight: + x = Owner.Left + Owner.Width; + y = Owner.Top + (Owner.Height / 2); + break; + } + + return new Point(x, y); + } + } + + /// + /// Draw the adorner + /// + /// PaintEventArgs + public override void Paint(PaintEventArgs paintEventArgs) + { + Graphics targetGraphics = paintEventArgs.Graphics; + + var bounds = BoundsOnSurface; + GraphicsState state = targetGraphics.Save(); + + targetGraphics.CompositingMode = CompositingMode.SourceCopy; + + targetGraphics.FillRectangle(Brushes.Black, bounds); + targetGraphics.DrawRectangle(new Pen(Brushes.White), bounds); + targetGraphics.Restore(state); + } + } } \ No newline at end of file diff --git a/src/Greenshot/Drawing/Adorners/TargetAdorner.cs b/src/Greenshot.Editor/Drawing/Adorners/TargetAdorner.cs similarity index 98% rename from src/Greenshot/Drawing/Adorners/TargetAdorner.cs rename to src/Greenshot.Editor/Drawing/Adorners/TargetAdorner.cs index 8528a27ed..0dec40a5f 100644 --- a/src/Greenshot/Drawing/Adorners/TargetAdorner.cs +++ b/src/Greenshot.Editor/Drawing/Adorners/TargetAdorner.cs @@ -24,7 +24,7 @@ using System.Drawing.Drawing2D; using System.Windows.Forms; using Greenshot.Base.Interfaces.Drawing; -namespace Greenshot.Drawing.Adorners +namespace Greenshot.Editor.Drawing.Adorners { /// /// This implements the special "gripper" for the Speech-Bubble tail diff --git a/src/Greenshot/Drawing/ArrowContainer.cs b/src/Greenshot.Editor/Drawing/ArrowContainer.cs similarity index 96% rename from src/Greenshot/Drawing/ArrowContainer.cs rename to src/Greenshot.Editor/Drawing/ArrowContainer.cs index ef855019a..f285d28bc 100644 --- a/src/Greenshot/Drawing/ArrowContainer.cs +++ b/src/Greenshot.Editor/Drawing/ArrowContainer.cs @@ -1,163 +1,163 @@ -/* - * Greenshot - a free and open source screenshot tool - * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom - * - * For more information see: https://getgreenshot.org/ - * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 1 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -using System; -using System.Drawing; -using System.Drawing.Drawing2D; -using Greenshot.Base.Interfaces.Drawing; -using Greenshot.Drawing.Fields; - -namespace Greenshot.Drawing -{ - /// - /// Description of LineContainer. - /// - [Serializable()] - public class ArrowContainer : LineContainer - { - public enum ArrowHeadCombination - { - NONE, - START_POINT, - END_POINT, - BOTH - }; - - private static readonly AdjustableArrowCap ARROW_CAP = new AdjustableArrowCap(4, 6); - - public ArrowContainer(Surface parent) : base(parent) - { - } - - /// - /// Do not use the base, just override so we have our own defaults - /// - protected override void InitializeFields() - { - AddField(GetType(), FieldType.LINE_THICKNESS, 2); - AddField(GetType(), FieldType.ARROWHEADS, 2); - AddField(GetType(), FieldType.LINE_COLOR, Color.Red); - AddField(GetType(), FieldType.FILL_COLOR, Color.Transparent); - AddField(GetType(), FieldType.SHADOW, true); - AddField(GetType(), FieldType.ARROWHEADS, ArrowHeadCombination.END_POINT); - } - - public override void Draw(Graphics graphics, RenderMode rm) - { - int lineThickness = GetFieldValueAsInt(FieldType.LINE_THICKNESS); - bool shadow = GetFieldValueAsBool(FieldType.SHADOW); - - if (lineThickness > 0) - { - graphics.SmoothingMode = SmoothingMode.HighQuality; - graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; - graphics.CompositingQuality = CompositingQuality.HighQuality; - graphics.PixelOffsetMode = PixelOffsetMode.None; - Color lineColor = GetFieldValueAsColor(FieldType.LINE_COLOR); - ArrowHeadCombination heads = (ArrowHeadCombination) GetFieldValue(FieldType.ARROWHEADS); - if (lineThickness > 0) - { - if (shadow) - { - //draw shadow first - int basealpha = 100; - int alpha = basealpha; - int steps = 5; - int currentStep = 1; - while (currentStep <= steps) - { - using Pen shadowCapPen = new Pen(Color.FromArgb(alpha, 100, 100, 100), lineThickness); - SetArrowHeads(heads, shadowCapPen); - - graphics.DrawLine(shadowCapPen, - Left + currentStep, - Top + currentStep, - Left + currentStep + Width, - Top + currentStep + Height); - - currentStep++; - alpha -= basealpha / steps; - } - } - - using Pen pen = new Pen(lineColor, lineThickness); - SetArrowHeads(heads, pen); - graphics.DrawLine(pen, Left, Top, Left + Width, Top + Height); - } - } - } - - private void SetArrowHeads(ArrowHeadCombination heads, Pen pen) - { - if (heads == ArrowHeadCombination.BOTH || heads == ArrowHeadCombination.START_POINT) - { - pen.CustomStartCap = ARROW_CAP; - } - - if (heads == ArrowHeadCombination.BOTH || heads == ArrowHeadCombination.END_POINT) - { - pen.CustomEndCap = ARROW_CAP; - } - } - - public override Rectangle DrawingBounds - { - get - { - int lineThickness = GetFieldValueAsInt(FieldType.LINE_THICKNESS); - if (lineThickness > 0) - { - using Pen pen = new Pen(Color.White) - { - Width = lineThickness - }; - SetArrowHeads((ArrowHeadCombination) GetFieldValue(FieldType.ARROWHEADS), pen); - using GraphicsPath path = new GraphicsPath(); - path.AddLine(Left, Top, Left + Width, Top + Height); - using Matrix matrix = new Matrix(); - Rectangle drawingBounds = Rectangle.Round(path.GetBounds(matrix, pen)); - drawingBounds.Inflate(2, 2); - return drawingBounds; - } - - return Rectangle.Empty; - } - } - - public override bool ClickableAt(int x, int y) - { - int lineThickness = GetFieldValueAsInt(FieldType.LINE_THICKNESS) + 10; - if (lineThickness > 0) - { - using Pen pen = new Pen(Color.White) - { - Width = lineThickness - }; - SetArrowHeads((ArrowHeadCombination) GetFieldValue(FieldType.ARROWHEADS), pen); - using GraphicsPath path = new GraphicsPath(); - path.AddLine(Left, Top, Left + Width, Top + Height); - return path.IsOutlineVisible(x, y, pen); - } - - return false; - } - } +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using System; +using System.Drawing; +using System.Drawing.Drawing2D; +using Greenshot.Base.Interfaces.Drawing; +using Greenshot.Editor.Drawing.Fields; + +namespace Greenshot.Editor.Drawing +{ + /// + /// Description of LineContainer. + /// + [Serializable()] + public class ArrowContainer : LineContainer + { + public enum ArrowHeadCombination + { + NONE, + START_POINT, + END_POINT, + BOTH + }; + + private static readonly AdjustableArrowCap ARROW_CAP = new AdjustableArrowCap(4, 6); + + public ArrowContainer(Surface parent) : base(parent) + { + } + + /// + /// Do not use the base, just override so we have our own defaults + /// + protected override void InitializeFields() + { + AddField(GetType(), FieldType.LINE_THICKNESS, 2); + AddField(GetType(), FieldType.ARROWHEADS, 2); + AddField(GetType(), FieldType.LINE_COLOR, Color.Red); + AddField(GetType(), FieldType.FILL_COLOR, Color.Transparent); + AddField(GetType(), FieldType.SHADOW, true); + AddField(GetType(), FieldType.ARROWHEADS, ArrowHeadCombination.END_POINT); + } + + public override void Draw(Graphics graphics, RenderMode rm) + { + int lineThickness = GetFieldValueAsInt(FieldType.LINE_THICKNESS); + bool shadow = GetFieldValueAsBool(FieldType.SHADOW); + + if (lineThickness > 0) + { + graphics.SmoothingMode = SmoothingMode.HighQuality; + graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; + graphics.CompositingQuality = CompositingQuality.HighQuality; + graphics.PixelOffsetMode = PixelOffsetMode.None; + Color lineColor = GetFieldValueAsColor(FieldType.LINE_COLOR); + ArrowHeadCombination heads = (ArrowHeadCombination) GetFieldValue(FieldType.ARROWHEADS); + if (lineThickness > 0) + { + if (shadow) + { + //draw shadow first + int basealpha = 100; + int alpha = basealpha; + int steps = 5; + int currentStep = 1; + while (currentStep <= steps) + { + using Pen shadowCapPen = new Pen(Color.FromArgb(alpha, 100, 100, 100), lineThickness); + SetArrowHeads(heads, shadowCapPen); + + graphics.DrawLine(shadowCapPen, + Left + currentStep, + Top + currentStep, + Left + currentStep + Width, + Top + currentStep + Height); + + currentStep++; + alpha -= basealpha / steps; + } + } + + using Pen pen = new Pen(lineColor, lineThickness); + SetArrowHeads(heads, pen); + graphics.DrawLine(pen, Left, Top, Left + Width, Top + Height); + } + } + } + + private void SetArrowHeads(ArrowHeadCombination heads, Pen pen) + { + if (heads == ArrowHeadCombination.BOTH || heads == ArrowHeadCombination.START_POINT) + { + pen.CustomStartCap = ARROW_CAP; + } + + if (heads == ArrowHeadCombination.BOTH || heads == ArrowHeadCombination.END_POINT) + { + pen.CustomEndCap = ARROW_CAP; + } + } + + public override Rectangle DrawingBounds + { + get + { + int lineThickness = GetFieldValueAsInt(FieldType.LINE_THICKNESS); + if (lineThickness > 0) + { + using Pen pen = new Pen(Color.White) + { + Width = lineThickness + }; + SetArrowHeads((ArrowHeadCombination) GetFieldValue(FieldType.ARROWHEADS), pen); + using GraphicsPath path = new GraphicsPath(); + path.AddLine(Left, Top, Left + Width, Top + Height); + using Matrix matrix = new Matrix(); + Rectangle drawingBounds = Rectangle.Round(path.GetBounds(matrix, pen)); + drawingBounds.Inflate(2, 2); + return drawingBounds; + } + + return Rectangle.Empty; + } + } + + public override bool ClickableAt(int x, int y) + { + int lineThickness = GetFieldValueAsInt(FieldType.LINE_THICKNESS) + 10; + if (lineThickness > 0) + { + using Pen pen = new Pen(Color.White) + { + Width = lineThickness + }; + SetArrowHeads((ArrowHeadCombination) GetFieldValue(FieldType.ARROWHEADS), pen); + using GraphicsPath path = new GraphicsPath(); + path.AddLine(Left, Top, Left + Width, Top + Height); + return path.IsOutlineVisible(x, y, pen); + } + + return false; + } + } } \ No newline at end of file diff --git a/src/Greenshot/Drawing/CropContainer.cs b/src/Greenshot.Editor/Drawing/CropContainer.cs similarity index 95% rename from src/Greenshot/Drawing/CropContainer.cs rename to src/Greenshot.Editor/Drawing/CropContainer.cs index 1ec022207..1aa4e06c2 100644 --- a/src/Greenshot/Drawing/CropContainer.cs +++ b/src/Greenshot.Editor/Drawing/CropContainer.cs @@ -1,108 +1,108 @@ -/* - * Greenshot - a free and open source screenshot tool - * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom - * - * For more information see: https://getgreenshot.org/ - * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 1 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -using System.Drawing; -using System.Runtime.Serialization; -using Greenshot.Base.Interfaces.Drawing; -using Greenshot.Drawing.Fields; -using Greenshot.Helpers; - -namespace Greenshot.Drawing -{ - /// - /// Description of CropContainer. - /// - 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, FieldFlag.CONFIRMABLE); - } - - public override void Invalidate() - { - _parent?.Invalidate(); - } - - /// - /// We need to override the DrawingBound, return a rectangle in the size of the image, to make sure this element is always draw - /// (we create a transparent brown over the complete picture) - /// - public override Rectangle DrawingBounds - { - get - { - if (_parent?.Image is { } image) - { - return new Rectangle(0, 0, image.Width, image.Height); - } - - return Rectangle.Empty; - } - } - - public override void Draw(Graphics g, RenderMode rm) - { - if (_parent == null) - { - return; - } - - using Brush cropBrush = new SolidBrush(Color.FromArgb(100, 150, 150, 100)); - Rectangle cropRectangle = GuiRectangle.GetGuiRectangle(Left, Top, Width, Height); - Rectangle selectionRect = new Rectangle(cropRectangle.Left - 1, cropRectangle.Top - 1, cropRectangle.Width + 1, cropRectangle.Height + 1); - Size imageSize = _parent.Image.Size; - - DrawSelectionBorder(g, selectionRect); - - // top - g.FillRectangle(cropBrush, new Rectangle(0, 0, imageSize.Width, cropRectangle.Top)); - // left - g.FillRectangle(cropBrush, new Rectangle(0, cropRectangle.Top, cropRectangle.Left, cropRectangle.Height)); - // right - g.FillRectangle(cropBrush, - new Rectangle(cropRectangle.Left + cropRectangle.Width, cropRectangle.Top, imageSize.Width - (cropRectangle.Left + cropRectangle.Width), cropRectangle.Height)); - // bottom - g.FillRectangle(cropBrush, new Rectangle(0, cropRectangle.Top + cropRectangle.Height, imageSize.Width, imageSize.Height - (cropRectangle.Top + cropRectangle.Height))); - } - - /// - /// No context menu for the CropContainer - /// - public override bool HasContextMenu => false; - } +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using System.Drawing; +using System.Runtime.Serialization; +using Greenshot.Base.Interfaces.Drawing; +using Greenshot.Editor.Drawing.Fields; +using Greenshot.Editor.Helpers; + +namespace Greenshot.Editor.Drawing +{ + /// + /// Description of CropContainer. + /// + 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, FieldFlag.CONFIRMABLE); + } + + public override void Invalidate() + { + _parent?.Invalidate(); + } + + /// + /// We need to override the DrawingBound, return a rectangle in the size of the image, to make sure this element is always draw + /// (we create a transparent brown over the complete picture) + /// + public override Rectangle DrawingBounds + { + get + { + if (_parent?.Image is { } image) + { + return new Rectangle(0, 0, image.Width, image.Height); + } + + return Rectangle.Empty; + } + } + + public override void Draw(Graphics g, RenderMode rm) + { + if (_parent == null) + { + return; + } + + using Brush cropBrush = new SolidBrush(Color.FromArgb(100, 150, 150, 100)); + Rectangle cropRectangle = GuiRectangle.GetGuiRectangle(Left, Top, Width, Height); + Rectangle selectionRect = new Rectangle(cropRectangle.Left - 1, cropRectangle.Top - 1, cropRectangle.Width + 1, cropRectangle.Height + 1); + Size imageSize = _parent.Image.Size; + + DrawSelectionBorder(g, selectionRect); + + // top + g.FillRectangle(cropBrush, new Rectangle(0, 0, imageSize.Width, cropRectangle.Top)); + // left + g.FillRectangle(cropBrush, new Rectangle(0, cropRectangle.Top, cropRectangle.Left, cropRectangle.Height)); + // right + g.FillRectangle(cropBrush, + new Rectangle(cropRectangle.Left + cropRectangle.Width, cropRectangle.Top, imageSize.Width - (cropRectangle.Left + cropRectangle.Width), cropRectangle.Height)); + // bottom + g.FillRectangle(cropBrush, new Rectangle(0, cropRectangle.Top + cropRectangle.Height, imageSize.Width, imageSize.Height - (cropRectangle.Top + cropRectangle.Height))); + } + + /// + /// No context menu for the CropContainer + /// + public override bool HasContextMenu => false; + } } \ No newline at end of file diff --git a/src/Greenshot/Drawing/CursorContainer.cs b/src/Greenshot.Editor/Drawing/CursorContainer.cs similarity index 96% rename from src/Greenshot/Drawing/CursorContainer.cs rename to src/Greenshot.Editor/Drawing/CursorContainer.cs index ec4568000..d2e19adba 100644 --- a/src/Greenshot/Drawing/CursorContainer.cs +++ b/src/Greenshot.Editor/Drawing/CursorContainer.cs @@ -1,128 +1,128 @@ -/* - * Greenshot - a free and open source screenshot tool - * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom - * - * For more information see: https://getgreenshot.org/ - * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 1 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -using System; -using System.Drawing; -using System.IO; -using System.Windows.Forms; -using System.Drawing.Drawing2D; -using log4net; -using System.Runtime.Serialization; -using Greenshot.Base.Interfaces.Drawing; - -namespace Greenshot.Drawing -{ - /// - /// Description of CursorContainer. - /// - [Serializable] - public class CursorContainer : DrawableContainer, ICursorContainer - { - private static readonly ILog LOG = LogManager.GetLogger(typeof(CursorContainer)); - - protected Cursor cursor; - - public CursorContainer(Surface parent) : base(parent) - { - Init(); - } - - protected override void OnDeserialized(StreamingContext streamingContext) - { - base.OnDeserialized(streamingContext); - Init(); - } - - private void Init() - { - CreateDefaultAdorners(); - } - - public CursorContainer(Surface parent, string filename) : this(parent) - { - Load(filename); - } - - public Cursor Cursor - { - set - { - if (cursor != null) - { - cursor.Dispose(); - } - - // Clone cursor (is this correct??) - cursor = new Cursor(value.CopyHandle()); - Width = value.Size.Width; - Height = value.Size.Height; - } - get { return cursor; } - } - - /// - /// This Dispose is called from the Dispose and the Destructor. - /// When disposing==true all non-managed resources should be freed too! - /// - /// - protected override void Dispose(bool disposing) - { - if (disposing) - { - if (cursor != null) - { - cursor.Dispose(); - } - } - - cursor = null; - base.Dispose(disposing); - } - - public void Load(string filename) - { - if (!File.Exists(filename)) - { - return; - } - - using Cursor fileCursor = new Cursor(filename); - Cursor = fileCursor; - LOG.Debug("Loaded file: " + filename + " with resolution: " + Height + "," + Width); - } - - public override void Draw(Graphics graphics, RenderMode rm) - { - if (cursor == null) - { - return; - } - - graphics.SmoothingMode = SmoothingMode.HighQuality; - graphics.InterpolationMode = InterpolationMode.NearestNeighbor; - graphics.CompositingQuality = CompositingQuality.Default; - graphics.PixelOffsetMode = PixelOffsetMode.None; - cursor.DrawStretched(graphics, Bounds); - } - - public override Size DefaultSize => cursor?.Size ?? new Size(16, 16); - } +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using System; +using System.Drawing; +using System.Drawing.Drawing2D; +using System.IO; +using System.Runtime.Serialization; +using System.Windows.Forms; +using Greenshot.Base.Interfaces.Drawing; +using log4net; + +namespace Greenshot.Editor.Drawing +{ + /// + /// Description of CursorContainer. + /// + [Serializable] + public class CursorContainer : DrawableContainer, ICursorContainer + { + private static readonly ILog LOG = LogManager.GetLogger(typeof(CursorContainer)); + + protected Cursor cursor; + + public CursorContainer(Surface parent) : base(parent) + { + Init(); + } + + protected override void OnDeserialized(StreamingContext streamingContext) + { + base.OnDeserialized(streamingContext); + Init(); + } + + private void Init() + { + CreateDefaultAdorners(); + } + + public CursorContainer(Surface parent, string filename) : this(parent) + { + Load(filename); + } + + public Cursor Cursor + { + set + { + if (cursor != null) + { + cursor.Dispose(); + } + + // Clone cursor (is this correct??) + cursor = new Cursor(value.CopyHandle()); + Width = value.Size.Width; + Height = value.Size.Height; + } + get { return cursor; } + } + + /// + /// This Dispose is called from the Dispose and the Destructor. + /// When disposing==true all non-managed resources should be freed too! + /// + /// + protected override void Dispose(bool disposing) + { + if (disposing) + { + if (cursor != null) + { + cursor.Dispose(); + } + } + + cursor = null; + base.Dispose(disposing); + } + + public void Load(string filename) + { + if (!File.Exists(filename)) + { + return; + } + + using Cursor fileCursor = new Cursor(filename); + Cursor = fileCursor; + LOG.Debug("Loaded file: " + filename + " with resolution: " + Height + "," + Width); + } + + public override void Draw(Graphics graphics, RenderMode rm) + { + if (cursor == null) + { + return; + } + + graphics.SmoothingMode = SmoothingMode.HighQuality; + graphics.InterpolationMode = InterpolationMode.NearestNeighbor; + graphics.CompositingQuality = CompositingQuality.Default; + graphics.PixelOffsetMode = PixelOffsetMode.None; + cursor.DrawStretched(graphics, Bounds); + } + + public override Size DefaultSize => cursor?.Size ?? new Size(16, 16); + } } \ No newline at end of file diff --git a/src/Greenshot/Drawing/DrawableContainer.cs b/src/Greenshot.Editor/Drawing/DrawableContainer.cs similarity index 95% rename from src/Greenshot/Drawing/DrawableContainer.cs rename to src/Greenshot.Editor/Drawing/DrawableContainer.cs index 95ca776d7..b7202b23e 100644 --- a/src/Greenshot/Drawing/DrawableContainer.cs +++ b/src/Greenshot.Editor/Drawing/DrawableContainer.cs @@ -1,670 +1,670 @@ -/* - * Greenshot - a free and open source screenshot tool - * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom - * - * For more information see: https://getgreenshot.org/ - * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 1 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -using Greenshot.Configuration; -using Greenshot.Drawing.Adorners; -using Greenshot.Drawing.Fields; -using Greenshot.Drawing.Filters; -using Greenshot.Helpers; -using Greenshot.Memento; -using log4net; -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Drawing; -using System.Drawing.Drawing2D; -using System.Linq; -using System.Runtime.Serialization; -using Greenshot.Base.IniFile; -using Greenshot.Base.Interfaces; -using Greenshot.Base.Interfaces.Drawing; -using Greenshot.Base.Interfaces.Drawing.Adorners; - -namespace Greenshot.Drawing -{ - /// - /// represents a rectangle, ellipse, label or whatever. Can contain filters, too. - /// serializable for clipboard support - /// Subclasses should fulfill INotifyPropertyChanged contract, i.e. call - /// OnPropertyChanged whenever a public property has been changed. - /// - [Serializable] - public abstract class DrawableContainer : AbstractFieldHolderWithChildren, IDrawableContainer - { - private static readonly ILog LOG = LogManager.GetLogger(typeof(DrawableContainer)); - protected static readonly EditorConfiguration EditorConfig = IniConfig.GetIniSection(); - private const int M11 = 0; - private const int M22 = 3; - - [OnDeserialized] - private void OnDeserializedInit(StreamingContext context) - { - _adorners = new List(); - OnDeserialized(context); - } - - /// - /// Override to implement your own deserialization logic, like initializing properties which are not serialized - /// - /// - protected virtual void OnDeserialized(StreamingContext streamingContext) - { - } - - protected EditStatus _defaultEditMode = EditStatus.DRAWING; - - public EditStatus DefaultEditMode - { - get { return _defaultEditMode; } - } - - /// - /// The public accessible Dispose - /// Will call the GarbageCollector to SuppressFinalize, preventing being cleaned twice - /// - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - protected virtual void Dispose(bool disposing) - { - if (!disposing) - { - return; - } - - _parent?.FieldAggregator?.UnbindElement(this); - } - - ~DrawableContainer() - { - Dispose(false); - } - - [NonSerialized] private PropertyChangedEventHandler _propertyChanged; - - public event PropertyChangedEventHandler PropertyChanged - { - add => _propertyChanged += value; - remove => _propertyChanged -= value; - } - - public IList Filters - { - get - { - List ret = new List(); - foreach (IFieldHolder c in Children) - { - if (c is IFilter) - { - ret.Add(c as IFilter); - } - } - - return ret; - } - } - - [NonSerialized] internal Surface _parent; - - public ISurface Parent - { - get => _parent; - set => SwitchParent((Surface) value); - } - - [NonSerialized] private TargetAdorner _targetAdorner; - public TargetAdorner TargetAdorner => _targetAdorner; - - [NonSerialized] private bool _selected; - - public bool Selected - { - get => _selected; - set - { - _selected = value; - OnPropertyChanged("Selected"); - } - } - - [NonSerialized] private EditStatus _status = EditStatus.UNDRAWN; - - public EditStatus Status - { - get => _status; - set => _status = value; - } - - - private int left; - - public int Left - { - get => left; - set - { - if (value == left) - { - return; - } - - left = value; - } - } - - private int top; - - public int Top - { - get => top; - set - { - if (value == top) - { - return; - } - - top = value; - } - } - - private int width; - - public int Width - { - get => width; - set - { - if (value == width) - { - return; - } - - width = value; - } - } - - private int height; - - public int Height - { - get => height; - set - { - if (value == height) - { - return; - } - - height = value; - } - } - - public Point Location - { - get => new Point(left, top); - set - { - left = value.X; - top = value.Y; - } - } - - public Size Size - { - get => new Size(width, height); - set - { - width = value.Width; - height = value.Height; - } - } - - /// - /// List of available Adorners - /// - [NonSerialized] private IList _adorners = new List(); - - public IList Adorners => _adorners; - - [NonSerialized] - // will store current bounds of this DrawableContainer before starting a resize - protected Rectangle _boundsBeforeResize = Rectangle.Empty; - - [NonSerialized] - // "workbench" rectangle - used for calculating bounds during resizing (to be applied to this DrawableContainer afterwards) - protected RectangleF _boundsAfterResize = RectangleF.Empty; - - public Rectangle Bounds - { - get => GuiRectangle.GetGuiRectangle(Left, Top, Width, Height); - set - { - Left = Round(value.Left); - Top = Round(value.Top); - Width = Round(value.Width); - Height = Round(value.Height); - } - } - - public virtual void ApplyBounds(RectangleF newBounds) - { - Left = Round(newBounds.Left); - Top = Round(newBounds.Top); - Width = Round(newBounds.Width); - Height = Round(newBounds.Height); - } - - public DrawableContainer(Surface parent) - { - InitializeFields(); - _parent = parent; - } - - public void Add(IFilter filter) - { - AddChild(filter); - } - - public void Remove(IFilter filter) - { - RemoveChild(filter); - } - - private static int Round(float f) - { - if (float.IsPositiveInfinity(f) || f > int.MaxValue / 2) return int.MaxValue / 2; - if (float.IsNegativeInfinity(f) || f < int.MinValue / 2) return int.MinValue / 2; - return (int) Math.Round(f); - } - - private bool accountForShadowChange; - - public virtual Rectangle DrawingBounds - { - get - { - foreach (IFilter filter in Filters) - { - if (filter.Invert) - { - return new Rectangle(Point.Empty, _parent.Image.Size); - } - } - - // Take a base safety margin - int lineThickness = 5; - - // add adorner size - lineThickness += Adorners.Max(adorner => Math.Max(adorner.Bounds.Width, adorner.Bounds.Height)); - - if (HasField(FieldType.LINE_THICKNESS)) - { - lineThickness += GetFieldValueAsInt(FieldType.LINE_THICKNESS); - } - - int offset = lineThickness / 2; - - int shadow = 0; - if (accountForShadowChange || (HasField(FieldType.SHADOW) && GetFieldValueAsBool(FieldType.SHADOW))) - { - accountForShadowChange = false; - shadow += 10; - } - - return new Rectangle(Bounds.Left - offset, Bounds.Top - offset, Bounds.Width + lineThickness + shadow, Bounds.Height + lineThickness + shadow); - } - } - - public virtual void Invalidate() - { - if (Status != EditStatus.UNDRAWN) - { - _parent?.InvalidateElements(DrawingBounds); - } - } - - public virtual bool InitContent() - { - return true; - } - - public virtual void OnDoubleClick() - { - } - - /// - /// Initialize a target gripper - /// - protected void InitAdorner(Color gripperColor, Point location) - { - _targetAdorner = new TargetAdorner(this, location); - Adorners.Add(_targetAdorner); - } - - /// - /// Create the default adorners for a rectangle based container - /// - protected void CreateDefaultAdorners() - { - if (Adorners.Count > 0) - { - LOG.Warn("Adorners are already defined!"); - } - - // 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)); - } - - public bool hasFilters => Filters.Count > 0; - - public abstract void Draw(Graphics graphics, RenderMode renderMode); - - public virtual void DrawContent(Graphics graphics, Bitmap bmp, RenderMode renderMode, Rectangle clipRectangle) - { - if (Children.Count > 0) - { - if (Status != EditStatus.IDLE) - { - DrawSelectionBorder(graphics, Bounds); - } - else - { - if (clipRectangle.Width != 0 && clipRectangle.Height != 0) - { - foreach (IFilter filter in Filters) - { - if (filter.Invert) - { - filter.Apply(graphics, bmp, Bounds, renderMode); - } - else - { - Rectangle drawingRect = new Rectangle(Bounds.Location, Bounds.Size); - drawingRect.Intersect(clipRectangle); - if (filter is MagnifierFilter) - { - // quick&dirty bugfix, because MagnifierFilter behaves differently when drawn only partially - // what we should actually do to resolve this is add a better magnifier which is not that special - filter.Apply(graphics, bmp, Bounds, renderMode); - } - else - { - filter.Apply(graphics, bmp, drawingRect, renderMode); - } - } - } - } - } - } - - Draw(graphics, renderMode); - } - - /// - /// Adjust UI elements to the supplied DPI settings - /// - /// uint with dpi value - public void AdjustToDpi(uint dpi) - { - foreach (var adorner in Adorners) - { - adorner.AdjustToDpi(dpi); - } - } - - public virtual bool Contains(int x, int y) - { - return Bounds.Contains(x, y); - } - - public virtual bool ClickableAt(int x, int y) - { - Rectangle r = GuiRectangle.GetGuiRectangle(Left, Top, Width, Height); - r.Inflate(5, 5); - return r.Contains(x, y); - } - - protected void DrawSelectionBorder(Graphics g, Rectangle rect) - { - using Pen pen = new Pen(Color.MediumSeaGreen) - { - DashPattern = new float[] - { - 1, 2 - }, - Width = 1 - }; - g.DrawRectangle(pen, rect); - } - - - public void ResizeTo(int width, int height, int anchorPosition) - { - Width = width; - Height = height; - } - - /// - /// Make a following bounds change on this drawablecontainer undoable! - /// - /// true means allow the moves to be merged - public void MakeBoundsChangeUndoable(bool allowMerge) - { - _parent.MakeUndoable(new DrawableContainerBoundsChangeMemento(this), allowMerge); - } - - public void MoveBy(int dx, int dy) - { - Left += dx; - Top += dy; - } - - /// - /// A handler for the MouseDown, used if you don't want the surface to handle this for you - /// - /// current mouse x - /// current mouse y - /// true if the event is handled, false if the surface needs to handle it - public virtual bool HandleMouseDown(int x, int y) - { - Left = _boundsBeforeResize.X = x; - Top = _boundsBeforeResize.Y = y; - return true; - } - - /// - /// A handler for the MouseMove, used if you don't want the surface to handle this for you - /// - /// current mouse x - /// current mouse y - /// true if the event is handled, false if the surface needs to handle it - public virtual bool HandleMouseMove(int x, int y) - { - Invalidate(); - - // reset "workrbench" rectangle to current bounds - _boundsAfterResize.X = _boundsBeforeResize.Left; - _boundsAfterResize.Y = _boundsBeforeResize.Top; - _boundsAfterResize.Width = x - _boundsAfterResize.Left; - _boundsAfterResize.Height = y - _boundsAfterResize.Top; - - ScaleHelper.Scale(_boundsBeforeResize, x, y, ref _boundsAfterResize, GetAngleRoundProcessor()); - - // apply scaled bounds to this DrawableContainer - ApplyBounds(_boundsAfterResize); - - Invalidate(); - return true; - } - - /// - /// A handler for the MouseUp - /// - /// current mouse x - /// current mouse y - public virtual void HandleMouseUp(int x, int y) - { - } - - protected virtual void SwitchParent(Surface newParent) - { - if (newParent == Parent) - { - return; - } - - _parent?.FieldAggregator?.UnbindElement(this); - - _parent = newParent; - foreach (IFilter filter in Filters) - { - filter.Parent = this; - } - } - - protected void OnPropertyChanged(string propertyName) - { - if (_propertyChanged != null) - { - _propertyChanged(this, new PropertyChangedEventArgs(propertyName)); - Invalidate(); - } - } - - /// - /// This method will be called before a field is changes. - /// Using this makes it possible to invalidate the object as is before changing. - /// - /// The field to be changed - /// The new value - public virtual void BeforeFieldChange(IField fieldToBeChanged, object newValue) - { - _parent?.MakeUndoable(new ChangeFieldHolderMemento(this, fieldToBeChanged), true); - Invalidate(); - } - - /// - /// Handle the field changed event, this should invalidate the correct bounds (e.g. when shadow comes or goes more pixels!) - /// - /// - /// - public void HandleFieldChanged(object sender, FieldChangedEventArgs e) - { - LOG.DebugFormat("Field {0} changed", e.Field.FieldType); - if (Equals(e.Field.FieldType, FieldType.SHADOW)) - { - accountForShadowChange = true; - } - } - - /// - /// Retrieve the Y scale from the matrix - /// - /// - /// - public static float CalculateScaleY(Matrix matrix) - { - return matrix.Elements[M22]; - } - - /// - /// Retrieve the X scale from the matrix - /// - /// - /// - public static float CalculateScaleX(Matrix matrix) - { - return matrix.Elements[M11]; - } - - /// - /// Retrieve the rotation angle from the matrix - /// - /// - /// - public static int CalculateAngle(Matrix matrix) - { - const int M11 = 0; - const int M21 = 2; - var radians = Math.Atan2(matrix.Elements[M21], matrix.Elements[M11]); - return (int) -Math.Round(radians * 180 / Math.PI); - } - - /// - /// This method is called on a DrawableContainers when: - /// 1) The capture on the surface is modified in such a way, that the elements would not be placed correctly. - /// 2) Currently not implemented: an element needs to be moved, scaled or rotated. - /// This basis implementation makes sure the coordinates of the element, including the TargetGripper, is correctly rotated/scaled/translated. - /// But this implementation doesn't take care of any changes to the content!! - /// - /// - public virtual void Transform(Matrix matrix) - { - if (matrix == null) - { - return; - } - - Point topLeft = new Point(Left, Top); - Point bottomRight = new Point(Left + Width, Top + Height); - 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; - } - - protected virtual ScaleHelper.IDoubleProcessor GetAngleRoundProcessor() - { - return ScaleHelper.ShapeAngleRoundBehavior.Instance; - } - - public virtual bool HasContextMenu => true; - - public virtual bool HasDefaultSize => false; - - public virtual Size DefaultSize => throw new NotSupportedException("Object doesn't have a default size"); - - /// - /// Allows to override the initializing of the fields, so we can actually have our own defaults - /// - protected virtual void InitializeFields() - { - } - } +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.Drawing.Drawing2D; +using System.Linq; +using System.Runtime.Serialization; +using Greenshot.Base.IniFile; +using Greenshot.Base.Interfaces; +using Greenshot.Base.Interfaces.Drawing; +using Greenshot.Base.Interfaces.Drawing.Adorners; +using Greenshot.Editor.Configuration; +using Greenshot.Editor.Drawing.Adorners; +using Greenshot.Editor.Drawing.Fields; +using Greenshot.Editor.Drawing.Filters; +using Greenshot.Editor.Helpers; +using Greenshot.Editor.Memento; +using log4net; + +namespace Greenshot.Editor.Drawing +{ + /// + /// represents a rectangle, ellipse, label or whatever. Can contain filters, too. + /// serializable for clipboard support + /// Subclasses should fulfill INotifyPropertyChanged contract, i.e. call + /// OnPropertyChanged whenever a public property has been changed. + /// + [Serializable] + public abstract class DrawableContainer : AbstractFieldHolderWithChildren, IDrawableContainer + { + private static readonly ILog LOG = LogManager.GetLogger(typeof(DrawableContainer)); + protected static readonly EditorConfiguration EditorConfig = IniConfig.GetIniSection(); + private const int M11 = 0; + private const int M22 = 3; + + [OnDeserialized] + private void OnDeserializedInit(StreamingContext context) + { + _adorners = new List(); + OnDeserialized(context); + } + + /// + /// Override to implement your own deserialization logic, like initializing properties which are not serialized + /// + /// + protected virtual void OnDeserialized(StreamingContext streamingContext) + { + } + + protected EditStatus _defaultEditMode = EditStatus.DRAWING; + + public EditStatus DefaultEditMode + { + get { return _defaultEditMode; } + } + + /// + /// The public accessible Dispose + /// Will call the GarbageCollector to SuppressFinalize, preventing being cleaned twice + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) + { + if (!disposing) + { + return; + } + + _parent?.FieldAggregator?.UnbindElement(this); + } + + ~DrawableContainer() + { + Dispose(false); + } + + [NonSerialized] private PropertyChangedEventHandler _propertyChanged; + + public event PropertyChangedEventHandler PropertyChanged + { + add => _propertyChanged += value; + remove => _propertyChanged -= value; + } + + public IList Filters + { + get + { + List ret = new List(); + foreach (IFieldHolder c in Children) + { + if (c is IFilter) + { + ret.Add(c as IFilter); + } + } + + return ret; + } + } + + [NonSerialized] internal Surface _parent; + + public ISurface Parent + { + get => _parent; + set => SwitchParent((Surface) value); + } + + [NonSerialized] private TargetAdorner _targetAdorner; + public TargetAdorner TargetAdorner => _targetAdorner; + + [NonSerialized] private bool _selected; + + public bool Selected + { + get => _selected; + set + { + _selected = value; + OnPropertyChanged("Selected"); + } + } + + [NonSerialized] private EditStatus _status = EditStatus.UNDRAWN; + + public EditStatus Status + { + get => _status; + set => _status = value; + } + + + private int left; + + public int Left + { + get => left; + set + { + if (value == left) + { + return; + } + + left = value; + } + } + + private int top; + + public int Top + { + get => top; + set + { + if (value == top) + { + return; + } + + top = value; + } + } + + private int width; + + public int Width + { + get => width; + set + { + if (value == width) + { + return; + } + + width = value; + } + } + + private int height; + + public int Height + { + get => height; + set + { + if (value == height) + { + return; + } + + height = value; + } + } + + public Point Location + { + get => new Point(left, top); + set + { + left = value.X; + top = value.Y; + } + } + + public Size Size + { + get => new Size(width, height); + set + { + width = value.Width; + height = value.Height; + } + } + + /// + /// List of available Adorners + /// + [NonSerialized] private IList _adorners = new List(); + + public IList Adorners => _adorners; + + [NonSerialized] + // will store current bounds of this DrawableContainer before starting a resize + protected Rectangle _boundsBeforeResize = Rectangle.Empty; + + [NonSerialized] + // "workbench" rectangle - used for calculating bounds during resizing (to be applied to this DrawableContainer afterwards) + protected RectangleF _boundsAfterResize = RectangleF.Empty; + + public Rectangle Bounds + { + get => GuiRectangle.GetGuiRectangle(Left, Top, Width, Height); + set + { + Left = Round(value.Left); + Top = Round(value.Top); + Width = Round(value.Width); + Height = Round(value.Height); + } + } + + public virtual void ApplyBounds(RectangleF newBounds) + { + Left = Round(newBounds.Left); + Top = Round(newBounds.Top); + Width = Round(newBounds.Width); + Height = Round(newBounds.Height); + } + + public DrawableContainer(Surface parent) + { + InitializeFields(); + _parent = parent; + } + + public void Add(IFilter filter) + { + AddChild(filter); + } + + public void Remove(IFilter filter) + { + RemoveChild(filter); + } + + private static int Round(float f) + { + if (float.IsPositiveInfinity(f) || f > int.MaxValue / 2) return int.MaxValue / 2; + if (float.IsNegativeInfinity(f) || f < int.MinValue / 2) return int.MinValue / 2; + return (int) Math.Round(f); + } + + private bool accountForShadowChange; + + public virtual Rectangle DrawingBounds + { + get + { + foreach (IFilter filter in Filters) + { + if (filter.Invert) + { + return new Rectangle(Point.Empty, _parent.Image.Size); + } + } + + // Take a base safety margin + int lineThickness = 5; + + // add adorner size + lineThickness += Adorners.Max(adorner => Math.Max(adorner.Bounds.Width, adorner.Bounds.Height)); + + if (HasField(FieldType.LINE_THICKNESS)) + { + lineThickness += GetFieldValueAsInt(FieldType.LINE_THICKNESS); + } + + int offset = lineThickness / 2; + + int shadow = 0; + if (accountForShadowChange || (HasField(FieldType.SHADOW) && GetFieldValueAsBool(FieldType.SHADOW))) + { + accountForShadowChange = false; + shadow += 10; + } + + return new Rectangle(Bounds.Left - offset, Bounds.Top - offset, Bounds.Width + lineThickness + shadow, Bounds.Height + lineThickness + shadow); + } + } + + public virtual void Invalidate() + { + if (Status != EditStatus.UNDRAWN) + { + _parent?.InvalidateElements(DrawingBounds); + } + } + + public virtual bool InitContent() + { + return true; + } + + public virtual void OnDoubleClick() + { + } + + /// + /// Initialize a target gripper + /// + protected void InitAdorner(Color gripperColor, Point location) + { + _targetAdorner = new TargetAdorner(this, location); + Adorners.Add(_targetAdorner); + } + + /// + /// Create the default adorners for a rectangle based container + /// + protected void CreateDefaultAdorners() + { + if (Adorners.Count > 0) + { + LOG.Warn("Adorners are already defined!"); + } + + // 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)); + } + + public bool hasFilters => Filters.Count > 0; + + public abstract void Draw(Graphics graphics, RenderMode renderMode); + + public virtual void DrawContent(Graphics graphics, Bitmap bmp, RenderMode renderMode, Rectangle clipRectangle) + { + if (Children.Count > 0) + { + if (Status != EditStatus.IDLE) + { + DrawSelectionBorder(graphics, Bounds); + } + else + { + if (clipRectangle.Width != 0 && clipRectangle.Height != 0) + { + foreach (IFilter filter in Filters) + { + if (filter.Invert) + { + filter.Apply(graphics, bmp, Bounds, renderMode); + } + else + { + Rectangle drawingRect = new Rectangle(Bounds.Location, Bounds.Size); + drawingRect.Intersect(clipRectangle); + if (filter is MagnifierFilter) + { + // quick&dirty bugfix, because MagnifierFilter behaves differently when drawn only partially + // what we should actually do to resolve this is add a better magnifier which is not that special + filter.Apply(graphics, bmp, Bounds, renderMode); + } + else + { + filter.Apply(graphics, bmp, drawingRect, renderMode); + } + } + } + } + } + } + + Draw(graphics, renderMode); + } + + /// + /// Adjust UI elements to the supplied DPI settings + /// + /// uint with dpi value + public void AdjustToDpi(uint dpi) + { + foreach (var adorner in Adorners) + { + adorner.AdjustToDpi(dpi); + } + } + + public virtual bool Contains(int x, int y) + { + return Bounds.Contains(x, y); + } + + public virtual bool ClickableAt(int x, int y) + { + Rectangle r = GuiRectangle.GetGuiRectangle(Left, Top, Width, Height); + r.Inflate(5, 5); + return r.Contains(x, y); + } + + protected void DrawSelectionBorder(Graphics g, Rectangle rect) + { + using Pen pen = new Pen(Color.MediumSeaGreen) + { + DashPattern = new float[] + { + 1, 2 + }, + Width = 1 + }; + g.DrawRectangle(pen, rect); + } + + + public void ResizeTo(int width, int height, int anchorPosition) + { + Width = width; + Height = height; + } + + /// + /// Make a following bounds change on this drawablecontainer undoable! + /// + /// true means allow the moves to be merged + public void MakeBoundsChangeUndoable(bool allowMerge) + { + _parent.MakeUndoable(new DrawableContainerBoundsChangeMemento(this), allowMerge); + } + + public void MoveBy(int dx, int dy) + { + Left += dx; + Top += dy; + } + + /// + /// A handler for the MouseDown, used if you don't want the surface to handle this for you + /// + /// current mouse x + /// current mouse y + /// true if the event is handled, false if the surface needs to handle it + public virtual bool HandleMouseDown(int x, int y) + { + Left = _boundsBeforeResize.X = x; + Top = _boundsBeforeResize.Y = y; + return true; + } + + /// + /// A handler for the MouseMove, used if you don't want the surface to handle this for you + /// + /// current mouse x + /// current mouse y + /// true if the event is handled, false if the surface needs to handle it + public virtual bool HandleMouseMove(int x, int y) + { + Invalidate(); + + // reset "workrbench" rectangle to current bounds + _boundsAfterResize.X = _boundsBeforeResize.Left; + _boundsAfterResize.Y = _boundsBeforeResize.Top; + _boundsAfterResize.Width = x - _boundsAfterResize.Left; + _boundsAfterResize.Height = y - _boundsAfterResize.Top; + + ScaleHelper.Scale(_boundsBeforeResize, x, y, ref _boundsAfterResize, GetAngleRoundProcessor()); + + // apply scaled bounds to this DrawableContainer + ApplyBounds(_boundsAfterResize); + + Invalidate(); + return true; + } + + /// + /// A handler for the MouseUp + /// + /// current mouse x + /// current mouse y + public virtual void HandleMouseUp(int x, int y) + { + } + + protected virtual void SwitchParent(Surface newParent) + { + if (newParent == Parent) + { + return; + } + + _parent?.FieldAggregator?.UnbindElement(this); + + _parent = newParent; + foreach (IFilter filter in Filters) + { + filter.Parent = this; + } + } + + protected void OnPropertyChanged(string propertyName) + { + if (_propertyChanged != null) + { + _propertyChanged(this, new PropertyChangedEventArgs(propertyName)); + Invalidate(); + } + } + + /// + /// This method will be called before a field is changes. + /// Using this makes it possible to invalidate the object as is before changing. + /// + /// The field to be changed + /// The new value + public virtual void BeforeFieldChange(IField fieldToBeChanged, object newValue) + { + _parent?.MakeUndoable(new ChangeFieldHolderMemento(this, fieldToBeChanged), true); + Invalidate(); + } + + /// + /// Handle the field changed event, this should invalidate the correct bounds (e.g. when shadow comes or goes more pixels!) + /// + /// + /// + public void HandleFieldChanged(object sender, FieldChangedEventArgs e) + { + LOG.DebugFormat("Field {0} changed", e.Field.FieldType); + if (Equals(e.Field.FieldType, FieldType.SHADOW)) + { + accountForShadowChange = true; + } + } + + /// + /// Retrieve the Y scale from the matrix + /// + /// + /// + public static float CalculateScaleY(Matrix matrix) + { + return matrix.Elements[M22]; + } + + /// + /// Retrieve the X scale from the matrix + /// + /// + /// + public static float CalculateScaleX(Matrix matrix) + { + return matrix.Elements[M11]; + } + + /// + /// Retrieve the rotation angle from the matrix + /// + /// + /// + public static int CalculateAngle(Matrix matrix) + { + const int M11 = 0; + const int M21 = 2; + var radians = Math.Atan2(matrix.Elements[M21], matrix.Elements[M11]); + return (int) -Math.Round(radians * 180 / Math.PI); + } + + /// + /// This method is called on a DrawableContainers when: + /// 1) The capture on the surface is modified in such a way, that the elements would not be placed correctly. + /// 2) Currently not implemented: an element needs to be moved, scaled or rotated. + /// This basis implementation makes sure the coordinates of the element, including the TargetGripper, is correctly rotated/scaled/translated. + /// But this implementation doesn't take care of any changes to the content!! + /// + /// + public virtual void Transform(Matrix matrix) + { + if (matrix == null) + { + return; + } + + Point topLeft = new Point(Left, Top); + Point bottomRight = new Point(Left + Width, Top + Height); + 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; + } + + protected virtual ScaleHelper.IDoubleProcessor GetAngleRoundProcessor() + { + return ScaleHelper.ShapeAngleRoundBehavior.Instance; + } + + public virtual bool HasContextMenu => true; + + public virtual bool HasDefaultSize => false; + + public virtual Size DefaultSize => throw new NotSupportedException("Object doesn't have a default size"); + + /// + /// Allows to override the initializing of the fields, so we can actually have our own defaults + /// + protected virtual void InitializeFields() + { + } + } } \ No newline at end of file diff --git a/src/Greenshot/Drawing/DrawableContainerList.cs b/src/Greenshot.Editor/Drawing/DrawableContainerList.cs similarity index 96% rename from src/Greenshot/Drawing/DrawableContainerList.cs rename to src/Greenshot.Editor/Drawing/DrawableContainerList.cs index 3a50a9ef8..9465d7779 100644 --- a/src/Greenshot/Drawing/DrawableContainerList.cs +++ b/src/Greenshot.Editor/Drawing/DrawableContainerList.cs @@ -1,728 +1,728 @@ -/* - * Greenshot - a free and open source screenshot tool - * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom - * - * For more information see: https://getgreenshot.org/ - * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 1 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -using Greenshot.Configuration; -using Greenshot.Memento; -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Drawing; -using System.Drawing.Drawing2D; -using System.Threading; -using System.Windows.Forms; -using Greenshot.Base.Core; -using Greenshot.Base.Interfaces; -using Greenshot.Base.Interfaces.Drawing; -using Greenshot.Forms; - -namespace Greenshot.Drawing -{ - /// - /// Dispatches most of a DrawableContainer's public properties and methods to a list of DrawableContainers. - /// - [Serializable] - public class DrawableContainerList : List, IDrawableContainerList - { - private static readonly ComponentResourceManager EditorFormResources = new ComponentResourceManager(typeof(ImageEditorForm)); - - public Guid ParentID { get; private set; } - - public DrawableContainerList() - { - } - - public DrawableContainerList(Guid parentId) - { - ParentID = parentId; - } - - public EditStatus Status - { - get { return this[Count - 1].Status; } - set - { - foreach (var dc in this) - { - dc.Status = value; - } - } - } - - public List AsIDrawableContainerList() - { - List interfaceList = new List(); - foreach (IDrawableContainer container in this) - { - interfaceList.Add(container); - } - - return interfaceList; - } - - /// - /// Gets or sets the selection status of the elements. - /// If several elements are in the list, true is only returned when all elements are selected. - /// - public bool Selected - { - get - { - bool ret = true; - foreach (var dc in this) - { - ret &= dc.Selected; - } - - return ret; - } - set - { - foreach (var dc in this) - { - dc.Selected = value; - } - } - } - - /// - /// Gets or sets the parent control of the elements in the list. - /// If there are several elements, the parent control of the last added is returned. - /// - public ISurface Parent - { - get - { - if (Count > 0) - { - return this[Count - 1].Parent; - } - - return null; - } - set - { - ParentID = value?.ID ?? Guid.NewGuid(); - foreach (var drawableContainer in this) - { - var dc = (DrawableContainer) drawableContainer; - dc.Parent = value; - } - } - } - - /// - /// Make a following bounds change on this containerlist undoable! - /// - /// true means allow the moves to be merged - public void MakeBoundsChangeUndoable(bool allowMerge) - { - if (Count > 0 && Parent != null) - { - var clone = new DrawableContainerList(); - clone.AddRange(this); - Parent.MakeUndoable(new DrawableContainerBoundsChangeMemento(clone), allowMerge); - } - } - - /// - /// Apply matrix to all elements - /// - public void Transform(Matrix matrix) - { - // Track modifications - bool modified = false; - Invalidate(); - foreach (var dc in this) - { - dc.Transform(matrix); - modified = true; - } - - // Invalidate after - Invalidate(); - // If we moved something, tell the surface it's modified! - if (modified) - { - Parent.Modified = true; - } - } - - /// - /// Moves all elements in the list by the given amount of pixels. - /// - /// pixels to move horizontally - /// pixels to move vertically - public void MoveBy(int dx, int dy) - { - // Track modifications - bool modified = false; - - // Invalidate before moving, otherwise the old locations aren't refreshed - Invalidate(); - foreach (var dc in this) - { - dc.Left += dx; - dc.Top += dy; - modified = true; - } - - // Invalidate after - Invalidate(); - - // If we moved something, tell the surface it's modified! - if (modified) - { - Parent.Modified = true; - } - } - - /// - /// Indicates whether on of the elements is clickable at the given location - /// - /// x coordinate to be checked - /// y coordinate to be checked - /// true if one of the elements in the list is clickable at the given location, false otherwise - public bool ClickableAt(int x, int y) - { - bool ret = false; - foreach (var dc in this) - { - ret |= dc.ClickableAt(x, y); - } - - return ret; - } - - /// - /// retrieves the topmost element being clickable at the given location - /// - /// x coordinate to be checked - /// y coordinate to be checked - /// the topmost element from the list being clickable at the given location, null if there is no clickable element - public IDrawableContainer ClickableElementAt(int x, int y) - { - for (int i = Count - 1; i >= 0; i--) - { - if (this[i].ClickableAt(x, y)) - { - return this[i]; - } - } - - return null; - } - - /// - /// Dispatches OnDoubleClick to all elements in the list. - /// - public void OnDoubleClick() - { - foreach (var drawableContainer in this) - { - var dc = (DrawableContainer) drawableContainer; - dc.OnDoubleClick(); - } - } - - /// - /// Check if there are any intersecting filters, if so we need to redraw more - /// - /// - /// true if an filter intersects - public bool HasIntersectingFilters(Rectangle clipRectangle) - { - foreach (var dc in this) - { - if (dc.DrawingBounds.IntersectsWith(clipRectangle) && dc.hasFilters && dc.Status == EditStatus.IDLE) - { - return true; - } - } - - return false; - } - - /// - /// Check if any of the drawableContainers are inside the rectangle - /// - /// - /// - public bool IntersectsWith(Rectangle clipRectangle) - { - foreach (var dc in this) - { - if (dc.DrawingBounds.IntersectsWith(clipRectangle)) - { - return true; - } - } - - return false; - } - - /// - /// A rectangle containing DrawingBounds of all drawableContainers in this list, - /// or empty rectangle if nothing is there. - /// - public Rectangle DrawingBounds - { - get - { - if (Count == 0) - { - return Rectangle.Empty; - } - else - { - var result = this[0].DrawingBounds; - for (int i = 1; i < Count; i++) - { - result = Rectangle.Union(result, this[i].DrawingBounds); - } - - return result; - } - } - } - - /// - /// Triggers all elements in the list ot be redrawn. - /// - /// the to the bitmap related Graphics object - /// Bitmap to draw - /// the rendermode in which the element is to be drawn - /// - public void Draw(Graphics g, Bitmap bitmap, RenderMode renderMode, Rectangle clipRectangle) - { - if (Parent == null) - { - return; - } - - foreach (var drawableContainer in this) - { - var dc = (DrawableContainer) drawableContainer; - if (dc.Parent == null) - { - continue; - } - - if (dc.DrawingBounds.IntersectsWith(clipRectangle)) - { - dc.DrawContent(g, bitmap, renderMode, clipRectangle); - } - } - } - - /// - /// Pass the field changed event to all elements in the list - /// - /// - /// - public void HandleFieldChangedEvent(object sender, FieldChangedEventArgs e) - { - foreach (var drawableContainer in this) - { - var dc = (DrawableContainer) drawableContainer; - dc.HandleFieldChanged(sender, e); - } - } - - /// - /// Invalidate the bounds of all the DC's in this list - /// - public void Invalidate() - { - if (Parent == null) - { - return; - } - - Rectangle region = Rectangle.Empty; - foreach (var dc in this) - { - region = Rectangle.Union(region, dc.DrawingBounds); - } - - Parent.InvalidateElements(region); - } - - /// - /// Indicates whether the given list of elements can be pulled up, - /// i.e. whether there is at least one unselected element higher in hierarchy - /// - /// list of elements to pull up - /// true if the elements could be pulled up - public bool CanPullUp(IDrawableContainerList elements) - { - if (elements.Count == 0 || elements.Count == Count) - { - return false; - } - - foreach (var element in elements) - { - if (IndexOf(element) < Count - elements.Count) - { - return true; - } - } - - return false; - } - - /// - /// Pulls one or several elements up one level in hierarchy (z-index). - /// - /// list of elements to pull up - public void PullElementsUp(IDrawableContainerList elements) - { - for (int i = Count - 1; i >= 0; i--) - { - var dc = this[i]; - if (!elements.Contains(dc)) - { - continue; - } - - if (Count > i + 1 && !elements.Contains(this[i + 1])) - { - SwapElements(i, i + 1); - } - } - } - - /// - /// Pulls one or several elements up to the topmost level(s) in hierarchy (z-index). - /// - /// of elements to pull to top - public void PullElementsToTop(IDrawableContainerList elements) - { - var dcs = ToArray(); - foreach (var dc in dcs) - { - if (!elements.Contains(dc)) - { - continue; - } - - Remove(dc); - Add(dc); - Parent.Modified = true; - } - } - - /// - /// Indicates whether the given list of elements can be pushed down, - /// i.e. whether there is at least one unselected element lower in hierarchy - /// - /// list of elements to push down - /// true if the elements could be pushed down - public bool CanPushDown(IDrawableContainerList elements) - { - if (elements.Count == 0 || elements.Count == Count) - { - return false; - } - - foreach (var element in elements) - { - if (IndexOf(element) >= elements.Count) - { - return true; - } - } - - return false; - } - - /// - /// Pushes one or several elements down one level in hierarchy (z-index). - /// - /// list of elements to push down - public void PushElementsDown(IDrawableContainerList elements) - { - for (int i = 0; i < Count; i++) - { - var dc = this[i]; - if (!elements.Contains(dc)) - { - continue; - } - - if ((i > 0) && !elements.Contains(this[i - 1])) - { - SwapElements(i, i - 1); - } - } - } - - /// - /// Pushes one or several elements down to the bottommost level(s) in hierarchy (z-index). - /// - /// of elements to push to bottom - public void PushElementsToBottom(IDrawableContainerList elements) - { - var dcs = ToArray(); - for (int i = dcs.Length - 1; i >= 0; i--) - { - var dc = dcs[i]; - if (!elements.Contains(dc)) - { - continue; - } - - Remove(dc); - Insert(0, dc); - Parent.Modified = true; - } - } - - /// - /// swaps two elements in hierarchy (z-index), - /// checks both indices to be in range - /// - /// index of the 1st element - /// index of the 2nd element - private void SwapElements(int index1, int index2) - { - if (index1 < 0 || index1 >= Count || index2 < 0 || index2 >= Count || index1 == index2) - { - return; - } - - var dc = this[index1]; - this[index1] = this[index2]; - this[index2] = dc; - Parent.Modified = true; - } - - /// - /// Add items to a context menu for the selected item - /// - /// - /// - public virtual void AddContextMenuItems(ContextMenuStrip menu, ISurface surface) - { - bool push = surface.Elements.CanPushDown(this); - bool pull = surface.Elements.CanPullUp(this); - - ToolStripMenuItem item; - - // Pull "up" - if (pull) - { - item = new ToolStripMenuItem(Language.GetString(LangKey.editor_uptotop)); - item.Click += delegate - { - surface.Elements.PullElementsToTop(this); - surface.Elements.Invalidate(); - }; - menu.Items.Add(item); - item = new ToolStripMenuItem(Language.GetString(LangKey.editor_uponelevel)); - item.Click += delegate - { - surface.Elements.PullElementsUp(this); - surface.Elements.Invalidate(); - }; - menu.Items.Add(item); - } - - // Push "down" - if (push) - { - item = new ToolStripMenuItem(Language.GetString(LangKey.editor_downtobottom)); - item.Click += delegate - { - surface.Elements.PushElementsToBottom(this); - surface.Elements.Invalidate(); - }; - menu.Items.Add(item); - item = new ToolStripMenuItem(Language.GetString(LangKey.editor_downonelevel)); - item.Click += delegate - { - surface.Elements.PushElementsDown(this); - surface.Elements.Invalidate(); - }; - menu.Items.Add(item); - } - - // Duplicate - item = new ToolStripMenuItem(Language.GetString(LangKey.editor_duplicate)); - item.Click += delegate - { - IDrawableContainerList dcs = this.Clone(); - dcs.Parent = surface; - dcs.MoveBy(10, 10); - surface.AddElements(dcs); - surface.DeselectAllElements(); - surface.SelectElements(dcs); - }; - menu.Items.Add(item); - - // Copy - item = new ToolStripMenuItem(Language.GetString(LangKey.editor_copytoclipboard)) - { - Image = (Image) EditorFormResources.GetObject("copyToolStripMenuItem.Image") - }; - item.Click += delegate { ClipboardHelper.SetClipboardData(typeof(IDrawableContainerList), this); }; - menu.Items.Add(item); - - // Cut - item = new ToolStripMenuItem(Language.GetString(LangKey.editor_cuttoclipboard)) - { - Image = (Image) EditorFormResources.GetObject("btnCut.Image") - }; - item.Click += delegate - { - ClipboardHelper.SetClipboardData(typeof(IDrawableContainerList), this); - surface.RemoveElements(this); - }; - menu.Items.Add(item); - - // Delete - item = new ToolStripMenuItem(Language.GetString(LangKey.editor_deleteelement)) - { - Image = (Image) EditorFormResources.GetObject("removeObjectToolStripMenuItem.Image") - }; - item.Click += delegate { surface.RemoveElements(this); }; - menu.Items.Add(item); - - // Reset - bool canReset = false; - foreach (var drawableContainer in this) - { - var container = (DrawableContainer) drawableContainer; - if (container.HasDefaultSize) - { - canReset = true; - } - } - - if (canReset) - { - item = new ToolStripMenuItem(Language.GetString(LangKey.editor_resetsize)); - //item.Image = ((System.Drawing.Image)(editorFormResources.GetObject("removeObjectToolStripMenuItem.Image"))); - item.Click += delegate - { - MakeBoundsChangeUndoable(false); - foreach (var drawableContainer in this) - { - var container = (DrawableContainer) drawableContainer; - if (!container.HasDefaultSize) - { - continue; - } - - Size defaultSize = container.DefaultSize; - container.MakeBoundsChangeUndoable(false); - container.Width = defaultSize.Width; - container.Height = defaultSize.Height; - } - - surface.Invalidate(); - }; - menu.Items.Add(item); - } - } - - public virtual void ShowContextMenu(MouseEventArgs e, ISurface iSurface) - { - if (!(iSurface is Surface surface)) - { - return; - } - - bool hasMenu = false; - foreach (var drawableContainer in this) - { - var container = (DrawableContainer) drawableContainer; - if (!container.HasContextMenu) - { - continue; - } - - hasMenu = true; - break; - } - - if (hasMenu) - { - ContextMenuStrip menu = new ContextMenuStrip(); - AddContextMenuItems(menu, surface); - if (menu.Items.Count > 0) - { - menu.Show(surface, surface.ToSurfaceCoordinates(e.Location)); - while (true) - { - if (menu.Visible) - { - Application.DoEvents(); - Thread.Sleep(100); - } - else - { - menu.Dispose(); - break; - } - } - } - } - } - - private bool _disposedValue; // To detect redundant calls - - protected virtual void Dispose(bool disposing) - { - if (!_disposedValue) - { - if (disposing) - { - foreach (var drawableContainer in this) - { - drawableContainer.Dispose(); - } - } - - _disposedValue = true; - } - } - - // This code added to correctly implement the disposable pattern. - public void Dispose() - { - // Do not change this code. Put cleanup code in Dispose(bool disposing) above. - Dispose(true); - } - - /// - /// Adjust UI elements to the supplied DPI settings - /// - /// - public void AdjustToDpi(uint dpi) - { - foreach (var drawableContainer in this) - { - drawableContainer.AdjustToDpi(dpi); - } - } - } +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.Drawing.Drawing2D; +using System.Threading; +using System.Windows.Forms; +using Greenshot.Base.Core; +using Greenshot.Base.Interfaces; +using Greenshot.Base.Interfaces.Drawing; +using Greenshot.Editor.Configuration; +using Greenshot.Editor.Forms; +using Greenshot.Editor.Memento; + +namespace Greenshot.Editor.Drawing +{ + /// + /// Dispatches most of a DrawableContainer's public properties and methods to a list of DrawableContainers. + /// + [Serializable] + public class DrawableContainerList : List, IDrawableContainerList + { + private static readonly ComponentResourceManager EditorFormResources = new ComponentResourceManager(typeof(ImageEditorForm)); + + public Guid ParentID { get; private set; } + + public DrawableContainerList() + { + } + + public DrawableContainerList(Guid parentId) + { + ParentID = parentId; + } + + public EditStatus Status + { + get { return this[Count - 1].Status; } + set + { + foreach (var dc in this) + { + dc.Status = value; + } + } + } + + public List AsIDrawableContainerList() + { + List interfaceList = new List(); + foreach (IDrawableContainer container in this) + { + interfaceList.Add(container); + } + + return interfaceList; + } + + /// + /// Gets or sets the selection status of the elements. + /// If several elements are in the list, true is only returned when all elements are selected. + /// + public bool Selected + { + get + { + bool ret = true; + foreach (var dc in this) + { + ret &= dc.Selected; + } + + return ret; + } + set + { + foreach (var dc in this) + { + dc.Selected = value; + } + } + } + + /// + /// Gets or sets the parent control of the elements in the list. + /// If there are several elements, the parent control of the last added is returned. + /// + public ISurface Parent + { + get + { + if (Count > 0) + { + return this[Count - 1].Parent; + } + + return null; + } + set + { + ParentID = value?.ID ?? Guid.NewGuid(); + foreach (var drawableContainer in this) + { + var dc = (DrawableContainer) drawableContainer; + dc.Parent = value; + } + } + } + + /// + /// Make a following bounds change on this containerlist undoable! + /// + /// true means allow the moves to be merged + public void MakeBoundsChangeUndoable(bool allowMerge) + { + if (Count > 0 && Parent != null) + { + var clone = new DrawableContainerList(); + clone.AddRange(this); + Parent.MakeUndoable(new DrawableContainerBoundsChangeMemento(clone), allowMerge); + } + } + + /// + /// Apply matrix to all elements + /// + public void Transform(Matrix matrix) + { + // Track modifications + bool modified = false; + Invalidate(); + foreach (var dc in this) + { + dc.Transform(matrix); + modified = true; + } + + // Invalidate after + Invalidate(); + // If we moved something, tell the surface it's modified! + if (modified) + { + Parent.Modified = true; + } + } + + /// + /// Moves all elements in the list by the given amount of pixels. + /// + /// pixels to move horizontally + /// pixels to move vertically + public void MoveBy(int dx, int dy) + { + // Track modifications + bool modified = false; + + // Invalidate before moving, otherwise the old locations aren't refreshed + Invalidate(); + foreach (var dc in this) + { + dc.Left += dx; + dc.Top += dy; + modified = true; + } + + // Invalidate after + Invalidate(); + + // If we moved something, tell the surface it's modified! + if (modified) + { + Parent.Modified = true; + } + } + + /// + /// Indicates whether on of the elements is clickable at the given location + /// + /// x coordinate to be checked + /// y coordinate to be checked + /// true if one of the elements in the list is clickable at the given location, false otherwise + public bool ClickableAt(int x, int y) + { + bool ret = false; + foreach (var dc in this) + { + ret |= dc.ClickableAt(x, y); + } + + return ret; + } + + /// + /// retrieves the topmost element being clickable at the given location + /// + /// x coordinate to be checked + /// y coordinate to be checked + /// the topmost element from the list being clickable at the given location, null if there is no clickable element + public IDrawableContainer ClickableElementAt(int x, int y) + { + for (int i = Count - 1; i >= 0; i--) + { + if (this[i].ClickableAt(x, y)) + { + return this[i]; + } + } + + return null; + } + + /// + /// Dispatches OnDoubleClick to all elements in the list. + /// + public void OnDoubleClick() + { + foreach (var drawableContainer in this) + { + var dc = (DrawableContainer) drawableContainer; + dc.OnDoubleClick(); + } + } + + /// + /// Check if there are any intersecting filters, if so we need to redraw more + /// + /// + /// true if an filter intersects + public bool HasIntersectingFilters(Rectangle clipRectangle) + { + foreach (var dc in this) + { + if (dc.DrawingBounds.IntersectsWith(clipRectangle) && dc.hasFilters && dc.Status == EditStatus.IDLE) + { + return true; + } + } + + return false; + } + + /// + /// Check if any of the drawableContainers are inside the rectangle + /// + /// + /// + public bool IntersectsWith(Rectangle clipRectangle) + { + foreach (var dc in this) + { + if (dc.DrawingBounds.IntersectsWith(clipRectangle)) + { + return true; + } + } + + return false; + } + + /// + /// A rectangle containing DrawingBounds of all drawableContainers in this list, + /// or empty rectangle if nothing is there. + /// + public Rectangle DrawingBounds + { + get + { + if (Count == 0) + { + return Rectangle.Empty; + } + else + { + var result = this[0].DrawingBounds; + for (int i = 1; i < Count; i++) + { + result = Rectangle.Union(result, this[i].DrawingBounds); + } + + return result; + } + } + } + + /// + /// Triggers all elements in the list ot be redrawn. + /// + /// the to the bitmap related Graphics object + /// Bitmap to draw + /// the rendermode in which the element is to be drawn + /// + public void Draw(Graphics g, Bitmap bitmap, RenderMode renderMode, Rectangle clipRectangle) + { + if (Parent == null) + { + return; + } + + foreach (var drawableContainer in this) + { + var dc = (DrawableContainer) drawableContainer; + if (dc.Parent == null) + { + continue; + } + + if (dc.DrawingBounds.IntersectsWith(clipRectangle)) + { + dc.DrawContent(g, bitmap, renderMode, clipRectangle); + } + } + } + + /// + /// Pass the field changed event to all elements in the list + /// + /// + /// + public void HandleFieldChangedEvent(object sender, FieldChangedEventArgs e) + { + foreach (var drawableContainer in this) + { + var dc = (DrawableContainer) drawableContainer; + dc.HandleFieldChanged(sender, e); + } + } + + /// + /// Invalidate the bounds of all the DC's in this list + /// + public void Invalidate() + { + if (Parent == null) + { + return; + } + + Rectangle region = Rectangle.Empty; + foreach (var dc in this) + { + region = Rectangle.Union(region, dc.DrawingBounds); + } + + Parent.InvalidateElements(region); + } + + /// + /// Indicates whether the given list of elements can be pulled up, + /// i.e. whether there is at least one unselected element higher in hierarchy + /// + /// list of elements to pull up + /// true if the elements could be pulled up + public bool CanPullUp(IDrawableContainerList elements) + { + if (elements.Count == 0 || elements.Count == Count) + { + return false; + } + + foreach (var element in elements) + { + if (IndexOf(element) < Count - elements.Count) + { + return true; + } + } + + return false; + } + + /// + /// Pulls one or several elements up one level in hierarchy (z-index). + /// + /// list of elements to pull up + public void PullElementsUp(IDrawableContainerList elements) + { + for (int i = Count - 1; i >= 0; i--) + { + var dc = this[i]; + if (!elements.Contains(dc)) + { + continue; + } + + if (Count > i + 1 && !elements.Contains(this[i + 1])) + { + SwapElements(i, i + 1); + } + } + } + + /// + /// Pulls one or several elements up to the topmost level(s) in hierarchy (z-index). + /// + /// of elements to pull to top + public void PullElementsToTop(IDrawableContainerList elements) + { + var dcs = ToArray(); + foreach (var dc in dcs) + { + if (!elements.Contains(dc)) + { + continue; + } + + Remove(dc); + Add(dc); + Parent.Modified = true; + } + } + + /// + /// Indicates whether the given list of elements can be pushed down, + /// i.e. whether there is at least one unselected element lower in hierarchy + /// + /// list of elements to push down + /// true if the elements could be pushed down + public bool CanPushDown(IDrawableContainerList elements) + { + if (elements.Count == 0 || elements.Count == Count) + { + return false; + } + + foreach (var element in elements) + { + if (IndexOf(element) >= elements.Count) + { + return true; + } + } + + return false; + } + + /// + /// Pushes one or several elements down one level in hierarchy (z-index). + /// + /// list of elements to push down + public void PushElementsDown(IDrawableContainerList elements) + { + for (int i = 0; i < Count; i++) + { + var dc = this[i]; + if (!elements.Contains(dc)) + { + continue; + } + + if ((i > 0) && !elements.Contains(this[i - 1])) + { + SwapElements(i, i - 1); + } + } + } + + /// + /// Pushes one or several elements down to the bottommost level(s) in hierarchy (z-index). + /// + /// of elements to push to bottom + public void PushElementsToBottom(IDrawableContainerList elements) + { + var dcs = ToArray(); + for (int i = dcs.Length - 1; i >= 0; i--) + { + var dc = dcs[i]; + if (!elements.Contains(dc)) + { + continue; + } + + Remove(dc); + Insert(0, dc); + Parent.Modified = true; + } + } + + /// + /// swaps two elements in hierarchy (z-index), + /// checks both indices to be in range + /// + /// index of the 1st element + /// index of the 2nd element + private void SwapElements(int index1, int index2) + { + if (index1 < 0 || index1 >= Count || index2 < 0 || index2 >= Count || index1 == index2) + { + return; + } + + var dc = this[index1]; + this[index1] = this[index2]; + this[index2] = dc; + Parent.Modified = true; + } + + /// + /// Add items to a context menu for the selected item + /// + /// + /// + public virtual void AddContextMenuItems(ContextMenuStrip menu, ISurface surface) + { + bool push = surface.Elements.CanPushDown(this); + bool pull = surface.Elements.CanPullUp(this); + + ToolStripMenuItem item; + + // Pull "up" + if (pull) + { + item = new ToolStripMenuItem(Language.GetString(LangKey.editor_uptotop)); + item.Click += delegate + { + surface.Elements.PullElementsToTop(this); + surface.Elements.Invalidate(); + }; + menu.Items.Add(item); + item = new ToolStripMenuItem(Language.GetString(LangKey.editor_uponelevel)); + item.Click += delegate + { + surface.Elements.PullElementsUp(this); + surface.Elements.Invalidate(); + }; + menu.Items.Add(item); + } + + // Push "down" + if (push) + { + item = new ToolStripMenuItem(Language.GetString(LangKey.editor_downtobottom)); + item.Click += delegate + { + surface.Elements.PushElementsToBottom(this); + surface.Elements.Invalidate(); + }; + menu.Items.Add(item); + item = new ToolStripMenuItem(Language.GetString(LangKey.editor_downonelevel)); + item.Click += delegate + { + surface.Elements.PushElementsDown(this); + surface.Elements.Invalidate(); + }; + menu.Items.Add(item); + } + + // Duplicate + item = new ToolStripMenuItem(Language.GetString(LangKey.editor_duplicate)); + item.Click += delegate + { + IDrawableContainerList dcs = this.Clone(); + dcs.Parent = surface; + dcs.MoveBy(10, 10); + surface.AddElements(dcs); + surface.DeselectAllElements(); + surface.SelectElements(dcs); + }; + menu.Items.Add(item); + + // Copy + item = new ToolStripMenuItem(Language.GetString(LangKey.editor_copytoclipboard)) + { + Image = (Image) EditorFormResources.GetObject("copyToolStripMenuItem.Image") + }; + item.Click += delegate { ClipboardHelper.SetClipboardData(typeof(IDrawableContainerList), this); }; + menu.Items.Add(item); + + // Cut + item = new ToolStripMenuItem(Language.GetString(LangKey.editor_cuttoclipboard)) + { + Image = (Image) EditorFormResources.GetObject("btnCut.Image") + }; + item.Click += delegate + { + ClipboardHelper.SetClipboardData(typeof(IDrawableContainerList), this); + surface.RemoveElements(this); + }; + menu.Items.Add(item); + + // Delete + item = new ToolStripMenuItem(Language.GetString(LangKey.editor_deleteelement)) + { + Image = (Image) EditorFormResources.GetObject("removeObjectToolStripMenuItem.Image") + }; + item.Click += delegate { surface.RemoveElements(this); }; + menu.Items.Add(item); + + // Reset + bool canReset = false; + foreach (var drawableContainer in this) + { + var container = (DrawableContainer) drawableContainer; + if (container.HasDefaultSize) + { + canReset = true; + } + } + + if (canReset) + { + item = new ToolStripMenuItem(Language.GetString(LangKey.editor_resetsize)); + //item.Image = ((System.Drawing.Image)(editorFormResources.GetObject("removeObjectToolStripMenuItem.Image"))); + item.Click += delegate + { + MakeBoundsChangeUndoable(false); + foreach (var drawableContainer in this) + { + var container = (DrawableContainer) drawableContainer; + if (!container.HasDefaultSize) + { + continue; + } + + Size defaultSize = container.DefaultSize; + container.MakeBoundsChangeUndoable(false); + container.Width = defaultSize.Width; + container.Height = defaultSize.Height; + } + + surface.Invalidate(); + }; + menu.Items.Add(item); + } + } + + public virtual void ShowContextMenu(MouseEventArgs e, ISurface iSurface) + { + if (!(iSurface is Surface surface)) + { + return; + } + + bool hasMenu = false; + foreach (var drawableContainer in this) + { + var container = (DrawableContainer) drawableContainer; + if (!container.HasContextMenu) + { + continue; + } + + hasMenu = true; + break; + } + + if (hasMenu) + { + ContextMenuStrip menu = new ContextMenuStrip(); + AddContextMenuItems(menu, surface); + if (menu.Items.Count > 0) + { + menu.Show(surface, surface.ToSurfaceCoordinates(e.Location)); + while (true) + { + if (menu.Visible) + { + Application.DoEvents(); + Thread.Sleep(100); + } + else + { + menu.Dispose(); + break; + } + } + } + } + } + + private bool _disposedValue; // To detect redundant calls + + protected virtual void Dispose(bool disposing) + { + if (!_disposedValue) + { + if (disposing) + { + foreach (var drawableContainer in this) + { + drawableContainer.Dispose(); + } + } + + _disposedValue = true; + } + } + + // This code added to correctly implement the disposable pattern. + public void Dispose() + { + // Do not change this code. Put cleanup code in Dispose(bool disposing) above. + Dispose(true); + } + + /// + /// Adjust UI elements to the supplied DPI settings + /// + /// + public void AdjustToDpi(uint dpi) + { + foreach (var drawableContainer in this) + { + drawableContainer.AdjustToDpi(dpi); + } + } + } } \ No newline at end of file diff --git a/src/Greenshot/Drawing/EllipseContainer.cs b/src/Greenshot.Editor/Drawing/EllipseContainer.cs similarity index 96% rename from src/Greenshot/Drawing/EllipseContainer.cs rename to src/Greenshot.Editor/Drawing/EllipseContainer.cs index 822587901..d2c854cb1 100644 --- a/src/Greenshot/Drawing/EllipseContainer.cs +++ b/src/Greenshot.Editor/Drawing/EllipseContainer.cs @@ -1,163 +1,163 @@ -/* - * Greenshot - a free and open source screenshot tool - * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom - * - * For more information see: https://getgreenshot.org/ - * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 1 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -using System; -using System.Drawing; -using System.Drawing.Drawing2D; -using Greenshot.Base.Interfaces.Drawing; -using Greenshot.Drawing.Fields; -using Greenshot.Helpers; - -namespace Greenshot.Drawing -{ - /// - /// Description of EllipseContainer. - /// - [Serializable()] - public class EllipseContainer : DrawableContainer - { - public EllipseContainer(Surface parent) : base(parent) - { - CreateDefaultAdorners(); - } - - protected override void InitializeFields() - { - AddField(GetType(), FieldType.LINE_THICKNESS, 2); - AddField(GetType(), FieldType.LINE_COLOR, Color.Red); - AddField(GetType(), FieldType.FILL_COLOR, Color.Transparent); - AddField(GetType(), FieldType.SHADOW, true); - } - - public override void Draw(Graphics graphics, RenderMode renderMode) - { - graphics.SmoothingMode = SmoothingMode.HighQuality; - graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; - graphics.CompositingQuality = CompositingQuality.HighQuality; - graphics.PixelOffsetMode = PixelOffsetMode.None; - - int lineThickness = GetFieldValueAsInt(FieldType.LINE_THICKNESS); - Color lineColor = GetFieldValueAsColor(FieldType.LINE_COLOR); - Color fillColor = GetFieldValueAsColor(FieldType.FILL_COLOR); - bool shadow = GetFieldValueAsBool(FieldType.SHADOW); - Rectangle rect = GuiRectangle.GetGuiRectangle(Left, Top, Width, Height); - DrawEllipse(rect, graphics, renderMode, lineThickness, lineColor, fillColor, shadow); - } - - /// - /// This allows another container to draw an ellipse - /// - /// - /// - /// - /// - /// - /// - /// - public static void DrawEllipse(Rectangle rect, Graphics graphics, RenderMode renderMode, int lineThickness, Color lineColor, Color fillColor, bool shadow) - { - bool lineVisible = lineThickness > 0 && Colors.IsVisible(lineColor); - // draw shadow before anything else - if (shadow && (lineVisible || Colors.IsVisible(fillColor))) - { - int basealpha = 100; - int alpha = basealpha; - int steps = 5; - int currentStep = lineVisible ? 1 : 0; - while (currentStep <= steps) - { - using Pen shadowPen = new Pen(Color.FromArgb(alpha, 100, 100, 100)) - { - Width = lineVisible ? lineThickness : 1 - }; - Rectangle shadowRect = GuiRectangle.GetGuiRectangle(rect.Left + currentStep, rect.Top + currentStep, rect.Width, rect.Height); - graphics.DrawEllipse(shadowPen, shadowRect); - currentStep++; - alpha -= basealpha / steps; - } - } - - //draw the original shape - if (Colors.IsVisible(fillColor)) - { - using Brush brush = new SolidBrush(fillColor); - graphics.FillEllipse(brush, rect); - } - - if (lineVisible) - { - using Pen pen = new Pen(lineColor, lineThickness); - graphics.DrawEllipse(pen, rect); - } - } - - public override bool Contains(int x, int y) - { - return EllipseContains(this, x, y); - } - - /// - /// Allow the code to be used externally - /// - /// - /// - /// - /// - public static bool EllipseContains(DrawableContainer caller, int x, int y) - { - double xDistanceFromCenter = x - (caller.Left + caller.Width / 2); - double yDistanceFromCenter = y - (caller.Top + caller.Height / 2); - // ellipse: x^2/a^2 + y^2/b^2 = 1 - return Math.Pow(xDistanceFromCenter, 2) / Math.Pow(caller.Width / 2, 2) + Math.Pow(yDistanceFromCenter, 2) / Math.Pow(caller.Height / 2, 2) < 1; - } - - public override bool ClickableAt(int x, int y) - { - int lineThickness = GetFieldValueAsInt(FieldType.LINE_THICKNESS) + 10; - Color fillColor = GetFieldValueAsColor(FieldType.FILL_COLOR); - Rectangle rect = GuiRectangle.GetGuiRectangle(Left, Top, Width, Height); - return EllipseClickableAt(rect, lineThickness, fillColor, x, y); - } - - public static bool EllipseClickableAt(Rectangle rect, int lineThickness, Color fillColor, int x, int y) - { - // If we clicked inside the rectangle and it's visible we are clickable at. - if (!Color.Transparent.Equals(fillColor)) - { - if (rect.Contains(x, y)) - { - return true; - } - } - - // check the rest of the lines - if (lineThickness > 0) - { - using Pen pen = new Pen(Color.White, lineThickness); - using GraphicsPath path = new GraphicsPath(); - path.AddEllipse(rect); - return path.IsOutlineVisible(x, y, pen); - } - - return false; - } - } +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using System; +using System.Drawing; +using System.Drawing.Drawing2D; +using Greenshot.Base.Interfaces.Drawing; +using Greenshot.Editor.Drawing.Fields; +using Greenshot.Editor.Helpers; + +namespace Greenshot.Editor.Drawing +{ + /// + /// Description of EllipseContainer. + /// + [Serializable()] + public class EllipseContainer : DrawableContainer + { + public EllipseContainer(Surface parent) : base(parent) + { + CreateDefaultAdorners(); + } + + protected override void InitializeFields() + { + AddField(GetType(), FieldType.LINE_THICKNESS, 2); + AddField(GetType(), FieldType.LINE_COLOR, Color.Red); + AddField(GetType(), FieldType.FILL_COLOR, Color.Transparent); + AddField(GetType(), FieldType.SHADOW, true); + } + + public override void Draw(Graphics graphics, RenderMode renderMode) + { + graphics.SmoothingMode = SmoothingMode.HighQuality; + graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; + graphics.CompositingQuality = CompositingQuality.HighQuality; + graphics.PixelOffsetMode = PixelOffsetMode.None; + + int lineThickness = GetFieldValueAsInt(FieldType.LINE_THICKNESS); + Color lineColor = GetFieldValueAsColor(FieldType.LINE_COLOR); + Color fillColor = GetFieldValueAsColor(FieldType.FILL_COLOR); + bool shadow = GetFieldValueAsBool(FieldType.SHADOW); + Rectangle rect = GuiRectangle.GetGuiRectangle(Left, Top, Width, Height); + DrawEllipse(rect, graphics, renderMode, lineThickness, lineColor, fillColor, shadow); + } + + /// + /// This allows another container to draw an ellipse + /// + /// + /// + /// + /// + /// + /// + /// + public static void DrawEllipse(Rectangle rect, Graphics graphics, RenderMode renderMode, int lineThickness, Color lineColor, Color fillColor, bool shadow) + { + bool lineVisible = lineThickness > 0 && Colors.IsVisible(lineColor); + // draw shadow before anything else + if (shadow && (lineVisible || Colors.IsVisible(fillColor))) + { + int basealpha = 100; + int alpha = basealpha; + int steps = 5; + int currentStep = lineVisible ? 1 : 0; + while (currentStep <= steps) + { + using Pen shadowPen = new Pen(Color.FromArgb(alpha, 100, 100, 100)) + { + Width = lineVisible ? lineThickness : 1 + }; + Rectangle shadowRect = GuiRectangle.GetGuiRectangle(rect.Left + currentStep, rect.Top + currentStep, rect.Width, rect.Height); + graphics.DrawEllipse(shadowPen, shadowRect); + currentStep++; + alpha -= basealpha / steps; + } + } + + //draw the original shape + if (Colors.IsVisible(fillColor)) + { + using Brush brush = new SolidBrush(fillColor); + graphics.FillEllipse(brush, rect); + } + + if (lineVisible) + { + using Pen pen = new Pen(lineColor, lineThickness); + graphics.DrawEllipse(pen, rect); + } + } + + public override bool Contains(int x, int y) + { + return EllipseContains(this, x, y); + } + + /// + /// Allow the code to be used externally + /// + /// + /// + /// + /// + public static bool EllipseContains(DrawableContainer caller, int x, int y) + { + double xDistanceFromCenter = x - (caller.Left + caller.Width / 2); + double yDistanceFromCenter = y - (caller.Top + caller.Height / 2); + // ellipse: x^2/a^2 + y^2/b^2 = 1 + return Math.Pow(xDistanceFromCenter, 2) / Math.Pow(caller.Width / 2, 2) + Math.Pow(yDistanceFromCenter, 2) / Math.Pow(caller.Height / 2, 2) < 1; + } + + public override bool ClickableAt(int x, int y) + { + int lineThickness = GetFieldValueAsInt(FieldType.LINE_THICKNESS) + 10; + Color fillColor = GetFieldValueAsColor(FieldType.FILL_COLOR); + Rectangle rect = GuiRectangle.GetGuiRectangle(Left, Top, Width, Height); + return EllipseClickableAt(rect, lineThickness, fillColor, x, y); + } + + public static bool EllipseClickableAt(Rectangle rect, int lineThickness, Color fillColor, int x, int y) + { + // If we clicked inside the rectangle and it's visible we are clickable at. + if (!Color.Transparent.Equals(fillColor)) + { + if (rect.Contains(x, y)) + { + return true; + } + } + + // check the rest of the lines + if (lineThickness > 0) + { + using Pen pen = new Pen(Color.White, lineThickness); + using GraphicsPath path = new GraphicsPath(); + path.AddEllipse(rect); + return path.IsOutlineVisible(x, y, pen); + } + + return false; + } + } } \ No newline at end of file diff --git a/src/Greenshot/Drawing/Fields/AbstractFieldHolder.cs b/src/Greenshot.Editor/Drawing/Fields/AbstractFieldHolder.cs similarity index 96% rename from src/Greenshot/Drawing/Fields/AbstractFieldHolder.cs rename to src/Greenshot.Editor/Drawing/Fields/AbstractFieldHolder.cs index 0ca381ace..a3f3c92da 100644 --- a/src/Greenshot/Drawing/Fields/AbstractFieldHolder.cs +++ b/src/Greenshot.Editor/Drawing/Fields/AbstractFieldHolder.cs @@ -1,192 +1,192 @@ -/* - * Greenshot - a free and open source screenshot tool - * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom - * - * For more information see: https://getgreenshot.org/ - * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 1 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Drawing; -using System.Runtime.Serialization; -using Greenshot.Base.IniFile; -using Greenshot.Base.Interfaces.Drawing; -using Greenshot.Configuration; -using log4net; - -namespace Greenshot.Drawing.Fields -{ - /// - /// Basic IFieldHolder implementation, providing access to a set of fields - /// - [Serializable] - public abstract class AbstractFieldHolder : IFieldHolder - { - private static readonly ILog LOG = LogManager.GetLogger(typeof(AbstractFieldHolder)); - private static readonly EditorConfiguration EditorConfig = IniConfig.GetIniSection(); - [NonSerialized] private readonly IDictionary _handlers = new Dictionary(); - - /// - /// called when a field's value has changed - /// - [NonSerialized] private FieldChangedEventHandler _fieldChanged; - - public event FieldChangedEventHandler FieldChanged - { - add { _fieldChanged += value; } - remove { _fieldChanged -= value; } - } - - // we keep two Collections of our fields, dictionary for quick access, list for serialization - // this allows us to use default serialization - [NonSerialized] private IDictionary _fieldsByType = new Dictionary(); - private readonly IList fields = new List(); - - [OnDeserialized] - private void OnDeserialized(StreamingContext context) - { - _fieldsByType = new Dictionary(); - // listen to changing properties - foreach (var field in fields) - { - field.PropertyChanged += delegate { _fieldChanged?.Invoke(this, new FieldChangedEventArgs(field)); }; - _fieldsByType[field.FieldType] = field; - } - } - - public void AddField(Type requestingType, IFieldType fieldType, object fieldValue) - { - AddField(EditorConfig.CreateField(requestingType, fieldType, fieldValue)); - } - - public virtual void AddField(IField field) - { - fields.Add(field); - if (_fieldsByType == null) - { - return; - } - - if (_fieldsByType.ContainsKey(field.FieldType)) - { - if (LOG.IsDebugEnabled) - { - LOG.DebugFormat("A field with of type '{0}' already exists in this {1}, will overwrite.", field.FieldType, GetType()); - } - } - - _fieldsByType[field.FieldType] = field; - - _handlers[field] = (sender, args) => { _fieldChanged?.Invoke(this, new FieldChangedEventArgs(field)); }; - field.PropertyChanged += _handlers[field]; - } - - public void RemoveField(IField field) - { - fields.Remove(field); - _fieldsByType.Remove(field.FieldType); - field.PropertyChanged -= _handlers[field]; - _handlers.Remove(field); - } - - public IList GetFields() - { - return fields; - } - - - public IField GetField(IFieldType fieldType) - { - try - { - return _fieldsByType[fieldType]; - } - catch (KeyNotFoundException e) - { - throw new ArgumentException("Field '" + fieldType + "' does not exist in " + GetType(), e); - } - } - - public object GetFieldValue(IFieldType fieldType) - { - return GetField(fieldType)?.Value; - } - - public string GetFieldValueAsString(IFieldType fieldType) - { - return Convert.ToString(GetFieldValue(fieldType)); - } - - public int GetFieldValueAsInt(IFieldType fieldType) - { - return Convert.ToInt32(GetFieldValue(fieldType)); - } - - public decimal GetFieldValueAsDecimal(IFieldType fieldType) - { - return Convert.ToDecimal(GetFieldValue(fieldType)); - } - - public double GetFieldValueAsDouble(IFieldType fieldType) - { - return Convert.ToDouble(GetFieldValue(fieldType)); - } - - public float GetFieldValueAsFloat(IFieldType fieldType) - { - return Convert.ToSingle(GetFieldValue(fieldType)); - } - - public bool GetFieldValueAsBool(IFieldType fieldType) - { - return Convert.ToBoolean(GetFieldValue(fieldType)); - } - - public Color GetFieldValueAsColor(IFieldType fieldType, Color defaultColor = default) - { - return (Color) (GetFieldValue(fieldType) ?? defaultColor); - } - - public bool HasField(IFieldType fieldType) - { - return _fieldsByType.ContainsKey(fieldType); - } - - public bool HasFieldValue(IFieldType fieldType) - { - return HasField(fieldType) && _fieldsByType[fieldType].HasValue; - } - - public void SetFieldValue(IFieldType fieldType, object value) - { - try - { - _fieldsByType[fieldType].Value = value; - } - catch (KeyNotFoundException e) - { - throw new ArgumentException("Field '" + fieldType + "' does not exist in " + GetType(), e); - } - } - - protected void OnFieldChanged(object sender, FieldChangedEventArgs e) - { - _fieldChanged?.Invoke(sender, e); - } - } +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.Runtime.Serialization; +using Greenshot.Base.IniFile; +using Greenshot.Base.Interfaces.Drawing; +using Greenshot.Editor.Configuration; +using log4net; + +namespace Greenshot.Editor.Drawing.Fields +{ + /// + /// Basic IFieldHolder implementation, providing access to a set of fields + /// + [Serializable] + public abstract class AbstractFieldHolder : IFieldHolder + { + private static readonly ILog LOG = LogManager.GetLogger(typeof(AbstractFieldHolder)); + private static readonly EditorConfiguration EditorConfig = IniConfig.GetIniSection(); + [NonSerialized] private readonly IDictionary _handlers = new Dictionary(); + + /// + /// called when a field's value has changed + /// + [NonSerialized] private FieldChangedEventHandler _fieldChanged; + + public event FieldChangedEventHandler FieldChanged + { + add { _fieldChanged += value; } + remove { _fieldChanged -= value; } + } + + // we keep two Collections of our fields, dictionary for quick access, list for serialization + // this allows us to use default serialization + [NonSerialized] private IDictionary _fieldsByType = new Dictionary(); + private readonly IList fields = new List(); + + [OnDeserialized] + private void OnDeserialized(StreamingContext context) + { + _fieldsByType = new Dictionary(); + // listen to changing properties + foreach (var field in fields) + { + field.PropertyChanged += delegate { _fieldChanged?.Invoke(this, new FieldChangedEventArgs(field)); }; + _fieldsByType[field.FieldType] = field; + } + } + + public void AddField(Type requestingType, IFieldType fieldType, object fieldValue) + { + AddField(EditorConfig.CreateField(requestingType, fieldType, fieldValue)); + } + + public virtual void AddField(IField field) + { + fields.Add(field); + if (_fieldsByType == null) + { + return; + } + + if (_fieldsByType.ContainsKey(field.FieldType)) + { + if (LOG.IsDebugEnabled) + { + LOG.DebugFormat("A field with of type '{0}' already exists in this {1}, will overwrite.", field.FieldType, GetType()); + } + } + + _fieldsByType[field.FieldType] = field; + + _handlers[field] = (sender, args) => { _fieldChanged?.Invoke(this, new FieldChangedEventArgs(field)); }; + field.PropertyChanged += _handlers[field]; + } + + public void RemoveField(IField field) + { + fields.Remove(field); + _fieldsByType.Remove(field.FieldType); + field.PropertyChanged -= _handlers[field]; + _handlers.Remove(field); + } + + public IList GetFields() + { + return fields; + } + + + public IField GetField(IFieldType fieldType) + { + try + { + return _fieldsByType[fieldType]; + } + catch (KeyNotFoundException e) + { + throw new ArgumentException("Field '" + fieldType + "' does not exist in " + GetType(), e); + } + } + + public object GetFieldValue(IFieldType fieldType) + { + return GetField(fieldType)?.Value; + } + + public string GetFieldValueAsString(IFieldType fieldType) + { + return Convert.ToString(GetFieldValue(fieldType)); + } + + public int GetFieldValueAsInt(IFieldType fieldType) + { + return Convert.ToInt32(GetFieldValue(fieldType)); + } + + public decimal GetFieldValueAsDecimal(IFieldType fieldType) + { + return Convert.ToDecimal(GetFieldValue(fieldType)); + } + + public double GetFieldValueAsDouble(IFieldType fieldType) + { + return Convert.ToDouble(GetFieldValue(fieldType)); + } + + public float GetFieldValueAsFloat(IFieldType fieldType) + { + return Convert.ToSingle(GetFieldValue(fieldType)); + } + + public bool GetFieldValueAsBool(IFieldType fieldType) + { + return Convert.ToBoolean(GetFieldValue(fieldType)); + } + + public Color GetFieldValueAsColor(IFieldType fieldType, Color defaultColor = default) + { + return (Color) (GetFieldValue(fieldType) ?? defaultColor); + } + + public bool HasField(IFieldType fieldType) + { + return _fieldsByType.ContainsKey(fieldType); + } + + public bool HasFieldValue(IFieldType fieldType) + { + return HasField(fieldType) && _fieldsByType[fieldType].HasValue; + } + + public void SetFieldValue(IFieldType fieldType, object value) + { + try + { + _fieldsByType[fieldType].Value = value; + } + catch (KeyNotFoundException e) + { + throw new ArgumentException("Field '" + fieldType + "' does not exist in " + GetType(), e); + } + } + + protected void OnFieldChanged(object sender, FieldChangedEventArgs e) + { + _fieldChanged?.Invoke(sender, e); + } + } } \ No newline at end of file diff --git a/src/Greenshot/Drawing/Fields/AbstractFieldHolderWithChildren.cs b/src/Greenshot.Editor/Drawing/Fields/AbstractFieldHolderWithChildren.cs similarity index 96% rename from src/Greenshot/Drawing/Fields/AbstractFieldHolderWithChildren.cs rename to src/Greenshot.Editor/Drawing/Fields/AbstractFieldHolderWithChildren.cs index bd680d992..968edef46 100644 --- a/src/Greenshot/Drawing/Fields/AbstractFieldHolderWithChildren.cs +++ b/src/Greenshot.Editor/Drawing/Fields/AbstractFieldHolderWithChildren.cs @@ -1,149 +1,149 @@ -/* - * Greenshot - a free and open source screenshot tool - * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom - * - * For more information see: https://getgreenshot.org/ - * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 1 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -using System; -using System.Collections.Generic; -using System.Runtime.Serialization; -using Greenshot.Base.Interfaces.Drawing; - -namespace Greenshot.Drawing.Fields -{ - /// - /// Basic IFieldHolderWithChildren implementation. Similar to IFieldHolder, - /// but has a List of IFieldHolder for children. - /// Field values are passed to and from children as well. - /// - [Serializable] - public abstract class AbstractFieldHolderWithChildren : AbstractFieldHolder - { - [NonSerialized] private readonly FieldChangedEventHandler _fieldChangedEventHandler; - - [NonSerialized] private EventHandler childrenChanged; - - public event EventHandler ChildrenChanged - { - add { childrenChanged += value; } - remove { childrenChanged -= value; } - } - - public IList Children = new List(); - - public AbstractFieldHolderWithChildren() - { - _fieldChangedEventHandler = OnFieldChanged; - } - - [OnDeserialized()] - private void OnDeserialized(StreamingContext context) - { - // listen to changing properties - foreach (IFieldHolder fieldHolder in Children) - { - fieldHolder.FieldChanged += _fieldChangedEventHandler; - } - - childrenChanged?.Invoke(this, EventArgs.Empty); - } - - public void AddChild(IFieldHolder fieldHolder) - { - Children.Add(fieldHolder); - fieldHolder.FieldChanged += _fieldChangedEventHandler; - childrenChanged?.Invoke(this, EventArgs.Empty); - } - - public void RemoveChild(IFieldHolder fieldHolder) - { - Children.Remove(fieldHolder); - fieldHolder.FieldChanged -= _fieldChangedEventHandler; - childrenChanged?.Invoke(this, EventArgs.Empty); - } - - public new IList GetFields() - { - var ret = new List(); - ret.AddRange(base.GetFields()); - foreach (IFieldHolder fh in Children) - { - ret.AddRange(fh.GetFields()); - } - - return ret; - } - - public new IField GetField(IFieldType fieldType) - { - IField ret = null; - if (base.HasField(fieldType)) - { - ret = base.GetField(fieldType); - } - else - { - foreach (IFieldHolder fh in Children) - { - if (fh.HasField(fieldType)) - { - ret = fh.GetField(fieldType); - break; - } - } - } - - if (ret == null) - { - throw new ArgumentException("Field '" + fieldType + "' does not exist in " + GetType()); - } - - return ret; - } - - public new bool HasField(IFieldType fieldType) - { - bool ret = base.HasField(fieldType); - if (!ret) - { - foreach (IFieldHolder fh in Children) - { - if (fh.HasField(fieldType)) - { - ret = true; - break; - } - } - } - - return ret; - } - - public new bool HasFieldValue(IFieldType fieldType) - { - IField f = GetField(fieldType); - return f != null && f.HasValue; - } - - public new void SetFieldValue(IFieldType fieldType, object value) - { - IField f = GetField(fieldType); - if (f != null) f.Value = value; - } - } +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using System; +using System.Collections.Generic; +using System.Runtime.Serialization; +using Greenshot.Base.Interfaces.Drawing; + +namespace Greenshot.Editor.Drawing.Fields +{ + /// + /// Basic IFieldHolderWithChildren implementation. Similar to IFieldHolder, + /// but has a List of IFieldHolder for children. + /// Field values are passed to and from children as well. + /// + [Serializable] + public abstract class AbstractFieldHolderWithChildren : AbstractFieldHolder + { + [NonSerialized] private readonly FieldChangedEventHandler _fieldChangedEventHandler; + + [NonSerialized] private EventHandler childrenChanged; + + public event EventHandler ChildrenChanged + { + add { childrenChanged += value; } + remove { childrenChanged -= value; } + } + + public IList Children = new List(); + + public AbstractFieldHolderWithChildren() + { + _fieldChangedEventHandler = OnFieldChanged; + } + + [OnDeserialized()] + private void OnDeserialized(StreamingContext context) + { + // listen to changing properties + foreach (IFieldHolder fieldHolder in Children) + { + fieldHolder.FieldChanged += _fieldChangedEventHandler; + } + + childrenChanged?.Invoke(this, EventArgs.Empty); + } + + public void AddChild(IFieldHolder fieldHolder) + { + Children.Add(fieldHolder); + fieldHolder.FieldChanged += _fieldChangedEventHandler; + childrenChanged?.Invoke(this, EventArgs.Empty); + } + + public void RemoveChild(IFieldHolder fieldHolder) + { + Children.Remove(fieldHolder); + fieldHolder.FieldChanged -= _fieldChangedEventHandler; + childrenChanged?.Invoke(this, EventArgs.Empty); + } + + public new IList GetFields() + { + var ret = new List(); + ret.AddRange(base.GetFields()); + foreach (IFieldHolder fh in Children) + { + ret.AddRange(fh.GetFields()); + } + + return ret; + } + + public new IField GetField(IFieldType fieldType) + { + IField ret = null; + if (base.HasField(fieldType)) + { + ret = base.GetField(fieldType); + } + else + { + foreach (IFieldHolder fh in Children) + { + if (fh.HasField(fieldType)) + { + ret = fh.GetField(fieldType); + break; + } + } + } + + if (ret == null) + { + throw new ArgumentException("Field '" + fieldType + "' does not exist in " + GetType()); + } + + return ret; + } + + public new bool HasField(IFieldType fieldType) + { + bool ret = base.HasField(fieldType); + if (!ret) + { + foreach (IFieldHolder fh in Children) + { + if (fh.HasField(fieldType)) + { + ret = true; + break; + } + } + } + + return ret; + } + + public new bool HasFieldValue(IFieldType fieldType) + { + IField f = GetField(fieldType); + return f != null && f.HasValue; + } + + public new void SetFieldValue(IFieldType fieldType, object value) + { + IField f = GetField(fieldType); + if (f != null) f.Value = value; + } + } } \ No newline at end of file diff --git a/src/Greenshot/Drawing/Fields/Binding/AbstractBindingConverter.cs b/src/Greenshot.Editor/Drawing/Fields/Binding/AbstractBindingConverter.cs similarity index 94% rename from src/Greenshot/Drawing/Fields/Binding/AbstractBindingConverter.cs rename to src/Greenshot.Editor/Drawing/Fields/Binding/AbstractBindingConverter.cs index b4e568986..0a993f766 100644 --- a/src/Greenshot/Drawing/Fields/Binding/AbstractBindingConverter.cs +++ b/src/Greenshot.Editor/Drawing/Fields/Binding/AbstractBindingConverter.cs @@ -1,54 +1,54 @@ -/* - * Greenshot - a free and open source screenshot tool - * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom - * - * For more information see: https://getgreenshot.org/ - * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 1 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -using System; - -namespace Greenshot.Drawing.Fields.Binding -{ - /// - /// Basic IBindingConverter implementation - /// - public abstract class AbstractBindingConverter : IBindingConverter - { - public object convert(object o) - { - if (o == null) - { - return null; - } - - if (o is T1) - { - return convert((T1) o); - } - - if (o is T2) - { - return convert((T2) o); - } - - throw new ArgumentException("Cannot handle argument of type " + o.GetType()); - } - - protected abstract T2 convert(T1 o); - protected abstract T1 convert(T2 o); - } +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using System; + +namespace Greenshot.Editor.Drawing.Fields.Binding +{ + /// + /// Basic IBindingConverter implementation + /// + public abstract class AbstractBindingConverter : IBindingConverter + { + public object convert(object o) + { + if (o == null) + { + return null; + } + + if (o is T1) + { + return convert((T1) o); + } + + if (o is T2) + { + return convert((T2) o); + } + + throw new ArgumentException("Cannot handle argument of type " + o.GetType()); + } + + protected abstract T2 convert(T1 o); + protected abstract T1 convert(T2 o); + } } \ No newline at end of file diff --git a/src/Greenshot/Drawing/Fields/Binding/BidirectionalBinding.cs b/src/Greenshot.Editor/Drawing/Fields/Binding/BidirectionalBinding.cs similarity index 97% rename from src/Greenshot/Drawing/Fields/Binding/BidirectionalBinding.cs rename to src/Greenshot.Editor/Drawing/Fields/Binding/BidirectionalBinding.cs index 30cbbffa7..8dc390080 100644 --- a/src/Greenshot/Drawing/Fields/Binding/BidirectionalBinding.cs +++ b/src/Greenshot.Editor/Drawing/Fields/Binding/BidirectionalBinding.cs @@ -1,184 +1,184 @@ -/* - * Greenshot - a free and open source screenshot tool - * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom - * - * For more information see: https://getgreenshot.org/ - * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 1 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -using System; -using System.ComponentModel; -using System.Reflection; - -namespace Greenshot.Drawing.Fields.Binding -{ - /// - /// Bidirectional binding of properties of two INotifyPropertyChanged instances. - /// This implementation synchronizes null values, too. If you do not want this - /// behavior (e.g. when binding to a - /// - public class BidirectionalBinding - { - private readonly INotifyPropertyChanged _controlObject; - private readonly INotifyPropertyChanged _fieldObject; - private readonly string _controlPropertyName; - private readonly string _fieldPropertyName; - private bool _updatingControl; - private bool _updatingField; - private IBindingConverter _converter; - private readonly IBindingValidator _validator; - - /// - /// Whether or not null values are passed on to the other object. - /// - protected bool AllowSynchronizeNull = true; - - /// - /// Bind properties of two objects bidirectionally - /// - /// Object containing 1st property to bind - /// Property of 1st object to bind - /// Object containing 2nd property to bind - /// Property of 2nd object to bind - public BidirectionalBinding(INotifyPropertyChanged controlObject, string controlPropertyName, INotifyPropertyChanged fieldObject, string fieldPropertyName) - { - _controlObject = controlObject; - _fieldObject = fieldObject; - _controlPropertyName = controlPropertyName; - _fieldPropertyName = fieldPropertyName; - - _controlObject.PropertyChanged += ControlPropertyChanged; - _fieldObject.PropertyChanged += FieldPropertyChanged; - } - - /// - /// Bind properties of two objects bidirectionally, converting the values using a converter - /// - /// Object containing 1st property to bind - /// Property of 1st object to bind - /// Object containing 2nd property to bind - /// Property of 2nd object to bind - /// taking care of converting the synchronized value to the correct target format and back - public BidirectionalBinding(INotifyPropertyChanged controlObject, string controlPropertyName, INotifyPropertyChanged fieldObject, string fieldPropertyName, - IBindingConverter converter) : this(controlObject, controlPropertyName, fieldObject, fieldPropertyName) - { - _converter = converter; - } - - /// - /// Bind properties of two objects bidirectionally, converting the values using a converter. - /// Synchronization can be intercepted by adding a validator. - /// - /// Object containing 1st property to bind - /// Property of 1st object to bind - /// Object containing 2nd property to bind - /// Property of 2nd object to bind - /// validator to intercept synchronization if the value does not match certain criteria - public BidirectionalBinding(INotifyPropertyChanged controlObject, string controlPropertyName, INotifyPropertyChanged fieldObject, string fieldPropertyName, - IBindingValidator validator) : this(controlObject, controlPropertyName, fieldObject, fieldPropertyName) - { - _validator = validator; - } - - /// - /// Bind properties of two objects bidirectionally, converting the values using a converter. - /// Synchronization can be intercepted by adding a validator. - /// - /// Object containing 1st property to bind - /// Property of 1st object to bind - /// Object containing 2nd property to bind - /// Property of 2nd object to bind - /// taking care of converting the synchronized value to the correct target format and back - /// validator to intercept synchronization if the value does not match certain criteria - public BidirectionalBinding(INotifyPropertyChanged controlObject, string controlPropertyName, INotifyPropertyChanged fieldObject, string fieldPropertyName, - IBindingConverter converter, IBindingValidator validator) : this(controlObject, controlPropertyName, fieldObject, fieldPropertyName, converter) - { - _validator = validator; - } - - public void ControlPropertyChanged(object sender, PropertyChangedEventArgs e) - { - if (!_updatingControl && e.PropertyName.Equals(_controlPropertyName)) - { - _updatingField = true; - Synchronize(_controlObject, _controlPropertyName, _fieldObject, _fieldPropertyName); - _updatingField = false; - } - } - - public void FieldPropertyChanged(object sender, PropertyChangedEventArgs e) - { - if (!_updatingField && e.PropertyName.Equals(_fieldPropertyName)) - { - _updatingControl = true; - Synchronize(_fieldObject, _fieldPropertyName, _controlObject, _controlPropertyName); - _updatingControl = false; - } - } - - private void Synchronize(INotifyPropertyChanged sourceObject, string sourceProperty, INotifyPropertyChanged targetObject, string targetProperty) - { - PropertyInfo targetPropertyInfo = ResolvePropertyInfo(targetObject, targetProperty); - PropertyInfo sourcePropertyInfo = ResolvePropertyInfo(sourceObject, sourceProperty); - - if (sourcePropertyInfo != null && targetPropertyInfo != null && targetPropertyInfo.CanWrite) - { - object bValue = sourcePropertyInfo.GetValue(sourceObject, null); - if (_converter != null && bValue != null) - { - bValue = _converter.convert(bValue); - } - - try - { - if (_validator == null || _validator.validate(bValue)) - { - targetPropertyInfo.SetValue(targetObject, bValue, null); - } - } - catch (Exception e) - { - throw new MemberAccessException( - "Could not set property '" + targetProperty + "' to '" + bValue + "' [" + (bValue?.GetType().Name ?? string.Empty) + "] on " + targetObject + - ". Probably other type than expected, IBindingCoverter to the rescue.", e); - } - } - } - - private static PropertyInfo ResolvePropertyInfo(object obj, string property) - { - PropertyInfo ret = null; - string[] properties = property.Split(".".ToCharArray()); - for (int i = 0; i < properties.Length; i++) - { - string prop = properties[i]; - ret = obj.GetType().GetProperty(prop); - if (ret != null && ret.CanRead && i < prop.Length - 1) - { - obj = ret.GetValue(obj, null); - } - } - - return ret; - } - - public IBindingConverter Converter - { - get { return _converter; } - set { _converter = value; } - } - } +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using System; +using System.ComponentModel; +using System.Reflection; + +namespace Greenshot.Editor.Drawing.Fields.Binding +{ + /// + /// Bidirectional binding of properties of two INotifyPropertyChanged instances. + /// This implementation synchronizes null values, too. If you do not want this + /// behavior (e.g. when binding to a + /// + public class BidirectionalBinding + { + private readonly INotifyPropertyChanged _controlObject; + private readonly INotifyPropertyChanged _fieldObject; + private readonly string _controlPropertyName; + private readonly string _fieldPropertyName; + private bool _updatingControl; + private bool _updatingField; + private IBindingConverter _converter; + private readonly IBindingValidator _validator; + + /// + /// Whether or not null values are passed on to the other object. + /// + protected bool AllowSynchronizeNull = true; + + /// + /// Bind properties of two objects bidirectionally + /// + /// Object containing 1st property to bind + /// Property of 1st object to bind + /// Object containing 2nd property to bind + /// Property of 2nd object to bind + public BidirectionalBinding(INotifyPropertyChanged controlObject, string controlPropertyName, INotifyPropertyChanged fieldObject, string fieldPropertyName) + { + _controlObject = controlObject; + _fieldObject = fieldObject; + _controlPropertyName = controlPropertyName; + _fieldPropertyName = fieldPropertyName; + + _controlObject.PropertyChanged += ControlPropertyChanged; + _fieldObject.PropertyChanged += FieldPropertyChanged; + } + + /// + /// Bind properties of two objects bidirectionally, converting the values using a converter + /// + /// Object containing 1st property to bind + /// Property of 1st object to bind + /// Object containing 2nd property to bind + /// Property of 2nd object to bind + /// taking care of converting the synchronized value to the correct target format and back + public BidirectionalBinding(INotifyPropertyChanged controlObject, string controlPropertyName, INotifyPropertyChanged fieldObject, string fieldPropertyName, + IBindingConverter converter) : this(controlObject, controlPropertyName, fieldObject, fieldPropertyName) + { + _converter = converter; + } + + /// + /// Bind properties of two objects bidirectionally, converting the values using a converter. + /// Synchronization can be intercepted by adding a validator. + /// + /// Object containing 1st property to bind + /// Property of 1st object to bind + /// Object containing 2nd property to bind + /// Property of 2nd object to bind + /// validator to intercept synchronization if the value does not match certain criteria + public BidirectionalBinding(INotifyPropertyChanged controlObject, string controlPropertyName, INotifyPropertyChanged fieldObject, string fieldPropertyName, + IBindingValidator validator) : this(controlObject, controlPropertyName, fieldObject, fieldPropertyName) + { + _validator = validator; + } + + /// + /// Bind properties of two objects bidirectionally, converting the values using a converter. + /// Synchronization can be intercepted by adding a validator. + /// + /// Object containing 1st property to bind + /// Property of 1st object to bind + /// Object containing 2nd property to bind + /// Property of 2nd object to bind + /// taking care of converting the synchronized value to the correct target format and back + /// validator to intercept synchronization if the value does not match certain criteria + public BidirectionalBinding(INotifyPropertyChanged controlObject, string controlPropertyName, INotifyPropertyChanged fieldObject, string fieldPropertyName, + IBindingConverter converter, IBindingValidator validator) : this(controlObject, controlPropertyName, fieldObject, fieldPropertyName, converter) + { + _validator = validator; + } + + public void ControlPropertyChanged(object sender, PropertyChangedEventArgs e) + { + if (!_updatingControl && e.PropertyName.Equals(_controlPropertyName)) + { + _updatingField = true; + Synchronize(_controlObject, _controlPropertyName, _fieldObject, _fieldPropertyName); + _updatingField = false; + } + } + + public void FieldPropertyChanged(object sender, PropertyChangedEventArgs e) + { + if (!_updatingField && e.PropertyName.Equals(_fieldPropertyName)) + { + _updatingControl = true; + Synchronize(_fieldObject, _fieldPropertyName, _controlObject, _controlPropertyName); + _updatingControl = false; + } + } + + private void Synchronize(INotifyPropertyChanged sourceObject, string sourceProperty, INotifyPropertyChanged targetObject, string targetProperty) + { + PropertyInfo targetPropertyInfo = ResolvePropertyInfo(targetObject, targetProperty); + PropertyInfo sourcePropertyInfo = ResolvePropertyInfo(sourceObject, sourceProperty); + + if (sourcePropertyInfo != null && targetPropertyInfo != null && targetPropertyInfo.CanWrite) + { + object bValue = sourcePropertyInfo.GetValue(sourceObject, null); + if (_converter != null && bValue != null) + { + bValue = _converter.convert(bValue); + } + + try + { + if (_validator == null || _validator.validate(bValue)) + { + targetPropertyInfo.SetValue(targetObject, bValue, null); + } + } + catch (Exception e) + { + throw new MemberAccessException( + "Could not set property '" + targetProperty + "' to '" + bValue + "' [" + (bValue?.GetType().Name ?? string.Empty) + "] on " + targetObject + + ". Probably other type than expected, IBindingCoverter to the rescue.", e); + } + } + } + + private static PropertyInfo ResolvePropertyInfo(object obj, string property) + { + PropertyInfo ret = null; + string[] properties = property.Split(".".ToCharArray()); + for (int i = 0; i < properties.Length; i++) + { + string prop = properties[i]; + ret = obj.GetType().GetProperty(prop); + if (ret != null && ret.CanRead && i < prop.Length - 1) + { + obj = ret.GetValue(obj, null); + } + } + + return ret; + } + + public IBindingConverter Converter + { + get { return _converter; } + set { _converter = value; } + } + } } \ No newline at end of file diff --git a/src/Greenshot/Drawing/Fields/Binding/DecimalDoublePercentageConverter.cs b/src/Greenshot.Editor/Drawing/Fields/Binding/DecimalDoublePercentageConverter.cs similarity index 94% rename from src/Greenshot/Drawing/Fields/Binding/DecimalDoublePercentageConverter.cs rename to src/Greenshot.Editor/Drawing/Fields/Binding/DecimalDoublePercentageConverter.cs index 6ce61c14d..0e8a35ddf 100644 --- a/src/Greenshot/Drawing/Fields/Binding/DecimalDoublePercentageConverter.cs +++ b/src/Greenshot.Editor/Drawing/Fields/Binding/DecimalDoublePercentageConverter.cs @@ -1,52 +1,52 @@ -/* - * Greenshot - a free and open source screenshot tool - * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom - * - * For more information see: https://getgreenshot.org/ - * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 1 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -using System; - -namespace Greenshot.Drawing.Fields.Binding -{ - /// - /// Converts decimal to double (%) and vice versa, e.g. 95f to 0.95d - /// - public class DecimalDoublePercentageConverter : AbstractBindingConverter - { - private static DecimalDoublePercentageConverter _uniqueInstance; - - private DecimalDoublePercentageConverter() - { - } - - protected override decimal convert(double o) - { - return Convert.ToDecimal(o) * 100; - } - - protected override double convert(decimal o) - { - return Convert.ToDouble(o) / 100; - } - - public static DecimalDoublePercentageConverter GetInstance() - { - return _uniqueInstance ??= new DecimalDoublePercentageConverter(); - } - } +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using System; + +namespace Greenshot.Editor.Drawing.Fields.Binding +{ + /// + /// Converts decimal to double (%) and vice versa, e.g. 95f to 0.95d + /// + public class DecimalDoublePercentageConverter : AbstractBindingConverter + { + private static DecimalDoublePercentageConverter _uniqueInstance; + + private DecimalDoublePercentageConverter() + { + } + + protected override decimal convert(double o) + { + return Convert.ToDecimal(o) * 100; + } + + protected override double convert(decimal o) + { + return Convert.ToDouble(o) / 100; + } + + public static DecimalDoublePercentageConverter GetInstance() + { + return _uniqueInstance ??= new DecimalDoublePercentageConverter(); + } + } } \ No newline at end of file diff --git a/src/Greenshot/Drawing/Fields/Binding/DecimalFloatConverter.cs b/src/Greenshot.Editor/Drawing/Fields/Binding/DecimalFloatConverter.cs similarity index 94% rename from src/Greenshot/Drawing/Fields/Binding/DecimalFloatConverter.cs rename to src/Greenshot.Editor/Drawing/Fields/Binding/DecimalFloatConverter.cs index 06721ae39..1f8e7a182 100644 --- a/src/Greenshot/Drawing/Fields/Binding/DecimalFloatConverter.cs +++ b/src/Greenshot.Editor/Drawing/Fields/Binding/DecimalFloatConverter.cs @@ -1,52 +1,52 @@ -/* - * Greenshot - a free and open source screenshot tool - * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom - * - * For more information see: https://getgreenshot.org/ - * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 1 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -using System; - -namespace Greenshot.Drawing.Fields.Binding -{ - /// - /// Converts decimal to float and vice versa. - /// - public class DecimalFloatConverter : AbstractBindingConverter - { - private static DecimalFloatConverter _uniqueInstance; - - private DecimalFloatConverter() - { - } - - protected override decimal convert(float o) - { - return Convert.ToDecimal(o); - } - - protected override float convert(decimal o) - { - return Convert.ToSingle(o); - } - - public static DecimalFloatConverter GetInstance() - { - return _uniqueInstance ??= new DecimalFloatConverter(); - } - } +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using System; + +namespace Greenshot.Editor.Drawing.Fields.Binding +{ + /// + /// Converts decimal to float and vice versa. + /// + public class DecimalFloatConverter : AbstractBindingConverter + { + private static DecimalFloatConverter _uniqueInstance; + + private DecimalFloatConverter() + { + } + + protected override decimal convert(float o) + { + return Convert.ToDecimal(o); + } + + protected override float convert(decimal o) + { + return Convert.ToSingle(o); + } + + public static DecimalFloatConverter GetInstance() + { + return _uniqueInstance ??= new DecimalFloatConverter(); + } + } } \ No newline at end of file diff --git a/src/Greenshot/Drawing/Fields/Binding/DecimalIntConverter.cs b/src/Greenshot.Editor/Drawing/Fields/Binding/DecimalIntConverter.cs similarity index 94% rename from src/Greenshot/Drawing/Fields/Binding/DecimalIntConverter.cs rename to src/Greenshot.Editor/Drawing/Fields/Binding/DecimalIntConverter.cs index 5986346b4..882ceaac9 100644 --- a/src/Greenshot/Drawing/Fields/Binding/DecimalIntConverter.cs +++ b/src/Greenshot.Editor/Drawing/Fields/Binding/DecimalIntConverter.cs @@ -1,52 +1,52 @@ -/* - * Greenshot - a free and open source screenshot tool - * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom - * - * For more information see: https://getgreenshot.org/ - * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 1 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -using System; - -namespace Greenshot.Drawing.Fields.Binding -{ - /// - /// Converts decimal to int and vice versa. - /// - public class DecimalIntConverter : AbstractBindingConverter - { - private static DecimalIntConverter _uniqueInstance; - - private DecimalIntConverter() - { - } - - protected override decimal convert(int o) - { - return Convert.ToDecimal(o); - } - - protected override int convert(decimal o) - { - return Convert.ToInt32(o); - } - - public static DecimalIntConverter GetInstance() - { - return _uniqueInstance ??= new DecimalIntConverter(); - } - } +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using System; + +namespace Greenshot.Editor.Drawing.Fields.Binding +{ + /// + /// Converts decimal to int and vice versa. + /// + public class DecimalIntConverter : AbstractBindingConverter + { + private static DecimalIntConverter _uniqueInstance; + + private DecimalIntConverter() + { + } + + protected override decimal convert(int o) + { + return Convert.ToDecimal(o); + } + + protected override int convert(decimal o) + { + return Convert.ToInt32(o); + } + + public static DecimalIntConverter GetInstance() + { + return _uniqueInstance ??= new DecimalIntConverter(); + } + } } \ No newline at end of file diff --git a/src/Greenshot/Drawing/Fields/Binding/IBindingConverter.cs b/src/Greenshot.Editor/Drawing/Fields/Binding/IBindingConverter.cs similarity index 94% rename from src/Greenshot/Drawing/Fields/Binding/IBindingConverter.cs rename to src/Greenshot.Editor/Drawing/Fields/Binding/IBindingConverter.cs index f9e1f0ffd..b5f5bb621 100644 --- a/src/Greenshot/Drawing/Fields/Binding/IBindingConverter.cs +++ b/src/Greenshot.Editor/Drawing/Fields/Binding/IBindingConverter.cs @@ -1,33 +1,33 @@ -/* - * Greenshot - a free and open source screenshot tool - * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom - * - * For more information see: https://getgreenshot.org/ - * The Greenshot project is hosted on GitHub https://github.com/greenshot/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 . - */ - -namespace Greenshot.Drawing.Fields.Binding -{ - /// - /// Interface for a bidirectional converter, for use with BidirectionalBinding. - /// convert(object) implementation must deal with both directions. - /// see DecimalIntConverter - /// - public interface IBindingConverter - { - object convert(object o); - } +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/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 . + */ + +namespace Greenshot.Editor.Drawing.Fields.Binding +{ + /// + /// Interface for a bidirectional converter, for use with BidirectionalBinding. + /// convert(object) implementation must deal with both directions. + /// see DecimalIntConverter + /// + public interface IBindingConverter + { + object convert(object o); + } } \ No newline at end of file diff --git a/src/Greenshot/Drawing/Fields/Binding/IBindingValidator.cs b/src/Greenshot.Editor/Drawing/Fields/Binding/IBindingValidator.cs similarity index 94% rename from src/Greenshot/Drawing/Fields/Binding/IBindingValidator.cs rename to src/Greenshot.Editor/Drawing/Fields/Binding/IBindingValidator.cs index 3981f0c17..6e53a7ea6 100644 --- a/src/Greenshot/Drawing/Fields/Binding/IBindingValidator.cs +++ b/src/Greenshot.Editor/Drawing/Fields/Binding/IBindingValidator.cs @@ -1,34 +1,34 @@ -/* - * Greenshot - a free and open source screenshot tool - * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom - * - * For more information see: https://getgreenshot.org/ - * The Greenshot project is hosted on GitHub https://github.com/greenshot/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 . - */ - -namespace Greenshot.Drawing.Fields.Binding -{ - /// - /// Interface for a bidirectional validator, for use with BidirectionalBinding. - /// Useful if you do not want to synchronize values which would be illegal on - /// one of the bound objects (e.g. null value on some form components) - /// see NotNullValidator - /// - public interface IBindingValidator - { - bool validate(object o); - } +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/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 . + */ + +namespace Greenshot.Editor.Drawing.Fields.Binding +{ + /// + /// Interface for a bidirectional validator, for use with BidirectionalBinding. + /// Useful if you do not want to synchronize values which would be illegal on + /// one of the bound objects (e.g. null value on some form components) + /// see NotNullValidator + /// + public interface IBindingValidator + { + bool validate(object o); + } } \ No newline at end of file diff --git a/src/Greenshot/Drawing/Fields/Binding/NotNullValidator.cs b/src/Greenshot.Editor/Drawing/Fields/Binding/NotNullValidator.cs similarity index 94% rename from src/Greenshot/Drawing/Fields/Binding/NotNullValidator.cs rename to src/Greenshot.Editor/Drawing/Fields/Binding/NotNullValidator.cs index 09cc898a9..426a560ee 100644 --- a/src/Greenshot/Drawing/Fields/Binding/NotNullValidator.cs +++ b/src/Greenshot.Editor/Drawing/Fields/Binding/NotNullValidator.cs @@ -1,45 +1,45 @@ -/* - * Greenshot - a free and open source screenshot tool - * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom - * - * For more information see: https://getgreenshot.org/ - * The Greenshot project is hosted on GitHub https://github.com/greenshot/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 . - */ - -namespace Greenshot.Drawing.Fields.Binding -{ - /// - /// Validates a value not to be null. - /// - public class NotNullValidator : IBindingValidator - { - private static NotNullValidator _uniqueInstance; - - private NotNullValidator() - { - } - - public bool validate(object o) - { - return o != null; - } - - public static NotNullValidator GetInstance() - { - return _uniqueInstance ??= new NotNullValidator(); - } - } +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/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 . + */ + +namespace Greenshot.Editor.Drawing.Fields.Binding +{ + /// + /// Validates a value not to be null. + /// + public class NotNullValidator : IBindingValidator + { + private static NotNullValidator _uniqueInstance; + + private NotNullValidator() + { + } + + public bool validate(object o) + { + return o != null; + } + + public static NotNullValidator GetInstance() + { + return _uniqueInstance ??= new NotNullValidator(); + } + } } \ No newline at end of file diff --git a/src/Greenshot/Drawing/Fields/Field.cs b/src/Greenshot.Editor/Drawing/Fields/Field.cs similarity index 96% rename from src/Greenshot/Drawing/Fields/Field.cs rename to src/Greenshot.Editor/Drawing/Fields/Field.cs index b06efa77a..c3870c54c 100644 --- a/src/Greenshot/Drawing/Fields/Field.cs +++ b/src/Greenshot.Editor/Drawing/Fields/Field.cs @@ -1,128 +1,128 @@ -/* - * Greenshot - a free and open source screenshot tool - * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom - * - * For more information see: https://getgreenshot.org/ - * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 1 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -using System; -using System.ComponentModel; -using Greenshot.Base.Interfaces.Drawing; - -namespace Greenshot.Drawing.Fields -{ - /// - /// Represents a single field of a drawable element, i.e. - /// line thickness of a rectangle. - /// - [Serializable] - public class Field : IField - { - [field: NonSerialized] public event PropertyChangedEventHandler PropertyChanged; - - private object _myValue; - - public object Value - { - get { return _myValue; } - set - { - if (!Equals(_myValue, value)) - { - _myValue = value; - PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Value")); - } - } - } - - public IFieldType FieldType { get; set; } - public string Scope { get; set; } - - /// - /// Constructs a new Field instance, usually you should be using FieldFactory - /// to create Fields. - /// - /// FieldType of the Field to be created - /// The scope to which the value of this Field is relevant. - /// Depending on the scope the Field's value may be shared for other elements - /// containing the same FieldType for defaulting to the last used value. - /// When scope is set to a Type (e.g. typeof(RectangleContainer)), its value - /// should not be reused for FieldHolders of another Type (e.g. typeof(EllipseContainer)) - /// - public Field(IFieldType fieldType, Type scope) - { - FieldType = fieldType; - Scope = scope.Name; - } - - public Field(IFieldType fieldType, string scope) - { - FieldType = fieldType; - Scope = scope; - } - - public Field(IFieldType fieldType) - { - FieldType = fieldType; - } - - /// - /// Returns true if this field holds a value other than null. - /// - public bool HasValue => Value != null; - - /// - /// Creates a flat clone of this Field. The fields value itself is not cloned. - /// - /// - public Field Clone() - { - return new Field(FieldType, Scope) - { - Value = Value - }; - } - - public override int GetHashCode() - { - int hashCode = 0; - unchecked - { - hashCode += 1000000009 * FieldType.GetHashCode(); - if (Scope != null) - hashCode += 1000000021 * Scope.GetHashCode(); - } - - return hashCode; - } - - public override bool Equals(object obj) - { - if (!(obj is Field other)) - { - return false; - } - - return FieldType == other.FieldType && Equals(Scope, other.Scope); - } - - public override string ToString() - { - return string.Format("[Field FieldType={1} Value={0} Scope={2}]", _myValue, FieldType, Scope); - } - } +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using System; +using System.ComponentModel; +using Greenshot.Base.Interfaces.Drawing; + +namespace Greenshot.Editor.Drawing.Fields +{ + /// + /// Represents a single field of a drawable element, i.e. + /// line thickness of a rectangle. + /// + [Serializable] + public class Field : IField + { + [field: NonSerialized] public event PropertyChangedEventHandler PropertyChanged; + + private object _myValue; + + public object Value + { + get { return _myValue; } + set + { + if (!Equals(_myValue, value)) + { + _myValue = value; + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Value")); + } + } + } + + public IFieldType FieldType { get; set; } + public string Scope { get; set; } + + /// + /// Constructs a new Field instance, usually you should be using FieldFactory + /// to create Fields. + /// + /// FieldType of the Field to be created + /// The scope to which the value of this Field is relevant. + /// Depending on the scope the Field's value may be shared for other elements + /// containing the same FieldType for defaulting to the last used value. + /// When scope is set to a Type (e.g. typeof(RectangleContainer)), its value + /// should not be reused for FieldHolders of another Type (e.g. typeof(EllipseContainer)) + /// + public Field(IFieldType fieldType, Type scope) + { + FieldType = fieldType; + Scope = scope.Name; + } + + public Field(IFieldType fieldType, string scope) + { + FieldType = fieldType; + Scope = scope; + } + + public Field(IFieldType fieldType) + { + FieldType = fieldType; + } + + /// + /// Returns true if this field holds a value other than null. + /// + public bool HasValue => Value != null; + + /// + /// Creates a flat clone of this Field. The fields value itself is not cloned. + /// + /// + public Field Clone() + { + return new Field(FieldType, Scope) + { + Value = Value + }; + } + + public override int GetHashCode() + { + int hashCode = 0; + unchecked + { + hashCode += 1000000009 * FieldType.GetHashCode(); + if (Scope != null) + hashCode += 1000000021 * Scope.GetHashCode(); + } + + return hashCode; + } + + public override bool Equals(object obj) + { + if (!(obj is Field other)) + { + return false; + } + + return FieldType == other.FieldType && Equals(Scope, other.Scope); + } + + public override string ToString() + { + return string.Format("[Field FieldType={1} Value={0} Scope={2}]", _myValue, FieldType, Scope); + } + } } \ No newline at end of file diff --git a/src/Greenshot/Drawing/Fields/FieldAggregator.cs b/src/Greenshot.Editor/Drawing/Fields/FieldAggregator.cs similarity index 96% rename from src/Greenshot/Drawing/Fields/FieldAggregator.cs rename to src/Greenshot.Editor/Drawing/Fields/FieldAggregator.cs index ee3c79d00..2df16d46f 100644 --- a/src/Greenshot/Drawing/Fields/FieldAggregator.cs +++ b/src/Greenshot.Editor/Drawing/Fields/FieldAggregator.cs @@ -1,224 +1,224 @@ -/* - * Greenshot - a free and open source screenshot tool - * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom - * - * For more information see: https://getgreenshot.org/ - * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 1 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -using System; -using Greenshot.Configuration; -using System.Collections.Generic; -using System.ComponentModel; -using System.Linq; -using Greenshot.Base.IniFile; -using Greenshot.Base.Interfaces; -using Greenshot.Base.Interfaces.Drawing; - -namespace Greenshot.Drawing.Fields -{ - /// - /// Represents the current set of properties for the editor. - /// When one of EditorProperties' properties is updated, the change will be promoted - /// to all bound elements. - /// * If an element is selected: - /// This class represents the element's properties - /// * I n>1 elements are selected: - /// This class represents the properties of all elements. - /// Properties that do not apply for ALL selected elements are null (or 0 respectively) - /// If the property values of the selected elements differ, the value of the last bound element wins. - /// - [Serializable] - public sealed class FieldAggregator : AbstractFieldHolder - { - private readonly IDrawableContainerList _boundContainers; - private bool _internalUpdateRunning; - - private static readonly EditorConfiguration EditorConfig = IniConfig.GetIniSection(); - - public FieldAggregator(ISurface parent) - { - foreach (var fieldType in FieldType.Values) - { - var field = new Field(fieldType, GetType()); - AddField(field); - } - - _boundContainers = new DrawableContainerList - { - Parent = parent - }; - } - - public override void AddField(IField field) - { - base.AddField(field); - field.PropertyChanged += OwnPropertyChanged; - } - - public void BindElements(IDrawableContainerList dcs) - { - foreach (var dc in dcs) - { - BindElement(dc); - } - } - - public void BindElement(IDrawableContainer dc) - { - if (!(dc is DrawableContainer container) || _boundContainers.Contains(container)) - { - return; - } - - _boundContainers.Add(container); - container.ChildrenChanged += delegate { UpdateFromBoundElements(); }; - UpdateFromBoundElements(); - } - - public void BindAndUpdateElement(IDrawableContainer dc) - { - UpdateElement(dc); - BindElement(dc); - } - - public void UpdateElement(IDrawableContainer dc) - { - if (!(dc is DrawableContainer container)) - { - return; - } - - _internalUpdateRunning = true; - foreach (var field in GetFields()) - { - if (container.HasField(field.FieldType) && field.HasValue) - { - //if(LOG.IsDebugEnabled) LOG.Debug(" "+field+ ": "+field.Value); - container.SetFieldValue(field.FieldType, field.Value); - } - } - - _internalUpdateRunning = false; - } - - public void UnbindElement(IDrawableContainer dc) - { - if (_boundContainers.Contains(dc)) - { - _boundContainers.Remove(dc); - UpdateFromBoundElements(); - } - } - - public void Clear() - { - ClearFields(); - _boundContainers.Clear(); - UpdateFromBoundElements(); - } - - /// - /// sets all field values to null, however does not remove fields - /// - private void ClearFields() - { - _internalUpdateRunning = true; - foreach (var field in GetFields()) - { - field.Value = null; - } - - _internalUpdateRunning = false; - } - - /// - /// Updates this instance using the respective fields from the bound elements. - /// Fields that do not apply to every bound element are set to null, or 0 respectively. - /// All other fields will be set to the field value of the least bound element. - /// - private void UpdateFromBoundElements() - { - ClearFields(); - _internalUpdateRunning = true; - foreach (var field in FindCommonFields()) - { - SetFieldValue(field.FieldType, field.Value); - } - - _internalUpdateRunning = false; - } - - private IList FindCommonFields() - { - IList returnFields = null; - if (_boundContainers.Count > 0) - { - // take all fields from the least selected container... - if (_boundContainers[_boundContainers.Count - 1] is DrawableContainer leastSelectedContainer) - { - returnFields = leastSelectedContainer.GetFields(); - for (int i = 0; i < _boundContainers.Count - 1; i++) - { - if (!(_boundContainers[i] is DrawableContainer dc)) continue; - IList fieldsToRemove = new List(); - foreach (IField field in returnFields) - { - // ... throw out those that do not apply to one of the other containers - if (!dc.HasField(field.FieldType)) - { - fieldsToRemove.Add(field); - } - } - - foreach (var field in fieldsToRemove) - { - returnFields.Remove(field); - } - } - } - } - - return returnFields ?? new List(); - } - - public void OwnPropertyChanged(object sender, PropertyChangedEventArgs ea) - { - IField field = (IField) sender; - if (_internalUpdateRunning || field.Value == null) - { - return; - } - - foreach (var drawableContainer1 in _boundContainers.ToList()) - { - var drawableContainer = (DrawableContainer) drawableContainer1; - if (!drawableContainer.HasField(field.FieldType)) - { - continue; - } - - IField drawableContainerField = drawableContainer.GetField(field.FieldType); - // Notify before change, so we can e.g. invalidate the area - drawableContainer.BeforeFieldChange(drawableContainerField, field.Value); - - drawableContainerField.Value = field.Value; - // update last used from DC field, so that scope is honored - EditorConfig.UpdateLastFieldValue(drawableContainerField); - } - } - } +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using Greenshot.Base.IniFile; +using Greenshot.Base.Interfaces; +using Greenshot.Base.Interfaces.Drawing; +using Greenshot.Editor.Configuration; + +namespace Greenshot.Editor.Drawing.Fields +{ + /// + /// Represents the current set of properties for the editor. + /// When one of EditorProperties' properties is updated, the change will be promoted + /// to all bound elements. + /// * If an element is selected: + /// This class represents the element's properties + /// * I n>1 elements are selected: + /// This class represents the properties of all elements. + /// Properties that do not apply for ALL selected elements are null (or 0 respectively) + /// If the property values of the selected elements differ, the value of the last bound element wins. + /// + [Serializable] + public sealed class FieldAggregator : AbstractFieldHolder + { + private readonly IDrawableContainerList _boundContainers; + private bool _internalUpdateRunning; + + private static readonly EditorConfiguration EditorConfig = IniConfig.GetIniSection(); + + public FieldAggregator(ISurface parent) + { + foreach (var fieldType in FieldType.Values) + { + var field = new Field(fieldType, GetType()); + AddField(field); + } + + _boundContainers = new DrawableContainerList + { + Parent = parent + }; + } + + public override void AddField(IField field) + { + base.AddField(field); + field.PropertyChanged += OwnPropertyChanged; + } + + public void BindElements(IDrawableContainerList dcs) + { + foreach (var dc in dcs) + { + BindElement(dc); + } + } + + public void BindElement(IDrawableContainer dc) + { + if (!(dc is DrawableContainer container) || _boundContainers.Contains(container)) + { + return; + } + + _boundContainers.Add(container); + container.ChildrenChanged += delegate { UpdateFromBoundElements(); }; + UpdateFromBoundElements(); + } + + public void BindAndUpdateElement(IDrawableContainer dc) + { + UpdateElement(dc); + BindElement(dc); + } + + public void UpdateElement(IDrawableContainer dc) + { + if (!(dc is DrawableContainer container)) + { + return; + } + + _internalUpdateRunning = true; + foreach (var field in GetFields()) + { + if (container.HasField(field.FieldType) && field.HasValue) + { + //if(LOG.IsDebugEnabled) LOG.Debug(" "+field+ ": "+field.Value); + container.SetFieldValue(field.FieldType, field.Value); + } + } + + _internalUpdateRunning = false; + } + + public void UnbindElement(IDrawableContainer dc) + { + if (_boundContainers.Contains(dc)) + { + _boundContainers.Remove(dc); + UpdateFromBoundElements(); + } + } + + public void Clear() + { + ClearFields(); + _boundContainers.Clear(); + UpdateFromBoundElements(); + } + + /// + /// sets all field values to null, however does not remove fields + /// + private void ClearFields() + { + _internalUpdateRunning = true; + foreach (var field in GetFields()) + { + field.Value = null; + } + + _internalUpdateRunning = false; + } + + /// + /// Updates this instance using the respective fields from the bound elements. + /// Fields that do not apply to every bound element are set to null, or 0 respectively. + /// All other fields will be set to the field value of the least bound element. + /// + private void UpdateFromBoundElements() + { + ClearFields(); + _internalUpdateRunning = true; + foreach (var field in FindCommonFields()) + { + SetFieldValue(field.FieldType, field.Value); + } + + _internalUpdateRunning = false; + } + + private IList FindCommonFields() + { + IList returnFields = null; + if (_boundContainers.Count > 0) + { + // take all fields from the least selected container... + if (_boundContainers[_boundContainers.Count - 1] is DrawableContainer leastSelectedContainer) + { + returnFields = leastSelectedContainer.GetFields(); + for (int i = 0; i < _boundContainers.Count - 1; i++) + { + if (!(_boundContainers[i] is DrawableContainer dc)) continue; + IList fieldsToRemove = new List(); + foreach (IField field in returnFields) + { + // ... throw out those that do not apply to one of the other containers + if (!dc.HasField(field.FieldType)) + { + fieldsToRemove.Add(field); + } + } + + foreach (var field in fieldsToRemove) + { + returnFields.Remove(field); + } + } + } + } + + return returnFields ?? new List(); + } + + public void OwnPropertyChanged(object sender, PropertyChangedEventArgs ea) + { + IField field = (IField) sender; + if (_internalUpdateRunning || field.Value == null) + { + return; + } + + foreach (var drawableContainer1 in _boundContainers.ToList()) + { + var drawableContainer = (DrawableContainer) drawableContainer1; + if (!drawableContainer.HasField(field.FieldType)) + { + continue; + } + + IField drawableContainerField = drawableContainer.GetField(field.FieldType); + // Notify before change, so we can e.g. invalidate the area + drawableContainer.BeforeFieldChange(drawableContainerField, field.Value); + + drawableContainerField.Value = field.Value; + // update last used from DC field, so that scope is honored + EditorConfig.UpdateLastFieldValue(drawableContainerField); + } + } + } } \ No newline at end of file diff --git a/src/Greenshot/Drawing/Fields/FieldType.cs b/src/Greenshot.Editor/Drawing/Fields/FieldType.cs similarity index 96% rename from src/Greenshot/Drawing/Fields/FieldType.cs rename to src/Greenshot.Editor/Drawing/Fields/FieldType.cs index ecc3f1781..c3b732f4b 100644 --- a/src/Greenshot/Drawing/Fields/FieldType.cs +++ b/src/Greenshot.Editor/Drawing/Fields/FieldType.cs @@ -1,106 +1,106 @@ -/* - * Greenshot - a free and open source screenshot tool - * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom - * - * For more information see: https://getgreenshot.org/ - * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 1 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -using System; -using Greenshot.Base.Interfaces.Drawing; - -namespace Greenshot.Drawing.Fields -{ - /// - /// Defines all FieldTypes + their default value. - /// (The additional value is why this is not an enum) - /// - [Serializable] - public class FieldType : IFieldType - { - public static readonly IFieldType ARROWHEADS = new FieldType("ARROWHEADS"); - public static readonly IFieldType BLUR_RADIUS = new FieldType("BLUR_RADIUS"); - public static readonly IFieldType BRIGHTNESS = new FieldType("BRIGHTNESS"); - public static readonly IFieldType FILL_COLOR = new FieldType("FILL_COLOR"); - public static readonly IFieldType FONT_BOLD = new FieldType("FONT_BOLD"); - public static readonly IFieldType FONT_FAMILY = new FieldType("FONT_FAMILY"); - public static readonly IFieldType FONT_ITALIC = new FieldType("FONT_ITALIC"); - public static readonly IFieldType FONT_SIZE = new FieldType("FONT_SIZE"); - public static readonly IFieldType TEXT_HORIZONTAL_ALIGNMENT = new FieldType("TEXT_HORIZONTAL_ALIGNMENT"); - public static readonly IFieldType TEXT_VERTICAL_ALIGNMENT = new FieldType("TEXT_VERTICAL_ALIGNMENT"); - public static readonly IFieldType HIGHLIGHT_COLOR = new FieldType("HIGHLIGHT_COLOR"); - public static readonly IFieldType LINE_COLOR = new FieldType("LINE_COLOR"); - public static readonly IFieldType LINE_THICKNESS = new FieldType("LINE_THICKNESS"); - public static readonly IFieldType MAGNIFICATION_FACTOR = new FieldType("MAGNIFICATION_FACTOR"); - public static readonly IFieldType PIXEL_SIZE = new FieldType("PIXEL_SIZE"); - public static readonly IFieldType PREVIEW_QUALITY = new FieldType("PREVIEW_QUALITY"); - public static readonly IFieldType SHADOW = new FieldType("SHADOW"); - public static readonly IFieldType PREPARED_FILTER_OBFUSCATE = new FieldType("PREPARED_FILTER_OBFUSCATE"); - public static readonly IFieldType PREPARED_FILTER_HIGHLIGHT = new FieldType("PREPARED_FILTER_HIGHLIGHT"); - public static readonly IFieldType FLAGS = new FieldType("FLAGS"); - - public static IFieldType[] Values = - { - ARROWHEADS, BLUR_RADIUS, BRIGHTNESS, FILL_COLOR, FONT_BOLD, FONT_FAMILY, FONT_ITALIC, FONT_SIZE, TEXT_HORIZONTAL_ALIGNMENT, TEXT_VERTICAL_ALIGNMENT, HIGHLIGHT_COLOR, - LINE_COLOR, LINE_THICKNESS, MAGNIFICATION_FACTOR, PIXEL_SIZE, PREVIEW_QUALITY, SHADOW, PREPARED_FILTER_OBFUSCATE, PREPARED_FILTER_HIGHLIGHT, FLAGS - }; - - public string Name { get; set; } - - private FieldType(string name) - { - Name = name; - } - - public override string ToString() - { - return Name; - } - - public override int GetHashCode() - { - int hashCode = 0; - unchecked - { - if (Name != null) - hashCode += 1000000009 * Name.GetHashCode(); - } - - return hashCode; - } - - public override bool Equals(object obj) - { - FieldType other = obj as FieldType; - if (other == null) - { - return false; - } - - return Equals(Name, other.Name); - } - - public static bool operator ==(FieldType a, FieldType b) - { - return Equals(a, b); - } - - public static bool operator !=(FieldType a, FieldType b) - { - return !Equals(a, b); - } - } +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using System; +using Greenshot.Base.Interfaces.Drawing; + +namespace Greenshot.Editor.Drawing.Fields +{ + /// + /// Defines all FieldTypes + their default value. + /// (The additional value is why this is not an enum) + /// + [Serializable] + public class FieldType : IFieldType + { + public static readonly IFieldType ARROWHEADS = new FieldType("ARROWHEADS"); + public static readonly IFieldType BLUR_RADIUS = new FieldType("BLUR_RADIUS"); + public static readonly IFieldType BRIGHTNESS = new FieldType("BRIGHTNESS"); + public static readonly IFieldType FILL_COLOR = new FieldType("FILL_COLOR"); + public static readonly IFieldType FONT_BOLD = new FieldType("FONT_BOLD"); + public static readonly IFieldType FONT_FAMILY = new FieldType("FONT_FAMILY"); + public static readonly IFieldType FONT_ITALIC = new FieldType("FONT_ITALIC"); + public static readonly IFieldType FONT_SIZE = new FieldType("FONT_SIZE"); + public static readonly IFieldType TEXT_HORIZONTAL_ALIGNMENT = new FieldType("TEXT_HORIZONTAL_ALIGNMENT"); + public static readonly IFieldType TEXT_VERTICAL_ALIGNMENT = new FieldType("TEXT_VERTICAL_ALIGNMENT"); + public static readonly IFieldType HIGHLIGHT_COLOR = new FieldType("HIGHLIGHT_COLOR"); + public static readonly IFieldType LINE_COLOR = new FieldType("LINE_COLOR"); + public static readonly IFieldType LINE_THICKNESS = new FieldType("LINE_THICKNESS"); + public static readonly IFieldType MAGNIFICATION_FACTOR = new FieldType("MAGNIFICATION_FACTOR"); + public static readonly IFieldType PIXEL_SIZE = new FieldType("PIXEL_SIZE"); + public static readonly IFieldType PREVIEW_QUALITY = new FieldType("PREVIEW_QUALITY"); + public static readonly IFieldType SHADOW = new FieldType("SHADOW"); + public static readonly IFieldType PREPARED_FILTER_OBFUSCATE = new FieldType("PREPARED_FILTER_OBFUSCATE"); + public static readonly IFieldType PREPARED_FILTER_HIGHLIGHT = new FieldType("PREPARED_FILTER_HIGHLIGHT"); + public static readonly IFieldType FLAGS = new FieldType("FLAGS"); + + public static IFieldType[] Values = + { + ARROWHEADS, BLUR_RADIUS, BRIGHTNESS, FILL_COLOR, FONT_BOLD, FONT_FAMILY, FONT_ITALIC, FONT_SIZE, TEXT_HORIZONTAL_ALIGNMENT, TEXT_VERTICAL_ALIGNMENT, HIGHLIGHT_COLOR, + LINE_COLOR, LINE_THICKNESS, MAGNIFICATION_FACTOR, PIXEL_SIZE, PREVIEW_QUALITY, SHADOW, PREPARED_FILTER_OBFUSCATE, PREPARED_FILTER_HIGHLIGHT, FLAGS + }; + + public string Name { get; set; } + + private FieldType(string name) + { + Name = name; + } + + public override string ToString() + { + return Name; + } + + public override int GetHashCode() + { + int hashCode = 0; + unchecked + { + if (Name != null) + hashCode += 1000000009 * Name.GetHashCode(); + } + + return hashCode; + } + + public override bool Equals(object obj) + { + FieldType other = obj as FieldType; + if (other == null) + { + return false; + } + + return Equals(Name, other.Name); + } + + public static bool operator ==(FieldType a, FieldType b) + { + return Equals(a, b); + } + + public static bool operator !=(FieldType a, FieldType b) + { + return !Equals(a, b); + } + } } \ No newline at end of file diff --git a/src/Greenshot/Drawing/FilterContainer.cs b/src/Greenshot.Editor/Drawing/FilterContainer.cs similarity index 95% rename from src/Greenshot/Drawing/FilterContainer.cs rename to src/Greenshot.Editor/Drawing/FilterContainer.cs index c8cab4e4c..3bdb5a5fd 100644 --- a/src/Greenshot/Drawing/FilterContainer.cs +++ b/src/Greenshot.Editor/Drawing/FilterContainer.cs @@ -1,115 +1,115 @@ -/* - * Greenshot - a free and open source screenshot tool - * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom - * - * For more information see: https://getgreenshot.org/ - * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 1 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -using System; -using System.Drawing; -using Greenshot.Drawing.Fields; -using Greenshot.Helpers; -using System.Drawing.Drawing2D; -using System.Runtime.Serialization; -using Greenshot.Base.Interfaces.Drawing; - -namespace Greenshot.Drawing -{ - /// - /// empty container for filter-only elements - /// - [Serializable] - public abstract class FilterContainer : DrawableContainer - { - public enum PreparedFilterMode - { - OBFUSCATE, - HIGHLIGHT - }; - - public enum PreparedFilter - { - BLUR, - PIXELIZE, - TEXT_HIGHTLIGHT, - AREA_HIGHLIGHT, - GRAYSCALE, - MAGNIFICATION - }; - - 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() - { - AddField(GetType(), FieldType.LINE_THICKNESS, 0); - AddField(GetType(), FieldType.LINE_COLOR, Color.Red); - AddField(GetType(), FieldType.SHADOW, false); - } - - public override void Draw(Graphics graphics, RenderMode rm) - { - int lineThickness = GetFieldValueAsInt(FieldType.LINE_THICKNESS); - Color lineColor = GetFieldValueAsColor(FieldType.LINE_COLOR); - bool shadow = GetFieldValueAsBool(FieldType.SHADOW); - bool lineVisible = lineThickness > 0 && Colors.IsVisible(lineColor); - if (lineVisible) - { - graphics.SmoothingMode = SmoothingMode.HighSpeed; - graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; - graphics.CompositingQuality = CompositingQuality.HighQuality; - graphics.PixelOffsetMode = PixelOffsetMode.None; - //draw shadow first - if (shadow) - { - int basealpha = 100; - int alpha = basealpha; - int steps = 5; - int currentStep = lineVisible ? 1 : 0; - while (currentStep <= steps) - { - using Pen shadowPen = new Pen(Color.FromArgb(alpha, 100, 100, 100), lineThickness); - Rectangle shadowRect = GuiRectangle.GetGuiRectangle(Left + currentStep, Top + currentStep, Width, Height); - graphics.DrawRectangle(shadowPen, shadowRect); - currentStep++; - alpha -= basealpha / steps; - } - } - - Rectangle rect = GuiRectangle.GetGuiRectangle(Left, Top, Width, Height); - if (lineThickness > 0) - { - using Pen pen = new Pen(lineColor, lineThickness); - graphics.DrawRectangle(pen, rect); - } - } - } - } +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using System; +using System.Drawing; +using System.Drawing.Drawing2D; +using System.Runtime.Serialization; +using Greenshot.Base.Interfaces.Drawing; +using Greenshot.Editor.Drawing.Fields; +using Greenshot.Editor.Helpers; + +namespace Greenshot.Editor.Drawing +{ + /// + /// empty container for filter-only elements + /// + [Serializable] + public abstract class FilterContainer : DrawableContainer + { + public enum PreparedFilterMode + { + OBFUSCATE, + HIGHLIGHT + }; + + public enum PreparedFilter + { + BLUR, + PIXELIZE, + TEXT_HIGHTLIGHT, + AREA_HIGHLIGHT, + GRAYSCALE, + MAGNIFICATION + }; + + 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() + { + AddField(GetType(), FieldType.LINE_THICKNESS, 0); + AddField(GetType(), FieldType.LINE_COLOR, Color.Red); + AddField(GetType(), FieldType.SHADOW, false); + } + + public override void Draw(Graphics graphics, RenderMode rm) + { + int lineThickness = GetFieldValueAsInt(FieldType.LINE_THICKNESS); + Color lineColor = GetFieldValueAsColor(FieldType.LINE_COLOR); + bool shadow = GetFieldValueAsBool(FieldType.SHADOW); + bool lineVisible = lineThickness > 0 && Colors.IsVisible(lineColor); + if (lineVisible) + { + graphics.SmoothingMode = SmoothingMode.HighSpeed; + graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; + graphics.CompositingQuality = CompositingQuality.HighQuality; + graphics.PixelOffsetMode = PixelOffsetMode.None; + //draw shadow first + if (shadow) + { + int basealpha = 100; + int alpha = basealpha; + int steps = 5; + int currentStep = lineVisible ? 1 : 0; + while (currentStep <= steps) + { + using Pen shadowPen = new Pen(Color.FromArgb(alpha, 100, 100, 100), lineThickness); + Rectangle shadowRect = GuiRectangle.GetGuiRectangle(Left + currentStep, Top + currentStep, Width, Height); + graphics.DrawRectangle(shadowPen, shadowRect); + currentStep++; + alpha -= basealpha / steps; + } + } + + Rectangle rect = GuiRectangle.GetGuiRectangle(Left, Top, Width, Height); + if (lineThickness > 0) + { + using Pen pen = new Pen(lineColor, lineThickness); + graphics.DrawRectangle(pen, rect); + } + } + } + } } \ No newline at end of file diff --git a/src/Greenshot/Drawing/Filters/AbstractFilter.cs b/src/Greenshot.Editor/Drawing/Filters/AbstractFilter.cs similarity index 94% rename from src/Greenshot/Drawing/Filters/AbstractFilter.cs rename to src/Greenshot.Editor/Drawing/Filters/AbstractFilter.cs index 4dfb6d66c..17517b971 100644 --- a/src/Greenshot/Drawing/Filters/AbstractFilter.cs +++ b/src/Greenshot.Editor/Drawing/Filters/AbstractFilter.cs @@ -1,83 +1,83 @@ -/* - * Greenshot - a free and open source screenshot tool - * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom - * - * For more information see: https://getgreenshot.org/ - * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 1 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -using System; -using System.ComponentModel; -using System.Drawing; -using Greenshot.Base.Interfaces.Drawing; -using Greenshot.Drawing.Fields; - -namespace Greenshot.Drawing.Filters -{ - /// - /// Graphical filter which can be added to DrawableContainer. - /// Subclasses should fulfill INotifyPropertyChanged contract, i.e. call - /// OnPropertyChanged whenever a public property has been changed. - /// - [Serializable] - public abstract class AbstractFilter : AbstractFieldHolder, IFilter - { - [NonSerialized] private PropertyChangedEventHandler propertyChanged; - - public event PropertyChangedEventHandler PropertyChanged - { - add { propertyChanged += value; } - remove { propertyChanged -= value; } - } - - private bool invert; - - public bool Invert - { - get { return invert; } - set - { - invert = value; - OnPropertyChanged("Invert"); - } - } - - protected DrawableContainer parent; - - public DrawableContainer Parent - { - get { return parent; } - set { parent = value; } - } - - public AbstractFilter(DrawableContainer parent) - { - this.parent = parent; - } - - public DrawableContainer GetParent() - { - return parent; - } - - public abstract void Apply(Graphics graphics, Bitmap applyBitmap, Rectangle rect, RenderMode renderMode); - - protected void OnPropertyChanged(string propertyName) - { - propertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); - } - } +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using System; +using System.ComponentModel; +using System.Drawing; +using Greenshot.Base.Interfaces.Drawing; +using Greenshot.Editor.Drawing.Fields; + +namespace Greenshot.Editor.Drawing.Filters +{ + /// + /// Graphical filter which can be added to DrawableContainer. + /// Subclasses should fulfill INotifyPropertyChanged contract, i.e. call + /// OnPropertyChanged whenever a public property has been changed. + /// + [Serializable] + public abstract class AbstractFilter : AbstractFieldHolder, IFilter + { + [NonSerialized] private PropertyChangedEventHandler propertyChanged; + + public event PropertyChangedEventHandler PropertyChanged + { + add { propertyChanged += value; } + remove { propertyChanged -= value; } + } + + private bool invert; + + public bool Invert + { + get { return invert; } + set + { + invert = value; + OnPropertyChanged("Invert"); + } + } + + protected DrawableContainer parent; + + public DrawableContainer Parent + { + get { return parent; } + set { parent = value; } + } + + public AbstractFilter(DrawableContainer parent) + { + this.parent = parent; + } + + public DrawableContainer GetParent() + { + return parent; + } + + public abstract void Apply(Graphics graphics, Bitmap applyBitmap, Rectangle rect, RenderMode renderMode); + + protected void OnPropertyChanged(string propertyName) + { + propertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); + } + } } \ No newline at end of file diff --git a/src/Greenshot/Drawing/Filters/BlurFilter.cs b/src/Greenshot.Editor/Drawing/Filters/BlurFilter.cs similarity index 94% rename from src/Greenshot/Drawing/Filters/BlurFilter.cs rename to src/Greenshot.Editor/Drawing/Filters/BlurFilter.cs index 5ecbd4ed9..ac0e26a64 100644 --- a/src/Greenshot/Drawing/Filters/BlurFilter.cs +++ b/src/Greenshot.Editor/Drawing/Filters/BlurFilter.cs @@ -1,83 +1,83 @@ -/* - * Greenshot - a free and open source screenshot tool - * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom - * - * For more information see: https://getgreenshot.org/ - * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 1 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -using System; -using System.Drawing; -using Greenshot.Drawing.Fields; -using System.Drawing.Drawing2D; -using Greenshot.Base.Core; -using Greenshot.Base.Interfaces.Drawing; -using Greenshot.Base.UnmanagedHelpers; - -namespace Greenshot.Drawing.Filters -{ - [Serializable] - public class BlurFilter : AbstractFilter - { - public double previewQuality; - - public double PreviewQuality - { - get { return previewQuality; } - set - { - previewQuality = value; - OnPropertyChanged("PreviewQuality"); - } - } - - public BlurFilter(DrawableContainer parent) : base(parent) - { - AddField(GetType(), FieldType.BLUR_RADIUS, 3); - AddField(GetType(), FieldType.PREVIEW_QUALITY, 1.0d); - } - - public override void Apply(Graphics graphics, Bitmap applyBitmap, Rectangle rect, RenderMode renderMode) - { - int blurRadius = GetFieldValueAsInt(FieldType.BLUR_RADIUS); - Rectangle applyRect = ImageHelper.CreateIntersectRectangle(applyBitmap.Size, rect, Invert); - if (applyRect.Width == 0 || applyRect.Height == 0) - { - return; - } - - GraphicsState state = graphics.Save(); - if (Invert) - { - graphics.SetClip(applyRect); - graphics.ExcludeClip(rect); - } - - if (GDIplus.IsBlurPossible(blurRadius)) - { - GDIplus.DrawWithBlur(graphics, applyBitmap, applyRect, null, null, blurRadius, false); - } - else - { - using IFastBitmap fastBitmap = FastBitmap.CreateCloneOf(applyBitmap, applyRect); - ImageHelper.ApplyBoxBlur(fastBitmap, blurRadius); - fastBitmap.DrawTo(graphics, applyRect); - } - - graphics.Restore(state); - } - } +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using System; +using System.Drawing; +using System.Drawing.Drawing2D; +using Greenshot.Base.Core; +using Greenshot.Base.Interfaces.Drawing; +using Greenshot.Base.UnmanagedHelpers; +using Greenshot.Editor.Drawing.Fields; + +namespace Greenshot.Editor.Drawing.Filters +{ + [Serializable] + public class BlurFilter : AbstractFilter + { + public double previewQuality; + + public double PreviewQuality + { + get { return previewQuality; } + set + { + previewQuality = value; + OnPropertyChanged("PreviewQuality"); + } + } + + public BlurFilter(DrawableContainer parent) : base(parent) + { + AddField(GetType(), FieldType.BLUR_RADIUS, 3); + AddField(GetType(), FieldType.PREVIEW_QUALITY, 1.0d); + } + + public override void Apply(Graphics graphics, Bitmap applyBitmap, Rectangle rect, RenderMode renderMode) + { + int blurRadius = GetFieldValueAsInt(FieldType.BLUR_RADIUS); + Rectangle applyRect = ImageHelper.CreateIntersectRectangle(applyBitmap.Size, rect, Invert); + if (applyRect.Width == 0 || applyRect.Height == 0) + { + return; + } + + GraphicsState state = graphics.Save(); + if (Invert) + { + graphics.SetClip(applyRect); + graphics.ExcludeClip(rect); + } + + if (GDIplus.IsBlurPossible(blurRadius)) + { + GDIplus.DrawWithBlur(graphics, applyBitmap, applyRect, null, null, blurRadius, false); + } + else + { + using IFastBitmap fastBitmap = FastBitmap.CreateCloneOf(applyBitmap, applyRect); + ImageHelper.ApplyBoxBlur(fastBitmap, blurRadius); + fastBitmap.DrawTo(graphics, applyRect); + } + + graphics.Restore(state); + } + } } \ No newline at end of file diff --git a/src/Greenshot/Drawing/Filters/BrightnessFilter.cs b/src/Greenshot.Editor/Drawing/Filters/BrightnessFilter.cs similarity index 94% rename from src/Greenshot/Drawing/Filters/BrightnessFilter.cs rename to src/Greenshot.Editor/Drawing/Filters/BrightnessFilter.cs index b372c42e3..cfdf7b025 100644 --- a/src/Greenshot/Drawing/Filters/BrightnessFilter.cs +++ b/src/Greenshot.Editor/Drawing/Filters/BrightnessFilter.cs @@ -1,73 +1,73 @@ -/* - * Greenshot - a free and open source screenshot tool - * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom - * - * For more information see: https://getgreenshot.org/ - * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 1 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -using System; -using System.Drawing; -using Greenshot.Drawing.Fields; -using System.Drawing.Imaging; -using System.Drawing.Drawing2D; -using Greenshot.Base.Core; -using Greenshot.Base.Interfaces.Drawing; - -namespace Greenshot.Drawing.Filters -{ - [Serializable()] - public class BrightnessFilter : AbstractFilter - { - public BrightnessFilter(DrawableContainer parent) : base(parent) - { - AddField(GetType(), FieldType.BRIGHTNESS, 0.9d); - } - - /// - /// Implements the Apply code for the Brightness Filet - /// - /// - /// - /// - /// - public override void Apply(Graphics graphics, Bitmap applyBitmap, Rectangle rect, RenderMode renderMode) - { - Rectangle applyRect = ImageHelper.CreateIntersectRectangle(applyBitmap.Size, rect, Invert); - - if (applyRect.Width == 0 || applyRect.Height == 0) - { - // nothing to do - return; - } - - GraphicsState state = graphics.Save(); - if (Invert) - { - graphics.SetClip(applyRect); - graphics.ExcludeClip(rect); - } - - float brightness = GetFieldValueAsFloat(FieldType.BRIGHTNESS); - using (ImageAttributes ia = ImageHelper.CreateAdjustAttributes(brightness, 1f, 1f)) - { - graphics.DrawImage(applyBitmap, applyRect, applyRect.X, applyRect.Y, applyRect.Width, applyRect.Height, GraphicsUnit.Pixel, ia); - } - - graphics.Restore(state); - } - } +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using System; +using System.Drawing; +using System.Drawing.Drawing2D; +using System.Drawing.Imaging; +using Greenshot.Base.Core; +using Greenshot.Base.Interfaces.Drawing; +using Greenshot.Editor.Drawing.Fields; + +namespace Greenshot.Editor.Drawing.Filters +{ + [Serializable()] + public class BrightnessFilter : AbstractFilter + { + public BrightnessFilter(DrawableContainer parent) : base(parent) + { + AddField(GetType(), FieldType.BRIGHTNESS, 0.9d); + } + + /// + /// Implements the Apply code for the Brightness Filet + /// + /// + /// + /// + /// + public override void Apply(Graphics graphics, Bitmap applyBitmap, Rectangle rect, RenderMode renderMode) + { + Rectangle applyRect = ImageHelper.CreateIntersectRectangle(applyBitmap.Size, rect, Invert); + + if (applyRect.Width == 0 || applyRect.Height == 0) + { + // nothing to do + return; + } + + GraphicsState state = graphics.Save(); + if (Invert) + { + graphics.SetClip(applyRect); + graphics.ExcludeClip(rect); + } + + float brightness = GetFieldValueAsFloat(FieldType.BRIGHTNESS); + using (ImageAttributes ia = ImageHelper.CreateAdjustAttributes(brightness, 1f, 1f)) + { + graphics.DrawImage(applyBitmap, applyRect, applyRect.X, applyRect.Y, applyRect.Width, applyRect.Height, GraphicsUnit.Pixel, ia); + } + + graphics.Restore(state); + } + } } \ No newline at end of file diff --git a/src/Greenshot/Drawing/Filters/GrayscaleFilter.cs b/src/Greenshot.Editor/Drawing/Filters/GrayscaleFilter.cs similarity index 95% rename from src/Greenshot/Drawing/Filters/GrayscaleFilter.cs rename to src/Greenshot.Editor/Drawing/Filters/GrayscaleFilter.cs index 356a91a19..5ed195fc7 100644 --- a/src/Greenshot/Drawing/Filters/GrayscaleFilter.cs +++ b/src/Greenshot.Editor/Drawing/Filters/GrayscaleFilter.cs @@ -1,90 +1,90 @@ -/* - * Greenshot - a free and open source screenshot tool - * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom - * - * For more information see: https://getgreenshot.org/ - * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 1 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -using System; -using System.Drawing; -using System.Drawing.Drawing2D; -using System.Drawing.Imaging; -using Greenshot.Base.Core; -using Greenshot.Base.Interfaces.Drawing; - -namespace Greenshot.Drawing.Filters -{ - /// - /// Description of GrayscaleFilter. - /// - [Serializable()] - public class GrayscaleFilter : AbstractFilter - { - public GrayscaleFilter(DrawableContainer parent) : base(parent) - { - } - - public override void Apply(Graphics graphics, Bitmap applyBitmap, Rectangle rect, RenderMode renderMode) - { - Rectangle applyRect = ImageHelper.CreateIntersectRectangle(applyBitmap.Size, rect, Invert); - - if (applyRect.Width == 0 || applyRect.Height == 0) - { - // nothing to do - return; - } - - GraphicsState state = graphics.Save(); - if (Invert) - { - graphics.SetClip(applyRect); - graphics.ExcludeClip(rect); - } - - ColorMatrix grayscaleMatrix = new ColorMatrix(new[] - { - new[] - { - .3f, .3f, .3f, 0, 0 - }, - new[] - { - .59f, .59f, .59f, 0, 0 - }, - new[] - { - .11f, .11f, .11f, 0, 0 - }, - new float[] - { - 0, 0, 0, 1, 0 - }, - new float[] - { - 0, 0, 0, 0, 1 - } - }); - using (ImageAttributes ia = new ImageAttributes()) - { - ia.SetColorMatrix(grayscaleMatrix); - graphics.DrawImage(applyBitmap, applyRect, applyRect.X, applyRect.Y, applyRect.Width, applyRect.Height, GraphicsUnit.Pixel, ia); - } - - graphics.Restore(state); - } - } +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using System; +using System.Drawing; +using System.Drawing.Drawing2D; +using System.Drawing.Imaging; +using Greenshot.Base.Core; +using Greenshot.Base.Interfaces.Drawing; + +namespace Greenshot.Editor.Drawing.Filters +{ + /// + /// Description of GrayscaleFilter. + /// + [Serializable()] + public class GrayscaleFilter : AbstractFilter + { + public GrayscaleFilter(DrawableContainer parent) : base(parent) + { + } + + public override void Apply(Graphics graphics, Bitmap applyBitmap, Rectangle rect, RenderMode renderMode) + { + Rectangle applyRect = ImageHelper.CreateIntersectRectangle(applyBitmap.Size, rect, Invert); + + if (applyRect.Width == 0 || applyRect.Height == 0) + { + // nothing to do + return; + } + + GraphicsState state = graphics.Save(); + if (Invert) + { + graphics.SetClip(applyRect); + graphics.ExcludeClip(rect); + } + + ColorMatrix grayscaleMatrix = new ColorMatrix(new[] + { + new[] + { + .3f, .3f, .3f, 0, 0 + }, + new[] + { + .59f, .59f, .59f, 0, 0 + }, + new[] + { + .11f, .11f, .11f, 0, 0 + }, + new float[] + { + 0, 0, 0, 1, 0 + }, + new float[] + { + 0, 0, 0, 0, 1 + } + }); + using (ImageAttributes ia = new ImageAttributes()) + { + ia.SetColorMatrix(grayscaleMatrix); + graphics.DrawImage(applyBitmap, applyRect, applyRect.X, applyRect.Y, applyRect.Width, applyRect.Height, GraphicsUnit.Pixel, ia); + } + + graphics.Restore(state); + } + } } \ No newline at end of file diff --git a/src/Greenshot/Drawing/Filters/HighlightFilter.cs b/src/Greenshot.Editor/Drawing/Filters/HighlightFilter.cs similarity index 95% rename from src/Greenshot/Drawing/Filters/HighlightFilter.cs rename to src/Greenshot.Editor/Drawing/Filters/HighlightFilter.cs index e42e1b60a..32337fb98 100644 --- a/src/Greenshot/Drawing/Filters/HighlightFilter.cs +++ b/src/Greenshot.Editor/Drawing/Filters/HighlightFilter.cs @@ -1,82 +1,82 @@ -/* - * Greenshot - a free and open source screenshot tool - * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom - * - * For more information see: https://getgreenshot.org/ - * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 1 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -using System; -using System.Drawing; -using Greenshot.Drawing.Fields; -using System.Drawing.Drawing2D; -using Greenshot.Base.Core; -using Greenshot.Base.Interfaces.Drawing; - -namespace Greenshot.Drawing.Filters -{ - [Serializable()] - public class HighlightFilter : AbstractFilter - { - public HighlightFilter(DrawableContainer parent) : base(parent) - { - AddField(GetType(), FieldType.FILL_COLOR, Color.Yellow); - } - - /// - /// Implements the Apply code for the Brightness Filet - /// - /// - /// - /// - /// - public override void Apply(Graphics graphics, Bitmap applyBitmap, Rectangle rect, RenderMode renderMode) - { - Rectangle applyRect = ImageHelper.CreateIntersectRectangle(applyBitmap.Size, rect, Invert); - - if (applyRect.Width == 0 || applyRect.Height == 0) - { - // nothing to do - return; - } - - GraphicsState state = graphics.Save(); - if (Invert) - { - graphics.SetClip(applyRect); - graphics.ExcludeClip(rect); - } - - using (IFastBitmap fastBitmap = FastBitmap.CreateCloneOf(applyBitmap, applyRect)) - { - Color highlightColor = GetFieldValueAsColor(FieldType.FILL_COLOR); - for (int y = fastBitmap.Top; y < fastBitmap.Bottom; y++) - { - for (int x = fastBitmap.Left; x < fastBitmap.Right; x++) - { - Color color = fastBitmap.GetColorAt(x, y); - color = Color.FromArgb(color.A, Math.Min(highlightColor.R, color.R), Math.Min(highlightColor.G, color.G), Math.Min(highlightColor.B, color.B)); - fastBitmap.SetColorAt(x, y, color); - } - } - - fastBitmap.DrawTo(graphics, applyRect.Location); - } - - graphics.Restore(state); - } - } +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using System; +using System.Drawing; +using System.Drawing.Drawing2D; +using Greenshot.Base.Core; +using Greenshot.Base.Interfaces.Drawing; +using Greenshot.Editor.Drawing.Fields; + +namespace Greenshot.Editor.Drawing.Filters +{ + [Serializable()] + public class HighlightFilter : AbstractFilter + { + public HighlightFilter(DrawableContainer parent) : base(parent) + { + AddField(GetType(), FieldType.FILL_COLOR, Color.Yellow); + } + + /// + /// Implements the Apply code for the Brightness Filet + /// + /// + /// + /// + /// + public override void Apply(Graphics graphics, Bitmap applyBitmap, Rectangle rect, RenderMode renderMode) + { + Rectangle applyRect = ImageHelper.CreateIntersectRectangle(applyBitmap.Size, rect, Invert); + + if (applyRect.Width == 0 || applyRect.Height == 0) + { + // nothing to do + return; + } + + GraphicsState state = graphics.Save(); + if (Invert) + { + graphics.SetClip(applyRect); + graphics.ExcludeClip(rect); + } + + using (IFastBitmap fastBitmap = FastBitmap.CreateCloneOf(applyBitmap, applyRect)) + { + Color highlightColor = GetFieldValueAsColor(FieldType.FILL_COLOR); + for (int y = fastBitmap.Top; y < fastBitmap.Bottom; y++) + { + for (int x = fastBitmap.Left; x < fastBitmap.Right; x++) + { + Color color = fastBitmap.GetColorAt(x, y); + color = Color.FromArgb(color.A, Math.Min(highlightColor.R, color.R), Math.Min(highlightColor.G, color.G), Math.Min(highlightColor.B, color.B)); + fastBitmap.SetColorAt(x, y, color); + } + } + + fastBitmap.DrawTo(graphics, applyRect.Location); + } + + graphics.Restore(state); + } + } } \ No newline at end of file diff --git a/src/Greenshot/Drawing/Filters/IFilter.cs b/src/Greenshot.Editor/Drawing/Filters/IFilter.cs similarity index 94% rename from src/Greenshot/Drawing/Filters/IFilter.cs rename to src/Greenshot.Editor/Drawing/Filters/IFilter.cs index 37a19e73a..144732761 100644 --- a/src/Greenshot/Drawing/Filters/IFilter.cs +++ b/src/Greenshot.Editor/Drawing/Filters/IFilter.cs @@ -1,35 +1,35 @@ -/* - * Greenshot - a free and open source screenshot tool - * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom - * - * For more information see: https://getgreenshot.org/ - * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 1 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -using System.ComponentModel; -using System.Drawing; -using Greenshot.Base.Interfaces.Drawing; - -namespace Greenshot.Drawing.Filters -{ - public interface IFilter : INotifyPropertyChanged, IFieldHolder - { - DrawableContainer Parent { get; set; } - void Apply(Graphics graphics, Bitmap bmp, Rectangle rect, RenderMode renderMode); - DrawableContainer GetParent(); - bool Invert { get; set; } - } +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using System.ComponentModel; +using System.Drawing; +using Greenshot.Base.Interfaces.Drawing; + +namespace Greenshot.Editor.Drawing.Filters +{ + public interface IFilter : INotifyPropertyChanged, IFieldHolder + { + DrawableContainer Parent { get; set; } + void Apply(Graphics graphics, Bitmap bmp, Rectangle rect, RenderMode renderMode); + DrawableContainer GetParent(); + bool Invert { get; set; } + } } \ No newline at end of file diff --git a/src/Greenshot/Drawing/Filters/MagnifierFilter.cs b/src/Greenshot.Editor/Drawing/Filters/MagnifierFilter.cs similarity index 95% rename from src/Greenshot/Drawing/Filters/MagnifierFilter.cs rename to src/Greenshot.Editor/Drawing/Filters/MagnifierFilter.cs index 6f0bf96c4..f76640820 100644 --- a/src/Greenshot/Drawing/Filters/MagnifierFilter.cs +++ b/src/Greenshot.Editor/Drawing/Filters/MagnifierFilter.cs @@ -1,70 +1,70 @@ -/* - * Greenshot - a free and open source screenshot tool - * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom - * - * For more information see: https://getgreenshot.org/ - * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 1 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -using System; -using System.Drawing; -using Greenshot.Drawing.Fields; -using System.Drawing.Drawing2D; -using Greenshot.Base.Core; -using Greenshot.Base.Interfaces.Drawing; - -namespace Greenshot.Drawing.Filters -{ - [Serializable] - public class MagnifierFilter : AbstractFilter - { - public MagnifierFilter(DrawableContainer parent) : base(parent) - { - AddField(GetType(), FieldType.MAGNIFICATION_FACTOR, 2); - } - - public override void Apply(Graphics graphics, Bitmap applyBitmap, Rectangle rect, RenderMode renderMode) - { - Rectangle applyRect = ImageHelper.CreateIntersectRectangle(applyBitmap.Size, rect, Invert); - - if (applyRect.Width == 0 || applyRect.Height == 0) - { - // nothing to do - return; - } - - int magnificationFactor = GetFieldValueAsInt(FieldType.MAGNIFICATION_FACTOR); - GraphicsState state = graphics.Save(); - if (Invert) - { - graphics.SetClip(applyRect); - graphics.ExcludeClip(rect); - } - - graphics.SmoothingMode = SmoothingMode.None; - graphics.InterpolationMode = InterpolationMode.NearestNeighbor; - graphics.CompositingQuality = CompositingQuality.HighQuality; - graphics.PixelOffsetMode = PixelOffsetMode.None; - int halfWidth = rect.Width / 2; - int halfHeight = rect.Height / 2; - int newWidth = rect.Width / magnificationFactor; - int newHeight = rect.Height / magnificationFactor; - Rectangle source = new Rectangle(rect.X + halfWidth - newWidth / 2, rect.Y + halfHeight - newHeight / 2, newWidth, newHeight); - graphics.DrawImage(applyBitmap, rect, source, GraphicsUnit.Pixel); - graphics.Restore(state); - } - } +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using System; +using System.Drawing; +using System.Drawing.Drawing2D; +using Greenshot.Base.Core; +using Greenshot.Base.Interfaces.Drawing; +using Greenshot.Editor.Drawing.Fields; + +namespace Greenshot.Editor.Drawing.Filters +{ + [Serializable] + public class MagnifierFilter : AbstractFilter + { + public MagnifierFilter(DrawableContainer parent) : base(parent) + { + AddField(GetType(), FieldType.MAGNIFICATION_FACTOR, 2); + } + + public override void Apply(Graphics graphics, Bitmap applyBitmap, Rectangle rect, RenderMode renderMode) + { + Rectangle applyRect = ImageHelper.CreateIntersectRectangle(applyBitmap.Size, rect, Invert); + + if (applyRect.Width == 0 || applyRect.Height == 0) + { + // nothing to do + return; + } + + int magnificationFactor = GetFieldValueAsInt(FieldType.MAGNIFICATION_FACTOR); + GraphicsState state = graphics.Save(); + if (Invert) + { + graphics.SetClip(applyRect); + graphics.ExcludeClip(rect); + } + + graphics.SmoothingMode = SmoothingMode.None; + graphics.InterpolationMode = InterpolationMode.NearestNeighbor; + graphics.CompositingQuality = CompositingQuality.HighQuality; + graphics.PixelOffsetMode = PixelOffsetMode.None; + int halfWidth = rect.Width / 2; + int halfHeight = rect.Height / 2; + int newWidth = rect.Width / magnificationFactor; + int newHeight = rect.Height / magnificationFactor; + Rectangle source = new Rectangle(rect.X + halfWidth - newWidth / 2, rect.Y + halfHeight - newHeight / 2, newWidth, newHeight); + graphics.DrawImage(applyBitmap, rect, source, GraphicsUnit.Pixel); + graphics.Restore(state); + } + } } \ No newline at end of file diff --git a/src/Greenshot/Drawing/Filters/PixelizationFilter.cs b/src/Greenshot.Editor/Drawing/Filters/PixelizationFilter.cs similarity index 95% rename from src/Greenshot/Drawing/Filters/PixelizationFilter.cs rename to src/Greenshot.Editor/Drawing/Filters/PixelizationFilter.cs index 7440b54a6..d0b88d4f7 100644 --- a/src/Greenshot/Drawing/Filters/PixelizationFilter.cs +++ b/src/Greenshot.Editor/Drawing/Filters/PixelizationFilter.cs @@ -1,105 +1,105 @@ -/* - * Greenshot - a free and open source screenshot tool - * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom - * - * For more information see: https://getgreenshot.org/ - * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 1 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -using System; -using System.Collections.Generic; -using System.Drawing; -using Greenshot.Base.Core; -using Greenshot.Base.Interfaces.Drawing; -using Greenshot.Drawing.Fields; -using Greenshot.Helpers; - -namespace Greenshot.Drawing.Filters -{ - [Serializable()] - public class PixelizationFilter : AbstractFilter - { - public PixelizationFilter(DrawableContainer parent) : base(parent) - { - AddField(GetType(), FieldType.PIXEL_SIZE, 5); - } - - public override void Apply(Graphics graphics, Bitmap applyBitmap, Rectangle rect, RenderMode renderMode) - { - int pixelSize = GetFieldValueAsInt(FieldType.PIXEL_SIZE); - ImageHelper.CreateIntersectRectangle(applyBitmap.Size, rect, Invert); - if (pixelSize <= 1 || rect.Width == 0 || rect.Height == 0) - { - // Nothing to do - return; - } - - if (rect.Width < pixelSize) - { - pixelSize = rect.Width; - } - - if (rect.Height < pixelSize) - { - pixelSize = rect.Height; - } - - using IFastBitmap dest = FastBitmap.CreateCloneOf(applyBitmap, rect); - using (IFastBitmap src = FastBitmap.Create(applyBitmap, rect)) - { - List colors = new List(); - int halbPixelSize = pixelSize / 2; - for (int y = src.Top - halbPixelSize; y < src.Bottom + halbPixelSize; y += pixelSize) - { - for (int x = src.Left - halbPixelSize; x <= src.Right + halbPixelSize; x += pixelSize) - { - colors.Clear(); - for (int yy = y; yy < y + pixelSize; yy++) - { - if (yy >= src.Top && yy < src.Bottom) - { - for (int xx = x; xx < x + pixelSize; xx++) - { - if (xx >= src.Left && xx < src.Right) - { - colors.Add(src.GetColorAt(xx, yy)); - } - } - } - } - - Color currentAvgColor = Colors.Mix(colors); - for (int yy = y; yy <= y + pixelSize; yy++) - { - if (yy >= src.Top && yy < src.Bottom) - { - for (int xx = x; xx <= x + pixelSize; xx++) - { - if (xx >= src.Left && xx < src.Right) - { - dest.SetColorAt(xx, yy, currentAvgColor); - } - } - } - } - } - } - } - - dest.DrawTo(graphics, rect.Location); - } - } +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using System; +using System.Collections.Generic; +using System.Drawing; +using Greenshot.Base.Core; +using Greenshot.Base.Interfaces.Drawing; +using Greenshot.Editor.Drawing.Fields; +using Greenshot.Editor.Helpers; + +namespace Greenshot.Editor.Drawing.Filters +{ + [Serializable()] + public class PixelizationFilter : AbstractFilter + { + public PixelizationFilter(DrawableContainer parent) : base(parent) + { + AddField(GetType(), FieldType.PIXEL_SIZE, 5); + } + + public override void Apply(Graphics graphics, Bitmap applyBitmap, Rectangle rect, RenderMode renderMode) + { + int pixelSize = GetFieldValueAsInt(FieldType.PIXEL_SIZE); + ImageHelper.CreateIntersectRectangle(applyBitmap.Size, rect, Invert); + if (pixelSize <= 1 || rect.Width == 0 || rect.Height == 0) + { + // Nothing to do + return; + } + + if (rect.Width < pixelSize) + { + pixelSize = rect.Width; + } + + if (rect.Height < pixelSize) + { + pixelSize = rect.Height; + } + + using IFastBitmap dest = FastBitmap.CreateCloneOf(applyBitmap, rect); + using (IFastBitmap src = FastBitmap.Create(applyBitmap, rect)) + { + List colors = new List(); + int halbPixelSize = pixelSize / 2; + for (int y = src.Top - halbPixelSize; y < src.Bottom + halbPixelSize; y += pixelSize) + { + for (int x = src.Left - halbPixelSize; x <= src.Right + halbPixelSize; x += pixelSize) + { + colors.Clear(); + for (int yy = y; yy < y + pixelSize; yy++) + { + if (yy >= src.Top && yy < src.Bottom) + { + for (int xx = x; xx < x + pixelSize; xx++) + { + if (xx >= src.Left && xx < src.Right) + { + colors.Add(src.GetColorAt(xx, yy)); + } + } + } + } + + Color currentAvgColor = Colors.Mix(colors); + for (int yy = y; yy <= y + pixelSize; yy++) + { + if (yy >= src.Top && yy < src.Bottom) + { + for (int xx = x; xx <= x + pixelSize; xx++) + { + if (xx >= src.Left && xx < src.Right) + { + dest.SetColorAt(xx, yy, currentAvgColor); + } + } + } + } + } + } + } + + dest.DrawTo(graphics, rect.Location); + } + } } \ No newline at end of file diff --git a/src/Greenshot/Drawing/FreehandContainer.cs b/src/Greenshot.Editor/Drawing/FreehandContainer.cs similarity index 96% rename from src/Greenshot/Drawing/FreehandContainer.cs rename to src/Greenshot.Editor/Drawing/FreehandContainer.cs index d62a2ee10..e37221551 100644 --- a/src/Greenshot/Drawing/FreehandContainer.cs +++ b/src/Greenshot.Editor/Drawing/FreehandContainer.cs @@ -1,323 +1,323 @@ -/* - * Greenshot - a free and open source screenshot tool - * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom - * - * For more information see: https://getgreenshot.org/ - * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 1 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -using Greenshot.Drawing.Fields; -using Greenshot.Helpers; -using System; -using System.Collections.Generic; -using System.Drawing; -using System.Drawing.Drawing2D; -using System.Runtime.Serialization; -using Greenshot.Base.Interfaces.Drawing; - -namespace Greenshot.Drawing -{ - /// - /// Description of PathContainer. - /// - [Serializable] - public class FreehandContainer : DrawableContainer - { - private static readonly float[] PointOffset = - { - 0.5f, 0.25f, 0.75f - }; - - [NonSerialized] private GraphicsPath freehandPath = new GraphicsPath(); - private Rectangle myBounds = Rectangle.Empty; - private Point lastMouse = Point.Empty; - private readonly List capturePoints = new List(); - private bool isRecalculated; - - /// - /// Constructor - /// - public FreehandContainer(Surface parent) : base(parent) - { - Width = parent.Image.Width; - Height = parent.Image.Height; - Top = 0; - Left = 0; - } - - protected override void InitializeFields() - { - AddField(GetType(), FieldType.LINE_THICKNESS, 3); - AddField(GetType(), FieldType.LINE_COLOR, Color.Red); - } - - public override void Transform(Matrix matrix) - { - Point[] points = capturePoints.ToArray(); - - matrix.TransformPoints(points); - capturePoints.Clear(); - capturePoints.AddRange(points); - RecalculatePath(); - } - - protected override void OnDeserialized(StreamingContext context) - { - RecalculatePath(); - } - - /// - /// This Dispose is called from the Dispose and the Destructor. - /// - /// When disposing==true all non-managed resources should be freed too! - protected override void Dispose(bool disposing) - { - base.Dispose(disposing); - if (disposing) - { - freehandPath?.Dispose(); - } - - freehandPath = null; - } - - /// - /// Called from Surface (the parent) when the drawing begins (mouse-down) - /// - /// true if the surface doesn't need to handle the event - public override bool HandleMouseDown(int mouseX, int mouseY) - { - lastMouse = new Point(mouseX, mouseY); - capturePoints.Add(lastMouse); - return true; - } - - /// - /// Called from Surface (the parent) if a mouse move is made while drawing - /// - /// true if the surface doesn't need to handle the event - public override bool HandleMouseMove(int mouseX, int mouseY) - { - Point previousPoint = capturePoints[capturePoints.Count - 1]; - - if (GeometryHelper.Distance2D(previousPoint.X, previousPoint.Y, mouseX, mouseY) >= 2 * EditorConfig.FreehandSensitivity) - { - capturePoints.Add(new Point(mouseX, mouseY)); - } - - if (GeometryHelper.Distance2D(lastMouse.X, lastMouse.Y, mouseX, mouseY) < EditorConfig.FreehandSensitivity) - { - return true; - } - - //path.AddCurve(new Point[]{lastMouse, new Point(mouseX, mouseY)}); - lastMouse = new Point(mouseX, mouseY); - freehandPath.AddLine(lastMouse, new Point(mouseX, mouseY)); - // Only re-calculate the bounds & redraw when we added something to the path - myBounds = Rectangle.Round(freehandPath.GetBounds()); - - Invalidate(); - return true; - } - - /// - /// Called when the surface finishes drawing the element - /// - public override void HandleMouseUp(int mouseX, int mouseY) - { - // Make sure we don't loose the ending point - if (GeometryHelper.Distance2D(lastMouse.X, lastMouse.Y, mouseX, mouseY) >= EditorConfig.FreehandSensitivity) - { - capturePoints.Add(new Point(mouseX, mouseY)); - } - - RecalculatePath(); - } - - /// - /// Here we recalculate the freehand path by smoothing out the lines with Beziers. - /// - private void RecalculatePath() - { - // Store the previous path, to dispose it later when we are finished - var previousFreehandPath = freehandPath; - var newFreehandPath = new GraphicsPath(); - - // Here we can put some cleanup... like losing all the uninteresting points. - if (capturePoints.Count >= 3) - { - int index = 0; - while ((capturePoints.Count - 1) % 3 != 0) - { - // duplicate points, first at 50% than 25% than 75% - capturePoints.Insert((int) (capturePoints.Count * PointOffset[index]), capturePoints[(int) (capturePoints.Count * PointOffset[index++])]); - } - - newFreehandPath.AddBeziers(capturePoints.ToArray()); - } - else if (capturePoints.Count == 2) - { - newFreehandPath.AddLine(capturePoints[0], capturePoints[1]); - } - - // Recalculate the bounds - myBounds = Rectangle.Round(newFreehandPath.GetBounds()); - - // assign - isRecalculated = true; - freehandPath = newFreehandPath; - - // dispose previous - previousFreehandPath?.Dispose(); - } - - /// - /// Do the drawing of the freehand "stroke" - /// - /// - /// - public override void Draw(Graphics graphics, RenderMode renderMode) - { - graphics.SmoothingMode = SmoothingMode.HighQuality; - graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; - graphics.CompositingQuality = CompositingQuality.HighQuality; - graphics.PixelOffsetMode = PixelOffsetMode.HighQuality; - - int lineThickness = GetFieldValueAsInt(FieldType.LINE_THICKNESS); - Color lineColor = GetFieldValueAsColor(FieldType.LINE_COLOR); - using var pen = new Pen(lineColor) - { - Width = lineThickness - }; - if (!(pen.Width > 0)) - { - return; - } - - // Make sure the lines are nicely rounded - pen.EndCap = LineCap.Round; - pen.StartCap = LineCap.Round; - pen.LineJoin = LineJoin.Round; - // Move to where we need to draw - graphics.TranslateTransform(Left, Top); - var currentFreehandPath = freehandPath; - if (currentFreehandPath != null) - { - if (isRecalculated && Selected && renderMode == RenderMode.EDIT) - { - isRecalculated = false; - DrawSelectionBorder(graphics, pen, currentFreehandPath); - } - - graphics.DrawPath(pen, currentFreehandPath); - } - - // Move back, otherwise everything is shifted - graphics.TranslateTransform(-Left, -Top); - } - - /// - /// Draw a selectionborder around the freehand path - /// - /// Graphics - /// Pen - /// GraphicsPath - protected static void DrawSelectionBorder(Graphics graphics, Pen linePen, GraphicsPath path) - { - using var selectionPen = (Pen) linePen.Clone(); - using var selectionPath = (GraphicsPath) path.Clone(); - selectionPen.Width += 5; - selectionPen.Color = Color.FromArgb(120, Color.LightSeaGreen); - graphics.DrawPath(selectionPen, selectionPath); - selectionPath.Widen(selectionPen); - selectionPen.DashPattern = new float[] - { - 2, 2 - }; - selectionPen.Color = Color.LightSeaGreen; - selectionPen.Width = 1; - graphics.DrawPath(selectionPen, selectionPath); - } - - /// - /// Get the bounds in which we have something drawn, plus safety margin, these are not the normal bounds... - /// - public override Rectangle DrawingBounds - { - get - { - if (!myBounds.IsEmpty) - { - int lineThickness = Math.Max(10, GetFieldValueAsInt(FieldType.LINE_THICKNESS)); - int safetymargin = 10; - return new Rectangle(myBounds.Left + Left - (safetymargin + lineThickness), myBounds.Top + Top - (safetymargin + lineThickness), - myBounds.Width + 2 * (lineThickness + safetymargin), myBounds.Height + 2 * (lineThickness + safetymargin)); - } - - if (_parent?.Image is Image image) - { - return new Rectangle(0, 0, image.Width, image.Height); - } - else - { - return Rectangle.Empty; - } - } - } - - /// - /// FreehandContainer are regarded equal if they are of the same type and their paths are equal. - /// - /// object - /// bool - public override bool Equals(object obj) - { - bool ret = false; - if (obj == null || GetType() != obj.GetType()) - { - return false; - } - - if (obj is FreehandContainer other && Equals(freehandPath, other.freehandPath)) - { - ret = true; - } - - return ret; - } - - public override int GetHashCode() - { - return freehandPath?.GetHashCode() ?? 0; - } - - public override bool ClickableAt(int x, int y) - { - bool returnValue = base.ClickableAt(x, y); - if (returnValue) - { - int lineThickness = GetFieldValueAsInt(FieldType.LINE_THICKNESS); - using var pen = new Pen(Color.White) - { - Width = lineThickness + 10 - }; - returnValue = freehandPath.IsOutlineVisible(x - Left, y - Top, pen); - } - - return returnValue; - } - } +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Drawing.Drawing2D; +using System.Runtime.Serialization; +using Greenshot.Base.Interfaces.Drawing; +using Greenshot.Editor.Drawing.Fields; +using Greenshot.Editor.Helpers; + +namespace Greenshot.Editor.Drawing +{ + /// + /// Description of PathContainer. + /// + [Serializable] + public class FreehandContainer : DrawableContainer + { + private static readonly float[] PointOffset = + { + 0.5f, 0.25f, 0.75f + }; + + [NonSerialized] private GraphicsPath freehandPath = new GraphicsPath(); + private Rectangle myBounds = Rectangle.Empty; + private Point lastMouse = Point.Empty; + private readonly List capturePoints = new List(); + private bool isRecalculated; + + /// + /// Constructor + /// + public FreehandContainer(Surface parent) : base(parent) + { + Width = parent.Image.Width; + Height = parent.Image.Height; + Top = 0; + Left = 0; + } + + protected override void InitializeFields() + { + AddField(GetType(), FieldType.LINE_THICKNESS, 3); + AddField(GetType(), FieldType.LINE_COLOR, Color.Red); + } + + public override void Transform(Matrix matrix) + { + Point[] points = capturePoints.ToArray(); + + matrix.TransformPoints(points); + capturePoints.Clear(); + capturePoints.AddRange(points); + RecalculatePath(); + } + + protected override void OnDeserialized(StreamingContext context) + { + RecalculatePath(); + } + + /// + /// This Dispose is called from the Dispose and the Destructor. + /// + /// When disposing==true all non-managed resources should be freed too! + protected override void Dispose(bool disposing) + { + base.Dispose(disposing); + if (disposing) + { + freehandPath?.Dispose(); + } + + freehandPath = null; + } + + /// + /// Called from Surface (the parent) when the drawing begins (mouse-down) + /// + /// true if the surface doesn't need to handle the event + public override bool HandleMouseDown(int mouseX, int mouseY) + { + lastMouse = new Point(mouseX, mouseY); + capturePoints.Add(lastMouse); + return true; + } + + /// + /// Called from Surface (the parent) if a mouse move is made while drawing + /// + /// true if the surface doesn't need to handle the event + public override bool HandleMouseMove(int mouseX, int mouseY) + { + Point previousPoint = capturePoints[capturePoints.Count - 1]; + + if (GeometryHelper.Distance2D(previousPoint.X, previousPoint.Y, mouseX, mouseY) >= 2 * EditorConfig.FreehandSensitivity) + { + capturePoints.Add(new Point(mouseX, mouseY)); + } + + if (GeometryHelper.Distance2D(lastMouse.X, lastMouse.Y, mouseX, mouseY) < EditorConfig.FreehandSensitivity) + { + return true; + } + + //path.AddCurve(new Point[]{lastMouse, new Point(mouseX, mouseY)}); + lastMouse = new Point(mouseX, mouseY); + freehandPath.AddLine(lastMouse, new Point(mouseX, mouseY)); + // Only re-calculate the bounds & redraw when we added something to the path + myBounds = Rectangle.Round(freehandPath.GetBounds()); + + Invalidate(); + return true; + } + + /// + /// Called when the surface finishes drawing the element + /// + public override void HandleMouseUp(int mouseX, int mouseY) + { + // Make sure we don't loose the ending point + if (GeometryHelper.Distance2D(lastMouse.X, lastMouse.Y, mouseX, mouseY) >= EditorConfig.FreehandSensitivity) + { + capturePoints.Add(new Point(mouseX, mouseY)); + } + + RecalculatePath(); + } + + /// + /// Here we recalculate the freehand path by smoothing out the lines with Beziers. + /// + private void RecalculatePath() + { + // Store the previous path, to dispose it later when we are finished + var previousFreehandPath = freehandPath; + var newFreehandPath = new GraphicsPath(); + + // Here we can put some cleanup... like losing all the uninteresting points. + if (capturePoints.Count >= 3) + { + int index = 0; + while ((capturePoints.Count - 1) % 3 != 0) + { + // duplicate points, first at 50% than 25% than 75% + capturePoints.Insert((int) (capturePoints.Count * PointOffset[index]), capturePoints[(int) (capturePoints.Count * PointOffset[index++])]); + } + + newFreehandPath.AddBeziers(capturePoints.ToArray()); + } + else if (capturePoints.Count == 2) + { + newFreehandPath.AddLine(capturePoints[0], capturePoints[1]); + } + + // Recalculate the bounds + myBounds = Rectangle.Round(newFreehandPath.GetBounds()); + + // assign + isRecalculated = true; + freehandPath = newFreehandPath; + + // dispose previous + previousFreehandPath?.Dispose(); + } + + /// + /// Do the drawing of the freehand "stroke" + /// + /// + /// + public override void Draw(Graphics graphics, RenderMode renderMode) + { + graphics.SmoothingMode = SmoothingMode.HighQuality; + graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; + graphics.CompositingQuality = CompositingQuality.HighQuality; + graphics.PixelOffsetMode = PixelOffsetMode.HighQuality; + + int lineThickness = GetFieldValueAsInt(FieldType.LINE_THICKNESS); + Color lineColor = GetFieldValueAsColor(FieldType.LINE_COLOR); + using var pen = new Pen(lineColor) + { + Width = lineThickness + }; + if (!(pen.Width > 0)) + { + return; + } + + // Make sure the lines are nicely rounded + pen.EndCap = LineCap.Round; + pen.StartCap = LineCap.Round; + pen.LineJoin = LineJoin.Round; + // Move to where we need to draw + graphics.TranslateTransform(Left, Top); + var currentFreehandPath = freehandPath; + if (currentFreehandPath != null) + { + if (isRecalculated && Selected && renderMode == RenderMode.EDIT) + { + isRecalculated = false; + DrawSelectionBorder(graphics, pen, currentFreehandPath); + } + + graphics.DrawPath(pen, currentFreehandPath); + } + + // Move back, otherwise everything is shifted + graphics.TranslateTransform(-Left, -Top); + } + + /// + /// Draw a selectionborder around the freehand path + /// + /// Graphics + /// Pen + /// GraphicsPath + protected static void DrawSelectionBorder(Graphics graphics, Pen linePen, GraphicsPath path) + { + using var selectionPen = (Pen) linePen.Clone(); + using var selectionPath = (GraphicsPath) path.Clone(); + selectionPen.Width += 5; + selectionPen.Color = Color.FromArgb(120, Color.LightSeaGreen); + graphics.DrawPath(selectionPen, selectionPath); + selectionPath.Widen(selectionPen); + selectionPen.DashPattern = new float[] + { + 2, 2 + }; + selectionPen.Color = Color.LightSeaGreen; + selectionPen.Width = 1; + graphics.DrawPath(selectionPen, selectionPath); + } + + /// + /// Get the bounds in which we have something drawn, plus safety margin, these are not the normal bounds... + /// + public override Rectangle DrawingBounds + { + get + { + if (!myBounds.IsEmpty) + { + int lineThickness = Math.Max(10, GetFieldValueAsInt(FieldType.LINE_THICKNESS)); + int safetymargin = 10; + return new Rectangle(myBounds.Left + Left - (safetymargin + lineThickness), myBounds.Top + Top - (safetymargin + lineThickness), + myBounds.Width + 2 * (lineThickness + safetymargin), myBounds.Height + 2 * (lineThickness + safetymargin)); + } + + if (_parent?.Image is Image image) + { + return new Rectangle(0, 0, image.Width, image.Height); + } + else + { + return Rectangle.Empty; + } + } + } + + /// + /// FreehandContainer are regarded equal if they are of the same type and their paths are equal. + /// + /// object + /// bool + public override bool Equals(object obj) + { + bool ret = false; + if (obj == null || GetType() != obj.GetType()) + { + return false; + } + + if (obj is FreehandContainer other && Equals(freehandPath, other.freehandPath)) + { + ret = true; + } + + return ret; + } + + public override int GetHashCode() + { + return freehandPath?.GetHashCode() ?? 0; + } + + public override bool ClickableAt(int x, int y) + { + bool returnValue = base.ClickableAt(x, y); + if (returnValue) + { + int lineThickness = GetFieldValueAsInt(FieldType.LINE_THICKNESS); + using var pen = new Pen(Color.White) + { + Width = lineThickness + 10 + }; + returnValue = freehandPath.IsOutlineVisible(x - Left, y - Top, pen); + } + + return returnValue; + } + } } \ No newline at end of file diff --git a/src/Greenshot/Drawing/HighlightContainer.cs b/src/Greenshot.Editor/Drawing/HighlightContainer.cs similarity index 94% rename from src/Greenshot/Drawing/HighlightContainer.cs rename to src/Greenshot.Editor/Drawing/HighlightContainer.cs index c9b502f06..3e104b85f 100644 --- a/src/Greenshot/Drawing/HighlightContainer.cs +++ b/src/Greenshot.Editor/Drawing/HighlightContainer.cs @@ -1,112 +1,112 @@ -/* - * Greenshot - a free and open source screenshot tool - * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom - * - * For more information see: https://getgreenshot.org/ - * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 1 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -using System; -using System.Runtime.Serialization; -using Greenshot.Base.Interfaces.Drawing; -using Greenshot.Drawing.Fields; -using Greenshot.Drawing.Filters; - -namespace Greenshot.Drawing -{ - /// - /// Description of ObfuscateContainer. - /// - [Serializable] - public class HighlightContainer : FilterContainer - { - public HighlightContainer(Surface parent) : base(parent) - { - Init(); - } - - /// - /// Use settings from base, extend with our own field - /// - protected override void InitializeFields() - { - base.InitializeFields(); - AddField(GetType(), FieldType.PREPARED_FILTER_HIGHLIGHT, PreparedFilter.TEXT_HIGHTLIGHT); - } - - protected override void OnDeserialized(StreamingContext context) - { - Init(); - } - - private void Init() - { - FieldChanged += HighlightContainer_OnFieldChanged; - ConfigurePreparedFilters(); - } - - protected void HighlightContainer_OnFieldChanged(object sender, FieldChangedEventArgs e) - { - if (!sender.Equals(this)) - { - return; - } - - if (Equals(e.Field.FieldType, FieldType.PREPARED_FILTER_HIGHLIGHT)) - { - ConfigurePreparedFilters(); - } - } - - private void ConfigurePreparedFilters() - { - PreparedFilter preset = (PreparedFilter) GetFieldValue(FieldType.PREPARED_FILTER_HIGHLIGHT); - while (Filters.Count > 0) - { - Remove(Filters[0]); - } - - switch (preset) - { - case PreparedFilter.TEXT_HIGHTLIGHT: - Add(new HighlightFilter(this)); - break; - case PreparedFilter.AREA_HIGHLIGHT: - var brightnessFilter = new BrightnessFilter(this) - { - Invert = true - }; - Add(brightnessFilter); - var blurFilter = new BlurFilter(this) - { - Invert = true - }; - Add(blurFilter); - break; - case PreparedFilter.GRAYSCALE: - AbstractFilter f = new GrayscaleFilter(this) - { - Invert = true - }; - Add(f); - break; - case PreparedFilter.MAGNIFICATION: - Add(new MagnifierFilter(this)); - break; - } - } - } +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using System; +using System.Runtime.Serialization; +using Greenshot.Base.Interfaces.Drawing; +using Greenshot.Editor.Drawing.Fields; +using Greenshot.Editor.Drawing.Filters; + +namespace Greenshot.Editor.Drawing +{ + /// + /// Description of ObfuscateContainer. + /// + [Serializable] + public class HighlightContainer : FilterContainer + { + public HighlightContainer(Surface parent) : base(parent) + { + Init(); + } + + /// + /// Use settings from base, extend with our own field + /// + protected override void InitializeFields() + { + base.InitializeFields(); + AddField(GetType(), FieldType.PREPARED_FILTER_HIGHLIGHT, PreparedFilter.TEXT_HIGHTLIGHT); + } + + protected override void OnDeserialized(StreamingContext context) + { + Init(); + } + + private void Init() + { + FieldChanged += HighlightContainer_OnFieldChanged; + ConfigurePreparedFilters(); + } + + protected void HighlightContainer_OnFieldChanged(object sender, FieldChangedEventArgs e) + { + if (!sender.Equals(this)) + { + return; + } + + if (Equals(e.Field.FieldType, FieldType.PREPARED_FILTER_HIGHLIGHT)) + { + ConfigurePreparedFilters(); + } + } + + private void ConfigurePreparedFilters() + { + PreparedFilter preset = (PreparedFilter) GetFieldValue(FieldType.PREPARED_FILTER_HIGHLIGHT); + while (Filters.Count > 0) + { + Remove(Filters[0]); + } + + switch (preset) + { + case PreparedFilter.TEXT_HIGHTLIGHT: + Add(new HighlightFilter(this)); + break; + case PreparedFilter.AREA_HIGHLIGHT: + var brightnessFilter = new BrightnessFilter(this) + { + Invert = true + }; + Add(brightnessFilter); + var blurFilter = new BlurFilter(this) + { + Invert = true + }; + Add(blurFilter); + break; + case PreparedFilter.GRAYSCALE: + AbstractFilter f = new GrayscaleFilter(this) + { + Invert = true + }; + Add(f); + break; + case PreparedFilter.MAGNIFICATION: + Add(new MagnifierFilter(this)); + break; + } + } + } } \ No newline at end of file diff --git a/src/Greenshot/Drawing/IconContainer.cs b/src/Greenshot.Editor/Drawing/IconContainer.cs similarity index 95% rename from src/Greenshot/Drawing/IconContainer.cs rename to src/Greenshot.Editor/Drawing/IconContainer.cs index 766126219..e98b37f82 100644 --- a/src/Greenshot/Drawing/IconContainer.cs +++ b/src/Greenshot.Editor/Drawing/IconContainer.cs @@ -1,120 +1,120 @@ -/* - * Greenshot - a free and open source screenshot tool - * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom - * - * For more information see: https://getgreenshot.org/ - * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 1 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -using System; -using System.Drawing; -using System.IO; -using System.Drawing.Drawing2D; -using log4net; -using System.Runtime.Serialization; -using Greenshot.Base.Interfaces.Drawing; - -namespace Greenshot.Drawing -{ - /// - /// Description of IconContainer. - /// - [Serializable] - public class IconContainer : DrawableContainer, IIconContainer - { - private static readonly ILog Log = LogManager.GetLogger(typeof(IconContainer)); - - 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) - { - Load(filename); - } - - public Icon Icon - { - set - { - icon?.Dispose(); - icon = (Icon) value.Clone(); - Width = value.Width; - Height = value.Height; - } - get => icon; - } - - /** - * This Dispose is called from the Dispose and the Destructor. - * When disposing==true all non-managed resources should be freed too! - */ - protected override void Dispose(bool disposing) - { - if (disposing) - { - icon?.Dispose(); - } - - icon = null; - base.Dispose(disposing); - } - - public void Load(string filename) - { - if (!File.Exists(filename)) - { - return; - } - - using Icon fileIcon = new Icon(filename); - Icon = fileIcon; - Log.Debug("Loaded file: " + filename + " with resolution: " + Height + "," + Width); - } - - public override void Draw(Graphics graphics, RenderMode rm) - { - if (icon == null) - { - return; - } - - graphics.SmoothingMode = SmoothingMode.HighQuality; - graphics.InterpolationMode = InterpolationMode.NearestNeighbor; - graphics.CompositingQuality = CompositingQuality.Default; - graphics.PixelOffsetMode = PixelOffsetMode.HighQuality; - graphics.DrawIcon(icon, Bounds); - } - - public override bool HasDefaultSize => true; - - public override Size DefaultSize => icon?.Size ?? new Size(16, 16); - } +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using System; +using System.Drawing; +using System.Drawing.Drawing2D; +using System.IO; +using System.Runtime.Serialization; +using Greenshot.Base.Interfaces.Drawing; +using log4net; + +namespace Greenshot.Editor.Drawing +{ + /// + /// Description of IconContainer. + /// + [Serializable] + public class IconContainer : DrawableContainer, IIconContainer + { + private static readonly ILog Log = LogManager.GetLogger(typeof(IconContainer)); + + 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) + { + Load(filename); + } + + public Icon Icon + { + set + { + icon?.Dispose(); + icon = (Icon) value.Clone(); + Width = value.Width; + Height = value.Height; + } + get => icon; + } + + /** + * This Dispose is called from the Dispose and the Destructor. + * When disposing==true all non-managed resources should be freed too! + */ + protected override void Dispose(bool disposing) + { + if (disposing) + { + icon?.Dispose(); + } + + icon = null; + base.Dispose(disposing); + } + + public void Load(string filename) + { + if (!File.Exists(filename)) + { + return; + } + + using Icon fileIcon = new Icon(filename); + Icon = fileIcon; + Log.Debug("Loaded file: " + filename + " with resolution: " + Height + "," + Width); + } + + public override void Draw(Graphics graphics, RenderMode rm) + { + if (icon == null) + { + return; + } + + graphics.SmoothingMode = SmoothingMode.HighQuality; + graphics.InterpolationMode = InterpolationMode.NearestNeighbor; + graphics.CompositingQuality = CompositingQuality.Default; + graphics.PixelOffsetMode = PixelOffsetMode.HighQuality; + graphics.DrawIcon(icon, Bounds); + } + + public override bool HasDefaultSize => true; + + public override Size DefaultSize => icon?.Size ?? new Size(16, 16); + } } \ No newline at end of file diff --git a/src/Greenshot/Drawing/ImageContainer.cs b/src/Greenshot.Editor/Drawing/ImageContainer.cs similarity index 96% rename from src/Greenshot/Drawing/ImageContainer.cs rename to src/Greenshot.Editor/Drawing/ImageContainer.cs index 631fe56ce..a46b0dae5 100644 --- a/src/Greenshot/Drawing/ImageContainer.cs +++ b/src/Greenshot.Editor/Drawing/ImageContainer.cs @@ -1,267 +1,267 @@ -/* - * Greenshot - a free and open source screenshot tool - * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom - * - * For more information see: https://getgreenshot.org/ - * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 1 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -using System; -using System.Drawing; -using System.IO; -using Greenshot.Drawing.Fields; -using System.Drawing.Drawing2D; -using log4net; -using System.Runtime.Serialization; -using Greenshot.Base.Core; -using Greenshot.Base.Effects; -using Greenshot.Base.Interfaces.Drawing; - -namespace Greenshot.Drawing -{ - /// - /// Description of BitmapContainer. - /// - [Serializable] - public class ImageContainer : DrawableContainer, IImageContainer - { - private static readonly ILog Log = LogManager.GetLogger(typeof(ImageContainer)); - - private Image image; - - /// - /// This is the shadow version of the bitmap, rendered once to save performance - /// Do not serialize, as the shadow is recreated from the original bitmap if it's not available - /// - [NonSerialized] private Image _shadowBitmap; - - /// - /// This is the offset for the shadow version of the bitmap - /// Do not serialize, as the offset is recreated - /// - [NonSerialized] private Point _shadowOffset = new Point(-1, -1); - - public ImageContainer(Surface parent, string filename) : this(parent) - { - Load(filename); - } - - 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() - { - AddField(GetType(), FieldType.SHADOW, false); - } - - protected void BitmapContainer_OnFieldChanged(object sender, FieldChangedEventArgs e) - { - if (!sender.Equals(this)) - { - return; - } - - if (FieldType.SHADOW.Equals(e.Field.FieldType)) - { - ChangeShadowField(); - } - } - - public void ChangeShadowField() - { - bool shadow = GetFieldValueAsBool(FieldType.SHADOW); - if (shadow) - { - CheckShadow(true); - Width = _shadowBitmap.Width; - Height = _shadowBitmap.Height; - Left -= _shadowOffset.X; - Top -= _shadowOffset.Y; - } - else - { - Width = image.Width; - Height = image.Height; - if (_shadowBitmap != null) - { - Left += _shadowOffset.X; - Top += _shadowOffset.Y; - } - } - } - - public Image Image - { - set - { - // Remove all current bitmaps - DisposeImage(); - DisposeShadow(); - image = ImageHelper.Clone(value); - bool shadow = GetFieldValueAsBool(FieldType.SHADOW); - CheckShadow(shadow); - if (!shadow) - { - Width = image.Width; - Height = image.Height; - } - else - { - Width = _shadowBitmap.Width; - Height = _shadowBitmap.Height; - Left -= _shadowOffset.X; - Top -= _shadowOffset.Y; - } - } - get { return image; } - } - - /// - /// The bulk of the clean-up code is implemented in Dispose(bool) - /// This Dispose is called from the Dispose and the Destructor. - /// When disposing==true all non-managed resources should be freed too! - /// - /// - protected override void Dispose(bool disposing) - { - if (disposing) - { - DisposeImage(); - DisposeShadow(); - } - - base.Dispose(disposing); - } - - private void DisposeImage() - { - image?.Dispose(); - image = null; - } - - private void DisposeShadow() - { - _shadowBitmap?.Dispose(); - _shadowBitmap = null; - } - - - /// - /// Make sure the content is also transformed. - /// - /// - public override void Transform(Matrix matrix) - { - int rotateAngle = CalculateAngle(matrix); - // we currently assume only one transformation has been made. - if (rotateAngle != 0) - { - Log.DebugFormat("Rotating element with {0} degrees.", rotateAngle); - DisposeShadow(); - using var tmpMatrix = new Matrix(); - using (image) - { - image = ImageHelper.ApplyEffect(image, new RotateEffect(rotateAngle), tmpMatrix); - } - } - - base.Transform(matrix); - } - - /// - /// - /// - /// - public void Load(string filename) - { - if (!File.Exists(filename)) - { - return; - } - - // Always make sure ImageHelper.LoadBitmap results are disposed some time, - // as we close the bitmap internally, we need to do it afterwards - using (var tmpImage = ImageHelper.LoadImage(filename)) - { - Image = tmpImage; - } - - Log.Debug("Loaded file: " + filename + " with resolution: " + Height + "," + Width); - } - - /// - /// This checks if a shadow is already generated - /// - /// - private void CheckShadow(bool shadow) - { - if (!shadow || _shadowBitmap != null) - { - return; - } - - using var matrix = new Matrix(); - _shadowBitmap = ImageHelper.ApplyEffect(image, new DropShadowEffect(), matrix); - } - - /// - /// Draw the actual container to the graphics object - /// - /// - /// - public override void Draw(Graphics graphics, RenderMode rm) - { - if (image == null) - { - return; - } - - bool shadow = GetFieldValueAsBool(FieldType.SHADOW); - graphics.SmoothingMode = SmoothingMode.HighQuality; - graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; - graphics.CompositingQuality = CompositingQuality.HighQuality; - graphics.PixelOffsetMode = PixelOffsetMode.HighQuality; - - if (shadow) - { - CheckShadow(true); - graphics.DrawImage(_shadowBitmap, Bounds); - } - else - { - graphics.DrawImage(image, Bounds); - } - } - - public override bool HasDefaultSize => true; - - public override Size DefaultSize => image?.Size ?? new Size(32, 32); - } +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using System; +using System.Drawing; +using System.Drawing.Drawing2D; +using System.IO; +using System.Runtime.Serialization; +using Greenshot.Base.Core; +using Greenshot.Base.Effects; +using Greenshot.Base.Interfaces.Drawing; +using Greenshot.Editor.Drawing.Fields; +using log4net; + +namespace Greenshot.Editor.Drawing +{ + /// + /// Description of BitmapContainer. + /// + [Serializable] + public class ImageContainer : DrawableContainer, IImageContainer + { + private static readonly ILog Log = LogManager.GetLogger(typeof(ImageContainer)); + + private Image image; + + /// + /// This is the shadow version of the bitmap, rendered once to save performance + /// Do not serialize, as the shadow is recreated from the original bitmap if it's not available + /// + [NonSerialized] private Image _shadowBitmap; + + /// + /// This is the offset for the shadow version of the bitmap + /// Do not serialize, as the offset is recreated + /// + [NonSerialized] private Point _shadowOffset = new Point(-1, -1); + + public ImageContainer(Surface parent, string filename) : this(parent) + { + Load(filename); + } + + 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() + { + AddField(GetType(), FieldType.SHADOW, false); + } + + protected void BitmapContainer_OnFieldChanged(object sender, FieldChangedEventArgs e) + { + if (!sender.Equals(this)) + { + return; + } + + if (FieldType.SHADOW.Equals(e.Field.FieldType)) + { + ChangeShadowField(); + } + } + + public void ChangeShadowField() + { + bool shadow = GetFieldValueAsBool(FieldType.SHADOW); + if (shadow) + { + CheckShadow(true); + Width = _shadowBitmap.Width; + Height = _shadowBitmap.Height; + Left -= _shadowOffset.X; + Top -= _shadowOffset.Y; + } + else + { + Width = image.Width; + Height = image.Height; + if (_shadowBitmap != null) + { + Left += _shadowOffset.X; + Top += _shadowOffset.Y; + } + } + } + + public Image Image + { + set + { + // Remove all current bitmaps + DisposeImage(); + DisposeShadow(); + image = ImageHelper.Clone(value); + bool shadow = GetFieldValueAsBool(FieldType.SHADOW); + CheckShadow(shadow); + if (!shadow) + { + Width = image.Width; + Height = image.Height; + } + else + { + Width = _shadowBitmap.Width; + Height = _shadowBitmap.Height; + Left -= _shadowOffset.X; + Top -= _shadowOffset.Y; + } + } + get { return image; } + } + + /// + /// The bulk of the clean-up code is implemented in Dispose(bool) + /// This Dispose is called from the Dispose and the Destructor. + /// When disposing==true all non-managed resources should be freed too! + /// + /// + protected override void Dispose(bool disposing) + { + if (disposing) + { + DisposeImage(); + DisposeShadow(); + } + + base.Dispose(disposing); + } + + private void DisposeImage() + { + image?.Dispose(); + image = null; + } + + private void DisposeShadow() + { + _shadowBitmap?.Dispose(); + _shadowBitmap = null; + } + + + /// + /// Make sure the content is also transformed. + /// + /// + public override void Transform(Matrix matrix) + { + int rotateAngle = CalculateAngle(matrix); + // we currently assume only one transformation has been made. + if (rotateAngle != 0) + { + Log.DebugFormat("Rotating element with {0} degrees.", rotateAngle); + DisposeShadow(); + using var tmpMatrix = new Matrix(); + using (image) + { + image = ImageHelper.ApplyEffect(image, new RotateEffect(rotateAngle), tmpMatrix); + } + } + + base.Transform(matrix); + } + + /// + /// + /// + /// + public void Load(string filename) + { + if (!File.Exists(filename)) + { + return; + } + + // Always make sure ImageHelper.LoadBitmap results are disposed some time, + // as we close the bitmap internally, we need to do it afterwards + using (var tmpImage = ImageHelper.LoadImage(filename)) + { + Image = tmpImage; + } + + Log.Debug("Loaded file: " + filename + " with resolution: " + Height + "," + Width); + } + + /// + /// This checks if a shadow is already generated + /// + /// + private void CheckShadow(bool shadow) + { + if (!shadow || _shadowBitmap != null) + { + return; + } + + using var matrix = new Matrix(); + _shadowBitmap = ImageHelper.ApplyEffect(image, new DropShadowEffect(), matrix); + } + + /// + /// Draw the actual container to the graphics object + /// + /// + /// + public override void Draw(Graphics graphics, RenderMode rm) + { + if (image == null) + { + return; + } + + bool shadow = GetFieldValueAsBool(FieldType.SHADOW); + graphics.SmoothingMode = SmoothingMode.HighQuality; + graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; + graphics.CompositingQuality = CompositingQuality.HighQuality; + graphics.PixelOffsetMode = PixelOffsetMode.HighQuality; + + if (shadow) + { + CheckShadow(true); + graphics.DrawImage(_shadowBitmap, Bounds); + } + else + { + graphics.DrawImage(image, Bounds); + } + } + + public override bool HasDefaultSize => true; + + public override Size DefaultSize => image?.Size ?? new Size(32, 32); + } } \ No newline at end of file diff --git a/src/Greenshot/Drawing/LineContainer.cs b/src/Greenshot.Editor/Drawing/LineContainer.cs similarity index 94% rename from src/Greenshot/Drawing/LineContainer.cs rename to src/Greenshot.Editor/Drawing/LineContainer.cs index 4f74f295d..1bcf25a81 100644 --- a/src/Greenshot/Drawing/LineContainer.cs +++ b/src/Greenshot.Editor/Drawing/LineContainer.cs @@ -1,123 +1,123 @@ -/* - * Greenshot - a free and open source screenshot tool - * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom - * - * For more information see: https://getgreenshot.org/ - * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 1 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -using System; -using System.Drawing; -using System.Drawing.Drawing2D; -using System.Runtime.Serialization; -using Greenshot.Base.Interfaces.Drawing; -using Greenshot.Drawing.Fields; -using Greenshot.Helpers; -using Greenshot.Drawing.Adorners; - -namespace Greenshot.Drawing -{ - /// - /// Description of LineContainer. - /// - [Serializable()] - public class LineContainer : DrawableContainer - { - public LineContainer(Surface parent) : base(parent) - { - Init(); - } - - protected override void InitializeFields() - { - AddField(GetType(), FieldType.LINE_THICKNESS, 2); - AddField(GetType(), FieldType.LINE_COLOR, Color.Red); - AddField(GetType(), FieldType.SHADOW, true); - } - - protected override void OnDeserialized(StreamingContext context) - { - Init(); - } - - protected void Init() - { - Adorners.Add(new MoveAdorner(this, Positions.TopLeft)); - Adorners.Add(new MoveAdorner(this, Positions.BottomRight)); - } - - public override void Draw(Graphics graphics, RenderMode rm) - { - graphics.SmoothingMode = SmoothingMode.HighQuality; - graphics.InterpolationMode = InterpolationMode.HighQualityBilinear; - graphics.CompositingQuality = CompositingQuality.HighQuality; - graphics.PixelOffsetMode = PixelOffsetMode.None; - - int lineThickness = GetFieldValueAsInt(FieldType.LINE_THICKNESS); - Color lineColor = GetFieldValueAsColor(FieldType.LINE_COLOR); - bool shadow = GetFieldValueAsBool(FieldType.SHADOW); - - if (lineThickness > 0) - { - if (shadow) - { - //draw shadow first - int basealpha = 100; - int alpha = basealpha; - int steps = 5; - int currentStep = 1; - while (currentStep <= steps) - { - using Pen shadowCapPen = new Pen(Color.FromArgb(alpha, 100, 100, 100), lineThickness); - graphics.DrawLine(shadowCapPen, - Left + currentStep, - Top + currentStep, - Left + currentStep + Width, - Top + currentStep + Height); - - currentStep++; - alpha -= basealpha / steps; - } - } - - using Pen pen = new Pen(lineColor, lineThickness); - graphics.DrawLine(pen, Left, Top, Left + Width, Top + Height); - } - } - - public override bool ClickableAt(int x, int y) - { - int lineThickness = GetFieldValueAsInt(FieldType.LINE_THICKNESS) + 5; - if (lineThickness > 0) - { - using Pen pen = new Pen(Color.White) - { - Width = lineThickness - }; - using GraphicsPath path = new GraphicsPath(); - path.AddLine(Left, Top, Left + Width, Top + Height); - return path.IsOutlineVisible(x, y, pen); - } - - return false; - } - - protected override ScaleHelper.IDoubleProcessor GetAngleRoundProcessor() - { - return ScaleHelper.LineAngleRoundBehavior.Instance; - } - } +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using System; +using System.Drawing; +using System.Drawing.Drawing2D; +using System.Runtime.Serialization; +using Greenshot.Base.Interfaces.Drawing; +using Greenshot.Editor.Drawing.Adorners; +using Greenshot.Editor.Drawing.Fields; +using Greenshot.Editor.Helpers; + +namespace Greenshot.Editor.Drawing +{ + /// + /// Description of LineContainer. + /// + [Serializable()] + public class LineContainer : DrawableContainer + { + public LineContainer(Surface parent) : base(parent) + { + Init(); + } + + protected override void InitializeFields() + { + AddField(GetType(), FieldType.LINE_THICKNESS, 2); + AddField(GetType(), FieldType.LINE_COLOR, Color.Red); + AddField(GetType(), FieldType.SHADOW, true); + } + + protected override void OnDeserialized(StreamingContext context) + { + Init(); + } + + protected void Init() + { + Adorners.Add(new MoveAdorner(this, Positions.TopLeft)); + Adorners.Add(new MoveAdorner(this, Positions.BottomRight)); + } + + public override void Draw(Graphics graphics, RenderMode rm) + { + graphics.SmoothingMode = SmoothingMode.HighQuality; + graphics.InterpolationMode = InterpolationMode.HighQualityBilinear; + graphics.CompositingQuality = CompositingQuality.HighQuality; + graphics.PixelOffsetMode = PixelOffsetMode.None; + + int lineThickness = GetFieldValueAsInt(FieldType.LINE_THICKNESS); + Color lineColor = GetFieldValueAsColor(FieldType.LINE_COLOR); + bool shadow = GetFieldValueAsBool(FieldType.SHADOW); + + if (lineThickness > 0) + { + if (shadow) + { + //draw shadow first + int basealpha = 100; + int alpha = basealpha; + int steps = 5; + int currentStep = 1; + while (currentStep <= steps) + { + using Pen shadowCapPen = new Pen(Color.FromArgb(alpha, 100, 100, 100), lineThickness); + graphics.DrawLine(shadowCapPen, + Left + currentStep, + Top + currentStep, + Left + currentStep + Width, + Top + currentStep + Height); + + currentStep++; + alpha -= basealpha / steps; + } + } + + using Pen pen = new Pen(lineColor, lineThickness); + graphics.DrawLine(pen, Left, Top, Left + Width, Top + Height); + } + } + + public override bool ClickableAt(int x, int y) + { + int lineThickness = GetFieldValueAsInt(FieldType.LINE_THICKNESS) + 5; + if (lineThickness > 0) + { + using Pen pen = new Pen(Color.White) + { + Width = lineThickness + }; + using GraphicsPath path = new GraphicsPath(); + path.AddLine(Left, Top, Left + Width, Top + Height); + return path.IsOutlineVisible(x, y, pen); + } + + return false; + } + + protected override ScaleHelper.IDoubleProcessor GetAngleRoundProcessor() + { + return ScaleHelper.LineAngleRoundBehavior.Instance; + } + } } \ No newline at end of file diff --git a/src/Greenshot/Drawing/ObfuscateContainer.cs b/src/Greenshot.Editor/Drawing/ObfuscateContainer.cs similarity index 93% rename from src/Greenshot/Drawing/ObfuscateContainer.cs rename to src/Greenshot.Editor/Drawing/ObfuscateContainer.cs index 9709dffde..1754ba650 100644 --- a/src/Greenshot/Drawing/ObfuscateContainer.cs +++ b/src/Greenshot.Editor/Drawing/ObfuscateContainer.cs @@ -1,89 +1,89 @@ -/* - * Greenshot - a free and open source screenshot tool - * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom - * - * For more information see: https://getgreenshot.org/ - * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 1 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -using System; -using System.Runtime.Serialization; -using Greenshot.Base.Interfaces.Drawing; -using Greenshot.Drawing.Fields; -using Greenshot.Drawing.Filters; - -namespace Greenshot.Drawing -{ - /// - /// Description of ObfuscateContainer. - /// - [Serializable] - public class ObfuscateContainer : FilterContainer - { - public ObfuscateContainer(Surface parent) : base(parent) - { - Init(); - } - - protected override void InitializeFields() - { - base.InitializeFields(); - AddField(GetType(), FieldType.PREPARED_FILTER_OBFUSCATE, PreparedFilter.PIXELIZE); - } - - protected override void OnDeserialized(StreamingContext context) - { - Init(); - } - - private void Init() - { - FieldChanged += ObfuscateContainer_OnFieldChanged; - ConfigurePreparedFilters(); - CreateDefaultAdorners(); - } - - protected void ObfuscateContainer_OnFieldChanged(object sender, FieldChangedEventArgs e) - { - if (sender.Equals(this)) - { - if (Equals(e.Field.FieldType, FieldType.PREPARED_FILTER_OBFUSCATE)) - { - ConfigurePreparedFilters(); - } - } - } - - private void ConfigurePreparedFilters() - { - PreparedFilter preset = (PreparedFilter) GetFieldValue(FieldType.PREPARED_FILTER_OBFUSCATE); - while (Filters.Count > 0) - { - Remove(Filters[0]); - } - - switch (preset) - { - case PreparedFilter.BLUR: - Add(new BlurFilter(this)); - break; - case PreparedFilter.PIXELIZE: - Add(new PixelizationFilter(this)); - break; - } - } - } +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using System; +using System.Runtime.Serialization; +using Greenshot.Base.Interfaces.Drawing; +using Greenshot.Editor.Drawing.Fields; +using Greenshot.Editor.Drawing.Filters; + +namespace Greenshot.Editor.Drawing +{ + /// + /// Description of ObfuscateContainer. + /// + [Serializable] + public class ObfuscateContainer : FilterContainer + { + public ObfuscateContainer(Surface parent) : base(parent) + { + Init(); + } + + protected override void InitializeFields() + { + base.InitializeFields(); + AddField(GetType(), FieldType.PREPARED_FILTER_OBFUSCATE, PreparedFilter.PIXELIZE); + } + + protected override void OnDeserialized(StreamingContext context) + { + Init(); + } + + private void Init() + { + FieldChanged += ObfuscateContainer_OnFieldChanged; + ConfigurePreparedFilters(); + CreateDefaultAdorners(); + } + + protected void ObfuscateContainer_OnFieldChanged(object sender, FieldChangedEventArgs e) + { + if (sender.Equals(this)) + { + if (Equals(e.Field.FieldType, FieldType.PREPARED_FILTER_OBFUSCATE)) + { + ConfigurePreparedFilters(); + } + } + } + + private void ConfigurePreparedFilters() + { + PreparedFilter preset = (PreparedFilter) GetFieldValue(FieldType.PREPARED_FILTER_OBFUSCATE); + while (Filters.Count > 0) + { + Remove(Filters[0]); + } + + switch (preset) + { + case PreparedFilter.BLUR: + Add(new BlurFilter(this)); + break; + case PreparedFilter.PIXELIZE: + Add(new PixelizationFilter(this)); + break; + } + } + } } \ No newline at end of file diff --git a/src/Greenshot/Drawing/Positions.cs b/src/Greenshot.Editor/Drawing/Positions.cs similarity index 94% rename from src/Greenshot/Drawing/Positions.cs rename to src/Greenshot.Editor/Drawing/Positions.cs index 0b3f391ec..863690887 100644 --- a/src/Greenshot/Drawing/Positions.cs +++ b/src/Greenshot.Editor/Drawing/Positions.cs @@ -1,38 +1,38 @@ -/* - * Greenshot - a free and open source screenshot tool - * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom, Francis Noel - * - * For more information see: https://getgreenshot.org/ - * The Greenshot project is hosted on GitHub https://github.com/greenshot/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 . - */ - -namespace Greenshot.Drawing -{ - /// - /// Position - /// - public enum Positions : int - { - TopLeft = 0, - TopCenter = 1, - TopRight = 2, - MiddleRight = 3, - BottomRight = 4, - BottomCenter = 5, - BottomLeft = 6, - MiddleLeft = 7 - } +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom, Francis Noel + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/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 . + */ + +namespace Greenshot.Editor.Drawing +{ + /// + /// Position + /// + public enum Positions : int + { + TopLeft = 0, + TopCenter = 1, + TopRight = 2, + MiddleRight = 3, + BottomRight = 4, + BottomCenter = 5, + BottomLeft = 6, + MiddleLeft = 7 + } } \ No newline at end of file diff --git a/src/Greenshot/Drawing/RectangleContainer.cs b/src/Greenshot.Editor/Drawing/RectangleContainer.cs similarity index 96% rename from src/Greenshot/Drawing/RectangleContainer.cs rename to src/Greenshot.Editor/Drawing/RectangleContainer.cs index 778451d2b..5e38fcf4b 100644 --- a/src/Greenshot/Drawing/RectangleContainer.cs +++ b/src/Greenshot.Editor/Drawing/RectangleContainer.cs @@ -1,167 +1,167 @@ -/* - * Greenshot - a free and open source screenshot tool - * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom - * - * For more information see: https://getgreenshot.org/ - * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 1 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -using System; -using System.Drawing; -using System.Drawing.Drawing2D; -using Greenshot.Drawing.Fields; -using Greenshot.Helpers; -using System.Runtime.Serialization; -using Greenshot.Base.Interfaces.Drawing; - -namespace Greenshot.Drawing -{ - /// - /// Represents a rectangular shape on the Surface - /// - [Serializable] - public class RectangleContainer : DrawableContainer - { - public RectangleContainer(Surface parent) : base(parent) - { - Init(); - } - - /// - /// Do some logic to make sure all field are initiated correctly - /// - /// StreamingContext - protected override void OnDeserialized(StreamingContext streamingContext) - { - base.OnDeserialized(streamingContext); - Init(); - } - - private void Init() - { - CreateDefaultAdorners(); - } - - protected override void InitializeFields() - { - AddField(GetType(), FieldType.LINE_THICKNESS, 2); - AddField(GetType(), FieldType.LINE_COLOR, Color.Red); - AddField(GetType(), FieldType.FILL_COLOR, Color.Transparent); - AddField(GetType(), FieldType.SHADOW, true); - } - - public override void Draw(Graphics graphics, RenderMode rm) - { - int lineThickness = GetFieldValueAsInt(FieldType.LINE_THICKNESS); - Color lineColor = GetFieldValueAsColor(FieldType.LINE_COLOR, Color.Red); - Color fillColor = GetFieldValueAsColor(FieldType.FILL_COLOR, Color.Transparent); - bool shadow = GetFieldValueAsBool(FieldType.SHADOW); - Rectangle rect = GuiRectangle.GetGuiRectangle(Left, Top, Width, Height); - - DrawRectangle(rect, graphics, rm, lineThickness, lineColor, fillColor, shadow); - } - - /// - /// This method can also be used from other containers, if the right values are passed! - /// - /// - /// - /// - /// - /// - /// - /// - public static void DrawRectangle(Rectangle rect, Graphics graphics, RenderMode rm, int lineThickness, Color lineColor, Color fillColor, bool shadow) - { - graphics.SmoothingMode = SmoothingMode.HighQuality; - graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; - graphics.CompositingQuality = CompositingQuality.HighQuality; - graphics.PixelOffsetMode = PixelOffsetMode.None; - - bool lineVisible = lineThickness > 0 && Colors.IsVisible(lineColor); - if (shadow && (lineVisible || Colors.IsVisible(fillColor))) - { - //draw shadow first - int basealpha = 100; - int alpha = basealpha; - int steps = 5; - int currentStep = lineVisible ? 1 : 0; - while (currentStep <= steps) - { - using Pen shadowPen = new Pen(Color.FromArgb(alpha, 100, 100, 100)) - { - Width = lineVisible ? lineThickness : 1 - }; - Rectangle shadowRect = GuiRectangle.GetGuiRectangle( - rect.Left + currentStep, - rect.Top + currentStep, - rect.Width, - rect.Height); - graphics.DrawRectangle(shadowPen, shadowRect); - currentStep++; - alpha -= basealpha / steps; - } - } - - - if (Colors.IsVisible(fillColor)) - { - using Brush brush = new SolidBrush(fillColor); - graphics.FillRectangle(brush, rect); - } - - graphics.SmoothingMode = SmoothingMode.HighSpeed; - if (lineVisible) - { - using Pen pen = new Pen(lineColor, lineThickness); - graphics.DrawRectangle(pen, rect); - } - } - - public override bool ClickableAt(int x, int y) - { - Rectangle rect = GuiRectangle.GetGuiRectangle(Left, Top, Width, Height); - int lineThickness = GetFieldValueAsInt(FieldType.LINE_THICKNESS) + 10; - Color fillColor = GetFieldValueAsColor(FieldType.FILL_COLOR); - - return RectangleClickableAt(rect, lineThickness, fillColor, x, y); - } - - - public static bool RectangleClickableAt(Rectangle rect, int lineThickness, Color fillColor, int x, int y) - { - // If we clicked inside the rectangle and it's visible we are clickable at. - if (!Color.Transparent.Equals(fillColor)) - { - if (rect.Contains(x, y)) - { - return true; - } - } - - // check the rest of the lines - if (lineThickness > 0) - { - using Pen pen = new Pen(Color.White, lineThickness); - using GraphicsPath path = new GraphicsPath(); - path.AddRectangle(rect); - return path.IsOutlineVisible(x, y, pen); - } - - return false; - } - } +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using System; +using System.Drawing; +using System.Drawing.Drawing2D; +using System.Runtime.Serialization; +using Greenshot.Base.Interfaces.Drawing; +using Greenshot.Editor.Drawing.Fields; +using Greenshot.Editor.Helpers; + +namespace Greenshot.Editor.Drawing +{ + /// + /// Represents a rectangular shape on the Surface + /// + [Serializable] + public class RectangleContainer : DrawableContainer + { + public RectangleContainer(Surface parent) : base(parent) + { + Init(); + } + + /// + /// Do some logic to make sure all field are initiated correctly + /// + /// StreamingContext + protected override void OnDeserialized(StreamingContext streamingContext) + { + base.OnDeserialized(streamingContext); + Init(); + } + + private void Init() + { + CreateDefaultAdorners(); + } + + protected override void InitializeFields() + { + AddField(GetType(), FieldType.LINE_THICKNESS, 2); + AddField(GetType(), FieldType.LINE_COLOR, Color.Red); + AddField(GetType(), FieldType.FILL_COLOR, Color.Transparent); + AddField(GetType(), FieldType.SHADOW, true); + } + + public override void Draw(Graphics graphics, RenderMode rm) + { + int lineThickness = GetFieldValueAsInt(FieldType.LINE_THICKNESS); + Color lineColor = GetFieldValueAsColor(FieldType.LINE_COLOR, Color.Red); + Color fillColor = GetFieldValueAsColor(FieldType.FILL_COLOR, Color.Transparent); + bool shadow = GetFieldValueAsBool(FieldType.SHADOW); + Rectangle rect = GuiRectangle.GetGuiRectangle(Left, Top, Width, Height); + + DrawRectangle(rect, graphics, rm, lineThickness, lineColor, fillColor, shadow); + } + + /// + /// This method can also be used from other containers, if the right values are passed! + /// + /// + /// + /// + /// + /// + /// + /// + public static void DrawRectangle(Rectangle rect, Graphics graphics, RenderMode rm, int lineThickness, Color lineColor, Color fillColor, bool shadow) + { + graphics.SmoothingMode = SmoothingMode.HighQuality; + graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; + graphics.CompositingQuality = CompositingQuality.HighQuality; + graphics.PixelOffsetMode = PixelOffsetMode.None; + + bool lineVisible = lineThickness > 0 && Colors.IsVisible(lineColor); + if (shadow && (lineVisible || Colors.IsVisible(fillColor))) + { + //draw shadow first + int basealpha = 100; + int alpha = basealpha; + int steps = 5; + int currentStep = lineVisible ? 1 : 0; + while (currentStep <= steps) + { + using Pen shadowPen = new Pen(Color.FromArgb(alpha, 100, 100, 100)) + { + Width = lineVisible ? lineThickness : 1 + }; + Rectangle shadowRect = GuiRectangle.GetGuiRectangle( + rect.Left + currentStep, + rect.Top + currentStep, + rect.Width, + rect.Height); + graphics.DrawRectangle(shadowPen, shadowRect); + currentStep++; + alpha -= basealpha / steps; + } + } + + + if (Colors.IsVisible(fillColor)) + { + using Brush brush = new SolidBrush(fillColor); + graphics.FillRectangle(brush, rect); + } + + graphics.SmoothingMode = SmoothingMode.HighSpeed; + if (lineVisible) + { + using Pen pen = new Pen(lineColor, lineThickness); + graphics.DrawRectangle(pen, rect); + } + } + + public override bool ClickableAt(int x, int y) + { + Rectangle rect = GuiRectangle.GetGuiRectangle(Left, Top, Width, Height); + int lineThickness = GetFieldValueAsInt(FieldType.LINE_THICKNESS) + 10; + Color fillColor = GetFieldValueAsColor(FieldType.FILL_COLOR); + + return RectangleClickableAt(rect, lineThickness, fillColor, x, y); + } + + + public static bool RectangleClickableAt(Rectangle rect, int lineThickness, Color fillColor, int x, int y) + { + // If we clicked inside the rectangle and it's visible we are clickable at. + if (!Color.Transparent.Equals(fillColor)) + { + if (rect.Contains(x, y)) + { + return true; + } + } + + // check the rest of the lines + if (lineThickness > 0) + { + using Pen pen = new Pen(Color.White, lineThickness); + using GraphicsPath path = new GraphicsPath(); + path.AddRectangle(rect); + return path.IsOutlineVisible(x, y, pen); + } + + return false; + } + } } \ No newline at end of file diff --git a/src/Greenshot/Drawing/SpeechbubbleContainer.cs b/src/Greenshot.Editor/Drawing/SpeechbubbleContainer.cs similarity index 96% rename from src/Greenshot/Drawing/SpeechbubbleContainer.cs rename to src/Greenshot.Editor/Drawing/SpeechbubbleContainer.cs index d54c98ec4..2b189945d 100644 --- a/src/Greenshot/Drawing/SpeechbubbleContainer.cs +++ b/src/Greenshot.Editor/Drawing/SpeechbubbleContainer.cs @@ -1,399 +1,399 @@ -/* - * Greenshot - a free and open source screenshot tool - * Copyright (C) 2007-2012 Thomas Braun, Jens Klingen, Robin Krom - * - * For more information see: https://getgreenshot.org/ - * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 1 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -using Greenshot.Drawing.Fields; -using Greenshot.Helpers; -using System; -using System.Drawing; -using System.Drawing.Drawing2D; -using System.Drawing.Text; -using System.Runtime.Serialization; -using Greenshot.Base.Interfaces.Drawing; - -namespace Greenshot.Drawing -{ - /// - /// Description of SpeechbubbleContainer. - /// - [Serializable] - public class SpeechbubbleContainer : TextContainer - { - private Point _initialGripperPoint; - - // Only used for serializing the TargetGripper location - private Point _storedTargetGripperLocation; - - /// - /// Store the current location of the target gripper - /// - /// - [OnSerializing] - private void SetValuesOnSerializing(StreamingContext context) - { - if (TargetAdorner != null) - { - _storedTargetGripperLocation = TargetAdorner.Location; - } - } - - /// - /// Restore the target gripper - /// - /// StreamingContext - protected override void OnDeserialized(StreamingContext streamingContext) - { - base.OnDeserialized(streamingContext); - InitAdorner(Color.Green, _storedTargetGripperLocation); - } - - public SpeechbubbleContainer(Surface parent) - : base(parent) - { - } - - /// - /// We set our own field values - /// - protected override void InitializeFields() - { - AddField(GetType(), FieldType.LINE_THICKNESS, 2); - AddField(GetType(), FieldType.LINE_COLOR, Color.Blue); - AddField(GetType(), FieldType.SHADOW, false); - AddField(GetType(), FieldType.FONT_ITALIC, false); - AddField(GetType(), FieldType.FONT_BOLD, true); - AddField(GetType(), FieldType.FILL_COLOR, Color.White); - AddField(GetType(), FieldType.FONT_FAMILY, FontFamily.GenericSansSerif.Name); - AddField(GetType(), FieldType.FONT_SIZE, 20f); - AddField(GetType(), FieldType.TEXT_HORIZONTAL_ALIGNMENT, StringAlignment.Center); - AddField(GetType(), FieldType.TEXT_VERTICAL_ALIGNMENT, StringAlignment.Center); - } - - /// - /// Called from Surface (the _parent) when the drawing begins (mouse-down) - /// - /// true if the surface doesn't need to handle the event - public override bool HandleMouseDown(int mouseX, int mouseY) - { - if (TargetAdorner == null) - { - _initialGripperPoint = new Point(mouseX, mouseY); - InitAdorner(Color.Green, new Point(mouseX, mouseY)); - } - - return base.HandleMouseDown(mouseX, mouseY); - } - - /// - /// Overriding the HandleMouseMove will help us to make sure the tail is always visible. - /// Should fix BUG-1682 - /// - /// - /// - /// base.HandleMouseMove - public override bool HandleMouseMove(int x, int y) - { - bool returnValue = base.HandleMouseMove(x, y); - - bool leftAligned = _boundsAfterResize.Right - _boundsAfterResize.Left >= 0; - bool topAligned = _boundsAfterResize.Bottom - _boundsAfterResize.Top >= 0; - - int xOffset = leftAligned ? -20 : 20; - int yOffset = topAligned ? -20 : 20; - - Point newGripperLocation = _initialGripperPoint; - newGripperLocation.Offset(xOffset, yOffset); - - if (TargetAdorner.Location != newGripperLocation) - { - Invalidate(); - TargetAdorner.Location = newGripperLocation; - Invalidate(); - } - - return returnValue; - } - - /// - /// The DrawingBound should be so close as possible to the shape, so we don't invalidate to much. - /// - public override Rectangle DrawingBounds - { - get - { - if (Status != EditStatus.UNDRAWN) - { - int lineThickness = GetFieldValueAsInt(FieldType.LINE_THICKNESS); - Color lineColor = GetFieldValueAsColor(FieldType.LINE_COLOR); - bool shadow = GetFieldValueAsBool(FieldType.SHADOW); - using Pen pen = new Pen(lineColor, lineThickness); - int inflateValue = lineThickness + 2 + (shadow ? 6 : 0); - using GraphicsPath tailPath = CreateTail(); - return Rectangle.Inflate(Rectangle.Union(Rectangle.Round(tailPath.GetBounds(new Matrix(), pen)), GuiRectangle.GetGuiRectangle(Left, Top, Width, Height)), - inflateValue, inflateValue); - } - - return Rectangle.Empty; - } - } - - /// - /// Helper method to create the bubble GraphicsPath, so we can also calculate the bounds - /// - /// - /// - private GraphicsPath CreateBubble(int lineThickness) - { - GraphicsPath bubble = new GraphicsPath(); - Rectangle rect = GuiRectangle.GetGuiRectangle(Left, Top, Width, Height); - - Rectangle bubbleRect = GuiRectangle.GetGuiRectangle(0, 0, rect.Width, rect.Height); - // adapt corner radius to small rectangle dimensions - int smallerSideLength = Math.Min(bubbleRect.Width, bubbleRect.Height); - int cornerRadius = Math.Min(30, smallerSideLength / 2 - lineThickness); - if (cornerRadius > 0) - { - bubble.AddArc(bubbleRect.X, bubbleRect.Y, cornerRadius, cornerRadius, 180, 90); - bubble.AddArc(bubbleRect.X + bubbleRect.Width - cornerRadius, bubbleRect.Y, cornerRadius, cornerRadius, 270, 90); - bubble.AddArc(bubbleRect.X + bubbleRect.Width - cornerRadius, bubbleRect.Y + bubbleRect.Height - cornerRadius, cornerRadius, cornerRadius, 0, 90); - bubble.AddArc(bubbleRect.X, bubbleRect.Y + bubbleRect.Height - cornerRadius, cornerRadius, cornerRadius, 90, 90); - } - else - { - bubble.AddRectangle(bubbleRect); - } - - bubble.CloseAllFigures(); - using (Matrix bubbleMatrix = new Matrix()) - { - bubbleMatrix.Translate(rect.Left, rect.Top); - bubble.Transform(bubbleMatrix); - } - - return bubble; - } - - /// - /// Helper method to create the tail of the bubble, so we can also calculate the bounds - /// - /// - 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), TargetAdorner.Location.X, TargetAdorner.Location.Y); - 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); - tailWidth = Math.Min(Math.Abs(rect.Height) / 2, tailWidth); - - GraphicsPath tail = new GraphicsPath(); - tail.AddLine(-tailWidth, 0, tailWidth, 0); - tail.AddLine(tailWidth, 0, 0, -tailLength); - tail.CloseFigure(); - - int tailAngle = 90 + (int) GeometryHelper.Angle2D(rect.Left + rect.Width / 2, rect.Top + rect.Height / 2, TargetAdorner.Location.X, TargetAdorner.Location.Y); - - using (Matrix tailMatrix = new Matrix()) - { - tailMatrix.Translate(rect.Left + rect.Width / 2, rect.Top + rect.Height / 2); - tailMatrix.Rotate(tailAngle); - tail.Transform(tailMatrix); - } - - return tail; - } - - /// - /// This is to draw the actual container - /// - /// - /// - public override void Draw(Graphics graphics, RenderMode renderMode) - { - if (TargetAdorner == null) - { - return; - } - - graphics.SmoothingMode = SmoothingMode.HighQuality; - graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; - graphics.CompositingQuality = CompositingQuality.HighQuality; - graphics.PixelOffsetMode = PixelOffsetMode.None; - graphics.TextRenderingHint = TextRenderingHint.AntiAliasGridFit; - - Color lineColor = GetFieldValueAsColor(FieldType.LINE_COLOR); - Color fillColor = GetFieldValueAsColor(FieldType.FILL_COLOR); - bool shadow = GetFieldValueAsBool(FieldType.SHADOW); - int lineThickness = GetFieldValueAsInt(FieldType.LINE_THICKNESS); - - bool lineVisible = lineThickness > 0 && Colors.IsVisible(lineColor); - Rectangle rect = GuiRectangle.GetGuiRectangle(Left, Top, Width, Height); - - if (Selected && renderMode == RenderMode.EDIT) - { - DrawSelectionBorder(graphics, rect); - } - - GraphicsPath bubble = CreateBubble(lineThickness); - - GraphicsPath tail = CreateTail(); - - //draw shadow first - if (shadow && (lineVisible || Colors.IsVisible(fillColor))) - { - const int basealpha = 100; - int alpha = basealpha; - const int steps = 5; - int currentStep = lineVisible ? 1 : 0; - using Matrix shadowMatrix = new Matrix(); - using GraphicsPath bubbleClone = (GraphicsPath) bubble.Clone(); - using GraphicsPath tailClone = (GraphicsPath) tail.Clone(); - shadowMatrix.Translate(1, 1); - while (currentStep <= steps) - { - using (Pen shadowPen = new Pen(Color.FromArgb(alpha, 100, 100, 100))) - { - shadowPen.Width = lineVisible ? lineThickness : 1; - tailClone.Transform(shadowMatrix); - graphics.DrawPath(shadowPen, tailClone); - bubbleClone.Transform(shadowMatrix); - graphics.DrawPath(shadowPen, bubbleClone); - } - - currentStep++; - alpha -= basealpha / steps; - } - } - - GraphicsState state = graphics.Save(); - // draw the tail border where the bubble is not visible - using (Region clipRegion = new Region(bubble)) - { - graphics.SetClip(clipRegion, CombineMode.Exclude); - using Pen pen = new Pen(lineColor, lineThickness); - graphics.DrawPath(pen, tail); - } - - graphics.Restore(state); - - if (Colors.IsVisible(fillColor)) - { - //draw the bubbleshape - state = graphics.Save(); - using (Brush brush = new SolidBrush(fillColor)) - { - graphics.FillPath(brush, bubble); - } - - graphics.Restore(state); - } - - if (lineVisible) - { - //draw the bubble border - state = graphics.Save(); - // Draw bubble where the Tail is not visible. - using (Region clipRegion = new Region(tail)) - { - graphics.SetClip(clipRegion, CombineMode.Exclude); - using Pen pen = new Pen(lineColor, lineThickness); - //pen.EndCap = pen.StartCap = LineCap.Round; - graphics.DrawPath(pen, bubble); - } - - graphics.Restore(state); - } - - if (Colors.IsVisible(fillColor)) - { - // Draw the tail border - state = graphics.Save(); - using (Brush brush = new SolidBrush(fillColor)) - { - graphics.FillPath(brush, tail); - } - - graphics.Restore(state); - } - - // cleanup the paths - bubble.Dispose(); - tail.Dispose(); - - // Draw the text - DrawText(graphics, rect, lineThickness, lineColor, shadow, StringFormat, Text, Font); - } - - public override bool Contains(int x, int y) - { - if (base.Contains(x, y)) - { - return true; - } - - Point clickedPoint = new Point(x, y); - if (Status != EditStatus.UNDRAWN) - { - int lineThickness = GetFieldValueAsInt(FieldType.LINE_THICKNESS); - Color lineColor = GetFieldValueAsColor(FieldType.LINE_COLOR); - using Pen pen = new Pen(lineColor, lineThickness); - using (GraphicsPath bubblePath = CreateBubble(lineThickness)) - { - bubblePath.Widen(pen); - if (bubblePath.IsVisible(clickedPoint)) - { - return true; - } - } - - using GraphicsPath tailPath = CreateTail(); - tailPath.Widen(pen); - if (tailPath.IsVisible(clickedPoint)) - { - return true; - } - } - - return false; - } - - public override bool ClickableAt(int x, int y) - { - return Contains(x, y); - } - - /// - /// Additional to the Transform of the TextContainer the bubble tail coordinates also need to be moved - /// - /// Matrix - public override void Transform(Matrix matrix) - { - Point[] points = - { - TargetAdorner.Location - }; - matrix.TransformPoints(points); - TargetAdorner.Location = points[0]; - base.Transform(matrix); - } - } +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2012 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using System; +using System.Drawing; +using System.Drawing.Drawing2D; +using System.Drawing.Text; +using System.Runtime.Serialization; +using Greenshot.Base.Interfaces.Drawing; +using Greenshot.Editor.Drawing.Fields; +using Greenshot.Editor.Helpers; + +namespace Greenshot.Editor.Drawing +{ + /// + /// Description of SpeechbubbleContainer. + /// + [Serializable] + public class SpeechbubbleContainer : TextContainer + { + private Point _initialGripperPoint; + + // Only used for serializing the TargetGripper location + private Point _storedTargetGripperLocation; + + /// + /// Store the current location of the target gripper + /// + /// + [OnSerializing] + private void SetValuesOnSerializing(StreamingContext context) + { + if (TargetAdorner != null) + { + _storedTargetGripperLocation = TargetAdorner.Location; + } + } + + /// + /// Restore the target gripper + /// + /// StreamingContext + protected override void OnDeserialized(StreamingContext streamingContext) + { + base.OnDeserialized(streamingContext); + InitAdorner(Color.Green, _storedTargetGripperLocation); + } + + public SpeechbubbleContainer(Surface parent) + : base(parent) + { + } + + /// + /// We set our own field values + /// + protected override void InitializeFields() + { + AddField(GetType(), FieldType.LINE_THICKNESS, 2); + AddField(GetType(), FieldType.LINE_COLOR, Color.Blue); + AddField(GetType(), FieldType.SHADOW, false); + AddField(GetType(), FieldType.FONT_ITALIC, false); + AddField(GetType(), FieldType.FONT_BOLD, true); + AddField(GetType(), FieldType.FILL_COLOR, Color.White); + AddField(GetType(), FieldType.FONT_FAMILY, FontFamily.GenericSansSerif.Name); + AddField(GetType(), FieldType.FONT_SIZE, 20f); + AddField(GetType(), FieldType.TEXT_HORIZONTAL_ALIGNMENT, StringAlignment.Center); + AddField(GetType(), FieldType.TEXT_VERTICAL_ALIGNMENT, StringAlignment.Center); + } + + /// + /// Called from Surface (the _parent) when the drawing begins (mouse-down) + /// + /// true if the surface doesn't need to handle the event + public override bool HandleMouseDown(int mouseX, int mouseY) + { + if (TargetAdorner == null) + { + _initialGripperPoint = new Point(mouseX, mouseY); + InitAdorner(Color.Green, new Point(mouseX, mouseY)); + } + + return base.HandleMouseDown(mouseX, mouseY); + } + + /// + /// Overriding the HandleMouseMove will help us to make sure the tail is always visible. + /// Should fix BUG-1682 + /// + /// + /// + /// base.HandleMouseMove + public override bool HandleMouseMove(int x, int y) + { + bool returnValue = base.HandleMouseMove(x, y); + + bool leftAligned = _boundsAfterResize.Right - _boundsAfterResize.Left >= 0; + bool topAligned = _boundsAfterResize.Bottom - _boundsAfterResize.Top >= 0; + + int xOffset = leftAligned ? -20 : 20; + int yOffset = topAligned ? -20 : 20; + + Point newGripperLocation = _initialGripperPoint; + newGripperLocation.Offset(xOffset, yOffset); + + if (TargetAdorner.Location != newGripperLocation) + { + Invalidate(); + TargetAdorner.Location = newGripperLocation; + Invalidate(); + } + + return returnValue; + } + + /// + /// The DrawingBound should be so close as possible to the shape, so we don't invalidate to much. + /// + public override Rectangle DrawingBounds + { + get + { + if (Status != EditStatus.UNDRAWN) + { + int lineThickness = GetFieldValueAsInt(FieldType.LINE_THICKNESS); + Color lineColor = GetFieldValueAsColor(FieldType.LINE_COLOR); + bool shadow = GetFieldValueAsBool(FieldType.SHADOW); + using Pen pen = new Pen(lineColor, lineThickness); + int inflateValue = lineThickness + 2 + (shadow ? 6 : 0); + using GraphicsPath tailPath = CreateTail(); + return Rectangle.Inflate(Rectangle.Union(Rectangle.Round(tailPath.GetBounds(new Matrix(), pen)), GuiRectangle.GetGuiRectangle(Left, Top, Width, Height)), + inflateValue, inflateValue); + } + + return Rectangle.Empty; + } + } + + /// + /// Helper method to create the bubble GraphicsPath, so we can also calculate the bounds + /// + /// + /// + private GraphicsPath CreateBubble(int lineThickness) + { + GraphicsPath bubble = new GraphicsPath(); + Rectangle rect = GuiRectangle.GetGuiRectangle(Left, Top, Width, Height); + + Rectangle bubbleRect = GuiRectangle.GetGuiRectangle(0, 0, rect.Width, rect.Height); + // adapt corner radius to small rectangle dimensions + int smallerSideLength = Math.Min(bubbleRect.Width, bubbleRect.Height); + int cornerRadius = Math.Min(30, smallerSideLength / 2 - lineThickness); + if (cornerRadius > 0) + { + bubble.AddArc(bubbleRect.X, bubbleRect.Y, cornerRadius, cornerRadius, 180, 90); + bubble.AddArc(bubbleRect.X + bubbleRect.Width - cornerRadius, bubbleRect.Y, cornerRadius, cornerRadius, 270, 90); + bubble.AddArc(bubbleRect.X + bubbleRect.Width - cornerRadius, bubbleRect.Y + bubbleRect.Height - cornerRadius, cornerRadius, cornerRadius, 0, 90); + bubble.AddArc(bubbleRect.X, bubbleRect.Y + bubbleRect.Height - cornerRadius, cornerRadius, cornerRadius, 90, 90); + } + else + { + bubble.AddRectangle(bubbleRect); + } + + bubble.CloseAllFigures(); + using (Matrix bubbleMatrix = new Matrix()) + { + bubbleMatrix.Translate(rect.Left, rect.Top); + bubble.Transform(bubbleMatrix); + } + + return bubble; + } + + /// + /// Helper method to create the tail of the bubble, so we can also calculate the bounds + /// + /// + 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), TargetAdorner.Location.X, TargetAdorner.Location.Y); + 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); + tailWidth = Math.Min(Math.Abs(rect.Height) / 2, tailWidth); + + GraphicsPath tail = new GraphicsPath(); + tail.AddLine(-tailWidth, 0, tailWidth, 0); + tail.AddLine(tailWidth, 0, 0, -tailLength); + tail.CloseFigure(); + + int tailAngle = 90 + (int) GeometryHelper.Angle2D(rect.Left + rect.Width / 2, rect.Top + rect.Height / 2, TargetAdorner.Location.X, TargetAdorner.Location.Y); + + using (Matrix tailMatrix = new Matrix()) + { + tailMatrix.Translate(rect.Left + rect.Width / 2, rect.Top + rect.Height / 2); + tailMatrix.Rotate(tailAngle); + tail.Transform(tailMatrix); + } + + return tail; + } + + /// + /// This is to draw the actual container + /// + /// + /// + public override void Draw(Graphics graphics, RenderMode renderMode) + { + if (TargetAdorner == null) + { + return; + } + + graphics.SmoothingMode = SmoothingMode.HighQuality; + graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; + graphics.CompositingQuality = CompositingQuality.HighQuality; + graphics.PixelOffsetMode = PixelOffsetMode.None; + graphics.TextRenderingHint = TextRenderingHint.AntiAliasGridFit; + + Color lineColor = GetFieldValueAsColor(FieldType.LINE_COLOR); + Color fillColor = GetFieldValueAsColor(FieldType.FILL_COLOR); + bool shadow = GetFieldValueAsBool(FieldType.SHADOW); + int lineThickness = GetFieldValueAsInt(FieldType.LINE_THICKNESS); + + bool lineVisible = lineThickness > 0 && Colors.IsVisible(lineColor); + Rectangle rect = GuiRectangle.GetGuiRectangle(Left, Top, Width, Height); + + if (Selected && renderMode == RenderMode.EDIT) + { + DrawSelectionBorder(graphics, rect); + } + + GraphicsPath bubble = CreateBubble(lineThickness); + + GraphicsPath tail = CreateTail(); + + //draw shadow first + if (shadow && (lineVisible || Colors.IsVisible(fillColor))) + { + const int basealpha = 100; + int alpha = basealpha; + const int steps = 5; + int currentStep = lineVisible ? 1 : 0; + using Matrix shadowMatrix = new Matrix(); + using GraphicsPath bubbleClone = (GraphicsPath) bubble.Clone(); + using GraphicsPath tailClone = (GraphicsPath) tail.Clone(); + shadowMatrix.Translate(1, 1); + while (currentStep <= steps) + { + using (Pen shadowPen = new Pen(Color.FromArgb(alpha, 100, 100, 100))) + { + shadowPen.Width = lineVisible ? lineThickness : 1; + tailClone.Transform(shadowMatrix); + graphics.DrawPath(shadowPen, tailClone); + bubbleClone.Transform(shadowMatrix); + graphics.DrawPath(shadowPen, bubbleClone); + } + + currentStep++; + alpha -= basealpha / steps; + } + } + + GraphicsState state = graphics.Save(); + // draw the tail border where the bubble is not visible + using (Region clipRegion = new Region(bubble)) + { + graphics.SetClip(clipRegion, CombineMode.Exclude); + using Pen pen = new Pen(lineColor, lineThickness); + graphics.DrawPath(pen, tail); + } + + graphics.Restore(state); + + if (Colors.IsVisible(fillColor)) + { + //draw the bubbleshape + state = graphics.Save(); + using (Brush brush = new SolidBrush(fillColor)) + { + graphics.FillPath(brush, bubble); + } + + graphics.Restore(state); + } + + if (lineVisible) + { + //draw the bubble border + state = graphics.Save(); + // Draw bubble where the Tail is not visible. + using (Region clipRegion = new Region(tail)) + { + graphics.SetClip(clipRegion, CombineMode.Exclude); + using Pen pen = new Pen(lineColor, lineThickness); + //pen.EndCap = pen.StartCap = LineCap.Round; + graphics.DrawPath(pen, bubble); + } + + graphics.Restore(state); + } + + if (Colors.IsVisible(fillColor)) + { + // Draw the tail border + state = graphics.Save(); + using (Brush brush = new SolidBrush(fillColor)) + { + graphics.FillPath(brush, tail); + } + + graphics.Restore(state); + } + + // cleanup the paths + bubble.Dispose(); + tail.Dispose(); + + // Draw the text + DrawText(graphics, rect, lineThickness, lineColor, shadow, StringFormat, Text, Font); + } + + public override bool Contains(int x, int y) + { + if (base.Contains(x, y)) + { + return true; + } + + Point clickedPoint = new Point(x, y); + if (Status != EditStatus.UNDRAWN) + { + int lineThickness = GetFieldValueAsInt(FieldType.LINE_THICKNESS); + Color lineColor = GetFieldValueAsColor(FieldType.LINE_COLOR); + using Pen pen = new Pen(lineColor, lineThickness); + using (GraphicsPath bubblePath = CreateBubble(lineThickness)) + { + bubblePath.Widen(pen); + if (bubblePath.IsVisible(clickedPoint)) + { + return true; + } + } + + using GraphicsPath tailPath = CreateTail(); + tailPath.Widen(pen); + if (tailPath.IsVisible(clickedPoint)) + { + return true; + } + } + + return false; + } + + public override bool ClickableAt(int x, int y) + { + return Contains(x, y); + } + + /// + /// Additional to the Transform of the TextContainer the bubble tail coordinates also need to be moved + /// + /// Matrix + public override void Transform(Matrix matrix) + { + Point[] points = + { + TargetAdorner.Location + }; + matrix.TransformPoints(points); + TargetAdorner.Location = points[0]; + base.Transform(matrix); + } + } } \ No newline at end of file diff --git a/src/Greenshot/Drawing/StepLabelContainer.cs b/src/Greenshot.Editor/Drawing/StepLabelContainer.cs similarity index 96% rename from src/Greenshot/Drawing/StepLabelContainer.cs rename to src/Greenshot.Editor/Drawing/StepLabelContainer.cs index 41f78da09..0e7c27516 100644 --- a/src/Greenshot/Drawing/StepLabelContainer.cs +++ b/src/Greenshot.Editor/Drawing/StepLabelContainer.cs @@ -1,225 +1,225 @@ -/* - * Greenshot - a free and open source screenshot tool - * Copyright (C) 2007-2012 Thomas Braun, Jens Klingen, Robin Krom - * - * For more information see: https://getgreenshot.org/ - * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 1 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -using Greenshot.Drawing.Fields; -using Greenshot.Helpers; -using System; -using System.Drawing; -using System.Drawing.Drawing2D; -using System.Drawing.Text; -using System.Runtime.Serialization; -using Greenshot.Base.Interfaces.Drawing; - -namespace Greenshot.Drawing -{ - /// - /// This is an enumerated label, every single StepLabelContainer shows the number of the order it was created. - /// To make sure that deleting recalculates, we check the location before every draw. - /// - [Serializable] - public sealed class StepLabelContainer : DrawableContainer - { - [NonSerialized] private StringFormat _stringFormat = new StringFormat(); - - private readonly bool _drawAsRectangle = false; - - public StepLabelContainer(Surface parent) : base(parent) - { - parent.AddStepLabel(this); - InitContent(); - Init(); - } - - private void Init() - { - CreateDefaultAdorners(); - } - - // Used to store the number of this label, so when deserializing it can be placed back to the StepLabels list in the right location - private int _number; - - // Used to store the counter start of the Surface, as the surface is NOT stored. - private int _counterStart = 1; - - public int Number - { - get { return _number; } - set { _number = value; } - } - - /// - /// Retrieve the counter before serializing - /// - /// - [OnSerializing] - private void SetValuesOnSerializing(StreamingContext context) - { - if (Parent != null) - { - Number = ((Surface) Parent).CountStepLabels(this); - _counterStart = ((Surface) Parent).CounterStart; - } - } - - /// - /// Restore values that don't serialize - /// - /// - protected override void OnDeserialized(StreamingContext context) - { - Init(); - _stringFormat = new StringFormat - { - Alignment = StringAlignment.Center, - LineAlignment = StringAlignment.Center - }; - } - - /// - /// Add the StepLabel to the parent - /// - /// - protected override void SwitchParent(Surface newParent) - { - if (newParent == Parent) - { - return; - } - - ((Surface) Parent)?.RemoveStepLabel(this); - base.SwitchParent(newParent); - if (newParent == null) - { - return; - } - - // Make sure the counter start is restored (this unfortunately happens multiple times... -> hack) - newParent.CounterStart = _counterStart; - newParent.AddStepLabel(this); - } - - public override Size DefaultSize => new Size(30, 30); - - public override bool InitContent() - { - _defaultEditMode = EditStatus.IDLE; - _stringFormat.Alignment = StringAlignment.Center; - _stringFormat.LineAlignment = StringAlignment.Center; - - // Set defaults - Width = DefaultSize.Width; - Height = DefaultSize.Height; - - return true; - } - - /// - /// This makes it possible for the label to be placed exactly in the middle of the pointer. - /// - public override bool HandleMouseDown(int mouseX, int mouseY) - { - return base.HandleMouseDown(mouseX - Width / 2, mouseY - Height / 2); - } - - /// - /// We set our own field values - /// - protected override void InitializeFields() - { - AddField(GetType(), FieldType.FILL_COLOR, Color.DarkRed); - AddField(GetType(), FieldType.LINE_COLOR, Color.White); - AddField(GetType(), FieldType.FLAGS, FieldFlag.COUNTER); - } - - /// - /// Make sure this element is no longer referenced from the surface - /// - protected override void Dispose(bool disposing) - { - base.Dispose(disposing); - if (!disposing) - { - return; - } - - ((Surface) Parent)?.RemoveStepLabel(this); - if (_stringFormat == null) - { - return; - } - - _stringFormat.Dispose(); - _stringFormat = null; - } - - public override bool HandleMouseMove(int x, int y) - { - Invalidate(); - Left = x - Width / 2; - Top = y - Height / 2; - Invalidate(); - return true; - } - - /// - /// Override the parent, calculate the label number, than draw - /// - /// - /// - public override void Draw(Graphics graphics, RenderMode rm) - { - graphics.SmoothingMode = SmoothingMode.HighQuality; - graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; - graphics.CompositingQuality = CompositingQuality.HighQuality; - graphics.PixelOffsetMode = PixelOffsetMode.None; - graphics.TextRenderingHint = TextRenderingHint.AntiAliasGridFit; - string text = ((Surface) Parent).CountStepLabels(this).ToString(); - Rectangle rect = GuiRectangle.GetGuiRectangle(Left, Top, Width, Height); - Color fillColor = GetFieldValueAsColor(FieldType.FILL_COLOR); - Color lineColor = GetFieldValueAsColor(FieldType.LINE_COLOR); - if (_drawAsRectangle) - { - RectangleContainer.DrawRectangle(rect, graphics, rm, 0, Color.Transparent, fillColor, false); - } - else - { - EllipseContainer.DrawEllipse(rect, graphics, rm, 0, Color.Transparent, fillColor, false); - } - - float fontSize = Math.Min(Width, Height) / 1.4f; - using FontFamily fam = new FontFamily(FontFamily.GenericSansSerif.Name); - using Font font = new Font(fam, fontSize, FontStyle.Bold, GraphicsUnit.Pixel); - TextContainer.DrawText(graphics, rect, 0, lineColor, false, _stringFormat, text, font); - } - - public override bool ClickableAt(int x, int y) - { - Rectangle rect = GuiRectangle.GetGuiRectangle(Left, Top, Width, Height); - Color fillColor = GetFieldValueAsColor(FieldType.FILL_COLOR); - if (_drawAsRectangle) - { - return RectangleContainer.RectangleClickableAt(rect, 0, fillColor, x, y); - } - - return EllipseContainer.EllipseClickableAt(rect, 0, fillColor, x, y); - } - } +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2012 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using System; +using System.Drawing; +using System.Drawing.Drawing2D; +using System.Drawing.Text; +using System.Runtime.Serialization; +using Greenshot.Base.Interfaces.Drawing; +using Greenshot.Editor.Drawing.Fields; +using Greenshot.Editor.Helpers; + +namespace Greenshot.Editor.Drawing +{ + /// + /// This is an enumerated label, every single StepLabelContainer shows the number of the order it was created. + /// To make sure that deleting recalculates, we check the location before every draw. + /// + [Serializable] + public sealed class StepLabelContainer : DrawableContainer + { + [NonSerialized] private StringFormat _stringFormat = new StringFormat(); + + private readonly bool _drawAsRectangle = false; + + public StepLabelContainer(Surface parent) : base(parent) + { + parent.AddStepLabel(this); + InitContent(); + Init(); + } + + private void Init() + { + CreateDefaultAdorners(); + } + + // Used to store the number of this label, so when deserializing it can be placed back to the StepLabels list in the right location + private int _number; + + // Used to store the counter start of the Surface, as the surface is NOT stored. + private int _counterStart = 1; + + public int Number + { + get { return _number; } + set { _number = value; } + } + + /// + /// Retrieve the counter before serializing + /// + /// + [OnSerializing] + private void SetValuesOnSerializing(StreamingContext context) + { + if (Parent != null) + { + Number = ((Surface) Parent).CountStepLabels(this); + _counterStart = ((Surface) Parent).CounterStart; + } + } + + /// + /// Restore values that don't serialize + /// + /// + protected override void OnDeserialized(StreamingContext context) + { + Init(); + _stringFormat = new StringFormat + { + Alignment = StringAlignment.Center, + LineAlignment = StringAlignment.Center + }; + } + + /// + /// Add the StepLabel to the parent + /// + /// + protected override void SwitchParent(Surface newParent) + { + if (newParent == Parent) + { + return; + } + + ((Surface) Parent)?.RemoveStepLabel(this); + base.SwitchParent(newParent); + if (newParent == null) + { + return; + } + + // Make sure the counter start is restored (this unfortunately happens multiple times... -> hack) + newParent.CounterStart = _counterStart; + newParent.AddStepLabel(this); + } + + public override Size DefaultSize => new Size(30, 30); + + public override bool InitContent() + { + _defaultEditMode = EditStatus.IDLE; + _stringFormat.Alignment = StringAlignment.Center; + _stringFormat.LineAlignment = StringAlignment.Center; + + // Set defaults + Width = DefaultSize.Width; + Height = DefaultSize.Height; + + return true; + } + + /// + /// This makes it possible for the label to be placed exactly in the middle of the pointer. + /// + public override bool HandleMouseDown(int mouseX, int mouseY) + { + return base.HandleMouseDown(mouseX - Width / 2, mouseY - Height / 2); + } + + /// + /// We set our own field values + /// + protected override void InitializeFields() + { + AddField(GetType(), FieldType.FILL_COLOR, Color.DarkRed); + AddField(GetType(), FieldType.LINE_COLOR, Color.White); + AddField(GetType(), FieldType.FLAGS, FieldFlag.COUNTER); + } + + /// + /// Make sure this element is no longer referenced from the surface + /// + protected override void Dispose(bool disposing) + { + base.Dispose(disposing); + if (!disposing) + { + return; + } + + ((Surface) Parent)?.RemoveStepLabel(this); + if (_stringFormat == null) + { + return; + } + + _stringFormat.Dispose(); + _stringFormat = null; + } + + public override bool HandleMouseMove(int x, int y) + { + Invalidate(); + Left = x - Width / 2; + Top = y - Height / 2; + Invalidate(); + return true; + } + + /// + /// Override the parent, calculate the label number, than draw + /// + /// + /// + public override void Draw(Graphics graphics, RenderMode rm) + { + graphics.SmoothingMode = SmoothingMode.HighQuality; + graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; + graphics.CompositingQuality = CompositingQuality.HighQuality; + graphics.PixelOffsetMode = PixelOffsetMode.None; + graphics.TextRenderingHint = TextRenderingHint.AntiAliasGridFit; + string text = ((Surface) Parent).CountStepLabels(this).ToString(); + Rectangle rect = GuiRectangle.GetGuiRectangle(Left, Top, Width, Height); + Color fillColor = GetFieldValueAsColor(FieldType.FILL_COLOR); + Color lineColor = GetFieldValueAsColor(FieldType.LINE_COLOR); + if (_drawAsRectangle) + { + RectangleContainer.DrawRectangle(rect, graphics, rm, 0, Color.Transparent, fillColor, false); + } + else + { + EllipseContainer.DrawEllipse(rect, graphics, rm, 0, Color.Transparent, fillColor, false); + } + + float fontSize = Math.Min(Width, Height) / 1.4f; + using FontFamily fam = new FontFamily(FontFamily.GenericSansSerif.Name); + using Font font = new Font(fam, fontSize, FontStyle.Bold, GraphicsUnit.Pixel); + TextContainer.DrawText(graphics, rect, 0, lineColor, false, _stringFormat, text, font); + } + + public override bool ClickableAt(int x, int y) + { + Rectangle rect = GuiRectangle.GetGuiRectangle(Left, Top, Width, Height); + Color fillColor = GetFieldValueAsColor(FieldType.FILL_COLOR); + if (_drawAsRectangle) + { + return RectangleContainer.RectangleClickableAt(rect, 0, fillColor, x, y); + } + + return EllipseContainer.EllipseClickableAt(rect, 0, fillColor, x, y); + } + } } \ No newline at end of file diff --git a/src/Greenshot/Drawing/Surface.cs b/src/Greenshot.Editor/Drawing/Surface.cs similarity index 97% rename from src/Greenshot/Drawing/Surface.cs rename to src/Greenshot.Editor/Drawing/Surface.cs index 3fafc49fe..7fd7485d0 100644 --- a/src/Greenshot/Drawing/Surface.cs +++ b/src/Greenshot.Editor/Drawing/Surface.cs @@ -1,2367 +1,2367 @@ -/* - * Greenshot - a free and open source screenshot tool - * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom - * - * For more information see: https://getgreenshot.org/ - * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 1 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -using Greenshot.Configuration; -using Greenshot.Drawing.Fields; -using Greenshot.Helpers; -using Greenshot.Memento; -using log4net; -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Drawing; -using System.Drawing.Drawing2D; -using System.Drawing.Imaging; -using System.IO; -using System.Runtime.Serialization.Formatters.Binary; -using System.Windows.Forms; -using Greenshot.Base.Controls; -using Greenshot.Base.Core; -using Greenshot.Base.Effects; -using Greenshot.Base.IniFile; -using Greenshot.Base.Interfaces; -using Greenshot.Base.Interfaces.Drawing; -using Greenshot.Base.Interfaces.Drawing.Adorners; - -namespace Greenshot.Drawing -{ - /// - /// Description of Surface. - /// - public sealed class Surface : Control, ISurface, INotifyPropertyChanged - { - private static readonly ILog LOG = LogManager.GetLogger(typeof(Surface)); - public static int Count; - private static readonly CoreConfiguration conf = IniConfig.GetIniSection(); - - // Property to identify the Surface ID - private Guid _uniqueId = Guid.NewGuid(); - - /// - /// This value is used to start counting the step labels - /// - private int _counterStart = 1; - - /// - /// The GUID of the surface - /// - public Guid ID - { - get => _uniqueId; - set => _uniqueId = value; - } - - /// - /// Event handlers (do not serialize!) - /// - [NonSerialized] private PropertyChangedEventHandler _propertyChanged; - - public event PropertyChangedEventHandler PropertyChanged - { - add => _propertyChanged += value; - remove => _propertyChanged -= value; - } - - [NonSerialized] private SurfaceElementEventHandler _movingElementChanged; - - public event SurfaceElementEventHandler MovingElementChanged - { - add => _movingElementChanged += value; - remove => _movingElementChanged -= value; - } - - [NonSerialized] private SurfaceDrawingModeEventHandler _drawingModeChanged; - - public event SurfaceDrawingModeEventHandler DrawingModeChanged - { - add => _drawingModeChanged += value; - remove => _drawingModeChanged -= value; - } - - [NonSerialized] private SurfaceSizeChangeEventHandler _surfaceSizeChanged; - - public event SurfaceSizeChangeEventHandler SurfaceSizeChanged - { - add => _surfaceSizeChanged += value; - remove => _surfaceSizeChanged -= value; - } - - [NonSerialized] private SurfaceMessageEventHandler _surfaceMessage; - - public event SurfaceMessageEventHandler SurfaceMessage - { - add => _surfaceMessage += value; - remove => _surfaceMessage -= value; - } - - /// - /// inUndoRedo makes sure we don't undo/redo while in a undo/redo action - /// - [NonSerialized] private bool _inUndoRedo; - - /// - /// Make only one surface move cycle undoable, see SurfaceMouseMove - /// - [NonSerialized] private bool _isSurfaceMoveMadeUndoable; - - /// - /// Undo/Redo stacks, should not be serialized as the file would be way to big - /// - [NonSerialized] private readonly Stack _undoStack = new Stack(); - - [NonSerialized] private readonly Stack _redoStack = new Stack(); - - /// - /// Last save location, do not serialize! - /// - [NonSerialized] private string _lastSaveFullPath; - - /// - /// current drawing mode, do not serialize! - /// - [NonSerialized] private DrawingModes _drawingMode = DrawingModes.None; - - /// - /// the keys-locked flag helps with focus issues - /// - [NonSerialized] private bool _keysLocked; - - /// - /// Location of the mouse-down (it "starts" here), do not serialize - /// - [NonSerialized] private Point _mouseStart = Point.Empty; - - /// - /// are we in a mouse down, do not serialize - /// - [NonSerialized] private bool _mouseDown; - - /// - /// The selected element for the mouse down, do not serialize - /// - [NonSerialized] private IDrawableContainer _mouseDownElement; - - /// - /// all selected elements, do not serialize - /// - [NonSerialized] private readonly IDrawableContainerList selectedElements; - - /// - /// the element we are drawing with, do not serialize - /// - [NonSerialized] private IDrawableContainer _drawingElement; - - /// - /// the element we want to draw with (not yet drawn), do not serialize - /// - [NonSerialized] private IDrawableContainer _undrawnElement; - - /// - /// the cropcontainer, when cropping this is set, do not serialize - /// - [NonSerialized] private IDrawableContainer _cropContainer; - - /// - /// the brush which is used for transparent backgrounds, set by the editor, do not serialize - /// - [NonSerialized] private Brush _transparencyBackgroundBrush; - - /// - /// The buffer is only for drawing on it when using filters (to supply access) - /// This saves a lot of "create new bitmap" commands - /// Should not be serialized, as it's generated. - /// The actual bitmap is in the paintbox... - /// TODO: Check if this buffer is still needed! - /// - [NonSerialized] private Bitmap _buffer; - - /// - /// all stepLabels for the surface, needed with serialization - /// - private readonly List _stepLabels = new List(); - - public void AddStepLabel(StepLabelContainer stepLabel) - { - if (!_stepLabels.Contains(stepLabel)) - { - _stepLabels.Add(stepLabel); - } - } - - public void RemoveStepLabel(StepLabelContainer stepLabel) - { - _stepLabels.Remove(stepLabel); - } - - /// - /// The start value of the counter objects - /// - public int CounterStart - { - get => _counterStart; - set - { - if (_counterStart == value) - { - return; - } - - _counterStart = value; - Invalidate(); - _propertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(CounterStart))); - } - } - - /// - /// Count all the VISIBLE steplabels in the surface, up to the supplied one - /// - /// can be null, if not the counting stops here - /// number of steplabels before the supplied container - public int CountStepLabels(IDrawableContainer stopAtContainer) - { - int number = CounterStart; - foreach (var possibleThis in _stepLabels) - { - if (possibleThis.Equals(stopAtContainer)) - { - break; - } - - if (IsOnSurface(possibleThis)) - { - number++; - } - } - - return number; - } - - /// - /// all elements on the surface, needed with serialization - /// - private readonly IDrawableContainerList _elements; - - /// - /// all elements on the surface, needed with serialization - /// - private FieldAggregator _fieldAggregator; - - /// - /// the cursor container, needed with serialization as we need a direct acces to it. - /// - private IDrawableContainer _cursorContainer; - - /// - /// the modified flag specifies if the surface has had modifications after the last export. - /// Initial state is modified, as "it's not saved" - /// After serialization this should actually be "false" (the surface came from a stream) - /// For now we just serialize it... - /// - private bool _modified = true; - - /// - /// The image is the actual captured image, needed with serialization - /// - private Image _image; - - public Image Image - { - get => _image; - set - { - _image = value; - UpdateSize(); - } - } - - [NonSerialized] private Matrix _zoomMatrix = new Matrix(1, 0, 0, 1, 0, 0); - [NonSerialized] private Matrix _inverseZoomMatrix = new Matrix(1, 0, 0, 1, 0, 0); - [NonSerialized] private Fraction _zoomFactor = Fraction.Identity; - - public Fraction ZoomFactor - { - get => _zoomFactor; - set - { - _zoomFactor = value; - var inverse = _zoomFactor.Inverse(); - _zoomMatrix = new Matrix(_zoomFactor, 0, 0, _zoomFactor, 0, 0); - _inverseZoomMatrix = new Matrix(inverse, 0, 0, inverse, 0, 0); - UpdateSize(); - } - } - - - /// - /// Sets the surface size as zoomed image size. - /// - private void UpdateSize() - { - var size = _image.Size; - Size = new Size((int) (size.Width * _zoomFactor), (int) (size.Height * _zoomFactor)); - } - - /// - /// The field aggregator is that which is used to have access to all the fields inside the currently selected elements. - /// e.g. used to decided if and which line thickness is shown when multiple elements are selected. - /// - public FieldAggregator FieldAggregator - { - get => _fieldAggregator; - set => _fieldAggregator = value; - } - - /// - /// The cursor container has it's own accessor so we can find and remove this (when needed) - /// - public IDrawableContainer CursorContainer => _cursorContainer; - - /// - /// A simple getter to ask if this surface has a cursor - /// - public bool HasCursor => _cursorContainer != null; - - /// - /// A simple helper method to remove the cursor from the surface - /// - public void RemoveCursor() - { - RemoveElement(_cursorContainer); - _cursorContainer = null; - } - - /// - /// The brush which is used to draw the transparent background - /// - public Brush TransparencyBackgroundBrush - { - get => _transparencyBackgroundBrush; - set => _transparencyBackgroundBrush = value; - } - - /// - /// Are the keys on this surface locked? - /// - public bool KeysLocked - { - get => _keysLocked; - set => _keysLocked = value; - } - - /// - /// Is this surface modified? This is only true if the surface has not been exported. - /// - public bool Modified - { - get => _modified; - set => _modified = value; - } - - /// - /// The DrawingMode property specifies the mode for drawing, more or less the element type. - /// - public DrawingModes DrawingMode - { - get => _drawingMode; - set - { - _drawingMode = value; - if (_drawingModeChanged != null) - { - SurfaceDrawingModeEventArgs eventArgs = new SurfaceDrawingModeEventArgs - { - DrawingMode = _drawingMode - }; - _drawingModeChanged.Invoke(this, eventArgs); - } - - DeselectAllElements(); - CreateUndrawnElement(); - } - } - - /// - /// Property for accessing the last save "full" path - /// - public string LastSaveFullPath - { - get => _lastSaveFullPath; - set => _lastSaveFullPath = value; - } - - /// - /// Property for accessing the URL to which the surface was recently uploaded - /// - public string UploadUrl { get; set; } - - /// - /// Property for accessing the capture details - /// - public ICaptureDetails CaptureDetails { get; set; } - - /// - /// Adjust UI elements to the supplied DPI settings - /// - /// - public void AdjustToDpi(uint dpi) - { - foreach (var element in this._elements) - { - element.AdjustToDpi(dpi); - } - } - - /// - /// Base Surface constructor - /// - public Surface() - { - _fieldAggregator = new FieldAggregator(this); - Count++; - _elements = new DrawableContainerList(_uniqueId); - selectedElements = new DrawableContainerList(_uniqueId); - LOG.Debug("Creating surface!"); - MouseDown += SurfaceMouseDown; - MouseUp += SurfaceMouseUp; - MouseMove += SurfaceMouseMove; - MouseDoubleClick += SurfaceDoubleClick; - Paint += SurfacePaint; - AllowDrop = true; - DragDrop += OnDragDrop; - DragEnter += OnDragEnter; - // bind selected & elements to this, otherwise they can't inform of modifications - selectedElements.Parent = this; - _elements.Parent = this; - // Make sure we are visible - Visible = true; - TabStop = false; - // Enable double buffering - DoubleBuffered = true; - SetStyle( - ControlStyles.UserPaint | ControlStyles.AllPaintingInWmPaint | ControlStyles.ResizeRedraw | ControlStyles.ContainerControl | ControlStyles.OptimizedDoubleBuffer | - ControlStyles.SupportsTransparentBackColor, true); - } - - /// - /// Private method, the current image is disposed the new one will stay. - /// - /// The new image - /// true if the old image needs to be disposed, when using undo this should not be true!! - private void SetImage(Image newImage, bool dispose) - { - // Dispose - if (_image != null && dispose) - { - _image.Dispose(); - } - - // Set new values - Image = newImage; - - _modified = true; - } - - /// - /// Surface constructor with an image - /// - /// - public Surface(Image newImage) : this() - { - LOG.DebugFormat("Got image with dimensions {0} and format {1}", newImage.Size, newImage.PixelFormat); - SetImage(newImage, true); - } - - /// - /// Surface contructor with a capture - /// - /// - public Surface(ICapture capture) : this(capture.Image) - { - // check if cursor is captured, and visible - if (capture.Cursor != null && capture.CursorVisible) - { - Rectangle cursorRect = new Rectangle(capture.CursorLocation, capture.Cursor.Size); - Rectangle captureRect = new Rectangle(Point.Empty, capture.Image.Size); - // check if cursor is on the capture, otherwise we leave it out. - if (cursorRect.IntersectsWith(captureRect)) - { - _cursorContainer = AddIconContainer(capture.Cursor, capture.CursorLocation.X, capture.CursorLocation.Y); - SelectElement(_cursorContainer); - } - } - - // Make sure the image is NOT disposed, we took the reference directly into ourselves - ((Capture) capture).NullImage(); - - CaptureDetails = capture.CaptureDetails; - } - - protected override void Dispose(bool disposing) - { - if (disposing) - { - Count--; - LOG.Debug("Disposing surface!"); - if (_buffer != null) - { - _buffer.Dispose(); - _buffer = null; - } - - if (_transparencyBackgroundBrush != null) - { - _transparencyBackgroundBrush.Dispose(); - _transparencyBackgroundBrush = null; - } - - // Cleanup undo/redo stacks - while (_undoStack != null && _undoStack.Count > 0) - { - _undoStack.Pop().Dispose(); - } - - while (_redoStack != null && _redoStack.Count > 0) - { - _redoStack.Pop().Dispose(); - } - - foreach (IDrawableContainer container in _elements) - { - container.Dispose(); - } - - if (_undrawnElement != null) - { - _undrawnElement.Dispose(); - _undrawnElement = null; - } - - if (_cropContainer != null) - { - _cropContainer.Dispose(); - _cropContainer = null; - } - } - - base.Dispose(disposing); - } - - /// - /// Undo the last action - /// - public void Undo() - { - if (_undoStack.Count > 0) - { - _inUndoRedo = true; - IMemento top = _undoStack.Pop(); - _redoStack.Push(top.Restore()); - _inUndoRedo = false; - } - } - - /// - /// Undo an undo (=redo) - /// - public void Redo() - { - if (_redoStack.Count > 0) - { - _inUndoRedo = true; - IMemento top = _redoStack.Pop(); - _undoStack.Push(top.Restore()); - _inUndoRedo = false; - } - } - - /// - /// Returns if the surface can do a undo - /// - public bool CanUndo => _undoStack.Count > 0; - - /// - /// Returns if the surface can do a redo - /// - public bool CanRedo => _redoStack.Count > 0; - - /// - /// Get the language key for the undo action - /// - public LangKey UndoActionLanguageKey => LangKey.none; - - /// - /// Get the language key for redo action - /// - public LangKey RedoActionLanguageKey => LangKey.none; - - /// - /// Make an action undo-able - /// - /// The memento implementing the undo - /// Allow changes to be merged - public void MakeUndoable(IMemento memento, bool allowMerge) - { - if (_inUndoRedo) - { - throw new InvalidOperationException("Invoking do within an undo/redo action."); - } - - if (memento != null) - { - bool allowPush = true; - if (_undoStack.Count > 0 && allowMerge) - { - // Check if merge is possible - allowPush = !_undoStack.Peek().Merge(memento); - } - - if (allowPush) - { - // Clear the redo-stack and dispose - while (_redoStack.Count > 0) - { - _redoStack.Pop().Dispose(); - } - - _undoStack.Push(memento); - } - } - } - - /// - /// This saves the elements of this surface to a stream. - /// Is used to save a template of the complete surface - /// - /// - /// - public long SaveElementsToStream(Stream streamWrite) - { - long bytesWritten = 0; - try - { - long lengtBefore = streamWrite.Length; - BinaryFormatter binaryWrite = new BinaryFormatter(); - binaryWrite.Serialize(streamWrite, _elements); - bytesWritten = streamWrite.Length - lengtBefore; - } - catch (Exception e) - { - LOG.Error("Error serializing elements to stream.", e); - } - - return bytesWritten; - } - - /// - /// This loads elements from a stream, among others this is used to load a surface. - /// - /// - public void LoadElementsFromStream(Stream streamRead) - { - try - { - BinaryFormatter binaryRead = new BinaryFormatter(); - IDrawableContainerList loadedElements = (IDrawableContainerList) binaryRead.Deserialize(streamRead); - loadedElements.Parent = this; - // Make sure the steplabels are sorted accoring to their number - _stepLabels.Sort((p1, p2) => p1.Number.CompareTo(p2.Number)); - DeselectAllElements(); - AddElements(loadedElements); - SelectElements(loadedElements); - FieldAggregator.BindElements(loadedElements); - } - catch (Exception e) - { - LOG.Error("Error serializing elements from stream.", e); - } - } - - /// - /// This is called from the DrawingMode setter, which is not very correct... - /// But here an element is created which is not yet draw, thus "undrawnElement". - /// The element is than used while drawing on the surface. - /// - private void CreateUndrawnElement() - { - if (_undrawnElement != null) - { - FieldAggregator.UnbindElement(_undrawnElement); - } - - switch (DrawingMode) - { - case DrawingModes.Rect: - _undrawnElement = new RectangleContainer(this); - break; - case DrawingModes.Ellipse: - _undrawnElement = new EllipseContainer(this); - break; - case DrawingModes.Text: - _undrawnElement = new TextContainer(this); - break; - case DrawingModes.SpeechBubble: - _undrawnElement = new SpeechbubbleContainer(this); - break; - case DrawingModes.StepLabel: - _undrawnElement = new StepLabelContainer(this); - break; - case DrawingModes.Line: - _undrawnElement = new LineContainer(this); - break; - case DrawingModes.Arrow: - _undrawnElement = new ArrowContainer(this); - break; - case DrawingModes.Highlight: - _undrawnElement = new HighlightContainer(this); - break; - case DrawingModes.Obfuscate: - _undrawnElement = new ObfuscateContainer(this); - break; - case DrawingModes.Crop: - _cropContainer = new CropContainer(this); - _undrawnElement = _cropContainer; - break; - case DrawingModes.Bitmap: - _undrawnElement = new ImageContainer(this); - break; - case DrawingModes.Path: - _undrawnElement = new FreehandContainer(this); - break; - case DrawingModes.None: - _undrawnElement = null; - break; - } - - if (_undrawnElement != null) - { - FieldAggregator.BindElement(_undrawnElement); - } - } - - #region Plugin interface implementations - - public IImageContainer AddImageContainer(Image image, int x, int y) - { - ImageContainer bitmapContainer = new ImageContainer(this) - { - Image = image, - Left = x, - Top = y - }; - AddElement(bitmapContainer); - return bitmapContainer; - } - - public IImageContainer AddImageContainer(string filename, int x, int y) - { - ImageContainer bitmapContainer = new ImageContainer(this); - bitmapContainer.Load(filename); - bitmapContainer.Left = x; - bitmapContainer.Top = y; - AddElement(bitmapContainer); - return bitmapContainer; - } - - public IIconContainer AddIconContainer(Icon icon, int x, int y) - { - IconContainer iconContainer = new IconContainer(this) - { - Icon = icon, - Left = x, - Top = y - }; - AddElement(iconContainer); - return iconContainer; - } - - public IIconContainer AddIconContainer(string filename, int x, int y) - { - IconContainer iconContainer = new IconContainer(this); - iconContainer.Load(filename); - iconContainer.Left = x; - iconContainer.Top = y; - AddElement(iconContainer); - return iconContainer; - } - - public ICursorContainer AddCursorContainer(Cursor cursor, int x, int y) - { - CursorContainer cursorContainer = new CursorContainer(this) - { - Cursor = cursor, - Left = x, - Top = y - }; - AddElement(cursorContainer); - return cursorContainer; - } - - public ICursorContainer AddCursorContainer(string filename, int x, int y) - { - CursorContainer cursorContainer = new CursorContainer(this); - cursorContainer.Load(filename); - cursorContainer.Left = x; - cursorContainer.Top = y; - AddElement(cursorContainer); - return cursorContainer; - } - - public ITextContainer AddTextContainer(string text, int x, int y, FontFamily family, float size, bool italic, bool bold, bool shadow, int borderSize, Color color, - Color fillColor) - { - TextContainer textContainer = new TextContainer(this) - { - Text = text, - Left = x, - Top = y - }; - textContainer.SetFieldValue(FieldType.FONT_FAMILY, family.Name); - textContainer.SetFieldValue(FieldType.FONT_BOLD, bold); - textContainer.SetFieldValue(FieldType.FONT_ITALIC, italic); - textContainer.SetFieldValue(FieldType.FONT_SIZE, size); - textContainer.SetFieldValue(FieldType.FILL_COLOR, fillColor); - textContainer.SetFieldValue(FieldType.LINE_COLOR, color); - textContainer.SetFieldValue(FieldType.LINE_THICKNESS, borderSize); - textContainer.SetFieldValue(FieldType.SHADOW, shadow); - // Make sure the Text fits - textContainer.FitToText(); - - //AggregatedProperties.UpdateElement(textContainer); - AddElement(textContainer); - return textContainer; - } - - #endregion - - #region DragDrop - - private void OnDragEnter(object sender, DragEventArgs e) - { - if (LOG.IsDebugEnabled) - { - LOG.Debug("DragEnter got following formats: "); - foreach (string format in ClipboardHelper.GetFormats(e.Data)) - { - LOG.Debug(format); - } - } - - if ((e.AllowedEffect & DragDropEffects.Copy) != DragDropEffects.Copy) - { - e.Effect = DragDropEffects.None; - } - else - { - if (ClipboardHelper.ContainsImage(e.Data) || ClipboardHelper.ContainsFormat(e.Data, "DragImageBits")) - { - e.Effect = DragDropEffects.Copy; - } - else - { - e.Effect = DragDropEffects.None; - } - } - } - - /// - /// Handle the drag/drop - /// - /// - /// - private void OnDragDrop(object sender, DragEventArgs e) - { - Point mouse = PointToClient(new Point(e.X, e.Y)); - if (e.Data.GetDataPresent("Text")) - { - string possibleUrl = ClipboardHelper.GetText(e.Data); - // Test if it's an url and try to download the image so we have it in the original form - if (possibleUrl != null && possibleUrl.StartsWith("http")) - { - using Image image = NetworkHelper.DownloadImage(possibleUrl); - if (image != null) - { - AddImageContainer(image, mouse.X, mouse.Y); - return; - } - } - } - - foreach (Image image in ClipboardHelper.GetImages(e.Data)) - { - AddImageContainer(image, mouse.X, mouse.Y); - mouse.Offset(10, 10); - image.Dispose(); - } - } - - #endregion - - /// - /// Auto crop the image - /// - /// true if cropped - public bool AutoCrop() - { - Rectangle cropRectangle; - using (Image tmpImage = GetImageForExport()) - { - cropRectangle = ImageHelper.FindAutoCropRectangle(tmpImage, conf.AutoCropDifference); - } - - if (!IsCropPossible(ref cropRectangle)) - { - return false; - } - - DeselectAllElements(); - // Maybe a bit obscure, but the following line creates a drop container - // It's available as "undrawnElement" - DrawingMode = DrawingModes.Crop; - _undrawnElement.Left = cropRectangle.X; - _undrawnElement.Top = cropRectangle.Y; - _undrawnElement.Width = cropRectangle.Width; - _undrawnElement.Height = cropRectangle.Height; - _undrawnElement.Status = EditStatus.UNDRAWN; - AddElement(_undrawnElement); - SelectElement(_undrawnElement); - _drawingElement = null; - _undrawnElement = null; - return true; - } - - /// - /// A simple clear - /// - /// The color for the background - public void Clear(Color newColor) - { - //create a blank bitmap the same size as original - Bitmap newBitmap = ImageHelper.CreateEmptyLike(Image, Color.Empty); - if (newBitmap != null) - { - // Make undoable - MakeUndoable(new SurfaceBackgroundChangeMemento(this, null), false); - SetImage(newBitmap, false); - Invalidate(); - } - } - - /// - /// Apply a bitmap effect to the surface - /// - /// - public void ApplyBitmapEffect(IEffect effect) - { - BackgroundForm backgroundForm = new BackgroundForm("Effect", "Please wait"); - backgroundForm.Show(); - Application.DoEvents(); - try - { - Rectangle imageRectangle = new Rectangle(Point.Empty, Image.Size); - Matrix matrix = new Matrix(); - Image newImage = ImageHelper.ApplyEffect(Image, effect, matrix); - if (newImage != null) - { - // Make sure the elements move according to the offset the effect made the bitmap move - _elements.Transform(matrix); - // Make undoable - MakeUndoable(new SurfaceBackgroundChangeMemento(this, matrix), false); - SetImage(newImage, false); - Invalidate(); - if (_surfaceSizeChanged != null && !imageRectangle.Equals(new Rectangle(Point.Empty, newImage.Size))) - { - _surfaceSizeChanged(this, null); - } - } - else - { - // clean up matrix, as it hasn't been used in the undo stack. - matrix.Dispose(); - } - } - finally - { - // Always close the background form - backgroundForm.CloseDialog(); - } - } - - /// - /// check if a crop is possible - /// - /// - /// true if this is possible - public bool IsCropPossible(ref Rectangle cropRectangle) - { - cropRectangle = GuiRectangle.GetGuiRectangle(cropRectangle.Left, cropRectangle.Top, cropRectangle.Width, cropRectangle.Height); - if (cropRectangle.Left < 0) - { - cropRectangle = new Rectangle(0, cropRectangle.Top, cropRectangle.Width + cropRectangle.Left, cropRectangle.Height); - } - - if (cropRectangle.Top < 0) - { - cropRectangle = new Rectangle(cropRectangle.Left, 0, cropRectangle.Width, cropRectangle.Height + cropRectangle.Top); - } - - if (cropRectangle.Left + cropRectangle.Width > Image.Width) - { - cropRectangle = new Rectangle(cropRectangle.Left, cropRectangle.Top, Image.Width - cropRectangle.Left, cropRectangle.Height); - } - - if (cropRectangle.Top + cropRectangle.Height > Image.Height) - { - cropRectangle = new Rectangle(cropRectangle.Left, cropRectangle.Top, cropRectangle.Width, Image.Height - cropRectangle.Top); - } - - if (cropRectangle.Height > 0 && cropRectangle.Width > 0) - { - return true; - } - - return false; - } - - /// - /// Use to send any registered SurfaceMessageEventHandler a message, e.g. used for the notification area - /// - /// Who send - /// Type of message - /// Message itself - public void SendMessageEvent(object source, SurfaceMessageTyp messageType, string message) - { - if (_surfaceMessage != null) - { - var eventArgs = new SurfaceMessageEventArgs - { - Message = message, - MessageType = messageType, - Surface = this - }; - _surfaceMessage(source, eventArgs); - } - } - - /// - /// Crop the surface - /// - /// - /// - public bool ApplyCrop(Rectangle cropRectangle) - { - if (IsCropPossible(ref cropRectangle)) - { - Rectangle imageRectangle = new Rectangle(Point.Empty, Image.Size); - Bitmap tmpImage; - // Make sure we have information, this this fails - try - { - tmpImage = ImageHelper.CloneArea(Image, cropRectangle, PixelFormat.DontCare); - } - catch (Exception ex) - { - ex.Data.Add("CropRectangle", cropRectangle); - ex.Data.Add("Width", Image.Width); - ex.Data.Add("Height", Image.Height); - ex.Data.Add("Pixelformat", Image.PixelFormat); - throw; - } - - Matrix matrix = new Matrix(); - matrix.Translate(-cropRectangle.Left, -cropRectangle.Top, MatrixOrder.Append); - // Make undoable - MakeUndoable(new SurfaceBackgroundChangeMemento(this, matrix), false); - - // Do not dispose otherwise we can't undo the image! - SetImage(tmpImage, false); - _elements.Transform(matrix); - if (_surfaceSizeChanged != null && !imageRectangle.Equals(new Rectangle(Point.Empty, tmpImage.Size))) - { - _surfaceSizeChanged(this, null); - } - - Invalidate(); - return true; - } - - return false; - } - - /// - /// The background here is the captured image. - /// This is called from the SurfaceBackgroundChangeMemento. - /// - /// - /// - public void UndoBackgroundChange(Image previous, Matrix matrix) - { - SetImage(previous, false); - if (matrix != null) - { - _elements.Transform(matrix); - } - - _surfaceSizeChanged?.Invoke(this, null); - Invalidate(); - } - - /// - /// Check if an adorner was "hit", and change the cursor if so - /// - /// MouseEventArgs - /// IAdorner - private IAdorner FindActiveAdorner(MouseEventArgs mouseEventArgs) - { - foreach (IDrawableContainer drawableContainer in selectedElements) - { - foreach (IAdorner adorner in drawableContainer.Adorners) - { - if (adorner.IsActive || adorner.HitTest(mouseEventArgs.Location)) - { - if (adorner.Cursor != null) - { - Cursor = adorner.Cursor; - } - - return adorner; - } - } - } - - return null; - } - - /// - /// Translate mouse coordinates as if they were applied directly to unscaled image. - /// - private MouseEventArgs InverseZoomMouseCoordinates(MouseEventArgs e) - => new MouseEventArgs(e.Button, e.Clicks, (int) (e.X / _zoomFactor), (int) (e.Y / _zoomFactor), e.Delta); - - /// - /// This event handler is called when someone presses the mouse on a surface. - /// - /// - /// - private void SurfaceMouseDown(object sender, MouseEventArgs e) - { - e = InverseZoomMouseCoordinates(e); - - // Handle Adorners - var adorner = FindActiveAdorner(e); - if (adorner != null) - { - adorner.MouseDown(sender, e); - return; - } - - _mouseStart = e.Location; - - // check contextmenu - if (e.Button == MouseButtons.Right) - { - IDrawableContainerList selectedList = null; - if (selectedElements != null && selectedElements.Count > 0) - { - selectedList = selectedElements; - } - else - { - // Single element - IDrawableContainer rightClickedContainer = _elements.ClickableElementAt(_mouseStart.X, _mouseStart.Y); - if (rightClickedContainer != null) - { - selectedList = new DrawableContainerList(ID) - { - rightClickedContainer - }; - } - } - - if (selectedList != null && selectedList.Count > 0) - { - selectedList.ShowContextMenu(e, this); - } - - return; - } - - _mouseDown = true; - _isSurfaceMoveMadeUndoable = false; - - if (_cropContainer != null && ((_undrawnElement == null) || (_undrawnElement != null && DrawingMode != DrawingModes.Crop))) - { - RemoveElement(_cropContainer, false); - _cropContainer = null; - _drawingElement = null; - } - - if (_drawingElement == null && DrawingMode != DrawingModes.None) - { - if (_undrawnElement == null) - { - DeselectAllElements(); - if (_undrawnElement == null) - { - CreateUndrawnElement(); - } - } - - _drawingElement = _undrawnElement; - // if a new element has been drawn, set location and register it - if (_drawingElement != null) - { - if (_undrawnElement != null) - { - _drawingElement.Status = _undrawnElement.DefaultEditMode; - } - - if (!_drawingElement.HandleMouseDown(_mouseStart.X, _mouseStart.Y)) - { - _drawingElement.Left = _mouseStart.X; - _drawingElement.Top = _mouseStart.Y; - } - - AddElement(_drawingElement); - _drawingElement.Selected = true; - } - - _undrawnElement = null; - } - else - { - // check whether an existing element was clicked - // we save mouse down element separately from selectedElements (checked on mouse up), - // since it could be moved around before it is actually selected - _mouseDownElement = _elements.ClickableElementAt(_mouseStart.X, _mouseStart.Y); - - if (_mouseDownElement != null) - { - _mouseDownElement.Status = EditStatus.MOVING; - } - } - } - - /// - /// This event handle is called when the mouse button is unpressed - /// - /// - /// - private void SurfaceMouseUp(object sender, MouseEventArgs e) - { - e = InverseZoomMouseCoordinates(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; - if (_mouseDownElement != null) - { - _mouseDownElement.Status = EditStatus.IDLE; - } - - _mouseDown = false; - _mouseDownElement = null; - if (DrawingMode == DrawingModes.None) - { - // check whether an existing element was clicked - IDrawableContainer element = _elements.ClickableElementAt(currentMouse.X, currentMouse.Y); - bool shiftModifier = (ModifierKeys & Keys.Shift) == Keys.Shift; - if (element != null) - { - element.Invalidate(); - bool alreadySelected = selectedElements.Contains(element); - if (shiftModifier) - { - if (alreadySelected) - { - DeselectElement(element); - } - else - { - SelectElement(element); - } - } - else - { - if (!alreadySelected) - { - DeselectAllElements(); - SelectElement(element); - } - } - } - else if (!shiftModifier) - { - DeselectAllElements(); - } - } - - if (selectedElements.Count > 0) - { - selectedElements.Invalidate(); - selectedElements.Selected = true; - } - - if (_drawingElement != null) - { - if (!_drawingElement.InitContent()) - { - _elements.Remove(_drawingElement); - _drawingElement.Invalidate(); - } - else - { - _drawingElement.HandleMouseUp(currentMouse.X, currentMouse.Y); - _drawingElement.Invalidate(); - if (Math.Abs(_drawingElement.Width) < 5 && Math.Abs(_drawingElement.Height) < 5) - { - _drawingElement.Width = 25; - _drawingElement.Height = 25; - } - - SelectElement(_drawingElement); - _drawingElement.Selected = true; - } - - _drawingElement = null; - } - } - - /// - /// This event handler is called when the mouse moves over the surface - /// - /// - /// - private void SurfaceMouseMove(object sender, MouseEventArgs e) - { - e = InverseZoomMouseCoordinates(e); - - // Handle Adorners - var adorner = FindActiveAdorner(e); - if (adorner != null) - { - adorner.MouseMove(sender, e); - return; - } - - Point currentMouse = e.Location; - - Cursor = DrawingMode != DrawingModes.None ? Cursors.Cross : Cursors.Default; - - if (_mouseDown) - { - if (_mouseDownElement != null) - { - // an element is currently dragged - _mouseDownElement.Invalidate(); - selectedElements.Invalidate(); - // Move the element - if (_mouseDownElement.Selected) - { - if (!_isSurfaceMoveMadeUndoable) - { - // Only allow one undoable per mouse-down/move/up "cycle" - _isSurfaceMoveMadeUndoable = true; - selectedElements.MakeBoundsChangeUndoable(false); - } - - // dragged element has been selected before -> move all - selectedElements.MoveBy(currentMouse.X - _mouseStart.X, currentMouse.Y - _mouseStart.Y); - } - else - { - if (!_isSurfaceMoveMadeUndoable) - { - // Only allow one undoable per mouse-down/move/up "cycle" - _isSurfaceMoveMadeUndoable = true; - _mouseDownElement.MakeBoundsChangeUndoable(false); - } - - // dragged element is not among selected elements -> just move dragged one - _mouseDownElement.MoveBy(currentMouse.X - _mouseStart.X, currentMouse.Y - _mouseStart.Y); - } - - _mouseStart = currentMouse; - _mouseDownElement.Invalidate(); - _modified = true; - } - else if (_drawingElement != null) - { - _drawingElement.HandleMouseMove(currentMouse.X, currentMouse.Y); - _modified = true; - } - } - } - - /// - /// This event handler is called when the surface is double clicked. - /// - /// - /// - private void SurfaceDoubleClick(object sender, MouseEventArgs e) - { - selectedElements.OnDoubleClick(); - selectedElements.Invalidate(); - } - - /// - /// Privately used to get the rendered image with all the elements on it. - /// - /// - /// - private Image GetImage(RenderMode renderMode) - { - // Generate a copy of the original image with a dpi equal to the default... - Bitmap clone = ImageHelper.Clone(_image, PixelFormat.DontCare); - // otherwise we would have a problem drawing the image to the surface... :( - using (Graphics graphics = Graphics.FromImage(clone)) - { - // Do not set the following, the containers need to decide themselves - //graphics.SmoothingMode = SmoothingMode.HighQuality; - //graphics.PixelOffsetMode = PixelOffsetMode.HighQuality; - //graphics.CompositingQuality = CompositingQuality.HighQuality; - //graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; - _elements.Draw(graphics, clone, renderMode, new Rectangle(Point.Empty, clone.Size)); - } - - return clone; - } - - /// - /// This returns the image "result" of this surface, with all the elements rendered on it. - /// - /// - public Image GetImageForExport() - { - return GetImage(RenderMode.EXPORT); - } - - private static Rectangle ZoomClipRectangle(Rectangle rc, double scale, int inflateAmount = 0) - { - rc = new Rectangle( - (int) (rc.X * scale), - (int) (rc.Y * scale), - (int) (rc.Width * scale) + 1, - (int) (rc.Height * scale) + 1 - ); - if (scale > 1) - { - inflateAmount = (int) (inflateAmount * scale); - } - - rc.Inflate(inflateAmount, inflateAmount); - return rc; - } - - public void InvalidateElements(Rectangle rc) - => Invalidate(ZoomClipRectangle(rc, _zoomFactor, 1)); - - /// - /// This is the event handler for the Paint Event, try to draw as little as possible! - /// - /// - /// PaintEventArgs - private void SurfacePaint(object sender, PaintEventArgs paintEventArgs) - { - Graphics targetGraphics = paintEventArgs.Graphics; - Rectangle targetClipRectangle = paintEventArgs.ClipRectangle; - if (Rectangle.Empty.Equals(targetClipRectangle)) - { - LOG.Debug("Empty cliprectangle??"); - return; - } - - // Correction to prevent rounding errors at certain zoom levels. - // When zooming to N/M, clip rectangle top and left coordinates should be multiples of N. - if (_zoomFactor.Numerator > 1 && _zoomFactor.Denominator > 1) - { - int horizontalCorrection = targetClipRectangle.Left % (int) _zoomFactor.Numerator; - int verticalCorrection = targetClipRectangle.Top % (int) _zoomFactor.Numerator; - if (horizontalCorrection != 0) - { - targetClipRectangle.X -= horizontalCorrection; - targetClipRectangle.Width += horizontalCorrection; - } - - if (verticalCorrection != 0) - { - targetClipRectangle.Y -= verticalCorrection; - targetClipRectangle.Height += verticalCorrection; - } - } - - Rectangle imageClipRectangle = ZoomClipRectangle(targetClipRectangle, _zoomFactor.Inverse(), 2); - - if (_elements.HasIntersectingFilters(imageClipRectangle) || _zoomFactor > Fraction.Identity) - { - if (_buffer != null) - { - if (_buffer.Width != Image.Width || _buffer.Height != Image.Height || _buffer.PixelFormat != Image.PixelFormat) - { - _buffer.Dispose(); - _buffer = null; - } - } - - if (_buffer == null) - { - _buffer = ImageHelper.CreateEmpty(Image.Width, Image.Height, Image.PixelFormat, Color.Empty, Image.HorizontalResolution, Image.VerticalResolution); - LOG.DebugFormat("Created buffer with size: {0}x{1}", Image.Width, Image.Height); - } - - // Elements might need the bitmap, so we copy the part we need - using (Graphics graphics = Graphics.FromImage(_buffer)) - { - // do not set the following, the containers need to decide this themselves! - //graphics.SmoothingMode = SmoothingMode.HighQuality; - //graphics.PixelOffsetMode = PixelOffsetMode.HighQuality; - //graphics.CompositingQuality = CompositingQuality.HighQuality; - //graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; - DrawBackground(graphics, imageClipRectangle); - graphics.DrawImage(Image, imageClipRectangle, imageClipRectangle, GraphicsUnit.Pixel); - graphics.SetClip(ZoomClipRectangle(Rectangle.Round(targetGraphics.ClipBounds), _zoomFactor.Inverse(), 2)); - _elements.Draw(graphics, _buffer, RenderMode.EDIT, imageClipRectangle); - } - - if (_zoomFactor == Fraction.Identity) - { - targetGraphics.DrawImage(_buffer, imageClipRectangle, imageClipRectangle, GraphicsUnit.Pixel); - } - else - { - targetGraphics.ScaleTransform(_zoomFactor, _zoomFactor); - if (_zoomFactor > Fraction.Identity) - { - DrawSharpImage(targetGraphics, _buffer, imageClipRectangle); - } - else - { - DrawSmoothImage(targetGraphics, _buffer, imageClipRectangle); - } - - targetGraphics.ResetTransform(); - } - } - else - { - DrawBackground(targetGraphics, targetClipRectangle); - if (_zoomFactor == Fraction.Identity) - { - targetGraphics.DrawImage(Image, imageClipRectangle, imageClipRectangle, GraphicsUnit.Pixel); - _elements.Draw(targetGraphics, null, RenderMode.EDIT, imageClipRectangle); - } - else - { - targetGraphics.ScaleTransform(_zoomFactor, _zoomFactor); - DrawSmoothImage(targetGraphics, Image, imageClipRectangle); - _elements.Draw(targetGraphics, null, RenderMode.EDIT, imageClipRectangle); - targetGraphics.ResetTransform(); - } - } - - // 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 DrawSmoothImage(Graphics targetGraphics, Image image, Rectangle imageClipRectangle) - { - var state = targetGraphics.Save(); - targetGraphics.SmoothingMode = SmoothingMode.HighQuality; - targetGraphics.InterpolationMode = InterpolationMode.HighQualityBilinear; - targetGraphics.CompositingQuality = CompositingQuality.HighQuality; - targetGraphics.PixelOffsetMode = PixelOffsetMode.None; - - targetGraphics.DrawImage(image, imageClipRectangle, imageClipRectangle, GraphicsUnit.Pixel); - - targetGraphics.Restore(state); - } - - private void DrawSharpImage(Graphics targetGraphics, Image image, Rectangle imageClipRectangle) - { - var state = targetGraphics.Save(); - targetGraphics.SmoothingMode = SmoothingMode.None; - targetGraphics.InterpolationMode = InterpolationMode.NearestNeighbor; - targetGraphics.CompositingQuality = CompositingQuality.HighQuality; - targetGraphics.PixelOffsetMode = PixelOffsetMode.None; - - targetGraphics.DrawImage(image, imageClipRectangle, imageClipRectangle, GraphicsUnit.Pixel); - - targetGraphics.Restore(state); - } - - private void DrawBackground(Graphics targetGraphics, Rectangle clipRectangle) - { - // check if we need to draw the checkerboard - if (Image.IsAlphaPixelFormat(Image.PixelFormat) && _transparencyBackgroundBrush != null) - { - targetGraphics.FillRectangle(_transparencyBackgroundBrush, clipRectangle); - } - else - { - targetGraphics.Clear(BackColor); - } - } - - /// - /// Draw a checkboard when capturing with transparency - /// - /// PaintEventArgs - protected override void OnPaintBackground(PaintEventArgs e) - { - } - - /// - /// Add a new element to the surface - /// - /// the new element - /// true if the adding should be undoable - /// true if invalidate needs to be called - public void AddElement(IDrawableContainer element, bool makeUndoable = true, bool invalidate = true) - { - _elements.Add(element); - if (element is DrawableContainer container) - { - container.FieldChanged += Element_FieldChanged; - } - - element.Parent = this; - if (element.Status == EditStatus.UNDRAWN) - { - element.Status = EditStatus.IDLE; - } - - if (element.Selected) - { - // Use false, as the element is invalidated when invalidate == true anyway - SelectElement(element, false); - } - - if (invalidate) - { - element.Invalidate(); - } - - if (makeUndoable) - { - MakeUndoable(new AddElementMemento(this, element), false); - } - - _modified = true; - } - - /// - /// Remove the list of elements - /// - /// IDrawableContainerList - /// flag specifying if the remove needs to be undoable - public void RemoveElements(IDrawableContainerList elementsToRemove, bool makeUndoable = true) - { - // fix potential issues with iterating a changing list - DrawableContainerList cloned = new DrawableContainerList(); - cloned.AddRange(elementsToRemove); - if (makeUndoable) - { - MakeUndoable(new DeleteElementsMemento(this, cloned), false); - } - - SuspendLayout(); - foreach (var drawableContainer in cloned) - { - RemoveElement(drawableContainer, false, false, false); - } - - ResumeLayout(); - Invalidate(); - if (_movingElementChanged != null) - { - SurfaceElementEventArgs eventArgs = new SurfaceElementEventArgs - { - Elements = cloned - }; - _movingElementChanged(this, eventArgs); - } - } - - /// - /// Remove an element of the elements list - /// - /// Element to remove - /// flag specifying if the remove needs to be undoable - /// flag specifying if an surface invalidate needs to be called - /// false to skip event generation - public void RemoveElement(IDrawableContainer elementToRemove, bool makeUndoable = true, bool invalidate = true, bool generateEvents = true) - { - DeselectElement(elementToRemove, generateEvents); - _elements.Remove(elementToRemove); - if (elementToRemove is DrawableContainer element) - { - element.FieldChanged -= Element_FieldChanged; - } - - if (elementToRemove != null) - { - elementToRemove.Parent = null; - } - - // Do not dispose, the memento should!! element.Dispose(); - if (invalidate) - { - Invalidate(); - } - - if (makeUndoable) - { - MakeUndoable(new DeleteElementMemento(this, elementToRemove), false); - } - - _modified = true; - } - - /// - /// Add the supplied elements to the surface - /// - /// DrawableContainerList - /// true if the adding should be undoable - public void AddElements(IDrawableContainerList elementsToAdd, bool makeUndoable = true) - { - // fix potential issues with iterating a changing list - DrawableContainerList cloned = new DrawableContainerList(); - cloned.AddRange(elementsToAdd); - if (makeUndoable) - { - MakeUndoable(new AddElementsMemento(this, cloned), false); - } - - SuspendLayout(); - foreach (var element in cloned) - { - element.Selected = true; - AddElement(element, false, false); - } - - ResumeLayout(); - Invalidate(); - } - - /// - /// Returns if this surface has selected elements - /// - /// - public bool HasSelectedElements => (selectedElements != null && selectedElements.Count > 0); - - /// - /// Remove all the selected elements - /// - public void RemoveSelectedElements() - { - if (HasSelectedElements) - { - // As RemoveElement will remove the element from the selectedElements list we need to copy the element to another list. - RemoveElements(selectedElements); - if (_movingElementChanged != null) - { - SurfaceElementEventArgs eventArgs = new SurfaceElementEventArgs(); - _movingElementChanged(this, eventArgs); - } - } - } - - /// - /// Cut the selected elements from the surface to the clipboard - /// - public void CutSelectedElements() - { - if (HasSelectedElements) - { - ClipboardHelper.SetClipboardData(typeof(IDrawableContainerList), selectedElements); - RemoveSelectedElements(); - } - } - - /// - /// Copy the selected elements to the clipboard - /// - public void CopySelectedElements() - { - if (HasSelectedElements) - { - ClipboardHelper.SetClipboardData(typeof(IDrawableContainerList), selectedElements); - } - } - - /// - /// This method is called to confirm/cancel "confirmable" elements, like the crop-container. - /// Called when pressing enter or using the "check" in the editor. - /// - /// - public void ConfirmSelectedConfirmableElements(bool confirm) - { - // create new collection so that we can iterate safely (selectedElements might change due with confirm/cancel) - List selectedDCs = new List(selectedElements); - foreach (IDrawableContainer dc in selectedDCs) - { - if (dc.Equals(_cropContainer)) - { - DrawingMode = DrawingModes.None; - // No undo memento for the cropcontainer itself, only for the effect - RemoveElement(_cropContainer, false); - if (confirm) - { - ApplyCrop(_cropContainer.Bounds); - } - - _cropContainer.Dispose(); - _cropContainer = null; - break; - } - } - } - - /// - /// Paste all the elements that are on the clipboard - /// - public void PasteElementFromClipboard() - { - IDataObject clipboard = ClipboardHelper.GetDataObject(); - - var formats = ClipboardHelper.GetFormats(clipboard); - if (formats == null || formats.Count == 0) - { - return; - } - - if (LOG.IsDebugEnabled) - { - LOG.Debug("List of clipboard formats available for pasting:"); - foreach (string format in formats) - { - LOG.Debug("\tgot format: " + format); - } - } - - if (formats.Contains(typeof(IDrawableContainerList).FullName)) - { - IDrawableContainerList dcs = (IDrawableContainerList) ClipboardHelper.GetFromDataObject(clipboard, typeof(IDrawableContainerList)); - if (dcs != null) - { - // Make element(s) only move 10,10 if the surface is the same - bool isSameSurface = (dcs.ParentID == _uniqueId); - dcs.Parent = this; - var moveOffset = isSameSurface ? new Point(10, 10) : Point.Empty; - // Here a fix for bug #1475, first calculate the bounds of the complete IDrawableContainerList - Rectangle drawableContainerListBounds = Rectangle.Empty; - foreach (var element in dcs) - { - drawableContainerListBounds = drawableContainerListBounds == Rectangle.Empty - ? element.DrawingBounds - : Rectangle.Union(drawableContainerListBounds, element.DrawingBounds); - } - - // And find a location inside the target surface to paste to - bool containersCanFit = drawableContainerListBounds.Width < Bounds.Width && drawableContainerListBounds.Height < Bounds.Height; - if (!containersCanFit) - { - Point containersLocation = drawableContainerListBounds.Location; - containersLocation.Offset(moveOffset); - if (!Bounds.Contains(containersLocation)) - { - // Easy fix for same surface - moveOffset = isSameSurface - ? new Point(-10, -10) - : new Point(-drawableContainerListBounds.Location.X + 10, -drawableContainerListBounds.Location.Y + 10); - } - } - else - { - Rectangle moveContainerListBounds = drawableContainerListBounds; - moveContainerListBounds.Offset(moveOffset); - // check if the element is inside - if (!Bounds.Contains(moveContainerListBounds)) - { - // Easy fix for same surface - if (isSameSurface) - { - moveOffset = new Point(-10, -10); - } - else - { - // For different surface, which is most likely smaller - int offsetX = 0; - int offsetY = 0; - if (drawableContainerListBounds.Right > Bounds.Right) - { - offsetX = Bounds.Right - drawableContainerListBounds.Right; - // Correction for the correction - if (drawableContainerListBounds.Left + offsetX < 0) - { - offsetX += Math.Abs(drawableContainerListBounds.Left + offsetX); - } - } - - if (drawableContainerListBounds.Bottom > Bounds.Bottom) - { - offsetY = Bounds.Bottom - drawableContainerListBounds.Bottom; - // Correction for the correction - if (drawableContainerListBounds.Top + offsetY < 0) - { - offsetY += Math.Abs(drawableContainerListBounds.Top + offsetY); - } - } - - moveOffset = new Point(offsetX, offsetY); - } - } - } - - dcs.MoveBy(moveOffset.X, moveOffset.Y); - AddElements(dcs); - FieldAggregator.BindElements(dcs); - DeselectAllElements(); - SelectElements(dcs); - } - } - else if (ClipboardHelper.ContainsImage(clipboard)) - { - Point pasteLocation = GetPasteLocation(0.1f, 0.1f); - - foreach (Image clipboardImage in ClipboardHelper.GetImages(clipboard)) - { - if (clipboardImage != null) - { - DeselectAllElements(); - IImageContainer container = AddImageContainer(clipboardImage as Bitmap, pasteLocation.X, pasteLocation.Y); - SelectElement(container); - clipboardImage.Dispose(); - pasteLocation.X += 10; - pasteLocation.Y += 10; - } - } - } - else if (ClipboardHelper.ContainsText(clipboard)) - { - Point pasteLocation = GetPasteLocation(0.4f, 0.4f); - - string text = ClipboardHelper.GetText(clipboard); - if (text != null) - { - DeselectAllElements(); - ITextContainer textContainer = AddTextContainer(text, pasteLocation.X, pasteLocation.Y, - FontFamily.GenericSansSerif, 12f, false, false, false, 2, Color.Black, Color.Transparent); - SelectElement(textContainer); - } - } - } - - /// - /// Find a location to paste elements. - /// If mouse is over the surface - use it's position, otherwise use the visible area. - /// Return a point in image coordinate space. - /// - /// 0.0f for the left edge of visible area, 1.0f for the right edge of visible area. - /// 0.0f for the top edge of visible area, 1.0f for the bottom edge of visible area. - private Point GetPasteLocation(float horizontalRatio = 0.5f, float verticalRatio = 0.5f) - { - var point = PointToClient(MousePosition); - var rc = GetVisibleRectangle(); - if (!rc.Contains(point)) - { - point = new Point( - rc.Left + (int) (rc.Width * horizontalRatio), - rc.Top + (int) (rc.Height * verticalRatio) - ); - } - - return ToImageCoordinates(point); - } - - /// - /// Get the rectangle bounding the part of this Surface currently visible in the editor (in surface coordinate space). - /// - public Rectangle GetVisibleRectangle() - { - var bounds = Bounds; - var clientArea = Parent.ClientRectangle; - return new Rectangle( - Math.Max(0, -bounds.Left), - Math.Max(0, -bounds.Top), - clientArea.Width, - clientArea.Height - ); - } - - /// - /// Get the rectangle bounding all selected elements (in surface coordinates space), - /// or empty rectangle if nothing is selcted. - /// - public Rectangle GetSelectionRectangle() - => ToSurfaceCoordinates(selectedElements.DrawingBounds); - - /// - /// Duplicate all the selecteded elements - /// - public void DuplicateSelectedElements() - { - LOG.DebugFormat("Duplicating {0} selected elements", selectedElements.Count); - IDrawableContainerList dcs = selectedElements.Clone(); - dcs.Parent = this; - dcs.MoveBy(10, 10); - AddElements(dcs); - DeselectAllElements(); - SelectElements(dcs); - } - - /// - /// Deselect the specified element - /// - /// IDrawableContainerList - /// false to skip event generation - public void DeselectElement(IDrawableContainer container, bool generateEvents = true) - { - container.Selected = false; - selectedElements.Remove(container); - FieldAggregator.UnbindElement(container); - if (generateEvents && _movingElementChanged != null) - { - var eventArgs = new SurfaceElementEventArgs - { - Elements = selectedElements - }; - _movingElementChanged(this, eventArgs); - } - } - - /// - /// Deselect the specified elements - /// - /// IDrawableContainerList - public void DeselectElements(IDrawableContainerList elements) - { - if (elements.Count == 0) - { - return; - } - - while (elements.Count > 0) - { - var element = elements[0]; - DeselectElement(element, false); - } - - if (_movingElementChanged != null) - { - SurfaceElementEventArgs eventArgs = new SurfaceElementEventArgs - { - Elements = selectedElements - }; - _movingElementChanged(this, eventArgs); - } - - Invalidate(); - } - - /// - /// Deselect all the selected elements - /// - public void DeselectAllElements() - { - DeselectElements(selectedElements); - } - - /// - /// Select the supplied element - /// - /// - /// false to skip invalidation - /// false to skip event generation - public void SelectElement(IDrawableContainer container, bool invalidate = true, bool generateEvents = true) - { - if (!selectedElements.Contains(container)) - { - selectedElements.Add(container); - container.Selected = true; - FieldAggregator.BindElement(container); - if (generateEvents && _movingElementChanged != null) - { - SurfaceElementEventArgs eventArgs = new SurfaceElementEventArgs - { - Elements = selectedElements - }; - _movingElementChanged(this, eventArgs); - } - - if (invalidate) - { - container.Invalidate(); - } - } - } - - /// - /// Select all elements, this is called when Ctrl+A is pressed - /// - public void SelectAllElements() - { - SelectElements(_elements); - } - - /// - /// Select the supplied elements - /// - /// - public void SelectElements(IDrawableContainerList elements) - { - SuspendLayout(); - foreach (var drawableContainer in elements) - { - var element = (DrawableContainer) drawableContainer; - SelectElement(element, false, false); - } - - if (_movingElementChanged != null) - { - SurfaceElementEventArgs eventArgs = new SurfaceElementEventArgs - { - Elements = selectedElements - }; - _movingElementChanged(this, eventArgs); - } - - ResumeLayout(); - Invalidate(); - } - - /// - /// Process key presses on the surface, this is called from the editor (and NOT an override from the Control) - /// - /// Keys - /// false if no keys were processed - public bool ProcessCmdKey(Keys k) - { - if (selectedElements.Count > 0) - { - bool shiftModifier = (ModifierKeys & Keys.Shift) == Keys.Shift; - int px = shiftModifier ? 10 : 1; - Point moveBy = Point.Empty; - - switch (k) - { - case Keys.Left: - case Keys.Left | Keys.Shift: - moveBy = new Point(-px, 0); - break; - case Keys.Up: - case Keys.Up | Keys.Shift: - moveBy = new Point(0, -px); - break; - case Keys.Right: - case Keys.Right | Keys.Shift: - moveBy = new Point(px, 0); - break; - case Keys.Down: - case Keys.Down | Keys.Shift: - moveBy = new Point(0, px); - break; - case Keys.PageUp: - PullElementsUp(); - break; - case Keys.PageDown: - PushElementsDown(); - break; - case Keys.Home: - PullElementsToTop(); - break; - case Keys.End: - PushElementsToBottom(); - break; - case Keys.Enter: - ConfirmSelectedConfirmableElements(true); - break; - case Keys.Escape: - ConfirmSelectedConfirmableElements(false); - break; - /*case Keys.Delete: - RemoveSelectedElements(); - break;*/ - default: - return false; - } - - if (!Point.Empty.Equals(moveBy)) - { - selectedElements.MakeBoundsChangeUndoable(true); - selectedElements.MoveBy(moveBy.X, moveBy.Y); - } - - return true; - } - - return false; - } - - /// - /// Property for accessing the elements on the surface - /// - public IDrawableContainerList Elements => _elements; - - /// - /// pulls selected elements up one level in hierarchy - /// - public void PullElementsUp() - { - _elements.PullElementsUp(selectedElements); - _elements.Invalidate(); - } - - /// - /// pushes selected elements up to top in hierarchy - /// - public void PullElementsToTop() - { - _elements.PullElementsToTop(selectedElements); - _elements.Invalidate(); - } - - /// - /// pushes selected elements down one level in hierarchy - /// - public void PushElementsDown() - { - _elements.PushElementsDown(selectedElements); - _elements.Invalidate(); - } - - /// - /// pushes selected elements down to bottom in hierarchy - /// - public void PushElementsToBottom() - { - _elements.PushElementsToBottom(selectedElements); - _elements.Invalidate(); - } - - /// - /// indicates whether the selected elements could be pulled up in hierarchy - /// - /// true if selected elements could be pulled up, false otherwise - public bool CanPullSelectionUp() - { - return _elements.CanPullUp(selectedElements); - } - - /// - /// indicates whether the selected elements could be pushed down in hierarchy - /// - /// true if selected elements could be pushed down, false otherwise - public bool CanPushSelectionDown() - { - return _elements.CanPushDown(selectedElements); - } - - public void Element_FieldChanged(object sender, FieldChangedEventArgs e) - { - selectedElements.HandleFieldChangedEvent(sender, e); - } - - public bool IsOnSurface(IDrawableContainer container) - { - return _elements.Contains(container); - } - - public Point ToSurfaceCoordinates(Point point) - { - Point[] points = - { - point - }; - _zoomMatrix.TransformPoints(points); - return points[0]; - } - - public Rectangle ToSurfaceCoordinates(Rectangle rc) - { - if (_zoomMatrix.IsIdentity) - { - return rc; - } - else - { - Point[] points = - { - rc.Location, rc.Location + rc.Size - }; - _zoomMatrix.TransformPoints(points); - return new Rectangle( - points[0].X, - points[0].Y, - points[1].X - points[0].X, - points[1].Y - points[0].Y - ); - } - } - - public Point ToImageCoordinates(Point point) - { - Point[] points = - { - point - }; - _inverseZoomMatrix.TransformPoints(points); - return points[0]; - } - - public Rectangle ToImageCoordinates(Rectangle rc) - { - if (_inverseZoomMatrix.IsIdentity) - { - return rc; - } - else - { - Point[] points = - { - rc.Location, rc.Location + rc.Size - }; - _inverseZoomMatrix.TransformPoints(points); - return new Rectangle( - points[0].X, - points[0].Y, - points[1].X - points[0].X, - points[1].Y - points[0].Y - ); - } - } - } +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.Drawing.Drawing2D; +using System.Drawing.Imaging; +using System.IO; +using System.Runtime.Serialization.Formatters.Binary; +using System.Windows.Forms; +using Greenshot.Base.Controls; +using Greenshot.Base.Core; +using Greenshot.Base.Effects; +using Greenshot.Base.IniFile; +using Greenshot.Base.Interfaces; +using Greenshot.Base.Interfaces.Drawing; +using Greenshot.Base.Interfaces.Drawing.Adorners; +using Greenshot.Editor.Configuration; +using Greenshot.Editor.Drawing.Fields; +using Greenshot.Editor.Helpers; +using Greenshot.Editor.Memento; +using log4net; + +namespace Greenshot.Editor.Drawing +{ + /// + /// Description of Surface. + /// + public sealed class Surface : Control, ISurface, INotifyPropertyChanged + { + private static readonly ILog LOG = LogManager.GetLogger(typeof(Surface)); + public static int Count; + private static readonly CoreConfiguration conf = IniConfig.GetIniSection(); + + // Property to identify the Surface ID + private Guid _uniqueId = Guid.NewGuid(); + + /// + /// This value is used to start counting the step labels + /// + private int _counterStart = 1; + + /// + /// The GUID of the surface + /// + public Guid ID + { + get => _uniqueId; + set => _uniqueId = value; + } + + /// + /// Event handlers (do not serialize!) + /// + [NonSerialized] private PropertyChangedEventHandler _propertyChanged; + + public event PropertyChangedEventHandler PropertyChanged + { + add => _propertyChanged += value; + remove => _propertyChanged -= value; + } + + [NonSerialized] private SurfaceElementEventHandler _movingElementChanged; + + public event SurfaceElementEventHandler MovingElementChanged + { + add => _movingElementChanged += value; + remove => _movingElementChanged -= value; + } + + [NonSerialized] private SurfaceDrawingModeEventHandler _drawingModeChanged; + + public event SurfaceDrawingModeEventHandler DrawingModeChanged + { + add => _drawingModeChanged += value; + remove => _drawingModeChanged -= value; + } + + [NonSerialized] private SurfaceSizeChangeEventHandler _surfaceSizeChanged; + + public event SurfaceSizeChangeEventHandler SurfaceSizeChanged + { + add => _surfaceSizeChanged += value; + remove => _surfaceSizeChanged -= value; + } + + [NonSerialized] private SurfaceMessageEventHandler _surfaceMessage; + + public event SurfaceMessageEventHandler SurfaceMessage + { + add => _surfaceMessage += value; + remove => _surfaceMessage -= value; + } + + /// + /// inUndoRedo makes sure we don't undo/redo while in a undo/redo action + /// + [NonSerialized] private bool _inUndoRedo; + + /// + /// Make only one surface move cycle undoable, see SurfaceMouseMove + /// + [NonSerialized] private bool _isSurfaceMoveMadeUndoable; + + /// + /// Undo/Redo stacks, should not be serialized as the file would be way to big + /// + [NonSerialized] private readonly Stack _undoStack = new Stack(); + + [NonSerialized] private readonly Stack _redoStack = new Stack(); + + /// + /// Last save location, do not serialize! + /// + [NonSerialized] private string _lastSaveFullPath; + + /// + /// current drawing mode, do not serialize! + /// + [NonSerialized] private DrawingModes _drawingMode = DrawingModes.None; + + /// + /// the keys-locked flag helps with focus issues + /// + [NonSerialized] private bool _keysLocked; + + /// + /// Location of the mouse-down (it "starts" here), do not serialize + /// + [NonSerialized] private Point _mouseStart = Point.Empty; + + /// + /// are we in a mouse down, do not serialize + /// + [NonSerialized] private bool _mouseDown; + + /// + /// The selected element for the mouse down, do not serialize + /// + [NonSerialized] private IDrawableContainer _mouseDownElement; + + /// + /// all selected elements, do not serialize + /// + [NonSerialized] private readonly IDrawableContainerList selectedElements; + + /// + /// the element we are drawing with, do not serialize + /// + [NonSerialized] private IDrawableContainer _drawingElement; + + /// + /// the element we want to draw with (not yet drawn), do not serialize + /// + [NonSerialized] private IDrawableContainer _undrawnElement; + + /// + /// the cropcontainer, when cropping this is set, do not serialize + /// + [NonSerialized] private IDrawableContainer _cropContainer; + + /// + /// the brush which is used for transparent backgrounds, set by the editor, do not serialize + /// + [NonSerialized] private Brush _transparencyBackgroundBrush; + + /// + /// The buffer is only for drawing on it when using filters (to supply access) + /// This saves a lot of "create new bitmap" commands + /// Should not be serialized, as it's generated. + /// The actual bitmap is in the paintbox... + /// TODO: Check if this buffer is still needed! + /// + [NonSerialized] private Bitmap _buffer; + + /// + /// all stepLabels for the surface, needed with serialization + /// + private readonly List _stepLabels = new List(); + + public void AddStepLabel(StepLabelContainer stepLabel) + { + if (!_stepLabels.Contains(stepLabel)) + { + _stepLabels.Add(stepLabel); + } + } + + public void RemoveStepLabel(StepLabelContainer stepLabel) + { + _stepLabels.Remove(stepLabel); + } + + /// + /// The start value of the counter objects + /// + public int CounterStart + { + get => _counterStart; + set + { + if (_counterStart == value) + { + return; + } + + _counterStart = value; + Invalidate(); + _propertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(CounterStart))); + } + } + + /// + /// Count all the VISIBLE steplabels in the surface, up to the supplied one + /// + /// can be null, if not the counting stops here + /// number of steplabels before the supplied container + public int CountStepLabels(IDrawableContainer stopAtContainer) + { + int number = CounterStart; + foreach (var possibleThis in _stepLabels) + { + if (possibleThis.Equals(stopAtContainer)) + { + break; + } + + if (IsOnSurface(possibleThis)) + { + number++; + } + } + + return number; + } + + /// + /// all elements on the surface, needed with serialization + /// + private readonly IDrawableContainerList _elements; + + /// + /// all elements on the surface, needed with serialization + /// + private FieldAggregator _fieldAggregator; + + /// + /// the cursor container, needed with serialization as we need a direct acces to it. + /// + private IDrawableContainer _cursorContainer; + + /// + /// the modified flag specifies if the surface has had modifications after the last export. + /// Initial state is modified, as "it's not saved" + /// After serialization this should actually be "false" (the surface came from a stream) + /// For now we just serialize it... + /// + private bool _modified = true; + + /// + /// The image is the actual captured image, needed with serialization + /// + private Image _image; + + public Image Image + { + get => _image; + set + { + _image = value; + UpdateSize(); + } + } + + [NonSerialized] private Matrix _zoomMatrix = new Matrix(1, 0, 0, 1, 0, 0); + [NonSerialized] private Matrix _inverseZoomMatrix = new Matrix(1, 0, 0, 1, 0, 0); + [NonSerialized] private Fraction _zoomFactor = Fraction.Identity; + + public Fraction ZoomFactor + { + get => _zoomFactor; + set + { + _zoomFactor = value; + var inverse = _zoomFactor.Inverse(); + _zoomMatrix = new Matrix(_zoomFactor, 0, 0, _zoomFactor, 0, 0); + _inverseZoomMatrix = new Matrix(inverse, 0, 0, inverse, 0, 0); + UpdateSize(); + } + } + + + /// + /// Sets the surface size as zoomed image size. + /// + private void UpdateSize() + { + var size = _image.Size; + Size = new Size((int) (size.Width * _zoomFactor), (int) (size.Height * _zoomFactor)); + } + + /// + /// The field aggregator is that which is used to have access to all the fields inside the currently selected elements. + /// e.g. used to decided if and which line thickness is shown when multiple elements are selected. + /// + public FieldAggregator FieldAggregator + { + get => _fieldAggregator; + set => _fieldAggregator = value; + } + + /// + /// The cursor container has it's own accessor so we can find and remove this (when needed) + /// + public IDrawableContainer CursorContainer => _cursorContainer; + + /// + /// A simple getter to ask if this surface has a cursor + /// + public bool HasCursor => _cursorContainer != null; + + /// + /// A simple helper method to remove the cursor from the surface + /// + public void RemoveCursor() + { + RemoveElement(_cursorContainer); + _cursorContainer = null; + } + + /// + /// The brush which is used to draw the transparent background + /// + public Brush TransparencyBackgroundBrush + { + get => _transparencyBackgroundBrush; + set => _transparencyBackgroundBrush = value; + } + + /// + /// Are the keys on this surface locked? + /// + public bool KeysLocked + { + get => _keysLocked; + set => _keysLocked = value; + } + + /// + /// Is this surface modified? This is only true if the surface has not been exported. + /// + public bool Modified + { + get => _modified; + set => _modified = value; + } + + /// + /// The DrawingMode property specifies the mode for drawing, more or less the element type. + /// + public DrawingModes DrawingMode + { + get => _drawingMode; + set + { + _drawingMode = value; + if (_drawingModeChanged != null) + { + SurfaceDrawingModeEventArgs eventArgs = new SurfaceDrawingModeEventArgs + { + DrawingMode = _drawingMode + }; + _drawingModeChanged.Invoke(this, eventArgs); + } + + DeselectAllElements(); + CreateUndrawnElement(); + } + } + + /// + /// Property for accessing the last save "full" path + /// + public string LastSaveFullPath + { + get => _lastSaveFullPath; + set => _lastSaveFullPath = value; + } + + /// + /// Property for accessing the URL to which the surface was recently uploaded + /// + public string UploadUrl { get; set; } + + /// + /// Property for accessing the capture details + /// + public ICaptureDetails CaptureDetails { get; set; } + + /// + /// Adjust UI elements to the supplied DPI settings + /// + /// + public void AdjustToDpi(uint dpi) + { + foreach (var element in this._elements) + { + element.AdjustToDpi(dpi); + } + } + + /// + /// Base Surface constructor + /// + public Surface() + { + _fieldAggregator = new FieldAggregator(this); + Count++; + _elements = new DrawableContainerList(_uniqueId); + selectedElements = new DrawableContainerList(_uniqueId); + LOG.Debug("Creating surface!"); + MouseDown += SurfaceMouseDown; + MouseUp += SurfaceMouseUp; + MouseMove += SurfaceMouseMove; + MouseDoubleClick += SurfaceDoubleClick; + Paint += SurfacePaint; + AllowDrop = true; + DragDrop += OnDragDrop; + DragEnter += OnDragEnter; + // bind selected & elements to this, otherwise they can't inform of modifications + selectedElements.Parent = this; + _elements.Parent = this; + // Make sure we are visible + Visible = true; + TabStop = false; + // Enable double buffering + DoubleBuffered = true; + SetStyle( + ControlStyles.UserPaint | ControlStyles.AllPaintingInWmPaint | ControlStyles.ResizeRedraw | ControlStyles.ContainerControl | ControlStyles.OptimizedDoubleBuffer | + ControlStyles.SupportsTransparentBackColor, true); + } + + /// + /// Private method, the current image is disposed the new one will stay. + /// + /// The new image + /// true if the old image needs to be disposed, when using undo this should not be true!! + private void SetImage(Image newImage, bool dispose) + { + // Dispose + if (_image != null && dispose) + { + _image.Dispose(); + } + + // Set new values + Image = newImage; + + _modified = true; + } + + /// + /// Surface constructor with an image + /// + /// + public Surface(Image newImage) : this() + { + LOG.DebugFormat("Got image with dimensions {0} and format {1}", newImage.Size, newImage.PixelFormat); + SetImage(newImage, true); + } + + /// + /// Surface contructor with a capture + /// + /// + public Surface(ICapture capture) : this(capture.Image) + { + // check if cursor is captured, and visible + if (capture.Cursor != null && capture.CursorVisible) + { + Rectangle cursorRect = new Rectangle(capture.CursorLocation, capture.Cursor.Size); + Rectangle captureRect = new Rectangle(Point.Empty, capture.Image.Size); + // check if cursor is on the capture, otherwise we leave it out. + if (cursorRect.IntersectsWith(captureRect)) + { + _cursorContainer = AddIconContainer(capture.Cursor, capture.CursorLocation.X, capture.CursorLocation.Y); + SelectElement(_cursorContainer); + } + } + + // Make sure the image is NOT disposed, we took the reference directly into ourselves + ((Capture) capture).NullImage(); + + CaptureDetails = capture.CaptureDetails; + } + + protected override void Dispose(bool disposing) + { + if (disposing) + { + Count--; + LOG.Debug("Disposing surface!"); + if (_buffer != null) + { + _buffer.Dispose(); + _buffer = null; + } + + if (_transparencyBackgroundBrush != null) + { + _transparencyBackgroundBrush.Dispose(); + _transparencyBackgroundBrush = null; + } + + // Cleanup undo/redo stacks + while (_undoStack != null && _undoStack.Count > 0) + { + _undoStack.Pop().Dispose(); + } + + while (_redoStack != null && _redoStack.Count > 0) + { + _redoStack.Pop().Dispose(); + } + + foreach (IDrawableContainer container in _elements) + { + container.Dispose(); + } + + if (_undrawnElement != null) + { + _undrawnElement.Dispose(); + _undrawnElement = null; + } + + if (_cropContainer != null) + { + _cropContainer.Dispose(); + _cropContainer = null; + } + } + + base.Dispose(disposing); + } + + /// + /// Undo the last action + /// + public void Undo() + { + if (_undoStack.Count > 0) + { + _inUndoRedo = true; + IMemento top = _undoStack.Pop(); + _redoStack.Push(top.Restore()); + _inUndoRedo = false; + } + } + + /// + /// Undo an undo (=redo) + /// + public void Redo() + { + if (_redoStack.Count > 0) + { + _inUndoRedo = true; + IMemento top = _redoStack.Pop(); + _undoStack.Push(top.Restore()); + _inUndoRedo = false; + } + } + + /// + /// Returns if the surface can do a undo + /// + public bool CanUndo => _undoStack.Count > 0; + + /// + /// Returns if the surface can do a redo + /// + public bool CanRedo => _redoStack.Count > 0; + + /// + /// Get the language key for the undo action + /// + public LangKey UndoActionLanguageKey => LangKey.none; + + /// + /// Get the language key for redo action + /// + public LangKey RedoActionLanguageKey => LangKey.none; + + /// + /// Make an action undo-able + /// + /// The memento implementing the undo + /// Allow changes to be merged + public void MakeUndoable(IMemento memento, bool allowMerge) + { + if (_inUndoRedo) + { + throw new InvalidOperationException("Invoking do within an undo/redo action."); + } + + if (memento != null) + { + bool allowPush = true; + if (_undoStack.Count > 0 && allowMerge) + { + // Check if merge is possible + allowPush = !_undoStack.Peek().Merge(memento); + } + + if (allowPush) + { + // Clear the redo-stack and dispose + while (_redoStack.Count > 0) + { + _redoStack.Pop().Dispose(); + } + + _undoStack.Push(memento); + } + } + } + + /// + /// This saves the elements of this surface to a stream. + /// Is used to save a template of the complete surface + /// + /// + /// + public long SaveElementsToStream(Stream streamWrite) + { + long bytesWritten = 0; + try + { + long lengtBefore = streamWrite.Length; + BinaryFormatter binaryWrite = new BinaryFormatter(); + binaryWrite.Serialize(streamWrite, _elements); + bytesWritten = streamWrite.Length - lengtBefore; + } + catch (Exception e) + { + LOG.Error("Error serializing elements to stream.", e); + } + + return bytesWritten; + } + + /// + /// This loads elements from a stream, among others this is used to load a surface. + /// + /// + public void LoadElementsFromStream(Stream streamRead) + { + try + { + BinaryFormatter binaryRead = new BinaryFormatter(); + IDrawableContainerList loadedElements = (IDrawableContainerList) binaryRead.Deserialize(streamRead); + loadedElements.Parent = this; + // Make sure the steplabels are sorted accoring to their number + _stepLabels.Sort((p1, p2) => p1.Number.CompareTo(p2.Number)); + DeselectAllElements(); + AddElements(loadedElements); + SelectElements(loadedElements); + FieldAggregator.BindElements(loadedElements); + } + catch (Exception e) + { + LOG.Error("Error serializing elements from stream.", e); + } + } + + /// + /// This is called from the DrawingMode setter, which is not very correct... + /// But here an element is created which is not yet draw, thus "undrawnElement". + /// The element is than used while drawing on the surface. + /// + private void CreateUndrawnElement() + { + if (_undrawnElement != null) + { + FieldAggregator.UnbindElement(_undrawnElement); + } + + switch (DrawingMode) + { + case DrawingModes.Rect: + _undrawnElement = new RectangleContainer(this); + break; + case DrawingModes.Ellipse: + _undrawnElement = new EllipseContainer(this); + break; + case DrawingModes.Text: + _undrawnElement = new TextContainer(this); + break; + case DrawingModes.SpeechBubble: + _undrawnElement = new SpeechbubbleContainer(this); + break; + case DrawingModes.StepLabel: + _undrawnElement = new StepLabelContainer(this); + break; + case DrawingModes.Line: + _undrawnElement = new LineContainer(this); + break; + case DrawingModes.Arrow: + _undrawnElement = new ArrowContainer(this); + break; + case DrawingModes.Highlight: + _undrawnElement = new HighlightContainer(this); + break; + case DrawingModes.Obfuscate: + _undrawnElement = new ObfuscateContainer(this); + break; + case DrawingModes.Crop: + _cropContainer = new CropContainer(this); + _undrawnElement = _cropContainer; + break; + case DrawingModes.Bitmap: + _undrawnElement = new ImageContainer(this); + break; + case DrawingModes.Path: + _undrawnElement = new FreehandContainer(this); + break; + case DrawingModes.None: + _undrawnElement = null; + break; + } + + if (_undrawnElement != null) + { + FieldAggregator.BindElement(_undrawnElement); + } + } + + #region Plugin interface implementations + + public IImageContainer AddImageContainer(Image image, int x, int y) + { + ImageContainer bitmapContainer = new ImageContainer(this) + { + Image = image, + Left = x, + Top = y + }; + AddElement(bitmapContainer); + return bitmapContainer; + } + + public IImageContainer AddImageContainer(string filename, int x, int y) + { + ImageContainer bitmapContainer = new ImageContainer(this); + bitmapContainer.Load(filename); + bitmapContainer.Left = x; + bitmapContainer.Top = y; + AddElement(bitmapContainer); + return bitmapContainer; + } + + public IIconContainer AddIconContainer(Icon icon, int x, int y) + { + IconContainer iconContainer = new IconContainer(this) + { + Icon = icon, + Left = x, + Top = y + }; + AddElement(iconContainer); + return iconContainer; + } + + public IIconContainer AddIconContainer(string filename, int x, int y) + { + IconContainer iconContainer = new IconContainer(this); + iconContainer.Load(filename); + iconContainer.Left = x; + iconContainer.Top = y; + AddElement(iconContainer); + return iconContainer; + } + + public ICursorContainer AddCursorContainer(Cursor cursor, int x, int y) + { + CursorContainer cursorContainer = new CursorContainer(this) + { + Cursor = cursor, + Left = x, + Top = y + }; + AddElement(cursorContainer); + return cursorContainer; + } + + public ICursorContainer AddCursorContainer(string filename, int x, int y) + { + CursorContainer cursorContainer = new CursorContainer(this); + cursorContainer.Load(filename); + cursorContainer.Left = x; + cursorContainer.Top = y; + AddElement(cursorContainer); + return cursorContainer; + } + + public ITextContainer AddTextContainer(string text, int x, int y, FontFamily family, float size, bool italic, bool bold, bool shadow, int borderSize, Color color, + Color fillColor) + { + TextContainer textContainer = new TextContainer(this) + { + Text = text, + Left = x, + Top = y + }; + textContainer.SetFieldValue(FieldType.FONT_FAMILY, family.Name); + textContainer.SetFieldValue(FieldType.FONT_BOLD, bold); + textContainer.SetFieldValue(FieldType.FONT_ITALIC, italic); + textContainer.SetFieldValue(FieldType.FONT_SIZE, size); + textContainer.SetFieldValue(FieldType.FILL_COLOR, fillColor); + textContainer.SetFieldValue(FieldType.LINE_COLOR, color); + textContainer.SetFieldValue(FieldType.LINE_THICKNESS, borderSize); + textContainer.SetFieldValue(FieldType.SHADOW, shadow); + // Make sure the Text fits + textContainer.FitToText(); + + //AggregatedProperties.UpdateElement(textContainer); + AddElement(textContainer); + return textContainer; + } + + #endregion + + #region DragDrop + + private void OnDragEnter(object sender, DragEventArgs e) + { + if (LOG.IsDebugEnabled) + { + LOG.Debug("DragEnter got following formats: "); + foreach (string format in ClipboardHelper.GetFormats(e.Data)) + { + LOG.Debug(format); + } + } + + if ((e.AllowedEffect & DragDropEffects.Copy) != DragDropEffects.Copy) + { + e.Effect = DragDropEffects.None; + } + else + { + if (ClipboardHelper.ContainsImage(e.Data) || ClipboardHelper.ContainsFormat(e.Data, "DragImageBits")) + { + e.Effect = DragDropEffects.Copy; + } + else + { + e.Effect = DragDropEffects.None; + } + } + } + + /// + /// Handle the drag/drop + /// + /// + /// + private void OnDragDrop(object sender, DragEventArgs e) + { + Point mouse = PointToClient(new Point(e.X, e.Y)); + if (e.Data.GetDataPresent("Text")) + { + string possibleUrl = ClipboardHelper.GetText(e.Data); + // Test if it's an url and try to download the image so we have it in the original form + if (possibleUrl != null && possibleUrl.StartsWith("http")) + { + using Image image = NetworkHelper.DownloadImage(possibleUrl); + if (image != null) + { + AddImageContainer(image, mouse.X, mouse.Y); + return; + } + } + } + + foreach (Image image in ClipboardHelper.GetImages(e.Data)) + { + AddImageContainer(image, mouse.X, mouse.Y); + mouse.Offset(10, 10); + image.Dispose(); + } + } + + #endregion + + /// + /// Auto crop the image + /// + /// true if cropped + public bool AutoCrop() + { + Rectangle cropRectangle; + using (Image tmpImage = GetImageForExport()) + { + cropRectangle = ImageHelper.FindAutoCropRectangle(tmpImage, conf.AutoCropDifference); + } + + if (!IsCropPossible(ref cropRectangle)) + { + return false; + } + + DeselectAllElements(); + // Maybe a bit obscure, but the following line creates a drop container + // It's available as "undrawnElement" + DrawingMode = DrawingModes.Crop; + _undrawnElement.Left = cropRectangle.X; + _undrawnElement.Top = cropRectangle.Y; + _undrawnElement.Width = cropRectangle.Width; + _undrawnElement.Height = cropRectangle.Height; + _undrawnElement.Status = EditStatus.UNDRAWN; + AddElement(_undrawnElement); + SelectElement(_undrawnElement); + _drawingElement = null; + _undrawnElement = null; + return true; + } + + /// + /// A simple clear + /// + /// The color for the background + public void Clear(Color newColor) + { + //create a blank bitmap the same size as original + Bitmap newBitmap = ImageHelper.CreateEmptyLike(Image, Color.Empty); + if (newBitmap != null) + { + // Make undoable + MakeUndoable(new SurfaceBackgroundChangeMemento(this, null), false); + SetImage(newBitmap, false); + Invalidate(); + } + } + + /// + /// Apply a bitmap effect to the surface + /// + /// + public void ApplyBitmapEffect(IEffect effect) + { + BackgroundForm backgroundForm = new BackgroundForm("Effect", "Please wait"); + backgroundForm.Show(); + Application.DoEvents(); + try + { + Rectangle imageRectangle = new Rectangle(Point.Empty, Image.Size); + Matrix matrix = new Matrix(); + Image newImage = ImageHelper.ApplyEffect(Image, effect, matrix); + if (newImage != null) + { + // Make sure the elements move according to the offset the effect made the bitmap move + _elements.Transform(matrix); + // Make undoable + MakeUndoable(new SurfaceBackgroundChangeMemento(this, matrix), false); + SetImage(newImage, false); + Invalidate(); + if (_surfaceSizeChanged != null && !imageRectangle.Equals(new Rectangle(Point.Empty, newImage.Size))) + { + _surfaceSizeChanged(this, null); + } + } + else + { + // clean up matrix, as it hasn't been used in the undo stack. + matrix.Dispose(); + } + } + finally + { + // Always close the background form + backgroundForm.CloseDialog(); + } + } + + /// + /// check if a crop is possible + /// + /// + /// true if this is possible + public bool IsCropPossible(ref Rectangle cropRectangle) + { + cropRectangle = GuiRectangle.GetGuiRectangle(cropRectangle.Left, cropRectangle.Top, cropRectangle.Width, cropRectangle.Height); + if (cropRectangle.Left < 0) + { + cropRectangle = new Rectangle(0, cropRectangle.Top, cropRectangle.Width + cropRectangle.Left, cropRectangle.Height); + } + + if (cropRectangle.Top < 0) + { + cropRectangle = new Rectangle(cropRectangle.Left, 0, cropRectangle.Width, cropRectangle.Height + cropRectangle.Top); + } + + if (cropRectangle.Left + cropRectangle.Width > Image.Width) + { + cropRectangle = new Rectangle(cropRectangle.Left, cropRectangle.Top, Image.Width - cropRectangle.Left, cropRectangle.Height); + } + + if (cropRectangle.Top + cropRectangle.Height > Image.Height) + { + cropRectangle = new Rectangle(cropRectangle.Left, cropRectangle.Top, cropRectangle.Width, Image.Height - cropRectangle.Top); + } + + if (cropRectangle.Height > 0 && cropRectangle.Width > 0) + { + return true; + } + + return false; + } + + /// + /// Use to send any registered SurfaceMessageEventHandler a message, e.g. used for the notification area + /// + /// Who send + /// Type of message + /// Message itself + public void SendMessageEvent(object source, SurfaceMessageTyp messageType, string message) + { + if (_surfaceMessage != null) + { + var eventArgs = new SurfaceMessageEventArgs + { + Message = message, + MessageType = messageType, + Surface = this + }; + _surfaceMessage(source, eventArgs); + } + } + + /// + /// Crop the surface + /// + /// + /// + public bool ApplyCrop(Rectangle cropRectangle) + { + if (IsCropPossible(ref cropRectangle)) + { + Rectangle imageRectangle = new Rectangle(Point.Empty, Image.Size); + Bitmap tmpImage; + // Make sure we have information, this this fails + try + { + tmpImage = ImageHelper.CloneArea(Image, cropRectangle, PixelFormat.DontCare); + } + catch (Exception ex) + { + ex.Data.Add("CropRectangle", cropRectangle); + ex.Data.Add("Width", Image.Width); + ex.Data.Add("Height", Image.Height); + ex.Data.Add("Pixelformat", Image.PixelFormat); + throw; + } + + Matrix matrix = new Matrix(); + matrix.Translate(-cropRectangle.Left, -cropRectangle.Top, MatrixOrder.Append); + // Make undoable + MakeUndoable(new SurfaceBackgroundChangeMemento(this, matrix), false); + + // Do not dispose otherwise we can't undo the image! + SetImage(tmpImage, false); + _elements.Transform(matrix); + if (_surfaceSizeChanged != null && !imageRectangle.Equals(new Rectangle(Point.Empty, tmpImage.Size))) + { + _surfaceSizeChanged(this, null); + } + + Invalidate(); + return true; + } + + return false; + } + + /// + /// The background here is the captured image. + /// This is called from the SurfaceBackgroundChangeMemento. + /// + /// + /// + public void UndoBackgroundChange(Image previous, Matrix matrix) + { + SetImage(previous, false); + if (matrix != null) + { + _elements.Transform(matrix); + } + + _surfaceSizeChanged?.Invoke(this, null); + Invalidate(); + } + + /// + /// Check if an adorner was "hit", and change the cursor if so + /// + /// MouseEventArgs + /// IAdorner + private IAdorner FindActiveAdorner(MouseEventArgs mouseEventArgs) + { + foreach (IDrawableContainer drawableContainer in selectedElements) + { + foreach (IAdorner adorner in drawableContainer.Adorners) + { + if (adorner.IsActive || adorner.HitTest(mouseEventArgs.Location)) + { + if (adorner.Cursor != null) + { + Cursor = adorner.Cursor; + } + + return adorner; + } + } + } + + return null; + } + + /// + /// Translate mouse coordinates as if they were applied directly to unscaled image. + /// + private MouseEventArgs InverseZoomMouseCoordinates(MouseEventArgs e) + => new MouseEventArgs(e.Button, e.Clicks, (int) (e.X / _zoomFactor), (int) (e.Y / _zoomFactor), e.Delta); + + /// + /// This event handler is called when someone presses the mouse on a surface. + /// + /// + /// + private void SurfaceMouseDown(object sender, MouseEventArgs e) + { + e = InverseZoomMouseCoordinates(e); + + // Handle Adorners + var adorner = FindActiveAdorner(e); + if (adorner != null) + { + adorner.MouseDown(sender, e); + return; + } + + _mouseStart = e.Location; + + // check contextmenu + if (e.Button == MouseButtons.Right) + { + IDrawableContainerList selectedList = null; + if (selectedElements != null && selectedElements.Count > 0) + { + selectedList = selectedElements; + } + else + { + // Single element + IDrawableContainer rightClickedContainer = _elements.ClickableElementAt(_mouseStart.X, _mouseStart.Y); + if (rightClickedContainer != null) + { + selectedList = new DrawableContainerList(ID) + { + rightClickedContainer + }; + } + } + + if (selectedList != null && selectedList.Count > 0) + { + selectedList.ShowContextMenu(e, this); + } + + return; + } + + _mouseDown = true; + _isSurfaceMoveMadeUndoable = false; + + if (_cropContainer != null && ((_undrawnElement == null) || (_undrawnElement != null && DrawingMode != DrawingModes.Crop))) + { + RemoveElement(_cropContainer, false); + _cropContainer = null; + _drawingElement = null; + } + + if (_drawingElement == null && DrawingMode != DrawingModes.None) + { + if (_undrawnElement == null) + { + DeselectAllElements(); + if (_undrawnElement == null) + { + CreateUndrawnElement(); + } + } + + _drawingElement = _undrawnElement; + // if a new element has been drawn, set location and register it + if (_drawingElement != null) + { + if (_undrawnElement != null) + { + _drawingElement.Status = _undrawnElement.DefaultEditMode; + } + + if (!_drawingElement.HandleMouseDown(_mouseStart.X, _mouseStart.Y)) + { + _drawingElement.Left = _mouseStart.X; + _drawingElement.Top = _mouseStart.Y; + } + + AddElement(_drawingElement); + _drawingElement.Selected = true; + } + + _undrawnElement = null; + } + else + { + // check whether an existing element was clicked + // we save mouse down element separately from selectedElements (checked on mouse up), + // since it could be moved around before it is actually selected + _mouseDownElement = _elements.ClickableElementAt(_mouseStart.X, _mouseStart.Y); + + if (_mouseDownElement != null) + { + _mouseDownElement.Status = EditStatus.MOVING; + } + } + } + + /// + /// This event handle is called when the mouse button is unpressed + /// + /// + /// + private void SurfaceMouseUp(object sender, MouseEventArgs e) + { + e = InverseZoomMouseCoordinates(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; + if (_mouseDownElement != null) + { + _mouseDownElement.Status = EditStatus.IDLE; + } + + _mouseDown = false; + _mouseDownElement = null; + if (DrawingMode == DrawingModes.None) + { + // check whether an existing element was clicked + IDrawableContainer element = _elements.ClickableElementAt(currentMouse.X, currentMouse.Y); + bool shiftModifier = (ModifierKeys & Keys.Shift) == Keys.Shift; + if (element != null) + { + element.Invalidate(); + bool alreadySelected = selectedElements.Contains(element); + if (shiftModifier) + { + if (alreadySelected) + { + DeselectElement(element); + } + else + { + SelectElement(element); + } + } + else + { + if (!alreadySelected) + { + DeselectAllElements(); + SelectElement(element); + } + } + } + else if (!shiftModifier) + { + DeselectAllElements(); + } + } + + if (selectedElements.Count > 0) + { + selectedElements.Invalidate(); + selectedElements.Selected = true; + } + + if (_drawingElement != null) + { + if (!_drawingElement.InitContent()) + { + _elements.Remove(_drawingElement); + _drawingElement.Invalidate(); + } + else + { + _drawingElement.HandleMouseUp(currentMouse.X, currentMouse.Y); + _drawingElement.Invalidate(); + if (Math.Abs(_drawingElement.Width) < 5 && Math.Abs(_drawingElement.Height) < 5) + { + _drawingElement.Width = 25; + _drawingElement.Height = 25; + } + + SelectElement(_drawingElement); + _drawingElement.Selected = true; + } + + _drawingElement = null; + } + } + + /// + /// This event handler is called when the mouse moves over the surface + /// + /// + /// + private void SurfaceMouseMove(object sender, MouseEventArgs e) + { + e = InverseZoomMouseCoordinates(e); + + // Handle Adorners + var adorner = FindActiveAdorner(e); + if (adorner != null) + { + adorner.MouseMove(sender, e); + return; + } + + Point currentMouse = e.Location; + + Cursor = DrawingMode != DrawingModes.None ? Cursors.Cross : Cursors.Default; + + if (_mouseDown) + { + if (_mouseDownElement != null) + { + // an element is currently dragged + _mouseDownElement.Invalidate(); + selectedElements.Invalidate(); + // Move the element + if (_mouseDownElement.Selected) + { + if (!_isSurfaceMoveMadeUndoable) + { + // Only allow one undoable per mouse-down/move/up "cycle" + _isSurfaceMoveMadeUndoable = true; + selectedElements.MakeBoundsChangeUndoable(false); + } + + // dragged element has been selected before -> move all + selectedElements.MoveBy(currentMouse.X - _mouseStart.X, currentMouse.Y - _mouseStart.Y); + } + else + { + if (!_isSurfaceMoveMadeUndoable) + { + // Only allow one undoable per mouse-down/move/up "cycle" + _isSurfaceMoveMadeUndoable = true; + _mouseDownElement.MakeBoundsChangeUndoable(false); + } + + // dragged element is not among selected elements -> just move dragged one + _mouseDownElement.MoveBy(currentMouse.X - _mouseStart.X, currentMouse.Y - _mouseStart.Y); + } + + _mouseStart = currentMouse; + _mouseDownElement.Invalidate(); + _modified = true; + } + else if (_drawingElement != null) + { + _drawingElement.HandleMouseMove(currentMouse.X, currentMouse.Y); + _modified = true; + } + } + } + + /// + /// This event handler is called when the surface is double clicked. + /// + /// + /// + private void SurfaceDoubleClick(object sender, MouseEventArgs e) + { + selectedElements.OnDoubleClick(); + selectedElements.Invalidate(); + } + + /// + /// Privately used to get the rendered image with all the elements on it. + /// + /// + /// + private Image GetImage(RenderMode renderMode) + { + // Generate a copy of the original image with a dpi equal to the default... + Bitmap clone = ImageHelper.Clone(_image, PixelFormat.DontCare); + // otherwise we would have a problem drawing the image to the surface... :( + using (Graphics graphics = Graphics.FromImage(clone)) + { + // Do not set the following, the containers need to decide themselves + //graphics.SmoothingMode = SmoothingMode.HighQuality; + //graphics.PixelOffsetMode = PixelOffsetMode.HighQuality; + //graphics.CompositingQuality = CompositingQuality.HighQuality; + //graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; + _elements.Draw(graphics, clone, renderMode, new Rectangle(Point.Empty, clone.Size)); + } + + return clone; + } + + /// + /// This returns the image "result" of this surface, with all the elements rendered on it. + /// + /// + public Image GetImageForExport() + { + return GetImage(RenderMode.EXPORT); + } + + private static Rectangle ZoomClipRectangle(Rectangle rc, double scale, int inflateAmount = 0) + { + rc = new Rectangle( + (int) (rc.X * scale), + (int) (rc.Y * scale), + (int) (rc.Width * scale) + 1, + (int) (rc.Height * scale) + 1 + ); + if (scale > 1) + { + inflateAmount = (int) (inflateAmount * scale); + } + + rc.Inflate(inflateAmount, inflateAmount); + return rc; + } + + public void InvalidateElements(Rectangle rc) + => Invalidate(ZoomClipRectangle(rc, _zoomFactor, 1)); + + /// + /// This is the event handler for the Paint Event, try to draw as little as possible! + /// + /// + /// PaintEventArgs + private void SurfacePaint(object sender, PaintEventArgs paintEventArgs) + { + Graphics targetGraphics = paintEventArgs.Graphics; + Rectangle targetClipRectangle = paintEventArgs.ClipRectangle; + if (Rectangle.Empty.Equals(targetClipRectangle)) + { + LOG.Debug("Empty cliprectangle??"); + return; + } + + // Correction to prevent rounding errors at certain zoom levels. + // When zooming to N/M, clip rectangle top and left coordinates should be multiples of N. + if (_zoomFactor.Numerator > 1 && _zoomFactor.Denominator > 1) + { + int horizontalCorrection = targetClipRectangle.Left % (int) _zoomFactor.Numerator; + int verticalCorrection = targetClipRectangle.Top % (int) _zoomFactor.Numerator; + if (horizontalCorrection != 0) + { + targetClipRectangle.X -= horizontalCorrection; + targetClipRectangle.Width += horizontalCorrection; + } + + if (verticalCorrection != 0) + { + targetClipRectangle.Y -= verticalCorrection; + targetClipRectangle.Height += verticalCorrection; + } + } + + Rectangle imageClipRectangle = ZoomClipRectangle(targetClipRectangle, _zoomFactor.Inverse(), 2); + + if (_elements.HasIntersectingFilters(imageClipRectangle) || _zoomFactor > Fraction.Identity) + { + if (_buffer != null) + { + if (_buffer.Width != Image.Width || _buffer.Height != Image.Height || _buffer.PixelFormat != Image.PixelFormat) + { + _buffer.Dispose(); + _buffer = null; + } + } + + if (_buffer == null) + { + _buffer = ImageHelper.CreateEmpty(Image.Width, Image.Height, Image.PixelFormat, Color.Empty, Image.HorizontalResolution, Image.VerticalResolution); + LOG.DebugFormat("Created buffer with size: {0}x{1}", Image.Width, Image.Height); + } + + // Elements might need the bitmap, so we copy the part we need + using (Graphics graphics = Graphics.FromImage(_buffer)) + { + // do not set the following, the containers need to decide this themselves! + //graphics.SmoothingMode = SmoothingMode.HighQuality; + //graphics.PixelOffsetMode = PixelOffsetMode.HighQuality; + //graphics.CompositingQuality = CompositingQuality.HighQuality; + //graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; + DrawBackground(graphics, imageClipRectangle); + graphics.DrawImage(Image, imageClipRectangle, imageClipRectangle, GraphicsUnit.Pixel); + graphics.SetClip(ZoomClipRectangle(Rectangle.Round(targetGraphics.ClipBounds), _zoomFactor.Inverse(), 2)); + _elements.Draw(graphics, _buffer, RenderMode.EDIT, imageClipRectangle); + } + + if (_zoomFactor == Fraction.Identity) + { + targetGraphics.DrawImage(_buffer, imageClipRectangle, imageClipRectangle, GraphicsUnit.Pixel); + } + else + { + targetGraphics.ScaleTransform(_zoomFactor, _zoomFactor); + if (_zoomFactor > Fraction.Identity) + { + DrawSharpImage(targetGraphics, _buffer, imageClipRectangle); + } + else + { + DrawSmoothImage(targetGraphics, _buffer, imageClipRectangle); + } + + targetGraphics.ResetTransform(); + } + } + else + { + DrawBackground(targetGraphics, targetClipRectangle); + if (_zoomFactor == Fraction.Identity) + { + targetGraphics.DrawImage(Image, imageClipRectangle, imageClipRectangle, GraphicsUnit.Pixel); + _elements.Draw(targetGraphics, null, RenderMode.EDIT, imageClipRectangle); + } + else + { + targetGraphics.ScaleTransform(_zoomFactor, _zoomFactor); + DrawSmoothImage(targetGraphics, Image, imageClipRectangle); + _elements.Draw(targetGraphics, null, RenderMode.EDIT, imageClipRectangle); + targetGraphics.ResetTransform(); + } + } + + // 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 DrawSmoothImage(Graphics targetGraphics, Image image, Rectangle imageClipRectangle) + { + var state = targetGraphics.Save(); + targetGraphics.SmoothingMode = SmoothingMode.HighQuality; + targetGraphics.InterpolationMode = InterpolationMode.HighQualityBilinear; + targetGraphics.CompositingQuality = CompositingQuality.HighQuality; + targetGraphics.PixelOffsetMode = PixelOffsetMode.None; + + targetGraphics.DrawImage(image, imageClipRectangle, imageClipRectangle, GraphicsUnit.Pixel); + + targetGraphics.Restore(state); + } + + private void DrawSharpImage(Graphics targetGraphics, Image image, Rectangle imageClipRectangle) + { + var state = targetGraphics.Save(); + targetGraphics.SmoothingMode = SmoothingMode.None; + targetGraphics.InterpolationMode = InterpolationMode.NearestNeighbor; + targetGraphics.CompositingQuality = CompositingQuality.HighQuality; + targetGraphics.PixelOffsetMode = PixelOffsetMode.None; + + targetGraphics.DrawImage(image, imageClipRectangle, imageClipRectangle, GraphicsUnit.Pixel); + + targetGraphics.Restore(state); + } + + private void DrawBackground(Graphics targetGraphics, Rectangle clipRectangle) + { + // check if we need to draw the checkerboard + if (Image.IsAlphaPixelFormat(Image.PixelFormat) && _transparencyBackgroundBrush != null) + { + targetGraphics.FillRectangle(_transparencyBackgroundBrush, clipRectangle); + } + else + { + targetGraphics.Clear(BackColor); + } + } + + /// + /// Draw a checkboard when capturing with transparency + /// + /// PaintEventArgs + protected override void OnPaintBackground(PaintEventArgs e) + { + } + + /// + /// Add a new element to the surface + /// + /// the new element + /// true if the adding should be undoable + /// true if invalidate needs to be called + public void AddElement(IDrawableContainer element, bool makeUndoable = true, bool invalidate = true) + { + _elements.Add(element); + if (element is DrawableContainer container) + { + container.FieldChanged += Element_FieldChanged; + } + + element.Parent = this; + if (element.Status == EditStatus.UNDRAWN) + { + element.Status = EditStatus.IDLE; + } + + if (element.Selected) + { + // Use false, as the element is invalidated when invalidate == true anyway + SelectElement(element, false); + } + + if (invalidate) + { + element.Invalidate(); + } + + if (makeUndoable) + { + MakeUndoable(new AddElementMemento(this, element), false); + } + + _modified = true; + } + + /// + /// Remove the list of elements + /// + /// IDrawableContainerList + /// flag specifying if the remove needs to be undoable + public void RemoveElements(IDrawableContainerList elementsToRemove, bool makeUndoable = true) + { + // fix potential issues with iterating a changing list + DrawableContainerList cloned = new DrawableContainerList(); + cloned.AddRange(elementsToRemove); + if (makeUndoable) + { + MakeUndoable(new DeleteElementsMemento(this, cloned), false); + } + + SuspendLayout(); + foreach (var drawableContainer in cloned) + { + RemoveElement(drawableContainer, false, false, false); + } + + ResumeLayout(); + Invalidate(); + if (_movingElementChanged != null) + { + SurfaceElementEventArgs eventArgs = new SurfaceElementEventArgs + { + Elements = cloned + }; + _movingElementChanged(this, eventArgs); + } + } + + /// + /// Remove an element of the elements list + /// + /// Element to remove + /// flag specifying if the remove needs to be undoable + /// flag specifying if an surface invalidate needs to be called + /// false to skip event generation + public void RemoveElement(IDrawableContainer elementToRemove, bool makeUndoable = true, bool invalidate = true, bool generateEvents = true) + { + DeselectElement(elementToRemove, generateEvents); + _elements.Remove(elementToRemove); + if (elementToRemove is DrawableContainer element) + { + element.FieldChanged -= Element_FieldChanged; + } + + if (elementToRemove != null) + { + elementToRemove.Parent = null; + } + + // Do not dispose, the memento should!! element.Dispose(); + if (invalidate) + { + Invalidate(); + } + + if (makeUndoable) + { + MakeUndoable(new DeleteElementMemento(this, elementToRemove), false); + } + + _modified = true; + } + + /// + /// Add the supplied elements to the surface + /// + /// DrawableContainerList + /// true if the adding should be undoable + public void AddElements(IDrawableContainerList elementsToAdd, bool makeUndoable = true) + { + // fix potential issues with iterating a changing list + DrawableContainerList cloned = new DrawableContainerList(); + cloned.AddRange(elementsToAdd); + if (makeUndoable) + { + MakeUndoable(new AddElementsMemento(this, cloned), false); + } + + SuspendLayout(); + foreach (var element in cloned) + { + element.Selected = true; + AddElement(element, false, false); + } + + ResumeLayout(); + Invalidate(); + } + + /// + /// Returns if this surface has selected elements + /// + /// + public bool HasSelectedElements => (selectedElements != null && selectedElements.Count > 0); + + /// + /// Remove all the selected elements + /// + public void RemoveSelectedElements() + { + if (HasSelectedElements) + { + // As RemoveElement will remove the element from the selectedElements list we need to copy the element to another list. + RemoveElements(selectedElements); + if (_movingElementChanged != null) + { + SurfaceElementEventArgs eventArgs = new SurfaceElementEventArgs(); + _movingElementChanged(this, eventArgs); + } + } + } + + /// + /// Cut the selected elements from the surface to the clipboard + /// + public void CutSelectedElements() + { + if (HasSelectedElements) + { + ClipboardHelper.SetClipboardData(typeof(IDrawableContainerList), selectedElements); + RemoveSelectedElements(); + } + } + + /// + /// Copy the selected elements to the clipboard + /// + public void CopySelectedElements() + { + if (HasSelectedElements) + { + ClipboardHelper.SetClipboardData(typeof(IDrawableContainerList), selectedElements); + } + } + + /// + /// This method is called to confirm/cancel "confirmable" elements, like the crop-container. + /// Called when pressing enter or using the "check" in the editor. + /// + /// + public void ConfirmSelectedConfirmableElements(bool confirm) + { + // create new collection so that we can iterate safely (selectedElements might change due with confirm/cancel) + List selectedDCs = new List(selectedElements); + foreach (IDrawableContainer dc in selectedDCs) + { + if (dc.Equals(_cropContainer)) + { + DrawingMode = DrawingModes.None; + // No undo memento for the cropcontainer itself, only for the effect + RemoveElement(_cropContainer, false); + if (confirm) + { + ApplyCrop(_cropContainer.Bounds); + } + + _cropContainer.Dispose(); + _cropContainer = null; + break; + } + } + } + + /// + /// Paste all the elements that are on the clipboard + /// + public void PasteElementFromClipboard() + { + IDataObject clipboard = ClipboardHelper.GetDataObject(); + + var formats = ClipboardHelper.GetFormats(clipboard); + if (formats == null || formats.Count == 0) + { + return; + } + + if (LOG.IsDebugEnabled) + { + LOG.Debug("List of clipboard formats available for pasting:"); + foreach (string format in formats) + { + LOG.Debug("\tgot format: " + format); + } + } + + if (formats.Contains(typeof(IDrawableContainerList).FullName)) + { + IDrawableContainerList dcs = (IDrawableContainerList) ClipboardHelper.GetFromDataObject(clipboard, typeof(IDrawableContainerList)); + if (dcs != null) + { + // Make element(s) only move 10,10 if the surface is the same + bool isSameSurface = (dcs.ParentID == _uniqueId); + dcs.Parent = this; + var moveOffset = isSameSurface ? new Point(10, 10) : Point.Empty; + // Here a fix for bug #1475, first calculate the bounds of the complete IDrawableContainerList + Rectangle drawableContainerListBounds = Rectangle.Empty; + foreach (var element in dcs) + { + drawableContainerListBounds = drawableContainerListBounds == Rectangle.Empty + ? element.DrawingBounds + : Rectangle.Union(drawableContainerListBounds, element.DrawingBounds); + } + + // And find a location inside the target surface to paste to + bool containersCanFit = drawableContainerListBounds.Width < Bounds.Width && drawableContainerListBounds.Height < Bounds.Height; + if (!containersCanFit) + { + Point containersLocation = drawableContainerListBounds.Location; + containersLocation.Offset(moveOffset); + if (!Bounds.Contains(containersLocation)) + { + // Easy fix for same surface + moveOffset = isSameSurface + ? new Point(-10, -10) + : new Point(-drawableContainerListBounds.Location.X + 10, -drawableContainerListBounds.Location.Y + 10); + } + } + else + { + Rectangle moveContainerListBounds = drawableContainerListBounds; + moveContainerListBounds.Offset(moveOffset); + // check if the element is inside + if (!Bounds.Contains(moveContainerListBounds)) + { + // Easy fix for same surface + if (isSameSurface) + { + moveOffset = new Point(-10, -10); + } + else + { + // For different surface, which is most likely smaller + int offsetX = 0; + int offsetY = 0; + if (drawableContainerListBounds.Right > Bounds.Right) + { + offsetX = Bounds.Right - drawableContainerListBounds.Right; + // Correction for the correction + if (drawableContainerListBounds.Left + offsetX < 0) + { + offsetX += Math.Abs(drawableContainerListBounds.Left + offsetX); + } + } + + if (drawableContainerListBounds.Bottom > Bounds.Bottom) + { + offsetY = Bounds.Bottom - drawableContainerListBounds.Bottom; + // Correction for the correction + if (drawableContainerListBounds.Top + offsetY < 0) + { + offsetY += Math.Abs(drawableContainerListBounds.Top + offsetY); + } + } + + moveOffset = new Point(offsetX, offsetY); + } + } + } + + dcs.MoveBy(moveOffset.X, moveOffset.Y); + AddElements(dcs); + FieldAggregator.BindElements(dcs); + DeselectAllElements(); + SelectElements(dcs); + } + } + else if (ClipboardHelper.ContainsImage(clipboard)) + { + Point pasteLocation = GetPasteLocation(0.1f, 0.1f); + + foreach (Image clipboardImage in ClipboardHelper.GetImages(clipboard)) + { + if (clipboardImage != null) + { + DeselectAllElements(); + IImageContainer container = AddImageContainer(clipboardImage as Bitmap, pasteLocation.X, pasteLocation.Y); + SelectElement(container); + clipboardImage.Dispose(); + pasteLocation.X += 10; + pasteLocation.Y += 10; + } + } + } + else if (ClipboardHelper.ContainsText(clipboard)) + { + Point pasteLocation = GetPasteLocation(0.4f, 0.4f); + + string text = ClipboardHelper.GetText(clipboard); + if (text != null) + { + DeselectAllElements(); + ITextContainer textContainer = AddTextContainer(text, pasteLocation.X, pasteLocation.Y, + FontFamily.GenericSansSerif, 12f, false, false, false, 2, Color.Black, Color.Transparent); + SelectElement(textContainer); + } + } + } + + /// + /// Find a location to paste elements. + /// If mouse is over the surface - use it's position, otherwise use the visible area. + /// Return a point in image coordinate space. + /// + /// 0.0f for the left edge of visible area, 1.0f for the right edge of visible area. + /// 0.0f for the top edge of visible area, 1.0f for the bottom edge of visible area. + private Point GetPasteLocation(float horizontalRatio = 0.5f, float verticalRatio = 0.5f) + { + var point = PointToClient(MousePosition); + var rc = GetVisibleRectangle(); + if (!rc.Contains(point)) + { + point = new Point( + rc.Left + (int) (rc.Width * horizontalRatio), + rc.Top + (int) (rc.Height * verticalRatio) + ); + } + + return ToImageCoordinates(point); + } + + /// + /// Get the rectangle bounding the part of this Surface currently visible in the editor (in surface coordinate space). + /// + public Rectangle GetVisibleRectangle() + { + var bounds = Bounds; + var clientArea = Parent.ClientRectangle; + return new Rectangle( + Math.Max(0, -bounds.Left), + Math.Max(0, -bounds.Top), + clientArea.Width, + clientArea.Height + ); + } + + /// + /// Get the rectangle bounding all selected elements (in surface coordinates space), + /// or empty rectangle if nothing is selcted. + /// + public Rectangle GetSelectionRectangle() + => ToSurfaceCoordinates(selectedElements.DrawingBounds); + + /// + /// Duplicate all the selecteded elements + /// + public void DuplicateSelectedElements() + { + LOG.DebugFormat("Duplicating {0} selected elements", selectedElements.Count); + IDrawableContainerList dcs = selectedElements.Clone(); + dcs.Parent = this; + dcs.MoveBy(10, 10); + AddElements(dcs); + DeselectAllElements(); + SelectElements(dcs); + } + + /// + /// Deselect the specified element + /// + /// IDrawableContainerList + /// false to skip event generation + public void DeselectElement(IDrawableContainer container, bool generateEvents = true) + { + container.Selected = false; + selectedElements.Remove(container); + FieldAggregator.UnbindElement(container); + if (generateEvents && _movingElementChanged != null) + { + var eventArgs = new SurfaceElementEventArgs + { + Elements = selectedElements + }; + _movingElementChanged(this, eventArgs); + } + } + + /// + /// Deselect the specified elements + /// + /// IDrawableContainerList + public void DeselectElements(IDrawableContainerList elements) + { + if (elements.Count == 0) + { + return; + } + + while (elements.Count > 0) + { + var element = elements[0]; + DeselectElement(element, false); + } + + if (_movingElementChanged != null) + { + SurfaceElementEventArgs eventArgs = new SurfaceElementEventArgs + { + Elements = selectedElements + }; + _movingElementChanged(this, eventArgs); + } + + Invalidate(); + } + + /// + /// Deselect all the selected elements + /// + public void DeselectAllElements() + { + DeselectElements(selectedElements); + } + + /// + /// Select the supplied element + /// + /// + /// false to skip invalidation + /// false to skip event generation + public void SelectElement(IDrawableContainer container, bool invalidate = true, bool generateEvents = true) + { + if (!selectedElements.Contains(container)) + { + selectedElements.Add(container); + container.Selected = true; + FieldAggregator.BindElement(container); + if (generateEvents && _movingElementChanged != null) + { + SurfaceElementEventArgs eventArgs = new SurfaceElementEventArgs + { + Elements = selectedElements + }; + _movingElementChanged(this, eventArgs); + } + + if (invalidate) + { + container.Invalidate(); + } + } + } + + /// + /// Select all elements, this is called when Ctrl+A is pressed + /// + public void SelectAllElements() + { + SelectElements(_elements); + } + + /// + /// Select the supplied elements + /// + /// + public void SelectElements(IDrawableContainerList elements) + { + SuspendLayout(); + foreach (var drawableContainer in elements) + { + var element = (DrawableContainer) drawableContainer; + SelectElement(element, false, false); + } + + if (_movingElementChanged != null) + { + SurfaceElementEventArgs eventArgs = new SurfaceElementEventArgs + { + Elements = selectedElements + }; + _movingElementChanged(this, eventArgs); + } + + ResumeLayout(); + Invalidate(); + } + + /// + /// Process key presses on the surface, this is called from the editor (and NOT an override from the Control) + /// + /// Keys + /// false if no keys were processed + public bool ProcessCmdKey(Keys k) + { + if (selectedElements.Count > 0) + { + bool shiftModifier = (ModifierKeys & Keys.Shift) == Keys.Shift; + int px = shiftModifier ? 10 : 1; + Point moveBy = Point.Empty; + + switch (k) + { + case Keys.Left: + case Keys.Left | Keys.Shift: + moveBy = new Point(-px, 0); + break; + case Keys.Up: + case Keys.Up | Keys.Shift: + moveBy = new Point(0, -px); + break; + case Keys.Right: + case Keys.Right | Keys.Shift: + moveBy = new Point(px, 0); + break; + case Keys.Down: + case Keys.Down | Keys.Shift: + moveBy = new Point(0, px); + break; + case Keys.PageUp: + PullElementsUp(); + break; + case Keys.PageDown: + PushElementsDown(); + break; + case Keys.Home: + PullElementsToTop(); + break; + case Keys.End: + PushElementsToBottom(); + break; + case Keys.Enter: + ConfirmSelectedConfirmableElements(true); + break; + case Keys.Escape: + ConfirmSelectedConfirmableElements(false); + break; + /*case Keys.Delete: + RemoveSelectedElements(); + break;*/ + default: + return false; + } + + if (!Point.Empty.Equals(moveBy)) + { + selectedElements.MakeBoundsChangeUndoable(true); + selectedElements.MoveBy(moveBy.X, moveBy.Y); + } + + return true; + } + + return false; + } + + /// + /// Property for accessing the elements on the surface + /// + public IDrawableContainerList Elements => _elements; + + /// + /// pulls selected elements up one level in hierarchy + /// + public void PullElementsUp() + { + _elements.PullElementsUp(selectedElements); + _elements.Invalidate(); + } + + /// + /// pushes selected elements up to top in hierarchy + /// + public void PullElementsToTop() + { + _elements.PullElementsToTop(selectedElements); + _elements.Invalidate(); + } + + /// + /// pushes selected elements down one level in hierarchy + /// + public void PushElementsDown() + { + _elements.PushElementsDown(selectedElements); + _elements.Invalidate(); + } + + /// + /// pushes selected elements down to bottom in hierarchy + /// + public void PushElementsToBottom() + { + _elements.PushElementsToBottom(selectedElements); + _elements.Invalidate(); + } + + /// + /// indicates whether the selected elements could be pulled up in hierarchy + /// + /// true if selected elements could be pulled up, false otherwise + public bool CanPullSelectionUp() + { + return _elements.CanPullUp(selectedElements); + } + + /// + /// indicates whether the selected elements could be pushed down in hierarchy + /// + /// true if selected elements could be pushed down, false otherwise + public bool CanPushSelectionDown() + { + return _elements.CanPushDown(selectedElements); + } + + public void Element_FieldChanged(object sender, FieldChangedEventArgs e) + { + selectedElements.HandleFieldChangedEvent(sender, e); + } + + public bool IsOnSurface(IDrawableContainer container) + { + return _elements.Contains(container); + } + + public Point ToSurfaceCoordinates(Point point) + { + Point[] points = + { + point + }; + _zoomMatrix.TransformPoints(points); + return points[0]; + } + + public Rectangle ToSurfaceCoordinates(Rectangle rc) + { + if (_zoomMatrix.IsIdentity) + { + return rc; + } + else + { + Point[] points = + { + rc.Location, rc.Location + rc.Size + }; + _zoomMatrix.TransformPoints(points); + return new Rectangle( + points[0].X, + points[0].Y, + points[1].X - points[0].X, + points[1].Y - points[0].Y + ); + } + } + + public Point ToImageCoordinates(Point point) + { + Point[] points = + { + point + }; + _inverseZoomMatrix.TransformPoints(points); + return points[0]; + } + + public Rectangle ToImageCoordinates(Rectangle rc) + { + if (_inverseZoomMatrix.IsIdentity) + { + return rc; + } + else + { + Point[] points = + { + rc.Location, rc.Location + rc.Size + }; + _inverseZoomMatrix.TransformPoints(points); + return new Rectangle( + points[0].X, + points[0].Y, + points[1].X - points[0].X, + points[1].Y - points[0].Y + ); + } + } + } } \ No newline at end of file diff --git a/src/Greenshot/Drawing/TextContainer.cs b/src/Greenshot.Editor/Drawing/TextContainer.cs similarity index 96% rename from src/Greenshot/Drawing/TextContainer.cs rename to src/Greenshot.Editor/Drawing/TextContainer.cs index ccaae042c..4d670b4e0 100644 --- a/src/Greenshot/Drawing/TextContainer.cs +++ b/src/Greenshot.Editor/Drawing/TextContainer.cs @@ -1,691 +1,691 @@ -/* - * Greenshot - a free and open source screenshot tool - * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom - * - * For more information see: https://getgreenshot.org/ - * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 1 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -using Greenshot.Drawing.Fields; -using Greenshot.Helpers; -using Greenshot.Memento; -using System; -using System.ComponentModel; -using System.Diagnostics; -using System.Drawing; -using System.Drawing.Drawing2D; -using System.Drawing.Text; -using System.Runtime.Serialization; -using System.Windows.Forms; -using Greenshot.Base.Core; -using Greenshot.Base.Interfaces.Drawing; - -namespace Greenshot.Drawing -{ - /// - /// Represents a textbox (extends RectangleContainer for border/background support - /// - [Serializable] - public class TextContainer : RectangleContainer, ITextContainer - { - // If makeUndoable is true the next text-change will make the change undoable. - // This is set to true AFTER the first change is made, as there is already a "add element" on the undo stack - // Although the name is wrong, we can't change it due to file serialization - // ReSharper disable once InconsistentNaming - private bool makeUndoable; - [NonSerialized] private Font _font; - public Font Font => _font; - - [NonSerialized] private TextBox _textBox; - - /// - /// The StringFormat object is not serializable!! - /// - [NonSerialized] private StringFormat _stringFormat = new StringFormat(); - - public StringFormat StringFormat => _stringFormat; - - // Although the name is wrong, we can't change it due to file serialization - // ReSharper disable once InconsistentNaming - private string text; - - // there is a binding on the following property! - public string Text - { - get => text; - set => ChangeText(value, true); - } - - internal void ChangeText(string newText, bool allowUndoable) - { - if ((text != null || newText == null) && string.Equals(text, newText)) return; - - if (makeUndoable && allowUndoable) - { - makeUndoable = false; - _parent.MakeUndoable(new TextChangeMemento(this), false); - } - - text = newText; - OnPropertyChanged("Text"); - } - - public TextContainer(Surface parent) : base(parent) - { - Init(); - } - - protected override void InitializeFields() - { - AddField(GetType(), FieldType.LINE_THICKNESS, 2); - AddField(GetType(), FieldType.LINE_COLOR, Color.Red); - AddField(GetType(), FieldType.SHADOW, true); - AddField(GetType(), FieldType.FONT_ITALIC, false); - AddField(GetType(), FieldType.FONT_BOLD, false); - AddField(GetType(), FieldType.FILL_COLOR, Color.Transparent); - AddField(GetType(), FieldType.FONT_FAMILY, FontFamily.GenericSansSerif.Name); - AddField(GetType(), FieldType.FONT_SIZE, 11f); - AddField(GetType(), FieldType.TEXT_HORIZONTAL_ALIGNMENT, StringAlignment.Center); - AddField(GetType(), FieldType.TEXT_VERTICAL_ALIGNMENT, StringAlignment.Center); - } - - /// - /// Do some logic to make sure all field are initiated correctly - /// - /// StreamingContext - protected override void OnDeserialized(StreamingContext streamingContext) - { - base.OnDeserialized(streamingContext); - Init(); - } - - protected override void Dispose(bool disposing) - { - if (disposing) - { - if (_font != null) - { - _font.Dispose(); - _font = null; - } - - if (_stringFormat != null) - { - _stringFormat.Dispose(); - _stringFormat = null; - } - - if (_textBox != null) - { - _textBox.Dispose(); - _textBox = null; - } - } - - base.Dispose(disposing); - } - - private void Init() - { - _stringFormat = new StringFormat - { - Trimming = StringTrimming.EllipsisWord - }; - - CreateTextBox(); - - UpdateFormat(); - UpdateTextBoxFormat(); - - PropertyChanged += TextContainer_PropertyChanged; - FieldChanged += TextContainer_FieldChanged; - } - - protected override void SwitchParent(Surface newParent) - { - if (_parent != null) - { - _parent.SizeChanged -= Parent_SizeChanged; - } - - base.SwitchParent(newParent); - if (_parent != null) - { - _parent.SizeChanged += Parent_SizeChanged; - } - } - - private void Parent_SizeChanged(object sender, EventArgs e) - { - UpdateTextBoxPosition(); - UpdateTextBoxFont(); - } - - public override void ApplyBounds(RectangleF newBounds) - { - base.ApplyBounds(newBounds); - UpdateTextBoxPosition(); - } - - public override void Invalidate() - { - base.Invalidate(); - if (_textBox != null && _textBox.Visible) - { - _textBox.Invalidate(); - } - } - - public void FitToText() - { - Size textSize = TextRenderer.MeasureText(text, _font); - int lineThickness = GetFieldValueAsInt(FieldType.LINE_THICKNESS); - Width = textSize.Width + lineThickness; - Height = textSize.Height + lineThickness; - } - - private void TextContainer_PropertyChanged(object sender, PropertyChangedEventArgs e) - { - if (_textBox == null) - { - return; - } - - if (_textBox.Visible) - { - _textBox.Invalidate(); - } - - UpdateTextBoxPosition(); - UpdateTextBoxFormat(); - if (e.PropertyName.Equals("Selected")) - { - if (!Selected && _textBox.Visible) - { - HideTextBox(); - } - else if (Selected && Status == EditStatus.DRAWING) - { - ShowTextBox(); - } - else if (_parent != null && Selected && Status == EditStatus.IDLE && _textBox.Visible) - { - // Fix (workaround) for BUG-1698 - _parent.KeysLocked = true; - } - } - - if (_textBox.Visible) - { - _textBox.Invalidate(); - } - } - - private void TextContainer_FieldChanged(object sender, FieldChangedEventArgs e) - { - if (_textBox == null) - { - return; - } - - if (_textBox.Visible) - { - _textBox.Invalidate(); - } - - // Only dispose the font, and re-create it, when a font field has changed. - if (e.Field.FieldType.Name.StartsWith("FONT")) - { - if (_font != null) - { - _font.Dispose(); - _font = null; - } - - UpdateFormat(); - } - else - { - UpdateAlignment(); - } - - UpdateTextBoxFormat(); - - if (_textBox.Visible) - { - _textBox.Invalidate(); - } - } - - public override void OnDoubleClick() - { - ShowTextBox(); - } - - private void CreateTextBox() - { - _textBox = new TextBox - { - ImeMode = ImeMode.On, - Multiline = true, - AcceptsTab = true, - AcceptsReturn = true, - BorderStyle = BorderStyle.None, - Visible = false, - Font = new Font(FontFamily.GenericSansSerif, 1) // just need something non-default here - }; - - _textBox.DataBindings.Add("Text", this, "Text", false, DataSourceUpdateMode.OnPropertyChanged); - _textBox.LostFocus += textBox_LostFocus; - _textBox.KeyDown += textBox_KeyDown; - } - - private void ShowTextBox() - { - if (_parent != null) - { - _parent.KeysLocked = true; - _parent.Controls.Add(_textBox); - } - - EnsureTextBoxContrast(); - if (_textBox != null) - { - _textBox.Show(); - _textBox.Focus(); - } - } - - /// - /// Makes textbox background dark if text color is very bright - /// - private void EnsureTextBoxContrast() - { - if (_textBox == null) - { - return; - } - - Color lc = GetFieldValueAsColor(FieldType.LINE_COLOR); - if (lc.R > 203 && lc.G > 203 && lc.B > 203) - { - _textBox.BackColor = Color.FromArgb(51, 51, 51); - } - else - { - _textBox.BackColor = Color.White; - } - } - - private void HideTextBox() - { - _parent?.Focus(); - _textBox?.Hide(); - if (_parent == null) - { - return; - } - - _parent.KeysLocked = false; - _parent.Controls.Remove(_textBox); - } - - /// - /// Make sure the size of the font is scaled - /// - /// - public override void Transform(Matrix matrix) - { - Rectangle rect = GuiRectangle.GetGuiRectangle(Left, Top, Width, Height); - int pixelsBefore = rect.Width * rect.Height; - - // Transform this container - base.Transform(matrix); - rect = GuiRectangle.GetGuiRectangle(Left, Top, Width, Height); - - int pixelsAfter = rect.Width * rect.Height; - float factor = pixelsAfter / (float) pixelsBefore; - - float fontSize = GetFieldValueAsFloat(FieldType.FONT_SIZE); - fontSize *= factor; - SetFieldValue(FieldType.FONT_SIZE, fontSize); - UpdateFormat(); - } - - private Font CreateFont(string fontFamilyName, bool fontBold, bool fontItalic, float fontSize) - { - FontStyle fontStyle = FontStyle.Regular; - - bool hasStyle = false; - using var fontFamily = new FontFamily(fontFamilyName); - bool boldAvailable = fontFamily.IsStyleAvailable(FontStyle.Bold); - if (fontBold && boldAvailable) - { - fontStyle |= FontStyle.Bold; - hasStyle = true; - } - - bool italicAvailable = fontFamily.IsStyleAvailable(FontStyle.Italic); - if (fontItalic && italicAvailable) - { - fontStyle |= FontStyle.Italic; - hasStyle = true; - } - - if (!hasStyle) - { - bool regularAvailable = fontFamily.IsStyleAvailable(FontStyle.Regular); - if (regularAvailable) - { - fontStyle = FontStyle.Regular; - } - else - { - if (boldAvailable) - { - fontStyle = FontStyle.Bold; - } - else if (italicAvailable) - { - fontStyle = FontStyle.Italic; - } - } - } - - return new Font(fontFamily, fontSize, fontStyle, GraphicsUnit.Pixel); - } - - /// - /// Generate the Font-Formal so we can draw correctly - /// - protected void UpdateFormat() - { - if (_textBox == null) - { - return; - } - - string fontFamily = GetFieldValueAsString(FieldType.FONT_FAMILY); - bool fontBold = GetFieldValueAsBool(FieldType.FONT_BOLD); - bool fontItalic = GetFieldValueAsBool(FieldType.FONT_ITALIC); - float fontSize = GetFieldValueAsFloat(FieldType.FONT_SIZE); - try - { - var newFont = CreateFont(fontFamily, fontBold, fontItalic, fontSize); - _font?.Dispose(); - _font = newFont; - } - catch (Exception ex) - { - // Problem, try again with the default - try - { - fontFamily = FontFamily.GenericSansSerif.Name; - SetFieldValue(FieldType.FONT_FAMILY, fontFamily); - var newFont = CreateFont(fontFamily, fontBold, fontItalic, fontSize); - _font?.Dispose(); - _font = newFont; - } - catch (Exception) - { - // When this happens... the PC is broken - ex.Data.Add("fontFamilyName", fontFamily); - ex.Data.Add("fontBold", fontBold); - ex.Data.Add("fontItalic", fontItalic); - ex.Data.Add("fontSize", fontSize); - throw ex; - } - } - - UpdateTextBoxFont(); - - UpdateAlignment(); - } - - private void UpdateAlignment() - { - _stringFormat.Alignment = (StringAlignment) GetFieldValue(FieldType.TEXT_HORIZONTAL_ALIGNMENT); - _stringFormat.LineAlignment = (StringAlignment) GetFieldValue(FieldType.TEXT_VERTICAL_ALIGNMENT); - } - - /// - /// Set TextBox font according to the TextContainer font and the parent zoom factor. - /// - private void UpdateTextBoxFont() - { - if (_textBox == null || _font == null) - { - return; - } - - var textBoxFontScale = _parent?.ZoomFactor ?? Fraction.Identity; - - var newFont = new Font( - _font.FontFamily, - _font.Size * textBoxFontScale, - _font.Style, - GraphicsUnit.Pixel - ); - _textBox.Font.Dispose(); - _textBox.Font = newFont; - } - - /// - /// This will align the textbox exactly to the inner size of the element - /// is a bit of a hack, but for now it seems to work... - /// - private void UpdateTextBoxPosition() - { - if (_textBox == null || Parent == null) - { - return; - } - - int lineThickness = GetFieldValueAsInt(FieldType.LINE_THICKNESS); - - int lineWidth = (int) Math.Floor(lineThickness / 2d); - int correction = (lineThickness + 1) % 2; - if (lineThickness <= 1) - { - lineWidth = 1; - correction = -1; - } - - Rectangle absRectangle = GuiRectangle.GetGuiRectangle(Left, Top, Width, Height); - Rectangle displayRectangle = Parent.ToSurfaceCoordinates(absRectangle); - _textBox.Left = displayRectangle.X + lineWidth; - _textBox.Top = displayRectangle.Y + lineWidth; - if (lineThickness <= 1) - { - lineWidth = 0; - } - - _textBox.Width = displayRectangle.Width - 2 * lineWidth + correction; - _textBox.Height = displayRectangle.Height - 2 * lineWidth + correction; - } - - /// - /// Set TextBox text align and fore color according to field values. - /// - private void UpdateTextBoxFormat() - { - if (_textBox == null) - { - return; - } - - var alignment = (StringAlignment) GetFieldValue(FieldType.TEXT_HORIZONTAL_ALIGNMENT); - switch (alignment) - { - case StringAlignment.Near: - _textBox.TextAlign = HorizontalAlignment.Left; - break; - case StringAlignment.Far: - _textBox.TextAlign = HorizontalAlignment.Right; - break; - case StringAlignment.Center: - _textBox.TextAlign = HorizontalAlignment.Center; - break; - } - - var lineColor = GetFieldValueAsColor(FieldType.LINE_COLOR); - _textBox.ForeColor = lineColor; - } - - private void textBox_KeyDown(object sender, KeyEventArgs e) - { - // ESC and Enter/Return (w/o Shift) hide text editor - if (e.KeyCode == Keys.Escape || ((e.KeyCode == Keys.Return || e.KeyCode == Keys.Enter) && e.Modifiers == Keys.None)) - { - HideTextBox(); - e.SuppressKeyPress = true; - } - - if (e.Control && !e.Alt && e.KeyCode == Keys.A) - { - _textBox.SelectAll(); - } - - // Added for FEATURE-1064 - if (e.KeyCode == Keys.Back && e.Control) - { - e.SuppressKeyPress = true; - int selStart = _textBox.SelectionStart; - while (selStart > 0 && _textBox.Text.Substring(selStart - 1, 1) == " ") - { - selStart--; - } - - int prevSpacePos = -1; - if (selStart != 0) - { - prevSpacePos = _textBox.Text.LastIndexOf(' ', selStart - 1); - } - - _textBox.Select(prevSpacePos + 1, _textBox.SelectionStart - prevSpacePos - 1); - _textBox.SelectedText = string.Empty; - } - } - - private void textBox_LostFocus(object sender, EventArgs e) - { - // next change will be made undoable - makeUndoable = true; - HideTextBox(); - } - - public override void Draw(Graphics graphics, RenderMode rm) - { - base.Draw(graphics, rm); - - graphics.SmoothingMode = SmoothingMode.HighQuality; - graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; - graphics.CompositingQuality = CompositingQuality.HighQuality; - graphics.PixelOffsetMode = PixelOffsetMode.None; - graphics.TextRenderingHint = TextRenderingHint.SystemDefault; - - Rectangle rect = GuiRectangle.GetGuiRectangle(Left, Top, Width, Height); - if (Selected && rm == RenderMode.EDIT) - { - DrawSelectionBorder(graphics, rect); - } - - if (string.IsNullOrEmpty(text)) - { - return; - } - - // we only draw the shadow if there is no background - bool shadow = GetFieldValueAsBool(FieldType.SHADOW); - Color fillColor = GetFieldValueAsColor(FieldType.FILL_COLOR); - int lineThickness = GetFieldValueAsInt(FieldType.LINE_THICKNESS); - Color lineColor = GetFieldValueAsColor(FieldType.LINE_COLOR); - bool drawShadow = shadow && (fillColor == Color.Transparent || fillColor == Color.Empty); - - DrawText(graphics, rect, lineThickness, lineColor, drawShadow, _stringFormat, text, _font); - } - - /// - /// This method can be used from other containers - /// - /// - /// - /// - /// - /// - /// - /// - /// - public static void DrawText(Graphics graphics, Rectangle drawingRectange, int lineThickness, Color fontColor, bool drawShadow, StringFormat stringFormat, string text, - Font font) - { -#if DEBUG - Debug.Assert(font != null); -#else - if (font == null) - { - return; - } -#endif - int textOffset = lineThickness > 0 ? (int) Math.Ceiling(lineThickness / 2d) : 0; - // draw shadow before anything else - if (drawShadow) - { - int basealpha = 100; - int alpha = basealpha; - int steps = 5; - int currentStep = 1; - while (currentStep <= steps) - { - int offset = currentStep; - Rectangle shadowRect = GuiRectangle.GetGuiRectangle(drawingRectange.Left + offset, drawingRectange.Top + offset, drawingRectange.Width, drawingRectange.Height); - if (lineThickness > 0) - { - shadowRect.Inflate(-textOffset, -textOffset); - } - using Brush fontBrush = new SolidBrush(Color.FromArgb(alpha, 100, 100, 100)); - graphics.DrawString(text, font, fontBrush, shadowRect, stringFormat); - - currentStep++; - alpha -= basealpha / steps; - } - } - - if (lineThickness > 0) - { - drawingRectange.Inflate(-textOffset, -textOffset); - } - using (Brush fontBrush = new SolidBrush(fontColor)) - { - if (stringFormat != null) - { - graphics.DrawString(text, font, fontBrush, drawingRectange, stringFormat); - } - else - { - graphics.DrawString(text, font, fontBrush, drawingRectange); - } - } - } - - public override bool ClickableAt(int x, int y) - { - Rectangle r = GuiRectangle.GetGuiRectangle(Left, Top, Width, Height); - r.Inflate(5, 5); - return r.Contains(x, y); - } - } +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using System; +using System.ComponentModel; +using System.Diagnostics; +using System.Drawing; +using System.Drawing.Drawing2D; +using System.Drawing.Text; +using System.Runtime.Serialization; +using System.Windows.Forms; +using Greenshot.Base.Core; +using Greenshot.Base.Interfaces.Drawing; +using Greenshot.Editor.Drawing.Fields; +using Greenshot.Editor.Helpers; +using Greenshot.Editor.Memento; + +namespace Greenshot.Editor.Drawing +{ + /// + /// Represents a textbox (extends RectangleContainer for border/background support + /// + [Serializable] + public class TextContainer : RectangleContainer, ITextContainer + { + // If makeUndoable is true the next text-change will make the change undoable. + // This is set to true AFTER the first change is made, as there is already a "add element" on the undo stack + // Although the name is wrong, we can't change it due to file serialization + // ReSharper disable once InconsistentNaming + private bool makeUndoable; + [NonSerialized] private Font _font; + public Font Font => _font; + + [NonSerialized] private TextBox _textBox; + + /// + /// The StringFormat object is not serializable!! + /// + [NonSerialized] private StringFormat _stringFormat = new StringFormat(); + + public StringFormat StringFormat => _stringFormat; + + // Although the name is wrong, we can't change it due to file serialization + // ReSharper disable once InconsistentNaming + private string text; + + // there is a binding on the following property! + public string Text + { + get => text; + set => ChangeText(value, true); + } + + internal void ChangeText(string newText, bool allowUndoable) + { + if ((text != null || newText == null) && string.Equals(text, newText)) return; + + if (makeUndoable && allowUndoable) + { + makeUndoable = false; + _parent.MakeUndoable(new TextChangeMemento(this), false); + } + + text = newText; + OnPropertyChanged("Text"); + } + + public TextContainer(Surface parent) : base(parent) + { + Init(); + } + + protected override void InitializeFields() + { + AddField(GetType(), FieldType.LINE_THICKNESS, 2); + AddField(GetType(), FieldType.LINE_COLOR, Color.Red); + AddField(GetType(), FieldType.SHADOW, true); + AddField(GetType(), FieldType.FONT_ITALIC, false); + AddField(GetType(), FieldType.FONT_BOLD, false); + AddField(GetType(), FieldType.FILL_COLOR, Color.Transparent); + AddField(GetType(), FieldType.FONT_FAMILY, FontFamily.GenericSansSerif.Name); + AddField(GetType(), FieldType.FONT_SIZE, 11f); + AddField(GetType(), FieldType.TEXT_HORIZONTAL_ALIGNMENT, StringAlignment.Center); + AddField(GetType(), FieldType.TEXT_VERTICAL_ALIGNMENT, StringAlignment.Center); + } + + /// + /// Do some logic to make sure all field are initiated correctly + /// + /// StreamingContext + protected override void OnDeserialized(StreamingContext streamingContext) + { + base.OnDeserialized(streamingContext); + Init(); + } + + protected override void Dispose(bool disposing) + { + if (disposing) + { + if (_font != null) + { + _font.Dispose(); + _font = null; + } + + if (_stringFormat != null) + { + _stringFormat.Dispose(); + _stringFormat = null; + } + + if (_textBox != null) + { + _textBox.Dispose(); + _textBox = null; + } + } + + base.Dispose(disposing); + } + + private void Init() + { + _stringFormat = new StringFormat + { + Trimming = StringTrimming.EllipsisWord + }; + + CreateTextBox(); + + UpdateFormat(); + UpdateTextBoxFormat(); + + PropertyChanged += TextContainer_PropertyChanged; + FieldChanged += TextContainer_FieldChanged; + } + + protected override void SwitchParent(Surface newParent) + { + if (_parent != null) + { + _parent.SizeChanged -= Parent_SizeChanged; + } + + base.SwitchParent(newParent); + if (_parent != null) + { + _parent.SizeChanged += Parent_SizeChanged; + } + } + + private void Parent_SizeChanged(object sender, EventArgs e) + { + UpdateTextBoxPosition(); + UpdateTextBoxFont(); + } + + public override void ApplyBounds(RectangleF newBounds) + { + base.ApplyBounds(newBounds); + UpdateTextBoxPosition(); + } + + public override void Invalidate() + { + base.Invalidate(); + if (_textBox != null && _textBox.Visible) + { + _textBox.Invalidate(); + } + } + + public void FitToText() + { + Size textSize = TextRenderer.MeasureText(text, _font); + int lineThickness = GetFieldValueAsInt(FieldType.LINE_THICKNESS); + Width = textSize.Width + lineThickness; + Height = textSize.Height + lineThickness; + } + + private void TextContainer_PropertyChanged(object sender, PropertyChangedEventArgs e) + { + if (_textBox == null) + { + return; + } + + if (_textBox.Visible) + { + _textBox.Invalidate(); + } + + UpdateTextBoxPosition(); + UpdateTextBoxFormat(); + if (e.PropertyName.Equals("Selected")) + { + if (!Selected && _textBox.Visible) + { + HideTextBox(); + } + else if (Selected && Status == EditStatus.DRAWING) + { + ShowTextBox(); + } + else if (_parent != null && Selected && Status == EditStatus.IDLE && _textBox.Visible) + { + // Fix (workaround) for BUG-1698 + _parent.KeysLocked = true; + } + } + + if (_textBox.Visible) + { + _textBox.Invalidate(); + } + } + + private void TextContainer_FieldChanged(object sender, FieldChangedEventArgs e) + { + if (_textBox == null) + { + return; + } + + if (_textBox.Visible) + { + _textBox.Invalidate(); + } + + // Only dispose the font, and re-create it, when a font field has changed. + if (e.Field.FieldType.Name.StartsWith("FONT")) + { + if (_font != null) + { + _font.Dispose(); + _font = null; + } + + UpdateFormat(); + } + else + { + UpdateAlignment(); + } + + UpdateTextBoxFormat(); + + if (_textBox.Visible) + { + _textBox.Invalidate(); + } + } + + public override void OnDoubleClick() + { + ShowTextBox(); + } + + private void CreateTextBox() + { + _textBox = new TextBox + { + ImeMode = ImeMode.On, + Multiline = true, + AcceptsTab = true, + AcceptsReturn = true, + BorderStyle = BorderStyle.None, + Visible = false, + Font = new Font(FontFamily.GenericSansSerif, 1) // just need something non-default here + }; + + _textBox.DataBindings.Add("Text", this, "Text", false, DataSourceUpdateMode.OnPropertyChanged); + _textBox.LostFocus += textBox_LostFocus; + _textBox.KeyDown += textBox_KeyDown; + } + + private void ShowTextBox() + { + if (_parent != null) + { + _parent.KeysLocked = true; + _parent.Controls.Add(_textBox); + } + + EnsureTextBoxContrast(); + if (_textBox != null) + { + _textBox.Show(); + _textBox.Focus(); + } + } + + /// + /// Makes textbox background dark if text color is very bright + /// + private void EnsureTextBoxContrast() + { + if (_textBox == null) + { + return; + } + + Color lc = GetFieldValueAsColor(FieldType.LINE_COLOR); + if (lc.R > 203 && lc.G > 203 && lc.B > 203) + { + _textBox.BackColor = Color.FromArgb(51, 51, 51); + } + else + { + _textBox.BackColor = Color.White; + } + } + + private void HideTextBox() + { + _parent?.Focus(); + _textBox?.Hide(); + if (_parent == null) + { + return; + } + + _parent.KeysLocked = false; + _parent.Controls.Remove(_textBox); + } + + /// + /// Make sure the size of the font is scaled + /// + /// + public override void Transform(Matrix matrix) + { + Rectangle rect = GuiRectangle.GetGuiRectangle(Left, Top, Width, Height); + int pixelsBefore = rect.Width * rect.Height; + + // Transform this container + base.Transform(matrix); + rect = GuiRectangle.GetGuiRectangle(Left, Top, Width, Height); + + int pixelsAfter = rect.Width * rect.Height; + float factor = pixelsAfter / (float) pixelsBefore; + + float fontSize = GetFieldValueAsFloat(FieldType.FONT_SIZE); + fontSize *= factor; + SetFieldValue(FieldType.FONT_SIZE, fontSize); + UpdateFormat(); + } + + private Font CreateFont(string fontFamilyName, bool fontBold, bool fontItalic, float fontSize) + { + FontStyle fontStyle = FontStyle.Regular; + + bool hasStyle = false; + using var fontFamily = new FontFamily(fontFamilyName); + bool boldAvailable = fontFamily.IsStyleAvailable(FontStyle.Bold); + if (fontBold && boldAvailable) + { + fontStyle |= FontStyle.Bold; + hasStyle = true; + } + + bool italicAvailable = fontFamily.IsStyleAvailable(FontStyle.Italic); + if (fontItalic && italicAvailable) + { + fontStyle |= FontStyle.Italic; + hasStyle = true; + } + + if (!hasStyle) + { + bool regularAvailable = fontFamily.IsStyleAvailable(FontStyle.Regular); + if (regularAvailable) + { + fontStyle = FontStyle.Regular; + } + else + { + if (boldAvailable) + { + fontStyle = FontStyle.Bold; + } + else if (italicAvailable) + { + fontStyle = FontStyle.Italic; + } + } + } + + return new Font(fontFamily, fontSize, fontStyle, GraphicsUnit.Pixel); + } + + /// + /// Generate the Font-Formal so we can draw correctly + /// + protected void UpdateFormat() + { + if (_textBox == null) + { + return; + } + + string fontFamily = GetFieldValueAsString(FieldType.FONT_FAMILY); + bool fontBold = GetFieldValueAsBool(FieldType.FONT_BOLD); + bool fontItalic = GetFieldValueAsBool(FieldType.FONT_ITALIC); + float fontSize = GetFieldValueAsFloat(FieldType.FONT_SIZE); + try + { + var newFont = CreateFont(fontFamily, fontBold, fontItalic, fontSize); + _font?.Dispose(); + _font = newFont; + } + catch (Exception ex) + { + // Problem, try again with the default + try + { + fontFamily = FontFamily.GenericSansSerif.Name; + SetFieldValue(FieldType.FONT_FAMILY, fontFamily); + var newFont = CreateFont(fontFamily, fontBold, fontItalic, fontSize); + _font?.Dispose(); + _font = newFont; + } + catch (Exception) + { + // When this happens... the PC is broken + ex.Data.Add("fontFamilyName", fontFamily); + ex.Data.Add("fontBold", fontBold); + ex.Data.Add("fontItalic", fontItalic); + ex.Data.Add("fontSize", fontSize); + throw ex; + } + } + + UpdateTextBoxFont(); + + UpdateAlignment(); + } + + private void UpdateAlignment() + { + _stringFormat.Alignment = (StringAlignment) GetFieldValue(FieldType.TEXT_HORIZONTAL_ALIGNMENT); + _stringFormat.LineAlignment = (StringAlignment) GetFieldValue(FieldType.TEXT_VERTICAL_ALIGNMENT); + } + + /// + /// Set TextBox font according to the TextContainer font and the parent zoom factor. + /// + private void UpdateTextBoxFont() + { + if (_textBox == null || _font == null) + { + return; + } + + var textBoxFontScale = _parent?.ZoomFactor ?? Fraction.Identity; + + var newFont = new Font( + _font.FontFamily, + _font.Size * textBoxFontScale, + _font.Style, + GraphicsUnit.Pixel + ); + _textBox.Font.Dispose(); + _textBox.Font = newFont; + } + + /// + /// This will align the textbox exactly to the inner size of the element + /// is a bit of a hack, but for now it seems to work... + /// + private void UpdateTextBoxPosition() + { + if (_textBox == null || Parent == null) + { + return; + } + + int lineThickness = GetFieldValueAsInt(FieldType.LINE_THICKNESS); + + int lineWidth = (int) Math.Floor(lineThickness / 2d); + int correction = (lineThickness + 1) % 2; + if (lineThickness <= 1) + { + lineWidth = 1; + correction = -1; + } + + Rectangle absRectangle = GuiRectangle.GetGuiRectangle(Left, Top, Width, Height); + Rectangle displayRectangle = Parent.ToSurfaceCoordinates(absRectangle); + _textBox.Left = displayRectangle.X + lineWidth; + _textBox.Top = displayRectangle.Y + lineWidth; + if (lineThickness <= 1) + { + lineWidth = 0; + } + + _textBox.Width = displayRectangle.Width - 2 * lineWidth + correction; + _textBox.Height = displayRectangle.Height - 2 * lineWidth + correction; + } + + /// + /// Set TextBox text align and fore color according to field values. + /// + private void UpdateTextBoxFormat() + { + if (_textBox == null) + { + return; + } + + var alignment = (StringAlignment) GetFieldValue(FieldType.TEXT_HORIZONTAL_ALIGNMENT); + switch (alignment) + { + case StringAlignment.Near: + _textBox.TextAlign = HorizontalAlignment.Left; + break; + case StringAlignment.Far: + _textBox.TextAlign = HorizontalAlignment.Right; + break; + case StringAlignment.Center: + _textBox.TextAlign = HorizontalAlignment.Center; + break; + } + + var lineColor = GetFieldValueAsColor(FieldType.LINE_COLOR); + _textBox.ForeColor = lineColor; + } + + private void textBox_KeyDown(object sender, KeyEventArgs e) + { + // ESC and Enter/Return (w/o Shift) hide text editor + if (e.KeyCode == Keys.Escape || ((e.KeyCode == Keys.Return || e.KeyCode == Keys.Enter) && e.Modifiers == Keys.None)) + { + HideTextBox(); + e.SuppressKeyPress = true; + } + + if (e.Control && !e.Alt && e.KeyCode == Keys.A) + { + _textBox.SelectAll(); + } + + // Added for FEATURE-1064 + if (e.KeyCode == Keys.Back && e.Control) + { + e.SuppressKeyPress = true; + int selStart = _textBox.SelectionStart; + while (selStart > 0 && _textBox.Text.Substring(selStart - 1, 1) == " ") + { + selStart--; + } + + int prevSpacePos = -1; + if (selStart != 0) + { + prevSpacePos = _textBox.Text.LastIndexOf(' ', selStart - 1); + } + + _textBox.Select(prevSpacePos + 1, _textBox.SelectionStart - prevSpacePos - 1); + _textBox.SelectedText = string.Empty; + } + } + + private void textBox_LostFocus(object sender, EventArgs e) + { + // next change will be made undoable + makeUndoable = true; + HideTextBox(); + } + + public override void Draw(Graphics graphics, RenderMode rm) + { + base.Draw(graphics, rm); + + graphics.SmoothingMode = SmoothingMode.HighQuality; + graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; + graphics.CompositingQuality = CompositingQuality.HighQuality; + graphics.PixelOffsetMode = PixelOffsetMode.None; + graphics.TextRenderingHint = TextRenderingHint.SystemDefault; + + Rectangle rect = GuiRectangle.GetGuiRectangle(Left, Top, Width, Height); + if (Selected && rm == RenderMode.EDIT) + { + DrawSelectionBorder(graphics, rect); + } + + if (string.IsNullOrEmpty(text)) + { + return; + } + + // we only draw the shadow if there is no background + bool shadow = GetFieldValueAsBool(FieldType.SHADOW); + Color fillColor = GetFieldValueAsColor(FieldType.FILL_COLOR); + int lineThickness = GetFieldValueAsInt(FieldType.LINE_THICKNESS); + Color lineColor = GetFieldValueAsColor(FieldType.LINE_COLOR); + bool drawShadow = shadow && (fillColor == Color.Transparent || fillColor == Color.Empty); + + DrawText(graphics, rect, lineThickness, lineColor, drawShadow, _stringFormat, text, _font); + } + + /// + /// This method can be used from other containers + /// + /// + /// + /// + /// + /// + /// + /// + /// + public static void DrawText(Graphics graphics, Rectangle drawingRectange, int lineThickness, Color fontColor, bool drawShadow, StringFormat stringFormat, string text, + Font font) + { +#if DEBUG + Debug.Assert(font != null); +#else + if (font == null) + { + return; + } +#endif + int textOffset = lineThickness > 0 ? (int) Math.Ceiling(lineThickness / 2d) : 0; + // draw shadow before anything else + if (drawShadow) + { + int basealpha = 100; + int alpha = basealpha; + int steps = 5; + int currentStep = 1; + while (currentStep <= steps) + { + int offset = currentStep; + Rectangle shadowRect = GuiRectangle.GetGuiRectangle(drawingRectange.Left + offset, drawingRectange.Top + offset, drawingRectange.Width, drawingRectange.Height); + if (lineThickness > 0) + { + shadowRect.Inflate(-textOffset, -textOffset); + } + using Brush fontBrush = new SolidBrush(Color.FromArgb(alpha, 100, 100, 100)); + graphics.DrawString(text, font, fontBrush, shadowRect, stringFormat); + + currentStep++; + alpha -= basealpha / steps; + } + } + + if (lineThickness > 0) + { + drawingRectange.Inflate(-textOffset, -textOffset); + } + using (Brush fontBrush = new SolidBrush(fontColor)) + { + if (stringFormat != null) + { + graphics.DrawString(text, font, fontBrush, drawingRectange, stringFormat); + } + else + { + graphics.DrawString(text, font, fontBrush, drawingRectange); + } + } + } + + public override bool ClickableAt(int x, int y) + { + Rectangle r = GuiRectangle.GetGuiRectangle(Left, Top, Width, Height); + r.Inflate(5, 5); + return r.Contains(x, y); + } + } } \ No newline at end of file diff --git a/src/Greenshot/Forms/ColorDialog.Designer.cs b/src/Greenshot.Editor/Forms/ColorDialog.Designer.cs similarity index 95% rename from src/Greenshot/Forms/ColorDialog.Designer.cs rename to src/Greenshot.Editor/Forms/ColorDialog.Designer.cs index c2a35728c..0f9426a67 100644 --- a/src/Greenshot/Forms/ColorDialog.Designer.cs +++ b/src/Greenshot.Editor/Forms/ColorDialog.Designer.cs @@ -1,287 +1,288 @@ -/* - * Greenshot - a free and open source screenshot tool - * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom - * - * For more information see: https://getgreenshot.org/ - * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 1 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -using Greenshot.Base.Controls; - -namespace Greenshot.Forms { - public partial class ColorDialog { - /// - /// Designer variable used to keep track of non-visual components. - /// - private System.ComponentModel.IContainer components = null; - - /// - /// Disposes resources used by the form. - /// - /// true if managed resources should be disposed; otherwise, false. - protected override void Dispose(bool disposing) - { - if (disposing) { - if (components != null) { - components.Dispose(); - } - } - base.Dispose(disposing); - } - - /// - /// This method is required for Windows Forms designer support. - /// Do not change the method contents inside the source code editor. The Forms designer might - /// not be able to load this method if it was changed manually. - /// - private void InitializeComponent() - { - System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(ColorDialog)); - this.btnTransparent = new GreenshotButton(); - this.colorPanel = new System.Windows.Forms.Panel(); - this.labelHtmlColor = new GreenshotLabel(); - this.textBoxHtmlColor = new System.Windows.Forms.TextBox(); - this.labelRed = new GreenshotLabel(); - this.labelGreen = new GreenshotLabel(); - this.labelBlue = new GreenshotLabel(); - this.textBoxRed = new System.Windows.Forms.TextBox(); - this.textBoxGreen = new System.Windows.Forms.TextBox(); - this.textBoxBlue = new System.Windows.Forms.TextBox(); - this.labelRecentColors = new GreenshotLabel(); - this.textBoxAlpha = new System.Windows.Forms.TextBox(); - this.labelAlpha = new GreenshotLabel(); - this.btnApply = new GreenshotButton(); - this.pipette = new Greenshot.Controls.Pipette(); - this.SuspendLayout(); - // - // btnTransparent - // - this.btnTransparent.BackColor = System.Drawing.Color.Transparent; - this.btnTransparent.LanguageKey = "colorpicker_transparent"; - this.btnTransparent.Location = new System.Drawing.Point(210, 4); - this.btnTransparent.Name = "btnTransparent"; - this.btnTransparent.Size = new System.Drawing.Size(78, 23); - this.btnTransparent.TabIndex = 0; - this.btnTransparent.TabStop = false; - this.btnTransparent.Text = "Transparent"; - this.btnTransparent.UseVisualStyleBackColor = false; - this.btnTransparent.Click += new System.EventHandler(this.BtnTransparentClick); - // - // colorPanel - // - this.colorPanel.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; - this.colorPanel.Location = new System.Drawing.Point(213, 30); - this.colorPanel.Name = "colorPanel"; - this.colorPanel.Size = new System.Drawing.Size(33, 23); - this.colorPanel.TabIndex = 1; - // - // labelHtmlColor - // - this.labelHtmlColor.Font = new System.Drawing.Font("Tahoma", 11F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.World); - this.labelHtmlColor.LanguageKey = "colorpicker_htmlcolor"; - this.labelHtmlColor.Location = new System.Drawing.Point(210, 57); - this.labelHtmlColor.Name = "labelHtmlColor"; - this.labelHtmlColor.Size = new System.Drawing.Size(78, 17); - this.labelHtmlColor.TabIndex = 2; - this.labelHtmlColor.Text = "HTML color"; - // - // textBoxHtmlColor - // - this.textBoxHtmlColor.Location = new System.Drawing.Point(210, 71); - this.textBoxHtmlColor.Name = "textBoxHtmlColor"; - this.textBoxHtmlColor.Size = new System.Drawing.Size(78, 20); - this.textBoxHtmlColor.TabIndex = 1; - this.textBoxHtmlColor.Click += new System.EventHandler(this.TextBoxGotFocus); - this.textBoxHtmlColor.TextChanged += new System.EventHandler(this.TextBoxHexadecimalTextChanged); - this.textBoxHtmlColor.GotFocus += new System.EventHandler(this.TextBoxGotFocus); - this.textBoxHtmlColor.KeyDown += new System.Windows.Forms.KeyEventHandler(this.TextBoxKeyDown); - // - // labelRed - // - this.labelRed.Font = new System.Drawing.Font("Tahoma", 11F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.World); - this.labelRed.LanguageKey = "colorpicker_red"; - this.labelRed.Location = new System.Drawing.Point(210, 98); - this.labelRed.Name = "labelRed"; - this.labelRed.Size = new System.Drawing.Size(78, 18); - this.labelRed.TabIndex = 4; - this.labelRed.Text = "Red"; - // - // labelGreen - // - this.labelGreen.Font = new System.Drawing.Font("Tahoma", 11F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.World); - this.labelGreen.LanguageKey = "colorpicker_green"; - this.labelGreen.Location = new System.Drawing.Point(210, 122); - this.labelGreen.Name = "labelGreen"; - this.labelGreen.Size = new System.Drawing.Size(78, 18); - this.labelGreen.TabIndex = 5; - this.labelGreen.Text = "Green"; - // - // labelBlue - // - this.labelBlue.Font = new System.Drawing.Font("Tahoma", 11F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.World); - this.labelBlue.LanguageKey = "colorpicker_blue"; - this.labelBlue.Location = new System.Drawing.Point(210, 146); - this.labelBlue.Name = "labelBlue"; - this.labelBlue.Size = new System.Drawing.Size(78, 18); - this.labelBlue.TabIndex = 6; - this.labelBlue.Text = "Blue"; - // - // textBoxRed - // - this.textBoxRed.Location = new System.Drawing.Point(258, 95); - this.textBoxRed.Name = "textBoxRed"; - this.textBoxRed.Size = new System.Drawing.Size(30, 20); - this.textBoxRed.TabIndex = 2; - this.textBoxRed.TextAlign = System.Windows.Forms.HorizontalAlignment.Right; - this.textBoxRed.Click += new System.EventHandler(this.TextBoxGotFocus); - this.textBoxRed.TextChanged += new System.EventHandler(this.TextBoxRgbTextChanged); - this.textBoxRed.GotFocus += new System.EventHandler(this.TextBoxGotFocus); - this.textBoxRed.KeyDown += new System.Windows.Forms.KeyEventHandler(this.TextBoxKeyDown); - // - // textBoxGreen - // - this.textBoxGreen.Location = new System.Drawing.Point(258, 119); - this.textBoxGreen.Name = "textBoxGreen"; - this.textBoxGreen.Size = new System.Drawing.Size(30, 20); - this.textBoxGreen.TabIndex = 3; - this.textBoxGreen.TextAlign = System.Windows.Forms.HorizontalAlignment.Right; - this.textBoxGreen.Click += new System.EventHandler(this.TextBoxGotFocus); - this.textBoxGreen.TextChanged += new System.EventHandler(this.TextBoxRgbTextChanged); - this.textBoxGreen.GotFocus += new System.EventHandler(this.TextBoxGotFocus); - this.textBoxGreen.KeyDown += new System.Windows.Forms.KeyEventHandler(this.TextBoxKeyDown); - // - // textBoxBlue - // - this.textBoxBlue.Location = new System.Drawing.Point(258, 143); - this.textBoxBlue.Name = "textBoxBlue"; - this.textBoxBlue.Size = new System.Drawing.Size(30, 20); - this.textBoxBlue.TabIndex = 4; - this.textBoxBlue.TextAlign = System.Windows.Forms.HorizontalAlignment.Right; - this.textBoxBlue.Click += new System.EventHandler(this.TextBoxGotFocus); - this.textBoxBlue.TextChanged += new System.EventHandler(this.TextBoxRgbTextChanged); - this.textBoxBlue.GotFocus += new System.EventHandler(this.TextBoxGotFocus); - this.textBoxBlue.KeyDown += new System.Windows.Forms.KeyEventHandler(this.TextBoxKeyDown); - // - // labelRecentColors - // - this.labelRecentColors.LanguageKey = "colorpicker_recentcolors"; - this.labelRecentColors.Location = new System.Drawing.Point(3, 175); - this.labelRecentColors.Name = "labelRecentColors"; - this.labelRecentColors.Size = new System.Drawing.Size(148, 13); - this.labelRecentColors.TabIndex = 10; - this.labelRecentColors.Text = "Recently used colors"; - // - // textBoxAlpha - // - this.textBoxAlpha.Location = new System.Drawing.Point(258, 167); - this.textBoxAlpha.Name = "textBoxAlpha"; - this.textBoxAlpha.Size = new System.Drawing.Size(30, 20); - this.textBoxAlpha.TabIndex = 5; - this.textBoxAlpha.TextAlign = System.Windows.Forms.HorizontalAlignment.Right; - this.textBoxAlpha.Click += new System.EventHandler(this.TextBoxGotFocus); - this.textBoxAlpha.TextChanged += new System.EventHandler(this.TextBoxRgbTextChanged); - this.textBoxAlpha.GotFocus += new System.EventHandler(this.TextBoxGotFocus); - this.textBoxAlpha.KeyDown += new System.Windows.Forms.KeyEventHandler(this.TextBoxKeyDown); - // - // labelAlpha - // - this.labelAlpha.Font = new System.Drawing.Font("Tahoma", 11F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.World); - this.labelAlpha.LanguageKey = "colorpicker_alpha"; - this.labelAlpha.Location = new System.Drawing.Point(210, 170); - this.labelAlpha.Name = "labelAlpha"; - this.labelAlpha.Size = new System.Drawing.Size(78, 18); - this.labelAlpha.TabIndex = 11; - this.labelAlpha.Text = "Alpha"; - // - // btnApply - // - this.btnApply.BackColor = System.Drawing.Color.Transparent; - this.btnApply.LanguageKey = "colorpicker_apply"; - this.btnApply.Location = new System.Drawing.Point(210, 191); - this.btnApply.Name = "btnApply"; - this.btnApply.Size = new System.Drawing.Size(78, 23); - this.btnApply.TabIndex = 12; - this.btnApply.TabStop = false; - this.btnApply.Text = "Apply"; - this.btnApply.UseVisualStyleBackColor = false; - this.btnApply.Click += new System.EventHandler(this.BtnApplyClick); - // - // pipette - // - this.pipette.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; - this.pipette.Cursor = System.Windows.Forms.Cursors.Arrow; - this.pipette.Image = ((System.Drawing.Image)(resources.GetObject("pipette.Image"))); - this.pipette.Location = new System.Drawing.Point(255, 30); - this.pipette.Name = "pipette"; - this.pipette.Size = new System.Drawing.Size(33, 23); - this.pipette.TabIndex = 13; - this.pipette.PipetteUsed += new System.EventHandler(this.PipetteUsed); - // - // ColorDialog - // - this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F); - this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi; - this.ClientSize = new System.Drawing.Size(292, 218); - this.Controls.Add(this.pipette); - this.Controls.Add(this.btnApply); - this.Controls.Add(this.textBoxAlpha); - this.Controls.Add(this.labelAlpha); - this.Controls.Add(this.labelRecentColors); - this.Controls.Add(this.textBoxBlue); - this.Controls.Add(this.textBoxGreen); - this.Controls.Add(this.textBoxRed); - this.Controls.Add(this.labelBlue); - this.Controls.Add(this.labelGreen); - this.Controls.Add(this.labelRed); - this.Controls.Add(this.textBoxHtmlColor); - this.Controls.Add(this.labelHtmlColor); - this.Controls.Add(this.colorPanel); - this.Controls.Add(this.btnTransparent); - this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; - this.LanguageKey = "colorpicker_title"; - this.MaximizeBox = false; - this.MinimizeBox = false; - this.Name = "ColorDialog"; - this.ShowIcon = false; - this.ShowInTaskbar = false; - this.SizeGripStyle = System.Windows.Forms.SizeGripStyle.Hide; - this.Text = "Color picker"; - this.ResumeLayout(false); - this.PerformLayout(); - - } - private GreenshotLabel labelRed; - private GreenshotLabel labelGreen; - private GreenshotLabel labelBlue; - private System.Windows.Forms.TextBox textBoxHtmlColor; - private GreenshotLabel labelRecentColors; - private GreenshotLabel labelAlpha; - private GreenshotLabel labelHtmlColor; - private GreenshotButton btnApply; - private System.Windows.Forms.TextBox textBoxAlpha; - private System.Windows.Forms.TextBox textBoxRed; - private System.Windows.Forms.TextBox textBoxGreen; - private System.Windows.Forms.TextBox textBoxBlue; - private System.Windows.Forms.Panel colorPanel; - private GreenshotButton btnTransparent; - private Greenshot.Controls.Pipette pipette; - - - - - - } -} +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: https://getgreenshot.org/ + * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using Greenshot.Base.Controls; +using Greenshot.Editor.Controls; + +namespace Greenshot.Editor.Forms { + public partial class ColorDialog { + /// + /// Designer variable used to keep track of non-visual components. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Disposes resources used by the form. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing) { + if (components != null) { + components.Dispose(); + } + } + base.Dispose(disposing); + } + + /// + /// This method is required for Windows Forms designer support. + /// Do not change the method contents inside the source code editor. The Forms designer might + /// not be able to load this method if it was changed manually. + /// + private void InitializeComponent() + { + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(ColorDialog)); + this.btnTransparent = new GreenshotButton(); + this.colorPanel = new System.Windows.Forms.Panel(); + this.labelHtmlColor = new GreenshotLabel(); + this.textBoxHtmlColor = new System.Windows.Forms.TextBox(); + this.labelRed = new GreenshotLabel(); + this.labelGreen = new GreenshotLabel(); + this.labelBlue = new GreenshotLabel(); + this.textBoxRed = new System.Windows.Forms.TextBox(); + this.textBoxGreen = new System.Windows.Forms.TextBox(); + this.textBoxBlue = new System.Windows.Forms.TextBox(); + this.labelRecentColors = new GreenshotLabel(); + this.textBoxAlpha = new System.Windows.Forms.TextBox(); + this.labelAlpha = new GreenshotLabel(); + this.btnApply = new GreenshotButton(); + this.pipette = new Pipette(); + this.SuspendLayout(); + // + // btnTransparent + // + this.btnTransparent.BackColor = System.Drawing.Color.Transparent; + this.btnTransparent.LanguageKey = "colorpicker_transparent"; + this.btnTransparent.Location = new System.Drawing.Point(210, 4); + this.btnTransparent.Name = "btnTransparent"; + this.btnTransparent.Size = new System.Drawing.Size(78, 23); + this.btnTransparent.TabIndex = 0; + this.btnTransparent.TabStop = false; + this.btnTransparent.Text = "Transparent"; + this.btnTransparent.UseVisualStyleBackColor = false; + this.btnTransparent.Click += new System.EventHandler(this.BtnTransparentClick); + // + // colorPanel + // + this.colorPanel.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; + this.colorPanel.Location = new System.Drawing.Point(213, 30); + this.colorPanel.Name = "colorPanel"; + this.colorPanel.Size = new System.Drawing.Size(33, 23); + this.colorPanel.TabIndex = 1; + // + // labelHtmlColor + // + this.labelHtmlColor.Font = new System.Drawing.Font("Tahoma", 11F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.World); + this.labelHtmlColor.LanguageKey = "colorpicker_htmlcolor"; + this.labelHtmlColor.Location = new System.Drawing.Point(210, 57); + this.labelHtmlColor.Name = "labelHtmlColor"; + this.labelHtmlColor.Size = new System.Drawing.Size(78, 17); + this.labelHtmlColor.TabIndex = 2; + this.labelHtmlColor.Text = "HTML color"; + // + // textBoxHtmlColor + // + this.textBoxHtmlColor.Location = new System.Drawing.Point(210, 71); + this.textBoxHtmlColor.Name = "textBoxHtmlColor"; + this.textBoxHtmlColor.Size = new System.Drawing.Size(78, 20); + this.textBoxHtmlColor.TabIndex = 1; + this.textBoxHtmlColor.Click += new System.EventHandler(this.TextBoxGotFocus); + this.textBoxHtmlColor.TextChanged += new System.EventHandler(this.TextBoxHexadecimalTextChanged); + this.textBoxHtmlColor.GotFocus += new System.EventHandler(this.TextBoxGotFocus); + this.textBoxHtmlColor.KeyDown += new System.Windows.Forms.KeyEventHandler(this.TextBoxKeyDown); + // + // labelRed + // + this.labelRed.Font = new System.Drawing.Font("Tahoma", 11F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.World); + this.labelRed.LanguageKey = "colorpicker_red"; + this.labelRed.Location = new System.Drawing.Point(210, 98); + this.labelRed.Name = "labelRed"; + this.labelRed.Size = new System.Drawing.Size(78, 18); + this.labelRed.TabIndex = 4; + this.labelRed.Text = "Red"; + // + // labelGreen + // + this.labelGreen.Font = new System.Drawing.Font("Tahoma", 11F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.World); + this.labelGreen.LanguageKey = "colorpicker_green"; + this.labelGreen.Location = new System.Drawing.Point(210, 122); + this.labelGreen.Name = "labelGreen"; + this.labelGreen.Size = new System.Drawing.Size(78, 18); + this.labelGreen.TabIndex = 5; + this.labelGreen.Text = "Green"; + // + // labelBlue + // + this.labelBlue.Font = new System.Drawing.Font("Tahoma", 11F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.World); + this.labelBlue.LanguageKey = "colorpicker_blue"; + this.labelBlue.Location = new System.Drawing.Point(210, 146); + this.labelBlue.Name = "labelBlue"; + this.labelBlue.Size = new System.Drawing.Size(78, 18); + this.labelBlue.TabIndex = 6; + this.labelBlue.Text = "Blue"; + // + // textBoxRed + // + this.textBoxRed.Location = new System.Drawing.Point(258, 95); + this.textBoxRed.Name = "textBoxRed"; + this.textBoxRed.Size = new System.Drawing.Size(30, 20); + this.textBoxRed.TabIndex = 2; + this.textBoxRed.TextAlign = System.Windows.Forms.HorizontalAlignment.Right; + this.textBoxRed.Click += new System.EventHandler(this.TextBoxGotFocus); + this.textBoxRed.TextChanged += new System.EventHandler(this.TextBoxRgbTextChanged); + this.textBoxRed.GotFocus += new System.EventHandler(this.TextBoxGotFocus); + this.textBoxRed.KeyDown += new System.Windows.Forms.KeyEventHandler(this.TextBoxKeyDown); + // + // textBoxGreen + // + this.textBoxGreen.Location = new System.Drawing.Point(258, 119); + this.textBoxGreen.Name = "textBoxGreen"; + this.textBoxGreen.Size = new System.Drawing.Size(30, 20); + this.textBoxGreen.TabIndex = 3; + this.textBoxGreen.TextAlign = System.Windows.Forms.HorizontalAlignment.Right; + this.textBoxGreen.Click += new System.EventHandler(this.TextBoxGotFocus); + this.textBoxGreen.TextChanged += new System.EventHandler(this.TextBoxRgbTextChanged); + this.textBoxGreen.GotFocus += new System.EventHandler(this.TextBoxGotFocus); + this.textBoxGreen.KeyDown += new System.Windows.Forms.KeyEventHandler(this.TextBoxKeyDown); + // + // textBoxBlue + // + this.textBoxBlue.Location = new System.Drawing.Point(258, 143); + this.textBoxBlue.Name = "textBoxBlue"; + this.textBoxBlue.Size = new System.Drawing.Size(30, 20); + this.textBoxBlue.TabIndex = 4; + this.textBoxBlue.TextAlign = System.Windows.Forms.HorizontalAlignment.Right; + this.textBoxBlue.Click += new System.EventHandler(this.TextBoxGotFocus); + this.textBoxBlue.TextChanged += new System.EventHandler(this.TextBoxRgbTextChanged); + this.textBoxBlue.GotFocus += new System.EventHandler(this.TextBoxGotFocus); + this.textBoxBlue.KeyDown += new System.Windows.Forms.KeyEventHandler(this.TextBoxKeyDown); + // + // labelRecentColors + // + this.labelRecentColors.LanguageKey = "colorpicker_recentcolors"; + this.labelRecentColors.Location = new System.Drawing.Point(3, 175); + this.labelRecentColors.Name = "labelRecentColors"; + this.labelRecentColors.Size = new System.Drawing.Size(148, 13); + this.labelRecentColors.TabIndex = 10; + this.labelRecentColors.Text = "Recently used colors"; + // + // textBoxAlpha + // + this.textBoxAlpha.Location = new System.Drawing.Point(258, 167); + this.textBoxAlpha.Name = "textBoxAlpha"; + this.textBoxAlpha.Size = new System.Drawing.Size(30, 20); + this.textBoxAlpha.TabIndex = 5; + this.textBoxAlpha.TextAlign = System.Windows.Forms.HorizontalAlignment.Right; + this.textBoxAlpha.Click += new System.EventHandler(this.TextBoxGotFocus); + this.textBoxAlpha.TextChanged += new System.EventHandler(this.TextBoxRgbTextChanged); + this.textBoxAlpha.GotFocus += new System.EventHandler(this.TextBoxGotFocus); + this.textBoxAlpha.KeyDown += new System.Windows.Forms.KeyEventHandler(this.TextBoxKeyDown); + // + // labelAlpha + // + this.labelAlpha.Font = new System.Drawing.Font("Tahoma", 11F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.World); + this.labelAlpha.LanguageKey = "colorpicker_alpha"; + this.labelAlpha.Location = new System.Drawing.Point(210, 170); + this.labelAlpha.Name = "labelAlpha"; + this.labelAlpha.Size = new System.Drawing.Size(78, 18); + this.labelAlpha.TabIndex = 11; + this.labelAlpha.Text = "Alpha"; + // + // btnApply + // + this.btnApply.BackColor = System.Drawing.Color.Transparent; + this.btnApply.LanguageKey = "colorpicker_apply"; + this.btnApply.Location = new System.Drawing.Point(210, 191); + this.btnApply.Name = "btnApply"; + this.btnApply.Size = new System.Drawing.Size(78, 23); + this.btnApply.TabIndex = 12; + this.btnApply.TabStop = false; + this.btnApply.Text = "Apply"; + this.btnApply.UseVisualStyleBackColor = false; + this.btnApply.Click += new System.EventHandler(this.BtnApplyClick); + // + // pipette + // + this.pipette.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; + this.pipette.Cursor = System.Windows.Forms.Cursors.Arrow; + this.pipette.Image = ((System.Drawing.Image)(resources.GetObject("pipette.Image"))); + this.pipette.Location = new System.Drawing.Point(255, 30); + this.pipette.Name = "pipette"; + this.pipette.Size = new System.Drawing.Size(33, 23); + this.pipette.TabIndex = 13; + this.pipette.PipetteUsed += new System.EventHandler(this.PipetteUsed); + // + // ColorDialog + // + this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi; + this.ClientSize = new System.Drawing.Size(292, 218); + this.Controls.Add(this.pipette); + this.Controls.Add(this.btnApply); + this.Controls.Add(this.textBoxAlpha); + this.Controls.Add(this.labelAlpha); + this.Controls.Add(this.labelRecentColors); + this.Controls.Add(this.textBoxBlue); + this.Controls.Add(this.textBoxGreen); + this.Controls.Add(this.textBoxRed); + this.Controls.Add(this.labelBlue); + this.Controls.Add(this.labelGreen); + this.Controls.Add(this.labelRed); + this.Controls.Add(this.textBoxHtmlColor); + this.Controls.Add(this.labelHtmlColor); + this.Controls.Add(this.colorPanel); + this.Controls.Add(this.btnTransparent); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; + this.LanguageKey = "colorpicker_title"; + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "ColorDialog"; + this.ShowIcon = false; + this.ShowInTaskbar = false; + this.SizeGripStyle = System.Windows.Forms.SizeGripStyle.Hide; + this.Text = "Color picker"; + this.ResumeLayout(false); + this.PerformLayout(); + + } + private GreenshotLabel labelRed; + private GreenshotLabel labelGreen; + private GreenshotLabel labelBlue; + private System.Windows.Forms.TextBox textBoxHtmlColor; + private GreenshotLabel labelRecentColors; + private GreenshotLabel labelAlpha; + private GreenshotLabel labelHtmlColor; + private GreenshotButton btnApply; + private System.Windows.Forms.TextBox textBoxAlpha; + private System.Windows.Forms.TextBox textBoxRed; + private System.Windows.Forms.TextBox textBoxGreen; + private System.Windows.Forms.TextBox textBoxBlue; + private System.Windows.Forms.Panel colorPanel; + private GreenshotButton btnTransparent; + private Pipette pipette; + + + + + + } +} diff --git a/src/Greenshot/Forms/ColorDialog.cs b/src/Greenshot.Editor/Forms/ColorDialog.cs similarity index 95% rename from src/Greenshot/Forms/ColorDialog.cs rename to src/Greenshot.Editor/Forms/ColorDialog.cs index 14f5e5022..50211782f 100644 --- a/src/Greenshot/Forms/ColorDialog.cs +++ b/src/Greenshot.Editor/Forms/ColorDialog.cs @@ -1,280 +1,280 @@ -/* - * Greenshot - a free and open source screenshot tool - * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom - * - * For more information see: https://getgreenshot.org/ - * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 1 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -using System; -using System.Collections.Generic; -using System.Drawing; -using System.Globalization; -using System.Threading; -using System.Windows.Forms; -using Greenshot.Base.IniFile; -using Greenshot.Configuration; -using Greenshot.Controls; - -namespace Greenshot.Forms -{ - /// - /// Description of ColorDialog. - /// - public partial class ColorDialog : BaseForm - { - private static readonly EditorConfiguration EditorConfig = IniConfig.GetIniSection(); - private static ColorDialog _instance; - - public ColorDialog() - { - SuspendLayout(); - InitializeComponent(); - SuspendLayout(); - CreateColorPalette(5, 5, 15, 15); - CreateLastUsedColorButtonRow(5, 190, 15, 15); - ResumeLayout(); - UpdateRecentColorsButtonRow(); - _instance = this; - } - - public static ColorDialog GetInstance() => _instance; - - private readonly List public class PickerDestination : AbstractDestination { - public const string DESIGNATION = "Picker"; - - public override string Designation => DESIGNATION; + public override string Designation => nameof(WellKnownDestinations.Picker); public override string Description => Language.GetString(LangKey.settings_destination_picker); diff --git a/src/Greenshot/Destinations/PrinterDestination.cs b/src/Greenshot/Destinations/PrinterDestination.cs index 6a85a1e83..7265c4576 100644 --- a/src/Greenshot/Destinations/PrinterDestination.cs +++ b/src/Greenshot/Destinations/PrinterDestination.cs @@ -24,6 +24,7 @@ using System.Collections.Generic; using System.Drawing; using System.Drawing.Printing; using System.Windows.Forms; +using Greenshot.Base; using Greenshot.Base.Core; using Greenshot.Base.Interfaces; using Greenshot.Configuration; @@ -36,7 +37,6 @@ namespace Greenshot.Destinations /// public class PrinterDestination : AbstractDestination { - public const string DESIGNATION = "Printer"; private readonly string _printerName; public PrinterDestination() @@ -48,7 +48,7 @@ namespace Greenshot.Destinations _printerName = printerName; } - public override string Designation => DESIGNATION; + public override string Designation => nameof(WellKnownDestinations.Printer); public override string Description { diff --git a/src/Greenshot/Forms/AboutForm.cs b/src/Greenshot/Forms/AboutForm.cs index b0e9781d0..a801cc63e 100644 --- a/src/Greenshot/Forms/AboutForm.cs +++ b/src/Greenshot/Forms/AboutForm.cs @@ -31,7 +31,6 @@ using System.Windows.Forms; using Greenshot.Base.Core; using Greenshot.Base.IniFile; using Greenshot.Configuration; -using Greenshot.Helpers; using log4net; namespace Greenshot.Forms @@ -44,10 +43,10 @@ namespace Greenshot.Forms private static readonly ILog Log = LogManager.GetLogger(typeof(AboutForm)); private Bitmap _bitmap; private readonly ColorAnimator _backgroundAnimation; - private readonly List _pixels = new List(); - private readonly List _colorFlow = new List(); - private readonly List _pixelColors = new List(); - private readonly Random _rand = new Random(); + private readonly List _pixels = new(); + private readonly List _colorFlow = new(); + private readonly List _pixelColors = new(); + private readonly Random _rand = new(); private readonly Color _backColor = Color.FromArgb(61, 61, 61); private readonly Color _pixelColor = Color.FromArgb(138, 255, 0); @@ -117,7 +116,7 @@ namespace Greenshot.Forms // 18 19 20 21 22 23 // The order in which we draw the dots & flow the colors. - private readonly List _flowOrder = new List{ 4, 3, 2, 1, 0, 5, 6, 7, 8, 9, 10, 14, 15, 18, 19, 20, 21, 22, 23, 16, 17, 13, 12, 11 }; + private readonly List _flowOrder = new() { 4, 3, 2, 1, 0, 5, 6, 7, 8, 9, 10, 14, 15, 18, 19, 20, 21, 22, 23, 16, 17, 13, 12, 11 }; /// /// Cleanup all the allocated resources @@ -177,8 +176,7 @@ namespace Greenshot.Forms if (IsTerminalServerSession) { // No animation - pixelAnimation = new RectangleAnimator(new Rectangle(gSpot.X, gSpot.Y, W - 2, W - 2), new Rectangle(gSpot.X, gSpot.Y, W - 2, W - 2), 1, EasingType.Cubic, - EasingMode.EaseIn); + pixelAnimation = new RectangleAnimator(new Rectangle(gSpot.X, gSpot.Y, W - 2, W - 2), new Rectangle(gSpot.X, gSpot.Y, W - 2, W - 2), 1, EasingType.Cubic, EasingMode.EaseIn); } else { diff --git a/src/Greenshot/Forms/CaptureForm.cs b/src/Greenshot/Forms/CaptureForm.cs index f06419949..6176c732e 100644 --- a/src/Greenshot/Forms/CaptureForm.cs +++ b/src/Greenshot/Forms/CaptureForm.cs @@ -19,8 +19,6 @@ * along with this program. If not, see . */ -using Greenshot.Drawing; -using Greenshot.Helpers; using log4net; using System; using System.Collections.Generic; @@ -38,6 +36,7 @@ using Greenshot.Base.IniFile; using Greenshot.Base.Interfaces; using Greenshot.Base.Interfaces.Ocr; using Greenshot.Base.UnmanagedHelpers; +using Greenshot.Editor.Helpers; namespace Greenshot.Forms { @@ -204,8 +203,7 @@ namespace Greenshot.Forms if (isOn) { // Initialize the zoom with a invalid position - _zoomAnimator = new RectangleAnimator(Rectangle.Empty, new Rectangle(int.MaxValue, int.MaxValue, 0, 0), FramesForMillis(1000), EasingType.Quintic, - EasingMode.EaseOut); + _zoomAnimator = new RectangleAnimator(Rectangle.Empty, new Rectangle(int.MaxValue, int.MaxValue, 0, 0), FramesForMillis(1000), EasingType.Quintic, EasingMode.EaseOut); VerifyZoomAnimation(_cursorPos, false); } else @@ -270,16 +268,6 @@ namespace Greenshot.Forms _capture.CursorVisible = !_capture.CursorVisible; Invalidate(); break; - //// TODO: Enable when the screen capture code works reliable - //case Keys.V: - // // Video - // if (capture.CaptureDetails.CaptureMode != CaptureMode.Video) { - // capture.CaptureDetails.CaptureMode = CaptureMode.Video; - // } else { - // capture.CaptureDetails.CaptureMode = captureMode; - // } - // Invalidate(); - // break; case Keys.Z: if (_captureMode == CaptureMode.Region) { @@ -309,8 +297,7 @@ namespace Greenshot.Forms // "Fade out" Zoom InitializeZoomer(false); // "Fade in" window - _windowAnimator = new RectangleAnimator(new Rectangle(_cursorPos, Size.Empty), _captureRect, FramesForMillis(700), EasingType.Quintic, - EasingMode.EaseOut); + _windowAnimator = new RectangleAnimator(new Rectangle(_cursorPos, Size.Empty), _captureRect, FramesForMillis(700), EasingType.Quintic, EasingMode.EaseOut); _captureRect = Rectangle.Empty; Invalidate(); break; @@ -670,7 +657,7 @@ namespace Greenshot.Forms } } - // always animate the Window area through to the last frame, so we see the fade-in/out untill the end + // always animate the Window area through to the last frame, so we see the fade-in/out until the end // Using a safety "offset" to make sure the text is invalidated too const int safetySize = 30; // Check if the animation needs to be drawn @@ -717,34 +704,21 @@ namespace Greenshot.Forms foreach (var line in ocrInfo.Lines) { var lineBounds = line.CalculatedBounds; - if (!lineBounds.IsEmpty) + if (lineBounds.IsEmpty) { - if (_mouseDown) - { - // Highlight the text which is selected - if (lineBounds.IntersectsWith(_captureRect)) - { - foreach (var word in line.Words) - { - if (word.Bounds.IntersectsWith(_captureRect)) - { - if (invalidateRectangle.IsEmpty) - { - invalidateRectangle = word.Bounds; - } - else - { - invalidateRectangle = Rectangle.Union(invalidateRectangle, word.Bounds); - } - } - } - } - } - else if (lineBounds.Contains(_mouseMovePos)) + continue; + } + if (_mouseDown) + { + // Highlight the text which is selected + if (lineBounds.IntersectsWith(_captureRect)) { foreach (var word in line.Words) { - if (!word.Bounds.Contains(_mouseMovePos)) continue; + if (!word.Bounds.IntersectsWith(_captureRect)) + { + continue; + } if (invalidateRectangle.IsEmpty) { invalidateRectangle = word.Bounds; @@ -753,11 +727,26 @@ namespace Greenshot.Forms { invalidateRectangle = Rectangle.Union(invalidateRectangle, word.Bounds); } - - break; } } } + else if (lineBounds.Contains(_mouseMovePos)) + { + foreach (var word in line.Words) + { + if (!word.Bounds.Contains(_mouseMovePos)) continue; + if (invalidateRectangle.IsEmpty) + { + invalidateRectangle = word.Bounds; + } + else + { + invalidateRectangle = Rectangle.Union(invalidateRectangle, word.Bounds); + } + + break; + } + } } if (!invalidateRectangle.IsEmpty) @@ -796,38 +785,39 @@ namespace Greenshot.Forms Rectangle targetRectangle = _zoomAnimator.Final; targetRectangle.Offset(pos); - if (!screenBounds.Contains(targetRectangle) || (!allowZoomOverCaptureRect && _captureRect.IntersectsWith(targetRectangle))) + if (screenBounds.Contains(targetRectangle) && (allowZoomOverCaptureRect || !_captureRect.IntersectsWith(targetRectangle))) { - Point destinationLocation = Point.Empty; - Rectangle tl = new Rectangle(pos.X - (zoomOffset.X + zoomSize.Width), pos.Y - (zoomOffset.Y + zoomSize.Height), zoomSize.Width, zoomSize.Height); - Rectangle tr = new Rectangle(pos.X + zoomOffset.X, pos.Y - (zoomOffset.Y + zoomSize.Height), zoomSize.Width, zoomSize.Height); - Rectangle bl = new Rectangle(pos.X - (zoomOffset.X + zoomSize.Width), pos.Y + zoomOffset.Y, zoomSize.Width, zoomSize.Height); - Rectangle br = new Rectangle(pos.X + zoomOffset.X, pos.Y + zoomOffset.Y, zoomSize.Width, zoomSize.Height); - if (screenBounds.Contains(br) && (allowZoomOverCaptureRect || !_captureRect.IntersectsWith(br))) - { - destinationLocation = new Point(zoomOffset.X, zoomOffset.Y); - } - else if (screenBounds.Contains(bl) && (allowZoomOverCaptureRect || !_captureRect.IntersectsWith(bl))) - { - destinationLocation = new Point(-zoomOffset.X - zoomSize.Width, zoomOffset.Y); - } - else if (screenBounds.Contains(tr) && (allowZoomOverCaptureRect || !_captureRect.IntersectsWith(tr))) - { - destinationLocation = new Point(zoomOffset.X, -zoomOffset.Y - zoomSize.Width); - } - else if (screenBounds.Contains(tl) && (allowZoomOverCaptureRect || !_captureRect.IntersectsWith(tl))) - { - destinationLocation = new Point(-zoomOffset.X - zoomSize.Width, -zoomOffset.Y - zoomSize.Width); - } + return; + } + Point destinationLocation = Point.Empty; + Rectangle tl = new Rectangle(pos.X - (zoomOffset.X + zoomSize.Width), pos.Y - (zoomOffset.Y + zoomSize.Height), zoomSize.Width, zoomSize.Height); + Rectangle tr = new Rectangle(pos.X + zoomOffset.X, pos.Y - (zoomOffset.Y + zoomSize.Height), zoomSize.Width, zoomSize.Height); + Rectangle bl = new Rectangle(pos.X - (zoomOffset.X + zoomSize.Width), pos.Y + zoomOffset.Y, zoomSize.Width, zoomSize.Height); + Rectangle br = new Rectangle(pos.X + zoomOffset.X, pos.Y + zoomOffset.Y, zoomSize.Width, zoomSize.Height); + if (screenBounds.Contains(br) && (allowZoomOverCaptureRect || !_captureRect.IntersectsWith(br))) + { + destinationLocation = new Point(zoomOffset.X, zoomOffset.Y); + } + else if (screenBounds.Contains(bl) && (allowZoomOverCaptureRect || !_captureRect.IntersectsWith(bl))) + { + destinationLocation = new Point(-zoomOffset.X - zoomSize.Width, zoomOffset.Y); + } + else if (screenBounds.Contains(tr) && (allowZoomOverCaptureRect || !_captureRect.IntersectsWith(tr))) + { + destinationLocation = new Point(zoomOffset.X, -zoomOffset.Y - zoomSize.Width); + } + else if (screenBounds.Contains(tl) && (allowZoomOverCaptureRect || !_captureRect.IntersectsWith(tl))) + { + destinationLocation = new Point(-zoomOffset.X - zoomSize.Width, -zoomOffset.Y - zoomSize.Width); + } - if (destinationLocation == Point.Empty && !allowZoomOverCaptureRect) - { - VerifyZoomAnimation(pos, true); - } - else - { - _zoomAnimator.ChangeDestination(new Rectangle(destinationLocation, zoomSize)); - } + if (destinationLocation == Point.Empty && !allowZoomOverCaptureRect) + { + VerifyZoomAnimation(pos, true); + } + else + { + _zoomAnimator.ChangeDestination(new Rectangle(destinationLocation, zoomSize)); } } @@ -877,8 +867,7 @@ namespace Greenshot.Forms } else { - graphics.DrawImage(_capture.Image, destinationRectangle, sourceRectangle.X, sourceRectangle.Y, sourceRectangle.Width, sourceRectangle.Height, - GraphicsUnit.Pixel, attributes); + graphics.DrawImage(_capture.Image, destinationRectangle, sourceRectangle.X, sourceRectangle.Y, sourceRectangle.Width, sourceRectangle.Height, GraphicsUnit.Pixel, attributes); } } @@ -964,33 +953,37 @@ namespace Greenshot.Forms foreach (var line in ocrInfo.Lines) { var lineBounds = line.CalculatedBounds; - if (!lineBounds.IsEmpty) + if (lineBounds.IsEmpty) { - graphics.DrawRectangle(pen, line.CalculatedBounds); - if (_mouseDown) - { - // Highlight the text which is selected - if (lineBounds.IntersectsWith(_captureRect)) - { - foreach (var word in line.Words) - { - if (word.Bounds.IntersectsWith(_captureRect)) - { - graphics.FillRectangle(highlightTextBrush, word.Bounds); - } - } - } - } - else if (lineBounds.Contains(_mouseMovePos)) + continue; + } + graphics.DrawRectangle(pen, line.CalculatedBounds); + if (_mouseDown) + { + // Highlight the text which is selected + if (lineBounds.IntersectsWith(_captureRect)) { foreach (var word in line.Words) { - if (!word.Bounds.Contains(_mouseMovePos)) continue; - graphics.FillRectangle(highlightTextBrush, word.Bounds); - break; + if (word.Bounds.IntersectsWith(_captureRect)) + { + graphics.FillRectangle(highlightTextBrush, word.Bounds); + } } } } + else if (lineBounds.Contains(_mouseMovePos)) + { + foreach (var word in line.Words) + { + if (!word.Bounds.Contains(_mouseMovePos)) + { + continue; + } + graphics.FillRectangle(highlightTextBrush, word.Bounds); + break; + } + } } } diff --git a/src/Greenshot/Forms/MainForm.cs b/src/Greenshot/Forms/MainForm.cs index c95a9e66a..b775149cb 100644 --- a/src/Greenshot/Forms/MainForm.cs +++ b/src/Greenshot/Forms/MainForm.cs @@ -32,26 +32,31 @@ using System.Threading; using System.Threading.Tasks; using System.Windows.Forms; using System.Windows.Forms.Integration; +using Greenshot.Base; using Greenshot.Base.Controls; using Greenshot.Base.Core; +using Greenshot.Base.Core.Enums; +using Greenshot.Base.Help; using Greenshot.Base.IniFile; using Greenshot.Base.Interfaces; using Greenshot.Base.Interfaces.Plugin; using Greenshot.Base.UnmanagedHelpers; using Greenshot.Configuration; using Greenshot.Destinations; -using Greenshot.Drawing; -using Greenshot.Help; +using Greenshot.Editor.Destinations; +using Greenshot.Editor.Drawing; +using Greenshot.Editor.Forms; using Greenshot.Helpers; +using Greenshot.Processors; using log4net; using Timer = System.Timers.Timer; namespace Greenshot.Forms { /// - /// Description of MainForm. + /// This is the MainForm, the shell of Greenshot /// - public partial class MainForm : BaseForm + public partial class MainForm : BaseForm, IGreenshotMainForm, ICaptureHelper { private static ILog LOG; private static ResourceMutex _applicationMutex; @@ -336,17 +341,18 @@ namespace Greenshot.Forms private static void FreeMutex() { // Remove the application mutex - if (_applicationMutex != null) + if (_applicationMutex == null) { - try - { - _applicationMutex.Dispose(); - _applicationMutex = null; - } - catch (Exception ex) - { - LOG.Error("Error releasing Mutex!", ex); - } + return; + } + try + { + _applicationMutex.Dispose(); + _applicationMutex = null; + } + catch (Exception ex) + { + LOG.Error("Error releasing Mutex!", ex); } } @@ -376,6 +382,8 @@ namespace Greenshot.Forms SimpleServiceProvider.Current.AddService
(this); // Also as itself SimpleServiceProvider.Current.AddService(this); + SimpleServiceProvider.Current.AddService(this); + SimpleServiceProvider.Current.AddService(this); _instance = this; @@ -417,9 +425,9 @@ namespace Greenshot.Forms UpdateUi(); // This forces the registration of all destinations inside Greenshot itself. - DestinationHelper.RegisterInternalDestinations(); + RegisterInternalDestinations(); // This forces the registration of all processors inside Greenshot itself. - ProcessorHelper.RegisterInternalProcessors(); + RegisterInternalProcessors(); // Load all the plugins PluginHelper.Instance.LoadPlugins(); @@ -495,6 +503,62 @@ namespace Greenshot.Forms } } + /// + /// Create all the internal destinations + /// + private void RegisterInternalDestinations() + { + var internalDestinations = new List + { + new FileDestination(), + new FileWithDialogDestination(), + new ClipboardDestination(), + new PrinterDestination(), + new EmailDestination(), + new PickerDestination() + }; + + int len = 250; + var stringBuilder = new StringBuilder(len); + using var proc = Process.GetCurrentProcess(); + var err = Kernel32.GetPackageFullName(proc.Handle, ref len, stringBuilder); + if (err != 0) + { + internalDestinations.Add(new EditorDestination()); + } + foreach (var internalDestination in internalDestinations) + { + if (internalDestination.IsActive) + { + SimpleServiceProvider.Current.AddService(internalDestination); + } + else + { + internalDestination.Dispose(); + } + } + } + + private void RegisterInternalProcessors() + { + var internalProcessors = new List + { + new TitleFixProcessor() + }; + + foreach (var internalProcessor in internalProcessors) + { + if (internalProcessor.isActive) + { + SimpleServiceProvider.Current.AddService(internalProcessor); + } + else + { + internalProcessor.Dispose(); + } + } + } + /// /// DataReceivedEventHandler /// @@ -845,6 +909,11 @@ namespace Greenshot.Forms { Hide(); ShowInTaskbar = false; + + + using var loProcess = Process.GetCurrentProcess(); + loProcess.MaxWorkingSet = (IntPtr)750000; + loProcess.MinWorkingSet = (IntPtr)300000; } private void CaptureRegion() @@ -1505,7 +1574,7 @@ namespace Greenshot.Forms IDestination selectedDestination = (IDestination) item.Data; if (item.Checked) { - if (selectedDestination.Designation.Equals(PickerDestination.DESIGNATION)) + if (selectedDestination.Designation.Equals(nameof(WellKnownDestinations.Picker))) { // If the item is the destination picker, remove all others _conf.OutputDestinations.Clear(); @@ -1513,7 +1582,7 @@ namespace Greenshot.Forms else { // If the item is not the destination picker, remove the picker - _conf.OutputDestinations.Remove(PickerDestination.DESIGNATION); + _conf.OutputDestinations.Remove(nameof(WellKnownDestinations.Picker)); } // Checked an item, add if the destination is not yet selected @@ -1534,7 +1603,7 @@ namespace Greenshot.Forms // Check if something was selected, if not make the picker the default if (_conf.OutputDestinations == null || _conf.OutputDestinations.Count == 0) { - _conf.OutputDestinations.Add(PickerDestination.DESIGNATION); + _conf.OutputDestinations.Add(nameof(WellKnownDestinations.Picker)); } IniConfig.Save(); @@ -1810,5 +1879,27 @@ namespace Greenshot.Forms notifyIcon = null; } } + + /// + /// TODO: Delete when the ICaptureHelper can be solve someway else + /// + /// WindowDetails + /// WindowDetails + public WindowDetails SelectCaptureWindow(WindowDetails windowToCapture) + { + return CaptureHelper.SelectCaptureWindow(windowToCapture); + } + + /// + /// TODO: Delete when the ICaptureHelper can be solve someway else + /// + /// WindowDetails + /// ICapture + /// WindowCaptureMode + /// ICapture + public ICapture CaptureWindow(WindowDetails windowToCapture, ICapture capture, WindowCaptureMode coreConfigurationWindowCaptureMode) + { + return CaptureHelper.CaptureWindow(windowToCapture, capture, coreConfigurationWindowCaptureMode); + } } } \ No newline at end of file diff --git a/src/Greenshot/Forms/SettingsForm.Designer.cs b/src/Greenshot/Forms/SettingsForm.Designer.cs index c6adb66e7..92eb61eef 100644 --- a/src/Greenshot/Forms/SettingsForm.Designer.cs +++ b/src/Greenshot/Forms/SettingsForm.Designer.cs @@ -20,6 +20,7 @@ */ using Greenshot.Base.Controls; +using Greenshot.Editor.Controls; namespace Greenshot.Forms { partial class SettingsForm { @@ -103,7 +104,7 @@ namespace Greenshot.Forms { this.groupbox_iecapture = new GreenshotGroupBox(); this.checkbox_ie_capture = new GreenshotCheckBox(); this.groupbox_windowscapture = new GreenshotGroupBox(); - this.colorButton_window_background = new Greenshot.Controls.ColorButton(); + this.colorButton_window_background = new ColorButton(); this.radiobuttonWindowCapture = new GreenshotRadioButton(); this.radiobuttonInteractiveCapture = new GreenshotRadioButton(); this.combobox_window_capture_mode = new System.Windows.Forms.ComboBox(); @@ -1327,7 +1328,7 @@ namespace Greenshot.Forms { private HotkeyControl lastregion_hotkeyControl; private GreenshotLabel label_lastregion_hotkey; private GreenshotGroupBox groupbox_hotkeys; - private Greenshot.Controls.ColorButton colorButton_window_background; + private ColorButton colorButton_window_background; private GreenshotRadioButton radiobuttonWindowCapture; private GreenshotCheckBox checkbox_ie_capture; private GreenshotGroupBox groupbox_capture; diff --git a/src/Greenshot/Forms/SettingsForm.cs b/src/Greenshot/Forms/SettingsForm.cs index 7305ffdad..e236360d1 100644 --- a/src/Greenshot/Forms/SettingsForm.cs +++ b/src/Greenshot/Forms/SettingsForm.cs @@ -28,8 +28,10 @@ using System.IO; using System.Linq; using System.Text.RegularExpressions; using System.Windows.Forms; +using Greenshot.Base; using Greenshot.Base.Controls; using Greenshot.Base.Core; +using Greenshot.Base.Core.Enums; using Greenshot.Base.IniFile; using Greenshot.Base.Interfaces; using Greenshot.Base.Interfaces.Plugin; @@ -428,7 +430,7 @@ namespace Greenshot.Forms imageNr++; } - if (PickerDestination.DESIGNATION.Equals(currentDestination.Designation)) + if (nameof(WellKnownDestinations.Picker).Equals(currentDestination.Designation)) { checkbox_picker.Checked = coreConfiguration.OutputDestinations.Contains(currentDestination.Designation); checkbox_picker.Text = currentDestination.Description; @@ -571,7 +573,7 @@ namespace Greenshot.Forms List destinations = new List(); if (checkbox_picker.Checked) { - destinations.Add(PickerDestination.DESIGNATION); + destinations.Add(nameof(WellKnownDestinations.Picker)); } foreach (int index in listview_destinations.CheckedIndices) @@ -742,7 +744,7 @@ namespace Greenshot.Forms foreach (int index in listview_destinations.CheckedIndices) { ListViewItem item = listview_destinations.Items[index]; - if (item.Tag is IDestination destinationFromTag && destinationFromTag.Designation.Equals(ClipboardDestination.DESIGNATION)) + if (item.Tag is IDestination destinationFromTag && destinationFromTag.Designation.Equals(nameof(WellKnownDestinations.Clipboard))) { clipboardDestinationChecked = true; break; diff --git a/src/Greenshot/Greenshot.csproj b/src/Greenshot/Greenshot.csproj index 39fd2fb5c..da2c79714 100644 --- a/src/Greenshot/Greenshot.csproj +++ b/src/Greenshot/Greenshot.csproj @@ -21,12 +21,6 @@ - - ColorDialog.cs - - - ImageEditorForm.cs - MainForm.cs @@ -38,6 +32,7 @@ + diff --git a/src/Greenshot/GreenshotMain.cs b/src/Greenshot/GreenshotMain.cs index 3269d9fec..2d42d4b11 100644 --- a/src/Greenshot/GreenshotMain.cs +++ b/src/Greenshot/GreenshotMain.cs @@ -64,6 +64,7 @@ namespace Greenshot CultureInfo.DefaultThreadCurrentCulture = CultureInfo.InvariantCulture; CultureInfo.DefaultThreadCurrentUICulture = CultureInfo.InvariantCulture; + MainForm.Start(args); } } diff --git a/src/Greenshot/Helpers/CaptureHelper.cs b/src/Greenshot/Helpers/CaptureHelper.cs index 1e2fd23ee..c55cc2946 100644 --- a/src/Greenshot/Helpers/CaptureHelper.cs +++ b/src/Greenshot/Helpers/CaptureHelper.cs @@ -19,24 +19,25 @@ * along with this program. If not, see . */ -using Greenshot.Configuration; -using Greenshot.Destinations; -using Greenshot.Drawing; -using Greenshot.Forms; using log4net; using System; using System.Collections.Generic; using System.Diagnostics; using System.Drawing; using System.IO; -using System.Runtime.InteropServices; using System.Text; using System.Threading; using System.Windows.Forms; +using Greenshot.Base; using Greenshot.Base.Core; +using Greenshot.Base.Core.Enums; using Greenshot.Base.IniFile; using Greenshot.Base.Interfaces; using Greenshot.Base.UnmanagedHelpers; +using Greenshot.Configuration; +using Greenshot.Editor.Destinations; +using Greenshot.Editor.Drawing; +using Greenshot.Forms; namespace Greenshot.Helpers { @@ -48,10 +49,8 @@ namespace Greenshot.Helpers private static readonly ILog Log = LogManager.GetLogger(typeof(CaptureHelper)); private static readonly CoreConfiguration CoreConfig = IniConfig.GetIniSection(); - - // TODO: when we get the screen capture code working correctly, this needs to be enabled - //private static ScreenCaptureHelper screenCapture = null; - private List _windows = new List(); + + private List _windows = new(); private WindowDetails _selectedCaptureWindow; private Rectangle _captureRect = Rectangle.Empty; private readonly bool _captureMouseCursor; @@ -411,11 +410,11 @@ namespace Greenshot.Helpers _capture.CaptureDetails.Title = "Clipboard"; _capture.CaptureDetails.AddMetaData("source", "Clipboard"); // Force Editor, keep picker - if (_capture.CaptureDetails.HasDestination(PickerDestination.DESIGNATION)) + if (_capture.CaptureDetails.HasDestination(nameof(WellKnownDestinations.Picker))) { _capture.CaptureDetails.ClearDestinations(); _capture.CaptureDetails.AddDestination(DestinationHelper.GetDestination(EditorDestination.DESIGNATION)); - _capture.CaptureDetails.AddDestination(DestinationHelper.GetDestination(PickerDestination.DESIGNATION)); + _capture.CaptureDetails.AddDestination(DestinationHelper.GetDestination(nameof(WellKnownDestinations.Picker))); } else { @@ -480,11 +479,11 @@ namespace Greenshot.Helpers } // Force Editor, keep picker, this is currently the only usefull destination - if (_capture.CaptureDetails.HasDestination(PickerDestination.DESIGNATION)) + if (_capture.CaptureDetails.HasDestination(nameof(WellKnownDestinations.Picker))) { _capture.CaptureDetails.ClearDestinations(); _capture.CaptureDetails.AddDestination(DestinationHelper.GetDestination(EditorDestination.DESIGNATION)); - _capture.CaptureDetails.AddDestination(DestinationHelper.GetDestination(PickerDestination.DESIGNATION)); + _capture.CaptureDetails.AddDestination(DestinationHelper.GetDestination(nameof(WellKnownDestinations.Picker))); } else { @@ -567,7 +566,7 @@ namespace Greenshot.Helpers { _windows = new List(); - // If the App Launcher is visisble, no other windows are active + // If the App Launcher is visible, no other windows are active WindowDetails appLauncherWindow = WindowDetails.GetAppLauncher(); if (appLauncherWindow != null && appLauncherWindow.Visible) { @@ -779,9 +778,9 @@ namespace Greenshot.Helpers ICaptureDetails captureDetails = _capture.CaptureDetails; bool canDisposeSurface = true; - if (captureDetails.HasDestination(PickerDestination.DESIGNATION)) + if (captureDetails.HasDestination(nameof(WellKnownDestinations.Picker))) { - DestinationHelper.ExportCapture(false, PickerDestination.DESIGNATION, surface, captureDetails); + DestinationHelper.ExportCapture(false, nameof(WellKnownDestinations.Picker), surface, captureDetails); captureDetails.CaptureDestinations.Clear(); canDisposeSurface = false; } @@ -799,7 +798,7 @@ namespace Greenshot.Helpers // or use the file that was written foreach (IDestination destination in captureDetails.CaptureDestinations) { - if (PickerDestination.DESIGNATION.Equals(destination.Designation)) + if (nameof(WellKnownDestinations.Picker).Equals(destination.Designation)) { continue; } @@ -899,30 +898,32 @@ namespace Greenshot.Helpers /// /// Check if Process uses PresentationFramework.dll -> meaning it uses WPF /// - /// Proces to check for the presentation framework + /// Process to check for the presentation framework /// true if the process uses WPF private static bool IsWpf(Process process) { - if (process != null) + if (process == null) { - try + return false; + } + try + { + foreach (ProcessModule module in process.Modules) { - foreach (ProcessModule module in process.Modules) + if (!module.ModuleName.StartsWith("PresentationFramework")) { - if (module.ModuleName.StartsWith("PresentationFramework")) - { - Log.InfoFormat("Found that Process {0} uses {1}, assuming it's using WPF", process.ProcessName, module.FileName); - return true; - } + continue; } - } - catch (Exception) - { - // Access denied on the modules - Log.WarnFormat("No access on the modules from process {0}, assuming WPF is used.", process.ProcessName); + Log.InfoFormat("Found that Process {0} uses {1}, assuming it's using WPF", process.ProcessName, module.FileName); return true; } } + catch (Exception) + { + // Access denied on the modules + Log.WarnFormat("No access on the modules from process {0}, assuming WPF is used.", process.ProcessName); + return true; + } return false; } @@ -933,7 +934,7 @@ namespace Greenshot.Helpers /// Window to capture /// The capture to store the details /// What WindowCaptureMode to use - /// + /// ICapture public static ICapture CaptureWindow(WindowDetails windowToCapture, ICapture captureForWindow, WindowCaptureMode windowCaptureMode) { if (captureForWindow == null) @@ -1163,7 +1164,7 @@ namespace Greenshot.Helpers // Workaround for problem with DPI retrieval, the FromHwnd activates the window... WindowDetails previouslyActiveWindow = WindowDetails.GetActiveWindow(); // Workaround for changed DPI settings in Windows 7 - var mainForm = SimpleServiceProvider.Current.GetInstance(); + var mainForm = SimpleServiceProvider.Current.GetInstance(); using (Graphics graphics = Graphics.FromHwnd(mainForm.Handle)) { _capture.CaptureDetails.DpiX = graphics.DpiX; @@ -1180,21 +1181,12 @@ namespace Greenshot.Helpers private void CaptureWithFeedback() { - // The following, to be precise the HideApp, causes the app to close as described in BUG-1620 - // Added check for metro (Modern UI) apps, which might be maximized and cover the screen. - - //foreach(WindowDetails app in WindowDetails.GetAppWindows()) { - // if (app.Maximised) { - // app.HideApp(); - // } - //} - using CaptureForm captureForm = new CaptureForm(_capture, _windows); // Make sure the form is hidden after showing, even if an exception occurs, so all errors will be shown DialogResult result; try { - var mainForm = SimpleServiceProvider.Current.GetInstance(); + var mainForm = SimpleServiceProvider.Current.GetInstance(); result = captureForm.ShowDialog(mainForm); } finally diff --git a/src/Greenshot/Helpers/PrintHelper.cs b/src/Greenshot/Helpers/PrintHelper.cs index e7dd9fe99..9e6a43a7f 100644 --- a/src/Greenshot/Helpers/PrintHelper.cs +++ b/src/Greenshot/Helpers/PrintHelper.cs @@ -24,11 +24,13 @@ using System.Drawing; using System.Drawing.Printing; using System.Windows.Forms; using Greenshot.Base.Core; +using Greenshot.Base.Core.Enums; using Greenshot.Base.Effects; using Greenshot.Base.IniFile; using Greenshot.Base.Interfaces; using Greenshot.Base.Interfaces.Plugin; using Greenshot.Configuration; +using Greenshot.Editor.Helpers; using Greenshot.Forms; using log4net; diff --git a/src/Greenshot/Helpers/ProcessorHelper.cs b/src/Greenshot/Helpers/ProcessorHelper.cs deleted file mode 100644 index daae56bc4..000000000 --- a/src/Greenshot/Helpers/ProcessorHelper.cs +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Greenshot - a free and open source screenshot tool - * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom - * - * For more information see: https://getgreenshot.org/ - * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 1 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -using System; -using Greenshot.Base.Core; -using Greenshot.Base.Interfaces; -using log4net; - -namespace Greenshot.Helpers -{ - /// - /// Description of ProcessorHelper. - /// - public static class ProcessorHelper - { - private static readonly ILog LOG = LogManager.GetLogger(typeof(ProcessorHelper)); - - /// - /// Register the internal processors - /// - public static void RegisterInternalProcessors() - { - foreach (Type processorType in InterfaceUtils.GetSubclassesOf(typeof(IProcessor), true)) - { - // Only take our own - if (!"Greenshot.Processors".Equals(processorType.Namespace)) - { - continue; - } - - try - { - if (!processorType.IsAbstract) - { - IProcessor processor; - try - { - processor = (IProcessor) Activator.CreateInstance(processorType); - } - catch (Exception e) - { - LOG.ErrorFormat("Can't create instance of {0}", processorType); - LOG.Error(e); - continue; - } - - if (processor.isActive) - { - LOG.DebugFormat("Found Processor {0} with designation {1}", processorType.Name, processor.Designation); - SimpleServiceProvider.Current.AddService(processor); - } - else - { - LOG.DebugFormat("Ignoring Processor {0} with designation {1}", processorType.Name, processor.Designation); - } - } - } - catch (Exception ex) - { - LOG.ErrorFormat("Error loading processor {0}, message: ", processorType.FullName, ex.Message); - } - } - } - } -} \ No newline at end of file