added keyword tree filtering
authorVladimir Nadvornik <nadvornik@suse.cz>
Fri, 13 Mar 2009 16:45:21 +0000 (16:45 +0000)
committerVladimir Nadvornik <nadvornik@suse.cz>
Fri, 13 Mar 2009 16:45:21 +0000 (16:45 +0000)
src/bar_keywords.c
src/metadata.c
src/metadata.h

index 4411d25..5d25f42 100644 (file)
@@ -116,6 +116,10 @@ struct _PaneKeywordsData
 
        GtkTreePath *click_tpath;
 
+       gboolean expand_checked;
+       gboolean collapse_unchecked;
+       gboolean hide_unchecked;
+       
        FileData *fd;
        gchar *key;
 };
@@ -150,7 +154,7 @@ static void bar_pane_keywords_write(PaneKeywordsData *pkd)
        string_list_free(list);
 }
 
-gboolean bar_keyword_tree_expand_if_set(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
+gboolean bar_keyword_tree_expand_if_set_cb(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
 {
        PaneKeywordsData *pkd = data;
        gboolean set;
@@ -164,15 +168,41 @@ gboolean bar_keyword_tree_expand_if_set(GtkTreeModel *model, GtkTreePath *path,
        return FALSE;
 }
 
+gboolean bar_keyword_tree_collapse_if_unset_cb(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
+{
+       PaneKeywordsData *pkd = data;
+       gboolean set;
+       gboolean is_keyword;
+
+       gtk_tree_model_get(model, iter, FILTER_KEYWORD_COLUMN_TOGGLE, &set,
+                                       FILTER_KEYWORD_COLUMN_IS_KEYWORD, &is_keyword, -1);
+       
+       if (is_keyword && !set && gtk_tree_view_row_expanded(GTK_TREE_VIEW(pkd->keyword_treeview), path))
+               {
+               gtk_tree_view_collapse_row(GTK_TREE_VIEW(pkd->keyword_treeview), path);
+               }
+       return FALSE;
+}
+
 static void bar_keyword_tree_sync(PaneKeywordsData *pkd)
 {
-       GtkTreeModelFilter *store;
+       GtkTreeModel *model;
 
-       store = GTK_TREE_MODEL_FILTER(gtk_tree_view_get_model(GTK_TREE_VIEW(pkd->keyword_treeview)));
+       GtkTreeModel *keyword_tree;
+       GList *keywords;
 
-       gtk_tree_model_filter_refilter(store);
-       gtk_tree_model_foreach(GTK_TREE_MODEL(store), bar_keyword_tree_expand_if_set, pkd);
-       
+       model = gtk_tree_view_get_model(GTK_TREE_VIEW(pkd->keyword_treeview));
+       keyword_tree = gtk_tree_model_filter_get_model(GTK_TREE_MODEL_FILTER(model));
+
+       keywords = keyword_list_pull(pkd->keyword_view);
+       keyword_show_set_in(GTK_TREE_STORE(keyword_tree), model, keywords);
+       if (pkd->hide_unchecked) keyword_hide_unset_in(GTK_TREE_STORE(keyword_tree), model, keywords);
+       string_list_free(keywords);
+
+       gtk_tree_model_filter_refilter(GTK_TREE_MODEL_FILTER(model));
+
+       if (pkd->expand_checked) gtk_tree_model_foreach(model, bar_keyword_tree_expand_if_set_cb, pkd);
+       if (pkd->collapse_unchecked) gtk_tree_model_foreach(model, bar_keyword_tree_collapse_if_unset_cb, pkd);
 }
 
 static void bar_pane_keywords_keyword_update_all(void)
@@ -323,6 +353,13 @@ void bar_pane_keywords_filter_modify(GtkTreeModel *model, GtkTreeIter *iter, GVa
 
 }
 
+gboolean bar_pane_keywords_filter_visible(GtkTreeModel *keyword_tree, GtkTreeIter *iter, gpointer data)
+{
+       GtkTreeModel *filter = data;
+       
+       return !keyword_is_hidden_in(keyword_tree, iter, filter);
+}
+
 static void bar_pane_keywords_set_selection(PaneKeywordsData *pkd, gboolean append)
 {
        GList *keywords = NULL;
@@ -855,9 +892,103 @@ static void bar_pane_keywords_delete_cb(GtkWidget *menu_widget, gpointer data)
        keyword_delete(GTK_TREE_STORE(keyword_tree), &kw_iter);
 }
 
+static void bar_pane_keywords_hide_cb(GtkWidget *menu_widget, gpointer data)
+{
+       PaneKeywordsData *pkd = data;
+       GtkTreeModel *model;
+       GtkTreeIter iter;
+
+       GtkTreeModel *keyword_tree;
+       GtkTreeIter kw_iter;
+
+        if (!pkd->click_tpath) return;
+
+       model = gtk_tree_view_get_model(GTK_TREE_VIEW(pkd->keyword_treeview));
+       keyword_tree = gtk_tree_model_filter_get_model(GTK_TREE_MODEL_FILTER(model));
+
+        if (!gtk_tree_model_get_iter(model, &iter, pkd->click_tpath)) return;
+       gtk_tree_model_filter_convert_iter_to_child_iter(GTK_TREE_MODEL_FILTER(model), &kw_iter, &iter);
+       
+       keyword_hide_in(GTK_TREE_STORE(keyword_tree), &kw_iter, model);
+}
+
+static void bar_pane_keywords_show_all_cb(GtkWidget *menu_widget, gpointer data)
+{
+       PaneKeywordsData *pkd = data;
+       GtkTreeModel *model;
+
+       GtkTreeModel *keyword_tree;
+
+       pkd->hide_unchecked = FALSE;
+
+       model = gtk_tree_view_get_model(GTK_TREE_VIEW(pkd->keyword_treeview));
+       keyword_tree = gtk_tree_model_filter_get_model(GTK_TREE_MODEL_FILTER(model));
+
+       keyword_show_all_in(GTK_TREE_STORE(keyword_tree), model);
+       bar_keyword_tree_sync(pkd);
+}
+
+static void bar_pane_keywords_expand_checked_cb(GtkWidget *menu_widget, gpointer data)
+{
+       PaneKeywordsData *pkd = data;
+       GtkTreeModel *model;
+
+       model = gtk_tree_view_get_model(GTK_TREE_VIEW(pkd->keyword_treeview));
+       gtk_tree_model_foreach(model, bar_keyword_tree_expand_if_set_cb, pkd);
+}
+
+static void bar_pane_keywords_collapse_unchecked_cb(GtkWidget *menu_widget, gpointer data)
+{
+       PaneKeywordsData *pkd = data;
+       GtkTreeModel *model;
+
+       model = gtk_tree_view_get_model(GTK_TREE_VIEW(pkd->keyword_treeview));
+       gtk_tree_model_foreach(model, bar_keyword_tree_collapse_if_unset_cb, pkd);
+}
+
+static void bar_pane_keywords_hide_unchecked_cb(GtkWidget *menu_widget, gpointer data)
+{
+       PaneKeywordsData *pkd = data;
+       GtkTreeModel *model;
+
+       GtkTreeModel *keyword_tree;
+       GList *keywords;
+       
+       model = gtk_tree_view_get_model(GTK_TREE_VIEW(pkd->keyword_treeview));
+       keyword_tree = gtk_tree_model_filter_get_model(GTK_TREE_MODEL_FILTER(model));
+
+       keywords = keyword_list_pull(pkd->keyword_view);
+       keyword_hide_unset_in(GTK_TREE_STORE(keyword_tree), model, keywords);
+       string_list_free(keywords);
+       bar_keyword_tree_sync(pkd);
+}
+
+static void bar_pane_keywords_expand_checked_toggle_cb(GtkWidget *menu_widget, gpointer data)
+{
+       PaneKeywordsData *pkd = data;
+       pkd->expand_checked = !pkd->expand_checked;
+       bar_keyword_tree_sync(pkd);
+}
+
+static void bar_pane_keywords_collapse_unchecked_toggle_cb(GtkWidget *menu_widget, gpointer data)
+{
+       PaneKeywordsData *pkd = data;
+       pkd->collapse_unchecked = !pkd->collapse_unchecked;
+       bar_keyword_tree_sync(pkd);
+}
+
+static void bar_pane_keywords_hide_unchecked_toggle_cb(GtkWidget *menu_widget, gpointer data)
+{
+       PaneKeywordsData *pkd = data;
+       pkd->hide_unchecked = !pkd->hide_unchecked;
+       bar_keyword_tree_sync(pkd);
+}
+
 static void bar_pane_keywords_menu_popup(GtkWidget *widget, PaneKeywordsData *pkd, gint x, gint y)
 {
        GtkWidget *menu;
+       GtkWidget *item;
+       GtkWidget *submenu;
         GtkTreeViewDropPosition pos;
         
         if (pkd->click_tpath) gtk_tree_path_free(pkd->click_tpath);
@@ -869,8 +1000,6 @@ static void bar_pane_keywords_menu_popup(GtkWidget *widget, PaneKeywordsData *pk
        if (pkd->click_tpath)
                {
                /* for the entry */
-               GtkWidget *item;
-               GtkWidget *submenu;
                gchar *text;
                gchar *mark;
                gint i;
@@ -884,11 +1013,8 @@ static void bar_pane_keywords_menu_popup(GtkWidget *widget, PaneKeywordsData *pk
                gtk_tree_model_get(model, &iter, FILTER_KEYWORD_COLUMN_NAME, &name,
                                                 FILTER_KEYWORD_COLUMN_MARK, &mark, -1);
                
-               text = g_strdup_printf(_("Edit \"%s\""), name);
-               menu_item_add_stock(menu, text, GTK_STOCK_EDIT, G_CALLBACK(bar_pane_keywords_edit_dialog_cb), pkd);
-               g_free(text);
-               text = g_strdup_printf(_("Delete \"%s\""), name);
-               menu_item_add_stock(menu, text, GTK_STOCK_DELETE, G_CALLBACK(bar_pane_keywords_delete_cb), pkd);
+               text = g_strdup_printf(_("Hide \"%s\""), name);
+               menu_item_add_stock(menu, text, GTK_STOCK_EDIT, G_CALLBACK(bar_pane_keywords_hide_cb), pkd);
                g_free(text);
                
                submenu = gtk_menu_new();
@@ -899,11 +1025,20 @@ static void bar_pane_keywords_menu_popup(GtkWidget *widget, PaneKeywordsData *pk
                        g_object_set_data(G_OBJECT(item), "mark", GINT_TO_POINTER(i + 1));
                        g_free(text);
                        }
-
                text = g_strdup_printf(_("Connect \"%s\" to mark"), name);
                item = menu_item_add(menu, text, NULL, NULL);
                gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), submenu);
                g_free(text);
+
+               menu_item_add_divider(menu);
+
+               text = g_strdup_printf(_("Edit \"%s\""), name);
+               menu_item_add_stock(menu, text, GTK_STOCK_EDIT, G_CALLBACK(bar_pane_keywords_edit_dialog_cb), pkd);
+               g_free(text);
+               text = g_strdup_printf(_("Delete \"%s\""), name);
+               menu_item_add_stock(menu, text, GTK_STOCK_DELETE, G_CALLBACK(bar_pane_keywords_delete_cb), pkd);
+               g_free(text);
+
                
                if (mark && mark[0])
                        {
@@ -917,6 +1052,23 @@ static void bar_pane_keywords_menu_popup(GtkWidget *widget, PaneKeywordsData *pk
                g_free(name);
                }
        /* for the pane */
+
+
+       menu_item_add(menu, _("Expand checked"), G_CALLBACK(bar_pane_keywords_expand_checked_cb), pkd);
+       menu_item_add(menu, _("Collapse unchecked"), G_CALLBACK(bar_pane_keywords_collapse_unchecked_cb), pkd);
+       menu_item_add(menu, _("Hide unchecked"), G_CALLBACK(bar_pane_keywords_hide_unchecked_cb), pkd);
+       menu_item_add(menu, _("Show all"), G_CALLBACK(bar_pane_keywords_show_all_cb), pkd);
+
+       submenu = gtk_menu_new();
+       item = menu_item_add(menu, _("On any change"), NULL, NULL);
+       gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), submenu);
+
+       menu_item_add_check(submenu, _("Expand checked"), pkd->expand_checked, G_CALLBACK(bar_pane_keywords_expand_checked_toggle_cb), pkd);
+       menu_item_add_check(submenu, _("Collapse unchecked"), pkd->collapse_unchecked, G_CALLBACK(bar_pane_keywords_collapse_unchecked_toggle_cb), pkd);
+       menu_item_add_check(submenu, _("Hide unchecked"), pkd->hide_unchecked, G_CALLBACK(bar_pane_keywords_hide_unchecked_toggle_cb), pkd);
+
+       menu_item_add_divider(menu);
+
        menu_item_add_stock(menu, _("Add keyword"), GTK_STOCK_EDIT, G_CALLBACK(bar_pane_keywords_add_dialog_cb), pkd);
        
        gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, 0, GDK_CURRENT_TIME);
@@ -986,6 +1138,7 @@ GtkWidget *bar_pane_keywords_new(const gchar *title, const gchar *key, gboolean
 
        pkd->key = g_strdup(key);
        
+       pkd->expand_checked = TRUE;
 
        hbox = gtk_hbox_new(FALSE, PREF_PAD_GAP);
 
@@ -1030,6 +1183,10 @@ GtkWidget *bar_pane_keywords_new(const gchar *title, const gchar *key, gboolean
                                              bar_pane_keywords_filter_modify,
                                              pkd,
                                              NULL);
+       gtk_tree_model_filter_set_visible_func(GTK_TREE_MODEL_FILTER(store),
+                                              bar_pane_keywords_filter_visible,
+                                              store,
+                                              NULL);
 
        pkd->keyword_treeview = gtk_tree_view_new_with_model(store);
        g_object_unref(store);
index 4262c95..0c1f523 100644 (file)
@@ -771,6 +771,7 @@ void keyword_copy(GtkTreeStore *keyword_tree, GtkTreeIter *to, GtkTreeIter *from
        gchar *mark, *name, *casefold;
        gboolean is_keyword;
 
+       /* do not copy KEYWORD_COLUMN_HIDE_IN, it fully shows the new subtree */
        gtk_tree_model_get(GTK_TREE_MODEL(keyword_tree), from, KEYWORD_COLUMN_MARK, &mark,
                                                KEYWORD_COLUMN_NAME, &name,
                                                KEYWORD_COLUMN_CASEFOLD, &casefold,
@@ -805,7 +806,7 @@ void keyword_copy_recursive(GtkTreeStore *keyword_tree, GtkTreeIter *to, GtkTree
 void keyword_move_recursive(GtkTreeStore *keyword_tree, GtkTreeIter *to, GtkTreeIter *from)
 {
        keyword_copy_recursive(keyword_tree, to, from);
-       gtk_tree_store_remove(keyword_tree, from);
+       keyword_delete(keyword_tree, from);
 }
 
 GList *keyword_tree_get_path(GtkTreeModel *keyword_tree, GtkTreeIter *iter_ptr)
@@ -898,7 +899,7 @@ gboolean keyword_tree_is_set(GtkTreeModel *keyword_tree, GtkTreeIter *iter, GLis
                {
                const gchar *kw = work->data;
                work = work->next;
-               
+
                casefold_list = g_list_prepend(casefold_list, g_utf8_casefold(kw, -1));
                }
        
@@ -1010,15 +1011,122 @@ void keyword_tree_reset(GtkTreeModel *keyword_tree, GtkTreeIter *iter_ptr, GList
 
 void keyword_delete(GtkTreeStore *keyword_tree, GtkTreeIter *iter_ptr)
 {
+       GList *list;
+       GtkTreeIter child;
+       while (gtk_tree_model_iter_children(GTK_TREE_MODEL(keyword_tree), &child, iter_ptr))
+               {
+               keyword_delete(keyword_tree, &child);
+               }
+       
+       meta_data_connect_mark_with_keyword(GTK_TREE_MODEL(keyword_tree), iter_ptr, -1);
+
+       gtk_tree_model_get(GTK_TREE_MODEL(keyword_tree), iter_ptr, KEYWORD_COLUMN_HIDE_IN, &list, -1);
+       g_list_free(list);
+       
        gtk_tree_store_remove(keyword_tree, iter_ptr);
 }
 
 
+void keyword_hide_in(GtkTreeStore *keyword_tree, GtkTreeIter *iter, gpointer id)
+{
+       GList *list;
+       gtk_tree_model_get(GTK_TREE_MODEL(keyword_tree), iter, KEYWORD_COLUMN_HIDE_IN, &list, -1);
+       if (!g_list_find(list, id))
+               {
+               list = g_list_prepend(list, id);
+               gtk_tree_store_set(keyword_tree, iter, KEYWORD_COLUMN_HIDE_IN, list, -1);
+               }
+}
+
+void keyword_show_in(GtkTreeStore *keyword_tree, GtkTreeIter *iter, gpointer id)
+{
+       GList *list;
+       gtk_tree_model_get(GTK_TREE_MODEL(keyword_tree), iter, KEYWORD_COLUMN_HIDE_IN, &list, -1);
+       list = g_list_remove(list, id);
+       gtk_tree_store_set(keyword_tree, iter, KEYWORD_COLUMN_HIDE_IN, list, -1);
+}
+
+gboolean keyword_is_hidden_in(GtkTreeModel *keyword_tree, GtkTreeIter *iter, gpointer id)
+{
+       GList *list;
+       gtk_tree_model_get(keyword_tree, iter, KEYWORD_COLUMN_HIDE_IN, &list, -1);
+       return !!g_list_find(list, id);
+}
+
+static gboolean keyword_show_all_in_cb(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
+{
+       keyword_show_in(GTK_TREE_STORE(model), iter, data);
+       return FALSE;
+}
+
+void keyword_show_all_in(GtkTreeStore *keyword_tree, gpointer id)
+{
+       gtk_tree_model_foreach(GTK_TREE_MODEL(keyword_tree), keyword_show_all_in_cb, id);
+}
+
+static void keyword_hide_unset_in_recursive(GtkTreeStore *keyword_tree, GtkTreeIter *iter_ptr, gpointer id, GList *keywords)
+{
+       GtkTreeIter iter = *iter_ptr;
+       while (TRUE)
+               {
+               if (keyword_get_is_keyword(GTK_TREE_MODEL(keyword_tree), &iter) && 
+                   !keyword_tree_is_set(GTK_TREE_MODEL(keyword_tree), &iter, keywords))
+                       {
+                       keyword_hide_in(keyword_tree, &iter, id);
+                       /* no need to check children of hidden node */
+                       }
+               else
+                       {
+                       GtkTreeIter child;
+                       if (gtk_tree_model_iter_children(GTK_TREE_MODEL(keyword_tree), &child, &iter)) 
+                               {
+                               keyword_hide_unset_in_recursive(keyword_tree, &child, id, keywords);
+                               }
+                       }
+               if (!gtk_tree_model_iter_next(GTK_TREE_MODEL(keyword_tree), &iter)) return;
+               }
+}
+
+void keyword_hide_unset_in(GtkTreeStore *keyword_tree, gpointer id, GList *keywords)
+{
+       GtkTreeIter iter;
+       gtk_tree_model_get_iter_first(GTK_TREE_MODEL(keyword_tree), &iter);
+       keyword_hide_unset_in_recursive(keyword_tree, &iter, id, keywords);
+}
+
+static gboolean keyword_show_set_in_cb(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter_ptr, gpointer data)
+{
+       GtkTreeIter iter = *iter_ptr;
+       GList *keywords = data;
+       gpointer id = keywords->data;
+       keywords = keywords->next; /* hack */
+       if (keyword_tree_is_set(model, &iter, keywords))
+               {
+               while (TRUE)
+                       {
+                       GtkTreeIter parent;
+                       keyword_show_in(GTK_TREE_STORE(model), &iter, id);
+                       if (!gtk_tree_model_iter_parent(GTK_TREE_MODEL(keyword_tree), &parent, &iter)) break;
+                       iter = parent;
+                       }
+               }
+       return FALSE;
+}
+
+void keyword_show_set_in(GtkTreeStore *keyword_tree, gpointer id, GList *keywords)
+{
+       /* hack: pass id to keyword_hide_unset_in_cb in the list */
+       keywords = g_list_prepend(keywords, id);
+       gtk_tree_model_foreach(GTK_TREE_MODEL(keyword_tree), keyword_show_set_in_cb, keywords);
+       keywords = g_list_delete_link(keywords, keywords);
+}
+
+
 void keyword_tree_new(void)
 {
        if (keyword_tree) return;
        
-       keyword_tree = gtk_tree_store_new(KEYWORD_COLUMN_COUNT, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_BOOLEAN);
+       keyword_tree = gtk_tree_store_new(KEYWORD_COLUMN_COUNT, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_BOOLEAN, G_TYPE_POINTER);
 }
 
 
index 7af80a1..0e31eed 100644 (file)
@@ -46,6 +46,7 @@ enum {
        KEYWORD_COLUMN_NAME,
        KEYWORD_COLUMN_CASEFOLD,
        KEYWORD_COLUMN_IS_KEYWORD,
+       KEYWORD_COLUMN_HIDE_IN,
        KEYWORD_COLUMN_COUNT
 };
 
@@ -74,6 +75,14 @@ void keyword_tree_reset(GtkTreeModel *keyword_tree, GtkTreeIter *iter_ptr, GList
 
 void keyword_delete(GtkTreeStore *keyword_tree, GtkTreeIter *iter_ptr);
 
+
+void keyword_hide_in(GtkTreeStore *keyword_tree, GtkTreeIter *iter, gpointer id);
+void keyword_show_in(GtkTreeStore *keyword_tree, GtkTreeIter *iter, gpointer id);
+gboolean keyword_is_hidden_in(GtkTreeModel *keyword_tree, GtkTreeIter *iter, gpointer id);
+void keyword_show_all_in(GtkTreeStore *keyword_tree, gpointer id);
+void keyword_hide_unset_in(GtkTreeStore *keyword_tree, gpointer id, GList *keywords);
+void keyword_show_set_in(GtkTreeStore *keyword_tree, gpointer id, GList *keywords);
+
 void keyword_tree_new_default(void);
 void keyword_tree_new(void);