| 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 // Download utility implementation | |
| 6 | |
| 7 #include <string> | |
| 8 | |
| 9 #include "chrome/browser/download_util.h" | |
| 10 | |
| 11 #include "base/base_drag_source.h" | |
| 12 #include "base/file_util.h" | |
| 13 #include "base/gfx/image_operations.h" | |
| 14 #include "chrome/app/theme/theme_resources.h" | |
| 15 #include "chrome/browser/browser_process.h" | |
| 16 #include "chrome/browser/download_manager.h" | |
| 17 #include "chrome/common/clipboard_service.h" | |
| 18 #include "chrome/browser/drag_utils.h" | |
| 19 #include "chrome/common/gfx/chrome_canvas.h" | |
| 20 #include "chrome/common/l10n_util.h" | |
| 21 #include "chrome/common/os_exchange_data.h" | |
| 22 #include "chrome/common/resource_bundle.h" | |
| 23 #include "chrome/views/view.h" | |
| 24 #include "generated_resources.h" | |
| 25 #include "SkPath.h" | |
| 26 #include "SkShader.h" | |
| 27 | |
| 28 namespace download_util { | |
| 29 | |
| 30 // BaseContextMenu ------------------------------------------------------------- | |
| 31 | |
| 32 BaseContextMenu::BaseContextMenu(DownloadItem* download) : download_(download) { | |
| 33 } | |
| 34 | |
| 35 BaseContextMenu::~BaseContextMenu() { | |
| 36 } | |
| 37 | |
| 38 // How many times to cycle the complete animation. This should be an odd number | |
| 39 // so that the animation ends faded out. | |
| 40 static const int kCompleteAnimationCycles = 5; | |
| 41 | |
| 42 bool BaseContextMenu::IsItemChecked(int id) const { | |
| 43 switch (id) { | |
| 44 case OPEN_WHEN_COMPLETE: | |
| 45 return download_->open_when_complete(); | |
| 46 case ALWAYS_OPEN_TYPE: { | |
| 47 const std::wstring extension = | |
| 48 file_util::GetFileExtensionFromPath(download_->full_path()); | |
| 49 return download_->manager()->ShouldOpenFileExtension(extension); | |
| 50 } | |
| 51 } | |
| 52 return false; | |
| 53 } | |
| 54 | |
| 55 bool BaseContextMenu::IsItemDefault(int id) const { | |
| 56 return false; | |
| 57 } | |
| 58 | |
| 59 std::wstring BaseContextMenu::GetLabel(int id) const { | |
| 60 switch (id) { | |
| 61 case SHOW_IN_FOLDER: | |
| 62 return l10n_util::GetString(IDS_DOWNLOAD_LINK_SHOW); | |
| 63 case COPY_LINK: | |
| 64 return l10n_util::GetString(IDS_CONTENT_CONTEXT_COPYLINKLOCATION); | |
| 65 case COPY_PATH: | |
| 66 return l10n_util::GetString(IDS_DOWNLOAD_MENU_COPY_PATH); | |
| 67 case COPY_FILE: | |
| 68 return l10n_util::GetString(IDS_DOWNLOAD_MENU_COPY_FILE); | |
| 69 case OPEN_WHEN_COMPLETE: | |
| 70 if (download_->state() == DownloadItem::IN_PROGRESS) | |
| 71 return l10n_util::GetString(IDS_DOWNLOAD_MENU_OPEN_WHEN_COMPLETE); | |
| 72 return l10n_util::GetString(IDS_DOWNLOAD_MENU_OPEN); | |
| 73 case ALWAYS_OPEN_TYPE: | |
| 74 return l10n_util::GetString(IDS_DOWNLOAD_MENU_ALWAYS_OPEN_TYPE); | |
| 75 case REMOVE_ITEM: | |
| 76 return l10n_util::GetString(IDS_DOWNLOAD_MENU_REMOVE_ITEM); | |
| 77 case CANCEL: | |
| 78 return l10n_util::GetString(IDS_DOWNLOAD_MENU_CANCEL); | |
| 79 default: | |
| 80 NOTREACHED(); | |
| 81 } | |
| 82 return std::wstring(); | |
| 83 } | |
| 84 | |
| 85 bool BaseContextMenu::SupportsCommand(int id) const { | |
| 86 return id > 0 && id < MENU_LAST; | |
| 87 } | |
| 88 | |
| 89 bool BaseContextMenu::IsCommandEnabled(int id) const { | |
| 90 switch (id) { | |
| 91 case SHOW_IN_FOLDER: | |
| 92 case COPY_PATH: | |
| 93 case COPY_FILE: | |
| 94 case OPEN_WHEN_COMPLETE: | |
| 95 return download_->state() != DownloadItem::CANCELLED; | |
| 96 case ALWAYS_OPEN_TYPE: | |
| 97 return CanOpenDownload(download_); | |
| 98 case CANCEL: | |
| 99 return download_->state() == DownloadItem::IN_PROGRESS; | |
| 100 default: | |
| 101 return id > 0 && id < MENU_LAST; | |
| 102 } | |
| 103 } | |
| 104 | |
| 105 void BaseContextMenu::ExecuteCommand(int id) { | |
| 106 ClipboardService* clipboard = g_browser_process->clipboard_service(); | |
| 107 DCHECK(clipboard); | |
| 108 switch (id) { | |
| 109 case SHOW_IN_FOLDER: | |
| 110 download_->manager()->ShowDownloadInShell(download_); | |
| 111 break; | |
| 112 case COPY_LINK: | |
| 113 clipboard->Clear(); | |
| 114 clipboard->WriteText(download_->url()); | |
| 115 break; | |
| 116 case COPY_PATH: | |
| 117 clipboard->Clear(); | |
| 118 clipboard->WriteText(download_->full_path()); | |
| 119 break; | |
| 120 case COPY_FILE: | |
| 121 // TODO(paulg): Move to OSExchangeData when implementing drag and drop? | |
| 122 clipboard->Clear(); | |
| 123 clipboard->WriteFile(download_->full_path()); | |
| 124 break; | |
| 125 case OPEN_WHEN_COMPLETE: | |
| 126 OpenDownload(download_); | |
| 127 break; | |
| 128 case ALWAYS_OPEN_TYPE: { | |
| 129 const std::wstring extension = | |
| 130 file_util::GetFileExtensionFromPath(download_->full_path()); | |
| 131 download_->manager()->OpenFilesOfExtension( | |
| 132 extension, !IsItemChecked(ALWAYS_OPEN_TYPE)); | |
| 133 break; | |
| 134 } | |
| 135 case REMOVE_ITEM: | |
| 136 download_->Remove(); | |
| 137 break; | |
| 138 case CANCEL: | |
| 139 download_->Cancel(true); | |
| 140 break; | |
| 141 default: | |
| 142 NOTREACHED(); | |
| 143 } | |
| 144 } | |
| 145 | |
| 146 // DownloadShelfContextMenu ---------------------------------------------------- | |
| 147 | |
| 148 DownloadShelfContextMenu::DownloadShelfContextMenu( | |
| 149 DownloadItem* download, | |
| 150 HWND window, | |
| 151 DownloadItemView::BaseDownloadItemModel* model, | |
| 152 const CPoint& point) | |
| 153 : BaseContextMenu(download), | |
| 154 model_(model) { | |
| 155 DCHECK(model_); | |
| 156 | |
| 157 // The menu's anchor point is determined based on the UI layout. | |
| 158 Menu::AnchorPoint anchor_point; | |
| 159 if (l10n_util::GetTextDirection() == l10n_util::RIGHT_TO_LEFT) | |
| 160 anchor_point = Menu::TOPRIGHT; | |
| 161 else | |
| 162 anchor_point = Menu::TOPLEFT; | |
| 163 | |
| 164 Menu context_menu(this, anchor_point, window); | |
| 165 if (download->state() == DownloadItem::COMPLETE) | |
| 166 context_menu.AppendMenuItem(OPEN_WHEN_COMPLETE, L"", Menu::NORMAL); | |
| 167 else | |
| 168 context_menu.AppendMenuItem(OPEN_WHEN_COMPLETE, L"", Menu::CHECKBOX); | |
| 169 context_menu.AppendMenuItem(ALWAYS_OPEN_TYPE, L"", Menu::CHECKBOX); | |
| 170 context_menu.AppendSeparator(); | |
| 171 context_menu.AppendMenuItem(SHOW_IN_FOLDER, L"", Menu::NORMAL); | |
| 172 context_menu.AppendSeparator(); | |
| 173 context_menu.AppendMenuItem(CANCEL, L"", Menu::NORMAL); | |
| 174 context_menu.RunMenuAt(point.x, point.y); | |
| 175 } | |
| 176 | |
| 177 DownloadShelfContextMenu::~DownloadShelfContextMenu() { | |
| 178 } | |
| 179 | |
| 180 bool DownloadShelfContextMenu::IsItemDefault(int id) const { | |
| 181 return id == OPEN_WHEN_COMPLETE; | |
| 182 } | |
| 183 | |
| 184 void DownloadShelfContextMenu::ExecuteCommand(int id) { | |
| 185 if (id == CANCEL) | |
| 186 model_->CancelTask(); | |
| 187 else | |
| 188 BaseContextMenu::ExecuteCommand(id); | |
| 189 } | |
| 190 | |
| 191 // DownloadDestinationContextMenu ---------------------------------------------- | |
| 192 | |
| 193 DownloadDestinationContextMenu::DownloadDestinationContextMenu( | |
| 194 DownloadItem* download, | |
| 195 HWND window, | |
| 196 const CPoint& point) | |
| 197 : BaseContextMenu(download) { | |
| 198 // The menu's anchor point is determined based on the UI layout. | |
| 199 Menu::AnchorPoint anchor_point; | |
| 200 if (l10n_util::GetTextDirection() == l10n_util::RIGHT_TO_LEFT) | |
| 201 anchor_point = Menu::TOPRIGHT; | |
| 202 else | |
| 203 anchor_point = Menu::TOPLEFT; | |
| 204 | |
| 205 Menu context_menu(this, anchor_point, window); | |
| 206 context_menu.AppendMenuItem(SHOW_IN_FOLDER, L"", Menu::NORMAL); | |
| 207 context_menu.AppendSeparator(); | |
| 208 context_menu.AppendMenuItem(COPY_LINK, L"", Menu::NORMAL); | |
| 209 context_menu.AppendMenuItem(COPY_PATH, L"", Menu::NORMAL); | |
| 210 context_menu.AppendMenuItem(COPY_FILE, L"", Menu::NORMAL); | |
| 211 context_menu.AppendSeparator(); | |
| 212 context_menu.AppendMenuItem(OPEN_WHEN_COMPLETE, L"", Menu::CHECKBOX); | |
| 213 context_menu.AppendMenuItem(ALWAYS_OPEN_TYPE, L"", Menu::CHECKBOX); | |
| 214 context_menu.AppendSeparator(); | |
| 215 context_menu.AppendMenuItem(REMOVE_ITEM, L"", Menu::NORMAL); | |
| 216 context_menu.RunMenuAt(point.x, point.y); | |
| 217 } | |
| 218 | |
| 219 DownloadDestinationContextMenu::~DownloadDestinationContextMenu() { | |
| 220 } | |
| 221 | |
| 222 // Download opening ------------------------------------------------------------ | |
| 223 | |
| 224 bool CanOpenDownload(DownloadItem* download) { | |
| 225 const std::wstring extension = | |
| 226 file_util::GetFileExtensionFromPath(download->full_path()); | |
| 227 return !download->manager()->IsExecutable(extension); | |
| 228 } | |
| 229 | |
| 230 void OpenDownload(DownloadItem* download) { | |
| 231 if (download->state() == DownloadItem::IN_PROGRESS) | |
| 232 download->set_open_when_complete(!download->open_when_complete()); | |
| 233 else if (download->state() == DownloadItem::COMPLETE) | |
| 234 download->manager()->OpenDownloadInShell(download, NULL); | |
| 235 } | |
| 236 | |
| 237 // Download progress painting -------------------------------------------------- | |
| 238 | |
| 239 // Common bitmaps used for download progress animations. We load them once the | |
| 240 // first time we do a progress paint, then reuse them as they are always the | |
| 241 // same. | |
| 242 SkBitmap* g_foreground_16 = NULL; | |
| 243 SkBitmap* g_background_16 = NULL; | |
| 244 SkBitmap* g_foreground_32 = NULL; | |
| 245 SkBitmap* g_background_32 = NULL; | |
| 246 | |
| 247 void PaintDownloadProgress(ChromeCanvas* canvas, | |
| 248 ChromeViews::View* containing_view, | |
| 249 int origin_x, | |
| 250 int origin_y, | |
| 251 int start_angle, | |
| 252 int percent_done, | |
| 253 PaintDownloadProgressSize size) { | |
| 254 DCHECK(containing_view); | |
| 255 | |
| 256 // Load up our common bitmaps | |
| 257 if (!g_background_16) { | |
| 258 ResourceBundle& rb = ResourceBundle::GetSharedInstance(); | |
| 259 g_foreground_16 = rb.GetBitmapNamed(IDR_DOWNLOAD_PROGRESS_FOREGROUND_16); | |
| 260 g_background_16 = rb.GetBitmapNamed(IDR_DOWNLOAD_PROGRESS_BACKGROUND_16); | |
| 261 g_foreground_32 = rb.GetBitmapNamed(IDR_DOWNLOAD_PROGRESS_FOREGROUND_32); | |
| 262 g_background_32 = rb.GetBitmapNamed(IDR_DOWNLOAD_PROGRESS_BACKGROUND_32); | |
| 263 } | |
| 264 | |
| 265 SkBitmap* background = (size == BIG) ? g_background_32 : g_background_16; | |
| 266 SkBitmap* foreground = (size == BIG) ? g_foreground_32 : g_foreground_16; | |
| 267 | |
| 268 const int kProgressIconSize = (size == BIG) ? kBigProgressIconSize : | |
| 269 kSmallProgressIconSize; | |
| 270 | |
| 271 int height = background->height(); | |
| 272 | |
| 273 // We start by storing the bounds of the background and foreground bitmaps | |
| 274 // so that it is easy to mirror the bounds if the UI layout is RTL. | |
| 275 gfx::Rect background_bounds(origin_x, origin_y, | |
| 276 background->width(), background->height()); | |
| 277 gfx::Rect foreground_bounds(origin_x, origin_y, | |
| 278 foreground->width(), foreground->height()); | |
| 279 | |
| 280 // Mirror the positions if necessary. | |
| 281 int mirrored_x = containing_view->MirroredLeftPointForRect(background_bounds); | |
| 282 background_bounds.set_x(mirrored_x); | |
| 283 mirrored_x = containing_view->MirroredLeftPointForRect(foreground_bounds); | |
| 284 foreground_bounds.set_x(mirrored_x); | |
| 285 | |
| 286 // Draw the background progress image. | |
| 287 SkPaint background_paint; | |
| 288 canvas->DrawBitmapInt(*background, | |
| 289 background_bounds.x(), | |
| 290 background_bounds.y(), | |
| 291 background_paint); | |
| 292 | |
| 293 // Layer the foreground progress image in an arc proportional to the download | |
| 294 // progress. The arc grows clockwise, starting in the midnight position, as | |
| 295 // the download progresses. However, if the download does not have known total | |
| 296 // size (the server didn't give us one), then we just spin an arc around until | |
| 297 // we're done. | |
| 298 float sweep_angle = 0.0; | |
| 299 float start_pos = static_cast<float>(kStartAngleDegrees); | |
| 300 if (percent_done < 0) { | |
| 301 sweep_angle = kUnknownAngleDegrees; | |
| 302 start_pos = static_cast<float>(start_angle); | |
| 303 } else if (percent_done > 0) { | |
| 304 sweep_angle = static_cast<float>(kMaxDegrees / 100.0 * percent_done); | |
| 305 } | |
| 306 | |
| 307 // Set up an arc clipping region for the foreground image. Don't bother using | |
| 308 // a clipping region if it would round to 360 (really 0) degrees, since that | |
| 309 // would eliminate the foreground completely and be quite confusing (it would | |
| 310 // look like 0% complete when it should be almost 100%). | |
| 311 SkPaint foreground_paint; | |
| 312 if (sweep_angle < static_cast<float>(kMaxDegrees - 1)) { | |
| 313 SkRect oval; | |
| 314 oval.set(SkIntToScalar(foreground_bounds.x()), | |
| 315 SkIntToScalar(foreground_bounds.y()), | |
| 316 SkIntToScalar(foreground_bounds.x() + kProgressIconSize), | |
| 317 SkIntToScalar(foreground_bounds.y() + kProgressIconSize)); | |
| 318 SkPath path; | |
| 319 path.arcTo(oval, | |
| 320 SkFloatToScalar(start_pos), | |
| 321 SkFloatToScalar(sweep_angle), false); | |
| 322 path.lineTo(SkIntToScalar(foreground_bounds.x() + kProgressIconSize / 2), | |
| 323 SkIntToScalar(foreground_bounds.y() + kProgressIconSize / 2)); | |
| 324 | |
| 325 SkShader* shader = | |
| 326 SkShader::CreateBitmapShader(*foreground, | |
| 327 SkShader::kClamp_TileMode, | |
| 328 SkShader::kClamp_TileMode); | |
| 329 SkMatrix shader_scale; | |
| 330 shader_scale.setTranslate(SkIntToScalar(foreground_bounds.x()), | |
| 331 SkIntToScalar(foreground_bounds.y())); | |
| 332 shader->setLocalMatrix(shader_scale); | |
| 333 foreground_paint.setShader(shader); | |
| 334 foreground_paint.setAntiAlias(true); | |
| 335 shader->unref(); | |
| 336 canvas->drawPath(path, foreground_paint); | |
| 337 return; | |
| 338 } | |
| 339 | |
| 340 canvas->DrawBitmapInt(*foreground, | |
| 341 foreground_bounds.x(), | |
| 342 foreground_bounds.y(), | |
| 343 foreground_paint); | |
| 344 } | |
| 345 | |
| 346 void PaintDownloadComplete(ChromeCanvas* canvas, | |
| 347 ChromeViews::View* containing_view, | |
| 348 int origin_x, | |
| 349 int origin_y, | |
| 350 double animation_progress, | |
| 351 PaintDownloadProgressSize size) { | |
| 352 DCHECK(containing_view); | |
| 353 | |
| 354 // Load up our common bitmaps. | |
| 355 if (!g_foreground_16) { | |
| 356 ResourceBundle& rb = ResourceBundle::GetSharedInstance(); | |
| 357 g_foreground_16 = rb.GetBitmapNamed(IDR_DOWNLOAD_PROGRESS_FOREGROUND_16); | |
| 358 g_foreground_32 = rb.GetBitmapNamed(IDR_DOWNLOAD_PROGRESS_FOREGROUND_32); | |
| 359 } | |
| 360 | |
| 361 SkBitmap* complete = (size == BIG) ? g_foreground_32 : g_foreground_16; | |
| 362 | |
| 363 // Mirror the positions if necessary. | |
| 364 gfx::Rect complete_bounds(origin_x, origin_y, | |
| 365 complete->width(), complete->height()); | |
| 366 complete_bounds.set_x( | |
| 367 containing_view->MirroredLeftPointForRect(complete_bounds)); | |
| 368 | |
| 369 // Start at full opacity, then loop back and forth five times before ending | |
| 370 // at zero opacity. | |
| 371 static const double PI = 3.141592653589793; | |
| 372 double opacity = sin(animation_progress * PI * kCompleteAnimationCycles + | |
| 373 PI/2) / 2 + 0.5; | |
| 374 | |
| 375 SkRect bounds; | |
| 376 bounds.set(SkIntToScalar(complete_bounds.x()), | |
| 377 SkIntToScalar(complete_bounds.y()), | |
| 378 SkIntToScalar(complete_bounds.x() + complete_bounds.width()), | |
| 379 SkIntToScalar(complete_bounds.y() + complete_bounds.height())); | |
| 380 canvas->saveLayerAlpha(&bounds, | |
| 381 static_cast<int>(255.0 * opacity), | |
| 382 SkCanvas::kARGB_ClipLayer_SaveFlag); | |
| 383 canvas->drawARGB(0, 255, 255, 255, SkPorterDuff::kClear_Mode); | |
| 384 canvas->DrawBitmapInt(*complete, complete_bounds.x(), complete_bounds.y()); | |
| 385 canvas->restore(); | |
| 386 } | |
| 387 | |
| 388 // Download dragging | |
| 389 void DragDownload(const DownloadItem* download, SkBitmap* icon) { | |
| 390 DCHECK(download); | |
| 391 | |
| 392 // Set up our OLE machinery | |
| 393 scoped_refptr<OSExchangeData> data(new OSExchangeData); | |
| 394 if (icon) | |
| 395 drag_utils::CreateDragImageForFile(download->file_name(), icon, data); | |
| 396 data->SetFilename(download->full_path()); | |
| 397 scoped_refptr<BaseDragSource> drag_source(new BaseDragSource); | |
| 398 | |
| 399 // Run the drag and drop loop | |
| 400 DWORD effects; | |
| 401 DoDragDrop(data.get(), drag_source.get(), DROPEFFECT_COPY | DROPEFFECT_LINK, | |
| 402 &effects); | |
| 403 } | |
| 404 | |
| 405 | |
| 406 } // namespace download_util | |
| 407 | |
| OLD | NEW |