From: Enrico Tröger Date: Tue, 19 May 2009 17:04:25 +0000 (+0200) Subject: Split the CookieManager extension into separate files X-Git-Url: https://spindle.queued.net/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=e1268d3dea25fcd6265df3e6e705843925b19948;p=midori Split the CookieManager extension into separate files --- diff --git a/extensions/cookie-manager.c b/extensions/cookie-manager.c deleted file mode 100644 index e3b9783f..00000000 --- a/extensions/cookie-manager.c +++ /dev/null @@ -1,975 +0,0 @@ -/* - Copyright (C) 2009 Enrico Tröger - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - See the file COPYING for the full license text. -*/ - -#include "config.h" -#include -#include -#include -#include - -#define CM_DEBUG 0 - -#define STOCK_COOKIE_MANAGER "cookie-manager" -#define CM_EMPTY_LABEL_TEXT "\n\n\n\n\n\n" - -enum -{ - COL_NAME, - COL_COOKIE, - COL_VISIBLE, - N_COLUMNS -}; - -typedef struct _CMData -{ - MidoriApp *app; - MidoriBrowser *browser; - MidoriExtension *extension; - - GtkWidget *panel_page; - GtkWidget *desc_label; - GtkWidget *delete_button; - GtkWidget *delete_popup_button; - GtkWidget *delete_all_button; - GtkWidget *expand_buttons[4]; - - GtkWidget *treeview; - GtkTreeStore *store; - GtkTreeModel *filter; - - GtkWidget *filter_entry; - - GtkWidget *popup_menu; - - SoupCookieJar *jar; - GSList *cookies; - - guint timer_id; - gint ignore_changed_count; -} CMData; - -static void cm_app_add_browser_cb(MidoriApp *app, MidoriBrowser *browser, MidoriExtension *ext); -static void cm_filter_tree(CMData *cmdata, const gchar *filter_text); -static void cm_deactivate_cb(MidoriExtension *extension, CMData *cmdata); -static void cm_jar_changed_cb(SoupCookieJar *jar, SoupCookie *old, SoupCookie *new, CMData *cmdata); - - -#if CM_DEBUG -static gchar *cookie_to_string(SoupCookie *c) -{ - if (c != NULL) - { - static gchar s[256]; /* this might be too small but for debugging it should be ok */ - g_snprintf(s, sizeof(s), "%s\t%s = %s", c->domain, c->name, c->value); - return s; - } - return NULL; -} -#endif - - -static void cm_free_cookie_list(CMData *cmdata) -{ - if (cmdata->cookies != NULL) - { - GSList *l; - - for (l = cmdata->cookies; l != NULL; l = g_slist_next(l)) - soup_cookie_free(l->data); - g_slist_free(cmdata->cookies); - cmdata->cookies = NULL; - } -} - - -static void cm_browser_close_cb(GtkObject *browser, CMData *cmdata) -{ - g_signal_handlers_disconnect_by_func(cmdata->extension, cm_deactivate_cb, cmdata); - g_signal_handlers_disconnect_by_func(cmdata->browser, cm_browser_close_cb, cmdata); - g_signal_handlers_disconnect_by_func(cmdata->jar, cm_jar_changed_cb, cmdata); - if (cmdata->timer_id > 0) - g_source_remove(cmdata->timer_id); - - cm_free_cookie_list(cmdata); - - /* the panel_page widget gets destroyed automatically when a browser is closed but not - * when the extension is deactivated */ - if (cmdata->panel_page != NULL && GTK_IS_WIDGET(cmdata->panel_page)) - gtk_widget_destroy(cmdata->panel_page); - - gtk_widget_destroy(cmdata->popup_menu); - g_free(cmdata); -} - - -static void cm_deactivate_cb(MidoriExtension *extension, CMData *cmdata) -{ - g_signal_handlers_disconnect_by_func(cmdata->app, cm_app_add_browser_cb, extension); - cm_browser_close_cb(NULL, cmdata); -} - - -static void cm_refresh_store(CMData *cmdata) -{ - GSList *l; - GHashTable *parents; - GtkTreeIter iter; - GtkTreeIter *parent_iter; - SoupCookie *cookie; - const gchar *filter_text; - - g_object_ref(cmdata->filter); - gtk_tree_view_set_model(GTK_TREE_VIEW(cmdata->treeview), NULL); - - gtk_tree_store_clear(cmdata->store); - - /* free the old list */ - cm_free_cookie_list(cmdata); - - cmdata->cookies = soup_cookie_jar_all_cookies(cmdata->jar); - - /* Hashtable holds domain names as keys, the corresponding tree iters as values */ - parents = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); - - for (l = cmdata->cookies; l != NULL; l = g_slist_next(l)) - { - cookie = l->data; - - /* look for the parent item for the current domain name and create it if it doesn't exist */ - if ((parent_iter = (GtkTreeIter*) g_hash_table_lookup(parents, cookie->domain)) == NULL) - { - parent_iter = g_new0(GtkTreeIter, 1); - - gtk_tree_store_append(cmdata->store, parent_iter, NULL); - gtk_tree_store_set(cmdata->store, parent_iter, - COL_NAME, cookie->domain, - COL_COOKIE, NULL, - COL_VISIBLE, TRUE, - -1); - - g_hash_table_insert(parents, g_strdup(cookie->domain), parent_iter); - } - - gtk_tree_store_append(cmdata->store, &iter, parent_iter); - gtk_tree_store_set(cmdata->store, &iter, - COL_NAME, cookie->name, - COL_COOKIE, cookie, - COL_VISIBLE, TRUE, - -1); - } - g_hash_table_destroy(parents); - - gtk_tree_view_set_model(GTK_TREE_VIEW(cmdata->treeview), GTK_TREE_MODEL(cmdata->filter)); - g_object_unref(cmdata->filter); - - /* if a filter is set, apply it again */ - filter_text = gtk_entry_get_text(GTK_ENTRY(cmdata->filter_entry)); - if (*filter_text != '\0') - { - cm_filter_tree(cmdata, filter_text); - gtk_tree_view_expand_all(GTK_TREE_VIEW(cmdata->treeview)); - } -} - - -static gchar *cm_get_cookie_description_text(SoupCookie *cookie) -{ - static gchar date_fmt[512]; - gchar *expires; - gchar *text; - time_t expiration_time; - const struct tm *tm; - - g_return_val_if_fail(cookie != NULL, NULL); - - if (cookie->expires != NULL) - { - expiration_time = soup_date_to_time_t(cookie->expires); - tm = localtime(&expiration_time); - /* even if some gcc versions complain about "%c", there is nothing wrong with and here we - * want to use it */ - strftime(date_fmt, sizeof(date_fmt), "%c", tm); - expires = date_fmt; - } - else - expires = _("At the end of the session"); - - text = g_markup_printf_escaped( - _("Host: %s\nName: %s\nValue: %s\nPath: %s\n" - "Secure: %s\nExpires: %s"), - cookie->domain, - cookie->name, - cookie->value, - cookie->path, - cookie->secure ? _("Yes") : _("No"), - expires); - - return text; -} - - -static void cm_set_button_sensitiveness(CMData *cmdata, gboolean set) -{ - guint i, len; - gboolean expand_set = (gtk_tree_model_iter_n_children(cmdata->filter, NULL) > 0); - - gtk_widget_set_sensitive(cmdata->delete_popup_button, set); - gtk_widget_set_sensitive(cmdata->delete_button, set); - - gtk_widget_set_sensitive(cmdata->delete_all_button, expand_set); - len = G_N_ELEMENTS(cmdata->expand_buttons); - for (i = 0; i < len; i++) - { - gtk_widget_set_sensitive(cmdata->expand_buttons[i], expand_set); - } -} - - -static void cm_tree_selection_changed_cb(GtkTreeSelection *selection, CMData *cmdata) -{ - GtkTreeIter iter, iter_store; - GtkTreeModel *model; - gchar *text; - gboolean valid = TRUE; - gboolean delete_possible = FALSE; - SoupCookie *cookie; - - if (! gtk_tree_selection_get_selected(selection, &model, &iter)) - valid = FALSE; - else - gtk_tree_model_filter_convert_iter_to_child_iter(GTK_TREE_MODEL_FILTER(model), - &iter_store, &iter); - - if (valid && gtk_tree_store_iter_is_valid(cmdata->store, &iter_store)) - { - delete_possible = TRUE; - - gtk_tree_model_get(model, &iter, COL_COOKIE, &cookie, -1); - if (cookie != NULL) - { - text = cm_get_cookie_description_text(cookie); - - gtk_label_set_markup(GTK_LABEL(cmdata->desc_label), text); - - g_free(text); - } - else - valid = FALSE; - } - /* This is a bit hack'ish but we add some empty lines to get a minimum height of the - * label at the bottom without any font size calculation. */ - if (! valid) - gtk_label_set_text(GTK_LABEL(cmdata->desc_label), CM_EMPTY_LABEL_TEXT); - cm_set_button_sensitiveness(cmdata, delete_possible); -} - - -static gboolean cm_tree_button_press_event_cb(GtkWidget *widget, GdkEventButton *ev, CMData *cmdata) -{ - if (ev->type == GDK_2BUTTON_PRESS) - { - GtkTreeSelection *selection; - GtkTreeModel *model; - GtkTreeIter iter; - - selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget)); - - if (gtk_tree_selection_get_selected(selection, &model, &iter)) - { - /* double click on parent node expands/collapses it */ - if (gtk_tree_model_iter_has_child(model, &iter)) - { - GtkTreePath *path = gtk_tree_model_get_path(model, &iter); - - if (gtk_tree_view_row_expanded(GTK_TREE_VIEW(widget), path)) - gtk_tree_view_collapse_row(GTK_TREE_VIEW(widget), path); - else - gtk_tree_view_expand_row(GTK_TREE_VIEW(widget), path, FALSE); - - gtk_tree_path_free(path); - - return TRUE; - } - } - } - - return FALSE; -} - - -static void cm_tree_show_popup_menu(GtkWidget *widget, GdkEventButton *event, CMData *cmdata) -{ - gint button, event_time; - - if (event != NULL) - { - button = event->button; - event_time = event->time; - } - else - { - button = 0; - event_time = gtk_get_current_event_time (); - } - - gtk_menu_popup(GTK_MENU(cmdata->popup_menu), NULL, NULL, NULL, NULL, button, event_time); -} - - -static gboolean cm_tree_popup_menu_cb(GtkWidget *widget, CMData *cmdata) -{ - cm_tree_show_popup_menu(widget, NULL, cmdata); - return TRUE; -} - - -static gboolean cm_tree_button_release_event_cb(GtkWidget *widget, GdkEventButton *ev, CMData *cmdata) -{ - if (ev->button == 3) - { - cm_tree_show_popup_menu(widget, ev, cmdata); - return TRUE; - } - return FALSE; -} - - -static void cm_tree_popup_collapse_activate_cb(GtkCheckMenuItem *item, CMData *cmdata) -{ - gtk_tree_view_collapse_all(GTK_TREE_VIEW(cmdata->treeview)); -} - - -static void cm_tree_popup_expand_activate_cb(GtkCheckMenuItem *item, CMData *cmdata) -{ - gtk_tree_view_expand_all(GTK_TREE_VIEW(cmdata->treeview)); -} - - -static void cm_store_remove(CMData *cmdata, GtkTreeIter *iter_model) -{ - GtkTreeIter iter_store; - - gtk_tree_model_filter_convert_iter_to_child_iter( - GTK_TREE_MODEL_FILTER(cmdata->filter), &iter_store, iter_model); - gtk_tree_store_remove(cmdata->store, &iter_store); -} - - -static void cm_delete_cookie(GtkTreeModel *model, GtkTreeIter *child, CMData *cmdata) -{ - SoupCookie *cookie; - - gtk_tree_model_get(model, child, COL_COOKIE, &cookie, -1); - - if (cookie != NULL) - { - cmdata->ignore_changed_count++; - - soup_cookie_jar_delete_cookie(cmdata->jar, cookie); - /* the SoupCookie object is freed when the whole list gets updated */ - } -} - - -static void cm_button_delete_clicked_cb(GtkToolButton *button, CMData *cmdata) -{ - GtkTreeIter iter, iter_store, child; - GtkTreeModel *model; - GtkTreeSelection *selection; - - selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(cmdata->treeview)); - if (! gtk_tree_selection_get_selected(selection, &model, &iter)) - return; - - if (gtk_tree_model_iter_has_child(model, &iter)) - { - GtkTreePath *path = gtk_tree_model_get_path(model, &iter); - - while (gtk_tree_model_iter_children(model, &child, &iter)) - { - cm_delete_cookie(model, &child, cmdata); - cm_store_remove(cmdata, &child); - /* we retrieve again the iter at path because it got invalid by the delete operation */ - gtk_tree_model_get_iter(model, &iter, path); - } - gtk_tree_path_free(path); - /* remove/hide the parent */ - gtk_tree_model_filter_convert_iter_to_child_iter(GTK_TREE_MODEL_FILTER(cmdata->filter), - &iter_store, &iter); - if (gtk_tree_model_iter_has_child(GTK_TREE_MODEL(cmdata->store), &iter_store)) - gtk_tree_store_set(cmdata->store, &iter_store, COL_VISIBLE, FALSE, -1); - else - cm_store_remove(cmdata, &iter); - } - else - { - GtkTreePath *path_store, *path_model; - - gtk_tree_model_filter_convert_iter_to_child_iter(GTK_TREE_MODEL_FILTER(cmdata->filter), - &iter_store, &iter); - path_store = gtk_tree_model_get_path(GTK_TREE_MODEL(cmdata->store), &iter_store); - path_model = gtk_tree_model_get_path(model, &iter); - - cm_delete_cookie(model, &iter, cmdata); - gtk_tree_store_remove(cmdata->store, &iter_store); - - /* check whether the parent still has children, otherwise delete it */ - if (gtk_tree_path_up(path_store)) - { - gtk_tree_model_get_iter(GTK_TREE_MODEL(cmdata->store), &iter_store, path_store); - if (! gtk_tree_model_iter_has_child(GTK_TREE_MODEL(cmdata->store), &iter_store)) - /* remove the empty parent */ - gtk_tree_store_remove(cmdata->store, &iter_store); - } - /* now for the filter model */ - if (gtk_tree_path_up(path_model)) - { - gtk_tree_model_get_iter(model, &iter, path_model); - if (! gtk_tree_model_iter_has_child(model, &iter)) - { - gtk_tree_model_filter_convert_iter_to_child_iter( - GTK_TREE_MODEL_FILTER(cmdata->filter), &iter_store, &iter); - /* hide the empty parent */ - gtk_tree_store_set(cmdata->store, &iter_store, COL_VISIBLE, FALSE, -1); - } - } - gtk_tree_path_free(path_store); - gtk_tree_path_free(path_model); - } -} - - -static void cm_delete_all_cookies_real(CMData *cmdata) -{ - GtkTreeIter iter, iter_store, child; - GtkTreePath *path_first, *path; - GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(cmdata->treeview)); - - path_first = gtk_tree_path_new_first(); - while (gtk_tree_model_get_iter(model, &iter, path_first)) - { - path = gtk_tree_model_get_path(model, &iter); - while (gtk_tree_model_iter_children(model, &child, &iter)) - { - cm_delete_cookie(model, &child, cmdata); - cm_store_remove(cmdata, &child); - /* we retrieve again the iter at path because it got invalid by the delete operation */ - gtk_tree_model_get_iter(model, &iter, path); - } - gtk_tree_path_free(path); - /* remove/hide the parent */ - gtk_tree_model_filter_convert_iter_to_child_iter(GTK_TREE_MODEL_FILTER(cmdata->filter), - &iter_store, &iter); - if (gtk_tree_model_iter_has_child(GTK_TREE_MODEL(cmdata->store), &iter_store)) - gtk_tree_store_set(cmdata->store, &iter_store, COL_VISIBLE, FALSE, -1); - else - cm_store_remove(cmdata, &iter); - } - gtk_tree_path_free(path_first); - - /* now that we deleted all matching cookies, we reset the filter */ - gtk_entry_set_text(GTK_ENTRY(cmdata->filter_entry), ""); - cm_set_button_sensitiveness(cmdata, FALSE); -} - - -static void cm_button_delete_all_clicked_cb(GtkToolButton *button, CMData *cmdata) -{ - GtkWidget *dialog; - MidoriBrowser *toplevel = midori_browser_get_for_widget(GTK_WIDGET(button)); - const gchar *filter_text; - - dialog = gtk_message_dialog_new(GTK_WINDOW(toplevel), - GTK_DIALOG_DESTROY_WITH_PARENT, - GTK_MESSAGE_QUESTION, - GTK_BUTTONS_YES_NO, - _("Do you really want to delete all cookies?")); - - gtk_window_set_title(GTK_WINDOW(dialog), _("Question")); - /* steal Midori's icon :) */ - if (toplevel != NULL) - gtk_window_set_icon_name(GTK_WINDOW(dialog), gtk_window_get_icon_name(GTK_WINDOW(toplevel))); - - filter_text = gtk_entry_get_text(GTK_ENTRY(cmdata->filter_entry)); - if (*filter_text != '\0') - { - gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog), - _("Only cookies which match the filter will be deleted.")); - } - - if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_YES) - cm_delete_all_cookies_real(cmdata); - - gtk_widget_destroy(dialog); -} - - -static void cm_tree_drag_data_get_cb(GtkWidget *widget, GdkDragContext *drag_context, - GtkSelectionData *data, guint info, guint ltime, CMData *cmdata) -{ - GtkTreeIter iter, iter_store; - GtkTreeSelection *selection; - GtkTreeModel *model; - - selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(cmdata->treeview)); - if (! gtk_tree_selection_get_selected(selection, &model, &iter)) - return; - - gtk_tree_model_filter_convert_iter_to_child_iter( - GTK_TREE_MODEL_FILTER(model), &iter_store, &iter); - - if (gtk_tree_store_iter_is_valid(cmdata->store, &iter_store)) - { - SoupCookie *cookie; - gchar *name, *text; - - gtk_tree_model_get(model, &iter, COL_NAME, &name, COL_COOKIE, &cookie, -1); - - if (cookie == NULL && name != NULL) - { - /* skip a leading dot */ - text = (*name == '.') ? name + 1 : name; - - gtk_selection_data_set_text(data, text, -1); - } - g_free(name); - } -} - - -#if GTK_CHECK_VERSION(2, 12, 0) -static gboolean cm_tree_query_tooltip(GtkWidget *widget, gint x, gint y, gboolean keyboard_mode, - GtkTooltip *tooltip, CMData *cmdata) -{ - GtkTreeIter iter; - GtkTreeModel *model; - - if (gtk_tree_view_get_tooltip_context(GTK_TREE_VIEW(widget), &x, &y, - keyboard_mode, &model, NULL, &iter)) - { - gchar *tooltip_text; - SoupCookie *cookie; - - gtk_tree_model_get(model, &iter, COL_COOKIE, &cookie, -1); - - if (cookie == NULL) /* not an item */ - return FALSE; - - tooltip_text = cm_get_cookie_description_text(cookie); - - gtk_tooltip_set_markup(tooltip, tooltip_text); - - g_free(tooltip_text); - - return TRUE; - } - - return FALSE; -} -#endif - - -static gboolean cm_filter_match(const gchar *haystack, const gchar *needle) -{ - gchar *haystack_lowered, *needle_lowered; - gboolean result; - - /* empty strings always match */ - if (haystack == NULL || needle == NULL || *needle == '\0') - return TRUE; - - haystack_lowered = g_utf8_strdown(haystack, -1); - needle_lowered = g_utf8_strdown(needle, -1); - - /* if one of both could not be converted into lower case, skip those */ - if (haystack_lowered == NULL || needle_lowered == NULL) - return FALSE; - - result = (strstr(haystack_lowered, needle_lowered) != NULL); - - g_free(haystack_lowered); - g_free(needle_lowered); - - return result; -} - - -static void cm_filter_tree(CMData *cmdata, const gchar *filter_text) -{ - GtkTreeIter iter, child; - GtkTreeModel *model; - gboolean show_child, show_parent; - gboolean child_visible; - gint i, n; - gchar *name; - gchar *domain; - - model = GTK_TREE_MODEL(cmdata->store); - if (! gtk_tree_model_get_iter_first(model, &iter)) - return; - - do - { - if (gtk_tree_model_iter_has_child(model, &iter)) - { - child_visible = FALSE; - - gtk_tree_model_get(model, &iter, COL_NAME, &domain, -1); - show_parent = cm_filter_match(domain, filter_text); - g_free(domain); - n = gtk_tree_model_iter_n_children(model, &iter); - for (i = 0; i < n; i++) - { - gtk_tree_model_iter_nth_child(model, &child, &iter, i); - - gtk_tree_model_get(model, &child, COL_NAME, &name, -1); - show_child = show_parent || cm_filter_match(name, filter_text); - g_free(name); - - if (show_child) - child_visible = TRUE; - - gtk_tree_store_set(cmdata->store, &child, COL_VISIBLE, show_child, -1); - } - gtk_tree_store_set(cmdata->store, &iter, COL_VISIBLE, child_visible, -1); - } - } - while (gtk_tree_model_iter_next(model, &iter)); -} - - -static void cm_filter_entry_changed_cb(GtkEditable *editable, CMData *cmdata) -{ - const gchar *text = gtk_entry_get_text(GTK_ENTRY(editable)); - - cm_filter_tree(cmdata, text); - - if (*text != '\0') - gtk_tree_view_expand_all(GTK_TREE_VIEW(cmdata->treeview)); - else - gtk_tree_view_collapse_all(GTK_TREE_VIEW(cmdata->treeview)); -} - - -static void cm_filter_entry_clear_icon_released_cb(GtkIconEntry *e, gint pos, gint btn, CMData *cmdata) -{ - if (pos == GTK_ICON_ENTRY_SECONDARY) - gtk_entry_set_text(GTK_ENTRY(e), ""); -} - - -static void cm_tree_prepare(CMData *cmdata) -{ - GtkCellRenderer *renderer; - GtkTreeViewColumn *column; - GtkTreeSelection *sel; - GtkWidget *tree; - GtkWidget *item; - GtkWidget *menu; - - cmdata->treeview = tree = gtk_tree_view_new(); - cmdata->store = gtk_tree_store_new(N_COLUMNS, G_TYPE_STRING, SOUP_TYPE_COOKIE, G_TYPE_BOOLEAN); - - renderer = gtk_cell_renderer_text_new(); - column = gtk_tree_view_column_new_with_attributes( - _("Name"), renderer, "text", COL_NAME, NULL); - gtk_tree_view_column_set_sort_indicator(column, TRUE); - gtk_tree_view_column_set_sort_column_id(column, COL_NAME); - gtk_tree_view_column_set_resizable(GTK_TREE_VIEW_COLUMN(column), TRUE); - gtk_tree_view_append_column(GTK_TREE_VIEW(tree), column); - - gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(tree), TRUE); - gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(tree), FALSE); - gtk_tree_view_set_search_column(GTK_TREE_VIEW(tree), COL_NAME); - gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(cmdata->store), COL_NAME, GTK_SORT_ASCENDING); - - /* selection handling */ - sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree)); - gtk_tree_selection_set_mode(sel, GTK_SELECTION_SINGLE); - - /* setting filter and model */ - cmdata->filter = gtk_tree_model_filter_new(GTK_TREE_MODEL(cmdata->store), NULL); - gtk_tree_model_filter_set_visible_column(GTK_TREE_MODEL_FILTER(cmdata->filter), COL_VISIBLE); - - gtk_tree_view_set_model(GTK_TREE_VIEW(tree), GTK_TREE_MODEL(cmdata->filter)); - g_object_unref(cmdata->store); - g_object_unref(cmdata->filter); - - /* signals */ - g_signal_connect(sel, "changed", G_CALLBACK(cm_tree_selection_changed_cb), cmdata); - g_signal_connect(tree, "button-press-event", G_CALLBACK(cm_tree_button_press_event_cb), cmdata); - g_signal_connect(tree, "button-release-event", G_CALLBACK(cm_tree_button_release_event_cb), cmdata); - g_signal_connect(tree, "popup-menu", G_CALLBACK(cm_tree_popup_menu_cb), cmdata); - - /* tooltips */ -#if GTK_CHECK_VERSION(2, 12, 0) - gtk_widget_set_has_tooltip(tree, TRUE); - g_signal_connect(tree, "query-tooltip", G_CALLBACK(cm_tree_query_tooltip), cmdata); -#endif - - /* drag'n'drop */ - gtk_tree_view_enable_model_drag_source( - GTK_TREE_VIEW(tree), - GDK_BUTTON1_MASK, - NULL, - 0, - GDK_ACTION_COPY - ); - gtk_drag_source_add_text_targets(tree); - /*gtk_drag_source_add_uri_targets(tree);*/ - g_signal_connect(tree, "drag-data-get", G_CALLBACK(cm_tree_drag_data_get_cb), cmdata); - - /* popup menu */ - menu = gtk_menu_new(); - - item = gtk_image_menu_item_new_from_stock(GTK_STOCK_DELETE, NULL); - gtk_widget_show(item); - gtk_container_add(GTK_CONTAINER(menu), item); - g_signal_connect(item, "activate", G_CALLBACK(cm_button_delete_clicked_cb), cmdata); - cmdata->delete_popup_button = item; - - item = gtk_separator_menu_item_new(); - gtk_widget_show(item); - gtk_container_add(GTK_CONTAINER(menu), item); - - item = gtk_image_menu_item_new_with_mnemonic(_("_Expand All")); - gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), - gtk_image_new_from_stock(GTK_STOCK_ADD, GTK_ICON_SIZE_MENU)); - gtk_widget_show(item); - gtk_container_add(GTK_CONTAINER(menu), item); - g_signal_connect(item, "activate", G_CALLBACK(cm_tree_popup_expand_activate_cb), cmdata); - cmdata->expand_buttons[2] = item; - - item = gtk_image_menu_item_new_with_mnemonic(_("_Collapse All")); - gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), - gtk_image_new_from_icon_name(GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU)); - gtk_widget_show(item); - gtk_container_add(GTK_CONTAINER(menu), item); - g_signal_connect(item, "activate", G_CALLBACK(cm_tree_popup_collapse_activate_cb), cmdata); - cmdata->expand_buttons[3] = item; - - cmdata->popup_menu = menu; -} - - -static gboolean cm_delayed_refresh(CMData *cmdata) -{ - cm_refresh_store(cmdata); - cmdata->timer_id = 0; - - return FALSE; -} - - -static void cm_jar_changed_cb(SoupCookieJar *jar, SoupCookie *old, SoupCookie *new, CMData *cmdata) -{ - if (cmdata->ignore_changed_count > 0) - { - cmdata->ignore_changed_count--; - return; - } - - /* We delay these events a little bit to avoid too many rebuilds of the tree. - * Some websites (like Flyspray bugtrackers sent a whole bunch of cookies at once. */ - if (cmdata->timer_id == 0) - { - cmdata->timer_id = g_timeout_add_seconds(1, (GSourceFunc) cm_delayed_refresh, cmdata); - } -} - - -static void cm_app_add_browser_cb(MidoriApp *app, MidoriBrowser *browser, MidoriExtension *ext) -{ - GtkWidget *panel; - GtkWidget *tree_swin; - GtkWidget *desc_swin; - GtkWidget *toolbar; - GtkWidget *paned; - GtkWidget *filter_hbox; - GtkWidget *filter_label; - GtkToolItem *toolitem; - SoupSession *session; - CMData *cmdata; - - cmdata = g_new0(CMData, 1); - cmdata->app = app; - cmdata->extension = ext; - cmdata->browser = browser; - - panel = katze_object_get_object(browser, "panel"); - toolbar = gtk_toolbar_new(); - gtk_toolbar_set_style(GTK_TOOLBAR(toolbar), GTK_TOOLBAR_BOTH_HORIZ); - gtk_toolbar_set_icon_size(GTK_TOOLBAR(toolbar), GTK_ICON_SIZE_BUTTON); - gtk_widget_show(toolbar); - - toolitem = gtk_tool_button_new_from_stock(GTK_STOCK_DELETE); - gtk_tool_item_set_is_important(toolitem, TRUE); - g_signal_connect(toolitem, "clicked", G_CALLBACK(cm_button_delete_clicked_cb), cmdata); - gtk_widget_show(GTK_WIDGET(toolitem)); - gtk_widget_set_sensitive(GTK_WIDGET(toolitem), FALSE); - gtk_toolbar_insert(GTK_TOOLBAR(toolbar), toolitem, -1); - cmdata->delete_button = GTK_WIDGET(toolitem); - - toolitem = gtk_tool_button_new_from_stock(GTK_STOCK_DELETE); - gtk_tool_button_set_label(GTK_TOOL_BUTTON(toolitem), _("Delete All")); - gtk_tool_item_set_tooltip_text(toolitem, - _("Deletes all shown cookies. " - "If a filter is set, only those cookies are deleted which match the filter.")); - gtk_tool_item_set_is_important(toolitem, TRUE); - g_signal_connect(toolitem, "clicked", G_CALLBACK(cm_button_delete_all_clicked_cb), cmdata); - gtk_widget_show(GTK_WIDGET(toolitem)); - gtk_toolbar_insert(GTK_TOOLBAR(toolbar), toolitem, -1); - cmdata->delete_all_button = GTK_WIDGET(toolitem); - - toolitem = gtk_separator_tool_item_new(); - gtk_separator_tool_item_set_draw(GTK_SEPARATOR_TOOL_ITEM(toolitem), FALSE); - gtk_tool_item_set_expand(toolitem, TRUE); - gtk_toolbar_insert(GTK_TOOLBAR(toolbar), toolitem, -1); - gtk_widget_show(GTK_WIDGET(toolitem)); - - toolitem = gtk_tool_button_new_from_stock(GTK_STOCK_ADD); - gtk_tool_item_set_tooltip_text(toolitem, _("Expand All")); - g_signal_connect(toolitem, "clicked", G_CALLBACK(cm_tree_popup_expand_activate_cb), cmdata); - gtk_widget_show(GTK_WIDGET(toolitem)); - gtk_toolbar_insert(GTK_TOOLBAR(toolbar), toolitem, -1); - cmdata->expand_buttons[0] = GTK_WIDGET(toolitem); - - toolitem = gtk_tool_button_new_from_stock(GTK_STOCK_REMOVE); - gtk_tool_item_set_tooltip_text(toolitem, _("Collapse All")); - g_signal_connect(toolitem, "clicked", G_CALLBACK(cm_tree_popup_collapse_activate_cb), cmdata); - gtk_widget_show(GTK_WIDGET(toolitem)); - gtk_toolbar_insert(GTK_TOOLBAR(toolbar), toolitem, -1); - cmdata->expand_buttons[1] = GTK_WIDGET(toolitem); - - cmdata->desc_label = gtk_label_new(CM_EMPTY_LABEL_TEXT); - gtk_label_set_selectable(GTK_LABEL(cmdata->desc_label), TRUE); - gtk_label_set_line_wrap(GTK_LABEL(cmdata->desc_label), TRUE); - gtk_label_set_line_wrap_mode(GTK_LABEL(cmdata->desc_label), PANGO_WRAP_CHAR); - gtk_misc_set_alignment(GTK_MISC(cmdata->desc_label), 0, 0); - gtk_misc_set_padding(GTK_MISC(cmdata->desc_label), 3, 3); - gtk_widget_show(cmdata->desc_label); - - desc_swin = gtk_scrolled_window_new(NULL, NULL); - gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(desc_swin), - GTK_POLICY_AUTOMATIC, GTK_POLICY_NEVER); - gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(desc_swin), GTK_SHADOW_NONE); - gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(desc_swin), cmdata->desc_label); - gtk_widget_show(desc_swin); - - cm_tree_prepare(cmdata); - gtk_widget_show(cmdata->treeview); - - tree_swin = gtk_scrolled_window_new(NULL, NULL); - gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(tree_swin), - GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); - gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(tree_swin), GTK_SHADOW_IN); - gtk_container_add(GTK_CONTAINER(tree_swin), cmdata->treeview); - gtk_widget_show(tree_swin); - - filter_label = gtk_label_new(_("Filter:")); - gtk_widget_show(filter_label); - - cmdata->filter_entry = gtk_icon_entry_new(); - gtk_widget_set_tooltip_text(cmdata->filter_entry, - _("Enter a filter string to show only cookies whose name or domain " - "field match the entered filter")); - gtk_widget_show(cmdata->filter_entry); - gtk_icon_entry_set_icon_from_stock(GTK_ICON_ENTRY(cmdata->filter_entry), - GTK_ICON_ENTRY_SECONDARY, GTK_STOCK_CLEAR); - gtk_icon_entry_set_icon_highlight(GTK_ICON_ENTRY (cmdata->filter_entry), - GTK_ICON_ENTRY_SECONDARY, TRUE); - g_signal_connect(cmdata->filter_entry, "icon-release", - G_CALLBACK(cm_filter_entry_clear_icon_released_cb), NULL); - g_signal_connect(cmdata->filter_entry, "changed", G_CALLBACK(cm_filter_entry_changed_cb), cmdata); - g_signal_connect(cmdata->filter_entry, "activate", G_CALLBACK(cm_filter_entry_changed_cb), cmdata); - - filter_hbox = gtk_hbox_new(FALSE, 0); - gtk_box_pack_start(GTK_BOX(filter_hbox), filter_label, FALSE, FALSE, 3); - gtk_box_pack_start(GTK_BOX(filter_hbox), cmdata->filter_entry, TRUE, TRUE, 3); - gtk_widget_show(filter_hbox); - - paned = gtk_vpaned_new(); - gtk_paned_pack1(GTK_PANED(paned), tree_swin, TRUE, FALSE); - gtk_paned_pack2(GTK_PANED(paned), desc_swin, FALSE, FALSE); - gtk_widget_show(paned); - - cmdata->panel_page = gtk_vbox_new(FALSE, 0); - gtk_box_pack_start(GTK_BOX(cmdata->panel_page), filter_hbox, FALSE, FALSE, 5); - gtk_box_pack_start(GTK_BOX(cmdata->panel_page), paned, TRUE, TRUE, 0); - gtk_widget_show(cmdata->panel_page); - - /* setup soup */ - session = webkit_get_default_session(); - cmdata->jar = SOUP_COOKIE_JAR(soup_session_get_feature(session, soup_cookie_jar_get_type())); - g_signal_connect(cmdata->jar, "changed", G_CALLBACK(cm_jar_changed_cb), cmdata); - - cm_refresh_store(cmdata); - - midori_panel_append_widget(MIDORI_PANEL(panel), cmdata->panel_page, - STOCK_COOKIE_MANAGER, _("Cookie Manager"), toolbar); - - g_signal_connect(ext, "deactivate", G_CALLBACK(cm_deactivate_cb), cmdata); - g_signal_connect(browser, "destroy", G_CALLBACK(cm_browser_close_cb), cmdata); - - g_object_unref(panel); -} - - -static void cm_activate_cb(MidoriExtension *extension, MidoriApp *app, gpointer data) -{ - KatzeArray* browsers; - MidoriBrowser* browser; - guint i; - - browsers = katze_object_get_object(app, "browsers"); - i = 0; - while ((browser = katze_array_get_nth_item(browsers, i++))) - cm_app_add_browser_cb(app, browser, extension); - g_object_unref(browsers); - g_signal_connect(app, "add-browser", G_CALLBACK(cm_app_add_browser_cb), extension); -} - - -MidoriExtension *extension_init(void) -{ - MidoriExtension *extension; - GtkIconFactory *factory; - GtkIconSource *icon_source; - GtkIconSet *icon_set; - static GtkStockItem items[] = - { - { STOCK_COOKIE_MANAGER, N_("_Cookie Manager"), 0, 0, NULL } - }; - - factory = gtk_icon_factory_new(); - gtk_stock_add(items, G_N_ELEMENTS(items)); - icon_set = gtk_icon_set_new(); - icon_source = gtk_icon_source_new(); - gtk_icon_source_set_icon_name(icon_source, GTK_STOCK_DIALOG_AUTHENTICATION); - gtk_icon_set_add_source(icon_set, icon_source); - gtk_icon_source_free(icon_source); - gtk_icon_factory_add(factory, STOCK_COOKIE_MANAGER, icon_set); - gtk_icon_set_unref(icon_set); - gtk_icon_factory_add_default(factory); - g_object_unref(factory); - - extension = g_object_new(MIDORI_TYPE_EXTENSION, - "name", _("Cookie Manager"), - "description", _("List, view and delete cookies"), - "version", "0.1", - "authors", "Enrico Tröger ", - NULL); - - g_signal_connect(extension, "activate", G_CALLBACK(cm_activate_cb), NULL); - - return extension; -} diff --git a/extensions/cookie-manager/cookie-manager-page.c b/extensions/cookie-manager/cookie-manager-page.c new file mode 100644 index 00000000..9e73fe38 --- /dev/null +++ b/extensions/cookie-manager/cookie-manager-page.c @@ -0,0 +1,985 @@ +/* + Copyright (C) 2009 Enrico Tröger + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + See the file COPYING for the full license text. +*/ + +#include + +#include +#include +#include +#include + +#include "cookie-manager.h" +#include "cookie-manager-page.h" + + +typedef struct _CookieManagerPagePrivate CookieManagerPagePrivate; + +#define COOKIE_MANAGER_PAGE_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj),\ + COOKIE_MANAGER_PAGE_TYPE, CookieManagerPagePrivate)) + + +#define CM_EMPTY_LABEL_TEXT "\n\n\n\n\n\n" + +enum +{ + COOKIE_MANAGER_COL_NAME, + COOKIE_MANAGER_COL_COOKIE, + COOKIE_MANAGER_COL_VISIBLE, + COOKIE_MANAGER_N_COLUMNS +}; + + +struct _CookieManagerPagePrivate +{ + GtkWidget *treeview; + GtkTreeStore *store; + GtkTreeModel *filter; + + GtkWidget *filter_entry; + + GtkWidget *desc_label; + GtkWidget *delete_button; + GtkWidget *delete_popup_button; + GtkWidget *delete_all_button; + GtkWidget *expand_buttons[4]; + + GtkWidget *toolbar; + GtkWidget *popup_menu; + + GSList *cookies; + SoupCookieJar *jar; + guint timer_id; + gint ignore_changed_count; +}; + + +static void cookie_manager_page_finalize(GObject *object); +static void cookie_manager_page_viewable_iface_init(MidoriViewableIface *iface); + +static void cm_button_delete_clicked_cb(GtkToolButton *button, CookieManagerPage *cmp); +static void cm_button_delete_all_clicked_cb(GtkToolButton *button, CookieManagerPage *cmp); +static void cm_tree_popup_collapse_activate_cb(GtkMenuItem *item, CookieManagerPage *cmp); +static void cm_tree_popup_expand_activate_cb(GtkMenuItem *item, CookieManagerPage *cmp); +static void cm_filter_tree(CookieManagerPage *cmp, const gchar *filter_text); +static void cm_jar_changed_cb(SoupCookieJar *jar, SoupCookie *old, SoupCookie *new, CookieManagerPage *cmp); + + +G_DEFINE_TYPE_WITH_CODE(CookieManagerPage, cookie_manager_page, GTK_TYPE_VBOX, + G_IMPLEMENT_INTERFACE(MIDORI_TYPE_VIEWABLE, + cookie_manager_page_viewable_iface_init)); + + + +static const gchar *cookie_manager_page_get_label(MidoriViewable *viewable) +{ + return _("Cookie Manager"); +} + + +static const gchar *cookie_manager_page_get_stock_id(MidoriViewable *viewable) +{ + return STOCK_COOKIE_MANAGER; +} + + +static void cm_free_cookie_list(CookieManagerPage *cmp) +{ + CookieManagerPagePrivate *priv = COOKIE_MANAGER_PAGE_GET_PRIVATE(cmp); + + if (priv->cookies != NULL) + { + GSList *l; + + for (l = priv->cookies; l != NULL; l = g_slist_next(l)) + soup_cookie_free(l->data); + g_slist_free(priv->cookies); + priv->cookies = NULL; + } +} + + +static void cm_create_toolbar(CookieManagerPage *cmp) +{ + CookieManagerPagePrivate *priv = COOKIE_MANAGER_PAGE_GET_PRIVATE(cmp); + GtkWidget *toolbar; + GtkToolItem *toolitem; + + priv->toolbar = toolbar = gtk_toolbar_new(); + gtk_toolbar_set_style(GTK_TOOLBAR(toolbar), GTK_TOOLBAR_BOTH_HORIZ); + gtk_toolbar_set_icon_size(GTK_TOOLBAR(toolbar), GTK_ICON_SIZE_BUTTON); + gtk_widget_show(toolbar); + + toolitem = gtk_tool_button_new_from_stock(GTK_STOCK_DELETE); + gtk_tool_item_set_is_important(toolitem, TRUE); + g_signal_connect(toolitem, "clicked", G_CALLBACK(cm_button_delete_clicked_cb), cmp); + gtk_widget_show(GTK_WIDGET(toolitem)); + gtk_widget_set_sensitive(GTK_WIDGET(toolitem), FALSE); + gtk_toolbar_insert(GTK_TOOLBAR(toolbar), toolitem, -1); + priv->delete_button = GTK_WIDGET(toolitem); + + toolitem = gtk_tool_button_new_from_stock(GTK_STOCK_DELETE); + gtk_tool_button_set_label(GTK_TOOL_BUTTON(toolitem), _("Delete All")); + gtk_tool_item_set_tooltip_text(toolitem, + _("Deletes all shown cookies. " + "If a filter is set, only those cookies are deleted which match the filter.")); + gtk_tool_item_set_is_important(toolitem, TRUE); + g_signal_connect(toolitem, "clicked", G_CALLBACK(cm_button_delete_all_clicked_cb), cmp); + gtk_widget_show(GTK_WIDGET(toolitem)); + gtk_toolbar_insert(GTK_TOOLBAR(toolbar), toolitem, -1); + priv->delete_all_button = GTK_WIDGET(toolitem); + + toolitem = gtk_separator_tool_item_new(); + gtk_separator_tool_item_set_draw(GTK_SEPARATOR_TOOL_ITEM(toolitem), FALSE); + gtk_tool_item_set_expand(toolitem, TRUE); + gtk_toolbar_insert(GTK_TOOLBAR(toolbar), toolitem, -1); + gtk_widget_show(GTK_WIDGET(toolitem)); + + toolitem = gtk_tool_button_new_from_stock(GTK_STOCK_ADD); + gtk_tool_item_set_tooltip_text(toolitem, _("Expand All")); + g_signal_connect(toolitem, "clicked", G_CALLBACK(cm_tree_popup_expand_activate_cb), cmp); + gtk_widget_show(GTK_WIDGET(toolitem)); + gtk_toolbar_insert(GTK_TOOLBAR(toolbar), toolitem, -1); + priv->expand_buttons[0] = GTK_WIDGET(toolitem); + + toolitem = gtk_tool_button_new_from_stock(GTK_STOCK_REMOVE); + gtk_tool_item_set_tooltip_text(toolitem, _("Collapse All")); + g_signal_connect(toolitem, "clicked", G_CALLBACK(cm_tree_popup_collapse_activate_cb), cmp); + gtk_widget_show(GTK_WIDGET(toolitem)); + gtk_toolbar_insert(GTK_TOOLBAR(toolbar), toolitem, -1); + priv->expand_buttons[1] = GTK_WIDGET(toolitem); +} + + +static GtkWidget *cookie_manager_page_get_toolbar(MidoriViewable *viewable) +{ + CookieManagerPagePrivate *priv = COOKIE_MANAGER_PAGE_GET_PRIVATE(viewable); + + return priv->toolbar; +} + + +static void cookie_manager_page_viewable_iface_init(MidoriViewableIface* iface) +{ + iface->get_stock_id = cookie_manager_page_get_stock_id; + iface->get_label = cookie_manager_page_get_label; + iface->get_toolbar = cookie_manager_page_get_toolbar; +} + + +static void cookie_manager_page_finalize(GObject *object) +{ + CookieManagerPagePrivate *priv = COOKIE_MANAGER_PAGE_GET_PRIVATE(object); + + g_signal_handlers_disconnect_by_func(priv->jar, cm_jar_changed_cb, object); + + if (priv->timer_id > 0) + g_source_remove(priv->timer_id); + + cm_free_cookie_list(COOKIE_MANAGER_PAGE(object)); + + gtk_widget_destroy(priv->popup_menu); + + G_OBJECT_CLASS(cookie_manager_page_parent_class)->finalize(object); +} + + +static void cm_refresh_store(CookieManagerPage *cmp) +{ + GSList *l; + GHashTable *parents; + GtkTreeIter iter; + GtkTreeIter *parent_iter; + SoupCookie *cookie; + const gchar *filter_text; + CookieManagerPagePrivate *priv = COOKIE_MANAGER_PAGE_GET_PRIVATE(cmp); + + g_object_ref(priv->filter); + gtk_tree_view_set_model(GTK_TREE_VIEW(priv->treeview), NULL); + + gtk_tree_store_clear(priv->store); + + /* free the old list */ + cm_free_cookie_list(cmp); + + priv->cookies = soup_cookie_jar_all_cookies(priv->jar); + + /* Hashtable holds domain names as keys, the corresponding tree iters as values */ + parents = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); + + for (l = priv->cookies; l != NULL; l = g_slist_next(l)) + { + cookie = l->data; + + /* look for the parent item for the current domain name and create it if it doesn't exist */ + if ((parent_iter = (GtkTreeIter*) g_hash_table_lookup(parents, cookie->domain)) == NULL) + { + parent_iter = g_new0(GtkTreeIter, 1); + + gtk_tree_store_append(priv->store, parent_iter, NULL); + gtk_tree_store_set(priv->store, parent_iter, + COOKIE_MANAGER_COL_NAME, cookie->domain, + COOKIE_MANAGER_COL_COOKIE, NULL, + COOKIE_MANAGER_COL_VISIBLE, TRUE, + -1); + + g_hash_table_insert(parents, g_strdup(cookie->domain), parent_iter); + } + + gtk_tree_store_append(priv->store, &iter, parent_iter); + gtk_tree_store_set(priv->store, &iter, + COOKIE_MANAGER_COL_NAME, cookie->name, + COOKIE_MANAGER_COL_COOKIE, cookie, + COOKIE_MANAGER_COL_VISIBLE, TRUE, + -1); + } + g_hash_table_destroy(parents); + + gtk_tree_view_set_model(GTK_TREE_VIEW(priv->treeview), GTK_TREE_MODEL(priv->filter)); + g_object_unref(priv->filter); + + /* if a filter is set, apply it again */ + filter_text = gtk_entry_get_text(GTK_ENTRY(priv->filter_entry)); + if (*filter_text != '\0') + { + cm_filter_tree(cmp, filter_text); + gtk_tree_view_expand_all(GTK_TREE_VIEW(priv->treeview)); + } +} + + +static gboolean cm_delayed_refresh(CookieManagerPage *cmp) +{ + CookieManagerPagePrivate *priv = COOKIE_MANAGER_PAGE_GET_PRIVATE(cmp); + + cm_refresh_store(cmp); + priv->timer_id = 0; + + return FALSE; +} + + +static void cm_jar_changed_cb(SoupCookieJar *jar, SoupCookie *old, SoupCookie *new, + CookieManagerPage *cmp) +{ + CookieManagerPagePrivate *priv = COOKIE_MANAGER_PAGE_GET_PRIVATE(cmp); + + if (priv->ignore_changed_count > 0) + { + priv->ignore_changed_count--; + return; + } + + /* We delay these events a little bit to avoid too many rebuilds of the tree. + * Some websites (like Flyspray bugtrackers sent a whole bunch of cookies at once. */ + if (priv->timer_id == 0) + priv->timer_id = g_timeout_add_seconds(1, (GSourceFunc) cm_delayed_refresh, cmp); +} + + +static void cookie_manager_page_class_init(CookieManagerPageClass *klass) +{ + GObjectClass *g_object_class; + g_object_class = G_OBJECT_CLASS(klass); + g_object_class->finalize = cookie_manager_page_finalize; + + g_type_class_add_private(klass, sizeof(CookieManagerPagePrivate)); +} + + +static void cm_set_button_sensitiveness(CookieManagerPage *cmp, gboolean set) +{ + CookieManagerPagePrivate *priv = COOKIE_MANAGER_PAGE_GET_PRIVATE(cmp); + gboolean expand_set = (gtk_tree_model_iter_n_children(priv->filter, NULL) > 0); + guint i, len; + + gtk_widget_set_sensitive(priv->delete_popup_button, set); + gtk_widget_set_sensitive(priv->delete_button, set); + + gtk_widget_set_sensitive(priv->delete_all_button, expand_set); + len = G_N_ELEMENTS(priv->expand_buttons); + for (i = 0; i < len; i++) + { + gtk_widget_set_sensitive(priv->expand_buttons[i], expand_set); + } +} + + +static void cm_tree_popup_collapse_activate_cb(GtkMenuItem *item, CookieManagerPage *cmp) +{ + CookieManagerPagePrivate *priv = COOKIE_MANAGER_PAGE_GET_PRIVATE(cmp); + + gtk_tree_view_collapse_all(GTK_TREE_VIEW(priv->treeview)); +} + + +static void cm_tree_popup_expand_activate_cb(GtkMenuItem *item, CookieManagerPage *cmp) +{ + CookieManagerPagePrivate *priv = COOKIE_MANAGER_PAGE_GET_PRIVATE(cmp); + + gtk_tree_view_expand_all(GTK_TREE_VIEW(priv->treeview)); +} + + +static void cm_store_remove(CookieManagerPage *cmp, GtkTreeIter *iter_model) +{ + GtkTreeIter iter_store; + CookieManagerPagePrivate *priv = COOKIE_MANAGER_PAGE_GET_PRIVATE(cmp); + + gtk_tree_model_filter_convert_iter_to_child_iter( + GTK_TREE_MODEL_FILTER(priv->filter), &iter_store, iter_model); + gtk_tree_store_remove(priv->store, &iter_store); +} + + +static void cm_delete_cookie(CookieManagerPage *cmp, GtkTreeModel *model, GtkTreeIter *child) +{ + SoupCookie *cookie; + CookieManagerPagePrivate *priv = COOKIE_MANAGER_PAGE_GET_PRIVATE(cmp); + + gtk_tree_model_get(model, child, COOKIE_MANAGER_COL_COOKIE, &cookie, -1); + + if (cookie != NULL) + { + priv->ignore_changed_count++; + + soup_cookie_jar_delete_cookie(priv->jar, cookie); + /* the SoupCookie object is freed when the whole list gets updated */ + } +} + + +static void cm_button_delete_clicked_cb(GtkToolButton *button, CookieManagerPage *cmp) +{ + GtkTreeIter iter, iter_store, child; + GtkTreeModel *model; + GtkTreeSelection *selection; + CookieManagerPagePrivate *priv = COOKIE_MANAGER_PAGE_GET_PRIVATE(cmp); + + selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(priv->treeview)); + if (! gtk_tree_selection_get_selected(selection, &model, &iter)) + return; + + if (gtk_tree_model_iter_has_child(model, &iter)) + { + GtkTreePath *path = gtk_tree_model_get_path(model, &iter); + + while (gtk_tree_model_iter_children(model, &child, &iter)) + { + cm_delete_cookie(cmp, model, &child); + cm_store_remove(cmp, &child); + /* we retrieve again the iter at path because it got invalid by the delete operation */ + gtk_tree_model_get_iter(model, &iter, path); + } + gtk_tree_path_free(path); + /* remove/hide the parent */ + gtk_tree_model_filter_convert_iter_to_child_iter(GTK_TREE_MODEL_FILTER(priv->filter), + &iter_store, &iter); + if (gtk_tree_model_iter_has_child(GTK_TREE_MODEL(priv->store), &iter_store)) + gtk_tree_store_set(priv->store, &iter_store, COOKIE_MANAGER_COL_VISIBLE, FALSE, -1); + else + cm_store_remove(cmp, &iter); + } + else + { + GtkTreePath *path_store, *path_model; + + gtk_tree_model_filter_convert_iter_to_child_iter(GTK_TREE_MODEL_FILTER(priv->filter), + &iter_store, &iter); + path_store = gtk_tree_model_get_path(GTK_TREE_MODEL(priv->store), &iter_store); + path_model = gtk_tree_model_get_path(model, &iter); + + cm_delete_cookie(cmp, model, &iter); + gtk_tree_store_remove(priv->store, &iter_store); + + /* check whether the parent still has children, otherwise delete it */ + if (gtk_tree_path_up(path_store)) + { + gtk_tree_model_get_iter(GTK_TREE_MODEL(priv->store), &iter_store, path_store); + if (! gtk_tree_model_iter_has_child(GTK_TREE_MODEL(priv->store), &iter_store)) + /* remove the empty parent */ + gtk_tree_store_remove(priv->store, &iter_store); + } + /* now for the filter model */ + if (gtk_tree_path_up(path_model)) + { + gtk_tree_model_get_iter(model, &iter, path_model); + if (! gtk_tree_model_iter_has_child(model, &iter)) + { + gtk_tree_model_filter_convert_iter_to_child_iter( + GTK_TREE_MODEL_FILTER(priv->filter), &iter_store, &iter); + /* hide the empty parent */ + gtk_tree_store_set(priv->store, &iter_store, COOKIE_MANAGER_COL_VISIBLE, FALSE, -1); + } + } + gtk_tree_path_free(path_store); + gtk_tree_path_free(path_model); + } +} + + +static void cm_delete_all_cookies_real(CookieManagerPage *cmp) +{ + CookieManagerPagePrivate *priv = COOKIE_MANAGER_PAGE_GET_PRIVATE(cmp); + GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(priv->treeview)); + GtkTreeIter iter, iter_store, child; + GtkTreePath *path_first, *path; + + path_first = gtk_tree_path_new_first(); + while (gtk_tree_model_get_iter(model, &iter, path_first)) + { + path = gtk_tree_model_get_path(model, &iter); + while (gtk_tree_model_iter_children(model, &child, &iter)) + { + cm_delete_cookie(cmp, model, &child); + cm_store_remove(cmp, &child); + /* we retrieve again the iter at path because it got invalid by the delete operation */ + gtk_tree_model_get_iter(model, &iter, path); + } + gtk_tree_path_free(path); + /* remove/hide the parent */ + gtk_tree_model_filter_convert_iter_to_child_iter(GTK_TREE_MODEL_FILTER(priv->filter), + &iter_store, &iter); + if (gtk_tree_model_iter_has_child(GTK_TREE_MODEL(priv->store), &iter_store)) + gtk_tree_store_set(priv->store, &iter_store, COOKIE_MANAGER_COL_VISIBLE, FALSE, -1); + else + cm_store_remove(cmp, &iter); + } + gtk_tree_path_free(path_first); + + /* now that we deleted all matching cookies, we reset the filter */ + gtk_entry_set_text(GTK_ENTRY(priv->filter_entry), ""); + cm_set_button_sensitiveness(cmp, FALSE); +} + + +static void cm_button_delete_all_clicked_cb(GtkToolButton *button, CookieManagerPage *cmp) +{ + GtkWidget *dialog; + const gchar *filter_text; + MidoriBrowser *toplevel = midori_browser_get_for_widget(GTK_WIDGET(button)); + CookieManagerPagePrivate *priv = COOKIE_MANAGER_PAGE_GET_PRIVATE(cmp); + + dialog = gtk_message_dialog_new(GTK_WINDOW(toplevel), + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_QUESTION, + GTK_BUTTONS_YES_NO, + _("Do you really want to delete all cookies?")); + + gtk_window_set_title(GTK_WINDOW(dialog), _("Question")); + /* steal Midori's icon :) */ + if (toplevel != NULL) + gtk_window_set_icon_name(GTK_WINDOW(dialog), gtk_window_get_icon_name(GTK_WINDOW(toplevel))); + + filter_text = gtk_entry_get_text(GTK_ENTRY(priv->filter_entry)); + if (*filter_text != '\0') + { + gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog), + _("Only cookies which match the filter will be deleted.")); + } + + if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_YES) + cm_delete_all_cookies_real(cmp); + + gtk_widget_destroy(dialog); +} + + +static void cm_tree_drag_data_get_cb(GtkWidget *widget, GdkDragContext *drag_context, + GtkSelectionData *data, guint info, guint ltime, + CookieManagerPage *cmp) +{ + GtkTreeIter iter, iter_store; + GtkTreeSelection *selection; + GtkTreeModel *model; + CookieManagerPagePrivate *priv = COOKIE_MANAGER_PAGE_GET_PRIVATE(cmp); + + selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(priv->treeview)); + if (! gtk_tree_selection_get_selected(selection, &model, &iter)) + return; + + gtk_tree_model_filter_convert_iter_to_child_iter( + GTK_TREE_MODEL_FILTER(model), &iter_store, &iter); + + if (gtk_tree_store_iter_is_valid(priv->store, &iter_store)) + { + SoupCookie *cookie; + gchar *name, *text; + + gtk_tree_model_get(model, &iter, + COOKIE_MANAGER_COL_NAME, &name, + COOKIE_MANAGER_COL_COOKIE, &cookie, + -1); + + if (cookie == NULL && name != NULL) + { + /* skip a leading dot */ + text = (*name == '.') ? name + 1 : name; + + gtk_selection_data_set_text(data, text, -1); + } + g_free(name); + } +} + + +static gchar *cm_get_cookie_description_text(SoupCookie *cookie) +{ + static gchar date_fmt[512]; + gchar *expires; + gchar *text; + time_t expiration_time; + const struct tm *tm; + + g_return_val_if_fail(cookie != NULL, NULL); + + if (cookie->expires != NULL) + { + expiration_time = soup_date_to_time_t(cookie->expires); + tm = localtime(&expiration_time); + /* even if some gcc versions complain about "%c", there is nothing wrong with and here we + * want to use it */ + strftime(date_fmt, sizeof(date_fmt), "%c", tm); + expires = date_fmt; + } + else + expires = _("At the end of the session"); + + text = g_markup_printf_escaped( + _("Host: %s\nName: %s\nValue: %s\nPath: %s\n" + "Secure: %s\nExpires: %s"), + cookie->domain, + cookie->name, + cookie->value, + cookie->path, + cookie->secure ? _("Yes") : _("No"), + expires); + + return text; +} + + +#if GTK_CHECK_VERSION(2, 12, 0) +static gboolean cm_tree_query_tooltip(GtkWidget *widget, gint x, gint y, gboolean keyboard_mode, + GtkTooltip *tooltip, CookieManagerPage *cmp) +{ + GtkTreeIter iter; + GtkTreeModel *model; + + if (gtk_tree_view_get_tooltip_context(GTK_TREE_VIEW(widget), &x, &y, + keyboard_mode, &model, NULL, &iter)) + { + gchar *tooltip_text; + SoupCookie *cookie; + + gtk_tree_model_get(model, &iter, COOKIE_MANAGER_COL_COOKIE, &cookie, -1); + + if (cookie == NULL) /* not an item */ + return FALSE; + + tooltip_text = cm_get_cookie_description_text(cookie); + + gtk_tooltip_set_markup(tooltip, tooltip_text); + + g_free(tooltip_text); + + return TRUE; + } + + return FALSE; +} +#endif + + +static gboolean cm_filter_match(const gchar *haystack, const gchar *needle) +{ + gchar *haystack_lowered, *needle_lowered; + gboolean result; + + /* empty strings always match */ + if (haystack == NULL || needle == NULL || *needle == '\0') + return TRUE; + + haystack_lowered = g_utf8_strdown(haystack, -1); + needle_lowered = g_utf8_strdown(needle, -1); + + /* if one of both could not be converted into lower case, skip those */ + if (haystack_lowered == NULL || needle_lowered == NULL) + return FALSE; + + result = (strstr(haystack_lowered, needle_lowered) != NULL); + + g_free(haystack_lowered); + g_free(needle_lowered); + + return result; +} + + +static void cm_filter_tree(CookieManagerPage *cmp, const gchar *filter_text) +{ + GtkTreeIter iter, child; + GtkTreeModel *model; + gboolean show_child, show_parent; + gboolean child_visible; + gint i, n; + gchar *name; + gchar *domain; + CookieManagerPagePrivate *priv = COOKIE_MANAGER_PAGE_GET_PRIVATE(cmp); + + model = GTK_TREE_MODEL(priv->store); + if (! gtk_tree_model_get_iter_first(model, &iter)) + return; + + do + { + if (gtk_tree_model_iter_has_child(model, &iter)) + { + child_visible = FALSE; + + gtk_tree_model_get(model, &iter, COOKIE_MANAGER_COL_NAME, &domain, -1); + show_parent = cm_filter_match(domain, filter_text); + g_free(domain); + n = gtk_tree_model_iter_n_children(model, &iter); + for (i = 0; i < n; i++) + { + gtk_tree_model_iter_nth_child(model, &child, &iter, i); + + gtk_tree_model_get(model, &child, COOKIE_MANAGER_COL_NAME, &name, -1); + show_child = show_parent || cm_filter_match(name, filter_text); + g_free(name); + + if (show_child) + child_visible = TRUE; + + gtk_tree_store_set(priv->store, &child, COOKIE_MANAGER_COL_VISIBLE, show_child, -1); + } + gtk_tree_store_set(priv->store, &iter, COOKIE_MANAGER_COL_VISIBLE, child_visible, -1); + } + } + while (gtk_tree_model_iter_next(model, &iter)); +} + + +static void cm_filter_entry_changed_cb(GtkEditable *editable, CookieManagerPage *cmp) +{ + const gchar *text = gtk_entry_get_text(GTK_ENTRY(editable)); + CookieManagerPagePrivate *priv = COOKIE_MANAGER_PAGE_GET_PRIVATE(cmp); + + cm_filter_tree(cmp, text); + + if (*text != '\0') + gtk_tree_view_expand_all(GTK_TREE_VIEW(priv->treeview)); + else + gtk_tree_view_collapse_all(GTK_TREE_VIEW(priv->treeview)); +} + + +static void cm_filter_entry_clear_icon_released_cb(GtkIconEntry *e, gint pos, gint btn, gpointer data) +{ + if (pos == GTK_ICON_ENTRY_SECONDARY) + gtk_entry_set_text(GTK_ENTRY(e), ""); +} + + +static void cm_tree_selection_changed_cb(GtkTreeSelection *selection, CookieManagerPage *cmp) +{ + GtkTreeIter iter, iter_store; + GtkTreeModel *model; + gchar *text; + gboolean valid = TRUE; + gboolean delete_possible = FALSE; + SoupCookie *cookie; + CookieManagerPagePrivate *priv = COOKIE_MANAGER_PAGE_GET_PRIVATE(cmp); + + if (! gtk_tree_selection_get_selected(selection, &model, &iter)) + valid = FALSE; + else + gtk_tree_model_filter_convert_iter_to_child_iter(GTK_TREE_MODEL_FILTER(model), + &iter_store, &iter); + + if (valid && gtk_tree_store_iter_is_valid(priv->store, &iter_store)) + { + delete_possible = TRUE; + + gtk_tree_model_get(model, &iter, COOKIE_MANAGER_COL_COOKIE, &cookie, -1); + if (cookie != NULL) + { + text = cm_get_cookie_description_text(cookie); + + gtk_label_set_markup(GTK_LABEL(priv->desc_label), text); + + g_free(text); + } + else + valid = FALSE; + } + /* This is a bit hack'ish but we add some empty lines to get a minimum height of the + * label at the bottom without any font size calculation. */ + if (! valid) + gtk_label_set_text(GTK_LABEL(priv->desc_label), CM_EMPTY_LABEL_TEXT); + cm_set_button_sensitiveness(cmp, delete_possible); +} + + +static void cm_tree_show_popup_menu(GtkWidget *widget, GdkEventButton *event, CookieManagerPage *cmp) +{ + gint button, event_time; + CookieManagerPagePrivate *priv = COOKIE_MANAGER_PAGE_GET_PRIVATE(cmp); + + if (event != NULL) + { + button = event->button; + event_time = event->time; + } + else + { + button = 0; + event_time = gtk_get_current_event_time (); + } + + gtk_menu_popup(GTK_MENU(priv->popup_menu), NULL, NULL, NULL, NULL, button, event_time); +} + + +static gboolean cm_tree_popup_menu_cb(GtkWidget *widget, CookieManagerPage *cmp) +{ + cm_tree_show_popup_menu(widget, NULL, cmp); + return TRUE; +} + + +static gboolean cm_tree_button_release_event_cb(GtkWidget *widget, GdkEventButton *ev, + CookieManagerPage *cmp) +{ + if (ev->button == 3) + { + cm_tree_show_popup_menu(widget, ev, cmp); + return TRUE; + } + return FALSE; +} + + +static gboolean cm_tree_button_press_event_cb(GtkWidget *widget, GdkEventButton *ev, + CookieManagerPage *cmp) +{ + if (ev->type == GDK_2BUTTON_PRESS) + { + GtkTreeSelection *selection; + GtkTreeModel *model; + GtkTreeIter iter; + + selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget)); + + if (gtk_tree_selection_get_selected(selection, &model, &iter)) + { + /* double click on parent node expands/collapses it */ + if (gtk_tree_model_iter_has_child(model, &iter)) + { + GtkTreePath *path = gtk_tree_model_get_path(model, &iter); + + if (gtk_tree_view_row_expanded(GTK_TREE_VIEW(widget), path)) + gtk_tree_view_collapse_row(GTK_TREE_VIEW(widget), path); + else + gtk_tree_view_expand_row(GTK_TREE_VIEW(widget), path, FALSE); + + gtk_tree_path_free(path); + + return TRUE; + } + } + } + + return FALSE; +} + + +static GtkWidget *cm_tree_prepare(CookieManagerPage *cmp) +{ + GtkCellRenderer *renderer; + GtkTreeViewColumn *column; + GtkTreeSelection *sel; + GtkWidget *item; + GtkWidget *menu; + GtkWidget *treeview; + CookieManagerPagePrivate *priv = COOKIE_MANAGER_PAGE_GET_PRIVATE(cmp); + + treeview = priv->treeview = gtk_tree_view_new(); + + renderer = gtk_cell_renderer_text_new(); + column = gtk_tree_view_column_new_with_attributes( + _("Name"), renderer, "text", COOKIE_MANAGER_COL_NAME, NULL); + gtk_tree_view_column_set_sort_indicator(column, TRUE); + gtk_tree_view_column_set_sort_column_id(column, COOKIE_MANAGER_COL_NAME); + gtk_tree_view_column_set_resizable(column, TRUE); + gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column); + + gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(treeview), TRUE); + gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(treeview), FALSE); + gtk_tree_view_set_search_column(GTK_TREE_VIEW(treeview), COOKIE_MANAGER_COL_NAME); + + /* selection handling */ + sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview)); + gtk_tree_selection_set_mode(sel, GTK_SELECTION_SINGLE); + + /* create the main store */ + priv->store = gtk_tree_store_new(COOKIE_MANAGER_N_COLUMNS, + G_TYPE_STRING, SOUP_TYPE_COOKIE, G_TYPE_BOOLEAN); + gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(priv->store), + COOKIE_MANAGER_COL_NAME, GTK_SORT_ASCENDING); + + /* setting filter and model */ + priv->filter = gtk_tree_model_filter_new(GTK_TREE_MODEL(priv->store), NULL); + gtk_tree_model_filter_set_visible_column(GTK_TREE_MODEL_FILTER(priv->filter), + COOKIE_MANAGER_COL_VISIBLE); + gtk_tree_view_set_model(GTK_TREE_VIEW(priv->treeview), GTK_TREE_MODEL(priv->filter)); + g_object_unref(priv->store); + g_object_unref(priv->filter); + + /* signals */ + g_signal_connect(sel, "changed", G_CALLBACK(cm_tree_selection_changed_cb), cmp); + g_signal_connect(treeview, "button-press-event", G_CALLBACK(cm_tree_button_press_event_cb), cmp); + g_signal_connect(treeview, "button-release-event", G_CALLBACK(cm_tree_button_release_event_cb), cmp); + g_signal_connect(treeview, "popup-menu", G_CALLBACK(cm_tree_popup_menu_cb), cmp); + + /* tooltips */ +#if GTK_CHECK_VERSION(2, 12, 0) + gtk_widget_set_has_tooltip(treeview, TRUE); + g_signal_connect(treeview, "query-tooltip", G_CALLBACK(cm_tree_query_tooltip), cmp); +#endif + + /* drag'n'drop */ + gtk_tree_view_enable_model_drag_source( + GTK_TREE_VIEW(treeview), + GDK_BUTTON1_MASK, + NULL, + 0, + GDK_ACTION_COPY + ); + gtk_drag_source_add_text_targets(treeview); + /*gtk_drag_source_add_uri_targets(treeview);*/ + g_signal_connect(treeview, "drag-data-get", G_CALLBACK(cm_tree_drag_data_get_cb), cmp); + + /* popup menu */ + priv->popup_menu = menu = gtk_menu_new(); + + item = gtk_image_menu_item_new_from_stock(GTK_STOCK_DELETE, NULL); + gtk_widget_show(item); + gtk_container_add(GTK_CONTAINER(menu), item); + g_signal_connect(item, "activate", G_CALLBACK(cm_button_delete_clicked_cb), cmp); + priv->delete_popup_button = item; + + item = gtk_separator_menu_item_new(); + gtk_widget_show(item); + gtk_container_add(GTK_CONTAINER(menu), item); + + item = gtk_image_menu_item_new_with_mnemonic(_("_Expand All")); + gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), + gtk_image_new_from_stock(GTK_STOCK_ADD, GTK_ICON_SIZE_MENU)); + gtk_widget_show(item); + gtk_container_add(GTK_CONTAINER(menu), item); + g_signal_connect(item, "activate", G_CALLBACK(cm_tree_popup_expand_activate_cb), cmp); + priv->expand_buttons[2] = item; + + item = gtk_image_menu_item_new_with_mnemonic(_("_Collapse All")); + gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), + gtk_image_new_from_icon_name(GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU)); + gtk_widget_show(item); + gtk_container_add(GTK_CONTAINER(menu), item); + g_signal_connect(item, "activate", G_CALLBACK(cm_tree_popup_collapse_activate_cb), cmp); + priv->expand_buttons[3] = item; + + return treeview; +} + + +static void cookie_manager_page_init(CookieManagerPage *self) +{ + GtkWidget *tree_swin; + GtkWidget *desc_swin; + GtkWidget *paned; + GtkWidget *filter_hbox; + GtkWidget *filter_label; + GtkWidget *treeview; + SoupSession *session; + CookieManagerPagePrivate *priv = COOKIE_MANAGER_PAGE_GET_PRIVATE(self); + + cm_create_toolbar(self); + + priv->desc_label = gtk_label_new(CM_EMPTY_LABEL_TEXT); + gtk_label_set_selectable(GTK_LABEL(priv->desc_label), TRUE); + gtk_label_set_line_wrap(GTK_LABEL(priv->desc_label), TRUE); + gtk_label_set_line_wrap_mode(GTK_LABEL(priv->desc_label), PANGO_WRAP_CHAR); + gtk_misc_set_alignment(GTK_MISC(priv->desc_label), 0, 0); + gtk_misc_set_padding(GTK_MISC(priv->desc_label), 3, 3); + gtk_widget_show(priv->desc_label); + + desc_swin = gtk_scrolled_window_new(NULL, NULL); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(desc_swin), + GTK_POLICY_AUTOMATIC, GTK_POLICY_NEVER); + gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(desc_swin), GTK_SHADOW_NONE); + gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(desc_swin), priv->desc_label); + gtk_widget_show(desc_swin); + + treeview = cm_tree_prepare(self); + gtk_widget_show(treeview); + + tree_swin = gtk_scrolled_window_new(NULL, NULL); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(tree_swin), + GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); + gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(tree_swin), GTK_SHADOW_IN); + gtk_container_add(GTK_CONTAINER(tree_swin), treeview); + gtk_widget_show(tree_swin); + + filter_label = gtk_label_new(_("Filter:")); + gtk_widget_show(filter_label); + + priv->filter_entry = gtk_icon_entry_new(); + gtk_widget_set_tooltip_text(priv->filter_entry, + _("Enter a filter string to show only cookies whose name or domain " + "field match the entered filter")); + gtk_widget_show(priv->filter_entry); + gtk_icon_entry_set_icon_from_stock(GTK_ICON_ENTRY(priv->filter_entry), + GTK_ICON_ENTRY_SECONDARY, GTK_STOCK_CLEAR); + gtk_icon_entry_set_icon_highlight(GTK_ICON_ENTRY (priv->filter_entry), + GTK_ICON_ENTRY_SECONDARY, TRUE); + g_signal_connect(priv->filter_entry, "icon-release", + G_CALLBACK(cm_filter_entry_clear_icon_released_cb), NULL); + g_signal_connect(priv->filter_entry, "changed", G_CALLBACK(cm_filter_entry_changed_cb), self); + g_signal_connect(priv->filter_entry, "activate", G_CALLBACK(cm_filter_entry_changed_cb), self); + + filter_hbox = gtk_hbox_new(FALSE, 0); + gtk_box_pack_start(GTK_BOX(filter_hbox), filter_label, FALSE, FALSE, 3); + gtk_box_pack_start(GTK_BOX(filter_hbox), priv->filter_entry, TRUE, TRUE, 3); + gtk_widget_show(filter_hbox); + + paned = gtk_vpaned_new(); + gtk_paned_pack1(GTK_PANED(paned), tree_swin, TRUE, FALSE); + gtk_paned_pack2(GTK_PANED(paned), desc_swin, FALSE, FALSE); + gtk_widget_show(paned); + + gtk_box_pack_start(GTK_BOX(self), filter_hbox, FALSE, FALSE, 5); + gtk_box_pack_start(GTK_BOX(self), paned, TRUE, TRUE, 0); + + /* setup soup */ + session = webkit_get_default_session(); + priv->jar = SOUP_COOKIE_JAR(soup_session_get_feature(session, soup_cookie_jar_get_type())); + g_signal_connect(priv->jar, "changed", G_CALLBACK(cm_jar_changed_cb), self); + + cm_refresh_store(self); +} + + +GtkWidget *cookie_manager_page_new(void) +{ + return g_object_new(COOKIE_MANAGER_PAGE_TYPE, NULL); +} + diff --git a/extensions/cookie-manager/cookie-manager-page.h b/extensions/cookie-manager/cookie-manager-page.h new file mode 100644 index 00000000..37e02c0d --- /dev/null +++ b/extensions/cookie-manager/cookie-manager-page.h @@ -0,0 +1,45 @@ +/* + Copyright (C) 2009 Enrico Tröger + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + See the file COPYING for the full license text. +*/ + +#ifndef __COOKIE_MANAGER_PAGE_H__ +#define __COOKIE_MANAGER_PAGE_H__ + +G_BEGIN_DECLS + +#define COOKIE_MANAGER_PAGE_TYPE (cookie_manager_page_get_type()) +#define COOKIE_MANAGER_PAGE(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),\ + COOKIE_MANAGER_PAGE_TYPE, CookieManagerPage)) +#define COOKIE_MANAGER_PAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),\ + COOKIE_MANAGER_PAGE_TYPE, CookieManagerPageClass)) +#define IS_COOKIE_MANAGER_PAGE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),\ + COOKIE_MANAGER_PAGE_TYPE)) +#define IS_COOKIE_MANAGER_PAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),\ + COOKIE_MANAGER_PAGE_TYPE)) + +typedef struct _CookieManagerPage CookieManagerPage; +typedef struct _CookieManagerPageClass CookieManagerPageClass; + +struct _CookieManagerPage +{ + GtkVBox parent; +}; + +struct _CookieManagerPageClass +{ + GtkVBoxClass parent_class; +}; + +GType cookie_manager_page_get_type (void); +GtkWidget* cookie_manager_page_new (void); + +G_END_DECLS + +#endif /* __COOKIE_MANAGER_PAGE_H__ */ diff --git a/extensions/cookie-manager/cookie-manager.c b/extensions/cookie-manager/cookie-manager.c new file mode 100644 index 00000000..38d557a6 --- /dev/null +++ b/extensions/cookie-manager/cookie-manager.c @@ -0,0 +1,130 @@ +/* + Copyright (C) 2009 Enrico Tröger + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + See the file COPYING for the full license text. +*/ + +#include "config.h" +#include +#include + +#include "cookie-manager.h" +#include "cookie-manager-page.h" + + + +typedef struct +{ + MidoriApp *app; + MidoriBrowser *browser; + MidoriExtension *extension; + GtkWidget *panel_page; +} CMData; + +static void cm_app_add_browser_cb(MidoriApp *app, MidoriBrowser *browser, MidoriExtension *ext); +static void cm_deactivate_cb(MidoriExtension *extension, CMData *cmdata); + + + +static void cm_browser_close_cb(GtkObject *browser, CMData *cmdata) +{ + g_signal_handlers_disconnect_by_func(cmdata->extension, cm_deactivate_cb, cmdata); + g_signal_handlers_disconnect_by_func(cmdata->browser, cm_browser_close_cb, cmdata); + + /* the panel_page widget gets destroyed automatically when a browser is closed but not + * when the extension is deactivated */ + if (cmdata->panel_page != NULL && IS_COOKIE_MANAGER_PAGE(cmdata->panel_page)) + gtk_widget_destroy(cmdata->panel_page); + + g_free(cmdata); +} + + +static void cm_deactivate_cb(MidoriExtension *extension, CMData *cmdata) +{ + g_signal_handlers_disconnect_by_func(cmdata->app, cm_app_add_browser_cb, extension); + cm_browser_close_cb(NULL, cmdata); +} + + +static void cm_app_add_browser_cb(MidoriApp *app, MidoriBrowser *browser, MidoriExtension *ext) +{ + GtkWidget *panel; + GtkWidget *page; + CMData *cmdata; + + panel = katze_object_get_object(browser, "panel"); + + page = cookie_manager_page_new(); + gtk_widget_show(page); + midori_panel_append_page(MIDORI_PANEL(panel), MIDORI_VIEWABLE(page)); + + cmdata = g_new0(CMData, 1); + cmdata->app = app; + cmdata->browser = browser; + cmdata->extension = ext; + cmdata->panel_page = page; + + g_signal_connect(browser, "destroy", G_CALLBACK(cm_browser_close_cb), cmdata); + g_signal_connect(ext, "deactivate", G_CALLBACK(cm_deactivate_cb), cmdata); + + g_object_unref(panel); +} + + +static void cm_activate_cb(MidoriExtension *extension, MidoriApp *app, gpointer data) +{ + guint i; + KatzeArray *browsers; + MidoriBrowser *browser; + + /* add the cookie manager panel page to existing browsers */ + browsers = katze_object_get_object(app, "browsers"); + i = 0; + while ((browser = katze_array_get_nth_item(browsers, i++))) + cm_app_add_browser_cb(app, browser, extension); + g_object_unref(browsers); + + g_signal_connect(app, "add-browser", G_CALLBACK(cm_app_add_browser_cb), extension); +} + + +MidoriExtension *extension_init(void) +{ + MidoriExtension *extension; + GtkIconFactory *factory; + GtkIconSource *icon_source; + GtkIconSet *icon_set; + static GtkStockItem items[] = + { + { STOCK_COOKIE_MANAGER, N_("_Cookie Manager"), 0, 0, NULL } + }; + + factory = gtk_icon_factory_new(); + gtk_stock_add(items, G_N_ELEMENTS(items)); + icon_set = gtk_icon_set_new(); + icon_source = gtk_icon_source_new(); + gtk_icon_source_set_icon_name(icon_source, GTK_STOCK_DIALOG_AUTHENTICATION); + gtk_icon_set_add_source(icon_set, icon_source); + gtk_icon_source_free(icon_source); + gtk_icon_factory_add(factory, STOCK_COOKIE_MANAGER, icon_set); + gtk_icon_set_unref(icon_set); + gtk_icon_factory_add_default(factory); + g_object_unref(factory); + + extension = g_object_new(MIDORI_TYPE_EXTENSION, + "name", _("Cookie Manager"), + "description", _("List, view and delete cookies"), + "version", "0.2", + "authors", "Enrico Tröger ", + NULL); + + g_signal_connect(extension, "activate", G_CALLBACK(cm_activate_cb), NULL); + + return extension; +} diff --git a/extensions/cookie-manager/cookie-manager.h b/extensions/cookie-manager/cookie-manager.h new file mode 100644 index 00000000..9f6a3996 --- /dev/null +++ b/extensions/cookie-manager/cookie-manager.h @@ -0,0 +1,20 @@ +/* + Copyright (C) 2009 Enrico Tröger + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + See the file COPYING for the full license text. +*/ + + +#ifndef __COOKIE_MANAGER_H__ +#define __COOKIE_MANAGER_H__ + + +#define STOCK_COOKIE_MANAGER "cookie-manager" + + +#endif /* __COOKIE_MANAGER_H__ */ diff --git a/po/POTFILES.in b/po/POTFILES.in index 542c6210..27dcc9fe 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -25,7 +25,8 @@ katze/katze-item.c katze/katze-array.c katze/katze-arrayaction.c extensions/colorful-tabs.c -extensions/cookie-manager.c +extensions/cookie-manager/cookie-manager.c +extensions/cookie-manager/cookie-manager-page.c extensions/feed-panel/feed-atom.c extensions/feed-panel/feed-panel.c extensions/feed-panel/feed-parse.c