| OLD | NEW |
| 1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chrome/browser/autofill/autofill_dialog.h" | 5 #include "chrome/browser/autofill/autofill_dialog.h" |
| 6 | 6 |
| 7 #include <gtk/gtk.h> | 7 #include <gtk/gtk.h> |
| 8 | 8 |
| 9 #include <algorithm> |
| 9 #include <string> | 10 #include <string> |
| 10 #include <vector> | 11 #include <vector> |
| 11 | 12 |
| 13 #include "app/gtk_signal.h" |
| 12 #include "app/l10n_util.h" | 14 #include "app/l10n_util.h" |
| 13 #include "base/logging.h" | 15 #include "base/logging.h" |
| 14 #include "base/message_loop.h" | 16 #include "base/message_loop.h" |
| 15 #include "base/utf_string_conversions.h" | 17 #include "base/utf_string_conversions.h" |
| 16 #include "chrome/browser/autofill/autofill_manager.h" | 18 #include "chrome/browser/autofill/autofill_manager.h" |
| 17 #include "chrome/browser/autofill/autofill_profile.h" | 19 #include "chrome/browser/autofill/autofill_profile.h" |
| 18 #include "chrome/browser/autofill/credit_card.h" | 20 #include "chrome/browser/autofill/credit_card.h" |
| 19 #include "chrome/browser/autofill/form_group.h" | 21 #include "chrome/browser/autofill/form_group.h" |
| 20 #include "chrome/browser/autofill/personal_data_manager.h" | 22 #include "chrome/browser/autofill/personal_data_manager.h" |
| 21 #include "chrome/browser/autofill/phone_number.h" | 23 #include "chrome/browser/autofill/phone_number.h" |
| 22 #include "chrome/browser/browser.h" | 24 #include "chrome/browser/browser.h" |
| 23 #include "chrome/browser/browser_list.h" | 25 #include "chrome/browser/browser_list.h" |
| 24 #include "chrome/browser/gtk/gtk_chrome_link_button.h" | 26 #include "chrome/browser/gtk/gtk_chrome_link_button.h" |
| 27 #include "chrome/browser/gtk/gtk_tree.h" |
| 25 #include "chrome/browser/gtk/gtk_util.h" | 28 #include "chrome/browser/gtk/gtk_util.h" |
| 26 #include "chrome/browser/gtk/options/options_layout_gtk.h" | 29 #include "chrome/browser/metrics/user_metrics.h" |
| 30 #include "chrome/browser/pref_member.h" |
| 31 #include "chrome/browser/pref_service.h" |
| 27 #include "chrome/browser/profile.h" | 32 #include "chrome/browser/profile.h" |
| 33 #include "chrome/common/notification_details.h" |
| 34 #include "chrome/common/notification_observer.h" |
| 35 #include "chrome/common/notification_type.h" |
| 28 #include "chrome/common/pref_names.h" | 36 #include "chrome/common/pref_names.h" |
| 29 #include "gfx/gtk_util.h" | 37 #include "gfx/gtk_util.h" |
| 30 #include "grit/chromium_strings.h" | 38 #include "grit/chromium_strings.h" |
| 31 #include "grit/generated_resources.h" | 39 #include "grit/generated_resources.h" |
| 32 #include "grit/locale_settings.h" | 40 #include "grit/locale_settings.h" |
| 33 | 41 |
| 42 // Shows the editor for adding/editing an AutoFillProfile. If |
| 43 // |auto_fill_profile| is NULL, a new AutoFillProfile should be created. |
| 44 void ShowAutoFillProfileEditor(gfx::NativeView parent, |
| 45 AutoFillDialogObserver* observer, |
| 46 Profile* profile, |
| 47 AutoFillProfile* auto_fill_profile); |
| 48 |
| 49 // Shows the editor for adding/editing a CreditCard. If |credit_card| is NULL, a |
| 50 // new CreditCard should be created. |
| 51 void ShowAutoFillCreditCardEditor(gfx::NativeView parent, |
| 52 AutoFillDialogObserver* observer, |
| 53 Profile* profile, |
| 54 CreditCard* credit_card); |
| 55 |
| 34 namespace { | 56 namespace { |
| 35 | 57 |
| 36 // The name of the object property used to store an entry widget pointer on | 58 // The resource id for the 'About Autofill' link button. |
| 37 // another widget. | 59 const gint kAutoFillDialogAboutLink = 1; |
| 38 const char kButtonDataKey[] = "label-entry"; | |
| 39 | |
| 40 // How far we indent dialog widgets, in pixels. | |
| 41 const int kAutoFillDialogIndent = 5; | |
| 42 | |
| 43 // The resource id for the 'Learn more' link button. | |
| 44 const gint kAutoFillDialogLearnMoreLink = 1; | |
| 45 | |
| 46 // All of these widgets are GtkEntrys. | |
| 47 typedef struct _AddressWidgets { | |
| 48 GtkWidget* label; | |
| 49 GtkWidget* full_name; | |
| 50 GtkWidget* email; | |
| 51 GtkWidget* company_name; | |
| 52 GtkWidget* address_line1; | |
| 53 GtkWidget* address_line2; | |
| 54 GtkWidget* city; | |
| 55 GtkWidget* state; | |
| 56 GtkWidget* zipcode; | |
| 57 GtkWidget* country; | |
| 58 GtkWidget* phone; | |
| 59 GtkWidget* fax; | |
| 60 } AddressWidgets; | |
| 61 | |
| 62 // All of these widgets are GtkEntrys except for |billing_address|, which is | |
| 63 // a GtkComboBox. | |
| 64 typedef struct _CreditCardWidgets { | |
| 65 GtkWidget* label; | |
| 66 GtkWidget* name_on_card; | |
| 67 GtkWidget* card_number; | |
| 68 GtkWidget* expiration_month; | |
| 69 GtkWidget* expiration_year; | |
| 70 GtkWidget* billing_address; | |
| 71 GtkWidget* phone; | |
| 72 string16 original_card_number; | |
| 73 } CreditCardWidgets; | |
| 74 | |
| 75 // Adds an alignment around |widget| which indents the widget by |offset|. | |
| 76 GtkWidget* IndentWidget(GtkWidget* widget, int offset) { | |
| 77 GtkWidget* alignment = gtk_alignment_new(0, 0, 0, 0); | |
| 78 gtk_alignment_set_padding(GTK_ALIGNMENT(alignment), 0, 0, | |
| 79 offset, 0); | |
| 80 gtk_container_add(GTK_CONTAINER(alignment), widget); | |
| 81 return alignment; | |
| 82 } | |
| 83 | |
| 84 // Makes sure we use the gtk theme colors by loading the base color of an entry | |
| 85 // widget. | |
| 86 void SetWhiteBackground(GtkWidget* widget) { | |
| 87 GtkWidget* entry = gtk_entry_new(); | |
| 88 gtk_widget_ensure_style(entry); | |
| 89 GtkStyle* style = gtk_widget_get_style(entry); | |
| 90 gtk_widget_modify_bg(widget, GTK_STATE_NORMAL, | |
| 91 &style->base[GTK_STATE_NORMAL]); | |
| 92 gtk_widget_destroy(entry); | |
| 93 } | |
| 94 | |
| 95 string16 GetEntryText(GtkWidget* entry) { | |
| 96 return UTF8ToUTF16(gtk_entry_get_text(GTK_ENTRY(entry))); | |
| 97 } | |
| 98 | |
| 99 void SetEntryText(GtkWidget* entry, const string16& text) { | |
| 100 gtk_entry_set_text(GTK_ENTRY(entry), UTF16ToUTF8(text).c_str()); | |
| 101 } | |
| 102 | |
| 103 void SetButtonData(GtkWidget* widget, GtkWidget* entry) { | |
| 104 g_object_set_data(G_OBJECT(widget), kButtonDataKey, entry); | |
| 105 } | |
| 106 | |
| 107 GtkWidget* GetButtonData(GtkWidget* widget) { | |
| 108 return static_cast<GtkWidget*>( | |
| 109 g_object_get_data(G_OBJECT(widget), kButtonDataKey)); | |
| 110 } | |
| 111 | |
| 112 //////////////////////////////////////////////////////////////////////////////// | |
| 113 // Form Table helpers. | |
| 114 // | |
| 115 // The following functions can be used to create a form with labeled widgets. | |
| 116 // | |
| 117 | |
| 118 // Creates a form table with dimensions |rows| x |cols|. | |
| 119 GtkWidget* InitFormTable(int rows, int cols) { | |
| 120 // We have two table rows per form table row. | |
| 121 GtkWidget* table = gtk_table_new(rows * 2, cols, false); | |
| 122 gtk_table_set_row_spacings(GTK_TABLE(table), gtk_util::kControlSpacing); | |
| 123 gtk_table_set_col_spacings(GTK_TABLE(table), gtk_util::kFormControlSpacing); | |
| 124 | |
| 125 // Leave no space between the label and the widget. | |
| 126 for (int i = 0; i < rows; i++) | |
| 127 gtk_table_set_row_spacing(GTK_TABLE(table), i * 2, 0); | |
| 128 | |
| 129 return table; | |
| 130 } | |
| 131 | |
| 132 // Sets the label of the form widget at |row|,|col|. The label is |len| columns | |
| 133 // long. | |
| 134 void FormTableSetLabel( | |
| 135 GtkWidget* table, int row, int col, int len, int label_id) { | |
| 136 // We have two table rows per form table row. | |
| 137 row *= 2; | |
| 138 | |
| 139 std::string text; | |
| 140 if (label_id) | |
| 141 text = l10n_util::GetStringUTF8(label_id); | |
| 142 GtkWidget* label = gtk_label_new(text.c_str()); | |
| 143 gtk_misc_set_alignment(GTK_MISC(label), 0, 0); | |
| 144 gtk_table_attach(GTK_TABLE(table), label, | |
| 145 col, col + len, // Left col, right col. | |
| 146 row, row + 1, // Top row, bottom row. | |
| 147 GTK_FILL, GTK_FILL, // Options. | |
| 148 0, 0); // Padding. | |
| 149 } | |
| 150 | |
| 151 // Sets the form widget at |row|,|col|. The widget fills up |len| columns. If | |
| 152 // |expand| is true, the widget will expand to fill all of the extra space in | |
| 153 // the table row. | |
| 154 void FormTableSetWidget(GtkWidget* table, | |
| 155 GtkWidget* widget, | |
| 156 int row, int col, | |
| 157 int len, bool expand) { | |
| 158 const GtkAttachOptions expand_option = | |
| 159 static_cast<GtkAttachOptions>(GTK_FILL | GTK_EXPAND); | |
| 160 GtkAttachOptions xoption = (expand) ? expand_option : GTK_FILL; | |
| 161 | |
| 162 // We have two table rows per form table row. | |
| 163 row *= 2; | |
| 164 gtk_table_attach(GTK_TABLE(table), widget, | |
| 165 col, col + len, // Left col, right col. | |
| 166 row + 1, row + 2, // Top row, bottom row. | |
| 167 xoption, GTK_FILL, // Options. | |
| 168 0, 0); // Padding. | |
| 169 } | |
| 170 | |
| 171 // Adds a labeled entry box to the form table at |row|,|col|. The entry widget | |
| 172 // fills up |len| columns. The returned widget is owned by |table| and should | |
| 173 // not be destroyed. | |
| 174 GtkWidget* FormTableAddEntry( | |
| 175 GtkWidget* table, int row, int col, int len, int label_id) { | |
| 176 FormTableSetLabel(table, row, col, len, label_id); | |
| 177 | |
| 178 GtkWidget* entry = gtk_entry_new(); | |
| 179 FormTableSetWidget(table, entry, row, col, len, false); | |
| 180 | |
| 181 return entry; | |
| 182 } | |
| 183 | |
| 184 // Adds a labeled entry box to the form table that will expand to fill extra | |
| 185 // space in the table row. | |
| 186 GtkWidget* FormTableAddExpandedEntry( | |
| 187 GtkWidget* table, int row, int col, int len, int label_id) { | |
| 188 FormTableSetLabel(table, row, col, len, label_id); | |
| 189 | |
| 190 GtkWidget* entry = gtk_entry_new(); | |
| 191 FormTableSetWidget(table, entry, row, col, len, true); | |
| 192 | |
| 193 return entry; | |
| 194 } | |
| 195 | |
| 196 // Adds a sized entry box to the form table. The entry widget width is set to | |
| 197 // |char_len|. | |
| 198 GtkWidget* FormTableAddSizedEntry( | |
| 199 GtkWidget* table, int row, int col, int char_len, int label_id) { | |
| 200 GtkWidget* entry = FormTableAddEntry(table, row, col, 1, label_id); | |
| 201 gtk_entry_set_width_chars(GTK_ENTRY(entry), char_len); | |
| 202 return entry; | |
| 203 } | |
| 204 | |
| 205 // Like FormTableAddEntry, but connects to the 'changed' signal. |changed| is a | |
| 206 // callback to handle the 'changed' signal that is emitted when the user edits | |
| 207 // the entry. |expander| is the expander widget that will be sent to the | |
| 208 // callback as the user data. | |
| 209 GtkWidget* FormTableAddLabelEntry( | |
| 210 GtkWidget* table, int row, int col, int len, int label_id, | |
| 211 GtkWidget* expander, GCallback changed) { | |
| 212 FormTableSetLabel(table, row, col, len, label_id); | |
| 213 | |
| 214 GtkWidget* entry = gtk_entry_new(); | |
| 215 g_signal_connect(entry, "changed", changed, expander); | |
| 216 FormTableSetWidget(table, entry, row, col, len, false); | |
| 217 | |
| 218 return entry; | |
| 219 } | |
| 220 | |
| 221 } // namespace | |
| 222 | 60 |
| 223 //////////////////////////////////////////////////////////////////////////////// | 61 //////////////////////////////////////////////////////////////////////////////// |
| 224 // AutoFillDialog | 62 // AutoFillDialog |
| 225 // | 63 // |
| 226 // The contents of the AutoFill dialog. This dialog allows users to add, edit | 64 // The contents of the AutoFill dialog. This dialog allows users to add, edit |
| 227 // and remove AutoFill profiles. | 65 // and remove AutoFill profiles. |
| 228 class AutoFillDialog : public PersonalDataManager::Observer { | 66 class AutoFillDialog : public PersonalDataManager::Observer, |
| 67 public NotificationObserver { |
| 229 public: | 68 public: |
| 230 AutoFillDialog(AutoFillDialogObserver* observer, | 69 // Identifiers for columns in the store. |
| 231 PersonalDataManager* personal_data_manager, | 70 enum Column { |
| 232 AutoFillProfile* imported_profile, | 71 COL_TITLE = 0, |
| 233 CreditCard* imported_credit_card); | 72 COL_IS_HEADER, |
| 73 COL_IS_SEPARATOR, // Identifies an empty row used to reserve visual space. |
| 74 COL_WEIGHT, |
| 75 COL_WEIGHT_SET, |
| 76 COL_COUNT |
| 77 }; |
| 78 |
| 79 // Used to identify the selection. See GetSelectionType. |
| 80 enum SelectionType { |
| 81 // Nothing is selected. |
| 82 SELECTION_EMPTY = 0, |
| 83 |
| 84 // At least one header/separator row is selected. |
| 85 SELECTION_HEADER = 1 << 0, |
| 86 |
| 87 // At least one non-header/separator row is selected. |
| 88 SELECTION_SINGLE = 1 << 1, |
| 89 |
| 90 // Multiple non-header/separator rows are selected. |
| 91 SELECTION_MULTI = 1 << 2, |
| 92 }; |
| 93 |
| 94 AutoFillDialog(Profile* profile, AutoFillDialogObserver* observer); |
| 234 ~AutoFillDialog(); | 95 ~AutoFillDialog(); |
| 235 | 96 |
| 236 // PersonalDataManager::Observer implementation: | 97 // PersonalDataManager::Observer implementation: |
| 237 void OnPersonalDataLoaded(); | 98 void OnPersonalDataLoaded(); |
| 99 void OnPersonalDataChanged(); |
| 100 |
| 101 // NotificationObserver implementation: |
| 102 virtual void Observe(NotificationType type, |
| 103 const NotificationSource& source, |
| 104 const NotificationDetails& details); |
| 238 | 105 |
| 239 // Shows the AutoFill dialog. | 106 // Shows the AutoFill dialog. |
| 240 void Show(); | 107 void Show(); |
| 241 | 108 |
| 242 private: | 109 private: |
| 243 // 'destroy' signal handler. Calls DeleteSoon on the global singleton dialog | 110 // 'destroy' signal handler. Calls DeleteSoon on the global singleton dialog |
| 244 // object. | 111 // object. |
| 245 static void OnDestroy(GtkWidget* widget, AutoFillDialog* autofill_dialog); | 112 CHROMEGTK_CALLBACK_0(AutoFillDialog, void, OnDestroy); |
| 246 | 113 |
| 247 // 'response' signal handler. Notifies the AutoFillDialogObserver that new | 114 // 'response' signal handler. Notifies the AutoFillDialogObserver that new |
| 248 // data is available if the response is GTK_RESPONSE_APPLY or GTK_RESPONSE_OK. | 115 // data is available if the response is GTK_RESPONSE_APPLY or GTK_RESPONSE_OK. |
| 249 // We close the dialog if the response is GTK_RESPONSE_OK or | 116 // We close the dialog if the response is GTK_RESPONSE_OK or |
| 250 // GTK_RESPONSE_CANCEL. | 117 // GTK_RESPONSE_CANCEL. |
| 251 static void OnResponse(GtkDialog* dialog, gint response_id, | 118 CHROMEG_CALLBACK_1(AutoFillDialog, void, OnResponse, GtkDialog*, gint); |
| 252 AutoFillDialog* autofill_dialog); | |
| 253 | 119 |
| 254 // 'clicked' signal handler. Adds a new address. | 120 CHROMEGTK_CALLBACK_0(AutoFillDialog, void, OnAutoFillCheckToggled); |
| 255 static void OnAddAddressClicked(GtkButton* button, AutoFillDialog* dialog); | 121 CHROMEG_CALLBACK_2(AutoFillDialog, void, OnRowActivated, GtkTreeView*, |
| 256 | 122 GtkTreePath*, GtkTreeViewColumn*); |
| 257 // 'clicked' signal handler. Adds a new credit card. | 123 CHROMEG_CALLBACK_0(AutoFillDialog, void, OnSelectionChanged, |
| 258 static void OnAddCreditCardClicked(GtkButton* button, AutoFillDialog* dialog); | 124 GtkTreeSelection*); |
| 259 | 125 CHROMEG_CALLBACK_1(AutoFillDialog, gboolean, OnCheckRowIsSeparator, |
| 260 // 'clicked' signal handler. Deletes the associated address. | 126 GtkTreeModel*, GtkTreeIter*); |
| 261 static void OnDeleteAddressClicked(GtkButton* button, AutoFillDialog* dialog); | 127 CHROMEGTK_CALLBACK_0(AutoFillDialog, void, OnAddAddress); |
| 262 | 128 CHROMEGTK_CALLBACK_0(AutoFillDialog, void, OnAddCreditCard); |
| 263 // 'clicked' signal handler. Deletes the associated credit card. | 129 CHROMEGTK_CALLBACK_0(AutoFillDialog, void, OnEdit); |
| 264 static void OnDeleteCreditCardClicked(GtkButton* button, | 130 CHROMEGTK_CALLBACK_0(AutoFillDialog, void, OnRemove); |
| 265 AutoFillDialog* dialog); | 131 CHROMEG_CALLBACK_3(AutoFillDialog, gboolean, OnSelectionFilter, |
| 266 | 132 GtkTreeSelection*, GtkTreeModel*, GtkTreePath*, gboolean); |
| 267 // 'changed' signal handler. Updates the title of the expander widget with | |
| 268 // the contents of the label entry widget. | |
| 269 static void OnLabelChanged(GtkEntry* label, GtkWidget* expander); | |
| 270 | 133 |
| 271 // Opens the 'Learn more' link in a new foreground tab. | 134 // Opens the 'Learn more' link in a new foreground tab. |
| 272 void OnLinkActivated(); | 135 void OnLinkActivated(); |
| 273 | 136 |
| 274 // Loads AutoFill profiles and credit cards using the PersonalDataManager. | 137 // Loads AutoFill profiles and credit cards using the PersonalDataManager. |
| 275 void LoadAutoFillData(); | 138 void LoadAutoFillData(); |
| 276 | 139 |
| 277 // Creates the dialog UI widgets. | 140 // Creates the dialog UI widgets. |
| 278 void InitializeWidgets(); | 141 void InitializeWidgets(); |
| 279 | 142 |
| 280 // Initializes the group widgets and returns their container. |name_id| is | 143 // Updates the state of the various widgets dependant upon the state of the |
| 281 // the resource ID of the group label. |button_id| is the resource name of | 144 // selection, loaded state and whether AutoFill is enabled. |
| 282 // the button label. |clicked_callback| is a callback that handles the | 145 void UpdateWidgetState(); |
| 283 // 'clicked' signal emitted when the user presses the 'Add' button. | |
| 284 GtkWidget* InitGroup(int label_id, | |
| 285 int button_id, | |
| 286 GCallback clicked_callback); | |
| 287 | 146 |
| 288 // Initializes the expander, frame and table widgets used to hold the address | 147 // Returns a bitmask of the selection types. |
| 289 // and credit card forms. |name_id| is the resource id of the label of the | 148 int GetSelectionType(); |
| 290 // expander widget. The content vbox widget is returned in |content_vbox|. | |
| 291 // Returns the expander widget. | |
| 292 GtkWidget* InitGroupContentArea(int name_id, GtkWidget** content_vbox); | |
| 293 | 149 |
| 294 // Returns a GtkExpander that is added to the appropriate vbox. Each method | 150 void AddAddressToTree(const AutoFillProfile& profile, GtkTreeIter* iter); |
| 295 // adds the necessary widgets and layout required to fill out information | |
| 296 // for either an address or a credit card. The expander will be expanded by | |
| 297 // default if |expand| is true. | |
| 298 GtkWidget* AddNewAddress(bool expand); | |
| 299 GtkWidget* AddNewCreditCard(bool expand); | |
| 300 | 151 |
| 301 // Adds a new address filled out with information from |profile|. | 152 void AddCreditCardToTree(const CreditCard& credit_card, GtkTreeIter* iter); |
| 302 void AddAddress(const AutoFillProfile& profile); | |
| 303 | 153 |
| 304 // Adds a new credit card filled out with information from |credit_card|. | 154 // Returns the set of selected profiles and cards. The values placed in |
| 305 void AddCreditCard(const CreditCard& credit_card); | 155 // the specified vectors are owned by PersonalDataManager. |
| 156 void GetSelectedEntries(std::vector<AutoFillProfile*>* profiles, |
| 157 std::vector<CreditCard*>* cards); |
| 306 | 158 |
| 307 // Returns the index of |billing_address| in the list of profiles. Returns -1 | 159 Profile* profile_; |
| 308 // if the address is not found. | |
| 309 int FindIndexOfAddress(string16 billing_address); | |
| 310 | 160 |
| 311 // Our observer. May not be NULL. | 161 // Our observer. May not be NULL. |
| 312 AutoFillDialogObserver* observer_; | 162 AutoFillDialogObserver* observer_; |
| 313 | 163 |
| 314 // The personal data manager, used to load AutoFill profiles and credit cards. | 164 // The personal data manager, used to load AutoFill profiles and credit cards. |
| 315 // Unowned pointer, may not be NULL. | 165 // Unowned pointer, may not be NULL. |
| 316 PersonalDataManager* personal_data_; | 166 PersonalDataManager* personal_data_; |
| 317 | 167 |
| 318 // The imported profile. May be NULL. | 168 // Number of profiles we're displaying. |
| 319 AutoFillProfile* imported_profile_; | 169 int profile_count_; |
| 320 | |
| 321 // The imported credit card. May be NULL. | |
| 322 CreditCard* imported_credit_card_; | |
| 323 | |
| 324 // The list of current AutoFill profiles. | |
| 325 std::vector<AutoFillProfile> profiles_; | |
| 326 | |
| 327 // The list of current AutoFill credit cards. | |
| 328 std::vector<CreditCard> credit_cards_; | |
| 329 | |
| 330 // The list of address widgets, used to modify the AutoFill profiles. | |
| 331 std::vector<AddressWidgets> address_widgets_; | |
| 332 | |
| 333 // The list of credit card widgets, used to modify the stored credit cards. | |
| 334 std::vector<CreditCardWidgets> credit_card_widgets_; | |
| 335 | 170 |
| 336 // The AutoFill dialog. | 171 // The AutoFill dialog. |
| 337 GtkWidget* dialog_; | 172 GtkWidget* dialog_; |
| 338 | 173 |
| 339 // The addresses group. | 174 BooleanPrefMember enable_form_autofill_; |
| 340 GtkWidget* addresses_vbox_; | |
| 341 | 175 |
| 342 // The credit cards group. | 176 GtkWidget* form_autofill_enable_check_; |
| 343 GtkWidget* creditcards_vbox_; | 177 |
| 178 // Displays the addresses then credit cards. |
| 179 GtkListStore* list_store_; |
| 180 |
| 181 // Displays the list_store_. |
| 182 GtkWidget* tree_; |
| 183 |
| 184 GtkWidget* add_address_button_; |
| 185 GtkWidget* add_credit_card_button_; |
| 186 GtkWidget* edit_button_; |
| 187 GtkWidget* remove_button_; |
| 344 | 188 |
| 345 DISALLOW_COPY_AND_ASSIGN(AutoFillDialog); | 189 DISALLOW_COPY_AND_ASSIGN(AutoFillDialog); |
| 346 }; | 190 }; |
| 347 | 191 |
| 348 // The singleton AutoFill dialog object. | 192 // The singleton AutoFill dialog object. |
| 349 static AutoFillDialog* dialog = NULL; | 193 static AutoFillDialog* dialog = NULL; |
| 350 | 194 |
| 351 AutoFillDialog::AutoFillDialog(AutoFillDialogObserver* observer, | 195 AutoFillDialog::AutoFillDialog(Profile* profile, |
| 352 PersonalDataManager* personal_data_manager, | 196 AutoFillDialogObserver* observer) |
| 353 AutoFillProfile* imported_profile, | 197 : profile_(profile), |
| 354 CreditCard* imported_credit_card) | 198 observer_(observer), |
| 355 : observer_(observer), | 199 personal_data_(profile->GetPersonalDataManager()), |
| 356 personal_data_(personal_data_manager), | 200 profile_count_(0) { |
| 357 imported_profile_(imported_profile), | |
| 358 imported_credit_card_(imported_credit_card) { | |
| 359 DCHECK(observer_); | 201 DCHECK(observer_); |
| 360 DCHECK(personal_data_); | 202 DCHECK(personal_data_); |
| 361 | 203 |
| 204 enable_form_autofill_.Init(prefs::kAutoFillEnabled, profile->GetPrefs(), |
| 205 this); |
| 206 |
| 207 personal_data_->SetObserver(this); |
| 208 |
| 362 InitializeWidgets(); | 209 InitializeWidgets(); |
| 363 LoadAutoFillData(); | 210 LoadAutoFillData(); |
| 364 | 211 |
| 365 gtk_util::ShowDialogWithLocalizedSize(dialog_, | 212 gtk_util::ShowDialogWithLocalizedSize(dialog_, |
| 366 IDS_AUTOFILL_DIALOG_WIDTH_CHARS, | 213 IDS_AUTOFILL_DIALOG_WIDTH_CHARS, |
| 367 IDS_AUTOFILL_DIALOG_HEIGHT_LINES, | 214 IDS_AUTOFILL_DIALOG_HEIGHT_LINES, |
| 368 true); | 215 true); |
| 369 } | 216 } |
| 370 | 217 |
| 371 AutoFillDialog::~AutoFillDialog() { | 218 AutoFillDialog::~AutoFillDialog() { |
| 372 // Removes observer if we are observing Profile load. Does nothing otherwise. | 219 // Removes observer if we are observing Profile load. Does nothing otherwise. |
| 373 if (personal_data_) | 220 personal_data_->RemoveObserver(this); |
| 374 personal_data_->RemoveObserver(this); | |
| 375 } | 221 } |
| 376 | 222 |
| 377 ///////////////////////////////////////////////////////////////////////////// | 223 ///////////////////////////////////////////////////////////////////////////// |
| 378 // PersonalDataManager::Observer implementation: | 224 // PersonalDataManager::Observer implementation: |
| 379 void AutoFillDialog::OnPersonalDataLoaded() { | 225 void AutoFillDialog::OnPersonalDataLoaded() { |
| 380 personal_data_->RemoveObserver(this); | |
| 381 LoadAutoFillData(); | 226 LoadAutoFillData(); |
| 382 } | 227 } |
| 383 | 228 |
| 229 void AutoFillDialog::OnPersonalDataChanged() { |
| 230 LoadAutoFillData(); |
| 231 } |
| 232 |
| 233 void AutoFillDialog::Observe(NotificationType type, |
| 234 const NotificationSource& source, |
| 235 const NotificationDetails& details) { |
| 236 DCHECK_EQ(NotificationType::PREF_CHANGED, type.value); |
| 237 const std::wstring* pref_name = Details<std::wstring>(details).ptr(); |
| 238 if (!pref_name || *pref_name == prefs::kAutoFillEnabled) { |
| 239 gtk_toggle_button_set_active( |
| 240 GTK_TOGGLE_BUTTON(form_autofill_enable_check_), |
| 241 enable_form_autofill_.GetValue() ? TRUE : FALSE); |
| 242 UpdateWidgetState(); |
| 243 } |
| 244 } |
| 245 |
| 384 void AutoFillDialog::Show() { | 246 void AutoFillDialog::Show() { |
| 385 gtk_util::PresentWindow(dialog_, gtk_get_current_event_time()); | 247 gtk_util::PresentWindow(dialog_, gtk_get_current_event_time()); |
| 386 } | 248 } |
| 387 | 249 |
| 388 // static | 250 void AutoFillDialog::OnDestroy(GtkWidget* widget) { |
| 389 void AutoFillDialog::OnDestroy(GtkWidget* widget, | |
| 390 AutoFillDialog* autofill_dialog) { | |
| 391 dialog = NULL; | 251 dialog = NULL; |
| 392 MessageLoop::current()->DeleteSoon(FROM_HERE, autofill_dialog); | 252 MessageLoop::current()->DeleteSoon(FROM_HERE, this); |
| 393 } | 253 } |
| 394 | 254 |
| 395 static AutoFillProfile AutoFillProfileFromWidgetValues( | 255 void AutoFillDialog::OnResponse(GtkDialog* dialog, gint response_id) { |
| 396 const AddressWidgets& widgets) { | |
| 397 // TODO(jhawkins): unique id? | |
| 398 AutoFillProfile profile(GetEntryText(widgets.label), 0); | |
| 399 profile.SetInfo(AutoFillType(NAME_FULL), | |
| 400 GetEntryText(widgets.full_name)); | |
| 401 profile.SetInfo(AutoFillType(EMAIL_ADDRESS), | |
| 402 GetEntryText(widgets.email)); | |
| 403 profile.SetInfo(AutoFillType(COMPANY_NAME), | |
| 404 GetEntryText(widgets.company_name)); | |
| 405 profile.SetInfo(AutoFillType(ADDRESS_HOME_LINE1), | |
| 406 GetEntryText(widgets.address_line1)); | |
| 407 profile.SetInfo(AutoFillType(ADDRESS_HOME_LINE2), | |
| 408 GetEntryText(widgets.address_line2)); | |
| 409 profile.SetInfo(AutoFillType(ADDRESS_HOME_CITY), | |
| 410 GetEntryText(widgets.city)); | |
| 411 profile.SetInfo(AutoFillType(ADDRESS_HOME_STATE), | |
| 412 GetEntryText(widgets.state)); | |
| 413 profile.SetInfo(AutoFillType(ADDRESS_HOME_ZIP), | |
| 414 GetEntryText(widgets.zipcode)); | |
| 415 profile.SetInfo(AutoFillType(ADDRESS_HOME_COUNTRY), | |
| 416 GetEntryText(widgets.country)); | |
| 417 | |
| 418 string16 number, city_code, country_code; | |
| 419 PhoneNumber::ParsePhoneNumber( | |
| 420 GetEntryText(widgets.phone), &number, &city_code, &country_code); | |
| 421 profile.SetInfo(AutoFillType(PHONE_HOME_COUNTRY_CODE), country_code); | |
| 422 profile.SetInfo(AutoFillType(PHONE_HOME_CITY_CODE), city_code); | |
| 423 profile.SetInfo(AutoFillType(PHONE_HOME_NUMBER), number); | |
| 424 | |
| 425 PhoneNumber::ParsePhoneNumber( | |
| 426 GetEntryText(widgets.fax), &number, &city_code, &country_code); | |
| 427 profile.SetInfo(AutoFillType(PHONE_FAX_COUNTRY_CODE), country_code); | |
| 428 profile.SetInfo(AutoFillType(PHONE_FAX_CITY_CODE), city_code); | |
| 429 profile.SetInfo(AutoFillType(PHONE_FAX_NUMBER), number); | |
| 430 | |
| 431 return profile; | |
| 432 } | |
| 433 | |
| 434 static CreditCard CreditCardFromWidgetValues( | |
| 435 const CreditCardWidgets& widgets) { | |
| 436 // TODO(jhawkins): unique id? | |
| 437 CreditCard credit_card(GetEntryText(widgets.label), 0); | |
| 438 credit_card.SetInfo(AutoFillType(CREDIT_CARD_NAME), | |
| 439 GetEntryText(widgets.name_on_card)); | |
| 440 credit_card.SetInfo(AutoFillType(CREDIT_CARD_EXP_MONTH), | |
| 441 GetEntryText(widgets.expiration_month)); | |
| 442 credit_card.SetInfo(AutoFillType(CREDIT_CARD_EXP_4_DIGIT_YEAR), | |
| 443 GetEntryText(widgets.expiration_year)); | |
| 444 | |
| 445 // If the CC number starts with an asterisk, then we know that the user has | |
| 446 // not modified the credit card number at the least, so use the original CC | |
| 447 // number in this case. | |
| 448 string16 cc_number = GetEntryText(widgets.card_number); | |
| 449 if (!cc_number.empty() && cc_number[0] == '*') | |
| 450 credit_card.SetInfo(AutoFillType(CREDIT_CARD_NUMBER), | |
| 451 widgets.original_card_number); | |
| 452 else | |
| 453 credit_card.SetInfo(AutoFillType(CREDIT_CARD_NUMBER), | |
| 454 GetEntryText(widgets.card_number)); | |
| 455 | |
| 456 std::string text = | |
| 457 gtk_combo_box_get_active_text(GTK_COMBO_BOX(widgets.billing_address)); | |
| 458 if (text != | |
| 459 l10n_util::GetStringUTF8(IDS_AUTOFILL_DIALOG_CHOOSE_EXISTING_ADDRESS)) { | |
| 460 // TODO(jhawkins): Should we validate the billing address combobox? | |
| 461 credit_card.set_billing_address(UTF8ToUTF16(text)); | |
| 462 } | |
| 463 | |
| 464 return credit_card; | |
| 465 } | |
| 466 | |
| 467 // static | |
| 468 void AutoFillDialog::OnResponse(GtkDialog* dialog, gint response_id, | |
| 469 AutoFillDialog* autofill_dialog) { | |
| 470 if (response_id == GTK_RESPONSE_APPLY || response_id == GTK_RESPONSE_OK) { | |
| 471 autofill_dialog->profiles_.clear(); | |
| 472 for (std::vector<AddressWidgets>::const_iterator iter = | |
| 473 autofill_dialog->address_widgets_.begin(); | |
| 474 iter != autofill_dialog->address_widgets_.end(); | |
| 475 ++iter) { | |
| 476 AutoFillProfile profile = AutoFillProfileFromWidgetValues(*iter); | |
| 477 autofill_dialog->profiles_.push_back(profile); | |
| 478 } | |
| 479 | |
| 480 autofill_dialog->credit_cards_.clear(); | |
| 481 for (std::vector<CreditCardWidgets>::const_iterator iter = | |
| 482 autofill_dialog->credit_card_widgets_.begin(); | |
| 483 iter != autofill_dialog->credit_card_widgets_.end(); | |
| 484 ++iter) { | |
| 485 CreditCard credit_card = CreditCardFromWidgetValues(*iter); | |
| 486 autofill_dialog->credit_cards_.push_back(credit_card); | |
| 487 } | |
| 488 | |
| 489 autofill_dialog->observer_->OnAutoFillDialogApply( | |
| 490 &autofill_dialog->profiles_, | |
| 491 &autofill_dialog->credit_cards_); | |
| 492 } | |
| 493 | |
| 494 if (response_id == GTK_RESPONSE_OK || | 256 if (response_id == GTK_RESPONSE_OK || |
| 495 response_id == GTK_RESPONSE_CANCEL || | 257 response_id == GTK_RESPONSE_CANCEL || |
| 496 response_id == GTK_RESPONSE_DELETE_EVENT) { | 258 response_id == GTK_RESPONSE_DELETE_EVENT) { |
| 497 gtk_widget_destroy(GTK_WIDGET(dialog)); | 259 gtk_widget_destroy(GTK_WIDGET(dialog)); |
| 498 } | 260 } |
| 499 | 261 |
| 500 if (response_id == kAutoFillDialogLearnMoreLink) | 262 if (response_id == kAutoFillDialogAboutLink) |
| 501 autofill_dialog->OnLinkActivated(); | 263 OnLinkActivated(); |
| 502 } | 264 } |
| 503 | 265 |
| 504 // static | 266 void AutoFillDialog::OnAutoFillCheckToggled(GtkWidget* widget) { |
| 505 void AutoFillDialog::OnAddAddressClicked(GtkButton* button, | 267 bool enabled = gtk_toggle_button_get_active( |
| 506 AutoFillDialog* dialog) { | 268 GTK_TOGGLE_BUTTON(form_autofill_enable_check_)); |
| 507 GtkWidget* new_address = dialog->AddNewAddress(true); | 269 if (enabled) { |
| 508 gtk_box_pack_start(GTK_BOX(dialog->addresses_vbox_), new_address, | 270 UserMetrics::RecordAction(UserMetricsAction("Options_FormAutofill_Enable"), |
| 509 FALSE, FALSE, 0); | 271 profile_); |
| 510 gtk_widget_show_all(new_address); | 272 } else { |
| 273 UserMetrics::RecordAction(UserMetricsAction("Options_FormAutofill_Disable"), |
| 274 profile_); |
| 275 } |
| 276 enable_form_autofill_.SetValue(enabled); |
| 277 profile_->GetPrefs()->ScheduleSavePersistentPrefs(); |
| 278 UpdateWidgetState(); |
| 511 } | 279 } |
| 512 | 280 |
| 513 // static | 281 void AutoFillDialog::OnRowActivated(GtkTreeView* tree_view, |
| 514 void AutoFillDialog::OnAddCreditCardClicked(GtkButton* button, | 282 GtkTreePath* path, |
| 515 AutoFillDialog* dialog) { | 283 GtkTreeViewColumn* column) { |
| 516 GtkWidget* new_creditcard = dialog->AddNewCreditCard(true); | 284 if (GetSelectionType() == SELECTION_SINGLE) |
| 517 gtk_box_pack_start(GTK_BOX(dialog->creditcards_vbox_), new_creditcard, | 285 OnEdit(NULL); |
| 518 FALSE, FALSE, 0); | |
| 519 gtk_widget_show_all(new_creditcard); | |
| 520 } | 286 } |
| 521 | 287 |
| 522 // static | 288 void AutoFillDialog::OnSelectionChanged(GtkTreeSelection* selection) { |
| 523 void AutoFillDialog::OnDeleteAddressClicked(GtkButton* button, | 289 UpdateWidgetState(); |
| 524 AutoFillDialog* dialog) { | 290 } |
| 525 GtkWidget* entry = GetButtonData(GTK_WIDGET(button)); | |
| 526 string16 label = GetEntryText(entry); | |
| 527 | 291 |
| 528 // TODO(jhawkins): Base this on ID. | 292 gboolean AutoFillDialog::OnCheckRowIsSeparator(GtkTreeModel* model, |
| 293 GtkTreeIter* iter) { |
| 294 gboolean is_separator; |
| 295 gtk_tree_model_get(model, iter, COL_IS_SEPARATOR, &is_separator, -1); |
| 296 return is_separator; |
| 297 } |
| 529 | 298 |
| 530 // Remove the profile. | 299 void AutoFillDialog::OnAddAddress(GtkWidget* widget) { |
| 531 for (std::vector<AutoFillProfile>::iterator iter = dialog->profiles_.begin(); | 300 ShowAutoFillProfileEditor(NULL, observer_, profile_, NULL); |
| 532 iter != dialog->profiles_.end(); | 301 } |
| 533 ++iter) { | 302 |
| 534 if (iter->Label() == label) { | 303 void AutoFillDialog::OnAddCreditCard(GtkWidget* widget) { |
| 535 dialog->profiles_.erase(iter); | 304 ShowAutoFillCreditCardEditor(NULL, observer_, profile_, NULL); |
| 536 break; | 305 } |
| 306 |
| 307 void AutoFillDialog::OnEdit(GtkWidget* widget) { |
| 308 DCHECK_EQ(SELECTION_SINGLE, GetSelectionType()); |
| 309 |
| 310 std::vector<AutoFillProfile*> profiles; |
| 311 std::vector<CreditCard*> cards; |
| 312 |
| 313 GetSelectedEntries(&profiles, &cards); |
| 314 |
| 315 if (profiles.size() == 1) |
| 316 ShowAutoFillProfileEditor(dialog_, observer_, profile_, profiles[0]); |
| 317 else if (cards.size() == 1) |
| 318 ShowAutoFillCreditCardEditor(dialog_, observer_, profile_, cards[0]); |
| 319 } |
| 320 |
| 321 void AutoFillDialog::OnRemove(GtkWidget* widget) { |
| 322 PersonalDataManager* data_manager = profile_->GetPersonalDataManager(); |
| 323 std::vector<AutoFillProfile*> selected_profiles; |
| 324 std::vector<CreditCard*> selected_cards; |
| 325 |
| 326 GetSelectedEntries(&selected_profiles, &selected_cards); |
| 327 |
| 328 std::vector<AutoFillProfile> profiles; |
| 329 for (std::vector<AutoFillProfile*>::const_iterator i = |
| 330 data_manager->profiles().begin(); |
| 331 i != data_manager->profiles().end(); ++i) { |
| 332 if (std::find(selected_profiles.begin(), selected_profiles.end(), *i) == |
| 333 selected_profiles.end()) { |
| 334 profiles.push_back(**i); |
| 537 } | 335 } |
| 538 } | 336 } |
| 539 | 337 |
| 540 // Remove the set of address widgets. | 338 std::vector<CreditCard> cards; |
| 541 for (std::vector<AddressWidgets>::iterator iter = | 339 for (std::vector<CreditCard*>::const_iterator i = |
| 542 dialog->address_widgets_.begin(); | 340 data_manager->credit_cards().begin(); |
| 543 iter != dialog->address_widgets_.end(); | 341 i != data_manager->credit_cards().end(); ++i) { |
| 544 ++iter) { | 342 if (std::find(selected_cards.begin(), selected_cards.end(), *i) == |
| 545 if (iter->label == entry) { | 343 selected_cards.end()) { |
| 546 dialog->address_widgets_.erase(iter); | 344 cards.push_back(**i); |
| 547 break; | |
| 548 } | 345 } |
| 549 } | 346 } |
| 550 | 347 |
| 551 // Get back to the expander widget. | 348 observer_->OnAutoFillDialogApply(&profiles, &cards); |
| 552 GtkWidget* expander = gtk_widget_get_ancestor(GTK_WIDGET(button), | |
| 553 GTK_TYPE_EXPANDER); | |
| 554 DCHECK(expander); | |
| 555 | |
| 556 // Destroying the widget will also remove it from the parent container. | |
| 557 gtk_widget_destroy(expander); | |
| 558 } | 349 } |
| 559 | 350 |
| 560 // static | 351 gboolean AutoFillDialog::OnSelectionFilter(GtkTreeSelection* selection, |
| 561 void AutoFillDialog::OnDeleteCreditCardClicked(GtkButton* button, | 352 GtkTreeModel* model, |
| 562 AutoFillDialog* dialog) { | 353 GtkTreePath* path, |
| 563 GtkWidget* entry = GetButtonData(GTK_WIDGET(button)); | 354 gboolean path_currently_selected) { |
| 564 string16 label = GetEntryText(entry); | 355 GtkTreeIter iter; |
| 565 | 356 if (!gtk_tree_model_get_iter(model, &iter, path)) { |
| 566 // TODO(jhawkins): Base this on ID. | 357 NOTREACHED(); |
| 567 | 358 return TRUE; |
| 568 // Remove the credit card. | |
| 569 for (std::vector<CreditCard>::iterator iter = dialog->credit_cards_.begin(); | |
| 570 iter != dialog->credit_cards_.end(); | |
| 571 ++iter) { | |
| 572 if (iter->Label() == label) { | |
| 573 dialog->credit_cards_.erase(iter); | |
| 574 break; | |
| 575 } | |
| 576 } | 359 } |
| 577 | 360 gboolean is_header; |
| 578 // Remove the set of credit widgets. | 361 gboolean is_separator; |
| 579 for (std::vector<CreditCardWidgets>::iterator iter = | 362 gtk_tree_model_get(model, &iter, COL_IS_HEADER, &is_header, |
| 580 dialog->credit_card_widgets_.begin(); | 363 COL_IS_SEPARATOR, &is_separator, -1); |
| 581 iter != dialog->credit_card_widgets_.end(); | 364 return !is_header && !is_separator; |
| 582 ++iter) { | |
| 583 if (iter->label == entry) { | |
| 584 dialog->credit_card_widgets_.erase(iter); | |
| 585 break; | |
| 586 } | |
| 587 } | |
| 588 | |
| 589 // Get back to the expander widget. | |
| 590 GtkWidget* expander = gtk_widget_get_ancestor(GTK_WIDGET(button), | |
| 591 GTK_TYPE_EXPANDER); | |
| 592 DCHECK(expander); | |
| 593 | |
| 594 // Destroying the widget will also remove it from the parent container. | |
| 595 gtk_widget_destroy(expander); | |
| 596 } | |
| 597 | |
| 598 // static | |
| 599 void AutoFillDialog::OnLabelChanged(GtkEntry* label, GtkWidget* expander) { | |
| 600 gtk_expander_set_label(GTK_EXPANDER(expander), gtk_entry_get_text(label)); | |
| 601 } | 365 } |
| 602 | 366 |
| 603 void AutoFillDialog::OnLinkActivated() { | 367 void AutoFillDialog::OnLinkActivated() { |
| 604 Browser* browser = BrowserList::GetLastActive(); | 368 Browser* browser = BrowserList::GetLastActive(); |
| 605 browser->OpenURL(GURL(kAutoFillLearnMoreUrl), GURL(), NEW_FOREGROUND_TAB, | 369 browser->OpenURL(GURL(kAutoFillLearnMoreUrl), GURL(), NEW_FOREGROUND_TAB, |
| 606 PageTransition::TYPED); | 370 PageTransition::TYPED); |
| 607 } | 371 } |
| 608 | 372 |
| 609 void AutoFillDialog::LoadAutoFillData() { | 373 void AutoFillDialog::LoadAutoFillData() { |
| 610 if (!personal_data_->IsDataLoaded()) { | 374 if (!personal_data_->IsDataLoaded()) { |
| 611 personal_data_->SetObserver(this); | 375 UpdateWidgetState(); |
| 612 return; | 376 return; |
| 613 } | 377 } |
| 614 | 378 |
| 615 if (imported_profile_) { | 379 // Rebuild the underyling store. |
| 616 profiles_.push_back(*imported_profile_); | 380 gtk_list_store_clear(list_store_); |
| 617 AddAddress(*imported_profile_); | |
| 618 } else { | |
| 619 for (std::vector<AutoFillProfile*>::const_iterator iter = | |
| 620 personal_data_->profiles().begin(); | |
| 621 iter != personal_data_->profiles().end(); ++iter) { | |
| 622 // The profile list is terminated by a NULL entry; | |
| 623 if (!*iter) | |
| 624 break; | |
| 625 | 381 |
| 626 AutoFillProfile* profile = *iter; | 382 GtkTreeIter iter; |
| 627 profiles_.push_back(*profile); | 383 // Address title. |
| 628 AddAddress(*profile); | 384 gtk_list_store_append(list_store_, &iter); |
| 629 } | 385 gtk_list_store_set( |
| 386 list_store_, &iter, |
| 387 COL_WEIGHT, PANGO_WEIGHT_BOLD, |
| 388 COL_WEIGHT_SET, TRUE, |
| 389 COL_TITLE, |
| 390 l10n_util::GetStringUTF8(IDS_AUTOFILL_ADDRESSES_GROUP_NAME).c_str(), |
| 391 COL_IS_HEADER, TRUE, |
| 392 -1); |
| 393 // Address separator. |
| 394 gtk_list_store_append(list_store_, &iter); |
| 395 gtk_list_store_set( |
| 396 list_store_, &iter, |
| 397 COL_IS_SEPARATOR, TRUE, |
| 398 -1); |
| 399 |
| 400 // The addresses. |
| 401 profile_count_ = 0; |
| 402 for (std::vector<AutoFillProfile*>::const_iterator i = |
| 403 personal_data_->profiles().begin(); |
| 404 i != personal_data_->profiles().end(); ++i) { |
| 405 AddAddressToTree(*(*i), &iter); |
| 406 profile_count_++; |
| 630 } | 407 } |
| 631 | 408 |
| 632 if (imported_credit_card_) { | 409 // Blank row between addresses and credit cards. |
| 633 credit_cards_.push_back(*imported_credit_card_); | 410 gtk_list_store_append(list_store_, &iter); |
| 634 AddCreditCard(*imported_credit_card_); | 411 gtk_list_store_set( |
| 635 } else { | 412 list_store_, &iter, |
| 636 for (std::vector<CreditCard*>::const_iterator iter = | 413 COL_IS_HEADER, TRUE, |
| 637 personal_data_->credit_cards().begin(); | 414 -1); |
| 638 iter != personal_data_->credit_cards().end(); ++iter) { | |
| 639 // The credit card list is terminated by a NULL entry; | |
| 640 if (!*iter) | |
| 641 break; | |
| 642 | 415 |
| 643 CreditCard* credit_card = *iter; | 416 // Credit card title. |
| 644 credit_cards_.push_back(*credit_card); | 417 gtk_list_store_append(list_store_, &iter); |
| 645 AddCreditCard(*credit_card); | 418 gtk_list_store_set( |
| 646 } | 419 list_store_, &iter, |
| 420 COL_WEIGHT, PANGO_WEIGHT_BOLD, |
| 421 COL_WEIGHT_SET, TRUE, |
| 422 COL_TITLE, |
| 423 l10n_util::GetStringUTF8(IDS_AUTOFILL_CREDITCARDS_GROUP_NAME).c_str(), |
| 424 COL_IS_HEADER, TRUE, |
| 425 -1); |
| 426 // Credit card separator. |
| 427 gtk_list_store_append(list_store_, &iter); |
| 428 gtk_list_store_set( |
| 429 list_store_, &iter, |
| 430 COL_IS_SEPARATOR, TRUE, |
| 431 -1); |
| 432 |
| 433 // The credit cards. |
| 434 for (std::vector<CreditCard*>::const_iterator i = |
| 435 personal_data_->credit_cards().begin(); |
| 436 i != personal_data_->credit_cards().end(); ++i) { |
| 437 AddCreditCardToTree(*(*i), &iter); |
| 647 } | 438 } |
| 439 |
| 440 UpdateWidgetState(); |
| 648 } | 441 } |
| 649 | 442 |
| 650 void AutoFillDialog::InitializeWidgets() { | 443 void AutoFillDialog::InitializeWidgets() { |
| 651 dialog_ = gtk_dialog_new_with_buttons( | 444 dialog_ = gtk_dialog_new_with_buttons( |
| 652 l10n_util::GetStringUTF8(IDS_AUTOFILL_OPTIONS).c_str(), | 445 l10n_util::GetStringUTF8(IDS_AUTOFILL_OPTIONS).c_str(), |
| 653 // AutoFill dialog is shared between all browser windows. | 446 // AutoFill dialog is shared between all browser windows. |
| 654 NULL, | 447 NULL, |
| 655 // Non-modal. | 448 // Non-modal. |
| 656 GTK_DIALOG_NO_SEPARATOR, | 449 GTK_DIALOG_NO_SEPARATOR, |
| 657 GTK_STOCK_APPLY, | |
| 658 GTK_RESPONSE_APPLY, | |
| 659 GTK_STOCK_CANCEL, | |
| 660 GTK_RESPONSE_CANCEL, | |
| 661 GTK_STOCK_OK, | 450 GTK_STOCK_OK, |
| 662 GTK_RESPONSE_OK, | 451 GTK_RESPONSE_OK, |
| 663 NULL); | 452 NULL); |
| 664 | 453 |
| 665 gtk_box_set_spacing(GTK_BOX(GTK_DIALOG(dialog_)->vbox), | 454 GtkBox* vbox = GTK_BOX(GTK_DIALOG(dialog_)->vbox); |
| 666 gtk_util::kContentAreaSpacing); | 455 gtk_box_set_spacing(vbox, gtk_util::kControlSpacing); |
| 667 g_signal_connect(dialog_, "response", G_CALLBACK(OnResponse), this); | 456 g_signal_connect(dialog_, "response", G_CALLBACK(OnResponseThunk), this); |
| 668 g_signal_connect(dialog_, "destroy", G_CALLBACK(OnDestroy), this); | 457 g_signal_connect(dialog_, "destroy", G_CALLBACK(OnDestroyThunk), this); |
| 458 |
| 459 form_autofill_enable_check_ = gtk_check_button_new_with_label( |
| 460 l10n_util::GetStringUTF8(IDS_OPTIONS_AUTOFILL_ENABLE).c_str()); |
| 461 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(form_autofill_enable_check_), |
| 462 enable_form_autofill_.GetValue()); |
| 463 g_signal_connect(G_OBJECT(form_autofill_enable_check_), "toggled", |
| 464 G_CALLBACK(OnAutoFillCheckToggledThunk), this); |
| 465 gtk_box_pack_start(vbox, form_autofill_enable_check_, FALSE, FALSE, 0); |
| 669 | 466 |
| 670 // Allow the contents to be scrolled. | 467 // Allow the contents to be scrolled. |
| 671 GtkWidget* scrolled_window = gtk_scrolled_window_new(NULL, NULL); | 468 GtkWidget* scrolled_window = gtk_scrolled_window_new(NULL, NULL); |
| 672 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window), | 469 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window), |
| 673 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); | 470 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); |
| 674 gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog_)->vbox), scrolled_window); | 471 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled_window), |
| 472 GTK_SHADOW_ETCHED_IN); |
| 675 | 473 |
| 676 // We create an event box so that we can color the frame background white. | 474 list_store_ = gtk_list_store_new(COL_COUNT, |
| 677 GtkWidget* frame_event_box = gtk_event_box_new(); | 475 G_TYPE_STRING, |
| 678 SetWhiteBackground(frame_event_box); | 476 G_TYPE_BOOLEAN, |
| 679 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scrolled_window), | 477 G_TYPE_BOOLEAN, |
| 680 frame_event_box); | 478 G_TYPE_INT, |
| 479 G_TYPE_BOOLEAN); |
| 480 tree_ = gtk_tree_view_new_with_model(GTK_TREE_MODEL(list_store_)); |
| 481 g_object_unref(list_store_); |
| 482 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(tree_), FALSE); |
| 483 gtk_tree_view_set_row_separator_func(GTK_TREE_VIEW(tree_), |
| 484 OnCheckRowIsSeparatorThunk, this, NULL); |
| 485 g_signal_connect(tree_, "row-activated", G_CALLBACK(OnRowActivatedThunk), |
| 486 this); |
| 487 gtk_container_add(GTK_CONTAINER(scrolled_window), tree_); |
| 681 | 488 |
| 682 // The frame outline of the content area. | 489 GtkWidget* h_box1 = gtk_hbox_new(FALSE, gtk_util::kControlSpacing); |
| 683 GtkWidget* frame = gtk_frame_new(NULL); | 490 gtk_box_pack_start(vbox, h_box1, TRUE, TRUE, 0); |
| 684 gtk_container_add(GTK_CONTAINER(frame_event_box), frame); | 491 gtk_box_pack_start(GTK_BOX(h_box1), scrolled_window, TRUE, TRUE, 0); |
| 685 | 492 |
| 686 // The content vbox. | 493 GtkWidget* controls_box = gtk_vbox_new(FALSE, gtk_util::kControlSpacing); |
| 687 GtkWidget* outer_vbox = gtk_vbox_new(false, 0); | 494 gtk_box_pack_start(GTK_BOX(h_box1), controls_box, FALSE, FALSE, 0); |
| 688 gtk_box_set_spacing(GTK_BOX(outer_vbox), gtk_util::kContentAreaSpacing); | |
| 689 gtk_container_add(GTK_CONTAINER(frame), outer_vbox); | |
| 690 | 495 |
| 691 addresses_vbox_ = InitGroup(IDS_AUTOFILL_ADDRESSES_GROUP_NAME, | 496 add_address_button_ = gtk_button_new_with_label( |
| 692 IDS_AUTOFILL_ADD_ADDRESS_BUTTON, | 497 l10n_util::GetStringUTF8(IDS_AUTOFILL_ADD_ADDRESS_BUTTON).c_str()); |
| 693 G_CALLBACK(OnAddAddressClicked)); | 498 g_signal_connect(add_address_button_, "clicked", |
| 694 gtk_box_pack_start_defaults(GTK_BOX(outer_vbox), addresses_vbox_); | 499 G_CALLBACK(OnAddAddressThunk), this); |
| 500 gtk_box_pack_start(GTK_BOX(controls_box), add_address_button_, FALSE, FALSE, |
| 501 0); |
| 695 | 502 |
| 696 creditcards_vbox_ = InitGroup(IDS_AUTOFILL_CREDITCARDS_GROUP_NAME, | 503 add_credit_card_button_ = gtk_button_new_with_label( |
| 697 IDS_AUTOFILL_ADD_CREDITCARD_BUTTON, | 504 l10n_util::GetStringUTF8(IDS_AUTOFILL_ADD_CREDITCARD_BUTTON).c_str()); |
| 698 G_CALLBACK(OnAddCreditCardClicked)); | 505 g_signal_connect(add_credit_card_button_, "clicked", |
| 699 gtk_box_pack_start_defaults(GTK_BOX(outer_vbox), creditcards_vbox_); | 506 G_CALLBACK(OnAddCreditCardThunk), this); |
| 507 gtk_box_pack_start(GTK_BOX(controls_box), add_credit_card_button_, FALSE, |
| 508 FALSE, 0); |
| 509 |
| 510 edit_button_ = gtk_button_new_with_label( |
| 511 l10n_util::GetStringUTF8(IDS_AUTOFILL_EDIT_BUTTON).c_str()); |
| 512 g_signal_connect(edit_button_, "clicked", G_CALLBACK(OnEditThunk), this); |
| 513 gtk_box_pack_start(GTK_BOX(controls_box), edit_button_, FALSE, FALSE, 0); |
| 514 |
| 515 remove_button_ = gtk_button_new_with_label( |
| 516 l10n_util::GetStringUTF8(IDS_AUTOFILL_DELETE_BUTTON).c_str()); |
| 517 g_signal_connect(remove_button_, "clicked", G_CALLBACK(OnRemoveThunk), this); |
| 518 gtk_box_pack_start(GTK_BOX(controls_box), remove_button_, FALSE, FALSE, 0); |
| 519 |
| 520 GtkTreeViewColumn* column = gtk_tree_view_column_new_with_attributes( |
| 521 "", |
| 522 gtk_cell_renderer_text_new(), |
| 523 "text", COL_TITLE, |
| 524 "weight", COL_WEIGHT, |
| 525 "weight-set", COL_WEIGHT_SET, |
| 526 NULL); |
| 527 gtk_tree_view_append_column(GTK_TREE_VIEW(tree_), column); |
| 528 |
| 529 GtkTreeSelection* selection = |
| 530 gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_)); |
| 531 gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE); |
| 532 gtk_tree_selection_set_select_function(selection, OnSelectionFilterThunk, |
| 533 this, NULL); |
| 534 g_signal_connect(selection, "changed", G_CALLBACK(OnSelectionChangedThunk), |
| 535 this); |
| 700 | 536 |
| 701 GtkWidget* link = gtk_chrome_link_button_new( | 537 GtkWidget* link = gtk_chrome_link_button_new( |
| 702 l10n_util::GetStringUTF8(IDS_AUTOFILL_LEARN_MORE).c_str()); | 538 l10n_util::GetStringUTF8(IDS_AUTOFILL_HELP_LABEL).c_str()); |
| 703 gtk_dialog_add_action_widget(GTK_DIALOG(dialog_), link, | 539 gtk_dialog_add_action_widget(GTK_DIALOG(dialog_), link, |
| 704 kAutoFillDialogLearnMoreLink); | 540 kAutoFillDialogAboutLink); |
| 705 | 541 |
| 706 // Setting the link widget to secondary positions the button on the left side | 542 // Setting the link widget to secondary positions the button on the left side |
| 707 // of the action area (vice versa for RTL layout). | 543 // of the action area (vice versa for RTL layout). |
| 708 gtk_button_box_set_child_secondary( | 544 gtk_button_box_set_child_secondary( |
| 709 GTK_BUTTON_BOX(GTK_DIALOG(dialog_)->action_area), link, TRUE); | 545 GTK_BUTTON_BOX(GTK_DIALOG(dialog_)->action_area), link, TRUE); |
| 710 } | 546 } |
| 711 | 547 |
| 712 GtkWidget* AutoFillDialog::InitGroup(int name_id, | 548 void AutoFillDialog::UpdateWidgetState() { |
| 713 int button_id, | 549 if (!personal_data_->IsDataLoaded() || !enable_form_autofill_.GetValue()) { |
| 714 GCallback clicked_callback) { | 550 gtk_widget_set_sensitive(add_address_button_, FALSE); |
| 715 GtkWidget* vbox = gtk_vbox_new(false, gtk_util::kControlSpacing); | 551 gtk_widget_set_sensitive(add_credit_card_button_, FALSE); |
| 716 | 552 gtk_widget_set_sensitive(edit_button_, FALSE); |
| 717 // Group label. | 553 gtk_widget_set_sensitive(remove_button_, FALSE); |
| 718 GtkWidget* label = gtk_util::CreateBoldLabel( | 554 gtk_widget_set_sensitive(tree_, FALSE); |
| 719 l10n_util::GetStringUTF8(name_id)); | 555 } else { |
| 720 gtk_box_pack_start(GTK_BOX(vbox), | 556 gtk_widget_set_sensitive(add_address_button_, TRUE); |
| 721 IndentWidget(label, kAutoFillDialogIndent), | 557 gtk_widget_set_sensitive(add_credit_card_button_, TRUE); |
| 722 FALSE, FALSE, 0); | 558 int selection_type = GetSelectionType(); |
| 723 | 559 gtk_widget_set_sensitive(edit_button_, selection_type == SELECTION_SINGLE); |
| 724 // Separator. | 560 // Enable the remove button if at least one non-header row is selected. |
| 725 GtkWidget* separator = gtk_hseparator_new(); | 561 gtk_widget_set_sensitive(remove_button_, |
| 726 gtk_box_pack_start(GTK_BOX(vbox), separator, FALSE, FALSE, 0); | 562 (selection_type & SELECTION_SINGLE) != 0); |
| 727 | 563 gtk_widget_set_sensitive(tree_, TRUE); |
| 728 // Add profile button. | 564 } |
| 729 GtkWidget* button = gtk_button_new_with_label( | |
| 730 l10n_util::GetStringUTF8(button_id).c_str()); | |
| 731 g_signal_connect(button, "clicked", clicked_callback, this); | |
| 732 gtk_box_pack_end_defaults(GTK_BOX(vbox), | |
| 733 IndentWidget(button, kAutoFillDialogIndent)); | |
| 734 | |
| 735 return vbox; | |
| 736 } | 565 } |
| 737 | 566 |
| 738 GtkWidget* AutoFillDialog::InitGroupContentArea(int name_id, | 567 static void RowIteratorFunction(GtkTreeModel* model, |
| 739 GtkWidget** content_vbox) { | 568 GtkTreePath* path, |
| 740 GtkWidget* expander = gtk_expander_new( | 569 GtkTreeIter* iter, |
| 741 l10n_util::GetStringUTF8(name_id).c_str()); | 570 gpointer data) { |
| 571 int* type = reinterpret_cast<int*>(data); |
| 572 bool is_header = false; |
| 573 GValue value = { 0 }; |
| 574 gtk_tree_model_get_value(model, iter, AutoFillDialog::COL_IS_HEADER, &value); |
| 575 is_header = g_value_get_boolean(&value); |
| 576 g_value_unset(&value); |
| 742 | 577 |
| 743 GtkWidget* frame = gtk_frame_new(NULL); | 578 if (!is_header) { |
| 744 gtk_container_add(GTK_CONTAINER(expander), frame); | 579 // Is it a separator? |
| 580 GValue value = { 0 }; |
| 581 gtk_tree_model_get_value(model, iter, AutoFillDialog::COL_IS_SEPARATOR, |
| 582 &value); |
| 583 is_header = g_value_get_boolean(&value); |
| 584 g_value_unset(&value); |
| 585 } |
| 745 | 586 |
| 746 GtkWidget* vbox = gtk_vbox_new(false, 0); | 587 if (is_header) { |
| 747 gtk_box_set_spacing(GTK_BOX(vbox), gtk_util::kControlSpacing); | 588 *type |= AutoFillDialog::SELECTION_HEADER; |
| 748 GtkWidget* vbox_alignment = gtk_alignment_new(0, 0, 0, 0); | 589 } else { |
| 749 gtk_alignment_set_padding(GTK_ALIGNMENT(vbox_alignment), | 590 if ((*type & AutoFillDialog::SELECTION_SINGLE) == 0) |
| 750 gtk_util::kControlSpacing, | 591 *type |= AutoFillDialog::SELECTION_SINGLE; |
| 751 gtk_util::kControlSpacing, | 592 else |
| 752 gtk_util::kGroupIndent, | 593 *type |= AutoFillDialog::SELECTION_MULTI; |
| 753 0); | 594 } |
| 754 gtk_container_add(GTK_CONTAINER(vbox_alignment), vbox); | |
| 755 gtk_container_add(GTK_CONTAINER(frame), vbox_alignment); | |
| 756 | |
| 757 *content_vbox = vbox; | |
| 758 return expander; | |
| 759 } | 595 } |
| 760 | 596 |
| 761 GtkWidget* AutoFillDialog::AddNewAddress(bool expand) { | 597 int AutoFillDialog::GetSelectionType() { |
| 762 AddressWidgets widgets = {0}; | 598 int state = SELECTION_EMPTY; |
| 763 GtkWidget* vbox; | 599 gtk_tree_selection_selected_foreach( |
| 764 GtkWidget* address = InitGroupContentArea(IDS_AUTOFILL_NEW_ADDRESS, &vbox); | 600 gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_)), RowIteratorFunction, |
| 765 | 601 &state); |
| 766 gtk_expander_set_expanded(GTK_EXPANDER(address), expand); | 602 return state; |
| 767 | |
| 768 GtkWidget* table = InitFormTable(5, 3); | |
| 769 gtk_box_pack_start_defaults(GTK_BOX(vbox), table); | |
| 770 | |
| 771 widgets.label = FormTableAddLabelEntry(table, 0, 0, 1, | |
| 772 IDS_AUTOFILL_DIALOG_LABEL, | |
| 773 address, G_CALLBACK(OnLabelChanged)); | |
| 774 widgets.full_name = FormTableAddEntry(table, 1, 0, 1, | |
| 775 IDS_AUTOFILL_DIALOG_FULL_NAME); | |
| 776 widgets.email = FormTableAddEntry(table, 2, 0, 1, | |
| 777 IDS_AUTOFILL_DIALOG_EMAIL); | |
| 778 widgets.company_name = FormTableAddEntry(table, 2, 1, 1, | |
| 779 IDS_AUTOFILL_DIALOG_COMPANY_NAME); | |
| 780 widgets.address_line1 = FormTableAddEntry(table, 3, 0, 2, | |
| 781 IDS_AUTOFILL_DIALOG_ADDRESS_LINE_1); | |
| 782 widgets.address_line2 = FormTableAddEntry(table, 4, 0, 2, | |
| 783 IDS_AUTOFILL_DIALOG_ADDRESS_LINE_2); | |
| 784 | |
| 785 GtkWidget* address_table = InitFormTable(1, 4); | |
| 786 gtk_box_pack_start_defaults(GTK_BOX(vbox), address_table); | |
| 787 | |
| 788 widgets.city = FormTableAddEntry(address_table, 0, 0, 1, | |
| 789 IDS_AUTOFILL_DIALOG_CITY); | |
| 790 widgets.state = FormTableAddEntry(address_table, 0, 1, 1, | |
| 791 IDS_AUTOFILL_DIALOG_STATE); | |
| 792 widgets.zipcode = FormTableAddSizedEntry(address_table, 0, 2, 7, | |
| 793 IDS_AUTOFILL_DIALOG_ZIP_CODE); | |
| 794 widgets.country = FormTableAddSizedEntry(address_table, 0, 3, 10, | |
| 795 IDS_AUTOFILL_DIALOG_COUNTRY); | |
| 796 | |
| 797 GtkWidget* phone_table = InitFormTable(1, 2); | |
| 798 gtk_box_pack_start_defaults(GTK_BOX(vbox), phone_table); | |
| 799 | |
| 800 widgets.phone = | |
| 801 FormTableAddEntry(phone_table, 0, 0, 1, IDS_AUTOFILL_DIALOG_PHONE); | |
| 802 widgets.fax = | |
| 803 FormTableAddEntry(phone_table, 0, 1, 1, IDS_AUTOFILL_DIALOG_FAX); | |
| 804 | |
| 805 GtkWidget* button = gtk_button_new_with_label( | |
| 806 l10n_util::GetStringUTF8(IDS_AUTOFILL_DELETE_BUTTON).c_str()); | |
| 807 g_signal_connect(button, "clicked", G_CALLBACK(OnDeleteAddressClicked), this); | |
| 808 SetButtonData(button, widgets.label); | |
| 809 GtkWidget* alignment = gtk_alignment_new(0, 0, 0, 0); | |
| 810 gtk_container_add(GTK_CONTAINER(alignment), button); | |
| 811 gtk_box_pack_start_defaults(GTK_BOX(vbox), alignment); | |
| 812 | |
| 813 address_widgets_.push_back(widgets); | |
| 814 return address; | |
| 815 } | 603 } |
| 816 | 604 |
| 817 GtkWidget* AutoFillDialog::AddNewCreditCard(bool expand) { | 605 void AutoFillDialog::AddAddressToTree(const AutoFillProfile& profile, |
| 818 CreditCardWidgets widgets = {0}; | 606 GtkTreeIter* iter) { |
| 819 GtkWidget* vbox; | 607 gtk_list_store_append(list_store_, iter); |
| 820 GtkWidget* credit_card = InitGroupContentArea(IDS_AUTOFILL_NEW_CREDITCARD, | 608 gtk_list_store_set( |
| 821 &vbox); | 609 list_store_, iter, |
| 822 | 610 COL_WEIGHT, PANGO_WEIGHT_NORMAL, |
| 823 gtk_expander_set_expanded(GTK_EXPANDER(credit_card), expand); | 611 COL_WEIGHT_SET, TRUE, |
| 824 | 612 COL_TITLE, UTF16ToUTF8(profile.PreviewSummary()).c_str(), |
| 825 GtkWidget* label_table = InitFormTable(1, 2); | 613 -1); |
| 826 gtk_box_pack_start_defaults(GTK_BOX(vbox), label_table); | |
| 827 | |
| 828 widgets.label = FormTableAddLabelEntry(label_table, 0, 0, 1, | |
| 829 IDS_AUTOFILL_DIALOG_LABEL, credit_card, | |
| 830 G_CALLBACK(OnLabelChanged)); | |
| 831 | |
| 832 GtkWidget* name_cc_table = InitFormTable(2, 6); | |
| 833 gtk_box_pack_start_defaults(GTK_BOX(vbox), name_cc_table); | |
| 834 | |
| 835 widgets.name_on_card = FormTableAddExpandedEntry( | |
| 836 name_cc_table, 0, 0, 3, IDS_AUTOFILL_DIALOG_NAME_ON_CARD); | |
| 837 widgets.card_number = FormTableAddExpandedEntry( | |
| 838 name_cc_table, 1, 0, 3, IDS_AUTOFILL_DIALOG_CREDIT_CARD_NUMBER); | |
| 839 widgets.expiration_month = FormTableAddSizedEntry(name_cc_table, 1, 3, 2, 0); | |
| 840 widgets.expiration_year = FormTableAddSizedEntry(name_cc_table, 1, 4, 4, 0); | |
| 841 | |
| 842 FormTableSetLabel(name_cc_table, 1, 3, 2, | |
| 843 IDS_AUTOFILL_DIALOG_EXPIRATION_DATE); | |
| 844 | |
| 845 gtk_table_set_col_spacing(GTK_TABLE(name_cc_table), 3, 2); | |
| 846 | |
| 847 GtkWidget* addresses_table = InitFormTable(2, 5); | |
| 848 gtk_box_pack_start_defaults(GTK_BOX(vbox), addresses_table); | |
| 849 | |
| 850 FormTableSetLabel(addresses_table, 0, 0, 3, | |
| 851 IDS_AUTOFILL_DIALOG_BILLING_ADDRESS); | |
| 852 | |
| 853 GtkWidget* billing = gtk_combo_box_new_text(); | |
| 854 widgets.billing_address = billing; | |
| 855 std::string combo_text = l10n_util::GetStringUTF8( | |
| 856 IDS_AUTOFILL_DIALOG_CHOOSE_EXISTING_ADDRESS); | |
| 857 gtk_combo_box_append_text(GTK_COMBO_BOX(billing), combo_text.c_str()); | |
| 858 gtk_combo_box_set_active(GTK_COMBO_BOX(billing), 0); | |
| 859 FormTableSetWidget(addresses_table, billing, 0, 0, 2, false); | |
| 860 | |
| 861 for (std::vector<AddressWidgets>::const_iterator iter = | |
| 862 address_widgets_.begin(); | |
| 863 iter != address_widgets_.end(); ++iter) { | |
| 864 // TODO(jhawkins): Validate the label and DCHECK on !empty(). | |
| 865 std::string text = gtk_entry_get_text(GTK_ENTRY(iter->label)); | |
| 866 if (!text.empty()) | |
| 867 gtk_combo_box_append_text(GTK_COMBO_BOX(widgets.billing_address), | |
| 868 text.c_str()); | |
| 869 } | |
| 870 | |
| 871 GtkWidget* phone_table = InitFormTable(1, 1); | |
| 872 gtk_box_pack_start_defaults(GTK_BOX(vbox), phone_table); | |
| 873 | |
| 874 widgets.phone = | |
| 875 FormTableAddEntry(phone_table, 0, 0, 1, IDS_AUTOFILL_DIALOG_PHONE); | |
| 876 | |
| 877 GtkWidget* button = gtk_button_new_with_label( | |
| 878 l10n_util::GetStringUTF8(IDS_AUTOFILL_DELETE_BUTTON).c_str()); | |
| 879 g_signal_connect(button, "clicked", | |
| 880 G_CALLBACK(OnDeleteCreditCardClicked), this); | |
| 881 SetButtonData(button, widgets.label); | |
| 882 GtkWidget* alignment = gtk_alignment_new(0, 0, 0, 0); | |
| 883 gtk_container_add(GTK_CONTAINER(alignment), button); | |
| 884 gtk_box_pack_start_defaults(GTK_BOX(vbox), alignment); | |
| 885 | |
| 886 credit_card_widgets_.push_back(widgets); | |
| 887 return credit_card; | |
| 888 } | 614 } |
| 889 | 615 |
| 890 void AutoFillDialog::AddAddress(const AutoFillProfile& profile) { | 616 void AutoFillDialog::AddCreditCardToTree(const CreditCard& credit_card, |
| 891 GtkWidget* address = AddNewAddress(false); | 617 GtkTreeIter* iter) { |
| 892 gtk_expander_set_label(GTK_EXPANDER(address), | 618 gtk_list_store_append(list_store_, iter); |
| 893 UTF16ToUTF8(profile.Label()).c_str()); | 619 gtk_list_store_set( |
| 894 | 620 list_store_, iter, |
| 895 // We just pushed the widgets to the back of the vector. | 621 COL_WEIGHT, PANGO_WEIGHT_NORMAL, |
| 896 const AddressWidgets& widgets = address_widgets_.back(); | 622 COL_WEIGHT_SET, TRUE, |
| 897 SetEntryText(widgets.label, profile.Label()); | 623 COL_TITLE, UTF16ToUTF8(credit_card.PreviewSummary()).c_str(), |
| 898 SetEntryText(widgets.full_name, | 624 -1); |
| 899 profile.GetFieldText(AutoFillType(NAME_FULL))); | |
| 900 SetEntryText(widgets.email, | |
| 901 profile.GetFieldText(AutoFillType(EMAIL_ADDRESS))); | |
| 902 SetEntryText(widgets.company_name, | |
| 903 profile.GetFieldText(AutoFillType(COMPANY_NAME))); | |
| 904 SetEntryText(widgets.address_line1, | |
| 905 profile.GetFieldText(AutoFillType(ADDRESS_HOME_LINE1))); | |
| 906 SetEntryText(widgets.address_line2, | |
| 907 profile.GetFieldText(AutoFillType(ADDRESS_HOME_LINE2))); | |
| 908 SetEntryText(widgets.city, | |
| 909 profile.GetFieldText(AutoFillType(ADDRESS_HOME_CITY))); | |
| 910 SetEntryText(widgets.state, | |
| 911 profile.GetFieldText(AutoFillType(ADDRESS_HOME_STATE))); | |
| 912 SetEntryText(widgets.zipcode, | |
| 913 profile.GetFieldText(AutoFillType(ADDRESS_HOME_ZIP))); | |
| 914 SetEntryText(widgets.country, | |
| 915 profile.GetFieldText(AutoFillType(ADDRESS_HOME_COUNTRY))); | |
| 916 SetEntryText(widgets.phone, | |
| 917 profile.GetFieldText(AutoFillType(PHONE_HOME_WHOLE_NUMBER))); | |
| 918 SetEntryText(widgets.fax, | |
| 919 profile.GetFieldText(AutoFillType(PHONE_FAX_WHOLE_NUMBER))); | |
| 920 | |
| 921 gtk_box_pack_start(GTK_BOX(addresses_vbox_), address, FALSE, FALSE, 0); | |
| 922 gtk_widget_show_all(address); | |
| 923 } | 625 } |
| 924 | 626 |
| 925 void AutoFillDialog::AddCreditCard(const CreditCard& credit_card) { | 627 void AutoFillDialog::GetSelectedEntries( |
| 926 GtkWidget* credit_card_widget = AddNewCreditCard(false); | 628 std::vector<AutoFillProfile*>* profiles, |
| 927 gtk_expander_set_label(GTK_EXPANDER(credit_card_widget), | 629 std::vector<CreditCard*>* cards) { |
| 928 UTF16ToUTF8(credit_card.Label()).c_str()); | 630 std::set<int> selection; |
| 631 gtk_tree::GetSelectedIndices( |
| 632 gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_)), &selection); |
| 929 | 633 |
| 930 // We just pushed the widgets to the back of the vector. | 634 for (std::set<int>::const_iterator i = selection.begin(); |
| 931 const CreditCardWidgets& widgets = credit_card_widgets_.back(); | 635 i != selection.end(); ++i) { |
| 932 SetEntryText(widgets.label, credit_card.Label()); | 636 // 2 is the number of header rows. |
| 933 SetEntryText(widgets.name_on_card, | 637 int index = *i - 2; |
| 934 credit_card.GetFieldText(AutoFillType(CREDIT_CARD_NAME))); | 638 if (index >= 0 && |
| 935 // Set obfuscated number if not empty. | 639 index < static_cast<int>(personal_data_->profiles().size())) { |
| 936 credit_card_widgets_.back().original_card_number = | 640 profiles->push_back(personal_data_->profiles()[index]); |
| 937 credit_card.GetFieldText(AutoFillType(CREDIT_CARD_NUMBER)); | 641 continue; |
| 938 string16 credit_card_number; | 642 } |
| 939 if (!widgets.original_card_number.empty()) | |
| 940 credit_card_number = credit_card.ObfuscatedNumber(); | |
| 941 SetEntryText(widgets.card_number, credit_card_number); | |
| 942 SetEntryText(widgets.expiration_month, | |
| 943 credit_card.GetFieldText(AutoFillType(CREDIT_CARD_EXP_MONTH))); | |
| 944 SetEntryText( | |
| 945 widgets.expiration_year, | |
| 946 credit_card.GetFieldText(AutoFillType(CREDIT_CARD_EXP_4_DIGIT_YEAR))); | |
| 947 | 643 |
| 948 // Two cases to consider: | 644 // Empty row, header and separator are next. |
| 949 // address not found - This means the address is not set and | 645 index -= profile_count_ + 3; |
| 950 // FindIndexOfAddress returns -1. -1 + 1 = 0, meaning the first item will | 646 if (index >= 0 && index < |
| 951 // be selected, "Choose existing address". | 647 static_cast<int>(personal_data_->credit_cards().size())) { |
| 952 // | 648 cards->push_back(personal_data_->credit_cards()[index]); |
| 953 // address is found - The index returned needs to be offset by one in order | 649 } |
| 954 // to compensate for the first entry, "Choose existing address". | 650 } |
| 955 int index = FindIndexOfAddress(credit_card.billing_address()) + 1; | |
| 956 gtk_combo_box_set_active(GTK_COMBO_BOX(widgets.billing_address), index); | |
| 957 | |
| 958 gtk_box_pack_start(GTK_BOX(creditcards_vbox_), credit_card_widget, | |
| 959 FALSE, FALSE, 0); | |
| 960 gtk_widget_show_all(credit_card_widget); | |
| 961 } | 651 } |
| 962 | 652 |
| 963 int AutoFillDialog::FindIndexOfAddress(string16 billing_address) { | 653 } // namespace |
| 964 int index = 0; | |
| 965 for (std::vector<AddressWidgets>::const_iterator iter = | |
| 966 address_widgets_.begin(); | |
| 967 iter != address_widgets_.end(); ++iter, ++index) { | |
| 968 std::string text = gtk_entry_get_text(GTK_ENTRY(iter->label)); | |
| 969 if (UTF8ToUTF16(text) == billing_address) | |
| 970 return index; | |
| 971 } | |
| 972 | |
| 973 return -1; | |
| 974 } | |
| 975 | 654 |
| 976 /////////////////////////////////////////////////////////////////////////////// | 655 /////////////////////////////////////////////////////////////////////////////// |
| 977 // Factory/finder method: | 656 // Factory/finder method: |
| 978 | 657 |
| 979 void ShowAutoFillDialog(gfx::NativeView parent, | 658 void ShowAutoFillDialog(gfx::NativeView parent, |
| 980 AutoFillDialogObserver* observer, | 659 AutoFillDialogObserver* observer, |
| 981 Profile* profile) { | 660 Profile* profile) { |
| 982 DCHECK(profile); | 661 DCHECK(profile); |
| 983 | 662 |
| 984 if (!dialog) { | 663 if (!dialog) |
| 985 dialog = new AutoFillDialog(observer, | 664 dialog = new AutoFillDialog(profile, observer); |
| 986 profile->GetPersonalDataManager(), | |
| 987 NULL, | |
| 988 NULL); | |
| 989 } | |
| 990 dialog->Show(); | 665 dialog->Show(); |
| 991 } | 666 } |
| OLD | NEW |