thunar-usagebars/thunar/thunar-sendto-model.c
4aiman df58603323 thunar 4.20.7 upstream
Tree of tag thunar-4.20.7 (commit df0e054) from https://gitlab.xfce.org/xfce/thunar.
Upstream history is not included here to keep the repository small.
2026-11-04 01:40:30 +03:00

351 lines
10 KiB
C

/* vi:set et ai sw=2 sts=2 ts=2: */
/*-
* Copyright (c) 2006 Benedikt Meurer <benny@xfce.org>
* Copyright (c) 2009 Jannis Pohlmann <jannis@xfce.org>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program 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 General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc., 59 Temple
* Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#ifdef HAVE_MEMORY_H
#include <memory.h>
#endif
#ifdef HAVE_STRING_H
#include <string.h>
#endif
#ifdef HAVE_GIO_UNIX
#include <gio/gdesktopappinfo.h>
#endif
#include "thunar/thunar-private.h"
#include "thunar/thunar-sendto-model.h"
static void
thunar_sendto_model_finalize (GObject *object);
static void
thunar_sendto_model_load (ThunarSendtoModel *sendto_model);
static void
thunar_sendto_model_event (GFileMonitor *monitor,
GFile *file,
GFile *other_file,
GFileMonitorEvent event_type,
gpointer user_data);
struct _ThunarSendtoModelClass
{
GObjectClass __parent__;
};
struct _ThunarSendtoModel
{
GObject __parent__;
GList *monitors;
GList *handlers;
guint loaded : 1;
};
G_DEFINE_TYPE (ThunarSendtoModel, thunar_sendto_model, G_TYPE_OBJECT)
static void
thunar_sendto_model_class_init (ThunarSendtoModelClass *klass)
{
GObjectClass *gobject_class;
gobject_class = G_OBJECT_CLASS (klass);
gobject_class->finalize = thunar_sendto_model_finalize;
}
static void
thunar_sendto_model_init (ThunarSendtoModel *sendto_model)
{
sendto_model->monitors = NULL;
}
static void
thunar_sendto_model_finalize (GObject *object)
{
ThunarSendtoModel *sendto_model = THUNAR_SENDTO_MODEL (object);
GList *lp;
/* release the handlers */
g_list_free_full (sendto_model->handlers, g_object_unref);
/* disconnect all monitors */
for (lp = sendto_model->monitors; lp != NULL; lp = lp->next)
{
g_file_monitor_cancel (lp->data);
g_object_unref (lp->data);
}
g_list_free (sendto_model->monitors);
(*G_OBJECT_CLASS (thunar_sendto_model_parent_class)->finalize) (object);
}
static gint
g_app_info_compare (gpointer a,
gpointer b)
{
return g_utf8_collate (g_app_info_get_name (b),
g_app_info_get_name (a));
}
static void
thunar_sendto_model_load (ThunarSendtoModel *sendto_model)
{
#ifdef HAVE_GIO_UNIX
GDesktopAppInfo *app_info = NULL;
#endif
gchar **specs;
gchar *path;
guint n;
GKeyFile *key_file;
gchar **mime_types;
/* lookup all sendto .desktop files */
specs = xfce_resource_match (XFCE_RESOURCE_DATA, "Thunar/sendto/*.desktop", TRUE);
for (n = 0; specs[n] != NULL; ++n)
{
/* lookup the absolute path to the .desktop file */
path = xfce_resource_lookup (XFCE_RESOURCE_DATA, specs[n]);
if (G_LIKELY (path != NULL))
{
/* try to load the .desktop file */
key_file = g_key_file_new ();
if (!g_key_file_load_from_file (key_file, path, G_KEY_FILE_NONE, NULL))
{
g_key_file_free (key_file);
continue;
}
#ifdef HAVE_GIO_UNIX
app_info = g_desktop_app_info_new_from_keyfile (key_file);
if (G_LIKELY (app_info != NULL))
{
/* add to our handler list, sorted by their desktop-ids (reverse order) */
sendto_model->handlers = g_list_insert_sorted (sendto_model->handlers,
G_APP_INFO (app_info),
(GCompareFunc) (void (*) (void)) g_app_info_compare);
/* attach the mime-types to the object */
mime_types = g_key_file_get_string_list (key_file,
G_KEY_FILE_DESKTOP_GROUP,
G_KEY_FILE_DESKTOP_KEY_MIME_TYPE,
NULL, NULL);
if (mime_types != NULL)
g_object_set_data_full (G_OBJECT (app_info), "mime-types", mime_types, (GDestroyNotify) g_strfreev);
}
#else
/* FIXME try to create the app info ourselves in a platform independent way */
#endif
g_key_file_free (key_file);
}
/* cleanup */
g_free (specs[n]);
g_free (path);
}
g_free (specs);
}
static void
thunar_sendto_model_event (GFileMonitor *monitor,
GFile *file,
GFile *other_file,
GFileMonitorEvent event_type,
gpointer user_data)
{
ThunarSendtoModel *sendto_model = THUNAR_SENDTO_MODEL (user_data);
/* release the previously loaded handlers */
if (G_LIKELY (sendto_model->handlers != NULL))
{
g_list_free_full (sendto_model->handlers, g_object_unref);
sendto_model->handlers = NULL;
}
/* reload the handlers for the model */
thunar_sendto_model_load (sendto_model);
}
/**
* thunar_sendto_model_get_default:
*
* Returns a reference to the default #ThunarSendtoModel
* instance. The caller is responsible to free the returned
* reference using g_object_unref() when no longer needed.
*
* Return value: reference to the default sendto model.
**/
ThunarSendtoModel *
thunar_sendto_model_get_default (void)
{
static ThunarSendtoModel *sendto_model = NULL;
if (G_UNLIKELY (sendto_model == NULL))
{
sendto_model = g_object_new (THUNAR_TYPE_SENDTO_MODEL, NULL);
g_object_add_weak_pointer (G_OBJECT (sendto_model), (gpointer) &sendto_model);
}
else
{
g_object_ref (G_OBJECT (sendto_model));
}
return sendto_model;
}
/**
* thunar_sendto_model_get_matching:
* @sendto_model : a #ThunarSendtoModel.
* @files : a #GList of #ThunarFile<!---->s.
*
* Returns the list of #GAppInfo<!---->s for the "Send To" targets that
* support the specified @files.
*
* The returned list is owned by the caller and must be freed when no
* longer needed, using:
* <informalexample><programlisting>
* g_list_free_full (list, g_object_unref);
* </programlisting></informalexample>
*
* Return value: a #GList of supported #GAppInfo<!---->s as
* "Send To" targets for the specified @files.
**/
GList *
thunar_sendto_model_get_matching (ThunarSendtoModel *sendto_model,
GList *files)
{
GFileMonitor *monitor;
GFile *file;
gchar **datadirs;
gchar *dir;
GList *handlers = NULL;
GList *hp;
GList *fp;
guint n;
const gchar **mime_types;
const gchar *content_type;
_thunar_return_val_if_fail (THUNAR_IS_SENDTO_MODEL (sendto_model), NULL);
/* no files, no sendto actions */
if (G_UNLIKELY (files == NULL))
return NULL;
/* connect to the monitor on-demand */
if (G_UNLIKELY (sendto_model->monitors == NULL))
{
/* watch all possible sendto directories */
datadirs = xfce_resource_dirs (XFCE_RESOURCE_DATA);
for (n = 0; datadirs[n] != NULL; ++n)
{
/* determine the path to the sendto directory */
dir = g_build_filename (datadirs[n], "Thunar", "sendto", NULL);
file = g_file_new_for_path (dir);
/* watch the directory for changes */
monitor = g_file_monitor_directory (file, G_FILE_MONITOR_NONE, NULL, NULL);
if (G_LIKELY (monitor != NULL))
{
g_signal_connect (monitor, "changed", G_CALLBACK (thunar_sendto_model_event), sendto_model);
sendto_model->monitors = g_list_prepend (sendto_model->monitors, monitor);
}
g_object_unref (file);
g_free (dir);
}
g_strfreev (datadirs);
/* load the model */
thunar_sendto_model_load (sendto_model);
}
/* test all handlers */
for (hp = sendto_model->handlers; hp != NULL; hp = hp->next)
{
/* FIXME Ignore GAppInfos which don't support multiple file arguments */
/* ignore the handler if it doesn't support URIs, but we don't have a local file */
if (!g_app_info_supports_uris (hp->data))
{
/* check if we have any non-local files */
for (fp = files; fp != NULL; fp = fp->next)
if (!thunar_file_is_local (fp->data))
break;
/* check if the test failed */
if (G_UNLIKELY (fp != NULL))
continue;
}
/* check if we need to test mime types for this handler */
mime_types = g_object_get_data (G_OBJECT (hp->data), "mime-types");
if (mime_types != NULL)
{
/* each file must match atleast one of the specified mime types */
for (fp = files; fp != NULL; fp = fp->next)
{
/* each file must be supported by one of the mime types */
for (n = 0; mime_types[n] != NULL; ++n)
{
content_type = thunar_file_get_content_type (fp->data);
if (g_content_type_equals (content_type, mime_types[n]))
break;
}
/* check if all mime types failed */
if (mime_types[n] == NULL)
break;
}
/* check if the test failed */
if (G_UNLIKELY (fp != NULL))
continue;
}
/* the handler is supported */
handlers = g_list_prepend (handlers, g_object_ref (G_OBJECT (hp->data)));
}
return handlers;
}