• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright (c) 2011 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.
4
5#import "chrome/browser/ui/cocoa/extensions/extension_install_dialog_controller.h"
6
7#include "base/mac/mac_util.h"
8#include "base/string_util.h"
9#include "base/sys_string_conversions.h"
10#include "base/utf_string_conversions.h"
11#include "chrome/browser/extensions/extension_install_dialog.h"
12#include "chrome/browser/ui/browser.h"
13#include "chrome/browser/ui/browser_list.h"
14#include "chrome/browser/ui/browser_window.h"
15#include "chrome/common/extensions/extension.h"
16#include "grit/generated_resources.h"
17#include "skia/ext/skia_utils_mac.h"
18#include "ui/base/l10n/l10n_util.h"
19#include "ui/base/l10n/l10n_util_mac.h"
20
21namespace {
22
23// Maximum height we will adjust controls to when trying to accomodate their
24// contents.
25const CGFloat kMaxControlHeight = 400;
26
27// Adjust a control's height so that its content its not clipped. Returns the
28// amount the control's height had to be adjusted.
29CGFloat AdjustControlHeightToFitContent(NSControl* control) {
30  NSRect currentRect = [control frame];
31  NSRect fitRect = currentRect;
32  fitRect.size.height = kMaxControlHeight;
33  CGFloat desiredHeight = [[control cell] cellSizeForBounds:fitRect].height;
34  CGFloat offset = desiredHeight - currentRect.size.height;
35
36  [control setFrameSize:NSMakeSize(currentRect.size.width,
37                                   currentRect.size.height + offset)];
38  return offset;
39}
40
41// Moves the control vertically by the specified amount.
42void OffsetControlVertically(NSControl* control, CGFloat amount) {
43  NSPoint origin = [control frame].origin;
44  origin.y += amount;
45  [control setFrameOrigin:origin];
46}
47
48}
49
50@implementation ExtensionInstallDialogController
51
52@synthesize iconView = iconView_;
53@synthesize titleField = titleField_;
54@synthesize subtitleField = subtitleField_;
55@synthesize warningsField = warningsField_;
56@synthesize warningsBox= warningsBox_;
57@synthesize cancelButton = cancelButton_;
58@synthesize okButton = okButton_;
59
60- (id)initWithParentWindow:(NSWindow*)window
61                   profile:(Profile*)profile
62                 extension:(const Extension*)extension
63                  delegate:(ExtensionInstallUI::Delegate*)delegate
64                      icon:(SkBitmap*)icon
65                  warnings:(const std::vector<string16>&)warnings
66                      type:(ExtensionInstallUI::PromptType)type {
67  NSString* nibpath = nil;
68
69  // We use a different XIB in the case of no warnings, that is a little bit
70  // more nicely laid out.
71  if (warnings.empty()) {
72    nibpath = [base::mac::MainAppBundle()
73               pathForResource:@"ExtensionInstallPromptNoWarnings"
74                        ofType:@"nib"];
75  } else {
76   nibpath = [base::mac::MainAppBundle()
77              pathForResource:@"ExtensionInstallPrompt"
78                       ofType:@"nib"];
79  }
80
81  if ((self = [super initWithWindowNibPath:nibpath owner:self])) {
82    parentWindow_ = window;
83    profile_ = profile;
84    icon_ = *icon;
85    delegate_ = delegate;
86
87    title_.reset(
88        [l10n_util::GetNSStringF(ExtensionInstallUI::kHeadingIds[type],
89                                 UTF8ToUTF16(extension->name())) retain]);
90    subtitle_.reset(
91         [l10n_util::GetNSString(ExtensionInstallUI::kWarningIds[type])
92          retain]);
93    button_.reset([l10n_util::GetNSString(ExtensionInstallUI::kButtonIds[type])
94                   retain]);
95
96    // We display the warnings as a simple text string, separated by newlines.
97    if (!warnings.empty()) {
98      string16 joined_warnings;
99      for (size_t i = 0; i < warnings.size(); ++i) {
100        if (i > 0)
101          joined_warnings += UTF8ToUTF16("\n\n");
102
103        joined_warnings += warnings[i];
104      }
105
106      warnings_.reset(
107          [base::SysUTF16ToNSString(joined_warnings) retain]);
108    }
109  }
110  return self;
111}
112
113- (void)runAsModalSheet {
114  [NSApp beginSheet:[self window]
115     modalForWindow:parentWindow_
116      modalDelegate:self
117     didEndSelector:@selector(didEndSheet:returnCode:contextInfo:)
118        contextInfo:nil];
119}
120
121- (IBAction)cancel:(id)sender {
122  delegate_->InstallUIAbort();
123  [NSApp endSheet:[self window]];
124}
125
126- (IBAction)ok:(id)sender {
127  delegate_->InstallUIProceed();
128  [NSApp endSheet:[self window]];
129}
130
131- (void)awakeFromNib {
132  [titleField_ setStringValue:title_.get()];
133  [subtitleField_ setStringValue:subtitle_.get()];
134  [okButton_ setTitle:button_.get()];
135
136  NSImage* image = gfx::SkBitmapToNSImage(icon_);
137  [iconView_ setImage:image];
138
139  // Make sure we're the window's delegate as set in the nib.
140  DCHECK_EQ(self, static_cast<ExtensionInstallDialogController*>(
141                      [[self window] delegate]));
142
143  // If there are any warnings, then we have to do some special layout.
144  if ([warnings_.get() length] > 0) {
145    [warningsField_ setStringValue:warnings_.get()];
146
147    // The dialog is laid out in the NIB exactly how we want it assuming that
148    // each label fits on one line. However, for each label, we want to allow
149    // wrapping onto multiple lines. So we accumulate an offset by measuring how
150    // big each label wants to be, and comparing it to how bit it actually is.
151    // Then we shift each label down and resize by the appropriate amount, then
152    // finally resize the window.
153    CGFloat totalOffset = 0.0;
154
155    // Text fields.
156    totalOffset += AdjustControlHeightToFitContent(titleField_);
157    OffsetControlVertically(titleField_, -totalOffset);
158
159    totalOffset += AdjustControlHeightToFitContent(subtitleField_);
160    OffsetControlVertically(subtitleField_, -totalOffset);
161
162    CGFloat warningsOffset = AdjustControlHeightToFitContent(warningsField_);
163    OffsetControlVertically(warningsField_, -warningsOffset);
164    totalOffset += warningsOffset;
165
166    NSRect warningsBoxRect = [warningsBox_ frame];
167    warningsBoxRect.origin.y -= totalOffset;
168    warningsBoxRect.size.height += warningsOffset;
169    [warningsBox_ setFrame:warningsBoxRect];
170
171    // buttons are positioned automatically in the XIB.
172
173    // Finally, adjust the window size.
174    NSRect currentRect = [[self window] frame];
175    [[self window] setFrame:NSMakeRect(currentRect.origin.x,
176                                       currentRect.origin.y - totalOffset,
177                                       currentRect.size.width,
178                                       currentRect.size.height + totalOffset)
179                    display:NO];
180  }
181}
182
183- (void)didEndSheet:(NSWindow*)sheet
184         returnCode:(int)returnCode
185        contextInfo:(void*)contextInfo {
186  [sheet close];
187}
188
189- (void)windowWillClose:(NSNotification*)notification {
190  [self autorelease];
191}
192
193@end  // ExtensionInstallDialogController
194
195void ShowExtensionInstallDialog(
196    Profile* profile,
197    ExtensionInstallUI::Delegate* delegate,
198    const Extension* extension,
199    SkBitmap* icon,
200    const std::vector<string16>& warnings,
201    ExtensionInstallUI::PromptType type) {
202  Browser* browser = BrowserList::GetLastActiveWithProfile(profile);
203  if (!browser) {
204    delegate->InstallUIAbort();
205    return;
206  }
207
208  BrowserWindow* window = browser->window();
209  if (!window) {
210    delegate->InstallUIAbort();
211    return;
212  }
213
214  gfx::NativeWindow native_window = window->GetNativeHandle();
215
216  ExtensionInstallDialogController* controller =
217      [[ExtensionInstallDialogController alloc]
218        initWithParentWindow:native_window
219                     profile:profile
220                   extension:extension
221                    delegate:delegate
222                        icon:icon
223                    warnings:warnings
224                        type:type];
225
226  [controller runAsModalSheet];
227}
228