| OLD | NEW |
| 1 // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2009 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/views/keyword_editor_view.h" | 5 #include "chrome/browser/search_engines/keyword_editor_controller.h" |
| 6 | 6 |
| 7 #include <vector> | 7 #include "chrome/browser/metrics/user_metrics.h" |
| 8 | |
| 9 #include "app/l10n_util.h" | |
| 10 #include "app/resource_bundle.h" | |
| 11 #include "base/gfx/png_decoder.h" | |
| 12 #include "base/stl_util-inl.h" | |
| 13 #include "base/string_util.h" | |
| 14 #include "chrome/browser/history/history.h" | |
| 15 #include "chrome/browser/profile.h" | 8 #include "chrome/browser/profile.h" |
| 16 #include "chrome/browser/metrics/user_metrics.h" | |
| 17 #include "chrome/browser/search_engines/template_url.h" | 9 #include "chrome/browser/search_engines/template_url.h" |
| 18 #include "chrome/browser/search_engines/template_url_model.h" | 10 #include "chrome/browser/search_engines/template_url_model.h" |
| 19 #include "chrome/browser/views/browser_dialogs.h" | 11 #include "chrome/browser/search_engines/template_url_table_model.h" |
| 20 #include "chrome/common/pref_names.h" | |
| 21 #include "chrome/common/pref_service.h" | |
| 22 #include "googleurl/src/gurl.h" | |
| 23 #include "grit/app_resources.h" | |
| 24 #include "grit/generated_resources.h" | |
| 25 #include "grit/locale_settings.h" | |
| 26 #include "grit/theme_resources.h" | |
| 27 #include "third_party/skia/include/core/SkBitmap.h" | |
| 28 #include "views/background.h" | |
| 29 #include "views/grid_layout.h" | |
| 30 #include "views/controls/button/native_button.h" | |
| 31 #include "views/controls/table/table_view.h" | |
| 32 #include "views/controls/textfield/textfield.h" | |
| 33 #include "views/standard_layout.h" | |
| 34 #include "views/widget/widget.h" | |
| 35 #include "views/window/dialog_delegate.h" | |
| 36 #include "views/window/window.h" | |
| 37 | 12 |
| 38 using views::GridLayout; | 13 KeywordEditorController::KeywordEditorController(Profile* profile) |
| 39 using views::NativeButton; | 14 : profile_(profile) { |
| 40 | 15 table_model_.reset(new TemplateURLTableModel(profile->GetTemplateURLModel())); |
| 41 // Group IDs used by TemplateURLTableModel. | |
| 42 static const int kMainGroupID = 0; | |
| 43 static const int kOtherGroupID = 1; | |
| 44 | |
| 45 namespace browser { | |
| 46 | |
| 47 // Declared in browser_dialogs.h so others don't have to depend on our header. | |
| 48 void ShowKeywordEditorView(Profile* profile) { | |
| 49 KeywordEditorView::Show(profile); | |
| 50 } | 16 } |
| 51 | 17 |
| 52 } // namespace browser | 18 KeywordEditorController::~KeywordEditorController() { |
| 53 | |
| 54 // ModelEntry ---------------------------------------------------- | |
| 55 | |
| 56 // ModelEntry wraps a TemplateURL as returned from the TemplateURL. | |
| 57 // ModelEntry also tracks state information about the URL. | |
| 58 | |
| 59 // Icon used while loading, or if a specific favicon can't be found. | |
| 60 static SkBitmap* default_icon = NULL; | |
| 61 | |
| 62 class ModelEntry { | |
| 63 public: | |
| 64 explicit ModelEntry(TemplateURLTableModel* model, | |
| 65 const TemplateURL& template_url) | |
| 66 : model_(model), | |
| 67 template_url_(template_url) { | |
| 68 load_state_ = NOT_LOADED; | |
| 69 if (!default_icon) { | |
| 70 default_icon = ResourceBundle::GetSharedInstance(). | |
| 71 GetBitmapNamed(IDR_DEFAULT_FAVICON); | |
| 72 } | |
| 73 } | |
| 74 | |
| 75 const TemplateURL& template_url() { | |
| 76 return template_url_; | |
| 77 } | |
| 78 | |
| 79 SkBitmap GetIcon() { | |
| 80 if (load_state_ == NOT_LOADED) | |
| 81 LoadFavIcon(); | |
| 82 if (!fav_icon_.isNull()) | |
| 83 return fav_icon_; | |
| 84 return *default_icon; | |
| 85 } | |
| 86 | |
| 87 // Resets internal status so that the next time the icon is asked for its | |
| 88 // fetched again. This should be invoked if the url is modified. | |
| 89 void ResetIcon() { | |
| 90 load_state_ = NOT_LOADED; | |
| 91 fav_icon_ = SkBitmap(); | |
| 92 } | |
| 93 | |
| 94 private: | |
| 95 // State of the favicon. | |
| 96 enum LoadState { | |
| 97 NOT_LOADED, | |
| 98 LOADING, | |
| 99 LOADED | |
| 100 }; | |
| 101 | |
| 102 void LoadFavIcon() { | |
| 103 load_state_ = LOADED; | |
| 104 HistoryService* hs = | |
| 105 model_->template_url_model()->profile()->GetHistoryService( | |
| 106 Profile::EXPLICIT_ACCESS); | |
| 107 if (!hs) | |
| 108 return; | |
| 109 GURL fav_icon_url = template_url().GetFavIconURL(); | |
| 110 if (!fav_icon_url.is_valid()) { | |
| 111 // The favicon url isn't always set. Guess at one here. | |
| 112 if (template_url_.url() && template_url_.url()->IsValid()) { | |
| 113 GURL url = GURL(template_url_.url()->url()); | |
| 114 if (url.is_valid()) | |
| 115 fav_icon_url = TemplateURL::GenerateFaviconURL(url); | |
| 116 } | |
| 117 if (!fav_icon_url.is_valid()) | |
| 118 return; | |
| 119 } | |
| 120 load_state_ = LOADING; | |
| 121 hs->GetFavIcon(fav_icon_url, | |
| 122 &request_consumer_, | |
| 123 NewCallback(this, &ModelEntry::OnFavIconDataAvailable)); | |
| 124 } | |
| 125 | |
| 126 void OnFavIconDataAvailable( | |
| 127 HistoryService::Handle handle, | |
| 128 bool know_favicon, | |
| 129 scoped_refptr<RefCountedBytes> data, | |
| 130 bool expired, | |
| 131 GURL icon_url) { | |
| 132 load_state_ = LOADED; | |
| 133 if (know_favicon && data.get() && | |
| 134 PNGDecoder::Decode(&data->data, &fav_icon_)) { | |
| 135 model_->FavIconAvailable(this); | |
| 136 } | |
| 137 } | |
| 138 | |
| 139 const TemplateURL& template_url_; | |
| 140 SkBitmap fav_icon_; | |
| 141 LoadState load_state_; | |
| 142 TemplateURLTableModel* model_; | |
| 143 CancelableRequestConsumer request_consumer_; | |
| 144 | |
| 145 DISALLOW_EVIL_CONSTRUCTORS(ModelEntry); | |
| 146 }; | |
| 147 | |
| 148 // TemplateURLTableModel ----------------------------------------- | |
| 149 | |
| 150 TemplateURLTableModel::TemplateURLTableModel( | |
| 151 TemplateURLModel* template_url_model) | |
| 152 : observer_(NULL), | |
| 153 template_url_model_(template_url_model) { | |
| 154 DCHECK(template_url_model); | |
| 155 Reload(); | |
| 156 } | 19 } |
| 157 | 20 |
| 158 TemplateURLTableModel::~TemplateURLTableModel() { | 21 int KeywordEditorController::AddTemplateURL(const std::wstring& title, |
| 159 STLDeleteElements(&entries_); | 22 const std::wstring& keyword, |
| 160 entries_.clear(); | 23 const std::wstring& url) { |
| 161 } | |
| 162 | |
| 163 void TemplateURLTableModel::Reload() { | |
| 164 STLDeleteElements(&entries_); | |
| 165 entries_.clear(); | |
| 166 | |
| 167 std::vector<const TemplateURL*> urls = template_url_model_->GetTemplateURLs(); | |
| 168 | |
| 169 // Keywords that can be made the default first. | |
| 170 for (std::vector<const TemplateURL*>::iterator i = urls.begin(); | |
| 171 i != urls.end(); ++i) { | |
| 172 const TemplateURL& template_url = *(*i); | |
| 173 // NOTE: we don't use ShowInDefaultList here to avoid items bouncing around | |
| 174 // the lists while editing. | |
| 175 if (template_url.show_in_default_list()) | |
| 176 entries_.push_back(new ModelEntry(this, template_url)); | |
| 177 } | |
| 178 | |
| 179 last_search_engine_index_ = static_cast<int>(entries_.size()); | |
| 180 | |
| 181 // Then the rest. | |
| 182 for (std::vector<const TemplateURL*>::iterator i = urls.begin(); | |
| 183 i != urls.end(); ++i) { | |
| 184 const TemplateURL* template_url = *i; | |
| 185 // NOTE: we don't use ShowInDefaultList here to avoid things bouncing | |
| 186 // the lists while editing. | |
| 187 if (!template_url->show_in_default_list()) | |
| 188 entries_.push_back(new ModelEntry(this, *template_url)); | |
| 189 } | |
| 190 | |
| 191 if (observer_) | |
| 192 observer_->OnModelChanged(); | |
| 193 } | |
| 194 | |
| 195 int TemplateURLTableModel::RowCount() { | |
| 196 return static_cast<int>(entries_.size()); | |
| 197 } | |
| 198 | |
| 199 std::wstring TemplateURLTableModel::GetText(int row, int col_id) { | |
| 200 DCHECK(row >= 0 && row < RowCount()); | |
| 201 const TemplateURL& url = entries_[row]->template_url(); | |
| 202 | |
| 203 switch (col_id) { | |
| 204 case IDS_SEARCH_ENGINES_EDITOR_DESCRIPTION_COLUMN: { | |
| 205 std::wstring url_short_name = url.short_name(); | |
| 206 // TODO(xji): Consider adding a special case if the short name is a URL, | |
| 207 // since those should always be displayed LTR. Please refer to | |
| 208 // http://crbug.com/6726 for more information. | |
| 209 l10n_util::AdjustStringForLocaleDirection(url_short_name, | |
| 210 &url_short_name); | |
| 211 return (template_url_model_->GetDefaultSearchProvider() == &url) ? | |
| 212 l10n_util::GetStringF(IDS_SEARCH_ENGINES_EDITOR_DEFAULT_ENGINE, | |
| 213 url_short_name) : url_short_name; | |
| 214 } | |
| 215 | |
| 216 case IDS_SEARCH_ENGINES_EDITOR_KEYWORD_COLUMN: { | |
| 217 const std::wstring& keyword = url.keyword(); | |
| 218 // Keyword should be domain name. Force it to have LTR directionality. | |
| 219 if (l10n_util::GetTextDirection() == l10n_util::RIGHT_TO_LEFT) { | |
| 220 std::wstring localized_keyword = keyword; | |
| 221 l10n_util::WrapStringWithLTRFormatting(&localized_keyword); | |
| 222 return localized_keyword; | |
| 223 } | |
| 224 return keyword; | |
| 225 break; | |
| 226 } | |
| 227 | |
| 228 default: | |
| 229 NOTREACHED(); | |
| 230 return std::wstring(); | |
| 231 } | |
| 232 } | |
| 233 | |
| 234 SkBitmap TemplateURLTableModel::GetIcon(int row) { | |
| 235 DCHECK(row >= 0 && row < RowCount()); | |
| 236 return entries_[row]->GetIcon(); | |
| 237 } | |
| 238 | |
| 239 void TemplateURLTableModel::SetObserver(TableModelObserver* observer) { | |
| 240 observer_ = observer; | |
| 241 } | |
| 242 | |
| 243 bool TemplateURLTableModel::HasGroups() { | |
| 244 return true; | |
| 245 } | |
| 246 | |
| 247 TemplateURLTableModel::Groups TemplateURLTableModel::GetGroups() { | |
| 248 Groups groups; | |
| 249 | |
| 250 Group search_engine_group; | |
| 251 search_engine_group.title = | |
| 252 l10n_util::GetString(IDS_SEARCH_ENGINES_EDITOR_MAIN_SEPARATOR); | |
| 253 search_engine_group.id = kMainGroupID; | |
| 254 groups.push_back(search_engine_group); | |
| 255 | |
| 256 Group other_group; | |
| 257 other_group.title = | |
| 258 l10n_util::GetString(IDS_SEARCH_ENGINES_EDITOR_OTHER_SEPARATOR); | |
| 259 other_group.id = kOtherGroupID; | |
| 260 groups.push_back(other_group); | |
| 261 | |
| 262 return groups; | |
| 263 } | |
| 264 | |
| 265 int TemplateURLTableModel::GetGroupID(int row) { | |
| 266 DCHECK(row >= 0 && row < RowCount()); | |
| 267 return row < last_search_engine_index_ ? kMainGroupID : kOtherGroupID; | |
| 268 } | |
| 269 | |
| 270 void TemplateURLTableModel::Remove(int index) { | |
| 271 scoped_ptr<ModelEntry> entry(entries_[static_cast<int>(index)]); | |
| 272 entries_.erase(entries_.begin() + static_cast<int>(index)); | |
| 273 if (index < last_search_engine_index_) | |
| 274 last_search_engine_index_--; | |
| 275 if (observer_) | |
| 276 observer_->OnItemsRemoved(index, 1); | |
| 277 } | |
| 278 | |
| 279 void TemplateURLTableModel::Add(int index, const TemplateURL* template_url) { | |
| 280 DCHECK(index >= 0 && index <= RowCount()); | |
| 281 ModelEntry* entry = new ModelEntry(this, *template_url); | |
| 282 entries_.insert(entries_.begin() + index, entry); | |
| 283 if (observer_) | |
| 284 observer_->OnItemsAdded(index, 1); | |
| 285 } | |
| 286 | |
| 287 void TemplateURLTableModel::ReloadIcon(int index) { | |
| 288 DCHECK(index >= 0 && index < RowCount()); | |
| 289 | |
| 290 entries_[index]->ResetIcon(); | |
| 291 | |
| 292 NotifyChanged(index); | |
| 293 } | |
| 294 | |
| 295 const TemplateURL& TemplateURLTableModel::GetTemplateURL(int index) { | |
| 296 return entries_[index]->template_url(); | |
| 297 } | |
| 298 | |
| 299 int TemplateURLTableModel::IndexOfTemplateURL( | |
| 300 const TemplateURL* template_url) { | |
| 301 for (std::vector<ModelEntry*>::iterator i = entries_.begin(); | |
| 302 i != entries_.end(); ++i) { | |
| 303 ModelEntry* entry = *i; | |
| 304 if (&(entry->template_url()) == template_url) | |
| 305 return static_cast<int>(i - entries_.begin()); | |
| 306 } | |
| 307 return -1; | |
| 308 } | |
| 309 | |
| 310 void TemplateURLTableModel::MoveToMainGroup(int index) { | |
| 311 if (index < last_search_engine_index_) | |
| 312 return; // Already in the main group. | |
| 313 | |
| 314 ModelEntry* current_entry = entries_[index]; | |
| 315 entries_.erase(index + entries_.begin()); | |
| 316 if (observer_) | |
| 317 observer_->OnItemsRemoved(index, 1); | |
| 318 | |
| 319 const int new_index = last_search_engine_index_++; | |
| 320 entries_.insert(entries_.begin() + new_index, current_entry); | |
| 321 if (observer_) | |
| 322 observer_->OnItemsAdded(new_index, 1); | |
| 323 } | |
| 324 | |
| 325 void TemplateURLTableModel::NotifyChanged(int index) { | |
| 326 if (observer_) | |
| 327 observer_->OnItemsChanged(index, 1); | |
| 328 } | |
| 329 | |
| 330 void TemplateURLTableModel::FavIconAvailable(ModelEntry* entry) { | |
| 331 std::vector<ModelEntry*>::iterator i = | |
| 332 find(entries_.begin(), entries_.end(), entry); | |
| 333 DCHECK(i != entries_.end()); | |
| 334 NotifyChanged(static_cast<int>(i - entries_.begin())); | |
| 335 } | |
| 336 | |
| 337 // KeywordEditorView ---------------------------------------------------------- | |
| 338 | |
| 339 // If non-null, there is an open editor and this is the window it is contained | |
| 340 // in. | |
| 341 static views::Window* open_window = NULL; | |
| 342 | |
| 343 // static | |
| 344 void KeywordEditorView::Show(Profile* profile) { | |
| 345 if (!profile->GetTemplateURLModel()) | |
| 346 return; | |
| 347 | |
| 348 if (open_window != NULL) | |
| 349 open_window->Close(); | |
| 350 DCHECK(!open_window); | |
| 351 | |
| 352 // Both of these will be deleted when the dialog closes. | |
| 353 KeywordEditorView* keyword_editor = new KeywordEditorView(profile); | |
| 354 | |
| 355 // Initialize the UI. By passing in an empty rect KeywordEditorView is | |
| 356 // queried for its preferred size. | |
| 357 open_window = views::Window::CreateChromeWindow(NULL, gfx::Rect(), | |
| 358 keyword_editor); | |
| 359 | |
| 360 open_window->Show(); | |
| 361 } | |
| 362 | |
| 363 KeywordEditorView::KeywordEditorView(Profile* profile) | |
| 364 : profile_(profile), | |
| 365 url_model_(profile->GetTemplateURLModel()) { | |
| 366 DCHECK(url_model_); | |
| 367 Init(); | |
| 368 } | |
| 369 | |
| 370 KeywordEditorView::~KeywordEditorView() { | |
| 371 // Only remove the listener if we installed one. | |
| 372 if (table_model_.get()) { | |
| 373 table_view_->SetModel(NULL); | |
| 374 url_model_->RemoveObserver(this); | |
| 375 } | |
| 376 } | |
| 377 | |
| 378 void KeywordEditorView::OnEditedKeyword(const TemplateURL* template_url, | |
| 379 const std::wstring& title, | |
| 380 const std::wstring& keyword, | |
| 381 const std::wstring& url) { | |
| 382 if (template_url) { | |
| 383 ModifyTemplateURL(template_url, title, keyword, url); | |
| 384 } else { | |
| 385 AddTemplateURL(title, keyword, url); | |
| 386 } | |
| 387 } | |
| 388 | |
| 389 void KeywordEditorView::AddTemplateURL(const std::wstring& title, | |
| 390 const std::wstring& keyword, | |
| 391 const std::wstring& url) { | |
| 392 DCHECK(!url.empty()); | 24 DCHECK(!url.empty()); |
| 393 | 25 |
| 394 UserMetrics::RecordAction(L"KeywordEditor_AddKeyword", profile_); | 26 UserMetrics::RecordAction(L"KeywordEditor_AddKeyword", profile_); |
| 395 | 27 |
| 396 TemplateURL* template_url = new TemplateURL(); | 28 TemplateURL* template_url = new TemplateURL(); |
| 397 template_url->set_short_name(title); | 29 template_url->set_short_name(title); |
| 398 template_url->set_keyword(keyword); | 30 template_url->set_keyword(keyword); |
| 399 template_url->SetURL(url, 0, 0); | 31 template_url->SetURL(url, 0, 0); |
| 400 | 32 |
| 401 // There's a bug (1090726) in TableView with groups enabled such that newly | 33 // There's a bug (1090726) in TableView with groups enabled such that newly |
| 402 // added items in groups ALWAYS appear at the end, regardless of the index | 34 // added items in groups ALWAYS appear at the end, regardless of the index |
| 403 // passed in. Worse yet, the selected rows get messed up when this happens | 35 // passed in. Worse yet, the selected rows get messed up when this happens |
| 404 // causing other problems. As a work around we always add the item to the end | 36 // causing other problems. As a work around we always add the item to the end |
| 405 // of the list. | 37 // of the list. |
| 406 const int new_index = table_model_->RowCount(); | 38 const int new_index = table_model_->RowCount(); |
| 407 url_model_->RemoveObserver(this); | |
| 408 table_model_->Add(new_index, template_url); | 39 table_model_->Add(new_index, template_url); |
| 409 url_model_->Add(template_url); | |
| 410 url_model_->AddObserver(this); | |
| 411 | 40 |
| 412 table_view_->Select(new_index); | 41 return new_index; |
| 413 } | 42 } |
| 414 | 43 |
| 415 void KeywordEditorView::ModifyTemplateURL(const TemplateURL* template_url, | 44 void KeywordEditorController::ModifyTemplateURL(const TemplateURL* template_url, |
| 416 const std::wstring& title, | 45 const std::wstring& title, |
| 417 const std::wstring& keyword, | 46 const std::wstring& keyword, |
| 418 const std::wstring& url) { | 47 const std::wstring& url) { |
| 419 const int index = table_model_->IndexOfTemplateURL(template_url); | 48 const int index = table_model_->IndexOfTemplateURL(template_url); |
| 420 if (index == -1) { | 49 if (index == -1) { |
| 421 // Will happen if url was deleted out from under us while the user was | 50 // Will happen if url was deleted out from under us while the user was |
| 422 // editing it. | 51 // editing it. |
| 423 return; | 52 return; |
| 424 } | 53 } |
| 425 | 54 |
| 426 // Don't do anything if the entry didn't change. | 55 // Don't do anything if the entry didn't change. |
| 427 if (template_url->short_name() == title && | 56 if (template_url->short_name() == title && |
| 428 template_url->keyword() == keyword && | 57 template_url->keyword() == keyword && |
| 429 ((url.empty() && !template_url->url()) || | 58 ((url.empty() && !template_url->url()) || |
| 430 (!url.empty() && template_url->url() && | 59 (!url.empty() && template_url->url() && |
| 431 template_url->url()->url() == url))) { | 60 template_url->url()->url() == url))) { |
| 432 return; | 61 return; |
| 433 } | 62 } |
| 434 | 63 |
| 435 url_model_->RemoveObserver(this); | 64 table_model_->ModifyTemplateURL(index, title, keyword, url); |
| 436 url_model_->ResetTemplateURL(template_url, title, keyword, url); | |
| 437 if (url_model_->GetDefaultSearchProvider() == template_url && | |
| 438 !TemplateURL::SupportsReplacement(template_url)) { | |
| 439 // The entry was the default search provider, but the url has been modified | |
| 440 // so that it no longer supports replacement. Reset the default search | |
| 441 // provider so that it doesn't point to a bogus entry. | |
| 442 url_model_->SetDefaultSearchProvider(NULL); | |
| 443 } | |
| 444 url_model_->AddObserver(this); | |
| 445 table_model_->ReloadIcon(index); // Also calls NotifyChanged(). | |
| 446 | |
| 447 // Force the make default button to update. | |
| 448 OnSelectionChanged(); | |
| 449 | 65 |
| 450 UserMetrics::RecordAction(L"KeywordEditor_ModifiedKeyword", profile_); | 66 UserMetrics::RecordAction(L"KeywordEditor_ModifiedKeyword", profile_); |
| 451 } | 67 } |
| 452 | 68 |
| 453 gfx::Size KeywordEditorView::GetPreferredSize() { | 69 bool KeywordEditorController::CanMakeDefault(const TemplateURL* url) const { |
| 454 return gfx::Size(views::Window::GetLocalizedContentsSize( | 70 return (url != url_model()->GetDefaultSearchProvider() && |
| 455 IDS_SEARCHENGINES_DIALOG_WIDTH_CHARS, | 71 url->url() && |
| 456 IDS_SEARCHENGINES_DIALOG_HEIGHT_LINES)); | 72 url->url()->SupportsReplacement()); |
| 457 } | 73 } |
| 458 | 74 |
| 459 bool KeywordEditorView::CanResize() const { | 75 bool KeywordEditorController::CanRemove(const TemplateURL* url) const { |
| 460 return true; | 76 return url != url_model()->GetDefaultSearchProvider(); |
| 461 } | 77 } |
| 462 | 78 |
| 463 std::wstring KeywordEditorView::GetWindowTitle() const { | 79 void KeywordEditorController::RemoveTemplateURL(int index) { |
| 464 return l10n_util::GetString(IDS_SEARCH_ENGINES_EDITOR_WINDOW_TITLE); | 80 table_model_->Remove(index); |
| 81 UserMetrics::RecordAction(L"KeywordEditor_RemoveKeyword", profile_); |
| 465 } | 82 } |
| 466 | 83 |
| 467 int KeywordEditorView::GetDialogButtons() const { | 84 int KeywordEditorController::MakeDefaultTemplateURL(int index) { |
| 468 return MessageBoxFlags::DIALOGBUTTON_CANCEL; | 85 return table_model_->MakeDefaultTemplateURL(index); |
| 469 } | 86 } |
| 470 | 87 |
| 471 bool KeywordEditorView::Accept() { | 88 bool KeywordEditorController::loaded() const { |
| 472 open_window = NULL; | 89 return url_model()->loaded(); |
| 473 return true; | |
| 474 } | 90 } |
| 475 | 91 |
| 476 bool KeywordEditorView::Cancel() { | 92 const TemplateURL* KeywordEditorController::GetTemplateURL(int index) const { |
| 477 open_window = NULL; | 93 return &table_model_->GetTemplateURL(index); |
| 478 return true; | |
| 479 } | 94 } |
| 480 | 95 |
| 481 views::View* KeywordEditorView::GetContentsView() { | 96 TemplateURLModel* KeywordEditorController::url_model() const { |
| 482 return this; | 97 return table_model_->template_url_model(); |
| 483 } | 98 } |
| 484 | |
| 485 void KeywordEditorView::Init() { | |
| 486 DCHECK(!table_model_.get()); | |
| 487 | |
| 488 url_model_->Load(); | |
| 489 url_model_->AddObserver(this); | |
| 490 | |
| 491 table_model_.reset(new TemplateURLTableModel(url_model_)); | |
| 492 | |
| 493 std::vector<TableColumn> columns; | |
| 494 columns.push_back( | |
| 495 TableColumn(IDS_SEARCH_ENGINES_EDITOR_DESCRIPTION_COLUMN, | |
| 496 TableColumn::LEFT, -1, .75)); | |
| 497 columns.back().sortable = true; | |
| 498 columns.push_back( | |
| 499 TableColumn(IDS_SEARCH_ENGINES_EDITOR_KEYWORD_COLUMN, | |
| 500 TableColumn::LEFT, -1, .25)); | |
| 501 columns.back().sortable = true; | |
| 502 table_view_ = new views::TableView(table_model_.get(), columns, | |
| 503 views::ICON_AND_TEXT, false, true, true); | |
| 504 table_view_->SetObserver(this); | |
| 505 | |
| 506 add_button_ = new views::NativeButton( | |
| 507 this, l10n_util::GetString(IDS_SEARCH_ENGINES_EDITOR_NEW_BUTTON)); | |
| 508 add_button_->SetEnabled(url_model_->loaded()); | |
| 509 | |
| 510 edit_button_ = new views::NativeButton( | |
| 511 this, l10n_util::GetString(IDS_SEARCH_ENGINES_EDITOR_EDIT_BUTTON)); | |
| 512 edit_button_->SetEnabled(false); | |
| 513 | |
| 514 remove_button_ = new views::NativeButton( | |
| 515 this, l10n_util::GetString(IDS_SEARCH_ENGINES_EDITOR_REMOVE_BUTTON)); | |
| 516 remove_button_->SetEnabled(false); | |
| 517 | |
| 518 make_default_button_ = new views::NativeButton( | |
| 519 this, | |
| 520 l10n_util::GetString(IDS_SEARCH_ENGINES_EDITOR_MAKE_DEFAULT_BUTTON)); | |
| 521 make_default_button_->SetEnabled(false); | |
| 522 | |
| 523 InitLayoutManager(); | |
| 524 } | |
| 525 | |
| 526 void KeywordEditorView::InitLayoutManager() { | |
| 527 const int related_x = kRelatedControlHorizontalSpacing; | |
| 528 const int related_y = kRelatedControlVerticalSpacing; | |
| 529 const int unrelated_y = kUnrelatedControlVerticalSpacing; | |
| 530 | |
| 531 GridLayout* contents_layout = CreatePanelGridLayout(this); | |
| 532 SetLayoutManager(contents_layout); | |
| 533 | |
| 534 // For the table and buttons. | |
| 535 views::ColumnSet* column_set = contents_layout->AddColumnSet(0); | |
| 536 column_set->AddColumn(GridLayout::FILL, GridLayout::FILL, 1, | |
| 537 GridLayout::USE_PREF, 0, 0); | |
| 538 column_set->AddPaddingColumn(0, related_x); | |
| 539 column_set->AddColumn(GridLayout::FILL, GridLayout::CENTER, 0, | |
| 540 GridLayout::USE_PREF, 0, 0); | |
| 541 | |
| 542 contents_layout->StartRow(0, 0); | |
| 543 contents_layout->AddView(table_view_, 1, 8, GridLayout::FILL, | |
| 544 GridLayout::FILL); | |
| 545 contents_layout->AddView(add_button_); | |
| 546 | |
| 547 contents_layout->StartRowWithPadding(0, 0, 0, related_y); | |
| 548 contents_layout->SkipColumns(2); | |
| 549 contents_layout->AddView(edit_button_); | |
| 550 | |
| 551 contents_layout->StartRowWithPadding(0, 0, 0, related_y); | |
| 552 contents_layout->SkipColumns(2); | |
| 553 contents_layout->AddView(remove_button_); | |
| 554 | |
| 555 contents_layout->StartRowWithPadding(0, 0, 0, related_y); | |
| 556 contents_layout->SkipColumns(2); | |
| 557 contents_layout->AddView(make_default_button_); | |
| 558 | |
| 559 contents_layout->AddPaddingRow(1, 0); | |
| 560 } | |
| 561 | |
| 562 void KeywordEditorView::OnSelectionChanged() { | |
| 563 const int selected_row_count = table_view_->SelectedRowCount(); | |
| 564 edit_button_->SetEnabled(selected_row_count == 1); | |
| 565 bool can_make_default = false; | |
| 566 bool can_remove = false; | |
| 567 if (selected_row_count == 1) { | |
| 568 const TemplateURL* selected_url = | |
| 569 &table_model_->GetTemplateURL(table_view_->FirstSelectedRow()); | |
| 570 can_make_default = | |
| 571 (selected_url != url_model_->GetDefaultSearchProvider() && | |
| 572 selected_url->url() && | |
| 573 selected_url->url()->SupportsReplacement()); | |
| 574 can_remove = (selected_url != url_model_->GetDefaultSearchProvider()); | |
| 575 } | |
| 576 remove_button_->SetEnabled(can_remove); | |
| 577 make_default_button_->SetEnabled(can_make_default); | |
| 578 } | |
| 579 | |
| 580 void KeywordEditorView::OnDoubleClick() { | |
| 581 if (edit_button_->IsEnabled()) | |
| 582 ButtonPressed(edit_button_); | |
| 583 } | |
| 584 | |
| 585 void KeywordEditorView::ButtonPressed(views::Button* sender) { | |
| 586 if (sender == add_button_) { | |
| 587 browser::EditSearchEngine(GetWindow()->GetNativeWindow(), NULL, this, | |
| 588 profile_); | |
| 589 } else if (sender == remove_button_) { | |
| 590 DCHECK(table_view_->SelectedRowCount() > 0); | |
| 591 // Remove the observer while we modify the model, that way we don't need to | |
| 592 // worry about the model calling us back when we mutate it. | |
| 593 url_model_->RemoveObserver(this); | |
| 594 int last_view_row = -1; | |
| 595 for (views::TableView::iterator i = table_view_->SelectionBegin(); | |
| 596 i != table_view_->SelectionEnd(); ++i) { | |
| 597 last_view_row = table_view_->model_to_view(*i); | |
| 598 const TemplateURL* template_url = &table_model_->GetTemplateURL(*i); | |
| 599 // Make sure to remove from the table model first, otherwise the | |
| 600 // TemplateURL would be freed. | |
| 601 table_model_->Remove(*i); | |
| 602 url_model_->Remove(template_url); | |
| 603 } | |
| 604 if (last_view_row >= table_model_->RowCount()) | |
| 605 last_view_row = table_model_->RowCount() - 1; | |
| 606 if (last_view_row >= 0) | |
| 607 table_view_->Select(table_view_->view_to_model(last_view_row)); | |
| 608 url_model_->AddObserver(this); | |
| 609 UserMetrics::RecordAction(L"KeywordEditor_RemoveKeyword", profile_); | |
| 610 } else if (sender == edit_button_) { | |
| 611 const int selected_row = table_view_->FirstSelectedRow(); | |
| 612 const TemplateURL* template_url = | |
| 613 &table_model_->GetTemplateURL(selected_row); | |
| 614 browser::EditSearchEngine(GetWindow()->GetNativeWindow(), template_url, | |
| 615 this, profile_); | |
| 616 } else if (sender == make_default_button_) { | |
| 617 MakeDefaultSearchProvider(); | |
| 618 } else { | |
| 619 NOTREACHED(); | |
| 620 } | |
| 621 } | |
| 622 | |
| 623 void KeywordEditorView::OnTemplateURLModelChanged() { | |
| 624 table_model_->Reload(); | |
| 625 add_button_->SetEnabled(url_model_->loaded()); | |
| 626 } | |
| 627 | |
| 628 void KeywordEditorView::MakeDefaultSearchProvider() { | |
| 629 MakeDefaultSearchProvider(table_view_->FirstSelectedRow()); | |
| 630 } | |
| 631 | |
| 632 void KeywordEditorView::MakeDefaultSearchProvider(int index) { | |
| 633 if (index < 0 || index >= table_model_->RowCount()) { | |
| 634 NOTREACHED(); | |
| 635 return; | |
| 636 } | |
| 637 const TemplateURL* keyword = &table_model_->GetTemplateURL(index); | |
| 638 const TemplateURL* current_default = url_model_->GetDefaultSearchProvider(); | |
| 639 if (current_default == keyword) | |
| 640 return; | |
| 641 | |
| 642 url_model_->RemoveObserver(this); | |
| 643 url_model_->SetDefaultSearchProvider(keyword); | |
| 644 url_model_->AddObserver(this); | |
| 645 | |
| 646 // The formatting of the default engine is different; notify the table that | |
| 647 // both old and new entries have changed. | |
| 648 if (current_default != NULL) { | |
| 649 table_model_->NotifyChanged(table_model_->IndexOfTemplateURL( | |
| 650 current_default)); | |
| 651 } | |
| 652 const int new_index = table_model_->IndexOfTemplateURL(keyword); | |
| 653 table_model_->NotifyChanged(new_index); | |
| 654 | |
| 655 // Make sure the new default is in the main group. | |
| 656 table_model_->MoveToMainGroup(index); | |
| 657 | |
| 658 // And select it. | |
| 659 table_view_->Select(table_model_->IndexOfTemplateURL(keyword)); | |
| 660 } | |
| OLD | NEW |