1 /********************************************************************************
2 *                                                                               *
3 *                       M e n u   T i t l e   W i d g e t                       *
4 *                                                                               *
5 *********************************************************************************
6 * Copyright (C) 1997,2006 by Jeroen van der Zijp.   All Rights Reserved.        *
7 *********************************************************************************
8 * This library is free software; you can redistribute it and/or                 *
9 * modify it under the terms of the GNU Lesser General Public                    *
10 * License as published by the Free Software Foundation; either                  *
11 * version 2.1 of the License, or (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 GNU             *
16 * Lesser General Public License for more details.                               *
17 *                                                                               *
18 * You should have received a copy of the GNU Lesser General Public              *
19 * License along with this library; if not, write to the Free Software           *
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA.    *
21 *********************************************************************************
22 * $Id: FXMenuTitle.cpp,v 1.52 2006/01/22 17:58:36 fox Exp $                     *
23 ********************************************************************************/
24 #include "xincs.h"
25 #include "fxver.h"
26 #include "fxdefs.h"
27 #include "fxkeys.h"
28 #include "FXHash.h"
29 #include "FXThread.h"
30 #include "FXStream.h"
31 #include "FXString.h"
32 #include "FXSize.h"
33 #include "FXPoint.h"
34 #include "FXRectangle.h"
35 #include "FXRegistry.h"
36 #include "FXAccelTable.h"
37 #include "FXApp.h"
38 #include "FXDCWindow.h"
39 #include "FXFont.h"
40 #include "FXIcon.h"
41 #include "FXMenuPane.h"
42 #include "FXMenuTitle.h"
43 
44 /*
45   Notes:
46   - Help text from constructor is third part; second part should be
47     accelerator key combination.
48   - When menu label changes, hotkey might have to be adjusted.
49   - Fix it so menu stays up when after Alt-F, you press Alt-E.
50   - Menu items should be derived from FXLabel.
51   - Look into SEL_FOCUS_SELF some more...
52   - GUI update disabled while menu is popped up.
53   - Menu shows besides Menu Title if menubar is vertical.
54 */
55 
56 
57 
58 using namespace FX;
59 
60 /*******************************************************************************/
61 
62 namespace FX {
63 
64 // Map
65 FXDEFMAP(FXMenuTitle) FXMenuTitleMap[]={
66   FXMAPFUNC(SEL_PAINT,0,FXMenuTitle::onPaint),
67   FXMAPFUNC(SEL_ENTER,0,FXMenuTitle::onEnter),
68   FXMAPFUNC(SEL_LEAVE,0,FXMenuTitle::onLeave),
69   FXMAPFUNC(SEL_LEFTBUTTONPRESS,0,FXMenuTitle::onLeftBtnPress),
70   FXMAPFUNC(SEL_LEFTBUTTONRELEASE,0,FXMenuTitle::onLeftBtnRelease),
71   FXMAPFUNC(SEL_MIDDLEBUTTONPRESS,0,FXMenuTitle::onDefault),
72   FXMAPFUNC(SEL_MIDDLEBUTTONRELEASE,0,FXMenuTitle::onDefault),
73   FXMAPFUNC(SEL_RIGHTBUTTONPRESS,0,FXMenuTitle::onDefault),
74   FXMAPFUNC(SEL_RIGHTBUTTONRELEASE,0,FXMenuTitle::onDefault),
75   FXMAPFUNC(SEL_KEYPRESS,0,FXMenuTitle::onKeyPress),
76   FXMAPFUNC(SEL_KEYRELEASE,0,FXMenuTitle::onKeyRelease),
77   FXMAPFUNC(SEL_KEYPRESS,FXWindow::ID_HOTKEY,FXMenuTitle::onHotKeyPress),
78   FXMAPFUNC(SEL_KEYRELEASE,FXWindow::ID_HOTKEY,FXMenuTitle::onHotKeyRelease),
79   FXMAPFUNC(SEL_FOCUS_UP,0,FXMenuTitle::onFocusUp),
80   FXMAPFUNC(SEL_FOCUS_DOWN,0,FXMenuTitle::onFocusDown),
81   FXMAPFUNC(SEL_FOCUSIN,0,FXMenuTitle::onFocusIn),
82   FXMAPFUNC(SEL_FOCUSOUT,0,FXMenuTitle::onFocusOut),
83   FXMAPFUNC(SEL_COMMAND,FXWindow::ID_POST,FXMenuTitle::onCmdPost),
84   FXMAPFUNC(SEL_COMMAND,FXWindow::ID_UNPOST,FXMenuTitle::onCmdUnpost),
85   };
86 
87 
88 // Object implementation
FXIMPLEMENT(FXMenuTitle,FXMenuCaption,FXMenuTitleMap,ARRAYNUMBER (FXMenuTitleMap))89 FXIMPLEMENT(FXMenuTitle,FXMenuCaption,FXMenuTitleMap,ARRAYNUMBER(FXMenuTitleMap))
90 
91 
92 
93 // Make a menu title button
94 FXMenuTitle::FXMenuTitle(FXComposite* p,const FXString& text,FXIcon* ic,FXPopup* pup,FXuint opts):
95   FXMenuCaption(p,text,ic,opts){
96   flags|=FLAG_ENABLED;
97   textColor=getApp()->getForeColor();
98   seltextColor=getApp()->getForeColor();
99   selbackColor=getApp()->getBaseColor();
100   pane=pup;
101   }
102 
103 
104 // Create window
create()105 void FXMenuTitle::create(){
106   FXMenuCaption::create();
107   if(pane) pane->create();
108   }
109 
110 
111 // Create window
detach()112 void FXMenuTitle::detach(){
113   FXMenuCaption::detach();
114   if(pane) pane->detach();
115   }
116 
117 
118 // If window can have focus
canFocus() const119 bool FXMenuTitle::canFocus() const { return true; }
120 
121 
122 // Get default width
getDefaultWidth()123 FXint FXMenuTitle::getDefaultWidth(){
124   FXint tw,iw;
125   tw=iw=0;
126   if(!label.empty()) tw=font->getTextWidth(label.text(),label.length());
127   if(icon) iw=icon->getWidth();
128   if(iw && tw) iw+=5;
129   return tw+iw+12;
130   }
131 
132 
133 // Get default height
getDefaultHeight()134 FXint FXMenuTitle::getDefaultHeight(){
135   FXint th,ih;
136   th=ih=0;
137   if(!label.empty()) th=font->getFontHeight();
138   if(icon) ih=icon->getHeight();
139   return FXMAX(th,ih)+4;
140   }
141 
142 
143 // Gained focus
onFocusIn(FXObject * sender,FXSelector sel,void * ptr)144 long FXMenuTitle::onFocusIn(FXObject* sender,FXSelector sel,void* ptr){
145   FXMenuCaption::onFocusIn(sender,sel,ptr);
146   update();
147   return 1;
148   }
149 
150 
151 // Lost focus
onFocusOut(FXObject * sender,FXSelector sel,void * ptr)152 long FXMenuTitle::onFocusOut(FXObject* sender,FXSelector sel,void* ptr){
153   FXMenuCaption::onFocusOut(sender,sel,ptr);
154   update();
155   return 1;
156   }
157 
158 
159 // Enter
onEnter(FXObject * sender,FXSelector sel,void * ptr)160 long FXMenuTitle::onEnter(FXObject* sender,FXSelector sel,void* ptr){
161   FXMenuCaption::onEnter(sender,sel,ptr);
162   if(!isEnabled()) return 1;
163   if(canFocus() && getParent()->getFocus()) setFocus();
164   update();
165   return 1;
166   }
167 
168 
169 // Leave
onLeave(FXObject * sender,FXSelector sel,void * ptr)170 long FXMenuTitle::onLeave(FXObject* sender,FXSelector sel,void* ptr){
171   FXMenuCaption::onLeave(sender,sel,ptr);
172   if(!isEnabled()) return 1;
173   update();
174   return 1;
175   }
176 
177 
178 // When pressed, perform ungrab, then process normally
onLeftBtnPress(FXObject *,FXSelector,void * ptr)179 long FXMenuTitle::onLeftBtnPress(FXObject*,FXSelector,void* ptr){
180   handle(this,FXSEL(SEL_FOCUS_SELF,0),ptr);
181   if(isEnabled()){
182     if(target && target->tryHandle(this,FXSEL(SEL_LEFTBUTTONPRESS,message),ptr)) return 1;
183     if(flags&FLAG_ACTIVE){
184       handle(this,FXSEL(SEL_COMMAND,ID_UNPOST),NULL);
185       }
186     else{
187       handle(this,FXSEL(SEL_COMMAND,ID_POST),NULL);
188       }
189     return 1;
190     }
191   return 0;
192   }
193 
194 
195 // Released left button
onLeftBtnRelease(FXObject *,FXSelector,void * ptr)196 long FXMenuTitle::onLeftBtnRelease(FXObject*,FXSelector,void* ptr){
197   FXEvent* event=(FXEvent*)ptr;
198   if(isEnabled()){
199     if(target && target->tryHandle(this,FXSEL(SEL_LEFTBUTTONRELEASE,message),ptr)) return 1;
200     if(event->moved){
201       handle(this,FXSEL(SEL_COMMAND,ID_UNPOST),ptr);
202       }
203     return 1;
204     }
205   return 0;
206   }
207 
208 
209 // Keyboard press; forward to menu pane
onKeyPress(FXObject *,FXSelector sel,void * ptr)210 long FXMenuTitle::onKeyPress(FXObject*,FXSelector sel,void* ptr){
211   if(isEnabled()){
212     FXTRACE((200,"%s::onKeyPress %p keysym=0x%04x state=%04x\n",getClassName(),this,((FXEvent*)ptr)->code,((FXEvent*)ptr)->state));
213     if(target && target->tryHandle(this,FXSEL(SEL_KEYPRESS,message),ptr)) return 1;
214     if(pane && pane->shown() && pane->handle(pane,sel,ptr)) return 1;
215     }
216   return 0;
217   }
218 
219 
220 // Keyboard release; forward to menu pane
onKeyRelease(FXObject *,FXSelector sel,void * ptr)221 long FXMenuTitle::onKeyRelease(FXObject*,FXSelector sel,void* ptr){
222   if(isEnabled()){
223     FXTRACE((200,"%s::onKeyRelease %p keysym=0x%04x state=%04x\n",getClassName(),this,((FXEvent*)ptr)->code,((FXEvent*)ptr)->state));
224     if(target && target->tryHandle(this,FXSEL(SEL_KEYRELEASE,message),ptr)) return 1;
225     if(pane && pane->shown() && pane->handle(pane,sel,ptr)) return 1;
226     }
227   return 0;
228   }
229 
230 
231 // Hot key combination pressed
onHotKeyPress(FXObject *,FXSelector,void * ptr)232 long FXMenuTitle::onHotKeyPress(FXObject*,FXSelector,void* ptr){
233   FXTRACE((200,"%s::onHotKeyPress %p\n",getClassName(),this));
234   handle(this,FXSEL(SEL_FOCUS_SELF,0),ptr);
235   return 1;
236   }
237 
238 
239 // Hot key combination released
onHotKeyRelease(FXObject *,FXSelector,void *)240 long FXMenuTitle::onHotKeyRelease(FXObject*,FXSelector,void*){
241   FXTRACE((200,"%s::onHotKeyRelease %p\n",getClassName(),this));
242   if(isEnabled()){
243     if(flags&FLAG_ACTIVE){
244       handle(this,FXSEL(SEL_COMMAND,ID_UNPOST),NULL);
245       }
246     else{
247       handle(this,FXSEL(SEL_COMMAND,ID_POST),NULL);
248       }
249     }
250   return 1;
251   }
252 
253 
254 // Post the menu
onCmdPost(FXObject *,FXSelector,void *)255 long FXMenuTitle::onCmdPost(FXObject*,FXSelector,void*){
256   FXint x,y,side;
257   if(pane && !pane->shown()){
258     translateCoordinatesTo(x,y,getRoot(),0,0);
259     side=getParent()->getLayoutHints();
260     if(side&LAYOUT_SIDE_LEFT){  // Vertical
261       y-=1;
262       if(side&LAYOUT_SIDE_BOTTOM){      // On right
263         x-=pane->getDefaultWidth();
264         }
265       else{                             // On left
266         x+=width;
267         }
268       }
269     else{                       // Horizontal
270       x-=1;
271       if(side&LAYOUT_SIDE_BOTTOM){      // On bottom
272         y-=pane->getDefaultHeight();
273         }
274       else{                             // On top
275         y+=height;
276         }
277       }
278     pane->popup(getParent(),x,y);
279     if(!getParent()->grabbed()) getParent()->grab();
280     }
281   flags&=~FLAG_UPDATE;
282   flags|=FLAG_ACTIVE;
283   update();
284   return 1;
285   }
286 
287 
288 // Unpost the menu
onCmdUnpost(FXObject *,FXSelector,void *)289 long FXMenuTitle::onCmdUnpost(FXObject*,FXSelector,void*){
290   if(pane && pane->shown()){
291     pane->popdown();
292     if(getParent()->grabbed()) getParent()->ungrab();
293     }
294   flags|=FLAG_UPDATE;
295   flags&=~FLAG_ACTIVE;
296   update();
297   return 1;
298   }
299 
300 
301 // Focus moved down
onFocusDown(FXObject *,FXSelector,void *)302 long FXMenuTitle::onFocusDown(FXObject*,FXSelector,void*){
303   if(pane && !pane->shown()){
304     handle(this,FXSEL(SEL_COMMAND,ID_POST),NULL);
305     return 1;
306     }
307   return 0;
308   }
309 
310 
311 // Focus moved up
onFocusUp(FXObject *,FXSelector,void *)312 long FXMenuTitle::onFocusUp(FXObject*,FXSelector,void*){
313   if(pane && pane->shown()){
314     handle(this,FXSEL(SEL_COMMAND,ID_UNPOST),NULL);
315     return 1;
316     }
317   return 0;
318   }
319 
320 
321 // Into focus chain
setFocus()322 void FXMenuTitle::setFocus(){
323   FXWindow *menuitem=getParent()->getFocus();
324   FXbool active=menuitem && menuitem->isActive();
325   FXMenuCaption::setFocus();
326   if(active) handle(this,FXSEL(SEL_COMMAND,ID_POST),NULL);
327   }
328 
329 
330 
331 // Out of focus chain
killFocus()332 void FXMenuTitle::killFocus(){
333   FXMenuCaption::killFocus();
334   handle(this,FXSEL(SEL_COMMAND,ID_UNPOST),NULL);
335   }
336 
337 
338 // Change the popup menu
setMenu(FXPopup * pup)339 void FXMenuTitle::setMenu(FXPopup *pup){
340   if(pup!=pane){
341     pane=pup;
342     recalc();
343     }
344   }
345 
346 
347 // Handle repaint
onPaint(FXObject *,FXSelector,void * ptr)348 long FXMenuTitle::onPaint(FXObject*,FXSelector,void* ptr){
349   FXEvent *ev=(FXEvent*)ptr;
350   FXDCWindow dc(this,ev);
351   FXint xx,yy;
352   dc.setFont(font);
353   xx=6;
354   yy=0;
355   if(isEnabled()){
356     if(isActive()){
357       dc.setForeground(selbackColor);
358       dc.fillRectangle(1,1,width-2,height-2);
359       //drawSunkenRectangle(dc,0,0,width,height);
360       dc.setForeground(shadowColor);
361       dc.fillRectangle(0,0,width,1);
362       dc.fillRectangle(0,0,1,height);
363       dc.setForeground(hiliteColor);
364       dc.fillRectangle(0,height-1,width,1);
365       dc.fillRectangle(width-1,0,1,height);
366       xx++;
367       yy++;
368       }
369     else if(underCursor()){
370       dc.setForeground(backColor);
371       dc.fillRectangle(1,1,width-2,height-2);
372       //drawRaisedRectangle(dc,0,0,width,height);
373       dc.setForeground(shadowColor);
374       dc.fillRectangle(0,height-1,width,1);
375       dc.fillRectangle(width-1,0,1,height);
376       dc.setForeground(hiliteColor);
377       dc.fillRectangle(0,0,width,1);
378       dc.fillRectangle(0,0,1,height);
379       }
380     else{
381       dc.setForeground(backColor);
382       dc.fillRectangle(0,0,width,height);
383       }
384     if(icon){
385       dc.drawIcon(icon,xx,yy+(height-icon->getHeight())/2);
386       xx+=5+icon->getWidth();
387       }
388     if(!label.empty()){
389       yy+=font->getFontAscent()+(height-font->getFontHeight())/2;
390       dc.setForeground(isActive() ? seltextColor : textColor);
391       dc.drawText(xx,yy,label);
392       if(0<=hotoff){
393         dc.fillRectangle(xx+font->getTextWidth(&label[0],hotoff),yy+1,font->getTextWidth(&label[hotoff],wclen(&label[hotoff])),1);
394         }
395       }
396     }
397   else{
398     dc.setForeground(backColor);
399     dc.fillRectangle(0,0,width,height);
400     if(icon){
401       dc.drawIconSunken(icon,xx,yy+(height-icon->getHeight())/2);
402       xx+=5+icon->getWidth();
403       }
404     if(!label.empty()){
405       yy+=font->getFontAscent()+(height-font->getFontHeight())/2;
406       dc.setForeground(hiliteColor);
407       dc.drawText(xx+1,yy+1,label);
408       if(0<=hotoff){
409         dc.fillRectangle(xx+font->getTextWidth(&label[0],hotoff),yy+1,font->getTextWidth(&label[hotoff],wclen(&label[hotoff])),1);
410         }
411       dc.setForeground(shadowColor);
412       dc.drawText(xx,yy,label);
413       if(0<=hotoff){
414         dc.fillRectangle(xx+font->getTextWidth(&label[0],hotoff),yy+1,font->getTextWidth(&label[hotoff],wclen(&label[hotoff])),1);
415         }
416       }
417     }
418   return 1;
419   }
420 
421 
422 // Test if logically inside
contains(FXint parentx,FXint parenty) const423 bool FXMenuTitle::contains(FXint parentx,FXint parenty) const {
424   FXint x,y;
425   if(FXMenuCaption::contains(parentx,parenty)) return true;
426   if(getMenu() && getMenu()->shown()){
427     getParent()->translateCoordinatesTo(x,y,getRoot(),parentx,parenty);
428     if(getMenu()->contains(x,y)) return true;
429     }
430   return false;
431   }
432 
433 
434 // Save object to stream
save(FXStream & store) const435 void FXMenuTitle::save(FXStream& store) const {
436   FXMenuCaption::save(store);
437   store << pane;
438   }
439 
440 
441 // Load object from stream
load(FXStream & store)442 void FXMenuTitle::load(FXStream& store){
443   FXMenuCaption::load(store);
444   store >> pane;
445   }
446 
447 
448 // Delete it
~FXMenuTitle()449 FXMenuTitle::~FXMenuTitle(){
450   pane=(FXMenuPane*)-1L;
451   }
452 
453 }
454 
455