Chromium Code Reviews| OLD | NEW |
|---|---|
| 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 Loading... | |
| 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 GlobalMenuBar::GlobalMenuBar(Browser* browser) | |
| 131 : browser_(browser), | 249 : browser_(browser), |
| 132 browser_window_(window), | 250 profile_(browser_->profile()), |
| 251 default_favicon_(NULL), | |
| 133 menu_bar_(gtk_menu_bar_new()), | 252 menu_bar_(gtk_menu_bar_new()), |
| 134 dummy_accel_group_(gtk_accel_group_new()), | 253 dummy_accel_group_(gtk_accel_group_new()), |
| 135 block_activation_(false) { | 254 block_activation_(false), |
| 255 history_menu_(NULL), | |
| 256 tab_restore_service_(NULL) { | |
| 136 // The global menu bar should never actually be shown in the app; it should | 257 // 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 | 258 // instead remain in our widget hierarchy simply to be noticed by third party |
| 138 // components. | 259 // components. |
| 139 gtk_widget_set_no_show_all(menu_bar_, TRUE); | 260 gtk_widget_set_no_show_all(menu_bar_.get(), TRUE); |
| 140 | 261 |
| 141 // Set a nice name so it shows up in gtkparasite and others. | 262 // Set a nice name so it shows up in gtkparasite and others. |
| 142 gtk_widget_set_name(menu_bar_, "chrome-hidden-global-menubar"); | 263 gtk_widget_set_name(menu_bar_.get(), "chrome-hidden-global-menubar"); |
| 143 | 264 |
| 144 BuildGtkMenuFrom(IDS_FILE_MENU_LINUX, &id_to_menu_item_, file_menu); | 265 BuildGtkMenuFrom(IDS_FILE_MENU_LINUX, &id_to_menu_item_, file_menu); |
| 145 BuildGtkMenuFrom(IDS_EDIT_MENU_LINUX, &id_to_menu_item_, edit_menu); | 266 BuildGtkMenuFrom(IDS_EDIT_MENU_LINUX, &id_to_menu_item_, edit_menu); |
| 146 BuildGtkMenuFrom(IDS_VIEW_MENU_LINUX, &id_to_menu_item_, view_menu); | 267 BuildGtkMenuFrom(IDS_VIEW_MENU_LINUX, &id_to_menu_item_, view_menu); |
| 268 history_menu_ = BuildGtkMenuFrom(IDS_HISTORY_MENU_LINUX, &id_to_menu_item_, | |
| 269 history_menu); | |
| 270 BuildGtkMenuFrom(IDS_BOOKMARKS_MENU_LINUX, &id_to_menu_item_, bookmark_menu); | |
| 147 BuildGtkMenuFrom(IDS_TOOLS_MENU_LINUX, &id_to_menu_item_, tools_menu); | 271 BuildGtkMenuFrom(IDS_TOOLS_MENU_LINUX, &id_to_menu_item_, tools_menu); |
| 148 BuildGtkMenuFrom(IDS_HELP_MENU_LINUX, &id_to_menu_item_, help_menu); | 272 BuildGtkMenuFrom(IDS_HELP_MENU_LINUX, &id_to_menu_item_, help_menu); |
| 149 | 273 |
| 150 for (IDMenuItemMap::const_iterator it = id_to_menu_item_.begin(); | 274 for (CommandIDMenuItemMap::const_iterator it = id_to_menu_item_.begin(); |
| 151 it != id_to_menu_item_.end(); ++it) { | 275 it != id_to_menu_item_.end(); ++it) { |
| 152 // Get the starting enabled state. | 276 // Get the starting enabled state. |
| 153 gtk_widget_set_sensitive( | 277 gtk_widget_set_sensitive( |
| 154 it->second, | 278 it->second, |
| 155 browser_->command_updater()->IsCommandEnabled(it->first)); | 279 browser_->command_updater()->IsCommandEnabled(it->first)); |
| 156 | 280 |
| 157 // Set the accelerator for each menu item. | 281 // Set the accelerator for each menu item. |
| 158 const ui::AcceleratorGtk* accelerator_gtk = | 282 const ui::AcceleratorGtk* accelerator_gtk = |
| 159 AcceleratorsGtk::GetInstance()->GetPrimaryAcceleratorForCommand( | 283 AcceleratorsGtk::GetInstance()->GetPrimaryAcceleratorForCommand( |
| 160 it->first); | 284 it->first); |
| 161 if (accelerator_gtk) { | 285 if (accelerator_gtk) { |
| 162 gtk_widget_add_accelerator(it->second, | 286 gtk_widget_add_accelerator(it->second, |
| 163 "activate", | 287 "activate", |
| 164 dummy_accel_group_, | 288 dummy_accel_group_, |
| 165 accelerator_gtk->GetGdkKeyCode(), | 289 accelerator_gtk->GetGdkKeyCode(), |
| 166 accelerator_gtk->gdk_modifier_type(), | 290 accelerator_gtk->gdk_modifier_type(), |
| 167 GTK_ACCEL_VISIBLE); | 291 GTK_ACCEL_VISIBLE); |
| 168 } | 292 } |
| 169 | 293 |
| 170 browser_->command_updater()->AddCommandObserver(it->first, this); | 294 browser_->command_updater()->AddCommandObserver(it->first, this); |
| 171 } | 295 } |
| 172 | 296 |
| 297 default_favicon_ = GtkThemeService::GetDefaultFavicon(true); | |
| 298 | |
| 299 if (profile_) { | |
| 300 tab_restore_service_ = profile_->GetTabRestoreService(); | |
| 301 if (tab_restore_service_) { | |
| 302 tab_restore_service_->LoadTabsFromLastSession(); | |
| 303 tab_restore_service_->AddObserver(this); | |
| 304 | |
| 305 // If LoadTabsFromLastSession doesn't load tabs, it won't call | |
| 306 // TabRestoreServiceChanged(). This ensures that all new windows after | |
| 307 // the first one will have their menus populated correctly. | |
| 308 TabRestoreServiceChanged(tab_restore_service_); | |
| 309 } | |
| 310 | |
| 311 registrar_.Add(this, NotificationType::BROWSER_THEME_CHANGED, | |
| 312 Source<Profile>(profile_)); | |
| 313 } | |
| 314 | |
| 173 // Listen for bookmark bar visibility changes and set the initial state. | 315 // Listen for bookmark bar visibility changes and set the initial state. |
| 174 registrar_.Add(this, NotificationType::BOOKMARK_BAR_VISIBILITY_PREF_CHANGED, | 316 registrar_.Add(this, NotificationType::BOOKMARK_BAR_VISIBILITY_PREF_CHANGED, |
| 175 NotificationService::AllSources()); | 317 NotificationService::AllSources()); |
| 176 Observe(NotificationType::BOOKMARK_BAR_VISIBILITY_PREF_CHANGED, | 318 Observe(NotificationType::BOOKMARK_BAR_VISIBILITY_PREF_CHANGED, |
| 177 NotificationService::AllSources(), | 319 NotificationService::AllSources(), |
| 178 NotificationService::NoDetails()); | 320 NotificationService::NoDetails()); |
| 179 } | 321 } |
| 180 | 322 |
| 181 GlobalMenuBar::~GlobalMenuBar() { | 323 GlobalMenuBar::~GlobalMenuBar() { |
| 182 for (IDMenuItemMap::const_iterator it = id_to_menu_item_.begin(); | 324 for (CommandIDMenuItemMap::const_iterator it = id_to_menu_item_.begin(); |
| 183 it != id_to_menu_item_.end(); ++it) { | 325 it != id_to_menu_item_.end(); ++it) { |
| 184 browser_->command_updater()->RemoveCommandObserver(it->first, this); | 326 browser_->command_updater()->RemoveCommandObserver(it->first, this); |
| 185 } | 327 } |
| 186 | 328 |
| 329 if (tab_restore_service_) | |
| 330 tab_restore_service_->RemoveObserver(this); | |
| 331 | |
| 332 STLDeleteContainerPairSecondPointers(menu_item_history_map_.begin(), | |
| 333 menu_item_history_map_.end()); | |
| 334 menu_item_history_map_.clear(); | |
| 335 | |
| 187 g_object_unref(dummy_accel_group_); | 336 g_object_unref(dummy_accel_group_); |
| 188 } | 337 } |
| 189 | 338 |
| 190 void GlobalMenuBar::BuildGtkMenuFrom(int menu_str_id, | 339 GtkWidget* GlobalMenuBar::BuildGtkMenuFrom( |
| 191 std::map<int, GtkWidget*>* id_to_menu_item, | 340 int menu_str_id, |
| 192 GlobalMenuBarCommand* commands) { | 341 std::map<int, GtkWidget*>* id_to_menu_item, |
| 342 GlobalMenuBarCommand* commands) { | |
| 193 GtkWidget* menu = gtk_menu_new(); | 343 GtkWidget* menu = gtk_menu_new(); |
| 194 for (int i = 0; commands[i].str_id != MENU_END; ++i) { | 344 for (int i = 0; commands[i].str_id != MENU_END; ++i) { |
| 195 GtkWidget* menu_item = NULL; | 345 GtkWidget* menu_item = BuildMenuItem( |
| 196 if (commands[i].str_id == MENU_SEPARATOR) { | 346 commands[i].str_id, commands[i].command, commands[i].tag, |
| 197 menu_item = gtk_separator_menu_item_new(); | 347 id_to_menu_item, menu); |
| 348 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item); | |
| 349 } | |
| 350 | |
| 351 gtk_widget_show(menu); | |
| 352 | |
| 353 GtkWidget* menu_item = gtk_menu_item_new_with_mnemonic( | |
| 354 gfx::ConvertAcceleratorsFromWindowsStyle( | |
| 355 l10n_util::GetStringUTF8(menu_str_id)).c_str()); | |
| 356 gtk_menu_item_set_submenu(GTK_MENU_ITEM(menu_item), menu); | |
| 357 gtk_widget_show(menu_item); | |
| 358 gtk_menu_shell_append(GTK_MENU_SHELL(menu_bar_.get()), menu_item); | |
| 359 | |
| 360 return menu; | |
| 361 } | |
| 362 | |
| 363 GtkWidget* GlobalMenuBar::BuildMenuItem( | |
| 364 int string_id, | |
| 365 int command_id, | |
| 366 int tag_id, | |
| 367 std::map<int, GtkWidget*>* id_to_menu_item, | |
| 368 GtkWidget* menu_to_add_to) { | |
| 369 GtkWidget* menu_item = NULL; | |
| 370 if (string_id == MENU_SEPARATOR) { | |
| 371 menu_item = gtk_separator_menu_item_new(); | |
| 372 } else { | |
| 373 std::string label = | |
| 374 gfx::ConvertAcceleratorsFromWindowsStyle( | |
| 375 l10n_util::GetStringUTF8(string_id)); | |
| 376 | |
| 377 if (command_id == IDC_SHOW_BOOKMARK_BAR) | |
| 378 menu_item = gtk_check_menu_item_new_with_mnemonic(label.c_str()); | |
| 379 else | |
| 380 menu_item = gtk_menu_item_new_with_mnemonic(label.c_str()); | |
| 381 | |
| 382 if (tag_id) { | |
| 383 g_object_set_data(G_OBJECT(menu_item), "type-tag", | |
| 384 GINT_TO_POINTER(tag_id)); | |
| 385 } | |
| 386 | |
| 387 if (command_id == MENU_DISABLED_LABEL) { | |
| 388 gtk_widget_set_sensitive(menu_item, FALSE); | |
| 198 } else { | 389 } 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)); | 390 id_to_menu_item->insert(std::make_pair(command_id, menu_item)); |
| 210 g_object_set_data(G_OBJECT(menu_item), "command-id", | 391 g_object_set_data(G_OBJECT(menu_item), "command-id", |
| 211 GINT_TO_POINTER(command_id)); | 392 GINT_TO_POINTER(command_id)); |
| 212 g_signal_connect(menu_item, "activate", | 393 g_signal_connect(menu_item, "activate", |
| 213 G_CALLBACK(OnItemActivatedThunk), this); | 394 G_CALLBACK(OnItemActivatedThunk), this); |
| 214 } | 395 } |
| 215 gtk_widget_show(menu_item); | 396 } |
| 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); | 397 gtk_widget_show(menu_item); |
| 226 | 398 return menu_item; |
| 227 gtk_menu_shell_append(GTK_MENU_SHELL(menu_bar_), menu_item); | 399 } |
| 400 | |
| 401 GlobalMenuBar::HistoryItem* GlobalMenuBar::HistoryItemForMenuItem( | |
| 402 GtkWidget* menu_item) { | |
| 403 MenuItemToHistoryMap::iterator it = menu_item_history_map_.find(menu_item); | |
| 404 return it != menu_item_history_map_.end() ? it->second : NULL; | |
| 405 } | |
| 406 | |
| 407 bool GlobalMenuBar::HasValidHistoryItemForTab( | |
| 408 const TabRestoreService::Tab& entry) { | |
| 409 if (entry.navigations.empty()) | |
| 410 return false; | |
| 411 | |
| 412 const TabNavigation& current_navigation = | |
| 413 entry.navigations.at(entry.current_navigation_index); | |
| 414 if (current_navigation.virtual_url() == GURL(chrome::kChromeUINewTabURL)) | |
| 415 return false; | |
| 416 | |
| 417 return true; | |
| 418 } | |
| 419 | |
| 420 GlobalMenuBar::HistoryItem* GlobalMenuBar::HistoryItemForTab( | |
| 421 const TabRestoreService::Tab& entry) { | |
| 422 if (!HasValidHistoryItemForTab(entry)) | |
| 423 return NULL; | |
| 424 | |
| 425 const TabNavigation& current_navigation = | |
| 426 entry.navigations.at(entry.current_navigation_index); | |
| 427 HistoryItem* item = new HistoryItem(); | |
| 428 item->title = current_navigation.title(); | |
| 429 item->url = current_navigation.virtual_url(); | |
| 430 item->session_id = entry.id; | |
| 431 | |
| 432 // Tab navigations don't come with icons, so we always have to request them. | |
| 433 GetFaviconForHistoryItem(item); | |
| 434 | |
| 435 return item; | |
| 436 | |
| 437 } | |
| 438 | |
| 439 GtkWidget* GlobalMenuBar::AddHistoryItemToMenu(HistoryItem* item, | |
| 440 GtkWidget* menu, | |
| 441 int tag, | |
| 442 int index) { | |
| 443 string16 title = item->title; | |
| 444 std::string url_string = item->url.possibly_invalid_spec(); | |
| 445 | |
| 446 if (title.empty()) | |
| 447 title = UTF8ToUTF16(url_string); | |
| 448 ui::ElideString(title, kMaximumMenuWidthInChars, &title); | |
| 449 | |
| 450 GtkWidget* menu_item = gtk_image_menu_item_new_with_label( | |
| 451 UTF16ToUTF8(title).c_str()); | |
| 452 gtk_util::SetAlwaysShowImage(menu_item); | |
| 453 | |
| 454 item->menu_item = menu_item; | |
| 455 gtk_widget_show(menu_item); | |
| 456 g_object_set_data(G_OBJECT(menu_item), "type-tag", GINT_TO_POINTER(tag)); | |
| 457 g_signal_connect(menu_item, "activate", | |
| 458 G_CALLBACK(OnRecentlyClosedItemActivatedThunk), this); | |
| 459 if (item->icon_image.get()) { | |
| 460 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menu_item), | |
| 461 item->icon_image.get()); | |
| 462 } else if (!item->tabs.size()) { | |
| 463 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menu_item), | |
| 464 gtk_image_new_from_pixbuf(default_favicon_)); | |
| 465 } | |
| 466 | |
| 467 std::string tooltip = gtk_util::BuildTooltipTitleFor(item->title, item->url); | |
| 468 gtk_widget_set_tooltip_markup(menu_item, tooltip.c_str()); | |
| 469 | |
| 470 menu_item_history_map_.insert(std::make_pair(menu_item, item)); | |
| 471 gtk_menu_shell_insert(GTK_MENU_SHELL(menu), menu_item, index); | |
| 472 | |
| 473 return menu_item; | |
| 474 } | |
| 475 | |
| 476 void GlobalMenuBar::GetFaviconForHistoryItem(HistoryItem* item) { | |
| 477 FaviconService* service = | |
| 478 profile_->GetFaviconService(Profile::EXPLICIT_ACCESS); | |
| 479 FaviconService::Handle handle = service->GetFaviconForURL( | |
| 480 item->url, | |
| 481 history::FAVICON, | |
| 482 &favicon_consumer_, | |
| 483 NewCallback(this, &GlobalMenuBar::GotFaviconData)); | |
| 484 favicon_consumer_.SetClientData(service, handle, item); | |
| 485 item->icon_handle = handle; | |
| 486 item->icon_requested = true; | |
| 487 } | |
| 488 | |
| 489 void GlobalMenuBar::GotFaviconData(FaviconService::Handle handle, | |
| 490 history::FaviconData favicon) { | |
| 491 HistoryItem* item = | |
| 492 favicon_consumer_.GetClientData( | |
| 493 profile_->GetFaviconService(Profile::EXPLICIT_ACCESS), handle); | |
| 494 DCHECK(item); | |
| 495 item->icon_requested = false; | |
| 496 item->icon_handle = NULL; | |
| 497 | |
| 498 SkBitmap icon; | |
| 499 if (favicon.is_valid() && | |
| 500 gfx::PNGCodec::Decode(favicon.image_data->front(), | |
| 501 favicon.image_data->size(), &icon)) { | |
| 502 GdkPixbuf* pixbuf = gfx::GdkPixbufFromSkBitmap(&icon); | |
| 503 if (pixbuf) { | |
| 504 item->icon_image.Own(gtk_image_new_from_pixbuf(pixbuf)); | |
| 505 g_object_unref(pixbuf); | |
| 506 | |
| 507 if (item->menu_item) { | |
| 508 gtk_image_menu_item_set_image( | |
| 509 GTK_IMAGE_MENU_ITEM(item->menu_item), | |
| 510 item->icon_image.get()); | |
| 511 } | |
| 512 } | |
| 513 } | |
| 514 } | |
| 515 | |
| 516 void GlobalMenuBar::CancelFaviconRequest(HistoryItem* item) { | |
| 517 DCHECK(item); | |
| 518 if (item->icon_requested) { | |
| 519 FaviconService* service = | |
| 520 profile_->GetFaviconService(Profile::EXPLICIT_ACCESS); | |
| 521 service->CancelRequest(item->icon_handle); | |
| 522 item->icon_requested = false; | |
| 523 item->icon_handle = NULL; | |
| 524 } | |
| 525 } | |
| 526 | |
| 527 int GlobalMenuBar::GetIndexOfMenuItemWithTag(GtkWidget* menu, int tag_id) { | |
| 528 GetIndexClosure closure; | |
| 529 closure.found = false; | |
| 530 closure.current_index = 0; | |
| 531 closure.tag = tag_id; | |
| 532 | |
| 533 gtk_container_foreach( | |
| 534 GTK_CONTAINER(menu), | |
| 535 reinterpret_cast<void (*)(GtkWidget*, void*)>(GetIndexCallback), | |
| 536 &closure); | |
| 537 | |
| 538 return closure.current_index; | |
| 539 } | |
| 540 | |
| 541 void GlobalMenuBar::ClearMenuSection(GtkWidget* menu, int tag) { | |
| 542 ClearMenuClosure closure; | |
| 543 closure.container = menu; | |
| 544 closure.menu_bar = this; | |
| 545 closure.tag = tag; | |
| 546 | |
| 547 gtk_container_foreach( | |
| 548 GTK_CONTAINER(menu), | |
| 549 reinterpret_cast<void (*)(GtkWidget*, void*)>(ClearMenuCallback), | |
| 550 &closure); | |
| 551 } | |
| 552 | |
| 553 // static | |
| 554 void GlobalMenuBar::GetIndexCallback(GtkWidget* menu_item, | |
| 555 GetIndexClosure* closure) { | |
| 556 int tag = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(menu_item), "type-tag")); | |
| 557 if (tag == closure->tag) | |
| 558 closure->found = true; | |
| 559 | |
| 560 if (!closure->found) | |
| 561 closure->current_index++; | |
| 562 } | |
| 563 | |
| 564 // static | |
| 565 void GlobalMenuBar::ClearMenuCallback(GtkWidget* menu_item, | |
| 566 ClearMenuClosure* closure) { | |
| 567 DCHECK_NE(closure->tag, 0); | |
| 568 | |
| 569 int tag = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(menu_item), "type-tag")); | |
| 570 if (closure->tag == tag) { | |
| 571 HistoryItem* item = closure->menu_bar->HistoryItemForMenuItem(menu_item); | |
| 572 | |
| 573 if (item) { | |
| 574 closure->menu_bar->CancelFaviconRequest(item); | |
| 575 closure->menu_bar->menu_item_history_map_.erase(menu_item); | |
| 576 delete item; | |
| 577 } | |
| 578 | |
| 579 GtkWidget* submenu = gtk_menu_item_get_submenu(GTK_MENU_ITEM(menu_item)); | |
| 580 if (submenu) | |
| 581 closure->menu_bar->ClearMenuSection(submenu, closure->tag); | |
| 582 | |
| 583 gtk_container_remove(GTK_CONTAINER(closure->container), menu_item); | |
| 584 } | |
| 228 } | 585 } |
| 229 | 586 |
| 230 void GlobalMenuBar::EnabledStateChangedForCommand(int id, bool enabled) { | 587 void GlobalMenuBar::EnabledStateChangedForCommand(int id, bool enabled) { |
| 231 IDMenuItemMap::iterator it = id_to_menu_item_.find(id); | 588 CommandIDMenuItemMap::iterator it = id_to_menu_item_.find(id); |
| 232 if (it != id_to_menu_item_.end()) | 589 if (it != id_to_menu_item_.end()) |
| 233 gtk_widget_set_sensitive(it->second, enabled); | 590 gtk_widget_set_sensitive(it->second, enabled); |
| 234 } | 591 } |
| 235 | 592 |
| 236 void GlobalMenuBar::Observe(NotificationType type, | 593 void GlobalMenuBar::Observe(NotificationType type, |
| 237 const NotificationSource& source, | 594 const NotificationSource& source, |
| 238 const NotificationDetails& details) { | 595 const NotificationDetails& details) { |
| 239 DCHECK(type.value == NotificationType::BOOKMARK_BAR_VISIBILITY_PREF_CHANGED); | 596 if (type.value == NotificationType::BOOKMARK_BAR_VISIBILITY_PREF_CHANGED) { |
| 597 CommandIDMenuItemMap::iterator it = | |
| 598 id_to_menu_item_.find(IDC_SHOW_BOOKMARK_BAR); | |
| 599 if (it != id_to_menu_item_.end()) { | |
| 600 PrefService* prefs = browser_->profile()->GetPrefs(); | |
| 240 | 601 |
| 241 IDMenuItemMap::iterator it = id_to_menu_item_.find(IDC_SHOW_BOOKMARK_BAR); | 602 block_activation_ = true; |
| 242 if (it != id_to_menu_item_.end()) { | 603 gtk_check_menu_item_set_active( |
| 243 PrefService* prefs = browser_->profile()->GetPrefs(); | 604 GTK_CHECK_MENU_ITEM(it->second), |
| 605 prefs->GetBoolean(prefs::kShowBookmarkBar)); | |
| 606 block_activation_ = false; | |
| 607 } | |
| 608 } else if (type.value == NotificationType::BROWSER_THEME_CHANGED) { | |
| 609 // Keeping track of which menu items have the default icon is going an | |
| 610 // error-prone pain, so instead just store the new default favicon and | |
| 611 // we'll update on the next menu change event. | |
| 612 default_favicon_ = GtkThemeService::GetDefaultFavicon(true); | |
| 613 } else { | |
| 614 NOTREACHED(); | |
| 615 } | |
| 616 } | |
| 244 | 617 |
| 245 block_activation_ = true; | 618 void GlobalMenuBar::TabRestoreServiceChanged(TabRestoreService* service) { |
| 246 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(it->second), | 619 const TabRestoreService::Entries& entries = service->entries(); |
| 247 prefs->GetBoolean(prefs::kShowBookmarkBar)); | 620 |
| 248 block_activation_ = false; | 621 ClearMenuSection(history_menu_, TAG_RECENTLY_CLOSED); |
| 622 | |
| 623 // We'll get the index the "Recently Closed" header. (This can vary depending | |
| 624 // on the number of "Most Visited" items. | |
| 625 int index = GetIndexOfMenuItemWithTag(history_menu_, | |
| 626 TAG_RECENTLY_CLOSED_HEADER) + 1; | |
| 627 | |
| 628 unsigned int added_count = 0; | |
| 629 for (TabRestoreService::Entries::const_iterator it = entries.begin(); | |
| 630 it != entries.end() && added_count < kRecentlyClosedCount; ++it) { | |
| 631 TabRestoreService::Entry* entry = *it; | |
| 632 | |
| 633 if (entry->type == TabRestoreService::WINDOW) { | |
| 634 TabRestoreService::Window* entry_win = | |
| 635 static_cast<TabRestoreService::Window*>(entry); | |
| 636 std::vector<TabRestoreService::Tab>& tabs = entry_win->tabs; | |
| 637 if (tabs.empty()) | |
| 638 continue; | |
| 639 | |
| 640 // Check that this window has valid content. Sometimes it is possible for | |
| 641 // there to not be any subitems for a given window; if that is the case, | |
| 642 // do not add the entry to the main menu. | |
| 643 int valid_tab_count = 0; | |
| 644 std::vector<TabRestoreService::Tab>::const_iterator it; | |
| 645 for (it = tabs.begin(); it != tabs.end(); ++it) { | |
| 646 if (HasValidHistoryItemForTab(*it)) | |
| 647 valid_tab_count++; | |
| 648 } | |
| 649 if (valid_tab_count == 0) | |
| 650 continue; | |
| 651 | |
| 652 // Create the item for the parent/window. Do not set the title yet | |
| 653 // because the actual number of items that are in the menu will not be | |
| 654 // known until things like the NTP are filtered out, which is done when | |
| 655 // the tab items are actually created. | |
| 656 HistoryItem* item = new HistoryItem(); | |
| 657 item->session_id = entry_win->id; | |
| 658 | |
| 659 GtkWidget* submenu = gtk_menu_new(); | |
| 660 | |
| 661 GtkWidget* restore_item = gtk_menu_item_new_with_label( | |
| 662 l10n_util::GetStringUTF8( | |
| 663 IDS_HISTORY_CLOSED_RESTORE_WINDOW_LINUX).c_str()); | |
| 664 g_object_set_data(G_OBJECT(restore_item), "type-tag", | |
| 665 GINT_TO_POINTER(TAG_RECENTLY_CLOSED)); | |
| 666 g_signal_connect(restore_item, "activate", | |
| 667 G_CALLBACK(OnRecentlyClosedItemActivatedThunk), this); | |
| 668 gtk_widget_show(restore_item); | |
| 669 | |
| 670 // The mac version of this code allows the user to click on the parent | |
| 671 // menu item to have the same effect as clicking the restore window | |
| 672 // submenu item. GTK+ helpfully activates a menu item when it shows a | |
| 673 // submenu so toss that feature out. | |
| 674 menu_item_history_map_.insert(std::make_pair(restore_item, item)); | |
| 675 gtk_menu_shell_append(GTK_MENU_SHELL(submenu), restore_item); | |
| 676 | |
| 677 GtkWidget* separator = gtk_separator_menu_item_new(); | |
| 678 gtk_widget_show(separator); | |
| 679 gtk_menu_shell_append(GTK_MENU_SHELL(submenu), separator); | |
| 680 | |
| 681 // Loop over the window's tabs and add them to the submenu. | |
| 682 int subindex = 2; | |
| 683 for (it = tabs.begin(); it != tabs.end(); ++it) { | |
| 684 TabRestoreService::Tab tab = *it; | |
| 685 HistoryItem* tab_item = HistoryItemForTab(tab); | |
| 686 if (tab_item) { | |
| 687 item->tabs.push_back(tab_item); | |
| 688 AddHistoryItemToMenu(tab_item, submenu, TAG_RECENTLY_CLOSED, | |
| 689 subindex++); | |
| 690 } | |
| 691 } | |
| 692 | |
| 693 // Now that the number of tabs that has been added is known, set the | |
| 694 // title of the parent menu item. | |
| 695 std::string title = | |
| 696 (item->tabs.size() == 1) ? | |
| 697 l10n_util::GetStringUTF8( | |
| 698 IDS_NEW_TAB_RECENTLY_CLOSED_WINDOW_SINGLE) : | |
| 699 l10n_util::GetStringFUTF8( | |
| 700 IDS_NEW_TAB_RECENTLY_CLOSED_WINDOW_MULTIPLE, | |
| 701 base::IntToString16(item->tabs.size())); | |
| 702 | |
| 703 // Create the menu item parent. Unlike mac, it's can't be activated. | |
| 704 GtkWidget* parent_item = gtk_image_menu_item_new_with_label( | |
| 705 title.c_str()); | |
| 706 gtk_widget_show(parent_item); | |
| 707 g_object_set_data(G_OBJECT(parent_item), "type-tag", | |
| 708 GINT_TO_POINTER(TAG_RECENTLY_CLOSED)); | |
| 709 gtk_menu_item_set_submenu(GTK_MENU_ITEM(parent_item), submenu); | |
| 710 | |
| 711 gtk_menu_shell_insert(GTK_MENU_SHELL(history_menu_), parent_item, | |
| 712 index++); | |
| 713 ++added_count; | |
| 714 } else if (entry->type == TabRestoreService::TAB) { | |
| 715 TabRestoreService::Tab* tab = | |
| 716 static_cast<TabRestoreService::Tab*>(entry); | |
| 717 HistoryItem* item = HistoryItemForTab(*tab); | |
| 718 if (item) { | |
| 719 AddHistoryItemToMenu(item, history_menu_, TAG_RECENTLY_CLOSED, index++); | |
| 720 ++added_count; | |
| 721 } | |
| 722 } | |
| 249 } | 723 } |
| 250 } | 724 } |
| 251 | 725 |
| 726 void GlobalMenuBar::TabRestoreServiceDestroyed( | |
| 727 TabRestoreService* service) { | |
| 728 // Intentionally left blank. We hold a weak reference to the service. | |
|
Evan Stade
2011/04/18 18:11:19
I think observers usually do hold weak refs. The i
| |
| 729 } | |
| 730 | |
| 252 void GlobalMenuBar::OnItemActivated(GtkWidget* sender) { | 731 void GlobalMenuBar::OnItemActivated(GtkWidget* sender) { |
| 253 if (block_activation_) | 732 if (block_activation_) |
| 254 return; | 733 return; |
| 255 | 734 |
| 256 int id = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(sender), "command-id")); | 735 int id = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(sender), "command-id")); |
| 257 browser_->ExecuteCommandIfEnabled(id); | 736 browser_->ExecuteCommandIfEnabled(id); |
| 258 } | 737 } |
| 738 | |
| 739 void GlobalMenuBar::OnRecentlyClosedItemActivated(GtkWidget* sender) { | |
| 740 WindowOpenDisposition disposition = | |
| 741 gtk_util::DispositionForCurrentButtonPressEvent(); | |
| 742 HistoryItem* item = HistoryItemForMenuItem(sender); | |
| 743 | |
| 744 // If this item can be restored using TabRestoreService, do so. Otherwise, | |
| 745 // just load the URL. | |
| 746 TabRestoreService* service = browser_->profile()->GetTabRestoreService(); | |
| 747 if (item->session_id && service) { | |
| 748 service->RestoreEntryById(browser_->tab_restore_service_delegate(), | |
| 749 item->session_id, false); | |
| 750 } else { | |
| 751 DCHECK(item->url.is_valid()); | |
| 752 browser_->OpenURL(item->url, GURL(), disposition, | |
| 753 PageTransition::AUTO_BOOKMARK); | |
| 754 } | |
| 755 } | |
| OLD | NEW |