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