915 lines
23 KiB
C#
915 lines
23 KiB
C#
|
// 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, jordi@ximian.com
|
||
|
// Mike Kestner <mkestner@novell.com>
|
||
|
// Everaldo Canuto <ecanuto@novell.com>
|
||
|
//
|
||
|
|
||
|
using System.Collections;
|
||
|
using System.Drawing;
|
||
|
using System.Threading;
|
||
|
|
||
|
namespace System.Windows.Forms {
|
||
|
|
||
|
/*
|
||
|
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 Control 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 ()
|
||
|
{
|
||
|
Control 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;
|
||
|
if (TopMenu is ContextMenu) {
|
||
|
PopUpWindow puw = TopMenu.Wnd as PopUpWindow;
|
||
|
DeselectItem (TopMenu.SelectedItem);
|
||
|
if (puw != null)
|
||
|
puw.HideWindow ();
|
||
|
} else {
|
||
|
DeselectItem (TopMenu.SelectedItem);
|
||
|
}
|
||
|
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;
|
||
|
if (TopMenu.SelectedItem != null)
|
||
|
item = FindSubItemByCoord (TopMenu.SelectedItem, Control.MousePosition);
|
||
|
if (item == null)
|
||
|
item = FindItemByCoords (TopMenu, pnt);
|
||
|
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;
|
||
|
|
||
|
/* Deactivate the menu when is topmenu and popdown and */
|
||
|
if (((CurrentMenu == TopMenu) && !(CurrentMenu is ContextMenu) && popdown_menu) || !item.IsPopup) {
|
||
|
Deactivate ();
|
||
|
UpdateCursor ();
|
||
|
}
|
||
|
|
||
|
/* 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)
|
||
|
{
|
||
|
if (menu.MenuItems.Count <= 0) // No submenus to track
|
||
|
return true;
|
||
|
|
||
|
MenuTracker tracker = menu.tracker;
|
||
|
tracker.active = true;
|
||
|
tracker.popup_active = true;
|
||
|
|
||
|
// Set GrabControl
|
||
|
Control src_ctrl = (tracker.TopMenu as ContextMenu).SourceControl;
|
||
|
tracker.GrabControl = src_ctrl.FindForm ();
|
||
|
if (tracker.GrabControl == null)
|
||
|
tracker.GrabControl = src_ctrl.FindRootParent ();
|
||
|
tracker.GrabControl.ActiveTracker = tracker;
|
||
|
|
||
|
menu.Wnd = new PopUpWindow (tracker.GrabControl, menu);
|
||
|
menu.Wnd.Location = menu.Wnd.PointToClient (pnt);
|
||
|
((PopUpWindow)menu.Wnd).ShowWindow ();
|
||
|
|
||
|
bool no_quit = true;
|
||
|
|
||
|
Object queue_id = XplatUI.StartLoop(Thread.CurrentThread);
|
||
|
|
||
|
while ((menu.Wnd != null) && menu.Wnd.Visible && no_quit) {
|
||
|
MSG msg = new MSG ();
|
||
|
no_quit = XplatUI.GetMessage(queue_id, ref msg, IntPtr.Zero, 0, 0);
|
||
|
|
||
|
switch((Msg)msg.message) {
|
||
|
case Msg.WM_KEYDOWN:
|
||
|
case Msg.WM_SYSKEYDOWN:
|
||
|
case Msg.WM_CHAR:
|
||
|
case Msg.WM_SYSCHAR:
|
||
|
case Msg.WM_KEYUP:
|
||
|
case Msg.WM_SYSKEYUP:
|
||
|
Control c = Control.FromHandle(msg.hwnd);
|
||
|
if (c != null) {
|
||
|
Message m = Message.Create(msg.hwnd, (int)msg.message, msg.wParam, msg.lParam);
|
||
|
c.PreProcessControlMessageInternal (ref m);
|
||
|
}
|
||
|
break;
|
||
|
default:
|
||
|
XplatUI.TranslateMessage (ref msg);
|
||
|
XplatUI.DispatchMessage (ref msg);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (tracker.GrabControl.IsDisposed)
|
||
|
return true;
|
||
|
|
||
|
if (!no_quit)
|
||
|
XplatUI.PostQuitMessage(0);
|
||
|
|
||
|
if (menu.Wnd != null) {
|
||
|
menu.Wnd.Dispose ();
|
||
|
menu.Wnd = null;
|
||
|
}
|
||
|
|
||
|
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;
|
||
|
}
|
||
|
} else if (!(CurrentMenu is ContextMenu)) { // ContextMenu root remains active.
|
||
|
HideSubPopups (CurrentMenu, TopMenu);
|
||
|
if (CurrentMenu.parent_menu != null)
|
||
|
CurrentMenu = CurrentMenu.parent_menu;
|
||
|
}
|
||
|
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 : Control
|
||
|
{
|
||
|
private Menu menu;
|
||
|
private Control form;
|
||
|
|
||
|
public PopUpWindow (Control form, Menu menu): base ()
|
||
|
{
|
||
|
this.menu = menu;
|
||
|
this.form = form;
|
||
|
SetStyle (ControlStyles.UserPaint | ControlStyles.AllPaintingInWmPaint, true);
|
||
|
SetStyle (ControlStyles.ResizeRedraw | ControlStyles.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; } }
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|