Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(822)

Side by Side Diff: chrome/browser/ui/gtk/global_menu_bar.cc

Issue 6840068: GTK: Add Recently Closed tabs to the History menu in the global menu bar. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Add bookmark menu stub for string freeze reasons Created 9 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2011 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/ui/gtk/global_menu_bar.h" 5 #include "chrome/browser/ui/gtk/global_menu_bar.h"
6 6
7 #include <gtk/gtk.h> 7 #include <gtk/gtk.h>
8 8
9 #include "base/utf_string_conversions.h"
10 #include "base/stl_util-inl.h"
11 #include "base/string_number_conversions.h"
9 #include "chrome/app/chrome_command_ids.h" 12 #include "chrome/app/chrome_command_ids.h"
13 #include "chrome/browser/favicon_service.h"
10 #include "chrome/browser/prefs/pref_service.h" 14 #include "chrome/browser/prefs/pref_service.h"
11 #include "chrome/browser/profiles/profile.h" 15 #include "chrome/browser/profiles/profile.h"
12 #include "chrome/browser/ui/browser.h" 16 #include "chrome/browser/ui/browser.h"
17 #include "chrome/browser/ui/browser_tab_restore_service_delegate.h"
13 #include "chrome/browser/ui/gtk/accelerators_gtk.h" 18 #include "chrome/browser/ui/gtk/accelerators_gtk.h"
19 #include "chrome/browser/ui/gtk/gtk_util.h"
20 #include "chrome/browser/ui/gtk/gtk_theme_service.h"
21 #include "chrome/browser/ui/gtk/owned_widget_gtk.h"
14 #include "chrome/common/pref_names.h" 22 #include "chrome/common/pref_names.h"
23 #include "chrome/common/url_constants.h"
15 #include "content/common/notification_service.h" 24 #include "content/common/notification_service.h"
16 #include "grit/generated_resources.h" 25 #include "grit/generated_resources.h"
17 #include "ui/base/l10n/l10n_util.h" 26 #include "ui/base/l10n/l10n_util.h"
27 #include "ui/base/text/text_elider.h"
28 #include "ui/gfx/codec/png_codec.h"
18 #include "ui/gfx/gtk_util.h" 29 #include "ui/gfx/gtk_util.h"
19 30
20 struct GlobalMenuBarCommand { 31 struct GlobalMenuBarCommand {
21 int str_id; 32 int str_id;
22 int command; 33 int command;
34 int tag;
35 };
36
37 struct GlobalMenuBar::ClearMenuClosure {
38 GtkWidget* container;
39 GlobalMenuBar* menu_bar;
40 int tag;
41 };
42
43 struct GlobalMenuBar::GetIndexClosure {
44 bool found;
45 int current_index;
46 int tag;
23 }; 47 };
24 48
25 namespace { 49 namespace {
26 50
27 const int MENU_SEPARATOR =-1; 51 const int MENU_SEPARATOR =-1;
28 const int MENU_END = -2; 52 const int MENU_END = -2;
53 const int MENU_DISABLED_LABEL = -3;
54
55 const int TAG_NORMAL = 0;
56 const int TAG_MOST_VISITED = 1;
57 const int TAG_RECENTLY_CLOSED = 2;
58 const int TAG_MOST_VISITED_HEADER = 3;
59 const int TAG_RECENTLY_CLOSED_HEADER = 4;
60
61 // The number of recently closed items to get.
62 const unsigned int kRecentlyClosedCount = 10;
63
64 // Menus more than this many chars long will get trimmed.
65 const int kMaximumMenuWidthInChars = 50;
29 66
30 GlobalMenuBarCommand file_menu[] = { 67 GlobalMenuBarCommand file_menu[] = {
31 { IDS_NEW_TAB, IDC_NEW_TAB }, 68 { IDS_NEW_TAB, IDC_NEW_TAB },
32 { IDS_NEW_WINDOW, IDC_NEW_WINDOW }, 69 { IDS_NEW_WINDOW, IDC_NEW_WINDOW },
33 { IDS_NEW_INCOGNITO_WINDOW, IDC_NEW_INCOGNITO_WINDOW }, 70 { IDS_NEW_INCOGNITO_WINDOW, IDC_NEW_INCOGNITO_WINDOW },
34 { IDS_REOPEN_CLOSED_TABS_LINUX, IDC_RESTORE_TAB }, 71 { IDS_REOPEN_CLOSED_TABS_LINUX, IDC_RESTORE_TAB },
35 { IDS_OPEN_FILE_LINUX, IDC_OPEN_FILE }, 72 { IDS_OPEN_FILE_LINUX, IDC_OPEN_FILE },
36 { IDS_OPEN_LOCATION_LINUX, IDC_FOCUS_LOCATION }, 73 { IDS_OPEN_LOCATION_LINUX, IDC_FOCUS_LOCATION },
37 74
38 { MENU_SEPARATOR, MENU_SEPARATOR }, 75 { MENU_SEPARATOR, MENU_SEPARATOR },
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after
92 { MENU_SEPARATOR, MENU_SEPARATOR }, 129 { MENU_SEPARATOR, MENU_SEPARATOR },
93 130
94 { IDS_FULLSCREEN, IDC_FULLSCREEN }, 131 { IDS_FULLSCREEN, IDC_FULLSCREEN },
95 { IDS_TEXT_DEFAULT_LINUX, IDC_ZOOM_NORMAL }, 132 { IDS_TEXT_DEFAULT_LINUX, IDC_ZOOM_NORMAL },
96 { IDS_TEXT_BIGGER_LINUX, IDC_ZOOM_PLUS }, 133 { IDS_TEXT_BIGGER_LINUX, IDC_ZOOM_PLUS },
97 { IDS_TEXT_SMALLER_LINUX, IDC_ZOOM_MINUS }, 134 { IDS_TEXT_SMALLER_LINUX, IDC_ZOOM_MINUS },
98 135
99 { MENU_END, MENU_END } 136 { MENU_END, MENU_END }
100 }; 137 };
101 138
139 GlobalMenuBarCommand history_menu[] = {
140 { IDS_HISTORY_HOME_LINUX, IDC_HOME },
141 { IDS_HISTORY_BACK_LINUX, IDC_BACK },
142 { IDS_HISTORY_FORWARD_LINUX, IDC_FORWARD },
143
144 { MENU_SEPARATOR, MENU_SEPARATOR },
145
146 { IDS_HISTORY_VISITED_LINUX, MENU_DISABLED_LABEL, TAG_MOST_VISITED_HEADER },
147
148 { MENU_SEPARATOR, MENU_SEPARATOR },
149
150 { IDS_HISTORY_CLOSED_LINUX, MENU_DISABLED_LABEL, TAG_RECENTLY_CLOSED_HEADER },
151
152 { MENU_SEPARATOR, MENU_SEPARATOR },
153
154 { IDS_SHOWFULLHISTORY_LINK, IDC_SHOW_HISTORY },
155
156 { MENU_END, MENU_END }
157 };
158
159 GlobalMenuBarCommand bookmark_menu[] = {
160 { IDS_BOOKMARK_MANAGER, IDC_SHOW_BOOKMARK_MANAGER },
161 { IDS_BOOKMARK_CURRENT_PAGE_LINUX, IDC_BOOKMARK_PAGE },
162 { IDS_BOOKMARK_ALL_TABS_LINUX, IDC_BOOKMARK_ALL_TABS },
163
164 { MENU_SEPARATOR, MENU_SEPARATOR },
165 // TODO(erg): Real implementation of bookmark bar bookmarks!
166 { MENU_SEPARATOR, MENU_SEPARATOR },
167
168 { IDS_BOOMARK_BAR_OPEN_ALL, IDC_BOOKMARK_BAR_OPEN_ALL },
169 { IDS_BOOMARK_BAR_OPEN_ALL_NEW_WINDOW, IDC_BOOKMARK_BAR_OPEN_ALL_NEW_WINDOW },
170 { IDS_BOOMARK_BAR_OPEN_ALL_INCOGNITO, IDC_BOOKMARK_BAR_OPEN_ALL_INCOGNITO },
171
172 { MENU_SEPARATOR, MENU_SEPARATOR },
173 // TODO(erg): "Other bookmarks" bookmarks
174
175 { MENU_END, MENU_END }
176 };
177
102 GlobalMenuBarCommand tools_menu[] = { 178 GlobalMenuBarCommand tools_menu[] = {
103 { IDS_SHOW_DOWNLOADS, IDC_SHOW_DOWNLOADS }, 179 { IDS_SHOW_DOWNLOADS, IDC_SHOW_DOWNLOADS },
104 { IDS_SHOW_HISTORY, IDC_SHOW_HISTORY }, 180 { IDS_SHOW_HISTORY, IDC_SHOW_HISTORY },
105 { IDS_SHOW_EXTENSIONS, IDC_MANAGE_EXTENSIONS }, 181 { IDS_SHOW_EXTENSIONS, IDC_MANAGE_EXTENSIONS },
106 182
107 { MENU_SEPARATOR, MENU_SEPARATOR }, 183 { MENU_SEPARATOR, MENU_SEPARATOR },
108 184
109 { IDS_TASK_MANAGER, IDC_TASK_MANAGER }, 185 { IDS_TASK_MANAGER, IDC_TASK_MANAGER },
110 { IDS_CLEAR_BROWSING_DATA, IDC_CLEAR_BROWSING_DATA }, 186 { IDS_CLEAR_BROWSING_DATA, IDC_CLEAR_BROWSING_DATA },
111 187
112 { MENU_SEPARATOR, MENU_SEPARATOR }, 188 { MENU_SEPARATOR, MENU_SEPARATOR },
113 189
114 { IDS_VIEW_SOURCE, IDC_VIEW_SOURCE }, 190 { IDS_VIEW_SOURCE, IDC_VIEW_SOURCE },
115 { IDS_DEV_TOOLS, IDC_DEV_TOOLS }, 191 { IDS_DEV_TOOLS, IDC_DEV_TOOLS },
116 { IDS_DEV_TOOLS_CONSOLE, IDC_DEV_TOOLS_CONSOLE }, 192 { IDS_DEV_TOOLS_CONSOLE, IDC_DEV_TOOLS_CONSOLE },
117 193
118 { MENU_END, MENU_END } 194 { MENU_END, MENU_END }
119 }; 195 };
120 196
121 GlobalMenuBarCommand help_menu[] = { 197 GlobalMenuBarCommand help_menu[] = {
122 { IDS_FEEDBACK, IDC_FEEDBACK }, 198 { IDS_FEEDBACK, IDC_FEEDBACK },
123 { IDS_HELP_PAGE , IDC_HELP_PAGE }, 199 { IDS_HELP_PAGE , IDC_HELP_PAGE },
124 { MENU_END, MENU_END } 200 { MENU_END, MENU_END }
125 }; 201 };
126 202
127 } // namespace 203 } // namespace
128 204
129 GlobalMenuBar::GlobalMenuBar(Browser* browser, 205 class GlobalMenuBar::HistoryItem {
130 BrowserWindowGtk* window) 206 public:
207 HistoryItem()
208 : icon_requested(false),
209 menu_item(NULL),
210 session_id(0) {}
211
212 // The title for the menu item.
213 string16 title;
214 // The URL that will be navigated to if the user selects this item.
215 GURL url;
216
217 // If the icon is being requested from the FaviconService, |icon_requested|
218 // will be true and |icon_handle| will be non-NULL. If this is false, then
219 // |icon_handle| will be NULL.
220 bool icon_requested;
221 // The Handle given to us by the FaviconService for the icon fetch request.
222 FaviconService::Handle icon_handle;
223
224 // The icon as a GtkImage for inclusion in a GtkImageMenuItem.
225 OwnedWidgetGtk icon_image;
226
227 // A pointer to the menu_item. This is a weak reference in the GTK+ version
228 // because the GtkMenu must sink the reference.
229 GtkWidget* menu_item;
230
231 // This ID is unique for a browser session and can be passed to the
232 // TabRestoreService to re-open the closed window or tab that this
233 // references. A non-0 session ID indicates that this is an entry can be
234 // restored that way. Otherwise, the URL will be used to open the item and
235 // this ID will be 0.
236 SessionID::id_type session_id;
237
238 // If the HistoryItem is a window, this will be the vector of tabs. Note
239 // that this is a list of weak references. The |menu_item_map_| is the owner
240 // of all items. If it is not a window, then the entry is a single page and
241 // the vector will be empty.
242 std::vector<HistoryItem*> tabs;
243
244 private:
245 DISALLOW_COPY_AND_ASSIGN(HistoryItem);
246 };
247
248
Evan Stade 2011/04/15 19:00:41 extra line
249 GlobalMenuBar::GlobalMenuBar(Browser* browser)
131 : browser_(browser), 250 : browser_(browser),
132 browser_window_(window), 251 profile_(browser_->profile()),
252 default_favicon_(NULL),
133 menu_bar_(gtk_menu_bar_new()), 253 menu_bar_(gtk_menu_bar_new()),
134 dummy_accel_group_(gtk_accel_group_new()), 254 dummy_accel_group_(gtk_accel_group_new()),
135 block_activation_(false) { 255 block_activation_(false),
256 history_menu_(NULL),
257 tab_restore_service_(NULL) {
136 // The global menu bar should never actually be shown in the app; it should 258 // The global menu bar should never actually be shown in the app; it should
137 // instead remain in our widget hierarchy simply to be noticed by third party 259 // instead remain in our widget hierarchy simply to be noticed by third party
138 // components. 260 // components.
139 gtk_widget_set_no_show_all(menu_bar_, TRUE); 261 gtk_widget_set_no_show_all(menu_bar_.get(), TRUE);
140 262
141 // Set a nice name so it shows up in gtkparasite and others. 263 // Set a nice name so it shows up in gtkparasite and others.
142 gtk_widget_set_name(menu_bar_, "chrome-hidden-global-menubar"); 264 gtk_widget_set_name(menu_bar_.get(), "chrome-hidden-global-menubar");
143 265
144 BuildGtkMenuFrom(IDS_FILE_MENU_LINUX, &id_to_menu_item_, file_menu); 266 BuildGtkMenuFrom(IDS_FILE_MENU_LINUX, &id_to_menu_item_, file_menu);
145 BuildGtkMenuFrom(IDS_EDIT_MENU_LINUX, &id_to_menu_item_, edit_menu); 267 BuildGtkMenuFrom(IDS_EDIT_MENU_LINUX, &id_to_menu_item_, edit_menu);
146 BuildGtkMenuFrom(IDS_VIEW_MENU_LINUX, &id_to_menu_item_, view_menu); 268 BuildGtkMenuFrom(IDS_VIEW_MENU_LINUX, &id_to_menu_item_, view_menu);
269 history_menu_ = BuildGtkMenuFrom(IDS_HISTORY_MENU_LINUX, &id_to_menu_item_,
270 history_menu);
271 BuildGtkMenuFrom(IDS_BOOKMARKS_MENU_LINUX, &id_to_menu_item_, bookmark_menu);
147 BuildGtkMenuFrom(IDS_TOOLS_MENU_LINUX, &id_to_menu_item_, tools_menu); 272 BuildGtkMenuFrom(IDS_TOOLS_MENU_LINUX, &id_to_menu_item_, tools_menu);
148 BuildGtkMenuFrom(IDS_HELP_MENU_LINUX, &id_to_menu_item_, help_menu); 273 BuildGtkMenuFrom(IDS_HELP_MENU_LINUX, &id_to_menu_item_, help_menu);
149 274
150 for (IDMenuItemMap::const_iterator it = id_to_menu_item_.begin(); 275 for (CommandIDMenuItemMap::const_iterator it = id_to_menu_item_.begin();
151 it != id_to_menu_item_.end(); ++it) { 276 it != id_to_menu_item_.end(); ++it) {
152 // Get the starting enabled state. 277 // Get the starting enabled state.
153 gtk_widget_set_sensitive( 278 gtk_widget_set_sensitive(
154 it->second, 279 it->second,
155 browser_->command_updater()->IsCommandEnabled(it->first)); 280 browser_->command_updater()->IsCommandEnabled(it->first));
156 281
157 // Set the accelerator for each menu item. 282 // Set the accelerator for each menu item.
158 const ui::AcceleratorGtk* accelerator_gtk = 283 const ui::AcceleratorGtk* accelerator_gtk =
159 AcceleratorsGtk::GetInstance()->GetPrimaryAcceleratorForCommand( 284 AcceleratorsGtk::GetInstance()->GetPrimaryAcceleratorForCommand(
160 it->first); 285 it->first);
161 if (accelerator_gtk) { 286 if (accelerator_gtk) {
162 gtk_widget_add_accelerator(it->second, 287 gtk_widget_add_accelerator(it->second,
163 "activate", 288 "activate",
164 dummy_accel_group_, 289 dummy_accel_group_,
165 accelerator_gtk->GetGdkKeyCode(), 290 accelerator_gtk->GetGdkKeyCode(),
166 accelerator_gtk->gdk_modifier_type(), 291 accelerator_gtk->gdk_modifier_type(),
167 GTK_ACCEL_VISIBLE); 292 GTK_ACCEL_VISIBLE);
168 } 293 }
169 294
170 browser_->command_updater()->AddCommandObserver(it->first, this); 295 browser_->command_updater()->AddCommandObserver(it->first, this);
171 } 296 }
172 297
298 default_favicon_ = GtkThemeService::GetDefaultFavicon(true);
299
300 if (profile_) {
301 tab_restore_service_ = profile_->GetTabRestoreService();
302 if (tab_restore_service_) {
303 tab_restore_service_->LoadTabsFromLastSession();
304 tab_restore_service_->AddObserver(this);
305
306 // If LoadTabsFromLastSession doesn't load tabs, it won't call
307 // TabRestoreServiceChanged(). This ensures that all new windows after
308 // the first one will have their menus populated correctly.
309 TabRestoreServiceChanged(tab_restore_service_);
310 }
311
312 registrar_.Add(this, NotificationType::BROWSER_THEME_CHANGED,
313 Source<Profile>(profile_));
314 }
315
173 // Listen for bookmark bar visibility changes and set the initial state. 316 // Listen for bookmark bar visibility changes and set the initial state.
174 registrar_.Add(this, NotificationType::BOOKMARK_BAR_VISIBILITY_PREF_CHANGED, 317 registrar_.Add(this, NotificationType::BOOKMARK_BAR_VISIBILITY_PREF_CHANGED,
175 NotificationService::AllSources()); 318 NotificationService::AllSources());
176 Observe(NotificationType::BOOKMARK_BAR_VISIBILITY_PREF_CHANGED, 319 Observe(NotificationType::BOOKMARK_BAR_VISIBILITY_PREF_CHANGED,
177 NotificationService::AllSources(), 320 NotificationService::AllSources(),
178 NotificationService::NoDetails()); 321 NotificationService::NoDetails());
179 } 322 }
180 323
181 GlobalMenuBar::~GlobalMenuBar() { 324 GlobalMenuBar::~GlobalMenuBar() {
182 for (IDMenuItemMap::const_iterator it = id_to_menu_item_.begin(); 325 for (CommandIDMenuItemMap::const_iterator it = id_to_menu_item_.begin();
183 it != id_to_menu_item_.end(); ++it) { 326 it != id_to_menu_item_.end(); ++it) {
184 browser_->command_updater()->RemoveCommandObserver(it->first, this); 327 browser_->command_updater()->RemoveCommandObserver(it->first, this);
185 } 328 }
186 329
330 if (tab_restore_service_)
331 tab_restore_service_->RemoveObserver(this);
332
333 STLDeleteContainerPairSecondPointers(menu_item_history_map_.begin(),
334 menu_item_history_map_.end());
335 menu_item_history_map_.clear();
336
187 g_object_unref(dummy_accel_group_); 337 g_object_unref(dummy_accel_group_);
188 } 338 }
189 339
190 void GlobalMenuBar::BuildGtkMenuFrom(int menu_str_id, 340 GtkWidget* GlobalMenuBar::BuildGtkMenuFrom(
191 std::map<int, GtkWidget*>* id_to_menu_item, 341 int menu_str_id,
192 GlobalMenuBarCommand* commands) { 342 std::map<int, GtkWidget*>* id_to_menu_item,
343 GlobalMenuBarCommand* commands) {
193 GtkWidget* menu = gtk_menu_new(); 344 GtkWidget* menu = gtk_menu_new();
194 for (int i = 0; commands[i].str_id != MENU_END; ++i) { 345 for (int i = 0; commands[i].str_id != MENU_END; ++i) {
195 GtkWidget* menu_item = NULL; 346 GtkWidget* menu_item = BuildMenuItem(
196 if (commands[i].str_id == MENU_SEPARATOR) { 347 commands[i].str_id, commands[i].command, commands[i].tag,
197 menu_item = gtk_separator_menu_item_new(); 348 id_to_menu_item, menu);
349 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
350 }
351
352 gtk_widget_show(menu);
353
354 GtkWidget* menu_item = gtk_menu_item_new_with_mnemonic(
355 gfx::ConvertAcceleratorsFromWindowsStyle(
356 l10n_util::GetStringUTF8(menu_str_id)).c_str());
357 gtk_menu_item_set_submenu(GTK_MENU_ITEM(menu_item), menu);
358 gtk_widget_show(menu_item);
359 gtk_menu_shell_append(GTK_MENU_SHELL(menu_bar_.get()), menu_item);
360
361 return menu;
362 }
363
364 GtkWidget* GlobalMenuBar::BuildMenuItem(
365 int string_id,
366 int command_id,
367 int tag_id,
368 std::map<int, GtkWidget*>* id_to_menu_item,
369 GtkWidget* menu_to_add_to) {
370 GtkWidget* menu_item = NULL;
371 if (string_id == MENU_SEPARATOR) {
372 menu_item = gtk_separator_menu_item_new();
373 } else {
374 std::string label =
375 gfx::ConvertAcceleratorsFromWindowsStyle(
376 l10n_util::GetStringUTF8(string_id));
377
378 if (command_id == IDC_SHOW_BOOKMARK_BAR)
379 menu_item = gtk_check_menu_item_new_with_mnemonic(label.c_str());
380 else
381 menu_item = gtk_menu_item_new_with_mnemonic(label.c_str());
382
383 if (tag_id) {
384 g_object_set_data(G_OBJECT(menu_item), "type-tag",
385 GINT_TO_POINTER(tag_id));
386 }
387
388 if (command_id == MENU_DISABLED_LABEL) {
389 gtk_widget_set_sensitive(menu_item, FALSE);
198 } else { 390 } else {
199 int command_id = commands[i].command;
200 std::string label =
201 gfx::ConvertAcceleratorsFromWindowsStyle(
202 l10n_util::GetStringUTF8(commands[i].str_id));
203
204 if (command_id == IDC_SHOW_BOOKMARK_BAR)
205 menu_item = gtk_check_menu_item_new_with_mnemonic(label.c_str());
206 else
207 menu_item = gtk_menu_item_new_with_mnemonic(label.c_str());
208
209 id_to_menu_item->insert(std::make_pair(command_id, menu_item)); 391 id_to_menu_item->insert(std::make_pair(command_id, menu_item));
210 g_object_set_data(G_OBJECT(menu_item), "command-id", 392 g_object_set_data(G_OBJECT(menu_item), "command-id",
211 GINT_TO_POINTER(command_id)); 393 GINT_TO_POINTER(command_id));
212 g_signal_connect(menu_item, "activate", 394 g_signal_connect(menu_item, "activate",
213 G_CALLBACK(OnItemActivatedThunk), this); 395 G_CALLBACK(OnItemActivatedThunk), this);
214 } 396 }
215 gtk_widget_show(menu_item); 397 }
216 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
217 }
218
219 gtk_widget_show(menu);
220
221 GtkWidget* menu_item = gtk_menu_item_new_with_mnemonic(
222 gfx::ConvertAcceleratorsFromWindowsStyle(
223 l10n_util::GetStringUTF8(menu_str_id)).c_str());
224 gtk_menu_item_set_submenu(GTK_MENU_ITEM(menu_item), menu);
225 gtk_widget_show(menu_item); 398 gtk_widget_show(menu_item);
226 399 return menu_item;
227 gtk_menu_shell_append(GTK_MENU_SHELL(menu_bar_), menu_item); 400 }
401
402 GlobalMenuBar::HistoryItem* GlobalMenuBar::HistoryItemForMenuItem(
403 GtkWidget* menu_item) {
404 MenuItemToHistoryMap::iterator it = menu_item_history_map_.find(menu_item);
405 if (it != menu_item_history_map_.end()) {
Evan Stade 2011/04/15 19:00:41 nit: return it != menu_item_history_map_.end() ? i
406 return it->second;
407 }
408 return NULL;
409 }
410
411 GlobalMenuBar::HistoryItem* GlobalMenuBar::HistoryItemForTab(
412 const TabRestoreService::Tab& entry) {
413 if (entry.navigations.empty())
414 return NULL;
415
416 const TabNavigation& current_navigation =
417 entry.navigations.at(entry.current_navigation_index);
418 if (current_navigation.virtual_url() == GURL(chrome::kChromeUINewTabURL))
419 return NULL;
420
421 HistoryItem* item = new HistoryItem();
422 item->title = current_navigation.title();
423 item->url = current_navigation.virtual_url();
424 item->session_id = entry.id;
425
426 // Tab navigations don't come with icons, so we always have to request them.
427 GetFaviconForHistoryItem(item);
428
429 return item;
430
431 }
432
433 GtkWidget* GlobalMenuBar::AddHistoryItemToMenu(HistoryItem* item,
434 GtkWidget* menu,
435 int tag,
436 int index) {
437 string16 title = item->title;
438 std::string url_string = item->url.possibly_invalid_spec();
439
440 if (title.empty())
441 title = UTF8ToUTF16(url_string);
442 ui::ElideString(title, kMaximumMenuWidthInChars, &title);
443
444 GtkWidget* menu_item = gtk_image_menu_item_new_with_label(
445 UTF16ToUTF8(title).c_str());
446 gtk_image_menu_item_set_always_show_image(GTK_IMAGE_MENU_ITEM(menu_item),
447 TRUE);
448 item->menu_item = menu_item;
449 gtk_widget_show(menu_item);
450 g_object_set_data(G_OBJECT(menu_item), "type-tag", GINT_TO_POINTER(tag));
451 g_signal_connect(menu_item, "activate",
452 G_CALLBACK(OnRecentlyClosedItemActivatedThunk), this);
453 if (item->icon_image.get()) {
454 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menu_item),
455 item->icon_image.get());
456 } else if (!item->tabs.size()) {
457 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menu_item),
458 gtk_image_new_from_pixbuf(default_favicon_));
459 }
460
461 std::string tooltip = gtk_util::BuildTooltipTitleFor(item->title, item->url);
462 gtk_widget_set_tooltip_markup(menu_item, tooltip.c_str());
463
464 menu_item_history_map_.insert(std::make_pair(menu_item, item));
465 gtk_menu_shell_insert(GTK_MENU_SHELL(menu), menu_item, index);
466
467 return menu_item;
468 }
469
470 void GlobalMenuBar::GetFaviconForHistoryItem(HistoryItem* item) {
471 FaviconService* service =
472 profile_->GetFaviconService(Profile::EXPLICIT_ACCESS);
473 FaviconService::Handle handle = service->GetFaviconForURL(
474 item->url,
475 history::FAVICON,
476 &favicon_consumer_,
477 NewCallback(this, &GlobalMenuBar::GotFaviconData));
478 favicon_consumer_.SetClientData(service, handle, item);
479 item->icon_handle = handle;
480 item->icon_requested = true;
481 }
482
483 void GlobalMenuBar::GotFaviconData(FaviconService::Handle handle,
484 history::FaviconData favicon) {
485 HistoryItem* item =
486 favicon_consumer_.GetClientData(
487 profile_->GetFaviconService(Profile::EXPLICIT_ACCESS), handle);
488 DCHECK(item);
489 item->icon_requested = false;
490 item->icon_handle = NULL;
491
492 SkBitmap icon;
493 if (favicon.is_valid() &&
494 gfx::PNGCodec::Decode(favicon.image_data->front(),
495 favicon.image_data->size(), &icon)) {
496 GdkPixbuf* pixbuf = gfx::GdkPixbufFromSkBitmap(&icon);
497 if (pixbuf) {
498 item->icon_image.Own(gtk_image_new_from_pixbuf(pixbuf));
499 g_object_unref(pixbuf);
500
501 if (item->menu_item) {
502 gtk_image_menu_item_set_image(
503 GTK_IMAGE_MENU_ITEM(item->menu_item),
504 item->icon_image.get());
505 }
506 }
507 }
508 }
509
510 void GlobalMenuBar::CancelFaviconRequest(HistoryItem* item) {
511 DCHECK(item);
512 if (item->icon_requested) {
513 FaviconService* service =
514 profile_->GetFaviconService(Profile::EXPLICIT_ACCESS);
515 service->CancelRequest(item->icon_handle);
516 item->icon_requested = false;
517 item->icon_handle = NULL;
518 }
519 }
520
521 int GlobalMenuBar::GetIndexOfMenuItemWithTag(GtkWidget* menu, int tag_id) {
522 GetIndexClosure closure;
523 closure.found = false;
524 closure.current_index = 0;
525 closure.tag = tag_id;
526
527 gtk_container_foreach(
528 GTK_CONTAINER(menu),
529 reinterpret_cast<void (*)(GtkWidget*, void*)>(GetIndexCallback),
530 &closure);
531
532 return closure.current_index;
533 }
534
535 void GlobalMenuBar::ClearMenuSection(GtkWidget* menu, int tag) {
536 ClearMenuClosure closure;
537 closure.container = menu;
538 closure.menu_bar = this;
539 closure.tag = tag;
540
541 gtk_container_foreach(
542 GTK_CONTAINER(menu),
543 reinterpret_cast<void (*)(GtkWidget*, void*)>(ClearMenuCallback),
544 &closure);
545 }
546
547 // static
548 void GlobalMenuBar::GetIndexCallback(GtkWidget* menu_item,
549 GetIndexClosure* closure) {
550 int tag = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(menu_item), "type-tag"));
551 if (tag == closure->tag)
552 closure->found = true;
553
554 if (!closure->found)
555 closure->current_index++;
556 }
557
558 // static
559 void GlobalMenuBar::ClearMenuCallback(GtkWidget* menu_item,
560 ClearMenuClosure* closure) {
561 DCHECK_NE(closure->tag, 0);
562
563 int tag = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(menu_item), "type-tag"));
564 if (closure->tag == tag) {
565 HistoryItem* item = closure->menu_bar->HistoryItemForMenuItem(menu_item);
566
567 if (item) {
568 closure->menu_bar->CancelFaviconRequest(item);
569 closure->menu_bar->menu_item_history_map_.erase(menu_item);
570 delete item;
571 }
572
573 GtkWidget* submenu = gtk_menu_item_get_submenu(GTK_MENU_ITEM(menu_item));
574 if (submenu) {
Evan Stade 2011/04/15 19:00:41 nit: no curlies
575 closure->menu_bar->ClearMenuSection(submenu, closure->tag);
576 }
577
578 gtk_container_remove(GTK_CONTAINER(closure->container), menu_item);
579 }
228 } 580 }
229 581
230 void GlobalMenuBar::EnabledStateChangedForCommand(int id, bool enabled) { 582 void GlobalMenuBar::EnabledStateChangedForCommand(int id, bool enabled) {
231 IDMenuItemMap::iterator it = id_to_menu_item_.find(id); 583 CommandIDMenuItemMap::iterator it = id_to_menu_item_.find(id);
232 if (it != id_to_menu_item_.end()) 584 if (it != id_to_menu_item_.end())
233 gtk_widget_set_sensitive(it->second, enabled); 585 gtk_widget_set_sensitive(it->second, enabled);
234 } 586 }
235 587
236 void GlobalMenuBar::Observe(NotificationType type, 588 void GlobalMenuBar::Observe(NotificationType type,
237 const NotificationSource& source, 589 const NotificationSource& source,
238 const NotificationDetails& details) { 590 const NotificationDetails& details) {
239 DCHECK(type.value == NotificationType::BOOKMARK_BAR_VISIBILITY_PREF_CHANGED); 591 if (type.value == NotificationType::BOOKMARK_BAR_VISIBILITY_PREF_CHANGED) {
592 CommandIDMenuItemMap::iterator it =
593 id_to_menu_item_.find(IDC_SHOW_BOOKMARK_BAR);
594 if (it != id_to_menu_item_.end()) {
595 PrefService* prefs = browser_->profile()->GetPrefs();
240 596
241 IDMenuItemMap::iterator it = id_to_menu_item_.find(IDC_SHOW_BOOKMARK_BAR); 597 block_activation_ = true;
242 if (it != id_to_menu_item_.end()) { 598 gtk_check_menu_item_set_active(
243 PrefService* prefs = browser_->profile()->GetPrefs(); 599 GTK_CHECK_MENU_ITEM(it->second),
600 prefs->GetBoolean(prefs::kShowBookmarkBar));
601 block_activation_ = false;
602 }
603 } else if (type.value == NotificationType::BROWSER_THEME_CHANGED) {
604 // Keeping track of which menu items have the default icon is going an
605 // error-prone pain, so instead just store the new default favicon and
606 // we'll update on the next menu change event.
607 default_favicon_ = GtkThemeService::GetDefaultFavicon(true);
608 } else {
609 NOTREACHED();
610 }
611 }
244 612
245 block_activation_ = true; 613 void GlobalMenuBar::TabRestoreServiceChanged(TabRestoreService* service) {
246 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(it->second), 614 const TabRestoreService::Entries& entries = service->entries();
247 prefs->GetBoolean(prefs::kShowBookmarkBar)); 615
248 block_activation_ = false; 616 ClearMenuSection(history_menu_, TAG_RECENTLY_CLOSED);
617
618 // We'll get the index the "Recently Closed" header. (This can vary depending
619 // on the number of "Most Visited" items.
620 int index = GetIndexOfMenuItemWithTag(history_menu_,
621 TAG_RECENTLY_CLOSED_HEADER) + 1;
622
623 unsigned int added_count = 0;
624 for (TabRestoreService::Entries::const_iterator it = entries.begin();
625 it != entries.end() && added_count < kRecentlyClosedCount; ++it) {
Evan Stade 2011/04/15 19:00:41 instead of added_count can we track the size of hi
Elliot Glaysher 2011/04/15 20:29:03 We can in this patch, but we won't be able to once
626 TabRestoreService::Entry* entry = *it;
627
628 if (entry->type == TabRestoreService::WINDOW) {
629 TabRestoreService::Window* entry_win = (TabRestoreService::Window*)entry;
Evan Stade 2011/04/15 19:00:41 C++ style cast
630 std::vector<TabRestoreService::Tab>& tabs = entry_win->tabs;
631 if (!tabs.size())
Evan Stade 2011/04/15 19:00:41 empty()
632 continue;
633
634 // Create the item for the parent/window. Do not set the title yet
635 // because the actual number of items that are in the menu will not be
636 // known until things like the NTP are filtered out, which is done when
637 // the tab items are actually created.
638 HistoryItem* item = new HistoryItem();
639 item->session_id = entry_win->id;
640
641 GtkWidget* submenu = gtk_menu_new();
642
643 GtkWidget* restore_item = gtk_menu_item_new_with_label(
644 l10n_util::GetStringUTF8(
645 IDS_HISTORY_CLOSED_RESTORE_WINDOW_LINUX).c_str());
646 g_object_set_data(G_OBJECT(restore_item), "type-tag",
647 GINT_TO_POINTER(TAG_RECENTLY_CLOSED));
648 g_signal_connect(restore_item, "activate",
649 G_CALLBACK(OnRecentlyClosedItemActivatedThunk), this);
650 gtk_widget_show(restore_item);
651
652 // The mac version of this code allows the user to click on the parent
653 // menu item to have the same effect as clicking the restore window
654 // submenu item. GTK+ helpfully activates a menu item when it shows a
655 // submenu so toss that feature out.
656 menu_item_history_map_.insert(std::make_pair(restore_item, item));
657 gtk_menu_shell_append(GTK_MENU_SHELL(submenu), restore_item);
658
659 GtkWidget* separator = gtk_separator_menu_item_new();
660 gtk_widget_show(separator);
661 gtk_menu_shell_append(GTK_MENU_SHELL(submenu), separator);
662
663 // Loop over the window's tabs and add them to the submenu.
664 int subindex = 2;
665 int subadd_count = 0;
666 std::vector<TabRestoreService::Tab>::const_iterator it;
667 for (it = tabs.begin(); it != tabs.end(); ++it) {
668 TabRestoreService::Tab tab = *it;
669 HistoryItem* tab_item = HistoryItemForTab(tab);
670 if (tab_item) {
671 ++subadd_count;
672 item->tabs.push_back(tab_item);
673 AddHistoryItemToMenu(tab_item, submenu, TAG_RECENTLY_CLOSED,
674 subindex++);
675 }
676 }
677
678 // Sometimes it is possible for there to not be any subitems for a given
679 // window; if that is the case, do not add the entry to the main menu.
680 if (subadd_count) {
Evan Stade 2011/04/15 19:00:41 use subindex > 2 instead?
681 // Now that the number of tabs that has been added is known, set the
682 // title of the parent menu item.
683 std::string title =
684 (item->tabs.size() == 1) ?
685 l10n_util::GetStringUTF8(
686 IDS_NEW_TAB_RECENTLY_CLOSED_WINDOW_SINGLE) :
687 l10n_util::GetStringFUTF8(
688 IDS_NEW_TAB_RECENTLY_CLOSED_WINDOW_MULTIPLE,
689 base::IntToString16(item->tabs.size()));
690
691 // Create the menu item parent. Unlike mac, it's can't be activated.
692 GtkWidget* parent_item = gtk_image_menu_item_new_with_label(
693 title.c_str());
694 gtk_widget_show(parent_item);
695 g_object_set_data(G_OBJECT(parent_item), "type-tag",
696 GINT_TO_POINTER(TAG_RECENTLY_CLOSED));
697 gtk_menu_item_set_submenu(GTK_MENU_ITEM(parent_item), submenu);
698
699 gtk_menu_shell_insert(GTK_MENU_SHELL(history_menu_), parent_item,
700 index++);
701 ++added_count;
702 } else {
703 // Clean up after this failed attempt to make a submenu.
704 menu_item_history_map_.erase(restore_item);
Evan Stade 2011/04/15 19:00:41 can we avoid adding it to the map in the first pla
705 g_object_ref_sink(submenu);
706 g_object_unref(submenu);
Evan Stade 2011/04/15 19:00:41 I think it would be a good idea to gtk_widget_dest
707 }
708 } else if (entry->type == TabRestoreService::TAB) {
709 TabRestoreService::Tab* tab =
710 static_cast<TabRestoreService::Tab*>(entry);
711 HistoryItem* item = HistoryItemForTab(*tab);
712 if (item) {
713 AddHistoryItemToMenu(item, history_menu_, TAG_RECENTLY_CLOSED, index++);
714 ++added_count;
715 }
716 }
249 } 717 }
250 } 718 }
251 719
720 void GlobalMenuBar::TabRestoreServiceDestroyed(
721 TabRestoreService* service) {
722 // Intentionally left blank. We hold a weak reference to the service.
723 }
724
252 void GlobalMenuBar::OnItemActivated(GtkWidget* sender) { 725 void GlobalMenuBar::OnItemActivated(GtkWidget* sender) {
253 if (block_activation_) 726 if (block_activation_)
254 return; 727 return;
255 728
256 int id = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(sender), "command-id")); 729 int id = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(sender), "command-id"));
257 browser_->ExecuteCommandIfEnabled(id); 730 browser_->ExecuteCommandIfEnabled(id);
258 } 731 }
732
733 void GlobalMenuBar::OnRecentlyClosedItemActivated(GtkWidget* sender) {
734 WindowOpenDisposition disposition =
735 gtk_util::DispositionForCurrentButtonPressEvent();
736 HistoryItem* item = HistoryItemForMenuItem(sender);
737
738 // If this item can be restored using TabRestoreService, do so. Otherwise,
739 // just load the URL.
740 TabRestoreService* service = browser_->profile()->GetTabRestoreService();
741 if (item->session_id && service) {
742 service->RestoreEntryById(browser_->tab_restore_service_delegate(),
743 item->session_id, false);
744 } else {
745 DCHECK(item->url.is_valid());
746 browser_->OpenURL(item->url, GURL(), disposition,
747 PageTransition::AUTO_BOOKMARK);
748 }
749 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698