blob: f6110909f574fada879408d05fa3e1e55e9c06d5 [file] [log] [blame]
// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/logging.h"
#include "base/mac/mac_util.h"
#include "base/message_loop/message_loop.h"
#include "chrome/browser/infobars/confirm_infobar_delegate.h"
#include "chrome/browser/infobars/infobar.h"
#include "chrome/browser/infobars/infobar_container.h"
#include "chrome/browser/infobars/infobar_service.h"
#import "chrome/browser/ui/cocoa/browser_window_controller.h"
#import "chrome/browser/ui/cocoa/infobars/infobar_cocoa.h"
#import "chrome/browser/ui/cocoa/infobars/infobar_container_cocoa.h"
#import "chrome/browser/ui/cocoa/infobars/infobar_container_controller.h"
#import "chrome/browser/ui/cocoa/infobars/infobar_controller.h"
#import "chrome/browser/ui/cocoa/location_bar/location_bar_view_mac.h"
#import "chrome/browser/ui/cocoa/view_id_util.h"
@interface InfoBarContainerController ()
// Removes |controller| from the list of controllers in this container and
// removes its view from the view hierarchy. This method is safe to call while
// |controller| is still on the call stack.
- (void)removeController:(InfoBarController*)controller;
@end
@implementation InfoBarContainerController
@synthesize shouldSuppressTopInfoBarTip = shouldSuppressTopInfoBarTip_;
- (id)initWithResizeDelegate:(id<ViewResizer>)resizeDelegate {
DCHECK(resizeDelegate);
if ((self = [super initWithNibName:nil bundle:nil])) {
base::scoped_nsobject<NSView> view(
[[NSView alloc] initWithFrame:NSZeroRect]);
[view setAutoresizingMask:NSViewWidthSizable | NSViewMinYMargin];
view_id_util::SetID(view, VIEW_ID_INFO_BAR_CONTAINER);
[self setView:view];
resizeDelegate_ = resizeDelegate;
containerCocoa_.reset(new InfoBarContainerCocoa(self));
infobarControllers_.reset([[NSMutableArray alloc] init]);
}
return self;
}
- (void)dealloc {
// Delete the container so that any remaining infobars are removed.
containerCocoa_.reset();
DCHECK_EQ([infobarControllers_ count], 0U);
view_id_util::UnsetID([self view]);
[super dealloc];
}
- (BrowserWindowController*)browserWindowController {
id controller = [[[self view] window] windowController];
if (![controller isKindOfClass:[BrowserWindowController class]])
return nil;
return controller;
}
- (CGFloat)infobarArrowX {
LocationBarViewMac* locationBar =
[[self browserWindowController] locationBarBridge];
return locationBar->GetPageInfoBubblePoint().x;
}
- (void)changeWebContents:(content::WebContents*)contents {
if (contents) {
containerCocoa_->ChangeInfoBarService(
InfoBarService::FromWebContents(contents));
} else {
containerCocoa_->ChangeInfoBarService(NULL);
}
}
- (void)tabDetachedWithContents:(content::WebContents*)contents {
if (currentWebContents_ == contents)
[self changeWebContents:NULL];
}
- (CGFloat)overlappingTipHeight {
return containerCocoa_->GetVerticalOverlap(NULL);
}
- (void)addInfoBar:(InfoBarCocoa*)infobar
position:(NSUInteger)position {
InfoBarController* controller = infobar->controller();
[controller setContainerController:self];
[infobarControllers_ insertObject:controller atIndex:position];
NSView* relativeView = nil;
if (position > 0)
relativeView = [[infobarControllers_ objectAtIndex:position - 1] view];
[[self view] addSubview:[controller view]
positioned:NSWindowAbove
relativeTo:relativeView];
}
- (void)removeInfoBar:(InfoBarCocoa*)infobar {
[infobar->controller() infobarWillClose];
[self removeController:infobar->controller()];
base::MessageLoop::current()->DeleteSoon(FROM_HERE, infobar);
}
- (void)positionInfoBarsAndRedraw:(BOOL)isAnimating {
if (isAnimating_ != isAnimating) {
isAnimating_ = isAnimating;
if ([resizeDelegate_ respondsToSelector:@selector(setAnimationInProgress:)])
[resizeDelegate_ setAnimationInProgress:isAnimating_];
}
NSRect containerBounds = [[self view] bounds];
int minY = 0;
// Stack the infobars at the bottom of the view, starting with the
// last infobar and working our way to the front of the array. This
// way we ensure that the first infobar added shows up on top, with
// the others below.
for (InfoBarController* controller in
[infobarControllers_ reverseObjectEnumerator]) {
NSRect frame;
frame.origin.x = NSMinX(containerBounds);
frame.origin.y = minY;
frame.size.width = NSWidth(containerBounds);
frame.size.height = [controller infobar]->total_height();
[[controller view] setFrame:frame];
minY += NSHeight(frame) - [controller infobar]->arrow_height();
[controller layoutArrow];
}
int totalHeight = 0;
int overlap = containerCocoa_->GetVerticalOverlap(&totalHeight);
if (NSHeight([[self view] frame]) != totalHeight) {
[resizeDelegate_ resizeView:[self view] newHeight:totalHeight];
} else if (oldOverlappingTipHeight_ != overlap) {
// If the infobar overlap changed but the height didn't change then
// explicitly ask for a layout.
[[self browserWindowController] layoutInfoBars];
}
oldOverlappingTipHeight_ = overlap;
}
- (void)setShouldSuppressTopInfoBarTip:(BOOL)flag {
if (shouldSuppressTopInfoBarTip_ == flag)
return;
shouldSuppressTopInfoBarTip_ = flag;
[self positionInfoBarsAndRedraw:isAnimating_];
}
- (void)removeController:(InfoBarController*)controller {
if (![infobarControllers_ containsObject:controller])
return;
// This code can be executed while InfoBarController is still on the stack, so
// we retain and autorelease the controller to prevent it from being
// dealloc'ed too early.
[[controller retain] autorelease];
[[controller view] removeFromSuperview];
[infobarControllers_ removeObject:controller];
}
@end