| Index: chrome/browser/autofill/autofill_dialog_gtk.cc
|
| diff --git a/chrome/browser/autofill/autofill_dialog_gtk.cc b/chrome/browser/autofill/autofill_dialog_gtk.cc
|
| index 48a6903187765a71fb858841f13287039be00dcb..ec8a1fec68520a4c7826cfe8db1ba848dd82d4e1 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();
|
| }
|
|
|