blob: dbd42e012a227be0c31733227ef290a09fe9c1fa [file] [log] [blame]
// Copyright (c) 2009 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/cocoa/download_item_controller.h"
#include "app/gfx/text_elider.h"
#include "app/l10n_util_mac.h"
#include "base/mac_util.h"
#include "base/sys_string_conversions.h"
#import "chrome/browser/cocoa/download_item_cell.h"
#include "chrome/browser/cocoa/download_item_mac.h"
#import "chrome/browser/cocoa/download_shelf_controller.h"
#include "chrome/browser/download/download_item_model.h"
#include "chrome/browser/download/download_shelf.h"
#include "chrome/browser/download/download_util.h"
#include "grit/generated_resources.h"
#include "third_party/GTM/AppKit/GTMUILocalizerAndLayoutTweaker.h"
static const int kTextWidth = 140; // Pixels
namespace {
// Helper to widen a view.
void WidenView(NSView* view, CGFloat widthChange) {
// If it is an NSBox, the autoresize of the contentView is the issue.
NSView* contentView = view;
if ([view isKindOfClass:[NSBox class]]) {
contentView = [(NSBox*)view contentView];
}
BOOL autoresizesSubviews = [contentView autoresizesSubviews];
if (autoresizesSubviews) {
[contentView setAutoresizesSubviews:NO];
}
NSRect frame = [view frame];
frame.size.width += widthChange;
[view setFrame:frame];
if (autoresizesSubviews) {
[contentView setAutoresizesSubviews:YES];
}
}
} // namespace
// A class for the chromium-side part of the download shelf context menu.
class DownloadShelfContextMenuMac : public DownloadShelfContextMenu {
public:
DownloadShelfContextMenuMac(BaseDownloadItemModel* model)
: DownloadShelfContextMenu(model) { }
using DownloadShelfContextMenu::ExecuteItemCommand;
using DownloadShelfContextMenu::ItemIsChecked;
using DownloadShelfContextMenu::IsItemCommandEnabled;
using DownloadShelfContextMenu::SHOW_IN_FOLDER;
using DownloadShelfContextMenu::OPEN_WHEN_COMPLETE;
using DownloadShelfContextMenu::ALWAYS_OPEN_TYPE;
using DownloadShelfContextMenu::CANCEL;
};
@interface DownloadItemController (Private)
- (void)setState:(DownoadItemState)state;
@end
// Implementation of DownloadItemController
@implementation DownloadItemController
- (id)initWithModel:(BaseDownloadItemModel*)downloadModel
shelf:(DownloadShelfController*)shelf {
if ((self = [super initWithNibName:@"DownloadItem"
bundle:mac_util::MainAppBundle()])) {
// Must be called before [self view], so that bridge_ is set in awakeFromNib
bridge_.reset(new DownloadItemMac(downloadModel, self));
menuBridge_.reset(new DownloadShelfContextMenuMac(downloadModel));
shelf_ = shelf;
state_ = kNormal;
creationTime_ = base::Time::Now();
}
return self;
}
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self];
[[self view] removeFromSuperview];
[super dealloc];
}
- (void)awakeFromNib {
// Since the shelf keeps laying out views as more items are added, relying on
// the WidthBaseTweaker to resize the dangerous download part does not work.
DCHECK(buttonTweaker_ != nil);
CGFloat widthChange = [buttonTweaker_ changedWidth];
// Grow the parent views
WidenView([self view], widthChange);
WidenView(dangerousDownloadView_, widthChange);
// Slide the two buttons over.
NSPoint frameOrigin = [buttonTweaker_ frame].origin;
frameOrigin.x += widthChange;
[buttonTweaker_ setFrameOrigin:frameOrigin];
[self setStateFromDownload:bridge_->download_model()];
bridge_->LoadIcon();
}
- (void)setStateFromDownload:(BaseDownloadItemModel*)downloadModel {
DCHECK_EQ(bridge_->download_model(), downloadModel);
// Handle dangerous downloads.
if (downloadModel->download()->safety_state() == DownloadItem::DANGEROUS) {
[self setState:kDangerous];
// Set label.
NSFont* font = [dangerousDownloadLabel_ font];
gfx::Font fontChr = gfx::Font::CreateFont(
base::SysNSStringToWide([font fontName]), [font pointSize]);
string16 elidedFilename = WideToUTF16(ElideFilename(
downloadModel->download()->original_name(), fontChr, kTextWidth));
NSString* dangerousWarning =
l10n_util::GetNSStringFWithFixup(IDS_PROMPT_DANGEROUS_DOWNLOAD,
elidedFilename);
[dangerousDownloadLabel_ setStringValue:dangerousWarning];
return;
}
// Set the correct popup menu.
if (downloadModel->download()->state() == DownloadItem::COMPLETE)
currentMenu_ = completeDownloadMenu_;
else
currentMenu_ = activeDownloadMenu_;
[progressView_ setMenu:currentMenu_]; // for context menu
[cell_ setStateFromDownload:downloadModel];
}
- (void)setIcon:(NSImage*)icon {
[cell_ setImage:icon];
}
- (void)remove {
// We are deleted after this!
[shelf_ remove:self];
}
- (void)updateVisibility:(id)sender {
// TODO(thakis): Make this prettier, by fading the items out or overlaying
// the partial visible one with a horizontal alpha gradient -- crbug.com/17830
NSView* view = [self view];
NSRect containerFrame = [[view superview] frame];
[view setHidden:(NSMaxX([view frame]) > NSWidth(containerFrame))];
}
- (IBAction)handleButtonClick:(id)sender {
if ([cell_ isButtonPartPressed]) {
DownloadItem* download = bridge_->download_model()->download();
if (download->state() == DownloadItem::IN_PROGRESS)
download->set_open_when_complete(!download->open_when_complete());
else if (download->state() == DownloadItem::COMPLETE)
download_util::OpenDownload(download);
} else {
[NSMenu popUpContextMenu:currentMenu_
withEvent:[NSApp currentEvent]
forView:progressView_];
}
}
- (NSSize)preferredSize {
if (state_ == kNormal)
return [progressView_ frame].size;
DCHECK_EQ(kDangerous, state_);
return [dangerousDownloadView_ frame].size;
}
- (DownloadItem*)download {
return bridge_->download_model()->download();
}
- (void)clearDangerousMode {
[self setState:kNormal];
}
- (BOOL)isDangerousMode {
return state_ == kDangerous;
}
- (void)setState:(DownoadItemState)state {
if (state_ == state)
return;
state_ = state;
if (state_ == kNormal) {
[progressView_ setHidden:NO];
[dangerousDownloadView_ setHidden:YES];
} else {
DCHECK_EQ(kDangerous, state_);
[progressView_ setHidden:YES];
[dangerousDownloadView_ setHidden:NO];
}
[shelf_ layoutItems];
}
- (IBAction)saveDownload:(id)sender {
// The user has confirmed a dangerous download. We record how quickly the
// user did this to detect whether we're being clickjacked.
UMA_HISTOGRAM_LONG_TIMES("clickjacking.save_download",
base::Time::Now() - creationTime_);
// This will change the state and notify us.
bridge_->download_model()->download()->manager()->DangerousDownloadValidated(
bridge_->download_model()->download());
}
- (IBAction)discardDownload:(id)sender {
UMA_HISTOGRAM_LONG_TIMES("clickjacking.discard_download",
base::Time::Now() - creationTime_);
if (bridge_->download_model()->download()->state() ==
DownloadItem::IN_PROGRESS)
bridge_->download_model()->download()->Cancel(true);
bridge_->download_model()->download()->Remove(true);
// WARNING: we are deleted at this point. Don't access 'this'.
}
// Sets the enabled and checked state of a particular menu item for this
// download. We translate the NSMenuItem selection to menu selections understood
// by the non platform specific download context menu.
- (BOOL)validateMenuItem:(NSMenuItem *)item {
SEL action = [item action];
int actionId = 0;
if (action == @selector(handleOpen:)) {
actionId = DownloadShelfContextMenuMac::OPEN_WHEN_COMPLETE;
} else if (action == @selector(handleAlwaysOpen:)) {
actionId = DownloadShelfContextMenuMac::ALWAYS_OPEN_TYPE;
} else if (action == @selector(handleReveal:)) {
actionId = DownloadShelfContextMenuMac::SHOW_IN_FOLDER;
} else if (action == @selector(handleCancel:)) {
actionId = DownloadShelfContextMenuMac::CANCEL;
} else {
NOTREACHED();
return YES;
}
if (menuBridge_->ItemIsChecked(actionId))
[item setState:NSOnState];
else
[item setState:NSOffState];
return menuBridge_->IsItemCommandEnabled(actionId) ? YES : NO;
}
- (IBAction)handleOpen:(id)sender {
menuBridge_->ExecuteItemCommand(
DownloadShelfContextMenuMac::OPEN_WHEN_COMPLETE);
}
- (IBAction)handleAlwaysOpen:(id)sender {
menuBridge_->ExecuteItemCommand(
DownloadShelfContextMenuMac::ALWAYS_OPEN_TYPE);
}
- (IBAction)handleReveal:(id)sender {
menuBridge_->ExecuteItemCommand(DownloadShelfContextMenuMac::SHOW_IN_FOLDER);
}
- (IBAction)handleCancel:(id)sender {
menuBridge_->ExecuteItemCommand(DownloadShelfContextMenuMac::CANCEL);
}
@end