| OLD | NEW |
| (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 | |
| OLD | NEW |