1 // Written by Christopher E. Miller
2 // See the included license.txt for copyright and license details.
3 
4 
5 ///
6 module dfl.menu;
7 
8 private import dfl.internal.dlib;
9 
10 private import dfl.internal.winapi, dfl.control, dfl.base, dfl.event;
11 private import dfl.internal.utf, dfl.drawing, dfl.application, dfl.collections;
12 
13 
14 version(DFL_NO_MENUS)
15 {
16 }
17 else
18 {
19 	///
20 	class ContextMenu: Menu // docmain
21 	{
22 		///
23 		final void show(Control control, Point pos)
24 		{
25 			SetForegroundWindow(control.handle);
26 			TrackPopupMenu(hmenu, TPM_LEFTALIGN | TPM_LEFTBUTTON | TPM_RIGHTBUTTON,
27 				pos.x, pos.y, 0, control.handle, null);
28 		}
29 		
30 		
31 		//EventHandler popup;
32 		Event!(ContextMenu, EventArgs) popup; ///
33 		
34 		
35 		// Used internally.
36 		this(HMENU hmenu, bool owned = true)
37 		{
38 			super(hmenu, owned);
39 			
40 			_init();
41 		}
42 		
43 		
44 		this()
45 		{
46 			super(CreatePopupMenu());
47 			
48 			_init();
49 		}
50 		
51 		
52 		~this()
53 		{
54 			Application.removeMenu(this);
55 			
56 			debug(APP_PRINT)
57 				cprintf("~ContextMenu\n");
58 		}
59 		
60 		
61 		protected override void onReflectedMessage(ref Message m)
62 		{
63 			super.onReflectedMessage(m);
64 			
65 			switch(m.msg)
66 			{
67 				case WM_INITMENU:
68 					assert(cast(HMENU)m.wParam == handle);
69 					
70 					//onPopup(EventArgs.empty);
71 					popup(this, EventArgs.empty);
72 					break;
73 				
74 				default:
75 			}
76 		}
77 		
78 		
79 		private:
80 		void _init()
81 		{
82 			Application.addContextMenu(this);
83 		}
84 	}
85 
86 
87 	///
88 	class MenuItem: Menu // docmain
89 	{
90 		///
91 		final @property void text(Dstring txt) // setter
92 		{
93 			if(!menuItems.length && txt == SEPARATOR_TEXT)
94 			{
95 				_type(_type() | MFT_SEPARATOR);
96 			}
97 			else
98 			{
99 				if(mparent)
100 				{
101 					MENUITEMINFOA mii;
102 					
103 					if(fType & MFT_SEPARATOR)
104 						fType = ~MFT_SEPARATOR;
105 					mii.cbSize = mii.sizeof;
106 					mii.fMask = MIIM_TYPE | MIIM_STATE; // Not setting the state can cause implicit disabled/gray if the text was empty.
107 					mii.fType = fType;
108 					mii.fState = fState;
109 					//mii.dwTypeData = stringToStringz(txt);
110 					
111 					mparent._setInfo(mid, false, &mii, txt);
112 				}
113 			}
114 			
115 			mtext = txt;
116 		}
117 		
118 		/// ditto
119 		final @property Dstring text() // getter
120 		{
121 			// if(mparent) fetch text ?
122 			return mtext;
123 		}
124 		
125 		
126 		///
127 		final @property void parent(Menu m) // setter
128 		{
129 			m.menuItems.add(this);
130 		}
131 		
132 		/// ditto
133 		final @property Menu parent() // getter
134 		{
135 			return mparent;
136 		}
137 		
138 		
139 		package final void _setParent(Menu newParent)
140 		{
141 			assert(!mparent);
142 			mparent = newParent;
143 			
144 			if(cast(size_t)mindex > mparent.menuItems.length)
145 				mindex = mparent.menuItems.length;
146 			
147 			_setParent();
148 		}
149 		
150 		
151 		private void _setParent()
152 		{
153 			MENUITEMINFOA mii;
154 			MenuItem miparent;
155 			
156 			mii.cbSize = mii.sizeof;
157 			mii.fMask = MIIM_TYPE | MIIM_STATE | MIIM_ID | MIIM_SUBMENU;
158 			mii.fType = fType;
159 			mii.fState = fState;
160 			mii.wID = mid;
161 			mii.hSubMenu = handle;
162 			//if(!(fType & MFT_SEPARATOR))
163 			//	mii.dwTypeData = stringToStringz(mtext);
164 			miparent = cast(MenuItem)mparent;
165 			if(miparent && !miparent.hmenu)
166 			{
167 				miparent.hmenu = CreatePopupMenu();
168 				
169 				if(miparent.parent() && miparent.parent.hmenu)
170 				{
171 					MENUITEMINFOA miiPopup;
172 					
173 					miiPopup.cbSize = miiPopup.sizeof;
174 					miiPopup.fMask = MIIM_SUBMENU;
175 					miiPopup.hSubMenu = miparent.hmenu;
176 					miparent.parent._setInfo(miparent._menuID, false, &miiPopup);
177 				}
178 			}
179 			mparent._insert(mindex, true, &mii, (fType & MFT_SEPARATOR) ? null : mtext);
180 		}
181 		
182 		
183 		package final void _unsetParent()
184 		{
185 			assert(mparent);
186 			assert(mparent.menuItems.length > 0);
187 			assert(mparent.hmenu);
188 			
189 			// Last child menu item, make the parent non-popup now.
190 			if(mparent.menuItems.length == 1)
191 			{
192 				MenuItem miparent;
193 				
194 				miparent = cast(MenuItem)mparent;
195 				if(miparent && miparent.hmenu)
196 				{
197 					MENUITEMINFOA miiPopup;
198 					
199 					miiPopup.cbSize = miiPopup.sizeof;
200 					miiPopup.fMask = MIIM_SUBMENU;
201 					miiPopup.hSubMenu = null;
202 					miparent.parent._setInfo(miparent._menuID, false, &miiPopup);
203 					
204 					miparent.hmenu = null;
205 				}
206 			}
207 			
208 			mparent = null;
209 			
210 			if(!Menu._compat092)
211 			{
212 				mindex = -1;
213 			}
214 		}
215 		
216 		
217 		///
218 		final @property void barBreak(bool byes) // setter
219 		{
220 			if(byes)
221 				_type(_type() | MFT_MENUBARBREAK);
222 			else
223 				_type(_type() & ~MFT_MENUBARBREAK);
224 		}
225 		
226 		/// ditto
227 		final @property bool barBreak() // getter
228 		{
229 			return (_type() & MFT_MENUBARBREAK) != 0;
230 		}
231 		
232 		
233 		// Can't be break().
234 		
235 		///
236 		final @property void breakItem(bool byes) // setter
237 		{
238 			if(byes)
239 				_type(_type() | MFT_MENUBREAK);
240 			else
241 				_type(_type() & ~MFT_MENUBREAK);
242 		}
243 		
244 		/// ditto
245 		final @property bool breakItem() // getter
246 		{
247 			return (_type() & MFT_MENUBREAK) != 0;
248 		}
249 		
250 		
251 		///
252 		final @property void checked(bool byes) // setter
253 		{
254 			if(byes)
255 				_state(_state() | MFS_CHECKED);
256 			else
257 				_state(_state() & ~MFS_CHECKED);
258 		}
259 		
260 		/// ditto
261 		final @property bool checked() // getter
262 		{
263 			return (_state() & MFS_CHECKED) != 0;
264 		}
265 		
266 		
267 		///
268 		final @property void defaultItem(bool byes) // setter
269 		{
270 			if(byes)
271 				_state(_state() | MFS_DEFAULT);
272 			else
273 				_state(_state() & ~MFS_DEFAULT);
274 		}
275 		
276 		/// ditto
277 		final @property bool defaultItem() // getter
278 		{
279 			return (_state() & MFS_DEFAULT) != 0;
280 		}
281 		
282 		
283 		///
284 		final @property void enabled(bool byes) // setter
285 		{
286 			if(byes)
287 				_state(_state() & ~MFS_GRAYED);
288 			else
289 				_state(_state() | MFS_GRAYED);
290 		}
291 		
292 		/// ditto
293 		final @property bool enabled() // getter
294 		{
295 			return (_state() & MFS_GRAYED) == 0;
296 		}
297 		
298 		
299 		///
300 		final @property void index(int idx) // setter
301 		{// Note: probably fails when the parent exists because mparent is still set and menuItems.insert asserts it's null.
302 			if(mparent)
303 			{
304 				if(cast(uint)idx > mparent.menuItems.length)
305 					throw new DflException("Invalid menu index");
306 				
307 				//RemoveMenu(mparent.handle, mid, MF_BYCOMMAND);
308 				mparent._remove(mid, MF_BYCOMMAND);
309 				mparent.menuItems._delitem(mindex);
310 				
311 				/+
312 				mindex = idx;
313 				_setParent();
314 				mparent.menuItems._additem(this);
315 				+/
316 				mparent.menuItems.insert(idx, this);
317 			}
318 			
319 			if(Menu._compat092)
320 			{
321 				mindex = idx;
322 			}
323 		}
324 		
325 		/// ditto
326 		final @property int index() // getter
327 		{
328 			return mindex;
329 		}
330 		
331 		
332 		override @property bool isParent() // getter
333 		{
334 			return handle != null; // ?
335 		}
336 		
337 		
338 		deprecated final @property void mergeOrder(int ord) // setter
339 		{
340 			//mergeord = ord;
341 		}
342 		
343 		deprecated final @property int mergeOrder() // getter
344 		{
345 			//return mergeord;
346 			return 0;
347 		}
348 		
349 		
350 		// TODO: mergeType().
351 		
352 		
353 		///
354 		// Returns a NUL char if none.
355 		final @property char mnemonic() // getter
356 		{
357 			bool singleAmp = false;
358 			
359 			foreach(char ch; mtext)
360 			{
361 				if(singleAmp)
362 				{
363 					if(ch == '&')
364 						singleAmp = false;
365 					else
366 						return ch;
367 				}
368 				else
369 				{
370 					if(ch == '&')
371 						singleAmp = true;
372 				}
373 			}
374 			
375 			return 0;
376 		}
377 		
378 		
379 		/+
380 		// TODO: implement owner drawn menus.
381 		
382 		final @property void ownerDraw(bool byes) // setter
383 		{
384 			
385 		}
386 		
387 		final @property bool ownerDraw() // getter
388 		{
389 			
390 		}
391 		+/
392 		
393 		
394 		///
395 		final @property void radioCheck(bool byes) // setter
396 		{
397 			auto par = parent;
398 			auto pidx = index;
399 			if(par)
400 				par.menuItems._removing(pidx, this);
401 			
402 			if(byes)
403 				//_type(_type() | MFT_RADIOCHECK);
404 				fType |= MFT_RADIOCHECK;
405 			else
406 				//_type(_type() & ~MFT_RADIOCHECK);
407 				fType &= ~MFT_RADIOCHECK;
408 			
409 			if(par)
410 				par.menuItems._added(pidx, this);
411 		}
412 		
413 		/// ditto
414 		final @property bool radioCheck() // getter
415 		{
416 			return (_type() & MFT_RADIOCHECK) != 0;
417 		}
418 		
419 		
420 		// TODO: shortcut(), showShortcut().
421 		
422 		
423 		/+
424 		// TODO: need to fake this ?
425 		
426 		final @property void visible(bool byes) // setter
427 		{
428 			// ?
429 			mvisible = byes;
430 		}
431 		
432 		final @property bool visible() // getter
433 		{
434 			return mvisible;
435 		}
436 		+/
437 		
438 		
439 		///
440 		final void performClick()
441 		{
442 			onClick(EventArgs.empty);
443 		}
444 		
445 		
446 		///
447 		final void performSelect()
448 		{
449 			onSelect(EventArgs.empty);
450 		}
451 		
452 		
453 		// Used internally.
454 		this(HMENU hmenu, bool owned = true) // package
455 		{
456 			super(hmenu, owned);
457 			_init();
458 		}
459 		
460 		
461 		///
462 		this(MenuItem[] items)
463 		{
464 			if(items.length)
465 			{
466 				HMENU hm = CreatePopupMenu();
467 				super(hm);
468 			}
469 			else
470 			{
471 				super();
472 			}
473 			_init();
474 			
475 			menuItems.addRange(items);
476 		}
477 		
478 		/// ditto
479 		this(Dstring text)
480 		{
481 			_init();
482 			
483 			this.text = text;
484 		}
485 		
486 		/// ditto
487 		this(Dstring text, MenuItem[] items)
488 		{
489 			if(items.length)
490 			{
491 				HMENU hm = CreatePopupMenu();
492 				super(hm);
493 			}
494 			else
495 			{
496 				super();
497 			}
498 			_init();
499 			
500 			this.text = text;
501 			
502 			menuItems.addRange(items);
503 		}
504 		
505 		/// ditto
506 		this()
507 		{
508 			_init();
509 		}
510 		
511 		
512 		~this()
513 		{
514 			Application.removeMenu(this);
515 			
516 			debug(APP_PRINT)
517 				cprintf("~MenuItem\n");
518 		}
519 		
520 		
521 		override Dstring toString()
522 		{
523 			return text;
524 		}
525 		
526 		
527 		override Dequ opEquals(Object o)
528 		{
529 			return text == getObjectString(o);
530 		}
531 		
532 		
533 		Dequ opEquals(Dstring val)
534 		{
535 			return text == val;
536 		}
537 		
538 		
539 		override int opCmp(Object o)
540 		{
541 			return stringICmp(text, getObjectString(o));
542 		}
543 		
544 		
545 		int opCmp(Dstring val)
546 		{
547 			return stringICmp(text, val);
548 		}
549 		
550 		
551 		protected override void onReflectedMessage(ref Message m)
552 		{
553 			super.onReflectedMessage(m);
554 			
555 			switch(m.msg)
556 			{
557 				case WM_COMMAND:
558 					assert(LOWORD(m.wParam) == mid);
559 					
560 					onClick(EventArgs.empty);
561 					break;
562 				
563 				case WM_MENUSELECT:
564 					onSelect(EventArgs.empty);
565 					break;
566 				
567 				case WM_INITMENUPOPUP:
568 					assert(!HIWORD(m.lParam));
569 					//assert(cast(HMENU)msg.wParam == mparent.handle);
570 					assert(cast(HMENU)m.wParam == handle);
571 					//assert(GetMenuItemID(mparent.handle, LOWORD(msg.lParam)) == mid);
572 					
573 					onPopup(EventArgs.empty);
574 					break;
575 				
576 				default:
577 			}
578 		}
579 		
580 		
581 		//EventHandler click;
582 		Event!(MenuItem, EventArgs) click; ///
583 		//EventHandler popup;
584 		Event!(MenuItem, EventArgs) popup; ///
585 		//EventHandler select;
586 		Event!(MenuItem, EventArgs) select; ///
587 		
588 		
589 		protected:
590 		
591 		///
592 		final @property int menuID() // getter
593 		{
594 			return mid;
595 		}
596 		
597 		
598 		package final @property int _menuID()
599 		{
600 			return mid;
601 		}
602 		
603 		
604 		///
605 		void onClick(EventArgs ea)
606 		{
607 			click(this, ea);
608 		}
609 		
610 		
611 		///
612 		void onPopup(EventArgs ea)
613 		{
614 			popup(this, ea);
615 		}
616 		
617 		
618 		///
619 		void onSelect(EventArgs ea)
620 		{
621 			select(this, ea);
622 		}
623 		
624 		
625 		private:
626 		
627 		int mid; // Menu ID.
628 		Dstring mtext;
629 		Menu mparent;
630 		UINT fType = 0; // MFT_*
631 		UINT fState = 0;
632 		int mindex = -1; //0;
633 		//int mergeord = 0;
634 		
635 		enum SEPARATOR_TEXT = "-";
636 		
637 		static assert(!MFS_UNCHECKED);
638 		static assert(!MFT_STRING);
639 		
640 		
641 		void _init()
642 		{
643 			if(Menu._compat092)
644 			{
645 				mindex = 0;
646 			}
647 			
648 			mid = Application.addMenuItem(this);
649 		}
650 		
651 		
652 		@property void _type(UINT newType) // setter
653 		{
654 			if(mparent)
655 			{
656 				MENUITEMINFOA mii;
657 				
658 				mii.cbSize = mii.sizeof;
659 				mii.fMask = MIIM_TYPE;
660 				mii.fType = newType;
661 				
662 				mparent._setInfo(mid, false, &mii);
663 			}
664 			
665 			fType = newType;
666 		}
667 		
668 		
669 		@property UINT _type() // getter
670 		{
671 			// if(mparent) fetch value ?
672 			return fType;
673 		}
674 		
675 		
676 		@property void _state(UINT newState) // setter
677 		{
678 			if(mparent)
679 			{
680 				MENUITEMINFOA mii;
681 				
682 				mii.cbSize = mii.sizeof;
683 				mii.fMask = MIIM_STATE;
684 				mii.fState = newState;
685 				
686 				mparent._setInfo(mid, false, &mii);
687 			}
688 			
689 			fState = newState;
690 		}
691 		
692 		
693 		@property UINT _state() // getter
694 		{
695 			// if(mparent) fetch value ? No: Windows seems to add disabled/gray when the text is empty.
696 			return fState;
697 		}
698 	}
699 
700 
701 	///
702 	abstract class Menu: DObject // docmain
703 	{
704 		// Retain DFL 0.9.2 compatibility.
705 		deprecated static void setDFL092()
706 		{
707 			version(SET_DFL_092)
708 			{
709 				pragma(msg, "DFL: DFL 0.9.2 compatibility set at compile time");
710 			}
711 			else
712 			{
713 				//_compat092 = true;
714 				Application.setCompat(DflCompat.MENU_092);
715 			}
716 		}
717 		
718 		version(SET_DFL_092)
719 			private enum _compat092 = true;
720 		else version(DFL_NO_COMPAT)
721 			private enum _compat092 = false;
722 		else
723 			private static @property bool _compat092() // getter
724 				{ return 0 != (Application._compat & DflCompat.MENU_092); }
725 		
726 		
727 		///
728 		static class MenuItemCollection
729 		{
730 			protected this(Menu owner)
731 			{
732 				_owner = owner;
733 			}
734 			
735 			
736 			package final void _additem(MenuItem mi)
737 			{
738 				// Fix indices after this point.
739 				int idx;
740 				idx = mi.index + 1; // Note, not orig idx.
741 				if(idx < items.length)
742 				{
743 					foreach(MenuItem onmi; items[idx .. items.length])
744 					{
745 						onmi.mindex++;
746 					}
747 				}
748 			}
749 			
750 			
751 			// Note: clear() doesn't call this. Update: does now.
752 			package final void _delitem(int idx)
753 			{
754 				// Fix indices after this point.
755 				if(idx < items.length)
756 				{
757 					foreach(MenuItem onmi; items[idx .. items.length])
758 					{
759 						onmi.mindex--;
760 					}
761 				}
762 			}
763 			
764 			
765 			/+
766 			void insert(int index, MenuItem mi)
767 			{
768 				mi.mindex = index;
769 				mi._setParent(_owner);
770 				_additem(mi);
771 			}
772 			+/
773 			
774 			
775 			void add(MenuItem mi)
776 			{
777 				if(!Menu._compat092)
778 				{
779 					mi.mindex = length;
780 				}
781 				
782 				/+
783 				mi._setParent(_owner);
784 				_additem(mi);
785 				+/
786 				insert(mi.mindex, mi);
787 			}
788 			
789 			void add(Dstring value)
790 			{
791 				return add(new MenuItem(value));
792 			}
793 			
794 			
795 			void addRange(MenuItem[] items)
796 			{
797 				if(!Menu._compat092)
798 					return _wraparray.addRange(items);
799 				
800 				foreach(MenuItem it; items)
801 				{
802 					insert(length, it);
803 				}
804 			}
805 			
806 			void addRange(Dstring[] items)
807 			{
808 				if(!Menu._compat092)
809 					return _wraparray.addRange(items);
810 				
811 				foreach(Dstring it; items)
812 				{
813 					insert(length, it);
814 				}
815 			}
816 			
817 			
818 			// TODO: finish.
819 			
820 			
821 			package:
822 			
823 			Menu _owner;
824 			MenuItem[] items; // Kept populated so the menu can be moved around.
825 			
826 			
827 			void _added(size_t idx, MenuItem val)
828 			{
829 				val.mindex = idx;
830 				val._setParent(_owner);
831 				_additem(val);
832 			}
833 			
834 			
835 			void _removing(size_t idx, MenuItem val)
836 			{
837 				if(size_t.max == idx) // Clear all.
838 				{
839 				}
840 				else
841 				{
842 					val._unsetParent();
843 					//RemoveMenu(_owner.handle, val._menuID, MF_BYCOMMAND);
844 					//_owner._remove(val._menuID, MF_BYCOMMAND);
845 					_owner._remove(idx, MF_BYPOSITION);
846 					_delitem(idx);
847 				}
848 			}
849 			
850 			
851 			public:
852 			
853 			mixin ListWrapArray!(MenuItem, items,
854 				_blankListCallback!(MenuItem), _added,
855 				_removing, _blankListCallback!(MenuItem),
856 				true, false, false,
857 				true) _wraparray; // CLEAR_EACH
858 		}
859 		
860 		
861 		// Extra.
862 		deprecated final void opCatAssign(MenuItem mi)
863 		{
864 			menuItems.insert(menuItems.length, mi);
865 		}
866 		
867 		
868 		private void _init()
869 		{
870 			items = new MenuItemCollection(this);
871 		}
872 		
873 		
874 		// Menu item that isn't popup (yet).
875 		protected this()
876 		{
877 			_init();
878 		}
879 		
880 		
881 		// Used internally.
882 		this(HMENU hmenu, bool owned = true) // package
883 		{
884 			this.hmenu = hmenu;
885 			this.owned = owned;
886 			
887 			_init();
888 		}
889 		
890 		
891 		// Used internally.
892 		this(HMENU hmenu, MenuItem[] items) // package
893 		{
894 			this.owned = true;
895 			this.hmenu = hmenu;
896 			
897 			_init();
898 			
899 			menuItems.addRange(items);
900 		}
901 		
902 		
903 		// Don't call directly.
904 		@disable this(MenuItem[] items);
905 		/+{
906 			/+
907 			this.owned = true;
908 			
909 			_init();
910 			
911 			menuItems.addRange(items);
912 			+/
913 			
914 			assert(0);
915 		}+/
916 		
917 		
918 		~this()
919 		{
920 			if(owned)
921 				DestroyMenu(hmenu);
922 		}
923 		
924 		
925 		///
926 		final @property void tag(Object o) // setter
927 		{
928 			ttag = o;
929 		}
930 		
931 		/// ditto
932 		final @property Object tag() // getter
933 		{
934 			return ttag;
935 		}
936 		
937 		
938 		///
939 		final @property HMENU handle() // getter
940 		{
941 			return hmenu;
942 		}
943 		
944 		
945 		///
946 		final @property MenuItemCollection menuItems() // getter
947 		{
948 			return items;
949 		}
950 		
951 		
952 		///
953 		@property bool isParent() // getter
954 		{
955 			return false;
956 		}
957 		
958 		
959 		///
960 		protected void onReflectedMessage(ref Message m)
961 		{
962 		}
963 		
964 		
965 		package final void _reflectMenu(ref Message m)
966 		{
967 			onReflectedMessage(m);
968 		}
969 		
970 		
971 		/+ package +/ protected void _setInfo(UINT uItem, BOOL fByPosition, LPMENUITEMINFOA lpmii, Dstring typeData = null) // package
972 		{
973 			if(typeData.length)
974 			{
975 				if(dfl.internal.utf.useUnicode)
976 				{
977 					static assert(MENUITEMINFOW.sizeof == MENUITEMINFOA.sizeof);
978 					lpmii.dwTypeData = cast(typeof(lpmii.dwTypeData))dfl.internal.utf.toUnicodez(typeData);
979 					_setMenuItemInfoW(hmenu, uItem, fByPosition, cast(MENUITEMINFOW*)lpmii);
980 				}
981 				else
982 				{
983 					lpmii.dwTypeData = cast(typeof(lpmii.dwTypeData))dfl.internal.utf.unsafeAnsiz(typeData);
984 					SetMenuItemInfoA(hmenu, uItem, fByPosition, lpmii);
985 				}
986 			}
987 			else
988 			{
989 				SetMenuItemInfoA(hmenu, uItem, fByPosition, lpmii);
990 			}
991 		}
992 		
993 		
994 		/+ package +/ protected void _insert(UINT uItem, BOOL fByPosition, LPMENUITEMINFOA lpmii, Dstring typeData = null) // package
995 		{
996 			if(typeData.length)
997 			{
998 				if(dfl.internal.utf.useUnicode)
999 				{
1000 					static assert(MENUITEMINFOW.sizeof == MENUITEMINFOA.sizeof);
1001 					lpmii.dwTypeData = cast(typeof(lpmii.dwTypeData))dfl.internal.utf.toUnicodez(typeData);
1002 					_insertMenuItemW(hmenu, uItem, fByPosition, cast(MENUITEMINFOW*)lpmii);
1003 				}
1004 				else
1005 				{
1006 					lpmii.dwTypeData = cast(typeof(lpmii.dwTypeData))dfl.internal.utf.unsafeAnsiz(typeData);
1007 					InsertMenuItemA(hmenu, uItem, fByPosition, lpmii);
1008 				}
1009 			}
1010 			else
1011 			{
1012 				InsertMenuItemA(hmenu, uItem, fByPosition, lpmii);
1013 			}
1014 		}
1015 		
1016 		
1017 		/+ package +/ protected void _remove(UINT uPosition, UINT uFlags) // package
1018 		{
1019 			RemoveMenu(hmenu, uPosition, uFlags);
1020 		}
1021 		
1022 		
1023 		package HMENU hmenu;
1024 		
1025 		private:
1026 		bool owned = true;
1027 		MenuItemCollection items;
1028 		Object ttag;
1029 	}
1030 
1031 
1032 	///
1033 	class MainMenu: Menu // docmain
1034 	{
1035 		// Used internally.
1036 		this(HMENU hmenu, bool owned = true)
1037 		{
1038 			super(hmenu, owned);
1039 		}
1040 		
1041 		
1042 		///
1043 		this()
1044 		{
1045 			super(CreateMenu());
1046 		}
1047 		
1048 		/// ditto
1049 		this(MenuItem[] items)
1050 		{
1051 			super(CreateMenu(), items);
1052 		}
1053 		
1054 		
1055 		/+ package +/ protected override void _setInfo(UINT uItem, BOOL fByPosition, LPMENUITEMINFOA lpmii, Dstring typeData = null) // package
1056 		{
1057 			Menu._setInfo(uItem, fByPosition, lpmii, typeData);
1058 			
1059 			if(hwnd)
1060 				DrawMenuBar(hwnd);
1061 		}
1062 		
1063 		
1064 		/+ package +/ protected override void _insert(UINT uItem, BOOL fByPosition, LPMENUITEMINFOA lpmii, Dstring typeData = null) // package
1065 		{
1066 			Menu._insert(uItem, fByPosition, lpmii, typeData);
1067 			
1068 			if(hwnd)
1069 				DrawMenuBar(hwnd);
1070 		}
1071 		
1072 		
1073 		/+ package +/ protected override void _remove(UINT uPosition, UINT uFlags) // package
1074 		{
1075 			Menu._remove(uPosition, uFlags);
1076 			
1077 			if(hwnd)
1078 				DrawMenuBar(hwnd);
1079 		}
1080 		
1081 		
1082 		private:
1083 		
1084 		HWND hwnd = HWND.init;
1085 		
1086 		
1087 		package final void _setHwnd(HWND hwnd)
1088 		{
1089 			this.hwnd = hwnd;
1090 		}
1091 	}
1092 }
1093