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

Side by Side Diff: chrome/browser/ui/gtk/download/download_shelf_gtk.cc

Issue 231733005: Delete the GTK+ port of Chrome. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Remerge to ToT Created 6 years, 8 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
OLDNEW
(Empty)
1 // Copyright (c) 2012 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/ui/gtk/download/download_shelf_gtk.h"
6
7 #include <string>
8
9 #include "base/bind.h"
10 #include "chrome/browser/chrome_notification_types.h"
11 #include "chrome/browser/download/download_item_model.h"
12 #include "chrome/browser/download/download_stats.h"
13 #include "chrome/browser/themes/theme_properties.h"
14 #include "chrome/browser/ui/browser.h"
15 #include "chrome/browser/ui/chrome_pages.h"
16 #include "chrome/browser/ui/gtk/browser_window_gtk.h"
17 #include "chrome/browser/ui/gtk/custom_button.h"
18 #include "chrome/browser/ui/gtk/download/download_item_gtk.h"
19 #include "chrome/browser/ui/gtk/gtk_chrome_link_button.h"
20 #include "chrome/browser/ui/gtk/gtk_chrome_shrinkable_hbox.h"
21 #include "chrome/browser/ui/gtk/gtk_theme_service.h"
22 #include "chrome/browser/ui/gtk/gtk_util.h"
23 #include "content/public/browser/download_item.h"
24 #include "content/public/browser/notification_source.h"
25 #include "content/public/browser/page_navigator.h"
26 #include "grit/generated_resources.h"
27 #include "grit/theme_resources.h"
28 #include "grit/ui_resources.h"
29 #include "third_party/skia/include/core/SkBitmap.h"
30 #include "ui/base/gtk/gtk_screen_util.h"
31 #include "ui/base/l10n/l10n_util.h"
32 #include "ui/base/resource/resource_bundle.h"
33 #include "ui/gfx/gtk_util.h"
34 #include "ui/gfx/image/image.h"
35 #include "ui/gfx/insets.h"
36 #include "ui/gfx/point.h"
37 #include "ui/gfx/rect.h"
38
39 namespace {
40
41 // The height of the download items.
42 const int kDownloadItemHeight = DownloadShelf::kSmallProgressIconSize;
43
44 // Padding between the download widgets.
45 const int kDownloadItemPadding = 10;
46
47 // Padding between the top/bottom of the download widgets and the edge of the
48 // shelf.
49 const int kTopBottomPadding = 4;
50
51 // Padding between the left side of the shelf and the first download item.
52 const int kLeftPadding = 2;
53
54 // Padding between the right side of the shelf and the close button.
55 const int kRightPadding = 10;
56
57 // Speed of the shelf show/hide animation.
58 const int kShelfAnimationDurationMs = 120;
59
60 // The time between when the user mouses out of the download shelf zone and
61 // when the shelf closes (when auto-close is enabled).
62 const int kAutoCloseDelayMs = 300;
63
64 // The area to the top of the shelf that is considered part of its "zone".
65 const int kShelfAuraSize = 40;
66
67 } // namespace
68
69 using content::DownloadItem;
70
71 DownloadShelfGtk::DownloadShelfGtk(Browser* browser, GtkWidget* parent)
72 : browser_(browser),
73 is_showing_(false),
74 theme_service_(GtkThemeService::GetFrom(browser->profile())),
75 close_on_mouse_out_(false),
76 mouse_in_shelf_(false),
77 weak_factory_(this) {
78 // Logically, the shelf is a vbox that contains two children: a one pixel
79 // tall event box, which serves as the top border, and an hbox, which holds
80 // the download items and other shelf widgets (close button, show-all-
81 // downloads link).
82 // To make things pretty, we have to add a few more widgets. To get padding
83 // right, we stick the hbox in an alignment. We put that alignment in an
84 // event box so we can color the background.
85
86 // Create the top border.
87 top_border_ = gtk_event_box_new();
88 gtk_widget_set_size_request(GTK_WIDGET(top_border_), 0, 1);
89
90 // Create |items_hbox_|. We use GtkChromeShrinkableHBox, so that download
91 // items can be hid automatically when there is no enough space to show them.
92 items_hbox_.Own(gtk_chrome_shrinkable_hbox_new(
93 TRUE, FALSE, kDownloadItemPadding));
94 // We want the download shelf to be horizontally shrinkable, so that the
95 // Chrome window can be resized freely even with many download items.
96 gtk_widget_set_size_request(items_hbox_.get(), 0, kDownloadItemHeight);
97
98 // Create a hbox that holds |items_hbox_| and other shelf widgets.
99 GtkWidget* outer_hbox = gtk_hbox_new(FALSE, kDownloadItemPadding);
100
101 // Pack the |items_hbox_| in the outer hbox.
102 gtk_box_pack_start(GTK_BOX(outer_hbox), items_hbox_.get(), TRUE, TRUE, 0);
103
104 // Get the padding and background color for |outer_hbox| right.
105 GtkWidget* padding = gtk_alignment_new(0, 0, 1, 1);
106 // Subtract 1 from top spacing to account for top border.
107 gtk_alignment_set_padding(GTK_ALIGNMENT(padding),
108 kTopBottomPadding - 1, kTopBottomPadding, kLeftPadding, kRightPadding);
109 padding_bg_ = gtk_event_box_new();
110 gtk_container_add(GTK_CONTAINER(padding_bg_), padding);
111 gtk_container_add(GTK_CONTAINER(padding), outer_hbox);
112
113 GtkWidget* vbox = gtk_vbox_new(FALSE, 0);
114 gtk_box_pack_start(GTK_BOX(vbox), top_border_, FALSE, FALSE, 0);
115 gtk_box_pack_start(GTK_BOX(vbox), padding_bg_, FALSE, FALSE, 0);
116
117 // Put the shelf in an event box so it gets its own window, which makes it
118 // easier to get z-ordering right.
119 shelf_.Own(gtk_event_box_new());
120 gtk_container_add(GTK_CONTAINER(shelf_.get()), vbox);
121
122 // Create and pack the close button.
123 close_button_.reset(CustomDrawButton::CloseButtonBar(theme_service_));
124 gtk_util::CenterWidgetInHBox(outer_hbox, close_button_->widget(), true, 0);
125 g_signal_connect(close_button_->widget(), "clicked",
126 G_CALLBACK(OnButtonClickThunk), this);
127
128 // Create the "Show all downloads..." link and connect to the click event.
129 link_button_ = theme_service_->BuildChromeLinkButton(
130 l10n_util::GetStringUTF8(IDS_SHOW_ALL_DOWNLOADS));
131 g_signal_connect(link_button_, "clicked",
132 G_CALLBACK(OnButtonClickThunk), this);
133 gtk_util::SetButtonTriggersNavigation(link_button_);
134 // Until we switch to vector graphics, force the font size.
135 // 13.4px == 10pt @ 96dpi
136 gtk_util::ForceFontSizePixels(GTK_CHROME_LINK_BUTTON(link_button_)->label,
137 13.4);
138
139 // Make the download arrow icon.
140 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
141 GtkWidget* download_image = gtk_image_new_from_pixbuf(
142 rb.GetNativeImageNamed(IDR_DOWNLOADS_FAVICON).ToGdkPixbuf());
143
144 // Pack the link and the icon in outer hbox.
145 gtk_util::CenterWidgetInHBox(outer_hbox, link_button_, true, 0);
146 gtk_util::CenterWidgetInHBox(outer_hbox, download_image, true, 0);
147
148 slide_widget_.reset(new SlideAnimatorGtk(shelf_.get(),
149 SlideAnimatorGtk::UP,
150 kShelfAnimationDurationMs,
151 false, true, this));
152
153 theme_service_->InitThemesFor(this);
154 registrar_.Add(this, chrome::NOTIFICATION_BROWSER_THEME_CHANGED,
155 content::Source<ThemeService>(theme_service_));
156
157 gtk_widget_show_all(shelf_.get());
158
159 // Stick ourselves at the bottom of the parent browser.
160 gtk_box_pack_end(GTK_BOX(parent), slide_widget_->widget(),
161 FALSE, FALSE, 0);
162 // Make sure we are at the very end.
163 gtk_box_reorder_child(GTK_BOX(parent), slide_widget_->widget(), 0);
164 }
165
166 DownloadShelfGtk::~DownloadShelfGtk() {
167 for (std::vector<DownloadItemGtk*>::iterator iter = download_items_.begin();
168 iter != download_items_.end(); ++iter) {
169 delete *iter;
170 }
171
172 shelf_.Destroy();
173 items_hbox_.Destroy();
174
175 // Make sure we're no longer an observer of the message loop.
176 SetCloseOnMouseOut(false);
177 }
178
179 content::PageNavigator* DownloadShelfGtk::GetNavigator() {
180 return browser_;
181 }
182
183 void DownloadShelfGtk::DoAddDownload(DownloadItem* download) {
184 download_items_.push_back(new DownloadItemGtk(this, download));
185 }
186
187 bool DownloadShelfGtk::IsShowing() const {
188 return slide_widget_->IsShowing();
189 }
190
191 bool DownloadShelfGtk::IsClosing() const {
192 return slide_widget_->IsClosing();
193 }
194
195 void DownloadShelfGtk::DoShow() {
196 slide_widget_->Open();
197 browser_->UpdateDownloadShelfVisibility(true);
198 CancelAutoClose();
199 }
200
201 void DownloadShelfGtk::DoClose(CloseReason reason) {
202 // When we are closing, we can vertically overlap the render view. Make sure
203 // we are on top.
204 gdk_window_raise(gtk_widget_get_window(shelf_.get()));
205 slide_widget_->Close();
206 browser_->UpdateDownloadShelfVisibility(false);
207 int num_in_progress = 0;
208 for (size_t i = 0; i < download_items_.size(); ++i) {
209 if (download_items_[i]->download()->GetState() == DownloadItem::IN_PROGRESS)
210 ++num_in_progress;
211 }
212 RecordDownloadShelfClose(
213 download_items_.size(), num_in_progress, reason == AUTOMATIC);
214 SetCloseOnMouseOut(false);
215 }
216
217 Browser* DownloadShelfGtk::browser() const {
218 return browser_;
219 }
220
221 void DownloadShelfGtk::Closed() {
222 // Don't remove completed downloads if the shelf is just being auto-hidden
223 // rather than explicitly closed by the user.
224 if (is_hidden())
225 return;
226 // When the close animation is complete, remove all completed downloads.
227 size_t i = 0;
228 while (i < download_items_.size()) {
229 DownloadItem* download = download_items_[i]->download();
230 DownloadItem::DownloadState state = download->GetState();
231 bool is_transfer_done = state == DownloadItem::COMPLETE ||
232 state == DownloadItem::CANCELLED ||
233 state == DownloadItem::INTERRUPTED;
234 if (is_transfer_done && !download->IsDangerous()) {
235 RemoveDownloadItem(download_items_[i]);
236 } else {
237 // We set all remaining items as "opened", so that the shelf will auto-
238 // close in the future without the user clicking on them.
239 download->SetOpened(true);
240 ++i;
241 }
242 }
243 }
244
245 void DownloadShelfGtk::Observe(int type,
246 const content::NotificationSource& source,
247 const content::NotificationDetails& details) {
248 if (type == chrome::NOTIFICATION_BROWSER_THEME_CHANGED) {
249 GdkColor color = theme_service_->GetGdkColor(
250 ThemeProperties::COLOR_TOOLBAR);
251 gtk_widget_modify_bg(padding_bg_, GTK_STATE_NORMAL, &color);
252
253 color = theme_service_->GetBorderColor();
254 gtk_widget_modify_bg(top_border_, GTK_STATE_NORMAL, &color);
255
256 // When using a non-standard, non-gtk theme, we make the link color match
257 // the bookmark text color. Otherwise, standard link blue can look very
258 // bad for some dark themes.
259 bool use_default_color = theme_service_->GetColor(
260 ThemeProperties::COLOR_BOOKMARK_TEXT) ==
261 ThemeProperties::GetDefaultColor(
262 ThemeProperties::COLOR_BOOKMARK_TEXT);
263 GdkColor bookmark_color = theme_service_->GetGdkColor(
264 ThemeProperties::COLOR_BOOKMARK_TEXT);
265 gtk_chrome_link_button_set_normal_color(
266 GTK_CHROME_LINK_BUTTON(link_button_),
267 use_default_color ? NULL : &bookmark_color);
268
269 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
270 close_button_->SetBackground(
271 theme_service_->GetColor(ThemeProperties::COLOR_TAB_TEXT),
272 rb.GetImageNamed(IDR_CLOSE_1).AsBitmap(),
273 rb.GetImageNamed(IDR_CLOSE_1_MASK).AsBitmap());
274 }
275 }
276
277 int DownloadShelfGtk::GetHeight() const {
278 GtkAllocation allocation;
279 gtk_widget_get_allocation(slide_widget_->widget(), &allocation);
280 return allocation.height;
281 }
282
283 void DownloadShelfGtk::RemoveDownloadItem(DownloadItemGtk* download_item) {
284 DCHECK(download_item);
285 std::vector<DownloadItemGtk*>::iterator i =
286 find(download_items_.begin(), download_items_.end(), download_item);
287 DCHECK(i != download_items_.end());
288 download_items_.erase(i);
289 delete download_item;
290 if (download_items_.empty()) {
291 slide_widget_->CloseWithoutAnimation();
292 browser_->UpdateDownloadShelfVisibility(false);
293 } else {
294 AutoCloseIfPossible();
295 }
296 }
297
298 GtkWidget* DownloadShelfGtk::GetHBox() const {
299 return items_hbox_.get();
300 }
301
302 void DownloadShelfGtk::MaybeShowMoreDownloadItems() {
303 // Show all existing download items. It'll trigger "size-allocate" signal,
304 // which will hide download items that don't have enough space to show.
305 gtk_widget_show_all(items_hbox_.get());
306 }
307
308 void DownloadShelfGtk::OnButtonClick(GtkWidget* button) {
309 if (button == close_button_->widget()) {
310 Close(USER_ACTION);
311 } else {
312 // The link button was clicked.
313 chrome::ShowDownloads(browser_);
314 }
315 }
316
317 void DownloadShelfGtk::AutoCloseIfPossible() {
318 for (std::vector<DownloadItemGtk*>::iterator iter = download_items_.begin();
319 iter != download_items_.end(); ++iter) {
320 if (!(*iter)->download()->GetOpened())
321 return;
322 }
323
324 SetCloseOnMouseOut(true);
325 }
326
327 void DownloadShelfGtk::CancelAutoClose() {
328 SetCloseOnMouseOut(false);
329 weak_factory_.InvalidateWeakPtrs();
330 }
331
332 void DownloadShelfGtk::ItemOpened() {
333 AutoCloseIfPossible();
334 }
335
336 void DownloadShelfGtk::SetCloseOnMouseOut(bool close) {
337 if (close_on_mouse_out_ == close)
338 return;
339
340 close_on_mouse_out_ = close;
341 mouse_in_shelf_ = close;
342 if (close)
343 base::MessageLoopForUI::current()->AddObserver(this);
344 else
345 base::MessageLoopForUI::current()->RemoveObserver(this);
346 }
347
348 void DownloadShelfGtk::WillProcessEvent(GdkEvent* event) {
349 }
350
351 void DownloadShelfGtk::DidProcessEvent(GdkEvent* event) {
352 gfx::Point cursor_screen_coords;
353
354 switch (event->type) {
355 case GDK_MOTION_NOTIFY:
356 cursor_screen_coords =
357 gfx::Point(event->motion.x_root, event->motion.y_root);
358 break;
359 case GDK_LEAVE_NOTIFY:
360 cursor_screen_coords =
361 gfx::Point(event->crossing.x_root, event->crossing.y_root);
362 break;
363 default:
364 return;
365 }
366
367 bool mouse_in_shelf = IsCursorInShelfZone(cursor_screen_coords);
368 if (mouse_in_shelf == mouse_in_shelf_)
369 return;
370 mouse_in_shelf_ = mouse_in_shelf;
371
372 if (mouse_in_shelf)
373 MouseEnteredShelf();
374 else
375 MouseLeftShelf();
376 }
377
378 bool DownloadShelfGtk::IsCursorInShelfZone(
379 const gfx::Point& cursor_screen_coords) {
380 bool realized = (shelf_.get() &&
381 gtk_widget_get_window(shelf_.get()));
382 // Do nothing if we've been unrealized in order to avoid a NOTREACHED in
383 // GetWidgetScreenPosition.
384 if (!realized)
385 return false;
386
387 gfx::Rect bounds = ui::GetWidgetScreenBounds(shelf_.get());
388
389 // Negative insets expand the rectangle. We only expand the top.
390 bounds.Inset(gfx::Insets(-kShelfAuraSize, 0, 0, 0));
391
392 return bounds.Contains(cursor_screen_coords);
393 }
394
395 void DownloadShelfGtk::MouseLeftShelf() {
396 DCHECK(close_on_mouse_out_);
397
398 base::MessageLoop::current()->PostDelayedTask(
399 FROM_HERE,
400 base::Bind(
401 &DownloadShelfGtk::Close, weak_factory_.GetWeakPtr(), AUTOMATIC),
402 base::TimeDelta::FromMilliseconds(kAutoCloseDelayMs));
403 }
404
405 void DownloadShelfGtk::MouseEnteredShelf() {
406 weak_factory_.InvalidateWeakPtrs();
407 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698