diff options
Diffstat (limited to 'chrome')
7 files changed, 770 insertions, 7 deletions
diff --git a/chrome/browser/ui/views/frame/browser_desktop_root_window_host_x11.cc b/chrome/browser/ui/views/frame/browser_desktop_root_window_host_x11.cc index f1c3002..b7f3fdb 100644 --- a/chrome/browser/ui/views/frame/browser_desktop_root_window_host_x11.cc +++ b/chrome/browser/ui/views/frame/browser_desktop_root_window_host_x11.cc @@ -10,10 +10,12 @@ BrowserDesktopRootWindowHostX11::BrowserDesktopRootWindowHostX11( views::internal::NativeWidgetDelegate* native_widget_delegate, views::DesktopNativeWidgetAura* desktop_native_widget_aura, - const gfx::Rect& initial_bounds) + const gfx::Rect& initial_bounds, + BrowserView* browser_view) : DesktopRootWindowHostX11(native_widget_delegate, - desktop_native_widget_aura, - initial_bounds) { + desktop_native_widget_aura, + initial_bounds), + browser_view_(browser_view) { } BrowserDesktopRootWindowHostX11::~BrowserDesktopRootWindowHostX11() { @@ -37,6 +39,28 @@ bool BrowserDesktopRootWindowHostX11::UsesNativeSystemMenu() const { } //////////////////////////////////////////////////////////////////////////////// +// BrowserDesktopRootWindowHostX11, +// views::DesktopRootWindowHostX11 implementation: + +aura::RootWindow* BrowserDesktopRootWindowHostX11::Init( + aura::Window* content_window, + const views::Widget::InitParams& params) { + aura::RootWindow* root_window = views::DesktopRootWindowHostX11::Init( + content_window, params); + + // We have now created our backing X11 window. We now need to (possibly) + // alert Unity that there's a menu bar attached to it. + global_menu_bar_x11_.reset(new GlobalMenuBarX11(browser_view_, this)); + + return root_window; +} + +void BrowserDesktopRootWindowHostX11::CloseNow() { + global_menu_bar_x11_.reset(); + DesktopRootWindowHostX11::CloseNow(); +} + +//////////////////////////////////////////////////////////////////////////////// // BrowserDesktopRootWindowHost, public: // static @@ -48,6 +72,7 @@ BrowserDesktopRootWindowHost* BrowserView* browser_view, BrowserFrame* browser_frame) { return new BrowserDesktopRootWindowHostX11(native_widget_delegate, - desktop_native_widget_aura, - initial_bounds); + desktop_native_widget_aura, + initial_bounds, + browser_view); } diff --git a/chrome/browser/ui/views/frame/browser_desktop_root_window_host_x11.h b/chrome/browser/ui/views/frame/browser_desktop_root_window_host_x11.h index 3461ebf..52aa8d7 100644 --- a/chrome/browser/ui/views/frame/browser_desktop_root_window_host_x11.h +++ b/chrome/browser/ui/views/frame/browser_desktop_root_window_host_x11.h @@ -7,6 +7,7 @@ #include "ui/views/widget/desktop_aura/desktop_root_window_host_x11.h" #include "chrome/browser/ui/views/frame/browser_desktop_root_window_host.h" +#include "chrome/browser/ui/views/frame/global_menu_bar_x11.h" class BrowserFrame; class BrowserView; @@ -22,7 +23,8 @@ class BrowserDesktopRootWindowHostX11 BrowserDesktopRootWindowHostX11( views::internal::NativeWidgetDelegate* native_widget_delegate, views::DesktopNativeWidgetAura* desktop_native_widget_aura, - const gfx::Rect& initial_bounds); + const gfx::Rect& initial_bounds, + BrowserView* browser_view); virtual ~BrowserDesktopRootWindowHostX11(); private: @@ -31,8 +33,20 @@ class BrowserDesktopRootWindowHostX11 virtual int GetMinimizeButtonOffset() const OVERRIDE; virtual bool UsesNativeSystemMenu() const OVERRIDE; + // Overridden from views::DesktopRootWindowHostX11: + virtual aura::RootWindow* Init( + aura::Window* content_window, + const views::Widget::InitParams& params) OVERRIDE; + virtual void CloseNow() OVERRIDE; + + BrowserView* browser_view_; + + // Each browser frame maintains its own menu bar object because the lower + // level dbus protocol associates a xid to a menu bar; we can't map multiple + // xids to the same menu bar. + scoped_ptr<GlobalMenuBarX11> global_menu_bar_x11_; + DISALLOW_COPY_AND_ASSIGN(BrowserDesktopRootWindowHostX11); }; - #endif // CHROME_BROWSER_UI_VIEWS_FRAME_BROWSER_DESKTOP_ROOT_WINDOW_HOST_X11_H_ diff --git a/chrome/browser/ui/views/frame/global_menu_bar_registrar_x11.cc b/chrome/browser/ui/views/frame/global_menu_bar_registrar_x11.cc new file mode 100644 index 0000000..55f2b27 --- /dev/null +++ b/chrome/browser/ui/views/frame/global_menu_bar_registrar_x11.cc @@ -0,0 +1,139 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/ui/views/frame/global_menu_bar_registrar_x11.h" + +#include "base/bind.h" +#include "base/logging.h" +#include "chrome/browser/ui/views/frame/global_menu_bar_x11.h" +#include "content/public/browser/browser_thread.h" + +using content::BrowserThread; + +namespace { + +const char kAppMenuRegistrarName[] = "com.canonical.AppMenu.Registrar"; +const char kAppMenuRegistrarPath[] = "/com/canonical/AppMenu/Registrar"; + +} // namespace + +// static +GlobalMenuBarRegistrarX11* GlobalMenuBarRegistrarX11::GetInstance() { + return Singleton<GlobalMenuBarRegistrarX11>::get(); +} + +void GlobalMenuBarRegistrarX11::OnWindowMapped(unsigned long xid) { + live_xids_.insert(xid); + + if (registrar_proxy_) + RegisterXID(xid); +} + +void GlobalMenuBarRegistrarX11::OnWindowUnmapped(unsigned long xid) { + if (registrar_proxy_) + UnregisterXID(xid); + + live_xids_.erase(xid); +} + +GlobalMenuBarRegistrarX11::GlobalMenuBarRegistrarX11() + : registrar_proxy_(NULL) { + // libdbusmenu uses the gio version of dbus; I tried using the code in dbus/, + // but it looks like that's isn't sharing the bus name with the gio version, + // even when |connection_type| is set to SHARED. + g_dbus_proxy_new_for_bus( + G_BUS_TYPE_SESSION, + static_cast<GDBusProxyFlags>( + G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES | + G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS | + G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START), + NULL, + kAppMenuRegistrarName, + kAppMenuRegistrarPath, + kAppMenuRegistrarName, + NULL, // TODO: Probalby want a real cancelable. + static_cast<GAsyncReadyCallback>(OnProxyCreatedThunk), + this); +} + +GlobalMenuBarRegistrarX11::~GlobalMenuBarRegistrarX11() { + if (registrar_proxy_) { + g_signal_handlers_disconnect_by_func( + registrar_proxy_, + reinterpret_cast<void*>(OnNameOwnerChangedThunk), + this); + g_object_unref(registrar_proxy_); + } +} + +void GlobalMenuBarRegistrarX11::RegisterXID(unsigned long xid) { + DCHECK(registrar_proxy_); + std::string path = GlobalMenuBarX11::GetPathForWindow(xid); + + // TODO(erg): The mozilla implementation goes to a lot of callback trouble + // just to make sure that they react to make sure there's some sort of + // cancelable object; including making a whole callback just to handle the + // cancelable. + // + // I don't see any reason why we should care if "RegisterWindow" completes or + // not. + g_dbus_proxy_call(registrar_proxy_, + "RegisterWindow", + g_variant_new("(uo)", xid, path.c_str()), + G_DBUS_CALL_FLAGS_NONE, -1, + NULL, + NULL, + NULL); +} + +void GlobalMenuBarRegistrarX11::UnregisterXID(unsigned long xid) { + DCHECK(registrar_proxy_); + std::string path = GlobalMenuBarX11::GetPathForWindow(xid); + + // TODO(erg): The mozilla implementation goes to a lot of callback trouble + // just to make sure that they react to make sure there's some sort of + // cancelable object; including making a whole callback just to handle the + // cancelable. + // + // I don't see any reason why we should care if "UnregisterWindow" completes + // or not. + g_dbus_proxy_call(registrar_proxy_, + "UnregisterWindow", + g_variant_new("(u)", xid), + G_DBUS_CALL_FLAGS_NONE, -1, + NULL, + NULL, + NULL); +} + +void GlobalMenuBarRegistrarX11::OnProxyCreated(GObject* source, + GAsyncResult* result) { + GError* error = NULL; + GDBusProxy* proxy = g_dbus_proxy_new_for_bus_finish(result, &error); + if (error) { + g_error_free(error); + return; + } + + // TODO(erg): Mozilla's implementation has a workaround for GDBus + // cancellation here. However, it's marked as fixed. If there's weird + // problems with cancelation, look at how they fixed their issues. + + registrar_proxy_ = proxy; + + g_signal_connect(registrar_proxy_, "notify::g-name-owner", + G_CALLBACK(OnNameOwnerChangedThunk), this); + + OnNameOwnerChanged(NULL, NULL); +} + +void GlobalMenuBarRegistrarX11::OnNameOwnerChanged(GObject* /* ignored */, + GParamSpec* /* ignored */) { + // If the name owner changed, we need to reregister all the live xids with + // the system. + for (std::set<unsigned long>::const_iterator it = live_xids_.begin(); + it != live_xids_.end(); ++it) { + RegisterXID(*it); + } +} diff --git a/chrome/browser/ui/views/frame/global_menu_bar_registrar_x11.h b/chrome/browser/ui/views/frame/global_menu_bar_registrar_x11.h new file mode 100644 index 0000000..e35e87c --- /dev/null +++ b/chrome/browser/ui/views/frame/global_menu_bar_registrar_x11.h @@ -0,0 +1,54 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_UI_VIEWS_FRAME_GLOBAL_MENU_BAR_REGISTRAR_X11_H_ +#define CHROME_BROWSER_UI_VIEWS_FRAME_GLOBAL_MENU_BAR_REGISTRAR_X11_H_ + +#include <gio/gio.h> + +#include <set> + +#include "base/basictypes.h" +#include "base/memory/ref_counted.h" +#include "base/memory/singleton.h" +#include "ui/base/glib/glib_signal.h" + +// Advertises our menu bars to Unity. +// +// GlobalMenuBarX11 is responsible for managing the DbusmenuServer for each +// XID. We need a separate object to own the dbus channel to +// com.canonical.AppMenu.Registrar and to register/unregister the mapping +// between a XID and the DbusmenuServer instance we are offering. +class GlobalMenuBarRegistrarX11 { + public: + static GlobalMenuBarRegistrarX11* GetInstance(); + + void OnWindowMapped(unsigned long xid); + void OnWindowUnmapped(unsigned long xid); + + private: + friend struct DefaultSingletonTraits<GlobalMenuBarRegistrarX11>; + + GlobalMenuBarRegistrarX11(); + ~GlobalMenuBarRegistrarX11(); + + // Sends the actual message. + void RegisterXID(unsigned long xid); + void UnregisterXID(unsigned long xid); + + CHROMEG_CALLBACK_1(GlobalMenuBarRegistrarX11, void, OnProxyCreated, + GObject*, GAsyncResult*); + CHROMEG_CALLBACK_1(GlobalMenuBarRegistrarX11, void, OnNameOwnerChanged, + GObject*, GParamSpec*); + + GDBusProxy* registrar_proxy_; + + // Window XIDs which want to be registered, but haven't yet been because + // we're waiting for the proxy to become available. + std::set<unsigned long> live_xids_; + + DISALLOW_COPY_AND_ASSIGN(GlobalMenuBarRegistrarX11); +}; + +#endif // CHROME_BROWSER_UI_VIEWS_FRAME_GLOBAL_MENU_BAR_REGISTRAR_X11_H_ diff --git a/chrome/browser/ui/views/frame/global_menu_bar_x11.cc b/chrome/browser/ui/views/frame/global_menu_bar_x11.cc new file mode 100644 index 0000000..e6ccb3e --- /dev/null +++ b/chrome/browser/ui/views/frame/global_menu_bar_x11.cc @@ -0,0 +1,420 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/ui/views/frame/global_menu_bar_x11.h" + +#include <dlfcn.h> +#include <glib-object.h> + +#include "base/logging.h" +#include "base/prefs/pref_service.h" +#include "base/strings/stringprintf.h" +#include "chrome/app/chrome_command_ids.h" +#include "chrome/browser/ui/browser_commands.h" +#include "chrome/browser/ui/views/frame/browser_desktop_root_window_host_x11.h" +#include "chrome/browser/ui/views/frame/browser_view.h" +#include "chrome/browser/ui/views/frame/global_menu_bar_registrar_x11.h" +#include "chrome/common/pref_names.h" +#include "grit/generated_resources.h" +#include "ui/base/accelerators/menu_label_accelerator_util_linux.h" +#include "ui/base/keycodes/keyboard_code_conversion_x.h" +#include "ui/base/l10n/l10n_util.h" + +// libdbusmenu-glib types +typedef struct _DbusmenuMenuitem DbusmenuMenuitem; +typedef DbusmenuMenuitem* (*dbusmenu_menuitem_new_func)(); +typedef DbusmenuMenuitem* (*dbusmenu_menuitem_new_with_id_func)(int id); + +typedef int (*dbusmenu_menuitem_get_id_func)(DbusmenuMenuitem* item); +typedef DbusmenuMenuitem* (*dbusmenu_menuitem_child_append_func)( + DbusmenuMenuitem* parent, + DbusmenuMenuitem* child); +typedef DbusmenuMenuitem* (*dbusmenu_menuitem_property_set_func)( + DbusmenuMenuitem* item, + const char* property, + const char* value); +typedef DbusmenuMenuitem* (*dbusmenu_menuitem_property_set_variant_func)( + DbusmenuMenuitem* item, + const char* property, + GVariant* value); +typedef DbusmenuMenuitem* (*dbusmenu_menuitem_property_set_bool_func)( + DbusmenuMenuitem* item, + const char* property, + bool value); +typedef DbusmenuMenuitem* (*dbusmenu_menuitem_property_set_int_func)( + DbusmenuMenuitem* item, + const char* property, + int value); + +typedef struct _DbusmenuServer DbusmenuServer; +typedef DbusmenuServer* (*dbusmenu_server_new_func)(const char* object); +typedef void (*dbusmenu_server_set_root_func)(DbusmenuServer* self, + DbusmenuMenuitem* root); + +// A line in the static menu definitions. +struct GlobalMenuBarCommand { + int str_id; + int command; + int tag; +}; + +namespace { + +// Retrieved functions from libdbusmenu-glib. + +// DbusmenuMenuItem methods: +dbusmenu_menuitem_new_func menuitem_new = NULL; +dbusmenu_menuitem_new_with_id_func menuitem_new_with_id = NULL; +dbusmenu_menuitem_get_id_func menuitem_get_id = NULL; +dbusmenu_menuitem_child_append_func menuitem_child_append = NULL; +dbusmenu_menuitem_property_set_func menuitem_property_set = NULL; +dbusmenu_menuitem_property_set_variant_func menuitem_property_set_variant = + NULL; +dbusmenu_menuitem_property_set_bool_func menuitem_property_set_bool = NULL; +dbusmenu_menuitem_property_set_int_func menuitem_property_set_int = NULL; + +// DbusmenuServer methods: +dbusmenu_server_new_func server_new = NULL; +dbusmenu_server_set_root_func server_set_root = NULL; + +// Properties that we set on menu items: +const char kPropertyEnabled[] = "enabled"; +const char kPropertyLabel[] = "label"; +const char kPropertyShortcut[] = "shortcut"; +const char kPropertyType[] = "type"; +const char kPropertyToggleType[] = "toggle-type"; +const char kPropertyToggleState[] = "toggle-state"; +const char kPropertyVisible[] = "visible"; + +const char kTypeCheckmark[] = "checkmark"; +const char kTypeSeparator[] = "separator"; + +// Constants used in menu definitions +const int MENU_SEPARATOR =-1; +const int MENU_END = -2; +const int MENU_DISABLED_LABEL = -3; + +GlobalMenuBarCommand file_menu[] = { + { IDS_NEW_TAB, IDC_NEW_TAB }, + { IDS_NEW_WINDOW, IDC_NEW_WINDOW }, + { IDS_NEW_INCOGNITO_WINDOW, IDC_NEW_INCOGNITO_WINDOW }, + { IDS_REOPEN_CLOSED_TABS_LINUX, IDC_RESTORE_TAB }, + { IDS_OPEN_FILE_LINUX, IDC_OPEN_FILE }, + { IDS_OPEN_LOCATION_LINUX, IDC_FOCUS_LOCATION }, + + { MENU_SEPARATOR, MENU_SEPARATOR }, + + { IDS_CREATE_SHORTCUTS, IDC_CREATE_SHORTCUTS }, + + { MENU_SEPARATOR, MENU_SEPARATOR }, + + { IDS_CLOSE_WINDOW_LINUX, IDC_CLOSE_WINDOW }, + { IDS_CLOSE_TAB_LINUX, IDC_CLOSE_TAB }, + { IDS_SAVE_PAGE, IDC_SAVE_PAGE }, + + { MENU_SEPARATOR, MENU_SEPARATOR }, + + { IDS_PRINT, IDC_PRINT }, + + { MENU_END, MENU_END } +}; + +GlobalMenuBarCommand edit_menu[] = { + { IDS_CUT, IDC_CUT }, + { IDS_COPY, IDC_COPY }, + { IDS_PASTE, IDC_PASTE }, + + { MENU_SEPARATOR, MENU_SEPARATOR }, + + { IDS_FIND, IDC_FIND }, + + { MENU_SEPARATOR, MENU_SEPARATOR }, + + { IDS_PREFERENCES, IDC_OPTIONS }, + + { MENU_END, MENU_END } +}; + + +GlobalMenuBarCommand view_menu[] = { + { IDS_SHOW_BOOKMARK_BAR, IDC_SHOW_BOOKMARK_BAR }, + + { MENU_SEPARATOR, MENU_SEPARATOR }, + + { IDS_STOP_MENU_LINUX, IDC_STOP }, + { IDS_RELOAD_MENU_LINUX, IDC_RELOAD }, + + { MENU_SEPARATOR, MENU_SEPARATOR }, + + { IDS_FULLSCREEN, IDC_FULLSCREEN }, + { IDS_TEXT_DEFAULT_LINUX, IDC_ZOOM_NORMAL }, + { IDS_TEXT_BIGGER_LINUX, IDC_ZOOM_PLUS }, + { IDS_TEXT_SMALLER_LINUX, IDC_ZOOM_MINUS }, + + { MENU_END, MENU_END } +}; + +// TODO(erg): History menu. + +GlobalMenuBarCommand tools_menu[] = { + { IDS_SHOW_DOWNLOADS, IDC_SHOW_DOWNLOADS }, + { IDS_SHOW_HISTORY, IDC_SHOW_HISTORY }, + { IDS_SHOW_EXTENSIONS, IDC_MANAGE_EXTENSIONS }, + + { MENU_SEPARATOR, MENU_SEPARATOR }, + + { IDS_TASK_MANAGER, IDC_TASK_MANAGER }, + { IDS_CLEAR_BROWSING_DATA, IDC_CLEAR_BROWSING_DATA }, + + { MENU_SEPARATOR, MENU_SEPARATOR }, + + { IDS_VIEW_SOURCE, IDC_VIEW_SOURCE }, + { IDS_DEV_TOOLS, IDC_DEV_TOOLS }, + { IDS_DEV_TOOLS_CONSOLE, IDC_DEV_TOOLS_CONSOLE }, + + { MENU_END, MENU_END } +}; + +GlobalMenuBarCommand help_menu[] = { + { IDS_FEEDBACK, IDC_FEEDBACK }, + { IDS_HELP_PAGE , IDC_HELP_PAGE_VIA_MENU }, + { MENU_END, MENU_END } +}; + + +void EnsureMethodsLoaded() { + static bool attempted_load = false; + if (attempted_load) + return; + attempted_load = true; + + void* dbusmenu_lib = dlopen("libdbusmenu-glib.so", RTLD_LAZY); + if (!dbusmenu_lib) + return; + + // DbusmenuMenuItem methods. + menuitem_new = reinterpret_cast<dbusmenu_menuitem_new_func>( + dlsym(dbusmenu_lib, "dbusmenu_menuitem_new")); + menuitem_new_with_id = reinterpret_cast<dbusmenu_menuitem_new_with_id_func>( + dlsym(dbusmenu_lib, "dbusmenu_menuitem_new_with_id")); + menuitem_get_id = reinterpret_cast<dbusmenu_menuitem_get_id_func>( + dlsym(dbusmenu_lib, "dbusmenu_menuitem_get_id")); + menuitem_child_append = reinterpret_cast<dbusmenu_menuitem_child_append_func>( + dlsym(dbusmenu_lib, "dbusmenu_menuitem_child_append")); + menuitem_property_set = reinterpret_cast<dbusmenu_menuitem_property_set_func>( + dlsym(dbusmenu_lib, "dbusmenu_menuitem_property_set")); + menuitem_property_set_variant = + reinterpret_cast<dbusmenu_menuitem_property_set_variant_func>( + dlsym(dbusmenu_lib, "dbusmenu_menuitem_property_set_variant")); + menuitem_property_set_bool = + reinterpret_cast<dbusmenu_menuitem_property_set_bool_func>( + dlsym(dbusmenu_lib, "dbusmenu_menuitem_property_set_bool")); + menuitem_property_set_int = + reinterpret_cast<dbusmenu_menuitem_property_set_int_func>( + dlsym(dbusmenu_lib, "dbusmenu_menuitem_property_set_int")); + + // DbusmenuServer methods. + server_new = reinterpret_cast<dbusmenu_server_new_func>( + dlsym(dbusmenu_lib, "dbusmenu_server_new")); + server_set_root = reinterpret_cast<dbusmenu_server_set_root_func>( + dlsym(dbusmenu_lib, "dbusmenu_server_set_root")); +} + +} // namespace + +GlobalMenuBarX11::GlobalMenuBarX11(BrowserView* browser_view, + BrowserDesktopRootWindowHostX11* host) + : browser_(browser_view->browser()), + browser_view_(browser_view), + host_(host), + server_(NULL), + root_item_(NULL) { + EnsureMethodsLoaded(); + + if (server_new) + host_->AddObserver(this); +} + +GlobalMenuBarX11::~GlobalMenuBarX11() { + if (server_) { + Disable(); + g_object_unref(server_); + host_->RemoveObserver(this); + } +} + +// static +std::string GlobalMenuBarX11::GetPathForWindow(unsigned long xid) { + return base::StringPrintf("/com/canonical/menu/%lX", xid); +} + +void GlobalMenuBarX11::InitServer(unsigned long xid) { + std::string path = GetPathForWindow(xid); + server_ = server_new(path.c_str()); + + root_item_ = menuitem_new(); + menuitem_property_set(root_item_, kPropertyLabel, "Root"); + menuitem_property_set_bool(root_item_, kPropertyVisible, true); + + BuildMenuFrom(root_item_, IDS_FILE_MENU_LINUX, &id_to_menu_item_, file_menu); + BuildMenuFrom(root_item_, IDS_EDIT_MENU_LINUX, &id_to_menu_item_, edit_menu); + BuildMenuFrom(root_item_, IDS_VIEW_MENU_LINUX, &id_to_menu_item_, view_menu); + // TODO(erg): History menu. + BuildMenuFrom(root_item_, IDS_TOOLS_MENU_LINUX, &id_to_menu_item_, + tools_menu); + BuildMenuFrom(root_item_, IDS_HELP_MENU_LINUX, &id_to_menu_item_, help_menu); + + for (CommandIDMenuItemMap::const_iterator it = id_to_menu_item_.begin(); + it != id_to_menu_item_.end(); ++it) { + menuitem_property_set_bool(it->second, kPropertyEnabled, + chrome::IsCommandEnabled(browser_, it->first)); + + ui::Accelerator accelerator; + if (browser_view_->GetAccelerator(it->first, &accelerator)) + RegisterAccelerator(it->second, accelerator); + + chrome::AddCommandObserver(browser_, it->first, this); + } + + pref_change_registrar_.Init(browser_->profile()->GetPrefs()); + pref_change_registrar_.Add( + prefs::kShowBookmarkBar, + base::Bind(&GlobalMenuBarX11::OnBookmarkBarVisibilityChanged, + base::Unretained(this))); + OnBookmarkBarVisibilityChanged(); + + server_set_root(server_, root_item_); +} + +void GlobalMenuBarX11::Disable() { + for (CommandIDMenuItemMap::const_iterator it = id_to_menu_item_.begin(); + it != id_to_menu_item_.end(); ++it) { + chrome::RemoveCommandObserver(browser_, it->first, this); + } + id_to_menu_item_.clear(); + + pref_change_registrar_.RemoveAll(); +} + +void GlobalMenuBarX11::BuildMenuFrom( + DbusmenuMenuitem* parent, + int menu_str_id, + std::map<int, DbusmenuMenuitem*>* id_to_menu_item, + GlobalMenuBarCommand* commands) { + DbusmenuMenuitem* top = menuitem_new(); + menuitem_property_set( + top, kPropertyLabel, + ui::RemoveWindowsStyleAccelerators( + l10n_util::GetStringUTF8(menu_str_id)).c_str()); + menuitem_property_set_bool(top, kPropertyVisible, true); + + for (int i = 0; commands[i].str_id != MENU_END; ++i) { + DbusmenuMenuitem* menu_item = BuildMenuItem( + commands[i].str_id, commands[i].command, commands[i].tag, + id_to_menu_item); + menuitem_child_append(top, menu_item); + } + + menuitem_child_append(parent, top); +} + +DbusmenuMenuitem* GlobalMenuBarX11::BuildMenuItem( + int string_id, + int command_id, + int tag_id, + std::map<int, DbusmenuMenuitem*>* id_to_menu_item) { + DbusmenuMenuitem* item = menuitem_new(); + + if (string_id == MENU_SEPARATOR) { + menuitem_property_set(item, kPropertyType, kTypeSeparator); + } else { + std::string label = ui::ConvertAcceleratorsFromWindowsStyle( + l10n_util::GetStringUTF8(string_id)); + menuitem_property_set(item, kPropertyLabel, label.c_str()); + + if (command_id == IDC_SHOW_BOOKMARK_BAR) + menuitem_property_set(item, kPropertyToggleType, kTypeCheckmark); + + if (tag_id) + g_object_set_data(G_OBJECT(item), "type-tag", GINT_TO_POINTER(tag_id)); + + if (command_id == MENU_DISABLED_LABEL) { + menuitem_property_set_bool(item, kPropertyEnabled, false); + } else { + id_to_menu_item->insert(std::make_pair(command_id, item)); + g_object_set_data(G_OBJECT(item), "command-id", + GINT_TO_POINTER(command_id)); + g_signal_connect(item, "item-activated", + G_CALLBACK(OnItemActivatedThunk), this); + } + } + + menuitem_property_set_bool(item, kPropertyVisible, true); + return item; +} + +void GlobalMenuBarX11::RegisterAccelerator(DbusmenuMenuitem* item, + const ui::Accelerator& accelerator) { + // A translation of libdbusmenu-gtk's menuitem_property_set_shortcut() + // translated from GDK types to ui::Accelerator types. + GVariantBuilder builder; + g_variant_builder_init(&builder, G_VARIANT_TYPE_ARRAY); + + if (accelerator.IsCtrlDown()) + g_variant_builder_add(&builder, "s", "Control"); + if (accelerator.IsAltDown()) + g_variant_builder_add(&builder, "s", "Alt"); + if (accelerator.IsShiftDown()) + g_variant_builder_add(&builder, "s", "Shift"); + + char* name = XKeysymToString(XKeysymForWindowsKeyCode( + accelerator.key_code(), false)); + if (!name) { + NOTIMPLEMENTED(); + return; + } + g_variant_builder_add(&builder, "s", name); + + GVariant* inside_array = g_variant_builder_end(&builder); + g_variant_builder_init(&builder, G_VARIANT_TYPE_ARRAY); + g_variant_builder_add_value(&builder, inside_array); + GVariant* outside_array = g_variant_builder_end(&builder); + + menuitem_property_set_variant(item, kPropertyShortcut, outside_array); +} + +void GlobalMenuBarX11::OnBookmarkBarVisibilityChanged() { + CommandIDMenuItemMap::iterator it = + id_to_menu_item_.find(IDC_SHOW_BOOKMARK_BAR); + if (it != id_to_menu_item_.end()) { + PrefService* prefs = browser_->profile()->GetPrefs(); + // Note: Unlike the GTK version, we don't appear to need to do tricks where + // we block activation while setting the toggle. + menuitem_property_set_int(it->second, kPropertyToggleState, + prefs->GetBoolean(prefs::kShowBookmarkBar)); + } +} + +void GlobalMenuBarX11::EnabledStateChangedForCommand(int id, bool enabled) { + CommandIDMenuItemMap::iterator it = id_to_menu_item_.find(id); + if (it != id_to_menu_item_.end()) + menuitem_property_set_bool(it->second, kPropertyEnabled, enabled); +} + +void GlobalMenuBarX11::OnWindowMapped(unsigned long xid) { + if (!server_) + InitServer(xid); + + GlobalMenuBarRegistrarX11::GetInstance()->OnWindowMapped(xid); +} + +void GlobalMenuBarX11::OnWindowUnmapped(unsigned long xid) { + GlobalMenuBarRegistrarX11::GetInstance()->OnWindowUnmapped(xid); +} + +void GlobalMenuBarX11::OnItemActivated(DbusmenuMenuitem* item, + unsigned int timestamp) { + int id = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(item), "command-id")); + chrome::ExecuteCommand(browser_, id); +} diff --git a/chrome/browser/ui/views/frame/global_menu_bar_x11.h b/chrome/browser/ui/views/frame/global_menu_bar_x11.h new file mode 100644 index 0000000..46f8cf3 --- /dev/null +++ b/chrome/browser/ui/views/frame/global_menu_bar_x11.h @@ -0,0 +1,106 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_UI_VIEWS_FRAME_GLOBAL_MENU_BAR_X11_H_ +#define CHROME_BROWSER_UI_VIEWS_FRAME_GLOBAL_MENU_BAR_X11_H_ + +#include <map> +#include <string> + +#include "base/compiler_specific.h" +#include "base/prefs/pref_change_registrar.h" +#include "chrome/browser/command_observer.h" +#include "ui/base/glib/glib_signal.h" +#include "ui/views/widget/desktop_aura/desktop_root_window_host_observer_x11.h" + +typedef struct _DbusmenuMenuitem DbusmenuMenuitem; +typedef struct _DbusmenuServer DbusmenuServer; + +namespace ui { +class Accelerator; +} + +class Browser; +class BrowserView; + +class BrowserDesktopRootWindowHostX11; +struct GlobalMenuBarCommand; + +// Controls the Mac style menu bar on Unity. +// +// Unity has an Apple-like menu bar at the top of the screen that changes +// depending on the active window. In the GTK port, we had a hidden GtkMenuBar +// object in each GtkWindow which existed only to be scrapped by the +// libdbusmenu-gtk code. Since we don't have GtkWindows anymore, we need to +// interface directly with the lower level libdbusmenu-glib, which we +// opportunistically dlopen() since not everyone is running Ubuntu. +class GlobalMenuBarX11 : public CommandObserver, + public views::DesktopRootWindowHostObserverX11 { + public: + GlobalMenuBarX11(BrowserView* browser_view, + BrowserDesktopRootWindowHostX11* host); + virtual ~GlobalMenuBarX11(); + + // Creates the object path for DbusemenuServer which is attached to |xid|. + static std::string GetPathForWindow(unsigned long xid); + + private: + typedef std::map<int, DbusmenuMenuitem*> CommandIDMenuItemMap; + + // Creates a DbusmenuServer, and attaches all the menu items. + void InitServer(unsigned long xid); + + // Stops listening to enable state changed events. + void Disable(); + + // Creates a whole menu defined with |commands| and titled with the string + // |menu_str_id|. Then appends it to |parent|. + void BuildMenuFrom(DbusmenuMenuitem* parent, + int menu_str_id, + CommandIDMenuItemMap* id_to_menu_item, + GlobalMenuBarCommand* commands); + + // Creates an individual menu item from a title and command, and subscribes + // to the activation signal. + DbusmenuMenuitem* BuildMenuItem( + int string_id, + int command_id, + int tag_id, + CommandIDMenuItemMap* id_to_menu_item); + + // Sets the accelerator for |item|. + void RegisterAccelerator(DbusmenuMenuitem* item, + const ui::Accelerator& accelerator); + + // Updates the visibility of the bookmark bar on pref changes. + void OnBookmarkBarVisibilityChanged(); + + // Overridden from CommandObserver: + virtual void EnabledStateChangedForCommand(int id, bool enabled) OVERRIDE; + + // Overridden from DesktopRootWindowHostObserverX11: + virtual void OnWindowMapped(unsigned long xid) OVERRIDE; + virtual void OnWindowUnmapped(unsigned long xid) OVERRIDE; + + CHROMEG_CALLBACK_1(GlobalMenuBarX11, void, OnItemActivated, DbusmenuMenuitem*, + unsigned int); + + Browser* browser_; + BrowserView* browser_view_; + BrowserDesktopRootWindowHostX11* host_; + + // Maps command ids to DbusmenuMenuitems so we can modify their + // enabled/checked state in response to state change notifications. + CommandIDMenuItemMap id_to_menu_item_; + + DbusmenuServer* server_; + DbusmenuMenuitem* root_item_; + + // Tracks value of the kShowBookmarkBar preference. + PrefChangeRegistrar pref_change_registrar_; + + DISALLOW_COPY_AND_ASSIGN(GlobalMenuBarX11); +}; + +#endif // CHROME_BROWSER_UI_VIEWS_FRAME_GLOBAL_MENU_BAR_X11_H_ diff --git a/chrome/chrome_browser_ui.gypi b/chrome/chrome_browser_ui.gypi index 9d177f11..640b247 100644 --- a/chrome/chrome_browser_ui.gypi +++ b/chrome/chrome_browser_ui.gypi @@ -1707,6 +1707,10 @@ 'browser/ui/views/frame/browser_desktop_root_window_host_x11.h', 'browser/ui/views/frame/glass_browser_frame_view.cc', 'browser/ui/views/frame/glass_browser_frame_view.h', + 'browser/ui/views/frame/global_menu_bar_x11.cc', + 'browser/ui/views/frame/global_menu_bar_x11.h', + 'browser/ui/views/frame/global_menu_bar_registrar_x11.cc', + 'browser/ui/views/frame/global_menu_bar_registrar_x11.h', 'browser/ui/views/frame/immersive_mode_controller.cc', 'browser/ui/views/frame/immersive_mode_controller.h', 'browser/ui/views/frame/immersive_mode_controller_ash.cc', @@ -2629,6 +2633,7 @@ # world. 'browser/ui/libgtk2ui/libgtk2ui.gyp:gtk2ui', '../ui/linux_ui/linux_ui.gyp:linux_ui', + '../build/linux/system.gyp:gio', ], }], ['use_aura==0 or chromeos==1 or OS!="linux"', { |