2007-04-13 Jonathan Pobst <monkey@jpobst.com>
authorJonathan Pobst <monkey@jpobst.com>
Fri, 13 Apr 2007 17:38:51 +0000 (17:38 -0000)
committerJonathan Pobst <monkey@jpobst.com>
Fri, 13 Apr 2007 17:38:51 +0000 (17:38 -0000)
* ToolStrip.cs: Add properties and internal methods to support merging.
* ToolStripItem.cs: Add MergeAction and MergeIndex.
* ToolStripItemCollection.cs: Add Insert and Remove methods that do
not trigger reparenting or layouts.
* ToolStripManager.cs: Add Merge and RevertMerge methods.
* ToolStripOverflow.cs: Add a convenience method to find the ToolStrip that
is hosting the overflow menu.

svn path=/trunk/mcs/; revision=75688

mcs/class/Managed.Windows.Forms/System.Windows.Forms/ChangeLog
mcs/class/Managed.Windows.Forms/System.Windows.Forms/ToolStrip.cs
mcs/class/Managed.Windows.Forms/System.Windows.Forms/ToolStripItem.cs
mcs/class/Managed.Windows.Forms/System.Windows.Forms/ToolStripItemCollection.cs
mcs/class/Managed.Windows.Forms/System.Windows.Forms/ToolStripManager.cs
mcs/class/Managed.Windows.Forms/System.Windows.Forms/ToolStripOverflow.cs

index 395aca9722c28a46ed66ec2afacf6845e4b919e6..2aea3b74962dc0598be79d0291e00bc361dbbeb0 100644 (file)
@@ -1,3 +1,13 @@
+2007-04-13  Jonathan Pobst  <monkey@jpobst.com>
+
+       * ToolStrip.cs: Add properties and internal methods to support merging.
+       * ToolStripItem.cs: Add MergeAction and MergeIndex.
+       * ToolStripItemCollection.cs: Add Insert and Remove methods that do
+       not trigger reparenting or layouts.
+       * ToolStripManager.cs: Add Merge and RevertMerge methods.
+       * ToolStripOverflow.cs: Add a convenience method to find the ToolStrip that
+       is hosting the overflow menu.
+
 2007-04-13  Jackson Harper  <jackson@ximian.com>
 
        * TextControl.cs: Set the line ending correctly for the first
index 7f123d19f934ee3fc96b860c523476a8090ec98f..d4ee2167c73971f54509e37322d170adc8a14266 100644 (file)
@@ -32,6 +32,7 @@ using System.Runtime.InteropServices;
 using System.ComponentModel;
 using System.Drawing;
 using System.Windows.Forms.Layout;
+using System.Collections.Generic;
 
 namespace System.Windows.Forms
 {
@@ -42,21 +43,26 @@ namespace System.Windows.Forms
        public class ToolStrip : ScrollableControl, IComponent, IDisposable
        {
                #region Private Variables
+               private bool allow_merge;
                private Color back_color;
                private bool can_overflow;
+               private ToolStrip currently_merged_with;
                private ToolStripDropDownDirection default_drop_down_direction;
                internal ToolStripItemCollection displayed_items;
                private Color fore_color;
                private Padding grip_margin;
                private ToolStripGripStyle grip_style;
+               private List<ToolStripItem> hidden_merged_items;
                private ImageList image_list;
                private Size image_scaling_size;
+               private bool is_currently_merged;
                private ToolStripItemCollection items;
                private LayoutEngine layout_engine;
                private LayoutSettings layout_settings;
                private ToolStripLayoutStyle layout_style;
                private Orientation orientation;
                private ToolStripOverflowButton overflow_button;
+               private List<ToolStripItem> pre_merge_items;
                private ToolStripRenderer renderer;
                private ToolStripRenderMode render_mode;
                private Timer tooltip_timer;
@@ -83,6 +89,7 @@ namespace System.Windows.Forms
 
                        this.SuspendLayout ();
                        this.items = new ToolStripItemCollection (this, items);
+                       this.allow_merge = true;
                        base.AutoSize = true;
                        this.back_color = Control.DefaultBackColor;
                        this.can_overflow = true;
@@ -113,6 +120,11 @@ namespace System.Windows.Forms
                #endregion
 
                #region Public Properties
+               public bool AllowMerge {
+                       get { return this.allow_merge; }
+                       set { this.allow_merge = false; }
+               }
+               
                public override AnchorStyles Anchor {
                        get { return base.Anchor; }
                        set { base.Anchor = value; }
@@ -926,8 +938,16 @@ namespace System.Windows.Forms
                        item.SetBounds (new Rectangle (location, item.Size));
                }
                
-               protected static void SetItemParent (ToolStripItem item, ToolStrip parent)
+               protected internal static void SetItemParent (ToolStripItem item, ToolStrip parent)
                {
+                       if (item.Parent != null) {
+                               item.Parent.Items.RemoveNoOwnerOrLayout (item);
+                               
+                               if (item.Parent is ToolStripOverflow)
+                                       (item.Parent as ToolStripOverflow).ParentToolStrip.Items.RemoveNoOwnerOrLayout (item);
+                       }
+                       
+                       parent.Items.AddNoOwnerOrLayout (item);
                        item.Parent = parent;
                }
 
@@ -1138,6 +1158,81 @@ namespace System.Windows.Forms
 
                        ToolTipTimer.Stop ();
                }
+               
+               internal ToolStrip CurrentlyMergedWith {
+                       get { return this.currently_merged_with; }
+                       set { this.currently_merged_with = value; }
+               }
+               
+               internal List<ToolStripItem> HiddenMergedItems {
+                       get {
+                               if (this.hidden_merged_items == null)
+                                       this.hidden_merged_items = new List<ToolStripItem> ();
+                                       
+                               return this.hidden_merged_items;
+                       }
+               }
+               
+               internal bool IsCurrentlyMerged {
+                       get { return this.is_currently_merged; }
+                       set { 
+                               this.is_currently_merged = value; 
+                               
+                               if (!value && this is MenuStrip) 
+                                       foreach (ToolStripMenuItem tsmi in this.Items)
+                                               tsmi.DropDown.IsCurrentlyMerged = value;
+                        }
+               }
+               
+               internal void BeginMerge ()
+               {
+                       if (!IsCurrentlyMerged) {
+                               IsCurrentlyMerged = true;
+                               
+                               if (this.pre_merge_items == null) {
+                                       this.pre_merge_items = new List<ToolStripItem> ();
+                       
+                               foreach (ToolStripItem tsi in this.Items)
+                                       this.pre_merge_items.Add (tsi);
+                               }
+                               
+                               if (this is MenuStrip)
+                                       foreach (ToolStripMenuItem tsmi in this.Items)
+                                               tsmi.DropDown.BeginMerge ();
+                       }
+               }
+               
+               internal void RevertMergeItem (ToolStripItem item)
+               {
+                       int index = 0;
+
+                       // Remove it from it's current Parent
+                       if (item.Parent != null && item.Parent != this) {
+                               if (item.Parent is ToolStripOverflow)
+                                       (item.Parent as ToolStripOverflow).ParentToolStrip.Items.RemoveNoOwnerOrLayout (item);
+                               else
+                                       item.Parent.Items.RemoveNoOwnerOrLayout (item);
+
+                               item.Parent = item.Owner;       
+                       }
+                       
+                       // Find where the item was before the merge
+                       index = item.Owner.pre_merge_items.IndexOf (item);
+
+                       // Find the first pre-merge item that was after this item, that
+                       // is currently in the Items collection.  Insert our item before
+                       // that one.
+                       for (int i = index; i < this.pre_merge_items.Count; i++) {
+                               if (this.Items.Contains (this.pre_merge_items[i])) {
+                                       item.Owner.Items.InsertNoOwnerOrLayout (this.Items.IndexOf (this.pre_merge_items[i]), item);
+                                       return;
+                               }
+                       }
+                       
+                       // There aren't any items that are supposed to be after this item,
+                       // so just append it to the end.
+                       item.Owner.Items.AddNoOwnerOrLayout (item);
+               }
                #endregion
        }
 }
index 48cb784c007197ab2e5ed86bc972bf4f96272825..9618935a3b01c13e1749cdabd8010c8901528fff 100644 (file)
@@ -70,6 +70,8 @@ namespace System.Windows.Forms
                internal bool is_pressed;
                private bool is_selected;
                private Padding margin;
+               private MergeAction merge_action;
+               private int merge_index;
                private string name;
                private ToolStripItemOverflow overflow;
                private ToolStrip owner;
@@ -122,6 +124,8 @@ namespace System.Windows.Forms
                        this.image_scaling = ToolStripItemImageScaling.SizeToFit;
                        this.image_transparent_color = Color.Empty;
                        this.margin = this.DefaultMargin;
+                       this.merge_action = MergeAction.Append;
+                       this.merge_index = -1;
                        this.name = name;
                        this.overflow = ToolStripItemOverflow.AsNeeded;
                        this.padding = this.DefaultPadding;
@@ -538,6 +542,23 @@ namespace System.Windows.Forms
                        }
                }
 
+               [DefaultValue (MergeAction.Append)]
+               public MergeAction MergeAction {
+                       get { return this.merge_action; }
+                       set {
+                               if (!Enum.IsDefined (typeof (MergeAction), value))
+                                       throw new InvalidEnumArgumentException (string.Format ("Enum argument value '{0}' is not valid for MergeAction", value));
+                                       
+                               this.merge_action = value;
+                       }
+               }
+
+               [DefaultValue (-1)]
+               public int MergeIndex {
+                       get { return this.merge_index; }
+                       set { this.merge_index = value; }
+               }
+
                [DefaultValue (null)]
                [Browsable (false)]
                public string Name {
index 7bf672ea8cfa02f81f0eca90fc5a3dfe6a8fa3b1..3bf355f85c93e2eada326d1a81d9fcbe184d5c54 100644 (file)
@@ -259,6 +259,22 @@ namespace System.Windows.Forms
                        int index = base.Add (value);
                        return index;
                }
+
+               internal void InsertNoOwnerOrLayout (int index, ToolStripItem value)
+               {
+                       if (value == null)
+                               throw new ArgumentNullException ("value");
+
+                       base.Insert (index, value);
+               }
+
+               internal void RemoveNoOwnerOrLayout (ToolStripItem value)
+               {
+                       if (value == null)
+                               throw new ArgumentNullException ("value");
+
+                       base.Remove (value);
+               }
                #endregion
                
                #region IList Members
index 1b0c7e9cb04a52324f116f90029fbede0daafcf4..76ecadbe3b2c1f331dd09bf08a8864866880de49 100644 (file)
@@ -178,6 +178,222 @@ namespace System.Windows.Forms
                        // Anything else is not a shortcut
                        return false;
                }
+
+               [MonoTODO ("Only supports one level of merging, cannot merge the same ToolStrip multiple times")]
+               public static bool Merge (ToolStrip sourceToolStrip, string targetName)
+               {
+                       if (string.IsNullOrEmpty (targetName))
+                               throw new ArgumentNullException ("targetName");
+                               
+                       return Merge (sourceToolStrip, FindToolStrip (targetName));
+               }
+               
+               [MonoTODO ("Only supports one level of merging, cannot merge the same ToolStrip multiple times")]
+               public static bool Merge (ToolStrip sourceToolStrip, ToolStrip targetToolStrip)
+               {
+                       // Check for exceptions
+                       if (sourceToolStrip == null)
+                               throw new ArgumentNullException ("sourceToolStrip");
+                               
+                       if (targetToolStrip == null)
+                               throw new ArgumentNullException ("targetName");
+                               
+                       if (targetToolStrip == sourceToolStrip)
+                               throw new ArgumentException ("Source and target ToolStrip must be different.");
+                       
+                       // If the toolstrips don't allow merging, don't merge them
+                       if (!sourceToolStrip.AllowMerge || !targetToolStrip.AllowMerge)
+                               return false;
+                       
+                       // We currently can't support merging multiple times
+                       if (sourceToolStrip.IsCurrentlyMerged || targetToolStrip.IsCurrentlyMerged)
+                               return false;
+                               
+                       // What I wouldn't give to be able to modify a collection
+                       // while enumerating through it...
+
+                       List<ToolStripItem> items_to_move = new List<ToolStripItem> ();
+                       
+                       // Create a list of every ToolStripItem we plan on moving
+                       foreach (ToolStripItem tsi in sourceToolStrip.Items) {
+                               switch (tsi.MergeAction) {
+                                       case MergeAction.Append:
+                                       default:
+                                               items_to_move.Add (tsi);
+                                               break;
+                                       case MergeAction.Insert:
+                                               if (tsi.MergeIndex >= 0)
+                                                       items_to_move.Add (tsi);
+                                               break;
+                                       case MergeAction.Replace:
+                                       case MergeAction.Remove:
+                                       case MergeAction.MatchOnly:
+                                               foreach (ToolStripItem target_tsi in targetToolStrip.Items)
+                                                       if (tsi.Text == target_tsi.Text) {
+                                                               items_to_move.Add (tsi);
+                                                               break;
+                                                       }
+                                               break;
+                               }
+                       }
+
+                       // If there was nothing valid to merge, return false
+                       if (items_to_move.Count == 0)
+                               return false;
+                               
+                       // Set some state so we can unmerge later
+                       sourceToolStrip.BeginMerge ();
+                       targetToolStrip.BeginMerge ();                  
+
+                       sourceToolStrip.SuspendLayout ();
+                       targetToolStrip.SuspendLayout ();
+       
+                       while (items_to_move.Count > 0) {
+                               ToolStripItem tsi = items_to_move[0];
+                               items_to_move.Remove (tsi);
+                               
+                               switch (tsi.MergeAction) {
+                                       case MergeAction.Append:
+                                       default:
+                                               // Just changing the parent will append it to the target
+                                               // and remove it from the source
+                                               ToolStrip.SetItemParent (tsi, targetToolStrip);
+                                               
+                                               break;
+                                       case MergeAction.Insert:
+                                               // Do the same work as Append, except Insert it into the
+                                               // location specified by the MergeIndex
+                                               RemoveItemFromParentToolStrip (tsi);
+
+                                               if (tsi.MergeIndex == -1)
+                                                       continue;
+                                               else if (tsi.MergeIndex >= targetToolStrip.Items.Count)
+                                                       targetToolStrip.Items.AddNoOwnerOrLayout (tsi);                                         
+                                               else
+                                                       targetToolStrip.Items.InsertNoOwnerOrLayout (tsi.MergeIndex, tsi);
+
+                                               tsi.Parent = targetToolStrip;
+                                               
+                                               break;
+                                       case MergeAction.Replace:
+                                               // Find a target ToolStripItem with the same Text, remove it
+                                               // and replace it with the source one
+                                               foreach (ToolStripItem target_tsi in targetToolStrip.Items)
+                                                       if (tsi.Text == target_tsi.Text) {
+                                                               RemoveItemFromParentToolStrip (tsi);
+
+                                                               // Insert where the old one is, then remove the old one
+                                                               targetToolStrip.Items.InsertNoOwnerOrLayout (targetToolStrip.Items.IndexOf (target_tsi), tsi);
+                                                               targetToolStrip.Items.RemoveNoOwnerOrLayout (target_tsi);
+
+                                                               // Store the replaced one so we can get it back in unmerge
+                                                               targetToolStrip.HiddenMergedItems.Add (target_tsi);
+                                                               break;
+                                                       }
+
+                                               break;
+                                       case MergeAction.Remove:
+                                               // Find a target ToolStripItem with the same Text, and remove
+                                               // it from the target, nothing else
+                                               foreach (ToolStripItem target_tsi in targetToolStrip.Items)
+                                                       if (tsi.Text == target_tsi.Text) {
+                                                               targetToolStrip.Items.RemoveNoOwnerOrLayout (target_tsi);
+                                                               
+                                                               // Store the removed one so we can get it back in unmerge
+                                                               targetToolStrip.HiddenMergedItems.Add (target_tsi);
+                                                               break;
+                                                       }
+
+                                               break;
+                                       case MergeAction.MatchOnly:
+                                               // Ugh, find the target ToolStripItem with the same Text, and take
+                                               // all the subitems from the source one, and append it to the target one
+                                               foreach (ToolStripItem target_tsi in targetToolStrip.Items)
+                                                       if (tsi.Text == target_tsi.Text) {
+                                                               if (target_tsi is ToolStripMenuItem && tsi is ToolStripMenuItem) {
+                                                                       ToolStripMenuItem source = (ToolStripMenuItem)tsi;
+                                                                       ToolStripMenuItem target = (ToolStripMenuItem)target_tsi;
+                                                                       
+                                                                       while (source.DropDownItems.Count > 0)
+                                                                               ToolStrip.SetItemParent (source.DropDownItems[0], target.DropDown);
+                                                               }
+                                                               
+                                                               break;
+                                                       }
+                                               
+                                               break;
+                               }
+                       }
+                       
+                       sourceToolStrip.ResumeLayout ();
+                       targetToolStrip.ResumeLayout ();
+                       
+                       // Store who we merged with, so we can unmerge when only given the target toolstrip
+                       sourceToolStrip.CurrentlyMergedWith = targetToolStrip;
+                       targetToolStrip.CurrentlyMergedWith = sourceToolStrip;
+                       
+                       return true;
+               }
+
+               public static bool RevertMerge (string targetName)
+               {
+                       return RevertMerge (FindToolStrip (targetName));
+               }
+               
+               public static bool RevertMerge (ToolStrip targetToolStrip)
+               {
+                       return RevertMerge (targetToolStrip, targetToolStrip.CurrentlyMergedWith);                      
+               }
+               
+               public static bool RevertMerge (ToolStrip targetToolStrip, ToolStrip sourceToolStrip)
+               {
+                       if (sourceToolStrip == null)
+                               throw new ArgumentNullException ("sourceToolStrip");
+                               
+                       List<ToolStripItem> items_to_move = new List<ToolStripItem> ();
+                       
+                       // Find every ToolStripItem who's Owner is the source toolstrip
+                       // - If it's a TSMI, see if any of the subitems need to be moved back
+                       foreach (ToolStripItem tsi in targetToolStrip.Items) {
+                               if (tsi.Owner == sourceToolStrip)
+                                       items_to_move.Add (tsi);
+                               else if (tsi is ToolStripMenuItem)
+                                       foreach (ToolStripMenuItem menuitem in (tsi as ToolStripMenuItem).DropDownItems)
+                                               foreach (ToolStripMenuItem tsmi in sourceToolStrip.Items)
+                                                       if (menuitem.Owner == tsmi.DropDown)
+                                                               items_to_move.Add (menuitem);   
+                       }
+
+                       // If we didn't find anything, return false
+                       if (items_to_move.Count == 0 && targetToolStrip.HiddenMergedItems.Count == 0)
+                               return false;
+
+                       // Put back all the target's items removed in the merge
+                       while (targetToolStrip.HiddenMergedItems.Count > 0) {
+                               targetToolStrip.RevertMergeItem (targetToolStrip.HiddenMergedItems[0]);
+                               targetToolStrip.HiddenMergedItems.RemoveAt (0);
+                       }
+                               
+                       sourceToolStrip.SuspendLayout ();
+                       targetToolStrip.SuspendLayout ();
+                       
+                       // Revert everything
+                       while (items_to_move.Count > 0) {
+                               sourceToolStrip.RevertMergeItem (items_to_move[0]);
+                               items_to_move.Remove (items_to_move[0]);
+                       }
+
+                       sourceToolStrip.ResumeLayout ();
+                       targetToolStrip.ResumeLayout ();
+                       
+                       sourceToolStrip.IsCurrentlyMerged = false;
+                       targetToolStrip.IsCurrentlyMerged = false;
+
+                       sourceToolStrip.CurrentlyMergedWith = null;
+                       targetToolStrip.CurrentlyMergedWith = null;
+
+                       return true;
+               }
                #endregion
                
                #region Public Events
@@ -239,6 +455,16 @@ namespace System.Windows.Forms
                        if (RendererChanged != null) RendererChanged (null, e);
                }
 
+               private static void RemoveItemFromParentToolStrip (ToolStripItem tsi)
+               {
+                       if (tsi.Parent != null) {
+                               tsi.Parent.Items.RemoveNoOwnerOrLayout (tsi);
+
+                               if (tsi.Parent is ToolStripOverflow)
+                                       (tsi.Parent as ToolStripOverflow).ParentToolStrip.Items.RemoveNoOwnerOrLayout (tsi);
+                       }
+               }
+               
                internal static event EventHandler AppClicked;
                internal static event EventHandler AppFocusChange;
                #endregion
index c5efa65608b75094f83effd466b07d1be455d23d..cffc469efdfa0a6bc163e852923a92f4df25a2b1 100644 (file)
@@ -126,6 +126,12 @@ namespace System.Windows.Forms
                        this.PerformLayout ();
                }
                #endregion
+
+               #region Internal Methods
+               internal ToolStrip ParentToolStrip {
+                       get { return (ToolStrip)this.OwnerItem.Parent; }
+               }
+               #endregion
        }
 }
 #endif
\ No newline at end of file