Changes corresponding to patch #841324 from Ian Brown:
[wxWidgets.git] / src / motif / toplevel.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: motif/toplevel.cpp
3 // Purpose: wxTopLevelWindow Motif implementation
4 // Author: Mattia Barbon
5 // Modified by:
6 // Created: 12/10/2002
7 // RCS-ID: $Id$
8 // Copyright: (c) Mattia Barbon
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
11
12 // ============================================================================
13 // declarations
14 // ============================================================================
15
16 // ----------------------------------------------------------------------------
17 // headers
18 // ----------------------------------------------------------------------------
19
20 #if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
21 #pragma implementation "toplevel.h"
22 #endif
23
24 #include "wx/toplevel.h"
25 #include "wx/app.h"
26
27 #ifdef __VMS__
28 #define XtDisplay XTDISPLAY
29 #define XtParent XTPARENT
30 #define XtScreen XTSCREEN
31 #define XtWindow XTWINDOW
32 #pragma message disable nosimpint
33 #endif
34
35 #include <Xm/Xm.h>
36 #include <X11/Shell.h>
37 #include <X11/Core.h>
38 #if XmVersion >= 1002
39 #include <Xm/XmAll.h>
40 #else
41 #include <Xm/Frame.h>
42 #endif
43
44 #ifdef __VMS__
45 #pragma message enable nosimpint
46 #endif
47
48 #include "wx/motif/private.h"
49
50 wxList wxModelessWindows; // Frames and modeless dialogs
51
52 // ---------------------------------------------------------------------------
53 // Callbacks
54 // ---------------------------------------------------------------------------
55
56 static void wxCloseTLWCallback( Widget widget, XtPointer client_data,
57 XmAnyCallbackStruct *cbs );
58 static void wxTLWEventHandler( Widget wid,
59 XtPointer client_data,
60 XEvent* event,
61 Boolean *continueToDispatch );
62
63 // ===========================================================================
64 // wxTopLevelWindowMotif implementation
65 // ===========================================================================
66
67 void wxTopLevelWindowMotif::PreDestroy()
68 {
69 if ( (GetWindowStyleFlag() & wxDIALOG_MODAL) != wxDIALOG_MODAL )
70 wxModelessWindows.DeleteObject(this);
71
72 m_icons.m_icons.Empty();
73
74 DestroyChildren();
75
76 // MessageDialog and FileDialog do not have a client widget
77 if( GetClientWidget() )
78 {
79 XtRemoveEventHandler( (Widget)GetClientWidget(),
80 ButtonPressMask | ButtonReleaseMask |
81 PointerMotionMask | KeyPressMask,
82 FALSE,
83 wxTLWEventHandler,
84 (XtPointer)this );
85 }
86 }
87
88 wxTopLevelWindowMotif::~wxTopLevelWindowMotif()
89 {
90 SetMainWidget( (WXWidget)0 );
91 }
92
93 void wxTopLevelWindowMotif::Init()
94 {
95 m_isShown = FALSE;
96 }
97
98 bool wxTopLevelWindowMotif::Create( wxWindow *parent, wxWindowID id,
99 const wxString& title,
100 const wxPoint& pos,
101 const wxSize& size,
102 long style,
103 const wxString& name )
104 {
105 SetName(name);
106 m_windowStyle = style;
107
108 if ( parent )
109 parent->AddChild(this);
110
111 wxTopLevelWindows.Append(this);
112
113 m_windowId = ( id > -1 ) ? id : NewControlId();
114
115 bool retval = DoCreate( parent, id, title, pos, size, style, name );
116
117 if( !retval ) return FALSE;
118
119 // Intercept CLOSE messages from the window manager
120 Widget shell = (Widget)GetShellWidget();
121 Atom WM_DELETE_WINDOW = XmInternAtom( XtDisplay( shell ),
122 "WM_DELETE_WINDOW", False );
123
124 // Remove and add WM_DELETE_WINDOW so ours is only handler
125 // This only appears to be necessary for wxDialog, but does not hurt
126 // for wxFrame
127 XmRemoveWMProtocols( shell, &WM_DELETE_WINDOW, 1 );
128 XmAddWMProtocols( shell, &WM_DELETE_WINDOW, 1 );
129 XmActivateWMProtocol( shell, WM_DELETE_WINDOW );
130
131 // Modified Steve Hammes for Motif 2.0
132 #if (XmREVISION > 1 || XmVERSION > 1)
133 XmAddWMProtocolCallback( shell, WM_DELETE_WINDOW,
134 (XtCallbackProc)wxCloseTLWCallback,
135 (XtPointer)this );
136 #elif XmREVISION == 1
137 XmAddWMProtocolCallback( shell, WM_DELETE_WINDOW,
138 (XtCallbackProc)wxCloseTLWCallback,
139 (caddr_t)this );
140 #else
141 XmAddWMProtocolCallback( shell, WM_DELETE_WINDOW,
142 (void (*)())wxCloseTLWCallback, (caddr_t)this );
143 #endif
144
145 // This patch come from Torsten Liermann lier@lier1.muc.de
146 if( XmIsMotifWMRunning( shell ) )
147 {
148 int decor = 0 ;
149 if( m_windowStyle & wxRESIZE_BORDER )
150 decor |= MWM_DECOR_RESIZEH;
151 if( m_windowStyle & wxSYSTEM_MENU )
152 decor |= MWM_DECOR_MENU;
153 if( ( m_windowStyle & wxCAPTION ) ||
154 ( m_windowStyle & wxTINY_CAPTION_HORIZ ) ||
155 ( m_windowStyle & wxTINY_CAPTION_VERT ) )
156 decor |= MWM_DECOR_TITLE;
157 if( m_windowStyle & wxTHICK_FRAME )
158 decor |= MWM_DECOR_BORDER;
159 if( m_windowStyle & wxMINIMIZE_BOX )
160 decor |= MWM_DECOR_MINIMIZE;
161 if( m_windowStyle & wxMAXIMIZE_BOX )
162 decor |= MWM_DECOR_MAXIMIZE;
163
164 XtVaSetValues( shell,
165 XmNmwmDecorations, decor,
166 NULL );
167 }
168 else
169 {
170 // This allows non-Motif window managers to support at least the
171 // no-decorations case.
172 if( ( m_windowStyle & wxCAPTION ) != wxCAPTION )
173 XtVaSetValues( shell,
174 XmNoverrideRedirect, TRUE,
175 NULL );
176 }
177
178 XtAddEventHandler( (Widget)GetClientWidget(),
179 ButtonPressMask | ButtonReleaseMask |
180 PointerMotionMask | KeyPressMask,
181 FALSE,
182 wxTLWEventHandler,
183 (XtPointer)this );
184
185 return retval;
186 }
187
188 void wxTopLevelWindowMotif::DoGetPosition(int *x, int *y) const
189 {
190 Widget top = (Widget) GetTopWidget();
191 Window parent_window = XtWindow((Widget) top),
192 next_parent = XtWindow((Widget) top),
193 root = RootWindowOfScreen(XtScreen((Widget) top));
194
195 // search for the parent that is child of ROOT, because the WM may
196 // reparent twice and notify only the next parent (like FVWM)
197 while (next_parent != root) {
198 Window *theChildren; unsigned int n;
199 parent_window = next_parent;
200 XQueryTree(XtDisplay((Widget) top), parent_window, &root,
201 &next_parent, &theChildren, &n);
202 XFree(theChildren); // not needed
203 }
204 int xx, yy; unsigned int dummy;
205 XGetGeometry(XtDisplay((Widget) top), parent_window, &root,
206 &xx, &yy, &dummy, &dummy, &dummy, &dummy);
207 if (x) *x = xx;
208 if (y) *y = yy;
209 }
210
211 void wxTopLevelWindowMotif::Raise()
212 {
213 Widget top = (Widget) GetTopWidget();
214 Window parent_window = XtWindow( top ),
215 next_parent = XtWindow( top ),
216 root = RootWindowOfScreen( XtScreen( top ) );
217 // search for the parent that is child of ROOT, because the WM may
218 // reparent twice and notify only the next parent (like FVWM)
219 while( next_parent != root )
220 {
221 Window *theChildren;
222 unsigned int n;
223
224 parent_window = next_parent;
225 XQueryTree( XtDisplay( top ), parent_window, &root,
226 &next_parent, &theChildren, &n );
227 XFree( theChildren ); // not needed
228 }
229 XRaiseWindow( XtDisplay( top ), parent_window );
230 }
231
232 void wxTopLevelWindowMotif::Lower()
233 {
234 Widget top = (Widget) GetTopWidget();
235 Window parent_window = XtWindow( top ),
236 next_parent = XtWindow( top ),
237 root = RootWindowOfScreen( XtScreen( top ) );
238 // search for the parent that is child of ROOT, because the WM may
239 // reparent twice and notify only the next parent (like FVWM)
240 while( next_parent != root )
241 {
242 Window *theChildren;
243 unsigned int n;
244
245 parent_window = next_parent;
246 XQueryTree( XtDisplay( top ), parent_window, &root,
247 &next_parent, &theChildren, &n );
248 XFree( theChildren ); // not needed
249 }
250 XLowerWindow( XtDisplay( top ), parent_window );
251 }
252
253 static inline Widget GetShell( const wxTopLevelWindowMotif* tlw )
254 {
255 Widget main = (Widget) tlw->GetMainWidget();
256 if( !main ) return (Widget) NULL;
257
258 return XtParent( main );
259 }
260
261 WXWidget wxTopLevelWindowMotif::GetShellWidget() const
262 {
263 return (WXWidget) GetShell( this );
264 }
265
266 bool wxTopLevelWindowMotif::ShowFullScreen( bool show,
267 long style )
268 {
269 // TODO, see wxGTK
270 return FALSE;
271 }
272
273 bool wxTopLevelWindowMotif::IsFullScreen() const
274 {
275 // TODO, see wxGTK
276 return FALSE;
277 }
278
279 void wxTopLevelWindowMotif::Restore()
280 {
281 Widget shell = GetShell( this );
282
283 if( shell )
284 XtVaSetValues( shell,
285 XmNiconic, FALSE,
286 NULL );
287 }
288
289 void wxTopLevelWindowMotif::Iconize( bool iconize )
290 {
291 Widget shell = GetShell( this );
292 if( !shell ) return;
293
294 if( !iconize )
295 Show( TRUE );
296
297 XtVaSetValues( shell,
298 XmNiconic, (Boolean)iconize,
299 NULL );
300 }
301
302 bool wxTopLevelWindowMotif::IsIconized() const
303 {
304 Widget shell = GetShell( this );
305
306 if( !shell )
307 return FALSE;
308
309 Boolean iconic;
310 XtVaGetValues( shell,
311 XmNiconic, &iconic,
312 NULL );
313
314 return iconic;
315 }
316
317 void wxTopLevelWindowMotif::Maximize( bool maximize )
318 {
319 Show( TRUE );
320
321 if( maximize )
322 Restore();
323 }
324
325 bool wxTopLevelWindowMotif::IsMaximized() const
326 {
327 return FALSE;
328 }
329
330 void wxTopLevelWindowMotif::SetSizeHints( int minW, int minH,
331 int maxW, int maxH,
332 int incW, int incH )
333 {
334 wxTopLevelWindowBase::SetSizeHints( minW, minH, maxW, maxH, incW, incH );
335
336 int count = 0;
337 Arg args[6];
338
339 if( minW > -1 ) { XtSetArg( args[count], XmNminWidth, minW ); ++count; }
340 if( minH > -1 ) { XtSetArg( args[count], XmNminHeight, minH ); ++count; }
341 if( maxW > -1 ) { XtSetArg( args[count], XmNmaxWidth, maxW ); ++count; }
342 if( maxH > -1 ) { XtSetArg( args[count], XmNmaxHeight, maxH ); ++count; }
343 if( incW > -1 ) { XtSetArg( args[count], XmNwidthInc, incW ); ++count; }
344 if( incH > -1 ) { XtSetArg( args[count], XmNheightInc, incH ); ++count; }
345
346 XtSetValues( (Widget)GetShellWidget(), args, count );
347 }
348
349 bool wxTopLevelWindowMotif::SetShape( const wxRegion& region )
350 {
351 return wxDoSetShape( (Display*)GetXDisplay(),
352 XtWindow( (Widget)GetShellWidget() ),
353 region );
354 }
355
356 // ---------------------------------------------------------------------------
357 // Callback definition
358 // ---------------------------------------------------------------------------
359
360 // Handle a close event from the window manager
361 static void wxCloseTLWCallback( Widget WXUNUSED(widget), XtPointer client_data,
362 XmAnyCallbackStruct *WXUNUSED(cbs) )
363 {
364 wxTopLevelWindowMotif* tlw = (wxTopLevelWindowMotif*)client_data;
365 wxCloseEvent closeEvent( wxEVT_CLOSE_WINDOW, tlw->GetId() );
366 closeEvent.SetEventObject( tlw );
367
368 // May delete the dialog (with delayed deletion)
369 tlw->GetEventHandler()->ProcessEvent(closeEvent);
370 }
371
372 void wxTLWEventHandler( Widget wid,
373 XtPointer WXUNUSED(client_data),
374 XEvent* event,
375 Boolean* continueToDispatch)
376 {
377 wxTopLevelWindowMotif* tlw =
378 (wxTopLevelWindowMotif*)wxGetWindowFromTable( wid );
379
380 if( tlw )
381 {
382 wxMouseEvent wxevent( wxEVT_NULL );
383
384 if( wxTranslateMouseEvent( wxevent, tlw, wid, event ) )
385 {
386 wxevent.SetEventObject( tlw );
387 wxevent.SetId( tlw->GetId() );
388 tlw->GetEventHandler()->ProcessEvent( wxevent );
389 }
390 else
391 {
392 // An attempt to implement OnCharHook by calling OnCharHook first;
393 // if this returns TRUE, set continueToDispatch to False
394 // (don't continue processing).
395 // Otherwise set it to True and call OnChar.
396 wxKeyEvent keyEvent( wxEVT_CHAR );
397 if( wxTranslateKeyEvent( keyEvent, tlw, wid, event ))
398 {
399 keyEvent.SetEventObject( tlw );
400 keyEvent.SetId( tlw->GetId() );
401 keyEvent.SetEventType( wxEVT_CHAR_HOOK );
402 if( tlw->GetEventHandler()->ProcessEvent( keyEvent ) )
403 {
404 *continueToDispatch = False;
405 return;
406 }
407 else
408 {
409 // For simplicity, OnKeyDown is the same as OnChar
410 // TODO: filter modifier key presses from OnChar
411 keyEvent.SetEventType( wxEVT_KEY_DOWN );
412
413 // Only process OnChar if OnKeyDown didn't swallow it
414 if( !tlw->GetEventHandler()->ProcessEvent( keyEvent ) )
415 {
416 keyEvent.SetEventType( wxEVT_CHAR );
417 tlw->GetEventHandler()->ProcessEvent( keyEvent );
418 }
419 }
420 }
421 }
422 }
423
424 *continueToDispatch = True;
425 }
426