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