Using extended attributes (xattr) to add some missing features to thunar
With some tiny changes to thunar/thunar-file.c and accepting a few custom xattrs, we can add some cool (missing) features to thunar. I went with the prefix user.xfce.
for these xattr names to avoid any potential future conflicts but the names are ultimately up to the developers. The good thing about using extended attributes is that you don't need to (and I'd argue shouldn't) add any new UI elements to thunar. These are mostly for people who know what they are doing and just need to override the default thunar file options. In some instances it can also remove the need for gvfs.
The things I could implement:
- user.xfce.icon_name: setting icon names for normal files/folders (not just .desktop files)
- user.xfce.emblems: setting any icon or picture as emblems without the need for gvfs
- user.xfce.mimetype: overriding a file's mimetype
- user.xfce.hide: hiding normal files (non-dot files) and showing the hidden files (dot-files)
- user.xfce.thumbnail (and user.xfce.thumbnail.<normal|large|xlarge|xxlarge>): overriding file thumbnails
Here's the updated thunar-file.c based on 775ab5b9. I have also pasted the git-diff here to show that it is absolutely minimal.
diff --git a/thunar/thunar-file.c b/thunar/thunar-file.c
index 4a4d6899..b4949600 100644
--- a/thunar/thunar-file.c
+++ b/thunar/thunar-file.c
@@ -50,6 +50,8 @@
#include <unistd.h>
#endif
+#include <sys/xattr.h>
+
#include <gio/gio.h>
#include <libxfce4ui/libxfce4ui.h>
#include <libxfce4util/libxfce4util.h>
@@ -117,7 +119,8 @@ static gboolean thunar_file_load (ThunarFile
static gboolean thunar_file_is_readable (const ThunarFile *file);
static gboolean thunar_file_same_filesystem (const ThunarFile *file_a,
const ThunarFile *file_b);
-
+static gchar *thunar_file_get_xattr_value (const ThunarFile *file,
+ const char *attr_name);
static GRecMutex G_LOCK_NAME (file_cache_mutex);
@@ -2451,9 +2454,17 @@ thunar_file_get_content_type (ThunarFile *file)
GFileInfo *info = NULL;
GError *err = NULL;
const gchar *content_type = NULL;
+ gchar *xattr_value;
_thunar_return_val_if_fail (THUNAR_IS_FILE (file), NULL);
+ xattr_value = thunar_file_get_xattr_value (file, "user.xfce.mimetype");
+ if (xattr_value)
+ {
+ file->content_type = xattr_value;
+ return file->content_type;
+ }
+
if (G_UNLIKELY (file->content_type == NULL))
{
G_LOCK (file_content_type_mutex);
@@ -3177,11 +3188,31 @@ thunar_file_is_writable (const ThunarFile *file)
gboolean
thunar_file_is_hidden (const ThunarFile *file)
{
+ gchar *xattr_value;
+
_thunar_return_val_if_fail (THUNAR_IS_FILE (file), FALSE);
if (file->info == NULL)
return FALSE;
+ xattr_value = thunar_file_get_xattr_value (file, "user.xfce.hide");
+ if (xattr_value)
+ {
+ switch (xattr_value[0])
+ {
+ case '1':
+ g_free(xattr_value);
+ return TRUE;
+
+ case '0':
+ g_free(xattr_value);
+ return FALSE;
+
+ default:
+ g_free(xattr_value);
+ }
+ }
+
return g_file_info_get_attribute_boolean (file->info, G_FILE_ATTRIBUTE_STANDARD_IS_HIDDEN)
|| g_file_info_get_attribute_boolean (file->info, G_FILE_ATTRIBUTE_STANDARD_IS_BACKUP);
}
@@ -3643,8 +3674,9 @@ GList*
thunar_file_get_emblem_names (ThunarFile *file)
{
guint32 uid;
- gchar **emblem_names;
+ gchar **emblem_names = NULL;
GList *emblems = NULL;
+ gchar *xattr_value;
_thunar_return_val_if_fail (THUNAR_IS_FILE (file), NULL);
@@ -3652,8 +3684,16 @@ thunar_file_get_emblem_names (ThunarFile *file)
if (file->info == NULL)
return NULL;
+ xattr_value = thunar_file_get_xattr_value (file, "user.xfce.emblems");
+ if (xattr_value)
+ {
+ emblem_names = g_strsplit (xattr_value, " ", -1);
+ g_free(xattr_value);
+ }
+
/* determine the custom emblems */
- emblem_names = g_file_info_get_attribute_stringv (file->info, "metadata::emblems");
+ if (!emblem_names)
+ emblem_names = g_file_info_get_attribute_stringv (file->info, "metadata::emblems");
if (G_UNLIKELY (emblem_names != NULL))
{
for (; *emblem_names != NULL; ++emblem_names)
@@ -3827,8 +3867,45 @@ thunar_file_is_desktop (const ThunarFile *file)
const gchar *
thunar_file_get_thumbnail_path (ThunarFile *file, ThunarThumbnailSize thumbnail_size)
{
+ gchar *xattr_value = NULL;
+ char *xattr_name;
+
_thunar_return_val_if_fail (THUNAR_IS_FILE (file), NULL);
+ switch (thumbnail_size)
+ {
+ case THUNAR_THUMBNAIL_SIZE_NORMAL:
+ xattr_name = "user.xfce.thumbnail.normal";
+ break;
+
+ case THUNAR_THUMBNAIL_SIZE_LARGE:
+ xattr_name = "user.xfce.thumbnail.large";
+ break;
+
+ case THUNAR_THUMBNAIL_SIZE_X_LARGE:
+ xattr_name = "user.xfce.thumbnail.xlarge";
+ break;
+
+ case THUNAR_THUMBNAIL_SIZE_XX_LARGE:
+ xattr_name = "user.xfce.thumbnail.xxlarge";
+ break;
+
+ default:
+ xattr_name = NULL;
+ }
+
+ if (xattr_name)
+ xattr_value = thunar_file_get_xattr_value (file, xattr_name);
+
+ if (!xattr_value)
+ xattr_value = thunar_file_get_xattr_value (file, "user.xfce.thumbnail");
+
+ if (xattr_value)
+ {
+ file->thumbnail_path = xattr_value;
+ return file->thumbnail_path;
+ }
+
/* if the thumbstate is known to be not there, return null */
if (thunar_file_get_thumb_state (file) == THUNAR_FILE_THUMB_STATE_NONE)
return NULL;
@@ -4063,10 +4140,23 @@ thunar_file_get_icon_name (ThunarFile *file,
guint i;
const gchar *special_dir;
GFileInfo *fileinfo;
+ gchar *xattr_value;
_thunar_return_val_if_fail (THUNAR_IS_FILE (file), NULL);
_thunar_return_val_if_fail (GTK_IS_ICON_THEME (icon_theme), NULL);
+ xattr_value = thunar_file_get_xattr_value (file, "user.xfce.icon_name");
+ if (xattr_value)
+ {
+ if (gtk_icon_theme_has_icon(icon_theme, xattr_value))
+ {
+ g_free(file->icon_name);
+ file->icon_name = xattr_value;
+ return thunar_file_get_icon_name_for_state (file->icon_name, icon_state);
+ }
+ g_free(xattr_value);
+ }
+
/* return cached name */
if (G_LIKELY (file->icon_name != NULL))
return thunar_file_get_icon_name_for_state (file->icon_name, icon_state);
@@ -4579,6 +4669,38 @@ thunar_file_same_filesystem (const ThunarFile *file_a,
+static gchar *
+thunar_file_get_xattr_value(const ThunarFile *file,
+ const char *attr_name)
+{
+ /* PATH_MAX should be enough for most attribute values */
+ char read_value[PATH_MAX];
+ ssize_t value_size;
+ char *path;
+ gchar *attr_value = NULL;
+
+ _thunar_return_val_if_fail (THUNAR_IS_FILE (file), NULL);
+
+ if (!file->gfile || !attr_name || !attr_name[0])
+ return NULL;
+
+ path = g_file_get_path (file->gfile);
+ if (!path)
+ return NULL;
+
+ value_size = getxattr (path, attr_name, read_value, PATH_MAX);
+ if (value_size > 0)
+ {
+ read_value[value_size] = 0;
+ attr_value = g_strdup(read_value);
+ }
+
+ g_free(path);
+ return attr_value;
+}
+
+
+
/**
* thunar_file_cache_lookup:
* @file : a #GFile.