MenuAPI.cs 30 KB

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