|
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 |