1 /********************************************************************************
2 *                                                                               *
3 *                       D o c k H a n d l e r   W i d g e t                     *
4 *                                                                               *
5 *********************************************************************************
6 * Copyright (C) 2005,2020 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 "FXApp.h"
40 #include "FXDockHandler.h"
41 
42 
43 /*
44   Notes:
45   - On MS-Windows, this works just fine, without fanfare.
46   - On X11, some quaintness in the X-Server causes havoc if reparent()
47     is called on a window while it was grabbed.
48   - Not to be deterred, we implement here the following workaround:
49 
50     1) We create a temporary dummy window, just 1x1 pixels in size, in the
51        upper left corner of the screen [yes, that was not a dead pixel
52        after all!].
53 
54     2) We add this to the hash table, so now events from the "true" window
55        as well those from the dummy window are dispatched to the toolbar grip.
56 
57     3) We temporarily replace the xid of the true window with the dummy one,
58        then invoke grab() to grab the mouse, then restore the original xid.
59 
60     4) Now you can wave your mouse around and dock or undock toolbars.
61 
62     5) When we're done, we replace the xid again with the dummy window,
63        call ungrab(), then restore the original xid again.
64 
65     6) Then, delete the dummy window.
66 
67   - The only downside of this method is that the win_x and win_y member
68     data in FXEvent is unreliable; fortunately, the standard toolbar docking
69     algorithms do not use these members.
70   - Of course, we'd rather not have to do all this; so don't hesitate
71     to inform us if you have a better way!
72 
73 */
74 
75 
76 
77 
78 using namespace FX;
79 
80 /*******************************************************************************/
81 
82 namespace FX {
83 
84 // Map
85 FXDEFMAP(FXDockHandler) FXDockHandlerMap[]={
86   FXMAPFUNC(SEL_MOTION,0,FXDockHandler::onMotion),
87   FXMAPFUNC(SEL_LEFTBUTTONPRESS,0,FXDockHandler::onLeftBtnPress),
88   FXMAPFUNC(SEL_LEFTBUTTONRELEASE,0,FXDockHandler::onLeftBtnRelease),
89   FXMAPFUNC(SEL_KEYPRESS,0,FXDockHandler::onKeyPress),
90   FXMAPFUNC(SEL_KEYRELEASE,0,FXDockHandler::onKeyRelease),
91   FXMAPFUNC(SEL_QUERY_TIP,0,FXDockHandler::onQueryTip),
92   FXMAPFUNC(SEL_QUERY_HELP,0,FXDockHandler::onQueryHelp),
93   FXMAPFUNC(SEL_COMMAND,FXDockHandler::ID_SETHELPSTRING,FXDockHandler::onCmdSetHelp),
94   FXMAPFUNC(SEL_COMMAND,FXDockHandler::ID_GETHELPSTRING,FXDockHandler::onCmdGetHelp),
95   FXMAPFUNC(SEL_COMMAND,FXDockHandler::ID_SETTIPSTRING,FXDockHandler::onCmdSetTip),
96   FXMAPFUNC(SEL_COMMAND,FXDockHandler::ID_GETTIPSTRING,FXDockHandler::onCmdGetTip),
97   };
98 
99 
100 // Object implementation
FXIMPLEMENT_ABSTRACT(FXDockHandler,FXFrame,FXDockHandlerMap,ARRAYNUMBER (FXDockHandlerMap))101 FXIMPLEMENT_ABSTRACT(FXDockHandler,FXFrame,FXDockHandlerMap,ARRAYNUMBER(FXDockHandlerMap))
102 
103 
104 // Deserialization
105 FXDockHandler::FXDockHandler(){
106   flags|=FLAG_ENABLED|FLAG_SHOWN;
107   xxx=0;
108   }
109 
110 
111 // Construct and init
FXDockHandler(FXComposite * p,FXObject * tgt,FXSelector sel,FXuint opts,FXint x,FXint y,FXint w,FXint h,FXint pl,FXint pr,FXint pt,FXint pb)112 FXDockHandler::FXDockHandler(FXComposite* p,FXObject* tgt,FXSelector sel,FXuint opts,FXint x,FXint y,FXint w,FXint h,FXint pl,FXint pr,FXint pt,FXint pb):FXFrame(p,opts,x,y,w,h,pl,pr,pt,pb){
113   flags|=FLAG_SHOWN|FLAG_ENABLED;
114   dragCursor=getApp()->getDefaultCursor(DEF_MOVE_CURSOR);
115   target=tgt;
116   message=sel;
117   xxx=0;
118   }
119 
120 
121 // Can have focus
canFocus() const122 FXbool FXDockHandler::canFocus() const { return true; }
123 
124 
125 // Moved
onMotion(FXObject *,FXSelector,void * ptr)126 long FXDockHandler::onMotion(FXObject*,FXSelector,void* ptr){
127   if(flags&FLAG_DODRAG){
128     handle(this,FXSEL(SEL_DRAGGED,0),ptr);
129     return 1;
130     }
131   if((flags&FLAG_TRYDRAG) && ((FXEvent*)ptr)->moved){
132     if(handle(this,FXSEL(SEL_BEGINDRAG,0),ptr)) flags|=FLAG_DODRAG;
133     flags&=~FLAG_TRYDRAG;
134     return 1;
135     }
136   return 0;
137   }
138 
139 
140 // Pressed LEFT button
onLeftBtnPress(FXObject *,FXSelector,void * ptr)141 long FXDockHandler::onLeftBtnPress(FXObject*,FXSelector,void* ptr){
142   flags&=~FLAG_TIP;
143   handle(this,FXSEL(SEL_FOCUS_SELF,0),ptr);
144   if(isEnabled()){
145     flags=(flags&~(FLAG_UPDATE|FLAG_DODRAG))|FLAG_TRYDRAG;
146 #ifdef WIN32
147     grab();
148 #else
149     Display *display=(Display*)getApp()->getDisplay();
150     const unsigned long mask=CWBackPixmap|CWWinGravity|CWBitGravity|CWBorderPixel|CWOverrideRedirect|CWSaveUnder|CWEventMask|CWDontPropagate|CWColormap|CWCursor;
151     XSetWindowAttributes wattr;
152     FXID tempxid=xid;
153     wattr.background_pixmap=None;
154     wattr.background_pixel=0;
155     wattr.border_pixmap=None;
156     wattr.border_pixel=0;
157     wattr.bit_gravity=ForgetGravity;
158     wattr.win_gravity=NorthWestGravity;
159     wattr.backing_store=NotUseful;
160     wattr.backing_planes=0;
161     wattr.backing_pixel=0;
162     wattr.save_under=false;
163     wattr.event_mask=ButtonPressMask|ButtonReleaseMask|PointerMotionMask|KeyPressMask|KeyReleaseMask|FocusChangeMask|StructureNotifyMask|ExposureMask|PropertyChangeMask|EnterWindowMask|LeaveWindowMask;
164     wattr.do_not_propagate_mask=KeyPressMask|KeyReleaseMask|ButtonPressMask|ButtonReleaseMask|PointerMotionMask|ButtonMotionMask;
165     wattr.override_redirect=true;
166     wattr.colormap=DefaultColormap(display,DefaultScreen(display));
167     wattr.cursor=None;
168     xxx=XCreateWindow(display,RootWindow(display,DefaultScreen(display)),0,0,1,1,0,DefaultDepth(display,DefaultScreen(display)),InputOutput,DefaultVisual(display,DefaultScreen(display)),mask,&wattr);
169     getApp()->hash.insert((void*)xxx,this);
170     XMapWindow(display,xxx);
171     xid=xxx;
172     grab();
173     xid=tempxid;
174 #endif
175     update();
176     }
177   return 1;
178   }
179 
180 
181 // Released LEFT button
onLeftBtnRelease(FXObject *,FXSelector,void * ptr)182 long FXDockHandler::onLeftBtnRelease(FXObject*,FXSelector,void* ptr){
183   if(isEnabled()){
184     FXuint flg=flags;
185     flags=(flags&~(FLAG_TRYDRAG|FLAG_DODRAG))|FLAG_UPDATE;
186     if(flg&(FLAG_DODRAG|FLAG_TRYDRAG)){
187 #ifdef WIN32
188       ungrab();
189 #else
190       Display *display=(Display*)getApp()->getDisplay();
191       FXID tempxid=xid;
192       xid=xxx;
193       ungrab();
194       xid=tempxid;
195       getApp()->hash.remove((void*)xxx);
196       XDestroyWindow(display,xxx);
197       xxx=0;
198 #endif
199       }
200     if(flg&FLAG_DODRAG){handle(this,FXSEL(SEL_ENDDRAG,0),ptr);}
201     update();
202     }
203   return 1;
204   }
205 
206 
207 // Key Press
onKeyPress(FXObject *,FXSelector,void * ptr)208 long FXDockHandler::onKeyPress(FXObject*,FXSelector,void* ptr){
209   FXEvent* event=static_cast<FXEvent*>(ptr);
210   if(isEnabled()){
211     if(target && target->tryHandle(this,FXSEL(SEL_KEYPRESS,message),ptr)) return 1;
212     if(event->code==KEY_Control_L || event->code==KEY_Control_R){
213       if(flags&FLAG_DODRAG){handle(this,FXSEL(SEL_DRAGGED,0),ptr);}
214       return 1;
215       }
216     }
217   return 0;
218   }
219 
220 
221 // Key Release
onKeyRelease(FXObject *,FXSelector,void * ptr)222 long FXDockHandler::onKeyRelease(FXObject*,FXSelector,void* ptr){
223   FXEvent* event=static_cast<FXEvent*>(ptr);
224   if(isEnabled()){
225     if(target && target->tryHandle(this,FXSEL(SEL_KEYRELEASE,message),ptr)) return 1;
226     if(event->code==KEY_Control_L || event->code==KEY_Control_R){
227       if(flags&FLAG_DODRAG){handle(this,FXSEL(SEL_DRAGGED,0),ptr);}
228       return 1;
229       }
230     }
231   return 0;
232   }
233 
234 
235 // We were asked about tip text
onQueryTip(FXObject * sender,FXSelector sel,void * ptr)236 long FXDockHandler::onQueryTip(FXObject* sender,FXSelector sel,void* ptr){
237   if(FXFrame::onQueryTip(sender,sel,ptr)) return 1;
238   if((flags&FLAG_TIP) && !tip.empty()){
239     sender->handle(this,FXSEL(SEL_COMMAND,ID_SETSTRINGVALUE),(void*)&tip);
240     return 1;
241     }
242   return 0;
243   }
244 
245 
246 // We were asked about status text
onQueryHelp(FXObject * sender,FXSelector sel,void * ptr)247 long FXDockHandler::onQueryHelp(FXObject* sender,FXSelector sel,void* ptr){
248   if(FXFrame::onQueryHelp(sender,sel,ptr)) return 1;
249   if((flags&FLAG_HELP) && !help.empty()){
250     sender->handle(this,FXSEL(SEL_COMMAND,ID_SETSTRINGVALUE),(void*)&help);
251     return 1;
252     }
253   return 0;
254   }
255 
256 
257 // Set tip using a message
onCmdSetTip(FXObject *,FXSelector,void * ptr)258 long FXDockHandler::onCmdSetTip(FXObject*,FXSelector,void* ptr){
259   setTipText(*((FXString*)ptr));
260   return 1;
261   }
262 
263 
264 // Get tip using a message
onCmdGetTip(FXObject *,FXSelector,void * ptr)265 long FXDockHandler::onCmdGetTip(FXObject*,FXSelector,void* ptr){
266   *((FXString*)ptr)=getTipText();
267   return 1;
268   }
269 
270 
271 // Set help using a message
onCmdSetHelp(FXObject *,FXSelector,void * ptr)272 long FXDockHandler::onCmdSetHelp(FXObject*,FXSelector,void* ptr){
273   setHelpText(*((FXString*)ptr));
274   return 1;
275   }
276 
277 
278 // Get help using a message
onCmdGetHelp(FXObject *,FXSelector,void * ptr)279 long FXDockHandler::onCmdGetHelp(FXObject*,FXSelector,void* ptr){
280   *((FXString*)ptr)=getHelpText();
281   return 1;
282   }
283 
284 
285 // Save data
save(FXStream & store) const286 void FXDockHandler::save(FXStream& store) const {
287   FXFrame::save(store);
288   store << tip;
289   store << help;
290   }
291 
292 
293 // Load data
load(FXStream & store)294 void FXDockHandler::load(FXStream& store){
295   FXFrame::load(store);
296   store >> tip;
297   store >> help;
298   }
299 
300 
301 }
302