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