widget/cocoa/nsWindowMap.mm

changeset 2
7e26c7da4463
equal deleted inserted replaced
-1:000000000000 0:bd0e564ebb7b
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/. */
5
6 #include "nsWindowMap.h"
7 #include "nsObjCExceptions.h"
8 #include "nsChildView.h"
9 #include "nsCocoaWindow.h"
10
11 @interface WindowDataMap(Private)
12
13 - (NSString*)keyForWindow:(NSWindow*)inWindow;
14
15 @end
16
17 @interface TopLevelWindowData(Private)
18
19 - (void)windowResignedKey:(NSNotification*)inNotification;
20 - (void)windowBecameKey:(NSNotification*)inNotification;
21 - (void)windowWillClose:(NSNotification*)inNotification;
22
23 @end
24
25 #pragma mark -
26
27 @implementation WindowDataMap
28
29 + (WindowDataMap*)sharedWindowDataMap
30 {
31 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
32
33 static WindowDataMap* sWindowMap = nil;
34 if (!sWindowMap)
35 sWindowMap = [[WindowDataMap alloc] init];
36
37 return sWindowMap;
38
39 NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
40 }
41
42 - (id)init
43 {
44 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
45
46 if ((self = [super init])) {
47 mWindowMap = [[NSMutableDictionary alloc] initWithCapacity:10];
48 }
49 return self;
50
51 NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
52 }
53
54 - (void)dealloc
55 {
56 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
57
58 [mWindowMap release];
59 [super dealloc];
60
61 NS_OBJC_END_TRY_ABORT_BLOCK;
62 }
63
64 - (void)ensureDataForWindow:(NSWindow*)inWindow
65 {
66 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
67
68 if (!inWindow || [self dataForWindow:inWindow])
69 return;
70
71 TopLevelWindowData* windowData = [[TopLevelWindowData alloc] initWithWindow:inWindow];
72 [self setData:windowData forWindow:inWindow]; // takes ownership
73 [windowData release];
74
75 NS_OBJC_END_TRY_ABORT_BLOCK;
76 }
77
78 - (id)dataForWindow:(NSWindow*)inWindow
79 {
80 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
81
82 return [mWindowMap objectForKey:[self keyForWindow:inWindow]];
83
84 NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
85 }
86
87 - (void)setData:(id)inData forWindow:(NSWindow*)inWindow
88 {
89 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
90
91 [mWindowMap setObject:inData forKey:[self keyForWindow:inWindow]];
92
93 NS_OBJC_END_TRY_ABORT_BLOCK;
94 }
95
96 - (void)removeDataForWindow:(NSWindow*)inWindow
97 {
98 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
99
100 [mWindowMap removeObjectForKey:[self keyForWindow:inWindow]];
101
102 NS_OBJC_END_TRY_ABORT_BLOCK;
103 }
104
105 - (NSString*)keyForWindow:(NSWindow*)inWindow
106 {
107 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
108
109 return [NSString stringWithFormat:@"%p", inWindow];
110
111 NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
112 }
113
114 @end
115
116 // TopLevelWindowData
117 //
118 // This class holds data about top-level windows. We can't use a window
119 // delegate, because an embedder may already have one.
120
121 @implementation TopLevelWindowData
122
123 - (id)initWithWindow:(NSWindow*)inWindow
124 {
125 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
126
127 if ((self = [super init])) {
128 [[NSNotificationCenter defaultCenter] addObserver:self
129 selector:@selector(windowBecameKey:)
130 name:NSWindowDidBecomeKeyNotification
131 object:inWindow];
132
133 [[NSNotificationCenter defaultCenter] addObserver:self
134 selector:@selector(windowResignedKey:)
135 name:NSWindowDidResignKeyNotification
136 object:inWindow];
137
138 [[NSNotificationCenter defaultCenter] addObserver:self
139 selector:@selector(windowBecameMain:)
140 name:NSWindowDidBecomeMainNotification
141 object:inWindow];
142
143 [[NSNotificationCenter defaultCenter] addObserver:self
144 selector:@selector(windowResignedMain:)
145 name:NSWindowDidResignMainNotification
146 object:inWindow];
147
148 [[NSNotificationCenter defaultCenter] addObserver:self
149 selector:@selector(windowWillClose:)
150 name:NSWindowWillCloseNotification
151 object:inWindow];
152 }
153 return self;
154
155 NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
156 }
157
158 - (void)dealloc
159 {
160 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
161
162 [[NSNotificationCenter defaultCenter] removeObserver:self];
163 [super dealloc];
164
165 NS_OBJC_END_TRY_ABORT_BLOCK;
166 }
167
168 // As best I can tell, if the notification's object has a corresponding
169 // top-level widget (an nsCocoaWindow object), it has a delegate (set in
170 // nsCocoaWindow::StandardCreate()) of class WindowDelegate, and otherwise
171 // not (Camino doesn't use top-level widgets (nsCocoaWindow objects) --
172 // only child widgets (nsChildView objects)). (The notification is sent
173 // to windowBecameKey: or windowBecameMain: below.)
174 //
175 // For use with clients that (like Firefox) do use top-level widgets (and
176 // have NSWindow delegates of class WindowDelegate).
177 + (void)activateInWindow:(NSWindow*)aWindow
178 {
179 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
180
181 WindowDelegate* delegate = (WindowDelegate*) [aWindow delegate];
182 if (!delegate || ![delegate isKindOfClass:[WindowDelegate class]])
183 return;
184
185 if ([delegate toplevelActiveState])
186 return;
187 [delegate sendToplevelActivateEvents];
188
189 NS_OBJC_END_TRY_ABORT_BLOCK;
190 }
191
192 // See comments above activateInWindow:
193 //
194 // If we're using top-level widgets (nsCocoaWindow objects), we send them
195 // NS_DEACTIVATE events (which propagate to child widgets (nsChildView
196 // objects) via nsWebShellWindow::HandleEvent()).
197 //
198 // For use with clients that (like Firefox) do use top-level widgets (and
199 // have NSWindow delegates of class WindowDelegate).
200 + (void)deactivateInWindow:(NSWindow*)aWindow
201 {
202 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
203
204 WindowDelegate* delegate = (WindowDelegate*) [aWindow delegate];
205 if (!delegate || ![delegate isKindOfClass:[WindowDelegate class]])
206 return;
207
208 if (![delegate toplevelActiveState])
209 return;
210 [delegate sendToplevelDeactivateEvents];
211
212 NS_OBJC_END_TRY_ABORT_BLOCK;
213 }
214
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)activateInWindowViews:(NSWindow*)aWindow
218 {
219 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
220
221 id firstResponder = [aWindow firstResponder];
222 if ([firstResponder isKindOfClass:[ChildView class]])
223 [firstResponder viewsWindowDidBecomeKey];
224
225 NS_OBJC_END_TRY_ABORT_BLOCK;
226 }
227
228 // For use with clients that (like Camino) don't use top-level widgets (and
229 // don't have NSWindow delegates of class WindowDelegate).
230 + (void)deactivateInWindowViews:(NSWindow*)aWindow
231 {
232 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
233
234 id firstResponder = [aWindow firstResponder];
235 if ([firstResponder isKindOfClass:[ChildView class]])
236 [firstResponder viewsWindowDidResignKey];
237
238 NS_OBJC_END_TRY_ABORT_BLOCK;
239 }
240
241 // We make certain exceptions for top-level windows in non-embedders (see
242 // comment above windowBecameMain below). And we need (elsewhere) to guard
243 // against sending duplicate events. But in general the NS_ACTIVATE event
244 // should be sent when a native window becomes key, and the NS_DEACTIVATE
245 // event should be sent when it resignes key.
246 - (void)windowBecameKey:(NSNotification*)inNotification
247 {
248 NSWindow* window = (NSWindow*)[inNotification object];
249
250 id delegate = [window delegate];
251 if (!delegate || ![delegate isKindOfClass:[WindowDelegate class]]) {
252 [TopLevelWindowData activateInWindowViews:window];
253 } else if ([window isSheet]) {
254 [TopLevelWindowData activateInWindow:window];
255 }
256
257 [[window contentView] setNeedsDisplay:YES];
258 }
259
260 - (void)windowResignedKey:(NSNotification*)inNotification
261 {
262 NSWindow* window = (NSWindow*)[inNotification object];
263
264 id delegate = [window delegate];
265 if (!delegate || ![delegate isKindOfClass:[WindowDelegate class]]) {
266 [TopLevelWindowData deactivateInWindowViews:window];
267 } else if ([window isSheet]) {
268 [TopLevelWindowData deactivateInWindow:window];
269 }
270
271 [[window contentView] setNeedsDisplay:YES];
272 }
273
274 // The appearance of a top-level window depends on its main state (not its key
275 // state). So (for non-embedders) we need to ensure that a top-level window
276 // is main when an NS_ACTIVATE event is sent to Gecko for it.
277 - (void)windowBecameMain:(NSNotification*)inNotification
278 {
279 NSWindow* window = (NSWindow*)[inNotification object];
280
281 id delegate = [window delegate];
282 // Don't send events to a top-level window that has a sheet open above it --
283 // as far as Gecko is concerned, it's inactive, and stays so until the sheet
284 // closes.
285 if (delegate && [delegate isKindOfClass:[WindowDelegate class]] && ![window attachedSheet])
286 [TopLevelWindowData activateInWindow:window];
287 }
288
289 - (void)windowResignedMain:(NSNotification*)inNotification
290 {
291 NSWindow* window = (NSWindow*)[inNotification object];
292
293 id delegate = [window delegate];
294 if (delegate && [delegate isKindOfClass:[WindowDelegate class]] && ![window attachedSheet])
295 [TopLevelWindowData deactivateInWindow:window];
296 }
297
298 - (void)windowWillClose:(NSNotification*)inNotification
299 {
300 NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
301
302 // postpone our destruction
303 [[self retain] autorelease];
304
305 // remove ourselves from the window map (which owns us)
306 [[WindowDataMap sharedWindowDataMap] removeDataForWindow:[inNotification object]];
307
308 NS_OBJC_END_TRY_ABORT_BLOCK;
309 }
310
311 @end

mercurial