diff options
Diffstat (limited to 'source/ShiftUI/Menu')
| -rw-r--r-- | source/ShiftUI/Menu/Menu.cs | 621 | ||||
| -rw-r--r-- | source/ShiftUI/Menu/MenuAPI.cs | 836 | ||||
| -rw-r--r-- | source/ShiftUI/Menu/MenuGlyph.cs | 36 | ||||
| -rw-r--r-- | source/ShiftUI/Menu/MenuItem.cs | 912 | ||||
| -rw-r--r-- | source/ShiftUI/Menu/MenuMerge.cs | 38 | ||||
| -rw-r--r-- | source/ShiftUI/Menu/MenuStrip.cs | 349 |
6 files changed, 2792 insertions, 0 deletions
diff --git a/source/ShiftUI/Menu/Menu.cs b/source/ShiftUI/Menu/Menu.cs new file mode 100644 index 0000000..0e04d70 --- /dev/null +++ b/source/ShiftUI/Menu/Menu.cs @@ -0,0 +1,621 @@ +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +// Copyright (c) 2004-2005 Novell, Inc. +// +// Authors: +// Jordi Mas i Hernandez, [email protected] +// +// TODO: +// - FindMenuItem +// - MdiListItem +// + +using System.Collections; +using System.ComponentModel; +using System.ComponentModel.Design; +using System.Reflection; +using System.Runtime.InteropServices; +using System.Collections.Generic; +using System.Drawing; +using System; + +namespace ShiftUI +{ + [ToolboxItemFilter("ShiftUI", ToolboxItemFilterType.Allow)] + [ListBindable(false)] + public abstract class Menu : Component + { + internal MenuItemCollection menu_items; + internal IntPtr menu_handle = IntPtr.Zero; + internal Menu parent_menu = null; + System.Drawing.Rectangle rect = new Rectangle (); + // UIA Framework Note: Used to keep track of expanded menus + internal Widget Wnd; + internal MenuTracker tracker; + private string control_name; + private object control_tag; + public const int FindHandle = 0; + public const int FindShortcut = 1; + + protected Menu (MenuItem[] items) + { + menu_items = new MenuItemCollection (this); + + if (items != null) + menu_items.AddRange (items); + } + + #region Public Properties + + [BrowsableAttribute(false)] + //[EditorBrowsableAttribute(EditorBrowsableState.Advanced)] + //[DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Hidden)] + public IntPtr Handle { + get { return menu_handle; } + } + + internal virtual void OnMenuChanged (EventArgs e) + { + EventHandler eh = (EventHandler)(Events [MenuChangedEvent]); + if (eh != null) + eh (this, e); + } + + [BrowsableAttribute(false)] + //[DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Hidden)] + public virtual bool IsParent { + get { + if (menu_items != null && menu_items.Count > 0) + return true; + else + return false; + } + } + + [BrowsableAttribute(false)] + //[DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Hidden)] + public MenuItem MdiListItem { + get { + throw new NotImplementedException (); + } + } + + [BrowsableAttribute(false)] + //[DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Content)] + [MergableProperty(false)] + public MenuItemCollection MenuItems { + get { return menu_items; } + } + + [BrowsableAttribute(false)] + //[DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Hidden)] + public string Name { + get { return control_name; } + set { control_name = value; } + } + + [Localizable(false)] + [Bindable(true)] + [TypeConverter(typeof(StringConverter))] + [DefaultValue(null)] + [MWFCategory("Data")] + public object Tag { + get { return control_tag; } + set { control_tag = value; } + } + + #endregion Public Properties + + #region Private Properties + + // UIA Framework Note: Used to obtain menu bounds + internal Rectangle Rect { + get { return rect; } + } + + internal MenuItem SelectedItem { + get { + foreach (MenuItem item in MenuItems) + if (item.Selected) + return item; + + return null; + } + } + + internal int Height { + get { return rect.Height; } + set { rect.Height = value; } + } + + internal int Width { + get { return rect.Width; } + set { rect.Width = value; } + } + + internal int X { + get { return rect.X; } + set { rect.X = value; } + } + + internal int Y { + get { return rect.Y; } + set { rect.Y = value; } + } + + internal MenuTracker Tracker { + get { + Menu top = this; + while (top.parent_menu != null) + top = top.parent_menu; + + return top.tracker; + } + } + #endregion Private Properties + + #region Public Methods + + protected void CloneMenu (Menu menuSrc) + { + Dispose (true); + + menu_items = new MenuItemCollection (this); + + for (int i = 0; i < menuSrc.MenuItems.Count ; i++) + menu_items.Add (menuSrc.MenuItems [i].CloneMenu ()); + } + + protected virtual IntPtr CreateMenuHandle () + { + return IntPtr.Zero; + } + + protected override void Dispose (bool disposing) + { + if (disposing) { + if (menu_items != null) { + // MenuItem.Dispose removes the item from the list + while (menu_items.Count > 0) { + menu_items [0].Dispose (); + } + } + if (menu_handle != IntPtr.Zero) { + menu_handle = IntPtr.Zero; + } + } + } + + // From Microsoft documentation is impossible to guess that + // this method is supossed to do + // + // update: according to MS documentation, first parameter is on of this + // constant values FindHandle or FindShortcut, value depends from what + // you what to search, by shortcut or handle. FindHandle and FindShortcut + // is a constant fields and was defined for this class. + public MenuItem FindMenuItem (int type, IntPtr value) + { + return null; + } + + protected int FindMergePosition (int mergeOrder) + { + int cnt = MenuItems.Count, cur, pos; + + for (pos = 0; pos < cnt; ) { + cur = (pos + cnt) /2; + if (MenuItems[cur].MergeOrder > mergeOrder) { + cnt = cur; + } else { + pos = cur +1; + } + } + + return pos; + } + + public MainMenu GetMainMenu () + { + for (Menu item = this; item != null; item = item.parent_menu) { + if (item is MainMenu) { + return (MainMenu) item; + } + } + + return null; + } + + internal virtual void InvalidateItem (MenuItem item) + { + if (Wnd != null) + Wnd.Invalidate (item.bounds); + } + + public virtual void MergeMenu (Menu menuSrc) + { + if (menuSrc == this) + throw new ArgumentException ("The menu cannot be merged with itself"); + + if (menuSrc == null) + return; + + for (int i = 0; i < menuSrc.MenuItems.Count; i++) { + + MenuItem sourceitem = menuSrc.MenuItems[i]; + + switch (sourceitem.MergeType) { + case MenuMerge.Remove: // Item not included + break; + + case MenuMerge.Add: + { + int pos = FindMergePosition (sourceitem.MergeOrder); + MenuItems.Add (pos, sourceitem.CloneMenu ()); + break; + } + + case MenuMerge.Replace: + case MenuMerge.MergeItems: + { + for (int pos = FindMergePosition (sourceitem.MergeOrder-1); pos <= MenuItems.Count; pos++) { + + if ((pos >= MenuItems.Count) || (MenuItems[pos].MergeOrder != sourceitem.MergeOrder)) { + MenuItems.Add (pos, sourceitem.CloneMenu ()); + break; + } + + MenuItem mergeitem = MenuItems[pos]; + + if (mergeitem.MergeType != MenuMerge.Add) { + if ((sourceitem.MergeType == MenuMerge.MergeItems) && (mergeitem.MergeType == MenuMerge.MergeItems)) { + mergeitem.MergeMenu (sourceitem); + } else { + MenuItems.Remove (sourceitem); + MenuItems.Add (pos, sourceitem.CloneMenu ()); + } + break; + } + } + + break; + } + + default: + break; + } + } + } + + protected internal virtual bool ProcessCmdKey (ref Message msg, Keys keyData) + { + if (tracker == null) + return false; + return tracker.ProcessKeys (ref msg, keyData); + } + + public override string ToString () + { + return base.ToString () + ", Items.Count: " + MenuItems.Count; + } + + #endregion Public Methods + static object MenuChangedEvent = new object (); + + // UIA Framework Note: Used to track changes in MenuItemCollection + internal event EventHandler MenuChanged { + add { Events.AddHandler (MenuChangedEvent, value); } + remove { Events.RemoveHandler (MenuChangedEvent, value); } + } + + [ListBindable(false)] + public class MenuItemCollection : IList, ICollection, IEnumerable + { + private Menu owner; + private ArrayList items = new ArrayList (); + + public MenuItemCollection (Menu owner) + { + this.owner = owner; + } + + #region Public Properties + + public int Count { + get { return items.Count;} + } + + public bool IsReadOnly { + get { return false; } + } + + bool ICollection.IsSynchronized { + get { return false;} + } + + object ICollection.SyncRoot { + get { return this;} + } + + bool IList.IsFixedSize { + get { return false;} + } + + public virtual MenuItem this [int index] { + get { + if (index < 0 || index >= Count) + throw new ArgumentOutOfRangeException ("Index of out range"); + + return (MenuItem) items[index]; + } + } + + public virtual MenuItem this [string key] { + get { + if (string.IsNullOrEmpty (key)) + return null; + + foreach (MenuItem m in items) + if (string.Compare (m.Name, key, true) == 0) + return m; + + return null; + } + } + + object IList.this[int index] { + get { return items[index]; } + set { throw new NotSupportedException (); } + } + + #endregion Public Properties + + #region Public Methods + + public virtual int Add (MenuItem item) + { + if (item.Parent != null) + item.Parent.MenuItems.Remove (item); + + items.Add (item); + item.Index = items.Count - 1; + UpdateItem (item); + + owner.OnMenuChanged (EventArgs.Empty); + if (owner.parent_menu != null) + owner.parent_menu.OnMenuChanged (EventArgs.Empty); + return items.Count - 1; + } + + internal void AddNoEvents (MenuItem mi) + { + if (mi.Parent != null) + mi.Parent.MenuItems.Remove (mi); + + items.Add (mi); + mi.Index = items.Count - 1; + mi.parent_menu = owner; + } + + public virtual MenuItem Add (string caption) + { + MenuItem item = new MenuItem (caption); + Add (item); + return item; + } + + public virtual int Add (int index, MenuItem item) + { + if (index < 0 || index > Count) + throw new ArgumentOutOfRangeException ("Index of out range"); + + ArrayList new_items = new ArrayList (Count + 1); + + for (int i = 0; i < index; i++) + new_items.Add (items[i]); + + new_items.Add (item); + + for (int i = index; i < Count; i++) + new_items.Add (items[i]); + + items = new_items; + UpdateItemsIndices (); + UpdateItem (item); + + return index; + } + + private void UpdateItem (MenuItem mi) + { + mi.parent_menu = owner; + owner.OnMenuChanged (EventArgs.Empty); + if (owner.parent_menu != null) + owner.parent_menu.OnMenuChanged (EventArgs.Empty); + if (owner.Tracker != null) + owner.Tracker.AddShortcuts (mi); + } + + internal void Insert (int index, MenuItem mi) + { + if (index < 0 || index > Count) + throw new ArgumentOutOfRangeException ("Index of out range"); + + items.Insert (index, mi); + + UpdateItemsIndices (); + UpdateItem (mi); + } + + public virtual MenuItem Add (string caption, EventHandler onClick) + { + MenuItem item = new MenuItem (caption, onClick); + Add (item); + + return item; + } + + public virtual MenuItem Add (string caption, MenuItem[] items) + { + MenuItem item = new MenuItem (caption, items); + Add (item); + + return item; + } + + public virtual void AddRange (MenuItem[] items) + { + if (items == null) + throw new ArgumentNullException ("items"); + + foreach (MenuItem mi in items) + Add (mi); + } + + public virtual void Clear () + { + MenuTracker tracker = owner.Tracker; + foreach (MenuItem item in items) { + if (tracker != null) + tracker.RemoveShortcuts (item); + item.parent_menu = null; + } + items.Clear (); + owner.OnMenuChanged (EventArgs.Empty); + } + + public bool Contains (MenuItem value) + { + return items.Contains (value); + } + + public virtual bool ContainsKey (string key) + { + return !(this[key] == null); + } + + public void CopyTo (Array dest, int index) + { + items.CopyTo (dest, index); + } + + public MenuItem[] Find (string key, bool searchAllChildren) + { + if (string.IsNullOrEmpty (key)) + throw new ArgumentNullException ("key"); + + List<MenuItem> list = new List<MenuItem> (); + + foreach (MenuItem m in items) + if (string.Compare (m.Name, key, true) == 0) + list.Add (m); + + if (searchAllChildren) + foreach (MenuItem m in items) + list.AddRange (m.MenuItems.Find (key, true)); + + return list.ToArray (); + } + + public IEnumerator GetEnumerator () + { + return items.GetEnumerator (); + } + + int IList.Add (object value) + { + return Add ((MenuItem)value); + } + + bool IList.Contains (object value) + { + return Contains ((MenuItem)value); + } + + int IList.IndexOf (object value) + { + return IndexOf ((MenuItem)value); + } + + void IList.Insert (int index, object value) + { + Insert (index, (MenuItem) value); + } + + void IList.Remove (object value) + { + Remove ((MenuItem) value); + } + + public int IndexOf (MenuItem value) + { + return items.IndexOf (value); + } + + public virtual int IndexOfKey (string key) + { + if (string.IsNullOrEmpty (key)) + return -1; + + return IndexOf (this[key]); + } + + public virtual void Remove (MenuItem item) + { + RemoveAt (item.Index); + } + + public virtual void RemoveAt (int index) + { + if (index < 0 || index >= Count) + throw new ArgumentOutOfRangeException ("Index of out range"); + + MenuItem item = (MenuItem) items [index]; + MenuTracker tracker = owner.Tracker; + if (tracker != null) + tracker.RemoveShortcuts (item); + item.parent_menu = null; + + items.RemoveAt (index); + + UpdateItemsIndices (); + owner.OnMenuChanged (EventArgs.Empty); + } + + public virtual void RemoveByKey (string key) + { + Remove (this[key]); + } + + #endregion Public Methods + + #region Private Methods + + private void UpdateItemsIndices () + { + for (int i = 0; i < Count; i++) // Recalculate indeces + ((MenuItem)items[i]).Index = i; + } + + #endregion Private Methods + } + } +} + + diff --git a/source/ShiftUI/Menu/MenuAPI.cs b/source/ShiftUI/Menu/MenuAPI.cs new file mode 100644 index 0000000..79a7a1d --- /dev/null +++ b/source/ShiftUI/Menu/MenuAPI.cs @@ -0,0 +1,836 @@ +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +// Copyright (c) 2004-2005 Novell, Inc. +// +// Authors: +// Jordi Mas i Hernandez, [email protected] +// Mike Kestner <[email protected]> +// Everaldo Canuto <[email protected]> +// + +using System.Collections; +using System.Drawing; +using System.Threading; +using System; + +namespace ShiftUI { + + /* + When writing this code the Wine project was of great help to + understand the logic behind some Win32 issues. Thanks to them. Jordi, + */ + // UIA Framework Note: This class used by UIA for its mouse action methods. + internal class MenuTracker { + + internal bool active; + internal bool popup_active; + internal bool popdown_menu; + internal bool hotkey_active; + private bool mouse_down = false; + public Menu CurrentMenu; + public Menu TopMenu; + public Widget GrabControl; + Point last_motion = Point.Empty; + + public MenuTracker (Menu top_menu) + { + TopMenu = CurrentMenu = top_menu; + foreach (MenuItem item in TopMenu.MenuItems) + AddShortcuts (item); + } + + enum KeyNavState { + Idle, + Startup, + NoPopups, + Navigating + } + + KeyNavState keynav_state = KeyNavState.Idle; + + public bool Navigating { + get { return keynav_state != KeyNavState.Idle || active; } + } + + internal static Point ScreenToMenu (Menu menu, Point pnt) + { + int x = pnt.X; + int y = pnt.Y; + XplatUI.ScreenToMenu (menu.Wnd.window.Handle, ref x, ref y); + return new Point (x, y); + } + + private void UpdateCursor () + { + Widget child_control = GrabControl.GetRealChildAtPoint (Cursor.Position); + if (child_control != null) { + if (active) + XplatUI.SetCursor (child_control.Handle, Cursors.Default.handle); + else + XplatUI.SetCursor (child_control.Handle, child_control.Cursor.handle); + } + } + + internal void Deactivate () + { + bool redrawbar = (keynav_state != KeyNavState.Idle) && (TopMenu is MainMenu); + + active = false; + popup_active = false; + hotkey_active = false; + if (GrabControl != null) + GrabControl.ActiveTracker = null; + keynav_state = KeyNavState.Idle; + CurrentMenu = TopMenu; + + if (redrawbar) + (TopMenu as MainMenu).Draw (); + } + + MenuItem FindItemByCoords (Menu menu, Point pt) + { + if (menu is MainMenu) + pt = ScreenToMenu (menu, pt); + else { + if (menu.Wnd == null) { + return null; + } + pt = menu.Wnd.PointToClient (pt); + } + foreach (MenuItem item in menu.MenuItems) { + Rectangle rect = item.bounds; + if (rect.Contains (pt)) + return item; + } + + return null; + } + + MenuItem GetItemAtXY (int x, int y) + { + Point pnt = new Point (x, y); + MenuItem item = null; + return item; + } + + // UIA Framework Note: Used to expand/collapse MenuItems + public bool OnMouseDown (MouseEventArgs args) + { + MenuItem item = GetItemAtXY (args.X, args.Y); + + mouse_down = true; + + if (item == null) { + Deactivate (); + return false; + } + + if ((args.Button & MouseButtons.Left) == 0) + return true; + + if (!item.Enabled) + return true; + + popdown_menu = active && item.VisibleItems; + + if (item.IsPopup || (item.Parent is MainMenu)) { + active = true; + item.Parent.InvalidateItem (item); + } + + if ((CurrentMenu == TopMenu) && !popdown_menu) + SelectItem (item.Parent, item, item.IsPopup); + + GrabControl.ActiveTracker = this; + return true; + } + + // UIA Framework Note: Used to select MenuItems + public void OnMotion (MouseEventArgs args) + { + // Windows helpfully sends us MOUSEMOVE messages when any key is pressed. + // So if the mouse hasn't actually moved since the last MOUSEMOVE, ignore it. + if (args.Location == last_motion) + return; + + last_motion = args.Location; + + MenuItem item = GetItemAtXY (args.X, args.Y); + + UpdateCursor (); + + if (CurrentMenu.SelectedItem == item) + return; + + GrabControl.ActiveTracker = (active || item != null) ? this : null; + + if (item == null) { + MenuItem old_item = CurrentMenu.SelectedItem; + + // Return when is a popup with visible subitems for MainMenu + if ((active && old_item.VisibleItems && old_item.IsPopup && (CurrentMenu is MainMenu))) + return; + + // Also returns when keyboard navigating + if (keynav_state == KeyNavState.Navigating) + return; + + // Select parent menu when move outside of menu item + if (old_item.Parent is MenuItem) { + MenuItem new_item = (old_item.Parent as MenuItem); + if (new_item.IsPopup) { + SelectItem (new_item.Parent, new_item, false); + return; + } + } + if (CurrentMenu != TopMenu) + CurrentMenu = CurrentMenu.parent_menu; + + DeselectItem (old_item); + } else { + keynav_state = KeyNavState.Idle; + SelectItem (item.Parent, item, active && item.IsPopup && popup_active && (CurrentMenu.SelectedItem != item)); + } + } + + // UIA Framework Note: Used to expand/collapse MenuItems + public void OnMouseUp (MouseEventArgs args) + { + /* mouse down dont comes from menu */ + if (!mouse_down) + return; + + mouse_down = false; + + /* is not left button */ + if ((args.Button & MouseButtons.Left) == 0) + return; + + MenuItem item = GetItemAtXY (args.X, args.Y); + + /* the user released the mouse button outside the menu */ + if (item == null) { + Deactivate (); + return; + } + + if (!item.Enabled) + return; + + /* Perform click when is not a popup */ + if (!item.IsPopup) { + DeselectItem (item); + + // Raise the form's MenuComplete event + if (TopMenu != null && TopMenu.Wnd != null) { + Form f = TopMenu.Wnd.FindForm (); + + if (f != null) + f.OnMenuComplete (EventArgs.Empty); + } + + item.PerformClick (); + } + } + + static public bool TrackPopupMenu (Menu menu, Point pnt) + { + return true; + } + + void DeselectItem (MenuItem item) + { + if (item == null) + return; + + item.Selected = false; + + /* When popup item then close all sub popups and unselect all sub items */ + if (item.IsPopup) { + HideSubPopups (item, TopMenu); + + /* Unselect all selected sub itens */ + foreach (MenuItem subitem in item.MenuItems) + if (subitem.Selected) + DeselectItem (subitem); + } + + Menu menu = item.Parent; + menu.InvalidateItem (item); + } + + void SelectItem (Menu menu, MenuItem item, bool execute) + { + MenuItem prev_item = CurrentMenu.SelectedItem; + + if (prev_item != item.Parent) { + DeselectItem (prev_item); + if ((CurrentMenu != menu) && (prev_item.Parent != item) && (prev_item.Parent is MenuItem)) { + DeselectItem (prev_item.Parent as MenuItem); + } + } + + if (CurrentMenu != menu) + CurrentMenu = menu; + + item.Selected = true; + menu.InvalidateItem (item); + + if (((CurrentMenu == TopMenu) && execute) || ((CurrentMenu != TopMenu) && popup_active)) + item.PerformSelect (); + + if ((execute) && ((prev_item == null) || (item != prev_item.Parent))) + ExecFocusedItem (menu, item); + } + + // Used when the user executes the action of an item (press enter, shortcut) + // or a sub-popup menu has to be shown + void ExecFocusedItem (Menu menu, MenuItem item) + { + if (item == null) + return; + + if (!item.Enabled) + return; + + if (item.IsPopup) { + ShowSubPopup (menu, item); + } else { + Deactivate (); + item.PerformClick (); + } + } + + // Create a popup window and show it or only show it if it is already created + void ShowSubPopup (Menu menu, MenuItem item) + { + if (item.Enabled == false) + return; + + if (!popdown_menu || !item.VisibleItems) + item.PerformPopup (); + + if (item.VisibleItems == false) + return; + + if (item.Wnd != null) { + item.Wnd.Dispose (); + } + + popup_active = true; + PopUpWindow puw = new PopUpWindow (GrabControl, item); + + Point pnt; + if (menu is MainMenu) + pnt = new Point (item.X, item.Y + item.Height - 2 - menu.Height); + else + pnt = new Point (item.X + item.Width - 3, item.Y - 3); + pnt = menu.Wnd.PointToScreen (pnt); + puw.Location = pnt; + item.Wnd = puw; + + puw.ShowWindow (); + } + + static public void HideSubPopups (Menu menu, Menu topmenu) + { + foreach (MenuItem item in menu.MenuItems) + if (item.IsPopup) + HideSubPopups (item, null); + + if (menu.Wnd == null) + return; + + PopUpWindow puw = menu.Wnd as PopUpWindow; + if (puw != null) { + puw.Hide (); + puw.Dispose (); + } + menu.Wnd = null; + + if ((topmenu != null) && (topmenu is MainMenu)) + ((MainMenu) topmenu).OnCollapse (EventArgs.Empty); + } + + MenuItem FindSubItemByCoord (Menu menu, Point pnt) + { + foreach (MenuItem item in menu.MenuItems) { + + if (item.IsPopup && item.Wnd != null && item.Wnd.Visible && item == menu.SelectedItem) { + MenuItem result = FindSubItemByCoord (item, pnt); + if (result != null) + return result; + } + + if (menu.Wnd == null || !menu.Wnd.Visible) + continue; + + Rectangle rect = item.bounds; + Point pnt_client = menu.Wnd.PointToScreen (new Point (item.X, item.Y)); + rect.X = pnt_client.X; + rect.Y = pnt_client.Y; + + if (rect.Contains (pnt) == true) + return item; + } + + return null; + } + + static MenuItem FindItemByKey (Menu menu, IntPtr key) + { + char key_char = Char.ToUpper ((char) (key.ToInt32() & 0xff)); + foreach (MenuItem item in menu.MenuItems) { + if (item.Mnemonic == key_char) + return item; + } + + string key_str = key_char.ToString (); + foreach (MenuItem item in menu.MenuItems) { + //if (item.Mnemonic == key_char) + if (item.Text.StartsWith (key_str)) + return item; + } + + return null; + } + + enum ItemNavigation { + First, + Last, + Next, + Previous, + } + + static MenuItem GetNextItem (Menu menu, ItemNavigation navigation) + { + int pos = 0; + bool selectable_items = false; + MenuItem item; + + // Check if there is at least a selectable item + for (int i = 0; i < menu.MenuItems.Count; i++) { + item = menu.MenuItems [i]; + if (item.Separator == false && item.Visible == true) { + selectable_items = true; + break; + } + } + + if (selectable_items == false) + return null; + + switch (navigation) { + case ItemNavigation.First: + + /* First item that is not separator and it is visible*/ + for (pos = 0; pos < menu.MenuItems.Count; pos++) { + item = menu.MenuItems [pos]; + if (item.Separator == false && item.Visible == true) + break; + } + + break; + + case ItemNavigation.Last: // Not used + break; + + case ItemNavigation.Next: + + pos = menu.SelectedItem == null ? - 1 : menu.SelectedItem.Index; + + /* Next item that is not separator and it is visible*/ + for (pos++; pos < menu.MenuItems.Count; pos++) { + item = menu.MenuItems [pos]; + if (item.Separator == false && item.Visible == true) + break; + } + + if (pos >= menu.MenuItems.Count) { /* Jump at the start of the menu */ + pos = 0; + /* Next item that is not separator and it is visible*/ + for (; pos < menu.MenuItems.Count; pos++) { + item = menu.MenuItems [pos]; + if (item.Separator == false && item.Visible == true) + break; + } + } + break; + + case ItemNavigation.Previous: + + if (menu.SelectedItem != null) + pos = menu.SelectedItem.Index; + + /* Previous item that is not separator and it is visible*/ + for (pos--; pos >= 0; pos--) { + item = menu.MenuItems [pos]; + if (item.Separator == false && item.Visible == true) + break; + } + + if (pos < 0 ) { /* Jump at the end of the menu*/ + pos = menu.MenuItems.Count - 1; + /* Previous item that is not separator and it is visible*/ + for (; pos >= 0; pos--) { + item = menu.MenuItems [pos]; + if (item.Separator == false && item.Visible == true) + break; + } + } + + break; + + default: + break; + } + + return menu.MenuItems [pos]; + } + + void ProcessMenuKey (Msg msg_type) + { + if (TopMenu.MenuItems.Count == 0) + return; + + MainMenu main_menu = TopMenu as MainMenu; + + switch (msg_type) { + case Msg.WM_SYSKEYDOWN: + switch (keynav_state) { + case KeyNavState.Idle: + keynav_state = KeyNavState.Startup; + hotkey_active = true; + GrabControl.ActiveTracker = this; + CurrentMenu = TopMenu; + main_menu.Draw (); + break; + case KeyNavState.Startup: + break; + default: + Deactivate (); + main_menu.Draw (); + break; + } + break; + + case Msg.WM_SYSKEYUP: + switch (keynav_state) { + case KeyNavState.Idle: + case KeyNavState.Navigating: + break; + case KeyNavState.Startup: + keynav_state = KeyNavState.NoPopups; + SelectItem (TopMenu, TopMenu.MenuItems [0], false); + break; + default: + Deactivate (); + main_menu.Draw (); + break; + } + break; + } + } + + bool ProcessMnemonic (Message msg, Keys key_data) + { + keynav_state = KeyNavState.Navigating; + MenuItem item = FindItemByKey (CurrentMenu, msg.WParam); + if ((item == null) || (GrabControl == null) || (GrabControl.ActiveTracker == null)) + return false; + + active = true; + GrabControl.ActiveTracker = this; + + SelectItem (CurrentMenu, item, true); + if (item.IsPopup) { + CurrentMenu = item; + SelectItem (item, item.MenuItems [0], false); + } + return true; + } + + Hashtable shortcuts = new Hashtable (); + + public void AddShortcuts (MenuItem item) + { + foreach (MenuItem child in item.MenuItems) { + AddShortcuts (child); + if (child.Shortcut != Shortcut.None) + shortcuts [(int)child.Shortcut] = child; + } + + if (item.Shortcut != Shortcut.None) + shortcuts [(int)item.Shortcut] = item; + } + + public void RemoveShortcuts (MenuItem item) + { + foreach (MenuItem child in item.MenuItems) { + RemoveShortcuts (child); + if (child.Shortcut != Shortcut.None) + shortcuts.Remove ((int)child.Shortcut); + } + + if (item.Shortcut != Shortcut.None) + shortcuts.Remove ((int)item.Shortcut); + } + + bool ProcessShortcut (Keys keyData) + { + MenuItem item = shortcuts [(int)keyData] as MenuItem; + if (item == null || !item.Enabled) + return false; + + if (active) + Deactivate (); + item.PerformClick (); + return true; + } + + public bool ProcessKeys (ref Message msg, Keys keyData) + { + // We should process Alt+key only if we don't have an active menu, + // and hide it otherwise. + if ((keyData & Keys.Alt) == Keys.Alt && active) { + Deactivate (); + return false; + } + + // If we get Alt-F4, Windows will ignore it because we have a capture, + // release the capture and the program will exit. (X11 doesn't care.) + if ((keyData & Keys.Alt) == Keys.Alt && (keyData & Keys.F4) == Keys.F4) { + if (GrabControl != null) + GrabControl.ActiveTracker = null; + + return false; + } + + if ((Msg)msg.Msg != Msg.WM_SYSKEYUP && ProcessShortcut (keyData)) + return true; + else if ((keyData & Keys.KeyCode) == Keys.Menu && TopMenu is MainMenu) { + ProcessMenuKey ((Msg) msg.Msg); + return true; + } else if ((keyData & Keys.Alt) == Keys.Alt) + return ProcessMnemonic (msg, keyData); + else if ((Msg)msg.Msg == Msg.WM_SYSKEYUP) + return false; + else if (!Navigating) + return false; + + MenuItem item; + + switch (keyData) { + case Keys.Up: + if (CurrentMenu is MainMenu) + return true; + else if (CurrentMenu.MenuItems.Count == 1 && CurrentMenu.parent_menu == TopMenu) { + DeselectItem (CurrentMenu.SelectedItem); + CurrentMenu = TopMenu; + return true; + } + item = GetNextItem (CurrentMenu, ItemNavigation.Previous); + if (item != null) + SelectItem (CurrentMenu, item, false); + break; + + case Keys.Down: + if (CurrentMenu is MainMenu) { + if (CurrentMenu.SelectedItem != null && CurrentMenu.SelectedItem.IsPopup) { + keynav_state = KeyNavState.Navigating; + item = CurrentMenu.SelectedItem; + ShowSubPopup (CurrentMenu, item); + SelectItem (item, item.MenuItems [0], false); + CurrentMenu = item; + active = true; + GrabControl.ActiveTracker = this; + } + return true; + } + item = GetNextItem (CurrentMenu, ItemNavigation.Next); + if (item != null) + SelectItem (CurrentMenu, item, false); + break; + + case Keys.Right: + if (CurrentMenu is MainMenu) { + item = GetNextItem (CurrentMenu, ItemNavigation.Next); + bool popup = item.IsPopup && keynav_state != KeyNavState.NoPopups; + SelectItem (CurrentMenu, item, popup); + if (popup) { + SelectItem (item, item.MenuItems [0], false); + CurrentMenu = item; + } + } else if (CurrentMenu.SelectedItem != null && CurrentMenu.SelectedItem.IsPopup) { + item = CurrentMenu.SelectedItem; + ShowSubPopup (CurrentMenu, item); + SelectItem (item, item.MenuItems [0], false); + CurrentMenu = item; + } else { + //Search up for a main menu + Menu Prnt = CurrentMenu.parent_menu; + while (Prnt != null && !(Prnt is MainMenu)) { + Prnt = Prnt.parent_menu; + } + if (Prnt is MainMenu) + { + item = GetNextItem(Prnt, ItemNavigation.Next); + SelectItem(Prnt, item, item.IsPopup); + if (item.IsPopup) + { + SelectItem(item, item.MenuItems[0], false); + CurrentMenu = item; + } + } + } + break; + + case Keys.Left: + if (CurrentMenu is MainMenu) { + item = GetNextItem (CurrentMenu, ItemNavigation.Previous); + bool popup = item.IsPopup && keynav_state != KeyNavState.NoPopups; + SelectItem (CurrentMenu, item, popup); + if (popup) { + SelectItem (item, item.MenuItems [0], false); + CurrentMenu = item; + } + } else if (CurrentMenu.parent_menu is MainMenu) { + item = GetNextItem (CurrentMenu.parent_menu, ItemNavigation.Previous); + SelectItem (CurrentMenu.parent_menu, item, item.IsPopup); + if (item.IsPopup) { + SelectItem (item, item.MenuItems [0], false); + CurrentMenu = item; + } + } + break; + + case Keys.Return: + if (CurrentMenu.SelectedItem != null && CurrentMenu.SelectedItem.IsPopup) { + keynav_state = KeyNavState.Navigating; + item = CurrentMenu.SelectedItem; + ShowSubPopup (CurrentMenu, item); + SelectItem (item, item.MenuItems [0], false); + CurrentMenu = item; + active = true; + GrabControl.ActiveTracker = this; + } else { + ExecFocusedItem (CurrentMenu, CurrentMenu.SelectedItem); + } + break; + + case Keys.Escape: + Deactivate (); + break; + + default: + ProcessMnemonic (msg, keyData); + break; + } + + return active; + } + } + + internal class PopUpWindow : Widget + { + private Menu menu; + private Widget form; + + public PopUpWindow (Widget form, Menu menu): base () + { + this.menu = menu; + this.form = form; + SetStyle (Widgetstyles.UserPaint | Widgetstyles.AllPaintingInWmPaint, true); + SetStyle (Widgetstyles.ResizeRedraw | Widgetstyles.Opaque, true); + is_visible = false; + } + + protected override CreateParams CreateParams + { + get { + CreateParams cp = base.CreateParams; + cp.Caption = "Menu PopUp"; + cp.Style = unchecked ((int)(WindowStyles.WS_POPUP)); + cp.ExStyle |= (int)(WindowExStyles.WS_EX_TOOLWINDOW | WindowExStyles.WS_EX_TOPMOST); + return cp; + } + } + + public void ShowWindow () + { + XplatUI.SetCursor(form.Handle, Cursors.Default.handle); + RefreshItems (); + Show (); + } + + internal override void OnPaintInternal (PaintEventArgs args) + { + ThemeEngine.Current.DrawPopupMenu (args.Graphics, menu, args.ClipRectangle, ClientRectangle); + } + + public void HideWindow () + { + XplatUI.SetCursor (form.Handle, form.Cursor.handle); + MenuTracker.HideSubPopups (menu, null); + Hide (); + } + + protected override void CreateHandle () + { + base.CreateHandle (); + RefreshItems (); + } + + // Called when the number of items has changed + internal void RefreshItems () + { + Point pt = new Point (Location.X, Location.Y); + + ThemeEngine.Current.CalcPopupMenuSize (DeviceContext, menu); + + if ((pt.X + menu.Rect.Width) > SystemInformation.VirtualScreen.Width) { + if (((pt.X - menu.Rect.Width) > 0) && !(menu.parent_menu is MainMenu)) + pt.X = pt.X - menu.Rect.Width; + else + pt.X = SystemInformation.VirtualScreen.Width - menu.Rect.Width; + + if (pt.X < 0) + pt.X = 0; + } + if ((pt.Y + menu.Rect.Height) > SystemInformation.VirtualScreen.Height) { + if ((pt.Y - menu.Rect.Height) > 0) + pt.Y = pt.Y - menu.Rect.Height; + else + pt.Y = SystemInformation.VirtualScreen.Height - menu.Rect.Height; + + if (pt.Y < 0) + pt.Y = 0; + } + + Location = pt; + Width = menu.Rect.Width; + Height = menu.Rect.Height; + } + + internal override bool ActivateOnShow { get { return false; } } + } +} + + diff --git a/source/ShiftUI/Menu/MenuGlyph.cs b/source/ShiftUI/Menu/MenuGlyph.cs new file mode 100644 index 0000000..92aa7f7 --- /dev/null +++ b/source/ShiftUI/Menu/MenuGlyph.cs @@ -0,0 +1,36 @@ +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +// Copyright (c) 2004 Novell, Inc. +// +// Authors: +// Peter Bartok [email protected] + + +// COMPLETE + +namespace ShiftUI { + public enum MenuGlyph { + Arrow = 0, + Min = 0, + Checkmark = 1, + Bullet = 2, + Max = 2 + } +} diff --git a/source/ShiftUI/Menu/MenuItem.cs b/source/ShiftUI/Menu/MenuItem.cs new file mode 100644 index 0000000..a893106 --- /dev/null +++ b/source/ShiftUI/Menu/MenuItem.cs @@ -0,0 +1,912 @@ +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +// Copyright (c) 2004-2005 Novell, Inc. +// +// Authors: +// Jordi Mas i Hernandez, [email protected] +// +// + +// NOT COMPLETE + +using System.Collections; +using System.ComponentModel; +using System.ComponentModel.Design; +using System.Drawing; +using System.Drawing.Text; +using System; + +namespace ShiftUI +{ + [DefaultProperty("Text")] + [DefaultEvent("Click")] + [DesignTimeVisible(false)] + [ToolboxItem(false)] + public class MenuItem : Menu + { + internal bool separator; + internal bool break_; + internal bool bar_break; + private Shortcut shortcut; + private string text; + private bool checked_; + private bool radiocheck; + private bool enabled; + private char mnemonic; + private bool showshortcut; + private int index; + private bool mdilist; + private Hashtable mdilist_items; + private Hashtable mdilist_forms; + private MdiClient mdicontainer; + private bool is_window_menu_item; + private bool defaut_item; + private bool visible; + private bool ownerdraw; + private int menuid; + private int mergeorder; + private int xtab; + private int menuheight; + private bool menubar; + private MenuMerge mergetype; + // UIA Framework Note: Used to obtain item bounds + internal Rectangle bounds; + + public MenuItem (): base (null) + { + CommonConstructor (string.Empty); + shortcut = Shortcut.None; + } + + public MenuItem (string text) : base (null) + { + CommonConstructor (text); + shortcut = Shortcut.None; + } + + public MenuItem (string text, EventHandler onClick) : base (null) + { + CommonConstructor (text); + shortcut = Shortcut.None; + Click += onClick; + } + + public MenuItem (string text, MenuItem[] items) : base (items) + { + CommonConstructor (text); + shortcut = Shortcut.None; + } + + public MenuItem (string text, EventHandler onClick, Shortcut shortcut) : base (null) + { + CommonConstructor (text); + Click += onClick; + this.shortcut = shortcut; + } + + public MenuItem (MenuMerge mergeType, int mergeOrder, Shortcut shortcut, string text, + EventHandler onClick, EventHandler onPopup, EventHandler onSelect, MenuItem[] items) + : base (items) + { + CommonConstructor (text); + this.shortcut = shortcut; + mergeorder = mergeOrder; + mergetype = mergeType; + + Click += onClick; + Popup += onPopup; + Select += onSelect; + } + + private void CommonConstructor (string text) + { + defaut_item = false; + separator = false; + break_ = false; + bar_break = false; + checked_ = false; + radiocheck = false; + enabled = true; + showshortcut = true; + visible = true; + ownerdraw = false; + menubar = false; + menuheight = 0; + xtab = 0; + index = -1; + mnemonic = '\0'; + menuid = -1; + mergeorder = 0; + mergetype = MenuMerge.Add; + Text = text; // Text can change separator status + } + + #region Events + static object ClickEvent = new object (); + static object DrawItemEvent = new object (); + static object MeasureItemEvent = new object (); + static object PopupEvent = new object (); + static object SelectEvent = new object (); + + public event EventHandler Click { + add { Events.AddHandler (ClickEvent, value); } + remove { Events.RemoveHandler (ClickEvent, value); } + } + + public event DrawItemEventHandler DrawItem { + add { Events.AddHandler (DrawItemEvent, value); } + remove { Events.RemoveHandler (DrawItemEvent, value); } + } + + public event MeasureItemEventHandler MeasureItem { + add { Events.AddHandler (MeasureItemEvent, value); } + remove { Events.RemoveHandler (MeasureItemEvent, value); } + } + + public event EventHandler Popup { + add { Events.AddHandler (PopupEvent, value); } + remove { Events.RemoveHandler (PopupEvent, value); } + } + + public event EventHandler Select { + add { Events.AddHandler (SelectEvent, value); } + remove { Events.RemoveHandler (SelectEvent, value); } + } + + #region UIA Framework Events + + static object UIACheckedChangedEvent = new object (); + + internal event EventHandler UIACheckedChanged { + add { Events.AddHandler (UIACheckedChangedEvent, value); } + remove { Events.RemoveHandler (UIACheckedChangedEvent, value); } + } + + internal void OnUIACheckedChanged (EventArgs e) + { + EventHandler eh = (EventHandler) Events [UIACheckedChangedEvent]; + if (eh != null) + eh (this, e); + } + + static object UIARadioCheckChangedEvent = new object (); + + internal event EventHandler UIARadioCheckChanged { + add { Events.AddHandler (UIARadioCheckChangedEvent, value); } + remove { Events.RemoveHandler (UIARadioCheckChangedEvent, value); } + } + + internal void OnUIARadioCheckChanged (EventArgs e) + { + EventHandler eh = (EventHandler) Events [UIARadioCheckChangedEvent]; + if (eh != null) + eh (this, e); + } + + static object UIAEnabledChangedEvent = new object (); + + internal event EventHandler UIAEnabledChanged { + add { Events.AddHandler (UIAEnabledChangedEvent, value); } + remove { Events.RemoveHandler (UIAEnabledChangedEvent, value); } + } + + internal void OnUIAEnabledChanged (EventArgs e) + { + EventHandler eh = (EventHandler) Events [UIAEnabledChangedEvent]; + if (eh != null) + eh (this, e); + } + + static object UIATextChangedEvent = new object (); + + internal event EventHandler UIATextChanged { + add { Events.AddHandler (UIATextChangedEvent, value); } + remove { Events.RemoveHandler (UIATextChangedEvent, value); } + } + + internal void OnUIATextChanged (EventArgs e) + { + EventHandler eh = (EventHandler) Events [UIATextChangedEvent]; + if (eh != null) + eh (this, e); + } + + #endregion + #endregion // Events + + #region Public Properties + + [Browsable(false)] + [DefaultValue(false)] + public bool BarBreak { + get { return break_; } + set { break_ = value; } + } + + [Browsable(false)] + [DefaultValue(false)] + public bool Break { + get { return bar_break; } + set { bar_break = value; } + } + + [DefaultValue(false)] + public bool Checked { + get { return checked_; } + set { + if (checked_ == value) + return; + + checked_ = value; + + // UIA Framework Event: Checked Changed + OnUIACheckedChanged (EventArgs.Empty); + } + } + + [DefaultValue(false)] + public bool DefaultItem { + get { return defaut_item; } + set { defaut_item = value; } + } + + [DefaultValue(true)] + [Localizable(true)] + public bool Enabled { + get { return enabled; } + set { + if (enabled == value) + return; + + enabled = value; + + // UIA Framework Event: Enabled Changed + OnUIAEnabledChanged (EventArgs.Empty); + + Invalidate (); + } + } + + [Browsable(false)] + public int Index { + get { return index; } + set { + if (Parent != null && Parent.MenuItems != null && (value < 0 || value >= Parent.MenuItems.Count)) + throw new ArgumentException ("'" + value + "' is not a valid value for 'value'"); + index = value; + } + } + + [Browsable(false)] + public override bool IsParent { + get { return IsPopup; } + } + + [DefaultValue(false)] + public bool MdiList { + get { return mdilist; } + set { + if (mdilist == value) + return; + mdilist = value; + + if (mdilist || mdilist_items == null) + return; + + foreach (MenuItem item in mdilist_items.Keys) + MenuItems.Remove (item); + mdilist_items.Clear (); + mdilist_items = null; + } + } + + protected int MenuID { + get { return menuid; } + } + + [DefaultValue(0)] + public int MergeOrder { + get { return mergeorder; } + set { mergeorder = value; } + } + + [DefaultValue(MenuMerge.Add)] + public MenuMerge MergeType { + get { return mergetype; } + set { + if (!Enum.IsDefined (typeof (MenuMerge), value)) + throw new InvalidEnumArgumentException (string.Format("Enum argument value '{0}' is not valid for MenuMerge", value)); + + mergetype = value; + } + } + + [Browsable(false)] + public char Mnemonic { + get { return mnemonic; } + } + + [DefaultValue(false)] + public bool OwnerDraw { + get { return ownerdraw; } + set { ownerdraw = value; } + } + + [Browsable(false)] + public Menu Parent { + get { return parent_menu;} + } + + [DefaultValue(false)] + public bool RadioCheck { + get { return radiocheck; } + set { + if (radiocheck == value) + return; + + radiocheck = value; + + // UIA Framework Event: Checked Changed + OnUIARadioCheckChanged (EventArgs.Empty); + } + } + + [DefaultValue(Shortcut.None)] + [Localizable(true)] + public Shortcut Shortcut { + get { return shortcut;} + set { + if (!Enum.IsDefined (typeof (Shortcut), value)) + throw new InvalidEnumArgumentException (string.Format("Enum argument value '{0}' is not valid for Shortcut", value)); + + shortcut = value; + UpdateMenuItem (); + } + } + + [DefaultValue(true)] + [Localizable(true)] + public bool ShowShortcut { + get { return showshortcut;} + set { showshortcut = value; } + } + + [Localizable(true)] + public string Text { + get { return text; } + set { + text = value; + + if (text == "-") + separator = true; + else + separator = false; + + // UIA Framework Event: Text Changed + OnUIATextChanged (EventArgs.Empty); + + ProcessMnemonic (); + Invalidate (); + } + } + + [DefaultValue(true)] + [Localizable(true)] + public bool Visible { + get { return visible;} + set { + if (value == visible) + return; + + visible = value; + + if (menu_items != null) { + foreach (MenuItem mi in menu_items) + mi.Visible = value; + } + + if (parent_menu != null) + parent_menu.OnMenuChanged (EventArgs.Empty); + } + } + + #endregion Public Properties + + #region Private Properties + + internal new int Height { + get { return bounds.Height; } + set { bounds.Height = value; } + } + + internal bool IsPopup { + get { + if (menu_items.Count > 0) + return true; + else + return false; + } + } + + internal bool MeasureEventDefined { + get { + if (ownerdraw == true && Events [MeasureItemEvent] != null) { + return true; + } else { + return false; + } + } + } + + internal bool MenuBar { + get { return menubar; } + set { menubar = value; } + } + + internal int MenuHeight { + get { return menuheight; } + set { menuheight = value; } + } + + bool selected; + internal bool Selected { + get { return selected; } + set { selected = value; } + } + + internal bool Separator { + get { return separator; } + set { separator = value; } + } + + internal DrawItemState Status { + get { + DrawItemState status = DrawItemState.None; + MenuTracker tracker = Parent.Tracker; + if (Selected) + status |= (tracker.active || tracker.Navigating ? DrawItemState.Selected : DrawItemState.HotLight); + if (!Enabled) + status |= DrawItemState.Grayed | DrawItemState.Disabled; + if (Checked) + status |= DrawItemState.Checked; + if (!tracker.Navigating) + status |= DrawItemState.NoAccelerator; + return status; + } + } + + internal bool VisibleItems { + get { + if (menu_items != null) { + foreach (MenuItem mi in menu_items) + if (mi.Visible) + return true; + } + return false; + } + } + + internal new int Width { + get { return bounds.Width; } + set { bounds.Width = value; } + } + + internal new int X { + get { return bounds.X; } + set { bounds.X = value; } + } + + internal int XTab { + get { return xtab; } + set { xtab = value; } + } + + internal new int Y { + get { return bounds.Y; } + set { bounds.Y = value; } + } + + #endregion Private Properties + + #region Public Methods + + public virtual MenuItem CloneMenu () + { + MenuItem item = new MenuItem (); + item.CloneMenu (this); + return item; + } + + protected void CloneMenu (MenuItem itemSrc) + { + base.CloneMenu (itemSrc); // Copy subitems + + // Window list + MdiList = itemSrc.MdiList; + is_window_menu_item = itemSrc.is_window_menu_item; + // Remove items corresponding to window menu items, and add new items + // (Otherwise window menu items would show up twice, since the PopulateWindowMenu doesn't + // now them) + bool populated = false; + for (int i = MenuItems.Count - 1; i >= 0; i--) { + if (MenuItems [i].is_window_menu_item) { + MenuItems.RemoveAt (i); + populated = true; + } + } + if (populated) + PopulateWindowMenu (); + + // Properties + BarBreak = itemSrc.BarBreak; + Break = itemSrc.Break; + Checked = itemSrc.Checked; + DefaultItem = itemSrc.DefaultItem; + Enabled = itemSrc.Enabled; + MergeOrder = itemSrc.MergeOrder; + MergeType = itemSrc.MergeType; + OwnerDraw = itemSrc.OwnerDraw; + //Parent = menuitem.Parent; + RadioCheck = itemSrc.RadioCheck; + Shortcut = itemSrc.Shortcut; + ShowShortcut = itemSrc.ShowShortcut; + Text = itemSrc.Text; + Visible = itemSrc.Visible; + Name = itemSrc.Name; + Tag = itemSrc.Tag; + + // Events + Events[ClickEvent] = itemSrc.Events[ClickEvent]; + Events[DrawItemEvent] = itemSrc.Events[DrawItemEvent]; + Events[MeasureItemEvent] = itemSrc.Events[MeasureItemEvent]; + Events[PopupEvent] = itemSrc.Events[PopupEvent]; + Events[SelectEvent] = itemSrc.Events[SelectEvent]; + } + + protected override void Dispose (bool disposing) + { + if (disposing && parent_menu != null) + parent_menu.MenuItems.Remove (this); + + base.Dispose (disposing); + } + + // This really clones the item + public virtual MenuItem MergeMenu () + { + MenuItem item = new MenuItem (); + item.CloneMenu (this); + return item; + } + + public void MergeMenu (MenuItem itemSrc) + { + base.MergeMenu (itemSrc); + } + + protected virtual void OnClick (EventArgs e) + { + EventHandler eh = (EventHandler)(Events [ClickEvent]); + if (eh != null) + eh (this, e); + } + + protected virtual void OnDrawItem (DrawItemEventArgs e) + { + DrawItemEventHandler eh = (DrawItemEventHandler)(Events [DrawItemEvent]); + if (eh != null) + eh (this, e); + } + + + protected virtual void OnInitMenuPopup (EventArgs e) + { + OnPopup (e); + } + + protected virtual void OnMeasureItem (MeasureItemEventArgs e) + { + if (!OwnerDraw) + return; + + MeasureItemEventHandler eh = (MeasureItemEventHandler)(Events [MeasureItemEvent]); + if (eh != null) + eh (this, e); + } + + protected virtual void OnPopup (EventArgs e) + { + EventHandler eh = (EventHandler)(Events [PopupEvent]); + if (eh != null) + eh (this, e); + } + + protected virtual void OnSelect (EventArgs e) + { + EventHandler eh = (EventHandler)(Events [SelectEvent]); + if (eh != null) + eh (this, e); + } + + public void PerformClick () + { + OnClick (EventArgs.Empty); + } + + public virtual void PerformSelect () + { + OnSelect (EventArgs.Empty); + } + + public override string ToString () + { + return base.ToString () + ", Items.Count: " + MenuItems.Count + ", Text: " + text; + } + + #endregion Public Methods + + #region Private Methods + + internal virtual void Invalidate () + { + if ((Parent == null) || !(Parent is MainMenu) || (Parent.Wnd == null)) + return; + + Form form = Parent.Wnd.FindForm (); + if ((form == null) || (!form.IsHandleCreated)) + return; + + XplatUI.RequestNCRecalc (form.Handle); + } + + internal void PerformPopup () + { + OnPopup (EventArgs.Empty); + } + + internal void PerformDrawItem (DrawItemEventArgs e) + { + PopulateWindowMenu (); + if (OwnerDraw) + OnDrawItem (e); + else + ThemeEngine.Current.DrawMenuItem (this, e); + } + + private void PopulateWindowMenu () + { + if (mdilist) { + if (mdilist_items == null) { + mdilist_items = new Hashtable (); + mdilist_forms = new Hashtable (); + } + + do { + MainMenu main = GetMainMenu (); + if (main == null || main.GetForm () == null) + break; + + Form form = main.GetForm (); + mdicontainer = form.MdiContainer; + if (mdicontainer == null) + break; + + + // Remove closed forms + MenuItem[] items = new MenuItem[mdilist_items.Count]; + mdilist_items.Keys.CopyTo (items, 0); + foreach (MenuItem item in items) { + Form mdichild = (Form) mdilist_items [item]; + if (!mdicontainer.mdi_child_list.Contains(mdichild)) { + mdilist_items.Remove (item); + mdilist_forms.Remove (mdichild); + MenuItems.Remove (item); + } + } + + // Add new forms and update state for existing forms. + for (int i = 0; i < mdicontainer.mdi_child_list.Count; i++) { + Form mdichild = (Form)mdicontainer.mdi_child_list[i]; + MenuItem item; + if (mdilist_forms.Contains (mdichild)) { + item = (MenuItem) mdilist_forms [mdichild]; + } else { + item = new MenuItem (); + item.is_window_menu_item = true; + item.Click += new EventHandler (MdiWindowClickHandler); + mdilist_items [item] = mdichild; + mdilist_forms [mdichild] = item; + MenuItems.AddNoEvents (item); + } + item.Visible = mdichild.Visible; + item.Text = "&" + (i + 1).ToString () + " " + mdichild.Text; + item.Checked = form.ActiveMdiChild == mdichild; + } + } while (false); + } else { + // Remove all forms + if (mdilist_items != null) { + foreach (MenuItem item in mdilist_items.Values) { + MenuItems.Remove (item); + } + + mdilist_forms.Clear (); + mdilist_items.Clear (); + } + } + } + + internal void PerformMeasureItem (MeasureItemEventArgs e) + { + OnMeasureItem (e); + } + + private void ProcessMnemonic () + { + if (text == null || text.Length < 2) { + mnemonic = '\0'; + return; + } + + bool bPrevAmp = false; + for (int i = 0; i < text.Length -1 ; i++) { + if (text[i] == '&') { + if (bPrevAmp == false && (text[i+1] != '&')) { + mnemonic = Char.ToUpper (text[i+1]); + return; + } + + bPrevAmp = true; + } + else + bPrevAmp = false; + } + + mnemonic = '\0'; + } + + private string GetShortCutTextCtrl () { return "Ctrl"; } + private string GetShortCutTextAlt () { return "Alt"; } + private string GetShortCutTextShift () { return "Shift"; } + + internal string GetShortCutText () + { + /* Ctrl+A - Ctrl+Z */ + if (Shortcut >= Shortcut.CtrlA && Shortcut <= Shortcut.CtrlZ) + return GetShortCutTextCtrl () + "+" + (char)((int) 'A' + (int)(Shortcut - Shortcut.CtrlA)); + + /* Alt+0 - Alt+9 */ + if (Shortcut >= Shortcut.Alt0 && Shortcut <= Shortcut.Alt9) + return GetShortCutTextAlt () + "+" + (char)((int) '0' + (int)(Shortcut - Shortcut.Alt0)); + + /* Alt+F1 - Alt+F2 */ + if (Shortcut >= Shortcut.AltF1 && Shortcut <= Shortcut.AltF9) + return GetShortCutTextAlt () + "+F" + (char)((int) '1' + (int)(Shortcut - Shortcut.AltF1)); + + /* Ctrl+0 - Ctrl+9 */ + if (Shortcut >= Shortcut.Ctrl0 && Shortcut <= Shortcut.Ctrl9) + return GetShortCutTextCtrl () + "+" + (char)((int) '0' + (int)(Shortcut - Shortcut.Ctrl0)); + + /* Ctrl+F0 - Ctrl+F9 */ + if (Shortcut >= Shortcut.CtrlF1 && Shortcut <= Shortcut.CtrlF9) + return GetShortCutTextCtrl () + "+F" + (char)((int) '1' + (int)(Shortcut - Shortcut.CtrlF1)); + + /* Ctrl+Shift+0 - Ctrl+Shift+9 */ + if (Shortcut >= Shortcut.CtrlShift0 && Shortcut <= Shortcut.CtrlShift9) + return GetShortCutTextCtrl () + "+" + GetShortCutTextShift () + "+" + (char)((int) '0' + (int)(Shortcut - Shortcut.CtrlShift0)); + + /* Ctrl+Shift+A - Ctrl+Shift+Z */ + if (Shortcut >= Shortcut.CtrlShiftA && Shortcut <= Shortcut.CtrlShiftZ) + return GetShortCutTextCtrl () + "+" + GetShortCutTextShift () + "+" + (char)((int) 'A' + (int)(Shortcut - Shortcut.CtrlShiftA)); + + /* Ctrl+Shift+F1 - Ctrl+Shift+F9 */ + if (Shortcut >= Shortcut.CtrlShiftF1 && Shortcut <= Shortcut.CtrlShiftF9) + return GetShortCutTextCtrl () + "+" + GetShortCutTextShift () + "+F" + (char)((int) '1' + (int)(Shortcut - Shortcut.CtrlShiftF1)); + + /* F1 - F9 */ + if (Shortcut >= Shortcut.F1 && Shortcut <= Shortcut.F9) + return "F" + (char)((int) '1' + (int)(Shortcut - Shortcut.F1)); + + /* Shift+F1 - Shift+F9 */ + if (Shortcut >= Shortcut.ShiftF1 && Shortcut <= Shortcut.ShiftF9) + return GetShortCutTextShift () + "+F" + (char)((int) '1' + (int)(Shortcut - Shortcut.ShiftF1)); + + /* Special cases */ + switch (Shortcut) { + case Shortcut.AltBksp: + return "AltBksp"; + case Shortcut.AltF10: + return GetShortCutTextAlt () + "+F10"; + case Shortcut.AltF11: + return GetShortCutTextAlt () + "+F11"; + case Shortcut.AltF12: + return GetShortCutTextAlt () + "+F12"; + case Shortcut.CtrlDel: + return GetShortCutTextCtrl () + "+Del"; + case Shortcut.CtrlF10: + return GetShortCutTextCtrl () + "+F10"; + case Shortcut.CtrlF11: + return GetShortCutTextCtrl () + "+F11"; + case Shortcut.CtrlF12: + return GetShortCutTextCtrl () + "+F12"; + case Shortcut.CtrlIns: + return GetShortCutTextCtrl () + "+Ins"; + case Shortcut.CtrlShiftF10: + return GetShortCutTextCtrl () + "+" + GetShortCutTextShift () + "+F10"; + case Shortcut.CtrlShiftF11: + return GetShortCutTextCtrl () + "+" + GetShortCutTextShift () + "+F11"; + case Shortcut.CtrlShiftF12: + return GetShortCutTextCtrl () + "+" + GetShortCutTextShift () + "+F12"; + case Shortcut.Del: + return "Del"; + case Shortcut.F10: + return "F10"; + case Shortcut.F11: + return "F11"; + case Shortcut.F12: + return "F12"; + case Shortcut.Ins: + return "Ins"; + case Shortcut.None: + return "None"; + case Shortcut.ShiftDel: + return GetShortCutTextShift () + "+Del"; + case Shortcut.ShiftF10: + return GetShortCutTextShift () + "+F10"; + case Shortcut.ShiftF11: + return GetShortCutTextShift () + "+F11"; + case Shortcut.ShiftF12: + return GetShortCutTextShift () + "+F12"; + case Shortcut.ShiftIns: + return GetShortCutTextShift () + "+Ins"; + default: + break; + } + + return ""; + } + + private void MdiWindowClickHandler (object sender, EventArgs e) + { + Form mdichild = (Form) mdilist_items [sender]; + + // people could add weird items to the Window menu + // so we can't assume its just us + if (mdichild == null) + return; + + mdicontainer.ActivateChild (mdichild); + } + + private void UpdateMenuItem () + { + if ((parent_menu == null) || (parent_menu.Tracker == null)) + return; + + parent_menu.Tracker.RemoveShortcuts (this); + parent_menu.Tracker.AddShortcuts (this); + } + + #endregion Private Methods + + } +} + + diff --git a/source/ShiftUI/Menu/MenuMerge.cs b/source/ShiftUI/Menu/MenuMerge.cs new file mode 100644 index 0000000..8731f64 --- /dev/null +++ b/source/ShiftUI/Menu/MenuMerge.cs @@ -0,0 +1,38 @@ +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +// Copyright (c) 2004 Novell, Inc. +// +// Authors: +// Jordi Mas i Hernandez, [email protected] +// +// + +namespace ShiftUI +{ + public enum MenuMerge + { + Add = 0, + Replace = 1, + MergeItems = 2, + Remove = 3, + } +} + + diff --git a/source/ShiftUI/Menu/MenuStrip.cs b/source/ShiftUI/Menu/MenuStrip.cs new file mode 100644 index 0000000..85e585e --- /dev/null +++ b/source/ShiftUI/Menu/MenuStrip.cs @@ -0,0 +1,349 @@ +// +// MenuStrip.cs +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +// Copyright (c) 2006 Jonathan Pobst +// +// Authors: +// Jonathan Pobst ([email protected]) +// + +using System; +using System.Drawing; +using System.ComponentModel; +using System.Runtime.InteropServices; + +namespace ShiftUI +{ + [ClassInterface (ClassInterfaceType.AutoDispatch)] + [ComVisible (true)] + public class MenuStrip : ToolStrip + { + private ToolStripMenuItem mdi_window_list_item; + + public MenuStrip () : base () + { + base.CanOverflow = false; + this.GripStyle = ToolStripGripStyle.Hidden; + this.Stretch = true; + this.Dock = DockStyle.Top; + } + + #region Public Properties + [DefaultValue (false)] + [Browsable (false)] + public new bool CanOverflow { + get { return base.CanOverflow; } + set { base.CanOverflow = value; } + } + + [DefaultValue (ToolStripGripStyle.Hidden)] + public new ToolStripGripStyle GripStyle { + get { return base.GripStyle; } + set { base.GripStyle = value; } + } + + [DefaultValue (null)] + [MergableProperty (false)] + [TypeConverter (typeof (MdiWindowListItemConverter))] + public ToolStripMenuItem MdiWindowListItem { + get { return this.mdi_window_list_item; } + set { + if (this.mdi_window_list_item != value) { + this.mdi_window_list_item = value; + this.RefreshMdiItems (); + } + } + } + + [DefaultValue (false)] + public new bool ShowItemToolTips { + get { return base.ShowItemToolTips; } + set { base.ShowItemToolTips = value; } + } + + [DefaultValue (true)] + public new bool Stretch { + get { return base.Stretch; } + set { base.Stretch = value; } + } + #endregion + + #region Protected Properties + protected override Padding DefaultGripMargin { get { return new Padding (2, 2, 0, 2); } } + protected override Padding DefaultPadding { get { return new Padding (6, 2, 0, 2); } } + protected override bool DefaultShowItemToolTips { get { return false; } } + protected override Size DefaultSize { get { return new Size (200, 24); } } + #endregion + + #region Protected Methods + protected override AccessibleObject CreateAccessibilityInstance () + { + return new MenuStripAccessibleObject (); + } + + protected internal override ToolStripItem CreateDefaultItem (string text, Image image, EventHandler onClick) + { + return new ToolStripMenuItem (text, image, onClick); + } + + protected virtual void OnMenuActivate (EventArgs e) + { + EventHandler eh = (EventHandler)(Events [MenuActivateEvent]); + if (eh != null) + eh (this, e); + } + + protected virtual void OnMenuDeactivate (EventArgs e) + { + EventHandler eh = (EventHandler)(Events [MenuDeactivateEvent]); + if (eh != null) + eh (this, e); + } + + protected override bool ProcessCmdKey (ref Message m, Keys keyData) + { + return base.ProcessCmdKey (ref m, keyData); + } + + protected override void WndProc (ref Message m) + { + base.WndProc (ref m); + } + #endregion + + #region Public Events + static object MenuActivateEvent = new object (); + static object MenuDeactivateEvent = new object (); + + public event EventHandler MenuActivate { + add { Events.AddHandler (MenuActivateEvent, value); } + remove { Events.RemoveHandler (MenuActivateEvent, value); } + } + + public event EventHandler MenuDeactivate { + add { Events.AddHandler (MenuDeactivateEvent, value); } + remove { Events.RemoveHandler (MenuDeactivateEvent, value); } + } + #endregion + + #region Internal Properties + internal override bool KeyboardActive { + get { return base.KeyboardActive; } + set { + if (base.KeyboardActive != value) { + base.KeyboardActive = value; + + if (value) + this.OnMenuActivate (EventArgs.Empty); + else + this.OnMenuDeactivate (EventArgs.Empty); + } + } + } + + internal bool MenuDroppedDown { + get { return this.menu_selected; } + set { this.menu_selected = value; } + } + #endregion + + #region Internal Methods + internal override void Dismiss (ToolStripDropDownCloseReason reason) + { + // Make sure we don't auto-dropdown next time we're activated + this.MenuDroppedDown = false; + + base.Dismiss (reason); + + this.FireMenuDeactivate (); + } + + internal void FireMenuActivate () + { + // The tracker lets us know when the form is clicked or loses focus + ToolStripManager.AppClicked += new EventHandler (ToolStripMenuTracker_AppClicked); + ToolStripManager.AppFocusChange += new EventHandler (ToolStripMenuTracker_AppFocusChange); + + this.OnMenuActivate (EventArgs.Empty); + } + + internal void FireMenuDeactivate () + { + // Detach from the tracker + ToolStripManager.AppClicked -= new EventHandler (ToolStripMenuTracker_AppClicked); ; + ToolStripManager.AppFocusChange -= new EventHandler (ToolStripMenuTracker_AppFocusChange); + + this.OnMenuDeactivate (EventArgs.Empty); + } + + internal override bool OnMenuKey () + { + // Set ourselves active and select our first item + ToolStripManager.SetActiveToolStrip (this, true); + ToolStripItem tsi = this.SelectNextToolStripItem (null, true); + + if (tsi == null) + return false; + + if (tsi is MdiWidgetStrip.SystemMenuItem) + this.SelectNextToolStripItem (tsi, true); + + return true; + } + + private void ToolStripMenuTracker_AppFocusChange (object sender, EventArgs e) + { + this.GetTopLevelToolStrip ().Dismiss (ToolStripDropDownCloseReason.AppFocusChange); + } + + private void ToolStripMenuTracker_AppClicked (object sender, EventArgs e) + { + this.GetTopLevelToolStrip ().Dismiss (ToolStripDropDownCloseReason.AppClicked); + } + + internal void RefreshMdiItems () + { + if (this.mdi_window_list_item == null) + return; + + Form parent_form = this.FindForm (); + + if (parent_form == null || parent_form.MainMenuStrip != this) + return; + + MdiClient mdi = parent_form.MdiContainer; + + // If there isn't a MdiContainer, we don't need to worry about MdiItems :) + if (mdi == null) + return; + + // Make a copy so we can delete from the real one + ToolStripItem[] loopitems = new ToolStripItem[this.mdi_window_list_item.DropDownItems.Count]; + this.mdi_window_list_item.DropDownItems.CopyTo (loopitems, 0); + + // If the mdi child has been removed, remove our menu item + foreach (ToolStripItem tsi in loopitems) + if (tsi is ToolStripMenuItem && (tsi as ToolStripMenuItem).IsMdiWindowListEntry) + if (!mdi.mdi_child_list.Contains ((tsi as ToolStripMenuItem).MdiClientForm) || !(tsi as ToolStripMenuItem).MdiClientForm.Visible) + this.mdi_window_list_item.DropDownItems.Remove (tsi); + + // Add the new forms and update state + for (int i = 0; i < mdi.mdi_child_list.Count; i++) { + Form mdichild = (Form)mdi.mdi_child_list[i]; + ToolStripMenuItem tsi; + + if (!mdichild.Visible) + continue; + + if ((tsi = FindMdiMenuItemOfForm (mdichild)) == null) { + if (CountMdiMenuItems () == 0 && this.mdi_window_list_item.DropDownItems.Count > 0 && !(this.mdi_window_list_item.DropDownItems[this.mdi_window_list_item.DropDownItems.Count - 1] is ToolStripSeparator)) + this.mdi_window_list_item.DropDownItems.Add (new ToolStripSeparator ()); + + tsi = new ToolStripMenuItem (); + tsi.MdiClientForm = mdichild; + this.mdi_window_list_item.DropDownItems.Add (tsi); + } + + tsi.Text = string.Format ("&{0} {1}", i + 1, mdichild.Text); + tsi.Checked = parent_form.ActiveMdiChild == mdichild; + } + + // Check that everything is in the correct order + if (NeedToReorderMdi ()) + ReorderMdiMenu (); + } + + private ToolStripMenuItem FindMdiMenuItemOfForm (Form f) + { + // Not terribly efficient, but Mdi window lists shouldn't get too big + foreach (ToolStripItem tsi in this.mdi_window_list_item.DropDownItems) + if (tsi is ToolStripMenuItem && (tsi as ToolStripMenuItem).MdiClientForm == f) + return (ToolStripMenuItem)tsi; + + return null; + } + + private int CountMdiMenuItems () + { + int count = 0; + + foreach (ToolStripItem tsi in this.mdi_window_list_item.DropDownItems) + if (tsi is ToolStripMenuItem && (tsi as ToolStripMenuItem).IsMdiWindowListEntry) + count++; + + return count; + } + + private bool NeedToReorderMdi () + { + // Mdi menus must be: User Items, Separator, Mdi Items + bool seenMdi = false; + + foreach (ToolStripItem tsi in this.mdi_window_list_item.DropDownItems) { + if (tsi is ToolStripMenuItem) { + if (!(tsi as ToolStripMenuItem).IsMdiWindowListEntry) { + if (seenMdi) + return true; + } else + seenMdi = true; + } + } + + return false; + } + + private void ReorderMdiMenu () + { + ToolStripItem[] loopitems = new ToolStripItem[this.mdi_window_list_item.DropDownItems.Count]; + this.mdi_window_list_item.DropDownItems.CopyTo (loopitems, 0); + + this.mdi_window_list_item.DropDownItems.Clear (); + + foreach (ToolStripItem tsi in loopitems) + if (tsi is ToolStripSeparator || !(tsi as ToolStripMenuItem).IsMdiWindowListEntry) + this.mdi_window_list_item.DropDownItems.Add (tsi); + + int count = this.mdi_window_list_item.DropDownItems.Count; + + if (count > 0 && !(this.mdi_window_list_item.DropDownItems[count - 1] is ToolStripSeparator)) + this.mdi_window_list_item.DropDownItems.Add (new ToolStripSeparator ()); + + foreach (ToolStripItem tsi in loopitems) + if (tsi is ToolStripMenuItem && (tsi as ToolStripMenuItem).IsMdiWindowListEntry) + this.mdi_window_list_item.DropDownItems.Add (tsi); + } + #endregion + + #region MenuStripAccessibleObject + private class MenuStripAccessibleObject : AccessibleObject + { + } + #endregion + + } + + #region MdiWindowListItemConverter + internal class MdiWindowListItemConverter : TypeConverter + { + } + #endregion +} |
