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