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 #import "chrome/browser/ui/browser.h"
13 #import "chrome/browser/ui/browser_window.h"
14 #import "chrome/browser/ui/cocoa/browser_window_controller.h"
15 #import "chrome/browser/ui/cocoa/info_bubble_view.h"
16 #import "chrome/browser/ui/cocoa/l10n_util.h"
17 #import "chrome/browser/ui/cocoa/toolbar/toolbar_controller.h"
18 #import "chrome/browser/ui/cocoa/wrench_menu/wrench_menu_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 "components/search_engines/util.h"
24 #import "third_party/google_toolbox_for_mac/src/AppKit/GTMUILocalizerAndLayoutTweaker.h"
25 #include "ui/base/l10n/l10n_util.h"
26 #include "ui/gfx/image/image.h"
29 const CGFloat kParagraphSpacing = 6;
32 namespace GlobalErrorBubbleControllerInternal {
34 // This is the bridge to the C++ GlobalErrorBubbleViewBase object.
35 class Bridge : public GlobalErrorBubbleViewBase {
37 Bridge(GlobalErrorBubbleController* controller) : controller_(controller) {
41 void CloseBubbleView() override { [controller_ close]; }
43 GlobalErrorBubbleController* controller_; // Weak, owns this.
46 } // namespace GlobalErrorBubbleControllerInternal
48 @implementation GlobalErrorBubbleController
50 + (GlobalErrorBubbleViewBase*)showForBrowser:(Browser*)browser
51 error:(const base::WeakPtr<GlobalErrorWithStandardBubble>&)error {
52 NSWindow* parentWindow = browser->window()->GetNativeWindow();
53 BrowserWindowController* bwc = [BrowserWindowController
54 browserWindowControllerForWindow:parentWindow];
55 NSView* wrenchButton = [[bwc toolbarController] wrenchButton];
56 NSPoint offset = NSMakePoint(
57 NSMidX([wrenchButton bounds]),
58 wrench_menu_controller::kWrenchBubblePointOffsetY);
60 // The bubble will be automatically deleted when the window is closed.
61 GlobalErrorBubbleController* bubble = [[GlobalErrorBubbleController alloc]
62 initWithWindowNibPath:@"GlobalErrorBubble"
63 relativeToView:wrenchButton
65 bubble->error_ = error;
66 bubble->bridge_.reset(new GlobalErrorBubbleControllerInternal::Bridge(
68 bubble->browser_ = browser;
69 [bubble showWindow:nil];
71 return bubble->bridge_.get();
74 - (void)awakeFromNib {
79 gfx::Image image = error_->GetBubbleViewIcon();
80 DCHECK(!image.IsEmpty());
81 [iconView_ setImage:image.ToNSImage()];
83 [title_ setStringValue:SysUTF16ToNSString(error_->GetBubbleViewTitle())];
84 std::vector<base::string16> messages = error_->GetBubbleViewMessages();
85 base::string16 message = JoinString(messages, '\n');
87 base::scoped_nsobject<NSMutableAttributedString> messageValue(
88 [[NSMutableAttributedString alloc]
89 initWithString:SysUTF16ToNSString(message)]);
90 base::scoped_nsobject<NSMutableParagraphStyle> style(
91 [[NSMutableParagraphStyle alloc] init]);
92 [style setParagraphSpacing:kParagraphSpacing];
93 [messageValue addAttribute:NSParagraphStyleAttributeName
95 range:NSMakeRange(0, [messageValue length])];
97 [message_ setAttributedStringValue:messageValue];
99 [acceptButton_ setTitle:
100 SysUTF16ToNSString(error_->GetBubbleViewAcceptButtonLabel())];
101 base::string16 cancelLabel = error_->GetBubbleViewCancelButtonLabel();
102 if (cancelLabel.empty())
103 [cancelButton_ setHidden:YES];
105 [cancelButton_ setTitle:SysUTF16ToNSString(cancelLabel)];
107 // First make sure that the window is wide enough to accommodate the buttons.
108 NSRect frame = [[self window] frame];
109 [layoutTweaker_ tweakUI:buttonContainer_];
110 CGFloat delta = NSWidth([buttonContainer_ frame]) - NSWidth(frame);
112 frame.size.width += delta;
113 [[self window] setFrame:frame display:NO];
116 // Adapt window height to bottom buttons. Do this before all other layouting.
117 NSArray* views = [NSArray arrayWithObjects:
118 title_, message_, buttonContainer_, nil];
119 NSSize ds = NSMakeSize(0, cocoa_l10n_util::VerticallyReflowGroup(views));
120 ds = [[self bubble] convertSize:ds toView:nil];
122 frame.origin.y -= ds.height;
123 frame.size.height += ds.height;
124 [[self window] setFrame:frame display:YES];
127 - (void)showWindow:(id)sender {
128 BrowserWindowController* bwc = [BrowserWindowController
129 browserWindowControllerForWindow:[self parentWindow]];
130 [bwc lockBarVisibilityForOwner:self withAnimation:NO delay:NO];
131 [super showWindow:sender];
136 error_->BubbleViewDidClose(browser_);
138 BrowserWindowController* bwc = [BrowserWindowController
139 browserWindowControllerForWindow:[self parentWindow]];
140 [bwc releaseBarVisibilityForOwner:self withAnimation:YES delay:NO];
144 - (IBAction)onAccept:(id)sender {
146 error_->BubbleViewAcceptButtonPressed(browser_);
150 - (IBAction)onCancel:(id)sender {
152 error_->BubbleViewCancelButtonPressed(browser_);
158 GlobalErrorBubbleViewBase* GlobalErrorBubbleViewBase::ShowStandardBubbleView(
160 const base::WeakPtr<GlobalErrorWithStandardBubble>& error) {
161 return [GlobalErrorBubbleController showForBrowser:browser error:error];