changeset 1428:3019f5d3a3c7

added keyword tree filtering
author nadvornik
date Fri, 13 Mar 2009 16:45:21 +0000
parents c449a9344c8e
children 985366bccfb8
files src/bar_keywords.c src/metadata.c src/metadata.h
diffstat 3 files changed, 291 insertions(+), 17 deletions(-) [+]
line wrap: on
line diff
--- a/src/bar_keywords.c	Fri Mar 13 12:06:01 2009 +0000
+++ b/src/bar_keywords.c	Fri Mar 13 16:45:21 2009 +0000
@@ -116,6 +116,10 @@
 
 	GtkTreePath *click_tpath;
 
+	gboolean expand_checked;
+	gboolean collapse_unchecked;
+	gboolean hide_unchecked;
+	
 	FileData *fd;
 	gchar *key;
 };
@@ -150,7 +154,7 @@
 	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 @@
 	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;
+
+	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));
 
-	gtk_tree_model_filter_refilter(store);
-	gtk_tree_model_foreach(GTK_TREE_MODEL(store), bar_keyword_tree_expand_if_set, pkd);
-	
+	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 @@
 
 }
 
+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 @@
 	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 @@
 	if (pkd->click_tpath)
 		{
 		/* for the entry */
-		GtkWidget *item;
-		GtkWidget *submenu;
 		gchar *text;
 		gchar *mark;
 		gint i;
@@ -884,11 +1013,8 @@
 		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 @@
 			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 @@
 		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 @@
 
 	pkd->key = g_strdup(key);
 	
+	pkd->expand_checked = TRUE;
 
 	hbox = gtk_hbox_new(FALSE, PREF_PAD_GAP);
 
@@ -1030,6 +1183,10 @@
 					      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);
--- a/src/metadata.c	Fri Mar 13 12:06:01 2009 +0000
+++ b/src/metadata.c	Fri Mar 13 16:45:21 2009 +0000
@@ -771,6 +771,7 @@
 	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_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 @@
 		{
 		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_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);
 }
 
 
--- a/src/metadata.h	Fri Mar 13 12:06:01 2009 +0000
+++ b/src/metadata.h	Fri Mar 13 16:45:21 2009 +0000
@@ -46,6 +46,7 @@
 	KEYWORD_COLUMN_NAME,
 	KEYWORD_COLUMN_CASEFOLD,
 	KEYWORD_COLUMN_IS_KEYWORD,
+	KEYWORD_COLUMN_HIDE_IN,
 	KEYWORD_COLUMN_COUNT
 };
 
@@ -74,6 +75,14 @@
 
 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);