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