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.
5 #include "chrome/browser/ui/views/frame/global_menu_bar_x11.h"
8 #include <glib-object.h>
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_root_window_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 "content/public/browser/notification_source.h"
31 #include "grit/generated_resources.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"
37 // libdbusmenu-glib types
38 typedef struct _DbusmenuMenuitem DbusmenuMenuitem
;
39 typedef DbusmenuMenuitem
* (*dbusmenu_menuitem_new_func
)();
40 typedef bool (*dbusmenu_menuitem_child_add_position_func
)(
41 DbusmenuMenuitem
* parent
,
42 DbusmenuMenuitem
* child
,
43 unsigned int position
);
44 typedef DbusmenuMenuitem
* (*dbusmenu_menuitem_child_append_func
)(
45 DbusmenuMenuitem
* parent
,
46 DbusmenuMenuitem
* child
);
47 typedef bool (*dbusmenu_menuitem_child_delete_func
)(
48 DbusmenuMenuitem
* parent
,
49 DbusmenuMenuitem
* child
);
50 typedef GList
* (*dbusmenu_menuitem_get_children_func
)(
51 DbusmenuMenuitem
* item
);
52 typedef DbusmenuMenuitem
* (*dbusmenu_menuitem_property_set_func
)(
53 DbusmenuMenuitem
* item
,
56 typedef DbusmenuMenuitem
* (*dbusmenu_menuitem_property_set_variant_func
)(
57 DbusmenuMenuitem
* item
,
60 typedef DbusmenuMenuitem
* (*dbusmenu_menuitem_property_set_bool_func
)(
61 DbusmenuMenuitem
* item
,
64 typedef DbusmenuMenuitem
* (*dbusmenu_menuitem_property_set_int_func
)(
65 DbusmenuMenuitem
* item
,
69 typedef struct _DbusmenuServer DbusmenuServer
;
70 typedef DbusmenuServer
* (*dbusmenu_server_new_func
)(const char* object
);
71 typedef void (*dbusmenu_server_set_root_func
)(DbusmenuServer
* self
,
72 DbusmenuMenuitem
* root
);
74 // A line in the static menu definitions.
75 struct GlobalMenuBarCommand
{
83 // Retrieved functions from libdbusmenu-glib.
85 // DbusmenuMenuItem methods:
86 dbusmenu_menuitem_new_func menuitem_new
= NULL
;
87 dbusmenu_menuitem_get_children_func menuitem_get_children
= NULL
;
88 dbusmenu_menuitem_child_add_position_func menuitem_child_add_position
= NULL
;
89 dbusmenu_menuitem_child_append_func menuitem_child_append
= NULL
;
90 dbusmenu_menuitem_child_delete_func menuitem_child_delete
= NULL
;
91 dbusmenu_menuitem_property_set_func menuitem_property_set
= NULL
;
92 dbusmenu_menuitem_property_set_variant_func menuitem_property_set_variant
=
94 dbusmenu_menuitem_property_set_bool_func menuitem_property_set_bool
= NULL
;
95 dbusmenu_menuitem_property_set_int_func menuitem_property_set_int
= NULL
;
97 // DbusmenuServer methods:
98 dbusmenu_server_new_func server_new
= NULL
;
99 dbusmenu_server_set_root_func server_set_root
= NULL
;
101 // Properties that we set on menu items:
102 const char kPropertyEnabled
[] = "enabled";
103 const char kPropertyLabel
[] = "label";
104 const char kPropertyShortcut
[] = "shortcut";
105 const char kPropertyType
[] = "type";
106 const char kPropertyToggleType
[] = "toggle-type";
107 const char kPropertyToggleState
[] = "toggle-state";
108 const char kPropertyVisible
[] = "visible";
110 const char kTypeCheckmark
[] = "checkmark";
111 const char kTypeSeparator
[] = "separator";
113 // Data set on GObjectgs.
114 const char kTypeTag
[] = "type-tag";
115 const char kHistoryItem
[] = "history-item";
117 // The maximum number of most visited items to display.
118 const unsigned int kMostVisitedCount
= 8;
120 // The number of recently closed items to get.
121 const unsigned int kRecentlyClosedCount
= 8;
123 // Menus more than this many chars long will get trimmed.
124 const int kMaximumMenuWidthInChars
= 50;
126 // Constants used in menu definitions.
127 const int MENU_SEPARATOR
=-1;
128 const int MENU_END
= -2;
129 const int MENU_DISABLED_ID
= -3;
131 // These tag values are used to refer to menu items.
132 const int TAG_MOST_VISITED
= 1;
133 const int TAG_RECENTLY_CLOSED
= 2;
134 const int TAG_MOST_VISITED_HEADER
= 3;
135 const int TAG_RECENTLY_CLOSED_HEADER
= 4;
137 GlobalMenuBarCommand 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
},
145 { MENU_SEPARATOR
, MENU_SEPARATOR
},
147 { IDS_CREATE_SHORTCUTS
, IDC_CREATE_SHORTCUTS
},
149 { MENU_SEPARATOR
, MENU_SEPARATOR
},
151 { IDS_CLOSE_WINDOW_LINUX
, IDC_CLOSE_WINDOW
},
152 { IDS_CLOSE_TAB_LINUX
, IDC_CLOSE_TAB
},
153 { IDS_SAVE_PAGE
, IDC_SAVE_PAGE
},
155 { MENU_SEPARATOR
, MENU_SEPARATOR
},
157 { IDS_PRINT
, IDC_PRINT
},
159 { MENU_END
, MENU_END
}
162 GlobalMenuBarCommand edit_menu
[] = {
163 { IDS_CUT
, IDC_CUT
},
164 { IDS_COPY
, IDC_COPY
},
165 { IDS_PASTE
, IDC_PASTE
},
167 { MENU_SEPARATOR
, MENU_SEPARATOR
},
169 { IDS_FIND
, IDC_FIND
},
171 { MENU_SEPARATOR
, MENU_SEPARATOR
},
173 { IDS_PREFERENCES
, IDC_OPTIONS
},
175 { MENU_END
, MENU_END
}
178 GlobalMenuBarCommand view_menu
[] = {
179 { IDS_SHOW_BOOKMARK_BAR
, IDC_SHOW_BOOKMARK_BAR
},
181 { MENU_SEPARATOR
, MENU_SEPARATOR
},
183 { IDS_STOP_MENU_LINUX
, IDC_STOP
},
184 { IDS_RELOAD_MENU_LINUX
, IDC_RELOAD
},
186 { MENU_SEPARATOR
, MENU_SEPARATOR
},
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
},
193 { MENU_END
, MENU_END
}
196 GlobalMenuBarCommand history_menu
[] = {
197 { IDS_HISTORY_HOME_LINUX
, IDC_HOME
},
198 { IDS_HISTORY_BACK_LINUX
, IDC_BACK
},
199 { IDS_HISTORY_FORWARD_LINUX
, IDC_FORWARD
},
201 { MENU_SEPARATOR
, MENU_SEPARATOR
},
203 { IDS_HISTORY_VISITED_LINUX
, MENU_DISABLED_ID
, TAG_MOST_VISITED_HEADER
},
205 { MENU_SEPARATOR
, MENU_SEPARATOR
},
207 { IDS_HISTORY_CLOSED_LINUX
, MENU_DISABLED_ID
, TAG_RECENTLY_CLOSED_HEADER
},
209 { MENU_SEPARATOR
, MENU_SEPARATOR
},
211 { IDS_SHOWFULLHISTORY_LINK
, IDC_SHOW_HISTORY
},
213 { MENU_END
, MENU_END
}
216 GlobalMenuBarCommand tools_menu
[] = {
217 { IDS_SHOW_DOWNLOADS
, IDC_SHOW_DOWNLOADS
},
218 { IDS_SHOW_HISTORY
, IDC_SHOW_HISTORY
},
219 { IDS_SHOW_EXTENSIONS
, IDC_MANAGE_EXTENSIONS
},
221 { MENU_SEPARATOR
, MENU_SEPARATOR
},
223 { IDS_TASK_MANAGER
, IDC_TASK_MANAGER
},
224 { IDS_CLEAR_BROWSING_DATA
, IDC_CLEAR_BROWSING_DATA
},
226 { MENU_SEPARATOR
, MENU_SEPARATOR
},
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
},
233 { MENU_END
, MENU_END
}
236 GlobalMenuBarCommand help_menu
[] = {
237 #if defined(GOOGLE_CHROME_BUILD)
238 { IDS_FEEDBACK
, IDC_FEEDBACK
},
240 { IDS_HELP_PAGE
, IDC_HELP_PAGE_VIA_MENU
},
241 { MENU_END
, MENU_END
}
244 void EnsureMethodsLoaded() {
245 static bool attempted_load
= false;
248 attempted_load
= true;
250 void* dbusmenu_lib
= dlopen("libdbusmenu-glib.so", RTLD_LAZY
);
252 dbusmenu_lib
= dlopen("libdbusmenu-glib.so.4", RTLD_LAZY
);
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"));
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"));
289 struct GlobalMenuBarX11::HistoryItem
{
290 HistoryItem() : session_id(0) {}
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.
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
;
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
;
311 DISALLOW_COPY_AND_ASSIGN(HistoryItem
);
314 GlobalMenuBarX11::GlobalMenuBarX11(BrowserView
* browser_view
,
315 BrowserDesktopWindowTreeHostX11
* host
)
316 : browser_(browser_view
->browser()),
317 profile_(browser_
->profile()),
318 browser_view_(browser_view
),
324 tab_restore_service_(NULL
),
325 weak_ptr_factory_(this) {
326 EnsureMethodsLoaded();
329 host_
->AddObserver(this);
332 GlobalMenuBarX11::~GlobalMenuBarX11() {
336 if (tab_restore_service_
)
337 tab_restore_service_
->RemoveObserver(this);
339 g_object_unref(server_
);
340 host_
->RemoveObserver(this);
345 std::string
GlobalMenuBarX11::GetPathForWindow(unsigned long xid
) {
346 return base::StringPrintf("/com/canonical/menu/%lX", xid
);
349 DbusmenuMenuitem
* GlobalMenuBarX11::BuildSeparator() {
350 DbusmenuMenuitem
* item
= menuitem_new();
351 menuitem_property_set(item
, kPropertyType
, kTypeSeparator
);
352 menuitem_property_set_bool(item
, kPropertyVisible
, true);
356 DbusmenuMenuitem
* GlobalMenuBarX11::BuildMenuItem(
357 const std::string
& label
,
359 DbusmenuMenuitem
* item
= menuitem_new();
360 menuitem_property_set(item
, kPropertyLabel
, label
.c_str());
361 menuitem_property_set_bool(item
, kPropertyVisible
, true);
364 g_object_set_data(G_OBJECT(item
), kTypeTag
, GINT_TO_POINTER(tag_id
));
369 void GlobalMenuBarX11::InitServer(unsigned long xid
) {
370 std::string path
= GetPathForWindow(xid
);
372 ANNOTATE_SCOPED_MEMORY_LEAK
; // http://crbug.com/314087
373 server_
= server_new(path
.c_str());
376 root_item_
= menuitem_new();
377 menuitem_property_set(root_item_
, kPropertyLabel
, "Root");
378 menuitem_property_set_bool(root_item_
, kPropertyVisible
, true);
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
);
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);
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
));
400 ui::Accelerator accelerator
;
401 if (browser_view_
->GetAccelerator(it
->first
, &accelerator
))
402 RegisterAccelerator(it
->second
, accelerator
);
404 chrome::AddCommandObserver(browser_
, it
->first
, this);
407 pref_change_registrar_
.Init(browser_
->profile()->GetPrefs());
408 pref_change_registrar_
.Add(
409 prefs::kShowBookmarkBar
,
410 base::Bind(&GlobalMenuBarX11::OnBookmarkBarVisibilityChanged
,
411 base::Unretained(this)));
412 OnBookmarkBarVisibilityChanged();
414 top_sites_
= profile_
->GetTopSites();
418 // Register for notification when TopSites changes so that we can update
420 registrar_
.Add(this, chrome::NOTIFICATION_TOP_SITES_CHANGED
,
421 content::Source
<history::TopSites
>(top_sites_
));
424 server_set_root(server_
, root_item_
);
427 void 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);
432 id_to_menu_item_
.clear();
434 pref_change_registrar_
.RemoveAll();
437 DbusmenuMenuitem
* GlobalMenuBarX11::BuildStaticMenu(
438 DbusmenuMenuitem
* parent
,
440 GlobalMenuBarCommand
* commands
) {
441 DbusmenuMenuitem
* top
= menuitem_new();
442 menuitem_property_set(
444 ui::RemoveWindowsStyleAccelerators(
445 l10n_util::GetStringUTF8(menu_str_id
)).c_str());
446 menuitem_property_set_bool(top
, kPropertyVisible
, true);
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();
454 std::string label
= ui::ConvertAcceleratorsFromWindowsStyle(
455 l10n_util::GetStringUTF8(commands
[i
].str_id
));
457 menu_item
= BuildMenuItem(label
, commands
[i
].tag
);
459 if (command_id
== MENU_DISABLED_ID
) {
460 menuitem_property_set_bool(menu_item
, kPropertyEnabled
, false);
462 if (command_id
== IDC_SHOW_BOOKMARK_BAR
)
463 menuitem_property_set(menu_item
, kPropertyToggleType
, kTypeCheckmark
);
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);
473 menuitem_child_append(top
, menu_item
);
474 g_object_unref(menu_item
);
477 menuitem_child_append(parent
, top
);
482 void 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
);
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");
496 char* name
= XKeysymToString(XKeysymForWindowsKeyCode(
497 accelerator
.key_code(), false));
502 g_variant_builder_add(&builder
, "s", name
);
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
);
509 menuitem_property_set_variant(item
, kPropertyShortcut
, outside_array
);
512 GlobalMenuBarX11::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
;
524 void GlobalMenuBarX11::AddHistoryItemToMenu(HistoryItem
* item
,
525 DbusmenuMenuitem
* menu
,
528 base::string16 title
= item
->title
;
529 std::string url_string
= item
->url
.possibly_invalid_spec();
532 title
= base::UTF8ToUTF16(url_string
);
533 gfx::ElideString(title
, kMaximumMenuWidthInChars
, &title
);
535 DbusmenuMenuitem
* menu_item
= BuildMenuItem(base::UTF16ToUTF8(title
), tag
);
536 g_signal_connect(menu_item
, "item-activated",
537 G_CALLBACK(OnHistoryItemActivatedThunk
), this);
539 g_object_set_data_full(G_OBJECT(menu_item
), kHistoryItem
, item
,
541 menuitem_child_add_position(menu
, menu_item
, index
);
542 g_object_unref(menu_item
);
545 void GlobalMenuBarX11::GetTopSitesData() {
548 top_sites_
->GetMostVisitedURLs(
549 base::Bind(&GlobalMenuBarX11::OnTopSitesReceived
,
550 weak_ptr_factory_
.GetWeakPtr()), false);
553 void GlobalMenuBarX11::OnTopSitesReceived(
554 const history::MostVisitedURLList
& visited_list
) {
555 ClearMenuSection(history_menu_
, TAG_MOST_VISITED
);
557 int index
= GetIndexOfMenuItemWithTag(history_menu_
,
558 TAG_MOST_VISITED_HEADER
) + 1;
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.
565 HistoryItem
* item
= new HistoryItem();
566 item
->title
= visited
.title
;
567 item
->url
= visited
.url
;
569 AddHistoryItemToMenu(item
,
576 void 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(it
->second
, kPropertyToggleState
,
584 prefs
->GetBoolean(prefs::kShowBookmarkBar
));
588 int GlobalMenuBarX11::GetIndexOfMenuItemWithTag(DbusmenuMenuitem
* menu
,
590 GList
* childs
= menuitem_get_children(menu
);
592 for (; childs
!= NULL
; childs
= childs
->next
, i
++) {
594 GPOINTER_TO_INT(g_object_get_data(G_OBJECT(childs
->data
), kTypeTag
));
603 void GlobalMenuBarX11::ClearMenuSection(DbusmenuMenuitem
* menu
, int tag_id
) {
604 std::vector
<DbusmenuMenuitem
*> menuitems_to_delete
;
606 GList
* childs
= menuitem_get_children(menu
);
607 for (; childs
!= NULL
; childs
= childs
->next
) {
608 DbusmenuMenuitem
* current_item
= reinterpret_cast<DbusmenuMenuitem
*>(
610 ClearMenuSection(current_item
, tag_id
);
613 GPOINTER_TO_INT(g_object_get_data(G_OBJECT(childs
->data
), kTypeTag
));
615 menuitems_to_delete
.push_back(current_item
);
618 for (std::vector
<DbusmenuMenuitem
*>::const_iterator it
=
619 menuitems_to_delete
.begin(); it
!= menuitems_to_delete
.end(); ++it
) {
620 menuitem_child_delete(menu
, *it
);
625 void GlobalMenuBarX11::DeleteHistoryItem(void* void_item
) {
627 reinterpret_cast<GlobalMenuBarX11::HistoryItem
*>(void_item
);
631 void GlobalMenuBarX11::EnabledStateChangedForCommand(int id
, bool enabled
) {
632 CommandIDMenuItemMap::iterator it
= id_to_menu_item_
.find(id
);
633 if (it
!= id_to_menu_item_
.end())
634 menuitem_property_set_bool(it
->second
, kPropertyEnabled
, enabled
);
637 void GlobalMenuBarX11::Observe(int type
,
638 const content::NotificationSource
& source
,
639 const content::NotificationDetails
& details
) {
640 if (type
== chrome::NOTIFICATION_TOP_SITES_CHANGED
) {
647 void GlobalMenuBarX11::TabRestoreServiceChanged(TabRestoreService
* service
) {
648 const TabRestoreService::Entries
& entries
= service
->entries();
650 ClearMenuSection(history_menu_
, TAG_RECENTLY_CLOSED
);
652 // We'll get the index the "Recently Closed" header. (This can vary depending
653 // on the number of "Most Visited" items.
654 int index
= GetIndexOfMenuItemWithTag(history_menu_
,
655 TAG_RECENTLY_CLOSED_HEADER
) + 1;
657 unsigned int added_count
= 0;
658 for (TabRestoreService::Entries::const_iterator it
= entries
.begin();
659 it
!= entries
.end() && added_count
< kRecentlyClosedCount
; ++it
) {
660 TabRestoreService::Entry
* entry
= *it
;
662 if (entry
->type
== TabRestoreService::WINDOW
) {
663 TabRestoreService::Window
* entry_win
=
664 static_cast<TabRestoreService::Window
*>(entry
);
665 std::vector
<TabRestoreService::Tab
>& tabs
= entry_win
->tabs
;
669 // Create the item for the parent/window.
670 HistoryItem
* item
= new HistoryItem();
671 item
->session_id
= entry_win
->id
;
673 std::string title
= item
->tabs
.size() == 1 ?
674 l10n_util::GetStringUTF8(
675 IDS_NEW_TAB_RECENTLY_CLOSED_WINDOW_SINGLE
) :
676 l10n_util::GetStringFUTF8(
677 IDS_NEW_TAB_RECENTLY_CLOSED_WINDOW_MULTIPLE
,
678 base::IntToString16(item
->tabs
.size()));
679 DbusmenuMenuitem
* parent_item
= BuildMenuItem(
680 title
, TAG_RECENTLY_CLOSED
);
681 menuitem_child_add_position(history_menu_
, parent_item
, index
++);
682 g_object_unref(parent_item
);
684 // The mac version of this code allows the user to click on the parent
685 // menu item to have the same effect as clicking the restore window
686 // submenu item. GTK+ helpfully activates a menu item when it shows a
687 // submenu so toss that feature out.
688 DbusmenuMenuitem
* restore_item
= BuildMenuItem(
689 l10n_util::GetStringUTF8(
690 IDS_HISTORY_CLOSED_RESTORE_WINDOW_LINUX
).c_str(),
691 TAG_RECENTLY_CLOSED
);
692 g_signal_connect(restore_item
, "item-activated",
693 G_CALLBACK(OnHistoryItemActivatedThunk
), this);
694 g_object_set_data_full(G_OBJECT(restore_item
), kHistoryItem
, item
,
696 menuitem_child_append(parent_item
, restore_item
);
697 g_object_unref(restore_item
);
699 DbusmenuMenuitem
* separator
= BuildSeparator();
700 menuitem_child_append(parent_item
, separator
);
701 g_object_unref(separator
);
703 // Loop over the window's tabs and add them to the submenu.
705 std::vector
<TabRestoreService::Tab
>::const_iterator iter
;
706 for (iter
= tabs
.begin(); iter
!= tabs
.end(); ++iter
) {
707 TabRestoreService::Tab tab
= *iter
;
708 HistoryItem
* tab_item
= HistoryItemForTab(tab
);
709 item
->tabs
.push_back(tab_item
);
710 AddHistoryItemToMenu(tab_item
,
717 } else if (entry
->type
== TabRestoreService::TAB
) {
718 TabRestoreService::Tab
* tab
= static_cast<TabRestoreService::Tab
*>(entry
);
719 HistoryItem
* item
= HistoryItemForTab(*tab
);
720 AddHistoryItemToMenu(item
,
729 void GlobalMenuBarX11::TabRestoreServiceDestroyed(
730 TabRestoreService
* service
) {
731 tab_restore_service_
= NULL
;
734 void GlobalMenuBarX11::OnWindowMapped(unsigned long xid
) {
738 GlobalMenuBarRegistrarX11::GetInstance()->OnWindowMapped(xid
);
741 void GlobalMenuBarX11::OnWindowUnmapped(unsigned long xid
) {
742 GlobalMenuBarRegistrarX11::GetInstance()->OnWindowUnmapped(xid
);
745 void GlobalMenuBarX11::OnItemActivated(DbusmenuMenuitem
* item
,
746 unsigned int timestamp
) {
747 int id
= GPOINTER_TO_INT(g_object_get_data(G_OBJECT(item
), "command-id"));
748 chrome::ExecuteCommand(browser_
, id
);
751 void GlobalMenuBarX11::OnHistoryItemActivated(DbusmenuMenuitem
* sender
,
752 unsigned int timestamp
) {
753 // Note: We don't have access to the event modifiers used to click the menu
754 // item since that happens in a different process.
755 HistoryItem
* item
= reinterpret_cast<HistoryItem
*>(
756 g_object_get_data(G_OBJECT(sender
), kHistoryItem
));
758 // If this item can be restored using TabRestoreService, do so. Otherwise,
759 // just load the URL.
760 TabRestoreService
* service
=
761 TabRestoreServiceFactory::GetForProfile(profile_
);
762 if (item
->session_id
&& service
) {
763 service
->RestoreEntryById(browser_
->tab_restore_service_delegate(),
764 item
->session_id
, browser_
->host_desktop_type(),
767 DCHECK(item
->url
.is_valid());
768 browser_
->OpenURL(content::OpenURLParams(
772 content::PAGE_TRANSITION_AUTO_BOOKMARK
,
777 void GlobalMenuBarX11::OnHistoryMenuAboutToShow(DbusmenuMenuitem
* item
) {
778 if (!tab_restore_service_
) {
779 tab_restore_service_
= TabRestoreServiceFactory::GetForProfile(profile_
);
780 if (tab_restore_service_
) {
781 tab_restore_service_
->LoadTabsFromLastSession();
782 tab_restore_service_
->AddObserver(this);
784 // If LoadTabsFromLastSession doesn't load tabs, it won't call
785 // TabRestoreServiceChanged(). This ensures that all new windows after
786 // the first one will have their menus populated correctly.
787 TabRestoreServiceChanged(tab_restore_service_
);