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