OLD | NEW |
| (Empty) |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #include "ash/system/drive/tray_drive.h" | |
6 | |
7 #include <vector> | |
8 | |
9 #include "ash/shell.h" | |
10 #include "ash/system/tray/system_tray.h" | |
11 #include "ash/system/tray/system_tray_delegate.h" | |
12 #include "ash/system/tray/tray_constants.h" | |
13 #include "ash/system/tray/tray_item_more.h" | |
14 #include "ash/system/tray/tray_item_view.h" | |
15 #include "ash/system/tray/tray_views.h" | |
16 #include "base/logging.h" | |
17 #include "base/string_number_conversions.h" | |
18 #include "base/utf_string_conversions.h" | |
19 #include "base/stl_util.h" | |
20 #include "grit/ash_strings.h" | |
21 #include "grit/ui_resources.h" | |
22 #include "ui/base/l10n/l10n_util.h" | |
23 #include "ui/base/resource/resource_bundle.h" | |
24 #include "ui/gfx/font.h" | |
25 #include "ui/gfx/image/image.h" | |
26 #include "ui/views/controls/button/image_button.h" | |
27 #include "ui/views/controls/label.h" | |
28 #include "ui/views/controls/progress_bar.h" | |
29 #include "ui/views/layout/box_layout.h" | |
30 #include "ui/views/layout/grid_layout.h" | |
31 #include "ui/views/widget/widget.h" | |
32 | |
33 namespace ash { | |
34 | |
35 namespace internal { | |
36 | |
37 namespace { | |
38 | |
39 const int kSidePadding = 8; | |
40 const int kHorizontalPadding = 6; | |
41 const int kVerticalPadding = 6; | |
42 const int kTopPadding = 6; | |
43 const int kBottomPadding = 10; | |
44 const int kProgressBarWidth = 100; | |
45 const int kProgressBarHeight = 8; | |
46 | |
47 string16 GetTrayLabel(const ash::DriveOperationStatusList& list) { | |
48 return l10n_util::GetStringFUTF16(IDS_ASH_STATUS_TRAY_DRIVE_SYNCING, | |
49 base::IntToString16(static_cast<int>(list.size()))); | |
50 } | |
51 | |
52 ash::DriveOperationStatusList* GetCurrentOperationList() { | |
53 ash::SystemTrayDelegate* delegate = | |
54 ash::Shell::GetInstance()->tray_delegate(); | |
55 ash::DriveOperationStatusList* list = new ash::DriveOperationStatusList(); | |
56 delegate->GetDriveOperationStatusList(list); | |
57 return list; | |
58 } | |
59 | |
60 } | |
61 | |
62 namespace tray { | |
63 | |
64 | |
65 class DriveDefaultView : public TrayItemMore { | |
66 public: | |
67 DriveDefaultView(SystemTrayItem* owner, | |
68 const DriveOperationStatusList* list) | |
69 : TrayItemMore(owner) { | |
70 ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance(); | |
71 | |
72 SetImage(bundle.GetImageNamed(IDR_AURA_UBER_TRAY_DRIVE).ToSkBitmap()); | |
73 Update(list); | |
74 } | |
75 | |
76 virtual ~DriveDefaultView() {} | |
77 | |
78 void Update(const DriveOperationStatusList* list) { | |
79 DCHECK(list); | |
80 string16 label = GetTrayLabel(*list); | |
81 SetLabel(label); | |
82 SetAccessibleName(label); | |
83 } | |
84 | |
85 private: | |
86 DISALLOW_COPY_AND_ASSIGN(DriveDefaultView); | |
87 }; | |
88 | |
89 class DriveDetailedView : public views::View, | |
90 public ViewClickListener { | |
91 public: | |
92 DriveDetailedView(SystemTrayItem* owner, | |
93 const DriveOperationStatusList* list) | |
94 : header_(NULL), | |
95 operations_(NULL), | |
96 settings_(NULL), | |
97 in_progress_img_(NULL), | |
98 done_img_(NULL), | |
99 failed_img_(NULL) { | |
100 SetLayoutManager(new views::BoxLayout( | |
101 views::BoxLayout::kVertical, 0, 0, 0)); | |
102 set_background(views::Background::CreateSolidBackground(kBackgroundColor)); | |
103 | |
104 in_progress_img_ = ResourceBundle::GetSharedInstance().GetBitmapNamed( | |
105 IDR_AURA_UBER_TRAY_DRIVE); | |
106 done_img_ = ResourceBundle::GetSharedInstance().GetBitmapNamed( | |
107 IDR_AURA_UBER_TRAY_DRIVE_DONE); | |
108 failed_img_ = ResourceBundle::GetSharedInstance().GetBitmapNamed( | |
109 IDR_AURA_UBER_TRAY_DRIVE_FAILED); | |
110 | |
111 Update(list); | |
112 } | |
113 | |
114 virtual ~DriveDetailedView() { | |
115 STLDeleteValues(&update_map_); | |
116 } | |
117 | |
118 void Update(const DriveOperationStatusList* list) { | |
119 AppendHeaderEntry(list); | |
120 AppendOperationList(list); | |
121 AppendSettings(); | |
122 PreferredSizeChanged(); | |
123 SchedulePaint(); | |
124 } | |
125 | |
126 private: | |
127 | |
128 class OperationProgressBar : public views::ProgressBar { | |
129 public: | |
130 OperationProgressBar() {} | |
131 private: | |
132 | |
133 // Overridden from View: | |
134 virtual gfx::Size GetPreferredSize() OVERRIDE { | |
135 return gfx::Size(kProgressBarWidth, kProgressBarHeight); | |
136 } | |
137 | |
138 DISALLOW_COPY_AND_ASSIGN(OperationProgressBar); | |
139 }; | |
140 | |
141 class RowView : public HoverHighlightView, | |
142 public views::ButtonListener { | |
143 public: | |
144 RowView(DriveDetailedView* parent, | |
145 ash::DriveOperationStatus::OperationState state, | |
146 double progress, | |
147 const FilePath& file_path) | |
148 : HoverHighlightView(parent), | |
149 container_(parent), | |
150 status_img_(NULL), | |
151 label_container_(NULL), | |
152 progress_bar_(NULL), | |
153 cancel_button_(NULL), | |
154 file_path_(file_path) { | |
155 // Status image. | |
156 status_img_ = new views::ImageView(); | |
157 AddChildView(status_img_); | |
158 | |
159 label_container_ = new views::View(); | |
160 label_container_->SetLayoutManager(new views::BoxLayout( | |
161 views::BoxLayout::kVertical, 0, 0, kVerticalPadding)); | |
162 views::Label* label = new views::Label( | |
163 UTF8ToUTF16(file_path.BaseName().value())); | |
164 label->SetHorizontalAlignment(views::Label::ALIGN_LEFT); | |
165 label_container_->AddChildView(label); | |
166 // Add progress bar. | |
167 progress_bar_ = new OperationProgressBar(); | |
168 label_container_->AddChildView(progress_bar_); | |
169 | |
170 AddChildView(label_container_); | |
171 | |
172 cancel_button_ = new views::ImageButton(this); | |
173 cancel_button_->SetImage(views::ImageButton::BS_NORMAL, | |
174 ResourceBundle::GetSharedInstance().GetBitmapNamed( | |
175 IDR_AURA_UBER_TRAY_DRIVE_CANCEL)); | |
176 cancel_button_->SetImage(views::ImageButton::BS_HOT, | |
177 ResourceBundle::GetSharedInstance().GetBitmapNamed( | |
178 IDR_AURA_UBER_TRAY_DRIVE_CANCEL_HOVER)); | |
179 | |
180 UpdateStatus(state, progress); | |
181 AddChildView(cancel_button_); | |
182 } | |
183 | |
184 void UpdateStatus(ash::DriveOperationStatus::OperationState state, | |
185 double progress) { | |
186 status_img_->SetImage(container_->GetImageForState(state)); | |
187 progress_bar_->SetValue(progress); | |
188 cancel_button_->SetVisible( | |
189 state == ash::DriveOperationStatus::OPERATION_IN_PROGRESS || | |
190 state == ash::DriveOperationStatus::OPERATION_SUSPENDED); | |
191 } | |
192 | |
193 private: | |
194 | |
195 // views::View overrides. | |
196 virtual gfx::Size GetPreferredSize() OVERRIDE { | |
197 return gfx::Size( | |
198 status_img_->GetPreferredSize().width() + | |
199 label_container_->GetPreferredSize().width() + | |
200 cancel_button_->GetPreferredSize().width() + | |
201 2 * kSidePadding + 2 * kHorizontalPadding, | |
202 std::max(status_img_->GetPreferredSize().height(), | |
203 std::max(label_container_->GetPreferredSize().height(), | |
204 cancel_button_->GetPreferredSize().height())) + | |
205 kTopPadding + kBottomPadding); | |
206 } | |
207 | |
208 virtual void Layout() OVERRIDE { | |
209 gfx::Rect child_area(GetLocalBounds()); | |
210 if (child_area.IsEmpty()) | |
211 return; | |
212 | |
213 int pos_x = child_area.x() + kSidePadding; | |
214 int pos_y = child_area.y() + kTopPadding; | |
215 | |
216 gfx::Rect bounds_status( | |
217 gfx::Point(pos_x, | |
218 pos_y + (child_area.height() - kTopPadding - | |
219 kBottomPadding - | |
220 status_img_->GetPreferredSize().height())/2), | |
221 status_img_->GetPreferredSize()); | |
222 status_img_->SetBoundsRect(bounds_status.Intersect(child_area)); | |
223 pos_x += status_img_->bounds().width() + kHorizontalPadding; | |
224 | |
225 gfx::Rect bounds_label(pos_x, | |
226 pos_y, | |
227 child_area.width() - 2 * kSidePadding - | |
228 2 * kHorizontalPadding - | |
229 status_img_->GetPreferredSize().width() - | |
230 cancel_button_->GetPreferredSize().width(), | |
231 label_container_->GetPreferredSize().height()); | |
232 label_container_->SetBoundsRect(bounds_label.Intersect(child_area)); | |
233 pos_x += label_container_->bounds().width() + kHorizontalPadding; | |
234 | |
235 gfx::Rect bounds_button( | |
236 gfx::Point(pos_x, | |
237 pos_y + (child_area.height() - kTopPadding - | |
238 kBottomPadding - | |
239 cancel_button_->GetPreferredSize().height())/2), | |
240 cancel_button_->GetPreferredSize()); | |
241 cancel_button_->SetBoundsRect(bounds_button.Intersect(child_area)); | |
242 } | |
243 | |
244 // views::ButtonListener overrides. | |
245 virtual void ButtonPressed(views::Button* sender, | |
246 const views::Event& event) OVERRIDE { | |
247 DCHECK(sender == cancel_button_); | |
248 container_->OnCancelOperation(file_path_); | |
249 } | |
250 | |
251 DriveDetailedView* container_; | |
252 views::ImageView* status_img_; | |
253 views::View* label_container_; | |
254 views::ProgressBar* progress_bar_; | |
255 views::ImageButton* cancel_button_; | |
256 FilePath file_path_; | |
257 | |
258 DISALLOW_COPY_AND_ASSIGN(RowView); | |
259 }; | |
260 | |
261 void AppendHeaderEntry(const DriveOperationStatusList* list) { | |
262 if (header_) | |
263 return; | |
264 header_ = CreateDetailedHeaderEntry(IDS_ASH_STATUS_TRAY_DRIVE, this); | |
265 AddChildView(header_); | |
266 } | |
267 | |
268 SkBitmap* GetImageForState(ash::DriveOperationStatus::OperationState state) { | |
269 switch (state) { | |
270 case ash::DriveOperationStatus::OPERATION_NOT_STARTED: | |
271 case ash::DriveOperationStatus::OPERATION_STARTED: | |
272 case ash::DriveOperationStatus::OPERATION_IN_PROGRESS: | |
273 case ash::DriveOperationStatus::OPERATION_SUSPENDED: | |
274 return in_progress_img_; | |
275 case ash::DriveOperationStatus::OPERATION_COMPLETED: | |
276 return done_img_; | |
277 case ash::DriveOperationStatus::OPERATION_FAILED: | |
278 return failed_img_; | |
279 } | |
280 return failed_img_; | |
281 } | |
282 | |
283 virtual void OnCancelOperation(const FilePath& file_path) { | |
284 SystemTrayDelegate* delegate = Shell::GetInstance()->tray_delegate(); | |
285 delegate->CancelDriveOperation(file_path); | |
286 } | |
287 | |
288 void AppendOperationList(const DriveOperationStatusList* list) { | |
289 if (!operations_) { | |
290 operations_ = new views::View; | |
291 operations_->SetLayoutManager(new views::BoxLayout( | |
292 views::BoxLayout::kVertical, 0, 0, 1)); | |
293 AddChildView(operations_); | |
294 } | |
295 | |
296 // Apply the update. | |
297 std::set<FilePath> new_set; | |
298 for (DriveOperationStatusList::const_iterator it = list->begin(); | |
299 it != list->end(); ++it) { | |
300 const DriveOperationStatus& operation = *it; | |
301 | |
302 new_set.insert(operation.file_path); | |
303 std::map<FilePath, RowView*>::iterator existing_item = | |
304 update_map_.find(operation.file_path); | |
305 | |
306 if (existing_item != update_map_.end()) { | |
307 existing_item->second->UpdateStatus(operation.state, | |
308 operation.progress); | |
309 } else { | |
310 RowView* row_view = new RowView(this, | |
311 operation.state, | |
312 operation.progress, | |
313 operation.file_path); | |
314 | |
315 update_map_[operation.file_path] = row_view; | |
316 operations_->AddChildView(row_view); | |
317 } | |
318 } | |
319 | |
320 // Remove items from the list that haven't been added or modified with this | |
321 // update batch. | |
322 std::set<FilePath> remove_set; | |
323 for (std::map<FilePath, RowView*>::iterator update_iter = | |
324 update_map_.begin(); | |
325 update_iter != update_map_.end(); ++update_iter) { | |
326 if (new_set.find(update_iter->first) == new_set.end()) { | |
327 remove_set.insert(update_iter->first); | |
328 } | |
329 } | |
330 | |
331 for (std::set<FilePath>::iterator removed_iter = remove_set.begin(); | |
332 removed_iter != remove_set.end(); ++removed_iter) { | |
333 delete update_map_[*removed_iter]; | |
334 update_map_.erase(*removed_iter); | |
335 } | |
336 | |
337 // Close the details if there is really nothing to show there anymore. | |
338 if (new_set.empty()) | |
339 GetWidget()->Close(); | |
340 } | |
341 | |
342 void AppendSettings() { | |
343 if (settings_) | |
344 return; | |
345 | |
346 HoverHighlightView* container = new HoverHighlightView(this); | |
347 container->set_fixed_height(kTrayPopupItemHeight); | |
348 container->AddLabel(ui::ResourceBundle::GetSharedInstance(). | |
349 GetLocalizedString(IDS_ASH_STATUS_TRAY_DRIVE_SETTINGS), | |
350 gfx::Font::NORMAL); | |
351 AddChildView(container); | |
352 settings_ = container; | |
353 } | |
354 | |
355 // Overridden from ViewClickListener. | |
356 virtual void ClickedOn(views::View* sender) OVERRIDE { | |
357 SystemTrayDelegate* delegate = Shell::GetInstance()->tray_delegate(); | |
358 if (sender == header_) { | |
359 Shell::GetInstance()->tray()->ShowDefaultView(); | |
360 } else if (sender == settings_) { | |
361 delegate->ShowDriveSettings(); | |
362 } | |
363 } | |
364 | |
365 // Maps operation entries to their file paths. | |
366 std::map<FilePath, RowView*> update_map_; | |
367 views::View* header_; | |
368 views::View* operations_; | |
369 views::View* settings_; | |
370 SkBitmap* in_progress_img_; | |
371 SkBitmap* done_img_; | |
372 SkBitmap* failed_img_; | |
373 | |
374 DISALLOW_COPY_AND_ASSIGN(DriveDetailedView); | |
375 }; | |
376 | |
377 } // namespace tray | |
378 | |
379 TrayDrive::TrayDrive() : | |
380 TrayImageItem(IDR_AURA_UBER_TRAY_DRIVE_LIGHT), | |
381 default_(NULL), | |
382 detailed_(NULL) { | |
383 } | |
384 | |
385 TrayDrive::~TrayDrive() { | |
386 } | |
387 | |
388 bool TrayDrive::GetInitialVisibility() { | |
389 scoped_ptr<DriveOperationStatusList> list(GetCurrentOperationList()); | |
390 return list->size() > 0; | |
391 } | |
392 | |
393 views::View* TrayDrive::CreateDefaultView(user::LoginStatus status) { | |
394 DCHECK(!default_); | |
395 | |
396 if (status != user::LOGGED_IN_USER && status != user::LOGGED_IN_OWNER) | |
397 return NULL; | |
398 | |
399 scoped_ptr<DriveOperationStatusList> list(GetCurrentOperationList()); | |
400 if (!list->size()) | |
401 return NULL; | |
402 | |
403 default_ = new tray::DriveDefaultView(this, list.get()); | |
404 return default_; | |
405 } | |
406 | |
407 views::View* TrayDrive::CreateDetailedView(user::LoginStatus status) { | |
408 DCHECK(!detailed_); | |
409 | |
410 if (status != user::LOGGED_IN_USER && status != user::LOGGED_IN_OWNER) | |
411 return NULL; | |
412 | |
413 scoped_ptr<DriveOperationStatusList> list(GetCurrentOperationList()); | |
414 if (!list->size()) | |
415 return NULL; | |
416 | |
417 detailed_ = new tray::DriveDetailedView(this, list.get()); | |
418 return detailed_; | |
419 } | |
420 | |
421 void TrayDrive::DestroyDefaultView() { | |
422 default_ = NULL; | |
423 } | |
424 | |
425 void TrayDrive::DestroyDetailedView() { | |
426 detailed_ = NULL; | |
427 } | |
428 | |
429 void TrayDrive::UpdateAfterLoginStatusChange(user::LoginStatus status) { | |
430 if (status == user::LOGGED_IN_USER || status == user::LOGGED_IN_OWNER) | |
431 return; | |
432 | |
433 tray_view()->SetVisible(false); | |
434 DestroyDefaultView(); | |
435 DestroyDetailedView(); | |
436 } | |
437 | |
438 void TrayDrive::OnDriveRefresh(const DriveOperationStatusList& list) { | |
439 tray_view()->SetVisible(list.size() > 0); | |
440 tray_view()->SchedulePaint(); | |
441 | |
442 if (default_) | |
443 default_->Update(&list); | |
444 | |
445 if (detailed_) | |
446 detailed_->Update(&list); | |
447 } | |
448 | |
449 } // namespace internal | |
450 } // namespace ash | |
OLD | NEW |