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