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

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

Issue 231733005: Delete the GTK+ port of Chrome. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Remerge to ToT Created 6 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
(Empty)
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "chrome/browser/ui/gtk/global_history_menu.h"
6
7 #include <gtk/gtk.h>
8
9 #include "base/bind.h"
10 #include "base/bind_helpers.h"
11 #include "base/memory/weak_ptr.h"
12 #include "base/stl_util.h"
13 #include "base/strings/string_number_conversions.h"
14 #include "base/strings/utf_string_conversions.h"
15 #include "chrome/browser/chrome_notification_types.h"
16 #include "chrome/browser/history/top_sites.h"
17 #include "chrome/browser/profiles/profile.h"
18 #include "chrome/browser/sessions/tab_restore_service.h"
19 #include "chrome/browser/sessions/tab_restore_service_factory.h"
20 #include "chrome/browser/themes/theme_service_factory.h"
21 #include "chrome/browser/ui/browser.h"
22 #include "chrome/browser/ui/browser_tab_restore_service_delegate.h"
23 #include "chrome/browser/ui/gtk/event_utils.h"
24 #include "chrome/browser/ui/gtk/global_menu_bar.h"
25 #include "chrome/browser/ui/gtk/gtk_theme_service.h"
26 #include "chrome/browser/ui/gtk/gtk_util.h"
27 #include "chrome/common/url_constants.h"
28 #include "content/public/browser/notification_source.h"
29 #include "grit/generated_resources.h"
30 #include "ui/base/gtk/owned_widget_gtk.h"
31 #include "ui/base/l10n/l10n_util.h"
32 #include "ui/gfx/codec/png_codec.h"
33 #include "ui/gfx/gtk_util.h"
34 #include "ui/gfx/text_elider.h"
35
36 using content::OpenURLParams;
37
38 namespace {
39
40 // The maximum number of most visited items to display.
41 const unsigned int kMostVisitedCount = 8;
42
43 // The number of recently closed items to get.
44 const unsigned int kRecentlyClosedCount = 8;
45
46 // Menus more than this many chars long will get trimmed.
47 const int kMaximumMenuWidthInChars = 50;
48
49 } // namespace
50
51 struct GlobalHistoryMenu::ClearMenuClosure {
52 GtkWidget* container;
53 GlobalHistoryMenu* menu_bar;
54 int tag;
55 };
56
57 struct GlobalHistoryMenu::GetIndexClosure {
58 bool found;
59 int current_index;
60 int tag;
61 };
62
63 class GlobalHistoryMenu::HistoryItem {
64 public:
65 HistoryItem()
66 : menu_item(NULL),
67 session_id(0) {}
68
69 // The title for the menu item.
70 base::string16 title;
71 // The URL that will be navigated to if the user selects this item.
72 GURL url;
73
74 // A pointer to the menu_item. This is a weak reference in the GTK+ version
75 // because the GtkMenu must sink the reference.
76 GtkWidget* menu_item;
77
78 // This ID is unique for a browser session and can be passed to the
79 // TabRestoreService to re-open the closed window or tab that this
80 // references. A non-0 session ID indicates that this is an entry can be
81 // restored that way. Otherwise, the URL will be used to open the item and
82 // this ID will be 0.
83 SessionID::id_type session_id;
84
85 // If the HistoryItem is a window, this will be the vector of tabs. Note
86 // that this is a list of weak references. The |menu_item_map_| is the owner
87 // of all items. If it is not a window, then the entry is a single page and
88 // the vector will be empty.
89 std::vector<HistoryItem*> tabs;
90
91 private:
92 DISALLOW_COPY_AND_ASSIGN(HistoryItem);
93 };
94
95 GlobalHistoryMenu::GlobalHistoryMenu(Browser* browser)
96 : browser_(browser),
97 profile_(browser_->profile()),
98 history_menu_(NULL),
99 top_sites_(NULL),
100 tab_restore_service_(NULL),
101 weak_ptr_factory_(this) {
102 }
103
104 GlobalHistoryMenu::~GlobalHistoryMenu() {
105 if (tab_restore_service_)
106 tab_restore_service_->RemoveObserver(this);
107
108 STLDeleteContainerPairSecondPointers(menu_item_history_map_.begin(),
109 menu_item_history_map_.end());
110 menu_item_history_map_.clear();
111
112 if (history_menu_) {
113 gtk_widget_destroy(history_menu_);
114 g_object_unref(history_menu_);
115 }
116 }
117
118 void GlobalHistoryMenu::Init(GtkWidget* history_menu,
119 GtkWidget* history_menu_item) {
120 history_menu_ = history_menu;
121 g_object_ref_sink(history_menu_);
122
123 // We have to connect to |history_menu_item|'s "activate" signal instead of
124 // |history_menu|'s "show" signal because we are not supposed to modify the
125 // menu during "show"
126 g_signal_connect(history_menu_item, "activate",
127 G_CALLBACK(OnMenuActivateThunk), this);
128
129 if (profile_) {
130 top_sites_ = profile_->GetTopSites();
131 if (top_sites_) {
132 GetTopSitesData();
133
134 // Register for notification when TopSites changes so that we can update
135 // ourself.
136 registrar_.Add(this, chrome::NOTIFICATION_TOP_SITES_CHANGED,
137 content::Source<history::TopSites>(top_sites_));
138 }
139 }
140 }
141
142 void GlobalHistoryMenu::GetTopSitesData() {
143 DCHECK(top_sites_);
144
145 top_sites_->GetMostVisitedURLs(
146 base::Bind(&GlobalHistoryMenu::OnTopSitesReceived,
147 weak_ptr_factory_.GetWeakPtr()), false);
148 }
149
150 void GlobalHistoryMenu::OnTopSitesReceived(
151 const history::MostVisitedURLList& visited_list) {
152 ClearMenuSection(history_menu_, GlobalMenuBar::TAG_MOST_VISITED);
153
154 int index = GetIndexOfMenuItemWithTag(
155 history_menu_,
156 GlobalMenuBar::TAG_MOST_VISITED_HEADER) + 1;
157
158 for (size_t i = 0; i < visited_list.size() && i < kMostVisitedCount; ++i) {
159 const history::MostVisitedURL& visited = visited_list[i];
160 if (visited.url.spec().empty())
161 break; // This is the signal that there are no more real visited sites.
162
163 HistoryItem* item = new HistoryItem();
164 item->title = visited.title;
165 item->url = visited.url;
166
167 AddHistoryItemToMenu(item,
168 history_menu_,
169 GlobalMenuBar::TAG_MOST_VISITED,
170 index++);
171 }
172 }
173
174 GlobalHistoryMenu::HistoryItem* GlobalHistoryMenu::HistoryItemForMenuItem(
175 GtkWidget* menu_item) {
176 MenuItemToHistoryMap::iterator it = menu_item_history_map_.find(menu_item);
177 return it != menu_item_history_map_.end() ? it->second : NULL;
178 }
179
180 GlobalHistoryMenu::HistoryItem* GlobalHistoryMenu::HistoryItemForTab(
181 const TabRestoreService::Tab& entry) {
182 const sessions::SerializedNavigationEntry& current_navigation =
183 entry.navigations.at(entry.current_navigation_index);
184 HistoryItem* item = new HistoryItem();
185 item->title = current_navigation.title();
186 item->url = current_navigation.virtual_url();
187 item->session_id = entry.id;
188
189 return item;
190 }
191
192 GtkWidget* GlobalHistoryMenu::AddHistoryItemToMenu(HistoryItem* item,
193 GtkWidget* menu,
194 int tag,
195 int index) {
196 base::string16 title = item->title;
197 std::string url_string = item->url.possibly_invalid_spec();
198
199 if (title.empty())
200 title = base::UTF8ToUTF16(url_string);
201 gfx::ElideString(title, kMaximumMenuWidthInChars, &title);
202
203 GtkWidget* menu_item = gtk_menu_item_new_with_label(
204 base::UTF16ToUTF8(title).c_str());
205
206 item->menu_item = menu_item;
207 gtk_widget_show(menu_item);
208 g_object_set_data(G_OBJECT(menu_item), "type-tag", GINT_TO_POINTER(tag));
209 g_signal_connect(menu_item, "activate",
210 G_CALLBACK(OnRecentlyClosedItemActivatedThunk), this);
211
212 std::string tooltip = gtk_util::BuildTooltipTitleFor(item->title, item->url);
213 gtk_widget_set_tooltip_markup(menu_item, tooltip.c_str());
214
215 menu_item_history_map_.insert(std::make_pair(menu_item, item));
216 gtk_menu_shell_insert(GTK_MENU_SHELL(menu), menu_item, index);
217
218 return menu_item;
219 }
220
221 int GlobalHistoryMenu::GetIndexOfMenuItemWithTag(GtkWidget* menu, int tag_id) {
222 GetIndexClosure closure;
223 closure.found = false;
224 closure.current_index = 0;
225 closure.tag = tag_id;
226
227 gtk_container_foreach(
228 GTK_CONTAINER(menu),
229 reinterpret_cast<void (*)(GtkWidget*, void*)>(GetIndexCallback),
230 &closure);
231
232 return closure.current_index;
233 }
234
235 void GlobalHistoryMenu::ClearMenuSection(GtkWidget* menu, int tag) {
236 ClearMenuClosure closure;
237 closure.container = menu;
238 closure.menu_bar = this;
239 closure.tag = tag;
240
241 gtk_container_foreach(
242 GTK_CONTAINER(menu),
243 reinterpret_cast<void (*)(GtkWidget*, void*)>(ClearMenuCallback),
244 &closure);
245 }
246
247 // static
248 void GlobalHistoryMenu::GetIndexCallback(GtkWidget* menu_item,
249 GetIndexClosure* closure) {
250 int tag = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(menu_item), "type-tag"));
251 if (tag == closure->tag)
252 closure->found = true;
253
254 if (!closure->found)
255 closure->current_index++;
256 }
257
258 // static
259 void GlobalHistoryMenu::ClearMenuCallback(GtkWidget* menu_item,
260 ClearMenuClosure* closure) {
261 DCHECK_NE(closure->tag, 0);
262
263 int tag = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(menu_item), "type-tag"));
264 if (closure->tag == tag) {
265 HistoryItem* item = closure->menu_bar->HistoryItemForMenuItem(menu_item);
266
267 if (item) {
268 closure->menu_bar->menu_item_history_map_.erase(menu_item);
269 delete item;
270 }
271
272 GtkWidget* submenu = gtk_menu_item_get_submenu(GTK_MENU_ITEM(menu_item));
273 if (submenu)
274 closure->menu_bar->ClearMenuSection(submenu, closure->tag);
275
276 gtk_container_remove(GTK_CONTAINER(closure->container), menu_item);
277 }
278 }
279
280 void GlobalHistoryMenu::Observe(int type,
281 const content::NotificationSource& source,
282 const content::NotificationDetails& details) {
283 if (type == chrome::NOTIFICATION_TOP_SITES_CHANGED) {
284 GetTopSitesData();
285 } else {
286 NOTREACHED();
287 }
288 }
289
290 void GlobalHistoryMenu::TabRestoreServiceChanged(TabRestoreService* service) {
291 const TabRestoreService::Entries& entries = service->entries();
292
293 ClearMenuSection(history_menu_, GlobalMenuBar::TAG_RECENTLY_CLOSED);
294
295 // We'll get the index the "Recently Closed" header. (This can vary depending
296 // on the number of "Most Visited" items.
297 int index = GetIndexOfMenuItemWithTag(
298 history_menu_,
299 GlobalMenuBar::TAG_RECENTLY_CLOSED_HEADER) + 1;
300
301 unsigned int added_count = 0;
302 for (TabRestoreService::Entries::const_iterator it = entries.begin();
303 it != entries.end() && added_count < kRecentlyClosedCount; ++it) {
304 TabRestoreService::Entry* entry = *it;
305
306 if (entry->type == TabRestoreService::WINDOW) {
307 TabRestoreService::Window* entry_win =
308 static_cast<TabRestoreService::Window*>(entry);
309 std::vector<TabRestoreService::Tab>& tabs = entry_win->tabs;
310 if (tabs.empty())
311 continue;
312
313 // Create the item for the parent/window.
314 HistoryItem* item = new HistoryItem();
315 item->session_id = entry_win->id;
316
317 GtkWidget* submenu = gtk_menu_new();
318 GtkWidget* restore_item = gtk_menu_item_new_with_label(
319 l10n_util::GetStringUTF8(
320 IDS_HISTORY_CLOSED_RESTORE_WINDOW_LINUX).c_str());
321 g_object_set_data(G_OBJECT(restore_item), "type-tag",
322 GINT_TO_POINTER(GlobalMenuBar::TAG_RECENTLY_CLOSED));
323 g_signal_connect(restore_item, "activate",
324 G_CALLBACK(OnRecentlyClosedItemActivatedThunk), this);
325 gtk_widget_show(restore_item);
326
327 // The mac version of this code allows the user to click on the parent
328 // menu item to have the same effect as clicking the restore window
329 // submenu item. GTK+ helpfully activates a menu item when it shows a
330 // submenu so toss that feature out.
331 menu_item_history_map_.insert(std::make_pair(restore_item, item));
332 gtk_menu_shell_append(GTK_MENU_SHELL(submenu), restore_item);
333
334 GtkWidget* separator = gtk_separator_menu_item_new();
335 gtk_widget_show(separator);
336 gtk_menu_shell_append(GTK_MENU_SHELL(submenu), separator);
337
338 // Loop over the window's tabs and add them to the submenu.
339 int subindex = 2;
340 std::vector<TabRestoreService::Tab>::const_iterator iter;
341 for (iter = tabs.begin(); iter != tabs.end(); ++iter) {
342 TabRestoreService::Tab tab = *iter;
343 HistoryItem* tab_item = HistoryItemForTab(tab);
344 item->tabs.push_back(tab_item);
345 AddHistoryItemToMenu(tab_item,
346 submenu,
347 GlobalMenuBar::TAG_RECENTLY_CLOSED,
348 subindex++);
349 }
350
351 std::string title = item->tabs.size() == 1 ?
352 l10n_util::GetStringUTF8(
353 IDS_NEW_TAB_RECENTLY_CLOSED_WINDOW_SINGLE) :
354 l10n_util::GetStringFUTF8(
355 IDS_NEW_TAB_RECENTLY_CLOSED_WINDOW_MULTIPLE,
356 base::IntToString16(item->tabs.size()));
357
358 // Create the menu item parent. Unlike mac, it's can't be activated.
359 GtkWidget* parent_item = gtk_menu_item_new_with_label(title.c_str());
360 gtk_widget_show(parent_item);
361 g_object_set_data(G_OBJECT(parent_item), "type-tag",
362 GINT_TO_POINTER(GlobalMenuBar::TAG_RECENTLY_CLOSED));
363 gtk_menu_item_set_submenu(GTK_MENU_ITEM(parent_item), submenu);
364
365 gtk_menu_shell_insert(GTK_MENU_SHELL(history_menu_), parent_item,
366 index++);
367 ++added_count;
368 } else if (entry->type == TabRestoreService::TAB) {
369 TabRestoreService::Tab* tab = static_cast<TabRestoreService::Tab*>(entry);
370 HistoryItem* item = HistoryItemForTab(*tab);
371 AddHistoryItemToMenu(item,
372 history_menu_,
373 GlobalMenuBar::TAG_RECENTLY_CLOSED,
374 index++);
375 ++added_count;
376 }
377 }
378 }
379
380 void GlobalHistoryMenu::TabRestoreServiceDestroyed(
381 TabRestoreService* service) {
382 tab_restore_service_ = NULL;
383 }
384
385 void GlobalHistoryMenu::OnRecentlyClosedItemActivated(GtkWidget* sender) {
386 WindowOpenDisposition disposition =
387 event_utils::DispositionForCurrentButtonPressEvent();
388 HistoryItem* item = HistoryItemForMenuItem(sender);
389
390 // If this item can be restored using TabRestoreService, do so. Otherwise,
391 // just load the URL.
392 TabRestoreService* service =
393 TabRestoreServiceFactory::GetForProfile(browser_->profile());
394 if (item->session_id && service) {
395 service->RestoreEntryById(browser_->tab_restore_service_delegate(),
396 item->session_id, browser_->host_desktop_type(),
397 UNKNOWN);
398 } else {
399 DCHECK(item->url.is_valid());
400 browser_->OpenURL(OpenURLParams(item->url, content::Referrer(), disposition,
401 content::PAGE_TRANSITION_AUTO_BOOKMARK, false));
402 }
403 }
404
405 void GlobalHistoryMenu::OnMenuActivate(GtkWidget* sender) {
406 if (!tab_restore_service_) {
407 tab_restore_service_ = TabRestoreServiceFactory::GetForProfile(profile_);
408 if (tab_restore_service_) {
409 tab_restore_service_->LoadTabsFromLastSession();
410 tab_restore_service_->AddObserver(this);
411
412 // If LoadTabsFromLastSession doesn't load tabs, it won't call
413 // TabRestoreServiceChanged(). This ensures that all new windows after
414 // the first one will have their menus populated correctly.
415 TabRestoreServiceChanged(tab_restore_service_);
416 }
417 }
418 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698