/*
* Copyright (C) 2010 ammonkey
*
* This library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License
* version 3.0 as published by the Free Software Foundation, Inc.,.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License version 3.0 for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see
* .
*
* Author: ammonkey
*/
#include "gof-file.h"
#include
#include
#include
#include "eel-i18n.h"
#include "eel-fcts.h"
#include "eel-string.h"
#include "eel-gio-extensions.h"
#include "eel-string.h"
#include "marlin-exec.h"
#include "marlin-icons.h"
#include "fm-list-model.h"
#include "pantheon-files-core.h"
G_LOCK_DEFINE_STATIC (file_cache_mutex);
static GHashTable *file_cache;
//static void gof_file_get_property (GObject * object, guint property_id, GValue * value, GParamSpec * pspec);
//static void gof_file_set_property (GObject * object, guint property_id, const GValue * value, GParamSpec * pspec);
G_DEFINE_TYPE (GOFFile, gof_file, G_TYPE_OBJECT)
#define SORT_LAST_CHAR1 '.'
#define SORT_LAST_CHAR2 '#'
#define ICON_NAME_THUMBNAIL_LOADING "image-loading"
enum {
CHANGED,
//UPDATED_DEEP_COUNT_IN_PROGRESS,
INFO_AVAILABLE,
ICON_CHANGED,
DESTROY,
LAST_SIGNAL
};
static guint signals[LAST_SIGNAL];
/*struct _GOFFilePrivate {
};*/
static guint32 effective_user_id;
static gpointer _g_object_ref0 (gpointer self) {
return self ? g_object_ref (self) : NULL;
}
const gchar *gof_file_get_thumbnail_path (GOFFile *file);
static GIcon *
get_icon_user_special_dirs(char *path)
{
GIcon *icon = NULL;
if (!path)
return NULL;
if (g_strcmp0 (path, g_get_home_dir ()) == 0)
icon = g_themed_icon_new ("user-home");
else if (g_strcmp0 (path, g_get_user_special_dir (G_USER_DIRECTORY_DESKTOP)) == 0)
icon = g_themed_icon_new ("user-desktop");
else if (g_strcmp0 (path, g_get_user_special_dir (G_USER_DIRECTORY_DOCUMENTS)) == 0)
icon = g_themed_icon_new_with_default_fallbacks ("folder-documents");
else if (g_strcmp0 (path, g_get_user_special_dir (G_USER_DIRECTORY_DOWNLOAD)) == 0)
icon = g_themed_icon_new_with_default_fallbacks ("folder-download");
else if (g_strcmp0 (path, g_get_user_special_dir (G_USER_DIRECTORY_MUSIC)) == 0)
icon = g_themed_icon_new_with_default_fallbacks ("folder-music");
else if (g_strcmp0 (path, g_get_user_special_dir (G_USER_DIRECTORY_PICTURES)) == 0)
icon = g_themed_icon_new_with_default_fallbacks ("folder-pictures");
else if (g_strcmp0 (path, g_get_user_special_dir (G_USER_DIRECTORY_PUBLIC_SHARE)) == 0)
icon = g_themed_icon_new_with_default_fallbacks ("folder-publicshare");
else if (g_strcmp0 (path, g_get_user_special_dir (G_USER_DIRECTORY_TEMPLATES)) == 0)
icon = g_themed_icon_new_with_default_fallbacks ("folder-templates");
else if (g_strcmp0 (path, g_get_user_special_dir (G_USER_DIRECTORY_VIDEOS)) == 0)
icon = g_themed_icon_new_with_default_fallbacks ("folder-videos");
return (icon);
}
GOFFile *
gof_file_new (GFile *location, GFile *dir)
{
GOFFile *file;
file = (GOFFile*) g_object_new (GOF_TYPE_FILE, NULL);
file->location = g_object_ref (location);
file->uri = g_file_get_uri (location);
if (dir != NULL)
file->directory = g_object_ref (dir);
else
file->directory = NULL;
file->basename = g_file_get_basename (file->location);
//file->parent_dir = g_file_enumerator_get_container (enumerator);
//g_debug ("%s: create %p", __func__, file);
return (file);
}
#if 0
void gof_file_changed (GOFFile *file)
{
GOFDirectoryAsync *dir;
/* get the DirectoryAsync associated to the file */
dir = gof_directory_async_cache_lookup (file->directory);
if (dir != NULL) {
if (!file->is_hidden || dir->show_hidden_files)
g_signal_emit_by_name (dir, "file_changed", file);
g_object_unref (dir);
}
g_signal_emit_by_name (file, "changed");
}
#endif
void
gof_file_icon_changed (GOFFile *file)
{
GOFDirectoryAsync *dir = NULL;
/* get the DirectoryAsync associated to the file */
if (file->directory != NULL) {
dir = gof_directory_async_cache_lookup (file->directory);
if (dir != NULL) {
if (!file->is_hidden || gof_preferences_get_show_hidden_files (gof_preferences_get_default ())) {
g_signal_emit_by_name (dir, "icon_changed", file);
}
g_object_unref (dir);
}
}
g_signal_emit_by_name (file, "icon_changed");
}
static void
gof_file_clear_info (GOFFile *file)
{
g_return_if_fail (file != NULL);
_g_object_unref0 (file->target_location);
_g_object_unref0 (file->mount);
_g_free0(file->utf8_collation_key);
_g_free0(file->formated_type);
_g_free0(file->format_size);
_g_free0(file->formated_modified);
_g_object_unref0 (file->icon);
_g_free0 (file->custom_display_name);
_g_free0 (file->custom_icon_name);
file->uid = -1;
file->gid = -1;
file->has_permissions = FALSE;
file->permissions = 0;
_g_free0(file->owner);
_g_free0(file->group);
file->can_unmount = FALSE;
}
/**
* gof_file_is_location_uri_default:
*
* example: afp://server.local:123/)
*
* Returns: TRUE if it is an URI at "/"; FALSE otherwise.
**/
static gboolean
gof_file_is_location_uri_default (GOFFile *file)
{
g_return_val_if_fail (file->info != NULL, FALSE);
gboolean res;
const char *target_uri = g_file_info_get_attribute_string (file->info, G_FILE_ATTRIBUTE_STANDARD_TARGET_URI);
if (target_uri == NULL)
target_uri = file->uri;
gchar **split = g_strsplit (target_uri, "/", 4);
res = (split[3] == NULL || !strcmp (split[3], ""));
g_strfreev (split);
return res;
}
gboolean
gof_file_is_mountable (GOFFile *file) {
g_return_val_if_fail (file->info != NULL, FALSE);
return g_file_info_get_file_type(file->info) == G_FILE_TYPE_MOUNTABLE;
}
guint
get_number_of_uri_parts (GOFFile *file) {
const char *target_uri = NULL;
if (file->info != NULL)
target_uri = g_file_info_get_attribute_string (file->info, G_FILE_ATTRIBUTE_STANDARD_TARGET_URI);
if (target_uri == NULL)
target_uri = file->uri;
gchar **split = g_strsplit (target_uri, "/", 6);
guint i, count;
count = 0;
for (i = 0; i < g_strv_length (split); i++) {
if (split [i][0] != NULL) {
count++;
}
}
g_strfreev (split);
return count;
}
gboolean
gof_file_is_smb_share (GOFFile *file)
{
gboolean res;
res = FALSE;
if (gof_file_is_smb_uri_scheme (file) || gof_file_is_network_uri_scheme (file)) {
res = get_number_of_uri_parts (file) == 3;
}
return res;
}
gboolean
gof_file_is_smb_server (GOFFile *file)
{
gboolean res;
res = FALSE;
if (gof_file_is_smb_uri_scheme (file) || gof_file_is_network_uri_scheme (file)){
res = get_number_of_uri_parts (file) == 2;
}
return res;
}
gboolean
gof_file_is_remote_uri_scheme (GOFFile *file)
{
if (gof_file_is_root_network_folder (file) || gof_file_is_other_uri_scheme (file))
return TRUE;
}
gboolean
gof_file_is_root_network_folder (GOFFile *file)
{
return (gof_file_is_network_uri_scheme (file) || gof_file_is_smb_server (file));
}
gboolean
gof_file_is_network_uri_scheme (GOFFile *file)
{
if (!G_IS_FILE (file->location))
return TRUE;
return g_file_has_uri_scheme (file->location, "network");
}
gboolean
gof_file_is_smb_uri_scheme (GOFFile *file)
{
if (!G_IS_FILE (file->location))
return TRUE;
return g_file_has_uri_scheme (file->location, "smb");
}
gboolean
gof_file_is_recent_uri_scheme (GOFFile *file)
{
if (!G_IS_FILE (file->location))
return TRUE;
return g_file_has_uri_scheme (file->location, "recent");
}
gboolean
gof_file_is_other_uri_scheme (GOFFile *file)
{
GFile *loc = file->location;
if (!G_IS_FILE (file->location))
return TRUE;
gboolean res;
res = g_file_has_uri_scheme (loc, "ftp") ||
g_file_has_uri_scheme (loc, "sftp") ||
g_file_has_uri_scheme (loc, "afp") ||
g_file_has_uri_scheme (loc, "dav") ||
g_file_has_uri_scheme (loc, "davs");
return res;
}
void
gof_file_get_folder_icon_from_uri_or_path (GOFFile *file)
{
if (file->icon != NULL)
return;
if (!file->is_hidden && file->uri != NULL) {
char *path = g_filename_from_uri (file->uri, NULL, NULL);
file->icon = get_icon_user_special_dirs(path);
_g_free0 (path);
}
if (file->icon == NULL && !g_file_is_native (file->location)
&& gof_file_is_remote_uri_scheme (file))
file->icon = g_themed_icon_new (MARLIN_ICON_FOLDER_REMOTE);
if (file->icon == NULL)
file->icon = g_themed_icon_new (MARLIN_ICON_FOLDER);
}
static void
gof_file_target_location_update (GOFFile *file)
{
if (file->target_location == NULL)
return;
/*GOFFile *gof = gof_file_get (file->target_location);
gof_file_query_update (gof);
file->is_directory = gof->is_directory;
file->ftype = gof->ftype;*/
file->target_gof = gof_file_get (file->target_location);
/* TODO make async */
gof_file_query_update (file->target_gof);
}
static void
gof_file_update_size (GOFFile *file)
{
g_free (file->format_size);
if (gof_file_is_folder (file) || gof_file_is_root_network_folder (file)) {
file->format_size = g_strdup ("—");
} else if (g_file_info_has_attribute (file->info, G_FILE_ATTRIBUTE_STANDARD_SIZE)) {
file->format_size = g_format_size (file->size);
} else {
file->format_size = g_strdup (_("Inaccessible"));
}
}
static void
gof_file_update_formated_type (GOFFile *file)
{
gchar *formated_type = NULL;
_g_free0 (file->formated_type);
const gchar *ftype = gof_file_get_ftype (file);
/* Do not interpret desktop files (lp:1660742) */
if (ftype != NULL) {
formated_type = g_content_type_get_description (ftype);
if (G_UNLIKELY (gof_file_is_symlink (file))) {
file->formated_type = g_strdup_printf (_("link to %s"), formated_type);
} else {
file->formated_type = g_strdup (formated_type);
}
} else {
file->formated_type = g_strdup ("");
}
g_free (formated_type);
}
static void
gof_file_update_icon_internal (GOFFile *file, gint size);
void
gof_file_update_type (GOFFile *file)
{
const gchar *ftype = gof_file_get_ftype (file);
gof_file_update_formated_type (file);
/* update icon */
file->icon = g_content_type_get_icon (ftype);
if (file->pix_size > 1)
gof_file_update_icon_internal (file, file->pix_size);
gof_file_icon_changed (file);
}
void
gof_file_update (GOFFile *file)
{
GKeyFile *key_file;
gchar *p;
g_return_if_fail (file->info != NULL);
/* free previously allocated */
gof_file_clear_info (file);
file->is_hidden = g_file_info_get_is_hidden (file->info) || g_file_info_get_is_backup (file->info);
file->size = (guint64) g_file_info_get_size (file->info);
file->file_type = g_file_info_get_file_type (file->info);
file->is_directory = (file->file_type == G_FILE_TYPE_DIRECTORY);
file->modified = g_file_info_get_attribute_uint64 (file->info, G_FILE_ATTRIBUTE_TIME_MODIFIED);
/* metadata */
if (file->is_directory) {
if (g_file_info_has_attribute (file->info, "metadata::marlin-sort-column-id"))
file->sort_column_id = fm_list_model_get_column_id_from_string (g_file_info_get_attribute_string (file->info, "metadata::marlin-sort-column-id"));
if (g_file_info_has_attribute (file->info, "metadata::marlin-sort-reversed"))
file->sort_order = !g_strcmp0 (g_file_info_get_attribute_string (file->info, "metadata::marlin-sort-reversed"), "true") ? GTK_SORT_DESCENDING : GTK_SORT_ASCENDING;
}
if (g_file_info_has_attribute (file->info, G_FILE_ATTRIBUTE_STANDARD_ICON)) {
file->icon = (GIcon *) g_file_info_get_attribute_object (file->info, G_FILE_ATTRIBUTE_STANDARD_ICON);
g_object_ref (file->icon);
}
/* Any location or target on a mount will now have the file->mount and file->is_mounted set */
const char *target_uri = g_file_info_get_attribute_string (file->info, G_FILE_ATTRIBUTE_STANDARD_TARGET_URI);
if (target_uri != NULL) {
file->target_location = g_file_new_for_uri (target_uri);
gof_file_target_location_update (file);
file->mount = g_file_find_enclosing_mount (file->target_location, NULL, NULL);
file->is_mounted = (file->mount != NULL);
} else {
file->mount = g_file_find_enclosing_mount (file->location, NULL, NULL);
file->is_mounted = (file->mount != NULL);
}
/* TODO the key-files could be loaded async.
The performance gain would not be that great*/
if ((file->is_desktop = gof_file_is_desktop_file (file)))
{
/* The following code snippet about desktop files come from Thunar thunar-file.c,
* Copyright (c) 2005-2007 Benedikt Meurer
* Copyright (c) 2009-2011 Jannis Pohlmann
*/
/* determine the custom icon and display name for .desktop files */
/* query a key file for the .desktop file */
//TODO make cancellable & error
key_file = eel_g_file_query_key_file (file->location, NULL, NULL);
if (key_file != NULL)
{
/* read the icon name from the .desktop file */
file->custom_icon_name = g_key_file_get_string (key_file,
G_KEY_FILE_DESKTOP_GROUP,
G_KEY_FILE_DESKTOP_KEY_ICON,
NULL);
if (G_UNLIKELY (eel_str_is_empty (file->custom_icon_name)))
{
/* make sure we set null if the string is empty else the assertion in
* thunar_icon_factory_lookup_icon() will fail */
_g_free0 (file->custom_icon_name);
file->custom_icon_name = NULL;
}
else
{
/* drop any suffix (e.g. '.png') from themed icons */
if (!g_path_is_absolute (file->custom_icon_name))
{
p = strrchr (file->custom_icon_name, '.');
if (p != NULL)
*p = '\0';
}
}
/* Do not show name from desktop file as this can be used as an exploit (lp:1660742) */
/* check if we have a target location */
gchar *url;
gchar *type;
type = g_key_file_get_string (key_file, G_KEY_FILE_DESKTOP_GROUP,
G_KEY_FILE_DESKTOP_KEY_TYPE, NULL);
if (eel_str_is_equal (type, "Link"))
{
url = g_key_file_get_string (key_file, G_KEY_FILE_DESKTOP_GROUP,
G_KEY_FILE_DESKTOP_KEY_URL, NULL);
if (G_LIKELY (url != NULL))
{
g_debug ("%s .desktop Link %s\n", G_STRFUNC, url);
file->target_location = g_file_new_for_uri (url);
gof_file_target_location_update (file);
g_free (url);
}
}
_g_free0 (type);
/* free the key file */
g_key_file_free (key_file);
}
}
if (file->custom_display_name == NULL) {
/* Use custom_display_name to store default display name if there is no custom name */
if (file->info && g_file_info_get_display_name (file->info) != NULL) {
if (file->directory != NULL &&
strcmp (g_file_get_uri_scheme (file->directory), "network") == 0 &&
!(strcmp (g_file_get_uri (file->target_location), "smb:///") == 0)) {
/* Show protocol after server name (lp:1184606) */
file->custom_display_name = g_strdup_printf ("%s (%s)", g_file_info_get_display_name (file->info),
g_utf8_strup (g_file_get_uri_scheme (file->target_location), -1));
} else {
file->custom_display_name = g_strdup (g_file_info_get_display_name (file->info));
}
}
}
/* sizes */
gof_file_update_size (file);
/* modified date */
if (g_file_info_has_attribute (file->info, G_FILE_ATTRIBUTE_TIME_MODIFIED)) {
file->formated_modified = gof_file_get_formated_time (file, G_FILE_ATTRIBUTE_TIME_MODIFIED);
} else {
file->formated_modified = g_strdup (_("Inaccessible"));
}
/* icon */
if (file->is_directory) {
gof_file_get_folder_icon_from_uri_or_path (file);
} else if (g_file_info_get_file_type(file->info) == G_FILE_TYPE_MOUNTABLE) {
file->icon = g_themed_icon_new_with_default_fallbacks ("folder-remote");
} else {
const gchar *ftype = gof_file_get_ftype (file);
if (ftype != NULL && file->icon == NULL)
file->icon = g_content_type_get_icon (ftype);
}
file->utf8_collation_key = g_utf8_collate_key_for_filename (gof_file_get_display_name (file), -1);
/* mark the thumb flags as state none, we'll load the thumbs once the directory
* would be loaded on a thread */
if (gof_file_get_thumbnail_path (file) != NULL) {
file->flags = GOF_FILE_THUMB_STATE_UNKNOWN; /* UNKNOWN means thumbnail not known to be unobtainable */
}
/* formated type */
gof_file_update_formated_type (file);
/* permissions */
file->has_permissions = g_file_info_has_attribute (file->info, G_FILE_ATTRIBUTE_UNIX_MODE);
file->permissions = g_file_info_get_attribute_uint32 (file->info, G_FILE_ATTRIBUTE_UNIX_MODE);
const char *owner = g_file_info_get_attribute_string (file->info, G_FILE_ATTRIBUTE_OWNER_USER);
const char *group = g_file_info_get_attribute_string (file->info, G_FILE_ATTRIBUTE_OWNER_GROUP);
if (owner != NULL)
file->owner = strdup (owner);
if (group != NULL)
file->group = strdup (group);
if (g_file_info_has_attribute (file->info, G_FILE_ATTRIBUTE_UNIX_UID)) {
file->uid = g_file_info_get_attribute_uint32 (file->info, G_FILE_ATTRIBUTE_UNIX_UID);
if (file->owner == NULL) {
file->owner = g_strdup_printf ("%d", file->uid);
}
} else if (file->owner != NULL) { /* e.g. ftp info yields owner but not uid */
file->uid = atoi (file->owner);
}
if (g_file_info_has_attribute (file->info, G_FILE_ATTRIBUTE_UNIX_GID)) {
file->gid = g_file_info_get_attribute_uint32 (file->info, G_FILE_ATTRIBUTE_UNIX_GID);
if (file->group == NULL) {
file->group = g_strdup_printf ("%d", file->gid);
}
} else if (file->group != NULL) { /* e.g. ftp info yields owner but not uid */
file->gid = atoi (file->group);
}
if (g_file_info_has_attribute (file->info, G_FILE_ATTRIBUTE_MOUNTABLE_CAN_UNMOUNT))
file->can_unmount = g_file_info_get_attribute_boolean (file->info, G_FILE_ATTRIBUTE_MOUNTABLE_CAN_UNMOUNT);
gof_file_update_trash_info (file);
gof_file_update_emblem (file);
}
static MarlinIconInfo *
gof_file_get_special_icon (GOFFile *file, int size, GOFFileIconFlags flags)
{
g_return_val_if_fail (size >= 1, NULL);
if (file->custom_icon_name != NULL) {
if (g_path_is_absolute (file->custom_icon_name))
return marlin_icon_info_lookup_from_path (file->custom_icon_name, size);
else
return marlin_icon_info_lookup_from_name (file->custom_icon_name, size);
}
if (flags & GOF_FILE_ICON_FLAGS_USE_THUMBNAILS
&& file->flags == GOF_FILE_THUMB_STATE_READY) {
const gchar *thumb_path = gof_file_get_thumbnail_path (file);
/* TODO thumb test : Playing with the thumbs */
//if (file->flags != 0 && thumb_path != NULL) {
if (thumb_path != NULL) {
//g_message ("show thumb %s %s %d\n", file->uri, thumb_path, size);
//return marlin_icon_info_lookup_from_path (thumb_path, size * 1.33);
return marlin_icon_info_lookup_from_path (thumb_path, size);
}
}
return NULL;
}
MarlinIconInfo *
gof_file_get_icon (GOFFile *file, int size, GOFFileIconFlags flags)
{
MarlinIconInfo *icon = NULL;
GIcon *gicon;
g_return_val_if_fail (file, NULL);
g_return_val_if_fail (size >= 1, NULL);
icon = gof_file_get_special_icon (file, size, flags);
if (icon != NULL && !marlin_icon_info_is_fallback (icon))
return icon;
_g_object_unref0 (icon);
if (flags & GOF_FILE_ICON_FLAGS_USE_THUMBNAILS
&& file->flags == GOF_FILE_THUMB_STATE_LOADING) {
gicon = g_themed_icon_new (ICON_NAME_THUMBNAIL_LOADING);
} else {
gicon = _g_object_ref0 (file->icon);
}
if (gicon != NULL) {
icon = marlin_icon_info_lookup (gicon, size);
if (icon != NULL && marlin_icon_info_is_fallback(icon)) {
g_object_unref (icon);
icon = marlin_icon_info_get_generic_icon (size);
}
g_object_unref (gicon);
} else {
icon = marlin_icon_info_get_generic_icon (size);
}
return icon;
}
#if 0
static GdkPixbuf
*ensure_pixbuf_from_nicon (GOFFile *file, gint size, gboolean force_size, MarlinIconInfo *nicon)
{
GdkPixbuf *pix;
MarlinIconInfo *temp_nicon;
g_return_val_if_fail (size >= 1, NULL);
pix = marlin_icon_info_get_pixbuf_force_size (nicon, size, force_size);
if (pix == NULL) {
temp_nicon = gof_file_get_icon (file, size, GOF_FILE_ICON_FLAGS_USE_THUMBNAILS);
pix = marlin_icon_info_get_pixbuf_force_size (temp_nicon, size, force_size);
if (temp_nicon)
g_object_unref (temp_nicon);
}
return pix;
}
#endif
GdkPixbuf *
gof_file_get_icon_pixbuf (GOFFile *file, gint size, gboolean force_size, GOFFileIconFlags flags)
{
MarlinIconInfo *nicon;
GdkPixbuf *pix;
g_return_val_if_fail (size >= 1, NULL);
nicon = gof_file_get_icon (file, size, flags);
pix = marlin_icon_info_get_pixbuf_force_size (nicon, size, force_size);
if (nicon) {
g_object_unref (nicon);
}
return pix;
}
static void
gof_file_update_icon_internal (GOFFile *file, gint size)
{
g_return_if_fail (size >= 1);
/* destroy pixbuff if already present */
_g_object_unref0 (file->pix);
/* make sure we always got a non null pixbuf of the specified size */
file->pix = gof_file_get_icon_pixbuf (file, size,
gof_preferences_get_force_icon_size (gof_preferences_get_default ()),
GOF_FILE_ICON_FLAGS_USE_THUMBNAILS);
file->pix_size = size;
}
/* This function is used by the icon renderer and fm-list-model.
* Store the pixbuf and update it only for size change.
*/
void gof_file_update_icon (GOFFile *file, gint size)
{
if (size <= 1)
return;
if (!(file->pix == NULL || file->pix_size != size))
return;
gof_file_update_icon_internal (file, size);
}
void gof_file_update_desktop_file (GOFFile *file)
{
g_free (file->utf8_collation_key);
file->utf8_collation_key = g_utf8_collate_key_for_filename (gof_file_get_display_name (file), -1);
gof_file_update_formated_type (file);
gof_file_update_size (file);
gof_file_icon_changed (file);
}
void gof_file_update_emblem (GOFFile *file)
{
/* Do not try to add emblems to network and remote files (except smb) - can cause blocking io*/
if (gof_file_is_other_uri_scheme (file) || gof_file_is_network_uri_scheme (file))
return;
/* Do not try to add emblems to smb shares either */
if (gof_file_is_smb_share (file))
return;
/* erase previous stored emblems */
if (file->emblems_list != NULL) {
g_list_free (file->emblems_list);
file->emblems_list = NULL;
}
if(plugins != NULL)
marlin_plugin_manager_update_file_info (plugins, file);
if(gof_file_is_symlink(file) || (file->is_desktop && file->target_gof))
{
gof_file_add_emblem(file, "emblem-symbolic-link");
/* testing up to 4 emblems */
/*gof_file_add_emblem(file, "emblem-generic");
gof_file_add_emblem(file, "emblem-important");
gof_file_add_emblem(file, "emblem-favorite");*/
}
/* We hide lock emblems if in Recents, because files here are not real files and emblems would always shown. */
if (!gof_file_is_writable (file) && !g_file_has_uri_scheme (file->location, "recent")) {
if (gof_file_is_readable (file))
gof_file_add_emblem (file, "emblem-readonly");
else
gof_file_add_emblem (file, "emblem-unreadable");
}
/* TODO update signal on real change */
//g_warning ("update emblem %s", file.uri);
if (file->emblems_list != NULL)
gof_file_icon_changed (file);
}
void gof_file_add_emblem (GOFFile* file, const gchar* emblem)
{
GList* emblems = g_list_first(file->emblems_list);
while(emblems != NULL)
{
if(!g_strcmp0(emblems->data, emblem))
return;
emblems = g_list_next(emblems);
}
file->emblems_list = g_list_append(file->emblems_list, (void*)emblem);
gof_file_icon_changed (file);
}
static void
print_error (GError *error)
{
if (error != NULL)
{
g_debug ("%s [code %d]\n", error->message, error->code);
g_clear_error (&error);
}
}
GMount *
gof_file_get_mount_at (GFile *target)
{
GVolumeMonitor *monitor;
GFile *root;
GList *mounts, *l;
GMount *found;
monitor = g_volume_monitor_get ();
mounts = g_volume_monitor_get_mounts (monitor);
found = NULL;
for (l = mounts; l != NULL; l = l->next) {
GMount *mount = G_MOUNT (l->data);
if (g_mount_is_shadowed (mount))
continue;
root = g_mount_get_root (mount);
if (g_file_equal (target, root)) {
found = g_object_ref (mount);
break;
}
g_object_unref (root);
}
g_list_free_full (mounts, g_object_unref);
g_object_unref (monitor);
return found;
}
static GFileInfo *
gof_file_query_info (GOFFile *file)
{
GFileInfo *info = NULL;
GError *err = NULL;
g_return_val_if_fail (G_IS_FILE (file->location), NULL);
file->is_mounted = TRUE;
file->exists = TRUE;
file->is_connected = TRUE;
info = g_file_query_info (file->location, "*", 0, NULL, &err);
if (err != NULL) {
if (err->domain == G_IO_ERROR && err->code == G_IO_ERROR_NOT_MOUNTED) {
file->is_mounted = FALSE;
} else if (err->code == G_IO_ERROR_NOT_FOUND
|| err->code == G_IO_ERROR_NOT_DIRECTORY) {
file->exists = FALSE;
} else if (err->code == G_IO_ERROR_TIMED_OUT) {
file->is_connected = FALSE;
}
print_error (err); /* also frees error */
}
return info;
}
/* query info and update. This call is synchronous */
void
gof_file_query_update (GOFFile *file)
{
GFileInfo *info = NULL;
if ((info = gof_file_query_info (file)) != NULL) {
g_clear_object (&file->info);
file->info = info;
gof_file_update (file);
}
}
/* ensure we got the file info */
gboolean
gof_file_ensure_query_info (GOFFile *file)
{
if (file->info == NULL)
gof_file_query_update (file);
return (file->info != NULL);
}
/* only the thumbnail has changed (been generated) */
void
gof_file_query_thumbnail_update (GOFFile *file)
{
gchar *base_name;
gchar *md5_hash;
/* Silently ignore invalid requests */
if (file->pix_size <= 1)
return;
if (gof_file_get_thumbnail_path (file) == NULL) {
/* get the thumbnail path from md5 filename */
md5_hash = g_compute_checksum_for_string (G_CHECKSUM_MD5, file->uri, -1);
base_name = g_strdup_printf ("%s.png", md5_hash);
/* Use $XDG_CACHE_HOME specified thumbnail directory instead of hard coding */
if (file->pix_size <= 128) {
file->thumbnail_path = g_build_filename (g_get_user_cache_dir (), "thumbnails",
"normal", base_name, NULL);
} else {
file->thumbnail_path = g_build_filename (g_get_user_cache_dir (), "thumbnails",
"large", base_name, NULL);
}
g_free (base_name);
g_free (md5_hash);
}
gof_file_update_icon_internal (file, file->pix_size);
}
void gof_file_update_trash_info (GOFFile *file)
{
GTimeVal g_trash_time;
const char * time_string;
g_return_if_fail (file->info != NULL);
file->trash_time = 0;
time_string = g_file_info_get_attribute_string (file->info, "trash::deletion-date");
if (time_string != NULL) {
g_time_val_from_iso8601 (time_string, &g_trash_time);
file->trash_time = g_trash_time.tv_sec;
}
}
void gof_file_remove_from_caches (GOFFile *file)
{
/* remove from file_cache */
if (file_cache != NULL && g_hash_table_remove (file_cache, file->location))
g_debug ("remove from file_cache %s", file->uri);
/* remove from directory_cache */
if (file->directory && G_OBJECT (file->directory)->ref_count > 0) {
gof_directory_async_remove_file_from_cache (file);
}
file->is_gone = TRUE;
}
static void gof_file_init (GOFFile *file) {
/*file->priv = G_TYPE_INSTANCE_GET_PRIVATE (file, GOF_TYPE_FILE, GOFFilePrivate);*/
file->info = NULL;
file->location = NULL;
file->target_location = NULL;
file->icon = NULL;
file->pix = NULL;
file->color = 0;
file->width = 0;
file->height = 0;
file->utf8_collation_key = NULL;
file->formated_type = NULL;
file->format_size = NULL;
file->formated_modified = NULL;
file->custom_display_name = NULL;
file->custom_icon_name = NULL;
file->owner = NULL;
file->group = NULL;
/* assume the file is mounted by default */
file->is_mounted = TRUE;
file->exists = TRUE;
file->is_connected = TRUE;
file->flags = GOF_FILE_THUMB_STATE_UNKNOWN;
file->pix_size = -1;
file->target_gof = NULL;
file->thumbnail_path = NULL;
file->sort_column_id = FM_LIST_MODEL_FILENAME;
file->sort_order = GTK_SORT_ASCENDING;
file->is_expanded = FALSE;
}
static void gof_file_finalize (GObject* obj) {
//g_debug ("%s: delete %p", __func__, obj);
GOFFile *file;
file = GOF_FILE (obj);
#if 0
if (file->pix)
g_warning ("%s %s %u\n", G_STRFUNC, file->uri, G_OBJECT (file->pix)->ref_count);
else
g_warning ("%s %s", G_STRFUNC, file->basename);
#endif
if (!(G_IS_FILE (file->location))) {
g_warning ("Invalid file location on finalize for %s", file->basename);
} else {
g_object_unref (file->location);
}
g_clear_object (&file->info);
_g_object_unref0 (file->directory);
_g_free0 (file->uri);
_g_free0(file->basename);
_g_free0(file->utf8_collation_key);
_g_free0(file->formated_type);
_g_free0(file->format_size);
_g_free0(file->formated_modified);
_g_object_unref0 (file->icon);
_g_object_unref0 (file->pix);
//g_clear_object (&file->pix);
_g_free0 (file->custom_display_name);
_g_free0 (file->custom_icon_name);
_g_object_unref0 (file->target_location);
_g_object_unref0 (file->mount);
/* TODO remove the target_gof */
_g_free0 (file->thumbnail_path);
if (file->target_gof != NULL) {
_g_object_unref0 (file->target_gof);
}
#ifndef NDEBUG
g_warn_if_fail (file->target_gof == NULL);
#endif
_g_free0 (file->owner);
_g_free0 (file->group);
G_OBJECT_CLASS (gof_file_parent_class)->finalize (obj);
}
static void gof_file_class_init (GOFFileClass * klass) {
/* determine the effective user id of the process */
effective_user_id = geteuid ();
gof_file_parent_class = g_type_class_peek_parent (klass);
//g_type_class_add_private (klass, sizeof (GOFFilePrivate));
/*G_OBJECT_CLASS (klass)->get_property = gof_file_get_property;
G_OBJECT_CLASS (klass)->set_property = gof_file_set_property;*/
G_OBJECT_CLASS (klass)->finalize = gof_file_finalize;
signals[CHANGED] = g_signal_new ("changed",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (GOFFileClass, changed),
NULL, NULL,
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE, 0);
signals[DESTROY] = g_signal_new ("destroy",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (GOFFileClass, destroy),
NULL, NULL,
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE, 0);
signals[INFO_AVAILABLE] = g_signal_new ("info_available",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (GOFFileClass, info_available),
NULL, NULL,
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE, 0);
signals[ICON_CHANGED] = g_signal_new ("icon_changed",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (GOFFileClass, icon_changed),
NULL, NULL,
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE, 0);
/*g_object_class_install_property (G_OBJECT_CLASS (klass), gof_FILE_NAME, g_param_spec_string ("name", "name", "name", NULL, G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB | G_PARAM_READABLE));
g_object_class_install_property (G_OBJECT_CLASS (klass), gof_FILE_SIZE, g_param_spec_uint64 ("size", "size", "size", 0, G_MAXUINT64, 0U, G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB | G_PARAM_READABLE));
g_object_class_install_property (G_OBJECT_CLASS (klass), gof_FILE_DIRECTORY, g_param_spec_boolean ("directory", "directory", "directory", FALSE, G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB | G_PARAM_READABLE));*/
}
#if 0
static void gof_file_get_property (GObject * object, guint property_id, GValue * value, GParamSpec * pspec) {
GOFFile * self;
self = GOF_FILE (object);
switch (property_id) {
case gof_FILE_NAME:
g_value_set_string (value, gof_file_get_name (self));
break;
case gof_FILE_SIZE:
g_value_set_uint64 (value, gof_file_get_size (self));
break;
case gof_FILE_DIRECTORY:
g_value_set_boolean (value, gof_file_get_directory (self));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void gof_file_set_property (GObject * object, guint property_id, const GValue * value, GParamSpec * pspec) {
GOFFile * self;
self = GOF_FILE (object);
switch (property_id) {
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
#endif
static int
compare_files_by_time (GOFFile *file1, GOFFile *file2)
{
if (file1->modified < file2->modified)
return -1;
else if (file1->modified > file2->modified)
return 1;
return 0;
}
static int
compare_by_time (GOFFile *file1, GOFFile *file2)
{
if (gof_file_is_folder (file1) && !gof_file_is_folder (file2))
return -1;
if (gof_file_is_folder (file2) && !gof_file_is_folder (file1))
return 1;
return compare_files_by_time (file1, file2);
}
static int
compare_by_type (GOFFile *file1, GOFFile *file2)
{
gchar *key1, *key2;
int compare;
/* Directories go first. Then, if mime types are identical,
* don't bother getting strings (for speed). This assumes
* that the string is dependent entirely on the mime type,
* which is true now but might not be later.
*/
if (gof_file_is_folder (file1) && gof_file_is_folder (file2))
return 0;
if (gof_file_is_folder (file1))
return -1;
if (gof_file_is_folder (file2))
return +1;
key1 = g_utf8_collate_key (file1->formated_type, -1);
key2 = g_utf8_collate_key (file2->formated_type, -1);
compare = g_strcmp0 (key1, key2);
g_free (key1);
g_free (key2);
return compare;
}
static int
compare_by_display_name (GOFFile *file1, GOFFile *file2)
{
g_return_val_if_fail (GOF_IS_FILE (file1), -1);
g_return_val_if_fail (GOF_IS_FILE (file2), -1);
const char *name_1, *name_2;
gboolean sort_last_1, sort_last_2;
int compare;
name_1 = gof_file_get_display_name (file1);
name_2 = gof_file_get_display_name (file2);
sort_last_1 = name_1[0] == SORT_LAST_CHAR1 || name_1[0] == SORT_LAST_CHAR2;
sort_last_2 = name_2[0] == SORT_LAST_CHAR1 || name_2[0] == SORT_LAST_CHAR2;
if (sort_last_1 && !sort_last_2) {
compare = +1;
} else if (!sort_last_1 && sort_last_2) {
compare = -1;
} else {
compare = g_strcmp0 (file1->utf8_collation_key, file2->utf8_collation_key);
}
return compare;
}
static int
compare_files_by_size (GOFFile *file1, GOFFile *file2)
{
if (file1->size < file2->size) {
return -1;
}
else if (file1->size > file2->size) {
return 1;
}
return 0;
}
static int
compare_by_size (GOFFile *file1, GOFFile *file2)
{
if (gof_file_is_folder (file1) && !gof_file_is_folder (file2))
return -1;
if (gof_file_is_folder (file2) && !gof_file_is_folder (file1))
return 1;
return compare_files_by_size (file1, file2);
}
static int
gof_file_compare_for_sort_internal (GOFFile *file1,
GOFFile *file2,
gboolean directories_first,
gboolean reversed)
{
if (directories_first) {
if (gof_file_is_folder (file1) && !gof_file_is_folder (file2))
return -1;
if (gof_file_is_folder (file2) && !gof_file_is_folder (file1))
return 1;
}
/*if (file1->details->sort_order < file2->details->sort_order) {
return reversed ? 1 : -1;
} else if (file_1->details->sort_order > file_2->details->sort_order) {
return reversed ? -1 : 1;
}*/
return 0;
}
int
gof_file_compare_for_sort (GOFFile *file1,
GOFFile *file2,
gint sort_type,
gboolean directories_first,
gboolean reversed)
{
int result;
if (file1 == file2) {
return 0;
}
result = gof_file_compare_for_sort_internal (file1, file2, directories_first, reversed);
if (result == 0) {
switch (sort_type) {
case FM_LIST_MODEL_FILENAME:
result = compare_by_display_name (file1, file2);
/*if (result == 0) {
result = compare_by_directory_name (file_1, file_2);
}*/
break;
case FM_LIST_MODEL_SIZE:
result = compare_by_size (file1, file2);
if (result == 0) {
result = compare_by_display_name (file1, file2);
}
break;
case FM_LIST_MODEL_TYPE:
result = compare_by_type (file1, file2);
if (result == 0) {
result = compare_by_display_name (file1, file2);
}
break;
case FM_LIST_MODEL_MODIFIED:
result = compare_by_time (file1, file2);
if (result == 0) {
result = compare_by_display_name (file1, file2);
}
break;
}
if (reversed) {
result = -result;
}
}
return result;
}
GOFFile *
gof_file_ref (GOFFile *file)
{
if (file == NULL) {
return NULL;
}
g_return_val_if_fail (GOF_IS_FILE (file), NULL);
return g_object_ref (file);
}
void
gof_file_unref (GOFFile *file)
{
if (file == NULL) {
return;
}
g_return_if_fail (GOF_IS_FILE (file));
g_object_unref (file);
}
GList *
gof_files_get_location_list (GList *files)
{
GList *gfile_list = NULL;
GList *l;
GOFFile *file;
for (l=files; l != NULL; l=l->next) {
file = (GOFFile *) l->data;
if (file != NULL && file->location != NULL) {
gfile_list = g_list_prepend (gfile_list, eel_g_file_ref (file->location));
}
}
//gfile_list = g_list_reverse (gfile_list);
return (gfile_list);
}
/**
* gof_file_is_writable: impoted from thunar
* @file : a #GOFFile instance.
*
* Determines whether the owner of the current process is allowed
* to write the @file.
*
* Return value: %TRUE if @file can be written.
**/
gboolean
gof_file_is_writable (GOFFile *file)
{
g_return_val_if_fail (GOF_IS_FILE (file), FALSE);
if (file->target_gof && !g_file_equal (file->location, file->target_gof->location)) {
return gof_file_is_writable (file->target_gof);
} else if (file->info != NULL && g_file_info_has_attribute (file->info, G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE)) {
return g_file_info_get_attribute_boolean (file->info, G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE);
} else if (file->has_permissions) {
return ((file->permissions & S_IWOTH) > 0) ||
((file->permissions & S_IWUSR) > 0) && (file->uid < 0 || file->uid == geteuid ()) ||
((file->permissions & S_IWGRP) > 0) && eel_user_in_group (file->group);
} else {
return TRUE; /* We will just have to assume we can write to the file */
}
gboolean can_write = g_file_info_get_attribute_boolean (file->info, G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE);
if (file->directory && g_file_info_has_attribute (file->info, G_FILE_ATTRIBUTE_ACCESS_CAN_EXECUTE)) {
return can_write && g_file_info_get_attribute_boolean (file->info, G_FILE_ATTRIBUTE_ACCESS_CAN_EXECUTE);
}
return can_write;
}
gboolean
gof_file_is_readable (GOFFile *file)
{
g_return_val_if_fail (GOF_IS_FILE (file), FALSE);
if (file->target_gof && !g_file_equal (file->location, file->target_gof->location)) {
return gof_file_is_readable (file->target_gof);
} else if (file->info != NULL && g_file_info_has_attribute (file->info, G_FILE_ATTRIBUTE_ACCESS_CAN_READ)) {
return g_file_info_get_attribute_boolean (file->info, G_FILE_ATTRIBUTE_ACCESS_CAN_READ);
} else if (file->has_permissions) {
return (file->permissions & S_IROTH) ||
(file->permissions & S_IRUSR) && (file->uid < 0 || file->uid == geteuid ()) ||
(file->permissions & S_IRGRP) && eel_user_in_group (file->group);
} else {
return TRUE; /* We will just have to assume we can read the file */
}
}
gboolean
gof_file_is_trashed (GOFFile *file)
{
g_return_val_if_fail (GOF_IS_FILE (file), FALSE);
return eel_g_file_is_trashed (gof_file_get_target_location (file));
}
const gchar *
gof_file_get_symlink_target (GOFFile *file)
{
g_return_val_if_fail (GOF_IS_FILE (file), NULL);
if (file->info == NULL)
return NULL;
return g_file_info_get_symlink_target (file->info);
}
gboolean
gof_file_is_symlink (GOFFile *file)
{
g_return_val_if_fail (GOF_IS_FILE (file), FALSE);
if (file->info == NULL)
return FALSE;
return g_file_info_get_is_symlink (file->info);
}
gchar *
gof_file_get_formated_time (GOFFile *file, const char *attr)
{
g_return_val_if_fail (file != NULL, NULL);
g_return_val_if_fail (file->info != NULL, NULL);
return pf_file_utils_get_formatted_time_attribute_from_info (file->info, attr);
}
/**
* gof_file_is_desktop_file: imported from thunar
* @file : a #GOFFile.
*
* Returns %TRUE if @file is a .desktop file, but not a .directory file.
*
* Return value: %TRUE if @file is a .desktop file.
**/
gboolean
gof_file_is_desktop_file (GOFFile *file)
{
const gchar *content_type;
gboolean is_desktop_file = FALSE;
g_return_val_if_fail (GOF_IS_FILE (file), FALSE);
if (file->info == NULL)
return FALSE;
content_type = gof_file_get_ftype (file);
if (content_type != NULL)
is_desktop_file = g_content_type_equals (content_type, "application/x-desktop");
return is_desktop_file
&& !g_str_has_suffix (file->basename, ".directory");
}
/**
* gof_file_is_executable: imported from thunar
* @file : a #GOFFile instance.
*
* Determines whether the owner of the current process is allowed
* to execute the @file (or enter the directory refered to by
* @file). On UNIX it also returns %TRUE if @file refers to a
* desktop entry.
*
* Return value: %TRUE if @file can be executed.
**/
gboolean
gof_file_is_executable (GOFFile *file)
{
gboolean can_execute = FALSE;
const gchar *content_type;
g_return_val_if_fail (GOF_IS_FILE (file), FALSE);
if (file->target_gof)
return gof_file_is_executable (file->target_gof);
if (file->info == NULL) {
return FALSE;
}
if (g_file_info_get_attribute_boolean (file->info, G_FILE_ATTRIBUTE_ACCESS_CAN_EXECUTE))
{
/* get the content type of the file */
content_type = gof_file_get_ftype (file);
if (G_LIKELY (content_type != NULL))
{
#ifdef G_OS_WIN32
/* check for .exe, .bar or .com */
can_execute = g_content_type_can_be_executable (content_type);
#else
/* check if the content type is save to execute, we don't use
* g_content_type_can_be_executable() for unix because it also returns
* true for "text/plain" and we don't want that */
if (g_content_type_is_a (content_type, "application/x-executable")
|| g_content_type_is_a (content_type, "application/x-shellscript"))
can_execute = TRUE;
#endif
}
}
return can_execute;
}
/**
* gof_file_set_thumb_state: imported from thunar
* @file : a #GOFFile.
* @thumb_state : the new #GOFFileThumbState.
*
* Sets the #GOFFileThumbState for @file to @thumb_state.
* This will cause a "icon-changed" signal to be emitted from
* #GOFMonitor.
**/
void
gof_file_set_thumb_state (GOFFile *file, GOFFileThumbState state)
{
g_return_if_fail (GOF_IS_FILE (file));
/* set the new thumbnail state */
file->flags = (file->flags & ~GOF_FILE_THUMB_STATE_MASK) | (state);
g_debug ("%s %s %u", G_STRFUNC, file->uri, file->flags);
if (file->flags == GOF_FILE_THUMB_STATE_READY)
gof_file_query_thumbnail_update (file);
/* notify others of this change, so that all components can update
* their file information */
gof_file_icon_changed (file);
}
GOFFile* gof_file_cache_lookup (GFile *location)
{
GOFFile *cached_file = NULL;
g_return_val_if_fail (G_IS_FILE (location), NULL);
/* allocate the GOFFile cache on-demand */
if (G_UNLIKELY (file_cache == NULL))
{
G_LOCK (file_cache_mutex);
file_cache = g_hash_table_new_full (g_file_hash,
(GEqualFunc) g_file_equal,
(GDestroyNotify) g_object_unref,
(GDestroyNotify) g_object_unref);
G_UNLOCK (file_cache_mutex);
}
if (file_cache != NULL)
cached_file = g_hash_table_lookup (file_cache, location);
return _g_object_ref0 (cached_file);
}
void
gof_file_set_expanded (GOFFile *file, gboolean expanded) {
g_return_if_fail (file != NULL && file->is_directory);
file->is_expanded = expanded;
}
GOFFile*
gof_file_get (GFile *location)
{
GFile *parent;
GOFFile *file = NULL;
GOFDirectoryAsync *dir = NULL;
g_return_val_if_fail (location != NULL && G_IS_FILE (location), NULL);
if ((parent = g_file_get_parent (location)) != NULL) {
dir = gof_directory_async_cache_lookup (parent);
if (dir != NULL) {
file = gof_directory_async_file_hash_lookup_location (dir, location);
g_object_unref (dir);
}
}
if (file == NULL)
file = gof_file_cache_lookup (location);
if (file != NULL) {
g_debug (">>>>reuse file %s", file->uri);
} else {
file = gof_file_new (location, parent);
g_debug (">>>>create file %s", file->uri);
G_LOCK (file_cache_mutex);
if (file_cache != NULL)
g_hash_table_insert (file_cache, g_object_ref (location), g_object_ref (file));
G_UNLOCK (file_cache_mutex);
}
if (parent)
g_object_unref (parent);
return (file);
}
GOFFile* gof_file_get_by_uri (const char *uri)
{
GFile *location;
GOFFile *file;
/* Check first that uri is valid */
gchar *scheme;
scheme = g_uri_parse_scheme (uri);
if (scheme == NULL) {
return gof_file_get_by_commandline_arg (uri);
} else {
g_free (scheme);
location = g_file_new_for_uri (uri);
if (location == NULL) {
return NULL;
}
}
file = gof_file_get (location);
#ifdef ENABLE_DEBUG
g_debug ("%s %s", G_STRFUNC, file->uri);
#endif
g_object_unref (location);
return file;
}
GOFFile* gof_file_get_by_commandline_arg (const char *arg)
{
GFile *location;
GOFFile *file;
location = g_file_new_for_commandline_arg (arg);
file = gof_file_get (location);
g_object_unref (location);
return file;
}
gchar* gof_file_list_to_string (GList *list, gsize *len)
{
GString *string;
GList *lp;
/* allocate initial string */
string = g_string_new (NULL);
for (lp = list; lp != NULL; lp = lp->next)
{
string = g_string_append (string, GOF_FILE(lp->data)->uri);
string = g_string_append (string, "\r\n");
}
*len = string->len;
return g_string_free (string, FALSE);
}
gboolean gof_file_same_filesystem (GOFFile *file_a, GOFFile *file_b)
{
const gchar *filesystem_id_a;
const gchar *filesystem_id_b;
g_return_val_if_fail (GOF_IS_FILE (file_a), FALSE);
g_return_val_if_fail (GOF_IS_FILE (file_b), FALSE);
/* return false if we have no information about one of the files */
if (file_a->info == NULL || file_b->info == NULL)
return FALSE;
/* determine the filesystem IDs */
filesystem_id_a = g_file_info_get_attribute_string (file_a->info,
G_FILE_ATTRIBUTE_ID_FILESYSTEM);
filesystem_id_b = g_file_info_get_attribute_string (file_b->info,
G_FILE_ATTRIBUTE_ID_FILESYSTEM);
/* compare the filesystem IDs */
return eel_str_is_equal (filesystem_id_a, filesystem_id_b);
}
/**
* gof_file_accepts_drop (imported from thunar):
* @file : a #GOFFile instance.
* @file_list : the list of #GFiles that will be droppped.
* @context : the current #GdkDragContext, which is used for the drop.
* @suggested_action_return : return location for the suggested #GdkDragAction or %NULL.
*
* Checks whether @file can accept @path_list for the given @context and
* returns the #GdkDragActions that can be used or 0 if no actions
* apply.
*
* If any #GdkDragActions apply and @suggested_action_return is not
* %NULL, the suggested #GdkDragAction for this drop will be stored to the
* location pointed to by @suggested_action_return.
*
* Return value: the #GdkDragActions supported for the drop or
* 0 if no drop is possible.
**/
GdkDragAction
gof_file_accepts_drop (GOFFile *file,
GList *file_list,
GdkDragContext *context,
GdkDragAction *suggested_action_return)
{
GdkDragAction suggested_action;
GdkDragAction actions;
GOFFile *ofile;
GFile *parent_file;
GList *lp;
guint n;
g_return_val_if_fail (GOF_IS_FILE (file), 0);
g_return_val_if_fail (GDK_IS_DRAG_CONTEXT (context), 0);
/* we can never drop an empty list */
if (G_UNLIKELY (file_list == NULL))
return 0;
/* default to whatever GTK+ thinks for the suggested action */
suggested_action = gdk_drag_context_get_suggested_action (context);
/* check if we have a writable directory here or an executable file */
if (gof_file_is_folder (file) && gof_file_is_writable (file))
{
/* determine the possible actions */
actions = gdk_drag_context_get_actions (context) & (GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK | GDK_ACTION_ASK);
/* check up to 100 of the paths (just in case somebody tries to
* drag around his music collection with 5000 files).
*/
for (lp = file_list, n = 0; lp != NULL && n < 100; lp = lp->next, ++n)
{
/* we cannot drop a file on itself */
if (G_UNLIKELY (g_file_equal (gof_file_get_target_location (file), lp->data)))
return 0;
/* check whether source and destination are the same */
parent_file = g_file_get_parent (lp->data);
if (G_LIKELY (parent_file != NULL))
{
if (g_file_equal (gof_file_get_target_location (file), parent_file))
{
g_object_unref (parent_file);
suggested_action = GDK_ACTION_ASK;
actions = GDK_ACTION_ASK|GDK_ACTION_LINK;
}
else
g_object_unref (parent_file);
}
/* Make these tests at the end so that any changes are not reversed subsequently */
char *scheme;
scheme = g_file_get_uri_scheme (lp->data);
if (!g_str_has_prefix (scheme, "file")) {
/* do not allow symbolic links from remote filesystems */
actions &= ~(GDK_ACTION_LINK);
}
g_free (scheme);
/* copy/move/link within the trash not possible */
if (G_UNLIKELY (eel_g_file_is_trashed (lp->data) && gof_file_is_trashed (file)))
return 0;
}
/* if the source offers both copy and move and the GTK+ suggested action is copy, try to
* be smart telling whether we should copy or move by default by checking whether the
* source and target are on the same disk. */
if ((actions & (GDK_ACTION_COPY | GDK_ACTION_MOVE)) != 0
&& (suggested_action == GDK_ACTION_COPY))
{
/* default to move as suggested action */
suggested_action = GDK_ACTION_MOVE;
/* check for up to 100 files, for the reason state above */
for (lp = file_list, n = 0; lp != NULL && n < 100; lp = lp->next, ++n)
{
/* dropping from the trash always suggests move */
if (G_UNLIKELY (eel_g_file_is_trashed (lp->data)))
break;
/* determine the cached version of the source file */
ofile = gof_file_get(lp->data);
/* we have only move if we know the source and both the source and the target
* are on the same disk, and the source file is owned by the current user.
*/
if (ofile == NULL
|| !gof_file_same_filesystem (file, ofile)
|| (ofile->info != NULL
&& ofile->uid > -1
&& ofile->uid != effective_user_id ))
{
/* default to copy and get outa here */
suggested_action = GDK_ACTION_COPY;
break;
}
}
}
}
else if (!gof_file_is_folder (file) && gof_file_is_executable (file))
{
/* determine the possible actions */
actions = gdk_drag_context_get_actions (context) & (GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK | GDK_ACTION_PRIVATE);
} else {
g_debug ("Not a valid drop target");
return 0;
}
/* Make these tests at the end so that any changes are not reversed subsequently */
char *scheme;
scheme = g_file_get_uri_scheme (gof_file_get_target_location (file));
/* do not allow symbolic links to remote filesystems */
if (!g_str_has_prefix (scheme, "file"))
actions &= ~(GDK_ACTION_LINK);
g_free (scheme);
/* cannot create symbolic links in the trash or copy to the trash */
if (gof_file_is_trashed (file))
actions &= ~(GDK_ACTION_COPY | GDK_ACTION_LINK);
if (actions == GDK_ACTION_ASK) {
/* No point in asking if there are no allowed actions */
return 0;
}
/* determine the preferred action based on the context */
if (G_LIKELY (suggested_action_return != NULL))
{
/* determine a working action */
if (G_LIKELY ((suggested_action & actions) != 0))
*suggested_action_return = suggested_action;
else if ((actions & GDK_ACTION_ASK) != 0)
*suggested_action_return = GDK_ACTION_ASK;
else if ((actions & GDK_ACTION_COPY) != 0)
*suggested_action_return = GDK_ACTION_COPY;
else if ((actions & GDK_ACTION_LINK) != 0)
*suggested_action_return = GDK_ACTION_LINK;
else if ((actions & GDK_ACTION_MOVE) != 0)
*suggested_action_return = GDK_ACTION_MOVE;
else
*suggested_action_return = GDK_ACTION_PRIVATE;
}
/* yeppa, we can drop here */
return actions;
}
static gboolean
gof_spawn_command_line_on_screen (char *cmd, GdkScreen *screen)
{
GAppInfo *app;
GdkAppLaunchContext *ctx;
GError *error = NULL;
gboolean succeed = FALSE;
app = g_app_info_create_from_commandline (cmd, NULL, 0, &error);
if (app != NULL && screen != NULL) {
ctx = gdk_display_get_app_launch_context (gdk_screen_get_display (screen));
succeed = g_app_info_launch (app, NULL, G_APP_LAUNCH_CONTEXT (ctx), &error);
g_object_unref (app);
g_object_unref (ctx);
}
if (error != NULL) {
g_error_free (error);
}
return (succeed);
}
/**
* gof_file_get_default_handler: imported from thunar
* @file : a #GOFFile instance.
*
* Returns the default #GAppInfo for @file or %NULL if there is none.
*
* The caller is responsible to free the returned #GAppInfo using
* g_object_unref().
*
* Return value: Default #GAppInfo for @file or %NULL if there is none.
**/
GAppInfo *
gof_file_get_default_handler (GOFFile *file)
{
const gchar *content_type;
gboolean must_support_uris = FALSE;
gchar *path;
g_return_val_if_fail (GOF_IS_FILE (file), NULL);
content_type = gof_file_get_ftype (file);
if (content_type != NULL)
{
path = g_file_get_path (file->location);
must_support_uris = (path == NULL);
_g_free0 (path);
return g_app_info_get_default_for_type (content_type, must_support_uris);
}
//g_app_info_get_default_for_uri_scheme
if (file->target_location != NULL)
return g_file_query_default_handler (file->target_location, NULL, NULL);
return g_file_query_default_handler (file->location, NULL, NULL);
}
gboolean
gof_file_execute (GOFFile *file, GdkScreen *screen, GList *file_list, GError **error)
{
/*gboolean snotify = FALSE;
gboolean terminal;*/
gboolean result = FALSE;
GKeyFile *key_file;
GError *err = NULL;
gchar *icon = NULL;
gchar *name;
gchar *type;
gchar *url;
gchar *location;
gchar *exec;
gchar *cmd = NULL;
gchar *quoted_location;
g_return_val_if_fail (GOF_IS_FILE (file), FALSE);
g_return_val_if_fail (GDK_IS_SCREEN (screen), FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
/* only execute locale executable files */
if (!g_file_is_native (file->location))
return FALSE;
location = g_file_get_path (file->location);
if (gof_file_is_desktop_file (file))
{
key_file = eel_g_file_query_key_file (file->location, NULL, &err);
if (key_file == NULL)
{
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_INVAL,
_("Failed to parse the desktop file: %s"), err->message);
g_error_free (err);
return FALSE;
}
type = g_key_file_get_string (key_file, G_KEY_FILE_DESKTOP_GROUP,
G_KEY_FILE_DESKTOP_KEY_TYPE, NULL);
if (G_LIKELY (eel_str_is_equal (type, "Application")))
{
exec = g_key_file_get_string (key_file, G_KEY_FILE_DESKTOP_GROUP,
G_KEY_FILE_DESKTOP_KEY_EXEC, NULL);
if (G_LIKELY (exec != NULL))
{
/* parse other fields */
name = g_key_file_get_locale_string (key_file, G_KEY_FILE_DESKTOP_GROUP,
G_KEY_FILE_DESKTOP_KEY_NAME, NULL,
NULL);
icon = g_key_file_get_string (key_file, G_KEY_FILE_DESKTOP_GROUP,
G_KEY_FILE_DESKTOP_KEY_ICON, NULL);
/* TODO use terminal snotify */
/*terminal = g_key_file_get_boolean (key_file, G_KEY_FILE_DESKTOP_GROUP,
G_KEY_FILE_DESKTOP_KEY_TERMINAL, NULL);
snotify = g_key_file_get_boolean (key_file, G_KEY_FILE_DESKTOP_GROUP,
G_KEY_FILE_DESKTOP_KEY_STARTUP_NOTIFY,
NULL);*/
cmd = marlin_exec_parse (exec, file_list, icon, name, location);
_g_free0 (name);
_g_free0 (icon);
_g_free0 (exec);
}
else
{
/// TRANSLATORS: `Exec' is a field name in a .desktop file. Don't translate it.
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_INVAL,
_("No Exec field specified"));
}
}
else if (eel_str_is_equal (type, "Link"))
{
url = g_key_file_get_string (key_file, G_KEY_FILE_DESKTOP_GROUP,
G_KEY_FILE_DESKTOP_KEY_URL, NULL);
if (G_LIKELY (url != NULL))
{
//printf ("%s Link %s\n", G_STRFUNC, url);
GOFFile *link = gof_file_get_by_commandline_arg (url);
result = gof_file_launch (link, screen, NULL);
g_object_unref (link);
return (result);
}
else
{
/// TRANSLATORS: `Exec' is a field name in a .desktop file. Don't translate it.
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_INVAL,
_("No URL field specified"));
}
}
else
{
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_INVAL,
_("Invalid desktop file"));
}
_g_free0 (type);
g_key_file_free (key_file);
}
else
{
quoted_location = g_shell_quote (location);
cmd = marlin_exec_auto_parse (quoted_location, file_list);
//printf ("%s exec: %s\n", G_STRFUNC, cmd);
_g_free0 (quoted_location);
}
if (cmd != NULL) {
//printf ("%s cmd: %s\n", G_STRFUNC, cmd);
result = gof_spawn_command_line_on_screen (cmd, screen);
}
_g_free0 (location);
_g_free0 (cmd);
return result;
}
static gboolean
gof_file_launch_with (GOFFile *file, GdkScreen *screen, GAppInfo* app_info)
{
GdkAppLaunchContext *context;
gboolean succeed;
GList path_list;
GError *error = NULL;
g_return_val_if_fail (GOF_IS_FILE (file), FALSE);
g_return_val_if_fail (GDK_IS_SCREEN (screen), FALSE);
/* fake a path list */
path_list.data = file->location;
path_list.next = path_list.prev = NULL;
context = gdk_display_get_app_launch_context (gdk_screen_get_display (screen));
succeed = g_app_info_launch (app_info, &path_list, G_APP_LAUNCH_CONTEXT (context), &error);
g_object_unref (context);
return succeed;
}
gboolean
gof_file_launch_files (GList *files, GdkScreen *screen, GAppInfo* app_info)
{
GdkAppLaunchContext *context;
gboolean succeed;
GList *gfiles;
GError *error = NULL;
g_return_val_if_fail (files != NULL, FALSE);
g_return_val_if_fail (GDK_IS_SCREEN (screen), FALSE);
context = gdk_display_get_app_launch_context (gdk_screen_get_display (screen));
gfiles = gof_files_get_location_list (files);
succeed = g_app_info_launch (app_info, gfiles, G_APP_LAUNCH_CONTEXT (context), &error);
print_error (error); /* also frees error */
g_list_free_full (gfiles, (GDestroyNotify) eel_g_file_unref);
g_object_unref (context);
return succeed;
}
gboolean
gof_file_launch (GOFFile *file, GdkScreen *screen, GAppInfo *app_info)
{
GAppInfo *app = NULL;
gboolean succeed;
GError *error = NULL;
g_return_val_if_fail (GOF_IS_FILE (file), FALSE);
g_return_val_if_fail (GDK_IS_SCREEN (screen), FALSE);
if (app_info != NULL)
app = g_app_info_dup (app_info);
/* Do not run executables if an app to open them with has been supplied */
if (app == NULL) {
/* check if we should execute the file */
if (gof_file_is_executable (file))
return gof_file_execute (file, screen, NULL, &error);
else
app = gof_file_get_default_handler (file);
}
if (app == NULL)
{
/* AppChooser dialog has already been shown by Marlin.MimeActions*/
return TRUE;
}
/* check if we're not trying to launch our own file manager */
/*if (g_strcmp0 (g_app_info_get_id (app_info), "marlin.desktop") == 0
|| g_strcmp0 (g_app_info_get_name (app_info), "marlin") == 0)
{
g_object_unref (G_OBJECT (app_info));
app_info = g_app_info_create_from_commandline ("marlin -t", "marlin", 0, NULL);
}*/
/* TODO allow launch of multiples same content type files */
succeed = gof_file_launch_with (file, screen, app);
/* TODO error */
g_object_unref (G_OBJECT (app));
return succeed;
}
void
gof_file_open_single (GOFFile *file, GdkScreen *screen, GAppInfo *app_info)
{
gof_file_launch (file, screen, app_info);
}
void
gof_file_list_free (GList *list)
{
g_list_foreach (list, (GFunc) gof_file_unref, NULL);
g_list_free (list);
}
GList *
gof_file_list_ref (GList *list)
{
g_list_foreach (list, (GFunc) gof_file_ref, NULL);
return list;
}
GList *
gof_file_list_copy (GList *list)
{
return g_list_copy (gof_file_list_ref (list));
}
static void
gof_file_update_existing (GOFFile *file, GFile *new_location)
{
GOFDirectoryAsync *dir = NULL;
if (file->directory != NULL) {
dir = gof_directory_async_cache_lookup (file->directory);
}
gof_file_remove_from_caches (file);
file->is_gone = FALSE;
g_object_unref (file->location);
file->location = g_object_ref (new_location);
if (dir != NULL)
gof_directory_async_file_hash_add_file (dir, file);
_g_free0 (file->uri);
file->uri = g_file_get_uri (new_location);
_g_free0 (file->basename);
file->basename = g_file_get_basename (file->location);
/* TODO update color on rename ? */
//file->color = 0;
file->pix_size = -1;
_g_free0 (file->thumbnail_path);
file->flags = 0;
gof_file_query_update (file);
g_object_unref (dir);
}
/* TODO move this mini job to marlin-file-operations? */
GOFFileOperation *
gof_file_operation_new (GOFFile *file,
GOFFileOperationCallback callback,
gpointer callback_data)
{
GOFFileOperation *op;
op = g_new0 (GOFFileOperation, 1);
op->file = gof_file_ref (file);
op->callback = callback;
op->callback_data = callback_data;
op->cancellable = g_cancellable_new ();
/* FIXME check this Glist */
op->file->operations_in_progress = g_list_prepend
(op->file->operations_in_progress, op);
return op;
}
static void
gof_file_operation_remove (GOFFileOperation *op)
{
op->file->operations_in_progress = g_list_remove
(op->file->operations_in_progress, op);
}
void
gof_file_operation_free (GOFFileOperation *op)
{
gof_file_operation_remove (op);
gof_file_unref (op->file);
g_object_unref (op->cancellable);
if (op->free_data) {
op->free_data (op->data);
}
_g_free0 (op);
}
void
gof_file_operation_complete (GOFFileOperation *op, GFile *result_file, GError *error)
{
/* Claim that something changed even if the operation failed.
* This makes it easier for some clients who see the "reverting"
* as "changing back".
*/
gof_file_operation_remove (op);
gof_file_icon_changed (op->file);
//marlin_file_changes_consume_changes (TRUE);
if (op->callback) {
(* op->callback) (op->file, result_file, error, op->callback_data);
}
gof_file_operation_free (op);
}
void
gof_file_operation_cancel (GOFFileOperation *op)
{
/* Cancel the operation if it's still in progress. */
g_cancellable_cancel (op->cancellable);
}
static void
rename_callback (GObject *source_object,
GAsyncResult *res,
gpointer callback_data)
{
GOFFileOperation *op;
GFile *new_file;
GError *error;
op = callback_data;
error = NULL;
new_file = g_file_set_display_name_finish (G_FILE (source_object),
res, &error);
//marlin_file_changes_queue_file_changed (new_file);
//marlin_file_changes_queue_file_removed (op->file->location);
//marlin_file_changes_queue_file_added (new_file);
if (error == NULL)
gof_file_update_existing (op->file, new_file);
else
marlin_dialogs_show_error (NULL,
error,
"Failed to rename %s",
g_file_get_parse_name (op->file->location));
//g_warning ("%s %u", G_STRFUNC, G_OBJECT (op->file)->ref_count);
gof_file_operation_complete (op, new_file, error);
if (new_file != NULL) {
g_object_unref (new_file);
} else {
g_error_free (error);
}
}
void
gof_file_rename (GOFFile *file,
const char *new_name,
GOFFileOperationCallback callback,
gpointer callback_data)
{
GOFFileOperation *op;
//char *uri;
//char *old_name;
//char *new_file_name;
//gboolean success, name_changed;
GError *error;
//g_warning ("%s %u", G_STRFUNC, G_OBJECT (file)->ref_count);
g_return_if_fail (GOF_IS_FILE (file));
g_return_if_fail (new_name != NULL);
g_return_if_fail (callback != NULL);
//TODO rename .desktop files
/* Return an error for incoming names containing path separators.
* But not for .desktop files as '/' are allowed for them */
if (strstr (new_name, "/") != NULL) {
error = g_error_new (G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
_("Slashes are not allowed in filenames"));
(* callback) (file, NULL, error, callback_data);
g_error_free (error);
return;
}
//TODO check
/* Self-owned files can't be renamed. Test the name-not-actually-changing
* case before this case.
*/
#if 0
if (nautilus_file_is_self_owned (file)) {
/* Claim that something changed even if the rename
* failed. This makes it easier for some clients who
* see the "reverting" to the old name as "changing
* back".
*/
nautilus_file_changed (file);
error = g_error_new (G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
_("Toplevel files cannot be renamed"));
(* callback) (file, NULL, error, callback_data);
g_error_free (error);
return;
}
#endif
/* Set up a renaming operation. */
op = gof_file_operation_new (file, callback, callback_data);
op->is_rename = TRUE;
/* Do the renaming. */
g_file_set_display_name_async (file->location,
new_name,
G_PRIORITY_DEFAULT,
op->cancellable,
rename_callback,
op);
}
gboolean
gof_file_can_set_owner (GOFFile *file)
{
/* unknown file uid */
if (file->uid == -1)
return FALSE;
/* root */
return geteuid() == 0;
}
/* copied from nautilus-file.c */
/**
* gof_file_can_set_group:
*
* Check whether the current user is allowed to change
* the group of a file.
*
* @file: The file in question.
*
* Return value: TRUE if the current user can change the
* group of @file, FALSE otherwise. It's always possible
* that when you actually try to do it, you will fail.
*/
gboolean
gof_file_can_set_group (GOFFile *file)
{
uid_t user_id;
if (file->gid == -1)
return FALSE;
user_id = geteuid();
/* Owner is allowed to set group (with restrictions). */
if (user_id == (uid_t) file->uid)
return TRUE;
/* Root is also allowed to set group. */
if (user_id == 0)
return TRUE;
return FALSE;
}
/* copied from nautilus-file.c */
/**
* nautilus_file_get_settable_group_names:
*
* Get a list of all group names that the current user
* can set the group of a specific file to.
*
* @file: The NautilusFile in question.
*/
GList *
gof_file_get_settable_group_names (GOFFile *file)
{
uid_t user_id;
GList *result = NULL;
if (!gof_file_can_set_group (file))
return NULL;
/* Check the user. */
user_id = geteuid();
if (user_id == 0) {
/* Root is allowed to set group to anything. */
result = eel_get_all_group_names ();
} else if (user_id == (uid_t) file->uid) {
/* Owner is allowed to set group to any that owner is member of. */
result = eel_get_group_names_for_user ();
} else {
g_warning ("unhandled case in %s", G_STRFUNC);
}
return result;
}
/* copied from nautilus-file.c */
/**
* gof_file_can_set_permissions:
*
* Check whether the current user is allowed to change
* the permissions of a file.
*
* @file: The file in question.
*
* Return value: TRUE if the current user can change the
* permissions of @file, FALSE otherwise. It's always possible
* that when you actually try to do it, you will fail.
*/
gboolean
gof_file_can_set_permissions (GOFFile *file)
{
uid_t user_id;
if (file->uid != -1 && g_file_is_native (file->location))
{
/* Check the user. */
user_id = geteuid();
/* Owner is allowed to set permissions. */
if (user_id == (uid_t) file->uid)
return TRUE;
/* Root is also allowed to set permissions. */
if (user_id == 0)
return TRUE;
/* Nobody else is allowed. */
return FALSE;
}
/* pretend to have full chmod rights when no info is available, relevant when
* the FS can't provide ownership info, for instance for FTP */
return TRUE;
}
/* copied from nautilus-file.c */
/**
* gof_file_get_permissions_as_string:
*
* Get a user-displayable string representing a file's permissions. The caller
* is responsible for _g_free0-ing this string.
* @file: GOFFile representing the file in question.
*
* Returns: Newly allocated string ready to display to the user.
*
**/
char *
gof_file_get_permissions_as_string (GOFFile *file)
{
gboolean is_link;
gboolean suid, sgid, sticky;
g_assert (GOF_IS_FILE (file));
is_link = gof_file_is_symlink (file);
/* We use ls conventions for displaying these three obscure flags */
suid = file->permissions & S_ISUID;
sgid = file->permissions & S_ISGID;
sticky = file->permissions & S_ISVTX;
return g_strdup_printf ("%c%c%c%c%c%c%c%c%c%c",
is_link ? 'l' : file->is_directory ? 'd' : '-',
file->permissions & S_IRUSR ? 'r' : '-',
file->permissions & S_IWUSR ? 'w' : '-',
file->permissions & S_IXUSR
? (suid ? 's' : 'x')
: (suid ? 'S' : '-'),
file->permissions & S_IRGRP ? 'r' : '-',
file->permissions & S_IWGRP ? 'w' : '-',
file->permissions & S_IXGRP
? (sgid ? 's' : 'x')
: (sgid ? 'S' : '-'),
file->permissions & S_IROTH ? 'r' : '-',
file->permissions & S_IWOTH ? 'w' : '-',
file->permissions & S_IXOTH
? (sticky ? 't' : 'x')
: (sticky ? 'T' : '-'));
}
gint
gof_file_compare_by_display_name (gconstpointer a, gconstpointer b)
{
return compare_by_display_name (GOF_FILE (a), GOF_FILE (b));
}
GFile *
gof_file_get_target_location (GOFFile *file)
{
/* Do not interpret desktop files (lp:1660742) */
if (file->target_location != NULL)
return file->target_location;
return file->location;
}
const gchar *
gof_file_get_display_name (GOFFile *file)
{
return file->custom_display_name ? file->custom_display_name : file->basename;
}
gboolean
gof_file_is_folder (GOFFile *file)
{
if (file == NULL) {
g_warning ("gof_file_is_folder () called with null file - ignoring");
return FALSE;
}
/* TODO check this works for non-local files and other uri schemes*/
if ((file->is_directory && !gof_file_is_root_network_folder (file)))
return TRUE;
if (gof_file_is_smb_share (file))
return TRUE;
if (file->file_type == G_FILE_TYPE_MOUNTABLE &&
file->info != NULL &&
g_file_info_get_attribute_boolean (file->info, G_FILE_ATTRIBUTE_MOUNTABLE_CAN_MOUNT))
return TRUE;
if (file->target_gof &&
file->target_gof->is_directory &&
gof_file_is_network_uri_scheme (file->target_gof)) {
return TRUE;
}
return FALSE;
}
const gchar *
gof_file_get_ftype (GOFFile *file)
{
if (file->info == NULL || gof_file_is_location_uri_default (file))
return NULL;
const char *ftype = NULL;
if (g_file_info_has_attribute (file->info, G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE))
return g_file_info_get_attribute_string (file->info, G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE);
if (g_file_info_has_attribute (file->info, G_FILE_ATTRIBUTE_STANDARD_FAST_CONTENT_TYPE))
ftype = g_file_info_get_attribute_string (file->info, G_FILE_ATTRIBUTE_STANDARD_FAST_CONTENT_TYPE);
if (!g_strcmp0 (ftype, "application/octet-stream") && file->tagstype)
return file->tagstype;
return ftype;
}
/**
* transfer: none
**/
const gchar *
gof_file_get_thumbnail_path (GOFFile *file)
{
if (file->thumbnail_path != NULL)
return file->thumbnail_path;
if (file->info != NULL && g_file_info_has_attribute (file->info, G_FILE_ATTRIBUTE_THUMBNAIL_PATH))
return g_file_info_get_attribute_byte_string (file->info, G_FILE_ATTRIBUTE_THUMBNAIL_PATH);
return NULL;
}
char*
gof_file_get_display_target_uri (GOFFile *file)
{
/* This returns a string that requires freeing */
gchar* uri;
uri = g_file_info_get_attribute_as_string (file->info, G_FILE_ATTRIBUTE_STANDARD_TARGET_URI);
if (uri == NULL) {
uri = strdup (file->uri);
}
return uri;
}
const gchar *
gof_file_get_preview_path(GOFFile* file)
{
gchar* thumbnail_path = gof_file_get_thumbnail_path(file);
gchar* new_thumbnail_path = NULL;
gchar** thumbnail_path_split = NULL;
if (thumbnail_path != NULL)
{
/* Construct new path to large thumbnail based on $XDG_CACHE_HOME */
thumbnail_path_split = g_strsplit(thumbnail_path, G_DIR_SEPARATOR_S, -1);
uint l;
l = g_strv_length(thumbnail_path_split);
if(l > 2)
{
new_thumbnail_path = g_strjoin(G_DIR_SEPARATOR_S, g_get_user_cache_dir (), "thumbnails/large", thumbnail_path_split[l-1], NULL);
if(!g_file_test(new_thumbnail_path, G_FILE_TEST_EXISTS))
{
new_thumbnail_path = g_strdup(thumbnail_path);
}
}
else
{
g_critical("Thumbnailer is not FD.o compliant?");
new_thumbnail_path = g_strdup(thumbnail_path);
}
g_strfreev(thumbnail_path_split);
}
return new_thumbnail_path;
}
gboolean
gof_file_can_unmount (GOFFile *file)
{
g_return_val_if_fail (GOF_IS_FILE (file), FALSE);
return file->can_unmount || (file->mount != NULL && g_mount_can_unmount (file->mount));
}
gboolean
gof_file_thumb_can_frame (GOFFile *file)
{
GOFDirectoryAsync *dir = NULL;
/* get the DirectoryAsync associated to the file */
if (file->directory != NULL) {
dir = gof_directory_async_cache_lookup (file->directory);
}
if (dir != NULL) {
gboolean can_frame = !dir->uri_contain_keypath_icons;
g_object_unref (dir);
return can_frame;
}
return FALSE;
}