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