Infobar material design refresh: layout
[chromium-blink-merge.git] / chrome / browser / ui / search_engines / template_url_table_model.cc
blob24a73e96296dcd70d9f4f017750f5ef122c6d5b2
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/ui/search_engines/template_url_table_model.h"
7 #include "base/bind.h"
8 #include "base/i18n/rtl.h"
9 #include "base/task/cancelable_task_tracker.h"
10 #include "chrome/grit/generated_resources.h"
11 #include "components/favicon/core/favicon_service.h"
12 #include "components/favicon_base/favicon_types.h"
13 #include "components/search_engines/template_url.h"
14 #include "components/search_engines/template_url_service.h"
15 #include "ui/base/l10n/l10n_util.h"
16 #include "ui/base/models/table_model_observer.h"
17 #include "ui/base/resource/resource_bundle.h"
18 #include "ui/gfx/image/image_skia.h"
19 #include "ui/resources/grit/ui_resources.h"
21 // Group IDs used by TemplateURLTableModel.
22 static const int kMainGroupID = 0;
23 static const int kOtherGroupID = 1;
24 static const int kExtensionGroupID = 2;
26 // ModelEntry ----------------------------------------------------
28 // ModelEntry wraps a TemplateURL as returned from the TemplateURL.
29 // ModelEntry also tracks state information about the URL.
31 // Icon used while loading, or if a specific favicon can't be found.
32 static const gfx::ImageSkia* default_icon = NULL;
34 class TemplateURLTableModel::ModelEntry {
35 public:
36 ModelEntry(TemplateURLTableModel* model, TemplateURL* template_url)
37 : template_url_(template_url),
38 load_state_(NOT_LOADED),
39 model_(model) {
40 if (!default_icon) {
41 default_icon = ResourceBundle::GetSharedInstance().
42 GetNativeImageNamed(IDR_DEFAULT_FAVICON).ToImageSkia();
46 TemplateURL* template_url() {
47 return template_url_;
50 gfx::ImageSkia GetIcon() {
51 if (load_state_ == NOT_LOADED)
52 LoadFavicon();
53 if (!favicon_.isNull())
54 return favicon_;
55 return *default_icon;
58 // Resets internal status so that the next time the icon is asked for its
59 // fetched again. This should be invoked if the url is modified.
60 void ResetIcon() {
61 load_state_ = NOT_LOADED;
62 favicon_ = gfx::ImageSkia();
65 private:
66 // State of the favicon.
67 enum LoadState {
68 NOT_LOADED,
69 LOADING,
70 LOADED
73 void LoadFavicon() {
74 load_state_ = LOADED;
75 if (!model_->favicon_service_)
76 return;
77 GURL favicon_url = template_url()->favicon_url();
78 if (!favicon_url.is_valid()) {
79 // The favicon url isn't always set. Guess at one here.
80 if (template_url_->url_ref().IsValid(
81 model_->template_url_service_->search_terms_data())) {
82 GURL url(template_url_->url());
83 if (url.is_valid())
84 favicon_url = TemplateURL::GenerateFaviconURL(url);
86 if (!favicon_url.is_valid())
87 return;
89 load_state_ = LOADING;
90 model_->favicon_service_->GetFaviconImage(
91 favicon_url,
92 base::Bind(&ModelEntry::OnFaviconDataAvailable, base::Unretained(this)),
93 &tracker_);
96 void OnFaviconDataAvailable(
97 const favicon_base::FaviconImageResult& image_result) {
98 load_state_ = LOADED;
99 if (!image_result.image.IsEmpty()) {
100 favicon_ = image_result.image.AsImageSkia();
101 model_->FaviconAvailable(this);
105 TemplateURL* template_url_;
106 gfx::ImageSkia favicon_;
107 LoadState load_state_;
108 TemplateURLTableModel* model_;
109 base::CancelableTaskTracker tracker_;
111 DISALLOW_COPY_AND_ASSIGN(ModelEntry);
114 // TemplateURLTableModel -----------------------------------------
116 TemplateURLTableModel::TemplateURLTableModel(
117 TemplateURLService* template_url_service,
118 favicon::FaviconService* favicon_service)
119 : observer_(NULL),
120 template_url_service_(template_url_service),
121 favicon_service_(favicon_service) {
122 DCHECK(template_url_service);
123 template_url_service_->Load();
124 template_url_service_->AddObserver(this);
125 Reload();
128 TemplateURLTableModel::~TemplateURLTableModel() {
129 template_url_service_->RemoveObserver(this);
130 STLDeleteElements(&entries_);
133 void TemplateURLTableModel::Reload() {
134 STLDeleteElements(&entries_);
136 TemplateURLService::TemplateURLVector urls =
137 template_url_service_->GetTemplateURLs();
139 std::vector<ModelEntry*> default_entries, other_entries, extension_entries;
140 // Keywords that can be made the default first.
141 for (TemplateURLService::TemplateURLVector::iterator i = urls.begin();
142 i != urls.end(); ++i) {
143 TemplateURL* template_url = *i;
144 // NOTE: we don't use ShowInDefaultList here to avoid items bouncing around
145 // the lists while editing.
146 if (template_url->show_in_default_list())
147 default_entries.push_back(new ModelEntry(this, template_url));
148 else if (template_url->GetType() == TemplateURL::OMNIBOX_API_EXTENSION)
149 extension_entries.push_back(new ModelEntry(this, template_url));
150 else
151 other_entries.push_back(new ModelEntry(this, template_url));
154 last_search_engine_index_ = static_cast<int>(default_entries.size());
155 last_other_engine_index_ = last_search_engine_index_ +
156 static_cast<int>(other_entries.size());
158 entries_.insert(entries_.end(),
159 default_entries.begin(),
160 default_entries.end());
162 entries_.insert(entries_.end(),
163 other_entries.begin(),
164 other_entries.end());
166 entries_.insert(entries_.end(),
167 extension_entries.begin(),
168 extension_entries.end());
170 if (observer_)
171 observer_->OnModelChanged();
174 int TemplateURLTableModel::RowCount() {
175 return static_cast<int>(entries_.size());
178 base::string16 TemplateURLTableModel::GetText(int row, int col_id) {
179 DCHECK(row >= 0 && row < RowCount());
180 const TemplateURL* url = entries_[row]->template_url();
181 if (col_id == IDS_SEARCH_ENGINES_EDITOR_DESCRIPTION_COLUMN) {
182 base::string16 url_short_name = url->short_name();
183 // TODO(xji): Consider adding a special case if the short name is a URL,
184 // since those should always be displayed LTR. Please refer to
185 // http://crbug.com/6726 for more information.
186 base::i18n::AdjustStringForLocaleDirection(&url_short_name);
187 return (template_url_service_->GetDefaultSearchProvider() == url) ?
188 l10n_util::GetStringFUTF16(IDS_SEARCH_ENGINES_EDITOR_DEFAULT_ENGINE,
189 url_short_name) : url_short_name;
192 DCHECK_EQ(IDS_SEARCH_ENGINES_EDITOR_KEYWORD_COLUMN, col_id);
193 // Keyword should be domain name. Force it to have LTR directionality.
194 return base::i18n::GetDisplayStringInLTRDirectionality(url->keyword());
197 gfx::ImageSkia TemplateURLTableModel::GetIcon(int row) {
198 DCHECK(row >= 0 && row < RowCount());
199 return entries_[row]->GetIcon();
202 void TemplateURLTableModel::SetObserver(ui::TableModelObserver* observer) {
203 observer_ = observer;
206 bool TemplateURLTableModel::HasGroups() {
207 return true;
210 TemplateURLTableModel::Groups TemplateURLTableModel::GetGroups() {
211 Groups groups;
213 Group search_engine_group;
214 search_engine_group.title =
215 l10n_util::GetStringUTF16(IDS_SEARCH_ENGINES_EDITOR_MAIN_SEPARATOR);
216 search_engine_group.id = kMainGroupID;
217 groups.push_back(search_engine_group);
219 Group other_group;
220 other_group.title =
221 l10n_util::GetStringUTF16(IDS_SEARCH_ENGINES_EDITOR_OTHER_SEPARATOR);
222 other_group.id = kOtherGroupID;
223 groups.push_back(other_group);
225 Group extension_group;
226 extension_group.title =
227 l10n_util::GetStringUTF16(IDS_SEARCH_ENGINES_EDITOR_EXTENSIONS_SEPARATOR);
228 extension_group.id = kExtensionGroupID;
229 groups.push_back(extension_group);
231 return groups;
234 int TemplateURLTableModel::GetGroupID(int row) {
235 DCHECK(row >= 0 && row < RowCount());
236 if (row < last_search_engine_index_)
237 return kMainGroupID;
238 return row < last_other_engine_index_ ? kOtherGroupID : kExtensionGroupID;
241 void TemplateURLTableModel::Remove(int index) {
242 // Remove the observer while we modify the model, that way we don't need to
243 // worry about the model calling us back when we mutate it.
244 template_url_service_->RemoveObserver(this);
245 TemplateURL* template_url = GetTemplateURL(index);
247 scoped_ptr<ModelEntry> entry(RemoveEntry(index));
249 // Make sure to remove from the table model first, otherwise the
250 // TemplateURL would be freed.
251 template_url_service_->Remove(template_url);
252 template_url_service_->AddObserver(this);
255 void TemplateURLTableModel::Add(int index,
256 const base::string16& short_name,
257 const base::string16& keyword,
258 const std::string& url) {
259 DCHECK(index >= 0 && index <= RowCount());
260 DCHECK(!url.empty());
261 template_url_service_->RemoveObserver(this);
262 TemplateURLData data;
263 data.SetShortName(short_name);
264 data.SetKeyword(keyword);
265 data.SetURL(url);
266 TemplateURL* turl = new TemplateURL(data);
267 template_url_service_->Add(turl);
268 scoped_ptr<ModelEntry> entry(new ModelEntry(this, turl));
269 template_url_service_->AddObserver(this);
270 AddEntry(index, entry.Pass());
273 void TemplateURLTableModel::ModifyTemplateURL(int index,
274 const base::string16& title,
275 const base::string16& keyword,
276 const std::string& url) {
277 DCHECK(index >= 0 && index <= RowCount());
278 DCHECK(!url.empty());
279 TemplateURL* template_url = GetTemplateURL(index);
280 // The default search provider should support replacement.
281 DCHECK(template_url_service_->GetDefaultSearchProvider() != template_url ||
282 template_url->SupportsReplacement(
283 template_url_service_->search_terms_data()));
284 template_url_service_->RemoveObserver(this);
285 template_url_service_->ResetTemplateURL(template_url, title, keyword, url);
286 template_url_service_->AddObserver(this);
287 ReloadIcon(index); // Also calls NotifyChanged().
290 void TemplateURLTableModel::ReloadIcon(int index) {
291 DCHECK(index >= 0 && index < RowCount());
293 entries_[index]->ResetIcon();
295 NotifyChanged(index);
298 TemplateURL* TemplateURLTableModel::GetTemplateURL(int index) {
299 return entries_[index]->template_url();
302 int TemplateURLTableModel::IndexOfTemplateURL(
303 const TemplateURL* template_url) {
304 for (std::vector<ModelEntry*>::iterator i = entries_.begin();
305 i != entries_.end(); ++i) {
306 ModelEntry* entry = *i;
307 if (entry->template_url() == template_url)
308 return static_cast<int>(i - entries_.begin());
310 return -1;
313 int TemplateURLTableModel::MoveToMainGroup(int index) {
314 if (index < last_search_engine_index_)
315 return index; // Already in the main group.
317 scoped_ptr<ModelEntry> current_entry(RemoveEntry(index));
318 const int new_index = last_search_engine_index_++;
319 AddEntry(new_index, current_entry.Pass());
320 return new_index;
323 int TemplateURLTableModel::MakeDefaultTemplateURL(int index) {
324 if (index < 0 || index >= RowCount()) {
325 NOTREACHED();
326 return -1;
329 TemplateURL* keyword = GetTemplateURL(index);
330 const TemplateURL* current_default =
331 template_url_service_->GetDefaultSearchProvider();
332 if (current_default == keyword)
333 return -1;
335 template_url_service_->RemoveObserver(this);
336 template_url_service_->SetUserSelectedDefaultSearchProvider(keyword);
337 template_url_service_->AddObserver(this);
339 // The formatting of the default engine is different; notify the table that
340 // both old and new entries have changed.
341 if (current_default != NULL) {
342 int old_index = IndexOfTemplateURL(current_default);
343 // current_default may not be in the list of TemplateURLs if the database is
344 // corrupt and the default TemplateURL is used from preferences
345 if (old_index >= 0)
346 NotifyChanged(old_index);
348 const int new_index = IndexOfTemplateURL(keyword);
349 NotifyChanged(new_index);
351 // Make sure the new default is in the main group.
352 return MoveToMainGroup(index);
355 void TemplateURLTableModel::NotifyChanged(int index) {
356 if (observer_) {
357 DCHECK_GE(index, 0);
358 observer_->OnItemsChanged(index, 1);
362 void TemplateURLTableModel::FaviconAvailable(ModelEntry* entry) {
363 std::vector<ModelEntry*>::iterator i =
364 std::find(entries_.begin(), entries_.end(), entry);
365 DCHECK(i != entries_.end());
366 NotifyChanged(static_cast<int>(i - entries_.begin()));
369 void TemplateURLTableModel::OnTemplateURLServiceChanged() {
370 Reload();
373 scoped_ptr<TemplateURLTableModel::ModelEntry>
374 TemplateURLTableModel::RemoveEntry(int index) {
375 scoped_ptr<ModelEntry> entry(entries_[index]);
376 entries_.erase(index + entries_.begin());
377 if (index < last_search_engine_index_)
378 --last_search_engine_index_;
379 if (index < last_other_engine_index_)
380 --last_other_engine_index_;
381 if (observer_)
382 observer_->OnItemsRemoved(index, 1);
383 return entry.Pass();
386 void TemplateURLTableModel::AddEntry(int index, scoped_ptr<ModelEntry> entry) {
387 entries_.insert(entries_.begin() + index, entry.release());
388 if (index <= last_other_engine_index_)
389 ++last_other_engine_index_;
390 if (observer_)
391 observer_->OnItemsAdded(index, 1);