1 namespace gui::controls;
5 private define SELECTION_COLOR = guiApp.currentSkin.selectionColor; //Color { 10, 36, 106 };
6 private define MENU_COLOR = activeBorder;
11 MenuItem item, oldItem;
12 InsertedFlags inserted;
16 #define ITEM_DISABLED(i) ((i).disabled || ((i).subMenu && !(i).subMenu.items.count))
18 class InsertedFlags { bool deleteLink:1, deleteItem:1, cleanItem:1, placed:1; };
20 #define ITEM_TEXT(item) (item.subMenu ? item.subMenu.text : item.text)
21 #define ITEM_HOTKEY(item) (item.subMenu ? item.subMenu.hotKey : item.hotKey)
23 #define DIVIDER_HEIGHT (guiApp.textMode ? textCellH : 8)
25 #define INTERIM_MENU (isMenuBar || interim)
26 //#define INTERIM_MENU interim
28 static int strcmpTillTab(char * a, char * b)
31 else if(b && !a) return -1;
35 for(c = 0; a[c] && b[c] && a[c] != '\t' && b[c] != '\t'; c++)
36 if(a[c] > b[c]) return 1;
37 else if(b[c] > a[c]) return -1;
38 if(a[c] && a[c] != '\t') return 1;
39 else if(b[c] && b[c] != '\t') return -1;
54 menu.RemoveItem(this);
67 text = CopyString(value);
72 manualAccelText = (value && strchr(value, '\t'));
74 if(accelerator && !manualAccelText)
75 property::accelerator = accelerator;
78 property Key hotKey { set { hotKey = value; } };
79 property Key accelerator
85 if(!manualAccelText && text)
87 char accelString[50] = "\t";
92 if(value.ctrl) strcat(accelString, "Ctrl+");
93 if(value.alt) strcat(accelString, "Alt+");
94 if(value.shift) strcat(accelString, "Shift+");
97 strcat(accelString, "0");
98 else if(value.code >= k1 && value.code <= k9)
100 accelString[strlen(accelString)] = '1' + (char)(value.code - k1);
101 accelString[strlen(accelString)+1] = 0;
105 Key accel = value.code;
106 bool needClass = false;
108 char * result = accel.OnGetString(tempString, null, &needClass);
109 int len = strlen(accelString);
110 if(result) strcpy(accelString + len, result);
111 // accelString[len] = toupper(accelString[len]);
114 tabPos = strchr(text, '\t');
116 length = tabPos - text;
118 length = strlen(text);
120 newText = new char[length+strlen(accelString)+1];
121 memcpy(newText, text, length);
123 strcat(newText, accelString);
124 if(copyText) delete text;
131 property bool checked
136 if(menu && radio && value)
139 ItemPtr groupFirst = menu.items.first;
140 ItemPtr otherItemPtr;
141 for(otherItemPtr = menu.items.first; otherItemPtr; otherItemPtr = otherItemPtr.next)
143 MenuItem otherItem = otherItemPtr.item;
144 if(otherItem.isDivider)
145 groupFirst = otherItemPtr.next;
146 else if(!otherItem.placement)
148 if(otherItem == this)
152 for(otherItemPtr = groupFirst; otherItemPtr; otherItemPtr = otherItemPtr.next)
154 MenuItem otherItem = otherItemPtr.item;
155 if(otherItem.isDivider)
157 else if(!otherItem.placement && otherItem.radio && otherItem != this)
158 otherItem.checked = false;
161 // Should callback be called here? guess not ;)
163 get { return checked; }
165 property bool disabled { set { if(this) disabled = value; } };
166 property bool checkable { set { checkable = value; } };
167 property bool isRadio { set { radio = value; } };
169 property uint id { set { id = value; } get { return id; } };
170 property BitmapResource bitmap
175 bitmaps[1] = BitmapResource { fileName = value.fileName, monochrome = true };
176 bitmaps[2] = BitmapResource { fileName = value.fileName, grayed = true };
179 property bool copyText
185 if(text && !copyText)
186 text = CopyString(ITEM_TEXT(this));
196 property bool bold { set { bold = value; } get { return bold; } };
198 virtual bool Window::NotifySelect(MenuItem selection, Modifiers mods);
208 BitmapResource bitmaps[3];
209 bool checkable, radio;
215 bool manualAccelText;
221 // delete ITEM_TEXT(this);
227 public class MenuDivider : MenuItem
234 // property Menu parent { set {} };
237 public class MenuPlacement : MenuItem
246 property Menu parent { set {} };
247 property char * text { set {} };
248 property Key hotKey { set {} };
256 int OnCompare(Menu menu)
258 return (this != null) != (menu != null);
261 void AddItem(MenuItem item)
267 for(ptr = items.first; ptr; ptr = ptr.next)
269 MenuItem check = ptr.item;
272 if(!strcmpTillTab(ITEM_TEXT(check), ITEM_TEXT(item)))
283 ptr.inserted = InsertedFlags { placed = true };
284 ptr.oldItem = ptr.item;
299 void RemoveItem(MenuItem item)
301 if(item.menu == this)
304 for(ptr = items.first; ptr; ptr = ptr.next)
307 if(ptr.inserted.placed)
309 ptr.item = ptr.oldItem;
315 if(!ptr.inserted.placed)
325 void AddSubMenu(Menu subMenu)
329 MenuItem menuItem { };
330 ItemPtr ptr { item = menuItem };
337 menuItem.menu = this;
341 menuItem.subMenu = subMenu;
347 void AddDynamic(MenuItem addedItem, Window master, bool persistent)
351 ItemPtr ptr = null, oldItemPtr;
353 for(oldItemPtr = items.first; oldItemPtr; oldItemPtr = oldItemPtr.next)
355 if((oldItemPtr.item.subMenu || oldItemPtr.item.placement) && !strcmpTillTab(ITEM_TEXT(oldItemPtr.item), ITEM_TEXT(addedItem)))
357 MenuItem oldItem = oldItemPtr.item;
358 if(!oldItem.placement)
360 oldItem.subMenu.Merge(addedItem.subMenu, true, master);
362 // If sub menu already has a master...
365 oldItemPtr.inserted = InsertedFlags { cleanItem = true };
366 if(!oldItemPtr.oldItem)
367 oldItemPtr.oldItem = oldItem;
368 oldItemPtr.item = addedItem;
380 ptr.inserted = InsertedFlags { deleteLink = true, deleteItem = true };
383 ptr.inserted = InsertedFlags { cleanItem = true, deleteItem = true };
385 ptr.item = addedItem;
389 addedItem.menu = this;
393 MenuItem FindItem(bool (* Window::notifySelect)(MenuItem selection, Modifiers mods), uint id)
397 for(ptr = items.first; ptr; ptr = ptr.next)
399 MenuItem item = ptr.item;
402 MenuItem subItem = item.subMenu.FindItem(notifySelect, id);
403 if(subItem) return subItem;
405 else if(!item.isDivider && !item.placement)
407 if(item.id == id && item.NotifySelect == notifySelect)
417 while((ptr = items.first))
420 if(ptr.inserted.cleanItem || ptr.inserted.placed)
422 ptr.item = ptr.oldItem;
430 void Merge(Menu menuBeingMerged, bool menuBar, Window window)
432 bool separated = false;
433 ItemPtr beingMergedItemPtr;
435 for(beingMergedItemPtr = menuBeingMerged.items.first; beingMergedItemPtr; beingMergedItemPtr = beingMergedItemPtr.next)
437 MenuItem beingMergedItem = beingMergedItemPtr.item;
438 ItemPtr mergeIntoItemPtr = null;
440 if(!beingMergedItem) continue;
441 if(beingMergedItem.subMenu)
443 for(mergeIntoItemPtr = items.first; mergeIntoItemPtr; mergeIntoItemPtr = mergeIntoItemPtr.next)
445 if((mergeIntoItemPtr.item.subMenu || mergeIntoItemPtr.item.placement) && !strcmpTillTab(ITEM_TEXT(mergeIntoItemPtr.item), ITEM_TEXT(beingMergedItem)))
447 MenuItem mergeIntoItem = mergeIntoItemPtr.item;
448 if(!beingMergedItem.placement || beingMergedItemPtr.inserted)
450 if(!mergeIntoItem.placement && !mergeIntoItemPtr.inserted.cleanItem) // Added this last check for ActiveChild overriding ActiveClient's menu
452 mergeIntoItem.subMenu.Merge(beingMergedItem.subMenu, menuBar, window);
454 // If sub menu already has a master...
457 mergeIntoItemPtr.inserted = InsertedFlags { cleanItem = true };
458 if(!mergeIntoItemPtr.oldItem)
459 mergeIntoItemPtr.oldItem = mergeIntoItem;
460 mergeIntoItemPtr.item = beingMergedItem;
462 mergeIntoItemPtr.master = window;
469 else if(!beingMergedItem.isDivider)
471 for(mergeIntoItemPtr = items.first; mergeIntoItemPtr; mergeIntoItemPtr = mergeIntoItemPtr.next)
473 MenuItem mergeIntoItem = mergeIntoItemPtr.item;
474 if(/*!mergeIntoItem.subMenu && /*mergeIntoItem.placement && !mergeIntoItemPtr.inserted && */!strcmpTillTab(ITEM_TEXT(mergeIntoItem), ITEM_TEXT(beingMergedItem)))
476 //if(!beingMergedItem.placement || beingMergedItemPtr.inserted)
478 mergeIntoItemPtr.inserted = InsertedFlags { cleanItem = true };
479 if(!mergeIntoItemPtr.oldItem)
480 mergeIntoItemPtr.oldItem = mergeIntoItem;
481 mergeIntoItemPtr.item = beingMergedItem;
482 mergeIntoItemPtr.master = beingMergedItemPtr.master ? beingMergedItemPtr.master : window;
489 if(!mergeIntoItemPtr)
491 if(beingMergedItem.placement && !beingMergedItemPtr.inserted)
493 // Simply add the placement at the end
494 mergeIntoItemPtr = ItemPtr { };
495 mergeIntoItemPtr.inserted = InsertedFlags { deleteLink = true, cleanItem = true };
496 mergeIntoItemPtr.item = beingMergedItem;
497 mergeIntoItemPtr.master = beingMergedItemPtr.master ? beingMergedItemPtr.master : window;
498 items.Add(mergeIntoItemPtr);
503 ItemPtr previous = items.last;
506 // If it is a menu bar, add the item before the first divider
507 for(previous = items.first; previous; previous = previous.next)
508 if(previous.item.isDivider && !previous.inserted) // Added previous.inserted check
510 previous = previous.prev;
516 if(previous && !previous.item.isDivider && !separated)
520 item = MenuDivider { },
521 inserted = InsertedFlags { deleteLink = true, deleteItem = true }
523 items.Insert(previous, ptr);
530 if(!beingMergedItem.isDivider || (previous.item && !previous.item.isDivider))
532 mergeIntoItemPtr = ItemPtr { };
533 items.Insert(previous, mergeIntoItemPtr);
534 mergeIntoItemPtr.inserted = InsertedFlags { deleteLink = true, cleanItem = true };
535 mergeIntoItemPtr.item = beingMergedItem;
536 mergeIntoItemPtr.master = beingMergedItemPtr.master ? beingMergedItemPtr.master : window;
544 void Clean(Window window)
547 for(ptr = items.first; ptr; ptr = next)
549 MenuItem item = ptr.item;
552 if(ptr.inserted.cleanItem)
554 ptr.item = ptr.oldItem;
557 else if(item.subMenu)
558 item.subMenu.Clean(window);
560 if(ptr.inserted.deleteItem)
563 if(ptr.inserted.deleteLink || ptr.inserted.cleanItem)
565 if(ptr.inserted.deleteLink)
569 ptr.inserted.deleteLink = false;
570 ptr.inserted.cleanItem = false;
571 ptr.inserted.deleteItem = false;
577 Menu FindMenu(char * name)
581 for(ptr = items.first; ptr; ptr = ptr.next)
583 MenuItem item = ptr.item;
585 if(item.subMenu && item.subMenu.text && !strcmpTillTab(item.subMenu.text, name))
591 property Menu parent { set { if(value) value.AddSubMenu(this); } };
592 property char * text { set { text = value; /* CopyString(value);*/ } };
593 property Key hotKey { set { hotKey = value; } };
617 public class PopupMenu : Window
619 class_property(icon) = "<:ecere>controls/menu.png";
631 void (* FontExtent)(Display display, Font font, char * text, int len, int * width, int * height);
633 FontResource boldFont { faceName = font.faceName, font.size, bold = true, window = this };
634 BitmapResource subArrow { fileName = "<:ecere>elements/arrowRight.png", window = this };
635 BitmapResource whiteSubArrow { fileName = "<:ecere>elements/arrowRight.png", monochrome = true, window = this };
636 BitmapResource disabledSubArrow { fileName = "<:ecere>elements/arrowRight.png", grayed = true, window = this };
643 if(menu) delete menu;
647 bool MenuDestroyMasters(bool unselect)
650 PopupMenu window = this, master;
653 for(; (master = (PopupMenu)window.master); window = master)
655 if(!eClass_IsDerived(master._class, _class) || master.isMenuBar)
659 if(eClass_IsDerived(master._class, _class) && master.isMenuBar)
661 master.pressed = false;
664 master.keyboardFocus = false;
665 master.selected = null;
669 result = window.Destroy(0);
670 // This looks like a hack...
675 bool MenuGoToPrevItem()
677 ItemPtr selected, current = this.selected;
678 for(selected = (current && current.prev) ? current.prev : menu.items.last;
680 (selected.item.isDivider || selected.item.placement || ITEM_DISABLED(selected.item)) &&
682 selected = selected.prev ? selected.prev : menu.items.last)
684 if(!current) current = selected; // Endless loop with no previously selected popups
686 this.selected = selected;
687 return selected && selected != current;
690 bool MenuGoToNextItem()
692 ItemPtr selected, current = this.selected;
693 for(selected = (current && current.next) ? current.next : menu.items.first;
695 (selected.item.isDivider || selected.item.placement || ITEM_DISABLED(selected.item)) &&
697 selected = selected.next ? selected.next : menu.items.first)
699 if(!current) current = selected; // Endless loop with no previously selected popups
701 this.selected = selected;
702 return selected && selected != current;
705 void MenuPopupChild(int x, int y, Menu childMenu)
710 if(childMenu.itemCount)
712 PopupMenu child { master = this, menu = childMenu };
716 Window parent = this.parent;
717 Window desktop = guiApp.desktop;
719 x += parent.absPosition.x + parent.clientStart.x - desktop.position.x;
720 y += parent.absPosition.y + parent.clientStart.y - desktop.position.y;
722 x += parent.absPosition.x + parent.clientStart.x;
723 y += parent.absPosition.y + parent.clientStart.y;
725 child.parent = desktop;
729 child.stayOnTop = true;
730 child.parent = parent;
731 child.interim = false;
733 child.position = Point { x, y };
735 // child.displayDriver = "GDI";
741 bool MenuPopupSelected()
745 int selectedX = guiApp.textMode ? 0 : 2;
747 if(selected && selected.item) // Why was this null from OnKeyHit?
749 ItemPtr selected = this.selected, ptr;
750 bool helpBreak = false;
751 Window parent = this.parent;
752 Window activeClient = parent.activeClient;
753 bool systemButtons = activeClient && activeClient.state == maximized;
755 keyboardFocus = true;
760 firstSlave.Destroy(0);
762 for(ptr = menu.items.first; ptr; ptr = ptr.next)
764 MenuItem item = ptr.item;
766 if(item.placement) continue; //&& !ptr.inserted) continue;
770 Menu childMenu = item.subMenu;
774 if(selected.item.subMenu)
775 MenuPopupChild(selectedX, 0, childMenu);
777 keyboardFocus = true;
782 return false; // true
789 int breakX = clientSize.w + 2 - (systemButtons ? 48 : 0);
790 for(nextPtr = ptr.next; nextPtr; nextPtr = nextPtr.next)
792 MenuItem nextItem = nextPtr.item;
793 if(!nextItem.isDivider && ITEM_TEXT(nextItem))
796 FontExtent(display, fontObject, ITEM_TEXT(nextItem), strlen(ITEM_TEXT(nextItem)), &len, null);
800 if(selectedX < breakX) selectedX = breakX;
804 else if(ITEM_TEXT(item))
806 FontExtent(display, fontObject, ITEM_TEXT(item), strlen(ITEM_TEXT(item)), &len, null);
807 selectedX += len + 16;
815 if(selected && selected.item.subMenu)
817 Menu childMenu = selected.item.subMenu;
822 for(ptr = menu.items.first; ptr; ptr = ptr.next)
824 MenuItem item = ptr.item;
826 if(item.placement) continue; //&& !ptr.inserted) continue;
833 y += (item.isDivider) ? DIVIDER_HEIGHT : rh;
837 PopupMenu slave = (PopupMenu)firstSlave;
838 if(!slave || slave.menu != childMenu)
840 if(firstSlave) firstSlave.Destroy(0);
841 MenuPopupChild(position.x + size.w, position.y + selectedY, childMenu);
851 bool MenuItemSelection(Menu parentMenu, ItemPtr selectionPtr, Key key)
853 MenuItem selection = selectionPtr.item;
854 if(!ITEM_DISABLED(selection))
856 Window master = this;
859 master = master.master;
861 if(selectionPtr.master)
862 master = selectionPtr.master;
863 while(eClass_IsDerived(master._class, _class) && master.master)
864 master = master.master;
866 if(selection.checkable)
867 selection.checked = !selection.checked;
868 else if(selection.radio)
870 if(selection.checked) return false;
871 selection.checked = !selection.checked;
875 MenuDestroyMasters(true);
877 /*return */selection.NotifySelect(master, selection, key.modifiers);
883 bool CheckAccelerators(Menu menu, Key key)
887 for(ptr = menu.items.first; ptr; ptr = ptr.next)
889 MenuItem item = ptr.item;
892 if(!CheckAccelerators(item.subMenu, key))
895 else if(!item.isDivider)
897 if(item.accelerator == key)
899 if(MenuItemSelection(menu, ptr, key))
907 ItemPtr FindSelected(int mx, int my, int * selectedX, int * selectedY)
909 Menu menu = this.menu;
910 // Mouse moved inside menu
911 ItemPtr selected = null;
916 if(isMenuBar && menu)
921 bool helpBreak = false;
922 Window parent = this.parent;
923 Window activeClient = parent.activeClient;
924 bool systemButtons = activeClient && activeClient.state == maximized;
926 for(ptr = menu.items.first; ptr; ptr = ptr.next)
928 MenuItem item = ptr.item;
929 if(item.placement) continue; //&& !ptr.inserted) continue;
936 int breakX = clientSize.w - (systemButtons ? 48 : 0);
937 for(nextPtr = ptr.next; nextPtr; nextPtr = nextPtr.next)
939 MenuItem nextItem = nextPtr.item;
940 if(!nextItem.isDivider && ITEM_TEXT(nextItem))
943 FontExtent(display, fontObject, ITEM_TEXT(nextItem), strlen(ITEM_TEXT(nextItem)), &len, null);
947 if(x < breakX) x = breakX;
953 FontExtent(display, fontObject, ITEM_TEXT(item), strlen(ITEM_TEXT(item)), &len, null);
954 if((mx >= x - 16 && mx < x + len + 16))
956 if(!ITEM_DISABLED(item))
962 *selectedY = -position.y;
973 for(ptr = menu.items.first; ptr; ptr = ptr.next)
975 MenuItem item = ptr.item;
976 if(item.placement) continue; //&& !ptr.inserted) continue;
977 if(mx >= 2 && (my >= y && my < y + rh) && !item.isDivider)
979 if(!ITEM_DISABLED(item))
982 *selectedX = position.x + size.w;
988 y += (item.isDivider) ? DIVIDER_HEIGHT : rh;
995 void OnRedraw(Surface surface)
1000 bool helpBreak = false;
1001 Window parent = this.parent;
1002 Window activeClient = parent.activeClient;
1003 bool systemButtons = activeClient && activeClient.state == maximized;
1004 int bitmapOffset = 0;
1006 surface.TextFont(fontObject);
1007 surface.TextExtent(" ", 1, null, &height);
1008 surface.SetBackground(SELECTION_COLOR);
1017 else if(guiApp.textMode)
1023 for(ptr = menu.items.first; ptr; ptr = ptr.next)
1025 MenuItem item = ptr.item;
1026 if(item.placement) continue; //&& !ptr.inserted) continue;
1027 if(!isMenuBar && selected == ptr)
1029 surface.SetForeground(white);
1032 surface.TextOpacity(true);
1033 surface.Area(0,y,clientSize.w-1,y+rh-1);
1036 surface.Area(2,y,clientSize.w-3,y+rh);
1040 surface.SetForeground(foreground);
1042 surface.TextOpacity(false);
1052 int breakX = clientSize.w - (systemButtons ? 48 : 0);
1053 for(nextPtr = ptr.next; nextPtr; nextPtr = nextPtr.next)
1055 MenuItem nextItem = nextPtr.item;
1057 if(!nextItem.isDivider && ITEM_TEXT(nextItem))
1059 surface.TextExtent(ITEM_TEXT(nextItem), strlen(ITEM_TEXT(nextItem)), &len, null);
1063 if(x < breakX) x = breakX;
1071 surface.SetForeground(Color { 85, 85, 85 });
1072 surface.DrawingChar(196);
1073 surface.HLine(x + 2, x + rw - 5, y + (rh) / 2);
1074 surface.DrawingChar(' ');
1078 surface.SetForeground(Color { 85, 85, 85 });
1079 surface.HLine(x + 2, x + rw - 5, y + (DIVIDER_HEIGHT) / 2);
1080 surface.SetForeground(white);
1081 surface.HLine(x + 2, x + rw - 5, y + (DIVIDER_HEIGHT) / 2 + 1);
1087 if(selected == ptr && guiApp.textMode)
1089 surface.SetBackground(SELECTION_COLOR);
1090 surface.SetForeground(white /*background*/);
1091 surface.TextOpacity(true);
1095 Interface::WriteKeyedTextDisabled(surface, x + bitmapOffset,
1096 y + (rh - height)/2, ITEM_TEXT(item), ITEM_HOTKEY(item), ITEM_DISABLED(item));
1098 surface.WriteText(x, y, "\373", 1);
1102 int textY = y + (rh - height)/2;
1103 BitmapResource bitmap = item.disabled ? item.bitmaps[2] : ((selected == ptr) ? item.bitmaps[1] : item.bitmaps[0]);
1104 if(!isMenuBar && bitmap)
1106 Bitmap icon = bitmap.bitmap;
1108 surface.Blit(icon, x + 3, y + (rh - icon.height)/2, 0,0, icon.width, icon.height);
1112 surface.TextFont(boldFont.font);
1114 surface.TextFont(fontObject);
1116 if(ITEM_DISABLED(item) && selected == ptr)
1118 surface.SetForeground(activeBorder);
1119 Interface::WriteKeyedText(surface, x + bitmapOffset + 5,
1120 textY, ITEM_TEXT(item), ITEM_HOTKEY(item));
1123 Interface::WriteKeyedTextDisabled(surface, x + bitmapOffset + 5,
1124 textY, ITEM_TEXT(item), ITEM_HOTKEY(item), ITEM_DISABLED(item));
1125 surface.SetForeground(foreground);
1128 surface.DrawLine(x+5, y+9, x+8,y+12);
1129 surface.DrawLine(x+5, y+10, x+8,y+13);
1130 surface.DrawLine(x+8, y+12, x+12,y+4);
1131 surface.DrawLine(x+8, y+13, x+12,y+5);
1138 // Draw little arrow
1141 surface.SetForeground(foreground);
1145 surface.SetForeground((selected == ptr) ? (background) : (foreground));
1146 surface.WriteText(clientSize.w-8, y+(rh - 8)/2, "\020", 1);
1150 Bitmap arrow = (selected == ptr) ? whiteSubArrow.bitmap : subArrow.bitmap;
1151 if(ITEM_DISABLED(ptr.item)) arrow = disabledSubArrow.bitmap;
1154 surface.Blit(arrow, clientSize.w-14, y+(rh - 8)/2, 0,0, arrow.width, arrow.height);
1156 surface.VLine(y+(rh - 8)/2, y+(rh - 8)/2+7, clientSize.w-10);
1157 surface.SetForeground(Color { 85, 85, 85 });
1158 surface.DrawLine(clientSize.w-10, y+(rh - 8)/2, clientSize.w-4, y+rh / 2);
1159 surface.SetForeground(activeBorder);
1160 surface.DrawLine(clientSize.w-10, y+(rh - 8)/2+7, clientSize.w-4, y+rh / 2);
1167 y += (item.isDivider) ? DIVIDER_HEIGHT : rh;
1169 else if(ITEM_TEXT(item) && !item.isDivider)
1172 surface.TextExtent(ITEM_TEXT(item), strlen(ITEM_TEXT(item)), &len, null);
1173 if(selected == ptr && !guiApp.textMode &&
1174 (item.subMenu || selected))
1176 surface.Bevel(pressed, x, y, len+10, height + 6);
1185 bool OnKeyDown(Key key, unichar ch)
1190 // Non-Interim menus: the slaves don't have focus... pass it down from menu bar
1193 if(!active && firstSlave)
1195 if(!firstSlave.OnKeyDown(key, ch))
1207 MenuDestroyMasters(true);
1220 for(ptr = menu.items.first; ptr; ptr = ptr.next)
1222 MenuItem item = ptr.item;
1223 if((item.placement && !ptr.inserted) || ITEM_DISABLED(item)) continue;
1224 if(((keyboardFocus || !isMenuBar) && ITEM_HOTKEY(item) == key) || (ITEM_HOTKEY(item) | Key { alt = true }) == key)
1226 if(!item.isDivider && !item.subMenu)
1228 if(MenuItemSelection(menu, ptr, key))
1231 else if(item.subMenu)
1234 MenuPopupSelected();
1240 if(ch >= 32 && ch != 128 && !isMenuBar)
1245 // Interim menus: the slaves are the ones with the focus
1246 if(result && INTERIM_MENU)
1248 Window master = this.master;
1249 if(eClass_IsDerived(master._class, _class))
1251 if(!master.OnKeyDown(key, ch))
1259 bool OnKeyUp(Key key, unichar ch)
1265 if(isMenuBar && altDown)
1270 keyboardFocus = true;
1276 if(firstSlave) firstSlave.Destroy(0);
1278 keyboardFocus = false;
1291 bool OnKeyHit(Key key, unichar ch)
1295 if(key == leftAlt || key == rightAlt) return true;
1297 if(key && isMenuBar)
1302 result = CheckAccelerators(menu, key);
1303 if(result && !key.alt && key != escape)
1308 if(result && visible)
1310 // Non-Interim menus: the slaves don't have focus... pass it down from menu bar
1313 if(!active && firstSlave)
1315 if(!firstSlave.OnKeyHit(key, ch))
1327 selected = menu.items.last;
1328 if(MenuGoToNextItem())
1336 selected = menu.items.first;
1337 if(MenuGoToPrevItem())
1345 if(MenuGoToPrevItem())
1346 MenuPopupSelected();
1351 PopupMenu master = (PopupMenu)this.master;
1352 if(master && !master.isMenuBar)
1364 if(MenuGoToNextItem() && MenuPopupSelected())
1369 if(selected && !ITEM_DISABLED(selected.item))
1371 if(!MenuPopupSelected())
1375 selected = menu.items.first;
1382 else if(!((PopupMenu)master).isMenuBar)
1384 if(MenuGoToNextItem())
1394 return MenuPopupSelected();
1398 if(firstSlave) firstSlave.Destroy(0);
1403 case down: case Key::altDown:
1406 return MenuPopupSelected();
1410 if(MenuGoToNextItem())
1415 case up: case altUp:
1418 if(MenuGoToPrevItem())
1423 case enter: case altEnter:
1424 case keyPadEnter: case altKeyPadEnter:
1427 if(!selected.item.isDivider && !selected.item.subMenu)
1429 if(MenuItemSelection(menu, selected, key))
1437 result = selected ? false : true;
1440 keyboardFocus = false;
1447 PopupMenu master = (PopupMenu)this.master;
1448 if(eClass_IsDerived(master._class, _class) && master.isMenuBar)
1450 ItemPtr selected = master.selected;
1452 master.pressed = true;
1453 master.selected = selected;
1454 master.keyboardFocus = true;
1467 if(ch >= 32 && !isMenuBar)
1470 for(ptr = menu.items.first; ptr; ptr = ptr.next)
1472 MenuItem item = ptr.item;
1473 if((item.placement && !ptr.inserted) || ITEM_DISABLED(item)) continue;
1474 if(ITEM_HOTKEY(item) == key || (ITEM_HOTKEY(item) | Key { alt = true }) == key)
1476 if(!item.isDivider && !item.subMenu)
1478 if(MenuItemSelection(menu, ptr, key))
1481 else if(item.subMenu)
1484 MenuPopupSelected();
1491 if(result && isMenuBar && pressed)
1494 // Interim menus: the slaves are the ones with the focus
1495 if(result && INTERIM_MENU)
1497 Window master = this.master;
1498 if(eClass_IsDerived(master._class, _class))
1500 if(!master.OnKeyHit(key, ch))
1508 bool OnLoadGraphics()
1510 Font font = fontObject;
1511 int maxW = 0, maxH = 0;
1516 background = menu ? menu.color : MENU_COLOR;
1517 FontExtent = Display::FontExtent;
1522 for(ptr = menu.items.first; ptr; ptr = ptr.next)
1524 MenuItem item = ptr.item;
1525 if(item.placement) continue; //&& !ptr.inserted) continue;
1532 FontExtent(display, font,ITEM_TEXT(item),strlen(ITEM_TEXT(item)),&width, &height);
1533 if(strstr(ITEM_TEXT(item), "\t"))
1536 if(item.subMenu) width += 20;
1537 if(!guiApp.textMode)
1542 if(!guiApp.textMode)
1543 height = DIVIDER_HEIGHT;
1548 if(width > maxW) maxW = width;
1549 if(height > maxH) maxH = height;
1551 totalHeight += height;
1553 if(item.bitmaps[0]) AddResource(item.bitmaps[0]);
1554 if(item.bitmaps[1]) AddResource(item.bitmaps[1]);
1555 if(item.bitmaps[2]) AddResource(item.bitmaps[2]);
1560 FontExtent(display, font,menu.text,strlen(menu.text),&width, &height);
1561 if(width > maxW) maxW = width;
1562 if(height > maxH) maxH = height;
1568 if(rw < maxW) rw = maxW;
1569 if(rh < maxH) rh = maxH;
1574 bool OnResizing(int * w, int * h)
1576 Window master = this.master;
1577 Window masterMenuBar = master.menuBar;
1578 if(this != masterMenuBar)
1582 *h = Max(*h, rh + 2);
1584 *h = Max(*h, totalHeight + 2);
1586 if(this != masterMenuBar)
1596 bool OnMoving(int *x, int *y, int w, int h)
1600 Window parent = this.parent;
1603 if(*y + h > parent.clientSize.h)
1605 PopupMenu master = (PopupMenu)this.master;
1607 if(eClass_IsDerived(master._class, _class))
1609 if(master.isMenuBar)
1610 *y -= master.size.h;
1612 *y += h - clientSize.h + rh;
1616 *x = Min(*x, parent.clientSize.w - w);
1620 *x = Min(*x, parent.size.w - w);
1621 *y = Min(*y, parent.size.h - h);
1625 *x = Min(*x, parent.clientSize.w - w);
1626 *y = Min(*y, parent.clientSize.h - h);
1628 if(parent == guiApp.desktop)
1631 *x = Max(*x, guiApp.virtualScreenPos.x);
1632 *y = Max(*y, guiApp.virtualScreenPos.y);
1644 bool OnMouseMove(int mx, int my, Modifiers mods)
1646 int selectedX, selectedY;
1648 ItemPtr selected = FindSelected(mx, my, &selectedX, &selectedY);
1650 if((!mods.isSideEffect /*|| !selected*/) && (/*selected && */
1651 selected != this.selected && (!selected || !ITEM_DISABLED(selected.item)) && (selected || !keyboardFocus)))
1653 if(!isMenuBar || pressed)
1655 bool pressed = this.pressed;
1657 if(firstSlave) firstSlave.Destroy(0);
1659 this.selected = selected;
1663 Menu childMenu = selected.item.subMenu;
1665 this.pressed = pressed;
1667 if(this.selected.item.subMenu)
1668 MenuPopupChild(selectedX, position.y + selectedY, childMenu);
1671 keyboardFocus = true;
1675 this.selected = selected;
1681 bool OnLeftButtonDown(int x, int y, Modifiers mods)
1685 if(GetTime() - unpressedTime < 0.01)
1689 int selectedX, selectedY;
1690 if(!mods.isActivate && !pressed)
1693 keyboardFocus = true;
1695 OnMouseMove(x,y, mods);
1699 if(firstSlave) firstSlave.Destroy(0);
1700 selected = FindSelected(x, y, &selectedX, &selectedY);
1704 //keyboardFocus = false;
1713 keyboardFocus = false;
1714 if(firstSlave) firstSlave.Destroy(0);
1720 keyboardFocus = true;
1722 OnMouseMove(x, y, mods);
1726 else //if(!INTERIM_MENU) // Why was this commented out?
1728 if(!(x >= 0 && y >= 0 && x < clientSize.w && y < clientSize.h))
1730 MenuDestroyMasters(false);
1737 bool OnRightButtonDown(int x, int y, Modifiers mods)
1739 if(x >= 0 && y >= 0 && x < clientSize.w && y < clientSize.h)
1743 Window master = this.master;
1744 Window activeClient = master.activeClient;
1745 if(activeClient.state == maximized)
1746 activeClient.ShowSysMenu(absPosition.x + x, absPosition.y + y);
1752 bool OnLeftButtonUp(int mx, int my, Modifiers mods)
1754 if(mx >= 2 /*0*/ && my >= 0 && mx < clientSize.w && my < clientSize.h)
1756 Menu menu = this.menu;
1760 if(!isMenuBar && menu)
1763 for(ptr = menu.items.first; ptr; ptr = ptr.next)
1765 MenuItem item = ptr.item;
1766 if(item.placement) continue; //&& !ptr.inserted) continue;
1767 if(my >= y && my < y + rh && !item.isDivider)
1775 y += (item.isDivider) ? DIVIDER_HEIGHT : rh;
1779 if(!isMenuBar || pressed)
1783 if(!selected.item.isDivider && !selected.item.subMenu)
1788 keyboardFocus = false;
1791 if(MenuItemSelection(menu, selected, (Key)mods))
1798 keyboardFocus = false;
1799 if(firstSlave) firstSlave.Destroy(0);
1806 OnRightButtonUp = OnLeftButtonUp;
1808 bool OnLeftDoubleClick(int x, int y, Modifiers mods)
1812 int selectedX, selectedY;
1813 ItemPtr selected = FindSelected(x, y, &selectedX, &selectedY);
1816 Window master = this.master;
1817 Window activeClient = master.activeClient;
1818 if(activeClient && activeClient.state == maximized)
1819 activeClient.SetState(normal, false, mods);
1825 bool OnMouseLeave(Modifiers mods)
1827 if(!pressed && !firstSlave)
1836 bool OnActivate(bool active, Window swap, bool * goOnWithActivation, bool direct)
1844 PopupMenu master = (PopupMenu)this.master;
1845 if(eClass_IsDerived(master._class, _class) && master.isMenuBar) // && swap != master && swap && swap.master != master)
1847 unpressedTime = GetTime();
1848 master.pressed = false;
1849 master.selected = null;
1850 master.keyboardFocus = false;
1851 // master.Update(null);
1853 // TOFIX: Redraw bug here without this...
1854 master.master.Update(null);
1861 bool destroy = true;
1865 for(master = swap.master; master; master = master.master)
1874 for(master = this.master; master; master = master.master)
1877 if(eClass_IsDerived(master._class, _class))
1884 if(MenuDestroyMasters(false))
1896 unpressedTime = GetTime();
1899 keyboardFocus = false;
1900 if(firstSlave) firstSlave.Destroy(0);
1920 if(firstSlave) firstSlave.Destroy(0);
1922 Move(position.x, position.y, size.w, size.h);
1926 if(interim || isMenuBar)
1934 property bool isMenuBar { set { isMenuBar = value; } };
1935 property bool focus { get { return keyboardFocus; } };