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 /chrome/browser/autofill/autofill_dialog_gtk.cc | |
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
Diffstat (limited to 'chrome/browser/autofill/autofill_dialog_gtk.cc')
-rw-r--r-- | chrome/browser/autofill/autofill_dialog_gtk.cc | 1239 |
1 files changed, 457 insertions, 782 deletions
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(); } |