summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorsky@chromium.org <sky@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-07-14 03:27:11 +0000
committersky@chromium.org <sky@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-07-14 03:27:11 +0000
commit548adcb9cb9343fe542f82cc35ffaa8f8279dfb6 (patch)
treeb32f0079645d72525d009e718751b58ab8b02bd5
parente8c11e4801e7f4ca0c5f607d7bb489e3b1098a0c (diff)
downloadchromium_src-548adcb9cb9343fe542f82cc35ffaa8f8279dfb6.zip
chromium_src-548adcb9cb9343fe542f82cc35ffaa8f8279dfb6.tar.gz
chromium_src-548adcb9cb9343fe542f82cc35ffaa8f8279dfb6.tar.bz2
Makes the auto fill dialogs match the mocks.
BUG=44620 46634 TEST=Make sure the auto fill dialogs work correctly. Review URL: http://codereview.chromium.org/2975003 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@52274 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--chrome/browser/autofill/auto_fill_editor_gtk.cc810
-rw-r--r--chrome/browser/autofill/autofill_dialog.h10
-rw-r--r--chrome/browser/autofill/autofill_dialog_gtk.cc1239
-rw-r--r--chrome/browser/autofill/credit_card.cc3
-rw-r--r--chrome/browser/autofill/personal_data_manager.cc39
-rw-r--r--chrome/browser/autofill/personal_data_manager.h10
-rw-r--r--chrome/browser/gtk/keyword_editor_view.cc8
-rw-r--r--chrome/browser/gtk/keyword_editor_view.h2
-rw-r--r--chrome/browser/gtk/options/content_page_gtk.cc62
-rw-r--r--chrome/browser/gtk/options/content_page_gtk.h7
-rw-r--r--chrome/chrome_browser.gypi1
11 files changed, 1302 insertions, 889 deletions
diff --git a/chrome/browser/autofill/auto_fill_editor_gtk.cc b/chrome/browser/autofill/auto_fill_editor_gtk.cc
new file mode 100644
index 0000000..dda15c9
--- /dev/null
+++ b/chrome/browser/autofill/auto_fill_editor_gtk.cc
@@ -0,0 +1,810 @@
+// Copyright (c) 2010 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/autofill/autofill_dialog.h"
+
+#include <gtk/gtk.h>
+
+#include "app/gtk_signal.h"
+#include "app/l10n_util.h"
+#include "app/resource_bundle.h"
+#include "base/message_loop.h"
+#include "base/string_util.h"
+#include "base/task.h"
+#include "base/time.h"
+#include "base/utf_string_conversions.h"
+#include "chrome/browser/autofill/personal_data_manager.h"
+#include "chrome/browser/autofill/phone_number.h"
+#include "chrome/browser/gtk/gtk_util.h"
+#include "chrome/browser/profile.h"
+#include "grit/app_resources.h"
+#include "grit/generated_resources.h"
+#include "grit/locale_settings.h"
+#include "grit/theme_resources.h"
+
+namespace {
+
+// Creates a label whose text is set from the resource id |label_id|.
+GtkWidget* CreateLabel(int label_id) {
+ GtkWidget* label = gtk_label_new(l10n_util::GetStringUTF8(label_id).c_str());
+ gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
+ return label;
+}
+
+// Sets the text of |entry| to the value of the field |type| from |profile|.
+void SetEntryText(GtkWidget* entry, FormGroup* profile, _FieldType type) {
+ gtk_entry_set_text(
+ GTK_ENTRY(entry),
+ UTF16ToUTF8(profile->GetFieldText(AutoFillType(type))).c_str());
+}
+
+// Returns the current value of |entry|.
+string16 GetEntryText(GtkWidget* entry) {
+ return UTF8ToUTF16(gtk_entry_get_text(GTK_ENTRY(entry)));
+}
+
+// Sets |form|'s field of type |type| to the text in |entry|.
+void SetFormValue(GtkWidget* entry, FormGroup* form, _FieldType type) {
+ form->SetInfo(AutoFillType(type), GetEntryText(entry));
+}
+
+// Sets the number of characters to display in |combobox| to |width|.
+void SetComboBoxCellRendererCharWidth(GtkWidget* combobox, int width) {
+ GList* cells = gtk_cell_layout_get_cells(GTK_CELL_LAYOUT(combobox));
+ DCHECK(g_list_length(cells) > 0);
+ GtkCellRendererText* renderer =
+ GTK_CELL_RENDERER_TEXT(g_list_first(cells)->data);
+ g_object_set(G_OBJECT(renderer), "width-chars", width, NULL);
+ g_list_free(cells);
+}
+
+// TableBuilder ----------------------------------------------------------------
+
+// A convenience used in populating a GtkTable. To use it create a TableBuilder
+// and repeatedly invoke AddWidget. TableBuilder keeps track of the current
+// row/col. You can increment the row explicitly by invoking |increment_row|.
+class TableBuilder {
+ public:
+ TableBuilder(int row_count, int col_count);
+ ~TableBuilder();
+
+ GtkWidget* table() { return table_; }
+
+ void increment_row() {
+ row_++;
+ col_ = 0;
+ }
+
+ GtkWidget* AddWidget(GtkWidget* widget, int col_span);
+
+ void set_x_padding(int x) { x_padding_ = x; }
+ void set_y_padding(int y) { y_padding_ = y; }
+
+ void reset_padding() {
+ x_padding_ = y_padding_ = gtk_util::kControlSpacing / 2;
+ }
+
+ private:
+ GtkWidget* table_;
+
+ // Number of rows/columns.
+ const int row_count_;
+ const int col_count_;
+
+ // Current row/column.
+ int row_;
+ int col_;
+
+ // Padding.
+ int x_padding_;
+ int y_padding_;
+
+ DISALLOW_COPY_AND_ASSIGN(TableBuilder);
+};
+
+TableBuilder::TableBuilder(int row_count, int col_count)
+ : table_(gtk_table_new(row_count, col_count, FALSE)),
+ row_count_(row_count),
+ col_count_(col_count),
+ row_(0),
+ col_(0),
+ x_padding_(gtk_util::kControlSpacing / 2),
+ y_padding_(gtk_util::kControlSpacing / 2) {
+}
+
+TableBuilder::~TableBuilder() {
+ DCHECK_EQ(row_count_, row_);
+}
+
+GtkWidget* TableBuilder::AddWidget(GtkWidget* widget, int col_span) {
+ gtk_table_attach(
+ GTK_TABLE(table_),
+ widget,
+ col_, col_ + col_span,
+ row_, row_ + 1,
+ // The next line makes the widget expand to take up any extra horizontal
+ // space.
+ static_cast<GtkAttachOptions>(GTK_FILL | GTK_EXPAND),
+ GTK_FILL,
+ x_padding_, y_padding_);
+ col_ += col_span;
+ if (col_ == col_count_) {
+ row_++;
+ col_ = 0;
+ }
+ return widget;
+}
+
+// AutoFillProfileEditor -------------------------------------------------------
+
+// Class responsible for editing or creating an AutoFillProfile.
+class AutoFillProfileEditor {
+ public:
+ AutoFillProfileEditor(AutoFillDialogObserver* observer,
+ Profile* profile,
+ AutoFillProfile* auto_fill_profile);
+
+ private:
+ friend class DeleteTask<AutoFillProfileEditor>;
+
+ ~AutoFillProfileEditor() {}
+
+ void Init();
+
+ // Sets the values of the various widgets to |profile|.
+ void SetWidgetValues(AutoFillProfile* profile);
+
+ // Notifies the observer of the new changes. This either updates the current
+ // AutoFillProfile or creates a new one.
+ void ApplyEdits();
+
+ // Sets the various form fields in |profile| to match the values in the
+ // widgets.
+ void SetProfileValuesFromWidgets(AutoFillProfile* profile);
+
+ // Updates the image displayed by |image| depending upon whether the text in
+ // |entry| is a valid phone number.
+ void UpdatePhoneImage(GtkWidget* entry, GtkWidget* image);
+
+ // Sets the size request for the widget to match the size of the good/bad
+ // images. We must do this as the image of the phone widgets is set to null
+ // when not empty.
+ void SetPhoneSizeRequest(GtkWidget* widget);
+
+ CHROMEGTK_CALLBACK_0(AutoFillProfileEditor, void, OnDestroy);
+ CHROMEG_CALLBACK_1(AutoFillProfileEditor, void, OnResponse, GtkDialog*, gint);
+ CHROMEG_CALLBACK_0(AutoFillProfileEditor, void, OnPhoneChanged, GtkEditable*);
+ CHROMEG_CALLBACK_0(AutoFillProfileEditor, void, OnFaxChanged, GtkEditable*);
+
+ // Are we creating a new profile?
+ const bool is_new_;
+
+ // If is_new_ is false this is the unique id of the profile the user is
+ // editing.
+ const int profile_id_;
+
+ AutoFillDialogObserver* observer_;
+
+ Profile* profile_;
+
+ GtkWidget* dialog_;
+ GtkWidget* full_name_;
+ GtkWidget* company_;
+ GtkWidget* address_1_;
+ GtkWidget* address_2_;
+ GtkWidget* city_;
+ GtkWidget* state_;
+ GtkWidget* zip_;
+ GtkWidget* country_;
+ GtkWidget* phone_;
+ GtkWidget* phone_image_;
+ GtkWidget* fax_;
+ GtkWidget* fax_image_;
+ GtkWidget* email_;
+
+ DISALLOW_COPY_AND_ASSIGN(AutoFillProfileEditor);
+};
+
+AutoFillProfileEditor::AutoFillProfileEditor(
+ AutoFillDialogObserver* observer,
+ Profile* profile,
+ AutoFillProfile* auto_fill_profile)
+ : is_new_(!auto_fill_profile ? true : false),
+ profile_id_(auto_fill_profile ? auto_fill_profile->unique_id() : 0),
+ observer_(observer),
+ profile_(profile) {
+ Init();
+
+ if (auto_fill_profile)
+ SetWidgetValues(auto_fill_profile);
+
+ gtk_util::ShowDialogWithLocalizedSize(
+ dialog_,
+ IDS_AUTOFILL_DIALOG_EDIT_ADDRESS_WIDTH_CHARS,
+ IDS_AUTOFILL_DIALOG_EDIT_ADDRESS_HEIGHT_LINES,
+ true);
+ gtk_util::PresentWindow(dialog_, gtk_get_current_event_time());
+}
+
+void AutoFillProfileEditor::Init() {
+ TableBuilder main_table_builder(15, 2);
+
+ main_table_builder.AddWidget(CreateLabel(IDS_AUTOFILL_DIALOG_FULL_NAME), 2);
+ full_name_ = main_table_builder.AddWidget(gtk_entry_new(), 1);
+ main_table_builder.increment_row();
+
+ main_table_builder.AddWidget(CreateLabel(IDS_AUTOFILL_DIALOG_COMPANY_NAME),
+ 2);
+ company_ = main_table_builder.AddWidget(gtk_entry_new(), 1);
+ main_table_builder.increment_row();
+
+ main_table_builder.AddWidget(CreateLabel(IDS_AUTOFILL_DIALOG_ADDRESS_LINE_1),
+ 2);
+ address_1_ = main_table_builder.AddWidget(gtk_entry_new(), 1);
+ main_table_builder.increment_row();
+
+ main_table_builder.AddWidget(CreateLabel(IDS_AUTOFILL_DIALOG_ADDRESS_LINE_2),
+ 2);
+ address_2_ = main_table_builder.AddWidget(gtk_entry_new(), 1);
+ main_table_builder.increment_row();
+
+ TableBuilder city_state_builder(2, 3);
+ city_state_builder.AddWidget(CreateLabel(IDS_AUTOFILL_DIALOG_CITY), 1);
+ city_state_builder.AddWidget(CreateLabel(IDS_AUTOFILL_DIALOG_STATE), 1);
+ city_state_builder.AddWidget(CreateLabel(IDS_AUTOFILL_DIALOG_ZIP_CODE), 1);
+
+ city_ = city_state_builder.AddWidget(gtk_entry_new(), 1);
+ state_ = city_state_builder.AddWidget(gtk_entry_new(), 1);
+ zip_ = city_state_builder.AddWidget(gtk_entry_new(), 1);
+
+ main_table_builder.set_x_padding(0);
+ main_table_builder.set_y_padding(0);
+ main_table_builder.AddWidget(city_state_builder.table(), 2);
+ main_table_builder.reset_padding();
+
+ main_table_builder.AddWidget(CreateLabel(IDS_AUTOFILL_DIALOG_COUNTRY), 2);
+ country_ = main_table_builder.AddWidget(gtk_entry_new(), 1);
+ main_table_builder.increment_row();
+
+ main_table_builder.AddWidget(CreateLabel(IDS_AUTOFILL_DIALOG_PHONE), 1);
+ main_table_builder.AddWidget(CreateLabel(IDS_AUTOFILL_DIALOG_FAX), 1);
+
+ GtkWidget* phone_box =
+ main_table_builder.AddWidget(gtk_hbox_new(FALSE, 0), 1);
+ gtk_box_set_spacing(GTK_BOX(phone_box), gtk_util::kControlSpacing);
+ phone_ = gtk_entry_new();
+ g_signal_connect(phone_, "changed", G_CALLBACK(OnPhoneChangedThunk), this);
+ gtk_box_pack_start(GTK_BOX(phone_box), phone_, TRUE, TRUE, 0);
+ phone_image_ = gtk_image_new_from_pixbuf(NULL);
+ SetPhoneSizeRequest(phone_image_);
+ gtk_box_pack_start(GTK_BOX(phone_box), phone_image_, FALSE, FALSE, 0);
+
+ GtkWidget* fax_box =
+ main_table_builder.AddWidget(gtk_hbox_new(FALSE, 0), 1);
+ gtk_box_set_spacing(GTK_BOX(fax_box), gtk_util::kControlSpacing);
+ fax_ = gtk_entry_new();
+ g_signal_connect(fax_, "changed", G_CALLBACK(OnFaxChangedThunk), this);
+ gtk_box_pack_start(GTK_BOX(fax_box), fax_, TRUE, TRUE, 0);
+ fax_image_ = gtk_image_new_from_pixbuf(NULL);
+ SetPhoneSizeRequest(fax_image_);
+ gtk_box_pack_start(GTK_BOX(fax_box), fax_image_, FALSE, FALSE, 0);
+
+ main_table_builder.AddWidget(CreateLabel(IDS_AUTOFILL_DIALOG_EMAIL), 2);
+ email_ = main_table_builder.AddWidget(gtk_entry_new(), 1);
+ main_table_builder.increment_row();
+
+ int caption_id = is_new_ ? IDS_AUTOFILL_ADD_ADDRESS_CAPTION :
+ IDS_AUTOFILL_EDIT_ADDRESS_CAPTION;
+ dialog_ = gtk_dialog_new_with_buttons(
+ l10n_util::GetStringUTF8(caption_id).c_str(),
+ NULL,
+ static_cast<GtkDialogFlags>(GTK_DIALOG_MODAL | GTK_DIALOG_NO_SEPARATOR),
+ GTK_STOCK_APPLY,
+ GTK_RESPONSE_APPLY,
+ GTK_STOCK_CANCEL,
+ GTK_RESPONSE_CANCEL,
+ NULL);
+
+ g_signal_connect(dialog_, "response", G_CALLBACK(OnResponseThunk), this);
+ g_signal_connect(dialog_, "destroy", G_CALLBACK(OnDestroyThunk), this);
+
+ gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog_)->vbox),
+ main_table_builder.table(), TRUE, TRUE, 0);
+}
+
+void AutoFillProfileEditor::SetWidgetValues(AutoFillProfile* profile) {
+ SetEntryText(full_name_, profile, NAME_FULL);
+ SetEntryText(company_, profile, COMPANY_NAME);
+ SetEntryText(address_1_, profile, ADDRESS_HOME_LINE1);
+ SetEntryText(address_2_, profile, ADDRESS_HOME_LINE2);
+ SetEntryText(city_, profile, ADDRESS_HOME_CITY);
+ SetEntryText(state_, profile, ADDRESS_HOME_STATE);
+ SetEntryText(zip_, profile, ADDRESS_HOME_ZIP);
+ SetEntryText(country_, profile, ADDRESS_HOME_COUNTRY);
+ SetEntryText(phone_, profile, PHONE_HOME_WHOLE_NUMBER);
+ SetEntryText(fax_, profile, PHONE_FAX_WHOLE_NUMBER);
+ SetEntryText(email_, profile, EMAIL_ADDRESS);
+
+ UpdatePhoneImage(phone_, phone_image_);
+ UpdatePhoneImage(fax_, fax_image_);
+}
+
+void AutoFillProfileEditor::ApplyEdits() {
+ // Build the current set of profiles.
+ std::vector<AutoFillProfile> profiles;
+ PersonalDataManager* data_manager = profile_->GetPersonalDataManager();
+ for (std::vector<AutoFillProfile*>::const_iterator i =
+ data_manager->profiles().begin();
+ i != data_manager->profiles().end(); ++i) {
+ profiles.push_back(**i);
+ }
+ AutoFillProfile* profile = NULL;
+
+ if (!is_new_) {
+ // The user is editing an existing profile, find it.
+ for (std::vector<AutoFillProfile>::iterator i = profiles.begin();
+ i != profiles.end(); ++i) {
+ if (i->unique_id() == profile_id_) {
+ profile = &(*i);
+ break;
+ }
+ }
+ DCHECK(profile); // We should have found a profile, if not we'll end up
+ // creating one below.
+ }
+
+ if (!profile) {
+ profiles.push_back(AutoFillProfile());
+ profile = &profiles.back();
+ }
+
+ // Update the values in the profile.
+ SetProfileValuesFromWidgets(profile);
+
+ // And apply the edits.
+ observer_->OnAutoFillDialogApply(&profiles, NULL);
+}
+
+void AutoFillProfileEditor::SetProfileValuesFromWidgets(
+ AutoFillProfile* profile) {
+ SetFormValue(full_name_, profile, NAME_FULL);
+ SetFormValue(company_, profile, COMPANY_NAME);
+ SetFormValue(address_1_, profile, ADDRESS_HOME_LINE1);
+ SetFormValue(address_2_, profile, ADDRESS_HOME_LINE2);
+ SetFormValue(city_, profile, ADDRESS_HOME_CITY);
+ SetFormValue(state_, profile, ADDRESS_HOME_STATE);
+ SetFormValue(zip_, profile, ADDRESS_HOME_ZIP);
+ SetFormValue(country_, profile, ADDRESS_HOME_COUNTRY);
+ SetFormValue(email_, profile, EMAIL_ADDRESS);
+
+ string16 number, city_code, country_code;
+ PhoneNumber::ParsePhoneNumber(
+ GetEntryText(phone_), &number, &city_code, &country_code);
+ profile->SetInfo(AutoFillType(PHONE_HOME_COUNTRY_CODE), country_code);
+ profile->SetInfo(AutoFillType(PHONE_HOME_CITY_CODE), city_code);
+ profile->SetInfo(AutoFillType(PHONE_HOME_NUMBER), number);
+
+ PhoneNumber::ParsePhoneNumber(
+ GetEntryText(fax_), &number, &city_code, &country_code);
+ profile->SetInfo(AutoFillType(PHONE_FAX_COUNTRY_CODE), country_code);
+ profile->SetInfo(AutoFillType(PHONE_FAX_CITY_CODE), city_code);
+ profile->SetInfo(AutoFillType(PHONE_FAX_NUMBER), number);
+}
+
+void AutoFillProfileEditor::UpdatePhoneImage(GtkWidget* entry,
+ GtkWidget* image) {
+ string16 number, city_code, country_code;
+ string16 text(GetEntryText(entry));
+ if (text.empty()) {
+ gtk_image_set_from_pixbuf(GTK_IMAGE(image), NULL);
+ } else if (PhoneNumber::ParsePhoneNumber(text, &number, &city_code,
+ &country_code)) {
+ gtk_image_set_from_pixbuf(GTK_IMAGE(image),
+ ResourceBundle::GetSharedInstance().GetPixbufNamed(
+ IDR_INPUT_GOOD));
+ } else {
+ gtk_image_set_from_pixbuf(GTK_IMAGE(image),
+ ResourceBundle::GetSharedInstance().GetPixbufNamed(
+ IDR_INPUT_ALERT));
+ }
+}
+
+void AutoFillProfileEditor::SetPhoneSizeRequest(GtkWidget* widget) {
+ GdkPixbuf* buf =
+ ResourceBundle::GetSharedInstance().GetPixbufNamed(IDR_INPUT_ALERT);
+ gtk_widget_set_size_request(widget,
+ gdk_pixbuf_get_width(buf),
+ gdk_pixbuf_get_height(buf));
+}
+
+void AutoFillProfileEditor::OnDestroy(GtkWidget* widget) {
+ MessageLoop::current()->DeleteSoon(FROM_HERE, this);
+}
+
+void AutoFillProfileEditor::OnResponse(GtkDialog* dialog, gint response_id) {
+ if (response_id == GTK_RESPONSE_APPLY || response_id == GTK_RESPONSE_OK)
+ ApplyEdits();
+
+ if (response_id == GTK_RESPONSE_OK ||
+ response_id == GTK_RESPONSE_APPLY ||
+ response_id == GTK_RESPONSE_CANCEL ||
+ response_id == GTK_RESPONSE_DELETE_EVENT) {
+ gtk_widget_destroy(GTK_WIDGET(dialog));
+ }
+}
+
+void AutoFillProfileEditor::OnPhoneChanged(GtkEditable* editable) {
+ UpdatePhoneImage(phone_, phone_image_);
+}
+
+void AutoFillProfileEditor::OnFaxChanged(GtkEditable* editable) {
+ UpdatePhoneImage(fax_, fax_image_);
+}
+
+// AutoFillCreditCardEditor ----------------------------------------------------
+
+// Number of years shown in the expiration year combobox.
+const int kNumYears = 10;
+
+// Class responsible for editing/creating a CreditCard.
+class AutoFillCreditCardEditor {
+ public:
+ AutoFillCreditCardEditor(AutoFillDialogObserver* observer,
+ Profile* profile,
+ CreditCard* credit_card);
+
+ private:
+ friend class DeleteTask<AutoFillCreditCardEditor>;
+
+ // Types of columns in the address_store_.
+ enum ColumnTypes {
+ // Unique if of the CreditCard.
+ COL_ID,
+
+ // Title of the column.
+ COL_TITLE,
+
+ COL_COUNT
+ };
+
+ ~AutoFillCreditCardEditor() {}
+
+ // Creates the GtkListStore used to show the billing addresses.
+ GtkListStore* CreateAddressStore();
+
+ // Creates the combobox used to show the billing addresses.
+ GtkWidget* CreateAddressWidget();
+
+ // Creates the combobox for chosing the month.
+ GtkWidget* CreateMonthWidget();
+
+ // Creates the combobox for chosing the year.
+ GtkWidget* CreateYearWidget();
+
+ void Init();
+
+ // Sets the values displayed in the various widgets from |profile|.
+ void SetWidgetValues(CreditCard* profile);
+
+ // Updates the observer with the CreditCard being edited.
+ void ApplyEdits();
+
+ // Updates |card| with the values from the widgets.
+ void SetCreditCardValuesFromWidgets(CreditCard* card);
+
+ CHROMEGTK_CALLBACK_0(AutoFillCreditCardEditor, void, OnDestroy);
+ CHROMEG_CALLBACK_1(AutoFillCreditCardEditor, void, OnResponse, GtkDialog*,
+ gint);
+
+ // Are we creating a new credit card?
+ const bool is_new_;
+
+ // If is_new_ is false this is the unique id of the credit card the user is
+ // editing.
+ const int credit_card_id_;
+
+ AutoFillDialogObserver* observer_;
+
+ Profile* profile_;
+
+ // Initial year shown in expiration date year combobox.
+ int base_year_;
+
+ GtkWidget* dialog_;
+ GtkWidget* name_;
+ GtkWidget* address_;
+ GtkWidget* number_;
+ GtkWidget* month_;
+ GtkWidget* year_;
+
+ GtkListStore* address_store_;
+
+ DISALLOW_COPY_AND_ASSIGN(AutoFillCreditCardEditor);
+};
+
+AutoFillCreditCardEditor::AutoFillCreditCardEditor(
+ AutoFillDialogObserver* observer,
+ Profile* profile,
+ CreditCard* credit_card)
+ : is_new_(!credit_card ? true : false),
+ credit_card_id_(credit_card ? credit_card->unique_id() : 0),
+ observer_(observer),
+ profile_(profile),
+ base_year_(0) {
+ base::Time::Exploded exploded_time;
+ base::Time::Now().LocalExplode(&exploded_time);
+ base_year_ = exploded_time.year;
+
+ Init();
+
+ if (credit_card) {
+ SetWidgetValues(credit_card);
+ } else {
+ // We're creating a new credit card. Select a default billing address (if
+ // there are any) and select Januay of next year.
+ PersonalDataManager* data_manager = profile_->GetPersonalDataManager();
+ if (!data_manager->profiles().empty())
+ gtk_combo_box_set_active(GTK_COMBO_BOX(address_), 0);
+ gtk_combo_box_set_active(GTK_COMBO_BOX(month_), 0);
+ gtk_combo_box_set_active(GTK_COMBO_BOX(year_), 1);
+ }
+
+ gtk_util::ShowDialogWithLocalizedSize(
+ dialog_,
+ IDS_AUTOFILL_DIALOG_EDIT_CCARD_WIDTH_CHARS,
+ IDS_AUTOFILL_DIALOG_EDIT_CCARD_HEIGHT_LINES,
+ true);
+ gtk_util::PresentWindow(dialog_, gtk_get_current_event_time());
+}
+
+GtkListStore* AutoFillCreditCardEditor::CreateAddressStore() {
+ GtkListStore* store =
+ gtk_list_store_new(COL_COUNT, G_TYPE_INT, G_TYPE_STRING);
+
+ GtkTreeIter iter;
+
+ PersonalDataManager* data_manager = profile_->GetPersonalDataManager();
+ for (std::vector<AutoFillProfile*>::const_iterator i =
+ data_manager->profiles().begin();
+ i != data_manager->profiles().end(); ++i) {
+ gtk_list_store_append(store, &iter);
+ gtk_list_store_set(
+ store, &iter,
+ COL_ID, (*i)->unique_id(),
+ COL_TITLE, UTF16ToUTF8((*i)->PreviewSummary()).c_str(),
+ -1);
+ }
+ return store;
+}
+
+GtkWidget* AutoFillCreditCardEditor::CreateAddressWidget() {
+ address_store_ = CreateAddressStore();
+
+ GtkWidget* widget = gtk_combo_box_new_with_model(
+ GTK_TREE_MODEL(address_store_));
+ g_object_unref(address_store_);
+
+ GtkCellRenderer* cell = gtk_cell_renderer_text_new();
+ gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(widget), cell, TRUE);
+ gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(widget), cell,
+ "text", COL_TITLE, NULL);
+ return widget;
+}
+
+GtkWidget* AutoFillCreditCardEditor::CreateMonthWidget() {
+ GtkWidget* combobox = gtk_combo_box_new_text();
+ for (int i = 1; i <= 12; ++i) {
+ gtk_combo_box_append_text(GTK_COMBO_BOX(combobox),
+ StringPrintf("%02i", i).c_str());
+ }
+
+ SetComboBoxCellRendererCharWidth(combobox, 2);
+ return combobox;
+}
+
+GtkWidget* AutoFillCreditCardEditor::CreateYearWidget() {
+ GtkWidget* combobox = gtk_combo_box_new_text();
+ for (int i = 0; i < kNumYears; ++i) {
+ gtk_combo_box_append_text(GTK_COMBO_BOX(combobox),
+ StringPrintf("%04i", i + base_year_).c_str());
+ }
+
+ SetComboBoxCellRendererCharWidth(combobox, 4);
+ return combobox;
+}
+
+void AutoFillCreditCardEditor::Init() {
+ TableBuilder main_table_builder(8, 2);
+
+ main_table_builder.AddWidget(
+ CreateLabel(IDS_AUTOFILL_DIALOG_NAME_ON_CARD), 2);
+ name_ = main_table_builder.AddWidget(gtk_entry_new(), 2);
+
+ main_table_builder.AddWidget(CreateLabel(IDS_AUTOFILL_DIALOG_BILLING_ADDRESS),
+ 2);
+ address_ = main_table_builder.AddWidget(CreateAddressWidget(), 2);
+
+ main_table_builder.AddWidget(
+ CreateLabel(IDS_AUTOFILL_DIALOG_CREDIT_CARD_NUMBER), 2);
+ number_ = main_table_builder.AddWidget(gtk_entry_new(), 1);
+ gtk_entry_set_width_chars(GTK_ENTRY(number_), 20);
+ // Add an empty widget purely for spacing to match the mocks.
+ main_table_builder.AddWidget(gtk_hbox_new(FALSE, 0), 1);
+
+ main_table_builder.AddWidget(CreateLabel(IDS_AUTOFILL_DIALOG_EXPIRATION_DATE),
+ 2);
+ GtkWidget* box = main_table_builder.AddWidget(
+ gtk_hbox_new(FALSE, gtk_util::kControlSpacing), 1);
+ month_ = CreateMonthWidget();
+ gtk_box_pack_start(GTK_BOX(box), month_, FALSE, FALSE, 0);
+ year_ = CreateYearWidget();
+ gtk_box_pack_start(GTK_BOX(box), year_, FALSE, FALSE, 0);
+ main_table_builder.increment_row();
+
+ int caption_id = is_new_ ? IDS_AUTOFILL_ADD_CREDITCARD_CAPTION :
+ IDS_AUTOFILL_EDIT_CREDITCARD_CAPTION;
+ dialog_ = gtk_dialog_new_with_buttons(
+ l10n_util::GetStringUTF8(caption_id).c_str(),
+ NULL,
+ static_cast<GtkDialogFlags>(GTK_DIALOG_MODAL | GTK_DIALOG_NO_SEPARATOR),
+ GTK_STOCK_APPLY,
+ GTK_RESPONSE_APPLY,
+ GTK_STOCK_CANCEL,
+ GTK_RESPONSE_CANCEL,
+ NULL);
+
+ g_signal_connect(dialog_, "response", G_CALLBACK(OnResponseThunk), this);
+ g_signal_connect(dialog_, "destroy", G_CALLBACK(OnDestroyThunk), this);
+
+ gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog_)->vbox),
+ main_table_builder.table(), TRUE, TRUE, 0);
+}
+
+void AutoFillCreditCardEditor::SetWidgetValues(CreditCard* card) {
+ SetEntryText(name_, card, CREDIT_CARD_NAME);
+
+ PersonalDataManager* data_manager = profile_->GetPersonalDataManager();
+ for (std::vector<AutoFillProfile*>::const_iterator i =
+ data_manager->profiles().begin();
+ i != data_manager->profiles().end(); ++i) {
+ if ((*i)->Label() == card->billing_address()) {
+ int index = static_cast<int>(i - data_manager->profiles().begin());
+ gtk_combo_box_set_active(GTK_COMBO_BOX(address_), index);
+ break;
+ }
+ }
+
+ SetEntryText(number_, card, CREDIT_CARD_NUMBER);
+
+ int month = StringToInt(
+ UTF16ToUTF8(card->GetFieldText(AutoFillType(CREDIT_CARD_EXP_MONTH))));
+ if (month >= 1 && month <= 12) {
+ gtk_combo_box_set_active(GTK_COMBO_BOX(month_), month - 1);
+ } else {
+ gtk_combo_box_set_active(GTK_COMBO_BOX(month_), 0);
+ }
+
+ int year = StringToInt(UTF16ToUTF8(
+ card->GetFieldText(AutoFillType(CREDIT_CARD_EXP_4_DIGIT_YEAR))));
+ if (year >= base_year_ && year < base_year_ + kNumYears)
+ gtk_combo_box_set_active(GTK_COMBO_BOX(year_), year - base_year_);
+ else
+ gtk_combo_box_set_active(GTK_COMBO_BOX(year_), 0);
+}
+
+void AutoFillCreditCardEditor::ApplyEdits() {
+ // Build a vector of the current set of credit cards.
+ PersonalDataManager* data_manager = profile_->GetPersonalDataManager();
+ std::vector<CreditCard> cards;
+ for (std::vector<CreditCard*>::const_iterator i =
+ data_manager->credit_cards().begin();
+ i != data_manager->credit_cards().end(); ++i) {
+ cards.push_back(**i);
+ }
+
+ CreditCard* card = NULL;
+
+ if (!is_new_) {
+ // The user is editing an existing credit card, find it.
+ for (std::vector<CreditCard>::iterator i = cards.begin();
+ i != cards.end(); ++i) {
+ if (i->unique_id() == credit_card_id_) {
+ card = &(*i);
+ break;
+ }
+ }
+ DCHECK(card); // We should have found the credit card we were created with,
+ // if not we'll end up creating one below.
+ }
+
+ if (!card) {
+ cards.push_back(CreditCard());
+ card = &cards.back();
+ }
+
+ // Update the credit card from what the user typedi n.
+ SetCreditCardValuesFromWidgets(card);
+
+ // And update the model.
+ observer_->OnAutoFillDialogApply(NULL, &cards);
+}
+
+void AutoFillCreditCardEditor::SetCreditCardValuesFromWidgets(
+ CreditCard* card) {
+ PersonalDataManager* data_manager = profile_->GetPersonalDataManager();
+
+ SetFormValue(name_, card, CREDIT_CARD_NAME);
+
+ card->set_billing_address(string16());
+ int selected_address_index =
+ gtk_combo_box_get_active(GTK_COMBO_BOX(address_));
+ if (selected_address_index != -1) {
+ GtkTreeIter iter;
+ gtk_tree_model_iter_nth_child(
+ GTK_TREE_MODEL(address_store_), &iter, NULL, selected_address_index);
+ GValue value = { 0 };
+ gtk_tree_model_get_value(
+ GTK_TREE_MODEL(address_store_), &iter, COL_ID, &value);
+ int id = g_value_get_int(&value);
+ for (std::vector<AutoFillProfile*>::const_iterator i =
+ data_manager->profiles().begin();
+ i != data_manager->profiles().end(); ++i) {
+ if ((*i)->unique_id() == id) {
+ card->set_billing_address((*i)->Label());
+ break;
+ }
+ }
+ g_value_unset(&value);
+ }
+
+ SetFormValue(number_, card, CREDIT_CARD_NUMBER);
+
+ int selected_month_index =
+ gtk_combo_box_get_active(GTK_COMBO_BOX(month_));
+ if (selected_month_index == -1)
+ selected_month_index = 0;
+ card->SetInfo(AutoFillType(CREDIT_CARD_EXP_MONTH),
+ IntToString16(selected_month_index + 1));
+
+ int selected_year_index =
+ gtk_combo_box_get_active(GTK_COMBO_BOX(year_));
+ if (selected_year_index == -1)
+ selected_year_index = 0;
+ card->SetInfo(AutoFillType(CREDIT_CARD_EXP_4_DIGIT_YEAR),
+ IntToString16(selected_year_index + base_year_));
+}
+
+void AutoFillCreditCardEditor::OnDestroy(GtkWidget* widget) {
+ MessageLoop::current()->DeleteSoon(FROM_HERE, this);
+}
+
+void AutoFillCreditCardEditor::OnResponse(GtkDialog* dialog, gint response_id) {
+ if (response_id == GTK_RESPONSE_APPLY || response_id == GTK_RESPONSE_OK)
+ ApplyEdits();
+
+ if (response_id == GTK_RESPONSE_OK ||
+ response_id == GTK_RESPONSE_APPLY ||
+ response_id == GTK_RESPONSE_CANCEL ||
+ response_id == GTK_RESPONSE_DELETE_EVENT) {
+ gtk_widget_destroy(GTK_WIDGET(dialog));
+ }
+}
+
+} // namespace
+
+void ShowAutoFillProfileEditor(gfx::NativeView parent,
+ AutoFillDialogObserver* observer,
+ Profile* profile,
+ AutoFillProfile* auto_fill_profile) {
+ // AutoFillProfileEditor takes care of deleting itself.
+ new AutoFillProfileEditor(observer, profile, auto_fill_profile);
+}
+
+void ShowAutoFillCreditCardEditor(gfx::NativeView parent,
+ AutoFillDialogObserver* observer,
+ Profile* profile,
+ CreditCard* credit_card) {
+ // AutoFillCreditCardEditor takes care of deleting itself.
+ new AutoFillCreditCardEditor(observer, profile, credit_card);
+}
diff --git a/chrome/browser/autofill/autofill_dialog.h b/chrome/browser/autofill/autofill_dialog.h
index 5e1a530..de70e53 100644
--- a/chrome/browser/autofill/autofill_dialog.h
+++ b/chrome/browser/autofill/autofill_dialog.h
@@ -18,7 +18,9 @@ class Profile;
class AutoFillDialogObserver {
public:
// The user has confirmed changes by clicking "Apply" or "OK". Any of the
- // parameters may be NULL.
+ // parameters may be NULL. A NULL parameter is treated as not changing the
+ // existing data. For example, |OnAutoFillDialogApply(new_profiles, NULL)|
+ // only sets the profiles and not the credit cards.
virtual void OnAutoFillDialogApply(
std::vector<AutoFillProfile>* profiles,
std::vector<CreditCard>* credit_cards) = 0;
@@ -28,9 +30,9 @@ class AutoFillDialogObserver {
};
// Shows the AutoFill dialog, which allows the user to edit profile information.
-// |profile| is profile from which you can get vectors of of autofill profiles
-// that contains the current profile information and credit cards.
-// The dialog fills out the profile fields using this data.
+// |profile| is profile from which you can get vectors of autofill profiles that
+// contains the current profile information and credit cards. The dialog fills
+// out the profile fields using this data.
//
// |observer| will be notified by OnAutoFillDialogAccept when the user has
// applied changes. May not be NULL.
diff --git a/chrome/browser/autofill/autofill_dialog_gtk.cc b/chrome/browser/autofill/autofill_dialog_gtk.cc
index 48a6903..ec8a1fe 100644
--- a/chrome/browser/autofill/autofill_dialog_gtk.cc
+++ b/chrome/browser/autofill/autofill_dialog_gtk.cc
@@ -6,9 +6,11 @@
#include <gtk/gtk.h>
+#include <algorithm>
#include <string>
#include <vector>
+#include "app/gtk_signal.h"
#include "app/l10n_util.h"
#include "base/logging.h"
#include "base/message_loop.h"
@@ -22,219 +24,84 @@
#include "chrome/browser/browser.h"
#include "chrome/browser/browser_list.h"
#include "chrome/browser/gtk/gtk_chrome_link_button.h"
+#include "chrome/browser/gtk/gtk_tree.h"
#include "chrome/browser/gtk/gtk_util.h"
-#include "chrome/browser/gtk/options/options_layout_gtk.h"
+#include "chrome/browser/metrics/user_metrics.h"
+#include "chrome/browser/pref_member.h"
+#include "chrome/browser/pref_service.h"
#include "chrome/browser/profile.h"
+#include "chrome/common/notification_details.h"
+#include "chrome/common/notification_observer.h"
+#include "chrome/common/notification_type.h"
#include "chrome/common/pref_names.h"
#include "gfx/gtk_util.h"
#include "grit/chromium_strings.h"
#include "grit/generated_resources.h"
#include "grit/locale_settings.h"
-namespace {
-
-// The name of the object property used to store an entry widget pointer on
-// another widget.
-const char kButtonDataKey[] = "label-entry";
-
-// How far we indent dialog widgets, in pixels.
-const int kAutoFillDialogIndent = 5;
-
-// The resource id for the 'Learn more' link button.
-const gint kAutoFillDialogLearnMoreLink = 1;
-
-// All of these widgets are GtkEntrys.
-typedef struct _AddressWidgets {
- GtkWidget* label;
- GtkWidget* full_name;
- GtkWidget* email;
- GtkWidget* company_name;
- GtkWidget* address_line1;
- GtkWidget* address_line2;
- GtkWidget* city;
- GtkWidget* state;
- GtkWidget* zipcode;
- GtkWidget* country;
- GtkWidget* phone;
- GtkWidget* fax;
-} AddressWidgets;
-
-// All of these widgets are GtkEntrys except for |billing_address|, which is
-// a GtkComboBox.
-typedef struct _CreditCardWidgets {
- GtkWidget* label;
- GtkWidget* name_on_card;
- GtkWidget* card_number;
- GtkWidget* expiration_month;
- GtkWidget* expiration_year;
- GtkWidget* billing_address;
- GtkWidget* phone;
- string16 original_card_number;
-} CreditCardWidgets;
-
-// Adds an alignment around |widget| which indents the widget by |offset|.
-GtkWidget* IndentWidget(GtkWidget* widget, int offset) {
- GtkWidget* alignment = gtk_alignment_new(0, 0, 0, 0);
- gtk_alignment_set_padding(GTK_ALIGNMENT(alignment), 0, 0,
- offset, 0);
- gtk_container_add(GTK_CONTAINER(alignment), widget);
- return alignment;
-}
-
-// Makes sure we use the gtk theme colors by loading the base color of an entry
-// widget.
-void SetWhiteBackground(GtkWidget* widget) {
- GtkWidget* entry = gtk_entry_new();
- gtk_widget_ensure_style(entry);
- GtkStyle* style = gtk_widget_get_style(entry);
- gtk_widget_modify_bg(widget, GTK_STATE_NORMAL,
- &style->base[GTK_STATE_NORMAL]);
- gtk_widget_destroy(entry);
-}
-
-string16 GetEntryText(GtkWidget* entry) {
- return UTF8ToUTF16(gtk_entry_get_text(GTK_ENTRY(entry)));
-}
-
-void SetEntryText(GtkWidget* entry, const string16& text) {
- gtk_entry_set_text(GTK_ENTRY(entry), UTF16ToUTF8(text).c_str());
-}
-
-void SetButtonData(GtkWidget* widget, GtkWidget* entry) {
- g_object_set_data(G_OBJECT(widget), kButtonDataKey, entry);
-}
-
-GtkWidget* GetButtonData(GtkWidget* widget) {
- return static_cast<GtkWidget*>(
- g_object_get_data(G_OBJECT(widget), kButtonDataKey));
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// Form Table helpers.
-//
-// The following functions can be used to create a form with labeled widgets.
-//
-
-// Creates a form table with dimensions |rows| x |cols|.
-GtkWidget* InitFormTable(int rows, int cols) {
- // We have two table rows per form table row.
- GtkWidget* table = gtk_table_new(rows * 2, cols, false);
- gtk_table_set_row_spacings(GTK_TABLE(table), gtk_util::kControlSpacing);
- gtk_table_set_col_spacings(GTK_TABLE(table), gtk_util::kFormControlSpacing);
-
- // Leave no space between the label and the widget.
- for (int i = 0; i < rows; i++)
- gtk_table_set_row_spacing(GTK_TABLE(table), i * 2, 0);
-
- return table;
-}
-
-// Sets the label of the form widget at |row|,|col|. The label is |len| columns
-// long.
-void FormTableSetLabel(
- GtkWidget* table, int row, int col, int len, int label_id) {
- // We have two table rows per form table row.
- row *= 2;
-
- std::string text;
- if (label_id)
- text = l10n_util::GetStringUTF8(label_id);
- GtkWidget* label = gtk_label_new(text.c_str());
- gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
- gtk_table_attach(GTK_TABLE(table), label,
- col, col + len, // Left col, right col.
- row, row + 1, // Top row, bottom row.
- GTK_FILL, GTK_FILL, // Options.
- 0, 0); // Padding.
-}
-
-// Sets the form widget at |row|,|col|. The widget fills up |len| columns. If
-// |expand| is true, the widget will expand to fill all of the extra space in
-// the table row.
-void FormTableSetWidget(GtkWidget* table,
- GtkWidget* widget,
- int row, int col,
- int len, bool expand) {
- const GtkAttachOptions expand_option =
- static_cast<GtkAttachOptions>(GTK_FILL | GTK_EXPAND);
- GtkAttachOptions xoption = (expand) ? expand_option : GTK_FILL;
-
- // We have two table rows per form table row.
- row *= 2;
- gtk_table_attach(GTK_TABLE(table), widget,
- col, col + len, // Left col, right col.
- row + 1, row + 2, // Top row, bottom row.
- xoption, GTK_FILL, // Options.
- 0, 0); // Padding.
-}
-
-// Adds a labeled entry box to the form table at |row|,|col|. The entry widget
-// fills up |len| columns. The returned widget is owned by |table| and should
-// not be destroyed.
-GtkWidget* FormTableAddEntry(
- GtkWidget* table, int row, int col, int len, int label_id) {
- FormTableSetLabel(table, row, col, len, label_id);
-
- GtkWidget* entry = gtk_entry_new();
- FormTableSetWidget(table, entry, row, col, len, false);
-
- return entry;
-}
-
-// Adds a labeled entry box to the form table that will expand to fill extra
-// space in the table row.
-GtkWidget* FormTableAddExpandedEntry(
- GtkWidget* table, int row, int col, int len, int label_id) {
- FormTableSetLabel(table, row, col, len, label_id);
-
- GtkWidget* entry = gtk_entry_new();
- FormTableSetWidget(table, entry, row, col, len, true);
-
- return entry;
-}
-
-// Adds a sized entry box to the form table. The entry widget width is set to
-// |char_len|.
-GtkWidget* FormTableAddSizedEntry(
- GtkWidget* table, int row, int col, int char_len, int label_id) {
- GtkWidget* entry = FormTableAddEntry(table, row, col, 1, label_id);
- gtk_entry_set_width_chars(GTK_ENTRY(entry), char_len);
- return entry;
-}
-
-// Like FormTableAddEntry, but connects to the 'changed' signal. |changed| is a
-// callback to handle the 'changed' signal that is emitted when the user edits
-// the entry. |expander| is the expander widget that will be sent to the
-// callback as the user data.
-GtkWidget* FormTableAddLabelEntry(
- GtkWidget* table, int row, int col, int len, int label_id,
- GtkWidget* expander, GCallback changed) {
- FormTableSetLabel(table, row, col, len, label_id);
+// Shows the editor for adding/editing an AutoFillProfile. If
+// |auto_fill_profile| is NULL, a new AutoFillProfile should be created.
+void ShowAutoFillProfileEditor(gfx::NativeView parent,
+ AutoFillDialogObserver* observer,
+ Profile* profile,
+ AutoFillProfile* auto_fill_profile);
- GtkWidget* entry = gtk_entry_new();
- g_signal_connect(entry, "changed", changed, expander);
- FormTableSetWidget(table, entry, row, col, len, false);
+// Shows the editor for adding/editing a CreditCard. If |credit_card| is NULL, a
+// new CreditCard should be created.
+void ShowAutoFillCreditCardEditor(gfx::NativeView parent,
+ AutoFillDialogObserver* observer,
+ Profile* profile,
+ CreditCard* credit_card);
- return entry;
-}
+namespace {
-} // namespace
+// The resource id for the 'About Autofill' link button.
+const gint kAutoFillDialogAboutLink = 1;
////////////////////////////////////////////////////////////////////////////////
// AutoFillDialog
//
// The contents of the AutoFill dialog. This dialog allows users to add, edit
// and remove AutoFill profiles.
-class AutoFillDialog : public PersonalDataManager::Observer {
+class AutoFillDialog : public PersonalDataManager::Observer,
+ public NotificationObserver {
public:
- AutoFillDialog(AutoFillDialogObserver* observer,
- PersonalDataManager* personal_data_manager,
- AutoFillProfile* imported_profile,
- CreditCard* imported_credit_card);
+ // Identifiers for columns in the store.
+ enum Column {
+ COL_TITLE = 0,
+ COL_IS_HEADER,
+ COL_IS_SEPARATOR, // Identifies an empty row used to reserve visual space.
+ COL_WEIGHT,
+ COL_WEIGHT_SET,
+ COL_COUNT
+ };
+
+ // Used to identify the selection. See GetSelectionType.
+ enum SelectionType {
+ // Nothing is selected.
+ SELECTION_EMPTY = 0,
+
+ // At least one header/separator row is selected.
+ SELECTION_HEADER = 1 << 0,
+
+ // At least one non-header/separator row is selected.
+ SELECTION_SINGLE = 1 << 1,
+
+ // Multiple non-header/separator rows are selected.
+ SELECTION_MULTI = 1 << 2,
+ };
+
+ AutoFillDialog(Profile* profile, AutoFillDialogObserver* observer);
~AutoFillDialog();
// PersonalDataManager::Observer implementation:
void OnPersonalDataLoaded();
+ void OnPersonalDataChanged();
+
+ // NotificationObserver implementation:
+ virtual void Observe(NotificationType type,
+ const NotificationSource& source,
+ const NotificationDetails& details);
// Shows the AutoFill dialog.
void Show();
@@ -242,31 +109,27 @@ class AutoFillDialog : public PersonalDataManager::Observer {
private:
// 'destroy' signal handler. Calls DeleteSoon on the global singleton dialog
// object.
- static void OnDestroy(GtkWidget* widget, AutoFillDialog* autofill_dialog);
+ CHROMEGTK_CALLBACK_0(AutoFillDialog, void, OnDestroy);
// 'response' signal handler. Notifies the AutoFillDialogObserver that new
// data is available if the response is GTK_RESPONSE_APPLY or GTK_RESPONSE_OK.
// We close the dialog if the response is GTK_RESPONSE_OK or
// GTK_RESPONSE_CANCEL.
- static void OnResponse(GtkDialog* dialog, gint response_id,
- AutoFillDialog* autofill_dialog);
-
- // 'clicked' signal handler. Adds a new address.
- static void OnAddAddressClicked(GtkButton* button, AutoFillDialog* dialog);
-
- // 'clicked' signal handler. Adds a new credit card.
- static void OnAddCreditCardClicked(GtkButton* button, AutoFillDialog* dialog);
-
- // 'clicked' signal handler. Deletes the associated address.
- static void OnDeleteAddressClicked(GtkButton* button, AutoFillDialog* dialog);
-
- // 'clicked' signal handler. Deletes the associated credit card.
- static void OnDeleteCreditCardClicked(GtkButton* button,
- AutoFillDialog* dialog);
-
- // 'changed' signal handler. Updates the title of the expander widget with
- // the contents of the label entry widget.
- static void OnLabelChanged(GtkEntry* label, GtkWidget* expander);
+ CHROMEG_CALLBACK_1(AutoFillDialog, void, OnResponse, GtkDialog*, gint);
+
+ CHROMEGTK_CALLBACK_0(AutoFillDialog, void, OnAutoFillCheckToggled);
+ CHROMEG_CALLBACK_2(AutoFillDialog, void, OnRowActivated, GtkTreeView*,
+ GtkTreePath*, GtkTreeViewColumn*);
+ CHROMEG_CALLBACK_0(AutoFillDialog, void, OnSelectionChanged,
+ GtkTreeSelection*);
+ CHROMEG_CALLBACK_1(AutoFillDialog, gboolean, OnCheckRowIsSeparator,
+ GtkTreeModel*, GtkTreeIter*);
+ CHROMEGTK_CALLBACK_0(AutoFillDialog, void, OnAddAddress);
+ CHROMEGTK_CALLBACK_0(AutoFillDialog, void, OnAddCreditCard);
+ CHROMEGTK_CALLBACK_0(AutoFillDialog, void, OnEdit);
+ CHROMEGTK_CALLBACK_0(AutoFillDialog, void, OnRemove);
+ CHROMEG_CALLBACK_3(AutoFillDialog, gboolean, OnSelectionFilter,
+ GtkTreeSelection*, GtkTreeModel*, GtkTreePath*, gboolean);
// Opens the 'Learn more' link in a new foreground tab.
void OnLinkActivated();
@@ -277,36 +140,23 @@ class AutoFillDialog : public PersonalDataManager::Observer {
// Creates the dialog UI widgets.
void InitializeWidgets();
- // Initializes the group widgets and returns their container. |name_id| is
- // the resource ID of the group label. |button_id| is the resource name of
- // the button label. |clicked_callback| is a callback that handles the
- // 'clicked' signal emitted when the user presses the 'Add' button.
- GtkWidget* InitGroup(int label_id,
- int button_id,
- GCallback clicked_callback);
-
- // Initializes the expander, frame and table widgets used to hold the address
- // and credit card forms. |name_id| is the resource id of the label of the
- // expander widget. The content vbox widget is returned in |content_vbox|.
- // Returns the expander widget.
- GtkWidget* InitGroupContentArea(int name_id, GtkWidget** content_vbox);
-
- // Returns a GtkExpander that is added to the appropriate vbox. Each method
- // adds the necessary widgets and layout required to fill out information
- // for either an address or a credit card. The expander will be expanded by
- // default if |expand| is true.
- GtkWidget* AddNewAddress(bool expand);
- GtkWidget* AddNewCreditCard(bool expand);
-
- // Adds a new address filled out with information from |profile|.
- void AddAddress(const AutoFillProfile& profile);
-
- // Adds a new credit card filled out with information from |credit_card|.
- void AddCreditCard(const CreditCard& credit_card);
-
- // Returns the index of |billing_address| in the list of profiles. Returns -1
- // if the address is not found.
- int FindIndexOfAddress(string16 billing_address);
+ // Updates the state of the various widgets dependant upon the state of the
+ // selection, loaded state and whether AutoFill is enabled.
+ void UpdateWidgetState();
+
+ // Returns a bitmask of the selection types.
+ int GetSelectionType();
+
+ void AddAddressToTree(const AutoFillProfile& profile, GtkTreeIter* iter);
+
+ void AddCreditCardToTree(const CreditCard& credit_card, GtkTreeIter* iter);
+
+ // Returns the set of selected profiles and cards. The values placed in
+ // the specified vectors are owned by PersonalDataManager.
+ void GetSelectedEntries(std::vector<AutoFillProfile*>* profiles,
+ std::vector<CreditCard*>* cards);
+
+ Profile* profile_;
// Our observer. May not be NULL.
AutoFillDialogObserver* observer_;
@@ -315,32 +165,26 @@ class AutoFillDialog : public PersonalDataManager::Observer {
// Unowned pointer, may not be NULL.
PersonalDataManager* personal_data_;
- // The imported profile. May be NULL.
- AutoFillProfile* imported_profile_;
-
- // The imported credit card. May be NULL.
- CreditCard* imported_credit_card_;
-
- // The list of current AutoFill profiles.
- std::vector<AutoFillProfile> profiles_;
+ // Number of profiles we're displaying.
+ int profile_count_;
- // The list of current AutoFill credit cards.
- std::vector<CreditCard> credit_cards_;
+ // The AutoFill dialog.
+ GtkWidget* dialog_;
- // The list of address widgets, used to modify the AutoFill profiles.
- std::vector<AddressWidgets> address_widgets_;
+ BooleanPrefMember enable_form_autofill_;
- // The list of credit card widgets, used to modify the stored credit cards.
- std::vector<CreditCardWidgets> credit_card_widgets_;
+ GtkWidget* form_autofill_enable_check_;
- // The AutoFill dialog.
- GtkWidget* dialog_;
+ // Displays the addresses then credit cards.
+ GtkListStore* list_store_;
- // The addresses group.
- GtkWidget* addresses_vbox_;
+ // Displays the list_store_.
+ GtkWidget* tree_;
- // The credit cards group.
- GtkWidget* creditcards_vbox_;
+ GtkWidget* add_address_button_;
+ GtkWidget* add_credit_card_button_;
+ GtkWidget* edit_button_;
+ GtkWidget* remove_button_;
DISALLOW_COPY_AND_ASSIGN(AutoFillDialog);
};
@@ -348,17 +192,20 @@ class AutoFillDialog : public PersonalDataManager::Observer {
// The singleton AutoFill dialog object.
static AutoFillDialog* dialog = NULL;
-AutoFillDialog::AutoFillDialog(AutoFillDialogObserver* observer,
- PersonalDataManager* personal_data_manager,
- AutoFillProfile* imported_profile,
- CreditCard* imported_credit_card)
- : observer_(observer),
- personal_data_(personal_data_manager),
- imported_profile_(imported_profile),
- imported_credit_card_(imported_credit_card) {
+AutoFillDialog::AutoFillDialog(Profile* profile,
+ AutoFillDialogObserver* observer)
+ : profile_(profile),
+ observer_(observer),
+ personal_data_(profile->GetPersonalDataManager()),
+ profile_count_(0) {
DCHECK(observer_);
DCHECK(personal_data_);
+ enable_form_autofill_.Init(prefs::kAutoFillEnabled, profile->GetPrefs(),
+ this);
+
+ personal_data_->SetObserver(this);
+
InitializeWidgets();
LoadAutoFillData();
@@ -370,234 +217,151 @@ AutoFillDialog::AutoFillDialog(AutoFillDialogObserver* observer,
AutoFillDialog::~AutoFillDialog() {
// Removes observer if we are observing Profile load. Does nothing otherwise.
- if (personal_data_)
- personal_data_->RemoveObserver(this);
+ personal_data_->RemoveObserver(this);
}
/////////////////////////////////////////////////////////////////////////////
// PersonalDataManager::Observer implementation:
void AutoFillDialog::OnPersonalDataLoaded() {
- personal_data_->RemoveObserver(this);
LoadAutoFillData();
}
-void AutoFillDialog::Show() {
- gtk_util::PresentWindow(dialog_, gtk_get_current_event_time());
+void AutoFillDialog::OnPersonalDataChanged() {
+ LoadAutoFillData();
}
-// static
-void AutoFillDialog::OnDestroy(GtkWidget* widget,
- AutoFillDialog* autofill_dialog) {
- dialog = NULL;
- MessageLoop::current()->DeleteSoon(FROM_HERE, autofill_dialog);
+void AutoFillDialog::Observe(NotificationType type,
+ const NotificationSource& source,
+ const NotificationDetails& details) {
+ DCHECK_EQ(NotificationType::PREF_CHANGED, type.value);
+ const std::wstring* pref_name = Details<std::wstring>(details).ptr();
+ if (!pref_name || *pref_name == prefs::kAutoFillEnabled) {
+ gtk_toggle_button_set_active(
+ GTK_TOGGLE_BUTTON(form_autofill_enable_check_),
+ enable_form_autofill_.GetValue() ? TRUE : FALSE);
+ UpdateWidgetState();
+ }
}
-static AutoFillProfile AutoFillProfileFromWidgetValues(
- const AddressWidgets& widgets) {
- // TODO(jhawkins): unique id?
- AutoFillProfile profile(GetEntryText(widgets.label), 0);
- profile.SetInfo(AutoFillType(NAME_FULL),
- GetEntryText(widgets.full_name));
- profile.SetInfo(AutoFillType(EMAIL_ADDRESS),
- GetEntryText(widgets.email));
- profile.SetInfo(AutoFillType(COMPANY_NAME),
- GetEntryText(widgets.company_name));
- profile.SetInfo(AutoFillType(ADDRESS_HOME_LINE1),
- GetEntryText(widgets.address_line1));
- profile.SetInfo(AutoFillType(ADDRESS_HOME_LINE2),
- GetEntryText(widgets.address_line2));
- profile.SetInfo(AutoFillType(ADDRESS_HOME_CITY),
- GetEntryText(widgets.city));
- profile.SetInfo(AutoFillType(ADDRESS_HOME_STATE),
- GetEntryText(widgets.state));
- profile.SetInfo(AutoFillType(ADDRESS_HOME_ZIP),
- GetEntryText(widgets.zipcode));
- profile.SetInfo(AutoFillType(ADDRESS_HOME_COUNTRY),
- GetEntryText(widgets.country));
-
- string16 number, city_code, country_code;
- PhoneNumber::ParsePhoneNumber(
- GetEntryText(widgets.phone), &number, &city_code, &country_code);
- profile.SetInfo(AutoFillType(PHONE_HOME_COUNTRY_CODE), country_code);
- profile.SetInfo(AutoFillType(PHONE_HOME_CITY_CODE), city_code);
- profile.SetInfo(AutoFillType(PHONE_HOME_NUMBER), number);
-
- PhoneNumber::ParsePhoneNumber(
- GetEntryText(widgets.fax), &number, &city_code, &country_code);
- profile.SetInfo(AutoFillType(PHONE_FAX_COUNTRY_CODE), country_code);
- profile.SetInfo(AutoFillType(PHONE_FAX_CITY_CODE), city_code);
- profile.SetInfo(AutoFillType(PHONE_FAX_NUMBER), number);
-
- return profile;
+void AutoFillDialog::Show() {
+ gtk_util::PresentWindow(dialog_, gtk_get_current_event_time());
}
-static CreditCard CreditCardFromWidgetValues(
- const CreditCardWidgets& widgets) {
- // TODO(jhawkins): unique id?
- CreditCard credit_card(GetEntryText(widgets.label), 0);
- credit_card.SetInfo(AutoFillType(CREDIT_CARD_NAME),
- GetEntryText(widgets.name_on_card));
- credit_card.SetInfo(AutoFillType(CREDIT_CARD_EXP_MONTH),
- GetEntryText(widgets.expiration_month));
- credit_card.SetInfo(AutoFillType(CREDIT_CARD_EXP_4_DIGIT_YEAR),
- GetEntryText(widgets.expiration_year));
-
- // If the CC number starts with an asterisk, then we know that the user has
- // not modified the credit card number at the least, so use the original CC
- // number in this case.
- string16 cc_number = GetEntryText(widgets.card_number);
- if (!cc_number.empty() && cc_number[0] == '*')
- credit_card.SetInfo(AutoFillType(CREDIT_CARD_NUMBER),
- widgets.original_card_number);
- else
- credit_card.SetInfo(AutoFillType(CREDIT_CARD_NUMBER),
- GetEntryText(widgets.card_number));
-
- std::string text =
- gtk_combo_box_get_active_text(GTK_COMBO_BOX(widgets.billing_address));
- if (text !=
- l10n_util::GetStringUTF8(IDS_AUTOFILL_DIALOG_CHOOSE_EXISTING_ADDRESS)) {
- // TODO(jhawkins): Should we validate the billing address combobox?
- credit_card.set_billing_address(UTF8ToUTF16(text));
- }
-
- return credit_card;
+void AutoFillDialog::OnDestroy(GtkWidget* widget) {
+ dialog = NULL;
+ MessageLoop::current()->DeleteSoon(FROM_HERE, this);
}
-// static
-void AutoFillDialog::OnResponse(GtkDialog* dialog, gint response_id,
- AutoFillDialog* autofill_dialog) {
- if (response_id == GTK_RESPONSE_APPLY || response_id == GTK_RESPONSE_OK) {
- autofill_dialog->profiles_.clear();
- for (std::vector<AddressWidgets>::const_iterator iter =
- autofill_dialog->address_widgets_.begin();
- iter != autofill_dialog->address_widgets_.end();
- ++iter) {
- AutoFillProfile profile = AutoFillProfileFromWidgetValues(*iter);
- autofill_dialog->profiles_.push_back(profile);
- }
-
- autofill_dialog->credit_cards_.clear();
- for (std::vector<CreditCardWidgets>::const_iterator iter =
- autofill_dialog->credit_card_widgets_.begin();
- iter != autofill_dialog->credit_card_widgets_.end();
- ++iter) {
- CreditCard credit_card = CreditCardFromWidgetValues(*iter);
- autofill_dialog->credit_cards_.push_back(credit_card);
- }
-
- autofill_dialog->observer_->OnAutoFillDialogApply(
- &autofill_dialog->profiles_,
- &autofill_dialog->credit_cards_);
- }
-
+void AutoFillDialog::OnResponse(GtkDialog* dialog, gint response_id) {
if (response_id == GTK_RESPONSE_OK ||
response_id == GTK_RESPONSE_CANCEL ||
response_id == GTK_RESPONSE_DELETE_EVENT) {
gtk_widget_destroy(GTK_WIDGET(dialog));
}
- if (response_id == kAutoFillDialogLearnMoreLink)
- autofill_dialog->OnLinkActivated();
+ if (response_id == kAutoFillDialogAboutLink)
+ OnLinkActivated();
}
-// static
-void AutoFillDialog::OnAddAddressClicked(GtkButton* button,
- AutoFillDialog* dialog) {
- GtkWidget* new_address = dialog->AddNewAddress(true);
- gtk_box_pack_start(GTK_BOX(dialog->addresses_vbox_), new_address,
- FALSE, FALSE, 0);
- gtk_widget_show_all(new_address);
+void AutoFillDialog::OnAutoFillCheckToggled(GtkWidget* widget) {
+ bool enabled = gtk_toggle_button_get_active(
+ GTK_TOGGLE_BUTTON(form_autofill_enable_check_));
+ if (enabled) {
+ UserMetrics::RecordAction(UserMetricsAction("Options_FormAutofill_Enable"),
+ profile_);
+ } else {
+ UserMetrics::RecordAction(UserMetricsAction("Options_FormAutofill_Disable"),
+ profile_);
+ }
+ enable_form_autofill_.SetValue(enabled);
+ profile_->GetPrefs()->ScheduleSavePersistentPrefs();
+ UpdateWidgetState();
}
-// static
-void AutoFillDialog::OnAddCreditCardClicked(GtkButton* button,
- AutoFillDialog* dialog) {
- GtkWidget* new_creditcard = dialog->AddNewCreditCard(true);
- gtk_box_pack_start(GTK_BOX(dialog->creditcards_vbox_), new_creditcard,
- FALSE, FALSE, 0);
- gtk_widget_show_all(new_creditcard);
+void AutoFillDialog::OnRowActivated(GtkTreeView* tree_view,
+ GtkTreePath* path,
+ GtkTreeViewColumn* column) {
+ if (GetSelectionType() == SELECTION_SINGLE)
+ OnEdit(NULL);
}
-// static
-void AutoFillDialog::OnDeleteAddressClicked(GtkButton* button,
- AutoFillDialog* dialog) {
- GtkWidget* entry = GetButtonData(GTK_WIDGET(button));
- string16 label = GetEntryText(entry);
-
- // TODO(jhawkins): Base this on ID.
-
- // Remove the profile.
- for (std::vector<AutoFillProfile>::iterator iter = dialog->profiles_.begin();
- iter != dialog->profiles_.end();
- ++iter) {
- if (iter->Label() == label) {
- dialog->profiles_.erase(iter);
- break;
- }
- }
+void AutoFillDialog::OnSelectionChanged(GtkTreeSelection* selection) {
+ UpdateWidgetState();
+}
- // Remove the set of address widgets.
- for (std::vector<AddressWidgets>::iterator iter =
- dialog->address_widgets_.begin();
- iter != dialog->address_widgets_.end();
- ++iter) {
- if (iter->label == entry) {
- dialog->address_widgets_.erase(iter);
- break;
- }
- }
+gboolean AutoFillDialog::OnCheckRowIsSeparator(GtkTreeModel* model,
+ GtkTreeIter* iter) {
+ gboolean is_separator;
+ gtk_tree_model_get(model, iter, COL_IS_SEPARATOR, &is_separator, -1);
+ return is_separator;
+}
+
+void AutoFillDialog::OnAddAddress(GtkWidget* widget) {
+ ShowAutoFillProfileEditor(NULL, observer_, profile_, NULL);
+}
+
+void AutoFillDialog::OnAddCreditCard(GtkWidget* widget) {
+ ShowAutoFillCreditCardEditor(NULL, observer_, profile_, NULL);
+}
- // Get back to the expander widget.
- GtkWidget* expander = gtk_widget_get_ancestor(GTK_WIDGET(button),
- GTK_TYPE_EXPANDER);
- DCHECK(expander);
+void AutoFillDialog::OnEdit(GtkWidget* widget) {
+ DCHECK_EQ(SELECTION_SINGLE, GetSelectionType());
- // Destroying the widget will also remove it from the parent container.
- gtk_widget_destroy(expander);
+ std::vector<AutoFillProfile*> profiles;
+ std::vector<CreditCard*> cards;
+
+ GetSelectedEntries(&profiles, &cards);
+
+ if (profiles.size() == 1)
+ ShowAutoFillProfileEditor(dialog_, observer_, profile_, profiles[0]);
+ else if (cards.size() == 1)
+ ShowAutoFillCreditCardEditor(dialog_, observer_, profile_, cards[0]);
}
-// static
-void AutoFillDialog::OnDeleteCreditCardClicked(GtkButton* button,
- AutoFillDialog* dialog) {
- GtkWidget* entry = GetButtonData(GTK_WIDGET(button));
- string16 label = GetEntryText(entry);
-
- // TODO(jhawkins): Base this on ID.
-
- // Remove the credit card.
- for (std::vector<CreditCard>::iterator iter = dialog->credit_cards_.begin();
- iter != dialog->credit_cards_.end();
- ++iter) {
- if (iter->Label() == label) {
- dialog->credit_cards_.erase(iter);
- break;
+void AutoFillDialog::OnRemove(GtkWidget* widget) {
+ PersonalDataManager* data_manager = profile_->GetPersonalDataManager();
+ std::vector<AutoFillProfile*> selected_profiles;
+ std::vector<CreditCard*> selected_cards;
+
+ GetSelectedEntries(&selected_profiles, &selected_cards);
+
+ std::vector<AutoFillProfile> profiles;
+ for (std::vector<AutoFillProfile*>::const_iterator i =
+ data_manager->profiles().begin();
+ i != data_manager->profiles().end(); ++i) {
+ if (std::find(selected_profiles.begin(), selected_profiles.end(), *i) ==
+ selected_profiles.end()) {
+ profiles.push_back(**i);
}
}
- // Remove the set of credit widgets.
- for (std::vector<CreditCardWidgets>::iterator iter =
- dialog->credit_card_widgets_.begin();
- iter != dialog->credit_card_widgets_.end();
- ++iter) {
- if (iter->label == entry) {
- dialog->credit_card_widgets_.erase(iter);
- break;
+ std::vector<CreditCard> cards;
+ for (std::vector<CreditCard*>::const_iterator i =
+ data_manager->credit_cards().begin();
+ i != data_manager->credit_cards().end(); ++i) {
+ if (std::find(selected_cards.begin(), selected_cards.end(), *i) ==
+ selected_cards.end()) {
+ cards.push_back(**i);
}
}
- // Get back to the expander widget.
- GtkWidget* expander = gtk_widget_get_ancestor(GTK_WIDGET(button),
- GTK_TYPE_EXPANDER);
- DCHECK(expander);
-
- // Destroying the widget will also remove it from the parent container.
- gtk_widget_destroy(expander);
+ observer_->OnAutoFillDialogApply(&profiles, &cards);
}
-// static
-void AutoFillDialog::OnLabelChanged(GtkEntry* label, GtkWidget* expander) {
- gtk_expander_set_label(GTK_EXPANDER(expander), gtk_entry_get_text(label));
+gboolean AutoFillDialog::OnSelectionFilter(GtkTreeSelection* selection,
+ GtkTreeModel* model,
+ GtkTreePath* path,
+ gboolean path_currently_selected) {
+ GtkTreeIter iter;
+ if (!gtk_tree_model_get_iter(model, &iter, path)) {
+ NOTREACHED();
+ return TRUE;
+ }
+ gboolean is_header;
+ gboolean is_separator;
+ gtk_tree_model_get(model, &iter, COL_IS_HEADER, &is_header,
+ COL_IS_SEPARATOR, &is_separator, -1);
+ return !is_header && !is_separator;
}
void AutoFillDialog::OnLinkActivated() {
@@ -608,43 +372,72 @@ void AutoFillDialog::OnLinkActivated() {
void AutoFillDialog::LoadAutoFillData() {
if (!personal_data_->IsDataLoaded()) {
- personal_data_->SetObserver(this);
+ UpdateWidgetState();
return;
}
- if (imported_profile_) {
- profiles_.push_back(*imported_profile_);
- AddAddress(*imported_profile_);
- } else {
- for (std::vector<AutoFillProfile*>::const_iterator iter =
- personal_data_->profiles().begin();
- iter != personal_data_->profiles().end(); ++iter) {
- // The profile list is terminated by a NULL entry;
- if (!*iter)
- break;
-
- AutoFillProfile* profile = *iter;
- profiles_.push_back(*profile);
- AddAddress(*profile);
- }
+ // Rebuild the underyling store.
+ gtk_list_store_clear(list_store_);
+
+ GtkTreeIter iter;
+ // Address 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,
+ l10n_util::GetStringUTF8(IDS_AUTOFILL_ADDRESSES_GROUP_NAME).c_str(),
+ COL_IS_HEADER, TRUE,
+ -1);
+ // Address separator.
+ gtk_list_store_append(list_store_, &iter);
+ gtk_list_store_set(
+ list_store_, &iter,
+ COL_IS_SEPARATOR, TRUE,
+ -1);
+
+ // The addresses.
+ profile_count_ = 0;
+ for (std::vector<AutoFillProfile*>::const_iterator i =
+ personal_data_->profiles().begin();
+ i != personal_data_->profiles().end(); ++i) {
+ AddAddressToTree(*(*i), &iter);
+ profile_count_++;
}
- if (imported_credit_card_) {
- credit_cards_.push_back(*imported_credit_card_);
- AddCreditCard(*imported_credit_card_);
- } else {
- for (std::vector<CreditCard*>::const_iterator iter =
- personal_data_->credit_cards().begin();
- iter != personal_data_->credit_cards().end(); ++iter) {
- // The credit card list is terminated by a NULL entry;
- if (!*iter)
- break;
-
- CreditCard* credit_card = *iter;
- credit_cards_.push_back(*credit_card);
- AddCreditCard(*credit_card);
- }
+ // Blank row between addresses and credit cards.
+ gtk_list_store_append(list_store_, &iter);
+ gtk_list_store_set(
+ list_store_, &iter,
+ COL_IS_HEADER, TRUE,
+ -1);
+
+ // Credit card 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,
+ l10n_util::GetStringUTF8(IDS_AUTOFILL_CREDITCARDS_GROUP_NAME).c_str(),
+ COL_IS_HEADER, TRUE,
+ -1);
+ // Credit card separator.
+ gtk_list_store_append(list_store_, &iter);
+ gtk_list_store_set(
+ list_store_, &iter,
+ COL_IS_SEPARATOR, TRUE,
+ -1);
+
+ // The credit cards.
+ for (std::vector<CreditCard*>::const_iterator i =
+ personal_data_->credit_cards().begin();
+ i != personal_data_->credit_cards().end(); ++i) {
+ AddCreditCardToTree(*(*i), &iter);
}
+
+ UpdateWidgetState();
}
void AutoFillDialog::InitializeWidgets() {
@@ -654,54 +447,97 @@ void AutoFillDialog::InitializeWidgets() {
NULL,
// Non-modal.
GTK_DIALOG_NO_SEPARATOR,
- GTK_STOCK_APPLY,
- GTK_RESPONSE_APPLY,
- GTK_STOCK_CANCEL,
- GTK_RESPONSE_CANCEL,
GTK_STOCK_OK,
GTK_RESPONSE_OK,
NULL);
- gtk_box_set_spacing(GTK_BOX(GTK_DIALOG(dialog_)->vbox),
- gtk_util::kContentAreaSpacing);
- g_signal_connect(dialog_, "response", G_CALLBACK(OnResponse), this);
- g_signal_connect(dialog_, "destroy", G_CALLBACK(OnDestroy), this);
+ GtkBox* vbox = GTK_BOX(GTK_DIALOG(dialog_)->vbox);
+ gtk_box_set_spacing(vbox, gtk_util::kControlSpacing);
+ g_signal_connect(dialog_, "response", G_CALLBACK(OnResponseThunk), this);
+ g_signal_connect(dialog_, "destroy", G_CALLBACK(OnDestroyThunk), this);
+
+ form_autofill_enable_check_ = gtk_check_button_new_with_label(
+ l10n_util::GetStringUTF8(IDS_OPTIONS_AUTOFILL_ENABLE).c_str());
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(form_autofill_enable_check_),
+ enable_form_autofill_.GetValue());
+ g_signal_connect(G_OBJECT(form_autofill_enable_check_), "toggled",
+ G_CALLBACK(OnAutoFillCheckToggledThunk), this);
+ gtk_box_pack_start(vbox, form_autofill_enable_check_, FALSE, FALSE, 0);
// Allow the contents to be scrolled.
GtkWidget* scrolled_window = gtk_scrolled_window_new(NULL, NULL);
gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
- gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog_)->vbox), scrolled_window);
-
- // We create an event box so that we can color the frame background white.
- GtkWidget* frame_event_box = gtk_event_box_new();
- SetWhiteBackground(frame_event_box);
- gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scrolled_window),
- frame_event_box);
-
- // The frame outline of the content area.
- GtkWidget* frame = gtk_frame_new(NULL);
- gtk_container_add(GTK_CONTAINER(frame_event_box), frame);
-
- // The content vbox.
- GtkWidget* outer_vbox = gtk_vbox_new(false, 0);
- gtk_box_set_spacing(GTK_BOX(outer_vbox), gtk_util::kContentAreaSpacing);
- gtk_container_add(GTK_CONTAINER(frame), outer_vbox);
-
- addresses_vbox_ = InitGroup(IDS_AUTOFILL_ADDRESSES_GROUP_NAME,
- IDS_AUTOFILL_ADD_ADDRESS_BUTTON,
- G_CALLBACK(OnAddAddressClicked));
- gtk_box_pack_start_defaults(GTK_BOX(outer_vbox), addresses_vbox_);
+ gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled_window),
+ GTK_SHADOW_ETCHED_IN);
+
+ list_store_ = gtk_list_store_new(COL_COUNT,
+ 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_), FALSE);
+ gtk_tree_view_set_row_separator_func(GTK_TREE_VIEW(tree_),
+ OnCheckRowIsSeparatorThunk, this, NULL);
+ g_signal_connect(tree_, "row-activated", G_CALLBACK(OnRowActivatedThunk),
+ this);
+ gtk_container_add(GTK_CONTAINER(scrolled_window), tree_);
+
+ GtkWidget* h_box1 = gtk_hbox_new(FALSE, gtk_util::kControlSpacing);
+ gtk_box_pack_start(vbox, h_box1, TRUE, TRUE, 0);
+ gtk_box_pack_start(GTK_BOX(h_box1), scrolled_window, TRUE, TRUE, 0);
+
+ GtkWidget* controls_box = gtk_vbox_new(FALSE, gtk_util::kControlSpacing);
+ gtk_box_pack_start(GTK_BOX(h_box1), controls_box, FALSE, FALSE, 0);
+
+ add_address_button_ = gtk_button_new_with_label(
+ l10n_util::GetStringUTF8(IDS_AUTOFILL_ADD_ADDRESS_BUTTON).c_str());
+ g_signal_connect(add_address_button_, "clicked",
+ G_CALLBACK(OnAddAddressThunk), this);
+ gtk_box_pack_start(GTK_BOX(controls_box), add_address_button_, FALSE, FALSE,
+ 0);
+
+ add_credit_card_button_ = gtk_button_new_with_label(
+ l10n_util::GetStringUTF8(IDS_AUTOFILL_ADD_CREDITCARD_BUTTON).c_str());
+ g_signal_connect(add_credit_card_button_, "clicked",
+ G_CALLBACK(OnAddCreditCardThunk), this);
+ gtk_box_pack_start(GTK_BOX(controls_box), add_credit_card_button_, FALSE,
+ FALSE, 0);
+
+ edit_button_ = gtk_button_new_with_label(
+ l10n_util::GetStringUTF8(IDS_AUTOFILL_EDIT_BUTTON).c_str());
+ g_signal_connect(edit_button_, "clicked", G_CALLBACK(OnEditThunk), this);
+ gtk_box_pack_start(GTK_BOX(controls_box), edit_button_, FALSE, FALSE, 0);
+
+ remove_button_ = gtk_button_new_with_label(
+ l10n_util::GetStringUTF8(IDS_AUTOFILL_DELETE_BUTTON).c_str());
+ g_signal_connect(remove_button_, "clicked", G_CALLBACK(OnRemoveThunk), this);
+ gtk_box_pack_start(GTK_BOX(controls_box), remove_button_, FALSE, FALSE, 0);
+
+ GtkTreeViewColumn* column = gtk_tree_view_column_new_with_attributes(
+ "",
+ gtk_cell_renderer_text_new(),
+ "text", COL_TITLE,
+ "weight", COL_WEIGHT,
+ "weight-set", COL_WEIGHT_SET,
+ NULL);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(tree_), column);
- creditcards_vbox_ = InitGroup(IDS_AUTOFILL_CREDITCARDS_GROUP_NAME,
- IDS_AUTOFILL_ADD_CREDITCARD_BUTTON,
- G_CALLBACK(OnAddCreditCardClicked));
- gtk_box_pack_start_defaults(GTK_BOX(outer_vbox), creditcards_vbox_);
+ GtkTreeSelection* selection =
+ gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_));
+ gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
+ gtk_tree_selection_set_select_function(selection, OnSelectionFilterThunk,
+ this, NULL);
+ g_signal_connect(selection, "changed", G_CALLBACK(OnSelectionChangedThunk),
+ this);
GtkWidget* link = gtk_chrome_link_button_new(
- l10n_util::GetStringUTF8(IDS_AUTOFILL_LEARN_MORE).c_str());
+ l10n_util::GetStringUTF8(IDS_AUTOFILL_HELP_LABEL).c_str());
gtk_dialog_add_action_widget(GTK_DIALOG(dialog_), link,
- kAutoFillDialogLearnMoreLink);
+ kAutoFillDialogAboutLink);
// Setting the link widget to secondary positions the button on the left side
// of the action area (vice versa for RTL layout).
@@ -709,270 +545,113 @@ void AutoFillDialog::InitializeWidgets() {
GTK_BUTTON_BOX(GTK_DIALOG(dialog_)->action_area), link, TRUE);
}
-GtkWidget* AutoFillDialog::InitGroup(int name_id,
- int button_id,
- GCallback clicked_callback) {
- GtkWidget* vbox = gtk_vbox_new(false, gtk_util::kControlSpacing);
-
- // Group label.
- GtkWidget* label = gtk_util::CreateBoldLabel(
- l10n_util::GetStringUTF8(name_id));
- gtk_box_pack_start(GTK_BOX(vbox),
- IndentWidget(label, kAutoFillDialogIndent),
- FALSE, FALSE, 0);
-
- // Separator.
- GtkWidget* separator = gtk_hseparator_new();
- gtk_box_pack_start(GTK_BOX(vbox), separator, FALSE, FALSE, 0);
-
- // Add profile button.
- GtkWidget* button = gtk_button_new_with_label(
- l10n_util::GetStringUTF8(button_id).c_str());
- g_signal_connect(button, "clicked", clicked_callback, this);
- gtk_box_pack_end_defaults(GTK_BOX(vbox),
- IndentWidget(button, kAutoFillDialogIndent));
-
- return vbox;
-}
-
-GtkWidget* AutoFillDialog::InitGroupContentArea(int name_id,
- GtkWidget** content_vbox) {
- GtkWidget* expander = gtk_expander_new(
- l10n_util::GetStringUTF8(name_id).c_str());
-
- GtkWidget* frame = gtk_frame_new(NULL);
- gtk_container_add(GTK_CONTAINER(expander), frame);
-
- GtkWidget* vbox = gtk_vbox_new(false, 0);
- gtk_box_set_spacing(GTK_BOX(vbox), gtk_util::kControlSpacing);
- GtkWidget* vbox_alignment = gtk_alignment_new(0, 0, 0, 0);
- gtk_alignment_set_padding(GTK_ALIGNMENT(vbox_alignment),
- gtk_util::kControlSpacing,
- gtk_util::kControlSpacing,
- gtk_util::kGroupIndent,
- 0);
- gtk_container_add(GTK_CONTAINER(vbox_alignment), vbox);
- gtk_container_add(GTK_CONTAINER(frame), vbox_alignment);
-
- *content_vbox = vbox;
- return expander;
-}
-
-GtkWidget* AutoFillDialog::AddNewAddress(bool expand) {
- AddressWidgets widgets = {0};
- GtkWidget* vbox;
- GtkWidget* address = InitGroupContentArea(IDS_AUTOFILL_NEW_ADDRESS, &vbox);
-
- gtk_expander_set_expanded(GTK_EXPANDER(address), expand);
-
- GtkWidget* table = InitFormTable(5, 3);
- gtk_box_pack_start_defaults(GTK_BOX(vbox), table);
-
- widgets.label = FormTableAddLabelEntry(table, 0, 0, 1,
- IDS_AUTOFILL_DIALOG_LABEL,
- address, G_CALLBACK(OnLabelChanged));
- widgets.full_name = FormTableAddEntry(table, 1, 0, 1,
- IDS_AUTOFILL_DIALOG_FULL_NAME);
- widgets.email = FormTableAddEntry(table, 2, 0, 1,
- IDS_AUTOFILL_DIALOG_EMAIL);
- widgets.company_name = FormTableAddEntry(table, 2, 1, 1,
- IDS_AUTOFILL_DIALOG_COMPANY_NAME);
- widgets.address_line1 = FormTableAddEntry(table, 3, 0, 2,
- IDS_AUTOFILL_DIALOG_ADDRESS_LINE_1);
- widgets.address_line2 = FormTableAddEntry(table, 4, 0, 2,
- IDS_AUTOFILL_DIALOG_ADDRESS_LINE_2);
-
- GtkWidget* address_table = InitFormTable(1, 4);
- gtk_box_pack_start_defaults(GTK_BOX(vbox), address_table);
-
- widgets.city = FormTableAddEntry(address_table, 0, 0, 1,
- IDS_AUTOFILL_DIALOG_CITY);
- widgets.state = FormTableAddEntry(address_table, 0, 1, 1,
- IDS_AUTOFILL_DIALOG_STATE);
- widgets.zipcode = FormTableAddSizedEntry(address_table, 0, 2, 7,
- IDS_AUTOFILL_DIALOG_ZIP_CODE);
- widgets.country = FormTableAddSizedEntry(address_table, 0, 3, 10,
- IDS_AUTOFILL_DIALOG_COUNTRY);
-
- GtkWidget* phone_table = InitFormTable(1, 2);
- gtk_box_pack_start_defaults(GTK_BOX(vbox), phone_table);
-
- widgets.phone =
- FormTableAddEntry(phone_table, 0, 0, 1, IDS_AUTOFILL_DIALOG_PHONE);
- widgets.fax =
- FormTableAddEntry(phone_table, 0, 1, 1, IDS_AUTOFILL_DIALOG_FAX);
-
- GtkWidget* button = gtk_button_new_with_label(
- l10n_util::GetStringUTF8(IDS_AUTOFILL_DELETE_BUTTON).c_str());
- g_signal_connect(button, "clicked", G_CALLBACK(OnDeleteAddressClicked), this);
- SetButtonData(button, widgets.label);
- GtkWidget* alignment = gtk_alignment_new(0, 0, 0, 0);
- gtk_container_add(GTK_CONTAINER(alignment), button);
- gtk_box_pack_start_defaults(GTK_BOX(vbox), alignment);
-
- address_widgets_.push_back(widgets);
- return address;
+void AutoFillDialog::UpdateWidgetState() {
+ if (!personal_data_->IsDataLoaded() || !enable_form_autofill_.GetValue()) {
+ gtk_widget_set_sensitive(add_address_button_, FALSE);
+ gtk_widget_set_sensitive(add_credit_card_button_, FALSE);
+ gtk_widget_set_sensitive(edit_button_, FALSE);
+ gtk_widget_set_sensitive(remove_button_, FALSE);
+ gtk_widget_set_sensitive(tree_, FALSE);
+ } else {
+ gtk_widget_set_sensitive(add_address_button_, TRUE);
+ gtk_widget_set_sensitive(add_credit_card_button_, TRUE);
+ int selection_type = GetSelectionType();
+ gtk_widget_set_sensitive(edit_button_, selection_type == SELECTION_SINGLE);
+ // Enable the remove button if at least one non-header row is selected.
+ gtk_widget_set_sensitive(remove_button_,
+ (selection_type & SELECTION_SINGLE) != 0);
+ gtk_widget_set_sensitive(tree_, TRUE);
+ }
}
-GtkWidget* AutoFillDialog::AddNewCreditCard(bool expand) {
- CreditCardWidgets widgets = {0};
- GtkWidget* vbox;
- GtkWidget* credit_card = InitGroupContentArea(IDS_AUTOFILL_NEW_CREDITCARD,
- &vbox);
-
- gtk_expander_set_expanded(GTK_EXPANDER(credit_card), expand);
-
- GtkWidget* label_table = InitFormTable(1, 2);
- gtk_box_pack_start_defaults(GTK_BOX(vbox), label_table);
-
- widgets.label = FormTableAddLabelEntry(label_table, 0, 0, 1,
- IDS_AUTOFILL_DIALOG_LABEL, credit_card,
- G_CALLBACK(OnLabelChanged));
-
- GtkWidget* name_cc_table = InitFormTable(2, 6);
- gtk_box_pack_start_defaults(GTK_BOX(vbox), name_cc_table);
-
- widgets.name_on_card = FormTableAddExpandedEntry(
- name_cc_table, 0, 0, 3, IDS_AUTOFILL_DIALOG_NAME_ON_CARD);
- widgets.card_number = FormTableAddExpandedEntry(
- name_cc_table, 1, 0, 3, IDS_AUTOFILL_DIALOG_CREDIT_CARD_NUMBER);
- widgets.expiration_month = FormTableAddSizedEntry(name_cc_table, 1, 3, 2, 0);
- widgets.expiration_year = FormTableAddSizedEntry(name_cc_table, 1, 4, 4, 0);
-
- FormTableSetLabel(name_cc_table, 1, 3, 2,
- IDS_AUTOFILL_DIALOG_EXPIRATION_DATE);
-
- gtk_table_set_col_spacing(GTK_TABLE(name_cc_table), 3, 2);
-
- GtkWidget* addresses_table = InitFormTable(2, 5);
- gtk_box_pack_start_defaults(GTK_BOX(vbox), addresses_table);
-
- FormTableSetLabel(addresses_table, 0, 0, 3,
- IDS_AUTOFILL_DIALOG_BILLING_ADDRESS);
-
- GtkWidget* billing = gtk_combo_box_new_text();
- widgets.billing_address = billing;
- std::string combo_text = l10n_util::GetStringUTF8(
- IDS_AUTOFILL_DIALOG_CHOOSE_EXISTING_ADDRESS);
- gtk_combo_box_append_text(GTK_COMBO_BOX(billing), combo_text.c_str());
- gtk_combo_box_set_active(GTK_COMBO_BOX(billing), 0);
- FormTableSetWidget(addresses_table, billing, 0, 0, 2, false);
-
- for (std::vector<AddressWidgets>::const_iterator iter =
- address_widgets_.begin();
- iter != address_widgets_.end(); ++iter) {
- // TODO(jhawkins): Validate the label and DCHECK on !empty().
- std::string text = gtk_entry_get_text(GTK_ENTRY(iter->label));
- if (!text.empty())
- gtk_combo_box_append_text(GTK_COMBO_BOX(widgets.billing_address),
- text.c_str());
+static void RowIteratorFunction(GtkTreeModel* model,
+ GtkTreePath* path,
+ GtkTreeIter* iter,
+ gpointer data) {
+ int* type = reinterpret_cast<int*>(data);
+ bool is_header = false;
+ GValue value = { 0 };
+ gtk_tree_model_get_value(model, iter, AutoFillDialog::COL_IS_HEADER, &value);
+ is_header = g_value_get_boolean(&value);
+ g_value_unset(&value);
+
+ if (!is_header) {
+ // Is it a separator?
+ GValue value = { 0 };
+ gtk_tree_model_get_value(model, iter, AutoFillDialog::COL_IS_SEPARATOR,
+ &value);
+ is_header = g_value_get_boolean(&value);
+ g_value_unset(&value);
}
- GtkWidget* phone_table = InitFormTable(1, 1);
- gtk_box_pack_start_defaults(GTK_BOX(vbox), phone_table);
-
- widgets.phone =
- FormTableAddEntry(phone_table, 0, 0, 1, IDS_AUTOFILL_DIALOG_PHONE);
-
- GtkWidget* button = gtk_button_new_with_label(
- l10n_util::GetStringUTF8(IDS_AUTOFILL_DELETE_BUTTON).c_str());
- g_signal_connect(button, "clicked",
- G_CALLBACK(OnDeleteCreditCardClicked), this);
- SetButtonData(button, widgets.label);
- GtkWidget* alignment = gtk_alignment_new(0, 0, 0, 0);
- gtk_container_add(GTK_CONTAINER(alignment), button);
- gtk_box_pack_start_defaults(GTK_BOX(vbox), alignment);
-
- credit_card_widgets_.push_back(widgets);
- return credit_card;
-}
-
-void AutoFillDialog::AddAddress(const AutoFillProfile& profile) {
- GtkWidget* address = AddNewAddress(false);
- gtk_expander_set_label(GTK_EXPANDER(address),
- UTF16ToUTF8(profile.Label()).c_str());
-
- // We just pushed the widgets to the back of the vector.
- const AddressWidgets& widgets = address_widgets_.back();
- SetEntryText(widgets.label, profile.Label());
- SetEntryText(widgets.full_name,
- profile.GetFieldText(AutoFillType(NAME_FULL)));
- SetEntryText(widgets.email,
- profile.GetFieldText(AutoFillType(EMAIL_ADDRESS)));
- SetEntryText(widgets.company_name,
- profile.GetFieldText(AutoFillType(COMPANY_NAME)));
- SetEntryText(widgets.address_line1,
- profile.GetFieldText(AutoFillType(ADDRESS_HOME_LINE1)));
- SetEntryText(widgets.address_line2,
- profile.GetFieldText(AutoFillType(ADDRESS_HOME_LINE2)));
- SetEntryText(widgets.city,
- profile.GetFieldText(AutoFillType(ADDRESS_HOME_CITY)));
- SetEntryText(widgets.state,
- profile.GetFieldText(AutoFillType(ADDRESS_HOME_STATE)));
- SetEntryText(widgets.zipcode,
- profile.GetFieldText(AutoFillType(ADDRESS_HOME_ZIP)));
- SetEntryText(widgets.country,
- profile.GetFieldText(AutoFillType(ADDRESS_HOME_COUNTRY)));
- SetEntryText(widgets.phone,
- profile.GetFieldText(AutoFillType(PHONE_HOME_WHOLE_NUMBER)));
- SetEntryText(widgets.fax,
- profile.GetFieldText(AutoFillType(PHONE_FAX_WHOLE_NUMBER)));
-
- gtk_box_pack_start(GTK_BOX(addresses_vbox_), address, FALSE, FALSE, 0);
- gtk_widget_show_all(address);
+ if (is_header) {
+ *type |= AutoFillDialog::SELECTION_HEADER;
+ } else {
+ if ((*type & AutoFillDialog::SELECTION_SINGLE) == 0)
+ *type |= AutoFillDialog::SELECTION_SINGLE;
+ else
+ *type |= AutoFillDialog::SELECTION_MULTI;
+ }
}
-void AutoFillDialog::AddCreditCard(const CreditCard& credit_card) {
- GtkWidget* credit_card_widget = AddNewCreditCard(false);
- gtk_expander_set_label(GTK_EXPANDER(credit_card_widget),
- UTF16ToUTF8(credit_card.Label()).c_str());
-
- // We just pushed the widgets to the back of the vector.
- const CreditCardWidgets& widgets = credit_card_widgets_.back();
- SetEntryText(widgets.label, credit_card.Label());
- SetEntryText(widgets.name_on_card,
- credit_card.GetFieldText(AutoFillType(CREDIT_CARD_NAME)));
- // Set obfuscated number if not empty.
- credit_card_widgets_.back().original_card_number =
- credit_card.GetFieldText(AutoFillType(CREDIT_CARD_NUMBER));
- string16 credit_card_number;
- if (!widgets.original_card_number.empty())
- credit_card_number = credit_card.ObfuscatedNumber();
- SetEntryText(widgets.card_number, credit_card_number);
- SetEntryText(widgets.expiration_month,
- credit_card.GetFieldText(AutoFillType(CREDIT_CARD_EXP_MONTH)));
- SetEntryText(
- widgets.expiration_year,
- credit_card.GetFieldText(AutoFillType(CREDIT_CARD_EXP_4_DIGIT_YEAR)));
-
- // Two cases to consider:
- // address not found - This means the address is not set and
- // FindIndexOfAddress returns -1. -1 + 1 = 0, meaning the first item will
- // be selected, "Choose existing address".
- //
- // address is found - The index returned needs to be offset by one in order
- // to compensate for the first entry, "Choose existing address".
- int index = FindIndexOfAddress(credit_card.billing_address()) + 1;
- gtk_combo_box_set_active(GTK_COMBO_BOX(widgets.billing_address), index);
-
- gtk_box_pack_start(GTK_BOX(creditcards_vbox_), credit_card_widget,
- FALSE, FALSE, 0);
- gtk_widget_show_all(credit_card_widget);
-}
+int AutoFillDialog::GetSelectionType() {
+ int state = SELECTION_EMPTY;
+ gtk_tree_selection_selected_foreach(
+ gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_)), RowIteratorFunction,
+ &state);
+ return state;
+}
+
+void AutoFillDialog::AddAddressToTree(const AutoFillProfile& profile,
+ GtkTreeIter* iter) {
+ gtk_list_store_append(list_store_, iter);
+ gtk_list_store_set(
+ list_store_, iter,
+ COL_WEIGHT, PANGO_WEIGHT_NORMAL,
+ COL_WEIGHT_SET, TRUE,
+ COL_TITLE, UTF16ToUTF8(profile.PreviewSummary()).c_str(),
+ -1);
+}
+
+void AutoFillDialog::AddCreditCardToTree(const CreditCard& credit_card,
+ GtkTreeIter* iter) {
+ gtk_list_store_append(list_store_, iter);
+ gtk_list_store_set(
+ list_store_, iter,
+ COL_WEIGHT, PANGO_WEIGHT_NORMAL,
+ COL_WEIGHT_SET, TRUE,
+ COL_TITLE, UTF16ToUTF8(credit_card.PreviewSummary()).c_str(),
+ -1);
+}
+
+void AutoFillDialog::GetSelectedEntries(
+ std::vector<AutoFillProfile*>* profiles,
+ std::vector<CreditCard*>* cards) {
+ std::set<int> selection;
+ gtk_tree::GetSelectedIndices(
+ gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_)), &selection);
+
+ for (std::set<int>::const_iterator i = selection.begin();
+ i != selection.end(); ++i) {
+ // 2 is the number of header rows.
+ int index = *i - 2;
+ if (index >= 0 &&
+ index < static_cast<int>(personal_data_->profiles().size())) {
+ profiles->push_back(personal_data_->profiles()[index]);
+ continue;
+ }
-int AutoFillDialog::FindIndexOfAddress(string16 billing_address) {
- int index = 0;
- for (std::vector<AddressWidgets>::const_iterator iter =
- address_widgets_.begin();
- iter != address_widgets_.end(); ++iter, ++index) {
- std::string text = gtk_entry_get_text(GTK_ENTRY(iter->label));
- if (UTF8ToUTF16(text) == billing_address)
- return index;
+ // Empty row, header and separator are next.
+ index -= profile_count_ + 3;
+ if (index >= 0 && index <
+ static_cast<int>(personal_data_->credit_cards().size())) {
+ cards->push_back(personal_data_->credit_cards()[index]);
+ }
}
-
- return -1;
}
+} // namespace
+
///////////////////////////////////////////////////////////////////////////////
// Factory/finder method:
@@ -981,11 +660,7 @@ void ShowAutoFillDialog(gfx::NativeView parent,
Profile* profile) {
DCHECK(profile);
- if (!dialog) {
- dialog = new AutoFillDialog(observer,
- profile->GetPersonalDataManager(),
- NULL,
- NULL);
- }
+ if (!dialog)
+ dialog = new AutoFillDialog(profile, observer);
dialog->Show();
}
diff --git a/chrome/browser/autofill/credit_card.cc b/chrome/browser/autofill/credit_card.cc
index dcebb32..4a9483e 100644
--- a/chrome/browser/autofill/credit_card.cc
+++ b/chrome/browser/autofill/credit_card.cc
@@ -39,7 +39,8 @@ CreditCard::CreditCard(const CreditCard& card) : FormGroup() {
CreditCard::CreditCard()
: expiration_month_(0),
- expiration_year_(0) {
+ expiration_year_(0),
+ unique_id_(0) {
}
FormGroup* CreditCard::Clone() const {
diff --git a/chrome/browser/autofill/personal_data_manager.cc b/chrome/browser/autofill/personal_data_manager.cc
index bc38f8e..ae664d1 100644
--- a/chrome/browser/autofill/personal_data_manager.cc
+++ b/chrome/browser/autofill/personal_data_manager.cc
@@ -71,16 +71,7 @@ void PersonalDataManager::OnWebDataServiceRequestDone(
// If both requests have responded, then all personal data is loaded.
if (pending_profiles_query_ == 0 && pending_creditcards_query_ == 0) {
is_data_loaded_ = true;
- // Copy is needed as observer can unsubscribe itself when notified.
- std::vector<PersonalDataManager::Observer*> temporary_copy;
- temporary_copy.resize(observers_.size());
- std::copy(observers_.begin(), observers_.end(), temporary_copy.begin());
- for (std::vector<PersonalDataManager::Observer*>::iterator
- iter = temporary_copy.begin();
- iter != temporary_copy.end();
- ++iter) {
- (*iter)->OnPersonalDataLoaded();
- }
+ FOR_EACH_OBSERVER(Observer, observers_, OnPersonalDataLoaded());
}
}
@@ -103,29 +94,15 @@ void PersonalDataManager::OnAutoFillDialogApply(
}
void PersonalDataManager::SetObserver(PersonalDataManager::Observer* observer) {
- for (std::vector<PersonalDataManager::Observer*>::iterator
- iter = observers_.begin();
- iter != observers_.end();
- ++iter) {
- if (*iter == observer) {
- // Already have this observer.
- return;
- }
- }
- observers_.push_back(observer);
+ // TODO: RemoveObserver is for compatability with old code, it should be
+ // nuked.
+ observers_.RemoveObserver(observer);
+ observers_.AddObserver(observer);
}
void PersonalDataManager::RemoveObserver(
PersonalDataManager::Observer* observer) {
- for (std::vector<PersonalDataManager::Observer*>::iterator
- iter = observers_.begin();
- iter != observers_.end();
- ++iter) {
- if (*iter == observer) {
- observers_.erase(iter);
- return;
- }
- }
+ observers_.RemoveObserver(observer);
}
bool PersonalDataManager::ImportFormData(
@@ -315,6 +292,8 @@ void PersonalDataManager::SetProfiles(std::vector<AutoFillProfile>* profiles) {
// Read our writes to ensure consistency with the database.
Refresh();
+
+ FOR_EACH_OBSERVER(Observer, observers_, OnPersonalDataChanged());
}
void PersonalDataManager::SetCreditCards(
@@ -384,6 +363,8 @@ void PersonalDataManager::SetCreditCards(
iter != credit_cards->end(); ++iter) {
credit_cards_.push_back(new CreditCard(*iter));
}
+
+ FOR_EACH_OBSERVER(Observer, observers_, OnPersonalDataChanged());
}
void PersonalDataManager::GetPossibleFieldTypes(const string16& text,
diff --git a/chrome/browser/autofill/personal_data_manager.h b/chrome/browser/autofill/personal_data_manager.h
index 8c5a04e..50455ec 100644
--- a/chrome/browser/autofill/personal_data_manager.h
+++ b/chrome/browser/autofill/personal_data_manager.h
@@ -8,6 +8,7 @@
#include <set>
#include <vector>
+#include "base/observer_list.h"
#include "base/ref_counted.h"
#include "base/scoped_ptr.h"
#include "base/scoped_vector.h"
@@ -36,8 +37,13 @@ class PersonalDataManager
class Observer {
public:
// Notifies the observer that the PersonalDataManager has finished loading.
+ // TODO: OnPersonalDataLoaded should be nuked in favor of only
+ // OnPersonalDataChanged.
virtual void OnPersonalDataLoaded() = 0;
+ // Notifies the observer that the PersonalDataManager changed in some way.
+ virtual void OnPersonalDataChanged() {}
+
protected:
virtual ~Observer() {}
};
@@ -240,8 +246,8 @@ class PersonalDataManager
WebDataService::Handle pending_profiles_query_;
WebDataService::Handle pending_creditcards_query_;
- // The observers. This can be empty.
- std::vector<PersonalDataManager::Observer*> observers_;
+ // The observers.
+ ObserverList<Observer> observers_;
DISALLOW_COPY_AND_ASSIGN(PersonalDataManager);
};
diff --git a/chrome/browser/gtk/keyword_editor_view.cc b/chrome/browser/gtk/keyword_editor_view.cc
index e18b7e4..6786f45 100644
--- a/chrome/browser/gtk/keyword_editor_view.cc
+++ b/chrome/browser/gtk/keyword_editor_view.cc
@@ -417,9 +417,9 @@ gboolean KeywordEditorView::OnCheckRowIsSeparator(GtkTreeModel* model,
}
// static
-gboolean KeywordEditorView::OnSelectionFilter(GtkTreeSelection *selection,
- GtkTreeModel *model,
- GtkTreePath *path,
+gboolean KeywordEditorView::OnSelectionFilter(GtkTreeSelection* selection,
+ GtkTreeModel* model,
+ GtkTreePath* path,
gboolean path_currently_selected,
gpointer user_data) {
GtkTreeIter iter;
@@ -434,7 +434,7 @@ gboolean KeywordEditorView::OnSelectionFilter(GtkTreeSelection *selection,
// static
void KeywordEditorView::OnSelectionChanged(
- GtkTreeSelection *selection, KeywordEditorView* editor) {
+ GtkTreeSelection* selection, KeywordEditorView* editor) {
editor->EnableControls();
}
diff --git a/chrome/browser/gtk/keyword_editor_view.h b/chrome/browser/gtk/keyword_editor_view.h
index a5d5f9e..3ce4b49 100644
--- a/chrome/browser/gtk/keyword_editor_view.h
+++ b/chrome/browser/gtk/keyword_editor_view.h
@@ -104,7 +104,7 @@ class KeywordEditorView : public TableModelObserver,
gpointer user_data);
// Callback for when user selects something.
- static void OnSelectionChanged(GtkTreeSelection *selection,
+ static void OnSelectionChanged(GtkTreeSelection* selection,
KeywordEditorView* editor);
// Callbacks for user actions modifying the table.
diff --git a/chrome/browser/gtk/options/content_page_gtk.cc b/chrome/browser/gtk/options/content_page_gtk.cc
index 01672d0..540766c 100644
--- a/chrome/browser/gtk/options/content_page_gtk.cc
+++ b/chrome/browser/gtk/options/content_page_gtk.cc
@@ -115,8 +115,6 @@ ContentPageGtk::ContentPageGtk(Profile* profile)
// Add preferences observers.
ask_to_save_passwords_.Init(prefs::kPasswordManagerEnabled,
profile->GetPrefs(), this);
- enable_form_autofill_.Init(prefs::kAutoFillEnabled,
- profile->GetPrefs(), this);
if (browser_defaults::kCanToggleSystemTitleBar) {
use_custom_chrome_frame_.Init(prefs::kUseCustomChromeFrame,
@@ -164,17 +162,6 @@ void ContentPageGtk::NotifyPrefChanged(const std::wstring* pref_name) {
GTK_TOGGLE_BUTTON(passwords_neversave_radio_), TRUE);
}
}
- if (!pref_name || *pref_name == prefs::kAutoFillEnabled) {
- if (enable_form_autofill_.GetValue()) {
- gtk_toggle_button_set_active(
- GTK_TOGGLE_BUTTON(form_autofill_enable_radio_), TRUE);
- gtk_widget_set_sensitive(autofill_button_, TRUE);
- } else {
- gtk_toggle_button_set_active(
- GTK_TOGGLE_BUTTON(form_autofill_disable_radio_), TRUE);
- gtk_widget_set_sensitive(autofill_button_, FALSE);
- }
- }
if (browser_defaults::kCanToggleSystemTitleBar &&
(!pref_name || *pref_name == prefs::kUseCustomChromeFrame)) {
if (use_custom_chrome_frame_.GetValue()) {
@@ -249,35 +236,16 @@ GtkWidget* ContentPageGtk::InitPasswordSavingGroup() {
GtkWidget* ContentPageGtk::InitFormAutoFillGroup() {
GtkWidget* vbox = gtk_vbox_new(FALSE, gtk_util::kControlSpacing);
- // Enable radio button.
- form_autofill_enable_radio_ = gtk_radio_button_new_with_label(NULL,
- l10n_util::GetStringUTF8(IDS_OPTIONS_AUTOFILL_ENABLE).c_str());
- g_signal_connect(G_OBJECT(form_autofill_enable_radio_), "toggled",
- G_CALLBACK(OnAutoFillRadioToggledThunk), this);
- gtk_box_pack_start(GTK_BOX(vbox), form_autofill_enable_radio_, FALSE,
- FALSE, 0);
-
- // Disable radio button.
- form_autofill_disable_radio_ = gtk_radio_button_new_with_label_from_widget(
- GTK_RADIO_BUTTON(form_autofill_enable_radio_),
- l10n_util::GetStringUTF8(IDS_OPTIONS_AUTOFILL_DISABLE).c_str());
- g_signal_connect(G_OBJECT(form_autofill_disable_radio_), "toggled",
- G_CALLBACK(OnAutoFillRadioToggledThunk), this);
- gtk_box_pack_start(GTK_BOX(vbox), form_autofill_disable_radio_, FALSE,
- FALSE, 0);
-
GtkWidget* button_hbox = gtk_hbox_new(FALSE, gtk_util::kControlSpacing);
gtk_container_add(GTK_CONTAINER(vbox), button_hbox);
// AutoFill button.
- autofill_button_ = gtk_button_new_with_label(
+ GtkWidget* autofill_button = gtk_button_new_with_label(
l10n_util::GetStringUTF8(IDS_AUTOFILL_OPTIONS).c_str());
- if (!profile()->GetPrefs()->GetBoolean(prefs::kAutoFillEnabled))
- gtk_widget_set_sensitive(autofill_button_, FALSE);
- g_signal_connect(G_OBJECT(autofill_button_), "clicked",
+ g_signal_connect(G_OBJECT(autofill_button), "clicked",
G_CALLBACK(OnAutoFillButtonClickedThunk), this);
- gtk_box_pack_start(GTK_BOX(button_hbox), autofill_button_, FALSE, FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(button_hbox), autofill_button, FALSE, FALSE, 0);
return vbox;
}
@@ -560,30 +528,6 @@ void ContentPageGtk::OnPasswordRadioToggled(GtkWidget* widget) {
ask_to_save_passwords_.SetValue(enabled);
}
-void ContentPageGtk::OnAutoFillRadioToggled(GtkWidget* widget) {
- if (initializing_)
- return;
-
- // We get two signals when selecting a radio button, one for the old radio
- // being toggled off and one for the new one being toggled on. Ignore the
- // signal for the toggling off the old button.
- if (!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
- return;
-
- bool enabled = gtk_toggle_button_get_active(
- GTK_TOGGLE_BUTTON(form_autofill_enable_radio_));
- if (enabled) {
- UserMetricsRecordAction(UserMetricsAction("Options_FormAutofill_Enable"),
- profile()->GetPrefs());
- gtk_widget_set_sensitive(autofill_button_, TRUE);
- } else {
- UserMetricsRecordAction(UserMetricsAction("Options_FormAutofill_Disable"),
- profile()->GetPrefs());
- gtk_widget_set_sensitive(autofill_button_, FALSE);
- }
- enable_form_autofill_.SetValue(enabled);
-}
-
void ContentPageGtk::OnSyncStartStopButtonClicked(GtkWidget* widget) {
DCHECK(sync_service_);
diff --git a/chrome/browser/gtk/options/content_page_gtk.h b/chrome/browser/gtk/options/content_page_gtk.h
index ec1ea01..024eec8 100644
--- a/chrome/browser/gtk/options/content_page_gtk.h
+++ b/chrome/browser/gtk/options/content_page_gtk.h
@@ -57,7 +57,6 @@ class ContentPageGtk : public OptionsPageBase,
CHROMEGTK_CALLBACK_0(ContentPageGtk, void, OnShowPasswordsButtonClicked);
CHROMEGTK_CALLBACK_0(ContentPageGtk, void, OnPasswordRadioToggled);
CHROMEGTK_CALLBACK_0(ContentPageGtk, void, OnAutoFillButtonClicked);
- CHROMEGTK_CALLBACK_0(ContentPageGtk, void, OnAutoFillRadioToggled);
CHROMEGTK_CALLBACK_0(ContentPageGtk, void, OnSyncStartStopButtonClicked);
CHROMEGTK_CALLBACK_0(ContentPageGtk, void, OnSyncCustomizeButtonClicked);
CHROMEGTK_CALLBACK_0(ContentPageGtk, void, OnSyncActionLinkClicked);
@@ -68,11 +67,6 @@ class ContentPageGtk : public OptionsPageBase,
GtkWidget* passwords_asktosave_radio_;
GtkWidget* passwords_neversave_radio_;
- // Widgets for the Form AutoFill group.
- GtkWidget* form_autofill_enable_radio_;
- GtkWidget* form_autofill_disable_radio_;
- GtkWidget* autofill_button_;
-
// Widgets for the Appearance group.
GtkWidget* system_title_bar_show_radio_;
GtkWidget* system_title_bar_hide_radio_;
@@ -97,7 +91,6 @@ class ContentPageGtk : public OptionsPageBase,
// Pref members.
BooleanPrefMember ask_to_save_passwords_;
- BooleanPrefMember enable_form_autofill_;
BooleanPrefMember use_custom_chrome_frame_;
// Flag to ignore gtk callbacks while we are loading prefs, to avoid
diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi
index 59ed5f0..8d50e2a 100644
--- a/chrome/chrome_browser.gypi
+++ b/chrome/chrome_browser.gypi
@@ -145,6 +145,7 @@
'browser/autofill/autofill_manager.h',
'browser/autofill/autofill_profile.cc',
'browser/autofill/autofill_profile.h',
+ 'browser/autofill/auto_fill_editor_gtk.cc',
'browser/autofill/autofill_text_field_mac.h',
'browser/autofill/autofill_text_field_mac.mm',
'browser/autofill/autofill_type.cc',