Bug 1793629 - Implement attention indicator for the unified extensions button, r...
[gecko.git] / widget / cocoa / nsWindowMap.mm
blob2f6442400e07af66366f225c9b87426d419f6517
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 {
30   NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
32   static WindowDataMap* sWindowMap = nil;
33   if (!sWindowMap) sWindowMap = [[WindowDataMap alloc] init];
35   return sWindowMap;
37   NS_OBJC_END_TRY_BLOCK_RETURN(nil);
40 - (id)init {
41   NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
43   if ((self = [super init])) {
44     mWindowMap = [[NSMutableDictionary alloc] initWithCapacity:10];
45   }
46   return self;
48   NS_OBJC_END_TRY_BLOCK_RETURN(nil);
51 - (void)dealloc {
52   NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
54   [mWindowMap release];
55   [super dealloc];
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
67   [windowData release];
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);
104 @end
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
120                                                object:inWindow];
122     [[NSNotificationCenter defaultCenter] addObserver:self
123                                              selector:@selector(windowResignedKey:)
124                                                  name:NSWindowDidResignKeyNotification
125                                                object:inWindow];
127     [[NSNotificationCenter defaultCenter] addObserver:self
128                                              selector:@selector(windowBecameMain:)
129                                                  name:NSWindowDidBecomeMainNotification
130                                                object:inWindow];
132     [[NSNotificationCenter defaultCenter] addObserver:self
133                                              selector:@selector(windowResignedMain:)
134                                                  name:NSWindowDidResignMainNotification
135                                                object:inWindow];
137     [[NSNotificationCenter defaultCenter] addObserver:self
138                                              selector:@selector(windowWillClose:)
139                                                  name:NSWindowWillCloseNotification
140                                                object:inWindow];
141   }
142   return self;
144   NS_OBJC_END_TRY_BLOCK_RETURN(nil);
147 - (void)dealloc {
148   NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
150   [[NSNotificationCenter defaultCenter] removeObserver:self];
151   [super dealloc];
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];
232   }
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];
243   }
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
255   // closes.
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;
280 @end