| OLD | NEW |
| 1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chrome/browser/gtk/bookmark_menu_controller_gtk.h" | 5 #include "chrome/browser/gtk/bookmark_menu_controller_gtk.h" |
| 6 | 6 |
| 7 #include <gtk/gtk.h> | 7 #include <gtk/gtk.h> |
| 8 | 8 |
| 9 #include "app/gfx/gtk_util.h" | 9 #include "app/gfx/gtk_util.h" |
| 10 #include "app/gtk_dnd_util.h" | 10 #include "app/gtk_dnd_util.h" |
| (...skipping 82 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 93 profile_(profile), | 93 profile_(profile), |
| 94 page_navigator_(navigator), | 94 page_navigator_(navigator), |
| 95 parent_window_(window), | 95 parent_window_(window), |
| 96 model_(profile->GetBookmarkModel()), | 96 model_(profile->GetBookmarkModel()), |
| 97 node_(node), | 97 node_(node), |
| 98 ignore_button_release_(false), | 98 ignore_button_release_(false), |
| 99 triggering_widget_(NULL) { | 99 triggering_widget_(NULL) { |
| 100 menu_ = gtk_menu_new(); | 100 menu_ = gtk_menu_new(); |
| 101 BuildMenu(node, start_child_index, menu_); | 101 BuildMenu(node, start_child_index, menu_); |
| 102 g_signal_connect(menu_, "hide", | 102 g_signal_connect(menu_, "hide", |
| 103 G_CALLBACK(OnMenuHidden), this); | 103 G_CALLBACK(OnMenuHiddenThunk), this); |
| 104 gtk_widget_show_all(menu_); | 104 gtk_widget_show_all(menu_); |
| 105 } | 105 } |
| 106 | 106 |
| 107 BookmarkMenuController::~BookmarkMenuController() { | 107 BookmarkMenuController::~BookmarkMenuController() { |
| 108 profile_->GetBookmarkModel()->RemoveObserver(this); | 108 profile_->GetBookmarkModel()->RemoveObserver(this); |
| 109 gtk_menu_popdown(GTK_MENU(menu_)); | 109 gtk_menu_popdown(GTK_MENU(menu_)); |
| 110 } | 110 } |
| 111 | 111 |
| 112 void BookmarkMenuController::Popup(GtkWidget* widget, gint button_type, | 112 void BookmarkMenuController::Popup(GtkWidget* widget, gint button_type, |
| 113 guint32 timestamp) { | 113 guint32 timestamp) { |
| (...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 151 node->GetURL(), GURL(), disposition, PageTransition::AUTO_BOOKMARK); | 151 node->GetURL(), GURL(), disposition, PageTransition::AUTO_BOOKMARK); |
| 152 } | 152 } |
| 153 | 153 |
| 154 void BookmarkMenuController::BuildMenu(const BookmarkNode* parent, | 154 void BookmarkMenuController::BuildMenu(const BookmarkNode* parent, |
| 155 int start_child_index, | 155 int start_child_index, |
| 156 GtkWidget* menu) { | 156 GtkWidget* menu) { |
| 157 DCHECK(!parent->GetChildCount() || | 157 DCHECK(!parent->GetChildCount() || |
| 158 start_child_index < parent->GetChildCount()); | 158 start_child_index < parent->GetChildCount()); |
| 159 | 159 |
| 160 g_signal_connect(menu, "button-press-event", | 160 g_signal_connect(menu, "button-press-event", |
| 161 G_CALLBACK(OnButtonPressed), this); | 161 G_CALLBACK(OnButtonPressedThunk), this); |
| 162 | 162 |
| 163 for (int i = start_child_index; i < parent->GetChildCount(); ++i) { | 163 for (int i = start_child_index; i < parent->GetChildCount(); ++i) { |
| 164 const BookmarkNode* node = parent->GetChild(i); | 164 const BookmarkNode* node = parent->GetChild(i); |
| 165 | 165 |
| 166 // This breaks on word boundaries. Ideally we would break on character | 166 // This breaks on word boundaries. Ideally we would break on character |
| 167 // boundaries. | 167 // boundaries. |
| 168 std::wstring elided_name = | 168 std::wstring elided_name = |
| 169 l10n_util::TruncateString(node->GetTitle(), kMaxChars); | 169 l10n_util::TruncateString(node->GetTitle(), kMaxChars); |
| 170 GtkWidget* menu_item = | 170 GtkWidget* menu_item = |
| 171 gtk_image_menu_item_new_with_label(WideToUTF8(elided_name).c_str()); | 171 gtk_image_menu_item_new_with_label(WideToUTF8(elided_name).c_str()); |
| 172 g_object_set_data(G_OBJECT(menu_item), "bookmark-node", AsVoid(node)); | 172 g_object_set_data(G_OBJECT(menu_item), "bookmark-node", AsVoid(node)); |
| 173 SetImageMenuItem(menu_item, node, profile_->GetBookmarkModel()); | 173 SetImageMenuItem(menu_item, node, profile_->GetBookmarkModel()); |
| 174 gtk_util::SetAlwaysShowImage(menu_item); | 174 gtk_util::SetAlwaysShowImage(menu_item); |
| 175 | 175 |
| 176 g_signal_connect(menu_item, "button-release-event", | 176 g_signal_connect(menu_item, "button-release-event", |
| 177 G_CALLBACK(OnButtonReleased), this); | 177 G_CALLBACK(OnButtonReleasedThunk), this); |
| 178 if (node->is_url()) { | 178 if (node->is_url()) { |
| 179 g_signal_connect(menu_item, "activate", | 179 g_signal_connect(menu_item, "activate", |
| 180 G_CALLBACK(OnMenuItemActivated), this); | 180 G_CALLBACK(OnMenuItemActivatedThunk), this); |
| 181 } else if (node->is_folder()) { | 181 } else if (node->is_folder()) { |
| 182 GtkWidget* submenu = gtk_menu_new(); | 182 GtkWidget* submenu = gtk_menu_new(); |
| 183 BuildMenu(node, 0, submenu); | 183 BuildMenu(node, 0, submenu); |
| 184 gtk_menu_item_set_submenu(GTK_MENU_ITEM(menu_item), submenu); | 184 gtk_menu_item_set_submenu(GTK_MENU_ITEM(menu_item), submenu); |
| 185 } else { | 185 } else { |
| 186 NOTREACHED(); | 186 NOTREACHED(); |
| 187 } | 187 } |
| 188 | 188 |
| 189 gtk_drag_source_set(menu_item, GDK_BUTTON1_MASK, NULL, 0, | 189 gtk_drag_source_set(menu_item, GDK_BUTTON1_MASK, NULL, 0, |
| 190 static_cast<GdkDragAction>(GDK_ACTION_COPY | GDK_ACTION_LINK)); | 190 static_cast<GdkDragAction>(GDK_ACTION_COPY | GDK_ACTION_LINK)); |
| 191 int target_mask = gtk_dnd_util::CHROME_BOOKMARK_ITEM; | 191 int target_mask = gtk_dnd_util::CHROME_BOOKMARK_ITEM; |
| 192 if (node->is_url()) | 192 if (node->is_url()) |
| 193 target_mask |= gtk_dnd_util::TEXT_URI_LIST | gtk_dnd_util::NETSCAPE_URL; | 193 target_mask |= gtk_dnd_util::TEXT_URI_LIST | gtk_dnd_util::NETSCAPE_URL; |
| 194 gtk_dnd_util::SetSourceTargetListFromCodeMask(menu_item, target_mask); | 194 gtk_dnd_util::SetSourceTargetListFromCodeMask(menu_item, target_mask); |
| 195 g_signal_connect(menu_item, "drag-begin", | 195 g_signal_connect(menu_item, "drag-begin", |
| 196 G_CALLBACK(&OnMenuItemDragBegin), this); | 196 G_CALLBACK(&OnMenuItemDragBeginThunk), this); |
| 197 g_signal_connect(menu_item, "drag-end", | 197 g_signal_connect(menu_item, "drag-end", |
| 198 G_CALLBACK(&OnMenuItemDragEnd), this); | 198 G_CALLBACK(&OnMenuItemDragEndThunk), this); |
| 199 g_signal_connect(menu_item, "drag-data-get", | 199 g_signal_connect(menu_item, "drag-data-get", |
| 200 G_CALLBACK(&OnMenuItemDragGet), this); | 200 G_CALLBACK(&OnMenuItemDragGetThunk), this); |
| 201 | 201 |
| 202 // It is important to connect to this signal after setting up the drag | 202 // It is important to connect to this signal after setting up the drag |
| 203 // source because we only want to stifle the menu's default handler and | 203 // source because we only want to stifle the menu's default handler and |
| 204 // not the handler that the drag source uses. | 204 // not the handler that the drag source uses. |
| 205 if (node->is_folder()) { | 205 if (node->is_folder()) { |
| 206 g_signal_connect(menu_item, "button-press-event", | 206 g_signal_connect(menu_item, "button-press-event", |
| 207 G_CALLBACK(OnFolderButtonPressed), this); | 207 G_CALLBACK(OnFolderButtonPressedThunk), this); |
| 208 } | 208 } |
| 209 | 209 |
| 210 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item); | 210 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item); |
| 211 node_to_menu_widget_map_[node] = menu_item; | 211 node_to_menu_widget_map_[node] = menu_item; |
| 212 } | 212 } |
| 213 | 213 |
| 214 if (parent->GetChildCount() == 0) { | 214 if (parent->GetChildCount() == 0) { |
| 215 GtkWidget* empty_menu = gtk_menu_item_new_with_label( | 215 GtkWidget* empty_menu = gtk_menu_item_new_with_label( |
| 216 l10n_util::GetStringUTF8(IDS_MENU_EMPTY_SUBMENU).c_str()); | 216 l10n_util::GetStringUTF8(IDS_MENU_EMPTY_SUBMENU).c_str()); |
| 217 gtk_widget_set_sensitive(empty_menu, FALSE); | 217 gtk_widget_set_sensitive(empty_menu, FALSE); |
| 218 g_object_set_data(G_OBJECT(menu), "parent-node", AsVoid(parent)); | 218 g_object_set_data(G_OBJECT(menu), "parent-node", AsVoid(parent)); |
| 219 gtk_menu_shell_append(GTK_MENU_SHELL(menu), empty_menu); | 219 gtk_menu_shell_append(GTK_MENU_SHELL(menu), empty_menu); |
| 220 } | 220 } |
| 221 } | 221 } |
| 222 | 222 |
| 223 // static | |
| 224 gboolean BookmarkMenuController::OnButtonPressed( | 223 gboolean BookmarkMenuController::OnButtonPressed( |
| 225 GtkWidget* sender, | 224 GtkWidget* sender, |
| 226 GdkEventButton* event, | 225 GdkEventButton* event) { |
| 227 BookmarkMenuController* controller) { | 226 ignore_button_release_ = false; |
| 228 controller->ignore_button_release_ = false; | |
| 229 GtkMenuShell* menu_shell = GTK_MENU_SHELL(sender); | 227 GtkMenuShell* menu_shell = GTK_MENU_SHELL(sender); |
| 230 | 228 |
| 231 if (event->button == 3) { | 229 if (event->button == 3) { |
| 232 // If the cursor is outside our bounds, pass this event up to the parent. | 230 // If the cursor is outside our bounds, pass this event up to the parent. |
| 233 if (!gtk_util::WidgetContainsCursor(sender)) { | 231 if (!gtk_util::WidgetContainsCursor(sender)) { |
| 234 if (menu_shell->parent_menu_shell) { | 232 if (menu_shell->parent_menu_shell) { |
| 235 return OnButtonPressed(menu_shell->parent_menu_shell, event, | 233 return OnButtonPressed(menu_shell->parent_menu_shell, event); |
| 236 controller); | |
| 237 } else { | 234 } else { |
| 238 // We are the top level menu; we can propagate no further. | 235 // We are the top level menu; we can propagate no further. |
| 239 return FALSE; | 236 return FALSE; |
| 240 } | 237 } |
| 241 } | 238 } |
| 242 | 239 |
| 243 // This will return NULL if we are not an empty menu. | 240 // This will return NULL if we are not an empty menu. |
| 244 const BookmarkNode* parent = GetParentNodeFromEmptyMenu(sender); | 241 const BookmarkNode* parent = GetParentNodeFromEmptyMenu(sender); |
| 245 bool is_empty_menu = !!parent; | 242 bool is_empty_menu = !!parent; |
| 246 // If there is no active menu item and we are not an empty menu, then do | 243 // If there is no active menu item and we are not an empty menu, then do |
| 247 // nothing. | 244 // nothing. |
| 248 GtkWidget* menu_item = menu_shell->active_menu_item; | 245 GtkWidget* menu_item = menu_shell->active_menu_item; |
| 249 if (!is_empty_menu && !menu_item) | 246 if (!is_empty_menu && !menu_item) |
| 250 return FALSE; | 247 return FALSE; |
| 251 | 248 |
| 252 const BookmarkNode* node = | 249 const BookmarkNode* node = |
| 253 menu_item ? GetNodeFromMenuItem(menu_item) : NULL; | 250 menu_item ? GetNodeFromMenuItem(menu_item) : NULL; |
| 254 DCHECK_NE(is_empty_menu, !!node); | 251 DCHECK_NE(is_empty_menu, !!node); |
| 255 if (!is_empty_menu) | 252 if (!is_empty_menu) |
| 256 parent = node->GetParent(); | 253 parent = node->GetParent(); |
| 257 | 254 |
| 258 // Show the right click menu and stop processing this button event. | 255 // Show the right click menu and stop processing this button event. |
| 259 std::vector<const BookmarkNode*> nodes; | 256 std::vector<const BookmarkNode*> nodes; |
| 260 if (node) | 257 if (node) |
| 261 nodes.push_back(node); | 258 nodes.push_back(node); |
| 262 controller->context_menu_controller_.reset( | 259 context_menu_controller_.reset( |
| 263 new BookmarkContextMenuController( | 260 new BookmarkContextMenuController( |
| 264 controller->parent_window_, controller, controller->profile_, | 261 parent_window_, this, profile_, |
| 265 controller->page_navigator_, parent, nodes, | 262 page_navigator_, parent, nodes, |
| 266 BookmarkContextMenuController::BOOKMARK_BAR)); | 263 BookmarkContextMenuController::BOOKMARK_BAR)); |
| 267 controller->context_menu_.reset( | 264 context_menu_.reset( |
| 268 new MenuGtk(NULL, controller->context_menu_controller_->menu_model())); | 265 new MenuGtk(NULL, context_menu_controller_->menu_model())); |
| 269 | 266 |
| 270 // Our bookmark folder menu loses the grab to the context menu. When the | 267 // Our bookmark folder menu loses the grab to the context menu. When the |
| 271 // context menu is hidden, re-assert our grab. | 268 // context menu is hidden, re-assert our grab. |
| 272 GtkWidget* grabbing_menu = gtk_grab_get_current(); | 269 GtkWidget* grabbing_menu = gtk_grab_get_current(); |
| 273 g_object_ref(grabbing_menu); | 270 g_object_ref(grabbing_menu); |
| 274 g_signal_connect(controller->context_menu_->widget(), "hide", | 271 g_signal_connect(context_menu_->widget(), "hide", |
| 275 G_CALLBACK(OnContextMenuHide), grabbing_menu); | 272 G_CALLBACK(OnContextMenuHide), grabbing_menu); |
| 276 | 273 |
| 277 controller->context_menu_->PopupAsContext(event->time); | 274 context_menu_->PopupAsContext(event->time); |
| 278 return TRUE; | 275 return TRUE; |
| 279 } | 276 } |
| 280 | 277 |
| 281 return FALSE; | 278 return FALSE; |
| 282 } | 279 } |
| 283 | 280 |
| 284 // static | |
| 285 gboolean BookmarkMenuController::OnButtonReleased( | 281 gboolean BookmarkMenuController::OnButtonReleased( |
| 286 GtkWidget* sender, | 282 GtkWidget* sender, |
| 287 GdkEventButton* event, | 283 GdkEventButton* event) { |
| 288 BookmarkMenuController* controller) { | 284 if (ignore_button_release_) { |
| 289 if (controller->ignore_button_release_) { | |
| 290 // Don't handle this message; it was a drag. | 285 // Don't handle this message; it was a drag. |
| 291 controller->ignore_button_release_ = false; | 286 ignore_button_release_ = false; |
| 292 return FALSE; | 287 return FALSE; |
| 293 } | 288 } |
| 294 | 289 |
| 295 // Releasing either button 1 or 2 should trigger the bookmark. | 290 // Releasing either button 1 or 2 should trigger the bookmark. |
| 296 if (!gtk_menu_item_get_submenu(GTK_MENU_ITEM(sender))) { | 291 if (!gtk_menu_item_get_submenu(GTK_MENU_ITEM(sender))) { |
| 297 // The menu item is a link node. | 292 // The menu item is a link node. |
| 298 if (event->button == 1 || event->button == 2) { | 293 if (event->button == 1 || event->button == 2) { |
| 299 WindowOpenDisposition disposition = | 294 WindowOpenDisposition disposition = |
| 300 event_utils::DispositionFromEventFlags(event->state); | 295 event_utils::DispositionFromEventFlags(event->state); |
| 301 controller->NavigateToMenuItem(sender, disposition); | 296 NavigateToMenuItem(sender, disposition); |
| 302 | 297 |
| 303 // We need to manually dismiss the popup menu because we're overriding | 298 // We need to manually dismiss the popup menu because we're overriding |
| 304 // button-release-event. | 299 // button-release-event. |
| 305 gtk_menu_popdown(GTK_MENU(controller->menu_)); | 300 gtk_menu_popdown(GTK_MENU(menu_)); |
| 306 return TRUE; | 301 return TRUE; |
| 307 } | 302 } |
| 308 } else { | 303 } else { |
| 309 // The menu item is a folder node. | 304 // The menu item is a folder node. |
| 310 if (event->button == 1) { | 305 if (event->button == 1) { |
| 311 gtk_menu_popup(GTK_MENU(gtk_menu_item_get_submenu(GTK_MENU_ITEM(sender))), | 306 gtk_menu_popup(GTK_MENU(gtk_menu_item_get_submenu(GTK_MENU_ITEM(sender))), |
| 312 sender->parent, sender, NULL, NULL, | 307 sender->parent, sender, NULL, NULL, |
| 313 event->button, event->time); | 308 event->button, event->time); |
| 314 } | 309 } |
| 315 } | 310 } |
| 316 | 311 |
| 317 return FALSE; | 312 return FALSE; |
| 318 } | 313 } |
| 319 | 314 |
| 320 // static | |
| 321 gboolean BookmarkMenuController::OnFolderButtonPressed( | 315 gboolean BookmarkMenuController::OnFolderButtonPressed( |
| 322 GtkWidget* sender, | 316 GtkWidget* sender, GdkEventButton* event) { |
| 323 GdkEventButton* event, | |
| 324 BookmarkMenuController* controller) { | |
| 325 // The button press may start a drag; don't let the default handler run. | 317 // The button press may start a drag; don't let the default handler run. |
| 326 if (event->button == 1) | 318 if (event->button == 1) |
| 327 return TRUE; | 319 return TRUE; |
| 328 return FALSE; | 320 return FALSE; |
| 329 } | 321 } |
| 330 | 322 |
| 331 // static | 323 void BookmarkMenuController::OnMenuHidden(GtkWidget* menu) { |
| 332 void BookmarkMenuController::OnMenuHidden(GtkWidget* menu, | 324 if (triggering_widget_) |
| 333 BookmarkMenuController* controller) { | 325 gtk_chrome_button_unset_paint_state(GTK_CHROME_BUTTON(triggering_widget_)); |
| 334 if (controller->triggering_widget_) { | |
| 335 gtk_chrome_button_unset_paint_state( | |
| 336 GTK_CHROME_BUTTON(controller->triggering_widget_)); | |
| 337 } | |
| 338 } | 326 } |
| 339 | 327 |
| 340 // static | 328 void BookmarkMenuController::OnMenuItemActivated(GtkWidget* menu_item) { |
| 341 void BookmarkMenuController::OnMenuItemActivated( | 329 NavigateToMenuItem(menu_item, CURRENT_TAB); |
| 342 GtkMenuItem* menu_item, BookmarkMenuController* controller) { | |
| 343 controller->NavigateToMenuItem(GTK_WIDGET(menu_item), CURRENT_TAB); | |
| 344 } | 330 } |
| 345 | 331 |
| 346 // static | 332 void BookmarkMenuController::OnMenuItemDragBegin(GtkWidget* menu_item, |
| 347 void BookmarkMenuController::OnMenuItemDragBegin( | 333 GdkDragContext* drag_context) { |
| 348 GtkWidget* menu_item, | |
| 349 GdkDragContext* drag_context, | |
| 350 BookmarkMenuController* controller) { | |
| 351 // The parent menu item might be removed during the drag. Ref it so |button| | 334 // The parent menu item might be removed during the drag. Ref it so |button| |
| 352 // won't get destroyed. | 335 // won't get destroyed. |
| 353 g_object_ref(menu_item->parent); | 336 g_object_ref(menu_item->parent); |
| 354 | 337 |
| 355 // Signal to any future OnButtonReleased calls that we're dragging instead of | 338 // Signal to any future OnButtonReleased calls that we're dragging instead of |
| 356 // pressing. | 339 // pressing. |
| 357 controller->ignore_button_release_ = true; | 340 ignore_button_release_ = true; |
| 358 | 341 |
| 359 const BookmarkNode* node = bookmark_utils::BookmarkNodeForWidget(menu_item); | 342 const BookmarkNode* node = bookmark_utils::BookmarkNodeForWidget(menu_item); |
| 360 GtkWidget* window = bookmark_utils::GetDragRepresentation( | 343 GtkWidget* window = bookmark_utils::GetDragRepresentation( |
| 361 node, controller->model_, | 344 node, model_, GtkThemeProvider::GetFrom(profile_)); |
| 362 GtkThemeProvider::GetFrom(controller->profile_)); | |
| 363 gint x, y; | 345 gint x, y; |
| 364 gtk_widget_get_pointer(menu_item, &x, &y); | 346 gtk_widget_get_pointer(menu_item, &x, &y); |
| 365 gtk_drag_set_icon_widget(drag_context, window, x, y); | 347 gtk_drag_set_icon_widget(drag_context, window, x, y); |
| 366 | 348 |
| 367 // Hide our node. | 349 // Hide our node. |
| 368 gtk_widget_hide(menu_item); | 350 gtk_widget_hide(menu_item); |
| 369 } | 351 } |
| 370 | 352 |
| 371 // static | 353 void BookmarkMenuController::OnMenuItemDragEnd(GtkWidget* menu_item, |
| 372 void BookmarkMenuController::OnMenuItemDragEnd( | 354 GdkDragContext* drag_context) { |
| 373 GtkWidget* menu_item, | |
| 374 GdkDragContext* drag_context, | |
| 375 BookmarkMenuController* controller) { | |
| 376 gtk_widget_show(menu_item); | 355 gtk_widget_show(menu_item); |
| 377 g_object_unref(menu_item->parent); | 356 g_object_unref(menu_item->parent); |
| 378 } | 357 } |
| 379 | 358 |
| 380 // static | |
| 381 void BookmarkMenuController::OnMenuItemDragGet( | 359 void BookmarkMenuController::OnMenuItemDragGet( |
| 382 GtkWidget* widget, GdkDragContext* context, | 360 GtkWidget* widget, GdkDragContext* context, |
| 383 GtkSelectionData* selection_data, | 361 GtkSelectionData* selection_data, |
| 384 guint target_type, guint time, | 362 guint target_type, guint time) { |
| 385 BookmarkMenuController* controller) { | |
| 386 const BookmarkNode* node = bookmark_utils::BookmarkNodeForWidget(widget); | 363 const BookmarkNode* node = bookmark_utils::BookmarkNodeForWidget(widget); |
| 387 bookmark_utils::WriteBookmarkToSelection(node, selection_data, target_type, | 364 bookmark_utils::WriteBookmarkToSelection(node, selection_data, target_type, |
| 388 controller->profile_); | 365 profile_); |
| 389 } | 366 } |
| OLD | NEW |