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 | |
|
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 } | |
| OLD | NEW |