Chromium Code Reviews| Index: ash/system/drive/tray_drive.cc |
| diff --git a/ash/system/drive/tray_drive.cc b/ash/system/drive/tray_drive.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..a3ff78a160c98e36d53a3fc4100efce317fda9ab |
| --- /dev/null |
| +++ b/ash/system/drive/tray_drive.cc |
| @@ -0,0 +1,433 @@ |
| +// Copyright (c) 2012 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. |
| + |
| +#include "ash/system/drive/tray_drive.h" |
| + |
| +#include <vector> |
| + |
| +#include "ash/shell.h" |
| +#include "ash/system/tray/system_tray.h" |
| +#include "ash/system/tray/system_tray_delegate.h" |
| +#include "ash/system/tray/tray_constants.h" |
| +#include "ash/system/tray/tray_item_more.h" |
| +#include "ash/system/tray/tray_item_view.h" |
| +#include "ash/system/tray/tray_views.h" |
| +#include "base/logging.h" |
| +#include "base/string_number_conversions.h" |
| +#include "base/utf_string_conversions.h" |
| +#include "base/stl_util.h" |
| +#include "grit/ash_strings.h" |
| +#include "grit/ui_resources.h" |
| +#include "ui/base/l10n/l10n_util.h" |
| +#include "ui/base/resource/resource_bundle.h" |
| +#include "ui/gfx/font.h" |
| +#include "ui/gfx/image/image.h" |
| +#include "ui/views/controls/button/image_button.h" |
| +#include "ui/views/controls/label.h" |
| +#include "ui/views/controls/progress_bar.h" |
| +#include "ui/views/layout/box_layout.h" |
| +#include "ui/views/layout/grid_layout.h" |
| + |
| +string16 GetTrayLabel(const ash::DriveOperationStatusList& list) { |
|
sadrul
2012/05/01 14:28:58
These two functions should either be static or in
zel
2012/05/01 16:56:52
Done.
|
| + return l10n_util::GetStringFUTF16(IDS_ASH_STATUS_TRAY_DRIVE_SYNCING, |
| + base::IntToString16(static_cast<int>(list.size()))); |
| +} |
| + |
| +ash::DriveOperationStatusList* GetCurrentOperationList() { |
| + ash::SystemTrayDelegate* delegate = |
| + ash::Shell::GetInstance()->tray_delegate(); |
| + ash::DriveOperationStatusList* list = new ash::DriveOperationStatusList(); |
| + delegate->GetDriveOperationStatusList(list); |
| + return list; |
| +} |
| + |
| +namespace ash { |
| + |
| +namespace internal { |
| + |
| +namespace tray { |
| + |
| +const int kSidePadding = 8; |
| +const int kHorizontalPadding = 6; |
| +const int kVerticalPadding = 6; |
| +const int kProgressBarWidth = 100; |
| +const int kProgressBarHeight = 8; |
| + |
| +class DriveDefaultView : public TrayItemMore { |
| + public: |
| + explicit DriveDefaultView(SystemTrayItem* owner, |
|
xiyuan
2012/05/01 16:47:53
nit: no need to have "explicit" when having more t
zel
2012/05/01 16:56:52
Done.
|
| + const DriveOperationStatusList* list) |
| + : TrayItemMore(owner) { |
| + ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance(); |
| + |
| + SetImage(bundle.GetImageNamed(IDR_AURA_UBER_TRAY_DRIVE).ToSkBitmap()); |
| + Update(list); |
| + } |
| + |
| + virtual ~DriveDefaultView() {} |
| + |
| + void Update(const DriveOperationStatusList* list) { |
| + DCHECK(list); |
| + string16 label = GetTrayLabel(*list); |
| + SetLabel(label); |
| + SetAccessibleName(label); |
| + } |
| + |
| + private: |
| + DISALLOW_COPY_AND_ASSIGN(DriveDefaultView); |
| +}; |
| + |
| +class DriveDetailedView : public views::View, |
| + public ViewClickListener { |
| + public: |
| + DriveDetailedView(SystemTrayItem* owner, |
| + const DriveOperationStatusList* list) |
| + : header_(NULL), operations_(NULL), settings_(NULL), |
| + in_progress_img_(NULL), done_img_(NULL), failed_img_(NULL) { |
|
sadrul
2012/05/01 14:28:58
Each in its own line.
zel
2012/05/01 16:56:52
Done.
|
| + SetLayoutManager(new views::BoxLayout( |
| + views::BoxLayout::kVertical, 0, 0, 0)); |
| + set_background(views::Background::CreateSolidBackground(kBackgroundColor)); |
| + |
| + in_progress_img_ = ResourceBundle::GetSharedInstance().GetBitmapNamed( |
| + IDR_AURA_UBER_TRAY_DRIVE); |
| + done_img_ = ResourceBundle::GetSharedInstance().GetBitmapNamed( |
| + IDR_AURA_UBER_TRAY_DRIVE_DONE); |
| + failed_img_ = ResourceBundle::GetSharedInstance().GetBitmapNamed( |
| + IDR_AURA_UBER_TRAY_DRIVE_FAILED); |
| + |
| + Update(list); |
| + } |
| + |
| + virtual ~DriveDetailedView() { |
| + STLDeleteValues(&update_map_); |
| + } |
| + |
| + void Update(const DriveOperationStatusList* list) { |
| + AppendHeaderEntry(list); |
| + AppendOperationList(list); |
| +// AppendSettings(); |
|
sadrul
2012/05/01 14:28:58
Left out intentionally?
zel
2012/05/01 16:56:52
Done.
|
| + ChildPreferredSizeChanged(operations_); |
| + SchedulePaint(); |
| + } |
| + |
| + private: |
| + class OperationProgressBar : public views::ProgressBar { |
| + public: |
| + OperationProgressBar() {} |
| + private: |
| + |
|
xiyuan
2012/05/01 16:47:53
nit: empty line before "private:"?
zel
2012/05/01 16:56:52
Done.
|
| + // Overridden from View: |
| + virtual gfx::Size GetPreferredSize() OVERRIDE { |
| + return gfx::Size(kProgressBarWidth, kProgressBarHeight); |
| + } |
| + }; |
|
sadrul
2012/05/01 14:28:58
DISALLOW_COPY_AND_ASSIGN
zel
2012/05/01 16:56:52
Done.
|
| + |
| + class RowView : public views::View, |
| + public views::ButtonListener { |
| + public: |
| + RowView(DriveDetailedView* parent, |
| + ash::DriveOperationStatus::OperationState state, |
| + double progress, |
| + const FilePath& file_path) |
| + : parent_(parent), status_img_(NULL), label_container_(NULL), |
| + progress_bar_(NULL), cancel_button_(NULL), file_path_(file_path) { |
|
sadrul
2012/05/01 14:28:58
each in its own line
zel
2012/05/01 16:56:52
Done.
|
| + // Status image. |
| + status_img_ = new views::ImageView(); |
| + status_img_->set_focusable(false); |
|
xiyuan
2012/05/01 16:47:53
nit: probably don't need this as ImageView is not
zel
2012/05/01 16:56:52
Done.
|
| + AddChildView(status_img_); |
| + |
| + label_container_ = new views::View(); |
| + label_container_->SetLayoutManager(new views::BoxLayout( |
| + views::BoxLayout::kVertical, 0, 0, kVerticalPadding)); |
| + views::Label* label = new views::Label( |
| + UTF8ToUTF16(file_path.BaseName().value())); |
| + label->SetHorizontalAlignment(views::Label::ALIGN_LEFT); |
| + label_container_->AddChildView(label); |
| + // Add progress bar. |
| + progress_bar_ = new OperationProgressBar(); |
| + label_container_->AddChildView(progress_bar_); |
| + |
| + AddChildView(label_container_); |
| + |
| + cancel_button_ = new views::ImageButton(this); |
| + cancel_button_->set_focusable(false); |
|
xiyuan
2012/05/01 16:47:53
nit: probably don't need this as ImageButton/Custo
zel
2012/05/01 16:56:52
Done.
|
| + cancel_button_->SetImage(views::ImageButton::BS_NORMAL, |
| + ResourceBundle::GetSharedInstance().GetBitmapNamed( |
| + IDR_AURA_UBER_TRAY_DRIVE_CANCEL)); |
| + |
| + UpdateStatus(parent_, state, progress); |
| + AddChildView(cancel_button_); |
| + } |
| + |
| + void UpdateStatus(DriveDetailedView* parent, |
|
xiyuan
2012/05/01 16:47:53
nit: since we have member parent_ now, probably do
zel
2012/05/01 16:56:52
Done.
zel
2012/05/01 16:56:52
Done.
|
| + ash::DriveOperationStatus::OperationState state, |
| + double progress) { |
| + status_img_->SetImage(parent->GetImageForState(state)); |
| + progress_bar_->SetValue(progress); |
| + cancel_button_->SetVisible( |
| + state == ash::DriveOperationStatus::OPERATION_IN_PROGRESS || |
| + state == ash::DriveOperationStatus::OPERATION_SUSPENDED); |
| + } |
| + |
| + private: |
| + |
|
xiyuan
2012/05/01 16:47:53
nit: nuke this empty line?
|
| + // views::View overrides. |
| + virtual gfx::Size GetPreferredSize() OVERRIDE { |
| + return gfx::Size( |
| + status_img_->GetPreferredSize().width() + |
| + label_container_->GetPreferredSize().width() + |
| + cancel_button_->GetPreferredSize().width() + |
| + 2 * kSidePadding + 2 * kHorizontalPadding, |
| + std::max(status_img_->GetPreferredSize().height(), |
| + std::max(label_container_->GetPreferredSize().height(), |
| + cancel_button_->GetPreferredSize().height())) + |
| + 2 * kVerticalPadding); |
| + |
|
xiyuan
2012/05/01 16:47:53
nit: nuke this empty line?
zel
2012/05/01 16:56:52
Done.
|
| + } |
| + |
| + virtual void Layout() OVERRIDE { |
| + gfx::Rect child_area(GetLocalBounds()); |
| + if (child_area.IsEmpty()) |
| + return; |
| + |
| + int pos_x = child_area.x() + kSidePadding; |
| + int pos_y = child_area.y() + kVerticalPadding; |
| + |
| + gfx::Rect bounds_status( |
| + gfx::Point(pos_x, |
| + (child_area.height() - 2 * kVerticalPadding - |
| + label_container_->GetPreferredSize().height())/2), |
| + status_img_->GetPreferredSize()); |
| + status_img_->SetBoundsRect(bounds_status.Intersect(child_area)); |
| + pos_x += status_img_->GetPreferredSize().width() + kHorizontalPadding; |
|
xiyuan
2012/05/01 16:47:53
nit: status_img_->GetPreferredSize().width() -> st
zel
2012/05/01 16:56:52
Done.
|
| + |
| + gfx::Rect bounds_label(pos_x, pos_y, |
|
xiyuan
2012/05/01 16:47:53
nit: pos_y on its own line
|
| + child_area.width() - 2 * kSidePadding - |
| + 2 * kHorizontalPadding - |
| + status_img_->GetPreferredSize().width() - |
| + cancel_button_->GetPreferredSize().width(), |
|
xiyuan
2012/05/01 16:47:53
nit: indent above 3 lines to make it clear that th
zel
2012/05/01 16:56:52
Done.
|
| + label_container_->GetPreferredSize().height()); |
| + label_container_->SetBoundsRect(bounds_label.Intersect(child_area)); |
| + pos_x += label_container_->bounds().width() + kHorizontalPadding; |
| + |
| + gfx::Rect bounds_button( |
| + gfx::Point(pos_x, |
| + (child_area.height() - 2 * kVerticalPadding - |
| + label_container_->GetPreferredSize().height())/2), |
| + cancel_button_->GetPreferredSize()); |
| + cancel_button_->SetBoundsRect(bounds_button.Intersect(child_area)); |
| + } |
| + |
| + // views::ButtonListener overrides. |
| + virtual void ButtonPressed(views::Button* sender, |
| + const views::Event& event) OVERRIDE { |
| + DCHECK(sender == cancel_button_); |
| + parent_->OnCancelOperation(file_path_); |
| + } |
| + |
| + DriveDetailedView* parent_; |
| + views::ImageView* status_img_; |
| + views::View* label_container_; |
| + views::ProgressBar* progress_bar_; |
| + views::ImageButton* cancel_button_; |
| + FilePath file_path_; |
| + }; |
|
sadrul
2012/05/01 14:28:58
DISALLOW_COPY_AND_ASSIGN
zel
2012/05/01 16:56:52
Done.
|
| + |
| + struct OperationItem { |
|
xiyuan
2012/05/01 16:47:53
nit: Is this still in use?
zel
2012/05/01 16:56:52
Done.
|
| + OperationItem(RowView* row_view, |
| + views::ImageView* status_img, |
| + views::ProgressBar* progress_bar, |
| + views::ImageButton* cancel_button) |
| + : row_view(row_view), |
| + status_img(status_img), |
| + progress_bar(progress_bar), |
| + cancel_button(cancel_button) { |
| + } |
| + RowView* row_view; |
| + views::ImageView* status_img; |
| + views::ProgressBar* progress_bar; |
| + views::ImageButton* cancel_button; |
| + }; |
| + |
| + void AppendHeaderEntry(const DriveOperationStatusList* list) { |
| + if (header_) |
| + return; |
| + header_ = CreateDetailedHeaderEntry(IDS_ASH_STATUS_TRAY_DRIVE, this); |
| + AddChildView(header_); |
| + } |
| + |
| + SkBitmap* GetImageForState(ash::DriveOperationStatus::OperationState state) { |
| + switch (state) { |
| + case ash::DriveOperationStatus::OPERATION_NOT_STARTED: |
| + case ash::DriveOperationStatus::OPERATION_STARTED: |
| + case ash::DriveOperationStatus::OPERATION_IN_PROGRESS: |
| + case ash::DriveOperationStatus::OPERATION_SUSPENDED: |
| + return in_progress_img_; |
| + case ash::DriveOperationStatus::OPERATION_COMPLETED: |
| + return done_img_; |
| + case ash::DriveOperationStatus::OPERATION_FAILED: |
| + return failed_img_; |
| + } |
| + return failed_img_; |
| + } |
| + |
| + virtual void OnCancelOperation(const FilePath& file_path) { |
| + SystemTrayDelegate* delegate = Shell::GetInstance()->tray_delegate(); |
| + delegate->CancelDriveOperation(file_path); |
| + } |
| + |
| + void AppendOperationList(const DriveOperationStatusList* list) { |
| + views::BoxLayout* layout = NULL; |
| + if (!operations_) { |
| + operations_ = new views::View; |
| + layout = new views::BoxLayout( |
| + views::BoxLayout::kVertical, 0, 0, 1); |
| + operations_->SetLayoutManager(layout); |
| + AddChildView(operations_); |
| + } else { |
| + layout = reinterpret_cast<views::BoxLayout*>( |
| + operations_->GetLayoutManager()); |
|
sadrul
2012/05/01 14:28:58
It doesn't look like layout is used after this. So
zel
2012/05/01 16:56:52
Done.
|
| + } |
| + |
| + // Apply the update. |
| + std::set<FilePath> new_set; |
| + for (DriveOperationStatusList::const_iterator it = list->begin(); |
| + it != list->end(); ++it) { |
| + const DriveOperationStatus& operation = *it; |
| + |
| + new_set.insert(operation.file_path); |
| + std::map<FilePath, RowView*>::iterator existing_item = |
| + update_map_.find(operation.file_path); |
| + |
| + if (existing_item != update_map_.end()) { |
| + existing_item->second->UpdateStatus(this, |
| + operation.state, |
| + operation.progress); |
| + } else { |
| + RowView* row_view = new RowView(this, |
| + operation.state, |
| + operation.progress, |
| + operation.file_path); |
| + |
| + update_map_[operation.file_path] = row_view; |
| + operations_->AddChildView(row_view); |
| + } |
| + } |
| + |
| + // Remove items from the list that haven't been added or modified with this |
| + // update batch. |
| + std::set<FilePath> remove_set; |
| + for (std::map<FilePath, RowView*>::iterator update_iter = |
| + update_map_.begin(); |
| + update_iter != update_map_.end(); ++update_iter) { |
| + if (new_set.find(update_iter->first) == new_set.end()) { |
| + remove_set.insert(update_iter->first); |
| + } |
| + } |
| + |
| + for (std::set<FilePath>::iterator removed_iter = remove_set.begin(); |
| + removed_iter != remove_set.end(); ++removed_iter) { |
| + delete update_map_[*removed_iter]; |
| + update_map_.erase(*removed_iter); |
| + } |
| + } |
| + |
| + void AppendSettings() { |
| + HoverHighlightView* container = new HoverHighlightView(this); |
| + container->set_fixed_height(kTrayPopupItemHeight); |
| + container->AddLabel(ui::ResourceBundle::GetSharedInstance(). |
| + GetLocalizedString(IDS_ASH_STATUS_TRAY_DRIVE_SETTINGS), |
| + gfx::Font::NORMAL); |
| + AddChildView(container); |
| + settings_ = container; |
| + } |
| + |
| + // Overridden from ViewClickListener. |
| + virtual void ClickedOn(views::View* sender) OVERRIDE { |
| + SystemTrayDelegate* delegate = Shell::GetInstance()->tray_delegate(); |
| + if (sender == header_) { |
| + Shell::GetInstance()->tray()->ShowDefaultView(); |
| + } else if (sender == settings_) { |
| + delegate->ShowDriveSettings(); |
| + } |
| + } |
|
xiyuan
2012/05/01 16:47:53
nit: insert an empty line
zel
2012/05/01 16:56:52
Done.
|
| + // Maps operation entries to their file paths. |
| + std::map<FilePath, RowView*> update_map_; |
| + views::View* header_; |
| + views::View* operations_; |
| + views::View* settings_; |
| + SkBitmap* in_progress_img_; |
| + SkBitmap* done_img_; |
| + SkBitmap* failed_img_; |
| + |
| + DISALLOW_COPY_AND_ASSIGN(DriveDetailedView); |
| +}; |
| + |
| +} // namespace tray |
| + |
| +TrayDrive::TrayDrive() : TrayImageItem(IDR_AURA_UBER_TRAY_DRIVE_LIGHT) { |
| +} |
| + |
| +bool TrayDrive::GetInitialVisibility() { |
| + scoped_ptr<DriveOperationStatusList> list(GetCurrentOperationList()); |
|
sadrul
2012/05/01 14:28:58
Is there a chance the tray can show up when logged
zel
2012/05/01 16:56:52
Nope. The user needs to be fully authenticated and
|
| + return list->size() > 0; |
| +} |
| + |
| +TrayDrive::~TrayDrive() { |
|
xiyuan
2012/05/01 16:47:53
nit: move this up to keep the same order as in hea
zel
2012/05/01 16:56:52
Done.
|
| +} |
| + |
| +views::View* TrayDrive::CreateDefaultView(user::LoginStatus status) { |
| + if (status != user::LOGGED_IN_USER && status != user::LOGGED_IN_OWNER) |
| + return NULL; |
| + |
| + scoped_ptr<DriveOperationStatusList> list(GetCurrentOperationList()); |
| + if (!list->size()) |
| + return NULL; |
| + |
| + default_.reset(new tray::DriveDefaultView(this, list.get())); |
| + return default_.get(); |
| +} |
| + |
| +views::View* TrayDrive::CreateDetailedView(user::LoginStatus status) { |
| + if (status != user::LOGGED_IN_USER && status != user::LOGGED_IN_OWNER) |
| + return NULL; |
| + |
| + scoped_ptr<DriveOperationStatusList> list(GetCurrentOperationList()); |
| + if (!list->size()) |
| + return NULL; |
| + |
| + detailed_.reset(new tray::DriveDetailedView(this, list.get())); |
| + return detailed_.get(); |
| +} |
| + |
| +void TrayDrive::DestroyDefaultView() { |
| + default_.reset(); |
| +} |
| + |
| +void TrayDrive::DestroyDetailedView() { |
| + detailed_.reset(); |
| +} |
| + |
| +void TrayDrive::UpdateAfterLoginStatusChange(user::LoginStatus status) { |
| + if (status == user::LOGGED_IN_USER || status == user::LOGGED_IN_OWNER) |
| + return; |
| + |
| + tray_view()->SetVisible(false); |
| + DestroyDefaultView(); |
| + DestroyDetailedView(); |
| +} |
| + |
| +void TrayDrive::OnDriveRefresh(const DriveOperationStatusList& list) { |
| + tray_view()->SetVisible(list.size() > 0); |
| + tray_view()->SchedulePaint(); |
| + |
| + if (default_.get()) |
| + default_->Update(&list); |
| + |
| + if (detailed_.get()) |
| + detailed_->Update(&list); |
| +} |
| + |
| +} // namespace internal |
| +} // namespace ash |