blob: 4ccb41abfbf79c51f9ed1900426ffecd80bab885 [file] [log] [blame]
// Copyright (c) 2012 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.
#import "chrome/browser/ui/cocoa/global_error_bubble_controller.h"
#include "base/logging.h"
#include "base/mac/scoped_nsobject.h"
#include "base/strings/string_util.h"
#include "base/strings/sys_string_conversions.h"
#include "base/strings/utf_string_conversions.h"
#include "chrome/browser/search_engines/util.h"
#import "chrome/browser/ui/browser.h"
#import "chrome/browser/ui/browser_window.h"
#import "chrome/browser/ui/cocoa/browser_window_controller.h"
#import "chrome/browser/ui/cocoa/info_bubble_view.h"
#import "chrome/browser/ui/cocoa/l10n_util.h"
#import "chrome/browser/ui/cocoa/toolbar/toolbar_controller.h"
#include "chrome/browser/ui/global_error/global_error.h"
#include "chrome/browser/ui/global_error/global_error_bubble_view_base.h"
#include "chrome/browser/ui/global_error/global_error_service.h"
#include "chrome/browser/ui/global_error/global_error_service_factory.h"
#include "grit/generated_resources.h"
#import "third_party/GTM/AppKit/GTMUILocalizerAndLayoutTweaker.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/gfx/image/image.h"
namespace {
// The vertical offset of the wrench bubble from the wrench menu button.
const CGFloat kWrenchBubblePointOffsetY = 6;
const CGFloat kParagraphSpacing = 6;
} // namespace
namespace GlobalErrorBubbleControllerInternal {
// This is the bridge to the C++ GlobalErrorBubbleViewBase object.
class Bridge : public GlobalErrorBubbleViewBase {
public:
Bridge(GlobalErrorBubbleController* controller) : controller_(controller) {
}
private:
virtual void CloseBubbleView() OVERRIDE {
[controller_ close];
}
GlobalErrorBubbleController* controller_; // Weak, owns this.
};
} // namespace GlobalErrorBubbleControllerInternal
@implementation GlobalErrorBubbleController
+ (GlobalErrorBubbleViewBase*)showForBrowser:(Browser*)browser
error:(const base::WeakPtr<GlobalError>&)error {
NSWindow* parentWindow = browser->window()->GetNativeWindow();
BrowserWindowController* bwc = [BrowserWindowController
browserWindowControllerForWindow:parentWindow];
NSView* wrenchButton = [[bwc toolbarController] wrenchButton];
NSPoint offset = NSMakePoint(NSMidX([wrenchButton bounds]),
kWrenchBubblePointOffsetY);
// The bubble will be automatically deleted when the window is closed.
GlobalErrorBubbleController* bubble = [[GlobalErrorBubbleController alloc]
initWithWindowNibPath:@"GlobalErrorBubble"
relativeToView:wrenchButton
offset:offset];
bubble->error_ = error;
bubble->bridge_.reset(new GlobalErrorBubbleControllerInternal::Bridge(
bubble));
bubble->browser_ = browser;
[bubble showWindow:nil];
return bubble->bridge_.get();
}
- (void)awakeFromNib {
[super awakeFromNib];
DCHECK(error_);
gfx::Image image = error_->GetBubbleViewIcon();
DCHECK(!image.IsEmpty());
[iconView_ setImage:image.ToNSImage()];
[title_ setStringValue:SysUTF16ToNSString(error_->GetBubbleViewTitle())];
std::vector<string16> messages = error_->GetBubbleViewMessages();
string16 message = JoinString(messages, '\n');
base::scoped_nsobject<NSMutableAttributedString> messageValue(
[[NSMutableAttributedString alloc]
initWithString:SysUTF16ToNSString(message)]);
base::scoped_nsobject<NSMutableParagraphStyle> style(
[[NSMutableParagraphStyle alloc] init]);
[style setParagraphSpacing:kParagraphSpacing];
[messageValue addAttribute:NSParagraphStyleAttributeName
value:style
range:NSMakeRange(0, [messageValue length])];
[message_ setAttributedStringValue:messageValue];
[acceptButton_ setTitle:
SysUTF16ToNSString(error_->GetBubbleViewAcceptButtonLabel())];
string16 cancelLabel = error_->GetBubbleViewCancelButtonLabel();
if (cancelLabel.empty())
[cancelButton_ setHidden:YES];
else
[cancelButton_ setTitle:SysUTF16ToNSString(cancelLabel)];
// First make sure that the window is wide enough to accommodate the buttons.
NSRect frame = [[self window] frame];
[layoutTweaker_ tweakUI:buttonContainer_];
CGFloat delta = NSWidth([buttonContainer_ frame]) - NSWidth(frame);
if (delta > 0) {
frame.size.width += delta;
[[self window] setFrame:frame display:NO];
}
// Adapt window height to bottom buttons. Do this before all other layouting.
NSArray* views = [NSArray arrayWithObjects:
title_, message_, buttonContainer_, nil];
NSSize ds = NSMakeSize(0, cocoa_l10n_util::VerticallyReflowGroup(views));
ds = [[self bubble] convertSize:ds toView:nil];
frame.origin.y -= ds.height;
frame.size.height += ds.height;
[[self window] setFrame:frame display:YES];
}
- (void)showWindow:(id)sender {
BrowserWindowController* bwc = [BrowserWindowController
browserWindowControllerForWindow:[self parentWindow]];
[bwc lockBarVisibilityForOwner:self withAnimation:NO delay:NO];
[super showWindow:sender];
}
- (void)close {
if (error_)
error_->BubbleViewDidClose(browser_);
bridge_.reset();
BrowserWindowController* bwc = [BrowserWindowController
browserWindowControllerForWindow:[self parentWindow]];
[bwc releaseBarVisibilityForOwner:self withAnimation:YES delay:NO];
[super close];
}
- (IBAction)onAccept:(id)sender {
if (error_)
error_->BubbleViewAcceptButtonPressed(browser_);
[self close];
}
- (IBAction)onCancel:(id)sender {
if (error_)
error_->BubbleViewCancelButtonPressed(browser_);
[self close];
}
@end
GlobalErrorBubbleViewBase* GlobalErrorBubbleViewBase::ShowBubbleView(
Browser* browser,
const base::WeakPtr<GlobalError>& error) {
return [GlobalErrorBubbleController showForBrowser:browser error:error];
}