From 8d79b13c2a58f672c9a8d8cd9781a8b6a4c10f2e Mon Sep 17 00:00:00 2001 From: Johan Althoff Date: Sat, 16 Apr 2022 09:39:07 +0200 Subject: [PATCH] fix: make DPI aware --- .gitignore | 7 +- README.md | 1 + screenzap/App.config | 35 +++--- screenzap/ClipboardMonitor.cs | 124 ++++++++++++++++++++ screenzap/ImageOverlayForm.cs | 132 +++++++++++++++++++++ screenzap/KeyboardHook.cs | 6 +- screenzap/Overlay.cs | 4 - screenzap/Program.cs | 7 +- screenzap/Screenzap.Designer.cs | 37 +++--- screenzap/Screenzap.cs | 50 ++++---- screenzap/SelectionRect.cs | 191 +++++++++++++++++++++++++++++++ screenzap/ShortcutEditor.cs | 7 -- screenzap/app.manifest | 79 +++++++++++++ screenzap/screenzap.csproj | 37 ++++++ screenzap/screenzap_trayicon.ico | Bin 0 -> 32375 bytes 15 files changed, 643 insertions(+), 74 deletions(-) create mode 100644 README.md create mode 100644 screenzap/ClipboardMonitor.cs create mode 100644 screenzap/ImageOverlayForm.cs create mode 100644 screenzap/SelectionRect.cs create mode 100644 screenzap/app.manifest create mode 100644 screenzap/screenzap_trayicon.ico diff --git a/.gitignore b/.gitignore index cade338..9855774 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ # This .gitignore file was automatically created by Microsoft(R) Visual Studio. ################################################################################ -/.vs/screenzap/v15 -/packages/Nito.AsyncEx.3.0.1 -/screenzap +/.vs +/packages +/screenzap/obj +/screenzap/bin diff --git a/README.md b/README.md new file mode 100644 index 0000000..404873e --- /dev/null +++ b/README.md @@ -0,0 +1 @@ +screenzap \ No newline at end of file diff --git a/screenzap/App.config b/screenzap/App.config index 2f69e2f..b56f9e3 100644 --- a/screenzap/App.config +++ b/screenzap/App.config @@ -1,18 +1,21 @@  - - -
- - - - - - - - - Ctrl+Alt+Shift+4 - - - - \ No newline at end of file + + +
+ + + + + + + + + + + + Ctrl+Alt+Shift+4 + + + + diff --git a/screenzap/ClipboardMonitor.cs b/screenzap/ClipboardMonitor.cs new file mode 100644 index 0000000..cd51958 --- /dev/null +++ b/screenzap/ClipboardMonitor.cs @@ -0,0 +1,124 @@ +using System; +using System.Drawing; +using System.Runtime.InteropServices; +using System.Windows.Forms; + +namespace screenzap +{ + public sealed class ClipboardMonitor : IDisposable + { + /// + /// Places the given window in the system-maintained clipboard format listener list. + /// + [DllImport("user32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + static extern bool AddClipboardFormatListener(IntPtr hwnd); + + /// + /// Removes the given window from the system-maintained clipboard format listener list. + /// + [DllImport("user32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + static extern bool RemoveClipboardFormatListener(IntPtr hwnd); + + /// + /// Sent when the contents of the clipboard have changed. + /// + private const int WM_CLIPBOARDUPDATE = 0x031D; + + private class Window : NativeWindow, IDisposable + { + public Window() + { + // create the handle for the window. + this.CreateHandle(new CreateParams()); + AddClipboardFormatListener(this.Handle); + } + + /// + /// Overridden to get the notifications. + /// + /// + protected override void WndProc(ref Message m) + { + base.WndProc(ref m); + + if (m.Msg == WM_CLIPBOARDUPDATE) + { + IDataObject iData = Clipboard.GetDataObject(); // Clipboard's data. + + /* Depending on the clipboard's current data format we can process the data differently. + * Feel free to add more checks if you want to process more formats. */ + if (iData.GetDataPresent(DataFormats.Text)) + { + string text = (string)iData.GetData(DataFormats.Text); + OnUpdateText(this, text); + } + else if (iData.GetDataPresent(DataFormats.Bitmap)) + { + Bitmap image = (Bitmap)iData.GetData(DataFormats.Bitmap); + OnUpdateImage(this, image); + } + } + } + public event EventHandler OnUpdateText; + public event EventHandler OnUpdateImage; + public void Dispose() + { + RemoveClipboardFormatListener(this.Handle); + this.DestroyHandle(); + } + } + + private Window _window; + public ClipboardMonitor() + { + _window = new Window(); + _window.OnUpdateImage += _window_OnUpdateImage; + _window.OnUpdateText += _window_OnUpdateText; + } + + public bool isListening = false; + + private void _window_OnUpdateText(object sender, string e) + { + if (isListening) + OnUpdateText?.Invoke(sender, e); + } + + private void _window_OnUpdateImage(object sender, Bitmap e) + { + if (isListening) + OnUpdateImage?.Invoke(sender, e); + } + + public event EventHandler OnUpdateText; + public event EventHandler OnUpdateImage; + #region IDisposable Support + private bool disposedValue = false; // To detect redundant calls + + void Dispose(bool disposing) + { + if (!disposedValue) + { + if (disposing) + { + // TODO: dispose managed state (managed objects). + _window.Dispose(); + } + + // TODO: free unmanaged resources (unmanaged objects) and override a finalizer below. + // TODO: set large fields to null. + + disposedValue = true; + } + } + + public void Dispose() + { + // Do not change this code. Put cleanup code in Dispose(bool disposing) above. + Dispose(true); + } + #endregion + } +} \ No newline at end of file diff --git a/screenzap/ImageOverlayForm.cs b/screenzap/ImageOverlayForm.cs new file mode 100644 index 0000000..1f5bb44 --- /dev/null +++ b/screenzap/ImageOverlayForm.cs @@ -0,0 +1,132 @@ +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Forms; + +namespace screenzap +{ + class ImageOverlayForm : Form + { + private Point mouseHit; + private Point formPosition; + private bool isMouseDown; + private bool isCtrlDown; + private decimal zoomLevel = 1m; + private decimal[] zoomLevels = { 0.1m, 0.25m, 1 / 3m, 0.5m, 2 / 3m, 0.75m, 1.0m, 1.25m, 4 / 3m, 1.5m, 5 / 3m, 2m, 3m, 4m, 6m, 8m, 10m, 15m, 20m, 30m, 40m, }; + + public ImageOverlayForm() + { + this.Cursor = Cursors.Cross; + this.TopMost = true; + //this.ShowInTaskbar = false; + this.FormBorderStyle = FormBorderStyle.None; + this.BackColor = Color.Gray; + this.TransparencyKey = Color.Red; + this.Opacity = 0.5; + this.Width = Screen.PrimaryScreen.WorkingArea.Width; + this.Height = Screen.PrimaryScreen.WorkingArea.Height; + //this.WindowState = FormWindowState.Maximized; + this.DoubleBuffered = true; + this.BackgroundImageLayout = ImageLayout.Stretch; + } + + public void setImage(Bitmap image) + { + this.BackgroundImage = image; + this.Width = image.Width; + this.Height = image.Height; + } + + protected override void OnKeyDown(KeyEventArgs e) + { + this.isCtrlDown = e.Control; + + base.OnKeyDown(e); + } + + protected override void OnKeyPress(KeyPressEventArgs e) + { + if (e.KeyChar == (char)Keys.Escape) + this.Hide(); + + base.OnKeyPress(e); + } + protected override void OnKeyUp(KeyEventArgs e) + { + this.isCtrlDown = e.Control; + + base.OnKeyUp(e); + } + + protected override void OnMouseDown(MouseEventArgs e) + { + isMouseDown = true; + mouseHit = e.Location; + formPosition = ((Form)TopLevelControl).Location; + + base.OnMouseDown(e); + } + + protected override void OnMouseUp(MouseEventArgs e) + { + isMouseDown = false; + + base.OnMouseUp(e); + } + + protected override void OnMouseMove(MouseEventArgs e) + { + if (isMouseDown) + { + int dx = e.Location.X - mouseHit.X; + int dy = e.Location.Y - mouseHit.Y; + Point newLocation = new Point(formPosition.X + dx, formPosition.Y + dy); + ((Form)TopLevelControl).Location = newLocation; + formPosition = newLocation; + } + + base.OnMouseMove(e); + } + + protected override void OnMouseWheel(MouseEventArgs e) + { + try + { + if (this.isCtrlDown) + { + if (e.Delta > 0) + setZoom(zoomLevel + 0.1m); + else + setZoom(zoomLevel - 0.1m); + } + else + { + if (e.Delta > 0) // pos, zoom in + setZoom(zoomLevels.Where(x => x > this.zoomLevel).First()); + else + setZoom(zoomLevels.Where(x => x < this.zoomLevel).Last()); + } + } + catch (Exception zoomException) + { + Console.WriteLine("zoomException"); + } + + base.OnMouseWheel(e); + } + + private void setZoom(decimal zoomLevel) + { + Point oldCenter = new Point(this.Left + this.Width / 2, this.Top + this.Height / 2); + this.Width = (int)(this.BackgroundImage.Width * zoomLevel); + this.Height = (int)(this.BackgroundImage.Height * zoomLevel); + this.Left = oldCenter.X - this.Width / 2; + this.Top = oldCenter.Y - this.Height / 2; + this.zoomLevel = zoomLevel; + } + + } +} diff --git a/screenzap/KeyboardHook.cs b/screenzap/KeyboardHook.cs index 3543f21..7cd00e5 100644 --- a/screenzap/KeyboardHook.cs +++ b/screenzap/KeyboardHook.cs @@ -73,15 +73,15 @@ public KeyboardHook() /// /// Registers a hot key in the system. /// - /// The modifiers that are associated with the hot key. + /// The modifiers that are associated with the hot key. /// The key itself that is associated with the hot key. - public void RegisterHotKey(ModifierKeys modifier, Keys key) + public void RegisterHotKey(ModifierKeys modifiers, Keys key) { // increment the counter. _currentId = _currentId + 1; // register the hot key. - if (!RegisterHotKey(_window.Handle, _currentId, (uint)modifier, (uint)key)) + if (!RegisterHotKey(_window.Handle, _currentId, (uint)modifiers, (uint)key)) throw new InvalidOperationException("Couldn’t register the hot key."); } diff --git a/screenzap/Overlay.cs b/screenzap/Overlay.cs index cbdb0d5..4deed49 100644 --- a/screenzap/Overlay.cs +++ b/screenzap/Overlay.cs @@ -1,9 +1,5 @@ using System; -using System.Collections.Generic; using System.Drawing; -using System.Linq; -using System.Text; -using System.Threading.Tasks; using System.Windows.Forms; namespace screenzap diff --git a/screenzap/Program.cs b/screenzap/Program.cs index eaf3ebc..f5455df 100644 --- a/screenzap/Program.cs +++ b/screenzap/Program.cs @@ -1,19 +1,20 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; using System.Windows.Forms; namespace screenzap { static class Program { + [System.Runtime.InteropServices.DllImport("user32.dll")] + public static extern bool SetProcessDPIAware(); + /// /// The main entry point for the application. /// [STAThread] static void Main() { + SetProcessDPIAware(); Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); Application.Run(new Screenzap()); diff --git a/screenzap/Screenzap.Designer.cs b/screenzap/Screenzap.Designer.cs index f07fc1c..8a36727 100644 --- a/screenzap/Screenzap.Designer.cs +++ b/screenzap/Screenzap.Designer.cs @@ -34,18 +34,19 @@ private void InitializeComponent() this.notifyIcon1 = new System.Windows.Forms.NotifyIcon(this.components); this.contextMenuStrip1 = new System.Windows.Forms.ContextMenuStrip(this.components); this.startWhenLoggedInToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.quitToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.setKeyboardShortcutToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.toolStripSeparator1 = new System.Windows.Forms.ToolStripSeparator(); + this.quitToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.contextMenuStrip1.SuspendLayout(); this.SuspendLayout(); // // label1 // this.label1.AutoSize = true; - this.label1.Location = new System.Drawing.Point(13, 22); + this.label1.Location = new System.Drawing.Point(17, 27); + this.label1.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.label1.Name = "label1"; - this.label1.Size = new System.Drawing.Size(35, 13); + this.label1.Size = new System.Drawing.Size(44, 16); this.label1.TabIndex = 0; this.label1.Text = "label1"; // @@ -58,47 +59,49 @@ private void InitializeComponent() // // contextMenuStrip1 // + this.contextMenuStrip1.ImageScalingSize = new System.Drawing.Size(20, 20); this.contextMenuStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { this.startWhenLoggedInToolStripMenuItem, this.setKeyboardShortcutToolStripMenuItem, this.toolStripSeparator1, this.quitToolStripMenuItem}); this.contextMenuStrip1.Name = "contextMenuStrip1"; - this.contextMenuStrip1.Size = new System.Drawing.Size(199, 98); + this.contextMenuStrip1.Size = new System.Drawing.Size(232, 82); // // startWhenLoggedInToolStripMenuItem // this.startWhenLoggedInToolStripMenuItem.CheckOnClick = true; this.startWhenLoggedInToolStripMenuItem.Name = "startWhenLoggedInToolStripMenuItem"; - this.startWhenLoggedInToolStripMenuItem.Size = new System.Drawing.Size(198, 22); + this.startWhenLoggedInToolStripMenuItem.Size = new System.Drawing.Size(231, 24); this.startWhenLoggedInToolStripMenuItem.Text = "Start when logged in"; this.startWhenLoggedInToolStripMenuItem.CheckStateChanged += new System.EventHandler(this.startWhenLoggedInToolStripMenuItem_CheckStateChanged); // - // quitToolStripMenuItem - // - this.quitToolStripMenuItem.Name = "quitToolStripMenuItem"; - this.quitToolStripMenuItem.Size = new System.Drawing.Size(198, 22); - this.quitToolStripMenuItem.Text = "&Quit"; - this.quitToolStripMenuItem.Click += new System.EventHandler(this.quitToolStripMenuItem_Click); - // // setKeyboardShortcutToolStripMenuItem // this.setKeyboardShortcutToolStripMenuItem.Name = "setKeyboardShortcutToolStripMenuItem"; - this.setKeyboardShortcutToolStripMenuItem.Size = new System.Drawing.Size(198, 22); + this.setKeyboardShortcutToolStripMenuItem.Size = new System.Drawing.Size(231, 24); this.setKeyboardShortcutToolStripMenuItem.Text = "Set &keyboard shortcut..."; this.setKeyboardShortcutToolStripMenuItem.Click += new System.EventHandler(this.setKeyboardShortcutToolStripMenuItem_Click); // // toolStripSeparator1 // this.toolStripSeparator1.Name = "toolStripSeparator1"; - this.toolStripSeparator1.Size = new System.Drawing.Size(195, 6); + this.toolStripSeparator1.Size = new System.Drawing.Size(228, 6); + // + // quitToolStripMenuItem + // + this.quitToolStripMenuItem.Name = "quitToolStripMenuItem"; + this.quitToolStripMenuItem.Size = new System.Drawing.Size(231, 24); + this.quitToolStripMenuItem.Text = "&Quit"; + this.quitToolStripMenuItem.Click += new System.EventHandler(this.quitToolStripMenuItem_Click); // // Screenzap // - this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); - this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.ClientSize = new System.Drawing.Size(328, 229); + this.AutoScaleDimensions = new System.Drawing.SizeF(120F, 120F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi; + this.ClientSize = new System.Drawing.Size(437, 282); this.Controls.Add(this.label1); + this.Margin = new System.Windows.Forms.Padding(4, 4, 4, 4); this.Name = "Screenzap"; this.Text = "Form1"; this.contextMenuStrip1.ResumeLayout(false); diff --git a/screenzap/Screenzap.cs b/screenzap/Screenzap.cs index dd2fa55..e0e21c9 100644 --- a/screenzap/Screenzap.cs +++ b/screenzap/Screenzap.cs @@ -1,15 +1,9 @@ using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Data; using System.Drawing; using System.Drawing.Imaging; using System.IO; -using System.Linq; using System.Media; using System.Reflection; -using System.Text; -using System.Threading.Tasks; using System.Windows.Forms; namespace screenzap @@ -21,6 +15,7 @@ public partial class Screenzap : Form string assemblyLocation = Assembly.GetExecutingAssembly().Location; // Or the EXE path. string sndPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) + Path.DirectorySeparatorChar + "zap.wav"; KeyCombo currentCombo; + bool isCapturing = false; public Screenzap() { @@ -31,7 +26,7 @@ public Screenzap() this.notifyIcon1.ShowBalloonTip(2000, "Screenzap is running!", $"Press {currentCombo.ToString()} to take a screenshot.", ToolTipIcon.Info); - hook.KeyPressed += new EventHandler(doCapture); + hook.KeyPressed += new EventHandler(DoCapture); try { hook.RegisterHotKey(currentCombo.getModifierKeys(), currentCombo.Key); @@ -52,33 +47,46 @@ protected override void OnLoad(EventArgs e) void updateTooltips(KeyCombo keyCombo) { - this.notifyIcon1.Text = $"Screenzap is running! \n\nPress {keyCombo.ToString()}."; + this.notifyIcon1.Text = $"Screenzap is running! \n\nPress {keyCombo}."; } - void doCapture(object sender, KeyPressedEventArgs e) + void DoCapture(object sender, KeyPressedEventArgs e) { - Overlay ovl = new Overlay(); - var captureRect = ovl.CaptureRect(); - if (captureRect == Rectangle.Empty) - return; + if (this.isCapturing) return; + this.isCapturing = true; + try + { + Overlay ovl = new Overlay(); + var captureRect = ovl.CaptureRect(); - var bmpScreenshot = new Bitmap(captureRect.Width, captureRect.Height, PixelFormat.Format32bppArgb); - var gfxScreenshot = Graphics.FromImage(bmpScreenshot); + if (captureRect.Width <= 0 || captureRect.Height <= 0) + { + Console.WriteLine($"Invalid capture area {captureRect.Width}x{captureRect.Height}"); + return; + } - gfxScreenshot.CopyFromScreen(captureRect.Location, new Point(0, 0), captureRect.Size, CopyPixelOperation.SourceCopy); - var rslt = bmpScreenshot.PhysicalDimension.ToString(); + Bitmap bmpScreenshot = new Bitmap(captureRect.Width, captureRect.Height, PixelFormat.Format32bppArgb); + Graphics gfxScreenshot = Graphics.FromImage(bmpScreenshot); + gfxScreenshot.CopyFromScreen(captureRect.Location, new Point(0, 0), captureRect.Size, CopyPixelOperation.SourceCopy); + Clipboard.SetImage(bmpScreenshot); - Clipboard.SetImage(bmpScreenshot); + SoundPlayer audio = new SoundPlayer(Properties.Resources.zap); + audio.Play(); + } + catch (Exception ex) + { + Console.Write(ex.ToString()); + } - SoundPlayer audio = new SoundPlayer(screenzap.Properties.Resources.zap); - audio.Play(); + this.isCapturing = false; } private void startWhenLoggedInToolStripMenuItem_CheckStateChanged(object sender, EventArgs e) { if (this.startWhenLoggedInToolStripMenuItem.Checked) Util.SetAutoStart(autostartAppName, assemblyLocation); - else { + else + { if (Util.IsAutoStartEnabled(autostartAppName, assemblyLocation)) Util.UnSetAutoStart(autostartAppName); } diff --git a/screenzap/SelectionRect.cs b/screenzap/SelectionRect.cs new file mode 100644 index 0000000..fa4b731 --- /dev/null +++ b/screenzap/SelectionRect.cs @@ -0,0 +1,191 @@ +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Forms; + +namespace screenzap +{ + class SelectionRectForm : Form + { + public SelectionRectForm() + { + this.Cursor = Cursors.Cross; + this.TopMost = true; + this.ShowInTaskbar = false; + this.FormBorderStyle = FormBorderStyle.None; + this.BackColor = Color.Gray; + this.TransparencyKey = Color.Red; + this.Opacity = 0.5; + this.Width = Screen.PrimaryScreen.WorkingArea.Width; + this.Height = Screen.PrimaryScreen.WorkingArea.Height; + this.WindowState = FormWindowState.Maximized; + this.DoubleBuffered = true; + } + } + class SelectionRect + { + private Form form; + private Point start; + private Point end; + + private int left + { + get { return Math.Min(start.X, end.X); } + } + + private int top + { + get { return Math.Min(start.Y, end.Y); } + } + + private int width + { + get { return Math.Abs(end.X - start.X); } + } + + private int height + { + get { return Math.Abs(end.Y - start.Y); } + } + + private Rectangle captureArea + { + get + { + var captureAreaLeft = Math.Max(left, 0); + var captureAreaTop = Math.Max(top, 0); + var captureAreaWidth = Math.Min(width, Screen.PrimaryScreen.Bounds.Width - left); + var captureAreaHeight = Math.Min(height, Screen.PrimaryScreen.Bounds.Height - top); + return new Rectangle(captureAreaLeft, captureAreaTop, captureAreaWidth, captureAreaHeight); + } + } + + private bool doPan = false; + private bool doSquare = false; + private bool doCenter = false; + + public Rectangle CaptureRect() + { + var rslt = form.ShowDialog(); + if (rslt == DialogResult.OK) + return captureArea; + return Rectangle.Empty; + } + + public SelectionRect() + { + form = new SelectionRectForm(); + form.MouseDown += Form_MouseDown; + form.MouseMove += Form_MouseMove; + form.MouseUp += Form_MouseUp; + form.KeyDown += Form_KeyDown; + form.KeyUp += Form_KeyUp; + form.Paint += Form_Paint; + } + + private void drawRect(PaintEventArgs e, Brush brush, Rectangle drawArea) + { + e.Graphics.FillRectangle(brush, drawArea.Left, drawArea.Top, drawArea.Width, drawArea.Height); + e.Graphics.DrawRectangle(Pens.White, drawArea.Left - 1, drawArea.Top - 1, drawArea.Width + 2, drawArea.Height + 2); + var coords = $"{drawArea.Width} x {drawArea.Height}"; + var textPos = new PointF(drawArea.Right - e.Graphics.MeasureString(coords, SystemFonts.CaptionFont).Width, drawArea.Bottom + 4); + e.Graphics.DrawString(coords, SystemFonts.CaptionFont, Brushes.White, textPos); + } + + private void Form_Paint(object sender, PaintEventArgs e) + { + drawRect(e, Brushes.Red, captureArea); + } + + private void Form_KeyDown(object sender, KeyEventArgs e) + { + if (e.Modifiers.HasFlag(Keys.Shift)) + doSquare = true; + + if (e.KeyCode == Keys.Space) + { + doPan = true; + if (Cursor.Position != end) + Cursor.Position = end; + } + + if (e.Modifiers.HasFlag(Keys.Alt)) + { + doCenter = true; + if (Cursor.Position != end) + Cursor.Position = end; + } + + if (e.KeyCode == Keys.Escape) + { + form.DialogResult = DialogResult.Cancel; + form.Close(); + } + } + + private void Form_KeyUp(object sender, KeyEventArgs e) + { + if (e.KeyCode == Keys.Space) + doPan = false; + + if (!e.Modifiers.HasFlag(Keys.Alt)) + doCenter = false; + + if (!e.Modifiers.HasFlag(Keys.Shift)) + doSquare = false; + } + + private Point getDiff(Point oldEnd, Point end) + { + return new Point(oldEnd.X - end.X, oldEnd.Y - end.Y); + } + + private void Form_MouseMove(object sender, MouseEventArgs e) + { + if (e.Button.HasFlag(MouseButtons.Left)) + { + var oldEnd = end; + end = new Point(e.X, e.Y); + var diff = getDiff(oldEnd, end); + + if (doPan) + { + start = new Point(start.X - diff.X, start.Y - diff.Y); + } + else + { + if (doCenter) + { + start = new Point(start.X + diff.X, start.Y + diff.Y); + } + if (doSquare) + { + var squared = Math.Max(width, height); + var polX = (end.X > start.X) ? 1 : -1; + var polY = (end.Y > start.Y) ? 1 : -1; + end.X = start.X + squared * polX; + end.Y = start.Y + squared * polY; + } + } + + } + form.Invalidate(); + } + + private void Form_MouseDown(object sender, MouseEventArgs e) + { + start = new Point(e.X, e.Y); + //Cursor.Hide(); + } + + private void Form_MouseUp(object sender, System.Windows.Forms.MouseEventArgs e) + { + Cursor.Show(); + form.DialogResult = DialogResult.OK; + form.Close(); + } + } +} diff --git a/screenzap/ShortcutEditor.cs b/screenzap/ShortcutEditor.cs index ce7883e..098179d 100644 --- a/screenzap/ShortcutEditor.cs +++ b/screenzap/ShortcutEditor.cs @@ -1,11 +1,4 @@ using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Data; -using System.Drawing; -using System.Linq; -using System.Text; -using System.Threading.Tasks; using System.Windows.Forms; namespace screenzap diff --git a/screenzap/app.manifest b/screenzap/app.manifest new file mode 100644 index 0000000..20b4817 --- /dev/null +++ b/screenzap/app.manifest @@ -0,0 +1,79 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/screenzap/screenzap.csproj b/screenzap/screenzap.csproj index 1e4d78c..8cc2d38 100644 --- a/screenzap/screenzap.csproj +++ b/screenzap/screenzap.csproj @@ -11,6 +11,21 @@ v4.6.1 512 true + publish\ + true + Disk + false + Foreground + 7 + Days + false + false + true + 0 + 1.0.0.%2a + false + false + true AnyCPU @@ -32,6 +47,12 @@ prompt 4 + + screenzap_trayicon.ico + + + app.manifest + @@ -81,6 +102,7 @@ ShortcutEditor.cs + SettingsSingleFileGenerator @@ -102,5 +124,20 @@ + + + + + + False + Microsoft .NET Framework 4.6.1 %28x86 and x64%29 + true + + + False + .NET Framework 3.5 SP1 + false + + \ No newline at end of file diff --git a/screenzap/screenzap_trayicon.ico b/screenzap/screenzap_trayicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..26190611274d9e73a18e03ce56cade4adde7895b GIT binary patch literal 32375 zcmc#+2Rzr$|35x9*?W(Yij3^+ttFMBWcO{@BV=!%vLz})5-JJFs`#LcBD8EiD59+F z{lC-C?_1x#efRhGzsKXc_qg}#o_F`U*Sh-%00KY^XlVf~dBG_n01g8HSXsZ^W61$1 zgU@(*zuafRdRo)~h>88@eiHzr-0%aK(D%J^0K{tmAO!DWRrut~b65ZX>3o+~R{@Aj zQv)f59^lfQpFmiO0q~900FM4rz}#L4>@wg2LfaVu3F!v-Eh)^HZ)l+Jv!B|7*OQB& zb#4*7nVJSKC&s{wkwNf$s0-A*Zw8g!Pr&20LQwoF6XZ7}fV(xJAfwzJTu9yxLaqQ{ zl_;1*yf>Ygi(#18_Ek4wSX0f{w)% z&<$y<=hnF;P|^JqJZg;uxz8*?a-lr%kK+KEdSu`9BR(4dpJ*n4>$(j(m%jdf8Qd(3 z1k^O-Kw0;jPrkdk_5gsucn(n6Qvf=bztBK^c|AD=k{{T@`WUcH{JYP-d651HfY3x9 zP=$Z=4UNXpVGxsXXss?I^EaRT_lAoPT@ry4E}KDC#T7Uo%l{n*grjb-69ipW0PqrE zzM4kW-2gDLk^;B$QgALKz_SnCpndV@Z`a2XjZ1!g%vyUibYGek-a!TXtP1D#8hG(( zaBb{RTkGGT65S(ul|2tY-0iKPX$$~*rZ3;;)LsG2<71!;j@K{0TmSa$%nT@c6$L!P zn85YoFW;^ITlyyLoB8^>U3A?vHURr{66hO~0_ULhuYNAWQw1TPHhqH^eTVSW4ZeW$ zrUok$fIUZk{^si|SQXrPatyRUjO%#Y7nT67GaH1ZZUSTQJqOR%UwnJ*7yy9YQF6$; zX=`o1nVtZJ%|Q?=EnJV^*8S1l?JBtFv&F#U)^dpD3{c-o0<1lMRQsFfesD$m!6zu= z2mruCsN*yM%)vYk-{H`TM;YD&)D2}=cNGACn5$q$at4Yj0L9Uo{DB1kP-cCXzVI6X z&^aIk4w)$dm0jXstJo$$OV0`GGQtd{+S->SutdyB2s1?V2a}vC0HV^=AgAU5oVSl~ z{q#VY^BNSrtOa?`OTn#*J5XLEf-AXUAo`{U2)TL&oC`Y&&Uoww$E}sXJ|l4;t0D;a zHgN$ma>Sgd#}qJs!6U7NfDprV6$x5rmjND5JmS~&yv{QnaE-LhuR?xU0k1wUg2s^< z$X{ci=G`EaSzX{^OEV~JdIIj%6+%pzQ0~S92fr_Z<@7lK_8QWHyVcj=+Fkk8^?=%0 z&+BcY65(0TNLIGaA>N;ZvbPU}U$;l#*OC+Dx5Ji(@#|_Y18RxJIWz z?Rz|w)8!CfJV?wt1A;HB082+6DAUjw{hYj`05G#<0|kwFP#*t!8;I`XwmvwXSK%77 z2G}H3aLSbzY!jn|gnobC!%_h_?al*r%EKS7|A#Hz;OI%^HF@_lx<7t(zAMZp?gxO z=IVz5@Qjdx{PzlU!*Tz*A83wSAU`MFzXIqP>HiSTUsfW7{eWbEt+ys@(q-l)%;8IJk~RzQMoV7t}`m@Hp^`wSvTn02LA^znKUC12aA3 zq21ud!*s~o!)v^X=&aNHWr^0;1?9jm>0=-d@BqJyr=Sk5fNOhZZEgM*-t{(6ofn_Rp{((Nb`Lc; z<@QV3P^iZqg?gtR%B+Po9{D455q-1$fW<=HttxPF>{*au{PRdl_DvT`3JMF@86 z{4I*#zN3cT02;%KS;xT3@tJShK;waI#RpAA(C*m2R*wpq`+qVa8;M(h9NeqD@n4yY zXuq780hzd1VDu9+2)+6ziTd$r-e2j;A9GVz&1gl^P_<|LfSD|sW>c<1G zY1ZE1zBanxTL9|NJp<{c=;z1nkN-qPUs6A*{Xs$>^fCWf zTR%b)lL5f7Q`?|z@$A2{d%exCOVmE%wV(tTxLK1QKce{Oo)bV(l2Zfy2D=^LLF3C` zwT0JlAPfz|bI^bDg^m2Zet(S;14T#I3V`vGozU)R`_5KgZy%Ki!;{`ID5p+963C`S zvheRGVkmYYX<+T40b^D@zl)*tO+PR&-v=8;vgMz_fNW=k!8vFjTu0yA^IwlcH(Uof zwXfhhmWS;leeusE+BH{%0Jvd|z!UuNZ^|Yl2h&Q+!4}aiuyG{+|A~awEuzH$*VrX! zZ~xdIN4(Gi%cT?aem;F#Ce&5RXPuANd z2YiB?p|5QZGRvPrpK10_`3mb8&={ch9sKrz)S~ZO`}xUwyHrpoh(X`j2imO7aE=%M zl<%|N?pg`;TxItV2#B`@lvJcZLhk3*zP&=@Obrw@cS74C68c=7(BAkt?)?qTU(E5Z zy+eIQc1yvFX6S!I!vg9XCKQqTK~mP)0>V=-!#LCc)L&nC8qxSQ+Q0V_7`!EG#d127jVgN=+4F7?IO$u1U{8#&o zFd^Mv*8UE7M_ECUOA3S~{y@Y3siL6C31feizbV&$)Nh0t*?YOqo`Ijvsz8bae<05B zEw<7(DBiRi#yiITl)Z)8emSuSTfCkvAc~(L_vO?a`47qbtruwT!|=p*IKEv!j{%gUPy0roFX#q%H&Ow#H}!8NN*Lon^AH$s z1Lt9GZEY35dJFBI{jf<4;N<unH@#uHx6SFT7TUBc%-MDZ_7+{a}Wl`9cu{ zAH$#oqzgj`tA4PWA3)XNr;rSubaizclAPm#*>`mntsXI$&0zj-oMI!q`R#=CHL`C} zd;Z#R14*M)kgqU7&$PIu|J>lo74<3@`sDi4YJGGr$;#y|}_4P;nMg9JB=?fncL7UJEazB(EZE%mW0YqHK!MJojw3E|-jps$U_Y(l)AkJ_f(h^817{K!bI?z8= zfVxT?5D*|1;DebRX0(xw41Hv;B0p}u&+BvhTl4$%9Dm*CZ~I7w*aD7c{GICm#!>K) z#n*EWyheK>>-%vQ~TeMY?e zvmb^0K{TH(L6P9a#~=G3ztmf(U+aDRx?Jloe77DyLi>cTKEpW12#PDfJ^Jo>=yy&* zAAAI4SHFk0T?f?FO&}tz3i|vHpskz%<2doa$|(dKwr~Zz4xNO3HiUbxy8$=9B0ML; z1M#j6Is(3y#Gv|OVr7A6J#N9+^B?QO*ZujuYt*Op{B>W~uh;vG;zP(*{t_=kaiSII zFQD@wvoOB*8QN7J;TeN0xGp)?q5rqssR3X-j}yjR?nC?Wuf?iko%22!owB-#`?EFzxe52jv4;ZJ|K*Uhfs;^K!mmC-2&Y69fq>63EC)k;9Bs5 z@izmwHzx+jDA3wQvgo&yy)OV#iefPC-LSR?kLdqVOh2mkORR`T5!RxYA7Q+@3GR<& z1IGY=xMmMStm6MU zwHe0M??Sm60FGE40n$q1U;{PM6Mr4+_vpVo=~@O*N2$V?LkH-B^ZPZfKXQ%6{9f%0 z)QxG72TXzFPANdO;orgfqdq_gQ19%5enKBWvA92id%X_A-|%r6#$9ee^5`7SKNmy% z5#C=uM{!@2^oo}ARpZ>I5 zMSUoJGX?hyTp(y<4Q@5>+wI`2gFNvtA~JsrZJ?0?AQN2 zFHx**?-0Rrt4`2voBGpzK(Z*gpaaH{wIP0Fi~jR62NM0wM1xFWICdvpy`e2OyQY8E z*X!?h}3R-G|pu&w57{K-*am0!MxL zeVzJG_mCZ#>yQV|hQxwrgNuK<5A{RKFdmx(X&||ZZ2JGLOo9yp{>^aqiRj@zS@N2k zNBr@7Jhc8E$)w8eIVcYTAtkim{=b&1XuOc~Lk2KJd~hBL*8H+R+lM0PyXhV}1L>fJ z^>520B(v9XqdBJmWen#gTpF>9%#;NO>XLj4)=m6UzS! z@SJ}e+#gt4vnLQQe~stQUZZ}u!u`F#_+r=w3rNU@#otN@H!r0El+BuGY12@8s z=pq{%n^XwLT^ODf`W>j>y#5+D4crsn3icSHunmCP~S(R_V{mu=NCx}nz;BP&4fFqVZ&}VOgvHgWLS@Lh=MlvU-W&r9hB>TzXzWndN z{q8j>jQ2=DSrP%`67Bz$CI2*U|8o2hedN0*+hEBVLW1)GJ1z~#HY3-gB+3{TjDXD}x4Z}Q1EV-NLg>D$ln_aLEg z|DFNHtbYjY553rDWCAKWXa05kXkO9pII?iP@OL$45E}`gK&tR>CkD9RdVg(C?3;0d z?fp~tXl`Fktim(N)j(cd5h5ml`vZR)_!@x|(3Mu!RfKyMga5cbLj6bMj^dU+(dp36 z=7VR({u$isR7j912G_owj~_hi_Wc>rzd8qNasc|dh0UW7vk$bbX`oH8-s(SFZWe*k zpMeeT5mo)I7~y(f5q9L?ra$TcddH4HuteZX+~S|b{WUcd=iGgG4?GVz`nT3H;&o)# zMWmGhX~i$UXZX6Ef9v{7OoSNzwlfCqz5F%X42^qb&n&d_t^zhLPWT&=e+%ue(?!3% z3Mf^Yi!r-&g2t9fSvevt+jx^ZuHT5biHF z?|b;0qtg&78T`G(|2N|A+h;>Z9iUHuO@g)sl;x-oD5EohS92SH+8zz~9(ME}vID+H z@b{mX*#Y?5E*W?}>gAeWfP;2kP{J+vJ6J&&FZuiQ|5p^mq(#8dQ+DtyoH0E2%K+mu z|ErdNw;en@TZC90mTRYrP>EP#t%R5BIpqZC!o-GYb74G zhwfo}=pNxfCBlbFJS@?7ey~I*rcgq3Q9|vYXulM={I| zx;h#MAPjg(0LV~8*4QcQCjjKKbTw2Dxwns8b93i>^JZ0f$Njwx`JIPsECa<^ImD`~ z@pc!EvGeg=QlaOqTO=e8qTQt?-paVsZ%8n>!1UQM!bi~|duw#bSUHZxzwf@|(b3h` z)^=rn+3~^KGk5d1oxXW|$S3(^^6`-uk%#Xn_IQq#3?@<1pV#1z(IMeJYFh5k*iWdS zvK>#7G@#BO19qV&ONv$gb+|)j6k>$61j4Dztpqlpn{)=_YFd8!eEPi)dW2$(#q`C@ zvfD&S8*BOFdexg0xTyrfi_$1Q8g3#|H|zLByh1uBlAC$2=aI%smMIp3%hbY038zr91||GS6wW=Ygbi;Z(7GKc#}lw+!;Opjgg zOYgZ@cLxj-Hb!#kHrsPoJ#rT;xeyq8D7;6NO`37G@0?J4aM`V-Ek6ZO`X9$t4BStr z)mad^vK7qZZg+D2q~xqz9lN{s>1f(@dHm${h7|vgaTZfFGCOW>!KtMZ2$AyZ&_z!j zl-LW>Lgx13Gwi+D`WIVbZUnTC(5R_jQ=n@M9;9o}zt(xmxm7p`NuDa(_wgW^Pmjn0E`DImj za~DqzH=1|P*yc+;Sj_QFl*Lb{HY7)SE*S=VlsWja=?T9IX(9h-;&d_{iR3-Bepw%t zN!%?uSuY$BRp%1tw7-3S!GQS0%Pa0b=~W5!1ka9(GlMExM}NV0aVoLBRu@3A|3l;L z+kMBS!!;*shn!w38*#?PS}24KnjdMgxHQ(s-!WkPgrY(mHE`L5#qYxhU$${v>^Y&? zo`A-WL+@utR7|*Yi0|t==acyq1e0rBp2yLd$)u&~T=3i2uXB_(gKaY%@=PT|;$O zi#-(}z3D(Sx@pp}(!9Q*<1%wzF=&mv%&f6Wa(?4oU2TMl*X)E3_rVlzzuFRpx_x)v za#6pt>Mykt%t`AddYj;Ml(%-1id5PVHkgq>_v*zQLNLAopOKhTF*kFh?(j9nyYp@g zd)2RbP$yHqc-ngCMe2n!%or219sKqxu;;eAG6dMj6$XEU!Mz(-Z`~{=p&2f5>0x|) z`oyO#?#%Vo3SLwvYLo`#sWbUBQkmIUqSLnw2?E}i-U%m8I!-XmT9~-|P`va$=#NpS z&^VoEdBfNE1>>uC{2CAQFbyYl>7vt_hBgBI{$j%7k9QInN~S&XkMHs*U9s)+YYdYf zeE62yad9F|C$=4;rbc%iW9K*z(l<=~193?e( z42jH%A(%bRWQHhO;UOTG;N*-SxO-w64U4Kd;p`lXVozc3*=ck?241N-+ z^awe@5HDzMUZppie^04Dy3DOqv`wV@*sR0;5Jvn_2^!6V)4EzstSIu* zqlT;n!uHO3+gS0P%r=R1ewL-QYj!VjgQI^5OP~BQV*tl9q1*OWe6(tD%%L&-yh8#n zRRJSj(BM6XTThVV`^~l2qh7(Wxt*Hq&754qVrJ75IE&Y3=64lu*VuhJPvhmCshdDU zdzI4fQbPTcqCVS=bDZl9I0%F8fF92jFdT_zt~?kUb6 z+qUenfwa@^HDzOs_YB(yo=Xx1g~^%?jn7_M=-z8)K@8eR$erT4omj6sY@4Xe-mS|X zvw{0xux{QJ<~FL{A-XF}Ti^ipV+>^I3q#vzaHO3ni48aPI>Y2_okG$VGYCmfJaOm@ z8|;-NCbMs=IP~0t-9p}>cwTr5VQgGe2O;e;zb|v~$97RO-!Tp=>nm}ii4>f*bromZ zgbVoQVIOE5+0qK!cOq8c_c#dBa#l~_>&W|A=j$<9RUsK}H%;&L`aKW!s5s{?sK;C2 zxGnIuwNcP`yfokLuuWT1i->W=oukv^B`KYn?kjupcPMlf)#$6pElWa%c+%-ccA}AB zK+dA3@4yr30g3IrL*nqS&!pX0eTvI(6xCX8j#%CZ`AxRzk+nD!*ylJdHjwS0jYJ2CgY8JJ!-laQ#Ac}V$_5ony!o@ zA{7F=rbA`t8r{f!?Zn%b#St@a9X@w)yk0h?DW1!U=aw0th3C}XS~vkRx(^U1oW|*G zzoz@Ps;IwixN)fLmMWX#u1`n39>vzqDApHp2@)QQzWFrPE_xRf4ZhstwM~Q>_e>(s z4#?)Gf9xUVtZig@$ZXBNpnWsxgx0<|<}uY#$Tozxn1SCNrr<r;$0>`QfB^TDoC8^X^zd147!w(T8US=Ba!>B}=xq3RLMcy>{N*_xOWC z_)A;&$iw-d`CzZ*yPT)78+C$#M!CBZe&vzz_HzS1*^`1d+p4**M84u=xg2=*5xsQ> zcg9ZYgMPL+eJGjfc?}5hAOWLXIzwqFb6maf8PJ34iJIy~&Er$l7iu&est&&(3)O?T z)iP*z%U{nV<~_5N3G|ZBv2SWr@)LxM35ziiZBH61)>9_-_THk-e$_C1&eTQ=BA4f7 zBE%Dpj(ZeaJiQhqntWxV%XVY1Od@`SONqI8H+Rg)X!?^p;obIk)iD_4Lcp*;PT|Bo1xK;1iGAxCzA5IypKe% z)y=(mZf@oHlijD7X4&Y3Q0ofL0#!L8={WJjG``Lyu3{!JXvw3^8|(Xt7t2UMt|$OSldpWPQ-#eVn(mO6!I+^k z?+C(YbFaot zCzcN1e0raDC&P%n((Vg5aC0*ez$fA&icGZUt~S=W^b}P23-h!uu@&#zqP3bE*Ovh( zPj3;Cj^P*M9g0Lec;CO~WKv_Q!7RS60m2x%ka7NHpV1&gduiCGsv0A}$Qw_wtV74@ z;L?JrC5(SRD84$hD8Bi*OV3n|Dw`C2V_JzJlYvIzn0dVI4!S&RM`DTXVyPc)LssEE zLRjRxct>Mp()wMkOAl3r|B`B2LF-4=p4>H^jFo=(FytX{poLIQYv|FyBvj-bCcO6|q#hA?;K0TM zSnFZ3gb|BUTW2MH=W((FL<=Hytah*G!u$;StHtc5CF1C+PL$f0r^*Gp_j11O6b|6A zHTSB;(`%SHY?cTvHif6svv3V|EJsAjBgWItNpuyN@*TiFU^_UGBo&EYI6yJX4tcgb zfsikqY-$@lLZVFRKDQ&Gw`7dji@V5Wc~RhYfP}e1^871tw@;co9fHFA^RSG4^6%nO zd!n%f+7Z5D;}`qrk`|W&#a!b$TW^^5r!q2KI39)}2p8FT*}`a#_8wAdsgzBcyj8k~ zphogAc(3f^h3OUbI$-xnoY#R@hNb1^{bntyO}XZjmKarL-ba46wO!{Wl3x(PHIYK8 zAnm-hWZT%2i`O>2@+PHfc*v}m7=E%{j+H0j;z9ieeqN5|V8hX)Nfj75#zj5Ov7C#W$I%6m30#wc$Il(D@(HEl%TBw^#PA| zJaZ-!``uN#BPGUO3oBy`SKKp%XJT9e)hDOTxcZ49+5{RfNf=h#D9s*d>g5Zt$X5zF61dWwv0zn>~jilOGB=bQKL1_sb! zQ^aEA+KRVdIWN621rAyGhLB<^qobXfPdosHpTNhZO}8ouQdnBt%2m6^QgE`?oC1EC zev5wBYc0}Dv?EHUF_$9tS|w(L!+>oNh9Zk{FlMpuOwy*w{Y=3feuMryVjTTVHn~!m zVGar~AIvchkh3DQ+k%OW<8u|65l$xM!#sA`c~QWkrO&&8REJJSE)OcJU{$yRUOc_3 zpkKHcpUz5}f4>O=yyGsr5SqA!{DUwdAvS>D=xCBf8X>JBJ69;IAFz!%&0RragJG

yTWF9 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 literal 0 HcmV?d00001