1 namespace gui::controls;
5 private define SELECTION_COLOR = guiApp.currentSkin.selectionColor; //Color { 10, 36, 106 };
6 private define SELECTION_TEXT = guiApp.currentSkin.selectionText; //Color { 10, 36, 106 };
7 private define menuBarColor = Color { 211, 218, 237 };
8 private define popupMenuColor = Color { 229,234,245 };
10 class ItemPtr : struct
13 MenuItem item, oldItem;
14 InsertedFlags inserted;
18 #define ITEM_DISABLED(i) ((i).disabled || ((i).subMenu && !(i).subMenu.items.count))
20 class InsertedFlags { bool deleteLink:1, deleteItem:1, cleanItem:1, placed:1; };
22 #define ITEM_TEXT(item) (item.subMenu ? item.subMenu.text : item.text)
23 #define ITEM_HOTKEY(item) (item.subMenu ? item.subMenu.hotKey : item.hotKey)
25 #define DIVIDER_HEIGHT (guiApp.textMode ? textCellH : 8)
27 #define INTERIM_MENU (isMenuBar || interim)
28 //#define INTERIM_MENU interim
30 static int strcmpTillTab(char * a, char * b)
33 else if(b && !a) return -1;
37 for(c = 0; a[c] && b[c] && a[c] != '\t' && b[c] != '\t'; c++)
38 if(a[c] > b[c]) return 1;
39 else if(b[c] > a[c]) return -1;
40 if(a[c] && a[c] != '\t') return 1;
41 else if(b[c] && b[c] != '\t') return -1;
56 menu.RemoveItem(this);
69 text = CopyString(value);
74 manualAccelText = (value && strchr(value, '\t'));
76 if(accelerator && !manualAccelText)
77 property::accelerator = accelerator;
80 property Key hotKey { set { hotKey = value; } };
81 property Key accelerator
87 if(!manualAccelText && text)
89 char accelString[50] = "\t";
94 if(value.ctrl) strcat(accelString, "Ctrl+");
95 if(value.alt) strcat(accelString, "Alt+");
96 if(value.shift) strcat(accelString, "Shift+");
99 strcat(accelString, "0");
100 else if(value.code >= k1 && value.code <= k9)
102 accelString[strlen(accelString)] = '1' + (char)(value.code - k1);
103 accelString[strlen(accelString)+1] = 0;
107 Key accel = value.code;
108 bool needClass = false;
110 char * result = accel.OnGetString(tempString, null, &needClass);
111 int len = strlen(accelString);
112 if(result) strcpy(accelString + len, result);
113 // accelString[len] = toupper(accelString[len]);
116 tabPos = strchr(text, '\t');
118 length = tabPos - text;
120 length = strlen(text);
122 newText = new char[length+strlen(accelString)+1];
123 memcpy(newText, text, length);
125 strcat(newText, accelString);
126 if(copyText) delete text;
133 property bool checked
138 if(menu && radio && value)
141 ItemPtr groupFirst = menu.items.first;
142 ItemPtr otherItemPtr;
143 for(otherItemPtr = menu.items.first; otherItemPtr; otherItemPtr = otherItemPtr.next)
145 MenuItem otherItem = otherItemPtr.item;
146 if(otherItem.isDivider)
147 groupFirst = otherItemPtr.next;
148 else if(!otherItem.placement)
150 if(otherItem == this)
154 for(otherItemPtr = groupFirst; otherItemPtr; otherItemPtr = otherItemPtr.next)
156 MenuItem otherItem = otherItemPtr.item;
157 if(otherItem.isDivider)
159 else if(!otherItem.placement && otherItem.radio && otherItem != this)
160 otherItem.checked = false;
163 // Should callback be called here? guess not ;)
165 get { return checked; }
167 property bool disabled { set { if(this) disabled = value; } };
168 property bool checkable { set { checkable = value; } };
169 property bool isRadio { set { radio = value; } };
171 property uint id { set { id = value; } get { return id; } };
172 property BitmapResource bitmap
177 bitmaps[1] = BitmapResource { fileName = value.fileName, monochrome = true };
178 bitmaps[2] = BitmapResource { fileName = value.fileName, grayed = true };
181 property bool copyText
187 if(text && !copyText)
188 text = CopyString(ITEM_TEXT(this));
198 property bool bold { set { bold = value; } get { return bold; } };
200 virtual bool Window::NotifySelect(MenuItem selection, Modifiers mods);
210 BitmapResource bitmaps[3];
211 bool checkable, radio;
217 bool manualAccelText;
223 // delete ITEM_TEXT(this);
229 public class MenuDivider : MenuItem
236 // property Menu parent { set {} };
239 public class MenuPlacement : MenuItem
248 property Menu parent { set {} };
249 property char * text { set {} };
250 property Key hotKey { set {} };
258 int OnCompare(Menu menu)
260 return (this != null) != (menu != null);
263 void AddItem(MenuItem item)
269 for(ptr = items.first; ptr; ptr = ptr.next)
271 MenuItem check = ptr.item;
274 if(!strcmpTillTab(ITEM_TEXT(check), ITEM_TEXT(item)))
285 ptr.inserted = InsertedFlags { placed = true };
286 ptr.oldItem = ptr.item;
301 void RemoveItem(MenuItem item)
303 if(item.menu == this)
306 for(ptr = items.first; ptr; ptr = ptr.next)
309 if(ptr.inserted.placed)
311 ptr.item = ptr.oldItem;
317 if(!ptr.inserted.placed)
327 void AddSubMenu(Menu subMenu)
331 MenuItem menuItem { };
332 ItemPtr ptr { item = menuItem };
339 menuItem.menu = this;
343 menuItem.subMenu = subMenu;
349 void AddDynamic(MenuItem addedItem, Window master, bool persistent)
353 ItemPtr ptr = null, oldItemPtr;
355 for(oldItemPtr = items.first; oldItemPtr; oldItemPtr = oldItemPtr.next)
357 if((oldItemPtr.item.subMenu || oldItemPtr.item.placement) && !strcmpTillTab(ITEM_TEXT(oldItemPtr.item), ITEM_TEXT(addedItem)))
359 MenuItem oldItem = oldItemPtr.item;
360 if(!oldItem.placement)
362 oldItem.subMenu.Merge(addedItem.subMenu, true, master);
364 // If sub menu already has a master...
367 oldItemPtr.inserted = InsertedFlags { cleanItem = true };
368 if(!oldItemPtr.oldItem)
369 oldItemPtr.oldItem = oldItem;
370 oldItemPtr.item = addedItem;
382 ptr.inserted = InsertedFlags { deleteLink = true, deleteItem = true };
385 ptr.inserted = InsertedFlags { cleanItem = true, deleteItem = true };
387 ptr.item = addedItem;
391 addedItem.menu = this;
395 MenuItem FindItem(bool (* Window::notifySelect)(MenuItem selection, Modifiers mods), uint id)
399 for(ptr = items.first; ptr; ptr = ptr.next)
401 MenuItem item = ptr.item;
404 MenuItem subItem = item.subMenu.FindItem(notifySelect, id);
405 if(subItem) return subItem;
407 else if(!item.isDivider && !item.placement)
409 if(item.id == id && item.NotifySelect == notifySelect)
419 while((ptr = items.first))
422 if(ptr.inserted.cleanItem || ptr.inserted.placed)
424 ptr.item = ptr.oldItem;
432 void Merge(Menu menuBeingMerged, bool menuBar, Window window)
434 bool separated = false;
435 ItemPtr beingMergedItemPtr;
437 for(beingMergedItemPtr = menuBeingMerged.items.first; beingMergedItemPtr; beingMergedItemPtr = beingMergedItemPtr.next)
439 MenuItem beingMergedItem = beingMergedItemPtr.item;
440 ItemPtr mergeIntoItemPtr = null;
442 if(!beingMergedItem) continue;
443 if(beingMergedItem.subMenu)
445 for(mergeIntoItemPtr = items.first; mergeIntoItemPtr; mergeIntoItemPtr = mergeIntoItemPtr.next)
447 if((mergeIntoItemPtr.item.subMenu || mergeIntoItemPtr.item.placement) && !strcmpTillTab(ITEM_TEXT(mergeIntoItemPtr.item), ITEM_TEXT(beingMergedItem)))
449 MenuItem mergeIntoItem = mergeIntoItemPtr.item;
450 if(!beingMergedItem.placement || beingMergedItemPtr.inserted)
452 if(!mergeIntoItem.placement && !mergeIntoItemPtr.inserted.cleanItem) // Added this last check for ActiveChild overriding ActiveClient's menu
454 mergeIntoItem.subMenu.Merge(beingMergedItem.subMenu, menuBar, window);
456 // If sub menu already has a master...
459 mergeIntoItemPtr.inserted = InsertedFlags { cleanItem = true };
460 if(!mergeIntoItemPtr.oldItem)
461 mergeIntoItemPtr.oldItem = mergeIntoItem;
462 mergeIntoItemPtr.item = beingMergedItem;
464 mergeIntoItemPtr.master = window;
471 else if(!beingMergedItem.isDivider)
473 for(mergeIntoItemPtr = items.first; mergeIntoItemPtr; mergeIntoItemPtr = mergeIntoItemPtr.next)
475 MenuItem mergeIntoItem = mergeIntoItemPtr.item;
476 if(/*!mergeIntoItem.subMenu && /*mergeIntoItem.placement && !mergeIntoItemPtr.inserted && */!strcmpTillTab(ITEM_TEXT(mergeIntoItem), ITEM_TEXT(beingMergedItem)))
478 //if(!beingMergedItem.placement || beingMergedItemPtr.inserted)
480 mergeIntoItemPtr.inserted = InsertedFlags { cleanItem = true };
481 if(!mergeIntoItemPtr.oldItem)
482 mergeIntoItemPtr.oldItem = mergeIntoItem;
483 mergeIntoItemPtr.item = beingMergedItem;
484 mergeIntoItemPtr.master = beingMergedItemPtr.master ? beingMergedItemPtr.master : window;
491 if(!mergeIntoItemPtr)
493 if(beingMergedItem.placement && !beingMergedItemPtr.inserted)
495 // Simply add the placement at the end
496 mergeIntoItemPtr = ItemPtr { };
497 mergeIntoItemPtr.inserted = InsertedFlags { deleteLink = true, cleanItem = true };
498 mergeIntoItemPtr.item = beingMergedItem;
499 mergeIntoItemPtr.master = beingMergedItemPtr.master ? beingMergedItemPtr.master : window;
500 items.Add(mergeIntoItemPtr);
505 ItemPtr previous = items.last;
508 // If it is a menu bar, add the item before the first divider
509 for(previous = items.first; previous; previous = previous.next)
510 if(previous.item.isDivider && !previous.inserted) // Added previous.inserted check
512 previous = previous.prev;
518 if(previous && !previous.item.isDivider && !separated)
522 item = MenuDivider { },
523 inserted = InsertedFlags { deleteLink = true, deleteItem = true }
525 items.Insert(previous, ptr);
532 if(!beingMergedItem.isDivider || (previous.item && !previous.item.isDivider))
534 mergeIntoItemPtr = ItemPtr { };
535 items.Insert(previous, mergeIntoItemPtr);
536 mergeIntoItemPtr.inserted = InsertedFlags { deleteLink = true, cleanItem = true };
537 mergeIntoItemPtr.item = beingMergedItem;
538 mergeIntoItemPtr.master = beingMergedItemPtr.master ? beingMergedItemPtr.master : window;
546 void Clean(Window window)
549 for(ptr = items.first; ptr; ptr = next)
551 MenuItem item = ptr.item;
554 if(ptr.inserted.cleanItem)
556 ptr.item = ptr.oldItem;
559 else if(item.subMenu)
560 item.subMenu.Clean(window);
562 if(ptr.inserted.deleteItem)
565 if(ptr.inserted.deleteLink || ptr.inserted.cleanItem)
567 if(ptr.inserted.deleteLink)
571 ptr.inserted.deleteLink = false;
572 ptr.inserted.cleanItem = false;
573 ptr.inserted.deleteItem = false;
579 Menu FindMenu(char * name)
583 for(ptr = items.first; ptr; ptr = ptr.next)
585 MenuItem item = ptr.item;
587 if(item.subMenu && item.subMenu.text && !strcmpTillTab(item.subMenu.text, name))
593 property Menu parent { set { if(value) value.AddSubMenu(this); } };
594 property char * text { set { text = value; /* CopyString(value);*/ } };
595 property Key hotKey { set { hotKey = value; } };
609 color = popupMenuColor;
619 public class PopupMenu : Window
621 class_property(icon) = "<:ecere>controls/menu.png";
633 void (* FontExtent)(Display display, Font font, char * text, int len, int * width, int * height);
635 FontResource boldFont { faceName = font.faceName, font.size, bold = true, window = this };
636 BitmapResource subArrow { fileName = "<:ecere>elements/arrowRight.png", window = this };
637 BitmapResource whiteSubArrow { fileName = "<:ecere>elements/arrowRight.png", monochrome = true, window = this };
638 BitmapResource disabledSubArrow { fileName = "<:ecere>elements/arrowRight.png", grayed = true, window = this };
645 if(menu) delete menu;
649 bool MenuDestroyMasters(bool unselect)
652 PopupMenu window = this, master;
655 for(; (master = (PopupMenu)window.master); window = master)
657 if(!eClass_IsDerived(master._class, _class) || master.isMenuBar)
661 if(eClass_IsDerived(master._class, _class) && master.isMenuBar)
663 master.pressed = false;
666 master.keyboardFocus = false;
667 master.selected = null;
671 result = window.Destroy(0);
672 // This looks like a hack...
677 bool MenuGoToPrevItem()
679 ItemPtr selected, current = this.selected;
680 for(selected = (current && current.prev) ? current.prev : menu.items.last;
682 (selected.item.isDivider || selected.item.placement || ITEM_DISABLED(selected.item)) &&
684 selected = selected.prev ? selected.prev : menu.items.last)
686 if(!current) current = selected; // Endless loop with no previously selected popups
688 this.selected = selected;
689 return selected && selected != current;
692 bool MenuGoToNextItem()
694 ItemPtr selected, current = this.selected;
695 for(selected = (current && current.next) ? current.next : menu.items.first;
697 (selected.item.isDivider || selected.item.placement || ITEM_DISABLED(selected.item)) &&
699 selected = selected.next ? selected.next : menu.items.first)
701 if(!current) current = selected; // Endless loop with no previously selected popups
703 this.selected = selected;
704 return selected && selected != current;
707 void MenuPopupChild(int x, int y, Menu childMenu)
712 if(childMenu.itemCount)
714 PopupMenu child { master = this, menu = childMenu };
718 Window parent = this.parent;
719 Window desktop = guiApp.desktop;
721 x += parent.absPosition.x + parent.clientStart.x - desktop.position.x;
722 y += parent.absPosition.y + parent.clientStart.y - desktop.position.y;
724 x += parent.absPosition.x + parent.clientStart.x;
725 y += parent.absPosition.y + parent.clientStart.y;
727 child.parent = desktop;
731 child.stayOnTop = true;
732 child.parent = parent;
733 child.interim = false;
735 child.position = Point { x, y };
737 // child.displayDriver = "GDI";
743 bool MenuPopupSelected()
747 int selectedX = guiApp.textMode ? 0 : 2;
749 if(selected && selected.item) // Why was this null from OnKeyHit?
751 ItemPtr selected = this.selected, ptr;
752 bool helpBreak = false;
753 Window parent = this.parent;
754 Window activeClient = parent.activeClient;
755 bool systemButtons = activeClient && activeClient.state == maximized;
757 keyboardFocus = true;
762 firstSlave.Destroy(0);
764 for(ptr = menu.items.first; ptr; ptr = ptr.next)
766 MenuItem item = ptr.item;
768 if(item.placement) continue; //&& !ptr.inserted) continue;
772 Menu childMenu = item.subMenu;
776 if(selected.item.subMenu)
777 MenuPopupChild(selectedX, 0, childMenu);
779 keyboardFocus = true;
784 return false; // true
791 int breakX = clientSize.w + 2 - (systemButtons ? 48 : 0);
792 for(nextPtr = ptr.next; nextPtr; nextPtr = nextPtr.next)
794 MenuItem nextItem = nextPtr.item;
795 if(!nextItem.isDivider && ITEM_TEXT(nextItem))
798 FontExtent(display, fontObject, ITEM_TEXT(nextItem), strlen(ITEM_TEXT(nextItem)), &len, null);
802 if(selectedX < breakX) selectedX = breakX;
806 else if(ITEM_TEXT(item))
808 FontExtent(display, fontObject, ITEM_TEXT(item), strlen(ITEM_TEXT(item)), &len, null);
809 selectedX += len + 16;
817 if(selected && selected.item.subMenu)
819 Menu childMenu = selected.item.subMenu;
824 for(ptr = menu.items.first; ptr; ptr = ptr.next)
826 MenuItem item = ptr.item;
828 if(item.placement) continue; //&& !ptr.inserted) continue;
835 y += (item.isDivider) ? DIVIDER_HEIGHT : rh;
839 PopupMenu slave = (PopupMenu)firstSlave;
840 if(!slave || slave.menu != childMenu)
842 if(firstSlave) firstSlave.Destroy(0);
843 MenuPopupChild(position.x + size.w, position.y + selectedY, childMenu);
853 bool MenuItemSelection(Menu parentMenu, ItemPtr selectionPtr, Key key)
855 MenuItem selection = selectionPtr.item;
856 if(!ITEM_DISABLED(selection))
858 Window master = this;
861 master = master.master;
863 if(selectionPtr.master)
864 master = selectionPtr.master;
865 while(eClass_IsDerived(master._class, _class) && master.master)
866 master = master.master;
868 if(selection.checkable)
869 selection.checked = !selection.checked;
870 else if(selection.radio)
872 if(selection.checked) return false;
873 selection.checked = !selection.checked;
877 MenuDestroyMasters(true);
879 /*return */selection.NotifySelect(master, selection, key.modifiers);
885 bool CheckAccelerators(Menu menu, Key key)
889 for(ptr = menu.items.first; ptr; ptr = ptr.next)
891 MenuItem item = ptr.item;
894 if(!CheckAccelerators(item.subMenu, key))
897 else if(!item.isDivider)
899 if(item.accelerator == key)
901 if(MenuItemSelection(menu, ptr, key))
909 ItemPtr FindSelected(int mx, int my, int * selectedX, int * selectedY)
911 Menu menu = this.menu;
912 // Mouse moved inside menu
913 ItemPtr selected = null;
918 if(isMenuBar && menu)
923 bool helpBreak = false;
924 Window parent = this.parent;
925 Window activeClient = parent.activeClient;
926 bool systemButtons = activeClient && activeClient.state == maximized;
928 for(ptr = menu.items.first; ptr; ptr = ptr.next)
930 MenuItem item = ptr.item;
931 if(item.placement) continue; //&& !ptr.inserted) continue;
938 int breakX = clientSize.w - (systemButtons ? 48 : 0);
939 for(nextPtr = ptr.next; nextPtr; nextPtr = nextPtr.next)
941 MenuItem nextItem = nextPtr.item;
942 if(!nextItem.isDivider && ITEM_TEXT(nextItem))
945 FontExtent(display, fontObject, ITEM_TEXT(nextItem), strlen(ITEM_TEXT(nextItem)), &len, null);
949 if(x < breakX) x = breakX;
955 FontExtent(display, fontObject, ITEM_TEXT(item), strlen(ITEM_TEXT(item)), &len, null);
956 if((mx >= x - 16 && mx < x + len + 16))
958 if(!ITEM_DISABLED(item))
964 *selectedY = -position.y;
975 for(ptr = menu.items.first; ptr; ptr = ptr.next)
977 MenuItem item = ptr.item;
978 if(item.placement) continue; //&& !ptr.inserted) continue;
979 if(mx >= 2 && (my >= y && my < y + rh) && !item.isDivider)
981 if(!ITEM_DISABLED(item))
984 *selectedX = position.x + size.w;
990 y += (item.isDivider) ? DIVIDER_HEIGHT : rh;
997 void OnRedraw(Surface surface)
1002 bool helpBreak = false;
1003 Window parent = this.parent;
1004 Window activeClient = parent.activeClient;
1005 bool systemButtons = activeClient && activeClient.state == maximized;
1006 int bitmapOffset = 0;
1008 surface.TextFont(fontObject);
1009 surface.TextExtent(" ", 1, null, &height);
1010 surface.SetBackground(SELECTION_COLOR);
1019 else if(guiApp.textMode)
1023 // Shiny gradient for menu bar
1027 { popupMenuColor, 1 }
1029 surface.Gradient(keys, sizeof(keys)/sizeof(ColorKey), 1, vertical, 0,0, clientSize.w-1, 7);
1035 for(ptr = menu.items.first; ptr; ptr = ptr.next)
1037 MenuItem item = ptr.item;
1038 if(item.placement) continue; //&& !ptr.inserted) continue;
1039 if(!isMenuBar && selected == ptr)
1041 surface.SetForeground(SELECTION_TEXT);
1044 surface.TextOpacity(true);
1045 surface.Area(0,y,clientSize.w-1,y+rh-1);
1048 surface.Area(2,y,clientSize.w-3,y+rh);
1052 surface.SetForeground(foreground);
1054 surface.TextOpacity(false);
1064 int breakX = clientSize.w - (systemButtons ? 48 : 0);
1065 for(nextPtr = ptr.next; nextPtr; nextPtr = nextPtr.next)
1067 MenuItem nextItem = nextPtr.item;
1069 if(!nextItem.isDivider && ITEM_TEXT(nextItem))
1071 surface.TextExtent(ITEM_TEXT(nextItem), strlen(ITEM_TEXT(nextItem)), &len, null);
1075 if(x < breakX) x = breakX;
1083 surface.SetForeground(Color { 85, 85, 85 });
1084 surface.DrawingChar(196);
1085 surface.HLine(x + 2, x + rw - 5, y + (rh) / 2);
1086 surface.DrawingChar(' ');
1090 surface.SetForeground(Color { 85, 85, 85 });
1091 surface.HLine(x + 2, x + rw - 5, y + (DIVIDER_HEIGHT) / 2);
1092 surface.SetForeground(white);
1093 surface.HLine(x + 2, x + rw - 5, y + (DIVIDER_HEIGHT) / 2 + 1);
1099 if(selected == ptr && guiApp.textMode)
1101 surface.SetBackground(SELECTION_COLOR);
1102 surface.SetForeground(SELECTION_TEXT /*background*/);
1103 surface.TextOpacity(true);
1107 Interface::WriteKeyedTextDisabled(surface, x + bitmapOffset,
1108 y + (rh - height)/2, ITEM_TEXT(item), ITEM_HOTKEY(item), ITEM_DISABLED(item));
1110 surface.WriteText(x, y, "\373", 1);
1114 int textY = y + (rh - height)/2;
1115 BitmapResource bitmap = item.disabled ? item.bitmaps[2] : ((selected == ptr) ? item.bitmaps[1] : item.bitmaps[0]);
1116 if(!isMenuBar && bitmap)
1118 Bitmap icon = bitmap.bitmap;
1120 surface.Blit(icon, x + 3, y + (rh - icon.height)/2, 0,0, icon.width, icon.height);
1124 surface.TextFont(boldFont.font);
1126 surface.TextFont(fontObject);
1128 if(ITEM_DISABLED(item) && selected == ptr)
1130 surface.SetForeground(activeBorder);
1131 Interface::WriteKeyedText(surface, x + bitmapOffset + 5,
1132 textY, ITEM_TEXT(item), ITEM_HOTKEY(item));
1135 Interface::WriteKeyedTextDisabled(surface, x + bitmapOffset + 5,
1136 textY, ITEM_TEXT(item), ITEM_HOTKEY(item), ITEM_DISABLED(item));
1137 surface.SetForeground(foreground);
1140 surface.DrawLine(x+5, y+9, x+8,y+12);
1141 surface.DrawLine(x+5, y+10, x+8,y+13);
1142 surface.DrawLine(x+8, y+12, x+12,y+4);
1143 surface.DrawLine(x+8, y+13, x+12,y+5);
1150 // Draw little arrow
1153 surface.SetForeground(foreground);
1157 surface.SetForeground((selected == ptr) ? (background) : (foreground));
1158 surface.WriteText(clientSize.w-8, y+(rh - 8)/2, "\020", 1);
1162 Bitmap arrow = (selected == ptr) ? whiteSubArrow.bitmap : subArrow.bitmap;
1163 if(ITEM_DISABLED(ptr.item)) arrow = disabledSubArrow.bitmap;
1166 surface.Blit(arrow, clientSize.w-14, y+(rh - 8)/2, 0,0, arrow.width, arrow.height);
1168 surface.VLine(y+(rh - 8)/2, y+(rh - 8)/2+7, clientSize.w-10);
1169 surface.SetForeground(Color { 85, 85, 85 });
1170 surface.DrawLine(clientSize.w-10, y+(rh - 8)/2, clientSize.w-4, y+rh / 2);
1171 surface.SetForeground(activeBorder);
1172 surface.DrawLine(clientSize.w-10, y+(rh - 8)/2+7, clientSize.w-4, y+rh / 2);
1179 y += (item.isDivider) ? DIVIDER_HEIGHT : rh;
1181 else if(ITEM_TEXT(item) && !item.isDivider)
1184 surface.TextExtent(ITEM_TEXT(item), strlen(ITEM_TEXT(item)), &len, null);
1185 if(selected == ptr && !guiApp.textMode &&
1186 (item.subMenu || selected))
1188 surface.Bevel(pressed, x, y, len+10, height + 6);
1197 bool OnKeyDown(Key key, unichar ch)
1202 // Non-Interim menus: the slaves don't have focus... pass it down from menu bar
1205 if(!active && firstSlave)
1207 if(!firstSlave.OnKeyDown(key, ch))
1219 MenuDestroyMasters(true);
1232 for(ptr = menu.items.first; ptr; ptr = ptr.next)
1234 MenuItem item = ptr.item;
1235 if((item.placement && !ptr.inserted) || ITEM_DISABLED(item)) continue;
1236 if(((keyboardFocus || !isMenuBar) && ITEM_HOTKEY(item) == key) || (ITEM_HOTKEY(item) | Key { alt = true }) == key)
1238 if(!item.isDivider && !item.subMenu)
1240 if(MenuItemSelection(menu, ptr, key))
1243 else if(item.subMenu)
1246 MenuPopupSelected();
1252 if(ch >= 32 && ch != 128 && !isMenuBar)
1257 // Interim menus: the slaves are the ones with the focus
1258 if(result && INTERIM_MENU)
1260 Window master = this.master;
1261 if(eClass_IsDerived(master._class, _class))
1263 if(!master.OnKeyDown(key, ch))
1271 bool OnKeyUp(Key key, unichar ch)
1277 if(isMenuBar && altDown)
1282 keyboardFocus = true;
1288 if(firstSlave) firstSlave.Destroy(0);
1290 keyboardFocus = false;
1303 bool OnKeyHit(Key key, unichar ch)
1307 if(key == leftAlt || key == rightAlt) return true;
1309 if(key && isMenuBar)
1314 result = CheckAccelerators(menu, key);
1315 if(result && !key.alt && key != escape)
1320 if(result && visible)
1322 // Non-Interim menus: the slaves don't have focus... pass it down from menu bar
1325 if(!active && firstSlave)
1327 if(!firstSlave.OnKeyHit(key, ch))
1339 selected = menu.items.last;
1340 if(MenuGoToNextItem())
1348 selected = menu.items.first;
1349 if(MenuGoToPrevItem())
1357 if(MenuGoToPrevItem())
1358 MenuPopupSelected();
1363 PopupMenu master = (PopupMenu)this.master;
1364 if(master && !master.isMenuBar)
1376 if(MenuGoToNextItem() && MenuPopupSelected())
1381 if(selected && !ITEM_DISABLED(selected.item))
1383 if(!MenuPopupSelected())
1387 selected = menu.items.first;
1394 else if(!((PopupMenu)master).isMenuBar)
1396 if(MenuGoToNextItem())
1406 return MenuPopupSelected();
1410 if(firstSlave) firstSlave.Destroy(0);
1415 case down: case Key::altDown:
1418 return MenuPopupSelected();
1422 if(MenuGoToNextItem())
1427 case up: case altUp:
1430 if(MenuGoToPrevItem())
1435 case enter: case altEnter:
1436 case keyPadEnter: case altKeyPadEnter:
1439 if(!selected.item.isDivider && !selected.item.subMenu)
1441 if(MenuItemSelection(menu, selected, key))
1449 result = selected ? false : true;
1452 keyboardFocus = false;
1459 PopupMenu master = (PopupMenu)this.master;
1460 if(eClass_IsDerived(master._class, _class) && master.isMenuBar)
1462 ItemPtr selected = master.selected;
1464 master.pressed = true;
1465 master.selected = selected;
1466 master.keyboardFocus = true;
1479 if(ch >= 32 && !isMenuBar)
1482 for(ptr = menu.items.first; ptr; ptr = ptr.next)
1484 MenuItem item = ptr.item;
1485 if((item.placement && !ptr.inserted) || ITEM_DISABLED(item)) continue;
1486 if(ITEM_HOTKEY(item) == key || (ITEM_HOTKEY(item) | Key { alt = true }) == key)
1488 if(!item.isDivider && !item.subMenu)
1490 if(MenuItemSelection(menu, ptr, key))
1493 else if(item.subMenu)
1496 MenuPopupSelected();
1503 if(result && isMenuBar && pressed)
1506 // Interim menus: the slaves are the ones with the focus
1507 if(result && INTERIM_MENU)
1509 Window master = this.master;
1510 if(eClass_IsDerived(master._class, _class))
1512 if(!master.OnKeyHit(key, ch))
1520 bool OnLoadGraphics()
1522 Font font = fontObject;
1523 int maxW = 0, maxH = 0;
1528 background = isMenuBar ? menuBarColor : (menu ? menu.color : popupMenuColor);
1529 FontExtent = Display::FontExtent;
1534 for(ptr = menu.items.first; ptr; ptr = ptr.next)
1536 MenuItem item = ptr.item;
1537 if(item.placement) continue; //&& !ptr.inserted) continue;
1544 FontExtent(display, font,ITEM_TEXT(item),strlen(ITEM_TEXT(item)),&width, &height);
1545 if(strstr(ITEM_TEXT(item), "\t"))
1548 if(item.subMenu) width += 20;
1549 if(!guiApp.textMode)
1554 if(!guiApp.textMode)
1555 height = DIVIDER_HEIGHT;
1560 if(width > maxW) maxW = width;
1561 if(height > maxH) maxH = height;
1563 totalHeight += height;
1565 if(item.bitmaps[0]) AddResource(item.bitmaps[0]);
1566 if(item.bitmaps[1]) AddResource(item.bitmaps[1]);
1567 if(item.bitmaps[2]) AddResource(item.bitmaps[2]);
1572 FontExtent(display, font,menu.text,strlen(menu.text),&width, &height);
1573 if(width > maxW) maxW = width;
1574 if(height > maxH) maxH = height;
1580 if(rw < maxW) rw = maxW;
1581 if(rh < maxH) rh = maxH;
1586 bool OnResizing(int * w, int * h)
1588 Window master = this.master;
1589 Window masterMenuBar = master.menuBar;
1590 if(this != masterMenuBar)
1594 *h = Max(*h, rh + 2);
1596 *h = Max(*h, totalHeight + 2);
1598 if(this != masterMenuBar)
1608 bool OnMoving(int *x, int *y, int w, int h)
1612 Window parent = this.parent;
1615 if(*y + h > parent.clientSize.h)
1617 PopupMenu master = (PopupMenu)this.master;
1619 if(eClass_IsDerived(master._class, _class))
1621 if(master.isMenuBar)
1622 *y -= master.size.h;
1624 *y += h - clientSize.h + rh;
1628 *x = Min(*x, ((parent == guiApp.desktop) ? guiApp.virtualScreen.w : parent.clientSize.w) - w);
1632 *x = Min(*x, parent.size.w - w);
1633 *y = Min(*y, parent.size.h - h);
1637 *x = Min(*x, parent.clientSize.w - w);
1638 *y = Min(*y, parent.clientSize.h - h);
1640 if(parent == guiApp.desktop)
1643 *x = Max(*x, guiApp.virtualScreenPos.x);
1644 *y = Max(*y, guiApp.virtualScreenPos.y);
1656 bool OnMouseMove(int mx, int my, Modifiers mods)
1658 int selectedX, selectedY;
1660 ItemPtr selected = FindSelected(mx, my, &selectedX, &selectedY);
1662 if((!mods.isSideEffect /*|| !selected*/) && (/*selected && */
1663 selected != this.selected && (!selected || !ITEM_DISABLED(selected.item)) && (selected || !keyboardFocus)))
1665 if(!isMenuBar || pressed)
1667 bool pressed = this.pressed;
1669 if(firstSlave) firstSlave.Destroy(0);
1671 this.selected = selected;
1675 Menu childMenu = selected.item.subMenu;
1677 this.pressed = pressed;
1679 if(this.selected.item.subMenu)
1680 MenuPopupChild(selectedX, position.y + selectedY, childMenu);
1683 keyboardFocus = true;
1687 this.selected = selected;
1693 bool OnLeftButtonDown(int x, int y, Modifiers mods)
1697 if(GetTime() - unpressedTime < 0.01)
1701 int selectedX, selectedY;
1702 if(!mods.isActivate && !pressed)
1705 keyboardFocus = true;
1707 OnMouseMove(x,y, mods);
1711 if(firstSlave) firstSlave.Destroy(0);
1712 selected = FindSelected(x, y, &selectedX, &selectedY);
1716 //keyboardFocus = false;
1725 keyboardFocus = false;
1726 if(firstSlave) firstSlave.Destroy(0);
1732 keyboardFocus = true;
1734 OnMouseMove(x, y, mods);
1738 else //if(!INTERIM_MENU) // Why was this commented out?
1740 if(!(x >= 0 && y >= 0 && x < clientSize.w && y < clientSize.h))
1742 MenuDestroyMasters(false);
1749 bool OnRightButtonDown(int x, int y, Modifiers mods)
1751 if(x >= 0 && y >= 0 && x < clientSize.w && y < clientSize.h)
1755 Window master = this.master;
1756 Window activeClient = master.activeClient;
1757 if(activeClient.state == maximized)
1758 activeClient.ShowSysMenu(absPosition.x + x, absPosition.y + y);
1764 bool OnLeftButtonUp(int mx, int my, Modifiers mods)
1766 if(mx >= 2 /*0*/ && my >= 0 && mx < clientSize.w && my < clientSize.h)
1768 Menu menu = this.menu;
1772 if(!isMenuBar && menu)
1775 for(ptr = menu.items.first; ptr; ptr = ptr.next)
1777 MenuItem item = ptr.item;
1778 if(item.placement) continue; //&& !ptr.inserted) continue;
1779 if(my >= y && my < y + rh && !item.isDivider)
1787 y += (item.isDivider) ? DIVIDER_HEIGHT : rh;
1791 if(!isMenuBar || pressed)
1795 if(!selected.item.isDivider && !selected.item.subMenu)
1800 keyboardFocus = false;
1803 if(MenuItemSelection(menu, selected, (Key)mods))
1810 keyboardFocus = false;
1811 if(firstSlave) firstSlave.Destroy(0);
1818 OnRightButtonUp = OnLeftButtonUp;
1820 bool OnLeftDoubleClick(int x, int y, Modifiers mods)
1824 int selectedX, selectedY;
1825 ItemPtr selected = FindSelected(x, y, &selectedX, &selectedY);
1828 Window master = this.master;
1829 Window activeClient = master.activeClient;
1830 if(activeClient && activeClient.state == maximized)
1831 activeClient.SetState(normal, false, mods);
1837 bool OnMouseLeave(Modifiers mods)
1839 if(!pressed && !firstSlave)
1848 bool OnActivate(bool active, Window swap, bool * goOnWithActivation, bool direct)
1856 PopupMenu master = (PopupMenu)this.master;
1857 if(eClass_IsDerived(master._class, _class) && master.isMenuBar) // && swap != master && swap && swap.master != master)
1859 if(rootWindow != this)
1861 unpressedTime = GetTime();
1862 master.pressed = false;
1863 master.selected = null;
1864 master.keyboardFocus = false;
1865 // master.Update(null);
1867 // TOFIX: Redraw bug here without this...
1868 master.master.Update(null);
1876 bool destroy = true;
1880 for(master = swap.master; master; master = master.master)
1889 for(master = this.master; master; master = master.master)
1892 if(eClass_IsDerived(master._class, _class))
1899 if(MenuDestroyMasters(false))
1906 // With new activation code this is not required anymore (double effect if there)
1907 else if(!firstSlave || firstSlave.rootWindow != firstSlave)
1912 unpressedTime = GetTime();
1915 keyboardFocus = false;
1916 if(firstSlave) firstSlave.Destroy(0);
1936 if(firstSlave) firstSlave.Destroy(0);
1938 Move(position.x, position.y, size.w, size.h);
1942 if(interim || isMenuBar)
1950 property bool isMenuBar { set { isMenuBar = value; } };
1951 property bool focus { get { return keyboardFocus; } };