Backed out changeset 3fe07c50c854 (bug 946316) for bustage. a=backout
[gecko.git] / widget / cocoa / nsWindowMap.mm
blob437533f5d4443a573d990f03cf25e00b8b385c8d
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;
15 @end
17 @interface TopLevelWindowData(Private)
19 - (void)windowResignedKey:(NSNotification*)inNotification;
20 - (void)windowBecameKey:(NSNotification*)inNotification;
21 - (void)windowWillClose:(NSNotification*)inNotification;
23 @end
25 #pragma mark -
27 @implementation WindowDataMap
29 + (WindowDataMap*)sharedWindowDataMap
31   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
33   static WindowDataMap* sWindowMap = nil;
34   if (!sWindowMap)
35     sWindowMap = [[WindowDataMap alloc] init];
37   return sWindowMap;
39   NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
42 - (id)init
44   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
46   if ((self = [super init])) {
47     mWindowMap = [[NSMutableDictionary alloc] initWithCapacity:10];
48   }
49   return self;
51   NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
54 - (void)dealloc
56   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
58   [mWindowMap release];
59   [super dealloc];
61   NS_OBJC_END_TRY_ABORT_BLOCK;
64 - (void)ensureDataForWindow:(NSWindow*)inWindow
66   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
68   if (!inWindow || [self dataForWindow:inWindow])
69     return;
71   TopLevelWindowData* windowData = [[TopLevelWindowData alloc] initWithWindow:inWindow];
72   [self setData:windowData forWindow:inWindow]; // takes ownership
73   [windowData release];
75   NS_OBJC_END_TRY_ABORT_BLOCK;
78 - (id)dataForWindow:(NSWindow*)inWindow
80   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
82   return [mWindowMap objectForKey:[self keyForWindow:inWindow]];
84   NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
87 - (void)setData:(id)inData forWindow:(NSWindow*)inWindow
89   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
91   [mWindowMap setObject:inData forKey:[self keyForWindow:inWindow]];
93   NS_OBJC_END_TRY_ABORT_BLOCK;
96 - (void)removeDataForWindow:(NSWindow*)inWindow
98   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
100   [mWindowMap removeObjectForKey:[self keyForWindow:inWindow]];
102   NS_OBJC_END_TRY_ABORT_BLOCK;
105 - (NSString*)keyForWindow:(NSWindow*)inWindow
107   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
109   return [NSString stringWithFormat:@"%p", inWindow];
111   NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
114 @end
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.
121 @implementation TopLevelWindowData
123 - (id)initWithWindow:(NSWindow*)inWindow
125   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
127   if ((self = [super init])) {
128     [[NSNotificationCenter defaultCenter] addObserver:self
129                                              selector:@selector(windowBecameKey:)
130                                                  name:NSWindowDidBecomeKeyNotification
131                                                object:inWindow];
133     [[NSNotificationCenter defaultCenter] addObserver:self
134                                              selector:@selector(windowResignedKey:)
135                                                  name:NSWindowDidResignKeyNotification
136                                                object:inWindow];
138     [[NSNotificationCenter defaultCenter] addObserver:self
139                                              selector:@selector(windowBecameMain:)
140                                                  name:NSWindowDidBecomeMainNotification
141                                                object:inWindow];
143     [[NSNotificationCenter defaultCenter] addObserver:self
144                                              selector:@selector(windowResignedMain:)
145                                                  name:NSWindowDidResignMainNotification
146                                                object:inWindow];
148     [[NSNotificationCenter defaultCenter] addObserver:self
149                                              selector:@selector(windowWillClose:)
150                                                  name:NSWindowWillCloseNotification
151                                                object:inWindow];
152   }
153   return self;
155   NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
158 - (void)dealloc
160   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
162   [[NSNotificationCenter defaultCenter] removeObserver:self];
163   [super dealloc];
165   NS_OBJC_END_TRY_ABORT_BLOCK;
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.)
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
179   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
181   WindowDelegate* delegate = (WindowDelegate*) [aWindow delegate];
182   if (!delegate || ![delegate isKindOfClass:[WindowDelegate class]])
183     return;
185   if ([delegate toplevelActiveState])
186     return;
187   [delegate sendToplevelActivateEvents];
189   NS_OBJC_END_TRY_ABORT_BLOCK;
192 // See comments above activateInWindow:
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()).
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
202   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
204   WindowDelegate* delegate = (WindowDelegate*) [aWindow delegate];
205   if (!delegate || ![delegate isKindOfClass:[WindowDelegate class]])
206     return;
208   if (![delegate toplevelActiveState])
209     return;
210   [delegate sendToplevelDeactivateEvents];
212   NS_OBJC_END_TRY_ABORT_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)activateInWindowViews:(NSWindow*)aWindow
219   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
221   id firstResponder = [aWindow firstResponder];
222   if ([firstResponder isKindOfClass:[ChildView class]])
223     [firstResponder viewsWindowDidBecomeKey];
225   NS_OBJC_END_TRY_ABORT_BLOCK;
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
232   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
234   id firstResponder = [aWindow firstResponder];
235   if ([firstResponder isKindOfClass:[ChildView class]])
236     [firstResponder viewsWindowDidResignKey];
238   NS_OBJC_END_TRY_ABORT_BLOCK;
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
248   NSWindow* window = (NSWindow*)[inNotification object];
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   }
257   [[window contentView] setNeedsDisplay:YES];
260 - (void)windowResignedKey:(NSNotification*)inNotification
262   NSWindow* window = (NSWindow*)[inNotification object];
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   }
271   [[window contentView] setNeedsDisplay:YES];
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
279   NSWindow* window = (NSWindow*)[inNotification object];
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];
289 - (void)windowResignedMain:(NSNotification*)inNotification
291   NSWindow* window = (NSWindow*)[inNotification object];
293   id delegate = [window delegate];
294   if (delegate && [delegate isKindOfClass:[WindowDelegate class]] && ![window attachedSheet])
295     [TopLevelWindowData deactivateInWindow:window];
298 - (void)windowWillClose:(NSNotification*)inNotification
300   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
302   // postpone our destruction
303   [[self retain] autorelease];
305   // remove ourselves from the window map (which owns us)
306   [[WindowDataMap sharedWindowDataMap] removeDataForWindow:[inNotification object]];
308   NS_OBJC_END_TRY_ABORT_BLOCK;
311 @end