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