Don't show supervised user as "already on this device" while they're being imported.
[chromium-blink-merge.git] / chrome / browser / ui / cocoa / constrained_window / constrained_window_sheet_controller.mm
blob7b3dc6a739d03aba8a94e6894c8fe7fb3a77ceff
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #import "chrome/browser/ui/cocoa/constrained_window/constrained_window_sheet_controller.h"
7 #include <map>
9 #include "base/logging.h"
10 #import "chrome/browser/ui/cocoa/constrained_window/constrained_window_sheet.h"
11 #include "chrome/browser/ui/cocoa/constrained_window/constrained_window_sheet_info.h"
12 #import "chrome/browser/ui/cocoa/web_contents_modal_dialog_host_cocoa.h"
14 namespace {
16 // Maps parent windows to sheet controllers.
17 NSMutableDictionary* g_sheetControllers;
19 // Get a value for the given window that can be used as a key in a dictionary.
20 NSValue* GetKeyForParentWindow(NSWindow* parent_window) {
21   return [NSValue valueWithNonretainedObject:parent_window];
24 // Returns the bounds to use when showing a sheet for a given parent view. This
25 // returns a rect in window coordinates.
26 NSRect GetSheetParentBoundsForParentView(NSView* view) {
27   // If the devtools view is open, it shrinks the size of the WebContents, so go
28   // up the hierarchy to the devtools container view to avoid that. Note that
29   // the devtools view is always in the hierarchy even if it is not open or it
30   // is detached.
31   NSView* devtools_view = [[[view superview] superview] superview];
32   if (devtools_view)
33     view = devtools_view;
34   return [view convertRect:[view bounds] toView:nil];
37 }  // namespace
39 // An invisible overlay window placed on top of the sheet's parent view.
40 // This window blocks interaction with the underlying view.
41 @interface CWSheetOverlayWindow : NSWindow {
42   base::scoped_nsobject<ConstrainedWindowSheetController> controller_;
44 @end
46 @interface ConstrainedWindowSheetController ()
47 - (id)initWithParentWindow:(NSWindow*)parentWindow;
48 - (ConstrainedWindowSheetInfo*)findSheetInfoForParentView:(NSView*)parentView;
49 - (ConstrainedWindowSheetInfo*)
50     findSheetInfoForSheet:(id<ConstrainedWindowSheet>)sheet;
51 - (void)onParentWindowWillClose:(NSNotification*)note;
52 - (void)onParentWindowSizeDidChange:(NSNotification*)note;
53 - (void)updateSheetPosition:(NSView*)parentView;
54 - (NSRect)overlayWindowFrameForParentView:(NSView*)parentView;
55 - (NSPoint)originForSheetSize:(NSSize)sheetSize
56               inContainerRect:(NSRect)containerRect;
57 - (void)onOverlayWindowMouseDown:(CWSheetOverlayWindow*)overlayWindow;
58 - (void)closeSheet:(ConstrainedWindowSheetInfo*)info
59      withAnimation:(BOOL)withAnimation;
60 @end
62 @implementation CWSheetOverlayWindow
64 - (id)initWithContentRect:(NSRect)rect
65                controller:(ConstrainedWindowSheetController*)controller {
66   if ((self = [super initWithContentRect:rect
67                                styleMask:NSBorderlessWindowMask
68                                  backing:NSBackingStoreBuffered
69                                    defer:NO])) {
70     [self setOpaque:NO];
71     [self setBackgroundColor:[NSColor clearColor]];
72     [self setIgnoresMouseEvents:NO];
73     [self setReleasedWhenClosed:NO];
74     controller_.reset([controller retain]);
75   }
76   return self;
79 - (void)mouseDown:(NSEvent*)event {
80   [controller_ onOverlayWindowMouseDown:self];
83 @end
85 @implementation ConstrainedWindowSheetController
87 + (ConstrainedWindowSheetController*)
88     controllerForParentWindow:(NSWindow*)parentWindow {
89   DCHECK(parentWindow);
90   ConstrainedWindowSheetController* controller =
91       [g_sheetControllers objectForKey:GetKeyForParentWindow(parentWindow)];
92   if (controller)
93     return controller;
95   base::scoped_nsobject<ConstrainedWindowSheetController> new_controller(
96       [[ConstrainedWindowSheetController alloc]
97           initWithParentWindow:parentWindow]);
98   if (!g_sheetControllers)
99     g_sheetControllers = [[NSMutableDictionary alloc] init];
100   [g_sheetControllers setObject:new_controller
101                          forKey:GetKeyForParentWindow(parentWindow)];
102   return new_controller;
105 + (ConstrainedWindowSheetController*)
106     controllerForSheet:(id<ConstrainedWindowSheet>)sheet {
107   for (ConstrainedWindowSheetController* controller in
108        [g_sheetControllers objectEnumerator]) {
109     if ([controller findSheetInfoForSheet:sheet])
110       return controller;
111   }
112   return nil;
115 + (id<ConstrainedWindowSheet>)sheetForOverlayWindow:(NSWindow*)overlayWindow {
116   for (ConstrainedWindowSheetController* controller in
117           [g_sheetControllers objectEnumerator]) {
118     for (ConstrainedWindowSheetInfo* info in controller->sheets_.get()) {
119       if ([overlayWindow isEqual:[info overlayWindow]])
120         return [info sheet];
121     }
122   }
123   return nil;
126 - (id)initWithParentWindow:(NSWindow*)parentWindow {
127   if ((self = [super init])) {
128     parentWindow_.reset([parentWindow retain]);
129     sheets_.reset([[NSMutableArray alloc] init]);
131     [[NSNotificationCenter defaultCenter]
132         addObserver:self
133            selector:@selector(onParentWindowWillClose:)
134                name:NSWindowWillCloseNotification
135              object:parentWindow_];
136   }
137   return self;
140 - (web_modal::WebContentsModalDialogHost*)dialogHost {
141   if (!dialogHost_)
142     dialogHost_.reset(new WebContentsModalDialogHostCocoa(self));
143   return dialogHost_.get();
146 - (NSWindow*)parentWindow {
147   return parentWindow_.get();
150 - (void)showSheet:(id<ConstrainedWindowSheet>)sheet
151     forParentView:(NSView*)parentView {
152   DCHECK(sheet);
153   DCHECK(parentView);
154   if (!activeView_.get())
155     activeView_.reset([parentView retain]);
157   // Observe the parent window's size.
158   [[NSNotificationCenter defaultCenter]
159       addObserver:self
160          selector:@selector(onParentWindowSizeDidChange:)
161              name:NSWindowDidResizeNotification
162            object:parentWindow_];
164   // Create an invisible overlay window.
165   NSRect rect = [self overlayWindowFrameForParentView:parentView];
166   base::scoped_nsobject<NSWindow> overlayWindow(
167       [[CWSheetOverlayWindow alloc] initWithContentRect:rect controller:self]);
168   [parentWindow_ addChildWindow:overlayWindow
169                         ordered:NSWindowAbove];
171   // Add an entry for the sheet.
172   base::scoped_nsobject<ConstrainedWindowSheetInfo> info(
173       [[ConstrainedWindowSheetInfo alloc] initWithSheet:sheet
174                                              parentView:parentView
175                                           overlayWindow:overlayWindow]);
176   [sheets_ addObject:info];
178   // Show or hide the sheet.
179   if ([activeView_ isEqual:parentView])
180     [info showSheet];
181   else
182     [info hideSheet];
185 - (NSPoint)originForSheet:(id<ConstrainedWindowSheet>)sheet
186            withWindowSize:(NSSize)size {
187   ConstrainedWindowSheetInfo* info = [self findSheetInfoForSheet:sheet];
188   DCHECK(info);
189   NSRect containerRect =
190       [self overlayWindowFrameForParentView:[info parentView]];
191   return [self originForSheetSize:size inContainerRect:containerRect];
194 - (void)closeSheet:(id<ConstrainedWindowSheet>)sheet {
195   ConstrainedWindowSheetInfo* info = [self findSheetInfoForSheet:sheet];
196   DCHECK(info);
197   [self closeSheet:info withAnimation:YES];
200 - (void)parentViewDidBecomeActive:(NSView*)parentView {
201   [[self findSheetInfoForParentView:activeView_] hideSheet];
202   activeView_.reset([parentView retain]);
203   [self updateSheetPosition:parentView];
204   [[self findSheetInfoForParentView:activeView_] showSheet];
207 - (void)pulseSheet:(id<ConstrainedWindowSheet>)sheet {
208   ConstrainedWindowSheetInfo* info = [self findSheetInfoForSheet:sheet];
209   DCHECK(info);
210   if ([activeView_ isEqual:[info parentView]])
211     [[info sheet] pulseSheet];
214 - (int)sheetCount {
215   return [sheets_ count];
218 - (NSSize)overlayWindowSizeForParentView:(NSView*)parentView {
219   return [self overlayWindowFrameForParentView:parentView].size;
222 - (ConstrainedWindowSheetInfo*)findSheetInfoForParentView:(NSView*)parentView {
223   for (ConstrainedWindowSheetInfo* info in sheets_.get()) {
224     if ([parentView isEqual:[info parentView]])
225       return info;
226   }
227   return NULL;
230 - (ConstrainedWindowSheetInfo*)
231     findSheetInfoForSheet:(id<ConstrainedWindowSheet>)sheet {
232   for (ConstrainedWindowSheetInfo* info in sheets_.get()) {
233     if ([sheet isEqual:[info sheet]])
234       return info;
235   }
236   return NULL;
239 - (void)onParentWindowWillClose:(NSNotification*)note {
240   [[NSNotificationCenter defaultCenter]
241       removeObserver:self
242                 name:NSWindowWillCloseNotification
243               object:parentWindow_];
245   // Close all sheets.
246   NSArray* sheets = [NSArray arrayWithArray:sheets_];
247   for (ConstrainedWindowSheetInfo* info in sheets)
248     [self closeSheet:info withAnimation:NO];
250   dialogHost_.reset();
252   // Delete this instance.
253   [g_sheetControllers removeObjectForKey:GetKeyForParentWindow(parentWindow_)];
254   if (![g_sheetControllers count]) {
255     [g_sheetControllers release];
256     g_sheetControllers = nil;
257   }
260 - (void)onParentWindowSizeDidChange:(NSNotification*)note {
261   [self updateSheetPosition:activeView_];
264 - (void)updateSheetPosition:(NSView*)parentView {
265   ConstrainedWindowSheetInfo* info =
266       [self findSheetInfoForParentView:parentView];
267   if (!info)
268     return;
270   NSRect rect = [self overlayWindowFrameForParentView:parentView];
271   [[info overlayWindow] setFrame:rect display:YES];
272   [[info sheet] updateSheetPosition];
275 - (NSRect)overlayWindowFrameForParentView:(NSView*)parentView {
276   NSRect viewFrame = GetSheetParentBoundsForParentView(parentView);
278   id<NSWindowDelegate> delegate = [[parentView window] delegate];
279   if ([delegate respondsToSelector:@selector(window:
280                                   willPositionSheet:
281                                           usingRect:)]) {
282     NSRect sheetFrame = NSZeroRect;
283     // This API needs Y to be the distance from the bottom of the overlay to
284     // the top of the sheet. X, width, and height are ignored.
285     sheetFrame.origin.y = NSMaxY(viewFrame);
286     NSRect customSheetFrame = [delegate window:[parentView window]
287                              willPositionSheet:nil
288                                      usingRect:sheetFrame];
289     viewFrame.size.height += NSMinY(customSheetFrame) - NSMinY(sheetFrame);
290   }
292   viewFrame.origin = [[parentView window] convertBaseToScreen:viewFrame.origin];
293   return viewFrame;
296 - (NSPoint)originForSheetSize:(NSSize)sheetSize
297               inContainerRect:(NSRect)containerRect {
298   NSPoint origin;
299   origin.x = roundf(NSMinX(containerRect) +
300                     (NSWidth(containerRect) - sheetSize.width) / 2.0);
301   origin.y = NSMaxY(containerRect) + 5 - sheetSize.height;
302   return origin;
305 - (void)onOverlayWindowMouseDown:(CWSheetOverlayWindow*)overlayWindow {
306   for (ConstrainedWindowSheetInfo* curInfo in sheets_.get()) {
307     if ([overlayWindow isEqual:[curInfo overlayWindow]]) {
308       [self pulseSheet:[curInfo sheet]];
309       [[curInfo sheet] makeSheetKeyAndOrderFront];
310       break;
311     }
312   }
315 - (void)closeSheet:(ConstrainedWindowSheetInfo*)info
316      withAnimation:(BOOL)withAnimation {
317   if (![sheets_ containsObject:info])
318     return;
320   [[NSNotificationCenter defaultCenter]
321       removeObserver:self
322                 name:NSWindowDidResizeNotification
323               object:parentWindow_];
325   [parentWindow_ removeChildWindow:[info overlayWindow]];
326   [[info sheet] closeSheetWithAnimation:withAnimation];
327   [[info overlayWindow] close];
328   [sheets_ removeObject:info];
331 @end