| 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;
|
| +}
|
|
|