]> spindle.queued.net Git - midori/commitdiff
Implement filter entry in the Cookie Manager
authorEnrico Tröger <enrico.troeger@uvena.de>
Tue, 31 Mar 2009 21:41:23 +0000 (23:41 +0200)
committerChristian Dywan <christian@twotoasts.de>
Tue, 31 Mar 2009 21:41:23 +0000 (23:41 +0200)
extensions/cookie-manager.c

index af482a22b9126cb858e86e6feaf965e81bb882e7..3fd813fd06d1a07d8da14a28037f40bd2fc5ad49 100644 (file)
@@ -13,6 +13,7 @@
 #include <time.h>
 #include <midori/midori.h>
 #include <webkit/webkit.h>
+#include <midori/gtkiconentry.h>
 
 #define CM_DEBUG 0
 
@@ -23,6 +24,7 @@ enum
 {
        COL_NAME,
        COL_COOKIE,
+       COL_VISIBLE,
        N_COLUMNS
 };
 
@@ -33,9 +35,15 @@ typedef struct _CMData
        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;
 
@@ -98,7 +106,7 @@ static void cm_refresh_store(CMData *cmdata)
        GtkTreeIter *parent_iter;
        SoupCookie *cookie;
 
-       g_object_ref(cmdata->store);
+       g_object_ref(cmdata->filter);
        gtk_tree_view_set_model(GTK_TREE_VIEW(cmdata->treeview), NULL);
 
        gtk_tree_store_clear(cmdata->store);
@@ -124,6 +132,7 @@ static void cm_refresh_store(CMData *cmdata)
                        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);
@@ -133,12 +142,13 @@ static void cm_refresh_store(CMData *cmdata)
                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->store));
-       g_object_unref(cmdata->store);
+       gtk_tree_view_set_model(GTK_TREE_VIEW(cmdata->treeview), GTK_TREE_MODEL(cmdata->filter));
+       g_object_unref(cmdata->filter);
 }
 
 
@@ -177,17 +187,42 @@ static gchar *cm_get_cookie_description_text(SoupCookie *cookie)
 }
 
 
+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;
+       GtkTreeIter iter, iter_store;
        GtkTreeModel *model;
        gchar *text;
+       gboolean valid = TRUE;
+       gboolean delete_possible = FALSE;
        SoupCookie *cookie;
 
-       gtk_tree_selection_get_selected(selection, &model, &iter);
+       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 (model != NULL && gtk_tree_store_iter_is_valid(GTK_TREE_STORE(model), &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)
                {
@@ -195,16 +230,16 @@ static void cm_tree_selection_changed_cb(GtkTreeSelection *selection, CMData *cm
 
                        gtk_label_set_markup(GTK_LABEL(cmdata->desc_label), text);
 
-                       gtk_widget_set_sensitive(cmdata->delete_button, TRUE);
-
                        g_free(text);
-                       return;
                }
+               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. */
-       gtk_label_set_text(GTK_LABEL(cmdata->desc_label), CM_EMPTY_LABEL_TEXT);
-       gtk_widget_set_sensitive(cmdata->delete_button, FALSE);
+       if (! valid)
+               gtk_label_set_text(GTK_LABEL(cmdata->desc_label), CM_EMPTY_LABEL_TEXT);
+       cm_set_button_sensitiveness(cmdata, delete_possible);
 }
 
 
@@ -217,9 +252,8 @@ static gboolean cm_tree_button_press_event_cb(GtkWidget *widget, GdkEventButton
                GtkTreeIter iter;
 
                selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
-               model = GTK_TREE_MODEL(cmdata->store);
 
-               if (gtk_tree_selection_get_selected(selection, NULL, &iter))
+               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))
@@ -237,6 +271,7 @@ static gboolean cm_tree_button_press_event_cb(GtkWidget *widget, GdkEventButton
                        }
                }
        }
+
        return FALSE;
 }
 
@@ -290,6 +325,16 @@ static void cm_tree_popup_expand_activate_cb(GtkCheckMenuItem *item, CMData *cmd
 }
 
 
+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;
@@ -306,71 +351,110 @@ static void cm_delete_cookie(GtkTreeModel *model, GtkTreeIter *child, CMData *cm
 }
 
 
-static void cm_button_delete_clicked_cb(GtkWidget *button, CMData *cmdata)
+static void cm_button_delete_clicked_cb(GtkToolButton *button, CMData *cmdata)
 {
-       GtkTreeIter iter;
+       GtkTreeIter iter, iter_store, child;
        GtkTreeModel *model;
        GtkTreeSelection *selection;
 
        selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(cmdata->treeview));
-       gtk_tree_selection_get_selected(selection, &model, &iter);
+       if (! gtk_tree_selection_get_selected(selection, &model, &iter))
+               return;
 
        if (gtk_tree_model_iter_has_child(model, &iter))
        {
-               gint i, n = gtk_tree_model_iter_n_children(model, &iter);
-               GtkTreeIter child;
+               GtkTreePath *path = gtk_tree_model_get_path(model, &iter);
 
-               for (i = 0; i < n; i++)
+               while (gtk_tree_model_iter_children(model, &child, &iter))
                {
-                       gtk_tree_model_iter_nth_child(model, &child, &iter, i);
                        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);
                }
-               /* remove the parent */
-               /* TODO does this really remove all children automatically? */
-               gtk_tree_store_remove(cmdata->store, &iter);
+               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 = gtk_tree_model_get_path(model, &iter);
+               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);
+               gtk_tree_store_remove(cmdata->store, &iter_store);
 
                /* check whether the parent still has children, otherwise delete it */
-               if (gtk_tree_path_up(path))
+               if (gtk_tree_path_up(path_store))
                {
-                       gtk_tree_model_get_iter(model, &iter, path);
-                       if (! gtk_tree_model_iter_has_child(model, &iter))
+                       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);
+                               gtk_tree_store_remove(cmdata->store, &iter_store);
                }
-               gtk_tree_path_free(path);
+               /* 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)
 {
-       GSList *l;
+       GtkTreeIter iter, iter_store, child;
+       GtkTreePath *path_first, *path;
+       GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(cmdata->treeview));
 
-       for (l = cmdata->cookies; l != NULL; l = g_slist_next(l))
+       path_first = gtk_tree_path_new_first();
+       while (gtk_tree_model_get_iter(model, &iter, path_first))
        {
-               SoupCookie *cookie = l->data;
-
-               cmdata->ignore_changed_count++;
-               soup_cookie_jar_delete_cookie(cmdata->jar, cookie);
-               /* the SoupCookie object is freed below when calling cm_free_cookie_list() */
+               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);
 
-       gtk_tree_store_clear(cmdata->store);
-       cm_free_cookie_list(cmdata);
+       cm_set_button_sensitiveness(cmdata, FALSE);
 }
 
 
-static void cm_button_delete_all_clicked_cb(GtkWidget *button, CMData *cmdata)
+static void cm_button_delete_all_clicked_cb(GtkToolButton *button, CMData *cmdata)
 {
        GtkWidget *dialog;
-       GtkWidget *toplevel = gtk_widget_get_toplevel(button);
+       GtkWidget *toplevel = gtk_widget_get_toplevel(GTK_WIDGET(button));
 
        dialog = gtk_message_dialog_new(GTK_WINDOW(toplevel),
                GTK_DIALOG_DESTROY_WITH_PARENT,
@@ -392,14 +476,18 @@ static void cm_button_delete_all_clicked_cb(GtkWidget *button, CMData *cmdata)
 static void cm_tree_drag_data_get_cb(GtkWidget *widget, GdkDragContext *drag_context,
                                                                         GtkSelectionData *data, guint info, guint ltime, CMData *cmdata)
 {
-       GtkTreeIter iter;
-       GtkTreeModel *model;
+       GtkTreeIter iter, iter_store;
        GtkTreeSelection *selection;
+       GtkTreeModel *model;
 
        selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(cmdata->treeview));
-       gtk_tree_selection_get_selected(selection, &model, &iter);
+       if (! gtk_tree_selection_get_selected(selection, &model, &iter))
+               return;
 
-       if (model != NULL && gtk_tree_store_iter_is_valid(GTK_TREE_STORE(model), &iter))
+       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;
@@ -422,8 +510,8 @@ static void cm_tree_drag_data_get_cb(GtkWidget *widget, GdkDragContext *drag_con
 static gboolean cm_tree_query_tooltip(GtkWidget *widget, gint x, gint y, gboolean keyboard_mode,
                                                                          GtkTooltip *tooltip, CMData *cmdata)
 {
-       GtkTreeModel *model;
        GtkTreeIter iter;
+       GtkTreeModel *model;
 
        if (gtk_tree_view_get_tooltip_context(GTK_TREE_VIEW(widget), &x, &y,
                        keyboard_mode, &model, NULL, &iter))
@@ -450,6 +538,66 @@ static gboolean cm_tree_query_tooltip(GtkWidget *widget, gint x, gint y, gboolea
 #endif
 
 
+static void cm_filter_tree(CMData *cmdata, const gchar *filter_text)
+{
+       GtkTreeIter iter, child;
+       GtkTreeModel *model;
+       gboolean show;
+       gboolean child_visible;
+       gint i, n;
+       gchar *name;
+
+       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;
+
+                       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 = filter_text == NULL || *filter_text == '\0' || strstr(name, filter_text);
+                               g_free(name);
+
+                               if (show)
+                                       child_visible = TRUE;
+
+                               gtk_tree_store_set(cmdata->store, &child, COL_VISIBLE, show, -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;
@@ -460,7 +608,7 @@ static void cm_tree_prepare(CMData *cmdata)
        GtkWidget *menu;
 
        cmdata->treeview = tree = gtk_tree_view_new();
-       cmdata->store = gtk_tree_store_new(N_COLUMNS, G_TYPE_STRING, SOUP_TYPE_COOKIE);
+       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(
@@ -471,7 +619,7 @@ static void cm_tree_prepare(CMData *cmdata)
        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_clickable(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);
 
@@ -479,8 +627,13 @@ static void cm_tree_prepare(CMData *cmdata)
        sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree));
        gtk_tree_selection_set_mode(sel, GTK_SELECTION_SINGLE);
 
-       gtk_tree_view_set_model(GTK_TREE_VIEW(tree), GTK_TREE_MODEL(cmdata->store));
+       /* 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);
@@ -513,6 +666,7 @@ static void cm_tree_prepare(CMData *cmdata)
        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);
@@ -524,6 +678,7 @@ static void cm_tree_prepare(CMData *cmdata)
        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),
@@ -531,6 +686,7 @@ static void cm_tree_prepare(CMData *cmdata)
        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;
 }
@@ -568,6 +724,9 @@ static void cm_app_add_browser_cb(MidoriApp *app, MidoriBrowser *browser, Midori
        GtkWidget *tree_swin;
        GtkWidget *desc_swin;
        GtkWidget *toolbar;
+       GtkWidget *paned;
+       GtkWidget *filter_hbox;
+       GtkWidget *filter_label;
        GtkToolItem *toolitem;
        SoupSession *session;
        CMData *cmdata;
@@ -595,6 +754,7 @@ static void cm_app_add_browser_cb(MidoriApp *app, MidoriBrowser *browser, Midori
        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);
@@ -607,12 +767,14 @@ static void cm_app_add_browser_cb(MidoriApp *app, MidoriBrowser *browser, Midori
        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);
@@ -624,7 +786,7 @@ static void cm_app_add_browser_cb(MidoriApp *app, MidoriBrowser *browser, Midori
        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_IN);
+       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);
 
@@ -638,10 +800,34 @@ static void cm_app_add_browser_cb(MidoriApp *app, MidoriBrowser *browser, Midori
        gtk_container_add(GTK_CONTAINER(tree_swin), cmdata->treeview);
     gtk_widget_show(tree_swin);
 
-       cmdata->panel_page = gtk_vpaned_new();
-       gtk_paned_pack1(GTK_PANED(cmdata->panel_page), tree_swin, TRUE, FALSE);
-       gtk_paned_pack2(GTK_PANED(cmdata->panel_page), desc_swin, FALSE, FALSE);
-    gtk_widget_show(cmdata->panel_page);
+       filter_label = gtk_label_new(_("Filter:"));
+       gtk_widget_show(filter_label);
+
+       cmdata->filter_entry = gtk_icon_entry_new();
+       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_released",
+               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, 3);
+       gtk_box_pack_start(GTK_BOX(filter_hbox), filter_label, FALSE, FALSE, 0);
+       gtk_box_pack_start(GTK_BOX(filter_hbox), cmdata->filter_entry, TRUE, TRUE, 0);
+    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, 3);
+       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();
@@ -698,7 +884,7 @@ MidoriExtension *extension_init(void)
                "name", _("Cookie Manager"),
                "description", _("List, view and delete cookies"),
                "version", "0.1",
-               "authors", "Enrico Tröger <enrico(at)xfce(dot)org>",
+               "authors", "Enrico Tröger <enrico(dot)troeger(at)uvena(dot)de>",
                NULL);
 
        g_signal_connect(extension, "activate", G_CALLBACK(cm_activate_cb), NULL);