| OLD | NEW |
| (Empty) | |
| 1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #include "chrome/browser/gtk/options/cookies_view.h" |
| 6 |
| 7 #include "app/l10n_util.h" |
| 8 #include "base/gfx/gtk_util.h" |
| 9 #include "base/message_loop.h" |
| 10 #include "base/string_util.h" |
| 11 #include "chrome/browser/cookies_table_model.h" |
| 12 #include "chrome/common/gtk_util.h" |
| 13 #include "grit/generated_resources.h" |
| 14 #include "third_party/skia/include/core/SkBitmap.h" |
| 15 |
| 16 namespace { |
| 17 |
| 18 // Initial size for dialog. |
| 19 const int kDialogDefaultWidth = 450; |
| 20 const int kDialogDefaultHeight = 450; |
| 21 |
| 22 // Delay after entering filter text before filtering occurs. |
| 23 const int kSearchFilterDelayMs = 500; |
| 24 |
| 25 // Response ids for our custom buttons. |
| 26 enum { |
| 27 RESPONSE_REMOVE = 1, |
| 28 RESPONSE_REMOVE_ALL |
| 29 }; |
| 30 |
| 31 // Column ids for |list_store_|. |
| 32 enum { |
| 33 COL_ICON, |
| 34 COL_SITE, |
| 35 COL_COOKIE_NAME, |
| 36 COL_COUNT, |
| 37 }; |
| 38 |
| 39 // The currently open cookie manager, if any. |
| 40 CookiesView* instance_ = NULL; |
| 41 |
| 42 // TODO(mattm): These functions are also in url_picker_dialog_gtk. Move them to |
| 43 // some sort of gtk table model helper? |
| 44 // Get the row number corresponding to |path|. |
| 45 gint GetRowNumForPath(GtkTreePath* path) { |
| 46 gint* indices = gtk_tree_path_get_indices(path); |
| 47 if (!indices) { |
| 48 NOTREACHED(); |
| 49 return -1; |
| 50 } |
| 51 return indices[0]; |
| 52 } |
| 53 |
| 54 // Get the row number corresponding to |iter|. |
| 55 gint GetRowNumForIter(GtkTreeModel* model, GtkTreeIter* iter) { |
| 56 GtkTreePath* path = gtk_tree_model_get_path(model, iter); |
| 57 int row = GetRowNumForPath(path); |
| 58 gtk_tree_path_free(path); |
| 59 return row; |
| 60 } |
| 61 |
| 62 // Get the row number in the child tree model corresponding to |sort_path| in |
| 63 // the parent tree model. |
| 64 gint GetTreeSortChildRowNumForPath(GtkTreeModel* sort_model, |
| 65 GtkTreePath* sort_path) { |
| 66 GtkTreePath *child_path = gtk_tree_model_sort_convert_path_to_child_path( |
| 67 GTK_TREE_MODEL_SORT(sort_model), sort_path); |
| 68 int row = GetRowNumForPath(child_path); |
| 69 gtk_tree_path_free(child_path); |
| 70 return row; |
| 71 } |
| 72 |
| 73 } // namespace |
| 74 |
| 75 CookiesView::~CookiesView() { |
| 76 } |
| 77 |
| 78 // static |
| 79 void CookiesView::Show(Profile* profile) { |
| 80 DCHECK(profile); |
| 81 |
| 82 // If there's already an existing editor window, activate it. |
| 83 if (instance_) { |
| 84 gtk_window_present(GTK_WINDOW(instance_->dialog_)); |
| 85 } else { |
| 86 instance_ = new CookiesView(profile); |
| 87 } |
| 88 } |
| 89 |
| 90 CookiesView::CookiesView(Profile* profile) |
| 91 : profile_(profile), |
| 92 filter_update_factory_(this) { |
| 93 Init(); |
| 94 } |
| 95 |
| 96 void CookiesView::Init() { |
| 97 dialog_ = gtk_dialog_new_with_buttons( |
| 98 l10n_util::GetStringUTF8(IDS_COOKIES_WINDOW_TITLE).c_str(), |
| 99 NULL, |
| 100 GTK_DIALOG_NO_SEPARATOR, |
| 101 GTK_STOCK_CLOSE, |
| 102 GTK_RESPONSE_CLOSE, |
| 103 NULL); |
| 104 |
| 105 remove_button_ = gtk_dialog_add_button( |
| 106 GTK_DIALOG(dialog_), |
| 107 gtk_util::ConvertAcceleratorsFromWindowsStyle( |
| 108 l10n_util::GetStringUTF8(IDS_COOKIES_REMOVE_LABEL)).c_str(), |
| 109 RESPONSE_REMOVE); |
| 110 gtk_button_box_set_child_secondary( |
| 111 GTK_BUTTON_BOX(GTK_DIALOG(dialog_)->action_area), |
| 112 remove_button_, |
| 113 TRUE); |
| 114 |
| 115 remove_all_button_ = gtk_dialog_add_button( |
| 116 GTK_DIALOG(dialog_), |
| 117 gtk_util::ConvertAcceleratorsFromWindowsStyle( |
| 118 l10n_util::GetStringUTF8(IDS_COOKIES_REMOVE_ALL_LABEL)).c_str(), |
| 119 RESPONSE_REMOVE_ALL); |
| 120 gtk_button_box_set_child_secondary( |
| 121 GTK_BUTTON_BOX(GTK_DIALOG(dialog_)->action_area), |
| 122 remove_all_button_, |
| 123 TRUE); |
| 124 |
| 125 gtk_dialog_set_default_response(GTK_DIALOG(dialog_), GTK_RESPONSE_CLOSE); |
| 126 gtk_window_set_default_size(GTK_WINDOW(dialog_), kDialogDefaultWidth, |
| 127 kDialogDefaultHeight); |
| 128 gtk_box_set_spacing(GTK_BOX(GTK_DIALOG(dialog_)->vbox), |
| 129 gtk_util::kContentAreaSpacing); |
| 130 g_signal_connect(dialog_, "response", G_CALLBACK(OnResponse), this); |
| 131 g_signal_connect(dialog_, "destroy", G_CALLBACK(OnWindowDestroy), this); |
| 132 |
| 133 // Filtering controls. |
| 134 GtkWidget* filter_hbox = gtk_hbox_new(FALSE, gtk_util::kControlSpacing); |
| 135 filter_entry_ = gtk_entry_new(); |
| 136 g_signal_connect(G_OBJECT(filter_entry_), "activate", |
| 137 G_CALLBACK(OnFilterEntryActivated), this); |
| 138 g_signal_connect(G_OBJECT(filter_entry_), "changed", |
| 139 G_CALLBACK(OnFilterEntryChanged), this); |
| 140 gtk_box_pack_start(GTK_BOX(filter_hbox), filter_entry_, |
| 141 TRUE, TRUE, 0); |
| 142 filter_clear_button_ = gtk_button_new_with_mnemonic( |
| 143 gtk_util::ConvertAcceleratorsFromWindowsStyle( |
| 144 l10n_util::GetStringUTF8(IDS_COOKIES_CLEAR_SEARCH_LABEL)).c_str()); |
| 145 g_signal_connect(G_OBJECT(filter_clear_button_), "clicked", |
| 146 G_CALLBACK(OnFilterClearButtonClicked), this); |
| 147 gtk_box_pack_start(GTK_BOX(filter_hbox), filter_clear_button_, |
| 148 FALSE, FALSE, 0); |
| 149 |
| 150 GtkWidget* filter_controls = gtk_util::CreateLabeledControlsGroup(NULL, |
| 151 l10n_util::GetStringUTF8(IDS_COOKIES_SEARCH_LABEL).c_str(), filter_hbox, |
| 152 NULL); |
| 153 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog_)->vbox), filter_controls, |
| 154 FALSE, FALSE, 0); |
| 155 |
| 156 // Cookie list. |
| 157 GtkWidget* cookie_list_vbox = gtk_vbox_new(FALSE, gtk_util::kControlSpacing); |
| 158 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog_)->vbox), cookie_list_vbox, |
| 159 TRUE, TRUE, 0); |
| 160 |
| 161 GtkWidget* description_label = gtk_label_new( |
| 162 l10n_util::GetStringUTF8(IDS_COOKIES_INFO_LABEL).c_str()); |
| 163 GtkWidget* description_label_alignment = gtk_alignment_new( |
| 164 0.0, 0.5, 0.0, 0.0); |
| 165 gtk_container_add(GTK_CONTAINER(description_label_alignment), |
| 166 description_label); |
| 167 gtk_box_pack_start(GTK_BOX(cookie_list_vbox), description_label_alignment, |
| 168 FALSE, FALSE, 0); |
| 169 |
| 170 GtkWidget* scroll_window = gtk_scrolled_window_new(NULL, NULL); |
| 171 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll_window), |
| 172 GTK_POLICY_AUTOMATIC, |
| 173 GTK_POLICY_AUTOMATIC); |
| 174 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scroll_window), |
| 175 GTK_SHADOW_ETCHED_IN); |
| 176 gtk_box_pack_start(GTK_BOX(cookie_list_vbox), scroll_window, TRUE, TRUE, 0); |
| 177 |
| 178 list_store_ = gtk_list_store_new(COL_COUNT, |
| 179 GDK_TYPE_PIXBUF, |
| 180 G_TYPE_STRING, |
| 181 G_TYPE_STRING); |
| 182 list_sort_ = gtk_tree_model_sort_new_with_model(GTK_TREE_MODEL(list_store_)); |
| 183 gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(list_sort_), |
| 184 COL_SITE, CompareSite, this, NULL); |
| 185 gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(list_sort_), |
| 186 COL_COOKIE_NAME, CompareCookieName, this, |
| 187 NULL); |
| 188 tree_ = gtk_tree_view_new_with_model(GTK_TREE_MODEL(list_sort_)); |
| 189 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(tree_), TRUE); |
| 190 gtk_container_add(GTK_CONTAINER(scroll_window), tree_); |
| 191 |
| 192 GtkTreeViewColumn* site_column = gtk_tree_view_column_new(); |
| 193 GtkCellRenderer* pixbuf_renderer = gtk_cell_renderer_pixbuf_new(); |
| 194 gtk_tree_view_column_pack_start(site_column, pixbuf_renderer, FALSE); |
| 195 gtk_tree_view_column_add_attribute(site_column, pixbuf_renderer, "pixbuf", |
| 196 COL_ICON); |
| 197 GtkCellRenderer* site_renderer = gtk_cell_renderer_text_new(); |
| 198 gtk_tree_view_column_pack_start(site_column, site_renderer, TRUE); |
| 199 gtk_tree_view_column_add_attribute(site_column, site_renderer, "text", |
| 200 COL_SITE); |
| 201 gtk_tree_view_column_set_title( |
| 202 site_column, l10n_util::GetStringUTF8( |
| 203 IDS_COOKIES_DOMAIN_COLUMN_HEADER).c_str()); |
| 204 gtk_tree_view_column_set_sort_column_id(site_column, COL_SITE); |
| 205 gtk_tree_view_append_column(GTK_TREE_VIEW(tree_), site_column); |
| 206 |
| 207 GtkTreeViewColumn* name_column = gtk_tree_view_column_new_with_attributes( |
| 208 l10n_util::GetStringUTF8( |
| 209 IDS_COOKIES_NAME_COLUMN_HEADER).c_str(), |
| 210 gtk_cell_renderer_text_new(), |
| 211 "text", COL_COOKIE_NAME, |
| 212 NULL); |
| 213 gtk_tree_view_column_set_sort_column_id(name_column, COL_COOKIE_NAME); |
| 214 gtk_tree_view_append_column(GTK_TREE_VIEW(tree_), name_column); |
| 215 |
| 216 selection_ = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_)); |
| 217 gtk_tree_selection_set_mode(selection_, GTK_SELECTION_MULTIPLE); |
| 218 g_signal_connect(G_OBJECT(selection_), "changed", |
| 219 G_CALLBACK(OnSelectionChanged), this); |
| 220 |
| 221 // Initialize model. |
| 222 cookies_table_model_.reset(new CookiesTableModel(profile_)); |
| 223 cookies_table_model_->SetObserver(this); |
| 224 OnModelChanged(); |
| 225 |
| 226 // Show dialog. |
| 227 EnableControls(); |
| 228 gtk_widget_show_all(dialog_); |
| 229 } |
| 230 |
| 231 void CookiesView::EnableControls() { |
| 232 gtk_widget_set_sensitive( |
| 233 remove_button_, gtk_tree_selection_count_selected_rows(selection_) > 0); |
| 234 gtk_widget_set_sensitive( |
| 235 remove_all_button_, cookies_table_model_->RowCount() > 0); |
| 236 |
| 237 const gchar* filter_text = gtk_entry_get_text(GTK_ENTRY(filter_entry_)); |
| 238 gtk_widget_set_sensitive(filter_clear_button_, filter_text && *filter_text); |
| 239 } |
| 240 |
| 241 void CookiesView::RemoveSelectedCookies() { |
| 242 GList* list = gtk_tree_selection_get_selected_rows(selection_, NULL); |
| 243 std::vector<int> selected_rows( |
| 244 gtk_tree_selection_count_selected_rows(selection_)); |
| 245 GList* node; |
| 246 size_t i; |
| 247 for (i = 0, node = list; node != NULL; ++i, node = node->next) { |
| 248 selected_rows[i] = GetTreeSortChildRowNumForPath( |
| 249 list_sort_, static_cast<GtkTreePath*>(node->data)); |
| 250 } |
| 251 g_list_foreach(list, (GFunc)gtk_tree_path_free, NULL); |
| 252 g_list_free(list); |
| 253 |
| 254 for (std::vector<int>::reverse_iterator selected = selected_rows.rbegin(); |
| 255 selected != selected_rows.rend(); ++selected) { |
| 256 cookies_table_model_->RemoveCookies(*selected, 1); |
| 257 } |
| 258 } |
| 259 |
| 260 void CookiesView::SetColumnValues(int row, GtkTreeIter* iter) { |
| 261 SkBitmap bitmap = cookies_table_model_->GetIcon(row); |
| 262 GdkPixbuf* pixbuf = gfx::GdkPixbufFromSkBitmap(&bitmap); |
| 263 std::wstring site = cookies_table_model_->GetText( |
| 264 row, IDS_COOKIES_DOMAIN_COLUMN_HEADER); |
| 265 std::wstring name = cookies_table_model_->GetText( |
| 266 row, IDS_COOKIES_NAME_COLUMN_HEADER); |
| 267 gtk_list_store_set(list_store_, iter, |
| 268 COL_ICON, pixbuf, |
| 269 COL_SITE, WideToUTF8(site).c_str(), |
| 270 COL_COOKIE_NAME, WideToUTF8(name).c_str(), |
| 271 -1); |
| 272 g_object_unref(pixbuf); |
| 273 } |
| 274 |
| 275 void CookiesView::AddNodeToList(int row) { |
| 276 GtkTreeIter iter; |
| 277 if (row == 0) { |
| 278 gtk_list_store_prepend(list_store_, &iter); |
| 279 } else { |
| 280 GtkTreeIter sibling; |
| 281 gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(list_store_), &sibling, |
| 282 NULL, row - 1); |
| 283 gtk_list_store_insert_after(list_store_, &iter, &sibling); |
| 284 } |
| 285 |
| 286 SetColumnValues(row, &iter); |
| 287 } |
| 288 |
| 289 void CookiesView::OnModelChanged() { |
| 290 gtk_list_store_clear(list_store_); |
| 291 for (int i = 0; i < cookies_table_model_->RowCount(); ++i) |
| 292 AddNodeToList(i); |
| 293 } |
| 294 |
| 295 void CookiesView::OnItemsChanged(int start, int length) { |
| 296 GtkTreeIter iter; |
| 297 bool rv = gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(list_store_), |
| 298 &iter, NULL, start); |
| 299 for (int i = 0; i < length; ++i) { |
| 300 if (!rv) { |
| 301 NOTREACHED(); |
| 302 return; |
| 303 } |
| 304 SetColumnValues(start + i, &iter); |
| 305 rv = gtk_tree_model_iter_next(GTK_TREE_MODEL(list_store_), &iter); |
| 306 } |
| 307 } |
| 308 |
| 309 void CookiesView::OnItemsAdded(int start, int length) { |
| 310 NOTREACHED(); |
| 311 } |
| 312 |
| 313 void CookiesView::OnItemsRemoved(int start, int length) { |
| 314 for (int i = 0; i < length; ++i) { |
| 315 GtkTreeIter iter; |
| 316 if (!gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(list_store_), &iter, |
| 317 NULL, start)) { |
| 318 NOTREACHED(); |
| 319 return; |
| 320 } |
| 321 gtk_list_store_remove(list_store_, &iter); |
| 322 } |
| 323 } |
| 324 |
| 325 // static |
| 326 gint CookiesView::CompareSite(GtkTreeModel* model, GtkTreeIter* a, |
| 327 GtkTreeIter* b, gpointer window) { |
| 328 int row1 = GetRowNumForIter(model, a); |
| 329 int row2 = GetRowNumForIter(model, b); |
| 330 return reinterpret_cast<CookiesView*>(window)->cookies_table_model_-> |
| 331 CompareValues(row1, row2, IDS_COOKIES_DOMAIN_COLUMN_HEADER); |
| 332 } |
| 333 |
| 334 // static |
| 335 gint CookiesView::CompareCookieName(GtkTreeModel* model, GtkTreeIter* a, |
| 336 GtkTreeIter* b, gpointer window) { |
| 337 int row1 = GetRowNumForIter(model, a); |
| 338 int row2 = GetRowNumForIter(model, b); |
| 339 return reinterpret_cast<CookiesView*>(window)->cookies_table_model_-> |
| 340 CompareValues(row1, row2, IDS_COOKIES_NAME_COLUMN_HEADER); |
| 341 } |
| 342 |
| 343 // static |
| 344 void CookiesView::OnResponse(GtkDialog* dialog, int response_id, |
| 345 CookiesView* window) { |
| 346 if (response_id == RESPONSE_REMOVE) { |
| 347 window->RemoveSelectedCookies(); |
| 348 } else if (response_id == RESPONSE_REMOVE_ALL) { |
| 349 window->cookies_table_model_->RemoveAllShownCookies(); |
| 350 } else { |
| 351 gtk_widget_destroy(window->dialog_); |
| 352 } |
| 353 } |
| 354 |
| 355 // static |
| 356 void CookiesView::OnWindowDestroy(GtkWidget* widget, CookiesView* window) { |
| 357 instance_ = NULL; |
| 358 MessageLoop::current()->DeleteSoon(FROM_HERE, window); |
| 359 } |
| 360 |
| 361 // static |
| 362 void CookiesView::OnSelectionChanged(GtkTreeSelection *selection, |
| 363 CookiesView* window) { |
| 364 window->EnableControls(); |
| 365 } |
| 366 |
| 367 void CookiesView::UpdateFilterResults() { |
| 368 const gchar* text = gtk_entry_get_text(GTK_ENTRY(filter_entry_)); |
| 369 if (text) |
| 370 cookies_table_model_->UpdateSearchResults(UTF8ToWide(text)); |
| 371 } |
| 372 |
| 373 // static |
| 374 void CookiesView::OnFilterEntryActivated(GtkEntry* entry, CookiesView* window) { |
| 375 window->filter_update_factory_.RevokeAll(); |
| 376 window->UpdateFilterResults(); |
| 377 } |
| 378 |
| 379 // static |
| 380 void CookiesView::OnFilterEntryChanged(GtkEditable* editable, |
| 381 CookiesView* window) { |
| 382 window->filter_update_factory_.RevokeAll(); |
| 383 MessageLoop::current()->PostDelayedTask(FROM_HERE, |
| 384 window->filter_update_factory_.NewRunnableMethod( |
| 385 &CookiesView::UpdateFilterResults), kSearchFilterDelayMs); |
| 386 window->EnableControls(); |
| 387 } |
| 388 |
| 389 // static |
| 390 void CookiesView::OnFilterClearButtonClicked(GtkButton* button, |
| 391 CookiesView* window) { |
| 392 gtk_entry_set_text(GTK_ENTRY(window->filter_entry_), ""); |
| 393 window->filter_update_factory_.RevokeAll(); |
| 394 window->UpdateFilterResults(); |
| 395 } |
| OLD | NEW |