MenuAPI.cs 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980
  1. // Permission is hereby granted, free of charge, to any person obtaining
  2. // a copy of this software and associated documentation files (the
  3. // "Software"), to deal in the Software without restriction, including
  4. // without limitation the rights to use, copy, modify, merge, publish,
  5. // distribute, sublicense, and/or sell copies of the Software, and to
  6. // permit persons to whom the Software is furnished to do so, subject to
  7. // the following conditions:
  8. //
  9. // The above copyright notice and this permission notice shall be
  10. // included in all copies or substantial portions of the Software.
  11. //
  12. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  13. // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  14. // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  15. // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  16. // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  17. // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  18. // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  19. //
  20. // Copyright (c) 2004-2005 Novell, Inc.
  21. //
  22. // Authors:
  23. // Jordi Mas i Hernandez, [email protected]
  24. //
  25. // NOT COMPLETE
  26. using System.Drawing;
  27. using System.Collections;
  28. namespace System.Windows.Forms
  29. {
  30. /*
  31. This class mimics the Win32 API Menu functionality
  32. When writing this code the Wine project was of great help to
  33. understand the logic behind some Win32 issues. Thanks to them. Jordi,
  34. */
  35. internal class MenuAPI
  36. {
  37. static ArrayList menu_list = new ArrayList ();
  38. public class MENU
  39. {
  40. public MF Flags; // Menu flags (MF_POPUP, MF_SYSMENU)
  41. public int X; // Used in MenuBar only
  42. public int Y; // Used in MenuBar only
  43. public int Width; // Width of the whole menu
  44. public int Height; // Height of the whole menu
  45. public Control Wnd; // In a Popup menu is the PopupWindow and in a MenuBar the Form
  46. public ArrayList items; // Array of menu items
  47. public int FocusedItem; // Currently focused item
  48. public IntPtr hParent;
  49. public MENUITEM SelectedItem; // Currently selected item
  50. public bool bMenubar;
  51. public bool bTracking;
  52. public Menu menu; // SWF.Menu
  53. public MENU (Menu menu_obj)
  54. {
  55. Wnd = null;
  56. hParent = IntPtr.Zero;
  57. items = new ArrayList ();
  58. Flags = MF.MF_INSERT;
  59. Width = Height = FocusedItem = X = Y = 0;
  60. bMenubar = false;
  61. bTracking = false;
  62. menu = menu_obj;
  63. }
  64. }
  65. public class MENUITEM
  66. {
  67. public MenuItem item;
  68. public Rectangle rect;
  69. public int wID;
  70. public IntPtr hSubMenu;
  71. public int pos; /* Position in the menuitems array*/
  72. public MENUITEM ()
  73. {
  74. wID = 0;
  75. pos = 0;
  76. rect = new Rectangle ();
  77. }
  78. };
  79. public class TRACKER
  80. {
  81. public IntPtr hCurrentMenu;
  82. public IntPtr hTopMenu;
  83. public TRACKER ()
  84. {
  85. hCurrentMenu = hTopMenu = IntPtr.Zero;
  86. }
  87. };
  88. public enum MenuMouseEvent
  89. {
  90. Down,
  91. Move,
  92. }
  93. internal enum ItemNavigation
  94. {
  95. First,
  96. Last,
  97. Next,
  98. Previous,
  99. }
  100. internal enum MF
  101. {
  102. MF_INSERT = 0x0,
  103. MF_APPEND = 0x100,
  104. MF_DELETE = 0x200,
  105. MF_REMOVE = 0x1000,
  106. MF_BYCOMMAND = 0,
  107. MF_BYPOSITION = 0x400,
  108. MF_SEPARATOR = 0x800,
  109. MF_ENABLED = 0,
  110. MF_GRAYED = 1,
  111. MF_DISABLED = 2,
  112. MF_UNCHECKED = 0,
  113. MF_CHECKED = 8,
  114. MF_USECHECKBITMAPS = 0x200,
  115. MF_STRING = 0,
  116. MF_BITMAP = 4,
  117. MF_OWNERDRAW = 0x100,
  118. MF_POPUP = 0x10,
  119. MF_MENUBARBREAK = 0x20,
  120. MF_MENUBREAK = 0x40,
  121. MF_UNHILITE = 0,
  122. MF_HILITE = 0x80,
  123. MF_DEFAULT = 0x1000,
  124. MF_SYSMENU = 0x2000,
  125. MF_HELP = 0x4000,
  126. MF_RIGHTJUSTIFY = 0x4000,
  127. MF_MENUBAR = 0x8000 // Internal
  128. }
  129. static MenuAPI ()
  130. {
  131. }
  132. static public IntPtr StoreMenuID (MENU menu)
  133. {
  134. int id = menu_list.Add (menu);
  135. return (IntPtr)(id + 1);
  136. }
  137. static public MENU GetMenuFromID (IntPtr ptr)
  138. {
  139. int id = (int)ptr;
  140. id = id - 1;
  141. if (menu_list[id] == null) // It has been delete it
  142. return null;
  143. return (MENU) menu_list[id];
  144. }
  145. static public IntPtr CreateMenu (Menu menu_obj)
  146. {
  147. MENU menu = new MENU (menu_obj);
  148. return StoreMenuID (menu);
  149. }
  150. static public IntPtr CreatePopupMenu (Menu menu_obj)
  151. {
  152. MENU popMenu = new MENU (menu_obj);
  153. popMenu.Flags |= MF.MF_POPUP;
  154. return StoreMenuID (popMenu);
  155. }
  156. static public int InsertMenuItem (IntPtr hMenu, int uItem, bool fByPosition, MenuItem item, ref IntPtr hSubMenu)
  157. {
  158. int id;
  159. if (fByPosition == false)
  160. throw new NotImplementedException ();
  161. MENU menu = GetMenuFromID (hMenu);
  162. if ((uint)uItem > menu.items.Count)
  163. uItem = menu.items.Count;
  164. MENUITEM menu_item = new MENUITEM ();
  165. menu_item.item = item;
  166. if (item.IsPopup) {
  167. menu_item.hSubMenu = CreatePopupMenu (menu_item.item);
  168. MENU submenu = GetMenuFromID (menu_item.hSubMenu);
  169. submenu.hParent = hMenu;
  170. }
  171. else
  172. menu_item.hSubMenu = IntPtr.Zero;
  173. hSubMenu = menu_item.hSubMenu;
  174. id = menu.items.Count;
  175. menu_item.pos = menu.items.Count;
  176. menu.items.Insert (uItem, menu_item);
  177. return id;
  178. }
  179. // The Point object contains screen coordinates
  180. static public bool TrackPopupMenu (IntPtr hTopMenu, IntPtr hMenu, Point pnt, bool bMenubar, Control Wnd)
  181. {
  182. TRACKER tracker = new TRACKER ();
  183. MENU menu = null;
  184. MSG msg = new MSG();
  185. bool no_quit = true;
  186. if (hMenu == IntPtr.Zero) // No submenus to track
  187. return true;
  188. menu = GetMenuFromID (hMenu);
  189. menu.Wnd = new PopUpWindow (hMenu, tracker);
  190. tracker.hCurrentMenu = hMenu;
  191. tracker.hTopMenu = hTopMenu;
  192. MENUITEM select_item = GetNextItem (hMenu, ItemNavigation.First);
  193. if (select_item != null) {
  194. MenuAPI.SelectItem (hMenu, select_item, false, tracker);
  195. }
  196. menu.Wnd.Location = menu.Wnd.PointToClient (pnt);
  197. if (menu.menu.IsDirty) {
  198. menu.items.Clear ();
  199. menu.menu.CreateItems ();
  200. ((PopUpWindow)menu.Wnd).RefreshItems ();
  201. menu.menu.IsDirty = false;
  202. }
  203. ((PopUpWindow)menu.Wnd).ShowWindow ();
  204. while ((menu.Wnd != null) && menu.Wnd.Visible && no_quit) {
  205. no_quit = XplatUI.GetMessage(ref msg, IntPtr.Zero, 0, 0);
  206. XplatUI.TranslateMessage(ref msg);
  207. XplatUI.DispatchMessage(ref msg);
  208. }
  209. if (!no_quit) {
  210. XplatUI.Exit();
  211. }
  212. if (menu.Wnd == null) {
  213. menu.Wnd.Dispose ();
  214. menu.Wnd = null;
  215. }
  216. return true;
  217. }
  218. /*
  219. Menu handeling API
  220. */
  221. static public Point ClientAreaPointToScreen (MENU menu, Point pnt)
  222. {
  223. Point rslt;
  224. int x;
  225. int y;
  226. pnt.Y -= menu.Y;
  227. x = pnt.X;
  228. y = pnt.Y;
  229. XplatUI.MenuToScreen(menu.Wnd.window.Handle, ref x, ref y);
  230. rslt = new Point(x, y);
  231. return rslt;
  232. }
  233. static public Point ClientAreaPointToClient (MENU menu, Point pnt)
  234. {
  235. Point rslt;
  236. int x;
  237. int y;
  238. x = pnt.X;
  239. y = pnt.Y;
  240. XplatUI.ScreenToMenu(menu.Wnd.window.Handle, ref x, ref y);
  241. rslt = new Point(x, y);
  242. rslt.Y += menu.Y;
  243. return rslt;
  244. }
  245. static public MENUITEM FindItemByCoords (IntPtr hMenu, Point pt)
  246. {
  247. MENU menu = GetMenuFromID (hMenu);
  248. for (int i = 0; i < menu.items.Count; i++) {
  249. MENUITEM item = (MENUITEM) menu.items[i];
  250. if (item.rect.Contains (pt)) {
  251. return item;
  252. }
  253. }
  254. return null;
  255. }
  256. // Get the current selected item
  257. static public MENUITEM GetSelected (IntPtr hMenu)
  258. {
  259. MENU menu = GetMenuFromID (hMenu);
  260. MENUITEM it;
  261. /* Loop all items */
  262. for (int i = 0; i < menu.items.Count; i++) {
  263. it = (MENUITEM) menu.items[i];
  264. if ((it.item.Status & DrawItemState.Selected) == DrawItemState.Selected) {
  265. return it;
  266. }
  267. }
  268. return null;
  269. }
  270. static internal void DrawMenuBar (IntPtr hMenu)
  271. {
  272. MENU menu = GetMenuFromID (hMenu);
  273. DrawMenuBar (hMenu, new Rectangle (menu.X, menu.Y, menu.Width, menu.Height));
  274. }
  275. // Little helper
  276. static internal void DrawMenuBar (IntPtr hMenu, Rectangle rect)
  277. {
  278. Graphics g;
  279. MENU menu = GetMenuFromID (hMenu);
  280. menu.X = rect.X;
  281. menu.Y = rect.Y;
  282. rect.Height = menu.Height;
  283. g = XplatUI.GetMenuDC(menu.Wnd.window.Handle, IntPtr.Zero);
  284. ThemeEngine.Current.DrawMenuBar (g, hMenu, rect);
  285. XplatUI.ReleaseMenuDC(menu.Wnd.window.Handle, g);
  286. }
  287. static public void UnSelectItem (IntPtr hMenu, MENUITEM item)
  288. {
  289. MENU menu = GetMenuFromID (hMenu);
  290. if (item == null)
  291. return;
  292. item.item.Status = item.item.Status &~ DrawItemState.Selected;
  293. if (menu.bMenubar) {
  294. DrawMenuBar (hMenu);
  295. } else {
  296. menu.Wnd.Invalidate (item.rect);
  297. }
  298. }
  299. // Select the item and unselect the previous selecte item
  300. static public void SelectItem (IntPtr hMenu, MENUITEM item, bool execute, TRACKER tracker)
  301. {
  302. MENU menu = GetMenuFromID (hMenu);
  303. MENUITEM previous_selitem = GetSelected (hMenu);
  304. /* Already selected */
  305. if (previous_selitem != null && item.rect == previous_selitem.rect) {
  306. return;
  307. }
  308. UnSelectItem (hMenu, previous_selitem);
  309. // If the previous item had subitems, hide them
  310. if (previous_selitem != null && previous_selitem.item.IsPopup)
  311. HideSubPopups (hMenu);
  312. if (tracker.hCurrentMenu != hMenu) {
  313. menu.Wnd.Capture = true;
  314. tracker.hCurrentMenu = hMenu;
  315. }
  316. menu.SelectedItem = item;
  317. item.item.Status |= DrawItemState.Selected;
  318. if (menu.bMenubar) {
  319. DrawMenuBar (hMenu);
  320. } else {
  321. menu.Wnd.Invalidate (item.rect);
  322. }
  323. item.item.PerformSelect ();
  324. if (execute)
  325. ExecFocusedItem (hMenu, item, tracker);
  326. }
  327. // Used when the user executes the action of an item (press enter, shortcut)
  328. // or a sub-popup menu has to be shown
  329. static public void ExecFocusedItem (IntPtr hMenu, MENUITEM item, TRACKER tracker)
  330. {
  331. if (item.item.Enabled == false)
  332. return;
  333. if (item.item.IsPopup) {
  334. ShowSubPopup (hMenu, item.hSubMenu, item, tracker);
  335. }
  336. else {
  337. // Execute function
  338. }
  339. }
  340. // Create a popup window and show it or only show it if it is already created
  341. static public void ShowSubPopup (IntPtr hParent, IntPtr hMenu, MENUITEM item, TRACKER tracker)
  342. {
  343. MENU menu = GetMenuFromID (hMenu);
  344. Point pnt = new Point ();
  345. if (item.item.Enabled == false)
  346. return;
  347. MENU menu_parent = GetMenuFromID (hParent);
  348. ((PopUpWindow)menu_parent.Wnd).LostFocus ();
  349. tracker.hCurrentMenu = hMenu;
  350. if (menu.Wnd != null)
  351. menu.Wnd.Dispose ();
  352. menu.Wnd = new PopUpWindow (hMenu, tracker);
  353. pnt.X = item.rect.X + item.rect.Width;
  354. pnt.Y = item.rect.Y + 1;
  355. pnt = menu_parent.Wnd.PointToScreen (pnt);
  356. menu.Wnd.Location = pnt;
  357. MENUITEM select_item = GetNextItem (hMenu, ItemNavigation.First);
  358. if (select_item != null)
  359. MenuAPI.SelectItem (hMenu, select_item, false, tracker);
  360. ((PopUpWindow)menu.Wnd).ShowWindow ();
  361. }
  362. /* Hides all the submenus open in a menu */
  363. static public void HideSubPopups (IntPtr hMenu)
  364. {
  365. MENU menu = GetMenuFromID (hMenu);
  366. MENUITEM item;
  367. for (int i = 0; i < menu.items.Count; i++) {
  368. item = (MENUITEM) menu.items[i];
  369. if (!item.item.IsPopup)
  370. continue;
  371. MENU sub_menu = GetMenuFromID (item.hSubMenu);
  372. if (sub_menu.Wnd != null) {
  373. HideSubPopups (item.hSubMenu);
  374. ((PopUpWindow)sub_menu.Wnd).Hide ();
  375. }
  376. }
  377. }
  378. static public void DestroyMenu (IntPtr hMenu)
  379. {
  380. if (hMenu == IntPtr.Zero)
  381. return;
  382. MENU menu = GetMenuFromID (hMenu);
  383. MENUITEM item;
  384. for (int i = 0; i < menu.items.Count; i++) {
  385. item = (MENUITEM) menu.items[i];
  386. if (item.item.IsPopup) {
  387. MENU sub_menu = GetMenuFromID (item.hSubMenu);
  388. if (sub_menu != null && sub_menu.Wnd != null)
  389. HideSubPopups (item.hSubMenu);
  390. DestroyMenu (item.hSubMenu);
  391. }
  392. }
  393. // Do not destroy the window of a Menubar
  394. if (menu.Wnd != null && menu.bMenubar == false) {
  395. ((PopUpWindow)menu.Wnd).Dispose ();
  396. menu.Wnd = null;
  397. }
  398. /* Unreference from the array list */
  399. menu_list[((int)hMenu)-1] = null;
  400. }
  401. // Find item by screen coordinates
  402. static public bool FindSubItemByCoord (IntPtr hMenu, Point pnt, ref IntPtr hMenuItem, ref MENUITEM itemfound)
  403. {
  404. Point pnt_client;
  405. Rectangle rect;
  406. MENU menu = GetMenuFromID (hMenu);
  407. MENUITEM item;
  408. for (int i = 0; i < menu.items.Count; i++) {
  409. item = (MENUITEM) menu.items[i];
  410. if (item.item.IsPopup)
  411. if (FindSubItemByCoord (item.hSubMenu, pnt, ref hMenuItem, ref itemfound) == true)
  412. return true;
  413. if (menu.Wnd == null) // Menu has not been created yet
  414. continue;
  415. rect = item.rect;
  416. pnt_client = menu.Wnd.PointToScreen (new Point (item.rect.X, item.rect.Y));
  417. rect.X = pnt_client.X;
  418. rect.Y = pnt_client.Y;
  419. if (rect.Contains (pnt) == true) {
  420. itemfound = item;
  421. hMenuItem = hMenu;
  422. return true;
  423. }
  424. }
  425. return false;
  426. }
  427. static public void SetMenuBarWindow (IntPtr hMenu, Control wnd)
  428. {
  429. MENU menu = GetMenuFromID (hMenu);
  430. menu.Wnd = wnd;
  431. menu.bMenubar = true;
  432. }
  433. static private void MenuBarMove (IntPtr hMenu, MENUITEM item, TRACKER tracker)
  434. {
  435. MENU menu = GetMenuFromID (hMenu);
  436. Point pnt = new Point (item.rect.X, item.rect.Y + item.rect.Height + 1);
  437. pnt = ClientAreaPointToScreen (menu, pnt);
  438. MenuAPI.SelectItem (hMenu, item, false, tracker);
  439. HideSubPopups (tracker.hCurrentMenu);
  440. tracker.hCurrentMenu = hMenu;
  441. MenuAPI.TrackPopupMenu (hMenu, item.hSubMenu, pnt, false, null);
  442. }
  443. // Function that process all menubar mouse events. Coordinates in screen position
  444. static public void TrackBarMouseEvent (IntPtr hMenu, Control wnd, MouseEventArgs e, MenuMouseEvent eventype, TRACKER tracker)
  445. {
  446. MENU menu = GetMenuFromID (hMenu);
  447. switch (eventype) {
  448. case MenuMouseEvent.Down: {
  449. Point pnt = new Point (e.X, e.Y);
  450. pnt = ClientAreaPointToClient (menu, pnt);
  451. MenuAPI.MENUITEM item = MenuAPI.FindItemByCoords (hMenu, pnt);
  452. if (item != null) {
  453. MENU top_menu = GetMenuFromID (tracker.hTopMenu);
  454. top_menu.bTracking = true;
  455. MenuBarMove (hMenu, item, tracker);
  456. if (item != null) {
  457. item.item.PerformClick ();
  458. }
  459. }
  460. break;
  461. }
  462. case MenuMouseEvent.Move: {
  463. if (tracker.hTopMenu != IntPtr.Zero && tracker.hCurrentMenu != IntPtr.Zero) {
  464. MENU top_menu = GetMenuFromID (tracker.hTopMenu);
  465. if (top_menu.bTracking == false)
  466. break;
  467. Point pnt = new Point (e.X, e.Y);
  468. //pnt = menu.Wnd.PointToClient (pnt);
  469. pnt = ClientAreaPointToClient (menu, pnt);
  470. MenuAPI.MENUITEM item = MenuAPI.FindItemByCoords (hMenu, pnt);
  471. if (item != null && menu.SelectedItem != item)
  472. MenuBarMove (hMenu, item, tracker);
  473. }
  474. break;
  475. }
  476. default:
  477. break;
  478. }
  479. }
  480. static public MENUITEM FindItemByKey (IntPtr hMenu, IntPtr key)
  481. {
  482. MENU menu = GetMenuFromID (hMenu);
  483. MENUITEM item;
  484. char key_char = (char ) (key.ToInt32() & 0xff);
  485. key_char = Char.ToUpper (key_char);
  486. for (int i = 0; i < menu.items.Count; i++) {
  487. item = (MENUITEM) menu.items[i];
  488. if (item.item.Mnemonic == '\0')
  489. continue;
  490. if (item.item.Mnemonic == key_char)
  491. return item;
  492. }
  493. return null;
  494. }
  495. // Get the next or previous selectable item on a menu
  496. static public MENUITEM GetNextItem (IntPtr hMenu, ItemNavigation navigation)
  497. {
  498. MENU menu = GetMenuFromID (hMenu);
  499. int pos = 0;
  500. bool selectable_items = false;
  501. MENUITEM item;
  502. // Check if there is at least a selectable item
  503. for (int i = 0; i < menu.items.Count; i++) {
  504. item = (MENUITEM)menu.items[i];
  505. if (item.item.Separator == false && item.item.Visible == true) {
  506. selectable_items = true;
  507. break;
  508. }
  509. }
  510. if (selectable_items == false)
  511. return null;
  512. switch (navigation) {
  513. case ItemNavigation.First: {
  514. pos = 0;
  515. /* Next item that is not separator and it is visible*/
  516. for (; pos < menu.items.Count; pos++) {
  517. item = (MENUITEM)menu.items[pos];
  518. if (item.item.Separator == false && item.item.Visible == true)
  519. break;
  520. }
  521. if (pos >= menu.items.Count) { /* Jump at the start of the menu */
  522. pos = 0;
  523. /* Next item that is not separator and it is visible*/
  524. for (; pos < menu.items.Count; pos++) {
  525. item = (MENUITEM)menu.items[pos];
  526. if (item.item.Separator == false && item.item.Visible == true)
  527. break;
  528. }
  529. }
  530. break;
  531. }
  532. case ItemNavigation.Last: { // Not used
  533. break;
  534. }
  535. case ItemNavigation.Next: {
  536. if (menu.SelectedItem != null)
  537. pos = menu.SelectedItem.pos;
  538. /* Next item that is not separator and it is visible*/
  539. for (pos++; pos < menu.items.Count; pos++) {
  540. item = (MENUITEM)menu.items[pos];
  541. if (item.item.Separator == false && item.item.Visible == true)
  542. break;
  543. }
  544. if (pos >= menu.items.Count) { /* Jump at the start of the menu */
  545. pos = 0;
  546. /* Next item that is not separator and it is visible*/
  547. for (; pos < menu.items.Count; pos++) {
  548. item = (MENUITEM)menu.items[pos];
  549. if (item.item.Separator == false && item.item.Visible == true)
  550. break;
  551. }
  552. }
  553. break;
  554. }
  555. case ItemNavigation.Previous: {
  556. if (menu.SelectedItem != null)
  557. pos = menu.SelectedItem.pos;
  558. /* Previous item that is not separator and it is visible*/
  559. for (pos--; pos >= 0; pos--) {
  560. item = (MENUITEM)menu.items[pos];
  561. if (item.item.Separator == false && item.item.Visible == true)
  562. break;
  563. }
  564. if (pos < 0 ) { /* Jump at the end of the menu*/
  565. pos = menu.items.Count - 1;
  566. /* Previous item that is not separator and it is visible*/
  567. for (; pos >= 0; pos--) {
  568. item = (MENUITEM)menu.items[pos];
  569. if (item.item.Separator == false && item.item.Visible == true)
  570. break;
  571. }
  572. }
  573. break;
  574. }
  575. default:
  576. break;
  577. }
  578. return (MENUITEM)menu.items[pos];
  579. }
  580. static public bool ProcessKeys (IntPtr hMenu, ref Message msg, Keys keyData, TRACKER tracker)
  581. {
  582. MENU menu = GetMenuFromID (hMenu);
  583. MENUITEM item;
  584. switch (keyData) {
  585. case Keys.Up: {
  586. item = GetNextItem (hMenu, ItemNavigation.Previous);
  587. if (item != null)
  588. MenuAPI.SelectItem (hMenu, item, false, tracker);
  589. break;
  590. }
  591. case Keys.Down: {
  592. item = GetNextItem (hMenu, ItemNavigation.Next);
  593. if (item != null)
  594. MenuAPI.SelectItem (hMenu, item, false, tracker);
  595. break;
  596. }
  597. /* Menubar selects and opens next. Popups next or open*/
  598. case Keys.Right: {
  599. // Try to Expand popup first
  600. if (menu.SelectedItem.item.IsPopup) {
  601. ShowSubPopup (hMenu, menu.SelectedItem.hSubMenu, menu.SelectedItem, tracker);
  602. } else {
  603. MENU parent = null;
  604. if (menu.hParent != IntPtr.Zero)
  605. parent = GetMenuFromID (menu.hParent);
  606. if (parent != null && parent.bMenubar == true) {
  607. MENUITEM select_item = GetNextItem (menu.hParent, ItemNavigation.Next);
  608. MenuBarMove (menu.hParent, select_item, tracker);
  609. }
  610. }
  611. break;
  612. }
  613. case Keys.Left: {
  614. // Try to Collapse popup first
  615. if (menu.SelectedItem.item.IsPopup) {
  616. } else {
  617. MENU parent = null;
  618. if (menu.hParent != IntPtr.Zero)
  619. parent = GetMenuFromID (menu.hParent);
  620. if (parent != null && parent.bMenubar == true) {
  621. MENUITEM select_item = GetNextItem (menu.hParent, ItemNavigation.Previous);
  622. MenuBarMove (menu.hParent, select_item, tracker);
  623. }
  624. }
  625. break;
  626. }
  627. case Keys.Return: {
  628. MenuAPI.ExecFocusedItem (hMenu, menu.SelectedItem, tracker);
  629. break;
  630. }
  631. default:
  632. break;
  633. }
  634. /* Try if it is a menu hot key */
  635. item = MenuAPI.FindItemByKey (hMenu, msg.WParam);
  636. if (item != null) {
  637. MenuAPI.SelectItem (hMenu, item, false, tracker);
  638. return true;
  639. }
  640. return false;
  641. }
  642. }
  643. /*
  644. class PopUpWindow
  645. */
  646. internal class PopUpWindow : Control
  647. {
  648. private IntPtr hMenu;
  649. private MenuAPI.TRACKER tracker;
  650. public PopUpWindow (IntPtr hMenu, MenuAPI.TRACKER tracker): base ()
  651. {
  652. this.hMenu = hMenu;
  653. this.tracker = tracker;
  654. MouseDown += new MouseEventHandler (OnMouseDownPUW);
  655. MouseMove += new MouseEventHandler (OnMouseMovePUW);
  656. MouseUp += new MouseEventHandler (OnMouseUpPUW);
  657. Paint += new PaintEventHandler (OnPaintPUW);
  658. SetStyle (ControlStyles.UserPaint | ControlStyles.AllPaintingInWmPaint, true);
  659. SetStyle (ControlStyles.ResizeRedraw | ControlStyles.Opaque, true);
  660. is_visible = false;
  661. }
  662. protected override CreateParams CreateParams
  663. {
  664. get {
  665. CreateParams cp = base.CreateParams;
  666. cp.Caption = "Menu PopUp";
  667. cp.Style = unchecked ((int)(WindowStyles.WS_POPUP));
  668. cp.ExStyle |= (int)(WindowStyles.WS_EX_TOOLWINDOW | WindowStyles.WS_EX_TOPMOST);
  669. return cp;
  670. }
  671. }
  672. public void ShowWindow ()
  673. {
  674. Show ();
  675. Capture = true;
  676. RefreshItems ();
  677. Refresh ();
  678. }
  679. public new void LostFocus ()
  680. {
  681. Capture = false;
  682. }
  683. protected override void OnResize (EventArgs e)
  684. {
  685. base.OnResize (e);
  686. }
  687. private void OnPaintPUW (Object o, PaintEventArgs pevent)
  688. {
  689. ThemeEngine.Current.DrawPopupMenu (pevent.Graphics, hMenu, pevent.ClipRectangle, ClientRectangle);
  690. }
  691. public void HideWindow ()
  692. {
  693. Capture = false;
  694. Hide ();
  695. MenuAPI.MENU top_menu = MenuAPI.GetMenuFromID (tracker.hTopMenu);
  696. top_menu.bTracking = false;
  697. MenuAPI.HideSubPopups (tracker.hTopMenu);
  698. if (top_menu.bMenubar) {
  699. MenuAPI.MENUITEM item = MenuAPI.GetSelected (tracker.hTopMenu);
  700. if (item != null) {
  701. MenuAPI.UnSelectItem (tracker.hTopMenu, item);
  702. }
  703. } else { // Context Menu
  704. ((PopUpWindow)top_menu.Wnd).Hide ();
  705. }
  706. }
  707. private void OnMouseDownPUW (object sender, MouseEventArgs e)
  708. {
  709. /* Click outside the client area*/
  710. if (ClientRectangle.Contains (e.X, e.Y) == false) {
  711. HideWindow ();
  712. }
  713. MenuAPI.MENUITEM item = MenuAPI.FindItemByCoords (hMenu, new Point (e.X, e.Y));
  714. if (item != null) {
  715. MenuAPI.ExecFocusedItem (hMenu, item, tracker);
  716. }
  717. }
  718. private void OnMouseUpPUW (object sender, MouseEventArgs e)
  719. {
  720. /* Click in an item area*/
  721. MenuAPI.MENUITEM item = MenuAPI.FindItemByCoords (hMenu, new Point (e.X, e.Y));
  722. if (item != null) {
  723. if (item.item.Enabled) {
  724. HideWindow ();
  725. }
  726. item.item.PerformClick ();
  727. }
  728. }
  729. private void OnMouseMovePUW (object sender, MouseEventArgs e)
  730. {
  731. MenuAPI.MENUITEM item = MenuAPI.FindItemByCoords (hMenu, new Point (e.X, e.Y));
  732. if (item != null) {
  733. MenuAPI.SelectItem (hMenu, item, false, tracker);
  734. } else {
  735. MenuAPI.MENU menu_parent = null;
  736. if (tracker.hTopMenu != IntPtr.Zero)
  737. menu_parent = MenuAPI.GetMenuFromID (tracker.hTopMenu);
  738. if (menu_parent == null)
  739. return;
  740. if (menu_parent.bMenubar) {
  741. MenuAPI.TrackBarMouseEvent (tracker.hTopMenu,
  742. this, new MouseEventArgs(e.Button, e.Clicks, MousePosition.X, MousePosition.Y, e.Delta),
  743. MenuAPI.MenuMouseEvent.Move, tracker);
  744. }
  745. IntPtr hMenuItem = IntPtr.Zero;
  746. MenuAPI.MENUITEM item_found = null;
  747. if (MenuAPI.FindSubItemByCoord (tracker.hTopMenu, MousePosition, ref hMenuItem, ref item_found) == false)
  748. return;
  749. MenuAPI.SelectItem (hMenuItem, item_found, false, tracker);
  750. }
  751. }
  752. protected override bool ProcessCmdKey (ref Message msg, Keys keyData)
  753. {
  754. return MenuAPI.ProcessKeys (hMenu, ref msg, keyData, tracker);
  755. }
  756. protected override void CreateHandle ()
  757. {
  758. base.CreateHandle ();
  759. RefreshItems ();
  760. }
  761. // Called when the number of items has changed
  762. internal void RefreshItems ()
  763. {
  764. MenuAPI.MENU menu = MenuAPI.GetMenuFromID (hMenu);
  765. ThemeEngine.Current.CalcPopupMenuSize (DeviceContext, hMenu);
  766. if ((Location.X + menu.Width) > SystemInformation.WorkingArea.Width) {
  767. Location = new Point (Location.X - menu.Width, Location.Y);
  768. }
  769. if ((Location.Y + menu.Height) > SystemInformation.WorkingArea.Height) {
  770. Location = new Point (Location.X, Location.Y - menu.Height);
  771. }
  772. Width = menu.Width;
  773. Height = menu.Height;
  774. }
  775. }
  776. }