adf316fcca3b11576279b1b4d070d3c09e56436e
[sdk] / ecere / src / gui / controls / Menu.ec
1 namespace gui::controls;
2
3 import "Window"
4
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 };
9
10 class ItemPtr : struct
11 {
12    ItemPtr prev, next;
13    MenuItem item, oldItem;
14    InsertedFlags inserted;
15    Window master;
16 };
17
18 #define ITEM_DISABLED(i)   ((i).disabled || ((i).subMenu && !(i).subMenu.items.count))
19
20 class InsertedFlags { bool deleteLink:1, deleteItem:1, cleanItem:1, placed:1; };
21
22 #define ITEM_TEXT(item)    (item.subMenu ? item.subMenu.text : item.text)
23 #define ITEM_HOTKEY(item)  (item.subMenu ? item.subMenu.hotKey : item.hotKey)
24
25 #define DIVIDER_HEIGHT  (guiApp.textMode ? textCellH : 8)
26
27 #define INTERIM_MENU (isMenuBar || interim)
28 //#define INTERIM_MENU interim
29
30 static int strcmpTillTab(char * a, char * b)
31 {
32    if(a && !b) return 1;
33    else if(b && !a) return -1;
34    else
35    {
36       int c;
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;
42       return 0;
43    }
44 }
45
46 public class MenuItem
47 {
48 public:
49    property Menu parent
50    {
51       set
52       {
53          if(menu != value)
54          {
55             if(menu)
56                menu.RemoveItem(this);
57             if(value)
58                value.AddItem(this);
59          }
60       }
61    };
62    property char * text
63    {
64       set
65       {
66          if(copyText)
67          {
68             delete text;
69             text = CopyString(value);
70          }
71          else
72             text = value;
73
74          manualAccelText = (value && strchr(value, '\t'));
75
76          if(accelerator && !manualAccelText)
77             property::accelerator = accelerator;
78       }
79    };
80    property Key hotKey { set { hotKey = value; } };
81    property Key accelerator
82    {
83       set
84       {
85          accelerator = value;
86
87          if(!manualAccelText && text)
88          {
89             char accelString[50] = "\t";
90             char * newText;
91             char * tabPos;
92             int length = 0;
93
94             if(value.ctrl)  strcat(accelString, $"Ctrl+");
95             if(value.alt)   strcat(accelString, $"Alt+");
96             if(value.shift) strcat(accelString, $"Shift+");
97
98             if(value.code == k0)
99                strcat(accelString, "0");
100             else if(value.code >= k1 && value.code <= k9)
101             {
102                accelString[strlen(accelString)] = '1' + (char)(value.code - k1);
103                accelString[strlen(accelString)+1] = 0;
104             }
105             else
106             {
107                Key accel = value.code;
108                bool needClass = false;
109                char tempString[50];
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]);
114             }
115
116             tabPos = strchr(text, '\t');
117             if(tabPos)
118                length = tabPos - text;
119             else
120                length = strlen(text);
121
122             newText = new char[length+strlen(accelString)+1];
123             memcpy(newText, text, length);
124             newText[length] = 0;
125             strcat(newText, accelString);
126             if(copyText) delete text;
127             text = newText;
128             copyText = true;
129          }
130       }
131    };
132
133    property bool checked
134    {
135       set
136       {
137          checked = value;
138          if(menu && radio && value)
139          {
140             // Find the group
141             ItemPtr groupFirst = menu.items.first;
142             ItemPtr otherItemPtr;
143             for(otherItemPtr = menu.items.first; otherItemPtr; otherItemPtr = otherItemPtr.next)
144             {
145                MenuItem otherItem = otherItemPtr.item;
146                if(otherItem.isDivider)
147                   groupFirst = otherItemPtr.next;
148                else if(!otherItem.placement)
149                {
150                   if(otherItem == this)
151                      break;
152                }
153             }
154             for(otherItemPtr = groupFirst; otherItemPtr; otherItemPtr = otherItemPtr.next)
155             {
156                MenuItem otherItem = otherItemPtr.item;
157                if(otherItem.isDivider)
158                   break;
159                else if(!otherItem.placement && otherItem.radio && otherItem != this)
160                   otherItem.checked = false;
161             }
162          }
163          // Should callback be called here? guess not ;)
164       }
165       get { return checked; }
166    };
167    property bool disabled { set { if(this) disabled = value; } };
168    property bool checkable { set { checkable = value; } };
169    property bool isRadio { set { radio = value; } };
170
171    property uint64 id { set { id = value; } get { return id; } };
172    property BitmapResource bitmap
173    {
174       set
175       {
176          delete bitmaps[0];
177          delete bitmaps[1];
178          delete bitmaps[2];
179          bitmaps[0] = value;
180          bitmaps[1] = value ? (value.alphaBlend ? value : { fileName = value.fileName, monochrome = true }) : null;
181          bitmaps[2] = value ? { fileName = value.fileName, grayed = true } : null;
182          if(value)
183          {
184             incref bitmaps[0];
185             incref bitmaps[1];
186             incref bitmaps[2];
187          }
188       }
189       get { return bitmaps[0]; }
190    };
191    property bool copyText
192    {
193       set
194       {
195          if(value)
196          {
197             if(text && !copyText)
198                text = CopyString(ITEM_TEXT(this));
199          }
200          else
201          {
202             if(text && copyText)
203                delete text;
204          }
205          copyText = value;
206       }
207    };
208    property bool bold { set { bold = value; } get { return bold; } };
209
210    virtual bool Window::NotifySelect(MenuItem selection, Modifiers mods);
211
212 private:
213    bool isDivider;
214    bool placement;
215
216    uint64 id;
217    Key hotKey;
218    Key accelerator;
219    char * text;
220    BitmapResource bitmaps[3];
221    bool checkable, radio;
222    bool checked;
223    bool disabled;
224    Menu menu;
225    Menu subMenu;
226    bool copyText;
227    bool manualAccelText;
228    bool bold;
229
230    ~MenuItem()
231    {
232       if(copyText)
233          // delete ITEM_TEXT(this);
234          delete text;
235       delete subMenu;
236       delete bitmaps[0];
237       delete bitmaps[1];
238       delete bitmaps[2];
239    }
240 };
241
242 public class MenuDivider : MenuItem
243 {
244    MenuDivider()
245    {
246       isDivider = true;
247    }
248
249 //   property Menu parent { set {} };
250 };
251
252 public class MenuPlacement : MenuItem
253 {
254    MenuPlacement()
255    {
256       placement = true;
257    }
258
259 public:
260 /*
261    property Menu parent { set {} };
262    property char * text { set {} };
263    property Key hotKey { set {} };
264 */
265 };
266
267 public class Menu
268 {
269    class_no_expansion
270
271    int OnCompare(Menu menu)
272    {
273       return (this != null) != (menu != null);
274    }
275 public:
276    void AddItem(MenuItem item)
277    {
278       if(item)
279       {
280          ItemPtr ptr;
281
282          for(ptr = items.first; ptr; ptr = ptr.next)
283          {
284             MenuItem check = ptr.item;
285             if(check.placement)
286             {
287                if(!strcmpTillTab(ITEM_TEXT(check), ITEM_TEXT(item)))
288                   break;
289             }
290          }
291          if(!ptr)
292          {
293             ptr = ItemPtr { };
294             items.Add(ptr);
295          }
296          else
297          {
298             ptr.inserted = InsertedFlags { placed = true };
299             ptr.oldItem = ptr.item;
300          }
301
302          ptr.item = item;
303
304          incref item;
305
306          if(!item.placement)
307             itemCount++;
308
309          // TOCHECK:
310          item.menu = this;
311       }
312    }
313
314    void RemoveItem(MenuItem item)
315    {
316       if(item.menu == this)
317       {
318          ItemPtr ptr;
319          for(ptr = items.first; ptr; ptr = ptr.next)
320             if(ptr.item == item)
321             {
322                if(ptr.inserted.placed)
323                {
324                   ptr.item = ptr.oldItem;
325                   ptr.oldItem = null;
326                }
327                if(!item.placement)
328                   itemCount--;
329                item.menu = null;
330                if(!ptr.inserted.placed)
331                   items.Delete(ptr);
332                else
333                   ptr.inserted = 0;
334                delete item;
335                break;
336             }
337       }
338    }
339
340    void AddSubMenu(Menu subMenu)
341    {
342       if(subMenu)
343       {
344          MenuItem menuItem { };
345          ItemPtr ptr { item = menuItem };
346          items.Add(ptr);
347
348          incref menuItem;
349
350          itemCount++;
351          // TOCHECK:
352          menuItem.menu = this;
353
354          if(subMenu)
355          {
356             menuItem.subMenu = subMenu;
357             incref subMenu;
358          }
359       }
360    }
361
362    void AddDynamic(MenuItem addedItem, Window master, bool persistent)
363    {
364       if(addedItem)
365       {
366          ItemPtr ptr = null, oldItemPtr;
367
368          for(oldItemPtr = items.first; oldItemPtr; oldItemPtr = oldItemPtr.next)
369          {
370             if((oldItemPtr.item.subMenu || oldItemPtr.item.placement) && !strcmpTillTab(ITEM_TEXT(oldItemPtr.item), ITEM_TEXT(addedItem)))
371             {
372                MenuItem oldItem = oldItemPtr.item;
373                if(!oldItem.placement)
374                {
375                   oldItem.subMenu.Merge(addedItem.subMenu, true, master);
376                }
377                // If sub menu already has a master...
378                else
379                {
380                   oldItemPtr.inserted = InsertedFlags { cleanItem = true };
381                   if(!oldItemPtr.oldItem)
382                      oldItemPtr.oldItem = oldItem;
383                   oldItemPtr.item = addedItem;
384                }
385                ptr = oldItemPtr;
386                break;
387             }
388          }
389
390          if(!ptr)
391          {
392             ptr = ItemPtr { };
393             items.Add(ptr);
394             if(!persistent)
395                ptr.inserted = InsertedFlags { deleteLink = true, deleteItem = true };
396          }
397          else if(!persistent)
398             ptr.inserted = InsertedFlags { cleanItem = true, deleteItem = true };
399
400          ptr.item = addedItem;
401          ptr.master = master;
402          incref addedItem;
403          itemCount++;
404          addedItem.menu = this;
405       }
406    }
407
408    MenuItem FindItem(bool (* Window::notifySelect)(MenuItem selection, Modifiers mods), uint64 id)
409    {
410       ItemPtr ptr;
411
412       for(ptr = items.first; ptr; ptr = ptr.next)
413       {
414          MenuItem item = ptr.item;
415          if(item.subMenu)
416          {
417             MenuItem subItem = item.subMenu.FindItem(notifySelect, id);
418             if(subItem) return subItem;
419          }
420          else if(!item.isDivider && !item.placement)
421          {
422             if(item.id == id && item.NotifySelect == notifySelect)
423                return item;
424          }
425       }
426       return null;
427    }
428
429    void Clear()
430    {
431       ItemPtr ptr;
432       while((ptr = items.first))
433       {
434          delete ptr.item;
435          if(ptr.inserted.cleanItem || ptr.inserted.placed)
436          {
437             ptr.item = ptr.oldItem;
438             ptr.oldItem = null;
439             delete ptr.item;
440          }
441          items.Delete(ptr);
442       }
443    }
444
445    void Merge(Menu menuBeingMerged, bool menuBar, Window window)
446    {
447       bool separated = false;
448       ItemPtr beingMergedItemPtr;
449
450       for(beingMergedItemPtr = menuBeingMerged.items.first; beingMergedItemPtr; beingMergedItemPtr = beingMergedItemPtr.next)
451       {
452          MenuItem beingMergedItem = beingMergedItemPtr.item;
453          ItemPtr mergeIntoItemPtr = null;
454
455          if(!beingMergedItem) continue;
456          if(beingMergedItem.subMenu)
457          {
458             for(mergeIntoItemPtr = items.first; mergeIntoItemPtr; mergeIntoItemPtr = mergeIntoItemPtr.next)
459             {
460                if((mergeIntoItemPtr.item.subMenu || mergeIntoItemPtr.item.placement) && !strcmpTillTab(ITEM_TEXT(mergeIntoItemPtr.item), ITEM_TEXT(beingMergedItem)))
461                {
462                   MenuItem mergeIntoItem = mergeIntoItemPtr.item;
463                   if(!beingMergedItem.placement || beingMergedItemPtr.inserted)
464                   {
465                      if(!mergeIntoItem.placement && !mergeIntoItemPtr.inserted.cleanItem) // Added this last check for ActiveChild overriding ActiveClient's menu
466                      {
467                         mergeIntoItem.subMenu.Merge(beingMergedItem.subMenu, menuBar, window);
468                      }
469                      // If sub menu already has a master...
470                      else
471                      {
472                         mergeIntoItemPtr.inserted = InsertedFlags { cleanItem = true };
473                         if(!mergeIntoItemPtr.oldItem)
474                            mergeIntoItemPtr.oldItem = mergeIntoItem;
475                         mergeIntoItemPtr.item = beingMergedItem;
476                      }
477                      mergeIntoItemPtr.master = window;
478                      itemCount++;
479                   }
480                   break;
481                }
482             }
483          }
484          else if(!beingMergedItem.isDivider)
485          {
486             for(mergeIntoItemPtr = items.first; mergeIntoItemPtr; mergeIntoItemPtr = mergeIntoItemPtr.next)
487             {
488                MenuItem mergeIntoItem = mergeIntoItemPtr.item;
489                if(/*!mergeIntoItem.subMenu && /-*mergeIntoItem.placement && !mergeIntoItemPtr.inserted && */!strcmpTillTab(ITEM_TEXT(mergeIntoItem), ITEM_TEXT(beingMergedItem)))
490                {
491                   //if(!beingMergedItem.placement || beingMergedItemPtr.inserted)
492                   {
493                      mergeIntoItemPtr.inserted = InsertedFlags { cleanItem = true };
494                      if(!mergeIntoItemPtr.oldItem)
495                         mergeIntoItemPtr.oldItem = mergeIntoItem;
496                      mergeIntoItemPtr.item = beingMergedItem;
497                      mergeIntoItemPtr.master = beingMergedItemPtr.master ? beingMergedItemPtr.master : window;
498                      itemCount++;
499                   }
500                   break;
501                }
502             }
503          }
504          if(!mergeIntoItemPtr)
505          {
506             if(beingMergedItem.placement && !beingMergedItemPtr.inserted)
507             {
508                // Simply add the placement at the end
509                mergeIntoItemPtr = ItemPtr { };
510                mergeIntoItemPtr.inserted = InsertedFlags { deleteLink = true, cleanItem = true };
511                mergeIntoItemPtr.item = beingMergedItem;
512                mergeIntoItemPtr.master = beingMergedItemPtr.master ? beingMergedItemPtr.master : window;
513                items.Add(mergeIntoItemPtr);
514                itemCount++;
515             }
516             else
517             {
518                ItemPtr previous = items.last;
519                if(menuBar)
520                {
521                   // If it is a menu bar, add the item before the first divider
522                   for(previous = items.first; previous; previous = previous.next)
523                      if(previous.item.isDivider && !previous.inserted)     // Added previous.inserted check
524                      {
525                         previous = previous.prev;
526                         break;
527                      }
528                }
529                else
530                {
531                   if(previous && !previous.item.isDivider && !separated)
532                   {
533                      ItemPtr ptr
534                      {
535                         item = MenuDivider { },
536                         inserted = InsertedFlags { deleteLink = true, deleteItem = true }
537                      };
538                      items.Insert(previous, ptr);
539                      previous = ptr;
540                      separated = true;
541                      itemCount++;
542                   }
543                }
544
545                if(!beingMergedItem.isDivider || !previous || (previous.item && !previous.item.isDivider))
546                {
547                   mergeIntoItemPtr = ItemPtr { };
548                   items.Insert(previous, mergeIntoItemPtr);
549                   mergeIntoItemPtr.inserted = InsertedFlags { deleteLink = true, cleanItem = true };
550                   mergeIntoItemPtr.item = beingMergedItem;
551                   mergeIntoItemPtr.master = beingMergedItemPtr.master ? beingMergedItemPtr.master : window;
552                   itemCount++;
553                }
554             }
555          }
556       }
557    }
558
559    void Clean(Window window)
560    {
561       ItemPtr ptr, next;
562       for(ptr = items.first; ptr; ptr = next)
563       {
564          MenuItem item = ptr.item;
565          next = ptr.next;
566
567          if(ptr.inserted.cleanItem)
568          {
569             ptr.item = ptr.oldItem;
570             ptr.oldItem = null;
571          }
572          else if(item.subMenu)
573             item.subMenu.Clean(window);
574
575          if(ptr.inserted.deleteItem)
576             delete item;
577
578          if(ptr.inserted.deleteLink || ptr.inserted.cleanItem)
579             itemCount--;
580          if(ptr.inserted.deleteLink)
581             items.Delete(ptr);
582          else
583          {
584             ptr.inserted.deleteLink = false;
585             ptr.inserted.cleanItem = false;
586             ptr.inserted.deleteItem = false;
587             ptr.master = window;
588          }
589       }
590    }
591
592    Menu FindMenu(char * name)
593    {
594       ItemPtr ptr;
595
596       for(ptr = items.first; ptr; ptr = ptr.next)
597       {
598          MenuItem item = ptr.item;
599
600          if(item.subMenu && item.subMenu.text && !strcmpTillTab(item.subMenu.text, name))
601             return item.subMenu;
602       }
603       return null;
604    }
605
606    property Menu parent { set { if(value) value.AddSubMenu(this); } };
607    property char * text { set { text = value; /* CopyString(value);*/ } };
608    property Key hotKey { set { hotKey = value; } };
609    property bool hasMargin { set { hasMargin = value; } };
610
611 private:
612    OldList items;
613    char * text;
614    int hotKey;
615    int w, h;
616    Color color;
617    int itemHeight;
618    int itemCount;
619    bool mergeClients;
620    bool hasMargin;
621
622    Menu()
623    {
624       color = popupMenuColor;
625    }
626
627    ~Menu()
628    {
629       Clean(null);
630       Clear();
631    }
632 };
633
634 public class PopupMenu : Window
635 {
636    class_property(icon) = "<:ecere>controls/menu.png";
637    bool isMenuBar;
638    int rw, rh;
639    int totalHeight;
640    Menu menu;
641    ItemPtr selected;
642    bool pressed;
643    bool altDown;
644    bool keyboardFocus;
645    bool mouseInput;
646    Time unpressedTime;
647
648    void (* FontExtent)(Display display, Font font, char * text, int len, int * width, int * height);
649
650    FontResource boldFont { faceName = font.faceName, font.size, bold = true, window = this };
651    BitmapResource subArrow { fileName = "<:ecere>elements/arrowRight.png", window = this };
652    BitmapResource whiteSubArrow { fileName = "<:ecere>elements/arrowRight.png", monochrome = true, window = this };
653    BitmapResource disabledSubArrow { fileName = "<:ecere>elements/arrowRight.png", grayed = true, window = this };
654
655    borderStyle = bevel;
656    interim = true;
657
658    ~PopupMenu()
659    {
660       if(menu) delete menu;
661    }
662
663    // Utility Functions
664    bool MenuDestroyMasters(bool unselect)
665    {
666       bool result;
667       PopupMenu window = this, master;
668
669       for(; (master = (PopupMenu)window.master); window = master)
670       {
671          if(!eClass_IsDerived(master._class, _class) || master.isMenuBar)
672             break;
673       }
674
675       if(eClass_IsDerived(master._class, _class) && master.isMenuBar)
676       {
677          master.pressed = false;
678          if(unselect)
679          {
680             master.keyboardFocus = false;
681             master.selected = null;
682          }
683          master.Update(null);
684       }
685       result = window.Destroy(0);
686       // This looks like a hack...
687       RestoreCaret();
688       return result;
689    }
690
691    bool MenuGoToPrevItem()
692    {
693       ItemPtr selected, current = this.selected;
694       for(selected = (current && current.prev) ? current.prev : menu.items.last;
695             selected &&
696             (selected.item.isDivider || selected.item.placement || ITEM_DISABLED(selected.item)) &&
697             selected != current;
698             selected = selected.prev ? selected.prev : menu.items.last)
699       {
700          if(!current) current = selected; // Endless loop with no previously selected popups
701       }
702       this.selected = selected;
703       return selected && selected != current;
704    }
705
706    bool MenuGoToNextItem()
707    {
708       ItemPtr selected, current = this.selected;
709       for(selected = (current && current.next) ? current.next : menu.items.first;
710             selected &&
711             (selected.item.isDivider || selected.item.placement || ITEM_DISABLED(selected.item)) &&
712             selected != current;
713             selected = selected.next ? selected.next : menu.items.first)
714       {
715          if(!current) current = selected; // Endless loop with no previously selected popups
716       }
717       this.selected = selected;
718       return selected && selected != current;
719    }
720
721    void MenuPopupChild(int x, int y, Menu childMenu)
722    {
723       if(childMenu)
724       {
725          RestoreCaret();
726          if(childMenu.itemCount)
727          {
728             PopupMenu child { master = this, menu = childMenu };
729
730             if(INTERIM_MENU)
731             {
732                Window parent = this.parent;
733                Window desktop = guiApp.desktop;
734
735                x += parent.absPosition.x + parent.clientStart.x - desktop.position.x;
736                y += parent.absPosition.y + parent.clientStart.y - desktop.position.y;
737    /*
738                x += parent.absPosition.x + parent.clientStart.x;
739                y += parent.absPosition.y + parent.clientStart.y;
740    */
741                child.parent = desktop;
742             }
743             else
744             {
745                child.stayOnTop = true;
746                child.parent = parent;
747                child.interim = false;
748             }
749             child.position = Point { x, y };
750             // child.id = id;
751             // child.displayDriver = "GDI";
752             child.Create();
753          }
754       }
755    }
756
757    bool MenuPopupSelected()
758    {
759       if(isMenuBar)
760       {
761          int selectedX = guiApp.textMode ? 0 : 2;
762
763          if(selected && selected.item)     // Why was this null from OnKeyHit?
764          {
765             ItemPtr selected = this.selected, ptr;
766             bool helpBreak = false;
767             Window parent = this.parent;
768             Window activeClient = parent.activeClient;
769             bool systemButtons = activeClient && activeClient.state == maximized;
770
771             keyboardFocus = true;
772             pressed = true;
773
774             //if(!INTERIM_MENU)
775             if(firstSlave)
776                firstSlave.Destroy(0);
777
778             for(ptr = menu.items.first; ptr; ptr = ptr.next)
779             {
780                MenuItem item = ptr.item;
781                int len;
782                if(item.placement) continue; //&& !ptr.inserted) continue;
783
784                if(ptr == selected)
785                {
786                   Menu childMenu = item.subMenu;
787
788                   this.selected = ptr;
789
790                   if(selected.item.subMenu)
791                      MenuPopupChild(selectedX, 0, childMenu);
792
793                   keyboardFocus = true;
794                   pressed = true;
795                   this.selected = ptr;
796
797                   Update(null);
798                   return false; // true
799                }
800                if(item.isDivider)
801                {
802                   if(!helpBreak)
803                   {
804                      ItemPtr nextPtr;
805                      int breakX = clientSize.w + 2 - (systemButtons ? 48 : 0);
806                      for(nextPtr = ptr.next; nextPtr; nextPtr = nextPtr.next)
807                      {
808                         MenuItem nextItem = nextPtr.item;
809                         if(!nextItem.isDivider && ITEM_TEXT(nextItem))
810                         {
811                            int len;
812                            FontExtent(display, fontObject, ITEM_TEXT(nextItem), strlen(ITEM_TEXT(nextItem)), &len, null);
813                            breakX -= len + 16;
814                         }
815                      }
816                      if(selectedX < breakX) selectedX = breakX;
817                      helpBreak = true;
818                   }
819                }
820                else if(ITEM_TEXT(item))
821                {
822                   FontExtent(display, fontObject, ITEM_TEXT(item), strlen(ITEM_TEXT(item)), &len, null);
823                   selectedX += len + 16;
824                }
825             }
826             Update(null);
827          }
828       }
829       else
830       {
831          if(selected && selected.item.subMenu)
832          {
833             Menu childMenu = selected.item.subMenu;
834             int y = 0;
835             int selectedY = 0;
836             ItemPtr ptr;
837
838             for(ptr = menu.items.first; ptr; ptr = ptr.next)
839             {
840                MenuItem item = ptr.item;
841
842                if(item.placement) continue; //&& !ptr.inserted) continue;
843                if(selected == ptr)
844                {
845
846                   selectedY = y;
847                   break;
848                }
849                y += (item.isDivider) ? DIVIDER_HEIGHT : rh;
850             }
851             if(ptr)
852             {
853                PopupMenu slave = (PopupMenu)firstSlave;
854                if(!slave || slave.menu != childMenu)
855                {
856                   if(firstSlave) firstSlave.Destroy(0);
857                   MenuPopupChild(position.x + size.w, position.y + selectedY, childMenu);
858                   Update(null);
859                   return true;
860                }
861             }
862          }
863       }
864       return false;
865    }
866
867    bool MenuItemSelection(Menu parentMenu, ItemPtr selectionPtr, Key key)
868    {
869       MenuItem selection = selectionPtr.item;
870       if(!ITEM_DISABLED(selection))
871       {
872          Window master = this;
873
874          if(!isMenuBar)
875             master = master.master;
876
877          if(selectionPtr.master)
878             master = selectionPtr.master;
879          while(eClass_IsDerived(master._class, _class) && master.master)
880             master = master.master;
881
882          if(selection.checkable)
883             selection.checked = !selection.checked;
884          else if(selection.radio)
885          {
886             if(selection.checked) return false;
887             selection.checked = !selection.checked;
888          }
889
890          if(!isMenuBar)
891             MenuDestroyMasters(true);
892
893          /*return */selection.NotifySelect(master, selection, key.modifiers);
894          return true;
895       }
896       return false;
897    }
898
899    bool CheckAccelerators(Menu menu, Key key)
900    {
901       ItemPtr ptr;
902
903       for(ptr = menu.items.first; ptr; ptr = ptr.next)
904       {
905          MenuItem item = ptr.item;
906          if(item.subMenu)
907          {
908             if(!CheckAccelerators(item.subMenu, key))
909                return false;
910          }
911          else if(!item.isDivider)
912          {
913             if(item.accelerator == key)
914             {
915                if(MenuItemSelection(menu, ptr, key))
916                   return false;
917             }
918          }
919       }
920       return true;
921    }
922
923    ItemPtr FindSelected(int mx, int my, int * selectedX, int * selectedY)
924    {
925       Menu menu = this.menu;
926       // Mouse moved inside menu
927       ItemPtr selected = null;
928
929       *selectedX = 0;
930       *selectedY = 0;
931
932       if(isMenuBar && menu)
933       {
934          ItemPtr ptr;
935          int x = 0;
936          int len;
937          bool helpBreak = false;
938          Window parent = this.parent;
939          Window activeClient = parent.activeClient;
940          bool systemButtons = activeClient && activeClient.state == maximized;
941
942          for(ptr = menu.items.first; ptr; ptr = ptr.next)
943          {
944             MenuItem item = ptr.item;
945             if(item.placement) continue; //&& !ptr.inserted) continue;
946             if(item.isDivider)
947             {
948                if(!helpBreak)
949                {
950                   ItemPtr nextPtr;
951
952                   int breakX = clientSize.w - (systemButtons ? 48 : 0);
953                   for(nextPtr = ptr.next; nextPtr; nextPtr = nextPtr.next)
954                   {
955                      MenuItem nextItem = nextPtr.item;
956                      if(!nextItem.isDivider && ITEM_TEXT(nextItem))
957                      {
958                         int len;
959                         FontExtent(display, fontObject, ITEM_TEXT(nextItem), strlen(ITEM_TEXT(nextItem)), &len, null);
960                         breakX -= len + 16;
961                      }
962                   }
963                   if(x < breakX) x = breakX;
964                   helpBreak = true;
965                }
966             }
967             else
968             {
969                char * text = ITEM_TEXT(item);
970                FontExtent(display, fontObject, text, text ? strlen(text) : 0, &len, null);
971                if((mx >= x - 16 && mx < x + len + 16))
972                {
973                   if(!ITEM_DISABLED(item))
974                      selected = ptr;
975                   if(guiApp.textMode)
976                      *selectedX = x;
977                   else
978                      *selectedX = x + 2;
979                   *selectedY = -position.y;
980                   break;
981                }
982                x += len + 16;
983             }
984          }
985       }
986       else if(menu)
987       {
988          ItemPtr ptr;
989          int y = 0;
990          for(ptr = menu.items.first; ptr; ptr = ptr.next)
991          {
992             MenuItem item = ptr.item;
993             if(item.placement) continue; //&& !ptr.inserted) continue;
994             if(mx >= 2 && (my >= y && my < y + rh) && !item.isDivider)
995             {
996                if(!ITEM_DISABLED(item))
997                   selected = ptr;
998                *selectedY = y;
999                *selectedX = position.x + size.w;
1000                break;
1001             }
1002             if(guiApp.textMode)
1003                y += textCellH;
1004             else
1005                y += (item.isDivider) ? DIVIDER_HEIGHT : rh;
1006          }
1007       }
1008       return selected;
1009    }
1010
1011    // Window Overrides
1012    void OnRedraw(Surface surface)
1013    {
1014       bool hasMargin = menu ? menu.hasMargin : false;
1015       int x = 0;
1016       int y = 0;
1017       int height;
1018       bool helpBreak = false;
1019       Window parent = this.parent;
1020       Window activeClient = parent.activeClient;
1021       bool systemButtons = activeClient && activeClient.state == maximized;
1022       int bitmapOffset = 0;
1023       bool hasBitmap = false;
1024       bool isRadio = false;
1025
1026       surface.TextFont(fontObject);
1027       surface.TextExtent(" ", 1, null, &height);
1028       surface.SetBackground(SELECTION_COLOR);
1029
1030       if(!isMenuBar)
1031       {
1032          if(menu)
1033          {
1034             ItemPtr ptr;
1035             for(ptr = menu.items.first; ptr; ptr = ptr.next)
1036             {
1037                if(ptr.item.bitmaps[0])
1038                {
1039                   hasBitmap = true;
1040                   break;
1041                }
1042             }
1043
1044             for(ptr = menu.items.first; ptr; ptr = ptr.next)
1045             {
1046                if(ptr.item.radio)
1047                {
1048                   isRadio = true;
1049                   break;
1050                }
1051             }
1052          }
1053
1054          if(guiApp.textMode)
1055             bitmapOffset = 16;
1056          else
1057             bitmapOffset = hasMargin ? 27 : (hasBitmap ? 18 : 12);
1058          if(hasBitmap && isRadio)
1059             bitmapOffset += 18;
1060       }
1061       else if(guiApp.textMode)
1062          bitmapOffset = 8;
1063       else
1064       {
1065          // Shiny gradient for menu bar
1066          ColorKey keys[2] =
1067          {
1068             { white, 0 },
1069             { popupMenuColor, 1 }
1070          };
1071          surface.Gradient(keys, sizeof(keys)/sizeof(ColorKey), 1, vertical, 0,0, clientSize.w-1, 7);
1072       }
1073
1074       if(menu)
1075       {
1076          ItemPtr ptr;
1077          for(ptr = menu.items.first; ptr; ptr = ptr.next)
1078          {
1079             MenuItem item = ptr.item;
1080             if(item.placement) continue; //&& !ptr.inserted) continue;
1081             if(!isMenuBar && selected == ptr)
1082             {
1083                surface.SetForeground(SELECTION_TEXT);
1084                if(guiApp.textMode)
1085                {
1086                   surface.TextOpacity(true);
1087                   surface.Area(0,y,clientSize.w-1,y+rh-1);
1088                }
1089                else
1090                   surface.Area(/*(hasMargin ? bitmapOffset : 0) +*/ 2,y,clientSize.w-3,y+rh);
1091             }
1092             else
1093             {
1094                surface.SetForeground(foreground);
1095                if(guiApp.textMode)
1096                   surface.TextOpacity(false);
1097             }
1098
1099             if(item.isDivider)
1100             {
1101                if(isMenuBar)
1102                {
1103                   if(!helpBreak)
1104                   {
1105                      ItemPtr nextPtr;
1106                      int breakX = clientSize.w - (systemButtons ? 48 : 0);
1107                      for(nextPtr = ptr.next; nextPtr; nextPtr = nextPtr.next)
1108                      {
1109                         MenuItem nextItem = nextPtr.item;
1110                         int len;
1111                         if(!nextItem.isDivider && ITEM_TEXT(nextItem))
1112                         {
1113                            surface.TextExtent(ITEM_TEXT(nextItem), strlen(ITEM_TEXT(nextItem)), &len, null);
1114                            breakX -= len + 16;
1115                         }
1116                      }
1117                      if(x < breakX) x = breakX;
1118                      helpBreak = true;
1119                   }
1120                }
1121                else
1122                {
1123                   if(guiApp.textMode)
1124                   {
1125                      surface.SetForeground(Color { 85, 85, 85 });
1126                      surface.DrawingChar(196);
1127                      surface.HLine(x + 2, x + rw - 5, y + (rh) / 2);
1128                      surface.DrawingChar(' ');
1129                   }
1130                   else
1131                   {
1132                      int start = x + hasMargin ? bitmapOffset : 2;
1133                      int end   = x + rw - (hasMargin ? 13 : 5);
1134                      surface.foreground = Color { 85, 85, 85 };
1135                      surface.HLine(start, end, y + (DIVIDER_HEIGHT) / 2);
1136                      surface.foreground = white;
1137                      surface.HLine(start, end, y + (DIVIDER_HEIGHT) / 2 + 1);
1138                   }
1139                }
1140             }
1141             else
1142             {
1143                if(selected == ptr && guiApp.textMode)
1144                {
1145                   surface.SetBackground(SELECTION_COLOR);
1146                   surface.SetForeground(SELECTION_TEXT /*background*/);
1147                   surface.TextOpacity(true);
1148                }
1149                if(guiApp.textMode)
1150                {
1151                   Interface::WriteKeyedTextDisabled(surface, x + bitmapOffset,
1152                      y + (rh - height)/2, ITEM_TEXT(item), ITEM_HOTKEY(item), ITEM_DISABLED(item));
1153                   if(item.checked)
1154                      surface.WriteText(x, y, "\373", 1);
1155                }
1156                else
1157                {
1158                   int textY = y + (rh - height)/2;
1159                   BitmapResource bitmap = item.disabled ? item.bitmaps[2] : ((selected == ptr) ? item.bitmaps[1] : item.bitmaps[0]);
1160                   if(!isMenuBar && bitmap)
1161                   {
1162                      Bitmap icon = bitmap.bitmap;
1163                      if(icon)
1164                         surface.Blit(icon, x + (isRadio ? 18 : 0) + (hasMargin ? 5 : 3), y + (rh - icon.height)/2, 0,0, icon.width, icon.height);
1165                   }
1166
1167                   if(item.bold)
1168                      surface.TextFont(boldFont.font);
1169                   else
1170                      surface.TextFont(fontObject);
1171
1172                   if(ITEM_DISABLED(item) && selected == ptr)
1173                   {
1174                      surface.SetForeground(formColor);
1175                      Interface::WriteKeyedText(surface, x + bitmapOffset + 5,
1176                         textY, ITEM_TEXT(item), ITEM_HOTKEY(item));
1177                   }
1178                   else
1179                      Interface::WriteKeyedTextDisabled(surface, x + bitmapOffset + 5,
1180                         textY, ITEM_TEXT(item), ITEM_HOTKEY(item), ITEM_DISABLED(item));
1181
1182                   // A nice vertical separation line
1183                   if(hasMargin && !isMenuBar)
1184                   {
1185                      surface.foreground = Color { 85, 85, 85 };
1186                      surface.VLine(clientArea.top, clientArea.bottom, x + bitmapOffset - 2);
1187                      surface.foreground = white;
1188                      surface.VLine(clientArea.top, clientArea.bottom, x + bitmapOffset - 1);
1189                   }
1190                   surface.foreground = foreground;
1191                   if(item.checked)
1192                   {
1193                      surface.DrawLine(x+5, y+9, x+8,y+12);
1194                      surface.DrawLine(x+5, y+10, x+8,y+13);
1195                      surface.DrawLine(x+8, y+12, x+12,y+4);
1196                      surface.DrawLine(x+8, y+13, x+12,y+5);
1197                   }
1198                }
1199             }
1200
1201             if(!isMenuBar)
1202             {
1203                // Draw little arrow
1204                if(item.subMenu)
1205                {
1206                   surface.SetForeground(foreground);
1207
1208                   if(guiApp.textMode)
1209                   {
1210                      surface.SetForeground((selected == ptr) ? (background) : (foreground));
1211                      surface.WriteText(clientSize.w-8, y+(rh - 8)/2, "\020", 1);
1212                   }
1213                   else
1214                   {
1215                      Bitmap arrow = (selected == ptr) ? whiteSubArrow.bitmap : subArrow.bitmap;
1216                      if(ITEM_DISABLED(ptr.item)) arrow = disabledSubArrow.bitmap;
1217
1218                      if(arrow)
1219                         surface.Blit(arrow, clientSize.w-14, y+(rh - 8)/2, 0,0, arrow.width, arrow.height);
1220                      /*
1221                      surface.VLine(y+(rh - 8)/2, y+(rh - 8)/2+7, clientSize.w-10);
1222                      surface.SetForeground(Color { 85, 85, 85 });
1223                      surface.DrawLine(clientSize.w-10, y+(rh - 8)/2, clientSize.w-4, y+rh / 2);
1224                      surface.SetForeground(formColor);
1225                      surface.DrawLine(clientSize.w-10, y+(rh - 8)/2+7, clientSize.w-4, y+rh / 2);
1226                      */
1227                   }
1228                }
1229                if(guiApp.textMode)
1230                   y += rh;
1231                else
1232                   y += (item.isDivider) ? DIVIDER_HEIGHT : rh;
1233             }
1234             else if(ITEM_TEXT(item) && !item.isDivider)
1235             {
1236                int len;
1237                surface.TextExtent(ITEM_TEXT(item), strlen(ITEM_TEXT(item)), &len, null);
1238                if(selected == ptr && !guiApp.textMode &&
1239                   (item.subMenu || selected))
1240                {
1241                   surface.Bevel(pressed, x, y, len+10, height + 6);
1242                }
1243                if(!item.isDivider)
1244                   x += len + 16;
1245             }
1246          }
1247       }
1248    }
1249
1250    bool OnKeyDown(Key key, unichar ch)
1251    {
1252       bool result = true;
1253       if(visible)
1254       {
1255          // Non-Interim menus: the slaves don't have focus... pass it down from menu bar
1256          if(!INTERIM_MENU)
1257          {
1258             if(!active && firstSlave)
1259             {
1260                if(!firstSlave.OnKeyDown(key, ch))
1261                   result = false;
1262             }
1263          }
1264
1265          switch(key)
1266          {
1267             case leftAlt:
1268             case rightAlt:
1269                if(!isMenuBar)
1270                {
1271                   altDown = false;
1272                   MenuDestroyMasters(true);
1273                }
1274                else
1275                {
1276                   altDown = true;
1277                }
1278                return false;
1279             default:
1280                altDown = false;
1281                if(key && menu)
1282                {
1283                   ItemPtr ptr;
1284
1285                   for(ptr = menu.items.first; ptr; ptr = ptr.next)
1286                   {
1287                      MenuItem item = ptr.item;
1288                      if((item.placement && !ptr.inserted) || ITEM_DISABLED(item)) continue;
1289                      if(((keyboardFocus || !isMenuBar) && ITEM_HOTKEY(item) == key) || (ITEM_HOTKEY(item) | Key { alt = true }) == key)
1290                      {
1291                         if(!item.isDivider && !item.subMenu)
1292                         {
1293                            if(MenuItemSelection(menu, ptr, key))
1294                               return false;
1295                         }
1296                         else if(item.subMenu)
1297                         {
1298                            selected = ptr;
1299                            MenuPopupSelected();
1300                            result = false;
1301                         }
1302                         break;
1303                      }
1304                   }
1305                   if(ch >= 32 && ch != 128 && !isMenuBar)
1306                      result = false;
1307                }
1308          }
1309
1310          // Interim menus: the slaves are the ones with the focus
1311          if(result && INTERIM_MENU)
1312          {
1313             Window master = this.master;
1314             if(eClass_IsDerived(master._class, _class))
1315             {
1316                if(!master.OnKeyDown(key, ch))
1317                   result = false;
1318             }
1319          }
1320       }
1321       return result;
1322    }
1323
1324    bool OnKeyUp(Key key, unichar ch)
1325    {
1326       switch(key)
1327       {
1328          case leftAlt:
1329          case rightAlt:
1330             if(isMenuBar && altDown)
1331             {
1332                if(!selected)
1333                {
1334                   MenuGoToNextItem();
1335                   keyboardFocus = true;
1336                   RestoreCaret();
1337                }
1338                else
1339                {
1340                   selected = null;
1341                   if(firstSlave) firstSlave.Destroy(0);
1342                   pressed = false;
1343                   keyboardFocus = false;
1344                   RestoreCaret();
1345                }
1346                Update(null);
1347                altDown = false;
1348                return false;
1349             }
1350             break;
1351       }
1352       altDown = false;
1353       return true;
1354    }
1355
1356    bool OnKeyHit(Key key, unichar ch)
1357    {
1358       bool result = true;
1359
1360       if(key == leftAlt || key == rightAlt) return true;
1361
1362       if(key && isMenuBar)
1363       {
1364          if(!keyboardFocus)
1365          {
1366             if(key)
1367                result = CheckAccelerators(menu, key);
1368             if(result && !key.alt && key != escape)
1369                return true;
1370          }
1371       }
1372
1373       if(result && visible)
1374       {
1375          // Non-Interim menus: the slaves don't have focus... pass it down from menu bar
1376          if(!INTERIM_MENU)
1377          {
1378             if(!active && firstSlave)
1379             {
1380                if(!firstSlave.OnKeyHit(key, ch))
1381                   result = false;
1382             }
1383          }
1384
1385          if(result)
1386          {
1387             switch(key)
1388             {
1389                case home:
1390                   if(!isMenuBar)
1391                   {
1392                      selected = menu.items.last;
1393                      if(MenuGoToNextItem())
1394                         Update(null);
1395                      result = false;
1396                   }
1397                   break;
1398                case end:
1399                   if(!isMenuBar)
1400                   {
1401                      selected = menu.items.first;
1402                      if(MenuGoToPrevItem())
1403                         Update(null);
1404                      result = false;
1405                   }
1406                   break;
1407                case left:
1408                   if(isMenuBar)
1409                   {
1410                      if(MenuGoToPrevItem())
1411                         MenuPopupSelected();
1412                      result = false;
1413                   }
1414                   else
1415                   {
1416                      PopupMenu master = (PopupMenu)this.master;
1417                      if(master && !master.isMenuBar)
1418                      {
1419                         Destroy(0);
1420                         if(!INTERIM_MENU)
1421                            master.Activate();
1422                         result = false;
1423                      }
1424                   }
1425                   break;
1426                case right:
1427                   if(isMenuBar)
1428                   {
1429                      if(MenuGoToNextItem() && MenuPopupSelected())
1430                         result = false;
1431                   }
1432                   else
1433                   {
1434                      if(selected && !ITEM_DISABLED(selected.item))
1435                      {
1436                         if(!MenuPopupSelected())
1437                         {
1438                            if(!selected)
1439                            {
1440                               selected = menu.items.first;
1441                               Update(null);
1442                            }
1443                         }
1444                         else
1445                            result = false;
1446                      }
1447                      else if(!((PopupMenu)master).isMenuBar)
1448                      {
1449                         if(MenuGoToNextItem())
1450                            Update(null);
1451                         result = false;
1452                      }
1453                   }
1454                   break;
1455                case space:
1456                   if(isMenuBar)
1457                   {
1458                      if(!pressed)
1459                         return MenuPopupSelected();
1460                      else
1461                      {
1462                         pressed = false;
1463                         if(firstSlave) firstSlave.Destroy(0);
1464                         Update(null);
1465                      }
1466                   }
1467                   break;
1468                case down: case Key::altDown:
1469                   if(isMenuBar)
1470                   {
1471                      return MenuPopupSelected();
1472                   }
1473                   else
1474                   {
1475                      if(MenuGoToNextItem())
1476                         Update(null);
1477                      result = false;
1478                   }
1479                   break;
1480                case up: case altUp:
1481                   if(!isMenuBar)
1482                   {
1483                      if(MenuGoToPrevItem())
1484                         Update(null);
1485                      result = false;
1486                   }
1487                   break;
1488                case enter: case altEnter:
1489                case keyPadEnter: case altKeyPadEnter:
1490                   if(selected)
1491                   {
1492                      if(!selected.item.isDivider && !selected.item.subMenu)
1493                      {
1494                         if(MenuItemSelection(menu, selected, key))
1495                            result = false;
1496                      }
1497                   }
1498                   break;
1499                case escape:
1500                   if(isMenuBar)
1501                   {
1502                      result = selected ? false : true;
1503                      selected = null;
1504                      pressed = false;
1505                      keyboardFocus = false;
1506                      RestoreCaret();
1507                      Update(null);
1508                      return result;
1509                   }
1510                   else
1511                   {
1512                      PopupMenu master = (PopupMenu)this.master;
1513                      if(eClass_IsDerived(master._class, _class) && master.isMenuBar)
1514                      {
1515                         ItemPtr selected = master.selected;
1516                         Destroy(0);
1517                         master.pressed = true;
1518                         master.selected = selected;
1519                         master.keyboardFocus = true;
1520                      }
1521                      else
1522                         Destroy(0);
1523
1524                      result = false;
1525                   }
1526                   break;
1527                default:
1528                   if(key && menu)
1529                   {
1530                      //ItemPtr ptr;
1531
1532                      if(ch >= 32 && !isMenuBar)
1533                         return false;
1534                      /*
1535                      for(ptr = menu.items.first; ptr; ptr = ptr.next)
1536                      {
1537                         MenuItem item = ptr.item;
1538                         if((item.placement && !ptr.inserted) || ITEM_DISABLED(item)) continue;
1539                         if(ITEM_HOTKEY(item) == key || (ITEM_HOTKEY(item) | Key { alt = true }) == key)
1540                         {
1541                            if(!item.isDivider && !item.subMenu)
1542                            {
1543                               if(MenuItemSelection(menu, ptr, key))
1544                                  return false;
1545                            }
1546                            else if(item.subMenu)
1547                            {
1548                               selected = ptr;
1549                               MenuPopupSelected();
1550                            }
1551                            break;
1552                         }
1553                      }*/
1554                   }
1555             }
1556             if(result && isMenuBar && pressed)
1557                result = false;
1558          }
1559          // Interim menus: the slaves are the ones with the focus
1560          if(result && INTERIM_MENU)
1561          {
1562             Window master = this.master;
1563             if(eClass_IsDerived(master._class, _class))
1564             {
1565                if(!master.OnKeyHit(key, ch))
1566                   result = false;
1567             }
1568          }
1569       }
1570       return result;
1571    }
1572
1573    bool OnLoadGraphics()
1574    {
1575       Font font = fontObject;
1576       int maxW = 0, maxH = 0;
1577       int width, height;
1578
1579       totalHeight = 0;
1580
1581       background = isMenuBar ? menuBarColor : (menu ? menu.color : popupMenuColor);
1582       FontExtent = Display::FontExtent;
1583       if(menu)
1584       {
1585          ItemPtr ptr;
1586
1587          // Default width & height for merging menus into menu bars
1588          if(isMenuBar)
1589          {
1590             FontExtent(display, font, "W",1, &maxW, &maxH);
1591             if(!guiApp.textMode)
1592                maxH += 6;
1593          }
1594
1595          for(ptr = menu.items.first; ptr; ptr = ptr.next)
1596          {
1597             MenuItem item = ptr.item;
1598             if(item.placement) continue; //&& !ptr.inserted) continue;
1599             width = 0;
1600             height = 0;
1601             if(!item.isDivider)
1602             {
1603                if(ITEM_TEXT(item))
1604                {
1605                   FontExtent(display, font,ITEM_TEXT(item),strlen(ITEM_TEXT(item)),&width, &height);
1606                   if(strstr(ITEM_TEXT(item), "\t"))
1607                      width += 8;
1608                }
1609                if(item.bitmap && item.radio)
1610                   width += 20;
1611                if(item.subMenu) width += 20;
1612                if(!guiApp.textMode)
1613                   height += 6;
1614             }
1615             else
1616             {
1617                if(!guiApp.textMode)
1618                   height = DIVIDER_HEIGHT;
1619                else
1620                   height = textCellH;
1621             }
1622
1623             if(width > maxW) maxW = width;
1624             if(height > maxH) maxH = height;
1625
1626             totalHeight += height;
1627
1628             if(item.bitmaps[0]) AddResource(item.bitmaps[0]);
1629             if(item.bitmaps[1]) AddResource(item.bitmaps[1]);
1630             if(item.bitmaps[2]) AddResource(item.bitmaps[2]);
1631          }
1632          maxW += menu.hasMargin ? 32 : 24;
1633          if(menu.text)
1634          {
1635             FontExtent(display, font,menu.text,strlen(menu.text),&width, &height);
1636             if(width > maxW) maxW = width;
1637             if(height > maxH) maxH = height;
1638          }
1639          rw = menu.w;
1640          rh = menu.h;
1641
1642          maxW += 10;
1643          if(rw < maxW) rw = maxW;
1644          if(rh < maxH) rh = maxH;
1645       }
1646       return true;
1647    }
1648
1649    bool OnResizing(int * w, int * h)
1650    {
1651       Window master = this.master;
1652       Window masterMenuBar = master.menuBar;
1653       if(this != masterMenuBar)
1654          *w = Max(*w, rw);
1655
1656       if(isMenuBar)
1657          *h = Max(*h, rh + 2);
1658       else if(menu)
1659          *h = Max(*h, totalHeight + 2);
1660
1661       if(this != masterMenuBar)
1662       {
1663          if(!*w)
1664             *w = 80;
1665          if(!*h)
1666             *h = 20;
1667       }
1668       return true;
1669    }
1670
1671    bool OnMoving(int *x, int *y, int w, int h)
1672    {
1673       if(!isMenuBar)
1674       {
1675          Window parent = this.parent;
1676          if(INTERIM_MENU)
1677          {
1678             if(*y + h > parent.clientSize.h)
1679             {
1680                PopupMenu master = (PopupMenu)this.master;
1681                *y -= h;
1682                if(eClass_IsDerived(master._class, _class))
1683                {
1684                   if(master.isMenuBar)
1685                      *y -= master.size.h;
1686                   else
1687                      *y += h - clientSize.h + rh;
1688                }
1689                *y = Max(*y, 0);
1690             }
1691             *x = Min(*x, ((parent == guiApp.desktop && guiApp.virtualScreen.w) ? guiApp.virtualScreen.w : parent.clientSize.w) - w);
1692          }
1693          else if(nonClient)
1694          {
1695             *x = Min(*x, parent.size.w - w);
1696             *y = Min(*y, parent.size.h - h);
1697          }
1698          else
1699          {
1700             *x = Min(*x, parent.clientSize.w - w);
1701             *y = Min(*y, parent.clientSize.h - h);
1702          }
1703          if(parent == guiApp.desktop)
1704          {
1705             // Just in case...
1706             *x = Max(*x, guiApp.virtualScreenPos.x);
1707             *y = Max(*y, guiApp.virtualScreenPos.y);
1708          }
1709          else
1710          {
1711             // Just in case...
1712             *x = Max(*x, 0);
1713             *y = Max(*y, 0);
1714          }
1715       }
1716       return true;
1717    }
1718
1719    bool OnMouseMove(int mx, int my, Modifiers mods)
1720    {
1721       int selectedX, selectedY;
1722       ItemPtr selected;
1723
1724       if(mods.isSideEffect) return true;
1725
1726       selected = FindSelected(mx, my, &selectedX, &selectedY);
1727
1728       if((!mods.isSideEffect || !this.selected) && (/*selected && */
1729          selected != this.selected && (!selected || !ITEM_DISABLED(selected.item)) && (selected || !keyboardFocus)))
1730       {
1731          if(!isMenuBar || pressed)
1732          {
1733             bool pressed = this.pressed;
1734
1735             if(firstSlave) firstSlave.Destroy(0);
1736
1737             this.selected = selected;
1738
1739             if(this.selected)
1740             {
1741                Menu childMenu = selected.item.subMenu;
1742
1743                this.pressed = pressed;
1744
1745                if(this.selected.item.subMenu)
1746                   MenuPopupChild(selectedX, position.y + selectedY, childMenu);
1747
1748                if(pressed)
1749                   keyboardFocus = true;
1750             }
1751          }
1752          else
1753             this.selected = selected;
1754          Update(null);
1755       }
1756       return true;
1757    }
1758
1759    bool OnLeftButtonDown(int x, int y, Modifiers mods)
1760    {
1761       if(isMenuBar)
1762       {
1763          // Had to boost this to 0.1 for Windows Basic / XP theme / Remote Desktop
1764          // Aero & Classic were fast enough for 0.01
1765          if(GetTime() - unpressedTime < 0.1)
1766             return true;
1767          if(INTERIM_MENU)
1768          {
1769             int selectedX, selectedY;
1770             if(!mods.isActivate && !pressed)
1771             {
1772                pressed = true;
1773                keyboardFocus = true;
1774                selected = null;
1775                OnMouseMove(x,y, mods);
1776             }
1777             else
1778             {
1779                if(firstSlave) firstSlave.Destroy(0);
1780                selected = FindSelected(x, y, &selectedX, &selectedY);
1781
1782                pressed = false;
1783                //selected = null;
1784                //keyboardFocus = false;
1785                Update(null);
1786             }
1787          }
1788          else
1789          {
1790             if(pressed)
1791             {
1792                pressed = false;
1793                keyboardFocus = false;
1794                if(firstSlave) firstSlave.Destroy(0);
1795                Update(null);
1796             }
1797             else
1798             {
1799                pressed = true;
1800                keyboardFocus = true;
1801                selected = null;
1802                OnMouseMove(x, y, mods);
1803             }
1804          }
1805       }
1806       else //if(!INTERIM_MENU)       // Why was this commented out?
1807       {
1808          if(!(x >= 0 && y >= 0 && x < clientSize.w && y < clientSize.h))
1809          {
1810             MenuDestroyMasters(false);
1811             return false;
1812          }
1813       }
1814       return true;
1815    }
1816
1817    bool OnRightButtonDown(int x, int y, Modifiers mods)
1818    {
1819       if(x >= 0 && y >= 0 && x < clientSize.w && y < clientSize.h)
1820       {
1821          if(isMenuBar)
1822          {
1823             Window master = this.master;
1824             Window activeClient = master.activeClient;
1825             if(activeClient.state == maximized)
1826                activeClient.ShowSysMenu(absPosition.x + x, absPosition.y + y);
1827          }
1828       }
1829       return true;
1830    }
1831
1832    bool OnLeftButtonUp(int mx, int my, Modifiers mods)
1833    {
1834       if(mx >= 2 /*0*/ && my >= 0 && mx < clientSize.w && my < clientSize.h)
1835       {
1836          Menu menu = this.menu;
1837          int y = 0;
1838
1839          // Button up
1840          if(!isMenuBar && menu)
1841          {
1842             ItemPtr ptr;
1843             for(ptr = menu.items.first; ptr; ptr = ptr.next)
1844             {
1845                MenuItem item = ptr.item;
1846                if(item.placement) continue; //&& !ptr.inserted) continue;
1847                if(my >= y && my < y + rh && !item.isDivider)
1848                {
1849                   selected = ptr;
1850                   break;
1851                }
1852                if(guiApp.textMode)
1853                   y += textCellH;
1854                else
1855                   y += (item.isDivider) ? DIVIDER_HEIGHT : rh;
1856             }
1857             Update(null);
1858          }
1859          if(!isMenuBar || pressed)
1860          {
1861             if(selected)
1862             {
1863                if(!selected.item.isDivider && !selected.item.subMenu)
1864                {
1865                   if(isMenuBar)
1866                   {
1867                      pressed = false;
1868                      keyboardFocus = false;
1869                      Update(null);
1870                   }
1871                   if(MenuItemSelection(menu, selected, Key { modifiers = mods }))
1872                      return false;
1873                }
1874             }
1875             else if(pressed)
1876             {
1877                pressed = false;
1878                keyboardFocus = false;
1879                if(firstSlave) firstSlave.Destroy(0);
1880             }
1881          }
1882       }
1883       return true;
1884    }
1885
1886    OnRightButtonUp = OnLeftButtonUp;
1887
1888    bool OnLeftDoubleClick(int x, int y, Modifiers mods)
1889    {
1890       if(isMenuBar)
1891       {
1892          int selectedX, selectedY;
1893          ItemPtr selected = FindSelected(x, y, &selectedX, &selectedY);
1894          if(!selected)
1895          {
1896             Window master = this.master;
1897             Window activeClient = master.activeClient;
1898             // TOFIX: Fix need for a cast here...
1899             while(activeClient && !((BorderBits)activeClient.borderStyle).fixed)
1900                activeClient = activeClient.activeClient;
1901             if(activeClient && activeClient.state == maximized)
1902                activeClient.SetState(normal, false, mods);
1903          }
1904       }
1905       return true;
1906    }
1907
1908    bool OnMouseLeave(Modifiers mods)
1909    {
1910       if(!pressed && !firstSlave)
1911       {
1912          selected = null;
1913          Update(null);
1914          ReleaseCapture();
1915       }
1916       return true;
1917    }
1918
1919    bool OnActivate(bool active, Window swap, bool * goOnWithActivation, bool direct)
1920    {
1921       if(!isMenuBar)
1922       {
1923          if(!active)
1924          {
1925             if(INTERIM_MENU)
1926             {
1927                PopupMenu master = (PopupMenu)this.master;
1928                if(eClass_IsDerived(master._class, _class) && master.isMenuBar) // && swap != master && swap && swap.master != master)
1929                {
1930                   {
1931                      master.unpressedTime = GetTime();
1932                      master.pressed = false;
1933                      master.selected = null;
1934                      master.keyboardFocus = false;
1935                      // master.Update(null);
1936
1937                      // TOFIX: Redraw bug here without this...
1938                      master.master.Update(null);
1939                   }
1940                }
1941                Destroy(0);
1942             }
1943             else
1944             {
1945                Window master;
1946                bool destroy = true;
1947
1948                if(swap)
1949                {
1950                   for(master = swap.master; master; master = master.master)
1951                      if(this == master)
1952                      {
1953                         destroy = false;
1954                         break;
1955                      }
1956                }
1957                if(destroy)
1958                {
1959                   for(master = this.master; master; master = master.master)
1960                      if(swap == master)
1961                      {
1962                         if(eClass_IsDerived(master._class, _class))
1963                            destroy = false;
1964                         break;
1965                      }
1966                }
1967                if(destroy)
1968                {
1969                   if(MenuDestroyMasters(false))
1970                      return false;
1971                }
1972             }
1973             return false;
1974          }
1975       }
1976       // With new activation code this is not required anymore (double effect if there)
1977       else
1978       {
1979          //if(!active)
1980          {
1981             selected = null;
1982             unpressedTime = GetTime();
1983             pressed = false;
1984             altDown = false;
1985             keyboardFocus = false;
1986             if(firstSlave) firstSlave.Destroy(0);
1987             Update(null);
1988          }
1989       }
1990       return true;
1991    }
1992
1993
1994 public:
1995    property Menu menu
1996    {
1997       set
1998       {
1999          delete menu;
2000          menu = value;
2001          if(menu)
2002             incref menu;
2003          if(created)
2004          {
2005             selected = null;
2006             if(firstSlave) firstSlave.Destroy(0);
2007             OnLoadGraphics();
2008             Move(position.x, position.y, size.w, size.h);
2009
2010             if(!text)
2011             {
2012                if(interim || isMenuBar)
2013                   text = master.text;
2014                else
2015                   text = menu.text;
2016             }
2017          }
2018       }
2019       get { return menu; }
2020    };
2021    property bool isMenuBar { set { isMenuBar = value; } };
2022    property bool focus { get { return keyboardFocus; } };
2023 };