Index: chrome/browser/views/download_item_view.cc |
=================================================================== |
--- chrome/browser/views/download_item_view.cc (revision 2758) |
+++ chrome/browser/views/download_item_view.cc (working copy) |
@@ -6,6 +6,8 @@ |
#include <vector> |
+#include "base/file_util.h" |
+#include "base/string_util.h" |
#include "chrome/app/theme/theme_resources.h" |
#include "chrome/browser/browser_process.h" |
#include "chrome/browser/download/download_util.h" |
@@ -13,6 +15,7 @@ |
#include "chrome/common/gfx/chrome_canvas.h" |
#include "chrome/common/resource_bundle.h" |
#include "chrome/common/win_util.h" |
+#include "chrome/views/native_button.h" |
#include "chrome/views/root_view.h" |
#include "chrome/views/view_container.h" |
@@ -22,15 +25,27 @@ |
// animation is added, and also possibly to take into account |
// different screen resolutions. |
static const int kTextWidth = 140; // Pixels |
+static const int kDangerousTextWidth = 200; // Pixels |
static const int kHorizontalTextPadding = 2; // Pixels |
static const int kVerticalPadding = 3; // Pixels |
static const int kVerticalTextSpacer = 2; // Pixels |
static const int kVerticalTextPadding = 2; // Pixels |
+// The maximum number of characters we show in a file name when displaying the |
+// dangerous download message. |
+static const int kFileNameMaxLength = 20; |
+ |
// We add some padding before the left image so that the progress animation icon |
// hides the corners of the left image. |
static const int kLeftPadding = 0; // Pixels. |
+// The space between the Save and Discard buttons when prompting for a dangerous |
+// donwload. |
+static const int kButtonPadding = 5; // Pixels. |
+ |
+// The space on the left and right side of the dangerous donwload label. |
+static const int kLabelPadding = 4; // Pixels. |
+ |
static const SkColor kFileNameColor = SkColorSetRGB(87, 108, 149); |
static const SkColor kStatusColor = SkColorSetRGB(123, 141, 174); |
@@ -49,11 +64,16 @@ |
body_state_(NORMAL), |
drop_down_state_(NORMAL), |
drop_down_pressed_(false), |
- file_name_(download_->file_name()), |
status_text_(l10n_util::GetString(IDS_DOWNLOAD_STATUS_STARTING)), |
show_status_text_(true), |
dragging_(false), |
- starting_drag_(false) { |
+ starting_drag_(false), |
+ warning_icon_(NULL), |
+ save_button_(NULL), |
+ discard_button_(NULL), |
+ dangerous_download_label_(NULL), |
+ dangerous_download_label_sized_(false), |
+ cached_button_size_(0, 0) { |
// TODO(idana) Bug# 1163334 |
// |
// We currently do not mirror each download item on the download shelf (even |
@@ -130,6 +150,19 @@ |
}; |
pushed_drop_down_image_set_ = pushed_drop_down_image_set; |
+ BodyImageSet dangerous_mode_body_image_set = { |
+ rb.GetBitmapNamed(IDR_DOWNLOAD_BUTTON_LEFT_TOP), |
+ rb.GetBitmapNamed(IDR_DOWNLOAD_BUTTON_LEFT_MIDDLE), |
+ rb.GetBitmapNamed(IDR_DOWNLOAD_BUTTON_LEFT_BOTTOM), |
+ rb.GetBitmapNamed(IDR_DOWNLOAD_BUTTON_CENTER_TOP), |
+ rb.GetBitmapNamed(IDR_DOWNLOAD_BUTTON_CENTER_MIDDLE), |
+ rb.GetBitmapNamed(IDR_DOWNLOAD_BUTTON_CENTER_BOTTOM), |
+ rb.GetBitmapNamed(IDR_DOWNLOAD_BUTTON_RIGHT_TOP_NO_DD), |
+ rb.GetBitmapNamed(IDR_DOWNLOAD_BUTTON_RIGHT_MIDDLE_NO_DD), |
+ rb.GetBitmapNamed(IDR_DOWNLOAD_BUTTON_RIGHT_BOTTOM_NO_DD) |
+ }; |
+ dangerous_mode_body_image_set_ = dangerous_mode_body_image_set; |
+ |
LoadIcon(); |
font_ = ResourceBundle::GetSharedInstance().GetFont(ResourceBundle::BaseFont); |
@@ -151,6 +184,33 @@ |
body_hover_animation_.reset(new SlideAnimation(this)); |
drop_hover_animation_.reset(new SlideAnimation(this)); |
+ if (download->safety_state() == DownloadItem::DANGEROUS) { |
+ body_state_ = DANGEROUS; |
+ drop_down_state_ = DANGEROUS; |
+ |
+ warning_icon_ = rb.GetBitmapNamed(IDR_WARNING); |
+ save_button_ = new ChromeViews::NativeButton( |
+ l10n_util::GetString(IDS_SAVE_DOWNLOAD)); |
+ save_button_->set_enforce_dlu_min_size(false); |
+ save_button_->SetListener(this); |
+ discard_button_ = new ChromeViews::NativeButton( |
+ l10n_util::GetString(IDS_DISCARD_DOWNLOAD)); |
+ discard_button_->SetListener(this); |
+ discard_button_->set_enforce_dlu_min_size(false); |
+ AddChildView(save_button_); |
+ AddChildView(discard_button_); |
+ std::wstring file_name = download->original_name(); |
+ // Ensure the file name is not too long. |
+ ElideString(file_name, kFileNameMaxLength, &file_name); |
+ dangerous_download_label_ = new ChromeViews::Label( |
+ l10n_util::GetStringF(IDS_PROMPT_DANGEROUS_DOWNLOAD, file_name)); |
+ dangerous_download_label_->SetMultiLine(true); |
+ dangerous_download_label_->SetHorizontalAlignment( |
+ ChromeViews::Label::ALIGN_LEFT); |
+ dangerous_download_label_->SetColor(kFileNameColor); |
+ AddChildView(dangerous_download_label_); |
+ } |
+ |
// Set up our animation |
StartDownloadProgress(); |
} |
@@ -189,6 +249,12 @@ |
void DownloadItemView::OnDownloadUpdated(DownloadItem* download) { |
DCHECK(download == download_); |
+ if (body_state_ == DANGEROUS && |
+ download->safety_state() == DownloadItem::DANGEROUS_BUT_VALIDATED) { |
+ // We have been approved. |
+ ClearDangerousMode(); |
+ } |
+ |
std::wstring status_text = model_->GetStatusText(); |
switch (download_->state()) { |
case DownloadItem::IN_PROGRESS: |
@@ -226,18 +292,46 @@ |
// View overrides |
+// In dangerous mode we have to layout our buttons. |
+void DownloadItemView::Layout() { |
+ if (IsDangerousMode()) { |
+ SizeLabelToMinWidth(); |
+ int x = kLeftPadding + dangerous_mode_body_image_set_.top_left->width() + |
+ warning_icon_->width() + kLabelPadding; |
+ int y = (height() - dangerous_download_label_->height()) / 2; |
+ dangerous_download_label_->SetBounds(x, y, |
+ dangerous_download_label_->width(), |
+ dangerous_download_label_->height()); |
+ CSize button_size; |
+ GetButtonSize(&button_size); |
+ x += dangerous_download_label_->width() + kLabelPadding; |
+ y = (height() - button_size.cy) / 2; |
+ save_button_->SetBounds(x, y, button_size.cx, button_size.cy); |
+ x += button_size.cx + kButtonPadding; |
+ discard_button_->SetBounds(x, y, button_size.cx, button_size.cy); |
+ } |
+} |
+ |
+void DownloadItemView::DidChangeBounds(const CRect& previous, |
+ const CRect& current) { |
+ Layout(); |
+} |
+ |
+void DownloadItemView::ButtonPressed(ChromeViews::NativeButton* sender) { |
+ if (sender == discard_button_) { |
+ if (download_->state() == DownloadItem::IN_PROGRESS) |
+ download_->Cancel(true); |
+ download_->Remove(true); |
+ // WARNING: we are deleted at this point. Don't access 'this'. |
+ } else if (sender == save_button_) { |
+ // This will change the state and notify us. |
+ download_->manager()->DangerousDownloadValidated(download_); |
+ } |
+} |
+ |
// Load an icon for the file type we're downloading, and animate any in progress |
// download state. |
void DownloadItemView::Paint(ChromeCanvas* canvas) { |
- int center_width = width() - kLeftPadding - |
- normal_body_image_set_.left->width() - |
- normal_body_image_set_.right->width() - |
- normal_drop_down_image_set_.center->width(); |
- |
- // May be caused by animation. |
- if (center_width <= 0) |
- return; |
- |
BodyImageSet* body_image_set; |
switch (body_state_) { |
case NORMAL: |
@@ -247,6 +341,9 @@ |
case PUSHED: |
body_image_set = &pushed_body_image_set_; |
break; |
+ case DANGEROUS: |
+ body_image_set = &dangerous_mode_body_image_set_; |
+ break; |
default: |
NOTREACHED(); |
} |
@@ -259,10 +356,24 @@ |
case PUSHED: |
drop_down_image_set = &pushed_drop_down_image_set_; |
break; |
+ case DANGEROUS: |
+ drop_down_image_set = NULL; // No drop-down in dangerous mode. |
+ break; |
default: |
NOTREACHED(); |
} |
+ int center_width = width() - kLeftPadding - |
+ body_image_set->left->width() - |
+ body_image_set->right->width() - |
+ (drop_down_image_set ? |
+ normal_drop_down_image_set_.center->width() : |
+ 0); |
+ |
+ // May be caused by animation. |
+ if (center_width <= 0) |
+ return; |
+ |
// Paint the background images. |
int x = kLeftPadding; |
PaintBitmaps(canvas, |
@@ -301,65 +412,76 @@ |
PaintBitmaps(canvas, |
hot_body_image_set_.top_right, hot_body_image_set_.right, |
hot_body_image_set_.bottom_right, |
- x, box_y_, box_height_, hot_body_image_set_.top_right->width()); |
+ x, box_y_, box_height_, |
+ hot_body_image_set_.top_right->width()); |
canvas->restore(); |
} |
x += body_image_set->top_right->width(); |
- PaintBitmaps(canvas, |
- drop_down_image_set->top, drop_down_image_set->center, |
- drop_down_image_set->bottom, |
- x, box_y_, box_height_, drop_down_image_set->top->width()); |
- // Overlay our drop-down hot state. |
- if (drop_hover_animation_->GetCurrentValue() > 0) { |
- canvas->saveLayerAlpha(NULL, |
- static_cast<int>(drop_hover_animation_->GetCurrentValue() * 255), |
- SkCanvas::kARGB_NoClipLayer_SaveFlag); |
- canvas->drawARGB(0, 255, 255, 255, SkPorterDuff::kClear_Mode); |
- |
+ // Paint the drop-down. |
+ if (drop_down_image_set) { |
PaintBitmaps(canvas, |
drop_down_image_set->top, drop_down_image_set->center, |
drop_down_image_set->bottom, |
x, box_y_, box_height_, drop_down_image_set->top->width()); |
- canvas->restore(); |
+ // Overlay our drop-down hot state. |
+ if (drop_hover_animation_->GetCurrentValue() > 0) { |
+ canvas->saveLayerAlpha(NULL, |
+ static_cast<int>(drop_hover_animation_->GetCurrentValue() * 255), |
+ SkCanvas::kARGB_NoClipLayer_SaveFlag); |
+ canvas->drawARGB(0, 255, 255, 255, SkPorterDuff::kClear_Mode); |
+ |
+ PaintBitmaps(canvas, |
+ drop_down_image_set->top, drop_down_image_set->center, |
+ drop_down_image_set->bottom, |
+ x, box_y_, box_height_, drop_down_image_set->top->width()); |
+ |
+ canvas->restore(); |
+ } |
} |
// Print the text, left aligned. |
// Last value of x was the end of the right image, just before the button. |
- if (show_status_text_) { |
- int y = box_y_ + kVerticalPadding; |
- canvas->DrawStringInt(file_name_, font_, kFileNameColor, |
- download_util::kSmallProgressIconSize, y, |
- kTextWidth, font_.height()); |
- y += font_.height() + kVerticalTextPadding; |
+ // Note that in dangerous mode we use a label (as the text is multi-line). |
+ if (!IsDangerousMode()) { |
+ if (show_status_text_) { |
+ int y = box_y_ + kVerticalPadding; |
+ canvas->DrawStringInt(download_->GetFileName(), font_, kFileNameColor, |
+ download_util::kSmallProgressIconSize, y, |
+ kTextWidth, font_.height()); |
+ y += font_.height() + kVerticalTextPadding; |
- canvas->DrawStringInt(status_text_, font_, kStatusColor, |
- download_util::kSmallProgressIconSize, y, |
- kTextWidth, font_.height()); |
- } else { |
- int y = box_y_ + (box_height_ - font_.height()) / 2; |
- canvas->DrawStringInt(file_name_, font_, kFileNameColor, |
- download_util::kSmallProgressIconSize, y, |
- kTextWidth, font_.height()); |
+ canvas->DrawStringInt(status_text_, font_, kStatusColor, |
+ download_util::kSmallProgressIconSize, y, |
+ kTextWidth, font_.height()); |
+ } else { |
+ int y = box_y_ + (box_height_ - font_.height()) / 2; |
+ canvas->DrawStringInt(download_->GetFileName(), font_, kFileNameColor, |
+ download_util::kSmallProgressIconSize, y, |
+ kTextWidth, font_.height()); |
+ } |
} |
// Paint the icon. |
IconManager* im = g_browser_process->icon_manager(); |
- SkBitmap* icon = im->LookupIcon(download_->full_path(), IconLoader::SMALL); |
+ SkBitmap* icon = IsDangerousMode() ? warning_icon_ : |
+ im->LookupIcon(download_->full_path(), IconLoader::SMALL); |
if (icon) { |
- if (download_->state() == DownloadItem::IN_PROGRESS) { |
- download_util::PaintDownloadProgress(canvas, this, 0, 0, |
- progress_angle_, |
- download_->PercentComplete(), |
- download_util::SMALL); |
- } else if (download_->state() == DownloadItem::COMPLETE && |
- complete_animation_->IsAnimating()) { |
- download_util::PaintDownloadComplete(canvas, this, 0, 0, |
- complete_animation_->GetCurrentValue(), |
- download_util::SMALL); |
+ if (!IsDangerousMode()) { |
+ if (download_->state() == DownloadItem::IN_PROGRESS) { |
+ download_util::PaintDownloadProgress(canvas, this, 0, 0, |
+ progress_angle_, |
+ download_->PercentComplete(), |
+ download_util::SMALL); |
+ } else if (download_->state() == DownloadItem::COMPLETE && |
+ complete_animation_->IsAnimating()) { |
+ download_util::PaintDownloadComplete(canvas, this, 0, 0, |
+ complete_animation_->GetCurrentValue(), |
+ download_util::SMALL); |
+ } |
} |
// Draw the icon image |
@@ -400,20 +522,65 @@ |
SchedulePaint(); |
} |
+void DownloadItemView::ClearDangerousMode() { |
+ DCHECK(download_->safety_state() == DownloadItem::DANGEROUS_BUT_VALIDATED && |
+ body_state_ == DANGEROUS && drop_down_state_ == DANGEROUS); |
+ |
+ body_state_ = NORMAL; |
+ drop_down_state_ = NORMAL; |
+ |
+ // Remove the views used by the dangerours mode. |
+ RemoveChildView(save_button_); |
+ delete save_button_; |
+ save_button_ = NULL; |
+ RemoveChildView(discard_button_); |
+ delete discard_button_; |
+ discard_button_ = NULL; |
+ RemoveChildView(dangerous_download_label_); |
+ delete dangerous_download_label_; |
+ dangerous_download_label_ = NULL; |
+ |
+ // We need to load the icon now that the download_ has the real path. |
+ LoadIcon(); |
+ |
+ // Force the shelf to layout again as our size has changed. |
+ parent_->Layout(); |
+ parent_->SchedulePaint(); |
+} |
+ |
void DownloadItemView::GetPreferredSize(CSize* out) { |
- int width = kLeftPadding + normal_body_image_set_.top_left->width(); |
- width += download_util::kSmallProgressIconSize; |
- width += kTextWidth; |
- width += normal_body_image_set_.top_right->width(); |
- width += normal_drop_down_image_set_.top->width(); |
- |
+ int width, height; |
+ if (IsDangerousMode()) { |
+ width = kLeftPadding + dangerous_mode_body_image_set_.top_left->width(); |
+ width += warning_icon_->width() + kLabelPadding; |
+ width += dangerous_download_label_->width() + kLabelPadding; |
+ CSize button_size; |
+ GetButtonSize(&button_size); |
+ width += button_size.cx * 2 + kButtonPadding; |
+ width += dangerous_mode_body_image_set_.top_right->width(); |
+ height = std::max<int>(2 * kVerticalPadding + 2 * font_.height() + |
+ kVerticalTextPadding, |
+ 2 * kVerticalPadding + warning_icon_->height()); |
+ height = std::max<int>(height, 2 * kVerticalPadding + button_size.cy); |
+ } else { |
+ width = kLeftPadding + normal_body_image_set_.top_left->width(); |
+ width += download_util::kSmallProgressIconSize; |
+ width += kTextWidth; |
+ width += normal_body_image_set_.top_right->width(); |
+ width += normal_drop_down_image_set_.top->width(); |
+ height = std::max<int>(2 * kVerticalPadding + 2 * font_.height() + |
+ kVerticalTextPadding, |
+ download_util::kSmallProgressIconSize); |
+ } |
out->cx = width; |
- out->cy = std::max<int>( |
- 2 * kVerticalPadding + 2 * font_.height() + kVerticalTextPadding, |
- download_util::kSmallProgressIconSize); |
+ out->cy = height; |
} |
void DownloadItemView::OnMouseExited(const ChromeViews::MouseEvent& event) { |
+ // Mouse should not activate us in dangerous mode. |
+ if (IsDangerousMode()) |
+ return; |
+ |
SetState(NORMAL, drop_down_pressed_ ? PUSHED : NORMAL); |
body_hover_animation_->Hide(); |
drop_hover_animation_->Hide(); |
@@ -421,6 +588,10 @@ |
// Display the context menu for this item. |
bool DownloadItemView::OnMousePressed(const ChromeViews::MouseEvent& event) { |
+ // Mouse should not activate us in dangerous mode. |
+ if (IsDangerousMode()) |
+ return true; |
+ |
// Stop any completion animation. |
if (complete_animation_.get() && complete_animation_->IsAnimating()) |
complete_animation_->End(); |
@@ -471,6 +642,10 @@ |
} |
void DownloadItemView::OnMouseMoved(const ChromeViews::MouseEvent& event) { |
+ // Mouse should not activate us in dangerous mode. |
+ if (IsDangerousMode()) |
+ return; |
+ |
bool on_body = event.x() < drop_down_x_; |
SetState(on_body ? HOT : NORMAL, on_body ? NORMAL : HOT); |
if (on_body) { |
@@ -484,6 +659,10 @@ |
void DownloadItemView::OnMouseReleased(const ChromeViews::MouseEvent& event, |
bool canceled) { |
+ // Mouse should not activate us in dangerous mode. |
+ if (IsDangerousMode()) |
+ return; |
+ |
if (dragging_) { |
// Starting a drag results in a MouseReleased, we need to ignore it. |
dragging_ = false; |
@@ -498,6 +677,10 @@ |
// Handle drag (file copy) operations. |
bool DownloadItemView::OnMouseDragged(const ChromeViews::MouseEvent& event) { |
+ // Mouse should not activate us in dangerous mode. |
+ if (IsDangerousMode()) |
+ return true; |
+ |
if (!starting_drag_) { |
starting_drag_ = true; |
drag_start_point_ = event.location(); |
@@ -543,3 +726,71 @@ |
NewCallback(this, &DownloadItemView::OnExtractIconComplete)); |
} |
+void DownloadItemView::GetButtonSize(CSize* size) { |
+ DCHECK(save_button_ && discard_button_); |
+ // We cache the size when successfully retrieved, not for performance reasons |
+ // but because if this DownloadItemView is being animated while the tab is |
+ // not showing, the native buttons are not parented and their preferred size |
+ // is 0, messing-up the layout. |
+ if (cached_button_size_.cx != 0) { |
+ *size = cached_button_size_; |
+ } |
+ |
+ CSize tmp_size; |
+ save_button_->GetMinimumSize(size); |
+ discard_button_->GetMinimumSize(&tmp_size); |
+ |
+ size->cx = std::max(size->cx, tmp_size.cx); |
+ size->cy = std::max(size->cy, tmp_size.cy); |
+ |
+ if (size->cx != 0) { |
+ cached_button_size_.cx = size->cx; |
+ cached_button_size_.cy = size->cy; |
+ } |
+} |
+ |
+// This method computes the miminum width of the label for diplaying its text |
+// on 2 lines. It just breaks the string in 2 lines on the spaces and keeps the |
+// configuration with minimum width. |
+void DownloadItemView::SizeLabelToMinWidth() { |
+ if (dangerous_download_label_sized_) |
+ return; |
+ |
+ std::wstring text = dangerous_download_label_->GetText(); |
+ TrimWhitespace(text, TRIM_ALL, &text); |
+ DCHECK_EQ(std::wstring::npos, text.find(L"\n")); |
+ |
+ // Make the label big so that GetPreferredSize() is not constrained by the |
+ // current width. |
+ dangerous_download_label_->SetBounds(0, 0, 1000, 1000); |
+ |
+ CSize size(0, 0); |
+ int min_width = -1; |
+ int sp_index = text.find(L" "); |
+ while (sp_index != std::wstring::npos) { |
+ text.replace(sp_index, 1, L"\n"); |
+ dangerous_download_label_->SetText(text); |
+ dangerous_download_label_->GetPreferredSize(&size); |
+ |
+ if (min_width == -1) |
+ min_width = size.cx; |
+ |
+ // If thw width is growing again, it means we passed the optimal width spot. |
+ if (size.cx > min_width) |
+ break; |
+ else |
+ min_width = size.cx; |
+ |
+ // Restore the string. |
+ text.replace(sp_index, 1, L" "); |
+ |
+ sp_index = text.find(L" ", sp_index + 1); |
+ } |
+ |
+ // If we have a line with no space, we won't cut it. |
+ if (min_width == -1) |
+ dangerous_download_label_->GetPreferredSize(&size); |
+ |
+ dangerous_download_label_->SetBounds(0, 0, size.cx, size.cy); |
+ dangerous_download_label_sized_ = true; |
+} |