Only reparent first window if there are several children
[wxWidgets.git] / src / x11 / reparent.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: reparent.cpp
3 // Purpose: wxWindow
4 // Author: Julian Smart
5 // Modified by:
6 // Created: 2002-03-09
7 // RCS-ID: $Id$
8 // Copyright: (c) Julian Smart
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
11
12 // ============================================================================
13 // declarations
14 // ============================================================================
15
16 // ----------------------------------------------------------------------------
17 // headers
18 // ----------------------------------------------------------------------------
19
20 #ifdef __GNUG__
21 #pragma implementation "reparent.h"
22 #endif
23
24 #include "wx/x11/reparent.h"
25 #include "wx/evtloop.h"
26 #include "wx/log.h"
27 #include "wx/app.h"
28 #include "wx/timer.h"
29
30 #include "wx/x11/private.h"
31 #include "X11/Xatom.h"
32
33 /*
34
35 Adapted from code by Mike Yang, as follows.
36
37 From: Mike Yang (mikey@eukanuba.wpd.sgi.com)
38 Subject: Re: Wrapping new widget around existing windows
39 Newsgroups: comp.windows.x
40 View: Complete Thread (17 articles) | Original Format
41 Date: 1991-08-09 09:45:48 PST
42
43
44 Enough people asked, so here's my test program which reparents another
45 window. It's a single file (reparent.c), and will work with Motif or
46 Xaw. Xaw users should comment out the "#define MOTIF" line.
47
48 The reparent program first prompts for the application name of the
49 client that it will reparent. If you're going to start the
50 application override_redirect (e.g. -xrm "*overrideRedirect: true"),
51 then this name is ignored and the first override_redirect window is
52 assumed to be the one.
53
54 Input focus is supposed to be correctly handled, as is resizing with
55 window manager hints. If you have input focus problems, try launching
56 your application override_redirect instead. This method is preferred
57 anyway, since you can map it off-screen and then avoid the "window
58 flash" effect as the application's top-level window is reparented.
59
60 -----------------------------------------------------------------------
61 Mike Yang Silicon Graphics, Inc.
62 mikey@sgi.com 415/335-1786
63
64
65 ------------------------------- cut here ------------------------------
66 */
67
68 /*
69
70 Copyright 1991 by Mike Yang, mikey@sgi.com, Silicon Graphics, Inc.
71
72 Permission to use, copy, modify, distribute, and sell this software and its
73 documentation for any purpose is hereby granted without fee, provided that
74 the above copyright notice appear in all copies and that both that
75 copyright notice and this permission notice appear in supporting
76 documentation, and that the name of SGI not be used in advertising or
77 publicity pertaining to distribution of the software without specific,
78 written prior permission. SGI makes no representations about the
79 suitability of this software for any purpose. It is provided "as is"
80 without express or implied warranty.
81
82 */
83
84 /*
85 * wxAdoptedWindow
86 */
87
88 wxAdoptedWindow::wxAdoptedWindow()
89 {
90 }
91
92 wxAdoptedWindow::wxAdoptedWindow(WXWindow window)
93 {
94 m_mainWidget = window;
95 }
96
97 wxAdoptedWindow::~wxAdoptedWindow()
98 {
99 }
100 /*
101 * wxReparenter
102 */
103
104 static bool Xerror;
105 static Atom WM_STATE = 0;
106 bool wxReparenter::sm_done = FALSE;
107 wxAdoptedWindow* wxReparenter::sm_toReparent = NULL;
108 wxWindow* wxReparenter::sm_newParent = NULL;
109 wxString wxReparenter::sm_name;
110 bool wxReparenter::sm_exactMatch = FALSE;
111
112 static int
113 ErrorHandler(Display* dpy, XErrorEvent* event)
114 {
115 Xerror = True;
116 return False;
117 }
118
119 // We assume that toReparent has had its X window set
120 // appropriately.
121 bool wxReparenter::Reparent(wxWindow* newParent, wxAdoptedWindow* toReparent)
122 {
123 XWindowAttributes xwa;
124 Window *children;
125 unsigned int numchildren, each;
126 Window returnroot, returnparent;
127 XErrorHandler old;
128 int parentOffset = 0;
129
130 old = XSetErrorHandler(ErrorHandler);
131 XReparentWindow((Display*) newParent->GetXDisplay(),
132 (Window) toReparent->GetXWindow(),
133 (Window) newParent->GetXWindow(),
134 0, 0);
135
136 if (!XQueryTree((Display*) newParent->GetXDisplay(),
137 (Window) toReparent->GetXWindow(),
138 &returnroot, &returnparent,
139 &children, &numchildren) || Xerror)
140 {
141 XSetErrorHandler(old);
142 return TRUE;
143 }
144
145 if (numchildren > 0)
146 {
147 // TEST: see if we can get away with reparenting just
148 // first one
149 if (numchildren > 1)
150 {
151 wxLogDebug(wxT("Found %d, but only reparenting 1 child."), numchildren);
152 numchildren = 1;
153 }
154 wxLogDebug(wxT("Reparenting %d children."), numchildren);
155 /* Stacking order is preserved since XQueryTree returns its children in
156 bottommost to topmost order
157 */
158 for (each=0; each<numchildren; each++)
159 {
160 XGetWindowAttributes((Display*) newParent->GetXDisplay(),
161 children[each], &xwa);
162 fprintf(stderr,
163 "Reparenting child at offset %d and position %d, %d.\n",
164 parentOffset, parentOffset+xwa.x, parentOffset+xwa.y);
165 XReparentWindow((Display*) newParent->GetXDisplay(),
166 children[each], (Window) newParent->GetXWindow(),
167 xwa.x, xwa.y);
168 }
169 }
170
171 XSetErrorHandler(old);
172 return TRUE;
173 }
174
175 // Wait for an appropriate window to be created.
176 // If exactMatch is FALSE, a substring match is OK.
177 // If windowName is empty, then wait for the next overrideRedirect window.
178 bool wxReparenter::WaitAndReparent(wxWindow* newParent, wxAdoptedWindow* toReparent,
179 const wxString& windowName,
180 bool exactMatch)
181 {
182 sm_newParent = newParent;
183 sm_toReparent = toReparent;
184 sm_exactMatch = exactMatch;
185 sm_name = windowName;
186
187 Display* display = (Display*) newParent->GetXDisplay() ;
188 XSelectInput(display,
189 RootWindowOfScreen(DefaultScreenOfDisplay(display)),
190 SubstructureNotifyMask);
191
192 if (!WM_STATE)
193 WM_STATE = XInternAtom(display, "WM_STATE", False);
194
195 #ifdef __WXDEBUG__
196 if (!windowName.IsEmpty())
197 wxLogDebug(_T("Waiting for window %s"), windowName.c_str());
198 #endif
199
200 sm_done = FALSE;
201
202 wxEventLoop eventLoop;
203 while (!sm_done)
204 {
205 if (eventLoop.Pending())
206 {
207 XEvent xevent;
208 XNextEvent(display, & xevent);
209 if (!wxTheApp->ProcessXEvent((WXEvent*) & xevent))
210 {
211 // Do the local event processing
212 ProcessXEvent((WXEvent*) & xevent);
213 }
214 }
215 else
216 {
217 #if wxUSE_TIMER
218 wxTimer::NotifyTimers();
219 wxTheApp->SendIdleEvents();
220 #endif
221 }
222 }
223 return TRUE;
224 }
225
226 bool wxReparenter::ProcessXEvent(WXEvent* event)
227 {
228 XEvent* xevent = (XEvent*) event;
229 Window client;
230
231 if (!sm_done)
232 {
233 if (xevent->type == MapNotify)
234 {
235 wxLogDebug(_T("Window was mapped"));
236 }
237
238 if (xevent->type == MapNotify && !xevent->xmap.override_redirect &&
239 (client = (Window) FindAClientWindow((WXWindow) xevent->xmap.window, sm_name)))
240 {
241 wxLogDebug(_T("Found a client window, about to reparent"));
242 wxASSERT(sm_toReparent->GetParent() == NULL);
243
244 sm_toReparent->SetHandle((WXWindow) client);
245 sm_newParent->AddChild(sm_toReparent);
246 sm_done = Reparent(sm_newParent, sm_toReparent);
247 return sm_done;
248 } else if (xevent->type == MapNotify &&
249 xevent->xmap.override_redirect &&
250 xevent->xmap.window)
251 {
252 wxLogDebug(_T("Found an override redirect window, about to reparent"));
253 sm_toReparent->SetHandle((WXWindow) xevent->xmap.window);
254 sm_newParent->AddChild(sm_toReparent);
255 wxASSERT(sm_toReparent->GetParent() == NULL);
256
257 sm_done = Reparent(sm_newParent, sm_toReparent);
258 return sm_done;
259 }
260 }
261 return FALSE;
262 }
263
264 WXWindow wxReparenter::FindAClientWindow(WXWindow window, const wxString& name)
265 {
266 int rvalue, i;
267 Atom actualtype;
268 int actualformat;
269 unsigned long nitems, bytesafter;
270 unsigned char *propreturn;
271 Window *children;
272 unsigned int numchildren;
273 Window returnroot, returnparent;
274 Window result = 0;
275 XErrorHandler old;
276 char *clientName;
277
278 Xerror = False;
279 old = XSetErrorHandler(ErrorHandler);
280 rvalue = XGetWindowProperty((Display*) wxGetDisplay(),
281 (Window) window, WM_STATE,
282 0, 1, False,
283 AnyPropertyType, &actualtype, &actualformat,
284 &nitems, &bytesafter, &propreturn);
285 XSetErrorHandler(old);
286 if (!Xerror && rvalue == Success && actualtype != None)
287 {
288 if (rvalue == Success)
289 {
290 XFree((char *) propreturn);
291 }
292 XFetchName((Display*) wxGetDisplay(), (Window) window, &clientName);
293
294 wxString str1(name);
295 wxString str2(clientName);
296 str1.Lower();
297 str2.Lower();
298
299 bool matches;
300 if (sm_exactMatch)
301 matches = (name == clientName);
302 else
303 matches = (str1.Contains(str2) || str2.Contains(str1));
304
305 XFree(clientName);
306
307 if (matches)
308 return (WXWindow) window;
309 else
310 return NULL;
311 }
312
313 old = XSetErrorHandler(ErrorHandler);
314 if (!XQueryTree((Display*) wxGetDisplay(), (Window) window, &returnroot, &returnparent,
315 &children, &numchildren) || Xerror) {
316 XSetErrorHandler(old);
317 return NULL;
318 }
319 XSetErrorHandler(old);
320
321 result = 0;
322 for (i=0; i<(int)numchildren && !result ;i++) {
323 result = (Window) FindAClientWindow((WXWindow) children[i], name);
324 }
325 if (numchildren) {
326 XFree((char *) children);
327 } return (WXWindow) result;
328 }
329