summaryrefslogtreecommitdiffstats
path: root/chrome/browser/autofill/autofill_dialog_gtk.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chrome/browser/autofill/autofill_dialog_gtk.cc')
-rw-r--r--chrome/browser/autofill/autofill_dialog_gtk.cc666
1 files changed, 666 insertions, 0 deletions
diff --git a/chrome/browser/autofill/autofill_dialog_gtk.cc b/chrome/browser/autofill/autofill_dialog_gtk.cc
new file mode 100644
index 0000000..ec8a1fe
--- /dev/null
+++ b/chrome/browser/autofill/autofill_dialog_gtk.cc
@@ -0,0 +1,666 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/autofill/autofill_dialog.h"
+
+#include <gtk/gtk.h>
+
+#include <algorithm>
+#include <string>
+#include <vector>
+
+#include "app/gtk_signal.h"
+#include "app/l10n_util.h"
+#include "base/logging.h"
+#include "base/message_loop.h"
+#include "base/utf_string_conversions.h"
+#include "chrome/browser/autofill/autofill_manager.h"
+#include "chrome/browser/autofill/autofill_profile.h"
+#include "chrome/browser/autofill/credit_card.h"
+#include "chrome/browser/autofill/form_group.h"
+#include "chrome/browser/autofill/personal_data_manager.h"
+#include "chrome/browser/autofill/phone_number.h"
+#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/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"
+
+// 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);
+
+// 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);
+
+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,
+ public NotificationObserver {
+ public:
+ // 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();
+
+ private:
+ // 'destroy' signal handler. Calls DeleteSoon on the global singleton dialog
+ // object.
+ 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.
+ 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();
+
+ // Loads AutoFill profiles and credit cards using the PersonalDataManager.
+ void LoadAutoFillData();
+
+ // Creates the dialog UI widgets.
+ void InitializeWidgets();
+
+ // 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_;
+
+ // The personal data manager, used to load AutoFill profiles and credit cards.
+ // Unowned pointer, may not be NULL.
+ PersonalDataManager* personal_data_;
+
+ // Number of profiles we're displaying.
+ int profile_count_;
+
+ // The AutoFill dialog.
+ GtkWidget* dialog_;
+
+ BooleanPrefMember enable_form_autofill_;
+
+ GtkWidget* form_autofill_enable_check_;
+
+ // Displays the addresses then credit cards.
+ GtkListStore* list_store_;
+
+ // Displays the list_store_.
+ GtkWidget* tree_;
+
+ GtkWidget* add_address_button_;
+ GtkWidget* add_credit_card_button_;
+ GtkWidget* edit_button_;
+ GtkWidget* remove_button_;
+
+ DISALLOW_COPY_AND_ASSIGN(AutoFillDialog);
+};
+
+// The singleton AutoFill dialog object.
+static AutoFillDialog* dialog = NULL;
+
+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();
+
+ gtk_util::ShowDialogWithLocalizedSize(dialog_,
+ IDS_AUTOFILL_DIALOG_WIDTH_CHARS,
+ IDS_AUTOFILL_DIALOG_HEIGHT_LINES,
+ true);
+}
+
+AutoFillDialog::~AutoFillDialog() {
+ // Removes observer if we are observing Profile load. Does nothing otherwise.
+ personal_data_->RemoveObserver(this);
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// PersonalDataManager::Observer implementation:
+void AutoFillDialog::OnPersonalDataLoaded() {
+ LoadAutoFillData();
+}
+
+void AutoFillDialog::OnPersonalDataChanged() {
+ LoadAutoFillData();
+}
+
+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();
+ }
+}
+
+void AutoFillDialog::Show() {
+ gtk_util::PresentWindow(dialog_, gtk_get_current_event_time());
+}
+
+void AutoFillDialog::OnDestroy(GtkWidget* widget) {
+ dialog = NULL;
+ MessageLoop::current()->DeleteSoon(FROM_HERE, this);
+}
+
+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 == kAutoFillDialogAboutLink)
+ OnLinkActivated();
+}
+
+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();
+}
+
+void AutoFillDialog::OnRowActivated(GtkTreeView* tree_view,
+ GtkTreePath* path,
+ GtkTreeViewColumn* column) {
+ if (GetSelectionType() == SELECTION_SINGLE)
+ OnEdit(NULL);
+}
+
+void AutoFillDialog::OnSelectionChanged(GtkTreeSelection* selection) {
+ UpdateWidgetState();
+}
+
+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);
+}
+
+void AutoFillDialog::OnEdit(GtkWidget* widget) {
+ DCHECK_EQ(SELECTION_SINGLE, GetSelectionType());
+
+ 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]);
+}
+
+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);
+ }
+ }
+
+ 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);
+ }
+ }
+
+ observer_->OnAutoFillDialogApply(&profiles, &cards);
+}
+
+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() {
+ Browser* browser = BrowserList::GetLastActive();
+ browser->OpenURL(GURL(kAutoFillLearnMoreUrl), GURL(), NEW_FOREGROUND_TAB,
+ PageTransition::TYPED);
+}
+
+void AutoFillDialog::LoadAutoFillData() {
+ if (!personal_data_->IsDataLoaded()) {
+ UpdateWidgetState();
+ return;
+ }
+
+ // 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_++;
+ }
+
+ // 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() {
+ dialog_ = gtk_dialog_new_with_buttons(
+ l10n_util::GetStringUTF8(IDS_AUTOFILL_OPTIONS).c_str(),
+ // AutoFill dialog is shared between all browser windows.
+ NULL,
+ // Non-modal.
+ GTK_DIALOG_NO_SEPARATOR,
+ GTK_STOCK_OK,
+ GTK_RESPONSE_OK,
+ NULL);
+
+ 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_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);
+
+ 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_HELP_LABEL).c_str());
+ gtk_dialog_add_action_widget(GTK_DIALOG(dialog_), link,
+ kAutoFillDialogAboutLink);
+
+ // Setting the link widget to secondary positions the button on the left side
+ // of the action area (vice versa for RTL layout).
+ gtk_button_box_set_child_secondary(
+ GTK_BUTTON_BOX(GTK_DIALOG(dialog_)->action_area), link, TRUE);
+}
+
+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);
+ }
+}
+
+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);
+ }
+
+ if (is_header) {
+ *type |= AutoFillDialog::SELECTION_HEADER;
+ } else {
+ if ((*type & AutoFillDialog::SELECTION_SINGLE) == 0)
+ *type |= AutoFillDialog::SELECTION_SINGLE;
+ else
+ *type |= AutoFillDialog::SELECTION_MULTI;
+ }
+}
+
+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;
+ }
+
+ // 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]);
+ }
+ }
+}
+
+} // namespace
+
+///////////////////////////////////////////////////////////////////////////////
+// Factory/finder method:
+
+void ShowAutoFillDialog(gfx::NativeView parent,
+ AutoFillDialogObserver* observer,
+ Profile* profile) {
+ DCHECK(profile);
+
+ if (!dialog)
+ dialog = new AutoFillDialog(profile, observer);
+ dialog->Show();
+}