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

Side by Side Diff: chrome/browser/gtk/bookmark_bar_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
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/bookmark_bar_gtk.h"
6
7 #include <vector>
8
9 #include "app/gtk_dnd_util.h"
10 #include "app/resource_bundle.h"
11 #include "base/utf_string_conversions.h"
12 #include "chrome/browser/bookmarks/bookmark_node_data.h"
13 #include "chrome/browser/bookmarks/bookmark_model.h"
14 #include "chrome/browser/bookmarks/bookmark_utils.h"
15 #include "chrome/browser/browser_shutdown.h"
16 #include "chrome/browser/gtk/bookmark_menu_controller_gtk.h"
17 #include "chrome/browser/gtk/bookmark_tree_model.h"
18 #include "chrome/browser/gtk/bookmark_utils_gtk.h"
19 #include "chrome/browser/gtk/browser_window_gtk.h"
20 #include "chrome/browser/gtk/cairo_cached_surface.h"
21 #include "chrome/browser/gtk/custom_button.h"
22 #include "chrome/browser/gtk/gtk_chrome_button.h"
23 #include "chrome/browser/gtk/gtk_theme_provider.h"
24 #include "chrome/browser/gtk/gtk_util.h"
25 #include "chrome/browser/gtk/hover_controller_gtk.h"
26 #include "chrome/browser/gtk/import_dialog_gtk.h"
27 #include "chrome/browser/gtk/menu_gtk.h"
28 #include "chrome/browser/gtk/rounded_window.h"
29 #include "chrome/browser/gtk/tabstrip_origin_provider.h"
30 #include "chrome/browser/gtk/tabs/tab_strip_gtk.h"
31 #include "chrome/browser/gtk/view_id_util.h"
32 #include "chrome/browser/importer/importer_data_types.h"
33 #include "chrome/browser/metrics/user_metrics.h"
34 #include "chrome/browser/ntp_background_util.h"
35 #include "chrome/browser/prefs/pref_service.h"
36 #include "chrome/browser/profiles/profile.h"
37 #include "chrome/browser/sync/sync_ui_util.h"
38 #include "chrome/browser/tab_contents/tab_contents.h"
39 #include "chrome/browser/tab_contents/tab_contents_view.h"
40 #include "chrome/browser/ui/browser.h"
41 #include "chrome/common/notification_service.h"
42 #include "chrome/common/pref_names.h"
43 #include "gfx/canvas_skia_paint.h"
44 #include "gfx/gtk_util.h"
45 #include "grit/app_resources.h"
46 #include "grit/generated_resources.h"
47 #include "grit/theme_resources.h"
48 #include "ui/base/animation/slide_animation.h"
49
50 namespace {
51
52 // The showing height of the bar.
53 const int kBookmarkBarHeight = 29;
54
55 // Padding for when the bookmark bar is floating.
56 const int kTopBottomNTPPadding = 12;
57 const int kLeftRightNTPPadding = 8;
58
59 // Padding around the bar's content area when the bookmark bar is floating.
60 const int kNTPPadding = 2;
61
62 // The number of pixels of rounding on the corners of the bookmark bar content
63 // area when in floating mode.
64 const int kNTPRoundedness = 3;
65
66 // The height of the bar when it is "hidden". It is usually not completely
67 // hidden because even when it is closed it forms the bottom few pixels of
68 // the toolbar.
69 const int kBookmarkBarMinimumHeight = 3;
70
71 // Left-padding for the instructional text.
72 const int kInstructionsPadding = 6;
73
74 // Padding around the "Other Bookmarks" button.
75 const int kOtherBookmarksPaddingHorizontal = 2;
76 const int kOtherBookmarksPaddingVertical = 1;
77
78 // The targets accepted by the toolbar and folder buttons for DnD.
79 const int kDestTargetList[] = { gtk_dnd_util::CHROME_BOOKMARK_ITEM,
80 gtk_dnd_util::CHROME_NAMED_URL,
81 gtk_dnd_util::TEXT_URI_LIST,
82 gtk_dnd_util::NETSCAPE_URL,
83 gtk_dnd_util::TEXT_PLAIN, -1 };
84
85 // Acceptable drag actions for the bookmark bar drag destinations.
86 const GdkDragAction kDragAction =
87 GdkDragAction(GDK_ACTION_MOVE | GDK_ACTION_COPY);
88
89 void SetToolBarStyle() {
90 static bool style_was_set = false;
91
92 if (style_was_set)
93 return;
94 style_was_set = true;
95
96 gtk_rc_parse_string(
97 "style \"chrome-bookmark-toolbar\" {"
98 " xthickness = 0\n"
99 " ythickness = 0\n"
100 " GtkWidget::focus-padding = 0\n"
101 " GtkContainer::border-width = 0\n"
102 " GtkToolbar::internal-padding = 1\n"
103 " GtkToolbar::shadow-type = GTK_SHADOW_NONE\n"
104 "}\n"
105 "widget \"*chrome-bookmark-toolbar\" style \"chrome-bookmark-toolbar\"");
106 }
107
108 } // namespace
109
110 const int BookmarkBarGtk::kBookmarkBarNTPHeight = 57;
111
112 BookmarkBarGtk::BookmarkBarGtk(BrowserWindowGtk* window,
113 Profile* profile, Browser* browser,
114 TabstripOriginProvider* tabstrip_origin_provider)
115 : profile_(NULL),
116 page_navigator_(NULL),
117 browser_(browser),
118 window_(window),
119 tabstrip_origin_provider_(tabstrip_origin_provider),
120 model_(NULL),
121 instructions_(NULL),
122 sync_service_(NULL),
123 dragged_node_(NULL),
124 drag_icon_(NULL),
125 toolbar_drop_item_(NULL),
126 theme_provider_(GtkThemeProvider::GetFrom(profile)),
127 show_instructions_(true),
128 menu_bar_helper_(this),
129 floating_(false),
130 last_allocation_width_(-1),
131 throbbing_widget_(NULL),
132 method_factory_(this) {
133 if (profile->GetProfileSyncService()) {
134 // Obtain a pointer to the profile sync service and add our instance as an
135 // observer.
136 sync_service_ = profile->GetProfileSyncService();
137 sync_service_->AddObserver(this);
138 }
139
140 Init(profile);
141 SetProfile(profile);
142 // Force an update by simulating being in the wrong state.
143 floating_ = !ShouldBeFloating();
144 UpdateFloatingState();
145
146 registrar_.Add(this, NotificationType::BROWSER_THEME_CHANGED,
147 NotificationService::AllSources());
148 }
149
150 BookmarkBarGtk::~BookmarkBarGtk() {
151 RemoveAllBookmarkButtons();
152 bookmark_toolbar_.Destroy();
153 event_box_.Destroy();
154 }
155
156 void BookmarkBarGtk::SetProfile(Profile* profile) {
157 DCHECK(profile);
158 if (profile_ == profile)
159 return;
160
161 RemoveAllBookmarkButtons();
162
163 profile_ = profile;
164
165 if (model_)
166 model_->RemoveObserver(this);
167
168 // TODO(erg): Handle extensions
169
170 model_ = profile_->GetBookmarkModel();
171 model_->AddObserver(this);
172 if (model_->IsLoaded())
173 Loaded(model_);
174
175 // else case: we'll receive notification back from the BookmarkModel when done
176 // loading, then we'll populate the bar.
177 }
178
179 void BookmarkBarGtk::SetPageNavigator(PageNavigator* navigator) {
180 page_navigator_ = navigator;
181 }
182
183 void BookmarkBarGtk::Init(Profile* profile) {
184 event_box_.Own(gtk_event_box_new());
185 g_signal_connect(event_box_.get(), "destroy",
186 G_CALLBACK(&OnEventBoxDestroyThunk), this);
187 g_signal_connect(event_box_.get(), "button-press-event",
188 G_CALLBACK(&OnButtonPressedThunk), this);
189
190 ntp_padding_box_ = gtk_alignment_new(0, 0, 1, 1);
191 gtk_container_add(GTK_CONTAINER(event_box_.get()), ntp_padding_box_);
192
193 paint_box_ = gtk_event_box_new();
194 gtk_container_add(GTK_CONTAINER(ntp_padding_box_), paint_box_);
195 GdkColor paint_box_color =
196 theme_provider_->GetGdkColor(BrowserThemeProvider::COLOR_TOOLBAR);
197 gtk_widget_modify_bg(paint_box_, GTK_STATE_NORMAL, &paint_box_color);
198 gtk_widget_add_events(paint_box_, GDK_POINTER_MOTION_MASK |
199 GDK_BUTTON_PRESS_MASK);
200
201 bookmark_hbox_ = gtk_hbox_new(FALSE, 0);
202 gtk_container_add(GTK_CONTAINER(paint_box_), bookmark_hbox_);
203
204 instructions_ = gtk_alignment_new(0, 0, 1, 1);
205 gtk_alignment_set_padding(GTK_ALIGNMENT(instructions_), 0, 0,
206 kInstructionsPadding, 0);
207 instructions_gtk_.reset(new BookmarkBarInstructionsGtk(this, profile));
208 gtk_container_add(GTK_CONTAINER(instructions_), instructions_gtk_->widget());
209 gtk_box_pack_start(GTK_BOX(bookmark_hbox_), instructions_,
210 TRUE, TRUE, 0);
211
212 gtk_drag_dest_set(instructions_,
213 GtkDestDefaults(GTK_DEST_DEFAULT_DROP | GTK_DEST_DEFAULT_MOTION),
214 NULL, 0, kDragAction);
215 gtk_dnd_util::SetDestTargetList(instructions_, kDestTargetList);
216 g_signal_connect(instructions_, "drag-data-received",
217 G_CALLBACK(&OnDragReceivedThunk), this);
218
219 g_signal_connect(event_box_.get(), "expose-event",
220 G_CALLBACK(&OnEventBoxExposeThunk), this);
221 UpdateEventBoxPaintability();
222
223 bookmark_toolbar_.Own(gtk_toolbar_new());
224 SetToolBarStyle();
225 gtk_widget_set_name(bookmark_toolbar_.get(), "chrome-bookmark-toolbar");
226 gtk_util::SuppressDefaultPainting(bookmark_toolbar_.get());
227 g_signal_connect(bookmark_toolbar_.get(), "size-allocate",
228 G_CALLBACK(&OnToolbarSizeAllocateThunk), this);
229 gtk_box_pack_start(GTK_BOX(bookmark_hbox_), bookmark_toolbar_.get(),
230 TRUE, TRUE, 0);
231
232 overflow_button_ = theme_provider_->BuildChromeButton();
233 g_object_set_data(G_OBJECT(overflow_button_), "left-align-popup",
234 reinterpret_cast<void*>(true));
235 SetOverflowButtonAppearance();
236 ConnectFolderButtonEvents(overflow_button_, false);
237 gtk_box_pack_start(GTK_BOX(bookmark_hbox_), overflow_button_,
238 FALSE, FALSE, 0);
239
240 gtk_drag_dest_set(bookmark_toolbar_.get(), GTK_DEST_DEFAULT_DROP,
241 NULL, 0, kDragAction);
242 gtk_dnd_util::SetDestTargetList(bookmark_toolbar_.get(), kDestTargetList);
243 g_signal_connect(bookmark_toolbar_.get(), "drag-motion",
244 G_CALLBACK(&OnToolbarDragMotionThunk), this);
245 g_signal_connect(bookmark_toolbar_.get(), "drag-leave",
246 G_CALLBACK(&OnDragLeaveThunk), this);
247 g_signal_connect(bookmark_toolbar_.get(), "drag-data-received",
248 G_CALLBACK(&OnDragReceivedThunk), this);
249
250 GtkWidget* vseparator = theme_provider_->CreateToolbarSeparator();
251 gtk_box_pack_start(GTK_BOX(bookmark_hbox_), vseparator,
252 FALSE, FALSE, 0);
253
254 // We pack the button manually (rather than using gtk_button_set_*) so that
255 // we can have finer control over its label.
256 other_bookmarks_button_ = theme_provider_->BuildChromeButton();
257 ConnectFolderButtonEvents(other_bookmarks_button_, false);
258 GtkWidget* other_padding = gtk_alignment_new(0, 0, 1, 1);
259 gtk_alignment_set_padding(GTK_ALIGNMENT(other_padding),
260 kOtherBookmarksPaddingVertical,
261 kOtherBookmarksPaddingVertical,
262 kOtherBookmarksPaddingHorizontal,
263 kOtherBookmarksPaddingHorizontal);
264 gtk_container_add(GTK_CONTAINER(other_padding), other_bookmarks_button_);
265 gtk_box_pack_start(GTK_BOX(bookmark_hbox_), other_padding,
266 FALSE, FALSE, 0);
267
268 sync_error_button_ = theme_provider_->BuildChromeButton();
269 gtk_button_set_image(
270 GTK_BUTTON(sync_error_button_),
271 gtk_image_new_from_pixbuf(
272 ResourceBundle::GetSharedInstance().GetPixbufNamed(IDR_WARNING)));
273 g_signal_connect(sync_error_button_, "button-press-event",
274 G_CALLBACK(OnSyncErrorButtonPressedThunk), this);
275 gtk_box_pack_start(GTK_BOX(bookmark_hbox_), sync_error_button_,
276 FALSE, FALSE, 0);
277
278 gtk_widget_set_size_request(event_box_.get(), -1, kBookmarkBarMinimumHeight);
279
280 slide_animation_.reset(new ui::SlideAnimation(this));
281
282 ViewIDUtil::SetID(other_bookmarks_button_, VIEW_ID_OTHER_BOOKMARKS);
283 ViewIDUtil::SetID(widget(), VIEW_ID_BOOKMARK_BAR);
284
285 gtk_widget_show_all(widget());
286 gtk_widget_hide(widget());
287 }
288
289 void BookmarkBarGtk::Show(bool animate) {
290 gtk_widget_show_all(widget());
291 bool old_floating = floating_;
292 UpdateFloatingState();
293 // TODO(estade): animate the transition between floating and non.
294 animate = animate && (old_floating == floating_);
295 if (animate) {
296 slide_animation_->Show();
297 } else {
298 slide_animation_->Reset(1);
299 AnimationProgressed(slide_animation_.get());
300 }
301
302 // Hide out behind the findbar. This is rather fragile code, it could
303 // probably be improved.
304 if (floating_) {
305 if (theme_provider_->UseGtkTheme()) {
306 if (GTK_WIDGET_REALIZED(event_box_->parent))
307 gdk_window_lower(event_box_->parent->window);
308 if (GTK_WIDGET_REALIZED(event_box_.get()))
309 gdk_window_lower(event_box_->window);
310 } else { // Chromium theme mode.
311 if (GTK_WIDGET_REALIZED(paint_box_)) {
312 gdk_window_lower(paint_box_->window);
313 // The event box won't stay below its children's GdkWindows unless we
314 // toggle the above-child property here. If the event box doesn't stay
315 // below its children then events will be routed to it rather than the
316 // children.
317 gtk_event_box_set_above_child(GTK_EVENT_BOX(event_box_.get()), TRUE);
318 gtk_event_box_set_above_child(GTK_EVENT_BOX(event_box_.get()), FALSE);
319 }
320 }
321 }
322
323 if (sync_ui_util::ShouldShowSyncErrorButton(sync_service_)) {
324 gtk_widget_show(sync_error_button_);
325 } else {
326 gtk_widget_hide(sync_error_button_);
327 }
328
329 // Maybe show the instructions
330 if (show_instructions_) {
331 gtk_widget_hide(bookmark_toolbar_.get());
332 gtk_widget_show(instructions_);
333 } else {
334 gtk_widget_hide(instructions_);
335 gtk_widget_show(bookmark_toolbar_.get());
336 }
337
338 SetChevronState();
339 }
340
341 void BookmarkBarGtk::Hide(bool animate) {
342 UpdateFloatingState();
343
344 // After coming out of fullscreen, the browser window sets the bookmark bar
345 // to the "hidden" state, which means we need to show our minimum height.
346 gtk_widget_show(widget());
347 // Sometimes we get called without a matching call to open. If that happens
348 // then force hide.
349 if (slide_animation_->IsShowing() && animate) {
350 slide_animation_->Hide();
351 } else {
352 gtk_widget_hide(bookmark_hbox_);
353 slide_animation_->Reset(0);
354 AnimationProgressed(slide_animation_.get());
355 }
356 }
357
358 void BookmarkBarGtk::OnStateChanged() {
359 if (sync_ui_util::ShouldShowSyncErrorButton(sync_service_)) {
360 gtk_widget_show(sync_error_button_);
361 } else {
362 gtk_widget_hide(sync_error_button_);
363 }
364 }
365
366 void BookmarkBarGtk::ShowImportDialog() {
367 ImportDialogGtk::Show(window_->window(), browser_->profile(),
368 importer::FAVORITES);
369 }
370
371 void BookmarkBarGtk::EnterFullscreen() {
372 if (ShouldBeFloating())
373 Show(false);
374 else
375 gtk_widget_hide(widget());
376 }
377
378 int BookmarkBarGtk::GetHeight() {
379 return event_box_->allocation.height - kBookmarkBarMinimumHeight;
380 }
381
382 bool BookmarkBarGtk::IsAnimating() {
383 return slide_animation_->is_animating();
384 }
385
386 bool BookmarkBarGtk::OnNewTabPage() {
387 return (browser_ && browser_->GetSelectedTabContents() &&
388 browser_->GetSelectedTabContents()->ShouldShowBookmarkBar());
389 }
390
391 void BookmarkBarGtk::Loaded(BookmarkModel* model) {
392 // If |instructions_| has been nulled, we are in the middle of browser
393 // shutdown. Do nothing.
394 if (!instructions_)
395 return;
396
397 RemoveAllBookmarkButtons();
398 CreateAllBookmarkButtons();
399 }
400
401 void BookmarkBarGtk::BookmarkModelBeingDeleted(BookmarkModel* model) {
402 // The bookmark model should never be deleted before us. This code exists
403 // to check for regressions in shutdown code and not crash.
404 if (!browser_shutdown::ShuttingDownWithoutClosingBrowsers())
405 NOTREACHED();
406
407 // Do minimal cleanup, presumably we'll be deleted shortly.
408 model_->RemoveObserver(this);
409 model_ = NULL;
410 }
411
412 void BookmarkBarGtk::BookmarkNodeMoved(BookmarkModel* model,
413 const BookmarkNode* old_parent,
414 int old_index,
415 const BookmarkNode* new_parent,
416 int new_index) {
417 const BookmarkNode* node = new_parent->GetChild(new_index);
418 BookmarkNodeRemoved(model, old_parent, old_index, node);
419 BookmarkNodeAdded(model, new_parent, new_index);
420 }
421
422 void BookmarkBarGtk::BookmarkNodeAdded(BookmarkModel* model,
423 const BookmarkNode* parent,
424 int index) {
425 const BookmarkNode* node = parent->GetChild(index);
426 if (parent != model_->GetBookmarkBarNode()) {
427 StartThrobbing(node);
428 return;
429 }
430 DCHECK(index >= 0 && index <= GetBookmarkButtonCount());
431
432 GtkToolItem* item = CreateBookmarkToolItem(node);
433 gtk_toolbar_insert(GTK_TOOLBAR(bookmark_toolbar_.get()),
434 item, index);
435 if (node->is_folder())
436 menu_bar_helper_.Add(gtk_bin_get_child(GTK_BIN(item)));
437
438 SetInstructionState();
439 SetChevronState();
440
441 MessageLoop::current()->PostTask(FROM_HERE,
442 method_factory_.NewRunnableMethod(
443 &BookmarkBarGtk::StartThrobbing, node));
444 }
445
446 void BookmarkBarGtk::BookmarkNodeRemoved(BookmarkModel* model,
447 const BookmarkNode* parent,
448 int old_index,
449 const BookmarkNode* node) {
450 if (parent != model_->GetBookmarkBarNode()) {
451 // We only care about nodes on the bookmark bar.
452 return;
453 }
454 DCHECK(old_index >= 0 && old_index < GetBookmarkButtonCount());
455
456 GtkWidget* to_remove = GTK_WIDGET(gtk_toolbar_get_nth_item(
457 GTK_TOOLBAR(bookmark_toolbar_.get()), old_index));
458 if (node->is_folder())
459 menu_bar_helper_.Remove(gtk_bin_get_child(GTK_BIN(to_remove)));
460 gtk_container_remove(GTK_CONTAINER(bookmark_toolbar_.get()),
461 to_remove);
462
463 SetInstructionState();
464 SetChevronState();
465 }
466
467 void BookmarkBarGtk::BookmarkNodeChanged(BookmarkModel* model,
468 const BookmarkNode* node) {
469 if (node->GetParent() != model_->GetBookmarkBarNode()) {
470 // We only care about nodes on the bookmark bar.
471 return;
472 }
473 int index = model_->GetBookmarkBarNode()->IndexOfChild(node);
474 DCHECK(index != -1);
475
476 GtkToolItem* item = gtk_toolbar_get_nth_item(
477 GTK_TOOLBAR(bookmark_toolbar_.get()), index);
478 GtkWidget* button = gtk_bin_get_child(GTK_BIN(item));
479 bookmark_utils::ConfigureButtonForNode(node, model, button, theme_provider_);
480 SetChevronState();
481 }
482
483 void BookmarkBarGtk::BookmarkNodeFavIconLoaded(BookmarkModel* model,
484 const BookmarkNode* node) {
485 BookmarkNodeChanged(model, node);
486 }
487
488 void BookmarkBarGtk::BookmarkNodeChildrenReordered(BookmarkModel* model,
489 const BookmarkNode* node) {
490 if (node != model_->GetBookmarkBarNode())
491 return; // We only care about reordering of the bookmark bar node.
492
493 // Purge and rebuild the bar.
494 RemoveAllBookmarkButtons();
495 CreateAllBookmarkButtons();
496 }
497
498 void BookmarkBarGtk::CreateAllBookmarkButtons() {
499 const BookmarkNode* bar = model_->GetBookmarkBarNode();
500 DCHECK(bar && model_->other_node());
501
502 // Create a button for each of the children on the bookmark bar.
503 for (int i = 0; i < bar->GetChildCount(); ++i) {
504 const BookmarkNode* node = bar->GetChild(i);
505 GtkToolItem* item = CreateBookmarkToolItem(node);
506 gtk_toolbar_insert(GTK_TOOLBAR(bookmark_toolbar_.get()), item, -1);
507 if (node->is_folder())
508 menu_bar_helper_.Add(gtk_bin_get_child(GTK_BIN(item)));
509 }
510
511 bookmark_utils::ConfigureButtonForNode(model_->other_node(),
512 model_, other_bookmarks_button_, theme_provider_);
513
514 SetInstructionState();
515 SetChevronState();
516 }
517
518 void BookmarkBarGtk::SetInstructionState() {
519 show_instructions_ = (model_->GetBookmarkBarNode()->GetChildCount() == 0);
520 if (show_instructions_) {
521 gtk_widget_hide(bookmark_toolbar_.get());
522 gtk_widget_show_all(instructions_);
523 } else {
524 gtk_widget_hide(instructions_);
525 gtk_widget_show(bookmark_toolbar_.get());
526 }
527 }
528
529 void BookmarkBarGtk::SetChevronState() {
530 if (!GTK_WIDGET_VISIBLE(bookmark_hbox_))
531 return;
532
533 if (show_instructions_) {
534 gtk_widget_hide(overflow_button_);
535 return;
536 }
537
538 int extra_space = 0;
539 if (GTK_WIDGET_VISIBLE(overflow_button_))
540 extra_space = overflow_button_->allocation.width;
541
542 int overflow_idx = GetFirstHiddenBookmark(extra_space, NULL);
543 if (overflow_idx == -1)
544 gtk_widget_hide(overflow_button_);
545 else
546 gtk_widget_show_all(overflow_button_);
547 }
548
549 void BookmarkBarGtk::RemoveAllBookmarkButtons() {
550 gtk_util::RemoveAllChildren(bookmark_toolbar_.get());
551 menu_bar_helper_.Clear();
552 menu_bar_helper_.Add(other_bookmarks_button_);
553 menu_bar_helper_.Add(overflow_button_);
554 }
555
556 int BookmarkBarGtk::GetBookmarkButtonCount() {
557 GList* children = gtk_container_get_children(
558 GTK_CONTAINER(bookmark_toolbar_.get()));
559 int count = g_list_length(children);
560 g_list_free(children);
561 return count;
562 }
563
564 void BookmarkBarGtk::SetOverflowButtonAppearance() {
565 GtkWidget* former_child = gtk_bin_get_child(GTK_BIN(overflow_button_));
566 if (former_child)
567 gtk_widget_destroy(former_child);
568
569 GtkWidget* new_child = theme_provider_->UseGtkTheme() ?
570 gtk_arrow_new(GTK_ARROW_DOWN, GTK_SHADOW_NONE) :
571 gtk_image_new_from_pixbuf(ResourceBundle::GetSharedInstance().
572 GetRTLEnabledPixbufNamed(IDR_BOOKMARK_BAR_CHEVRONS));
573
574 gtk_container_add(GTK_CONTAINER(overflow_button_), new_child);
575 SetChevronState();
576 }
577
578 int BookmarkBarGtk::GetFirstHiddenBookmark(
579 int extra_space, std::vector<GtkWidget*>* showing_folders) {
580 int rv = 0;
581 bool overflow = false;
582 GList* toolbar_items =
583 gtk_container_get_children(GTK_CONTAINER(bookmark_toolbar_.get()));
584 for (GList* iter = toolbar_items; iter; iter = g_list_next(iter)) {
585 GtkWidget* tool_item = reinterpret_cast<GtkWidget*>(iter->data);
586 if (gtk_widget_get_direction(tool_item) == GTK_TEXT_DIR_RTL) {
587 overflow = (tool_item->allocation.x + tool_item->style->xthickness <
588 bookmark_toolbar_.get()->allocation.x - extra_space);
589 } else {
590 overflow =
591 (tool_item->allocation.x + tool_item->allocation.width +
592 tool_item->style->xthickness >
593 bookmark_toolbar_.get()->allocation.width +
594 bookmark_toolbar_.get()->allocation.x + extra_space);
595 }
596 overflow = overflow || tool_item->allocation.x == -1;
597
598 if (overflow)
599 break;
600
601 if (showing_folders &&
602 model_->GetBookmarkBarNode()->GetChild(rv)->is_folder()) {
603 showing_folders->push_back(gtk_bin_get_child(GTK_BIN(tool_item)));
604 }
605 rv++;
606 }
607
608 g_list_free(toolbar_items);
609
610 if (!overflow)
611 return -1;
612
613 return rv;
614 }
615
616 bool BookmarkBarGtk::ShouldBeFloating() {
617 return (!IsAlwaysShown() || (window_ && window_->IsFullscreen())) &&
618 window_ && window_->GetDisplayedTabContents() &&
619 window_->GetDisplayedTabContents()->ShouldShowBookmarkBar();
620 }
621
622 void BookmarkBarGtk::UpdateFloatingState() {
623 bool old_floating = floating_;
624 floating_ = ShouldBeFloating();
625 if (floating_ == old_floating)
626 return;
627
628 if (floating_) {
629 #if !defined(OS_CHROMEOS)
630 gtk_event_box_set_visible_window(GTK_EVENT_BOX(paint_box_), TRUE);
631 #endif
632 GdkColor stroke_color = theme_provider_->UseGtkTheme() ?
633 theme_provider_->GetBorderColor() :
634 theme_provider_->GetGdkColor(BrowserThemeProvider::COLOR_NTP_HEADER);
635 gtk_util::ActAsRoundedWindow(paint_box_, stroke_color, kNTPRoundedness,
636 gtk_util::ROUNDED_ALL, gtk_util::BORDER_ALL);
637
638 gtk_alignment_set_padding(GTK_ALIGNMENT(ntp_padding_box_),
639 kTopBottomNTPPadding, kTopBottomNTPPadding,
640 kLeftRightNTPPadding, kLeftRightNTPPadding);
641 gtk_container_set_border_width(GTK_CONTAINER(bookmark_hbox_), kNTPPadding);
642 } else {
643 gtk_util::StopActingAsRoundedWindow(paint_box_);
644 #if !defined(OS_CHROMEOS)
645 gtk_event_box_set_visible_window(GTK_EVENT_BOX(paint_box_), FALSE);
646 #endif
647 gtk_alignment_set_padding(GTK_ALIGNMENT(ntp_padding_box_), 0, 0, 0, 0);
648 gtk_container_set_border_width(GTK_CONTAINER(bookmark_hbox_), 0);
649 }
650
651 UpdateEventBoxPaintability();
652 // |window_| can be NULL during testing.
653 if (window_) {
654 window_->BookmarkBarIsFloating(floating_);
655
656 // Listen for parent size allocations.
657 if (floating_ && widget()->parent) {
658 // Only connect once.
659 if (g_signal_handler_find(widget()->parent, G_SIGNAL_MATCH_FUNC,
660 0, 0, NULL, reinterpret_cast<gpointer>(OnParentSizeAllocateThunk),
661 NULL) == 0) {
662 g_signal_connect(widget()->parent, "size-allocate",
663 G_CALLBACK(OnParentSizeAllocateThunk), this);
664 }
665 }
666 }
667 }
668
669 void BookmarkBarGtk::UpdateEventBoxPaintability() {
670 gtk_widget_set_app_paintable(event_box_.get(),
671 !theme_provider_->UseGtkTheme() || floating_);
672 // When using the GTK+ theme, we need to have the event box be visible so
673 // buttons don't get a halo color from the background. When using Chromium
674 // themes, we want to let the background show through the toolbar.
675
676 #if !defined(OS_CHROMEOS)
677 gtk_event_box_set_visible_window(GTK_EVENT_BOX(event_box_.get()),
678 theme_provider_->UseGtkTheme());
679 #endif
680 }
681
682 void BookmarkBarGtk::PaintEventBox() {
683 gfx::Size tab_contents_size;
684 if (GetTabContentsSize(&tab_contents_size) &&
685 tab_contents_size != last_tab_contents_size_) {
686 last_tab_contents_size_ = tab_contents_size;
687 gtk_widget_queue_draw(event_box_.get());
688 }
689 }
690
691 bool BookmarkBarGtk::GetTabContentsSize(gfx::Size* size) {
692 Browser* browser = browser_;
693 if (!browser) {
694 NOTREACHED();
695 return false;
696 }
697 TabContents* tab_contents = browser->GetSelectedTabContents();
698 if (!tab_contents) {
699 // It is possible to have a browser but no TabContents while under testing,
700 // so don't NOTREACHED() and error the program.
701 return false;
702 }
703 if (!tab_contents->view()) {
704 NOTREACHED();
705 return false;
706 }
707 *size = tab_contents->view()->GetContainerSize();
708 return true;
709 }
710
711 void BookmarkBarGtk::StartThrobbing(const BookmarkNode* node) {
712 const BookmarkNode* parent_on_bb = NULL;
713 for (const BookmarkNode* parent = node; parent;
714 parent = parent->GetParent()) {
715 if (parent->GetParent() == model_->GetBookmarkBarNode()) {
716 parent_on_bb = parent;
717 break;
718 }
719 }
720
721 GtkWidget* widget_to_throb = NULL;
722
723 if (!parent_on_bb) {
724 // Descendant of "Other Bookmarks".
725 widget_to_throb = other_bookmarks_button_;
726 } else {
727 int hidden = GetFirstHiddenBookmark(0, NULL);
728 int idx = model_->GetBookmarkBarNode()->IndexOfChild(parent_on_bb);
729
730 if (hidden >= 0 && hidden <= idx) {
731 widget_to_throb = overflow_button_;
732 } else {
733 if (parent_on_bb->is_url())
734 return;
735 widget_to_throb = gtk_bin_get_child(GTK_BIN(gtk_toolbar_get_nth_item(
736 GTK_TOOLBAR(bookmark_toolbar_.get()), idx)));
737 }
738 }
739
740 SetThrobbingWidget(widget_to_throb);
741 }
742
743 void BookmarkBarGtk::SetThrobbingWidget(GtkWidget* widget) {
744 if (throbbing_widget_) {
745 HoverControllerGtk* hover_controller =
746 HoverControllerGtk::GetHoverControllerGtk(throbbing_widget_);
747 if (hover_controller)
748 hover_controller->StartThrobbing(0);
749
750 g_signal_handlers_disconnect_by_func(
751 throbbing_widget_,
752 reinterpret_cast<gpointer>(OnThrobbingWidgetDestroyThunk),
753 this);
754 g_object_unref(throbbing_widget_);
755 throbbing_widget_ = NULL;
756 }
757
758 if (widget) {
759 throbbing_widget_ = widget;
760 g_object_ref(throbbing_widget_);
761 g_signal_connect(throbbing_widget_, "destroy",
762 G_CALLBACK(OnThrobbingWidgetDestroyThunk), this);
763
764 HoverControllerGtk* hover_controller =
765 HoverControllerGtk::GetHoverControllerGtk(throbbing_widget_);
766 if (hover_controller)
767 hover_controller->StartThrobbing(4);
768 }
769 }
770
771 bool BookmarkBarGtk::IsAlwaysShown() {
772 return profile_->GetPrefs()->GetBoolean(prefs::kShowBookmarkBar);
773 }
774
775 void BookmarkBarGtk::AnimationProgressed(const ui::Animation* animation) {
776 DCHECK_EQ(animation, slide_animation_.get());
777
778 int max_height = ShouldBeFloating() ?
779 kBookmarkBarNTPHeight : kBookmarkBarHeight;
780 gint height =
781 static_cast<gint>(animation->GetCurrentValue() *
782 (max_height - kBookmarkBarMinimumHeight)) +
783 kBookmarkBarMinimumHeight;
784 gtk_widget_set_size_request(event_box_.get(), -1, height);
785 }
786
787 void BookmarkBarGtk::AnimationEnded(const ui::Animation* animation) {
788 DCHECK_EQ(animation, slide_animation_.get());
789
790 if (!slide_animation_->IsShowing()) {
791 gtk_widget_hide(bookmark_hbox_);
792
793 // We can be windowless during unit tests.
794 if (window_) {
795 // Because of our constant resizing and our toolbar/bookmark bar overlap
796 // shenanigans, gtk+ gets confused, partially draws parts of the bookmark
797 // bar into the toolbar and than doesn't queue a redraw to fix it. So do
798 // it manually by telling the toolbar area to redraw itself.
799 window_->QueueToolbarRedraw();
800 }
801 }
802 }
803
804 void BookmarkBarGtk::Observe(NotificationType type,
805 const NotificationSource& source,
806 const NotificationDetails& details) {
807 if (type == NotificationType::BROWSER_THEME_CHANGED) {
808 if (model_) {
809 // Regenerate the bookmark bar with all new objects with their theme
810 // properties set correctly for the new theme.
811 RemoveAllBookmarkButtons();
812 CreateAllBookmarkButtons();
813 } else {
814 DLOG(ERROR) << "Received a theme change notification while we "
815 << "don't have a BookmarkModel. Taking no action.";
816 }
817
818 UpdateEventBoxPaintability();
819
820 GdkColor paint_box_color =
821 theme_provider_->GetGdkColor(BrowserThemeProvider::COLOR_TOOLBAR);
822 gtk_widget_modify_bg(paint_box_, GTK_STATE_NORMAL, &paint_box_color);
823
824 if (floating_) {
825 GdkColor stroke_color = theme_provider_->UseGtkTheme() ?
826 theme_provider_->GetBorderColor() :
827 theme_provider_->GetGdkColor(BrowserThemeProvider::COLOR_NTP_HEADER);
828 gtk_util::SetRoundedWindowBorderColor(paint_box_, stroke_color);
829 }
830
831 SetOverflowButtonAppearance();
832 }
833 }
834
835 GtkWidget* BookmarkBarGtk::CreateBookmarkButton(const BookmarkNode* node) {
836 GtkWidget* button = theme_provider_->BuildChromeButton();
837 bookmark_utils::ConfigureButtonForNode(node, model_, button, theme_provider_);
838
839 // The tool item is also a source for dragging
840 gtk_drag_source_set(button, GDK_BUTTON1_MASK, NULL, 0,
841 static_cast<GdkDragAction>(GDK_ACTION_MOVE | GDK_ACTION_COPY));
842 int target_mask = bookmark_utils::GetCodeMask(node->is_folder());
843 gtk_dnd_util::SetSourceTargetListFromCodeMask(button, target_mask);
844 g_signal_connect(button, "drag-begin",
845 G_CALLBACK(&OnButtonDragBeginThunk), this);
846 g_signal_connect(button, "drag-end",
847 G_CALLBACK(&OnButtonDragEndThunk), this);
848 g_signal_connect(button, "drag-data-get",
849 G_CALLBACK(&OnButtonDragGetThunk), this);
850 // We deliberately don't connect to "drag-data-delete" because the action of
851 // moving a button will regenerate all the contents of the bookmarks bar
852 // anyway.
853
854 if (node->is_url()) {
855 // Connect to 'button-release-event' instead of 'clicked' because we need
856 // access to the modifier keys and we do different things on each
857 // button.
858 g_signal_connect(button, "button-press-event",
859 G_CALLBACK(OnButtonPressedThunk), this);
860 g_signal_connect(button, "clicked",
861 G_CALLBACK(OnClickedThunk), this);
862 gtk_util::SetButtonTriggersNavigation(button);
863 } else {
864 ConnectFolderButtonEvents(button, true);
865 }
866
867 return button;
868 }
869
870 GtkToolItem* BookmarkBarGtk::CreateBookmarkToolItem(const BookmarkNode* node) {
871 GtkWidget* button = CreateBookmarkButton(node);
872 g_object_set_data(G_OBJECT(button), "left-align-popup",
873 reinterpret_cast<void*>(true));
874
875 GtkToolItem* item = gtk_tool_item_new();
876 gtk_container_add(GTK_CONTAINER(item), button);
877 gtk_widget_show_all(GTK_WIDGET(item));
878
879 return item;
880 }
881
882 void BookmarkBarGtk::ConnectFolderButtonEvents(GtkWidget* widget,
883 bool is_tool_item) {
884 // For toolbar items (i.e. not the overflow button or other bookmarks
885 // button), we handle motion and highlighting manually.
886 gtk_drag_dest_set(widget,
887 is_tool_item ? GTK_DEST_DEFAULT_DROP :
888 GTK_DEST_DEFAULT_ALL,
889 NULL,
890 0,
891 kDragAction);
892 gtk_dnd_util::SetDestTargetList(widget, kDestTargetList);
893 g_signal_connect(widget, "drag-data-received",
894 G_CALLBACK(&OnDragReceivedThunk), this);
895 if (is_tool_item) {
896 g_signal_connect(widget, "drag-motion",
897 G_CALLBACK(&OnFolderDragMotionThunk), this);
898 g_signal_connect(widget, "drag-leave",
899 G_CALLBACK(&OnDragLeaveThunk), this);
900 }
901
902 g_signal_connect(widget, "button-press-event",
903 G_CALLBACK(OnButtonPressedThunk), this);
904 g_signal_connect(widget, "clicked",
905 G_CALLBACK(OnFolderClickedThunk), this);
906
907 // Accept middle mouse clicking (which opens all). This must be called after
908 // connecting to "button-press-event" because the handler it attaches stops
909 // the propagation of that signal.
910 gtk_util::SetButtonClickableByMouseButtons(widget, true, true, false);
911 }
912
913 const BookmarkNode* BookmarkBarGtk::GetNodeForToolButton(GtkWidget* widget) {
914 // First check to see if |button| is special cased.
915 if (widget == other_bookmarks_button_)
916 return model_->other_node();
917 else if (widget == event_box_.get() || widget == overflow_button_)
918 return model_->GetBookmarkBarNode();
919
920 // Search the contents of |bookmark_toolbar_| for the corresponding widget
921 // and find its index.
922 GtkWidget* item_to_find = gtk_widget_get_parent(widget);
923 int index_to_use = -1;
924 int index = 0;
925 GList* children = gtk_container_get_children(
926 GTK_CONTAINER(bookmark_toolbar_.get()));
927 for (GList* item = children; item; item = item->next, index++) {
928 if (item->data == item_to_find) {
929 index_to_use = index;
930 break;
931 }
932 }
933 g_list_free(children);
934
935 if (index_to_use != -1)
936 return model_->GetBookmarkBarNode()->GetChild(index_to_use);
937
938 return NULL;
939 }
940
941 void BookmarkBarGtk::PopupMenuForNode(GtkWidget* sender,
942 const BookmarkNode* node,
943 GdkEventButton* event) {
944 if (!model_->IsLoaded()) {
945 // Don't do anything if the model isn't loaded.
946 return;
947 }
948
949 const BookmarkNode* parent = NULL;
950 std::vector<const BookmarkNode*> nodes;
951 if (sender == other_bookmarks_button_) {
952 nodes.push_back(node);
953 parent = model_->GetBookmarkBarNode();
954 } else if (sender != bookmark_toolbar_.get()) {
955 nodes.push_back(node);
956 parent = node->GetParent();
957 } else {
958 parent = model_->GetBookmarkBarNode();
959 nodes.push_back(parent);
960 }
961
962 GtkWindow* window = GTK_WINDOW(gtk_widget_get_toplevel(sender));
963 current_context_menu_controller_.reset(
964 new BookmarkContextMenuController(
965 window, this, profile_, page_navigator_, parent, nodes));
966 current_context_menu_.reset(
967 new MenuGtk(NULL, current_context_menu_controller_->menu_model()));
968 current_context_menu_->PopupAsContext(event->time);
969 }
970
971 gboolean BookmarkBarGtk::OnButtonPressed(GtkWidget* sender,
972 GdkEventButton* event) {
973 last_pressed_coordinates_ = gfx::Point(event->x, event->y);
974
975 if (event->button == 3 && GTK_WIDGET_VISIBLE(bookmark_hbox_)) {
976 const BookmarkNode* node = GetNodeForToolButton(sender);
977 DCHECK(node);
978 DCHECK(page_navigator_);
979 PopupMenuForNode(sender, node, event);
980 }
981
982 return FALSE;
983 }
984
985 gboolean BookmarkBarGtk::OnSyncErrorButtonPressed(GtkWidget* sender,
986 GdkEventButton* event) {
987 if (sender == sync_error_button_) {
988 DCHECK(sync_service_ && !sync_service_->IsManaged());
989 sync_service_->ShowLoginDialog(NULL);
990 }
991
992 return FALSE;
993 }
994
995 void BookmarkBarGtk::OnClicked(GtkWidget* sender) {
996 const BookmarkNode* node = GetNodeForToolButton(sender);
997 DCHECK(node);
998 DCHECK(node->is_url());
999 DCHECK(page_navigator_);
1000
1001 page_navigator_->OpenURL(
1002 node->GetURL(), GURL(),
1003 gtk_util::DispositionForCurrentButtonPressEvent(),
1004 PageTransition::AUTO_BOOKMARK);
1005
1006 UserMetrics::RecordAction(UserMetricsAction("ClickedBookmarkBarURLButton"),
1007 profile_);
1008 }
1009
1010 void BookmarkBarGtk::OnButtonDragBegin(GtkWidget* button,
1011 GdkDragContext* drag_context) {
1012 // The parent tool item might be removed during the drag. Ref it so |button|
1013 // won't get destroyed.
1014 g_object_ref(button->parent);
1015
1016 const BookmarkNode* node = GetNodeForToolButton(button);
1017 DCHECK(!dragged_node_);
1018 dragged_node_ = node;
1019 DCHECK(dragged_node_);
1020
1021 drag_icon_ = bookmark_utils::GetDragRepresentationForNode(
1022 node, model_, theme_provider_);
1023
1024 // We have to jump through some hoops to get the drag icon to line up because
1025 // it is a different size than the button.
1026 GtkRequisition req;
1027 gtk_widget_size_request(drag_icon_, &req);
1028 gfx::Rect button_rect = gtk_util::WidgetBounds(button);
1029 gfx::Point drag_icon_relative =
1030 gfx::Rect(req.width, req.height).CenterPoint().Add(
1031 (last_pressed_coordinates_.Subtract(button_rect.CenterPoint())));
1032 gtk_drag_set_icon_widget(drag_context, drag_icon_,
1033 drag_icon_relative.x(),
1034 drag_icon_relative.y());
1035
1036 // Hide our node, but reserve space for it on the toolbar.
1037 int index = gtk_toolbar_get_item_index(GTK_TOOLBAR(bookmark_toolbar_.get()),
1038 GTK_TOOL_ITEM(button->parent));
1039 gtk_widget_hide(button);
1040 toolbar_drop_item_ = CreateBookmarkToolItem(dragged_node_);
1041 g_object_ref_sink(GTK_OBJECT(toolbar_drop_item_));
1042 gtk_toolbar_set_drop_highlight_item(GTK_TOOLBAR(bookmark_toolbar_.get()),
1043 GTK_TOOL_ITEM(toolbar_drop_item_), index);
1044 // Make sure it stays hidden for the duration of the drag.
1045 gtk_widget_set_no_show_all(button, TRUE);
1046 }
1047
1048 void BookmarkBarGtk::OnButtonDragEnd(GtkWidget* button,
1049 GdkDragContext* drag_context) {
1050 gtk_widget_show(button);
1051 gtk_widget_set_no_show_all(button, FALSE);
1052
1053 ClearToolbarDropHighlighting();
1054
1055 DCHECK(dragged_node_);
1056 dragged_node_ = NULL;
1057
1058 DCHECK(drag_icon_);
1059 gtk_widget_destroy(drag_icon_);
1060 drag_icon_ = NULL;
1061
1062 g_object_unref(button->parent);
1063 }
1064
1065 void BookmarkBarGtk::OnButtonDragGet(GtkWidget* widget, GdkDragContext* context,
1066 GtkSelectionData* selection_data,
1067 guint target_type, guint time) {
1068 const BookmarkNode* node = bookmark_utils::BookmarkNodeForWidget(widget);
1069 bookmark_utils::WriteBookmarkToSelection(node, selection_data, target_type,
1070 profile_);
1071 }
1072
1073 void BookmarkBarGtk::OnFolderClicked(GtkWidget* sender) {
1074 // Stop its throbbing, if any.
1075 HoverControllerGtk* hover_controller =
1076 HoverControllerGtk::GetHoverControllerGtk(sender);
1077 if (hover_controller)
1078 hover_controller->StartThrobbing(0);
1079
1080 GdkEvent* event = gtk_get_current_event();
1081 if (event->button.button == 1) {
1082 PopupForButton(sender);
1083 } else if (event->button.button == 2) {
1084 const BookmarkNode* node = GetNodeForToolButton(sender);
1085 bookmark_utils::OpenAll(window_->GetNativeHandle(),
1086 profile_, page_navigator_,
1087 node, NEW_BACKGROUND_TAB);
1088 }
1089 }
1090
1091 gboolean BookmarkBarGtk::ItemDraggedOverToolbar(GdkDragContext* context,
1092 int index,
1093 guint time) {
1094 GdkAtom target_type =
1095 gtk_drag_dest_find_target(bookmark_toolbar_.get(), context, NULL);
1096 if (target_type == GDK_NONE) {
1097 // We shouldn't act like a drop target when something that we can't deal
1098 // with is dragged over the toolbar.
1099 return FALSE;
1100 }
1101
1102 if (!toolbar_drop_item_) {
1103 if (dragged_node_) {
1104 toolbar_drop_item_ = CreateBookmarkToolItem(dragged_node_);
1105 g_object_ref_sink(GTK_OBJECT(toolbar_drop_item_));
1106 } else {
1107 // Create a fake item the size of other_node().
1108 //
1109 // TODO(erg): Maybe somehow figure out the real size for the drop target?
1110 toolbar_drop_item_ =
1111 CreateBookmarkToolItem(model_->other_node());
1112 g_object_ref_sink(GTK_OBJECT(toolbar_drop_item_));
1113 }
1114 }
1115
1116 gtk_toolbar_set_drop_highlight_item(GTK_TOOLBAR(bookmark_toolbar_.get()),
1117 GTK_TOOL_ITEM(toolbar_drop_item_),
1118 index);
1119 if (target_type ==
1120 gtk_dnd_util::GetAtomForTarget(gtk_dnd_util::CHROME_BOOKMARK_ITEM)) {
1121 gdk_drag_status(context, GDK_ACTION_MOVE, time);
1122 } else {
1123 gdk_drag_status(context, GDK_ACTION_COPY, time);
1124 }
1125
1126 return TRUE;
1127 }
1128
1129 gboolean BookmarkBarGtk::OnToolbarDragMotion(GtkWidget* toolbar,
1130 GdkDragContext* context,
1131 gint x,
1132 gint y,
1133 guint time) {
1134 gint index = gtk_toolbar_get_drop_index(GTK_TOOLBAR(toolbar), x, y);
1135 return ItemDraggedOverToolbar(context, index, time);
1136 }
1137
1138 int BookmarkBarGtk::GetToolbarIndexForDragOverFolder(
1139 GtkWidget* button, gint x) {
1140 int margin = std::min(15, static_cast<int>(0.3 * button->allocation.width));
1141 if (x > margin && x < (button->allocation.width - margin))
1142 return -1;
1143
1144 gint index = gtk_toolbar_get_item_index(GTK_TOOLBAR(bookmark_toolbar_.get()),
1145 GTK_TOOL_ITEM(button->parent));
1146 if (x > margin)
1147 index++;
1148 return index;
1149 }
1150
1151 gboolean BookmarkBarGtk::OnFolderDragMotion(GtkWidget* button,
1152 GdkDragContext* context,
1153 gint x,
1154 gint y,
1155 guint time) {
1156 GdkAtom target_type = gtk_drag_dest_find_target(button, context, NULL);
1157 if (target_type == GDK_NONE)
1158 return FALSE;
1159
1160 int index = GetToolbarIndexForDragOverFolder(button, x);
1161 if (index < 0) {
1162 ClearToolbarDropHighlighting();
1163
1164 // Drag is over middle of folder.
1165 gtk_drag_highlight(button);
1166 if (target_type ==
1167 gtk_dnd_util::GetAtomForTarget(gtk_dnd_util::CHROME_BOOKMARK_ITEM)) {
1168 gdk_drag_status(context, GDK_ACTION_MOVE, time);
1169 } else {
1170 gdk_drag_status(context, GDK_ACTION_COPY, time);
1171 }
1172
1173 return TRUE;
1174 }
1175
1176 // Remove previous highlighting.
1177 gtk_drag_unhighlight(button);
1178 return ItemDraggedOverToolbar(context, index, time);
1179 }
1180
1181 void BookmarkBarGtk::ClearToolbarDropHighlighting() {
1182 if (toolbar_drop_item_) {
1183 g_object_unref(toolbar_drop_item_);
1184 toolbar_drop_item_ = NULL;
1185 }
1186
1187 gtk_toolbar_set_drop_highlight_item(GTK_TOOLBAR(bookmark_toolbar_.get()),
1188 NULL, 0);
1189 }
1190
1191 void BookmarkBarGtk::OnDragLeave(GtkWidget* sender,
1192 GdkDragContext* context,
1193 guint time) {
1194 if (GTK_IS_BUTTON(sender))
1195 gtk_drag_unhighlight(sender);
1196
1197 ClearToolbarDropHighlighting();
1198 }
1199
1200 void BookmarkBarGtk::OnToolbarSizeAllocate(GtkWidget* widget,
1201 GtkAllocation* allocation) {
1202 if (bookmark_toolbar_.get()->allocation.width ==
1203 last_allocation_width_) {
1204 // If the width hasn't changed, then the visibility of the chevron
1205 // doesn't need to change. This check prevents us from getting stuck in a
1206 // loop where allocates are queued indefinitely while the visibility of
1207 // overflow chevron toggles without actual resizes of the toolbar.
1208 return;
1209 }
1210 last_allocation_width_ = bookmark_toolbar_.get()->allocation.width;
1211
1212 SetChevronState();
1213 }
1214
1215 void BookmarkBarGtk::OnDragReceived(GtkWidget* widget,
1216 GdkDragContext* context,
1217 gint x, gint y,
1218 GtkSelectionData* selection_data,
1219 guint target_type, guint time) {
1220 gboolean dnd_success = FALSE;
1221 gboolean delete_selection_data = FALSE;
1222
1223 const BookmarkNode* dest_node = model_->GetBookmarkBarNode();
1224 gint index;
1225 if (widget == bookmark_toolbar_.get()) {
1226 index = gtk_toolbar_get_drop_index(
1227 GTK_TOOLBAR(bookmark_toolbar_.get()), x, y);
1228 } else if (widget == instructions_) {
1229 dest_node = model_->GetBookmarkBarNode();
1230 index = 0;
1231 } else {
1232 index = GetToolbarIndexForDragOverFolder(widget, x);
1233 if (index < 0) {
1234 dest_node = GetNodeForToolButton(widget);
1235 index = dest_node->GetChildCount();
1236 }
1237 }
1238
1239 switch (target_type) {
1240 case gtk_dnd_util::CHROME_BOOKMARK_ITEM: {
1241 std::vector<const BookmarkNode*> nodes =
1242 bookmark_utils::GetNodesFromSelection(context, selection_data,
1243 target_type,
1244 profile_,
1245 &delete_selection_data,
1246 &dnd_success);
1247 DCHECK(!nodes.empty());
1248 for (std::vector<const BookmarkNode*>::iterator it = nodes.begin();
1249 it != nodes.end(); ++it) {
1250 model_->Move(*it, dest_node, index);
1251 index = dest_node->IndexOfChild(*it) + 1;
1252 }
1253 break;
1254 }
1255
1256 case gtk_dnd_util::CHROME_NAMED_URL: {
1257 dnd_success = bookmark_utils::CreateNewBookmarkFromNamedUrl(
1258 selection_data, model_, dest_node, index);
1259 break;
1260 }
1261
1262 case gtk_dnd_util::TEXT_URI_LIST: {
1263 dnd_success = bookmark_utils::CreateNewBookmarksFromURIList(
1264 selection_data, model_, dest_node, index);
1265 break;
1266 }
1267
1268 case gtk_dnd_util::NETSCAPE_URL: {
1269 dnd_success = bookmark_utils::CreateNewBookmarkFromNetscapeURL(
1270 selection_data, model_, dest_node, index);
1271 break;
1272 }
1273
1274 case gtk_dnd_util::TEXT_PLAIN: {
1275 guchar* text = gtk_selection_data_get_text(selection_data);
1276 if (!text)
1277 break;
1278 GURL url(reinterpret_cast<char*>(text));
1279 g_free(text);
1280 // TODO(estade): It would be nice to head this case off at drag motion,
1281 // so that it doesn't look like we can drag onto the bookmark bar.
1282 if (!url.is_valid())
1283 break;
1284 std::string title = bookmark_utils::GetNameForURL(url);
1285 model_->AddURL(dest_node, index, UTF8ToUTF16(title), url);
1286 dnd_success = TRUE;
1287 break;
1288 }
1289 }
1290
1291 gtk_drag_finish(context, dnd_success, delete_selection_data, time);
1292 }
1293
1294 gboolean BookmarkBarGtk::OnEventBoxExpose(GtkWidget* widget,
1295 GdkEventExpose* event) {
1296 GtkThemeProvider* theme_provider = theme_provider_;
1297
1298 // We don't need to render the toolbar image in GTK mode, except when
1299 // detached.
1300 if (theme_provider->UseGtkTheme() && !floating_)
1301 return FALSE;
1302
1303 if (!floating_) {
1304 cairo_t* cr = gdk_cairo_create(GDK_DRAWABLE(widget->window));
1305 gdk_cairo_rectangle(cr, &event->area);
1306 cairo_clip(cr);
1307
1308 // Paint the background theme image.
1309 gfx::Point tabstrip_origin =
1310 tabstrip_origin_provider_->GetTabStripOriginForWidget(widget);
1311 gtk_util::DrawThemedToolbarBackground(widget, cr, event, tabstrip_origin,
1312 theme_provider);
1313
1314 cairo_destroy(cr);
1315 } else {
1316 gfx::Size tab_contents_size;
1317 if (!GetTabContentsSize(&tab_contents_size))
1318 return FALSE;
1319 gfx::CanvasSkiaPaint canvas(event, true);
1320
1321 gfx::Rect area = GTK_WIDGET_NO_WINDOW(widget) ?
1322 gfx::Rect(widget->allocation) :
1323 gfx::Rect(0, 0, widget->allocation.width, widget->allocation.height);
1324 NtpBackgroundUtil::PaintBackgroundDetachedMode(theme_provider, &canvas,
1325 area, tab_contents_size.height());
1326 }
1327
1328 return FALSE; // Propagate expose to children.
1329 }
1330
1331 void BookmarkBarGtk::OnEventBoxDestroy(GtkWidget* widget) {
1332 if (model_)
1333 model_->RemoveObserver(this);
1334
1335 if (sync_service_)
1336 sync_service_->RemoveObserver(this);
1337 }
1338
1339 void BookmarkBarGtk::OnParentSizeAllocate(GtkWidget* widget,
1340 GtkAllocation* allocation) {
1341 // In floating mode, our layout depends on the size of the tab contents.
1342 // We get the size-allocate signal before the tab contents does, hence we
1343 // need to post a delayed task so we will paint correctly. Note that
1344 // gtk_widget_queue_draw by itself does not work, despite that it claims to
1345 // be asynchronous.
1346 if (floating_) {
1347 MessageLoop::current()->PostTask(FROM_HERE,
1348 method_factory_.NewRunnableMethod(
1349 &BookmarkBarGtk::PaintEventBox));
1350 }
1351 }
1352
1353 void BookmarkBarGtk::OnThrobbingWidgetDestroy(GtkWidget* widget) {
1354 SetThrobbingWidget(NULL);
1355 }
1356
1357 // MenuBarHelper::Delegate implementation --------------------------------------
1358 void BookmarkBarGtk::PopupForButton(GtkWidget* button) {
1359 const BookmarkNode* node = GetNodeForToolButton(button);
1360 DCHECK(node);
1361 DCHECK(page_navigator_);
1362
1363 int first_hidden = GetFirstHiddenBookmark(0, NULL);
1364 if (first_hidden == -1) {
1365 // No overflow exists: don't show anything for the overflow button.
1366 if (button == overflow_button_)
1367 return;
1368 } else {
1369 // Overflow exists: don't show anything for an overflowed folder button.
1370 if (button != overflow_button_ && button != other_bookmarks_button_ &&
1371 node->GetParent()->IndexOfChild(node) >= first_hidden) {
1372 return;
1373 }
1374 }
1375
1376 current_menu_.reset(
1377 new BookmarkMenuController(browser_, profile_, page_navigator_,
1378 GTK_WINDOW(gtk_widget_get_toplevel(button)),
1379 node,
1380 button == overflow_button_ ?
1381 first_hidden : 0));
1382 menu_bar_helper_.MenuStartedShowing(button, current_menu_->widget());
1383 GdkEvent* event = gtk_get_current_event();
1384 current_menu_->Popup(button, event->button.button, event->button.time);
1385 gdk_event_free(event);
1386 }
1387
1388 void BookmarkBarGtk::PopupForButtonNextTo(GtkWidget* button,
1389 GtkMenuDirectionType dir) {
1390 const BookmarkNode* relative_node = GetNodeForToolButton(button);
1391 DCHECK(relative_node);
1392
1393 // Find out the order of the buttons.
1394 std::vector<GtkWidget*> folder_list;
1395 const int first_hidden = GetFirstHiddenBookmark(0, &folder_list);
1396 if (first_hidden != -1)
1397 folder_list.push_back(overflow_button_);
1398 folder_list.push_back(other_bookmarks_button_);
1399
1400 // Find the position of |button|.
1401 int button_idx = -1;
1402 for (size_t i = 0; i < folder_list.size(); ++i) {
1403 if (folder_list[i] == button) {
1404 button_idx = i;
1405 break;
1406 }
1407 }
1408 DCHECK_NE(button_idx, -1);
1409
1410 // Find the GtkWidget* for the actual target button.
1411 int shift = dir == GTK_MENU_DIR_PARENT ? -1 : 1;
1412 button_idx = (button_idx + shift + folder_list.size()) % folder_list.size();
1413 PopupForButton(folder_list[button_idx]);
1414 }
1415
1416 void BookmarkBarGtk::CloseMenu() {
1417 current_context_menu_->Cancel();
1418 }
OLDNEW
« no previous file with comments | « chrome/browser/gtk/bookmark_bar_gtk.h ('k') | chrome/browser/gtk/bookmark_bar_gtk_interactive_uitest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698