diff options
author | sky@chromium.org <sky@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-07-14 03:27:11 +0000 |
---|---|---|
committer | sky@chromium.org <sky@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-07-14 03:27:11 +0000 |
commit | 548adcb9cb9343fe542f82cc35ffaa8f8279dfb6 (patch) | |
tree | b32f0079645d72525d009e718751b58ab8b02bd5 | |
parent | e8c11e4801e7f4ca0c5f607d7bb489e3b1098a0c (diff) | |
download | chromium_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.cc | 810 | ||||
-rw-r--r-- | chrome/browser/autofill/autofill_dialog.h | 10 | ||||
-rw-r--r-- | chrome/browser/autofill/autofill_dialog_gtk.cc | 1239 | ||||
-rw-r--r-- | chrome/browser/autofill/credit_card.cc | 3 | ||||
-rw-r--r-- | chrome/browser/autofill/personal_data_manager.cc | 39 | ||||
-rw-r--r-- | chrome/browser/autofill/personal_data_manager.h | 10 | ||||
-rw-r--r-- | chrome/browser/gtk/keyword_editor_view.cc | 8 | ||||
-rw-r--r-- | chrome/browser/gtk/keyword_editor_view.h | 2 | ||||
-rw-r--r-- | chrome/browser/gtk/options/content_page_gtk.cc | 62 | ||||
-rw-r--r-- | chrome/browser/gtk/options/content_page_gtk.h | 7 | ||||
-rw-r--r-- | chrome/chrome_browser.gypi | 1 |
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', |