Home | History | Annotate | Download | only in extensions
      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 
     21 namespace {
     22 
     23 // Maximum height we will adjust controls to when trying to accomodate their
     24 // contents.
     25 const 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.
     29 CGFloat 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.
     42 void 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 
    195 void 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