Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(35)

Side by Side Diff: chrome/browser/download_tab_view.cc

Issue 2826: Move the download code to new directories: (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: Created 12 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « chrome/browser/download_tab_view.h ('k') | chrome/browser/download_uitest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright (c) 2006-2008 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 "chrome/browser/download_tab_view.h"
6
7 #include <time.h>
8
9 #include <algorithm>
10 #include <functional>
11
12 #include "base/file_util.h"
13 #include "base/string_util.h"
14 #include "base/task.h"
15 #include "base/time_format.h"
16 #include "base/timer.h"
17 #include "chrome/app/theme/theme_resources.h"
18 #include "chrome/browser/browser_process.h"
19 #include "chrome/browser/download_manager.h"
20 #include "chrome/browser/download_util.h"
21 #include "chrome/browser/profile.h"
22 #include "chrome/browser/user_metrics.h"
23 #include "chrome/common/gfx/chrome_canvas.h"
24 #include "chrome/common/gfx/chrome_font.h"
25 #include "chrome/common/l10n_util.h"
26 #include "chrome/common/resource_bundle.h"
27 #include "chrome/common/stl_util-inl.h"
28 #include "chrome/common/time_format.h"
29 #include "chrome/views/background.h"
30 #include "googleurl/src/gurl.h"
31 #include "generated_resources.h"
32
33 // Approximate spacing, in pixels, taken from initial UI mock up screens
34 static const int kVerticalPadding = 5;
35 static const int kHorizontalButtonPadding = 15;
36
37 // For vertical and horizontal element spacing
38 static const int kSpacer = 20;
39
40 // Horizontal space between the left edge of the entries and the
41 // left edge of the view.
42 static const int kLeftMargin = 38;
43
44 // x-position of the icon (massage this so it visually matches
45 // kDestinationSearchOffset in native_ui_contents.cc
46 static const int kDownloadIconOffset = 132;
47
48 // Padding between the progress icon and the title, url
49 static const int kInfoPadding = 16;
50
51 // Horizontal distance from the left window edge to the left icon edge
52 static const int kDateSize = 132;
53
54 // Maximum size of the text for the file name or URL
55 static const int kFilenameSize = 350;
56
57 // Maximum size of the progress text during download, which is taken
58 // out of kFilenameSize
59 static const int kProgressSize = 170;
60
61 // Status label color (grey)
62 static const SkColor kStatusColor = SkColorSetRGB(128, 128, 128);
63
64 // URL label color (green)
65 static const SkColor kUrlColor = SkColorSetRGB(0, 128, 0);
66
67 // Paused download indicator (red)
68 static const SkColor kPauseColor = SkColorSetRGB(128, 0, 0);
69
70 // Selected item background color
71 static const SkColor kSelectedItemColor = SkColorSetRGB(215, 232, 255);
72
73 // State key used to identify search text.
74 static const wchar_t kSearchTextKey[] = L"st";
75
76 // Sorting functor for DownloadItem --------------------------------------------
77
78 // Sort DownloadItems into ascending order by their start time.
79 class DownloadItemSorter : public std::binary_function<DownloadItem*,
80 DownloadItem*,
81 bool> {
82 public:
83 bool operator()(const DownloadItem* lhs, const DownloadItem* rhs) {
84 return lhs->start_time() < rhs->start_time();
85 }
86 };
87
88
89 // DownloadItemTabView implementation ------------------------------------------
90 DownloadItemTabView::DownloadItemTabView()
91 : model_(NULL),
92 parent_(NULL) {
93 // Create our element views using empty strings for now,
94 // set them based on the model's state in Layout().
95 since_ = new ChromeViews::Label(L"");
96 ResourceBundle& rb = ResourceBundle::GetSharedInstance();
97 ChromeFont font = rb.GetFont(ResourceBundle::WebFont);
98 since_->SetHorizontalAlignment(ChromeViews::Label::ALIGN_LEFT);
99 since_->SetFont(font);
100 AddChildView(since_);
101
102 date_ = new ChromeViews::Label(L"");
103 date_->SetColor(kStatusColor);
104 date_->SetHorizontalAlignment(ChromeViews::Label::ALIGN_LEFT);
105 date_->SetFont(font);
106 AddChildView(date_);
107
108 // file_name_ is enabled once the download has finished and we can open
109 // it via ShellExecute.
110 file_name_ = new ChromeViews::Link(L"");
111 file_name_->SetHorizontalAlignment(ChromeViews::Label::ALIGN_LEFT);
112 file_name_->SetController(this);
113 file_name_->SetFont(font);
114 AddChildView(file_name_);
115
116 // Set our URL name
117 download_url_ = new ChromeViews::Label(L"");
118 download_url_->SetColor(kUrlColor);
119 download_url_->SetHorizontalAlignment(ChromeViews::Label::ALIGN_LEFT);
120 download_url_->SetFont(font);
121 AddChildView(download_url_);
122
123 // Set our time remaining
124 time_remaining_ = new ChromeViews::Label(L"");
125 time_remaining_->SetColor(kStatusColor);
126 time_remaining_->SetHorizontalAlignment(ChromeViews::Label::ALIGN_LEFT);
127 time_remaining_->SetFont(font);
128 AddChildView(time_remaining_);
129
130 // Set our download progress
131 download_progress_ = new ChromeViews::Label(L"");
132 download_progress_->SetColor(kStatusColor);
133 download_progress_->SetHorizontalAlignment(ChromeViews::Label::ALIGN_LEFT);
134 download_progress_->SetFont(font);
135 AddChildView(download_progress_);
136
137 // Set our 'Pause', 'Cancel' and 'Show in folder' links using
138 // actual strings, since these are constant
139 pause_ = new ChromeViews::Link(l10n_util::GetString(IDS_DOWNLOAD_LINK_PAUSE));
140 pause_->SetController(this);
141 pause_->SetFont(font);
142 AddChildView(pause_);
143
144 cancel_ = new ChromeViews::Link(
145 l10n_util::GetString(IDS_DOWNLOAD_LINK_CANCEL));
146 cancel_->SetController(this);
147 cancel_->SetFont(font);
148 AddChildView(cancel_);
149
150 show_ = new ChromeViews::Link(l10n_util::GetString(IDS_DOWNLOAD_LINK_SHOW));
151 show_->SetController(this);
152 show_->SetFont(font);
153 AddChildView(show_);
154 }
155
156 DownloadItemTabView::~DownloadItemTabView() {
157 }
158
159 void DownloadItemTabView::SetModel(DownloadItem* model,
160 DownloadTabView* parent) {
161 DCHECK(model && parent);
162 model_ = model;
163 parent_ = parent;
164 parent_->LookupIcon(model_);
165 }
166
167 void DownloadItemTabView::GetPreferredSize(CSize* out) {
168 CSize pause_size;
169 pause_->GetPreferredSize(&pause_size);
170 CSize cancel_size;
171 cancel_->GetPreferredSize(&cancel_size);
172 CSize show_size;
173 show_->GetPreferredSize(&show_size);
174
175 out->cx = download_util::kBigProgressIconSize +
176 2 * kSpacer +
177 kHorizontalButtonPadding +
178 kFilenameSize +
179 std::max(pause_size.cx + cancel_size.cx + kHorizontalButtonPadding,
180 show_size.cx);
181
182 out->cy = download_util::kBigProgressIconSize;
183 }
184
185 // Each DownloadItemTabView has reasonably complex layout requirements
186 // that are based on the state of its model. To make the code much simpler
187 // to read, Layout() is split into state specific code which will result
188 // in some redundant code.
189 void DownloadItemTabView::Layout() {
190 DCHECK(model_);
191 switch (model_->state()) {
192 case DownloadItem::COMPLETE:
193 LayoutComplete();
194 break;
195 case DownloadItem::CANCELLED:
196 LayoutCancelled();
197 break;
198 case DownloadItem::IN_PROGRESS:
199 LayoutInProgress();
200 break;
201 case DownloadItem::REMOVING:
202 break;
203 default:
204 NOTREACHED();
205 }
206 }
207
208 // Only display the date if the download is the last that occurred
209 // on a given day.
210 void DownloadItemTabView::LayoutDate() {
211 if (!parent_->ShouldDrawDateForDownload(model_)) {
212 since_->SetVisible(false);
213 date_->SetVisible(false);
214 return;
215 }
216
217 CSize since_size;
218
219 since_->SetText(TimeFormat::RelativeDate(model_->start_time(), NULL));
220 since_->GetPreferredSize(&since_size);
221 since_->SetBounds(kLeftMargin, download_util::kBigProgressIconOffset,
222 kDateSize, since_size.cy);
223 since_->SetVisible(true);
224
225 CSize date_size;
226 date_->SetText(base::TimeFormatShortDate(model_->start_time()));
227 date_->GetPreferredSize(&date_size);
228 date_->SetBounds(kLeftMargin, since_size.cy + kVerticalPadding +
229 download_util::kBigProgressIconOffset,
230 kDateSize, date_size.cy);
231 date_->SetVisible(true);
232 }
233
234 // DownloadItem::COMPLETE state layout
235 void DownloadItemTabView::LayoutComplete() {
236 // Hide unused UI elements
237 pause_->SetVisible(false);
238 pause_->SetEnabled(false);
239 cancel_->SetVisible(false);
240 cancel_->SetEnabled(false);
241 time_remaining_->SetVisible(false);
242 download_progress_->SetVisible(false);
243
244 LayoutDate();
245 int dx = kDownloadIconOffset - download_util::kBigProgressIconOffset +
246 download_util::kBigProgressIconSize + kInfoPadding;
247
248 // File name and URL
249 CSize file_name_size;
250 file_name_->SetText(model_->file_name());
251 file_name_->GetPreferredSize(&file_name_size);
252 file_name_->SetBounds(dx, download_util::kBigProgressIconOffset,
253 std::min(kFilenameSize,
254 static_cast<int>(file_name_size.cx)),
255 file_name_size.cy);
256 file_name_->SetVisible(true);
257 file_name_->SetEnabled(true);
258
259 GURL url(model_->url());
260 download_url_->SetURL(url);
261 CSize url_size;
262 download_url_->GetPreferredSize(&url_size);
263 download_url_->SetBounds(dx,
264 file_name_size.cy + kVerticalPadding +
265 download_util::kBigProgressIconOffset,
266 std::min(kFilenameSize,
267 static_cast<int>(GetWidth() - dx)),
268 url_size.cy);
269 download_url_->SetVisible(true);
270 dx += kFilenameSize + kSpacer;
271
272 // Action button (text is constant and set in constructor)
273 CSize show_size;
274 show_->GetPreferredSize(&show_size);
275 show_->SetBounds(dx, ((file_name_size.cy + url_size.cy) / 2) +
276 download_util::kBigProgressIconOffset,
277 show_size.cx, show_size.cy);
278 show_->SetVisible(true);
279 show_->SetEnabled(true);
280 }
281
282 // DownloadItem::CANCELLED state layout
283 void DownloadItemTabView::LayoutCancelled() {
284 // Hide unused UI elements
285 show_->SetVisible(false);
286 show_->SetEnabled(false);
287 pause_->SetVisible(false);
288 pause_->SetEnabled(false);
289 cancel_->SetVisible(false);
290 cancel_->SetEnabled(false);
291
292 LayoutDate();
293 int dx = kDownloadIconOffset - download_util::kBigProgressIconOffset +
294 download_util::kBigProgressIconSize + kInfoPadding;
295
296 // File name and URL, truncated to show cancelled status
297 CSize file_name_size;
298 file_name_->SetText(model_->file_name());
299 file_name_->GetPreferredSize(&file_name_size);
300 file_name_->SetBounds(dx, download_util::kBigProgressIconOffset,
301 kFilenameSize - kProgressSize - kSpacer,
302 file_name_size.cy);
303 file_name_->SetVisible(true);
304 file_name_->SetEnabled(false);
305
306 GURL url(model_->url());
307 download_url_->SetURL(url);
308 CSize url_size;
309 download_url_->GetPreferredSize(&url_size);
310 download_url_->SetBounds(dx,
311 file_name_size.cy + kVerticalPadding +
312 download_util::kBigProgressIconOffset,
313 std::min(kFilenameSize - kProgressSize - kSpacer,
314 static_cast<int>(GetWidth() - dx)),
315 url_size.cy);
316 download_url_->SetVisible(true);
317
318 dx += kFilenameSize - kProgressSize;
319
320 // Display cancelled status
321 CSize cancel_size;
322 time_remaining_->SetColor(kStatusColor);
323 time_remaining_->SetText(l10n_util::GetString(IDS_DOWNLOAD_TAB_CANCELLED));
324 time_remaining_->GetPreferredSize(&cancel_size);
325 time_remaining_->SetBounds(dx, download_util::kBigProgressIconOffset,
326 kProgressSize, cancel_size.cy);
327 time_remaining_->SetVisible(true);
328
329 // Display received size, we may not know the total size if the server didn't
330 // provide a content-length.
331 int64 total = model_->total_bytes();
332 int64 size = model_->received_bytes();
333 DataUnits amount_units = GetByteDisplayUnits(size);
334 std::wstring received_size = FormatBytes(size, amount_units, true);
335 std::wstring amount = received_size;
336
337 // We don't know which string we'll end up using for constructing the final
338 // progress string so we need to adjust both strings for the locale
339 // direction.
340 std::wstring amount_localized;
341 if (l10n_util::AdjustStringForLocaleDirection(amount, &amount_localized)) {
342 amount.assign(amount_localized);
343 received_size.assign(amount_localized);
344 }
345
346 if (total > 0) {
347 amount_units = GetByteDisplayUnits(total);
348 std::wstring total_text = FormatBytes(total, amount_units, true);
349 std::wstring total_text_localized;
350 if (l10n_util::AdjustStringForLocaleDirection(total_text,
351 &total_text_localized))
352 total_text.assign(total_text_localized);
353
354 // Note that there is no need to adjust the new amount string for the
355 // locale direction as ChromeViews::Label does that for us.
356 amount = l10n_util::GetStringF(IDS_DOWNLOAD_TAB_PROGRESS_SIZE,
357 received_size,
358 total_text);
359 }
360
361 CSize byte_size;
362 download_progress_->SetText(amount);
363 download_progress_->GetPreferredSize(&byte_size);
364 download_progress_->SetBounds(dx,
365 file_name_size.cy + kVerticalPadding +
366 download_util::kBigProgressIconOffset,
367 kProgressSize,
368 byte_size.cy);
369 download_progress_->SetVisible(true);
370 }
371
372 // DownloadItem::IN_PROGRESS state layout
373 void DownloadItemTabView::LayoutInProgress() {
374 // Hide unused UI elements
375 show_->SetVisible(false);
376 show_->SetEnabled(false);
377
378 LayoutDate();
379 int dx = kDownloadIconOffset - download_util::kBigProgressIconOffset +
380 download_util::kBigProgressIconSize +
381 kInfoPadding;
382
383 // File name and URL, truncated to show progress status
384 CSize file_name_size;
385 file_name_->SetText(model_->file_name());
386 file_name_->GetPreferredSize(&file_name_size);
387 file_name_->SetBounds(dx, download_util::kBigProgressIconOffset,
388 kFilenameSize - kProgressSize - kSpacer,
389 file_name_size.cy);
390 file_name_->SetVisible(true);
391 file_name_->SetEnabled(false);
392
393 GURL url(model_->url());
394 download_url_->SetURL(url);
395 CSize url_size;
396 download_url_->GetPreferredSize(&url_size);
397 download_url_->SetBounds(dx, file_name_size.cy + kVerticalPadding +
398 download_util::kBigProgressIconOffset,
399 std::min(kFilenameSize - kProgressSize - kSpacer,
400 static_cast<int>(GetWidth() - dx)),
401 url_size.cy);
402 download_url_->SetVisible(true);
403
404 dx += kFilenameSize - kProgressSize;
405
406 // Set the time remaining and progress display strings. This can
407 // be complicated by not having received the total download size
408 // In that case, we can't calculate time remaining so we just
409 // display speed and received size.
410
411 // Size
412 int64 total = model_->total_bytes();
413 int64 size = model_->received_bytes();
414 DataUnits amount_units = GetByteDisplayUnits(size);
415 std::wstring received_size = FormatBytes(size, amount_units, true);
416 std::wstring amount = received_size;
417
418 // Adjust both strings for the locale direction since we don't yet know which
419 // string we'll end up using for constructing the final progress string.
420 std::wstring amount_localized;
421 if (l10n_util::AdjustStringForLocaleDirection(amount, &amount_localized)) {
422 amount.assign(amount_localized);
423 received_size.assign(amount_localized);
424 }
425
426 if (total > 0) {
427 amount_units = GetByteDisplayUnits(total);
428 std::wstring total_text = FormatBytes(total, amount_units, true);
429 std::wstring total_text_localized;
430 if (l10n_util::AdjustStringForLocaleDirection(total_text,
431 &total_text_localized))
432 total_text.assign(total_text_localized);
433
434 amount = l10n_util::GetStringF(IDS_DOWNLOAD_TAB_PROGRESS_SIZE,
435 received_size,
436 total_text);
437
438 // We adjust the 'amount' string in case we use it as part of the progress
439 // text.
440 if (l10n_util::AdjustStringForLocaleDirection(amount, &amount_localized))
441 amount.assign(amount_localized);
442 }
443
444 // Speed
445 int64 speed = model_->CurrentSpeed();
446 std::wstring progress = amount;
447 if (!model_->is_paused() && speed > 0) {
448 amount_units = GetByteDisplayUnits(speed);
449 std::wstring speed_text = FormatSpeed(speed, amount_units, true);
450 std::wstring speed_text_localized;
451 if (l10n_util::AdjustStringForLocaleDirection(speed_text,
452 &speed_text_localized))
453 speed_text.assign(speed_text_localized);
454
455 progress = l10n_util::GetStringF(IDS_DOWNLOAD_TAB_PROGRESS_SPEED,
456 speed_text,
457 amount);
458
459 // For some reason, the appearance of the dash character ('-') in a string
460 // causes Windows to ignore the 'LRE'/'RLE'/'PDF' Unicode formatting
461 // characters within the string and this causes the string to be displayed
462 // incorrectly on RTL UIs. Therefore, we add the Unicode right-to-left
463 // override character (U+202E) if the locale is RTL in order to fix this
464 // problem.
465 if (l10n_util::GetTextDirection() == l10n_util::RIGHT_TO_LEFT)
466 progress.insert(0, L"\x202E");
467 }
468
469 // Time remaining
470 int y_pos = file_name_size.cy + kVerticalPadding +
471 download_util::kBigProgressIconOffset;
472 CSize time_size;
473 time_remaining_->SetColor(kStatusColor);
474 if (model_->is_paused()) {
475 time_remaining_->SetColor(kPauseColor);
476 time_remaining_->SetText(
477 l10n_util::GetString(IDS_DOWNLOAD_PROGRESS_PAUSED));
478 time_remaining_->GetPreferredSize(&time_size);
479 time_remaining_->SetBounds(dx, download_util::kBigProgressIconOffset,
480 kProgressSize, time_size.cy);
481 time_remaining_->SetVisible(true);
482 } else if (total > 0) {
483 TimeDelta remaining;
484 if (model_->TimeRemaining(&remaining))
485 time_remaining_->SetText(TimeFormat::TimeRemaining(remaining));
486 time_remaining_->GetPreferredSize(&time_size);
487 time_remaining_->SetBounds(dx, download_util::kBigProgressIconOffset,
488 kProgressSize, time_size.cy);
489 time_remaining_->SetVisible(true);
490 } else {
491 time_remaining_->SetText(L"");
492 y_pos = ((file_name_size.cy + url_size.cy) / 2) +
493 download_util::kBigProgressIconOffset;
494 }
495
496 CSize byte_size;
497 download_progress_->SetText(progress);
498 download_progress_->GetPreferredSize(&byte_size);
499 download_progress_->SetBounds(dx, y_pos,
500 kProgressSize, byte_size.cy);
501 download_progress_->SetVisible(true);
502
503 dx += kProgressSize + kSpacer;
504 y_pos = ((file_name_size.cy + url_size.cy) / 2) +
505 download_util::kBigProgressIconOffset;
506
507 // Pause (or Resume) / Cancel buttons.
508 if (model_->is_paused())
509 pause_->SetText(l10n_util::GetString(IDS_DOWNLOAD_LINK_RESUME));
510 else
511 pause_->SetText(l10n_util::GetString(IDS_DOWNLOAD_LINK_PAUSE));
512
513 CSize pause_size;
514 pause_->SetVisible(true);
515 pause_->SetEnabled(true);
516 pause_->GetPreferredSize(&pause_size);
517 pause_->SetBounds(dx, y_pos, pause_size.cx, pause_size.cy);
518
519 dx += pause_size.cx + kHorizontalButtonPadding;
520
521 CSize cancel_size;
522 cancel_->GetPreferredSize(&cancel_size);
523 cancel_->SetBounds(dx, y_pos, cancel_size.cx, cancel_size.cy);
524 cancel_->SetVisible(true);
525 cancel_->SetEnabled(true);
526 }
527
528 void DownloadItemTabView::Paint(ChromeCanvas* canvas) {
529 PaintBackground(canvas);
530
531 if (model_->state() == DownloadItem::IN_PROGRESS) {
532 download_util::PaintDownloadProgress(canvas,
533 this,
534 kDownloadIconOffset -
535 download_util::kBigProgressIconOffset,
536 0,
537 parent_->start_angle(),
538 model_->PercentComplete(),
539 download_util::BIG);
540 }
541
542 // Most of the UI elements in the DownloadItemTabView are represented as
543 // child Views and therefore they get mirrored automatically in
544 // right-to-left UIs. The download item icon is not contained within a child
545 // View so we need to mirror it manually if the locale is RTL.
546 SkBitmap* icon = parent_->LookupIcon(model_);
547 if (icon) {
548 gfx::Rect icon_bounds(kDownloadIconOffset,
549 download_util::kBigProgressIconOffset,
550 icon->width(), icon->height());
551 icon_bounds.set_x(MirroredLeftPointForRect(icon_bounds));
552 canvas->DrawBitmapInt(*icon, icon_bounds.x(), icon_bounds.y());
553 }
554 }
555
556 void DownloadItemTabView::PaintBackground(ChromeCanvas* canvas) {
557 if (parent_->ItemIsSelected(model_)) {
558 // Before we paint the border and the focus rect, we need to mirror the
559 // highlighted area if the View is using a right-to-left UI layout. We need
560 // to explicitly mirror the position because the highlighted area is
561 // directly painted on the canvas (as opposed to being represented as a
562 // child View like the rest of the UI elements in DownloadItemTabView).
563 gfx::Rect highlighted_bounds(kDownloadIconOffset -
564 download_util::kBigProgressIconOffset,
565 0,
566 download_util::kBigProgressIconSize +
567 kInfoPadding + kFilenameSize,
568 download_util::kBigProgressIconSize);
569 highlighted_bounds.set_x(MirroredLeftPointForRect(highlighted_bounds));
570
571 canvas->FillRectInt(kSelectedItemColor,
572 highlighted_bounds.x(),
573 highlighted_bounds.y(),
574 highlighted_bounds.width(),
575 highlighted_bounds.height());
576
577 canvas->DrawFocusRect(highlighted_bounds.x(),
578 highlighted_bounds.y(),
579 highlighted_bounds.width(),
580 highlighted_bounds.height());
581 }
582 }
583
584 void DownloadItemTabView::DidChangeBounds(const CRect& previous,
585 const CRect& current) {
586 Layout();
587 }
588
589 bool DownloadItemTabView::OnMousePressed(const ChromeViews::MouseEvent& event) {
590 CPoint point(event.GetX(), event.GetY());
591
592 // If the click is in the highlight region, then highlight this download.
593 // Otherwise, remove the highlighting from any download.
594 CRect select_rect(kDownloadIconOffset - download_util::kBigProgressIconOffset,
595 0,
596 kDownloadIconOffset -
597 download_util::kBigProgressIconOffset +
598 download_util::kBigProgressIconSize + kInfoPadding +
599 kFilenameSize,
600 download_util::kBigProgressIconSize);
601
602 // The position of the highlighted region does not take into account the
603 // View's UI layout so we have to manually mirror the position if the View is
604 // using a right-to-left UI layout.
605 gfx::Rect mirrored_rect(select_rect);
606 select_rect.MoveToX(MirroredLeftPointForRect(mirrored_rect));
607 if (select_rect.PtInRect(point)) {
608 parent_->ItemBecameSelected(model_);
609
610 if (event.IsRightMouseButton()) {
611 ChromeViews::View::ConvertPointToScreen(this, &point);
612
613 download_util::DownloadDestinationContextMenu menu(
614 model_, GetViewContainer()->GetHWND(), point);
615 }
616 } else {
617 parent_->ItemBecameSelected(NULL);
618 }
619
620 return true;
621 }
622
623 // Handle drag (file copy) operations.
624 bool DownloadItemTabView::OnMouseDragged(const ChromeViews::MouseEvent& event) {
625 if (model_->state() != DownloadItem::COMPLETE)
626 return false;
627
628 CPoint point(event.GetX(), event.GetY());
629
630 // In order to make sure drag and drop works as expected when the UI is
631 // mirrored, we can either flip the mouse X coordinate or flip the X position
632 // of the drag rectangle. Flipping the mouse X coordinate is easier.
633 point.x = MirroredXCoordinateInsideView(point.x);
634 CRect drag_rect(kDownloadIconOffset - download_util::kBigProgressIconOffset,
635 0,
636 kDownloadIconOffset - download_util::kBigProgressIconOffset +
637 download_util::kBigProgressIconSize + kInfoPadding +
638 kFilenameSize,
639 download_util::kBigProgressIconSize);
640
641 if (drag_rect.PtInRect(point)) {
642 SkBitmap* icon = parent_->LookupIcon(model_);
643 if (icon)
644 download_util::DragDownload(model_, icon);
645 }
646
647 return true;
648 }
649
650 void DownloadItemTabView::LinkActivated(ChromeViews::Link* source,
651 int event_flags) {
652 // There are several links in our view that could have been clicked:
653 if (source == file_name_) {
654 ChromeViews::ViewContainer* container = this->GetViewContainer();
655 HWND parent_window = container ? container->GetHWND() : NULL;
656 model_->manager()->OpenDownloadInShell(model_, parent_window);
657 } else if (source == pause_) {
658 model_->TogglePause();
659 } else if (source == cancel_) {
660 model_->Cancel(true /* update history service */);
661 } else if (source == show_) {
662 model_->manager()->ShowDownloadInShell(model_);
663 } else {
664 NOTREACHED();
665 }
666
667 parent_->ItemBecameSelected(model_);
668 }
669
670
671 // DownloadTabView implementation ----------------------------------------------
672
673 DownloadTabView::DownloadTabView(DownloadManager* model)
674 : model_(model),
675 start_angle_(download_util::kStartAngleDegrees),
676 scroll_helper_(kSpacer, download_util::kBigProgressIconSize + kSpacer),
677 selected_index_(-1) {
678 DCHECK(model_);
679 }
680
681 DownloadTabView::~DownloadTabView() {
682 StopDownloadProgress();
683 model_->RemoveObserver(this);
684
685 // DownloadManager owns the contents.
686 downloads_.clear();
687 ClearDownloadInProgress();
688
689 icon_consumer_.CancelAllRequests();
690 }
691
692 void DownloadTabView::Initialize() {
693 model_->AddObserver(this);
694 }
695
696 // Start progress animation timers when we get our first (in-progress) download.
697 void DownloadTabView::StartDownloadProgress() {
698 if (progress_timer_.IsRunning())
699 return;
700 progress_timer_.Start(
701 TimeDelta::FromMilliseconds(download_util::kProgressRateMs), this,
702 &DownloadTabView::UpdateDownloadProgress);
703 }
704
705 // Stop progress animation when there are no more in-progress downloads.
706 void DownloadTabView::StopDownloadProgress() {
707 progress_timer_.Stop();
708 }
709
710 // Update our animations.
711 void DownloadTabView::UpdateDownloadProgress() {
712 start_angle_ = (start_angle_ + download_util::kUnknownIncrementDegrees) %
713 download_util::kMaxDegrees;
714 SchedulePaint();
715 }
716
717 void DownloadTabView::DidChangeBounds(const CRect& previous,
718 const CRect& current) {
719 Layout();
720 }
721
722 void DownloadTabView::Layout() {
723 CRect r;
724 DetachAllFloatingViews();
725 View* v = GetParent();
726 if (v) {
727 v->GetLocalBounds(&r, true);
728 int x = GetX();
729 int y = GetY();
730 int w = v->GetWidth();
731 int h = static_cast<int>(downloads_.size()) *
732 (download_util::kBigProgressIconSize + kSpacer) + kSpacer;
733 SetBounds(x, y, w, h);
734 }
735 }
736
737 // Paint our scrolled region
738 void DownloadTabView::Paint(ChromeCanvas* canvas) {
739 ChromeViews::View::Paint(canvas);
740
741 if (download_util::kBigIconSize == 0 || downloads_.size() == 0)
742 return;
743
744 SkRect clip;
745 if (canvas->getClipBounds(&clip)) {
746 int row_start = (SkScalarRound(clip.fTop) - kSpacer) /
747 (download_util::kBigProgressIconSize + kSpacer);
748 int row_stop = SkScalarRound(clip.fBottom) /
749 (download_util::kBigProgressIconSize + kSpacer);
750 SkRect download_rect;
751 for (int i = row_start; i <= row_stop; ++i) {
752 int y = i * (download_util::kBigProgressIconSize + kSpacer) + kSpacer;
753 if (HasFloatingViewForPoint(0, y))
754 continue;
755 download_rect.set(SkIntToScalar(0),
756 SkIntToScalar(y),
757 SkIntToScalar(GetWidth()),
758 SkIntToScalar(y + download_util::kBigProgressIconSize));
759 if (SkRect::Intersects(clip, download_rect)) {
760 // The DownloadManager stores downloads earliest first, but this
761 // view displays latest first, so adjust the index:
762 int index = static_cast<int>(downloads_.size()) - 1 - i;
763 download_renderer_.SetModel(downloads_[index], this);
764 PaintFloatingView(canvas, &download_renderer_,
765 0, y,
766 GetWidth(), download_util::kBigProgressIconSize);
767 }
768 }
769 }
770 }
771
772 // Draw the DownloadItemTabView for the current position.
773 bool DownloadTabView::GetFloatingViewIDForPoint(int x, int y, int* id) {
774 if (y < kSpacer ||
775 y > (kSpacer + download_util::kBigProgressIconSize) *
776 static_cast<int>(downloads_.size()))
777 return false;
778
779 // Are we hovering over a download or the spacer? If we're over the
780 // download, create a floating view for it.
781 if ((y - kSpacer) % (download_util::kBigProgressIconSize + kSpacer) <
782 download_util::kBigProgressIconSize) {
783 int row = y / (download_util::kBigProgressIconSize + kSpacer);
784 *id = static_cast<int>(downloads_.size()) - 1 - row;
785 return true;
786 }
787 return false;
788 }
789
790 ChromeViews::View* DownloadTabView::CreateFloatingViewForIndex(int index) {
791 if (index >= static_cast<int>(downloads_.size())) {
792 // It's possible that the downloads have been cleared via the "Clear
793 // Browsing Data" command, so this index is gone.
794 return NULL;
795 }
796
797 DownloadItemTabView* dl = new DownloadItemTabView();
798 dl->SetModel(downloads_[index], this);
799 int row = static_cast<int>(downloads_.size()) - 1 - index;
800 int y_pos = row * (download_util::kBigProgressIconSize + kSpacer) + kSpacer;
801 dl->SetBounds(0, y_pos, GetWidth(), download_util::kBigProgressIconSize);
802 dl->Layout();
803 AttachFloatingView(dl, index);
804 return dl;
805 }
806
807 bool DownloadTabView::EnumerateFloatingViews(
808 ChromeViews::View::FloatingViewPosition position,
809 int starting_id, int* id) {
810 DCHECK(id);
811 return View::EnumerateFloatingViewsForInterval(
812 0, static_cast<int>(downloads_.size()), false, position, starting_id, id);
813 }
814
815 ChromeViews::View* DownloadTabView::ValidateFloatingViewForID(int id) {
816 return CreateFloatingViewForIndex(id);
817 }
818
819 void DownloadTabView::OnDownloadUpdated(DownloadItem* download) {
820 switch (download->state()) {
821 case DownloadItem::COMPLETE:
822 case DownloadItem::CANCELLED: {
823 base::hash_set<DownloadItem*>::iterator d = in_progress_.find(download);
824 if (d != in_progress_.end()) {
825 (*d)->RemoveObserver(this);
826 in_progress_.erase(d);
827 }
828 if (in_progress_.empty())
829 StopDownloadProgress();
830 LoadIcon(download);
831 break;
832 }
833 case DownloadItem::IN_PROGRESS: {
834 // If all IN_PROGRESS downloads are paused, don't waste CPU issuing any
835 // further progress updates until at least one download is active again.
836 if (download->is_paused()) {
837 bool continue_update = false;
838 base::hash_set<DownloadItem*>::iterator it = in_progress_.begin();
839 for (; it != in_progress_.end(); ++it) {
840 if (!(*it)->is_paused()) {
841 continue_update = true;
842 break;
843 }
844 }
845 if (!continue_update)
846 StopDownloadProgress();
847 } else {
848 StartDownloadProgress();
849 }
850 break;
851 }
852 case DownloadItem::REMOVING:
853 // Handled below.
854 break;
855 default:
856 NOTREACHED();
857 break;
858 }
859
860 OrderedDownloads::iterator it = find(downloads_.begin(),
861 downloads_.end(),
862 download);
863 if (it == downloads_.end())
864 return;
865
866 const int index = static_cast<int>(it - downloads_.begin());
867 DownloadItemTabView* view =
868 static_cast<DownloadItemTabView*>(RetrieveFloatingViewForID(index));
869 if (view) {
870 if (download->state() != DownloadItem::REMOVING) {
871 view->Layout();
872 SchedulePaintForViewAtIndex(index);
873 } else if (selected_index_ == index) {
874 selected_index_ = -1;
875 }
876 }
877 }
878
879 // A download has started or been deleted. Query our DownloadManager for the
880 // current set of downloads, which will call us back in SetDownloads once it
881 // has retrieved them.
882 void DownloadTabView::ModelChanged() {
883 downloads_.clear();
884 ClearDownloadInProgress();
885 DetachAllFloatingViews();
886
887 // Issue the query.
888 model_->GetDownloads(this, search_text_);
889 }
890
891 void DownloadTabView::SetDownloads(std::vector<DownloadItem*>& downloads) {
892 // Stop progress timers.
893 StopDownloadProgress();
894
895 // Clear out old state and remove self as observer for each download.
896 downloads_.clear();
897 ClearDownloadInProgress();
898
899 // Swap new downloads in.
900 downloads_.swap(downloads);
901 sort(downloads_.begin(), downloads_.end(), DownloadItemSorter());
902
903 // Scan for any in progress downloads and add ourself to them as an observer.
904 for (OrderedDownloads::iterator it = downloads_.begin();
905 it != downloads_.end(); ++it) {
906 DownloadItem* download = *it;
907 if (download->state() == DownloadItem::IN_PROGRESS) {
908 download->AddObserver(this);
909 in_progress_.insert(download);
910 }
911 }
912
913 // Start any progress timers if required.
914 if (!in_progress_.empty())
915 StartDownloadProgress();
916
917 // Update the UI.
918 selected_index_ = -1;
919 GetParent()->GetParent()->Layout();
920 SchedulePaint();
921 }
922
923
924 // If we have the icon in our cache, then return it. If not, look it up via the
925 // IconManager. Ignore in progress requests (duplicates).
926 SkBitmap* DownloadTabView::LookupIcon(DownloadItem* download) {
927 IconManager* im = g_browser_process->icon_manager();
928 // Fast look up.
929 SkBitmap* icon = im->LookupIcon(download->full_path(), IconLoader::NORMAL);
930
931 // Expensive look up.
932 if (!icon)
933 LoadIcon(download);
934
935 return icon;
936 }
937
938 // Bypass the caches and perform the Icon extraction directly. This is useful in
939 // the case where the download has completed and we want to re-check the file
940 // to see if it has an embedded icon (which we couldn't do at download start).
941 void DownloadTabView::LoadIcon(DownloadItem* download) {
942 IconManager* im = g_browser_process->icon_manager();
943 IconManager::Handle h =
944 im->LoadIcon(download->full_path(), IconLoader::NORMAL,
945 &icon_consumer_,
946 NewCallback(this, &DownloadTabView::OnExtractIconComplete));
947 icon_consumer_.SetClientData(im, h, download);
948 }
949
950 void DownloadTabView::ClearDownloadInProgress() {
951 for (base::hash_set<DownloadItem*>::iterator it = in_progress_.begin();
952 it != in_progress_.end(); ++it)
953 (*it)->RemoveObserver(this);
954 in_progress_.clear();
955 }
956
957 // Check to see if the download is the latest download on a given day.
958 // We use this to determine when to draw the date next to a particular
959 // download view: if the DownloadItem is the latest download on a given
960 // day, the date gets drawn.
961 bool DownloadTabView::ShouldDrawDateForDownload(DownloadItem* download) {
962 DCHECK(download);
963 OrderedDownloads::iterator it = find(downloads_.begin(),
964 downloads_.end(),
965 download);
966 DCHECK(it != downloads_.end());
967 const int index = static_cast<int>(it - downloads_.begin());
968
969 // If download is the last or only download, it draws the date.
970 if (downloads_.size() - 1 == index)
971 return true;
972
973 const DownloadItem* next = downloads_[index + 1];
974
975 Time next_midnight = next->start_time().LocalMidnight();
976 Time curr_midnight = download->start_time().LocalMidnight();
977 if (next_midnight == curr_midnight) {
978 // 'next' happened today: let it draw the date so we don't have to.
979 return false;
980 }
981 return true;
982 }
983
984 int DownloadTabView::GetPageScrollIncrement(
985 ChromeViews::ScrollView* scroll_view, bool is_horizontal,
986 bool is_positive) {
987 return scroll_helper_.GetPageScrollIncrement(scroll_view, is_horizontal,
988 is_positive);
989 }
990
991 int DownloadTabView::GetLineScrollIncrement(
992 ChromeViews::ScrollView* scroll_view, bool is_horizontal,
993 bool is_positive) {
994 return scroll_helper_.GetLineScrollIncrement(scroll_view, is_horizontal,
995 is_positive);
996 }
997
998 void DownloadTabView::ItemBecameSelected(const DownloadItem* download) {
999 int index = -1;
1000 if (download != NULL) {
1001 OrderedDownloads::const_iterator it = find(downloads_.begin(),
1002 downloads_.end(),
1003 download);
1004 DCHECK(it != downloads_.end());
1005 index = static_cast<int>(it - downloads_.begin());
1006 if (index == selected_index_)
1007 return; // Avoid unnecessary paint.
1008 }
1009
1010 if (selected_index_ >= 0)
1011 SchedulePaintForViewAtIndex(selected_index_);
1012 if (index >= 0)
1013 SchedulePaintForViewAtIndex(index);
1014 selected_index_ = index;
1015 }
1016
1017 bool DownloadTabView::ItemIsSelected(DownloadItem* download) {
1018 OrderedDownloads::iterator it = find(downloads_.begin(),
1019 downloads_.end(),
1020 download);
1021 if (it != downloads_.end())
1022 return selected_index_ == static_cast<int>(it - downloads_.begin());
1023 return false;
1024 }
1025
1026 void DownloadTabView::SchedulePaintForViewAtIndex(int index) {
1027 int y = GetYPositionForIndex(index);
1028 SchedulePaint(0, y, GetWidth(), download_util::kBigProgressIconSize);
1029 }
1030
1031 int DownloadTabView::GetYPositionForIndex(int index) {
1032 int row = static_cast<int>(downloads_.size()) - 1 - index;
1033 return row * (download_util::kBigProgressIconSize + kSpacer) + kSpacer;
1034 }
1035
1036 void DownloadTabView::SetSearchText(const std::wstring& search_text) {
1037 search_text_ = search_text;
1038 model_->GetDownloads(this, search_text_);
1039 }
1040
1041 // The 'icon_bitmap' is ignored here, since it is cached by the IconManager.
1042 // When the paint message runs, we'll use the fast IconManager lookup API to
1043 // retrieve it.
1044 void DownloadTabView::OnExtractIconComplete(IconManager::Handle handle,
1045 SkBitmap* icon_bitmap) {
1046 IconManager* im = g_browser_process->icon_manager();
1047 DownloadItem* download = icon_consumer_.GetClientData(im, handle);
1048 OrderedDownloads::iterator it = find(downloads_.begin(),
1049 downloads_.end(),
1050 download);
1051 if (it != downloads_.end()) {
1052 const int index = static_cast<int>(it - downloads_.begin());
1053 SchedulePaintForViewAtIndex(index);
1054 }
1055 }
1056
1057 // DownloadTabUIFactory ------------------------------------------------------
1058
1059 class DownloadTabUIFactory : public NativeUIFactory {
1060 public:
1061 DownloadTabUIFactory() {}
1062 virtual ~DownloadTabUIFactory() {}
1063
1064 virtual NativeUI* CreateNativeUIForURL(const GURL& url,
1065 NativeUIContents* contents) {
1066 return new DownloadTabUI(contents);
1067 }
1068
1069 private:
1070 DISALLOW_EVIL_CONSTRUCTORS(DownloadTabUIFactory);
1071 };
1072
1073 // DownloadTabUI -------------------------------------------------------------
1074
1075 DownloadTabUI::DownloadTabUI(NativeUIContents* contents)
1076 #pragma warning(suppress: 4355) // Okay to pass "this" here.
1077 : searchable_container_(this),
1078 download_tab_view_(NULL),
1079 contents_(contents) {
1080 DownloadManager* dlm = contents_->profile()->GetDownloadManager();
1081 download_tab_view_ = new DownloadTabView(dlm);
1082 searchable_container_.SetContents(download_tab_view_);
1083 download_tab_view_->Initialize();
1084
1085 NotificationService* ns = NotificationService::current();
1086 ns->AddObserver(this, NOTIFY_DOWNLOAD_START,
1087 NotificationService::AllSources());
1088 ns->AddObserver(this, NOTIFY_DOWNLOAD_STOP,
1089 NotificationService::AllSources());
1090
1091 // Spin the throbber if there are active downloads, since we may have been
1092 // created after the NOTIFY_DOWNLOAD_START was sent. If the download manager
1093 // has not been created, don't bother since it will negatively impact start
1094 // up time with history requests.
1095 Profile* profile = contents_->profile();
1096 if (profile &&
1097 profile->HasCreatedDownloadManager() &&
1098 profile->GetDownloadManager()->in_progress_count() > 0)
1099 contents_->SetIsLoading(true, NULL);
1100 }
1101
1102 DownloadTabUI::~DownloadTabUI() {
1103 NotificationService* ns = NotificationService::current();
1104 ns->RemoveObserver(this, NOTIFY_DOWNLOAD_START,
1105 NotificationService::AllSources());
1106 ns->RemoveObserver(this, NOTIFY_DOWNLOAD_STOP,
1107 NotificationService::AllSources());
1108 }
1109
1110 const std::wstring DownloadTabUI::GetTitle() const {
1111 return l10n_util::GetString(IDS_DOWNLOAD_TITLE);
1112 }
1113
1114 const int DownloadTabUI::GetFavIconID() const {
1115 return IDR_DOWNLOADS_FAVICON;
1116 }
1117
1118 const int DownloadTabUI::GetSectionIconID() const {
1119 return IDR_DOWNLOADS_SECTION;
1120 }
1121
1122 const std::wstring DownloadTabUI::GetSearchButtonText() const {
1123 return l10n_util::GetString(IDS_DOWNLOAD_SEARCH_BUTTON);
1124 }
1125
1126 ChromeViews::View* DownloadTabUI::GetView() {
1127 return &searchable_container_;
1128 }
1129
1130 void DownloadTabUI::WillBecomeVisible(NativeUIContents* parent) {
1131 UserMetrics::RecordAction(L"Destination_Downloads", parent->profile());
1132 }
1133
1134 void DownloadTabUI::WillBecomeInvisible(NativeUIContents* parent) {
1135 }
1136
1137 void DownloadTabUI::Navigate(const PageState& state) {
1138 std::wstring search_text;
1139 state.GetProperty(kSearchTextKey, &search_text);
1140 download_tab_view_->SetSearchText(search_text);
1141 searchable_container_.GetSearchField()->SetText(search_text);
1142 }
1143
1144 bool DownloadTabUI::SetInitialFocus() {
1145 searchable_container_.GetSearchField()->RequestFocus();
1146 return true;
1147 }
1148
1149 // static
1150 GURL DownloadTabUI::GetURL() {
1151 std::string spec(NativeUIContents::GetScheme());
1152 spec.append("://downloads");
1153 return GURL(spec);
1154 }
1155
1156 // static
1157 NativeUIFactory* DownloadTabUI::GetNativeUIFactory() {
1158 return new DownloadTabUIFactory();
1159 }
1160
1161 void DownloadTabUI::DoSearch(const std::wstring& new_text) {
1162 download_tab_view_->SetSearchText(new_text);
1163 PageState* page_state = contents_->page_state().Copy();
1164 page_state->SetProperty(kSearchTextKey, new_text);
1165 contents_->SetPageState(page_state);
1166 }
1167
1168 void DownloadTabUI::Observe(NotificationType type,
1169 const NotificationSource& source,
1170 const NotificationDetails& details) {
1171 switch (type) {
1172 case NOTIFY_DOWNLOAD_START:
1173 case NOTIFY_DOWNLOAD_STOP:
1174 DCHECK(profile()->HasCreatedDownloadManager());
1175 contents_->SetIsLoading(
1176 profile()->GetDownloadManager()->in_progress_count() > 0,
1177 NULL);
1178 break;
1179 default:
1180 break;
1181 }
1182 }
1183
OLDNEW
« no previous file with comments | « chrome/browser/download_tab_view.h ('k') | chrome/browser/download_uitest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698