/// <summary>
        ///     Handle DPI changes for the specified ContextMenuStrip
        /// </summary>
        /// <param name="contextMenuStrip">ContextMenuStrip</param>
        /// <returns>DpiHandler</returns>
        public static DpiHandler AttachDpiHandler(this ContextMenuStrip contextMenuStrip)
        {
            // Create a DpiHandler which runs "outside" of the contextMenu (not via WinProc)
            var dpiHandler = new DpiHandler(true);

            dpiHandler.MessageHandler = contextMenuStrip.WinProcFormsMessages().Subscribe(message => dpiHandler.HandleContextMenuMessages(message));
            return(dpiHandler);
        }
        /// <summary>
        ///     Handle DPI changes for the specified Form
        ///     Using this DOES NOT enable dpi scaling in the non client area, for this you will need to call:
        ///     DpiHandler.TryEnableNonClientDpiScaling(this.Handle) from the WndProc in the WM_NCCREATE message.
        ///     It's better to extend DpiAwareForm, which does this for you.
        /// </summary>
        /// <param name="form">Control</param>
        /// <returns>DpiHandler</returns>
        public static DpiHandler AttachDpiHandler(this Form form)
        {
            // Create a DpiHandler which runs "outside" of the form (not via WinProc)
            var dpiHandler = new DpiHandler(true);

            dpiHandler.MessageHandler = form.WinProcFormsMessages().Subscribe(message => dpiHandler.HandleWindowMessages(message));
            return(dpiHandler);
        }
예제 #3
0
        /// <summary>
        ///     Handle DPI changes for the specified Form
        ///     Using this DOES NOT enable dpi scaling in the non client area, for this you will need to call:
        ///     DpiHandler.TryEnableNonClientDpiScaling(this.Handle) from the WndProc in the WM_NCCREATE message.
        ///     It's better to extend DpiAwareForm, which does this for you.
        /// </summary>
        /// <param name="form">Control</param>
        /// <returns>DpiHandler</returns>
        public static DpiHandler AttachDpiHandler(this Form form)
        {
            // Create a DpiHandler which runs "outside" of the form (not via WinProc)
            var dpiHandler = new DpiHandler(true);
            var listener   = new WinProcListener(form);

            listener.AddHook(dpiHandler.HandleWindowMessages);
            dpiHandler.MessageHandler = listener;
            return(dpiHandler);
        }
예제 #4
0
        /// <summary>
        ///     Handle DPI changes for the specified ContextMenuStrip
        /// </summary>
        /// <param name="contextMenuStrip">ContextMenuStrip</param>
        /// <returns>DpiHandler</returns>
        public static DpiHandler AttachDpiHandler(this ContextMenuStrip contextMenuStrip)
        {
            // Create a DpiHandler which runs "outside" of the contextMenu (not via WinProc)
            var dpiHandler = new DpiHandler(true);
            var listener   = new WinProcListener(contextMenuStrip);

            listener.AddHook(dpiHandler.HandleContextMenuMessages);
            dpiHandler.MessageHandler = listener;
            return(dpiHandler);
        }
예제 #5
0
        /// <summary>
        /// Setup the Bitmap scaling (for icons)
        /// </summary>
        private void SetupBitmapScaleHandler()
        {
            ContextMenuDpiHandler = contextMenu.AttachDpiHandler();

            var dpiChangeSubscription = DpiHandler.OnDpiChangeInfo.Subscribe(info =>
            {
                switch (info.DpiChangeEventType)
                {
                case DpiChangeEventTypes.Before:
                    // Change the ImageScalingSize before setting the bitmaps
                    var width = DpiHandler.ScaleWithDpi(coreConfiguration.IconSize.Width, info.NewDpi);
                    var size  = new Size(width, width);
                    contextMenu.SuspendLayout();
                    contextMenu.ImageScalingSize   = size;
                    contextmenu_quicksettings.Size = new Size(170, width + 8);
                    break;

                case DpiChangeEventTypes.After:
                    // Redraw the form
                    contextMenu.ResumeLayout(true);
                    contextMenu.Refresh();
                    notifyIcon.Icon = GreenshotResources.GetGreenshotIcon();
                    break;
                }
            });

            var contextMenuResourceScaleHandler = BitmapScaleHandler.WithComponentResourceManager(ContextMenuDpiHandler, GetType(), (bitmap, dpi) => bitmap.ScaleIconForDisplaying(dpi));

            contextMenuResourceScaleHandler.AddTarget(contextmenu_capturewindow, "contextmenu_capturewindow.Image");
            contextMenuResourceScaleHandler.AddTarget(contextmenu_capturearea, "contextmenu_capturearea.Image");
            contextMenuResourceScaleHandler.AddTarget(contextmenu_capturelastregion, "contextmenu_capturelastregion.Image");
            contextMenuResourceScaleHandler.AddTarget(contextmenu_capturefullscreen, "contextmenu_capturefullscreen.Image");
            contextMenuResourceScaleHandler.AddTarget(contextmenu_captureclipboard, "contextmenu_captureclipboard.Image");
            contextMenuResourceScaleHandler.AddTarget(contextmenu_openfile, "contextmenu_openfile.Image");
            contextMenuResourceScaleHandler.AddTarget(contextmenu_settings, "contextmenu_settings.Image");
            contextMenuResourceScaleHandler.AddTarget(contextmenu_help, "contextmenu_help.Image");
            contextMenuResourceScaleHandler.AddTarget(contextmenu_donate, "contextmenu_donate.Image");
            contextMenuResourceScaleHandler.AddTarget(contextmenu_exit, "contextmenu_exit.Image");

            // this is special handling, for the icons which come from the executables
            var exeBitmapScaleHandler = BitmapScaleHandler.Create <string>(ContextMenuDpiHandler,
                                                                           (path, dpi) => PluginUtils.GetCachedExeIcon(path, 0, dpi >= 120),
                                                                           (bitmap, dpi) => bitmap.ScaleIconForDisplaying(dpi));

            exeBitmapScaleHandler.AddTarget(contextmenu_captureie, PluginUtils.GetExePath("iexplore.exe"));

            // Add cleanup
            Application.ApplicationExit += (sender, args) =>
            {
                dpiChangeSubscription.Dispose();
                ContextMenuDpiHandler.Dispose();
                contextMenuResourceScaleHandler.Dispose();
                exeBitmapScaleHandler.Dispose();
            };
        }
예제 #6
0
        /// <summary>
        ///     Implementation of the IGreenshotPlugin.Initialize
        /// </summary>
        /// <returns>true if plugin is initialized, false if not (doesn't show)</returns>
        public bool Initialize()
        {
            _greenshotHost.ContextMenuDpiHandler.OnDpiChanged.Subscribe(dpi =>
            {
                var size = DpiHandler.ScaleWithDpi(_coreConfiguration.IconSize.Width, dpi);

                _jiraConnector.UpdateSvgSize(size);
            });

            return(true);
        }
예제 #7
0
        /// <summary>
        /// </summary>
        /// <param name="bitmap"></param>
        /// <param name="dpi"></param>
        /// <returns></returns>
        private Bitmap ScaleIconForDisplaying(Bitmap bitmap, double dpi)
        {
            var newSize = DpiHandler.ScaleWithDpi(16, dpi);
            var result  = new Bitmap(newSize, newSize, bitmap.PixelFormat);

            using (var graphics = Graphics.FromImage(result))
            {
                graphics.InterpolationMode = InterpolationMode.NearestNeighbor;
                graphics.DrawImage(bitmap, new Rectangle(0, 0, newSize, newSize), new Rectangle(0, 0, 16, 16), GraphicsUnit.Pixel);
            }
            return(result);
        }
예제 #8
0
        /// <summary>
        ///     Handle DPI changes for the specified Window, this is actually not really needed for WPF.
        /// </summary>
        /// <param name="window">Window</param>
        /// <returns>DpiHandler</returns>
        public static DpiHandler AttachDpiHandler(this Window window)
        {
            if (Log.IsVerboseEnabled())
            {
                Log.Verbose().WriteLine("Creating a dpi handler for {0}", window.GetType());
            }

            var dpiHandler = new DpiHandler();

            AttachDpiHandler(window, dpiHandler);

            return(dpiHandler);
        }
예제 #9
0
        public void Test_UnscaleWithDpi()
        {
            var size96 = DpiHandler.UnscaleWithDpi(16, 96);

            Assert.Equal(16, size96);
            var size120 = DpiHandler.UnscaleWithDpi(16, 120);

            Assert.Equal(12, size120);
            var size144 = DpiHandler.UnscaleWithDpi(16, 144);

            Assert.Equal(10, size144);
            var size192 = DpiHandler.UnscaleWithDpi(16, 192);

            Assert.Equal(8, size192);
        }
예제 #10
0
        public void Test_ScaleWithDpi()
        {
            var size96 = DpiHandler.ScaleWithDpi(16, 96);

            Assert.Equal(16, size96);
            var size120 = DpiHandler.ScaleWithDpi(16, 120);

            Assert.Equal(20, size120);
            var size144 = DpiHandler.ScaleWithDpi(16, 144);

            Assert.Equal(24, size144);
            var size192 = DpiHandler.ScaleWithDpi(16, 192);

            Assert.Equal(32, size192);
        }
예제 #11
0
        public void TestScreenbounds()
        {
            var screenboundsAllScreens  = GetScreenBoundsAllScreens();
            var screenboundsDisplayInfo = DisplayInfo.ScreenBounds;

            // The following scales the screenboundsAllScreens which comes from build in code without DPI awareness
            // with the current DPI so it should also work when running with a different DPI setting
            var monitorHandle = DisplayInfo.AllDisplayInfos.First().MonitorHandle;

            NativeDpiMethods.GetDpiForMonitor(monitorHandle, Dpi.Enums.MonitorDpiType.EffectiveDpi, out var xDpi, out var yDpi);
            if (xDpi != DpiHandler.DefaultScreenDpi)
            {
                var newSize = DpiHandler.ScaleWithDpi(screenboundsAllScreens.Size, xDpi);
                screenboundsAllScreens = new NativeRect(screenboundsAllScreens.Location, newSize);
            }

            Assert.Equal(screenboundsAllScreens, screenboundsDisplayInfo);
        }
예제 #12
0
        public void Test_ScaleWithDpi_UnscaleWithDpi()
        {
            var testSize     = new NativeSize(16, 16);
            var size96       = DpiHandler.ScaleWithDpi(testSize, 96);
            var resultSize96 = DpiHandler.UnscaleWithDpi(size96, 96);

            Assert.Equal(testSize, resultSize96);

            var size120       = DpiHandler.ScaleWithDpi(testSize, 120);
            var resultSize120 = DpiHandler.UnscaleWithDpi(size120, 120);

            Assert.Equal(testSize, resultSize120);

            var size144       = DpiHandler.ScaleWithDpi(testSize, 144);
            var resultSize144 = DpiHandler.UnscaleWithDpi(size144, 144);

            Assert.Equal(testSize, resultSize144);
        }
        /// <summary>
        ///     Prepare an "icon" to be displayed correctly scaled
        /// </summary>
        /// <param name="original">original icon Bitmap</param>
        /// <param name="dpi">double with the dpi value</param>
        /// <param name="baseSize">the base size of the icon, default is 16</param>
        /// <returns>Bitmap</returns>
        public static Bitmap ScaleIconForDisplaying(this Bitmap original, uint dpi, int baseSize = 16)
        {
            if (original == null)
            {
                return(null);
            }
            if (dpi < DpiHandler.DefaultScreenDpi)
            {
                dpi = DpiHandler.DefaultScreenDpi;
            }
            var width = DpiHandler.ScaleWithDpi(baseSize, dpi);

            if (original.Width == width)
            {
                return(original);
            }

            return(original.Resize(true, width, width, interpolationMode: InterpolationMode.NearestNeighbor));
        }
        /// <summary>
        /// This takes the height and width of the Popup and will return the location where it should be displayed
        /// </summary>
        /// <param name="actualPopupWidth">double</param>
        /// <param name="actualPopupHeight">double</param>
        /// <returns>Point</returns>
        public Point GetPosition(double actualPopupWidth, double actualPopupHeight)
        {
            var taskbar       = Shell32Api.TaskbarPosition;
            var taskbarBounds = taskbar.Bounds;

            var actualSize = new NativeSize((int)actualPopupWidth, (int)actualPopupHeight);

            // Use the DPI of the desktop
            var dpi = NativeDpiMethods.GetDpi(InteropWindowQuery.GetDesktopWindow().Handle);

            actualSize = DpiHandler.ScaleWithDpi(actualSize, dpi);
            int x, y;

            // Define the new position
            switch (taskbar.AppBarEdge)
            {
            case AppBarEdges.Left:
                x = taskbarBounds.Right + _xOffset;
                y = taskbarBounds.Bottom - _yOffset - actualSize.Height;
                break;

            case AppBarEdges.Top:
                x = taskbarBounds.Right - _xOffset - actualSize.Width;
                y = taskbarBounds.Bottom + _yOffset;
                break;

            case AppBarEdges.Right:
                x = taskbarBounds.Left - _xOffset - actualSize.Width;
                y = taskbarBounds.Bottom - _yOffset - actualSize.Height;
                break;

            case AppBarEdges.Bottom:
            default:
                x = taskbarBounds.Right - _xOffset - actualSize.Width;
                y = taskbarBounds.Top - _yOffset - actualSize.Height;
                break;
            }

            var position = DpiHandler.UnscaleWithDpi(new NativePoint(x, y), dpi);

            Log.Debug().WriteLine("Taskbar location {0} at {1}, calculate popup position: {2}", taskbar.AppBarEdge, taskbarBounds, position);
            return(position);
        }
예제 #15
0
        public FormExtendsDpiAwareForm()
        {
            InitializeComponent();

            _contextMenuDpiHandler = contextMenuStrip1.AttachDpiHandler();

            _contextMenuDpiHandler.OnDpiChanged.Subscribe(dpi =>
            {
                Log.Info().WriteLine("ContextMenuStrip DPI: {0}", dpi);
            });
            ScaleHandler = BitmapScaleHandler.WithComponentResourceManager(DpiHandler, GetType(), ScaleIconForDisplaying);

            ScaleHandler.AddTarget(somethingMenuItem, "somethingMenuItem.Image");
            ScaleHandler.AddTarget(something2MenuItem, "something2MenuItem.Image");

            EnvironmentMonitor.EnvironmentUpdateEvents.Subscribe(args =>
            {
                Log.Info().WriteLine("{0} - {1}", args.SystemParametersInfoAction, args.Area);
                MessageBox.Show(this, $"{args.SystemParametersInfoAction} - {args.Area}", "Change!");
            });
        }
예제 #16
0
        public FormWithAttachedDpiHandler()
        {
            _dpiHandler = this.AttachDpiHandler();
            InitializeComponent();
            ScaleHandler = BitmapScaleHandler.WithComponentResourceManager(_dpiHandler, GetType(), ScaleIconForDisplaying);

            // This can be used to do something with DPI changes, subscription should be disposed!
            _dpiChangeSubscription = _dpiHandler.OnDpiChanged.Subscribe(dpi =>
            {
                var width = DpiHandler.ScaleWithDpi(20, dpi);
                var size  = new Size(width, width);
                //menuStrip1.ImageScalingSize = size;
            });


            ScaleHandler.AddTarget(somethingMenuItem, "somethingMenuItem.Image");
            ScaleHandler.AddTarget(something2MenuItem, "something2MenuItem.Image");

            EnvironmentMonitor.EnvironmentUpdateEvents.Subscribe(args =>
            {
                Log.Info().WriteLine("{0} - {1}", args.SystemParametersInfoAction, args.Area);
                MessageBox.Show(this, $"{args.SystemParametersInfoAction} - {args.Area}", "Change!");
            });
        }
예제 #17
0
        /// <summary>
        ///     Attach a DpiHandler to the specified window
        /// </summary>
        /// <param name="window">Windows</param>
        /// <param name="dpiHandler">DpiHandler</param>
        private static void AttachDpiHandler(Window window, DpiHandler dpiHandler)
        {
            var windowInteropHelper = new WindowInteropHelper(window);

            // Due to the nature of the WPF Window, we cannot get to the WM_NCCREATE event from outside
            // The SourceInitialized event is actually called when this event happens.
            window.SourceInitialized += (sender, args) =>
            {
                DpiHandler.TryEnableNonClientDpiScaling(windowInteropHelper.Handle);

                // Get the HwndSource for the window, this works very early in the process by calling EnsureHandle (which creates the handle)
                var hwndSource = HwndSource.FromHwnd(windowInteropHelper.Handle);
                if (hwndSource == null)
                {
                    throw new NotSupportedException("No HwndSource available, although EnsureHandle was called?");
                }
                hwndSource.AddHook(dpiHandler.HandleWindowMessages);
                dpiHandler.MessageHandler = hwndSource;
                // Add the layout transform action
                dpiHandler.OnDpiChanged.Subscribe(dpi => window.UpdateLayoutTransform(dpi / DpiHandler.DefaultScreenDpi));
                // Apply scaling
                window.UpdateLayoutTransform(DpiHandler.GetDpi(windowInteropHelper.Handle) / DpiHandler.DefaultScreenDpi);
            };
        }
예제 #18
0
 protected AbstractAdorner(IDrawableContainer owner)
 {
     _size.Width = _size.Height = DpiHandler.ScaleWithDpi(5, NativeDpiMethods.GetDpi(InteropWindowQuery.GetDesktopWindow().Handle));
     Owner       = owner;
 }
예제 #19
0
 public static int GetDpi(Window window)
 {
     return(DpiHandler.GetDpi(new WindowInteropHelper(window).Handle));
 }
예제 #20
0
 protected override void OnStartup(StartupEventArgs e)
 {
     base.OnStartup(e);
     DpiHandler.EnableDpiAwareness();
 }
예제 #21
0
        /// <summary>
        ///     This method will create and show the destination picker menu
        /// </summary>
        /// <param name="addDynamics">Boolean if the dynamic values also need to be added</param>
        /// <param name="surface">The surface which can be exported</param>
        /// <param name="captureDetails">Details for the surface</param>
        /// <param name="destinations">The list of destinations to show</param>
        /// <returns></returns>
        protected ExportInformation ShowPickerMenu(bool addDynamics, ISurface surface, ICaptureDetails captureDetails, IEnumerable <IDestination> destinations)
        {
            var menu = new ContextMenuStrip
            {
                Tag      = null,
                TopLevel = true
            };
            var dpiHandler         = menu.AttachDpiHandler();
            var bitmapScaleHandler = BitmapScaleHandler.Create <IDestination>(
                dpiHandler,
                (destination, dpi) => destination.GetDisplayIcon(dpi),
                (bitmap, d) => bitmap.ScaleIconForDisplaying(d));

            dpiHandler.OnDpiChanged.Subscribe(dpi =>
            {
                var width             = DpiHandler.ScaleWithDpi(CoreConfiguration.IconSize.Width, dpi);
                var size              = new Size(width, width);
                menu.ImageScalingSize = size;
            });

            // Generate an empty ExportInformation object, for when nothing was selected.
            var exportInformation = new ExportInformation("", GreenshotLanguage.SettingsDestinationPicker);

            menu.Closing += (source, eventArgs) =>
            {
                Log.Debug().WriteLine("Close reason: {0}", eventArgs.CloseReason);
                switch (eventArgs.CloseReason)
                {
                case ToolStripDropDownCloseReason.AppFocusChange:
                    if (menu.Tag == null)
                    {
                        // Do not allow the close if the tag is not set, this means the user clicked somewhere else.
                        eventArgs.Cancel = true;
                    }
                    else
                    {
                        Log.Debug().WriteLine("Letting the menu 'close' as the tag is set to '{0}'", menu.Tag);
                    }
                    break;

                case ToolStripDropDownCloseReason.ItemClicked:
                case ToolStripDropDownCloseReason.CloseCalled:
                    // The ContextMenuStrip can be "closed" for these reasons.
                    break;

                case ToolStripDropDownCloseReason.Keyboard:
                    // Dispose as the close is clicked
                    if (!captureDetails.HasDestination("Editor"))
                    {
                        surface.Dispose();
                        surface = null;
                    }
                    break;

                default:
                    eventArgs.Cancel = true;
                    break;
                }
            };
            menu.MouseEnter += (sender, args) =>
            {
                // in case the menu has been unfocused, focus again so that dropdown menus will still open on mouseenter
                if (!menu.ContainsFocus)
                {
                    menu.Focus();
                }
            };
            foreach (var destination in destinations)
            {
                // Fix foreach loop variable for the delegate
                var item = destination.GetMenuItem(addDynamics, menu,
                                                   async(sender, e) =>
                {
                    var toolStripMenuItem  = sender as ToolStripMenuItem;
                    var clickedDestination = (IDestination)toolStripMenuItem?.Tag;
                    if (clickedDestination == null)
                    {
                        return;
                    }
                    menu.Tag = clickedDestination.Designation;
                    // Export
                    exportInformation = await clickedDestination.ExportCaptureAsync(true, surface, captureDetails);
                    if (exportInformation != null && exportInformation.ExportMade)
                    {
                        Log.Info().WriteLine("Export to {0} success, closing menu", exportInformation.DestinationDescription);
                        // close menu
                        menu.Close();
                        menu.Dispose();
                        // Cleanup surface, only if there is no editor in the destinations and we didn't export to the editor
                        if (!captureDetails.HasDestination("Editor") && !"Editor".Equals(clickedDestination.Designation))
                        {
                            surface.Dispose();
                            surface = null;
                        }
                    }
                    else
                    {
                        Log.Info().WriteLine("Export cancelled or failed, showing menu again");

                        // Make sure a click besides the menu don't close it.
                        menu.Tag = null;

                        // This prevents the problem that the context menu shows in the task-bar
                        ShowMenuAtCursor(menu);
                    }
                }, bitmapScaleHandler
                                                   );
                if (item != null)
                {
                    menu.Items.Add(item);
                }
            }
            // Close
            menu.Items.Add(new ToolStripSeparator());
            var closeItem = new ToolStripMenuItem(GreenshotLanguage.Close)
            {
                Image = GreenshotResources.GetBitmap("Close.Image")
            };

            closeItem.Click += (sender, args) =>
            {
                // This menu entry is the close itself, we can dispose the surface
                menu.Close();
                menu.Dispose();
                // Only dispose if there is a destination which keeps the capture
                if (captureDetails.HasDestination("Editor"))
                {
                    return;
                }

                surface.Dispose();
                surface = null;
            };
            menu.Items.Add(closeItem);

            ShowMenuAtCursor(menu);
            return(exportInformation);
        }