1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "nsWindowMap.h"
7 #include "nsObjCExceptions.h"
8 #include "nsChildView.h"
9 #include "nsCocoaWindow.h"
11 @interface WindowDataMap (Private)
13 - (NSString*)keyForWindow:(NSWindow*)inWindow;
17 @interface TopLevelWindowData (Private)
19 - (void)windowResignedKey:(NSNotification*)inNotification;
20 - (void)windowBecameKey:(NSNotification*)inNotification;
21 - (void)windowWillClose:(NSNotification*)inNotification;
27 @implementation WindowDataMap
29 + (WindowDataMap*)sharedWindowDataMap {
30 NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
32 static WindowDataMap* sWindowMap = nil;
33 if (!sWindowMap) sWindowMap = [[WindowDataMap alloc] init];
37 NS_OBJC_END_TRY_BLOCK_RETURN(nil);
41 NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
43 if ((self = [super init])) {
44 mWindowMap = [[NSMutableDictionary alloc] initWithCapacity:10];
48 NS_OBJC_END_TRY_BLOCK_RETURN(nil);
52 NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
57 NS_OBJC_END_TRY_IGNORE_BLOCK;
60 - (void)ensureDataForWindow:(NSWindow*)inWindow {
61 NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
63 if (!inWindow || [self dataForWindow:inWindow]) return;
65 TopLevelWindowData* windowData = [[TopLevelWindowData alloc] initWithWindow:inWindow];
66 [self setData:windowData forWindow:inWindow]; // takes ownership
69 NS_OBJC_END_TRY_IGNORE_BLOCK;
72 - (id)dataForWindow:(NSWindow*)inWindow {
73 NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
75 return [mWindowMap objectForKey:[self keyForWindow:inWindow]];
77 NS_OBJC_END_TRY_BLOCK_RETURN(nil);
80 - (void)setData:(id)inData forWindow:(NSWindow*)inWindow {
81 NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
83 [mWindowMap setObject:inData forKey:[self keyForWindow:inWindow]];
85 NS_OBJC_END_TRY_IGNORE_BLOCK;
88 - (void)removeDataForWindow:(NSWindow*)inWindow {
89 NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
91 [mWindowMap removeObjectForKey:[self keyForWindow:inWindow]];
93 NS_OBJC_END_TRY_IGNORE_BLOCK;
96 - (NSString*)keyForWindow:(NSWindow*)inWindow {
97 NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
99 return [NSString stringWithFormat:@"%p", inWindow];
101 NS_OBJC_END_TRY_BLOCK_RETURN(nil);
106 // TopLevelWindowData
108 // This class holds data about top-level windows. We can't use a window
109 // delegate, because an embedder may already have one.
111 @implementation TopLevelWindowData
113 - (id)initWithWindow:(NSWindow*)inWindow {
114 NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
116 if ((self = [super init])) {
117 [[NSNotificationCenter defaultCenter] addObserver:self
118 selector:@selector(windowBecameKey:)
119 name:NSWindowDidBecomeKeyNotification
122 [[NSNotificationCenter defaultCenter] addObserver:self
123 selector:@selector(windowResignedKey:)
124 name:NSWindowDidResignKeyNotification
127 [[NSNotificationCenter defaultCenter] addObserver:self
128 selector:@selector(windowBecameMain:)
129 name:NSWindowDidBecomeMainNotification
132 [[NSNotificationCenter defaultCenter] addObserver:self
133 selector:@selector(windowResignedMain:)
134 name:NSWindowDidResignMainNotification
137 [[NSNotificationCenter defaultCenter] addObserver:self
138 selector:@selector(windowWillClose:)
139 name:NSWindowWillCloseNotification
144 NS_OBJC_END_TRY_BLOCK_RETURN(nil);
148 NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
150 [[NSNotificationCenter defaultCenter] removeObserver:self];
153 NS_OBJC_END_TRY_IGNORE_BLOCK;
156 // As best I can tell, if the notification's object has a corresponding
157 // top-level widget (an nsCocoaWindow object), it has a delegate (set in
158 // nsCocoaWindow::StandardCreate()) of class WindowDelegate, and otherwise
159 // not (Camino didn't use top-level widgets (nsCocoaWindow objects) --
160 // only child widgets (nsChildView objects)). (The notification is sent
161 // to windowBecameKey: or windowBecameMain: below.)
163 // For use with clients that (like Firefox) do use top-level widgets (and
164 // have NSWindow delegates of class WindowDelegate).
165 + (void)activateInWindow:(NSWindow*)aWindow {
166 NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
168 WindowDelegate* delegate = (WindowDelegate*)[aWindow delegate];
169 if (!delegate || ![delegate isKindOfClass:[WindowDelegate class]]) return;
171 if ([delegate toplevelActiveState]) return;
172 [delegate sendToplevelActivateEvents];
174 NS_OBJC_END_TRY_IGNORE_BLOCK;
177 // See comments above activateInWindow:
179 // If we're using top-level widgets (nsCocoaWindow objects), we send them
180 // NS_DEACTIVATE events (which propagate to child widgets (nsChildView
181 // objects) via nsWebShellWindow::HandleEvent()).
183 // For use with clients that (like Firefox) do use top-level widgets (and
184 // have NSWindow delegates of class WindowDelegate).
185 + (void)deactivateInWindow:(NSWindow*)aWindow {
186 NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
188 WindowDelegate* delegate = (WindowDelegate*)[aWindow delegate];
189 if (!delegate || ![delegate isKindOfClass:[WindowDelegate class]]) return;
191 if (![delegate toplevelActiveState]) return;
192 [delegate sendToplevelDeactivateEvents];
194 NS_OBJC_END_TRY_IGNORE_BLOCK;
197 // For use with clients that (like Camino) don't use top-level widgets (and
198 // don't have NSWindow delegates of class WindowDelegate).
199 + (void)activateInWindowViews:(NSWindow*)aWindow {
200 NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
202 id firstResponder = [aWindow firstResponder];
203 if ([firstResponder isKindOfClass:[ChildView class]]) [firstResponder viewsWindowDidBecomeKey];
205 NS_OBJC_END_TRY_IGNORE_BLOCK;
208 // For use with clients that (like Camino) don't use top-level widgets (and
209 // don't have NSWindow delegates of class WindowDelegate).
210 + (void)deactivateInWindowViews:(NSWindow*)aWindow {
211 NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
213 id firstResponder = [aWindow firstResponder];
214 if ([firstResponder isKindOfClass:[ChildView class]]) [firstResponder viewsWindowDidResignKey];
216 NS_OBJC_END_TRY_IGNORE_BLOCK;
219 // We make certain exceptions for top-level windows in non-embedders (see
220 // comment above windowBecameMain below). And we need (elsewhere) to guard
221 // against sending duplicate events. But in general the NS_ACTIVATE event
222 // should be sent when a native window becomes key, and the NS_DEACTIVATE
223 // event should be sent when it resignes key.
224 - (void)windowBecameKey:(NSNotification*)inNotification {
225 NSWindow* window = (NSWindow*)[inNotification object];
227 id delegate = [window delegate];
228 if (!delegate || ![delegate isKindOfClass:[WindowDelegate class]]) {
229 [TopLevelWindowData activateInWindowViews:window];
230 } else if ([window isSheet]) {
231 [TopLevelWindowData activateInWindow:window];
235 - (void)windowResignedKey:(NSNotification*)inNotification {
236 NSWindow* window = (NSWindow*)[inNotification object];
238 id delegate = [window delegate];
239 if (!delegate || ![delegate isKindOfClass:[WindowDelegate class]]) {
240 [TopLevelWindowData deactivateInWindowViews:window];
241 } else if ([window isSheet]) {
242 [TopLevelWindowData deactivateInWindow:window];
246 // The appearance of a top-level window depends on its main state (not its key
247 // state). So (for non-embedders) we need to ensure that a top-level window
248 // is main when an NS_ACTIVATE event is sent to Gecko for it.
249 - (void)windowBecameMain:(NSNotification*)inNotification {
250 NSWindow* window = (NSWindow*)[inNotification object];
252 id delegate = [window delegate];
253 // Don't send events to a top-level window that has a sheet open above it --
254 // as far as Gecko is concerned, it's inactive, and stays so until the sheet
256 if (delegate && [delegate isKindOfClass:[WindowDelegate class]] && ![window attachedSheet])
257 [TopLevelWindowData activateInWindow:window];
260 - (void)windowResignedMain:(NSNotification*)inNotification {
261 NSWindow* window = (NSWindow*)[inNotification object];
263 id delegate = [window delegate];
264 if (delegate && [delegate isKindOfClass:[WindowDelegate class]] && ![window attachedSheet])
265 [TopLevelWindowData deactivateInWindow:window];
268 - (void)windowWillClose:(NSNotification*)inNotification {
269 NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
271 // postpone our destruction
272 [[self retain] autorelease];
274 // remove ourselves from the window map (which owns us)
275 [[WindowDataMap sharedWindowDataMap] removeDataForWindow:[inNotification object]];
277 NS_OBJC_END_TRY_IGNORE_BLOCK;