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