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
180 bitmaps[1] = value ? (value.alphaBlend ? value : { fileName = value.fileName, monochrome = true }) : null;
181 bitmaps[2] = value ? { fileName = value.fileName, grayed = true } : null;
186 get { return bitmaps[0]; }
188 property bool copyText
194 if(text && !copyText)
195 text = CopyString(ITEM_TEXT(this));
205 property bool bold { set { bold = value; } get { return bold; } };
207 virtual bool Window::NotifySelect(MenuItem selection, Modifiers mods);
217 BitmapResource bitmaps[3];
218 bool checkable, radio;
224 bool manualAccelText;
230 // delete ITEM_TEXT(this);
239 public class MenuDivider : MenuItem
246 // property Menu parent { set {} };
249 public class MenuPlacement : MenuItem
258 property Menu parent { set {} };
259 property char * text { set {} };
260 property Key hotKey { set {} };
268 int OnCompare(Menu menu)
270 return (this != null) != (menu != null);
273 void AddItem(MenuItem item)
279 for(ptr = items.first; ptr; ptr = ptr.next)
281 MenuItem check = ptr.item;
284 if(!strcmpTillTab(ITEM_TEXT(check), ITEM_TEXT(item)))
295 ptr.inserted = InsertedFlags { placed = true };
296 ptr.oldItem = ptr.item;
311 void RemoveItem(MenuItem item)
313 if(item.menu == this)
316 for(ptr = items.first; ptr; ptr = ptr.next)
319 if(ptr.inserted.placed)
321 ptr.item = ptr.oldItem;
327 if(!ptr.inserted.placed)
337 void AddSubMenu(Menu subMenu)
341 MenuItem menuItem { };
342 ItemPtr ptr { item = menuItem };
349 menuItem.menu = this;
353 menuItem.subMenu = subMenu;
359 void AddDynamic(MenuItem addedItem, Window master, bool persistent)
363 ItemPtr ptr = null, oldItemPtr;
365 for(oldItemPtr = items.first; oldItemPtr; oldItemPtr = oldItemPtr.next)
367 if((oldItemPtr.item.subMenu || oldItemPtr.item.placement) && !strcmpTillTab(ITEM_TEXT(oldItemPtr.item), ITEM_TEXT(addedItem)))
369 MenuItem oldItem = oldItemPtr.item;
370 if(!oldItem.placement)
372 oldItem.subMenu.Merge(addedItem.subMenu, true, master);
374 // If sub menu already has a master...
377 oldItemPtr.inserted = InsertedFlags { cleanItem = true };
378 if(!oldItemPtr.oldItem)
379 oldItemPtr.oldItem = oldItem;
380 oldItemPtr.item = addedItem;
392 ptr.inserted = InsertedFlags { deleteLink = true, deleteItem = true };
395 ptr.inserted = InsertedFlags { cleanItem = true, deleteItem = true };
397 ptr.item = addedItem;
401 addedItem.menu = this;
405 MenuItem FindItem(bool (* Window::notifySelect)(MenuItem selection, Modifiers mods), uint id)
409 for(ptr = items.first; ptr; ptr = ptr.next)
411 MenuItem item = ptr.item;
414 MenuItem subItem = item.subMenu.FindItem(notifySelect, id);
415 if(subItem) return subItem;
417 else if(!item.isDivider && !item.placement)
419 if(item.id == id && item.NotifySelect == notifySelect)
429 while((ptr = items.first))
432 if(ptr.inserted.cleanItem || ptr.inserted.placed)
434 ptr.item = ptr.oldItem;
442 void Merge(Menu menuBeingMerged, bool menuBar, Window window)
444 bool separated = false;
445 ItemPtr beingMergedItemPtr;
447 for(beingMergedItemPtr = menuBeingMerged.items.first; beingMergedItemPtr; beingMergedItemPtr = beingMergedItemPtr.next)
449 MenuItem beingMergedItem = beingMergedItemPtr.item;
450 ItemPtr mergeIntoItemPtr = null;
452 if(!beingMergedItem) continue;
453 if(beingMergedItem.subMenu)
455 for(mergeIntoItemPtr = items.first; mergeIntoItemPtr; mergeIntoItemPtr = mergeIntoItemPtr.next)
457 if((mergeIntoItemPtr.item.subMenu || mergeIntoItemPtr.item.placement) && !strcmpTillTab(ITEM_TEXT(mergeIntoItemPtr.item), ITEM_TEXT(beingMergedItem)))
459 MenuItem mergeIntoItem = mergeIntoItemPtr.item;
460 if(!beingMergedItem.placement || beingMergedItemPtr.inserted)
462 if(!mergeIntoItem.placement && !mergeIntoItemPtr.inserted.cleanItem) // Added this last check for ActiveChild overriding ActiveClient's menu
464 mergeIntoItem.subMenu.Merge(beingMergedItem.subMenu, menuBar, window);
466 // If sub menu already has a master...
469 mergeIntoItemPtr.inserted = InsertedFlags { cleanItem = true };
470 if(!mergeIntoItemPtr.oldItem)
471 mergeIntoItemPtr.oldItem = mergeIntoItem;
472 mergeIntoItemPtr.item = beingMergedItem;
474 mergeIntoItemPtr.master = window;
481 else if(!beingMergedItem.isDivider)
483 for(mergeIntoItemPtr = items.first; mergeIntoItemPtr; mergeIntoItemPtr = mergeIntoItemPtr.next)
485 MenuItem mergeIntoItem = mergeIntoItemPtr.item;
486 if(/*!mergeIntoItem.subMenu && /-*mergeIntoItem.placement && !mergeIntoItemPtr.inserted && */!strcmpTillTab(ITEM_TEXT(mergeIntoItem), ITEM_TEXT(beingMergedItem)))
488 //if(!beingMergedItem.placement || beingMergedItemPtr.inserted)
490 mergeIntoItemPtr.inserted = InsertedFlags { cleanItem = true };
491 if(!mergeIntoItemPtr.oldItem)
492 mergeIntoItemPtr.oldItem = mergeIntoItem;
493 mergeIntoItemPtr.item = beingMergedItem;
494 mergeIntoItemPtr.master = beingMergedItemPtr.master ? beingMergedItemPtr.master : window;
501 if(!mergeIntoItemPtr)
503 if(beingMergedItem.placement && !beingMergedItemPtr.inserted)
505 // Simply add the placement at the end
506 mergeIntoItemPtr = ItemPtr { };
507 mergeIntoItemPtr.inserted = InsertedFlags { deleteLink = true, cleanItem = true };
508 mergeIntoItemPtr.item = beingMergedItem;
509 mergeIntoItemPtr.master = beingMergedItemPtr.master ? beingMergedItemPtr.master : window;
510 items.Add(mergeIntoItemPtr);
515 ItemPtr previous = items.last;
518 // If it is a menu bar, add the item before the first divider
519 for(previous = items.first; previous; previous = previous.next)
520 if(previous.item.isDivider && !previous.inserted) // Added previous.inserted check
522 previous = previous.prev;
528 if(previous && !previous.item.isDivider && !separated)
532 item = MenuDivider { },
533 inserted = InsertedFlags { deleteLink = true, deleteItem = true }
535 items.Insert(previous, ptr);
542 if(!beingMergedItem.isDivider || (previous.item && !previous.item.isDivider))
544 mergeIntoItemPtr = ItemPtr { };
545 items.Insert(previous, mergeIntoItemPtr);
546 mergeIntoItemPtr.inserted = InsertedFlags { deleteLink = true, cleanItem = true };
547 mergeIntoItemPtr.item = beingMergedItem;
548 mergeIntoItemPtr.master = beingMergedItemPtr.master ? beingMergedItemPtr.master : window;
556 void Clean(Window window)
559 for(ptr = items.first; ptr; ptr = next)
561 MenuItem item = ptr.item;
564 if(ptr.inserted.cleanItem)
566 ptr.item = ptr.oldItem;
569 else if(item.subMenu)
570 item.subMenu.Clean(window);
572 if(ptr.inserted.deleteItem)
575 if(ptr.inserted.deleteLink || ptr.inserted.cleanItem)
577 if(ptr.inserted.deleteLink)
581 ptr.inserted.deleteLink = false;
582 ptr.inserted.cleanItem = false;
583 ptr.inserted.deleteItem = false;
589 Menu FindMenu(char * name)
593 for(ptr = items.first; ptr; ptr = ptr.next)
595 MenuItem item = ptr.item;
597 if(item.subMenu && item.subMenu.text && !strcmpTillTab(item.subMenu.text, name))
603 property Menu parent { set { if(value) value.AddSubMenu(this); } };
604 property char * text { set { text = value; /* CopyString(value);*/ } };
605 property Key hotKey { set { hotKey = value; } };
606 property bool hasMargin { set { hasMargin = value; } };
621 color = popupMenuColor;
631 public class PopupMenu : Window
633 class_property(icon) = "<:ecere>controls/menu.png";
645 void (* FontExtent)(Display display, Font font, char * text, int len, int * width, int * height);
647 FontResource boldFont { faceName = font.faceName, font.size, bold = true, window = this };
648 BitmapResource subArrow { fileName = "<:ecere>elements/arrowRight.png", window = this };
649 BitmapResource whiteSubArrow { fileName = "<:ecere>elements/arrowRight.png", monochrome = true, window = this };
650 BitmapResource disabledSubArrow { fileName = "<:ecere>elements/arrowRight.png", grayed = true, window = this };
657 if(menu) delete menu;
661 bool MenuDestroyMasters(bool unselect)
664 PopupMenu window = this, master;
667 for(; (master = (PopupMenu)window.master); window = master)
669 if(!eClass_IsDerived(master._class, _class) || master.isMenuBar)
673 if(eClass_IsDerived(master._class, _class) && master.isMenuBar)
675 master.pressed = false;
678 master.keyboardFocus = false;
679 master.selected = null;
683 result = window.Destroy(0);
684 // This looks like a hack...
689 bool MenuGoToPrevItem()
691 ItemPtr selected, current = this.selected;
692 for(selected = (current && current.prev) ? current.prev : menu.items.last;
694 (selected.item.isDivider || selected.item.placement || ITEM_DISABLED(selected.item)) &&
696 selected = selected.prev ? selected.prev : menu.items.last)
698 if(!current) current = selected; // Endless loop with no previously selected popups
700 this.selected = selected;
701 return selected && selected != current;
704 bool MenuGoToNextItem()
706 ItemPtr selected, current = this.selected;
707 for(selected = (current && current.next) ? current.next : menu.items.first;
709 (selected.item.isDivider || selected.item.placement || ITEM_DISABLED(selected.item)) &&
711 selected = selected.next ? selected.next : menu.items.first)
713 if(!current) current = selected; // Endless loop with no previously selected popups
715 this.selected = selected;
716 return selected && selected != current;
719 void MenuPopupChild(int x, int y, Menu childMenu)
724 if(childMenu.itemCount)
726 PopupMenu child { master = this, menu = childMenu };
730 Window parent = this.parent;
731 Window desktop = guiApp.desktop;
733 x += parent.absPosition.x + parent.clientStart.x - desktop.position.x;
734 y += parent.absPosition.y + parent.clientStart.y - desktop.position.y;
736 x += parent.absPosition.x + parent.clientStart.x;
737 y += parent.absPosition.y + parent.clientStart.y;
739 child.parent = desktop;
743 child.stayOnTop = true;
744 child.parent = parent;
745 child.interim = false;
747 child.position = Point { x, y };
749 // child.displayDriver = "GDI";
755 bool MenuPopupSelected()
759 int selectedX = guiApp.textMode ? 0 : 2;
761 if(selected && selected.item) // Why was this null from OnKeyHit?
763 ItemPtr selected = this.selected, ptr;
764 bool helpBreak = false;
765 Window parent = this.parent;
766 Window activeClient = parent.activeClient;
767 bool systemButtons = activeClient && activeClient.state == maximized;
769 keyboardFocus = true;
774 firstSlave.Destroy(0);
776 for(ptr = menu.items.first; ptr; ptr = ptr.next)
778 MenuItem item = ptr.item;
780 if(item.placement) continue; //&& !ptr.inserted) continue;
784 Menu childMenu = item.subMenu;
788 if(selected.item.subMenu)
789 MenuPopupChild(selectedX, 0, childMenu);
791 keyboardFocus = true;
796 return false; // true
803 int breakX = clientSize.w + 2 - (systemButtons ? 48 : 0);
804 for(nextPtr = ptr.next; nextPtr; nextPtr = nextPtr.next)
806 MenuItem nextItem = nextPtr.item;
807 if(!nextItem.isDivider && ITEM_TEXT(nextItem))
810 FontExtent(display, fontObject, ITEM_TEXT(nextItem), strlen(ITEM_TEXT(nextItem)), &len, null);
814 if(selectedX < breakX) selectedX = breakX;
818 else if(ITEM_TEXT(item))
820 FontExtent(display, fontObject, ITEM_TEXT(item), strlen(ITEM_TEXT(item)), &len, null);
821 selectedX += len + 16;
829 if(selected && selected.item.subMenu)
831 Menu childMenu = selected.item.subMenu;
836 for(ptr = menu.items.first; ptr; ptr = ptr.next)
838 MenuItem item = ptr.item;
840 if(item.placement) continue; //&& !ptr.inserted) continue;
847 y += (item.isDivider) ? DIVIDER_HEIGHT : rh;
851 PopupMenu slave = (PopupMenu)firstSlave;
852 if(!slave || slave.menu != childMenu)
854 if(firstSlave) firstSlave.Destroy(0);
855 MenuPopupChild(position.x + size.w, position.y + selectedY, childMenu);
865 bool MenuItemSelection(Menu parentMenu, ItemPtr selectionPtr, Key key)
867 MenuItem selection = selectionPtr.item;
868 if(!ITEM_DISABLED(selection))
870 Window master = this;
873 master = master.master;
875 if(selectionPtr.master)
876 master = selectionPtr.master;
877 while(eClass_IsDerived(master._class, _class) && master.master)
878 master = master.master;
880 if(selection.checkable)
881 selection.checked = !selection.checked;
882 else if(selection.radio)
884 if(selection.checked) return false;
885 selection.checked = !selection.checked;
889 MenuDestroyMasters(true);
891 /*return */selection.NotifySelect(master, selection, key.modifiers);
897 bool CheckAccelerators(Menu menu, Key key)
901 for(ptr = menu.items.first; ptr; ptr = ptr.next)
903 MenuItem item = ptr.item;
906 if(!CheckAccelerators(item.subMenu, key))
909 else if(!item.isDivider)
911 if(item.accelerator == key)
913 if(MenuItemSelection(menu, ptr, key))
921 ItemPtr FindSelected(int mx, int my, int * selectedX, int * selectedY)
923 Menu menu = this.menu;
924 // Mouse moved inside menu
925 ItemPtr selected = null;
930 if(isMenuBar && menu)
935 bool helpBreak = false;
936 Window parent = this.parent;
937 Window activeClient = parent.activeClient;
938 bool systemButtons = activeClient && activeClient.state == maximized;
940 for(ptr = menu.items.first; ptr; ptr = ptr.next)
942 MenuItem item = ptr.item;
943 if(item.placement) continue; //&& !ptr.inserted) continue;
950 int breakX = clientSize.w - (systemButtons ? 48 : 0);
951 for(nextPtr = ptr.next; nextPtr; nextPtr = nextPtr.next)
953 MenuItem nextItem = nextPtr.item;
954 if(!nextItem.isDivider && ITEM_TEXT(nextItem))
957 FontExtent(display, fontObject, ITEM_TEXT(nextItem), strlen(ITEM_TEXT(nextItem)), &len, null);
961 if(x < breakX) x = breakX;
967 char * text = ITEM_TEXT(item);
968 FontExtent(display, fontObject, text, text ? strlen(text) : 0, &len, null);
969 if((mx >= x - 16 && mx < x + len + 16))
971 if(!ITEM_DISABLED(item))
977 *selectedY = -position.y;
988 for(ptr = menu.items.first; ptr; ptr = ptr.next)
990 MenuItem item = ptr.item;
991 if(item.placement) continue; //&& !ptr.inserted) continue;
992 if(mx >= 2 && (my >= y && my < y + rh) && !item.isDivider)
994 if(!ITEM_DISABLED(item))
997 *selectedX = position.x + size.w;
1003 y += (item.isDivider) ? DIVIDER_HEIGHT : rh;
1010 void OnRedraw(Surface surface)
1012 bool hasMargin = menu ? menu.hasMargin : false;
1016 bool helpBreak = false;
1017 Window parent = this.parent;
1018 Window activeClient = parent.activeClient;
1019 bool systemButtons = activeClient && activeClient.state == maximized;
1020 int bitmapOffset = 0;
1021 bool hasBitmap = false;
1023 surface.TextFont(fontObject);
1024 surface.TextExtent(" ", 1, null, &height);
1025 surface.SetBackground(SELECTION_COLOR);
1032 for(ptr = menu.items.first; ptr; ptr = ptr.next)
1034 if(ptr.item.bitmaps[0])
1045 bitmapOffset = hasMargin ? 27 : (hasBitmap ? 18 : 12);
1047 else if(guiApp.textMode)
1051 // Shiny gradient for menu bar
1055 { popupMenuColor, 1 }
1057 surface.Gradient(keys, sizeof(keys)/sizeof(ColorKey), 1, vertical, 0,0, clientSize.w-1, 7);
1063 for(ptr = menu.items.first; ptr; ptr = ptr.next)
1065 MenuItem item = ptr.item;
1066 if(item.placement) continue; //&& !ptr.inserted) continue;
1067 if(!isMenuBar && selected == ptr)
1069 surface.SetForeground(SELECTION_TEXT);
1072 surface.TextOpacity(true);
1073 surface.Area(0,y,clientSize.w-1,y+rh-1);
1076 surface.Area(/*(hasMargin ? bitmapOffset : 0) +*/ 2,y,clientSize.w-3,y+rh);
1080 surface.SetForeground(foreground);
1082 surface.TextOpacity(false);
1092 int breakX = clientSize.w - (systemButtons ? 48 : 0);
1093 for(nextPtr = ptr.next; nextPtr; nextPtr = nextPtr.next)
1095 MenuItem nextItem = nextPtr.item;
1097 if(!nextItem.isDivider && ITEM_TEXT(nextItem))
1099 surface.TextExtent(ITEM_TEXT(nextItem), strlen(ITEM_TEXT(nextItem)), &len, null);
1103 if(x < breakX) x = breakX;
1111 surface.SetForeground(Color { 85, 85, 85 });
1112 surface.DrawingChar(196);
1113 surface.HLine(x + 2, x + rw - 5, y + (rh) / 2);
1114 surface.DrawingChar(' ');
1118 int start = x + hasMargin ? bitmapOffset : 2;
1119 int end = x + rw - (hasMargin ? 13 : 5);
1120 surface.foreground = Color { 85, 85, 85 };
1121 surface.HLine(start, end, y + (DIVIDER_HEIGHT) / 2);
1122 surface.foreground = white;
1123 surface.HLine(start, end, y + (DIVIDER_HEIGHT) / 2 + 1);
1129 if(selected == ptr && guiApp.textMode)
1131 surface.SetBackground(SELECTION_COLOR);
1132 surface.SetForeground(SELECTION_TEXT /*background*/);
1133 surface.TextOpacity(true);
1137 Interface::WriteKeyedTextDisabled(surface, x + bitmapOffset,
1138 y + (rh - height)/2, ITEM_TEXT(item), ITEM_HOTKEY(item), ITEM_DISABLED(item));
1140 surface.WriteText(x, y, "\373", 1);
1144 int textY = y + (rh - height)/2;
1145 BitmapResource bitmap = item.disabled ? item.bitmaps[2] : ((selected == ptr) ? item.bitmaps[1] : item.bitmaps[0]);
1146 if(!isMenuBar && bitmap)
1148 Bitmap icon = bitmap.bitmap;
1150 surface.Blit(icon, x + hasMargin ? 5 : 3, y + (rh - icon.height)/2, 0,0, icon.width, icon.height);
1154 surface.TextFont(boldFont.font);
1156 surface.TextFont(fontObject);
1158 if(ITEM_DISABLED(item) && selected == ptr)
1160 surface.SetForeground(formColor);
1161 Interface::WriteKeyedText(surface, x + bitmapOffset + 5,
1162 textY, ITEM_TEXT(item), ITEM_HOTKEY(item));
1165 Interface::WriteKeyedTextDisabled(surface, x + bitmapOffset + 5,
1166 textY, ITEM_TEXT(item), ITEM_HOTKEY(item), ITEM_DISABLED(item));
1168 // A nice vertical separation line
1169 if(hasMargin && !isMenuBar)
1171 surface.foreground = Color { 85, 85, 85 };
1172 surface.VLine(clientArea.top, clientArea.bottom, x + bitmapOffset - 2);
1173 surface.foreground = white;
1174 surface.VLine(clientArea.top, clientArea.bottom, x + bitmapOffset - 1);
1176 surface.foreground = foreground;
1179 surface.DrawLine(x+5, y+9, x+8,y+12);
1180 surface.DrawLine(x+5, y+10, x+8,y+13);
1181 surface.DrawLine(x+8, y+12, x+12,y+4);
1182 surface.DrawLine(x+8, y+13, x+12,y+5);
1189 // Draw little arrow
1192 surface.SetForeground(foreground);
1196 surface.SetForeground((selected == ptr) ? (background) : (foreground));
1197 surface.WriteText(clientSize.w-8, y+(rh - 8)/2, "\020", 1);
1201 Bitmap arrow = (selected == ptr) ? whiteSubArrow.bitmap : subArrow.bitmap;
1202 if(ITEM_DISABLED(ptr.item)) arrow = disabledSubArrow.bitmap;
1205 surface.Blit(arrow, clientSize.w-14, y+(rh - 8)/2, 0,0, arrow.width, arrow.height);
1207 surface.VLine(y+(rh - 8)/2, y+(rh - 8)/2+7, clientSize.w-10);
1208 surface.SetForeground(Color { 85, 85, 85 });
1209 surface.DrawLine(clientSize.w-10, y+(rh - 8)/2, clientSize.w-4, y+rh / 2);
1210 surface.SetForeground(formColor);
1211 surface.DrawLine(clientSize.w-10, y+(rh - 8)/2+7, clientSize.w-4, y+rh / 2);
1218 y += (item.isDivider) ? DIVIDER_HEIGHT : rh;
1220 else if(ITEM_TEXT(item) && !item.isDivider)
1223 surface.TextExtent(ITEM_TEXT(item), strlen(ITEM_TEXT(item)), &len, null);
1224 if(selected == ptr && !guiApp.textMode &&
1225 (item.subMenu || selected))
1227 surface.Bevel(pressed, x, y, len+10, height + 6);
1236 bool OnKeyDown(Key key, unichar ch)
1241 // Non-Interim menus: the slaves don't have focus... pass it down from menu bar
1244 if(!active && firstSlave)
1246 if(!firstSlave.OnKeyDown(key, ch))
1258 MenuDestroyMasters(true);
1271 for(ptr = menu.items.first; ptr; ptr = ptr.next)
1273 MenuItem item = ptr.item;
1274 if((item.placement && !ptr.inserted) || ITEM_DISABLED(item)) continue;
1275 if(((keyboardFocus || !isMenuBar) && ITEM_HOTKEY(item) == key) || (ITEM_HOTKEY(item) | Key { alt = true }) == key)
1277 if(!item.isDivider && !item.subMenu)
1279 if(MenuItemSelection(menu, ptr, key))
1282 else if(item.subMenu)
1285 MenuPopupSelected();
1291 if(ch >= 32 && ch != 128 && !isMenuBar)
1296 // Interim menus: the slaves are the ones with the focus
1297 if(result && INTERIM_MENU)
1299 Window master = this.master;
1300 if(eClass_IsDerived(master._class, _class))
1302 if(!master.OnKeyDown(key, ch))
1310 bool OnKeyUp(Key key, unichar ch)
1316 if(isMenuBar && altDown)
1321 keyboardFocus = true;
1327 if(firstSlave) firstSlave.Destroy(0);
1329 keyboardFocus = false;
1342 bool OnKeyHit(Key key, unichar ch)
1346 if(key == leftAlt || key == rightAlt) return true;
1348 if(key && isMenuBar)
1353 result = CheckAccelerators(menu, key);
1354 if(result && !key.alt && key != escape)
1359 if(result && visible)
1361 // Non-Interim menus: the slaves don't have focus... pass it down from menu bar
1364 if(!active && firstSlave)
1366 if(!firstSlave.OnKeyHit(key, ch))
1378 selected = menu.items.last;
1379 if(MenuGoToNextItem())
1387 selected = menu.items.first;
1388 if(MenuGoToPrevItem())
1396 if(MenuGoToPrevItem())
1397 MenuPopupSelected();
1402 PopupMenu master = (PopupMenu)this.master;
1403 if(master && !master.isMenuBar)
1415 if(MenuGoToNextItem() && MenuPopupSelected())
1420 if(selected && !ITEM_DISABLED(selected.item))
1422 if(!MenuPopupSelected())
1426 selected = menu.items.first;
1433 else if(!((PopupMenu)master).isMenuBar)
1435 if(MenuGoToNextItem())
1445 return MenuPopupSelected();
1449 if(firstSlave) firstSlave.Destroy(0);
1454 case down: case Key::altDown:
1457 return MenuPopupSelected();
1461 if(MenuGoToNextItem())
1466 case up: case altUp:
1469 if(MenuGoToPrevItem())
1474 case enter: case altEnter:
1475 case keyPadEnter: case altKeyPadEnter:
1478 if(!selected.item.isDivider && !selected.item.subMenu)
1480 if(MenuItemSelection(menu, selected, key))
1488 result = selected ? false : true;
1491 keyboardFocus = false;
1498 PopupMenu master = (PopupMenu)this.master;
1499 if(eClass_IsDerived(master._class, _class) && master.isMenuBar)
1501 ItemPtr selected = master.selected;
1503 master.pressed = true;
1504 master.selected = selected;
1505 master.keyboardFocus = true;
1518 if(ch >= 32 && !isMenuBar)
1521 for(ptr = menu.items.first; ptr; ptr = ptr.next)
1523 MenuItem item = ptr.item;
1524 if((item.placement && !ptr.inserted) || ITEM_DISABLED(item)) continue;
1525 if(ITEM_HOTKEY(item) == key || (ITEM_HOTKEY(item) | Key { alt = true }) == key)
1527 if(!item.isDivider && !item.subMenu)
1529 if(MenuItemSelection(menu, ptr, key))
1532 else if(item.subMenu)
1535 MenuPopupSelected();
1542 if(result && isMenuBar && pressed)
1545 // Interim menus: the slaves are the ones with the focus
1546 if(result && INTERIM_MENU)
1548 Window master = this.master;
1549 if(eClass_IsDerived(master._class, _class))
1551 if(!master.OnKeyHit(key, ch))
1559 bool OnLoadGraphics()
1561 Font font = fontObject;
1562 int maxW = 0, maxH = 0;
1567 background = isMenuBar ? menuBarColor : (menu ? menu.color : popupMenuColor);
1568 FontExtent = Display::FontExtent;
1573 // Default width & height for merging menus into menu bars
1576 FontExtent(display, font, "W",1, &maxW, &maxH);
1577 if(!guiApp.textMode)
1581 for(ptr = menu.items.first; ptr; ptr = ptr.next)
1583 MenuItem item = ptr.item;
1584 if(item.placement) continue; //&& !ptr.inserted) continue;
1591 FontExtent(display, font,ITEM_TEXT(item),strlen(ITEM_TEXT(item)),&width, &height);
1592 if(strstr(ITEM_TEXT(item), "\t"))
1595 if(item.subMenu) width += 20;
1596 if(!guiApp.textMode)
1601 if(!guiApp.textMode)
1602 height = DIVIDER_HEIGHT;
1607 if(width > maxW) maxW = width;
1608 if(height > maxH) maxH = height;
1610 totalHeight += height;
1612 if(item.bitmaps[0]) AddResource(item.bitmaps[0]);
1613 if(item.bitmaps[1]) AddResource(item.bitmaps[1]);
1614 if(item.bitmaps[2]) AddResource(item.bitmaps[2]);
1616 maxW += menu.hasMargin ? 32 : 24;
1619 FontExtent(display, font,menu.text,strlen(menu.text),&width, &height);
1620 if(width > maxW) maxW = width;
1621 if(height > maxH) maxH = height;
1627 if(rw < maxW) rw = maxW;
1628 if(rh < maxH) rh = maxH;
1633 bool OnResizing(int * w, int * h)
1635 Window master = this.master;
1636 Window masterMenuBar = master.menuBar;
1637 if(this != masterMenuBar)
1641 *h = Max(*h, rh + 2);
1643 *h = Max(*h, totalHeight + 2);
1645 if(this != masterMenuBar)
1655 bool OnMoving(int *x, int *y, int w, int h)
1659 Window parent = this.parent;
1662 if(*y + h > parent.clientSize.h)
1664 PopupMenu master = (PopupMenu)this.master;
1666 if(eClass_IsDerived(master._class, _class))
1668 if(master.isMenuBar)
1669 *y -= master.size.h;
1671 *y += h - clientSize.h + rh;
1675 *x = Min(*x, ((parent == guiApp.desktop && guiApp.virtualScreen.w) ? guiApp.virtualScreen.w : parent.clientSize.w) - w);
1679 *x = Min(*x, parent.size.w - w);
1680 *y = Min(*y, parent.size.h - h);
1684 *x = Min(*x, parent.clientSize.w - w);
1685 *y = Min(*y, parent.clientSize.h - h);
1687 if(parent == guiApp.desktop)
1690 *x = Max(*x, guiApp.virtualScreenPos.x);
1691 *y = Max(*y, guiApp.virtualScreenPos.y);
1703 bool OnMouseMove(int mx, int my, Modifiers mods)
1705 int selectedX, selectedY;
1707 ItemPtr selected = FindSelected(mx, my, &selectedX, &selectedY);
1709 if((!mods.isSideEffect || !this.selected) && (/*selected && */
1710 selected != this.selected && (!selected || !ITEM_DISABLED(selected.item)) && (selected || !keyboardFocus)))
1712 if(!isMenuBar || pressed)
1714 bool pressed = this.pressed;
1716 if(firstSlave) firstSlave.Destroy(0);
1718 this.selected = selected;
1722 Menu childMenu = selected.item.subMenu;
1724 this.pressed = pressed;
1726 if(this.selected.item.subMenu)
1727 MenuPopupChild(selectedX, position.y + selectedY, childMenu);
1730 keyboardFocus = true;
1734 this.selected = selected;
1740 bool OnLeftButtonDown(int x, int y, Modifiers mods)
1744 Time t = GetTime(), u = unpressedTime;
1745 // Had to boost this to 0.1 for Windows Basic / XP theme / Remote Desktop
1746 // Aero & Classic were fast enough for 0.01
1747 if(GetTime() - unpressedTime < 0.1)
1751 int selectedX, selectedY;
1752 if(!mods.isActivate && !pressed)
1755 keyboardFocus = true;
1757 OnMouseMove(x,y, mods);
1761 if(firstSlave) firstSlave.Destroy(0);
1762 selected = FindSelected(x, y, &selectedX, &selectedY);
1766 //keyboardFocus = false;
1775 keyboardFocus = false;
1776 if(firstSlave) firstSlave.Destroy(0);
1782 keyboardFocus = true;
1784 OnMouseMove(x, y, mods);
1788 else //if(!INTERIM_MENU) // Why was this commented out?
1790 if(!(x >= 0 && y >= 0 && x < clientSize.w && y < clientSize.h))
1792 MenuDestroyMasters(false);
1799 bool OnRightButtonDown(int x, int y, Modifiers mods)
1801 if(x >= 0 && y >= 0 && x < clientSize.w && y < clientSize.h)
1805 Window master = this.master;
1806 Window activeClient = master.activeClient;
1807 if(activeClient.state == maximized)
1808 activeClient.ShowSysMenu(absPosition.x + x, absPosition.y + y);
1814 bool OnLeftButtonUp(int mx, int my, Modifiers mods)
1816 if(mx >= 2 /*0*/ && my >= 0 && mx < clientSize.w && my < clientSize.h)
1818 Menu menu = this.menu;
1822 if(!isMenuBar && menu)
1825 for(ptr = menu.items.first; ptr; ptr = ptr.next)
1827 MenuItem item = ptr.item;
1828 if(item.placement) continue; //&& !ptr.inserted) continue;
1829 if(my >= y && my < y + rh && !item.isDivider)
1837 y += (item.isDivider) ? DIVIDER_HEIGHT : rh;
1841 if(!isMenuBar || pressed)
1845 if(!selected.item.isDivider && !selected.item.subMenu)
1850 keyboardFocus = false;
1853 if(MenuItemSelection(menu, selected, Key { modifiers = mods }))
1860 keyboardFocus = false;
1861 if(firstSlave) firstSlave.Destroy(0);
1868 OnRightButtonUp = OnLeftButtonUp;
1870 bool OnLeftDoubleClick(int x, int y, Modifiers mods)
1874 int selectedX, selectedY;
1875 ItemPtr selected = FindSelected(x, y, &selectedX, &selectedY);
1878 Window master = this.master;
1879 Window activeClient = master.activeClient;
1880 // TOFIX: Fix need for a cast here...
1881 while(activeClient && !((BorderBits)activeClient.borderStyle).fixed)
1882 activeClient = activeClient.activeClient;
1883 if(activeClient && activeClient.state == maximized)
1884 activeClient.SetState(normal, false, mods);
1890 bool OnMouseLeave(Modifiers mods)
1892 if(!pressed && !firstSlave)
1901 bool OnActivate(bool active, Window swap, bool * goOnWithActivation, bool direct)
1909 PopupMenu master = (PopupMenu)this.master;
1910 if(eClass_IsDerived(master._class, _class) && master.isMenuBar) // && swap != master && swap && swap.master != master)
1913 master.unpressedTime = GetTime();
1914 master.pressed = false;
1915 master.selected = null;
1916 master.keyboardFocus = false;
1917 // master.Update(null);
1919 // TOFIX: Redraw bug here without this...
1920 master.master.Update(null);
1928 bool destroy = true;
1932 for(master = swap.master; master; master = master.master)
1941 for(master = this.master; master; master = master.master)
1944 if(eClass_IsDerived(master._class, _class))
1951 if(MenuDestroyMasters(false))
1958 // With new activation code this is not required anymore (double effect if there)
1964 unpressedTime = GetTime();
1967 keyboardFocus = false;
1968 if(firstSlave) firstSlave.Destroy(0);
1988 if(firstSlave) firstSlave.Destroy(0);
1990 Move(position.x, position.y, size.w, size.h);
1994 if(interim || isMenuBar)
2001 get { return menu; }
2003 property bool isMenuBar { set { isMenuBar = value; } };
2004 property bool focus { get { return keyboardFocus; } };