1 // Copyright (c) 2011 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/infobars/infobar_container_controller.h"
7 #include "base/logging.h"
8 #include "base/mac/mac_util.h"
9 #include "chrome/browser/infobars/confirm_infobar_delegate.h"
10 #include "chrome/browser/infobars/infobar.h"
11 #include "chrome/browser/infobars/infobar_container.h"
12 #include "chrome/browser/infobars/infobar_service.h"
13 #import "chrome/browser/ui/cocoa/browser_window_controller.h"
14 #import "chrome/browser/ui/cocoa/infobars/infobar_cocoa.h"
15 #import "chrome/browser/ui/cocoa/infobars/infobar_container_cocoa.h"
16 #import "chrome/browser/ui/cocoa/infobars/infobar_controller.h"
17 #import "chrome/browser/ui/cocoa/location_bar/location_bar_view_mac.h"
18 #import "chrome/browser/ui/cocoa/view_id_util.h"
20 @interface InfoBarContainerController ()
21 // Removes |controller| from the list of controllers in this container and
22 // removes its view from the view hierarchy. This method is safe to call while
23 // |controller| is still on the call stack.
24 - (void)removeController:(InfoBarController*)controller;
28 @implementation InfoBarContainerController
30 @synthesize shouldSuppressTopInfoBarTip = shouldSuppressTopInfoBarTip_;
32 - (id)initWithResizeDelegate:(id<ViewResizer>)resizeDelegate {
33 DCHECK(resizeDelegate);
34 if ((self = [super initWithNibName:nil bundle:nil])) {
35 base::scoped_nsobject<NSView> view(
36 [[NSView alloc] initWithFrame:NSZeroRect]);
37 [view setAutoresizingMask:NSViewWidthSizable | NSViewMinYMargin];
38 view_id_util::SetID(view, VIEW_ID_INFO_BAR_CONTAINER);
41 resizeDelegate_ = resizeDelegate;
42 containerCocoa_.reset(new InfoBarContainerCocoa(self));
43 infobarControllers_.reset([[NSMutableArray alloc] init]);
49 // Delete the container so that any remaining infobars are removed.
50 containerCocoa_.reset();
51 DCHECK_EQ([infobarControllers_ count], 0U);
52 view_id_util::UnsetID([self view]);
56 - (BrowserWindowController*)browserWindowController {
57 id controller = [[[self view] window] windowController];
58 if (![controller isKindOfClass:[BrowserWindowController class]])
63 - (CGFloat)infobarArrowX {
64 LocationBarViewMac* locationBar =
65 [[self browserWindowController] locationBarBridge];
66 return locationBar->GetPageInfoBubblePoint().x;
69 - (void)changeWebContents:(content::WebContents*)contents {
70 currentWebContents_ = contents;
72 containerCocoa_->ChangeInfoBarService(
73 InfoBarService::FromWebContents(contents));
75 containerCocoa_->ChangeInfoBarService(NULL);
79 - (void)tabDetachedWithContents:(content::WebContents*)contents {
80 if (currentWebContents_ == contents)
81 [self changeWebContents:NULL];
84 - (CGFloat)overlappingTipHeight {
85 return containerCocoa_->GetVerticalOverlap(NULL);
88 - (void)addInfoBar:(InfoBarCocoa*)infobar
89 position:(NSUInteger)position {
90 InfoBarController* controller = infobar->controller();
91 [controller setContainerController:self];
92 [infobarControllers_ insertObject:controller atIndex:position];
94 NSView* relativeView = nil;
96 relativeView = [[infobarControllers_ objectAtIndex:position - 1] view];
97 [[self view] addSubview:[controller view]
98 positioned:NSWindowAbove
99 relativeTo:relativeView];
102 - (void)removeInfoBar:(InfoBarCocoa*)infobar {
103 [infobar->controller() infobarWillHide];
104 [self removeController:infobar->controller()];
107 - (void)positionInfoBarsAndRedraw:(BOOL)isAnimating {
108 if (isAnimating_ != isAnimating) {
109 isAnimating_ = isAnimating;
110 if ([resizeDelegate_ respondsToSelector:@selector(setAnimationInProgress:)])
111 [resizeDelegate_ setAnimationInProgress:isAnimating_];
114 NSRect containerBounds = [[self view] bounds];
117 // Stack the infobars at the bottom of the view, starting with the
118 // last infobar and working our way to the front of the array. This
119 // way we ensure that the first infobar added shows up on top, with
121 for (InfoBarController* controller in
122 [infobarControllers_ reverseObjectEnumerator]) {
124 frame.origin.x = NSMinX(containerBounds);
125 frame.origin.y = minY;
126 frame.size.width = NSWidth(containerBounds);
127 frame.size.height = [controller infobar]->total_height();
128 [[controller view] setFrame:frame];
130 minY += NSHeight(frame) - [controller infobar]->arrow_height();
131 [controller layoutArrow];
135 int overlap = containerCocoa_->GetVerticalOverlap(&totalHeight);
137 if (NSHeight([[self view] frame]) != totalHeight) {
138 [resizeDelegate_ resizeView:[self view] newHeight:totalHeight];
139 } else if (oldOverlappingTipHeight_ != overlap) {
140 // If the infobar overlap changed but the height didn't change then
141 // explicitly ask for a layout.
142 [[self browserWindowController] layoutInfoBars];
144 oldOverlappingTipHeight_ = overlap;
147 - (void)setShouldSuppressTopInfoBarTip:(BOOL)flag {
148 if (shouldSuppressTopInfoBarTip_ == flag)
150 shouldSuppressTopInfoBarTip_ = flag;
151 [self positionInfoBarsAndRedraw:isAnimating_];
154 - (void)removeController:(InfoBarController*)controller {
155 if (![infobarControllers_ containsObject:controller])
158 // This code can be executed while InfoBarController is still on the stack, so
159 // we retain and autorelease the controller to prevent it from being
160 // dealloc'ed too early.
161 [[controller retain] autorelease];
162 [[controller view] removeFromSuperview];
163 [infobarControllers_ removeObject:controller];