]> spindle.queued.net Git - midori/commitdiff
Split the CookieManager extension into separate files
authorEnrico Tröger <enrico.troeger@uvena.de>
Tue, 19 May 2009 17:04:25 +0000 (19:04 +0200)
committerChristian Dywan <christian@twotoasts.de>
Tue, 19 May 2009 17:04:25 +0000 (19:04 +0200)
extensions/cookie-manager.c [deleted file]
extensions/cookie-manager/cookie-manager-page.c [new file with mode: 0644]
extensions/cookie-manager/cookie-manager-page.h [new file with mode: 0644]
extensions/cookie-manager/cookie-manager.c [new file with mode: 0644]
extensions/cookie-manager/cookie-manager.h [new file with mode: 0644]
po/POTFILES.in

diff --git a/extensions/cookie-manager.c b/extensions/cookie-manager.c
deleted file mode 100644 (file)
index e3b9783..0000000
+++ /dev/null
@@ -1,975 +0,0 @@
-/*
- Copyright (C) 2009 Enrico Tröger <enrico(dot)troeger(at)uvena(dot)de>
-
- 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 <time.h>
-#include <midori/midori.h>
-#include <webkit/webkit.h>
-#include <midori/gtkiconentry.h>
-
-#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(
-                       _("<b>Host</b>: %s\n<b>Name</b>: %s\n<b>Value</b>: %s\n<b>Path</b>: %s\n"
-                         "<b>Secure</b>: %s\n<b>Expires</b>: %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 <enrico(dot)troeger(at)uvena(dot)de>",
-               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 (file)
index 0000000..9e73fe3
--- /dev/null
@@ -0,0 +1,985 @@
+/*
+ Copyright (C) 2009 Enrico Tröger <enrico(dot)troeger(at)uvena(dot)de>
+
+ 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 <gtk/gtk.h>
+
+#include <midori/midori.h>
+#include <midori/gtkiconentry.h>
+#include <webkit/webkit.h>
+#include <time.h>
+
+#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(
+                       _("<b>Host</b>: %s\n<b>Name</b>: %s\n<b>Value</b>: %s\n<b>Path</b>: %s\n"
+                         "<b>Secure</b>: %s\n<b>Expires</b>: %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 (file)
index 0000000..37e02c0
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ Copyright (C) 2009 Enrico Tröger <enrico(dot)troeger(at)uvena(dot)de>
+
+ 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 (file)
index 0000000..38d557a
--- /dev/null
@@ -0,0 +1,130 @@
+/*
+ Copyright (C) 2009 Enrico Tröger <enrico(dot)troeger(at)uvena(dot)de>
+
+ 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 <midori/midori.h>
+#include <webkit/webkit.h>
+
+#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 <enrico(dot)troeger(at)uvena(dot)de>",
+               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 (file)
index 0000000..9f6a399
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ Copyright (C) 2009 Enrico Tröger <enrico(dot)troeger(at)uvena(dot)de>
+
+ 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__ */
index 542c62109f7cca4bae5229658bfee5f3dc050e99..27dcc9fe48dc980e64231b89693a6ba412d7cd9b 100644 (file)
@@ -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