1 /********************************************************************************
2 *                                                                               *
3 *                         M e n u   B a r   W i d g e t                         *
4 *                                                                               *
5 *********************************************************************************
6 * Copyright (C) 1998,2021 by Jeroen van der Zijp.   All Rights Reserved.        *
7 *********************************************************************************
8 * This library is free software; you can redistribute it and/or modify          *
9 * it under the terms of the GNU Lesser General Public License as published by   *
10 * the Free Software Foundation; either version 3 of the License, or             *
11 * (at your option) any later version.                                           *
12 *                                                                               *
13 * This library is distributed in the hope that it will be useful,               *
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of                *
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                 *
16 * GNU Lesser General Public License for more details.                           *
17 *                                                                               *
18 * You should have received a copy of the GNU Lesser General Public License      *
19 * along with this program.  If not, see <http://www.gnu.org/licenses/>          *
20 ********************************************************************************/
21 #include "xincs.h"
22 #include "fxver.h"
23 #include "fxdefs.h"
24 #include "fxmath.h"
25 #include "FXArray.h"
26 #include "FXHash.h"
27 #include "FXMutex.h"
28 #include "FXStream.h"
29 #include "FXString.h"
30 #include "FXSize.h"
31 #include "FXPoint.h"
32 #include "FXRectangle.h"
33 #include "FXStringDictionary.h"
34 #include "FXSettings.h"
35 #include "FXRegistry.h"
36 #include "FXAccelTable.h"
37 #include "FXEvent.h"
38 #include "FXWindow.h"
39 #include "FXApp.h"
40 #include "FXButton.h"
41 #include "FXMenuBar.h"
42 
43 
44 /*
45   Notes:
46   - Hittin Alt- key only should attract focus to the first item in the menubar.
47   - If width of menu gets too small, expand the height to make it multiple rows.
48   - FIXME popping a menu and leaving the cursor inside the menu title
49     somehow does not allow keyboard navigation.
50 */
51 
52 using namespace FX;
53 
54 /*******************************************************************************/
55 
56 namespace FX {
57 
58 // Map
59 FXDEFMAP(FXMenuBar) FXMenuBarMap[]={
60   FXMAPFUNC(SEL_ENTER,0,FXMenuBar::onEnter),
61   FXMAPFUNC(SEL_LEAVE,0,FXMenuBar::onLeave),
62   FXMAPFUNC(SEL_MOTION,0,FXMenuBar::onMotion),
63   FXMAPFUNC(SEL_LEFTBUTTONPRESS,0,FXMenuBar::onButtonPress),
64   FXMAPFUNC(SEL_LEFTBUTTONRELEASE,0,FXMenuBar::onButtonRelease),
65   FXMAPFUNC(SEL_MIDDLEBUTTONPRESS,0,FXMenuBar::onButtonPress),
66   FXMAPFUNC(SEL_MIDDLEBUTTONRELEASE,0,FXMenuBar::onButtonRelease),
67   FXMAPFUNC(SEL_RIGHTBUTTONPRESS,0,FXMenuBar::onButtonPress),
68   FXMAPFUNC(SEL_RIGHTBUTTONRELEASE,0,FXMenuBar::onButtonRelease),
69   FXMAPFUNC(SEL_FOCUS_RIGHT,0,FXMenuBar::onFocusRight),
70   FXMAPFUNC(SEL_FOCUS_LEFT,0,FXMenuBar::onFocusLeft),
71   FXMAPFUNC(SEL_FOCUS_NEXT,0,FXMenuBar::onDefault),
72   FXMAPFUNC(SEL_FOCUS_PREV,0,FXMenuBar::onDefault),
73   FXMAPFUNC(SEL_FOCUS_UP,0,FXMenuBar::onDefault),
74   FXMAPFUNC(SEL_FOCUS_DOWN,0,FXMenuBar::onDefault),
75   FXMAPFUNC(SEL_COMMAND,FXWindow::ID_UNPOST,FXMenuBar::onCmdUnpost),
76   };
77 
78 
79 // Object implementation
FXIMPLEMENT(FXMenuBar,FXToolBar,FXMenuBarMap,ARRAYNUMBER (FXMenuBarMap))80 FXIMPLEMENT(FXMenuBar,FXToolBar,FXMenuBarMap,ARRAYNUMBER(FXMenuBarMap))
81 
82 
83 // Make a floatable menubar
84 FXMenuBar::FXMenuBar(FXComposite* p,FXComposite* q,FXuint opts,FXint x,FXint y,FXint w,FXint h,FXint pl,FXint pr,FXint pt,FXint pb,FXint hs,FXint vs):FXToolBar(p,q,opts,x,y,w,h,pl,pr,pt,pb,hs,vs){
85   flags|=FLAG_ENABLED;
86   dragCursor=getApp()->getDefaultCursor(DEF_RARROW_CURSOR);
87   }
88 
89 
90 // Make a non-floatable menubar
FXMenuBar(FXComposite * p,FXuint opts,FXint x,FXint y,FXint w,FXint h,FXint pl,FXint pr,FXint pt,FXint pb,FXint hs,FXint vs)91 FXMenuBar::FXMenuBar(FXComposite* p,FXuint opts,FXint x,FXint y,FXint w,FXint h,FXint pl,FXint pr,FXint pt,FXint pb,FXint hs,FXint vs):FXToolBar(p,opts,x,y,w,h,pl,pr,pt,pb,hs,vs){
92   flags|=FLAG_ENABLED;
93   dragCursor=getApp()->getDefaultCursor(DEF_RARROW_CURSOR);
94   }
95 
96 
97 // Focus moved to right
onFocusRight(FXObject *,FXSelector,void * ptr)98 long FXMenuBar::onFocusRight(FXObject*,FXSelector,void* ptr){
99   FXWindow *child;
100   if(getFocus()){
101     child=getFocus()->getNext();
102     while(child){
103       if(child->handle(this,FXSEL(SEL_FOCUS_SELF,0),ptr)) return 1;
104       child=child->getNext();
105       }
106     child=getFirst();
107     while(child){
108       if(child->handle(this,FXSEL(SEL_FOCUS_SELF,0),ptr)) return 1;
109       child=child->getNext();
110       }
111     }
112   return 0;
113   }
114 
115 
116 // Focus moved to left
onFocusLeft(FXObject *,FXSelector,void * ptr)117 long FXMenuBar::onFocusLeft(FXObject*,FXSelector,void* ptr){
118   FXWindow *child;
119   if(getFocus()){
120     child=getFocus()->getPrev();
121     while(child){
122       if(child->handle(this,FXSEL(SEL_FOCUS_SELF,0),ptr)) return 1;
123       child=child->getPrev();
124       }
125     child=getLast();
126     while(child){
127       if(child->handle(this,FXSEL(SEL_FOCUS_SELF,0),ptr)) return 1;
128       child=child->getPrev();
129       }
130     }
131   return 0;
132   }
133 
134 
135 // Enter:- when inside the popup, all is normal!
onEnter(FXObject * sender,FXSelector sel,void * ptr)136 long FXMenuBar::onEnter(FXObject* sender,FXSelector sel,void* ptr){
137   FXint px,py;
138   FXToolBar::onEnter(sender,sel,ptr);
139   if(!getFocus() || !getFocus()->isActive()) return 1;
140   if(((FXEvent*)ptr)->code==CROSSINGNORMAL){
141     translateCoordinatesTo(px,py,getParent(),((FXEvent*)ptr)->win_x,((FXEvent*)ptr)->win_y);
142     if(contains(px,py) && grabbed()) ungrab();
143     }
144   return 1;
145   }
146 
147 
148 // Leave:- when outside the popup, a click will hide the popup!
onLeave(FXObject * sender,FXSelector sel,void * ptr)149 long FXMenuBar::onLeave(FXObject* sender,FXSelector sel,void* ptr){
150   FXint px,py;
151   FXToolBar::onLeave(sender,sel,ptr);
152   if(!getFocus() || !getFocus()->isActive()) return 1;
153   if(((FXEvent*)ptr)->code==CROSSINGNORMAL){
154     translateCoordinatesTo(px,py,getParent(),((FXEvent*)ptr)->win_x,((FXEvent*)ptr)->win_y);
155     if(!contains(px,py) && !grabbed()) grab();
156     }
157   return 1;
158   }
159 
160 
161 // We're considered inside the menu bar when either
162 // in the bar or in any active menus
contains(FXint parentx,FXint parenty) const163 FXbool FXMenuBar::contains(FXint parentx,FXint parenty) const {
164   FXint x,y;
165   if(FXComposite::contains(parentx,parenty)) return true;
166   if(getFocus()){
167     getParent()->translateCoordinatesTo(x,y,this,parentx,parenty);
168     if(getFocus()->contains(x,y)) return true;
169     }
170   return false;
171   }
172 
173 
174 // Moved while outside
175 // We need to do this because the definition of ``inside'' means
176 // that we're inside even though possibly we're not in THIS window!!!
onMotion(FXObject *,FXSelector,void * ptr)177 long FXMenuBar::onMotion(FXObject*,FXSelector,void* ptr){
178   FXEvent* ev=(FXEvent*)ptr;
179   FXint px,py;
180   if(!getFocus() || !getFocus()->isActive()) return 0;
181   translateCoordinatesTo(px,py,getParent(),ev->win_x,ev->win_y);
182   if(contains(px,py)){
183     if(grabbed()) ungrab();
184     }
185   else{
186     if(!grabbed()) grab();
187     }
188   return 0;
189   }
190 
191 
192 // Button pressed
onButtonPress(FXObject *,FXSelector,void *)193 long FXMenuBar::onButtonPress(FXObject*,FXSelector,void*){
194   FXTRACE((200,"%s::onButtonPress %p\n",getClassName(),this));
195   handle(this,FXSEL(SEL_COMMAND,ID_UNPOST),NULL);
196   return 1;
197   }
198 
199 
200 // Button released
onButtonRelease(FXObject *,FXSelector,void * ptr)201 long FXMenuBar::onButtonRelease(FXObject*,FXSelector,void* ptr){
202   FXEvent* ev=(FXEvent*)ptr;
203   FXTRACE((200,"%s::onButtonRelease %p\n",getClassName(),this));
204   if(ev->moved){ handle(this,FXSEL(SEL_COMMAND,ID_UNPOST),NULL); }
205   return 1;
206   }
207 
208 
209 // Unpost the menu
onCmdUnpost(FXObject *,FXSelector,void *)210 long FXMenuBar::onCmdUnpost(FXObject*,FXSelector,void*){
211   FXTRACE((200,"%s::onCmdUnpost %p\n",getClassName(),this));
212   if(getFocus()) getFocus()->killFocus();
213   //killFocus();
214   return 1;
215   }
216 
217 }
218