From dba0ec79f8d70f684c48c3ed250dacb9570200f8 Mon Sep 17 00:00:00 2001 From: Jaex Date: Sun, 3 Nov 2013 12:53:49 +0200 Subject: [PATCH] Initial commit of ShareX project r748 --- CodeWorks.sln | 26 + CodeWorks/CodeWorks.csproj | 134 + CodeWorks/Licenses/ShareXLicense.txt | 24 + CodeWorks/MainForm.Designer.cs | 302 ++ CodeWorks/MainForm.cs | 280 ++ CodeWorks/MainForm.resx | 120 + CodeWorks/Program.cs | 41 + CodeWorks/Properties/AssemblyInfo.cs | 35 + CodeWorks/Properties/Resources.Designer.cs | 84 + CodeWorks/Properties/Resources.resx | 124 + CodeWorks/RegionArea.cs | 79 + CodeWorks/RegionAreaManager.cs | 85 + CodeWorks/TextInfo.cs | 58 + Docs/CodeMaid.settings | 404 ++ Docs/ShareX_Class.zip | Bin 0 -> 1456 bytes Docs/VersionHistory.txt | 201 + LICENSE => Docs/license.txt | 16 +- Docs/svn global ignore.txt | 1 + .../Configuration/CoreConfiguration.cs | 434 ++ .../Configuration/EditorConfiguration.cs | 141 + .../Configuration/LanguageKeys.cs | 241 ++ .../Controls/AnimatingForm.cs | 141 + .../Controls/BackgroundForm.cs | 116 + .../Controls/BackgroundForm.designer.cs | 99 + .../Controls/BindableToolStripButton.cs | 54 + .../Controls/BindableToolStripComboBox.cs | 57 + .../BindableToolStripDropDownButton.cs | 84 + GreenshotImageEditor/Controls/ColorButton.cs | 101 + .../Controls/FontFamilyComboBox.cs | 68 + .../Controls/FormWithoutActivation.cs | 36 + .../Controls/GreenshotButton.cs | 36 + .../Controls/GreenshotCheckBox.cs | 60 + .../Controls/GreenshotColumnSorter.cs | 138 + .../Controls/GreenshotComboBox.cs | 132 + .../Controls/GreenshotForm.cs | 635 +++ .../Controls/GreenshotGroupBox.cs | 36 + .../Controls/GreenshotLabel.cs | 36 + .../Controls/GreenshotRadioButton.cs | 60 + .../Controls/GreenshotTabPage.cs | 36 + .../Controls/GreenshotTextBox.cs | 50 + .../Controls/GreenshotToolDropDownButton.cs | 36 + .../Controls/GreenshotToolStripButton.cs | 36 + .../Controls/GreenshotToolStripLabel.cs | 36 + .../Controls/GreenshotToolStripMenuItem.cs | 36 + .../Controls/IGreenshotConfigBindable.cs | 44 + .../Controls/IGreenshotLanguageBindable.cs | 38 + GreenshotImageEditor/Controls/MenuStripEx.cs | 72 + .../Controls/NonJumpingPanel.cs | 40 + GreenshotImageEditor/Controls/Pipette.cs | 206 + .../Controls/PleaseWaitForm.cs | 136 + .../Controls/PleaseWaitForm.designer.cs | 102 + .../Controls/QualityDialog.cs | 75 + .../Controls/QualityDialog.designer.cs | 149 + .../Controls/SaveImageFileDialog.cs | 262 ++ .../Controls/ToolStripColorButton.cs | 99 + GreenshotImageEditor/Controls/ToolStripEx.cs | 73 + .../Controls/ToolStripNumericUpDown.cs | 90 + .../Core/BinaryStructHelper.cs | 108 + GreenshotImageEditor/Core/Cache.cs | 247 ++ GreenshotImageEditor/Core/ClipboardHelper.cs | 803 ++++ GreenshotImageEditor/Core/Effects.cs | 376 ++ GreenshotImageEditor/Core/EncryptionHelper.cs | 101 + GreenshotImageEditor/Core/EnumExtensions.cs | 122 + GreenshotImageEditor/Core/FastBitmap.cs | 1171 +++++ GreenshotImageEditor/Core/FilenameHelper.cs | 545 +++ .../Core/GreenshotResources.cs | 54 + .../Core/GreenshotResources.resx | 471 ++ GreenshotImageEditor/Core/ImageHelper.cs | 1525 +++++++ GreenshotImageEditor/Core/ImageOutput.cs | 630 +++ GreenshotImageEditor/Core/LOG.cs | 89 + GreenshotImageEditor/Core/Language.cs | 863 ++++ GreenshotImageEditor/Core/Objects.cs | 100 + GreenshotImageEditor/Core/QuantizerHelper.cs | 770 ++++ GreenshotImageEditor/Core/WindowCapture.cs | 906 ++++ GreenshotImageEditor/Core/WindowsHelper.cs | 2074 +++++++++ .../Drawing/ArrowContainer.cs | 159 + GreenshotImageEditor/Drawing/CropContainer.cs | 86 + .../Drawing/CursorContainer.cs | 117 + .../Drawing/DrawableContainer.cs | 856 ++++ .../Drawing/DrawableContainerList.cs | 621 +++ .../Drawing/EllipseContainer.cs | 136 + .../Drawing/Fields/AbstractFieldHolder.cs | 203 + .../Fields/AbstractFieldHolderWithChildren.cs | 143 + .../Binding/AbstractBindingConverter.cs | 59 + .../Fields/Binding/AlignmentConverter.cs | 101 + .../Fields/Binding/BidirectionalBinding.cs | 180 + .../DecimalDoublePercentageConverter.cs | 53 + .../Fields/Binding/DecimalFloatConverter.cs | 53 + .../Fields/Binding/DecimalIntConverter.cs | 53 + .../Fields/Binding/IBindingConverter.cs | 33 + .../Fields/Binding/IBindingValidator.cs | 34 + .../Fields/Binding/NotNullValidator.cs | 46 + GreenshotImageEditor/Drawing/Fields/Field.cs | 151 + .../Drawing/Fields/FieldAggregator.cs | 218 + .../Drawing/Fields/FieldType.cs | 125 + .../Drawing/Fields/IFieldHolder.cs | 61 + .../Drawing/FilterContainer.cs | 94 + .../Drawing/Filters/AbstractFilter.cs | 93 + .../Drawing/Filters/BlurFilter.cs | 80 + .../Drawing/Filters/BrightnessFilter.cs | 70 + .../Drawing/Filters/GrayscaleFilter.cs | 70 + .../Drawing/Filters/HighlightFilter.cs | 79 + .../Drawing/Filters/IFilter.cs | 39 + .../Drawing/Filters/MagnifierFilter.cs | 69 + .../Drawing/Filters/PixelizationFilter.cs | 103 + .../Drawing/FreehandContainer.cs | 321 ++ GreenshotImageEditor/Drawing/Gripper.cs | 83 + .../Drawing/HighlightContainer.cs | 100 + GreenshotImageEditor/Drawing/IconContainer.cs | 123 + .../Drawing/ImageContainer.cs | 248 ++ GreenshotImageEditor/Drawing/LineContainer.cs | 137 + .../Drawing/ObfuscateContainer.cs | 83 + .../Drawing/RectangleContainer.cs | 135 + .../Drawing/RoundedRectangle.cs | 155 + GreenshotImageEditor/Drawing/Surface.cs | 1886 ++++++++ GreenshotImageEditor/Drawing/TextContainer.cs | 387 ++ GreenshotImageEditor/Forms/BaseForm.cs | 32 + .../Forms/ColorDialog.Designer.cs | 285 ++ GreenshotImageEditor/Forms/ColorDialog.cs | 274 ++ GreenshotImageEditor/Forms/ColorDialog.resx | 135 + .../Forms/ColorPickerToolStripButton.cs | 78 + .../Forms/DropShadowSettingsForm.Designer.cs | 246 ++ .../Forms/DropShadowSettingsForm.cs | 51 + .../Forms/ImageEditorForm.Designer.cs | 1714 ++++++++ GreenshotImageEditor/Forms/ImageEditorForm.cs | 1366 ++++++ .../Forms/ImageEditorForm.resx | 916 ++++ .../Forms/MovableShowColorForm.Designer.cs | 209 + .../Forms/MovableShowColorForm.cs | 120 + .../Forms/ResizeSettingsForm.Designer.cs | 186 + .../Forms/ResizeSettingsForm.cs | 208 + .../Forms/ToolStripMenuSelectList.cs | 286 ++ .../Forms/TornEdgeSettingsForm.Designer.cs | 367 ++ .../Forms/TornEdgeSettingsForm.cs | 57 + GreenshotImageEditor/Greenshot.sln | 43 + .../GreenshotImageEditor.csproj | 348 ++ GreenshotImageEditor/Helpers/Colors.cs | 59 + .../Helpers/GeometryHelper.cs | 68 + GreenshotImageEditor/Helpers/GuiRectangle.cs | 73 + GreenshotImageEditor/Helpers/LogHelper.cs | 38 + GreenshotImageEditor/Helpers/ScaleHelper.cs | 419 ++ .../Helpers/ToolStripItemEndisabler.cs | 112 + GreenshotImageEditor/IniFile/IniAttributes.cs | 75 + GreenshotImageEditor/IniFile/IniConfig.cs | 564 +++ GreenshotImageEditor/IniFile/IniReader.cs | 84 + GreenshotImageEditor/IniFile/IniSection.cs | 234 + GreenshotImageEditor/IniFile/IniValue.cs | 586 +++ GreenshotImageEditor/Interfaces/Capture.cs | 206 + .../Interfaces/Drawing/Container.cs | 177 + .../Interfaces/Forms/ImageEditor.cs | 74 + GreenshotImageEditor/Interfaces/Generic.cs | 214 + .../Interfaces/IDestination.cs | 220 + GreenshotImageEditor/Interfaces/IProcessor.cs | 71 + .../Interfaces/Plugin/PluginInterfaces.cs | 295 ++ GreenshotImageEditor/Interop/Base.cs | 32 + GreenshotImageEditor/Interop/COMWrapper.cs | 979 +++++ .../Interop/ComProgIdAttribute.cs | 92 + .../Interop/IAppVisibility.cs | 47 + GreenshotImageEditor/Interop/IDispatch.cs | 36 + .../Interop/IOleCommandTarget.cs | 38 + GreenshotImageEditor/Interop/IOleWindow.cs | 36 + .../Interop/IServiceProvider.cs | 36 + GreenshotImageEditor/Language.cs | 39 + .../Memento/AddElementMemento.cs | 85 + .../Memento/ChangeFieldHolderMemento.cs | 93 + .../Memento/DeleteElementMemento.cs | 93 + .../DrawableContainerBoundsChangeMemento.cs | 115 + GreenshotImageEditor/Memento/IMemento.cs | 55 + .../Memento/SurfaceBackgroundChangeMemento.cs | 86 + .../Memento/TextChangeMemento.cs | 89 + .../Properties/AssemblyInfo.cs | 51 + GreenshotImageEditor/UnmanagedHelpers/DWM.cs | 194 + .../UnmanagedHelpers/Enumerations.cs | 1250 ++++++ .../UnmanagedHelpers/GDI32.cs | 372 ++ .../UnmanagedHelpers/GDIplus.cs | 342 ++ .../UnmanagedHelpers/Kernel32.cs | 145 + .../UnmanagedHelpers/PsAPI.cs | 42 + .../UnmanagedHelpers/Shell32.cs | 84 + .../UnmanagedHelpers/Structs.cs | 498 +++ .../UnmanagedHelpers/User32.cs | 424 ++ .../UnmanagedHelpers/Win32Errors.cs | 149 + .../UnmanagedHelpers/WinMM.cs | 38 + .../icons/applicationIcon/16.ico | Bin 0 -> 2550 bytes .../icons/applicationIcon/16.png | Bin 0 -> 3185 bytes .../icons/applicationIcon/32.ico | Bin 0 -> 6518 bytes .../icons/applicationIcon/32.png | Bin 0 -> 3705 bytes .../icons/applicationIcon/48.ico | Bin 0 -> 13430 bytes .../icons/applicationIcon/48.png | Bin 0 -> 4588 bytes .../icons/applicationIcon/90.png | Bin 0 -> 8020 bytes .../icons/applicationIcon/icon.ico | Bin 0 -> 15227 bytes GreenshotImageEditor/icons/arrow_redo.png | Bin 0 -> 349 bytes GreenshotImageEditor/icons/arrow_rollback.png | Bin 0 -> 631 bytes GreenshotImageEditor/icons/arrow_undo.png | Bin 0 -> 345 bytes GreenshotImageEditor/icons/cancel.png | Bin 0 -> 587 bytes GreenshotImageEditor/icons/cross.png | Bin 0 -> 655 bytes GreenshotImageEditor/icons/cut.png | Bin 0 -> 648 bytes GreenshotImageEditor/icons/delete.png | Bin 0 -> 715 bytes GreenshotImageEditor/icons/filter_blur.png | Bin 0 -> 461 bytes .../icons/filter_pixelate.png | Bin 0 -> 236 bytes .../icons/folder-open-image.png | Bin 0 -> 764 bytes GreenshotImageEditor/icons/folder_explore.png | Bin 0 -> 679 bytes GreenshotImageEditor/icons/font_color.png | Bin 0 -> 194 bytes GreenshotImageEditor/icons/freehand.png | Bin 0 -> 3370 bytes .../icons/fugue/arrow-resize.png | Bin 0 -> 401 bytes .../icons/fugue/clipboard-paste-image.png | Bin 0 -> 726 bytes .../icons/fugue/clipboard.png | Bin 0 -> 470 bytes .../icons/fugue/color-swatch.png | Bin 0 -> 537 bytes GreenshotImageEditor/icons/fugue/cross.png | Bin 0 -> 476 bytes GreenshotImageEditor/icons/fugue/cursor.png | Bin 0 -> 527 bytes .../icons/fugue/disk-black.png | Bin 0 -> 433 bytes .../icons/fugue/drive-globe.png | Bin 0 -> 786 bytes .../icons/fugue/edit-alignment-center.png | Bin 0 -> 235 bytes .../icons/fugue/edit-alignment-right.png | Bin 0 -> 234 bytes .../icons/fugue/edit-alignment.png | Bin 0 -> 233 bytes .../icons/fugue/edit-blur.png | Bin 0 -> 3436 bytes .../icons/fugue/edit-bold.png | Bin 0 -> 258 bytes .../icons/fugue/edit-italic.png | Bin 0 -> 219 bytes .../icons/fugue/edit-pixelate.png | Bin 0 -> 2941 bytes .../icons/fugue/edit-underline.png | Bin 0 -> 260 bytes .../fugue/edit-vertical-alignment-middle.png | Bin 0 -> 392 bytes .../fugue/edit-vertical-alignment-top.png | Bin 0 -> 342 bytes .../icons/fugue/edit-vertical-alignment.png | Bin 0 -> 340 bytes .../icons/fugue/filter_base.pdn | Bin 0 -> 11655 bytes .../icons/fugue/filter_highlight_area.png | Bin 0 -> 3441 bytes .../fugue/filter_highlight_grayscale.png | Bin 0 -> 3031 bytes .../icons/fugue/filter_highlight_text.png | Bin 0 -> 3054 bytes GreenshotImageEditor/icons/fugue/gear.png | Bin 0 -> 721 bytes .../icons/fugue/highlighter-color.png | Bin 0 -> 458 bytes .../icons/fugue/highlighter-text.png | Bin 0 -> 594 bytes .../icons/fugue/image-blur.png | Bin 0 -> 590 bytes .../icons/fugue/image-pixelate.png | Bin 0 -> 3208 bytes GreenshotImageEditor/icons/fugue/images.png | Bin 0 -> 657 bytes .../icons/fugue/layer-shape-arrow.png | Bin 0 -> 3150 bytes .../icons/fugue/layer-shape-ellipse.png | Bin 0 -> 740 bytes .../icons/fugue/layer-shape-line.png | Bin 0 -> 414 bytes .../icons/fugue/layer-shape-text.png | Bin 0 -> 612 bytes .../icons/fugue/layer-shape.png | Bin 0 -> 498 bytes .../icons/fugue/magnifier.png | Bin 0 -> 664 bytes .../icons/fugue/mail-open-image.png | Bin 0 -> 766 bytes .../icons/fugue/minus-circle.png | Bin 0 -> 623 bytes .../icons/fugue/money-coin.png | Bin 0 -> 689 bytes .../icons/fugue/paint-can-color-bg.png | Bin 0 -> 654 bytes .../icons/fugue/paint-can-color.png | Bin 0 -> 684 bytes .../icons/fugue/pencil-color-bg.png | Bin 0 -> 485 bytes .../icons/fugue/pencil-color.png | Bin 0 -> 561 bytes GreenshotImageEditor/icons/fugue/printer.png | Bin 0 -> 722 bytes GreenshotImageEditor/icons/fugue/question.png | Bin 0 -> 750 bytes .../icons/fugue/ruler-crop.png | Bin 0 -> 690 bytes GreenshotImageEditor/icons/fugue/scissors.png | Bin 0 -> 676 bytes GreenshotImageEditor/icons/fugue/slash.png | Bin 0 -> 752 bytes GreenshotImageEditor/icons/fugue/tick.png | Bin 0 -> 634 bytes GreenshotImageEditor/icons/heart.png | Bin 0 -> 775 bytes GreenshotImageEditor/icons/help.png | Bin 0 -> 786 bytes GreenshotImageEditor/icons/highlighter.png | Bin 0 -> 440 bytes .../icons/layer-rotate-left.png | Bin 0 -> 608 bytes GreenshotImageEditor/icons/layer-rotate.png | Bin 0 -> 614 bytes GreenshotImageEditor/icons/page_copy.png | Bin 0 -> 663 bytes GreenshotImageEditor/icons/palette.png | Bin 0 -> 856 bytes GreenshotImageEditor/icons/picture_save.png | Bin 0 -> 755 bytes GreenshotImageEditor/icons/picture_saveas.png | Bin 0 -> 829 bytes .../icons/picture_to_clipboard.png | Bin 0 -> 672 bytes GreenshotImageEditor/icons/pipette.png | Bin 0 -> 543 bytes GreenshotImageEditor/icons/printer.png | Bin 0 -> 731 bytes .../icons/propertyitemcontainer.gif | Bin 0 -> 75 bytes GreenshotImageEditor/icons/redo.png | Bin 0 -> 791 bytes GreenshotImageEditor/icons/ruler-crop.png | Bin 0 -> 690 bytes GreenshotImageEditor/icons/shadow.png | Bin 0 -> 1289 bytes .../icons/shape_arrow_add.png | Bin 0 -> 468 bytes .../icons/shape_arrowheads.png | Bin 0 -> 298 bytes GreenshotImageEditor/icons/shape_copy.png | Bin 0 -> 482 bytes .../icons/shape_ellipse_add.png | Bin 0 -> 652 bytes .../icons/shape_ellipse_delete.png | Bin 0 -> 657 bytes GreenshotImageEditor/icons/shape_line.png | Bin 0 -> 455 bytes GreenshotImageEditor/icons/shape_paste.png | Bin 0 -> 574 bytes .../icons/shape_square_add.png | Bin 0 -> 539 bytes .../icons/shape_square_bordercolor.png | Bin 0 -> 546 bytes .../icons/shape_square_delete.png | Bin 0 -> 537 bytes .../icons/shape_square_fillcolor.png | Bin 0 -> 551 bytes GreenshotImageEditor/icons/text_bold.png | Bin 0 -> 304 bytes GreenshotImageEditor/icons/text_dropcaps.png | Bin 0 -> 314 bytes GreenshotImageEditor/icons/text_italic.png | Bin 0 -> 223 bytes GreenshotImageEditor/icons/text_underline.png | Bin 0 -> 273 bytes .../icons/textfield_delete.png | Bin 0 -> 335 bytes GreenshotImageEditor/icons/undo.png | Bin 0 -> 803 bytes GreenshotImageEditor/icons/wand-hat.png | Bin 0 -> 686 bytes GreenshotImageEditor/icons/wrench.png | Bin 0 -> 610 bytes GreenshotImageEditor/icons/wrench_orange.png | Bin 0 -> 584 bytes GreenshotImageEditor/icons/zoom.png | Bin 0 -> 692 bytes HelpersLib/AviWriter.cs | 472 ++ HelpersLib/CLI/CLICommand.cs | 39 + HelpersLib/CLI/CLICommandAction.cs | 84 + HelpersLib/CLI/CLIHelper.cs | 51 + HelpersLib/CLI/CLIManager.cs | 92 + HelpersLib/CLI/CLIManagerRegex.cs | 166 + HelpersLib/CLI/CLIParser.cs | 91 + HelpersLib/ClipboardFormat.cs | 43 + HelpersLib/ColorBgra.cs | 121 + HelpersLib/ColorMatrixManager.cs | 203 + HelpersLib/Colors/CMYK.cs | 222 + HelpersLib/Colors/ColorBox.cs | 182 + HelpersLib/Colors/ColorEventHandler.cs | 53 + HelpersLib/Colors/ColorPicker.cs | 169 + HelpersLib/Colors/ColorSlider.cs | 167 + HelpersLib/Colors/ColorUserControl.cs | 421 ++ HelpersLib/Colors/DialogColor.Designer.cs | 651 +++ HelpersLib/Colors/DialogColor.cs | 264 ++ HelpersLib/Colors/DialogColor.resx | 120 + HelpersLib/Colors/HSB.cs | 195 + HelpersLib/Colors/MyColor.cs | 115 + HelpersLib/Colors/RGBA.cs | 167 + HelpersLib/Cryptographic/Crc32.cs | 152 + HelpersLib/Cryptographic/HashCheck.cs | 164 + HelpersLib/Cryptographic/Translator.cs | 182 + HelpersLib/Cryptographic/TranslatorHelper.cs | 196 + HelpersLib/CursorData.cs | 118 + HelpersLib/DebugHelper.cs | 74 + HelpersLib/DebugTimer.cs | 72 + HelpersLib/Enums.cs | 292 ++ HelpersLib/Extensions/EnumExtensions.cs | 93 + HelpersLib/Extensions/Extensions.cs | 438 ++ HelpersLib/Extensions/GraphicsExtensions.cs | 70 + .../Extensions/GraphicsPathExtensions.cs | 196 + HelpersLib/Extensions/ObjectExtensions.cs | 156 + HelpersLib/Extensions/StreamExtensions.cs | 135 + HelpersLib/Extensions/StringExtensions.cs | 216 + HelpersLib/Extensions/XMLExtensions.cs | 265 ++ HelpersLib/ExternalProgram.cs | 97 + HelpersLib/FastDateTime.cs | 60 + HelpersLib/FileDownloader.cs | 223 + .../FileNameEditors/CssFileNameEditor.cs | 52 + .../FileNameEditors/ExeFileNameEditor.cs | 51 + HelpersLib/FixedSizedQueue.cs | 45 + .../Forms/ClipboardContentViewer.Designer.cs | 163 + HelpersLib/Forms/ClipboardContentViewer.cs | 104 + HelpersLib/Forms/ClipboardContentViewer.resx | 120 + .../Forms/ClipboardFormatForm.Designer.cs | 135 + HelpersLib/Forms/ClipboardFormatForm.cs | 63 + HelpersLib/Forms/ClipboardFormatForm.resx | 120 + HelpersLib/Forms/DebugForm.Designer.cs | 97 + HelpersLib/Forms/DebugForm.cs | 98 + HelpersLib/Forms/DebugForm.resx | 120 + HelpersLib/Forms/ErrorForm.Designer.cs | 164 + HelpersLib/Forms/ErrorForm.cs | 115 + HelpersLib/Forms/ErrorForm.resx | 120 + HelpersLib/Forms/HashCheckForm.Designer.cs | 211 + HelpersLib/Forms/HashCheckForm.cs | 129 + HelpersLib/Forms/HashCheckForm.resx | 120 + HelpersLib/Forms/ImageViewer.cs | 156 + HelpersLib/Forms/InputBox.cs | 175 + HelpersLib/Forms/InputBox.resx | 120 + HelpersLib/Forms/MyMessageBox.cs | 158 + HelpersLib/Forms/TrayForm.cs | 62 + HelpersLib/GIF/AnimatedGif.cs | 123 + HelpersLib/GIF/GifClass.cs | 239 ++ HelpersLib/GIF/GifCreator.cs | 131 + HelpersLib/GIF/GrayscaleQuantizer.cs | 93 + HelpersLib/GIF/OctreeQuantizer.cs | 504 +++ HelpersLib/GIF/PaletteQuantizer.cs | 145 + HelpersLib/GIF/Quantizer.cs | 309 ++ HelpersLib/Helpers/CaptureHelpers.cs | 299 ++ HelpersLib/Helpers/ClipboardHelpers.cs | 227 + HelpersLib/Helpers/ColorHelpers.cs | 355 ++ HelpersLib/Helpers/Helpers.cs | 728 ++++ HelpersLib/Helpers/ImageHelpers.cs | 819 ++++ HelpersLib/Helpers/RegistryHelpers.cs | 253 ++ HelpersLib/Helpers/ShortcutHelpers.cs | 102 + HelpersLib/HelpersLib.csproj | 362 ++ HelpersLib/Links.cs | 40 + HelpersLib/Logger.cs | 121 + HelpersLib/NameParser.cs | 277 ++ HelpersLib/Native/FolderSelectDialog.cs | 382 ++ HelpersLib/Native/NativeEnums.cs | 1916 +++++++++ HelpersLib/Native/NativeMethods.cs | 648 +++ HelpersLib/Native/NativeMethods_Helpers.cs | 452 ++ HelpersLib/Native/NativeStructs.cs | 563 +++ HelpersLib/Native/TaskbarManager.cs | 175 + HelpersLib/Printer/PrintForm.Designer.cs | 169 + HelpersLib/Printer/PrintForm.cs | 124 + HelpersLib/Printer/PrintForm.resx | 120 + HelpersLib/Printer/PrintHelper.cs | 195 + HelpersLib/Printer/PrintSettings.cs | 50 + HelpersLib/Printer/PrintTextForm.Designer.cs | 101 + HelpersLib/Printer/PrintTextForm.cs | 105 + HelpersLib/Printer/PrintTextForm.resx | 120 + HelpersLib/Printer/PrintTextHelper.cs | 232 + HelpersLib/Properties/AssemblyInfo.cs | 15 + HelpersLib/Properties/Resources.Designer.cs | 103 + HelpersLib/Properties/Resources.resx | 133 + HelpersLib/Resources/Icon.png | Bin 0 -> 15655 bytes HelpersLib/Resources/Loading.gif | Bin 0 -> 23169 bytes HelpersLib/Resources/ShareX_Icon.ico | Bin 0 -> 47458 bytes HelpersLib/Resources/crosshair.cur | Bin 0 -> 326 bytes HelpersLib/SettingsBase.cs | 82 + HelpersLib/SettingsHelper.cs | 189 + .../ApplicationInstanceManager.cs | 122 + .../InstanceProxy.cs | 58 + HelpersLib/StringLineReader.cs | 87 + HelpersLib/ThreadWorker.cs | 70 + HelpersLib/Tokenizer/BetweenTagsResult.cs | 37 + HelpersLib/Tokenizer/Token.cs | 50 + HelpersLib/Tokenizer/Tokenizer.cs | 259 ++ HelpersLib/UnsafeBitmap.cs | 177 + HelpersLib/UpdateChecker/UpdateChecker.cs | 147 + .../UpdateCheckerLabel.Designer.cs | 105 + .../UpdateChecker/UpdateCheckerLabel.cs | 105 + .../UpdateChecker/UpdateCheckerLabel.resx | 140 + HelpersLib/UpdateChecker/UpdateInfo.cs | 68 + .../UpdateChecker/UpdaterForm.Designer.cs | 178 + HelpersLib/UpdateChecker/UpdaterForm.cs | 249 ++ HelpersLib/UpdateChecker/UpdaterForm.resx | 120 + .../UserControls/BlackStyle/MyButton.cs | 173 + .../UserControls/BlackStyle/MyCheckBox.cs | 216 + HelpersLib/UserControls/BlackStyle/MyLabel.cs | 117 + .../UserControls/BlackStyle/MyProgressBar.cs | 216 + HelpersLib/UserControls/Canvas.cs | 97 + HelpersLib/UserControls/MenuButton.cs | 59 + HelpersLib/UserControls/MyListView.cs | 110 + .../UserControls/MyPictureBox.Designer.cs | 99 + HelpersLib/UserControls/MyPictureBox.cs | 286 ++ HelpersLib/UserControls/MyPictureBox.resx | 123 + .../UserControls/NoFocusBorderButton.cs | 51 + .../SplitContainerCustomSplitter.cs | 41 + .../ToolStripRadioButtonMenuItem.cs | 275 ++ HelpersLib/WindowState.cs | 69 + HelpersLib/XmlColor.cs | 87 + HelpersLib/XmlFont.cs | 84 + HelpersLib/packages.config | 6 + HistoryLib/Enums.cs | 34 + HistoryLib/HistoryForm.Designer.cs | 375 ++ HistoryLib/HistoryForm.cs | 331 ++ HistoryLib/HistoryForm.resx | 126 + HistoryLib/HistoryItem.cs | 42 + HistoryLib/HistoryItemInfoForm.Designer.cs | 67 + HistoryLib/HistoryItemInfoForm.cs | 38 + HistoryLib/HistoryItemInfoForm.resx | 120 + HistoryLib/HistoryItemManager.cs | 277 ++ HistoryLib/HistoryItemManager_ContextMenu.cs | 562 +++ HistoryLib/HistoryItemOld.cs | 45 + HistoryLib/HistoryLib.csproj | 141 + HistoryLib/HistoryManager.cs | 85 + HistoryLib/ImageHistoryForm.Designer.cs | 242 ++ HistoryLib/ImageHistoryForm.cs | 251 ++ HistoryLib/ImageHistoryForm.resx | 169 + HistoryLib/Properties/AssemblyInfo.cs | 15 + HistoryLib/Properties/Resources.Designer.cs | 73 + HistoryLib/Properties/Resources.resx | 124 + HistoryLib/Resources/ShareX_Icon.ico | Bin 0 -> 47458 bytes HistoryLib/UserControls/ObjectListView.cs | 132 + HistoryLib/XMLManager.cs | 176 + HistoryLib/XMLManagerOld.cs | 196 + HistoryLib/packages.config | 6 + ImageEffectsLib/Adjustments/Alpha.cs | 53 + ImageEffectsLib/Adjustments/Brightness.cs | 50 + ImageEffectsLib/Adjustments/Colorize.cs | 53 + ImageEffectsLib/Adjustments/Contrast.cs | 50 + ImageEffectsLib/Adjustments/Gamma.cs | 50 + ImageEffectsLib/Adjustments/Grayscale.cs | 50 + ImageEffectsLib/Adjustments/Hue.cs | 50 + ImageEffectsLib/Adjustments/Inverse.cs | 41 + ImageEffectsLib/Adjustments/Matrix.cs | 117 + ImageEffectsLib/Adjustments/Saturation.cs | 50 + ImageEffectsLib/Filters/Blur.cs | 53 + ImageEffectsLib/Filters/Pixelate.cs | 52 + ImageEffectsLib/Filters/Reflection.cs | 62 + ImageEffectsLib/Filters/Shadow.cs | 64 + ImageEffectsLib/Filters/TornEdge.cs | 56 + ImageEffectsLib/ImageEffect.cs | 34 + ImageEffectsLib/ImageEffectManager.cs | 46 + ImageEffectsLib/ImageEffectsForm.Designer.cs | 279 ++ ImageEffectsLib/ImageEffectsForm.cs | 296 ++ ImageEffectsLib/ImageEffectsForm.resx | 2498 +++++++++++ ImageEffectsLib/ImageEffectsLib.csproj | 166 + ImageEffectsLib/Manipulations/Background.cs | 47 + .../Manipulations/BackgroundGradient.cs | 54 + ImageEffectsLib/Manipulations/Border.cs | 53 + ImageEffectsLib/Manipulations/Canvas.cs | 55 + ImageEffectsLib/Manipulations/Flip.cs | 70 + ImageEffectsLib/Manipulations/Resize.cs | 55 + ImageEffectsLib/Manipulations/Rotate.cs | 53 + ImageEffectsLib/Manipulations/Scale.cs | 55 + ImageEffectsLib/Manipulations/Skew.cs | 55 + ImageEffectsLib/Properties/AssemblyInfo.cs | 15 + ImageEffectsLib/Watermark/GradientData.cs | 47 + .../Watermark/GradientMaker.Designer.cs | 201 + ImageEffectsLib/Watermark/GradientMaker.cs | 218 + ImageEffectsLib/Watermark/GradientMaker.resx | 120 + ImageEffectsLib/Watermark/GradientStop.cs | 64 + ImageEffectsLib/Watermark/WatermarkConfig.cs | 87 + .../Watermark/WatermarkForm.Designer.cs | 555 +++ ImageEffectsLib/Watermark/WatermarkForm.cs | 269 ++ ImageEffectsLib/Watermark/WatermarkForm.resx | 120 + ImageEffectsLib/Watermark/WatermarkManager.cs | 277 ++ IndexerLib/Enums.cs | 39 + IndexerLib/FolderInfo.cs | 73 + IndexerLib/HtmlHelper.cs | 80 + IndexerLib/Indexer.cs | 136 + IndexerLib/IndexerDefault.css | 84 + IndexerLib/IndexerHtml.cs | 132 + IndexerLib/IndexerLib.csproj | 96 + IndexerLib/IndexerSettings.cs | 71 + IndexerLib/IndexerText.cs | 78 + IndexerLib/IndexerXml.cs | 121 + IndexerLib/Properties/AssemblyInfo.cs | 15 + IndexerLib/Properties/Resources.Designer.cs | 121 + IndexerLib/Properties/Resources.resx | 130 + IndexerLib/Resources/doctype_xhtml.txt | 4 + IndexerLib/Resources/valid_xhtml.txt | 4 + IndexerLib/packages.config | 4 + Lib/Starksoft.Net.Ftp.dll | Bin 0 -> 94208 bytes Lib/Starksoft.Net.Proxy.dll | Bin 0 -> 29184 bytes README.md | 3 +- ScreenCapture/AVICache.cs | 139 + ScreenCapture/Enums.cs | 47 + ScreenCapture/Forms/DiamondRegion.cs | 44 + ScreenCapture/Forms/EllipseRegion.cs | 43 + ScreenCapture/Forms/FreeHandRegion.cs | 130 + ScreenCapture/Forms/PolygonRegion.cs | 164 + ScreenCapture/Forms/RectangleLight.cs | 240 ++ ScreenCapture/Forms/RectangleRegion.cs | 333 ++ .../Forms/RegionCapturePreview.Designer.cs | 246 ++ ScreenCapture/Forms/RegionCapturePreview.cs | 182 + ScreenCapture/Forms/RegionCapturePreview.resx | 229 + ScreenCapture/Forms/RoundedRectangleRegion.cs | 66 + ScreenCapture/Forms/Surface.cs | 366 ++ ScreenCapture/Forms/TriangleRegion.cs | 76 + ScreenCapture/LocationInfo.cs | 39 + ScreenCapture/Properties/AssemblyInfo.cs | 15 + .../Properties/Resources.Designer.cs | 70 + ScreenCapture/Properties/Resources.resx | 124 + ScreenCapture/RegionHelpers/AreaManager.cs | 370 ++ ScreenCapture/RegionHelpers/DrawObject.cs | 60 + ScreenCapture/RegionHelpers/InputManager.cs | 111 + ScreenCapture/RegionHelpers/MouseState.cs | 45 + ScreenCapture/RegionHelpers/NodeObject.cs | 61 + ScreenCapture/RegionHelpers/ResizeManager.cs | 302 ++ .../RegionHelpers/ShapeCaptureHelpers.cs | 83 + ScreenCapture/RegionHelpers/SurfaceOptions.cs | 84 + ScreenCapture/Resources/Crosshair.cur | Bin 0 -> 326 bytes ScreenCapture/ScreenCapture.csproj | 144 + ScreenCapture/ScreenRecorder.cs | 255 ++ ScreenCapture/ScreenRecorderCache.cs | 153 + ScreenCapture/Screenshot.cs | 174 + ScreenCapture/Screenshot_Transparent.cs | 487 +++ ScreenCapture/WindowInfo.cs | 118 + ScreenCapture/WindowsList.cs | 95 + ScreenCapture/WindowsListAdvanced.cs | 106 + ScreenCapture/packages.config | 5 + ShareX Setup.cmd | 2 + ShareX Setup.iss | 85 + ShareX.sln | 62 + ShareX.sln.DotSettings | 45 + ShareX/ApplicationConfig.cs | 132 + .../Controls/HotkeyManagerControl.Designer.cs | 133 + ShareX/Controls/HotkeyManagerControl.cs | 192 + ShareX/Controls/HotkeyManagerControl.resx | 120 + .../HotkeySelectionControl.Designer.cs | 100 + ShareX/Controls/HotkeySelectionControl.cs | 271 ++ ShareX/Controls/HotkeySelectionControl.resx | 120 + ShareX/Enums.cs | 175 + ShareX/Forms/AboutForm.Designer.cs | 316 ++ ShareX/Forms/AboutForm.cs | 174 + ShareX/Forms/AboutForm.resx | 133 + ShareX/Forms/AfterCaptureForm.Designer.cs | 135 + ShareX/Forms/AfterCaptureForm.cs | 103 + ShareX/Forms/AfterCaptureForm.resx | 120 + ShareX/Forms/AfterUploadForm.cs | 260 ++ ShareX/Forms/AfterUploadForm.designer.cs | 207 + ShareX/Forms/AfterUploadForm.resx | 123 + ShareX/Forms/AutoCaptureForm.Designer.cs | 220 + ShareX/Forms/AutoCaptureForm.cs | 215 + ShareX/Forms/AutoCaptureForm.resx | 126 + ShareX/Forms/ExternalProgramForm.Designer.cs | 155 + ShareX/Forms/ExternalProgramForm.cs | 68 + ShareX/Forms/ExternalProgramForm.resx | 120 + ShareX/Forms/HotkeyForm.cs | 147 + ShareX/Forms/HotkeySettingsForm.Designer.cs | 63 + ShareX/Forms/HotkeySettingsForm.cs | 49 + ShareX/Forms/HotkeySettingsForm.resx | 120 + ShareX/Forms/MainForm.Designer.cs | 1742 ++++++++ ShareX/Forms/MainForm.cs | 1085 +++++ ShareX/Forms/MainForm.resx | 2567 +++++++++++ ShareX/Forms/MainForm_Capture.cs | 632 +++ ShareX/Forms/ScreenColorPicker.Designer.cs | 164 + ShareX/Forms/ScreenColorPicker.cs | 165 + ShareX/Forms/ScreenColorPicker.resx | 120 + ShareX/Forms/ScreenRecordForm.cs | 207 + ShareX/Forms/ScreenRegionForm.Designer.cs | 50 + ShareX/Forms/ScreenRegionForm.cs | 68 + ShareX/Forms/ScreenRegionForm.resx | 120 + ShareX/Forms/SettingsForm.Designer.cs | 829 ++++ ShareX/Forms/SettingsForm.cs | 415 ++ ShareX/Forms/SettingsForm.resx | 120 + ShareX/Forms/TaskSettingsForm.Designer.cs | 1806 ++++++++ ShareX/Forms/TaskSettingsForm.cs | 762 ++++ ShareX/Forms/TaskSettingsForm.resx | 144 + ShareX/Forms/UploadTestForm.Designer.cs | 266 ++ ShareX/Forms/UploadTestForm.cs | 348 ++ ShareX/Forms/UploadTestForm.resx | 126 + ShareX/Forms/WatchFolderForm.Designer.cs | 158 + ShareX/Forms/WatchFolderForm.cs | 68 + ShareX/Forms/WatchFolderForm.resx | 120 + ShareX/HotkeyInfo.cs | 221 + ShareX/HotkeyManager.cs | 178 + ShareX/HotkeySettings.cs | 60 + ShareX/HotkeysConfig.cs | 35 + ShareX/ImageData.cs | 59 + ShareX/ImageTag.cs | 32 + ShareX/Program.cs | 446 ++ ShareX/Properties/AssemblyInfo.cs | 15 + ShareX/Properties/Resources.Designer.cs | 792 ++++ ShareX/Properties/Resources.resx | 340 ++ ShareX/Resources/Camera.wav | Bin 0 -> 43820 bytes ShareX/Resources/Ellipse.png | Bin 0 -> 1353 bytes ShareX/Resources/FreeHand.png | Bin 0 -> 1305 bytes ShareX/Resources/Fullscreen.png | Bin 0 -> 1068 bytes ShareX/Resources/Polygon.png | Bin 0 -> 352 bytes ShareX/Resources/Rectangle.png | Bin 0 -> 1331 bytes ShareX/Resources/RoundedRectangle.png | Bin 0 -> 454 bytes ShareX/Resources/ShareX_Icon.ico | Bin 0 -> 47458 bytes ShareX/Resources/ShareX_Logo.png | Bin 0 -> 20935 bytes ShareX/Resources/Triangle.png | Bin 0 -> 1306 bytes ShareX/Resources/Window.png | Bin 0 -> 1319 bytes ShareX/Resources/address-book-blue.png | Bin 0 -> 698 bytes ShareX/Resources/application--pencil.png | Bin 0 -> 550 bytes ShareX/Resources/application-block.png | Bin 0 -> 526 bytes ShareX/Resources/application-blue.png | Bin 0 -> 353 bytes ShareX/Resources/application-browser.png | Bin 0 -> 647 bytes ShareX/Resources/application-form.png | Bin 0 -> 445 bytes ShareX/Resources/application-icon-large.png | Bin 0 -> 1472 bytes ShareX/Resources/application-monitor.png | Bin 0 -> 595 bytes ShareX/Resources/application-task.png | Bin 0 -> 490 bytes ShareX/Resources/au.png | Bin 0 -> 673 bytes ShareX/Resources/block.png | Bin 0 -> 609 bytes ShareX/Resources/bug.png | Bin 0 -> 704 bytes ShareX/Resources/camcorder--pencil.png | Bin 0 -> 723 bytes ShareX/Resources/camcorder-image.png | Bin 0 -> 639 bytes ShareX/Resources/camera.png | Bin 0 -> 626 bytes ShareX/Resources/clipboard--plus.png | Bin 0 -> 604 bytes ShareX/Resources/clock--plus.png | Bin 0 -> 754 bytes ShareX/Resources/color.png | Bin 0 -> 783 bytes ShareX/Resources/control-record-yellow.png | Bin 0 -> 522 bytes ShareX/Resources/control-record.png | Bin 0 -> 484 bytes ShareX/Resources/cross-button.png | Bin 0 -> 555 bytes ShareX/Resources/diamond.png | Bin 0 -> 1318 bytes ShareX/Resources/document--arrow.png | Bin 0 -> 582 bytes ShareX/Resources/document-copy.png | Bin 0 -> 564 bytes ShareX/Resources/drive-globe.png | Bin 0 -> 786 bytes ShareX/Resources/edit-scale.png | Bin 0 -> 367 bytes ShareX/Resources/folder--plus.png | Bin 0 -> 579 bytes ShareX/Resources/folder-open-image.png | Bin 0 -> 764 bytes ShareX/Resources/folder-tree.png | Bin 0 -> 518 bytes ShareX/Resources/fr.png | Bin 0 -> 545 bytes ShareX/Resources/gear.png | Bin 0 -> 721 bytes ShareX/Resources/globe--pencil.png | Bin 0 -> 938 bytes ShareX/Resources/globe-share.png | Bin 0 -> 865 bytes ShareX/Resources/hammer--arrow.png | Bin 0 -> 760 bytes ShareX/Resources/hammer--pencil.png | Bin 0 -> 705 bytes ShareX/Resources/hammer-screwdriver.png | Bin 0 -> 786 bytes ShareX/Resources/hammer.png | Bin 0 -> 554 bytes ShareX/Resources/image-export.png | Bin 0 -> 589 bytes ShareX/Resources/image-saturation.png | Bin 0 -> 1474 bytes ShareX/Resources/image.png | Bin 0 -> 547 bytes ShareX/Resources/keyboard--pencil.png | Bin 0 -> 662 bytes ShareX/Resources/layer-shape-curve.png | Bin 0 -> 481 bytes ShareX/Resources/layer-shape-ellipse.png | Bin 0 -> 740 bytes ShareX/Resources/layer-shape-polygon.png | Bin 0 -> 631 bytes ShareX/Resources/layer-shape-round.png | Bin 0 -> 623 bytes ShareX/Resources/layer-shape.png | Bin 0 -> 498 bytes ShareX/Resources/layer.png | Bin 0 -> 460 bytes ShareX/Resources/layers-arrange.png | Bin 0 -> 620 bytes ShareX/Resources/layers-ungroup.png | Bin 0 -> 828 bytes ShareX/Resources/monitor.png | Bin 0 -> 511 bytes ShareX/Resources/navigation-000-button.png | Bin 0 -> 614 bytes ShareX/Resources/navigation-090-button.png | Bin 0 -> 613 bytes ShareX/Resources/notebook.png | Bin 0 -> 541 bytes ShareX/Resources/pencil.png | Bin 0 -> 1548 bytes ShareX/Resources/pipette.png | Bin 0 -> 543 bytes ShareX/Resources/present.png | Bin 0 -> 771 bytes ShareX/Resources/question-button.png | Bin 0 -> 648 bytes ShareX/Resources/steam.ico | Bin 0 -> 1406 bytes ShareX/Resources/tick-button.png | Bin 0 -> 568 bytes ShareX/Resources/toolbox.png | Bin 0 -> 488 bytes ShareX/Resources/tr.png | Bin 0 -> 492 bytes ShareX/Resources/upload-cloud.png | Bin 0 -> 1541 bytes ShareX/ScreenRegionManager.cs | 66 + ShareX/ShareX.csproj | 455 ++ ShareX/TaskHelpers.cs | 327 ++ ShareX/TaskInfo.cs | 144 + ShareX/TaskManager.cs | 413 ++ ShareX/TaskSettings.cs | 314 ++ ShareX/UploadInfoManager.cs | 293 ++ ShareX/UploadInfoParser.cs | 70 + ShareX/UploadInfoStatus.cs | 70 + ShareX/UploadManager.cs | 290 ++ ShareX/UploadTask.cs | 959 +++++ ShareX/WatchFolder.cs | 143 + ShareX/WatchFolderManager.cs | 130 + ShareX/WatchFolderSettings.cs | 34 + ShareX/app.manifest | 8 + ShareX/packages.config | 7 + Update.xml | 11 + UploadersLib/ApiKeys/ApiKeys.cs | 82 + UploadersLib/ApiKeys/ApiKeysConfig.cs | 178 + UploadersLib/ApiKeys/ApiKeysUI.Designer.cs | 60 + UploadersLib/ApiKeys/ApiKeysUI.cs | 44 + UploadersLib/ApiKeys/ApiKeysUI.resx | 120 + UploadersLib/ApiKeys/jira_sharex.pfx | Bin 0 -> 1646 bytes UploadersLib/Config/GoogleTranslatorConfig.cs | 43 + UploadersLib/Config/UploadersConfig.cs | 379 ++ UploadersLib/Enums.cs | 321 ++ .../FTPClient/FTPClientForm.Designer.cs | 515 +++ UploadersLib/FTPClient/FTPClientForm.cs | 615 +++ UploadersLib/FTPClient/FTPClientForm.resx | 129 + UploadersLib/FTPClient/IconHelper.cs | 235 + UploadersLib/FTPClient/IconListManager.cs | 138 + UploadersLib/FTPClient/ListViewEx.cs | 499 +++ .../FTPClient/LoginDialog.Designer.cs | 130 + UploadersLib/FTPClient/LoginDialog.cs | 44 + UploadersLib/FTPClient/LoginDialog.resx | 120 + UploadersLib/Favicons/Box.ico | Bin 0 -> 1150 bytes UploadersLib/Favicons/Dropbox.ico | Bin 0 -> 2550 bytes UploadersLib/Favicons/Flickr.ico | Bin 0 -> 92854 bytes UploadersLib/Favicons/Gett.ico | Bin 0 -> 5430 bytes UploadersLib/Favicons/Google.ico | Bin 0 -> 1150 bytes UploadersLib/Favicons/GoogleDrive.ico | Bin 0 -> 5430 bytes UploadersLib/Favicons/ImageShack.ico | Bin 0 -> 1150 bytes UploadersLib/Favicons/Imgur.ico | Bin 0 -> 1150 bytes UploadersLib/Favicons/Localhostr.ico | Bin 0 -> 1150 bytes UploadersLib/Favicons/Mega.ico | Bin 0 -> 1150 bytes UploadersLib/Favicons/Minus.ico | Bin 0 -> 2550 bytes UploadersLib/Favicons/Pastebin.ico | Bin 0 -> 1406 bytes UploadersLib/Favicons/Photobucket.ico | Bin 0 -> 4286 bytes UploadersLib/Favicons/Picasa.ico | Bin 0 -> 1406 bytes UploadersLib/Favicons/RapidShare.ico | Bin 0 -> 1150 bytes UploadersLib/Favicons/SendSpace.ico | Bin 0 -> 2550 bytes UploadersLib/Favicons/TinyPic.ico | Bin 0 -> 1406 bytes UploadersLib/Favicons/TwitPic.ico | Bin 0 -> 1150 bytes UploadersLib/Favicons/TwitSnaps.ico | Bin 0 -> 8246 bytes UploadersLib/Favicons/Twitter.ico | Bin 0 -> 1150 bytes UploadersLib/Favicons/YFrog.ico | Bin 0 -> 1150 bytes UploadersLib/Favicons/jira.png | Bin 0 -> 396 bytes UploadersLib/FileUploader.cs | 48 + UploadersLib/FileUploaders/Box.cs | 260 ++ .../FileUploaders/CustomFileUploader.cs | 61 + UploadersLib/FileUploaders/DropIO.cs | 132 + UploadersLib/FileUploaders/Dropbox.cs | 415 ++ UploadersLib/FileUploaders/Email.cs | 82 + UploadersLib/FileUploaders/FTP.cs | 397 ++ UploadersLib/FileUploaders/FTP/FTPAccount.cs | 241 ++ .../FileUploaders/FTP/FTPAccountManager.cs | 55 + UploadersLib/FileUploaders/FTP/FTPAdapter.cs | 419 ++ UploadersLib/FileUploaders/FTP/FTPHelpers.cs | 220 + UploadersLib/FileUploaders/FTPUploader.cs | 90 + UploadersLib/FileUploaders/FileBin.cs | 49 + UploadersLib/FileUploaders/FileSonic.cs | 85 + UploadersLib/FileUploaders/Ge_tt.cs | 139 + UploadersLib/FileUploaders/GoogleDrive.cs | 156 + UploadersLib/FileUploaders/Hostr.cs | 96 + UploadersLib/FileUploaders/Jira.cs | 257 ++ .../Localhost/LocalhostAccount.cs | 197 + UploadersLib/FileUploaders/Mega.cs | 194 + UploadersLib/FileUploaders/Minus.cs | 389 ++ UploadersLib/FileUploaders/RapidShare.cs | 238 + UploadersLib/FileUploaders/SFTP.cs | 235 + UploadersLib/FileUploaders/SendSpace.cs | 592 +++ .../FileUploaders/SendSpaceManager.cs | 91 + UploadersLib/FileUploaders/ShareCX.cs | 52 + .../FileUploaders/SharedFolderUploader.cs | 60 + .../GUI/AccountTypeControl.Designer.cs | 75 + UploadersLib/GUI/AccountTypeControl.cs | 66 + UploadersLib/GUI/AccountTypeControl.resx | 120 + UploadersLib/GUI/AccountsControl.Designer.cs | 148 + UploadersLib/GUI/AccountsControl.cs | 73 + UploadersLib/GUI/AccountsControl.resx | 120 + UploadersLib/GUI/DropboxFilesForm.Designer.cs | 202 + UploadersLib/GUI/DropboxFilesForm.cs | 245 ++ UploadersLib/GUI/DropboxFilesForm.resx | 126 + UploadersLib/GUI/EmailForm.Designer.cs | 144 + UploadersLib/GUI/EmailForm.cs | 63 + UploadersLib/GUI/EmailForm.resx | 120 + .../GUI/GoogleTranslateGUI.Designer.cs | 240 ++ UploadersLib/GUI/GoogleTranslateGUI.cs | 121 + UploadersLib/GUI/GoogleTranslateGUI.resx | 120 + UploadersLib/GUI/GoogleTranslateGUIHelper.cs | 140 + UploadersLib/GUI/GoogleTranslateGUIWorker.cs | 70 + UploadersLib/GUI/JiraUpload.Designer.cs | 140 + UploadersLib/GUI/JiraUpload.cs | 97 + UploadersLib/GUI/JiraUpload.resx | 120 + UploadersLib/GUI/OAuth2Control.Designer.cs | 137 + UploadersLib/GUI/OAuth2Control.cs | 108 + UploadersLib/GUI/OAuth2Control.resx | 120 + UploadersLib/GUI/ResponseForm.Designer.cs | 123 + UploadersLib/GUI/ResponseForm.cs | 60 + UploadersLib/GUI/ResponseForm.resx | 120 + UploadersLib/GUI/TwitterMsg.Designer.cs | 124 + UploadersLib/GUI/TwitterMsg.cs | 182 + UploadersLib/GUI/TwitterMsg.resx | 120 + .../GUI/UploadersConfigForm.Designer.cs | 3820 +++++++++++++++++ UploadersLib/GUI/UploadersConfigForm.cs | 1220 ++++++ UploadersLib/GUI/UploadersConfigForm.resx | 264 ++ UploadersLib/GUI/UploadersConfigFormGUI.cs | 655 +++ UploadersLib/GUI/UploadersConfigFormHelper.cs | 1423 ++++++ UploadersLib/GUI/UserPassBox.Designer.cs | 176 + UploadersLib/GUI/UserPassBox.cs | 92 + UploadersLib/GUI/UserPassBox.resx | 120 + UploadersLib/HelperClasses/AccountInfo.cs | 33 + UploadersLib/HelperClasses/Argument.cs | 44 + .../HelperClasses/CertFileNameEditor.cs | 51 + .../HelperClasses/CustomUploaderItem.cs | 219 + .../HelperClasses/CustomUploaderParse.cs | 274 ++ .../HelperClasses/ImageListManager.cs | 64 + UploadersLib/HelperClasses/JSONHelper.cs | 79 + .../HelperClasses/KeyFileNameEditor.cs | 51 + UploadersLib/HelperClasses/OAuth/IOAuth.cs | 36 + UploadersLib/HelperClasses/OAuth/IOAuth2.cs | 40 + .../HelperClasses/OAuth/OAuth2Info.cs | 46 + .../HelperClasses/OAuth/OAuth2Token.cs | 55 + UploadersLib/HelperClasses/OAuth/OAuthInfo.cs | 111 + .../HelperClasses/OAuth/OAuthManager.cs | 259 ++ UploadersLib/HelperClasses/ProgressManager.cs | 111 + UploadersLib/HelperClasses/TCPClient.cs | 193 + UploadersLib/ImageUploader.cs | 43 + .../ImageUploaders/CustomImageUploader.cs | 61 + UploadersLib/ImageUploaders/FlickrUploader.cs | 368 ++ UploadersLib/ImageUploaders/ImageBin.cs | 61 + .../ImageUploaders/ImageShackUploader.cs | 70 + UploadersLib/ImageUploaders/Img1Uploader.cs | 48 + UploadersLib/ImageUploaders/Imgur_v2.cs | 169 + UploadersLib/ImageUploaders/Imgur_v3.cs | 251 ++ UploadersLib/ImageUploaders/ImmioUploader.cs | 64 + UploadersLib/ImageUploaders/Photobucket.cs | 174 + UploadersLib/ImageUploaders/Picasa.cs | 215 + .../ImageUploaders/TinyPicUploader.cs | 231 + .../ImageUploaders/TwitPicUploader.cs | 136 + .../ImageUploaders/TwitSnapsUploader.cs | 104 + .../ImageUploaders/UploadScreenshot.cs | 101 + UploadersLib/ImageUploaders/YfrogUploader.cs | 160 + UploadersLib/OtherServices/GoogleTranslate.cs | 139 + UploadersLib/OtherServices/Lyricsfly.cs | 191 + UploadersLib/Properties/AssemblyInfo.cs | 15 + UploadersLib/Properties/Resources.Designer.cs | 663 +++ UploadersLib/Properties/Resources.resx | 301 ++ UploadersLib/ProxyInfo.cs | 128 + UploadersLib/Resources/ShareX_Icon.ico | Bin 0 -> 47458 bytes UploadersLib/Resources/ShareX_Logo.png | Bin 0 -> 20935 bytes UploadersLib/Resources/folder-network.png | Bin 0 -> 573 bytes UploadersLib/Resources/folder.gif | Bin 0 -> 995 bytes UploadersLib/Resources/folder_gray.gif | Bin 0 -> 1002 bytes UploadersLib/Resources/folder_photos.gif | Bin 0 -> 999 bytes UploadersLib/Resources/folder_public.gif | Bin 0 -> 1017 bytes UploadersLib/Resources/folder_star.gif | Bin 0 -> 1034 bytes UploadersLib/Resources/folder_user.gif | Bin 0 -> 1040 bytes UploadersLib/Resources/globe-network.png | Bin 0 -> 820 bytes UploadersLib/Resources/mail.png | Bin 0 -> 494 bytes UploadersLib/Resources/package.gif | Bin 0 -> 1054 bytes UploadersLib/Resources/page_white.gif | Bin 0 -> 361 bytes UploadersLib/Resources/page_white_acrobat.gif | Bin 0 -> 605 bytes .../Resources/page_white_actionscript.gif | Bin 0 -> 630 bytes UploadersLib/Resources/page_white_c.gif | Bin 0 -> 607 bytes UploadersLib/Resources/page_white_code.gif | Bin 0 -> 612 bytes .../Resources/page_white_compressed.gif | Bin 0 -> 1042 bytes .../Resources/page_white_cplusplus.gif | Bin 0 -> 614 bytes UploadersLib/Resources/page_white_csharp.gif | Bin 0 -> 625 bytes UploadersLib/Resources/page_white_cup.gif | Bin 0 -> 633 bytes UploadersLib/Resources/page_white_dvd.gif | Bin 0 -> 628 bytes UploadersLib/Resources/page_white_excel.gif | Bin 0 -> 622 bytes UploadersLib/Resources/page_white_film.gif | Bin 0 -> 624 bytes UploadersLib/Resources/page_white_flash.gif | Bin 0 -> 608 bytes UploadersLib/Resources/page_white_gear.gif | Bin 0 -> 609 bytes UploadersLib/Resources/page_white_paint.gif | Bin 0 -> 622 bytes UploadersLib/Resources/page_white_php.gif | Bin 0 -> 601 bytes UploadersLib/Resources/page_white_picture.gif | Bin 0 -> 626 bytes .../Resources/page_white_powerpoint.gif | Bin 0 -> 623 bytes UploadersLib/Resources/page_white_ruby.gif | Bin 0 -> 615 bytes UploadersLib/Resources/page_white_sound.gif | Bin 0 -> 617 bytes UploadersLib/Resources/page_white_text.gif | Bin 0 -> 381 bytes UploadersLib/Resources/page_white_tux.gif | Bin 0 -> 624 bytes UploadersLib/Resources/page_white_vector.gif | Bin 0 -> 628 bytes .../Resources/page_white_visualstudio.gif | Bin 0 -> 623 bytes UploadersLib/Resources/page_white_word.gif | Bin 0 -> 630 bytes UploadersLib/Resources/server-network.png | Bin 0 -> 507 bytes UploadersLib/SocialServices/Twitter.cs | 82 + UploadersLib/TextUploader.cs | 57 + .../TextUploaders/CustomTextUploader.cs | 89 + UploadersLib/TextUploaders/Gist.cs | 67 + UploadersLib/TextUploaders/Paste2.cs | 78 + UploadersLib/TextUploaders/Paste_ee.cs | 78 + UploadersLib/TextUploaders/Pastebin.cs | 148 + UploadersLib/TextUploaders/Pastebin_ca.cs | 134 + UploadersLib/TextUploaders/Pastee.cs | 66 + UploadersLib/TextUploaders/Slexy.cs | 105 + UploadersLib/URLShortener.cs | 34 + .../URLShorteners/BitlyURLShortener.cs | 68 + .../URLShorteners/CustomURLShortener.cs | 78 + .../URLShorteners/GoogleURLShortener.cs | 192 + .../URLShorteners/IsgdURLShortener.cs | 50 + UploadersLib/URLShorteners/JmpURLShortener.cs | 71 + .../URLShorteners/ThreelyURLShortener.cs | 58 + .../URLShorteners/TinyURLShortener.cs | 50 + .../URLShorteners/TurlURLShortener.cs | 64 + UploadersLib/UploadResult.cs | 113 + UploadersLib/Uploader.cs | 648 +++ UploadersLib/UploadersLib.csproj | 451 ++ UploadersLib/packages.config | 8 + 903 files changed, 133633 insertions(+), 10 deletions(-) create mode 100644 CodeWorks.sln create mode 100644 CodeWorks/CodeWorks.csproj create mode 100644 CodeWorks/Licenses/ShareXLicense.txt create mode 100644 CodeWorks/MainForm.Designer.cs create mode 100644 CodeWorks/MainForm.cs create mode 100644 CodeWorks/MainForm.resx create mode 100644 CodeWorks/Program.cs create mode 100644 CodeWorks/Properties/AssemblyInfo.cs create mode 100644 CodeWorks/Properties/Resources.Designer.cs create mode 100644 CodeWorks/Properties/Resources.resx create mode 100644 CodeWorks/RegionArea.cs create mode 100644 CodeWorks/RegionAreaManager.cs create mode 100644 CodeWorks/TextInfo.cs create mode 100644 Docs/CodeMaid.settings create mode 100644 Docs/ShareX_Class.zip create mode 100644 Docs/VersionHistory.txt rename LICENSE => Docs/license.txt (98%) create mode 100644 Docs/svn global ignore.txt create mode 100644 GreenshotImageEditor/Configuration/CoreConfiguration.cs create mode 100644 GreenshotImageEditor/Configuration/EditorConfiguration.cs create mode 100644 GreenshotImageEditor/Configuration/LanguageKeys.cs create mode 100644 GreenshotImageEditor/Controls/AnimatingForm.cs create mode 100644 GreenshotImageEditor/Controls/BackgroundForm.cs create mode 100644 GreenshotImageEditor/Controls/BackgroundForm.designer.cs create mode 100644 GreenshotImageEditor/Controls/BindableToolStripButton.cs create mode 100644 GreenshotImageEditor/Controls/BindableToolStripComboBox.cs create mode 100644 GreenshotImageEditor/Controls/BindableToolStripDropDownButton.cs create mode 100644 GreenshotImageEditor/Controls/ColorButton.cs create mode 100644 GreenshotImageEditor/Controls/FontFamilyComboBox.cs create mode 100644 GreenshotImageEditor/Controls/FormWithoutActivation.cs create mode 100644 GreenshotImageEditor/Controls/GreenshotButton.cs create mode 100644 GreenshotImageEditor/Controls/GreenshotCheckBox.cs create mode 100644 GreenshotImageEditor/Controls/GreenshotColumnSorter.cs create mode 100644 GreenshotImageEditor/Controls/GreenshotComboBox.cs create mode 100644 GreenshotImageEditor/Controls/GreenshotForm.cs create mode 100644 GreenshotImageEditor/Controls/GreenshotGroupBox.cs create mode 100644 GreenshotImageEditor/Controls/GreenshotLabel.cs create mode 100644 GreenshotImageEditor/Controls/GreenshotRadioButton.cs create mode 100644 GreenshotImageEditor/Controls/GreenshotTabPage.cs create mode 100644 GreenshotImageEditor/Controls/GreenshotTextBox.cs create mode 100644 GreenshotImageEditor/Controls/GreenshotToolDropDownButton.cs create mode 100644 GreenshotImageEditor/Controls/GreenshotToolStripButton.cs create mode 100644 GreenshotImageEditor/Controls/GreenshotToolStripLabel.cs create mode 100644 GreenshotImageEditor/Controls/GreenshotToolStripMenuItem.cs create mode 100644 GreenshotImageEditor/Controls/IGreenshotConfigBindable.cs create mode 100644 GreenshotImageEditor/Controls/IGreenshotLanguageBindable.cs create mode 100644 GreenshotImageEditor/Controls/MenuStripEx.cs create mode 100644 GreenshotImageEditor/Controls/NonJumpingPanel.cs create mode 100644 GreenshotImageEditor/Controls/Pipette.cs create mode 100644 GreenshotImageEditor/Controls/PleaseWaitForm.cs create mode 100644 GreenshotImageEditor/Controls/PleaseWaitForm.designer.cs create mode 100644 GreenshotImageEditor/Controls/QualityDialog.cs create mode 100644 GreenshotImageEditor/Controls/QualityDialog.designer.cs create mode 100644 GreenshotImageEditor/Controls/SaveImageFileDialog.cs create mode 100644 GreenshotImageEditor/Controls/ToolStripColorButton.cs create mode 100644 GreenshotImageEditor/Controls/ToolStripEx.cs create mode 100644 GreenshotImageEditor/Controls/ToolStripNumericUpDown.cs create mode 100644 GreenshotImageEditor/Core/BinaryStructHelper.cs create mode 100644 GreenshotImageEditor/Core/Cache.cs create mode 100644 GreenshotImageEditor/Core/ClipboardHelper.cs create mode 100644 GreenshotImageEditor/Core/Effects.cs create mode 100644 GreenshotImageEditor/Core/EncryptionHelper.cs create mode 100644 GreenshotImageEditor/Core/EnumExtensions.cs create mode 100644 GreenshotImageEditor/Core/FastBitmap.cs create mode 100644 GreenshotImageEditor/Core/FilenameHelper.cs create mode 100644 GreenshotImageEditor/Core/GreenshotResources.cs create mode 100644 GreenshotImageEditor/Core/GreenshotResources.resx create mode 100644 GreenshotImageEditor/Core/ImageHelper.cs create mode 100644 GreenshotImageEditor/Core/ImageOutput.cs create mode 100644 GreenshotImageEditor/Core/LOG.cs create mode 100644 GreenshotImageEditor/Core/Language.cs create mode 100644 GreenshotImageEditor/Core/Objects.cs create mode 100644 GreenshotImageEditor/Core/QuantizerHelper.cs create mode 100644 GreenshotImageEditor/Core/WindowCapture.cs create mode 100644 GreenshotImageEditor/Core/WindowsHelper.cs create mode 100644 GreenshotImageEditor/Drawing/ArrowContainer.cs create mode 100644 GreenshotImageEditor/Drawing/CropContainer.cs create mode 100644 GreenshotImageEditor/Drawing/CursorContainer.cs create mode 100644 GreenshotImageEditor/Drawing/DrawableContainer.cs create mode 100644 GreenshotImageEditor/Drawing/DrawableContainerList.cs create mode 100644 GreenshotImageEditor/Drawing/EllipseContainer.cs create mode 100644 GreenshotImageEditor/Drawing/Fields/AbstractFieldHolder.cs create mode 100644 GreenshotImageEditor/Drawing/Fields/AbstractFieldHolderWithChildren.cs create mode 100644 GreenshotImageEditor/Drawing/Fields/Binding/AbstractBindingConverter.cs create mode 100644 GreenshotImageEditor/Drawing/Fields/Binding/AlignmentConverter.cs create mode 100644 GreenshotImageEditor/Drawing/Fields/Binding/BidirectionalBinding.cs create mode 100644 GreenshotImageEditor/Drawing/Fields/Binding/DecimalDoublePercentageConverter.cs create mode 100644 GreenshotImageEditor/Drawing/Fields/Binding/DecimalFloatConverter.cs create mode 100644 GreenshotImageEditor/Drawing/Fields/Binding/DecimalIntConverter.cs create mode 100644 GreenshotImageEditor/Drawing/Fields/Binding/IBindingConverter.cs create mode 100644 GreenshotImageEditor/Drawing/Fields/Binding/IBindingValidator.cs create mode 100644 GreenshotImageEditor/Drawing/Fields/Binding/NotNullValidator.cs create mode 100644 GreenshotImageEditor/Drawing/Fields/Field.cs create mode 100644 GreenshotImageEditor/Drawing/Fields/FieldAggregator.cs create mode 100644 GreenshotImageEditor/Drawing/Fields/FieldType.cs create mode 100644 GreenshotImageEditor/Drawing/Fields/IFieldHolder.cs create mode 100644 GreenshotImageEditor/Drawing/FilterContainer.cs create mode 100644 GreenshotImageEditor/Drawing/Filters/AbstractFilter.cs create mode 100644 GreenshotImageEditor/Drawing/Filters/BlurFilter.cs create mode 100644 GreenshotImageEditor/Drawing/Filters/BrightnessFilter.cs create mode 100644 GreenshotImageEditor/Drawing/Filters/GrayscaleFilter.cs create mode 100644 GreenshotImageEditor/Drawing/Filters/HighlightFilter.cs create mode 100644 GreenshotImageEditor/Drawing/Filters/IFilter.cs create mode 100644 GreenshotImageEditor/Drawing/Filters/MagnifierFilter.cs create mode 100644 GreenshotImageEditor/Drawing/Filters/PixelizationFilter.cs create mode 100644 GreenshotImageEditor/Drawing/FreehandContainer.cs create mode 100644 GreenshotImageEditor/Drawing/Gripper.cs create mode 100644 GreenshotImageEditor/Drawing/HighlightContainer.cs create mode 100644 GreenshotImageEditor/Drawing/IconContainer.cs create mode 100644 GreenshotImageEditor/Drawing/ImageContainer.cs create mode 100644 GreenshotImageEditor/Drawing/LineContainer.cs create mode 100644 GreenshotImageEditor/Drawing/ObfuscateContainer.cs create mode 100644 GreenshotImageEditor/Drawing/RectangleContainer.cs create mode 100644 GreenshotImageEditor/Drawing/RoundedRectangle.cs create mode 100644 GreenshotImageEditor/Drawing/Surface.cs create mode 100644 GreenshotImageEditor/Drawing/TextContainer.cs create mode 100644 GreenshotImageEditor/Forms/BaseForm.cs create mode 100644 GreenshotImageEditor/Forms/ColorDialog.Designer.cs create mode 100644 GreenshotImageEditor/Forms/ColorDialog.cs create mode 100644 GreenshotImageEditor/Forms/ColorDialog.resx create mode 100644 GreenshotImageEditor/Forms/ColorPickerToolStripButton.cs create mode 100644 GreenshotImageEditor/Forms/DropShadowSettingsForm.Designer.cs create mode 100644 GreenshotImageEditor/Forms/DropShadowSettingsForm.cs create mode 100644 GreenshotImageEditor/Forms/ImageEditorForm.Designer.cs create mode 100644 GreenshotImageEditor/Forms/ImageEditorForm.cs create mode 100644 GreenshotImageEditor/Forms/ImageEditorForm.resx create mode 100644 GreenshotImageEditor/Forms/MovableShowColorForm.Designer.cs create mode 100644 GreenshotImageEditor/Forms/MovableShowColorForm.cs create mode 100644 GreenshotImageEditor/Forms/ResizeSettingsForm.Designer.cs create mode 100644 GreenshotImageEditor/Forms/ResizeSettingsForm.cs create mode 100644 GreenshotImageEditor/Forms/ToolStripMenuSelectList.cs create mode 100644 GreenshotImageEditor/Forms/TornEdgeSettingsForm.Designer.cs create mode 100644 GreenshotImageEditor/Forms/TornEdgeSettingsForm.cs create mode 100644 GreenshotImageEditor/Greenshot.sln create mode 100644 GreenshotImageEditor/GreenshotImageEditor.csproj create mode 100644 GreenshotImageEditor/Helpers/Colors.cs create mode 100644 GreenshotImageEditor/Helpers/GeometryHelper.cs create mode 100644 GreenshotImageEditor/Helpers/GuiRectangle.cs create mode 100644 GreenshotImageEditor/Helpers/LogHelper.cs create mode 100644 GreenshotImageEditor/Helpers/ScaleHelper.cs create mode 100644 GreenshotImageEditor/Helpers/ToolStripItemEndisabler.cs create mode 100644 GreenshotImageEditor/IniFile/IniAttributes.cs create mode 100644 GreenshotImageEditor/IniFile/IniConfig.cs create mode 100644 GreenshotImageEditor/IniFile/IniReader.cs create mode 100644 GreenshotImageEditor/IniFile/IniSection.cs create mode 100644 GreenshotImageEditor/IniFile/IniValue.cs create mode 100644 GreenshotImageEditor/Interfaces/Capture.cs create mode 100644 GreenshotImageEditor/Interfaces/Drawing/Container.cs create mode 100644 GreenshotImageEditor/Interfaces/Forms/ImageEditor.cs create mode 100644 GreenshotImageEditor/Interfaces/Generic.cs create mode 100644 GreenshotImageEditor/Interfaces/IDestination.cs create mode 100644 GreenshotImageEditor/Interfaces/IProcessor.cs create mode 100644 GreenshotImageEditor/Interfaces/Plugin/PluginInterfaces.cs create mode 100644 GreenshotImageEditor/Interop/Base.cs create mode 100644 GreenshotImageEditor/Interop/COMWrapper.cs create mode 100644 GreenshotImageEditor/Interop/ComProgIdAttribute.cs create mode 100644 GreenshotImageEditor/Interop/IAppVisibility.cs create mode 100644 GreenshotImageEditor/Interop/IDispatch.cs create mode 100644 GreenshotImageEditor/Interop/IOleCommandTarget.cs create mode 100644 GreenshotImageEditor/Interop/IOleWindow.cs create mode 100644 GreenshotImageEditor/Interop/IServiceProvider.cs create mode 100644 GreenshotImageEditor/Language.cs create mode 100644 GreenshotImageEditor/Memento/AddElementMemento.cs create mode 100644 GreenshotImageEditor/Memento/ChangeFieldHolderMemento.cs create mode 100644 GreenshotImageEditor/Memento/DeleteElementMemento.cs create mode 100644 GreenshotImageEditor/Memento/DrawableContainerBoundsChangeMemento.cs create mode 100644 GreenshotImageEditor/Memento/IMemento.cs create mode 100644 GreenshotImageEditor/Memento/SurfaceBackgroundChangeMemento.cs create mode 100644 GreenshotImageEditor/Memento/TextChangeMemento.cs create mode 100644 GreenshotImageEditor/Properties/AssemblyInfo.cs create mode 100644 GreenshotImageEditor/UnmanagedHelpers/DWM.cs create mode 100644 GreenshotImageEditor/UnmanagedHelpers/Enumerations.cs create mode 100644 GreenshotImageEditor/UnmanagedHelpers/GDI32.cs create mode 100644 GreenshotImageEditor/UnmanagedHelpers/GDIplus.cs create mode 100644 GreenshotImageEditor/UnmanagedHelpers/Kernel32.cs create mode 100644 GreenshotImageEditor/UnmanagedHelpers/PsAPI.cs create mode 100644 GreenshotImageEditor/UnmanagedHelpers/Shell32.cs create mode 100644 GreenshotImageEditor/UnmanagedHelpers/Structs.cs create mode 100644 GreenshotImageEditor/UnmanagedHelpers/User32.cs create mode 100644 GreenshotImageEditor/UnmanagedHelpers/Win32Errors.cs create mode 100644 GreenshotImageEditor/UnmanagedHelpers/WinMM.cs create mode 100644 GreenshotImageEditor/icons/applicationIcon/16.ico create mode 100644 GreenshotImageEditor/icons/applicationIcon/16.png create mode 100644 GreenshotImageEditor/icons/applicationIcon/32.ico create mode 100644 GreenshotImageEditor/icons/applicationIcon/32.png create mode 100644 GreenshotImageEditor/icons/applicationIcon/48.ico create mode 100644 GreenshotImageEditor/icons/applicationIcon/48.png create mode 100644 GreenshotImageEditor/icons/applicationIcon/90.png create mode 100644 GreenshotImageEditor/icons/applicationIcon/icon.ico create mode 100644 GreenshotImageEditor/icons/arrow_redo.png create mode 100644 GreenshotImageEditor/icons/arrow_rollback.png create mode 100644 GreenshotImageEditor/icons/arrow_undo.png create mode 100644 GreenshotImageEditor/icons/cancel.png create mode 100644 GreenshotImageEditor/icons/cross.png create mode 100644 GreenshotImageEditor/icons/cut.png create mode 100644 GreenshotImageEditor/icons/delete.png create mode 100644 GreenshotImageEditor/icons/filter_blur.png create mode 100644 GreenshotImageEditor/icons/filter_pixelate.png create mode 100644 GreenshotImageEditor/icons/folder-open-image.png create mode 100644 GreenshotImageEditor/icons/folder_explore.png create mode 100644 GreenshotImageEditor/icons/font_color.png create mode 100644 GreenshotImageEditor/icons/freehand.png create mode 100644 GreenshotImageEditor/icons/fugue/arrow-resize.png create mode 100644 GreenshotImageEditor/icons/fugue/clipboard-paste-image.png create mode 100644 GreenshotImageEditor/icons/fugue/clipboard.png create mode 100644 GreenshotImageEditor/icons/fugue/color-swatch.png create mode 100644 GreenshotImageEditor/icons/fugue/cross.png create mode 100644 GreenshotImageEditor/icons/fugue/cursor.png create mode 100644 GreenshotImageEditor/icons/fugue/disk-black.png create mode 100644 GreenshotImageEditor/icons/fugue/drive-globe.png create mode 100644 GreenshotImageEditor/icons/fugue/edit-alignment-center.png create mode 100644 GreenshotImageEditor/icons/fugue/edit-alignment-right.png create mode 100644 GreenshotImageEditor/icons/fugue/edit-alignment.png create mode 100644 GreenshotImageEditor/icons/fugue/edit-blur.png create mode 100644 GreenshotImageEditor/icons/fugue/edit-bold.png create mode 100644 GreenshotImageEditor/icons/fugue/edit-italic.png create mode 100644 GreenshotImageEditor/icons/fugue/edit-pixelate.png create mode 100644 GreenshotImageEditor/icons/fugue/edit-underline.png create mode 100644 GreenshotImageEditor/icons/fugue/edit-vertical-alignment-middle.png create mode 100644 GreenshotImageEditor/icons/fugue/edit-vertical-alignment-top.png create mode 100644 GreenshotImageEditor/icons/fugue/edit-vertical-alignment.png create mode 100644 GreenshotImageEditor/icons/fugue/filter_base.pdn create mode 100644 GreenshotImageEditor/icons/fugue/filter_highlight_area.png create mode 100644 GreenshotImageEditor/icons/fugue/filter_highlight_grayscale.png create mode 100644 GreenshotImageEditor/icons/fugue/filter_highlight_text.png create mode 100644 GreenshotImageEditor/icons/fugue/gear.png create mode 100644 GreenshotImageEditor/icons/fugue/highlighter-color.png create mode 100644 GreenshotImageEditor/icons/fugue/highlighter-text.png create mode 100644 GreenshotImageEditor/icons/fugue/image-blur.png create mode 100644 GreenshotImageEditor/icons/fugue/image-pixelate.png create mode 100644 GreenshotImageEditor/icons/fugue/images.png create mode 100644 GreenshotImageEditor/icons/fugue/layer-shape-arrow.png create mode 100644 GreenshotImageEditor/icons/fugue/layer-shape-ellipse.png create mode 100644 GreenshotImageEditor/icons/fugue/layer-shape-line.png create mode 100644 GreenshotImageEditor/icons/fugue/layer-shape-text.png create mode 100644 GreenshotImageEditor/icons/fugue/layer-shape.png create mode 100644 GreenshotImageEditor/icons/fugue/magnifier.png create mode 100644 GreenshotImageEditor/icons/fugue/mail-open-image.png create mode 100644 GreenshotImageEditor/icons/fugue/minus-circle.png create mode 100644 GreenshotImageEditor/icons/fugue/money-coin.png create mode 100644 GreenshotImageEditor/icons/fugue/paint-can-color-bg.png create mode 100644 GreenshotImageEditor/icons/fugue/paint-can-color.png create mode 100644 GreenshotImageEditor/icons/fugue/pencil-color-bg.png create mode 100644 GreenshotImageEditor/icons/fugue/pencil-color.png create mode 100644 GreenshotImageEditor/icons/fugue/printer.png create mode 100644 GreenshotImageEditor/icons/fugue/question.png create mode 100644 GreenshotImageEditor/icons/fugue/ruler-crop.png create mode 100644 GreenshotImageEditor/icons/fugue/scissors.png create mode 100644 GreenshotImageEditor/icons/fugue/slash.png create mode 100644 GreenshotImageEditor/icons/fugue/tick.png create mode 100644 GreenshotImageEditor/icons/heart.png create mode 100644 GreenshotImageEditor/icons/help.png create mode 100644 GreenshotImageEditor/icons/highlighter.png create mode 100644 GreenshotImageEditor/icons/layer-rotate-left.png create mode 100644 GreenshotImageEditor/icons/layer-rotate.png create mode 100644 GreenshotImageEditor/icons/page_copy.png create mode 100644 GreenshotImageEditor/icons/palette.png create mode 100644 GreenshotImageEditor/icons/picture_save.png create mode 100644 GreenshotImageEditor/icons/picture_saveas.png create mode 100644 GreenshotImageEditor/icons/picture_to_clipboard.png create mode 100644 GreenshotImageEditor/icons/pipette.png create mode 100644 GreenshotImageEditor/icons/printer.png create mode 100644 GreenshotImageEditor/icons/propertyitemcontainer.gif create mode 100644 GreenshotImageEditor/icons/redo.png create mode 100644 GreenshotImageEditor/icons/ruler-crop.png create mode 100644 GreenshotImageEditor/icons/shadow.png create mode 100644 GreenshotImageEditor/icons/shape_arrow_add.png create mode 100644 GreenshotImageEditor/icons/shape_arrowheads.png create mode 100644 GreenshotImageEditor/icons/shape_copy.png create mode 100644 GreenshotImageEditor/icons/shape_ellipse_add.png create mode 100644 GreenshotImageEditor/icons/shape_ellipse_delete.png create mode 100644 GreenshotImageEditor/icons/shape_line.png create mode 100644 GreenshotImageEditor/icons/shape_paste.png create mode 100644 GreenshotImageEditor/icons/shape_square_add.png create mode 100644 GreenshotImageEditor/icons/shape_square_bordercolor.png create mode 100644 GreenshotImageEditor/icons/shape_square_delete.png create mode 100644 GreenshotImageEditor/icons/shape_square_fillcolor.png create mode 100644 GreenshotImageEditor/icons/text_bold.png create mode 100644 GreenshotImageEditor/icons/text_dropcaps.png create mode 100644 GreenshotImageEditor/icons/text_italic.png create mode 100644 GreenshotImageEditor/icons/text_underline.png create mode 100644 GreenshotImageEditor/icons/textfield_delete.png create mode 100644 GreenshotImageEditor/icons/undo.png create mode 100644 GreenshotImageEditor/icons/wand-hat.png create mode 100644 GreenshotImageEditor/icons/wrench.png create mode 100644 GreenshotImageEditor/icons/wrench_orange.png create mode 100644 GreenshotImageEditor/icons/zoom.png create mode 100644 HelpersLib/AviWriter.cs create mode 100644 HelpersLib/CLI/CLICommand.cs create mode 100644 HelpersLib/CLI/CLICommandAction.cs create mode 100644 HelpersLib/CLI/CLIHelper.cs create mode 100644 HelpersLib/CLI/CLIManager.cs create mode 100644 HelpersLib/CLI/CLIManagerRegex.cs create mode 100644 HelpersLib/CLI/CLIParser.cs create mode 100644 HelpersLib/ClipboardFormat.cs create mode 100644 HelpersLib/ColorBgra.cs create mode 100644 HelpersLib/ColorMatrixManager.cs create mode 100644 HelpersLib/Colors/CMYK.cs create mode 100644 HelpersLib/Colors/ColorBox.cs create mode 100644 HelpersLib/Colors/ColorEventHandler.cs create mode 100644 HelpersLib/Colors/ColorPicker.cs create mode 100644 HelpersLib/Colors/ColorSlider.cs create mode 100644 HelpersLib/Colors/ColorUserControl.cs create mode 100644 HelpersLib/Colors/DialogColor.Designer.cs create mode 100644 HelpersLib/Colors/DialogColor.cs create mode 100644 HelpersLib/Colors/DialogColor.resx create mode 100644 HelpersLib/Colors/HSB.cs create mode 100644 HelpersLib/Colors/MyColor.cs create mode 100644 HelpersLib/Colors/RGBA.cs create mode 100644 HelpersLib/Cryptographic/Crc32.cs create mode 100644 HelpersLib/Cryptographic/HashCheck.cs create mode 100644 HelpersLib/Cryptographic/Translator.cs create mode 100644 HelpersLib/Cryptographic/TranslatorHelper.cs create mode 100644 HelpersLib/CursorData.cs create mode 100644 HelpersLib/DebugHelper.cs create mode 100644 HelpersLib/DebugTimer.cs create mode 100644 HelpersLib/Enums.cs create mode 100644 HelpersLib/Extensions/EnumExtensions.cs create mode 100644 HelpersLib/Extensions/Extensions.cs create mode 100644 HelpersLib/Extensions/GraphicsExtensions.cs create mode 100644 HelpersLib/Extensions/GraphicsPathExtensions.cs create mode 100644 HelpersLib/Extensions/ObjectExtensions.cs create mode 100644 HelpersLib/Extensions/StreamExtensions.cs create mode 100644 HelpersLib/Extensions/StringExtensions.cs create mode 100644 HelpersLib/Extensions/XMLExtensions.cs create mode 100644 HelpersLib/ExternalProgram.cs create mode 100644 HelpersLib/FastDateTime.cs create mode 100644 HelpersLib/FileDownloader.cs create mode 100644 HelpersLib/FileNameEditors/CssFileNameEditor.cs create mode 100644 HelpersLib/FileNameEditors/ExeFileNameEditor.cs create mode 100644 HelpersLib/FixedSizedQueue.cs create mode 100644 HelpersLib/Forms/ClipboardContentViewer.Designer.cs create mode 100644 HelpersLib/Forms/ClipboardContentViewer.cs create mode 100644 HelpersLib/Forms/ClipboardContentViewer.resx create mode 100644 HelpersLib/Forms/ClipboardFormatForm.Designer.cs create mode 100644 HelpersLib/Forms/ClipboardFormatForm.cs create mode 100644 HelpersLib/Forms/ClipboardFormatForm.resx create mode 100644 HelpersLib/Forms/DebugForm.Designer.cs create mode 100644 HelpersLib/Forms/DebugForm.cs create mode 100644 HelpersLib/Forms/DebugForm.resx create mode 100644 HelpersLib/Forms/ErrorForm.Designer.cs create mode 100644 HelpersLib/Forms/ErrorForm.cs create mode 100644 HelpersLib/Forms/ErrorForm.resx create mode 100644 HelpersLib/Forms/HashCheckForm.Designer.cs create mode 100644 HelpersLib/Forms/HashCheckForm.cs create mode 100644 HelpersLib/Forms/HashCheckForm.resx create mode 100644 HelpersLib/Forms/ImageViewer.cs create mode 100644 HelpersLib/Forms/InputBox.cs create mode 100644 HelpersLib/Forms/InputBox.resx create mode 100644 HelpersLib/Forms/MyMessageBox.cs create mode 100644 HelpersLib/Forms/TrayForm.cs create mode 100644 HelpersLib/GIF/AnimatedGif.cs create mode 100644 HelpersLib/GIF/GifClass.cs create mode 100644 HelpersLib/GIF/GifCreator.cs create mode 100644 HelpersLib/GIF/GrayscaleQuantizer.cs create mode 100644 HelpersLib/GIF/OctreeQuantizer.cs create mode 100644 HelpersLib/GIF/PaletteQuantizer.cs create mode 100644 HelpersLib/GIF/Quantizer.cs create mode 100644 HelpersLib/Helpers/CaptureHelpers.cs create mode 100644 HelpersLib/Helpers/ClipboardHelpers.cs create mode 100644 HelpersLib/Helpers/ColorHelpers.cs create mode 100644 HelpersLib/Helpers/Helpers.cs create mode 100644 HelpersLib/Helpers/ImageHelpers.cs create mode 100644 HelpersLib/Helpers/RegistryHelpers.cs create mode 100644 HelpersLib/Helpers/ShortcutHelpers.cs create mode 100644 HelpersLib/HelpersLib.csproj create mode 100644 HelpersLib/Links.cs create mode 100644 HelpersLib/Logger.cs create mode 100644 HelpersLib/NameParser.cs create mode 100644 HelpersLib/Native/FolderSelectDialog.cs create mode 100644 HelpersLib/Native/NativeEnums.cs create mode 100644 HelpersLib/Native/NativeMethods.cs create mode 100644 HelpersLib/Native/NativeMethods_Helpers.cs create mode 100644 HelpersLib/Native/NativeStructs.cs create mode 100644 HelpersLib/Native/TaskbarManager.cs create mode 100644 HelpersLib/Printer/PrintForm.Designer.cs create mode 100644 HelpersLib/Printer/PrintForm.cs create mode 100644 HelpersLib/Printer/PrintForm.resx create mode 100644 HelpersLib/Printer/PrintHelper.cs create mode 100644 HelpersLib/Printer/PrintSettings.cs create mode 100644 HelpersLib/Printer/PrintTextForm.Designer.cs create mode 100644 HelpersLib/Printer/PrintTextForm.cs create mode 100644 HelpersLib/Printer/PrintTextForm.resx create mode 100644 HelpersLib/Printer/PrintTextHelper.cs create mode 100644 HelpersLib/Properties/AssemblyInfo.cs create mode 100644 HelpersLib/Properties/Resources.Designer.cs create mode 100644 HelpersLib/Properties/Resources.resx create mode 100644 HelpersLib/Resources/Icon.png create mode 100644 HelpersLib/Resources/Loading.gif create mode 100644 HelpersLib/Resources/ShareX_Icon.ico create mode 100644 HelpersLib/Resources/crosshair.cur create mode 100644 HelpersLib/SettingsBase.cs create mode 100644 HelpersLib/SettingsHelper.cs create mode 100644 HelpersLib/SingleInstanceApplication/ApplicationInstanceManager.cs create mode 100644 HelpersLib/SingleInstanceApplication/InstanceProxy.cs create mode 100644 HelpersLib/StringLineReader.cs create mode 100644 HelpersLib/ThreadWorker.cs create mode 100644 HelpersLib/Tokenizer/BetweenTagsResult.cs create mode 100644 HelpersLib/Tokenizer/Token.cs create mode 100644 HelpersLib/Tokenizer/Tokenizer.cs create mode 100644 HelpersLib/UnsafeBitmap.cs create mode 100644 HelpersLib/UpdateChecker/UpdateChecker.cs create mode 100644 HelpersLib/UpdateChecker/UpdateCheckerLabel.Designer.cs create mode 100644 HelpersLib/UpdateChecker/UpdateCheckerLabel.cs create mode 100644 HelpersLib/UpdateChecker/UpdateCheckerLabel.resx create mode 100644 HelpersLib/UpdateChecker/UpdateInfo.cs create mode 100644 HelpersLib/UpdateChecker/UpdaterForm.Designer.cs create mode 100644 HelpersLib/UpdateChecker/UpdaterForm.cs create mode 100644 HelpersLib/UpdateChecker/UpdaterForm.resx create mode 100644 HelpersLib/UserControls/BlackStyle/MyButton.cs create mode 100644 HelpersLib/UserControls/BlackStyle/MyCheckBox.cs create mode 100644 HelpersLib/UserControls/BlackStyle/MyLabel.cs create mode 100644 HelpersLib/UserControls/BlackStyle/MyProgressBar.cs create mode 100644 HelpersLib/UserControls/Canvas.cs create mode 100644 HelpersLib/UserControls/MenuButton.cs create mode 100644 HelpersLib/UserControls/MyListView.cs create mode 100644 HelpersLib/UserControls/MyPictureBox.Designer.cs create mode 100644 HelpersLib/UserControls/MyPictureBox.cs create mode 100644 HelpersLib/UserControls/MyPictureBox.resx create mode 100644 HelpersLib/UserControls/NoFocusBorderButton.cs create mode 100644 HelpersLib/UserControls/SplitContainerCustomSplitter.cs create mode 100644 HelpersLib/UserControls/ToolStripRadioButtonMenuItem.cs create mode 100644 HelpersLib/WindowState.cs create mode 100644 HelpersLib/XmlColor.cs create mode 100644 HelpersLib/XmlFont.cs create mode 100644 HelpersLib/packages.config create mode 100644 HistoryLib/Enums.cs create mode 100644 HistoryLib/HistoryForm.Designer.cs create mode 100644 HistoryLib/HistoryForm.cs create mode 100644 HistoryLib/HistoryForm.resx create mode 100644 HistoryLib/HistoryItem.cs create mode 100644 HistoryLib/HistoryItemInfoForm.Designer.cs create mode 100644 HistoryLib/HistoryItemInfoForm.cs create mode 100644 HistoryLib/HistoryItemInfoForm.resx create mode 100644 HistoryLib/HistoryItemManager.cs create mode 100644 HistoryLib/HistoryItemManager_ContextMenu.cs create mode 100644 HistoryLib/HistoryItemOld.cs create mode 100644 HistoryLib/HistoryLib.csproj create mode 100644 HistoryLib/HistoryManager.cs create mode 100644 HistoryLib/ImageHistoryForm.Designer.cs create mode 100644 HistoryLib/ImageHistoryForm.cs create mode 100644 HistoryLib/ImageHistoryForm.resx create mode 100644 HistoryLib/Properties/AssemblyInfo.cs create mode 100644 HistoryLib/Properties/Resources.Designer.cs create mode 100644 HistoryLib/Properties/Resources.resx create mode 100644 HistoryLib/Resources/ShareX_Icon.ico create mode 100644 HistoryLib/UserControls/ObjectListView.cs create mode 100644 HistoryLib/XMLManager.cs create mode 100644 HistoryLib/XMLManagerOld.cs create mode 100644 HistoryLib/packages.config create mode 100644 ImageEffectsLib/Adjustments/Alpha.cs create mode 100644 ImageEffectsLib/Adjustments/Brightness.cs create mode 100644 ImageEffectsLib/Adjustments/Colorize.cs create mode 100644 ImageEffectsLib/Adjustments/Contrast.cs create mode 100644 ImageEffectsLib/Adjustments/Gamma.cs create mode 100644 ImageEffectsLib/Adjustments/Grayscale.cs create mode 100644 ImageEffectsLib/Adjustments/Hue.cs create mode 100644 ImageEffectsLib/Adjustments/Inverse.cs create mode 100644 ImageEffectsLib/Adjustments/Matrix.cs create mode 100644 ImageEffectsLib/Adjustments/Saturation.cs create mode 100644 ImageEffectsLib/Filters/Blur.cs create mode 100644 ImageEffectsLib/Filters/Pixelate.cs create mode 100644 ImageEffectsLib/Filters/Reflection.cs create mode 100644 ImageEffectsLib/Filters/Shadow.cs create mode 100644 ImageEffectsLib/Filters/TornEdge.cs create mode 100644 ImageEffectsLib/ImageEffect.cs create mode 100644 ImageEffectsLib/ImageEffectManager.cs create mode 100644 ImageEffectsLib/ImageEffectsForm.Designer.cs create mode 100644 ImageEffectsLib/ImageEffectsForm.cs create mode 100644 ImageEffectsLib/ImageEffectsForm.resx create mode 100644 ImageEffectsLib/ImageEffectsLib.csproj create mode 100644 ImageEffectsLib/Manipulations/Background.cs create mode 100644 ImageEffectsLib/Manipulations/BackgroundGradient.cs create mode 100644 ImageEffectsLib/Manipulations/Border.cs create mode 100644 ImageEffectsLib/Manipulations/Canvas.cs create mode 100644 ImageEffectsLib/Manipulations/Flip.cs create mode 100644 ImageEffectsLib/Manipulations/Resize.cs create mode 100644 ImageEffectsLib/Manipulations/Rotate.cs create mode 100644 ImageEffectsLib/Manipulations/Scale.cs create mode 100644 ImageEffectsLib/Manipulations/Skew.cs create mode 100644 ImageEffectsLib/Properties/AssemblyInfo.cs create mode 100644 ImageEffectsLib/Watermark/GradientData.cs create mode 100644 ImageEffectsLib/Watermark/GradientMaker.Designer.cs create mode 100644 ImageEffectsLib/Watermark/GradientMaker.cs create mode 100644 ImageEffectsLib/Watermark/GradientMaker.resx create mode 100644 ImageEffectsLib/Watermark/GradientStop.cs create mode 100644 ImageEffectsLib/Watermark/WatermarkConfig.cs create mode 100644 ImageEffectsLib/Watermark/WatermarkForm.Designer.cs create mode 100644 ImageEffectsLib/Watermark/WatermarkForm.cs create mode 100644 ImageEffectsLib/Watermark/WatermarkForm.resx create mode 100644 ImageEffectsLib/Watermark/WatermarkManager.cs create mode 100644 IndexerLib/Enums.cs create mode 100644 IndexerLib/FolderInfo.cs create mode 100644 IndexerLib/HtmlHelper.cs create mode 100644 IndexerLib/Indexer.cs create mode 100644 IndexerLib/IndexerDefault.css create mode 100644 IndexerLib/IndexerHtml.cs create mode 100644 IndexerLib/IndexerLib.csproj create mode 100644 IndexerLib/IndexerSettings.cs create mode 100644 IndexerLib/IndexerText.cs create mode 100644 IndexerLib/IndexerXml.cs create mode 100644 IndexerLib/Properties/AssemblyInfo.cs create mode 100644 IndexerLib/Properties/Resources.Designer.cs create mode 100644 IndexerLib/Properties/Resources.resx create mode 100644 IndexerLib/Resources/doctype_xhtml.txt create mode 100644 IndexerLib/Resources/valid_xhtml.txt create mode 100644 IndexerLib/packages.config create mode 100644 Lib/Starksoft.Net.Ftp.dll create mode 100644 Lib/Starksoft.Net.Proxy.dll create mode 100644 ScreenCapture/AVICache.cs create mode 100644 ScreenCapture/Enums.cs create mode 100644 ScreenCapture/Forms/DiamondRegion.cs create mode 100644 ScreenCapture/Forms/EllipseRegion.cs create mode 100644 ScreenCapture/Forms/FreeHandRegion.cs create mode 100644 ScreenCapture/Forms/PolygonRegion.cs create mode 100644 ScreenCapture/Forms/RectangleLight.cs create mode 100644 ScreenCapture/Forms/RectangleRegion.cs create mode 100644 ScreenCapture/Forms/RegionCapturePreview.Designer.cs create mode 100644 ScreenCapture/Forms/RegionCapturePreview.cs create mode 100644 ScreenCapture/Forms/RegionCapturePreview.resx create mode 100644 ScreenCapture/Forms/RoundedRectangleRegion.cs create mode 100644 ScreenCapture/Forms/Surface.cs create mode 100644 ScreenCapture/Forms/TriangleRegion.cs create mode 100644 ScreenCapture/LocationInfo.cs create mode 100644 ScreenCapture/Properties/AssemblyInfo.cs create mode 100644 ScreenCapture/Properties/Resources.Designer.cs create mode 100644 ScreenCapture/Properties/Resources.resx create mode 100644 ScreenCapture/RegionHelpers/AreaManager.cs create mode 100644 ScreenCapture/RegionHelpers/DrawObject.cs create mode 100644 ScreenCapture/RegionHelpers/InputManager.cs create mode 100644 ScreenCapture/RegionHelpers/MouseState.cs create mode 100644 ScreenCapture/RegionHelpers/NodeObject.cs create mode 100644 ScreenCapture/RegionHelpers/ResizeManager.cs create mode 100644 ScreenCapture/RegionHelpers/ShapeCaptureHelpers.cs create mode 100644 ScreenCapture/RegionHelpers/SurfaceOptions.cs create mode 100644 ScreenCapture/Resources/Crosshair.cur create mode 100644 ScreenCapture/ScreenCapture.csproj create mode 100644 ScreenCapture/ScreenRecorder.cs create mode 100644 ScreenCapture/ScreenRecorderCache.cs create mode 100644 ScreenCapture/Screenshot.cs create mode 100644 ScreenCapture/Screenshot_Transparent.cs create mode 100644 ScreenCapture/WindowInfo.cs create mode 100644 ScreenCapture/WindowsList.cs create mode 100644 ScreenCapture/WindowsListAdvanced.cs create mode 100644 ScreenCapture/packages.config create mode 100644 ShareX Setup.cmd create mode 100644 ShareX Setup.iss create mode 100644 ShareX.sln create mode 100644 ShareX.sln.DotSettings create mode 100644 ShareX/ApplicationConfig.cs create mode 100644 ShareX/Controls/HotkeyManagerControl.Designer.cs create mode 100644 ShareX/Controls/HotkeyManagerControl.cs create mode 100644 ShareX/Controls/HotkeyManagerControl.resx create mode 100644 ShareX/Controls/HotkeySelectionControl.Designer.cs create mode 100644 ShareX/Controls/HotkeySelectionControl.cs create mode 100644 ShareX/Controls/HotkeySelectionControl.resx create mode 100644 ShareX/Enums.cs create mode 100644 ShareX/Forms/AboutForm.Designer.cs create mode 100644 ShareX/Forms/AboutForm.cs create mode 100644 ShareX/Forms/AboutForm.resx create mode 100644 ShareX/Forms/AfterCaptureForm.Designer.cs create mode 100644 ShareX/Forms/AfterCaptureForm.cs create mode 100644 ShareX/Forms/AfterCaptureForm.resx create mode 100644 ShareX/Forms/AfterUploadForm.cs create mode 100644 ShareX/Forms/AfterUploadForm.designer.cs create mode 100644 ShareX/Forms/AfterUploadForm.resx create mode 100644 ShareX/Forms/AutoCaptureForm.Designer.cs create mode 100644 ShareX/Forms/AutoCaptureForm.cs create mode 100644 ShareX/Forms/AutoCaptureForm.resx create mode 100644 ShareX/Forms/ExternalProgramForm.Designer.cs create mode 100644 ShareX/Forms/ExternalProgramForm.cs create mode 100644 ShareX/Forms/ExternalProgramForm.resx create mode 100644 ShareX/Forms/HotkeyForm.cs create mode 100644 ShareX/Forms/HotkeySettingsForm.Designer.cs create mode 100644 ShareX/Forms/HotkeySettingsForm.cs create mode 100644 ShareX/Forms/HotkeySettingsForm.resx create mode 100644 ShareX/Forms/MainForm.Designer.cs create mode 100644 ShareX/Forms/MainForm.cs create mode 100644 ShareX/Forms/MainForm.resx create mode 100644 ShareX/Forms/MainForm_Capture.cs create mode 100644 ShareX/Forms/ScreenColorPicker.Designer.cs create mode 100644 ShareX/Forms/ScreenColorPicker.cs create mode 100644 ShareX/Forms/ScreenColorPicker.resx create mode 100644 ShareX/Forms/ScreenRecordForm.cs create mode 100644 ShareX/Forms/ScreenRegionForm.Designer.cs create mode 100644 ShareX/Forms/ScreenRegionForm.cs create mode 100644 ShareX/Forms/ScreenRegionForm.resx create mode 100644 ShareX/Forms/SettingsForm.Designer.cs create mode 100644 ShareX/Forms/SettingsForm.cs create mode 100644 ShareX/Forms/SettingsForm.resx create mode 100644 ShareX/Forms/TaskSettingsForm.Designer.cs create mode 100644 ShareX/Forms/TaskSettingsForm.cs create mode 100644 ShareX/Forms/TaskSettingsForm.resx create mode 100644 ShareX/Forms/UploadTestForm.Designer.cs create mode 100644 ShareX/Forms/UploadTestForm.cs create mode 100644 ShareX/Forms/UploadTestForm.resx create mode 100644 ShareX/Forms/WatchFolderForm.Designer.cs create mode 100644 ShareX/Forms/WatchFolderForm.cs create mode 100644 ShareX/Forms/WatchFolderForm.resx create mode 100644 ShareX/HotkeyInfo.cs create mode 100644 ShareX/HotkeyManager.cs create mode 100644 ShareX/HotkeySettings.cs create mode 100644 ShareX/HotkeysConfig.cs create mode 100644 ShareX/ImageData.cs create mode 100644 ShareX/ImageTag.cs create mode 100644 ShareX/Program.cs create mode 100644 ShareX/Properties/AssemblyInfo.cs create mode 100644 ShareX/Properties/Resources.Designer.cs create mode 100644 ShareX/Properties/Resources.resx create mode 100644 ShareX/Resources/Camera.wav create mode 100644 ShareX/Resources/Ellipse.png create mode 100644 ShareX/Resources/FreeHand.png create mode 100644 ShareX/Resources/Fullscreen.png create mode 100644 ShareX/Resources/Polygon.png create mode 100644 ShareX/Resources/Rectangle.png create mode 100644 ShareX/Resources/RoundedRectangle.png create mode 100644 ShareX/Resources/ShareX_Icon.ico create mode 100644 ShareX/Resources/ShareX_Logo.png create mode 100644 ShareX/Resources/Triangle.png create mode 100644 ShareX/Resources/Window.png create mode 100644 ShareX/Resources/address-book-blue.png create mode 100644 ShareX/Resources/application--pencil.png create mode 100644 ShareX/Resources/application-block.png create mode 100644 ShareX/Resources/application-blue.png create mode 100644 ShareX/Resources/application-browser.png create mode 100644 ShareX/Resources/application-form.png create mode 100644 ShareX/Resources/application-icon-large.png create mode 100644 ShareX/Resources/application-monitor.png create mode 100644 ShareX/Resources/application-task.png create mode 100644 ShareX/Resources/au.png create mode 100644 ShareX/Resources/block.png create mode 100644 ShareX/Resources/bug.png create mode 100644 ShareX/Resources/camcorder--pencil.png create mode 100644 ShareX/Resources/camcorder-image.png create mode 100644 ShareX/Resources/camera.png create mode 100644 ShareX/Resources/clipboard--plus.png create mode 100644 ShareX/Resources/clock--plus.png create mode 100644 ShareX/Resources/color.png create mode 100644 ShareX/Resources/control-record-yellow.png create mode 100644 ShareX/Resources/control-record.png create mode 100644 ShareX/Resources/cross-button.png create mode 100644 ShareX/Resources/diamond.png create mode 100644 ShareX/Resources/document--arrow.png create mode 100644 ShareX/Resources/document-copy.png create mode 100644 ShareX/Resources/drive-globe.png create mode 100644 ShareX/Resources/edit-scale.png create mode 100644 ShareX/Resources/folder--plus.png create mode 100644 ShareX/Resources/folder-open-image.png create mode 100644 ShareX/Resources/folder-tree.png create mode 100644 ShareX/Resources/fr.png create mode 100644 ShareX/Resources/gear.png create mode 100644 ShareX/Resources/globe--pencil.png create mode 100644 ShareX/Resources/globe-share.png create mode 100644 ShareX/Resources/hammer--arrow.png create mode 100644 ShareX/Resources/hammer--pencil.png create mode 100644 ShareX/Resources/hammer-screwdriver.png create mode 100644 ShareX/Resources/hammer.png create mode 100644 ShareX/Resources/image-export.png create mode 100644 ShareX/Resources/image-saturation.png create mode 100644 ShareX/Resources/image.png create mode 100644 ShareX/Resources/keyboard--pencil.png create mode 100644 ShareX/Resources/layer-shape-curve.png create mode 100644 ShareX/Resources/layer-shape-ellipse.png create mode 100644 ShareX/Resources/layer-shape-polygon.png create mode 100644 ShareX/Resources/layer-shape-round.png create mode 100644 ShareX/Resources/layer-shape.png create mode 100644 ShareX/Resources/layer.png create mode 100644 ShareX/Resources/layers-arrange.png create mode 100644 ShareX/Resources/layers-ungroup.png create mode 100644 ShareX/Resources/monitor.png create mode 100644 ShareX/Resources/navigation-000-button.png create mode 100644 ShareX/Resources/navigation-090-button.png create mode 100644 ShareX/Resources/notebook.png create mode 100644 ShareX/Resources/pencil.png create mode 100644 ShareX/Resources/pipette.png create mode 100644 ShareX/Resources/present.png create mode 100644 ShareX/Resources/question-button.png create mode 100644 ShareX/Resources/steam.ico create mode 100644 ShareX/Resources/tick-button.png create mode 100644 ShareX/Resources/toolbox.png create mode 100644 ShareX/Resources/tr.png create mode 100644 ShareX/Resources/upload-cloud.png create mode 100644 ShareX/ScreenRegionManager.cs create mode 100644 ShareX/ShareX.csproj create mode 100644 ShareX/TaskHelpers.cs create mode 100644 ShareX/TaskInfo.cs create mode 100644 ShareX/TaskManager.cs create mode 100644 ShareX/TaskSettings.cs create mode 100644 ShareX/UploadInfoManager.cs create mode 100644 ShareX/UploadInfoParser.cs create mode 100644 ShareX/UploadInfoStatus.cs create mode 100644 ShareX/UploadManager.cs create mode 100644 ShareX/UploadTask.cs create mode 100644 ShareX/WatchFolder.cs create mode 100644 ShareX/WatchFolderManager.cs create mode 100644 ShareX/WatchFolderSettings.cs create mode 100644 ShareX/app.manifest create mode 100644 ShareX/packages.config create mode 100644 Update.xml create mode 100644 UploadersLib/ApiKeys/ApiKeys.cs create mode 100644 UploadersLib/ApiKeys/ApiKeysConfig.cs create mode 100644 UploadersLib/ApiKeys/ApiKeysUI.Designer.cs create mode 100644 UploadersLib/ApiKeys/ApiKeysUI.cs create mode 100644 UploadersLib/ApiKeys/ApiKeysUI.resx create mode 100644 UploadersLib/ApiKeys/jira_sharex.pfx create mode 100644 UploadersLib/Config/GoogleTranslatorConfig.cs create mode 100644 UploadersLib/Config/UploadersConfig.cs create mode 100644 UploadersLib/Enums.cs create mode 100644 UploadersLib/FTPClient/FTPClientForm.Designer.cs create mode 100644 UploadersLib/FTPClient/FTPClientForm.cs create mode 100644 UploadersLib/FTPClient/FTPClientForm.resx create mode 100644 UploadersLib/FTPClient/IconHelper.cs create mode 100644 UploadersLib/FTPClient/IconListManager.cs create mode 100644 UploadersLib/FTPClient/ListViewEx.cs create mode 100644 UploadersLib/FTPClient/LoginDialog.Designer.cs create mode 100644 UploadersLib/FTPClient/LoginDialog.cs create mode 100644 UploadersLib/FTPClient/LoginDialog.resx create mode 100644 UploadersLib/Favicons/Box.ico create mode 100644 UploadersLib/Favicons/Dropbox.ico create mode 100644 UploadersLib/Favicons/Flickr.ico create mode 100644 UploadersLib/Favicons/Gett.ico create mode 100644 UploadersLib/Favicons/Google.ico create mode 100644 UploadersLib/Favicons/GoogleDrive.ico create mode 100644 UploadersLib/Favicons/ImageShack.ico create mode 100644 UploadersLib/Favicons/Imgur.ico create mode 100644 UploadersLib/Favicons/Localhostr.ico create mode 100644 UploadersLib/Favicons/Mega.ico create mode 100644 UploadersLib/Favicons/Minus.ico create mode 100644 UploadersLib/Favicons/Pastebin.ico create mode 100644 UploadersLib/Favicons/Photobucket.ico create mode 100644 UploadersLib/Favicons/Picasa.ico create mode 100644 UploadersLib/Favicons/RapidShare.ico create mode 100644 UploadersLib/Favicons/SendSpace.ico create mode 100644 UploadersLib/Favicons/TinyPic.ico create mode 100644 UploadersLib/Favicons/TwitPic.ico create mode 100644 UploadersLib/Favicons/TwitSnaps.ico create mode 100644 UploadersLib/Favicons/Twitter.ico create mode 100644 UploadersLib/Favicons/YFrog.ico create mode 100644 UploadersLib/Favicons/jira.png create mode 100644 UploadersLib/FileUploader.cs create mode 100644 UploadersLib/FileUploaders/Box.cs create mode 100644 UploadersLib/FileUploaders/CustomFileUploader.cs create mode 100644 UploadersLib/FileUploaders/DropIO.cs create mode 100644 UploadersLib/FileUploaders/Dropbox.cs create mode 100644 UploadersLib/FileUploaders/Email.cs create mode 100644 UploadersLib/FileUploaders/FTP.cs create mode 100644 UploadersLib/FileUploaders/FTP/FTPAccount.cs create mode 100644 UploadersLib/FileUploaders/FTP/FTPAccountManager.cs create mode 100644 UploadersLib/FileUploaders/FTP/FTPAdapter.cs create mode 100644 UploadersLib/FileUploaders/FTP/FTPHelpers.cs create mode 100644 UploadersLib/FileUploaders/FTPUploader.cs create mode 100644 UploadersLib/FileUploaders/FileBin.cs create mode 100644 UploadersLib/FileUploaders/FileSonic.cs create mode 100644 UploadersLib/FileUploaders/Ge_tt.cs create mode 100644 UploadersLib/FileUploaders/GoogleDrive.cs create mode 100644 UploadersLib/FileUploaders/Hostr.cs create mode 100644 UploadersLib/FileUploaders/Jira.cs create mode 100644 UploadersLib/FileUploaders/Localhost/LocalhostAccount.cs create mode 100644 UploadersLib/FileUploaders/Mega.cs create mode 100644 UploadersLib/FileUploaders/Minus.cs create mode 100644 UploadersLib/FileUploaders/RapidShare.cs create mode 100644 UploadersLib/FileUploaders/SFTP.cs create mode 100644 UploadersLib/FileUploaders/SendSpace.cs create mode 100644 UploadersLib/FileUploaders/SendSpaceManager.cs create mode 100644 UploadersLib/FileUploaders/ShareCX.cs create mode 100644 UploadersLib/FileUploaders/SharedFolderUploader.cs create mode 100644 UploadersLib/GUI/AccountTypeControl.Designer.cs create mode 100644 UploadersLib/GUI/AccountTypeControl.cs create mode 100644 UploadersLib/GUI/AccountTypeControl.resx create mode 100644 UploadersLib/GUI/AccountsControl.Designer.cs create mode 100644 UploadersLib/GUI/AccountsControl.cs create mode 100644 UploadersLib/GUI/AccountsControl.resx create mode 100644 UploadersLib/GUI/DropboxFilesForm.Designer.cs create mode 100644 UploadersLib/GUI/DropboxFilesForm.cs create mode 100644 UploadersLib/GUI/DropboxFilesForm.resx create mode 100644 UploadersLib/GUI/EmailForm.Designer.cs create mode 100644 UploadersLib/GUI/EmailForm.cs create mode 100644 UploadersLib/GUI/EmailForm.resx create mode 100644 UploadersLib/GUI/GoogleTranslateGUI.Designer.cs create mode 100644 UploadersLib/GUI/GoogleTranslateGUI.cs create mode 100644 UploadersLib/GUI/GoogleTranslateGUI.resx create mode 100644 UploadersLib/GUI/GoogleTranslateGUIHelper.cs create mode 100644 UploadersLib/GUI/GoogleTranslateGUIWorker.cs create mode 100644 UploadersLib/GUI/JiraUpload.Designer.cs create mode 100644 UploadersLib/GUI/JiraUpload.cs create mode 100644 UploadersLib/GUI/JiraUpload.resx create mode 100644 UploadersLib/GUI/OAuth2Control.Designer.cs create mode 100644 UploadersLib/GUI/OAuth2Control.cs create mode 100644 UploadersLib/GUI/OAuth2Control.resx create mode 100644 UploadersLib/GUI/ResponseForm.Designer.cs create mode 100644 UploadersLib/GUI/ResponseForm.cs create mode 100644 UploadersLib/GUI/ResponseForm.resx create mode 100644 UploadersLib/GUI/TwitterMsg.Designer.cs create mode 100644 UploadersLib/GUI/TwitterMsg.cs create mode 100644 UploadersLib/GUI/TwitterMsg.resx create mode 100644 UploadersLib/GUI/UploadersConfigForm.Designer.cs create mode 100644 UploadersLib/GUI/UploadersConfigForm.cs create mode 100644 UploadersLib/GUI/UploadersConfigForm.resx create mode 100644 UploadersLib/GUI/UploadersConfigFormGUI.cs create mode 100644 UploadersLib/GUI/UploadersConfigFormHelper.cs create mode 100644 UploadersLib/GUI/UserPassBox.Designer.cs create mode 100644 UploadersLib/GUI/UserPassBox.cs create mode 100644 UploadersLib/GUI/UserPassBox.resx create mode 100644 UploadersLib/HelperClasses/AccountInfo.cs create mode 100644 UploadersLib/HelperClasses/Argument.cs create mode 100644 UploadersLib/HelperClasses/CertFileNameEditor.cs create mode 100644 UploadersLib/HelperClasses/CustomUploaderItem.cs create mode 100644 UploadersLib/HelperClasses/CustomUploaderParse.cs create mode 100644 UploadersLib/HelperClasses/ImageListManager.cs create mode 100644 UploadersLib/HelperClasses/JSONHelper.cs create mode 100644 UploadersLib/HelperClasses/KeyFileNameEditor.cs create mode 100644 UploadersLib/HelperClasses/OAuth/IOAuth.cs create mode 100644 UploadersLib/HelperClasses/OAuth/IOAuth2.cs create mode 100644 UploadersLib/HelperClasses/OAuth/OAuth2Info.cs create mode 100644 UploadersLib/HelperClasses/OAuth/OAuth2Token.cs create mode 100644 UploadersLib/HelperClasses/OAuth/OAuthInfo.cs create mode 100644 UploadersLib/HelperClasses/OAuth/OAuthManager.cs create mode 100644 UploadersLib/HelperClasses/ProgressManager.cs create mode 100644 UploadersLib/HelperClasses/TCPClient.cs create mode 100644 UploadersLib/ImageUploader.cs create mode 100644 UploadersLib/ImageUploaders/CustomImageUploader.cs create mode 100644 UploadersLib/ImageUploaders/FlickrUploader.cs create mode 100644 UploadersLib/ImageUploaders/ImageBin.cs create mode 100644 UploadersLib/ImageUploaders/ImageShackUploader.cs create mode 100644 UploadersLib/ImageUploaders/Img1Uploader.cs create mode 100644 UploadersLib/ImageUploaders/Imgur_v2.cs create mode 100644 UploadersLib/ImageUploaders/Imgur_v3.cs create mode 100644 UploadersLib/ImageUploaders/ImmioUploader.cs create mode 100644 UploadersLib/ImageUploaders/Photobucket.cs create mode 100644 UploadersLib/ImageUploaders/Picasa.cs create mode 100644 UploadersLib/ImageUploaders/TinyPicUploader.cs create mode 100644 UploadersLib/ImageUploaders/TwitPicUploader.cs create mode 100644 UploadersLib/ImageUploaders/TwitSnapsUploader.cs create mode 100644 UploadersLib/ImageUploaders/UploadScreenshot.cs create mode 100644 UploadersLib/ImageUploaders/YfrogUploader.cs create mode 100644 UploadersLib/OtherServices/GoogleTranslate.cs create mode 100644 UploadersLib/OtherServices/Lyricsfly.cs create mode 100644 UploadersLib/Properties/AssemblyInfo.cs create mode 100644 UploadersLib/Properties/Resources.Designer.cs create mode 100644 UploadersLib/Properties/Resources.resx create mode 100644 UploadersLib/ProxyInfo.cs create mode 100644 UploadersLib/Resources/ShareX_Icon.ico create mode 100644 UploadersLib/Resources/ShareX_Logo.png create mode 100644 UploadersLib/Resources/folder-network.png create mode 100644 UploadersLib/Resources/folder.gif create mode 100644 UploadersLib/Resources/folder_gray.gif create mode 100644 UploadersLib/Resources/folder_photos.gif create mode 100644 UploadersLib/Resources/folder_public.gif create mode 100644 UploadersLib/Resources/folder_star.gif create mode 100644 UploadersLib/Resources/folder_user.gif create mode 100644 UploadersLib/Resources/globe-network.png create mode 100644 UploadersLib/Resources/mail.png create mode 100644 UploadersLib/Resources/package.gif create mode 100644 UploadersLib/Resources/page_white.gif create mode 100644 UploadersLib/Resources/page_white_acrobat.gif create mode 100644 UploadersLib/Resources/page_white_actionscript.gif create mode 100644 UploadersLib/Resources/page_white_c.gif create mode 100644 UploadersLib/Resources/page_white_code.gif create mode 100644 UploadersLib/Resources/page_white_compressed.gif create mode 100644 UploadersLib/Resources/page_white_cplusplus.gif create mode 100644 UploadersLib/Resources/page_white_csharp.gif create mode 100644 UploadersLib/Resources/page_white_cup.gif create mode 100644 UploadersLib/Resources/page_white_dvd.gif create mode 100644 UploadersLib/Resources/page_white_excel.gif create mode 100644 UploadersLib/Resources/page_white_film.gif create mode 100644 UploadersLib/Resources/page_white_flash.gif create mode 100644 UploadersLib/Resources/page_white_gear.gif create mode 100644 UploadersLib/Resources/page_white_paint.gif create mode 100644 UploadersLib/Resources/page_white_php.gif create mode 100644 UploadersLib/Resources/page_white_picture.gif create mode 100644 UploadersLib/Resources/page_white_powerpoint.gif create mode 100644 UploadersLib/Resources/page_white_ruby.gif create mode 100644 UploadersLib/Resources/page_white_sound.gif create mode 100644 UploadersLib/Resources/page_white_text.gif create mode 100644 UploadersLib/Resources/page_white_tux.gif create mode 100644 UploadersLib/Resources/page_white_vector.gif create mode 100644 UploadersLib/Resources/page_white_visualstudio.gif create mode 100644 UploadersLib/Resources/page_white_word.gif create mode 100644 UploadersLib/Resources/server-network.png create mode 100644 UploadersLib/SocialServices/Twitter.cs create mode 100644 UploadersLib/TextUploader.cs create mode 100644 UploadersLib/TextUploaders/CustomTextUploader.cs create mode 100644 UploadersLib/TextUploaders/Gist.cs create mode 100644 UploadersLib/TextUploaders/Paste2.cs create mode 100644 UploadersLib/TextUploaders/Paste_ee.cs create mode 100644 UploadersLib/TextUploaders/Pastebin.cs create mode 100644 UploadersLib/TextUploaders/Pastebin_ca.cs create mode 100644 UploadersLib/TextUploaders/Pastee.cs create mode 100644 UploadersLib/TextUploaders/Slexy.cs create mode 100644 UploadersLib/URLShortener.cs create mode 100644 UploadersLib/URLShorteners/BitlyURLShortener.cs create mode 100644 UploadersLib/URLShorteners/CustomURLShortener.cs create mode 100644 UploadersLib/URLShorteners/GoogleURLShortener.cs create mode 100644 UploadersLib/URLShorteners/IsgdURLShortener.cs create mode 100644 UploadersLib/URLShorteners/JmpURLShortener.cs create mode 100644 UploadersLib/URLShorteners/ThreelyURLShortener.cs create mode 100644 UploadersLib/URLShorteners/TinyURLShortener.cs create mode 100644 UploadersLib/URLShorteners/TurlURLShortener.cs create mode 100644 UploadersLib/UploadResult.cs create mode 100644 UploadersLib/Uploader.cs create mode 100644 UploadersLib/UploadersLib.csproj create mode 100644 UploadersLib/packages.config diff --git a/CodeWorks.sln b/CodeWorks.sln new file mode 100644 index 000000000..46cff8d74 --- /dev/null +++ b/CodeWorks.sln @@ -0,0 +1,26 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2012 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CodeWorks", "CodeWorks\CodeWorks.csproj", "{DE11B434-2D8D-4155-B150-72DF9F76FAB4}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HelpersLib", "HelpersLib\HelpersLib.csproj", "{327750E1-9FB7-4CC3-8AEA-9BC42180CAD3}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {DE11B434-2D8D-4155-B150-72DF9F76FAB4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {DE11B434-2D8D-4155-B150-72DF9F76FAB4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DE11B434-2D8D-4155-B150-72DF9F76FAB4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {DE11B434-2D8D-4155-B150-72DF9F76FAB4}.Release|Any CPU.Build.0 = Release|Any CPU + {327750E1-9FB7-4CC3-8AEA-9BC42180CAD3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {327750E1-9FB7-4CC3-8AEA-9BC42180CAD3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {327750E1-9FB7-4CC3-8AEA-9BC42180CAD3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {327750E1-9FB7-4CC3-8AEA-9BC42180CAD3}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/CodeWorks/CodeWorks.csproj b/CodeWorks/CodeWorks.csproj new file mode 100644 index 000000000..3a61b8b7d --- /dev/null +++ b/CodeWorks/CodeWorks.csproj @@ -0,0 +1,134 @@ + + + + Debug + AnyCPU + 9.0.30729 + 2.0 + {DE11B434-2D8D-4155-B150-72DF9F76FAB4} + WinExe + Properties + CodeWorks + CodeWorks + v3.5 + 512 + + + + + 3.5 + publish\ + true + Disk + false + Foreground + 7 + Days + false + false + true + 0 + 1.0.0.%2a + false + false + true + + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + AllRules.ruleset + false + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + AllRules.ruleset + false + + + + + 3.5 + + + 3.5 + + + 3.5 + + + + + + + + + + + Form + + + MainForm.cs + + + + + True + True + Resources.resx + + + + + + + + MainForm.cs + + + ResXFileCodeGenerator + Resources.Designer.cs + + + + + False + .NET Framework 3.5 SP1 Client Profile + false + + + False + .NET Framework 3.5 SP1 + true + + + False + Windows Installer 3.1 + true + + + + + {327750e1-9fb7-4cc3-8aea-9bc42180cad3} + HelpersLib + + + + + \ No newline at end of file diff --git a/CodeWorks/Licenses/ShareXLicense.txt b/CodeWorks/Licenses/ShareXLicense.txt new file mode 100644 index 000000000..3eebc4366 --- /dev/null +++ b/CodeWorks/Licenses/ShareXLicense.txt @@ -0,0 +1,24 @@ +#region License Information (GPL v3) + +/* + ShareX - A program that allows you to take screenshots and share any file type + Copyright (C) 2008-2013 ShareX Developers + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + Optionally you can also view the license at . +*/ + +#endregion License Information (GPL v3) \ No newline at end of file diff --git a/CodeWorks/MainForm.Designer.cs b/CodeWorks/MainForm.Designer.cs new file mode 100644 index 000000000..3b735a1df --- /dev/null +++ b/CodeWorks/MainForm.Designer.cs @@ -0,0 +1,302 @@ +namespace CodeWorks +{ + partial class MainForm + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.txtFolderPath = new System.Windows.Forms.TextBox(); + this.btnSearch = new System.Windows.Forms.Button(); + this.lvResults = new System.Windows.Forms.ListView(); + this.columnHeader1 = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.btnAddLicense = new System.Windows.Forms.Button(); + this.btnAddLicenseAll = new System.Windows.Forms.Button(); + this.scTextBoxes = new System.Windows.Forms.SplitContainer(); + this.groupBox1 = new System.Windows.Forms.GroupBox(); + this.tbDefaultText = new System.Windows.Forms.TextBox(); + this.groupBox2 = new System.Windows.Forms.GroupBox(); + this.btnCopyToClipboard = new System.Windows.Forms.Button(); + this.tbNewText = new System.Windows.Forms.TextBox(); + this.scMain = new System.Windows.Forms.SplitContainer(); + this.label1 = new System.Windows.Forms.Label(); + this.lblFileCount = new System.Windows.Forms.Label(); + this.btnFindRegionAreas = new System.Windows.Forms.Button(); + this.btnOrderLines = new System.Windows.Forms.Button(); + this.scTextBoxes.Panel1.SuspendLayout(); + this.scTextBoxes.Panel2.SuspendLayout(); + this.scTextBoxes.SuspendLayout(); + this.groupBox1.SuspendLayout(); + this.groupBox2.SuspendLayout(); + this.scMain.Panel1.SuspendLayout(); + this.scMain.Panel2.SuspendLayout(); + this.scMain.SuspendLayout(); + this.SuspendLayout(); + // + // txtFolderPath + // + this.txtFolderPath.Location = new System.Drawing.Point(72, 8); + this.txtFolderPath.Name = "txtFolderPath"; + this.txtFolderPath.Size = new System.Drawing.Size(974, 20); + this.txtFolderPath.TabIndex = 0; + // + // btnSearch + // + this.btnSearch.Location = new System.Drawing.Point(152, 40); + this.btnSearch.Name = "btnSearch"; + this.btnSearch.Size = new System.Drawing.Size(136, 24); + this.btnSearch.TabIndex = 1; + this.btnSearch.Text = "Search (Old method)"; + this.btnSearch.UseVisualStyleBackColor = true; + this.btnSearch.Click += new System.EventHandler(this.btnSearch_Click); + // + // lvResults + // + this.lvResults.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] { + this.columnHeader1}); + this.lvResults.Dock = System.Windows.Forms.DockStyle.Fill; + this.lvResults.FullRowSelect = true; + this.lvResults.GridLines = true; + this.lvResults.HeaderStyle = System.Windows.Forms.ColumnHeaderStyle.None; + this.lvResults.Location = new System.Drawing.Point(0, 0); + this.lvResults.MultiSelect = false; + this.lvResults.Name = "lvResults"; + this.lvResults.Size = new System.Drawing.Size(1040, 351); + this.lvResults.TabIndex = 2; + this.lvResults.UseCompatibleStateImageBehavior = false; + this.lvResults.View = System.Windows.Forms.View.Details; + this.lvResults.SelectedIndexChanged += new System.EventHandler(this.lvResults_SelectedIndexChanged); + // + // columnHeader1 + // + this.columnHeader1.Width = 900; + // + // btnAddLicense + // + this.btnAddLicense.Location = new System.Drawing.Point(296, 40); + this.btnAddLicense.Name = "btnAddLicense"; + this.btnAddLicense.Size = new System.Drawing.Size(80, 24); + this.btnAddLicense.TabIndex = 4; + this.btnAddLicense.Text = "Add license"; + this.btnAddLicense.UseVisualStyleBackColor = true; + this.btnAddLicense.Click += new System.EventHandler(this.btnAddLicense_Click); + // + // btnAddLicenseAll + // + this.btnAddLicenseAll.Location = new System.Drawing.Point(384, 40); + this.btnAddLicenseAll.Name = "btnAddLicenseAll"; + this.btnAddLicenseAll.Size = new System.Drawing.Size(112, 24); + this.btnAddLicenseAll.TabIndex = 5; + this.btnAddLicenseAll.Text = "Add license to all"; + this.btnAddLicenseAll.UseVisualStyleBackColor = true; + this.btnAddLicenseAll.Click += new System.EventHandler(this.btnAddLicenseAll_Click); + // + // scTextBoxes + // + this.scTextBoxes.Dock = System.Windows.Forms.DockStyle.Fill; + this.scTextBoxes.Location = new System.Drawing.Point(0, 0); + this.scTextBoxes.Name = "scTextBoxes"; + // + // scTextBoxes.Panel1 + // + this.scTextBoxes.Panel1.Controls.Add(this.groupBox1); + // + // scTextBoxes.Panel2 + // + this.scTextBoxes.Panel2.Controls.Add(this.groupBox2); + this.scTextBoxes.Size = new System.Drawing.Size(1040, 346); + this.scTextBoxes.SplitterDistance = 501; + this.scTextBoxes.TabIndex = 6; + // + // groupBox1 + // + this.groupBox1.Controls.Add(this.tbDefaultText); + this.groupBox1.Dock = System.Windows.Forms.DockStyle.Fill; + this.groupBox1.Location = new System.Drawing.Point(0, 0); + this.groupBox1.Name = "groupBox1"; + this.groupBox1.Size = new System.Drawing.Size(501, 346); + this.groupBox1.TabIndex = 0; + this.groupBox1.TabStop = false; + this.groupBox1.Text = "Default text"; + // + // tbDefaultText + // + this.tbDefaultText.Dock = System.Windows.Forms.DockStyle.Fill; + this.tbDefaultText.Location = new System.Drawing.Point(3, 16); + this.tbDefaultText.Multiline = true; + this.tbDefaultText.Name = "tbDefaultText"; + this.tbDefaultText.ScrollBars = System.Windows.Forms.ScrollBars.Both; + this.tbDefaultText.Size = new System.Drawing.Size(495, 327); + this.tbDefaultText.TabIndex = 0; + // + // groupBox2 + // + this.groupBox2.Controls.Add(this.btnCopyToClipboard); + this.groupBox2.Controls.Add(this.tbNewText); + this.groupBox2.Dock = System.Windows.Forms.DockStyle.Fill; + this.groupBox2.Location = new System.Drawing.Point(0, 0); + this.groupBox2.Name = "groupBox2"; + this.groupBox2.Size = new System.Drawing.Size(535, 346); + this.groupBox2.TabIndex = 0; + this.groupBox2.TabStop = false; + this.groupBox2.Text = "New text"; + // + // btnCopyToClipboard + // + this.btnCopyToClipboard.Location = new System.Drawing.Point(384, 312); + this.btnCopyToClipboard.Name = "btnCopyToClipboard"; + this.btnCopyToClipboard.Size = new System.Drawing.Size(115, 23); + this.btnCopyToClipboard.TabIndex = 2; + this.btnCopyToClipboard.Text = "Copy to clipboard"; + this.btnCopyToClipboard.UseVisualStyleBackColor = true; + this.btnCopyToClipboard.Click += new System.EventHandler(this.btnCopyToClipboard_Click); + // + // tbNewText + // + this.tbNewText.Dock = System.Windows.Forms.DockStyle.Fill; + this.tbNewText.Location = new System.Drawing.Point(3, 16); + this.tbNewText.Multiline = true; + this.tbNewText.Name = "tbNewText"; + this.tbNewText.ScrollBars = System.Windows.Forms.ScrollBars.Both; + this.tbNewText.Size = new System.Drawing.Size(529, 327); + this.tbNewText.TabIndex = 1; + // + // scMain + // + this.scMain.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.scMain.Location = new System.Drawing.Point(8, 72); + this.scMain.Name = "scMain"; + this.scMain.Orientation = System.Windows.Forms.Orientation.Horizontal; + // + // scMain.Panel1 + // + this.scMain.Panel1.Controls.Add(this.lvResults); + // + // scMain.Panel2 + // + this.scMain.Panel2.Controls.Add(this.scTextBoxes); + this.scMain.Size = new System.Drawing.Size(1040, 704); + this.scMain.SplitterDistance = 351; + this.scMain.SplitterWidth = 7; + this.scMain.TabIndex = 7; + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(8, 12); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(63, 13); + this.label1.TabIndex = 8; + this.label1.Text = "Folder path:"; + // + // lblFileCount + // + this.lblFileCount.AutoSize = true; + this.lblFileCount.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(162))); + this.lblFileCount.Location = new System.Drawing.Point(512, 44); + this.lblFileCount.Name = "lblFileCount"; + this.lblFileCount.Size = new System.Drawing.Size(0, 16); + this.lblFileCount.TabIndex = 9; + // + // btnFindRegionAreas + // + this.btnFindRegionAreas.Location = new System.Drawing.Point(8, 40); + this.btnFindRegionAreas.Name = "btnFindRegionAreas"; + this.btnFindRegionAreas.Size = new System.Drawing.Size(136, 24); + this.btnFindRegionAreas.TabIndex = 10; + this.btnFindRegionAreas.Text = "Search region areas"; + this.btnFindRegionAreas.UseVisualStyleBackColor = true; + this.btnFindRegionAreas.Click += new System.EventHandler(this.btnFindRegionAreas_Click); + // + // btnOrderLines + // + this.btnOrderLines.Location = new System.Drawing.Point(912, 40); + this.btnOrderLines.Name = "btnOrderLines"; + this.btnOrderLines.Size = new System.Drawing.Size(128, 24); + this.btnOrderLines.TabIndex = 11; + this.btnOrderLines.Text = "Clipboard lines A-Z"; + this.btnOrderLines.UseVisualStyleBackColor = true; + this.btnOrderLines.Click += new System.EventHandler(this.btnOrderLines_Click); + // + // MainForm + // + this.AllowDrop = true; + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(1057, 785); + this.Controls.Add(this.btnOrderLines); + this.Controls.Add(this.btnFindRegionAreas); + this.Controls.Add(this.lblFileCount); + this.Controls.Add(this.label1); + this.Controls.Add(this.scMain); + this.Controls.Add(this.btnAddLicenseAll); + this.Controls.Add(this.btnAddLicense); + this.Controls.Add(this.btnSearch); + this.Controls.Add(this.txtFolderPath); + this.Name = "MainForm"; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; + this.Text = "CodeWorks"; + this.DragDrop += new System.Windows.Forms.DragEventHandler(this.MainForm_DragDrop); + this.DragEnter += new System.Windows.Forms.DragEventHandler(this.MainForm_DragEnter); + this.Resize += new System.EventHandler(this.MainForm_Resize); + this.scTextBoxes.Panel1.ResumeLayout(false); + this.scTextBoxes.Panel2.ResumeLayout(false); + this.scTextBoxes.ResumeLayout(false); + this.groupBox1.ResumeLayout(false); + this.groupBox1.PerformLayout(); + this.groupBox2.ResumeLayout(false); + this.groupBox2.PerformLayout(); + this.scMain.Panel1.ResumeLayout(false); + this.scMain.Panel2.ResumeLayout(false); + this.scMain.ResumeLayout(false); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.TextBox txtFolderPath; + private System.Windows.Forms.Button btnSearch; + private System.Windows.Forms.ListView lvResults; + private System.Windows.Forms.ColumnHeader columnHeader1; + private System.Windows.Forms.Button btnAddLicense; + private System.Windows.Forms.Button btnAddLicenseAll; + private System.Windows.Forms.SplitContainer scTextBoxes; + private System.Windows.Forms.GroupBox groupBox1; + private System.Windows.Forms.TextBox tbDefaultText; + private System.Windows.Forms.GroupBox groupBox2; + private System.Windows.Forms.TextBox tbNewText; + private System.Windows.Forms.SplitContainer scMain; + private System.Windows.Forms.Label label1; + private System.Windows.Forms.Label lblFileCount; + private System.Windows.Forms.Button btnFindRegionAreas; + private System.Windows.Forms.Button btnOrderLines; + private System.Windows.Forms.Button btnCopyToClipboard; + } +} + diff --git a/CodeWorks/MainForm.cs b/CodeWorks/MainForm.cs new file mode 100644 index 000000000..70fc98957 --- /dev/null +++ b/CodeWorks/MainForm.cs @@ -0,0 +1,280 @@ +#region License Information (GPL v3) + +/* + ShareX - A program that allows you to take screenshots and share any file type + Copyright (C) 2008-2013 ShareX Developers + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + Optionally you can also view the license at . +*/ + +#endregion License Information (GPL v3) + +using CodeWorks.Properties; +using HelpersLib; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Media; +using System.Text; +using System.Text.RegularExpressions; +using System.Windows.Forms; + +namespace CodeWorks +{ + public partial class MainForm : Form + { + private readonly string folderPath = Path.GetFullPath(@"..\..\..\..\trunk"); + private readonly string[] ignoreFolders = new string[] { "bin", "obj", "Properties", "GreenshotImageEditor", "AviFile" }; + private readonly string[] ignoreFiles = new string[] { }; + private readonly string[] ignoreFilenamesEndsWith = new string[] { ".designer.cs" }; + private readonly string[] allowFilenamesEndsWith = new string[] { ".cs" }; + private readonly string licenseText = Resources.ShareXLicense; + + public MainForm() + { + InitializeComponent(); + txtFolderPath.Text = folderPath; + } + + private void btnFindRegionAreas_Click(object sender, EventArgs e) + { + lvResults.Items.Clear(); + SearchFolderRegionAreas(txtFolderPath.Text); + lblFileCount.Text = lvResults.Items.Count + " files."; + SystemSounds.Exclamation.Play(); + } + + private void SearchFolderRegionAreas(string path) + { + if (IsValidFolder(path)) + { + foreach (string folder in Directory.GetDirectories(path)) + { + SearchFolderRegionAreas(folder); + } + + foreach (string file in Directory.GetFiles(path)) + { + CleanRegionAreas(file, "License Information (GPL v3)", licenseText); + } + } + } + + private bool CleanRegionAreas(string path, string searchRegionName, string newRegionText) + { + if (IsValidFile(path)) + { + TextInfo info = new TextInfo(path); + RegionAreaManager regionAreaManager = new RegionAreaManager(info.DefaultText); + List regionAreas = regionAreaManager.GetRegionAreas(); + int offset = 0; + + foreach (RegionArea regionArea in regionAreas) + { + if (regionArea.RegionName.IndexOf(searchRegionName, StringComparison.InvariantCultureIgnoreCase) >= 0) + { + regionArea.RegionIndexOffset = offset; + string regionAreaTextRemoved = regionArea.RemoveRegionText(); + offset += -regionArea.RegionLength; + regionAreaManager.Text = regionAreaTextRemoved; + } + } + + info.NewText = newRegionText + "\r\n\r\n" + regionAreaManager.Text.Trim(); + + if (info.IsDifferent) + { + lvResults.Items.Add(path).Tag = info; + return true; + } + } + + return false; + } + + private void btnSearch_Click(object sender, EventArgs e) + { + lvResults.Items.Clear(); + SearchFolder(txtFolderPath.Text); + lblFileCount.Text = lvResults.Items.Count + " files."; + SystemSounds.Exclamation.Play(); + } + + private void SearchFolder(string path) + { + if (IsValidFolder(path)) + { + foreach (string folder in Directory.GetDirectories(path)) + { + SearchFolder(folder); + } + + foreach (string file in Directory.GetFiles(path)) + { + CheckFile(file); + } + } + } + + private bool IsValidFolder(string path) + { + if (!string.IsNullOrEmpty(path)) + { + string foldername = Path.GetFileName(path).ToLowerInvariant(); + return ignoreFolders.All(x => !foldername.Equals(x, StringComparison.InvariantCultureIgnoreCase)) && Directory.Exists(path); + } + + return false; + } + + private bool IsValidFile(string path) + { + if (!string.IsNullOrEmpty(path)) + { + string filename = Path.GetFileName(path).ToLowerInvariant(); + return Path.HasExtension(filename) && allowFilenamesEndsWith.Any(x => filename.EndsWith(x)) && + ignoreFiles.All(x => !filename.Equals(x, StringComparison.InvariantCultureIgnoreCase)) && + ignoreFilenamesEndsWith.All(x => !filename.EndsWith(x)) && File.Exists(path); + } + + return false; + } + + private bool CheckFile(string path) + { + if (IsValidFile(path)) + { + TextInfo info = new TextInfo(path); + info.NewText = CheckLicense(info.DefaultText); + // result = RemoveDuplicateLines(result); + + if (info.IsDifferent) + { + lvResults.Items.Add(path).Tag = info; + return true; + } + } + + return false; + } + + private string CheckLicense(string text) + { + if (!text.StartsWith(licenseText)) + { + return text.Insert(0, licenseText + "\r\n\r\n"); + } + + return null; + } + + private string RemoveDuplicateLines(string text) + { + return Regex.Replace(text, @"(\r\n\s*){3,}", "\r\n\r\n", RegexOptions.Singleline); + } + + private void lvResults_SelectedIndexChanged(object sender, EventArgs e) + { + if (lvResults.SelectedItems.Count > 0) + { + TextInfo info = lvResults.SelectedItems[0].Tag as TextInfo; + + if (info != null) + { + tbDefaultText.Text = info.DefaultText; + tbNewText.Text = info.NewText; + } + } + } + + private void btnAddLicense_Click(object sender, EventArgs e) + { + if (lvResults.SelectedItems.Count > 0) + { + TextInfo info = lvResults.SelectedItems[0].Tag as TextInfo; + + if (info != null) + { + info.WriteNewText(); + lvResults.Items.Remove(lvResults.SelectedItems[0]); + } + } + } + + private void btnAddLicenseAll_Click(object sender, EventArgs e) + { + foreach (ListViewItem lvi in lvResults.Items) + { + TextInfo info = lvi.Tag as TextInfo; + + if (info != null) + { + info.WriteNewText(); + } + } + + lvResults.Items.Clear(); + } + + private void MainForm_Resize(object sender, EventArgs e) + { + Refresh(); + } + + private void MainForm_DragEnter(object sender, DragEventArgs e) + { + if (e.Data.GetDataPresent(DataFormats.FileDrop, false)) + { + e.Effect = DragDropEffects.Copy; + } + else + { + e.Effect = DragDropEffects.None; + } + } + + private void MainForm_DragDrop(object sender, DragEventArgs e) + { + if (e.Data.GetDataPresent(DataFormats.FileDrop, false)) + { + string[] files = e.Data.GetData(DataFormats.FileDrop, false) as string[]; + txtFolderPath.Text = files[0]; + } + } + + private void btnOrderLines_Click(object sender, EventArgs e) + { + string clipboard = Clipboard.GetText(); + clipboard = clipboard.Trim(); + string[] lines = new StringLineReader(clipboard).ReadAllLines(); + Array.Sort(lines); + string result = string.Join("\r\n", lines); + + tbDefaultText.Text = clipboard; + tbNewText.Text = result; + } + + private void btnCopyToClipboard_Click(object sender, EventArgs e) + { + if (!string.IsNullOrEmpty(tbNewText.Text)) + { + Clipboard.SetText(tbNewText.Text); + } + } + } +} \ No newline at end of file diff --git a/CodeWorks/MainForm.resx b/CodeWorks/MainForm.resx new file mode 100644 index 000000000..7080a7d11 --- /dev/null +++ b/CodeWorks/MainForm.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/CodeWorks/Program.cs b/CodeWorks/Program.cs new file mode 100644 index 000000000..8ec8348de --- /dev/null +++ b/CodeWorks/Program.cs @@ -0,0 +1,41 @@ +#region License Information (GPL v3) + +/* + ShareX - A program that allows you to take screenshots and share any file type + Copyright (C) 2008-2013 ShareX Developers + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + Optionally you can also view the license at . +*/ + +#endregion License Information (GPL v3) + +using System; +using System.Windows.Forms; + +namespace CodeWorks +{ + internal static class Program + { + [STAThread] + private static void Main() + { + Application.EnableVisualStyles(); + Application.SetCompatibleTextRenderingDefault(false); + Application.Run(new MainForm()); + } + } +} \ No newline at end of file diff --git a/CodeWorks/Properties/AssemblyInfo.cs b/CodeWorks/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..8a385b7d3 --- /dev/null +++ b/CodeWorks/Properties/AssemblyInfo.cs @@ -0,0 +1,35 @@ +using System.Reflection; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("CodeWorks")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("ShareX Developers")] +[assembly: AssemblyProduct("CodeWorks")] +[assembly: AssemblyCopyright("Copyright (C) 2012 ShareX Developers")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("3c399454-8486-40f9-b65c-24b23909c368")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.180")] +[assembly: AssemblyFileVersion("1.0.0.0")] \ No newline at end of file diff --git a/CodeWorks/Properties/Resources.Designer.cs b/CodeWorks/Properties/Resources.Designer.cs new file mode 100644 index 000000000..fd314cfd0 --- /dev/null +++ b/CodeWorks/Properties/Resources.Designer.cs @@ -0,0 +1,84 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.18047 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace CodeWorks.Properties { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("CodeWorks.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized string similar to #region License Information (GPL v3) + /// + ////* + /// ShareX - A program that allows you to take screenshots and share any file type + /// Copyright (C) 2008-2013 ShareX Developers + /// + /// This program is free software; you can redistribute it and/or + /// modify it under the terms of the GNU General Public License + /// as published by the Free Software Foundation; either version 2 + /// of the License, or (at your option) any later version. + /// + /// This program is distributed in the hope that it will be useful, + /// [rest of string was truncated]";. + /// + internal static string ShareXLicense { + get { + return ResourceManager.GetString("ShareXLicense", resourceCulture); + } + } + } +} diff --git a/CodeWorks/Properties/Resources.resx b/CodeWorks/Properties/Resources.resx new file mode 100644 index 000000000..d72ed2249 --- /dev/null +++ b/CodeWorks/Properties/Resources.resx @@ -0,0 +1,124 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + ..\licenses\sharexlicense.txt;System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;windows-1254 + + \ No newline at end of file diff --git a/CodeWorks/RegionArea.cs b/CodeWorks/RegionArea.cs new file mode 100644 index 000000000..8f0f0f145 --- /dev/null +++ b/CodeWorks/RegionArea.cs @@ -0,0 +1,79 @@ +#region License Information (GPL v3) + +/* + ShareX - A program that allows you to take screenshots and share any file type + Copyright (C) 2008-2013 ShareX Developers + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + Optionally you can also view the license at . +*/ + +#endregion License Information (GPL v3) + +namespace CodeWorks +{ + public class RegionArea + { + public int RegionStartIndex { get; set; } + public int RegionEndIndex { get; set; } + + public int RegionLength + { + get { return RegionEndIndex - RegionStartIndex + 1; } + } + + public string RegionName { get; set; } + + public int RegionIndexOffset { get; set; } + + private RegionAreaManager manager; + + public RegionArea(RegionAreaManager regionAreaManager) + { + manager = regionAreaManager; + } + + public RegionArea(RegionAreaManager regionAreaManager, int regionStartIndex) + : this(regionAreaManager) + { + RegionStartIndex = regionStartIndex; + } + + public string GetRegionText() + { + return manager.Text.Substring(RegionStartIndex + RegionIndexOffset, RegionLength); + } + + public string RemoveRegionText() + { + return manager.Text.Remove(RegionStartIndex + RegionIndexOffset, RegionLength); + } + + public string ReplaceRegionText(string replaceWith) + { + string result = string.Empty; + + if (RegionStartIndex + RegionIndexOffset > 0) + { + result = manager.Text.Substring(0, RegionStartIndex + RegionIndexOffset); + } + + result += replaceWith + manager.Text.Substring(RegionEndIndex + RegionIndexOffset); + + return result; + } + } +} \ No newline at end of file diff --git a/CodeWorks/RegionAreaManager.cs b/CodeWorks/RegionAreaManager.cs new file mode 100644 index 000000000..78848c7cf --- /dev/null +++ b/CodeWorks/RegionAreaManager.cs @@ -0,0 +1,85 @@ +#region License Information (GPL v3) + +/* + ShareX - A program that allows you to take screenshots and share any file type + Copyright (C) 2008-2013 ShareX Developers + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + Optionally you can also view the license at . +*/ + +#endregion License Information (GPL v3) + +using System; +using System.Collections.Generic; +using HelpersLib; + +namespace CodeWorks +{ + public class RegionAreaManager + { + public string Text { get; set; } + public List RegionAreas { get; private set; } + + public RegionAreaManager(string text) + { + Text = text; + } + + public List GetRegionAreas() + { + RegionAreas = new List(); + + if (!string.IsNullOrEmpty(Text)) + { + bool searchingRegionStart = true; + + StringLineReader reader = new StringLineReader(Text); + string line; + RegionArea regionArea = null; + int index = 0; + + while ((line = reader.ReadLine()) != null) + { + line = line.Trim(); + + if (searchingRegionStart) + { + if (line.StartsWith("#region", StringComparison.InvariantCulture)) + { + searchingRegionStart = false; + regionArea = new RegionArea(this, index); + if (line.Length > 8) regionArea.RegionName = line.Substring(8); + } + } + else + { + if (line.StartsWith("#endregion", StringComparison.InvariantCulture)) + { + searchingRegionStart = true; + regionArea.RegionEndIndex = reader.Position - 1; + RegionAreas.Add(regionArea); + } + } + + index = reader.Position; + } + } + + return RegionAreas; + } + } +} \ No newline at end of file diff --git a/CodeWorks/TextInfo.cs b/CodeWorks/TextInfo.cs new file mode 100644 index 000000000..60dfcb146 --- /dev/null +++ b/CodeWorks/TextInfo.cs @@ -0,0 +1,58 @@ +#region License Information (GPL v3) + +/* + ShareX - A program that allows you to take screenshots and share any file type + Copyright (C) 2008-2013 ShareX Developers + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + Optionally you can also view the license at . +*/ + +#endregion License Information (GPL v3) + +using System.IO; +using System.Text; + +namespace CodeWorks +{ + public class TextInfo + { + public string FilePath { get; set; } + public string DefaultText { get; set; } + public string NewText { get; set; } + + public bool IsDifferent + { + get { return !string.IsNullOrEmpty(NewText) && DefaultText != NewText; } + } + + public TextInfo() { } + + public TextInfo(string filePath) + { + FilePath = filePath; + DefaultText = File.ReadAllText(FilePath, Encoding.UTF8); + } + + public void WriteNewText() + { + if (!string.IsNullOrEmpty(FilePath) && !string.IsNullOrEmpty(NewText) && File.Exists(FilePath)) + { + File.WriteAllText(FilePath, NewText, Encoding.UTF8); + } + } + } +} \ No newline at end of file diff --git a/Docs/CodeMaid.settings b/Docs/CodeMaid.settings new file mode 100644 index 000000000..8aa14a84f --- /dev/null +++ b/Docs/CodeMaid.settings @@ -0,0 +1,404 @@ + + + + +
+ + + + + + True + + + 5 + + + False + + + True + + + True + + + 11 + + + + + + 9 + + + True + + + 8 + + + 1 + + + Segoe UI + + + False + + + param||exception||returns||value||permissions + + + False + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + 12 + + + False + + + 0 + + + True + + + True + + + True + + + 100 + + + True + + + True + + + True + + + False + + + False + + + False + + + True + + + True + + + 10 + + + True + + + .*\.Designer\.cs||.*\.Designer\.vb||.*\.resx + + + True + + + False + + + True + + + True + + + 0 + + + False + + + True + + + True + + + True + + + True + + + 10 + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + 4 + + + True + + + 0 + + + True + + + False + + + False + + + 2 + + + True + + + True + + + True + + + False + + + 15 + + + True + + + False + + + True + + + summary||remarks||example + + + True + + + True + + + True + + + .cpp .h||.xaml .xaml.cs||.xml .xsd||.ascx .ascx.cs||.aspx .aspx.cs||.master .master.cs + + + False + + + True + + + True + + + 3 + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + 6 + + + True + + + True + + + False + + + False + + + True + + + 7 + + + True + + + True + + + False + + + True + + + True + + + True + + + False + + + False + + + True + + + True + + + False + + + True + + + True + + + True + + + 17 + + + True + + + True + + + True + + + + \ No newline at end of file diff --git a/Docs/ShareX_Class.zip b/Docs/ShareX_Class.zip new file mode 100644 index 0000000000000000000000000000000000000000..125f96a333c8ea45308fee8d8fabb884539084d0 GIT binary patch literal 1456 zcmWIWW@Zs#U|`^2kZccfKJoQT%qb=Y1|L?C2m^z2PGWJfUUG47@a?=?4g$6AkL2CB z_!X6&UGoUdRJJrx@n8`!^Qw$_V;Hxw;_hA3b)ipB|GMD+O26dVvqf97rql!+^S%Q~D6!N7;Y3%Fdjp3Vp z;dRNq`0rMC{~2CSzq5FPrdI6wPzL*FzoyLFHqpsTU3s!_LO>Ts1QVC@y52{x&nOfm zyKY&!N~6r^Nnm3BW)&A#*9l4?*1Seq{QQ?yZ4#QgIA^41A8cOiF3PCP`Yr0X#Gzdp zhRUlCuP(XZDOCFM3B!cTyPIr96X(92D$^d~eoxGUb^4izzg#I<6VG1KKX^v+&7ae) z!5dXN)K!apwoU!JvGrLDM}%^U)MB44T0&gy&o}A&)h;paRSBaVzk z_wINoPWFw|ZJjSG%h0bdaT)8*M{P?^?T@Nau=u{y^}c#RRKew?7SnQAw;nWZQJ5y! zuW{JJV`Wr<`qqsP{q-D{Uw>P+Zueim?X2GCZKs-Rvp4(;EtJWav`Y1j<+G(Cg$5nF zejA#p{k@R)yz*Vnq%#w{-zrkw^jEotJ*{9|JBPZcRmum^&s!VuNOQ^1tcOm z%DmDm9Ta)xOdo7YoN;SG=LyF%OK-`wZDZU%f5H1ZCh>y46`!sud5Sg0+y4A2omJXfJ zYr7`vd(is5f7k-x>D>^R-rFa=wb;bSz+lVFz#tAx@4*>~MX3?-kYrz0T#}kwkds)F z+8TT@@3w+S?Ykv^nIGL{Gw;0SJ7ZO0lBRz4l$5J+n*03rNIhmeC+7S6yJMF5y=ANV zzZY0OpI6*?_g{#qRnx|)c|miIh$v?)(OZ=^|IxXr7kT$ReSd#r`&+({Xm0iwi7)0I zG5PY|Pj^YhQ-{PSH>GAjYA$}O?zG0HLC7p@s>!WamyQN$2OSX1y4$4a{<&zU|Ej$|`qE1y??s%xpENwXJWb9o|J-q@$L}-0{PdqT zzk4rcO?>oHXJ(=c+oG?VJviFa9e1RrwEWzrz3u9;yR(^i_Al(&6T*CyZD0A+MkVum zulRH4{d)4V^v&&f>)OsW!TkyEPH+8wXUCcJ4cC`3?Y=A>dp$mCp24Ry|MOSVtNQ-P z)a(3Zb9?dm*@^wDck38m?)@xu@Y_`LuqN>-3C^}`PO=Tw*^ai?)c4u8njbpB@J0N@ z54ZSujsJ0u6*DuK>v%Ztt(br9=aQf|KI|1K=~wRE{#jq)rYGO{pE1Cjk;$F`SHY$N zjA{^20F#JfjteTn$RNRB+uHn?^UOKMj$HTE97r^>$(Tt9+2jwvf)q!BLbj;LX5n9u bMKh*AEP~Mi-mGjO_b~zCVIX~-1;hgYu(*Ha literal 0 HcmV?d00001 diff --git a/Docs/VersionHistory.txt b/Docs/VersionHistory.txt new file mode 100644 index 000000000..2e8ad0cbc --- /dev/null +++ b/Docs/VersionHistory.txt @@ -0,0 +1,201 @@ +ShareX 8.3 r700 - 2013-10-15 + +Added new After Capture task "Add image effects" in favour of resize, border and shadow +Added hotkey support for Tools (screen color picker, hash check and index folder) and to stop uploading +Added mega.co.nz file uploader support + +ShareX 8.2 r655 – 2013-09-26 + +Added folder indexer with support for text, HTML and XML +Option to change clipboard content format for wider software compatibility +New after capture task: Image resize +Added support for indexing a folder during Clipboard Upload +Ability to use Windows modifier for hotkeys using configuration file +Rectangle Lite support in Advanced settings for slow computers + + +ShareX 8.1 r573 - 2013-09-04 + +Redesigned main configuration with simplified settings +Improved hotkey system to support all possible hotkey specific tasks +Support custom formats for clipboard content +Improved screen recorder +Automatic mode for proxy configuration + +ShareX 8.0 r463 - 2013-08-12 + +Ability to create custom hotkeys with task settings +Unlimited screen recorder support +Commandline support for screen recorder +Added auto capture support +Capture selected monitor screen +Windows 7 taskbar support +Ability to change settings folder + +ShareX 7.0.0.350 - 2013-04-05 + +Added modified version of Greenshot image editor (After capture -> Annotate Image) +Imgur, Google URL Shortener and Picasa using OAuth 2.0 now (You need to re-authorize) +Added Imgur album support +Added Google Drive uploader +Fixed Flickr upload issue +Updated to use Twitter 1.1 API +Fixed Possible crash with non existent program paths +Simplified Proxy handling behavior. No need to manually enter proxy host as the program will automatically retrieve it from the system proxy. +Added "Send URL with email" After upload task +Issues with DST time fixed + +ShareX 6.7.0.311 - 2013-03-19 + +Issues with arguments and custom uploaders fixed +Fixed screen capture issues with Custom DPI settings +POST file support for custom text uploader +Shows cursor only if its visible +Added Hash Check Tool +Added localhostr.com support + +ShareX 6.6.2.293 - 2013-02-01 + +Fixed errors with screen recording +Added Name Formatting support for custom uploaders +Fixed errors with updater +Remembers file upload directory +Goo.gl shortener fix +Fixed issues with slow FTP uploads + +ShareX 6.6.1.283 - 2013-01-21 + +Reverted minimum system requirements to .NET 3.5 to support Windows XP + +ShareX 6.6.0.280 - 2013-01-20 + +Added Screenshot delay setting +Added Clipboard upload settings tab +Fixed bug related to clipboard copy +Additional directory checks before saving screenshot +Added custom uploader support for all data types (image, text, file, url shortener) +Added Screen Recording with GIF/AVI output +Minimum system requirements updated to .NET 4.5 + +ShareX 6.5.0.248 - 2012-09-19 + +Use Delete key to remove items from main window +Added "Post capture tasks" window +Long file name error while capturing Active Window fixed +Updated Dropbox uploader +Auto Generated file names feature updated +Added Watermark support +Image history will remember your view settings +History & Image history will remember position, size and window state info +Be able to see response for a failed upload +Added "Add Border" Option to "After Capture Tasks" menu +Imgur upload bug fixed + +ShareX 6.4.0.220 - 2012-08-10 + +Shows Balloon Tip after capture +Added Picasa image uploader +Added option to Clear List in Main Window +Minor UI changes to updater + +ShareX 6.3.0.212 - 2012-08-03 + +Added new hotkeys to main window, history, image history: Enter = Open URL, Ctrl + Enter = Open file, Ctrl + C = Copy URL, F5 = Refresh +Added separate name format for active window capture +Moved magnifier out of text area +Added the option of capturing Active Windows without borders +FTP path issues fixed +Added Auto Increment option for Naming Files +Added paste.ee text uploader support +Added Progress(in %) information to the main window title +Added Shadow Offset setting +Double Clicking on a file performs checks in this order to open it: Shortended url first, url then and then the local file. +Added ge.tt file uploader + +ShareX 6.2.0.195 - 2012-07-15 + +Added watch folder tab to settings for auto uploading newly created files in these folders +Added screen color picker to tools section +Added image history with a thumbnail view +Updated naming pattern to include variables such as 'active window title' and 'image width/height' +It is now possible to copy multiple uploaded item infos (html, bbcode etc.) +In 'shape capture' mode spacebar will now perform a fullscreen capture + +ShareX 6.1.0.180 - 2012-06-23 + +Added support for sharing via social networking services initially with Twitter +After capture tasks and After upload tasks are configurable from the main window or tray menu +Reconfigured main window to have the main menu strip vertically +History storage optimizations +Ability to retry upload via main window context menu +Added printer support + +ShareX 6.0.0.161 - 2012-06-09 + +Improved right click menu to include more open/copy options +Added support to preview image in the main window +Added support to run External Programs +Added pastee.org Text Uploader support +Ability to capture Last region +Proportional resizing of shapes while holding shift key +Added shortcut to "Send To" without using registry +Cursor can be moved using arrow keys during a shapes capture +Screen-wide crosshair support during shapes capture +Magnifier support during shapes capture, resizeable with mouse wheel (hold shift to increase box size) +Added imm.io Image Uploader +Rebranded as ShareX + +ZUploader 5.5.0.3030 - 2012-03-07 + +5.5.0 Added Email as file uploader +5.4.1 Clipboard upload auto detects URL and shortens it +5.4.0 Added optional shadow capture for transparent capture +5.3.1 Settings are backed up for automatic recovery +5.3.0 Added Twitpic, Twitsnaps and yFrog +5.2.1 Improved tray menu +5.2.0 New updater with automatic downloader +5.1.0 Added image resize settings +5.0.0 Added Window & Control capture support to shape captures +4.9.0 Added new post-capture option: save screenshots to file +4.8.0 Added Box file uploader support +4.7.2 Supported image/text/file based FTP destinations +4.7.1 Added camera sound when capture is made +4.7.0 Added Transparent window capture +4.6.0 Added Hotkeys support +4.5.0 Added support to capture window from a list of windows +4.4.0 Added support to capture shapes to Clipboard +4.3.0 Added Minus uploader support +4.2.0 Added Photobucket uploader support +4.1.1 Added user account support for Goo.gl URL Shortener service +4.1.0 Added tray icon support +4.0.0 Added Capture menu to capture rectangle, rounded rectangle, ellipse, triangle, diamond, polygon and freehand shapes +3.12.0 Plugin system to perform Crop Shot and Entire Screen capture +3.11.0 New Uploaders Configuration window to setup user accounts +3.10.0 Added Clipboard Content Viewer to confirm uploading using Clipboard +3.9.0 Added URL Shortener support +3.8.0 Added ClipboardUpload hotkey (Ctrl + Page up) +3.7.0 Added upload queue system to limit simultaneous uploads +3.6.0 Added buffer size setting to improve upload speed +3.5.0 Added support to copy multiple URLs in history form +3.4.0 Text file detection improved for detecting popular programming code files +3.3.0 Image and text encoding operations moved to upload thread to prevent the main window from freezing +3.2.0 Drag & drop bitmap / text support +3.1.0 Startup time improved by using precompiled xml serialization dll (SGEN) and threads +3.0.0 Improved uploading speed and memory allocation especially for big uploads, many bug fixes +2.8.0 Added auto naming settings for clipboard upload +2.6.0 Added debug tab to settings form which also saves debug messages automatically to "ZUploaderPersonalPath/Log_(Month)_(Year).txt" +2.5.0 Added "Show response" button to right click menu to see upload response (Useful for debugging) +2.4.0 Added new CLI command (-clipboardupload) +2.3.0 Portable support +2.2.0 Added image viewer to history form +2.1.0 Added filters to history form +2.0.0 History support using SQLite as database +1.5.0 Image settings for changing image related settings such as image format, quality etc. +1.4.0 Single instance support for better shell extension uploading (Useful for CLI uploads) +1.3.0 Added "Stop upload" button to right click menu +1.2.0 Added support for Shell Extension "Upload using ShareX" +1.1.0 Reconfigured UI, Added proxy support + +ZUploader 1.0.0.0 - 27-03-2010 + +1.0.0 Initial version of ZUploader diff --git a/LICENSE b/Docs/license.txt similarity index 98% rename from LICENSE rename to Docs/license.txt index 6f5a36cbc..20d40b6bc 100644 --- a/LICENSE +++ b/Docs/license.txt @@ -1,7 +1,7 @@ -GNU GENERAL PUBLIC LICENSE + GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 - Copyright (C) 2007 Free Software Foundation, Inc. {http://fsf.org/} + Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. @@ -631,8 +631,8 @@ to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. - {one line to give the program's name and a brief idea of what it does.} - Copyright (C) {year} {name of author} + + Copyright (C) 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 @@ -645,14 +645,14 @@ the "copyright" line and a pointer to where the full notice is found. GNU General Public License for more details. You should have received a copy of the GNU General Public License - along with this program. If not, see {http://www.gnu.org/licenses/}. + along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: - ShareX Copyright (C) 2013 + Copyright (C) This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. @@ -664,11 +664,11 @@ might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see -{http://www.gnu.org/licenses/}. +. The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read -{http://www.gnu.org/philosophy/why-not-lgpl.html}. +. \ No newline at end of file diff --git a/Docs/svn global ignore.txt b/Docs/svn global ignore.txt new file mode 100644 index 000000000..8af6a1a37 --- /dev/null +++ b/Docs/svn global ignore.txt @@ -0,0 +1 @@ +.o *.lo *.la *.al .libs *.so *.so.[0-9] .a *.pyc *.pyo *.rej *~ ## .#* ..swp .DS_Store thumbs.db Thumbs.db *.bak *.class *.mine *.obj *.ncb *.lib *.log *.idb *.ilk *.msi *.res *.pch *.suo *.exp *.*~ *.~ ~. cvs CVS *.CVS *.cvs release Release debug Debug ignore Ignore bin Bin obj Obj *.csproj.user *.user \ No newline at end of file diff --git a/GreenshotImageEditor/Configuration/CoreConfiguration.cs b/GreenshotImageEditor/Configuration/CoreConfiguration.cs new file mode 100644 index 000000000..1bf59cac9 --- /dev/null +++ b/GreenshotImageEditor/Configuration/CoreConfiguration.cs @@ -0,0 +1,434 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2013 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: http://getgreenshot.org/ + * The Greenshot project is hosted on Sourceforge: http://sourceforge.net/projects/greenshot/ + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using Greenshot.IniFile; +using Greenshot.Plugin; +using System; +using System.Collections.Generic; +using System.Drawing; +using System.IO; +using System.Windows.Forms; + +namespace GreenshotPlugin.Core +{ + public enum ClipboardFormat + { + PNG, DIB, HTML, HTMLDATAURL, BITMAP + } + public enum OutputFormat + { + bmp, gif, jpg, png, tiff, greenshot + } + 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. + /// + [IniSection("Core", Description = "Greenshot core configuration")] + public class CoreConfiguration : IniSection + { + [IniProperty("Language", Description = "The language in IETF format (e.g. en-US)")] + public string Language; + + [IniProperty("RegionHotkey", Description = "Hotkey for starting the region capture", DefaultValue = "PrintScreen")] + public string RegionHotkey; + [IniProperty("WindowHotkey", Description = "Hotkey for starting the window capture", DefaultValue = "Alt + PrintScreen")] + public string WindowHotkey; + [IniProperty("FullscreenHotkey", Description = "Hotkey for starting the fullscreen capture", DefaultValue = "Ctrl + PrintScreen")] + public string FullscreenHotkey; + [IniProperty("LastregionHotkey", Description = "Hotkey for starting the last region capture", DefaultValue = "Shift + PrintScreen")] + public string LastregionHotkey; + [IniProperty("IEHotkey", Description = "Hotkey for starting the IE capture", DefaultValue = "Shift + Ctrl + PrintScreen")] + public string IEHotkey; + + [IniProperty("IsFirstLaunch", Description = "Is this the first time launch?", DefaultValue = "true")] + public bool IsFirstLaunch; + [IniProperty("Destinations", Separator = ",", Description = "Which destinations? Possible options (more might be added by plugins) are: Editor, FileDefault, FileWithDialog, Clipboard, Printer, EMail, Picker", DefaultValue = "Picker")] + public List OutputDestinations = new List(); + [IniProperty("ClipboardFormats", Separator = ",", Description = "Specify which formats we copy on the clipboard? Options are: PNG, HTML, HTMLDATAURL and DIB", DefaultValue = "PNG,DIB")] + public List ClipboardFormats = new List(); + + [IniProperty("CaptureMousepointer", Description = "Should the mouse be captured?", DefaultValue = "true")] + public bool CaptureMousepointer; + [IniProperty("CaptureWindowsInteractive", Description = "Use interactive window selection to capture? (false=Capture active window)", DefaultValue = "false")] + public bool CaptureWindowsInteractive; + [IniProperty("CaptureDelay", Description = "Capture delay in millseconds.", DefaultValue = "100")] + public int CaptureDelay; + [IniProperty("ScreenCaptureMode", Description = "The capture mode used to capture a screen. (Auto, FullScreen, Fixed)", DefaultValue = "Auto")] + public ScreenCaptureMode ScreenCaptureMode; + [IniProperty("ScreenToCapture", Description = "The screen number to capture when using ScreenCaptureMode Fixed.", DefaultValue = "1")] + public int ScreenToCapture; + [IniProperty("WindowCaptureMode", Description = "The capture mode used to capture a Window (Screen, GDI, Aero, AeroTransparent, Auto).", DefaultValue = "Auto")] + public WindowCaptureMode WindowCaptureMode; + [IniProperty("WindowCaptureAllChildLocations", Description = "Enable/disable capture all children, very slow but will make it possible to use this information in the editor.", DefaultValue = "False")] + public bool WindowCaptureAllChildLocations; + + [IniProperty("DWMBackgroundColor", Description = "The background color for a DWM window capture.")] + public Color DWMBackgroundColor; + + [IniProperty("PlayCameraSound", LanguageKey = "settings_playsound", Description = "Play a camera sound after taking a capture.", DefaultValue = "false")] + public bool PlayCameraSound = false; + [IniProperty("ShowTrayNotification", LanguageKey = "settings_shownotify", Description = "Show a notification from the systray when a capture is taken.", DefaultValue = "true")] + public bool ShowTrayNotification = true; + + [IniProperty("OutputFilePath", Description = "Output file path.")] + public string OutputFilePath; + [IniProperty("OutputFileAllowOverwrite", Description = "If the target file already exists True will make Greenshot always overwrite and False will display a 'Save-As' dialog.", DefaultValue = "true")] + public bool OutputFileAllowOverwrite; + [IniProperty("OutputFileFilenamePattern", Description = "Filename pattern for screenshot.", DefaultValue = "${capturetime:d\"yyyy-MM-dd HH_mm_ss\"}-${title}")] + public string OutputFileFilenamePattern; + [IniProperty("OutputFileFormat", Description = "Default file type for writing screenshots. (bmp, gif, jpg, png, tiff)", DefaultValue = "png")] + public OutputFormat OutputFileFormat = OutputFormat.png; + [IniProperty("OutputFileReduceColors", Description = "If set to true, than the colors of the output file are reduced to 256 (8-bit) colors", DefaultValue = "false")] + public bool OutputFileReduceColors; + [IniProperty("OutputFileAutoReduceColors", Description = "If set to true the amount of colors is counted and if smaller than 256 the color reduction is automatically used.", DefaultValue = "true")] + public bool OutputFileAutoReduceColors; + + [IniProperty("OutputFileCopyPathToClipboard", Description = "When saving a screenshot, copy the path to the clipboard?", DefaultValue = "true")] + public bool OutputFileCopyPathToClipboard; + [IniProperty("OutputFileAsFullpath", Description = "SaveAs Full path?")] + public string OutputFileAsFullpath; + + [IniProperty("OutputFileJpegQuality", Description = "JPEG file save quality in %.", DefaultValue = "80")] + public int OutputFileJpegQuality; + [IniProperty("OutputFilePromptQuality", Description = "Ask for the quality before saving?", DefaultValue = "false")] + public bool OutputFilePromptQuality; + [IniProperty("OutputFileIncrementingNumber", Description = "The number for the ${NUM} in the filename pattern, is increased automatically after each save.", DefaultValue = "1")] + public uint OutputFileIncrementingNumber; + + [IniProperty("OutputPrintPromptOptions", LanguageKey = "settings_alwaysshowprintoptionsdialog", Description = "Ask for print options when printing?", DefaultValue = "true")] + public bool OutputPrintPromptOptions; + [IniProperty("OutputPrintAllowRotate", LanguageKey = "printoptions_allowrotate", Description = "Allow rotating the picture for fitting on paper?", DefaultValue = "false")] + public bool OutputPrintAllowRotate; + [IniProperty("OutputPrintAllowEnlarge", LanguageKey = "printoptions_allowenlarge", Description = "Allow growing the picture for fitting on paper?", DefaultValue = "false")] + public bool OutputPrintAllowEnlarge; + [IniProperty("OutputPrintAllowShrink", LanguageKey = "printoptions_allowshrink", Description = "Allow shrinking the picture for fitting on paper?", DefaultValue = "true")] + public bool OutputPrintAllowShrink; + [IniProperty("OutputPrintCenter", LanguageKey = "printoptions_allowcenter", Description = "Center image when printing?", DefaultValue = "true")] + public bool OutputPrintCenter; + [IniProperty("OutputPrintInverted", LanguageKey = "printoptions_inverted", Description = "Print image inverted (use e.g. for console captures)", DefaultValue = "false")] + public bool OutputPrintInverted; + [IniProperty("OutputPrintGrayscale", LanguageKey = "printoptions_printgrayscale", Description = "Force grayscale printing", DefaultValue = "false")] + public bool OutputPrintGrayscale; + [IniProperty("OutputPrintMonochrome", LanguageKey = "printoptions_printmonochrome", Description = "Force monorchrome printing", DefaultValue = "false")] + public bool OutputPrintMonochrome; + [IniProperty("OutputPrintMonochromeThreshold", Description = "Threshold for monochrome filter (0 - 255), lower value means less black", DefaultValue = "127")] + public byte OutputPrintMonochromeThreshold; + [IniProperty("OutputPrintFooter", LanguageKey = "printoptions_timestamp", Description = "Print footer on print?", DefaultValue = "true")] + public bool OutputPrintFooter; + [IniProperty("OutputPrintFooterPattern", Description = "Footer pattern", DefaultValue = "${capturetime:d\"D\"} ${capturetime:d\"T\"} - ${title}")] + public string OutputPrintFooterPattern; + [IniProperty("NotificationSound", Description = "The wav-file to play when a capture is taken, loaded only once at the Greenshot startup", DefaultValue = "default")] + public string NotificationSound; + [IniProperty("UseProxy", Description = "Use your global proxy?", DefaultValue = "True")] + public bool UseProxy; + [IniProperty("IECapture", Description = "Enable/disable IE capture", DefaultValue = "True")] + public bool IECapture; + [IniProperty("IEFieldCapture", Description = "Enable/disable IE field capture, very slow but will make it possible to annotate the fields of a capture in the editor.", DefaultValue = "False")] + public bool IEFieldCapture; + [IniProperty("WindowClassesToCheckForIE", Description = "Comma separated list of Window-Classes which need to be checked for a IE instance!", DefaultValue = "AfxFrameOrView70,IMWindowClass")] + public List WindowClassesToCheckForIE; + [IniProperty("AutoCropDifference", Description = "Sets how to compare the colors for the autocrop detection, the higher the more is 'selected'. Possible values are from 0 to 255, where everything above ~150 doesn't make much sense!", DefaultValue = "10")] + public int AutoCropDifference; + + [IniProperty("IncludePlugins", Description = "Comma separated list of Plugins which are allowed. If something in the list, than every plugin not in the list will not be loaded!")] + public List IncludePlugins; + [IniProperty("ExcludePlugins", Description = "Comma separated list of Plugins which are NOT allowed.")] + public List ExcludePlugins; + [IniProperty("ExcludeDestinations", Description = "Comma separated list of destinations which should be disabled.", DefaultValue = "OneNote")] + public List ExcludeDestinations; + + [IniProperty("UpdateCheckInterval", Description = "How many days between every update check? (0=no checks)", DefaultValue = "1")] + public int UpdateCheckInterval; + [IniProperty("LastUpdateCheck", Description = "Last update check")] + public DateTime LastUpdateCheck; + + [IniProperty("DisableSettings", Description = "Enable/disable the access to the settings, can only be changed manually in this .ini", DefaultValue = "False")] + public bool DisableSettings; + [IniProperty("DisableQuickSettings", Description = "Enable/disable the access to the quick settings, can only be changed manually in this .ini", DefaultValue = "False")] + public bool DisableQuickSettings; + [IniProperty("DisableTrayicon", Description = "Disable the trayicon, can only be changed manually in this .ini", DefaultValue = "False")] + public bool HideTrayicon; + [IniProperty("HideExpertSettings", Description = "Hide expert tab in the settings, can only be changed manually in this .ini", DefaultValue = "False")] + public bool HideExpertSettings; + + [IniProperty("ThumnailPreview", Description = "Enable/disable thumbnail previews", DefaultValue = "True")] + public bool ThumnailPreview; + + [IniProperty("NoGDICaptureForProduct", Description = "List of products for which GDI capturing doesn't work.", DefaultValue = "IntelliJ,IDEA")] + public List NoGDICaptureForProduct; + [IniProperty("NoDWMCaptureForProduct", Description = "List of products for which DWM capturing doesn't work.", DefaultValue = "Citrix,ICA,Client")] + public List NoDWMCaptureForProduct; + + [IniProperty("OptimizeForRDP", Description = "Make some optimizations for usage with remote desktop", DefaultValue = "False")] + public bool OptimizeForRDP; + [IniProperty("MinimizeWorkingSetSize", Description = "Optimize memory footprint, but with a performance penalty!", DefaultValue = "False")] + public bool MinimizeWorkingSetSize; + + [IniProperty("WindowCaptureRemoveCorners", Description = "Remove the corners from a window capture", DefaultValue = "True")] + public bool WindowCaptureRemoveCorners; + + [IniProperty("CheckForUnstable", Description = "Also check for unstable version updates", DefaultValue = "False")] + public bool CheckForUnstable; + + [IniProperty("ActiveTitleFixes", Description = "The fixes that are active.")] + public List ActiveTitleFixes; + + [IniProperty("TitleFixMatcher", Description = "The regular expressions to match the title with.")] + public Dictionary TitleFixMatcher; + + [IniProperty("TitleFixReplacer", Description = "The replacements for the matchers.")] + public Dictionary TitleFixReplacer; + + [IniProperty("ExperimentalFeatures", Description = "A list of experimental features, this allows us to test certain features before releasing them.", ExcludeIfNull = true)] + public List ExperimentalFeatures; + + [IniProperty("EnableSpecialDIBClipboardReader", Description = "Enable a special DIB clipboard reader", DefaultValue = "True")] + public bool EnableSpecialDIBClipboardReader; + + [IniProperty("WindowCornerCutShape", Description = "The cutshape which is used to remove the window corners, is mirrorred for all corners", DefaultValue = "5,3,2,1,1")] + public List WindowCornerCutShape; + + [IniProperty("LeftClickAction", Description = "Specify what action is made if the tray icon is left clicked, if a double-click action is specified this action is initiated after a delay (configurable via the windows double-click speed)", DefaultValue = "SHOW_CONTEXT_MENU")] + public ClickActions LeftClickAction; + + [IniProperty("DoubleClickAction", Description = "Specify what action is made if the tray icon is double clicked", DefaultValue = "OPEN_LAST_IN_EXPLORER")] + public ClickActions DoubleClickAction; + + [IniProperty("ZoomerEnabled", Description = "Sets if the zoomer is enabled", DefaultValue = "True")] + public bool ZoomerEnabled; + + [IniProperty("MaxMenuItemLength", Description = "Maximum length of submenu items in the context menu, making this longer might cause context menu issues on dual screen systems.", DefaultValue = "25")] + public int MaxMenuItemLength; + + [IniProperty("MailApiTo", Description = "The 'to' field for the email destination (settings for Outlook can be found under the Office section)", DefaultValue = "")] + public string MailApiTo; + [IniProperty("MailApiCC", Description = "The 'CC' field for the email destination (settings for Outlook can be found under the Office section)", DefaultValue = "")] + public string MailApiCC; + [IniProperty("MailApiBCC", Description = "The 'BCC' field for the email destination (settings for Outlook can be found under the Office section)", DefaultValue = "")] + public string MailApiBCC; + + // Specifies what THIS build is + public BuildStates BuildState = BuildStates.UNSTABLE; + + /// + /// A helper method which returns true if the supplied experimental feature is enabled + /// + /// + /// + public bool isExperimentalFeatureEnabled(string experimentalFeature) + { + return (ExperimentalFeatures != null && ExperimentalFeatures.Contains(experimentalFeature)); + } + + /// + /// Supply values we can't put as defaults + /// + /// The property to return a default for + /// object with the default value for the supplied property + public override object GetDefault(string property) + { + switch (property) + { + case "PluginWhitelist": + case "PluginBacklist": + return new List(); + case "OutputFileAsFullpath": + if (IniConfig.IsPortable) + { + return Path.Combine(Application.StartupPath, @"..\..\Documents\Pictures\Greenshots\dummy.png"); + } + else + { + return Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "dummy.png"); + } + case "OutputFilePath": + if (IniConfig.IsPortable) + { + string pafOutputFilePath = Path.Combine(Application.StartupPath, @"..\..\Documents\Pictures\Greenshots"); + if (!Directory.Exists(pafOutputFilePath)) + { + try + { + Directory.CreateDirectory(pafOutputFilePath); + return pafOutputFilePath; + } + catch (Exception ex) + { + LOG.Warn(ex); + // Problem creating directory, fallback to Desktop + } + } + else + { + return pafOutputFilePath; + } + } + return Environment.GetFolderPath(Environment.SpecialFolder.Desktop); + case "DWMBackgroundColor": + return Color.Transparent; + case "ActiveTitleFixes": + List activeDefaults = new List(); + activeDefaults.Add("Firefox"); + activeDefaults.Add("IE"); + activeDefaults.Add("Chrome"); + return activeDefaults; + case "TitleFixMatcher": + Dictionary matcherDefaults = new Dictionary(); + matcherDefaults.Add("Firefox", " - Mozilla Firefox.*"); + matcherDefaults.Add("IE", " - (Microsoft|Windows) Internet Explorer.*"); + matcherDefaults.Add("Chrome", " - Google Chrome.*"); + return matcherDefaults; + case "TitleFixReplacer": + Dictionary replacerDefaults = new Dictionary(); + replacerDefaults.Add("Firefox", ""); + replacerDefaults.Add("IE", ""); + replacerDefaults.Add("Chrome", ""); + return replacerDefaults; + } + return null; + } + + /// + /// This method will be called before converting the property, making to possible to correct a certain value + /// Can be used when migration is needed + /// + /// The name of the property + /// The string value of the property + /// string with the propertyValue, modified or not... + public override string PreCheckValue(string propertyName, string propertyValue) + { + // Changed the separator, now we need to correct this + if ("Destinations".Equals(propertyName)) + { + if (propertyValue != null) + { + return propertyValue.Replace('|', ','); + } + } + if ("OutputFilePath".Equals(propertyName)) + { + if (string.IsNullOrEmpty(propertyValue)) + { + return null; + } + } + return base.PreCheckValue(propertyName, propertyValue); + } + + /// + /// This method will be called after reading the configuration, so eventually some corrections can be made + /// + public override void AfterLoad() + { + // Comment with releases + // CheckForUnstable = true; + + if (OutputDestinations == null) + { + OutputDestinations = new List(); + } + + // Make sure there is an output! + if (OutputDestinations.Count == 0) + { + OutputDestinations.Add("Editor"); + } + + // Prevent both settings at once, bug #3435056 + if (OutputDestinations.Contains("Clipboard") && OutputFileCopyPathToClipboard) + { + OutputFileCopyPathToClipboard = false; + } + + // Make sure we have clipboard formats, otherwise a paste doesn't make sense! + if (ClipboardFormats == null || ClipboardFormats.Count == 0) + { + ClipboardFormats = new List(); + ClipboardFormats.Add(ClipboardFormat.PNG); + ClipboardFormats.Add(ClipboardFormat.HTML); + ClipboardFormats.Add(ClipboardFormat.DIB); + } + + // Make sure the lists are lowercase, to speedup the check + if (NoGDICaptureForProduct != null) + { + // Fix error in configuration + if (NoGDICaptureForProduct.Count == 1) + { + if ("intellij idea".Equals(NoGDICaptureForProduct[0])) + { + NoGDICaptureForProduct[0] = "intellij,idea"; + IsDirty = true; + } + } + for (int i = 0; i < NoGDICaptureForProduct.Count; i++) + { + NoGDICaptureForProduct[i] = NoGDICaptureForProduct[i].ToLower(); + } + } + if (NoDWMCaptureForProduct != null) + { + // Fix error in configuration + if (NoDWMCaptureForProduct.Count == 1) + { + if ("citrix ica client".Equals(NoDWMCaptureForProduct[0])) + { + NoDWMCaptureForProduct[0] = "citrix,ica,client"; + IsDirty = true; + } + } + for (int i = 0; i < NoDWMCaptureForProduct.Count; i++) + { + NoDWMCaptureForProduct[i] = NoDWMCaptureForProduct[i].ToLower(); + } + } + + if (AutoCropDifference < 0) + { + AutoCropDifference = 0; + } + if (AutoCropDifference > 255) + { + AutoCropDifference = 255; + } + } + } +} \ No newline at end of file diff --git a/GreenshotImageEditor/Configuration/EditorConfiguration.cs b/GreenshotImageEditor/Configuration/EditorConfiguration.cs new file mode 100644 index 000000000..c19c66237 --- /dev/null +++ b/GreenshotImageEditor/Configuration/EditorConfiguration.cs @@ -0,0 +1,141 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2013 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: http://getgreenshot.org/ + * The Greenshot project is hosted on Sourceforge: http://sourceforge.net/projects/greenshot/ + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using Greenshot.Drawing.Fields; +using Greenshot.IniFile; +using GreenshotPlugin.UnmanagedHelpers; +using System; +using System.Collections.Generic; +using System.Drawing; + +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; + + [IniProperty("LastFieldValue", Separator = "|", Description = "Field values, make sure the last used settings are re-used")] + public Dictionary LastUsedFieldValues; + + [IniProperty("MatchSizeToCapture", Description = "Match the editor window size to the capture", DefaultValue = "True")] + public bool MatchSizeToCapture; + [IniProperty("WindowPlacementFlags", Description = "Placement flags", DefaultValue = "0")] + public WindowPlacementFlags WindowPlacementFlags; + [IniProperty("WindowShowCommand", Description = "Show command", DefaultValue = "Normal")] + public ShowWindowCommand ShowWindowCommand; + [IniProperty("WindowMinPosition", Description = "Position of minimized window", DefaultValue = "-1,-1")] + public Point WindowMinPosition; + [IniProperty("WindowMaxPosition", Description = "Position of maximized window", DefaultValue = "-1,-1")] + public Point WindowMaxPosition; + [IniProperty("WindowNormalPosition", Description = "Position of normal window", DefaultValue = "100,100,600,500")] + public Rectangle WindowNormalPosition; + [IniProperty("ReuseEditor", Description = "Reuse already open editor", DefaultValue = "false")] + public bool ReuseEditor; + [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; + + [IniProperty("SuppressSaveDialogAtClose", Description = "Suppressed the 'do you want to save' dialog when closing the editor.", DefaultValue = "False")] + public bool SuppressSaveDialogAtClose; + + /// Type of the class for which to create the field + /// FieldType of the field to construct + /// 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 Field CreateField(Type requestingType, FieldType 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); + } + Field returnField = new Field(fieldType, requestingType); + returnField.Value = fieldValue; + return returnField; + } + + public void UpdateLastFieldValue(Field 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.myValue; + } + else + { + LastUsedFieldValues.Add(requestedField, field.myValue); + } + } + + 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/GreenshotImageEditor/Configuration/LanguageKeys.cs b/GreenshotImageEditor/Configuration/LanguageKeys.cs new file mode 100644 index 000000000..2f0d5d855 --- /dev/null +++ b/GreenshotImageEditor/Configuration/LanguageKeys.cs @@ -0,0 +1,241 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2013 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: http://getgreenshot.org/ + * The Greenshot project is hosted on Sourceforge: http://sourceforge.net/projects/greenshot/ + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +namespace Greenshot.Configuration +{ + public enum LangKey + { + none, + about_bugs, + about_donations, + about_host, + about_icons, + about_license, + about_title, + about_translation, + application_title, + bugreport_cancel, + bugreport_info, + bugreport_title, + clipboard_error, + clipboard_inuse, + colorpicker_alpha, + colorpicker_apply, + colorpicker_blue, + colorpicker_green, + colorpicker_htmlcolor, + colorpicker_recentcolors, + colorpicker_red, + colorpicker_title, + colorpicker_transparent, + config_unauthorizedaccess_write, + contextmenu_about, + contextmenu_capturearea, + contextmenu_captureclipboard, + contextmenu_capturefullscreen, + contextmenu_capturefullscreen_all, + contextmenu_capturefullscreen_left, + contextmenu_capturefullscreen_top, + contextmenu_capturefullscreen_right, + contextmenu_capturefullscreen_bottom, + contextmenu_capturelastregion, + contextmenu_capturewindow, + contextmenu_donate, + contextmenu_exit, + contextmenu_help, + contextmenu_openfile, + contextmenu_quicksettings, + contextmenu_settings, + contextmenu_captureie, + contextmenu_openrecentcapture, + editor_align_bottom, + editor_align_center, + editor_align_horizontal, + editor_align_middle, + editor_align_left, + editor_align_right, + editor_align_top, + editor_align_vertical, + editor_arrange, + editor_arrowheads, + editor_arrowheads_both, + editor_arrowheads_end, + editor_arrowheads_none, + editor_arrowheads_start, + editor_backcolor, + editor_blur_radius, + editor_bold, + editor_brightness, + editor_cancel, + editor_clipboardfailed, + editor_close, + editor_close_on_save, + editor_close_on_save_title, + editor_confirm, + editor_copyimagetoclipboard, + editor_copypathtoclipboard, + editor_copytoclipboard, + editor_crop, + editor_cursortool, + editor_cuttoclipboard, + editor_deleteelement, + editor_downonelevel, + editor_downtobottom, + editor_drawarrow, + editor_drawellipse, + editor_drawhighlighter, + editor_drawline, + editor_drawfreehand, + editor_drawrectangle, + editor_drawtextbox, + editor_duplicate, + editor_edit, + editor_email, + editor_file, + editor_fontsize, + editor_forecolor, + editor_highlight_area, + editor_highlight_grayscale, + editor_highlight_mode, + editor_highlight_text, + editor_highlight_magnify, + editor_pixel_size, + editor_imagesaved, + editor_italic, + editor_load_objects, + editor_magnification_factor, + editor_match_capture_size, + editor_obfuscate, + editor_obfuscate_blur, + editor_obfuscate_mode, + editor_obfuscate_pixelize, + editor_object, + editor_opendirinexplorer, + editor_pastefromclipboard, + editor_preview_quality, + editor_print, + editor_save, + editor_save_objects, + editor_saveas, + editor_selectall, + editor_senttoprinter, + editor_shadow, + editor_torn_edge, + editor_border, + editor_grayscale, + editor_effects, + editor_storedtoclipboard, + editor_thickness, + editor_title, + editor_uponelevel, + editor_uptotop, + editor_autocrop, + editor_undo, + editor_redo, + editor_insertwindow, + editor_resetsize, + error, + error_multipleinstances, + error_nowriteaccess, + error_openfile, + error_openlink, + error_save, + help_title, + jpegqualitydialog_choosejpegquality, + qualitydialog_dontaskagain, + qualitydialog_title, + settings_reducecolors, + print_error, + printoptions_allowcenter, + printoptions_allowenlarge, + printoptions_allowrotate, + printoptions_allowshrink, + printoptions_colors, + printoptions_dontaskagain, + printoptions_pagelayout, + printoptions_printcolor, + printoptions_printgrayscale, + printoptions_printmonochrome, + printoptions_timestamp, + printoptions_inverted, + printoptions_title, + quicksettings_destination_file, + settings_alwaysshowqualitydialog, + settings_alwaysshowprintoptionsdialog, + settings_applicationsettings, + settings_autostartshortcut, + settings_capture, + settings_capture_mousepointer, + settings_capture_windows_interactive, + settings_copypathtoclipboard, + settings_destination, + settings_destination_clipboard, + settings_destination_editor, + settings_destination_email, + settings_destination_file, + settings_destination_fileas, + settings_destination_printer, + settings_destination_picker, + settings_editor, + settings_filenamepattern, + settings_general, + settings_iecapture, + settings_jpegquality, + settings_qualitysettings, + settings_language, + settings_message_filenamepattern, + settings_output, + settings_playsound, + settings_plugins, + settings_plugins_name, + settings_plugins_version, + settings_plugins_createdby, + settings_plugins_dllpath, + settings_preferredfilesettings, + settings_primaryimageformat, + settings_printer, + settings_printoptions, + settings_registerhotkeys, + settings_showflashlight, + settings_storagelocation, + settings_title, + settings_tooltip_filenamepattern, + settings_tooltip_language, + settings_tooltip_primaryimageformat, + settings_tooltip_registerhotkeys, + settings_tooltip_storagelocation, + settings_visualization, + settings_shownotify, + settings_waittime, + settings_windowscapture, + settings_window_capture_mode, + settings_network, + settings_checkperiod, + settings_usedefaultproxy, + tooltip_firststart, + warning, + warning_hotkeys, + hotkeys, + wait_ie_capture, + update_found, + exported_to + } +} \ No newline at end of file diff --git a/GreenshotImageEditor/Controls/AnimatingForm.cs b/GreenshotImageEditor/Controls/AnimatingForm.cs new file mode 100644 index 000000000..b060b3b57 --- /dev/null +++ b/GreenshotImageEditor/Controls/AnimatingForm.cs @@ -0,0 +1,141 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2013 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: http://getgreenshot.org/ + * The Greenshot project is hosted on Sourceforge: http://sourceforge.net/projects/greenshot/ + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using GreenshotPlugin.UnmanagedHelpers; +using System; +using System.Windows.Forms; + +namespace GreenshotPlugin.Controls +{ + /// + /// Extend this Form to have the possibility for animations on your form + /// + public class AnimatingForm : GreenshotForm + { + private const int DEFAULT_VREFRESH = 60; + private int vRefresh = 0; + private Timer timer = null; + + /// + /// This flag specifies if any animation is used + /// + protected bool EnableAnimation + { + get; + set; + } + + /// + /// Vertical Refresh Rate + /// + protected int VRefresh + { + get + { + if (vRefresh == 0) + { + // get te hDC of the desktop to get the VREFRESH + using (SafeWindowDCHandle desktopHandle = SafeWindowDCHandle.fromDesktop()) + { + vRefresh = GDI32.GetDeviceCaps(desktopHandle, DeviceCaps.VREFRESH); + } + } + // A vertical refresh rate value of 0 or 1 represents the display hardware's default refresh rate. + // As there is currently no know way to get the default, we guess it. + if (vRefresh <= 1) + { + vRefresh = DEFAULT_VREFRESH; + } + return vRefresh; + } + } + + /// + /// Check if we are in a Terminal Server session OR need to optimize for RDP / remote desktop connections + /// + protected bool isTerminalServerSession + { + get + { + return coreConfiguration.OptimizeForRDP || SystemInformation.TerminalServerSession; + } + } + + /// + /// Calculate the amount of frames that an animation takes + /// + /// + /// Number of frames, 1 if in Terminal Server Session + protected int FramesForMillis(int milliseconds) + { + // If we are in a Terminal Server Session we return 1 + if (isTerminalServerSession) + { + return 1; + } + return milliseconds / VRefresh; + } + + /// + /// Initialize the animation + /// + protected AnimatingForm() + { + Load += delegate + { + if (EnableAnimation) + { + timer = new Timer(); + timer.Interval = 1000 / VRefresh; + timer.Tick += timer_Tick; + timer.Start(); + } + }; + + // Unregister at close + FormClosing += delegate + { + if (timer != null) + { + timer.Stop(); + } + }; + } + + /// + /// The tick handler initiates the animation. + /// + /// + /// + private void timer_Tick(object sender, EventArgs e) + { + Animate(); + } + + /// + /// This method will be called every frame, so implement your animation/redraw logic here. + /// + protected virtual void Animate() + { + throw new NotImplementedException(); + } + } +} \ No newline at end of file diff --git a/GreenshotImageEditor/Controls/BackgroundForm.cs b/GreenshotImageEditor/Controls/BackgroundForm.cs new file mode 100644 index 000000000..626f3ba1c --- /dev/null +++ b/GreenshotImageEditor/Controls/BackgroundForm.cs @@ -0,0 +1,116 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2013 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: http://getgreenshot.org/ + * The Greenshot project is hosted on Sourceforge: http://sourceforge.net/projects/greenshot/ + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using System; +using System.Drawing; +using System.Threading; +using System.Windows.Forms; +using GreenshotPlugin.Core; + +namespace GreenshotPlugin.Controls +{ + /// + /// Description of PleaseWaitForm. + /// + public partial class BackgroundForm : Form + { + private volatile bool shouldClose = false; + + private void BackgroundShowDialog() + { + ShowDialog(); + } + + public static BackgroundForm ShowAndWait(string title, string text) + { + BackgroundForm backgroundForm = new BackgroundForm(title, text); + // Show form in background thread + Thread backgroundTask = new Thread(backgroundForm.BackgroundShowDialog); + backgroundForm.Name = "Background form"; + backgroundTask.IsBackground = true; + backgroundTask.SetApartmentState(ApartmentState.STA); + backgroundTask.Start(); + return backgroundForm; + } + + public BackgroundForm(string title, string text) + { + // + // The InitializeComponent() call is required for Windows Forms designer support. + // + InitializeComponent(); + Icon = GreenshotResources.getGreenshotIcon(); + shouldClose = false; + Text = title; + label_pleasewait.Text = text; + FormClosing += PreventFormClose; + timer_checkforclose.Start(); + } + + // Can be used instead of ShowDialog + public new void Show() + { + base.Show(); + bool positioned = false; + foreach (Screen screen in Screen.AllScreens) + { + if (screen.Bounds.Contains(Cursor.Position)) + { + positioned = true; + Location = new Point(screen.Bounds.X + (screen.Bounds.Width / 2) - (Width / 2), screen.Bounds.Y + (screen.Bounds.Height / 2) - (Height / 2)); + break; + } + } + if (!positioned) + { + Location = new Point(Cursor.Position.X - Width / 2, Cursor.Position.Y - Height / 2); + } + } + + private void PreventFormClose(object sender, FormClosingEventArgs e) + { + if (!shouldClose) + { + e.Cancel = true; + } + } + + private void Timer_checkforcloseTick(object sender, EventArgs e) + { + if (shouldClose) + { + timer_checkforclose.Stop(); + BeginInvoke(new EventHandler(delegate { Close(); })); + } + } + + public void CloseDialog() + { + shouldClose = true; + Application.DoEvents(); + } + + private void BackgroundFormFormClosing(object sender, FormClosingEventArgs e) + { + timer_checkforclose.Stop(); + } + } +} \ No newline at end of file diff --git a/GreenshotImageEditor/Controls/BackgroundForm.designer.cs b/GreenshotImageEditor/Controls/BackgroundForm.designer.cs new file mode 100644 index 000000000..8df6f0e6d --- /dev/null +++ b/GreenshotImageEditor/Controls/BackgroundForm.designer.cs @@ -0,0 +1,99 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2013 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: http://getgreenshot.org/ + * The Greenshot project is hosted on Sourceforge: http://sourceforge.net/projects/greenshot/ + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +namespace GreenshotPlugin.Controls +{ + partial class BackgroundForm + { + /// + /// 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() + { + this.components = new System.ComponentModel.Container(); + this.label_pleasewait = new System.Windows.Forms.Label(); + this.timer_checkforclose = new System.Windows.Forms.Timer(this.components); + this.SuspendLayout(); + // + // label_pleasewait + // + this.label_pleasewait.AutoSize = true; + this.label_pleasewait.Dock = System.Windows.Forms.DockStyle.Fill; + this.label_pleasewait.Location = new System.Drawing.Point(0, 0); + this.label_pleasewait.Name = "label_pleasewait"; + this.label_pleasewait.Padding = new System.Windows.Forms.Padding(10); + this.label_pleasewait.Size = new System.Drawing.Size(90, 33); + this.label_pleasewait.TabIndex = 0; + this.label_pleasewait.Text = "Please wait..."; + this.label_pleasewait.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; + this.label_pleasewait.UseWaitCursor = true; + // + // timer_checkforclose + // + this.timer_checkforclose.Interval = 200; + this.timer_checkforclose.Tick += new System.EventHandler(this.Timer_checkforcloseTick); + // + // BackgroundForm + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.AutoSize = true; + this.ClientSize = new System.Drawing.Size(169, 52); + this.ControlBox = true; + this.Controls.Add(this.label_pleasewait); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "BackgroundForm"; + this.SizeGripStyle = System.Windows.Forms.SizeGripStyle.Hide; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; + this.Text = "Greenshot"; + this.TopMost = true; + this.UseWaitCursor = true; + this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.BackgroundFormFormClosing); + this.ResumeLayout(false); + this.PerformLayout(); + } + private System.Windows.Forms.Timer timer_checkforclose; + private System.Windows.Forms.Label label_pleasewait; + } +} diff --git a/GreenshotImageEditor/Controls/BindableToolStripButton.cs b/GreenshotImageEditor/Controls/BindableToolStripButton.cs new file mode 100644 index 000000000..f6d7f5e0f --- /dev/null +++ b/GreenshotImageEditor/Controls/BindableToolStripButton.cs @@ -0,0 +1,54 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2013 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: http://getgreenshot.org/ + * The Greenshot project is hosted on Sourceforge: http://sourceforge.net/projects/greenshot/ + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using GreenshotPlugin.Controls; +using System; +using System.ComponentModel; +using System.Windows.Forms; + +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() + : base() + { + CheckedChanged += BindableToolStripButton_CheckedChanged; + } + + private void BindableToolStripButton_CheckedChanged(object sender, EventArgs e) + { + if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs("Checked")); + } + } +} \ No newline at end of file diff --git a/GreenshotImageEditor/Controls/BindableToolStripComboBox.cs b/GreenshotImageEditor/Controls/BindableToolStripComboBox.cs new file mode 100644 index 000000000..0044b85c5 --- /dev/null +++ b/GreenshotImageEditor/Controls/BindableToolStripComboBox.cs @@ -0,0 +1,57 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2013 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: http://getgreenshot.org/ + * The Greenshot project is hosted on Sourceforge: http://sourceforge.net/projects/greenshot/ + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using GreenshotPlugin.Controls; +using System; +using System.ComponentModel; +using System.Windows.Forms; + +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() + : base() + { + SelectedIndexChanged += BindableToolStripComboBox_SelectedIndexChanged; + } + + private void BindableToolStripComboBox_SelectedIndexChanged(object sender, EventArgs e) + { + if (PropertyChanged != null) + { + PropertyChanged(this, new PropertyChangedEventArgs("SelectedItem")); + } + } + } +} \ No newline at end of file diff --git a/GreenshotImageEditor/Controls/BindableToolStripDropDownButton.cs b/GreenshotImageEditor/Controls/BindableToolStripDropDownButton.cs new file mode 100644 index 000000000..1397806da --- /dev/null +++ b/GreenshotImageEditor/Controls/BindableToolStripDropDownButton.cs @@ -0,0 +1,84 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2013 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: http://getgreenshot.org/ + * The Greenshot project is hosted on Sourceforge: http://sourceforge.net/projects/greenshot/ + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using GreenshotPlugin.Controls; +using System.ComponentModel; +using System.Windows.Forms; + +namespace Greenshot.Controls +{ + /// + /// A simple ToolStripDropDownButton implementing INotifyPropertyChanged for data binding. + /// Also, when a DropDownItem is selected, the DropDownButton adops 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 BindableToolStripDropDownButton() + { + } + + 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; + if (PropertyChanged != null) PropertyChanged(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; + if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs("SelectedTag")); + } + } + } +} \ No newline at end of file diff --git a/GreenshotImageEditor/Controls/ColorButton.cs b/GreenshotImageEditor/Controls/ColorButton.cs new file mode 100644 index 000000000..d9011aa80 --- /dev/null +++ b/GreenshotImageEditor/Controls/ColorButton.cs @@ -0,0 +1,101 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2013 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: http://getgreenshot.org/ + * The Greenshot project is hosted on Sourceforge: http://sourceforge.net/projects/greenshot/ + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using GreenshotPlugin.Controls; +using System; +using System.ComponentModel; +using System.Drawing; +using System.Drawing.Drawing2D; +using System.Windows.Forms; + +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) + { + ColorDialog colorDialog = ColorDialog.GetInstance(); + 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) + { + if (!colorDialog.Color.Equals(SelectedColor)) + { + SelectedColor = colorDialog.Color; + if (PropertyChanged != null) + { + PropertyChanged(this, new PropertyChangedEventArgs("SelectedColor")); + } + } + } + } + } +} \ No newline at end of file diff --git a/GreenshotImageEditor/Controls/FontFamilyComboBox.cs b/GreenshotImageEditor/Controls/FontFamilyComboBox.cs new file mode 100644 index 000000000..556de85da --- /dev/null +++ b/GreenshotImageEditor/Controls/FontFamilyComboBox.cs @@ -0,0 +1,68 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2013 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: http://getgreenshot.org/ + * The Greenshot project is hosted on Sourceforge: http://sourceforge.net/projects/greenshot/ + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +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() + : base() + { + ComboBox.DataSource = FontFamily.Families; + ComboBox.DisplayMember = "Name"; + SelectedIndexChanged += BindableToolStripComboBox_SelectedIndexChanged; + } + + private void BindableToolStripComboBox_SelectedIndexChanged(object sender, EventArgs e) + { + if (PropertyChanged != null) + { + 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/GreenshotImageEditor/Controls/FormWithoutActivation.cs b/GreenshotImageEditor/Controls/FormWithoutActivation.cs new file mode 100644 index 000000000..c02ef1910 --- /dev/null +++ b/GreenshotImageEditor/Controls/FormWithoutActivation.cs @@ -0,0 +1,36 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2013 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: http://getgreenshot.org/ + * The Greenshot project is hosted on Sourceforge: http://sourceforge.net/projects/greenshot/ + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using System.Windows.Forms; + +namespace GreenshotPlugin.Controls +{ + /// + /// FormWithoutActivation is exactly like a normal form, but doesn't activate (steal focus) + /// + public class FormWithoutActivation : Form + { + protected override bool ShowWithoutActivation + { + get { return true; } + } + } +} \ No newline at end of file diff --git a/GreenshotImageEditor/Controls/GreenshotButton.cs b/GreenshotImageEditor/Controls/GreenshotButton.cs new file mode 100644 index 000000000..24696eff5 --- /dev/null +++ b/GreenshotImageEditor/Controls/GreenshotButton.cs @@ -0,0 +1,36 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2013 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: http://getgreenshot.org/ + * The Greenshot project is hosted on Sourceforge: http://sourceforge.net/projects/greenshot/ + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using System.ComponentModel; +using System.Windows.Forms; + +namespace GreenshotPlugin.Controls +{ + public class GreenshotButton : Button, IGreenshotLanguageBindable + { + [Category("Greenshot"), DefaultValue(null), Description("Specifies key of the language file to use when displaying the text.")] + public string LanguageKey + { + get; + set; + } + } +} \ No newline at end of file diff --git a/GreenshotImageEditor/Controls/GreenshotCheckBox.cs b/GreenshotImageEditor/Controls/GreenshotCheckBox.cs new file mode 100644 index 000000000..5c4af077f --- /dev/null +++ b/GreenshotImageEditor/Controls/GreenshotCheckBox.cs @@ -0,0 +1,60 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2013 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: http://getgreenshot.org/ + * The Greenshot project is hosted on Sourceforge: http://sourceforge.net/projects/greenshot/ + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using System.ComponentModel; +using System.Windows.Forms; + +namespace GreenshotPlugin.Controls +{ + /// + /// Description of GreenshotCheckbox. + /// + public class GreenshotCheckBox : CheckBox, IGreenshotLanguageBindable, IGreenshotConfigBindable + { + [Category("Greenshot"), DefaultValue(null), Description("Specifies key of the language file to use when displaying the text.")] + public string LanguageKey + { + get; + set; + } + + private string sectionName = "Core"; + [Category("Greenshot"), DefaultValue("Core"), Description("Specifies the Ini-Section to map this control with.")] + public string SectionName + { + get + { + return sectionName; + } + set + { + sectionName = value; + } + } + + [Category("Greenshot"), DefaultValue(null), Description("Specifies the property name to map the configuration.")] + public string PropertyName + { + get; + set; + } + } +} \ No newline at end of file diff --git a/GreenshotImageEditor/Controls/GreenshotColumnSorter.cs b/GreenshotImageEditor/Controls/GreenshotColumnSorter.cs new file mode 100644 index 000000000..3d4452fc3 --- /dev/null +++ b/GreenshotImageEditor/Controls/GreenshotColumnSorter.cs @@ -0,0 +1,138 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2013 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: http://getgreenshot.org/ + * The Greenshot project is hosted on Sourceforge: http://sourceforge.net/projects/greenshot/ + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using System.Collections; +using System.Windows.Forms; + +namespace GreenshotPlugin.Controls +{ + /// + /// This class is an implementation of the 'IComparer' interface. + /// + public class GreenshotColumnSorter : IComparer + { + /// + /// Specifies the column to be sorted + /// + private int ColumnToSort; + /// + /// Specifies the order in which to sort (i.e. 'Ascending'). + /// + private SortOrder OrderOfSort; + /// + /// Case insensitive comparer object + /// + private CaseInsensitiveComparer ObjectCompare; + + /// + /// Class constructor. Initializes various elements + /// + public GreenshotColumnSorter() + { + // Initialize the column to '0' + ColumnToSort = 0; + + // Initialize the sort order to 'none' + OrderOfSort = SortOrder.None; + + // Initialize the CaseInsensitiveComparer object + ObjectCompare = new CaseInsensitiveComparer(); + } + + /// + /// This method is inherited from the IComparer interface. It compares the two objects passed using a case insensitive comparison. + /// + /// First object to be compared + /// Second object to be compared + /// The result of the comparison. "0" if equal, negative if 'x' is less than 'y' and positive if 'x' is greater than 'y' + public int Compare(object x, object y) + { + int compareResult; + ListViewItem listviewX, listviewY; + + if (x == null && y == null) + { + return 0; + } + else if (x == null && y != null) + { + return -1; + } + else if (x != null && y == null) + { + return 1; + } + // Cast the objects to be compared to ListViewItem objects + listviewX = (ListViewItem)x; + listviewY = (ListViewItem)y; + + // Compare the two items + compareResult = ObjectCompare.Compare(listviewX.SubItems[ColumnToSort].Text, listviewY.SubItems[ColumnToSort].Text); + + // Calculate correct return value based on object comparison + if (OrderOfSort == SortOrder.Ascending) + { + // Ascending sort is selected, return normal result of compare operation + return compareResult; + } + else if (OrderOfSort == SortOrder.Descending) + { + // Descending sort is selected, return negative result of compare operation + return (-compareResult); + } + else + { + // Return '0' to indicate they are equal + return 0; + } + } + + /// + /// Gets or sets the number of the column to which to apply the sorting operation (Defaults to '0'). + /// + public int SortColumn + { + set + { + ColumnToSort = value; + } + get + { + return ColumnToSort; + } + } + + /// + /// Gets or sets the order of sorting to apply (for example, 'Ascending' or 'Descending'). + /// + public SortOrder Order + { + set + { + OrderOfSort = value; + } + get + { + return OrderOfSort; + } + } + } +} \ No newline at end of file diff --git a/GreenshotImageEditor/Controls/GreenshotComboBox.cs b/GreenshotImageEditor/Controls/GreenshotComboBox.cs new file mode 100644 index 000000000..d9ce493cb --- /dev/null +++ b/GreenshotImageEditor/Controls/GreenshotComboBox.cs @@ -0,0 +1,132 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2013 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: http://getgreenshot.org/ + * The Greenshot project is hosted on Sourceforge: http://sourceforge.net/projects/greenshot/ + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using GreenshotPlugin.Core; +using System; +using System.ComponentModel; +using System.Windows.Forms; + +namespace GreenshotPlugin.Controls +{ + public class GreenshotComboBox : ComboBox, IGreenshotConfigBindable + { + private Type enumType = null; + private Enum selectedEnum = null; + private string sectionName = "Core"; + [Category("Greenshot"), DefaultValue("Core"), Description("Specifies the Ini-Section to map this control with.")] + public string SectionName + { + get + { + return sectionName; + } + set + { + sectionName = value; + } + } + + [Category("Greenshot"), DefaultValue(null), Description("Specifies the property name to map the configuration.")] + public string PropertyName + { + get; + set; + } + + public GreenshotComboBox() + { + SelectedIndexChanged += delegate + { + StoreSelectedEnum(); + }; + } + + public void SetValue(Enum currentValue) + { + if (currentValue != null) + { + selectedEnum = currentValue; + SelectedItem = Language.Translate(currentValue); + } + } + + /// + /// This is a method to popululate the ComboBox + /// with the items from the enumeration + /// + /// TEnum to populate with + public void Populate(Type enumType) + { + // Store the enum-type, so we can work with it + this.enumType = enumType; + + var availableValues = Enum.GetValues(enumType); + Items.Clear(); + string enumTypeName = enumType.Name; + foreach (var enumValue in availableValues) + { + Items.Add(Language.Translate((Enum)enumValue)); + } + } + + /// + /// Store the selected value internally + /// + private void StoreSelectedEnum() + { + string enumTypeName = enumType.Name; + string selectedValue = SelectedItem as string; + var availableValues = Enum.GetValues(enumType); + object returnValue = null; + + try + { + returnValue = Enum.Parse(enumType, selectedValue); + } + catch (Exception) + { + } + + foreach (Enum enumValue in availableValues) + { + string enumKey = enumTypeName + "." + enumValue.ToString(); + if (Language.hasKey(enumKey)) + { + string translation = Language.GetString(enumTypeName + "." + enumValue.ToString()); + if (translation.Equals(selectedValue)) + { + returnValue = enumValue; + } + } + } + selectedEnum = (Enum)returnValue; + } + + /// + /// Get the selected enum value from the combobox, uses generics + /// + /// The enum value of the combobox + public Enum GetSelectedEnum() + { + return selectedEnum; + } + } +} \ No newline at end of file diff --git a/GreenshotImageEditor/Controls/GreenshotForm.cs b/GreenshotImageEditor/Controls/GreenshotForm.cs new file mode 100644 index 000000000..558ca42cc --- /dev/null +++ b/GreenshotImageEditor/Controls/GreenshotForm.cs @@ -0,0 +1,635 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2013 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: http://getgreenshot.org/ + * The Greenshot project is hosted on Sourceforge: http://sourceforge.net/projects/greenshot/ + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using Greenshot.IniFile; +using GreenshotPlugin.Core; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.ComponentModel.Design; +using System.IO; +using System.Reflection; +using System.Windows.Forms; + +namespace GreenshotPlugin.Controls +{ + /// + /// This form is used for automatically binding the elements of the form to the language + /// + public class GreenshotForm : Form, IGreenshotLanguageBindable + { + protected static CoreConfiguration coreConfiguration; + private static IDictionary reflectionCache = new Dictionary(); + private IComponentChangeService m_changeService; + private bool isDesignModeLanguageSet = false; + private bool applyLanguageManually = false; + private bool storeFieldsManually = false; + private IDictionary designTimeControls; + private IDictionary designTimeToolStripItems; + + static GreenshotForm() + { + if (!IsInDesignMode) + { + coreConfiguration = IniConfig.GetIniSection(); + } + } + + [Category("Greenshot"), DefaultValue(null), Description("Specifies key of the language file to use when displaying the text.")] + public string LanguageKey + { + get; + set; + } + + /// + /// Used to check the designmode during a constructor + /// + /// + protected static bool IsInDesignMode + { + get + { + return (Application.ExecutablePath.IndexOf("devenv.exe", StringComparison.OrdinalIgnoreCase) > -1) || (Application.ExecutablePath.IndexOf("sharpdevelop.exe", StringComparison.OrdinalIgnoreCase) > -1 || (Application.ExecutablePath.IndexOf("wdexpress.exe", StringComparison.OrdinalIgnoreCase) > -1)); + } + } + + protected bool ManualLanguageApply + { + get + { + return applyLanguageManually; + } + set + { + applyLanguageManually = value; + } + } + + protected bool ManualStoreFields + { + get + { + return storeFieldsManually; + } + set + { + storeFieldsManually = value; + } + } + + /// + /// Code to initialize the language etc during design time + /// + protected void InitializeForDesigner() + { + if (DesignMode) + { + designTimeControls = new Dictionary(); + designTimeToolStripItems = new Dictionary(); + try + { + ITypeResolutionService typeResService = GetService(typeof(ITypeResolutionService)) as ITypeResolutionService; + + // Add a hard-path if you are using SharpDevelop + // Language.AddLanguageFilePath(@"C:\Greenshot\Greenshot\Languages"); + + // this "type" + Assembly currentAssembly = GetType().Assembly; + string assemblyPath = typeResService.GetPathOfAssembly(currentAssembly.GetName()); + string assemblyDirectory = Path.GetDirectoryName(assemblyPath); + if (!Language.AddLanguageFilePath(Path.Combine(assemblyDirectory, @"..\..\Greenshot\Languages\"))) + { + Language.AddLanguageFilePath(Path.Combine(assemblyDirectory, @"..\..\..\Greenshot\Languages\")); + } + if (!Language.AddLanguageFilePath(Path.Combine(assemblyDirectory, @"..\..\Languages\"))) + { + Language.AddLanguageFilePath(Path.Combine(assemblyDirectory, @"..\..\..\Languages\")); + } + } + catch (Exception ex) + { + MessageBox.Show(ex.Message); + } + } + } + + /// + /// This override is only for the design-time of the form + /// + /// + protected override void OnPaint(PaintEventArgs e) + { + if (DesignMode) + { + if (!isDesignModeLanguageSet) + { + isDesignModeLanguageSet = true; + try + { + ApplyLanguage(); + } + catch (Exception) + { + } + } + } + base.OnPaint(e); + } + + protected override void OnLoad(EventArgs e) + { + if (!DesignMode) + { + if (!applyLanguageManually) + { + ApplyLanguage(); + } + FillFields(); + base.OnLoad(e); + } + else + { + LOG.Info("OnLoad called from designer."); + InitializeForDesigner(); + base.OnLoad(e); + ApplyLanguage(); + } + } + + /// + /// check if the form was closed with an OK, if so store the values in the GreenshotControls + /// + /// + protected override void OnClosed(EventArgs e) + { + if (!DesignMode && !storeFieldsManually) + { + if (DialogResult == DialogResult.OK) + { + LOG.Info("Form was closed with OK: storing field values."); + StoreFields(); + } + } + base.OnClosed(e); + } + + /// + /// This override allows the control to register event handlers for IComponentChangeService events + /// at the time the control is sited, which happens only in design mode. + /// + public override ISite Site + { + get + { + return base.Site; + } + set + { + // Clear any component change event handlers. + ClearChangeNotifications(); + + // Set the new Site value. + base.Site = value; + + m_changeService = (IComponentChangeService)GetService(typeof(IComponentChangeService)); + + // Register event handlers for component change events. + RegisterChangeNotifications(); + } + } + + private void ClearChangeNotifications() + { + // The m_changeService value is null when not in design mode, + // as the IComponentChangeService is only available at design time. + m_changeService = (IComponentChangeService)GetService(typeof(IComponentChangeService)); + + // Clear our the component change events to prepare for re-siting. + if (m_changeService != null) + { + m_changeService.ComponentChanged -= OnComponentChanged; + m_changeService.ComponentAdded -= OnComponentAdded; + } + } + + private void RegisterChangeNotifications() + { + // Register the event handlers for the IComponentChangeService events + if (m_changeService != null) + { + m_changeService.ComponentChanged += OnComponentChanged; + m_changeService.ComponentAdded += OnComponentAdded; + } + } + + /// + /// This method handles the OnComponentChanged event to display a notification. + /// + /// + /// + private void OnComponentChanged(object sender, ComponentChangedEventArgs ce) + { + if (ce.Component != null && ((IComponent)ce.Component).Site != null && ce.Member != null) + { + if ("LanguageKey".Equals(ce.Member.Name)) + { + Control control = ce.Component as Control; + if (control != null) + { + LOG.InfoFormat("Changing LanguageKey for {0} to {1}", control.Name, ce.NewValue); + ApplyLanguage(control, (string)ce.NewValue); + } + else + { + ToolStripItem item = ce.Component as ToolStripItem; + if (item != null) + { + LOG.InfoFormat("Changing LanguageKey for {0} to {1}", item.Name, ce.NewValue); + ApplyLanguage(item, (string)ce.NewValue); + } + else + { + LOG.InfoFormat("Not possible to changing LanguageKey for {0} to {1}", ce.Component.GetType(), ce.NewValue); + } + } + } + } + } + + private void OnComponentAdded(object sender, ComponentEventArgs ce) + { + if (ce.Component != null && ((IComponent)ce.Component).Site != null) + { + Control control = ce.Component as Control; + if (control != null) + { + if (!designTimeControls.ContainsKey(control.Name)) + { + designTimeControls.Add(control.Name, control); + } + else + { + designTimeControls[control.Name] = control; + } + } + else if (ce.Component is ToolStripItem) + { + ToolStripItem item = ce.Component as ToolStripItem; + if (!designTimeControls.ContainsKey(item.Name)) + { + designTimeToolStripItems.Add(item.Name, item); + } + else + { + designTimeToolStripItems[item.Name] = item; + } + } + } + } + + // Clean up any resources being used. + protected override void Dispose(bool disposing) + { + if (disposing) + { + ClearChangeNotifications(); + } + base.Dispose(disposing); + } + + protected void ApplyLanguage(ToolStripItem applyTo, string languageKey) + { + string langString = null; + if (!string.IsNullOrEmpty(languageKey)) + { + if (!Language.TryGetString(languageKey, out langString)) + { + LOG.WarnFormat("Unknown language key '{0}' configured for control '{1}', this might be okay.", languageKey, applyTo.Name); + return; + } + applyTo.Text = langString; + } + else + { + // Fallback to control name! + if (Language.TryGetString(applyTo.Name, out langString)) + { + applyTo.Text = langString; + return; + } + if (!DesignMode) + { + LOG.DebugFormat("Greenshot control without language key: {0}", applyTo.Name); + } + } + } + + protected void ApplyLanguage(ToolStripItem applyTo) + { + IGreenshotLanguageBindable languageBindable = applyTo as IGreenshotLanguageBindable; + if (languageBindable != null) + { + ApplyLanguage(applyTo, languageBindable.LanguageKey); + } + } + + protected void ApplyLanguage(Control applyTo) + { + IGreenshotLanguageBindable languageBindable = applyTo as IGreenshotLanguageBindable; + if (languageBindable == null) + { + // check if it's a menu! + ToolStrip toolStrip = applyTo as ToolStrip; + if (toolStrip != null) + { + foreach (ToolStripItem item in toolStrip.Items) + { + ApplyLanguage(item); + } + } + return; + } + + // Apply language text to the control + ApplyLanguage(applyTo, languageBindable.LanguageKey); + + // Repopulate the combox boxes + IGreenshotConfigBindable configBindable = applyTo as IGreenshotConfigBindable; + GreenshotComboBox comboxBox = applyTo as GreenshotComboBox; + if (configBindable != null && comboxBox != null) + { + if (!string.IsNullOrEmpty(configBindable.SectionName) && !string.IsNullOrEmpty(configBindable.PropertyName)) + { + IniSection section = IniConfig.GetIniSection(configBindable.SectionName); + if (section != null) + { + // Only update the language, so get the actual value and than repopulate + Enum currentValue = (Enum)comboxBox.GetSelectedEnum(); + comboxBox.Populate(section.Values[configBindable.PropertyName].ValueType); + comboxBox.SetValue(currentValue); + } + } + } + } + + /// + /// Helper method to cache the fieldinfo values, so we don't need to reflect all the time! + /// + /// + /// + private static FieldInfo[] GetCachedFields(Type typeToGetFieldsFor) + { + FieldInfo[] fields = null; + if (!reflectionCache.TryGetValue(typeToGetFieldsFor, out fields)) + { + fields = typeToGetFieldsFor.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); + reflectionCache.Add(typeToGetFieldsFor, fields); + } + return fields; + } + + /// + /// Apply all the language settings to the "Greenshot" Controls on this form + /// + protected void ApplyLanguage() + { + string langString = null; + SuspendLayout(); + try + { + // Set title of the form + if (!string.IsNullOrEmpty(LanguageKey) && Language.TryGetString(LanguageKey, out langString)) + { + Text = langString; + } + + // Reset the text values for all GreenshotControls + foreach (FieldInfo field in GetCachedFields(GetType())) + { + Object controlObject = field.GetValue(this); + if (controlObject == null) + { + LOG.DebugFormat("No value: {0}", field.Name); + continue; + } + Control applyToControl = controlObject as Control; + if (applyToControl == null) + { + ToolStripItem applyToItem = controlObject as ToolStripItem; + if (applyToItem == null) + { + LOG.DebugFormat("No Control or ToolStripItem: {0}", field.Name); + continue; + } + ApplyLanguage(applyToItem); + } + else + { + ApplyLanguage(applyToControl); + } + } + + if (DesignMode) + { + foreach (Control designControl in designTimeControls.Values) + { + ApplyLanguage(designControl); + } + foreach (ToolStripItem designToolStripItem in designTimeToolStripItems.Values) + { + ApplyLanguage(designToolStripItem); + } + } + } + finally + { + ResumeLayout(); + } + } + + /// + /// Apply the language text to supplied control + /// + protected void ApplyLanguage(Control applyTo, string languageKey) + { + string langString = null; + if (!string.IsNullOrEmpty(languageKey)) + { + if (!Language.TryGetString(languageKey, out langString)) + { + LOG.WarnFormat("Wrong language key '{0}' configured for control '{1}'", languageKey, applyTo.Name); + return; + } + applyTo.Text = langString; + } + else + { + // Fallback to control name! + if (Language.TryGetString(applyTo.Name, out langString)) + { + applyTo.Text = langString; + return; + } + if (!DesignMode) + { + LOG.DebugFormat("Greenshot control without language key: {0}", applyTo.Name); + } + } + } + + /// + /// Fill all GreenshotControls with the values from the configuration + /// + protected void FillFields() + { + foreach (FieldInfo field in GetCachedFields(GetType())) + { + Object controlObject = field.GetValue(this); + if (controlObject == null) + { + continue; + } + IGreenshotConfigBindable configBindable = controlObject as IGreenshotConfigBindable; + if (configBindable == null) + { + continue; + } + if (!string.IsNullOrEmpty(configBindable.SectionName) && !string.IsNullOrEmpty(configBindable.PropertyName)) + { + IniSection section = IniConfig.GetIniSection(configBindable.SectionName); + if (section != null) + { + IniValue iniValue = null; + if (!section.Values.TryGetValue(configBindable.PropertyName, out iniValue)) + { + LOG.WarnFormat("Wrong property '{0}' configured for field '{1}'", configBindable.PropertyName, field.Name); + continue; + } + + CheckBox checkBox = controlObject as CheckBox; + if (checkBox != null) + { + checkBox.Checked = (bool)iniValue.Value; + checkBox.Enabled = !iniValue.IsFixed; + continue; + } + RadioButton radíoButton = controlObject as RadioButton; + if (radíoButton != null) + { + radíoButton.Checked = (bool)iniValue.Value; + radíoButton.Enabled = !iniValue.IsFixed; + continue; + } + + TextBox textBox = controlObject as TextBox; + if (textBox != null) + { + textBox.Text = iniValue.ToString(); + textBox.Enabled = !iniValue.IsFixed; + continue; + } + + GreenshotComboBox comboxBox = controlObject as GreenshotComboBox; + if (comboxBox != null) + { + comboxBox.Populate(iniValue.ValueType); + comboxBox.SetValue((Enum)iniValue.Value); + comboxBox.Enabled = !iniValue.IsFixed; + continue; + } + } + } + } + OnFieldsFilled(); + } + + protected virtual void OnFieldsFilled() + { + } + + /// + /// Store all GreenshotControl values to the configuration + /// + protected void StoreFields() + { + bool iniDirty = false; + foreach (FieldInfo field in GetCachedFields(GetType())) + { + Object controlObject = field.GetValue(this); + if (controlObject == null) + { + continue; + } + IGreenshotConfigBindable configBindable = controlObject as IGreenshotConfigBindable; + if (configBindable == null) + { + continue; + } + + if (!string.IsNullOrEmpty(configBindable.SectionName) && !string.IsNullOrEmpty(configBindable.PropertyName)) + { + IniSection section = IniConfig.GetIniSection(configBindable.SectionName); + if (section != null) + { + IniValue iniValue = null; + if (!section.Values.TryGetValue(configBindable.PropertyName, out iniValue)) + { + continue; + } + CheckBox checkBox = controlObject as CheckBox; + if (checkBox != null) + { + iniValue.Value = checkBox.Checked; + iniDirty = true; + continue; + } + RadioButton radioButton = controlObject as RadioButton; + if (radioButton != null) + { + iniValue.Value = radioButton.Checked; + iniDirty = true; + continue; + } + TextBox textBox = controlObject as TextBox; + if (textBox != null) + { + iniValue.UseValueOrDefault(textBox.Text); + iniDirty = true; + continue; + } + GreenshotComboBox comboxBox = controlObject as GreenshotComboBox; + if (comboxBox != null) + { + iniValue.Value = comboxBox.GetSelectedEnum(); + iniDirty = true; + continue; + } + } + } + } + if (iniDirty) + { + //IniConfig.Save(); + } + } + } +} \ No newline at end of file diff --git a/GreenshotImageEditor/Controls/GreenshotGroupBox.cs b/GreenshotImageEditor/Controls/GreenshotGroupBox.cs new file mode 100644 index 000000000..7e131285f --- /dev/null +++ b/GreenshotImageEditor/Controls/GreenshotGroupBox.cs @@ -0,0 +1,36 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2013 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: http://getgreenshot.org/ + * The Greenshot project is hosted on Sourceforge: http://sourceforge.net/projects/greenshot/ + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using System.ComponentModel; +using System.Windows.Forms; + +namespace GreenshotPlugin.Controls +{ + public class GreenshotGroupBox : GroupBox, IGreenshotLanguageBindable + { + [Category("Greenshot"), DefaultValue(null), Description("Specifies key of the language file to use when displaying the text.")] + public string LanguageKey + { + get; + set; + } + } +} \ No newline at end of file diff --git a/GreenshotImageEditor/Controls/GreenshotLabel.cs b/GreenshotImageEditor/Controls/GreenshotLabel.cs new file mode 100644 index 000000000..0bc6730a3 --- /dev/null +++ b/GreenshotImageEditor/Controls/GreenshotLabel.cs @@ -0,0 +1,36 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2013 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: http://getgreenshot.org/ + * The Greenshot project is hosted on Sourceforge: http://sourceforge.net/projects/greenshot/ + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using System.ComponentModel; +using System.Windows.Forms; + +namespace GreenshotPlugin.Controls +{ + public class GreenshotLabel : Label, IGreenshotLanguageBindable + { + [Category("Greenshot"), DefaultValue(null), Description("Specifies key of the language file to use when displaying the text.")] + public string LanguageKey + { + get; + set; + } + } +} \ No newline at end of file diff --git a/GreenshotImageEditor/Controls/GreenshotRadioButton.cs b/GreenshotImageEditor/Controls/GreenshotRadioButton.cs new file mode 100644 index 000000000..e7050ff84 --- /dev/null +++ b/GreenshotImageEditor/Controls/GreenshotRadioButton.cs @@ -0,0 +1,60 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2013 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: http://getgreenshot.org/ + * The Greenshot project is hosted on Sourceforge: http://sourceforge.net/projects/greenshot/ + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using System.ComponentModel; +using System.Windows.Forms; + +namespace GreenshotPlugin.Controls +{ + /// + /// Description of GreenshotCheckbox. + /// + public class GreenshotRadioButton : RadioButton, IGreenshotLanguageBindable, IGreenshotConfigBindable + { + [Category("Greenshot"), DefaultValue(null), Description("Specifies key of the language file to use when displaying the text.")] + public string LanguageKey + { + get; + set; + } + + private string sectionName = "Core"; + [Category("Greenshot"), DefaultValue("Core"), Description("Specifies the Ini-Section to map this control with.")] + public string SectionName + { + get + { + return sectionName; + } + set + { + sectionName = value; + } + } + + [Category("Greenshot"), DefaultValue(null), Description("Specifies the property name to map the configuration.")] + public string PropertyName + { + get; + set; + } + } +} \ No newline at end of file diff --git a/GreenshotImageEditor/Controls/GreenshotTabPage.cs b/GreenshotImageEditor/Controls/GreenshotTabPage.cs new file mode 100644 index 000000000..9bb61bbb8 --- /dev/null +++ b/GreenshotImageEditor/Controls/GreenshotTabPage.cs @@ -0,0 +1,36 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2013 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: http://getgreenshot.org/ + * The Greenshot project is hosted on Sourceforge: http://sourceforge.net/projects/greenshot/ + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using System.ComponentModel; +using System.Windows.Forms; + +namespace GreenshotPlugin.Controls +{ + public class GreenshotTabPage : TabPage, IGreenshotLanguageBindable + { + [Category("Greenshot"), DefaultValue(null), Description("Specifies key of the language file to use when displaying the text.")] + public string LanguageKey + { + get; + set; + } + } +} \ No newline at end of file diff --git a/GreenshotImageEditor/Controls/GreenshotTextBox.cs b/GreenshotImageEditor/Controls/GreenshotTextBox.cs new file mode 100644 index 000000000..8a8e9a4a0 --- /dev/null +++ b/GreenshotImageEditor/Controls/GreenshotTextBox.cs @@ -0,0 +1,50 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2013 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: http://getgreenshot.org/ + * The Greenshot project is hosted on Sourceforge: http://sourceforge.net/projects/greenshot/ + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using System.ComponentModel; +using System.Windows.Forms; + +namespace GreenshotPlugin.Controls +{ + public class GreenshotTextBox : TextBox, IGreenshotConfigBindable + { + private string sectionName = "Core"; + [Category("Greenshot"), DefaultValue("Core"), Description("Specifies the Ini-Section to map this control with.")] + public string SectionName + { + get + { + return sectionName; + } + set + { + sectionName = value; + } + } + + [Category("Greenshot"), DefaultValue(null), Description("Specifies the property name to map the configuration.")] + public string PropertyName + { + get; + set; + } + } +} \ No newline at end of file diff --git a/GreenshotImageEditor/Controls/GreenshotToolDropDownButton.cs b/GreenshotImageEditor/Controls/GreenshotToolDropDownButton.cs new file mode 100644 index 000000000..a407b4f27 --- /dev/null +++ b/GreenshotImageEditor/Controls/GreenshotToolDropDownButton.cs @@ -0,0 +1,36 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2013 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: http://getgreenshot.org/ + * The Greenshot project is hosted on Sourceforge: http://sourceforge.net/projects/greenshot/ + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using System.ComponentModel; +using System.Windows.Forms; + +namespace GreenshotPlugin.Controls +{ + public class GreenshotToolStripDropDownButton : ToolStripDropDownButton, IGreenshotLanguageBindable + { + [Category("Greenshot"), DefaultValue(null), Description("Specifies key of the language file to use when displaying the text.")] + public string LanguageKey + { + get; + set; + } + } +} \ No newline at end of file diff --git a/GreenshotImageEditor/Controls/GreenshotToolStripButton.cs b/GreenshotImageEditor/Controls/GreenshotToolStripButton.cs new file mode 100644 index 000000000..53eea6ad9 --- /dev/null +++ b/GreenshotImageEditor/Controls/GreenshotToolStripButton.cs @@ -0,0 +1,36 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2013 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: http://getgreenshot.org/ + * The Greenshot project is hosted on Sourceforge: http://sourceforge.net/projects/greenshot/ + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using System.ComponentModel; +using System.Windows.Forms; + +namespace GreenshotPlugin.Controls +{ + public class GreenshotToolStripButton : ToolStripButton, IGreenshotLanguageBindable + { + [Category("Greenshot"), DefaultValue(null), Description("Specifies key of the language file to use when displaying the text.")] + public string LanguageKey + { + get; + set; + } + } +} \ No newline at end of file diff --git a/GreenshotImageEditor/Controls/GreenshotToolStripLabel.cs b/GreenshotImageEditor/Controls/GreenshotToolStripLabel.cs new file mode 100644 index 000000000..8b479fe4f --- /dev/null +++ b/GreenshotImageEditor/Controls/GreenshotToolStripLabel.cs @@ -0,0 +1,36 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2013 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: http://getgreenshot.org/ + * The Greenshot project is hosted on Sourceforge: http://sourceforge.net/projects/greenshot/ + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using System.ComponentModel; +using System.Windows.Forms; + +namespace GreenshotPlugin.Controls +{ + public class GreenshotToolStripLabel : ToolStripLabel, IGreenshotLanguageBindable + { + [Category("Greenshot"), DefaultValue(null), Description("Specifies key of the language file to use when displaying the text.")] + public string LanguageKey + { + get; + set; + } + } +} \ No newline at end of file diff --git a/GreenshotImageEditor/Controls/GreenshotToolStripMenuItem.cs b/GreenshotImageEditor/Controls/GreenshotToolStripMenuItem.cs new file mode 100644 index 000000000..b6fc4604d --- /dev/null +++ b/GreenshotImageEditor/Controls/GreenshotToolStripMenuItem.cs @@ -0,0 +1,36 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2013 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: http://getgreenshot.org/ + * The Greenshot project is hosted on Sourceforge: http://sourceforge.net/projects/greenshot/ + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using System.ComponentModel; +using System.Windows.Forms; + +namespace GreenshotPlugin.Controls +{ + public class GreenshotToolStripMenuItem : ToolStripMenuItem, IGreenshotLanguageBindable + { + [Category("Greenshot"), DefaultValue(null), Description("Specifies key of the language file to use when displaying the text.")] + public string LanguageKey + { + get; + set; + } + } +} \ No newline at end of file diff --git a/GreenshotImageEditor/Controls/IGreenshotConfigBindable.cs b/GreenshotImageEditor/Controls/IGreenshotConfigBindable.cs new file mode 100644 index 000000000..56e350790 --- /dev/null +++ b/GreenshotImageEditor/Controls/IGreenshotConfigBindable.cs @@ -0,0 +1,44 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2013 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: http://getgreenshot.org/ + * The Greenshot project is hosted on Sourceforge: http://sourceforge.net/projects/greenshot/ + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +namespace GreenshotPlugin.Controls +{ + public interface IGreenshotConfigBindable + { + /// + /// The class where the property-value is stored + /// + string SectionName + { + get; + set; + } + + /// + /// Path to the property value which will be mapped with this control + /// + string PropertyName + { + get; + set; + } + } +} \ No newline at end of file diff --git a/GreenshotImageEditor/Controls/IGreenshotLanguageBindable.cs b/GreenshotImageEditor/Controls/IGreenshotLanguageBindable.cs new file mode 100644 index 000000000..e9e78a3c8 --- /dev/null +++ b/GreenshotImageEditor/Controls/IGreenshotLanguageBindable.cs @@ -0,0 +1,38 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2013 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: http://getgreenshot.org/ + * The Greenshot project is hosted on Sourceforge: http://sourceforge.net/projects/greenshot/ + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +namespace GreenshotPlugin.Controls +{ + /// + /// This interface describes the designer fields that need to be implemented for Greenshot controls + /// + public interface IGreenshotLanguageBindable + { + /// + /// Language key to use to fill the Text value with + /// + string LanguageKey + { + get; + set; + } + } +} \ No newline at end of file diff --git a/GreenshotImageEditor/Controls/MenuStripEx.cs b/GreenshotImageEditor/Controls/MenuStripEx.cs new file mode 100644 index 000000000..904adb9d0 --- /dev/null +++ b/GreenshotImageEditor/Controls/MenuStripEx.cs @@ -0,0 +1,72 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2013 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: http://getgreenshot.org/ + * The Greenshot project is hosted on Sourceforge: http://sourceforge.net/projects/greenshot/ + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using System; +using System.Windows.Forms; + +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: http://blogs.msdn.com/b/rickbrew/archive/2006/01/09/511003.aspx + /// + public class MenuStripEx : MenuStrip + { + private const int WM_MOUSEACTIVATE = 0x21; + + private enum NativeConstants : uint + { + MA_ACTIVATE = 1, + MA_ACTIVATEANDEAT = 2, + MA_NOACTIVATE = 3, + MA_NOACTIVATEANDEAT = 4, + } + + private bool clickThrough = false; + /// + /// 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/GreenshotImageEditor/Controls/NonJumpingPanel.cs b/GreenshotImageEditor/Controls/NonJumpingPanel.cs new file mode 100644 index 000000000..0d86d0c0d --- /dev/null +++ b/GreenshotImageEditor/Controls/NonJumpingPanel.cs @@ -0,0 +1,40 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2013 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: http://getgreenshot.org/ + * The Greenshot project is hosted on Sourceforge: http://sourceforge.net/projects/greenshot/ + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/// +/// See: http://nickstips.wordpress.com/2010/03/03/c-panel-resets-scroll-position-after-focus-is-lost-and-regained/ +/// + +using System.Drawing; +using System.Windows.Forms; + +namespace GreenshotPlugin.Controls +{ + 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; + } + } +} \ No newline at end of file diff --git a/GreenshotImageEditor/Controls/Pipette.cs b/GreenshotImageEditor/Controls/Pipette.cs new file mode 100644 index 000000000..26bff82f3 --- /dev/null +++ b/GreenshotImageEditor/Controls/Pipette.cs @@ -0,0 +1,206 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2013 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: http://getgreenshot.org/ + * The Greenshot project is hosted on Sourceforge: http://sourceforge.net/projects/greenshot/ + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using System.ComponentModel; +using Greenshot.Forms; +using GreenshotPlugin.UnmanagedHelpers; +using System; +using System.Drawing; +using System.Windows.Forms; + +namespace Greenshot.Controls +{ + /// + /// This code was supplied by Hi-Coder as a patch for Greenshot + /// Needed some modifications to be stable. + /// + public class Pipette : Label, IMessageFilter, IDisposable + { + private MovableShowColorForm movableShowColorForm; + private bool dragging; + private Cursor _cursor; + private Bitmap _image; + private const int VK_ESC = 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((Bitmap)_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())) + { + IntPtr icon; + IconInfo iconInfo = new IconInfo(); + User32.GetIconInfo(iconHandle, out iconInfo); + iconInfo.xHotspot = hotspotX; + iconInfo.yHotspot = hotspotY; + iconInfo.fIcon = false; + 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(); + } + if (movableShowColorForm != null) + { + 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(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); + } + + #region IMessageFilter Members + + public bool PreFilterMessage(ref Message m) + { + if (dragging) + { + if (m.Msg == (int)WindowsMessages.WM_CHAR) + { + if ((int)m.WParam == VK_ESC) + { + User32.ReleaseCapture(); + } + } + } + return false; + } + + #endregion IMessageFilter Members + } + + public class PipetteUsedArgs : EventArgs + { + public Color color; + + public PipetteUsedArgs(Color c) + { + color = c; + } + } +} \ No newline at end of file diff --git a/GreenshotImageEditor/Controls/PleaseWaitForm.cs b/GreenshotImageEditor/Controls/PleaseWaitForm.cs new file mode 100644 index 000000000..ab428929f --- /dev/null +++ b/GreenshotImageEditor/Controls/PleaseWaitForm.cs @@ -0,0 +1,136 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2013 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: http://getgreenshot.org/ + * The Greenshot project is hosted on Sourceforge: http://sourceforge.net/projects/greenshot/ + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using GreenshotPlugin.Core; +using System; +using System.Threading; +using System.Windows.Forms; + +namespace GreenshotPlugin.Controls +{ + /// + /// Description of PleaseWaitForm. + /// + public partial class PleaseWaitForm : Form + { + private Thread waitFor = null; + private string title; + + public PleaseWaitForm() + { + // + // The InitializeComponent() call is required for Windows Forms designer support. + // + InitializeComponent(); + Icon = GreenshotResources.getGreenshotIcon(); + } + + /// + /// Prevent the close-window button showing + /// + private const int CP_NOCLOSE_BUTTON = 0x200; + protected override CreateParams CreateParams + { + get + { + CreateParams createParams = base.CreateParams; + createParams.ClassStyle = createParams.ClassStyle | CP_NOCLOSE_BUTTON; + return createParams; + } + } + + /// + /// Show the "please wait" form, execute the code from the delegate and wait until execution finishes. + /// The supplied delegate will be wrapped with a try/catch so this method can return any exception that was thrown. + /// + /// The title of the form (and Thread) + /// The text in the form + /// delegate { with your code } + public void ShowAndWait(string title, string text, ThreadStart waitDelegate) + { + this.title = title; + Text = title; + label_pleasewait.Text = text; + cancelButton.Text = Language.GetString("CANCEL"); + + // Make sure the form is shown. + Show(); + + // Variable to store the exception, if one is generated, from inside the thread. + Exception threadException = null; + try + { + // Wrap the passed delegate in a try/catch which makes it possible to save the exception + waitFor = new Thread(new ThreadStart( + delegate + { + try + { + waitDelegate.Invoke(); + } + catch (Exception ex) + { + LOG.Error("invoke error:", ex); + threadException = ex; + } + }) + ); + waitFor.Name = title; + waitFor.IsBackground = true; + waitFor.SetApartmentState(ApartmentState.STA); + waitFor.Start(); + + // Wait until finished + while (!waitFor.Join(TimeSpan.FromMilliseconds(100))) + { + Application.DoEvents(); + } + LOG.DebugFormat("Finished {0}", title); + } + catch (Exception ex) + { + LOG.Error(ex); + throw; + } + finally + { + Close(); + } + // Check if an exception occured, if so throw it + if (threadException != null) + { + throw threadException; + } + } + + /// + /// Called if the cancel button is clicked, will use Thread.Abort() + /// + /// + /// + private void CancelButtonClick(object sender, EventArgs e) + { + LOG.DebugFormat("Cancel clicked on {0}", title); + cancelButton.Enabled = false; + waitFor.Abort(); + } + } +} \ No newline at end of file diff --git a/GreenshotImageEditor/Controls/PleaseWaitForm.designer.cs b/GreenshotImageEditor/Controls/PleaseWaitForm.designer.cs new file mode 100644 index 000000000..55d2a32b2 --- /dev/null +++ b/GreenshotImageEditor/Controls/PleaseWaitForm.designer.cs @@ -0,0 +1,102 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2013 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: http://getgreenshot.org/ + * The Greenshot project is hosted on Sourceforge: http://sourceforge.net/projects/greenshot/ + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +namespace GreenshotPlugin.Controls { + partial class PleaseWaitForm { + /// + /// 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() { + this.label_pleasewait = new System.Windows.Forms.Label(); + this.cancelButton = new System.Windows.Forms.Button(); + this.SuspendLayout(); + // + // label_pleasewait + // + this.label_pleasewait.AutoSize = true; + this.label_pleasewait.Dock = System.Windows.Forms.DockStyle.Fill; + this.label_pleasewait.Location = new System.Drawing.Point(0, 0); + this.label_pleasewait.Name = "label_pleasewait"; + this.label_pleasewait.Padding = new System.Windows.Forms.Padding(10); + this.label_pleasewait.Size = new System.Drawing.Size(90, 33); + this.label_pleasewait.TabIndex = 0; + this.label_pleasewait.Text = "Please wait..."; + this.label_pleasewait.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; + this.label_pleasewait.UseWaitCursor = true; + // + // cancelButton + // + this.cancelButton.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.cancelButton.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.cancelButton.Location = new System.Drawing.Point(38, 41); + this.cancelButton.Name = "cancelButton"; + this.cancelButton.Size = new System.Drawing.Size(94, 23); + this.cancelButton.TabIndex = 1; + this.cancelButton.Text = "Cancel"; + this.cancelButton.UseVisualStyleBackColor = true; + this.cancelButton.UseWaitCursor = true; + this.cancelButton.Click += new System.EventHandler(this.CancelButtonClick); + // + // PleaseWaitForm + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.AutoSize = true; + this.CancelButton = this.cancelButton; + this.ClientSize = new System.Drawing.Size(169, 76); + this.Controls.Add(this.cancelButton); + this.Controls.Add(this.label_pleasewait); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "PleaseWaitForm"; + this.SizeGripStyle = System.Windows.Forms.SizeGripStyle.Hide; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; + this.Text = "Greenshot"; + this.UseWaitCursor = true; + this.ResumeLayout(false); + this.PerformLayout(); + } + private System.Windows.Forms.Button cancelButton; + private System.Windows.Forms.Label label_pleasewait; + } +} diff --git a/GreenshotImageEditor/Controls/QualityDialog.cs b/GreenshotImageEditor/Controls/QualityDialog.cs new file mode 100644 index 000000000..c378698b9 --- /dev/null +++ b/GreenshotImageEditor/Controls/QualityDialog.cs @@ -0,0 +1,75 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2013 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: http://getgreenshot.org/ + * The Greenshot project is hosted on Sourceforge: http://sourceforge.net/projects/greenshot/ + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using System; +using Greenshot.IniFile; +using Greenshot.Plugin; +using GreenshotPlugin.Core; + +namespace GreenshotPlugin.Controls +{ + /// + /// Description of JpegQualityDialog. + /// + public partial class QualityDialog : GreenshotForm + { + private static CoreConfiguration conf = IniConfig.GetIniSection(); + public SurfaceOutputSettings Settings + { + get; + set; + } + + public QualityDialog(SurfaceOutputSettings outputSettings) + { + Settings = outputSettings; + // + // The InitializeComponent() call is required for Windows Forms designer support. + // + InitializeComponent(); + Icon = GreenshotResources.getGreenshotIcon(); + + checkBox_reduceColors.Checked = Settings.ReduceColors; + trackBarJpegQuality.Enabled = OutputFormat.jpg.Equals(outputSettings.Format); + trackBarJpegQuality.Value = Settings.JPGQuality; + textBoxJpegQuality.Enabled = OutputFormat.jpg.Equals(outputSettings.Format); + textBoxJpegQuality.Text = Settings.JPGQuality.ToString(); + } + + private void Button_okClick(object sender, EventArgs e) + { + Settings.JPGQuality = trackBarJpegQuality.Value; + Settings.ReduceColors = checkBox_reduceColors.Checked; + if (checkbox_dontaskagain.Checked) + { + conf.OutputFileJpegQuality = Settings.JPGQuality; + conf.OutputFilePromptQuality = false; + conf.OutputFileReduceColors = Settings.ReduceColors; + IniConfig.Save(); + } + } + + private void TrackBarJpegQualityScroll(object sender, EventArgs e) + { + textBoxJpegQuality.Text = trackBarJpegQuality.Value.ToString(); + } + } +} \ No newline at end of file diff --git a/GreenshotImageEditor/Controls/QualityDialog.designer.cs b/GreenshotImageEditor/Controls/QualityDialog.designer.cs new file mode 100644 index 000000000..9b26a086d --- /dev/null +++ b/GreenshotImageEditor/Controls/QualityDialog.designer.cs @@ -0,0 +1,149 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2013 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: http://getgreenshot.org/ + * The Greenshot project is hosted on Sourceforge: http://sourceforge.net/projects/greenshot/ + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +namespace GreenshotPlugin.Controls { + partial class QualityDialog { + /// + /// 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() + { + this.label_choosejpegquality = new GreenshotPlugin.Controls.GreenshotLabel(); + this.textBoxJpegQuality = new System.Windows.Forms.TextBox(); + this.trackBarJpegQuality = new System.Windows.Forms.TrackBar(); + this.checkbox_dontaskagain = new GreenshotPlugin.Controls.GreenshotCheckBox(); + this.button_ok = new GreenshotPlugin.Controls.GreenshotButton(); + this.checkBox_reduceColors = new System.Windows.Forms.CheckBox(); + ((System.ComponentModel.ISupportInitialize)(this.trackBarJpegQuality)).BeginInit(); + this.SuspendLayout(); + // + // label_choosejpegquality + // + this.label_choosejpegquality.Location = new System.Drawing.Point(12, 47); + this.label_choosejpegquality.Name = "label_choosejpegquality"; + this.label_choosejpegquality.Size = new System.Drawing.Size(268, 19); + this.label_choosejpegquality.TabIndex = 15; + this.label_choosejpegquality.LanguageKey = "jpegqualitydialog_choosejpegquality"; + // + // textBoxJpegQuality + // + this.textBoxJpegQuality.Location = new System.Drawing.Point(245, 69); + this.textBoxJpegQuality.Name = "textBoxJpegQuality"; + this.textBoxJpegQuality.ReadOnly = true; + this.textBoxJpegQuality.Size = new System.Drawing.Size(35, 20); + this.textBoxJpegQuality.TabIndex = 16; + this.textBoxJpegQuality.TextAlign = System.Windows.Forms.HorizontalAlignment.Right; + // + // trackBarJpegQuality + // + this.trackBarJpegQuality.LargeChange = 10; + this.trackBarJpegQuality.Location = new System.Drawing.Point(12, 69); + this.trackBarJpegQuality.Maximum = 100; + this.trackBarJpegQuality.Name = "trackBarJpegQuality"; + this.trackBarJpegQuality.Size = new System.Drawing.Size(233, 45); + this.trackBarJpegQuality.TabIndex = 14; + this.trackBarJpegQuality.TickFrequency = 10; + this.trackBarJpegQuality.Scroll += new System.EventHandler(this.TrackBarJpegQualityScroll); + // + // checkbox_dontaskagain + // + this.checkbox_dontaskagain.CheckAlign = System.Drawing.ContentAlignment.TopLeft; + this.checkbox_dontaskagain.ImageAlign = System.Drawing.ContentAlignment.TopLeft; + this.checkbox_dontaskagain.Location = new System.Drawing.Point(12, 106); + this.checkbox_dontaskagain.Name = "checkbox_dontaskagain"; + this.checkbox_dontaskagain.LanguageKey = "qualitydialog_dontaskagain"; + this.checkbox_dontaskagain.Size = new System.Drawing.Size(268, 37); + this.checkbox_dontaskagain.TabIndex = 17; + this.checkbox_dontaskagain.TextAlign = System.Drawing.ContentAlignment.TopLeft; + this.checkbox_dontaskagain.UseVisualStyleBackColor = true; + // + // button_ok + // + this.button_ok.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.button_ok.Location = new System.Drawing.Point(205, 149); + this.button_ok.Name = "button_ok"; + this.button_ok.Size = new System.Drawing.Size(75, 23); + this.button_ok.TabIndex = 18; + this.button_ok.LanguageKey = "OK"; + this.button_ok.UseVisualStyleBackColor = true; + this.button_ok.Click += new System.EventHandler(this.Button_okClick); + // + // checkBox_reduceColors + // + this.checkBox_reduceColors.AutoSize = true; + this.checkBox_reduceColors.Location = new System.Drawing.Point(12, 11); + this.checkBox_reduceColors.Name = "checkBox_reduceColors"; + this.checkBox_reduceColors.Size = new System.Drawing.Size(95, 17); + this.checkBox_reduceColors.TabIndex = 19; + this.checkBox_reduceColors.Text = "settings_reducecolors"; + this.checkBox_reduceColors.UseVisualStyleBackColor = true; + // + // QualityDialog + // + this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi; + this.ClientSize = new System.Drawing.Size(299, 184); + this.ControlBox = false; + this.Controls.Add(this.checkBox_reduceColors); + this.Controls.Add(this.button_ok); + this.Controls.Add(this.checkbox_dontaskagain); + this.Controls.Add(this.label_choosejpegquality); + this.Controls.Add(this.textBoxJpegQuality); + this.Controls.Add(this.trackBarJpegQuality); + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "QualityDialog"; + this.SizeGripStyle = System.Windows.Forms.SizeGripStyle.Hide; + this.LanguageKey = "qualitydialog_title"; + ((System.ComponentModel.ISupportInitialize)(this.trackBarJpegQuality)).EndInit(); + this.ResumeLayout(false); + this.PerformLayout(); + + } + private GreenshotPlugin.Controls.GreenshotButton button_ok; + private GreenshotPlugin.Controls.GreenshotCheckBox checkbox_dontaskagain; + private System.Windows.Forms.TrackBar trackBarJpegQuality; + private System.Windows.Forms.TextBox textBoxJpegQuality; + private GreenshotPlugin.Controls.GreenshotLabel label_choosejpegquality; + private System.Windows.Forms.CheckBox checkBox_reduceColors; + } +} diff --git a/GreenshotImageEditor/Controls/SaveImageFileDialog.cs b/GreenshotImageEditor/Controls/SaveImageFileDialog.cs new file mode 100644 index 000000000..315ccfa2f --- /dev/null +++ b/GreenshotImageEditor/Controls/SaveImageFileDialog.cs @@ -0,0 +1,262 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2013 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: http://getgreenshot.org/ + * The Greenshot project is hosted on Sourceforge: http://sourceforge.net/projects/greenshot/ + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using Greenshot.IniFile; +using Greenshot.Plugin; +using GreenshotPlugin.Core; +using System; +using System.IO; +using System.Windows.Forms; + +namespace GreenshotPlugin.Controls +{ + /// + /// Custom dialog for saving images, wraps SaveFileDialog. + /// For some reason SFD is sealed :( + /// + public class SaveImageFileDialog : IDisposable + { + private static CoreConfiguration conf = IniConfig.GetIniSection(); + protected SaveFileDialog saveFileDialog; + private FilterOption[] filterOptions; + private DirectoryInfo eagerlyCreatedDirectory; + private ICaptureDetails captureDetails = null; + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) + { + if (disposing) + { + if (saveFileDialog != null) + { + saveFileDialog.Dispose(); + saveFileDialog = null; + } + } + } + + public SaveImageFileDialog() + { + init(); + } + + public SaveImageFileDialog(ICaptureDetails captureDetails) + { + this.captureDetails = captureDetails; + init(); + } + + private void init() + { + saveFileDialog = new SaveFileDialog(); + applyFilterOptions(); + string initialDirectory = null; + try + { + initialDirectory = Path.GetDirectoryName(conf.OutputFileAsFullpath); + } + catch + { + LOG.WarnFormat("OutputFileAsFullpath was set to {0}, ignoring due to problem in path.", conf.OutputFileAsFullpath); + } + + if (!string.IsNullOrEmpty(initialDirectory) && Directory.Exists(initialDirectory)) + { + saveFileDialog.InitialDirectory = initialDirectory; + } + else if (Directory.Exists(conf.OutputFilePath)) + { + saveFileDialog.InitialDirectory = conf.OutputFilePath; + } + // The following property fixes a problem that the directory where we save is locked (bug #2899790) + saveFileDialog.RestoreDirectory = true; + saveFileDialog.OverwritePrompt = true; + saveFileDialog.CheckPathExists = false; + saveFileDialog.AddExtension = true; + ApplySuggestedValues(); + } + + private void applyFilterOptions() + { + prepareFilterOptions(); + string fdf = ""; + int preselect = 0; + for (int i = 0; i < filterOptions.Length; i++) + { + FilterOption fo = filterOptions[i]; + fdf += fo.Label + "|*." + fo.Extension + "|"; + if (conf.OutputFileAsFullpath.EndsWith(fo.Extension, StringComparison.CurrentCultureIgnoreCase)) preselect = i; + } + fdf = fdf.Substring(0, fdf.Length - 1); + saveFileDialog.Filter = fdf; + saveFileDialog.FilterIndex = preselect + 1; + } + + private void prepareFilterOptions() + { + OutputFormat[] supportedImageFormats = (OutputFormat[])Enum.GetValues(typeof(OutputFormat)); + filterOptions = new FilterOption[supportedImageFormats.Length]; + for (int i = 0; i < filterOptions.Length; i++) + { + string ifo = supportedImageFormats[i].ToString(); + if (ifo.ToLower().Equals("jpeg")) ifo = "Jpg"; // we dont want no jpeg files, so let the dialog check for jpg + FilterOption fo = new FilterOption(); + fo.Label = ifo.ToUpper(); + fo.Extension = ifo.ToLower(); + filterOptions.SetValue(fo, i); + } + } + + /// + /// filename exactly as typed in the filename field + /// + public string FileName + { + get { return saveFileDialog.FileName; } + set { saveFileDialog.FileName = value; } + } + + /// + /// initial directory of the dialog + /// + public string InitialDirectory + { + get { return saveFileDialog.InitialDirectory; } + set { saveFileDialog.InitialDirectory = value; } + } + + /// + /// returns filename as typed in the filename field with extension. + /// if filename field value ends with selected extension, the value is just returned. + /// otherwise, the selected extension is appended to the filename. + /// + public string FileNameWithExtension + { + get + { + string fn = saveFileDialog.FileName; + // if the filename contains a valid extension, which is the same like the selected filter item's extension, the filename is okay + if (fn.EndsWith(Extension, StringComparison.CurrentCultureIgnoreCase)) return fn; + // otherwise we just add the selected filter item's extension + else return fn + "." + Extension; + } + set + { + FileName = Path.GetFileNameWithoutExtension(value); + Extension = Path.GetExtension(value); + } + } + + /// + /// gets or sets selected extension + /// + public string Extension + { + get + { + return filterOptions[saveFileDialog.FilterIndex - 1].Extension; + } + set + { + for (int i = 0; i < filterOptions.Length; i++) + { + if (value.Equals(filterOptions[i].Extension, StringComparison.CurrentCultureIgnoreCase)) + { + saveFileDialog.FilterIndex = i + 1; + } + } + } + } + + public DialogResult ShowDialog() + { + DialogResult ret = saveFileDialog.ShowDialog(); + CleanUp(); + return ret; + } + + /// + /// sets InitialDirectory and FileName property of a SaveFileDialog smartly, considering default pattern and last used path + /// + /// a SaveFileDialog instance + private void ApplySuggestedValues() + { + // build the full path and set dialog properties + FileName = FilenameHelper.GetFilenameWithoutExtensionFromPattern(conf.OutputFileFilenamePattern, captureDetails); + } + + private string GetRootDirFromConfig() + { + string rootDir = conf.OutputFilePath; + rootDir = FilenameHelper.FillVariables(rootDir, false); + return rootDir; + } + + private class FilterOption + { + public string Label; + public string Extension; + } + + private void CleanUp() + { + // fix for bug #3379053 + try + { + if (eagerlyCreatedDirectory != null && eagerlyCreatedDirectory.GetFiles().Length == 0 && eagerlyCreatedDirectory.GetDirectories().Length == 0) + { + eagerlyCreatedDirectory.Delete(); + eagerlyCreatedDirectory = null; + } + } + catch (Exception e) + { + LOG.WarnFormat("Couldn't cleanup directory due to: {0}", e.Message); + eagerlyCreatedDirectory = null; + } + } + + private string CreateDirectoryIfNotExists(string fullPath) + { + string dirName = null; + try + { + dirName = Path.GetDirectoryName(fullPath); + DirectoryInfo di = new DirectoryInfo(dirName); + if (!di.Exists) + { + di = Directory.CreateDirectory(dirName); + eagerlyCreatedDirectory = di; + } + } + catch (Exception e) + { + LOG.Error("Error in CreateDirectoryIfNotExists", e); + } + return dirName; + } + } +} \ No newline at end of file diff --git a/GreenshotImageEditor/Controls/ToolStripColorButton.cs b/GreenshotImageEditor/Controls/ToolStripColorButton.cs new file mode 100644 index 000000000..24c71a364 --- /dev/null +++ b/GreenshotImageEditor/Controls/ToolStripColorButton.cs @@ -0,0 +1,99 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2013 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: http://getgreenshot.org/ + * The Greenshot project is hosted on Sourceforge: http://sourceforge.net/projects/greenshot/ + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using GreenshotPlugin.Controls; +using System; +using System.ComponentModel; +using System.Drawing; +using System.Drawing.Drawing2D; +using System.Windows.Forms; + +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) + { + ColorDialog colorDialog = ColorDialog.GetInstance(); + 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) + { + if (!colorDialog.Color.Equals(SelectedColor)) + { + SelectedColor = colorDialog.Color; + if (PropertyChanged != null) + { + PropertyChanged(this, new PropertyChangedEventArgs("SelectedColor")); + } + } + } + } + } +} \ No newline at end of file diff --git a/GreenshotImageEditor/Controls/ToolStripEx.cs b/GreenshotImageEditor/Controls/ToolStripEx.cs new file mode 100644 index 000000000..72ee2278e --- /dev/null +++ b/GreenshotImageEditor/Controls/ToolStripEx.cs @@ -0,0 +1,73 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2013 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: http://getgreenshot.org/ + * The Greenshot project is hosted on Sourceforge: http://sourceforge.net/projects/greenshot/ + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +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: http://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, + MA_NOACTIVATE = 3, + MA_NOACTIVATEANDEAT = 4, + } + + private bool clickThrough = false; + /// + /// 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/GreenshotImageEditor/Controls/ToolStripNumericUpDown.cs b/GreenshotImageEditor/Controls/ToolStripNumericUpDown.cs new file mode 100644 index 000000000..0a998bb6c --- /dev/null +++ b/GreenshotImageEditor/Controls/ToolStripNumericUpDown.cs @@ -0,0 +1,90 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2013 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: http://getgreenshot.org/ + * The Greenshot project is hosted on Sourceforge: http://sourceforge.net/projects/greenshot/ + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +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 + { + get { return 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) + { + if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs("Value")); + } + } +} \ No newline at end of file diff --git a/GreenshotImageEditor/Core/BinaryStructHelper.cs b/GreenshotImageEditor/Core/BinaryStructHelper.cs new file mode 100644 index 000000000..2bae0b49c --- /dev/null +++ b/GreenshotImageEditor/Core/BinaryStructHelper.cs @@ -0,0 +1,108 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2013 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: http://getgreenshot.org/ + * The Greenshot project is hosted on Sourceforge: http://sourceforge.net/projects/greenshot/ + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using System; +using System.Runtime.InteropServices; + +namespace GreenshotPlugin.Core +{ + /// + /// A helper class which does the mashalling for structs + /// + public static class BinaryStructHelper + { + /// + /// Get a struct from a byte array + /// + /// typeof struct + /// byte[] + /// struct + public static T FromByteArray(byte[] bytes) where T : struct + { + IntPtr ptr = IntPtr.Zero; + try + { + int size = Marshal.SizeOf(typeof(T)); + ptr = Marshal.AllocHGlobal(size); + Marshal.Copy(bytes, 0, ptr, size); + return FromIntPtr(ptr); + } + finally + { + if (ptr != IntPtr.Zero) + { + Marshal.FreeHGlobal(ptr); + } + } + } + + /// + /// Get a struct from a byte array + /// + /// typeof struct + /// byte[] + /// struct + public static T FromIntPtr(IntPtr intPtr) where T : struct + { + object obj = Marshal.PtrToStructure(intPtr, typeof(T)); + return (T)obj; + } + + /// + /// copy a struct to a byte array + /// + /// typeof struct + /// struct + /// byte[] + public static byte[] ToByteArray(T obj) where T : struct + { + IntPtr ptr = IntPtr.Zero; + try + { + int size = Marshal.SizeOf(typeof(T)); + ptr = Marshal.AllocHGlobal(size); + Marshal.StructureToPtr(obj, ptr, true); + return FromPtrToByteArray(ptr); + } + finally + { + if (ptr != IntPtr.Zero) + { + Marshal.FreeHGlobal(ptr); + } + } + } + + /// + /// copy a struct from a pointer to a byte array + /// + /// typeof struct + /// IntPtr to struct + /// byte[] + public static byte[] FromPtrToByteArray(IntPtr ptr) where T : struct + { + int size = Marshal.SizeOf(typeof(T)); + byte[] bytes = new byte[size]; + Marshal.Copy(ptr, bytes, 0, size); + return bytes; + } + } +} \ No newline at end of file diff --git a/GreenshotImageEditor/Core/Cache.cs b/GreenshotImageEditor/Core/Cache.cs new file mode 100644 index 000000000..5e77ad806 --- /dev/null +++ b/GreenshotImageEditor/Core/Cache.cs @@ -0,0 +1,247 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2013 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: http://getgreenshot.org/ + * The Greenshot project is hosted on Sourceforge: http://sourceforge.net/projects/greenshot/ + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using System; +using System.Collections.Generic; +using System.Timers; + +namespace GreenshotPlugin.Core +{ + /// + /// Cache class + /// + /// Type of key + /// Type of value + public class Cache + { + private IDictionary internalCache = new Dictionary(); + private object lockObject = new object(); + private int secondsToExpire = 10; + private CacheObjectExpired expiredCallback = null; + public delegate void CacheObjectExpired(TK key, TV cacheValue); + + /// + /// Initialize the cache + /// + public Cache() + { + } + + /// + /// Initialize the cache + /// + /// + public Cache(CacheObjectExpired expiredCallback) + : this() + { + this.expiredCallback = expiredCallback; + } + + /// + /// Initialize the cache with a expire setting + /// + /// + public Cache(int secondsToExpire) + : this() + { + this.secondsToExpire = secondsToExpire; + } + + /// + /// Initialize the cache with a expire setting + /// + /// + /// + public Cache(int secondsToExpire, CacheObjectExpired expiredCallback) + : this(expiredCallback) + { + this.secondsToExpire = secondsToExpire; + } + + /// + /// Enumerable for the values in the cache + /// + public IEnumerable Elements + { + get + { + List elements = new List(); + + foreach (TV element in internalCache.Values) + { + elements.Add(element); + } + foreach (TV element in elements) + { + yield return element; + } + } + } + + /// + /// Get the value by key from the cache + /// + /// + /// + public TV this[TK key] + { + get + { + TV result = default(TV); + lock (lockObject) + { + if (internalCache.ContainsKey(key)) + { + result = internalCache[key]; + } + } + return result; + } + } + + /// + /// Contains + /// + /// + /// true if the cache contains the key + public bool Contains(TK key) + { + return internalCache.ContainsKey(key); + } + + /// + /// Add a value to the cache + /// + /// + /// + public void Add(TK key, TV value) + { + Add(key, value, null); + } + + /// + /// Add a value to the cache + /// + /// + /// + /// optional value for the seconds to expire + public void Add(TK key, TV value, int? secondsToExpire) + { + lock (lockObject) + { + var cachedItem = new CachedItem(key, value, secondsToExpire.HasValue ? secondsToExpire.Value : this.secondsToExpire); + cachedItem.Expired += delegate(TK cacheKey, TV cacheValue) + { + if (internalCache.ContainsKey(cacheKey)) + { + LOG.DebugFormat("Expiring object with Key: {0}", cacheKey); + if (expiredCallback != null) + { + expiredCallback(cacheKey, cacheValue); + } + Remove(cacheKey); + } + else + { + LOG.DebugFormat("Expired old object with Key: {0}", cacheKey); + } + }; + + if (internalCache.ContainsKey(key)) + { + internalCache[key] = value; + LOG.DebugFormat("Updated item with Key: {0}", key); + } + else + { + internalCache.Add(key, cachedItem); + LOG.DebugFormat("Added item with Key: {0}", key); + } + } + } + + /// + /// Remove item from cache + /// + /// + public void Remove(TK key) + { + lock (lockObject) + { + if (!internalCache.ContainsKey(key)) + { + throw new ApplicationException(String.Format("An object with key ‘{0}’ does not exists in cache", key)); + } + internalCache.Remove(key); + LOG.DebugFormat("Removed item with Key: {0}", key); + } + } + + /// + /// A cache item + /// + private class CachedItem + { + public event CacheObjectExpired Expired; + private int secondsToExpire; + private readonly Timer _timerEvent; + + public CachedItem(TK key, TV item, int secondsToExpire) + { + if (key == null) + { + throw new ArgumentNullException("key is not valid"); + } + Key = key; + Item = item; + this.secondsToExpire = secondsToExpire; + if (secondsToExpire > 0) + { + _timerEvent = new Timer(secondsToExpire * 1000) { AutoReset = false }; + _timerEvent.Elapsed += timerEvent_Elapsed; + _timerEvent.Start(); + } + } + + private void ExpireNow() + { + _timerEvent.Stop(); + if (secondsToExpire > 0 && Expired != null) + { + Expired(Key, Item); + } + } + + private void timerEvent_Elapsed(object sender, ElapsedEventArgs e) + { + ExpireNow(); + } + + public TK Key { get; private set; } + public TV Item { get; private set; } + + public static implicit operator TV(CachedItem a) + { + return a.Item; + } + } + } +} \ No newline at end of file diff --git a/GreenshotImageEditor/Core/ClipboardHelper.cs b/GreenshotImageEditor/Core/ClipboardHelper.cs new file mode 100644 index 000000000..532379a14 --- /dev/null +++ b/GreenshotImageEditor/Core/ClipboardHelper.cs @@ -0,0 +1,803 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2013 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: http://getgreenshot.org/ + * The Greenshot project is hosted on Sourceforge: http://sourceforge.net/projects/greenshot/ + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using Greenshot.IniFile; +using Greenshot.Plugin; +using GreenshotPlugin.UnmanagedHelpers; +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; + +namespace GreenshotPlugin.Core +{ + /// + /// Description of ClipboardHelper. + /// + public static class ClipboardHelper + { + private static readonly Object clipboardLockObject = new Object(); + private static readonly CoreConfiguration config = IniConfig.GetIniSection(); + private static readonly string FORMAT_FILECONTENTS = "FileContents"; + private static readonly string FORMAT_PNG = "PNG"; + private static readonly string FORMAT_PNG_OFFICEART = "PNG+Office Art"; + private static readonly string FORMAT_JPG = "JPG"; + private static readonly string FORMAT_JFIF = "JFIF"; + private static readonly string FORMAT_JFIF_OFFICEART = "JFIF+Office Art"; + private static readonly string FORMAT_GIF = "GIF"; + private static readonly string FORMAT_BITMAP_PLACEHOLDER = "_BITMAP_"; + + private static IntPtr nextClipboardViewer = IntPtr.Zero; + // Template for the HTML Text on the clipboard + // see: http://msdn.microsoft.com/en-us/library/ms649015%28v=vs.85%29.aspx + // or: http://msdn.microsoft.com/en-us/library/Aa767917.aspx + private const string HTML_CLIPBOARD_STRING = @"Version:0.9 +StartHTML:<<<<<<<1 +EndHTML:<<<<<<<2 +StartFragment:<<<<<<<3 +EndFragment:<<<<<<<4 +StartSelection:<<<<<<<3 +EndSelection:<<<<<<<4 + + + +Greenshot capture + + + + + + +"; + private const string HTML_CLIPBOARD_BASE64_STRING = @"Version:0.9 +StartHTML:<<<<<<<1 +EndHTML:<<<<<<<2 +StartFragment:<<<<<<<3 +EndFragment:<<<<<<<4 +StartSelection:<<<<<<<3 +EndSelection:<<<<<<<4 + + + +Greenshot capture + + + + + + +"; + + /// + /// Get the current "ClipboardOwner" but only if it isn't us! + /// + /// current clipboard owner + private static string GetClipboardOwner() + { + string owner = null; + try + { + IntPtr hWnd = User32.GetClipboardOwner(); + if (hWnd != IntPtr.Zero) + { + IntPtr pid = IntPtr.Zero; + IntPtr tid = User32.GetWindowThreadProcessId(hWnd, out pid); + Process me = Process.GetCurrentProcess(); + Process ownerProcess = Process.GetProcessById(pid.ToInt32()); + // Exclude myself + if (ownerProcess != null && me.Id != ownerProcess.Id) + { + // Get Process Name + owner = ownerProcess.ProcessName; + // Try to get the starting Process Filename, this might fail. + try + { + owner = ownerProcess.Modules[0].FileName; + } + catch (Exception) + { + } + } + } + } + catch (Exception e) + { + LOG.Warn("Non critical error: Couldn't get clipboard owner.", e); + } + return owner; + } + + /// + /// The SetDataObject will lock/try/catch clipboard operations making it save and not show exceptions. + /// The bool "copy" is used to decided if the information stays on the clipboard after exit. + /// + /// + /// + private static void SetDataObject(IDataObject ido, bool copy) + { + lock (clipboardLockObject) + { + int retryCount = 2; + while (retryCount >= 0) + { + try + { + Clipboard.SetDataObject(ido, copy); + break; + } + catch (Exception ee) + { + if (retryCount == 0) + { + string messageText = null; + string clipboardOwner = GetClipboardOwner(); + if (clipboardOwner != null) + { + messageText = Language.GetFormattedString("clipboard_inuse", clipboardOwner); + } + else + { + messageText = Language.GetString("clipboard_error"); + } + LOG.Error(messageText, ee); + } + else + { + Thread.Sleep(100); + } + } + finally + { + --retryCount; + } + } + } + } + + /// + /// The GetDataObject will lock/try/catch clipboard operations making it save and not show exceptions. + /// + public static IDataObject GetDataObject() + { + lock (clipboardLockObject) + { + int retryCount = 2; + while (retryCount >= 0) + { + try + { + return Clipboard.GetDataObject(); + } + catch (Exception ee) + { + if (retryCount == 0) + { + string messageText = null; + string clipboardOwner = GetClipboardOwner(); + if (clipboardOwner != null) + { + messageText = Language.GetFormattedString("clipboard_inuse", clipboardOwner); + } + else + { + messageText = Language.GetString("clipboard_error"); + } + LOG.Error(messageText, ee); + } + else + { + Thread.Sleep(100); + } + } + finally + { + --retryCount; + } + } + } + return null; + } + + /// + /// Wrapper for Clipboard.ContainsText, Created for Bug #3432313 + /// + /// boolean if there is text on the clipboard + public static bool ContainsText() + { + IDataObject clipboardData = GetDataObject(); + return ContainsText(clipboardData); + } + + /// + /// Test if the IDataObject contains Text + /// + /// + /// + public static bool ContainsText(IDataObject dataObject) + { + if (dataObject != null) + { + if (dataObject.GetDataPresent(DataFormats.Text) || dataObject.GetDataPresent(DataFormats.UnicodeText)) + { + return true; + } + } + return false; + } + + /// + /// Wrapper for Clipboard.ContainsImage, specialized for Greenshot, Created for Bug #3432313 + /// + /// boolean if there is an image on the clipboard + public static bool ContainsImage() + { + IDataObject clipboardData = GetDataObject(); + return ContainsImage(clipboardData); + } + + /// + /// Check if the IDataObject has an image + /// + /// + /// true if an image is there + public static bool ContainsImage(IDataObject dataObject) + { + if (dataObject != null) + { + if (dataObject.GetDataPresent(DataFormats.Bitmap) + || dataObject.GetDataPresent(DataFormats.Dib) + || dataObject.GetDataPresent(DataFormats.Tiff) + || dataObject.GetDataPresent(DataFormats.EnhancedMetafile) + || dataObject.GetDataPresent(FORMAT_PNG) + || dataObject.GetDataPresent(FORMAT_JPG) + || dataObject.GetDataPresent(FORMAT_GIF)) + { + return true; + } + List imageFiles = GetImageFilenames(dataObject); + if (imageFiles != null && imageFiles.Count > 0) + { + return true; + } + if (dataObject.GetDataPresent(FORMAT_FILECONTENTS)) + { + try + { + MemoryStream imageStream = dataObject.GetData(FORMAT_FILECONTENTS) as MemoryStream; + if (isValidStream(imageStream)) + { + using (Image tmpImage = Image.FromStream(imageStream)) + { + // If we get here, there is an image + return true; + } + } + } + catch (Exception) + { + } + } + } + return false; + } + + /// + /// Simple helper to check the stream + /// + /// + /// + private static bool isValidStream(MemoryStream memoryStream) + { + return memoryStream != null && memoryStream.Length > 0; + } + + /// + /// Wrapper for Clipboard.GetImage, Created for Bug #3432313 + /// + /// Image if there is an image on the clipboard + public static Image GetImage() + { + IDataObject clipboardData = GetDataObject(); + // Return the first image + foreach (Image clipboardImage in GetImages(clipboardData)) + { + return clipboardImage; + } + return null; + } + + /// + /// Get all images (multiple if filenames are available) from the dataObject + /// Returned images must be disposed by the calling code! + /// + /// + /// IEnumerable + public static IEnumerable GetImages(IDataObject dataObject) + { + // Get single image, this takes the "best" match + Image singleImage = GetImage(dataObject); + if (singleImage != null) + { + LOG.InfoFormat("Got image from clipboard with size {0} and format {1}", singleImage.Size, singleImage.PixelFormat); + yield return singleImage; + } + else + { + // check if files are supplied + List imageFiles = GetImageFilenames(dataObject); + if (imageFiles != null) + { + foreach (string imageFile in imageFiles) + { + Image returnImage = null; + try + { + returnImage = ImageHelper.LoadImage(imageFile); + } + catch (Exception streamImageEx) + { + LOG.Error("Problem retrieving Image from clipboard.", streamImageEx); + } + if (returnImage != null) + { + LOG.InfoFormat("Got image from clipboard with size {0} and format {1}", returnImage.Size, returnImage.PixelFormat); + yield return returnImage; + } + } + } + } + } + + /// + /// Get an Image from the IDataObject, don't check for FileDrop + /// + /// + /// Image or null + private static Image GetImage(IDataObject dataObject) + { + Image returnImage = null; + if (dataObject != null) + { + IList formats = GetFormats(dataObject); + string[] retrieveFormats; + + // Found a weird bug, where PNG's from Outlook 2010 are clipped + // So I build some special logik to get the best format: + if (formats != null && formats.Contains(FORMAT_PNG_OFFICEART) && formats.Contains(DataFormats.Dib)) + { + // Outlook ?? + LOG.Info("Most likely the current clipboard contents come from Outlook, as this has a problem with PNG and others we place the DIB format to the front..."); + retrieveFormats = new string[] { DataFormats.Dib, FORMAT_BITMAP_PLACEHOLDER, FORMAT_FILECONTENTS, FORMAT_PNG_OFFICEART, FORMAT_PNG, FORMAT_JFIF_OFFICEART, FORMAT_JPG, FORMAT_JFIF, DataFormats.Tiff, FORMAT_GIF }; + } + else + { + retrieveFormats = new string[] { FORMAT_PNG_OFFICEART, FORMAT_PNG, FORMAT_JFIF_OFFICEART, FORMAT_JPG, FORMAT_JFIF, DataFormats.Tiff, DataFormats.Dib, FORMAT_BITMAP_PLACEHOLDER, FORMAT_FILECONTENTS, FORMAT_GIF }; + } + foreach (string currentFormat in retrieveFormats) + { + if (FORMAT_BITMAP_PLACEHOLDER.Equals(currentFormat)) + { + LOG.Info("Using default .NET Clipboard.GetImage()"); + try + { + returnImage = Clipboard.GetImage(); + if (returnImage != null) + { + return returnImage; + } + else + { + LOG.Info("Clipboard.GetImage() didn't return an image."); + } + } + catch (Exception ex) + { + LOG.Error("Problem retrieving Image via Clipboard.GetImage(): ", ex); + } + } + else if (formats.Contains(currentFormat)) + { + LOG.InfoFormat("Found {0}, trying to retrieve.", currentFormat); + if (currentFormat == DataFormats.Dib) + { + returnImage = GetDIBImage(dataObject); + } + else + { + returnImage = GetImageFormat(currentFormat, dataObject); + } + if (returnImage != null) + { + return returnImage; + } + } + else + { + LOG.DebugFormat("Couldn't find format {0}.", currentFormat); + } + } + } + return null; + } + + /// + /// the DIB readed should solve the issue reported here: https://sourceforge.net/projects/greenshot/forums/forum/676083/topic/6354353/index/page/1 + /// + /// Image + private static Image GetDIBImage(IDataObject dataObejct) + { + try + { + // If the EnableSpecialDIBClipboardReader flag in the config is set, use the code from: + // http://www.thomaslevesque.com/2009/02/05/wpf-paste-an-image-from-the-clipboard/ + // to read the DeviceIndependentBitmap from the clipboard, this might fix bug 3576125 + if (config.EnableSpecialDIBClipboardReader) + { + MemoryStream dibStream = GetFromDataObject(dataObejct, DataFormats.Dib) as MemoryStream; + if (isValidStream(dibStream)) + { + LOG.Info("Found valid DIB stream, trying to process it."); + byte[] dibBuffer = new byte[dibStream.Length]; + dibStream.Read(dibBuffer, 0, dibBuffer.Length); + BitmapInfoHeader infoHeader = BinaryStructHelper.FromByteArray(dibBuffer); + // Only use this code, when the biCommpression != 0 (BI_RGB) + if (infoHeader.biCompression != 0) + { + LOG.InfoFormat("Using special DIB format reader for biCompression {0}", infoHeader.biCompression); + int fileHeaderSize = Marshal.SizeOf(typeof(BitmapFileHeader)); + uint infoHeaderSize = infoHeader.biSize; + int fileSize = (int)(fileHeaderSize + infoHeader.biSize + infoHeader.biSizeImage); + + BitmapFileHeader fileHeader = new BitmapFileHeader(); + fileHeader.bfType = BitmapFileHeader.BM; + fileHeader.bfSize = fileSize; + fileHeader.bfReserved1 = 0; + fileHeader.bfReserved2 = 0; + fileHeader.bfOffBits = (int)(fileHeaderSize + infoHeaderSize + infoHeader.biClrUsed * 4); + + byte[] fileHeaderBytes = BinaryStructHelper.ToByteArray(fileHeader); + + using (MemoryStream bitmapStream = new MemoryStream()) + { + bitmapStream.Write(fileHeaderBytes, 0, fileHeaderSize); + bitmapStream.Write(dibBuffer, 0, dibBuffer.Length); + bitmapStream.Seek(0, SeekOrigin.Begin); + using (Image tmpImage = Image.FromStream(bitmapStream)) + { + if (tmpImage != null) + { + return ImageHelper.Clone(tmpImage); + } + } + } + } + else + { + LOG.InfoFormat("Skipping special DIB format reader for biCompression {0}", infoHeader.biCompression); + } + } + } + else + { + LOG.Info("Skipping special DIB format reader as it's disabled in the configuration."); + } + } + catch (Exception dibEx) + { + LOG.Error("Problem retrieving DIB from clipboard.", dibEx); + } + return null; + } + + /// + /// Helper method to try to get an image in the specified format from the dataObject + /// + /// string with the format + /// IDataObject + /// Image or null + private static Image GetImageFormat(string format, IDataObject dataObject) + { + MemoryStream imageStream = GetFromDataObject(dataObject, format) as MemoryStream; + if (isValidStream(imageStream)) + { + try + { + using (FileStream fs = new FileStream(@"C:\Localdata\test.png", FileMode.OpenOrCreate)) + { + imageStream.WriteTo(fs); + } + imageStream.Seek(0, SeekOrigin.Begin); + using (Image tmpImage = Image.FromStream(imageStream, true, true)) + { + if (tmpImage != null) + { + LOG.InfoFormat("Got image with clipboard format {0} from the clipboard.", format); + return ImageHelper.Clone(tmpImage); + } + } + } + catch (Exception streamImageEx) + { + LOG.Error(string.Format("Problem retrieving {0} from clipboard.", format), streamImageEx); + } + } + return null; + } + + /// + /// Wrapper for Clipboard.GetText created for Bug #3432313 + /// + /// string if there is text on the clipboard + public static string GetText() + { + return GetText(GetDataObject()); + } + + /// + /// Get Text from the DataObject + /// + /// string if there is text on the clipboard + public static string GetText(IDataObject dataObject) + { + if (ContainsText(dataObject)) + { + return (String)dataObject.GetData(DataFormats.Text); + } + return null; + } + + /// + /// Set text to the clipboard + /// + /// + public static void SetClipboardData(string text) + { + IDataObject ido = new DataObject(); + ido.SetData(DataFormats.Text, true, text); + SetDataObject(ido, true); + } + + private static string getHTMLString(ISurface surface, string filename) + { + string utf8EncodedHTMLString = Encoding.GetEncoding(0).GetString(Encoding.UTF8.GetBytes(HTML_CLIPBOARD_STRING)); + utf8EncodedHTMLString = utf8EncodedHTMLString.Replace("${width}", surface.Image.Width.ToString()); + utf8EncodedHTMLString = utf8EncodedHTMLString.Replace("${height}", surface.Image.Height.ToString()); + utf8EncodedHTMLString = utf8EncodedHTMLString.Replace("${file}", filename); + StringBuilder sb = new StringBuilder(); + sb.Append(utf8EncodedHTMLString); + sb.Replace("<<<<<<<1", (utf8EncodedHTMLString.IndexOf("") + "".Length).ToString("D8")); + sb.Replace("<<<<<<<2", (utf8EncodedHTMLString.IndexOf("")).ToString("D8")); + sb.Replace("<<<<<<<3", (utf8EncodedHTMLString.IndexOf("") + "".Length).ToString("D8")); + sb.Replace("<<<<<<<4", (utf8EncodedHTMLString.IndexOf("")).ToString("D8")); + return sb.ToString(); + } + + private static string getHTMLDataURLString(ISurface surface, MemoryStream pngStream) + { + string utf8EncodedHTMLString = Encoding.GetEncoding(0).GetString(Encoding.UTF8.GetBytes(HTML_CLIPBOARD_BASE64_STRING)); + utf8EncodedHTMLString = utf8EncodedHTMLString.Replace("${width}", surface.Image.Width.ToString()); + utf8EncodedHTMLString = utf8EncodedHTMLString.Replace("${height}", surface.Image.Height.ToString()); + utf8EncodedHTMLString = utf8EncodedHTMLString.Replace("${format}", "png"); + utf8EncodedHTMLString = utf8EncodedHTMLString.Replace("${data}", Convert.ToBase64String(pngStream.GetBuffer(), 0, (int)pngStream.Length)); + StringBuilder sb = new StringBuilder(); + sb.Append(utf8EncodedHTMLString); + sb.Replace("<<<<<<<1", (utf8EncodedHTMLString.IndexOf("") + "".Length).ToString("D8")); + sb.Replace("<<<<<<<2", (utf8EncodedHTMLString.IndexOf("")).ToString("D8")); + sb.Replace("<<<<<<<3", (utf8EncodedHTMLString.IndexOf("") + "".Length).ToString("D8")); + sb.Replace("<<<<<<<4", (utf8EncodedHTMLString.IndexOf("")).ToString("D8")); + return sb.ToString(); + } + + /// + /// Set Object with type Type to the clipboard + /// + /// Type + /// object + public static void SetClipboardData(Type type, Object obj) + { + DataFormats.Format format = DataFormats.GetFormat(type.FullName); + + //now copy to clipboard + IDataObject dataObj = new DataObject(); + dataObj.SetData(format.Name, false, obj); + // Use false to make the object dissapear when the application stops. + SetDataObject(dataObj, true); + } + + /// + /// Retrieve a list of all formats currently on the clipboard + /// + /// List with the current formats + public static List GetFormats() + { + return GetFormats(GetDataObject()); + } + + /// + /// Retrieve a list of all formats currently in the IDataObject + /// + /// List with the current formats + public static List GetFormats(IDataObject dataObj) + { + string[] formats = null; + + if (dataObj != null) + { + formats = dataObj.GetFormats(); + } + if (formats != null) + { + LOG.DebugFormat("Got clipboard formats: {0}", String.Join(",", formats)); + return new List(formats); + } + return new List(); + } + + /// + /// Check if there is currently something in the dataObject which has the supplied format + /// + /// IDataObject + /// string with format + /// true if one the format is found + public static bool ContainsFormat(string format) + { + return ContainsFormat(GetDataObject(), new string[] { format }); + } + + /// + /// Check if there is currently something on the clipboard which has the supplied format + /// + /// string with format + /// true if one the format is found + public static bool ContainsFormat(IDataObject dataObject, string format) + { + return ContainsFormat(dataObject, new string[] { format }); + } + + /// + /// Check if there is currently something on the clipboard which has one of the supplied formats + /// + /// string[] with formats + /// true if one of the formats was found + public static bool ContainsFormat(string[] formats) + { + return ContainsFormat(GetDataObject(), formats); + } + + /// + /// Check if there is currently something on the clipboard which has one of the supplied formats + /// + /// IDataObject + /// string[] with formats + /// true if one of the formats was found + public static bool ContainsFormat(IDataObject dataObject, string[] formats) + { + bool formatFound = false; + List currentFormats = GetFormats(dataObject); + if (currentFormats == null || currentFormats.Count == 0 || formats == null || formats.Length == 0) + { + return false; + } + foreach (string format in formats) + { + if (currentFormats.Contains(format)) + { + formatFound = true; + break; + } + } + return formatFound; + } + + /// + /// Get Object of type Type from the clipboard + /// + /// Type to get + /// object from clipboard + public static Object GetClipboardData(Type type) + { + string format = type.FullName; + return GetClipboardData(format); + } + + /// + /// Get Object for format from IDataObject + /// + /// IDataObject + /// Type to get + /// object from IDataObject + public static Object GetFromDataObject(IDataObject dataObj, Type type) + { + if (type != null) + { + return GetFromDataObject(dataObj, type.FullName); + } + return null; + } + + /// + /// Get ImageFilenames from the IDataObject + /// + /// IDataObject + /// + public static List GetImageFilenames(IDataObject dataObject) + { + List filenames = new List(); + string[] dropFileNames = (string[])dataObject.GetData(DataFormats.FileDrop); + try + { + if (dropFileNames != null && dropFileNames.Length > 0) + { + foreach (string filename in dropFileNames) + { + string ext = Path.GetExtension(filename).ToLower(); + if ((ext == ".jpg") || (ext == ".jpeg") || (ext == ".tiff") || (ext == ".gif") || (ext == ".png") || (ext == ".bmp") || (ext == ".ico") || (ext == ".wmf")) + { + filenames.Add(filename); + } + } + } + } + catch (Exception ex) + { + LOG.Warn("Ignoring an issue with getting the dropFilenames from the clipboard: ", ex); + } + return filenames; + } + + /// + /// Get Object for format from IDataObject + /// + /// IDataObject + /// format to get + /// object from IDataObject + public static Object GetFromDataObject(IDataObject dataObj, string format) + { + if (dataObj != null) + { + try + { + return dataObj.GetData(format); + } + catch (Exception e) + { + LOG.Error("Error in GetClipboardData.", e); + } + } + return null; + } + + /// + /// Get Object for format from the clipboard + /// + /// format to get + /// object from clipboard + public static Object GetClipboardData(string format) + { + return GetFromDataObject(GetDataObject(), format); + } + } +} \ No newline at end of file diff --git a/GreenshotImageEditor/Core/Effects.cs b/GreenshotImageEditor/Core/Effects.cs new file mode 100644 index 000000000..ba5b28d03 --- /dev/null +++ b/GreenshotImageEditor/Core/Effects.cs @@ -0,0 +1,376 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2013 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: http://getgreenshot.org/ + * The Greenshot project is hosted on Sourceforge: http://sourceforge.net/projects/greenshot/ + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using GreenshotPlugin; +using GreenshotPlugin.Core; +using System; +using System.Drawing; +using System.Drawing.Imaging; + +namespace Greenshot.Core +{ + /// + /// Interface to describe an effect + /// + public interface IEffect + { + Image Apply(Image sourceImage, out Point offsetChange); + } + + /// + /// DropShadowEffect + /// + public class DropShadowEffect : IEffect + { + public DropShadowEffect() + { + Darkness = 0.6f; + ShadowSize = 9; + ShadowOffset = new Point(0, 0); + } + + public float Darkness + { + get; + set; + } + public int ShadowSize + { + get; + set; + } + public Point ShadowOffset + { + get; + set; + } + + public virtual Image Apply(Image sourceImage, out Point offsetChange) + { + return ImageHelper.CreateShadow(sourceImage, Darkness, ShadowSize, ShadowOffset, out offsetChange, PixelFormat.Format32bppArgb); + } + } + + /// + /// TornEdgeEffect extends on DropShadowEffect + /// + public class TornEdgeEffect : DropShadowEffect + { + public TornEdgeEffect() + : base() + { + ShadowSize = 7; + ToothHeight = 12; + HorizontalToothRange = 20; + VerticalToothRange = 20; + } + + public int ToothHeight + { + get; + set; + } + public int HorizontalToothRange + { + get; + set; + } + public int VerticalToothRange + { + get; + set; + } + + public override Image Apply(Image sourceImage, out Point offsetChange) + { + using (Image tmpTornImage = ImageHelper.CreateTornEdge(sourceImage, ToothHeight, HorizontalToothRange, VerticalToothRange)) + { + return ImageHelper.CreateShadow(tmpTornImage, Darkness, ShadowSize, ShadowOffset, out offsetChange, PixelFormat.Format32bppArgb); + } + } + } + + /// + /// GrayscaleEffect + /// + public class GrayscaleEffect : IEffect + { + public Image Apply(Image sourceImage, out Point offsetChange) + { + offsetChange = Point.Empty; + return ImageHelper.CreateGrayscale(sourceImage); + } + } + + /// + /// MonochromeEffect + /// + public class MonochromeEffect : IEffect + { + private byte threshold; + + /// Threshold for monochrome filter (0 - 255), lower value means less black + public MonochromeEffect(byte threshold) + { + this.threshold = threshold; + } + + public Image Apply(Image sourceImage, out Point offsetChange) + { + offsetChange = Point.Empty; + return ImageHelper.CreateMonochrome(sourceImage, threshold); + } + } + + /// + /// AdjustEffect + /// + public class AdjustEffect : IEffect + { + public AdjustEffect() + : base() + { + Contrast = 1f; + Brightness = 1f; + Gamma = 1f; + } + + public float Contrast + { + get; + set; + } + public float Brightness + { + get; + set; + } + public float Gamma + { + get; + set; + } + + public Image Apply(Image sourceImage, out Point offsetChange) + { + offsetChange = Point.Empty; + return ImageHelper.Adjust(sourceImage, Brightness, Contrast, Gamma); + } + } + + /// + /// ReduceColorsEffect + /// + public class ReduceColorsEffect : IEffect + { + public ReduceColorsEffect() + : base() + { + Colors = 256; + } + + public int Colors + { + get; + set; + } + + public Image Apply(Image sourceImage, out Point offsetChange) + { + offsetChange = Point.Empty; + using (WuQuantizer quantizer = new WuQuantizer((Bitmap)sourceImage)) + { + int colorCount = quantizer.GetColorCount(); + if (colorCount > Colors) + { + try + { + return quantizer.GetQuantizedImage(Colors); + } + catch (Exception e) + { + LOG.Warn("Error occurred while Quantizing the image, ignoring and using original. Error: ", e); + } + } + } + return null; + } + } + + /// + /// InvertEffect + /// + public class InvertEffect : IEffect + { + public Image Apply(Image sourceImage, out Point offsetChange) + { + offsetChange = Point.Empty; + return ImageHelper.CreateNegative(sourceImage); + } + } + + /// + /// BorderEffect + /// + public class BorderEffect : IEffect + { + public BorderEffect() + { + Width = 2; + Color = Color.Black; + } + + public Color Color + { + get; + set; + } + public int Width + { + get; + set; + } + + public Image Apply(Image sourceImage, out Point offsetChange) + { + return ImageHelper.CreateBorder(sourceImage, Width, Color, sourceImage.PixelFormat, out offsetChange); + } + } + + /// + /// RotateEffect + /// + public class RotateEffect : IEffect + { + public RotateEffect(int angle) + { + Angle = angle; + } + + public int Angle + { + get; + set; + } + + public Image Apply(Image sourceImage, out Point offsetChange) + { + offsetChange = Point.Empty; + RotateFlipType flipType; + if (Angle == 90) + { + flipType = RotateFlipType.Rotate90FlipNone; + } + else if (Angle == -90 || Angle == 270) + { + flipType = RotateFlipType.Rotate270FlipNone; + } + else + { + throw new NotSupportedException("Currently only an angle of 90 or -90 (270) is supported."); + } + return ImageHelper.RotateFlip(sourceImage, flipType); + } + } + + /// + /// ResizeEffect + /// + public class ResizeEffect : IEffect + { + public ResizeEffect(int width, int height, bool maintainAspectRatio) + { + Width = width; + Height = height; + MaintainAspectRatio = maintainAspectRatio; + } + + public int Width + { + get; + set; + } + public int Height + { + get; + set; + } + public bool MaintainAspectRatio + { + get; + set; + } + + public Image Apply(Image sourceImage, out Point offsetChange) + { + offsetChange = Point.Empty; + return ImageHelper.ResizeImage(sourceImage, MaintainAspectRatio, Width, Height); + } + } + + /// + /// ResizeCanvasEffect + /// + public class ResizeCanvasEffect : IEffect + { + public ResizeCanvasEffect(int left, int right, int top, int bottom) + { + Left = left; + Right = right; + Top = top; + Bottom = bottom; + BackgroundColor = Color.Empty; // Uses the default background color depending on the format + } + + public int Left + { + get; + set; + } + public int Right + { + get; + set; + } + public int Top + { + get; + set; + } + public int Bottom + { + get; + set; + } + public Color BackgroundColor + { + get; + set; + } + + public Image Apply(Image sourceImage, out Point offsetChange) + { + // Make sure the elements move according to the offset the effect made the bitmap move + offsetChange = new Point(Left, Top); + return ImageHelper.ResizeCanvas(sourceImage, BackgroundColor, Left, Right, Top, Bottom); + } + } +} \ No newline at end of file diff --git a/GreenshotImageEditor/Core/EncryptionHelper.cs b/GreenshotImageEditor/Core/EncryptionHelper.cs new file mode 100644 index 000000000..95cf9fb24 --- /dev/null +++ b/GreenshotImageEditor/Core/EncryptionHelper.cs @@ -0,0 +1,101 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2013 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: http://getgreenshot.org/ + * The Greenshot project is hosted on Sourceforge: http://sourceforge.net/projects/greenshot/ + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using System; +using System.IO; +using System.Security.Cryptography; +using System.Text; + +namespace GreenshotPlugin.Core +{ + public static class EncryptionHelper + { + private const string RGBIV = "dlgjowejgogkklwj"; + private const string KEY = "lsjvkwhvwujkagfauguwcsjgu2wueuff"; + + /// + /// A simply rijndael aes encryption, can be used to store passwords + /// + /// the string to call upon + /// an encryped string in base64 form + public static string Encrypt(this string ClearText) + { + string returnValue = ClearText; + try + { + byte[] clearTextBytes = Encoding.ASCII.GetBytes(ClearText); + SymmetricAlgorithm rijn = SymmetricAlgorithm.Create(); + + using (MemoryStream ms = new MemoryStream()) + { + byte[] rgbIV = Encoding.ASCII.GetBytes(RGBIV); + byte[] key = Encoding.ASCII.GetBytes(KEY); + CryptoStream cs = new CryptoStream(ms, rijn.CreateEncryptor(key, rgbIV), CryptoStreamMode.Write); + + cs.Write(clearTextBytes, 0, clearTextBytes.Length); + + cs.Close(); + returnValue = Convert.ToBase64String(ms.ToArray()); + } + } + catch (Exception ex) + { + LOG.ErrorFormat("Error encrypting, error: ", ex.Message); + } + return returnValue; + } + + /// + /// A simply rijndael aes decryption, can be used to store passwords + /// + /// a base64 encoded rijndael encrypted string + /// Decrypeted text + public static string Decrypt(this string EncryptedText) + { + string returnValue = EncryptedText; + try + { + byte[] encryptedTextBytes = Convert.FromBase64String(EncryptedText); + using (MemoryStream ms = new MemoryStream()) + { + SymmetricAlgorithm rijn = SymmetricAlgorithm.Create(); + + byte[] rgbIV = Encoding.ASCII.GetBytes(RGBIV); + byte[] key = Encoding.ASCII.GetBytes(KEY); + + CryptoStream cs = new CryptoStream(ms, rijn.CreateDecryptor(key, rgbIV), + CryptoStreamMode.Write); + + cs.Write(encryptedTextBytes, 0, encryptedTextBytes.Length); + + cs.Close(); + returnValue = Encoding.ASCII.GetString(ms.ToArray()); + } + } + catch (Exception ex) + { + LOG.ErrorFormat("Error decrypting {0}, error: ", EncryptedText, ex.Message); + } + + return returnValue; + } + } +} \ No newline at end of file diff --git a/GreenshotImageEditor/Core/EnumExtensions.cs b/GreenshotImageEditor/Core/EnumExtensions.cs new file mode 100644 index 000000000..819f1b08a --- /dev/null +++ b/GreenshotImageEditor/Core/EnumExtensions.cs @@ -0,0 +1,122 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2013 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: http://getgreenshot.org/ + * The Greenshot project is hosted on Sourceforge: http://sourceforge.net/projects/greenshot/ + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using System; + +namespace GreenshotPlugin.Core +{ + public static class EnumerationExtensions + { + public static bool Has(this Enum type, T value) + { + Type underlyingType = Enum.GetUnderlyingType(value.GetType()); + try + { + if (underlyingType == typeof(int)) + { + return (((int)(object)type & (int)(object)value) == (int)(object)value); + } + else if (underlyingType == typeof(uint)) + { + return (((uint)(object)type & (uint)(object)value) == (uint)(object)value); + } + } + catch + { + } + return false; + } + + public static bool Is(this Enum type, T value) + { + Type underlyingType = Enum.GetUnderlyingType(value.GetType()); + try + { + if (underlyingType == typeof(int)) + { + return (int)(object)type == (int)(object)value; + } + else if (underlyingType == typeof(uint)) + { + return (uint)(object)type == (uint)(object)value; + } + } + catch + { + } + return false; + } + + /// + /// Add a flag to an enum + /// + /// + /// + /// + public static T Add(this Enum type, T value) + { + Type underlyingType = Enum.GetUnderlyingType(value.GetType()); + try + { + if (underlyingType == typeof(int)) + { + return (T)(object)(((int)(object)type | (int)(object)value)); + } + else if (underlyingType == typeof(uint)) + { + return (T)(object)(((uint)(object)type | (uint)(object)value)); + } + } + catch (Exception ex) + { + throw new ArgumentException(string.Format("Could not append value '{0}' to enumerated type '{1}'.", value, typeof(T).Name), ex); + } + throw new ArgumentException(string.Format("Could not append value '{0}' to enumerated type '{1}'.", value, typeof(T).Name)); + } + + /// + /// Remove a flag from an enum type + /// + /// + /// + /// + public static T Remove(this Enum type, T value) + { + Type underlyingType = Enum.GetUnderlyingType(value.GetType()); + try + { + if (underlyingType == typeof(int)) + { + return (T)(object)(((int)(object)type & ~(int)(object)value)); + } + else if (underlyingType == typeof(uint)) + { + return (T)(object)(((uint)(object)type & ~(uint)(object)value)); + } + } + catch (Exception ex) + { + throw new ArgumentException(string.Format("Could not remove value '{0}' from enumerated type '{1}'.", value, typeof(T).Name), ex); + } + throw new ArgumentException(string.Format("Could not remove value '{0}' from enumerated type '{1}'.", value, typeof(T).Name)); + } + } +} \ No newline at end of file diff --git a/GreenshotImageEditor/Core/FastBitmap.cs b/GreenshotImageEditor/Core/FastBitmap.cs new file mode 100644 index 000000000..c3161023b --- /dev/null +++ b/GreenshotImageEditor/Core/FastBitmap.cs @@ -0,0 +1,1171 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2013 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: http://getgreenshot.org/ + * The Greenshot project is hosted on Sourceforge: http://sourceforge.net/projects/greenshot/ + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Drawing.Imaging; + +namespace GreenshotPlugin.Core +{ + /// + /// The interface for the FastBitmap + /// + public interface IFastBitmap : IDisposable + { + /// + /// Get the color at x,y + /// The returned Color object depends on the underlying pixel format + /// + /// int x + /// int y + /// Color + Color GetColorAt(int x, int y); + + /// + /// Set the color at the specified location + /// + /// int x + /// int y + /// Color + void SetColorAt(int x, int y, Color color); + + /// + /// Get the color at x,y + /// The returned byte[] color depends on the underlying pixel format + /// + /// int x + /// int y + /// Set the color at the specified location + /// + /// int x + /// int y + /// byte[] color + void SetColorAt(int x, int y, byte[] color); + + /// + /// Lock the bitmap + /// + void Lock(); + + /// + /// Unlock the bitmap + /// + void Unlock(); + + /// + /// Unlock the bitmap and get the underlying bitmap in one call + /// + /// + Bitmap UnlockAndReturnBitmap(); + + /// + /// Size of the underlying image + /// + Size Size + { + get; + } + + /// + /// Height of the image area that this fastbitmap covers + /// + int Height + { + get; + } + + /// + /// Width of the image area that this fastbitmap covers + /// + int Width + { + get; + } + + /// + /// Top of the image area that this fastbitmap covers + /// + int Top + { + get; + } + + /// + /// Left of the image area that this fastbitmap covers + /// + int Left + { + get; + } + + /// + /// Right of the image area that this fastbitmap covers + /// + int Right + { + get; + } + + /// + /// Bottom of the image area that this fastbitmap covers + /// + int Bottom + { + get; + } + + /// + /// Does the underlying image need to be disposed + /// + bool NeedsDispose + { + get; + set; + } + + /// + /// Returns if this FastBitmap has an alpha channel + /// + bool hasAlphaChannel + { + get; + } + + /// + /// Draw the stored bitmap to the destionation bitmap at the supplied point + /// + /// Graphics + /// Point with location + void DrawTo(Graphics graphics, Point destination); + + /// + /// Draw the stored Bitmap on the Destination bitmap with the specified rectangle + /// Be aware that the stored bitmap will be resized to the specified rectangle!! + /// + /// Graphics + /// Rectangle with destination + void DrawTo(Graphics graphics, Rectangle destinationRect); + + /// + /// Return true if the coordinates are inside the FastBitmap + /// + /// + /// + /// + bool Contains(int x, int y); + + /// + /// Set the bitmap resolution + /// + /// + /// + void SetResolution(float horizontal, float vertical); + } + + /// + /// This interface can be used for when offsetting is needed + /// + public interface IFastBitmapWithOffset : IFastBitmap + { + /// + /// Return true if the coordinates are inside the FastBitmap + /// + /// + /// + /// + new bool Contains(int x, int y); + + /// + /// Set the color at the specified location, using offsetting so the original coordinates can be used + /// + /// int x + /// int y + /// Color color + new void SetColorAt(int x, int y, Color color); + + /// + /// Set the color at the specified location, using offsetting so the original coordinates can be used + /// + /// int x + /// int y + /// byte[] color + new void SetColorAt(int x, int y, byte[] color); + + /// + /// Get the color at x,y + /// The returned Color object depends on the underlying pixel format + /// + /// int x + /// int y + /// Color + new Color GetColorAt(int x, int y); + + /// + /// Get the color at x,y, using offsetting so the original coordinates can be used + /// The returned byte[] color depends on the underlying pixel format + /// + /// int x + /// int y + /// This interface can be used for when clipping is needed + /// + public interface IFastBitmapWithClip : IFastBitmap + { + Rectangle Clip + { + get; + set; + } + + bool InvertClip + { + get; + set; + } + + /// + /// Set the color at the specified location, this doesn't do anything if the location is excluded due to clipping + /// + /// int x + /// int y + /// Color color + new void SetColorAt(int x, int y, Color color); + + /// + /// Set the color at the specified location, this doesn't do anything if the location is excluded due to clipping + /// + /// int x + /// int y + /// byte[] color + new void SetColorAt(int x, int y, byte[] color); + + /// + /// Return true if the coordinates are inside the FastBitmap and not clipped + /// + /// + /// + /// + new bool Contains(int x, int y); + } + + /// + /// This interface is implemented when there is a alpha-blending possibility + /// + public interface IFastBitmapWithBlend : IFastBitmap + { + Color BackgroundBlendColor + { + get; + set; + } + + Color GetBlendedColorAt(int x, int y); + } + + /// + /// The base class for the fast bitmap implementation + /// + public unsafe abstract class FastBitmap : IFastBitmap, IFastBitmapWithClip, IFastBitmapWithOffset + { + protected const int PIXELFORMAT_INDEX_A = 3; + protected const int PIXELFORMAT_INDEX_R = 2; + protected const int PIXELFORMAT_INDEX_G = 1; + protected const int PIXELFORMAT_INDEX_B = 0; + + public const int COLOR_INDEX_R = 0; + public const int COLOR_INDEX_G = 1; + public const int COLOR_INDEX_B = 2; + public const int COLOR_INDEX_A = 3; + + protected Rectangle area = Rectangle.Empty; + /// + /// If this is set to true, the bitmap will be disposed when disposing the IFastBitmap + /// + public bool NeedsDispose + { + get; + set; + } + + public Rectangle Clip + { + get; + set; + } + + public bool InvertClip + { + get; + set; + } + + /// + /// The bitmap for which the FastBitmap is creating access + /// + protected Bitmap bitmap; + + protected BitmapData bmData; + protected int stride; /* bytes per pixel row */ + protected bool bitsLocked = false; + protected byte* pointer; + + public static IFastBitmap Create(Bitmap source) + { + return Create(source, Rectangle.Empty); + } + + public void SetResolution(float horizontal, float vertical) + { + bitmap.SetResolution(horizontal, vertical); + } + + /// + /// Factory for creating a FastBitmap depending on the pixelformat of the source + /// The supplied rectangle specifies the area for which the FastBitmap does its thing + /// + /// Bitmap to access + /// Rectangle which specifies the area to have access to, can be Rectangle.Empty for the whole image + /// IFastBitmap + public static IFastBitmap Create(Bitmap source, Rectangle area) + { + switch (source.PixelFormat) + { + case PixelFormat.Format8bppIndexed: + return new FastChunkyBitmap(source, area); + case PixelFormat.Format24bppRgb: + return new Fast24RGBBitmap(source, area); + case PixelFormat.Format32bppRgb: + return new Fast32RGBBitmap(source, area); + case PixelFormat.Format32bppArgb: + case PixelFormat.Format32bppPArgb: + return new Fast32ARGBBitmap(source, area); + default: + throw new NotSupportedException(string.Format("Not supported Pixelformat {0}", source.PixelFormat)); + } + } + + /// + /// Factory for creating a FastBitmap as a destination for the source + /// + /// Bitmap to clone + /// IFastBitmap + public static IFastBitmap CreateCloneOf(Image source) + { + return CreateCloneOf(source, source.PixelFormat, Rectangle.Empty); + } + + /// + /// Factory for creating a FastBitmap as a destination for the source + /// + /// Bitmap to clone + /// new Pixelformat + /// IFastBitmap + public static IFastBitmap CreateCloneOf(Image source, PixelFormat pixelFormat) + { + return CreateCloneOf(source, pixelFormat, Rectangle.Empty); + } + + /// + /// Factory for creating a FastBitmap as a destination for the source + /// + /// Bitmap to clone + /// Area of the bitmap to access, can be Rectangle.Empty for the whole + /// IFastBitmap + public static IFastBitmap CreateCloneOf(Image source, Rectangle area) + { + return CreateCloneOf(source, PixelFormat.DontCare, area); + } + + /// + /// Factory for creating a FastBitmap as a destination for the source + /// + /// Bitmap to clone + /// Pixelformat of the cloned bitmap + /// Area of the bitmap to access, can be Rectangle.Empty for the whole + /// IFastBitmap + public static IFastBitmap CreateCloneOf(Image source, PixelFormat pixelFormat, Rectangle area) + { + Bitmap destination = ImageHelper.CloneArea(source, area, pixelFormat); + FastBitmap fastBitmap = Create(destination) as FastBitmap; + fastBitmap.NeedsDispose = true; + fastBitmap.Left = area.Left; + fastBitmap.Top = area.Top; + return fastBitmap; + } + + /// + /// Factory for creating a FastBitmap as a destination + /// + /// + /// + /// + /// IFastBitmap + public static IFastBitmap CreateEmpty(Size newSize, PixelFormat pixelFormat, Color backgroundColor) + { + Bitmap destination = ImageHelper.CreateEmpty(newSize.Width, newSize.Height, pixelFormat, backgroundColor, 96f, 96f); + IFastBitmap fastBitmap = Create(destination); + fastBitmap.NeedsDispose = true; + return fastBitmap; + } + + /// + /// Constructor which stores the image and locks it when called + /// + /// + protected FastBitmap(Bitmap bitmap, Rectangle area) + { + this.bitmap = bitmap; + Rectangle bitmapArea = new Rectangle(Point.Empty, bitmap.Size); + if (area != Rectangle.Empty) + { + area.Intersect(bitmapArea); + this.area = area; + } + else + { + this.area = bitmapArea; + } + // As the lock takes care that only the specified area is made available we need to calculate the offset + Left = area.Left; + Top = area.Top; + // Default cliping is done to the area without invert + Clip = this.area; + InvertClip = false; + // Always lock, so we don't need to do this ourselves + Lock(); + } + + /// + /// Return the size of the image + /// + public Size Size + { + get + { + if (area == Rectangle.Empty) + { + return bitmap.Size; + } + return area.Size; + } + } + + /// + /// Return the width of the image + /// + public int Width + { + get + { + if (area == Rectangle.Empty) + { + return bitmap.Width; + } + return area.Width; + } + } + + /// + /// Return the height of the image + /// + public int Height + { + get + { + if (area == Rectangle.Empty) + { + return bitmap.Height; + } + return area.Height; + } + } + + private int left; + /// + /// Return the left of the fastbitmap, this is also used as an offset + /// + public int Left + { + get + { + return 0; + } + set + { + left = value; + } + } + + /// + /// Return the left of the fastbitmap, this is also used as an offset + /// + int IFastBitmapWithOffset.Left + { + get + { + return left; + } + set + { + left = value; + } + } + + private int top; + /// + /// Return the top of the fastbitmap, this is also used as an offset + /// + public int Top + { + get + { + return 0; + } + set + { + top = value; + } + } + + /// + /// Return the top of the fastbitmap, this is also used as an offset + /// + int IFastBitmapWithOffset.Top + { + get + { + return top; + } + set + { + top = value; + } + } + + /// + /// Return the right of the fastbitmap + /// + public int Right + { + get + { + return Left + Width; + } + } + + /// + /// Return the bottom of the fastbitmap + /// + public int Bottom + { + get + { + return Top + Height; + } + } + + /// + /// Returns the underlying bitmap, unlocks it and prevents that it will be disposed + /// + public Bitmap UnlockAndReturnBitmap() + { + if (bitsLocked) + { + Unlock(); + } + NeedsDispose = false; + return bitmap; + } + + public virtual bool hasAlphaChannel + { + get + { + return false; + } + } + + /// + /// Destructor + /// + ~FastBitmap() + { + Dispose(false); + } + + /// + /// The public accessible Dispose + /// Will call the GarbageCollector to SuppressFinalize, preventing being cleaned twice + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + // 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 virtual void Dispose(bool disposing) + { + Unlock(); + if (disposing) + { + if (bitmap != null && NeedsDispose) + { + bitmap.Dispose(); + } + } + bitmap = null; + bmData = null; + pointer = null; + } + + /// + /// Lock the bitmap so we have direct access to the memory + /// + public void Lock() + { + if (Width > 0 && Height > 0 && !bitsLocked) + { + bmData = bitmap.LockBits(area, ImageLockMode.ReadWrite, bitmap.PixelFormat); + bitsLocked = true; + + IntPtr Scan0 = bmData.Scan0; + pointer = (byte*)(void*)Scan0; + stride = bmData.Stride; + } + } + + /// + /// Unlock the System Memory + /// + public void Unlock() + { + if (bitsLocked) + { + bitmap.UnlockBits(bmData); + bitsLocked = false; + } + } + + /// + /// Draw the stored bitmap to the destionation bitmap at the supplied point + /// + /// + /// + public void DrawTo(Graphics graphics, Point destination) + { + DrawTo(graphics, new Rectangle(destination, area.Size)); + } + + /// + /// Draw the stored Bitmap on the Destination bitmap with the specified rectangle + /// Be aware that the stored bitmap will be resized to the specified rectangle!! + /// + /// + /// + /// + public void DrawTo(Graphics graphics, Rectangle destinationRect) + { + // Make sure this.bitmap is unlocked, if it was locked + bool isLocked = bitsLocked; + if (isLocked) + { + Unlock(); + } + + graphics.DrawImage(bitmap, destinationRect, area, GraphicsUnit.Pixel); + } + + /// + /// returns true if x & y are inside the FastBitmap + /// + /// + /// + /// true if x & y are inside the FastBitmap + public bool Contains(int x, int y) + { + return area.Contains(x - Left, y - Top); + } + + public abstract Color GetColorAt(int x, int y); + + public abstract void SetColorAt(int x, int y, Color color); + + public abstract void GetColorAt(int x, int y, byte[] color); + + public abstract void SetColorAt(int x, int y, byte[] color); + + #region IFastBitmapWithClip + + bool IFastBitmapWithClip.Contains(int x, int y) + { + bool contains = Clip.Contains(x, y); + if (InvertClip) + { + return !contains; + } + else + { + return contains; + } + } + + void IFastBitmapWithClip.SetColorAt(int x, int y, byte[] color) + { + bool contains = Clip.Contains(x, y); + if ((InvertClip && contains) || (!InvertClip && !contains)) + { + return; + } + SetColorAt(x, y, color); + } + + void IFastBitmapWithClip.SetColorAt(int x, int y, Color color) + { + bool contains = Clip.Contains(x, y); + if ((InvertClip && contains) || (!InvertClip && !contains)) + { + return; + } + SetColorAt(x, y, color); + } + + #endregion IFastBitmapWithClip + + #region IFastBitmapWithOffset + + /// + /// returns true if x & y are inside the FastBitmap + /// + /// + /// + /// true if x & y are inside the FastBitmap + bool IFastBitmapWithOffset.Contains(int x, int y) + { + return area.Contains(x - Left, y - Top); + } + + Color IFastBitmapWithOffset.GetColorAt(int x, int y) + { + x -= left; + y -= top; + return GetColorAt(x, y); + } + + void IFastBitmapWithOffset.GetColorAt(int x, int y, byte[] color) + { + x -= left; + y -= top; + GetColorAt(x, y, color); + } + + void IFastBitmapWithOffset.SetColorAt(int x, int y, byte[] color) + { + x -= left; + y -= top; + SetColorAt(x, y, color); + } + + void IFastBitmapWithOffset.SetColorAt(int x, int y, Color color) + { + x -= left; + y -= top; + SetColorAt(x, y, color); + } + + #endregion IFastBitmapWithOffset + } + + /// + /// This is the implementation of the FastBitmat for the 8BPP pixelformat + /// + public unsafe class FastChunkyBitmap : FastBitmap + { + // Used for indexed images + private Color[] colorEntries; + private Dictionary colorCache = new Dictionary(); + + public FastChunkyBitmap(Bitmap source, Rectangle area) + : base(source, area) + { + colorEntries = bitmap.Palette.Entries; + } + + /// + /// Get the color from the specified location + /// + /// + /// + /// Color + public override Color GetColorAt(int x, int y) + { + int offset = x + (y * stride); + byte colorIndex = pointer[offset]; + return colorEntries[colorIndex]; + } + + /// + /// Get the color from the specified location into the specified array + /// + /// + /// + /// byte[4] as reference + public override void GetColorAt(int x, int y, byte[] color) + { + throw new NotImplementedException("No performance gain!"); + } + + /// + /// Set the color at the specified location from the specified array + /// + /// + /// + /// byte[4] as reference + public override void SetColorAt(int x, int y, byte[] color) + { + throw new NotImplementedException("No performance gain!"); + } + + /// + /// Get the color-index from the specified location + /// + /// + /// + /// byte with index + public byte GetColorIndexAt(int x, int y) + { + int offset = x + (y * stride); + return pointer[offset]; + } + + /// + /// Set the color-index at the specified location + /// + /// + /// + /// + public void SetColorIndexAt(int x, int y, byte colorIndex) + { + int offset = x + (y * stride); + pointer[offset] = colorIndex; + } + + /// + /// Set the supplied color at the specified location. + /// Throws an ArgumentException if the color is not in the palette + /// + /// + /// + /// Color to set + public override void SetColorAt(int x, int y, Color color) + { + int offset = x + (y * stride); + byte colorIndex; + if (!colorCache.TryGetValue(color, out colorIndex)) + { + bool foundColor = false; + for (colorIndex = 0; colorIndex < colorEntries.Length; colorIndex++) + { + if (color == colorEntries[colorIndex]) + { + colorCache.Add(color, colorIndex); + foundColor = true; + break; + } + } + if (!foundColor) + { + throw new ArgumentException("No such color!"); + } + } + pointer[offset] = colorIndex; + } + } + + /// + /// This is the implementation of the IFastBitmap for 24 bit images (no Alpha) + /// + public unsafe class Fast24RGBBitmap : FastBitmap + { + public Fast24RGBBitmap(Bitmap source, Rectangle area) + : base(source, area) + { + } + + /// + /// Retrieve the color at location x,y + /// Before the first time this is called the Lock() should be called once! + /// + /// X coordinate + /// Y Coordinate + /// Color + public override Color GetColorAt(int x, int y) + { + int offset = (x * 3) + (y * stride); + return Color.FromArgb(255, pointer[PIXELFORMAT_INDEX_R + offset], pointer[PIXELFORMAT_INDEX_G + offset], pointer[PIXELFORMAT_INDEX_B + offset]); + } + + /// + /// Set the color at location x,y + /// Before the first time this is called the Lock() should be called once! + /// + /// + /// + /// + public override void SetColorAt(int x, int y, Color color) + { + int offset = (x * 3) + (y * stride); + pointer[PIXELFORMAT_INDEX_R + offset] = color.R; + pointer[PIXELFORMAT_INDEX_G + offset] = color.G; + pointer[PIXELFORMAT_INDEX_B + offset] = color.B; + } + + /// + /// Get the color from the specified location into the specified array + /// + /// + /// + /// byte[4] as reference (r,g,b) + public override void GetColorAt(int x, int y, byte[] color) + { + int offset = (x * 3) + (y * stride); + color[PIXELFORMAT_INDEX_R] = pointer[PIXELFORMAT_INDEX_R + offset]; + color[PIXELFORMAT_INDEX_G] = pointer[PIXELFORMAT_INDEX_G + offset]; + color[PIXELFORMAT_INDEX_B] = pointer[PIXELFORMAT_INDEX_B + offset]; + } + + /// + /// Set the color at the specified location from the specified array + /// + /// + /// + /// byte[4] as reference (r,g,b) + public override void SetColorAt(int x, int y, byte[] color) + { + int offset = (x * 3) + (y * stride); + pointer[PIXELFORMAT_INDEX_R + offset] = color[PIXELFORMAT_INDEX_R]; + pointer[PIXELFORMAT_INDEX_G + offset] = color[PIXELFORMAT_INDEX_G]; + pointer[PIXELFORMAT_INDEX_B + offset] = color[PIXELFORMAT_INDEX_B]; + } + } + + /// + /// This is the implementation of the IFastBitmap for 32 bit images (no Alpha) + /// + public unsafe class Fast32RGBBitmap : FastBitmap + { + public Fast32RGBBitmap(Bitmap source, Rectangle area) + : base(source, area) + { + } + + /// + /// Retrieve the color at location x,y + /// Before the first time this is called the Lock() should be called once! + /// + /// X coordinate + /// Y Coordinate + /// Color + public override Color GetColorAt(int x, int y) + { + int offset = (x * 4) + (y * stride); + return Color.FromArgb(255, pointer[PIXELFORMAT_INDEX_R + offset], pointer[PIXELFORMAT_INDEX_G + offset], pointer[PIXELFORMAT_INDEX_B + offset]); + } + + /// + /// Set the color at location x,y + /// Before the first time this is called the Lock() should be called once! + /// + /// + /// + /// + public override void SetColorAt(int x, int y, Color color) + { + int offset = (x * 4) + (y * stride); + pointer[PIXELFORMAT_INDEX_R + offset] = color.R; + pointer[PIXELFORMAT_INDEX_G + offset] = color.G; + pointer[PIXELFORMAT_INDEX_B + offset] = color.B; + } + + /// + /// Get the color from the specified location into the specified array + /// + /// + /// + /// byte[4] as reference (a,r,g,b) + public override void GetColorAt(int x, int y, byte[] color) + { + int offset = (x * 4) + (y * stride); + color[COLOR_INDEX_R] = pointer[PIXELFORMAT_INDEX_R + offset]; + color[COLOR_INDEX_G] = pointer[PIXELFORMAT_INDEX_G + offset]; + color[COLOR_INDEX_B] = pointer[PIXELFORMAT_INDEX_B + offset]; + } + + /// + /// Set the color at the specified location from the specified array + /// + /// + /// + /// byte[4] as reference (r,g,b) + public override void SetColorAt(int x, int y, byte[] color) + { + int offset = (x * 4) + (y * stride); + pointer[PIXELFORMAT_INDEX_R + offset] = color[COLOR_INDEX_R]; // R + pointer[PIXELFORMAT_INDEX_G + offset] = color[COLOR_INDEX_G]; + pointer[PIXELFORMAT_INDEX_B + offset] = color[COLOR_INDEX_B]; + } + } + + /// + /// This is the implementation of the IFastBitmap for 32 bit images with Alpha + /// + public unsafe class Fast32ARGBBitmap : FastBitmap, IFastBitmapWithBlend + { + public override bool hasAlphaChannel + { + get + { + return true; + } + } + + public Color BackgroundBlendColor + { + get; + set; + } + + public Fast32ARGBBitmap(Bitmap source, Rectangle area) + : base(source, area) + { + BackgroundBlendColor = Color.White; + } + + /// + /// Retrieve the color at location x,y + /// + /// X coordinate + /// Y Coordinate + /// Color + public override Color GetColorAt(int x, int y) + { + int offset = (x * 4) + (y * stride); + return Color.FromArgb(pointer[PIXELFORMAT_INDEX_A + offset], pointer[PIXELFORMAT_INDEX_R + offset], pointer[PIXELFORMAT_INDEX_G + offset], pointer[PIXELFORMAT_INDEX_B + offset]); + } + + /// + /// Set the color at location x,y + /// Before the first time this is called the Lock() should be called once! + /// + /// + /// + /// + public override void SetColorAt(int x, int y, Color color) + { + int offset = (x * 4) + (y * stride); + pointer[PIXELFORMAT_INDEX_A + offset] = color.A; + pointer[PIXELFORMAT_INDEX_R + offset] = color.R; + pointer[PIXELFORMAT_INDEX_G + offset] = color.G; + pointer[PIXELFORMAT_INDEX_B + offset] = color.B; + } + + /// + /// Get the color from the specified location into the specified array + /// + /// + /// + /// byte[4] as reference (r,g,b,a) + public override void GetColorAt(int x, int y, byte[] color) + { + int offset = (x * 4) + (y * stride); + color[COLOR_INDEX_R] = pointer[PIXELFORMAT_INDEX_R + offset]; + color[COLOR_INDEX_G] = pointer[PIXELFORMAT_INDEX_G + offset]; + color[COLOR_INDEX_B] = pointer[PIXELFORMAT_INDEX_B + offset]; + color[COLOR_INDEX_A] = pointer[PIXELFORMAT_INDEX_A + offset]; + } + + /// + /// Set the color at the specified location from the specified array + /// + /// + /// + /// byte[4] as reference (r,g,b,a) + public override void SetColorAt(int x, int y, byte[] color) + { + int offset = (x * 4) + (y * stride); + pointer[PIXELFORMAT_INDEX_R + offset] = color[COLOR_INDEX_R]; // R + pointer[PIXELFORMAT_INDEX_G + offset] = color[COLOR_INDEX_G]; + pointer[PIXELFORMAT_INDEX_B + offset] = color[COLOR_INDEX_B]; + pointer[PIXELFORMAT_INDEX_A + offset] = color[COLOR_INDEX_A]; + } + + /// + /// Retrieve the color, without alpha (is blended), at location x,y + /// Before the first time this is called the Lock() should be called once! + /// + /// X coordinate + /// Y Coordinate + /// Color + public Color GetBlendedColorAt(int x, int y) + { + int offset = (x * 4) + (y * stride); + int a = pointer[PIXELFORMAT_INDEX_A + offset]; + int red = pointer[PIXELFORMAT_INDEX_R + offset]; + int green = pointer[PIXELFORMAT_INDEX_G + offset]; + int blue = pointer[PIXELFORMAT_INDEX_B + offset]; + + if (a < 255) + { + // As the request is to get without alpha, we blend. + int rem = 255 - a; + red = (red * a + BackgroundBlendColor.R * rem) / 255; + green = (green * a + BackgroundBlendColor.G * rem) / 255; + blue = (blue * a + BackgroundBlendColor.B * rem) / 255; + } + return Color.FromArgb(255, red, green, blue); + } + } +} \ No newline at end of file diff --git a/GreenshotImageEditor/Core/FilenameHelper.cs b/GreenshotImageEditor/Core/FilenameHelper.cs new file mode 100644 index 000000000..94fa3b8e6 --- /dev/null +++ b/GreenshotImageEditor/Core/FilenameHelper.cs @@ -0,0 +1,545 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2013 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: http://getgreenshot.org/ + * The Greenshot project is hosted on Sourceforge: http://sourceforge.net/projects/greenshot/ + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using Greenshot.IniFile; +using Greenshot.Plugin; +using System; +using System.Collections; +using System.IO; +using System.Text.RegularExpressions; +using System.Windows.Forms; + +namespace GreenshotPlugin.Core +{ + public static class FilenameHelper + { + private static readonly Regex VAR_REGEXP = new Regex(@"\${(?[^:}]+)[:]?(?[^}]*)}", RegexOptions.Compiled); + private static readonly Regex SPLIT_REGEXP = new Regex(";(?=(?:[^\"]*\"[^\"]*\")*[^\"]*$)", RegexOptions.Compiled); + private const int MAX_TITLE_LENGTH = 80; + private static CoreConfiguration conf = IniConfig.GetIniSection(); + private const string UNSAFE_REPLACEMENT = "_"; + + /// + /// Remove invalid characters from the fully qualified filename + /// + /// string with the full path to a file + /// string with the full path to a file, without invalid characters + public static string MakeFQFilenameSafe(string fullPath) + { + string path = MakePathSafe(Path.GetDirectoryName(fullPath)); + string filename = MakeFilenameSafe(Path.GetFileName(fullPath)); + // Make the fullpath again and return + return Path.Combine(path, filename); + } + + /// + /// Remove invalid characters from the filename + /// + /// string with the full path to a file + /// string with the full path to a file, without invalid characters + public static string MakeFilenameSafe(string filename) + { + // Make the filename save! + if (filename != null) + { + foreach (char disallowed in Path.GetInvalidFileNameChars()) + { + filename = filename.Replace(disallowed.ToString(), UNSAFE_REPLACEMENT); + } + } + return filename; + } + + /// + /// Remove invalid characters from the path + /// + /// string with the full path to a file + /// string with the full path to a file, without invalid characters + public static string MakePathSafe(string path) + { + // Make the path save! + if (path != null) + { + foreach (char disallowed in Path.GetInvalidPathChars()) + { + path = path.Replace(disallowed.ToString(), UNSAFE_REPLACEMENT); + } + } + return path; + } + + public static string GetFilenameWithoutExtensionFromPattern(string pattern) + { + return GetFilenameWithoutExtensionFromPattern(pattern, null); + } + + public static string GetFilenameWithoutExtensionFromPattern(string pattern, ICaptureDetails captureDetails) + { + return FillPattern(pattern, captureDetails, true); + } + + public static string GetFilenameFromPattern(string pattern, OutputFormat imageFormat) + { + return GetFilenameFromPattern(pattern, imageFormat, null); + } + + public static string GetFilenameFromPattern(string pattern, OutputFormat imageFormat, ICaptureDetails captureDetails) + { + return FillPattern(pattern, captureDetails, true) + "." + imageFormat.ToString().ToLower(); + } + + /// + /// Return a filename for the current image format (png,jpg etc) with the default file pattern + /// that is specified in the configuration + /// + /// A string with the format + /// The filename which should be used to save the image + public static string GetFilename(OutputFormat format, ICaptureDetails captureDetails) + { + string pattern = conf.OutputFileFilenamePattern; + if (pattern == null || string.IsNullOrEmpty(pattern.Trim())) + { + pattern = "greenshot ${capturetime}"; + } + return GetFilenameFromPattern(pattern, format, captureDetails); + } + + /// + /// This method will be called by the regexp.replace as a MatchEvaluator delegate! + /// Will delegate this to the MatchVarEvaluatorInternal and catch any exceptions + /// What are we matching? + /// The detail, can be null + /// Variables from the process + /// Variables from the user + /// Variables from the machine + /// string with the match replacement + private static string MatchVarEvaluator(Match match, ICaptureDetails captureDetails, IDictionary processVars, IDictionary userVars, IDictionary machineVars, bool filenameSafeMode) + { + try + { + return MatchVarEvaluatorInternal(match, captureDetails, processVars, userVars, machineVars, filenameSafeMode); + } + catch (Exception e) + { + LOG.Error("Error in MatchVarEvaluatorInternal", e); + } + return ""; + } + + /// + /// This method will be called by the regexp.replace as a MatchEvaluator delegate! + /// + /// What are we matching? + /// The detail, can be null + /// + private static string MatchVarEvaluatorInternal(Match match, ICaptureDetails captureDetails, IDictionary processVars, IDictionary userVars, IDictionary machineVars, bool filenameSafeMode) + { + // some defaults + int padWidth = 0; + int startIndex = 0; + int endIndex = 0; + char padChar = ' '; + string dateFormat = "yyyy-MM-dd HH-mm-ss"; + + string replaceValue = ""; + string variable = match.Groups["variable"].Value; + string parameters = match.Groups["parameters"].Value; + + if (parameters != null && parameters.Length > 0) + { + string[] parms = SPLIT_REGEXP.Split(parameters); + foreach (string parameter in parms) + { + switch (parameter.Substring(0, 1)) + { + case "p": + string[] padParams = parameter.Substring(1).Split(new char[] { ',' }); + try + { + padWidth = int.Parse(padParams[0]); + } + catch + { + }; + if (padParams.Length > 1) + { + padChar = padParams[1][0]; + } + break; + case "d": + dateFormat = parameter.Substring(1); + if (dateFormat.StartsWith("\"")) + { + dateFormat = dateFormat.Substring(1); + } + if (dateFormat.EndsWith("\"")) + { + dateFormat = dateFormat.Substring(0, dateFormat.Length - 1); + } + break; + case "s": + string range = parameter.Substring(1); + string[] rangelist = range.Split(new char[] { ',' }); + if (rangelist.Length > 0) + { + try + { + startIndex = int.Parse(rangelist[0]); + } + catch + { + // Ignore + } + } + if (rangelist.Length > 1) + { + try + { + endIndex = int.Parse(rangelist[1]); + } + catch + { + // Ignore + } + } + break; + } + } + } + if (processVars != null && processVars.Contains(variable)) + { + replaceValue = (string)processVars[variable]; + if (filenameSafeMode) + { + replaceValue = MakePathSafe(replaceValue); + } + } + else if (userVars != null && userVars.Contains(variable)) + { + replaceValue = (string)userVars[variable]; + if (filenameSafeMode) + { + replaceValue = MakePathSafe(replaceValue); + } + } + else if (machineVars != null && machineVars.Contains(variable)) + { + replaceValue = (string)machineVars[variable]; + if (filenameSafeMode) + { + replaceValue = MakePathSafe(replaceValue); + } + } + else if (captureDetails != null && captureDetails.MetaData != null && captureDetails.MetaData.ContainsKey(variable)) + { + replaceValue = captureDetails.MetaData[variable]; + if (filenameSafeMode) + { + replaceValue = MakePathSafe(replaceValue); + } + } + else + { + // Handle other variables + // Default use "now" for the capture take´n + DateTime capturetime = DateTime.Now; + // Use default application name for title + string title = Application.ProductName; + + // Check if we have capture details + if (captureDetails != null) + { + capturetime = captureDetails.DateTime; + if (captureDetails.Title != null) + { + title = captureDetails.Title; + if (title.Length > MAX_TITLE_LENGTH) + { + title = title.Substring(0, MAX_TITLE_LENGTH); + } + } + } + switch (variable) + { + case "domain": + replaceValue = Environment.UserDomainName; + break; + case "user": + replaceValue = Environment.UserName; + break; + case "hostname": + replaceValue = Environment.MachineName; + break; + case "YYYY": + if (padWidth == 0) + { + padWidth = -4; + padChar = '0'; + } + replaceValue = capturetime.Year.ToString(); + break; + case "MM": + replaceValue = capturetime.Month.ToString(); + if (padWidth == 0) + { + padWidth = -2; + padChar = '0'; + } + break; + case "DD": + replaceValue = capturetime.Day.ToString(); + if (padWidth == 0) + { + padWidth = -2; + padChar = '0'; + } + break; + case "hh": + if (padWidth == 0) + { + padWidth = -2; + padChar = '0'; + } + replaceValue = capturetime.Hour.ToString(); + break; + case "mm": + if (padWidth == 0) + { + padWidth = -2; + padChar = '0'; + } + replaceValue = capturetime.Minute.ToString(); + break; + case "ss": + if (padWidth == 0) + { + padWidth = -2; + padChar = '0'; + } + replaceValue = capturetime.Second.ToString(); + break; + case "now": + replaceValue = DateTime.Now.ToString(dateFormat); + if (filenameSafeMode) + { + replaceValue = MakeFilenameSafe(replaceValue); + } + break; + case "capturetime": + replaceValue = capturetime.ToString(dateFormat); + if (filenameSafeMode) + { + replaceValue = MakeFilenameSafe(replaceValue); + } + break; + case "NUM": + conf.OutputFileIncrementingNumber++; + IniConfig.Save(); + replaceValue = conf.OutputFileIncrementingNumber.ToString(); + if (padWidth == 0) + { + padWidth = -6; + padChar = '0'; + } + + break; + case "title": + replaceValue = title; + if (filenameSafeMode) + { + replaceValue = MakeFilenameSafe(replaceValue); + } + break; + case "MyPictures": + replaceValue = Environment.GetFolderPath(Environment.SpecialFolder.MyPictures); + break; + case "MyMusic": + replaceValue = Environment.GetFolderPath(Environment.SpecialFolder.MyMusic); + break; + case "MyDocuments": + replaceValue = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments); + break; + case "Personal": + replaceValue = Environment.GetFolderPath(Environment.SpecialFolder.Personal); + break; + case "Desktop": + replaceValue = Environment.GetFolderPath(Environment.SpecialFolder.Desktop); + break; + case "ApplicationData": + replaceValue = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData); + break; + case "LocalApplicationData": + replaceValue = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData); + break; + } + } + // do padding + if (padWidth > 0) + { + replaceValue = replaceValue.PadRight(padWidth, padChar); + } + else if (padWidth < 0) + { + replaceValue = replaceValue.PadLeft(-padWidth, padChar); + } + + // do substring + if (startIndex != 0 || endIndex != 0) + { + if (startIndex < 0) + { + startIndex = replaceValue.Length + startIndex; + } + if (endIndex < 0) + { + endIndex = replaceValue.Length + endIndex; + } + if (endIndex != 0) + { + try + { + replaceValue = replaceValue.Substring(startIndex, endIndex); + } + catch + { + // Ignore + } + } + else + { + try + { + replaceValue = replaceValue.Substring(startIndex); + } + catch + { + // Ignore + } + } + } + + return replaceValue; + } + + /// + /// "Simply" fill the pattern with environment variables + /// + /// String with pattern ${var} + /// true to make sure everything is filenamesafe + /// Filled string + public static string FillVariables(string pattern, bool filenameSafeMode) + { + IDictionary processVars = null; + IDictionary userVars = null; + IDictionary machineVars = null; + try + { + processVars = Environment.GetEnvironmentVariables(EnvironmentVariableTarget.Process); + } + catch (Exception e) + { + LOG.Error("Error retrieving EnvironmentVariableTarget.Process", e); + } + + try + { + userVars = Environment.GetEnvironmentVariables(EnvironmentVariableTarget.User); + } + catch (Exception e) + { + LOG.Error("Error retrieving EnvironmentVariableTarget.User", e); + } + + try + { + machineVars = Environment.GetEnvironmentVariables(EnvironmentVariableTarget.Machine); + } + catch (Exception e) + { + LOG.Error("Error retrieving EnvironmentVariableTarget.Machine", e); + } + + return VAR_REGEXP.Replace(pattern, + delegate(Match m) + { + return MatchVarEvaluator(m, null, processVars, userVars, machineVars, filenameSafeMode); + } + ); + } + + /// + /// Fill the pattern wit the supplied details + /// + /// Pattern + /// CaptureDetails + /// Should the result be made "filename" safe? + /// Filled pattern + public static string FillPattern(string pattern, ICaptureDetails captureDetails, bool filenameSafeMode) + { + IDictionary processVars = null; + IDictionary userVars = null; + IDictionary machineVars = null; + try + { + processVars = Environment.GetEnvironmentVariables(EnvironmentVariableTarget.Process); + } + catch (Exception e) + { + LOG.Error("Error retrieving EnvironmentVariableTarget.Process", e); + } + + try + { + userVars = Environment.GetEnvironmentVariables(EnvironmentVariableTarget.User); + } + catch (Exception e) + { + LOG.Error("Error retrieving EnvironmentVariableTarget.User", e); + } + + try + { + machineVars = Environment.GetEnvironmentVariables(EnvironmentVariableTarget.Machine); + } + catch (Exception e) + { + LOG.Error("Error retrieving EnvironmentVariableTarget.Machine", e); + } + + try + { + return VAR_REGEXP.Replace(pattern, + delegate(Match m) + { + return MatchVarEvaluator(m, captureDetails, processVars, userVars, machineVars, filenameSafeMode); + } + ); + } + catch (Exception e) + { + // adding additional data for bug tracking + e.Data.Add("title", captureDetails.Title); + e.Data.Add("pattern", pattern); + throw; + } + } + } +} \ No newline at end of file diff --git a/GreenshotImageEditor/Core/GreenshotResources.cs b/GreenshotImageEditor/Core/GreenshotResources.cs new file mode 100644 index 000000000..ec605de4d --- /dev/null +++ b/GreenshotImageEditor/Core/GreenshotResources.cs @@ -0,0 +1,54 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2013 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: http://getgreenshot.org/ + * The Greenshot project is hosted on Sourceforge: http://sourceforge.net/projects/greenshot/ + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using System.ComponentModel; +using System.Drawing; + +namespace GreenshotPlugin.Core +{ + /// + /// Centralized storage of the icons & bitmaps + /// + public static class GreenshotResources + { + private static ComponentResourceManager greenshotResources = new ComponentResourceManager(typeof(GreenshotResources)); + + public static Image getImage(string imageName) + { + return (Image)greenshotResources.GetObject(imageName); + } + + public static Icon getIcon(string imageName) + { + return (Icon)greenshotResources.GetObject(imageName); + } + + public static Icon getGreenshotIcon() + { + return getIcon("Greenshot.Icon"); + } + + public static Image getGreenshotImage() + { + return getImage("Greenshot.Image"); + } + } +} \ No newline at end of file diff --git a/GreenshotImageEditor/Core/GreenshotResources.resx b/GreenshotImageEditor/Core/GreenshotResources.resx new file mode 100644 index 000000000..5613cbe7e --- /dev/null +++ b/GreenshotImageEditor/Core/GreenshotResources.resx @@ -0,0 +1,471 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + + AAABAAUAAAAAAAEACAClFwAAVgAAADAwAAABAAgAqA4AAPsXAAAgIAAAAQAIAKgIAACjJgAAGBgAAAEA + CADIBgAASy8AABAQAAABAAgAaAUAABM2AACJUE5HDQoaCgAAAA1JSERSAAABAAAAAQAIBgAAAFxyqGYA + ABdsSURBVHja7Z1fqFVVHsf3YQqnUTJQSJMcujkK3UHuFW5geBXGYK5B0EP6Gto8zIsG8zKY82rCvKXP + 6bv2FqQP9eAfEhS8Eilozo0xTAOFbGycKLjTd9u6nnvvXnuvvff6/dbea30/cEioPPucs9Z3/dbv72By + cnI2I4QkyYACQEi6UAAISRgKACEJQwEgJGEoAIQkDAWAkIShABCSMBQAQhKGAkBIwlAACEkYCgAhCUMB + ICRhKACEJAwFgJCEoQAQkjAUAEIShgJASMJQAAhJGAoAIQlDASAkYSgAhCQMBYCQhKEAEJIwFABCEoYC + QEjCUAAISRgKACEJQwEgJGEoAIQkDAWAkIShABCSMBQAQhKGAkBIwlAACEkYCgAhCUMBICRhKACEJAwF + gJCEoQAQkjAUAEIShgJASMJQAAhJmOgF4MllP2dP/+GH/M8rx77L7t9Ylv304Ins4e0l2X/v/Db04xES + lCgF4Her/pc9v+PbbNXkvezpdT9Y/7uHd5Zkt8+tzL4++Wz2/ZdLQz82IepEJQDY+Ov33Myen/q29v97 + 7/Ly7Nqx32f3ppeH/hiEqBGNAIzsvJVv/ieX/tzq75n5cE12/eja/JpASOxEIQBj715vdOrb+P7G0uyz + fRspAiR6ei8Avje/gSJAUqDXArBh97+z9btviv398AtABAiJld4KwIrx+9kr738u/j5XjoxkMyfWhP64 + hIjQWwF45fDn2Yqx++Lv89MPT2Sf7pzgVYBESS8FQOv0N1w/tjYPERISG70UgIn3rmarttxTez9YAad2 + bA79sQnxTu8EAKm9Ux+fV3/fiwdeyu6cXRH64xPild4JANJ7Jw5eVX9fJAhdOTwS+uMT4pXeCYB06M9G + m5AgfBYoRDJ/BihK+vk/v8nuXn6G6cckGL0TAO37vwGFQ5/setn5v0cFItKTYbFUpSfDx4DrBYqSKAZE + k94JgFb4r4iPtk5W/jcoSBrdN9NYpGBpfHHkRVYnEhUoADWoEgCUIGPzty1IAkxAIhr0TgBCXQFQG3B6 + zybrv8fGH3nzltf3/PrUs9nl99arf1aSDr0TgC46ASWfiSJAJOmdAIQKA9qyATWyEi8fWp87CAnxTe8E + IFQi0Om3Ny1yzOFZth29lD216kfR92Y9ApHCSQDg2cZJh38ivIWFj4aaprEmQleaDTalegDYsIUANa8j + vAoQCawCgE0OrzZi2S4nHJxk8Fojni19UnWhGAjfz/YTF714/F35dNcEOxkTrxQKAE62F3Z902hxw1xF + Tz3pEFbocmCI49j+6+LvPwxDg8Q38wQAJj7CbGWttF2B1/ziuy+JWQN41q3HpsVPYFsRUIhwZFUokpC6 + zAkA7vY4VX1uKNydLxwYFctqkz6Fy+7dUyfPq5r/hlOvbaYzkHgjFwCJzW+ACODUklq0kk1BbactrI/t + xy+KfJ4qPntnY+16ATxvPiTll985d+gOXZ1gqRlHrrYzl4Rn8Kcdm2ex+X2Y/Takm2v6zsK7c25FfvLb + REvbCTlMHQHAc+YFSTWuKvjs8DOwKCkNBn89sWbWdwprEdIOLJxwsAbaWDGuDsyQAuDyPeKUx3fRxkkK + 0YYI0iKIm8E/ZzOVRCCNZBaE5nDiNYlg4L6Pze+y4LtsAfgQQgN+M4gAOyHFi5oAAK3mmhACbAS8sFlt + mwGnHBY3XnVOOtylt31wSetrm0eZAEg5RZmKHC+qAlC3qYYvYBI/tfpxMhOskLaRidfPnFX/HMCWDCRd + I9HE+Ui6j6oAgKKc+j6CGgBJx2kRNgHVyEpkPUKcqAtALNls8DWM7p1RfU9bY1KtpCTWI8SHugA0XUTm + Pr983YNHBUm/nnaI1+NUgnl6+9xKNesiRC5AkfWk7ZCMxYIjj1AXgDo5Adhk8OjDueVq3sJMhoUBp5W0 + uapZlWj73rQrI2kFxEVnBaBNQRKAEKC5pmQIS9MKKHLCheqNwHTkeOicAGBR407rq9JP+sTS6Algu/uH + 6o7EKUnxoC4ASDVFlWAR2PwSacnSIiBZmgwfBwSz6MQN1R/RRz6HaSwDTGMZoo+6ANgWj9TmN0iKgNSz + l21+EKpFepPaDmx4+HIwIcn2PeHvxTUH/hsKgg7qAmBLKNEIZUmGICECcMj5+gwu/RT6IACIUvxx779q + iyPeAwcFk49kURUA25htrVCWRjIL8gPW77nZ2HmJZ/zq+HNOJnaXBcCXLweWG/wfdDrKoCoANjNccyFr + hLGaFCVh48P0xeZ3NX+7KgC++0vgKjR9aAPzDwRQFYCiPPYQlXWaYSxbAhMwzThMQVJdQglAmSNXqrkM + BBKiQxHwy+Dv08tnNRaR7eTVTmQBsVS3dS0KIN2nscopSuoz+PPOiVnp5ppld+8QvfXKTrA+Eaovgc2R + q2GRxPLbdYW8J6B0c03bgglVV29zRvYRbQG1fXeabdJZmuyPua7AIZpJhOysE0s6q8RU4jJsWYmvHr8g + PiLNIN1jMiXmzQXwOd/epZ1UqDssiOUU0a5KLHLkhkhJZlWiHxZNBoJZjsQNjYaSFAA/aH2PNudfiCEp + NkuE1MM6GxDWAF51hKBu9laIphqGmARAOo0alM1JCOHI5ZQkP1ROBzZDJeamAw8tMvwIZqhE3caaIKQP + 4KOtk0HeVwrJ4S5lMfiQDVLb/IZmPeNluH9jWb6GU7paOI0HlyLUhJ1QzUmlkRCBqgScLrdIXwgOMli1 + VdcVfGYcaKgbiV0MggoA0PQeG2LuauNzwCssPMTcyyy7EFOSDa4CgI0Pv1aTdYZrLRrLxCoEgzf2bcwF + IFRNtnYYC6TQ0KLtiHfXgqQu+3F8VmhqzbTQZlEtQNNhGU3RvgbElARUhSlKwintcvrhaoScDZi+rjkS + Xb0C4Do0vv+aV8eo9Mj7EJQWA9UZl9UGzXqAWJW8CmwINOPAgBQUJhng+IL1d/fyM43M3C4mc0nWJMSW + hORUDSi9abSsAOlR5akSYkpSmSNXemhLTDkIzuXA0uaPRjJLTLH/LhGiLNnmyNVKioplLdXqByBdjil5 + FYilBLiLhIgEFG1ATX9SLKHk2g1BpMsxJUQg1Xu/FhqzCYexbT7t3hIxHCqNOgJJz/fzFRqExx93tb7/ + SH1As67DtvG0U5JjcAg2EgCN5ppNu8kaYKlg87O9tA6wAuB8k07qsm26UENS+l5W3rgnoJYn1DV9E6SU + wtlFpEOC+H3P7B4vFPUQCWWg70lljQVAe148Tph5zTV/nSqDxWDi2DF4ZfuOpEOwbLOFapDad/9Sq67A + MThBiH98i4BLc5kQJcmg7z0KWwlAzEU1pB2w1pCK29Yn4DoTIEQyEui7I7CVALRpygAT3qSnDoPUVPzY + dN71nyYDUgx1CpIABaAZrQeD1GnKgAUBpx4WRZV3H7He2+dW1pqUQ7oJfvfckYvGMhX3dJjUMPXrXi1D + lJWDvlvBagLQpjxVqyiJ6LCwGw+sPjiT2zhx6QRshrgA+CrLZFIPKSNUg9m+1wSICgCUHt1pfHpn+25y + ERlC9CaMobdEKwEoK4iQ/EEoAqQIbT9ADOuwlQDYYqDSQyIBcxDIQrSrEouGpPQNkUQgDYdMWVooSRct + KyCG0x+0EoCiQgjNFlGx/AjEHxrrL6bDp7EA2DafdjgmBjOM+EW6MKjvBUDDNBaAoo0XwhMbU3824g+p + 5iCx+Z4aCYBt04UoyYylNRPxj28RiG3zg9oCUNYXMFQ2Fq8BxAbSjyEEbSJSrgVJfaSWAFQ5P0IVZMR0 + JyP+aVqUBOsSab6xnfrDOAsAvowLB0atKogveerj80E+RN/zsYke+cj78fuPhqQUhAtx2qM2wUzHih0n + AUDCDzz+Zd1/Qk6IoQCQpiBpDdOSQs3GDE2pAODUx2RUFyWkABDSPxYJgGms2cQECuUDkG5TTkisDP5y + dG0uAGiqCRO/jaczVF+2vpdkEhKKweTkZKty4GFQ+utjFntd6nQlIoQ8xqsAINQyundG9QP0vSsrISHx + KgAhQoExZmcRooVXAQCaAxq1h5MQEhveBUBzRDPDf4S0w7sAAI0GjW1mEhBCHiEiAECyMAimPwqSYizO + IEQTMQGAQxAi0LYd+EK4+Qnxx+CtwyOzUll0EAHkBviyBLj5CfHL4OCDJ2al+5v58Am4FCQRQuqR1wJo + NNdEdGD9npu1Q4QYvghPP1N9CfHPXDGQVlcdMyA0HxQ5fr+wdgCbHjXZSPChuU+IHHMCELKiztRk85Qn + RJc5AWBcnZD0mNcPgFV1hKTFPAFoUlePWQDos7Z83YNF4T6E7XCHx995+9xK3ucJ6RiNBQCbHuG9OnPY + cM2An4HVe4R0g9oCAM/9+P5rrQYwwsuPXoO0CAgJSy0B8NnwA9cDTBeiNUBIOJwFgLPWCIkPpyiAdKsv + TvYhJAyVeQAaE39jmrdOSJ+ozATUGvjJ5p6E6FNaC4B8/YmDV9Ue5vTbmxgZIESR0mpA7XHfGlWJhJDH + 5AJQdPprNvcchunIhOiRjwYr6qyLTL+x/dfVH4gRAUL0sPYE1OzvPwxbfROih1UAtO//BkYDCNHDKgDb + jl7y3tHXBdQJoPEnIUQeqwC8fuZskAeiABCiR+euABQAQvTonADMfLgmrxIkhMhjFQCN+X5FhGxOSkhq + WAVAOw3YoNWenBBSMRtw6uT5wr79UrAzMSG6lArA6L6ZbOTNW2oPQ/OfEF1KBQD1AFuPTatYAegJ8OnO + Cc7+I0SRyvHgWs5AtgYjRJ9KAQDSWYFM/yUkDE4CIHkVgOMPiT80/QnRx0kAAHoDIjnIpwhw8xMSFmcB + ABjtPfHeVS8ZgjD70f2Hm5+QcNQSAEOTsWCGh3eW5FOB2PSDkPA0EgADhAAvF4sAJz42PT39hHSHVgJg + wNUAPoKVY98t+nd3Lz+Td/qlqU9I9/AiAISQfkIBICRhFglAmTmPKj0MD2W1HiFxMCcAKP+FQ2/VlnuV + /xM8+SjagUOPd3tC+svgjX0bZ8f3X2sU0kMBz1fHn8vFgEJASP+YNx68KbAILhwY5Vw/QnqGFwEAsAaQ + 2ccEH0L6gzcBABAB5PbTEggL/DnL1z3IVow/StBCohasNDhv8cLvA6GmM5d4FQDAxh5hQMXmC7u+yR25 + rgVbaMEORy6zM9PFuwAA1vfrgroMbP6mlZqoypw+tIGWW4KICAD47J2Nec4AkQM5GyjR9tWshT0Z00NM + ADjhRxaJ/gzg61PP5s5ckgZiAgDY418GnPxo09Ykd8MFjmhPB1EBoEkpg8bYNl7h0kBUAOgM9A+8/GP7 + r4u/D8KGn+x6OfTHJcKICgAXkX9ePX5BzPRfCFu1x4+oAICPtk6G/ozRoHX6Gyjg8UMB6BFoyOpSremT + iwdeYnp3xFAAegI8/1Mfn1d/35kP12RXDo+E/vhECApAT0Be/yvvf67+vr4mNpu6BPaH7BaiAsBkIH9o + zWgsoq6Iw1rJu0X/sunxKkpWgrCgYSycjExBDoeoANB89EcfBAAFSev33Myen/q21t8PMTAdpoguogJw + +u1NVHdPdF0AfDwfLEakITN7VA8xAWAIyS9dFQCf4+IAG8voIiYAzCf3y8jOW9no3pkg720TAN/ViMMw + CUkHEQHA6Q/PMb29/ggVBShz5ErWJLC7lA4iAsDkERleP3NW/T1tjlyNKwkPEnm8CwA9/3KEyAQscuTC + 27/12LT3XgRF8Copi1cBYDMJWdDsc+LgVbX3szlyx969XjvU1xT2mJTFmwDw5NchdDUgHH/bT1xUOf0N + 7Cshx+BvZ1fMtjErcUp8ceRF3vmV0HIG2lKAtSsSy56FtCefDYhFhTBTHSHgfMBwaJjgtiSuEH4IcOq1 + zVxnAsybDgznDu6ZEAQMllhoaiIkdP/GsrxVFE/8cEjG30FZDF7zCjJMmxZlWNdPrf5x3sRrrGMzJCVl + Fo0HJ/1ASgSqEnBChCJB3WiAGZSyesvdUsGCkxGHGT5zij0QKQA9BiKA64APk9w1BbfrAoCNP7pvptF3 + kqI/iwIQAXDMITGnqWmO5q2I4LgU4XRZAHylS+P7gBim4HMYvHV4ZJaDIvsPrAH4b7AJXK8FyNuAI7fO + PbirAuDbMYrIAzpax74v5vIA4ODDF5ziPSg2YAbDkYvpQXDmGnCiYbPDAdbUzA0lAGXp5VJRkRRSkRcl + AuFUgDkY84cmzdEYSlKEbcqUdE1C7DkIhZmAUL4LB0aTD5GQxYQoS7alJGslRcVcj2BNBWY5JikC14vt + xy+qvqctzVzTGol1zmVpLUAKdyBSH+1rQNHm0+6PEGuhW2UxEOf7kYVobj7bxtNOSY61KtGpGpCTYslC + NDYgNt2Z3eOLTv9QQ1JibHTjJADs708WolEWbEtLDtUeLcZrgHM/AFoBZCHIM4A/QEIEyjZbqA7JMR6E + zgIQo/qR9kiIQNVa0+xItJDYRt05CwD7/BMbCA3CJ9C2MhF3foT7qtqBh0pGAskKAGjTlAEnBRbKcGoq + ZsM9vL0kyvhqiiBJCKPBmlgDOPWvH13rtBYoAP6oJQB1/QBw1qBSDUUqZYsC1gXEoG5hCukm5jevihLg + d799bmX21fHnah0CKPcdefOW+ueK0QoWEQBsfDhqmqg0HC2oyaYQxAHWAiw/vAxtLT86Af3hVQAQGoIJ + 6EOd2WWY2AgVBoyxJsCbAEi0qILiIgsxtuwr0p6pk+dVW5ODGKdd1xIAW0GEZJPK2Msx+wKcuGiqieaa + w45c9BaAOQ+zXnNzaIcCY7z/A2cBQIjm1I7Nhf9O2ivLHIQwQNjh2YdTz6XdGDYJQnhw5kpbbdpViTGa + /8BZAGxFQVr14THmYXcZONrQVbeJmY3DAp596Q2jZQXEWggEnAUg9JioWE2wrgFTf3z/NS/XOVzfpg9t + ELsaaA0pjfnwcRIAm/mvHY6p6llP2iGR1ivdWEZ6YGrs108nAbDdf7SnxNAhKIdkYY+0CEjNK0xhvVUK + QOh+bAuJtTVTSKRHjQHpzeRbBFJpjlspALbYZ6hsLF4D/KOVWivtScehBP9FG6sU1gpqElIZR14qAGWb + LVRBRtMMQdMr3/zT4KNXfp/RDqdpWHA4nFxDl8PUKUiKBasAVJ20oQSgbj42FgJers+KRZDSoEjtwhrN + eHpekPTLK09gKhADnPZm0jVesZv7RSwSANeZACFSMYGrALQ1B/E+8P7Gfhpo/45lCWXSDFt+WN8pbviF + zAmASd5wzeIKNSfeRQB8nWquDSr6SihHLtvLdYfBP75cOosFjlcdReziFQDebHSm8f1csVYmhnLkxppW + 20cajwfX7stuKFs8ks905chIdJ7hUCLeZNaEmX4MqwXFSAtDljgY4Mg1d3riRmMBCDEjDtjSMjVOs9hM + 1y5acQtBlAI9Jqq6Sg1T9zqbMo0FAJlj2z64pP7ARX0JtZ4ltlFpXReAtqKeWky/CY0FAGw7ekk0e2wh + NtNRcyHHdH/tqgD4zkzEukFEJxbh9kkrAZDKwbZRZIJrJ7KEDGP5posCIJWWjFRkvCdFYD6tBABohQNt + iyZEh9hYykNDdde1WVHSNQkxNvVsS2sB0Iol22oSQuQjxFIiKl1Ka8MmoBqRpZiucD5oLQBA+iSxpSVr + m/+GWJqThJiya7tCaSYlsaL0MV4EAEi1Zyo7bUNlsoEmE2IQrVi95W7+3PjzcFgLd1QsShPH1lqg2s01 + bb+npj8iFgvOB94EAPheTFXmWqhMNlAnJwDOUjxrnasKPNcIX0nnHWiLaNHpG0LIaQU8wqsAANwrIQRt + CkxgYqOXXNXi77oA+KhP1whhaWV12k7eENN+Y8zsbIJ3AQCmnXTdrrJ1M7i6LAA+n026pZZGc9eyzroh + HLmMCDxCRAAMJn/b5HAXLTAzGNTUZNchlBcblE1KljjRpEVA2gy3CWYoRy6IbdJvE0QFoIjhmuy299tQ + 6chlyUCSVklfm2uWNZcJ6ciNcdRXXdQFwDchGpPYUpI1FrN0RpsPH44BgoXvqUzotbNJh4mtuKsJ/wfb + mhgAeoKg9wAAAABJRU5ErkJggigAAAAwAAAAYAAAAAEACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA8 + PDwAOkE+ADpEPwA5RUAAN01DADdORAA4SUEAOExDADVRRAA0VUYANFhHADNaSAA0WUgAMl1JAC9nTQAu + ak4ALWxPADFgSwAwY0wAMGRMAC1uUAAscVEAKnRSACp3VAApeVQAKH1WACeAVwAmg1gAJYVZACSIWgAk + i1wAIo1cACGSXgAhlF8AH5lhAB6cYgAdn2QAIJZgACCYYQAcomQAG6ZmABykZQAbqGcAGqpoABmtaQAX + smsAFrVsABixagAVuW4AFLxvABO/cAAUvnAADs52ABLAcQARx3MAEcd0ABDKdAAO0HcADdJ4AAzWeQAL + 2XoADNh6AAndfAAH5X8ACOJ+AAjkfwAH5oAABumBAATuggAD8oUABPCEAAL1hQAB+IcAAfqIAAD+iQBx + /50Akf+yALH/yQDR/98A////AAAAAAACLwAABFAAAAZwAAAIkAAACrAAAAvPAAAO8AAAIP8SAD3/MQBb + /1EAef9xAJj/kQC1/7EA1P/RAP///wAAAAAAFC8AACJQAAAwcAAAPZAAAEywAABZzwAAZ/AAAHj/EQCK + /zEAnP9RAK7/cQDA/5EA0v+xAOT/0QD///8AAAAAACYvAABAUAAAWnAAAHSQAACOsAAAqc8AAMLwAADR + /xEA2P8xAN7/UQDj/3EA6f+RAO//sQD2/9EA////AAAAAAAvJgAAUEEAAHBbAACQdAAAsI4AAM+pAADw + wwAA/9IRAP/YMQD/3VEA/+RxAP/qkQD/8LEA//bRAP///wAAAAAALxQAAFAiAABwMAAAkD4AALBNAADP + WwAA8GkAAP95EQD/ijEA/51RAP+vcQD/wZEA/9KxAP/l0QD///8AAAAAAC8DAABQBAAAcAYAAJAJAACw + CgAAzwwAAPAOAAD/IBIA/z4xAP9cUQD/enEA/5eRAP+2sQD/1NEA////AAAAAAAvAA4AUAAXAHAAIQCQ + ACsAsAA2AM8AQADwAEkA/xFaAP8xcAD/UYYA/3GcAP+RsgD/scgA/9HfAP///wAAAAAALwAgAFAANgBw + AEwAkABiALAAeADPAI4A8ACkAP8RswD/Mb4A/1HHAP9x0QD/kdwA/7HlAP/R8AD///8AAAAAACwALwBL + AFAAaQBwAIcAkAClALAAxADPAOEA8ADwEf8A8jH/APRR/wD2cf8A95H/APmx/wD70f8A////AAAAAAAb + AC8ALQBQAD8AcABSAJAAYwCwAHYAzwCIAPAAmRH/AKYx/wC0Uf8AwnH/AM+R/wDcsf8A69H/AP///wAA + AAAACAAvAA4AUAAVAHAAGwCQACEAsAAmAM8ALADwAD4R/wBYMf8AcVH/AIxx/wCmkf8Av7H/ANrR/wD/ + //8AAAAAAiYwJgIHSkpKSkkzBz1KSkEMAAAAJkpKSkAHPUpKSko7AAAAAAAAAAAAAAAAAAAAOUpKSj0C + SUpKSkoqAAIUFAIAAAACSUpKSkohHkpKSkodAAAAAAAAAAAAAAAAAgAUSkpKSkoXKUpKSkkMAAAAAAAA + AAAMSkpKSkorAB05ORsAAAAAAAAAAAAAAAAARBQZSkpKSkobAB4zLAwAAAAAAAAAAAAAQ0pKSkoZAAAA + BSQxHgIAAAAAAAAAAAAASkIFRUpKSkkFAAAAAAAAAAAAAAAAAAAAD0FKSSoAAAADQEpKSjMAAAAAAAAA + AAAASkoFFUJKQxcAAAAAAAAAAAAAAAAAAAAAAAIRBRMPAQAeSkpKSkoMAAAAAAAAAAAASkYCAAAHAAAA + AAAAAAAAAAAAAAAAAAAAAAAHOUpKQg0mSkpKSkoOAAAAAAAAAAAASR4AAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAApSkpKSjgRSkpKSkMCAAAAAAAAAAAAEQAAAAAAAAAAAAAAAAAAAAAAAAACKkE9GQA4SkpKSkUB + HERKPhMAAAAAAAAAAAAAOUlBFwAAAAAAAAAAAAAAAAAAAAAvSkpKSRcvSkpKSj0AAAEHAAAAAAAAAAAA + AAAASkpKSREAAAAAAAAAAAAAAAAAAAJFSkpKSjAKQ0pKRxUAAAAAAAAAAAAAAAAAAAAASkpKSiYAAAAA + AAAAAAAAAAAAAAdGSkpKSjAABx4gCQAAAAAAAAAAAAAAAAAAAAAASkpKSh4AAAAAAAAAAAAAAAAAAAAs + SUpKShUAAAAAAAAAAAAAAAAAAAAAAAAAAAAASkpKQwUAAAAAAAAAAAAAAAAAAAACJEE5FwAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAIzcsDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAXMzMXAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABlKSkpKGwAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADlKSkpKPQAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAj1KSkpKQQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAHyNKSkpKKQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAALwIqRUUsAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAEXIQ8A + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAATdKSkokAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAF0pKSkpKDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAASjcFJkpKSkpKFQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIaIREAAAAAAAAA + AAAASko1D0pKSkpJBwAAAAAAAgAAAAAAAAAAAAAAAAAAAAAABj1KSkkeAAAAAAAAAAAASkpKAClKSkke + AgAAAAAAAAAAAAACAAAAAAAAAAACAgAAIUpKSkpFAgAAAAAAAAAASkpDAAAMFQURBQAAAAACAAAAAgAA + AAAAAAAAAjBKSTACL0pKSkpKCQAAAAAAAAAASkohAAAAEUFKSS8CAAAAAAAAAAAAAAAAAAAAKkpKSkoo + HEpKSkpDAAAAAAAAAAAALhcAAAAAPUpKSkoeAAAAAAIAAAAAAh4zLAwAQUpKSko+ATFKSkYVAAAAAAAA + AAAACS09LgkHSkpKSkozAAAAAAAAAAAAL0pKSkYJOkpKSko5AAANFAMAAAAAAAAAAAAAPkpKSkEHRkpK + SkopAAIAAAwXBQIHSUpKSkojGEpKSkkXAAAAAAAAAAAAAAAAAAAASkpKSkoZHkpKSkMFAAAAKUpKSR4M + SkpKSkoqABAtLw8AAAAAAAAAAAAAAAAAAAAASkpKSkoaABQpIQcAAAATSkpKSkkMPUpKSkoUAAAAAAAA + AAAAAAAAAAAAAAAAAAAAQ0pKSkYHAAAAGz5DKwceSkpKSkoXDDlKQx4AAAAAAAAAAAAAAAAAAAAAAAAA + AAAAEThGORMAAAAXSkpKSjAUSkpKSkoMAAICAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAx + SkpKSkkCMEpKSSoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwSkpKSkUCABUhDgAC + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPSkpKSisCAAAAAAAAAQAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFTg9JgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAgAAAgABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQABAAEAAQABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAApEIAAAAAAACkQgAAAAAAAKRCAAAAAAAApEIA + AAAAAACkQgAAAAAAAKRCAAAAAAAApEIAAAAAAACkQgAAAAAAAKRCAAAAAAAApEIAAAAAAACkQgAAAAAA + AKRCAAAAAAAApEIAAAAAAACkQgAAAAAAAKRCAAAAAAAApEIAAAAAAACkQgAAAAAAAKRCAAAAAAAApEIA + AAAAAACkQgAAAAAAAKRCAAAAAAAApEIAAAAAAACkQgAAAAAAAKRCAAAAAAAApEIAAAAAAACkQgAAAAAA + AKRCAAAAAAAApEIAAAAAAACkQgAAAAAAAKRCAAAAAAAApEIAAAAAAACkQgAAAAAAAKRCAAAAAAAApEIA + AAAAAACkQgAAAAAAAKRCAAAAAAAApEIAAAAAAACkQgAAAAAAAKRCAAAAAAAApEIAAAAAAACkQgAAAAAA + AKRCAAAAAAAApEIAAAAAAACkQgAAAAAAAKRCAAAAAAAApEIAAAAAAACkQgAAAAAAAKRCKAAAACAAAABA + AAAAAQAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADw9PQA6QT4AOkQ/ADlGQAA3TUMAN05EADhJQQA4 + TEMANVFFADRVRgAzWkgANFhIADJdSQAvZk0ALmlOADFhSgAwY0wAMGRMAC1tUAArc1IALHJRACp1UgAq + d1QAKXlUACh9VgAngFcAJoJYACWGWgAliVsAJItcACOOXAAkjFwAIZJeACGVXwAfmWEAHpxiAB2fZAAg + lmAAIJhhAByhZAAbp2cAHKVmABuoZwAaqWgAF7JrABezbAAXtWwAGLBqABa4bQAUvXAADs52ABLBcQAR + xXMAEch0AA7QdwAN0ngADNV5AAvaegAK3HwACeB9AAjlfwAH5oAABumBAAPyhQAE8YQAA/SFAAH4hwAB + +ogAAP6JAACwNgAAz0AAAPBKABH/WwAx/3EAUf+HAHH/nQCR/7IAsf/JANH/3wD///8AAAAAAAIvAAAE + UAAABnAAAAiQAAAKsAAAC88AAA7wAAAg/xIAPf8xAFv/UQB5/3EAmP+RALX/sQDU/9EA////AAAAAAAU + LwAAIlAAADBwAAA9kAAATLAAAFnPAABn8AAAeP8RAIr/MQCc/1EArv9xAMD/kQDS/7EA5P/RAP///wAA + AAAAJi8AAEBQAABacAAAdJAAAI6wAACpzwAAwvAAANH/EQDY/zEA3v9RAOP/cQDp/5EA7/+xAPb/0QD/ + //8AAAAAAC8mAABQQQAAcFsAAJB0AACwjgAAz6kAAPDDAAD/0hEA/9gxAP/dUQD/5HEA/+qRAP/wsQD/ + 9tEA////AAAAAAAvFAAAUCIAAHAwAACQPgAAsE0AAM9bAADwaQAA/3kRAP+KMQD/nVEA/69xAP/BkQD/ + 0rEA/+XRAP///wAAAAAALwMAAFAEAABwBgAAkAkAALAKAADPDAAA8A4AAP8gEgD/PjEA/1xRAP96cQD/ + l5EA/7axAP/U0QD///8AAAAAAC8ADgBQABcAcAAhAJAAKwCwADYAzwBAAPAASQD/EVoA/zFwAP9RhgD/ + cZwA/5GyAP+xyAD/0d8A////AAAAAAAvACAAUAA2AHAATACQAGIAsAB4AM8AjgDwAKQA/xGzAP8xvgD/ + UccA/3HRAP+R3AD/seUA/9HwAP///wAAAAAALAAvAEsAUABpAHAAhwCQAKUAsADEAM8A4QDwAPAR/wDy + Mf8A9FH/APZx/wD3kf8A+bH/APvR/wD///8AAAAAABsALwAtAFAAPwBwAFIAkABjALAAdgDPAIgA8ACZ + Ef8ApjH/ALRR/wDCcf8Az5H/ANyx/wDr0f8A////AAAAAAAIAC8ADgBQABUAcAAbAJAAIQCwACYAzwAs + APAAPhH/AFgx/wBxUf8AjHH/AKaR/wC/sf8A2tH/AP///wAAABg2KgdEQ0M2DzY4EgAANkRDHDpEQzkA + AAAAAAAAAAEIREREITZDQyYAAAAAAAdDREQ1ETg4EQAAAAAAAAAAOxJEREQpBx8WAAAAAAAAADpERCEA + AB81KQAAAAAAAABEGy1EOwUAAAAAAAAAAAAABx8YDAARQ0REGQAAAAAAAEQNAAIAAAAAAAAAAAAAAAAA + Cz5DORZDQ0MfAAAAAAAAGAAAAAAAAAAAAAAAAAAfKgsmQ0NDFjFDOAcAAAAAAAA+QBsAAAAAAAAAAAAA + JkRDQBlDQ0MLAAIAAAAAAAAAAEREPwAAAAAAAAAAAAAwQ0NDBRwuFAAAAAAAAAAAAAAAREQ+AAAAAAAA + AAAAABRDQzEAAAAAAAAAAAAAAAAAAAA0Ng4AAAAAAAAAAAAAAAcPAAAAAAAAAAAAAAAAAAAAAAAcOC4C + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACURERCYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAS + REREKQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABsrQzkFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAADQAAIS0RAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABACFEREEDAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAEMcLURERAsAAAAAAAAAAAAAAAAAAAACJi4LAAAAAAAAREENQUQ0AAAAAAAAAAAAAAAAAAIA + ACpERDwAAAAAAABEPAAHER8YAAAAAAAAAAAAAAAYQUEXNURERAIAAAAAADURAAA2REQjAAAAAAAABx8W + ADxERDsUQ0QvAAAAAAAAHjsxB0RERDYAAAAAAAA6REQhOERENgAHCwAAAAAAAABEREQjNUREHgAAJjsw + CERERDULMzELAAAAAAAAAAAAAERERCQCFhYUAw9EREQhNkRDGwAAAAAAAAAAAAAAAAAAJEA1BwAIQEQ+ + FERERCYCFxEAAAAAAAAAAAAAAAAAAAAAAAAAACFEREQZKUA1AwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + DUREQwsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCcNAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAoAAAAGAAAADAAAAAB + AAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPDw8ADpBPgA6RD8AOkRAADdPRAA4SkEAOExDADZRRAA1 + VUYAM1pIADJeSQAxYEsAMGRMAC1tUAArc1IALHFRACp1UgAqd1QAKXlUACh9VgAngFcAJoJYACWFWQAk + iVsAJItcACONXAAkjFwAIpFeACGUXwAfmmIAHp5jACCWYAAgmGEAHaFkABumZgAcpGUAGqpoABitaQAV + uW4AFL5wAA/NdgASwXEAEcVzABDJdAAO0HcADdN4AAzVeQAL2HoACdx8AAjhfQAI5H8AB+eAAAbqgQAE + 7oMABPCEAAH4hwAB+ogAAP6JAFH/yABx/9MAkf/cALH/5QDR//AA////AAAAAAAALw4AAFAYAABwIgAA + kCwAALA2AADPQAAA8EoAEf9bADH/cQBR/4cAcf+dAJH/sgCx/8kA0f/fAP///wAAAAAAAi8AAARQAAAG + cAAACJAAAAqwAAALzwAADvAAACD/EgA9/zEAW/9RAHn/cQCY/5EAtf+xANT/0QD///8AAAAAABQvAAAi + UAAAMHAAAD2QAABMsAAAWc8AAGfwAAB4/xEAiv8xAJz/UQCu/3EAwP+RANL/sQDk/9EA////AAAAAAAm + LwAAQFAAAFpwAAB0kAAAjrAAAKnPAADC8AAA0f8RANj/MQDe/1EA4/9xAOn/kQDv/7EA9v/RAP///wAA + AAAALyYAAFBBAABwWwAAkHQAALCOAADPqQAA8MMAAP/SEQD/2DEA/91RAP/kcQD/6pEA//CxAP/20QD/ + //8AAAAAAC8UAABQIgAAcDAAAJA+AACwTQAAz1sAAPBpAAD/eREA/4oxAP+dUQD/r3EA/8GRAP/SsQD/ + 5dEA////AAAAAAAvAwAAUAQAAHAGAACQCQAAsAoAAM8MAADwDgAA/yASAP8+MQD/XFEA/3pxAP+XkQD/ + trEA/9TRAP///wAAAAAALwAOAFAAFwBwACEAkAArALAANgDPAEAA8ABJAP8RWgD/MXAA/1GGAP9xnAD/ + kbIA/7HIAP/R3wD///8AAAAAAC8AIABQADYAcABMAJAAYgCwAHgAzwCOAPAApAD/EbMA/zG+AP9RxwD/ + cdEA/5HcAP+x5QD/0fAA////AAAAAAAsAC8ASwBQAGkAcACHAJAApQCwAMQAzwDhAPAA8BH/APIx/wD0 + Uf8A9nH/APeR/wD5sf8A+9H/AP///wAAAAAAGwAvAC0AUAA/AHAAUgCQAGMAsAB2AM8AiADwAJkR/wCm + Mf8AtFH/AMJx/wDPkf8A3LH/AOvR/wD///8AAAAAAAgALwAOAFAAFQBwABsAkAAhALAAJgDPACwA8AA+ + Ef8AWDH/AHFR/wCMcf8AppH/AL+x/wDa0f8A////AAAMLSQhOTkTISMDADI5JC45LQAAAAAAABEmOTkR + LCcDAAAAAzg5KAYYGAQAAAAAADgUOC0DAAAAAwAAABEkDQMkOTQDAwAAADAAAwAAAwAAAAAAAAAkOScn + OTgGAAAAAB0RAAAAAAAAAAAkNhoyOTYEHg8AAAAAADk5CQAAAAAAAwM4OS8PJxQAAAAAAAMAADk4CAAD + AAAAAAAjMxgDAAADAAAAAAAAABEZDQAAAAAAAAAAAAAAAAAAAAAAAwAAAA85OREAAAADAAAAAAMAAAAA + AAAAAAAAABs5ORQAAAEAAAAAAwAAAAAAAAMAAAAAAA8WIAsAAAAAAAAAAAAAAAMAAAAAAwAAAAEGNjka + AAAAAAAAAAADAAAAAAAAAAAAADYWOTklAAAAAAAAAAAAAAADIycEAAAAADkgGiUKAAAAAAAAAAABGhoO + OTkhAAAAACgHACo5HgAAAAAADwsUOTkbNjgRAwAAACYxDjg5LwAABwMaOTgbOTkPAwYAAAAAADk5Jxoo + DwAbOTEhOTkMDAwAAAAAAAAAACo1EQAZNiQnOTkJHBMBAAMAAAMAAAMAAAAAAAAwOTgLJxwAAAAAAAAA + AAAAAAAAAAAAAAAWNCEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAQABAAEAAQAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACgAAAAQAAAAIAAAAAEACAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA8PT0AOkE+ADlGQAA3TUMAOElBADhMQwA1U0UANVVGADNbSQAy + XUkALmtPAC5sTwAxYUsAMGJMAC1vUAArc1IAK3RTACh8VgAngFcAJ4FYACaEWQAkiVsAH5piACGVYAAg + mGEAHKJlABunZwAaqWgAGa1pABa1bAAYsGoAFbtvABS8bwAPzXYAEsJyABHEcgAQynUADtF4AAzVeQAL + 2nsACt18AAjifgAI5X8ABuuCAATvgwAD84UABPCEAAL2hgAB+YgAAP6JAABQNwAAcEwAAJBjAACweQAA + z48AAPCmABH/tAAx/74AUf/IAHH/0wCR/9wAsf/lANH/8AD///8AAAAAAAAvDgAAUBgAAHAiAACQLAAA + sDYAAM9AAADwSgAR/1sAMf9xAFH/hwBx/50Akf+yALH/yQDR/98A////AAAAAAACLwAABFAAAAZwAAAI + kAAACrAAAAvPAAAO8AAAIP8SAD3/MQBb/1EAef9xAJj/kQC1/7EA1P/RAP///wAAAAAAFC8AACJQAAAw + cAAAPZAAAEywAABZzwAAZ/AAAHj/EQCK/zEAnP9RAK7/cQDA/5EA0v+xAOT/0QD///8AAAAAACYvAABA + UAAAWnAAAHSQAACOsAAAqc8AAMLwAADR/xEA2P8xAN7/UQDj/3EA6f+RAO//sQD2/9EA////AAAAAAAv + JgAAUEEAAHBbAACQdAAAsI4AAM+pAADwwwAA/9IRAP/YMQD/3VEA/+RxAP/qkQD/8LEA//bRAP///wAA + AAAALxQAAFAiAABwMAAAkD4AALBNAADPWwAA8GkAAP95EQD/ijEA/51RAP+vcQD/wZEA/9KxAP/l0QD/ + //8AAAAAAC8DAABQBAAAcAYAAJAJAACwCgAAzwwAAPAOAAD/IBIA/z4xAP9cUQD/enEA/5eRAP+2sQD/ + 1NEA////AAAAAAAvAA4AUAAXAHAAIQCQACsAsAA2AM8AQADwAEkA/xFaAP8xcAD/UYYA/3GcAP+RsgD/ + scgA/9HfAP///wAAAAAALwAgAFAANgBwAEwAkABiALAAeADPAI4A8ACkAP8RswD/Mb4A/1HHAP9x0QD/ + kdwA/7HlAP/R8AD///8AAAAAACwALwBLAFAAaQBwAIcAkAClALAAxADPAOEA8ADwEf8A8jH/APRR/wD2 + cf8A95H/APmx/wD70f8A////AAAAAAAbAC8ALQBQAD8AcABSAJAAYwCwAHYAzwCIAPAAmRH/AKYx/wC0 + Uf8AwnH/AM+R/wDcsf8A69H/AP///wAAAAAACAAvAA4AUAAVAHAAGwCQACEAsAAmAM8ALADwAD4R/wBY + Mf8AcVH/AIxx/wCmkf8Av7H/ANrR/wD///8AAiUZLScLDgAtJSQiAAAAAB0rHQcFAAAAHBgFJhgAAAAV + AAAAAAAACwwwHiscAAAALxEAAAAAEDEcJRMAAAAAACoQAAAAAAUbCAAAAAAAAAAUKQcAAAAAAAAAAAAA + AAAAGi0IAAAAAAAAAAAAAAAAAAQWIgAAAAAAAAAAAAAAAAAoIi4CAAAAAAAAABkfAAAAIwAeFwAAAAcF + JiUhKwEAACcaLiYAEQwvJh8fAAEAAAApHgYdEjEkGRUAAAAAAAAAAAAJMR0UDAAAAAAAAAAAAAAAAA0C + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA= + + + + + iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAMAAAC67D+PAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8 + YQUAAAAgY0hSTQAAeiYAAICEAAD6AAAAgOgAAHUwAADqYAAAOpgAABdwnLpRPAAAAwBQTFRFgICA//// + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAODgHVgAAAAlwSFlzAAAOvgAA + Dr4B6kKxwAAAABZJREFUGFdjYAABRhAAs4hlkq4DZDgACywAM12jTsYAAAAASUVORK5CYII= + + + + + iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8 + YQUAAAAgY0hSTQAAeiYAAICEAAD6AAAAgOgAAHUwAADqYAAAOpgAABdwnLpRPAAAAnBJREFUOE+dk11I + k1Ecxs+2q1DLqwRvvCgoM6mLvoTAC6WLSrDUYBcSGK6y6EMzc6a2NnERlVKhSEMTYWSyksTZh7KZGboU + HNmUKemcupnuI5tuqHs6/7cSUenrwMPhPf/n97wPB46IrVrHCwuTxCJR5EbxbHiUZHQnEzE2uhj18Wsw + zPPLGgQmdErli9Ws8C2VX8wFX9y0rmiWnJ9/dg38Qc02dZdKUlQ3DrcuBINIfQTItMDJWiBHByj1gMEK + 0OxY9rkrywEvb7OQdzclR6tKDjRUV522qh7Kl5q6unDqQTnuNbZD89qEyhYTNK9M0PcMwLewgOsFh5oH + 70oSbXfYBmZUiM8P1Se06Z4WBP5UvarFALffj+q6goDjTXJTf7k4nWVmp159ayhDnVYu1Ot7tvmnImB+ + ztX4Y6dZUYMRzrk5VD4uxPueWmTlpVxmCVlZF1wuG8pqVJj0eKA+s5cHRMNm2Iapvn3wjCRirGOHUF2j + 12PY7Ubx/SJ4vJMglsXLZJcWefrI+Ge09PZCGr8V105sQU3xdgx0HYHfJ4O5ebdQXVNXjLb2Csy4x0EM + sexgRka2f2kJvkAAEzz9VmkCatWR0JaEoqkiDJ26cDxRh2LQ6YSyQgGna0zwEkMs25+envON13P7fII+ + 2e3QGo1rVN/RAZPFvOwjhli2RyrNdfNEh9eL0elpdFutsPMmLl55peiMZuQhLzHEsl1paXlf5udhdTjQ + abEIu21mZl2t9BBDLItOSpKP8HSj2Yx+Xn9oauq3Ig95iSGWRcTFKVr57Q/zv9pnZ/9K5CWGWBYaG5sZ + EhNT+j8idt0X+S+H3wE2DYYIXysH6QAAAABJRU5ErkJggg== + + + + + iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8 + YQUAAAAgY0hSTQAAeiYAAICEAAD6AAAAgOgAAHUwAADqYAAAOpgAABdwnLpRPAAAAm1JREFUOE+Nkl9I + U1Ecx39T31o9SBq97cWHiUIimKiQ0zFbbcJ1U2YkBtLuFYkQnMrcdKQyEUIwWk+GDy58EfUhmYoTRtKE + HitI8kGZIkEW/oF0um/nd3OyYUnn8rn3nMPn+733wNXYe3spOTQajVXMb55vpE/CiUTiqyB91+b1Ugry + j3gcWwcH2Nzfx8benspsJALhyII8qaeHUiHJ7U5F+Xl0hM3dXXzZ2cGn7W183NpCcG4OPISrmNvbdQZF + IaZOlolsNhvVOZ1U29XFtO4fH+ObeGtqyYuJCSTJM5s9Aqqqr1ez6s1ut5OtqYksHR1tB6Lg++HhhRL+ + Ej4OO+yqmbOCDLGwCuSsrKznLpcLl8EOu5wRBRkkSdJ1t9vdtyPOrCgK+vv74fV6L+DxeODz+VQnFouh + u7u7j7NksVj0o6Oj42tra3A4HOjs7ITT6URzczMkqQ7V1UaUl1egpOQ2zOZ7qjM/v4yBgcFxzlJNTU3l + 1NTU8urqKoxGowjLMJnMqKioFME7aRiNd1VndnYRIyOBZc6SwWBwRKPR9XA4jKKiIjQ0PBSS9a+YTLWq + 4xTX5OTbdc5SWVnZk1AohGAwCJ1OB7v9EazWB/+EnbGxMUxPT4OzVFxc7IpE3mFmJoS2tqcYHg5gaOgl + /P5ACq/E/A+tre1YXPygwlnS6/XupaUVLCysoLGx8b9IFnCWcnJyWrKzsweZzMzMIf5l7weA1++BN9HP + MPhacEv2o8o1iV8nJ2An6XOWxIK0Wi1dy82lG6Wlz9SfPmWcJhJg4qeniIsnO+xyhrPnBVcLC0lbUPD4 + Sn6+/zLYUd2zgt/AGvcWHCMAZwAAAABJRU5ErkJggg== + + + + + iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8 + YQUAAAAgY0hSTQAAeiYAAICEAAD6AAAAgOgAAHUwAADqYAAAOpgAABdwnLpRPAAAAW1JREFUOE+NkL1L + QlEYh9/b4NzS1BgNShBRQQ3VGEGr/0BDBEG0uLRIFIREIX2ANhgZKphj/4PLASOi0i4SYWWmWH5y/bhv + 5yc4HTl04YHD+z4893AMGvB53S7Hg+1cNQxjBGtm/p4YerrdvXlsDfJ7s7MlCp4ukgD7U3QX8mx+ZDIm + A5wx6+/hKiEs0+drnNiY5WTynlOpZ85mcz1wxgw7OHCVwPECCXlVDoev2ec75EDggiORGMfjCQ5dXrHf + f8LRaAwKw1UCR/MkbLns2Da/mOZAsIMDVwn45ki0pWB1OlrgwFUCBzMkrG6X662WFjhwlcDeNIlGu82/ + zaYWOHCVgHeSRFX+vVSraYEDVwnsuEj8WBbnKxUtcOAqAY+TREleP1cua4EDVwlsj5MoNBr8WixqgQNX + CWyNkfis19ksFLTAgasE1kdJvMsHTOfzWuDAVQLuYRJf8oHeqlUtcOAqgRUHBZcdJP4D3H7gDzdsNup2 + mXizAAAAAElFTkSuQmCC + + + + + iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8 + YQUAAAAgY0hSTQAAeiYAAICEAAD6AAAAgOgAAHUwAADqYAAAOpgAABdwnLpRPAAAAXJJREFUOE+lk0FL + AkEYhlvwv3jzoiDoQdCbdEnYf6CrqCgoHgRRAk/9EQVLdEGyFiQNMS+dvHnoEkgglGAmCL7NO6RMIZvU + wsMO3zzzzGk0ACf/+hjQNO1ccKlXKsYx0OUZeflXoFmtVsUS2P4CHboi0FQDrXK5jM12i/VmYwsduiLQ + UgNmqVTCuzj8tlrZQoeuCJhqoFMsFvG6XmO2WNhCh64IdNRAt1Ao4EXc/jSf20KHrgh01YCVy+Uwnkzw + vFzaQoeuCFhqoJfJZBCLxY6Crgj01EA/lUrB4/HA7XYfhHs78vk8A301MIzH4/B6vRiNHjAY3H+DM+7p + ug6fz4dsNsvAUA2Mo9Eo/H4/LOsOTqdTYprXEs64x0AwGEQ6nWZgrAYeDcNAIBBAu30r/6Reb0t2MwbC + 4TCSySQDj/uAeEyngqnL5fpoNG4QCoUktVpHspsxEIlEkEgk+AKnaoAP8kwwczgcF4fg3g+u9gEu/son + bfJW/NwRDyIAAAAASUVORK5CYII= + + + + + iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8 + YQUAAAAgY0hSTQAAeiYAAICEAAD6AAAAgOgAAHUwAADqYAAAOpgAABdwnLpRPAAAAUNJREFUOE+lk79L + QlEcxW9/gqCrm6vg4uYoOAgOrqLk4ioP0r2Glhp0SSjoF1FE0BIUDU3RdIOGoKBVGlpapaHTObeuCPe6 + 9ITD5fs9n3Pue8JbAWBS/VSQRvPwKR/j3JgaZXVqPv5TzPOXLhYoZDEcQidVWyhw3qzfn3tBAWH7PRjg + uV7HV5JAM6USyX50u86btlrOCwoOCR7Q+Oz1cFcu473dhmbppdFwu8dq1e3EBgU0zB6NXQJvzSaui0U8 + VCq4LZWwn8vhLJ+HPDFiowUEzITADsGrQgFHmYzTSTYL7eSJiRZs0timRoTGhC956wXDXtrJEyM2eAIt + t34Be8NgTPLELCuQYe8Z9tK8ZBf+ieuEnxj20rzB26SYF7zCGsGEoVeW6NTMoJFiXlDAkFllqMOwTs2+ + IOYFBf/9oFJ9ibr0B4f94vVG3bWDAAAAAElFTkSuQmCC + + + \ No newline at end of file diff --git a/GreenshotImageEditor/Core/ImageHelper.cs b/GreenshotImageEditor/Core/ImageHelper.cs new file mode 100644 index 000000000..65108a856 --- /dev/null +++ b/GreenshotImageEditor/Core/ImageHelper.cs @@ -0,0 +1,1525 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2013 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: http://getgreenshot.org/ + * The Greenshot project is hosted on Sourceforge: http://sourceforge.net/projects/greenshot/ + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using Greenshot.Core; +using GreenshotPlugin.UnmanagedHelpers; +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Drawing.Drawing2D; +using System.Drawing.Imaging; +using System.IO; + +namespace GreenshotPlugin.Core +{ + /// + /// Description of ImageHelper. + /// + public static class ImageHelper + { + /// + /// Create a thumbnail from an image + /// + /// + /// + /// + /// + public static Image CreateThumbnail(Image image, int thumbWidth, int thumbHeight) + { + return CreateThumbnail(image, thumbWidth, thumbHeight, -1, -1); + } + + /// + /// Create a Thumbnail + /// + /// + /// + /// + /// + /// + /// + public static Image CreateThumbnail(Image image, int thumbWidth, int thumbHeight, int maxWidth, int maxHeight) + { + int srcWidth = image.Width; + int srcHeight = image.Height; + if (thumbHeight < 0) + { + thumbHeight = (int)(thumbWidth * ((float)srcHeight / (float)srcWidth)); + } + if (thumbWidth < 0) + { + thumbWidth = (int)(thumbHeight * ((float)srcWidth / (float)srcHeight)); + } + if (maxWidth > 0 && thumbWidth > maxWidth) + { + thumbWidth = Math.Min(thumbWidth, maxWidth); + thumbHeight = (int)(thumbWidth * ((float)srcHeight / (float)srcWidth)); + } + if (maxHeight > 0 && thumbHeight > maxHeight) + { + thumbHeight = Math.Min(thumbHeight, maxHeight); + thumbWidth = (int)(thumbHeight * ((float)srcWidth / (float)srcHeight)); + } + + Bitmap bmp = new Bitmap(thumbWidth, thumbHeight); + using (Graphics graphics = Graphics.FromImage(bmp)) + { + graphics.SmoothingMode = SmoothingMode.HighQuality; + graphics.PixelOffsetMode = PixelOffsetMode.HighQuality; + graphics.CompositingQuality = CompositingQuality.HighQuality; + graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; + Rectangle rectDestination = new Rectangle(0, 0, thumbWidth, thumbHeight); + graphics.DrawImage(image, rectDestination, 0, 0, srcWidth, srcHeight, GraphicsUnit.Pixel); + } + return bmp; + } + + /// + /// Crops the image to the specified rectangle + /// + /// Image to crop + /// Rectangle with bitmap coordinates, will be "intersected" to the bitmap + public static bool Crop(ref Image image, ref Rectangle cropRectangle) + { + Image returnImage = null; + if (image != null && image is Bitmap && ((image.Width * image.Height) > 0)) + { + cropRectangle.Intersect(new Rectangle(0, 0, image.Width, image.Height)); + if (cropRectangle.Width != 0 || cropRectangle.Height != 0) + { + returnImage = CloneArea(image, cropRectangle, PixelFormat.DontCare); + image.Dispose(); + image = returnImage; + return true; + } + } + LOG.Warn("Can't crop a null/zero size image!"); + return false; + } + + /// + /// Private helper method for the FindAutoCropRectangle + /// + /// + /// + /// Rectangle + private static Rectangle FindAutoCropRectangle(IFastBitmap fastBitmap, Point colorPoint, int cropDifference) + { + Rectangle cropRectangle = Rectangle.Empty; + Color referenceColor = fastBitmap.GetColorAt(colorPoint.X, colorPoint.Y); + Point min = new Point(int.MaxValue, int.MaxValue); + Point max = new Point(int.MinValue, int.MinValue); + + if (cropDifference > 0) + { + for (int y = 0; y < fastBitmap.Height; y++) + { + for (int x = 0; x < fastBitmap.Width; x++) + { + Color currentColor = fastBitmap.GetColorAt(x, y); + int diffR = Math.Abs(currentColor.R - referenceColor.R); + int diffG = Math.Abs(currentColor.G - referenceColor.G); + int diffB = Math.Abs(currentColor.B - referenceColor.B); + if (((diffR + diffG + diffB) / 3) > cropDifference) + { + if (x < min.X) min.X = x; + if (y < min.Y) min.Y = y; + if (x > max.X) max.X = x; + if (y > max.Y) max.Y = y; + } + } + } + } + else + { + for (int y = 0; y < fastBitmap.Height; y++) + { + for (int x = 0; x < fastBitmap.Width; x++) + { + Color currentColor = fastBitmap.GetColorAt(x, y); + if (referenceColor.Equals(currentColor)) + { + if (x < min.X) min.X = x; + if (y < min.Y) min.Y = y; + if (x > max.X) max.X = x; + if (y > max.Y) max.Y = y; + } + } + } + } + + if (!(Point.Empty.Equals(min) && max.Equals(new Point(fastBitmap.Width - 1, fastBitmap.Height - 1)))) + { + if (!(min.X == int.MaxValue || min.Y == int.MaxValue || max.X == int.MinValue || min.X == int.MinValue)) + { + cropRectangle = new Rectangle(min.X, min.Y, max.X - min.X + 1, max.Y - min.Y + 1); + } + } + return cropRectangle; + } + + /// + /// Get a rectangle for the image which crops the image of all colors equal to that on 0,0 + /// + /// + /// Rectangle + public static Rectangle FindAutoCropRectangle(Image image, int cropDifference) + { + Rectangle cropRectangle = Rectangle.Empty; + Rectangle currentRectangle = Rectangle.Empty; + List checkPoints = new List(); + // Top Left + checkPoints.Add(new Point(0, 0)); + // Bottom Left + checkPoints.Add(new Point(0, image.Height - 1)); + // Top Right + checkPoints.Add(new Point(image.Width - 1, 0)); + // Bottom Right + checkPoints.Add(new Point(image.Width - 1, image.Height - 1)); + using (IFastBitmap fastBitmap = FastBitmap.Create((Bitmap)image)) + { + // find biggest area + foreach (Point checkPoint in checkPoints) + { + currentRectangle = FindAutoCropRectangle(fastBitmap, checkPoint, cropDifference); + if (currentRectangle.Width * currentRectangle.Height > cropRectangle.Width * cropRectangle.Height) + { + cropRectangle = currentRectangle; + } + } + } + return cropRectangle; + } + + /// + /// Load an image from file + /// + /// + /// + public static Image LoadImage(string filename) + { + if (string.IsNullOrEmpty(filename)) + { + return null; + } + if (!File.Exists(filename)) + { + return null; + } + Image fileImage = null; + LOG.InfoFormat("Loading image from file {0}", filename); + // Fixed lock problem Bug #3431881 + using (Stream imageFileStream = File.OpenRead(filename)) + { + // And fixed problem that the bitmap stream is disposed... by Cloning the image + // This also ensures the bitmap is correctly created + + if (filename.EndsWith(".ico")) + { + // Icon logic, try to get the Vista icon, else the biggest possible + try + { + using (Image tmpImage = ExtractVistaIcon(imageFileStream)) + { + if (tmpImage != null) + { + fileImage = Clone(tmpImage); + } + } + } + catch (Exception vistaIconException) + { + LOG.Warn("Can't read icon from " + filename, vistaIconException); + } + if (fileImage == null) + { + try + { + // No vista icon, try normal icon + imageFileStream.Position = 0; + // We create a copy of the bitmap, so everything else can be disposed + using (Icon tmpIcon = new Icon(imageFileStream, new Size(1024, 1024))) + { + using (Image tmpImage = tmpIcon.ToBitmap()) + { + fileImage = Clone(tmpImage); + } + } + } + catch (Exception iconException) + { + LOG.Warn("Can't read icon from " + filename, iconException); + } + } + } + if (fileImage == null) + { + // We create a copy of the bitmap, so everything else can be disposed + imageFileStream.Position = 0; + using (Image tmpImage = Image.FromStream(imageFileStream, true, true)) + { + LOG.DebugFormat("Loaded {0} with Size {1}x{2} and PixelFormat {3}", filename, tmpImage.Width, tmpImage.Height, tmpImage.PixelFormat); + fileImage = Clone(tmpImage); + } + } + } + if (fileImage != null) + { + LOG.InfoFormat("Information about file {0}: {1}x{2}-{3} Resolution {4}x{5}", filename, fileImage.Width, fileImage.Height, fileImage.PixelFormat, fileImage.HorizontalResolution, fileImage.VerticalResolution); + } + return fileImage; + } + + /// + /// Based on: http://www.codeproject.com/KB/cs/IconExtractor.aspx + /// And a hint from: http://www.codeproject.com/KB/cs/IconLib.aspx + /// + /// Stream with the icon information + /// Bitmap with the Vista Icon (256x256) + private static Bitmap ExtractVistaIcon(Stream iconStream) + { + const int SizeICONDIR = 6; + const int SizeICONDIRENTRY = 16; + Bitmap bmpPngExtracted = null; + try + { + byte[] srcBuf = new byte[iconStream.Length]; + iconStream.Read(srcBuf, 0, (int)iconStream.Length); + int iCount = BitConverter.ToInt16(srcBuf, 4); + for (int iIndex = 0; iIndex < iCount; iIndex++) + { + int iWidth = srcBuf[SizeICONDIR + SizeICONDIRENTRY * iIndex]; + int iHeight = srcBuf[SizeICONDIR + SizeICONDIRENTRY * iIndex + 1]; + int iBitCount = BitConverter.ToInt16(srcBuf, SizeICONDIR + SizeICONDIRENTRY * iIndex + 6); + if (iWidth == 0 && iHeight == 0) + { + int iImageSize = BitConverter.ToInt32(srcBuf, SizeICONDIR + SizeICONDIRENTRY * iIndex + 8); + int iImageOffset = BitConverter.ToInt32(srcBuf, SizeICONDIR + SizeICONDIRENTRY * iIndex + 12); + using (MemoryStream destStream = new MemoryStream()) + { + destStream.Write(srcBuf, iImageOffset, iImageSize); + destStream.Seek(0, SeekOrigin.Begin); + bmpPngExtracted = new Bitmap(destStream); // This is PNG! :) + } + break; + } + } + } + catch + { + return null; + } + return bmpPngExtracted; + } + + /// + /// See: http://msdn.microsoft.com/en-us/library/windows/desktop/ms648069%28v=vs.85%29.aspx + /// + /// The icon to + /// The file (EXE or DLL) to get the icon from + /// Index of the icon + /// true if the large icon is wanted + /// Icon + public static Icon ExtractAssociatedIcon(string location, int index, bool takeLarge) + { + IntPtr large; + IntPtr small; + Shell32.ExtractIconEx(location, index, out large, out small, 1); + Icon returnIcon = null; + bool isLarge = false; + bool isSmall = false; + try + { + if (takeLarge && !IntPtr.Zero.Equals(large)) + { + returnIcon = Icon.FromHandle(large); + isLarge = true; + } + else if (!IntPtr.Zero.Equals(small)) + { + returnIcon = Icon.FromHandle(small); + isSmall = true; + } + else if (!IntPtr.Zero.Equals(large)) + { + returnIcon = Icon.FromHandle(large); + isLarge = true; + } + } + finally + { + if (isLarge && !IntPtr.Zero.Equals(small)) + { + User32.DestroyIcon(small); + } + if (isSmall && !IntPtr.Zero.Equals(large)) + { + User32.DestroyIcon(large); + } + } + return returnIcon; + } + + /// + /// Get the number of icon in the file + /// + /// + /// Location of the EXE or DLL + /// + public static int CountAssociatedIcons(string location) + { + IntPtr large = IntPtr.Zero; + IntPtr small = IntPtr.Zero; + return Shell32.ExtractIconEx(location, -1, out large, out small, 0); + } + + /// + /// Apply the effect to the bitmap + /// + /// Bitmap + /// IEffect + /// Bitmap + public static Image ApplyEffect(Image sourceImage, IEffect effect, out Point offset) + { + List effects = new List(); + effects.Add(effect); + return ApplyEffects(sourceImage, effects, out offset); + } + + /// + /// Apply the effects in the supplied order to the bitmap + /// + /// Bitmap + /// List + /// Bitmap + public static Image ApplyEffects(Image sourceImage, List effects, out Point offset) + { + Image currentImage = sourceImage; + bool disposeImage = false; + // Default out value for the offset, will be modified there where needed + offset = new Point(0, 0); + Point tmpPoint; + foreach (IEffect effect in effects) + { + Image tmpImage = effect.Apply(currentImage, out tmpPoint); + if (tmpImage != null) + { + offset.Offset(tmpPoint); + if (disposeImage) + { + currentImage.Dispose(); + } + currentImage = tmpImage; + tmpImage = null; + // Make sure the "new" image is disposed + disposeImage = true; + } + } + return currentImage; + } + + /// + /// Make the picture look like it's torn + /// + /// Bitmap to make torn edge off + /// How large (height) is each tooth + /// How wide is a horizontal tooth + /// How wide is a vertical tooth + /// Changed bitmap + public static Image CreateTornEdge(Image sourceImage, int toothHeight, int horizontalToothRange, int verticalToothRange) + { + Image returnImage = CreateEmpty(sourceImage.Width, sourceImage.Height, PixelFormat.Format32bppArgb, Color.Empty, sourceImage.HorizontalResolution, sourceImage.VerticalResolution); + using (GraphicsPath path = new GraphicsPath()) + { + Random random = new Random(); + int HorizontalRegions = (int)(sourceImage.Width / horizontalToothRange); + int VerticalRegions = (int)(sourceImage.Height / verticalToothRange); + + // Start + Point previousEndingPoint = new Point(horizontalToothRange, random.Next(1, toothHeight)); + Point newEndingPoint; + // Top + for (int i = 0; i < HorizontalRegions; i++) + { + int x = (int)previousEndingPoint.X + horizontalToothRange; + int y = random.Next(1, toothHeight); + newEndingPoint = new Point(x, y); + path.AddLine(previousEndingPoint, newEndingPoint); + previousEndingPoint = newEndingPoint; + } + + // Right + for (int i = 0; i < VerticalRegions; i++) + { + int x = sourceImage.Width - random.Next(1, toothHeight); + int y = (int)previousEndingPoint.Y + verticalToothRange; + newEndingPoint = new Point(x, y); + path.AddLine(previousEndingPoint, newEndingPoint); + previousEndingPoint = newEndingPoint; + } + + // Bottom + for (int i = 0; i < HorizontalRegions; i++) + { + int x = (int)previousEndingPoint.X - horizontalToothRange; + int y = sourceImage.Height - random.Next(1, toothHeight); + newEndingPoint = new Point(x, y); + path.AddLine(previousEndingPoint, newEndingPoint); + previousEndingPoint = newEndingPoint; + } + + // Left + for (int i = 0; i < VerticalRegions; i++) + { + int x = random.Next(1, toothHeight); + int y = (int)previousEndingPoint.Y - verticalToothRange; + newEndingPoint = new Point(x, y); + path.AddLine(previousEndingPoint, newEndingPoint); + previousEndingPoint = newEndingPoint; + } + path.CloseFigure(); + + // Draw the created figure with the original image by using a TextureBrush so we have anti-aliasing + using (Graphics graphics = Graphics.FromImage(returnImage)) + { + graphics.SmoothingMode = SmoothingMode.HighQuality; + graphics.PixelOffsetMode = PixelOffsetMode.HighQuality; + graphics.CompositingQuality = CompositingQuality.HighQuality; + graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; + using (Brush brush = new TextureBrush(sourceImage)) + { + // Imporant note: If the target wouldn't be at 0,0 we need to translate-transform!! + graphics.FillPath(brush, path); + } + } + } + return returnImage; + } + + /// + /// Helper Method for the ApplyBlur + /// + /// + /// + private static int[] CreateGaussianBlurRow(int amount) + { + int size = 1 + (amount * 2); + int[] weights = new int[size]; + + for (int i = 0; i <= amount; ++i) + { + // 1 + aa - aa + 2ai - ii + weights[i] = 16 * (i + 1); + weights[weights.Length - i - 1] = weights[i]; + } + + return weights; + } + + /// + /// Apply BoxBlur to the destinationBitmap + /// + /// Bitmap to blur + /// Must be ODD! + public static void ApplyBoxBlur(Bitmap destinationBitmap, int range) + { + // We only need one fastbitmap as we use it as source and target (the reading is done for one line H/V, writing after "parsing" one line H/V) + using (IFastBitmap fastBitmap = FastBitmap.Create(destinationBitmap)) + { + ApplyBoxBlur(fastBitmap, range); + } + } + + /// + /// Apply BoxBlur to the fastBitmap + /// + /// IFastBitmap to blur + /// Must be ODD! + public static void ApplyBoxBlur(IFastBitmap fastBitmap, int range) + { + // Range must be odd! + if ((range & 1) == 0) + { + range++; + } + if (range <= 1) + { + return; + } + // Box blurs are frequently used to approximate a Gaussian blur. + // By the central limit theorem, if applied 3 times on the same image, a box blur approximates the Gaussian kernel to within about 3%, yielding the same result as a quadratic convolution kernel. + // This might be true, but the GDI+ BlurEffect doesn't look the same, a 2x blur is more simular and we only make 2x Box-Blur. + // (Might also be a mistake in our blur, but for now it looks great) + if (fastBitmap.hasAlphaChannel) + { + BoxBlurHorizontalAlpha(fastBitmap, range); + BoxBlurVerticalAlpha(fastBitmap, range); + BoxBlurHorizontalAlpha(fastBitmap, range); + BoxBlurVerticalAlpha(fastBitmap, range); + } + else + { + BoxBlurHorizontal(fastBitmap, range); + BoxBlurVertical(fastBitmap, range); + BoxBlurHorizontal(fastBitmap, range); + BoxBlurVertical(fastBitmap, range); + } + } + + /// + /// BoxBlurHorizontal is a private helper method for the BoxBlur + /// + /// Target BitmapBuffer + /// Range must be odd! + private static void BoxBlurHorizontal(IFastBitmap targetFastBitmap, int range) + { + if (targetFastBitmap.hasAlphaChannel) + { + throw new NotSupportedException("BoxBlurHorizontal should NOT be called for bitmaps with alpha channel"); + } + int halfRange = range / 2; + Color[] newColors = new Color[targetFastBitmap.Width]; + byte[] tmpColor = new byte[3]; + for (int y = targetFastBitmap.Top; y < targetFastBitmap.Bottom; y++) + { + int hits = 0; + int r = 0; + int g = 0; + int b = 0; + for (int x = targetFastBitmap.Left - halfRange; x < targetFastBitmap.Right; x++) + { + int oldPixel = x - halfRange - 1; + if (oldPixel >= targetFastBitmap.Left) + { + targetFastBitmap.GetColorAt(oldPixel, y, tmpColor); + r -= tmpColor[FastBitmap.COLOR_INDEX_R]; + g -= tmpColor[FastBitmap.COLOR_INDEX_G]; + b -= tmpColor[FastBitmap.COLOR_INDEX_B]; + hits--; + } + + int newPixel = x + halfRange; + if (newPixel < targetFastBitmap.Right) + { + targetFastBitmap.GetColorAt(newPixel, y, tmpColor); + r += tmpColor[FastBitmap.COLOR_INDEX_R]; + g += tmpColor[FastBitmap.COLOR_INDEX_G]; + b += tmpColor[FastBitmap.COLOR_INDEX_B]; + hits++; + } + + if (x >= targetFastBitmap.Left) + { + newColors[x - targetFastBitmap.Left] = Color.FromArgb(255, (byte)(r / hits), (byte)(g / hits), (byte)(b / hits)); + } + } + for (int x = targetFastBitmap.Left; x < targetFastBitmap.Right; x++) + { + targetFastBitmap.SetColorAt(x, y, newColors[x - targetFastBitmap.Left]); + } + } + } + + /// + /// BoxBlurHorizontal is a private helper method for the BoxBlur, only for IFastBitmaps with alpha channel + /// + /// Source BitmapBuffer + /// Target BitmapBuffer + /// Range must be odd! + private static void BoxBlurHorizontalAlpha(IFastBitmap targetFastBitmap, int range) + { + if (!targetFastBitmap.hasAlphaChannel) + { + throw new NotSupportedException("BoxBlurHorizontalAlpha should be called for bitmaps with alpha channel"); + } + int halfRange = range / 2; + Color[] newColors = new Color[targetFastBitmap.Width]; + byte[] tmpColor = new byte[4]; + for (int y = targetFastBitmap.Top; y < targetFastBitmap.Bottom; y++) + { + int hits = 0; + int a = 0; + int r = 0; + int g = 0; + int b = 0; + for (int x = targetFastBitmap.Left - halfRange; x < targetFastBitmap.Right; x++) + { + int oldPixel = x - halfRange - 1; + if (oldPixel >= targetFastBitmap.Left) + { + targetFastBitmap.GetColorAt(oldPixel, y, tmpColor); + a -= tmpColor[FastBitmap.COLOR_INDEX_A]; + r -= tmpColor[FastBitmap.COLOR_INDEX_R]; + g -= tmpColor[FastBitmap.COLOR_INDEX_G]; + b -= tmpColor[FastBitmap.COLOR_INDEX_B]; + hits--; + } + + int newPixel = x + halfRange; + if (newPixel < targetFastBitmap.Right) + { + targetFastBitmap.GetColorAt(newPixel, y, tmpColor); + a += tmpColor[FastBitmap.COLOR_INDEX_A]; + r += tmpColor[FastBitmap.COLOR_INDEX_R]; + g += tmpColor[FastBitmap.COLOR_INDEX_G]; + b += tmpColor[FastBitmap.COLOR_INDEX_B]; + hits++; + } + + if (x >= targetFastBitmap.Left) + { + newColors[x - targetFastBitmap.Left] = Color.FromArgb((byte)(a / hits), (byte)(r / hits), (byte)(g / hits), (byte)(b / hits)); + } + } + for (int x = targetFastBitmap.Left; x < targetFastBitmap.Right; x++) + { + targetFastBitmap.SetColorAt(x, y, newColors[x - targetFastBitmap.Left]); + } + } + } + + /// + /// BoxBlurVertical is a private helper method for the BoxBlur + /// + /// BitmapBuffer which previously was created with BoxBlurHorizontal + /// Range must be odd! + private static void BoxBlurVertical(IFastBitmap targetFastBitmap, int range) + { + if (targetFastBitmap.hasAlphaChannel) + { + throw new NotSupportedException("BoxBlurVertical should NOT be called for bitmaps with alpha channel"); + } + int w = targetFastBitmap.Width; + int halfRange = range / 2; + Color[] newColors = new Color[targetFastBitmap.Height]; + int oldPixelOffset = -(halfRange + 1) * w; + int newPixelOffset = (halfRange) * w; + byte[] tmpColor = new byte[4]; + for (int x = targetFastBitmap.Left; x < targetFastBitmap.Right; x++) + { + int hits = 0; + int r = 0; + int g = 0; + int b = 0; + for (int y = targetFastBitmap.Top - halfRange; y < targetFastBitmap.Bottom; y++) + { + int oldPixel = y - halfRange - 1; + if (oldPixel >= targetFastBitmap.Top) + { + targetFastBitmap.GetColorAt(x, oldPixel, tmpColor); + r -= tmpColor[FastBitmap.COLOR_INDEX_R]; + g -= tmpColor[FastBitmap.COLOR_INDEX_G]; + b -= tmpColor[FastBitmap.COLOR_INDEX_B]; + hits--; + } + + int newPixel = y + halfRange; + if (newPixel < targetFastBitmap.Bottom) + { + targetFastBitmap.GetColorAt(x, newPixel, tmpColor); + r += tmpColor[FastBitmap.COLOR_INDEX_R]; + g += tmpColor[FastBitmap.COLOR_INDEX_G]; + b += tmpColor[FastBitmap.COLOR_INDEX_B]; + hits++; + } + + if (y >= targetFastBitmap.Top) + { + newColors[y - targetFastBitmap.Top] = Color.FromArgb(255, (byte)(r / hits), (byte)(g / hits), (byte)(b / hits)); + } + } + + for (int y = targetFastBitmap.Top; y < targetFastBitmap.Bottom; y++) + { + targetFastBitmap.SetColorAt(x, y, newColors[y - targetFastBitmap.Top]); + } + } + } + + /// + /// BoxBlurVertical is a private helper method for the BoxBlur + /// + /// BitmapBuffer which previously was created with BoxBlurHorizontal + /// Range must be odd! + private static void BoxBlurVerticalAlpha(IFastBitmap targetFastBitmap, int range) + { + if (!targetFastBitmap.hasAlphaChannel) + { + throw new NotSupportedException("BoxBlurVerticalAlpha should be called for bitmaps with alpha channel"); + } + + int w = targetFastBitmap.Width; + int halfRange = range / 2; + Color[] newColors = new Color[targetFastBitmap.Height]; + int oldPixelOffset = -(halfRange + 1) * w; + int newPixelOffset = (halfRange) * w; + byte[] tmpColor = new byte[4]; + for (int x = targetFastBitmap.Left; x < targetFastBitmap.Right; x++) + { + int hits = 0; + int a = 0; + int r = 0; + int g = 0; + int b = 0; + for (int y = targetFastBitmap.Top - halfRange; y < targetFastBitmap.Bottom; y++) + { + int oldPixel = y - halfRange - 1; + if (oldPixel >= targetFastBitmap.Top) + { + targetFastBitmap.GetColorAt(x, oldPixel, tmpColor); + a -= tmpColor[FastBitmap.COLOR_INDEX_A]; + r -= tmpColor[FastBitmap.COLOR_INDEX_R]; + g -= tmpColor[FastBitmap.COLOR_INDEX_G]; + b -= tmpColor[FastBitmap.COLOR_INDEX_B]; + hits--; + } + + int newPixel = y + halfRange; + if (newPixel < targetFastBitmap.Bottom) + { + //int colorg = pixels[index + newPixelOffset]; + targetFastBitmap.GetColorAt(x, newPixel, tmpColor); + a += tmpColor[FastBitmap.COLOR_INDEX_A]; + r += tmpColor[FastBitmap.COLOR_INDEX_R]; + g += tmpColor[FastBitmap.COLOR_INDEX_G]; + b += tmpColor[FastBitmap.COLOR_INDEX_B]; + hits++; + } + + if (y >= targetFastBitmap.Top) + { + newColors[y - targetFastBitmap.Top] = Color.FromArgb((byte)(a / hits), (byte)(r / hits), (byte)(g / hits), (byte)(b / hits)); + } + } + + for (int y = targetFastBitmap.Top; y < targetFastBitmap.Bottom; y++) + { + targetFastBitmap.SetColorAt(x, y, newColors[y - targetFastBitmap.Top]); + } + } + } + + /// + /// This method fixes the problem that we can't apply a filter outside the target bitmap, + /// therefor the filtered-bitmap will be shifted if we try to draw it outside the target bitmap. + /// It will also account for the Invert flag. + /// + /// + /// + /// + /// + public static Rectangle CreateIntersectRectangle(Size applySize, Rectangle rect, bool invert) + { + Rectangle myRect; + if (invert) + { + myRect = new Rectangle(0, 0, applySize.Width, applySize.Height); + } + else + { + Rectangle applyRect = new Rectangle(0, 0, applySize.Width, applySize.Height); + myRect = new Rectangle(rect.X, rect.Y, rect.Width, rect.Height); + myRect.Intersect(applyRect); + } + return myRect; + } + + /// + /// Create a new bitmap where the sourceBitmap has a shadow + /// + /// Bitmap to make a shadow on + /// How dark is the shadow + /// Size of the shadow + /// What pixel format must the returning bitmap have + /// How many pixels is the original image moved? + /// Bitmap with the shadow, is bigger than the sourceBitmap!! + public static Bitmap CreateShadow(Image sourceBitmap, float darkness, int shadowSize, Point shadowOffset, out Point offset, PixelFormat targetPixelformat) + { + // Create a new "clean" image + offset = shadowOffset; + offset.X += shadowSize - 1; + offset.Y += shadowSize - 1; + Bitmap returnImage = CreateEmpty(sourceBitmap.Width + (shadowSize * 2), sourceBitmap.Height + (shadowSize * 2), targetPixelformat, Color.Empty, sourceBitmap.HorizontalResolution, sourceBitmap.VerticalResolution); + // Make sure the shadow is odd, there is no reason for an even blur! + if ((shadowSize & 1) == 0) + { + shadowSize++; + } + bool useGDIBlur = GDIplus.IsBlurPossible(shadowSize); + // Create "mask" for the shadow + ColorMatrix maskMatrix = new ColorMatrix(); + maskMatrix.Matrix00 = 0; + maskMatrix.Matrix11 = 0; + maskMatrix.Matrix22 = 0; + if (useGDIBlur) + { + maskMatrix.Matrix33 = darkness + 0.1f; + } + else + { + maskMatrix.Matrix33 = darkness; + } + Rectangle shadowRectangle = new Rectangle(new Point(shadowSize, shadowSize), sourceBitmap.Size); + ApplyColorMatrix((Bitmap)sourceBitmap, Rectangle.Empty, returnImage, shadowRectangle, maskMatrix); + + // blur "shadow", apply to whole new image + if (useGDIBlur) + { + // Use GDI Blur + Rectangle newImageRectangle = new Rectangle(0, 0, returnImage.Width, returnImage.Height); + GDIplus.ApplyBlur(returnImage, newImageRectangle, shadowSize + 1, false); + } + else + { + // try normal software blur + //returnImage = CreateBlur(returnImage, newImageRectangle, true, shadowSize, 1d, false, newImageRectangle); + ApplyBoxBlur(returnImage, shadowSize); + } + + // Draw the original image over the shadow + using (Graphics graphics = Graphics.FromImage(returnImage)) + { + // Make sure we draw with the best quality! + graphics.SmoothingMode = SmoothingMode.HighQuality; + graphics.PixelOffsetMode = PixelOffsetMode.HighQuality; + graphics.CompositingQuality = CompositingQuality.HighQuality; + graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; + // draw original with a TextureBrush so we have nice antialiasing! + using (Brush textureBrush = new TextureBrush(sourceBitmap, WrapMode.Clamp)) + { + // We need to do a translate-tranform otherwise the image is wrapped + graphics.TranslateTransform(offset.X, offset.Y); + graphics.FillRectangle(textureBrush, 0, 0, sourceBitmap.Width, sourceBitmap.Height); + } + } + return returnImage; + } + + /// + /// Return negative of Bitmap + /// + /// Bitmap to create a negative off + /// Negative bitmap + public static Bitmap CreateNegative(Image sourceImage) + { + Bitmap clone = (Bitmap)Clone(sourceImage); + ColorMatrix invertMatrix = new ColorMatrix(new float[][] { + new float[] {-1, 0, 0, 0, 0}, + new float[] {0, -1, 0, 0, 0}, + new float[] {0, 0, -1, 0, 0}, + new float[] {0, 0, 0, 1, 0}, + new float[] {1, 1, 1, 1, 1} + }); + ApplyColorMatrix(clone, invertMatrix); + return clone; + } + + /// + /// Apply a color matrix to the image + /// + /// Image to apply matrix to + /// ColorMatrix to apply + public static void ApplyColorMatrix(Bitmap source, ColorMatrix colorMatrix) + { + ApplyColorMatrix(source, Rectangle.Empty, source, Rectangle.Empty, colorMatrix); + } + + /// + /// Apply a color matrix by copying from the source to the destination + /// + /// Image to copy from + /// Rectangle to copy from + /// Rectangle to copy to + /// Image to copy to + /// ColorMatrix to apply + public static void ApplyColorMatrix(Bitmap source, Rectangle sourceRect, Bitmap dest, Rectangle destRect, ColorMatrix colorMatrix) + { + ImageAttributes imageAttributes = new ImageAttributes(); + imageAttributes.ClearColorMatrix(); + imageAttributes.SetColorMatrix(colorMatrix); + ApplyImageAttributes(source, sourceRect, dest, destRect, imageAttributes); + } + + /// + /// Apply image attributes to the image + /// + /// Image to apply matrix to + /// ImageAttributes to apply + public static void ApplyColorMatrix(Bitmap source, ImageAttributes imageAttributes) + { + ApplyImageAttributes(source, Rectangle.Empty, source, Rectangle.Empty, imageAttributes); + } + + /// + /// Apply a color matrix by copying from the source to the destination + /// + /// Image to copy from + /// Rectangle to copy from + /// Rectangle to copy to + /// Image to copy to + /// ImageAttributes to apply + public static void ApplyImageAttributes(Bitmap source, Rectangle sourceRect, Bitmap dest, Rectangle destRect, ImageAttributes imageAttributes) + { + if (sourceRect == Rectangle.Empty) + { + sourceRect = new Rectangle(0, 0, source.Width, source.Height); + } + if (dest == null) + { + dest = source; + } + if (destRect == Rectangle.Empty) + { + destRect = new Rectangle(0, 0, dest.Width, dest.Height); + } + using (Graphics graphics = Graphics.FromImage(dest)) + { + // Make sure we draw with the best quality! + graphics.SmoothingMode = SmoothingMode.HighQuality; + graphics.PixelOffsetMode = PixelOffsetMode.HighQuality; + graphics.CompositingQuality = CompositingQuality.HighQuality; + graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; + graphics.CompositingMode = CompositingMode.SourceCopy; + + graphics.DrawImage(source, destRect, sourceRect.X, sourceRect.Y, sourceRect.Width, sourceRect.Height, GraphicsUnit.Pixel, imageAttributes); + } + } + + /// + /// Returns a b/w of Bitmap + /// + /// Bitmap to create a b/w of + /// Threshold for monochrome filter (0 - 255), lower value means less black + /// b/w bitmap + public static Bitmap CreateMonochrome(Image sourceImage, byte threshold) + { + using (IFastBitmap fastBitmap = FastBitmap.CreateCloneOf(sourceImage, sourceImage.PixelFormat)) + { + for (int y = 0; y < fastBitmap.Height; y++) + { + for (int x = 0; x < fastBitmap.Width; x++) + { + Color color = fastBitmap.GetColorAt(x, y); + int colorBrightness = ((color.R + color.G + color.B) / 3 > threshold) ? 255 : 0; + Color monoColor = Color.FromArgb(color.A, colorBrightness, colorBrightness, colorBrightness); + fastBitmap.SetColorAt(x, y, monoColor); + } + } + return fastBitmap.UnlockAndReturnBitmap(); + } + } + + /// + /// Create a new bitmap where the sourceBitmap has a Simple border around it + /// + /// Bitmap to make a border on + /// Size of the border + /// Color of the border + /// What pixel format must the returning bitmap have + /// How many pixels is the original image moved? + /// Bitmap with the shadow, is bigger than the sourceBitmap!! + public static Image CreateBorder(Image sourceImage, int borderSize, Color borderColor, PixelFormat targetPixelformat, out Point offset) + { + // "return" the shifted offset, so the caller can e.g. move elements + offset = new Point(borderSize, borderSize); + + // Create a new "clean" image + Bitmap newImage = CreateEmpty(sourceImage.Width + (borderSize * 2), sourceImage.Height + (borderSize * 2), targetPixelformat, Color.Empty, sourceImage.HorizontalResolution, sourceImage.VerticalResolution); + using (Graphics graphics = Graphics.FromImage(newImage)) + { + // Make sure we draw with the best quality! + graphics.SmoothingMode = SmoothingMode.HighQuality; + graphics.PixelOffsetMode = PixelOffsetMode.HighQuality; + graphics.CompositingQuality = CompositingQuality.HighQuality; + graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; + using (GraphicsPath path = new GraphicsPath()) + { + path.AddRectangle(new Rectangle(borderSize >> 1, borderSize >> 1, newImage.Width - (borderSize), newImage.Height - (borderSize))); + using (Pen pen = new Pen(borderColor, borderSize)) + { + pen.LineJoin = LineJoin.Round; + pen.StartCap = LineCap.Round; + pen.EndCap = LineCap.Round; + graphics.DrawPath(pen, path); + } + } + // draw original with a TextureBrush so we have nice antialiasing! + using (Brush textureBrush = new TextureBrush(sourceImage, WrapMode.Clamp)) + { + // We need to do a translate-tranform otherwise the image is wrapped + graphics.TranslateTransform(offset.X, offset.Y); + graphics.FillRectangle(textureBrush, 0, 0, sourceImage.Width, sourceImage.Height); + } + } + return newImage; + } + + /// + /// Create ImageAttributes to modify + /// + /// + /// + /// + /// ImageAttributes + public static ImageAttributes CreateAdjustAttributes(float brightness, float contrast, float gamma) + { + float adjustedBrightness = brightness - 1.0f; + ColorMatrix applyColorMatrix = new ColorMatrix( + new float[][] { + new float[] {contrast, 0, 0, 0, 0}, // scale red + new float[] {0, contrast, 0, 0, 0}, // scale green + new float[] {0, 0, contrast, 0, 0}, // scale blue + new float[] {0, 0, 0, 1.0f, 0}, // don't scale alpha + new float[] {adjustedBrightness, adjustedBrightness, adjustedBrightness, 0, 1} + }); + + //create some image attributes + ImageAttributes attributes = new ImageAttributes(); + attributes.ClearColorMatrix(); + attributes.SetColorMatrix(applyColorMatrix, ColorMatrixFlag.Default, ColorAdjustType.Bitmap); + attributes.SetGamma(gamma, ColorAdjustType.Bitmap); + return attributes; + } + + /// + /// Adjust the brightness, contract or gamma of an image. + /// Use the value "1.0f" for no changes. + /// + /// Original bitmap + /// Bitmap with grayscale + public static Image Adjust(Image sourceImage, float brightness, float contrast, float gamma) + { + //create a blank bitmap the same size as original + // If using 8bpp than the following exception comes: A Graphics object cannot be created from an image that has an indexed pixel format. + Bitmap newBitmap = CreateEmpty(sourceImage.Width, sourceImage.Height, PixelFormat.Format24bppRgb, Color.Empty, sourceImage.HorizontalResolution, sourceImage.VerticalResolution); + ApplyImageAttributes((Bitmap)sourceImage, Rectangle.Empty, newBitmap, Rectangle.Empty, CreateAdjustAttributes(brightness, contrast, gamma)); + return newBitmap; + } + + /// + /// Create a new bitmap where the sourceBitmap is in grayscale + /// + /// Original bitmap + /// Bitmap with grayscale + public static Image CreateGrayscale(Image sourceImage) + { + Bitmap clone = (Bitmap)Clone(sourceImage); + ColorMatrix grayscaleMatrix = new ColorMatrix(new float[][] { + new float[] {.3f, .3f, .3f, 0, 0}, + new float[] {.59f, .59f, .59f, 0, 0}, + new float[] {.11f, .11f, .11f, 0, 0}, + new float[] {0, 0, 0, 1, 0}, + new float[] {0, 0, 0, 0, 1} + }); + ApplyColorMatrix(clone, grayscaleMatrix); + return clone; + } + + /// + /// Checks if the supplied Bitmap has a PixelFormat we support + /// + /// bitmap to check + /// bool if we support it + public static bool SupportsPixelFormat(Image image) + { + return SupportsPixelFormat(image.PixelFormat); + } + + /// + /// Checks if we support the pixel format + /// + /// PixelFormat to check + /// bool if we support it + public static bool SupportsPixelFormat(PixelFormat pixelformat) + { + return (pixelformat.Equals(PixelFormat.Format32bppArgb) || + pixelformat.Equals(PixelFormat.Format32bppPArgb) || + pixelformat.Equals(PixelFormat.Format32bppRgb) || + pixelformat.Equals(PixelFormat.Format24bppRgb)); + } + + /// + /// Wrapper for just cloning which calls the CloneArea + /// + /// Image to clone + /// Bitmap with clone image data + public static Image Clone(Image sourceImage) + { + if (sourceImage is Metafile) + { + return (Image)sourceImage.Clone(); + } + return CloneArea(sourceImage, Rectangle.Empty, PixelFormat.DontCare); + } + + /// + /// Wrapper for just cloning & TargetFormat which calls the CloneArea + /// + /// Image to clone + /// Target Format, use PixelFormat.DontCare if you want the original (or a default if the source PixelFormat is not supported) + /// Bitmap with clone image data + public static Bitmap Clone(Image sourceBitmap, PixelFormat targetFormat) + { + return CloneArea(sourceBitmap, Rectangle.Empty, targetFormat); + } + + /// + /// Clone an image, taking some rules into account: + /// 1) When sourceRect is the whole bitmap there is a GDI+ bug in Clone + /// Clone will than return the same PixelFormat as the source + /// a quick workaround is using new Bitmap which uses a default of Format32bppArgb + /// 2) When going from a transparent to a non transparent bitmap, we draw the background white! + /// + /// Source bitmap to clone + /// Rectangle to copy from the source, use Rectangle.Empty for all + /// Target Format, use PixelFormat.DontCare if you want the original (or a default if the source PixelFormat is not supported) + /// + public static Bitmap CloneArea(Image sourceImage, Rectangle sourceRect, PixelFormat targetFormat) + { + Bitmap newImage = null; + Rectangle bitmapRect = new Rectangle(0, 0, sourceImage.Width, sourceImage.Height); + + // Make sure the source is not Rectangle.Empty + if (Rectangle.Empty.Equals(sourceRect)) + { + sourceRect = new Rectangle(0, 0, sourceImage.Width, sourceImage.Height); + } + else + { + sourceRect.Intersect(bitmapRect); + } + + // If no pixelformat is supplied + if (PixelFormat.DontCare == targetFormat || PixelFormat.Undefined == targetFormat) + { + if (SupportsPixelFormat(sourceImage.PixelFormat)) + { + targetFormat = sourceImage.PixelFormat; + } + else if (Image.IsAlphaPixelFormat(sourceImage.PixelFormat)) + { + targetFormat = PixelFormat.Format32bppArgb; + } + else + { + targetFormat = PixelFormat.Format24bppRgb; + } + } + + // check the target format + if (!SupportsPixelFormat(targetFormat)) + { + if (Image.IsAlphaPixelFormat(targetFormat)) + { + targetFormat = PixelFormat.Format32bppArgb; + } + else + { + targetFormat = PixelFormat.Format24bppRgb; + } + } + + bool destinationIsTransparent = Image.IsAlphaPixelFormat(targetFormat); + bool sourceIsTransparent = Image.IsAlphaPixelFormat(sourceImage.PixelFormat); + bool fromTransparentToNon = !destinationIsTransparent && sourceIsTransparent; + bool isBitmap = sourceImage is Bitmap; + bool isAreaEqual = sourceRect.Equals(bitmapRect); + if (isAreaEqual || fromTransparentToNon || !isBitmap) + { + // Rule 1: if the areas are equal, always copy ourselves + newImage = new Bitmap(bitmapRect.Width, bitmapRect.Height, targetFormat); + // Make sure both images have the same resolution + newImage.SetResolution(sourceImage.HorizontalResolution, sourceImage.VerticalResolution); + + using (Graphics graphics = Graphics.FromImage(newImage)) + { + if (fromTransparentToNon) + { + // Rule 2: Make sure the background color is white + graphics.Clear(Color.White); + } + // decide fastest copy method + if (isAreaEqual) + { + graphics.DrawImageUnscaled(sourceImage, 0, 0); + } + else + { + graphics.DrawImage(sourceImage, 0, 0, sourceRect, GraphicsUnit.Pixel); + } + } + } + else + { + // Let GDI+ decide how to convert, need to test what is quicker... + newImage = (sourceImage as Bitmap).Clone(sourceRect, targetFormat); + // Make sure both images have the same resolution + newImage.SetResolution(sourceImage.HorizontalResolution, sourceImage.VerticalResolution); + } + return newImage; + } + + /// + /// Rotate the bitmap + /// + /// + /// + /// + public static Image RotateFlip(Image sourceImage, RotateFlipType rotateFlipType) + { + Image returnImage = Clone(sourceImage); + returnImage.RotateFlip(rotateFlipType); + return returnImage; + } + + /// + /// A generic way to create an empty image + /// + /// the source bitmap as the specifications for the new bitmap + /// The color to fill with, or Color.Empty to take the default depending on the pixel format + /// + public static Bitmap CreateEmptyLike(Image sourceImage, Color backgroundColor) + { + PixelFormat pixelFormat = sourceImage.PixelFormat; + if (backgroundColor.A < 255) + { + pixelFormat = PixelFormat.Format32bppArgb; + } + return CreateEmpty(sourceImage.Width, sourceImage.Height, pixelFormat, backgroundColor, sourceImage.HorizontalResolution, sourceImage.VerticalResolution); + } + + /// + /// A generic way to create an empty image + /// + /// + /// + /// + /// The color to fill with, or Color.Empty to take the default depending on the pixel format + /// + /// + /// Bitmap + public static Bitmap CreateEmpty(int width, int height, PixelFormat format, Color backgroundColor, float horizontalResolution, float verticalResolution) + { + // Create a new "clean" image + Bitmap newImage = new Bitmap(width, height, format); + newImage.SetResolution(horizontalResolution, verticalResolution); + if (format != PixelFormat.Format8bppIndexed) + { + using (Graphics graphics = Graphics.FromImage(newImage)) + { + // Make sure the background color is what we want (transparent or white, depending on the pixel format) + if (!Color.Empty.Equals(backgroundColor)) + { + graphics.Clear(backgroundColor); + } + else if (Image.IsAlphaPixelFormat(format)) + { + graphics.Clear(Color.Transparent); + } + else + { + graphics.Clear(Color.White); + } + } + } + return newImage; + } + + /// + /// Get a scaled version of the sourceBitmap + /// + /// + /// 1-99 to make smaller, use 101 and more to make the picture bigger + /// + public static Bitmap ScaleByPercent(Bitmap sourceBitmap, int percent) + { + float nPercent = ((float)percent / 100); + + int sourceWidth = sourceBitmap.Width; + int sourceHeight = sourceBitmap.Height; + int destWidth = (int)(sourceWidth * nPercent); + int destHeight = (int)(sourceHeight * nPercent); + + Bitmap scaledBitmap = CreateEmpty(destWidth, destHeight, sourceBitmap.PixelFormat, Color.Empty, sourceBitmap.HorizontalResolution, sourceBitmap.VerticalResolution); + using (Graphics graphics = Graphics.FromImage(scaledBitmap)) + { + graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; + graphics.DrawImage(sourceBitmap, new Rectangle(0, 0, destWidth, destHeight), new Rectangle(0, 0, sourceWidth, sourceHeight), GraphicsUnit.Pixel); + } + return scaledBitmap; + } + + /// + /// Resize canvas with pixel to the left, right, top and bottom + /// + /// + /// The color to fill with, or Color.Empty to take the default depending on the pixel format + /// + /// + /// + /// + /// a new bitmap with the source copied on it + public static Image ResizeCanvas(Image sourceImage, Color backgroundColor, int left, int right, int top, int bottom) + { + Bitmap newBitmap = CreateEmpty(sourceImage.Width + left + right, sourceImage.Height + top + bottom, sourceImage.PixelFormat, backgroundColor, sourceImage.HorizontalResolution, sourceImage.VerticalResolution); + using (Graphics graphics = Graphics.FromImage(newBitmap)) + { + graphics.DrawImageUnscaled(sourceImage, left, top); + } + return newBitmap; + } + + /// + /// Wrapper for the more complex Resize, this resize could be used for e.g. Thumbnails + /// + /// + /// true to maintain the aspect ratio + /// + /// + /// + public static Image ResizeImage(Image sourceImage, bool maintainAspectRatio, int newWidth, int newHeight) + { + Point throwAway; + return ResizeImage(sourceImage, maintainAspectRatio, false, Color.Empty, newWidth, newHeight, out throwAway); + } + + /// + /// Count how many times the supplied color exists + /// + /// Image to count the pixels of + /// Color to count/param> + /// true if Alpha needs to be checked + /// int with the number of pixels which have colorToCount + public static int CountColor(Image sourceImage, Color colorToCount, bool includeAlpha) + { + int colors = 0; + int toCount = colorToCount.ToArgb(); + if (!includeAlpha) + { + toCount = toCount & 0xffffff; + } + using (IFastBitmap bb = FastBitmap.Create((Bitmap)sourceImage)) + { + for (int y = 0; y < bb.Height; y++) + { + for (int x = 0; x < bb.Width; x++) + { + int bitmapcolor = bb.GetColorAt(x, y).ToArgb(); + if (!includeAlpha) + { + bitmapcolor = bitmapcolor & 0xffffff; + } + if (bitmapcolor == toCount) + { + colors++; + } + } + } + return colors; + } + } + + /// + /// Scale the bitmap, keeping aspect ratio, but the canvas will always have the specified size. + /// + /// Image to scale + /// true to maintain the aspect ratio + /// The color to fill with, or Color.Empty to take the default depending on the pixel format + /// new width + /// new height + /// a new bitmap with the specified size, the source-Image scaled to fit with aspect ratio locked + public static Image ResizeImage(Image sourceImage, bool maintainAspectRatio, bool canvasUseNewSize, Color backgroundColor, int newWidth, int newHeight, out Point offset) + { + int destX = 0; + int destY = 0; + + float nPercentW = 0; + float nPercentH = 0; + + nPercentW = ((float)newWidth / (float)sourceImage.Width); + nPercentH = ((float)newHeight / (float)sourceImage.Height); + if (maintainAspectRatio) + { + if (nPercentW == 1) + { + nPercentW = nPercentH; + if (canvasUseNewSize) + { + destX = Math.Max(0, Convert.ToInt32((newWidth - (sourceImage.Width * nPercentW)) / 2)); + } + } + else if (nPercentH == 1) + { + nPercentH = nPercentW; + if (canvasUseNewSize) + { + destY = Math.Max(0, Convert.ToInt32((newHeight - (sourceImage.Height * nPercentH)) / 2)); + } + } + else if (nPercentH != 0 && nPercentH < nPercentW) + { + nPercentW = nPercentH; + if (canvasUseNewSize) + { + destX = Math.Max(0, Convert.ToInt32((newWidth - (sourceImage.Width * nPercentW)) / 2)); + } + } + else + { + nPercentH = nPercentW; + if (canvasUseNewSize) + { + destY = Math.Max(0, Convert.ToInt32((newHeight - (sourceImage.Height * nPercentH)) / 2)); + } + } + } + + offset = new Point(destX, destY); + + int destWidth = (int)(sourceImage.Width * nPercentW); + int destHeight = (int)(sourceImage.Height * nPercentH); + if (newWidth == 0) + { + newWidth = destWidth; + } + if (newHeight == 0) + { + newHeight = destHeight; + } + Image newImage = null; + if (maintainAspectRatio && canvasUseNewSize) + { + newImage = CreateEmpty(newWidth, newHeight, sourceImage.PixelFormat, backgroundColor, sourceImage.HorizontalResolution, sourceImage.VerticalResolution); + } + else + { + newImage = CreateEmpty(destWidth, destHeight, sourceImage.PixelFormat, backgroundColor, sourceImage.HorizontalResolution, sourceImage.VerticalResolution); + } + + using (Graphics graphics = Graphics.FromImage(newImage)) + { + graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; + graphics.DrawImage(sourceImage, new Rectangle(destX, destY, destWidth, destHeight), new Rectangle(0, 0, sourceImage.Width, sourceImage.Height), GraphicsUnit.Pixel); + } + return newImage; + } + } +} \ No newline at end of file diff --git a/GreenshotImageEditor/Core/ImageOutput.cs b/GreenshotImageEditor/Core/ImageOutput.cs new file mode 100644 index 000000000..d1a56cabc --- /dev/null +++ b/GreenshotImageEditor/Core/ImageOutput.cs @@ -0,0 +1,630 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2013 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: http://getgreenshot.org/ + * The Greenshot project is hosted on Sourceforge: http://sourceforge.net/projects/greenshot/ + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using System.Runtime.InteropServices; +using System.Text; +using Greenshot.IniFile; +using Greenshot.Plugin; +using GreenshotPlugin.Controls; +using System; +using System.Drawing; +using System.Drawing.Imaging; +using System.IO; +using System.Reflection; +using System.Text.RegularExpressions; +using System.Windows.Forms; +using Encoder = System.Drawing.Imaging.Encoder; + +namespace GreenshotPlugin.Core +{ + /// + /// Description of ImageOutput. + /// + public static class ImageOutput + { + private static CoreConfiguration conf = IniConfig.GetIniSection(); + private static readonly int PROPERTY_TAG_SOFTWARE_USED = 0x0131; + private static Cache tmpFileCache = new Cache(10 * 60 * 60, RemoveExpiredTmpFile); + + /// + /// Creates a PropertyItem (Metadata) to store with the image. + /// For the possible ID's see: http://msdn.microsoft.com/de-de/library/system.drawing.imaging.propertyitem.id(v=vs.80).aspx + /// This code uses Reflection to create a PropertyItem, although it's not adviced it's not as stupid as having a image in the project so we can read a PropertyItem from that! + /// + /// ID + /// Text + /// + private static PropertyItem CreatePropertyItem(int id, string text) + { + PropertyItem propertyItem = null; + try + { + ConstructorInfo ci = typeof(PropertyItem).GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public, null, new Type[] { }, null); + propertyItem = (PropertyItem)ci.Invoke(null); + // Make sure it's of type string + propertyItem.Type = 2; + // Set the ID + propertyItem.Id = id; + // Set the text + byte[] byteString = ASCIIEncoding.ASCII.GetBytes(text + " "); + // Set Zero byte for String end. + byteString[byteString.Length - 1] = 0; + propertyItem.Value = byteString; + propertyItem.Len = text.Length + 1; + } + catch (Exception e) + { + LOG.WarnFormat("Error creating a PropertyItem: {0}", e.Message); + } + return propertyItem; + } + + #region save + + /// + /// Saves ISurface to stream with specified output settings + /// + /// ISurface to save + /// Stream to save to + /// SurfaceOutputSettings + public static void SaveToStream(ISurface surface, Stream stream, SurfaceOutputSettings outputSettings) + { + Image imageToSave = null; + bool disposeImage = CreateImageFromSurface(surface, outputSettings, out imageToSave); + SaveToStream(imageToSave, surface, stream, outputSettings); + // cleanup if needed + if (disposeImage && imageToSave != null) + { + imageToSave.Dispose(); + } + } + + /// + /// Saves image to stream with specified quality + /// To prevent problems with GDI version of before Windows 7: + /// the stream is checked if it's seekable and if needed a MemoryStream as "cache" is used. + /// + /// image to save + /// surface for the elements, needed if the greenshot format is used + /// Stream to save to + /// SurfaceOutputSettings + public static void SaveToStream(Image imageToSave, ISurface surface, Stream stream, SurfaceOutputSettings outputSettings) + { + ImageFormat imageFormat = null; + bool useMemoryStream = false; + MemoryStream memoryStream = null; + if (outputSettings.Format == OutputFormat.greenshot && surface == null) + { + throw new ArgumentException("Surface needs to be se when using OutputFormat.Greenshot"); + } + + try + { + switch (outputSettings.Format) + { + case OutputFormat.bmp: + imageFormat = ImageFormat.Bmp; + break; + case OutputFormat.gif: + imageFormat = ImageFormat.Gif; + break; + case OutputFormat.jpg: + imageFormat = ImageFormat.Jpeg; + break; + case OutputFormat.tiff: + imageFormat = ImageFormat.Tiff; + break; + case OutputFormat.greenshot: + case OutputFormat.png: + default: + // Problem with non-seekable streams most likely doesn't happen with Windows 7 (OS Version 6.1 and later) + // http://stackoverflow.com/questions/8349260/generic-gdi-error-on-one-machine-but-not-the-other + if (!stream.CanSeek) + { + int majorVersion = Environment.OSVersion.Version.Major; + int minorVersion = Environment.OSVersion.Version.Minor; + if (majorVersion < 6 || (majorVersion == 6 && minorVersion == 0)) + { + useMemoryStream = true; + LOG.Warn("Using memorystream prevent an issue with saving to a non seekable stream."); + } + } + imageFormat = ImageFormat.Png; + break; + } + LOG.DebugFormat("Saving image to stream with Format {0} and PixelFormat {1}", imageFormat, imageToSave.PixelFormat); + + // Check if we want to use a memory stream, to prevent a issue which happens with Windows before "7". + // The save is made to the targetStream, this is directed to either the MemoryStream or the original + Stream targetStream = stream; + if (useMemoryStream) + { + memoryStream = new MemoryStream(); + targetStream = memoryStream; + } + + if (imageFormat == ImageFormat.Jpeg) + { + bool foundEncoder = false; + foreach (ImageCodecInfo imageCodec in ImageCodecInfo.GetImageEncoders()) + { + if (imageCodec.FormatID == imageFormat.Guid) + { + EncoderParameters parameters = new EncoderParameters(1); + parameters.Param[0] = new EncoderParameter(Encoder.Quality, outputSettings.JPGQuality); + // Removing transparency if it's not supported in the output + if (Image.IsAlphaPixelFormat(imageToSave.PixelFormat)) + { + Image nonAlphaImage = ImageHelper.Clone(imageToSave, PixelFormat.Format24bppRgb); + AddTag(nonAlphaImage); + nonAlphaImage.Save(targetStream, imageCodec, parameters); + nonAlphaImage.Dispose(); + nonAlphaImage = null; + } + else + { + AddTag(imageToSave); + imageToSave.Save(targetStream, imageCodec, parameters); + } + foundEncoder = true; + break; + } + } + if (!foundEncoder) + { + throw new ApplicationException("No JPG encoder found, this should not happen."); + } + } + else + { + // Removing transparency if it's not supported in the output + if (imageFormat != ImageFormat.Png && Image.IsAlphaPixelFormat(imageToSave.PixelFormat)) + { + Image nonAlphaImage = ImageHelper.Clone(imageToSave, PixelFormat.Format24bppRgb); + AddTag(nonAlphaImage); + nonAlphaImage.Save(targetStream, imageFormat); + nonAlphaImage.Dispose(); + nonAlphaImage = null; + } + else + { + AddTag(imageToSave); + imageToSave.Save(targetStream, imageFormat); + } + } + + // If we used a memory stream, we need to stream the memory stream to the original stream. + if (useMemoryStream) + { + memoryStream.WriteTo(stream); + } + + // Output the surface elements, size and marker to the stream + if (outputSettings.Format == OutputFormat.greenshot) + { + using (MemoryStream tmpStream = new MemoryStream()) + { + long bytesWritten = surface.SaveElementsToStream(tmpStream); + using (BinaryWriter writer = new BinaryWriter(tmpStream)) + { + writer.Write(bytesWritten); + Version v = Assembly.GetExecutingAssembly().GetName().Version; + byte[] marker = Encoding.ASCII.GetBytes(String.Format("Greenshot{0:00}.{1:00}", v.Major, v.Minor)); + writer.Write(marker); + tmpStream.WriteTo(stream); + } + } + } + } + finally + { + if (memoryStream != null) + { + memoryStream.Dispose(); + } + } + } + + /// + /// Create an image from a surface with the settings from the output settings applied + /// + /// + /// + /// + /// true if the image must be disposed + public static bool CreateImageFromSurface(ISurface surface, SurfaceOutputSettings outputSettings, out Image imageToSave) + { + bool disposeImage = false; + ImageFormat imageFormat = null; + switch (outputSettings.Format) + { + case OutputFormat.bmp: + imageFormat = ImageFormat.Bmp; + break; + case OutputFormat.gif: + imageFormat = ImageFormat.Gif; + break; + case OutputFormat.jpg: + imageFormat = ImageFormat.Jpeg; + break; + case OutputFormat.tiff: + imageFormat = ImageFormat.Tiff; + break; + case OutputFormat.greenshot: + case OutputFormat.png: + default: + imageFormat = ImageFormat.Png; + break; + } + + if (outputSettings.Format == OutputFormat.greenshot || outputSettings.SaveBackgroundOnly) + { + // We save the image of the surface, this should not be disposed + imageToSave = surface.Image; + } + else + { + // We create the export image of the surface to save + imageToSave = surface.GetImageForExport(); + disposeImage = true; + } + + // The following block of modifications should be skipped when saving the greenshot format, no effects or otherwise! + if (outputSettings.Format != OutputFormat.greenshot) + { + Image tmpImage; + if (outputSettings.Effects != null && outputSettings.Effects.Count > 0) + { + // apply effects, if there are any + Point ignoreOffset; + tmpImage = ImageHelper.ApplyEffects((Bitmap)imageToSave, outputSettings.Effects, out ignoreOffset); + if (tmpImage != null) + { + if (disposeImage) + { + imageToSave.Dispose(); + } + imageToSave = tmpImage; + disposeImage = true; + } + } + + // check for color reduction, forced or automatically, only when the DisableReduceColors is false + if (!outputSettings.DisableReduceColors && (conf.OutputFileAutoReduceColors || outputSettings.ReduceColors)) + { + bool isAlpha = Image.IsAlphaPixelFormat(imageToSave.PixelFormat); + if (outputSettings.ReduceColors || (!isAlpha && conf.OutputFileAutoReduceColors)) + { + using (WuQuantizer quantizer = new WuQuantizer((Bitmap)imageToSave)) + { + int colorCount = quantizer.GetColorCount(); + LOG.InfoFormat("Image with format {0} has {1} colors", imageToSave.PixelFormat, colorCount); + if (outputSettings.ReduceColors || colorCount < 256) + { + try + { + LOG.Info("Reducing colors on bitmap to 256."); + tmpImage = quantizer.GetQuantizedImage(256); + if (disposeImage) + { + imageToSave.Dispose(); + } + imageToSave = tmpImage; + // Make sure the "new" image is disposed + disposeImage = true; + } + catch (Exception e) + { + LOG.Warn("Error occurred while Quantizing the image, ignoring and using original. Error: ", e); + } + } + } + } + else if (isAlpha && !outputSettings.ReduceColors) + { + LOG.Info("Skipping 'optional' color reduction as the image has alpha"); + } + } + } + return disposeImage; + } + + /// + /// Add the greenshot property! + /// + /// + private static void AddTag(Image imageToSave) + { + // Create meta-data + PropertyItem softwareUsedPropertyItem = CreatePropertyItem(PROPERTY_TAG_SOFTWARE_USED, "Greenshot"); + if (softwareUsedPropertyItem != null) + { + try + { + imageToSave.SetPropertyItem(softwareUsedPropertyItem); + } + catch (Exception) + { + LOG.WarnFormat("Couldn't set property {0}", softwareUsedPropertyItem.Id); + } + } + } + + /// + /// Load a Greenshot surface + /// + /// + /// + public static ISurface LoadGreenshotSurface(string fullPath, ISurface returnSurface) + { + if (string.IsNullOrEmpty(fullPath)) + { + return null; + } + Image fileImage = null; + LOG.InfoFormat("Loading image from file {0}", fullPath); + // Fixed lock problem Bug #3431881 + using (Stream surfaceFileStream = File.OpenRead(fullPath)) + { + // And fixed problem that the bitmap stream is disposed... by Cloning the image + // This also ensures the bitmap is correctly created + + // We create a copy of the bitmap, so everything else can be disposed + surfaceFileStream.Position = 0; + using (Image tmpImage = Image.FromStream(surfaceFileStream, true, true)) + { + LOG.DebugFormat("Loaded {0} with Size {1}x{2} and PixelFormat {3}", fullPath, tmpImage.Width, tmpImage.Height, tmpImage.PixelFormat); + fileImage = ImageHelper.Clone(tmpImage); + } + // Start at -14 read "GreenshotXX.YY" (XX=Major, YY=Minor) + const int markerSize = 14; + surfaceFileStream.Seek(-markerSize, SeekOrigin.End); + string greenshotMarker; + using (StreamReader streamReader = new StreamReader(surfaceFileStream)) + { + greenshotMarker = streamReader.ReadToEnd(); + if (greenshotMarker == null || !greenshotMarker.StartsWith("Greenshot")) + { + throw new ArgumentException(string.Format("{0} is not a Greenshot file!", fullPath)); + } + LOG.InfoFormat("Greenshot file format: {0}", greenshotMarker); + const int filesizeLocation = 8 + markerSize; + surfaceFileStream.Seek(-filesizeLocation, SeekOrigin.End); + long bytesWritten = 0; + using (BinaryReader reader = new BinaryReader(surfaceFileStream)) + { + bytesWritten = reader.ReadInt64(); + surfaceFileStream.Seek(-(bytesWritten + filesizeLocation), SeekOrigin.End); + returnSurface.LoadElementsFromStream(surfaceFileStream); + } + } + } + if (fileImage != null) + { + returnSurface.Image = fileImage; + LOG.InfoFormat("Information about file {0}: {1}x{2}-{3} Resolution {4}x{5}", fullPath, fileImage.Width, fileImage.Height, fileImage.PixelFormat, fileImage.HorizontalResolution, fileImage.VerticalResolution); + } + return returnSurface; + } + + /// + /// Saves image to specific path with specified quality + /// + public static void Save(ISurface surface, string fullPath, bool allowOverwrite, SurfaceOutputSettings outputSettings, bool copyPathToClipboard) + { + fullPath = FilenameHelper.MakeFQFilenameSafe(fullPath); + string path = Path.GetDirectoryName(fullPath); + + // check whether path exists - if not create it + DirectoryInfo di = new DirectoryInfo(path); + if (!di.Exists) + { + Directory.CreateDirectory(di.FullName); + } + + if (!allowOverwrite && File.Exists(fullPath)) + { + ArgumentException throwingException = new ArgumentException("File '" + fullPath + "' already exists."); + throwingException.Data.Add("fullPath", fullPath); + throw throwingException; + } + LOG.DebugFormat("Saving surface to {0}", fullPath); + // Create the stream and call SaveToStream + using (FileStream stream = new FileStream(fullPath, FileMode.Create, FileAccess.Write)) + { + SaveToStream(surface, stream, outputSettings); + } + + if (copyPathToClipboard) + { + ClipboardHelper.SetClipboardData(fullPath); + } + } + + /// + /// Get the OutputFormat for a filename + /// + /// filename (can be a complete path) + /// OutputFormat + public static OutputFormat FormatForFilename(string fullPath) + { + // Fix for bug 2912959 + string extension = fullPath.Substring(fullPath.LastIndexOf(".") + 1); + OutputFormat format = OutputFormat.png; + try + { + if (extension != null) + { + format = (OutputFormat)Enum.Parse(typeof(OutputFormat), extension.ToLower()); + } + } + catch (ArgumentException ae) + { + LOG.Warn("Couldn't parse extension: " + extension, ae); + } + return format; + } + + #endregion save + + #region save-as + + /// + /// Save with showing a dialog + /// + /// + /// + /// Path to filename + public static string SaveWithDialog(ISurface surface, ICaptureDetails captureDetails) + { + string returnValue = null; + using (SaveImageFileDialog saveImageFileDialog = new SaveImageFileDialog(captureDetails)) + { + DialogResult dialogResult = saveImageFileDialog.ShowDialog(); + if (dialogResult.Equals(DialogResult.OK)) + { + try + { + string fileNameWithExtension = saveImageFileDialog.FileNameWithExtension; + SurfaceOutputSettings outputSettings = new SurfaceOutputSettings(FormatForFilename(fileNameWithExtension)); + if (conf.OutputFilePromptQuality) + { + QualityDialog qualityDialog = new QualityDialog(outputSettings); + qualityDialog.ShowDialog(); + } + // TODO: For now we always overwrite, should be changed + Save(surface, fileNameWithExtension, true, outputSettings, conf.OutputFileCopyPathToClipboard); + returnValue = fileNameWithExtension; + IniConfig.Save(); + } + catch (ExternalException) + { + MessageBox.Show(Language.GetFormattedString("error_nowriteaccess", saveImageFileDialog.FileName).Replace(@"\\", @"\"), Language.GetString("error")); + } + } + } + return returnValue; + } + + #endregion save-as + + /// + /// Create a tmpfile which has the name like in the configured pattern. + /// Used e.g. by the email export + /// + /// + /// + /// + /// Path to image file + public static string SaveNamedTmpFile(ISurface surface, ICaptureDetails captureDetails, SurfaceOutputSettings outputSettings) + { + string pattern = conf.OutputFileFilenamePattern; + if (pattern == null || string.IsNullOrEmpty(pattern.Trim())) + { + pattern = "greenshot ${capturetime}"; + } + string filename = FilenameHelper.GetFilenameFromPattern(pattern, outputSettings.Format, captureDetails); + // Prevent problems with "other characters", which causes a problem in e.g. Outlook 2007 or break our HTML + filename = Regex.Replace(filename, @"[^\d\w\.]", "_"); + // Remove multiple "_" + filename = Regex.Replace(filename, @"_+", "_"); + string tmpFile = Path.Combine(Path.GetTempPath(), filename); + + LOG.Debug("Creating TMP File: " + tmpFile); + + // Catching any exception to prevent that the user can't write in the directory. + // This is done for e.g. bugs #2974608, #2963943, #2816163, #2795317, #2789218 + try + { + Save(surface, tmpFile, true, outputSettings, false); + tmpFileCache.Add(tmpFile, tmpFile); + } + catch (Exception e) + { + // Show the problem + MessageBox.Show(e.Message, "Error"); + // when save failed we present a SaveWithDialog + tmpFile = SaveWithDialog(surface, captureDetails); + } + return tmpFile; + } + + /// + /// Helper method to create a temp image file + /// + /// + /// + public static string SaveToTmpFile(ISurface surface, SurfaceOutputSettings outputSettings, string destinationPath) + { + string tmpFile = Path.GetRandomFileName() + "." + outputSettings.Format.ToString(); + // Prevent problems with "other characters", which could cause problems + tmpFile = Regex.Replace(tmpFile, @"[^\d\w\.]", ""); + if (destinationPath == null) + { + destinationPath = Path.GetTempPath(); + } + string tmpPath = Path.Combine(destinationPath, tmpFile); + LOG.Debug("Creating TMP File : " + tmpPath); + + try + { + Save(surface, tmpPath, true, outputSettings, false); + tmpFileCache.Add(tmpPath, tmpPath); + } + catch (Exception) + { + return null; + } + return tmpPath; + } + + /// + /// Cleanup all created tmpfiles + /// + public static void RemoveTmpFiles() + { + foreach (string tmpFile in tmpFileCache.Elements) + { + if (File.Exists(tmpFile)) + { + LOG.DebugFormat("Removing old temp file {0}", tmpFile); + File.Delete(tmpFile); + } + tmpFileCache.Remove(tmpFile); + } + } + + /// + /// Cleanup handler for expired tempfiles + /// + /// + /// + private static void RemoveExpiredTmpFile(string filekey, object filename) + { + string path = filename as string; + if (path != null && File.Exists(path)) + { + LOG.DebugFormat("Removing expired file {0}", path); + File.Delete(path); + } + } + } +} \ No newline at end of file diff --git a/GreenshotImageEditor/Core/LOG.cs b/GreenshotImageEditor/Core/LOG.cs new file mode 100644 index 000000000..0e90b03d1 --- /dev/null +++ b/GreenshotImageEditor/Core/LOG.cs @@ -0,0 +1,89 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2013 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: http://getgreenshot.org/ + * The Greenshot project is hosted on Sourceforge: http://sourceforge.net/projects/greenshot/ + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using System; + +namespace GreenshotPlugin +{ + // Workaround for removing log4net + public static class LOG + { + public static bool IsDebugEnabled + { + get + { + return false; + } + } + + public static void Warn(string text) + { + } + + public static void WarnFormat(string format, params object[] args) + { + } + + public static void Warn(Exception e) + { + } + + public static void Warn(string text, Exception e) + { + } + + public static void Error(string text) + { + } + + public static void ErrorFormat(string format, params object[] args) + { + } + + public static void Error(Exception e) + { + } + + public static void Error(string text, Exception e) + { + } + + public static void Info(string text) + { + } + + public static void InfoFormat(string format, params object[] args) + { + } + + public static void Debug(string text) + { + } + + public static void DebugFormat(string text, params object[] args) + { + } + + public static void Debug(string text, Exception e) + { + } + } +} \ No newline at end of file diff --git a/GreenshotImageEditor/Core/Language.cs b/GreenshotImageEditor/Core/Language.cs new file mode 100644 index 000000000..9fa7e95be --- /dev/null +++ b/GreenshotImageEditor/Core/Language.cs @@ -0,0 +1,863 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2013 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: http://getgreenshot.org/ + * The Greenshot project is hosted on Sourceforge: http://sourceforge.net/projects/greenshot/ + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using System; +using System.Collections.Generic; +using System.IO; +using System.Text.RegularExpressions; +using System.Xml; + +namespace GreenshotPlugin.Core +{ + public delegate void LanguageChangedHandler(object sender, EventArgs e); + + /// + /// This class supplies the GUI with translations, based upon keys. + /// The language resources are loaded from the language files found on fixed or supplied paths + /// + public class Language + { + private static List languagePaths = new List(); + private static IDictionary> languageFiles = new Dictionary>(); + private static IDictionary helpFiles = new Dictionary(); + private const string DEFAULT_LANGUAGE = "en-US"; + private const string HELP_FILENAME_PATTERN = @"help-*.html"; + private const string LANGUAGE_FILENAME_PATTERN = @"language*.xml"; + private static Regex PREFIX_REGEXP = new Regex(@"language_([a-zA-Z0-9]+).*"); + private static Regex IETF_CLEAN_REGEXP = new Regex(@"[^a-zA-Z]+"); + private static Regex IETF_REGEXP = new Regex(@"^.*([a-zA-Z]{2}-[a-zA-Z]{2})\.xml$"); + private const string LANGUAGE_GROUPS_KEY = @"SYSTEM\CurrentControlSet\Control\Nls\Language Groups"; + private static List unsupportedLanguageGroups = new List(); + private static IDictionary resources = new Dictionary(); + private static string currentLanguage = null; + + public static event LanguageChangedHandler LanguageChanged; + + /// + /// Static initializer for the language code + /// + static Language() + { + /*try + { + string applicationDataFolder = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData); + string applicationFolder = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); + + // PAF Path + AddPath(Path.Combine(applicationFolder, @"App\Greenshot\Languages")); + + // Application data path + AddPath(Path.Combine(applicationDataFolder, @"Greenshot\Languages\")); + + // Startup path + AddPath(Path.Combine(applicationFolder, @"Languages")); + } + catch (Exception pathException) + { + LOG.Error(pathException); + } + + try + { + using (RegistryKey languageGroupsKey = Registry.LocalMachine.OpenSubKey(LANGUAGE_GROUPS_KEY, false)) + { + if (languageGroupsKey != null) + { + string[] groups = languageGroupsKey.GetValueNames(); + foreach (string group in groups) + { + string groupValue = (string)languageGroupsKey.GetValue(group); + bool isGroupNotInstalled = "0".Equals(groupValue); + if (isGroupNotInstalled) + { + unsupportedLanguageGroups.Add(group.ToLower()); + } + } + } + } + } + catch (Exception e) + { + LOG.Warn("Couldn't read the installed language groups.", e); + } + + coreConfig = IniConfig.GetIniSection(); + ScanFiles(); + if (!string.IsNullOrEmpty(coreConfig.Language)) + { + CurrentLanguage = coreConfig.Language; + if (CurrentLanguage != null && CurrentLanguage != coreConfig.Language) + { + coreConfig.Language = CurrentLanguage; + IniConfig.Save(); + } + } + + if (CurrentLanguage == null) + { + LOG.Warn("Couldn't set language from configuration, changing to default. Installation problem?"); + CurrentLanguage = DEFAULT_LANGUAGE; + if (CurrentLanguage != null) + { + coreConfig.Language = CurrentLanguage; + IniConfig.Save(); + } + } + + if (CurrentLanguage == null) + { + LOG.Error("Couldn't set language, installation problem?"); + }*/ + } + + /// + /// Internal method to add a path to the paths that will be scanned for language files! + /// + /// + /// true if the path exists and is added + private static bool AddPath(string path) + { + if (!languagePaths.Contains(path)) + { + if (Directory.Exists(path)) + { + LOG.DebugFormat("Adding language path {0}", path); + languagePaths.Add(path); + return true; + } + else + { + LOG.InfoFormat("Not adding non existing language path {0}", path); + } + } + return false; + } + + /// + /// Add a new path to the paths that will be scanned for language files! + /// + /// + /// true if the path exists and is added + public static bool AddLanguageFilePath(string path) + { + if (!languagePaths.Contains(path)) + { + LOG.DebugFormat("New language path {0}", path); + if (AddPath(path)) + { + ScanFiles(); + Reload(); + } + else + { + return false; + } + } + return true; + } + + /// + /// Load the files for the specified ietf + /// + /// + private static void LoadFiles(string ietf) + { + ietf = ReformatIETF(ietf); + if (!languageFiles.ContainsKey(ietf)) + { + LOG.ErrorFormat("No language {0} available.", ietf); + return; + } + List filesToLoad = languageFiles[ietf]; + foreach (LanguageFile fileToLoad in filesToLoad) + { + LoadResources(fileToLoad); + } + } + + /// + /// Load the language resources from the scanned files + /// + private static void Reload() + { + resources.Clear(); + LoadFiles(DEFAULT_LANGUAGE); + if (currentLanguage != null && !currentLanguage.Equals(DEFAULT_LANGUAGE)) + { + LoadFiles(currentLanguage); + } + } + + /// + /// Get or set the current language + /// + public static string CurrentLanguage + { + get + { + return currentLanguage; + } + set + { + string ietf = FindBestIETFMatch(value); + if (!languageFiles.ContainsKey(ietf)) + { + LOG.WarnFormat("No match for language {0} found!", ietf); + } + else + { + if (currentLanguage == null || !currentLanguage.Equals(ietf)) + { + currentLanguage = ietf; + Reload(); + if (LanguageChanged != null) + { + try + { + LanguageChanged(null, null); + } + catch + { + } + } + return; + } + } + LOG.Debug("CurrentLanguage not changed!"); + } + } + + /// + /// Try to find the best match for the supplied IETF + /// + /// + /// IETF + private static string FindBestIETFMatch(string inputIETF) + { + string returnIETF = inputIETF; + if (string.IsNullOrEmpty(returnIETF)) + { + returnIETF = DEFAULT_LANGUAGE; + } + returnIETF = ReformatIETF(returnIETF); + if (!languageFiles.ContainsKey(returnIETF)) + { + LOG.WarnFormat("Unknown language {0}, trying best match!", returnIETF); + if (returnIETF.Length == 5) + { + returnIETF = returnIETF.Substring(0, 2); + } + foreach (string availableIETF in languageFiles.Keys) + { + if (availableIETF.StartsWith(returnIETF)) + { + LOG.InfoFormat("Found language {0}, best match for {1}!", availableIETF, returnIETF); + returnIETF = availableIETF; + break; + } + } + } + return returnIETF; + } + + /// + /// This helper method clears all non alpha characters from the IETF, and does a reformatting. + /// This prevents problems with multiple formats or typos. + /// + /// + /// + private static string ReformatIETF(string inputIETF) + { + string returnIETF = null; + if (!string.IsNullOrEmpty(inputIETF)) + { + returnIETF = inputIETF.ToLower(); + returnIETF = IETF_CLEAN_REGEXP.Replace(returnIETF, ""); + if (returnIETF.Length == 4) + { + returnIETF = returnIETF.Substring(0, 2) + "-" + returnIETF.Substring(2, 2).ToUpper(); + } + } + return returnIETF; + } + + /// + /// Return a list of all the supported languages + /// + public static IList SupportedLanguages + { + get + { + IList languages = new List(); + // Loop over all languages with all the files in there + foreach (List langs in languageFiles.Values) + { + // Loop over all the files for a language + foreach (LanguageFile langFile in langs) + { + // Only take the ones without prefix, these are the "base" language files + if (langFile.Prefix == null) + { + languages.Add(langFile); + break; + } + } + } + return languages; + } + } + + /// + /// Return the path to the help-file + /// + public static string HelpFilePath + { + get + { + if (helpFiles.ContainsKey(currentLanguage)) + { + return helpFiles[currentLanguage]; + } + return helpFiles[DEFAULT_LANGUAGE]; + } + } + + /// + /// Load the resources from the language file + /// + /// File to load from + private static void LoadResources(LanguageFile languageFile) + { + LOG.InfoFormat("Loading language file {0}", languageFile.Filepath); + try + { + XmlDocument xmlDocument = new XmlDocument(); + xmlDocument.Load(languageFile.Filepath); + XmlNodeList resourceNodes = xmlDocument.GetElementsByTagName("resource"); + foreach (XmlNode resourceNode in resourceNodes) + { + string key = resourceNode.Attributes["name"].Value; + if (!string.IsNullOrEmpty(languageFile.Prefix)) + { + key = languageFile.Prefix + "." + key; + } + string text = resourceNode.InnerText; + if (!string.IsNullOrEmpty(text)) + { + text = text.Trim(); + } + if (!resources.ContainsKey(key)) + { + resources.Add(key, text); + } + else + { + resources[key] = text; + } + } + } + catch (Exception e) + { + LOG.Error("Could not load language file " + languageFile.Filepath, e); + } + } + + /// + /// Load the language file information + /// + /// + /// + private static LanguageFile LoadFileInfo(string languageFilePath) + { + try + { + XmlDocument xmlDocument = new XmlDocument(); + xmlDocument.Load(languageFilePath); + XmlNodeList nodes = xmlDocument.GetElementsByTagName("language"); + if (nodes.Count > 0) + { + LanguageFile languageFile = new LanguageFile(); + languageFile.Filepath = languageFilePath; + XmlNode node = nodes.Item(0); + languageFile.Description = node.Attributes["description"].Value; + if (node.Attributes["ietf"] != null) + { + languageFile.Ietf = ReformatIETF(node.Attributes["ietf"].Value); + } + if (node.Attributes["version"] != null) + { + languageFile.Version = new Version(node.Attributes["version"].Value); + } + if (node.Attributes["prefix"] != null) + { + languageFile.Prefix = node.Attributes["prefix"].Value.ToLower(); + } + if (node.Attributes["languagegroup"] != null) + { + string languageGroup = node.Attributes["languagegroup"].Value; + languageFile.LanguageGroup = languageGroup.ToLower(); + } + return languageFile; + } + else + { + throw new XmlException("Root element is missing"); + } + } + catch (Exception e) + { + LOG.Error("Could not load language file " + languageFilePath, e); + } + return null; + } + + /// + /// Scan the files in all directories + /// + private static void ScanFiles() + { + languageFiles.Clear(); + helpFiles.Clear(); + foreach (string languagePath in languagePaths) + { + if (!Directory.Exists(languagePath)) + { + LOG.InfoFormat("Skipping non existing language path {0}", languagePath); + continue; + } + LOG.InfoFormat("Searching language directory '{0}' for language files with pattern '{1}'", languagePath, LANGUAGE_FILENAME_PATTERN); + try + { + foreach (string languageFilepath in Directory.GetFiles(languagePath, LANGUAGE_FILENAME_PATTERN, SearchOption.AllDirectories)) + { + //LOG.DebugFormat("Found language file: {0}", languageFilepath); + LanguageFile languageFile = LoadFileInfo(languageFilepath); + if (languageFile == null) + { + continue; + } + if (string.IsNullOrEmpty(languageFile.Ietf)) + { + LOG.WarnFormat("Fixing missing ietf in language-file {0}", languageFilepath); + string languageFilename = Path.GetFileName(languageFilepath); + if (IETF_REGEXP.IsMatch(languageFilename)) + { + string replacementIETF = IETF_REGEXP.Replace(languageFilename, "$1"); + languageFile.Ietf = ReformatIETF(replacementIETF); + LOG.InfoFormat("Fixed IETF to {0}", languageFile.Ietf); + } + else + { + LOG.ErrorFormat("Missing ietf , no recover possible... skipping language-file {0}!", languageFilepath); + continue; + } + } + + // Check if we can display the file + if (!string.IsNullOrEmpty(languageFile.LanguageGroup) && unsupportedLanguageGroups.Contains(languageFile.LanguageGroup)) + { + LOG.InfoFormat("Skipping unsuported (not able to display) language {0} from file {1}", languageFile.Description, languageFilepath); + continue; + } + + // build prefix, based on the filename, but only if it's not set in the file itself. + if (string.IsNullOrEmpty(languageFile.Prefix)) + { + string languageFilename = Path.GetFileNameWithoutExtension(languageFilepath); + if (PREFIX_REGEXP.IsMatch(languageFilename)) + { + languageFile.Prefix = PREFIX_REGEXP.Replace(languageFilename, "$1"); + if (!string.IsNullOrEmpty(languageFile.Prefix)) + { + languageFile.Prefix = languageFile.Prefix.Replace("plugin", "").ToLower(); + } + } + } + List currentFiles = null; + if (languageFiles.ContainsKey(languageFile.Ietf)) + { + currentFiles = languageFiles[languageFile.Ietf]; + bool needToAdd = true; + List deleteList = new List(); + foreach (LanguageFile compareWithLangfile in currentFiles) + { + if ((languageFile.Prefix == null && compareWithLangfile.Prefix == null) || (languageFile.Prefix != null && languageFile.Prefix.Equals(compareWithLangfile.Prefix))) + { + if (compareWithLangfile.Version > languageFile.Version) + { + LOG.WarnFormat("Skipping {0}:{1}:{2} as {3}:{4}:{5} is newer", languageFile.Filepath, languageFile.Prefix, languageFile.Version, compareWithLangfile.Filepath, compareWithLangfile.Prefix, compareWithLangfile.Version); + needToAdd = false; + break; + } + else + { + LOG.WarnFormat("Found {0}:{1}:{2} and deleting {3}:{4}:{5}", languageFile.Filepath, languageFile.Prefix, languageFile.Version, compareWithLangfile.Filepath, compareWithLangfile.Prefix, compareWithLangfile.Version); + deleteList.Add(compareWithLangfile); + } + } + } + if (needToAdd) + { + foreach (LanguageFile deleteFile in deleteList) + { + currentFiles.Remove(deleteFile); + } + LOG.InfoFormat("Added language {0} from: {1}", languageFile.Description, languageFile.Filepath); + currentFiles.Add(languageFile); + } + } + else + { + currentFiles = new List(); + currentFiles.Add(languageFile); + languageFiles.Add(languageFile.Ietf, currentFiles); + LOG.InfoFormat("Added language {0} from: {1}", languageFile.Description, languageFile.Filepath); + } + } + } + catch (DirectoryNotFoundException) + { + LOG.InfoFormat("Non existing language directory: {0}", languagePath); + } + catch (Exception e) + { + LOG.Error("Error trying for read directory " + languagePath, e); + } + + // Now find the help files + LOG.InfoFormat("Searching language directory '{0}' for help files with pattern '{1}'", languagePath, HELP_FILENAME_PATTERN); + try + { + foreach (string helpFilepath in Directory.GetFiles(languagePath, HELP_FILENAME_PATTERN, SearchOption.AllDirectories)) + { + LOG.DebugFormat("Found help file: {0}", helpFilepath); + string helpFilename = Path.GetFileName(helpFilepath); + string ietf = ReformatIETF(helpFilename.Replace(".html", "").Replace("help-", "")); + if (!helpFiles.ContainsKey(ietf)) + { + helpFiles.Add(ietf, helpFilepath); + } + else + { + LOG.WarnFormat("skipping help file {0}, already a file with the same IETF {1} found!", helpFilepath, ietf); + } + } + } + catch (DirectoryNotFoundException) + { + LOG.InfoFormat("Non existing language directory: {0}", languagePath); + } + catch (Exception e) + { + LOG.Error("Error trying for read directory " + languagePath, e); + } + } + } + + /// + /// Check if a resource with prefix.key exists + /// + /// + /// + /// true if available + public static bool hasKey(string prefix, Enum key) + { + if (key == null) + { + return false; + } + return hasKey(prefix + "." + key.ToString()); + } + + /// + /// Check if a resource with key exists + /// + /// + /// true if available + public static bool hasKey(Enum key) + { + if (key == null) + { + return false; + } + return hasKey(key.ToString()); + } + + /// + /// Check if a resource with prefix.key exists + /// + /// + /// + /// true if available + public static bool hasKey(string prefix, string key) + { + return hasKey(prefix + "." + key); + } + + /// + /// Check if a resource with key exists + /// + /// + /// true if available + public static bool hasKey(string key) + { + if (key == null) + { + return false; + } + return resources.ContainsKey(key); + } + + /// + /// TryGet method which combines hasKey & GetString + /// + /// + /// out string + /// + public static bool TryGetString(string key, out string languageString) + { + return resources.TryGetValue(key, out languageString); + } + + /// + /// TryGet method which combines hasKey & GetString + /// + /// string with prefix + /// string with key + /// out string + /// + public static bool TryGetString(string prefix, string key, out string languageString) + { + return resources.TryGetValue(prefix + "." + key, out languageString); + } + + public static string Translate(object key) + { + string typename = key.GetType().Name; + string enumKey = typename + "." + key.ToString(); + if (hasKey(enumKey)) + { + return GetString(enumKey); + } + return key.ToString(); + } + + /// + /// Get the resource for key + /// + /// + /// resource or a "string ###key### not found" + public static string GetString(Enum key) + { + if (key == null) + { + return null; + } + return GetString(key.ToString()); + } + + /// + /// Get the resource for prefix.key + /// + /// + /// + /// resource or a "string ###prefix.key### not found" + public static string GetString(string prefix, Enum key) + { + if (key == null) + { + return null; + } + return GetString(prefix + "." + key.ToString()); + } + + /// + /// Get the resource for prefix.key + /// + /// + /// + /// resource or a "string ###prefix.key### not found" + public static string GetString(string prefix, string key) + { + return GetString(prefix + "." + key); + } + + /// + /// Get the resource for key + /// + /// + /// resource or a "string ###key### not found" + public static string GetString(string key) + { + if (key == null) + { + return null; + } + string returnValue; + if (!resources.TryGetValue(key, out returnValue)) + { + return "string ###" + key + "### not found"; + } + return returnValue; + } + + /// + /// Get the resource for key, format with with string.format an supply the parameters + /// + /// + /// formatted resource or a "string ###key### not found" + public static string GetFormattedString(Enum key, object param) + { + return GetFormattedString(key.ToString(), param); + } + + /// + /// Get the resource for prefix.key, format with with string.format an supply the parameters + /// + /// + /// formatted resource or a "string ###prefix.key### not found" + public static string GetFormattedString(string prefix, Enum key, object param) + { + return GetFormattedString(prefix, key.ToString(), param); + } + + /// + /// Get the resource for prefix.key, format with with string.format an supply the parameters + /// + /// + /// formatted resource or a "string ###prefix.key### not found" + public static string GetFormattedString(string prefix, string key, object param) + { + return GetFormattedString(prefix + "." + key, param); + } + + /// + /// Get the resource for key, format with with string.format an supply the parameters + /// + /// + /// formatted resource or a "string ###key### not found" + public static string GetFormattedString(string key, object param) + { + string returnValue; + if (!resources.TryGetValue(key, out returnValue)) + { + return "string ###" + key + "### not found"; + } + return String.Format(returnValue, param); + } + } + + /// + /// This class contains the information about a language file + /// + public class LanguageFile : IEquatable + { + public string Description + { + get; + set; + } + + public string Ietf + { + get; + set; + } + + public Version Version + { + get; + set; + } + + public string LanguageGroup + { + get; + set; + } + + public string Filepath + { + get; + set; + } + + public string Prefix + { + get; + set; + } + + /// + /// Overload equals so we can delete a entry from a collection + /// + /// + /// + public bool Equals(LanguageFile other) + { + if (Prefix != null) + { + if (!Prefix.Equals(other.Prefix)) + { + return false; + } + } + else if (other.Prefix != null) + { + return false; + } + if (Ietf != null) + { + if (!Ietf.Equals(other.Ietf)) + { + return false; + } + } + else if (other.Ietf != null) + { + return false; + } + if (Version != null) + { + if (!Version.Equals(other.Version)) + { + return false; + } + } + else if (other.Version != null) + { + return false; + } + if (Filepath != null) + { + if (!Filepath.Equals(other.Filepath)) + { + return false; + } + } + else if (other.Filepath != null) + { + return false; + } + return true; + } + } +} \ No newline at end of file diff --git a/GreenshotImageEditor/Core/Objects.cs b/GreenshotImageEditor/Core/Objects.cs new file mode 100644 index 000000000..afd70b0c2 --- /dev/null +++ b/GreenshotImageEditor/Core/Objects.cs @@ -0,0 +1,100 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2013 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: http://getgreenshot.org/ + * The Greenshot project is hosted on Sourceforge: http://sourceforge.net/projects/greenshot/ + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using System; +using System.Collections.Generic; +using System.IO; +using System.Reflection; +using System.Runtime.Serialization; +using System.Runtime.Serialization.Formatters.Binary; + +namespace GreenshotPlugin.Core +{ + public static class Objects + { + /// + /// Perform a deep Copy of the object. + /// + /// The type of object being copied. + /// The object instance to copy. + /// The copied object. + public static T Clone(this T source) + { + if (!typeof(T).IsSerializable) + { + throw new ArgumentException("The type must be serializable.", "source"); + } + + // Don't serialize a null object, simply return the default for that object + if (ReferenceEquals(source, null)) + { + return default(T); + } + + IFormatter formatter = new BinaryFormatter(); + Stream stream = new MemoryStream(); + using (stream) + { + formatter.Serialize(stream, source); + stream.Seek(0, SeekOrigin.Begin); + return (T)formatter.Deserialize(stream); + } + } + + public static void CloneTo(this T source, T destination) + { + Type type = typeof(T); + FieldInfo[] myObjectFields = type.GetFields(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance); + + foreach (FieldInfo fi in myObjectFields) + { + fi.SetValue(destination, fi.GetValue(source)); + } + PropertyInfo[] myObjectProperties = type.GetProperties(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance); + + foreach (PropertyInfo pi in myObjectProperties) + { + if (pi.CanWrite) + { + pi.SetValue(destination, pi.GetValue(source, null), null); + } + } + } + + public static bool CompareLists(IList l1, IList l2) + { + if (l1.Count != l2.Count) + { + return false; + } + int matched = 0; + foreach (T item in l1) + { + if (!l2.Contains(item)) + { + return false; + } + matched++; + } + return matched == l1.Count; + } + } +} \ No newline at end of file diff --git a/GreenshotImageEditor/Core/QuantizerHelper.cs b/GreenshotImageEditor/Core/QuantizerHelper.cs new file mode 100644 index 000000000..5f4554b16 --- /dev/null +++ b/GreenshotImageEditor/Core/QuantizerHelper.cs @@ -0,0 +1,770 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2013 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: http://getgreenshot.org/ + * The Greenshot project is hosted on Sourceforge: http://sourceforge.net/projects/greenshot/ + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Drawing; +using System.Drawing.Imaging; + +namespace GreenshotPlugin.Core +{ + internal class WuColorCube + { + /// + /// Gets or sets the red minimum. + /// + /// The red minimum. + public Int32 RedMinimum { get; set; } + + /// + /// Gets or sets the red maximum. + /// + /// The red maximum. + public Int32 RedMaximum { get; set; } + + /// + /// Gets or sets the green minimum. + /// + /// The green minimum. + public Int32 GreenMinimum { get; set; } + + /// + /// Gets or sets the green maximum. + /// + /// The green maximum. + public Int32 GreenMaximum { get; set; } + + /// + /// Gets or sets the blue minimum. + /// + /// The blue minimum. + public Int32 BlueMinimum { get; set; } + + /// + /// Gets or sets the blue maximum. + /// + /// The blue maximum. + public Int32 BlueMaximum { get; set; } + + /// + /// Gets or sets the cube volume. + /// + /// The volume. + public Int32 Volume { get; set; } + } + + public class WuQuantizer : IDisposable + { + private const Int32 MAXCOLOR = 512; + private const Int32 RED = 2; + private const Int32 GREEN = 1; + private const Int32 BLUE = 0; + private const Int32 SIDESIZE = 33; + private const Int32 MAXSIDEINDEX = 32; + private const Int32 MAXVOLUME = SIDESIZE * SIDESIZE * SIDESIZE; + + // To count the colors + private int colorCount = 0; + + private Int32[] reds; + private Int32[] greens; + private Int32[] blues; + private Int32[] sums; + + private Int64[, ,] weights; + private Int64[, ,] momentsRed; + private Int64[, ,] momentsGreen; + private Int64[, ,] momentsBlue; + private Single[, ,] moments; + + private byte[] tag; + + private WuColorCube[] cubes; + private Bitmap sourceBitmap; + private Bitmap resultBitmap; + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) + { + if (disposing) + { + if (resultBitmap != null) + { + resultBitmap.Dispose(); + resultBitmap = null; + } + } + } + + /// + /// See for more details. + /// + public WuQuantizer(Bitmap sourceBitmap) + { + this.sourceBitmap = sourceBitmap; + // Make sure the color count variables are reset + BitArray bitArray = new BitArray((int)Math.Pow(2, 24)); + colorCount = 0; + + // creates all the cubes + cubes = new WuColorCube[MAXCOLOR]; + + // initializes all the cubes + for (Int32 cubeIndex = 0; cubeIndex < MAXCOLOR; cubeIndex++) + { + cubes[cubeIndex] = new WuColorCube(); + } + + // resets the reference minimums + cubes[0].RedMinimum = 0; + cubes[0].GreenMinimum = 0; + cubes[0].BlueMinimum = 0; + + // resets the reference maximums + cubes[0].RedMaximum = MAXSIDEINDEX; + cubes[0].GreenMaximum = MAXSIDEINDEX; + cubes[0].BlueMaximum = MAXSIDEINDEX; + + weights = new Int64[SIDESIZE, SIDESIZE, SIDESIZE]; + momentsRed = new Int64[SIDESIZE, SIDESIZE, SIDESIZE]; + momentsGreen = new Int64[SIDESIZE, SIDESIZE, SIDESIZE]; + momentsBlue = new Int64[SIDESIZE, SIDESIZE, SIDESIZE]; + moments = new Single[SIDESIZE, SIDESIZE, SIDESIZE]; + + Int32[] table = new Int32[256]; + + for (Int32 tableIndex = 0; tableIndex < 256; ++tableIndex) + { + table[tableIndex] = tableIndex * tableIndex; + } + + // Use a bitmap to store the initial match, which is just as good as an array and saves us 2x the storage + using (IFastBitmap sourceFastBitmap = FastBitmap.Create(sourceBitmap)) + { + IFastBitmapWithBlend sourceFastBitmapWithBlend = sourceFastBitmap as IFastBitmapWithBlend; + sourceFastBitmap.Lock(); + using (FastChunkyBitmap destinationFastBitmap = FastBitmap.CreateEmpty(sourceBitmap.Size, PixelFormat.Format8bppIndexed, Color.White) as FastChunkyBitmap) + { + destinationFastBitmap.Lock(); + for (int y = 0; y < sourceFastBitmap.Height; y++) + { + for (int x = 0; x < sourceFastBitmap.Width; x++) + { + Color color; + if (sourceFastBitmapWithBlend == null) + { + color = sourceFastBitmap.GetColorAt(x, y); + } + else + { + color = sourceFastBitmapWithBlend.GetBlendedColorAt(x, y); + } + // To count the colors + int index = color.ToArgb() & 0x00ffffff; + // Check if we already have this color + if (!bitArray.Get(index)) + { + // If not, add 1 to the single colors + colorCount++; + bitArray.Set(index, true); + } + + Int32 indexRed = (color.R >> 3) + 1; + Int32 indexGreen = (color.G >> 3) + 1; + Int32 indexBlue = (color.B >> 3) + 1; + + weights[indexRed, indexGreen, indexBlue]++; + momentsRed[indexRed, indexGreen, indexBlue] += color.R; + momentsGreen[indexRed, indexGreen, indexBlue] += color.G; + momentsBlue[indexRed, indexGreen, indexBlue] += color.B; + moments[indexRed, indexGreen, indexBlue] += table[color.R] + table[color.G] + table[color.B]; + + // Store the initial "match" + Int32 paletteIndex = (indexRed << 10) + (indexRed << 6) + indexRed + (indexGreen << 5) + indexGreen + indexBlue; + destinationFastBitmap.SetColorIndexAt(x, y, (byte)(paletteIndex & 0xff)); + } + } + resultBitmap = destinationFastBitmap.UnlockAndReturnBitmap(); + } + } + } + + /// + /// See for more details. + /// + public Int32 GetColorCount() + { + return colorCount; + } + + /// + /// Reindex the 24/32 BPP (A)RGB image to a 8BPP + /// + /// Bitmap + public Bitmap SimpleReindex() + { + List colors = new List(); + Dictionary lookup = new Dictionary(); + using (FastChunkyBitmap bbbDest = FastBitmap.Create(resultBitmap) as FastChunkyBitmap) + { + bbbDest.Lock(); + using (IFastBitmap bbbSrc = FastBitmap.Create(sourceBitmap)) + { + IFastBitmapWithBlend bbbSrcBlend = bbbSrc as IFastBitmapWithBlend; + + bbbSrc.Lock(); + byte index; + for (int y = 0; y < bbbSrc.Height; y++) + { + for (int x = 0; x < bbbSrc.Width; x++) + { + Color color; + if (bbbSrcBlend != null) + { + color = bbbSrcBlend.GetBlendedColorAt(x, y); + } + else + { + color = bbbSrc.GetColorAt(x, y); + } + if (lookup.ContainsKey(color)) + { + index = lookup[color]; + } + else + { + colors.Add(color); + index = (byte)(colors.Count - 1); + lookup.Add(color, index); + } + bbbDest.SetColorIndexAt(x, y, index); + } + } + } + } + + // generates palette + ColorPalette imagePalette = resultBitmap.Palette; + Color[] entries = imagePalette.Entries; + for (Int32 paletteIndex = 0; paletteIndex < 256; paletteIndex++) + { + if (paletteIndex < colorCount) + { + entries[paletteIndex] = colors[paletteIndex]; + } + else + { + entries[paletteIndex] = Color.Black; + } + } + resultBitmap.Palette = imagePalette; + + // Make sure the bitmap is not disposed, as we return it. + Bitmap tmpBitmap = resultBitmap; + resultBitmap = null; + return tmpBitmap; + } + + /// + /// Get the image + /// + public Bitmap GetQuantizedImage(int allowedColorCount) + { + if (allowedColorCount > 256) + { + throw new ArgumentOutOfRangeException("Quantizing muss be done to get less than 256 colors"); + } + if (colorCount < allowedColorCount) + { + // Simple logic to reduce to 8 bit + LOG.Info("Colors in the image are already less as whished for, using simple copy to indexed image, no quantizing needed!"); + return SimpleReindex(); + } + // preprocess the colors + CalculateMoments(); + LOG.Info("Calculated the moments..."); + Int32 next = 0; + Single[] volumeVariance = new Single[MAXCOLOR]; + + // processes the cubes + for (Int32 cubeIndex = 1; cubeIndex < allowedColorCount; ++cubeIndex) + { + // if cut is possible; make it + if (Cut(cubes[next], cubes[cubeIndex])) + { + volumeVariance[next] = cubes[next].Volume > 1 ? CalculateVariance(cubes[next]) : 0.0f; + volumeVariance[cubeIndex] = cubes[cubeIndex].Volume > 1 ? CalculateVariance(cubes[cubeIndex]) : 0.0f; + } + else + { + // the cut was not possible, revert the index + volumeVariance[next] = 0.0f; + cubeIndex--; + } + + next = 0; + Single temp = volumeVariance[0]; + + for (Int32 index = 1; index <= cubeIndex; ++index) + { + if (volumeVariance[index] > temp) + { + temp = volumeVariance[index]; + next = index; + } + } + + if (temp <= 0.0) + { + allowedColorCount = cubeIndex + 1; + break; + } + } + + Int32[] lookupRed = new Int32[MAXCOLOR]; + Int32[] lookupGreen = new Int32[MAXCOLOR]; + Int32[] lookupBlue = new Int32[MAXCOLOR]; + + tag = new byte[MAXVOLUME]; + + // precalculates lookup tables + for (int k = 0; k < allowedColorCount; ++k) + { + Mark(cubes[k], k, tag); + + long weight = Volume(cubes[k], weights); + + if (weight > 0) + { + lookupRed[k] = (int)(Volume(cubes[k], momentsRed) / weight); + lookupGreen[k] = (int)(Volume(cubes[k], momentsGreen) / weight); + lookupBlue[k] = (int)(Volume(cubes[k], momentsBlue) / weight); + } + else + { + lookupRed[k] = 0; + lookupGreen[k] = 0; + lookupBlue[k] = 0; + } + } + + reds = new Int32[allowedColorCount + 1]; + greens = new Int32[allowedColorCount + 1]; + blues = new Int32[allowedColorCount + 1]; + sums = new Int32[allowedColorCount + 1]; + + LOG.Info("Starting bitmap reconstruction..."); + + using (FastChunkyBitmap dest = FastBitmap.Create(resultBitmap) as FastChunkyBitmap) + { + using (IFastBitmap src = FastBitmap.Create(sourceBitmap)) + { + IFastBitmapWithBlend srcBlend = src as IFastBitmapWithBlend; + Dictionary lookup = new Dictionary(); + byte bestMatch; + for (int y = 0; y < src.Height; y++) + { + for (int x = 0; x < src.Width; x++) + { + Color color; + if (srcBlend != null) + { + // WithoutAlpha, this makes it possible to ignore the alpha + color = srcBlend.GetBlendedColorAt(x, y); + } + else + { + color = src.GetColorAt(x, y); + } + + // Check if we already matched the color + if (!lookup.ContainsKey(color)) + { + // If not we need to find the best match + + // First get initial match + bestMatch = dest.GetColorIndexAt(x, y); + bestMatch = tag[bestMatch]; + + Int32 bestDistance = 100000000; + for (int lookupIndex = 0; lookupIndex < allowedColorCount; lookupIndex++) + { + Int32 foundRed = lookupRed[lookupIndex]; + Int32 foundGreen = lookupGreen[lookupIndex]; + Int32 foundBlue = lookupBlue[lookupIndex]; + Int32 deltaRed = color.R - foundRed; + Int32 deltaGreen = color.G - foundGreen; + Int32 deltaBlue = color.B - foundBlue; + + Int32 distance = deltaRed * deltaRed + deltaGreen * deltaGreen + deltaBlue * deltaBlue; + + if (distance < bestDistance) + { + bestDistance = distance; + bestMatch = (byte)lookupIndex; + } + } + lookup.Add(color, bestMatch); + } + else + { + // Already matched, so we just use the lookup + bestMatch = lookup[color]; + } + + reds[bestMatch] += color.R; + greens[bestMatch] += color.G; + blues[bestMatch] += color.B; + sums[bestMatch]++; + + dest.SetColorIndexAt(x, y, bestMatch); + } + } + } + } + + // generates palette + ColorPalette imagePalette = resultBitmap.Palette; + Color[] entries = imagePalette.Entries; + for (Int32 paletteIndex = 0; paletteIndex < allowedColorCount; paletteIndex++) + { + if (sums[paletteIndex] > 0) + { + reds[paletteIndex] /= sums[paletteIndex]; + greens[paletteIndex] /= sums[paletteIndex]; + blues[paletteIndex] /= sums[paletteIndex]; + } + + entries[paletteIndex] = Color.FromArgb(255, reds[paletteIndex], greens[paletteIndex], blues[paletteIndex]); + } + resultBitmap.Palette = imagePalette; + + // Make sure the bitmap is not disposed, as we return it. + Bitmap tmpBitmap = resultBitmap; + resultBitmap = null; + return tmpBitmap; + } + + /// + /// Converts the histogram to a series of moments. + /// + private void CalculateMoments() + { + Int64[] area = new Int64[SIDESIZE]; + Int64[] areaRed = new Int64[SIDESIZE]; + Int64[] areaGreen = new Int64[SIDESIZE]; + Int64[] areaBlue = new Int64[SIDESIZE]; + Single[] area2 = new Single[SIDESIZE]; + + for (Int32 redIndex = 1; redIndex <= MAXSIDEINDEX; ++redIndex) + { + for (Int32 index = 0; index <= MAXSIDEINDEX; ++index) + { + area[index] = 0; + areaRed[index] = 0; + areaGreen[index] = 0; + areaBlue[index] = 0; + area2[index] = 0; + } + + for (Int32 greenIndex = 1; greenIndex <= MAXSIDEINDEX; ++greenIndex) + { + Int64 line = 0; + Int64 lineRed = 0; + Int64 lineGreen = 0; + Int64 lineBlue = 0; + Single line2 = 0.0f; + + for (Int32 blueIndex = 1; blueIndex <= MAXSIDEINDEX; ++blueIndex) + { + line += weights[redIndex, greenIndex, blueIndex]; + lineRed += momentsRed[redIndex, greenIndex, blueIndex]; + lineGreen += momentsGreen[redIndex, greenIndex, blueIndex]; + lineBlue += momentsBlue[redIndex, greenIndex, blueIndex]; + line2 += moments[redIndex, greenIndex, blueIndex]; + + area[blueIndex] += line; + areaRed[blueIndex] += lineRed; + areaGreen[blueIndex] += lineGreen; + areaBlue[blueIndex] += lineBlue; + area2[blueIndex] += line2; + + weights[redIndex, greenIndex, blueIndex] = weights[redIndex - 1, greenIndex, blueIndex] + area[blueIndex]; + momentsRed[redIndex, greenIndex, blueIndex] = momentsRed[redIndex - 1, greenIndex, blueIndex] + areaRed[blueIndex]; + momentsGreen[redIndex, greenIndex, blueIndex] = momentsGreen[redIndex - 1, greenIndex, blueIndex] + areaGreen[blueIndex]; + momentsBlue[redIndex, greenIndex, blueIndex] = momentsBlue[redIndex - 1, greenIndex, blueIndex] + areaBlue[blueIndex]; + moments[redIndex, greenIndex, blueIndex] = moments[redIndex - 1, greenIndex, blueIndex] + area2[blueIndex]; + } + } + } + } + + /// + /// Computes the volume of the cube in a specific moment. + /// + private static Int64 Volume(WuColorCube cube, Int64[, ,] moment) + { + return moment[cube.RedMaximum, cube.GreenMaximum, cube.BlueMaximum] - + moment[cube.RedMaximum, cube.GreenMaximum, cube.BlueMinimum] - + moment[cube.RedMaximum, cube.GreenMinimum, cube.BlueMaximum] + + moment[cube.RedMaximum, cube.GreenMinimum, cube.BlueMinimum] - + moment[cube.RedMinimum, cube.GreenMaximum, cube.BlueMaximum] + + moment[cube.RedMinimum, cube.GreenMaximum, cube.BlueMinimum] + + moment[cube.RedMinimum, cube.GreenMinimum, cube.BlueMaximum] - + moment[cube.RedMinimum, cube.GreenMinimum, cube.BlueMinimum]; + } + + /// + /// Computes the volume of the cube in a specific moment. For the floating-point values. + /// + private static Single VolumeFloat(WuColorCube cube, Single[, ,] moment) + { + return moment[cube.RedMaximum, cube.GreenMaximum, cube.BlueMaximum] - + moment[cube.RedMaximum, cube.GreenMaximum, cube.BlueMinimum] - + moment[cube.RedMaximum, cube.GreenMinimum, cube.BlueMaximum] + + moment[cube.RedMaximum, cube.GreenMinimum, cube.BlueMinimum] - + moment[cube.RedMinimum, cube.GreenMaximum, cube.BlueMaximum] + + moment[cube.RedMinimum, cube.GreenMaximum, cube.BlueMinimum] + + moment[cube.RedMinimum, cube.GreenMinimum, cube.BlueMaximum] - + moment[cube.RedMinimum, cube.GreenMinimum, cube.BlueMinimum]; + } + + /// + /// Splits the cube in given position, and color direction. + /// + private static Int64 Top(WuColorCube cube, Int32 direction, Int32 position, Int64[, ,] moment) + { + switch (direction) + { + case RED: + return (moment[position, cube.GreenMaximum, cube.BlueMaximum] - + moment[position, cube.GreenMaximum, cube.BlueMinimum] - + moment[position, cube.GreenMinimum, cube.BlueMaximum] + + moment[position, cube.GreenMinimum, cube.BlueMinimum]); + + case GREEN: + return (moment[cube.RedMaximum, position, cube.BlueMaximum] - + moment[cube.RedMaximum, position, cube.BlueMinimum] - + moment[cube.RedMinimum, position, cube.BlueMaximum] + + moment[cube.RedMinimum, position, cube.BlueMinimum]); + + case BLUE: + return (moment[cube.RedMaximum, cube.GreenMaximum, position] - + moment[cube.RedMaximum, cube.GreenMinimum, position] - + moment[cube.RedMinimum, cube.GreenMaximum, position] + + moment[cube.RedMinimum, cube.GreenMinimum, position]); + + default: + return 0; + } + } + + /// + /// Splits the cube in a given color direction at its minimum. + /// + private static Int64 Bottom(WuColorCube cube, Int32 direction, Int64[, ,] moment) + { + switch (direction) + { + case RED: + return (-moment[cube.RedMinimum, cube.GreenMaximum, cube.BlueMaximum] + + moment[cube.RedMinimum, cube.GreenMaximum, cube.BlueMinimum] + + moment[cube.RedMinimum, cube.GreenMinimum, cube.BlueMaximum] - + moment[cube.RedMinimum, cube.GreenMinimum, cube.BlueMinimum]); + + case GREEN: + return (-moment[cube.RedMaximum, cube.GreenMinimum, cube.BlueMaximum] + + moment[cube.RedMaximum, cube.GreenMinimum, cube.BlueMinimum] + + moment[cube.RedMinimum, cube.GreenMinimum, cube.BlueMaximum] - + moment[cube.RedMinimum, cube.GreenMinimum, cube.BlueMinimum]); + + case BLUE: + return (-moment[cube.RedMaximum, cube.GreenMaximum, cube.BlueMinimum] + + moment[cube.RedMaximum, cube.GreenMinimum, cube.BlueMinimum] + + moment[cube.RedMinimum, cube.GreenMaximum, cube.BlueMinimum] - + moment[cube.RedMinimum, cube.GreenMinimum, cube.BlueMinimum]); + default: + return 0; + } + } + + /// + /// Calculates statistical variance for a given cube. + /// + private Single CalculateVariance(WuColorCube cube) + { + Single volumeRed = Volume(cube, momentsRed); + Single volumeGreen = Volume(cube, momentsGreen); + Single volumeBlue = Volume(cube, momentsBlue); + Single volumeMoment = VolumeFloat(cube, moments); + Single volumeWeight = Volume(cube, weights); + + Single distance = volumeRed * volumeRed + volumeGreen * volumeGreen + volumeBlue * volumeBlue; + + return volumeMoment - (distance / volumeWeight); + } + + /// + /// Finds the optimal (maximal) position for the cut. + /// + private Single Maximize(WuColorCube cube, Int32 direction, Int32 first, Int32 last, Int32[] cut, Int64 wholeRed, Int64 wholeGreen, Int64 wholeBlue, Int64 wholeWeight) + { + Int64 bottomRed = Bottom(cube, direction, momentsRed); + Int64 bottomGreen = Bottom(cube, direction, momentsGreen); + Int64 bottomBlue = Bottom(cube, direction, momentsBlue); + Int64 bottomWeight = Bottom(cube, direction, weights); + + Single result = 0.0f; + cut[0] = -1; + + for (Int32 position = first; position < last; ++position) + { + // determines the cube cut at a certain position + Int64 halfRed = bottomRed + Top(cube, direction, position, momentsRed); + Int64 halfGreen = bottomGreen + Top(cube, direction, position, momentsGreen); + Int64 halfBlue = bottomBlue + Top(cube, direction, position, momentsBlue); + Int64 halfWeight = bottomWeight + Top(cube, direction, position, weights); + + // the cube cannot be cut at bottom (this would lead to empty cube) + if (halfWeight != 0) + { + Single halfDistance = halfRed * halfRed + halfGreen * halfGreen + halfBlue * halfBlue; + Single temp = halfDistance / halfWeight; + + halfRed = wholeRed - halfRed; + halfGreen = wholeGreen - halfGreen; + halfBlue = wholeBlue - halfBlue; + halfWeight = wholeWeight - halfWeight; + + if (halfWeight != 0) + { + halfDistance = halfRed * halfRed + halfGreen * halfGreen + halfBlue * halfBlue; + temp += halfDistance / halfWeight; + + if (temp > result) + { + result = temp; + cut[0] = position; + } + } + } + } + + return result; + } + + /// + /// Cuts a cube with another one. + /// + private Boolean Cut(WuColorCube first, WuColorCube second) + { + Int32 direction; + + Int32[] cutRed = { 0 }; + Int32[] cutGreen = { 0 }; + Int32[] cutBlue = { 0 }; + + Int64 wholeRed = Volume(first, momentsRed); + Int64 wholeGreen = Volume(first, momentsGreen); + Int64 wholeBlue = Volume(first, momentsBlue); + Int64 wholeWeight = Volume(first, weights); + + Single maxRed = Maximize(first, RED, first.RedMinimum + 1, first.RedMaximum, cutRed, wholeRed, wholeGreen, wholeBlue, wholeWeight); + Single maxGreen = Maximize(first, GREEN, first.GreenMinimum + 1, first.GreenMaximum, cutGreen, wholeRed, wholeGreen, wholeBlue, wholeWeight); + Single maxBlue = Maximize(first, BLUE, first.BlueMinimum + 1, first.BlueMaximum, cutBlue, wholeRed, wholeGreen, wholeBlue, wholeWeight); + + if ((maxRed >= maxGreen) && (maxRed >= maxBlue)) + { + direction = RED; + + // cannot split empty cube + if (cutRed[0] < 0) return false; + } + else + { + if ((maxGreen >= maxRed) && (maxGreen >= maxBlue)) + { + direction = GREEN; + } + else + { + direction = BLUE; + } + } + + second.RedMaximum = first.RedMaximum; + second.GreenMaximum = first.GreenMaximum; + second.BlueMaximum = first.BlueMaximum; + + // cuts in a certain direction + switch (direction) + { + case RED: + second.RedMinimum = first.RedMaximum = cutRed[0]; + second.GreenMinimum = first.GreenMinimum; + second.BlueMinimum = first.BlueMinimum; + break; + + case GREEN: + second.GreenMinimum = first.GreenMaximum = cutGreen[0]; + second.RedMinimum = first.RedMinimum; + second.BlueMinimum = first.BlueMinimum; + break; + + case BLUE: + second.BlueMinimum = first.BlueMaximum = cutBlue[0]; + second.RedMinimum = first.RedMinimum; + second.GreenMinimum = first.GreenMinimum; + break; + } + + // determines the volumes after cut + first.Volume = (first.RedMaximum - first.RedMinimum) * (first.GreenMaximum - first.GreenMinimum) * (first.BlueMaximum - first.BlueMinimum); + second.Volume = (second.RedMaximum - second.RedMinimum) * (second.GreenMaximum - second.GreenMinimum) * (second.BlueMaximum - second.BlueMinimum); + + // the cut was successfull + return true; + } + + /// + /// Marks all the tags with a given label. + /// + private void Mark(WuColorCube cube, Int32 label, byte[] tag) + { + for (Int32 redIndex = cube.RedMinimum + 1; redIndex <= cube.RedMaximum; ++redIndex) + { + for (Int32 greenIndex = cube.GreenMinimum + 1; greenIndex <= cube.GreenMaximum; ++greenIndex) + { + for (Int32 blueIndex = cube.BlueMinimum + 1; blueIndex <= cube.BlueMaximum; ++blueIndex) + { + tag[(redIndex << 10) + (redIndex << 6) + redIndex + (greenIndex << 5) + greenIndex + blueIndex] = (byte)label; + } + } + } + } + } +} \ No newline at end of file diff --git a/GreenshotImageEditor/Core/WindowCapture.cs b/GreenshotImageEditor/Core/WindowCapture.cs new file mode 100644 index 000000000..0414c8d47 --- /dev/null +++ b/GreenshotImageEditor/Core/WindowCapture.cs @@ -0,0 +1,906 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2013 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: http://getgreenshot.org/ + * The Greenshot project is hosted on Sourceforge: http://sourceforge.net/projects/greenshot/ + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using Greenshot.IniFile; +using Greenshot.Plugin; +using GreenshotPlugin.UnmanagedHelpers; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Drawing; +using System.Drawing.Imaging; +using System.Runtime.InteropServices; +using System.Windows.Forms; + +namespace GreenshotPlugin.Core +{ + /// + /// This Class is used to pass details about the capture around. + /// The time the Capture was taken and the Title of the window (or a region of) that is captured + /// + public class CaptureDetails : ICaptureDetails + { + private string title; + public string Title + { + get { return title; } + set { title = value; } + } + + private string filename; + public string Filename + { + get { return filename; } + set { filename = value; } + } + + private DateTime dateTime; + public DateTime DateTime + { + get { return dateTime; } + set { dateTime = value; } + } + + private float dpiX; + public float DpiX + { + get + { + return dpiX; + } + set + { + dpiX = value; + } + } + + private float dpiY; + public float DpiY + { + get + { + return dpiY; + } + set + { + dpiY = value; + } + } + private Dictionary metaData = new Dictionary(); + public Dictionary MetaData + { + get { return metaData; } + } + + public void AddMetaData(string key, string value) + { + if (metaData.ContainsKey(key)) + { + metaData[key] = value; + } + else + { + metaData.Add(key, value); + } + } + + private CaptureMode captureMode; + public CaptureMode CaptureMode + { + get { return captureMode; } + set { captureMode = value; } + } + + private List captureDestinations = new List(); + public List CaptureDestinations + { + get { return captureDestinations; } + set { captureDestinations = value; } + } + + public void ClearDestinations() + { + captureDestinations.Clear(); + } + + public void RemoveDestination(IDestination destination) + { + if (captureDestinations.Contains(destination)) + { + captureDestinations.Remove(destination); + } + } + + public void AddDestination(IDestination captureDestination) + { + if (!captureDestinations.Contains(captureDestination)) + { + captureDestinations.Add(captureDestination); + } + } + + public bool HasDestination(string designation) + { + foreach (IDestination destination in captureDestinations) + { + if (designation.Equals(destination.Designation)) + { + return true; + } + } + return false; + } + + public CaptureDetails() + { + dateTime = DateTime.Now; + } + } + + /// + /// This class is used to pass an instance of the "Capture" around + /// Having the Bitmap, eventually the Windows Title and cursor all together. + /// + public class Capture : IDisposable, ICapture + { + private List elements = new List(); + + private Rectangle screenBounds; + /// + /// Get/Set the Screenbounds + /// + public Rectangle ScreenBounds + { + get + { + if (screenBounds == null) + { + screenBounds = WindowCapture.GetScreenBounds(); + } + return screenBounds; + } + set { screenBounds = value; } + } + + private Image image; + /// + /// Get/Set the Image + /// + public Image Image + { + get { return image; } + set + { + if (image != null) + { + image.Dispose(); + } + image = value; + if (value != null) + { + if (value.PixelFormat.Equals(PixelFormat.Format8bppIndexed) || value.PixelFormat.Equals(PixelFormat.Format1bppIndexed) || value.PixelFormat.Equals(PixelFormat.Format4bppIndexed)) + { + LOG.Debug("Converting Bitmap to PixelFormat.Format32bppArgb as we don't support: " + value.PixelFormat); + try + { + // Default Bitmap PixelFormat is Format32bppArgb + image = new Bitmap(value); + } + finally + { + // Always dispose, even when a exception occured + value.Dispose(); + } + } + LOG.DebugFormat("Image is set with the following specifications: {0} - {1}", image.Size, image.PixelFormat); + } + else + { + LOG.Debug("Image is removed."); + } + } + } + + public void NullImage() + { + image = null; + } + + private Icon cursor; + /// + /// Get/Set the image for the Cursor + /// + public Icon Cursor + { + get { return cursor; } + set + { + if (cursor != null) + { + cursor.Dispose(); + } + cursor = (Icon)value.Clone(); + } + } + + private bool cursorVisible = false; + /// + /// Set if the cursor is visible + /// + public bool CursorVisible + { + get { return cursorVisible; } + set { cursorVisible = value; } + } + + private Point cursorLocation = Point.Empty; + /// + /// Get/Set the CursorLocation + /// + public Point CursorLocation + { + get { return cursorLocation; } + set { cursorLocation = value; } + } + + private Point location = Point.Empty; + /// + /// Get/set the Location + /// + public Point Location + { + get { return location; } + set { location = value; } + } + + private CaptureDetails captureDetails; + /// + /// Get/set the CaptureDetails + /// + public ICaptureDetails CaptureDetails + { + get { return captureDetails; } + set { captureDetails = (CaptureDetails)value; } + } + + /// + /// Default Constructor + /// + public Capture() + { + screenBounds = WindowCapture.GetScreenBounds(); + captureDetails = new CaptureDetails(); + } + + /// + /// Constructor with Image + /// Note: the supplied bitmap can be disposed immediately or when constructor is called. + /// + /// Image + public Capture(Image newImage) + : this() + { + Image = newImage; + } + + /// + /// Destructor + /// + ~Capture() + { + Dispose(false); + } + + /// + /// The public accessible Dispose + /// Will call the GarbageCollector to SuppressFinalize, preventing being cleaned twice + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + /// + /// This Dispose is called from the Dispose and the Destructor. + /// When disposing==true all non-managed resources should be freed too! + /// + /// + protected virtual void Dispose(bool disposing) + { + if (disposing) + { + if (image != null) + { + image.Dispose(); + } + if (cursor != null) + { + cursor.Dispose(); + } + } + image = null; + cursor = null; + } + + /// + /// Crops the capture to the specified rectangle (with Bitmap coordinates!) + /// + /// Rectangle with bitmap coordinates + public bool Crop(Rectangle cropRectangle) + { + LOG.Debug("Cropping to: " + cropRectangle.ToString()); + if (ImageHelper.Crop(ref image, ref cropRectangle)) + { + location = cropRectangle.Location; + // Change mouse location according to the cropRegtangle (including screenbounds) offset + MoveMouseLocation(-cropRectangle.Location.X, -cropRectangle.Location.Y); + // Move all the elements + // TODO: Enable when the elements are usable again. + // MoveElements(-cropRectangle.Location.X, -cropRectangle.Location.Y); + + // Remove invisible elements + List newElements = new List(); + foreach (ICaptureElement captureElement in elements) + { + if (captureElement.Bounds.IntersectsWith(cropRectangle)) + { + newElements.Add(captureElement); + } + } + elements = newElements; + + return true; + } + return false; + } + + /// + /// Apply a translate to the mouse location. + /// e.g. needed for crop + /// + /// x coordinates to move the mouse + /// y coordinates to move the mouse + public void MoveMouseLocation(int x, int y) + { + cursorLocation.Offset(x, y); + } + + // TODO: Enable when the elements are usable again. + ///// + ///// Apply a translate to the elements + ///// e.g. needed for crop + ///// + ///// x coordinates to move the elements + ///// y coordinates to move the elements + //public void MoveElements(int x, int y) { + // MoveElements(elements, x, y); + //} + + //private void MoveElements(List listOfElements, int x, int y) { + // foreach(ICaptureElement childElement in listOfElements) { + // Rectangle bounds = childElement.Bounds; + // bounds.Offset(x, y); + // childElement.Bounds = bounds; + // MoveElements(childElement.Children, x, y); + // } + //} + + ///// + ///// Add a new element to the capture + ///// + ///// CaptureElement + //public void AddElement(ICaptureElement element) { + // int match = elements.IndexOf(element); + // if (match >= 0) { + // if (elements[match].Children.Count < element.Children.Count) { + // elements.RemoveAt(match); + // elements.Add(element); + // } + // } else { + // elements.Add(element); + // } + //} + + ///// + ///// Returns a list of rectangles which represent object that are on the capture + ///// + //public List Elements { + // get { + // return elements; + // } + // set { + // elements = value; + // } + //} + } + + /// + /// A class representing an element in the capture + /// + public class CaptureElement : ICaptureElement + { + public CaptureElement(Rectangle bounds) + { + Bounds = bounds; + } + + public CaptureElement(string name) + { + Name = name; + } + + public CaptureElement(string name, Rectangle bounds) + { + Name = name; + Bounds = bounds; + } + + private List children = new List(); + public List Children + { + get + { + return children; + } + set + { + children = value; + } + } + + public string Name + { + get; + set; + } + public Rectangle Bounds + { + get; + set; + } + + // CaptureElements are regarded equal if their bounds are equal. this should be sufficient. + public override bool Equals(object obj) + { + bool ret = false; + if (obj != null && GetType().Equals(obj.GetType())) + { + CaptureElement other = obj as CaptureElement; + if (Bounds.Equals(other.Bounds)) + { + ret = true; + } + } + return ret; + } + + public override int GetHashCode() + { + return Bounds.GetHashCode(); + } + } + + /// + /// The Window Capture code + /// + public class WindowCapture + { + private static CoreConfiguration conf = IniConfig.GetIniSection(); + + /// + /// Used to cleanup the unmanged resource in the iconInfo for the CaptureCursor method + /// + /// + /// + [DllImport("gdi32", SetLastError = true)] + private static extern bool DeleteObject(IntPtr hObject); + + private WindowCapture() + { + } + + /// + /// Get the bounds of all screens combined. + /// + /// A Rectangle of the bounds of the entire display area. + public static Rectangle GetScreenBounds() + { + int left = 0, top = 0, bottom = 0, right = 0; + foreach (Screen screen in Screen.AllScreens) + { + left = Math.Min(left, screen.Bounds.X); + top = Math.Min(top, screen.Bounds.Y); + int screenAbsRight = screen.Bounds.X + screen.Bounds.Width; + int screenAbsBottom = screen.Bounds.Y + screen.Bounds.Height; + right = Math.Max(right, screenAbsRight); + bottom = Math.Max(bottom, screenAbsBottom); + } + return new Rectangle(left, top, (right + Math.Abs(left)), (bottom + Math.Abs(top))); + } + + /// + /// Retrieves the cursor location safely, accounting for DPI settings in Vista/Windows 7. + /// Point with cursor location, relative to the origin of the monitor setup (i.e. negative coordinates are + /// possible in multiscreen setups) + public static Point GetCursorLocation() + { + if (Environment.OSVersion.Version.Major >= 6) + { + POINT cursorLocation; + if (User32.GetPhysicalCursorPos(out cursorLocation)) + { + return new Point(cursorLocation.X, cursorLocation.Y); + } + else + { + Win32Error error = Win32.GetLastErrorCode(); + LOG.ErrorFormat("Error retrieving PhysicalCursorPos : {0}", Win32.GetMessage(error)); + } + } + return new Point(Cursor.Position.X, Cursor.Position.Y); + } + + /// + /// Retrieves the cursor location safely, accounting for DPI settings in Vista/Windows 7. This implementation + /// can conveniently be used when the cursor location is needed to deal with a fullscreen bitmap. + /// Point with cursor location, relative to the top left corner of the monitor setup (which itself might + /// actually not be on any screen) + public static Point GetCursorLocationRelativeToScreenBounds() + { + return GetLocationRelativeToScreenBounds(GetCursorLocation()); + } + + /// + /// Converts locationRelativeToScreenOrigin to be relative to top left corner of all screen bounds, which might + /// be different in multiscreen setups. This implementation + /// can conveniently be used when the cursor location is needed to deal with a fullscreen bitmap. + /// + /// + /// + public static Point GetLocationRelativeToScreenBounds(Point locationRelativeToScreenOrigin) + { + Point ret = locationRelativeToScreenOrigin; + Rectangle bounds = GetScreenBounds(); + ret.Offset(-bounds.X, -bounds.Y); + return ret; + } + + /// + /// This method will capture the current Cursor by using User32 Code + /// + /// A Capture Object with the Mouse Cursor information in it. + public static ICapture CaptureCursor(ICapture capture) + { + LOG.Debug("Capturing the mouse cursor."); + if (capture == null) + { + capture = new Capture(); + } + int x, y; + CursorInfo cursorInfo = new CursorInfo(); + IconInfo iconInfo; + cursorInfo.cbSize = Marshal.SizeOf(cursorInfo); + if (User32.GetCursorInfo(out cursorInfo)) + { + if (cursorInfo.flags == User32.CURSOR_SHOWING) + { + using (SafeIconHandle safeIcon = User32.CopyIcon(cursorInfo.hCursor)) + { + if (User32.GetIconInfo(safeIcon, out iconInfo)) + { + Point cursorLocation = GetCursorLocation(); + // Allign cursor location to Bitmap coordinates (instead of Screen coordinates) + x = cursorLocation.X - iconInfo.xHotspot - capture.ScreenBounds.X; + y = cursorLocation.Y - iconInfo.yHotspot - capture.ScreenBounds.Y; + // Set the location + capture.CursorLocation = new Point(x, y); + + using (Icon icon = Icon.FromHandle(safeIcon.DangerousGetHandle())) + { + capture.Cursor = icon; + } + + if (iconInfo.hbmMask != IntPtr.Zero) + { + DeleteObject(iconInfo.hbmMask); + } + if (iconInfo.hbmColor != IntPtr.Zero) + { + DeleteObject(iconInfo.hbmColor); + } + } + } + } + } + return capture; + } + + /// + /// This method will call the CaptureRectangle with the screenbounds, therefor Capturing the whole screen. + /// + /// A Capture Object with the Screen as an Image + public static ICapture CaptureScreen(ICapture capture) + { + if (capture == null) + { + capture = new Capture(); + } + return CaptureRectangle(capture, capture.ScreenBounds); + } + + /// + /// Helper method to create an exception that might explain what is wrong while capturing + /// + /// string with current method + /// ICapture + /// Rectangle of what we want to capture + /// + private static Exception CreateCaptureException(string method, Rectangle captureBounds) + { + Exception exceptionToThrow = User32.CreateWin32Exception(method); + if (!captureBounds.IsEmpty) + { + exceptionToThrow.Data.Add("Height", captureBounds.Height); + exceptionToThrow.Data.Add("Width", captureBounds.Width); + } + return exceptionToThrow; + } + + /// + /// Helper method to check if it is allowed to capture the process using DWM + /// + /// Process owning the window + /// true if it's allowed + public static bool isDWMAllowed(Process process) + { + if (process != null) + { + if (conf.NoDWMCaptureForProduct != null && conf.NoDWMCaptureForProduct.Count > 0) + { + try + { + string productName = process.MainModule.FileVersionInfo.ProductName; + if (productName != null && conf.NoDWMCaptureForProduct.Contains(productName.ToLower())) + { + return false; + } + } + catch (Exception ex) + { + LOG.Warn(ex.Message); + } + } + } + return true; + } + + /// + /// Helper method to check if it is allowed to capture the process using GDI + /// + /// Process owning the window + /// true if it's allowed + public static bool isGDIAllowed(Process process) + { + if (process != null) + { + if (conf.NoGDICaptureForProduct != null && conf.NoGDICaptureForProduct.Count > 0) + { + try + { + string productName = process.MainModule.FileVersionInfo.ProductName; + if (productName != null && conf.NoGDICaptureForProduct.Contains(productName.ToLower())) + { + return false; + } + } + catch (Exception ex) + { + LOG.Warn(ex.Message); + } + } + } + return true; + } + + /// + /// This method will use User32 code to capture the specified captureBounds from the screen + /// + /// ICapture where the captured Bitmap will be stored + /// Rectangle with the bounds to capture + /// A Capture Object with a part of the Screen as an Image + public static ICapture CaptureRectangle(ICapture capture, Rectangle captureBounds) + { + if (capture == null) + { + capture = new Capture(); + } + capture.Image = CaptureRectangle(captureBounds); + capture.Location = captureBounds.Location; + if (capture.CaptureDetails != null) + { + ((Bitmap)capture.Image).SetResolution(capture.CaptureDetails.DpiX, capture.CaptureDetails.DpiY); + } + if (capture.Image == null) + { + return null; + } + return capture; + } + + /// + /// This method will use User32 code to capture the specified captureBounds from the screen + /// + /// Rectangle with the bounds to capture + /// Bitmap which is captured from the screen at the location specified by the captureBounds + public static Bitmap CaptureRectangle(Rectangle captureBounds) + { + Bitmap returnBitmap = null; + if (captureBounds.Height <= 0 || captureBounds.Width <= 0) + { + LOG.Warn("Nothing to capture, ignoring!"); + return null; + } + else + { + LOG.Debug("CaptureRectangle Called!"); + } + // .NET GDI+ Solution, according to some post this has a GDI+ leak... + // See http://connect.microsoft.com/VisualStudio/feedback/details/344752/gdi-object-leak-when-calling-graphics-copyfromscreen + // Bitmap capturedBitmap = new Bitmap(captureBounds.Width, captureBounds.Height); + // using (Graphics graphics = Graphics.FromImage(capturedBitmap)) { + // graphics.CopyFromScreen(captureBounds.Location, Point.Empty, captureBounds.Size, CopyPixelOperation.CaptureBlt); + // } + // capture.Image = capturedBitmap; + // capture.Location = captureBounds.Location; + + using (SafeWindowDCHandle desktopDCHandle = SafeWindowDCHandle.fromDesktop()) + { + if (desktopDCHandle.IsInvalid) + { + // Get Exception before the error is lost + Exception exceptionToThrow = CreateCaptureException("desktopDCHandle", captureBounds); + // throw exception + throw exceptionToThrow; + } + + // create a device context we can copy to + using (SafeCompatibleDCHandle safeCompatibleDCHandle = GDI32.CreateCompatibleDC(desktopDCHandle)) + { + // Check if the device context is there, if not throw an error with as much info as possible! + if (safeCompatibleDCHandle.IsInvalid) + { + // Get Exception before the error is lost + Exception exceptionToThrow = CreateCaptureException("CreateCompatibleDC", captureBounds); + // throw exception + throw exceptionToThrow; + } + // Create BitmapInfoHeader for CreateDIBSection + BitmapInfoHeader bmi = new BitmapInfoHeader(captureBounds.Width, captureBounds.Height, 24); + + // Make sure the last error is set to 0 + Win32.SetLastError(0); + + // create a bitmap we can copy it to, using GetDeviceCaps to get the width/height + IntPtr bits0; // not used for our purposes. It returns a pointer to the raw bits that make up the bitmap. + using (SafeDibSectionHandle safeDibSectionHandle = GDI32.CreateDIBSection(desktopDCHandle, ref bmi, BitmapInfoHeader.DIB_RGB_COLORS, out bits0, IntPtr.Zero, 0)) + { + if (safeDibSectionHandle.IsInvalid) + { + // Get Exception before the error is lost + Exception exceptionToThrow = CreateCaptureException("CreateDIBSection", captureBounds); + exceptionToThrow.Data.Add("hdcDest", safeCompatibleDCHandle.DangerousGetHandle().ToInt32()); + exceptionToThrow.Data.Add("hdcSrc", desktopDCHandle.DangerousGetHandle().ToInt32()); + + // Throw so people can report the problem + throw exceptionToThrow; + } + else + { + // select the bitmap object and store the old handle + using (SafeSelectObjectHandle selectObject = safeCompatibleDCHandle.SelectObject(safeDibSectionHandle)) + { + // bitblt over (make copy) + GDI32.BitBlt(safeCompatibleDCHandle, 0, 0, captureBounds.Width, captureBounds.Height, desktopDCHandle, captureBounds.X, captureBounds.Y, CopyPixelOperation.SourceCopy | CopyPixelOperation.CaptureBlt); + } + + // get a .NET image object for it + // A suggestion for the "A generic error occurred in GDI+." E_FAIL/080004005 error is to re-try... + bool success = false; + ExternalException exception = null; + for (int i = 0; i < 3; i++) + { + try + { + // Collect all screens inside this capture + List screensInsideCapture = new List(); + foreach (Screen screen in Screen.AllScreens) + { + if (screen.Bounds.IntersectsWith(captureBounds)) + { + screensInsideCapture.Add(screen); + } + } + // Check all all screens are of an equal size + bool offscreenContent = false; + using (Region captureRegion = new Region(captureBounds)) + { + // Exclude every visible part + foreach (Screen screen in screensInsideCapture) + { + captureRegion.Exclude(screen.Bounds); + } + // If the region is not empty, we have "offscreenContent" + using (Graphics screenGraphics = Graphics.FromHwnd(User32.GetDesktopWindow())) + { + offscreenContent = !captureRegion.IsEmpty(screenGraphics); + } + } + // Check if we need to have a transparent background, needed for offscreen content + if (offscreenContent) + { + using (Bitmap tmpBitmap = Bitmap.FromHbitmap(safeDibSectionHandle.DangerousGetHandle())) + { + // Create a new bitmap which has a transparent background + returnBitmap = ImageHelper.CreateEmpty(tmpBitmap.Width, tmpBitmap.Height, PixelFormat.Format32bppArgb, Color.Transparent, tmpBitmap.HorizontalResolution, tmpBitmap.VerticalResolution); + // Content will be copied here + using (Graphics graphics = Graphics.FromImage(returnBitmap)) + { + // For all screens copy the content to the new bitmap + foreach (Screen screen in Screen.AllScreens) + { + Rectangle screenBounds = screen.Bounds; + // Make sure the bounds are offsetted to the capture bounds + screenBounds.Offset(-captureBounds.X, -captureBounds.Y); + graphics.DrawImage(tmpBitmap, screenBounds, screenBounds.X, screenBounds.Y, screenBounds.Width, screenBounds.Height, GraphicsUnit.Pixel); + } + } + } + } + else + { + // All screens, which are inside the capture, are of equal size + // assign image to Capture, the image will be disposed there.. + returnBitmap = Bitmap.FromHbitmap(safeDibSectionHandle.DangerousGetHandle()); + } + // We got through the capture without exception + success = true; + break; + } + catch (ExternalException ee) + { + LOG.Warn("Problem getting bitmap at try " + i + " : ", ee); + exception = ee; + } + } + if (!success) + { + LOG.Error("Still couldn't create Bitmap!"); + throw exception; + } + } + } + } + } + return returnBitmap; + } + } +} \ No newline at end of file diff --git a/GreenshotImageEditor/Core/WindowsHelper.cs b/GreenshotImageEditor/Core/WindowsHelper.cs new file mode 100644 index 000000000..d7a8069c2 --- /dev/null +++ b/GreenshotImageEditor/Core/WindowsHelper.cs @@ -0,0 +1,2074 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2013 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: http://getgreenshot.org/ + * The Greenshot project is hosted on Sourceforge: http://sourceforge.net/projects/greenshot/ + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using System.Diagnostics.CodeAnalysis; +using Greenshot.IniFile; +using Greenshot.Interop; +using Greenshot.Plugin; +using GreenshotPlugin.UnmanagedHelpers; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Drawing; +using System.Drawing.Imaging; +using System.Runtime.InteropServices; +using System.Text; +using System.Text.RegularExpressions; +using System.Windows.Forms; + +/// +/// Code for handling with "windows" +/// Main code is taken from vbAccelerator, location: +/// http://www.vbaccelerator.com/home/NET/Code/Libraries/Windows/Enumerating_Windows/article.asp +/// but a LOT of changes/enhancements were made to adapt it for Greenshot. +/// +namespace GreenshotPlugin.Core +{ + #region EnumWindows + + /// + /// EnumWindows wrapper for .NET + /// + public class WindowsEnumerator + { + #region Member Variables + + private List items = null; + + #endregion Member Variables + + /// + /// Returns the collection of windows returned by + /// GetWindows + /// + public List Items + { + get + { + return items; + } + } + + /// + /// Gets all top level windows on the system. + /// + public WindowsEnumerator GetWindows() + { + GetWindows(IntPtr.Zero, null); + return this; + } + + /// + /// Gets all child windows of the specified window + /// + /// Window Handle to get children for + public WindowsEnumerator GetWindows(WindowDetails parent) + { + if (parent != null) + { + GetWindows(parent.Handle, null); + } + else + { + GetWindows(IntPtr.Zero, null); + } + return this; + } + + /// + /// Gets all child windows of the specified window + /// + /// Window Handle to get children for + /// Window Classname to copy, use null to copy all + public WindowsEnumerator GetWindows(IntPtr hWndParent, string classname) + { + items = new List(); + List windows = new List(); + User32.EnumChildWindows(hWndParent, WindowEnum, 0); + + bool hasParent = !IntPtr.Zero.Equals(hWndParent); + string parentText = null; + if (hasParent) + { + StringBuilder title = new StringBuilder(260, 260); + User32.GetWindowText(hWndParent, title, title.Capacity); + parentText = title.ToString(); + } + + foreach (WindowDetails window in items) + { + if (hasParent) + { + window.Text = parentText; + window.ParentHandle = hWndParent; + } + if (classname == null || window.ClassName.Equals(classname)) + { + windows.Add(window); + } + } + items = windows; + return this; + } + + #region EnumWindows callback + + /// + /// The enum Windows callback. + /// + /// Window Handle + /// Application defined value + /// 1 to continue enumeration, 0 to stop + private int WindowEnum(IntPtr hWnd, int lParam) + { + if (OnWindowEnum(hWnd)) + { + return 1; + } + else + { + return 0; + } + } + + #endregion EnumWindows callback + + /// + /// Called whenever a new window is about to be added + /// by the Window enumeration called from GetWindows. + /// If overriding this function, return true to continue + /// enumeration or false to stop. If you do not call + /// the base implementation the Items collection will + /// be empty. + /// + /// Window handle to add + /// True to continue enumeration, False to stop + protected virtual bool OnWindowEnum(IntPtr hWnd) + { + if (!WindowDetails.isIgnoreHandle(hWnd)) + { + items.Add(new WindowDetails(hWnd)); + } + return true; + } + + #region Constructor, Dispose + + public WindowsEnumerator() + { + // nothing to do + } + + #endregion Constructor, Dispose + } + + #endregion EnumWindows + + /// + + #region WindowDetails + + /// + /// Provides details about a Window returned by the + /// enumeration + /// + [SuppressMessage("Microsoft.Design", "CA1049:TypesThatOwnNativeResourcesShouldBeDisposable")] + public class WindowDetails : IEquatable + { + private const string METRO_WINDOWS_CLASS = "Windows.UI.Core.CoreWindow"; + private const string METRO_APPLAUNCHER_CLASS = "ImmersiveLauncher"; + private const string METRO_GUTTER_CLASS = "ImmersiveGutter"; + + private static Dictionary> classnameTree = new Dictionary>(); + private static CoreConfiguration conf = IniConfig.GetIniSection(); + private static List ignoreHandles = new List(); + private static Dictionary iconCache = new Dictionary(); + private static List excludeProcessesFromFreeze = new List(); + private static IAppVisibility appVisibility = null; + + static WindowDetails() + { + try + { + // Only try to instanciate when Windows 8 or later. + if (Environment.OSVersion.Version.Major >= 6 && Environment.OSVersion.Version.Minor >= 2) + { + appVisibility = COMWrapper.CreateInstance(); + } + } + catch { } + } + + public static void AddProcessToExcludeFromFreeze(string processname) + { + if (!excludeProcessesFromFreeze.Contains(processname)) + { + excludeProcessesFromFreeze.Add(processname); + } + } + + internal static bool isIgnoreHandle(IntPtr handle) + { + return ignoreHandles.Contains(handle); + } + + private List childWindows = null; + private IntPtr parentHandle = IntPtr.Zero; + private WindowDetails parent = null; + private bool frozen = false; + + public bool isApp + { + get + { + return METRO_WINDOWS_CLASS.Equals(ClassName); + } + } + + public bool isGutter + { + get + { + return METRO_GUTTER_CLASS.Equals(ClassName); + } + } + + public bool isAppLauncher + { + get + { + return METRO_APPLAUNCHER_CLASS.Equals(ClassName); + } + } + + /// + /// Check if this window is the window of a metro app + /// + public bool isMetroApp + { + get + { + return isAppLauncher || isApp; + } + } + + /// + /// The window handle. + /// + private IntPtr hWnd = IntPtr.Zero; + + /// + /// To allow items to be compared, the hash code + /// is set to the Window handle, so two EnumWindowsItem + /// objects for the same Window will be equal. + /// + /// The Window Handle for this window + public override int GetHashCode() + { + return Handle.ToInt32(); + } + + public override bool Equals(object right) + { + return Equals(right as WindowDetails); + } + + public bool Equals(WindowDetails other) + { + if (ReferenceEquals(other, null)) + { + return false; + } + + if (ReferenceEquals(this, other)) + { + return true; + } + + if (GetType() != other.GetType()) + { + return false; + } + return other.Handle == Handle; + } + + public bool HasChildren + { + get + { + return (childWindows != null) && (childWindows.Count > 0); + } + } + + public void FreezeDetails() + { + frozen = true; + } + + public void UnfreezeDetails() + { + frozen = false; + } + + public string ProcessPath + { + get + { + if (Handle == IntPtr.Zero) + { + // not a valid window handle + return string.Empty; + } + // Get the process id + IntPtr processid; + User32.GetWindowThreadProcessId(Handle, out processid); + return Kernel32.GetProcessPath(processid); + } + } + + /// + /// Get the icon belonging to the process + /// + public Image DisplayIcon + { + get + { + try + { + using (Icon appIcon = GetAppIcon(Handle)) + { + if (appIcon != null) + { + return appIcon.ToBitmap(); + } + } + } + catch (Exception ex) + { + LOG.WarnFormat("Couldn't get icon for window {0} due to: {1}", Text, ex.Message); + LOG.Warn(ex); + } + if (isMetroApp) + { + // No method yet to get the metro icon + return null; + } + try + { + string filename = ProcessPath; + if (!iconCache.ContainsKey(filename)) + { + Image icon = null; + using (Icon appIcon = Shell32.ExtractAssociatedIcon(filename)) + { + if (appIcon != null) + { + icon = appIcon.ToBitmap(); + } + } + iconCache.Add(filename, icon); + } + return iconCache[filename]; + } + catch (Exception ex) + { + LOG.WarnFormat("Couldn't get icon for window {0} due to: {1}", Text, ex.Message); + LOG.Warn(ex); + } + return null; + } + } + + /// + /// Get the icon for a hWnd + /// + /// + /// + private static Icon GetAppIcon(IntPtr hwnd) + { + const int ICON_SMALL = 0; + const int ICON_BIG = 1; + const int ICON_SMALL2 = 2; + + IntPtr iconHandle = User32.SendMessage(hwnd, (int)WindowsMessages.WM_GETICON, ICON_SMALL2, 0); + if (iconHandle == IntPtr.Zero) + { + iconHandle = User32.SendMessage(hwnd, (int)WindowsMessages.WM_GETICON, ICON_SMALL, 0); + } + if (iconHandle == IntPtr.Zero) + { + iconHandle = User32.GetClassLongWrapper(hwnd, (int)ClassLongIndex.GCL_HICONSM); + } + if (iconHandle == IntPtr.Zero) + { + iconHandle = User32.SendMessage(hwnd, (int)WindowsMessages.WM_GETICON, ICON_BIG, 0); + } + if (iconHandle == IntPtr.Zero) + { + iconHandle = User32.GetClassLongWrapper(hwnd, (int)ClassLongIndex.GCL_HICON); + } + + if (iconHandle == IntPtr.Zero) + { + return null; + } + + Icon icon = Icon.FromHandle(iconHandle); + + return icon; + } + + /// + /// Use this to make remove internal windows, like the mainform and the captureforms, invisible + /// + /// + public static void RegisterIgnoreHandle(IntPtr ignoreHandle) + { + ignoreHandles.Add(ignoreHandle); + } + + /// + /// Use this to remove the with RegisterIgnoreHandle registered handle + /// + /// + public static void UnregisterIgnoreHandle(IntPtr ignoreHandle) + { + ignoreHandles.Remove(ignoreHandle); + } + + public List Children + { + get + { + if (childWindows == null) + { + GetChildren(); + } + return childWindows; + } + } + + /// + /// Retrieve all windows with a certain title or classname + /// + /// The regexp to look for in the title + /// The regexp to look for in the classname + /// List with all the found windows + private static List FindWindow(List windows, string titlePattern, string classnamePattern) + { + List foundWindows = new List(); + Regex titleRegexp = null; + Regex classnameRegexp = null; + + if (titlePattern != null && titlePattern.Trim().Length > 0) + { + titleRegexp = new Regex(titlePattern); + } + if (classnamePattern != null && classnamePattern.Trim().Length > 0) + { + classnameRegexp = new Regex(classnamePattern); + } + + foreach (WindowDetails window in windows) + { + if (titleRegexp != null && titleRegexp.IsMatch(window.Text)) + { + foundWindows.Add(window); + } + else if (classnameRegexp != null && classnameRegexp.IsMatch(window.ClassName)) + { + foundWindows.Add(window); + } + } + return foundWindows; + } + + /// + /// Retrieve the child with mathing classname + /// + public WindowDetails GetChild(string childClassname) + { + foreach (WindowDetails child in Children) + { + if (childClassname.Equals(child.ClassName)) + { + return child; + } + } + return null; + } + + /// + /// Retrieve the children with mathing classname + /// + public IEnumerable GetChilden(string childClassname) + { + foreach (WindowDetails child in Children) + { + if (childClassname.Equals(child.ClassName)) + { + yield return child; + } + } + } + + public IntPtr ParentHandle + { + get + { + if (parentHandle == IntPtr.Zero) + { + parentHandle = User32.GetParent(Handle); + parent = null; + } + return parentHandle; + } + set + { + if (parentHandle != value) + { + parentHandle = value; + parent = null; + } + } + } + + /// + /// Get the parent of the current window + /// + /// WindowDetails of the parent, or null if none + public WindowDetails GetParent() + { + if (parent == null) + { + if (parentHandle == IntPtr.Zero) + { + parentHandle = User32.GetParent(Handle); + } + if (parentHandle != IntPtr.Zero) + { + parent = new WindowDetails(parentHandle); + } + } + return parent; + } + + /// + /// Retrieve all the children, this only stores the children internally. + /// One should normally use the getter "Children" + /// + public List GetChildren() + { + if (childWindows == null) + { + return GetChildren(0); + } + return childWindows; + } + + /// + /// Retrieve all the children, this only stores the children internally, use the "Children" property for the value + /// + /// Specify how many levels we go in + public List GetChildren(int levelsToGo) + { + if (childWindows == null) + { + childWindows = new List(); + foreach (WindowDetails childWindow in new WindowsEnumerator().GetWindows(hWnd, null).Items) + { + childWindows.Add(childWindow); + if (levelsToGo > 0) + { + childWindow.GetChildren(levelsToGo - 1); + } + } + } + return childWindows; + } + + /// + /// Retrieve children with a certain title or classname + /// + /// The regexp to look for in the title + /// The regexp to look for in the classname + /// List with all the found windows, or an emptry list + public List FindChildren(string titlePattern, string classnamePattern) + { + return FindWindow(Children, titlePattern, classnamePattern); + } + + /// + /// Recursing helper method for the FindPath + /// + /// List with classnames + /// The index in the list to look for + /// WindowDetails if a match was found + private WindowDetails FindPath(List classnames, int index) + { + WindowDetails resultWindow = null; + List foundWindows = FindChildren(null, classnames[index]); + if (index == classnames.Count - 1) + { + if (foundWindows.Count > 0) + { + resultWindow = foundWindows[0]; + } + } + else + { + foreach (WindowDetails foundWindow in foundWindows) + { + resultWindow = foundWindow.FindPath(classnames, index + 1); + if (resultWindow != null) + { + break; + } + } + } + return resultWindow; + } + + /// + /// This method will find the child window according to a path of classnames. + /// Usually used for finding a certain "content" window like for the IE Browser + /// + /// List with classname "path" + /// true allows the search to skip a classname of the path + /// WindowDetails if found + public WindowDetails FindPath(List classnames, bool allowSkip) + { + int index = 0; + WindowDetails resultWindow = FindPath(classnames, index++); + if (resultWindow == null && allowSkip) + { + while (resultWindow == null && index < classnames.Count) + { + resultWindow = FindPath(classnames, index); + } + } + return resultWindow; + } + + /// + /// Deep scan for a certain classname pattern + /// + /// Classname regexp pattern + /// The first WindowDetails found + public static WindowDetails DeepScan(WindowDetails windowDetails, Regex classnamePattern) + { + if (classnamePattern.IsMatch(windowDetails.ClassName)) + { + return windowDetails; + } + // First loop through this level + foreach (WindowDetails child in windowDetails.Children) + { + if (classnamePattern.IsMatch(child.ClassName)) + { + return child; + } + } + // Go into all children + foreach (WindowDetails child in windowDetails.Children) + { + WindowDetails deepWindow = DeepScan(child, classnamePattern); + if (deepWindow != null) + { + return deepWindow; + } + } + return null; + } + + /// + /// GetWindow + /// + /// The GetWindowCommand to use + /// null if nothing found, otherwise the WindowDetails instance of the "child" + public WindowDetails GetWindow(GetWindowCommand gwCommand) + { + IntPtr tmphWnd = User32.GetWindow(Handle, gwCommand); + if (IntPtr.Zero == tmphWnd) + { + return null; + } + WindowDetails windowDetails = new WindowDetails(tmphWnd); + windowDetails.parent = this; + return windowDetails; + } + + /// + /// Gets the window's handle + /// + public IntPtr Handle + { + get + { + return hWnd; + } + } + + private string text = null; + /// + /// Gets the window's title (caption) + /// + public string Text + { + set + { + text = value; + } + get + { + if (text == null) + { + StringBuilder title = new StringBuilder(260, 260); + User32.GetWindowText(hWnd, title, title.Capacity); + text = title.ToString(); + } + return text; + } + } + + private string className = null; + /// + /// Gets the window's class name. + /// + public string ClassName + { + get + { + if (className == null) + { + className = GetClassName(hWnd); + } + return className; + } + } + + /// + /// Gets/Sets whether the window is iconic (mimimised) or not. + /// + public bool Iconic + { + get + { + if (isMetroApp) + { + return !Visible; + } + return User32.IsIconic(hWnd) || Location.X <= -32000; + } + set + { + if (value) + { + User32.SendMessage(hWnd, (int)WindowsMessages.WM_SYSCOMMAND, (IntPtr)User32.SC_MINIMIZE, IntPtr.Zero); + } + else + { + User32.SendMessage(hWnd, (int)WindowsMessages.WM_SYSCOMMAND, (IntPtr)User32.SC_RESTORE, IntPtr.Zero); + } + } + } + + /// + /// Gets/Sets whether the window is maximised or not. + /// + public bool Maximised + { + get + { + if (isApp) + { + if (Visible) + { + Rectangle windowRectangle = WindowRectangle; + foreach (Screen screen in Screen.AllScreens) + { + if (screen.Bounds.Contains(windowRectangle)) + { + if (windowRectangle.Equals(screen.Bounds)) + { + return true; + } + } + } + } + return false; + } + return User32.IsZoomed(hWnd); + } + set + { + if (value) + { + User32.SendMessage(hWnd, (int)WindowsMessages.WM_SYSCOMMAND, (IntPtr)User32.SC_MAXIMIZE, IntPtr.Zero); + } + else + { + User32.SendMessage(hWnd, (int)WindowsMessages.WM_SYSCOMMAND, (IntPtr)User32.SC_MINIMIZE, IntPtr.Zero); + } + } + } + + /// + /// This doesn't work as good as is should, but does move the App out of the way... + /// + public void HideApp() + { + User32.ShowWindow(Handle, ShowWindowCommand.Hide); + } + + /// + /// Gets whether the window is visible. + /// + public bool Visible + { + get + { + if (isApp) + { + Rectangle windowRectangle = WindowRectangle; + foreach (Screen screen in Screen.AllScreens) + { + if (screen.Bounds.Contains(windowRectangle)) + { + if (windowRectangle.Equals(screen.Bounds)) + { + // Fullscreen, it's "visible" when AppVisibilityOnMonitor says yes + // Although it might be the other App, this is not "very" important + RECT rect = new RECT(screen.Bounds); + IntPtr monitor = User32.MonitorFromRect(ref rect, User32.MONITOR_DEFAULTTONULL); + if (monitor != IntPtr.Zero) + { + if (appVisibility != null) + { + MONITOR_APP_VISIBILITY monitorAppVisibility = appVisibility.GetAppVisibilityOnMonitor(monitor); + //LOG.DebugFormat("App {0} visible: {1} on {2}", Text, monitorAppVisibility, screen.Bounds); + if (monitorAppVisibility == MONITOR_APP_VISIBILITY.MAV_APP_VISIBLE) + { + return true; + } + } + } + } + else + { + // Is only partly on the screen, when this happens the app is allways visible! + return true; + } + } + } + return false; + } + if (isGutter) + { + // gutter is only made available when it's visible + return true; + } + if (isAppLauncher) + { + return IsAppLauncherVisible; + } + return User32.IsWindowVisible(hWnd); + } + } + + public bool HasParent + { + get + { + GetParent(); + return parentHandle != IntPtr.Zero; + } + } + + public IntPtr ProcessId + { + get + { + IntPtr processId; + User32.GetWindowThreadProcessId(Handle, out processId); + return processId; + } + } + + public Process Process + { + get + { + try + { + IntPtr processId; + User32.GetWindowThreadProcessId(Handle, out processId); + Process process = Process.GetProcessById(processId.ToInt32()); + if (process != null) + { + return process; + } + } + catch (Exception ex) + { + LOG.Warn(ex); + } + return null; + } + } + + /// + /// Make sure the next call of a cached value is guaranteed the real value + /// + public void Reset() + { + previousWindowRectangle = Rectangle.Empty; + } + + private Rectangle previousWindowRectangle = Rectangle.Empty; + private long lastWindowRectangleRetrieveTime = 0; + private const long CACHE_TIME = TimeSpan.TicksPerSecond * 2; + /// + /// Gets the bounding rectangle of the window + /// + public Rectangle WindowRectangle + { + get + { + // Try to return a cached value + long now = DateTime.Now.Ticks; + if (previousWindowRectangle.IsEmpty || !frozen) + { + if (previousWindowRectangle.IsEmpty || now - lastWindowRectangleRetrieveTime > CACHE_TIME) + { + Rectangle windowRect = Rectangle.Empty; + if (!HasParent && DWM.isDWMEnabled()) + { + GetExtendedFrameBounds(out windowRect); + } + + if (windowRect.IsEmpty) + { + GetWindowRect(out windowRect); + } + + // Correction for maximized windows, only if it's not an app + if (!HasParent && !isApp && Maximised) + { + Size size = Size.Empty; + GetBorderSize(out size); + windowRect = new Rectangle(windowRect.X + size.Width, windowRect.Y + size.Height, windowRect.Width - (2 * size.Width), windowRect.Height - (2 * size.Height)); + } + lastWindowRectangleRetrieveTime = now; + // Try to return something valid, by getting returning the previous size if the window doesn't have a Rectangle anymore + if (windowRect.IsEmpty) + { + return previousWindowRectangle; + } + previousWindowRectangle = windowRect; + return windowRect; + } + } + return previousWindowRectangle; + } + } + + /// + /// Gets the location of the window relative to the screen. + /// + public Point Location + { + get + { + Rectangle tmpRectangle = WindowRectangle; + return new Point(tmpRectangle.Left, tmpRectangle.Top); + } + } + + /// + /// Gets the size of the window. + /// + public Size Size + { + get + { + Rectangle tmpRectangle = WindowRectangle; + return new Size(tmpRectangle.Right - tmpRectangle.Left, tmpRectangle.Bottom - tmpRectangle.Top); + } + } + + /// + /// Get the client rectangle, this is the part of the window inside the borders (drawable area) + /// + public Rectangle ClientRectangle + { + get + { + Rectangle clientRect = Rectangle.Empty; + GetClientRect(out clientRect); + return clientRect; + } + } + + /// + /// Check if the supplied point lies in the window + /// + /// Point with the coordinates to check + /// true if the point lies within + public bool Contains(Point p) + { + return WindowRectangle.Contains(Cursor.Position); + } + + /// + /// Restores and Brings the window to the front, + /// assuming it is a visible application window. + /// + public void Restore() + { + if (Iconic) + { + User32.SendMessage(hWnd, (int)WindowsMessages.WM_SYSCOMMAND, (IntPtr)User32.SC_RESTORE, IntPtr.Zero); + } + User32.BringWindowToTop(hWnd); + User32.SetForegroundWindow(hWnd); + // Make sure windows has time to perform the action + while (Iconic) + { + Application.DoEvents(); + } + } + + /// + /// Get / Set the WindowStyle + /// + public WindowStyleFlags WindowStyle + { + get + { + return (WindowStyleFlags)User32.GetWindowLongWrapper(hWnd, (int)WindowLongIndex.GWL_STYLE); + } + set + { + User32.SetWindowLongWrapper(hWnd, (int)WindowLongIndex.GWL_STYLE, (uint)value); + } + } + + /// + /// Get/Set the WindowPlacement + /// + public WindowPlacement WindowPlacement + { + get + { + WindowPlacement placement = WindowPlacement.Default; + User32.GetWindowPlacement(Handle, ref placement); + return placement; + } + set + { + User32.SetWindowPlacement(Handle, ref value); + } + } + + /// + /// Get/Set the Extended WindowStyle + /// + public ExtendedWindowStyleFlags ExtendedWindowStyle + { + get + { + return (ExtendedWindowStyleFlags)User32.GetWindowLongWrapper(hWnd, (int)WindowLongIndex.GWL_EXSTYLE); + } + set + { + User32.SetWindowLongWrapper(hWnd, (int)WindowLongIndex.GWL_EXSTYLE, (uint)value); + } + } + + /// + /// Capture Window with GDI+ + /// + /// The capture to fill + /// ICapture + public ICapture CaptureGDIWindow(ICapture capture) + { + Image capturedImage = PrintWindow(); + if (capturedImage != null) + { + capture.Image = capturedImage; + capture.Location = Location; + return capture; + } + return null; + } + + /// + /// Capture DWM Window + /// + /// Capture to fill + /// Wanted WindowCaptureMode + /// True if auto modus is used + /// ICapture with the capture + public ICapture CaptureDWMWindow(ICapture capture, WindowCaptureMode windowCaptureMode, bool autoMode) + { + IntPtr thumbnailHandle = IntPtr.Zero; + Form tempForm = null; + bool tempFormShown = false; + try + { + tempForm = new Form(); + tempForm.ShowInTaskbar = false; + tempForm.FormBorderStyle = FormBorderStyle.None; + tempForm.TopMost = true; + + // Register the Thumbnail + DWM.DwmRegisterThumbnail(tempForm.Handle, Handle, out thumbnailHandle); + + // Get the original size + SIZE sourceSize; + DWM.DwmQueryThumbnailSourceSize(thumbnailHandle, out sourceSize); + + if (sourceSize.width <= 0 || sourceSize.height <= 0) + { + return null; + } + + // Calculate the location of the temp form + Point formLocation; + Rectangle windowRectangle = WindowRectangle; + Size borderSize = new Size(); + bool doesCaptureFit = false; + if (!Maximised) + { + // Assume using it's own location + formLocation = windowRectangle.Location; + using (Region workingArea = new Region(Screen.PrimaryScreen.Bounds)) + { + // Find the screen where the window is and check if it fits + foreach (Screen screen in Screen.AllScreens) + { + if (screen != Screen.PrimaryScreen) + { + workingArea.Union(screen.Bounds); + } + } + + // If the formLocation is not inside the visible area + if (!workingArea.AreRectangleCornersVisisble(windowRectangle)) + { + // If none found we find the biggest screen + foreach (Screen screen in Screen.AllScreens) + { + Rectangle newWindowRectangle = new Rectangle(screen.WorkingArea.Location, windowRectangle.Size); + if (workingArea.AreRectangleCornersVisisble(newWindowRectangle)) + { + formLocation = screen.Bounds.Location; + doesCaptureFit = true; + break; + } + } + } + else + { + doesCaptureFit = true; + } + } + } + else + { + //GetClientRect(out windowRectangle); + GetBorderSize(out borderSize); + formLocation = new Point(windowRectangle.X - borderSize.Width, windowRectangle.Y - borderSize.Height); + } + + tempForm.Location = formLocation; + tempForm.Size = sourceSize.ToSize(); + + // Prepare rectangle to capture from the screen. + Rectangle captureRectangle = new Rectangle(formLocation.X, formLocation.Y, sourceSize.width, sourceSize.height); + if (Maximised) + { + // Correct capture size for maximized window by offsetting the X,Y with the border size + captureRectangle.X += borderSize.Width; + captureRectangle.Y += borderSize.Height; + // and subtrackting the border from the size (2 times, as we move right/down for the capture without resizing) + captureRectangle.Width -= 2 * borderSize.Width; + captureRectangle.Height -= 2 * borderSize.Height; + } + else if (autoMode) + { + // check if the capture fits + if (!doesCaptureFit) + { + // if GDI is allowed.. (a screenshot won't be better than we comes if we continue) + if (!isMetroApp && WindowCapture.isGDIAllowed(Process)) + { + // we return null which causes the capturing code to try another method. + return null; + } + } + } + + // Prepare the displaying of the Thumbnail + DWM_THUMBNAIL_PROPERTIES props = new DWM_THUMBNAIL_PROPERTIES(); + props.Opacity = (byte)255; + props.Visible = true; + props.Destination = new RECT(0, 0, sourceSize.width, sourceSize.height); + DWM.DwmUpdateThumbnailProperties(thumbnailHandle, ref props); + tempForm.Show(); + tempFormShown = true; + + // Intersect with screen + captureRectangle.Intersect(capture.ScreenBounds); + + // Destination bitmap for the capture + Bitmap capturedBitmap = null; + bool frozen = false; + try + { + // Check if we make a transparent capture + if (windowCaptureMode == WindowCaptureMode.AeroTransparent) + { + frozen = FreezeWindow(); + // Use white, later black to capture transparent + tempForm.BackColor = Color.White; + // Make sure everything is visible + tempForm.Refresh(); + Application.DoEvents(); + + try + { + using (Bitmap whiteBitmap = WindowCapture.CaptureRectangle(captureRectangle)) + { + // Apply a white color + tempForm.BackColor = Color.Black; + // Make sure everything is visible + tempForm.Refresh(); + if (!isMetroApp) + { + // Make sure the application window is active, so the colors & buttons are right + ToForeground(); + } + // Make sure all changes are processed and visisble + Application.DoEvents(); + using (Bitmap blackBitmap = WindowCapture.CaptureRectangle(captureRectangle)) + { + capturedBitmap = ApplyTransparency(blackBitmap, whiteBitmap); + } + } + } + catch (Exception e) + { + LOG.Debug("Exception: ", e); + // Some problem occured, cleanup and make a normal capture + if (capturedBitmap != null) + { + capturedBitmap.Dispose(); + capturedBitmap = null; + } + } + } + // If no capture up till now, create a normal capture. + if (capturedBitmap == null) + { + // Remove transparency, this will break the capturing + if (!autoMode) + { + tempForm.BackColor = Color.FromArgb(255, conf.DWMBackgroundColor.R, conf.DWMBackgroundColor.G, conf.DWMBackgroundColor.B); + } + else + { + Color colorizationColor = DWM.ColorizationColor; + // Modify by losing the transparency and increasing the intensity (as if the background color is white) + colorizationColor = Color.FromArgb(255, (colorizationColor.R + 255) >> 1, (colorizationColor.G + 255) >> 1, (colorizationColor.B + 255) >> 1); + tempForm.BackColor = colorizationColor; + } + // Make sure everything is visible + tempForm.Refresh(); + if (!isMetroApp) + { + // Make sure the application window is active, so the colors & buttons are right + ToForeground(); + } + // Make sure all changes are processed and visisble + Application.DoEvents(); + // Capture from the screen + capturedBitmap = WindowCapture.CaptureRectangle(captureRectangle); + } + if (capturedBitmap != null) + { + // Not needed for Windows 8 + if (!(Environment.OSVersion.Version.Major == 6 && Environment.OSVersion.Version.Minor >= 2)) + { + // Only if the Inivalue is set, not maximized and it's not a tool window. + if (conf.WindowCaptureRemoveCorners && !Maximised && (ExtendedWindowStyle & ExtendedWindowStyleFlags.WS_EX_TOOLWINDOW) == 0) + { + // Remove corners + if (!Image.IsAlphaPixelFormat(capturedBitmap.PixelFormat)) + { + LOG.Debug("Changing pixelformat to Alpha for the RemoveCorners"); + Bitmap tmpBitmap = ImageHelper.Clone(capturedBitmap, PixelFormat.Format32bppArgb); + capturedBitmap.Dispose(); + capturedBitmap = tmpBitmap; + } + RemoveCorners(capturedBitmap); + } + } + } + } + finally + { + // Make sure to ALWAYS unfreeze!! + if (frozen) + { + UnfreezeWindow(); + } + } + + capture.Image = capturedBitmap; + // Make sure the capture location is the location of the window, not the copy + capture.Location = Location; + } + finally + { + if (thumbnailHandle != IntPtr.Zero) + { + // Unregister (cleanup), as we are finished we don't need the form or the thumbnail anymore + DWM.DwmUnregisterThumbnail(thumbnailHandle); + } + if (tempForm != null) + { + if (tempFormShown) + { + tempForm.Close(); + } + tempForm.Dispose(); + tempForm = null; + } + } + + return capture; + } + + /// + /// Helper method to remove the corners from a DMW capture + /// + /// The bitmap to remove the corners from. + private void RemoveCorners(Bitmap image) + { + using (IFastBitmap fastBitmap = FastBitmap.Create(image)) + { + for (int y = 0; y < conf.WindowCornerCutShape.Count; y++) + { + for (int x = 0; x < conf.WindowCornerCutShape[y]; x++) + { + fastBitmap.SetColorAt(x, y, Color.Transparent); + fastBitmap.SetColorAt(image.Width - 1 - x, y, Color.Transparent); + fastBitmap.SetColorAt(image.Width - 1 - x, image.Height - 1 - y, Color.Transparent); + fastBitmap.SetColorAt(x, image.Height - 1 - y, Color.Transparent); + } + } + } + } + + /// + /// Apply transparency by comparing a transparent capture with a black and white background + /// A "Math.min" makes sure there is no overflow, but this could cause the picture to have shifted colors. + /// The pictures should have been taken without differency, exect for the colors. + /// + /// Bitmap with the black image + /// Bitmap with the black image + /// Bitmap with transparency + private Bitmap ApplyTransparency(Bitmap blackBitmap, Bitmap whiteBitmap) + { + using (IFastBitmap targetBuffer = FastBitmap.CreateEmpty(blackBitmap.Size, PixelFormat.Format32bppArgb, Color.Transparent)) + { + targetBuffer.SetResolution(blackBitmap.HorizontalResolution, blackBitmap.VerticalResolution); + using (IFastBitmap blackBuffer = FastBitmap.Create(blackBitmap)) + { + using (IFastBitmap whiteBuffer = FastBitmap.Create(whiteBitmap)) + { + for (int y = 0; y < blackBuffer.Height; y++) + { + for (int x = 0; x < blackBuffer.Width; x++) + { + Color c0 = blackBuffer.GetColorAt(x, y); + Color c1 = whiteBuffer.GetColorAt(x, y); + // Calculate alpha as double in range 0-1 + double alpha = (c0.R - c1.R + 255) / 255d; + if (alpha == 1) + { + // Alpha == 1 means no change! + targetBuffer.SetColorAt(x, y, c0); + } + else if (alpha == 0) + { + // Complete transparency, use transparent pixel + targetBuffer.SetColorAt(x, y, Color.Transparent); + } + else + { + // Calculate original color + byte originalAlpha = (byte)Math.Min(255, alpha * 255); + //LOG.DebugFormat("Alpha {0} & c0 {1} & c1 {2}", alpha, c0, c1); + byte originalRed = (byte)Math.Min(255, c0.R / alpha); + byte originalGreen = (byte)Math.Min(255, c0.G / alpha); + byte originalBlue = (byte)Math.Min(255, c0.B / alpha); + Color originalColor = Color.FromArgb(originalAlpha, originalRed, originalGreen, originalBlue); + //Color originalColor = Color.FromArgb(originalAlpha, originalRed, c0.G, c0.B); + targetBuffer.SetColorAt(x, y, originalColor); + } + } + } + } + } + return targetBuffer.UnlockAndReturnBitmap(); + } + } + + /// + /// Helper method to get the window size for DWM Windows + /// + /// out Rectangle + /// bool true if it worked + private bool GetExtendedFrameBounds(out Rectangle rectangle) + { + RECT rect; + int result = DWM.DwmGetWindowAttribute(Handle, (int)DWMWINDOWATTRIBUTE.DWMWA_EXTENDED_FRAME_BOUNDS, out rect, Marshal.SizeOf(typeof(RECT))); + if (result >= 0) + { + rectangle = rect.ToRectangle(); + return true; + } + rectangle = Rectangle.Empty; + return false; + } + + /// + /// Helper method to get the window size for GDI Windows + /// + /// out Rectangle + /// bool true if it worked + private bool GetClientRect(out Rectangle rectangle) + { + WindowInfo windowInfo = new WindowInfo(); + // Get the Window Info for this window + bool result = User32.GetWindowInfo(Handle, ref windowInfo); + if (result) + { + rectangle = windowInfo.rcClient.ToRectangle(); + } + else + { + rectangle = Rectangle.Empty; + } + return result; + } + + /// + /// Helper method to get the window size for GDI Windows + /// + /// out Rectangle + /// bool true if it worked + private bool GetWindowRect(out Rectangle rectangle) + { + WindowInfo windowInfo = new WindowInfo(); + // Get the Window Info for this window + bool result = User32.GetWindowInfo(Handle, ref windowInfo); + if (result) + { + rectangle = windowInfo.rcWindow.ToRectangle(); + } + else + { + rectangle = Rectangle.Empty; + } + return result; + } + + /// + /// Helper method to get the Border size for GDI Windows + /// + /// out Rectangle + /// bool true if it worked + private bool GetBorderSize(out Size size) + { + WindowInfo windowInfo = new WindowInfo(); + // Get the Window Info for this window + bool result = User32.GetWindowInfo(Handle, ref windowInfo); + if (result) + { + size = new Size((int)windowInfo.cxWindowBorders, (int)windowInfo.cyWindowBorders); + } + else + { + size = Size.Empty; + } + return result; + } + + /// + /// Set the window as foreground window + /// + public static void ToForeground(IntPtr handle) + { + User32.SetForegroundWindow(handle); + } + + /// + /// Set the window as foreground window + /// + public void ToForeground() + { + ToForeground(Handle); + } + + /// + /// Get the region for a window + /// + private Region GetRegion() + { + using (SafeRegionHandle region = GDI32.CreateRectRgn(0, 0, 0, 0)) + { + if (!region.IsInvalid) + { + RegionResult result = User32.GetWindowRgn(Handle, region); + if (result != RegionResult.REGION_ERROR && result != RegionResult.REGION_NULLREGION) + { + return Region.FromHrgn(region.DangerousGetHandle()); + } + } + } + return null; + } + + private bool CanFreezeOrUnfreeze(string titleOrProcessname) + { + if (string.IsNullOrEmpty(titleOrProcessname)) + { + return false; + } + if (titleOrProcessname.ToLower().Contains("greenshot")) + { + return false; + } + + foreach (string excludeProcess in excludeProcessesFromFreeze) + { + if (titleOrProcessname.ToLower().Contains(excludeProcess)) + { + return false; + } + } + return true; + } + + /// + /// Freezes the process belonging to the window + /// Warning: Use only if no other way!! + /// + private bool FreezeWindow() + { + Process proc = Process.GetProcessById(ProcessId.ToInt32()); + string processName = proc.ProcessName; + if (!CanFreezeOrUnfreeze(processName)) + { + LOG.DebugFormat("Not freezing {0}", processName); + return false; + } + if (!CanFreezeOrUnfreeze(Text)) + { + LOG.DebugFormat("Not freezing {0}", processName); + return false; + } + LOG.DebugFormat("Freezing process: {0}", processName); + + bool frozen = false; + + foreach (ProcessThread pT in proc.Threads) + { + IntPtr pOpenThread = Kernel32.OpenThread(ThreadAccess.SUSPEND_RESUME, false, (uint)pT.Id); + + if (pOpenThread == IntPtr.Zero) + { + break; + } + frozen = true; + Kernel32.SuspendThread(pOpenThread); + } + return frozen; + } + + /// + /// Unfreeze the process belonging to the window + /// + public void UnfreezeWindow() + { + Process proc = Process.GetProcessById(ProcessId.ToInt32()); + + string processName = proc.ProcessName; + if (!CanFreezeOrUnfreeze(processName)) + { + LOG.DebugFormat("Not unfreezing {0}", processName); + return; + } + if (!CanFreezeOrUnfreeze(Text)) + { + LOG.DebugFormat("Not unfreezing {0}", processName); + return; + } + LOG.DebugFormat("Unfreezing process: {0}", processName); + + foreach (ProcessThread pT in proc.Threads) + { + IntPtr pOpenThread = Kernel32.OpenThread(ThreadAccess.SUSPEND_RESUME, false, (uint)pT.Id); + + if (pOpenThread == IntPtr.Zero) + { + break; + } + + Kernel32.ResumeThread(pOpenThread); + } + } + + /// + /// Return an Image representating the Window! + /// As GDI+ draws it, it will be without Aero borders! + /// + public Image PrintWindow() + { + Rectangle windowRect = WindowRectangle; + // Start the capture + Exception exceptionOccured = null; + Image returnImage = null; + using (Region region = GetRegion()) + { + PixelFormat pixelFormat = PixelFormat.Format24bppRgb; + // Only use 32 bpp ARGB when the window has a region + if (region != null) + { + pixelFormat = PixelFormat.Format32bppArgb; + } + returnImage = new Bitmap(windowRect.Width, windowRect.Height, pixelFormat); + using (Graphics graphics = Graphics.FromImage(returnImage)) + { + IntPtr hDCDest = graphics.GetHdc(); + try + { + bool printSucceeded = User32.PrintWindow(Handle, hDCDest, 0x0); + if (!printSucceeded) + { + // something went wrong, most likely a "0x80004005" (Acess Denied) when using UAC + exceptionOccured = User32.CreateWin32Exception("PrintWindow"); + } + } + finally + { + graphics.ReleaseHdc(hDCDest); + } + + // Apply the region "transparency" + if (region != null && !region.IsEmpty(graphics)) + { + graphics.ExcludeClip(region); + graphics.Clear(Color.Transparent); + } + + graphics.Flush(); + } + } + + // Return null if error + if (exceptionOccured != null) + { + LOG.ErrorFormat("Error calling print window: {0}", exceptionOccured.Message); + if (returnImage != null) + { + returnImage.Dispose(); + } + return null; + } + if (!HasParent && Maximised) + { + LOG.Debug("Correcting for maximalization"); + Size borderSize = Size.Empty; + GetBorderSize(out borderSize); + Rectangle borderRectangle = new Rectangle(borderSize.Width, borderSize.Height, windowRect.Width - (2 * borderSize.Width), windowRect.Height - (2 * borderSize.Height)); + ImageHelper.Crop(ref returnImage, ref borderRectangle); + } + return returnImage; + } + + /// + /// Constructs a new instance of this class for + /// the specified Window Handle. + /// + /// The Window Handle + public WindowDetails(IntPtr hWnd) + { + this.hWnd = hWnd; + } + + /// + /// Gets an instance of the current active foreground window + /// + /// WindowDetails of the current window + public static WindowDetails GetActiveWindow() + { + IntPtr hWnd = User32.GetForegroundWindow(); + if (hWnd != null && hWnd != IntPtr.Zero) + { + if (ignoreHandles.Contains(hWnd)) + { + return GetDesktopWindow(); + } + + WindowDetails activeWindow = new WindowDetails(hWnd); + // Invisible Windows should not be active + if (!activeWindow.Visible) + { + return GetDesktopWindow(); + } + return activeWindow; + } + return null; + } + + /// + /// Check if this window is Greenshot + /// + public bool IsGreenshot + { + get + { + try + { + if (!isMetroApp) + { + return "Greenshot".Equals(Process.MainModule.FileVersionInfo.ProductName); + } + } + catch (Exception ex) + { + LOG.Warn(ex); + } + return false; + } + } + + /// + /// Gets the Destop window + /// + /// WindowDetails for the destop window + public static WindowDetails GetDesktopWindow() + { + return new WindowDetails(User32.GetDesktopWindow()); + } + + /// + /// Get all the top level windows + /// + /// List with all the top level windows + public static List GetAllWindows() + { + return GetAllWindows(null); + } + + /// + /// Get all the top level windows, with matching classname + /// + /// List with all the top level windows + public static List GetAllWindows(string classname) + { + return new WindowsEnumerator().GetWindows(IntPtr.Zero, classname).Items; + } + + /// + /// Recursive "find children which" + /// + /// Window to look into + /// point to check for + /// + public WindowDetails FindChildUnderPoint(Point point) + { + if (!Contains(point)) + { + return null; + } + foreach (WindowDetails childWindow in Children) + { + if (childWindow.Contains(point)) + { + return childWindow.FindChildUnderPoint(point); + } + } + return this; + } + + /// + /// Retrieves the classname for a hWnd + /// + /// IntPtr with the windows handle + /// String with ClassName + public static String GetClassName(IntPtr hWnd) + { + StringBuilder classNameBuilder = new StringBuilder(260, 260); + User32.GetClassName(hWnd, classNameBuilder, classNameBuilder.Capacity); + return classNameBuilder.ToString(); + } + + /// + /// Get all the visible top level windows + /// + /// List with all the visible top level windows + public static List GetVisibleWindows() + { + List windows = new List(); + Rectangle screenBounds = WindowCapture.GetScreenBounds(); + List allWindows = GetMetroApps(); + allWindows.AddRange(GetAllWindows()); + foreach (WindowDetails window in allWindows) + { + // Ignore windows without title + if (window.Text.Length == 0) + { + continue; + } + // Ignore invisible + if (!window.Visible) + { + continue; + } + // Ignore some classes + List ignoreClasses = new List(new string[] { "Progman", "XLMAIN", "Button", "Dwm" }); //"MS-SDIa" + if (ignoreClasses.Contains(window.ClassName)) + { + continue; + } + // Windows without size + Rectangle windowRect = window.WindowRectangle; + windowRect.Intersect(screenBounds); + if (windowRect.Size.IsEmpty) + { + continue; + } + windows.Add(window); + } + return windows; + } + + /// + /// Get the WindowDetails for all Metro Apps + /// These are all Windows with Classname "Windows.UI.Core.CoreWindow" + /// + /// List with visible metro apps + public static List GetMetroApps() + { + List metroApps = new List(); + // if the appVisibility != null we have Windows 8. + if (appVisibility == null) + { + return metroApps; + } + //string[] wcs = {"ImmersiveGutter", "Snapped Desktop", "ImmersiveBackgroundWindow","ImmersiveLauncher","Windows.UI.Core.CoreWindow","ApplicationManager_ImmersiveShellWindow","SearchPane","MetroGhostWindow","EdgeUiInputWndClass", "NativeHWNDHost", "Shell_CharmWindow"}; + //List specials = new List(); + //foreach(string wc in wcs) { + // IntPtr wcHandle = User32.FindWindow(null, null); + // while (wcHandle != IntPtr.Zero) { + // WindowDetails special = new WindowDetails(wcHandle); + // if (special.WindowRectangle.Left >= 1920 && special.WindowRectangle.Size != Size.Empty) { + // specials.Add(special); + // LOG.DebugFormat("Found special {0} : {1} at {2} visible: {3} {4} {5}", special.ClassName, special.Text, special.WindowRectangle, special.Visible, special.ExtendedWindowStyle, special.WindowStyle); + // } + // wcHandle = User32.FindWindowEx(IntPtr.Zero, wcHandle, null, null); + // }; + //} + IntPtr nextHandle = User32.FindWindow(METRO_WINDOWS_CLASS, null); + while (nextHandle != IntPtr.Zero) + { + WindowDetails metroApp = new WindowDetails(nextHandle); + metroApps.Add(metroApp); + // Check if we have a gutter! + if (metroApp.Visible && !metroApp.Maximised) + { + IntPtr gutterHandle = User32.FindWindow(METRO_GUTTER_CLASS, null); + if (gutterHandle != IntPtr.Zero) + { + metroApps.Add(new WindowDetails(gutterHandle)); + } + } + nextHandle = User32.FindWindowEx(IntPtr.Zero, nextHandle, METRO_WINDOWS_CLASS, null); + }; + + return metroApps; + } + + /// + /// Get all the top level windows + /// + /// List with all the top level windows + public static List GetTopLevelWindows() + { + List windows = new List(); + var possibleTopLevelWindows = GetMetroApps(); + possibleTopLevelWindows.AddRange(GetAllWindows()); + foreach (WindowDetails window in possibleTopLevelWindows) + { + // Ignore windows without title + if (window.Text.Length == 0) + { + continue; + } + // Ignore some classes + List ignoreClasses = new List(new string[] { "Progman", "XLMAIN", "Button", "Dwm" }); //"MS-SDIa" + if (ignoreClasses.Contains(window.ClassName)) + { + continue; + } + // Windows without size + if (window.WindowRectangle.Size.IsEmpty) + { + continue; + } + if (window.HasParent) + { + continue; + } + if ((window.ExtendedWindowStyle & ExtendedWindowStyleFlags.WS_EX_TOOLWINDOW) != 0) + { + continue; + } + // Skip preview windows, like the one from Firefox + if ((window.WindowStyle & WindowStyleFlags.WS_VISIBLE) == 0) + { + continue; + } + if (!window.Visible && !window.Iconic) + { + continue; + } + windows.Add(window); + } + return windows; + } + + /// + /// Find a window belonging to the same process as the supplied window. + /// + /// + /// + public static WindowDetails GetLinkedWindow(WindowDetails windowToLinkTo) + { + IntPtr processIdSelectedWindow = windowToLinkTo.ProcessId; + foreach (WindowDetails window in GetAllWindows()) + { + // Ignore windows without title + if (window.Text.Length == 0) + { + continue; + } + // Ignore invisible + if (!window.Visible) + { + continue; + } + if (window.Handle == windowToLinkTo.Handle) + { + continue; + } + if (window.Iconic) + { + continue; + } + + // Windows without size + Size windowSize = window.WindowRectangle.Size; + if (windowSize.Width == 0 || windowSize.Height == 0) + { + continue; + } + + if (window.ProcessId == processIdSelectedWindow) + { + LOG.InfoFormat("Found window {0} belonging to same process as the window {1}", window.Text, windowToLinkTo.Text); + return window; + } + } + return null; + } + + /// + /// Helper method to "active" all windows that are not in the supplied list. + /// One should preferably call "GetVisibleWindows" for the oldWindows. + /// + /// List with old windows + public static void ActiveNewerWindows(List oldWindows) + { + List windowsAfter = GetVisibleWindows(); + foreach (WindowDetails window in windowsAfter) + { + if (!oldWindows.Contains(window)) + { + window.ToForeground(); + } + } + } + + /// + /// Get the AppLauncher + /// + /// + public static WindowDetails GetAppLauncher() + { + // Only if Windows 8 (or higher) + if (appVisibility == null) + { + return null; + } + IntPtr appLauncher = User32.FindWindow(METRO_APPLAUNCHER_CLASS, null); + if (appLauncher != IntPtr.Zero) + { + return new WindowDetails(appLauncher); + } + return null; + } + + /// + /// Return true if the metro-app-launcher is visible + /// + /// + public static bool IsAppLauncherVisible + { + get + { + if (appVisibility != null) + { + return appVisibility.IsLauncherVisible; + } + return false; + } + } + } + + #endregion WindowDetails +} \ No newline at end of file diff --git a/GreenshotImageEditor/Drawing/ArrowContainer.cs b/GreenshotImageEditor/Drawing/ArrowContainer.cs new file mode 100644 index 000000000..115e08a99 --- /dev/null +++ b/GreenshotImageEditor/Drawing/ArrowContainer.cs @@ -0,0 +1,159 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2013 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: http://getgreenshot.org/ + * The Greenshot project is hosted on Sourceforge: http://sourceforge.net/projects/greenshot/ + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using Greenshot.Drawing.Fields; +using Greenshot.Plugin.Drawing; +using System; +using System.Drawing; +using System.Drawing.Drawing2D; + +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) + { + 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 = 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)) + { + pen.Width = lineThickness; + SetArrowHeads((ArrowHeadCombination)GetFieldValue(FieldType.ARROWHEADS), pen); + using (GraphicsPath path = new GraphicsPath()) + { + path.AddLine(Left, Top, Left + Width, Top + Height); + Rectangle drawingBounds = Rectangle.Round(path.GetBounds(new Matrix(), pen)); + drawingBounds.Inflate(2, 2); + return drawingBounds; + } + } + } + else + { + 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)) + { + pen.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); + } + } + } + else + { + return false; + } + } + } +} \ No newline at end of file diff --git a/GreenshotImageEditor/Drawing/CropContainer.cs b/GreenshotImageEditor/Drawing/CropContainer.cs new file mode 100644 index 000000000..85b962c19 --- /dev/null +++ b/GreenshotImageEditor/Drawing/CropContainer.cs @@ -0,0 +1,86 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2013 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: http://getgreenshot.org/ + * The Greenshot project is hosted on Sourceforge: http://sourceforge.net/projects/greenshot/ + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using Greenshot.Drawing.Fields; +using Greenshot.Helpers; +using Greenshot.Plugin.Drawing; +using System.Drawing; + +namespace Greenshot.Drawing +{ + /// + /// Description of CropContainer. + /// + public class CropContainer : DrawableContainer + { + public CropContainer(Surface parent) + : base(parent) + { + AddField(GetType(), FieldType.FLAGS, FieldType.Flag.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 + { + return new Rectangle(0, 0, parent.Width, parent.Height); + } + } + + public override void Draw(Graphics g, RenderMode rm) + { + 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); + + DrawSelectionBorder(g, selectionRect); + + // top + g.FillRectangle(cropBrush, new Rectangle(0, 0, parent.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, parent.Width - (cropRectangle.Left + cropRectangle.Width), cropRectangle.Height)); + // bottom + g.FillRectangle(cropBrush, new Rectangle(0, cropRectangle.Top + cropRectangle.Height, parent.Width, parent.Height - (cropRectangle.Top + cropRectangle.Height))); + } + } + + public override bool hasContextMenu + { + get + { + // No context menu for the CropContainer + return false; + } + } + } +} \ No newline at end of file diff --git a/GreenshotImageEditor/Drawing/CursorContainer.cs b/GreenshotImageEditor/Drawing/CursorContainer.cs new file mode 100644 index 000000000..7b851aeac --- /dev/null +++ b/GreenshotImageEditor/Drawing/CursorContainer.cs @@ -0,0 +1,117 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2013 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: http://getgreenshot.org/ + * The Greenshot project is hosted on Sourceforge: http://sourceforge.net/projects/greenshot/ + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using Greenshot.Plugin.Drawing; +using GreenshotPlugin; +using System; +using System.Drawing; +using System.Drawing.Drawing2D; +using System.IO; +using System.Windows.Forms; + +namespace Greenshot.Drawing +{ + /// + /// Description of CursorContainer. + /// + [Serializable()] + public class CursorContainer : DrawableContainer, ICursorContainer + { + protected Cursor cursor; + + public CursorContainer(Surface parent) + : base(parent) + { + } + + public CursorContainer(Surface parent, string filename) + : base(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)) + { + 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) + { + graphics.SmoothingMode = SmoothingMode.HighQuality; + graphics.InterpolationMode = InterpolationMode.NearestNeighbor; + graphics.CompositingQuality = CompositingQuality.Default; + graphics.PixelOffsetMode = PixelOffsetMode.None; + cursor.DrawStretched(graphics, Bounds); + } + } + + public override Size DefaultSize + { + get + { + return cursor.Size; + } + } + } +} \ No newline at end of file diff --git a/GreenshotImageEditor/Drawing/DrawableContainer.cs b/GreenshotImageEditor/Drawing/DrawableContainer.cs new file mode 100644 index 000000000..5512cb76d --- /dev/null +++ b/GreenshotImageEditor/Drawing/DrawableContainer.cs @@ -0,0 +1,856 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2013 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: http://getgreenshot.org/ + * The Greenshot project is hosted on Sourceforge: http://sourceforge.net/projects/greenshot/ + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using Greenshot.Configuration; +using Greenshot.Drawing.Fields; +using Greenshot.Drawing.Filters; +using Greenshot.Helpers; +using Greenshot.IniFile; +using Greenshot.Memento; +using Greenshot.Plugin; +using Greenshot.Plugin.Drawing; +using GreenshotPlugin; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.Drawing.Drawing2D; +using System.Windows.Forms; + +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, INotifyPropertyChanged, IDrawableContainer + { + protected static readonly EditorConfiguration editorConfig = IniConfig.GetIniSection(); + private bool isMadeUndoable = false; + + public virtual void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) + { + if (disposing) + { + if (grippers != null) + { + for (int i = 0; i < grippers.Length; i++) + { + if (grippers[i] != null) + { + grippers[i].Dispose(); + grippers[i] = null; + } + } + grippers = null; + } + + FieldAggregator aggProps = parent.FieldAggregator; + aggProps.UnbindElement(this); + } + } + + ~DrawableContainer() + { + Dispose(false); + } + + [NonSerialized] + private PropertyChangedEventHandler propertyChanged; + public event PropertyChangedEventHandler PropertyChanged + { + add { propertyChanged += value; } + remove { propertyChanged -= value; } + } + + public List 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 { return parent; } + set { SwitchParent((Surface)value); } + } + [NonSerialized] + protected Gripper[] grippers; + private bool layoutSuspended = false; + + [NonSerialized] + private bool selected = false; + public bool Selected + { + get { return selected; } + set + { + selected = value; + OnPropertyChanged("Selected"); + } + } + + [NonSerialized] + private EditStatus status = EditStatus.UNDRAWN; + public EditStatus Status + { + get + { + return status; + } + set + { + status = value; + } + } + + private int left = 0; + public int Left + { + get { return left; } + set + { + if (value != left) + { + left = value; + DoLayout(); + } + } + } + + private int top = 0; + public int Top + { + get { return top; } + set + { + if (value != top) + { + top = value; + DoLayout(); + } + } + } + + private int width = 0; + public int Width + { + get { return width; } + set + { + if (value != width) + { + width = value; + DoLayout(); + } + } + } + + private int height = 0; + public int Height + { + get { return height; } + set + { + if (value != height) + { + height = value; + DoLayout(); + } + } + } + + public Point Location + { + get + { + return new Point(left, top); + } + } + + public Size Size + { + get + { + return new Size(width, height); + } + } + + [NonSerialized] + /// + /// will store current bounds of this DrawableContainer before starting a resize + /// + private Rectangle boundsBeforeResize = Rectangle.Empty; + + [NonSerialized] + /// + /// "workbench" rectangle - used for calculatoing bounds during resizing (to be applied to this DrawableContainer afterwards) + /// + private RectangleF boundsAfterResize = RectangleF.Empty; + + public Rectangle Bounds + { + get { return 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) + { + this.parent = parent; + InitControls(); + } + + public void Add(IFilter filter) + { + AddChild(filter); + } + + public void Remove(IFilter filter) + { + RemoveChild(filter); + } + + private int round(float f) + { + if (float.IsPositiveInfinity(f) || f > int.MaxValue / 2) return int.MaxValue / 2; + else if (float.IsNegativeInfinity(f) || f < int.MinValue / 2) return int.MinValue / 2; + return (int)Math.Round(f); + } + + private int round(double d) + { + if (Double.IsPositiveInfinity(d) || d > int.MaxValue / 2) return int.MaxValue / 2; + else if (Double.IsNegativeInfinity(d) || d < int.MinValue / 2) return int.MinValue / 2; + else return (int)Math.Round(d); + } + + private bool accountForShadowChange = false; + public virtual Rectangle DrawingBounds + { + get + { + foreach (IFilter filter in Filters) + { + if (filter.Invert) + { + return new Rectangle(Point.Empty, parent.Image.Size); + } + } + // Take a base safetymargin + int lineThickness = 5; + 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() + { + parent.Invalidate(DrawingBounds); + } + + public void AlignToParent(HorizontalAlignment horizontalAlignment, VerticalAlignment verticalAlignment) + { + int lineThickness = GetFieldValueAsInt(FieldType.LINE_THICKNESS); + if (horizontalAlignment == HorizontalAlignment.Left) + { + Left = lineThickness / 2; + } + if (horizontalAlignment == HorizontalAlignment.Right) + { + Left = parent.Width - Width - lineThickness / 2; + } + if (horizontalAlignment == HorizontalAlignment.Center) + { + Left = (parent.Width / 2) - (Width / 2) - lineThickness / 2; + } + + if (verticalAlignment == VerticalAlignment.TOP) + { + Top = lineThickness / 2; + } + if (verticalAlignment == VerticalAlignment.BOTTOM) + { + Top = parent.Height - Height - lineThickness / 2; + } + if (verticalAlignment == VerticalAlignment.CENTER) + { + Top = (parent.Height / 2) - (Height / 2) - lineThickness / 2; + } + } + + public virtual bool InitContent() + { + return true; + } + + public virtual void OnDoubleClick() + { + } + + private void InitControls() + { + InitGrippers(); + + DoLayout(); + } + + protected void InitGrippers() + { + grippers = new Gripper[8]; + for (int i = 0; i < grippers.Length; i++) + { + grippers[i] = new Gripper(); + grippers[i].Position = i; + grippers[i].MouseDown += gripperMouseDown; + grippers[i].MouseUp += gripperMouseUp; + grippers[i].MouseMove += gripperMouseMove; + grippers[i].Visible = false; + grippers[i].Parent = parent; + } + grippers[Gripper.POSITION_TOP_CENTER].Cursor = Cursors.SizeNS; + grippers[Gripper.POSITION_MIDDLE_RIGHT].Cursor = Cursors.SizeWE; + grippers[Gripper.POSITION_BOTTOM_CENTER].Cursor = Cursors.SizeNS; + grippers[Gripper.POSITION_MIDDLE_LEFT].Cursor = Cursors.SizeWE; + if (parent != null) + { + parent.Controls.AddRange(grippers); // otherwise we'll attach them in switchParent + } + } + + public void SuspendLayout() + { + layoutSuspended = true; + } + + public void ResumeLayout() + { + layoutSuspended = false; + DoLayout(); + } + + protected virtual void DoLayout() + { + if (grippers == null) + { + return; + } + if (!layoutSuspended) + { + int center = (Gripper.GripperSize - 1) / 2; + int[] xChoords = new int[] { Left - center, Left + Width / 2 - center, Left + Width - center }; + int[] yChoords = new int[] { Top - center, Top + Height / 2 - center, Top + Height - center }; + + grippers[Gripper.POSITION_TOP_LEFT].Left = xChoords[0]; grippers[Gripper.POSITION_TOP_LEFT].Top = yChoords[0]; + grippers[Gripper.POSITION_TOP_CENTER].Left = xChoords[1]; grippers[Gripper.POSITION_TOP_CENTER].Top = yChoords[0]; + grippers[Gripper.POSITION_TOP_RIGHT].Left = xChoords[2]; grippers[Gripper.POSITION_TOP_RIGHT].Top = yChoords[0]; + grippers[Gripper.POSITION_MIDDLE_RIGHT].Left = xChoords[2]; grippers[Gripper.POSITION_MIDDLE_RIGHT].Top = yChoords[1]; + grippers[Gripper.POSITION_BOTTOM_RIGHT].Left = xChoords[2]; grippers[Gripper.POSITION_BOTTOM_RIGHT].Top = yChoords[2]; + grippers[Gripper.POSITION_BOTTOM_CENTER].Left = xChoords[1]; grippers[Gripper.POSITION_BOTTOM_CENTER].Top = yChoords[2]; + grippers[Gripper.POSITION_BOTTOM_LEFT].Left = xChoords[0]; grippers[Gripper.POSITION_BOTTOM_LEFT].Top = yChoords[2]; + grippers[Gripper.POSITION_MIDDLE_LEFT].Left = xChoords[0]; grippers[Gripper.POSITION_MIDDLE_LEFT].Top = yChoords[1]; + + if ((grippers[Gripper.POSITION_TOP_LEFT].Left < grippers[Gripper.POSITION_BOTTOM_RIGHT].Left && grippers[Gripper.POSITION_TOP_LEFT].Top < grippers[Gripper.POSITION_BOTTOM_RIGHT].Top) || + grippers[Gripper.POSITION_TOP_LEFT].Left > grippers[Gripper.POSITION_BOTTOM_RIGHT].Left && grippers[Gripper.POSITION_TOP_LEFT].Top > grippers[Gripper.POSITION_BOTTOM_RIGHT].Top) + { + grippers[Gripper.POSITION_TOP_LEFT].Cursor = Cursors.SizeNWSE; + grippers[Gripper.POSITION_TOP_RIGHT].Cursor = Cursors.SizeNESW; + grippers[Gripper.POSITION_BOTTOM_RIGHT].Cursor = Cursors.SizeNWSE; + grippers[Gripper.POSITION_BOTTOM_LEFT].Cursor = Cursors.SizeNESW; + } + else if ((grippers[Gripper.POSITION_TOP_LEFT].Left > grippers[Gripper.POSITION_BOTTOM_RIGHT].Left && grippers[Gripper.POSITION_TOP_LEFT].Top < grippers[Gripper.POSITION_BOTTOM_RIGHT].Top) || + grippers[Gripper.POSITION_TOP_LEFT].Left < grippers[Gripper.POSITION_BOTTOM_RIGHT].Left && grippers[Gripper.POSITION_TOP_LEFT].Top > grippers[Gripper.POSITION_BOTTOM_RIGHT].Top) + { + grippers[Gripper.POSITION_TOP_LEFT].Cursor = Cursors.SizeNESW; + grippers[Gripper.POSITION_TOP_RIGHT].Cursor = Cursors.SizeNWSE; + grippers[Gripper.POSITION_BOTTOM_RIGHT].Cursor = Cursors.SizeNESW; + grippers[Gripper.POSITION_BOTTOM_LEFT].Cursor = Cursors.SizeNWSE; + } + else if (grippers[Gripper.POSITION_TOP_LEFT].Left == grippers[Gripper.POSITION_BOTTOM_RIGHT].Left) + { + grippers[Gripper.POSITION_TOP_LEFT].Cursor = Cursors.SizeNS; + grippers[Gripper.POSITION_BOTTOM_RIGHT].Cursor = Cursors.SizeNS; + } + else if (grippers[Gripper.POSITION_TOP_LEFT].Top == grippers[Gripper.POSITION_BOTTOM_RIGHT].Top) + { + grippers[Gripper.POSITION_TOP_LEFT].Cursor = Cursors.SizeWE; + grippers[Gripper.POSITION_BOTTOM_RIGHT].Cursor = Cursors.SizeWE; + } + } + } + + private int mx; + private int my; + + private void gripperMouseDown(object sender, MouseEventArgs e) + { + mx = e.X; + my = e.Y; + Status = EditStatus.RESIZING; + boundsBeforeResize = new Rectangle(left, top, width, height); + boundsAfterResize = new RectangleF(boundsBeforeResize.Left, boundsBeforeResize.Top, boundsBeforeResize.Width, boundsBeforeResize.Height); + isMadeUndoable = false; + } + + private void gripperMouseUp(object sender, MouseEventArgs e) + { + Status = EditStatus.IDLE; + boundsBeforeResize = Rectangle.Empty; + boundsAfterResize = RectangleF.Empty; + isMadeUndoable = false; + Invalidate(); + } + + private void gripperMouseMove(object sender, MouseEventArgs e) + { + if (Status.Equals(EditStatus.RESIZING)) + { + // check if we already made this undoable + if (!isMadeUndoable) + { + // don't allow another undo until we are finished with this move + isMadeUndoable = true; + // Make undo-able + MakeBoundsChangeUndoable(false); + } + + Invalidate(); + SuspendLayout(); + + Gripper gr = (Gripper)sender; + int absX = gr.Left + e.X; + int absY = gr.Top + e.Y; + + // 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, gr.Position, new PointF(absX, absY), ScaleHelper.GetScaleOptions()); + + // apply scaled bounds to this DrawableContainer + ApplyBounds(boundsAfterResize); + + ResumeLayout(); + Invalidate(); + } + } + + private void childLabelMouseMove(object sender, MouseEventArgs e) + { + if (Status.Equals(EditStatus.RESIZING)) + { + Invalidate(); + SuspendLayout(); + Left += e.X - mx; + Top += e.Y - my; + ResumeLayout(); + Invalidate(); + } + } + + public bool hasFilters + { + get + { + return 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); + filter.Apply(graphics, bmp, drawingRect, renderMode); + } + } + } + } + } + Draw(graphics, renderMode); + } + + 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)) + { + pen.DashPattern = new float[] { 1, 2 }; + pen.Width = 1; + g.DrawRectangle(pen, rect); + } + } + + public virtual void ShowGrippers() + { + if (grippers != null) + { + for (int i = 0; i < grippers.Length; i++) + { + if (grippers[i].Enabled) + { + grippers[i].Show(); + } + else + { + grippers[i].Hide(); + } + } + } + ResumeLayout(); + } + + public void HideGrippers() + { + SuspendLayout(); + if (grippers != null) + { + for (int i = 0; i < grippers.Length; i++) + { + grippers[i].Hide(); + } + } + } + + public void ResizeTo(int width, int height, int anchorPosition) + { + SuspendLayout(); + Width = width; + Height = height; + ResumeLayout(); + } + + /// + /// 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) + { + SuspendLayout(); + Left += dx; + Top += dy; + ResumeLayout(); + } + + /// + /// 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(); + SuspendLayout(); + + // 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); + + ResumeLayout(); + Invalidate(); + return true; + } + + /// + /// A handler for the MouseUp + /// + /// current mouse x + /// current mouse y + public virtual void HandleMouseUp(int x, int y) + { + } + + private void SwitchParent(Surface newParent) + { + if (parent != null && grippers != null) + { + for (int i = 0; i < grippers.Length; i++) + { + parent.Controls.Remove(grippers[i]); + } + } + else if (grippers == null) + { + InitControls(); + } + parent = newParent; + parent.Controls.AddRange(grippers); + foreach (IFilter filter in Filters) + { + filter.Parent = this; + } + } + + // drawablecontainers are regarded equal if they are of the same type and their bounds are equal. this should be sufficient. + public override bool Equals(object obj) + { + bool ret = false; + if (obj != null && GetType().Equals(obj.GetType())) + { + DrawableContainer other = obj as DrawableContainer; + if (left == other.left && top == other.top && width == other.width && height == other.height) + { + ret = true; + } + } + return ret; + } + + public override int GetHashCode() + { + return left.GetHashCode() ^ top.GetHashCode() ^ width.GetHashCode() ^ height.GetHashCode() ^ GetFields().GetHashCode(); + } + + 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(Field 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 (e.Field.FieldType == FieldType.SHADOW) + { + accountForShadowChange = true; + } + Invalidate(); + } + + public virtual bool CanRotate + { + get + { + return true; + } + } + + public virtual void Rotate(RotateFlipType rotateFlipType) + { + // somehow the rotation is the wrong way? + int angle = 90; + if (RotateFlipType.Rotate90FlipNone == rotateFlipType) + { + angle = 270; + } + + Rectangle beforeBounds = new Rectangle(Left, Top, Width, Height); + LOG.DebugFormat("Bounds before: {0}", beforeBounds); + GraphicsPath translatePath = new GraphicsPath(); + translatePath.AddRectangle(beforeBounds); + Matrix rotateMatrix = new Matrix(); + rotateMatrix.RotateAt(angle, new PointF(parent.Width >> 1, parent.Height >> 1)); + translatePath.Transform(rotateMatrix); + RectangleF newBounds = translatePath.GetBounds(); + LOG.DebugFormat("New bounds by using graphics path: {0}", newBounds); + + int ox = 0; + int oy = 0; + int centerX = parent.Width >> 1; + int centerY = parent.Height >> 1; + // Transform from screen to normal coordinates + int px = Left - centerX; + int py = centerY - Top; + double theta = Math.PI * angle / 180.0; + double x1 = Math.Cos(theta) * (px - ox) - Math.Sin(theta) * (py - oy) + ox; + double y1 = Math.Sin(theta) * (px - ox) + Math.Cos(theta) * (py - oy) + oy; + + // Transform from screen to normal coordinates + px = (Left + Width) - centerX; + py = centerY - (Top + Height); + + double x2 = Math.Cos(theta) * (px - ox) - Math.Sin(theta) * (py - oy) + ox; + double y2 = Math.Sin(theta) * (px - ox) + Math.Cos(theta) * (py - oy) + oy; + + // Transform back to screen coordinates, as we rotate the bitmap we need to switch the center X&Y + x1 += centerY; + y1 = centerX - y1; + x2 += centerY; + y2 = centerX - y2; + + // Calculate to rectangle + double newWidth = x2 - x1; + double newHeight = y2 - y1; + + RectangleF newRectangle = new RectangleF( + (float)x1, + (float)y1, + (float)newWidth, + (float)newHeight + ); + ApplyBounds(newRectangle); + LOG.DebugFormat("New bounds by using old method: {0}", newRectangle); + } + + protected virtual ScaleHelper.IDoubleProcessor GetAngleRoundProcessor() + { + return ScaleHelper.ShapeAngleRoundBehavior.Instance; + } + + public virtual bool hasContextMenu + { + get + { + return true; + } + } + + public virtual bool hasDefaultSize + { + get + { + return false; + } + } + + public virtual Size DefaultSize + { + get + { + throw new NotSupportedException("Object doesn't have a default size"); + } + } + } +} \ No newline at end of file diff --git a/GreenshotImageEditor/Drawing/DrawableContainerList.cs b/GreenshotImageEditor/Drawing/DrawableContainerList.cs new file mode 100644 index 000000000..f5b0b46e4 --- /dev/null +++ b/GreenshotImageEditor/Drawing/DrawableContainerList.cs @@ -0,0 +1,621 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2013 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: http://getgreenshot.org/ + * The Greenshot project is hosted on Sourceforge: http://sourceforge.net/projects/greenshot/ + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using System.ComponentModel; +using System.Threading; +using Greenshot.Drawing.Fields; +using Greenshot.IniFile; +using Greenshot.Memento; +using Greenshot.Plugin; +using Greenshot.Plugin.Drawing; +using GreenshotPlugin.Core; +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Windows.Forms; + +namespace Greenshot.Drawing +{ + /// + /// Dispatches most of a DrawableContainer's public properties and methods to a list of DrawableContainers. + /// + [Serializable()] + public class DrawableContainerList : List + { + private static CoreConfiguration conf = IniConfig.GetIniSection(); + private static ComponentResourceManager editorFormResources = new ComponentResourceManager(typeof(ImageEditorForm)); + + public DrawableContainerList() + { + } + + public EditStatus Status + { + get + { + return this[Count - 1].Status; + } + set + { + foreach (DrawableContainer 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 (DrawableContainer dc in this) + { + ret &= dc.Selected; + } + return ret; + } + set + { + foreach (DrawableContainer 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 + { + foreach (DrawableContainer dc in this) + { + 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) + { + List movingList = new List(); + Surface surface = null; + foreach (DrawableContainer dc in this) + { + movingList.Add(dc); + surface = dc.parent; + } + if (movingList.Count > 0 && surface != null) + { + surface.MakeUndoable(new DrawableContainerBoundsChangeMemento(movingList), allowMerge); + } + } + + /// + /// 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 (DrawableContainer 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; + } + } + + /// + /// Hides the grippers of all elements in the list. + /// + public void HideGrippers() + { + foreach (DrawableContainer dc in this) + { + dc.HideGrippers(); + dc.Invalidate(); + } + } + + /// + /// Shows the grippers of all elements in the list. + /// + public void ShowGrippers() + { + foreach (DrawableContainer dc in this) + { + dc.ShowGrippers(); + dc.Invalidate(); + } + } + + /// + /// 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 (DrawableContainer 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 (DrawableContainer dc in this) + { + 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 (DrawableContainer 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 (DrawableContainer dc in this) + { + if (dc.DrawingBounds.IntersectsWith(clipRectangle)) + { + return true; + } + } + return false; + } + + /// + /// Triggers all elements in the list ot be redrawn. + /// + /// the related Graphics object + /// the rendermode in which the element is to be drawn + public void Draw(Graphics g, Bitmap bitmap, RenderMode renderMode, Rectangle clipRectangle) + { + foreach (DrawableContainer dc in this) + { + 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 (DrawableContainer dc in this) + { + dc.HandleFieldChanged(sender, e); + } + } + + /// + /// Invalidate the bounds of all the DC's in this list + /// + public void Invalidate() + { + foreach (DrawableContainer dc in this) + { + dc.Invalidate(); + } + } + + /// + /// 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(DrawableContainerList elements) + { + if (elements.Count == 0 || elements.Count == Count) + { + return false; + } + foreach (DrawableContainer 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(DrawableContainerList elements) + { + for (int i = Count - 1; i >= 0; i--) + { + IDrawableContainer dc = this[i]; + if (elements.Contains(dc)) + { + 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(DrawableContainerList elements) + { + IDrawableContainer[] dcs = ToArray(); + for (int i = 0; i < dcs.Length; i++) + { + IDrawableContainer dc = dcs[i]; + if (elements.Contains(dc)) + { + 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(DrawableContainerList elements) + { + if (elements.Count == 0 || elements.Count == Count) + { + return false; + } + foreach (DrawableContainer 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(DrawableContainerList elements) + { + for (int i = 0; i < Count; i++) + { + IDrawableContainer dc = this[i]; + if (elements.Contains(dc)) + { + 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(DrawableContainerList elements) + { + IDrawableContainer[] dcs = ToArray(); + for (int i = dcs.Length - 1; i >= 0; i--) + { + IDrawableContainer dc = dcs[i]; + if (elements.Contains(dc)) + { + 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) + { + IDrawableContainer 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, Surface surface) + { + bool push = surface.Elements.CanPushDown(this); + bool pull = surface.Elements.CanPullUp(this); + + ToolStripMenuItem item; + + // Pull "up" + if (pull) + { + item = new ToolStripMenuItem("Up to top"); + item.Click += delegate + { + surface.Elements.PullElementsToTop(this); + surface.Elements.Invalidate(); + }; + menu.Items.Add(item); + item = new ToolStripMenuItem("Up one level"); + item.Click += delegate + { + surface.Elements.PullElementsUp(this); + surface.Elements.Invalidate(); + }; + menu.Items.Add(item); + } + // Push "down" + if (push) + { + item = new ToolStripMenuItem("Down to bottom"); + item.Click += delegate + { + surface.Elements.PushElementsToBottom(this); + surface.Elements.Invalidate(); + }; + menu.Items.Add(item); + item = new ToolStripMenuItem("Down one level"); + item.Click += delegate + { + surface.Elements.PushElementsDown(this); + surface.Elements.Invalidate(); + }; + menu.Items.Add(item); + } + + // Duplicate + item = new ToolStripMenuItem("Duplicate selected element"); + item.Click += delegate + { + DrawableContainerList 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("Copy"); + item.Image = ((Image)(editorFormResources.GetObject("copyToolStripMenuItem.Image"))); + item.Click += delegate + { + ClipboardHelper.SetClipboardData(typeof(DrawableContainerList), this); + }; + menu.Items.Add(item); + + // Cut + item = new ToolStripMenuItem("Cut"); + item.Image = ((Image)(editorFormResources.GetObject("btnCut.Image"))); + item.Click += delegate + { + ClipboardHelper.SetClipboardData(typeof(DrawableContainerList), this); + List containersToDelete = new List(); + foreach (DrawableContainer container in this) + { + containersToDelete.Add(container); + } + foreach (DrawableContainer container in containersToDelete) + { + surface.RemoveElement(container, true); + } + }; + menu.Items.Add(item); + + // Delete + item = new ToolStripMenuItem("Delete"); + item.Image = ((Image)(editorFormResources.GetObject("removeObjectToolStripMenuItem.Image"))); + item.Click += delegate + { + List containersToDelete = new List(); + foreach (DrawableContainer container in this) + { + containersToDelete.Add(container); + } + foreach (DrawableContainer container in containersToDelete) + { + surface.RemoveElement(container, true); + } + }; + menu.Items.Add(item); + + // Reset + bool canReset = false; + foreach (DrawableContainer container in this) + { + if (container.hasDefaultSize) + { + canReset = true; + } + } + if (canReset) + { + item = new ToolStripMenuItem("Reset size"); + //item.Image = ((System.Drawing.Image)(editorFormResources.GetObject("removeObjectToolStripMenuItem.Image"))); + item.Click += delegate + { + foreach (DrawableContainer container in this) + { + if (container.hasDefaultSize) + { + Size defaultSize = container.DefaultSize; + container.Invalidate(); + container.MakeBoundsChangeUndoable(false); + container.Width = defaultSize.Width; + container.Height = defaultSize.Height; + container.Invalidate(); + } + } + }; + menu.Items.Add(item); + } + } + + public virtual void ShowContextMenu(MouseEventArgs e, Surface surface) + { + bool hasMenu = false; + foreach (DrawableContainer container in this) + { + if (container.hasContextMenu) + { + hasMenu = true; + break; + } + } + if (hasMenu) + { + ContextMenuStrip menu = new ContextMenuStrip(); + AddContextMenuItems(menu, surface); + if (menu.Items.Count > 0) + { + menu.Show(surface, e.Location); + while (true) + { + if (menu.Visible) + { + Application.DoEvents(); + Thread.Sleep(100); + } + else + { + menu.Dispose(); + break; + } + } + } + } + } + } +} \ No newline at end of file diff --git a/GreenshotImageEditor/Drawing/EllipseContainer.cs b/GreenshotImageEditor/Drawing/EllipseContainer.cs new file mode 100644 index 000000000..d92d2422a --- /dev/null +++ b/GreenshotImageEditor/Drawing/EllipseContainer.cs @@ -0,0 +1,136 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2013 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: http://getgreenshot.org/ + * The Greenshot project is hosted on Sourceforge: http://sourceforge.net/projects/greenshot/ + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using Greenshot.Drawing.Fields; +using Greenshot.Helpers; +using Greenshot.Plugin.Drawing; +using System; +using System.Drawing; +using System.Drawing.Drawing2D; + +namespace Greenshot.Drawing +{ + /// + /// Description of EllipseContainer. + /// + [Serializable()] + public class EllipseContainer : DrawableContainer + { + public EllipseContainer(Surface parent) + : base(parent) + { + 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); + 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))) + { + shadowPen.Width = lineVisible ? lineThickness : 1; + Rectangle shadowRect = GuiRectangle.GetGuiRectangle(Left + currentStep, Top + currentStep, Width, Height); + graphics.DrawEllipse(shadowPen, shadowRect); + currentStep++; + alpha = alpha - basealpha / steps; + } + } + } + //draw the original shape + Rectangle rect = GuiRectangle.GetGuiRectangle(Left, Top, Width, Height); + 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) + { + double xDistanceFromCenter = x - (Left + Width / 2); + double yDistanceFromCenter = y - (Top + Height / 2); + // ellipse: x^2/a^2 + y^2/b^2 = 1 + return Math.Pow(xDistanceFromCenter, 2) / Math.Pow(Width / 2, 2) + Math.Pow(yDistanceFromCenter, 2) / Math.Pow(Height / 2, 2) < 1; + } + + 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); + + // If we clicked inside the rectangle and it's visible we are clickable at. + if (!Color.Transparent.Equals(fillColor)) + { + if (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); + } + } + } + else + { + return false; + } + } + } +} \ No newline at end of file diff --git a/GreenshotImageEditor/Drawing/Fields/AbstractFieldHolder.cs b/GreenshotImageEditor/Drawing/Fields/AbstractFieldHolder.cs new file mode 100644 index 000000000..c2365b400 --- /dev/null +++ b/GreenshotImageEditor/Drawing/Fields/AbstractFieldHolder.cs @@ -0,0 +1,203 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2013 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: http://getgreenshot.org/ + * The Greenshot project is hosted on Sourceforge: http://sourceforge.net/projects/greenshot/ + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using Greenshot.Configuration; +using Greenshot.IniFile; +using GreenshotPlugin; +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Runtime.Serialization; + +namespace Greenshot.Drawing.Fields +{ + /// + /// Basic IFieldHolder implementation, providing access to a set of fields + /// + [Serializable()] + public abstract class AbstractFieldHolder : IFieldHolder + { + private static EditorConfiguration editorConfiguration = IniConfig.GetIniSection(); + + /// + /// called when a field's value has changed + /// + [NonSerialized] + private FieldChangedEventHandler fieldChanged; + public event FieldChangedEventHandler FieldChanged + { + add { fieldChanged += value; } + remove { fieldChanged -= value; } + } + + // we keep to Coolections of our fields, dictionary for quick access, list for serialization + // this allows us to use default serialization + [NonSerialized] + private Dictionary fieldsByType = new Dictionary(); + private List fields = new List(); + + public AbstractFieldHolder() + { + } + + [OnDeserialized()] + private void OnDeserialized(StreamingContext context) + { + fieldsByType = new Dictionary(); + // listen to changing properties + foreach (Field field in fields) + { + field.PropertyChanged += delegate + { + if (fieldChanged != null) + { + fieldChanged(this, new FieldChangedEventArgs(field)); + } + }; + fieldsByType[field.FieldType] = field; + } + } + + public void AddField(Type requestingType, FieldType fieldType, object fieldValue) + { + AddField(editorConfiguration.CreateField(requestingType, fieldType, fieldValue)); + } + + public virtual void AddField(Field field) + { + if (fieldsByType != null && 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()); + } + } + + fields.Add(field); + fieldsByType[field.FieldType] = field; + field.PropertyChanged += delegate { if (fieldChanged != null) fieldChanged(this, new FieldChangedEventArgs(field)); }; + } + + public void RemoveField(Field field) + { + fields.Remove(field); + fieldsByType.Remove(field.FieldType); + field.PropertyChanged -= delegate + { + if (fieldChanged != null) + { + fieldChanged(this, new FieldChangedEventArgs(field)); + } + }; + } + + public List GetFields() + { + return fields; + } + + public Field GetField(FieldType fieldType) + { + try + { + return fieldsByType[fieldType]; + } + catch (KeyNotFoundException e) + { + throw new ArgumentException("Field '" + fieldType + "' does not exist in " + GetType(), e); + } + } + + public object GetFieldValue(FieldType fieldType) + { + return GetField(fieldType).Value; + } + + #region convenience methods to save us some casts outside + + public string GetFieldValueAsString(FieldType fieldType) + { + return Convert.ToString(GetFieldValue(fieldType)); + } + + public int GetFieldValueAsInt(FieldType fieldType) + { + return Convert.ToInt32(GetFieldValue(fieldType)); + } + + public decimal GetFieldValueAsDecimal(FieldType fieldType) + { + return Convert.ToDecimal(GetFieldValue(fieldType)); + } + + public double GetFieldValueAsDouble(FieldType fieldType) + { + return Convert.ToDouble(GetFieldValue(fieldType)); + } + + public float GetFieldValueAsFloat(FieldType fieldType) + { + return Convert.ToSingle(GetFieldValue(fieldType)); + } + + public bool GetFieldValueAsBool(FieldType fieldType) + { + return Convert.ToBoolean(GetFieldValue(fieldType)); + } + + public Color GetFieldValueAsColor(FieldType fieldType) + { + return (Color)GetFieldValue(fieldType); + } + + #endregion convenience methods to save us some casts outside + + public bool HasField(FieldType fieldType) + { + return fieldsByType.ContainsKey(fieldType); + } + + public bool HasFieldValue(FieldType fieldType) + { + return HasField(fieldType) && fieldsByType[fieldType].HasValue; + } + + public void SetFieldValue(FieldType 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) + { + if (fieldChanged != null) + { + fieldChanged(sender, e); + } + } + } +} \ No newline at end of file diff --git a/GreenshotImageEditor/Drawing/Fields/AbstractFieldHolderWithChildren.cs b/GreenshotImageEditor/Drawing/Fields/AbstractFieldHolderWithChildren.cs new file mode 100644 index 000000000..fc9618e9a --- /dev/null +++ b/GreenshotImageEditor/Drawing/Fields/AbstractFieldHolderWithChildren.cs @@ -0,0 +1,143 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2013 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: http://getgreenshot.org/ + * The Greenshot project is hosted on Sourceforge: http://sourceforge.net/projects/greenshot/ + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using System; +using System.Collections.Generic; +using System.Runtime.Serialization; + +namespace Greenshot.Drawing.Fields +{ + /// + /// Basic IFieldHolderWithChildren implementation. Similar to IFieldHolder, + /// but has a List of children. + /// Field values are passed to and from children as well. + /// + [Serializable()] + public abstract class AbstractFieldHolderWithChildren : AbstractFieldHolder + { + private FieldChangedEventHandler fieldChangedEventHandler; + + [NonSerialized] + private EventHandler childrenChanged; + public event EventHandler ChildrenChanged + { + add { childrenChanged += value; } + remove { childrenChanged -= value; } + } + + public List 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; + } + if (childrenChanged != null) childrenChanged(this, EventArgs.Empty); + } + + public void AddChild(IFieldHolder fieldHolder) + { + Children.Add(fieldHolder); + fieldHolder.FieldChanged += fieldChangedEventHandler; + if (childrenChanged != null) childrenChanged(this, EventArgs.Empty); + } + + public void RemoveChild(IFieldHolder fieldHolder) + { + Children.Remove(fieldHolder); + fieldHolder.FieldChanged -= fieldChangedEventHandler; + if (childrenChanged != null) childrenChanged(this, EventArgs.Empty); + } + + public new List GetFields() + { + List ret = new List(); + ret.AddRange(base.GetFields()); + foreach (IFieldHolder fh in Children) + { + ret.AddRange(fh.GetFields()); + } + return ret; + } + + public new Field GetField(FieldType fieldType) + { + Field 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(FieldType 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(FieldType fieldType) + { + Field f = GetField(fieldType); + return f != null && f.HasValue; + } + + public new void SetFieldValue(FieldType fieldType, object value) + { + Field f = GetField(fieldType); + if (f != null) f.Value = value; + } + } +} \ No newline at end of file diff --git a/GreenshotImageEditor/Drawing/Fields/Binding/AbstractBindingConverter.cs b/GreenshotImageEditor/Drawing/Fields/Binding/AbstractBindingConverter.cs new file mode 100644 index 000000000..8177933b4 --- /dev/null +++ b/GreenshotImageEditor/Drawing/Fields/Binding/AbstractBindingConverter.cs @@ -0,0 +1,59 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2013 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: http://getgreenshot.org/ + * The Greenshot project is hosted on Sourceforge: http://sourceforge.net/projects/greenshot/ + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using System; + +namespace Greenshot.Drawing.Fields.Binding +{ + /// + /// Basic IBindingConverter implementation + /// + public abstract class AbstractBindingConverter : IBindingConverter + { + public AbstractBindingConverter() + { + } + + public object convert(object o) + { + if (o == null) + { + return null; + } + else if (o is T1) + { + return convert((T1)o); + } + else if (o is T2) + { + return convert((T2)o); + } + else + { + 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/GreenshotImageEditor/Drawing/Fields/Binding/AlignmentConverter.cs b/GreenshotImageEditor/Drawing/Fields/Binding/AlignmentConverter.cs new file mode 100644 index 000000000..9c4134e44 --- /dev/null +++ b/GreenshotImageEditor/Drawing/Fields/Binding/AlignmentConverter.cs @@ -0,0 +1,101 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2013 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: http://getgreenshot.org/ + * The Greenshot project is hosted on Sourceforge: http://sourceforge.net/projects/greenshot/ + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using Greenshot.Plugin; +using System; +using System.Drawing; +using System.Windows.Forms; + +namespace Greenshot.Drawing.Fields.Binding +{ + /// + /// Converting horizontal alignment to its StringAlignment representation and vice versa. + /// Beware: there's currently no RTL support. + /// + public class HorizontalAlignmentConverter : AbstractBindingConverter + { + private static HorizontalAlignmentConverter uniqueInstance; + + protected override HorizontalAlignment convert(StringAlignment stringAlignment) + { + switch (stringAlignment) + { + case StringAlignment.Near: return HorizontalAlignment.Left; + case StringAlignment.Center: return HorizontalAlignment.Center; + case StringAlignment.Far: return HorizontalAlignment.Right; + default: throw new NotImplementedException("Cannot handle: " + stringAlignment); + } + } + + protected override StringAlignment convert(HorizontalAlignment horizontalAligment) + { + switch (horizontalAligment) + { + case HorizontalAlignment.Left: return StringAlignment.Near; + case HorizontalAlignment.Center: return StringAlignment.Center; + case HorizontalAlignment.Right: return StringAlignment.Far; + default: throw new NotImplementedException("Cannot handle: " + horizontalAligment); + } + } + + public static HorizontalAlignmentConverter GetInstance() + { + if (uniqueInstance == null) uniqueInstance = new HorizontalAlignmentConverter(); + return uniqueInstance; + } + } + + /// + /// Converting vertical alignment to its StringAlignment representation and vice versa. + /// + public class VerticalAlignmentConverter : AbstractBindingConverter + { + private static VerticalAlignmentConverter uniqueInstance; + + protected override VerticalAlignment convert(StringAlignment stringAlignment) + { + switch (stringAlignment) + { + case StringAlignment.Near: return VerticalAlignment.TOP; + case StringAlignment.Center: return VerticalAlignment.CENTER; + case StringAlignment.Far: return VerticalAlignment.BOTTOM; + default: throw new NotImplementedException("Cannot handle: " + stringAlignment); + } + } + + protected override StringAlignment convert(VerticalAlignment verticalAligment) + { + switch (verticalAligment) + { + case VerticalAlignment.TOP: return StringAlignment.Near; + case VerticalAlignment.CENTER: return StringAlignment.Center; + case VerticalAlignment.BOTTOM: return StringAlignment.Far; + default: throw new NotImplementedException("Cannot handle: " + verticalAligment); + } + } + + public static VerticalAlignmentConverter GetInstance() + { + if (uniqueInstance == null) uniqueInstance = new VerticalAlignmentConverter(); + return uniqueInstance; + } + } +} \ No newline at end of file diff --git a/GreenshotImageEditor/Drawing/Fields/Binding/BidirectionalBinding.cs b/GreenshotImageEditor/Drawing/Fields/Binding/BidirectionalBinding.cs new file mode 100644 index 000000000..6dda62913 --- /dev/null +++ b/GreenshotImageEditor/Drawing/Fields/Binding/BidirectionalBinding.cs @@ -0,0 +1,180 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2013 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: http://getgreenshot.org/ + * The Greenshot project is hosted on Sourceforge: http://sourceforge.net/projects/greenshot/ + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +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 INotifyPropertyChanged controlObject; + private INotifyPropertyChanged fieldObject; + private string controlPropertyName; + private string fieldPropertyName; + private bool updatingControl = false; + private bool updatingField = false; + private IBindingConverter converter; + private 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) + { + this.controlObject = controlObject; + this.fieldObject = fieldObject; + this.controlPropertyName = controlPropertyName; + this.fieldPropertyName = fieldPropertyName; + + this.controlObject.PropertyChanged += ControlPropertyChanged; + this.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 synchronzied 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) + { + this.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 synchronisation 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) + { + this.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 synchronzied value to the correct target format and back + /// validator to intercept synchronisation 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) + { + this.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 != null) ? bValue.GetType().Name : "") + "] on " + targetObject + ". Probably other type than expected, IBindingCoverter to the rescue.", e); + } + } + } + + private 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/GreenshotImageEditor/Drawing/Fields/Binding/DecimalDoublePercentageConverter.cs b/GreenshotImageEditor/Drawing/Fields/Binding/DecimalDoublePercentageConverter.cs new file mode 100644 index 000000000..4d451451b --- /dev/null +++ b/GreenshotImageEditor/Drawing/Fields/Binding/DecimalDoublePercentageConverter.cs @@ -0,0 +1,53 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2013 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: http://getgreenshot.org/ + * The Greenshot project is hosted on Sourceforge: http://sourceforge.net/projects/greenshot/ + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using System; + +namespace Greenshot.Drawing.Fields.Binding +{ + /// + /// Converts decimal to double (%) and vice versa, e.g. 95f <---> 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() + { + if (uniqueInstance == null) uniqueInstance = new DecimalDoublePercentageConverter(); + return uniqueInstance; + } + } +} \ No newline at end of file diff --git a/GreenshotImageEditor/Drawing/Fields/Binding/DecimalFloatConverter.cs b/GreenshotImageEditor/Drawing/Fields/Binding/DecimalFloatConverter.cs new file mode 100644 index 000000000..7e01a2e78 --- /dev/null +++ b/GreenshotImageEditor/Drawing/Fields/Binding/DecimalFloatConverter.cs @@ -0,0 +1,53 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2013 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: http://getgreenshot.org/ + * The Greenshot project is hosted on Sourceforge: http://sourceforge.net/projects/greenshot/ + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +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.ToInt16(o); + } + + public static DecimalFloatConverter GetInstance() + { + if (uniqueInstance == null) uniqueInstance = new DecimalFloatConverter(); + return uniqueInstance; + } + } +} \ No newline at end of file diff --git a/GreenshotImageEditor/Drawing/Fields/Binding/DecimalIntConverter.cs b/GreenshotImageEditor/Drawing/Fields/Binding/DecimalIntConverter.cs new file mode 100644 index 000000000..db7766606 --- /dev/null +++ b/GreenshotImageEditor/Drawing/Fields/Binding/DecimalIntConverter.cs @@ -0,0 +1,53 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2013 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: http://getgreenshot.org/ + * The Greenshot project is hosted on Sourceforge: http://sourceforge.net/projects/greenshot/ + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +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.ToInt16(o); + } + + public static DecimalIntConverter GetInstance() + { + if (uniqueInstance == null) uniqueInstance = new DecimalIntConverter(); + return uniqueInstance; + } + } +} \ No newline at end of file diff --git a/GreenshotImageEditor/Drawing/Fields/Binding/IBindingConverter.cs b/GreenshotImageEditor/Drawing/Fields/Binding/IBindingConverter.cs new file mode 100644 index 000000000..b7cb8a61f --- /dev/null +++ b/GreenshotImageEditor/Drawing/Fields/Binding/IBindingConverter.cs @@ -0,0 +1,33 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2013 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: http://getgreenshot.org/ + * The Greenshot project is hosted on Sourceforge: http://sourceforge.net/projects/greenshot/ + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +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); + } +} \ No newline at end of file diff --git a/GreenshotImageEditor/Drawing/Fields/Binding/IBindingValidator.cs b/GreenshotImageEditor/Drawing/Fields/Binding/IBindingValidator.cs new file mode 100644 index 000000000..3e0afd836 --- /dev/null +++ b/GreenshotImageEditor/Drawing/Fields/Binding/IBindingValidator.cs @@ -0,0 +1,34 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2013 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: http://getgreenshot.org/ + * The Greenshot project is hosted on Sourceforge: http://sourceforge.net/projects/greenshot/ + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +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); + } +} \ No newline at end of file diff --git a/GreenshotImageEditor/Drawing/Fields/Binding/NotNullValidator.cs b/GreenshotImageEditor/Drawing/Fields/Binding/NotNullValidator.cs new file mode 100644 index 000000000..954ce13c3 --- /dev/null +++ b/GreenshotImageEditor/Drawing/Fields/Binding/NotNullValidator.cs @@ -0,0 +1,46 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2013 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: http://getgreenshot.org/ + * The Greenshot project is hosted on Sourceforge: http://sourceforge.net/projects/greenshot/ + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +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() + { + if (uniqueInstance == null) uniqueInstance = new NotNullValidator(); + return uniqueInstance; + } + } +} \ No newline at end of file diff --git a/GreenshotImageEditor/Drawing/Fields/Field.cs b/GreenshotImageEditor/Drawing/Fields/Field.cs new file mode 100644 index 000000000..b2afbfc4a --- /dev/null +++ b/GreenshotImageEditor/Drawing/Fields/Field.cs @@ -0,0 +1,151 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2013 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: http://getgreenshot.org/ + * The Greenshot project is hosted on Sourceforge: http://sourceforge.net/projects/greenshot/ + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using System; +using System.ComponentModel; + +namespace Greenshot.Drawing.Fields +{ + /// + /// Represents a single field of a drawable element, i.e. + /// line thickness of a rectangle. + /// + [Serializable] + public class Field : INotifyPropertyChanged + { + [field: NonSerialized] + public event PropertyChangedEventHandler PropertyChanged; + + public object myValue; + public object Value + { + get + { + return myValue; + } + set + { + if (!Equals(myValue, value)) + { + myValue = value; + if (PropertyChanged != null) + { + PropertyChanged(this, new PropertyChangedEventArgs("Value")); + } + } + } + } + public FieldType FieldType; + public string Scope; + + /// + /// 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(FieldType fieldType, Type scope) + { + FieldType = fieldType; + Scope = scope.Name; + } + + public Field(FieldType fieldType, string scope) + { + FieldType = fieldType; + Scope = scope; + } + + public Field(FieldType fieldType) + { + FieldType = fieldType; + } + + /// + /// Returns true if this field holds a value other than null. + /// + public bool HasValue + { + get { return Value != null; } + } + + /// + /// Creates a flat clone of this Field. The fields value itself is not cloned. + /// + /// + public Field Clone() + { + Field ret = new Field(FieldType, Scope); + ret.Value = Value; + return ret; + } + + 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) + { + Field other = obj as Field; + if (other == null) + { + 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); + } + } + + /// + /// EventHandler to be used when a field value changes + /// + public delegate void FieldChangedEventHandler(object sender, FieldChangedEventArgs e); + + /// + /// EventArgs to be used with FieldChangedEventHandler + /// + public class FieldChangedEventArgs : EventArgs + { + public readonly Field Field; + + public FieldChangedEventArgs(Field field) + { + Field = field; + } + } +} \ No newline at end of file diff --git a/GreenshotImageEditor/Drawing/Fields/FieldAggregator.cs b/GreenshotImageEditor/Drawing/Fields/FieldAggregator.cs new file mode 100644 index 000000000..eaf52b3ea --- /dev/null +++ b/GreenshotImageEditor/Drawing/Fields/FieldAggregator.cs @@ -0,0 +1,218 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2013 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: http://getgreenshot.org/ + * The Greenshot project is hosted on Sourceforge: http://sourceforge.net/projects/greenshot/ + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using Greenshot.Configuration; +using Greenshot.IniFile; +using Greenshot.Plugin.Drawing; +using System.Collections.Generic; +using System.ComponentModel; + +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. + /// + public class FieldAggregator : AbstractFieldHolder + { + private List boundContainers; + private bool internalUpdateRunning = false; + + private enum Status { IDLE, BINDING, UPDATING }; + + private static EditorConfiguration editorConfiguration = IniConfig.GetIniSection(); + + public FieldAggregator() + { + foreach (FieldType fieldType in FieldType.Values) + { + Field field = new Field(fieldType, GetType()); + AddField(field); + } + boundContainers = new List(); + } + + public override void AddField(Field field) + { + base.AddField(field); + field.PropertyChanged += OwnPropertyChanged; + } + + public void BindElements(DrawableContainerList dcs) + { + foreach (DrawableContainer dc in dcs) + { + BindElement(dc); + } + } + + public void BindElement(IDrawableContainer dc) + { + DrawableContainer container = dc as DrawableContainer; + if (container != null && !boundContainers.Contains(container)) + { + boundContainers.Add(container); + container.ChildrenChanged += delegate + { + UpdateFromBoundElements(); + }; + UpdateFromBoundElements(); + } + } + + public void BindAndUpdateElement(IDrawableContainer dc) + { + UpdateElement(dc); + BindElement(dc); + } + + public void UpdateElement(IDrawableContainer dc) + { + DrawableContainer container = dc as DrawableContainer; + if (container == null) + { + return; + } + internalUpdateRunning = true; + foreach (Field 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 (Field 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 (Field field in FindCommonFields()) + { + SetFieldValue(field.FieldType, field.Value); + } + internalUpdateRunning = false; + } + + private List FindCommonFields() + { + List returnFields = null; + if (boundContainers.Count > 0) + { + // take all fields from the least selected container... + DrawableContainer leastSelectedContainer = boundContainers[boundContainers.Count - 1] as DrawableContainer; + if (leastSelectedContainer != null) + { + returnFields = leastSelectedContainer.GetFields(); + for (int i = 0; i < boundContainers.Count - 1; i++) + { + DrawableContainer dc = boundContainers[i] as DrawableContainer; + if (dc != null) + { + List fieldsToRemove = new List(); + foreach (Field f in returnFields) + { + // ... throw out those that do not apply to one of the other containers + if (!dc.HasField(f.FieldType)) + { + fieldsToRemove.Add(f); + } + } + foreach (Field f in fieldsToRemove) + { + returnFields.Remove(f); + } + } + } + } + } + if (returnFields == null) + { + returnFields = new List(); + } + return returnFields; + } + + public void OwnPropertyChanged(object sender, PropertyChangedEventArgs ea) + { + Field field = (Field)sender; + if (!internalUpdateRunning && field.Value != null) + { + foreach (DrawableContainer drawableContainer in boundContainers) + { + if (drawableContainer.HasField(field.FieldType)) + { + Field 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 + editorConfiguration.UpdateLastFieldValue(drawableContainerField); + } + } + } + } + } +} \ No newline at end of file diff --git a/GreenshotImageEditor/Drawing/Fields/FieldType.cs b/GreenshotImageEditor/Drawing/Fields/FieldType.cs new file mode 100644 index 000000000..5b487c17a --- /dev/null +++ b/GreenshotImageEditor/Drawing/Fields/FieldType.cs @@ -0,0 +1,125 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2013 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: http://getgreenshot.org/ + * The Greenshot project is hosted on Sourceforge: http://sourceforge.net/projects/greenshot/ + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using System; + +namespace Greenshot.Drawing.Fields +{ + /// + /// Defines all FieldTypes + their default value. + /// (The additional value is why this is not an enum) + /// + [Serializable] + public class FieldType + { + public static readonly FieldType ARROWHEADS = new FieldType("ARROWHEADS"); + public static readonly FieldType BLUR_RADIUS = new FieldType("BLUR_RADIUS"); + public static readonly FieldType BRIGHTNESS = new FieldType("BRIGHTNESS"); + public static readonly FieldType FILL_COLOR = new FieldType("FILL_COLOR"); + public static readonly FieldType FONT_BOLD = new FieldType("FONT_BOLD"); + public static readonly FieldType FONT_FAMILY = new FieldType("FONT_FAMILY"); + public static readonly FieldType FONT_ITALIC = new FieldType("FONT_ITALIC"); + public static readonly FieldType FONT_SIZE = new FieldType("FONT_SIZE"); + public static readonly FieldType TEXT_HORIZONTAL_ALIGNMENT = new FieldType("TEXT_HORIZONTAL_ALIGNMENT"); + public static readonly FieldType TEXT_VERTICAL_ALIGNMENT = new FieldType("TEXT_VERTICAL_ALIGNMENT"); + public static readonly FieldType HIGHLIGHT_COLOR = new FieldType("HIGHLIGHT_COLOR"); + public static readonly FieldType LINE_COLOR = new FieldType("LINE_COLOR"); + public static readonly FieldType LINE_THICKNESS = new FieldType("LINE_THICKNESS"); + public static readonly FieldType MAGNIFICATION_FACTOR = new FieldType("MAGNIFICATION_FACTOR"); + public static readonly FieldType PIXEL_SIZE = new FieldType("PIXEL_SIZE"); + public static readonly FieldType PREVIEW_QUALITY = new FieldType("PREVIEW_QUALITY"); + public static readonly FieldType SHADOW = new FieldType("SHADOW"); + public static readonly FieldType PREPARED_FILTER_OBFUSCATE = new FieldType("PREPARED_FILTER_OBFUSCATE"); + public static readonly FieldType PREPARED_FILTER_HIGHLIGHT = new FieldType("PREPARED_FILTER_HIGHLIGHT"); + public static readonly FieldType FLAGS = new FieldType("FLAGS"); + + public static FieldType[] Values = new FieldType[]{ + 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 + }; + + [Flags] + public enum Flag + { + NONE = 0, + CONFIRMABLE = 1 + } + + public string Name; + + 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/GreenshotImageEditor/Drawing/Fields/IFieldHolder.cs b/GreenshotImageEditor/Drawing/Fields/IFieldHolder.cs new file mode 100644 index 000000000..2500a64ee --- /dev/null +++ b/GreenshotImageEditor/Drawing/Fields/IFieldHolder.cs @@ -0,0 +1,61 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2013 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: http://getgreenshot.org/ + * The Greenshot project is hosted on Sourceforge: http://sourceforge.net/projects/greenshot/ + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using System.Collections.Generic; + +namespace Greenshot.Drawing.Fields +{ + /// + /// Any element holding Fields must provide access to it. + /// AbstractFieldHolder is the basic implementation. + /// If you need the fieldHolder to have child fieldHolders, + /// you should consider using IFieldHolderWithChildren. + /// + public interface IFieldHolder + { + event FieldChangedEventHandler FieldChanged; + + void AddField(Field field); + + void RemoveField(Field field); + + List GetFields(); + + Field GetField(FieldType fieldType); + + bool HasField(FieldType fieldType); + + void SetFieldValue(FieldType fieldType, object value); + } + + /// + /// Extended fieldHolder which has fieldHolder children. + /// Implementations should pass field values to and from + /// their children. + /// AbstractFieldHolderWithChildren is the basic implementation. + /// + public interface IFieldHolderWithChildren : IFieldHolder + { + void AddChild(IFieldHolder fieldHolder); + + void RemoveChild(IFieldHolder fieldHolder); + } +} \ No newline at end of file diff --git a/GreenshotImageEditor/Drawing/FilterContainer.cs b/GreenshotImageEditor/Drawing/FilterContainer.cs new file mode 100644 index 000000000..cceaafa89 --- /dev/null +++ b/GreenshotImageEditor/Drawing/FilterContainer.cs @@ -0,0 +1,94 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2013 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: http://getgreenshot.org/ + * The Greenshot project is hosted on Sourceforge: http://sourceforge.net/projects/greenshot/ + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using Greenshot.Drawing.Fields; +using Greenshot.Helpers; +using Greenshot.Plugin.Drawing; +using System; +using System.Drawing; +using System.Drawing.Drawing2D; + +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 PreparedFilter Filter + { + get { return (PreparedFilter)GetFieldValue(FieldType.PREPARED_FILTER_HIGHLIGHT); } + } + + public FilterContainer(Surface parent) + : base(parent) + { + 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 = 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/GreenshotImageEditor/Drawing/Filters/AbstractFilter.cs b/GreenshotImageEditor/Drawing/Filters/AbstractFilter.cs new file mode 100644 index 000000000..c797b71cd --- /dev/null +++ b/GreenshotImageEditor/Drawing/Filters/AbstractFilter.cs @@ -0,0 +1,93 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2013 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: http://getgreenshot.org/ + * The Greenshot project is hosted on Sourceforge: http://sourceforge.net/projects/greenshot/ + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using Greenshot.Drawing.Fields; +using Greenshot.Plugin.Drawing; +using System; +using System.ComponentModel; +using System.Drawing; + +/// +/// Graphical filter which can be added to DrawableContainer. +/// Subclasses should fulfill INotifyPropertyChanged contract, i.e. call +/// OnPropertyChanged whenever a public property has been changed. +/// +namespace Greenshot.Drawing.Filters +{ + [Serializable()] + public abstract class AbstractFilter : AbstractFieldHolder, IFilter + { + [NonSerialized] + private PropertyChangedEventHandler propertyChanged; + public event PropertyChangedEventHandler PropertyChanged + { + add { propertyChanged += value; } + remove { propertyChanged -= value; } + } + + private bool invert = false; + 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) + { + if (propertyChanged != null) + { + propertyChanged(this, new PropertyChangedEventArgs(propertyName)); + } + } + } +} \ No newline at end of file diff --git a/GreenshotImageEditor/Drawing/Filters/BlurFilter.cs b/GreenshotImageEditor/Drawing/Filters/BlurFilter.cs new file mode 100644 index 000000000..210675b11 --- /dev/null +++ b/GreenshotImageEditor/Drawing/Filters/BlurFilter.cs @@ -0,0 +1,80 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2013 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: http://getgreenshot.org/ + * The Greenshot project is hosted on Sourceforge: http://sourceforge.net/projects/greenshot/ + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using Greenshot.Drawing.Fields; +using Greenshot.Plugin.Drawing; +using GreenshotPlugin.Core; +using GreenshotPlugin.UnmanagedHelpers; +using System; +using System.Drawing; +using System.Drawing.Drawing2D; + +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 unsafe override void Apply(Graphics graphics, Bitmap applyBitmap, Rectangle rect, RenderMode renderMode) + { + int blurRadius = GetFieldValueAsInt(FieldType.BLUR_RADIUS); + double previewQuality = GetFieldValueAsDouble(FieldType.PREVIEW_QUALITY); + 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); + return; + } + } +} \ No newline at end of file diff --git a/GreenshotImageEditor/Drawing/Filters/BrightnessFilter.cs b/GreenshotImageEditor/Drawing/Filters/BrightnessFilter.cs new file mode 100644 index 000000000..39743a419 --- /dev/null +++ b/GreenshotImageEditor/Drawing/Filters/BrightnessFilter.cs @@ -0,0 +1,70 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2013 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: http://getgreenshot.org/ + * The Greenshot project is hosted on Sourceforge: http://sourceforge.net/projects/greenshot/ + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using Greenshot.Drawing.Fields; +using Greenshot.Plugin.Drawing; +using GreenshotPlugin.Core; +using System; +using System.Drawing; +using System.Drawing.Drawing2D; +using System.Drawing.Imaging; + +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); + 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/GreenshotImageEditor/Drawing/Filters/GrayscaleFilter.cs b/GreenshotImageEditor/Drawing/Filters/GrayscaleFilter.cs new file mode 100644 index 000000000..f142a8a63 --- /dev/null +++ b/GreenshotImageEditor/Drawing/Filters/GrayscaleFilter.cs @@ -0,0 +1,70 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2013 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: http://getgreenshot.org/ + * The Greenshot project is hosted on Sourceforge: http://sourceforge.net/projects/greenshot/ + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using Greenshot.Plugin.Drawing; +using GreenshotPlugin.Core; +using System; +using System.Drawing; +using System.Drawing.Drawing2D; +using System.Drawing.Imaging; + +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 float[][] { + new float[] {.3f, .3f, .3f, 0, 0}, + new float[] {.59f, .59f, .59f, 0, 0}, + new float[] {.11f, .11f, .11f, 0, 0}, + new float[] {0, 0, 0, 1, 0}, + new float[] {0, 0, 0, 0, 1} + }); + 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/GreenshotImageEditor/Drawing/Filters/HighlightFilter.cs b/GreenshotImageEditor/Drawing/Filters/HighlightFilter.cs new file mode 100644 index 000000000..c62f80d12 --- /dev/null +++ b/GreenshotImageEditor/Drawing/Filters/HighlightFilter.cs @@ -0,0 +1,79 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2013 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: http://getgreenshot.org/ + * The Greenshot project is hosted on Sourceforge: http://sourceforge.net/projects/greenshot/ + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using Greenshot.Drawing.Fields; +using Greenshot.Plugin.Drawing; +using GreenshotPlugin.Core; +using System; +using System.Drawing; +using System.Drawing.Drawing2D; + +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); + } + } +} \ No newline at end of file diff --git a/GreenshotImageEditor/Drawing/Filters/IFilter.cs b/GreenshotImageEditor/Drawing/Filters/IFilter.cs new file mode 100644 index 000000000..74e6fa292 --- /dev/null +++ b/GreenshotImageEditor/Drawing/Filters/IFilter.cs @@ -0,0 +1,39 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2013 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: http://getgreenshot.org/ + * The Greenshot project is hosted on Sourceforge: http://sourceforge.net/projects/greenshot/ + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using Greenshot.Drawing.Fields; +using Greenshot.Plugin.Drawing; +using System.ComponentModel; +using System.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; } + } +} \ No newline at end of file diff --git a/GreenshotImageEditor/Drawing/Filters/MagnifierFilter.cs b/GreenshotImageEditor/Drawing/Filters/MagnifierFilter.cs new file mode 100644 index 000000000..65f21ea12 --- /dev/null +++ b/GreenshotImageEditor/Drawing/Filters/MagnifierFilter.cs @@ -0,0 +1,69 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2013 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: http://getgreenshot.org/ + * The Greenshot project is hosted on Sourceforge: http://sourceforge.net/projects/greenshot/ + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using Greenshot.Drawing.Fields; +using Greenshot.Plugin.Drawing; +using GreenshotPlugin.Core; +using System; +using System.Drawing; +using System.Drawing.Drawing2D; + +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); + } + } +} \ No newline at end of file diff --git a/GreenshotImageEditor/Drawing/Filters/PixelizationFilter.cs b/GreenshotImageEditor/Drawing/Filters/PixelizationFilter.cs new file mode 100644 index 000000000..53438d6df --- /dev/null +++ b/GreenshotImageEditor/Drawing/Filters/PixelizationFilter.cs @@ -0,0 +1,103 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2013 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: http://getgreenshot.org/ + * The Greenshot project is hosted on Sourceforge: http://sourceforge.net/projects/greenshot/ + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using Greenshot.Drawing.Fields; +using Greenshot.Helpers; +using Greenshot.Plugin.Drawing; +using GreenshotPlugin.Core; +using System; +using System.Collections.Generic; +using System.Drawing; + +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); + Rectangle applyRect = 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 = y + pixelSize) + { + for (int x = src.Left - halbPixelSize; x <= src.Right + halbPixelSize; x = 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/GreenshotImageEditor/Drawing/FreehandContainer.cs b/GreenshotImageEditor/Drawing/FreehandContainer.cs new file mode 100644 index 000000000..2f56a3f96 --- /dev/null +++ b/GreenshotImageEditor/Drawing/FreehandContainer.cs @@ -0,0 +1,321 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2013 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: http://getgreenshot.org/ + * The Greenshot project is hosted on Sourceforge: http://sourceforge.net/projects/greenshot/ + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using Greenshot.Drawing.Fields; +using Greenshot.Helpers; +using Greenshot.Plugin.Drawing; +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Drawing.Drawing2D; +using System.Runtime.Serialization; + +namespace Greenshot.Drawing +{ + /// + /// Description of PathContainer. + /// + [Serializable()] + public class FreehandContainer : DrawableContainer + { + private static readonly float[] POINT_OFFSET = new float[] { 0.5f, 0.25f, 0.75f }; + + [NonSerialized] + private GraphicsPath freehandPath = new GraphicsPath(); + private Rectangle myBounds = Rectangle.Empty; + private Point lastMouse = Point.Empty; + private List capturePoints = new List(); + private bool isRecalculated = false; + + /// + /// Constructor + /// + public FreehandContainer(Surface parent) + : base(parent) + { + Init(); + AddField(GetType(), FieldType.LINE_THICKNESS, 3); + AddField(GetType(), FieldType.LINE_COLOR, Color.Red); + Width = parent.Width; + Height = parent.Height; + Top = 0; + Left = 0; + } + + protected void Init() + { + if (grippers != null) + { + for (int i = 0; i < grippers.Length; i++) + { + grippers[i].Enabled = false; + grippers[i].Visible = false; + } + } + } + + [OnDeserialized()] + private void OnDeserialized(StreamingContext context) + { + InitGrippers(); + DoLayout(); + Init(); + 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) + { + if (freehandPath != null) + { + 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) + { + //path.AddCurve(new Point[]{lastMouse, new Point(mouseX, mouseY)}); + freehandPath.AddLine(lastMouse, new Point(mouseX, mouseY)); + 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() + { + isRecalculated = true; + // Dispose the previous path, if we have one + if (freehandPath != null) + { + freehandPath.Dispose(); + } + freehandPath = 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 * POINT_OFFSET[index]), capturePoints[(int)(capturePoints.Count * POINT_OFFSET[index++])]); + } + freehandPath.AddBeziers(capturePoints.ToArray()); + } + else if (capturePoints.Count == 2) + { + freehandPath.AddLine(capturePoints[0], capturePoints[1]); + } + + // Recalculate the bounds + myBounds = Rectangle.Round(freehandPath.GetBounds()); + } + + /// + /// Currently we can't rotate the freehand + /// + public override bool CanRotate + { + get + { + return false; + } + } + + /// + /// 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 (Pen pen = new Pen(lineColor)) + { + pen.Width = lineThickness; + if (pen.Width > 0) + { + // 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); + if (isRecalculated && Selected && renderMode == RenderMode.EDIT) + { + DrawSelectionBorder(graphics, pen); + } + graphics.DrawPath(pen, freehandPath); + // Move back, otherwise everything is shifted + graphics.TranslateTransform(-Left, -Top); + } + } + } + + /// + /// Draw a selectionborder around the freehand path + /// + /// + /// + protected void DrawSelectionBorder(Graphics graphics, Pen linePen) + { + using (Pen selectionPen = (Pen)linePen.Clone()) + { + using (GraphicsPath selectionPath = (GraphicsPath)freehandPath.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))); + } + else + { + return new Rectangle(0, 0, parent.Width, parent.Height); + } + } + } + + /// + /// FreehandContainer are regarded equal if they are of the same type and their paths are equal. + /// + /// + /// + public override bool Equals(object obj) + { + bool ret = false; + if (obj != null && GetType().Equals(obj.GetType())) + { + FreehandContainer other = obj as FreehandContainer; + if (freehandPath.Equals(other.freehandPath)) + { + ret = true; + } + } + return ret; + } + + public override int GetHashCode() + { + return freehandPath.GetHashCode(); + } + + protected override void DoLayout() + { + } + + public override void ShowGrippers() + { + ResumeLayout(); + } + + public override bool ClickableAt(int x, int y) + { + bool returnValue = base.ClickableAt(x, y); + if (returnValue) + { + int lineThickness = GetFieldValueAsInt(FieldType.LINE_THICKNESS); + using (Pen pen = new Pen(Color.White)) + { + pen.Width = lineThickness + 10; + returnValue = freehandPath.IsOutlineVisible(x - Left, y - Top, pen); + } + } + return returnValue; + } + } +} \ No newline at end of file diff --git a/GreenshotImageEditor/Drawing/Gripper.cs b/GreenshotImageEditor/Drawing/Gripper.cs new file mode 100644 index 000000000..b97861c85 --- /dev/null +++ b/GreenshotImageEditor/Drawing/Gripper.cs @@ -0,0 +1,83 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2013 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: http://getgreenshot.org/ + * The Greenshot project is hosted on Sourceforge: http://sourceforge.net/projects/greenshot/ + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using System.Drawing; +using System.Windows.Forms; + +namespace Greenshot.Drawing +{ + /// + /// Description of Gripper. + /// + public class Gripper : Label + { + /// + /// Constants for anchor/gripper position: + /// 0 1 2 + /// 7 3 + /// 6 5 4 + /// + public const int POSITION_TOP_LEFT = 0; + public const int POSITION_TOP_CENTER = 1; + public const int POSITION_TOP_RIGHT = 2; + public const int POSITION_MIDDLE_RIGHT = 3; + public const int POSITION_BOTTOM_RIGHT = 4; + public const int POSITION_BOTTOM_CENTER = 5; + public const int POSITION_BOTTOM_LEFT = 6; + public const int POSITION_MIDDLE_LEFT = 7; + public const int GripperSize = 7; + + public int Position; + + public Gripper() + { + Width = GripperSize; + Height = GripperSize; + BackColor = Color.White; + BorderStyle = BorderStyle.FixedSingle; + } + + public bool IsTop() + { + return Position == 0 || Position == 1 || Position == 2; + } + + public bool IsRight() + { + return Position == 2 || Position == 3 || Position == 4; + } + + public bool IsBottom() + { + return Position == 4 || Position == 5 || Position == 6; + } + + public bool IsLeft() + { + return Position == 6 || Position == 7 || Position == 0; + } + + public bool IsCorner() + { + return Position == 0 || Position == 2 || Position == 4 || Position == 6; + } + } +} \ No newline at end of file diff --git a/GreenshotImageEditor/Drawing/HighlightContainer.cs b/GreenshotImageEditor/Drawing/HighlightContainer.cs new file mode 100644 index 000000000..10b5afcb5 --- /dev/null +++ b/GreenshotImageEditor/Drawing/HighlightContainer.cs @@ -0,0 +1,100 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2013 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: http://getgreenshot.org/ + * The Greenshot project is hosted on Sourceforge: http://sourceforge.net/projects/greenshot/ + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using Greenshot.Drawing.Fields; +using Greenshot.Drawing.Filters; +using System; +using System.Drawing; +using System.Runtime.Serialization; + +namespace Greenshot.Drawing +{ + /// + /// Description of ObfuscateContainer. + /// + [Serializable()] + public class HighlightContainer : FilterContainer + { + public HighlightContainer(Surface parent) + : base(parent) + { + AddField(GetType(), FieldType.LINE_THICKNESS, 0); + AddField(GetType(), FieldType.LINE_COLOR, Color.Red); + AddField(GetType(), FieldType.SHADOW, false); + AddField(GetType(), FieldType.PREPARED_FILTER_HIGHLIGHT, PreparedFilter.TEXT_HIGHTLIGHT); + init(); + } + + [OnDeserialized()] + private void OnDeserialized(StreamingContext context) + { + init(); + } + + private void init() + { + FieldChanged += HighlightContainer_OnFieldChanged; + ConfigurePreparedFilters(); + } + + protected void HighlightContainer_OnFieldChanged(object sender, FieldChangedEventArgs e) + { + if (sender.Equals(this)) + { + if (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: + AbstractFilter bf = new BrightnessFilter(this); + bf.Invert = true; + Add(bf); + bf = new BlurFilter(this); + bf.Invert = true; + Add(bf); + break; + case PreparedFilter.GRAYSCALE: + AbstractFilter f = new GrayscaleFilter(this); + f.Invert = true; + Add(f); + break; + case PreparedFilter.MAGNIFICATION: + Add(new MagnifierFilter(this)); + break; + } + } + } +} \ No newline at end of file diff --git a/GreenshotImageEditor/Drawing/IconContainer.cs b/GreenshotImageEditor/Drawing/IconContainer.cs new file mode 100644 index 000000000..3885c6f4f --- /dev/null +++ b/GreenshotImageEditor/Drawing/IconContainer.cs @@ -0,0 +1,123 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2013 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: http://getgreenshot.org/ + * The Greenshot project is hosted on Sourceforge: http://sourceforge.net/projects/greenshot/ + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using Greenshot.Plugin.Drawing; +using GreenshotPlugin; +using System; +using System.Drawing; +using System.Drawing.Drawing2D; +using System.IO; + +namespace Greenshot.Drawing +{ + /// + /// Description of IconContainer. + /// + [Serializable()] + public class IconContainer : DrawableContainer, IIconContainer + { + protected Icon icon; + + public IconContainer(Surface parent) + : base(parent) + { + } + + public IconContainer(Surface parent, string filename) + : base(parent) + { + Load(filename); + } + + public Icon Icon + { + set + { + if (icon != null) + { + icon.Dispose(); + } + icon = (Icon)value.Clone(); + Width = value.Width; + Height = value.Height; + } + get { return 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) + { + if (icon != null) + { + icon.Dispose(); + } + } + icon = null; + base.Dispose(disposing); + } + + public void Load(string filename) + { + if (File.Exists(filename)) + { + 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) + { + graphics.SmoothingMode = SmoothingMode.HighQuality; + graphics.InterpolationMode = InterpolationMode.NearestNeighbor; + graphics.CompositingQuality = CompositingQuality.Default; + graphics.PixelOffsetMode = PixelOffsetMode.HighQuality; + graphics.DrawIcon(icon, Bounds); + } + } + + public override bool hasDefaultSize + { + get + { + return true; + } + } + + public override Size DefaultSize + { + get + { + return icon.Size; + } + } + } +} \ No newline at end of file diff --git a/GreenshotImageEditor/Drawing/ImageContainer.cs b/GreenshotImageEditor/Drawing/ImageContainer.cs new file mode 100644 index 000000000..2e97e4b2a --- /dev/null +++ b/GreenshotImageEditor/Drawing/ImageContainer.cs @@ -0,0 +1,248 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2013 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: http://getgreenshot.org/ + * The Greenshot project is hosted on Sourceforge: http://sourceforge.net/projects/greenshot/ + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using Greenshot.Core; +using Greenshot.Drawing.Fields; +using Greenshot.Plugin.Drawing; +using GreenshotPlugin; +using GreenshotPlugin.Core; +using System; +using System.Drawing; +using System.Drawing.Drawing2D; +using System.IO; + +namespace Greenshot.Drawing +{ + /// + /// Description of BitmapContainer. + /// + [Serializable()] + public class ImageContainer : DrawableContainer, IImageContainer + { + 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 = null; + + /// + /// 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) + { + AddField(GetType(), FieldType.SHADOW, false); + FieldChanged += BitmapContainer_OnFieldChanged; + } + + protected void BitmapContainer_OnFieldChanged(object sender, FieldChangedEventArgs e) + { + if (sender.Equals(this)) + { + if (e.Field.FieldType == FieldType.SHADOW) + { + ChangeShadowField(); + } + } + } + + public void ChangeShadowField() + { + bool shadow = GetFieldValueAsBool(FieldType.SHADOW); + if (shadow) + { + CheckShadow(shadow); + Width = shadowBitmap.Width; + Height = shadowBitmap.Height; + Left = Left - shadowOffset.X; + Top = Top - shadowOffset.Y; + } + else + { + Width = image.Width; + Height = image.Height; + if (shadowBitmap != null) + { + Left = Left + shadowOffset.X; + Top = Top + shadowOffset.Y; + } + } + } + + public Image Image + { + set + { + // Remove all current bitmaps + disposeImages(); + 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 = Left - shadowOffset.X; + Top = 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) + { + disposeImages(); + } + image = null; + shadowBitmap = null; + base.Dispose(disposing); + } + + private void disposeImages() + { + if (image != null) + { + image.Dispose(); + } + if (shadowBitmap != null) + { + shadowBitmap.Dispose(); + } + image = null; + shadowBitmap = null; + } + + /// + /// + /// + /// + public void Load(string filename) + { + if (File.Exists(filename)) + { + // Always make sure ImageHelper.LoadBitmap results are disposed some time, + // as we close the bitmap internally, we need to do it afterwards + using (Image tmpImage = ImageHelper.LoadImage(filename)) + { + Image = tmpImage; + } + LOG.Debug("Loaded file: " + filename + " with resolution: " + Height + "," + Width); + } + } + + /// + /// Rotate the bitmap + /// + /// + public override void Rotate(RotateFlipType rotateFlipType) + { + Image newImage = ImageHelper.RotateFlip((Bitmap)image, rotateFlipType); + if (newImage != null) + { + // Remove all current bitmaps, also the shadow (will be recreated) + disposeImages(); + image = newImage; + } + base.Rotate(rotateFlipType); + } + + /// + /// This checks if a shadow is already generated + /// + /// + private void CheckShadow(bool shadow) + { + if (shadow && shadowBitmap == null) + { + shadowBitmap = ImageHelper.ApplyEffect(image, new DropShadowEffect(), out shadowOffset); + } + } + + /// + /// Draw the actual container to the graphics object + /// + /// + /// + public override void Draw(Graphics graphics, RenderMode rm) + { + if (image != null) + { + bool shadow = GetFieldValueAsBool(FieldType.SHADOW); + graphics.SmoothingMode = SmoothingMode.HighQuality; + graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; + graphics.CompositingQuality = CompositingQuality.HighQuality; + graphics.PixelOffsetMode = PixelOffsetMode.HighQuality; + + if (shadow) + { + CheckShadow(shadow); + graphics.DrawImage(shadowBitmap, Bounds); + } + else + { + graphics.DrawImage(image, Bounds); + } + } + } + + public override bool hasDefaultSize + { + get + { + return true; + } + } + + public override Size DefaultSize + { + get + { + return image.Size; + } + } + } +} \ No newline at end of file diff --git a/GreenshotImageEditor/Drawing/LineContainer.cs b/GreenshotImageEditor/Drawing/LineContainer.cs new file mode 100644 index 000000000..bf714bfa3 --- /dev/null +++ b/GreenshotImageEditor/Drawing/LineContainer.cs @@ -0,0 +1,137 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2013 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: http://getgreenshot.org/ + * The Greenshot project is hosted on Sourceforge: http://sourceforge.net/projects/greenshot/ + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using Greenshot.Drawing.Fields; +using Greenshot.Helpers; +using Greenshot.Plugin.Drawing; +using System; +using System.Drawing; +using System.Drawing.Drawing2D; +using System.Runtime.Serialization; + +namespace Greenshot.Drawing +{ + /// + /// Description of LineContainer. + /// + [Serializable()] + public class LineContainer : DrawableContainer + { + public static readonly int MAX_CLICK_DISTANCE_TOLERANCE = 10; + + public LineContainer(Surface parent) + : base(parent) + { + Init(); + AddField(GetType(), FieldType.LINE_THICKNESS, 2); + AddField(GetType(), FieldType.LINE_COLOR, Color.Red); + AddField(GetType(), FieldType.SHADOW, true); + } + + [OnDeserialized()] + private void OnDeserialized(StreamingContext context) + { + InitGrippers(); + DoLayout(); + Init(); + } + + protected void Init() + { + if (grippers != null) + { + foreach (int index in new int[] { 1, 2, 3, 5, 6, 7 }) + { + grippers[index].Enabled = false; + } + } + } + + 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 = 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)) + { + pen.Width = lineThickness; + using (GraphicsPath path = new GraphicsPath()) + { + path.AddLine(Left, Top, Left + Width, Top + Height); + return path.IsOutlineVisible(x, y, pen); + } + } + } + else + { + return false; + } + } + + protected override ScaleHelper.IDoubleProcessor GetAngleRoundProcessor() + { + return ScaleHelper.LineAngleRoundBehavior.Instance; + } + } +} \ No newline at end of file diff --git a/GreenshotImageEditor/Drawing/ObfuscateContainer.cs b/GreenshotImageEditor/Drawing/ObfuscateContainer.cs new file mode 100644 index 000000000..eb051eaf4 --- /dev/null +++ b/GreenshotImageEditor/Drawing/ObfuscateContainer.cs @@ -0,0 +1,83 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2013 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: http://getgreenshot.org/ + * The Greenshot project is hosted on Sourceforge: http://sourceforge.net/projects/greenshot/ + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using Greenshot.Drawing.Fields; +using Greenshot.Drawing.Filters; +using System; +using System.Runtime.Serialization; + +namespace Greenshot.Drawing +{ + /// + /// Description of ObfuscateContainer. + /// + [Serializable()] + public class ObfuscateContainer : FilterContainer + { + public ObfuscateContainer(Surface parent) + : base(parent) + { + AddField(GetType(), FieldType.PREPARED_FILTER_OBFUSCATE, PreparedFilter.PIXELIZE); + init(); + } + + [OnDeserialized()] + private void OnDeserialized(StreamingContext context) + { + init(); + } + + private void init() + { + FieldChanged += ObfuscateContainer_OnFieldChanged; + ConfigurePreparedFilters(); + } + + protected void ObfuscateContainer_OnFieldChanged(object sender, FieldChangedEventArgs e) + { + if (sender.Equals(this)) + { + if (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/GreenshotImageEditor/Drawing/RectangleContainer.cs b/GreenshotImageEditor/Drawing/RectangleContainer.cs new file mode 100644 index 000000000..c7e7458b9 --- /dev/null +++ b/GreenshotImageEditor/Drawing/RectangleContainer.cs @@ -0,0 +1,135 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2013 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: http://getgreenshot.org/ + * The Greenshot project is hosted on Sourceforge: http://sourceforge.net/projects/greenshot/ + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using Greenshot.Drawing.Fields; +using Greenshot.Helpers; +using Greenshot.Plugin.Drawing; +using System; +using System.Drawing; +using System.Drawing.Drawing2D; + +namespace Greenshot.Drawing +{ + /// + /// Represents a rectangular shape on the Surface + /// + [Serializable()] + public class RectangleContainer : DrawableContainer + { + public RectangleContainer(Surface parent) + : base(parent) + { + 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) + { + 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); + 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))) + { + shadowPen.Width = lineVisible ? lineThickness : 1; + Rectangle shadowRect = GuiRectangle.GetGuiRectangle( + Left + currentStep, + Top + currentStep, + Width, + Height); + graphics.DrawRectangle(shadowPen, shadowRect); + currentStep++; + alpha = alpha - (basealpha / steps); + } + } + } + + Rectangle rect = GuiRectangle.GetGuiRectangle(Left, Top, Width, Height); + + 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); + + // 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); + } + } + } + else + { + return false; + } + } + } +} \ No newline at end of file diff --git a/GreenshotImageEditor/Drawing/RoundedRectangle.cs b/GreenshotImageEditor/Drawing/RoundedRectangle.cs new file mode 100644 index 000000000..b2234a8eb --- /dev/null +++ b/GreenshotImageEditor/Drawing/RoundedRectangle.cs @@ -0,0 +1,155 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2013 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: http://getgreenshot.org/ + * The Greenshot project is hosted on Sourceforge: http://sourceforge.net/projects/greenshot/ + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using System.Drawing; +using System.Drawing.Drawing2D; + +namespace Greenshot.Drawing +{ + /// + /// TODO: currently this is only used in the capture form, we might move this code directly to there! + /// + public abstract class RoundedRectangle + { + public enum RectangleCorners + { + None = 0, TopLeft = 1, TopRight = 2, + BottomLeft = 4, BottomRight = 8, + All = TopLeft | TopRight | BottomLeft | BottomRight + } + + public static GraphicsPath Create2(int x, int y, int width, int height, int radius) + { + GraphicsPath gp = new GraphicsPath(); + gp.AddLine(x + radius, y, x + width - (radius * 2), y); // Line + gp.AddArc(x + width - (radius * 2), y, radius * 2, radius * 2, 270, 90); // Corner + gp.AddLine(x + width, y + radius, x + width, y + height - (radius * 2)); // Line + gp.AddArc(x + width - (radius * 2), y + height - (radius * 2), radius * 2, radius * 2, 0, 90); // Corner + gp.AddLine(x + width - (radius * 2), y + height, x + radius, y + height); // Line + gp.AddArc(x, y + height - (radius * 2), radius * 2, radius * 2, 90, 90); // Corner + gp.AddLine(x, y + height - (radius * 2), x, y + radius); // Line + gp.AddArc(x, y, radius * 2, radius * 2, 180, 90); // Corner + gp.CloseFigure(); + + return gp; + } + + public static GraphicsPath Create(int x, int y, int width, int height, int radius, RectangleCorners corners) + { + int xw = x + width; + int yh = y + height; + int xwr = xw - radius; + int yhr = yh - radius; + int xr = x + radius; + int yr = y + radius; + int r2 = radius * 2; + int xwr2 = xw - r2; + int yhr2 = yh - r2; + + GraphicsPath p = new GraphicsPath(); + p.StartFigure(); + + //Top Left Corner + if ((RectangleCorners.TopLeft & corners) == RectangleCorners.TopLeft) + { + p.AddArc(x, y, r2, r2, 180, 90); + } + else + { + p.AddLine(x, yr, x, y); + p.AddLine(x, y, xr, y); + } + + //Top Edge + p.AddLine(xr, y, xwr, y); + + //Top Right Corner + if ((RectangleCorners.TopRight & corners) == RectangleCorners.TopRight) + { + p.AddArc(xwr2, y, r2, r2, 270, 90); + } + else + { + p.AddLine(xwr, y, xw, y); + p.AddLine(xw, y, xw, yr); + } + + //Right Edge + p.AddLine(xw, yr, xw, yhr); + + //Bottom Right Corner + if ((RectangleCorners.BottomRight & corners) == RectangleCorners.BottomRight) + { + p.AddArc(xwr2, yhr2, r2, r2, 0, 90); + } + else + { + p.AddLine(xw, yhr, xw, yh); + p.AddLine(xw, yh, xwr, yh); + } + + //Bottom Edge + p.AddLine(xwr, yh, xr, yh); + + //Bottom Left Corner + if ((RectangleCorners.BottomLeft & corners) == RectangleCorners.BottomLeft) + { + p.AddArc(x, yhr2, r2, r2, 90, 90); + } + else + { + p.AddLine(xr, yh, x, yh); + p.AddLine(x, yh, x, yhr); + } + + //Left Edge + p.AddLine(x, yhr, x, yr); + + p.CloseFigure(); + return p; + } + + public static GraphicsPath Create(Rectangle rect, int radius, RectangleCorners corners) + { + return Create(rect.X, rect.Y, rect.Width, rect.Height, radius, corners); + } + + public static GraphicsPath Create(int x, int y, int width, int height, int radius) + { + return Create(x, y, width, height, radius, RectangleCorners.All); + } + + public static GraphicsPath Create(Rectangle rect, int radius) + { + return Create(rect.X, rect.Y, rect.Width, rect.Height, radius); + } + + public static GraphicsPath Create(int x, int y, int width, int height) + { + return Create(x, y, width, height, 5); + } + + public static GraphicsPath Create(Rectangle rect) + { + return Create(rect.X, rect.Y, rect.Width, rect.Height); + } + } +} \ No newline at end of file diff --git a/GreenshotImageEditor/Drawing/Surface.cs b/GreenshotImageEditor/Drawing/Surface.cs new file mode 100644 index 000000000..5df7369cf --- /dev/null +++ b/GreenshotImageEditor/Drawing/Surface.cs @@ -0,0 +1,1886 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2013 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: http://getgreenshot.org/ + * The Greenshot project is hosted on Sourceforge: http://sourceforge.net/projects/greenshot/ + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using Greenshot.Configuration; +using Greenshot.Core; +using Greenshot.Drawing.Fields; +using Greenshot.Helpers; +using Greenshot.IniFile; +using Greenshot.Memento; +using Greenshot.Plugin; +using Greenshot.Plugin.Drawing; +using GreenshotPlugin; +using GreenshotPlugin.Controls; +using GreenshotPlugin.Core; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.Drawing.Imaging; +using System.IO; +using System.Runtime.Serialization.Formatters.Binary; +using System.Windows.Forms; + +namespace Greenshot.Drawing +{ + /// + /// Description of Surface. + /// + public class Surface : Control, ISurface + { + public static int Count = 0; + private static CoreConfiguration conf = IniConfig.GetIniSection(); + + /// + /// Event handlers (do not serialize!) + /// + [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 = false; + + /// + /// Make only one surfacemove cycle undoable, see SurfaceMouseMove + /// + [NonSerialized] + private bool isSurfaceMoveMadeUndoable = false; + + /// + /// Undo/Redo stacks, should not be serialized as the file would be way to big + /// + [NonSerialized] + private Stack undoStack = new Stack(); + [NonSerialized] + private Stack redoStack = new Stack(); + + /// + /// Last save location, do not serialize! + /// + [NonSerialized] + private string lastSaveFullPath = null; + + /// + /// current drawing mode, do not serialize! + /// + [NonSerialized] + private DrawingModes drawingMode = DrawingModes.None; + + /// + /// the keyslocked flag helps with focus issues + /// + [NonSerialized] + private bool keysLocked = false; + + /// + /// 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 = false; + + /// + /// are we dragging, do not serialize + /// + [NonSerialized] + private bool draggingInProgress = false; + + /// + /// The selected element for the mouse down, do not serialize + /// + [NonSerialized] + private IDrawableContainer mouseDownElement = null; + + /// + /// all selected elements, do not serialize + /// + [NonSerialized] + private DrawableContainerList selectedElements = new DrawableContainerList(); + + /// + /// the element we are drawing with, do not serialize + /// + [NonSerialized] + private IDrawableContainer drawingElement = null; + + /// + /// the element we want to draw with (not yet drawn), do not serialize + /// + [NonSerialized] + private IDrawableContainer undrawnElement = null; + + /// + /// the cropcontainer, when cropping this is set, do not serialize + /// + [NonSerialized] + private IDrawableContainer cropContainer = null; + + /// + /// 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 = null; + + /// + /// all elements on the surface, needed with serialization + /// + private DrawableContainerList elements = new DrawableContainerList(); + + /// + /// all elements on the surface, needed with serialization + /// + private FieldAggregator fieldAggregator = new FieldAggregator(); + + /// + /// the cursor container, needed with serialization as we need a direct acces to it. + /// + private IDrawableContainer cursorContainer = null; + + /// + /// the capture details, needed with serialization + /// + private ICaptureDetails captureDetails = null; + + /// + /// 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 = null; + public Image Image + { + get + { + return image; + } + set + { + image = value; + Size = image.Size; + } + } + + /// + /// 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 + { + return fieldAggregator; + } + set + { + fieldAggregator = value; + } + } + + /// + /// The cursor container has it's own accessor so we can find and remove this (when needed) + /// + public IDrawableContainer CursorContainer + { + get + { + return cursorContainer; + } + } + + /// + /// A simple getter to ask if this surface has a cursor + /// + public bool HasCursor + { + get + { + return cursorContainer != null; + } + } + + /// + /// A simple helper method to remove the cursor from the surface + /// + public void RemoveCursor() + { + RemoveElement(cursorContainer, true); + cursorContainer = null; + } + + /// + /// The brush which is used to draw the transparent background + /// + public Brush TransparencyBackgroundBrush + { + get + { + return transparencyBackgroundBrush; + } + set + { + transparencyBackgroundBrush = value; + } + } + + /// + /// Are the keys on this surface locked? + /// + public bool KeysLocked + { + get + { + return keysLocked; + } + set + { + keysLocked = value; + } + } + + /// + /// Is this surface modified? This is only true if the surface has not been exported. + /// + public bool Modified + { + get + { + return modified; + } + set + { + modified = value; + } + } + + /// + /// The DrawingMode property specifies the mode for drawing, more or less the element type. + /// + public DrawingModes DrawingMode + { + get { return drawingMode; } + set + { + drawingMode = value; + if (drawingModeChanged != null) + { + SurfaceDrawingModeEventArgs eventArgs = new SurfaceDrawingModeEventArgs(); + eventArgs.DrawingMode = drawingMode; + drawingModeChanged.Invoke(this, eventArgs); + } + DeselectAllElements(); + CreateUndrawnElement(); + } + } + + /// + /// Property for accessing the last save "full" path + /// + public string LastSaveFullPath + { + get + { + return 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 + { + return captureDetails; + } + set + { + captureDetails = value; + } + } + + /// + /// Base Surface constructor + /// + public Surface() + : base() + { + Count++; + 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; + Size = newImage.Size; + + 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 + { + get + { + return undoStack.Count > 0; + } + } + + /// + /// Returns if the surface can do a redo + /// + public bool CanRedo + { + get + { + return redoStack.Count > 0; + } + } + + /// + /// Get the language key for the undo action + /// + public LangKey UndoActionLanguageKey + { + get + { + if (CanUndo) + { + return undoStack.Peek().ActionLanguageKey; + } + else + { + return LangKey.none; + } + } + } + + /// + /// Get the language key for redo action + /// + public LangKey RedoActionLanguageKey + { + get + { + if (CanRedo) + { + return redoStack.Peek().ActionLanguageKey; + } + else + { + return LangKey.none; + } + } + } + + /// + /// Make an action undo-able + /// + /// The memento implementing the undo + 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(); + DrawableContainerList loadedElements = (DrawableContainerList)binaryRead.Deserialize(streamRead); + if (loadedElements != null) + { + loadedElements.Parent = this; + 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.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); + bitmapContainer.Image = image; + bitmapContainer.Left = x; + bitmapContainer.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); + iconContainer.Icon = icon; + iconContainer.Left = x; + iconContainer.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); + cursorContainer.Cursor = cursor; + cursorContainer.Left = x; + cursorContainer.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, HorizontalAlignment horizontalAlignment, VerticalAlignment verticalAlignment, FontFamily family, float size, bool italic, bool bold, bool shadow, int borderSize, Color color, Color fillColor) + { + TextContainer textContainer = new TextContainer(this); + textContainer.Text = text; + 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(); + // Align to Surface + textContainer.AlignToParent(horizontalAlignment, verticalAlignment); + + //AggregatedProperties.UpdateElement(textContainer); + AddElement(textContainer); + return textContainer; + } + + #endregion Plugin interface implementations + + #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 (draggingInProgress || (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) + { + List filenames = ClipboardHelper.GetImageFilenames(e.Data); + Point mouse = PointToClient(new Point(e.X, e.Y)); + + foreach (Image image in ClipboardHelper.GetImages(e.Data)) + { + AddImageContainer(image, mouse.X, mouse.Y); + mouse.Offset(10, 10); + image.Dispose(); + } + } + + // private void QueryContinueDragDrop(object sender, QueryContinueDragEventArgs e) { + // LOG.Debug("QueryContinueDrag: " + e.Action); + // if (e.EscapePressed) { + // e.Action = DragAction.Cancel; + // } + // } + // + // private void GiveFeedbackDragDrop(object sender, GiveFeedbackEventArgs e) { + // e.UseDefaultCursors = true; + // } + + #endregion DragDrop + + /// + /// Auto crop the image + /// + /// true if cropped + public bool AutoCrop() + { + Rectangle cropRectangle = ImageHelper.FindAutoCropRectangle(Image, conf.AutoCropDifference); + if (isCropPossible(ref cropRectangle)) + { + 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; + } + return false; + } + + /// + /// 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((Bitmap)Image, Color.Empty); + if (newBitmap != null) + { + // Make undoable + MakeUndoable(new SurfaceBackgroundChangeMemento(this, Point.Empty), 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); + Point offset; + Image newImage = ImageHelper.ApplyEffect(Image, effect, out offset); + if (newImage != null) + { + // Make sure the elements move according to the offset the effect made the bitmap move + elements.MoveBy(offset.X, offset.Y); + // Make undoable + MakeUndoable(new SurfaceBackgroundChangeMemento(this, offset), false); + SetImage(newImage, false); + Invalidate(); + if (surfaceSizeChanged != null && !imageRectangle.Equals(new Rectangle(Point.Empty, newImage.Size))) + { + surfaceSizeChanged(this, null); + } + } + } + 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 > Width) + { + cropRectangle = new Rectangle(cropRectangle.Left, cropRectangle.Top, Width - cropRectangle.Left, cropRectangle.Height); + } + if (cropRectangle.Top + cropRectangle.Height > Height) + { + cropRectangle = new Rectangle(cropRectangle.Left, cropRectangle.Top, cropRectangle.Width, 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) + { + SurfaceMessageEventArgs eventArgs = new SurfaceMessageEventArgs(); + eventArgs.Message = message; + eventArgs.MessageType = messageType; + eventArgs.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; + } + + Point offset = new Point(-cropRectangle.Left, -cropRectangle.Top); + // Make undoable + MakeUndoable(new SurfaceBackgroundChangeMemento(this, offset), false); + + // Do not dispose otherwise we can't undo the image! + SetImage(tmpImage, false); + elements.MoveBy(offset.X, offset.Y); + 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, Point offset) + { + SetImage(previous, false); + elements.MoveBy(offset.X, offset.Y); + if (surfaceSizeChanged != null) + { + surfaceSizeChanged(this, null); + } + Invalidate(); + } + + /// + /// This event handler is called when someone presses the mouse on a surface. + /// + /// + /// + private void SurfaceMouseDown(object sender, MouseEventArgs e) + { + mouseStart = e.Location; + + // check contextmenu + if (e.Button == MouseButtons.Right) + { + DrawableContainerList 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(); + selectedList.Add(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; + drawingElement.Status = EditStatus.DRAWING; + undrawnElement = null; + // if a new element has been drawn, set location and register it + if (drawingElement != null) + { + drawingElement.PropertyChanged += ElementPropertyChanged; + if (!drawingElement.HandleMouseDown(mouseStart.X, mouseStart.Y)) + { + drawingElement.Left = mouseStart.X; + drawingElement.Top = mouseStart.Y; + } + AddElement(drawingElement); + drawingElement.Selected = true; + } + } + 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) + { + 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.ShowGrippers(); + 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) + { + Point currentMouse = e.Location; + + if (DrawingMode != DrawingModes.None) + { + Cursor = Cursors.Cross; + } + else + { + Cursor = Cursors.Default; + } + + if (mouseDown) + { + if (mouseDownElement != null) + { // an element is currently dragged + mouseDownElement.Invalidate(); + selectedElements.HideGrippers(); + // 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); + } + + /// + /// This is the event handler for the Paint Event, try to draw as little as possible! + /// + /// + /// + private void SurfacePaint(object sender, PaintEventArgs e) + { + Graphics targetGraphics = e.Graphics; + Rectangle clipRectangle = e.ClipRectangle; + if (Rectangle.Empty.Equals(clipRectangle)) + { + LOG.Debug("Empty cliprectangle??"); + return; + } + + if (elements.hasIntersectingFilters(clipRectangle)) + { + 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; + graphics.DrawImage(Image, clipRectangle, clipRectangle, GraphicsUnit.Pixel); + graphics.SetClip(targetGraphics); + elements.Draw(graphics, buffer, RenderMode.EDIT, clipRectangle); + } + targetGraphics.DrawImage(buffer, clipRectangle, clipRectangle, GraphicsUnit.Pixel); + } + else + { + targetGraphics.DrawImage(Image, clipRectangle, clipRectangle, GraphicsUnit.Pixel); + elements.Draw(targetGraphics, null, RenderMode.EDIT, clipRectangle); + } + } + + /// + /// Draw a checkboard when capturing with transparency + /// + /// PaintEventArgs + protected override void OnPaintBackground(PaintEventArgs e) + { + // check if we need to draw the checkerboard + if (Image.IsAlphaPixelFormat(Image.PixelFormat) && transparencyBackgroundBrush != null) + { + Graphics targetGraphics = e.Graphics; + Rectangle clipRectangle = e.ClipRectangle; + targetGraphics.FillRectangle(transparencyBackgroundBrush, clipRectangle); + } + else + { + Graphics targetGraphics = e.Graphics; + targetGraphics.Clear(BackColor); + //base.OnPaintBackground(e); + } + } + + /// + /// Wrapper for makeUndoable flag which was introduced later, will call AddElement with makeundoable set to true + /// + /// the new element + public void AddElement(IDrawableContainer element) + { + AddElement(element, true); + } + + /// + /// Add a new element to the surface + /// + /// the new element + /// true if the adding should be undoable + public void AddElement(IDrawableContainer element, bool makeUndoable) + { + elements.Add(element); + DrawableContainer container = element as DrawableContainer; + if (container != null) + { + container.FieldChanged += element_FieldChanged; + } + element.PropertyChanged += ElementPropertyChanged; + if (element.Status == EditStatus.UNDRAWN) + { + element.Status = EditStatus.IDLE; + } + element.Invalidate(); + if (makeUndoable) + { + MakeUndoable(new AddElementMemento(this, element), false); + } + modified = true; + } + + /// + /// Remove an element of the elements list + /// + /// Element to remove + /// flag specifying if the remove needs to be undoable + public void RemoveElement(IDrawableContainer elementToRemove, bool makeUndoable) + { + DeselectElement(elementToRemove); + elements.Remove(elementToRemove); + DrawableContainer element = elementToRemove as DrawableContainer; + if (element != null) + { + element.FieldChanged -= element_FieldChanged; + } + elementToRemove.PropertyChanged -= ElementPropertyChanged; + // Do not dispose, the memento should!! element.Dispose(); + elementToRemove.Invalidate(); + if (makeUndoable) + { + MakeUndoable(new DeleteElementMemento(this, elementToRemove), false); + } + modified = true; + } + + /// + /// Add the supplied elements to the surface + /// + /// + public void AddElements(DrawableContainerList elementsToAdd) + { + foreach (IDrawableContainer element in elementsToAdd) + { + AddElement(element, true); + } + } + + /// + /// Returns if this surface has selected elements + /// + /// + public bool HasSelectedElements + { + get + { + return (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. + List elementsToRemove = new List(); + foreach (DrawableContainer element in selectedElements) + { + // Collect to remove later + elementsToRemove.Add(element); + } + // Remove now + foreach (DrawableContainer element in elementsToRemove) + { + RemoveElement(element, true); + } + selectedElements.Clear(); + if (movingElementChanged != null) + { + SurfaceElementEventArgs eventArgs = new SurfaceElementEventArgs(); + eventArgs.Elements = selectedElements; + movingElementChanged(this, eventArgs); + } + } + } + + /// + /// Cut the selected elements from the surface to the clipboard + /// + public void CutSelectedElements() + { + if (HasSelectedElements) + { + ClipboardHelper.SetClipboardData(typeof(DrawableContainerList), selectedElements); + RemoveSelectedElements(); + } + } + + /// + /// Copy the selected elements to the clipboard + /// + public void CopySelectedElements() + { + if (HasSelectedElements) + { + ClipboardHelper.SetClipboardData(typeof(DrawableContainerList), 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; + } + } + } + + /// + /// Paste all the elements that are on the clipboard + /// + public void PasteElementFromClipboard() + { + IDataObject clipboard = ClipboardHelper.GetDataObject(); + + List 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(DrawableContainerList).FullName)) + { + DrawableContainerList dcs = (DrawableContainerList)ClipboardHelper.GetFromDataObject(clipboard, typeof(DrawableContainerList)); + if (dcs != null) + { + dcs.Parent = this; + dcs.MoveBy(10, 10); + AddElements(dcs); + FieldAggregator.BindElements(dcs); + DeselectAllElements(); + SelectElements(dcs); + } + } + else if (ClipboardHelper.ContainsImage(clipboard)) + { + int x = 10; + int y = 10; + foreach (Image clipboardImage in ClipboardHelper.GetImages(clipboard)) + { + if (clipboardImage != null) + { + DeselectAllElements(); + IImageContainer container = AddImageContainer(clipboardImage as Bitmap, x, y); + SelectElement(container); + clipboardImage.Dispose(); + x += 10; + y += 10; + } + } + } + else if (ClipboardHelper.ContainsText(clipboard)) + { + string text = ClipboardHelper.GetText(clipboard); + if (text != null) + { + DeselectAllElements(); + ITextContainer textContainer = AddTextContainer(text, HorizontalAlignment.Center, VerticalAlignment.CENTER, + FontFamily.GenericSansSerif, 12f, false, false, false, 2, Color.Black, Color.Transparent); + SelectElement(textContainer); + } + } + } + + /// + /// Duplicate all the selecteded elements + /// + public void DuplicateSelectedElements() + { + LOG.DebugFormat("Duplicating {0} selected elements", selectedElements.Count); + DrawableContainerList dcs = selectedElements.Clone(); + dcs.Parent = this; + dcs.MoveBy(10, 10); + AddElements(dcs); + DeselectAllElements(); + SelectElements(dcs); + } + + /// + /// Deselect the specified element + /// + /// + public void DeselectElement(IDrawableContainer container) + { + container.HideGrippers(); + container.Selected = false; + selectedElements.Remove(container); + FieldAggregator.UnbindElement(container); + if (movingElementChanged != null) + { + SurfaceElementEventArgs eventArgs = new SurfaceElementEventArgs(); + eventArgs.Elements = selectedElements; + movingElementChanged(this, eventArgs); + } + } + + /// + /// Deselect all the selected elements + /// + public void DeselectAllElements() + { + if (HasSelectedElements) + { + while (selectedElements.Count > 0) + { + IDrawableContainer element = selectedElements[0]; + element.Invalidate(); + element.HideGrippers(); + element.Selected = false; + selectedElements.Remove(element); + FieldAggregator.UnbindElement(element); + } + if (movingElementChanged != null) + { + SurfaceElementEventArgs eventArgs = new SurfaceElementEventArgs(); + eventArgs.Elements = selectedElements; + movingElementChanged(this, eventArgs); + } + } + } + + /// + /// Select the supplied element + /// + /// + public void SelectElement(IDrawableContainer container) + { + if (!selectedElements.Contains(container)) + { + selectedElements.Add(container); + container.ShowGrippers(); + container.Selected = true; + FieldAggregator.BindElement(container); + if (movingElementChanged != null) + { + SurfaceElementEventArgs eventArgs = new SurfaceElementEventArgs(); + eventArgs.Elements = selectedElements; + movingElementChanged(this, eventArgs); + } + 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(DrawableContainerList elements) + { + foreach (DrawableContainer element in elements) + { + SelectElement(element); + } + } + + /// + /// 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 DrawableContainerList Elements + { + get + { + return 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 ElementPropertyChanged(object sender, PropertyChangedEventArgs e) + { + //Invalidate(); + } + + public void element_FieldChanged(object sender, FieldChangedEventArgs e) + { + selectedElements.HandleFieldChangedEvent(sender, e); + } + } +} \ No newline at end of file diff --git a/GreenshotImageEditor/Drawing/TextContainer.cs b/GreenshotImageEditor/Drawing/TextContainer.cs new file mode 100644 index 000000000..7005fb008 --- /dev/null +++ b/GreenshotImageEditor/Drawing/TextContainer.cs @@ -0,0 +1,387 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2013 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: http://getgreenshot.org/ + * The Greenshot project is hosted on Sourceforge: http://sourceforge.net/projects/greenshot/ + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using Greenshot.Drawing.Fields; +using Greenshot.Helpers; +using Greenshot.Memento; +using Greenshot.Plugin; +using Greenshot.Plugin.Drawing; +using System; +using System.ComponentModel; +using System.Drawing; +using System.Drawing.Drawing2D; +using System.Drawing.Text; +using System.Runtime.Serialization; +using System.Windows.Forms; + +namespace Greenshot.Drawing +{ + /// + /// Represents a textbox (extends RectangleContainer for border/background support + /// + [Serializable] + public class TextContainer : RectangleContainer, ITextContainer + { + private bool fontInvalidated = true; + // 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 + private bool makeUndoable = false; + private Font font; + + /// + /// The StringFormat object is not serializable!! + /// + [NonSerialized] + private StringFormat stringFormat; + + private string text; + // there is a binding on the following property! + public string Text + { + get { return text; } + set + { + ChangeText(value, true); + } + } + + internal void ChangeText(string newText, bool allowUndoable) + { + if ((text == null && newText != null) || !text.Equals(newText)) + { + if (makeUndoable && allowUndoable) + { + makeUndoable = false; + parent.MakeUndoable(new TextChangeMemento(this), false); + } + text = newText; + OnPropertyChanged("Text"); + } + } + + [NonSerialized] + private TextBox textBox; + + public TextContainer(Surface parent) + : base(parent) + { + Init(); + 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, HorizontalAlignment.Center); + AddField(GetType(), FieldType.TEXT_VERTICAL_ALIGNMENT, VerticalAlignment.CENTER); + stringFormat = new StringFormat(); + stringFormat.Trimming = StringTrimming.EllipsisWord; + } + + [OnDeserialized] + private void OnDeserialized(StreamingContext context) + { + stringFormat = new StringFormat(); + Init(); + UpdateFormat(); + } + + 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() + { + CreateTextBox(); + PropertyChanged += TextContainer_PropertyChanged; + FieldChanged += TextContainer_FieldChanged; + } + + public void FitToText() + { + UpdateFormat(); + 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 (e.PropertyName.Equals("Selected")) + { + if (!Selected && textBox.Visible) + { + HideTextBox(); + } + else if (Selected && Status == EditStatus.DRAWING) + { + ShowTextBox(); + } + } + if (textBox.Visible) + { + UpdateTextBoxPosition(); + UpdateTextBoxFormat(); + textBox.Invalidate(); + } + } + + private void TextContainer_FieldChanged(object sender, FieldChangedEventArgs e) + { + if (textBox.Visible) + { + UpdateTextBoxFormat(); + textBox.Invalidate(); + } + else + { + UpdateFormat(); + //Invalidate(); + } + font.Dispose(); + font = null; + fontInvalidated = true; + } + + public override void OnDoubleClick() + { + ShowTextBox(); + textBox.Focus(); + } + + private void CreateTextBox() + { + textBox = new TextBox(); + textBox.ImeMode = ImeMode.On; + textBox.Multiline = true; + textBox.AcceptsTab = true; + textBox.AcceptsReturn = true; + textBox.DataBindings.Add("Text", this, "Text", false, DataSourceUpdateMode.OnPropertyChanged); + textBox.LostFocus += textBox_LostFocus; + textBox.KeyDown += textBox_KeyDown; + textBox.BorderStyle = BorderStyle.FixedSingle; + textBox.Visible = false; + } + + private void ShowTextBox() + { + parent.KeysLocked = true; + parent.Controls.Add(textBox); + textBox.Show(); + textBox.Focus(); + } + + private void HideTextBox() + { + parent.Focus(); + textBox.Hide(); + parent.KeysLocked = false; + parent.Controls.Remove(textBox); + } + + private void UpdateFormat() + { + string fontFamily = GetFieldValueAsString(FieldType.FONT_FAMILY); + bool fontBold = GetFieldValueAsBool(FieldType.FONT_BOLD); + bool fontItalic = GetFieldValueAsBool(FieldType.FONT_ITALIC); + float fontSize = GetFieldValueAsFloat(FieldType.FONT_SIZE); + + if (fontInvalidated && fontFamily != null && fontSize != 0) + { + FontStyle fs = FontStyle.Regular; + + bool hasStyle = false; + using (FontFamily fam = new FontFamily(fontFamily)) + { + bool boldAvailable = fam.IsStyleAvailable(FontStyle.Bold); + if (fontBold && boldAvailable) + { + fs |= FontStyle.Bold; + hasStyle = true; + } + + bool italicAvailable = fam.IsStyleAvailable(FontStyle.Italic); + if (fontItalic && italicAvailable) + { + fs |= FontStyle.Italic; + hasStyle = true; + } + + if (!hasStyle) + { + bool regularAvailable = fam.IsStyleAvailable(FontStyle.Regular); + if (regularAvailable) + { + fs = FontStyle.Regular; + } + else + { + if (boldAvailable) + { + fs = FontStyle.Bold; + } + else if (italicAvailable) + { + fs = FontStyle.Italic; + } + } + } + font = new Font(fam, fontSize, fs, GraphicsUnit.Pixel); + } + fontInvalidated = false; + } + + stringFormat.Alignment = (StringAlignment)GetFieldValue(FieldType.TEXT_HORIZONTAL_ALIGNMENT); + stringFormat.LineAlignment = (StringAlignment)GetFieldValue(FieldType.TEXT_VERTICAL_ALIGNMENT); + } + + private void UpdateTextBoxPosition() + { + textBox.Left = Left; + textBox.Top = Top; + textBox.Width = Width; + textBox.Height = Height; + } + + public override void ApplyBounds(RectangleF newBounds) + { + base.ApplyBounds(newBounds); + UpdateTextBoxPosition(); + } + + private void UpdateTextBoxFormat() + { + UpdateFormat(); + Color lineColor = GetFieldValueAsColor(FieldType.LINE_COLOR); + textBox.ForeColor = lineColor; + textBox.Font = font; + } + + 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; + } + } + + 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); + UpdateFormat(); + 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 (text == null || text.Length == 0) + { + 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); + int textOffset = (lineThickness > 0) ? (int)Math.Ceiling(lineThickness / 2d) : 0; + // draw shadow before anything else + if (shadow && (fillColor == Color.Transparent || fillColor == Color.Empty)) + { + int basealpha = 100; + int alpha = basealpha; + int steps = 5; + int currentStep = 1; + while (currentStep <= steps) + { + int offset = currentStep; + Rectangle shadowRect = GuiRectangle.GetGuiRectangle(Left + offset, Top + offset, Width, 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 = alpha - basealpha / steps; + } + } + } + Color lineColor = GetFieldValueAsColor(FieldType.LINE_COLOR); + Rectangle fontRect = rect; + if (lineThickness > 0) + { + graphics.SmoothingMode = SmoothingMode.HighSpeed; + fontRect.Inflate(-textOffset, -textOffset); + } + graphics.SmoothingMode = SmoothingMode.HighQuality; + using (Brush fontBrush = new SolidBrush(lineColor)) + { + graphics.DrawString(text, font, fontBrush, fontRect, stringFormat); + } + } + + 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/GreenshotImageEditor/Forms/BaseForm.cs b/GreenshotImageEditor/Forms/BaseForm.cs new file mode 100644 index 000000000..390d3bf4e --- /dev/null +++ b/GreenshotImageEditor/Forms/BaseForm.cs @@ -0,0 +1,32 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2013 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: http://getgreenshot.org/ + * The Greenshot project is hosted on Sourceforge: http://sourceforge.net/projects/greenshot/ + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using GreenshotPlugin.Controls; + +namespace Greenshot +{ + /// + /// This class is only here to help in the Designer mode, so it's clear where the language files are + /// + public class BaseForm : GreenshotForm + { + } +} \ No newline at end of file diff --git a/GreenshotImageEditor/Forms/ColorDialog.Designer.cs b/GreenshotImageEditor/Forms/ColorDialog.Designer.cs new file mode 100644 index 000000000..07453032b --- /dev/null +++ b/GreenshotImageEditor/Forms/ColorDialog.Designer.cs @@ -0,0 +1,285 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2013 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: http://getgreenshot.org/ + * The Greenshot project is hosted on Sourceforge: http://sourceforge.net/projects/greenshot/ + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +namespace Greenshot { + 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 GreenshotPlugin.Controls.GreenshotButton(); + this.colorPanel = new System.Windows.Forms.Panel(); + this.labelHtmlColor = new GreenshotPlugin.Controls.GreenshotLabel(); + this.textBoxHtmlColor = new System.Windows.Forms.TextBox(); + this.labelRed = new GreenshotPlugin.Controls.GreenshotLabel(); + this.labelGreen = new GreenshotPlugin.Controls.GreenshotLabel(); + this.labelBlue = new GreenshotPlugin.Controls.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 GreenshotPlugin.Controls.GreenshotLabel(); + this.textBoxAlpha = new System.Windows.Forms.TextBox(); + this.labelAlpha = new GreenshotPlugin.Controls.GreenshotLabel(); + this.btnApply = new GreenshotPlugin.Controls.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 GreenshotPlugin.Controls.GreenshotLabel labelRed; + private GreenshotPlugin.Controls.GreenshotLabel labelGreen; + private GreenshotPlugin.Controls.GreenshotLabel labelBlue; + private System.Windows.Forms.TextBox textBoxHtmlColor; + private GreenshotPlugin.Controls.GreenshotLabel labelRecentColors; + private GreenshotPlugin.Controls.GreenshotLabel labelAlpha; + private GreenshotPlugin.Controls.GreenshotLabel labelHtmlColor; + private GreenshotPlugin.Controls.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 GreenshotPlugin.Controls.GreenshotButton btnTransparent; + private Greenshot.Controls.Pipette pipette; + + + + + + } +} diff --git a/GreenshotImageEditor/Forms/ColorDialog.cs b/GreenshotImageEditor/Forms/ColorDialog.cs new file mode 100644 index 000000000..ea1d701b0 --- /dev/null +++ b/GreenshotImageEditor/Forms/ColorDialog.cs @@ -0,0 +1,274 @@ +/* + * Greenshot - a free and open source screenshot tool + * Copyright (C) 2007-2013 Thomas Braun, Jens Klingen, Robin Krom + * + * For more information see: http://getgreenshot.org/ + * The Greenshot project is hosted on Sourceforge: http://sourceforge.net/projects/greenshot/ + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using System.Globalization; +using Greenshot.Configuration; +using Greenshot.Controls; +using Greenshot.IniFile; +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Threading; +using System.Windows.Forms; + +namespace Greenshot +{ + /// + /// Description of ColorDialog. + /// + public partial class ColorDialog : BaseForm + { + private static ColorDialog uniqueInstance; + private static EditorConfiguration editorConfiguration = IniConfig.GetIniSection(); + + private ColorDialog() + { + SuspendLayout(); + InitializeComponent(); + SuspendLayout(); + createColorPalette(5, 5, 15, 15); + createLastUsedColorButtonRow(5, 190, 15, 15); + ResumeLayout(); + updateRecentColorsButtonRow(); + } + + public static ColorDialog GetInstance() + { + if (uniqueInstance == null) + { + uniqueInstance = new ColorDialog(); + } + return uniqueInstance; + } + + private List