1/////////////////////////////////////////////////////////////////////////////// 2// Name: src/cocoa/toplevel.mm 3// Purpose: implements wxTopLevelWindow for Cocoa 4// Author: David Elliott 5// Modified by: 6// Created: 2002/11/27 7// Copyright: (c) 2002 David Elliott 8// Licence: wxWindows licence 9/////////////////////////////////////////////////////////////////////////////// 10 11// ============================================================================ 12// declarations 13// ============================================================================ 14 15// ---------------------------------------------------------------------------- 16// headers 17// ---------------------------------------------------------------------------- 18 19// For compilers that support precompilation, includes "wx.h". 20#include "wx/wxprec.h" 21 22#include "wx/toplevel.h" 23 24#ifndef WX_PRECOMP 25 #include "wx/window.h" 26 #include "wx/menuitem.h" 27 #include "wx/frame.h" 28 #include "wx/log.h" 29 #include "wx/app.h" 30#endif //WX_PRECOMP 31 32#include "wx/cocoa/autorelease.h" 33#include "wx/cocoa/string.h" 34#include "wx/cocoa/ObjcRef.h" 35 36#include "wx/cocoa/objc/NSView.h" 37#include "wx/cocoa/objc/NSWindow.h" 38#import <AppKit/NSPanel.h> 39#import <AppKit/NSButtonCell.h> 40#import <AppKit/NSControl.h> 41 42// ---------------------------------------------------------------------------- 43// globals 44// ---------------------------------------------------------------------------- 45 46// list of all frames and modeless dialogs 47wxWindowList wxModelessWindows; 48 49// ============================================================================ 50// wxTopLevelWindowCocoa implementation 51// ============================================================================ 52 53wxTopLevelWindowCocoa *wxTopLevelWindowCocoa::sm_cocoaDeactivateWindow = NULL; 54 55// ---------------------------------------------------------------------------- 56// wxTopLevelWindowCocoa creation 57// ---------------------------------------------------------------------------- 58BEGIN_EVENT_TABLE(wxTopLevelWindowCocoa,wxTopLevelWindowBase) 59 EVT_CLOSE(wxTopLevelWindowCocoa::OnCloseWindow) 60END_EVENT_TABLE() 61 62void wxTopLevelWindowCocoa::Init() 63{ 64 m_iconized = 65 m_maximizeOnShow = 66 m_closed = false; 67} 68 69unsigned int wxTopLevelWindowCocoa::NSWindowStyleForWxStyle(long style) 70{ 71 unsigned int styleMask = 0; 72 if(style & wxCAPTION) 73 styleMask |= NSTitledWindowMask; 74 if(style & wxMINIMIZE_BOX) 75 styleMask |= NSMiniaturizableWindowMask; 76 #if 0 77 if(style & wxMAXIMIZE_BOX) 78 styleMask |= NSWindowMask; 79 #endif 80 if(style & wxCLOSE_BOX) 81 styleMask |= NSClosableWindowMask; 82 if(style & wxRESIZE_BORDER) 83 styleMask |= NSResizableWindowMask; 84 if(style & wxSIMPLE_BORDER) 85 styleMask |= NSBorderlessWindowMask; 86 return styleMask; 87} 88 89NSRect wxTopLevelWindowCocoa::MakeInitialNSWindowContentRect(const wxPoint& pos, const wxSize& size, unsigned int cocoaStyleMask) 90{ 91 // Arbitrarily use (100,100) as the origin when default coords are given. 92 wxCoord x = pos.x!=wxDefaultCoord ? pos.x : 100; 93 wxCoord y = pos.y!=wxDefaultCoord ? pos.y : 100; 94 95 wxCoord w = WidthDefault(size.x); 96 wxCoord h = HeightDefault(size.y); 97 98 NSPoint cocoaOrigin = OriginInCocoaScreenCoordinatesForRectInWxDisplayCoordinates(x,y,w,h,true); 99 100 return [NSWindow 101 contentRectForFrameRect:NSMakeRect(cocoaOrigin.x,cocoaOrigin.y,w,h) 102 styleMask:cocoaStyleMask]; 103 104} 105 106bool wxTopLevelWindowCocoa::Create(wxWindow *parent, 107 wxWindowID winid, 108 const wxString& title, 109 const wxPoint& pos, 110 const wxSize& size, 111 long style, 112 const wxString& name) 113{ 114 wxAutoNSAutoreleasePool pool; 115 wxTopLevelWindows.Append(this); 116 117 if(!CreateBase(parent,winid,pos,size,style,wxDefaultValidator,name)) 118 return false; 119 120 if ( parent ) 121 parent->AddChild(this); 122 123 unsigned int cocoaStyle = NSWindowStyleForWxStyle(style); 124 if(style & wxFRAME_TOOL_WINDOW) 125 cocoaStyle |= NSUtilityWindowMask; 126 127 NSRect cocoaRect = MakeInitialNSWindowContentRect(pos,size,cocoaStyle); 128 129 m_cocoaNSWindow = NULL; 130 m_cocoaNSView = NULL; 131 // NOTE: We may need to deal with the contentView becoming a wx NSView as well. 132 NSWindow *newWindow; 133 // Create a WXNSPanel or a WXNSWindow depending on what type of window is desired. 134 if(style & wxFRAME_TOOL_WINDOW) 135 newWindow = [[WX_GET_OBJC_CLASS(WXNSPanel) alloc] initWithContentRect:cocoaRect styleMask:cocoaStyle backing:NSBackingStoreBuffered defer:NO]; 136 else 137 newWindow = [[WX_GET_OBJC_CLASS(WXNSWindow) alloc] initWithContentRect:cocoaRect styleMask:cocoaStyle backing:NSBackingStoreBuffered defer:NO]; 138 // Make sure the default content view is a WXNSView 139 [newWindow setContentView: [[WX_GET_OBJC_CLASS(WXNSView) alloc] initWithFrame: [[newWindow contentView] frame]]]; 140 // Associate the window and view 141 SetNSWindow(newWindow); 142 143 // NOTE: SetNSWindow has retained the Cocoa object for this object. 144 // Because we do not release on close, the following release matches the 145 // above alloc and thus the retain count will be 1. 146 [m_cocoaNSWindow release]; 147 148 if(style & wxFRAME_NO_TASKBAR) 149 [m_cocoaNSWindow setExcludedFromWindowsMenu: YES]; 150 if(style & wxSTAY_ON_TOP) 151 [m_cocoaNSWindow setLevel:NSFloatingWindowLevel]; 152 [m_cocoaNSWindow setTitle:wxNSStringWithWxString(title)]; 153 return true; 154} 155 156wxTopLevelWindowCocoa::~wxTopLevelWindowCocoa() 157{ 158 wxASSERT(sm_cocoaDeactivateWindow!=this); 159 wxAutoNSAutoreleasePool pool; 160 DestroyChildren(); 161 if(m_cocoaNSView) 162 SendDestroyEvent(); 163 SetNSWindow(NULL); 164} 165 166bool wxTopLevelWindowCocoa::Destroy() 167{ 168 if(sm_cocoaDeactivateWindow==this) 169 { 170 sm_cocoaDeactivateWindow = NULL; 171 wxTopLevelWindowCocoa::CocoaDelegate_windowDidResignKey(); 172 } 173 return wxTopLevelWindowBase::Destroy(); 174} 175 176// ---------------------------------------------------------------------------- 177// wxTopLevelWindowCocoa Cocoa Specifics 178// ---------------------------------------------------------------------------- 179 180wxMenuBar* wxTopLevelWindowCocoa::GetAppMenuBar(wxCocoaNSWindow *win) 181{ 182 wxTopLevelWindowCocoa *parent = wxDynamicCast(GetParent(),wxTopLevelWindow); 183 if(parent) 184 return parent->GetAppMenuBar(win); 185 return NULL; 186} 187 188void wxTopLevelWindowCocoa::SetNSWindow(WX_NSWindow cocoaNSWindow) 189{ 190 bool need_debug = cocoaNSWindow || m_cocoaNSWindow; 191 if(need_debug) wxLogTrace(wxTRACE_COCOA_RetainRelease,wxT("wxTopLevelWindowCocoa=%p::SetNSWindow [m_cocoaNSWindow=%p retainCount]=%d"),this,m_cocoaNSWindow,[m_cocoaNSWindow retainCount]); 192 DisassociateNSWindow(m_cocoaNSWindow); 193 wxGCSafeRetain(cocoaNSWindow); 194 wxGCSafeRelease(m_cocoaNSWindow); 195 m_cocoaNSWindow = cocoaNSWindow; 196 // NOTE: We are no longer using posing so we won't get events on the 197 // window's view unless it was explicitly created as the wx view class. 198 if(m_cocoaNSWindow) 199 SetNSView([m_cocoaNSWindow contentView]); 200 else 201 SetNSView(NULL); 202 AssociateNSWindow(m_cocoaNSWindow); 203 if(need_debug) wxLogTrace(wxTRACE_COCOA_RetainRelease,wxT("wxTopLevelWindowCocoa=%p::SetNSWindow [cocoaNSWindow=%p retainCount]=%d"),this,cocoaNSWindow,[cocoaNSWindow retainCount]); 204} 205 206void wxTopLevelWindowCocoa::CocoaReplaceView(WX_NSView oldView, WX_NSView newView) 207{ 208 if([m_cocoaNSWindow contentView] == (id)oldView) 209 [m_cocoaNSWindow setContentView:newView]; 210} 211 212/*static*/ void wxTopLevelWindowCocoa::DeactivatePendingWindow() 213{ 214 if(sm_cocoaDeactivateWindow) 215 sm_cocoaDeactivateWindow->wxTopLevelWindowCocoa::CocoaDelegate_windowDidResignKey(); 216 sm_cocoaDeactivateWindow = NULL; 217} 218 219void wxTopLevelWindowCocoa::CocoaDelegate_windowDidBecomeKey(void) 220{ 221 DeactivatePendingWindow(); 222 wxLogTrace(wxTRACE_COCOA,wxT("wxTopLevelWindowCocoa=%p::CocoaDelegate_windowDidBecomeKey"),this); 223 wxActivateEvent event(wxEVT_ACTIVATE, true, GetId()); 224 event.SetEventObject(this); 225 HandleWindowEvent(event); 226} 227 228void wxTopLevelWindowCocoa::CocoaDelegate_windowDidResignKey(void) 229{ 230 wxLogTrace(wxTRACE_COCOA,wxT("wxTopLevelWindowCocoa=%p::CocoaDelegate_windowDidResignKey"),this); 231 wxActivateEvent event(wxEVT_ACTIVATE, false, GetId()); 232 event.SetEventObject(this); 233 HandleWindowEvent(event); 234} 235 236void wxTopLevelWindowCocoa::CocoaDelegate_windowDidBecomeMain(void) 237{ 238 wxLogTrace(wxTRACE_COCOA,wxT("wxTopLevelWindowCocoa=%p::CocoaDelegate_windowDidBecomeMain"),this); 239} 240 241void wxTopLevelWindowCocoa::CocoaDelegate_windowDidResignMain(void) 242{ 243 wxLogTrace(wxTRACE_COCOA,wxT("wxTopLevelWindowCocoa=%p::CocoaDelegate_windowDidResignMain"),this); 244} 245 246void wxTopLevelWindowCocoa::CocoaDelegate_windowWillClose(void) 247{ 248 m_closed = true; 249 Destroy(); 250} 251 252bool wxTopLevelWindowCocoa::CocoaDelegate_windowShouldClose() 253{ 254 return wxWindowBase::Close(false); 255} 256 257void wxTopLevelWindowCocoa::CocoaDelegate_wxMenuItemAction(WX_NSMenuItem menuItem) 258{ 259} 260 261bool wxTopLevelWindowCocoa::CocoaDelegate_validateMenuItem(WX_NSMenuItem menuItem) 262{ 263 return false; 264} 265 266// ---------------------------------------------------------------------------- 267// wxTopLevelWindowCocoa maximize/minimize 268// ---------------------------------------------------------------------------- 269 270void wxTopLevelWindowCocoa::Maximize(bool maximize) 271{ 272} 273 274bool wxTopLevelWindowCocoa::IsMaximized() const 275{ 276 return false ; 277} 278 279void wxTopLevelWindowCocoa::Iconize(bool iconize) 280{ 281} 282 283bool wxTopLevelWindowCocoa::IsIconized() const 284{ 285 return false; 286} 287 288void wxTopLevelWindowCocoa::Restore() 289{ 290} 291 292bool wxTopLevelWindowCocoa::Show(bool show) 293{ 294 if(m_isShown == show) 295 return false; 296 wxAutoNSAutoreleasePool pool; 297 if(show) 298 { 299 // Send the window a size event because wxWidgets apps expect it 300 // NOTE: This should really only be done the first time a window 301 // is shown. I doubt this will cause any problems though. 302 wxSizeEvent event(GetSize(), GetId()); 303 event.SetEventObject(this); 304 HandleWindowEvent(event); 305 306 [m_cocoaNSWindow makeKeyAndOrderFront:m_cocoaNSWindow]; 307 } 308 else 309 [m_cocoaNSWindow orderOut:m_cocoaNSWindow]; 310 m_isShown = show; 311 return true; 312} 313 314bool wxTopLevelWindowCocoa::Close(bool force) 315{ 316 if(force) 317 return wxWindowBase::Close(force); 318 // performClose will fake the user clicking the close button which 319 // will invoke windowShouldClose which will call the base class version 320 // of Close() which will NOT Destroy() the window (see below) but 321 // if closing is not stopped, then performClose will go ahead and 322 // close the window which will send the close notifications setting 323 // m_closed to true and Destroy()ing the window. 324 [m_cocoaNSWindow performClose:m_cocoaNSWindow]; 325 return m_closed; 326} 327 328void wxTopLevelWindowCocoa::OnCloseWindow(wxCloseEvent& event) 329{ 330 // If the event was forced, close the window which will Destroy() it 331 if(!event.CanVeto()) 332 [m_cocoaNSWindow close]; 333 // if the event was not forced, it's probably because the user clicked 334 // the close button, or Close(false) was called which (see above) is 335 // redirected to performClose and thus Cocoa itself will close the window 336} 337 338// ---------------------------------------------------------------------------- 339// wxTopLevelWindowCocoa misc 340// ---------------------------------------------------------------------------- 341 342void wxTopLevelWindowCocoa::SetTitle(const wxString& title) 343{ 344 [m_cocoaNSWindow setTitle:wxNSStringWithWxString(title)]; 345} 346 347wxString wxTopLevelWindowCocoa::GetTitle() const 348{ 349 return wxStringWithNSString([m_cocoaNSWindow title]); 350} 351 352wxWindow* wxTopLevelWindowCocoa::SetDefaultItem(wxWindow *win) 353{ 354 wxWindow *old = wxTopLevelWindowBase::SetDefaultItem(win); 355 356 NSCell *newCell; 357 if(win != NULL) 358 { 359 NSView *newView = win->GetNSView(); 360 // newView does not have to be an NSControl, we only cast to NSControl* 361 // to silence the warning about cell not being implemented. 362 if(newView != nil && [newView respondsToSelector:@selector(cell)]) 363 newCell = [(NSControl*)newView cell]; 364 else 365 newCell = nil; 366 367 if(newCell != nil && ![newCell isKindOfClass:[NSButtonCell class]]) 368 { // It's not an NSButtonCell, set the default to nil. 369 newCell = nil; 370 } 371 } 372 else 373 newCell = nil; 374 375 [GetNSWindow() setDefaultButtonCell:(NSButtonCell*)newCell]; 376 return old; 377} 378 379bool wxTopLevelWindowCocoa::ShowFullScreen(bool show, long style) 380{ 381 return false; 382} 383 384bool wxTopLevelWindowCocoa::IsFullScreen() const 385{ 386 return false; 387} 388 389void wxTopLevelWindowCocoa::CocoaSetWxWindowSize(int width, int height) 390{ 391 // Set the NSView size by setting the frame size to enclose it 392 unsigned int styleMask = [m_cocoaNSWindow styleMask]; 393 NSRect oldFrameRect = [m_cocoaNSWindow frame]; 394 NSRect contentRect = [NSWindow 395 contentRectForFrameRect: oldFrameRect 396 styleMask: styleMask]; 397 contentRect.size.width = width; 398 contentRect.size.height = height; 399 NSRect newFrameRect = [NSWindow 400 frameRectForContentRect: contentRect 401 styleMask: styleMask]; 402 403 // Cocoa uses +y is up but wxWidgets uses +y is down. We want an increase/decrease in height 404 // to not effect where the top of the window is placed so we set the new y origin relative the 405 // old one taking the height change into account. 406 newFrameRect.origin.y = oldFrameRect.origin.y + oldFrameRect.size.height - newFrameRect.size.height; 407 408 [m_cocoaNSWindow setFrame: newFrameRect display: NO]; 409} 410 411void wxTopLevelWindowCocoa::DoMoveWindow(int x, int y, int width, int height) 412{ 413 wxLogTrace(wxTRACE_COCOA_TopLevelWindow_Size,wxT("wxTopLevelWindow=%p::DoMoveWindow(%d,%d,%d,%d)"),this,x,y,width,height); 414 415 NSPoint cocoaOrigin = OriginInCocoaScreenCoordinatesForRectInWxDisplayCoordinates(x,y,width,height,false); 416 417 NSRect cocoaRect = NSMakeRect(cocoaOrigin.x,cocoaOrigin.y,width,height); 418 [m_cocoaNSWindow setFrame: cocoaRect display:NO]; 419} 420 421void wxTopLevelWindowCocoa::DoGetSize(int *w, int *h) const 422{ 423 NSRect cocoaRect = [m_cocoaNSWindow frame]; 424 if(w) 425 *w=(int)cocoaRect.size.width; 426 if(h) 427 *h=(int)cocoaRect.size.height; 428 wxLogTrace(wxTRACE_COCOA_TopLevelWindow_Size,wxT("wxTopLevelWindow=%p::DoGetSize = (%d,%d)"),this,(int)cocoaRect.size.width,(int)cocoaRect.size.height); 429} 430 431void wxTopLevelWindowCocoa::DoGetPosition(int *x, int *y) const 432{ 433 NSRect windowFrame = [m_cocoaNSWindow frame]; 434 435 wxPoint theWxOrigin = OriginInWxDisplayCoordinatesForRectInCocoaScreenCoordinates(windowFrame); 436 437 if(*x) 438 *x = theWxOrigin.x; 439 if(*y) 440 *y = theWxOrigin.y; 441 442 wxLogTrace(wxTRACE_COCOA_TopLevelWindow_Size,wxT("wxTopLevelWindow=%p::DoGetPosition = (%d,%d)"),this,(int)theWxOrigin.x,(int)theWxOrigin.y); 443} 444