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

Side by Side Diff: chrome/browser/gtk/download_item_gtk.cc

Issue 6251001: Move chrome/browser/gtk/ to chrome/browser/ui/gtk/... (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/
Patch Set: '' Created 9 years, 11 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/gtk/download_item_gtk.h ('k') | chrome/browser/gtk/download_shelf_gtk.h » ('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) 2010 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/gtk/download_item_gtk.h"
6
7 #include "app/l10n_util.h"
8 #include "app/resource_bundle.h"
9 #include "app/text_elider.h"
10 #include "base/basictypes.h"
11 #include "base/callback.h"
12 #include "base/metrics/histogram.h"
13 #include "base/string_util.h"
14 #include "base/time.h"
15 #include "base/utf_string_conversions.h"
16 #include "chrome/browser/browser_process.h"
17 #include "chrome/browser/download/download_item.h"
18 #include "chrome/browser/download/download_item_model.h"
19 #include "chrome/browser/download/download_manager.h"
20 #include "chrome/browser/download/download_shelf.h"
21 #include "chrome/browser/download/download_util.h"
22 #include "chrome/browser/gtk/custom_drag.h"
23 #include "chrome/browser/gtk/download_shelf_gtk.h"
24 #include "chrome/browser/gtk/gtk_theme_provider.h"
25 #include "chrome/browser/gtk/gtk_util.h"
26 #include "chrome/browser/gtk/menu_gtk.h"
27 #include "chrome/browser/gtk/nine_box.h"
28 #include "chrome/browser/ui/browser.h"
29 #include "chrome/common/notification_service.h"
30 #include "gfx/canvas_skia_paint.h"
31 #include "gfx/color_utils.h"
32 #include "gfx/font.h"
33 #include "gfx/skia_utils_gtk.h"
34 #include "grit/generated_resources.h"
35 #include "grit/theme_resources.h"
36 #include "third_party/skia/include/core/SkBitmap.h"
37 #include "ui/base/animation/slide_animation.h"
38
39 namespace {
40
41 // The width of the |menu_button_| widget. It has to be at least as wide as the
42 // bitmap that we use to draw it, i.e. 16, but can be more.
43 const int kMenuButtonWidth = 16;
44
45 // Padding on left and right of items in dangerous download prompt.
46 const int kDangerousElementPadding = 3;
47
48 // Amount of space we allot to showing the filename. If the filename is too wide
49 // it will be elided.
50 const int kTextWidth = 140;
51
52 // We only cap the size of the tooltip so we don't crash.
53 const int kTooltipMaxWidth = 1000;
54
55 // The minimum width we will ever draw the download item. Used as a lower bound
56 // during animation. This number comes from the width of the images used to
57 // make the download item.
58 const int kMinDownloadItemWidth = download_util::kSmallProgressIconSize;
59
60 // New download item animation speed in milliseconds.
61 const int kNewItemAnimationDurationMs = 800;
62
63 // How long the 'download complete' animation should last for.
64 const int kCompleteAnimationDurationMs = 2500;
65
66 // Width of the body area of the download item.
67 // TODO(estade): get rid of the fudge factor. http://crbug.com/18692
68 const int kBodyWidth = kTextWidth + 50 + download_util::kSmallProgressIconSize;
69
70 // The font size of the text, and that size rounded down to the nearest integer
71 // for the size of the arrow in GTK theme mode.
72 const double kTextSize = 13.4; // 13.4px == 10pt @ 96dpi
73
74 // Darken light-on-dark download status text by 20% before drawing, thus
75 // creating a "muted" version of title text for both dark-on-light and
76 // light-on-dark themes.
77 static const double kDownloadItemLuminanceMod = 0.8;
78
79 } // namespace
80
81 // DownloadShelfContextMenuGtk -------------------------------------------------
82
83 class DownloadShelfContextMenuGtk : public DownloadShelfContextMenu,
84 public MenuGtk::Delegate {
85 public:
86 // The constructor creates the menu and immediately pops it up.
87 // |model| is the download item model associated with this context menu,
88 // |widget| is the button that popped up this context menu, and |e| is
89 // the button press event that caused this menu to be created.
90 DownloadShelfContextMenuGtk(BaseDownloadItemModel* model,
91 DownloadItemGtk* download_item)
92 : DownloadShelfContextMenu(model),
93 download_item_(download_item) {
94 }
95
96 ~DownloadShelfContextMenuGtk() {
97 }
98
99 void Popup(GtkWidget* widget, GdkEvent* event) {
100 // Create the menu if we have not created it yet or we created it for
101 // an in-progress download that has since completed.
102 if (download_->state() == DownloadItem::COMPLETE)
103 menu_.reset(new MenuGtk(this, GetFinishedMenuModel()));
104 else
105 menu_.reset(new MenuGtk(this, GetInProgressMenuModel()));
106 menu_->Popup(widget, event);
107 }
108
109 // MenuGtk::Delegate implementation:
110 virtual void StoppedShowing() {
111 download_item_->menu_showing_ = false;
112 gtk_widget_queue_draw(download_item_->menu_button_);
113 }
114
115 virtual GtkWidget* GetImageForCommandId(int command_id) const {
116 const char* stock = NULL;
117 switch (command_id) {
118 case SHOW_IN_FOLDER:
119 case OPEN_WHEN_COMPLETE:
120 stock = GTK_STOCK_OPEN;
121 break;
122
123 case CANCEL:
124 stock = GTK_STOCK_CANCEL;
125 break;
126
127 case ALWAYS_OPEN_TYPE:
128 case TOGGLE_PAUSE:
129 stock = NULL;
130 }
131
132 return stock ? gtk_image_new_from_stock(stock, GTK_ICON_SIZE_MENU) : NULL;
133 }
134
135 private:
136 // The menu we show on Popup(). We keep a pointer to it for a couple reasons:
137 // * we don't want to have to recreate the menu every time it's popped up.
138 // * we have to keep it in scope for longer than the duration of Popup(), or
139 // completing the user-selected action races against the menu's
140 // destruction.
141 scoped_ptr<MenuGtk> menu_;
142
143 // The download item that created us.
144 DownloadItemGtk* download_item_;
145 };
146
147 // DownloadItemGtk -------------------------------------------------------------
148
149 NineBox* DownloadItemGtk::body_nine_box_normal_ = NULL;
150 NineBox* DownloadItemGtk::body_nine_box_prelight_ = NULL;
151 NineBox* DownloadItemGtk::body_nine_box_active_ = NULL;
152
153 NineBox* DownloadItemGtk::menu_nine_box_normal_ = NULL;
154 NineBox* DownloadItemGtk::menu_nine_box_prelight_ = NULL;
155 NineBox* DownloadItemGtk::menu_nine_box_active_ = NULL;
156
157 NineBox* DownloadItemGtk::dangerous_nine_box_ = NULL;
158
159 DownloadItemGtk::DownloadItemGtk(DownloadShelfGtk* parent_shelf,
160 BaseDownloadItemModel* download_model)
161 : parent_shelf_(parent_shelf),
162 arrow_(NULL),
163 menu_showing_(false),
164 theme_provider_(GtkThemeProvider::GetFrom(
165 parent_shelf->browser()->profile())),
166 progress_angle_(download_util::kStartAngleDegrees),
167 download_model_(download_model),
168 dangerous_prompt_(NULL),
169 dangerous_label_(NULL),
170 icon_small_(NULL),
171 icon_large_(NULL),
172 creation_time_(base::Time::Now()) {
173 LoadIcon();
174
175 body_.Own(gtk_button_new());
176 gtk_widget_set_app_paintable(body_.get(), TRUE);
177 UpdateTooltip();
178
179 g_signal_connect(body_.get(), "expose-event",
180 G_CALLBACK(OnExposeThunk), this);
181 g_signal_connect(body_.get(), "clicked",
182 G_CALLBACK(OnClickThunk), this);
183 GTK_WIDGET_UNSET_FLAGS(body_.get(), GTK_CAN_FOCUS);
184 // Remove internal padding on the button.
185 GtkRcStyle* no_padding_style = gtk_rc_style_new();
186 no_padding_style->xthickness = 0;
187 no_padding_style->ythickness = 0;
188 gtk_widget_modify_style(body_.get(), no_padding_style);
189 g_object_unref(no_padding_style);
190
191 name_label_ = gtk_label_new(NULL);
192
193 UpdateNameLabel();
194
195 status_label_ = gtk_label_new(NULL);
196 g_signal_connect(status_label_, "destroy",
197 G_CALLBACK(gtk_widget_destroyed), &status_label_);
198 // Left align and vertically center the labels.
199 gtk_misc_set_alignment(GTK_MISC(name_label_), 0, 0.5);
200 gtk_misc_set_alignment(GTK_MISC(status_label_), 0, 0.5);
201 // Until we switch to vector graphics, force the font size.
202 gtk_util::ForceFontSizePixels(name_label_, kTextSize);
203 gtk_util::ForceFontSizePixels(status_label_, kTextSize);
204
205 // Stack the labels on top of one another.
206 GtkWidget* text_stack = gtk_vbox_new(FALSE, 0);
207 gtk_box_pack_start(GTK_BOX(text_stack), name_label_, TRUE, TRUE, 0);
208 gtk_box_pack_start(GTK_BOX(text_stack), status_label_, FALSE, FALSE, 0);
209
210 // We use a GtkFixed because we don't want it to have its own window.
211 // This choice of widget is not critically important though.
212 progress_area_.Own(gtk_fixed_new());
213 gtk_widget_set_size_request(progress_area_.get(),
214 download_util::kSmallProgressIconSize,
215 download_util::kSmallProgressIconSize);
216 gtk_widget_set_app_paintable(progress_area_.get(), TRUE);
217 g_signal_connect(progress_area_.get(), "expose-event",
218 G_CALLBACK(OnProgressAreaExposeThunk), this);
219
220 // Put the download progress icon on the left of the labels.
221 GtkWidget* body_hbox = gtk_hbox_new(FALSE, 0);
222 gtk_container_add(GTK_CONTAINER(body_.get()), body_hbox);
223 gtk_box_pack_start(GTK_BOX(body_hbox), progress_area_.get(), FALSE, FALSE, 0);
224 gtk_box_pack_start(GTK_BOX(body_hbox), text_stack, TRUE, TRUE, 0);
225
226 menu_button_ = gtk_button_new();
227 gtk_widget_set_app_paintable(menu_button_, TRUE);
228 GTK_WIDGET_UNSET_FLAGS(menu_button_, GTK_CAN_FOCUS);
229 g_signal_connect(menu_button_, "expose-event",
230 G_CALLBACK(OnExposeThunk), this);
231 g_signal_connect(menu_button_, "button-press-event",
232 G_CALLBACK(OnMenuButtonPressEventThunk), this);
233 g_object_set_data(G_OBJECT(menu_button_), "left-align-popup",
234 reinterpret_cast<void*>(true));
235
236 GtkWidget* shelf_hbox = parent_shelf->GetHBox();
237 hbox_.Own(gtk_hbox_new(FALSE, 0));
238 g_signal_connect(hbox_.get(), "expose-event",
239 G_CALLBACK(OnHboxExposeThunk), this);
240 gtk_box_pack_start(GTK_BOX(hbox_.get()), body_.get(), FALSE, FALSE, 0);
241 gtk_box_pack_start(GTK_BOX(hbox_.get()), menu_button_, FALSE, FALSE, 0);
242 gtk_box_pack_start(GTK_BOX(shelf_hbox), hbox_.get(), FALSE, FALSE, 0);
243 // Insert as the leftmost item.
244 gtk_box_reorder_child(GTK_BOX(shelf_hbox), hbox_.get(), 0);
245
246 get_download()->AddObserver(this);
247
248 new_item_animation_.reset(new ui::SlideAnimation(this));
249 new_item_animation_->SetSlideDuration(kNewItemAnimationDurationMs);
250 gtk_widget_show_all(hbox_.get());
251
252 if (IsDangerous()) {
253 // Hide the download item components for now.
254 gtk_widget_hide(body_.get());
255 gtk_widget_hide(menu_button_);
256
257 // Create an hbox to hold it all.
258 dangerous_hbox_ = gtk_hbox_new(FALSE, kDangerousElementPadding);
259
260 // Add padding at the beginning and end. The hbox will add padding between
261 // the empty labels and the other elements.
262 GtkWidget* empty_label_a = gtk_label_new(NULL);
263 GtkWidget* empty_label_b = gtk_label_new(NULL);
264 gtk_box_pack_start(GTK_BOX(dangerous_hbox_), empty_label_a,
265 FALSE, FALSE, 0);
266 gtk_box_pack_end(GTK_BOX(dangerous_hbox_), empty_label_b,
267 FALSE, FALSE, 0);
268
269 // Create the warning icon.
270 dangerous_image_ = gtk_image_new();
271 gtk_box_pack_start(GTK_BOX(dangerous_hbox_), dangerous_image_,
272 FALSE, FALSE, 0);
273
274 dangerous_label_ = gtk_label_new(NULL);
275 // We pass TRUE, TRUE so that the label will condense to less than its
276 // request when the animation is going on.
277 gtk_box_pack_start(GTK_BOX(dangerous_hbox_), dangerous_label_,
278 TRUE, TRUE, 0);
279
280 // Create the nevermind button.
281 GtkWidget* dangerous_decline = gtk_button_new_with_label(
282 l10n_util::GetStringUTF8(IDS_DISCARD_DOWNLOAD).c_str());
283 g_signal_connect(dangerous_decline, "clicked",
284 G_CALLBACK(OnDangerousDeclineThunk), this);
285 gtk_util::CenterWidgetInHBox(dangerous_hbox_, dangerous_decline, false, 0);
286
287 // Create the ok button.
288 GtkWidget* dangerous_accept = gtk_button_new_with_label(
289 l10n_util::GetStringUTF8(
290 download_model->download()->is_extension_install() ?
291 IDS_CONTINUE_EXTENSION_DOWNLOAD : IDS_SAVE_DOWNLOAD).c_str());
292 g_signal_connect(dangerous_accept, "clicked",
293 G_CALLBACK(OnDangerousAcceptThunk), this);
294 gtk_util::CenterWidgetInHBox(dangerous_hbox_, dangerous_accept, false, 0);
295
296 // Put it in an alignment so that padding will be added on the left and
297 // right.
298 dangerous_prompt_ = gtk_alignment_new(0.0, 0.0, 1.0, 1.0);
299 gtk_alignment_set_padding(GTK_ALIGNMENT(dangerous_prompt_),
300 0, 0, kDangerousElementPadding, kDangerousElementPadding);
301 gtk_container_add(GTK_CONTAINER(dangerous_prompt_), dangerous_hbox_);
302 gtk_box_pack_start(GTK_BOX(hbox_.get()), dangerous_prompt_, FALSE, FALSE,
303 0);
304 gtk_widget_set_app_paintable(dangerous_prompt_, TRUE);
305 gtk_widget_set_redraw_on_allocate(dangerous_prompt_, TRUE);
306 g_signal_connect(dangerous_prompt_, "expose-event",
307 G_CALLBACK(OnDangerousPromptExposeThunk), this);
308 gtk_widget_show_all(dangerous_prompt_);
309 }
310
311 registrar_.Add(this, NotificationType::BROWSER_THEME_CHANGED,
312 NotificationService::AllSources());
313 theme_provider_->InitThemesFor(this);
314
315 // Set the initial width of the widget to be animated.
316 if (IsDangerous()) {
317 gtk_widget_set_size_request(dangerous_hbox_,
318 dangerous_hbox_start_width_, -1);
319 } else {
320 gtk_widget_set_size_request(body_.get(), kMinDownloadItemWidth, -1);
321 }
322
323 new_item_animation_->Show();
324 }
325
326 DownloadItemGtk::~DownloadItemGtk() {
327 icon_consumer_.CancelAllRequests();
328 StopDownloadProgress();
329 get_download()->RemoveObserver(this);
330
331 // We may free some shelf space for showing more download items.
332 parent_shelf_->MaybeShowMoreDownloadItems();
333
334 hbox_.Destroy();
335 progress_area_.Destroy();
336 body_.Destroy();
337
338 // Make sure this widget has been destroyed and the pointer we hold to it
339 // NULLed.
340 DCHECK(!status_label_);
341 }
342
343 void DownloadItemGtk::OnDownloadUpdated(DownloadItem* download) {
344 DCHECK_EQ(download, get_download());
345
346 if (dangerous_prompt_ != NULL &&
347 download->safety_state() == DownloadItem::DANGEROUS_BUT_VALIDATED) {
348 // We have been approved.
349 gtk_widget_show_all(hbox_.get());
350 gtk_widget_destroy(dangerous_prompt_);
351 gtk_widget_set_size_request(body_.get(), kBodyWidth, -1);
352 dangerous_prompt_ = NULL;
353
354 // We may free some shelf space for showing more download items.
355 parent_shelf_->MaybeShowMoreDownloadItems();
356 }
357
358 if (download->GetUserVerifiedFilePath() != icon_filepath_) {
359 // Turns out the file path is "unconfirmed %d.crdownload" for dangerous
360 // downloads. When the download is confirmed, the file is renamed on
361 // another thread, so reload the icon if the download filename changes.
362 LoadIcon();
363
364 UpdateTooltip();
365 }
366
367 switch (download->state()) {
368 case DownloadItem::REMOVING:
369 parent_shelf_->RemoveDownloadItem(this); // This will delete us!
370 return;
371 case DownloadItem::CANCELLED:
372 StopDownloadProgress();
373 gtk_widget_queue_draw(progress_area_.get());
374 break;
375 case DownloadItem::COMPLETE:
376 if (download->auto_opened()) {
377 parent_shelf_->RemoveDownloadItem(this); // This will delete us!
378 return;
379 }
380 StopDownloadProgress();
381
382 // Set up the widget as a drag source.
383 DownloadItemDrag::SetSource(body_.get(), get_download(), icon_large_);
384
385 complete_animation_.reset(new ui::SlideAnimation(this));
386 complete_animation_->SetSlideDuration(kCompleteAnimationDurationMs);
387 complete_animation_->SetTweenType(ui::Tween::LINEAR);
388 complete_animation_->Show();
389 break;
390 case DownloadItem::IN_PROGRESS:
391 get_download()->is_paused() ?
392 StopDownloadProgress() : StartDownloadProgress();
393 break;
394 default:
395 NOTREACHED();
396 }
397
398 // Now update the status label. We may have already removed it; if so, we
399 // do nothing.
400 if (!status_label_) {
401 return;
402 }
403
404 status_text_ = UTF16ToUTF8(download_model_->GetStatusText());
405 // Remove the status text label.
406 if (status_text_.empty()) {
407 gtk_widget_destroy(status_label_);
408 return;
409 }
410
411 UpdateStatusLabel(status_text_);
412 }
413
414 void DownloadItemGtk::AnimationProgressed(const ui::Animation* animation) {
415 if (animation == complete_animation_.get()) {
416 gtk_widget_queue_draw(progress_area_.get());
417 } else {
418 if (IsDangerous()) {
419 int progress = static_cast<int>((dangerous_hbox_full_width_ -
420 dangerous_hbox_start_width_) *
421 new_item_animation_->GetCurrentValue());
422 int showing_width = dangerous_hbox_start_width_ + progress;
423 gtk_widget_set_size_request(dangerous_hbox_, showing_width, -1);
424 } else {
425 DCHECK(animation == new_item_animation_.get());
426 int showing_width = std::max(kMinDownloadItemWidth,
427 static_cast<int>(kBodyWidth *
428 new_item_animation_->GetCurrentValue()));
429 gtk_widget_set_size_request(body_.get(), showing_width, -1);
430 }
431 }
432 }
433
434 void DownloadItemGtk::Observe(NotificationType type,
435 const NotificationSource& source,
436 const NotificationDetails& details) {
437 if (type == NotificationType::BROWSER_THEME_CHANGED) {
438 // Our GtkArrow is only visible in gtk mode. Otherwise, we let the custom
439 // rendering code do whatever it wants.
440 if (theme_provider_->UseGtkTheme()) {
441 if (!arrow_) {
442 arrow_ = gtk_arrow_new(GTK_ARROW_DOWN, GTK_SHADOW_NONE);
443 gtk_widget_set_size_request(arrow_,
444 static_cast<int>(kTextSize),
445 static_cast<int>(kTextSize));
446 gtk_container_add(GTK_CONTAINER(menu_button_), arrow_);
447 }
448
449 gtk_widget_set_size_request(menu_button_, -1, -1);
450 gtk_widget_show(arrow_);
451 } else {
452 InitNineBoxes();
453
454 gtk_widget_set_size_request(menu_button_, kMenuButtonWidth, 0);
455
456 if (arrow_)
457 gtk_widget_hide(arrow_);
458 }
459
460 UpdateNameLabel();
461 UpdateStatusLabel(status_text_);
462 UpdateDangerWarning();
463 }
464 }
465
466 DownloadItem* DownloadItemGtk::get_download() {
467 return download_model_->download();
468 }
469
470 bool DownloadItemGtk::IsDangerous() {
471 return get_download()->safety_state() == DownloadItem::DANGEROUS;
472 }
473
474 // Download progress animation functions.
475
476 void DownloadItemGtk::UpdateDownloadProgress() {
477 progress_angle_ = (progress_angle_ +
478 download_util::kUnknownIncrementDegrees) %
479 download_util::kMaxDegrees;
480 gtk_widget_queue_draw(progress_area_.get());
481 }
482
483 void DownloadItemGtk::StartDownloadProgress() {
484 if (progress_timer_.IsRunning())
485 return;
486 progress_timer_.Start(
487 base::TimeDelta::FromMilliseconds(download_util::kProgressRateMs), this,
488 &DownloadItemGtk::UpdateDownloadProgress);
489 }
490
491 void DownloadItemGtk::StopDownloadProgress() {
492 progress_timer_.Stop();
493 }
494
495 // Icon loading functions.
496
497 void DownloadItemGtk::OnLoadSmallIconComplete(IconManager::Handle handle,
498 SkBitmap* icon_bitmap) {
499 icon_small_ = icon_bitmap;
500 gtk_widget_queue_draw(progress_area_.get());
501 }
502
503 void DownloadItemGtk::OnLoadLargeIconComplete(IconManager::Handle handle,
504 SkBitmap* icon_bitmap) {
505 icon_large_ = icon_bitmap;
506 DownloadItemDrag::SetSource(body_.get(), get_download(), icon_large_);
507 }
508
509 void DownloadItemGtk::LoadIcon() {
510 icon_consumer_.CancelAllRequests();
511 IconManager* im = g_browser_process->icon_manager();
512 icon_filepath_ = get_download()->GetUserVerifiedFilePath();
513 im->LoadIcon(icon_filepath_,
514 IconLoader::SMALL, &icon_consumer_,
515 NewCallback(this, &DownloadItemGtk::OnLoadSmallIconComplete));
516 im->LoadIcon(icon_filepath_,
517 IconLoader::LARGE, &icon_consumer_,
518 NewCallback(this, &DownloadItemGtk::OnLoadLargeIconComplete));
519 }
520
521 void DownloadItemGtk::UpdateTooltip() {
522 string16 elided_filename = gfx::ElideFilename(
523 get_download()->GetFileNameToReportUser(),
524 gfx::Font(), kTooltipMaxWidth);
525 gtk_widget_set_tooltip_text(body_.get(),
526 UTF16ToUTF8(elided_filename).c_str());
527 }
528
529 void DownloadItemGtk::UpdateNameLabel() {
530 // TODO(estade): This is at best an educated guess, since we don't actually
531 // use gfx::Font() to draw the text. This is why we need to add so
532 // much padding when we set the size request. We need to either use gfx::Font
533 // or somehow extend TextElider.
534 string16 elided_filename = gfx::ElideFilename(
535 get_download()->GetFileNameToReportUser(),
536 gfx::Font(), kTextWidth);
537
538 GdkColor color = theme_provider_->GetGdkColor(
539 BrowserThemeProvider::COLOR_BOOKMARK_TEXT);
540 gtk_util::SetLabelColor(name_label_, theme_provider_->UseGtkTheme() ?
541 NULL : &color);
542 gtk_label_set_text(GTK_LABEL(name_label_),
543 UTF16ToUTF8(elided_filename).c_str());
544 }
545
546 void DownloadItemGtk::UpdateStatusLabel(const std::string& status_text) {
547 if (!status_label_)
548 return;
549
550 GdkColor text_color;
551 if (!theme_provider_->UseGtkTheme()) {
552 SkColor color = theme_provider_->GetColor(
553 BrowserThemeProvider::COLOR_BOOKMARK_TEXT);
554 if (color_utils::RelativeLuminance(color) > 0.5) {
555 color = SkColorSetRGB(
556 static_cast<int>(kDownloadItemLuminanceMod *
557 SkColorGetR(color)),
558 static_cast<int>(kDownloadItemLuminanceMod *
559 SkColorGetG(color)),
560 static_cast<int>(kDownloadItemLuminanceMod *
561 SkColorGetB(color)));
562 }
563
564 // Lighten the color by blending it with the download item body color. These
565 // values are taken from IDR_DOWNLOAD_BUTTON.
566 SkColor blend_color = SkColorSetRGB(241, 245, 250);
567 text_color = gfx::SkColorToGdkColor(
568 color_utils::AlphaBlend(blend_color, color, 77));
569 }
570
571 gtk_util::SetLabelColor(status_label_, theme_provider_->UseGtkTheme() ?
572 NULL : &text_color);
573 gtk_label_set_text(GTK_LABEL(status_label_), status_text.c_str());
574 }
575
576 void DownloadItemGtk::UpdateDangerWarning() {
577 if (dangerous_prompt_) {
578 // We create |dangerous_warning| as a wide string so we can more easily
579 // calculate its length in characters.
580 string16 dangerous_warning;
581 if (get_download()->is_extension_install()) {
582 dangerous_warning =
583 l10n_util::GetStringUTF16(IDS_PROMPT_DANGEROUS_DOWNLOAD_EXTENSION);
584 } else {
585 string16 elided_filename = gfx::ElideFilename(
586 get_download()->target_name(), gfx::Font(), kTextWidth);
587
588 dangerous_warning =
589 l10n_util::GetStringFUTF16(IDS_PROMPT_DANGEROUS_DOWNLOAD,
590 elided_filename);
591 }
592
593 if (theme_provider_->UseGtkTheme()) {
594 gtk_image_set_from_stock(GTK_IMAGE(dangerous_image_),
595 GTK_STOCK_DIALOG_WARNING, GTK_ICON_SIZE_SMALL_TOOLBAR);
596
597 gtk_util::SetLabelColor(dangerous_label_, NULL);
598 } else {
599 // Set the warning icon.
600 ResourceBundle& rb = ResourceBundle::GetSharedInstance();
601 GdkPixbuf* download_pixbuf = rb.GetPixbufNamed(IDR_WARNING);
602 gtk_image_set_from_pixbuf(GTK_IMAGE(dangerous_image_), download_pixbuf);
603
604 GdkColor color = theme_provider_->GetGdkColor(
605 BrowserThemeProvider::COLOR_BOOKMARK_TEXT);
606 gtk_util::SetLabelColor(dangerous_label_, &color);
607 }
608
609 gtk_label_set_text(GTK_LABEL(dangerous_label_),
610 UTF16ToUTF8(dangerous_warning).c_str());
611
612 // Until we switch to vector graphics, force the font size.
613 gtk_util::ForceFontSizePixels(dangerous_label_, kTextSize);
614
615 // Get the label width when displaying in one line, and reduce it to 60% to
616 // wrap the label into two lines.
617 gtk_widget_set_size_request(dangerous_label_, -1, -1);
618 gtk_label_set_line_wrap(GTK_LABEL(dangerous_label_), FALSE);
619
620 GtkRequisition req;
621 gtk_widget_size_request(dangerous_label_, &req);
622
623 gint label_width = req.width * 6 / 10;
624 gtk_label_set_line_wrap(GTK_LABEL(dangerous_label_), TRUE);
625 gtk_widget_set_size_request(dangerous_label_, label_width, -1);
626
627 // The width will depend on the text. We must do this each time we possibly
628 // change the label above.
629 gtk_widget_size_request(dangerous_hbox_, &req);
630 dangerous_hbox_full_width_ = req.width;
631 dangerous_hbox_start_width_ = dangerous_hbox_full_width_ - label_width;
632 }
633 }
634
635 // static
636 void DownloadItemGtk::InitNineBoxes() {
637 if (body_nine_box_normal_)
638 return;
639
640 body_nine_box_normal_ = new NineBox(
641 IDR_DOWNLOAD_BUTTON_LEFT_TOP,
642 IDR_DOWNLOAD_BUTTON_CENTER_TOP,
643 IDR_DOWNLOAD_BUTTON_RIGHT_TOP,
644 IDR_DOWNLOAD_BUTTON_LEFT_MIDDLE,
645 IDR_DOWNLOAD_BUTTON_CENTER_MIDDLE,
646 IDR_DOWNLOAD_BUTTON_RIGHT_MIDDLE,
647 IDR_DOWNLOAD_BUTTON_LEFT_BOTTOM,
648 IDR_DOWNLOAD_BUTTON_CENTER_BOTTOM,
649 IDR_DOWNLOAD_BUTTON_RIGHT_BOTTOM);
650
651 body_nine_box_prelight_ = new NineBox(
652 IDR_DOWNLOAD_BUTTON_LEFT_TOP_H,
653 IDR_DOWNLOAD_BUTTON_CENTER_TOP_H,
654 IDR_DOWNLOAD_BUTTON_RIGHT_TOP_H,
655 IDR_DOWNLOAD_BUTTON_LEFT_MIDDLE_H,
656 IDR_DOWNLOAD_BUTTON_CENTER_MIDDLE_H,
657 IDR_DOWNLOAD_BUTTON_RIGHT_MIDDLE_H,
658 IDR_DOWNLOAD_BUTTON_LEFT_BOTTOM_H,
659 IDR_DOWNLOAD_BUTTON_CENTER_BOTTOM_H,
660 IDR_DOWNLOAD_BUTTON_RIGHT_BOTTOM_H);
661
662 body_nine_box_active_ = new NineBox(
663 IDR_DOWNLOAD_BUTTON_LEFT_TOP_P,
664 IDR_DOWNLOAD_BUTTON_CENTER_TOP_P,
665 IDR_DOWNLOAD_BUTTON_RIGHT_TOP_P,
666 IDR_DOWNLOAD_BUTTON_LEFT_MIDDLE_P,
667 IDR_DOWNLOAD_BUTTON_CENTER_MIDDLE_P,
668 IDR_DOWNLOAD_BUTTON_RIGHT_MIDDLE_P,
669 IDR_DOWNLOAD_BUTTON_LEFT_BOTTOM_P,
670 IDR_DOWNLOAD_BUTTON_CENTER_BOTTOM_P,
671 IDR_DOWNLOAD_BUTTON_RIGHT_BOTTOM_P);
672
673 menu_nine_box_normal_ = new NineBox(
674 IDR_DOWNLOAD_BUTTON_MENU_TOP, 0, 0,
675 IDR_DOWNLOAD_BUTTON_MENU_MIDDLE, 0, 0,
676 IDR_DOWNLOAD_BUTTON_MENU_BOTTOM, 0, 0);
677
678 menu_nine_box_prelight_ = new NineBox(
679 IDR_DOWNLOAD_BUTTON_MENU_TOP_H, 0, 0,
680 IDR_DOWNLOAD_BUTTON_MENU_MIDDLE_H, 0, 0,
681 IDR_DOWNLOAD_BUTTON_MENU_BOTTOM_H, 0, 0);
682
683 menu_nine_box_active_ = new NineBox(
684 IDR_DOWNLOAD_BUTTON_MENU_TOP_P, 0, 0,
685 IDR_DOWNLOAD_BUTTON_MENU_MIDDLE_P, 0, 0,
686 IDR_DOWNLOAD_BUTTON_MENU_BOTTOM_P, 0, 0);
687
688 dangerous_nine_box_ = new NineBox(
689 IDR_DOWNLOAD_BUTTON_LEFT_TOP,
690 IDR_DOWNLOAD_BUTTON_CENTER_TOP,
691 IDR_DOWNLOAD_BUTTON_RIGHT_TOP_NO_DD,
692 IDR_DOWNLOAD_BUTTON_LEFT_MIDDLE,
693 IDR_DOWNLOAD_BUTTON_CENTER_MIDDLE,
694 IDR_DOWNLOAD_BUTTON_RIGHT_MIDDLE_NO_DD,
695 IDR_DOWNLOAD_BUTTON_LEFT_BOTTOM,
696 IDR_DOWNLOAD_BUTTON_CENTER_BOTTOM,
697 IDR_DOWNLOAD_BUTTON_RIGHT_BOTTOM_NO_DD);
698 }
699
700 gboolean DownloadItemGtk::OnHboxExpose(GtkWidget* widget, GdkEventExpose* e) {
701 if (theme_provider_->UseGtkTheme()) {
702 int border_width = GTK_CONTAINER(widget)->border_width;
703 int x = widget->allocation.x + border_width;
704 int y = widget->allocation.y + border_width;
705 int width = widget->allocation.width - border_width * 2;
706 int height = widget->allocation.height - border_width * 2;
707
708 if (IsDangerous()) {
709 // Draw a simple frame around the area when we're displaying the warning.
710 gtk_paint_shadow(widget->style, widget->window,
711 static_cast<GtkStateType>(widget->state),
712 static_cast<GtkShadowType>(GTK_SHADOW_OUT),
713 &e->area, widget, "frame",
714 x, y, width, height);
715 } else {
716 // Manually draw the GTK button border around the download item. We draw
717 // the left part of the button (the file), a divider, and then the right
718 // part of the button (the menu). We can't draw a button on top of each
719 // other (*cough*Clearlooks*cough*) so instead, to draw the left part of
720 // the button, we instruct GTK to draw the entire button...with a
721 // doctored clip rectangle to the left part of the button sans
722 // separator. We then repeat this for the right button.
723 GtkStyle* style = body_.get()->style;
724
725 GtkAllocation left_allocation = body_.get()->allocation;
726 GdkRectangle left_clip = {
727 left_allocation.x, left_allocation.y,
728 left_allocation.width, left_allocation.height
729 };
730
731 GtkAllocation right_allocation = menu_button_->allocation;
732 GdkRectangle right_clip = {
733 right_allocation.x, right_allocation.y,
734 right_allocation.width, right_allocation.height
735 };
736
737 GtkShadowType body_shadow =
738 GTK_BUTTON(body_.get())->depressed ? GTK_SHADOW_IN : GTK_SHADOW_OUT;
739 gtk_paint_box(style, widget->window,
740 static_cast<GtkStateType>(GTK_WIDGET_STATE(body_.get())),
741 body_shadow,
742 &left_clip, widget, "button",
743 x, y, width, height);
744
745 GtkShadowType menu_shadow =
746 GTK_BUTTON(menu_button_)->depressed ? GTK_SHADOW_IN : GTK_SHADOW_OUT;
747 gtk_paint_box(style, widget->window,
748 static_cast<GtkStateType>(GTK_WIDGET_STATE(menu_button_)),
749 menu_shadow,
750 &right_clip, widget, "button",
751 x, y, width, height);
752
753 // Doing the math to reverse engineer where we should be drawing our line
754 // is hard and relies on copying GTK internals, so instead steal the
755 // allocation of the gtk arrow which is close enough (and will error on
756 // the conservative side).
757 GtkAllocation arrow_allocation = arrow_->allocation;
758 gtk_paint_vline(style, widget->window,
759 static_cast<GtkStateType>(GTK_WIDGET_STATE(widget)),
760 &e->area, widget, "button",
761 arrow_allocation.y,
762 arrow_allocation.y + arrow_allocation.height,
763 left_allocation.x + left_allocation.width);
764 }
765 }
766 return FALSE;
767 }
768
769 gboolean DownloadItemGtk::OnExpose(GtkWidget* widget, GdkEventExpose* e) {
770 if (!theme_provider_->UseGtkTheme()) {
771 bool is_body = widget == body_.get();
772
773 NineBox* nine_box = NULL;
774 // If true, this widget is |body_|, otherwise it is |menu_button_|.
775 if (GTK_WIDGET_STATE(widget) == GTK_STATE_PRELIGHT)
776 nine_box = is_body ? body_nine_box_prelight_ : menu_nine_box_prelight_;
777 else if (GTK_WIDGET_STATE(widget) == GTK_STATE_ACTIVE)
778 nine_box = is_body ? body_nine_box_active_ : menu_nine_box_active_;
779 else
780 nine_box = is_body ? body_nine_box_normal_ : menu_nine_box_normal_;
781
782 // When the button is showing, we want to draw it as active. We have to do
783 // this explicitly because the button's state will be NORMAL while the menu
784 // has focus.
785 if (!is_body && menu_showing_)
786 nine_box = menu_nine_box_active_;
787
788 nine_box->RenderToWidget(widget);
789 }
790
791 GtkWidget* child = gtk_bin_get_child(GTK_BIN(widget));
792 if (child)
793 gtk_container_propagate_expose(GTK_CONTAINER(widget), child, e);
794
795 return TRUE;
796 }
797
798 void DownloadItemGtk::OnClick(GtkWidget* widget) {
799 UMA_HISTOGRAM_LONG_TIMES("clickjacking.open_download",
800 base::Time::Now() - creation_time_);
801 get_download()->OpenDownload();
802 }
803
804 gboolean DownloadItemGtk::OnProgressAreaExpose(GtkWidget* widget,
805 GdkEventExpose* event) {
806 // Create a transparent canvas.
807 gfx::CanvasSkiaPaint canvas(event, false);
808 if (complete_animation_.get()) {
809 if (complete_animation_->is_animating()) {
810 download_util::PaintDownloadComplete(&canvas,
811 widget->allocation.x, widget->allocation.y,
812 complete_animation_->GetCurrentValue(),
813 download_util::SMALL);
814 }
815 } else if (get_download()->state() !=
816 DownloadItem::CANCELLED) {
817 download_util::PaintDownloadProgress(&canvas,
818 widget->allocation.x, widget->allocation.y,
819 progress_angle_,
820 get_download()->PercentComplete(),
821 download_util::SMALL);
822 }
823
824 // |icon_small_| may be NULL if it is still loading. If the file is an
825 // unrecognized type then we will get back a generic system icon. Hence
826 // there is no need to use the chromium-specific default download item icon.
827 if (icon_small_) {
828 const int offset = download_util::kSmallProgressIconOffset;
829 canvas.DrawBitmapInt(*icon_small_,
830 widget->allocation.x + offset, widget->allocation.y + offset);
831 }
832
833 return TRUE;
834 }
835
836 gboolean DownloadItemGtk::OnMenuButtonPressEvent(GtkWidget* button,
837 GdkEvent* event) {
838 // Stop any completion animation.
839 if (complete_animation_.get() && complete_animation_->is_animating())
840 complete_animation_->End();
841
842 if (event->type == GDK_BUTTON_PRESS) {
843 GdkEventButton* event_button = reinterpret_cast<GdkEventButton*>(event);
844 if (event_button->button == 1) {
845 if (menu_.get() == NULL) {
846 menu_.reset(new DownloadShelfContextMenuGtk(
847 download_model_.get(), this));
848 }
849 menu_->Popup(button, event);
850 menu_showing_ = true;
851 gtk_widget_queue_draw(button);
852 }
853 }
854
855 return FALSE;
856 }
857
858 gboolean DownloadItemGtk::OnDangerousPromptExpose(GtkWidget* widget,
859 GdkEventExpose* event) {
860 if (!theme_provider_->UseGtkTheme()) {
861 // The hbox renderer will take care of the border when in GTK mode.
862 dangerous_nine_box_->RenderToWidget(widget);
863 }
864 return FALSE; // Continue propagation.
865 }
866
867 void DownloadItemGtk::OnDangerousAccept(GtkWidget* button) {
868 UMA_HISTOGRAM_LONG_TIMES("clickjacking.save_download",
869 base::Time::Now() - creation_time_);
870 get_download()->DangerousDownloadValidated();
871 }
872
873 void DownloadItemGtk::OnDangerousDecline(GtkWidget* button) {
874 UMA_HISTOGRAM_LONG_TIMES("clickjacking.discard_download",
875 base::Time::Now() - creation_time_);
876 if (get_download()->state() == DownloadItem::IN_PROGRESS)
877 get_download()->Cancel(true);
878 get_download()->Remove(true);
879 }
OLDNEW
« no previous file with comments | « chrome/browser/gtk/download_item_gtk.h ('k') | chrome/browser/gtk/download_shelf_gtk.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698