summaryrefslogtreecommitdiffstats
path: root/chrome
diff options
context:
space:
mode:
authorjhawkins@chromium.org <jhawkins@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-03-14 21:29:00 +0000
committerjhawkins@chromium.org <jhawkins@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-03-14 21:29:00 +0000
commit74c2d8475aa9e4b4aa5a574ce9fb80d0a411df4a (patch)
tree2d2c0d87fc224db5d5d7c9babe94d11d1822b021 /chrome
parent442f14853ac893c4c7d9dac7c9292f933250e039 (diff)
downloadchromium_src-74c2d8475aa9e4b4aa5a574ce9fb80d0a411df4a.zip
chromium_src-74c2d8475aa9e4b4aa5a574ce9fb80d0a411df4a.tar.gz
chromium_src-74c2d8475aa9e4b4aa5a574ce9fb80d0a411df4a.tar.bz2
Revert 78081 - Options: Remove more unused dialogs.
BUG=75320 TEST=none Review URL: http://codereview.chromium.org/6685042 TBR=jhawkins@chromium.org Review URL: http://codereview.chromium.org/6675002 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@78097 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome')
-rw-r--r--chrome/browser/ui/gtk/browser_window_gtk.cc2
-rw-r--r--chrome/browser/ui/gtk/clear_browsing_data_dialog_gtk.cc249
-rw-r--r--chrome/browser/ui/gtk/clear_browsing_data_dialog_gtk.h66
-rw-r--r--chrome/browser/ui/gtk/keyword_editor_view.cc495
-rw-r--r--chrome/browser/ui/gtk/keyword_editor_view.h163
-rw-r--r--chrome/browser/ui/gtk/keyword_editor_view_unittest.cc271
-rw-r--r--chrome/browser/ui/views/browser_dialogs.h4
-rw-r--r--chrome/browser/ui/views/dialog_stubs_gtk.cc12
-rw-r--r--chrome/browser/ui/views/edit_search_engine_dialog.cc269
-rw-r--r--chrome/browser/ui/views/edit_search_engine_dialog.h105
-rw-r--r--chrome/browser/ui/views/keyword_editor_view.cc308
-rw-r--r--chrome/browser/ui/views/keyword_editor_view.h117
-rw-r--r--chrome/chrome_browser.gypi10
-rw-r--r--chrome/chrome_tests.gypi1
14 files changed, 2072 insertions, 0 deletions
diff --git a/chrome/browser/ui/gtk/browser_window_gtk.cc b/chrome/browser/ui/gtk/browser_window_gtk.cc
index f881387..2a418b6 100644
--- a/chrome/browser/ui/gtk/browser_window_gtk.cc
+++ b/chrome/browser/ui/gtk/browser_window_gtk.cc
@@ -43,6 +43,7 @@
#include "chrome/browser/ui/gtk/browser_titlebar.h"
#include "chrome/browser/ui/gtk/browser_toolbar_gtk.h"
#include "chrome/browser/ui/gtk/cairo_cached_surface.h"
+#include "chrome/browser/ui/gtk/clear_browsing_data_dialog_gtk.h"
#include "chrome/browser/ui/gtk/collected_cookies_gtk.h"
#include "chrome/browser/ui/gtk/create_application_shortcuts_dialog_gtk.h"
#include "chrome/browser/ui/gtk/download_in_progress_dialog_gtk.h"
@@ -57,6 +58,7 @@
#include "chrome/browser/ui/gtk/info_bubble_gtk.h"
#include "chrome/browser/ui/gtk/infobars/infobar_container_gtk.h"
#include "chrome/browser/ui/gtk/infobars/infobar_gtk.h"
+#include "chrome/browser/ui/gtk/keyword_editor_view.h"
#include "chrome/browser/ui/gtk/location_bar_view_gtk.h"
#include "chrome/browser/ui/gtk/nine_box.h"
#include "chrome/browser/ui/gtk/reload_button_gtk.h"
diff --git a/chrome/browser/ui/gtk/clear_browsing_data_dialog_gtk.cc b/chrome/browser/ui/gtk/clear_browsing_data_dialog_gtk.cc
new file mode 100644
index 0000000..86d45d8
--- /dev/null
+++ b/chrome/browser/ui/gtk/clear_browsing_data_dialog_gtk.cc
@@ -0,0 +1,249 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/gtk/clear_browsing_data_dialog_gtk.h"
+
+#include <string>
+
+#include "chrome/browser/browsing_data_remover.h"
+#include "chrome/browser/prefs/pref_service.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/gtk/browser_window_gtk.h"
+#include "chrome/browser/ui/gtk/gtk_chrome_link_button.h"
+#include "chrome/browser/ui/gtk/gtk_util.h"
+#include "chrome/common/pref_names.h"
+#include "grit/generated_resources.h"
+#include "grit/locale_settings.h"
+#include "ui/base/l10n/l10n_util.h"
+
+namespace {
+
+// Returns true if the checkbox is checked.
+gboolean IsChecked(GtkWidget* widget) {
+ return gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
+}
+
+} // namespace
+
+// static
+void ClearBrowsingDataDialogGtk::Show(GtkWindow* parent, Profile* profile) {
+ new ClearBrowsingDataDialogGtk(parent, profile);
+}
+
+ClearBrowsingDataDialogGtk::ClearBrowsingDataDialogGtk(GtkWindow* parent,
+ Profile* profile) :
+ profile_(profile), remover_(NULL) {
+ // Build the dialog.
+ std::string dialog_name = l10n_util::GetStringUTF8(
+ IDS_CLEAR_BROWSING_DATA_TITLE);
+ dialog_ = gtk_dialog_new_with_buttons(
+ dialog_name.c_str(),
+ parent,
+ (GtkDialogFlags) (GTK_DIALOG_MODAL | GTK_DIALOG_NO_SEPARATOR),
+ NULL);
+
+ GtkWidget* cancel_button = gtk_dialog_add_button(GTK_DIALOG(dialog_),
+ GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT);
+ gtk_widget_grab_focus(cancel_button);
+
+ gtk_util::AddButtonToDialog(dialog_,
+ l10n_util::GetStringUTF8(IDS_CLEAR_BROWSING_DATA_COMMIT).c_str(),
+ GTK_STOCK_APPLY, GTK_RESPONSE_ACCEPT);
+
+ GtkWidget* content_area = GTK_DIALOG(dialog_)->vbox;
+ gtk_box_set_spacing(GTK_BOX(content_area), gtk_util::kContentAreaSpacing);
+
+ GtkWidget* vbox = gtk_vbox_new(FALSE, gtk_util::kControlSpacing);
+ gtk_container_add(GTK_CONTAINER(content_area), vbox);
+
+ // Label on top of the checkboxes.
+ GtkWidget* description = gtk_label_new(
+ l10n_util::GetStringUTF8(IDS_CLEAR_BROWSING_DATA_LABEL).c_str());
+ gtk_misc_set_alignment(GTK_MISC(description), 0, 0);
+ gtk_box_pack_start(GTK_BOX(vbox), description, FALSE, FALSE, 0);
+
+ // History checkbox.
+ del_history_checkbox_ = gtk_check_button_new_with_label(
+ l10n_util::GetStringUTF8(IDS_DEL_BROWSING_HISTORY_CHKBOX).c_str());
+ gtk_box_pack_start(GTK_BOX(vbox), del_history_checkbox_, FALSE, FALSE, 0);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(del_history_checkbox_),
+ profile_->GetPrefs()->GetBoolean(prefs::kDeleteBrowsingHistory));
+ g_signal_connect(del_history_checkbox_, "toggled",
+ G_CALLBACK(OnDialogWidgetClickedThunk), this);
+
+ // Downloads checkbox.
+ del_downloads_checkbox_ = gtk_check_button_new_with_label(
+ l10n_util::GetStringUTF8(IDS_DEL_DOWNLOAD_HISTORY_CHKBOX).c_str());
+ gtk_box_pack_start(GTK_BOX(vbox), del_downloads_checkbox_, FALSE, FALSE, 0);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(del_downloads_checkbox_),
+ profile_->GetPrefs()->GetBoolean(prefs::kDeleteDownloadHistory));
+ g_signal_connect(del_downloads_checkbox_, "toggled",
+ G_CALLBACK(OnDialogWidgetClickedThunk), this);
+
+ // Cache checkbox.
+ del_cache_checkbox_ = gtk_check_button_new_with_label(
+ l10n_util::GetStringUTF8(IDS_DEL_CACHE_CHKBOX).c_str());
+ gtk_box_pack_start(GTK_BOX(vbox), del_cache_checkbox_, FALSE, FALSE, 0);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(del_cache_checkbox_),
+ profile_->GetPrefs()->GetBoolean(prefs::kDeleteCache));
+ g_signal_connect(del_cache_checkbox_, "toggled",
+ G_CALLBACK(OnDialogWidgetClickedThunk), this);
+
+ // Cookies checkbox.
+ del_cookies_checkbox_ = gtk_check_button_new_with_label(
+ l10n_util::GetStringUTF8(IDS_DEL_COOKIES_CHKBOX).c_str());
+ gtk_box_pack_start(GTK_BOX(vbox), del_cookies_checkbox_, FALSE, FALSE, 0);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(del_cookies_checkbox_),
+ profile_->GetPrefs()->GetBoolean(prefs::kDeleteCookies));
+ g_signal_connect(del_cookies_checkbox_, "toggled",
+ G_CALLBACK(OnDialogWidgetClickedThunk), this);
+
+ // Passwords checkbox.
+ del_passwords_checkbox_ = gtk_check_button_new_with_label(
+ l10n_util::GetStringUTF8(IDS_DEL_PASSWORDS_CHKBOX).c_str());
+ gtk_box_pack_start(GTK_BOX(vbox), del_passwords_checkbox_, FALSE, FALSE, 0);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(del_passwords_checkbox_),
+ profile_->GetPrefs()->GetBoolean(prefs::kDeletePasswords));
+ g_signal_connect(del_passwords_checkbox_, "toggled",
+ G_CALLBACK(OnDialogWidgetClickedThunk), this);
+
+ // Form data checkbox.
+ del_form_data_checkbox_ = gtk_check_button_new_with_label(
+ l10n_util::GetStringUTF8(IDS_DEL_FORM_DATA_CHKBOX).c_str());
+ gtk_box_pack_start(GTK_BOX(vbox), del_form_data_checkbox_, FALSE, FALSE, 0);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(del_form_data_checkbox_),
+ profile_->GetPrefs()->GetBoolean(prefs::kDeleteFormData));
+ g_signal_connect(del_form_data_checkbox_, "toggled",
+ G_CALLBACK(OnDialogWidgetClickedThunk), this);
+
+ // Create a horizontal layout for the combo box and label.
+ GtkWidget* combo_hbox = gtk_hbox_new(FALSE, gtk_util::kLabelSpacing);
+ GtkWidget* time_period_label_ = gtk_label_new(
+ l10n_util::GetStringUTF8(IDS_CLEAR_BROWSING_DATA_TIME_LABEL).c_str());
+ gtk_box_pack_start(GTK_BOX(combo_hbox), time_period_label_, FALSE, FALSE, 0);
+
+ // Time period combo box items.
+ time_period_combobox_ = gtk_combo_box_new_text();
+ gtk_combo_box_append_text(GTK_COMBO_BOX(time_period_combobox_),
+ l10n_util::GetStringUTF8(IDS_CLEAR_DATA_HOUR).c_str());
+ gtk_combo_box_append_text(GTK_COMBO_BOX(time_period_combobox_),
+ l10n_util::GetStringUTF8(IDS_CLEAR_DATA_DAY).c_str());
+ gtk_combo_box_append_text(GTK_COMBO_BOX(time_period_combobox_),
+ l10n_util::GetStringUTF8(IDS_CLEAR_DATA_WEEK).c_str());
+ gtk_combo_box_append_text(GTK_COMBO_BOX(time_period_combobox_),
+ l10n_util::GetStringUTF8(IDS_CLEAR_DATA_4WEEKS).c_str());
+ gtk_combo_box_append_text(GTK_COMBO_BOX(time_period_combobox_),
+ l10n_util::GetStringUTF8(IDS_CLEAR_DATA_EVERYTHING).c_str());
+ gtk_combo_box_set_active(GTK_COMBO_BOX(time_period_combobox_),
+ profile_->GetPrefs()->GetInteger(prefs::kDeleteTimePeriod));
+ gtk_box_pack_start(GTK_BOX(combo_hbox),
+ time_period_combobox_, FALSE, FALSE, 0);
+ g_signal_connect(time_period_combobox_, "changed",
+ G_CALLBACK(OnDialogWidgetClickedThunk), this);
+
+ // Add the combo/label time period box to the vertical layout.
+ gtk_box_pack_start(GTK_BOX(vbox), combo_hbox, FALSE, FALSE, 0);
+
+ // Add widgets for the area below the accept buttons.
+ GtkWidget* flash_link = gtk_chrome_link_button_new(
+ l10n_util::GetStringUTF8(IDS_FLASH_STORAGE_SETTINGS).c_str());
+ g_signal_connect(G_OBJECT(flash_link), "clicked",
+ G_CALLBACK(OnFlashLinkClickedThunk), this);
+ GtkWidget* flash_link_hbox = gtk_hbox_new(FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(flash_link_hbox), flash_link, FALSE, FALSE, 0);
+ gtk_box_pack_end(GTK_BOX(content_area), flash_link_hbox, FALSE, FALSE, 0);
+
+ GtkWidget* separator = gtk_hseparator_new();
+ gtk_box_pack_end(GTK_BOX(content_area), separator, FALSE, FALSE, 0);
+
+ // Make sure we can move things around.
+ DCHECK_EQ(GTK_DIALOG(dialog_)->action_area->parent, content_area);
+
+ // Now rearrange those because they're *above* the accept buttons...there's
+ // no way to place them in the correct position with gtk_box_pack_end() so
+ // manually move things into the correct order.
+ gtk_box_reorder_child(GTK_BOX(content_area), flash_link_hbox, -1);
+ gtk_box_reorder_child(GTK_BOX(content_area), separator, -1);
+ gtk_box_reorder_child(GTK_BOX(content_area), GTK_DIALOG(dialog_)->action_area,
+ -1);
+
+ g_signal_connect(dialog_, "response", G_CALLBACK(OnResponseThunk), this);
+
+ UpdateDialogButtons();
+
+ gtk_util::ShowDialogWithMinLocalizedWidth(dialog_,
+ IDS_CLEARDATA_DIALOG_WIDTH_CHARS);
+}
+
+ClearBrowsingDataDialogGtk::~ClearBrowsingDataDialogGtk() {
+}
+
+void ClearBrowsingDataDialogGtk::OnResponse(GtkWidget* dialog,
+ int response_id) {
+ if (response_id == GTK_RESPONSE_ACCEPT) {
+ PrefService* prefs = profile_->GetPrefs();
+ prefs->SetBoolean(prefs::kDeleteBrowsingHistory,
+ IsChecked(del_history_checkbox_));
+ prefs->SetBoolean(prefs::kDeleteDownloadHistory,
+ IsChecked(del_downloads_checkbox_));
+ prefs->SetBoolean(prefs::kDeleteCache,
+ IsChecked(del_cache_checkbox_));
+ prefs->SetBoolean(prefs::kDeleteCookies,
+ IsChecked(del_cookies_checkbox_));
+ prefs->SetBoolean(prefs::kDeletePasswords,
+ IsChecked(del_passwords_checkbox_));
+ prefs->SetBoolean(prefs::kDeleteFormData,
+ IsChecked(del_form_data_checkbox_));
+ prefs->SetInteger(prefs::kDeleteTimePeriod,
+ gtk_combo_box_get_active(GTK_COMBO_BOX(time_period_combobox_)));
+
+ int period_selected = gtk_combo_box_get_active(
+ GTK_COMBO_BOX(time_period_combobox_));
+
+ // BrowsingDataRemover deletes itself when done.
+ remover_ = new BrowsingDataRemover(profile_,
+ static_cast<BrowsingDataRemover::TimePeriod>(period_selected),
+ base::Time());
+ remover_->Remove(GetCheckedItems());
+ }
+
+ delete this;
+ gtk_widget_destroy(dialog);
+}
+
+void ClearBrowsingDataDialogGtk::OnDialogWidgetClicked(GtkWidget* widget) {
+ UpdateDialogButtons();
+}
+
+void ClearBrowsingDataDialogGtk::OnFlashLinkClicked(GtkWidget* button) {
+ // We open a new browser window so the Options dialog doesn't get lost
+ // behind other windows.
+ Browser* browser = Browser::Create(profile_);
+ browser->OpenURL(GURL(l10n_util::GetStringUTF8(IDS_FLASH_STORAGE_URL)),
+ GURL(), NEW_FOREGROUND_TAB, PageTransition::LINK);
+ browser->window()->Show();
+}
+
+void ClearBrowsingDataDialogGtk::UpdateDialogButtons() {
+ gtk_dialog_set_response_sensitive(GTK_DIALOG(dialog_), GTK_RESPONSE_ACCEPT,
+ GetCheckedItems() != 0);
+}
+
+int ClearBrowsingDataDialogGtk::GetCheckedItems() {
+ int items = 0;
+ if (IsChecked(del_history_checkbox_))
+ items |= BrowsingDataRemover::REMOVE_HISTORY;
+ if (IsChecked(del_downloads_checkbox_))
+ items |= BrowsingDataRemover::REMOVE_DOWNLOADS;
+ if (IsChecked(del_cookies_checkbox_))
+ items |= BrowsingDataRemover::REMOVE_COOKIES;
+ if (IsChecked(del_passwords_checkbox_))
+ items |= BrowsingDataRemover::REMOVE_PASSWORDS;
+ if (IsChecked(del_form_data_checkbox_))
+ items |= BrowsingDataRemover::REMOVE_FORM_DATA;
+ if (IsChecked(del_cache_checkbox_))
+ items |= BrowsingDataRemover::REMOVE_CACHE;
+ return items;
+}
diff --git a/chrome/browser/ui/gtk/clear_browsing_data_dialog_gtk.h b/chrome/browser/ui/gtk/clear_browsing_data_dialog_gtk.h
new file mode 100644
index 0000000..2782385
--- /dev/null
+++ b/chrome/browser/ui/gtk/clear_browsing_data_dialog_gtk.h
@@ -0,0 +1,66 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_GTK_CLEAR_BROWSING_DATA_DIALOG_GTK_H_
+#define CHROME_BROWSER_UI_GTK_CLEAR_BROWSING_DATA_DIALOG_GTK_H_
+#pragma once
+
+#include "base/basictypes.h"
+#include "base/scoped_ptr.h"
+#include "ui/base/gtk/gtk_signal.h"
+
+typedef struct _GtkWidget GtkWidget;
+typedef struct _GtkWindow GtkWindow;
+
+class BrowsingDataRemover;
+class Profile;
+
+class ClearBrowsingDataDialogGtk {
+ public:
+ // Displays the dialog box to clear browsing data from |profile|.
+ static void Show(GtkWindow* parent, Profile* profile);
+
+ private:
+ ClearBrowsingDataDialogGtk(GtkWindow* parent, Profile* profile);
+ ~ClearBrowsingDataDialogGtk();
+
+ // Handler to respond to OK and Cancel responses from the dialog.
+ CHROMEGTK_CALLBACK_1(ClearBrowsingDataDialogGtk, void, OnResponse, int);
+
+ // Handler to respond to widget clicked actions from the dialog.
+ CHROMEGTK_CALLBACK_0(ClearBrowsingDataDialogGtk, void, OnDialogWidgetClicked);
+
+ CHROMEGTK_CALLBACK_0(ClearBrowsingDataDialogGtk, void, OnFlashLinkClicked);
+
+ // Enable or disable the dialog buttons depending on the state of the
+ // checkboxes.
+ void UpdateDialogButtons();
+
+ // Create a bitmask from the checkboxes of the dialog.
+ int GetCheckedItems();
+
+ // The dialog window.
+ GtkWidget* dialog_;
+
+ // UI elements.
+ GtkWidget* del_history_checkbox_;
+ GtkWidget* del_downloads_checkbox_;
+ GtkWidget* del_cache_checkbox_;
+ GtkWidget* del_cookies_checkbox_;
+ GtkWidget* del_passwords_checkbox_;
+ GtkWidget* del_form_data_checkbox_;
+ GtkWidget* time_period_combobox_;
+
+ // Our current profile.
+ Profile* profile_;
+
+ // If non-null it means removal is in progress. BrowsingDataRemover takes care
+ // of deleting itself when done.
+ BrowsingDataRemover* remover_;
+
+ DISALLOW_COPY_AND_ASSIGN(ClearBrowsingDataDialogGtk);
+};
+
+
+#endif // CHROME_BROWSER_UI_GTK_CLEAR_BROWSING_DATA_DIALOG_GTK_H_
diff --git a/chrome/browser/ui/gtk/keyword_editor_view.cc b/chrome/browser/ui/gtk/keyword_editor_view.cc
new file mode 100644
index 0000000..f1777d2
--- /dev/null
+++ b/chrome/browser/ui/gtk/keyword_editor_view.cc
@@ -0,0 +1,495 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/gtk/keyword_editor_view.h"
+
+#include <string>
+
+#include "base/message_loop.h"
+#include "base/utf_string_conversions.h"
+#include "chrome/browser/metrics/user_metrics.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/search_engines/template_url.h"
+#include "chrome/browser/search_engines/template_url_model.h"
+#include "chrome/browser/ui/gtk/edit_search_engine_dialog.h"
+#include "chrome/browser/ui/gtk/gtk_tree.h"
+#include "chrome/browser/ui/gtk/gtk_util.h"
+#include "chrome/browser/ui/search_engines/keyword_editor_controller.h"
+#include "chrome/browser/ui/search_engines/template_url_table_model.h"
+#include "grit/generated_resources.h"
+#include "grit/locale_settings.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/gfx/gtk_util.h"
+
+namespace {
+
+// How many rows should be added to an index into the |table_model_| to get the
+// corresponding row in |list_store_|
+const int kFirstGroupRowOffset = 2;
+const int kSecondGroupRowOffset = 5;
+
+KeywordEditorView* instance_ = NULL;
+
+} // namespace
+
+// static
+void KeywordEditorView::Show(Profile* profile) {
+ DCHECK(profile);
+ // If this panel is opened from an Incognito window, closing that window can
+ // leave this with a stale pointer. Use the original profile instead.
+ // See http://crbug.com/23359.
+ profile = profile->GetOriginalProfile();
+ if (!profile->GetTemplateURLModel())
+ return;
+
+ // If there's already an existing editor window, activate it.
+ if (instance_) {
+ gtk_util::PresentWindow(instance_->dialog_, 0);
+ } else {
+ instance_ = new KeywordEditorView(profile);
+ gtk_util::ShowDialogWithLocalizedSize(instance_->dialog_,
+ IDS_SEARCHENGINES_DIALOG_WIDTH_CHARS,
+ IDS_SEARCHENGINES_DIALOG_HEIGHT_LINES,
+ true);
+ }
+}
+
+void KeywordEditorView::OnEditedKeyword(const TemplateURL* template_url,
+ const string16& title,
+ const string16& keyword,
+ const std::string& url) {
+ if (template_url) {
+ controller_->ModifyTemplateURL(template_url, title, keyword, url);
+
+ // Force the make default button to update.
+ EnableControls();
+ } else {
+ SelectModelRow(controller_->AddTemplateURL(title, keyword, url));
+ }
+}
+
+KeywordEditorView::~KeywordEditorView() {
+ controller_->url_model()->RemoveObserver(this);
+}
+
+KeywordEditorView::KeywordEditorView(Profile* profile)
+ : profile_(profile),
+ controller_(new KeywordEditorController(profile)),
+ table_model_(controller_->table_model()) {
+ Init();
+}
+
+void KeywordEditorView::Init() {
+ std::string dialog_name =
+ l10n_util::GetStringUTF8(IDS_SEARCH_ENGINES_EDITOR_WINDOW_TITLE);
+ dialog_ = gtk_dialog_new_with_buttons(
+ dialog_name.c_str(),
+ NULL,
+ // Non-modal.
+ GTK_DIALOG_NO_SEPARATOR,
+ GTK_STOCK_CLOSE,
+ GTK_RESPONSE_CLOSE,
+ NULL);
+
+ gtk_box_set_spacing(GTK_BOX(GTK_DIALOG(dialog_)->vbox),
+ gtk_util::kContentAreaSpacing);
+
+ GtkWidget* hbox = gtk_hbox_new(FALSE, gtk_util::kControlSpacing);
+ gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog_)->vbox), hbox);
+
+ GtkWidget* scroll_window = gtk_scrolled_window_new(NULL, NULL);
+ gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll_window),
+ GTK_POLICY_AUTOMATIC,
+ GTK_POLICY_AUTOMATIC);
+ gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scroll_window),
+ GTK_SHADOW_ETCHED_IN);
+ gtk_box_pack_start(GTK_BOX(hbox), scroll_window, TRUE, TRUE, 0);
+
+ list_store_ = gtk_list_store_new(COL_COUNT,
+ GDK_TYPE_PIXBUF,
+ G_TYPE_STRING,
+ G_TYPE_STRING,
+ G_TYPE_BOOLEAN,
+ G_TYPE_BOOLEAN,
+ G_TYPE_INT,
+ G_TYPE_BOOLEAN);
+ tree_ = gtk_tree_view_new_with_model(GTK_TREE_MODEL(list_store_));
+ g_object_unref(list_store_);
+ gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(tree_), TRUE);
+ gtk_tree_view_set_row_separator_func(GTK_TREE_VIEW(tree_),
+ OnCheckRowIsSeparator,
+ NULL, NULL);
+ g_signal_connect(tree_, "row-activated",
+ G_CALLBACK(OnRowActivated), this);
+ gtk_container_add(GTK_CONTAINER(scroll_window), tree_);
+
+ GtkTreeViewColumn* title_column = gtk_tree_view_column_new();
+ GtkCellRenderer* pixbuf_renderer = gtk_cell_renderer_pixbuf_new();
+ gtk_tree_view_column_pack_start(title_column, pixbuf_renderer, FALSE);
+ gtk_tree_view_column_add_attribute(title_column, pixbuf_renderer, "pixbuf",
+ COL_FAVICON);
+ GtkCellRenderer* title_renderer = gtk_cell_renderer_text_new();
+ gtk_tree_view_column_pack_start(title_column, title_renderer, TRUE);
+ gtk_tree_view_column_add_attribute(title_column, title_renderer, "text",
+ COL_TITLE);
+ gtk_tree_view_column_add_attribute(title_column, title_renderer, "weight",
+ COL_WEIGHT);
+ gtk_tree_view_column_add_attribute(title_column, title_renderer, "weight-set",
+ COL_WEIGHT_SET);
+ gtk_tree_view_column_set_title(
+ title_column, l10n_util::GetStringUTF8(
+ IDS_SEARCH_ENGINES_EDITOR_DESCRIPTION_COLUMN).c_str());
+ gtk_tree_view_append_column(GTK_TREE_VIEW(tree_), title_column);
+
+ GtkTreeViewColumn* keyword_column = gtk_tree_view_column_new_with_attributes(
+ l10n_util::GetStringUTF8(
+ IDS_SEARCH_ENGINES_EDITOR_KEYWORD_COLUMN).c_str(),
+ gtk_cell_renderer_text_new(),
+ "text", COL_KEYWORD,
+ NULL);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(tree_), keyword_column);
+
+ selection_ = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_));
+ gtk_tree_selection_set_mode(selection_, GTK_SELECTION_SINGLE);
+ gtk_tree_selection_set_select_function(selection_, OnSelectionFilter,
+ NULL, NULL);
+ g_signal_connect(selection_, "changed",
+ G_CALLBACK(OnSelectionChanged), this);
+
+ GtkWidget* button_box = gtk_vbox_new(FALSE, gtk_util::kControlSpacing);
+ gtk_box_pack_start(GTK_BOX(hbox), button_box, FALSE, FALSE, 0);
+
+ add_button_ = gtk_button_new_with_mnemonic(
+ gfx::ConvertAcceleratorsFromWindowsStyle(
+ l10n_util::GetStringUTF8(
+ IDS_SEARCH_ENGINES_EDITOR_NEW_BUTTON)).c_str());
+ g_signal_connect(add_button_, "clicked",
+ G_CALLBACK(OnAddButtonClicked), this);
+ gtk_box_pack_start(GTK_BOX(button_box), add_button_, FALSE, FALSE, 0);
+
+ edit_button_ = gtk_button_new_with_label(
+ l10n_util::GetStringUTF8(IDS_SEARCH_ENGINES_EDITOR_EDIT_BUTTON).c_str());
+ g_signal_connect(edit_button_, "clicked",
+ G_CALLBACK(OnEditButtonClicked), this);
+ gtk_box_pack_start(GTK_BOX(button_box), edit_button_, FALSE, FALSE, 0);
+
+ remove_button_ = gtk_button_new_with_mnemonic(
+ gfx::ConvertAcceleratorsFromWindowsStyle(
+ l10n_util::GetStringUTF8(
+ IDS_SEARCH_ENGINES_EDITOR_REMOVE_BUTTON)).c_str());
+ g_signal_connect(remove_button_, "clicked",
+ G_CALLBACK(OnRemoveButtonClicked), this);
+ gtk_box_pack_start(GTK_BOX(button_box), remove_button_, FALSE, FALSE, 0);
+
+ make_default_button_ = gtk_button_new_with_label(
+ l10n_util::GetStringUTF8(
+ IDS_SEARCH_ENGINES_EDITOR_MAKE_DEFAULT_BUTTON).c_str());
+ g_signal_connect(make_default_button_, "clicked",
+ G_CALLBACK(OnMakeDefaultButtonClicked), this);
+ gtk_box_pack_start(GTK_BOX(button_box), make_default_button_, FALSE, FALSE,
+ 0);
+
+ controller_->url_model()->AddObserver(this);
+ table_model_->SetObserver(this);
+ table_model_->Reload();
+
+ EnableControls();
+
+ g_signal_connect(dialog_, "response", G_CALLBACK(OnResponseThunk), this);
+ g_signal_connect(dialog_, "destroy", G_CALLBACK(OnWindowDestroy), this);
+}
+
+void KeywordEditorView::EnableControls() {
+ bool can_edit = false;
+ bool can_make_default = false;
+ bool can_remove = false;
+ int model_row = GetSelectedModelRow();
+ if (model_row != -1) {
+ const TemplateURL* selected_url = controller_->GetTemplateURL(model_row);
+ can_edit = controller_->CanEdit(selected_url);
+ can_make_default = controller_->CanMakeDefault(selected_url);
+ can_remove = controller_->CanRemove(selected_url);
+ }
+ gtk_widget_set_sensitive(add_button_, controller_->loaded());
+ gtk_widget_set_sensitive(edit_button_, can_edit);
+ gtk_widget_set_sensitive(remove_button_, can_remove);
+ gtk_widget_set_sensitive(make_default_button_, can_make_default);
+}
+
+void KeywordEditorView::SetColumnValues(int model_row, GtkTreeIter* iter) {
+ SkBitmap bitmap = table_model_->GetIcon(model_row);
+ GdkPixbuf* pixbuf = gfx::GdkPixbufFromSkBitmap(&bitmap);
+ gtk_list_store_set(
+ list_store_, iter,
+ COL_FAVICON, pixbuf,
+ // Dunno why, even with COL_WEIGHT_SET to FALSE here, the weight still
+ // has an effect. So we just set it to normal.
+ COL_WEIGHT, PANGO_WEIGHT_NORMAL,
+ COL_WEIGHT_SET, TRUE,
+ COL_TITLE, UTF16ToUTF8(table_model_->GetText(
+ model_row, IDS_SEARCH_ENGINES_EDITOR_DESCRIPTION_COLUMN)).c_str(),
+ COL_KEYWORD, UTF16ToUTF8(table_model_->GetText(
+ model_row, IDS_SEARCH_ENGINES_EDITOR_KEYWORD_COLUMN)).c_str(),
+ -1);
+ g_object_unref(pixbuf);
+}
+
+int KeywordEditorView::GetListStoreRowForModelRow(int model_row) const {
+ if (model_row < model_second_group_index_)
+ return model_row + kFirstGroupRowOffset;
+ else
+ return model_row + kSecondGroupRowOffset;
+}
+
+int KeywordEditorView::GetModelRowForPath(GtkTreePath* path) const {
+ gint* indices = gtk_tree_path_get_indices(path);
+ if (!indices) {
+ NOTREACHED();
+ return -1;
+ }
+ if (indices[0] >= model_second_group_index_ + kSecondGroupRowOffset)
+ return indices[0] - kSecondGroupRowOffset;
+ return indices[0] - kFirstGroupRowOffset;
+}
+
+int KeywordEditorView::GetModelRowForIter(GtkTreeIter* iter) const {
+ GtkTreePath* path = gtk_tree_model_get_path(GTK_TREE_MODEL(list_store_),
+ iter);
+ int model_row = GetModelRowForPath(path);
+ gtk_tree_path_free(path);
+ return model_row;
+}
+
+int KeywordEditorView::GetSelectedModelRow() const {
+ GtkTreeIter iter;
+ if (!gtk_tree_selection_get_selected(selection_, NULL, &iter))
+ return -1;
+ return GetModelRowForIter(&iter);
+}
+
+void KeywordEditorView::SelectModelRow(int model_row) {
+ int row = GetListStoreRowForModelRow(model_row);
+ gtk_tree::SelectAndFocusRowNum(row, GTK_TREE_VIEW(tree_));
+}
+
+void KeywordEditorView::AddNodeToList(int model_row) {
+ GtkTreeIter iter;
+ int row = GetListStoreRowForModelRow(model_row);
+ if (row == 0) {
+ gtk_list_store_prepend(list_store_, &iter);
+ } else {
+ GtkTreeIter sibling;
+ gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(list_store_), &sibling,
+ NULL, row - 1);
+ gtk_list_store_insert_after(list_store_, &iter, &sibling);
+ }
+
+ SetColumnValues(model_row, &iter);
+}
+
+void KeywordEditorView::OnModelChanged() {
+ model_second_group_index_ = table_model_->last_search_engine_index();
+ gtk_list_store_clear(list_store_);
+
+ ui::TableModel::Groups groups(table_model_->GetGroups());
+ if (groups.size() != 2) {
+ NOTREACHED();
+ return;
+ }
+
+ GtkTreeIter iter;
+ // First group title.
+ gtk_list_store_append(list_store_, &iter);
+ gtk_list_store_set(
+ list_store_, &iter,
+ COL_WEIGHT, PANGO_WEIGHT_BOLD,
+ COL_WEIGHT_SET, TRUE,
+ COL_TITLE, UTF16ToUTF8(groups[0].title).c_str(),
+ COL_IS_HEADER, TRUE,
+ -1);
+ // First group separator.
+ gtk_list_store_append(list_store_, &iter);
+ gtk_list_store_set(
+ list_store_, &iter,
+ COL_IS_HEADER, TRUE,
+ COL_IS_SEPARATOR, TRUE,
+ -1);
+
+ // Blank row between groups.
+ gtk_list_store_append(list_store_, &iter);
+ gtk_list_store_set(
+ list_store_, &iter,
+ COL_IS_HEADER, TRUE,
+ -1);
+ // Second group title.
+ gtk_list_store_append(list_store_, &iter);
+ gtk_list_store_set(
+ list_store_, &iter,
+ COL_WEIGHT, PANGO_WEIGHT_BOLD,
+ COL_WEIGHT_SET, TRUE,
+ COL_TITLE, UTF16ToUTF8(groups[1].title).c_str(),
+ COL_IS_HEADER, TRUE,
+ -1);
+ // Second group separator.
+ gtk_list_store_append(list_store_, &iter);
+ gtk_list_store_set(
+ list_store_, &iter,
+ COL_IS_HEADER, TRUE,
+ COL_IS_SEPARATOR, TRUE,
+ -1);
+
+ for (int i = 0; i < table_model_->RowCount(); ++i)
+ AddNodeToList(i);
+}
+
+void KeywordEditorView::OnItemsChanged(int start, int length) {
+ DCHECK(model_second_group_index_ == table_model_->last_search_engine_index());
+ GtkTreeIter iter;
+ for (int i = 0; i < length; ++i) {
+ int row = GetListStoreRowForModelRow(start + i);
+ bool rv = gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(list_store_),
+ &iter, NULL, row);
+ if (!rv) {
+ NOTREACHED();
+ return;
+ }
+ SetColumnValues(start + i, &iter);
+ rv = gtk_tree_model_iter_next(GTK_TREE_MODEL(list_store_), &iter);
+ }
+}
+
+void KeywordEditorView::OnItemsAdded(int start, int length) {
+ model_second_group_index_ = table_model_->last_search_engine_index();
+ for (int i = 0; i < length; ++i) {
+ AddNodeToList(start + i);
+ }
+}
+
+void KeywordEditorView::OnItemsRemoved(int start, int length) {
+ // This is quite likely not correct with removing multiple in one call, but
+ // that shouldn't happen since we only can select and modify/remove one at a
+ // time.
+ DCHECK_EQ(length, 1);
+ for (int i = 0; i < length; ++i) {
+ int row = GetListStoreRowForModelRow(start + i);
+ GtkTreeIter iter;
+ if (!gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(list_store_), &iter,
+ NULL, row)) {
+ NOTREACHED();
+ return;
+ }
+ gtk_list_store_remove(list_store_, &iter);
+ }
+ model_second_group_index_ = table_model_->last_search_engine_index();
+}
+
+void KeywordEditorView::OnTemplateURLModelChanged() {
+ EnableControls();
+}
+
+// static
+void KeywordEditorView::OnWindowDestroy(GtkWidget* widget,
+ KeywordEditorView* window) {
+ instance_ = NULL;
+ MessageLoop::current()->DeleteSoon(FROM_HERE, window);
+}
+
+void KeywordEditorView::OnResponse(GtkWidget* dialog, int response_id) {
+ gtk_widget_destroy(dialog_);
+}
+
+// static
+gboolean KeywordEditorView::OnCheckRowIsSeparator(GtkTreeModel* model,
+ GtkTreeIter* iter,
+ gpointer user_data) {
+ gboolean is_separator;
+ gtk_tree_model_get(model, iter, COL_IS_SEPARATOR, &is_separator, -1);
+ return is_separator;
+}
+
+// static
+gboolean KeywordEditorView::OnSelectionFilter(GtkTreeSelection* selection,
+ GtkTreeModel* model,
+ GtkTreePath* path,
+ gboolean path_currently_selected,
+ gpointer user_data) {
+ GtkTreeIter iter;
+ if (!gtk_tree_model_get_iter(model, &iter, path)) {
+ NOTREACHED();
+ return TRUE;
+ }
+ gboolean is_header;
+ gtk_tree_model_get(model, &iter, COL_IS_HEADER, &is_header, -1);
+ return !is_header;
+}
+
+// static
+void KeywordEditorView::OnSelectionChanged(
+ GtkTreeSelection* selection, KeywordEditorView* editor) {
+ editor->EnableControls();
+}
+
+// static
+void KeywordEditorView::OnRowActivated(GtkTreeView* tree_view,
+ GtkTreePath* path,
+ GtkTreeViewColumn* column,
+ KeywordEditorView* editor) {
+ OnEditButtonClicked(NULL, editor);
+}
+
+// static
+void KeywordEditorView::OnAddButtonClicked(GtkButton* button,
+ KeywordEditorView* editor) {
+ new EditSearchEngineDialog(
+ GTK_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(
+ gtk_util::GetDialogWindow(editor->dialog_)))),
+ NULL,
+ editor,
+ editor->profile_);
+}
+
+// static
+void KeywordEditorView::OnEditButtonClicked(GtkButton* button,
+ KeywordEditorView* editor) {
+ int model_row = editor->GetSelectedModelRow();
+ if (model_row == -1)
+ return;
+
+ new EditSearchEngineDialog(
+ GTK_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(
+ gtk_util::GetDialogWindow(editor->dialog_)))),
+ editor->controller_->GetTemplateURL(model_row),
+ editor,
+ editor->profile_);
+}
+
+// static
+void KeywordEditorView::OnRemoveButtonClicked(GtkButton* button,
+ KeywordEditorView* editor) {
+ int model_row = editor->GetSelectedModelRow();
+ if (model_row == -1) {
+ NOTREACHED();
+ return;
+ }
+ editor->controller_->RemoveTemplateURL(model_row);
+ if (model_row >= editor->table_model_->RowCount())
+ model_row = editor->table_model_->RowCount() - 1;
+ if (model_row >= 0)
+ editor->SelectModelRow(model_row);
+}
+
+// static
+void KeywordEditorView::OnMakeDefaultButtonClicked(GtkButton* button,
+ KeywordEditorView* editor) {
+ int model_row = editor->GetSelectedModelRow();
+ if (model_row == -1) {
+ NOTREACHED();
+ return;
+ }
+ int new_index = editor->controller_->MakeDefaultTemplateURL(model_row);
+ if (new_index > 0) {
+ editor->SelectModelRow(new_index);
+ }
+}
diff --git a/chrome/browser/ui/gtk/keyword_editor_view.h b/chrome/browser/ui/gtk/keyword_editor_view.h
new file mode 100644
index 0000000..0bc68b5
--- /dev/null
+++ b/chrome/browser/ui/gtk/keyword_editor_view.h
@@ -0,0 +1,163 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_GTK_KEYWORD_EDITOR_VIEW_H_
+#define CHROME_BROWSER_UI_GTK_KEYWORD_EDITOR_VIEW_H_
+#pragma once
+
+#include <gtk/gtk.h>
+
+#include "base/basictypes.h"
+#include "base/gtest_prod_util.h"
+#include "base/scoped_ptr.h"
+#include "base/string16.h"
+#include "chrome/browser/search_engines/template_url_model_observer.h"
+#include "chrome/browser/ui/search_engines/edit_search_engine_controller.h"
+#include "ui/base/gtk/gtk_signal.h"
+#include "ui/base/models/table_model_observer.h"
+
+class KeywordEditorController;
+class Profile;
+class TemplateURLTableModel;
+
+class KeywordEditorView : public ui::TableModelObserver,
+ public TemplateURLModelObserver,
+ public EditSearchEngineControllerDelegate {
+ public:
+ virtual ~KeywordEditorView();
+
+ // Create (if necessary) and show the keyword editor window.
+ static void Show(Profile* profile);
+
+ // Overriden from EditSearchEngineControllerDelegate.
+ virtual void OnEditedKeyword(const TemplateURL* template_url,
+ const string16& title,
+ const string16& keyword,
+ const std::string& url);
+ private:
+ // Column ids for |list_store_|.
+ enum {
+ COL_FAVICON,
+ COL_TITLE,
+ COL_KEYWORD,
+ COL_IS_HEADER,
+ COL_IS_SEPARATOR,
+ COL_WEIGHT,
+ COL_WEIGHT_SET,
+ COL_COUNT,
+ };
+
+ explicit KeywordEditorView(Profile* profile);
+ void Init();
+
+ // Enable buttons based on selection state.
+ void EnableControls();
+
+ // Set the column values for |row| of |table_model_| in the |list_store_| at
+ // |iter|.
+ void SetColumnValues(int row, GtkTreeIter* iter);
+
+ // Get the row number in the GtkListStore corresponding to |model_row|.
+ int GetListStoreRowForModelRow(int model_row) const;
+
+ // Get the row number in the TemplateURLTableModel corresponding to |path|.
+ int GetModelRowForPath(GtkTreePath* path) const;
+
+ // Get the row number in the TemplateURLTableModel corresponding to |iter|.
+ int GetModelRowForIter(GtkTreeIter* iter) const;
+
+ // Get the row number in the TemplateURLTableModel of the current selection,
+ // or -1 if no row is selected.
+ int GetSelectedModelRow() const;
+
+ // Select the row in the |tree_| corresponding to |model_row|.
+ void SelectModelRow(int model_row);
+
+ // Add the values from |row| of |table_model_|.
+ void AddNodeToList(int row);
+
+ // ui::TableModelObserver implementation.
+ virtual void OnModelChanged();
+ virtual void OnItemsChanged(int start, int length);
+ virtual void OnItemsAdded(int start, int length);
+ virtual void OnItemsRemoved(int start, int length);
+
+ // TemplateURLModelObserver notification.
+ virtual void OnTemplateURLModelChanged();
+
+ // Callback for window destruction.
+ static void OnWindowDestroy(GtkWidget* widget, KeywordEditorView* window);
+
+ // Callback for dialog buttons.
+ CHROMEGTK_CALLBACK_1(KeywordEditorView, void, OnResponse, int);
+
+ // Callback checking whether a row should be drawn as a separator.
+ static gboolean OnCheckRowIsSeparator(GtkTreeModel* model,
+ GtkTreeIter* iter,
+ gpointer user_data);
+
+ // Callback checking whether a row may be selected. We use some rows in the
+ // table as headers/separators for the groups, which should not be selectable.
+ static gboolean OnSelectionFilter(GtkTreeSelection* selection,
+ GtkTreeModel* model,
+ GtkTreePath* path,
+ gboolean path_currently_selected,
+ gpointer user_data);
+
+ // Callback for when user selects something.
+ static void OnSelectionChanged(GtkTreeSelection* selection,
+ KeywordEditorView* editor);
+
+ // Callbacks for user actions modifying the table.
+ static void OnRowActivated(GtkTreeView* tree_view,
+ GtkTreePath* path,
+ GtkTreeViewColumn* column,
+ KeywordEditorView* editor);
+ static void OnAddButtonClicked(GtkButton* button,
+ KeywordEditorView* editor);
+ static void OnEditButtonClicked(GtkButton* button,
+ KeywordEditorView* editor);
+ static void OnRemoveButtonClicked(GtkButton* button,
+ KeywordEditorView* editor);
+ static void OnMakeDefaultButtonClicked(GtkButton* button,
+ KeywordEditorView* editor);
+
+ // The table listing the search engines.
+ GtkWidget* tree_;
+ GtkListStore* list_store_;
+ GtkTreeSelection* selection_;
+
+ // Buttons for acting on the table.
+ GtkWidget* add_button_;
+ GtkWidget* edit_button_;
+ GtkWidget* remove_button_;
+ GtkWidget* make_default_button_;
+
+ // The containing dialog.
+ GtkWidget* dialog_;
+
+ // The profile.
+ Profile* profile_;
+
+ scoped_ptr<KeywordEditorController> controller_;
+
+ TemplateURLTableModel* table_model_;
+
+ // We store our own index of the start of the second group within the model,
+ // as when OnItemsRemoved is called the value in the model is already updated
+ // but we need the old value to know which row to remove from the
+ // |list_store_|.
+ int model_second_group_index_;
+
+ friend class KeywordEditorViewTest;
+ FRIEND_TEST_ALL_PREFIXES(KeywordEditorViewTest, Empty);
+ FRIEND_TEST_ALL_PREFIXES(KeywordEditorViewTest, Add);
+ FRIEND_TEST_ALL_PREFIXES(KeywordEditorViewTest, MakeDefault);
+ FRIEND_TEST_ALL_PREFIXES(KeywordEditorViewTest, Remove);
+ FRIEND_TEST_ALL_PREFIXES(KeywordEditorViewTest, Edit);
+
+ DISALLOW_COPY_AND_ASSIGN(KeywordEditorView);
+};
+
+#endif // CHROME_BROWSER_UI_GTK_KEYWORD_EDITOR_VIEW_H_
diff --git a/chrome/browser/ui/gtk/keyword_editor_view_unittest.cc b/chrome/browser/ui/gtk/keyword_editor_view_unittest.cc
new file mode 100644
index 0000000..fa88efd
--- /dev/null
+++ b/chrome/browser/ui/gtk/keyword_editor_view_unittest.cc
@@ -0,0 +1,271 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/gtk/keyword_editor_view.h"
+
+#include <gtk/gtk.h>
+
+#include <string>
+#include <vector>
+
+#include "base/string16.h"
+#include "base/string_util.h"
+#include "base/utf_string_conversions.h"
+#include "chrome/browser/search_engines/template_url.h"
+#include "chrome/browser/search_engines/template_url_model.h"
+#include "chrome/browser/ui/gtk/gtk_tree.h"
+#include "chrome/browser/ui/search_engines/template_url_table_model.h"
+#include "chrome/test/testing_profile.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+class KeywordEditorViewTest : public testing::Test {
+ public:
+ virtual void SetUp() {
+ profile_.reset(new TestingProfile());
+ profile_->CreateTemplateURLModel();
+ }
+
+ TemplateURL* AddToModel(const std::string& name,
+ const std::string& keyword,
+ bool make_default) {
+ TemplateURL* template_url = new TemplateURL();
+ template_url->set_short_name(UTF8ToUTF16(name));
+ template_url->set_keyword(UTF8ToUTF16(keyword));
+ template_url->SetURL("http://example.com/{searchTerms}", 0, 0);
+ profile_->GetTemplateURLModel()->Add(template_url);
+ if (make_default)
+ profile_->GetTemplateURLModel()->SetDefaultSearchProvider(template_url);
+ return template_url;
+ }
+
+ int GetSelectedRowNum(const KeywordEditorView& editor) {
+ GtkTreeIter iter;
+ if (!gtk_tree_selection_get_selected(editor.selection_, NULL, &iter))
+ return -1;
+ return gtk_tree::GetRowNumForIter(GTK_TREE_MODEL(editor.list_store_),
+ &iter);
+ }
+
+ // Get the search engines displayed in the dialog in the order they are
+ // displayed, as a comma seperated string.
+ // The headers are included as "!,_" for the first group header and "_,@,_"
+ // for the second group header (This allows the tests to ensure the headers
+ // aren't accidentally misplaced/moved.)
+ // Ex: EXPECT_STREQ("!,_,A (Default),_,@,_,B",
+ // GetDisplayedEngines(editor).c_str());
+ std::string GetDisplayedEngines(const KeywordEditorView& editor) {
+ ui::TableModel::Groups groups(editor.table_model_->GetGroups());
+ std::vector<std::string> parts;
+ GtkTreeModel* tree_model = GTK_TREE_MODEL(editor.list_store_);
+ GtkTreeIter iter;
+ if (!gtk_tree_model_get_iter_first(tree_model, &iter))
+ return std::string();
+ while (true) {
+ gchar* name;
+ gboolean is_header;
+ gtk_tree_model_get(tree_model, &iter,
+ KeywordEditorView::COL_TITLE, &name,
+ KeywordEditorView::COL_IS_HEADER, &is_header,
+ -1);
+ if (name && UTF16ToUTF8(groups[0].title) == name)
+ parts.push_back("!");
+ else if (name && UTF16ToUTF8(groups[1].title) == name)
+ parts.push_back("@");
+ else if (is_header)
+ parts.push_back("_");
+ else if (name)
+ parts.push_back(name);
+ else
+ parts.push_back("???");
+ if (name)
+ g_free(name);
+ if (!gtk_tree_model_iter_next(tree_model, &iter))
+ break;
+ }
+ return JoinString(parts, ',');
+ }
+
+ protected:
+ MessageLoopForUI message_loop_;
+ scoped_ptr<TestingProfile> profile_;
+};
+
+TEST_F(KeywordEditorViewTest, Empty) {
+ KeywordEditorView editor(profile_.get());
+ EXPECT_EQ(TRUE, GTK_WIDGET_SENSITIVE(editor.add_button_));
+ EXPECT_EQ(FALSE, GTK_WIDGET_SENSITIVE(editor.edit_button_));
+ EXPECT_EQ(FALSE, GTK_WIDGET_SENSITIVE(editor.remove_button_));
+ EXPECT_EQ(FALSE, GTK_WIDGET_SENSITIVE(editor.make_default_button_));
+ EXPECT_STREQ("!,_,_,@,_", GetDisplayedEngines(editor).c_str());
+ EXPECT_EQ(-1, GetSelectedRowNum(editor));
+}
+
+TEST_F(KeywordEditorViewTest, Add) {
+ AddToModel("A1", "k1", true);
+ KeywordEditorView editor(profile_.get());
+ EXPECT_STREQ("!,_,A1 (Default),_,@,_", GetDisplayedEngines(editor).c_str());
+ EXPECT_EQ(-1, GetSelectedRowNum(editor));
+
+ editor.OnEditedKeyword(NULL, ASCIIToUTF16("B"), ASCIIToUTF16("b"),
+ "example.com");
+ EXPECT_EQ(TRUE, GTK_WIDGET_SENSITIVE(editor.add_button_));
+ EXPECT_EQ(TRUE, GTK_WIDGET_SENSITIVE(editor.edit_button_));
+ EXPECT_EQ(TRUE, GTK_WIDGET_SENSITIVE(editor.remove_button_));
+ EXPECT_EQ(FALSE, GTK_WIDGET_SENSITIVE(editor.make_default_button_));
+ EXPECT_STREQ("!,_,A1 (Default),_,@,_,B", GetDisplayedEngines(editor).c_str());
+ EXPECT_EQ(6, GetSelectedRowNum(editor));
+
+ editor.OnEditedKeyword(NULL, ASCIIToUTF16("C"), ASCIIToUTF16("c"),
+ "example.com/{searchTerms}");
+ EXPECT_EQ(TRUE, GTK_WIDGET_SENSITIVE(editor.add_button_));
+ EXPECT_EQ(TRUE, GTK_WIDGET_SENSITIVE(editor.edit_button_));
+ EXPECT_EQ(TRUE, GTK_WIDGET_SENSITIVE(editor.remove_button_));
+ EXPECT_EQ(TRUE, GTK_WIDGET_SENSITIVE(editor.make_default_button_));
+ EXPECT_STREQ("!,_,A1 (Default),_,@,_,B,C",
+ GetDisplayedEngines(editor).c_str());
+ EXPECT_EQ(7, GetSelectedRowNum(editor));
+
+ editor.OnEditedKeyword(NULL, ASCIIToUTF16("D"), ASCIIToUTF16("d"),
+ "example.com");
+ EXPECT_EQ(TRUE, GTK_WIDGET_SENSITIVE(editor.add_button_));
+ EXPECT_EQ(TRUE, GTK_WIDGET_SENSITIVE(editor.edit_button_));
+ EXPECT_EQ(TRUE, GTK_WIDGET_SENSITIVE(editor.remove_button_));
+ EXPECT_EQ(FALSE, GTK_WIDGET_SENSITIVE(editor.make_default_button_));
+ EXPECT_STREQ("!,_,A1 (Default),_,@,_,B,C,D",
+ GetDisplayedEngines(editor).c_str());
+ EXPECT_EQ(8, GetSelectedRowNum(editor));
+}
+
+TEST_F(KeywordEditorViewTest, MakeDefault) {
+ AddToModel("A", "a", true);
+ AddToModel("B", "b", false);
+ AddToModel("C", "c", false);
+ AddToModel("D", "d", false);
+ KeywordEditorView editor(profile_.get());
+ EXPECT_STREQ("!,_,A (Default),_,@,_,B,C,D",
+ GetDisplayedEngines(editor).c_str());
+
+ GtkTreeIter iter;
+ gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(editor.list_store_),
+ &iter, NULL, 6);
+ gtk_tree_selection_select_iter(editor.selection_, &iter);
+ EXPECT_EQ(TRUE, GTK_WIDGET_SENSITIVE(editor.make_default_button_));
+
+ gtk_button_clicked(GTK_BUTTON(editor.make_default_button_));
+ EXPECT_EQ(FALSE, GTK_WIDGET_SENSITIVE(editor.remove_button_));
+ EXPECT_EQ(FALSE, GTK_WIDGET_SENSITIVE(editor.make_default_button_));
+ EXPECT_STREQ("!,_,A,B (Default),_,@,_,C,D",
+ GetDisplayedEngines(editor).c_str());
+ EXPECT_EQ(3, GetSelectedRowNum(editor));
+
+ gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(editor.list_store_),
+ &iter, NULL, 8);
+ gtk_tree_selection_select_iter(editor.selection_, &iter);
+ EXPECT_EQ(TRUE, GTK_WIDGET_SENSITIVE(editor.make_default_button_));
+
+ gtk_button_clicked(GTK_BUTTON(editor.make_default_button_));
+ EXPECT_EQ(FALSE, GTK_WIDGET_SENSITIVE(editor.remove_button_));
+ EXPECT_EQ(FALSE, GTK_WIDGET_SENSITIVE(editor.make_default_button_));
+ EXPECT_STREQ("!,_,A,B,D (Default),_,@,_,C",
+ GetDisplayedEngines(editor).c_str());
+ EXPECT_EQ(4, GetSelectedRowNum(editor));
+
+ gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(editor.list_store_),
+ &iter, NULL, 2);
+ gtk_tree_selection_select_iter(editor.selection_, &iter);
+ EXPECT_EQ(TRUE, GTK_WIDGET_SENSITIVE(editor.make_default_button_));
+
+ gtk_button_clicked(GTK_BUTTON(editor.make_default_button_));
+ EXPECT_EQ(FALSE, GTK_WIDGET_SENSITIVE(editor.remove_button_));
+ EXPECT_EQ(FALSE, GTK_WIDGET_SENSITIVE(editor.make_default_button_));
+ EXPECT_STREQ("!,_,A (Default),B,D,_,@,_,C",
+ GetDisplayedEngines(editor).c_str());
+ EXPECT_EQ(2, GetSelectedRowNum(editor));
+
+ gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(editor.list_store_),
+ &iter, NULL, 4);
+ gtk_tree_selection_select_iter(editor.selection_, &iter);
+ EXPECT_EQ(TRUE, GTK_WIDGET_SENSITIVE(editor.make_default_button_));
+
+ gtk_button_clicked(GTK_BUTTON(editor.make_default_button_));
+ EXPECT_EQ(FALSE, GTK_WIDGET_SENSITIVE(editor.remove_button_));
+ EXPECT_EQ(FALSE, GTK_WIDGET_SENSITIVE(editor.make_default_button_));
+ EXPECT_STREQ("!,_,A,B,D (Default),_,@,_,C",
+ GetDisplayedEngines(editor).c_str());
+ EXPECT_EQ(4, GetSelectedRowNum(editor));
+}
+
+TEST_F(KeywordEditorViewTest, Remove) {
+ AddToModel("A", "a", true);
+ AddToModel("B", "b", true);
+ AddToModel("C", "c", false);
+ AddToModel("D", "d", false);
+ KeywordEditorView editor(profile_.get());
+ EXPECT_STREQ("!,_,A,B (Default),_,@,_,C,D",
+ GetDisplayedEngines(editor).c_str());
+
+ GtkTreeIter iter;
+ gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(editor.list_store_),
+ &iter, NULL, 2);
+ gtk_tree_selection_select_iter(editor.selection_, &iter);
+ EXPECT_EQ(TRUE, GTK_WIDGET_SENSITIVE(editor.remove_button_));
+
+ gtk_button_clicked(GTK_BUTTON(editor.remove_button_));
+ EXPECT_STREQ("!,_,B (Default),_,@,_,C,D",
+ GetDisplayedEngines(editor).c_str());
+ EXPECT_EQ(FALSE, GTK_WIDGET_SENSITIVE(editor.remove_button_));
+ EXPECT_EQ(2, GetSelectedRowNum(editor));
+
+ gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(editor.list_store_),
+ &iter, NULL, 6);
+ gtk_tree_selection_select_iter(editor.selection_, &iter);
+ EXPECT_EQ(TRUE, GTK_WIDGET_SENSITIVE(editor.remove_button_));
+
+ gtk_button_clicked(GTK_BUTTON(editor.remove_button_));
+ EXPECT_STREQ("!,_,B (Default),_,@,_,D",
+ GetDisplayedEngines(editor).c_str());
+ EXPECT_EQ(TRUE, GTK_WIDGET_SENSITIVE(editor.remove_button_));
+ EXPECT_EQ(6, GetSelectedRowNum(editor));
+
+ gtk_button_clicked(GTK_BUTTON(editor.remove_button_));
+ EXPECT_STREQ("!,_,B (Default),_,@,_",
+ GetDisplayedEngines(editor).c_str());
+ EXPECT_EQ(FALSE, GTK_WIDGET_SENSITIVE(editor.remove_button_));
+ EXPECT_EQ(2, GetSelectedRowNum(editor));
+}
+
+TEST_F(KeywordEditorViewTest, Edit) {
+ TemplateURL* a = AddToModel("A", "a", true);
+ TemplateURL* b = AddToModel("B", "b", true);
+ TemplateURL* c = AddToModel("C", "c", false);
+ TemplateURL* d = AddToModel("D", "d", false);
+ KeywordEditorView editor(profile_.get());
+ EXPECT_STREQ("!,_,A,B (Default),_,@,_,C,D",
+ GetDisplayedEngines(editor).c_str());
+
+ editor.OnEditedKeyword(a, ASCIIToUTF16("AA"), ASCIIToUTF16("a"),
+ "example.com/{searchTerms}");
+ EXPECT_STREQ("!,_,AA,B (Default),_,@,_,C,D",
+ GetDisplayedEngines(editor).c_str());
+
+ editor.OnEditedKeyword(b, ASCIIToUTF16("BB"), ASCIIToUTF16("b"),
+ "foo.example.com/{searchTerms}");
+ EXPECT_STREQ("!,_,AA,BB (Default),_,@,_,C,D",
+ GetDisplayedEngines(editor).c_str());
+
+ editor.OnEditedKeyword(b, ASCIIToUTF16("BBB"), ASCIIToUTF16("b"),
+ "example.com");
+ EXPECT_STREQ("!,_,AA,BBB,_,@,_,C,D",
+ GetDisplayedEngines(editor).c_str());
+
+ editor.OnEditedKeyword(d, ASCIIToUTF16("DD"), ASCIIToUTF16("d"),
+ "example.com");
+ EXPECT_STREQ("!,_,AA,BBB,_,@,_,C,DD",
+ GetDisplayedEngines(editor).c_str());
+
+ editor.OnEditedKeyword(c, ASCIIToUTF16("CC"), ASCIIToUTF16("cc"),
+ "example.com");
+ EXPECT_STREQ("!,_,AA,BBB,_,@,_,CC,DD",
+ GetDisplayedEngines(editor).c_str());
+}
diff --git a/chrome/browser/ui/views/browser_dialogs.h b/chrome/browser/ui/views/browser_dialogs.h
index 295aeec..d5be7e9 100644
--- a/chrome/browser/ui/views/browser_dialogs.h
+++ b/chrome/browser/ui/views/browser_dialogs.h
@@ -47,6 +47,10 @@ void ShowBugReportView(views::Window* parent,
Profile* profile,
TabContents* tab);
+// Shows the "Clear browsing data" dialog box. See ClearBrowsingDataView.
+void ShowClearBrowsingDataView(gfx::NativeWindow parent,
+ Profile* profile);
+
// Shows or hides the global bookmark bubble for the star button.
void ShowBookmarkBubbleView(views::Window* parent,
const gfx::Rect& bounds,
diff --git a/chrome/browser/ui/views/dialog_stubs_gtk.cc b/chrome/browser/ui/views/dialog_stubs_gtk.cc
index e496110..00cd889 100644
--- a/chrome/browser/ui/views/dialog_stubs_gtk.cc
+++ b/chrome/browser/ui/views/dialog_stubs_gtk.cc
@@ -10,8 +10,10 @@
#include "base/logging.h"
#include "chrome/browser/fonts_languages_window.h"
#include "chrome/browser/ui/gtk/about_chrome_dialog.h"
+#include "chrome/browser/ui/gtk/clear_browsing_data_dialog_gtk.h"
#include "chrome/browser/ui/gtk/collected_cookies_gtk.h"
#include "chrome/browser/ui/gtk/edit_search_engine_dialog.h"
+#include "chrome/browser/ui/gtk/keyword_editor_view.h"
#include "chrome/browser/ui/gtk/repost_form_warning_gtk.h"
#include "chrome/browser/ui/gtk/task_manager_gtk.h"
#include "chrome/browser/ui/options/options_window.h"
@@ -22,6 +24,16 @@
namespace browser {
+void ShowClearBrowsingDataView(views::Widget* parent,
+ Profile* profile) {
+ ClearBrowsingDataDialogGtk::Show(GTK_WINDOW(parent->GetNativeView()),
+ profile);
+}
+
+void ShowKeywordEditorView(Profile* profile) {
+ KeywordEditorView::Show(profile);
+}
+
void ShowTaskManager() {
TaskManagerGtk::Show(false);
}
diff --git a/chrome/browser/ui/views/edit_search_engine_dialog.cc b/chrome/browser/ui/views/edit_search_engine_dialog.cc
new file mode 100644
index 0000000..a727fdb
--- /dev/null
+++ b/chrome/browser/ui/views/edit_search_engine_dialog.cc
@@ -0,0 +1,269 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/views/edit_search_engine_dialog.h"
+
+#include "base/i18n/rtl.h"
+#include "base/string_util.h"
+#include "base/utf_string_conversions.h"
+#include "chrome/browser/search_engines/template_url.h"
+#include "chrome/browser/ui/search_engines/edit_search_engine_controller.h"
+#include "googleurl/src/gurl.h"
+#include "grit/app_resources.h"
+#include "grit/generated_resources.h"
+#include "grit/theme_resources.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/base/resource/resource_bundle.h"
+#include "views/controls/image_view.h"
+#include "views/controls/label.h"
+#include "views/controls/table/table_view.h"
+#include "views/controls/textfield/textfield.h"
+#include "views/layout/grid_layout.h"
+#include "views/layout/layout_constants.h"
+#include "views/window/window.h"
+
+using views::GridLayout;
+using views::ImageView;
+using views::Textfield;
+
+
+namespace {
+// Converts a URL as understood by TemplateURL to one appropriate for display
+// to the user.
+std::wstring GetDisplayURL(const TemplateURL& turl) {
+ return turl.url() ? turl.url()->DisplayURL() : std::wstring();
+}
+} // namespace
+
+namespace browser {
+
+void EditSearchEngine(gfx::NativeWindow parent,
+ const TemplateURL* template_url,
+ EditSearchEngineControllerDelegate* delegate,
+ Profile* profile) {
+ EditSearchEngineDialog::Show(parent, template_url, delegate, profile);
+}
+
+} // namespace browser
+
+EditSearchEngineDialog::EditSearchEngineDialog(
+ const TemplateURL* template_url,
+ EditSearchEngineControllerDelegate* delegate,
+ Profile* profile)
+ : controller_(new EditSearchEngineController(template_url,
+ delegate,
+ profile)) {
+ Init();
+}
+
+// static
+void EditSearchEngineDialog::Show(gfx::NativeWindow parent,
+ const TemplateURL* template_url,
+ EditSearchEngineControllerDelegate* delegate,
+ Profile* profile) {
+ EditSearchEngineDialog* contents =
+ new EditSearchEngineDialog(template_url, delegate, profile);
+ // Window interprets an empty rectangle as needing to query the content for
+ // the size as well as centering relative to the parent.
+ views::Window::CreateChromeWindow(parent, gfx::Rect(), contents);
+ contents->window()->Show();
+ contents->GetDialogClientView()->UpdateDialogButtons();
+ contents->title_tf_->SelectAll();
+ contents->title_tf_->RequestFocus();
+}
+
+bool EditSearchEngineDialog::IsModal() const {
+ return true;
+}
+
+std::wstring EditSearchEngineDialog::GetWindowTitle() const {
+ return UTF16ToWide(l10n_util::GetStringUTF16(controller_->template_url() ?
+ IDS_SEARCH_ENGINES_EDITOR_EDIT_WINDOW_TITLE :
+ IDS_SEARCH_ENGINES_EDITOR_NEW_WINDOW_TITLE));
+}
+
+bool EditSearchEngineDialog::IsDialogButtonEnabled(
+ MessageBoxFlags::DialogButton button) const {
+ if (button == MessageBoxFlags::DIALOGBUTTON_OK) {
+ return (controller_->IsKeywordValid(WideToUTF16(keyword_tf_->text())) &&
+ controller_->IsTitleValid(WideToUTF16(title_tf_->text())) &&
+ controller_->IsURLValid(WideToUTF8(url_tf_->text())));
+ }
+ return true;
+}
+
+bool EditSearchEngineDialog::Cancel() {
+ controller_->CleanUpCancelledAdd();
+ return true;
+}
+
+bool EditSearchEngineDialog::Accept() {
+ controller_->AcceptAddOrEdit(WideToUTF16(title_tf_->text()),
+ WideToUTF16(keyword_tf_->text()),
+ WideToUTF8(url_tf_->text()));
+ return true;
+}
+
+views::View* EditSearchEngineDialog::GetContentsView() {
+ return this;
+}
+
+void EditSearchEngineDialog::ContentsChanged(Textfield* sender,
+ const std::wstring& new_contents) {
+ GetDialogClientView()->UpdateDialogButtons();
+ UpdateImageViews();
+}
+
+bool EditSearchEngineDialog::HandleKeyEvent(
+ Textfield* sender,
+ const views::KeyEvent& key_event) {
+ return false;
+}
+
+void EditSearchEngineDialog::Init() {
+ // Create the views we'll need.
+ if (controller_->template_url()) {
+ title_tf_ =
+ CreateTextfield(controller_->template_url()->short_name(), false);
+ keyword_tf_ = CreateTextfield(controller_->template_url()->keyword(), true);
+ url_tf_ =
+ CreateTextfield(GetDisplayURL(*controller_->template_url()), false);
+ // We don't allow users to edit prepopulate URLs. This is done as
+ // occasionally we need to update the URL of prepopulated TemplateURLs.
+ url_tf_->SetReadOnly(controller_->template_url()->prepopulate_id() != 0);
+ } else {
+ title_tf_ = CreateTextfield(std::wstring(), false);
+ keyword_tf_ = CreateTextfield(std::wstring(), true);
+ url_tf_ = CreateTextfield(std::wstring(), false);
+ }
+ title_iv_ = new ImageView();
+ keyword_iv_ = new ImageView();
+ url_iv_ = new ImageView();
+
+ UpdateImageViews();
+
+ const int related_x = views::kRelatedControlHorizontalSpacing;
+ const int related_y = views::kRelatedControlVerticalSpacing;
+ const int unrelated_y = views::kUnrelatedControlVerticalSpacing;
+
+ // View and GridLayout take care of deleting GridLayout for us.
+ GridLayout* layout = GridLayout::CreatePanel(this);
+ SetLayoutManager(layout);
+
+ // Define the structure of the layout.
+
+ // For the buttons.
+ views::ColumnSet* column_set = layout->AddColumnSet(0);
+ column_set->AddPaddingColumn(1, 0);
+ column_set->AddColumn(GridLayout::FILL, GridLayout::CENTER, 0,
+ GridLayout::USE_PREF, 0, 0);
+ column_set->AddPaddingColumn(0, related_x);
+ column_set->AddColumn(GridLayout::FILL, GridLayout::CENTER, 0,
+ GridLayout::USE_PREF, 0, 0);
+ column_set->LinkColumnSizes(1, 3, -1);
+
+ // For the Textfields.
+ column_set = layout->AddColumnSet(1);
+ column_set->AddColumn(GridLayout::LEADING, GridLayout::CENTER, 0,
+ GridLayout::USE_PREF, 0, 0);
+ column_set->AddPaddingColumn(0, related_x);
+ column_set->AddColumn(GridLayout::FILL, GridLayout::CENTER, 1,
+ GridLayout::USE_PREF, 0, 0);
+ column_set->AddPaddingColumn(0, related_x);
+ column_set->AddColumn(GridLayout::CENTER, GridLayout::CENTER, 0,
+ GridLayout::USE_PREF, 0, 0);
+
+ // For the description.
+ column_set = layout->AddColumnSet(2);
+ column_set->AddColumn(GridLayout::FILL, GridLayout::CENTER, 0,
+ GridLayout::USE_PREF, 0, 0);
+
+ // Add the contents.
+ layout->StartRow(0, 1);
+ layout->AddView(CreateLabel(IDS_SEARCH_ENGINES_EDITOR_DESCRIPTION_LABEL));
+ layout->AddView(title_tf_);
+ layout->AddView(title_iv_);
+
+ layout->StartRowWithPadding(0, 1, 0, related_y);
+ layout->AddView(CreateLabel(IDS_SEARCH_ENGINES_EDITOR_KEYWORD_LABEL));
+ layout->AddView(keyword_tf_);
+ layout->AddView(keyword_iv_);
+
+ layout->StartRowWithPadding(0, 1, 0, related_y);
+ layout->AddView(CreateLabel(IDS_SEARCH_ENGINES_EDITOR_URL_LABEL));
+ layout->AddView(url_tf_);
+ layout->AddView(url_iv_);
+
+ // On RTL UIs (such as Arabic and Hebrew) the description text is not
+ // displayed correctly since it contains the substring "%s". This substring
+ // is not interpreted by the Unicode BiDi algorithm as an LTR string and
+ // therefore the end result is that the following right to left text is
+ // displayed: ".three two s% one" (where 'one', 'two', etc. are words in
+ // Hebrew).
+ //
+ // In order to fix this problem we transform the substring "%s" so that it
+ // is displayed correctly when rendered in an RTL context.
+ layout->StartRowWithPadding(0, 2, 0, unrelated_y);
+ std::wstring description = UTF16ToWide(l10n_util::GetStringUTF16(
+ IDS_SEARCH_ENGINES_EDITOR_URL_DESCRIPTION_LABEL));
+ if (base::i18n::IsRTL()) {
+ const std::wstring reversed_percent(L"s%");
+ std::wstring::size_type percent_index =
+ description.find(L"%s", static_cast<std::wstring::size_type>(0));
+ if (percent_index != std::wstring::npos)
+ description.replace(percent_index,
+ reversed_percent.length(),
+ reversed_percent);
+ }
+
+ views::Label* description_label = new views::Label(description);
+ description_label->SetHorizontalAlignment(views::Label::ALIGN_LEFT);
+ layout->AddView(description_label);
+
+ layout->AddPaddingRow(0, related_y);
+}
+
+views::Label* EditSearchEngineDialog::CreateLabel(int message_id) {
+ views::Label* label =
+ new views::Label(UTF16ToWide(l10n_util::GetStringUTF16(message_id)));
+ label->SetHorizontalAlignment(views::Label::ALIGN_LEFT);
+ return label;
+}
+
+Textfield* EditSearchEngineDialog::CreateTextfield(const std::wstring& text,
+ bool lowercase) {
+ Textfield* text_field = new Textfield(
+ lowercase ? Textfield::STYLE_LOWERCASE : Textfield::STYLE_DEFAULT);
+ text_field->SetText(text);
+ text_field->SetController(this);
+ return text_field;
+}
+
+void EditSearchEngineDialog::UpdateImageViews() {
+ UpdateImageView(keyword_iv_,
+ controller_->IsKeywordValid(WideToUTF16(keyword_tf_->text())),
+ IDS_SEARCH_ENGINES_INVALID_KEYWORD_TT);
+ UpdateImageView(url_iv_, controller_->IsURLValid(WideToUTF8(url_tf_->text())),
+ IDS_SEARCH_ENGINES_INVALID_URL_TT);
+ UpdateImageView(title_iv_,
+ controller_->IsTitleValid(WideToUTF16(title_tf_->text())),
+ IDS_SEARCH_ENGINES_INVALID_TITLE_TT);
+}
+
+void EditSearchEngineDialog::UpdateImageView(ImageView* image_view,
+ bool is_valid,
+ int invalid_message_id) {
+ if (is_valid) {
+ image_view->SetTooltipText(std::wstring());
+ image_view->SetImage(
+ ResourceBundle::GetSharedInstance().GetBitmapNamed(
+ IDR_INPUT_GOOD));
+ } else {
+ image_view->SetTooltipText(
+ UTF16ToWide(l10n_util::GetStringUTF16(invalid_message_id)));
+ image_view->SetImage(
+ ResourceBundle::GetSharedInstance().GetBitmapNamed(
+ IDR_INPUT_ALERT));
+ }
+}
diff --git a/chrome/browser/ui/views/edit_search_engine_dialog.h b/chrome/browser/ui/views/edit_search_engine_dialog.h
new file mode 100644
index 0000000..12ae190
--- /dev/null
+++ b/chrome/browser/ui/views/edit_search_engine_dialog.h
@@ -0,0 +1,105 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// EditSearchEngineDialog provides text fields for editing a keyword: the title,
+// url and actual keyword. It is used by the KeywordEditorView of the Options
+// dialog, and also on its own to confirm the addition of a keyword added by
+// the ExternalJSObject via the RenderView.
+
+#ifndef CHROME_BROWSER_UI_VIEWS_EDIT_SEARCH_ENGINE_DIALOG_H_
+#define CHROME_BROWSER_UI_VIEWS_EDIT_SEARCH_ENGINE_DIALOG_H_
+#pragma once
+
+#include <windows.h>
+
+#include "views/controls/textfield/textfield_controller.h"
+#include "views/window/dialog_delegate.h"
+
+namespace views {
+class Label;
+class ImageView;
+class Window;
+}
+
+class EditSearchEngineController;
+class EditSearchEngineControllerDelegate;
+class Profile;
+class TemplateURL;
+class TemplateURLModel;
+
+class EditSearchEngineDialog : public views::View,
+ public views::TextfieldController,
+ public views::DialogDelegate {
+ public:
+ // The |template_url| and/or |delegate| may be NULL.
+ EditSearchEngineDialog(const TemplateURL* template_url,
+ EditSearchEngineControllerDelegate* delegate,
+ Profile* profile);
+ virtual ~EditSearchEngineDialog() {}
+
+ // Shows the dialog to the user.
+ static void Show(gfx::NativeWindow parent,
+ const TemplateURL* template_url,
+ EditSearchEngineControllerDelegate* delegate,
+ Profile* profile);
+
+ // views::DialogDelegate:
+ virtual bool IsModal() const;
+ virtual std::wstring GetWindowTitle() const;
+ virtual bool IsDialogButtonEnabled(
+ MessageBoxFlags::DialogButton button) const;
+ virtual bool Cancel();
+ virtual bool Accept();
+ virtual views::View* GetContentsView();
+
+ // views::TextfieldController:
+ // Updates whether the user can accept the dialog as well as updating image
+ // views showing whether value is valid.
+ virtual void ContentsChanged(views::Textfield* sender,
+ const std::wstring& new_contents);
+ virtual bool HandleKeyEvent(views::Textfield* sender,
+ const views::KeyEvent& key_event);
+
+ private:
+ void Init();
+
+ // Create a Label containing the text with the specified message id.
+ views::Label* CreateLabel(int message_id);
+
+ // Creates a text field with the specified text. If |lowercase| is true, the
+ // Textfield is configured to map all input to lower case.
+ views::Textfield* CreateTextfield(const std::wstring& text, bool lowercase);
+
+ // Invokes UpdateImageView for each of the images views.
+ void UpdateImageViews();
+
+ // Updates the tooltip and image of the image view based on is_valid. If
+ // is_valid is false the tooltip of the image view is set to the message with
+ // id invalid_message_id, otherwise the tooltip is set to the empty text.
+ void UpdateImageView(views::ImageView* image_view,
+ bool is_valid,
+ int invalid_message_id);
+
+ // Used to parent window to. May be NULL or an invalid window.
+ HWND parent_;
+
+ // View containing the buttons, text fields ...
+ views::View* view_;
+
+ // Text fields.
+ views::Textfield* title_tf_;
+ views::Textfield* keyword_tf_;
+ views::Textfield* url_tf_;
+
+ // Shows error images.
+ views::ImageView* title_iv_;
+ views::ImageView* keyword_iv_;
+ views::ImageView* url_iv_;
+
+ scoped_ptr<EditSearchEngineController> controller_;
+
+ DISALLOW_COPY_AND_ASSIGN(EditSearchEngineDialog);
+};
+
+#endif // CHROME_BROWSER_UI_VIEWS_EDIT_SEARCH_ENGINE_DIALOG_H_
diff --git a/chrome/browser/ui/views/keyword_editor_view.cc b/chrome/browser/ui/views/keyword_editor_view.cc
new file mode 100644
index 0000000..d8125e1
--- /dev/null
+++ b/chrome/browser/ui/views/keyword_editor_view.cc
@@ -0,0 +1,308 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/views/keyword_editor_view.h"
+
+#include <string>
+#include <vector>
+
+#include "base/stl_util-inl.h"
+#include "base/string_util.h"
+#include "base/utf_string_conversions.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/search_engines/template_url.h"
+#include "chrome/browser/search_engines/template_url_model.h"
+#include "chrome/browser/ui/search_engines/template_url_table_model.h"
+#include "chrome/browser/ui/views/browser_dialogs.h"
+#include "chrome/browser/ui/views/first_run_search_engine_view.h"
+#include "chrome/common/pref_names.h"
+#include "googleurl/src/gurl.h"
+#include "grit/generated_resources.h"
+#include "grit/locale_settings.h"
+#include "grit/theme_resources.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/gfx/point.h"
+#include "views/background.h"
+#include "views/controls/button/native_button.h"
+#include "views/controls/table/table_view.h"
+#include "views/controls/textfield/textfield.h"
+#include "views/layout/grid_layout.h"
+#include "views/layout/layout_constants.h"
+#include "views/widget/widget.h"
+#include "views/window/dialog_delegate.h"
+#include "views/window/window.h"
+
+using views::GridLayout;
+using views::NativeButton;
+
+namespace browser {
+
+// Declared in browser_dialogs.h so others don't have to depend on our header.
+void ShowKeywordEditorView(Profile* profile) {
+ KeywordEditorView::Show(profile);
+}
+
+} // namespace browser
+
+
+// KeywordEditorView ----------------------------------------------------------
+
+// If non-null, there is an open editor and this is the window it is contained
+// in.
+static views::Window* open_window = NULL;
+
+// static
+// The typical case for showing a KeywordEditorView does not involve an
+// observer, so use this function signature generally.
+void KeywordEditorView::Show(Profile* profile) {
+ KeywordEditorView::ShowAndObserve(profile, NULL);
+}
+
+// static
+void KeywordEditorView::ShowAndObserve(Profile* profile,
+ SearchEngineSelectionObserver* observer) {
+ // If this panel is opened from an Incognito window, closing that window can
+ // leave this with a stale pointer. Use the original profile instead.
+ // See http://crbug.com/23359.
+ profile = profile ->GetOriginalProfile();
+ if (!profile->GetTemplateURLModel())
+ return;
+
+ if (open_window != NULL)
+ open_window->CloseWindow();
+ DCHECK(!open_window);
+
+ // Both of these will be deleted when the dialog closes.
+ KeywordEditorView* keyword_editor = new KeywordEditorView(profile, observer);
+
+ // Initialize the UI. By passing in an empty rect KeywordEditorView is
+ // queried for its preferred size.
+ open_window = views::Window::CreateChromeWindow(NULL, gfx::Rect(),
+ keyword_editor);
+
+ open_window->Show();
+}
+
+KeywordEditorView::KeywordEditorView(Profile* profile,
+ SearchEngineSelectionObserver* observer)
+ : profile_(profile),
+ observer_(observer),
+ controller_(new KeywordEditorController(profile)),
+ default_chosen_(false) {
+ DCHECK(controller_->url_model());
+ controller_->url_model()->AddObserver(this);
+ Init();
+}
+
+KeywordEditorView::~KeywordEditorView() {
+ table_view_->SetModel(NULL);
+ controller_->url_model()->RemoveObserver(this);
+}
+
+void KeywordEditorView::OnEditedKeyword(const TemplateURL* template_url,
+ const string16& title,
+ const string16& keyword,
+ const std::string& url) {
+ if (template_url) {
+ controller_->ModifyTemplateURL(template_url, title, keyword, url);
+
+ // Force the make default button to update.
+ OnSelectionChanged();
+ } else {
+ table_view_->Select(controller_->AddTemplateURL(title, keyword, url));
+ }
+}
+
+
+gfx::Size KeywordEditorView::GetPreferredSize() {
+ return gfx::Size(views::Window::GetLocalizedContentsSize(
+ IDS_SEARCHENGINES_DIALOG_WIDTH_CHARS,
+ IDS_SEARCHENGINES_DIALOG_HEIGHT_LINES));
+}
+
+bool KeywordEditorView::CanResize() const {
+ return true;
+}
+
+std::wstring KeywordEditorView::GetWindowTitle() const {
+ return UTF16ToWide(
+ l10n_util::GetStringUTF16(IDS_SEARCH_ENGINES_EDITOR_WINDOW_TITLE));
+}
+
+std::wstring KeywordEditorView::GetWindowName() const {
+ return UTF8ToWide(prefs::kKeywordEditorWindowPlacement);
+}
+
+int KeywordEditorView::GetDialogButtons() const {
+ return MessageBoxFlags::DIALOGBUTTON_CANCEL;
+}
+
+bool KeywordEditorView::Accept() {
+ open_window = NULL;
+ return true;
+}
+
+bool KeywordEditorView::Cancel() {
+ open_window = NULL;
+ return true;
+}
+
+views::View* KeywordEditorView::GetContentsView() {
+ return this;
+}
+
+void KeywordEditorView::Init() {
+ std::vector<ui::TableColumn> columns;
+ columns.push_back(
+ ui::TableColumn(IDS_SEARCH_ENGINES_EDITOR_DESCRIPTION_COLUMN,
+ ui::TableColumn::LEFT, -1, .75));
+ columns.back().sortable = true;
+ columns.push_back(
+ ui::TableColumn(IDS_SEARCH_ENGINES_EDITOR_KEYWORD_COLUMN,
+ ui::TableColumn::LEFT, -1, .25));
+ columns.back().sortable = true;
+ table_view_ = new views::TableView(controller_->table_model(), columns,
+ views::ICON_AND_TEXT, false, true, true);
+ table_view_->SetObserver(this);
+
+ add_button_ = new views::NativeButton(this, UTF16ToWide(
+ l10n_util::GetStringUTF16(IDS_SEARCH_ENGINES_EDITOR_NEW_BUTTON)));
+ add_button_->SetEnabled(controller_->loaded());
+ add_button_->AddAccelerator(
+ views::Accelerator(ui::VKEY_A, false, false, true));
+ add_button_->SetAccessibleKeyboardShortcut(L"A");
+
+ edit_button_ = new views::NativeButton(this, UTF16ToWide(
+ l10n_util::GetStringUTF16(IDS_SEARCH_ENGINES_EDITOR_EDIT_BUTTON)));
+ edit_button_->SetEnabled(false);
+
+ remove_button_ = new views::NativeButton(this, UTF16ToWide(
+ l10n_util::GetStringUTF16(IDS_SEARCH_ENGINES_EDITOR_REMOVE_BUTTON)));
+ remove_button_->SetEnabled(false);
+ remove_button_->AddAccelerator(
+ views::Accelerator(ui::VKEY_R, false, false, true));
+ remove_button_->SetAccessibleKeyboardShortcut(L"R");
+
+ make_default_button_ = new views::NativeButton(
+ this,
+ UTF16ToWide(l10n_util::GetStringUTF16(
+ IDS_SEARCH_ENGINES_EDITOR_MAKE_DEFAULT_BUTTON)));
+ make_default_button_->SetEnabled(false);
+
+ InitLayoutManager();
+}
+
+void KeywordEditorView::InitLayoutManager() {
+ const int related_x = views::kRelatedControlHorizontalSpacing;
+ const int related_y = views::kRelatedControlVerticalSpacing;
+ const int unrelated_y = views::kUnrelatedControlVerticalSpacing;
+
+ GridLayout* contents_layout = GridLayout::CreatePanel(this);
+ SetLayoutManager(contents_layout);
+
+ // For the table and buttons.
+ views::ColumnSet* column_set = contents_layout->AddColumnSet(0);
+ column_set->AddColumn(GridLayout::FILL, GridLayout::FILL, 1,
+ GridLayout::USE_PREF, 0, 0);
+ column_set->AddPaddingColumn(0, related_x);
+ column_set->AddColumn(GridLayout::FILL, GridLayout::CENTER, 0,
+ GridLayout::USE_PREF, 0, 0);
+
+ contents_layout->StartRow(0, 0);
+ contents_layout->AddView(table_view_, 1, 8, GridLayout::FILL,
+ GridLayout::FILL);
+ contents_layout->AddView(add_button_);
+
+ contents_layout->StartRowWithPadding(0, 0, 0, related_y);
+ contents_layout->SkipColumns(2);
+ contents_layout->AddView(edit_button_);
+
+ contents_layout->StartRowWithPadding(0, 0, 0, related_y);
+ contents_layout->SkipColumns(2);
+ contents_layout->AddView(remove_button_);
+
+ contents_layout->StartRowWithPadding(0, 0, 0, related_y);
+ contents_layout->SkipColumns(2);
+ contents_layout->AddView(make_default_button_);
+
+ contents_layout->AddPaddingRow(1, 0);
+}
+
+void KeywordEditorView::OnSelectionChanged() {
+ bool only_one_url_left =
+ controller_->url_model()->GetTemplateURLs().size() == 1;
+ if (table_view_->SelectedRowCount() == 1) {
+ const TemplateURL* selected_url =
+ controller_->GetTemplateURL(table_view_->FirstSelectedRow());
+ edit_button_->SetEnabled(controller_->CanEdit(selected_url));
+ make_default_button_->SetEnabled(controller_->CanMakeDefault(selected_url));
+ remove_button_->SetEnabled(!only_one_url_left &&
+ controller_->CanRemove(selected_url));
+ } else {
+ edit_button_->SetEnabled(false);
+ make_default_button_->SetEnabled(false);
+ for (views::TableView::iterator i = table_view_->SelectionBegin();
+ i != table_view_->SelectionEnd(); ++i) {
+ const TemplateURL* selected_url = controller_->GetTemplateURL(*i);
+ if (!controller_->CanRemove(selected_url)) {
+ remove_button_->SetEnabled(false);
+ return;
+ }
+ }
+ remove_button_->SetEnabled(!only_one_url_left);
+ }
+}
+
+void KeywordEditorView::OnDoubleClick() {
+ if (edit_button_->IsEnabled()) {
+ DWORD pos = GetMessagePos();
+ gfx::Point cursor_point(pos);
+ views::MouseEvent event(ui::ET_MOUSE_RELEASED,
+ cursor_point.x(), cursor_point.y(),
+ ui::EF_LEFT_BUTTON_DOWN);
+ ButtonPressed(edit_button_, event);
+ }
+}
+
+void KeywordEditorView::ButtonPressed(
+ views::Button* sender, const views::Event& event) {
+ if (sender == add_button_) {
+ browser::EditSearchEngine(GetWindow()->GetNativeWindow(), NULL, this,
+ profile_);
+ } else if (sender == remove_button_) {
+ DCHECK_GT(table_view_->SelectedRowCount(), 0);
+ int last_view_row = -1;
+ for (views::TableView::iterator i = table_view_->SelectionBegin();
+ i != table_view_->SelectionEnd(); ++i) {
+ last_view_row = table_view_->ModelToView(*i);
+ controller_->RemoveTemplateURL(*i);
+ }
+ if (last_view_row >= controller_->table_model()->RowCount())
+ last_view_row = controller_->table_model()->RowCount() - 1;
+ if (last_view_row >= 0)
+ table_view_->Select(table_view_->ViewToModel(last_view_row));
+ } else if (sender == edit_button_) {
+ const int selected_row = table_view_->FirstSelectedRow();
+ const TemplateURL* template_url =
+ controller_->GetTemplateURL(selected_row);
+ browser::EditSearchEngine(GetWindow()->GetNativeWindow(), template_url,
+ this, profile_);
+ } else if (sender == make_default_button_) {
+ MakeDefaultTemplateURL();
+ } else {
+ NOTREACHED();
+ }
+}
+
+void KeywordEditorView::OnTemplateURLModelChanged() {
+ add_button_->SetEnabled(controller_->loaded());
+}
+
+void KeywordEditorView::MakeDefaultTemplateURL() {
+ int new_index =
+ controller_->MakeDefaultTemplateURL(table_view_->FirstSelectedRow());
+ if (new_index >= 0)
+ table_view_->Select(new_index);
+ default_chosen_ = true;
+}
diff --git a/chrome/browser/ui/views/keyword_editor_view.h b/chrome/browser/ui/views/keyword_editor_view.h
new file mode 100644
index 0000000..746119e
--- /dev/null
+++ b/chrome/browser/ui/views/keyword_editor_view.h
@@ -0,0 +1,117 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_VIEWS_KEYWORD_EDITOR_VIEW_H_
+#define CHROME_BROWSER_UI_VIEWS_KEYWORD_EDITOR_VIEW_H_
+#pragma once
+
+#include <Windows.h>
+
+#include "base/string16.h"
+#include "chrome/browser/search_engines/template_url_model_observer.h"
+#include "chrome/browser/ui/search_engines/edit_search_engine_controller.h"
+#include "chrome/browser/ui/search_engines/keyword_editor_controller.h"
+#include "views/controls/button/button.h"
+#include "views/controls/table/table_view_observer.h"
+#include "views/view.h"
+#include "views/window/dialog_delegate.h"
+
+namespace views {
+class Label;
+class NativeButton;
+}
+
+namespace {
+class BorderView;
+}
+
+class SearchEngineSelectionObserver;
+class SkBitmap;
+class TemplateURL;
+
+// KeywordEditorView allows the user to edit keywords.
+
+class KeywordEditorView : public views::View,
+ public views::TableViewObserver,
+ public views::ButtonListener,
+ public TemplateURLModelObserver,
+ public views::DialogDelegate,
+ public EditSearchEngineControllerDelegate {
+ public:
+ // Shows the KeywordEditorView for the specified profile. If there is a
+ // KeywordEditorView already open, it is closed and a new one is shown.
+ static void Show(Profile* profile);
+
+ // Shows the KeywordEditorView for the specified profile, and passes in
+ // an observer to be called back on view close.
+ static void ShowAndObserve(Profile* profile,
+ SearchEngineSelectionObserver* observer);
+
+ KeywordEditorView(Profile* profile,
+ SearchEngineSelectionObserver* observer);
+
+ virtual ~KeywordEditorView();
+
+ // Overridden from EditSearchEngineControllerDelegate.
+ // Calls AddTemplateURL or ModifyTemplateURL as appropriate.
+ virtual void OnEditedKeyword(const TemplateURL* template_url,
+ const string16& title,
+ const string16& keyword,
+ const std::string& url);
+
+ // Overridden to invoke Layout.
+ virtual gfx::Size GetPreferredSize();
+
+ // views::DialogDelegate methods:
+ virtual bool CanResize() const;
+ virtual std::wstring GetWindowTitle() const;
+ virtual std::wstring GetWindowName() const;
+ virtual int GetDialogButtons() const;
+ virtual bool Accept();
+ virtual bool Cancel();
+ virtual views::View* GetContentsView();
+
+ private:
+ void Init();
+
+ // Creates the layout and adds the views to it.
+ void InitLayoutManager();
+
+ // TableViewObserver method. Updates buttons contingent on the selection.
+ virtual void OnSelectionChanged();
+ // Edits the selected item.
+ virtual void OnDoubleClick();
+
+ // Button::ButtonListener method.
+ virtual void ButtonPressed(views::Button* sender, const views::Event& event);
+
+ // TemplateURLModelObserver notification.
+ virtual void OnTemplateURLModelChanged();
+
+ // Toggles whether the selected keyword is the default search provider.
+ void MakeDefaultTemplateURL();
+
+ // The profile.
+ Profile* profile_;
+
+ // Observer gets a callback when the KeywordEditorView closes.
+ SearchEngineSelectionObserver* observer_;
+
+ scoped_ptr<KeywordEditorController> controller_;
+
+ // True if the user has set a default search engine in this dialog.
+ bool default_chosen_;
+
+ // All the views are added as children, so that we don't need to delete
+ // them directly.
+ views::TableView* table_view_;
+ views::NativeButton* add_button_;
+ views::NativeButton* edit_button_;
+ views::NativeButton* remove_button_;
+ views::NativeButton* make_default_button_;
+
+ DISALLOW_COPY_AND_ASSIGN(KeywordEditorView);
+};
+
+#endif // CHROME_BROWSER_UI_VIEWS_KEYWORD_EDITOR_VIEW_H_
diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi
index 8e5a78b..d26f309 100644
--- a/chrome/chrome_browser.gypi
+++ b/chrome/chrome_browser.gypi
@@ -2532,6 +2532,8 @@
'browser/ui/gtk/certificate_viewer.h',
'browser/ui/gtk/chrome_gtk_frame.cc',
'browser/ui/gtk/chrome_gtk_frame.h',
+ 'browser/ui/gtk/clear_browsing_data_dialog_gtk.cc',
+ 'browser/ui/gtk/clear_browsing_data_dialog_gtk.h',
'browser/ui/gtk/collected_cookies_gtk.cc',
'browser/ui/gtk/collected_cookies_gtk.h',
'browser/ui/gtk/constrained_html_delegate_gtk.cc',
@@ -2631,6 +2633,8 @@
'browser/ui/gtk/instant_confirm_dialog_gtk.h',
'browser/ui/gtk/js_modal_dialog_gtk.cc',
'browser/ui/gtk/js_modal_dialog_gtk.h',
+ 'browser/ui/gtk/keyword_editor_view.cc',
+ 'browser/ui/gtk/keyword_editor_view.h',
'browser/ui/gtk/location_bar_view_gtk.cc',
'browser/ui/gtk/location_bar_view_gtk.h',
'browser/ui/gtk/menu_bar_helper.cc',
@@ -2960,6 +2964,8 @@
'browser/ui/views/js_modal_dialog_views.h',
'browser/ui/views/keyboard_overlay_delegate.cc',
'browser/ui/views/keyboard_overlay_delegate.h',
+ 'browser/ui/views/keyword_editor_view.cc',
+ 'browser/ui/views/keyword_editor_view.h',
'browser/ui/views/list_background.h',
'browser/ui/views/local_storage_info_view.cc',
'browser/ui/views/local_storage_info_view.h',
@@ -3701,6 +3707,8 @@
['include', '^browser/ui/gtk/certificate_viewer.h'],
['include', '^browser/ui/gtk/chrome_gtk_frame.cc'],
['include', '^browser/ui/gtk/chrome_gtk_frame.h'],
+ ['include', '^browser/ui/gtk/clear_browsing_data_dialog_gtk.cc'],
+ ['include', '^browser/ui/gtk/clear_browsing_data_dialog_gtk.h'],
['include', '^browser/ui/gtk/collected_cookies_gtk.cc'],
['include', '^browser/ui/gtk/collected_cookies_gtk.h'],
['include', '^browser/ui/gtk/constrained_window_gtk.cc'],
@@ -3735,6 +3743,8 @@
['include', '^browser/ui/gtk/importer/import_lock_dialog_gtk.h'],
['include', '^browser/ui/gtk/importer/import_progress_dialog_gtk.cc'],
['include', '^browser/ui/gtk/importer/import_progress_dialog_gtk.h'],
+ ['include', '^browser/ui/gtk/keyword_editor_view.cc'],
+ ['include', '^browser/ui/gtk/keyword_editor_view.h'],
['include', '^browser/ui/gtk/nine_box.cc'],
['include', '^browser/ui/gtk/nine_box.h'],
['include', '^browser/ui/gtk/owned_widget_gtk.cc'],
diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi
index bbbbc9c..fcf5ba0 100644
--- a/chrome/chrome_tests.gypi
+++ b/chrome/chrome_tests.gypi
@@ -1304,6 +1304,7 @@
'browser/ui/gtk/gtk_chrome_shrinkable_hbox_unittest.cc',
'browser/ui/gtk/gtk_expanded_container_unittest.cc',
'browser/ui/gtk/gtk_theme_provider_unittest.cc',
+ 'browser/ui/gtk/keyword_editor_view_unittest.cc',
'browser/ui/gtk/reload_button_gtk_unittest.cc',
'browser/ui/gtk/status_icons/status_tray_gtk_unittest.cc',
'browser/ui/gtk/tabs/tab_renderer_gtk_unittest.cc',