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