using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms.Layout;
+using System.Collections.Generic;
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;
this.SuspendLayout ();
this.items = new ToolStripItemCollection (this, items);
+ this.allow_merge = true;
base.AutoSize = true;
this.back_color = Control.DefaultBackColor;
this.can_overflow = true;
#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; }
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;
}
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
}
}
// 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
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