From 8b45489b1123ea33adec779b32ce8dfb0f1a6a1a Mon Sep 17 00:00:00 2001 From: Killy Date: Sun, 26 Apr 2020 16:50:17 +0300 Subject: [PATCH 01/29] Zoom feature for the editor --- Greenshot/Drawing/Adorners/AbstractAdorner.cs | 17 ++ Greenshot/Drawing/Adorners/MoveAdorner.cs | 2 +- Greenshot/Drawing/Adorners/ResizeAdorner.cs | 2 +- Greenshot/Drawing/Adorners/TargetAdorner.cs | 2 +- Greenshot/Drawing/CropContainer.cs | 17 +- Greenshot/Drawing/DrawableContainer.cs | 2 +- Greenshot/Drawing/DrawableContainerList.cs | 2 +- Greenshot/Drawing/Surface.cs | 100 +++++++-- Greenshot/Drawing/TextContainer.cs | 14 +- Greenshot/Forms/ImageEditorForm.Designer.cs | 212 +++++++++++++++++- Greenshot/Forms/ImageEditorForm.cs | 167 ++++++++++++-- Greenshot/Forms/ImageEditorForm.resx | 81 +++++++ .../icons/fugue/magnifier-zoom-actual.png | Bin 0 -> 742 bytes Greenshot/icons/fugue/magnifier-zoom-fit.png | Bin 0 -> 726 bytes Greenshot/icons/fugue/magnifier-zoom-in.png | Bin 0 -> 733 bytes Greenshot/icons/fugue/magnifier-zoom-out.png | Bin 0 -> 707 bytes Greenshot/icons/fugue/magnifier-zoom.png | Bin 0 -> 676 bytes GreenshotPlugin/Interfaces/ISurface.cs | 19 +- 18 files changed, 585 insertions(+), 52 deletions(-) create mode 100644 Greenshot/icons/fugue/magnifier-zoom-actual.png create mode 100644 Greenshot/icons/fugue/magnifier-zoom-fit.png create mode 100644 Greenshot/icons/fugue/magnifier-zoom-in.png create mode 100644 Greenshot/icons/fugue/magnifier-zoom-out.png create mode 100644 Greenshot/icons/fugue/magnifier-zoom.png diff --git a/Greenshot/Drawing/Adorners/AbstractAdorner.cs b/Greenshot/Drawing/Adorners/AbstractAdorner.cs index 0612753aa..fc3d72371 100644 --- a/Greenshot/Drawing/Adorners/AbstractAdorner.cs +++ b/Greenshot/Drawing/Adorners/AbstractAdorner.cs @@ -116,6 +116,23 @@ namespace Greenshot.Drawing.Adorners } } + /// + /// Return the bounds of the Adorner as displayed on the parent Surface + /// + protected virtual Rectangle SurfaceBounds + { + get + { + Point[] points = { Location }; + var matrix = Owner.Parent.ZoomMatrix; + if (!matrix.IsIdentity) + { + matrix.TransformPoints(points); + } + return new Rectangle(points[0].X - _size.Width / 2, points[0].Y - _size.Height / 2, _size.Width, _size.Height); + } + } + /// /// The adorner is active if the edit status is not idle or undrawn /// diff --git a/Greenshot/Drawing/Adorners/MoveAdorner.cs b/Greenshot/Drawing/Adorners/MoveAdorner.cs index ec3dcf2d3..21c42a9fa 100644 --- a/Greenshot/Drawing/Adorners/MoveAdorner.cs +++ b/Greenshot/Drawing/Adorners/MoveAdorner.cs @@ -142,7 +142,7 @@ namespace Greenshot.Drawing.Adorners { Graphics targetGraphics = paintEventArgs.Graphics; - var bounds = Bounds; + var bounds = SurfaceBounds; GraphicsState state = targetGraphics.Save(); targetGraphics.SmoothingMode = SmoothingMode.None; diff --git a/Greenshot/Drawing/Adorners/ResizeAdorner.cs b/Greenshot/Drawing/Adorners/ResizeAdorner.cs index e75126f10..ef61b17bc 100644 --- a/Greenshot/Drawing/Adorners/ResizeAdorner.cs +++ b/Greenshot/Drawing/Adorners/ResizeAdorner.cs @@ -169,7 +169,7 @@ namespace Greenshot.Drawing.Adorners { Graphics targetGraphics = paintEventArgs.Graphics; - var bounds = Bounds; + var bounds = SurfaceBounds; GraphicsState state = targetGraphics.Save(); targetGraphics.SmoothingMode = SmoothingMode.None; diff --git a/Greenshot/Drawing/Adorners/TargetAdorner.cs b/Greenshot/Drawing/Adorners/TargetAdorner.cs index 8ca0a91ef..23cf29d09 100644 --- a/Greenshot/Drawing/Adorners/TargetAdorner.cs +++ b/Greenshot/Drawing/Adorners/TargetAdorner.cs @@ -96,7 +96,7 @@ namespace Greenshot.Drawing.Adorners { Graphics targetGraphics = paintEventArgs.Graphics; - var bounds = Bounds; + var bounds = SurfaceBounds; targetGraphics.FillRectangle(Brushes.Green, bounds.X, bounds.Y, bounds.Width, bounds.Height); } diff --git a/Greenshot/Drawing/CropContainer.cs b/Greenshot/Drawing/CropContainer.cs index 2dc9761c0..f5288074d 100644 --- a/Greenshot/Drawing/CropContainer.cs +++ b/Greenshot/Drawing/CropContainer.cs @@ -56,7 +56,15 @@ namespace Greenshot.Drawing { /// 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 => new Rectangle(0,0,_parent?.Width??0, _parent?.Height ?? 0); + public override Rectangle DrawingBounds { + get { + if (_parent?.Image is Image image) { + return new Rectangle(0, 0, image.Width, image.Height); + } else { + return new Rectangle(); + } + } + } public override void Draw(Graphics g, RenderMode rm) { if (_parent == null) @@ -67,17 +75,18 @@ namespace Greenshot.Drawing { using Brush cropBrush = new SolidBrush(Color.FromArgb(100, 150, 150, 100)); Rectangle cropRectangle = GuiRectangle.GetGuiRectangle(Left, Top, Width, Height); Rectangle selectionRect = new Rectangle(cropRectangle.Left - 1, cropRectangle.Top - 1, cropRectangle.Width + 1, cropRectangle.Height + 1); + Size imageSize = _parent.Image.Size; DrawSelectionBorder(g, selectionRect); // top - g.FillRectangle(cropBrush, new Rectangle(0, 0, _parent.Width, cropRectangle.Top)); + g.FillRectangle(cropBrush, new Rectangle(0, 0, imageSize.Width, cropRectangle.Top)); // left g.FillRectangle(cropBrush, new Rectangle(0, cropRectangle.Top, cropRectangle.Left, cropRectangle.Height)); // right - g.FillRectangle(cropBrush, new Rectangle(cropRectangle.Left + cropRectangle.Width, cropRectangle.Top, _parent.Width - (cropRectangle.Left + cropRectangle.Width), cropRectangle.Height)); + g.FillRectangle(cropBrush, new Rectangle(cropRectangle.Left + cropRectangle.Width, cropRectangle.Top, imageSize.Width - (cropRectangle.Left + cropRectangle.Width), cropRectangle.Height)); // bottom - g.FillRectangle(cropBrush, new Rectangle(0, cropRectangle.Top + cropRectangle.Height, _parent.Width, _parent.Height - (cropRectangle.Top + cropRectangle.Height))); + g.FillRectangle(cropBrush, new Rectangle(0, cropRectangle.Top + cropRectangle.Height, imageSize.Width, imageSize.Height - (cropRectangle.Top + cropRectangle.Height))); } public override bool HasContextMenu { diff --git a/Greenshot/Drawing/DrawableContainer.cs b/Greenshot/Drawing/DrawableContainer.cs index 8d805b1f0..65ff42de3 100644 --- a/Greenshot/Drawing/DrawableContainer.cs +++ b/Greenshot/Drawing/DrawableContainer.cs @@ -298,7 +298,7 @@ namespace Greenshot.Drawing public virtual void Invalidate() { if (Status != EditStatus.UNDRAWN) { - _parent?.Invalidate(DrawingBounds); + _parent?.InvalidateElements(DrawingBounds); } } diff --git a/Greenshot/Drawing/DrawableContainerList.cs b/Greenshot/Drawing/DrawableContainerList.cs index d3c325f5b..17b879538 100644 --- a/Greenshot/Drawing/DrawableContainerList.cs +++ b/Greenshot/Drawing/DrawableContainerList.cs @@ -286,7 +286,7 @@ namespace Greenshot.Drawing { { region = Rectangle.Union(region, dc.DrawingBounds); } - Parent.Invalidate(region); + Parent.InvalidateElements(region); } /// /// Indicates whether the given list of elements can be pulled up, diff --git a/Greenshot/Drawing/Surface.cs b/Greenshot/Drawing/Surface.cs index bd56db3a2..5c38bea8e 100644 --- a/Greenshot/Drawing/Surface.cs +++ b/Greenshot/Drawing/Surface.cs @@ -298,10 +298,34 @@ namespace Greenshot.Drawing set { _image = value; - Size = _image.Size; + UpdateSize(); } } + [NonSerialized] + private float _zoomFactor = 1.0f; + public float ZoomFactor + { + get => _zoomFactor; + set + { + _zoomFactor = value; + ZoomMatrix = new Matrix(_zoomFactor, 0, 0, _zoomFactor, 0, 0); + UpdateSize(); + } + } + + public Matrix ZoomMatrix { get; private set; } = new Matrix(1, 0, 0, 1, 0, 0); + + /// + /// Sets the surface size as zoomed image size. + /// + private void UpdateSize() + { + var size = _image.Size; + Size = new Size((int)(size.Width * _zoomFactor), (int)(size.Height * _zoomFactor)); + } + /// /// The field aggregator is that which is used to have access to all the fields inside the currently selected elements. /// e.g. used to decided if and which line thickness is shown when multiple elements are selected. @@ -447,7 +471,6 @@ namespace Greenshot.Drawing // Set new values Image = newImage; - Size = newImage.Size; _modified = true; } @@ -1086,6 +1109,12 @@ namespace Greenshot.Drawing return null; } + /// + /// Translate mouse coordinates as if they were applied directly to unscaled image. + /// + private MouseEventArgs InverseZoomMouseCoordinates(MouseEventArgs e) + => new MouseEventArgs(e.Button, e.Clicks, (int)(e.X / _zoomFactor), (int)(e.Y / _zoomFactor), e.Delta); + /// /// This event handler is called when someone presses the mouse on a surface. /// @@ -1093,6 +1122,7 @@ namespace Greenshot.Drawing /// private void SurfaceMouseDown(object sender, MouseEventArgs e) { + e = InverseZoomMouseCoordinates(e); // Handle Adorners var adorner = FindActiveAdorner(e); @@ -1187,6 +1217,7 @@ namespace Greenshot.Drawing /// private void SurfaceMouseUp(object sender, MouseEventArgs e) { + e = InverseZoomMouseCoordinates(e); // Handle Adorners var adorner = FindActiveAdorner(e); @@ -1276,6 +1307,8 @@ namespace Greenshot.Drawing /// private void SurfaceMouseMove(object sender, MouseEventArgs e) { + e = InverseZoomMouseCoordinates(e); + // Handle Adorners var adorner = FindActiveAdorner(e); if (adorner != null) @@ -1371,6 +1404,25 @@ namespace Greenshot.Drawing return GetImage(RenderMode.EXPORT); } + private Rectangle ZoomClipRectangle(Rectangle rc, double scale) + => new Rectangle( + (int)(rc.X * scale), + (int)(rc.Y * scale), + (int)((rc.Width + 1) * scale) + 1, // making sure to redraw enough pixels when moving scaled image + (int)((rc.Height + 1) * scale) + 1 + ); + + private RectangleF ZoomClipRectangle(RectangleF rc, double scale) + => new RectangleF( + (float)Math.Floor(rc.X * scale), + (float)Math.Floor(rc.Y * scale), + (float)Math.Ceiling(rc.Width * scale), + (float)Math.Ceiling(rc.Height * scale) + ); + + public void InvalidateElements(Rectangle rc) + => Invalidate(ZoomClipRectangle(rc, _zoomFactor)); + /// /// This is the event handler for the Paint Event, try to draw as little as possible! /// @@ -1379,14 +1431,16 @@ namespace Greenshot.Drawing private void SurfacePaint(object sender, PaintEventArgs paintEventArgs) { Graphics targetGraphics = paintEventArgs.Graphics; - Rectangle clipRectangle = paintEventArgs.ClipRectangle; - if (Rectangle.Empty.Equals(clipRectangle)) + Rectangle targetClipRectangle = paintEventArgs.ClipRectangle; + if (Rectangle.Empty.Equals(targetClipRectangle)) { LOG.Debug("Empty cliprectangle??"); return; } + Rectangle imageClipRectangle = ZoomClipRectangle(targetClipRectangle, 1.0 / _zoomFactor); - if (_elements.HasIntersectingFilters(clipRectangle)) + bool isZoomedIn = ZoomFactor > 1f; + if (_elements.HasIntersectingFilters(imageClipRectangle) || isZoomedIn) { if (_buffer != null) { @@ -1409,18 +1463,38 @@ namespace Greenshot.Drawing //graphics.PixelOffsetMode = PixelOffsetMode.HighQuality; //graphics.CompositingQuality = CompositingQuality.HighQuality; //graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; - DrawBackground(graphics, clipRectangle); - graphics.DrawImage(Image, clipRectangle, clipRectangle, GraphicsUnit.Pixel); - graphics.SetClip(targetGraphics); - _elements.Draw(graphics, _buffer, RenderMode.EDIT, clipRectangle); + DrawBackground(graphics, imageClipRectangle); + graphics.DrawImage(Image, imageClipRectangle, imageClipRectangle, GraphicsUnit.Pixel); + graphics.SetClip(ZoomClipRectangle(targetGraphics.ClipBounds, 1.0 / _zoomFactor)); + _elements.Draw(graphics, _buffer, RenderMode.EDIT, imageClipRectangle); } - targetGraphics.DrawImage(_buffer, clipRectangle, clipRectangle, GraphicsUnit.Pixel); + targetGraphics.ScaleTransform(_zoomFactor, _zoomFactor); + if (isZoomedIn) + { + var state = targetGraphics.Save(); + targetGraphics.SmoothingMode = SmoothingMode.None; + targetGraphics.InterpolationMode = InterpolationMode.NearestNeighbor; + targetGraphics.CompositingQuality = CompositingQuality.HighQuality; + targetGraphics.PixelOffsetMode = PixelOffsetMode.None; + + targetGraphics.DrawImage(_buffer, imageClipRectangle, imageClipRectangle, GraphicsUnit.Pixel); + + targetGraphics.Restore(state); + } + else + { + targetGraphics.DrawImage(_buffer, imageClipRectangle, imageClipRectangle, GraphicsUnit.Pixel); + } + targetGraphics.ResetTransform(); } else { - DrawBackground(targetGraphics, clipRectangle); - targetGraphics.DrawImage(Image, clipRectangle, clipRectangle, GraphicsUnit.Pixel); - _elements.Draw(targetGraphics, null, RenderMode.EDIT, clipRectangle); + DrawBackground(targetGraphics, targetClipRectangle); + + targetGraphics.ScaleTransform(_zoomFactor, _zoomFactor); + targetGraphics.DrawImage(Image, imageClipRectangle, imageClipRectangle, GraphicsUnit.Pixel); + _elements.Draw(targetGraphics, null, RenderMode.EDIT, imageClipRectangle); + targetGraphics.ResetTransform(); } // No clipping for the adorners diff --git a/Greenshot/Drawing/TextContainer.cs b/Greenshot/Drawing/TextContainer.cs index 0f4866d71..bd4c75274 100644 --- a/Greenshot/Drawing/TextContainer.cs +++ b/Greenshot/Drawing/TextContainer.cs @@ -442,14 +442,20 @@ namespace Greenshot.Drawing correction = -1; } Rectangle absRectangle = GuiRectangle.GetGuiRectangle(Left, Top, Width, Height); - _textBox.Left = absRectangle.Left + lineWidth; - _textBox.Top = absRectangle.Top + lineWidth; + Point[] points = { absRectangle.Location, absRectangle.Location + absRectangle.Size }; + var matrix = Parent.ZoomMatrix; + if (!matrix.IsIdentity) + { + matrix.TransformPoints(points); + } + _textBox.Left = points[0].X + lineWidth; + _textBox.Top = points[0].Y + lineWidth; if (lineThickness <= 1) { lineWidth = 0; } - _textBox.Width = absRectangle.Width - 2 * lineWidth + correction; - _textBox.Height = absRectangle.Height - 2 * lineWidth + correction; + _textBox.Width = points[1].X - points[0].X - 2 * lineWidth + correction; + _textBox.Height = points[1].Y - points[0].Y - 2 * lineWidth + correction; } public override void ApplyBounds(RectangleF newBounds) diff --git a/Greenshot/Forms/ImageEditorForm.Designer.cs b/Greenshot/Forms/ImageEditorForm.Designer.cs index 9a653cc21..5789a19a6 100644 --- a/Greenshot/Forms/ImageEditorForm.Designer.cs +++ b/Greenshot/Forms/ImageEditorForm.Designer.cs @@ -194,6 +194,26 @@ namespace Greenshot { this.alignLeftToolStripMenuItem = new GreenshotPlugin.Controls.GreenshotToolStripMenuItem(); this.alignCenterToolStripMenuItem = new GreenshotPlugin.Controls.GreenshotToolStripMenuItem(); this.alignRightToolStripMenuItem = new GreenshotPlugin.Controls.GreenshotToolStripMenuItem(); + this.zoomMenuStrip = new System.Windows.Forms.ContextMenuStrip(this.components); + this.zoomInMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.zoomOutMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.zoomMenuSeparator1 = new System.Windows.Forms.ToolStripSeparator(); + this.zoomBestFitMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.zoomMenuSeparator2 = new System.Windows.Forms.ToolStripSeparator(); + this.zoom25MenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.zoom50MenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.zoom66MenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.zoom75MenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.zoomMenuSeparator3 = new System.Windows.Forms.ToolStripSeparator(); + this.zoomActualSizeMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.zoomMenuSeparator4 = new System.Windows.Forms.ToolStripSeparator(); + this.zoom200MenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.zoom300MenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.zoom400MenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.zoom600MenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.zoomStatusDropDownBtn = new System.Windows.Forms.ToolStripDropDownButton(); + this.zoomMainMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.statusStripSpacer = new System.Windows.Forms.ToolStripStatusLabel(); this.topToolStripContainer.BottomToolStripPanel.SuspendLayout(); this.topToolStripContainer.ContentPanel.SuspendLayout(); this.topToolStripContainer.LeftToolStripPanel.SuspendLayout(); @@ -203,6 +223,7 @@ namespace Greenshot { this.tableLayoutPanel1.SuspendLayout(); this.toolsToolStrip.SuspendLayout(); this.menuStrip1.SuspendLayout(); + this.zoomMenuStrip.SuspendLayout(); this.destinationsToolStrip.SuspendLayout(); this.propertiesToolStrip.SuspendLayout(); this.fileSavedStatusContextMenu.SuspendLayout(); @@ -238,7 +259,9 @@ namespace Greenshot { this.statusStrip1.Dock = System.Windows.Forms.DockStyle.None; this.statusStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { this.dimensionsLabel, - this.statusLabel}); + this.statusLabel, + this.statusStripSpacer, + this.zoomStatusDropDownBtn}); this.statusStrip1.Location = new System.Drawing.Point(0, 0); this.statusStrip1.Name = "statusStrip1"; this.statusStrip1.Size = new System.Drawing.Size(785, 24); @@ -538,6 +561,7 @@ namespace Greenshot { this.editToolStripMenuItem, this.objectToolStripMenuItem, this.pluginToolStripMenuItem, + this.zoomMainMenuItem, this.helpToolStripMenuItem}); this.menuStrip1.Name = "menuStrip1"; this.menuStrip1.BackColor = System.Drawing.SystemColors.Control; @@ -1624,6 +1648,171 @@ namespace Greenshot { this.alignRightToolStripMenuItem.Name = "alignRightToolStripMenuItem"; this.alignRightToolStripMenuItem.Tag = System.Drawing.StringAlignment.Far; // + // zoomMainMenuItem + // + this.zoomMainMenuItem.DropDown = this.zoomMenuStrip; + this.zoomMainMenuItem.Name = "zoomMainMenuItem"; + this.zoomMainMenuItem.Size = new System.Drawing.Size(51, 20); + this.zoomMainMenuItem.Text = "Zoom"; + // + // zoomMenuStrip + // + this.zoomMenuStrip.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.zoomInMenuItem, + this.zoomOutMenuItem, + this.zoomMenuSeparator1, + this.zoomBestFitMenuItem, + this.zoomMenuSeparator2, + this.zoom25MenuItem, + this.zoom50MenuItem, + this.zoom66MenuItem, + this.zoom75MenuItem, + this.zoomMenuSeparator3, + this.zoomActualSizeMenuItem, + this.zoomMenuSeparator4, + this.zoom200MenuItem, + this.zoom300MenuItem, + this.zoom400MenuItem, + this.zoom600MenuItem}); + this.zoomMenuStrip.Name = "zoomMenuStrip"; + this.zoomMenuStrip.Size = new System.Drawing.Size(210, 292); + // + // zoomInMenuItem + // + this.zoomInMenuItem.Image = ((System.Drawing.Image)(resources.GetObject("zoomInMenuItem.Image"))); + this.zoomInMenuItem.Name = "zoomInMenuItem"; + this.zoomInMenuItem.ShortcutKeyDisplayString = "Ctrl++"; + this.zoomInMenuItem.Size = new System.Drawing.Size(209, 22); + this.zoomInMenuItem.Text = "Zoom In"; + this.zoomInMenuItem.Click += new System.EventHandler(this.ZoomInMenuItemClick); + // + // zoomOutMenuItem + // + this.zoomOutMenuItem.Image = ((System.Drawing.Image)(resources.GetObject("zoomOutMenuItem.Image"))); + this.zoomOutMenuItem.Name = "zoomOutMenuItem"; + this.zoomOutMenuItem.ShortcutKeyDisplayString = "Ctrl+-"; + this.zoomOutMenuItem.Size = new System.Drawing.Size(209, 22); + this.zoomOutMenuItem.Text = "Zoom Out"; + this.zoomOutMenuItem.Click += new System.EventHandler(this.ZoomOutMenuItemClick); + // + // zoomMenuSeparator1 + // + this.zoomMenuSeparator1.Name = "zoomMenuSeparator1"; + this.zoomMenuSeparator1.Size = new System.Drawing.Size(206, 6); + // + // zoomBestFitMenuItem + // + this.zoomBestFitMenuItem.Image = ((System.Drawing.Image)(resources.GetObject("zoomBestFitMenuItem.Image"))); + this.zoomBestFitMenuItem.Name = "zoomBestFitMenuItem"; + this.zoomBestFitMenuItem.ShortcutKeyDisplayString = "Ctrl+*"; + this.zoomBestFitMenuItem.Size = new System.Drawing.Size(209, 22); + this.zoomBestFitMenuItem.Text = "Best Fit"; + this.zoomBestFitMenuItem.Click += new System.EventHandler(this.ZoomBestFitMenuItemClick); + // + // zoomMenuSeparator2 + // + this.zoomMenuSeparator2.Name = "zoomMenuSeparator2"; + this.zoomMenuSeparator2.Size = new System.Drawing.Size(206, 6); + // + // zoom25MenuItem + // + this.zoom25MenuItem.Name = "zoom25MenuItem"; + this.zoom25MenuItem.Size = new System.Drawing.Size(209, 22); + this.zoom25MenuItem.Tag = "25"; + this.zoom25MenuItem.Text = "25%"; + this.zoom25MenuItem.Click += new System.EventHandler(this.ZoomSetValueMenuItemClick); + // + // zoom50MenuItem + // + this.zoom50MenuItem.Name = "zoom50MenuItem"; + this.zoom50MenuItem.Size = new System.Drawing.Size(209, 22); + this.zoom50MenuItem.Tag = "50"; + this.zoom50MenuItem.Text = "50%"; + this.zoom50MenuItem.Click += new System.EventHandler(this.ZoomSetValueMenuItemClick); + // + // zoom66MenuItem + // + this.zoom66MenuItem.Name = "zoom66MenuItem"; + this.zoom66MenuItem.Size = new System.Drawing.Size(209, 22); + this.zoom66MenuItem.Tag = "66"; + this.zoom66MenuItem.Text = "66%"; + this.zoom66MenuItem.Click += new System.EventHandler(this.ZoomSetValueMenuItemClick); + // + // zoom75MenuItem + // + this.zoom75MenuItem.Name = "zoom75MenuItem"; + this.zoom75MenuItem.Size = new System.Drawing.Size(209, 22); + this.zoom75MenuItem.Tag = "75"; + this.zoom75MenuItem.Text = "75%"; + this.zoom75MenuItem.Click += new System.EventHandler(this.ZoomSetValueMenuItemClick); + // + // zoomMenuSeparator3 + // + this.zoomMenuSeparator3.Name = "zoomMenuSeparator3"; + this.zoomMenuSeparator3.Size = new System.Drawing.Size(206, 6); + // + // zoomActualSizeMenuItem + // + this.zoomActualSizeMenuItem.Image = ((System.Drawing.Image)(resources.GetObject("zoomActualSizeMenuItem.Image"))); + this.zoomActualSizeMenuItem.Name = "zoomActualSizeMenuItem"; + this.zoomActualSizeMenuItem.ShortcutKeyDisplayString = "Ctrl+/"; + this.zoomActualSizeMenuItem.Size = new System.Drawing.Size(209, 22); + this.zoomActualSizeMenuItem.Tag = "100"; + this.zoomActualSizeMenuItem.Text = "100% - Actual Size"; + this.zoomActualSizeMenuItem.Click += new System.EventHandler(this.ZoomSetValueMenuItemClick); + // + // zoomMenuSeparator4 + // + this.zoomMenuSeparator4.Name = "zoomMenuSeparator4"; + this.zoomMenuSeparator4.Size = new System.Drawing.Size(206, 6); + // + // zoom200MenuItem + // + this.zoom200MenuItem.Name = "zoom200MenuItem"; + this.zoom200MenuItem.Size = new System.Drawing.Size(209, 22); + this.zoom200MenuItem.Tag = "200"; + this.zoom200MenuItem.Text = "200%"; + this.zoom200MenuItem.Click += new System.EventHandler(this.ZoomSetValueMenuItemClick); + // + // zoom300MenuItem + // + this.zoom300MenuItem.Name = "zoom300MenuItem"; + this.zoom300MenuItem.Size = new System.Drawing.Size(209, 22); + this.zoom300MenuItem.Tag = "300"; + this.zoom300MenuItem.Text = "300%"; + this.zoom300MenuItem.Click += new System.EventHandler(this.ZoomSetValueMenuItemClick); + // + // zoom400MenuItem + // + this.zoom400MenuItem.Name = "zoom400MenuItem"; + this.zoom400MenuItem.Size = new System.Drawing.Size(209, 22); + this.zoom400MenuItem.Tag = "400"; + this.zoom400MenuItem.Text = "400%"; + this.zoom400MenuItem.Click += new System.EventHandler(this.ZoomSetValueMenuItemClick); + // + // zoom600MenuItem + // + this.zoom600MenuItem.Name = "zoom600MenuItem"; + this.zoom600MenuItem.Size = new System.Drawing.Size(209, 22); + this.zoom600MenuItem.Tag = "600"; + this.zoom600MenuItem.Text = "600%"; + this.zoom600MenuItem.Click += new System.EventHandler(this.ZoomSetValueMenuItemClick); + // + // statusStripSpacer + // + this.statusStripSpacer.Name = "statusStripSpacer"; + this.statusStripSpacer.Size = new System.Drawing.Size(599, 19); + this.statusStripSpacer.Spring = true; + // + // zoomStatusDropDownBtn + // + this.zoomStatusDropDownBtn.DropDown = this.zoomMenuStrip; + this.zoomStatusDropDownBtn.Image = ((System.Drawing.Image)(resources.GetObject("zoomStatusDropDownBtn.Image"))); + this.zoomStatusDropDownBtn.ImageTransparentColor = System.Drawing.Color.Magenta; + this.zoomStatusDropDownBtn.Name = "zoomStatusDropDownBtn"; + this.zoomStatusDropDownBtn.Size = new System.Drawing.Size(64, 22); + this.zoomStatusDropDownBtn.Text = "100%"; + // // ImageEditorForm // this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F); @@ -1645,6 +1834,7 @@ namespace Greenshot { this.statusStrip1.ResumeLayout(true); this.tableLayoutPanel1.ResumeLayout(true); this.toolsToolStrip.ResumeLayout(true); + this.zoomMenuStrip.ResumeLayout(false); this.menuStrip1.ResumeLayout(true); this.destinationsToolStrip.ResumeLayout(true); this.propertiesToolStrip.ResumeLayout(true); @@ -1794,5 +1984,25 @@ namespace Greenshot { private Greenshot.Controls.ToolStripColorButton btnLineColor; private GreenshotPlugin.Controls.GreenshotToolStripMenuItem autoCropToolStripMenuItem; private System.Windows.Forms.ToolStripSeparator toolStripSeparator17; + private System.Windows.Forms.ContextMenuStrip zoomMenuStrip; + private System.Windows.Forms.ToolStripMenuItem zoomInMenuItem; + private System.Windows.Forms.ToolStripMenuItem zoomOutMenuItem; + private System.Windows.Forms.ToolStripSeparator zoomMenuSeparator1; + private System.Windows.Forms.ToolStripMenuItem zoomBestFitMenuItem; + private System.Windows.Forms.ToolStripSeparator zoomMenuSeparator2; + private System.Windows.Forms.ToolStripMenuItem zoom25MenuItem; + private System.Windows.Forms.ToolStripMenuItem zoom50MenuItem; + private System.Windows.Forms.ToolStripMenuItem zoom66MenuItem; + private System.Windows.Forms.ToolStripMenuItem zoom75MenuItem; + private System.Windows.Forms.ToolStripSeparator zoomMenuSeparator3; + private System.Windows.Forms.ToolStripMenuItem zoomActualSizeMenuItem; + private System.Windows.Forms.ToolStripSeparator zoomMenuSeparator4; + private System.Windows.Forms.ToolStripMenuItem zoom200MenuItem; + private System.Windows.Forms.ToolStripMenuItem zoom300MenuItem; + private System.Windows.Forms.ToolStripMenuItem zoom400MenuItem; + private System.Windows.Forms.ToolStripMenuItem zoom600MenuItem; + private System.Windows.Forms.ToolStripDropDownButton zoomStatusDropDownBtn; + private System.Windows.Forms.ToolStripMenuItem zoomMainMenuItem; + private System.Windows.Forms.ToolStripStatusLabel statusStripSpacer; } } diff --git a/Greenshot/Forms/ImageEditorForm.cs b/Greenshot/Forms/ImageEditorForm.cs index bcb8c275c..dd9ec94e1 100644 --- a/Greenshot/Forms/ImageEditorForm.cs +++ b/Greenshot/Forms/ImageEditorForm.cs @@ -66,6 +66,12 @@ namespace Greenshot { // whether part of the editor controls are disabled depending on selected item(s) private bool _controlsDisabledDueToConfirmable; + /// + /// All provided zoom values (in percents) in ascending order. + /// + private readonly int[] ZOOM_VALUES = new[] { 25, 50, 66, 75, 100, 200, 300, 400, 600 }; + private int _zoomValue = 100; + /// /// An Implementation for the IImageEditor, this way Plugins have access to the HWND handles wich can be used with Win32 API calls. /// @@ -420,20 +426,14 @@ namespace Greenshot { /// /// /// - private void SurfaceSizeChanged(object sender, EventArgs e) { - if (EditorConfiguration.MatchSizeToCapture) { - // Set editor's initial size to the size of the surface plus the size of the chrome - Size imageSize = Surface.Image.Size; - Size currentFormSize = Size; - Size currentImageClientSize = panel1.ClientSize; - int minimumFormWidth = 650; - int minimumFormHeight = 530; - int newWidth = Math.Max(minimumFormWidth, currentFormSize.Width - currentImageClientSize.Width + imageSize.Width); - int newHeight = Math.Max(minimumFormHeight, currentFormSize.Height - currentImageClientSize.Height + imageSize.Height); - Size = new Size(newWidth, newHeight); + private void SurfaceSizeChanged(object sender, EventArgs e) + { + if (EditorConfiguration.MatchSizeToCapture) + { + Size = GetOptimalWindowSize(); } dimensionsLabel.Text = Surface.Image.Width + "x" + Surface.Image.Height; - ImageEditorFormResize(sender, new EventArgs()); + AlignCanvasPositionAfterResize(); } public ISurface Surface { @@ -860,15 +860,34 @@ namespace Greenshot { case Keys.Oemcomma: // Rotate CCW Ctrl + , RotateCcwToolstripButtonClick(sender, e); break; - case Keys.OemPeriod: // Rotate CW Ctrl + . + case Keys.OemPeriod: // Rotate CW Ctrl + . RotateCwToolstripButtonClick(sender, e); break; - case Keys.Add: // Ctrl + + - case Keys.Oemplus: // Ctrl + + + case Keys.Add: // Ctrl + Num+ + case Keys.Oemplus: // Ctrl + + + ZoomInMenuItemClick(sender, e); + break; + case Keys.Subtract: // Ctrl + Num- + case Keys.OemMinus: // Ctrl + - + ZoomOutMenuItemClick(sender, e); + break; + case Keys.Divide: // Ctrl + Num/ + case Keys.OemQuestion: // Ctrl + / (?) + ZoomSetValueMenuItemClick(zoomActualSizeMenuItem, e); + break; + case Keys.Multiply: // Ctrl + Num* + case Keys.D8: // Ctrl + 8 (*) + ZoomBestFitMenuItemClick(sender, e); + break; + } + } else if (e.Modifiers.Equals(Keys.Control | Keys.Shift)) { + switch (e.KeyCode) { + case Keys.Add: // Ctrl + Shift + Num+ + case Keys.Oemplus: // Ctrl + Shift + + EnlargeCanvasToolStripMenuItemClick(sender, e); break; - case Keys.Subtract: // Ctrl + - - case Keys.OemMinus: // Ctrl + - + case Keys.Subtract: // Ctrl + Shift + Num- + case Keys.OemMinus: // Ctrl + Shift + - ShrinkCanvasToolStripMenuItemClick(sender, e); break; } @@ -1444,28 +1463,130 @@ namespace Greenshot { } private void ImageEditorFormResize(object sender, EventArgs e) { + AlignCanvasPositionAfterResize(); + } + + private void AlignCanvasPositionAfterResize() { if (Surface?.Image == null || panel1 == null) { return; } - Size imageSize = Surface.Image.Size; - Size currentClientSize = panel1.ClientSize; var canvas = Surface as Control; + Size canvasSize = canvas.Size; + Size currentClientSize = panel1.ClientSize; Panel panel = (Panel) canvas?.Parent; if (panel == null) { return; } int offsetX = -panel.HorizontalScroll.Value; int offsetY = -panel.VerticalScroll.Value; - if (currentClientSize.Width > imageSize.Width) { - canvas.Left = offsetX + (currentClientSize.Width - imageSize.Width) / 2; + if (currentClientSize.Width > canvasSize.Width) { + canvas.Left = offsetX + (currentClientSize.Width - canvasSize.Width) / 2; } else { canvas.Left = offsetX + 0; } - if (currentClientSize.Height > imageSize.Height) { - canvas.Top = offsetY + (currentClientSize.Height - imageSize.Height) / 2; + if (currentClientSize.Height > canvasSize.Height) { + canvas.Top = offsetY + (currentClientSize.Height - canvasSize.Height) / 2; } else { canvas.Top = offsetY + 0; } } + + /// + /// Compute a size as a sum of surface size and chrome. + /// Upper bound is working area of the screen. Lower bound is fixed value. + /// + private Size GetOptimalWindowSize() { + var surfaceSize = (Surface as Control).Size; + var chromeSize = GetChromeSize(); + var newWidth = chromeSize.Width + surfaceSize.Width; + var newHeight = chromeSize.Height + surfaceSize.Height; + + // Upper bound. Don't make it bigger than the available working area. + var screen = Screen.FromControl(this); + var workingArea = screen.WorkingArea; + newWidth = Math.Min(newWidth, workingArea.Width); + newHeight = Math.Min(newHeight, workingArea.Height); + + // Lower bound. Don't make it smaller than a fixed value. + int minimumFormWidth = 650; + int minimumFormHeight = 530; + newWidth = Math.Max(minimumFormWidth, newWidth); + newHeight = Math.Max(minimumFormHeight, newHeight); + + return new Size(newWidth, newHeight); + } + + private Size GetChromeSize() + => Size - panel1.ClientSize; + + /// + /// Compute a size that the form can take without getting out of working area of the screen. + /// + private Size GetAvailableScreenSpace() { + var screen = Screen.FromControl(this); + var screenBounds = screen.Bounds; + var workingArea = screen.WorkingArea; + if (Left > screenBounds.Left && Top > screenBounds.Top) { + return new Size(workingArea.Width - Left, workingArea.Height - Top); + } else { + return workingArea.Size; + } + } + + private void ZoomInMenuItemClick(object sender, EventArgs e) { + var nextIndex = Array.FindIndex(ZOOM_VALUES, v => v > _zoomValue); + var nextValue = nextIndex < 0 ? ZOOM_VALUES[ZOOM_VALUES.Length - 1] : ZOOM_VALUES[nextIndex]; + + ZoomSetValue(nextValue); + } + + private void ZoomOutMenuItemClick(object sender, EventArgs e) { + var nextIndex = Array.FindLastIndex(ZOOM_VALUES, v => v < _zoomValue); + var nextValue = nextIndex < 0 ? ZOOM_VALUES[0] : ZOOM_VALUES[nextIndex]; + + ZoomSetValue(nextValue); + } + + private void ZoomSetValueMenuItemClick(object sender, EventArgs e) { + var senderMenuItem = (ToolStripMenuItem)sender; + int zoomPercent = int.Parse((string)senderMenuItem.Tag); + + ZoomSetValue(zoomPercent); + } + + private void ZoomBestFitMenuItemClick(object sender, EventArgs e) { + var maxWindowSize = GetAvailableScreenSpace(); + var chromeSize = GetChromeSize(); + var maxImageSize = maxWindowSize - chromeSize; + var imageSize = Surface.Image.Size; + + static bool isFit(int zoom, int source, int boundary) + => source * zoom / 100 <= boundary; + + var nextValue = Array.FindLast( + ZOOM_VALUES, + zoom => isFit(zoom, imageSize.Width, maxImageSize.Width) + && isFit(zoom, imageSize.Height, maxImageSize.Height) + ); + + ZoomSetValue(nextValue); + } + + private void ZoomSetValue(int value) { + _zoomValue = value; + Surface.ZoomFactor = 1f * value / 100; + + // Update zoom controls + string valueString = value.ToString(); + zoomStatusDropDownBtn.Text = valueString + "%"; + foreach (var item in zoomMenuStrip.Items) { + if (item is ToolStripMenuItem menuItem) { + menuItem.Checked = menuItem.Tag as string == valueString; + } + } + + Size = GetOptimalWindowSize(); + AlignCanvasPositionAfterResize(); + } } } diff --git a/Greenshot/Forms/ImageEditorForm.resx b/Greenshot/Forms/ImageEditorForm.resx index e4188604b..025543b10 100644 --- a/Greenshot/Forms/ImageEditorForm.resx +++ b/Greenshot/Forms/ImageEditorForm.resx @@ -937,6 +937,84 @@ BIhPAPEZIL7CAAQ6fptA+D+IJskFIM2cgtoMKm6rQfg/iEY3oB9oC8jWozBbgXquAvFykGYQkDJuZlBy WQvC/0E0QRfANIJoLmF9BnXPHTD8H8QmyQD9wBMMSPg/iE20ATK6uQwWUTeR8X8Qn2gDHJOfM6Dh/yA+ igHkZijqZSZyXQAA4IG1TpHFZ2gAAAAASUVORK5CYII= + + + + + iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAABl0RVh0U29m + dHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAJKSURBVDhPY/j//z+DX8FVDBxZ/ZAhqeWVRkrbm7lA + fB+If0HpuSBxkDxIHU4DIqruB0bXPrzftODpia0n3+668/zLiaNXPh3oXf7yFEgcJA9SBzbALeMsCvbI + Oq/hk3fx/rSND3ccufH60MuPP259+vb7+etPP28/evPt7PztT3b5AuVB6sAGWMUcQMdz83svnth96cWB + q48/ngBpBor9B9FP3n47d+3JpxMlEy6dAqkDG6Djtwkd35+77e72M/feXbj78suDd19+fQCK/QfRID5I + fOme+3tA6sAGqLitRse/dp5/fgCkGMj+j44/fvv97PC118eA7F9gA5Rc1qLj+wu239sNsgmk+Ofvv5+B + Yv9BNDgsPv68tWrfw0MgdWAD1D13oOO52W3nz+6/+urw/ZdfT4M0AcXgYQAMyHPF3RcvgdSBDXBwcGCw + s7NjsLW1ZbCxsWEwd8q0Mgo+/GLe5gdnbz77fBoU+mCbgfSz998vbtj/6pRxyMn7egHHILGAbIC1tbU8 + 0JDijsl7/ltFXnjVOOP5pZNXvpx+/u7H9VNXv5zpmPvymk3crfvmkdcDDYPPQNIBzACgRi03N/eaHTv2 + /BcVFWtWs2qxc0x+PheI7wPxLyg91z7xiYZF1E0GFAOAtlsEBga3HTly5r+iolIPCwuLqpJxCYNTyisM + DDSAAcUAoO3eCQkpEy5evPtfT89gGlCzMRAzEG2Aubl5ya1br/7b2zvPY2VldQFpJskAPT09LRERkRag + 5hiYZhAWlrEhzgDy8X8GAJItIDq7n94UAAAAAElFTkSuQmCC + + + + + iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAABl0RVh0U29m + dHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAIySURBVDhPY/j//z+DtrY2g559IYNfwVU4jqx+yJDU + 8kojpe3NXCC+D8S/oPRckDhIHqQObACyRhiOqLofGF378H7Tgqcntp58u+vO8y8njl75dKB3+ctTIHGQ + PEgd2AC3jLMo2CPrvIZP3sX70zY+3HHkxutDj958O/vk7bdzIAxiz9/+ZJcvUB6kDmyAVcwBdDw3v/fi + id2XXhy4+vjjCZhmGL725NOJkgmXToHUgQ3Q8duEju/P3XZ3+5l77y7cffnlAToGiS/dc38PSB3YABW3 + 1ej4187zzw+AFAPZ/9Hxx2+/nx2+9voYkP0LbICSy1p0fH/B9nu7QTaBFH/69vs5Mn798eetVfseHgKp + Axug7rkDHc/Nbjt/dv/VV4fvv/x6Gj0MgAF5rrj74iWQOrABDg4ODHZ2dgy2trYMNjY2DOZOmVZGwYdf + zNv84OzNZ59RDHj2/vvFDftfnTIOOXlfL+AYJBaQDbC2tpYHGlLcMXnPf6vIC68aZzy/dPLKl9PP3/24 + furqlzMdc19es4m7dd888nqgYfAZSDqAGQDUqOXm5l6zY8ee/6KiYs1qVi12jsnP5wLxfSD+BaXn2ic+ + 0bCIusmAYgDQdovAwOC2I0fO/FdUVOphYWFRVTIuYXBKeYWBgQYwoBgAtN07ISFlwsWLd//r6RlMA2o2 + BmIGog0wNzcvuXXr1X97e+d5rKysLiDNJBmgp6enJSIi0gLUHAPTDMLCMjbEGUA+/s8AAJUZIgOF4ptY + AAAAAElFTkSuQmCC + + + + + iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAABl0RVh0U29m + dHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAJISURBVDhPnZLLaxNRFIfvIvkLTF0J0uAjNDKoWaTk + bS1RUYshLowWqZqFD/BBs/KxUEMoaKEgiJuRbqRQ6UaojTqUYGpJYmpMSFuMxklK2mmmmba2kzSZINd7 + pk1p4qa6+Djn3vs73x24gzDGSKvVIsp6B3XcntzEdS+LLnt5jdtXoAksQdqoNOzDOeRkwdbBGufuso4L + D7Lso/7Z0HBYeP+DE0OfkiuB3oF8BPbhHHKywH51oo7j12OaUzfj7ADDhTJ8MbNclOZWSlUOgH5wdD50 + mpxDThYYOgON0Ld646F0XsyQHgOFpYpcgZywNuPpS0QgJwsOdLxphKXfpkdAQHq8KErL0EOFNfSvGJaB + nCzYY3/diPQuxgUgmBfWMHx6Tih9gQrrX6XqXHBqYRxyskDdPtQI2z/y8wMISI8r1d+rMAwV1iAYHM1+ + hJws2H/C3wh9wxebyC4UZ0iPAV4oyxVYKkpc95N4AnKywGazIYvFgsxmMzKZTEjfds1w2BmcH2JmvxdW + K5svAIjlKj8cLCR1Z8MsdWZ8/RW2CoxG424i6e55xmCD6yv/8AWXCCfFz9xieToyKUZ76PyU6WKK1bum + HYec0fX/oCYggy12+7H7fj+Dm5p2Pt5n8FqOXOFoAkuQNiptvZTTtJ7/huoE5PZWh8PpGxuL4uZm9VOF + QrFXrfOgNjf/F0SA6gTk9pNdXe6+eDyNKergczKsI6BtC/R6vSeV4rHVevSlUqlsh+F/ElAU1aJSqbxk + uLM2DOzYZdqe4P/B6A86Ah9NBTgWLgAAAABJRU5ErkJggg== + + + + + iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAABl0RVh0U29m + dHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAJXSURBVDhPnZLda1JhHMefC/0Lcl0FMelFZhwqLxy+ + t4YV1UjsImvFKi+qQS9MuujlohIZ1GAQjG7O2k0ERdALpW3SZNpQ0zllall6NKwzj1OWczId8fT8zuaY + drO6+PBwvs/393kezjkIY4ykUimitNdQ19XoGqabGXTOyknMtjmawBBqqysNOexDjxesH6xz4gZjOHU7 + w9wd+eF96yuMfmPL3o8zJdfA05wfctiHHi/QXwg2cPBSSHLkcpgZepVxeD7nJ+YXaz9LlWU2X6p+/T5X + CT62Z0ePkn3o8QJFt6sZ+spA2DsWmXVlC5VMrzWESYZBQp6nYtmS1zIY8UOPF+zqet0MQ79L2kEQSBWn + i+XaPMlwMldOQwY8cTJO6PGCbfrnzdTeh1i+CMDJJFu7AeCO5SehxwvEnS+aYUbsqTEY5n4tJarLvxdI + hmGF9wCCZx8yE9DjBTsPOZqhe22h4HiUc8+VqtnT1/2YZBhWuAV5kVN998MR6PECnU6HNBoNUqvVSKVS + IXnHRcVeo3t2+E06mOYW4zBUp7BQTb0c5/yy4z6GOja58hXWC5RK5VYi6et/6MQK0zR35xEb8c2UP7HF + pbg/Wg7007mY6kyCkZvihj3GwMp/UBeQwTa9/sAth8OJW1o239uhsGr2nWdpAkOora609mxW0n7yC2oQ + kNPbDQajzeMJ4NZW8QOBQLBdLLOgDjP3F0SAGgTk9MM9PebBcDiJKWr3EBmWEdCGBXK53JJIcFir3T8s + FAo7YfifBBRFtYlEIisZ7q4PA5u2qDYm+H8w+gNv9h/ta4LougAAAABJRU5ErkJggg== + + + + + iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8 + YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAHYSURBVDhPY8AH4ltfa6S0vZ6b3Pr6PhD/AtEgPkgcqgQ3 + iKi6Hxhd8/B+0/ynJ7acfLvrzrPPJ45c+Xigd9nLUyBxkDxUKSZwyzmj4Z176f7UjQ92HL7++tCjN9/O + Pn7z7RwIg9jztz3e5QOUB6mDakEFVjH75xb0Xjyx++KLA1cefToO0wzD1558OlE84eJpkDqoFlSg7bvp + /pytd3aADMCFl+6+vwekDqoFFSi7rf614/xzuGJ0Fzx+++3soauvjoHUQbWgAiXnNffn77i3G6wZqBib + ASv3PTgEUgfVggrUPLfPzWo/d3bv5VeH77/4fBrdgEevv54r7rpwCaQOqgUVWDpmWhkGHXoxf/P9szef + fkIx4Nn7b5fW73t5yjj45H2doKOYsWBpaSlvbW1d3DF5z3/LyAuvGqY/vXTiyqfTz999v37qyucz7fNe + XrOOvXXfNOIaZjqwsbHRcnV1r9mxY89/UVHRZlXLZjuH5GdzHZOf3XdMevYLRIP49vFPMW0G2moRGBjc + duTImf8KCko9bGxsqlApwgBos098fPKEixfv/tfT05/KyspqDJUiDpiZmZXcuvXqv52d8zxmZmYXqDDx + wMDAQEtYWLgFaHMMVIhegIEBADK7VwLsrsplAAAAAElFTkSuQmCC @@ -1026,4 +1104,7 @@ 17, 17 + + 782, 17 + \ No newline at end of file diff --git a/Greenshot/icons/fugue/magnifier-zoom-actual.png b/Greenshot/icons/fugue/magnifier-zoom-actual.png new file mode 100644 index 0000000000000000000000000000000000000000..21cf71d635eba0f9646931cbe60c96972a285ac4 GIT binary patch literal 742 zcmV)l(v^w`AQ8JqUw}r_G&HU#Qq<#yg(0so zO-lat&+;<%hp_CWo#kl5vfLaV)&Sz0bd zYbr|7BtR6)br1wT>~?`dUoUIyaaddn_cz+du77Z{kmhI#P{;i?o976%MRa-sK@b`- zGU(-s#Fyvg;^NJtJmI&>ijv9+LTTHJ4#0d2;-;()Q?CiO%NO{Mae$_wEkJWjEEz%4)^@ z>K*TZ23rVel{9P!9>5cLF%$|f27?o~Tl#LiiN&v7jm67#lgKdUh^ki7BnJK!e8S;q zyD&RDHx~?EzE>=6KeZ~Q3`H^T_l+so{j-C=UICqVyFH^bGjr3y;H8Jz?6>Eb&VU1$ zo8YtJPg1FkB}^s20ZmVU|E$-;!vl7n=hL{9w|h>$u=>vg6nVD)v7Z7A Y06Z)@+8V}-SpWb407*qoM6N<$g5B>}-T(jq literal 0 HcmV?d00001 diff --git a/Greenshot/icons/fugue/magnifier-zoom-fit.png b/Greenshot/icons/fugue/magnifier-zoom-fit.png new file mode 100644 index 0000000000000000000000000000000000000000..8364359fdea5b43e86d72af9e429679c0dd59064 GIT binary patch literal 726 zcmV;{0xA88P)~8 zhaein|M}?}<0fHXUh&(Fch!X_uh z!v)kQ4R*o5e@y83>GK!ge}m*K!?!W8voi7Uak5H+Xb>L+p1=6^2c!;c04pmuI^K8s z{6`_6jZ7fEp5JbsAK(8`Rq^;8nMnnuTFdsbq@m1Z#yWGG3{PkqPjQ{`gtiLW>20Hu{@16Ua>B=3Z z7algeKjoxYcpg9h^L5{euUuOWe&l9hE!qWr$3{lL}q5L?mvIN-T>)gLDFm?Dk|<*TwGZh8ynX9{{6c}ygxtOW@KbOgpo`c z{=!)-kXZQVt*or>T3A?_9~&Dy{qf^R%YnkT8HfO21AhMesaaLkmY}I_yve{tO>vL!1h_O%NOeA7nl{+Odc3RS}`sS6>vt2Ys?m5c{B%Du^4kDh$_3 zpR|;=h-;JnBx$l6?+u%{3~}IrkCU8xzVF_1&$+m&s@TQwI;JMrX@*;2Yh68G-xUW2I*?`ClFWGgwV%zF$vcrFzuNp zAd2D+GTe0t0408I!ufS1Kw11A>wdG*U~^bObDx{!yPR?%k*vpM1>^`sXf_)=!1LUq z&x=+42WXSaN>PYBdHtQ6CJBbXG5lD_ZVw#9f1o@~faiHV#z%ro&Baho6J>BKKsBQ{ z@{$6&+8xDoUuiLOkZwABauNTpa-TB((m<;5G$P8#hI zptka)jywz}a3&I2yvee&w_N@kuhN-oSJRosj!85aEHOoCj?S~}rF+HV_a`u1!1iF8fFw!5rKQ{R zb8}Z7=5k*WFqFmiXqo_gSue-NhMgS8qU%0C5g)VBYzUjhsOn}0<&?S0Lw P00000NkvXXu0mjfX;4x~ literal 0 HcmV?d00001 diff --git a/Greenshot/icons/fugue/magnifier-zoom-out.png b/Greenshot/icons/fugue/magnifier-zoom-out.png new file mode 100644 index 0000000000000000000000000000000000000000..3ec2b7a5d32c41a3313a95bec8f326ba356b3b46 GIT binary patch literal 707 zcmV;!0zCbRP)6dkX{uLg1LH81P=-xq6aa85~3GVVpJ&FNG@s! zNl@F}{OxXbJL~MymSlUd;DZM<`~A#&ytkO5C?FUN0;2yEI52S?Y=y;Q1tybe6vy!> zx`t7@QI=7rA%tL`SA;_OMsw{bsh_Vkj-q=zJt(xBCxazf_)9LA_qrcMu*A3>TbM+GV5(hbY4y zu-%c_iJQr{i} ztCCUuST`t2uvw|LWuz->1{7t{8>DlexWitLwXxRdNEC??-5^u|SkAK!q)TfCF8&Ux z0s?g>)RgGZgZKH_MG{>T!iW!_8)9Zza2gEuIJtBFikIrF9Gf{I4&Hyv z;-#{odgi!I^bC>KQblg1vn}I`x161%>=CH63ZzavhBokGBoe*JFtfLuBR5`Wa@VeA za*du%BnWd{mRki?Lw1Re4h#&QUtEmE80PZb_4Tz!Hjyo281Zh$nu3k()8-W5IiJrz zy|56QXP8U(N~NDq(B~r9MQ8@1C3;;}Y5)Y3gBsBm4002ovPDHLkV1nfvIIaKy literal 0 HcmV?d00001 diff --git a/Greenshot/icons/fugue/magnifier-zoom.png b/Greenshot/icons/fugue/magnifier-zoom.png new file mode 100644 index 0000000000000000000000000000000000000000..941b1035673a42d2c0dfe636ef27fd1d7236775f GIT binary patch literal 676 zcmV;V0$crwP);C@z`;D((zc8>c-~j*s{|o&2^LwAVGRsS2O^(~@ za_lc(zWw*(#O3e0=dS(O4wNqfDq9aW;Kz?2D4Kz`YW(~6x6jL&|E8Wg8<(62r!W&E z1Eaho6Pv6A?|&hI-?!Io|Je8Y&)@4zAPrx>d`7{aK7A}zk^lcvRgs-Tkei(qq#15F zkZrEZ$EU8u@CKv~?1E39KB4=-Nmo<$CXnWM_Ws+?XYUz)qAM`e=KHf_-vcM87ck_d zWhA*C!Z@1Je0=CZ#KFS+6R1xb?1g{-n9%Xl=P$qihHB=;7Rb+EeES1Z2R49}l^Y%J zJAMA65Hk}K^KYOsbj|<%{bxLR_JaUO9oP$!-#dVU{}@1N0)&6F>CQcVI&z(f&bO!1 zLY)6tks_O!iIL^Zr7u4Yoctlk&d%4(f*Jq+ /// This returns false if the container is deleted but still in the undo stack bool IsOnSurface(IDrawableContainer container); - void Invalidate(Rectangle rectangleToInvalidate); void Invalidate(); + /// + /// Invalidates the specified region of the Surface. + /// Takes care of the Surface zoom level, accepts rectangle in the coordinate space of the Image. + /// + /// Bounding rectangle for updated elements, in the coordinate space of the Image. + void InvalidateElements(Rectangle rectangleToInvalidate); bool Modified { get; @@ -189,6 +195,15 @@ namespace GreenshotPlugin.Interfaces int Width { get; } int Height { get; } + /// + /// Zoom value applied to the surface. 1.0f for actual size (100%). + /// + float ZoomFactor { get; set; } + /// + /// Matrix representing zoom applied to the surface. + /// + Matrix ZoomMatrix { get; } + void MakeUndoable(IMemento memento, bool allowMerge); } -} \ No newline at end of file +} From 136953aa4e16f4c7150becaea546f70f9622872a Mon Sep 17 00:00:00 2001 From: Killy Date: Mon, 27 Apr 2020 01:13:22 +0300 Subject: [PATCH 02/29] Fixes for some identified issues - better interface for coordinate translation; - correct context menu location; - speech bubble tail can be dragged over the whole image. --- Greenshot/Drawing/Adorners/AbstractAdorner.cs | 11 ++------ Greenshot/Drawing/Adorners/TargetAdorner.cs | 22 +++++++-------- Greenshot/Drawing/DrawableContainerList.cs | 7 ++--- Greenshot/Drawing/Surface.cs | 28 +++++++++++++++++-- Greenshot/Drawing/TextContainer.cs | 15 ++++------ GreenshotPlugin/Interfaces/ISurface.cs | 11 ++++++-- 6 files changed, 55 insertions(+), 39 deletions(-) diff --git a/Greenshot/Drawing/Adorners/AbstractAdorner.cs b/Greenshot/Drawing/Adorners/AbstractAdorner.cs index fc3d72371..51ae7f14f 100644 --- a/Greenshot/Drawing/Adorners/AbstractAdorner.cs +++ b/Greenshot/Drawing/Adorners/AbstractAdorner.cs @@ -1,4 +1,4 @@ -/* +/* * Greenshot - a free and open source screenshot tool * Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom * @@ -123,13 +123,8 @@ namespace Greenshot.Drawing.Adorners { get { - Point[] points = { Location }; - var matrix = Owner.Parent.ZoomMatrix; - if (!matrix.IsIdentity) - { - matrix.TransformPoints(points); - } - return new Rectangle(points[0].X - _size.Width / 2, points[0].Y - _size.Height / 2, _size.Width, _size.Height); + Point displayLocation = Owner.Parent.ToSurfaceCoordinates(Location); + return new Rectangle(displayLocation.X - _size.Width / 2, displayLocation.Y - _size.Height / 2, _size.Width, _size.Height); } } diff --git a/Greenshot/Drawing/Adorners/TargetAdorner.cs b/Greenshot/Drawing/Adorners/TargetAdorner.cs index 23cf29d09..4011754f7 100644 --- a/Greenshot/Drawing/Adorners/TargetAdorner.cs +++ b/Greenshot/Drawing/Adorners/TargetAdorner.cs @@ -1,4 +1,4 @@ -/* +/* * Greenshot - a free and open source screenshot tool * Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom * @@ -61,26 +61,26 @@ namespace Greenshot.Drawing.Adorners Owner.Invalidate(); Point newGripperLocation = new Point(mouseEventArgs.X, mouseEventArgs.Y); - Rectangle surfaceBounds = new Rectangle(0, 0, Owner.Parent.Width, Owner.Parent.Height); + Rectangle imageBounds = new Rectangle(0, 0, Owner.Parent.Image.Width, Owner.Parent.Image.Height); // Check if gripper inside the parent (surface), if not we need to move it inside // This was made for BUG-1682 - if (!surfaceBounds.Contains(newGripperLocation)) + if (!imageBounds.Contains(newGripperLocation)) { - if (newGripperLocation.X > surfaceBounds.Right) + if (newGripperLocation.X > imageBounds.Right) { - newGripperLocation.X = surfaceBounds.Right - 5; + newGripperLocation.X = imageBounds.Right - 5; } - if (newGripperLocation.X < surfaceBounds.Left) + if (newGripperLocation.X < imageBounds.Left) { - newGripperLocation.X = surfaceBounds.Left; + newGripperLocation.X = imageBounds.Left; } - if (newGripperLocation.Y > surfaceBounds.Bottom) + if (newGripperLocation.Y > imageBounds.Bottom) { - newGripperLocation.Y = surfaceBounds.Bottom - 5; + newGripperLocation.Y = imageBounds.Bottom - 5; } - if (newGripperLocation.Y < surfaceBounds.Top) + if (newGripperLocation.Y < imageBounds.Top) { - newGripperLocation.Y = surfaceBounds.Top; + newGripperLocation.Y = imageBounds.Top; } } diff --git a/Greenshot/Drawing/DrawableContainerList.cs b/Greenshot/Drawing/DrawableContainerList.cs index 17b879538..9451b06ea 100644 --- a/Greenshot/Drawing/DrawableContainerList.cs +++ b/Greenshot/Drawing/DrawableContainerList.cs @@ -523,9 +523,9 @@ namespace Greenshot.Drawing { } } - public virtual void ShowContextMenu(MouseEventArgs e, ISurface surface) + public virtual void ShowContextMenu(MouseEventArgs e, ISurface iSurface) { - if (!(surface is Surface)) + if (!(iSurface is Surface surface)) { return; } @@ -542,8 +542,7 @@ namespace Greenshot.Drawing { ContextMenuStrip menu = new ContextMenuStrip(); AddContextMenuItems(menu, surface); if (menu.Items.Count > 0) { - // TODO: cast should be somehow avoided - menu.Show((Surface)surface, e.Location); + menu.Show(surface, surface.ToSurfaceCoordinates(e.Location)); while (true) { if (menu.Visible) { Application.DoEvents(); diff --git a/Greenshot/Drawing/Surface.cs b/Greenshot/Drawing/Surface.cs index 5c38bea8e..0057a0838 100644 --- a/Greenshot/Drawing/Surface.cs +++ b/Greenshot/Drawing/Surface.cs @@ -1,4 +1,4 @@ -/* +/* * Greenshot - a free and open source screenshot tool * Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom * @@ -302,6 +302,8 @@ namespace Greenshot.Drawing } } + [NonSerialized] + private Matrix _zoomMatrix = new Matrix(1, 0, 0, 1, 0, 0); [NonSerialized] private float _zoomFactor = 1.0f; public float ZoomFactor @@ -310,12 +312,11 @@ namespace Greenshot.Drawing set { _zoomFactor = value; - ZoomMatrix = new Matrix(_zoomFactor, 0, 0, _zoomFactor, 0, 0); + _zoomMatrix = new Matrix(_zoomFactor, 0, 0, _zoomFactor, 0, 0); UpdateSize(); } } - public Matrix ZoomMatrix { get; private set; } = new Matrix(1, 0, 0, 1, 0, 0); /// /// Sets the surface size as zoomed image size. @@ -2108,5 +2109,26 @@ namespace Greenshot.Drawing { return _elements.Contains(container); } + + public Point ToSurfaceCoordinates(Point point) + { + Point[] points = { point }; + _zoomMatrix.TransformPoints(points); + return points[0]; + } + + public Rectangle ToSurfaceCoordinates(Rectangle rc) + { + if (_zoomMatrix.IsIdentity) + { + return rc; + } + else + { + Point[] points = { rc.Location, rc.Location + rc.Size }; + _zoomMatrix.TransformPoints(points); + return new Rectangle(points[0].X, points[0].Y, points[1].X - points[0].X, points[1].Y - points[1].Y); + } + } } } diff --git a/Greenshot/Drawing/TextContainer.cs b/Greenshot/Drawing/TextContainer.cs index bd4c75274..726ab5b7f 100644 --- a/Greenshot/Drawing/TextContainer.cs +++ b/Greenshot/Drawing/TextContainer.cs @@ -442,20 +442,15 @@ namespace Greenshot.Drawing correction = -1; } Rectangle absRectangle = GuiRectangle.GetGuiRectangle(Left, Top, Width, Height); - Point[] points = { absRectangle.Location, absRectangle.Location + absRectangle.Size }; - var matrix = Parent.ZoomMatrix; - if (!matrix.IsIdentity) - { - matrix.TransformPoints(points); - } - _textBox.Left = points[0].X + lineWidth; - _textBox.Top = points[0].Y + lineWidth; + Rectangle displayRectangle = Parent.ToSurfaceCoordinates(absRectangle); + _textBox.Left = displayRectangle.X + lineWidth; + _textBox.Top = displayRectangle.Y + lineWidth; if (lineThickness <= 1) { lineWidth = 0; } - _textBox.Width = points[1].X - points[0].X - 2 * lineWidth + correction; - _textBox.Height = points[1].Y - points[0].Y - 2 * lineWidth + correction; + _textBox.Width = displayRectangle.Width - 2 * lineWidth + correction; + _textBox.Height = displayRectangle.Height - 2 * lineWidth + correction; } public override void ApplyBounds(RectangleF newBounds) diff --git a/GreenshotPlugin/Interfaces/ISurface.cs b/GreenshotPlugin/Interfaces/ISurface.cs index 8ab44c810..a94415cbc 100644 --- a/GreenshotPlugin/Interfaces/ISurface.cs +++ b/GreenshotPlugin/Interfaces/ISurface.cs @@ -21,7 +21,6 @@ using System; using System.Drawing; -using System.Drawing.Drawing2D; using System.IO; using System.Windows.Forms; using GreenshotPlugin.Effects; @@ -200,9 +199,15 @@ namespace GreenshotPlugin.Interfaces /// float ZoomFactor { get; set; } /// - /// Matrix representing zoom applied to the surface. + /// Translate a point from image coorditate space to surface coordinate space. /// - Matrix ZoomMatrix { get; } + /// A point in the coordinate space of the image. + Point ToSurfaceCoordinates(Point point); + /// + /// Translate a rectangle from image coorditate space to surface coordinate space. + /// + /// A rectangle in the coordinate space of the image. + Rectangle ToSurfaceCoordinates(Rectangle rc); void MakeUndoable(IMemento memento, bool allowMerge); } From 79742d1b4d01a11b2dc6d10e7a0074d6824ecc43 Mon Sep 17 00:00:00 2001 From: Killy Date: Mon, 27 Apr 2020 01:14:52 +0300 Subject: [PATCH 03/29] Code review nitpicks --- Greenshot/Drawing/Adorners/AbstractAdorner.cs | 4 ++-- Greenshot/Drawing/Adorners/MoveAdorner.cs | 2 +- Greenshot/Drawing/Adorners/ResizeAdorner.cs | 2 +- Greenshot/Drawing/Adorners/TargetAdorner.cs | 4 ++-- Greenshot/Drawing/CropContainer.cs | 2 +- Greenshot/Drawing/Surface.cs | 6 +++--- 6 files changed, 10 insertions(+), 10 deletions(-) diff --git a/Greenshot/Drawing/Adorners/AbstractAdorner.cs b/Greenshot/Drawing/Adorners/AbstractAdorner.cs index 51ae7f14f..70a56f29e 100644 --- a/Greenshot/Drawing/Adorners/AbstractAdorner.cs +++ b/Greenshot/Drawing/Adorners/AbstractAdorner.cs @@ -1,4 +1,4 @@ -/* +/* * Greenshot - a free and open source screenshot tool * Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom * @@ -119,7 +119,7 @@ namespace Greenshot.Drawing.Adorners /// /// Return the bounds of the Adorner as displayed on the parent Surface /// - protected virtual Rectangle SurfaceBounds + protected virtual Rectangle BoundsOnSurface { get { diff --git a/Greenshot/Drawing/Adorners/MoveAdorner.cs b/Greenshot/Drawing/Adorners/MoveAdorner.cs index 21c42a9fa..d07a25ef7 100644 --- a/Greenshot/Drawing/Adorners/MoveAdorner.cs +++ b/Greenshot/Drawing/Adorners/MoveAdorner.cs @@ -142,7 +142,7 @@ namespace Greenshot.Drawing.Adorners { Graphics targetGraphics = paintEventArgs.Graphics; - var bounds = SurfaceBounds; + var bounds = BoundsOnSurface; GraphicsState state = targetGraphics.Save(); targetGraphics.SmoothingMode = SmoothingMode.None; diff --git a/Greenshot/Drawing/Adorners/ResizeAdorner.cs b/Greenshot/Drawing/Adorners/ResizeAdorner.cs index ef61b17bc..dce78b461 100644 --- a/Greenshot/Drawing/Adorners/ResizeAdorner.cs +++ b/Greenshot/Drawing/Adorners/ResizeAdorner.cs @@ -169,7 +169,7 @@ namespace Greenshot.Drawing.Adorners { Graphics targetGraphics = paintEventArgs.Graphics; - var bounds = SurfaceBounds; + var bounds = BoundsOnSurface; GraphicsState state = targetGraphics.Save(); targetGraphics.SmoothingMode = SmoothingMode.None; diff --git a/Greenshot/Drawing/Adorners/TargetAdorner.cs b/Greenshot/Drawing/Adorners/TargetAdorner.cs index 4011754f7..67c01a533 100644 --- a/Greenshot/Drawing/Adorners/TargetAdorner.cs +++ b/Greenshot/Drawing/Adorners/TargetAdorner.cs @@ -1,4 +1,4 @@ -/* +/* * Greenshot - a free and open source screenshot tool * Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom * @@ -96,7 +96,7 @@ namespace Greenshot.Drawing.Adorners { Graphics targetGraphics = paintEventArgs.Graphics; - var bounds = SurfaceBounds; + var bounds = BoundsOnSurface; targetGraphics.FillRectangle(Brushes.Green, bounds.X, bounds.Y, bounds.Width, bounds.Height); } diff --git a/Greenshot/Drawing/CropContainer.cs b/Greenshot/Drawing/CropContainer.cs index f5288074d..58cee17b1 100644 --- a/Greenshot/Drawing/CropContainer.cs +++ b/Greenshot/Drawing/CropContainer.cs @@ -61,7 +61,7 @@ namespace Greenshot.Drawing { if (_parent?.Image is Image image) { return new Rectangle(0, 0, image.Width, image.Height); } else { - return new Rectangle(); + return Rectangle.Empty; } } } diff --git a/Greenshot/Drawing/Surface.cs b/Greenshot/Drawing/Surface.cs index 0057a0838..3d1f309a3 100644 --- a/Greenshot/Drawing/Surface.cs +++ b/Greenshot/Drawing/Surface.cs @@ -1,4 +1,4 @@ -/* +/* * Greenshot - a free and open source screenshot tool * Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom * @@ -1405,7 +1405,7 @@ namespace Greenshot.Drawing return GetImage(RenderMode.EXPORT); } - private Rectangle ZoomClipRectangle(Rectangle rc, double scale) + private static Rectangle ZoomClipRectangle(Rectangle rc, double scale) => new Rectangle( (int)(rc.X * scale), (int)(rc.Y * scale), @@ -1413,7 +1413,7 @@ namespace Greenshot.Drawing (int)((rc.Height + 1) * scale) + 1 ); - private RectangleF ZoomClipRectangle(RectangleF rc, double scale) + private static RectangleF ZoomClipRectangle(RectangleF rc, double scale) => new RectangleF( (float)Math.Floor(rc.X * scale), (float)Math.Floor(rc.Y * scale), From 41082c2be15c94a8a3dfe9fd0e9db06c99139a96 Mon Sep 17 00:00:00 2001 From: Killy Date: Mon, 27 Apr 2020 01:18:05 +0300 Subject: [PATCH 04/29] Image size in OneNoteExporter There is now a distinction between Surface size and Surface.Image size. --- GreenshotOfficePlugin/OfficeExport/OneNoteExporter.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/GreenshotOfficePlugin/OfficeExport/OneNoteExporter.cs b/GreenshotOfficePlugin/OfficeExport/OneNoteExporter.cs index 23525b608..611594334 100644 --- a/GreenshotOfficePlugin/OfficeExport/OneNoteExporter.cs +++ b/GreenshotOfficePlugin/OfficeExport/OneNoteExporter.cs @@ -95,7 +95,7 @@ namespace GreenshotOfficePlugin.OfficeExport var pngOutputSettings = new SurfaceOutputSettings(OutputFormat.png, 100, false); ImageOutput.SaveToStream(surfaceToUpload, pngStream, pngOutputSettings); var base64String = Convert.ToBase64String(pngStream.GetBuffer()); - var imageXmlStr = string.Format(XmlImageContent, base64String, surfaceToUpload.Width, surfaceToUpload.Height); + var imageXmlStr = string.Format(XmlImageContent, base64String, surfaceToUpload.Image.Width, surfaceToUpload.Image.Height); var pageChangesXml = string.Format(XmlOutline, imageXmlStr, page.Id, OnenoteNamespace2010, page.Name); LOG.InfoFormat("Sending XML: {0}", pageChangesXml); oneNoteApplication.ComObject.UpdatePageContent(pageChangesXml, DateTime.MinValue, XMLSchema.xs2010, false); From 95c759e3fd62eaed79a5bbbde218e1a0c7e00bd8 Mon Sep 17 00:00:00 2001 From: Killy Date: Mon, 27 Apr 2020 01:19:38 +0300 Subject: [PATCH 05/29] Changed shortcuts - Ctrl+0, Ctrl+9 --- Greenshot/Forms/ImageEditorForm.Designer.cs | 4 ++-- Greenshot/Forms/ImageEditorForm.cs | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Greenshot/Forms/ImageEditorForm.Designer.cs b/Greenshot/Forms/ImageEditorForm.Designer.cs index 5789a19a6..606b72773 100644 --- a/Greenshot/Forms/ImageEditorForm.Designer.cs +++ b/Greenshot/Forms/ImageEditorForm.Designer.cs @@ -1704,7 +1704,7 @@ namespace Greenshot { // this.zoomBestFitMenuItem.Image = ((System.Drawing.Image)(resources.GetObject("zoomBestFitMenuItem.Image"))); this.zoomBestFitMenuItem.Name = "zoomBestFitMenuItem"; - this.zoomBestFitMenuItem.ShortcutKeyDisplayString = "Ctrl+*"; + this.zoomBestFitMenuItem.ShortcutKeyDisplayString = "Ctrl+9"; this.zoomBestFitMenuItem.Size = new System.Drawing.Size(209, 22); this.zoomBestFitMenuItem.Text = "Best Fit"; this.zoomBestFitMenuItem.Click += new System.EventHandler(this.ZoomBestFitMenuItemClick); @@ -1755,7 +1755,7 @@ namespace Greenshot { // this.zoomActualSizeMenuItem.Image = ((System.Drawing.Image)(resources.GetObject("zoomActualSizeMenuItem.Image"))); this.zoomActualSizeMenuItem.Name = "zoomActualSizeMenuItem"; - this.zoomActualSizeMenuItem.ShortcutKeyDisplayString = "Ctrl+/"; + this.zoomActualSizeMenuItem.ShortcutKeyDisplayString = "Ctrl+0"; this.zoomActualSizeMenuItem.Size = new System.Drawing.Size(209, 22); this.zoomActualSizeMenuItem.Tag = "100"; this.zoomActualSizeMenuItem.Text = "100% - Actual Size"; diff --git a/Greenshot/Forms/ImageEditorForm.cs b/Greenshot/Forms/ImageEditorForm.cs index dd9ec94e1..3396acd15 100644 --- a/Greenshot/Forms/ImageEditorForm.cs +++ b/Greenshot/Forms/ImageEditorForm.cs @@ -871,12 +871,12 @@ namespace Greenshot { case Keys.OemMinus: // Ctrl + - ZoomOutMenuItemClick(sender, e); break; - case Keys.Divide: // Ctrl + Num/ - case Keys.OemQuestion: // Ctrl + / (?) + case Keys.NumPad0: // Ctrl + Num0 + case Keys.D0: // Ctrl + 0 ZoomSetValueMenuItemClick(zoomActualSizeMenuItem, e); break; - case Keys.Multiply: // Ctrl + Num* - case Keys.D8: // Ctrl + 8 (*) + case Keys.NumPad9: // Ctrl + Num9 + case Keys.D9: // Ctrl + 9 ZoomBestFitMenuItemClick(sender, e); break; } From ec2c46daa852d5692bf891ca1700ac6c267abfd3 Mon Sep 17 00:00:00 2001 From: Killy Date: Mon, 27 Apr 2020 01:23:23 +0300 Subject: [PATCH 06/29] GetOptimalWindowSize - bound by available screen space Limiting by working area size was not good enough - having whole chrome on screen is important. --- Greenshot/Forms/ImageEditorForm.cs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/Greenshot/Forms/ImageEditorForm.cs b/Greenshot/Forms/ImageEditorForm.cs index 3396acd15..4609ff4a8 100644 --- a/Greenshot/Forms/ImageEditorForm.cs +++ b/Greenshot/Forms/ImageEditorForm.cs @@ -1502,10 +1502,9 @@ namespace Greenshot { var newHeight = chromeSize.Height + surfaceSize.Height; // Upper bound. Don't make it bigger than the available working area. - var screen = Screen.FromControl(this); - var workingArea = screen.WorkingArea; - newWidth = Math.Min(newWidth, workingArea.Width); - newHeight = Math.Min(newHeight, workingArea.Height); + var maxWindowSize = GetAvailableScreenSpace(); + newWidth = Math.Min(newWidth, maxWindowSize.Width); + newHeight = Math.Min(newHeight, maxWindowSize.Height); // Lower bound. Don't make it smaller than a fixed value. int minimumFormWidth = 650; From 3d39241f82799b741afb039e65783f6d0e52f9c0 Mon Sep 17 00:00:00 2001 From: Killy Date: Mon, 27 Apr 2020 16:00:58 +0300 Subject: [PATCH 07/29] More fixes for Surface.Image size - crop works properly; - Freehand drawings are selectable everywhere at all zoom levels. Known issue: freehand selection outline is not drawn --- Greenshot/Drawing/FreehandContainer.cs | 13 ++++++++++--- Greenshot/Drawing/Surface.cs | 8 ++++---- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/Greenshot/Drawing/FreehandContainer.cs b/Greenshot/Drawing/FreehandContainer.cs index 129ba4822..c7d5cf1e8 100644 --- a/Greenshot/Drawing/FreehandContainer.cs +++ b/Greenshot/Drawing/FreehandContainer.cs @@ -47,8 +47,8 @@ namespace Greenshot.Drawing { /// Constructor /// public FreehandContainer(Surface parent) : base(parent) { - Width = parent.Width; - Height = parent.Height; + Width = parent.Image.Width; + Height = parent.Image.Height; Top = 0; Left = 0; } @@ -236,7 +236,14 @@ namespace Greenshot.Drawing { 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)); } - return new Rectangle(0, 0, _parent?.Width??0, _parent?.Height?? 0); + if (_parent?.Image is Image image) + { + return new Rectangle(0, 0, image.Width, image.Height); + } + else + { + return Rectangle.Empty; + } } } diff --git a/Greenshot/Drawing/Surface.cs b/Greenshot/Drawing/Surface.cs index 3d1f309a3..e9d26ef05 100644 --- a/Greenshot/Drawing/Surface.cs +++ b/Greenshot/Drawing/Surface.cs @@ -991,13 +991,13 @@ namespace Greenshot.Drawing { cropRectangle = new Rectangle(cropRectangle.Left, 0, cropRectangle.Width, cropRectangle.Height + cropRectangle.Top); } - if (cropRectangle.Left + cropRectangle.Width > Width) + if (cropRectangle.Left + cropRectangle.Width > Image.Width) { - cropRectangle = new Rectangle(cropRectangle.Left, cropRectangle.Top, Width - cropRectangle.Left, cropRectangle.Height); + cropRectangle = new Rectangle(cropRectangle.Left, cropRectangle.Top, Image.Width - cropRectangle.Left, cropRectangle.Height); } - if (cropRectangle.Top + cropRectangle.Height > Height) + if (cropRectangle.Top + cropRectangle.Height > Image.Height) { - cropRectangle = new Rectangle(cropRectangle.Left, cropRectangle.Top, cropRectangle.Width, Height - cropRectangle.Top); + cropRectangle = new Rectangle(cropRectangle.Left, cropRectangle.Top, cropRectangle.Width, Image.Height - cropRectangle.Top); } if (cropRectangle.Height > 0 && cropRectangle.Width > 0) { From 169dbdccec9e117d6bcaabb26bcbf66e90fc2471 Mon Sep 17 00:00:00 2001 From: Killy Date: Mon, 27 Apr 2020 19:15:08 +0300 Subject: [PATCH 08/29] Rework for paste from clipboard - location is determined by the same code for images and text; - use visible area to determine the location. --- Greenshot/Drawing/DrawableContainer.cs | 27 ----- Greenshot/Drawing/Surface.cs | 99 +++++++++++++++---- .../Interfaces/Drawing/Container.cs | 1 - GreenshotPlugin/Interfaces/ISurface.cs | 16 ++- 4 files changed, 93 insertions(+), 50 deletions(-) diff --git a/Greenshot/Drawing/DrawableContainer.cs b/Greenshot/Drawing/DrawableContainer.cs index 65ff42de3..e7caf7ea3 100644 --- a/Greenshot/Drawing/DrawableContainer.cs +++ b/Greenshot/Drawing/DrawableContainer.cs @@ -302,33 +302,6 @@ namespace Greenshot.Drawing } } - public void AlignToParent(HorizontalAlignment horizontalAlignment, VerticalAlignment verticalAlignment) { - if (_parent == null) - { - return; - } - 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() {} diff --git a/Greenshot/Drawing/Surface.cs b/Greenshot/Drawing/Surface.cs index e9d26ef05..4435a7f39 100644 --- a/Greenshot/Drawing/Surface.cs +++ b/Greenshot/Drawing/Surface.cs @@ -305,6 +305,8 @@ namespace Greenshot.Drawing [NonSerialized] private Matrix _zoomMatrix = new Matrix(1, 0, 0, 1, 0, 0); [NonSerialized] + private Matrix _inverseZoomMatrix = new Matrix(1, 0, 0, 1, 0, 0); + [NonSerialized] private float _zoomFactor = 1.0f; public float ZoomFactor { @@ -313,6 +315,7 @@ namespace Greenshot.Drawing { _zoomFactor = value; _zoomMatrix = new Matrix(_zoomFactor, 0, 0, _zoomFactor, 0, 0); + _inverseZoomMatrix = new Matrix(1f / _zoomFactor, 0, 0, 1f / _zoomFactor, 0, 0); UpdateSize(); } } @@ -803,9 +806,9 @@ namespace Greenshot.Drawing 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) + public ITextContainer AddTextContainer(string text, int x, int y, FontFamily family, float size, bool italic, bool bold, bool shadow, int borderSize, Color color, Color fillColor) { - TextContainer textContainer = new TextContainer(this) {Text = text}; + TextContainer textContainer = new TextContainer(this) {Text = text, Left = x, Top = y}; textContainer.SetFieldValue(FieldType.FONT_FAMILY, family.Name); textContainer.SetFieldValue(FieldType.FONT_BOLD, bold); textContainer.SetFieldValue(FieldType.FONT_ITALIC, italic); @@ -816,8 +819,6 @@ namespace Greenshot.Drawing textContainer.SetFieldValue(FieldType.SHADOW, shadow); // Make sure the Text fits textContainer.FitToText(); - // Align to Surface - textContainer.AlignToParent(horizontalAlignment, verticalAlignment); //AggregatedProperties.UpdateElement(textContainer); AddElement(textContainer); @@ -1817,43 +1818,72 @@ namespace Greenshot.Drawing } else if (ClipboardHelper.ContainsImage(clipboard)) { - int x = 10; - int y = 10; - - // FEATURE-995: Added a check for the current mouse cursor location, to paste the image on that location. - var mousePositionOnControl = PointToClient(MousePosition); - if (ClientRectangle.Contains(mousePositionOnControl)) - { - x = mousePositionOnControl.X; - y = mousePositionOnControl.Y; - } + Point pasteLocation = GetPasteLocation(0.1f, 0.1f); foreach (Image clipboardImage in ClipboardHelper.GetImages(clipboard)) { if (clipboardImage != null) { DeselectAllElements(); - IImageContainer container = AddImageContainer(clipboardImage as Bitmap, x, y); + IImageContainer container = AddImageContainer(clipboardImage as Bitmap, pasteLocation.X, pasteLocation.Y); SelectElement(container); clipboardImage.Dispose(); - x += 10; - y += 10; + pasteLocation.X += 10; + pasteLocation.Y += 10; } } } else if (ClipboardHelper.ContainsText(clipboard)) { + Point pasteLocation = GetPasteLocation(0.4f, 0.4f); + string text = ClipboardHelper.GetText(clipboard); if (text != null) { DeselectAllElements(); - ITextContainer textContainer = AddTextContainer(text, HorizontalAlignment.Center, VerticalAlignment.CENTER, + ITextContainer textContainer = AddTextContainer(text, pasteLocation.X, pasteLocation.Y, FontFamily.GenericSansSerif, 12f, false, false, false, 2, Color.Black, Color.Transparent); SelectElement(textContainer); } } } + /// + /// Find a location to paste elements. + /// If mouse is over the surface - use it's position, otherwise use the visible area. + /// Return a point in image coordinate space. + /// + /// 0.0f for the left edge of visible area, 1.0f for the right edge of visible area. + /// 0.0f for the top edge of visible area, 1.0f for the bottom edge of visible area. + private Point GetPasteLocation(float horizontalRatio = 0.5f, float verticalRatio = 0.5f) + { + var point = PointToClient(MousePosition); + var rc = GetVisibleRectangle(); + if (!rc.Contains(point)) + { + point = new Point( + rc.Left + (int)(rc.Width * horizontalRatio), + rc.Top + (int)(rc.Height * verticalRatio) + ); + } + return ToImageCoordinates(point); + } + + /// + /// Get the rectangle bounding the part of this Surface currently visible in the editor (in surface coordinate space). + /// + private Rectangle GetVisibleRectangle() + { + var bounds = Bounds; + var clientArea = Parent.ClientRectangle; + return new Rectangle( + Math.Max(0, -bounds.Left), + Math.Max(0, -bounds.Top), + clientArea.Width, + clientArea.Height + ); + } + /// /// Duplicate all the selecteded elements /// @@ -2127,7 +2157,38 @@ namespace Greenshot.Drawing { Point[] points = { rc.Location, rc.Location + rc.Size }; _zoomMatrix.TransformPoints(points); - return new Rectangle(points[0].X, points[0].Y, points[1].X - points[0].X, points[1].Y - points[1].Y); + return new Rectangle( + points[0].X, + points[0].Y, + points[1].X - points[0].X, + points[1].Y - points[0].Y + ); + } + } + + public Point ToImageCoordinates(Point point) + { + Point[] points = { point }; + _inverseZoomMatrix.TransformPoints(points); + return points[0]; + } + + public Rectangle ToImageCoordinates(Rectangle rc) + { + if (_inverseZoomMatrix.IsIdentity) + { + return rc; + } + else + { + Point[] points = { rc.Location, rc.Location + rc.Size }; + _inverseZoomMatrix.TransformPoints(points); + return new Rectangle( + points[0].X, + points[0].Y, + points[1].X - points[0].X, + points[1].Y - points[0].Y + ); } } } diff --git a/GreenshotPlugin/Interfaces/Drawing/Container.cs b/GreenshotPlugin/Interfaces/Drawing/Container.cs index b2b6e3d19..35a6cf50a 100644 --- a/GreenshotPlugin/Interfaces/Drawing/Container.cs +++ b/GreenshotPlugin/Interfaces/Drawing/Container.cs @@ -102,7 +102,6 @@ namespace GreenshotPlugin.Interfaces.Drawing get; set; } - void AlignToParent(HorizontalAlignment horizontalAlignment, VerticalAlignment verticalAlignment); void Invalidate(); bool ClickableAt(int x, int y); void MoveBy(int x, int y); diff --git a/GreenshotPlugin/Interfaces/ISurface.cs b/GreenshotPlugin/Interfaces/ISurface.cs index a94415cbc..e32247188 100644 --- a/GreenshotPlugin/Interfaces/ISurface.cs +++ b/GreenshotPlugin/Interfaces/ISurface.cs @@ -84,8 +84,8 @@ namespace GreenshotPlugin.Interfaces /// The TextContainer will be "re"sized to the text size. /// /// String to show - /// Left, Center, Right - /// TOP, CENTER, BOTTOM + /// Where to put the container, X coordinate in the Image coordinate space + /// Where to put the container, Y coordinate in the Image coordinate space /// FontFamily /// Font Size in float /// bool true if italic @@ -94,7 +94,7 @@ namespace GreenshotPlugin.Interfaces /// size of border (0 for none) /// Color of string /// Color of background (e.g. Color.Transparent) - ITextContainer AddTextContainer(string text, HorizontalAlignment horizontalAlignment, VerticalAlignment verticalAlignment, FontFamily family, float size, bool italic, bool bold, bool shadow, int borderSize, Color color, Color fillColor); + ITextContainer AddTextContainer(string text, int x, int y, FontFamily family, float size, bool italic, bool bold, bool shadow, int borderSize, Color color, Color fillColor); IImageContainer AddImageContainer(Image image, int x, int y); ICursorContainer AddCursorContainer(Cursor cursor, int x, int y); @@ -208,6 +208,16 @@ namespace GreenshotPlugin.Interfaces /// /// A rectangle in the coordinate space of the image. Rectangle ToSurfaceCoordinates(Rectangle rc); + /// + /// Translate a point from surface coorditate space to image coordinate space. + /// + /// A point in the coordinate space of the surface. + Point ToImageCoordinates(Point point); + /// + /// Translate a rectangle from surface coorditate space to image coordinate space. + /// + /// A rectangle in the coordinate space of the surface. + Rectangle ToImageCoordinates(Rectangle rc); void MakeUndoable(IMemento memento, bool allowMerge); } From 195b5c3ab7579e850e1816c4ec72aaae2ebe46ae Mon Sep 17 00:00:00 2001 From: Killy Date: Mon, 27 Apr 2020 19:15:36 +0300 Subject: [PATCH 09/29] Fix NPE --- Greenshot/Drawing/TextContainer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Greenshot/Drawing/TextContainer.cs b/Greenshot/Drawing/TextContainer.cs index 726ab5b7f..c73424908 100644 --- a/Greenshot/Drawing/TextContainer.cs +++ b/Greenshot/Drawing/TextContainer.cs @@ -428,7 +428,7 @@ namespace Greenshot.Drawing /// private void UpdateTextBoxPosition() { - if (_textBox == null) + if (_textBox == null || Parent == null) { return; } From 2be1898c53f90c4769e38d55685f3688f2795c91 Mon Sep 17 00:00:00 2001 From: Killy Date: Mon, 27 Apr 2020 19:30:22 +0300 Subject: [PATCH 10/29] Width and Height shouldn't be exposed through ISurface anymore --- GreenshotPlugin/Interfaces/ISurface.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/GreenshotPlugin/Interfaces/ISurface.cs b/GreenshotPlugin/Interfaces/ISurface.cs index e32247188..73b31b05d 100644 --- a/GreenshotPlugin/Interfaces/ISurface.cs +++ b/GreenshotPlugin/Interfaces/ISurface.cs @@ -191,8 +191,6 @@ namespace GreenshotPlugin.Interfaces get; set; } - int Width { get; } - int Height { get; } /// /// Zoom value applied to the surface. 1.0f for actual size (100%). From 5fe700bc840c1aac874ac474ae03c362d1d1a615 Mon Sep 17 00:00:00 2001 From: MXI Date: Mon, 27 Apr 2020 21:00:59 +0300 Subject: [PATCH 11/29] Fix GetAvailableScreenSpace on secondary screens Co-Authored-By: jklingen --- Greenshot/Forms/ImageEditorForm.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Greenshot/Forms/ImageEditorForm.cs b/Greenshot/Forms/ImageEditorForm.cs index 4609ff4a8..d5de0b5c9 100644 --- a/Greenshot/Forms/ImageEditorForm.cs +++ b/Greenshot/Forms/ImageEditorForm.cs @@ -1526,7 +1526,7 @@ namespace Greenshot { var screenBounds = screen.Bounds; var workingArea = screen.WorkingArea; if (Left > screenBounds.Left && Top > screenBounds.Top) { - return new Size(workingArea.Width - Left, workingArea.Height - Top); + return new Size(workingArea.Right - Left, workingArea.Bottom - Top); } else { return workingArea.Size; } From d47271e7e14c848145411a2ef5820424f3c62d7b Mon Sep 17 00:00:00 2001 From: Killy Date: Tue, 28 Apr 2020 18:55:43 +0300 Subject: [PATCH 12/29] Fix rendering quality at small zoom levels when redrawing small parts --- Greenshot/Drawing/Surface.cs | 42 +++++++++++++++++++++++++----------- 1 file changed, 30 insertions(+), 12 deletions(-) diff --git a/Greenshot/Drawing/Surface.cs b/Greenshot/Drawing/Surface.cs index 4435a7f39..09bbe028e 100644 --- a/Greenshot/Drawing/Surface.cs +++ b/Greenshot/Drawing/Surface.cs @@ -1,4 +1,4 @@ -/* +/* * Greenshot - a free and open source screenshot tool * Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom * @@ -1473,19 +1473,11 @@ namespace Greenshot.Drawing targetGraphics.ScaleTransform(_zoomFactor, _zoomFactor); if (isZoomedIn) { - var state = targetGraphics.Save(); - targetGraphics.SmoothingMode = SmoothingMode.None; - targetGraphics.InterpolationMode = InterpolationMode.NearestNeighbor; - targetGraphics.CompositingQuality = CompositingQuality.HighQuality; - targetGraphics.PixelOffsetMode = PixelOffsetMode.None; - - targetGraphics.DrawImage(_buffer, imageClipRectangle, imageClipRectangle, GraphicsUnit.Pixel); - - targetGraphics.Restore(state); + DrawSharpImage(targetGraphics, _buffer, imageClipRectangle); } else { - targetGraphics.DrawImage(_buffer, imageClipRectangle, imageClipRectangle, GraphicsUnit.Pixel); + DrawSmoothImage(targetGraphics, _buffer, imageClipRectangle); } targetGraphics.ResetTransform(); } @@ -1494,7 +1486,7 @@ namespace Greenshot.Drawing DrawBackground(targetGraphics, targetClipRectangle); targetGraphics.ScaleTransform(_zoomFactor, _zoomFactor); - targetGraphics.DrawImage(Image, imageClipRectangle, imageClipRectangle, GraphicsUnit.Pixel); + DrawSmoothImage(targetGraphics, Image, imageClipRectangle); _elements.Draw(targetGraphics, null, RenderMode.EDIT, imageClipRectangle); targetGraphics.ResetTransform(); } @@ -1511,6 +1503,32 @@ namespace Greenshot.Drawing } } + private void DrawSmoothImage(Graphics targetGraphics, Image image, Rectangle imageClipRectangle) + { + var state = targetGraphics.Save(); + targetGraphics.SmoothingMode = SmoothingMode.HighQuality; + targetGraphics.InterpolationMode = InterpolationMode.HighQualityBilinear; + targetGraphics.CompositingQuality = CompositingQuality.HighQuality; + targetGraphics.PixelOffsetMode = PixelOffsetMode.None; + + targetGraphics.DrawImage(image, imageClipRectangle, imageClipRectangle, GraphicsUnit.Pixel); + + targetGraphics.Restore(state); + } + + private void DrawSharpImage(Graphics targetGraphics, Image image, Rectangle imageClipRectangle) + { + var state = targetGraphics.Save(); + targetGraphics.SmoothingMode = SmoothingMode.None; + targetGraphics.InterpolationMode = InterpolationMode.NearestNeighbor; + targetGraphics.CompositingQuality = CompositingQuality.HighQuality; + targetGraphics.PixelOffsetMode = PixelOffsetMode.None; + + targetGraphics.DrawImage(image, imageClipRectangle, imageClipRectangle, GraphicsUnit.Pixel); + + targetGraphics.Restore(state); + } + private void DrawBackground(Graphics targetGraphics, Rectangle clipRectangle) { // check if we need to draw the checkerboard From 4c0277be90fc31ab5119ba23f716f5a0ad5f771e Mon Sep 17 00:00:00 2001 From: Killy Date: Tue, 28 Apr 2020 19:19:17 +0300 Subject: [PATCH 13/29] Robust clip region resize --- Greenshot/Drawing/Surface.cs | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/Greenshot/Drawing/Surface.cs b/Greenshot/Drawing/Surface.cs index 09bbe028e..a6da5b4ed 100644 --- a/Greenshot/Drawing/Surface.cs +++ b/Greenshot/Drawing/Surface.cs @@ -1406,24 +1406,24 @@ namespace Greenshot.Drawing return GetImage(RenderMode.EXPORT); } - private static Rectangle ZoomClipRectangle(Rectangle rc, double scale) - => new Rectangle( + private static Rectangle ZoomClipRectangle(Rectangle rc, double scale, int inflateAmount = 0) + { + rc = new Rectangle( (int)(rc.X * scale), (int)(rc.Y * scale), - (int)((rc.Width + 1) * scale) + 1, // making sure to redraw enough pixels when moving scaled image - (int)((rc.Height + 1) * scale) + 1 - ); - - private static RectangleF ZoomClipRectangle(RectangleF rc, double scale) - => new RectangleF( - (float)Math.Floor(rc.X * scale), - (float)Math.Floor(rc.Y * scale), - (float)Math.Ceiling(rc.Width * scale), - (float)Math.Ceiling(rc.Height * scale) + (int)(rc.Width * scale) + 1, + (int)(rc.Height * scale) + 1 ); + if (scale > 1) + { + inflateAmount = (int)(inflateAmount * scale); + } + rc.Inflate(inflateAmount, inflateAmount); + return rc; + } public void InvalidateElements(Rectangle rc) - => Invalidate(ZoomClipRectangle(rc, _zoomFactor)); + => Invalidate(ZoomClipRectangle(rc, _zoomFactor, 1)); /// /// This is the event handler for the Paint Event, try to draw as little as possible! @@ -1439,7 +1439,7 @@ namespace Greenshot.Drawing LOG.Debug("Empty cliprectangle??"); return; } - Rectangle imageClipRectangle = ZoomClipRectangle(targetClipRectangle, 1.0 / _zoomFactor); + Rectangle imageClipRectangle = ZoomClipRectangle(targetClipRectangle, 1.0 / _zoomFactor, 2); bool isZoomedIn = ZoomFactor > 1f; if (_elements.HasIntersectingFilters(imageClipRectangle) || isZoomedIn) @@ -1467,7 +1467,7 @@ namespace Greenshot.Drawing //graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; DrawBackground(graphics, imageClipRectangle); graphics.DrawImage(Image, imageClipRectangle, imageClipRectangle, GraphicsUnit.Pixel); - graphics.SetClip(ZoomClipRectangle(targetGraphics.ClipBounds, 1.0 / _zoomFactor)); + graphics.SetClip(ZoomClipRectangle(Rectangle.Round(targetGraphics.ClipBounds), 1.0 / _zoomFactor, 2)); _elements.Draw(graphics, _buffer, RenderMode.EDIT, imageClipRectangle); } targetGraphics.ScaleTransform(_zoomFactor, _zoomFactor); From e6e2ed523ab166529dd15a08eabe8fab65e31071 Mon Sep 17 00:00:00 2001 From: Killy Date: Tue, 28 Apr 2020 21:43:01 +0300 Subject: [PATCH 14/29] Keep center of visible area static on zoom when possible --- Greenshot/Drawing/Surface.cs | 2 +- Greenshot/Forms/ImageEditorForm.cs | 25 +++++++++++++++++++++++-- 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/Greenshot/Drawing/Surface.cs b/Greenshot/Drawing/Surface.cs index a6da5b4ed..339e4b02c 100644 --- a/Greenshot/Drawing/Surface.cs +++ b/Greenshot/Drawing/Surface.cs @@ -1890,7 +1890,7 @@ namespace Greenshot.Drawing /// /// Get the rectangle bounding the part of this Surface currently visible in the editor (in surface coordinate space). /// - private Rectangle GetVisibleRectangle() + public Rectangle GetVisibleRectangle() { var bounds = Bounds; var clientArea = Parent.ClientRectangle; diff --git a/Greenshot/Forms/ImageEditorForm.cs b/Greenshot/Forms/ImageEditorForm.cs index d5de0b5c9..ec53969f5 100644 --- a/Greenshot/Forms/ImageEditorForm.cs +++ b/Greenshot/Forms/ImageEditorForm.cs @@ -1572,8 +1572,24 @@ namespace Greenshot { } private void ZoomSetValue(int value) { + var surface = Surface as Surface; + var panel = surface?.Parent as Panel; + if (panel == null) + { + return; + } + + // Store old scroll position + var rc = surface.GetVisibleRectangle(); + var size = surface.Size; + var horizontalCenter = 1.0 * (rc.Left + rc.Width / 2) / size.Width; + var verticalCenter = 1.0 * (rc.Top + rc.Height / 2) / size.Height; + + // Set the new zoom value _zoomValue = value; Surface.ZoomFactor = 1f * value / 100; + Size = GetOptimalWindowSize(); + AlignCanvasPositionAfterResize(); // Update zoom controls string valueString = value.ToString(); @@ -1584,8 +1600,13 @@ namespace Greenshot { } } - Size = GetOptimalWindowSize(); - AlignCanvasPositionAfterResize(); + // Restore scroll position + rc = surface.GetVisibleRectangle(); + size = surface.Size; + panel.AutoScrollPosition = new Point( + (int)(horizontalCenter * size.Width) - rc.Width / 2, + (int)(verticalCenter * size.Height) - rc.Height / 2 + ); } } } From 1ef2df9602dc155279e78024c7247e2618905446 Mon Sep 17 00:00:00 2001 From: Killy Date: Tue, 28 Apr 2020 21:46:45 +0300 Subject: [PATCH 15/29] Unneeded using after 169dbdc --- Greenshot/Drawing/DrawableContainer.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/Greenshot/Drawing/DrawableContainer.cs b/Greenshot/Drawing/DrawableContainer.cs index e7caf7ea3..33339ee00 100644 --- a/Greenshot/Drawing/DrawableContainer.cs +++ b/Greenshot/Drawing/DrawableContainer.cs @@ -33,7 +33,6 @@ using System.ComponentModel; using System.Drawing; using System.Drawing.Drawing2D; using System.Runtime.Serialization; -using System.Windows.Forms; using GreenshotPlugin.IniFile; using GreenshotPlugin.Interfaces; using GreenshotPlugin.Interfaces.Drawing.Adorners; From 99742ad05ad203c44b7ef8ed7a857a3de08d47dd Mon Sep 17 00:00:00 2001 From: Killy Date: Wed, 29 Apr 2020 23:23:29 +0300 Subject: [PATCH 16/29] Fix for Best Fit on images bigger than 4 times the available space --- Greenshot/Forms/ImageEditorForm.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Greenshot/Forms/ImageEditorForm.cs b/Greenshot/Forms/ImageEditorForm.cs index ec53969f5..334a72dc3 100644 --- a/Greenshot/Forms/ImageEditorForm.cs +++ b/Greenshot/Forms/ImageEditorForm.cs @@ -1562,11 +1562,12 @@ namespace Greenshot { static bool isFit(int zoom, int source, int boundary) => source * zoom / 100 <= boundary; - var nextValue = Array.FindLast( + var nextIndex = Array.FindLastIndex( ZOOM_VALUES, zoom => isFit(zoom, imageSize.Width, maxImageSize.Width) && isFit(zoom, imageSize.Height, maxImageSize.Height) ); + var nextValue = nextIndex < 0 ? ZOOM_VALUES[0] : ZOOM_VALUES[nextIndex]; ZoomSetValue(nextValue); } From 464e5e872f723b2cd0df343c5b82c309088a41c6 Mon Sep 17 00:00:00 2001 From: Killy Date: Thu, 30 Apr 2020 17:39:03 +0300 Subject: [PATCH 17/29] Fix weird scroll position when going from no scroll zoom value Also prefer top left corner in that situation - it is less disorienting when working with screenshots. --- Greenshot/Forms/ImageEditorForm.cs | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/Greenshot/Forms/ImageEditorForm.cs b/Greenshot/Forms/ImageEditorForm.cs index 334a72dc3..ca0cb1a7b 100644 --- a/Greenshot/Forms/ImageEditorForm.cs +++ b/Greenshot/Forms/ImageEditorForm.cs @@ -1581,10 +1581,19 @@ namespace Greenshot { } // Store old scroll position + // When no scroll is currently needed - prefer top left corner. + var horizontalCenter = 0.0; + var verticalCenter = 0.0; var rc = surface.GetVisibleRectangle(); var size = surface.Size; - var horizontalCenter = 1.0 * (rc.Left + rc.Width / 2) / size.Width; - var verticalCenter = 1.0 * (rc.Top + rc.Height / 2) / size.Height; + if (size.Width > rc.Width) + { + horizontalCenter = 1.0 * (rc.Left + rc.Width / 2) / size.Width; + } + if (size.Height > rc.Height) + { + verticalCenter = 1.0 * (rc.Top + rc.Height / 2) / size.Height; + } // Set the new zoom value _zoomValue = value; From dcf75fd081b6b0792069fa7a88c4cccf0b8ee626 Mon Sep 17 00:00:00 2001 From: Killy Date: Thu, 30 Apr 2020 17:57:36 +0300 Subject: [PATCH 18/29] Use Fractions to represent zoom factor The goal is to be able to get as close as possible to perfect 66.(6)% (2/3) zoom factor, and also remove types mismatch between the editor form and the surface. --- Greenshot/Drawing/Surface.cs | 13 +- Greenshot/Forms/ImageEditorForm.Designer.cs | 18 +-- Greenshot/Forms/ImageEditorForm.cs | 24 ++-- GreenshotPlugin/Core/Fraction.cs | 152 ++++++++++++++++++++ GreenshotPlugin/Interfaces/ISurface.cs | 5 +- 5 files changed, 183 insertions(+), 29 deletions(-) create mode 100644 GreenshotPlugin/Core/Fraction.cs diff --git a/Greenshot/Drawing/Surface.cs b/Greenshot/Drawing/Surface.cs index 339e4b02c..811466cf7 100644 --- a/Greenshot/Drawing/Surface.cs +++ b/Greenshot/Drawing/Surface.cs @@ -307,15 +307,16 @@ namespace Greenshot.Drawing [NonSerialized] private Matrix _inverseZoomMatrix = new Matrix(1, 0, 0, 1, 0, 0); [NonSerialized] - private float _zoomFactor = 1.0f; - public float ZoomFactor + private Fraction _zoomFactor = Fraction.Identity; + public Fraction ZoomFactor { get => _zoomFactor; set { _zoomFactor = value; + var inverse = _zoomFactor.Inverse(); _zoomMatrix = new Matrix(_zoomFactor, 0, 0, _zoomFactor, 0, 0); - _inverseZoomMatrix = new Matrix(1f / _zoomFactor, 0, 0, 1f / _zoomFactor, 0, 0); + _inverseZoomMatrix = new Matrix(inverse, 0, 0, inverse, 0, 0); UpdateSize(); } } @@ -1439,9 +1440,9 @@ namespace Greenshot.Drawing LOG.Debug("Empty cliprectangle??"); return; } - Rectangle imageClipRectangle = ZoomClipRectangle(targetClipRectangle, 1.0 / _zoomFactor, 2); + Rectangle imageClipRectangle = ZoomClipRectangle(targetClipRectangle, _zoomFactor.Inverse(), 2); - bool isZoomedIn = ZoomFactor > 1f; + bool isZoomedIn = _zoomFactor > Fraction.Identity; if (_elements.HasIntersectingFilters(imageClipRectangle) || isZoomedIn) { if (_buffer != null) @@ -1467,7 +1468,7 @@ namespace Greenshot.Drawing //graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; DrawBackground(graphics, imageClipRectangle); graphics.DrawImage(Image, imageClipRectangle, imageClipRectangle, GraphicsUnit.Pixel); - graphics.SetClip(ZoomClipRectangle(Rectangle.Round(targetGraphics.ClipBounds), 1.0 / _zoomFactor, 2)); + graphics.SetClip(ZoomClipRectangle(Rectangle.Round(targetGraphics.ClipBounds), _zoomFactor.Inverse(), 2)); _elements.Draw(graphics, _buffer, RenderMode.EDIT, imageClipRectangle); } targetGraphics.ScaleTransform(_zoomFactor, _zoomFactor); diff --git a/Greenshot/Forms/ImageEditorForm.Designer.cs b/Greenshot/Forms/ImageEditorForm.Designer.cs index 606b72773..04da3d0a0 100644 --- a/Greenshot/Forms/ImageEditorForm.Designer.cs +++ b/Greenshot/Forms/ImageEditorForm.Designer.cs @@ -1718,7 +1718,7 @@ namespace Greenshot { // this.zoom25MenuItem.Name = "zoom25MenuItem"; this.zoom25MenuItem.Size = new System.Drawing.Size(209, 22); - this.zoom25MenuItem.Tag = "25"; + this.zoom25MenuItem.Tag = "1/4"; this.zoom25MenuItem.Text = "25%"; this.zoom25MenuItem.Click += new System.EventHandler(this.ZoomSetValueMenuItemClick); // @@ -1726,7 +1726,7 @@ namespace Greenshot { // this.zoom50MenuItem.Name = "zoom50MenuItem"; this.zoom50MenuItem.Size = new System.Drawing.Size(209, 22); - this.zoom50MenuItem.Tag = "50"; + this.zoom50MenuItem.Tag = "1/2"; this.zoom50MenuItem.Text = "50%"; this.zoom50MenuItem.Click += new System.EventHandler(this.ZoomSetValueMenuItemClick); // @@ -1734,7 +1734,7 @@ namespace Greenshot { // this.zoom66MenuItem.Name = "zoom66MenuItem"; this.zoom66MenuItem.Size = new System.Drawing.Size(209, 22); - this.zoom66MenuItem.Tag = "66"; + this.zoom66MenuItem.Tag = "2/3"; this.zoom66MenuItem.Text = "66%"; this.zoom66MenuItem.Click += new System.EventHandler(this.ZoomSetValueMenuItemClick); // @@ -1742,7 +1742,7 @@ namespace Greenshot { // this.zoom75MenuItem.Name = "zoom75MenuItem"; this.zoom75MenuItem.Size = new System.Drawing.Size(209, 22); - this.zoom75MenuItem.Tag = "75"; + this.zoom75MenuItem.Tag = "3/4"; this.zoom75MenuItem.Text = "75%"; this.zoom75MenuItem.Click += new System.EventHandler(this.ZoomSetValueMenuItemClick); // @@ -1757,7 +1757,7 @@ namespace Greenshot { this.zoomActualSizeMenuItem.Name = "zoomActualSizeMenuItem"; this.zoomActualSizeMenuItem.ShortcutKeyDisplayString = "Ctrl+0"; this.zoomActualSizeMenuItem.Size = new System.Drawing.Size(209, 22); - this.zoomActualSizeMenuItem.Tag = "100"; + this.zoomActualSizeMenuItem.Tag = "1/1"; this.zoomActualSizeMenuItem.Text = "100% - Actual Size"; this.zoomActualSizeMenuItem.Click += new System.EventHandler(this.ZoomSetValueMenuItemClick); // @@ -1770,7 +1770,7 @@ namespace Greenshot { // this.zoom200MenuItem.Name = "zoom200MenuItem"; this.zoom200MenuItem.Size = new System.Drawing.Size(209, 22); - this.zoom200MenuItem.Tag = "200"; + this.zoom200MenuItem.Tag = "2/1"; this.zoom200MenuItem.Text = "200%"; this.zoom200MenuItem.Click += new System.EventHandler(this.ZoomSetValueMenuItemClick); // @@ -1778,7 +1778,7 @@ namespace Greenshot { // this.zoom300MenuItem.Name = "zoom300MenuItem"; this.zoom300MenuItem.Size = new System.Drawing.Size(209, 22); - this.zoom300MenuItem.Tag = "300"; + this.zoom300MenuItem.Tag = "3/1"; this.zoom300MenuItem.Text = "300%"; this.zoom300MenuItem.Click += new System.EventHandler(this.ZoomSetValueMenuItemClick); // @@ -1786,7 +1786,7 @@ namespace Greenshot { // this.zoom400MenuItem.Name = "zoom400MenuItem"; this.zoom400MenuItem.Size = new System.Drawing.Size(209, 22); - this.zoom400MenuItem.Tag = "400"; + this.zoom400MenuItem.Tag = "4/1"; this.zoom400MenuItem.Text = "400%"; this.zoom400MenuItem.Click += new System.EventHandler(this.ZoomSetValueMenuItemClick); // @@ -1794,7 +1794,7 @@ namespace Greenshot { // this.zoom600MenuItem.Name = "zoom600MenuItem"; this.zoom600MenuItem.Size = new System.Drawing.Size(209, 22); - this.zoom600MenuItem.Tag = "600"; + this.zoom600MenuItem.Tag = "6/1"; this.zoom600MenuItem.Text = "600%"; this.zoom600MenuItem.Click += new System.EventHandler(this.ZoomSetValueMenuItemClick); // diff --git a/Greenshot/Forms/ImageEditorForm.cs b/Greenshot/Forms/ImageEditorForm.cs index ca0cb1a7b..0b5c40d42 100644 --- a/Greenshot/Forms/ImageEditorForm.cs +++ b/Greenshot/Forms/ImageEditorForm.cs @@ -69,8 +69,7 @@ namespace Greenshot { /// /// All provided zoom values (in percents) in ascending order. /// - private readonly int[] ZOOM_VALUES = new[] { 25, 50, 66, 75, 100, 200, 300, 400, 600 }; - private int _zoomValue = 100; + private readonly Fraction[] ZOOM_VALUES = new Fraction[] { (1, 4), (1, 2), (2, 3), (3, 4), (1 ,1), (2, 1), (3, 1), (4, 1), (6, 1) }; /// /// An Implementation for the IImageEditor, this way Plugins have access to the HWND handles wich can be used with Win32 API calls. @@ -1533,14 +1532,16 @@ namespace Greenshot { } private void ZoomInMenuItemClick(object sender, EventArgs e) { - var nextIndex = Array.FindIndex(ZOOM_VALUES, v => v > _zoomValue); + var zoomValue = Surface.ZoomFactor; + var nextIndex = Array.FindIndex(ZOOM_VALUES, v => v > zoomValue); var nextValue = nextIndex < 0 ? ZOOM_VALUES[ZOOM_VALUES.Length - 1] : ZOOM_VALUES[nextIndex]; ZoomSetValue(nextValue); } private void ZoomOutMenuItemClick(object sender, EventArgs e) { - var nextIndex = Array.FindLastIndex(ZOOM_VALUES, v => v < _zoomValue); + var zoomValue = Surface.ZoomFactor; + var nextIndex = Array.FindLastIndex(ZOOM_VALUES, v => v < zoomValue); var nextValue = nextIndex < 0 ? ZOOM_VALUES[0] : ZOOM_VALUES[nextIndex]; ZoomSetValue(nextValue); @@ -1548,7 +1549,7 @@ namespace Greenshot { private void ZoomSetValueMenuItemClick(object sender, EventArgs e) { var senderMenuItem = (ToolStripMenuItem)sender; - int zoomPercent = int.Parse((string)senderMenuItem.Tag); + var zoomPercent = Fraction.Parse((string)senderMenuItem.Tag); ZoomSetValue(zoomPercent); } @@ -1559,8 +1560,8 @@ namespace Greenshot { var maxImageSize = maxWindowSize - chromeSize; var imageSize = Surface.Image.Size; - static bool isFit(int zoom, int source, int boundary) - => source * zoom / 100 <= boundary; + static bool isFit(Fraction scale, int source, int boundary) + => (int)(source * scale) <= boundary; var nextIndex = Array.FindLastIndex( ZOOM_VALUES, @@ -1572,7 +1573,7 @@ namespace Greenshot { ZoomSetValue(nextValue); } - private void ZoomSetValue(int value) { + private void ZoomSetValue(Fraction value) { var surface = Surface as Surface; var panel = surface?.Parent as Panel; if (panel == null) @@ -1596,14 +1597,13 @@ namespace Greenshot { } // Set the new zoom value - _zoomValue = value; - Surface.ZoomFactor = 1f * value / 100; + Surface.ZoomFactor = value; Size = GetOptimalWindowSize(); AlignCanvasPositionAfterResize(); // Update zoom controls - string valueString = value.ToString(); - zoomStatusDropDownBtn.Text = valueString + "%"; + zoomStatusDropDownBtn.Text = ((int)(100 * (double)value)).ToString() + "%"; + var valueString = value.ToString(); foreach (var item in zoomMenuStrip.Items) { if (item is ToolStripMenuItem menuItem) { menuItem.Checked = menuItem.Tag as string == valueString; diff --git a/GreenshotPlugin/Core/Fraction.cs b/GreenshotPlugin/Core/Fraction.cs new file mode 100644 index 000000000..312b91bb8 --- /dev/null +++ b/GreenshotPlugin/Core/Fraction.cs @@ -0,0 +1,152 @@ +using System; +using System.Text.RegularExpressions; + +namespace GreenshotPlugin.Core +{ + /// + /// Basic Fraction (Rational) numbers with features only needed to represent scale factors. + /// + public readonly struct Fraction : IEquatable, IComparable + { + public static Fraction Identity { get; } = new Fraction(1, 1); + + public uint Numerator { get; } + public uint Denominator { get; } + + public Fraction(uint numerator, uint denominator) + { + if (denominator == 0) + { + throw new ArgumentException("Can't divide by zero.", nameof(denominator)); + } + if (numerator == 0) + { + throw new ArgumentException("Zero is not supported by this implementation.", nameof(numerator)); + } + var gcd = GreatestCommonDivisor(numerator, denominator); + Numerator = numerator / gcd; + Denominator = denominator / gcd; + } + + public Fraction Inverse() + => new Fraction(Denominator, Numerator); + + #region Parse + + private static readonly Regex PARSE_REGEX = new Regex(@"^([1-9][0-9]*)\/([1-9][0-9]*)$", RegexOptions.Compiled); + public static bool TryParse(string str, out Fraction result) + { + var match = PARSE_REGEX.Match(str); + if (!match.Success) + { + result = Identity; + return false; + } + var numerator = uint.Parse(match.Groups[1].Value); + var denominator = uint.Parse(match.Groups[2].Value); + result = new Fraction(numerator, denominator); + return true; + } + + public static Fraction Parse(string str) + => TryParse(str, out var result) + ? result + : throw new ArgumentException($"Could not parse the input \"{str}\".", nameof(str)); + + #endregion + + #region Overrides, interface implementations + + public override string ToString() + => $"{Numerator}/{Denominator}"; + + public override bool Equals(object obj) + => obj is Fraction fraction && Equals(fraction); + + public bool Equals(Fraction other) + => Numerator == other.Numerator && Denominator == other.Denominator; + + public override int GetHashCode() + { + unchecked + { + int hashCode = -1534900553; + hashCode = hashCode * -1521134295 + Numerator.GetHashCode(); + hashCode = hashCode * -1521134295 + Denominator.GetHashCode(); + return hashCode; + } + } + + public int CompareTo(Fraction other) + => (int)(Numerator * other.Denominator) - (int)(other.Numerator * Denominator); + + #endregion + + #region Equality operators + + public static bool operator ==(Fraction left, Fraction right) + => left.Equals(right); + + public static bool operator !=(Fraction left, Fraction right) + => !(left == right); + + #endregion + + #region Comparison operators + + public static bool operator <(Fraction left, Fraction right) + => left.CompareTo(right) < 0; + + public static bool operator <=(Fraction left, Fraction right) + => left.CompareTo(right) <= 0; + + public static bool operator >(Fraction left, Fraction right) + => left.CompareTo(right) > 0; + + public static bool operator >=(Fraction left, Fraction right) + => left.CompareTo(right) >= 0; + + #endregion + + #region Scale operators + + public static Fraction operator *(Fraction left, Fraction right) + => new Fraction(left.Numerator * right.Numerator, left.Denominator * right.Denominator); + + public static Fraction operator *(Fraction left, uint right) + => new Fraction(left.Numerator * right, left.Denominator); + + public static Fraction operator *(uint left, Fraction right) + => new Fraction(left * right.Numerator, right.Denominator); + + public static Fraction operator /(Fraction left, Fraction right) + => new Fraction(left.Numerator * right.Denominator, left.Denominator * right.Numerator); + + public static Fraction operator /(Fraction left, uint right) + => new Fraction(left.Numerator, left.Denominator * right); + + public static Fraction operator /(uint left, Fraction right) + => new Fraction(left * right.Denominator, right.Numerator); + + #endregion + + #region Type conversion operators + + public static implicit operator double(Fraction fraction) + => 1.0 * fraction.Numerator / fraction.Denominator; + + public static implicit operator float(Fraction fraction) + => 1.0f * fraction.Numerator / fraction.Denominator; + + public static implicit operator Fraction(uint number) + => new Fraction(number, 1u); + + public static implicit operator Fraction((uint numerator, uint demoninator) tuple) + => new Fraction(tuple.numerator, tuple.demoninator); + + #endregion + + private static uint GreatestCommonDivisor(uint a, uint b) + => (b != 0) ? GreatestCommonDivisor(b, a % b) : a; + } +} diff --git a/GreenshotPlugin/Interfaces/ISurface.cs b/GreenshotPlugin/Interfaces/ISurface.cs index 73b31b05d..068ab0f29 100644 --- a/GreenshotPlugin/Interfaces/ISurface.cs +++ b/GreenshotPlugin/Interfaces/ISurface.cs @@ -23,6 +23,7 @@ using System; using System.Drawing; using System.IO; using System.Windows.Forms; +using GreenshotPlugin.Core; using GreenshotPlugin.Effects; using GreenshotPlugin.Interfaces.Drawing; @@ -193,9 +194,9 @@ namespace GreenshotPlugin.Interfaces } /// - /// Zoom value applied to the surface. 1.0f for actual size (100%). + /// Zoom value applied to the surface. /// - float ZoomFactor { get; set; } + Fraction ZoomFactor { get; set; } /// /// Translate a point from image coorditate space to surface coordinate space. /// From bac1ff4ba06e855239658688cf8e23002efb248c Mon Sep 17 00:00:00 2001 From: Killy Date: Thu, 30 Apr 2020 18:30:51 +0300 Subject: [PATCH 19/29] Fix for pixel jerk --- Greenshot/Drawing/Surface.cs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/Greenshot/Drawing/Surface.cs b/Greenshot/Drawing/Surface.cs index 811466cf7..2ea5e0b00 100644 --- a/Greenshot/Drawing/Surface.cs +++ b/Greenshot/Drawing/Surface.cs @@ -1440,6 +1440,25 @@ namespace Greenshot.Drawing LOG.Debug("Empty cliprectangle??"); return; } + + // Correction to prevent rounding errors at certain zoom levels. + // When zooming to N/M, clip rectangle top and left coordinates should be multiples of N. + if (_zoomFactor.Numerator > 1 && _zoomFactor.Denominator > 1) + { + int horizontalCorrection = targetClipRectangle.Left % (int)_zoomFactor.Numerator; + int verticalCorrection = targetClipRectangle.Top % (int)_zoomFactor.Numerator; + if (horizontalCorrection != 0) + { + targetClipRectangle.X -= horizontalCorrection; + targetClipRectangle.Width += horizontalCorrection; + } + if (verticalCorrection != 0) + { + targetClipRectangle.Y -= verticalCorrection; + targetClipRectangle.Height += verticalCorrection; + } + } + Rectangle imageClipRectangle = ZoomClipRectangle(targetClipRectangle, _zoomFactor.Inverse(), 2); bool isZoomedIn = _zoomFactor > Fraction.Identity; From f494385ef73d45527357b5eabb79a40dffbed0e7 Mon Sep 17 00:00:00 2001 From: Killy Date: Thu, 30 Apr 2020 19:16:05 +0300 Subject: [PATCH 20/29] Naming consistency - not a percentage value anymore --- Greenshot/Forms/ImageEditorForm.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Greenshot/Forms/ImageEditorForm.cs b/Greenshot/Forms/ImageEditorForm.cs index 0b5c40d42..1e177e428 100644 --- a/Greenshot/Forms/ImageEditorForm.cs +++ b/Greenshot/Forms/ImageEditorForm.cs @@ -1549,9 +1549,9 @@ namespace Greenshot { private void ZoomSetValueMenuItemClick(object sender, EventArgs e) { var senderMenuItem = (ToolStripMenuItem)sender; - var zoomPercent = Fraction.Parse((string)senderMenuItem.Tag); + var nextValue = Fraction.Parse((string)senderMenuItem.Tag); - ZoomSetValue(zoomPercent); + ZoomSetValue(nextValue); } private void ZoomBestFitMenuItemClick(object sender, EventArgs e) { From d93c9d6a3a24c18a0490b72e3ddfbc48849ca221 Mon Sep 17 00:00:00 2001 From: Killy Date: Fri, 1 May 2020 00:46:27 +0300 Subject: [PATCH 21/29] TextContainer's TextBox on zoom - fix for position, update font size --- Greenshot/Drawing/TextContainer.cs | 59 +++++++++++++++++++++++++----- 1 file changed, 49 insertions(+), 10 deletions(-) diff --git a/Greenshot/Drawing/TextContainer.cs b/Greenshot/Drawing/TextContainer.cs index c73424908..939dd3de6 100644 --- a/Greenshot/Drawing/TextContainer.cs +++ b/Greenshot/Drawing/TextContainer.cs @@ -22,6 +22,7 @@ using Greenshot.Drawing.Fields; using Greenshot.Helpers; using Greenshot.Memento; +using GreenshotPlugin.Core; using GreenshotPlugin.Interfaces.Drawing; using System; using System.ComponentModel; @@ -155,6 +156,24 @@ namespace Greenshot.Drawing FieldChanged += TextContainer_FieldChanged; } + protected override void SwitchParent(Surface newParent) + { + _parent.SizeChanged -= Parent_SizeChanged; + base.SwitchParent(newParent); + _parent.SizeChanged += Parent_SizeChanged; + } + + private void Parent_SizeChanged(object sender, EventArgs e) + { + UpdateTextBoxPosition(); + UpdateTextBoxFont(); + } + + public override void ApplyBounds(RectangleF newBounds) + { + base.ApplyBounds(newBounds); + UpdateTextBoxPosition(); + } public override void Invalidate() { @@ -255,7 +274,8 @@ namespace Greenshot.Drawing AcceptsTab = true, AcceptsReturn = true, BorderStyle = BorderStyle.None, - Visible = false + Visible = false, + Font = new Font(FontFamily.GenericSansSerif, 1) // just need something non-default here }; _textBox.DataBindings.Add("Text", this, "Text", false, DataSourceUpdateMode.OnPropertyChanged); @@ -388,7 +408,6 @@ namespace Greenshot.Drawing var newFont = CreateFont(fontFamily, fontBold, fontItalic, fontSize); _font?.Dispose(); _font = newFont; - _textBox.Font = _font; } catch (Exception ex) { @@ -400,7 +419,6 @@ namespace Greenshot.Drawing var newFont = CreateFont(fontFamily, fontBold, fontItalic, fontSize); _font?.Dispose(); _font = newFont; - _textBox.Font = _font; } catch (Exception) { @@ -413,6 +431,8 @@ namespace Greenshot.Drawing } } + UpdateTextBoxFont(); + UpdateAlignment(); } @@ -423,7 +443,29 @@ namespace Greenshot.Drawing } /// - /// This will create the textbox exactly to the inner size of the element + /// Set TextBox font according to the TextContainer font and the parent zoom factor. + /// + private void UpdateTextBoxFont() + { + if (_textBox == null || _font == null) + { + return; + } + + var textBoxFontScale = _parent?.ZoomFactor ?? Fraction.Identity; + + var newFont = new Font( + _font.FontFamily, + _font.Size * textBoxFontScale, + _font.Style, + GraphicsUnit.Pixel + ); + _textBox.Font.Dispose(); + _textBox.Font = newFont; + } + + /// + /// This will align the textbox exactly to the inner size of the element /// is a bit of a hack, but for now it seems to work... /// private void UpdateTextBoxPosition() @@ -453,12 +495,9 @@ namespace Greenshot.Drawing _textBox.Height = displayRectangle.Height - 2 * lineWidth + correction; } - public override void ApplyBounds(RectangleF newBounds) - { - base.ApplyBounds(newBounds); - UpdateTextBoxPosition(); - } - + /// + /// Set TextBox text align and fore color according to field values. + /// private void UpdateTextBoxFormat() { if (_textBox == null) From ef5b5deb7ade72376f7adef6081125ab90cec90d Mon Sep 17 00:00:00 2001 From: Killy Date: Fri, 1 May 2020 18:16:33 +0300 Subject: [PATCH 22/29] Smarter zoom - keep selected elements in sight --- Greenshot/Drawing/DrawableContainerList.cs | 24 ++++++++++++ Greenshot/Drawing/Surface.cs | 7 ++++ Greenshot/Forms/ImageEditorForm.cs | 38 +++++++++++++------ .../Interfaces/Drawing/Container.cs | 4 ++ 4 files changed, 62 insertions(+), 11 deletions(-) diff --git a/Greenshot/Drawing/DrawableContainerList.cs b/Greenshot/Drawing/DrawableContainerList.cs index 9451b06ea..3d200598e 100644 --- a/Greenshot/Drawing/DrawableContainerList.cs +++ b/Greenshot/Drawing/DrawableContainerList.cs @@ -235,6 +235,30 @@ namespace Greenshot.Drawing { return false; } + /// + /// A rectangle containing DrawingBounds of all drawableContainers in this list, + /// or empty rectangle if nothing is there. + /// + public Rectangle DrawingBounds + { + get + { + if (Count == 0) + { + return Rectangle.Empty; + } + else + { + var result = this[0].DrawingBounds; + for (int i = 1; i < Count; i++) + { + result = Rectangle.Union(result, this[i].DrawingBounds); + } + return result; + } + } + } + /// /// Triggers all elements in the list ot be redrawn. /// diff --git a/Greenshot/Drawing/Surface.cs b/Greenshot/Drawing/Surface.cs index 2ea5e0b00..8b33b6a56 100644 --- a/Greenshot/Drawing/Surface.cs +++ b/Greenshot/Drawing/Surface.cs @@ -1922,6 +1922,13 @@ namespace Greenshot.Drawing ); } + /// + /// Get the rectangle bounding all selected elements (in surface coordinates space), + /// or empty rectangle if nothing is selcted. + /// + public Rectangle GetSelectionRectangle() + => ToSurfaceCoordinates(selectedElements.DrawingBounds); + /// /// Duplicate all the selecteded elements /// diff --git a/Greenshot/Forms/ImageEditorForm.cs b/Greenshot/Forms/ImageEditorForm.cs index 1e177e428..8bf59df27 100644 --- a/Greenshot/Forms/ImageEditorForm.cs +++ b/Greenshot/Forms/ImageEditorForm.cs @@ -1580,21 +1580,37 @@ namespace Greenshot { { return; } + if (value == Surface.ZoomFactor) + { + return; + } - // Store old scroll position - // When no scroll is currently needed - prefer top left corner. - var horizontalCenter = 0.0; - var verticalCenter = 0.0; - var rc = surface.GetVisibleRectangle(); + // Store scroll position + var rc = surface.GetVisibleRectangle(); // use visible rc by default var size = surface.Size; - if (size.Width > rc.Width) + if (value > Surface.ZoomFactor) // being smart on zoom-in { - horizontalCenter = 1.0 * (rc.Left + rc.Width / 2) / size.Width; - } - if (size.Height > rc.Height) - { - verticalCenter = 1.0 * (rc.Top + rc.Height / 2) / size.Height; + var sel = surface.GetSelectionRectangle(); + if (sel != Rectangle.Empty) + { + rc.Intersect(sel); // zoom to visible part of selection + } + else + { + // if image fits completely to currently visible rc and there are no things to focus on + // - prefer top left corner to zoom-in as less disorienting for screenshots + if (size.Width < rc.Width) + { + rc.Width = 0; + } + if (size.Height < rc.Height) + { + rc.Height = 0; + } + } } + var horizontalCenter = 1.0 * (rc.Left + rc.Width / 2) / size.Width; + var verticalCenter = 1.0 * (rc.Top + rc.Height / 2) / size.Height; // Set the new zoom value Surface.ZoomFactor = value; diff --git a/GreenshotPlugin/Interfaces/Drawing/Container.cs b/GreenshotPlugin/Interfaces/Drawing/Container.cs index 35a6cf50a..f4065801a 100644 --- a/GreenshotPlugin/Interfaces/Drawing/Container.cs +++ b/GreenshotPlugin/Interfaces/Drawing/Container.cs @@ -145,6 +145,10 @@ namespace GreenshotPlugin.Interfaces.Drawing get; set; } + Rectangle DrawingBounds + { + get; + } void MakeBoundsChangeUndoable(bool allowMerge); void Transform(Matrix matrix); void MoveBy(int dx, int dy); From 0fce43404d4a87c28713cfa9fdf10c845142e8c1 Mon Sep 17 00:00:00 2001 From: Killy Date: Fri, 1 May 2020 18:45:10 +0300 Subject: [PATCH 23/29] Fix: prevent image blurring at 100% zoom Even at 100% GDI+ manages to do some smoothing. Also minor optimization - not messing with Graphics state when not needed. --- Greenshot/Drawing/Surface.cs | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/Greenshot/Drawing/Surface.cs b/Greenshot/Drawing/Surface.cs index 8b33b6a56..9f2a839cd 100644 --- a/Greenshot/Drawing/Surface.cs +++ b/Greenshot/Drawing/Surface.cs @@ -1461,8 +1461,7 @@ namespace Greenshot.Drawing Rectangle imageClipRectangle = ZoomClipRectangle(targetClipRectangle, _zoomFactor.Inverse(), 2); - bool isZoomedIn = _zoomFactor > Fraction.Identity; - if (_elements.HasIntersectingFilters(imageClipRectangle) || isZoomedIn) + if (_elements.HasIntersectingFilters(imageClipRectangle) || _zoomFactor > Fraction.Identity) { if (_buffer != null) { @@ -1491,7 +1490,11 @@ namespace Greenshot.Drawing _elements.Draw(graphics, _buffer, RenderMode.EDIT, imageClipRectangle); } targetGraphics.ScaleTransform(_zoomFactor, _zoomFactor); - if (isZoomedIn) + if (_zoomFactor == Fraction.Identity) + { + targetGraphics.DrawImage(_buffer, imageClipRectangle, imageClipRectangle, GraphicsUnit.Pixel); + } + else if(_zoomFactor > Fraction.Identity) { DrawSharpImage(targetGraphics, _buffer, imageClipRectangle); } @@ -1506,7 +1509,14 @@ namespace Greenshot.Drawing DrawBackground(targetGraphics, targetClipRectangle); targetGraphics.ScaleTransform(_zoomFactor, _zoomFactor); - DrawSmoothImage(targetGraphics, Image, imageClipRectangle); + if (_zoomFactor == Fraction.Identity) + { + targetGraphics.DrawImage(Image, imageClipRectangle, imageClipRectangle, GraphicsUnit.Pixel); + } + else + { + DrawSmoothImage(targetGraphics, Image, imageClipRectangle); + } _elements.Draw(targetGraphics, null, RenderMode.EDIT, imageClipRectangle); targetGraphics.ResetTransform(); } From b2a6fc877403388eb007fe8272242c964a0c0a1e Mon Sep 17 00:00:00 2001 From: Killy Date: Fri, 1 May 2020 18:53:08 +0300 Subject: [PATCH 24/29] No ScaleTransform is needed at 100% zoom --- Greenshot/Drawing/Surface.cs | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/Greenshot/Drawing/Surface.cs b/Greenshot/Drawing/Surface.cs index 9f2a839cd..6c89238f0 100644 --- a/Greenshot/Drawing/Surface.cs +++ b/Greenshot/Drawing/Surface.cs @@ -1489,36 +1489,39 @@ namespace Greenshot.Drawing graphics.SetClip(ZoomClipRectangle(Rectangle.Round(targetGraphics.ClipBounds), _zoomFactor.Inverse(), 2)); _elements.Draw(graphics, _buffer, RenderMode.EDIT, imageClipRectangle); } - targetGraphics.ScaleTransform(_zoomFactor, _zoomFactor); if (_zoomFactor == Fraction.Identity) { targetGraphics.DrawImage(_buffer, imageClipRectangle, imageClipRectangle, GraphicsUnit.Pixel); } - else if(_zoomFactor > Fraction.Identity) - { - DrawSharpImage(targetGraphics, _buffer, imageClipRectangle); - } else { - DrawSmoothImage(targetGraphics, _buffer, imageClipRectangle); + targetGraphics.ScaleTransform(_zoomFactor, _zoomFactor); + if (_zoomFactor > Fraction.Identity) + { + DrawSharpImage(targetGraphics, _buffer, imageClipRectangle); + } + else + { + DrawSmoothImage(targetGraphics, _buffer, imageClipRectangle); + } + targetGraphics.ResetTransform(); } - targetGraphics.ResetTransform(); } else { DrawBackground(targetGraphics, targetClipRectangle); - - targetGraphics.ScaleTransform(_zoomFactor, _zoomFactor); if (_zoomFactor == Fraction.Identity) { targetGraphics.DrawImage(Image, imageClipRectangle, imageClipRectangle, GraphicsUnit.Pixel); + _elements.Draw(targetGraphics, null, RenderMode.EDIT, imageClipRectangle); } else { + targetGraphics.ScaleTransform(_zoomFactor, _zoomFactor); DrawSmoothImage(targetGraphics, Image, imageClipRectangle); + _elements.Draw(targetGraphics, null, RenderMode.EDIT, imageClipRectangle); + targetGraphics.ResetTransform(); } - _elements.Draw(targetGraphics, null, RenderMode.EDIT, imageClipRectangle); - targetGraphics.ResetTransform(); } // No clipping for the adorners From 79ea558dbbdede0d1fcd5a1f2a7520d8d800648b Mon Sep 17 00:00:00 2001 From: Killy Date: Sat, 2 May 2020 01:52:05 +0300 Subject: [PATCH 25/29] Fix: zoom-in when selection is out of sight --- Greenshot/Forms/ImageEditorForm.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Greenshot/Forms/ImageEditorForm.cs b/Greenshot/Forms/ImageEditorForm.cs index 8bf59df27..d397b8bc8 100644 --- a/Greenshot/Forms/ImageEditorForm.cs +++ b/Greenshot/Forms/ImageEditorForm.cs @@ -1590,10 +1590,11 @@ namespace Greenshot { var size = surface.Size; if (value > Surface.ZoomFactor) // being smart on zoom-in { - var sel = surface.GetSelectionRectangle(); - if (sel != Rectangle.Empty) + var selection = surface.GetSelectionRectangle(); + selection.Intersect(rc); + if (selection != Rectangle.Empty) { - rc.Intersect(sel); // zoom to visible part of selection + rc = selection; // zoom to visible part of selection } else { From e8343b4ed2c879790b8d85c77bc64d337248f6a3 Mon Sep 17 00:00:00 2001 From: jklingen Date: Sun, 3 May 2020 18:27:04 +0200 Subject: [PATCH 26/29] Installer: Backport Russian Translation for the Installer from PR #186 --- Greenshot/releases/innosetup/setup.iss | 1298 ++++++++++++------------ 1 file changed, 656 insertions(+), 642 deletions(-) diff --git a/Greenshot/releases/innosetup/setup.iss b/Greenshot/releases/innosetup/setup.iss index b2fd8e371..0c42af120 100644 --- a/Greenshot/releases/innosetup/setup.iss +++ b/Greenshot/releases/innosetup/setup.iss @@ -1,642 +1,656 @@ -#define ExeName "Greenshot" -#define Version GetEnv('BuildVersionSimple') -#define FileVersion GetEnv('AssemblyInformationalVersion') -#define BaseDir "..\..\.." -#define ReleaseDir "..\..\bin\Release\net472" -#define BinDir "bin\Release\net472" - -; Include the scripts to install .NET Framework -; See http://www.codeproject.com/KB/install/dotnetfx_innosetup_instal.aspx -#include "scripts\products.iss" -#include "scripts\products\stringversion.iss" -#include "scripts\products\winversion.iss" -#include "scripts\products\fileversion.iss" -#include "scripts\products\msi20.iss" -#include "scripts\products\msi31.iss" -#include "scripts\products\dotnetfxversion.iss" -#include "scripts\products\dotnetfx47.iss" - -[Files] -Source: {#ReleaseDir}\Greenshot.exe; DestDir: {app}; Components: greenshot; Flags: overwritereadonly ignoreversion replacesameversion -Source: {#ReleaseDir}\GreenshotPlugin.dll; DestDir: {app}; Components: greenshot; Flags: overwritereadonly ignoreversion replacesameversion -Source: {#ReleaseDir}\Greenshot.exe.config; DestDir: {app}; Components: greenshot; Flags: overwritereadonly ignoreversion replacesameversion -Source: {#ReleaseDir}\log4net.dll; DestDir: {app}; Components: greenshot; Flags: overwritereadonly ignoreversion replacesameversion -Source: {#ReleaseDir}\Dapplo.Http*.dll; DestDir: {app}; Components: greenshot; Flags: overwritereadonly ignoreversion replacesameversion -Source: {#ReleaseDir}\Dapplo.Log.dll; DestDir: {app}; Components: greenshot; Flags: overwritereadonly ignoreversion replacesameversion -Source: {#ReleaseDir}\Svg.dll; DestDir: {app}; Components: greenshot; Flags: overwritereadonly ignoreversion replacesameversion -Source: {#ReleaseDir}\Fizzler.dll; DestDir: {app}; Components: greenshot; Flags: overwritereadonly ignoreversion replacesameversion -Source: {#ReleaseDir}\Newtonsoft.Json.dll; DestDir: {app}; Components: greenshot; Flags: overwritereadonly ignoreversion replacesameversion -Source: ..\..\log4net.xml; DestDir: {app}; Components: greenshot; Flags: overwritereadonly ignoreversion -Source: {#ReleaseDir}\checksum.SHA256; DestDir: {app}; Components: greenshot; Flags: overwritereadonly ignoreversion replacesameversion -;Source: ..\greenshot-defaults.ini; DestDir: {app}; Flags: overwritereadonly ignoreversion replacesameversion -Source: ..\additional_files\installer.txt; DestDir: {app}; Components: greenshot; Flags: overwritereadonly recursesubdirs ignoreversion replacesameversion -Source: ..\additional_files\license.txt; DestDir: {app}; Components: greenshot; Flags: overwritereadonly recursesubdirs ignoreversion replacesameversion -Source: ..\additional_files\readme.txt; DestDir: {app}; Components: greenshot; Flags: overwritereadonly recursesubdirs ignoreversion replacesameversion - -; Core language files -Source: ..\..\Languages\*nl-NL*; Excludes: "*installer*,*website*"; DestDir: {app}\Languages; Components: greenshot; Flags: overwritereadonly ignoreversion replacesameversion; -Source: ..\..\Languages\*en-US*; Excludes: "*installer*,*website*"; DestDir: {app}\Languages; Components: greenshot; Flags: overwritereadonly ignoreversion replacesameversion; -Source: ..\..\Languages\*de-DE*; Excludes: "*installer*,*website*"; DestDir: {app}\Languages; Components: greenshot; Flags: overwritereadonly ignoreversion replacesameversion; - -; Additional language files -Source: ..\..\Languages\*ar-SY*; Excludes: "*installer*,*website*"; DestDir: {app}\Languages; Components: languages\arSY; Flags: overwritereadonly ignoreversion replacesameversion; -Source: ..\..\Languages\*ca-CA*; Excludes: "*installer*,*website*"; DestDir: {app}\Languages; Components: languages\caCA; Flags: overwritereadonly ignoreversion replacesameversion; -Source: ..\..\Languages\*cs-CZ*; Excludes: "*installer*,*website*"; DestDir: {app}\Languages; Components: languages\csCZ; Flags: overwritereadonly ignoreversion replacesameversion; -Source: ..\..\Languages\*da-DK*; Excludes: "*installer*,*website*"; DestDir: {app}\Languages; Components: languages\daDK; Flags: overwritereadonly ignoreversion replacesameversion; -Source: ..\..\Languages\*de-x-franconia*; Excludes: "*installer*,*website*"; DestDir: {app}\Languages; Components: languages\dexfranconia; Flags: overwritereadonly ignoreversion replacesameversion; -Source: ..\..\Languages\*el-GR*; Excludes: "*installer*,*website*"; DestDir: {app}\Languages; Components: languages\elGR; Flags: overwritereadonly ignoreversion replacesameversion; -Source: ..\..\Languages\*es-ES*; Excludes: "*installer*,*website*"; DestDir: {app}\Languages; Components: languages\esES; Flags: overwritereadonly ignoreversion replacesameversion; -Source: ..\..\Languages\*et-EE*; Excludes: "*installer*,*website*"; DestDir: {app}\Languages; Components: languages\etEE; Flags: overwritereadonly ignoreversion replacesameversion; -Source: ..\..\Languages\*fa-IR*; Excludes: "*installer*,*website*"; DestDir: {app}\Languages; Components: languages\faIR; Flags: overwritereadonly ignoreversion replacesameversion; -Source: ..\..\Languages\*fi-FI*; Excludes: "*installer*,*website*"; DestDir: {app}\Languages; Components: languages\fiFI; Flags: overwritereadonly ignoreversion replacesameversion; -Source: ..\..\Languages\*fr-FR*; Excludes: "*installer*,*website*"; DestDir: {app}\Languages; Components: languages\frFR; Flags: overwritereadonly ignoreversion replacesameversion; -Source: ..\..\Languages\*fr-QC*; Excludes: "*installer*,*website*"; DestDir: {app}\Languages; Components: languages\frQC; Flags: overwritereadonly ignoreversion replacesameversion; -Source: ..\..\Languages\*he-IL*; Excludes: "*installer*,*website*"; DestDir: {app}\Languages; Components: languages\heIL; Flags: overwritereadonly ignoreversion replacesameversion; -Source: ..\..\Languages\*hu-HU*; Excludes: "*installer*,*website*"; DestDir: {app}\Languages; Components: languages\huHU; Flags: overwritereadonly ignoreversion replacesameversion; -Source: ..\..\Languages\*id-ID*; Excludes: "*installer*,*website*"; DestDir: {app}\Languages; Components: languages\idID; Flags: overwritereadonly ignoreversion replacesameversion; -Source: ..\..\Languages\*it-IT*; Excludes: "*installer*,*website*"; DestDir: {app}\Languages; Components: languages\itIT; Flags: overwritereadonly ignoreversion replacesameversion; -Source: ..\..\Languages\*ja-JP*; Excludes: "*installer*,*website*"; DestDir: {app}\Languages; Components: languages\jaJP; Flags: overwritereadonly ignoreversion replacesameversion; -Source: ..\..\Languages\*ko-KR*; Excludes: "*installer*,*website*"; DestDir: {app}\Languages; Components: languages\koKR; Flags: overwritereadonly ignoreversion replacesameversion; -Source: ..\..\Languages\*kab-DZ*; Excludes: "*installer*,*website*"; DestDir: {app}\Languages; Components: languages\kabDZ; Flags: overwritereadonly ignoreversion replacesameversion; -Source: ..\..\Languages\*lt-LT*; Excludes: "*installer*,*website*"; DestDir: {app}\Languages; Components: languages\ltLT; Flags: overwritereadonly ignoreversion replacesameversion; -Source: ..\..\Languages\*lv-LV*; Excludes: "*installer*,*website*"; DestDir: {app}\Languages; Components: languages\lvLV; Flags: overwritereadonly ignoreversion replacesameversion; -Source: ..\..\Languages\*nn-NO*; Excludes: "*installer*,*website*"; DestDir: {app}\Languages; Components: languages\nnNO; Flags: overwritereadonly ignoreversion replacesameversion; -Source: ..\..\Languages\*pl-PL*; Excludes: "*installer*,*website*"; DestDir: {app}\Languages; Components: languages\plPL; Flags: overwritereadonly ignoreversion replacesameversion; -Source: ..\..\Languages\*pt-BR*; Excludes: "*installer*,*website*"; DestDir: {app}\Languages; Components: languages\ptBR; Flags: overwritereadonly ignoreversion replacesameversion; -Source: ..\..\Languages\*pt-PT*; Excludes: "*installer*,*website*"; DestDir: {app}\Languages; Components: languages\ptPT; Flags: overwritereadonly ignoreversion replacesameversion; -Source: ..\..\Languages\*ro-RO*; Excludes: "*installer*,*website*"; DestDir: {app}\Languages; Components: languages\roRO; Flags: overwritereadonly ignoreversion replacesameversion; -Source: ..\..\Languages\*ru-RU*; Excludes: "*installer*,*website*"; DestDir: {app}\Languages; Components: languages\ruRU; Flags: overwritereadonly ignoreversion replacesameversion; -Source: ..\..\Languages\*sk-SK*; Excludes: "*installer*,*website*"; DestDir: {app}\Languages; Components: languages\skSK; Flags: overwritereadonly ignoreversion replacesameversion; -Source: ..\..\Languages\*sl-SI*; Excludes: "*installer*,*website*"; DestDir: {app}\Languages; Components: languages\slSI; Flags: overwritereadonly ignoreversion replacesameversion; -Source: ..\..\Languages\*sr-RS*; Excludes: "*installer*,*website*"; DestDir: {app}\Languages; Components: languages\srRS; Flags: overwritereadonly ignoreversion replacesameversion; -Source: ..\..\Languages\*sv-SE*; Excludes: "*installer*,*website*"; DestDir: {app}\Languages; Components: languages\svSE; Flags: overwritereadonly ignoreversion replacesameversion; -Source: ..\..\Languages\*tr-TR*; Excludes: "*installer*,*website*"; DestDir: {app}\Languages; Components: languages\trTR; Flags: overwritereadonly ignoreversion replacesameversion; -Source: ..\..\Languages\*uk-UA*; Excludes: "*installer*,*website*"; DestDir: {app}\Languages; Components: languages\ukUA; Flags: overwritereadonly ignoreversion replacesameversion; -Source: ..\..\Languages\*vi-VN*; Excludes: "*installer*,*website*"; DestDir: {app}\Languages; Components: languages\viVN; Flags: overwritereadonly ignoreversion replacesameversion; -Source: ..\..\Languages\*zh-CN*; Excludes: "*installer*,*website*"; DestDir: {app}\Languages; Components: languages\zhCN; Flags: overwritereadonly ignoreversion replacesameversion; -Source: ..\..\Languages\*zh-TW*; Excludes: "*installer*,*website*"; DestDir: {app}\Languages; Components: languages\zhTW; Flags: overwritereadonly ignoreversion replacesameversion; - -;Office Plugin -Source: {#BaseDir}\GreenshotOfficePlugin\{#BinDir}\GreenshotOfficePlugin.dll; DestDir: {app}\Plugins\GreenshotOfficePlugin; Components: plugins\office; Flags: overwritereadonly recursesubdirs ignoreversion replacesameversion; -;OCR Plugin -Source: {#BaseDir}\GreenshotOCRPlugin\{#BinDir}\GreenshotOCRPlugin.dll; DestDir: {app}\Plugins\GreenshotOCRPlugin; Components: plugins\ocr; Flags: overwritereadonly recursesubdirs ignoreversion replacesameversion; -Source: {#BaseDir}\GreenshotOCRPlugin\Languages\language_ocr*.xml; DestDir: {app}\Languages\Plugins\GreenshotOCRPlugin; Components: plugins\ocr; Flags: overwritereadonly ignoreversion replacesameversion; -Source: {#BaseDir}\GreenshotOCRCommand\{#BinDir}\GreenshotOCRCommand.exe; DestDir: {app}\Plugins\GreenshotOCRPlugin; Components: plugins\ocr; Flags: overwritereadonly recursesubdirs ignoreversion replacesameversion; -Source: {#BaseDir}\GreenshotOCRCommand\{#BinDir}\GreenshotOCRCommand.exe.config; DestDir: {app}\Plugins\GreenshotOCRPlugin; Components: plugins\ocr; Flags: overwritereadonly recursesubdirs ignoreversion replacesameversion; -;JIRA Plugin -Source: {#BaseDir}\GreenshotJiraPlugin\{#BinDir}\GreenshotJiraPlugin.dll; DestDir: {app}\Plugins\GreenshotJiraPlugin; Components: plugins\jira; Flags: overwritereadonly recursesubdirs ignoreversion replacesameversion; -Source: {#BaseDir}\GreenshotJiraPlugin\{#BinDir}\Dapplo.Jira.dll; DestDir: {app}\Plugins\GreenshotJiraPlugin; Components: plugins\jira; Flags: overwritereadonly recursesubdirs ignoreversion replacesameversion; -Source: {#BaseDir}\GreenshotJiraPlugin\Languages\language_jira*.xml; DestDir: {app}\Languages\Plugins\GreenshotJiraPlugin; Components: plugins\jira; Flags: overwritereadonly ignoreversion replacesameversion; -;Imgur Plugin -Source: {#BaseDir}\GreenshotImgurPlugin\{#BinDir}\GreenshotImgurPlugin.dll; DestDir: {app}\Plugins\GreenshotImgurPlugin; Components: plugins\imgur; Flags: overwritereadonly recursesubdirs ignoreversion replacesameversion; -Source: {#BaseDir}\GreenshotImgurPlugin\Languages\language_imgur*.xml; DestDir: {app}\Languages\Plugins\GreenshotImgurPlugin; Components: plugins\imgur; Flags: overwritereadonly ignoreversion replacesameversion; -;Box Plugin -Source: {#BaseDir}\GreenshotBoxPlugin\{#BinDir}\GreenshotBoxPlugin.dll; DestDir: {app}\Plugins\GreenshotBoxPlugin; Components: plugins\box; Flags: overwritereadonly recursesubdirs ignoreversion replacesameversion; -Source: {#BaseDir}\GreenshotBoxPlugin\Languages\language_box*.xml; DestDir: {app}\Languages\Plugins\GreenshotBoxPlugin; Components: plugins\box; Flags: overwritereadonly ignoreversion replacesameversion; -;DropBox Plugin -Source: {#BaseDir}\GreenshotDropBoxPlugin\{#BinDir}\GreenshotDropboxPlugin.dll; DestDir: {app}\Plugins\GreenshotDropBoxPlugin; Components: plugins\dropbox; Flags: overwritereadonly recursesubdirs ignoreversion replacesameversion; -Source: {#BaseDir}\GreenshotDropBoxPlugin\Languages\language_dropbox*.xml; DestDir: {app}\Languages\Plugins\GreenshotDropBoxPlugin; Components: plugins\dropbox; Flags: overwritereadonly ignoreversion replacesameversion; -;Flickr Plugin -Source: {#BaseDir}\GreenshotFlickrPlugin\{#BinDir}\GreenshotFlickrPlugin.dll; DestDir: {app}\Plugins\GreenshotFlickrPlugin; Components: plugins\flickr; Flags: overwritereadonly recursesubdirs ignoreversion replacesameversion; -Source: {#BaseDir}\GreenshotFlickrPlugin\Languages\language_flickr*.xml; DestDir: {app}\Languages\Plugins\GreenshotFlickrPlugin; Components: plugins\flickr; Flags: overwritereadonly ignoreversion replacesameversion; -;Photobucket Plugin -Source: {#BaseDir}\GreenshotPhotobucketPlugin\{#BinDir}\GreenshotPhotobucketPlugin.dll; DestDir: {app}\Plugins\GreenshotPhotobucketPlugin; Components: plugins\photobucket; Flags: overwritereadonly recursesubdirs ignoreversion replacesameversion; -Source: {#BaseDir}\GreenshotPhotobucketPlugin\Languages\language_photo*.xml; DestDir: {app}\Languages\Plugins\GreenshotPhotobucketPlugin; Components: plugins\photobucket; Flags: overwritereadonly ignoreversion replacesameversion; -;Picasa Plugin -Source: {#BaseDir}\GreenshotPicasaPlugin\{#BinDir}\GreenshotPicasaPlugin.dll; DestDir: {app}\Plugins\GreenshotPicasaPlugin; Components: plugins\picasa; Flags: overwritereadonly recursesubdirs ignoreversion replacesameversion; -Source: {#BaseDir}\GreenshotPicasaPlugin\Languages\language_picasa*.xml; DestDir: {app}\Languages\Plugins\GreenshotPicasaPlugin; Components: plugins\picasa; Flags: overwritereadonly ignoreversion replacesameversion; -;Confluence Plugin -Source: {#BaseDir}\GreenshotConfluencePlugin\{#BinDir}\GreenshotConfluencePlugin.dll; DestDir: {app}\Plugins\GreenshotConfluencePlugin; Components: plugins\confluence; Flags: overwritereadonly recursesubdirs ignoreversion replacesameversion; -Source: {#BaseDir}\GreenshotConfluencePlugin\Languages\language_confluence*.xml; DestDir: {app}\Languages\Plugins\GreenshotConfluencePlugin; Components: plugins\confluence; Flags: overwritereadonly ignoreversion replacesameversion; -;ExternalCommand Plugin -Source: {#BaseDir}\GreenshotExternalCommandPlugin\{#BinDir}\GreenshotExternalCommandPlugin.dll; DestDir: {app}\Plugins\GreenshotExternalCommandPlugin; Components: plugins\externalcommand; Flags: overwritereadonly recursesubdirs ignoreversion replacesameversion; -Source: {#BaseDir}\GreenshotExternalCommandPlugin\Languages\language_externalcommand*.xml; DestDir: {app}\Languages\Plugins\GreenshotExternalCommandPlugin; Components: plugins\externalcommand; Flags: overwritereadonly ignoreversion replacesameversion; -;Win 10 Plugin -Source: {#BaseDir}\GreenshotWin10Plugin\{#BinDir}\GreenshotWin10Plugin.dll; DestDir: {app}\Plugins\GreenshotWin10Plugin; Components: plugins\win10; Flags: overwritereadonly recursesubdirs ignoreversion replacesameversion; -[Setup] -; changes associations is used when the installer installs new extensions, it clears the explorer icon cache -ChangesAssociations=yes -AppId={#ExeName} -AppName={#ExeName} -AppMutex=F48E86D3-E34C-4DB7-8F8F-9A0EA55F0D08 -AppPublisher={#ExeName} -AppPublisherURL=http://getgreenshot.org -AppSupportURL=http://getgreenshot.org -AppUpdatesURL=http://getgreenshot.org -AppVerName={#ExeName} {#Version} -AppVersion={#Version} -ArchitecturesInstallIn64BitMode=x64 -Compression=lzma2/ultra64 -SolidCompression=yes -DefaultDirName={code:DefDirRoot}\{#ExeName} -DefaultGroupName={#ExeName} -InfoBeforeFile=..\additional_files\readme.txt -LicenseFile=..\additional_files\license.txt -LanguageDetectionMethod=uilanguage -MinVersion=6.1.7600 -OutputBaseFilename={#ExeName}-INSTALLER-{#Version}-UNSTABLE -OutputDir=..\ -PrivilegesRequired=lowest -SetupIconFile=..\..\icons\applicationIcon\icon.ico -; Create a SHA1 signature -; SignTool=SignTool sign /debug /fd sha1 /tr http://time.certum.pl /td sha1 $f -; Append a SHA256 to the previous SHA1 signature (this is what as does) -; SignTool=SignTool sign /debug /as /fd sha256 /tr http://time.certum.pl /td sha256 $f -; SignedUninstaller=yes -UninstallDisplayIcon={app}\{#ExeName}.exe -Uninstallable=true -VersionInfoCompany={#ExeName} -VersionInfoProductName={#ExeName} -VersionInfoProductTextVersion={#FileVersion} -VersionInfoTextVersion={#FileVersion} -VersionInfoVersion={#Version} -; Reference a bitmap, max size 164x314 -WizardImageFile=installer-large.bmp -; Reference a bitmap, max size 55x58 -WizardSmallImageFile=installer-small.bmp -[Registry] -; Delete all startup entries, so we don't have leftover values -Root: HKCU; Subkey: Software\Microsoft\Windows\CurrentVersion\Run; ValueType: none; ValueName: {#ExeName}; Flags: deletevalue noerror; -Root: HKLM; Subkey: Software\Microsoft\Windows\CurrentVersion\Run; ValueType: none; ValueName: {#ExeName}; Flags: deletevalue noerror; -Root: HKCU32; Subkey: Software\Microsoft\Windows\CurrentVersion\Run; ValueType: none; ValueName: {#ExeName}; Flags: deletevalue noerror; Check: IsWin64() -Root: HKLM32; Subkey: Software\Microsoft\Windows\CurrentVersion\Run; ValueType: none; ValueName: {#ExeName}; Flags: deletevalue noerror; Check: IsWin64() - -; delete filetype mappings -; HKEY_LOCAL_USER - for current user only -Root: HKCU; Subkey: Software\Classes\.greenshot; ValueType: none; ValueName: {#ExeName}; Flags: deletevalue noerror; -Root: HKCU; Subkey: Software\Classes\Greenshot; ValueType: none; ValueName: {#ExeName}; Flags: deletevalue noerror; -; HKEY_LOCAL_MACHINE - for all users when admin (with the noerror this doesn't matter) -Root: HKLM; Subkey: Software\Classes\.greenshot; ValueType: none; ValueName: {#ExeName}; Flags: deletevalue noerror; -Root: HKLM; Subkey: Software\Classes\Greenshot; ValueType: none; ValueName: {#ExeName}; Flags: deletevalue noerror; - -; Create the startup entries if requested to do so -; HKEY_LOCAL_USER - for current user only -Root: HKCU; Subkey: Software\Microsoft\Windows\CurrentVersion\Run; ValueType: string; ValueName: {#ExeName}; ValueData: {app}\{#ExeName}.exe; Permissions: users-modify; Flags: uninsdeletevalue noerror; Tasks: startup; Check: IsRegularUser -; HKEY_LOCAL_MACHINE - for all users when admin -Root: HKLM; Subkey: Software\Microsoft\Windows\CurrentVersion\Run; ValueType: string; ValueName: {#ExeName}; ValueData: {app}\{#ExeName}.exe; Permissions: admins-modify; Flags: uninsdeletevalue noerror; Tasks: startup; Check: not IsRegularUser - -; Register our own filetype for all users -; HKEY_LOCAL_USER - for current user only -Root: HKCU; Subkey: Software\Classes\.greenshot; ValueType: string; ValueName: ""; ValueData: "Greenshot"; Permissions: users-modify; Flags: uninsdeletevalue noerror; Check: IsRegularUser -Root: HKCU; Subkey: Software\Classes\Greenshot; ValueType: string; ValueName: ""; ValueData: "Greenshot File"; Permissions: users-modify; Flags: uninsdeletevalue noerror; Check: IsRegularUser -Root: HKCU; Subkey: Software\Classes\Greenshot\DefaultIcon; ValueType: string; ValueName: ""; ValueData: "{app}\Greenshot.EXE,0"; Permissions: users-modify; Flags: uninsdeletevalue noerror; Check: IsRegularUser -Root: HKCU; Subkey: Software\Classes\Greenshot\shell\open\command; ValueType: string; ValueName: ""; ValueData: """{app}\Greenshot.EXE"" --openfile ""%1"""; Permissions: users-modify; Flags: uninsdeletevalue noerror; Check: IsRegularUser -; HKEY_LOCAL_MACHINE - for all users when admin -Root: HKLM; Subkey: Software\Classes\.greenshot; ValueType: string; ValueName: ""; ValueData: "Greenshot"; Permissions: admins-modify; Flags: uninsdeletevalue noerror; Check: not IsRegularUser -Root: HKLM; Subkey: Software\Classes\Greenshot; ValueType: string; ValueName: ""; ValueData: "Greenshot File"; Permissions: admins-modify; Flags: uninsdeletevalue noerror; Check: not IsRegularUser -Root: HKLM; Subkey: Software\Classes\Greenshot\DefaultIcon; ValueType: string; ValueName: ""; ValueData: "{app}\Greenshot.EXE,0"; Permissions: admins-modify; Flags: uninsdeletevalue noerror; Check: not IsRegularUser -Root: HKLM; Subkey: Software\Classes\Greenshot\shell\open\command; ValueType: string; ValueName: ""; ValueData: """{app}\Greenshot.EXE"" --openfile ""%1"""; Permissions: admins-modify; Flags: uninsdeletevalue noerror; Check: not IsRegularUser - -[Icons] -Name: {group}\{#ExeName}; Filename: {app}\{#ExeName}.exe; WorkingDir: {app}; AppUserModelID: "{#ExeName}" -Name: {group}\Uninstall {#ExeName}; Filename: {uninstallexe}; WorkingDir: {app}; -Name: {group}\Readme.txt; Filename: {app}\readme.txt; WorkingDir: {app} -Name: {group}\License.txt; Filename: {app}\license.txt; WorkingDir: {app} - -[Languages] -Name: en; MessagesFile: compiler:Default.isl -Name: cn; MessagesFile: Languages\ChineseSimplified.isl -Name: de; MessagesFile: compiler:Languages\German.isl -Name: es; MessagesFile: compiler:Languages\Spanish.isl -Name: fi; MessagesFile: compiler:Languages\Finnish.isl -Name: fr; MessagesFile: compiler:Languages\French.isl -Name: nl; MessagesFile: compiler:Languages\Dutch.isl -Name: lt; MessagesFile: Languages\Latvian.isl -Name: nn; MessagesFile: Languages\NorwegianNynorsk.isl -Name: sr; MessagesFile: Languages\SerbianCyrillic.isl -Name: sv; MessagesFile: Languages\Swedish.isl -Name: uk; MessagesFile: compiler:Languages\Ukrainian.isl - -[Tasks] -Name: startup; Description: {cm:startup} - -[CustomMessages] - -de.confluence=Confluence Plug-in -de.default=Standard installation -en.office=Microsoft Office Plug-in -de.externalcommand=Externes Kommando Plug-in -de.imgur=Imgur Plug-in (Siehe: http://imgur.com) -de.jira=Jira Plug-in -de.language=Zusätzliche Sprachen -de.ocr=OCR Plug-in (benötigt Microsoft Office Document Imaging (MODI)) -de.optimize=Optimierung der Leistung, kann etwas dauern. -de.startgreenshot={#ExeName} starten -de.startup={#ExeName} starten wenn Windows hochfährt -de.win10=Windows 10 Plug-in - -en.confluence=Confluence plug-in -en.default=Default installation -en.office=Microsoft Office plug-in -en.externalcommand=Open with external command plug-in -en.imgur=Imgur plug-in (See: http://imgur.com) -en.jira=Jira plug-in -en.language=Additional languages -en.ocr=OCR plug-in (needs Microsoft Office Document Imaging (MODI)) -en.optimize=Optimizing performance, this may take a while. -en.startgreenshot=Start {#ExeName} -en.startup=Start {#ExeName} with Windows start -en.win10=Windows 10 plug-in - -es.confluence=Extensión para Confluence -es.default=${default} -es.externalcommand=Extensión para abrir con programas externos -es.imgur=Extensión para Imgur (Ver http://imgur.com) -es.jira=Extensión para Jira -es.language=Idiomas adicionales -es.ocr=Extensión para OCR (necesita Microsoft Office Document Imaging (MODI)) -es.optimize=Optimizando rendimiento; por favor, espera. -es.startgreenshot=Lanzar {#ExeName} -es.startup=Lanzar {#ExeName} al iniciarse Windows -es.win10=Extensión para Windows 10 - -fi.confluence=Confluence-liitännäinen -fi.default=${default} -fi.office=Microsoft-Office-liitännäinen -fi.externalcommand=Avaa Ulkoinen komento-liitännäisellä -fi.imgur=Imgur-liitännäinen (Katso: http://imgur.com) -fi.jira=Jira-liitännäinen -fi.language=Lisäkielet -fi.ocr=OCR-liitännäinen (Tarvitaan: Microsoft Office Document Imaging (MODI)) -fi.optimize=Optimoidaan suorituskykyä, tämä voi kestää hetken. -fi.startgreenshot=Käynnistä {#ExeName} -fi.startup=Käynnistä {#ExeName} Windowsin käynnistyessä -fi.win10=Windows 10-liitännäinen - -fr.confluence=Greffon Confluence -fr.default=${default} -fr.office=Greffon Microsoft Office -fr.externalcommand=Ouvrir avec le greffon de commande externe -fr.imgur=Greffon Imgur (Voir: http://imgur.com) -fr.jira=Greffon Jira -fr.language=Langues additionnelles -fr.ocr=Greffon OCR (nécessite Document Imaging de Microsoft Office [MODI]) -fr.optimize=Optimisation des performances, Ceci peut prendre un certain temps. -fr.startgreenshot=Démarrer {#ExeName} -fr.startup=Lancer {#ExeName} au démarrage de Windows -fr.win10=Greffon Windows 10 - -lt.confluence=Confluence spraudnis -lt.default=${default} -lt.office=Microsoft Office spraudnis -lt.externalcommand=Pielāgotu darbību spraudnis -lt.imgur=Imgur spraudnis (Vairāk šeit: http://imgur.com) -lt.jira=Jira spraudnis -lt.language=Papildus valodas -lt.ocr=OCR spraudnis (nepieciešams Microsoft Office Document Imaging (MODI)) -lt.optimize=Uzlaboju veikstpēju, tas prasīs kādu laiciņu. -lt.startgreenshot=Palaist {#ExeName} -lt.startup=Palaist {#ExeName} uzsākot darbus -lt.win10=Windows 10 spraudnis - -nl.confluence=Confluence plug-in -nl.default=Standaardinstallatie -nl.office=Microsoft Office plug-in -nl.externalcommand=Openen met extern commando plug-in -nl.imgur=Imgur plug-in (zie: http://imgur.com) -nl.jira=Jira plug-in -nl.language=Extra talen -nl.ocr=OCR plug-in (vereist Microsoft Office Document Imaging (MODI)) -nl.optimize=Prestaties verbeteren, even geduld. -nl.startgreenshot={#ExeName} starten -nl.startup={#ExeName} automatisch starten met Windows -nl.win10=Windows 10 plug-in - -nn.confluence=Confluence-tillegg -nn.default=Default installation -nn.office=Microsoft Office Tillegg -nn.externalcommand=Tillegg for å opne med ekstern kommando -nn.imgur=Imgur-tillegg (sjå http://imgur.com) -nn.jira=Jira-tillegg -nn.language=Andre språk -nn.ocr=OCR-tillegg (krev Microsoft Office Document Imaging (MODI)) -nn.optimize=Optimaliserar ytelse, dette kan ta litt tid... -nn.startgreenshot=Start {#ExeName} -nn.startup=Start {#ExeName} når Windows startar -nn.win10=Windows 10 Tillegg - -sr.confluence=Прикључак за Конфлуенс -sr.default=${default} -sr.externalcommand=Отвори са прикључком за спољне наредбе -sr.imgur=Прикључак за Имиџер (http://imgur.com) -sr.jira=Прикључак за Џиру -sr.language=Додатни језици -sr.ocr=OCR прикључак (захтева Microsoft Office Document Imaging (MODI)) -sr.optimize=Оптимизујем перформансе… -sr.startgreenshot=Покрени Гриншот -sr.startup=Покрени програм са системом -sr.win10=Прикључак за Windows 10 - -sv.startup=Starta {#ExeName} med Windows -sv.startgreenshot=Starta {#ExeName} -sv.jira=Jira-insticksprogram -sv.confluence=Confluence-insticksprogram -sv.externalcommand=Öppna med externt kommando-insticksprogram -sv.ocr=OCR-insticksprogram (kräver Microsoft Office Document Imaging (MODI)) -sv.imgur=Imgur-insticksprogram (Se: http://imgur.com) -sv.language=Ytterligare språk -sv.optimize=Optimerar prestanda, detta kan ta en stund. -sv.win10=Windows 10-insticksprogram - -uk.confluence=Плагін Confluence -uk.default=${default} -uk.externalcommand=Плагін запуску зовнішньої команди -uk.imgur=Плагін Imgur (див.: http://imgur.com) -uk.jira=Плагін Jira -uk.language=Додаткові мови -uk.ocr=Плагін OCR (потребує Microsoft Office Document Imaging (MODI)) -uk.optimize=Оптимізація продуктивності, це може забрати час. -uk.startgreenshot=Запустити {#ExeName} -uk.startup=Запускати {#ExeName} під час запуску Windows -uk.win10=Плагін Windows 10 - -cn.confluence=Confluence插件 -cn.default=${default} -cn.externalcommand=使用外部命令打开插件 -cn.imgur=Imgur插件( (请访问: http://imgur.com)) -cn.jira=Jira插件 -cn.language=其它语言 -cn.ocr=OCR插件(需要Microsoft Office Document Imaging (MODI)的支持) -cn.optimize=正在优化性能,这可能需要一点时间。 -cn.startgreenshot=启动{#ExeName} -cn.startup=让{#ExeName}随Windows一起启动 -cn.win10=Windows 10插件 - -[Types] -Name: "default"; Description: "{cm:default}" -Name: "full"; Description: "{code:FullInstall}" -Name: "compact"; Description: "{code:CompactInstall}" -Name: "custom"; Description: "{code:CustomInstall}"; Flags: iscustom - -[Components] -Name: "greenshot"; Description: "Greenshot"; Types: default full compact custom; Flags: fixed -Name: "plugins\office"; Description: {cm:office}; Types: default full custom; Flags: disablenouninstallwarning -Name: "plugins\ocr"; Description: {cm:ocr}; Types: default full custom; Flags: disablenouninstallwarning -Name: "plugins\jira"; Description: {cm:jira}; Types: full custom; Flags: disablenouninstallwarning -Name: "plugins\imgur"; Description: {cm:imgur}; Types: default full custom; Flags: disablenouninstallwarning -Name: "plugins\confluence"; Description: {cm:confluence}; Types: full custom; Flags: disablenouninstallwarning -Name: "plugins\externalcommand"; Description: {cm:externalcommand}; Types: default full custom; Flags: disablenouninstallwarning -;Name: "plugins\networkimport"; Description: "Network Import Plugin"; Types: full -Name: "plugins\box"; Description: "Box Plugin"; Types: full custom; Flags: disablenouninstallwarning -Name: "plugins\dropbox"; Description: "Dropbox Plugin"; Types: full custom; Flags: disablenouninstallwarning -Name: "plugins\flickr"; Description: "Flickr Plugin"; Types: full custom; Flags: disablenouninstallwarning -Name: "plugins\picasa"; Description: "Picasa Plugin"; Types: full custom; Flags: disablenouninstallwarning -Name: "plugins\photobucket"; Description: "Photobucket Plugin"; Types: full custom; Flags: disablenouninstallwarning -Name: "plugins\win10"; Description: "Windows 10 Plugin"; Types: default full custom; Flags: disablenouninstallwarning; Check: IsWindows10OrNewer() -Name: "languages"; Description: {cm:language}; Types: full custom; Flags: disablenouninstallwarning -Name: "languages\arSY"; Description: "العربية"; Types: full custom; Flags: disablenouninstallwarning; Check: hasLanguageGroup('d') -Name: "languages\caCA"; Description: "Català"; Types: full custom; Flags: disablenouninstallwarning; Check: hasLanguageGroup('1') -Name: "languages\csCZ"; Description: "Ceština"; Types: full custom; Flags: disablenouninstallwarning; Check: hasLanguageGroup('1') -Name: "languages\daDK"; Description: "Dansk"; Types: full custom; Flags: disablenouninstallwarning; Check: hasLanguageGroup('1') -Name: "languages\dexfranconia"; Description: "Frängisch (Deutsch)"; Types: full custom; Flags: disablenouninstallwarning; Check: hasLanguageGroup('1') -Name: "languages\elGR"; Description: "ελληνικά"; Types: full custom; Flags: disablenouninstallwarning; Check: hasLanguageGroup('4') -Name: "languages\esES"; Description: "Español"; Types: full custom; Flags: disablenouninstallwarning; Check: hasLanguageGroup('1') -Name: "languages\etEE"; Description: "Eesti"; Types: full custom; Flags: disablenouninstallwarning; Check: hasLanguageGroup('2') -Name: "languages\faIR"; Description: "پارسی"; Types: full custom; Flags: disablenouninstallwarning; Check: hasLanguageGroup('d') -Name: "languages\fiFI"; Description: "Suomi"; Types: full custom; Flags: disablenouninstallwarning; Check: hasLanguageGroup('1') -Name: "languages\frFR"; Description: "Français"; Types: full custom; Flags: disablenouninstallwarning; Check: hasLanguageGroup('1') -Name: "languages\frQC"; Description: "Français - Québec"; Types: full custom; Flags: disablenouninstallwarning; Check: hasLanguageGroup('1') -Name: "languages\heIL"; Description: "עִבְרִית"; Types: full custom; Flags: disablenouninstallwarning; Check: hasLanguageGroup('c') -Name: "languages\huHU"; Description: "Magyar"; Types: full custom; Flags: disablenouninstallwarning; Check: hasLanguageGroup('2') -Name: "languages\idID"; Description: "Bahasa Indonesia"; Types: full custom; Flags: disablenouninstallwarning; Check: hasLanguageGroup('1') -Name: "languages\itIT"; Description: "Italiano"; Types: full custom; Flags: disablenouninstallwarning; Check: hasLanguageGroup('1') -Name: "languages\jaJP"; Description: "日本語"; Types: full custom; Flags: disablenouninstallwarning; Check: hasLanguageGroup('7') -Name: "languages\koKR"; Description: "한국어"; Types: full custom; Flags: disablenouninstallwarning; Check: hasLanguageGroup('8') -Name: "languages\kabDZ"; Description: "Taqbaylit"; Types: full custom; Flags: disablenouninstallwarning; Check: hasLanguageGroup('8') -Name: "languages\ltLT"; Description: "Lietuvių"; Types: full custom; Flags: disablenouninstallwarning; Check: hasLanguageGroup('3') -Name: "languages\lvLV"; Description: "Latviski"; Types: full custom; Flags: disablenouninstallwarning; Check: hasLanguageGroup('3') -Name: "languages\nnNO"; Description: "Nynorsk"; Types: full custom; Flags: disablenouninstallwarning; Check: hasLanguageGroup('1') -Name: "languages\plPL"; Description: "Polski"; Types: full custom; Flags: disablenouninstallwarning; Check: hasLanguageGroup('2') -Name: "languages\ptBR"; Description: "Português do Brasil"; Types: full custom; Flags: disablenouninstallwarning; Check: hasLanguageGroup('1') -Name: "languages\ptPT"; Description: "Português de Portugal"; Types: full custom; Flags: disablenouninstallwarning; Check: hasLanguageGroup('1') -Name: "languages\ruRU"; Description: "Pусский"; Types: full custom; Flags: disablenouninstallwarning; Check: hasLanguageGroup('5') -Name: "languages\roRO"; Description: "Română"; Types: full custom; Flags: disablenouninstallwarning; Check: hasLanguageGroup('2') -Name: "languages\skSK"; Description: "Slovenčina"; Types: full custom; Flags: disablenouninstallwarning; Check: hasLanguageGroup('2') -Name: "languages\slSI"; Description: "Slovenščina"; Types: full custom; Flags: disablenouninstallwarning; Check: hasLanguageGroup('2') -Name: "languages\srRS"; Description: "Српски"; Types: full custom; Flags: disablenouninstallwarning; Check: hasLanguageGroup('5') -Name: "languages\svSE"; Description: "Svenska"; Types: full custom; Flags: disablenouninstallwarning; Check: hasLanguageGroup('1') -Name: "languages\trTR"; Description: "Türk"; Types: full custom; Flags: disablenouninstallwarning; Check: hasLanguageGroup('6') -Name: "languages\ukUA"; Description: "Українська"; Types: full custom; Flags: disablenouninstallwarning; Check: hasLanguageGroup('5') -Name: "languages\viVN"; Description: "Việt"; Types: full custom; Flags: disablenouninstallwarning; Check: hasLanguageGroup('e') -Name: "languages\zhCN"; Description: "简体中文"; Types: full custom; Flags: disablenouninstallwarning; Check: hasLanguageGroup('a') -Name: "languages\zhTW"; Description: "繁體中文"; Types: full custom; Flags: disablenouninstallwarning; Check: hasLanguageGroup('9') -[Code] -// Do we have a regular user trying to install this? -function IsRegularUser(): Boolean; -begin - Result := not (IsAdmin or IsAdminInstallMode); -end; - -// The following code is used to select the installation path, this is localappdata if non poweruser -function DefDirRoot(Param: String): String; -begin - if IsRegularUser then - Result := ExpandConstant('{localappdata}') - else - Result := ExpandConstant('{pf}') -end; - - -function FullInstall(Param : String) : String; -begin - result := SetupMessage(msgFullInstallation); -end; - -function CustomInstall(Param : String) : String; -begin - result := SetupMessage(msgCustomInstallation); -end; - -function CompactInstall(Param : String) : String; -begin - result := SetupMessage(msgCompactInstallation); -end; -///////////////////////////////////////////////////////////////////// -// The following uninstall code was found at: -// http://stackoverflow.com/questions/2000296/innosetup-how-to-automatically-uninstall-previous-installed-version -// and than modified to work in a 32/64 bit environment -///////////////////////////////////////////////////////////////////// -function GetUninstallStrings(): array of String; -var - sUnInstPath: String; - sUnInstallString: String; - asUninstallStrings : array of String; - index : Integer; -begin - sUnInstPath := ExpandConstant('Software\Microsoft\Windows\CurrentVersion\Uninstall\{#emit SetupSetting("AppId")}_is1'); - sUnInstallString := ''; - index := 0; - - // Retrieve uninstall string from HKLM32 or HKCU32 - if RegQueryStringValue(HKLM32, sUnInstPath, 'UninstallString', sUnInstallString) then - begin - SetArrayLength(asUninstallStrings, index + 1); - asUninstallStrings[index] := sUnInstallString; - index := index +1; - end; - - if RegQueryStringValue(HKCU32, sUnInstPath, 'UninstallString', sUnInstallString) then - begin - SetArrayLength(asUninstallStrings, index + 1); - asUninstallStrings[index] := sUnInstallString; - index := index +1; - end; - - // Only for Windows with 64 bit support: Retrieve uninstall string from HKLM64 or HKCU64 - if IsWin64 then - begin - if RegQueryStringValue(HKLM64, sUnInstPath, 'UninstallString', sUnInstallString) then - begin - SetArrayLength(asUninstallStrings, index + 1); - asUninstallStrings[index] := sUnInstallString; - index := index +1; - end; - - if RegQueryStringValue(HKCU64, sUnInstPath, 'UninstallString', sUnInstallString) then - begin - SetArrayLength(asUninstallStrings, index + 1); - asUninstallStrings[index] := sUnInstallString; - index := index +1; - end; - end; - Result := asUninstallStrings; -end; - -///////////////////////////////////////////////////////////////////// -procedure UnInstallOldVersions(); -var - sUnInstallString: String; - index: Integer; - isUninstallMade: Boolean; - iResultCode : Integer; - asUninstallStrings : array of String; -begin - isUninstallMade := false; - asUninstallStrings := GetUninstallStrings(); - for index := 0 to (GetArrayLength(asUninstallStrings) -1) do - begin - sUnInstallString := RemoveQuotes(asUninstallStrings[index]); - if Exec(sUnInstallString, '/SILENT /NORESTART /SUPPRESSMSGBOXES','', SW_HIDE, ewWaitUntilTerminated, iResultCode) then - isUninstallMade := true; - end; - - // Wait a few seconds to prevent installation issues, otherwise files are removed in one process while the other tries to link to them - if (isUninstallMade) then - Sleep(2000); -end; - -///////////////////////////////////////////////////////////////////// -procedure CurStepChanged(CurStep: TSetupStep); -begin - if (CurStep=ssInstall) then - begin - UnInstallOldVersions(); - end; -end; -///////////////////////////////////////////////////////////////////// -// End of unstall code -///////////////////////////////////////////////////////////////////// - -// Build a list of greenshot parameters from the supplied installer parameters -function GetParamsForGS(argument: String): String; -var - i: Integer; - parametersString: String; - currentParameter: String; - foundStart: Boolean; - foundNoRun: Boolean; - foundLanguage: Boolean; -begin - foundNoRun := false; - foundLanguage := false; - foundStart := false; - for i:= 0 to ParamCount() do begin - currentParameter := ParamStr(i); - - // check if norun is supplied - if Lowercase(currentParameter) = '/norun' then begin - foundNoRun := true; - continue; - end; - - if foundStart then begin - parametersString := parametersString + ' ' + currentParameter; - foundStart := false; - end - else begin - if Lowercase(currentParameter) = '/language' then begin - foundStart := true; - foundLanguage := true; - parametersString := parametersString + ' ' + currentParameter; - end; - end; - end; - if not foundLanguage then begin - parametersString := parametersString + ' /language ' + ExpandConstant('{language}'); - end; - if foundNoRun then begin - parametersString := parametersString + ' /norun'; - end; - // For debugging comment out the following - //MsgBox(parametersString, mbInformation, MB_OK); - - Result := parametersString; -end; - -// Check if language group is installed -function hasLanguageGroup(argument: String): Boolean; -var - keyValue: String; - returnValue: Boolean; -begin - returnValue := true; - if (RegQueryStringValue( HKLM, 'SYSTEM\CurrentControlSet\Control\Nls\Language Groups', argument, keyValue)) then begin - if Length(keyValue) = 0 then begin - returnValue := false; - end; - end; - Result := returnValue; -end; - -function hasDotNet() : boolean; -begin - Result := netfxspversion(NetFx4x, '') >= 71; -end; - -// Initialize the setup -function InitializeSetup(): Boolean; -begin - // Check for .NET and install 4.7.1 if we don't have it - if not hasDotNet() then - begin - // Enhance installer, if needed, otherwise .NET installations won't work - msi20('2.0'); - msi31('3.0'); - - //install .net 4.7.1 - dotnetfx47(71); - end; - Result := true; -end; - -function IsWindowsVersionOrNewer(Major, Minor: Integer): Boolean; -var - Version: TWindowsVersion; -begin - GetWindowsVersionEx(Version); - Result := - (Version.Major > Major) or - ((Version.Major = Major) and (Version.Minor >= Minor)); -end; - -function IsWindows10OrNewer: Boolean; -begin - Result := IsWindowsVersionOrNewer(10, 0); -end; - -[Run] -Filename: "{app}\{#ExeName}.exe"; Description: "{cm:startgreenshot}"; Parameters: "{code:GetParamsForGS}"; WorkingDir: "{app}"; Flags: nowait postinstall runasoriginaluser -Filename: "http://getgreenshot.org/thank-you/?language={language}&version={#Version}"; Flags: shellexec runasoriginaluser - -[InstallDelete] -Name: {app}; Type: dirifempty; +#define ExeName "Greenshot" +#define Version GetEnv('BuildVersionSimple') +#define FileVersion GetEnv('AssemblyInformationalVersion') +#define BaseDir "..\..\.." +#define ReleaseDir "..\..\bin\Release\net472" +#define BinDir "bin\Release\net472" + +; Include the scripts to install .NET Framework +; See http://www.codeproject.com/KB/install/dotnetfx_innosetup_instal.aspx +#include "scripts\products.iss" +#include "scripts\products\stringversion.iss" +#include "scripts\products\winversion.iss" +#include "scripts\products\fileversion.iss" +#include "scripts\products\msi20.iss" +#include "scripts\products\msi31.iss" +#include "scripts\products\dotnetfxversion.iss" +#include "scripts\products\dotnetfx47.iss" + +[Files] +Source: {#ReleaseDir}\Greenshot.exe; DestDir: {app}; Components: greenshot; Flags: overwritereadonly ignoreversion replacesameversion +Source: {#ReleaseDir}\GreenshotPlugin.dll; DestDir: {app}; Components: greenshot; Flags: overwritereadonly ignoreversion replacesameversion +Source: {#ReleaseDir}\Greenshot.exe.config; DestDir: {app}; Components: greenshot; Flags: overwritereadonly ignoreversion replacesameversion +Source: {#ReleaseDir}\log4net.dll; DestDir: {app}; Components: greenshot; Flags: overwritereadonly ignoreversion replacesameversion +Source: {#ReleaseDir}\Dapplo.Http*.dll; DestDir: {app}; Components: greenshot; Flags: overwritereadonly ignoreversion replacesameversion +Source: {#ReleaseDir}\Dapplo.Log.dll; DestDir: {app}; Components: greenshot; Flags: overwritereadonly ignoreversion replacesameversion +Source: {#ReleaseDir}\Svg.dll; DestDir: {app}; Components: greenshot; Flags: overwritereadonly ignoreversion replacesameversion +Source: {#ReleaseDir}\Fizzler.dll; DestDir: {app}; Components: greenshot; Flags: overwritereadonly ignoreversion replacesameversion +Source: {#ReleaseDir}\Newtonsoft.Json.dll; DestDir: {app}; Components: greenshot; Flags: overwritereadonly ignoreversion replacesameversion +Source: ..\..\log4net.xml; DestDir: {app}; Components: greenshot; Flags: overwritereadonly ignoreversion +Source: {#ReleaseDir}\checksum.SHA256; DestDir: {app}; Components: greenshot; Flags: overwritereadonly ignoreversion replacesameversion +;Source: ..\greenshot-defaults.ini; DestDir: {app}; Flags: overwritereadonly ignoreversion replacesameversion +Source: ..\additional_files\installer.txt; DestDir: {app}; Components: greenshot; Flags: overwritereadonly recursesubdirs ignoreversion replacesameversion +Source: ..\additional_files\license.txt; DestDir: {app}; Components: greenshot; Flags: overwritereadonly recursesubdirs ignoreversion replacesameversion +Source: ..\additional_files\readme.txt; DestDir: {app}; Components: greenshot; Flags: overwritereadonly recursesubdirs ignoreversion replacesameversion + +; Core language files +Source: ..\..\Languages\*nl-NL*; Excludes: "*installer*,*website*"; DestDir: {app}\Languages; Components: greenshot; Flags: overwritereadonly ignoreversion replacesameversion; +Source: ..\..\Languages\*en-US*; Excludes: "*installer*,*website*"; DestDir: {app}\Languages; Components: greenshot; Flags: overwritereadonly ignoreversion replacesameversion; +Source: ..\..\Languages\*de-DE*; Excludes: "*installer*,*website*"; DestDir: {app}\Languages; Components: greenshot; Flags: overwritereadonly ignoreversion replacesameversion; + +; Additional language files +Source: ..\..\Languages\*ar-SY*; Excludes: "*installer*,*website*"; DestDir: {app}\Languages; Components: languages\arSY; Flags: overwritereadonly ignoreversion replacesameversion; +Source: ..\..\Languages\*ca-CA*; Excludes: "*installer*,*website*"; DestDir: {app}\Languages; Components: languages\caCA; Flags: overwritereadonly ignoreversion replacesameversion; +Source: ..\..\Languages\*cs-CZ*; Excludes: "*installer*,*website*"; DestDir: {app}\Languages; Components: languages\csCZ; Flags: overwritereadonly ignoreversion replacesameversion; +Source: ..\..\Languages\*da-DK*; Excludes: "*installer*,*website*"; DestDir: {app}\Languages; Components: languages\daDK; Flags: overwritereadonly ignoreversion replacesameversion; +Source: ..\..\Languages\*de-x-franconia*; Excludes: "*installer*,*website*"; DestDir: {app}\Languages; Components: languages\dexfranconia; Flags: overwritereadonly ignoreversion replacesameversion; +Source: ..\..\Languages\*el-GR*; Excludes: "*installer*,*website*"; DestDir: {app}\Languages; Components: languages\elGR; Flags: overwritereadonly ignoreversion replacesameversion; +Source: ..\..\Languages\*es-ES*; Excludes: "*installer*,*website*"; DestDir: {app}\Languages; Components: languages\esES; Flags: overwritereadonly ignoreversion replacesameversion; +Source: ..\..\Languages\*et-EE*; Excludes: "*installer*,*website*"; DestDir: {app}\Languages; Components: languages\etEE; Flags: overwritereadonly ignoreversion replacesameversion; +Source: ..\..\Languages\*fa-IR*; Excludes: "*installer*,*website*"; DestDir: {app}\Languages; Components: languages\faIR; Flags: overwritereadonly ignoreversion replacesameversion; +Source: ..\..\Languages\*fi-FI*; Excludes: "*installer*,*website*"; DestDir: {app}\Languages; Components: languages\fiFI; Flags: overwritereadonly ignoreversion replacesameversion; +Source: ..\..\Languages\*fr-FR*; Excludes: "*installer*,*website*"; DestDir: {app}\Languages; Components: languages\frFR; Flags: overwritereadonly ignoreversion replacesameversion; +Source: ..\..\Languages\*fr-QC*; Excludes: "*installer*,*website*"; DestDir: {app}\Languages; Components: languages\frQC; Flags: overwritereadonly ignoreversion replacesameversion; +Source: ..\..\Languages\*he-IL*; Excludes: "*installer*,*website*"; DestDir: {app}\Languages; Components: languages\heIL; Flags: overwritereadonly ignoreversion replacesameversion; +Source: ..\..\Languages\*hu-HU*; Excludes: "*installer*,*website*"; DestDir: {app}\Languages; Components: languages\huHU; Flags: overwritereadonly ignoreversion replacesameversion; +Source: ..\..\Languages\*id-ID*; Excludes: "*installer*,*website*"; DestDir: {app}\Languages; Components: languages\idID; Flags: overwritereadonly ignoreversion replacesameversion; +Source: ..\..\Languages\*it-IT*; Excludes: "*installer*,*website*"; DestDir: {app}\Languages; Components: languages\itIT; Flags: overwritereadonly ignoreversion replacesameversion; +Source: ..\..\Languages\*ja-JP*; Excludes: "*installer*,*website*"; DestDir: {app}\Languages; Components: languages\jaJP; Flags: overwritereadonly ignoreversion replacesameversion; +Source: ..\..\Languages\*ko-KR*; Excludes: "*installer*,*website*"; DestDir: {app}\Languages; Components: languages\koKR; Flags: overwritereadonly ignoreversion replacesameversion; +Source: ..\..\Languages\*kab-DZ*; Excludes: "*installer*,*website*"; DestDir: {app}\Languages; Components: languages\kabDZ; Flags: overwritereadonly ignoreversion replacesameversion; +Source: ..\..\Languages\*lt-LT*; Excludes: "*installer*,*website*"; DestDir: {app}\Languages; Components: languages\ltLT; Flags: overwritereadonly ignoreversion replacesameversion; +Source: ..\..\Languages\*lv-LV*; Excludes: "*installer*,*website*"; DestDir: {app}\Languages; Components: languages\lvLV; Flags: overwritereadonly ignoreversion replacesameversion; +Source: ..\..\Languages\*nn-NO*; Excludes: "*installer*,*website*"; DestDir: {app}\Languages; Components: languages\nnNO; Flags: overwritereadonly ignoreversion replacesameversion; +Source: ..\..\Languages\*pl-PL*; Excludes: "*installer*,*website*"; DestDir: {app}\Languages; Components: languages\plPL; Flags: overwritereadonly ignoreversion replacesameversion; +Source: ..\..\Languages\*pt-BR*; Excludes: "*installer*,*website*"; DestDir: {app}\Languages; Components: languages\ptBR; Flags: overwritereadonly ignoreversion replacesameversion; +Source: ..\..\Languages\*pt-PT*; Excludes: "*installer*,*website*"; DestDir: {app}\Languages; Components: languages\ptPT; Flags: overwritereadonly ignoreversion replacesameversion; +Source: ..\..\Languages\*ro-RO*; Excludes: "*installer*,*website*"; DestDir: {app}\Languages; Components: languages\roRO; Flags: overwritereadonly ignoreversion replacesameversion; +Source: ..\..\Languages\*ru-RU*; Excludes: "*installer*,*website*"; DestDir: {app}\Languages; Components: languages\ruRU; Flags: overwritereadonly ignoreversion replacesameversion; +Source: ..\..\Languages\*sk-SK*; Excludes: "*installer*,*website*"; DestDir: {app}\Languages; Components: languages\skSK; Flags: overwritereadonly ignoreversion replacesameversion; +Source: ..\..\Languages\*sl-SI*; Excludes: "*installer*,*website*"; DestDir: {app}\Languages; Components: languages\slSI; Flags: overwritereadonly ignoreversion replacesameversion; +Source: ..\..\Languages\*sr-RS*; Excludes: "*installer*,*website*"; DestDir: {app}\Languages; Components: languages\srRS; Flags: overwritereadonly ignoreversion replacesameversion; +Source: ..\..\Languages\*sv-SE*; Excludes: "*installer*,*website*"; DestDir: {app}\Languages; Components: languages\svSE; Flags: overwritereadonly ignoreversion replacesameversion; +Source: ..\..\Languages\*tr-TR*; Excludes: "*installer*,*website*"; DestDir: {app}\Languages; Components: languages\trTR; Flags: overwritereadonly ignoreversion replacesameversion; +Source: ..\..\Languages\*uk-UA*; Excludes: "*installer*,*website*"; DestDir: {app}\Languages; Components: languages\ukUA; Flags: overwritereadonly ignoreversion replacesameversion; +Source: ..\..\Languages\*vi-VN*; Excludes: "*installer*,*website*"; DestDir: {app}\Languages; Components: languages\viVN; Flags: overwritereadonly ignoreversion replacesameversion; +Source: ..\..\Languages\*zh-CN*; Excludes: "*installer*,*website*"; DestDir: {app}\Languages; Components: languages\zhCN; Flags: overwritereadonly ignoreversion replacesameversion; +Source: ..\..\Languages\*zh-TW*; Excludes: "*installer*,*website*"; DestDir: {app}\Languages; Components: languages\zhTW; Flags: overwritereadonly ignoreversion replacesameversion; + +;Office Plugin +Source: {#BaseDir}\GreenshotOfficePlugin\{#BinDir}\GreenshotOfficePlugin.dll; DestDir: {app}\Plugins\GreenshotOfficePlugin; Components: plugins\office; Flags: overwritereadonly recursesubdirs ignoreversion replacesameversion; +;OCR Plugin +Source: {#BaseDir}\GreenshotOCRPlugin\{#BinDir}\GreenshotOCRPlugin.dll; DestDir: {app}\Plugins\GreenshotOCRPlugin; Components: plugins\ocr; Flags: overwritereadonly recursesubdirs ignoreversion replacesameversion; +Source: {#BaseDir}\GreenshotOCRPlugin\Languages\language_ocr*.xml; DestDir: {app}\Languages\Plugins\GreenshotOCRPlugin; Components: plugins\ocr; Flags: overwritereadonly ignoreversion replacesameversion; +Source: {#BaseDir}\GreenshotOCRCommand\{#BinDir}\GreenshotOCRCommand.exe; DestDir: {app}\Plugins\GreenshotOCRPlugin; Components: plugins\ocr; Flags: overwritereadonly recursesubdirs ignoreversion replacesameversion; +Source: {#BaseDir}\GreenshotOCRCommand\{#BinDir}\GreenshotOCRCommand.exe.config; DestDir: {app}\Plugins\GreenshotOCRPlugin; Components: plugins\ocr; Flags: overwritereadonly recursesubdirs ignoreversion replacesameversion; +;JIRA Plugin +Source: {#BaseDir}\GreenshotJiraPlugin\{#BinDir}\GreenshotJiraPlugin.dll; DestDir: {app}\Plugins\GreenshotJiraPlugin; Components: plugins\jira; Flags: overwritereadonly recursesubdirs ignoreversion replacesameversion; +Source: {#BaseDir}\GreenshotJiraPlugin\{#BinDir}\Dapplo.Jira.dll; DestDir: {app}\Plugins\GreenshotJiraPlugin; Components: plugins\jira; Flags: overwritereadonly recursesubdirs ignoreversion replacesameversion; +Source: {#BaseDir}\GreenshotJiraPlugin\Languages\language_jira*.xml; DestDir: {app}\Languages\Plugins\GreenshotJiraPlugin; Components: plugins\jira; Flags: overwritereadonly ignoreversion replacesameversion; +;Imgur Plugin +Source: {#BaseDir}\GreenshotImgurPlugin\{#BinDir}\GreenshotImgurPlugin.dll; DestDir: {app}\Plugins\GreenshotImgurPlugin; Components: plugins\imgur; Flags: overwritereadonly recursesubdirs ignoreversion replacesameversion; +Source: {#BaseDir}\GreenshotImgurPlugin\Languages\language_imgur*.xml; DestDir: {app}\Languages\Plugins\GreenshotImgurPlugin; Components: plugins\imgur; Flags: overwritereadonly ignoreversion replacesameversion; +;Box Plugin +Source: {#BaseDir}\GreenshotBoxPlugin\{#BinDir}\GreenshotBoxPlugin.dll; DestDir: {app}\Plugins\GreenshotBoxPlugin; Components: plugins\box; Flags: overwritereadonly recursesubdirs ignoreversion replacesameversion; +Source: {#BaseDir}\GreenshotBoxPlugin\Languages\language_box*.xml; DestDir: {app}\Languages\Plugins\GreenshotBoxPlugin; Components: plugins\box; Flags: overwritereadonly ignoreversion replacesameversion; +;DropBox Plugin +Source: {#BaseDir}\GreenshotDropBoxPlugin\{#BinDir}\GreenshotDropboxPlugin.dll; DestDir: {app}\Plugins\GreenshotDropBoxPlugin; Components: plugins\dropbox; Flags: overwritereadonly recursesubdirs ignoreversion replacesameversion; +Source: {#BaseDir}\GreenshotDropBoxPlugin\Languages\language_dropbox*.xml; DestDir: {app}\Languages\Plugins\GreenshotDropBoxPlugin; Components: plugins\dropbox; Flags: overwritereadonly ignoreversion replacesameversion; +;Flickr Plugin +Source: {#BaseDir}\GreenshotFlickrPlugin\{#BinDir}\GreenshotFlickrPlugin.dll; DestDir: {app}\Plugins\GreenshotFlickrPlugin; Components: plugins\flickr; Flags: overwritereadonly recursesubdirs ignoreversion replacesameversion; +Source: {#BaseDir}\GreenshotFlickrPlugin\Languages\language_flickr*.xml; DestDir: {app}\Languages\Plugins\GreenshotFlickrPlugin; Components: plugins\flickr; Flags: overwritereadonly ignoreversion replacesameversion; +;Photobucket Plugin +Source: {#BaseDir}\GreenshotPhotobucketPlugin\{#BinDir}\GreenshotPhotobucketPlugin.dll; DestDir: {app}\Plugins\GreenshotPhotobucketPlugin; Components: plugins\photobucket; Flags: overwritereadonly recursesubdirs ignoreversion replacesameversion; +Source: {#BaseDir}\GreenshotPhotobucketPlugin\Languages\language_photo*.xml; DestDir: {app}\Languages\Plugins\GreenshotPhotobucketPlugin; Components: plugins\photobucket; Flags: overwritereadonly ignoreversion replacesameversion; +;Picasa Plugin +Source: {#BaseDir}\GreenshotPicasaPlugin\{#BinDir}\GreenshotPicasaPlugin.dll; DestDir: {app}\Plugins\GreenshotPicasaPlugin; Components: plugins\picasa; Flags: overwritereadonly recursesubdirs ignoreversion replacesameversion; +Source: {#BaseDir}\GreenshotPicasaPlugin\Languages\language_picasa*.xml; DestDir: {app}\Languages\Plugins\GreenshotPicasaPlugin; Components: plugins\picasa; Flags: overwritereadonly ignoreversion replacesameversion; +;Confluence Plugin +Source: {#BaseDir}\GreenshotConfluencePlugin\{#BinDir}\GreenshotConfluencePlugin.dll; DestDir: {app}\Plugins\GreenshotConfluencePlugin; Components: plugins\confluence; Flags: overwritereadonly recursesubdirs ignoreversion replacesameversion; +Source: {#BaseDir}\GreenshotConfluencePlugin\Languages\language_confluence*.xml; DestDir: {app}\Languages\Plugins\GreenshotConfluencePlugin; Components: plugins\confluence; Flags: overwritereadonly ignoreversion replacesameversion; +;ExternalCommand Plugin +Source: {#BaseDir}\GreenshotExternalCommandPlugin\{#BinDir}\GreenshotExternalCommandPlugin.dll; DestDir: {app}\Plugins\GreenshotExternalCommandPlugin; Components: plugins\externalcommand; Flags: overwritereadonly recursesubdirs ignoreversion replacesameversion; +Source: {#BaseDir}\GreenshotExternalCommandPlugin\Languages\language_externalcommand*.xml; DestDir: {app}\Languages\Plugins\GreenshotExternalCommandPlugin; Components: plugins\externalcommand; Flags: overwritereadonly ignoreversion replacesameversion; +;Win 10 Plugin +Source: {#BaseDir}\GreenshotWin10Plugin\{#BinDir}\GreenshotWin10Plugin.dll; DestDir: {app}\Plugins\GreenshotWin10Plugin; Components: plugins\win10; Flags: overwritereadonly recursesubdirs ignoreversion replacesameversion; +[Setup] +; changes associations is used when the installer installs new extensions, it clears the explorer icon cache +ChangesAssociations=yes +AppId={#ExeName} +AppName={#ExeName} +AppMutex=F48E86D3-E34C-4DB7-8F8F-9A0EA55F0D08 +AppPublisher={#ExeName} +AppPublisherURL=http://getgreenshot.org +AppSupportURL=http://getgreenshot.org +AppUpdatesURL=http://getgreenshot.org +AppVerName={#ExeName} {#Version} +AppVersion={#Version} +ArchitecturesInstallIn64BitMode=x64 +Compression=lzma2/ultra64 +SolidCompression=yes +DefaultDirName={code:DefDirRoot}\{#ExeName} +DefaultGroupName={#ExeName} +InfoBeforeFile=..\additional_files\readme.txt +LicenseFile=..\additional_files\license.txt +LanguageDetectionMethod=uilanguage +MinVersion=6.1.7600 +OutputBaseFilename={#ExeName}-INSTALLER-{#Version}-UNSTABLE +OutputDir=..\ +PrivilegesRequired=lowest +SetupIconFile=..\..\icons\applicationIcon\icon.ico +; Create a SHA1 signature +; SignTool=SignTool sign /debug /fd sha1 /tr http://time.certum.pl /td sha1 $f +; Append a SHA256 to the previous SHA1 signature (this is what as does) +; SignTool=SignTool sign /debug /as /fd sha256 /tr http://time.certum.pl /td sha256 $f +; SignedUninstaller=yes +UninstallDisplayIcon={app}\{#ExeName}.exe +Uninstallable=true +VersionInfoCompany={#ExeName} +VersionInfoProductName={#ExeName} +VersionInfoProductTextVersion={#FileVersion} +VersionInfoTextVersion={#FileVersion} +VersionInfoVersion={#Version} +; Reference a bitmap, max size 164x314 +WizardImageFile=installer-large.bmp +; Reference a bitmap, max size 55x58 +WizardSmallImageFile=installer-small.bmp +[Registry] +; Delete all startup entries, so we don't have leftover values +Root: HKCU; Subkey: Software\Microsoft\Windows\CurrentVersion\Run; ValueType: none; ValueName: {#ExeName}; Flags: deletevalue noerror; +Root: HKLM; Subkey: Software\Microsoft\Windows\CurrentVersion\Run; ValueType: none; ValueName: {#ExeName}; Flags: deletevalue noerror; +Root: HKCU32; Subkey: Software\Microsoft\Windows\CurrentVersion\Run; ValueType: none; ValueName: {#ExeName}; Flags: deletevalue noerror; Check: IsWin64() +Root: HKLM32; Subkey: Software\Microsoft\Windows\CurrentVersion\Run; ValueType: none; ValueName: {#ExeName}; Flags: deletevalue noerror; Check: IsWin64() + +; delete filetype mappings +; HKEY_LOCAL_USER - for current user only +Root: HKCU; Subkey: Software\Classes\.greenshot; ValueType: none; ValueName: {#ExeName}; Flags: deletevalue noerror; +Root: HKCU; Subkey: Software\Classes\Greenshot; ValueType: none; ValueName: {#ExeName}; Flags: deletevalue noerror; +; HKEY_LOCAL_MACHINE - for all users when admin (with the noerror this doesn't matter) +Root: HKLM; Subkey: Software\Classes\.greenshot; ValueType: none; ValueName: {#ExeName}; Flags: deletevalue noerror; +Root: HKLM; Subkey: Software\Classes\Greenshot; ValueType: none; ValueName: {#ExeName}; Flags: deletevalue noerror; + +; Create the startup entries if requested to do so +; HKEY_LOCAL_USER - for current user only +Root: HKCU; Subkey: Software\Microsoft\Windows\CurrentVersion\Run; ValueType: string; ValueName: {#ExeName}; ValueData: {app}\{#ExeName}.exe; Permissions: users-modify; Flags: uninsdeletevalue noerror; Tasks: startup; Check: IsRegularUser +; HKEY_LOCAL_MACHINE - for all users when admin +Root: HKLM; Subkey: Software\Microsoft\Windows\CurrentVersion\Run; ValueType: string; ValueName: {#ExeName}; ValueData: {app}\{#ExeName}.exe; Permissions: admins-modify; Flags: uninsdeletevalue noerror; Tasks: startup; Check: not IsRegularUser + +; Register our own filetype for all users +; HKEY_LOCAL_USER - for current user only +Root: HKCU; Subkey: Software\Classes\.greenshot; ValueType: string; ValueName: ""; ValueData: "Greenshot"; Permissions: users-modify; Flags: uninsdeletevalue noerror; Check: IsRegularUser +Root: HKCU; Subkey: Software\Classes\Greenshot; ValueType: string; ValueName: ""; ValueData: "Greenshot File"; Permissions: users-modify; Flags: uninsdeletevalue noerror; Check: IsRegularUser +Root: HKCU; Subkey: Software\Classes\Greenshot\DefaultIcon; ValueType: string; ValueName: ""; ValueData: "{app}\Greenshot.EXE,0"; Permissions: users-modify; Flags: uninsdeletevalue noerror; Check: IsRegularUser +Root: HKCU; Subkey: Software\Classes\Greenshot\shell\open\command; ValueType: string; ValueName: ""; ValueData: """{app}\Greenshot.EXE"" --openfile ""%1"""; Permissions: users-modify; Flags: uninsdeletevalue noerror; Check: IsRegularUser +; HKEY_LOCAL_MACHINE - for all users when admin +Root: HKLM; Subkey: Software\Classes\.greenshot; ValueType: string; ValueName: ""; ValueData: "Greenshot"; Permissions: admins-modify; Flags: uninsdeletevalue noerror; Check: not IsRegularUser +Root: HKLM; Subkey: Software\Classes\Greenshot; ValueType: string; ValueName: ""; ValueData: "Greenshot File"; Permissions: admins-modify; Flags: uninsdeletevalue noerror; Check: not IsRegularUser +Root: HKLM; Subkey: Software\Classes\Greenshot\DefaultIcon; ValueType: string; ValueName: ""; ValueData: "{app}\Greenshot.EXE,0"; Permissions: admins-modify; Flags: uninsdeletevalue noerror; Check: not IsRegularUser +Root: HKLM; Subkey: Software\Classes\Greenshot\shell\open\command; ValueType: string; ValueName: ""; ValueData: """{app}\Greenshot.EXE"" --openfile ""%1"""; Permissions: admins-modify; Flags: uninsdeletevalue noerror; Check: not IsRegularUser + +[Icons] +Name: {group}\{#ExeName}; Filename: {app}\{#ExeName}.exe; WorkingDir: {app}; AppUserModelID: "{#ExeName}" +Name: {group}\Uninstall {#ExeName}; Filename: {uninstallexe}; WorkingDir: {app}; +Name: {group}\Readme.txt; Filename: {app}\readme.txt; WorkingDir: {app} +Name: {group}\License.txt; Filename: {app}\license.txt; WorkingDir: {app} + +[Languages] +Name: en; MessagesFile: compiler:Default.isl +Name: cn; MessagesFile: Languages\ChineseSimplified.isl +Name: de; MessagesFile: compiler:Languages\German.isl +Name: es; MessagesFile: compiler:Languages\Spanish.isl +Name: fi; MessagesFile: compiler:Languages\Finnish.isl +Name: fr; MessagesFile: compiler:Languages\French.isl +Name: nl; MessagesFile: compiler:Languages\Dutch.isl +Name: lt; MessagesFile: Languages\Latvian.isl +Name: nn; MessagesFile: Languages\NorwegianNynorsk.isl +Name: ru; MessagesFile: compiler:Languages\Russian.isl +Name: sr; MessagesFile: Languages\SerbianCyrillic.isl +Name: sv; MessagesFile: Languages\Swedish.isl +Name: uk; MessagesFile: compiler:Languages\Ukrainian.isl + +[Tasks] +Name: startup; Description: {cm:startup} + +[CustomMessages] + +de.confluence=Confluence Plug-in +de.default=Standard installation +en.office=Microsoft Office Plug-in +de.externalcommand=Externes Kommando Plug-in +de.imgur=Imgur Plug-in (Siehe: http://imgur.com) +de.jira=Jira Plug-in +de.language=Zusätzliche Sprachen +de.ocr=OCR Plug-in (benötigt Microsoft Office Document Imaging (MODI)) +de.optimize=Optimierung der Leistung, kann etwas dauern. +de.startgreenshot={#ExeName} starten +de.startup={#ExeName} starten wenn Windows hochfährt +de.win10=Windows 10 Plug-in + +en.confluence=Confluence plug-in +en.default=Default installation +en.office=Microsoft Office plug-in +en.externalcommand=Open with external command plug-in +en.imgur=Imgur plug-in (See: http://imgur.com) +en.jira=Jira plug-in +en.language=Additional languages +en.ocr=OCR plug-in (needs Microsoft Office Document Imaging (MODI)) +en.optimize=Optimizing performance, this may take a while. +en.startgreenshot=Start {#ExeName} +en.startup=Start {#ExeName} with Windows start +en.win10=Windows 10 plug-in + +es.confluence=Extensión para Confluence +es.default=${default} +es.externalcommand=Extensión para abrir con programas externos +es.imgur=Extensión para Imgur (Ver http://imgur.com) +es.jira=Extensión para Jira +es.language=Idiomas adicionales +es.ocr=Extensión para OCR (necesita Microsoft Office Document Imaging (MODI)) +es.optimize=Optimizando rendimiento; por favor, espera. +es.startgreenshot=Lanzar {#ExeName} +es.startup=Lanzar {#ExeName} al iniciarse Windows +es.win10=Extensión para Windows 10 + +fi.confluence=Confluence-liitännäinen +fi.default=${default} +fi.office=Microsoft-Office-liitännäinen +fi.externalcommand=Avaa Ulkoinen komento-liitännäisellä +fi.imgur=Imgur-liitännäinen (Katso: http://imgur.com) +fi.jira=Jira-liitännäinen +fi.language=Lisäkielet +fi.ocr=OCR-liitännäinen (Tarvitaan: Microsoft Office Document Imaging (MODI)) +fi.optimize=Optimoidaan suorituskykyä, tämä voi kestää hetken. +fi.startgreenshot=Käynnistä {#ExeName} +fi.startup=Käynnistä {#ExeName} Windowsin käynnistyessä +fi.win10=Windows 10-liitännäinen + +fr.confluence=Greffon Confluence +fr.default=${default} +fr.office=Greffon Microsoft Office +fr.externalcommand=Ouvrir avec le greffon de commande externe +fr.imgur=Greffon Imgur (Voir: http://imgur.com) +fr.jira=Greffon Jira +fr.language=Langues additionnelles +fr.ocr=Greffon OCR (nécessite Document Imaging de Microsoft Office [MODI]) +fr.optimize=Optimisation des performances, Ceci peut prendre un certain temps. +fr.startgreenshot=Démarrer {#ExeName} +fr.startup=Lancer {#ExeName} au démarrage de Windows +fr.win10=Greffon Windows 10 + +lt.confluence=Confluence spraudnis +lt.default=${default} +lt.office=Microsoft Office spraudnis +lt.externalcommand=Pielāgotu darbību spraudnis +lt.imgur=Imgur spraudnis (Vairāk šeit: http://imgur.com) +lt.jira=Jira spraudnis +lt.language=Papildus valodas +lt.ocr=OCR spraudnis (nepieciešams Microsoft Office Document Imaging (MODI)) +lt.optimize=Uzlaboju veikstpēju, tas prasīs kādu laiciņu. +lt.startgreenshot=Palaist {#ExeName} +lt.startup=Palaist {#ExeName} uzsākot darbus +lt.win10=Windows 10 spraudnis + +nl.confluence=Confluence plug-in +nl.default=Standaardinstallatie +nl.office=Microsoft Office plug-in +nl.externalcommand=Openen met extern commando plug-in +nl.imgur=Imgur plug-in (zie: http://imgur.com) +nl.jira=Jira plug-in +nl.language=Extra talen +nl.ocr=OCR plug-in (vereist Microsoft Office Document Imaging (MODI)) +nl.optimize=Prestaties verbeteren, even geduld. +nl.startgreenshot={#ExeName} starten +nl.startup={#ExeName} automatisch starten met Windows +nl.win10=Windows 10 plug-in + +nn.confluence=Confluence-tillegg +nn.default=Default installation +nn.office=Microsoft Office Tillegg +nn.externalcommand=Tillegg for å opne med ekstern kommando +nn.imgur=Imgur-tillegg (sjå http://imgur.com) +nn.jira=Jira-tillegg +nn.language=Andre språk +nn.ocr=OCR-tillegg (krev Microsoft Office Document Imaging (MODI)) +nn.optimize=Optimaliserar ytelse, dette kan ta litt tid... +nn.startgreenshot=Start {#ExeName} +nn.startup=Start {#ExeName} når Windows startar +nn.win10=Windows 10 Tillegg + +ru.confluence=Плагин Confluence +ru.default=${default} +ru.office=Плагин Microsoft Office +ru.externalcommand=Открыть с плагином с помощью внешней команды +ru.imgur=Плагин Imgur (смотрите https://imgur.com/) +ru.jira=Плагин Jira +ru.language=Дополнительные языки +ru.ocr=Плагин OCR (требуется Microsoft Office Document Imaging (MODI)) +ru.optimize=Идет оптимизация производительности, это может занять некоторое время. +ru.startgreenshot=Запустить {#ExeName} +ru.startup=Запускать {#ExeName} при старте Windows +ru.win10=Плагин Windows 10 + +sr.confluence=Прикључак за Конфлуенс +sr.default=${default} +sr.externalcommand=Отвори са прикључком за спољне наредбе +sr.imgur=Прикључак за Имиџер (http://imgur.com) +sr.jira=Прикључак за Џиру +sr.language=Додатни језици +sr.ocr=OCR прикључак (захтева Microsoft Office Document Imaging (MODI)) +sr.optimize=Оптимизујем перформансе… +sr.startgreenshot=Покрени Гриншот +sr.startup=Покрени програм са системом +sr.win10=Прикључак за Windows 10 + +sv.startup=Starta {#ExeName} med Windows +sv.startgreenshot=Starta {#ExeName} +sv.jira=Jira-insticksprogram +sv.confluence=Confluence-insticksprogram +sv.externalcommand=Öppna med externt kommando-insticksprogram +sv.ocr=OCR-insticksprogram (kräver Microsoft Office Document Imaging (MODI)) +sv.imgur=Imgur-insticksprogram (Se: http://imgur.com) +sv.language=Ytterligare språk +sv.optimize=Optimerar prestanda, detta kan ta en stund. +sv.win10=Windows 10-insticksprogram + +uk.confluence=Плагін Confluence +uk.default=${default} +uk.externalcommand=Плагін запуску зовнішньої команди +uk.imgur=Плагін Imgur (див.: http://imgur.com) +uk.jira=Плагін Jira +uk.language=Додаткові мови +uk.ocr=Плагін OCR (потребує Microsoft Office Document Imaging (MODI)) +uk.optimize=Оптимізація продуктивності, це може забрати час. +uk.startgreenshot=Запустити {#ExeName} +uk.startup=Запускати {#ExeName} під час запуску Windows +uk.win10=Плагін Windows 10 + +cn.confluence=Confluence插件 +cn.default=${default} +cn.externalcommand=使用外部命令打开插件 +cn.imgur=Imgur插件( (请访问: http://imgur.com)) +cn.jira=Jira插件 +cn.language=其它语言 +cn.ocr=OCR插件(需要Microsoft Office Document Imaging (MODI)的支持) +cn.optimize=正在优化性能,这可能需要一点时间。 +cn.startgreenshot=启动{#ExeName} +cn.startup=让{#ExeName}随Windows一起启动 +cn.win10=Windows 10插件 + +[Types] +Name: "default"; Description: "{cm:default}" +Name: "full"; Description: "{code:FullInstall}" +Name: "compact"; Description: "{code:CompactInstall}" +Name: "custom"; Description: "{code:CustomInstall}"; Flags: iscustom + +[Components] +Name: "greenshot"; Description: "Greenshot"; Types: default full compact custom; Flags: fixed +Name: "plugins\office"; Description: {cm:office}; Types: default full custom; Flags: disablenouninstallwarning +Name: "plugins\ocr"; Description: {cm:ocr}; Types: default full custom; Flags: disablenouninstallwarning +Name: "plugins\jira"; Description: {cm:jira}; Types: full custom; Flags: disablenouninstallwarning +Name: "plugins\imgur"; Description: {cm:imgur}; Types: default full custom; Flags: disablenouninstallwarning +Name: "plugins\confluence"; Description: {cm:confluence}; Types: full custom; Flags: disablenouninstallwarning +Name: "plugins\externalcommand"; Description: {cm:externalcommand}; Types: default full custom; Flags: disablenouninstallwarning +;Name: "plugins\networkimport"; Description: "Network Import Plugin"; Types: full +Name: "plugins\box"; Description: "Box Plugin"; Types: full custom; Flags: disablenouninstallwarning +Name: "plugins\dropbox"; Description: "Dropbox Plugin"; Types: full custom; Flags: disablenouninstallwarning +Name: "plugins\flickr"; Description: "Flickr Plugin"; Types: full custom; Flags: disablenouninstallwarning +Name: "plugins\picasa"; Description: "Picasa Plugin"; Types: full custom; Flags: disablenouninstallwarning +Name: "plugins\photobucket"; Description: "Photobucket Plugin"; Types: full custom; Flags: disablenouninstallwarning +Name: "plugins\win10"; Description: "Windows 10 Plugin"; Types: default full custom; Flags: disablenouninstallwarning; Check: IsWindows10OrNewer() +Name: "languages"; Description: {cm:language}; Types: full custom; Flags: disablenouninstallwarning +Name: "languages\arSY"; Description: "العربية"; Types: full custom; Flags: disablenouninstallwarning; Check: hasLanguageGroup('d') +Name: "languages\caCA"; Description: "Català"; Types: full custom; Flags: disablenouninstallwarning; Check: hasLanguageGroup('1') +Name: "languages\csCZ"; Description: "Ceština"; Types: full custom; Flags: disablenouninstallwarning; Check: hasLanguageGroup('1') +Name: "languages\daDK"; Description: "Dansk"; Types: full custom; Flags: disablenouninstallwarning; Check: hasLanguageGroup('1') +Name: "languages\dexfranconia"; Description: "Frängisch (Deutsch)"; Types: full custom; Flags: disablenouninstallwarning; Check: hasLanguageGroup('1') +Name: "languages\elGR"; Description: "ελληνικά"; Types: full custom; Flags: disablenouninstallwarning; Check: hasLanguageGroup('4') +Name: "languages\esES"; Description: "Español"; Types: full custom; Flags: disablenouninstallwarning; Check: hasLanguageGroup('1') +Name: "languages\etEE"; Description: "Eesti"; Types: full custom; Flags: disablenouninstallwarning; Check: hasLanguageGroup('2') +Name: "languages\faIR"; Description: "پارسی"; Types: full custom; Flags: disablenouninstallwarning; Check: hasLanguageGroup('d') +Name: "languages\fiFI"; Description: "Suomi"; Types: full custom; Flags: disablenouninstallwarning; Check: hasLanguageGroup('1') +Name: "languages\frFR"; Description: "Français"; Types: full custom; Flags: disablenouninstallwarning; Check: hasLanguageGroup('1') +Name: "languages\frQC"; Description: "Français - Québec"; Types: full custom; Flags: disablenouninstallwarning; Check: hasLanguageGroup('1') +Name: "languages\heIL"; Description: "עִבְרִית"; Types: full custom; Flags: disablenouninstallwarning; Check: hasLanguageGroup('c') +Name: "languages\huHU"; Description: "Magyar"; Types: full custom; Flags: disablenouninstallwarning; Check: hasLanguageGroup('2') +Name: "languages\idID"; Description: "Bahasa Indonesia"; Types: full custom; Flags: disablenouninstallwarning; Check: hasLanguageGroup('1') +Name: "languages\itIT"; Description: "Italiano"; Types: full custom; Flags: disablenouninstallwarning; Check: hasLanguageGroup('1') +Name: "languages\jaJP"; Description: "日本語"; Types: full custom; Flags: disablenouninstallwarning; Check: hasLanguageGroup('7') +Name: "languages\koKR"; Description: "한국어"; Types: full custom; Flags: disablenouninstallwarning; Check: hasLanguageGroup('8') +Name: "languages\kabDZ"; Description: "Taqbaylit"; Types: full custom; Flags: disablenouninstallwarning; Check: hasLanguageGroup('8') +Name: "languages\ltLT"; Description: "Lietuvių"; Types: full custom; Flags: disablenouninstallwarning; Check: hasLanguageGroup('3') +Name: "languages\lvLV"; Description: "Latviski"; Types: full custom; Flags: disablenouninstallwarning; Check: hasLanguageGroup('3') +Name: "languages\nnNO"; Description: "Nynorsk"; Types: full custom; Flags: disablenouninstallwarning; Check: hasLanguageGroup('1') +Name: "languages\plPL"; Description: "Polski"; Types: full custom; Flags: disablenouninstallwarning; Check: hasLanguageGroup('2') +Name: "languages\ptBR"; Description: "Português do Brasil"; Types: full custom; Flags: disablenouninstallwarning; Check: hasLanguageGroup('1') +Name: "languages\ptPT"; Description: "Português de Portugal"; Types: full custom; Flags: disablenouninstallwarning; Check: hasLanguageGroup('1') +Name: "languages\ruRU"; Description: "Pусский"; Types: full custom; Flags: disablenouninstallwarning; Check: hasLanguageGroup('5') +Name: "languages\roRO"; Description: "Română"; Types: full custom; Flags: disablenouninstallwarning; Check: hasLanguageGroup('2') +Name: "languages\skSK"; Description: "Slovenčina"; Types: full custom; Flags: disablenouninstallwarning; Check: hasLanguageGroup('2') +Name: "languages\slSI"; Description: "Slovenščina"; Types: full custom; Flags: disablenouninstallwarning; Check: hasLanguageGroup('2') +Name: "languages\srRS"; Description: "Српски"; Types: full custom; Flags: disablenouninstallwarning; Check: hasLanguageGroup('5') +Name: "languages\svSE"; Description: "Svenska"; Types: full custom; Flags: disablenouninstallwarning; Check: hasLanguageGroup('1') +Name: "languages\trTR"; Description: "Türk"; Types: full custom; Flags: disablenouninstallwarning; Check: hasLanguageGroup('6') +Name: "languages\ukUA"; Description: "Українська"; Types: full custom; Flags: disablenouninstallwarning; Check: hasLanguageGroup('5') +Name: "languages\viVN"; Description: "Việt"; Types: full custom; Flags: disablenouninstallwarning; Check: hasLanguageGroup('e') +Name: "languages\zhCN"; Description: "简体中文"; Types: full custom; Flags: disablenouninstallwarning; Check: hasLanguageGroup('a') +Name: "languages\zhTW"; Description: "繁體中文"; Types: full custom; Flags: disablenouninstallwarning; Check: hasLanguageGroup('9') +[Code] +// Do we have a regular user trying to install this? +function IsRegularUser(): Boolean; +begin + Result := not (IsAdmin or IsAdminInstallMode); +end; + +// The following code is used to select the installation path, this is localappdata if non poweruser +function DefDirRoot(Param: String): String; +begin + if IsRegularUser then + Result := ExpandConstant('{localappdata}') + else + Result := ExpandConstant('{pf}') +end; + + +function FullInstall(Param : String) : String; +begin + result := SetupMessage(msgFullInstallation); +end; + +function CustomInstall(Param : String) : String; +begin + result := SetupMessage(msgCustomInstallation); +end; + +function CompactInstall(Param : String) : String; +begin + result := SetupMessage(msgCompactInstallation); +end; +///////////////////////////////////////////////////////////////////// +// The following uninstall code was found at: +// http://stackoverflow.com/questions/2000296/innosetup-how-to-automatically-uninstall-previous-installed-version +// and than modified to work in a 32/64 bit environment +///////////////////////////////////////////////////////////////////// +function GetUninstallStrings(): array of String; +var + sUnInstPath: String; + sUnInstallString: String; + asUninstallStrings : array of String; + index : Integer; +begin + sUnInstPath := ExpandConstant('Software\Microsoft\Windows\CurrentVersion\Uninstall\{#emit SetupSetting("AppId")}_is1'); + sUnInstallString := ''; + index := 0; + + // Retrieve uninstall string from HKLM32 or HKCU32 + if RegQueryStringValue(HKLM32, sUnInstPath, 'UninstallString', sUnInstallString) then + begin + SetArrayLength(asUninstallStrings, index + 1); + asUninstallStrings[index] := sUnInstallString; + index := index +1; + end; + + if RegQueryStringValue(HKCU32, sUnInstPath, 'UninstallString', sUnInstallString) then + begin + SetArrayLength(asUninstallStrings, index + 1); + asUninstallStrings[index] := sUnInstallString; + index := index +1; + end; + + // Only for Windows with 64 bit support: Retrieve uninstall string from HKLM64 or HKCU64 + if IsWin64 then + begin + if RegQueryStringValue(HKLM64, sUnInstPath, 'UninstallString', sUnInstallString) then + begin + SetArrayLength(asUninstallStrings, index + 1); + asUninstallStrings[index] := sUnInstallString; + index := index +1; + end; + + if RegQueryStringValue(HKCU64, sUnInstPath, 'UninstallString', sUnInstallString) then + begin + SetArrayLength(asUninstallStrings, index + 1); + asUninstallStrings[index] := sUnInstallString; + index := index +1; + end; + end; + Result := asUninstallStrings; +end; + +///////////////////////////////////////////////////////////////////// +procedure UnInstallOldVersions(); +var + sUnInstallString: String; + index: Integer; + isUninstallMade: Boolean; + iResultCode : Integer; + asUninstallStrings : array of String; +begin + isUninstallMade := false; + asUninstallStrings := GetUninstallStrings(); + for index := 0 to (GetArrayLength(asUninstallStrings) -1) do + begin + sUnInstallString := RemoveQuotes(asUninstallStrings[index]); + if Exec(sUnInstallString, '/SILENT /NORESTART /SUPPRESSMSGBOXES','', SW_HIDE, ewWaitUntilTerminated, iResultCode) then + isUninstallMade := true; + end; + + // Wait a few seconds to prevent installation issues, otherwise files are removed in one process while the other tries to link to them + if (isUninstallMade) then + Sleep(2000); +end; + +///////////////////////////////////////////////////////////////////// +procedure CurStepChanged(CurStep: TSetupStep); +begin + if (CurStep=ssInstall) then + begin + UnInstallOldVersions(); + end; +end; +///////////////////////////////////////////////////////////////////// +// End of unstall code +///////////////////////////////////////////////////////////////////// + +// Build a list of greenshot parameters from the supplied installer parameters +function GetParamsForGS(argument: String): String; +var + i: Integer; + parametersString: String; + currentParameter: String; + foundStart: Boolean; + foundNoRun: Boolean; + foundLanguage: Boolean; +begin + foundNoRun := false; + foundLanguage := false; + foundStart := false; + for i:= 0 to ParamCount() do begin + currentParameter := ParamStr(i); + + // check if norun is supplied + if Lowercase(currentParameter) = '/norun' then begin + foundNoRun := true; + continue; + end; + + if foundStart then begin + parametersString := parametersString + ' ' + currentParameter; + foundStart := false; + end + else begin + if Lowercase(currentParameter) = '/language' then begin + foundStart := true; + foundLanguage := true; + parametersString := parametersString + ' ' + currentParameter; + end; + end; + end; + if not foundLanguage then begin + parametersString := parametersString + ' /language ' + ExpandConstant('{language}'); + end; + if foundNoRun then begin + parametersString := parametersString + ' /norun'; + end; + // For debugging comment out the following + //MsgBox(parametersString, mbInformation, MB_OK); + + Result := parametersString; +end; + +// Check if language group is installed +function hasLanguageGroup(argument: String): Boolean; +var + keyValue: String; + returnValue: Boolean; +begin + returnValue := true; + if (RegQueryStringValue( HKLM, 'SYSTEM\CurrentControlSet\Control\Nls\Language Groups', argument, keyValue)) then begin + if Length(keyValue) = 0 then begin + returnValue := false; + end; + end; + Result := returnValue; +end; + +function hasDotNet() : boolean; +begin + Result := netfxspversion(NetFx4x, '') >= 71; +end; + +// Initialize the setup +function InitializeSetup(): Boolean; +begin + // Check for .NET and install 4.7.1 if we don't have it + if not hasDotNet() then + begin + // Enhance installer, if needed, otherwise .NET installations won't work + msi20('2.0'); + msi31('3.0'); + + //install .net 4.7.1 + dotnetfx47(71); + end; + Result := true; +end; + +function IsWindowsVersionOrNewer(Major, Minor: Integer): Boolean; +var + Version: TWindowsVersion; +begin + GetWindowsVersionEx(Version); + Result := + (Version.Major > Major) or + ((Version.Major = Major) and (Version.Minor >= Minor)); +end; + +function IsWindows10OrNewer: Boolean; +begin + Result := IsWindowsVersionOrNewer(10, 0); +end; + +[Run] +Filename: "{app}\{#ExeName}.exe"; Description: "{cm:startgreenshot}"; Parameters: "{code:GetParamsForGS}"; WorkingDir: "{app}"; Flags: nowait postinstall runasoriginaluser +Filename: "http://getgreenshot.org/thank-you/?language={language}&version={#Version}"; Flags: shellexec runasoriginaluser + +[InstallDelete] +Name: {app}; Type: dirifempty; From 53c93e25b576b7c3397ae981e7f8d3206c79b794 Mon Sep 17 00:00:00 2001 From: jklingen Date: Sun, 3 May 2020 18:27:33 +0200 Subject: [PATCH 27/29] Installer: Backport: Fix Prefix for German Translation Message --- Greenshot/releases/innosetup/setup.iss | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Greenshot/releases/innosetup/setup.iss b/Greenshot/releases/innosetup/setup.iss index 0c42af120..f15a578aa 100644 --- a/Greenshot/releases/innosetup/setup.iss +++ b/Greenshot/releases/innosetup/setup.iss @@ -1,4 +1,4 @@ -#define ExeName "Greenshot" +#define ExeName "Greenshot" #define Version GetEnv('BuildVersionSimple') #define FileVersion GetEnv('AssemblyInformationalVersion') #define BaseDir "..\..\.." @@ -215,7 +215,7 @@ Name: startup; Description: {cm:startup} de.confluence=Confluence Plug-in de.default=Standard installation -en.office=Microsoft Office Plug-in +de.office=Microsoft Office Plug-in de.externalcommand=Externes Kommando Plug-in de.imgur=Imgur Plug-in (Siehe: http://imgur.com) de.jira=Jira Plug-in From 2a5ae1557e132b0d332bf7bdb094d462938059ca Mon Sep 17 00:00:00 2001 From: MXI Date: Mon, 11 May 2020 00:03:53 +0300 Subject: [PATCH 28/29] Converters fix for release 1.3 (#204) * Converters fix for release 1.3 --- .../Drawing/Fields/Binding/DecimalFloatConverter.cs | 8 ++++---- .../Drawing/Fields/Binding/DecimalIntConverter.cs | 10 +++++----- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/Greenshot/Drawing/Fields/Binding/DecimalFloatConverter.cs b/Greenshot/Drawing/Fields/Binding/DecimalFloatConverter.cs index b4a4283e4..70581bc10 100644 --- a/Greenshot/Drawing/Fields/Binding/DecimalFloatConverter.cs +++ b/Greenshot/Drawing/Fields/Binding/DecimalFloatConverter.cs @@ -35,13 +35,13 @@ namespace Greenshot.Drawing.Fields.Binding { } protected override float convert(decimal o) { - return Convert.ToInt16(o); + return Convert.ToSingle(o); } public static DecimalFloatConverter GetInstance() - { - return _uniqueInstance ??= new DecimalFloatConverter(); - } + { + return _uniqueInstance ??= new DecimalFloatConverter(); + } } } diff --git a/Greenshot/Drawing/Fields/Binding/DecimalIntConverter.cs b/Greenshot/Drawing/Fields/Binding/DecimalIntConverter.cs index d71d27528..693e532dd 100644 --- a/Greenshot/Drawing/Fields/Binding/DecimalIntConverter.cs +++ b/Greenshot/Drawing/Fields/Binding/DecimalIntConverter.cs @@ -1,4 +1,4 @@ -/* +/* * Greenshot - a free and open source screenshot tool * Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom * @@ -35,13 +35,13 @@ namespace Greenshot.Drawing.Fields.Binding { } protected override int convert(decimal o) { - return Convert.ToInt16(o); + return Convert.ToInt32(o); } public static DecimalIntConverter GetInstance() - { - return _uniqueInstance ??= new DecimalIntConverter(); - } + { + return _uniqueInstance ??= new DecimalIntConverter(); + } } } From 5db1f5564b660734b2fe18dee070e23be7c8d7da Mon Sep 17 00:00:00 2001 From: Robin Krom Date: Sun, 10 May 2020 23:04:31 +0200 Subject: [PATCH 29/29] Fixed the issue that someone could have a windows 10 version which is older than what we can support. (#207) Also upgrade Inno Setup and SVG --- Directory.Build.props | 2 +- Greenshot/Greenshot.csproj | 2 +- GreenshotPlugin/Core/WindowsVersion.cs | 7 ++++++- GreenshotPlugin/GreenshotPlugin.csproj | 2 +- GreenshotWin10Plugin/Win10Plugin.cs | 8 ++++++-- 5 files changed, 15 insertions(+), 6 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index 37b63badc..1415521cf 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -56,7 +56,7 @@ - + all runtime; build; native; contentfiles; analyzers diff --git a/Greenshot/Greenshot.csproj b/Greenshot/Greenshot.csproj index 1b25c59a2..080651ceb 100644 --- a/Greenshot/Greenshot.csproj +++ b/Greenshot/Greenshot.csproj @@ -17,7 +17,7 @@ - + diff --git a/GreenshotPlugin/Core/WindowsVersion.cs b/GreenshotPlugin/Core/WindowsVersion.cs index 6dd4faa08..cd263d518 100644 --- a/GreenshotPlugin/Core/WindowsVersion.cs +++ b/GreenshotPlugin/Core/WindowsVersion.cs @@ -41,7 +41,7 @@ namespace GreenshotPlugin.Core public static bool IsWindows7OrLater { get; } = WinVersion.Major == 6 && WinVersion.Minor >= 1 || WinVersion.Major > 6; public static bool IsWindows7OrLower { get; } = WinVersionTotal <= 6.1; - + /// /// Test if the current OS is Windows 8.0 /// @@ -102,6 +102,11 @@ namespace GreenshotPlugin.Core /// true if we are running on Windows XP or later public static bool IsWindowsXpOrLater { get; } = WinVersion.Major >= 5 || WinVersion.Major == 5 && WinVersion.Minor >= 1; + /// + /// Returns the windows build number + /// + public static int BuildVersion => WinVersion.Build; + /// /// Test if the current Windows version is 10 and the build number or later /// See the build numbers here diff --git a/GreenshotPlugin/GreenshotPlugin.csproj b/GreenshotPlugin/GreenshotPlugin.csproj index 1a9598bc0..574e0fafc 100644 --- a/GreenshotPlugin/GreenshotPlugin.csproj +++ b/GreenshotPlugin/GreenshotPlugin.csproj @@ -15,7 +15,7 @@ - + diff --git a/GreenshotWin10Plugin/Win10Plugin.cs b/GreenshotWin10Plugin/Win10Plugin.cs index 1e0519071..53344f391 100644 --- a/GreenshotWin10Plugin/Win10Plugin.cs +++ b/GreenshotWin10Plugin/Win10Plugin.cs @@ -35,7 +35,9 @@ namespace GreenshotWin10Plugin [Plugin("Win10", false)] public sealed class Win10Plugin : IGreenshotPlugin { - public void Dispose() + private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(typeof(Win10Plugin)); + + public void Dispose() { Dispose(true); } @@ -58,8 +60,10 @@ namespace GreenshotWin10Plugin /// true if plugin is initialized, false if not (doesn't show) public bool Initialize() { - if (!WindowsVersion.IsWindows10OrLater) + // Here we check if the build version of Windows is actually what we support + if (!WindowsVersion.IsWindows10BuildOrLater(17763)) { + Log.WarnFormat("No support for Windows build {0}", WindowsVersion.BuildVersion); return false; }