global_menu_bar_x11.cc revision 1320f92c476a1ad9d19dba2a48c72b75566198e9
1// Copyright 2013 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/views/frame/global_menu_bar_x11.h" 6 7#include <dlfcn.h> 8#include <glib-object.h> 9 10#include "base/debug/leak_annotations.h" 11#include "base/logging.h" 12#include "base/prefs/pref_service.h" 13#include "base/stl_util.h" 14#include "base/strings/string_number_conversions.h" 15#include "base/strings/stringprintf.h" 16#include "base/strings/utf_string_conversions.h" 17#include "chrome/app/chrome_command_ids.h" 18#include "chrome/browser/chrome_notification_types.h" 19#include "chrome/browser/history/top_sites.h" 20#include "chrome/browser/profiles/profile.h" 21#include "chrome/browser/sessions/tab_restore_service.h" 22#include "chrome/browser/sessions/tab_restore_service_factory.h" 23#include "chrome/browser/ui/browser.h" 24#include "chrome/browser/ui/browser_commands.h" 25#include "chrome/browser/ui/browser_tab_restore_service_delegate.h" 26#include "chrome/browser/ui/views/frame/browser_desktop_window_tree_host_x11.h" 27#include "chrome/browser/ui/views/frame/browser_view.h" 28#include "chrome/browser/ui/views/frame/global_menu_bar_registrar_x11.h" 29#include "chrome/common/pref_names.h" 30#include "chrome/grit/generated_resources.h" 31#include "content/public/browser/notification_source.h" 32#include "ui/base/accelerators/menu_label_accelerator_util_linux.h" 33#include "ui/base/l10n/l10n_util.h" 34#include "ui/events/keycodes/keyboard_code_conversion_x.h" 35#include "ui/gfx/text_elider.h" 36 37// libdbusmenu-glib types 38typedef struct _DbusmenuMenuitem DbusmenuMenuitem; 39typedef DbusmenuMenuitem* (*dbusmenu_menuitem_new_func)(); 40typedef bool (*dbusmenu_menuitem_child_add_position_func)( 41 DbusmenuMenuitem* parent, 42 DbusmenuMenuitem* child, 43 unsigned int position); 44typedef DbusmenuMenuitem* (*dbusmenu_menuitem_child_append_func)( 45 DbusmenuMenuitem* parent, 46 DbusmenuMenuitem* child); 47typedef bool (*dbusmenu_menuitem_child_delete_func)( 48 DbusmenuMenuitem* parent, 49 DbusmenuMenuitem* child); 50typedef GList* (*dbusmenu_menuitem_get_children_func)( 51 DbusmenuMenuitem* item); 52typedef DbusmenuMenuitem* (*dbusmenu_menuitem_property_set_func)( 53 DbusmenuMenuitem* item, 54 const char* property, 55 const char* value); 56typedef DbusmenuMenuitem* (*dbusmenu_menuitem_property_set_variant_func)( 57 DbusmenuMenuitem* item, 58 const char* property, 59 GVariant* value); 60typedef DbusmenuMenuitem* (*dbusmenu_menuitem_property_set_bool_func)( 61 DbusmenuMenuitem* item, 62 const char* property, 63 bool value); 64typedef DbusmenuMenuitem* (*dbusmenu_menuitem_property_set_int_func)( 65 DbusmenuMenuitem* item, 66 const char* property, 67 int value); 68 69typedef struct _DbusmenuServer DbusmenuServer; 70typedef DbusmenuServer* (*dbusmenu_server_new_func)(const char* object); 71typedef void (*dbusmenu_server_set_root_func)(DbusmenuServer* self, 72 DbusmenuMenuitem* root); 73 74// A line in the static menu definitions. 75struct GlobalMenuBarCommand { 76 int str_id; 77 int command; 78 int tag; 79}; 80 81namespace { 82 83// Retrieved functions from libdbusmenu-glib. 84 85// DbusmenuMenuItem methods: 86dbusmenu_menuitem_new_func menuitem_new = NULL; 87dbusmenu_menuitem_get_children_func menuitem_get_children = NULL; 88dbusmenu_menuitem_child_add_position_func menuitem_child_add_position = NULL; 89dbusmenu_menuitem_child_append_func menuitem_child_append = NULL; 90dbusmenu_menuitem_child_delete_func menuitem_child_delete = NULL; 91dbusmenu_menuitem_property_set_func menuitem_property_set = NULL; 92dbusmenu_menuitem_property_set_variant_func menuitem_property_set_variant = 93 NULL; 94dbusmenu_menuitem_property_set_bool_func menuitem_property_set_bool = NULL; 95dbusmenu_menuitem_property_set_int_func menuitem_property_set_int = NULL; 96 97// DbusmenuServer methods: 98dbusmenu_server_new_func server_new = NULL; 99dbusmenu_server_set_root_func server_set_root = NULL; 100 101// Properties that we set on menu items: 102const char kPropertyEnabled[] = "enabled"; 103const char kPropertyLabel[] = "label"; 104const char kPropertyShortcut[] = "shortcut"; 105const char kPropertyType[] = "type"; 106const char kPropertyToggleType[] = "toggle-type"; 107const char kPropertyToggleState[] = "toggle-state"; 108const char kPropertyVisible[] = "visible"; 109 110const char kTypeCheckmark[] = "checkmark"; 111const char kTypeSeparator[] = "separator"; 112 113// Data set on GObjectgs. 114const char kTypeTag[] = "type-tag"; 115const char kHistoryItem[] = "history-item"; 116 117// The maximum number of most visited items to display. 118const unsigned int kMostVisitedCount = 8; 119 120// The number of recently closed items to get. 121const unsigned int kRecentlyClosedCount = 8; 122 123// Menus more than this many chars long will get trimmed. 124const int kMaximumMenuWidthInChars = 50; 125 126// Constants used in menu definitions. 127const int MENU_SEPARATOR =-1; 128const int MENU_END = -2; 129const int MENU_DISABLED_ID = -3; 130 131// These tag values are used to refer to menu items. 132const int TAG_MOST_VISITED = 1; 133const int TAG_RECENTLY_CLOSED = 2; 134const int TAG_MOST_VISITED_HEADER = 3; 135const int TAG_RECENTLY_CLOSED_HEADER = 4; 136 137GlobalMenuBarCommand file_menu[] = { 138 { IDS_NEW_TAB, IDC_NEW_TAB }, 139 { IDS_NEW_WINDOW, IDC_NEW_WINDOW }, 140 { IDS_NEW_INCOGNITO_WINDOW, IDC_NEW_INCOGNITO_WINDOW }, 141 { IDS_REOPEN_CLOSED_TABS_LINUX, IDC_RESTORE_TAB }, 142 { IDS_OPEN_FILE_LINUX, IDC_OPEN_FILE }, 143 { IDS_OPEN_LOCATION_LINUX, IDC_FOCUS_LOCATION }, 144 145 { MENU_SEPARATOR, MENU_SEPARATOR }, 146 147 { IDS_CREATE_SHORTCUTS, IDC_CREATE_SHORTCUTS }, 148 149 { MENU_SEPARATOR, MENU_SEPARATOR }, 150 151 { IDS_CLOSE_WINDOW_LINUX, IDC_CLOSE_WINDOW }, 152 { IDS_CLOSE_TAB_LINUX, IDC_CLOSE_TAB }, 153 { IDS_SAVE_PAGE, IDC_SAVE_PAGE }, 154 155 { MENU_SEPARATOR, MENU_SEPARATOR }, 156 157 { IDS_PRINT, IDC_PRINT }, 158 159 { MENU_END, MENU_END } 160}; 161 162GlobalMenuBarCommand edit_menu[] = { 163 { IDS_CUT, IDC_CUT }, 164 { IDS_COPY, IDC_COPY }, 165 { IDS_PASTE, IDC_PASTE }, 166 167 { MENU_SEPARATOR, MENU_SEPARATOR }, 168 169 { IDS_FIND, IDC_FIND }, 170 171 { MENU_SEPARATOR, MENU_SEPARATOR }, 172 173 { IDS_PREFERENCES, IDC_OPTIONS }, 174 175 { MENU_END, MENU_END } 176}; 177 178GlobalMenuBarCommand view_menu[] = { 179 { IDS_SHOW_BOOKMARK_BAR, IDC_SHOW_BOOKMARK_BAR }, 180 181 { MENU_SEPARATOR, MENU_SEPARATOR }, 182 183 { IDS_STOP_MENU_LINUX, IDC_STOP }, 184 { IDS_RELOAD_MENU_LINUX, IDC_RELOAD }, 185 186 { MENU_SEPARATOR, MENU_SEPARATOR }, 187 188 { IDS_FULLSCREEN, IDC_FULLSCREEN }, 189 { IDS_TEXT_DEFAULT_LINUX, IDC_ZOOM_NORMAL }, 190 { IDS_TEXT_BIGGER_LINUX, IDC_ZOOM_PLUS }, 191 { IDS_TEXT_SMALLER_LINUX, IDC_ZOOM_MINUS }, 192 193 { MENU_END, MENU_END } 194}; 195 196GlobalMenuBarCommand history_menu[] = { 197 { IDS_HISTORY_HOME_LINUX, IDC_HOME }, 198 { IDS_HISTORY_BACK_LINUX, IDC_BACK }, 199 { IDS_HISTORY_FORWARD_LINUX, IDC_FORWARD }, 200 201 { MENU_SEPARATOR, MENU_SEPARATOR }, 202 203 { IDS_HISTORY_VISITED_LINUX, MENU_DISABLED_ID, TAG_MOST_VISITED_HEADER }, 204 205 { MENU_SEPARATOR, MENU_SEPARATOR }, 206 207 { IDS_HISTORY_CLOSED_LINUX, MENU_DISABLED_ID, TAG_RECENTLY_CLOSED_HEADER }, 208 209 { MENU_SEPARATOR, MENU_SEPARATOR }, 210 211 { IDS_SHOWFULLHISTORY_LINK, IDC_SHOW_HISTORY }, 212 213 { MENU_END, MENU_END } 214}; 215 216GlobalMenuBarCommand tools_menu[] = { 217 { IDS_SHOW_DOWNLOADS, IDC_SHOW_DOWNLOADS }, 218 { IDS_SHOW_HISTORY, IDC_SHOW_HISTORY }, 219 { IDS_SHOW_EXTENSIONS, IDC_MANAGE_EXTENSIONS }, 220 221 { MENU_SEPARATOR, MENU_SEPARATOR }, 222 223 { IDS_TASK_MANAGER, IDC_TASK_MANAGER }, 224 { IDS_CLEAR_BROWSING_DATA, IDC_CLEAR_BROWSING_DATA }, 225 226 { MENU_SEPARATOR, MENU_SEPARATOR }, 227 228 { IDS_VIEW_SOURCE, IDC_VIEW_SOURCE }, 229 { IDS_DEV_TOOLS, IDC_DEV_TOOLS }, 230 { IDS_DEV_TOOLS_CONSOLE, IDC_DEV_TOOLS_CONSOLE }, 231 { IDS_DEV_TOOLS_DEVICES, IDC_DEV_TOOLS_DEVICES }, 232 233 { MENU_END, MENU_END } 234}; 235 236GlobalMenuBarCommand help_menu[] = { 237#if defined(GOOGLE_CHROME_BUILD) 238 { IDS_FEEDBACK, IDC_FEEDBACK }, 239#endif 240 { IDS_HELP_PAGE , IDC_HELP_PAGE_VIA_MENU }, 241 { MENU_END, MENU_END } 242}; 243 244void EnsureMethodsLoaded() { 245 static bool attempted_load = false; 246 if (attempted_load) 247 return; 248 attempted_load = true; 249 250 void* dbusmenu_lib = dlopen("libdbusmenu-glib.so", RTLD_LAZY); 251 if (!dbusmenu_lib) 252 dbusmenu_lib = dlopen("libdbusmenu-glib.so.4", RTLD_LAZY); 253 if (!dbusmenu_lib) 254 return; 255 256 // DbusmenuMenuItem methods. 257 menuitem_new = reinterpret_cast<dbusmenu_menuitem_new_func>( 258 dlsym(dbusmenu_lib, "dbusmenu_menuitem_new")); 259 menuitem_child_add_position = 260 reinterpret_cast<dbusmenu_menuitem_child_add_position_func>( 261 dlsym(dbusmenu_lib, "dbusmenu_menuitem_child_add_position")); 262 menuitem_child_append = reinterpret_cast<dbusmenu_menuitem_child_append_func>( 263 dlsym(dbusmenu_lib, "dbusmenu_menuitem_child_append")); 264 menuitem_child_delete = reinterpret_cast<dbusmenu_menuitem_child_delete_func>( 265 dlsym(dbusmenu_lib, "dbusmenu_menuitem_child_delete")); 266 menuitem_get_children = reinterpret_cast<dbusmenu_menuitem_get_children_func>( 267 dlsym(dbusmenu_lib, "dbusmenu_menuitem_get_children")); 268 menuitem_property_set = reinterpret_cast<dbusmenu_menuitem_property_set_func>( 269 dlsym(dbusmenu_lib, "dbusmenu_menuitem_property_set")); 270 menuitem_property_set_variant = 271 reinterpret_cast<dbusmenu_menuitem_property_set_variant_func>( 272 dlsym(dbusmenu_lib, "dbusmenu_menuitem_property_set_variant")); 273 menuitem_property_set_bool = 274 reinterpret_cast<dbusmenu_menuitem_property_set_bool_func>( 275 dlsym(dbusmenu_lib, "dbusmenu_menuitem_property_set_bool")); 276 menuitem_property_set_int = 277 reinterpret_cast<dbusmenu_menuitem_property_set_int_func>( 278 dlsym(dbusmenu_lib, "dbusmenu_menuitem_property_set_int")); 279 280 // DbusmenuServer methods. 281 server_new = reinterpret_cast<dbusmenu_server_new_func>( 282 dlsym(dbusmenu_lib, "dbusmenu_server_new")); 283 server_set_root = reinterpret_cast<dbusmenu_server_set_root_func>( 284 dlsym(dbusmenu_lib, "dbusmenu_server_set_root")); 285} 286 287} // namespace 288 289struct GlobalMenuBarX11::HistoryItem { 290 HistoryItem() : session_id(0) {} 291 292 // The title for the menu item. 293 base::string16 title; 294 // The URL that will be navigated to if the user selects this item. 295 GURL url; 296 297 // This ID is unique for a browser session and can be passed to the 298 // TabRestoreService to re-open the closed window or tab that this 299 // references. A non-0 session ID indicates that this is an entry can be 300 // restored that way. Otherwise, the URL will be used to open the item and 301 // this ID will be 0. 302 SessionID::id_type session_id; 303 304 // If the HistoryItem is a window, this will be the vector of tabs. Note 305 // that this is a list of weak references. The |menu_item_map_| is the owner 306 // of all items. If it is not a window, then the entry is a single page and 307 // the vector will be empty. 308 std::vector<HistoryItem*> tabs; 309 310 private: 311 DISALLOW_COPY_AND_ASSIGN(HistoryItem); 312}; 313 314GlobalMenuBarX11::GlobalMenuBarX11(BrowserView* browser_view, 315 BrowserDesktopWindowTreeHostX11* host) 316 : browser_(browser_view->browser()), 317 profile_(browser_->profile()), 318 browser_view_(browser_view), 319 host_(host), 320 server_(NULL), 321 root_item_(NULL), 322 history_menu_(NULL), 323 top_sites_(NULL), 324 tab_restore_service_(NULL), 325 weak_ptr_factory_(this) { 326 EnsureMethodsLoaded(); 327 328 if (server_new) 329 host_->AddObserver(this); 330} 331 332GlobalMenuBarX11::~GlobalMenuBarX11() { 333 if (server_) { 334 Disable(); 335 336 if (tab_restore_service_) 337 tab_restore_service_->RemoveObserver(this); 338 339 g_object_unref(server_); 340 host_->RemoveObserver(this); 341 } 342} 343 344// static 345std::string GlobalMenuBarX11::GetPathForWindow(unsigned long xid) { 346 return base::StringPrintf("/com/canonical/menu/%lX", xid); 347} 348 349DbusmenuMenuitem* GlobalMenuBarX11::BuildSeparator() { 350 DbusmenuMenuitem* item = menuitem_new(); 351 menuitem_property_set(item, kPropertyType, kTypeSeparator); 352 menuitem_property_set_bool(item, kPropertyVisible, true); 353 return item; 354} 355 356DbusmenuMenuitem* GlobalMenuBarX11::BuildMenuItem( 357 const std::string& label, 358 int tag_id) { 359 DbusmenuMenuitem* item = menuitem_new(); 360 menuitem_property_set(item, kPropertyLabel, label.c_str()); 361 menuitem_property_set_bool(item, kPropertyVisible, true); 362 363 if (tag_id) 364 g_object_set_data(G_OBJECT(item), kTypeTag, GINT_TO_POINTER(tag_id)); 365 366 return item; 367} 368 369void GlobalMenuBarX11::InitServer(unsigned long xid) { 370 std::string path = GetPathForWindow(xid); 371 { 372 ANNOTATE_SCOPED_MEMORY_LEAK; // http://crbug.com/314087 373 server_ = server_new(path.c_str()); 374 } 375 376 root_item_ = menuitem_new(); 377 menuitem_property_set(root_item_, kPropertyLabel, "Root"); 378 menuitem_property_set_bool(root_item_, kPropertyVisible, true); 379 380 // First build static menu content. 381 BuildStaticMenu(root_item_, IDS_FILE_MENU_LINUX, file_menu); 382 BuildStaticMenu(root_item_, IDS_EDIT_MENU_LINUX, edit_menu); 383 BuildStaticMenu(root_item_, IDS_VIEW_MENU_LINUX, view_menu); 384 history_menu_ = BuildStaticMenu( 385 root_item_, IDS_HISTORY_MENU_LINUX, history_menu); 386 BuildStaticMenu(root_item_, IDS_TOOLS_MENU_LINUX, tools_menu); 387 BuildStaticMenu(root_item_, IDS_HELP_MENU_LINUX, help_menu); 388 389 // We have to connect to |history_menu_item|'s "activate" signal instead of 390 // |history_menu|'s "show" signal because we are not supposed to modify the 391 // menu during "show" 392 g_signal_connect(history_menu_, "about-to-show", 393 G_CALLBACK(OnHistoryMenuAboutToShowThunk), this); 394 395 for (CommandIDMenuItemMap::const_iterator it = id_to_menu_item_.begin(); 396 it != id_to_menu_item_.end(); ++it) { 397 menuitem_property_set_bool(it->second, kPropertyEnabled, 398 chrome::IsCommandEnabled(browser_, it->first)); 399 400 ui::Accelerator accelerator; 401 if (browser_view_->GetAccelerator(it->first, &accelerator)) 402 RegisterAccelerator(it->second, accelerator); 403 404 chrome::AddCommandObserver(browser_, it->first, this); 405 } 406 407 pref_change_registrar_.Init(browser_->profile()->GetPrefs()); 408 pref_change_registrar_.Add( 409 bookmarks::prefs::kShowBookmarkBar, 410 base::Bind(&GlobalMenuBarX11::OnBookmarkBarVisibilityChanged, 411 base::Unretained(this))); 412 OnBookmarkBarVisibilityChanged(); 413 414 top_sites_ = profile_->GetTopSites(); 415 if (top_sites_) { 416 GetTopSitesData(); 417 418 // Register for notification when TopSites changes so that we can update 419 // ourself. 420 registrar_.Add(this, chrome::NOTIFICATION_TOP_SITES_CHANGED, 421 content::Source<history::TopSites>(top_sites_)); 422 } 423 424 server_set_root(server_, root_item_); 425} 426 427void GlobalMenuBarX11::Disable() { 428 for (CommandIDMenuItemMap::const_iterator it = id_to_menu_item_.begin(); 429 it != id_to_menu_item_.end(); ++it) { 430 chrome::RemoveCommandObserver(browser_, it->first, this); 431 } 432 id_to_menu_item_.clear(); 433 434 pref_change_registrar_.RemoveAll(); 435} 436 437DbusmenuMenuitem* GlobalMenuBarX11::BuildStaticMenu( 438 DbusmenuMenuitem* parent, 439 int menu_str_id, 440 GlobalMenuBarCommand* commands) { 441 DbusmenuMenuitem* top = menuitem_new(); 442 menuitem_property_set( 443 top, kPropertyLabel, 444 ui::RemoveWindowsStyleAccelerators( 445 l10n_util::GetStringUTF8(menu_str_id)).c_str()); 446 menuitem_property_set_bool(top, kPropertyVisible, true); 447 448 for (int i = 0; commands[i].str_id != MENU_END; ++i) { 449 DbusmenuMenuitem* menu_item = NULL; 450 int command_id = commands[i].command; 451 if (commands[i].str_id == MENU_SEPARATOR) { 452 menu_item = BuildSeparator(); 453 } else { 454 std::string label = ui::ConvertAcceleratorsFromWindowsStyle( 455 l10n_util::GetStringUTF8(commands[i].str_id)); 456 457 menu_item = BuildMenuItem(label, commands[i].tag); 458 459 if (command_id == MENU_DISABLED_ID) { 460 menuitem_property_set_bool(menu_item, kPropertyEnabled, false); 461 } else { 462 if (command_id == IDC_SHOW_BOOKMARK_BAR) 463 menuitem_property_set(menu_item, kPropertyToggleType, kTypeCheckmark); 464 465 id_to_menu_item_.insert(std::make_pair(command_id, menu_item)); 466 g_object_set_data(G_OBJECT(menu_item), "command-id", 467 GINT_TO_POINTER(command_id)); 468 g_signal_connect(menu_item, "item-activated", 469 G_CALLBACK(OnItemActivatedThunk), this); 470 } 471 } 472 473 menuitem_child_append(top, menu_item); 474 g_object_unref(menu_item); 475 } 476 477 menuitem_child_append(parent, top); 478 g_object_unref(top); 479 return top; 480} 481 482void GlobalMenuBarX11::RegisterAccelerator(DbusmenuMenuitem* item, 483 const ui::Accelerator& accelerator) { 484 // A translation of libdbusmenu-gtk's menuitem_property_set_shortcut() 485 // translated from GDK types to ui::Accelerator types. 486 GVariantBuilder builder; 487 g_variant_builder_init(&builder, G_VARIANT_TYPE_ARRAY); 488 489 if (accelerator.IsCtrlDown()) 490 g_variant_builder_add(&builder, "s", "Control"); 491 if (accelerator.IsAltDown()) 492 g_variant_builder_add(&builder, "s", "Alt"); 493 if (accelerator.IsShiftDown()) 494 g_variant_builder_add(&builder, "s", "Shift"); 495 496 char* name = XKeysymToString(XKeysymForWindowsKeyCode( 497 accelerator.key_code(), false)); 498 if (!name) { 499 NOTIMPLEMENTED(); 500 return; 501 } 502 g_variant_builder_add(&builder, "s", name); 503 504 GVariant* inside_array = g_variant_builder_end(&builder); 505 g_variant_builder_init(&builder, G_VARIANT_TYPE_ARRAY); 506 g_variant_builder_add_value(&builder, inside_array); 507 GVariant* outside_array = g_variant_builder_end(&builder); 508 509 menuitem_property_set_variant(item, kPropertyShortcut, outside_array); 510} 511 512GlobalMenuBarX11::HistoryItem* GlobalMenuBarX11::HistoryItemForTab( 513 const TabRestoreService::Tab& entry) { 514 const sessions::SerializedNavigationEntry& current_navigation = 515 entry.navigations.at(entry.current_navigation_index); 516 HistoryItem* item = new HistoryItem(); 517 item->title = current_navigation.title(); 518 item->url = current_navigation.virtual_url(); 519 item->session_id = entry.id; 520 521 return item; 522} 523 524void GlobalMenuBarX11::AddHistoryItemToMenu(HistoryItem* item, 525 DbusmenuMenuitem* menu, 526 int tag, 527 int index) { 528 base::string16 title = item->title; 529 std::string url_string = item->url.possibly_invalid_spec(); 530 531 if (title.empty()) 532 title = base::UTF8ToUTF16(url_string); 533 gfx::ElideString(title, kMaximumMenuWidthInChars, &title); 534 535 DbusmenuMenuitem* menu_item = BuildMenuItem(base::UTF16ToUTF8(title), tag); 536 g_signal_connect(menu_item, "item-activated", 537 G_CALLBACK(OnHistoryItemActivatedThunk), this); 538 539 g_object_set_data_full(G_OBJECT(menu_item), kHistoryItem, item, 540 DeleteHistoryItem); 541 menuitem_child_add_position(menu, menu_item, index); 542 g_object_unref(menu_item); 543} 544 545void GlobalMenuBarX11::GetTopSitesData() { 546 DCHECK(top_sites_); 547 548 top_sites_->GetMostVisitedURLs( 549 base::Bind(&GlobalMenuBarX11::OnTopSitesReceived, 550 weak_ptr_factory_.GetWeakPtr()), false); 551} 552 553void GlobalMenuBarX11::OnTopSitesReceived( 554 const history::MostVisitedURLList& visited_list) { 555 ClearMenuSection(history_menu_, TAG_MOST_VISITED); 556 557 int index = GetIndexOfMenuItemWithTag(history_menu_, 558 TAG_MOST_VISITED_HEADER) + 1; 559 560 for (size_t i = 0; i < visited_list.size() && i < kMostVisitedCount; ++i) { 561 const history::MostVisitedURL& visited = visited_list[i]; 562 if (visited.url.spec().empty()) 563 break; // This is the signal that there are no more real visited sites. 564 565 HistoryItem* item = new HistoryItem(); 566 item->title = visited.title; 567 item->url = visited.url; 568 569 AddHistoryItemToMenu(item, 570 history_menu_, 571 TAG_MOST_VISITED, 572 index++); 573 } 574} 575 576void GlobalMenuBarX11::OnBookmarkBarVisibilityChanged() { 577 CommandIDMenuItemMap::iterator it = 578 id_to_menu_item_.find(IDC_SHOW_BOOKMARK_BAR); 579 if (it != id_to_menu_item_.end()) { 580 PrefService* prefs = browser_->profile()->GetPrefs(); 581 // Note: Unlike the GTK version, we don't appear to need to do tricks where 582 // we block activation while setting the toggle. 583 menuitem_property_set_int( 584 it->second, 585 kPropertyToggleState, 586 prefs->GetBoolean(bookmarks::prefs::kShowBookmarkBar)); 587 } 588} 589 590int GlobalMenuBarX11::GetIndexOfMenuItemWithTag(DbusmenuMenuitem* menu, 591 int tag_id) { 592 GList* childs = menuitem_get_children(menu); 593 int i = 0; 594 for (; childs != NULL; childs = childs->next, i++) { 595 int tag = 596 GPOINTER_TO_INT(g_object_get_data(G_OBJECT(childs->data), kTypeTag)); 597 if (tag == tag_id) 598 return i; 599 } 600 601 NOTREACHED(); 602 return -1; 603} 604 605void GlobalMenuBarX11::ClearMenuSection(DbusmenuMenuitem* menu, int tag_id) { 606 std::vector<DbusmenuMenuitem*> menuitems_to_delete; 607 608 GList* childs = menuitem_get_children(menu); 609 for (; childs != NULL; childs = childs->next) { 610 DbusmenuMenuitem* current_item = reinterpret_cast<DbusmenuMenuitem*>( 611 childs->data); 612 ClearMenuSection(current_item, tag_id); 613 614 int tag = 615 GPOINTER_TO_INT(g_object_get_data(G_OBJECT(childs->data), kTypeTag)); 616 if (tag == tag_id) 617 menuitems_to_delete.push_back(current_item); 618 } 619 620 for (std::vector<DbusmenuMenuitem*>::const_iterator it = 621 menuitems_to_delete.begin(); it != menuitems_to_delete.end(); ++it) { 622 menuitem_child_delete(menu, *it); 623 } 624} 625 626// static 627void GlobalMenuBarX11::DeleteHistoryItem(void* void_item) { 628 HistoryItem* item = 629 reinterpret_cast<GlobalMenuBarX11::HistoryItem*>(void_item); 630 delete item; 631} 632 633void GlobalMenuBarX11::EnabledStateChangedForCommand(int id, bool enabled) { 634 CommandIDMenuItemMap::iterator it = id_to_menu_item_.find(id); 635 if (it != id_to_menu_item_.end()) 636 menuitem_property_set_bool(it->second, kPropertyEnabled, enabled); 637} 638 639void GlobalMenuBarX11::Observe(int type, 640 const content::NotificationSource& source, 641 const content::NotificationDetails& details) { 642 if (type == chrome::NOTIFICATION_TOP_SITES_CHANGED) { 643 GetTopSitesData(); 644 } else { 645 NOTREACHED(); 646 } 647} 648 649void GlobalMenuBarX11::TabRestoreServiceChanged(TabRestoreService* service) { 650 const TabRestoreService::Entries& entries = service->entries(); 651 652 ClearMenuSection(history_menu_, TAG_RECENTLY_CLOSED); 653 654 // We'll get the index the "Recently Closed" header. (This can vary depending 655 // on the number of "Most Visited" items. 656 int index = GetIndexOfMenuItemWithTag(history_menu_, 657 TAG_RECENTLY_CLOSED_HEADER) + 1; 658 659 unsigned int added_count = 0; 660 for (TabRestoreService::Entries::const_iterator it = entries.begin(); 661 it != entries.end() && added_count < kRecentlyClosedCount; ++it) { 662 TabRestoreService::Entry* entry = *it; 663 664 if (entry->type == TabRestoreService::WINDOW) { 665 TabRestoreService::Window* entry_win = 666 static_cast<TabRestoreService::Window*>(entry); 667 std::vector<TabRestoreService::Tab>& tabs = entry_win->tabs; 668 if (tabs.empty()) 669 continue; 670 671 // Create the item for the parent/window. 672 HistoryItem* item = new HistoryItem(); 673 item->session_id = entry_win->id; 674 675 std::string title = tabs.size() == 1 ? 676 l10n_util::GetStringUTF8( 677 IDS_NEW_TAB_RECENTLY_CLOSED_WINDOW_SINGLE) : 678 l10n_util::GetStringFUTF8( 679 IDS_NEW_TAB_RECENTLY_CLOSED_WINDOW_MULTIPLE, 680 base::IntToString16(tabs.size())); 681 DbusmenuMenuitem* parent_item = BuildMenuItem( 682 title, TAG_RECENTLY_CLOSED); 683 menuitem_child_add_position(history_menu_, parent_item, index++); 684 g_object_unref(parent_item); 685 686 // The mac version of this code allows the user to click on the parent 687 // menu item to have the same effect as clicking the restore window 688 // submenu item. GTK+ helpfully activates a menu item when it shows a 689 // submenu so toss that feature out. 690 DbusmenuMenuitem* restore_item = BuildMenuItem( 691 l10n_util::GetStringUTF8( 692 IDS_HISTORY_CLOSED_RESTORE_WINDOW_LINUX).c_str(), 693 TAG_RECENTLY_CLOSED); 694 g_signal_connect(restore_item, "item-activated", 695 G_CALLBACK(OnHistoryItemActivatedThunk), this); 696 g_object_set_data_full(G_OBJECT(restore_item), kHistoryItem, item, 697 DeleteHistoryItem); 698 menuitem_child_append(parent_item, restore_item); 699 g_object_unref(restore_item); 700 701 DbusmenuMenuitem* separator = BuildSeparator(); 702 menuitem_child_append(parent_item, separator); 703 g_object_unref(separator); 704 705 // Loop over the window's tabs and add them to the submenu. 706 int subindex = 2; 707 std::vector<TabRestoreService::Tab>::const_iterator iter; 708 for (iter = tabs.begin(); iter != tabs.end(); ++iter) { 709 TabRestoreService::Tab tab = *iter; 710 HistoryItem* tab_item = HistoryItemForTab(tab); 711 item->tabs.push_back(tab_item); 712 AddHistoryItemToMenu(tab_item, 713 parent_item, 714 TAG_RECENTLY_CLOSED, 715 subindex++); 716 } 717 718 ++added_count; 719 } else if (entry->type == TabRestoreService::TAB) { 720 TabRestoreService::Tab* tab = static_cast<TabRestoreService::Tab*>(entry); 721 HistoryItem* item = HistoryItemForTab(*tab); 722 AddHistoryItemToMenu(item, 723 history_menu_, 724 TAG_RECENTLY_CLOSED, 725 index++); 726 ++added_count; 727 } 728 } 729} 730 731void GlobalMenuBarX11::TabRestoreServiceDestroyed( 732 TabRestoreService* service) { 733 tab_restore_service_ = NULL; 734} 735 736void GlobalMenuBarX11::OnWindowMapped(unsigned long xid) { 737 if (!server_) 738 InitServer(xid); 739 740 GlobalMenuBarRegistrarX11::GetInstance()->OnWindowMapped(xid); 741} 742 743void GlobalMenuBarX11::OnWindowUnmapped(unsigned long xid) { 744 GlobalMenuBarRegistrarX11::GetInstance()->OnWindowUnmapped(xid); 745} 746 747void GlobalMenuBarX11::OnItemActivated(DbusmenuMenuitem* item, 748 unsigned int timestamp) { 749 int id = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(item), "command-id")); 750 chrome::ExecuteCommand(browser_, id); 751} 752 753void GlobalMenuBarX11::OnHistoryItemActivated(DbusmenuMenuitem* sender, 754 unsigned int timestamp) { 755 // Note: We don't have access to the event modifiers used to click the menu 756 // item since that happens in a different process. 757 HistoryItem* item = reinterpret_cast<HistoryItem*>( 758 g_object_get_data(G_OBJECT(sender), kHistoryItem)); 759 760 // If this item can be restored using TabRestoreService, do so. Otherwise, 761 // just load the URL. 762 TabRestoreService* service = 763 TabRestoreServiceFactory::GetForProfile(profile_); 764 if (item->session_id && service) { 765 service->RestoreEntryById(browser_->tab_restore_service_delegate(), 766 item->session_id, browser_->host_desktop_type(), 767 UNKNOWN); 768 } else { 769 DCHECK(item->url.is_valid()); 770 browser_->OpenURL(content::OpenURLParams( 771 item->url, 772 content::Referrer(), 773 NEW_FOREGROUND_TAB, 774 ui::PAGE_TRANSITION_AUTO_BOOKMARK, 775 false)); 776 } 777} 778 779void GlobalMenuBarX11::OnHistoryMenuAboutToShow(DbusmenuMenuitem* item) { 780 if (!tab_restore_service_) { 781 tab_restore_service_ = TabRestoreServiceFactory::GetForProfile(profile_); 782 if (tab_restore_service_) { 783 tab_restore_service_->LoadTabsFromLastSession(); 784 tab_restore_service_->AddObserver(this); 785 786 // If LoadTabsFromLastSession doesn't load tabs, it won't call 787 // TabRestoreServiceChanged(). This ensures that all new windows after 788 // the first one will have their menus populated correctly. 789 TabRestoreServiceChanged(tab_restore_service_); 790 } 791 } 792} 793