Added a mechanism allowing a toplevel window to delay its deactivation
[wxWidgets.git] / src / cocoa / toplevel.mm
1 ///////////////////////////////////////////////////////////////////////////////
2 // Name:        cocoa/toplevel.mm
3 // Purpose:     implements wxTopLevelWindow for Cocoa
4 // Author:      David Elliott 
5 // Modified by:
6 // Created:     2002/11/27
7 // RCS-ID:      $Id$
8 // Copyright:   (c) 2002 David Elliott
9 // Licence:     wxWindows licence
10 ///////////////////////////////////////////////////////////////////////////////
11
12 // ============================================================================
13 // declarations
14 // ============================================================================
15
16 // ----------------------------------------------------------------------------
17 // headers
18 // ----------------------------------------------------------------------------
19
20 // For compilers that support precompilation, includes "wx.h".
21 #include "wx/wxprec.h"
22 #ifndef WX_PRECOMP
23     #include "wx/window.h"
24     #include "wx/toplevel.h"
25     #include "wx/menuitem.h"
26     #include "wx/frame.h"
27     #include "wx/log.h"
28     #include "wx/app.h"
29 #endif //WX_PRECOMP
30
31 #include "wx/cocoa/autorelease.h"
32 #include "wx/cocoa/string.h"
33
34 #import <AppKit/NSView.h>
35 #import <AppKit/NSWindow.h>
36 #import <AppKit/NSPanel.h>
37 // ----------------------------------------------------------------------------
38 // globals
39 // ----------------------------------------------------------------------------
40
41 // list of all frames and modeless dialogs
42 wxWindowList       wxModelessWindows;
43
44 // ============================================================================
45 // wxTopLevelWindowCocoa implementation
46 // ============================================================================
47
48 wxTopLevelWindowCocoa *wxTopLevelWindowCocoa::sm_cocoaDeactivateWindow = NULL;
49
50 // ----------------------------------------------------------------------------
51 // wxTopLevelWindowCocoa creation
52 // ----------------------------------------------------------------------------
53 BEGIN_EVENT_TABLE(wxTopLevelWindowCocoa,wxTopLevelWindowBase)
54     EVT_CLOSE(wxTopLevelWindowCocoa::OnCloseWindow)
55 END_EVENT_TABLE()
56
57 void wxTopLevelWindowCocoa::Init()
58 {
59     m_iconized =
60     m_maximizeOnShow =
61     m_closed = false;
62 }
63
64 unsigned int wxTopLevelWindowCocoa::NSWindowStyleForWxStyle(long style)
65 {
66     unsigned int styleMask = 0;
67     if(style & wxCAPTION)
68         styleMask |= NSTitledWindowMask;
69     if(style & wxMINIMIZE_BOX)
70         styleMask |= NSMiniaturizableWindowMask;
71     #if 0
72     if(style & wxMAXIMIZE_BOX)
73         styleMask |= NSWindowMask;
74         #endif
75     if(style & wxCLOSE_BOX)
76         styleMask |= NSClosableWindowMask;
77     if(style & wxRESIZE_BORDER)
78         styleMask |= NSResizableWindowMask;
79     if(style & wxSIMPLE_BORDER)
80         styleMask |= NSBorderlessWindowMask;
81     return styleMask;
82 }
83
84 bool wxTopLevelWindowCocoa::Create(wxWindow *parent,
85                                  wxWindowID winid,
86                                  const wxString& title,
87                                  const wxPoint& pos,
88                                  const wxSize& size,
89                                  long style,
90                                  const wxString& name)
91 {
92     wxAutoNSAutoreleasePool pool;
93     wxTopLevelWindows.Append(this);
94
95     if(!CreateBase(parent,winid,pos,size,style,wxDefaultValidator,name))
96         return FALSE;
97
98     if ( parent )
99         parent->AddChild(this);
100
101     unsigned int cocoaStyle = NSWindowStyleForWxStyle(style);
102     if(style & wxFRAME_TOOL_WINDOW)
103         cocoaStyle |= NSUtilityWindowMask;
104
105     wxPoint realpos = pos;
106     wxSize realsize = size;
107     // FIXME: this is lame
108     if(realpos.x==-1)
109         realpos.x=100;
110     if(realpos.y==-1)
111         realpos.y=100;
112     if(realsize.x==-1)
113         realsize.x=200;
114     if(realsize.y==-1)
115         realsize.y=200;
116     // NOTE: y-origin needs to be flipped.
117     NSRect cocoaRect = [NSWindow contentRectForFrameRect:NSMakeRect(realpos.x,realpos.y,realsize.x,realsize.y) styleMask:cocoaStyle];
118
119     m_cocoaNSWindow = NULL;
120     m_cocoaNSView = NULL;
121     if(style & wxFRAME_TOOL_WINDOW)
122         SetNSWindow([[NSPanel alloc] initWithContentRect:cocoaRect styleMask:cocoaStyle backing:NSBackingStoreBuffered defer:NO]);
123     else
124         SetNSWindow([[NSWindow alloc] initWithContentRect:cocoaRect styleMask:cocoaStyle backing:NSBackingStoreBuffered defer:NO]);
125     // NOTE: SetNSWindow has retained the Cocoa object for this object.
126     // Because we do not release on close, the following release matches the
127     // above alloc and thus the retain count will be 1.
128     [m_cocoaNSWindow release];
129
130     if(style & wxFRAME_NO_TASKBAR)
131         [m_cocoaNSWindow setExcludedFromWindowsMenu: YES];
132     if(style & wxSTAY_ON_TOP)
133         [m_cocoaNSWindow setLevel:NSFloatingWindowLevel];
134     [m_cocoaNSWindow setTitle:wxNSStringWithWxString(title)];
135     return TRUE;
136 }
137
138 wxTopLevelWindowCocoa::~wxTopLevelWindowCocoa()
139 {
140     wxASSERT(sm_cocoaDeactivateWindow!=this);
141     wxAutoNSAutoreleasePool pool;
142     DestroyChildren();
143     SetNSWindow(NULL);
144 }
145
146 bool wxTopLevelWindowCocoa::Destroy()
147 {
148     if(sm_cocoaDeactivateWindow==this)
149     {
150         sm_cocoaDeactivateWindow = NULL;
151         wxTopLevelWindowCocoa::CocoaDelegate_windowDidResignKey();
152     }
153     return wxTopLevelWindowBase::Destroy();
154 }
155
156 // ----------------------------------------------------------------------------
157 // wxTopLevelWindowCocoa Cocoa Specifics
158 // ----------------------------------------------------------------------------
159
160 wxMenuBar* wxTopLevelWindowCocoa::GetAppMenuBar(wxCocoaNSWindow *win)
161 {
162     wxTopLevelWindowCocoa *parent = wxDynamicCast(GetParent(),wxTopLevelWindow);
163     if(parent)
164         return parent->GetAppMenuBar(win);
165     return NULL;
166 }
167
168 void wxTopLevelWindowCocoa::SetNSWindow(WX_NSWindow cocoaNSWindow)
169 {
170     bool need_debug = cocoaNSWindow || m_cocoaNSWindow;
171     if(need_debug) wxLogDebug("wxTopLevelWindowCocoa=%p::SetNSWindow [m_cocoaNSWindow=%p retainCount]=%d",this,m_cocoaNSWindow,[m_cocoaNSWindow retainCount]);
172     DisassociateNSWindow(m_cocoaNSWindow);
173     [cocoaNSWindow retain];
174     [m_cocoaNSWindow release];
175     m_cocoaNSWindow = cocoaNSWindow;
176     if(m_cocoaNSWindow)
177         SetNSView([m_cocoaNSWindow contentView]);
178     else
179         SetNSView(NULL);
180     AssociateNSWindow(m_cocoaNSWindow);
181     if(need_debug) wxLogDebug("wxTopLevelWindowCocoa=%p::SetNSWindow [cocoaNSWindow=%p retainCount]=%d",this,cocoaNSWindow,[cocoaNSWindow retainCount]);
182 }
183
184 void wxTopLevelWindowCocoa::CocoaReplaceView(WX_NSView oldView, WX_NSView newView)
185 {
186     if([m_cocoaNSWindow contentView] == (id)oldView)
187         [m_cocoaNSWindow setContentView:newView];
188 }
189
190 /*static*/ void wxTopLevelWindowCocoa::DeactivatePendingWindow()
191 {
192     if(sm_cocoaDeactivateWindow)
193         sm_cocoaDeactivateWindow->wxTopLevelWindowCocoa::CocoaDelegate_windowDidResignKey();
194     sm_cocoaDeactivateWindow = NULL;
195 }
196
197 void wxTopLevelWindowCocoa::CocoaDelegate_windowDidBecomeKey(void)
198 {
199     DeactivatePendingWindow();
200     wxLogDebug("wxTopLevelWindowCocoa=%p::CocoaDelegate_windowDidBecomeKey",this);
201     wxActivateEvent event(wxEVT_ACTIVATE, TRUE, GetId());
202     event.SetEventObject(this);
203     GetEventHandler()->ProcessEvent(event);
204 }
205
206 void wxTopLevelWindowCocoa::CocoaDelegate_windowDidResignKey(void)
207 {
208     wxLogDebug("wxTopLevelWindowCocoa=%p::CocoaDelegate_windowDidResignKey",this);
209     wxActivateEvent event(wxEVT_ACTIVATE, FALSE, GetId());
210     event.SetEventObject(this);
211     GetEventHandler()->ProcessEvent(event);
212 }
213
214 void wxTopLevelWindowCocoa::CocoaDelegate_windowDidBecomeMain(void)
215 {
216     wxLogDebug("wxTopLevelWindowCocoa=%p::CocoaDelegate_windowDidBecomeMain",this);
217 }
218
219 void wxTopLevelWindowCocoa::CocoaDelegate_windowDidResignMain(void)
220 {
221     wxLogDebug("wxTopLevelWindowCocoa=%p::CocoaDelegate_windowDidResignMain",this);
222 }
223
224 void wxTopLevelWindowCocoa::CocoaDelegate_windowWillClose(void)
225 {
226     m_closed = true;
227     Destroy();
228     /* Be SURE that idle events get ran.  If the window was not active when
229     it was closed, then there will be no more events to trigger this and
230     therefore it must be done here */
231     wxTheApp->CocoaInstallRequestedIdleHandler();
232 }
233
234 bool wxTopLevelWindowCocoa::CocoaDelegate_windowShouldClose()
235 {
236     return wxWindowBase::Close(false);
237 }
238
239 // ----------------------------------------------------------------------------
240 // wxTopLevelWindowCocoa maximize/minimize
241 // ----------------------------------------------------------------------------
242
243 void wxTopLevelWindowCocoa::Maximize(bool maximize)
244 {
245 }
246
247 bool wxTopLevelWindowCocoa::IsMaximized() const
248 {
249     return false ; 
250 }
251
252 void wxTopLevelWindowCocoa::Iconize(bool iconize)
253 {
254 }
255
256 bool wxTopLevelWindowCocoa::IsIconized() const
257 {
258     return FALSE;
259 }
260
261 void wxTopLevelWindowCocoa::Restore()
262 {
263 }
264
265 bool wxTopLevelWindowCocoa::Show(bool show)
266 {
267     if(m_isShown == show)
268         return false;
269     wxAutoNSAutoreleasePool pool;
270     if(show)
271     {
272         // Send the window a size event because wxWindows apps expect it
273         // NOTE: This should really only be done the first time a window
274         // is shown.  I doubt this will cause any problems though.
275         wxSizeEvent event(GetSize(), GetId());
276         event.SetEventObject(this);
277         GetEventHandler()->ProcessEvent(event);
278
279         [m_cocoaNSWindow makeKeyAndOrderFront:m_cocoaNSWindow];
280     }
281     else
282         [m_cocoaNSWindow orderOut:m_cocoaNSWindow];
283     m_isShown = show;
284     return true;
285 }
286
287 bool wxTopLevelWindowCocoa::Close(bool force)
288 {
289     if(force)
290         return wxWindowBase::Close(force);
291     // performClose  will fake the user clicking the close button which
292     // will invoke windowShouldClose which will call the base class version
293     // of Close() which will NOT Destroy() the window (see below) but
294     // if closing is not stopped, then performClose will go ahead and
295     // close the window which will send the close notifications setting
296     // m_closed to true and Destroy()ing the window.
297     [m_cocoaNSWindow performClose:m_cocoaNSWindow];
298     return m_closed;
299 }
300
301 void wxTopLevelWindowCocoa::OnCloseWindow(wxCloseEvent& event)
302 {
303     // If the event was forced, close the window which will Destroy() it
304     if(!event.CanVeto())
305         [m_cocoaNSWindow close];
306     // if the event was not forced, it's probably because the user clicked
307     // the close button, or Close(false) was called which (see above) is
308     // redirected to performClose and thus Cocoa itself will close the window
309 }
310
311 // ----------------------------------------------------------------------------
312 // wxTopLevelWindowCocoa misc
313 // ----------------------------------------------------------------------------
314
315 bool wxTopLevelWindowCocoa::ShowFullScreen(bool show, long style)
316 {
317     return FALSE;
318 }
319
320 bool wxTopLevelWindowCocoa::IsFullScreen() const
321 {
322     return FALSE;
323 }
324
325 void wxTopLevelWindowCocoa::CocoaSetWxWindowSize(int width, int height)
326 {
327     // Set the NSView size by setting the frame size to enclose it
328     unsigned int styleMask = [m_cocoaNSWindow styleMask];
329     NSRect frameRect = [m_cocoaNSWindow frame];
330     NSRect contentRect = [NSWindow
331         contentRectForFrameRect: frameRect
332         styleMask: styleMask];
333     contentRect.size.width = width;
334     contentRect.size.height = height;
335     frameRect = [NSWindow
336         frameRectForContentRect: contentRect
337         styleMask: styleMask];
338     [m_cocoaNSWindow setFrame: frameRect display: NO];
339 }
340
341 void wxTopLevelWindowCocoa::DoMoveWindow(int x, int y, int width, int height)
342 {
343 //    wxLogDebug("wxTopLevelWindow=%p::DoMoveWindow(%d,%d,%d,%d)",this,x,y,width,height);
344
345     NSRect cocoaRect = NSMakeRect(x,y,width,height);
346     [m_cocoaNSWindow setFrame: cocoaRect display:NO];
347 }
348
349 void wxTopLevelWindowCocoa::DoGetSize(int *w, int *h) const
350 {
351     NSRect cocoaRect = [m_cocoaNSWindow frame];
352     if(w)
353         *w=(int)cocoaRect.size.width;
354     if(h)
355         *h=(int)cocoaRect.size.height;
356 //    wxLogDebug("wxTopLevelWindow=%p::DoGetSize = (%d,%d)",this,(int)cocoaRect.size.width,(int)cocoaRect.size.height);
357 }
358
359 void wxTopLevelWindowCocoa::DoGetPosition(int *x, int *y) const
360 {
361     NSRect cocoaRect = [m_cocoaNSWindow frame];
362     if(x)
363         *x=(int)cocoaRect.origin.x;
364     if(y)
365         *y=(int)cocoaRect.origin.y;
366 //    wxLogDebug("wxTopLevelWindow=%p::DoGetPosition = (%d,%d)",this,(int)cocoaRect.origin.x,(int)cocoaRect.origin.y);
367 }
368