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/global_error_bubble_controller.h"
7 #include "base/logging.h"
8 #include "base/mac/scoped_nsobject.h"
9 #include "base/strings/string_util.h"
10 #include "base/strings/sys_string_conversions.h"
11 #include "base/strings/utf_string_conversions.h"
12 #include "chrome/browser/search_engines/util.h"
13 #import "chrome/browser/ui/browser.h"
14 #import "chrome/browser/ui/browser_window.h"
15 #import "chrome/browser/ui/cocoa/browser_window_controller.h"
16 #import "chrome/browser/ui/cocoa/info_bubble_view.h"
17 #import "chrome/browser/ui/cocoa/l10n_util.h"
18 #import "chrome/browser/ui/cocoa/toolbar/toolbar_controller.h"
19 #include "chrome/browser/ui/global_error/global_error.h"
20 #include "chrome/browser/ui/global_error/global_error_bubble_view_base.h"
21 #include "chrome/browser/ui/global_error/global_error_service.h"
22 #include "chrome/browser/ui/global_error/global_error_service_factory.h"
23 #include "grit/generated_resources.h"
24 #import "third_party/GTM/AppKit/GTMUILocalizerAndLayoutTweaker.h"
25 #include "ui/base/l10n/l10n_util.h"
26 #include "ui/gfx/image/image.h"
30 // The vertical offset of the wrench bubble from the wrench menu button.
31 const CGFloat kWrenchBubblePointOffsetY = 6;
33 const CGFloat kParagraphSpacing = 6;
37 namespace GlobalErrorBubbleControllerInternal {
39 // This is the bridge to the C++ GlobalErrorBubbleViewBase object.
40 class Bridge : public GlobalErrorBubbleViewBase {
42 Bridge(GlobalErrorBubbleController* controller) : controller_(controller) {
46 virtual void CloseBubbleView() OVERRIDE {
50 GlobalErrorBubbleController* controller_; // Weak, owns this.
53 } // namespace GlobalErrorBubbleControllerInternal
55 @implementation GlobalErrorBubbleController
57 + (GlobalErrorBubbleViewBase*)showForBrowser:(Browser*)browser
58 error:(const base::WeakPtr<GlobalErrorWithStandardBubble>&)error {
59 NSWindow* parentWindow = browser->window()->GetNativeWindow();
60 BrowserWindowController* bwc = [BrowserWindowController
61 browserWindowControllerForWindow:parentWindow];
62 NSView* wrenchButton = [[bwc toolbarController] wrenchButton];
63 NSPoint offset = NSMakePoint(NSMidX([wrenchButton bounds]),
64 kWrenchBubblePointOffsetY);
66 // The bubble will be automatically deleted when the window is closed.
67 GlobalErrorBubbleController* bubble = [[GlobalErrorBubbleController alloc]
68 initWithWindowNibPath:@"GlobalErrorBubble"
69 relativeToView:wrenchButton
71 bubble->error_ = error;
72 bubble->bridge_.reset(new GlobalErrorBubbleControllerInternal::Bridge(
74 bubble->browser_ = browser;
75 [bubble showWindow:nil];
77 return bubble->bridge_.get();
80 - (void)awakeFromNib {
85 gfx::Image image = error_->GetBubbleViewIcon();
86 DCHECK(!image.IsEmpty());
87 [iconView_ setImage:image.ToNSImage()];
89 [title_ setStringValue:SysUTF16ToNSString(error_->GetBubbleViewTitle())];
90 std::vector<string16> messages = error_->GetBubbleViewMessages();
91 string16 message = JoinString(messages, '\n');
93 base::scoped_nsobject<NSMutableAttributedString> messageValue(
94 [[NSMutableAttributedString alloc]
95 initWithString:SysUTF16ToNSString(message)]);
96 base::scoped_nsobject<NSMutableParagraphStyle> style(
97 [[NSMutableParagraphStyle alloc] init]);
98 [style setParagraphSpacing:kParagraphSpacing];
99 [messageValue addAttribute:NSParagraphStyleAttributeName
101 range:NSMakeRange(0, [messageValue length])];
103 [message_ setAttributedStringValue:messageValue];
105 [acceptButton_ setTitle:
106 SysUTF16ToNSString(error_->GetBubbleViewAcceptButtonLabel())];
107 string16 cancelLabel = error_->GetBubbleViewCancelButtonLabel();
108 if (cancelLabel.empty())
109 [cancelButton_ setHidden:YES];
111 [cancelButton_ setTitle:SysUTF16ToNSString(cancelLabel)];
113 // First make sure that the window is wide enough to accommodate the buttons.
114 NSRect frame = [[self window] frame];
115 [layoutTweaker_ tweakUI:buttonContainer_];
116 CGFloat delta = NSWidth([buttonContainer_ frame]) - NSWidth(frame);
118 frame.size.width += delta;
119 [[self window] setFrame:frame display:NO];
122 // Adapt window height to bottom buttons. Do this before all other layouting.
123 NSArray* views = [NSArray arrayWithObjects:
124 title_, message_, buttonContainer_, nil];
125 NSSize ds = NSMakeSize(0, cocoa_l10n_util::VerticallyReflowGroup(views));
126 ds = [[self bubble] convertSize:ds toView:nil];
128 frame.origin.y -= ds.height;
129 frame.size.height += ds.height;
130 [[self window] setFrame:frame display:YES];
133 - (void)showWindow:(id)sender {
134 BrowserWindowController* bwc = [BrowserWindowController
135 browserWindowControllerForWindow:[self parentWindow]];
136 [bwc lockBarVisibilityForOwner:self withAnimation:NO delay:NO];
137 [super showWindow:sender];
142 error_->BubbleViewDidClose(browser_);
144 BrowserWindowController* bwc = [BrowserWindowController
145 browserWindowControllerForWindow:[self parentWindow]];
146 [bwc releaseBarVisibilityForOwner:self withAnimation:YES delay:NO];
150 - (IBAction)onAccept:(id)sender {
152 error_->BubbleViewAcceptButtonPressed(browser_);
156 - (IBAction)onCancel:(id)sender {
158 error_->BubbleViewCancelButtonPressed(browser_);
164 GlobalErrorBubbleViewBase* GlobalErrorBubbleViewBase::ShowStandardBubbleView(
166 const base::WeakPtr<GlobalErrorWithStandardBubble>& error) {
167 return [GlobalErrorBubbleController showForBrowser:browser error:error];