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 |