Componentize AccountReconcilor.
[chromium-blink-merge.git] / chrome / browser / custom_home_pages_table_model.cc
blob95b25af4409ce3f2d38b600b348fc0ea79e7c05c
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.
5 #include "chrome/browser/custom_home_pages_table_model.h"
7 #include "base/bind.h"
8 #include "base/bind_helpers.h"
9 #include "base/i18n/rtl.h"
10 #include "base/prefs/pref_service.h"
11 #include "base/strings/utf_string_conversions.h"
12 #include "chrome/browser/history/history_service_factory.h"
13 #include "chrome/browser/profiles/profile.h"
14 #include "chrome/browser/ui/browser.h"
15 #include "chrome/browser/ui/browser_iterator.h"
16 #include "chrome/browser/ui/browser_list.h"
17 #include "chrome/browser/ui/tabs/tab_strip_model.h"
18 #include "chrome/common/pref_names.h"
19 #include "chrome/common/url_constants.h"
20 #include "content/public/browser/web_contents.h"
21 #include "grit/generated_resources.h"
22 #include "grit/ui_resources.h"
23 #include "net/base/net_util.h"
24 #include "ui/base/l10n/l10n_util.h"
25 #include "ui/base/models/table_model_observer.h"
26 #include "ui/gfx/codec/png_codec.h"
27 #include "url/gurl.h"
29 namespace {
31 // Checks whether the given URL should count as one of the "current" pages.
32 // Returns true for all pages except dev tools and settings.
33 bool ShouldAddPage(const GURL& url) {
34 if (url.is_empty())
35 return false;
37 if (url.SchemeIs(content::kChromeDevToolsScheme))
38 return false;
40 if (url.SchemeIs(content::kChromeUIScheme)) {
41 if (url.host() == chrome::kChromeUISettingsHost)
42 return false;
44 // For a settings page, the path will start with "/settings" not "settings"
45 // so find() will return 1, not 0.
46 if (url.host() == chrome::kChromeUIUberHost &&
47 url.path().find(chrome::kChromeUISettingsHost) == 1) {
48 return false;
52 return true;
55 } // namespace
57 struct CustomHomePagesTableModel::Entry {
58 Entry() : title_handle(0) {}
60 // URL of the page.
61 GURL url;
63 // Page title. If this is empty, we'll display the URL as the entry.
64 base::string16 title;
66 // If non-zero, indicates we're loading the title for the page.
67 HistoryService::Handle title_handle;
70 CustomHomePagesTableModel::CustomHomePagesTableModel(Profile* profile)
71 : profile_(profile),
72 observer_(NULL) {
75 CustomHomePagesTableModel::~CustomHomePagesTableModel() {
78 void CustomHomePagesTableModel::SetURLs(const std::vector<GURL>& urls) {
79 entries_.resize(urls.size());
80 for (size_t i = 0; i < urls.size(); ++i) {
81 entries_[i].url = urls[i];
82 entries_[i].title.erase();
83 LoadTitle(&(entries_[i]));
85 // Complete change, so tell the view to just rebuild itself.
86 if (observer_)
87 observer_->OnModelChanged();
90 /**
91 * Move a number of existing entries to a new position, reordering the table.
93 * We determine the range of elements affected by the move, save the moved
94 * elements, compact the remaining ones, and re-insert moved elements.
95 * Expects |index_list| to be ordered ascending.
97 void CustomHomePagesTableModel::MoveURLs(int insert_before,
98 const std::vector<int>& index_list) {
99 if (index_list.empty()) return;
100 DCHECK(insert_before >= 0 && insert_before <= RowCount());
102 // The range of elements that needs to be reshuffled is [ |first|, |last| ).
103 int first = std::min(insert_before, index_list.front());
104 int last = std::max(insert_before, index_list.back() + 1);
106 // Save the dragged elements. Also, adjust insertion point if it is before a
107 // dragged element.
108 std::vector<Entry> moved_entries;
109 for (size_t i = 0; i < index_list.size(); ++i) {
110 moved_entries.push_back(entries_[index_list[i]]);
111 if (index_list[i] == insert_before)
112 insert_before++;
115 // Compact the range between beginning and insertion point, moving downwards.
116 size_t skip_count = 0;
117 for (int i = first; i < insert_before; ++i) {
118 if (skip_count < index_list.size() && index_list[skip_count] == i)
119 skip_count++;
120 else
121 entries_[i - skip_count] = entries_[i];
124 // Moving items down created a gap. We start compacting up after it.
125 first = insert_before;
126 insert_before -= skip_count;
128 // Now compact up for elements after the insertion point.
129 skip_count = 0;
130 for (int i = last - 1; i >= first; --i) {
131 if (skip_count < index_list.size() &&
132 index_list[index_list.size() - skip_count - 1] == i) {
133 skip_count++;
134 } else {
135 entries_[i + skip_count] = entries_[i];
139 // Insert moved elements.
140 std::copy(moved_entries.begin(), moved_entries.end(),
141 entries_.begin() + insert_before);
143 // Possibly large change, so tell the view to just rebuild itself.
144 if (observer_)
145 observer_->OnModelChanged();
148 void CustomHomePagesTableModel::Add(int index, const GURL& url) {
149 DCHECK(index >= 0 && index <= RowCount());
150 entries_.insert(entries_.begin() + static_cast<size_t>(index), Entry());
151 entries_[index].url = url;
152 LoadTitle(&(entries_[index]));
153 if (observer_)
154 observer_->OnItemsAdded(index, 1);
157 void CustomHomePagesTableModel::Remove(int index) {
158 DCHECK(index >= 0 && index < RowCount());
159 Entry* entry = &(entries_[index]);
160 // Cancel any pending load requests now so we don't deref a bogus pointer when
161 // we get the loaded notification.
162 if (entry->title_handle) {
163 HistoryService* history_service = HistoryServiceFactory::GetForProfile(
164 profile_, Profile::EXPLICIT_ACCESS);
165 if (history_service)
166 history_service->CancelRequest(entry->title_handle);
168 entries_.erase(entries_.begin() + static_cast<size_t>(index));
169 if (observer_)
170 observer_->OnItemsRemoved(index, 1);
173 void CustomHomePagesTableModel::SetToCurrentlyOpenPages() {
174 // Remove the current entries.
175 while (RowCount())
176 Remove(0);
178 // And add all tabs for all open browsers with our profile.
179 int add_index = 0;
180 for (chrome::BrowserIterator it; !it.done(); it.Next()) {
181 Browser* browser = *it;
182 if (browser->profile() != profile_)
183 continue; // Skip incognito browsers.
185 for (int tab_index = 0;
186 tab_index < browser->tab_strip_model()->count();
187 ++tab_index) {
188 const GURL url =
189 browser->tab_strip_model()->GetWebContentsAt(tab_index)->GetURL();
190 if (ShouldAddPage(url))
191 Add(add_index++, url);
196 std::vector<GURL> CustomHomePagesTableModel::GetURLs() {
197 std::vector<GURL> urls(entries_.size());
198 for (size_t i = 0; i < entries_.size(); ++i)
199 urls[i] = entries_[i].url;
200 return urls;
203 int CustomHomePagesTableModel::RowCount() {
204 return static_cast<int>(entries_.size());
207 base::string16 CustomHomePagesTableModel::GetText(int row, int column_id) {
208 DCHECK(column_id == 0);
209 DCHECK(row >= 0 && row < RowCount());
210 return entries_[row].title.empty() ? FormattedURL(row) : entries_[row].title;
213 base::string16 CustomHomePagesTableModel::GetTooltip(int row) {
214 return entries_[row].title.empty() ? base::string16() :
215 l10n_util::GetStringFUTF16(IDS_OPTIONS_STARTUP_PAGE_TOOLTIP,
216 entries_[row].title, FormattedURL(row));
219 void CustomHomePagesTableModel::SetObserver(ui::TableModelObserver* observer) {
220 observer_ = observer;
223 void CustomHomePagesTableModel::LoadTitle(Entry* entry) {
224 HistoryService* history_service = HistoryServiceFactory::GetForProfile(
225 profile_, Profile::EXPLICIT_ACCESS);
226 if (history_service) {
227 entry->title_handle = history_service->QueryURL(entry->url, false,
228 &history_query_consumer_,
229 base::Bind(&CustomHomePagesTableModel::OnGotTitle,
230 base::Unretained(this)));
234 void CustomHomePagesTableModel::OnGotTitle(HistoryService::Handle handle,
235 bool found_url,
236 const history::URLRow* row,
237 history::VisitVector* visits) {
238 int entry_index;
239 Entry* entry =
240 GetEntryByLoadHandle(&Entry::title_handle, handle, &entry_index);
241 if (!entry) {
242 // The URLs changed before we were called back.
243 return;
245 entry->title_handle = 0;
246 if (found_url && !row->title().empty()) {
247 entry->title = row->title();
248 if (observer_)
249 observer_->OnItemsChanged(static_cast<int>(entry_index), 1);
253 CustomHomePagesTableModel::Entry*
254 CustomHomePagesTableModel::GetEntryByLoadHandle(
255 CancelableRequestProvider::Handle Entry::* member,
256 CancelableRequestProvider::Handle handle,
257 int* index) {
258 for (size_t i = 0; i < entries_.size(); ++i) {
259 if (entries_[i].*member == handle) {
260 *index = static_cast<int>(i);
261 return &entries_[i];
264 return NULL;
267 base::string16 CustomHomePagesTableModel::FormattedURL(int row) const {
268 std::string languages =
269 profile_->GetPrefs()->GetString(prefs::kAcceptLanguages);
270 base::string16 url = net::FormatUrl(entries_[row].url, languages);
271 url = base::i18n::GetDisplayStringInLTRDirectionality(url);
272 return url;