<commit_before>// 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/strings/sys_string_conversions.h"
#include "base/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/l10n_util.h"
#import "chrome/browser/ui/cocoa/info_bubble_view.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/base/resource/resource_bundle.h"
#include "ui/gfx/image/image.h"

namespace {

// The vertical offset of the wrench bubble from the wrench menu button.
const CGFloat kWrenchBubblePointOffsetY = 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_);

  ResourceBundle& rb = ResourceBundle::GetSharedInstance();
  [iconView_ setImage:rb.GetNativeImageNamed(
      error_->GetBubbleViewIconResourceID()).ToNSImage()];

  [title_ setStringValue:SysUTF16ToNSString(error_->GetBubbleViewTitle())];
  // TODO(yoz): Support multi-line messages.
  string16 message;
  if (!error_->GetBubbleViewMessages().empty())
    message = error_->GetBubbleViewMessages()[0];
  [message_ setStringValue:SysUTF16ToNSString(message)];
  [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 accomidate 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<commit_msg>Mac: support multi-line global error bubble messages.<commit_after>// 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/memory/scoped_nsobject.h"
#include "base/string_util.h"
#include "base/strings/sys_string_conversions.h"
#include "base/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/l10n_util.h"
#import "chrome/browser/ui/cocoa/info_bubble_view.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/base/resource/resource_bundle.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_);

  ResourceBundle& rb = ResourceBundle::GetSharedInstance();
  [iconView_ setImage:rb.GetNativeImageNamed(
      error_->GetBubbleViewIconResourceID()).ToNSImage()];

  [title_ setStringValue:SysUTF16ToNSString(error_->GetBubbleViewTitle())];
  std::vector<string16> messages = error_->GetBubbleViewMessages();
  string16 message = JoinString(messages, '\n');

  scoped_nsobject<NSMutableAttributedString> messageValue(
      [[NSMutableAttributedString alloc]
        initWithString:SysUTF16ToNSString(message)]);
  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
{'fraction_non_alphanumeric': 0.10918357345878663, 'fraction_numerical': 0.005782612956973439, 'mean_word_length': 5.180496668685645, 'pattern_counts': {'":': 0, '<': 8, '<?xml version=': 0, '>': 35, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3629707', 'n_tokens_mistral': 3199, 'n_tokens_neox': 3067, 'n_words': 626}