This source file includes following definitions.
- GetTrayLabel
 
- GetCurrentOperationList
 
- Update
 
- failed_img_
 
- Update
 
- GetPreferredSize
 
- operation_id_
 
- UpdateStatus
 
- GetPreferredSize
 
- Layout
 
- ButtonPressed
 
- AppendHeaderEntry
 
- GetImageForState
 
- OnCancelOperation
 
- AppendOperationList
 
- AppendSettings
 
- OnViewClicked
 
- detailed_
 
- GetInitialVisibility
 
- CreateDefaultView
 
- CreateDetailedView
 
- DestroyDefaultView
 
- DestroyDetailedView
 
- UpdateAfterLoginStatusChange
 
- OnDriveJobUpdated
 
- HideIfNoOperations
 
#include "ash/system/drive/tray_drive.h"
#include <vector>
#include "ash/metrics/user_metrics_recorder.h"
#include "ash/shell.h"
#include "ash/system/tray/fixed_sized_scroll_view.h"
#include "ash/system/tray/hover_highlight_view.h"
#include "ash/system/tray/system_tray.h"
#include "ash/system/tray/system_tray_delegate.h"
#include "ash/system/tray/system_tray_notifier.h"
#include "ash/system/tray/tray_constants.h"
#include "ash/system/tray/tray_details_view.h"
#include "ash/system/tray/tray_item_more.h"
#include "ash/system/tray/tray_item_view.h"
#include "base/logging.h"
#include "base/stl_util.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/utf_string_conversions.h"
#include "grit/ash_resources.h"
#include "grit/ash_strings.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/image_view.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 {
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 = 11;
const int64 kHideDelayInMs = 1000;
base::string16 GetTrayLabel(const ash::DriveOperationStatusList& list) {
  return l10n_util::GetStringFUTF16(IDS_ASH_STATUS_TRAY_DRIVE_SYNCING,
      base::IntToString16(static_cast<int>(list.size())));
}
scoped_ptr<ash::DriveOperationStatusList> GetCurrentOperationList() {
  ash::SystemTrayDelegate* delegate =
      ash::Shell::GetInstance()->system_tray_delegate();
  scoped_ptr<ash::DriveOperationStatusList> list(
      new ash::DriveOperationStatusList);
  delegate->GetDriveOperationStatusList(list.get());
  return list.Pass();
}
}
namespace tray {
class DriveDefaultView : public TrayItemMore {
 public:
  DriveDefaultView(SystemTrayItem* owner,
                   const DriveOperationStatusList* list)
      : TrayItemMore(owner, true) {
    ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance();
    SetImage(bundle.GetImageNamed(IDR_AURA_UBER_TRAY_DRIVE).ToImageSkia());
    Update(list);
  }
  virtual ~DriveDefaultView() {}
  void Update(const DriveOperationStatusList* list) {
    DCHECK(list);
    base::string16 label = GetTrayLabel(*list);
    SetLabel(label);
    SetAccessibleName(label);
  }
 private:
  DISALLOW_COPY_AND_ASSIGN(DriveDefaultView);
};
class DriveDetailedView : public TrayDetailsView,
                          public ViewClickListener {
 public:
  DriveDetailedView(SystemTrayItem* owner,
                    const DriveOperationStatusList* list)
      : TrayDetailsView(owner),
        settings_(NULL),
        in_progress_img_(NULL),
        done_img_(NULL),
        failed_img_(NULL) {
    in_progress_img_ = ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
        IDR_AURA_UBER_TRAY_DRIVE);
    done_img_ = ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
        IDR_AURA_UBER_TRAY_DRIVE_DONE);
    failed_img_ = ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
        IDR_AURA_UBER_TRAY_DRIVE_FAILED);
    Update(list);
  }
  virtual ~DriveDetailedView() {
    STLDeleteValues(&update_map_);
  }
  void Update(const DriveOperationStatusList* list) {
    AppendOperationList(list);
    AppendSettings();
    AppendHeaderEntry(list);
    SchedulePaint();
  }
 private:
  class OperationProgressBar : public views::ProgressBar {
   public:
    OperationProgressBar() {}
   private:
    
    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 base::FilePath& file_path,
            int32 operation_id)
        : HoverHighlightView(parent),
          container_(parent),
          status_img_(NULL),
          label_container_(NULL),
          progress_bar_(NULL),
          cancel_button_(NULL),
          operation_id_(operation_id) {
      
      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)
      base::string16 file_label =
          base::UTF8ToUTF16(file_path.BaseName().value());
#elif defined(OS_WIN)
      base::string16 file_label =
          base::WideToUTF16(file_path.BaseName().value());
#endif
      views::Label* label = new views::Label(file_label);
      label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
      label_container_->AddChildView(label);
      
      progress_bar_ = new OperationProgressBar();
      label_container_->AddChildView(progress_bar_);
      AddChildView(label_container_);
      cancel_button_ = new views::ImageButton(this);
      cancel_button_->SetImage(views::ImageButton::STATE_NORMAL,
          ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
              IDR_AURA_UBER_TRAY_DRIVE_CANCEL));
      cancel_button_->SetImage(views::ImageButton::STATE_HOVERED,
          ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
              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_NOT_STARTED ||
          state == ash::DriveOperationStatus::OPERATION_IN_PROGRESS);
    }
   private:
    
    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(
          gfx::IntersectRects(bounds_status, 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(
          gfx::IntersectRects(bounds_label, 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(
          gfx::IntersectRects(bounds_button, child_area));
    }
    
    virtual void ButtonPressed(views::Button* sender,
                               const ui::Event& event) OVERRIDE {
      DCHECK(sender == cancel_button_);
      Shell::GetInstance()->metrics()->RecordUserMetricsAction(
          ash::UMA_STATUS_AREA_DRIVE_CANCEL_OPERATION);
      container_->OnCancelOperation(operation_id_);
    }
    DriveDetailedView* container_;
    views::ImageView* status_img_;
    views::View* label_container_;
    views::ProgressBar* progress_bar_;
    views::ImageButton* cancel_button_;
    int32 operation_id_;
    DISALLOW_COPY_AND_ASSIGN(RowView);
  };
  void AppendHeaderEntry(const DriveOperationStatusList* list) {
    if (footer())
      return;
    CreateSpecialRow(IDS_ASH_STATUS_TRAY_DRIVE, this);
  }
  gfx::ImageSkia* GetImageForState(
      ash::DriveOperationStatus::OperationState state) {
    switch (state) {
      case ash::DriveOperationStatus::OPERATION_NOT_STARTED:
      case ash::DriveOperationStatus::OPERATION_IN_PROGRESS:
        return in_progress_img_;
      case ash::DriveOperationStatus::OPERATION_COMPLETED:
        return done_img_;
      case ash::DriveOperationStatus::OPERATION_FAILED:
        return failed_img_;
    }
    return failed_img_;
  }
  void OnCancelOperation(int32 operation_id) {
    SystemTrayDelegate* delegate = Shell::GetInstance()->system_tray_delegate();
    delegate->CancelDriveOperation(operation_id);
  }
  void AppendOperationList(const DriveOperationStatusList* list) {
    if (!scroller())
      CreateScrollableList();
    
    std::set<base::FilePath> new_set;
    bool item_list_changed = false;
    for (DriveOperationStatusList::const_iterator it = list->begin();
         it != list->end(); ++it) {
      const DriveOperationStatus& operation = *it;
      new_set.insert(operation.file_path);
      std::map<base::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,
                                        operation.id);
        update_map_[operation.file_path] = row_view;
        scroll_content()->AddChildView(row_view);
        item_list_changed = true;
      }
    }
    
    
    std::set<base::FilePath> remove_set;
    for (std::map<base::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<base::FilePath>::iterator removed_iter = remove_set.begin();
        removed_iter != remove_set.end(); ++removed_iter)  {
      delete update_map_[*removed_iter];
      update_map_.erase(*removed_iter);
      item_list_changed = true;
    }
    if (item_list_changed)
      scroller()->Layout();
    
    if (new_set.empty() && GetWidget())
      GetWidget()->Close();
  }
  void AppendSettings() {
    if (settings_)
      return;
    HoverHighlightView* container = new HoverHighlightView(this);
    container->AddLabel(ui::ResourceBundle::GetSharedInstance().
        GetLocalizedString(IDS_ASH_STATUS_TRAY_DRIVE_SETTINGS),
        gfx::Font::NORMAL);
    AddChildView(container);
    settings_ = container;
  }
  
  virtual void OnViewClicked(views::View* sender) OVERRIDE {
    SystemTrayDelegate* delegate = Shell::GetInstance()->system_tray_delegate();
    if (sender == footer()->content()) {
      TransitionToDefaultView();
    } else if (sender == settings_) {
      delegate->ShowDriveSettings();
    }
  }
  
  std::map<base::FilePath, RowView*> update_map_;
  views::View* settings_;
  gfx::ImageSkia* in_progress_img_;
  gfx::ImageSkia* done_img_;
  gfx::ImageSkia* failed_img_;
  DISALLOW_COPY_AND_ASSIGN(DriveDetailedView);
};
}  
TrayDrive::TrayDrive(SystemTray* system_tray) :
    TrayImageItem(system_tray, IDR_AURA_UBER_TRAY_DRIVE_LIGHT),
    default_(NULL),
    detailed_(NULL) {
  Shell::GetInstance()->system_tray_notifier()->AddDriveObserver(this);
}
TrayDrive::~TrayDrive() {
  Shell::GetInstance()->system_tray_notifier()->RemoveDriveObserver(this);
}
bool TrayDrive::GetInitialVisibility() {
  return false;
}
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->empty() && !tray_view()->visible())
    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->empty() && !tray_view()->visible())
    return NULL;
  Shell::GetInstance()->metrics()->RecordUserMetricsAction(
      ash::UMA_STATUS_AREA_DETAILED_DRIVE_VIEW);
  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::OnDriveJobUpdated(const DriveOperationStatus& status) {
  
  
  
  
  
  
  
  
  scoped_ptr<DriveOperationStatusList> list(GetCurrentOperationList());
  bool is_new_item = true;
  for (size_t i = 0; i < list->size(); ++i) {
    if ((*list)[i].id == status.id) {
      (*list)[i] = status;
      is_new_item = false;
      break;
    }
  }
  if (is_new_item)
    list->push_back(status);
  
  bool all_jobs_finished = true;
  for (size_t i = 0; i < list->size(); ++i) {
    if ((*list)[i].state != DriveOperationStatus::OPERATION_COMPLETED &&
        (*list)[i].state != DriveOperationStatus::OPERATION_FAILED) {
      all_jobs_finished = false;
      break;
    }
  }
  if (all_jobs_finished) {
    
    
    
    hide_timer_.Start(FROM_HERE,
                      base::TimeDelta::FromMilliseconds(kHideDelayInMs),
                      this,
                      &TrayDrive::HideIfNoOperations);
    return;
  }
  
  hide_timer_.Stop();
  tray_view()->SetVisible(true);
  if (default_)
    default_->Update(list.get());
  if (detailed_)
    detailed_->Update(list.get());
}
void TrayDrive::HideIfNoOperations() {
  DriveOperationStatusList empty_list;
  tray_view()->SetVisible(false);
  if (default_)
    default_->Update(&empty_list);
  if (detailed_)
    detailed_->Update(&empty_list);
}
}