diff --git a/Greenshot/Forms/ImageEditorForm.cs b/Greenshot/Forms/ImageEditorForm.cs index 5cd7e9afc..c07a4f26c 100644 --- a/Greenshot/Forms/ImageEditorForm.cs +++ b/Greenshot/Forms/ImageEditorForm.cs @@ -299,7 +299,7 @@ namespace Greenshot { continue; } - ToolStripMenuItem item = destination.GetMenuItem(true, new EventHandler(DestinationToolStripMenuItemClick)); + ToolStripMenuItem item = destination.GetMenuItem(true, null, new EventHandler(DestinationToolStripMenuItemClick)); if (item != null) { item.ShortcutKeys = destination.EditorShortcutKeys; fileStripMenuItem.DropDownItems.Add(item); diff --git a/GreenshotPlugin/Core/AbstractDestination.cs b/GreenshotPlugin/Core/AbstractDestination.cs index ed53c7c08..dbaf5134d 100644 --- a/GreenshotPlugin/Core/AbstractDestination.cs +++ b/GreenshotPlugin/Core/AbstractDestination.cs @@ -71,68 +71,6 @@ namespace GreenshotPlugin.Core { } } - /// - /// Return a menu item - /// - /// - /// ToolStripMenuItem - public virtual ToolStripMenuItem GetMenuItem(bool addDynamics, EventHandler destinationClickHandler) { - ToolStripMenuItem basisMenuItem; - basisMenuItem = new ToolStripMenuItem(Description); - basisMenuItem.Image = DisplayIcon; - basisMenuItem.Tag = this; - basisMenuItem.Text = Description; - if (isDynamic && addDynamics) { - basisMenuItem.DropDownOpening += delegate (object source, EventArgs eventArgs) { - if (basisMenuItem.DropDownItems.Count == 0) { - List subDestinations = new List(); - // Fixing Bug #3536968 by catching the COMException (every exception) and not displaying the "subDestinations" - try { - subDestinations.AddRange(DynamicDestinations()); - } catch(Exception ex) { - LOG.ErrorFormat("Skipping {0}, due to the following error: {1}", Description, ex.Message); - } - if (subDestinations.Count > 0) { - subDestinations.Sort(); - - ToolStripMenuItem destinationMenuItem = null; - if (!useDynamicsOnly) { - destinationMenuItem = new ToolStripMenuItem(Description); - destinationMenuItem.Tag = this; - destinationMenuItem.Image = DisplayIcon; - destinationMenuItem.Click += destinationClickHandler; - basisMenuItem.DropDownItems.Add(destinationMenuItem); - } - if (useDynamicsOnly && subDestinations.Count == 1) { - basisMenuItem.Tag = subDestinations[0]; - basisMenuItem.Text = subDestinations[0].Description; - basisMenuItem.Click -= destinationClickHandler; - basisMenuItem.Click += destinationClickHandler; - } else { - foreach (IDestination subDestination in subDestinations) { - destinationMenuItem = new ToolStripMenuItem(subDestination.Description); - destinationMenuItem.Tag = subDestination; - destinationMenuItem.Image = subDestination.DisplayIcon; - destinationMenuItem.Click += destinationClickHandler; - basisMenuItem.DropDownItems.Add(destinationMenuItem); - } - } - } else { - // Setting base "click" only if there are no sub-destinations - - // Make sure any previous handler is removed, otherwise it would be added multiple times! - basisMenuItem.Click -= destinationClickHandler; - basisMenuItem.Click += destinationClickHandler; - } - } - }; - } else { - basisMenuItem.Click += destinationClickHandler; - } - - return basisMenuItem; - } - public virtual IEnumerable DynamicDestinations() { yield break; } @@ -201,6 +139,25 @@ namespace GreenshotPlugin.Core { return Description; } + /// + /// Helper method to add events which set the tag, this way we can see why there might be a close. + /// + /// Item to add the events to + /// Menu to set the tag + /// Value for the tag + private void AddTagEvents(ToolStripMenuItem menuItem, ContextMenuStrip menu, string tagValue) { + if (menuItem != null && menu != null) { + menuItem.MouseDown += delegate(object source, MouseEventArgs eventArgs) { + LOG.DebugFormat("Setting tag to '{0}'", tagValue); + menu.Tag = tagValue; + }; + menuItem.MouseUp += delegate(object source, MouseEventArgs eventArgs) { + LOG.Debug("Deleting tag"); + menu.Tag = null; + }; + } + } + /// /// This method will create and show the destination picker menu /// @@ -213,11 +170,22 @@ namespace GreenshotPlugin.Core { // Generate an empty ExportInformation object, for when nothing was selected. ExportInformation exportInformation = new ExportInformation(Designation, Language.GetString("settings_destination_picker")); ContextMenuStrip menu = new ContextMenuStrip(); + menu.Tag = null; + menu.Closing += delegate(object source, ToolStripDropDownClosingEventArgs eventArgs) { LOG.DebugFormat("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.DebugFormat("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 @@ -231,7 +199,7 @@ namespace GreenshotPlugin.Core { }; foreach (IDestination destination in destinations) { // Fix foreach loop variable for the delegate - ToolStripMenuItem item = destination.GetMenuItem(addDynamics, + ToolStripMenuItem item = destination.GetMenuItem(addDynamics, menu, delegate(object sender, EventArgs e) { ToolStripMenuItem toolStripMenuItem = sender as ToolStripMenuItem; if (toolStripMenuItem == null) { @@ -242,9 +210,7 @@ namespace GreenshotPlugin.Core { return; } bool isEditor = "Editor".Equals(clickedDestination.Designation); - // Make sure the menu is invisible, don't close it - menu.Hide(); - + menu.Tag = clickedDestination.Designation; // Export exportInformation = clickedDestination.ExportCapture(true, surface, captureDetails); if (exportInformation != null && exportInformation.ExportMade) { @@ -259,6 +225,10 @@ namespace GreenshotPlugin.Core { } } else { LOG.Info("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); } @@ -316,5 +286,73 @@ namespace GreenshotPlugin.Core { } } } + + /// + /// Return a menu item + /// + /// + /// ToolStripMenuItem + public virtual ToolStripMenuItem GetMenuItem(bool addDynamics, ContextMenuStrip menu, EventHandler destinationClickHandler) { + ToolStripMenuItem basisMenuItem; + basisMenuItem = new ToolStripMenuItem(Description); + basisMenuItem.Image = DisplayIcon; + basisMenuItem.Tag = this; + basisMenuItem.Text = Description; + AddTagEvents(basisMenuItem, menu, Description); + if (isDynamic && addDynamics) { + basisMenuItem.DropDownOpening += delegate(object source, EventArgs eventArgs) { + if (basisMenuItem.DropDownItems.Count == 0) { + List subDestinations = new List(); + // Fixing Bug #3536968 by catching the COMException (every exception) and not displaying the "subDestinations" + try { + subDestinations.AddRange(DynamicDestinations()); + } catch (Exception ex) { + LOG.ErrorFormat("Skipping {0}, due to the following error: {1}", Description, ex.Message); + } + if (subDestinations.Count > 0) { + subDestinations.Sort(); + + ToolStripMenuItem destinationMenuItem = null; + + if (!useDynamicsOnly) { + destinationMenuItem = new ToolStripMenuItem(Description); + destinationMenuItem.Tag = this; + destinationMenuItem.Image = DisplayIcon; + destinationMenuItem.Click += destinationClickHandler; + AddTagEvents(destinationMenuItem, menu, Description); + + basisMenuItem.DropDownItems.Add(destinationMenuItem); + } + if (useDynamicsOnly && subDestinations.Count == 1) { + basisMenuItem.Tag = subDestinations[0]; + basisMenuItem.Text = subDestinations[0].Description; + basisMenuItem.Click -= destinationClickHandler; + basisMenuItem.Click += destinationClickHandler; + } else { + foreach (IDestination subDestination in subDestinations) { + destinationMenuItem = new ToolStripMenuItem(subDestination.Description); + destinationMenuItem.Tag = subDestination; + destinationMenuItem.Image = subDestination.DisplayIcon; + destinationMenuItem.Click += destinationClickHandler; + AddTagEvents(destinationMenuItem, menu, subDestination.Description); + basisMenuItem.DropDownItems.Add(destinationMenuItem); + } + } + } else { + // Setting base "click" only if there are no sub-destinations + + // Make sure any previous handler is removed, otherwise it would be added multiple times! + basisMenuItem.Click -= destinationClickHandler; + basisMenuItem.Click += destinationClickHandler; + } + } + }; + } else { + basisMenuItem.Click += destinationClickHandler; + } + + return basisMenuItem; + } + } } diff --git a/GreenshotPlugin/Interfaces/IDestination.cs b/GreenshotPlugin/Interfaces/IDestination.cs index 27f149ab2..c9701e9c6 100644 --- a/GreenshotPlugin/Interfaces/IDestination.cs +++ b/GreenshotPlugin/Interfaces/IDestination.cs @@ -136,9 +136,10 @@ namespace Greenshot.Plugin { /// Return a menu item /// /// Resolve the dynamic destinations too? + /// The menu for which the item is created /// Handler which is called when clicked /// ToolStripMenuItem - ToolStripMenuItem GetMenuItem(bool addDynamics, EventHandler destinationClickHandler); + ToolStripMenuItem GetMenuItem(bool addDynamics, ContextMenuStrip menu, EventHandler destinationClickHandler); /// /// Gets the ShortcutKeys for the Editor