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 =
66 [[TopLevelWindowData alloc] initWithWindow:inWindow];
67 [self setData:windowData forWindow:inWindow]; // takes ownership
70 NS_OBJC_END_TRY_IGNORE_BLOCK;
73 - (id)dataForWindow:(NSWindow*)inWindow {
74 NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
76 return [mWindowMap objectForKey:[self keyForWindow:inWindow]];
78 NS_OBJC_END_TRY_BLOCK_RETURN(nil);
81 - (void)setData:(id)inData forWindow:(NSWindow*)inWindow {
82 NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
84 [mWindowMap setObject:inData forKey:[self keyForWindow:inWindow]];
86 NS_OBJC_END_TRY_IGNORE_BLOCK;
89 - (void)removeDataForWindow:(NSWindow*)inWindow {
90 NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
92 [mWindowMap removeObjectForKey:[self keyForWindow:inWindow]];
94 NS_OBJC_END_TRY_IGNORE_BLOCK;
97 - (NSString*)keyForWindow:(NSWindow*)inWindow {
98 NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
100 return [NSString stringWithFormat:@"%p", inWindow];
102 NS_OBJC_END_TRY_BLOCK_RETURN(nil);
107 // TopLevelWindowData
109 // This class holds data about top-level windows. We can't use a window
110 // delegate, because an embedder may already have one.
112 @implementation TopLevelWindowData
114 - (id)initWithWindow:(NSWindow*)inWindow {
115 NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
117 if ((self = [super init])) {
118 [[NSNotificationCenter defaultCenter]
120 selector:@selector(windowBecameKey:)
121 name:NSWindowDidBecomeKeyNotification
124 [[NSNotificationCenter defaultCenter]
126 selector:@selector(windowResignedKey:)
127 name:NSWindowDidResignKeyNotification
130 [[NSNotificationCenter defaultCenter]
132 selector:@selector(windowBecameMain:)
133 name:NSWindowDidBecomeMainNotification
136 [[NSNotificationCenter defaultCenter]
138 selector:@selector(windowResignedMain:)
139 name:NSWindowDidResignMainNotification
142 [[NSNotificationCenter defaultCenter]
144 selector:@selector(windowWillClose:)
145 name:NSWindowWillCloseNotification
150 NS_OBJC_END_TRY_BLOCK_RETURN(nil);
154 NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
156 [[NSNotificationCenter defaultCenter] removeObserver:self];
159 NS_OBJC_END_TRY_IGNORE_BLOCK;
162 // As best I can tell, if the notification's object has a corresponding
163 // top-level widget (an nsCocoaWindow object), it has a delegate (set in
164 // nsCocoaWindow::StandardCreate()) of class WindowDelegate, and otherwise
165 // not (Camino didn't use top-level widgets (nsCocoaWindow objects) --
166 // only child widgets (nsChildView objects)). (The notification is sent
167 // to windowBecameKey: or windowBecameMain: below.)
169 // For use with clients that (like Firefox) do use top-level widgets (and
170 // have NSWindow delegates of class WindowDelegate).
171 + (void)activateInWindow:(NSWindow*)aWindow {
172 NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
174 WindowDelegate* delegate = (WindowDelegate*)[aWindow delegate];
175 if (!delegate || ![delegate isKindOfClass:[WindowDelegate class]]) return;
177 if ([delegate toplevelActiveState]) return;
178 [delegate sendToplevelActivateEvents];
180 NS_OBJC_END_TRY_IGNORE_BLOCK;
183 // See comments above activateInWindow:
185 // If we're using top-level widgets (nsCocoaWindow objects), we send them
186 // NS_DEACTIVATE events (which propagate to child widgets (nsChildView
187 // objects) via nsWebShellWindow::HandleEvent()).
189 // For use with clients that (like Firefox) do use top-level widgets (and
190 // have NSWindow delegates of class WindowDelegate).
191 + (void)deactivateInWindow:(NSWindow*)aWindow {
192 NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
194 WindowDelegate* delegate = (WindowDelegate*)[aWindow delegate];
195 if (!delegate || ![delegate isKindOfClass:[WindowDelegate class]]) return;
197 if (![delegate toplevelActiveState]) return;
198 [delegate sendToplevelDeactivateEvents];
200 NS_OBJC_END_TRY_IGNORE_BLOCK;
203 // For use with clients that (like Camino) don't use top-level widgets (and
204 // don't have NSWindow delegates of class WindowDelegate).
205 + (void)activateInWindowViews:(NSWindow*)aWindow {
206 NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
208 id firstResponder = [aWindow firstResponder];
209 if ([firstResponder isKindOfClass:[ChildView class]])
210 [firstResponder viewsWindowDidBecomeKey];
212 NS_OBJC_END_TRY_IGNORE_BLOCK;
215 // For use with clients that (like Camino) don't use top-level widgets (and
216 // don't have NSWindow delegates of class WindowDelegate).
217 + (void)deactivateInWindowViews:(NSWindow*)aWindow {
218 NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
220 id firstResponder = [aWindow firstResponder];
221 if ([firstResponder isKindOfClass:[ChildView class]])
222 [firstResponder viewsWindowDidResignKey];
224 NS_OBJC_END_TRY_IGNORE_BLOCK;
227 // We make certain exceptions for top-level windows in non-embedders (see
228 // comment above windowBecameMain below). And we need (elsewhere) to guard
229 // against sending duplicate events. But in general the NS_ACTIVATE event
230 // should be sent when a native window becomes key, and the NS_DEACTIVATE
231 // event should be sent when it resignes key.
232 - (void)windowBecameKey:(NSNotification*)inNotification {
233 NSWindow* window = (NSWindow*)[inNotification object];
235 id delegate = [window delegate];
236 if (!delegate || ![delegate isKindOfClass:[WindowDelegate class]]) {
237 [TopLevelWindowData activateInWindowViews:window];
238 } else if ([window isSheet] || [NSApp modalWindow]) {
239 [TopLevelWindowData activateInWindow:window];
243 - (void)windowResignedKey:(NSNotification*)inNotification {
244 NSWindow* window = (NSWindow*)[inNotification object];
246 id delegate = [window delegate];
247 if (!delegate || ![delegate isKindOfClass:[WindowDelegate class]]) {
248 [TopLevelWindowData deactivateInWindowViews:window];
249 } else if ([window isSheet] || [NSApp modalWindow]) {
250 [TopLevelWindowData deactivateInWindow:window];
254 // The appearance of a top-level window depends on its main state (not its key
255 // state). So (for non-embedders) we need to ensure that a top-level window
256 // is main when an NS_ACTIVATE event is sent to Gecko for it.
257 - (void)windowBecameMain:(NSNotification*)inNotification {
258 NSWindow* window = (NSWindow*)[inNotification object];
260 id delegate = [window delegate];
261 // Don't send events to a top-level window that has a sheet/modal-window open
262 // above it -- as far as Gecko is concerned, it's inactive, and stays so until
263 // the sheet/modal-window closes.
264 if (delegate && [delegate isKindOfClass:[WindowDelegate class]] &&
265 ![window attachedSheet] && ![NSApp modalWindow])
266 [TopLevelWindowData activateInWindow:window];
269 - (void)windowResignedMain:(NSNotification*)inNotification {
270 NSWindow* window = (NSWindow*)[inNotification object];
272 id delegate = [window delegate];
273 if (delegate && [delegate isKindOfClass:[WindowDelegate class]] &&
274 ![window attachedSheet] && ![NSApp modalWindow])
275 [TopLevelWindowData deactivateInWindow:window];
278 - (void)windowWillClose:(NSNotification*)inNotification {
279 NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
281 // postpone our destruction
282 [[self retain] autorelease];
284 // remove ourselves from the window map (which owns us)
285 [[WindowDataMap sharedWindowDataMap]
286 removeDataForWindow:[inNotification object]];
288 NS_OBJC_END_TRY_IGNORE_BLOCK;