diff options
Diffstat (limited to 'chrome/browser/views/download_item_view.cc')
-rw-r--r-- | chrome/browser/views/download_item_view.cc | 369 |
1 files changed, 310 insertions, 59 deletions
diff --git a/chrome/browser/views/download_item_view.cc b/chrome/browser/views/download_item_view.cc index 2dcba5a..6d0af1e3 100644 --- a/chrome/browser/views/download_item_view.cc +++ b/chrome/browser/views/download_item_view.cc @@ -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" @@ -14,6 +16,7 @@ #include "chrome/common/l10n_util.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" @@ -23,15 +26,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); @@ -50,11 +65,16 @@ DownloadItemView::DownloadItemView(DownloadItem* download, 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 @@ -131,6 +151,19 @@ DownloadItemView::DownloadItemView(DownloadItem* download, }; 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); @@ -152,6 +185,33 @@ DownloadItemView::DownloadItemView(DownloadItem* download, 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(); } @@ -190,6 +250,12 @@ void DownloadItemView::StopDownloadProgress() { 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: @@ -227,18 +293,46 @@ void DownloadItemView::OnDownloadUpdated(DownloadItem* download) { // 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: @@ -248,6 +342,9 @@ void DownloadItemView::Paint(ChromeCanvas* canvas) { case PUSHED: body_image_set = &pushed_body_image_set_; break; + case DANGEROUS: + body_image_set = &dangerous_mode_body_image_set_; + break; default: NOTREACHED(); } @@ -260,10 +357,24 @@ void DownloadItemView::Paint(ChromeCanvas* canvas) { 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, @@ -302,65 +413,76 @@ void DownloadItemView::Paint(ChromeCanvas* canvas) { 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; - - 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()); + // 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(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 @@ -401,20 +523,65 @@ void DownloadItemView::SetState(State body_state, State drop_down_state) { 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(); +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, 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(); @@ -422,6 +589,10 @@ void DownloadItemView::OnMouseExited(const ChromeViews::MouseEvent& event) { // 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(); @@ -472,6 +643,10 @@ bool DownloadItemView::OnMousePressed(const ChromeViews::MouseEvent& event) { } 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) { @@ -485,6 +660,10 @@ void DownloadItemView::OnMouseMoved(const ChromeViews::MouseEvent& event) { 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; @@ -499,6 +678,10 @@ void DownloadItemView::OnMouseReleased(const ChromeViews::MouseEvent& event, // 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(); @@ -544,3 +727,71 @@ void DownloadItemView::LoadIcon() { 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; +}
\ No newline at end of file |