MenuAPI.cs 30 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159
  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 Novell, Inc.
  21. //
  22. // Authors:
  23. // Jordi Mas i Hernandez, [email protected]
  24. //
  25. // NOT COMPLETE
  26. using System.Drawing;
  27. using System.Drawing.Text;
  28. using System.Collections;
  29. namespace System.Windows.Forms
  30. {
  31. /*
  32. This class mimics the Win32 API Menu functionality
  33. When writting this code the Wine project was of great help to
  34. understand the logic behind some Win32 issues. Thanks to them. Jordi,
  35. */
  36. internal class MenuAPI
  37. {
  38. static StringFormat string_format_text = new StringFormat ();
  39. static StringFormat string_format_shortcut = new StringFormat ();
  40. static StringFormat string_format_menubar_text = new StringFormat ();
  41. static ArrayList menu_list = new ArrayList ();
  42. static Font MENU_FONT = new Font (FontFamily.GenericSansSerif, 8.25f);
  43. static int POPUP_ARROW_WITDH;
  44. static int POPUP_ARROW_HEIGHT;
  45. const int SEPARATOR_HEIGHT = 5;
  46. const int SM_CXBORDER = 1;
  47. const int SM_CYBORDER = 1;
  48. const int SM_CXMENUCHECK = 14; // Width of the menu check
  49. const int SM_CYMENUCHECK = 14; // Height of the menu check
  50. const int SM_CXARROWCHECK = 16; // Width of the arrow
  51. const int SM_CYARROWCHECK = 16; // Height of the arrow
  52. const int SM_CYMENU = 18; // Minimum height of a menu
  53. const int MENU_TAB_SPACE = 8; // Pixels added to the width of an item because of a tab
  54. const int MENU_BAR_ITEMS_SPACE = 8; // Space between menu bar items
  55. public class MENU
  56. {
  57. public MF Flags; // Menu flags (MF_POPUP, MF_SYSMENU)
  58. public int Width; // Width of the whole menu
  59. public int Height; // Height of the whole menu
  60. public Control Wnd; // In a Popup menu is the PopupWindow and in a MenuBar the Form
  61. public ArrayList items; // Array of menu items
  62. public int FocusedItem; // Currently focused item
  63. public IntPtr hParent;
  64. public TRACKER tracker;
  65. public MENUITEM SelectedItem; // Currently focused item
  66. public bool bMenubar;
  67. public MENU ()
  68. {
  69. Wnd = null;
  70. hParent = IntPtr.Zero;
  71. items = new ArrayList ();
  72. Flags = MF.MF_INSERT;
  73. Width = Height = FocusedItem = 0;
  74. tracker = new TRACKER ();
  75. bMenubar = false;
  76. }
  77. }
  78. public class MENUITEM
  79. {
  80. public MenuItem item;
  81. public Rectangle rect;
  82. public int fMask;
  83. public int fType;
  84. public MF fState;
  85. public int wID;
  86. public IntPtr hSubMenu;
  87. public int xTab;
  88. public int pos; /* Position in the menuitems array*/
  89. public MENUITEM ()
  90. {
  91. xTab = 0;
  92. fMask = 0;
  93. wID = 0;
  94. pos = 0;
  95. rect = new Rectangle ();
  96. }
  97. };
  98. public class TRACKER
  99. {
  100. public IntPtr hCurrentMenu;
  101. public IntPtr hTopMenu;
  102. public TRACKER ()
  103. {
  104. hCurrentMenu = hTopMenu = IntPtr.Zero;
  105. }
  106. };
  107. public enum MenuMouseEvent
  108. {
  109. Down,
  110. Move,
  111. }
  112. internal enum ItemNavigation
  113. {
  114. First,
  115. Last,
  116. Next,
  117. Previous,
  118. }
  119. internal enum MF
  120. {
  121. MF_INSERT = 0x0,
  122. MF_APPEND = 0x100,
  123. MF_DELETE = 0x200,
  124. MF_REMOVE = 0x1000,
  125. MF_BYCOMMAND = 0,
  126. MF_BYPOSITION = 0x400,
  127. MF_SEPARATOR = 0x800,
  128. MF_ENABLED = 0,
  129. MF_GRAYED = 1,
  130. MF_DISABLED = 2,
  131. MF_UNCHECKED = 0,
  132. MF_CHECKED = 8,
  133. MF_USECHECKBITMAPS = 0x200,
  134. MF_STRING = 0,
  135. MF_BITMAP = 4,
  136. MF_OWNERDRAW = 0x100,
  137. MF_POPUP = 0x10,
  138. MF_MENUBARBREAK = 0x20,
  139. MF_MENUBREAK = 0x40,
  140. MF_UNHILITE = 0,
  141. MF_HILITE = 0x80,
  142. MF_DEFAULT = 0x1000,
  143. MF_SYSMENU = 0x2000,
  144. MF_HELP = 0x4000,
  145. MF_RIGHTJUSTIFY = 0x4000,
  146. MF_MENUBAR = 0x8000 // Internal
  147. }
  148. static MenuAPI ()
  149. {
  150. string_format_text.LineAlignment = StringAlignment.Center;
  151. string_format_text.Alignment = StringAlignment.Near;
  152. string_format_text.HotkeyPrefix = HotkeyPrefix.Show;
  153. string_format_shortcut.LineAlignment = StringAlignment.Center;
  154. string_format_shortcut.Alignment = StringAlignment.Far;
  155. string_format_menubar_text.LineAlignment = StringAlignment.Center;
  156. string_format_menubar_text.Alignment = StringAlignment.Center;
  157. string_format_menubar_text.HotkeyPrefix = HotkeyPrefix.Show;
  158. }
  159. static public IntPtr StoreMenuID (MENU menu)
  160. {
  161. int id = menu_list.Add (menu);
  162. return (IntPtr)(id + 1);
  163. }
  164. static public MENU GetMenuFromID (IntPtr ptr)
  165. {
  166. int id = (int)ptr;
  167. id = id - 1;
  168. return (MENU) menu_list[id];
  169. }
  170. static public IntPtr CreateMenu ()
  171. {
  172. MENU menu = new MENU ();
  173. return StoreMenuID (menu);
  174. }
  175. static public IntPtr CreatePopupMenu ()
  176. {
  177. MENU popMenu = new MENU ();
  178. popMenu.Flags |= MF.MF_POPUP;
  179. return StoreMenuID (popMenu);
  180. }
  181. static public int InsertMenuItem (IntPtr hMenu, int uItem, bool fByPosition, MenuItem item, ref IntPtr hSubMenu)
  182. {
  183. int id;
  184. if (fByPosition == false)
  185. throw new NotImplementedException ();
  186. // Insert the item
  187. MENU menu = GetMenuFromID (hMenu);
  188. if ((uint)uItem > menu.items.Count)
  189. uItem = menu.items.Count;
  190. MENUITEM menu_item = new MENUITEM ();
  191. menu_item.item = item;
  192. if (item.IsPopup) {
  193. menu_item.hSubMenu = CreatePopupMenu ();
  194. MENU submenu = GetMenuFromID (menu_item.hSubMenu);
  195. submenu.hParent = hMenu;
  196. }
  197. else
  198. menu_item.hSubMenu = IntPtr.Zero;
  199. //menu_item.Flags |= MF.MF_POPUP;
  200. hSubMenu = menu_item.hSubMenu;
  201. id = menu.items.Count;
  202. menu_item.pos = menu.items.Count;
  203. menu.items.Insert (uItem, menu_item);
  204. return id;
  205. }
  206. // X and Y are screen coordinates
  207. static public bool TrackPopupMenu (IntPtr hTopMenu, IntPtr hMenu, Point pnt, bool bMenubar, Control Wnd)
  208. {
  209. MENU menu = GetMenuFromID (hMenu);
  210. PopUpWindow popup = new PopUpWindow (hMenu);
  211. menu.Wnd = popup;
  212. menu.tracker.hCurrentMenu = hMenu;
  213. menu.tracker.hTopMenu = hTopMenu;
  214. //menu.bMenubar = bMenubar;
  215. MENUITEM select_item = GetNextItem (hMenu, ItemNavigation.First);
  216. if (select_item != null)
  217. MenuAPI.SelectItem (hMenu, select_item, false);
  218. popup.Location = popup.PointToClient (pnt);
  219. popup.ShowWindow ();
  220. menu.Wnd.Refresh ();
  221. Application.Run ();
  222. //popup.DestroyHandle ();
  223. menu.Wnd = null;
  224. return true;
  225. }
  226. /*
  227. Menu drawing API
  228. */
  229. static public void CalcItemSize (Graphics dc, MENUITEM item, int y, int x, bool menuBar)
  230. {
  231. item.rect.Y = y;
  232. item.rect.X = x;
  233. if (item.item.Visible == false)
  234. return;
  235. if (item.item.Separator == true) {
  236. item.rect.Height = SEPARATOR_HEIGHT / 2;
  237. item.rect.Width = -1;
  238. return;
  239. }
  240. SizeF size;
  241. size = dc.MeasureString (item.item.Text, MENU_FONT);
  242. item.rect.Width = (int) size.Width;
  243. item.rect.Height = (int) size.Height;
  244. if (!menuBar) {
  245. if (item.item.Shortcut != Shortcut.None && item.item.ShowShortcut) {
  246. item.xTab = SM_CXMENUCHECK + MENU_TAB_SPACE + (int) size.Width;
  247. size = dc.MeasureString (" " + item.item.GetShortCutText (), MENU_FONT);
  248. item.rect.Width += MENU_TAB_SPACE + (int) size.Width;
  249. }
  250. item.rect.Width += 4 + (SM_CXMENUCHECK * 2);
  251. }
  252. else {
  253. item.rect.Width += MENU_BAR_ITEMS_SPACE;
  254. x += item.rect.Width;
  255. }
  256. if (item.rect.Height < SM_CYMENU - 1)
  257. item.rect.Height = SM_CYMENU - 1;
  258. //Console.WriteLine ("CalcItemSize " + item.rect);
  259. }
  260. static public void CalcPopupMenuSize (Graphics dc, IntPtr hMenu)
  261. {
  262. int x = 3;
  263. int start = 0;
  264. int i, n, y, max;
  265. MENU menu = GetMenuFromID (hMenu);
  266. menu.Height = 0;
  267. while (start < menu.items.Count) {
  268. y = 2;
  269. max = 0;
  270. for (i = start; i < menu.items.Count; i++) {
  271. MENUITEM item = (MENUITEM) menu.items[i];
  272. if ((i != start) && (item.item.Break || item.item.BarBreak))
  273. break;
  274. CalcItemSize (dc, item, y, x, false);
  275. y += item.rect.Height;
  276. if (item.rect.Width > max)
  277. max = item.rect.Width;
  278. }
  279. // Reemplace the -1 by the menu width (separators)
  280. for (n = start; n < i; n++, start++) {
  281. MENUITEM item = (MENUITEM) menu.items[n];
  282. item.rect.Width = max;
  283. }
  284. if (y > menu.Height)
  285. menu.Height = y;
  286. x+= max;
  287. }
  288. menu.Width = x;
  289. //space for border
  290. menu.Width += 2;
  291. menu.Height += 2;
  292. menu.Width += SM_CXBORDER;
  293. menu.Height += SM_CYBORDER;
  294. //Console.WriteLine ("CalcPopupMenuSize {0} {1}", menu.Width, menu.Height);
  295. }
  296. static public void DrawMenuItem (Graphics dc, MENUITEM item, int menu_height, bool menuBar)
  297. {
  298. StringFormat string_format;
  299. if (item.item.Visible == false)
  300. return;
  301. if (menuBar)
  302. string_format = string_format_menubar_text;
  303. else
  304. string_format = string_format_text;
  305. if (item.item.Separator == true) {
  306. dc.DrawLine (ThemeEngine.Current.ResPool.GetPen (ThemeEngine.Current.ColorButtonShadow),
  307. item.rect.X, item.rect.Y, item.rect.X + item.rect.Width, item.rect.Y);
  308. dc.DrawLine (ThemeEngine.Current.ResPool.GetPen (ThemeEngine.Current.ColorButtonHilight),
  309. item.rect.X, item.rect.Y + 1, item.rect.X + item.rect.Width, item.rect.Y + 1);
  310. return;
  311. }
  312. Rectangle rect_text = item.rect;
  313. if (!menuBar)
  314. rect_text.X += SM_CXMENUCHECK;
  315. if (item.item.BarBreak) { /* Draw vertical break bar*/
  316. Rectangle rect = item.rect;
  317. rect.Y++;
  318. rect.Width = 3;
  319. rect.Height = menu_height - 6;
  320. dc.DrawLine (ThemeEngine.Current.ResPool.GetPen (ThemeEngine.Current.ColorButtonShadow),
  321. rect.X, rect.Y , rect.X, rect.Y + rect.Height);
  322. dc.DrawLine (ThemeEngine.Current.ResPool.GetPen (ThemeEngine.Current.ColorButtonHilight),
  323. rect.X + 1, rect.Y , rect.X +1, rect.Y + rect.Height);
  324. }
  325. //Console.WriteLine ("DrawMenuItem {0}, {1}, {2}", item.item.Text, item.rect, rect_text);
  326. if ((item.fState & MF.MF_HILITE) == MF.MF_HILITE) {
  327. Rectangle rect = item.rect;
  328. rect.X++;
  329. rect.Width -=2;
  330. dc.FillRectangle (ThemeEngine.Current.ResPool.GetSolidBrush
  331. (ThemeEngine.Current.ColorHilight), rect);
  332. }
  333. if (item.item.Enabled) {
  334. Color color_text;
  335. if ((item.fState & MF.MF_HILITE) == MF.MF_HILITE)
  336. color_text = ThemeEngine.Current.ColorHilightText;
  337. else
  338. color_text = ThemeEngine.Current.ColorMenuText;
  339. dc.DrawString (item.item.Text, MENU_FONT,
  340. ThemeEngine.Current.ResPool.GetSolidBrush (color_text),
  341. rect_text, string_format);
  342. if (!menuBar && item.item.Shortcut != Shortcut.None && item.item.ShowShortcut) {
  343. string str = item.item.GetShortCutText ();
  344. Rectangle rect = rect_text;
  345. rect.X = item.xTab;
  346. rect.Width -= item.xTab;
  347. dc.DrawString (str, MENU_FONT, ThemeEngine.Current.ResPool.GetSolidBrush (color_text),
  348. rect, string_format_shortcut);
  349. }
  350. }
  351. else {
  352. ControlPaint.DrawStringDisabled (dc,
  353. item.item.Text, MENU_FONT, Color.Black, rect_text,
  354. string_format);
  355. }
  356. /* Draw arrow */
  357. if (menuBar == false && item.item.IsPopup) {
  358. Bitmap bmp = new Bitmap (SM_CXARROWCHECK, SM_CYARROWCHECK);
  359. Graphics gr = Graphics.FromImage (bmp);
  360. Rectangle rect_arrow = new Rectangle (0, 0, SM_CXARROWCHECK, SM_CYARROWCHECK);
  361. ControlPaint.DrawMenuGlyph (gr, rect_arrow, MenuGlyph.Arrow);
  362. bmp.MakeTransparent ();
  363. dc.DrawImage (bmp, item.rect.X + item.rect.Width - SM_CXARROWCHECK,
  364. item.rect.Y + ((item.rect.Height - SM_CYARROWCHECK) /2));
  365. gr.Dispose ();
  366. bmp.Dispose ();
  367. }
  368. /* Draw checked or radio */
  369. if (menuBar == false && item.item.Checked) {
  370. Rectangle area = item.rect;
  371. Bitmap bmp = new Bitmap (SM_CXMENUCHECK, SM_CYMENUCHECK);
  372. Graphics gr = Graphics.FromImage (bmp);
  373. Rectangle rect_arrow = new Rectangle (0, 0, SM_CXMENUCHECK, SM_CYMENUCHECK);
  374. if (item.item.RadioCheck)
  375. ControlPaint.DrawMenuGlyph (gr, rect_arrow, MenuGlyph.Bullet);
  376. else
  377. ControlPaint.DrawMenuGlyph (gr, rect_arrow, MenuGlyph.Checkmark);
  378. bmp.MakeTransparent ();
  379. dc.DrawImage (bmp, area.X, item.rect.Y + ((item.rect.Height - SM_CYMENUCHECK) / 2));
  380. gr.Dispose ();
  381. bmp.Dispose ();
  382. }
  383. }
  384. static public void DrawPopupMenu (Graphics dc, IntPtr hMenu, Rectangle cliparea, Rectangle rect)
  385. {
  386. MENU menu = GetMenuFromID (hMenu);
  387. dc.FillRectangle (ThemeEngine.Current.ResPool.GetSolidBrush
  388. (ThemeEngine.Current.ColorMenu), cliparea);
  389. /* Draw menu borders */
  390. dc.DrawLine (ThemeEngine.Current.ResPool.GetPen (ThemeEngine.Current.ColorHilightText),
  391. rect.X, rect.Y, rect.X + rect.Width, rect.Y);
  392. dc.DrawLine (ThemeEngine.Current.ResPool.GetPen (ThemeEngine.Current.ColorHilightText),
  393. rect.X, rect.Y, rect.X, rect.Y + rect.Height);
  394. dc.DrawLine (ThemeEngine.Current.ResPool.GetPen (ThemeEngine.Current.ColorButtonShadow),
  395. rect.X + rect.Width - 1 , rect.Y , rect.X + rect.Width - 1, rect.Y + rect.Height);
  396. dc.DrawLine (ThemeEngine.Current.ResPool.GetPen (ThemeEngine.Current.ColorButtonDkShadow),
  397. rect.X + rect.Width, rect.Y , rect.X + rect.Width, rect.Y + rect.Height);
  398. dc.DrawLine (ThemeEngine.Current.ResPool.GetPen (ThemeEngine.Current.ColorButtonShadow),
  399. rect.X , rect.Y + rect.Height - 1 , rect.X + rect.Width - 1, rect.Y + rect.Height -1);
  400. dc.DrawLine (ThemeEngine.Current.ResPool.GetPen (ThemeEngine.Current.ColorButtonDkShadow),
  401. rect.X , rect.Y + rect.Height, rect.X + rect.Width - 1, rect.Y + rect.Height);
  402. for (int i = 0; i < menu.items.Count; i++)
  403. if (cliparea.IntersectsWith (((MENUITEM) menu.items[i]).rect)) {
  404. DrawMenuItem (dc, (MENUITEM) menu.items[i], menu.Height, menu.bMenubar);
  405. }
  406. }
  407. // Updates the menu rect and returns the height
  408. static public int MenuBarCalcSize (Graphics dc, IntPtr hMenu, int width)
  409. {
  410. int x = 0;
  411. int i = 0;
  412. int y = 0;
  413. MENU menu = GetMenuFromID (hMenu);
  414. menu.Height = 0;
  415. MENUITEM item;
  416. while (i < menu.items.Count) {
  417. item = (MENUITEM) menu.items[i];
  418. CalcItemSize (dc, item, y, x, true);
  419. i = i + 1;
  420. if (x + item.rect.Width > width) {
  421. item.rect.X = 0;
  422. y += item.rect.Height;
  423. item.rect.Y = y;
  424. x = 0;
  425. }
  426. x += item.rect.Width;
  427. item.fState |= MF.MF_MENUBAR;
  428. if (y + item.rect.Height > menu.Height)
  429. menu.Height = item.rect.Height + y;
  430. }
  431. menu.Width = width;
  432. return menu.Height;
  433. }
  434. // Draws a menu bar in a Window
  435. static public void DrawMenuBar (Graphics dc, IntPtr hMenu, Rectangle rect)
  436. {
  437. MENU menu = GetMenuFromID (hMenu);
  438. Rectangle rect_menu = new Rectangle ();
  439. if (menu.Height == 0)
  440. MenuBarCalcSize (dc, hMenu, rect_menu.Width);
  441. rect.Height = menu.Height;
  442. rect.Width = menu.Width;
  443. for (int i = 0; i < menu.items.Count; i++)
  444. DrawMenuItem (dc, (MENUITEM) menu.items[i], menu.Height, true);
  445. }
  446. /*
  447. Menu handeling API
  448. */
  449. static public MENUITEM FindItemByCoords (IntPtr hMenu, Point pt)
  450. {
  451. MENU menu = GetMenuFromID (hMenu);
  452. for (int i = 0; i < menu.items.Count; i++) {
  453. MENUITEM item = (MENUITEM) menu.items[i];
  454. if (item.rect.Contains (pt)) {
  455. return item;
  456. }
  457. }
  458. return null;
  459. }
  460. static public void SelectItem (IntPtr hMenu, MENUITEM item, bool execute)
  461. {
  462. MENU menu = GetMenuFromID (hMenu);
  463. MENUITEM highlight_item = null;
  464. /* Already selected */
  465. for (int i = 0; i < menu.items.Count; i++) {
  466. MENUITEM it = (MENUITEM) menu.items[i];
  467. if ((it.fState & MF.MF_HILITE) == MF.MF_HILITE) {
  468. if (item.rect == it.rect)
  469. return;
  470. highlight_item = item;
  471. }
  472. }
  473. //Console.WriteLine ("SelectItem:Current is {0} {1}", tracker.hCurrentMenu, hMenu);
  474. if (menu.tracker.hCurrentMenu != hMenu) {
  475. Console.WriteLine ("Changing current menu!");
  476. HideSubPopups (hMenu);
  477. menu.tracker.hCurrentMenu = hMenu;
  478. }
  479. /* Unselect previous item*/
  480. for (int i = 0; i < menu.items.Count; i++) {
  481. MENUITEM it = (MENUITEM) menu.items[i];
  482. if ((it.fState & MF.MF_HILITE) == MF.MF_HILITE) {
  483. it.fState = item.fState & ~MF.MF_HILITE;
  484. menu.Wnd.Invalidate (it.rect);
  485. if (menu.bMenubar)
  486. Console.WriteLine ("**Select item invalidate {0}", it.rect);
  487. }
  488. //if (menu.items[i].Equals(item))
  489. // pos = i;
  490. }
  491. menu.SelectedItem = item;
  492. item.fState |= MF.MF_HILITE;
  493. //menu.items[pos] = item;
  494. if (menu.bMenubar)
  495. Console.WriteLine ("**Select item invalidate {0}", item.rect);
  496. menu.Wnd.Invalidate (item.rect);
  497. if (execute)
  498. ExecFocusedItem (hMenu, item);
  499. }
  500. /*
  501. Used when the user executes the action of an item (press enter, shortcut)
  502. or a sub-popup menu has to be shown
  503. */
  504. static public void ExecFocusedItem (IntPtr hMenu, MENUITEM item)
  505. {
  506. if (item.item.IsPopup) {
  507. ShowSubPopup (hMenu, item.hSubMenu, item);
  508. }
  509. else {
  510. // Execute function
  511. }
  512. }
  513. static public void ShowSubPopup (IntPtr hParent, IntPtr hMenu, MENUITEM item)
  514. {
  515. MENU menu = GetMenuFromID (hMenu);
  516. if (menu.Wnd != null) /* Already showing */
  517. return;
  518. if (item.item.Enabled == false)
  519. return;
  520. MENU menu_parent = GetMenuFromID (hParent);
  521. PopUpWindow popup = new PopUpWindow (hMenu);
  522. ((PopUpWindow)menu_parent.Wnd).LostFocus ();
  523. menu.Wnd = popup;
  524. menu.tracker.hCurrentMenu = hMenu;
  525. //Console.WriteLine ("ShowSubPopup:Setting current to {0}", menu.tracker.hCurrentMenu);
  526. Point pnt = new Point ();
  527. pnt.X = item.rect.X + item.rect.Width;
  528. pnt.Y = item.rect.Y + 1;
  529. //Console.WriteLine ("ShowSubPopup prev:" + pnt);
  530. pnt = menu_parent.Wnd.PointToScreen (pnt);
  531. popup.Location = pnt;
  532. MENUITEM select_item = GetNextItem (hMenu, ItemNavigation.First);
  533. if (select_item != null)
  534. MenuAPI.SelectItem (hMenu, select_item, false);
  535. popup.ShowWindow ();
  536. popup.Refresh ();
  537. //Console.WriteLine ("ShowSubPopup location:" + popup.Location);
  538. }
  539. /* Hides all the submenus open in a menu */
  540. static public void HideSubPopups (IntPtr hMenu)
  541. {
  542. //Console.WriteLine ("HideSubPopups: " + hMenu);
  543. MENU menu = GetMenuFromID (hMenu);
  544. MENUITEM item;
  545. for (int i = 0; i < menu.items.Count; i++) {
  546. item = (MENUITEM) menu.items[i];
  547. if (item.item.IsPopup) {
  548. MENU sub_menu = GetMenuFromID (item.hSubMenu);
  549. if (sub_menu.Wnd != null) {
  550. //Console.WriteLine ("Hiding!");
  551. HideSubPopups (item.hSubMenu);
  552. ((PopUpWindow)sub_menu.Wnd).Destroy ();
  553. sub_menu.Wnd = null;
  554. }
  555. }
  556. }
  557. }
  558. static public void DestroyMenu (IntPtr hMenu)
  559. {
  560. if (hMenu == IntPtr.Zero)
  561. return;
  562. MENU menu = GetMenuFromID (hMenu);
  563. MENUITEM item;
  564. for (int i = 0; i < menu.items.Count; i++) {
  565. item = (MENUITEM) menu.items[i];
  566. //Console.WriteLine ("Destroy item: "+ item.item.Text + " pop:" + item.item.IsPopup);
  567. if (item.item.IsPopup) {
  568. MENU sub_menu = GetMenuFromID (item.hSubMenu);
  569. if (sub_menu != null && sub_menu.Wnd != null) {
  570. // TODO: Remove from list
  571. HideSubPopups (item.hSubMenu);
  572. DestroyMenu (item.hSubMenu);
  573. }
  574. }
  575. }
  576. // TODO: Remove from list
  577. // Do not destroy the window of a Menubar
  578. if (menu.Wnd != null && menu.bMenubar == false) {
  579. ((PopUpWindow)menu.Wnd).Destroy ();
  580. menu.Wnd = null;
  581. }
  582. }
  583. static public void SetMenuBarWindow (IntPtr hMenu, Control wnd)
  584. {
  585. Console.WriteLine ("SetMenuBarWindow {0}", hMenu);
  586. MENU menu = GetMenuFromID (hMenu);
  587. menu.Wnd = wnd;
  588. menu.bMenubar = true;
  589. }
  590. static private void MenuBarMove (IntPtr hMenu, MENUITEM item)
  591. {
  592. MENU menu = GetMenuFromID (hMenu);
  593. Point pnt = new Point (item.rect.X, item.rect.Y + item.rect.Height);
  594. pnt = menu.Wnd.PointToScreen (pnt);
  595. Console.WriteLine ("MenuBarMove {0} {1}", item.item.Text, menu.tracker.hCurrentMenu);
  596. MenuAPI.SelectItem (hMenu, item, false);
  597. MenuAPI.DestroyMenu (menu.tracker.hCurrentMenu);
  598. menu.tracker.hCurrentMenu = hMenu;
  599. MenuAPI.TrackPopupMenu (hMenu, item.hSubMenu, pnt, false, null);
  600. }
  601. // Function that process all menubar mouse events
  602. static public void TrackBarMouseEvent (IntPtr hMenu, Control wnd, MouseEventArgs e, MenuMouseEvent eventype)
  603. {
  604. MENU menu = GetMenuFromID (hMenu);
  605. switch (eventype) {
  606. case MenuMouseEvent.Down: {
  607. MenuAPI.MENUITEM item = MenuAPI.FindItemByCoords (hMenu, new Point (e.X, e.Y));
  608. if (item != null && menu.SelectedItem != item)
  609. MenuBarMove (hMenu, item);
  610. break;
  611. }
  612. case MenuMouseEvent.Move: {
  613. if (menu.tracker.hCurrentMenu != IntPtr.Zero) {
  614. MenuAPI.MENUITEM item = MenuAPI.FindItemByCoords (hMenu, new Point (e.X, e.Y));
  615. if (item != null && menu.SelectedItem != item)
  616. MenuBarMove (hMenu, item);
  617. }
  618. break;
  619. }
  620. default:
  621. break;
  622. }
  623. }
  624. static public MENUITEM FindItemByKey (IntPtr hMenu, IntPtr key)
  625. {
  626. MENU menu = GetMenuFromID (hMenu);
  627. MENUITEM item;
  628. char key_char = (char ) (key.ToInt32() & 0xff);
  629. key_char = Char.ToUpper (key_char);
  630. for (int i = 0; i < menu.items.Count; i++) {
  631. item = (MENUITEM) menu.items[i];
  632. //Console.WriteLine ("text {0} mnenonic {1} {2}", item.item.Text, item.item.Mnemonic, key_char);
  633. if (item.item.Mnemonic == '\0')
  634. continue;
  635. if (item.item.Mnemonic == key_char)
  636. return item;
  637. }
  638. return null;
  639. }
  640. // Get the next or previous selectable item on a menu
  641. static public MENUITEM GetNextItem (IntPtr hMenu, ItemNavigation navigation)
  642. {
  643. MENU menu = GetMenuFromID (hMenu);
  644. int pos = 0;
  645. bool selectable_items = false;
  646. MENUITEM item;
  647. // Check if there is at least a selectable item
  648. for (int i = 0; i < menu.items.Count; i++) {
  649. item = (MENUITEM)menu.items[i];
  650. if (item.item.Separator == false && item.item.Visible == true) {
  651. selectable_items = true;
  652. break;
  653. }
  654. }
  655. if (selectable_items == false)
  656. return null;
  657. switch (navigation) {
  658. case ItemNavigation.First: {
  659. pos = 0;
  660. /* Next item that is not separator and it is visible*/
  661. for (; pos < menu.items.Count; pos++) {
  662. item = (MENUITEM)menu.items[pos];
  663. if (item.item.Separator == false && item.item.Visible == true)
  664. break;
  665. }
  666. if (pos >= menu.items.Count) { /* Jump at the start of the menu */
  667. pos = 0;
  668. /* Next item that is not separator and it is visible*/
  669. for (; pos < menu.items.Count; pos++) {
  670. item = (MENUITEM)menu.items[pos];
  671. if (item.item.Separator == false && item.item.Visible == true)
  672. break;
  673. }
  674. }
  675. break;
  676. }
  677. case ItemNavigation.Last: { // Not used
  678. break;
  679. }
  680. case ItemNavigation.Next: {
  681. if (menu.SelectedItem != null)
  682. pos = menu.SelectedItem.pos;
  683. /* Next item that is not separator and it is visible*/
  684. for (pos++; pos < menu.items.Count; pos++) {
  685. item = (MENUITEM)menu.items[pos];
  686. if (item.item.Separator == false && item.item.Visible == true)
  687. break;
  688. }
  689. if (pos >= menu.items.Count) { /* Jump at the start of the menu */
  690. pos = 0;
  691. /* Next item that is not separator and it is visible*/
  692. for (; pos < menu.items.Count; pos++) {
  693. item = (MENUITEM)menu.items[pos];
  694. if (item.item.Separator == false && item.item.Visible == true)
  695. break;
  696. }
  697. }
  698. break;
  699. }
  700. case ItemNavigation.Previous: {
  701. if (menu.SelectedItem != null)
  702. pos = menu.SelectedItem.pos;
  703. /* Previous item that is not separator and it is visible*/
  704. for (pos--; pos >= 0; pos--) {
  705. item = (MENUITEM)menu.items[pos];
  706. if (item.item.Separator == false && item.item.Visible == true)
  707. break;
  708. }
  709. if (pos < 0 ) { /* Jump at the end of the menu*/
  710. pos = menu.items.Count - 1;
  711. /* Previous item that is not separator and it is visible*/
  712. for (; pos >= 0; pos--) {
  713. item = (MENUITEM)menu.items[pos];
  714. if (item.item.Separator == false && item.item.Visible == true)
  715. break;
  716. }
  717. }
  718. break;
  719. }
  720. default:
  721. break;
  722. }
  723. return (MENUITEM)menu.items[pos];
  724. }
  725. static public bool ProcessKeys (IntPtr hMenu, ref Message msg, Keys keyData)
  726. {
  727. MENU menu = GetMenuFromID (hMenu);
  728. Console.WriteLine ("Menu.ProcessCmdKey {0} {1} {2}",keyData, msg.LParam, msg.WParam);
  729. MENUITEM item;
  730. switch (keyData) {
  731. case Keys.Up: {
  732. item = GetNextItem (hMenu, ItemNavigation.Previous);
  733. if (item != null)
  734. MenuAPI.SelectItem (hMenu, item, false);
  735. break;
  736. }
  737. case Keys.Down: {
  738. item = GetNextItem (hMenu, ItemNavigation.Next);
  739. if (item != null)
  740. MenuAPI.SelectItem (hMenu, item, false);
  741. break;
  742. }
  743. /* Menubar selects and opens next. Popups next or open*/
  744. case Keys.Right: {
  745. //Console.WriteLine ("Key.Right next menubar item info {0}", menu.hParent);
  746. // Try to Expand popup first
  747. if (menu.SelectedItem.item.IsPopup) {
  748. ShowSubPopup (hMenu, menu.SelectedItem.hSubMenu, menu.SelectedItem);
  749. Console.WriteLine ("Key.Right next menubar item Showing popup");
  750. } else {
  751. MENU parent = null;
  752. if (menu.hParent != IntPtr.Zero)
  753. parent = GetMenuFromID (menu.hParent);
  754. if (parent != null && parent.bMenubar == true) {
  755. MENUITEM select_item = GetNextItem (menu.hParent, ItemNavigation.Next);
  756. MenuBarMove (menu.hParent, select_item);
  757. }
  758. }
  759. break;
  760. }
  761. case Keys.Left: {
  762. //Console.WriteLine ("Key.Right next menubar item info {0}", menu.hParent);
  763. // Try to Collapse popup first
  764. if (menu.SelectedItem.item.IsPopup) {
  765. } else {
  766. MENU parent = null;
  767. if (menu.hParent != IntPtr.Zero)
  768. parent = GetMenuFromID (menu.hParent);
  769. if (parent != null && parent.bMenubar == true) {
  770. MENUITEM select_item = GetNextItem (menu.hParent, ItemNavigation.Previous);
  771. MenuBarMove (menu.hParent, select_item);
  772. }
  773. }
  774. break;
  775. }
  776. default:
  777. break;
  778. }
  779. /* Try if it is a menu hot key */
  780. item = MenuAPI.FindItemByKey (hMenu, msg.WParam);
  781. //Console.WriteLine ("menu item is null: " + (item == null));
  782. if (item != null) {
  783. Console.WriteLine ("HotKey found: item.text" + item.item.Text);
  784. MenuAPI.SelectItem (hMenu, item, false);
  785. return true;
  786. }
  787. return false;
  788. }
  789. }
  790. /*
  791. class PopUpWindow
  792. */
  793. internal class PopUpWindow : Control
  794. {
  795. private IntPtr hMenu;
  796. public PopUpWindow (IntPtr hMenu): base ()
  797. {
  798. this.hMenu = hMenu;
  799. MouseDown += new MouseEventHandler (OnMouseDownPUW);
  800. MouseMove += new MouseEventHandler (OnMouseMovePUW);
  801. MouseUp += new MouseEventHandler (OnMouseUpPUW);
  802. Paint += new PaintEventHandler (OnPaintPUW);
  803. SetStyle (ControlStyles.UserPaint | ControlStyles.AllPaintingInWmPaint, true);
  804. SetStyle (ControlStyles.ResizeRedraw | ControlStyles.Opaque, true);
  805. }
  806. protected override CreateParams CreateParams
  807. {
  808. get {
  809. CreateParams cp = base.CreateParams;
  810. cp.Style = unchecked ((int)(WindowStyles.WS_POPUP | WindowStyles.WS_VISIBLE | WindowStyles.WS_CLIPSIBLINGS | WindowStyles.WS_CLIPCHILDREN));
  811. cp.ExStyle |= (int)WindowStyles.WS_EX_TOOLWINDOW;
  812. return cp;
  813. }
  814. }
  815. public void ShowWindow ()
  816. {
  817. Capture = true;
  818. Show ();
  819. Refresh ();
  820. }
  821. public void Destroy ()
  822. {
  823. Capture = false;
  824. DestroyHandle ();
  825. }
  826. public void LostFocus ()
  827. {
  828. Capture = false;
  829. }
  830. protected override void OnResize(EventArgs e)
  831. {
  832. base.OnResize (e);
  833. //Console.WriteLine ("OnResize {0} {1} ", Width, Height);
  834. }
  835. private void OnPaintPUW (Object o, PaintEventArgs pevent)
  836. {
  837. if (Width <= 0 || Height <= 0 || Visible == false)
  838. return;
  839. Draw (pevent.ClipRectangle);
  840. pevent.Graphics.DrawImage (ImageBuffer, pevent.ClipRectangle, pevent.ClipRectangle, GraphicsUnit.Pixel);
  841. }
  842. private void OnMouseDownPUW (object sender, MouseEventArgs e)
  843. {
  844. //Console.WriteLine ("OnMouseDownPUW");
  845. /* Click outside the client area*/
  846. if (ClientRectangle.Contains (e.X, e.Y) == false) {
  847. Console.WriteLine ("Hide");
  848. Capture = false;
  849. Hide ();
  850. }
  851. }
  852. private void OnMouseUpPUW (object sender, MouseEventArgs e)
  853. {
  854. //Console.WriteLine ("OnMouseUpPUW");
  855. /* Click outside the client area*/
  856. MenuAPI.MENUITEM item = MenuAPI.FindItemByCoords (hMenu, new Point (e.X, e.Y));
  857. MenuAPI.MENU menu = MenuAPI.GetMenuFromID (hMenu);
  858. if (item != null && item.item.Enabled) {
  859. item.item.PerformClick ();
  860. MenuAPI.DestroyMenu (menu.tracker.hTopMenu);
  861. Capture = false;
  862. Refresh ();
  863. }
  864. }
  865. private void OnMouseMovePUW (object sender, MouseEventArgs e)
  866. {
  867. //Console.WriteLine ("OnMouseMovePUW");
  868. MenuAPI.MENUITEM item = MenuAPI.FindItemByCoords (hMenu, new Point (e.X, e.Y));
  869. MenuAPI.MENU menu = MenuAPI.GetMenuFromID (hMenu);
  870. if (item != null) {
  871. MenuAPI.SelectItem (hMenu, item, true);
  872. } else {
  873. MenuAPI.MENU menu_parent = null;
  874. if (menu.tracker.hTopMenu != IntPtr.Zero)
  875. menu_parent = MenuAPI.GetMenuFromID (menu.tracker.hTopMenu);
  876. if (menu_parent!=null && menu_parent.bMenubar) {
  877. Console.WriteLine ("MenuBar tracker move " + e.Y );
  878. Point pnt = PointToClient (MousePosition);
  879. MenuAPI.TrackBarMouseEvent (menu.tracker.hTopMenu,
  880. this, new MouseEventArgs(e.Button, e.Clicks, pnt.X, pnt.Y, e.Delta),
  881. MenuAPI.MenuMouseEvent.Move);
  882. }
  883. }
  884. }
  885. protected override bool ProcessCmdKey (ref Message msg, Keys keyData)
  886. {
  887. Console.WriteLine ("PopUpWindow.ProcessCmdKey");
  888. return MenuAPI.ProcessKeys (hMenu, ref msg, keyData);
  889. }
  890. protected override void CreateHandle ()
  891. {
  892. base.CreateHandle ();
  893. MenuAPI.MENU menu = MenuAPI.GetMenuFromID (hMenu);
  894. MenuAPI.CalcPopupMenuSize (DeviceContext, hMenu);
  895. Width = menu.Width;
  896. Height = menu.Height;
  897. Console.WriteLine ("CreateHandle {0} {1}", Width, Height);
  898. }
  899. private void Draw (Rectangle clip)
  900. {
  901. MenuAPI.DrawPopupMenu (DeviceContext, hMenu, clip, ClientRectangle);
  902. }
  903. }
  904. }