// Copyright (c) 2009 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/gtk/options/passwords_page_gtk.h" #include "app/gfx/gtk_util.h" #include "app/l10n_util.h" #include "app/resource_bundle.h" #include "chrome/common/gtk_tree.h" #include "chrome/common/gtk_util.h" #include "chrome/common/pref_names.h" #include "chrome/common/pref_service.h" #include "chrome/common/url_constants.h" #include "grit/app_resources.h" #include "grit/chromium_strings.h" #include "grit/generated_resources.h" #include "net/base/net_util.h" namespace { // Initial width of the first column. const int kSiteColumnInitialSize = 265; // Column ids for |password_list_store_|. enum { COL_SITE, COL_USERNAME, COL_COUNT, }; } // anonymous namespace /////////////////////////////////////////////////////////////////////////////// // PasswordsPageGtk, public: PasswordsPageGtk::PasswordsPageGtk(Profile* profile) : populater(this), password_showing_(false), profile_(profile) { remove_button_ = gtk_button_new_with_label( l10n_util::GetStringUTF8(IDS_PASSWORDS_PAGE_VIEW_REMOVE_BUTTON).c_str()); gtk_widget_set_sensitive(remove_button_, FALSE); g_signal_connect(remove_button_, "clicked", G_CALLBACK(OnRemoveButtonClicked), this); remove_all_button_ = gtk_button_new_with_label(l10n_util::GetStringUTF8( IDS_PASSWORDS_PAGE_VIEW_REMOVE_ALL_BUTTON).c_str()); gtk_widget_set_sensitive(remove_all_button_, FALSE); g_signal_connect(remove_all_button_, "clicked", G_CALLBACK(OnRemoveAllButtonClicked), this); show_password_button_ = gtk_button_new_with_label( l10n_util::GetStringUTF8(IDS_PASSWORDS_PAGE_VIEW_HIDE_BUTTON).c_str()); GtkRequisition hide_size, show_size; // Get the size request of the button with the "hide password" text. gtk_widget_size_request(show_password_button_, &hide_size); gtk_button_set_label(GTK_BUTTON(show_password_button_), l10n_util::GetStringUTF8(IDS_PASSWORDS_PAGE_VIEW_SHOW_BUTTON).c_str()); // Get the size request of the button with the "show password" text. gtk_widget_size_request(show_password_button_, &show_size); // Determine the maximum width and height. if (hide_size.width > show_size.width) show_size.width = hide_size.width; if (hide_size.height > show_size.height) show_size.height = hide_size.height; // Force the button to be large enough for both labels. gtk_widget_set_size_request(show_password_button_, show_size.width, show_size.height); gtk_widget_set_sensitive(show_password_button_, FALSE); g_signal_connect(show_password_button_, "clicked", G_CALLBACK(OnShowPasswordButtonClicked), this); password_ = gtk_label_new(""); GtkWidget* buttons = gtk_vbox_new(FALSE, gtk_util::kControlSpacing); gtk_box_pack_start(GTK_BOX(buttons), remove_button_, FALSE, FALSE, 0); gtk_box_pack_start(GTK_BOX(buttons), remove_all_button_, FALSE, FALSE, 0); gtk_box_pack_start(GTK_BOX(buttons), show_password_button_, FALSE, FALSE, 0); gtk_box_pack_start(GTK_BOX(buttons), password_, FALSE, FALSE, 0); GtkWidget* scroll_window = gtk_scrolled_window_new(NULL, NULL); gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll_window), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scroll_window), GTK_SHADOW_ETCHED_IN); // Sets password_tree_ among other things. InitPasswordTree(); gtk_container_add(GTK_CONTAINER(scroll_window), password_tree_); page_ = gtk_hbox_new(FALSE, gtk_util::kControlSpacing); gtk_container_set_border_width(GTK_CONTAINER(page_), gtk_util::kContentAreaBorder); gtk_box_pack_end(GTK_BOX(page_), buttons, FALSE, FALSE, 0); gtk_box_pack_end(GTK_BOX(page_), scroll_window, TRUE, TRUE, 0); } PasswordsPageGtk::~PasswordsPageGtk() { } /////////////////////////////////////////////////////////////////////////////// // PasswordsPageGtk, private: void PasswordsPageGtk::InitPasswordTree() { password_list_store_ = gtk_list_store_new(COL_COUNT, G_TYPE_STRING, G_TYPE_STRING); password_list_sort_ = gtk_tree_model_sort_new_with_model( GTK_TREE_MODEL(password_list_store_)); gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(password_list_sort_), COL_SITE, CompareSite, this, NULL); gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(password_list_sort_), COL_USERNAME, CompareUsername, this, NULL); password_tree_ = gtk_tree_view_new_with_model(password_list_sort_); g_object_unref(password_list_store_); g_object_unref(password_list_sort_); gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(password_tree_), TRUE); password_selection_ = gtk_tree_view_get_selection( GTK_TREE_VIEW(password_tree_)); gtk_tree_selection_set_mode(password_selection_, GTK_SELECTION_SINGLE); g_signal_connect(password_selection_, "changed", G_CALLBACK(OnPasswordSelectionChanged), this); GtkTreeViewColumn* column = gtk_tree_view_column_new_with_attributes( l10n_util::GetStringUTF8(IDS_PASSWORDS_PAGE_VIEW_SITE_COLUMN).c_str(), gtk_cell_renderer_text_new(), "text", COL_SITE, NULL); gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED); gtk_tree_view_column_set_resizable(column, TRUE); gtk_tree_view_column_set_fixed_width(column, kSiteColumnInitialSize); gtk_tree_view_column_set_sort_column_id(column, COL_SITE); gtk_tree_view_append_column(GTK_TREE_VIEW(password_tree_), column); column = gtk_tree_view_column_new_with_attributes( l10n_util::GetStringUTF8(IDS_PASSWORDS_PAGE_VIEW_USERNAME_COLUMN).c_str(), gtk_cell_renderer_text_new(), "text", COL_USERNAME, NULL); gtk_tree_view_column_set_sort_column_id(column, COL_USERNAME); gtk_tree_view_append_column(GTK_TREE_VIEW(password_tree_), column); populater.populate(); } PasswordStore* PasswordsPageGtk::GetPasswordStore() { return profile_->GetPasswordStore(Profile::EXPLICIT_ACCESS); } void PasswordsPageGtk::SetPasswordList( const std::vector<webkit_glue::PasswordForm*>& result) { std::wstring languages = profile_->GetPrefs()->GetString(prefs::kAcceptLanguages); gtk_list_store_clear(password_list_store_); password_list_.resize(result.size()); for (size_t i = 0; i < result.size(); ++i) { password_list_[i] = *result[i]; std::wstring formatted = net::FormatUrl(result[i]->origin, languages, false, UnescapeRule::NONE, NULL, NULL, NULL); std::string site = WideToUTF8(formatted); std::string user = UTF16ToUTF8(result[i]->username_value); GtkTreeIter iter; gtk_list_store_insert_with_values(password_list_store_, &iter, (gint) i, COL_SITE, site.c_str(), COL_USERNAME, user.c_str(), -1); } gtk_widget_set_sensitive(remove_all_button_, result.size() > 0); } // static void PasswordsPageGtk::OnRemoveButtonClicked(GtkButton* widget, PasswordsPageGtk* page) { GtkTreeIter iter; if (!gtk_tree_selection_get_selected(page->password_selection_, NULL, &iter)) { NOTREACHED(); return; } GtkTreePath* path = gtk_tree_model_get_path( GTK_TREE_MODEL(page->password_list_sort_), &iter); gint index = gtk_tree::GetTreeSortChildRowNumForPath( page->password_list_sort_, path); gtk_tree_path_free(path); GtkTreeIter child_iter; gtk_tree_model_sort_convert_iter_to_child_iter( GTK_TREE_MODEL_SORT(page->password_list_sort_), &child_iter, &iter); // Remove from GTK list, DB, and vector. gtk_list_store_remove(page->password_list_store_, &child_iter); page->GetPasswordStore()->RemoveLogin(page->password_list_[index]); page->password_list_.erase(page->password_list_.begin() + index); gtk_widget_set_sensitive(page->remove_all_button_, page->password_list_.size() > 0); } // static void PasswordsPageGtk::OnRemoveAllButtonClicked(GtkButton* widget, PasswordsPageGtk* page) { GtkWindow* window = GTK_WINDOW(gtk_widget_get_toplevel(page->page_)); GtkWidget* confirm = gtk_message_dialog_new( window, static_cast<GtkDialogFlags>( GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT), GTK_MESSAGE_QUESTION, GTK_BUTTONS_YES_NO, "%s", l10n_util::GetStringUTF8( IDS_PASSWORDS_PAGE_VIEW_TEXT_DELETE_ALL_PASSWORDS).c_str()); gtk_util::ApplyMessageDialogQuirks(confirm); gtk_window_set_title(GTK_WINDOW(confirm), l10n_util::GetStringUTF8( IDS_PASSWORDS_PAGE_VIEW_CAPTION_DELETE_ALL_PASSWORDS).c_str()); g_signal_connect(confirm, "response", G_CALLBACK(OnRemoveAllConfirmResponse), page); gtk_widget_show_all(confirm); } // static void PasswordsPageGtk::OnRemoveAllConfirmResponse(GtkDialog* confirm, gint response, PasswordsPageGtk* page) { bool confirmed = false; switch (response) { case GTK_RESPONSE_YES: confirmed = true; break; default: break; } gtk_widget_destroy(GTK_WIDGET(confirm)); if (!confirmed) return; // Remove from GTK list, DB, and vector. PasswordStore* store = page->GetPasswordStore(); gtk_list_store_clear(page->password_list_store_); for (size_t i = 0; i < page->password_list_.size(); ++i) { store->RemoveLogin(page->password_list_[i]); } page->password_list_.clear(); gtk_widget_set_sensitive(page->remove_all_button_, FALSE); } // static void PasswordsPageGtk::OnShowPasswordButtonClicked(GtkButton* widget, PasswordsPageGtk* page) { page->password_showing_ = !page->password_showing_; if (!page->password_showing_) { // Hide the password. gtk_label_set_text(GTK_LABEL(page->password_), ""); gtk_button_set_label(GTK_BUTTON(page->show_password_button_), l10n_util::GetStringUTF8(IDS_PASSWORDS_PAGE_VIEW_SHOW_BUTTON).c_str()); return; } // Show the password. GtkTreeIter iter; if (!gtk_tree_selection_get_selected(page->password_selection_, NULL, &iter)) { NOTREACHED(); return; } GtkTreePath* path = gtk_tree_model_get_path( GTK_TREE_MODEL(page->password_list_sort_), &iter); gint index = gtk_tree::GetTreeSortChildRowNumForPath( page->password_list_sort_, path); gtk_tree_path_free(path); std::string pass = UTF16ToUTF8(page->password_list_[index].password_value); gtk_label_set_text(GTK_LABEL(page->password_), pass.c_str()); gtk_button_set_label(GTK_BUTTON(page->show_password_button_), l10n_util::GetStringUTF8(IDS_PASSWORDS_PAGE_VIEW_HIDE_BUTTON).c_str()); } // static void PasswordsPageGtk::OnPasswordSelectionChanged(GtkTreeSelection* selection, PasswordsPageGtk* page) { // No matter how the selection changed, we want to hide the old password. gtk_label_set_text(GTK_LABEL(page->password_), ""); gtk_button_set_label(GTK_BUTTON(page->show_password_button_), l10n_util::GetStringUTF8(IDS_PASSWORDS_PAGE_VIEW_SHOW_BUTTON).c_str()); page->password_showing_ = false; GtkTreeIter iter; if (!gtk_tree_selection_get_selected(selection, NULL, &iter)) { gtk_widget_set_sensitive(page->show_password_button_, FALSE); gtk_widget_set_sensitive(page->remove_button_, FALSE); return; } gtk_widget_set_sensitive(page->show_password_button_, TRUE); gtk_widget_set_sensitive(page->remove_button_, TRUE); } // static gint PasswordsPageGtk::CompareSite(GtkTreeModel* model, GtkTreeIter* a, GtkTreeIter* b, gpointer window) { int row1 = gtk_tree::GetRowNumForIter(model, a); int row2 = gtk_tree::GetRowNumForIter(model, b); PasswordsPageGtk* page = reinterpret_cast<PasswordsPageGtk*>(window); return page->password_list_[row1].origin.spec().compare( page->password_list_[row2].origin.spec()); } // static gint PasswordsPageGtk::CompareUsername(GtkTreeModel* model, GtkTreeIter* a, GtkTreeIter* b, gpointer window) { int row1 = gtk_tree::GetRowNumForIter(model, a); int row2 = gtk_tree::GetRowNumForIter(model, b); PasswordsPageGtk* page = reinterpret_cast<PasswordsPageGtk*>(window); return page->password_list_[row1].username_value.compare( page->password_list_[row2].username_value); } void PasswordsPageGtk::PasswordListPopulater::populate() { DCHECK(!pending_login_query_); PasswordStore* store = page_->GetPasswordStore(); pending_login_query_ = store->GetAutofillableLogins(this); } void PasswordsPageGtk::PasswordListPopulater::OnPasswordStoreRequestDone( int handle, const std::vector<webkit_glue::PasswordForm*>& result) { DCHECK_EQ(pending_login_query_, handle); pending_login_query_ = 0; page_->SetPasswordList(result); }