michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "nsWindowMap.h" michael@0: #include "nsObjCExceptions.h" michael@0: #include "nsChildView.h" michael@0: #include "nsCocoaWindow.h" michael@0: michael@0: @interface WindowDataMap(Private) michael@0: michael@0: - (NSString*)keyForWindow:(NSWindow*)inWindow; michael@0: michael@0: @end michael@0: michael@0: @interface TopLevelWindowData(Private) michael@0: michael@0: - (void)windowResignedKey:(NSNotification*)inNotification; michael@0: - (void)windowBecameKey:(NSNotification*)inNotification; michael@0: - (void)windowWillClose:(NSNotification*)inNotification; michael@0: michael@0: @end michael@0: michael@0: #pragma mark - michael@0: michael@0: @implementation WindowDataMap michael@0: michael@0: + (WindowDataMap*)sharedWindowDataMap michael@0: { michael@0: NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; michael@0: michael@0: static WindowDataMap* sWindowMap = nil; michael@0: if (!sWindowMap) michael@0: sWindowMap = [[WindowDataMap alloc] init]; michael@0: michael@0: return sWindowMap; michael@0: michael@0: NS_OBJC_END_TRY_ABORT_BLOCK_NIL; michael@0: } michael@0: michael@0: - (id)init michael@0: { michael@0: NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; michael@0: michael@0: if ((self = [super init])) { michael@0: mWindowMap = [[NSMutableDictionary alloc] initWithCapacity:10]; michael@0: } michael@0: return self; michael@0: michael@0: NS_OBJC_END_TRY_ABORT_BLOCK_NIL; michael@0: } michael@0: michael@0: - (void)dealloc michael@0: { michael@0: NS_OBJC_BEGIN_TRY_ABORT_BLOCK; michael@0: michael@0: [mWindowMap release]; michael@0: [super dealloc]; michael@0: michael@0: NS_OBJC_END_TRY_ABORT_BLOCK; michael@0: } michael@0: michael@0: - (void)ensureDataForWindow:(NSWindow*)inWindow michael@0: { michael@0: NS_OBJC_BEGIN_TRY_ABORT_BLOCK; michael@0: michael@0: if (!inWindow || [self dataForWindow:inWindow]) michael@0: return; michael@0: michael@0: TopLevelWindowData* windowData = [[TopLevelWindowData alloc] initWithWindow:inWindow]; michael@0: [self setData:windowData forWindow:inWindow]; // takes ownership michael@0: [windowData release]; michael@0: michael@0: NS_OBJC_END_TRY_ABORT_BLOCK; michael@0: } michael@0: michael@0: - (id)dataForWindow:(NSWindow*)inWindow michael@0: { michael@0: NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; michael@0: michael@0: return [mWindowMap objectForKey:[self keyForWindow:inWindow]]; michael@0: michael@0: NS_OBJC_END_TRY_ABORT_BLOCK_NIL; michael@0: } michael@0: michael@0: - (void)setData:(id)inData forWindow:(NSWindow*)inWindow michael@0: { michael@0: NS_OBJC_BEGIN_TRY_ABORT_BLOCK; michael@0: michael@0: [mWindowMap setObject:inData forKey:[self keyForWindow:inWindow]]; michael@0: michael@0: NS_OBJC_END_TRY_ABORT_BLOCK; michael@0: } michael@0: michael@0: - (void)removeDataForWindow:(NSWindow*)inWindow michael@0: { michael@0: NS_OBJC_BEGIN_TRY_ABORT_BLOCK; michael@0: michael@0: [mWindowMap removeObjectForKey:[self keyForWindow:inWindow]]; michael@0: michael@0: NS_OBJC_END_TRY_ABORT_BLOCK; michael@0: } michael@0: michael@0: - (NSString*)keyForWindow:(NSWindow*)inWindow michael@0: { michael@0: NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; michael@0: michael@0: return [NSString stringWithFormat:@"%p", inWindow]; michael@0: michael@0: NS_OBJC_END_TRY_ABORT_BLOCK_NIL; michael@0: } michael@0: michael@0: @end michael@0: michael@0: // TopLevelWindowData michael@0: // michael@0: // This class holds data about top-level windows. We can't use a window michael@0: // delegate, because an embedder may already have one. michael@0: michael@0: @implementation TopLevelWindowData michael@0: michael@0: - (id)initWithWindow:(NSWindow*)inWindow michael@0: { michael@0: NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; michael@0: michael@0: if ((self = [super init])) { michael@0: [[NSNotificationCenter defaultCenter] addObserver:self michael@0: selector:@selector(windowBecameKey:) michael@0: name:NSWindowDidBecomeKeyNotification michael@0: object:inWindow]; michael@0: michael@0: [[NSNotificationCenter defaultCenter] addObserver:self michael@0: selector:@selector(windowResignedKey:) michael@0: name:NSWindowDidResignKeyNotification michael@0: object:inWindow]; michael@0: michael@0: [[NSNotificationCenter defaultCenter] addObserver:self michael@0: selector:@selector(windowBecameMain:) michael@0: name:NSWindowDidBecomeMainNotification michael@0: object:inWindow]; michael@0: michael@0: [[NSNotificationCenter defaultCenter] addObserver:self michael@0: selector:@selector(windowResignedMain:) michael@0: name:NSWindowDidResignMainNotification michael@0: object:inWindow]; michael@0: michael@0: [[NSNotificationCenter defaultCenter] addObserver:self michael@0: selector:@selector(windowWillClose:) michael@0: name:NSWindowWillCloseNotification michael@0: object:inWindow]; michael@0: } michael@0: return self; michael@0: michael@0: NS_OBJC_END_TRY_ABORT_BLOCK_NIL; michael@0: } michael@0: michael@0: - (void)dealloc michael@0: { michael@0: NS_OBJC_BEGIN_TRY_ABORT_BLOCK; michael@0: michael@0: [[NSNotificationCenter defaultCenter] removeObserver:self]; michael@0: [super dealloc]; michael@0: michael@0: NS_OBJC_END_TRY_ABORT_BLOCK; michael@0: } michael@0: michael@0: // As best I can tell, if the notification's object has a corresponding michael@0: // top-level widget (an nsCocoaWindow object), it has a delegate (set in michael@0: // nsCocoaWindow::StandardCreate()) of class WindowDelegate, and otherwise michael@0: // not (Camino doesn't use top-level widgets (nsCocoaWindow objects) -- michael@0: // only child widgets (nsChildView objects)). (The notification is sent michael@0: // to windowBecameKey: or windowBecameMain: below.) michael@0: // michael@0: // For use with clients that (like Firefox) do use top-level widgets (and michael@0: // have NSWindow delegates of class WindowDelegate). michael@0: + (void)activateInWindow:(NSWindow*)aWindow michael@0: { michael@0: NS_OBJC_BEGIN_TRY_ABORT_BLOCK; michael@0: michael@0: WindowDelegate* delegate = (WindowDelegate*) [aWindow delegate]; michael@0: if (!delegate || ![delegate isKindOfClass:[WindowDelegate class]]) michael@0: return; michael@0: michael@0: if ([delegate toplevelActiveState]) michael@0: return; michael@0: [delegate sendToplevelActivateEvents]; michael@0: michael@0: NS_OBJC_END_TRY_ABORT_BLOCK; michael@0: } michael@0: michael@0: // See comments above activateInWindow: michael@0: // michael@0: // If we're using top-level widgets (nsCocoaWindow objects), we send them michael@0: // NS_DEACTIVATE events (which propagate to child widgets (nsChildView michael@0: // objects) via nsWebShellWindow::HandleEvent()). michael@0: // michael@0: // For use with clients that (like Firefox) do use top-level widgets (and michael@0: // have NSWindow delegates of class WindowDelegate). michael@0: + (void)deactivateInWindow:(NSWindow*)aWindow michael@0: { michael@0: NS_OBJC_BEGIN_TRY_ABORT_BLOCK; michael@0: michael@0: WindowDelegate* delegate = (WindowDelegate*) [aWindow delegate]; michael@0: if (!delegate || ![delegate isKindOfClass:[WindowDelegate class]]) michael@0: return; michael@0: michael@0: if (![delegate toplevelActiveState]) michael@0: return; michael@0: [delegate sendToplevelDeactivateEvents]; michael@0: michael@0: NS_OBJC_END_TRY_ABORT_BLOCK; michael@0: } michael@0: michael@0: // For use with clients that (like Camino) don't use top-level widgets (and michael@0: // don't have NSWindow delegates of class WindowDelegate). michael@0: + (void)activateInWindowViews:(NSWindow*)aWindow michael@0: { michael@0: NS_OBJC_BEGIN_TRY_ABORT_BLOCK; michael@0: michael@0: id firstResponder = [aWindow firstResponder]; michael@0: if ([firstResponder isKindOfClass:[ChildView class]]) michael@0: [firstResponder viewsWindowDidBecomeKey]; michael@0: michael@0: NS_OBJC_END_TRY_ABORT_BLOCK; michael@0: } michael@0: michael@0: // For use with clients that (like Camino) don't use top-level widgets (and michael@0: // don't have NSWindow delegates of class WindowDelegate). michael@0: + (void)deactivateInWindowViews:(NSWindow*)aWindow michael@0: { michael@0: NS_OBJC_BEGIN_TRY_ABORT_BLOCK; michael@0: michael@0: id firstResponder = [aWindow firstResponder]; michael@0: if ([firstResponder isKindOfClass:[ChildView class]]) michael@0: [firstResponder viewsWindowDidResignKey]; michael@0: michael@0: NS_OBJC_END_TRY_ABORT_BLOCK; michael@0: } michael@0: michael@0: // We make certain exceptions for top-level windows in non-embedders (see michael@0: // comment above windowBecameMain below). And we need (elsewhere) to guard michael@0: // against sending duplicate events. But in general the NS_ACTIVATE event michael@0: // should be sent when a native window becomes key, and the NS_DEACTIVATE michael@0: // event should be sent when it resignes key. michael@0: - (void)windowBecameKey:(NSNotification*)inNotification michael@0: { michael@0: NSWindow* window = (NSWindow*)[inNotification object]; michael@0: michael@0: id delegate = [window delegate]; michael@0: if (!delegate || ![delegate isKindOfClass:[WindowDelegate class]]) { michael@0: [TopLevelWindowData activateInWindowViews:window]; michael@0: } else if ([window isSheet]) { michael@0: [TopLevelWindowData activateInWindow:window]; michael@0: } michael@0: michael@0: [[window contentView] setNeedsDisplay:YES]; michael@0: } michael@0: michael@0: - (void)windowResignedKey:(NSNotification*)inNotification michael@0: { michael@0: NSWindow* window = (NSWindow*)[inNotification object]; michael@0: michael@0: id delegate = [window delegate]; michael@0: if (!delegate || ![delegate isKindOfClass:[WindowDelegate class]]) { michael@0: [TopLevelWindowData deactivateInWindowViews:window]; michael@0: } else if ([window isSheet]) { michael@0: [TopLevelWindowData deactivateInWindow:window]; michael@0: } michael@0: michael@0: [[window contentView] setNeedsDisplay:YES]; michael@0: } michael@0: michael@0: // The appearance of a top-level window depends on its main state (not its key michael@0: // state). So (for non-embedders) we need to ensure that a top-level window michael@0: // is main when an NS_ACTIVATE event is sent to Gecko for it. michael@0: - (void)windowBecameMain:(NSNotification*)inNotification michael@0: { michael@0: NSWindow* window = (NSWindow*)[inNotification object]; michael@0: michael@0: id delegate = [window delegate]; michael@0: // Don't send events to a top-level window that has a sheet open above it -- michael@0: // as far as Gecko is concerned, it's inactive, and stays so until the sheet michael@0: // closes. michael@0: if (delegate && [delegate isKindOfClass:[WindowDelegate class]] && ![window attachedSheet]) michael@0: [TopLevelWindowData activateInWindow:window]; michael@0: } michael@0: michael@0: - (void)windowResignedMain:(NSNotification*)inNotification michael@0: { michael@0: NSWindow* window = (NSWindow*)[inNotification object]; michael@0: michael@0: id delegate = [window delegate]; michael@0: if (delegate && [delegate isKindOfClass:[WindowDelegate class]] && ![window attachedSheet]) michael@0: [TopLevelWindowData deactivateInWindow:window]; michael@0: } michael@0: michael@0: - (void)windowWillClose:(NSNotification*)inNotification michael@0: { michael@0: NS_OBJC_BEGIN_TRY_ABORT_BLOCK; michael@0: michael@0: // postpone our destruction michael@0: [[self retain] autorelease]; michael@0: michael@0: // remove ourselves from the window map (which owns us) michael@0: [[WindowDataMap sharedWindowDataMap] removeDataForWindow:[inNotification object]]; michael@0: michael@0: NS_OBJC_END_TRY_ABORT_BLOCK; michael@0: } michael@0: michael@0: @end