ecere: Fixed up i18n Support; Started Chinese translation integration
[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                char * text = ITEM_TEXT(item);
956                FontExtent(display, fontObject, text, text ? strlen(text) : 0, &len, null);
957                if((mx >= x - 16 && mx < x + len + 16))
958                {
959                   if(!ITEM_DISABLED(item))
960                      selected = ptr;
961                   if(guiApp.textMode)
962                      *selectedX = x;
963                   else
964                      *selectedX = x + 2;
965                   *selectedY = -position.y;
966                   break;
967                }
968                x += len + 16;
969             }
970          }
971       }
972       else if(menu)
973       {
974          ItemPtr ptr;
975          int y = 0;
976          for(ptr = menu.items.first; ptr; ptr = ptr.next)
977          {
978             MenuItem item = ptr.item;
979             if(item.placement) continue; //&& !ptr.inserted) continue;
980             if(mx >= 2 && (my >= y && my < y + rh) && !item.isDivider)
981             {
982                if(!ITEM_DISABLED(item))
983                   selected = ptr;
984                *selectedY = y;
985                *selectedX = position.x + size.w;
986                break;
987             }
988             if(guiApp.textMode)
989                y += textCellH;
990             else
991                y += (item.isDivider) ? DIVIDER_HEIGHT : rh;
992          }
993       }
994       return selected;
995    }
996
997    // Window Overrides
998    void OnRedraw(Surface surface)
999    {
1000       int x = 0;
1001       int y = 0;
1002       int height;
1003       bool helpBreak = false;
1004       Window parent = this.parent;
1005       Window activeClient = parent.activeClient;
1006       bool systemButtons = activeClient && activeClient.state == maximized;
1007       int bitmapOffset = 0;
1008
1009       surface.TextFont(fontObject);
1010       surface.TextExtent(" ", 1, null, &height);
1011       surface.SetBackground(SELECTION_COLOR);
1012
1013       if(!isMenuBar)
1014       {
1015          if(guiApp.textMode)
1016             bitmapOffset = 16;
1017          else
1018             bitmapOffset = 12;
1019       }
1020       else if(guiApp.textMode)
1021          bitmapOffset = 8;
1022       else
1023       {
1024          // Shiny gradient for menu bar
1025          ColorKey keys[2] = 
1026          {
1027             { white, 0 },
1028             { popupMenuColor, 1 }
1029          };
1030          surface.Gradient(keys, sizeof(keys)/sizeof(ColorKey), 1, vertical, 0,0, clientSize.w-1, 7);
1031       }
1032
1033       if(menu)
1034       {
1035          ItemPtr ptr;
1036          for(ptr = menu.items.first; ptr; ptr = ptr.next)
1037          {
1038             MenuItem item = ptr.item;
1039             if(item.placement) continue; //&& !ptr.inserted) continue;
1040             if(!isMenuBar && selected == ptr)
1041             {
1042                surface.SetForeground(SELECTION_TEXT);
1043                if(guiApp.textMode)
1044                {
1045                   surface.TextOpacity(true);
1046                   surface.Area(0,y,clientSize.w-1,y+rh-1);
1047                }
1048                else
1049                   surface.Area(2,y,clientSize.w-3,y+rh);
1050             }
1051             else
1052             {
1053                surface.SetForeground(foreground);
1054                if(guiApp.textMode)
1055                   surface.TextOpacity(false);
1056             }
1057
1058             if(item.isDivider)
1059             {
1060                if(isMenuBar)
1061                {
1062                   if(!helpBreak)
1063                   {
1064                      ItemPtr nextPtr;
1065                      int breakX = clientSize.w - (systemButtons ? 48 : 0);
1066                      for(nextPtr = ptr.next; nextPtr; nextPtr = nextPtr.next)
1067                      {
1068                         MenuItem nextItem = nextPtr.item;
1069                         int len;
1070                         if(!nextItem.isDivider && ITEM_TEXT(nextItem))
1071                         {
1072                            surface.TextExtent(ITEM_TEXT(nextItem), strlen(ITEM_TEXT(nextItem)), &len, null);
1073                            breakX -= len + 16;
1074                         }
1075                      }
1076                      if(x < breakX) x = breakX;
1077                      helpBreak = true;
1078                   }
1079                }
1080                else
1081                {
1082                   if(guiApp.textMode)
1083                   {
1084                      surface.SetForeground(Color { 85, 85, 85 });
1085                      surface.DrawingChar(196);
1086                      surface.HLine(x + 2, x + rw - 5, y + (rh) / 2);
1087                      surface.DrawingChar(' ');
1088                   }
1089                   else
1090                   {
1091                      surface.SetForeground(Color { 85, 85, 85 });
1092                      surface.HLine(x + 2, x + rw - 5, y + (DIVIDER_HEIGHT) / 2);
1093                      surface.SetForeground(white);
1094                      surface.HLine(x + 2, x + rw - 5, y + (DIVIDER_HEIGHT) / 2 + 1);
1095                   }
1096                }
1097             }
1098             else
1099             {
1100                if(selected == ptr && guiApp.textMode)
1101                {
1102                   surface.SetBackground(SELECTION_COLOR);
1103                   surface.SetForeground(SELECTION_TEXT /*background*/);
1104                   surface.TextOpacity(true);
1105                }
1106                if(guiApp.textMode)
1107                {
1108                   Interface::WriteKeyedTextDisabled(surface, x + bitmapOffset,
1109                      y + (rh - height)/2, ITEM_TEXT(item), ITEM_HOTKEY(item), ITEM_DISABLED(item));
1110                   if(item.checked)
1111                      surface.WriteText(x, y, "\373", 1);
1112                }
1113                else
1114                {
1115                   int textY = y + (rh - height)/2;
1116                   BitmapResource bitmap = item.disabled ? item.bitmaps[2] : ((selected == ptr) ? item.bitmaps[1] : item.bitmaps[0]);
1117                   if(!isMenuBar && bitmap)
1118                   {
1119                      Bitmap icon = bitmap.bitmap;
1120                      if(icon)
1121                         surface.Blit(icon, x + 3, y + (rh - icon.height)/2, 0,0, icon.width, icon.height);
1122                   }
1123
1124                   if(item.bold)
1125                      surface.TextFont(boldFont.font);
1126                   else
1127                      surface.TextFont(fontObject);
1128
1129                   if(ITEM_DISABLED(item) && selected == ptr)
1130                   {
1131                      surface.SetForeground(activeBorder);
1132                      Interface::WriteKeyedText(surface, x + bitmapOffset + 5,
1133                         textY, ITEM_TEXT(item), ITEM_HOTKEY(item));
1134                   }
1135                   else
1136                      Interface::WriteKeyedTextDisabled(surface, x + bitmapOffset + 5,
1137                         textY, ITEM_TEXT(item), ITEM_HOTKEY(item), ITEM_DISABLED(item));
1138                   surface.SetForeground(foreground);
1139                   if(item.checked)
1140                   {
1141                      surface.DrawLine(x+5, y+9, x+8,y+12);
1142                      surface.DrawLine(x+5, y+10, x+8,y+13);
1143                      surface.DrawLine(x+8, y+12, x+12,y+4);
1144                      surface.DrawLine(x+8, y+13, x+12,y+5);
1145                   }
1146                }
1147             }
1148
1149             if(!isMenuBar)
1150             {
1151                // Draw little arrow
1152                if(item.subMenu)
1153                {
1154                   surface.SetForeground(foreground);
1155
1156                   if(guiApp.textMode)
1157                   {
1158                      surface.SetForeground((selected == ptr) ? (background) : (foreground));
1159                      surface.WriteText(clientSize.w-8, y+(rh - 8)/2, "\020", 1);
1160                   }
1161                   else
1162                   {
1163                      Bitmap arrow = (selected == ptr) ? whiteSubArrow.bitmap : subArrow.bitmap;
1164                      if(ITEM_DISABLED(ptr.item)) arrow = disabledSubArrow.bitmap;
1165
1166                      if(arrow)
1167                         surface.Blit(arrow, clientSize.w-14, y+(rh - 8)/2, 0,0, arrow.width, arrow.height);
1168                      /*
1169                      surface.VLine(y+(rh - 8)/2, y+(rh - 8)/2+7, clientSize.w-10);
1170                      surface.SetForeground(Color { 85, 85, 85 });
1171                      surface.DrawLine(clientSize.w-10, y+(rh - 8)/2, clientSize.w-4, y+rh / 2);
1172                      surface.SetForeground(activeBorder);
1173                      surface.DrawLine(clientSize.w-10, y+(rh - 8)/2+7, clientSize.w-4, y+rh / 2);
1174                      */
1175                   }
1176                }
1177                if(guiApp.textMode)
1178                   y += rh;
1179                else
1180                   y += (item.isDivider) ? DIVIDER_HEIGHT : rh;
1181             }
1182             else if(ITEM_TEXT(item) && !item.isDivider)
1183             {
1184                int len;
1185                surface.TextExtent(ITEM_TEXT(item), strlen(ITEM_TEXT(item)), &len, null);
1186                if(selected == ptr && !guiApp.textMode &&
1187                   (item.subMenu || selected))
1188                {
1189                   surface.Bevel(pressed, x, y, len+10, height + 6);
1190                }
1191                if(!item.isDivider)
1192                   x += len + 16;
1193             }
1194          }
1195       }
1196    }
1197
1198    bool OnKeyDown(Key key, unichar ch)
1199    {
1200       bool result = true;
1201       if(visible)
1202       {
1203          // Non-Interim menus: the slaves don't have focus... pass it down from menu bar
1204          if(!INTERIM_MENU)
1205          {
1206             if(!active && firstSlave)
1207             {
1208                if(!firstSlave.OnKeyDown(key, ch))
1209                   result = false;
1210             }
1211          }
1212
1213          switch(key)
1214          {
1215             case leftAlt:
1216             case rightAlt:
1217                if(!isMenuBar)
1218                {
1219                   altDown = false;
1220                   MenuDestroyMasters(true);
1221                }
1222                else
1223                {
1224                   altDown = true;
1225                }
1226                return false;
1227             default:
1228                altDown = false;
1229                if(key && menu)
1230                {
1231                   ItemPtr ptr;
1232                   
1233                   for(ptr = menu.items.first; ptr; ptr = ptr.next)
1234                   {
1235                      MenuItem item = ptr.item;
1236                      if((item.placement && !ptr.inserted) || ITEM_DISABLED(item)) continue;
1237                      if(((keyboardFocus || !isMenuBar) && ITEM_HOTKEY(item) == key) || (ITEM_HOTKEY(item) | Key { alt = true }) == key)
1238                      {
1239                         if(!item.isDivider && !item.subMenu)
1240                         {
1241                            if(MenuItemSelection(menu, ptr, key))
1242                               return false;
1243                         }
1244                         else if(item.subMenu)
1245                         {
1246                            selected = ptr;
1247                            MenuPopupSelected();
1248                            result = false;
1249                         }
1250                         break;
1251                      }
1252                   }
1253                   if(ch >= 32 && ch != 128 && !isMenuBar)
1254                      result = false;
1255                }
1256          }
1257
1258          // Interim menus: the slaves are the ones with the focus
1259          if(result && INTERIM_MENU)
1260          {
1261             Window master = this.master;
1262             if(eClass_IsDerived(master._class, _class))
1263             {
1264                if(!master.OnKeyDown(key, ch))
1265                   result = false;
1266             }
1267          }
1268       }
1269       return result;
1270    }
1271
1272    bool OnKeyUp(Key key, unichar ch)
1273    {
1274       switch(key)
1275       {
1276          case leftAlt:
1277          case rightAlt:
1278             if(isMenuBar && altDown)
1279             {
1280                if(!selected)
1281                {
1282                   MenuGoToNextItem();
1283                   keyboardFocus = true;
1284                   RestoreCaret();
1285                }
1286                else
1287                {
1288                   selected = null;
1289                   if(firstSlave) firstSlave.Destroy(0);
1290                   pressed = false;
1291                   keyboardFocus = false;
1292                   RestoreCaret();
1293                }
1294                Update(null);
1295                altDown = false;
1296                return false;
1297             }
1298             break;
1299       }
1300       altDown = false;
1301       return true;
1302    }
1303
1304    bool OnKeyHit(Key key, unichar ch)
1305    {
1306       bool result = true;
1307
1308       if(key == leftAlt || key == rightAlt) return true;
1309
1310       if(key && isMenuBar)
1311       {
1312          if(!keyboardFocus)
1313          {
1314             if(key)
1315                result = CheckAccelerators(menu, key);
1316             if(result && !key.alt && key != escape)
1317                return true;
1318          }
1319       }
1320
1321       if(result && visible)
1322       {
1323          // Non-Interim menus: the slaves don't have focus... pass it down from menu bar
1324          if(!INTERIM_MENU)
1325          {
1326             if(!active && firstSlave)
1327             {
1328                if(!firstSlave.OnKeyHit(key, ch))
1329                   result = false;
1330             }
1331          }
1332
1333          if(result)
1334          {
1335             switch(key)
1336             {
1337                case home:
1338                   if(!isMenuBar)
1339                   {
1340                      selected = menu.items.last;
1341                      if(MenuGoToNextItem())
1342                         Update(null);
1343                      result = false;
1344                   }         
1345                   break;
1346                case end:
1347                   if(!isMenuBar)
1348                   {
1349                      selected = menu.items.first;
1350                      if(MenuGoToPrevItem())
1351                         Update(null);
1352                      result = false;
1353                   }         
1354                   break;
1355                case left:
1356                   if(isMenuBar)
1357                   {
1358                      if(MenuGoToPrevItem())
1359                         MenuPopupSelected();
1360                      result = false;
1361                   }
1362                   else
1363                   {
1364                      PopupMenu master = (PopupMenu)this.master;
1365                      if(master && !master.isMenuBar)
1366                      {
1367                         Destroy(0);
1368                         if(!INTERIM_MENU)
1369                            master.Activate();
1370                         result = false;
1371                      }
1372                   }
1373                   break;
1374                case right:
1375                   if(isMenuBar)
1376                   {
1377                      if(MenuGoToNextItem() && MenuPopupSelected())
1378                         result = false;
1379                   }
1380                   else
1381                   {
1382                      if(selected && !ITEM_DISABLED(selected.item))
1383                      {
1384                         if(!MenuPopupSelected())
1385                         {
1386                            if(!selected)
1387                            {
1388                               selected = menu.items.first;
1389                               Update(null);
1390                            }
1391                         }
1392                         else
1393                            result = false;
1394                      }
1395                      else if(!((PopupMenu)master).isMenuBar)
1396                      {
1397                         if(MenuGoToNextItem())
1398                            Update(null);
1399                         result = false;
1400                      }
1401                   }
1402                   break;
1403                case space:
1404                   if(isMenuBar)
1405                   {
1406                      if(!pressed)
1407                         return MenuPopupSelected();
1408                      else
1409                      {
1410                         pressed = false;
1411                         if(firstSlave) firstSlave.Destroy(0);
1412                         Update(null);
1413                      }
1414                   }
1415                   break;
1416                case down: case Key::altDown:
1417                   if(isMenuBar)
1418                   {
1419                      return MenuPopupSelected();
1420                   }
1421                   else
1422                   {
1423                      if(MenuGoToNextItem())
1424                         Update(null);
1425                      result = false;
1426                   }
1427                   break;
1428                case up: case altUp:
1429                   if(!isMenuBar)
1430                   {
1431                      if(MenuGoToPrevItem())
1432                         Update(null);
1433                      result = false;
1434                   }
1435                   break;
1436                case enter: case altEnter:
1437                case keyPadEnter: case altKeyPadEnter:
1438                   if(selected)
1439                   {
1440                      if(!selected.item.isDivider && !selected.item.subMenu)
1441                      {
1442                         if(MenuItemSelection(menu, selected, key))
1443                            result = false;
1444                      }
1445                   }
1446                   break;
1447                case escape:
1448                   if(isMenuBar)
1449                   {
1450                      result = selected ? false : true;
1451                      selected = null;
1452                      pressed = false;
1453                      keyboardFocus = false;
1454                      RestoreCaret();
1455                      Update(null);
1456                      return result;
1457                   }
1458                   else
1459                   {
1460                      PopupMenu master = (PopupMenu)this.master;
1461                      if(eClass_IsDerived(master._class, _class) && master.isMenuBar)
1462                      {
1463                         ItemPtr selected = master.selected;
1464                         Destroy(0);
1465                         master.pressed = true;
1466                         master.selected = selected;
1467                         master.keyboardFocus = true;
1468                      }
1469                      else
1470                         Destroy(0);
1471
1472                      result = false;
1473                   }
1474                   break;
1475                default:
1476                   if(key && menu)
1477                   {
1478                      //ItemPtr ptr;
1479                      
1480                      if(ch >= 32 && !isMenuBar)
1481                         return false;
1482                      /*
1483                      for(ptr = menu.items.first; ptr; ptr = ptr.next)
1484                      {
1485                         MenuItem item = ptr.item;
1486                         if((item.placement && !ptr.inserted) || ITEM_DISABLED(item)) continue;
1487                         if(ITEM_HOTKEY(item) == key || (ITEM_HOTKEY(item) | Key { alt = true }) == key)
1488                         {
1489                            if(!item.isDivider && !item.subMenu)
1490                            {
1491                               if(MenuItemSelection(menu, ptr, key))
1492                                  return false;
1493                            }
1494                            else if(item.subMenu)
1495                            {
1496                               selected = ptr;
1497                               MenuPopupSelected();
1498                            }
1499                            break;
1500                         }
1501                      }*/
1502                   }
1503             }
1504             if(result && isMenuBar && pressed)
1505                result = false;
1506          }
1507          // Interim menus: the slaves are the ones with the focus
1508          if(result && INTERIM_MENU)
1509          {
1510             Window master = this.master;
1511             if(eClass_IsDerived(master._class, _class))
1512             {
1513                if(!master.OnKeyHit(key, ch))
1514                   result = false;
1515             }
1516          }
1517       }
1518       return result;
1519    }
1520
1521    bool OnLoadGraphics()
1522    {
1523       Font font = fontObject;
1524       int maxW = 0, maxH = 0;
1525       int width, height;
1526
1527       totalHeight = 0;
1528
1529       background = isMenuBar ? menuBarColor : (menu ? menu.color : popupMenuColor);
1530       FontExtent = Display::FontExtent;
1531       if(menu)
1532       {
1533          ItemPtr ptr;
1534
1535          for(ptr = menu.items.first; ptr; ptr = ptr.next)
1536          {
1537             MenuItem item = ptr.item;
1538             if(item.placement) continue; //&& !ptr.inserted) continue;
1539             width = 0;
1540             height = 0;
1541             if(!item.isDivider)
1542             {
1543                if(ITEM_TEXT(item))
1544                {
1545                   FontExtent(display, font,ITEM_TEXT(item),strlen(ITEM_TEXT(item)),&width, &height);
1546                   if(strstr(ITEM_TEXT(item), "\t"))
1547                      width += 8;
1548                }
1549                if(item.subMenu) width += 20;
1550                if(!guiApp.textMode)
1551                   height += 6;
1552             }
1553             else
1554             {
1555                if(!guiApp.textMode)
1556                   height = DIVIDER_HEIGHT;
1557                else
1558                   height = textCellH;
1559             }
1560
1561             if(width > maxW) maxW = width;
1562             if(height > maxH) maxH = height;
1563
1564             totalHeight += height;
1565
1566             if(item.bitmaps[0]) AddResource(item.bitmaps[0]);
1567             if(item.bitmaps[1]) AddResource(item.bitmaps[1]);
1568             if(item.bitmaps[2]) AddResource(item.bitmaps[2]);
1569          }
1570          maxW += 24;
1571          if(menu.text)
1572          {
1573             FontExtent(display, font,menu.text,strlen(menu.text),&width, &height);
1574             if(width > maxW) maxW = width;
1575             if(height > maxH) maxH = height;
1576          }
1577          rw = menu.w;
1578          rh = menu.h;
1579
1580          maxW += 10;
1581          if(rw < maxW) rw = maxW;
1582          if(rh < maxH) rh = maxH;
1583       }
1584       return true;
1585    }
1586
1587    bool OnResizing(int * w, int * h)
1588    {
1589       Window master = this.master;
1590       Window masterMenuBar = master.menuBar;
1591       if(this != masterMenuBar)
1592          *w = Max(*w, rw);
1593
1594       if(isMenuBar)
1595          *h = Max(*h, rh + 2);
1596       else if(menu)
1597          *h = Max(*h, totalHeight + 2);
1598
1599       if(this != masterMenuBar)
1600       {
1601          if(!*w) 
1602             *w = 80;
1603          if(!*h) 
1604             *h = 20;
1605       }
1606       return true;
1607    }
1608
1609    bool OnMoving(int *x, int *y, int w, int h)
1610    {
1611       if(!isMenuBar)
1612       {
1613          Window parent = this.parent;
1614          if(INTERIM_MENU)
1615          {
1616             if(*y + h > parent.clientSize.h)
1617             {
1618                PopupMenu master = (PopupMenu)this.master;
1619                *y -= h;
1620                if(eClass_IsDerived(master._class, _class))
1621                {
1622                   if(master.isMenuBar)
1623                      *y -= master.size.h;
1624                   else
1625                      *y += h - clientSize.h + rh;
1626                }
1627                *y = Max(*y, 0);
1628             }
1629             *x = Min(*x, ((parent == guiApp.desktop && guiApp.virtualScreen.w) ? guiApp.virtualScreen.w : parent.clientSize.w) - w);
1630          }
1631          else if(nonClient)
1632          {
1633             *x = Min(*x, parent.size.w - w);
1634             *y = Min(*y, parent.size.h - h);
1635          }
1636          else
1637          {
1638             *x = Min(*x, parent.clientSize.w - w);
1639             *y = Min(*y, parent.clientSize.h - h);   
1640          }
1641          if(parent == guiApp.desktop)
1642          {
1643             // Just in case...
1644             *x = Max(*x, guiApp.virtualScreenPos.x);
1645             *y = Max(*y, guiApp.virtualScreenPos.y);
1646          }
1647          else
1648          {
1649             // Just in case...
1650             *x = Max(*x, 0);
1651             *y = Max(*y, 0);
1652          }
1653       }
1654       return true;
1655    }
1656
1657    bool OnMouseMove(int mx, int my, Modifiers mods)
1658    {
1659       int selectedX, selectedY;
1660
1661       ItemPtr selected = FindSelected(mx, my, &selectedX, &selectedY);
1662
1663       if((!mods.isSideEffect || !this.selected) && (/*selected && */
1664          selected != this.selected && (!selected || !ITEM_DISABLED(selected.item)) && (selected || !keyboardFocus)))
1665       {
1666          if(!isMenuBar || pressed)
1667          {
1668             bool pressed = this.pressed;
1669
1670             if(firstSlave) firstSlave.Destroy(0);
1671
1672             this.selected = selected;
1673
1674             if(this.selected)
1675             {
1676                Menu childMenu = selected.item.subMenu;
1677                
1678                this.pressed = pressed;
1679
1680                if(this.selected.item.subMenu)
1681                   MenuPopupChild(selectedX, position.y + selectedY, childMenu);
1682
1683                if(pressed)
1684                   keyboardFocus = true;
1685             }
1686          }
1687          else
1688             this.selected = selected;
1689          Update(null);
1690       }
1691       return true;
1692    }
1693
1694    bool OnLeftButtonDown(int x, int y, Modifiers mods)
1695    {
1696       if(isMenuBar)
1697       {
1698          Time t = GetTime(), u = unpressedTime;
1699          // Had to boost this to 0.1 for Windows Basic / XP theme / Remote Desktop
1700          // Aero & Classic were fast enough for 0.01
1701          if(GetTime() - unpressedTime < 0.1)
1702             return true;
1703          if(INTERIM_MENU)
1704          {
1705             int selectedX, selectedY;
1706             if(!mods.isActivate && !pressed)
1707             {
1708                pressed = true;
1709                keyboardFocus = true;
1710                selected = null;
1711                OnMouseMove(x,y, mods);
1712             }
1713             else
1714             {
1715                if(firstSlave) firstSlave.Destroy(0);
1716                selected = FindSelected(x, y, &selectedX, &selectedY);
1717
1718                pressed = false;
1719                //selected = null;
1720                //keyboardFocus = false;
1721                Update(null);
1722             }
1723          }
1724          else
1725          {
1726             if(pressed)
1727             {
1728                pressed = false;
1729                keyboardFocus = false;
1730                if(firstSlave) firstSlave.Destroy(0);
1731                Update(null);
1732             }
1733             else
1734             {
1735                pressed = true;
1736                keyboardFocus = true;
1737                selected = null;
1738                OnMouseMove(x, y, mods);
1739             }
1740          }
1741       }
1742       else //if(!INTERIM_MENU)       // Why was this commented out?
1743       {
1744          if(!(x >= 0 && y >= 0 && x < clientSize.w && y < clientSize.h))
1745          {
1746             MenuDestroyMasters(false);
1747             return false;
1748          }
1749       }
1750       return true;
1751    }
1752
1753    bool OnRightButtonDown(int x, int y, Modifiers mods)
1754    {
1755       if(x >= 0 && y >= 0 && x < clientSize.w && y < clientSize.h)
1756       {
1757          if(isMenuBar)
1758          {
1759             Window master = this.master;
1760             Window activeClient = master.activeClient;
1761             if(activeClient.state == maximized)
1762                activeClient.ShowSysMenu(absPosition.x + x, absPosition.y + y);
1763          }
1764       }
1765       return true;
1766    }
1767
1768    bool OnLeftButtonUp(int mx, int my, Modifiers mods)
1769    {
1770       if(mx >= 2 /*0*/ && my >= 0 && mx < clientSize.w && my < clientSize.h)
1771       {
1772          Menu menu = this.menu;
1773          int y = 0;
1774
1775          // Button up
1776          if(!isMenuBar && menu)
1777          {
1778             ItemPtr ptr;
1779             for(ptr = menu.items.first; ptr; ptr = ptr.next)
1780             {
1781                MenuItem item = ptr.item;
1782                if(item.placement) continue; //&& !ptr.inserted) continue;
1783                if(my >= y && my < y + rh && !item.isDivider)
1784                {
1785                   selected = ptr;
1786                   break;
1787                }
1788                if(guiApp.textMode)
1789                   y += textCellH;
1790                else
1791                   y += (item.isDivider) ? DIVIDER_HEIGHT : rh;
1792             }
1793             Update(null);
1794          }
1795          if(!isMenuBar || pressed)
1796          {
1797             if(selected)
1798             {
1799                if(!selected.item.isDivider && !selected.item.subMenu)
1800                {
1801                   if(isMenuBar)
1802                   {
1803                      pressed = false;
1804                      keyboardFocus = false;
1805                      Update(null);
1806                   }
1807                   if(MenuItemSelection(menu, selected, (Key)mods))
1808                      return false;
1809                }
1810             }
1811             else if(pressed)
1812             {
1813                pressed = false;
1814                keyboardFocus = false;
1815                if(firstSlave) firstSlave.Destroy(0);
1816             }
1817          }
1818       }
1819       return true;
1820    }
1821
1822    OnRightButtonUp = OnLeftButtonUp;
1823
1824    bool OnLeftDoubleClick(int x, int y, Modifiers mods)
1825    {
1826       if(isMenuBar)
1827       {
1828          int selectedX, selectedY;
1829          ItemPtr selected = FindSelected(x, y, &selectedX, &selectedY);
1830          if(!selected)
1831          {
1832             Window master = this.master;
1833             Window activeClient = master.activeClient;
1834             if(activeClient && activeClient.state == maximized)
1835                activeClient.SetState(normal, false, mods);
1836          }
1837       }
1838       return true;
1839    }
1840
1841    bool OnMouseLeave(Modifiers mods)
1842    {
1843       if(!pressed && !firstSlave)
1844       {
1845          selected = null;
1846          Update(null);
1847          ReleaseCapture();
1848       }
1849       return true;
1850    }
1851
1852    bool OnActivate(bool active, Window swap, bool * goOnWithActivation, bool direct)
1853    {
1854       if(!isMenuBar)
1855       {
1856          if(!active)
1857          {
1858             if(INTERIM_MENU)
1859             {
1860                PopupMenu master = (PopupMenu)this.master;
1861                if(eClass_IsDerived(master._class, _class) && master.isMenuBar) // && swap != master && swap && swap.master != master)
1862                {
1863                   {
1864                      master.unpressedTime = GetTime();
1865                      master.pressed = false;
1866                      master.selected = null;
1867                      master.keyboardFocus = false;
1868                      // master.Update(null);
1869
1870                      // TOFIX: Redraw bug here without this...
1871                      master.master.Update(null);
1872                   }
1873                }
1874                Destroy(0);
1875             }
1876             else
1877             {
1878                Window master;
1879                bool destroy = true;
1880
1881                if(swap)
1882                {
1883                   for(master = swap.master; master; master = master.master)
1884                      if(this == master)
1885                      {
1886                         destroy = false;
1887                         break;
1888                      }
1889                }
1890                if(destroy)
1891                {
1892                   for(master = this.master; master; master = master.master)
1893                      if(swap == master)
1894                      {
1895                         if(eClass_IsDerived(master._class, _class))
1896                            destroy = false;
1897                         break;
1898                      }
1899                }
1900                if(destroy) 
1901                {
1902                   if(MenuDestroyMasters(false))
1903                      return false;
1904                }
1905             }
1906             return false;
1907          }
1908       }
1909       // With new activation code this is not required anymore (double effect if there)
1910       else
1911       {
1912          //if(!active)
1913          {
1914             selected = null;
1915             unpressedTime = GetTime();
1916             pressed = false;
1917             altDown = false;
1918             keyboardFocus = false;
1919             if(firstSlave) firstSlave.Destroy(0);
1920             Update(null);
1921          }
1922       }
1923       return true;
1924    }
1925
1926
1927 public:
1928    property Menu menu
1929    {
1930       set
1931       {
1932          delete menu;
1933          menu = value;
1934          if(menu)
1935             incref menu;
1936          if(created)
1937          {
1938             selected = null;
1939             if(firstSlave) firstSlave.Destroy(0);
1940             OnLoadGraphics();
1941             Move(position.x, position.y, size.w, size.h);
1942
1943             if(!text)
1944             {
1945                if(interim || isMenuBar)
1946                   text = master.text;
1947                else
1948                   text = menu.text;
1949             }
1950          }
1951       }
1952    };
1953    property bool isMenuBar { set { isMenuBar = value; } };
1954    property bool focus { get { return keyboardFocus; } };
1955 };