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