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..9da6397a61850bd600bab52aa9bde766286108d3 |
--- /dev/null |
+++ b/ash/system/drive/tray_drive.cc |
@@ -0,0 +1,456 @@ |
+// 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" |
+#include "ui/views/widget/widget.h" |
+ |
+namespace ash { |
+ |
+namespace internal { |
+ |
+namespace { |
+ |
+const int kSidePadding = 8; |
+const int kHorizontalPadding = 6; |
+const int kVerticalPadding = 6; |
+const int kTopPadding = 6; |
+const int kBottomPadding = 10; |
+const int kProgressBarWidth = 100; |
+const int kProgressBarHeight = 8; |
+ |
+string16 GetTrayLabel(const ash::DriveOperationStatusList& list) { |
+ 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 tray { |
+ |
+ |
+class DriveDefaultView : public TrayItemMore { |
+ public: |
+ DriveDefaultView(SystemTrayItem* owner, |
+ 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) { |
+ 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(); |
+ PreferredSizeChanged(); |
+ SchedulePaint(); |
+ } |
+ |
+ private: |
+ |
+ class OperationProgressBar : public views::ProgressBar { |
+ public: |
+ OperationProgressBar() {} |
+ private: |
+ |
+ // Overridden from View: |
+ virtual gfx::Size GetPreferredSize() OVERRIDE { |
+ return gfx::Size(kProgressBarWidth, kProgressBarHeight); |
+ } |
+ |
+ DISALLOW_COPY_AND_ASSIGN(OperationProgressBar); |
+ }; |
+ |
+ class RowView : public HoverHighlightView, |
+ public views::ButtonListener { |
+ public: |
+ RowView(DriveDetailedView* parent, |
+ ash::DriveOperationStatus::OperationState state, |
+ double progress, |
+ const FilePath& file_path) |
+ : HoverHighlightView(parent), |
+ container_(parent), |
+ status_img_(NULL), |
+ label_container_(NULL), |
+ progress_bar_(NULL), |
+ cancel_button_(NULL), |
+ file_path_(file_path) { |
+ // Status image. |
+ status_img_ = new views::ImageView(); |
+ AddChildView(status_img_); |
+ |
+ label_container_ = new views::View(); |
+ label_container_->SetLayoutManager(new views::BoxLayout( |
+ views::BoxLayout::kVertical, 0, 0, kVerticalPadding)); |
+#if defined(OS_POSIX) |
+ string16 file_label = |
+ UTF8ToUTF16(file_path.BaseName().value()); |
+#elif defined(OS_WIN) |
+ string16 file_label = |
+ WideToUTF16(file_path.BaseName().value()); |
+#endif |
+ views::Label* label = new views::Label(file_label); |
+ 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_->SetImage(views::ImageButton::BS_NORMAL, |
+ ResourceBundle::GetSharedInstance().GetBitmapNamed( |
+ IDR_AURA_UBER_TRAY_DRIVE_CANCEL)); |
+ cancel_button_->SetImage(views::ImageButton::BS_HOT, |
+ ResourceBundle::GetSharedInstance().GetBitmapNamed( |
+ IDR_AURA_UBER_TRAY_DRIVE_CANCEL_HOVER)); |
+ |
+ UpdateStatus(state, progress); |
+ AddChildView(cancel_button_); |
+ } |
+ |
+ void UpdateStatus(ash::DriveOperationStatus::OperationState state, |
+ double progress) { |
+ status_img_->SetImage(container_->GetImageForState(state)); |
+ progress_bar_->SetValue(progress); |
+ cancel_button_->SetVisible( |
+ state == ash::DriveOperationStatus::OPERATION_IN_PROGRESS || |
+ state == ash::DriveOperationStatus::OPERATION_SUSPENDED); |
+ } |
+ |
+ private: |
+ |
+ // 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())) + |
+ kTopPadding + kBottomPadding); |
+ } |
+ |
+ 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() + kTopPadding; |
+ |
+ gfx::Rect bounds_status( |
+ gfx::Point(pos_x, |
+ pos_y + (child_area.height() - kTopPadding - |
+ kBottomPadding - |
+ status_img_->GetPreferredSize().height())/2), |
+ status_img_->GetPreferredSize()); |
+ status_img_->SetBoundsRect(bounds_status.Intersect(child_area)); |
+ pos_x += status_img_->bounds().width() + kHorizontalPadding; |
+ |
+ gfx::Rect bounds_label(pos_x, |
+ pos_y, |
+ child_area.width() - 2 * kSidePadding - |
+ 2 * kHorizontalPadding - |
+ status_img_->GetPreferredSize().width() - |
+ cancel_button_->GetPreferredSize().width(), |
+ 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, |
+ pos_y + (child_area.height() - kTopPadding - |
+ kBottomPadding - |
+ cancel_button_->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_); |
+ container_->OnCancelOperation(file_path_); |
+ } |
+ |
+ DriveDetailedView* container_; |
+ views::ImageView* status_img_; |
+ views::View* label_container_; |
+ views::ProgressBar* progress_bar_; |
+ views::ImageButton* cancel_button_; |
+ FilePath file_path_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(RowView); |
+ }; |
+ |
+ 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) { |
+ if (!operations_) { |
+ operations_ = new views::View; |
+ operations_->SetLayoutManager(new views::BoxLayout( |
+ views::BoxLayout::kVertical, 0, 0, 1)); |
+ AddChildView(operations_); |
+ } |
+ |
+ // 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(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); |
+ } |
+ |
+ // Close the details if there is really nothing to show there anymore. |
+ if (new_set.empty()) |
+ GetWidget()->Close(); |
+ } |
+ |
+ void AppendSettings() { |
+ if (settings_) |
+ return; |
+ |
+ 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(); |
+ } |
+ } |
+ |
+ // 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), |
+ default_(NULL), |
+ detailed_(NULL) { |
+} |
+ |
+TrayDrive::~TrayDrive() { |
+} |
+ |
+bool TrayDrive::GetInitialVisibility() { |
+ scoped_ptr<DriveOperationStatusList> list(GetCurrentOperationList()); |
+ return list->size() > 0; |
+} |
+ |
+views::View* TrayDrive::CreateDefaultView(user::LoginStatus status) { |
+ DCHECK(!default_); |
+ |
+ if (status != user::LOGGED_IN_USER && status != user::LOGGED_IN_OWNER) |
+ return NULL; |
+ |
+ scoped_ptr<DriveOperationStatusList> list(GetCurrentOperationList()); |
+ if (!list->size()) |
+ return NULL; |
+ |
+ default_ = new tray::DriveDefaultView(this, list.get()); |
+ return default_; |
+} |
+ |
+views::View* TrayDrive::CreateDetailedView(user::LoginStatus status) { |
+ DCHECK(!detailed_); |
+ |
+ if (status != user::LOGGED_IN_USER && status != user::LOGGED_IN_OWNER) |
+ return NULL; |
+ |
+ scoped_ptr<DriveOperationStatusList> list(GetCurrentOperationList()); |
+ if (!list->size()) |
+ return NULL; |
+ |
+ detailed_ = new tray::DriveDetailedView(this, list.get()); |
+ return detailed_; |
+} |
+ |
+void TrayDrive::DestroyDefaultView() { |
+ default_ = NULL; |
+} |
+ |
+void TrayDrive::DestroyDetailedView() { |
+ detailed_ = NULL; |
+} |
+ |
+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_) |
+ default_->Update(&list); |
+ |
+ if (detailed_) |
+ detailed_->Update(&list); |
+} |
+ |
+} // namespace internal |
+} // namespace ash |