Add a minor text member to ui::MenuModel.
[chromium-blink-merge.git] / chrome / browser / ui / cocoa / download / download_item_controller.mm
bloba4037977fb416f6ef361e64ba11d2517eb60696b
1 // Copyright (c) 2012 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.
5 #import "chrome/browser/ui/cocoa/download/download_item_controller.h"
7 #include "base/mac/bundle_locations.h"
8 #include "base/mac/mac_util.h"
9 #include "base/metrics/histogram.h"
10 #include "base/strings/string16.h"
11 #include "base/strings/string_util.h"
12 #include "base/strings/sys_string_conversions.h"
13 #include "base/strings/utf_string_conversions.h"
14 #include "chrome/browser/download/chrome_download_manager_delegate.h"
15 #include "chrome/browser/download/download_item_model.h"
16 #include "chrome/browser/download/download_shelf_context_menu.h"
17 #import "chrome/browser/themes/theme_properties.h"
18 #import "chrome/browser/themes/theme_service.h"
19 #import "chrome/browser/ui/cocoa/download/download_item_button.h"
20 #import "chrome/browser/ui/cocoa/download/download_item_cell.h"
21 #include "chrome/browser/ui/cocoa/download/download_item_mac.h"
22 #import "chrome/browser/ui/cocoa/download/download_shelf_controller.h"
23 #import "chrome/browser/ui/cocoa/themed_window.h"
24 #import "chrome/browser/ui/cocoa/ui_localizer.h"
25 #include "content/public/browser/download_item.h"
26 #include "content/public/browser/page_navigator.h"
27 #include "grit/generated_resources.h"
28 #include "grit/theme_resources.h"
29 #include "third_party/GTM/AppKit/GTMUILocalizerAndLayoutTweaker.h"
30 #include "ui/base/l10n/l10n_util_mac.h"
31 #include "ui/base/resource/resource_bundle.h"
32 #include "ui/base/text/text_elider.h"
33 #include "ui/gfx/font.h"
34 #include "ui/gfx/image/image.h"
36 using content::DownloadItem;
38 namespace {
40 // NOTE: Mac currently doesn't use this like Windows does.  Mac uses this to
41 // control the min size on the dangerous download text.  TVL sent a query off to
42 // UX to fully spec all the the behaviors of download items and truncations
43 // rules so all platforms can get inline in the future.
44 const int kTextWidth = 140;            // Pixels
46 // The maximum width in pixels for the file name tooltip.
47 const int kToolTipMaxWidth = 900;
50 // Helper to widen a view.
51 void WidenView(NSView* view, CGFloat widthChange) {
52   // If it is an NSBox, the autoresize of the contentView is the issue.
53   NSView* contentView = view;
54   if ([view isKindOfClass:[NSBox class]]) {
55     contentView = [(NSBox*)view contentView];
56   }
57   BOOL autoresizesSubviews = [contentView autoresizesSubviews];
58   if (autoresizesSubviews) {
59     [contentView setAutoresizesSubviews:NO];
60   }
62   NSRect frame = [view frame];
63   frame.size.width += widthChange;
64   [view setFrame:frame];
66   if (autoresizesSubviews) {
67     [contentView setAutoresizesSubviews:YES];
68   }
71 }  // namespace
73 class DownloadShelfContextMenuMac : public DownloadShelfContextMenu {
74  public:
75   DownloadShelfContextMenuMac(DownloadItem* downloadItem,
76                               content::PageNavigator* navigator)
77       : DownloadShelfContextMenu(downloadItem, navigator) { }
79   // DownloadShelfContextMenu::GetMenuModel is protected.
80   using DownloadShelfContextMenu::GetMenuModel;
83 @interface DownloadItemController (Private)
84 - (void)themeDidChangeNotification:(NSNotification*)aNotification;
85 - (void)updateTheme:(ui::ThemeProvider*)themeProvider;
86 - (void)setState:(DownoadItemState)state;
87 @end
89 // Implementation of DownloadItemController
91 @implementation DownloadItemController
93 - (id)initWithDownload:(DownloadItem*)downloadItem
94                  shelf:(DownloadShelfController*)shelf
95              navigator:(content::PageNavigator*)navigator {
96   if ((self = [super initWithNibName:@"DownloadItem"
97                               bundle:base::mac::FrameworkBundle()])) {
98     // Must be called before [self view], so that bridge_ is set in awakeFromNib
99     bridge_.reset(new DownloadItemMac(downloadItem, self));
100     menuBridge_.reset(new DownloadShelfContextMenuMac(downloadItem, navigator));
102     NSNotificationCenter* defaultCenter = [NSNotificationCenter defaultCenter];
103     [defaultCenter addObserver:self
104                       selector:@selector(themeDidChangeNotification:)
105                           name:kBrowserThemeDidChangeNotification
106                         object:nil];
108     shelf_ = shelf;
109     state_ = kNormal;
110     creationTime_ = base::Time::Now();
111     font_list_.reset(new gfx::FontList(
112         ui::ResourceBundle::GetSharedInstance().GetFontList(
113             ui::ResourceBundle::BaseFont)));
114   }
115   return self;
118 - (void)dealloc {
119   [[NSNotificationCenter defaultCenter] removeObserver:self];
120   [progressView_ setController:nil];
121   [[self view] removeFromSuperview];
122   [super dealloc];
125 - (void)awakeFromNib {
126   [progressView_ setController:self];
128   [self setStateFromDownload:bridge_->download_model()];
130   GTMUILocalizerAndLayoutTweaker* localizerAndLayoutTweaker =
131       [[[GTMUILocalizerAndLayoutTweaker alloc] init] autorelease];
132   [localizerAndLayoutTweaker applyLocalizer:localizer_ tweakingUI:[self view]];
134   // The strings are based on the download item's name, sizing tweaks have to be
135   // manually done.
136   DCHECK(buttonTweaker_ != nil);
137   CGFloat widthChange = [buttonTweaker_ changedWidth];
138   // If it's a dangerous download, size the two lines so the text/filename
139   // is always visible.
140   if ([self isDangerousMode]) {
141     widthChange +=
142         [GTMUILocalizerAndLayoutTweaker
143           sizeToFitFixedHeightTextField:dangerousDownloadLabel_
144                                minWidth:kTextWidth];
145   }
146   // Grow the parent views
147   WidenView([self view], widthChange);
148   WidenView(dangerousDownloadView_, widthChange);
149   // Slide the two buttons over.
150   NSPoint frameOrigin = [buttonTweaker_ frame].origin;
151   frameOrigin.x += widthChange;
152   [buttonTweaker_ setFrameOrigin:frameOrigin];
154   bridge_->LoadIcon();
155   [self updateToolTip];
158 - (void)setStateFromDownload:(DownloadItemModel*)downloadModel {
159   DCHECK_EQ([self download], downloadModel->download());
161   // Handle dangerous downloads.
162   if (downloadModel->IsDangerous()) {
163     [self setState:kDangerous];
165     ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
166     NSString* dangerousWarning;
167     NSString* confirmButtonTitle;
168     NSImage* alertIcon;
170     dangerousWarning =
171         base::SysUTF16ToNSString(downloadModel->GetWarningText(
172             *font_list_, kTextWidth));
173     confirmButtonTitle =
174         base::SysUTF16ToNSString(downloadModel->GetWarningConfirmButtonText());
175     if (downloadModel->IsMalicious())
176       alertIcon = rb.GetNativeImageNamed(IDR_SAFEBROWSING_WARNING).ToNSImage();
177     else
178       alertIcon = rb.GetNativeImageNamed(IDR_WARNING).ToNSImage();
179     DCHECK(alertIcon);
180     [image_ setImage:alertIcon];
181     DCHECK(dangerousWarning);
182     [dangerousDownloadLabel_ setStringValue:dangerousWarning];
183     DCHECK(confirmButtonTitle);
184     [dangerousDownloadConfirmButton_ setTitle:confirmButtonTitle];
185     return;
186   }
188   // Set path to draggable download on completion.
189   if (downloadModel->download()->GetState() == DownloadItem::COMPLETE)
190     [progressView_ setDownload:downloadModel->download()->GetTargetFilePath()];
192   [cell_ setStateFromDownload:downloadModel];
195 - (void)setIcon:(NSImage*)icon {
196   [cell_ setImage:icon];
199 - (void)remove {
200   // We are deleted after this!
201   [shelf_ remove:self];
204 - (void)updateVisibility:(id)sender {
205   if ([[self view] window])
206     [self updateTheme:[[[self view] window] themeProvider]];
208   NSView* view = [self view];
209   NSRect containerFrame = [[view superview] frame];
210   [view setHidden:(NSMaxX([view frame]) > NSWidth(containerFrame))];
213 - (void)downloadWasOpened {
214   [shelf_ downloadWasOpened:self];
217 - (IBAction)handleButtonClick:(id)sender {
218   NSEvent* event = [NSApp currentEvent];
219   DownloadItem* download = [self download];
220   if ([event modifierFlags] & NSCommandKeyMask) {
221     // Let cmd-click show the file in Finder, like e.g. in Safari and Spotlight.
222     download->ShowDownloadInShell();
223   } else {
224     download->OpenDownload();
225   }
228 - (NSSize)preferredSize {
229   if (state_ == kNormal)
230     return [progressView_ frame].size;
231   DCHECK_EQ(kDangerous, state_);
232   return [dangerousDownloadView_ frame].size;
235 - (DownloadItem*)download {
236   return bridge_->download_model()->download();
239 - (ui::MenuModel*)contextMenuModel {
240   return menuBridge_->GetMenuModel();
243 - (void)updateToolTip {
244   string16 tooltip_text =
245       bridge_->download_model()->GetTooltipText(*font_list_, kToolTipMaxWidth);
246   [progressView_ setToolTip:base::SysUTF16ToNSString(tooltip_text)];
249 - (void)clearDangerousMode {
250   [self setState:kNormal];
251   // The state change hide the dangerouse download view and is now showing the
252   // download progress view.  This means the view is likely to be a different
253   // size, so trigger a shelf layout to fix up spacing.
254   [shelf_ layoutItems];
257 - (BOOL)isDangerousMode {
258   return state_ == kDangerous;
261 - (void)setState:(DownoadItemState)state {
262   if (state_ == state)
263     return;
264   state_ = state;
265   if (state_ == kNormal) {
266     [progressView_ setHidden:NO];
267     [dangerousDownloadView_ setHidden:YES];
268   } else {
269     DCHECK_EQ(kDangerous, state_);
270     [progressView_ setHidden:YES];
271     [dangerousDownloadView_ setHidden:NO];
272   }
273   // NOTE: Do not relayout the shelf, as this could get called during initial
274   // setup of the the item, so the localized text and sizing might not have
275   // happened yet.
278 // Called after a theme change took place, possibly for a different profile.
279 - (void)themeDidChangeNotification:(NSNotification*)notification {
280   [self updateTheme:[[[self view] window] themeProvider]];
283 // Adapt appearance to the current theme. Called after theme changes and before
284 // this is shown for the first time.
285 - (void)updateTheme:(ui::ThemeProvider*)themeProvider {
286   if (!themeProvider)
287     return;
289   NSColor* color = themeProvider->GetNSColor(ThemeProperties::COLOR_TAB_TEXT);
290   [dangerousDownloadLabel_ setTextColor:color];
293 - (IBAction)saveDownload:(id)sender {
294   // The user has confirmed a dangerous download.  We record how quickly the
295   // user did this to detect whether we're being clickjacked.
296   UMA_HISTOGRAM_LONG_TIMES("clickjacking.save_download",
297                            base::Time::Now() - creationTime_);
298   // This will change the state and notify us.
299   bridge_->download_model()->download()->ValidateDangerousDownload();
302 - (IBAction)discardDownload:(id)sender {
303   UMA_HISTOGRAM_LONG_TIMES("clickjacking.discard_download",
304                            base::Time::Now() - creationTime_);
305   DownloadItem* download = bridge_->download_model()->download();
306   download->Remove();
307   // WARNING: we are deleted at this point.  Don't access 'this'.
310 @end