changeset 2855:009f0a811472

Fix #642: If a folder is a link, use an icon to indicate it is a link https://github.com/BestImageViewer/geeqie/issues/642 There is no stock icon for a folder as a link. The icon used is not particularly appropriate, but its meaning should be clear. The target path is shown as a tooltip.
author Colin Clark <colin.clark@cclark.uk>
date Sun, 21 Oct 2018 11:32:53 +0100
parents 3ccf51c15dba
children 8521d2405863
files src/typedefs.h src/view_dir.c src/view_dir.h src/view_dir_list.c src/view_dir_tree.c
diffstat 5 files changed, 136 insertions(+), 9 deletions(-) [+]
line wrap: on
line diff
--- a/src/typedefs.h	Sun Oct 21 10:48:33 2018 +0100
+++ b/src/typedefs.h	Sun Oct 21 11:32:53 2018 +0100
@@ -992,6 +992,7 @@
 	GdkPixbuf *open;
 	GdkPixbuf *deny;
 	GdkPixbuf *parent;
+	GdkPixbuf *link;
 };
 
 struct _SecureSaveInfo {
--- a/src/view_dir.c	Sun Oct 21 10:48:33 2018 +0100
+++ b/src/view_dir.c	Sun Oct 21 11:32:53 2018 +0100
@@ -49,6 +49,8 @@
 	pf->open   = gtk_widget_render_icon(widget, GTK_STOCK_OPEN, size, NULL);
 	pf->deny   = gtk_widget_render_icon(widget, GTK_STOCK_STOP, size, NULL);
 	pf->parent = gtk_widget_render_icon(widget, GTK_STOCK_GO_UP, size, NULL);
+	/* FIXME: this is not a suitable icon */
+	pf->link = gtk_widget_render_icon(widget, GTK_STOCK_REDO, size, NULL);
 #else
 	/* GQView legacy icons */
 	pf->close  = pixbuf_inline(PIXBUF_INLINE_FOLDER_CLOSED);
@@ -67,6 +69,7 @@
 	g_object_unref(pf->open);
 	g_object_unref(pf->deny);
 	g_object_unref(pf->parent);
+	g_object_unref(pf->link);
 
 	g_free(pf);
 }
--- a/src/view_dir.h	Sun Oct 21 10:48:33 2018 +0100
+++ b/src/view_dir.h	Sun Oct 21 11:32:53 2018 +0100
@@ -27,6 +27,7 @@
 	DIR_COLUMN_NAME,
 	DIR_COLUMN_COLOR,
 	DIR_COLUMN_DATE,
+	DIR_COLUMN_LINK,
 	DIR_COLUMN_COUNT
 };
 
--- a/src/view_dir_list.c	Sun Oct 21 10:48:33 2018 +0100
+++ b/src/view_dir_list.c	Sun Oct 21 11:32:53 2018 +0100
@@ -149,6 +149,7 @@
 	FileData *fd;
 	SortType sort_type = SORT_NAME;
 	gboolean sort_ascend = TRUE;
+	gchar *link;
 
 	old_list = VDLIST(vd)->list;
 
@@ -190,7 +191,11 @@
 
 		if (access_file(fd->path, R_OK | X_OK) && fd->name)
 			{
-			if (fd->name[0] == '.' && fd->name[1] == '\0')
+			if (islink(fd->path))
+				{
+				pixbuf = vd->pf->link;
+				}
+			else if (fd->name[0] == '.' && fd->name[1] == '\0')
 				{
 				pixbuf = vd->pf->open;
 				}
@@ -237,6 +242,15 @@
 				match = -1;
 				}
 
+			if (islink(fd->path))
+				{
+				link = realpath(fd->path, NULL);
+				}
+			else
+				{
+				link = NULL;
+				}
+
 			if (match < 0)
 				{
 				GtkTreeIter new;
@@ -254,6 +268,7 @@
 						   DIR_COLUMN_POINTER, fd,
 						   DIR_COLUMN_ICON, pixbuf,
 						   DIR_COLUMN_NAME, fd->name,
+						   DIR_COLUMN_LINK, link,
 						   DIR_COLUMN_DATE, date,
 						   -1);
 
@@ -268,6 +283,7 @@
 				gtk_list_store_set(store, &iter,
 						   DIR_COLUMN_ICON, pixbuf,
 						   DIR_COLUMN_NAME, fd->name,
+						   DIR_COLUMN_LINK, link,
 						   DIR_COLUMN_DATE, date,
 						   -1);
 
@@ -292,6 +308,7 @@
 	vd->drop_fd = NULL;
 
 	filelist_free(old_list);
+	g_free(link);
 	return ret;
 }
 
@@ -433,7 +450,7 @@
 
 	vd->type = DIRVIEW_LIST;
 
-	store = gtk_list_store_new(5, G_TYPE_POINTER, GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_BOOLEAN, G_TYPE_STRING);
+	store = gtk_list_store_new(6, G_TYPE_POINTER, GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_BOOLEAN, G_TYPE_STRING, G_TYPE_STRING);
 	vd->view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
 	g_object_unref(store);
 
@@ -463,6 +480,8 @@
 
 	gtk_tree_view_append_column(GTK_TREE_VIEW(vd->view), column);
 
+	gtk_tree_view_set_tooltip_column(GTK_TREE_VIEW(vd->view), DIR_COLUMN_LINK);
+
 	return vd;
 }
 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */
--- a/src/view_dir_tree.c	Sun Oct 21 10:48:33 2018 +0100
+++ b/src/view_dir_tree.c	Sun Oct 21 11:32:53 2018 +0100
@@ -133,6 +133,8 @@
 {
 	GtkTreeModel *store;
 	GtkTreePath *tpath;
+	NodeData *nd;
+	FileData *fd = NULL;
 
 	store = gtk_tree_view_get_model(GTK_TREE_VIEW(vd->view));
 	tpath = gtk_tree_model_get_path(store, iter);
@@ -143,7 +145,18 @@
 		   that the iter is populated */
 		g_signal_handlers_block_by_func(G_OBJECT(vd->view), vdtree_row_expanded, vd);
 		gtk_tree_view_expand_row(GTK_TREE_VIEW(vd->view), tpath, FALSE);
-		vdtree_icon_set_by_iter(vd, iter, vd->pf->open);
+		gtk_tree_model_get(store, iter, DIR_COLUMN_POINTER, &nd, -1);
+		fd = (nd) ? nd->fd : NULL;
+
+		if (fd && islink(fd->path))
+			{
+			vdtree_icon_set_by_iter(vd, iter, vd->pf->link);
+			}
+		else
+			{
+			vdtree_icon_set_by_iter(vd, iter, vd->pf->open);
+			}
+
 		g_signal_handlers_unblock_by_func(G_OBJECT(vd->view), vdtree_row_expanded, vd);
 		}
 	else
@@ -379,12 +392,20 @@
 	GdkPixbuf *pixbuf;
 	NodeData *end;
 	GtkTreeIter empty;
+	gchar *link = NULL;
 
 	if (!fd) return;
 
 	if (access_file(fd->path, R_OK | X_OK))
 		{
-		pixbuf = vd->pf->close;
+		if (islink(fd->path))
+			{
+			pixbuf = vd->pf->link;
+			}
+		else
+			{
+			pixbuf = vd->pf->close;
+			}
 		}
 	else
 		{
@@ -397,11 +418,21 @@
 	nd->expanded = FALSE;
 	nd->last_update = time(NULL);
 
+	if (islink(fd->path))
+		{
+		link = realpath(fd->path, NULL);
+		}
+	else
+		{
+		link = NULL;
+		}
+
 	store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vd->view)));
 	gtk_tree_store_append(store, &child, parent);
 	gtk_tree_store_set(store, &child, DIR_COLUMN_POINTER, nd,
 					 DIR_COLUMN_ICON, pixbuf,
 					 DIR_COLUMN_NAME, nd->fd->name,
+					 DIR_COLUMN_LINK, link,
 					 DIR_COLUMN_COLOR, FALSE, -1);
 
 	/* all nodes are created with an "empty" node, so that the expander is shown
@@ -429,6 +460,8 @@
 			}
 		gtk_tree_path_free(tpath);
 		}
+
+	g_free(link);
 }
 
 gboolean vdtree_populate_path_by_iter(ViewDir *vd, GtkTreeIter *iter, gboolean force, FileData *target_fd)
@@ -441,6 +474,7 @@
 	GtkTreeIter child;
 	NodeData *nd;
 	gboolean add_hidden = FALSE;
+	gchar *link = NULL;
 
 	store = gtk_tree_view_get_model(GTK_TREE_VIEW(vd->view));
 	gtk_tree_model_get(store, iter, DIR_COLUMN_POINTER, &nd, -1);
@@ -542,6 +576,18 @@
 					}
 
 				gtk_tree_store_set(GTK_TREE_STORE(store), &child, DIR_COLUMN_NAME, fd->name, -1);
+
+				if (islink(fd->path))
+					{
+					link = realpath(fd->path, NULL);
+					}
+				else
+					{
+					link = NULL;
+					}
+
+				gtk_tree_store_set(GTK_TREE_STORE(store), &child, DIR_COLUMN_LINK, link, -1);
+
 				cnd->version = fd->version;
 				old = g_list_remove(old, cnd);
 				file_data_unref(fd);
@@ -577,6 +623,8 @@
 	nd->expanded = TRUE;
 	nd->last_update = current_time;
 
+	g_free(link);
+
 	return TRUE;
 }
 
@@ -783,7 +831,15 @@
 			if (fd)
 				{
 				vdtree_populate_path_by_iter(vd, &iter, FALSE, vd->dir_fd);
-				vdtree_icon_set_by_iter(vd, &iter, vd->pf->open);
+
+				if (islink(fd->path))
+					{
+					vdtree_icon_set_by_iter(vd, &iter, vd->pf->link);
+					}
+				else
+					{
+					vdtree_icon_set_by_iter(vd, &iter, vd->pf->open);
+					}
 				}
 			break;
 		}
@@ -822,6 +878,7 @@
 	GtkTreeViewColumn *column;
 	GtkTreeIter iter;
 	NodeData *nd = NULL;
+	FileData *fd;
 
 	if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget), bevent->x, bevent->y,
 					  &tpath, &column, NULL, NULL))
@@ -846,7 +903,16 @@
 			    !gtk_tree_view_row_expanded(GTK_TREE_VIEW(vd->view), tpath))
 				{
 				vdtree_populate_path_by_iter(vd, &iter, FALSE, vd->dir_fd);
-				vdtree_icon_set_by_iter(vd, &iter, vd->pf->open);
+
+				fd = (nd) ? nd->fd : NULL;
+				if (fd && islink(fd->path))
+					{
+					vdtree_icon_set_by_iter(vd, &iter, vd->pf->link);
+					}
+				else
+					{
+					vdtree_icon_set_by_iter(vd, &iter, vd->pf->open);
+					}
 				}
 
 			gtk_tree_path_free(tpath);
@@ -872,16 +938,51 @@
 static void vdtree_row_expanded(GtkTreeView *treeview, GtkTreeIter *iter, GtkTreePath *tpath, gpointer data)
 {
 	ViewDir *vd = data;
+	GtkTreeModel *store;
+	NodeData *nd = NULL;
+	FileData *fd;
+
+	gtk_tree_view_set_tooltip_column(treeview, DIR_COLUMN_LINK);
 
 	vdtree_populate_path_by_iter(vd, iter, FALSE, NULL);
-	vdtree_icon_set_by_iter(vd, iter, vd->pf->open);
+	store = gtk_tree_view_get_model(GTK_TREE_VIEW(treeview));
+
+	gtk_tree_model_get_iter(store, iter, tpath);
+	gtk_tree_model_get(store, iter, DIR_COLUMN_POINTER, &nd, -1);
+
+	fd = (nd) ? nd->fd : NULL;
+	if (fd && islink(fd->path))
+		{
+		vdtree_icon_set_by_iter(vd, iter, vd->pf->link);
+		}
+	else
+		{
+		vdtree_icon_set_by_iter(vd, iter, vd->pf->open);
+		}
 }
 
 static void vdtree_row_collapsed(GtkTreeView *treeview, GtkTreeIter *iter, GtkTreePath *tpath, gpointer data)
 {
 	ViewDir *vd = data;
+	GtkTreeModel *store;
+	NodeData *nd = NULL;
+	FileData *fd;
 
-	vdtree_icon_set_by_iter(vd, iter, vd->pf->close);
+	vdtree_populate_path_by_iter(vd, iter, FALSE, NULL);
+	store = gtk_tree_view_get_model(GTK_TREE_VIEW(treeview));
+
+	gtk_tree_model_get_iter(store, iter, tpath);
+	gtk_tree_model_get(store, iter, DIR_COLUMN_POINTER, &nd, -1);
+
+	fd = (nd) ? nd->fd : NULL;
+	if (fd && islink(fd->path))
+		{
+		vdtree_icon_set_by_iter(vd, iter, vd->pf->link);
+		}
+	else
+		{
+		vdtree_icon_set_by_iter(vd, iter, vd->pf->close);
+		}
 }
 
 static gint vdtree_sort_cb(GtkTreeModel *store, GtkTreeIter *a, GtkTreeIter *b, gpointer data)
@@ -958,7 +1059,7 @@
 	vd->dnd_drop_leave_func = vdtree_dnd_drop_expand_cancel;
 	vd->dnd_drop_update_func = vdtree_dnd_drop_expand;
 
-	store = gtk_tree_store_new(4, G_TYPE_POINTER, GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_INT);
+	store = gtk_tree_store_new(6, G_TYPE_POINTER, GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_INT, G_TYPE_STRING, G_TYPE_STRING);
 	vd->view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
 	g_object_unref(store);
 
@@ -987,6 +1088,8 @@
 
 	gtk_tree_view_append_column(GTK_TREE_VIEW(vd->view), column);
 
+	gtk_tree_view_set_tooltip_column(GTK_TREE_VIEW(vd->view), DIR_COLUMN_LINK);
+
 	vdtree_setup_root(vd);
 
 	g_signal_connect(G_OBJECT(vd->view), "row_expanded",