1 /********************************************************************************
2 * *
3 * P o p u p W i n d o w O b j e c 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 "fxkeys.h"
26 #include "FXArray.h"
27 #include "FXHash.h"
28 #include "FXMutex.h"
29 #include "FXStream.h"
30 #include "FXString.h"
31 #include "FXSize.h"
32 #include "FXPoint.h"
33 #include "FXRectangle.h"
34 #include "FXStringDictionary.h"
35 #include "FXSettings.h"
36 #include "FXRegistry.h"
37 #include "FXEvent.h"
38 #include "FXWindow.h"
39 #include "FXDCWindow.h"
40 #include "FXApp.h"
41 #include "FXPopup.h"
42 #include "fxpriv.h"
43
44 /*
45 Notes:
46 - FXPopup now supports stretchable children; this is useful when popups
47 are displayed & forced to other size than default, e.g. for a ComboBox!
48 - LAYOUT_FIX_xxx takes precedence over PACK_UNIFORM_xxx!!
49 - Perhaps the grab owner could be equal to the owner?
50 - Perhaps popup should resize when recalc() has been called!
51 - Need to implement Mikael Aronssen's suggestion to make it easier to
52 run FXPopup in a modal loop. This shouls also return a code, just
53 like FXDialogBox does. Thus it becomes very straighforward to run
54 a popup menu just to get a single choice value back.
55 - Do not assume root window is at (0,0); multi-monitor machines may
56 have secondary monitor anywhere relative to primary display.
57 - If any child is resized due to GUI update, and FXPopup is in
58 POPUP_SHRINKWRAP mode, ID_LAYOUT causes resize() of the popup it
59 self as well as layout() of its interior. For normal shell windows
60 only layout() is called.
61 */
62
63
64 // Frame styles
65 #define FRAME_MASK (FRAME_SUNKEN|FRAME_RAISED|FRAME_THICK)
66
67 using namespace FX;
68
69 /*******************************************************************************/
70
71 namespace FX {
72
73
74 // Map
75 FXDEFMAP(FXPopup) FXPopupMap[]={
76 FXMAPFUNC(SEL_PAINT,0,FXPopup::onPaint),
77 FXMAPFUNC(SEL_ENTER,0,FXPopup::onEnter),
78 FXMAPFUNC(SEL_LEAVE,0,FXPopup::onLeave),
79 FXMAPFUNC(SEL_MOTION,0,FXPopup::onMotion),
80 FXMAPFUNC(SEL_MAP,0,FXPopup::onMap),
81 FXMAPFUNC(SEL_FOCUS_NEXT,0,FXPopup::onDefault),
82 FXMAPFUNC(SEL_FOCUS_PREV,0,FXPopup::onDefault),
83 FXMAPFUNC(SEL_FOCUS_UP,0,FXPopup::onFocusUp),
84 FXMAPFUNC(SEL_FOCUS_DOWN,0,FXPopup::onFocusDown),
85 FXMAPFUNC(SEL_FOCUS_LEFT,0,FXPopup::onFocusLeft),
86 FXMAPFUNC(SEL_FOCUS_RIGHT,0,FXPopup::onFocusRight),
87 FXMAPFUNC(SEL_LEFTBUTTONPRESS,0,FXPopup::onButtonPress),
88 FXMAPFUNC(SEL_LEFTBUTTONRELEASE,0,FXPopup::onButtonRelease),
89 FXMAPFUNC(SEL_MIDDLEBUTTONPRESS,0,FXPopup::onButtonPress),
90 FXMAPFUNC(SEL_MIDDLEBUTTONRELEASE,0,FXPopup::onButtonRelease),
91 FXMAPFUNC(SEL_RIGHTBUTTONPRESS,0,FXPopup::onButtonPress),
92 FXMAPFUNC(SEL_RIGHTBUTTONRELEASE,0,FXPopup::onButtonRelease),
93 FXMAPFUNC(SEL_KEYPRESS,0,FXPopup::onKeyPress),
94 FXMAPFUNC(SEL_KEYRELEASE,0,FXPopup::onKeyRelease),
95 FXMAPFUNC(SEL_UNGRABBED,0,FXPopup::onUngrabbed),
96 FXMAPFUNC(SEL_CHORE,FXPopup::ID_LAYOUT,FXPopup::onLayout),
97 FXMAPFUNC(SEL_COMMAND,FXWindow::ID_UNPOST,FXPopup::onCmdUnpost),
98 FXMAPFUNCS(SEL_COMMAND,FXPopup::ID_CHOICE,FXPopup::ID_CHOICE+999,FXPopup::onCmdChoice),
99 };
100
101
102 // Object implementation
FXIMPLEMENT(FXPopup,FXShell,FXPopupMap,ARRAYNUMBER (FXPopupMap))103 FXIMPLEMENT(FXPopup,FXShell,FXPopupMap,ARRAYNUMBER(FXPopupMap))
104
105
106 // Deserialization
107 FXPopup::FXPopup():prevActive(NULL),nextActive(NULL){
108 }
109
110
111 // Transient window used for popups
FXPopup(FXWindow * own,FXuint opts,FXint x,FXint y,FXint w,FXint h)112 FXPopup::FXPopup(FXWindow* own,FXuint opts,FXint x,FXint y,FXint w,FXint h):FXShell(own,opts,x,y,w,h),prevActive(NULL),nextActive(NULL){
113 defaultCursor=getApp()->getDefaultCursor(DEF_RARROW_CURSOR);
114 dragCursor=getApp()->getDefaultCursor(DEF_RARROW_CURSOR);
115 flags|=FLAG_ENABLED;
116 grabowner=NULL;
117 baseColor=getApp()->getBaseColor();
118 hiliteColor=getApp()->getHiliteColor();
119 shadowColor=getApp()->getShadowColor();
120 borderColor=getApp()->getBorderColor();
121 border=(options&FRAME_THICK)?2:(options&(FRAME_SUNKEN|FRAME_RAISED))?1:0;
122 }
123
124
125 // Popups do override-redirect
doesOverrideRedirect() const126 FXbool FXPopup::doesOverrideRedirect() const {
127 return true;
128 }
129
130
131 // Popups do save-unders
doesSaveUnder() const132 FXbool FXPopup::doesSaveUnder() const {
133 return true;
134 }
135
136
137 #ifdef WIN32
138
GetClass() const139 const void* FXPopup::GetClass() const { return TEXT("FXPopup"); }
140
141 #endif
142
143
144 // Setting focus causes keyboard grab, because all keyboard
145 // events need to be reported to the active popup.
setFocus()146 void FXPopup::setFocus(){
147 FXShell::setFocus();
148 //grabKeyboard();
149 }
150
151
152 // Killing the focus should revert the keyboard grab to the
153 // previously active popup, or release the keyboard grab
killFocus()154 void FXPopup::killFocus(){
155 FXShell::killFocus();
156 //if(prevActive){
157 // prevActive->setFocus();
158 // }
159 //else{
160 // ungrabKeyboard();
161 // }
162 }
163
164
165 // Get owner; if it has none, it's owned by itself
getGrabOwner() const166 FXWindow* FXPopup::getGrabOwner() const {
167 return grabowner ? grabowner : (FXWindow*)this;
168 }
169
170
171 // Get width
getDefaultWidth()172 FXint FXPopup::getDefaultWidth(){
173 FXWindow* child;
174 FXint w,wmax,wcum;
175 FXuint hints;
176 wmax=wcum=0;
177 for(child=getFirst(); child; child=child->getNext()){
178 if(child->shown()){
179 hints=child->getLayoutHints();
180 if(hints&LAYOUT_FIX_WIDTH) w=child->getWidth();
181 else w=child->getDefaultWidth();
182 if(wmax<w) wmax=w;
183 }
184 }
185 for(child=getFirst(); child; child=child->getNext()){
186 if(child->shown()){
187 hints=child->getLayoutHints();
188 if(hints&LAYOUT_FIX_WIDTH) w=child->getWidth();
189 else if(options&PACK_UNIFORM_WIDTH) w=wmax;
190 else w=child->getDefaultWidth();
191 wcum+=w;
192 }
193 }
194 if(!(options&POPUP_HORIZONTAL)) wcum=wmax;
195 return wcum+(border<<1);
196 }
197
198
199 // Get height
getDefaultHeight()200 FXint FXPopup::getDefaultHeight(){
201 FXWindow* child;
202 FXint h,hmax,hcum;
203 FXuint hints;
204 hmax=hcum=0;
205 for(child=getFirst(); child; child=child->getNext()){
206 if(child->shown()){
207 hints=child->getLayoutHints();
208 if(hints&LAYOUT_FIX_HEIGHT) h=child->getHeight();
209 else h=child->getDefaultHeight();
210 if(hmax<h) hmax=h;
211 }
212 }
213 for(child=getFirst(); child; child=child->getNext()){
214 if(child->shown()){
215 hints=child->getLayoutHints();
216 if(hints&LAYOUT_FIX_HEIGHT) h=child->getHeight();
217 else if(options&PACK_UNIFORM_HEIGHT) h=hmax;
218 else h=child->getDefaultHeight();
219 hcum+=h;
220 }
221 }
222 if(options&POPUP_HORIZONTAL) hcum=hmax;
223 return hcum+(border<<1);
224 }
225
226
227 // Recalculate layout
layout()228 void FXPopup::layout(){
229 FXWindow *child;
230 FXint w,h,x,y,remain,t;
231 FXuint hints;
232 FXint sumexpand=0;
233 FXint numexpand=0;
234 FXint mw=0;
235 FXint mh=0;
236 FXint e=0;
237
238 // Horizontal
239 if(options&POPUP_HORIZONTAL){
240
241 // Get maximum size if uniform packed
242 if(options&PACK_UNIFORM_WIDTH) mw=maxChildWidth();
243
244 // Space available
245 remain=width-(border<<1);
246
247 // Find number of paddable children and total space remaining
248 for(child=getFirst(); child; child=child->getNext()){
249 if(child->shown()){
250 hints=child->getLayoutHints();
251 if(hints&LAYOUT_FIX_WIDTH) w=child->getWidth();
252 else if(options&PACK_UNIFORM_WIDTH) w=mw;
253 else w=child->getDefaultWidth();
254 FXASSERT(w>=0);
255 if((hints&LAYOUT_FILL_X) && !(hints&LAYOUT_FIX_WIDTH)){
256 sumexpand+=w;
257 numexpand++;
258 }
259 else{
260 remain-=w;
261 }
262 }
263 }
264
265 // Do the layout
266 for(child=getFirst(),x=border; child; child=child->getNext()){
267 if(child->shown()){
268 hints=child->getLayoutHints();
269 if(hints&LAYOUT_FIX_WIDTH) w=child->getWidth();
270 else if(options&PACK_UNIFORM_WIDTH) w=mw;
271 else w=child->getDefaultWidth();
272 if((hints&LAYOUT_FILL_X) && !(hints&LAYOUT_FIX_WIDTH)){
273 if(sumexpand>0){
274 t=w*remain;
275 FXASSERT(sumexpand>0);
276 w=t/sumexpand;
277 e+=t%sumexpand;
278 if(e>=sumexpand){w++;e-=sumexpand;}
279 }
280 else{
281 FXASSERT(numexpand>0);
282 w=remain/numexpand;
283 e+=remain%numexpand;
284 if(e>=numexpand){w++;e-=numexpand;}
285 }
286 }
287 child->position(x,border,w,height-(border<<1));
288 x+=w;
289 }
290 }
291 }
292
293 // Vertical
294 else{
295
296 // Get maximum size if uniform packed
297 if(options&PACK_UNIFORM_HEIGHT) mh=maxChildHeight();
298
299 // Space available
300 remain=height-(border<<1);
301
302 // Find number of paddable children and total space remaining
303 for(child=getFirst(); child; child=child->getNext()){
304 if(child->shown()){
305 hints=child->getLayoutHints();
306 if(hints&LAYOUT_FIX_HEIGHT) h=child->getHeight();
307 else if(options&PACK_UNIFORM_HEIGHT) h=mh;
308 else h=child->getDefaultHeight();
309 FXASSERT(h>=0);
310 if((hints&LAYOUT_FILL_Y) && !(hints&LAYOUT_FIX_HEIGHT)){
311 sumexpand+=h;
312 numexpand++;
313 }
314 else{
315 remain-=h;
316 }
317 }
318 }
319
320 // Do the layout
321 for(child=getFirst(),y=border; child; child=child->getNext()){
322 if(child->shown()){
323 hints=child->getLayoutHints();
324 if(hints&LAYOUT_FIX_HEIGHT) h=child->getHeight();
325 else if(options&PACK_UNIFORM_HEIGHT) h=mh;
326 else h=child->getDefaultHeight();
327 if((hints&LAYOUT_FILL_Y) && !(hints&LAYOUT_FIX_HEIGHT)){
328 if(sumexpand>0){
329 t=h*remain;
330 FXASSERT(sumexpand>0);
331 h=t/sumexpand;
332 e+=t%sumexpand;
333 if(e>=sumexpand){h++;e-=sumexpand;}
334 }
335 else{
336 FXASSERT(numexpand>0);
337 h=remain/numexpand;
338 e+=remain%numexpand;
339 if(e>=numexpand){h++;e-=numexpand;}
340 }
341 }
342 child->position(border,y,width-(border<<1),h);
343 y+=h;
344 }
345 }
346 }
347 flags&=~FLAG_DIRTY;
348 }
349
350
drawBorderRectangle(FXDCWindow & dc,FXint x,FXint y,FXint w,FXint h)351 void FXPopup::drawBorderRectangle(FXDCWindow& dc,FXint x,FXint y,FXint w,FXint h){
352 dc.setForeground(borderColor);
353 dc.drawRectangle(x,y,w-1,h-1);
354 }
355
356
drawRaisedRectangle(FXDCWindow & dc,FXint x,FXint y,FXint w,FXint h)357 void FXPopup::drawRaisedRectangle(FXDCWindow& dc,FXint x,FXint y,FXint w,FXint h){
358 dc.setForeground(shadowColor);
359 dc.fillRectangle(x,y+h-1,w,1);
360 dc.fillRectangle(x+w-1,y,1,h);
361 dc.setForeground(hiliteColor);
362 dc.fillRectangle(x,y,w,1);
363 dc.fillRectangle(x,y,1,h);
364 }
365
366
drawSunkenRectangle(FXDCWindow & dc,FXint x,FXint y,FXint w,FXint h)367 void FXPopup::drawSunkenRectangle(FXDCWindow& dc,FXint x,FXint y,FXint w,FXint h){
368 dc.setForeground(shadowColor);
369 dc.fillRectangle(x,y,w,1);
370 dc.fillRectangle(x,y,1,h);
371 dc.setForeground(hiliteColor);
372 dc.fillRectangle(x,y+h-1,w,1);
373 dc.fillRectangle(x+w-1,y,1,h);
374 }
375
376
drawRidgeRectangle(FXDCWindow & dc,FXint x,FXint y,FXint w,FXint h)377 void FXPopup::drawRidgeRectangle(FXDCWindow& dc,FXint x,FXint y,FXint w,FXint h){
378 dc.setForeground(hiliteColor);
379 dc.fillRectangle(x,y,w,1);
380 dc.fillRectangle(x,y,1,h);
381 dc.fillRectangle(x+1,y+h-2,w-2,1);
382 dc.fillRectangle(x+w-2,y+1,1,h-2);
383 dc.setForeground(shadowColor);
384 dc.fillRectangle(x+1,y+1,w-3,1);
385 dc.fillRectangle(x+1,y+1,1,h-3);
386 dc.fillRectangle(x,y+h-1,w,1);
387 dc.fillRectangle(x+w-1,y,1,h);
388 }
389
390
drawGrooveRectangle(FXDCWindow & dc,FXint x,FXint y,FXint w,FXint h)391 void FXPopup::drawGrooveRectangle(FXDCWindow& dc,FXint x,FXint y,FXint w,FXint h){
392 dc.setForeground(shadowColor);
393 dc.fillRectangle(x,y,w,1);
394 dc.fillRectangle(x,y,1,h);
395 dc.fillRectangle(x+1,y+h-2,w-2,1);
396 dc.fillRectangle(x+w-2,y+1,1,h-2);
397 dc.setForeground(hiliteColor);
398 dc.fillRectangle(x+1,y+1,w-2,1);
399 dc.fillRectangle(x+1,y+1,1,h-2);
400 dc.fillRectangle(x+1,y+h-1,w,1);
401 dc.fillRectangle(x+w-1,y+1,1,h);
402 }
403
404
drawDoubleRaisedRectangle(FXDCWindow & dc,FXint x,FXint y,FXint w,FXint h)405 void FXPopup::drawDoubleRaisedRectangle(FXDCWindow& dc,FXint x,FXint y,FXint w,FXint h){
406 dc.setForeground(baseColor);
407 dc.fillRectangle(x,y,w-1,1);
408 dc.fillRectangle(x,y,1,h-1);
409 dc.setForeground(hiliteColor);
410 dc.fillRectangle(x+1,y+1,w-2,1);
411 dc.fillRectangle(x+1,y+1,1,h-2);
412 dc.setForeground(shadowColor);
413 dc.fillRectangle(x+1,y+h-2,w-2,1);
414 dc.fillRectangle(x+w-2,y+1,1,h-1);
415 dc.setForeground(borderColor);
416 dc.fillRectangle(x,y+h-1,w,1);
417 dc.fillRectangle(x+w-1,y,1,h);
418 }
419
420
drawDoubleSunkenRectangle(FXDCWindow & dc,FXint x,FXint y,FXint w,FXint h)421 void FXPopup::drawDoubleSunkenRectangle(FXDCWindow& dc,FXint x,FXint y,FXint w,FXint h){
422 dc.setForeground(shadowColor);
423 dc.fillRectangle(x,y,w-1,1);
424 dc.fillRectangle(x,y,1,h-1);
425 dc.setForeground(borderColor);
426 dc.fillRectangle(x+1,y+1,w-3,1);
427 dc.fillRectangle(x+1,y+1,1,h-3);
428 dc.setForeground(hiliteColor);
429 dc.fillRectangle(x,y+h-1,w,1);
430 dc.fillRectangle(x+w-1,y,1,h);
431 dc.setForeground(baseColor);
432 dc.fillRectangle(x+1,y+h-2,w-2,1);
433 dc.fillRectangle(x+w-2,y+1,1,h-2);
434 }
435
436
437 // Draw border
drawFrame(FXDCWindow & dc,FXint x,FXint y,FXint w,FXint h)438 void FXPopup::drawFrame(FXDCWindow& dc,FXint x,FXint y,FXint w,FXint h){
439 switch(options&FRAME_MASK){
440 case FRAME_LINE: drawBorderRectangle(dc,x,y,w,h); break;
441 case FRAME_SUNKEN: drawSunkenRectangle(dc,x,y,w,h); break;
442 case FRAME_RAISED: drawRaisedRectangle(dc,x,y,w,h); break;
443 case FRAME_GROOVE: drawGrooveRectangle(dc,x,y,w,h); break;
444 case FRAME_RIDGE: drawRidgeRectangle(dc,x,y,w,h); break;
445 case FRAME_SUNKEN|FRAME_THICK: drawDoubleSunkenRectangle(dc,x,y,w,h); break;
446 case FRAME_RAISED|FRAME_THICK: drawDoubleRaisedRectangle(dc,x,y,w,h); break;
447 }
448 }
449
450
451 // Handle repaint
onPaint(FXObject *,FXSelector,void * ptr)452 long FXPopup::onPaint(FXObject*,FXSelector,void* ptr){
453 FXEvent *ev=(FXEvent*)ptr;
454 FXDCWindow dc(this,ev);
455 dc.setForeground(backColor);
456 dc.fillRectangle(border,border,width-(border<<1),height-(border<<1));
457 drawFrame(dc,0,0,width,height);
458 return 1;
459 }
460
461
462 // Moving focus up
onFocusUp(FXObject * sender,FXSelector sel,void * ptr)463 long FXPopup::onFocusUp(FXObject* sender,FXSelector sel,void* ptr){
464 if(!(options&POPUP_HORIZONTAL)) return FXPopup::onFocusPrev(sender,sel,ptr);
465 return 0;
466 }
467
468 // Moving focus down
onFocusDown(FXObject * sender,FXSelector sel,void * ptr)469 long FXPopup::onFocusDown(FXObject* sender,FXSelector sel,void* ptr){
470 if(!(options&POPUP_HORIZONTAL)) return FXPopup::onFocusNext(sender,sel,ptr);
471 return 0;
472 }
473
474 // Moving focus left
onFocusLeft(FXObject * sender,FXSelector sel,void * ptr)475 long FXPopup::onFocusLeft(FXObject* sender,FXSelector sel,void* ptr){
476 if(options&POPUP_HORIZONTAL) return FXPopup::onFocusPrev(sender,sel,ptr);
477 return 0;
478 }
479
480 // Moving focus right
onFocusRight(FXObject * sender,FXSelector sel,void * ptr)481 long FXPopup::onFocusRight(FXObject* sender,FXSelector sel,void* ptr){
482 if(options&POPUP_HORIZONTAL) return FXPopup::onFocusNext(sender,sel,ptr);
483 return 0;
484 }
485
486 // Focus moved down; wrap back to begin if at end
onFocusNext(FXObject *,FXSelector,void * ptr)487 long FXPopup::onFocusNext(FXObject*,FXSelector,void* ptr){
488 FXWindow *child;
489 if(getFocus()){
490 child=getFocus()->getNext();
491 while(child){
492 if(child->shown()){
493 if(child->handle(this,FXSEL(SEL_FOCUS_SELF,0),ptr)) return 1;
494 }
495 child=child->getNext();
496 }
497 }
498 child=getFirst();
499 while(child){
500 if(child->shown()){
501 if(child->handle(this,FXSEL(SEL_FOCUS_SELF,0),ptr)) return 1;
502 }
503 child=child->getNext();
504 }
505 return 0;
506 }
507
508
509 // Focus moved up; wrap back to end if at begin
onFocusPrev(FXObject *,FXSelector,void * ptr)510 long FXPopup::onFocusPrev(FXObject*,FXSelector,void* ptr){
511 FXWindow *child;
512 if(getFocus()){
513 child=getFocus()->getPrev();
514 while(child){
515 if(child->shown()){
516 if(child->handle(this,FXSEL(SEL_FOCUS_SELF,0),ptr)) return 1;
517 }
518 child=child->getPrev();
519 }
520 }
521 child=getLast();
522 while(child){
523 if(child->shown()){
524 if(child->handle(this,FXSEL(SEL_FOCUS_SELF,0),ptr)) return 1;
525 }
526 child=child->getPrev();
527 }
528 return 0;
529 }
530
531
532 // Moved into the popup:- tell the target
onEnter(FXObject * sender,FXSelector sel,void * ptr)533 long FXPopup::onEnter(FXObject* sender,FXSelector sel,void* ptr){
534 FXint px,py;
535 FXShell::onEnter(sender,sel,ptr);
536 translateCoordinatesTo(px,py,getParent(),((FXEvent*)ptr)->win_x,((FXEvent*)ptr)->win_y);
537 if(contains(px,py) && getGrabOwner()->grabbed()) getGrabOwner()->ungrab();
538 return 1;
539 }
540
541
542 // Moved outside the popup:- tell the target
onLeave(FXObject * sender,FXSelector sel,void * ptr)543 long FXPopup::onLeave(FXObject* sender,FXSelector sel,void* ptr){
544 FXint px,py;
545 FXShell::onLeave(sender,sel,ptr);
546 translateCoordinatesTo(px,py,getParent(),((FXEvent*)ptr)->win_x,((FXEvent*)ptr)->win_y);
547 if(!contains(px,py) && shown() && !getGrabOwner()->grabbed() && getGrabOwner()->shown()) getGrabOwner()->grab();
548 return 1;
549 }
550
551
552 // Moved (while outside the popup):- tell the target
onMotion(FXObject *,FXSelector,void * ptr)553 long FXPopup::onMotion(FXObject*,FXSelector,void* ptr){
554 FXEvent* ev=(FXEvent*)ptr;
555 FXint xx,yy;
556 if(contains(ev->root_x,ev->root_y)){
557 if(getGrabOwner()->grabbed()) getGrabOwner()->ungrab();
558 }
559 else{
560 getGrabOwner()->getParent()->translateCoordinatesFrom(xx,yy,getRoot(),ev->root_x,ev->root_y);
561 if(!getGrabOwner()->contains(xx,yy)){
562 if(!getGrabOwner()->grabbed() && getGrabOwner()->shown()) getGrabOwner()->grab();
563 }
564 }
565 return 1;
566 }
567
568
569 // Window may have appeared under the cursor, so ungrab if it was grabbed
onMap(FXObject * sender,FXSelector sel,void * ptr)570 long FXPopup::onMap(FXObject* sender,FXSelector sel,void* ptr){
571 FXint x,y; FXuint buttons;
572 FXShell::onMap(sender,sel,ptr);
573 getCursorPosition(x,y,buttons);
574 if(0<=x && 0<=y && x<width && y<height){
575 FXTRACE((200,"under cursor\n"));
576 if(getGrabOwner()->grabbed()) getGrabOwner()->ungrab();
577 }
578 return 1;
579 }
580
581
582 // Perform layout; return 0 because no GUI update is needed
onLayout(FXObject *,FXSelector,void *)583 long FXPopup::onLayout(FXObject*,FXSelector,void*){
584 if(getShrinkWrap()){
585 resize(getDefaultWidth(),getDefaultHeight());
586 }
587 else{
588 layout();
589 }
590 return 0;
591 }
592
593
594 // Pressed button outside popup
onButtonPress(FXObject *,FXSelector,void *)595 long FXPopup::onButtonPress(FXObject*,FXSelector,void*){
596 FXTRACE((200,"%s::onButtonPress %p\n",getClassName(),this));
597 handle(this,FXSEL(SEL_COMMAND,ID_UNPOST),NULL);
598 //popdown(0);
599 return 1;
600 }
601
602
603 // Released button outside popup
onButtonRelease(FXObject *,FXSelector,void * ptr)604 long FXPopup::onButtonRelease(FXObject*,FXSelector,void* ptr){
605 FXEvent* event=(FXEvent*)ptr;
606 FXTRACE((200,"%s::onButtonRelease %p\n",getClassName(),this));
607 if(event->moved){handle(this,FXSEL(SEL_COMMAND,ID_UNPOST),NULL);}
608 //popdown(0);
609 return 1;
610 }
611
612
613 // The widget lost the grab for some reason; unpost the menu
onUngrabbed(FXObject * sender,FXSelector sel,void * ptr)614 long FXPopup::onUngrabbed(FXObject* sender,FXSelector sel,void* ptr){
615 FXShell::onUngrabbed(sender,sel,ptr);
616 handle(this,FXSEL(SEL_COMMAND,ID_UNPOST),NULL);
617 return 1;
618 }
619
620
621 // Key press; escape cancels popup
onKeyPress(FXObject * sender,FXSelector sel,void * ptr)622 long FXPopup::onKeyPress(FXObject* sender,FXSelector sel,void* ptr){
623 FXEvent* event=(FXEvent*)ptr;
624 if(event->code==KEY_Escape || event->code==KEY_Cancel){
625 handle(this,FXSEL(SEL_COMMAND,ID_UNPOST),NULL);
626 return 1;
627 }
628 return FXShell::onKeyPress(sender,sel,ptr);
629 }
630
631
632 // Key release; escape cancels popup
onKeyRelease(FXObject * sender,FXSelector sel,void * ptr)633 long FXPopup::onKeyRelease(FXObject* sender,FXSelector sel,void* ptr){
634 FXEvent* event=(FXEvent*)ptr;
635 if(event->code==KEY_Escape || event->code==KEY_Cancel){
636 handle(this,FXSEL(SEL_COMMAND,ID_UNPOST),NULL);
637 return 1;
638 }
639 return FXShell::onKeyRelease(sender,sel,ptr);
640 }
641
642
643 // Unpost menu in case it was its own owner; otherwise
644 // tell the owner to do so.
onCmdUnpost(FXObject *,FXSelector,void * ptr)645 long FXPopup::onCmdUnpost(FXObject*,FXSelector,void* ptr){
646 FXTRACE((150,"%s::onCmdUnpost %p\n",getClassName(),this));
647 if(grabowner){
648 grabowner->handle(this,FXSEL(SEL_COMMAND,ID_UNPOST),ptr);
649 }
650 else{
651 popdown();
652 if(grabbed()) ungrab();
653 }
654 return 1;
655 }
656
657
658 // Show popup and add to popup stack
show()659 void FXPopup::show(){
660 if(!(flags&FLAG_SHOWN)){
661 FXShell::show();
662 prevActive=getApp()->popupWindow;
663 if(prevActive) prevActive->nextActive=this;
664 getApp()->popupWindow=this;
665 setFocus();
666 }
667 }
668
669
670 // Hide popup and remove from popup stack
hide()671 void FXPopup::hide(){
672 if(flags&FLAG_SHOWN){
673 FXShell::hide();
674 if(getApp()->popupWindow==this) getApp()->popupWindow=prevActive;
675 if(prevActive) prevActive->nextActive=nextActive;
676 if(nextActive) nextActive->prevActive=prevActive;
677 nextActive=NULL;
678 prevActive=NULL;
679 killFocus();
680 }
681 // Focus back to popup under this one, iff this was top one
682 }
683
684
685 // Popup the menu at some location
popup(FXWindow * grabto,FXint x,FXint y,FXint w,FXint h)686 void FXPopup::popup(FXWindow* grabto,FXint x,FXint y,FXint w,FXint h){
687 FXint rx,ry,rw,rh;
688 #ifdef WIN32
689 RECT rect;
690 MONITORINFO minfo;
691 HMONITOR monitor;
692
693 rect.left=x;
694 rect.right=x+w;
695 rect.top=y;
696 rect.bottom=y+h;
697
698 // Get monitor info if we have this API
699 monitor=MonitorFromRect(&rect,MONITOR_DEFAULTTOPRIMARY);
700 if(monitor){
701 memset(&minfo,0,sizeof(minfo));
702 minfo.cbSize=sizeof(minfo);
703 GetMonitorInfo(monitor,&minfo);
704 rx=minfo.rcWork.left;
705 ry=minfo.rcWork.top;
706 rw=minfo.rcWork.right-minfo.rcWork.left;
707 rh=minfo.rcWork.bottom-minfo.rcWork.top;
708 }
709
710 // Otherwise use the work-area
711 else{
712 SystemParametersInfo(SPI_GETWORKAREA,sizeof(RECT),&rect,0);
713 rx=rect.left;
714 ry=rect.top;
715 rw=rect.right-rect.left;
716 rh=rect.bottom-rect.top;
717 }
718 #else
719 rx=getRoot()->getX();
720 ry=getRoot()->getY();
721 rw=getRoot()->getWidth();
722 rh=getRoot()->getHeight();
723 #endif
724 FXTRACE((150,"%s::popup %p\n",getClassName(),this));
725 grabowner=grabto;
726 if((options&POPUP_SHRINKWRAP) || w<=1) w=getDefaultWidth();
727 if((options&POPUP_SHRINKWRAP) || h<=1) h=getDefaultHeight();
728 if(x+w>rx+rw) x=rx+rw-w;
729 if(y+h>ry+rh) y=ry+rh-h;
730 if(x<rx) x=rx;
731 if(y<ry) y=ry;
732 position(x,y,w,h);
733 show();
734 raise();
735 setFocus();
736 if(!grabowner) grab();
737 }
738
739
740 // Pops down menu and its submenus
popdown()741 void FXPopup::popdown(){
742 FXTRACE((150,"%s::popdown %p\n",getClassName(),this));
743 if(!grabowner) ungrab();
744 grabowner=NULL;
745 killFocus();
746 hide();
747 }
748
749
750 /*
751 // Popup the menu and grab to the given owner
752 FXint FXPopup::popup(FXint x,FXint y,FXint w,FXint h){
753 FXint rw,rh;
754 create();
755 if((options&POPUP_SHRINKWRAP) || w<=1) w=getDefaultWidth();
756 if((options&POPUP_SHRINKWRAP) || h<=1) h=getDefaultHeight();
757 rw=getRoot()->getWidth();
758 rh=getRoot()->getHeight();
759 if(x+w>rw) x=rw-w;
760 if(y+h>rh) y=rh-h;
761 if(x<0) x=0;
762 if(y<0) y=0;
763 position(x,y,w,h);
764 show();
765 raise();
766 return getApp()->runPopup(this);
767 }
768
769
770 // Pop down the menu
771 void FXPopup::popdown(FXint value){
772 getApp()->stopModal(this,value);
773 hide();
774 }
775 */
776
777 // Close popup
onCmdChoice(FXObject *,FXSelector,void *)778 long FXPopup::onCmdChoice(FXObject*,FXSelector,void*){
779 //popdown(SELID(sel)-ID_CHOICE);
780 return 1;
781 }
782
783
784 // Set base color
setBaseColor(FXColor clr)785 void FXPopup::setBaseColor(FXColor clr){
786 if(baseColor!=clr){
787 baseColor=clr;
788 update();
789 }
790 }
791
792
793 // Set highlight color
setHiliteColor(FXColor clr)794 void FXPopup::setHiliteColor(FXColor clr){
795 if(hiliteColor!=clr){
796 hiliteColor=clr;
797 update();
798 }
799 }
800
801
802 // Set shadow color
setShadowColor(FXColor clr)803 void FXPopup::setShadowColor(FXColor clr){
804 if(shadowColor!=clr){
805 shadowColor=clr;
806 update();
807 }
808 }
809
810
811 // Set border color
setBorderColor(FXColor clr)812 void FXPopup::setBorderColor(FXColor clr){
813 if(borderColor!=clr){
814 borderColor=clr;
815 update();
816 }
817 }
818
819
820 // Get popup orientation
getOrientation() const821 FXuint FXPopup::getOrientation() const {
822 return (options&POPUP_HORIZONTAL);
823 }
824
825
826 // Set popup orientation
setOrientation(FXuint orient)827 void FXPopup::setOrientation(FXuint orient){
828 FXuint opts=((orient^options)&POPUP_HORIZONTAL)^options;
829 if(options!=opts){
830 options=opts;
831 recalc();
832 }
833 }
834
835
836 // Return shrinkwrap mode
getShrinkWrap() const837 FXbool FXPopup::getShrinkWrap() const {
838 return (options&POPUP_SHRINKWRAP)!=0;
839 }
840
841
842 // Change shrinkwrap mode
setShrinkWrap(FXbool flag)843 void FXPopup::setShrinkWrap(FXbool flag){
844 options^=((0-flag)^options)&POPUP_SHRINKWRAP;
845 }
846
847
848 // Change frame border style
setFrameStyle(FXuint style)849 void FXPopup::setFrameStyle(FXuint style){
850 FXuint opts=((style^options)&FRAME_MASK)^options;
851 if(options!=opts){
852 FXint b=(opts&FRAME_THICK) ? 2 : (opts&(FRAME_SUNKEN|FRAME_RAISED)) ? 1 : 0;
853 options=opts;
854 if(border!=b){
855 border=b;
856 recalc();
857 }
858 update();
859 }
860 }
861
862
863 // Get frame style
getFrameStyle() const864 FXuint FXPopup::getFrameStyle() const {
865 return (options&FRAME_MASK);
866 }
867
868
869 // Unlink popup from active popup stack
~FXPopup()870 FXPopup::~FXPopup(){
871 if(getApp()->popupWindow==this) getApp()->popupWindow=prevActive;
872 if(prevActive) prevActive->nextActive=nextActive;
873 if(nextActive) nextActive->prevActive=prevActive;
874 prevActive=(FXPopup*)-1L;
875 nextActive=(FXPopup*)-1L;
876 grabowner=(FXWindow*)-1L;
877 }
878
879 }
880