172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen// Copyright (c) 2011 The Chromium Authors. All rights reserved.
2c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Use of this source code is governed by a BSD-style license that can be
3c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// found in the LICENSE file.
4c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
5ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "chrome/browser/ui/gtk/download/download_shelf_gtk.h"
6c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
721d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen#include <string>
821d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen
9c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/browser/download/download_item.h"
10c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/browser/download/download_item_model.h"
11c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/browser/download/download_util.h"
124a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch#include "chrome/browser/ui/browser.h"
1372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "chrome/browser/ui/gtk/browser_window_gtk.h"
1472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "chrome/browser/ui/gtk/custom_button.h"
15ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "chrome/browser/ui/gtk/download/download_item_gtk.h"
1672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "chrome/browser/ui/gtk/gtk_chrome_link_button.h"
1772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "chrome/browser/ui/gtk/gtk_chrome_shrinkable_hbox.h"
18ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "chrome/browser/ui/gtk/gtk_theme_service.h"
1972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "chrome/browser/ui/gtk/gtk_util.h"
20dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen#include "content/common/notification_service.h"
21c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "grit/generated_resources.h"
22c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "grit/theme_resources.h"
2372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "ui/base/l10n/l10n_util.h"
2472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "ui/base/resource/resource_bundle.h"
2572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "ui/gfx/gtk_util.h"
2672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "ui/gfx/insets.h"
2772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "ui/gfx/point.h"
2872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "ui/gfx/rect.h"
29c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
30c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochnamespace {
31c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
32c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// The height of the download items.
33c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochconst int kDownloadItemHeight = download_util::kSmallProgressIconSize;
34c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
35c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Padding between the download widgets.
36c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochconst int kDownloadItemPadding = 10;
37c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
38c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Padding between the top/bottom of the download widgets and the edge of the
39c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// shelf.
40c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochconst int kTopBottomPadding = 4;
41c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
42c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Padding between the left side of the shelf and the first download item.
43c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochconst int kLeftPadding = 2;
44c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
45c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Padding between the right side of the shelf and the close button.
46c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochconst int kRightPadding = 10;
47c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
48c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Speed of the shelf show/hide animation.
49c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochconst int kShelfAnimationDurationMs = 120;
50c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
5172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen// The time between when the user mouses out of the download shelf zone and
5272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen// when the shelf closes (when auto-close is enabled).
5372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsenconst int kAutoCloseDelayMs = 300;
5472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
5572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen// The area to the top of the shelf that is considered part of its "zone".
5672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsenconst int kShelfAuraSize = 40;
5772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
58c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}  // namespace
59c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
60c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochDownloadShelfGtk::DownloadShelfGtk(Browser* browser, GtkWidget* parent)
61c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    : browser_(browser),
62c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      is_showing_(false),
63ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      theme_service_(GtkThemeService::GetFrom(browser->profile())),
6472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      close_on_mouse_out_(false),
6572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      mouse_in_shelf_(false),
6672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      auto_close_factory_(this) {
67c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Logically, the shelf is a vbox that contains two children: a one pixel
68c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // tall event box, which serves as the top border, and an hbox, which holds
69c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // the download items and other shelf widgets (close button, show-all-
70c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // downloads link).
71c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // To make things pretty, we have to add a few more widgets. To get padding
72c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // right, we stick the hbox in an alignment. We put that alignment in an
73c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // event box so we can color the background.
74c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
75c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Create the top border.
76c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  top_border_ = gtk_event_box_new();
77c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  gtk_widget_set_size_request(GTK_WIDGET(top_border_), 0, 1);
78c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
79c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Create |items_hbox_|. We use GtkChromeShrinkableHBox, so that download
80c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // items can be hid automatically when there is no enough space to show them.
81c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  items_hbox_.Own(gtk_chrome_shrinkable_hbox_new(
82c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      TRUE, FALSE, kDownloadItemPadding));
83c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // We want the download shelf to be horizontally shrinkable, so that the
84c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Chrome window can be resized freely even with many download items.
85c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  gtk_widget_set_size_request(items_hbox_.get(), 0, kDownloadItemHeight);
86c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
87c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Create a hbox that holds |items_hbox_| and other shelf widgets.
88c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  GtkWidget* outer_hbox = gtk_hbox_new(FALSE, kDownloadItemPadding);
89c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
90c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Pack the |items_hbox_| in the outer hbox.
91c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  gtk_box_pack_start(GTK_BOX(outer_hbox), items_hbox_.get(), TRUE, TRUE, 0);
92c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
93c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Get the padding and background color for |outer_hbox| right.
94c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  GtkWidget* padding = gtk_alignment_new(0, 0, 1, 1);
95c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Subtract 1 from top spacing to account for top border.
96c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  gtk_alignment_set_padding(GTK_ALIGNMENT(padding),
97c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      kTopBottomPadding - 1, kTopBottomPadding, kLeftPadding, kRightPadding);
98c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  padding_bg_ = gtk_event_box_new();
99c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  gtk_container_add(GTK_CONTAINER(padding_bg_), padding);
100c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  gtk_container_add(GTK_CONTAINER(padding), outer_hbox);
101c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
102c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  GtkWidget* vbox = gtk_vbox_new(FALSE, 0);
103c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  gtk_box_pack_start(GTK_BOX(vbox), top_border_, FALSE, FALSE, 0);
104c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  gtk_box_pack_start(GTK_BOX(vbox), padding_bg_, FALSE, FALSE, 0);
105c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
106c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Put the shelf in an event box so it gets its own window, which makes it
107c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // easier to get z-ordering right.
108c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  shelf_.Own(gtk_event_box_new());
109c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  gtk_container_add(GTK_CONTAINER(shelf_.get()), vbox);
110c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
111c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Create and pack the close button.
112ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  close_button_.reset(CustomDrawButton::CloseButton(theme_service_));
113c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  gtk_util::CenterWidgetInHBox(outer_hbox, close_button_->widget(), true, 0);
114c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  g_signal_connect(close_button_->widget(), "clicked",
115c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                   G_CALLBACK(OnButtonClickThunk), this);
116c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
117c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Create the "Show all downloads..." link and connect to the click event.
118c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  std::string link_text =
119c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      l10n_util::GetStringUTF8(IDS_SHOW_ALL_DOWNLOADS);
120c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  link_button_ = gtk_chrome_link_button_new(link_text.c_str());
121c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  g_signal_connect(link_button_, "clicked",
122c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                   G_CALLBACK(OnButtonClickThunk), this);
123c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  gtk_util::SetButtonTriggersNavigation(link_button_);
124c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Until we switch to vector graphics, force the font size.
125c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // 13.4px == 10pt @ 96dpi
126c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  gtk_util::ForceFontSizePixels(GTK_CHROME_LINK_BUTTON(link_button_)->label,
127c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                13.4);
128c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
129c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Make the download arrow icon.
130c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  ResourceBundle& rb = ResourceBundle::GetSharedInstance();
131c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  GdkPixbuf* download_pixbuf = rb.GetPixbufNamed(IDR_DOWNLOADS_FAVICON);
132c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  GtkWidget* download_image = gtk_image_new_from_pixbuf(download_pixbuf);
133c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
134c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Pack the link and the icon in outer hbox.
135c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  gtk_util::CenterWidgetInHBox(outer_hbox, link_button_, true, 0);
136c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  gtk_util::CenterWidgetInHBox(outer_hbox, download_image, true, 0);
137c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
138c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  slide_widget_.reset(new SlideAnimatorGtk(shelf_.get(),
139c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                           SlideAnimatorGtk::UP,
140c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                           kShelfAnimationDurationMs,
141c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                           false, true, this));
142c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
143ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  theme_service_->InitThemesFor(this);
144c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  registrar_.Add(this, NotificationType::BROWSER_THEME_CHANGED,
145c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                 NotificationService::AllSources());
146c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
147c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  gtk_widget_show_all(shelf_.get());
148c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
149c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Stick ourselves at the bottom of the parent browser.
150c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  gtk_box_pack_end(GTK_BOX(parent), slide_widget_->widget(),
151c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                   FALSE, FALSE, 0);
152c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Make sure we are at the very end.
153c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  gtk_box_reorder_child(GTK_BOX(parent), slide_widget_->widget(), 0);
154c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  Show();
155c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
156c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
157c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochDownloadShelfGtk::~DownloadShelfGtk() {
158c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  for (std::vector<DownloadItemGtk*>::iterator iter = download_items_.begin();
159c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch       iter != download_items_.end(); ++iter) {
160c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    delete *iter;
161c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
162c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
163c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  shelf_.Destroy();
164c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  items_hbox_.Destroy();
16572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
16672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  // Make sure we're no longer an observer of the message loop.
16772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  SetCloseOnMouseOut(false);
168c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
169c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
170c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid DownloadShelfGtk::AddDownload(BaseDownloadItemModel* download_model_) {
171c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  download_items_.push_back(new DownloadItemGtk(this, download_model_));
172c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  Show();
173c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
174c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
175c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochbool DownloadShelfGtk::IsShowing() const {
176c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return slide_widget_->IsShowing();
177c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
178c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
179c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochbool DownloadShelfGtk::IsClosing() const {
180c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return slide_widget_->IsClosing();
181c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
182c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
183c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid DownloadShelfGtk::Show() {
184c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  slide_widget_->Open();
185c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  browser_->UpdateDownloadShelfVisibility(true);
18672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  CancelAutoClose();
187c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
188c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
189c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid DownloadShelfGtk::Close() {
190c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // When we are closing, we can vertically overlap the render view. Make sure
191c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // we are on top.
192c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  gdk_window_raise(shelf_.get()->window);
193c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  slide_widget_->Close();
194c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  browser_->UpdateDownloadShelfVisibility(false);
19572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  SetCloseOnMouseOut(false);
196c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
197c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
19821d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian MonsenBrowser* DownloadShelfGtk::browser() const {
19921d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  return browser_;
20021d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen}
20121d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen
202c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid DownloadShelfGtk::Closed() {
203c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // When the close animation is complete, remove all completed downloads.
204c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  size_t i = 0;
205c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  while (i < download_items_.size()) {
206c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    DownloadItem* download = download_items_[i]->get_download();
207ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    bool is_transfer_done = download->IsComplete() ||
208ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen                            download->IsCancelled() ||
209ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen                            download->IsInterrupted();
210c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (is_transfer_done &&
211c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        download->safety_state() != DownloadItem::DANGEROUS) {
212c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      RemoveDownloadItem(download_items_[i]);
213c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    } else {
21472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      // We set all remaining items as "opened", so that the shelf will auto-
21572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      // close in the future without the user clicking on them.
21672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      download->set_opened(true);
217c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      ++i;
218c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
219c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
220c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
221c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
222c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid DownloadShelfGtk::Observe(NotificationType type,
223c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                               const NotificationSource& source,
224c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                               const NotificationDetails& details) {
225c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (type == NotificationType::BROWSER_THEME_CHANGED) {
226ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    GdkColor color = theme_service_->GetGdkColor(
227ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen        ThemeService::COLOR_TOOLBAR);
228c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    gtk_widget_modify_bg(padding_bg_, GTK_STATE_NORMAL, &color);
229c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
230ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    color = theme_service_->GetBorderColor();
231c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    gtk_widget_modify_bg(top_border_, GTK_STATE_NORMAL, &color);
232c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
233c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    gtk_chrome_link_button_set_use_gtk_theme(
234ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen        GTK_CHROME_LINK_BUTTON(link_button_), theme_service_->UseGtkTheme());
235c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
236c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // When using a non-standard, non-gtk theme, we make the link color match
237c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // the bookmark text color. Otherwise, standard link blue can look very
238c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // bad for some dark themes.
239ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    bool use_default_color = theme_service_->GetColor(
240ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen        ThemeService::COLOR_BOOKMARK_TEXT) ==
241ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen        ThemeService::GetDefaultColor(
242ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen            ThemeService::COLOR_BOOKMARK_TEXT);
243ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    GdkColor bookmark_color = theme_service_->GetGdkColor(
244ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen        ThemeService::COLOR_BOOKMARK_TEXT);
245c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    gtk_chrome_link_button_set_normal_color(
246c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        GTK_CHROME_LINK_BUTTON(link_button_),
247c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        use_default_color ? NULL : &bookmark_color);
248c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
249c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    ResourceBundle& rb = ResourceBundle::GetSharedInstance();
250c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    close_button_->SetBackground(
251ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen        theme_service_->GetColor(ThemeService::COLOR_TAB_TEXT),
252c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        rb.GetBitmapNamed(IDR_CLOSE_BAR),
253c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        rb.GetBitmapNamed(IDR_CLOSE_BAR_MASK));
254c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
255c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
256c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
257c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochint DownloadShelfGtk::GetHeight() const {
258c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return slide_widget_->widget()->allocation.height;
259c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
260c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
261c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid DownloadShelfGtk::RemoveDownloadItem(DownloadItemGtk* download_item) {
262c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  DCHECK(download_item);
263c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  std::vector<DownloadItemGtk*>::iterator i =
264c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      find(download_items_.begin(), download_items_.end(), download_item);
265c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  DCHECK(i != download_items_.end());
266c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  download_items_.erase(i);
267c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  delete download_item;
268c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (download_items_.empty()) {
269c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    slide_widget_->CloseWithoutAnimation();
270c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    browser_->UpdateDownloadShelfVisibility(false);
27172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  } else {
27272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    AutoCloseIfPossible();
273c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
274c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
275c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
276c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochGtkWidget* DownloadShelfGtk::GetHBox() const {
277c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return items_hbox_.get();
278c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
279c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
280c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid DownloadShelfGtk::MaybeShowMoreDownloadItems() {
281c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Show all existing download items. It'll trigger "size-allocate" signal,
282c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // which will hide download items that don't have enough space to show.
283c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  gtk_widget_show_all(items_hbox_.get());
284c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
285c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
286c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid DownloadShelfGtk::OnButtonClick(GtkWidget* button) {
287c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (button == close_button_->widget()) {
288c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    Close();
289c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  } else {
290c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // The link button was clicked.
291c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    browser_->ShowDownloadsTab();
292c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
293c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
29472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
29572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsenvoid DownloadShelfGtk::AutoCloseIfPossible() {
29672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  for (std::vector<DownloadItemGtk*>::iterator iter = download_items_.begin();
29772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen       iter != download_items_.end(); ++iter) {
29872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    if (!(*iter)->get_download()->opened())
29972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      return;
30072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  }
30172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
30272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  SetCloseOnMouseOut(true);
30372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen}
30472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
30572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsenvoid DownloadShelfGtk::CancelAutoClose() {
30672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  SetCloseOnMouseOut(false);
30772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  auto_close_factory_.RevokeAll();
30872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen}
30972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
31072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsenvoid DownloadShelfGtk::ItemOpened() {
31172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  AutoCloseIfPossible();
31272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen}
31372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
31472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsenvoid DownloadShelfGtk::SetCloseOnMouseOut(bool close) {
31572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  if (close_on_mouse_out_ == close)
31672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    return;
31772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
31872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  close_on_mouse_out_ = close;
31972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  mouse_in_shelf_ = close;
32072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  if (close)
32172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    MessageLoopForUI::current()->AddObserver(this);
32272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  else
32372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    MessageLoopForUI::current()->RemoveObserver(this);
32472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen}
32572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
32672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsenvoid DownloadShelfGtk::WillProcessEvent(GdkEvent* event) {
32772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen}
32872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
32972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsenvoid DownloadShelfGtk::DidProcessEvent(GdkEvent* event) {
33072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  gfx::Point cursor_screen_coords;
33172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
33272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  switch (event->type) {
33372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    case GDK_MOTION_NOTIFY:
33472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      cursor_screen_coords =
33572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen          gfx::Point(event->motion.x_root, event->motion.y_root);
33672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      break;
33772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    case GDK_LEAVE_NOTIFY:
33872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      cursor_screen_coords =
33972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen          gfx::Point(event->crossing.x_root, event->crossing.y_root);
34072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      break;
34172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    default:
34272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      return;
34372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  }
34472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
34572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  bool mouse_in_shelf = IsCursorInShelfZone(cursor_screen_coords);
34672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  if (mouse_in_shelf == mouse_in_shelf_)
34772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    return;
34872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  mouse_in_shelf_ = mouse_in_shelf;
34972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
35072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  if (mouse_in_shelf)
35172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    MouseEnteredShelf();
35272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  else
35372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    MouseLeftShelf();
35472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen}
35572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
35672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsenbool DownloadShelfGtk::IsCursorInShelfZone(
35772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    const gfx::Point& cursor_screen_coords) {
35872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  gfx::Rect bounds(gtk_util::GetWidgetScreenPosition(shelf_.get()),
35972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen                   gfx::Size(shelf_.get()->allocation.width,
36072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen                             shelf_.get()->allocation.height));
36172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
36272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  // Negative insets expand the rectangle. We only expand the top.
36372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  bounds.Inset(gfx::Insets(-kShelfAuraSize, 0, 0, 0));
36472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
36572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  return bounds.Contains(cursor_screen_coords);
36672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen}
36772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
36872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsenvoid DownloadShelfGtk::MouseLeftShelf() {
36972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  DCHECK(close_on_mouse_out_);
37072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
37172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  MessageLoop::current()->PostDelayedTask(
37272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      FROM_HERE,
37372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      auto_close_factory_.NewRunnableMethod(&DownloadShelfGtk::Close),
37472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      kAutoCloseDelayMs);
37572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen}
37672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
37772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsenvoid DownloadShelfGtk::MouseEnteredShelf() {
37872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  auto_close_factory_.RevokeAll();
37972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen}
380