thunar 4.20.2 upstream

Tree of tag thunar-4.20.2 (commit 11a519f) from https://gitlab.xfce.org/xfce/thunar.
Upstream history is not included here to keep the repository small.
This commit is contained in:
4aiman 2025-02-10 09:12:59 +01:00
commit e969f94310
430 changed files with 482388 additions and 0 deletions

29
plugins/Makefile.am Normal file
View file

@ -0,0 +1,29 @@
if THUNAR_PLUGIN_APR
THUNAR_APR_SUBDIRS = thunar-apr
endif
if THUNAR_PLUGIN_SBR
THUNAR_SBR_SUBDIRS = thunar-sbr
endif
if THUNAR_PLUGIN_TPA
THUNAR_TPA_SUBDIRS = thunar-tpa
endif
if THUNAR_PLUGIN_UCA
THUNAR_UCA_SUBDIRS = thunar-uca
endif
if THUNAR_PLUGIN_WALLPAPER
THUNAR_WALLPAPER_SUBDIRS = thunar-wallpaper
endif
SUBDIRS = \
$(THUNAR_APR_SUBDIRS) \
$(THUNAR_SBR_SUBDIRS) \
$(THUNAR_TPA_SUBDIRS) \
$(THUNAR_UCA_SUBDIRS) \
$(THUNAR_WALLPAPER_SUBDIRS) \
thunar-sendto-email
# vi:set ts=8 sw=8 noet ai nocindent syntax=automake:

View file

@ -0,0 +1,60 @@
AM_CPPFLAGS = \
-I$(top_builddir) \
-I$(top_builddir)/plugins \
-I$(top_srcdir) \
-I$(top_srcdir)/plugins \
-DG_LOG_DOMAIN=\"thunar-apr\" \
-DPACKAGE_LOCALE_DIR=\"$(localedir)\" \
$(PLATFORM_CPPFLAGS)
extensionsdir = $(libdir)/thunarx-$(THUNARX_VERSION_API)
extensions_LTLIBRARIES = \
thunar-apr.la
thunar_apr_la_SOURCES = \
thunar-apr-abstract-page.c \
thunar-apr-abstract-page.h \
thunar-apr-desktop-page.c \
thunar-apr-desktop-page.h \
thunar-apr-image-page.c \
thunar-apr-image-page.h \
thunar-apr-plugin.c \
thunar-apr-private.c \
thunar-apr-private.h \
thunar-apr-provider.c \
thunar-apr-provider.h
thunar_apr_la_CFLAGS = \
$(EXIF_CFLAGS) \
$(EXO_CFLAGS) \
$(LIBXFCE4UTIL_CFLAGS) \
$(LIBXFCE4UI_CFLAGS) \
$(GLIB_CFLAGS) \
$(GTK_CFLAGS) \
$(PLATFORM_CFLAGS)
thunar_apr_la_LDFLAGS = \
-avoid-version \
-export-dynamic \
-no-undefined \
-export-symbols-regex "^thunar_extension_(shutdown|initialize|list_types)" \
-module \
$(PLATFORM_LDFLAGS)
thunar_apr_la_LIBADD = \
$(top_builddir)/thunarx/libthunarx-$(THUNARX_VERSION_API).la \
$(EXIF_LIBS) \
$(EXO_LIBS) \
$(LIBXFCE4UTIL_LIBS) \
$(LIBXFCE4UI_LIBS) \
$(GLIB_LIBS) \
$(GTK_LIBS)
thunar_apr_la_DEPENDENCIES = \
$(top_builddir)/thunarx/libthunarx-$(THUNARX_VERSION_API).la
EXTRA_DIST = \
README.md
# vi:set ts=8 sw=8 noet ai nocindent syntax=automake:

View file

@ -0,0 +1,13 @@
Thunar Advanced Properties (thunar-apr)
=======================================
Thunar-apr is an extension to Thunar, which provides additional pages forthe file properties dialog, that are displayed for certain kinds of files:
* Launcher/Link
* Image
The Launcher/Link page is displayed for .desktop files and allows users to easily edit the most important values (i.e. the command for a launcher and the URL for a link).
The Image page is displayed for all kinds of image files supported by the GTK+ version used by Thunar. The page displays atleast the exact format of the image and the dimensions in pixels. If the plugin is build with support for libexif (you can explicitly disable this using the `--disable-exif` configure option) and the image file includes Exif tags, the most important Exif tags found in the image file will also be displayed.
By default the thunar-apr extension will be installed, but as with every extension, it will slightly increase the resource usage of Thunar (this shouldn't be a real problem unless you're targeting an embedded system), and so you can pass `--disable-apr-plugin` to configure and the plugin won't be built and installed. Since it is an extension, you can also easily uninstall it afterwards by removing the thunar-apr.so file from the lib/thunarx-2/ directory of your installation (be sure to quit Thunar before removing files though).

View file

@ -0,0 +1,254 @@
/* vi:set et ai sw=2 sts=2 ts=2: */
/*-
* Copyright (c) 2005-2006 Benedikt Meurer <benny@xfce.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; 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
#include <thunar-apr/thunar-apr-abstract-page.h>
#include <thunar-apr/thunar-apr-private.h>
/* Property identifiers */
enum
{
PROP_0,
PROP_FILE,
};
/* Signal identifiers */
enum
{
FILE_CHANGED,
LAST_SIGNAL,
};
static void
thunar_apr_abstract_page_dispose (GObject *object);
static void
thunar_apr_abstract_page_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec);
static void
thunar_apr_abstract_page_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec);
static void
thunar_apr_abstract_page_file_changed (ThunarAprAbstractPage *abstract_page,
ThunarxFileInfo *file);
static guint abstract_page_signals[LAST_SIGNAL];
THUNARX_DEFINE_ABSTRACT_TYPE (ThunarAprAbstractPage,
thunar_apr_abstract_page,
THUNARX_TYPE_PROPERTY_PAGE);
static void
thunar_apr_abstract_page_class_init (ThunarAprAbstractPageClass *klass)
{
GObjectClass *gobject_class;
gobject_class = G_OBJECT_CLASS (klass);
gobject_class->dispose = thunar_apr_abstract_page_dispose;
gobject_class->get_property = thunar_apr_abstract_page_get_property;
gobject_class->set_property = thunar_apr_abstract_page_set_property;
/**
* ThunarAprAbstractPage:file:
*
* The #ThunarxFileInfo for the file being displayed by
* this property page.
**/
g_object_class_install_property (gobject_class,
PROP_FILE,
g_param_spec_object ("file", "file", "file",
THUNARX_TYPE_FILE_INFO,
G_PARAM_READWRITE));
/**
* ThunarAprAbstractPage::file-changed:
* @abstract_page : a #ThunarAprAbstractPage.
* @file : a #ThunarxFileInfo.
*
* Emitted by @abstract_page whenever the associated
* @file changes.
**/
abstract_page_signals[FILE_CHANGED] =
g_signal_new ("file-changed",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (ThunarAprAbstractPageClass, file_changed),
NULL, NULL,
g_cclosure_marshal_VOID__OBJECT,
G_TYPE_NONE, 1,
THUNARX_TYPE_FILE_INFO);
}
static void
thunar_apr_abstract_page_init (ThunarAprAbstractPage *abstract_page)
{
/* be sure to setup the i18n support for the thunar-apr plugin */
thunar_apr_i18n_init ();
}
static void
thunar_apr_abstract_page_dispose (GObject *object)
{
ThunarAprAbstractPage *abstract_page = THUNAR_APR_ABSTRACT_PAGE (object);
/* disconnect from the file */
thunar_apr_abstract_page_set_file (abstract_page, NULL);
(*G_OBJECT_CLASS (thunar_apr_abstract_page_parent_class)->dispose) (object);
}
static void
thunar_apr_abstract_page_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
ThunarAprAbstractPage *abstract_page = THUNAR_APR_ABSTRACT_PAGE (object);
switch (prop_id)
{
case PROP_FILE:
g_value_set_object (value, thunar_apr_abstract_page_get_file (abstract_page));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
thunar_apr_abstract_page_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
ThunarAprAbstractPage *abstract_page = THUNAR_APR_ABSTRACT_PAGE (object);
switch (prop_id)
{
case PROP_FILE:
thunar_apr_abstract_page_set_file (abstract_page, g_value_get_object (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
thunar_apr_abstract_page_file_changed (ThunarAprAbstractPage *abstract_page,
ThunarxFileInfo *file)
{
/* emit the "file-changed" signal */
g_signal_emit (G_OBJECT (abstract_page), abstract_page_signals[FILE_CHANGED], 0, file);
}
/**
* thunar_apr_abstract_page_get_file:
* @abstract_page : a #ThunarAprAbstractPage.
*
* Returns the #ThunarxFileInfo for the file being
* displayed by the @abstract_page.
*
* Return value: the file for @abstract_page.
**/
ThunarxFileInfo *
thunar_apr_abstract_page_get_file (ThunarAprAbstractPage *abstract_page)
{
g_return_val_if_fail (THUNAR_APR_IS_ABSTRACT_PAGE (abstract_page), NULL);
return abstract_page->file;
}
/**
* thunar_apr_abstract_page_set_file:
* @abstract_page : a #ThunarAprAbstractPage.
* @file : a #ThunarxFileInfo or %NULL.
*
* Sets the #ThunarxFileInfo being displayed by the
* @abstract_page to @file. If @file is %NULL, the
* property will be reset.
**/
void
thunar_apr_abstract_page_set_file (ThunarAprAbstractPage *abstract_page,
ThunarxFileInfo *file)
{
g_return_if_fail (THUNAR_APR_IS_ABSTRACT_PAGE (abstract_page));
g_return_if_fail (file == NULL || THUNARX_IS_FILE_INFO (file));
/* verify that we don't already display that file */
if (G_UNLIKELY (abstract_page->file == file))
return;
/* disconnect from the previous file */
if (G_UNLIKELY (abstract_page->file != NULL))
{
g_signal_handlers_disconnect_by_func (G_OBJECT (abstract_page->file), thunar_apr_abstract_page_file_changed, abstract_page);
g_object_unref (G_OBJECT (abstract_page->file));
}
/* activate the new file */
abstract_page->file = file;
/* connect to the new file */
if (G_LIKELY (file != NULL))
{
/* connect "changed" signal and take a reference */
g_signal_connect_swapped (G_OBJECT (file), "changed", G_CALLBACK (thunar_apr_abstract_page_file_changed), abstract_page);
g_object_ref (G_OBJECT (file));
/* update the initial state */
thunar_apr_abstract_page_file_changed (abstract_page, file);
}
/* notify listeners */
g_object_notify (G_OBJECT (abstract_page), "file");
}

View file

@ -0,0 +1,66 @@
/* vi:set et ai sw=2 sts=2 ts=2: */
/*-
* Copyright (c) 2005-2006 Benedikt Meurer <benny@xfce.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#ifndef __THUNAR_APR_ABSTRACT_PAGE_H__
#define __THUNAR_APR_ABSTRACT_PAGE_H__
#include <thunarx/thunarx.h>
G_BEGIN_DECLS;
typedef struct _ThunarAprAbstractPageClass ThunarAprAbstractPageClass;
typedef struct _ThunarAprAbstractPage ThunarAprAbstractPage;
#define THUNAR_APR_TYPE_ABSTRACT_PAGE (thunar_apr_abstract_page_get_type ())
#define THUNAR_APR_ABSTRACT_PAGE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), THUNAR_APR_TYPE_ABSTRACT_PAGE, ThunarAprAbstractPage))
#define THUNAR_APR_ABSTRACT_PAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), THUNAR_APR_TYPE_ABSTRACT_PAGE, ThunarAprAbstractPageClass))
#define THUNAR_APR_IS_ABSTRACT_PAGE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), THUNAR_APR_TYPE_ABSTRACT_PAGE))
#define THUNAR_APR_IS_ABSTRACT_PAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), THUNAR_APR_TYPE_ABSTRACT_PAGE))
#define THUNAR_APR_ABSTRACT_PAGE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), THUNAR_APR_TYPE_ABSTRACT_PAGE, ThunarAprAbstractPageClass))
struct _ThunarAprAbstractPageClass
{
ThunarxPropertyPageClass __parent__;
/* signals */
void (*file_changed) (ThunarAprAbstractPage *abstract_page,
ThunarxFileInfo *file);
};
struct _ThunarAprAbstractPage
{
ThunarxPropertyPage __parent__;
ThunarxFileInfo *file;
};
GType
thunar_apr_abstract_page_get_type (void) G_GNUC_CONST;
void
thunar_apr_abstract_page_register_type (ThunarxProviderPlugin *plugin);
ThunarxFileInfo *
thunar_apr_abstract_page_get_file (ThunarAprAbstractPage *abstract_page);
void
thunar_apr_abstract_page_set_file (ThunarAprAbstractPage *abstract_page,
ThunarxFileInfo *file);
G_END_DECLS;
#endif /* !__THUNAR_APR_ABSTRACT_PAGE_H__ */

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,45 @@
/* vi:set et ai sw=2 sts=2 ts=2: */
/*-
* Copyright (c) 2005-2006 Benedikt Meurer <benny@xfce.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#ifndef __THUNAR_APR_DESKTOP_PAGE_H__
#define __THUNAR_APR_DESKTOP_PAGE_H__
#include <thunar-apr/thunar-apr-abstract-page.h>
G_BEGIN_DECLS;
typedef struct _ThunarAprDesktopPageClass ThunarAprDesktopPageClass;
typedef struct _ThunarAprDesktopPage ThunarAprDesktopPage;
#define THUNAR_APR_TYPE_DESKTOP_PAGE (thunar_apr_desktop_page_get_type ())
#define THUNAR_APR_DESKTOP_PAGE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), THUNAR_APR_TYPE_DESKTOP_PAGE, ThunarAprDesktopPage))
#define THUNAR_APR_DESKTOP_PAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), THUNAR_APR_TYPE_DESKTOP_PAGE, ThunarAprDesktopPageClass))
#define THUNAR_APR_IS_DESKTOP_PAGE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), THUNAR_APR_TYPE_DESKTOP_PAGE))
#define THUNAR_APR_IS_DESKTOP_PAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), THUNAR_APR_TYPE_DESKTOP_PAGE))
#define THUNAR_APR_DESKTOP_PAGE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), THUNAR_APR_TYPE_DESKTOP_PAGE, ThunarAprDesktopPageClass))
GType
thunar_apr_desktop_page_get_type (void) G_GNUC_CONST;
void
thunar_apr_desktop_page_register_type (ThunarxProviderPlugin *plugin);
G_END_DECLS;
#endif /* !__THUNAR_APR_DESKTOP_PAGE_H__ */

View file

@ -0,0 +1,319 @@
/* vi:set et ai sw=2 sts=2 ts=2: */
/*-
* Copyright (c) 2005-2006 Benedikt Meurer <benny@xfce.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; 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
#include <exo/exo.h>
#include <thunar-apr/thunar-apr-image-page.h>
#ifdef HAVE_EXIF
#include <libexif/exif-data.h>
#endif
static void
thunar_apr_image_page_file_changed (ThunarAprAbstractPage *abstract_page,
ThunarxFileInfo *file);
#ifdef HAVE_EXIF
static const struct
{
const gchar *name;
ExifTag tag;
}
/* clang-format off */
TAIP_EXIF[] =
{
{ N_ ("Date Taken:"), EXIF_TAG_DATE_TIME_ORIGINAL, },
{ N_ ("Camera Brand:"), EXIF_TAG_MAKE, },
{ N_ ("Camera Model:"), EXIF_TAG_MODEL, },
{ N_ ("Exposure Time:"), EXIF_TAG_EXPOSURE_TIME, },
{ N_ ("Exposure Program:"), EXIF_TAG_EXPOSURE_PROGRAM, },
{ N_ ("Aperture Value:"), EXIF_TAG_APERTURE_VALUE, },
{ N_ ("Metering Mode:"), EXIF_TAG_METERING_MODE, },
{ N_ ("Flash Fired:"), EXIF_TAG_FLASH, },
{ N_ ("Focal Length:"), EXIF_TAG_FOCAL_LENGTH, },
{ N_ ("Shutter Speed:"), EXIF_TAG_SHUTTER_SPEED_VALUE, },
{ N_ ("ISO Speed Ratings:"), EXIF_TAG_ISO_SPEED_RATINGS, },
{ N_ ("Software:"), EXIF_TAG_SOFTWARE, },
{ N_ ("Description:"), EXIF_TAG_IMAGE_DESCRIPTION, },
{ N_ ("Comment:"), EXIF_TAG_USER_COMMENT, },
};
/* clang-format on */
#endif
struct _ThunarAprImagePageClass
{
ThunarAprAbstractPageClass __parent__;
};
struct _ThunarAprImagePage
{
ThunarAprAbstractPage __parent__;
GtkWidget *type_label;
GtkWidget *dimensions_label;
#ifdef HAVE_EXIF
GtkWidget *exif_labels[G_N_ELEMENTS (TAIP_EXIF)];
#endif
};
THUNARX_DEFINE_TYPE (ThunarAprImagePage,
thunar_apr_image_page,
THUNAR_APR_TYPE_ABSTRACT_PAGE);
static void
thunar_apr_image_page_class_init (ThunarAprImagePageClass *klass)
{
ThunarAprAbstractPageClass *thunarapr_abstract_page_class;
thunarapr_abstract_page_class = THUNAR_APR_ABSTRACT_PAGE_CLASS (klass);
thunarapr_abstract_page_class->file_changed = thunar_apr_image_page_file_changed;
}
static void
thunar_apr_image_page_init (ThunarAprImagePage *image_page)
{
AtkRelationSet *relations;
PangoAttribute *attribute;
PangoAttrList *attr_list;
AtkRelation *relation;
AtkObject *object;
GtkWidget *label;
GtkWidget *grid;
#ifdef HAVE_EXIF
GtkWidget *spacer;
guint n;
#endif
gtk_container_set_border_width (GTK_CONTAINER (image_page), 12);
thunarx_property_page_set_label (THUNARX_PROPERTY_PAGE (image_page), _("Image"));
/* allocate shared bold Pango attributes */
attr_list = pango_attr_list_new ();
attribute = pango_attr_weight_new (PANGO_WEIGHT_BOLD);
attribute->start_index = 0;
attribute->end_index = -1;
pango_attr_list_insert (attr_list, attribute);
grid = gtk_grid_new ();
gtk_grid_set_column_spacing (GTK_GRID (grid), 12);
gtk_grid_set_row_spacing (GTK_GRID (grid), 6);
gtk_container_add (GTK_CONTAINER (image_page), grid);
gtk_widget_show (grid);
label = gtk_label_new (_("Image Type:"));
gtk_label_set_xalign (GTK_LABEL (label), 1.0f);
gtk_label_set_attributes (GTK_LABEL (label), attr_list);
gtk_grid_attach (GTK_GRID (grid), label, 0, 0, 1, 1);
gtk_widget_show (label);
image_page->type_label = gtk_label_new ("");
gtk_label_set_selectable (GTK_LABEL (image_page->type_label), TRUE);
gtk_label_set_xalign (GTK_LABEL (image_page->type_label), 0.0f);
gtk_label_set_ellipsize (GTK_LABEL (image_page->type_label), PANGO_ELLIPSIZE_END);
gtk_widget_set_hexpand (image_page->type_label, TRUE);
gtk_grid_attach (GTK_GRID (grid), image_page->type_label, 1, 0, 1, 1);
gtk_widget_show (image_page->type_label);
/* set Atk label relation for the label */
object = gtk_widget_get_accessible (image_page->type_label);
relations = atk_object_ref_relation_set (gtk_widget_get_accessible (label));
relation = atk_relation_new (&object, 1, ATK_RELATION_LABEL_FOR);
atk_relation_set_add (relations, relation);
g_object_unref (G_OBJECT (relation));
g_object_unref (relations);
label = gtk_label_new (_("Image Size:"));
gtk_label_set_xalign (GTK_LABEL (label), 1.0f);
gtk_label_set_attributes (GTK_LABEL (label), attr_list);
gtk_grid_attach (GTK_GRID (grid), label, 0, 1, 1, 1);
gtk_widget_show (label);
image_page->dimensions_label = gtk_label_new ("");
gtk_label_set_selectable (GTK_LABEL (image_page->dimensions_label), TRUE);
gtk_label_set_xalign (GTK_LABEL (image_page->dimensions_label), 0.0f);
gtk_label_set_ellipsize (GTK_LABEL (image_page->dimensions_label), PANGO_ELLIPSIZE_END);
gtk_widget_set_hexpand (image_page->dimensions_label, TRUE);
gtk_grid_attach (GTK_GRID (grid), image_page->dimensions_label, 1, 1, 1, 1);
gtk_widget_show (image_page->dimensions_label);
/* set Atk label relation for the label */
object = gtk_widget_get_accessible (image_page->dimensions_label);
relations = atk_object_ref_relation_set (gtk_widget_get_accessible (label));
relation = atk_relation_new (&object, 1, ATK_RELATION_LABEL_FOR);
atk_relation_set_add (relations, relation);
g_object_unref (G_OBJECT (relation));
g_object_unref (relations);
#ifdef HAVE_EXIF
/* some spacing between the General info and the Exif info */
spacer = g_object_new (GTK_TYPE_BOX, "orientation", GTK_ORIENTATION_VERTICAL, "height-request", 6, NULL);
gtk_grid_attach (GTK_GRID (grid), spacer, 0, 2, 2, 1);
gtk_widget_show (spacer);
/* add labels for the Exif info */
for (n = 0; n < G_N_ELEMENTS (TAIP_EXIF); ++n)
{
label = gtk_label_new (_(TAIP_EXIF[n].name));
gtk_label_set_xalign (GTK_LABEL (label), 1.0f);
gtk_label_set_attributes (GTK_LABEL (label), attr_list);
gtk_grid_attach (GTK_GRID (grid), label, 0, n + 3, 1, 1);
gtk_widget_show (label);
image_page->exif_labels[n] = gtk_label_new ("");
gtk_label_set_selectable (GTK_LABEL (image_page->exif_labels[n]), TRUE);
gtk_label_set_xalign (GTK_LABEL (image_page->exif_labels[n]), 0.0f);
gtk_label_set_ellipsize (GTK_LABEL (image_page->exif_labels[n]), PANGO_ELLIPSIZE_END);
gtk_widget_set_hexpand (image_page->exif_labels[n], TRUE);
gtk_grid_attach (GTK_GRID (grid), image_page->exif_labels[n], 1, n + 3, 1, 1);
gtk_widget_show (image_page->exif_labels[n]);
g_object_bind_property (G_OBJECT (image_page->exif_labels[n]), "visible", G_OBJECT (label), "visible", G_BINDING_SYNC_CREATE);
/* set Atk label relation for the label */
object = gtk_widget_get_accessible (image_page->exif_labels[n]);
relations = atk_object_ref_relation_set (gtk_widget_get_accessible (label));
relation = atk_relation_new (&object, 1, ATK_RELATION_LABEL_FOR);
atk_relation_set_add (relations, relation);
g_object_unref (G_OBJECT (relation));
g_object_unref (relations);
}
#endif
/* release shared bold Pango attributes */
pango_attr_list_unref (attr_list);
}
static void
thunar_apr_image_page_file_changed (ThunarAprAbstractPage *abstract_page,
ThunarxFileInfo *file)
{
ThunarAprImagePage *image_page = THUNAR_APR_IMAGE_PAGE (abstract_page);
GdkPixbufFormat *format;
gchar *filename;
gchar *text;
gchar *uri;
gint height;
gint width;
#ifdef HAVE_EXIF
ExifEntry *exif_entry;
ExifData *exif_data;
gchar exif_buffer[1024];
guint n;
#endif
/* determine the URI for the file */
uri = thunarx_file_info_get_uri (file);
if (G_UNLIKELY (uri == NULL))
return;
/* determine the local path of the file */
filename = g_filename_from_uri (uri, NULL, NULL);
if (G_LIKELY (filename != NULL))
{
/* determine the pixbuf format */
format = gdk_pixbuf_get_file_info (filename, &width, &height);
if (G_LIKELY (format != NULL))
{
/* update the "Image Type" label */
gchar *name = gdk_pixbuf_format_get_name (format);
gchar *desc = gdk_pixbuf_format_get_description (format);
text = g_strdup_printf ("%s (%s)", name, desc);
gtk_label_set_text (GTK_LABEL (image_page->type_label), text);
g_free (name);
g_free (desc);
g_free (text);
/* update the "Image Size" label */
text = g_strdup_printf (ngettext ("%dx%d pixel", "%dx%d pixels", width + height), width, height);
gtk_label_set_text (GTK_LABEL (image_page->dimensions_label), text);
g_free (text);
#ifdef HAVE_EXIF
/* hide all Exif labels (will be shown again if data is available) */
for (n = 0; n < G_N_ELEMENTS (TAIP_EXIF); ++n)
gtk_widget_hide (image_page->exif_labels[n]);
/* try to load the Exif data for the file */
exif_data = exif_data_new_from_file (filename);
if (G_LIKELY (exif_data != NULL))
{
/* update all Exif labels */
for (n = 0; n < G_N_ELEMENTS (TAIP_EXIF); ++n)
{
/* lookup the entry for the tag */
exif_entry = exif_data_get_entry (exif_data, TAIP_EXIF[n].tag);
if (G_LIKELY (exif_entry != NULL))
{
/* determine the value */
if (exif_entry_get_value (exif_entry, exif_buffer, sizeof (exif_buffer)) != NULL)
{
/* setup the label text */
text = (g_utf8_validate (exif_buffer, -1, NULL)) ? g_strdup (exif_buffer) : g_filename_display_name (exif_buffer);
gtk_label_set_text (GTK_LABEL (image_page->exif_labels[n]), text);
g_free (text);
/* show the label */
gtk_widget_show (image_page->exif_labels[n]);
}
}
}
/* cleanup */
exif_data_free (exif_data);
}
#endif
}
else
{
/* tell the user that we're unable to determine the file info */
gtk_label_set_text (GTK_LABEL (image_page->type_label), _("Unknown"));
gtk_label_set_text (GTK_LABEL (image_page->dimensions_label), _("Unknown"));
#ifdef HAVE_EXIF
/* hide all Exif labels */
for (n = 0; n < G_N_ELEMENTS (TAIP_EXIF); ++n)
gtk_widget_hide (image_page->exif_labels[n]);
#endif
}
}
/* cleanup */
g_free (filename);
g_free (uri);
}

View file

@ -0,0 +1,45 @@
/* vi:set et ai sw=2 sts=2 ts=2: */
/*-
* Copyright (c) 2005-2006 Benedikt Meurer <benny@xfce.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#ifndef __THUNAR_APR_IMAGE_PAGE_H__
#define __THUNAR_APR_IMAGE_PAGE_H__
#include <thunar-apr/thunar-apr-abstract-page.h>
G_BEGIN_DECLS;
typedef struct _ThunarAprImagePageClass ThunarAprImagePageClass;
typedef struct _ThunarAprImagePage ThunarAprImagePage;
#define THUNAR_APR_TYPE_IMAGE_PAGE (thunar_apr_image_page_get_type ())
#define THUNAR_APR_IMAGE_PAGE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), THUNAR_APR_TYPE_IMAGE_PAGE, ThunarAprImagePage))
#define THUNAR_APR_IMAGE_PAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), THUNAR_APR_TYPE_IMAGE_PAGE, ThunarAprImagePageClass))
#define THUNAR_APR_IS_IMAGE_PAGE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), THUNAR_APR_TYPE_IMAGE_PAGE))
#define THUNAR_APR_IS_IMAGE_PAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), THUNAR_APR_TYPE_IMAGE_PAGE))
#define THUNAR_APR_IMAGE_PAGE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), THUNAR_APR_TYPE_IMAGE_PAGE, ThunarAprImagePageClass))
GType
thunar_apr_image_page_get_type (void) G_GNUC_CONST;
void
thunar_apr_image_page_register_type (ThunarxProviderPlugin *plugin);
G_END_DECLS;
#endif /* !__THUNAR_APR_IMAGE_PAGE_H__ */

View file

@ -0,0 +1,91 @@
/* vi:set et ai sw=2 sts=2 ts=2: */
/*-
* Copyright (c) 2005-2006 Benedikt Meurer <benny@xfce.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; 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
#include <thunar-apr/thunar-apr-abstract-page.h>
#include <thunar-apr/thunar-apr-desktop-page.h>
#include <thunar-apr/thunar-apr-image-page.h>
#include <thunar-apr/thunar-apr-provider.h>
G_MODULE_EXPORT void
thunar_extension_initialize (ThunarxProviderPlugin *plugin);
G_MODULE_EXPORT void
thunar_extension_shutdown (void);
G_MODULE_EXPORT void
thunar_extension_list_types (const GType **types,
gint *n_types);
static GType type_list[1];
G_MODULE_EXPORT void
thunar_extension_initialize (ThunarxProviderPlugin *plugin)
{
const gchar *mismatch;
/* verify that the thunarx versions are compatible */
mismatch = thunarx_check_version (THUNARX_MAJOR_VERSION, THUNARX_MINOR_VERSION, THUNARX_MICRO_VERSION);
if (G_UNLIKELY (mismatch != NULL))
{
g_warning ("Version mismatch: %s", mismatch);
return;
}
#ifdef G_ENABLE_DEBUG
g_message ("Initializing ThunarApr extension");
#endif
/* register the types provided by this plugin */
thunar_apr_abstract_page_register_type (plugin);
thunar_apr_desktop_page_register_type (plugin);
thunar_apr_image_page_register_type (plugin);
thunar_apr_provider_register_type (plugin);
/* setup the plugin provider type list */
type_list[0] = THUNAR_APR_TYPE_PROVIDER;
}
G_MODULE_EXPORT void
thunar_extension_shutdown (void)
{
#ifdef G_ENABLE_DEBUG
g_message ("Shutting down ThunarApr extension");
#endif
}
G_MODULE_EXPORT void
thunar_extension_list_types (const GType **types,
gint *n_types)
{
*types = type_list;
*n_types = G_N_ELEMENTS (type_list);
}

View file

@ -0,0 +1,49 @@
/* vi:set et ai sw=2 sts=2 ts=2: */
/*-
* Copyright (c) 2006 Benedikt Meurer <benny@xfce.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; 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_LIBINTL_H
#include <libintl.h>
#endif
#ifdef HAVE_LOCALE_H
#include <locale.h>
#endif
#include <exo/exo.h>
#include <thunar-apr/thunar-apr-private.h>
/**
* thunar_apr_i18n_init:
*
* Sets up i18n support for the thunar-apr plugin.
**/
void
thunar_apr_i18n_init (void)
{
bindtextdomain (GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR);
#ifdef HAVE_BIND_TEXTDOMAIN_CODESET
bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
#endif
}

View file

@ -0,0 +1,33 @@
/* vi:set et ai sw=2 sts=2 ts=2: */
/*-
* Copyright (c) 2006 Benedikt Meurer <benny@xfce.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#ifndef __THUNAR_APR_PRIVATE_H__
#define __THUNAR_APR_PRIVATE_H__
#include <glib.h>
G_BEGIN_DECLS;
void
thunar_apr_i18n_init (void);
G_END_DECLS;
#endif /* !__THUNAR_APR_PRIVATE_H__ */

View file

@ -0,0 +1,135 @@
/* vi:set et ai sw=2 sts=2 ts=2: */
/*-
* Copyright (c) 2005-2006 Benedikt Meurer <benny@xfce.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; 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
#include <thunar-apr/thunar-apr-desktop-page.h>
#include <thunar-apr/thunar-apr-image-page.h>
#include <thunar-apr/thunar-apr-provider.h>
static void
thunar_apr_provider_property_page_provider_init (ThunarxPropertyPageProviderIface *iface);
static GList *
thunar_apr_provider_get_pages (ThunarxPropertyPageProvider *property_page_provider,
GList *files);
struct _ThunarAprProviderClass
{
GObjectClass __parent__;
};
struct _ThunarAprProvider
{
GObject __parent__;
};
THUNARX_DEFINE_TYPE_WITH_CODE (ThunarAprProvider,
thunar_apr_provider,
G_TYPE_OBJECT,
THUNARX_IMPLEMENT_INTERFACE (THUNARX_TYPE_PROPERTY_PAGE_PROVIDER,
thunar_apr_provider_property_page_provider_init));
static void
thunar_apr_provider_class_init (ThunarAprProviderClass *klass)
{
}
static void
thunar_apr_provider_property_page_provider_init (ThunarxPropertyPageProviderIface *iface)
{
iface->get_pages = thunar_apr_provider_get_pages;
}
static void
thunar_apr_provider_init (ThunarAprProvider *apr_provider)
{
}
static GList *
thunar_apr_provider_get_pages (ThunarxPropertyPageProvider *property_page_provider,
GList *files)
{
GSList *formats;
GSList *lp;
gchar **mime_types;
gchar *scheme;
GList *pages = NULL;
gint n;
/* we can handle only property pages for a single file */
if (G_UNLIKELY (files == NULL || files->next != NULL))
return NULL;
/* determine the URI scheme of the file (works only for local files) */
scheme = thunarx_file_info_get_uri_scheme (files->data);
if (G_LIKELY (strcmp (scheme, "file") == 0))
{
/* ThunarAprDesktopPage case */
if (G_LIKELY (pages == NULL))
{
/* check if we have a .desktop file here */
if (thunarx_file_info_has_mime_type (files->data, "application/x-desktop"))
pages = g_list_append (pages, g_object_new (THUNAR_APR_TYPE_DESKTOP_PAGE, "file", files->data, NULL));
}
/* ThunarAprImagePage case */
if (G_LIKELY (pages == NULL))
{
/* determine the supported GdkPixbuf formats */
formats = gdk_pixbuf_get_formats ();
for (lp = formats; lp != NULL && pages == NULL; lp = lp->next)
{
/* check if any of the mime types of this format matches */
mime_types = gdk_pixbuf_format_get_mime_types (lp->data);
for (n = 0; mime_types[n] != NULL && pages == NULL; ++n)
if (thunarx_file_info_has_mime_type (files->data, mime_types[n]))
pages = g_list_append (pages, g_object_new (THUNAR_APR_TYPE_IMAGE_PAGE, "file", files->data, NULL));
g_strfreev (mime_types);
}
g_slist_free (formats);
}
}
g_free (scheme);
return pages;
}

View file

@ -0,0 +1,45 @@
/* vi:set et ai sw=2 sts=2 ts=2: */
/*-
* Copyright (c) 2005-2006 Benedikt Meurer <benny@xfce.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#ifndef __THUNAR_APR_PROVIDER_H__
#define __THUNAR_APR_PROVIDER_H__
#include <thunarx/thunarx.h>
G_BEGIN_DECLS;
typedef struct _ThunarAprProviderClass ThunarAprProviderClass;
typedef struct _ThunarAprProvider ThunarAprProvider;
#define THUNAR_APR_TYPE_PROVIDER (thunar_apr_provider_get_type ())
#define THUNAR_APR_PROVIDER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), THUNAR_APR_TYPE_PROVIDER, ThunarAprProvider))
#define THUNAR_APR_PROVIDER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), THUNAR_APR_TYPE_PROVIDER, ThunarAprProviderClass))
#define THUNAR_APR_IS_PROVIDER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), THUNAR_APR_TYPE_PROVIDER))
#define THUNAR_APR_IS_PROVIDER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), THUNAR_APR_TYPE_PROVIDER))
#define THUNAR_APR_PROVIDER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), THUNAR_APR_TYPE_PROVIDER, ThunarAprProviderClass))
GType
thunar_apr_provider_get_type (void) G_GNUC_CONST;
void
thunar_apr_provider_register_type (ThunarxProviderPlugin *plugin);
G_END_DECLS;
#endif /* !__THUNAR_APR_PROVIDER_H__ */

View file

@ -0,0 +1,60 @@
AM_CPPFLAGS = \
-I$(top_builddir) \
-I$(top_builddir)/plugins \
-I$(top_srcdir) \
-I$(top_srcdir)/plugins \
-DG_LOG_DOMAIN=\"thunar-sbr\" \
$(PLATFORM_CPPFLAGS)
extensionsdir = $(libdir)/thunarx-$(THUNARX_VERSION_API)
extensions_LTLIBRARIES = \
thunar-sbr.la
thunar_sbr_la_SOURCES = \
thunar-sbr-case-renamer.c \
thunar-sbr-case-renamer.h \
thunar-sbr-date-renamer.c \
thunar-sbr-date-renamer.h \
thunar-sbr-enum-types.c \
thunar-sbr-enum-types.h \
thunar-sbr-insert-renamer.c \
thunar-sbr-insert-renamer.h \
thunar-sbr-number-renamer.c \
thunar-sbr-number-renamer.h \
thunar-sbr-plugin.c \
thunar-sbr-provider.c \
thunar-sbr-provider.h \
thunar-sbr-remove-renamer.c \
thunar-sbr-remove-renamer.h \
thunar-sbr-replace-renamer.c \
thunar-sbr-replace-renamer.h
thunar_sbr_la_CFLAGS = \
$(EXIF_CFLAGS) \
$(EXO_CFLAGS) \
$(GLIB_CFLAGS) \
$(PCRE2_CFLAGS) \
$(PLATFORM_CFLAGS)
thunar_sbr_la_LDFLAGS = \
-avoid-version \
-export-dynamic \
-no-undefined \
-export-symbols-regex "^thunar_extension_(shutdown|initialize|list_types)" \
-module \
$(PLATFORM_LDFLAGS)
thunar_sbr_la_LIBADD = \
$(top_builddir)/thunarx/libthunarx-$(THUNARX_VERSION_API).la \
$(EXIF_LIBS) \
$(EXO_LIBS) \
$(GLIB_LIBS) \
$(PCRE2_LIBS)
thunar_sbr_la_DEPENDENCIES = \
$(top_builddir)/thunarx/libthunarx-$(THUNARX_VERSION_API).la
EXTRA_DIST = \
README.md
# vi:set ts=8 sw=8 noet ai nocindent syntax=automake:

View file

@ -0,0 +1,24 @@
Thunar Simple Builtin Renamers (thunar-sbr)
===========================================
Thunar-sbr is an extension to Thunar, which includes a bunch of simple renamers for the bulk rename dialog:
* Insert / Overwrite
* Insert Date / Time
* Numbering
* Remove Characters
* Search & Replace
* Uppercase / Lowercase
By default the thunar-sbr extension will be installed, but as with every extension, it will slightly increase the resource usage of Thunar (this shouldn't be a real problem unless you're targeting an embedded system), and so you can pass `--disable-sbr-plugin` to configure and the plugin won't be built and installed. Since it is an extension, you can also easily uninstall it afterwards by removing the thunar-sbr.so file from the lib/thunarx-2/ directory of your installation (be sure to quit Thunar before removing files though).
Regular Expressions
-------------------
The "Search & Replace" renamer can be built with support for regular expressions using the Perl-compatible regular expression (PCRE) library version 6.0 or above. The reasons to use PCRE instead of the POSIX regular expressions, that are part of the system C library, are:
* PCRE supports UTF-8, which Thunar uses internally for all user visible texts (be sure to enable this when installing PCRE using the `--enable-utf8` configure switch).
* PCRE uses a more consistent syntax, which is easier to understand for most people.

View file

@ -0,0 +1,319 @@
/* vi:set et ai sw=2 sts=2 ts=2: */
/*-
* Copyright (c) 2006 Benedikt Meurer <benny@xfce.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; 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
#include <exo/exo.h>
#include <thunar-sbr/thunar-sbr-case-renamer.h>
/* Property identifiers */
enum
{
PROP_0,
PROP_MODE,
};
static void
thunar_sbr_case_renamer_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec);
static void
thunar_sbr_case_renamer_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec);
static gchar *
thunar_sbr_case_renamer_process (ThunarxRenamer *renamer,
ThunarxFileInfo *file,
const gchar *text,
guint idx);
struct _ThunarSbrCaseRenamerClass
{
ThunarxRenamerClass __parent__;
};
struct _ThunarSbrCaseRenamer
{
ThunarxRenamer __parent__;
ThunarSbrCaseRenamerMode mode;
};
THUNARX_DEFINE_TYPE (ThunarSbrCaseRenamer, thunar_sbr_case_renamer, THUNARX_TYPE_RENAMER);
static void
thunar_sbr_case_renamer_class_init (ThunarSbrCaseRenamerClass *klass)
{
ThunarxRenamerClass *thunarxrenamer_class;
GObjectClass *gobject_class;
gobject_class = G_OBJECT_CLASS (klass);
gobject_class->get_property = thunar_sbr_case_renamer_get_property;
gobject_class->set_property = thunar_sbr_case_renamer_set_property;
thunarxrenamer_class = THUNARX_RENAMER_CLASS (klass);
thunarxrenamer_class->process = thunar_sbr_case_renamer_process;
/**
* ThunarSbrCaseRenamer:mode:
*
* The #ThunarSbrCaseRenamerMode for this
* #ThunarSbrCaseRenamer instance.
**/
g_object_class_install_property (gobject_class,
PROP_MODE,
g_param_spec_enum ("mode", "mode", "mode",
THUNAR_SBR_TYPE_CASE_RENAMER_MODE,
THUNAR_SBR_CASE_RENAMER_MODE_LOWER,
G_PARAM_READWRITE));
}
static void
thunar_sbr_case_renamer_init (ThunarSbrCaseRenamer *case_renamer)
{
AtkRelationSet *relations;
AtkRelation *relation;
GEnumClass *klass;
AtkObject *object;
GtkWidget *combo;
GtkWidget *label;
GtkWidget *hbox;
guint n;
hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
gtk_box_pack_start (GTK_BOX (case_renamer), hbox, FALSE, FALSE, 0);
gtk_widget_show (hbox);
label = gtk_label_new_with_mnemonic (_("Con_vert to:"));
gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
gtk_widget_show (label);
combo = gtk_combo_box_text_new ();
klass = g_type_class_ref (THUNAR_SBR_TYPE_CASE_RENAMER_MODE);
for (n = 0; n < klass->n_values; ++n)
gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo), _(klass->values[n].value_nick));
g_object_bind_property (G_OBJECT (case_renamer), "mode", G_OBJECT (combo), "active", G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
gtk_box_pack_start (GTK_BOX (hbox), combo, FALSE, FALSE, 0);
gtk_label_set_mnemonic_widget (GTK_LABEL (label), combo);
g_type_class_unref (klass);
gtk_widget_show (combo);
/* set Atk label relation for the combo */
object = gtk_widget_get_accessible (combo);
relations = atk_object_ref_relation_set (gtk_widget_get_accessible (label));
relation = atk_relation_new (&object, 1, ATK_RELATION_LABEL_FOR);
atk_relation_set_add (relations, relation);
g_object_unref (G_OBJECT (relation));
g_object_unref (relations);
}
static void
thunar_sbr_case_renamer_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
ThunarSbrCaseRenamer *case_renamer = THUNAR_SBR_CASE_RENAMER (object);
switch (prop_id)
{
case PROP_MODE:
g_value_set_enum (value, thunar_sbr_case_renamer_get_mode (case_renamer));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
thunar_sbr_case_renamer_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
ThunarSbrCaseRenamer *case_renamer = THUNAR_SBR_CASE_RENAMER (object);
switch (prop_id)
{
case PROP_MODE:
thunar_sbr_case_renamer_set_mode (case_renamer, g_value_get_enum (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static gchar *
tscr_utf8_strcase (const gchar *text,
gboolean title_case)
{
const gchar *t;
gboolean upper = TRUE;
gunichar c;
GString *result;
/* allocate a new string for the result */
result = g_string_sized_new (32);
/* convert the input text */
for (t = text; *t != '\0'; t = g_utf8_next_char (t))
{
/* check the next char */
c = g_utf8_get_char (t);
if (title_case && g_unichar_isspace (c))
{
upper = TRUE;
}
else if (upper && g_unichar_isalpha (c))
{
c = g_unichar_toupper (c);
upper = FALSE;
}
else
{
c = g_unichar_tolower (c);
}
/* append the char to the result */
g_string_append_unichar (result, c);
}
return g_string_free (result, FALSE);
}
static gchar *
thunar_sbr_case_renamer_process (ThunarxRenamer *renamer,
ThunarxFileInfo *file,
const gchar *text,
guint idx)
{
ThunarSbrCaseRenamer *case_renamer = THUNAR_SBR_CASE_RENAMER (renamer);
switch (case_renamer->mode)
{
case THUNAR_SBR_CASE_RENAMER_MODE_LOWER:
return g_utf8_strdown (text, -1);
case THUNAR_SBR_CASE_RENAMER_MODE_UPPER:
return g_utf8_strup (text, -1);
case THUNAR_SBR_CASE_RENAMER_MODE_TITLE:
return tscr_utf8_strcase (text, TRUE);
case THUNAR_SBR_CASE_RENAMER_MODE_FIRST_UPPER:
return tscr_utf8_strcase (text, FALSE);
default:
g_assert_not_reached ();
return NULL;
}
}
/**
* thunar_sbr_case_renamer_new:
*
* Allocates a new #ThunarSbrCaseRenamer instance.
*
* Return value: the newly allocated #ThunarSbrCaseRenamer.
**/
ThunarSbrCaseRenamer *
thunar_sbr_case_renamer_new (void)
{
return g_object_new (THUNAR_SBR_TYPE_CASE_RENAMER,
"name", _("Uppercase / Lowercase"),
NULL);
}
/**
* thunar_sbr_case_renamer_get_mode:
* @case_renamer : a #ThunarSbrCaseRenamer.
*
* Returns the current #ThunarSbrCaseRenamerMode
* for the @case_renamer.
*
* Return value: the current mode of @case_renamer.
**/
ThunarSbrCaseRenamerMode
thunar_sbr_case_renamer_get_mode (ThunarSbrCaseRenamer *case_renamer)
{
g_return_val_if_fail (THUNAR_SBR_IS_CASE_RENAMER (case_renamer), THUNAR_SBR_CASE_RENAMER_MODE_LOWER);
return case_renamer->mode;
}
/**
* thunar_sbr_case_renamer_set_mode:
* @case_renamer : a #ThunarSbrCaseRenamer.
* @mode : the new #ThunarSbrCaseRenamerMode.
*
* Sets the #ThunarSbrCaseRenamerMode of the @case_renamer
* to the specified @mode.
**/
void
thunar_sbr_case_renamer_set_mode (ThunarSbrCaseRenamer *case_renamer,
ThunarSbrCaseRenamerMode mode)
{
g_return_if_fail (THUNAR_SBR_IS_CASE_RENAMER (case_renamer));
/* check if we already use that mode */
if (G_UNLIKELY (case_renamer->mode == mode))
return;
/* apply the new mode */
case_renamer->mode = mode;
/* notify listeners */
g_object_notify (G_OBJECT (case_renamer), "mode");
/* emit the "changed" signal */
thunarx_renamer_changed (THUNARX_RENAMER (case_renamer));
}

View file

@ -0,0 +1,54 @@
/* vi:set et ai sw=2 sts=2 ts=2: */
/*-
* Copyright (c) 2006 Benedikt Meurer <benny@xfce.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#ifndef __THUNAR_SBR_CASE_RENAMER_H__
#define __THUNAR_SBR_CASE_RENAMER_H__
#include <thunar-sbr/thunar-sbr-enum-types.h>
G_BEGIN_DECLS;
typedef struct _ThunarSbrCaseRenamerClass ThunarSbrCaseRenamerClass;
typedef struct _ThunarSbrCaseRenamer ThunarSbrCaseRenamer;
#define THUNAR_SBR_TYPE_CASE_RENAMER (thunar_sbr_case_renamer_get_type ())
#define THUNAR_SBR_CASE_RENAMER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), THUNAR_SBR_TYPE_CASE_RENAMER, ThunarSbrCaseRenamer))
#define THUNAR_SBR_CASE_RENAMER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), THUNAR_SBR_TYPE_CASE_RENAMER, ThunarSbrCaseRenamerClass))
#define THUNAR_SBR_IS_CASE_RENAMER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), THUNAR_SBR_TYPE_CASE_RENAMER))
#define THUNAR_SBR_IS_CASE_RENAMER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), THUNAR_SBR_TYPE_CASE_RENAMER))
#define THUNAR_SBR_CASE_RENAMER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), THUNAR_SBR_TYPE_CASE_RENAMER, ThunarSbrCaseRenamerClass))
GType
thunar_sbr_case_renamer_get_type (void) G_GNUC_CONST;
void
thunar_sbr_case_renamer_register_type (ThunarxProviderPlugin *plugin);
ThunarSbrCaseRenamer *
thunar_sbr_case_renamer_new (void) G_GNUC_MALLOC;
ThunarSbrCaseRenamerMode
thunar_sbr_case_renamer_get_mode (ThunarSbrCaseRenamer *case_renamer);
void
thunar_sbr_case_renamer_set_mode (ThunarSbrCaseRenamer *case_renamer,
ThunarSbrCaseRenamerMode mode);
G_END_DECLS;
#endif /* !__THUNAR_SBR_CASE_RENAMER_H__ */

View file

@ -0,0 +1,789 @@
/* vi:set et ai sw=2 sts=2 ts=2: */
/*-
* Copyright (c) 2007 Nick Schermer <nick@xfce.org>
* Copyright (c) 2007 Benedikt Meurer <benny@xfce.org>
* Copyright (c) 2009 Jannis Pohlmann <jannis@xfce.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; 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_STRING_H
#include <string.h>
#endif
#ifdef HAVE_TIME_H
#include <time.h>
#endif
#ifdef HAVE_STDIO_H
#include <stdio.h>
#endif
#include <libxfce4util/libxfce4util.h>
#include <thunar-sbr/thunar-sbr-date-renamer.h>
#ifdef HAVE_EXIF
#include <libexif/exif-data.h>
#endif
/* Property identifiers */
enum
{
PROP_0,
PROP_MODE,
PROP_FORMAT,
PROP_OFFSET,
PROP_OFFSET_MODE,
};
static void
thunar_sbr_date_renamer_finalize (GObject *object);
static void
thunar_sbr_date_renamer_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec);
static void
thunar_sbr_date_renamer_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec);
static gchar *
thunar_sbr_get_time_string (guint64 file_time,
const gchar *custom_format);
#ifdef HAVE_EXIF
static guint64
thunar_sbr_get_time_from_string (const gchar *string);
#endif
static guint64
thunar_sbr_get_time (ThunarxFileInfo *file,
ThunarSbrDateMode mode);
static gchar *
thunar_sbr_date_renamer_process (ThunarxRenamer *renamer,
ThunarxFileInfo *file,
const gchar *text,
guint idx);
struct _ThunarSbrDateRenamerClass
{
ThunarxRenamerClass __parent__;
};
struct _ThunarSbrDateRenamer
{
ThunarxRenamer __parent__;
ThunarSbrDateMode mode;
guint offset;
ThunarSbrOffsetMode offset_mode;
gchar *format;
};
THUNARX_DEFINE_TYPE (ThunarSbrDateRenamer, thunar_sbr_date_renamer, THUNARX_TYPE_RENAMER);
static void
thunar_sbr_date_renamer_class_init (ThunarSbrDateRenamerClass *klass)
{
ThunarxRenamerClass *thunarxrenamer_class;
GObjectClass *gobject_class;
gobject_class = G_OBJECT_CLASS (klass);
gobject_class->finalize = thunar_sbr_date_renamer_finalize;
gobject_class->get_property = thunar_sbr_date_renamer_get_property;
gobject_class->set_property = thunar_sbr_date_renamer_set_property;
thunarxrenamer_class = THUNARX_RENAMER_CLASS (klass);
thunarxrenamer_class->process = thunar_sbr_date_renamer_process;
/**
* ThunarSbrDateRenamer:mode:
*
* The #ThunarSbrDateMode to use.
**/
g_object_class_install_property (gobject_class,
PROP_MODE,
g_param_spec_enum ("mode",
"mode",
"mode",
THUNAR_SBR_TYPE_DATE_MODE,
THUNAR_SBR_DATE_MODE_NOW,
G_PARAM_READWRITE));
/**
* ThunarSbrDateRenamer:format:
*
* The date format to insert.
**/
g_object_class_install_property (gobject_class,
PROP_FORMAT,
g_param_spec_string ("format",
"format",
"format",
"%Y%m%d",
G_PARAM_CONSTRUCT | G_PARAM_READWRITE));
/**
* ThunarSbrDateRenamer:offset:
*
* The starting offset at which to insert/overwrite.
**/
g_object_class_install_property (gobject_class,
PROP_OFFSET,
g_param_spec_uint ("offset",
"offset",
"offset",
0, G_MAXUINT, 0,
G_PARAM_READWRITE));
/**
* ThunarSbrDateRenamer:offset-mode:
*
* The offset mode for the renamer.
**/
g_object_class_install_property (gobject_class,
PROP_OFFSET_MODE,
g_param_spec_enum ("offset-mode",
"offset-mode",
"offset-mode",
THUNAR_SBR_TYPE_OFFSET_MODE,
THUNAR_SBR_OFFSET_MODE_LEFT,
G_PARAM_READWRITE));
}
static void
thunar_sbr_date_renamer_init (ThunarSbrDateRenamer *date_renamer)
{
GEnumClass *klass;
AtkRelationSet *relations;
AtkRelation *relation;
AtkObject *object;
GtkWidget *grid, *hbox;
GtkWidget *label, *combo;
GtkWidget *spinner;
GtkWidget *entry;
GtkAdjustment *adjustment;
guint n;
grid = gtk_grid_new ();
gtk_grid_set_row_spacing (GTK_GRID (grid), 6);
gtk_grid_set_column_spacing (GTK_GRID (grid), 12);
gtk_box_pack_start (GTK_BOX (date_renamer), grid, TRUE, TRUE, 0);
gtk_widget_show (grid);
label = gtk_label_new_with_mnemonic (_("Insert _time:"));
gtk_grid_attach (GTK_GRID (grid), label, 0, 0, 1, 1);
gtk_widget_show (label);
combo = gtk_combo_box_text_new ();
klass = g_type_class_ref (THUNAR_SBR_TYPE_DATE_MODE);
for (n = 0; n < klass->n_values; ++n)
gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo), _(klass->values[n].value_nick));
g_object_bind_property (G_OBJECT (date_renamer), "mode", G_OBJECT (combo), "active", G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
gtk_grid_attach (GTK_GRID (grid), combo, 1, 0, 1, 1);
gtk_label_set_mnemonic_widget (GTK_LABEL (label), combo);
g_type_class_unref (klass);
gtk_widget_show (combo);
/* set Atk label relation for the combo */
object = gtk_widget_get_accessible (combo);
relations = atk_object_ref_relation_set (gtk_widget_get_accessible (label));
relation = atk_relation_new (&object, 1, ATK_RELATION_LABEL_FOR);
atk_relation_set_add (relations, relation);
g_object_unref (G_OBJECT (relation));
g_object_unref (relations);
label = gtk_label_new_with_mnemonic (_("_Format:"));
gtk_grid_attach (GTK_GRID (grid), label, 2, 0, 1, 1);
gtk_widget_show (label);
entry = gtk_entry_new ();
g_object_bind_property (G_OBJECT (entry), "text", G_OBJECT (date_renamer), "format", G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
gtk_widget_set_hexpand (GTK_WIDGET (entry), TRUE);
gtk_grid_attach (GTK_GRID (grid), entry, 3, 0, 1, 1);
gtk_widget_set_tooltip_text (entry,
_("The format describes the date and time parts to insert "
"into the file name. For example, %Y will be substituted "
"with the year, %m with the month and %d with the day. "
"See the documentation of the date utility for additional "
"information."));
gtk_label_set_mnemonic_widget (GTK_LABEL (label), entry);
gtk_widget_show (entry);
/* set Atk label relation for the entry */
object = gtk_widget_get_accessible (entry);
relations = atk_object_ref_relation_set (gtk_widget_get_accessible (label));
relation = atk_relation_new (&object, 1, ATK_RELATION_LABEL_FOR);
atk_relation_set_add (relations, relation);
g_object_unref (G_OBJECT (relation));
g_object_unref (relations);
label = gtk_label_new_with_mnemonic (_("_At position:"));
gtk_grid_attach (GTK_GRID (grid), label, 0, 1, 1, 1);
gtk_widget_show (label);
hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
gtk_grid_attach (GTK_GRID (grid), hbox, 1, 1, 3, 1);
gtk_widget_show (hbox);
spinner = gtk_spin_button_new_with_range (0u, G_MAXUINT, 1u);
gtk_entry_set_width_chars (GTK_ENTRY (spinner), 4);
gtk_entry_set_alignment (GTK_ENTRY (spinner), 1.0f);
gtk_entry_set_activates_default (GTK_ENTRY (spinner), TRUE);
gtk_spin_button_set_digits (GTK_SPIN_BUTTON (spinner), 0);
gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinner), TRUE);
gtk_spin_button_set_snap_to_ticks (GTK_SPIN_BUTTON (spinner), TRUE);
gtk_box_pack_start (GTK_BOX (hbox), spinner, FALSE, FALSE, 0);
gtk_label_set_mnemonic_widget (GTK_LABEL (label), spinner);
gtk_widget_show (spinner);
adjustment = gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON (spinner));
g_object_bind_property (G_OBJECT (date_renamer), "offset", G_OBJECT (adjustment), "value", G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
combo = gtk_combo_box_text_new ();
klass = g_type_class_ref (THUNAR_SBR_TYPE_OFFSET_MODE);
for (n = 0; n < klass->n_values; ++n)
gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo), _(klass->values[n].value_nick));
g_object_bind_property (G_OBJECT (date_renamer), "offset-mode", G_OBJECT (combo), "active", G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
gtk_box_pack_start (GTK_BOX (hbox), combo, FALSE, FALSE, 0);
g_type_class_unref (klass);
gtk_widget_show (combo);
}
static void
thunar_sbr_date_renamer_finalize (GObject *object)
{
ThunarSbrDateRenamer *date_renamer = THUNAR_SBR_DATE_RENAMER (object);
/* release the format */
g_free (date_renamer->format);
(*G_OBJECT_CLASS (thunar_sbr_date_renamer_parent_class)->finalize) (object);
}
static void
thunar_sbr_date_renamer_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
ThunarSbrDateRenamer *date_renamer = THUNAR_SBR_DATE_RENAMER (object);
switch (prop_id)
{
case PROP_MODE:
g_value_set_enum (value, thunar_sbr_date_renamer_get_mode (date_renamer));
break;
case PROP_FORMAT:
g_value_set_string (value, thunar_sbr_date_renamer_get_format (date_renamer));
break;
case PROP_OFFSET:
g_value_set_uint (value, thunar_sbr_date_renamer_get_offset (date_renamer));
break;
case PROP_OFFSET_MODE:
g_value_set_enum (value, thunar_sbr_date_renamer_get_offset_mode (date_renamer));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
thunar_sbr_date_renamer_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
ThunarSbrDateRenamer *date_renamer = THUNAR_SBR_DATE_RENAMER (object);
switch (prop_id)
{
case PROP_MODE:
thunar_sbr_date_renamer_set_mode (date_renamer, g_value_get_enum (value));
break;
case PROP_FORMAT:
thunar_sbr_date_renamer_set_format (date_renamer, g_value_get_string (value));
break;
case PROP_OFFSET:
thunar_sbr_date_renamer_set_offset (date_renamer, g_value_get_uint (value));
break;
case PROP_OFFSET_MODE:
thunar_sbr_date_renamer_set_offset_mode (date_renamer, g_value_get_enum (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static gchar *
thunar_sbr_get_time_string (guint64 file_time,
const gchar *format)
{
struct tm *tm;
time_t _time;
gchar *converted;
gchar buffer[1024];
gint length;
_time = (time_t) file_time;
/* determine the local file time */
tm = localtime (&_time);
/* conver the format to the current locale */
converted = g_locale_from_utf8 (format, -1, NULL, NULL, NULL);
/* parse the format */
length = strftime (buffer, sizeof (buffer), converted, tm);
/* cleanup */
g_free (converted);
/* check if strftime succeeded */
if (G_UNLIKELY (length == 0))
return NULL;
/* return the utf-8 string */
return g_locale_to_utf8 (buffer, -1, NULL, NULL, NULL);
}
#ifdef HAVE_EXIF
static guint64
thunar_sbr_get_time_from_string (const gchar *string)
{
struct tm tm;
#ifdef HAVE_STRPTIME
/* parse the string with strptime */
strptime (string, "%Y:%m:%d %T", &tm);
#else
gint result;
gint year, month, day, hour, min, sec;
result = sscanf (string, "%d:%d:%d %d:%d:%d", &year, &month, &day, &hour, &min, &sec);
/* only continue when atleast the date is parsed succesfully */
if (G_LIKELY (result >= 3 && g_date_valid_dmy (day, month, year)))
{
/* be sure to start with a clean tm */
memset (&tm, 0, sizeof (tm));
/* set the date */
tm.tm_year = year - 1900;
tm.tm_mon = month - 1;
tm.tm_mday = day;
/* set the time */
tm.tm_hour = result >= 4 ? hour : 0;
tm.tm_min = result >= 5 ? min : 0;
tm.tm_sec = result >= 6 ? sec : 0;
}
else
{
return 0;
}
#endif
/* return the local time */
return mktime (&tm);
}
#endif
static guint64
thunar_sbr_get_time (ThunarxFileInfo *file,
ThunarSbrDateMode mode)
{
GFileInfo *file_info;
guint64 file_time = 0;
#ifdef HAVE_EXIF
gchar *uri, *filename;
ExifEntry *exif_entry;
ExifData *exif_data;
gchar exif_buffer[128];
#endif
switch (mode)
{
case THUNAR_SBR_DATE_MODE_NOW:
/* set the time to the current time */
file_time = time (NULL);
break;
case THUNAR_SBR_DATE_MODE_ATIME:
case THUNAR_SBR_DATE_MODE_MTIME:
/* get the file info */
file_info = thunarx_file_info_get_file_info (file);
/* get the time from the info */
if (mode == THUNAR_SBR_DATE_MODE_ATIME)
{
file_time = g_file_info_get_attribute_uint64 (file_info,
G_FILE_ATTRIBUTE_TIME_ACCESS);
}
else
{
file_time = g_file_info_get_attribute_uint64 (file_info,
G_FILE_ATTRIBUTE_TIME_MODIFIED);
}
/* release the file info */
g_object_unref (file_info);
break;
#ifdef HAVE_EXIF
case THUNAR_SBR_DATE_MODE_TAKEN:
/* get the uri */
uri = thunarx_file_info_get_uri (file);
if (G_LIKELY (uri != NULL))
{
/* determine the local path of the file */
filename = g_filename_from_uri (uri, NULL, NULL);
if (G_LIKELY (filename != NULL))
{
/* try to load the exif data for the file */
exif_data = exif_data_new_from_file (filename);
if (G_LIKELY (exif_data != NULL))
{
/* lookup the entry for the tag, fallback on less common ones */
exif_entry = exif_data_get_entry (exif_data, EXIF_TAG_DATE_TIME);
if (exif_entry == NULL)
exif_entry = exif_data_get_entry (exif_data, EXIF_TAG_DATE_TIME_ORIGINAL);
if (exif_entry == NULL)
exif_entry = exif_data_get_entry (exif_data, EXIF_TAG_DATE_TIME_DIGITIZED);
if (G_LIKELY (exif_entry != NULL))
{
/* determine the value */
if (exif_entry_get_value (exif_entry, exif_buffer, sizeof (exif_buffer)) != NULL)
file_time = thunar_sbr_get_time_from_string (exif_buffer);
}
/* cleanup */
exif_data_free (exif_data);
}
/* cleanup */
g_free (filename);
}
/* cleanup */
g_free (uri);
}
break;
#endif
}
return file_time;
}
static gchar *
thunar_sbr_date_renamer_process (ThunarxRenamer *renamer,
ThunarxFileInfo *file,
const gchar *text,
guint idx)
{
ThunarSbrDateRenamer *date_renamer = THUNAR_SBR_DATE_RENAMER (renamer);
gchar *string;
guint64 file_time;
const gchar *s;
GString *result;
guint text_length;
guint offset;
/* return when there is no text in the custom format entry */
if (G_UNLIKELY (date_renamer->format == NULL || *date_renamer->format == '\0'))
return g_strdup (text);
/* determine the input text length */
text_length = g_utf8_strlen (text, -1);
/* determine the real offset and check if it's valid */
offset = (date_renamer->offset_mode == THUNAR_SBR_OFFSET_MODE_LEFT) ? date_renamer->offset : (text_length - date_renamer->offset);
if (G_UNLIKELY (offset > text_length))
return g_strdup (text);
/* get the file time */
file_time = thunar_sbr_get_time (file, date_renamer->mode);
if (file_time == 0)
return g_strdup (text);
/* allocate space for the result */
result = g_string_sized_new (2 * text_length);
/* determine the text pointer for the offset */
s = g_utf8_offset_to_pointer (text, offset);
/* add the text before the insert/overwrite offset */
g_string_append_len (result, text, s - text);
/* parse the time string */
string = thunar_sbr_get_time_string (file_time, date_renamer->format);
if (string)
{
g_string_append (result, string);
g_free (string);
}
/* append the remaining text */
g_string_append (result, s);
/* return the result */
return g_string_free (result, FALSE);
}
/**
* thunar_sbr_date_renamer_new:
*
* Allocates a new #ThunarSbrDateRenamer instance.
*
* Return value: the newly allocated #ThunarSbrDateRenamer.
**/
ThunarSbrDateRenamer *
thunar_sbr_date_renamer_new (void)
{
return g_object_new (THUNAR_SBR_TYPE_DATE_RENAMER,
"name", _("Insert Date / Time"),
NULL);
}
/**
* thunar_sbr_date_renamer_get_mode:
* @date_renamer : a #ThunarSbrDateRenamer.
*
* Returns the mode of the @date_renamer.
*
* Return value: the mode of @date_renamer.
**/
ThunarSbrDateMode
thunar_sbr_date_renamer_get_mode (ThunarSbrDateRenamer *date_renamer)
{
g_return_val_if_fail (THUNAR_SBR_IS_DATE_RENAMER (date_renamer), THUNAR_SBR_DATE_MODE_NOW);
return date_renamer->mode;
}
/**
* thunar_sbr_date_renamer_set_mode:
* @date_renamer : a #ThunarSbrDateRenamer.
* @mode : the new #ThunarSbrDateMode for @date_renamer.
*
* Sets the mode of @date_renamer to @mode.
**/
void
thunar_sbr_date_renamer_set_mode (ThunarSbrDateRenamer *date_renamer,
ThunarSbrDateMode mode)
{
g_return_if_fail (THUNAR_SBR_IS_DATE_RENAMER (date_renamer));
/* check if we have a new mode */
if (G_LIKELY (date_renamer->mode != mode))
{
/* apply the new mode */
date_renamer->mode = mode;
/* update the renamer */
thunarx_renamer_changed (THUNARX_RENAMER (date_renamer));
/* notify listeners */
g_object_notify (G_OBJECT (date_renamer), "mode");
}
}
/**
* thunar_sbr_date_renamer_get_format:
* @date_renamer : a #ThunarSbrDateRenamer.
*
* Returns the format for the @date_renamer.
*
* Return value: the format for @date_renamer.
**/
const gchar *
thunar_sbr_date_renamer_get_format (ThunarSbrDateRenamer *date_renamer)
{
g_return_val_if_fail (THUNAR_SBR_IS_DATE_RENAMER (date_renamer), NULL);
return date_renamer->format;
}
/**
* thunar_sbr_date_renamer_set_format:
* @date_renamer : a #ThunarSbrDateRenamer.
* @format : the new format for @date_renamer.
*
* Sets the format for @date_renamer to @format.
**/
void
thunar_sbr_date_renamer_set_format (ThunarSbrDateRenamer *date_renamer,
const gchar *format)
{
g_return_if_fail (THUNAR_SBR_IS_DATE_RENAMER (date_renamer));
/* check if we have a new format */
if (G_LIKELY (g_strcmp0 (date_renamer->format, format) != 0))
{
/* apply the new format */
g_free (date_renamer->format);
date_renamer->format = g_strdup (format);
/* update the renamer */
thunarx_renamer_changed (THUNARX_RENAMER (date_renamer));
/* notify listeners */
g_object_notify (G_OBJECT (date_renamer), "format");
}
}
/**
* thunar_sbr_date_renamer_get_offset:
* @date_renamer : a #ThunarSbrInsertRenamer.
*
* Returns the offset for the @date_renamer.
*
* Return value: the offset for @date_renamer.
**/
guint
thunar_sbr_date_renamer_get_offset (ThunarSbrDateRenamer *date_renamer)
{
g_return_val_if_fail (THUNAR_SBR_IS_DATE_RENAMER (date_renamer), 0);
return date_renamer->offset;
}
/**
* thunar_sbr_date_renamer_set_offset:
* @date_renamer : a #ThunarSbrInsertRenamer.
* @offset : the new offset for @date_renamer.
*
* Sets the offset for @date_renamer to @offset.
**/
void
thunar_sbr_date_renamer_set_offset (ThunarSbrDateRenamer *date_renamer,
guint offset)
{
g_return_if_fail (THUNAR_SBR_IS_DATE_RENAMER (date_renamer));
/* check if we have a new offset */
if (G_LIKELY (date_renamer->offset != offset))
{
/* apply the new offset */
date_renamer->offset = offset;
/* update the renamer */
thunarx_renamer_changed (THUNARX_RENAMER (date_renamer));
/* notify listeners */
g_object_notify (G_OBJECT (date_renamer), "offset");
}
}
/**
* thunar_sbr_date_renamer_get_offset_mode:
* @date_renamer : a #ThunarSbrInsertRenamer.
*
* Returns the offset mode for the @date_renamer.
*
* Return value: the offset mode for @date_renamer.
**/
ThunarSbrOffsetMode
thunar_sbr_date_renamer_get_offset_mode (ThunarSbrDateRenamer *date_renamer)
{
g_return_val_if_fail (THUNAR_SBR_IS_DATE_RENAMER (date_renamer), THUNAR_SBR_OFFSET_MODE_LEFT);
return date_renamer->offset_mode;
}
/**
* thunar_sbr_date_renamer_set_offset_mode:
* @date_renamer : a #ThunarSbrInsertRenamer.
* @offset_mode : the new offset mode for @date_renamer.
*
* Sets the offset mode for @date_renamer to @offset_mode.
**/
void
thunar_sbr_date_renamer_set_offset_mode (ThunarSbrDateRenamer *date_renamer,
ThunarSbrOffsetMode offset_mode)
{
g_return_if_fail (THUNAR_SBR_IS_DATE_RENAMER (date_renamer));
/* check if we have a new setting */
if (G_LIKELY (date_renamer->offset_mode != offset_mode))
{
/* apply the new setting */
date_renamer->offset_mode = offset_mode;
/* update the renamer */
thunarx_renamer_changed (THUNARX_RENAMER (date_renamer));
/* notify listeners */
g_object_notify (G_OBJECT (date_renamer), "offset-mode");
}
}

View file

@ -0,0 +1,74 @@
/* vi:set et ai sw=2 sts=2 ts=2: */
/*-
* Copyright (c) 2007 Nick Schermer <nick@xfce.org>
* Copyright (c) 2007 Benedikt Meurer <benny@xfce.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#ifndef __THUNAR_SBR_DATE_RENAMER_H__
#define __THUNAR_SBR_DATE_RENAMER_H__
#include <thunar-sbr/thunar-sbr-enum-types.h>
G_BEGIN_DECLS;
typedef struct _ThunarSbrDateRenamerClass ThunarSbrDateRenamerClass;
typedef struct _ThunarSbrDateRenamer ThunarSbrDateRenamer;
#define THUNAR_SBR_TYPE_DATE_RENAMER (thunar_sbr_date_renamer_get_type ())
#define THUNAR_SBR_DATE_RENAMER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), THUNAR_SBR_TYPE_DATE_RENAMER, ThunarSbrDateRenamer))
#define THUNAR_SBR_DATE_RENAMER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), THUNAR_SBR_TYPE_DATE_RENAMER, ThunarSbrDateRenamerClass))
#define THUNAR_SBR_IS_DATE_RENAMER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), THUNAR_SBR_TYPE_DATE_RENAMER))
#define THUNAR_SBR_IS_DATE_RENAMER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), THUNAR_SBR_TYPE_DATE_RENAMER))
#define THUNAR_SBR_DATE_RENAMER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), THUNAR_SBR_TYPE_DATE_RENAMER, ThunarSbrDateRenamerClass))
GType
thunar_sbr_date_renamer_get_type (void) G_GNUC_CONST;
void
thunar_sbr_date_renamer_register_type (ThunarxProviderPlugin *plugin);
ThunarSbrDateRenamer *
thunar_sbr_date_renamer_new (void) G_GNUC_MALLOC;
ThunarSbrDateMode
thunar_sbr_date_renamer_get_mode (ThunarSbrDateRenamer *date_renamer);
void
thunar_sbr_date_renamer_set_mode (ThunarSbrDateRenamer *date_renamer,
ThunarSbrDateMode mode);
const gchar *
thunar_sbr_date_renamer_get_format (ThunarSbrDateRenamer *date_renamer);
void
thunar_sbr_date_renamer_set_format (ThunarSbrDateRenamer *date_renamer,
const gchar *format);
guint
thunar_sbr_date_renamer_get_offset (ThunarSbrDateRenamer *date_renamer);
void
thunar_sbr_date_renamer_set_offset (ThunarSbrDateRenamer *date_renamer,
guint offset);
ThunarSbrOffsetMode
thunar_sbr_date_renamer_get_offset_mode (ThunarSbrDateRenamer *date_renamer);
void
thunar_sbr_date_renamer_set_offset_mode (ThunarSbrDateRenamer *date_renamer,
ThunarSbrOffsetMode offset_mode);
G_END_DECLS;
#endif /* !__THUNAR_SBR_DATE_RENAMER_H__ */

View file

@ -0,0 +1,154 @@
/* vi:set et ai sw=2 sts=2 ts=2: */
/*-
* Copyright (c) 2006 Benedikt Meurer <benny@xfce.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; 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
#include <glib/gi18n-lib.h>
#include <thunar-sbr/thunar-sbr-enum-types.h>
static GType case_renamer_mode_type;
static GType insert_mode_type;
static GType number_mode_type;
static GType offset_mode_type;
static GType text_mode_type;
static GType date_mode_type;
GType
thunar_sbr_case_renamer_mode_get_type (void)
{
return case_renamer_mode_type;
}
GType
thunar_sbr_insert_mode_get_type (void)
{
return insert_mode_type;
}
GType
thunar_sbr_number_mode_get_type (void)
{
return number_mode_type;
}
GType
thunar_sbr_offset_mode_get_type (void)
{
return offset_mode_type;
}
GType
thunar_sbr_text_mode_get_type (void)
{
return text_mode_type;
}
GType
thunar_sbr_date_mode_get_type (void)
{
return date_mode_type;
}
void
thunar_sbr_register_enum_types (ThunarxProviderPlugin *plugin)
{
/* clang-format off */
static const GEnumValue case_renamer_mode_values[] =
{
{ THUNAR_SBR_CASE_RENAMER_MODE_LOWER, "THUNAR_SBR_CASE_RENAMER_MODE_LOWER", N_ ("lowercase"), },
{ THUNAR_SBR_CASE_RENAMER_MODE_UPPER, "THUNAR_SBR_CASE_RENAMER_MODE_UPPER", N_ ("UPPERCASE"), },
{ THUNAR_SBR_CASE_RENAMER_MODE_TITLE, "THUNAR_SBR_CASE_RENAMER_MODE_TITLE", N_ ("Title Case"), },
{ THUNAR_SBR_CASE_RENAMER_MODE_FIRST_UPPER, "THUNAR_SBR_CASE_RENAMER_MODE_FIRST_UPPER", N_ ("First character uppercase"), },
{ 0, NULL, NULL, },
};
static const GEnumValue insert_mode_values[] =
{
{ THUNAR_SBR_INSERT_MODE_INSERT, "THUNAR_SBR_INSERT_MODE_INSERT", N_ ("Insert"), },
{ THUNAR_SBR_INSERT_MODE_OVERWRITE, "THUNAR_SBR_INSERT_MODE_OVERWRITE", N_ ("Overwrite"), },
{ 0, NULL, NULL, },
};
static const GEnumValue number_mode_values[] =
{
{ THUNAR_SBR_NUMBER_MODE_123, "THUNAR_SBR_NUMBER_MODE_123", N_ ("1, 2, 3, ..."), },
{ THUNAR_SBR_NUMBER_MODE_010203, "THUNAR_SBR_NUMBER_MODE_010203", N_ ("01, 02, 03, ..."), },
{ THUNAR_SBR_NUMBER_MODE_001002003, "THUNAR_SBR_NUMBER_MODE_001002003", N_ ("001, 002, 003, ..."), },
{ THUNAR_SBR_NUMBER_MODE_000100020003, "THUNAR_SBR_NUMBER_MODE_000100020003", N_ ("0001, 0002, 0003, ..."), },
{ THUNAR_SBR_NUMBER_MODE_000010000200003, "THUNAR_SBR_NUMBER_MODE_000010000200003", N_ ("00001, 00002, 00003, ..."), },
{ THUNAR_SBR_NUMBER_MODE_ABC, "THUNAR_SBR_NUMBER_MODE_ABC", N_ ("a, b, c, d, ..."), },
{ 0, NULL, NULL, },
};
static const GEnumValue offset_mode_values[] =
{
{ THUNAR_SBR_OFFSET_MODE_LEFT, "THUNAR_SBR_OFFSET_MODE_LEFT", N_ ("From the front (left)"), },
{ THUNAR_SBR_OFFSET_MODE_RIGHT, "THUNAR_SBR_OFFSET_MODE_RIGHT", N_ ("From the back (right)"), },
{ 0, NULL, NULL, },
};
static const GEnumValue text_mode_values[] =
{
{ THUNAR_SBR_TEXT_MODE_OTN, "THUNAR_SBR_TEXT_MODE_OTN", N_ ("Old Name - Text - Number"), },
{ THUNAR_SBR_TEXT_MODE_NTO, "THUNAR_SBR_TEXT_MODE_NTO", N_ ("Number - Text - Old Name"), },
{ THUNAR_SBR_TEXT_MODE_TN, "THUNAR_SBR_TEXT_MODE_TN", N_ ("Text - Number"), },
{ THUNAR_SBR_TEXT_MODE_NT, "THUNAR_SBR_TEXT_MODE_NT", N_ ("Number - Text"), },
{ 0, NULL, NULL, },
};
static const GEnumValue date_mode_values[] =
{
{ THUNAR_SBR_DATE_MODE_NOW, "THUNAR_SBR_DATE_MODE_NOW", N_ ("Current"), },
{ THUNAR_SBR_DATE_MODE_ATIME, "THUNAR_SBR_DATE_MODE_ATIME", N_ ("Date Accessed"), },
{ THUNAR_SBR_DATE_MODE_MTIME, "THUNAR_SBR_DATE_MODE_MTIME", N_ ("Date Modified"), },
#ifdef HAVE_EXIF
{ THUNAR_SBR_DATE_MODE_TAKEN, "THUNAR_SBR_DATE_MODE_TAKEN", N_ ("Date Picture Taken"), },
#endif
{ 0, NULL, NULL, },
};
/* clang-format on */
case_renamer_mode_type = thunarx_provider_plugin_register_enum (plugin, "ThunarSbrCaseRenamerMode", case_renamer_mode_values);
insert_mode_type = thunarx_provider_plugin_register_enum (plugin, "ThunarSbrInsertMode", insert_mode_values);
number_mode_type = thunarx_provider_plugin_register_enum (plugin, "ThunarSbrNumberMode", number_mode_values);
offset_mode_type = thunarx_provider_plugin_register_enum (plugin, "ThunarSbrOffsetMode", offset_mode_values);
text_mode_type = thunarx_provider_plugin_register_enum (plugin, "ThunarSbrTextMode", text_mode_values);
date_mode_type = thunarx_provider_plugin_register_enum (plugin, "ThunarSbrDateMode", date_mode_values);
}

View file

@ -0,0 +1,170 @@
/* vi:set et ai sw=2 sts=2 ts=2: */
/*-
* Copyright (c) 2006 Benedikt Meurer <benny@xfce.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#ifndef __THUNAR_SBR_ENUM_TYPES_H__
#define __THUNAR_SBR_ENUM_TYPES_H__
#include <thunarx/thunarx.h>
G_BEGIN_DECLS;
#define THUNAR_SBR_TYPE_CASE_RENAMER_MODE (thunar_sbr_case_renamer_mode_get_type ())
/**
* ThunarSbrCaseRenamerMode:
* @THUNAR_SBR_CASE_RENAMER_MODE_LOWER : convert to lower case.
* @THUNAR_SBR_CASE_RENAMER_MODE_UPPER : convert to upper case.
* @THUNAR_SBR_CASE_RENAMER_MODE_TITLE : convert to title case.
* @THUNAR_SBR_CASE_RENAMER_MODE_FIRST_UPPER : convert to First character uppercase.
*
* The operation mode for the #ThunarSbrCaseRenamer.
**/
typedef enum
{
THUNAR_SBR_CASE_RENAMER_MODE_LOWER,
THUNAR_SBR_CASE_RENAMER_MODE_UPPER,
THUNAR_SBR_CASE_RENAMER_MODE_TITLE,
THUNAR_SBR_CASE_RENAMER_MODE_FIRST_UPPER,
} ThunarSbrCaseRenamerMode;
GType
thunar_sbr_case_renamer_mode_get_type (void) G_GNUC_CONST;
#define THUNAR_SBR_TYPE_INSERT_MODE (thunar_sbr_insert_mode_get_type ())
/**
* ThunarSbrInsertMode:
* @THUNAR_SBR_INSERT_MODE_INSERT : insert characters.
* @THUNAR_SBR_INSERT_MODE_OVERWRITE : overwrite existing characters.
*
* The operation mode for the #ThunarSbrInsertRenamer.
**/
typedef enum
{
THUNAR_SBR_INSERT_MODE_INSERT,
THUNAR_SBR_INSERT_MODE_OVERWRITE,
} ThunarSbrInsertMode;
GType
thunar_sbr_insert_mode_get_type (void) G_GNUC_CONST;
#define THUNAR_SBR_TYPE_NUMBER_MODE (thunar_sbr_number_mode_get_type ())
/**
* ThunarSbrNumberMode:
* @THUNAR_SBR_NUMBER_MODE_123 : 1, 2, 3, ...
* @THUNAR_SBR_NUMBER_MODE_010203 : 01, 02, 03, ...
* @THUNAR_SBR_NUMBER_MODE_001002003 : 001, 002, 003, ...
* @THUNAR_SBR_NUMBER_MODE_000100020003 : 0001, 0002, 0003, ...
* @THUNAR_SBR_NUMBER_MODE_000100020003 : 00001, 00002, 00003, ...
* @THUNAR_SBR_NUMBER_MODE_ABC : a, b, c, ...
*
* The numbering mode for the #ThunarSbrNumberRenamer.
**/
typedef enum
{
THUNAR_SBR_NUMBER_MODE_123,
THUNAR_SBR_NUMBER_MODE_010203,
THUNAR_SBR_NUMBER_MODE_001002003,
THUNAR_SBR_NUMBER_MODE_000100020003,
THUNAR_SBR_NUMBER_MODE_000010000200003,
THUNAR_SBR_NUMBER_MODE_ABC,
} ThunarSbrNumberMode;
GType
thunar_sbr_number_mode_get_type (void) G_GNUC_CONST;
#define THUNAR_SBR_TYPE_OFFSET_MODE (thunar_sbr_offset_mode_get_type ())
/**
* ThunarSbrOffsetMode:
* @THUNAR_SBR_OFFSET_MODE_LEFT : offset starting from the left.
* @THUNAR_SBR_OFFSET_MODE_RIGHT : offset starting from the right.
*
* The offset mode for the #ThunarSbrInsertRenamer and the
* #ThunarSbrRemoveRenamer.
**/
typedef enum
{
THUNAR_SBR_OFFSET_MODE_LEFT,
THUNAR_SBR_OFFSET_MODE_RIGHT,
} ThunarSbrOffsetMode;
GType
thunar_sbr_offset_mode_get_type (void) G_GNUC_CONST;
#define THUNAR_SBR_TYPE_TEXT_MODE (thunar_sbr_text_mode_get_type ())
/**
* ThunarSbrTextMode:
* @THUNAR_SBR_TEXT_MODE_OTN : Old Name - Text - Number
* @THUNAR_SBR_TEXT_MODE_NTO : Number - Text - Old Name
* @THUNAR_SBR_TEXT_MODE_TN : Text - Number
* @THUNAR_SBR_TEXT_MODE_NT : Number - Text
*
* The text mode for the #ThunarSbrNumberRenamer.
**/
typedef enum
{
THUNAR_SBR_TEXT_MODE_OTN,
THUNAR_SBR_TEXT_MODE_NTO,
THUNAR_SBR_TEXT_MODE_TN,
THUNAR_SBR_TEXT_MODE_NT,
} ThunarSbrTextMode;
GType
thunar_sbr_text_mode_get_type (void) G_GNUC_CONST;
#define THUNAR_SBR_TYPE_DATE_MODE (thunar_sbr_date_mode_get_type ())
/**
* ThunarSbrDateMode:
* @THUNAR_SBR_DATE_MODE_NOW : Current Time
* @THUNAR_SBR_DATE_MODE_ATIME : Access Time
* @THUNAR_SBR_DATE_MODE_MTIME : Modification Time
* @THUNAR_SBR_DATE_MODE_TAKEN : Picture Taken Time
*
* The date mode for the #ThunarSbrDateRenamer.
**/
typedef enum
{
THUNAR_SBR_DATE_MODE_NOW,
THUNAR_SBR_DATE_MODE_ATIME,
THUNAR_SBR_DATE_MODE_MTIME,
#ifdef HAVE_EXIF
THUNAR_SBR_DATE_MODE_TAKEN,
#endif
} ThunarSbrDateMode;
GType
thunar_sbr_date_mode_get_type (void) G_GNUC_CONST;
void
thunar_sbr_register_enum_types (ThunarxProviderPlugin *plugin);
G_END_DECLS;
#endif /* !__THUNAR_SBR_ENUM_TYPES_H__ */

View file

@ -0,0 +1,578 @@
/* vi:set et ai sw=2 sts=2 ts=2: */
/*-
* Copyright (c) 2006 Benedikt Meurer <benny@xfce.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; 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
#include <libxfce4util/libxfce4util.h>
#include <thunar-sbr/thunar-sbr-insert-renamer.h>
/* Property identifiers */
enum
{
PROP_0,
PROP_MODE,
PROP_OFFSET,
PROP_OFFSET_MODE,
PROP_TEXT,
};
static void
thunar_sbr_insert_renamer_finalize (GObject *object);
static void
thunar_sbr_insert_renamer_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec);
static void
thunar_sbr_insert_renamer_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec);
static gchar *
thunar_sbr_insert_renamer_process (ThunarxRenamer *renamer,
ThunarxFileInfo *file,
const gchar *text,
guint idx);
struct _ThunarSbrInsertRenamerClass
{
ThunarxRenamerClass __parent__;
};
struct _ThunarSbrInsertRenamer
{
ThunarxRenamer __parent__;
ThunarSbrInsertMode mode;
guint offset;
ThunarSbrOffsetMode offset_mode;
gchar *text;
};
THUNARX_DEFINE_TYPE (ThunarSbrInsertRenamer, thunar_sbr_insert_renamer, THUNARX_TYPE_RENAMER);
static void
thunar_sbr_insert_renamer_class_init (ThunarSbrInsertRenamerClass *klass)
{
ThunarxRenamerClass *thunarxrenamer_class;
GObjectClass *gobject_class;
gobject_class = G_OBJECT_CLASS (klass);
gobject_class->finalize = thunar_sbr_insert_renamer_finalize;
gobject_class->get_property = thunar_sbr_insert_renamer_get_property;
gobject_class->set_property = thunar_sbr_insert_renamer_set_property;
thunarxrenamer_class = THUNARX_RENAMER_CLASS (klass);
thunarxrenamer_class->process = thunar_sbr_insert_renamer_process;
/**
* ThunarSbrInsertRenamer:mode:
*
* The #ThunarSbrInsertMode to use.
**/
g_object_class_install_property (gobject_class,
PROP_MODE,
g_param_spec_enum ("mode", "mode", "mode",
THUNAR_SBR_TYPE_INSERT_MODE,
THUNAR_SBR_INSERT_MODE_INSERT,
G_PARAM_READWRITE));
/**
* ThunarSbrInsertRenamer:offset:
*
* The starting offset at which to insert/overwrite.
**/
g_object_class_install_property (gobject_class,
PROP_OFFSET,
g_param_spec_uint ("offset",
"offset",
"offset",
0, G_MAXUINT, 1,
G_PARAM_READWRITE));
/**
* ThunarSbrInsertRenamer:offset-mode:
*
* The offset mode for the renamer.
**/
g_object_class_install_property (gobject_class,
PROP_OFFSET_MODE,
g_param_spec_enum ("offset-mode",
"offset-mode",
"offset-mode",
THUNAR_SBR_TYPE_OFFSET_MODE,
THUNAR_SBR_OFFSET_MODE_LEFT,
G_PARAM_READWRITE));
/**
* ThunarSbrInsertRenamer:text:
*
* The text to insert/overwrite.
**/
g_object_class_install_property (gobject_class,
PROP_TEXT,
g_param_spec_string ("text",
"text",
"text",
NULL,
G_PARAM_READWRITE));
}
static void
thunar_sbr_insert_renamer_init (ThunarSbrInsertRenamer *insert_renamer)
{
AtkRelationSet *relations;
GtkAdjustment *adjustment;
AtkRelation *relation;
GEnumClass *klass;
AtkObject *object;
GtkWidget *spinner;
GtkWidget *combo;
GtkWidget *entry;
GtkWidget *label;
GtkWidget *grid;
GtkWidget *hbox;
guint n;
grid = gtk_grid_new ();
gtk_grid_set_column_spacing (GTK_GRID (grid), 12);
gtk_grid_set_row_spacing (GTK_GRID (grid), 6);
gtk_box_pack_start (GTK_BOX (insert_renamer), grid, TRUE, TRUE, 0);
gtk_widget_show (grid);
combo = gtk_combo_box_text_new ();
klass = g_type_class_ref (THUNAR_SBR_TYPE_INSERT_MODE);
for (n = 0; n < klass->n_values; ++n)
gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo), _(klass->values[n].value_nick));
g_object_bind_property (G_OBJECT (insert_renamer), "mode", G_OBJECT (combo), "active", G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
gtk_grid_attach (GTK_GRID (grid), combo, 0, 0, 1, 1);
g_type_class_unref (klass);
gtk_widget_show (combo);
label = gtk_label_new_with_mnemonic (_("_Text:"));
gtk_grid_attach (GTK_GRID (grid), label, 1, 0, 1, 1);
gtk_widget_show (label);
entry = gtk_entry_new ();
gtk_entry_set_activates_default (GTK_ENTRY (entry), TRUE);
g_object_bind_property (G_OBJECT (entry), "text", G_OBJECT (insert_renamer), "text", G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
gtk_widget_set_hexpand (entry, TRUE);
gtk_grid_attach (GTK_GRID (grid), entry, 2, 0, 1, 1);
gtk_label_set_mnemonic_widget (GTK_LABEL (label), entry);
gtk_widget_show (entry);
/* set Atk label relation for the entry */
object = gtk_widget_get_accessible (entry);
relations = atk_object_ref_relation_set (gtk_widget_get_accessible (label));
relation = atk_relation_new (&object, 1, ATK_RELATION_LABEL_FOR);
atk_relation_set_add (relations, relation);
g_object_unref (G_OBJECT (relation));
g_object_unref (relations);
label = gtk_label_new_with_mnemonic (_("_At position:"));
gtk_label_set_xalign (GTK_LABEL (label), 1.0f);
gtk_grid_attach (GTK_GRID (grid), label, 0, 1, 2, 1);
gtk_widget_show (label);
hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
gtk_grid_attach (GTK_GRID (grid), hbox, 2, 1, 1, 1);
gtk_widget_show (hbox);
spinner = gtk_spin_button_new_with_range (0u, G_MAXUINT, 1u);
gtk_entry_set_width_chars (GTK_ENTRY (spinner), 4);
gtk_entry_set_alignment (GTK_ENTRY (spinner), 1.0f);
gtk_entry_set_activates_default (GTK_ENTRY (spinner), TRUE);
gtk_spin_button_set_digits (GTK_SPIN_BUTTON (spinner), 0);
gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinner), TRUE);
gtk_spin_button_set_snap_to_ticks (GTK_SPIN_BUTTON (spinner), TRUE);
gtk_box_pack_start (GTK_BOX (hbox), spinner, FALSE, FALSE, 0);
gtk_label_set_mnemonic_widget (GTK_LABEL (label), spinner);
gtk_widget_show (spinner);
adjustment = gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON (spinner));
g_object_bind_property (G_OBJECT (insert_renamer), "offset", G_OBJECT (adjustment), "value", G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
/* set Atk label relation for the entry */
object = gtk_widget_get_accessible (spinner);
relations = atk_object_ref_relation_set (gtk_widget_get_accessible (label));
relation = atk_relation_new (&object, 1, ATK_RELATION_LABEL_FOR);
atk_relation_set_add (relations, relation);
g_object_unref (G_OBJECT (relation));
g_object_unref (relations);
combo = gtk_combo_box_text_new ();
klass = g_type_class_ref (THUNAR_SBR_TYPE_OFFSET_MODE);
for (n = 0; n < klass->n_values; ++n)
gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo), _(klass->values[n].value_nick));
g_object_bind_property (G_OBJECT (insert_renamer), "offset-mode", G_OBJECT (combo), "active", G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
gtk_box_pack_start (GTK_BOX (hbox), combo, FALSE, FALSE, 0);
g_type_class_unref (klass);
gtk_widget_show (combo);
}
static void
thunar_sbr_insert_renamer_finalize (GObject *object)
{
ThunarSbrInsertRenamer *insert_renamer = THUNAR_SBR_INSERT_RENAMER (object);
/* release the text */
g_free (insert_renamer->text);
(*G_OBJECT_CLASS (thunar_sbr_insert_renamer_parent_class)->finalize) (object);
}
static void
thunar_sbr_insert_renamer_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
ThunarSbrInsertRenamer *insert_renamer = THUNAR_SBR_INSERT_RENAMER (object);
switch (prop_id)
{
case PROP_MODE:
g_value_set_enum (value, thunar_sbr_insert_renamer_get_mode (insert_renamer));
break;
case PROP_OFFSET:
g_value_set_uint (value, thunar_sbr_insert_renamer_get_offset (insert_renamer));
break;
case PROP_OFFSET_MODE:
g_value_set_enum (value, thunar_sbr_insert_renamer_get_offset_mode (insert_renamer));
break;
case PROP_TEXT:
g_value_set_string (value, thunar_sbr_insert_renamer_get_text (insert_renamer));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
thunar_sbr_insert_renamer_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
ThunarSbrInsertRenamer *insert_renamer = THUNAR_SBR_INSERT_RENAMER (object);
switch (prop_id)
{
case PROP_MODE:
thunar_sbr_insert_renamer_set_mode (insert_renamer, g_value_get_enum (value));
break;
case PROP_OFFSET:
thunar_sbr_insert_renamer_set_offset (insert_renamer, g_value_get_uint (value));
break;
case PROP_OFFSET_MODE:
thunar_sbr_insert_renamer_set_offset_mode (insert_renamer, g_value_get_enum (value));
break;
case PROP_TEXT:
thunar_sbr_insert_renamer_set_text (insert_renamer, g_value_get_string (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static gchar *
thunar_sbr_insert_renamer_process (ThunarxRenamer *renamer,
ThunarxFileInfo *file,
const gchar *text,
guint idx)
{
ThunarSbrInsertRenamer *insert_renamer = THUNAR_SBR_INSERT_RENAMER (renamer);
const gchar *t;
const gchar *s;
GString *result;
guint text_length;
guint offset;
/* check if we have any text to insert/overwrite */
if (G_UNLIKELY (insert_renamer->text == NULL || *insert_renamer->text == '\0'))
return g_strdup (text);
/* determine the input text length */
text_length = g_utf8_strlen (text, -1);
/* determine the real offset and check if it's valid */
offset = (insert_renamer->offset_mode == THUNAR_SBR_OFFSET_MODE_LEFT) ? insert_renamer->offset : (text_length - insert_renamer->offset);
if (G_UNLIKELY (offset > text_length))
return g_strdup (text);
/* allocate space for the result */
result = g_string_sized_new (2 * text_length);
/* determine the text pointer for the offset */
s = g_utf8_offset_to_pointer (text, offset);
/* add the text before the insert/overwrite offset */
g_string_append_len (result, text, s - text);
/* add the text to insert */
g_string_append (result, insert_renamer->text);
/* skip over the input text if overwriting */
if (insert_renamer->mode == THUNAR_SBR_INSERT_MODE_OVERWRITE)
{
/* skip over the number of characters in the overwrite text */
for (t = insert_renamer->text; *s != '\0' && *t != '\0'; s = g_utf8_next_char (s), t = g_utf8_next_char (t))
;
}
/* append the remaining text */
g_string_append (result, s);
/* return the result */
return g_string_free (result, FALSE);
}
/**
* thunar_sbr_insert_renamer_new:
*
* Allocates a new #ThunarSbrInsertRenamer instance.
*
* Return value: the newly allocated #ThunarSbrInsertRenamer.
**/
ThunarSbrInsertRenamer *
thunar_sbr_insert_renamer_new (void)
{
return g_object_new (THUNAR_SBR_TYPE_INSERT_RENAMER,
"name", _("Insert / Overwrite"),
NULL);
}
/**
* thunar_sbr_insert_renamer_get_mode:
* @insert_renamer : a #ThunarSbrInsertRenamer.
*
* Returns the mode of the @insert_renamer.
*
* Return value: the mode of @insert_renamer.
**/
ThunarSbrInsertMode
thunar_sbr_insert_renamer_get_mode (ThunarSbrInsertRenamer *insert_renamer)
{
g_return_val_if_fail (THUNAR_SBR_IS_INSERT_RENAMER (insert_renamer), THUNAR_SBR_INSERT_MODE_INSERT);
return insert_renamer->mode;
}
/**
* thunar_sbr_insert_renamer_set_mode:
* @insert_renamer : a #ThunarSbrInsertRenamer.
* @mode : the new #ThunarSbrInsertMode for @insert_renamer.
*
* Sets the mode of @insert_renamer to @mode.
**/
void
thunar_sbr_insert_renamer_set_mode (ThunarSbrInsertRenamer *insert_renamer,
ThunarSbrInsertMode mode)
{
g_return_if_fail (THUNAR_SBR_IS_INSERT_RENAMER (insert_renamer));
/* check if we have a new mode */
if (G_LIKELY (insert_renamer->mode != mode))
{
/* apply the new mode */
insert_renamer->mode = mode;
/* update the renamer */
thunarx_renamer_changed (THUNARX_RENAMER (insert_renamer));
/* notify listeners */
g_object_notify (G_OBJECT (insert_renamer), "mode");
}
}
/**
* thunar_sbr_insert_renamer_get_offset:
* @insert_renamer : a #ThunarSbrInsertRenamer.
*
* Returns the offset for the @insert_renamer.
*
* Return value: the offset for @insert_renamer.
**/
guint
thunar_sbr_insert_renamer_get_offset (ThunarSbrInsertRenamer *insert_renamer)
{
g_return_val_if_fail (THUNAR_SBR_IS_INSERT_RENAMER (insert_renamer), 0);
return insert_renamer->offset;
}
/**
* thunar_sbr_insert_renamer_set_offset:
* @insert_renamer : a #ThunarSbrInsertRenamer.
* @offset : the new offset for @insert_renamer.
*
* Sets the offset for @insert_renamer to @offset.
**/
void
thunar_sbr_insert_renamer_set_offset (ThunarSbrInsertRenamer *insert_renamer,
guint offset)
{
g_return_if_fail (THUNAR_SBR_IS_INSERT_RENAMER (insert_renamer));
/* check if we have a new offset */
if (G_LIKELY (insert_renamer->offset != offset))
{
/* apply the new offset */
insert_renamer->offset = offset;
/* update the renamer */
thunarx_renamer_changed (THUNARX_RENAMER (insert_renamer));
/* notify listeners */
g_object_notify (G_OBJECT (insert_renamer), "offset");
}
}
/**
* thunar_sbr_insert_renamer_get_offset_mode:
* @insert_renamer : a #ThunarSbrInsertRenamer.
*
* Returns the offset mode for the @insert_renamer.
*
* Return value: the offset mode for @insert_renamer.
**/
ThunarSbrOffsetMode
thunar_sbr_insert_renamer_get_offset_mode (ThunarSbrInsertRenamer *insert_renamer)
{
g_return_val_if_fail (THUNAR_SBR_IS_INSERT_RENAMER (insert_renamer), THUNAR_SBR_OFFSET_MODE_LEFT);
return insert_renamer->offset_mode;
}
/**
* thunar_sbr_insert_renamer_set_offset_mode:
* @insert_renamer : a #ThunarSbrInsertRenamer.
* @offset_mode : the new offset mode for @insert_renamer.
*
* Sets the offset mode for @insert_renamer to @offset_mode.
**/
void
thunar_sbr_insert_renamer_set_offset_mode (ThunarSbrInsertRenamer *insert_renamer,
ThunarSbrOffsetMode offset_mode)
{
g_return_if_fail (THUNAR_SBR_IS_INSERT_RENAMER (insert_renamer));
/* check if we have a new setting */
if (G_LIKELY (insert_renamer->offset_mode != offset_mode))
{
/* apply the new setting */
insert_renamer->offset_mode = offset_mode;
/* update the renamer */
thunarx_renamer_changed (THUNARX_RENAMER (insert_renamer));
/* notify listeners */
g_object_notify (G_OBJECT (insert_renamer), "offset-mode");
}
}
/**
* thunar_sbr_insert_renamer_get_text:
* @insert_renamer : a #ThunarSbrInsertRenamer.
*
* Returns the text for the @insert_renamer.
*
* Return value: the text for @insert_renamer.
**/
const gchar *
thunar_sbr_insert_renamer_get_text (ThunarSbrInsertRenamer *insert_renamer)
{
g_return_val_if_fail (THUNAR_SBR_IS_INSERT_RENAMER (insert_renamer), NULL);
return insert_renamer->text;
}
/**
* thunar_sbr_insert_renamer_set_text:
* @insert_renamer : a #ThunarSbrInsertRenamer.
* @text : the new text for @insert_renamer.
*
* Sets the text for @insert_renamer to @text.
**/
void
thunar_sbr_insert_renamer_set_text (ThunarSbrInsertRenamer *insert_renamer,
const gchar *text)
{
g_return_if_fail (THUNAR_SBR_IS_INSERT_RENAMER (insert_renamer));
/* check if we have a new text */
if (G_LIKELY (g_strcmp0 (insert_renamer->text, text) != 0))
{
/* apply the new text */
g_free (insert_renamer->text);
insert_renamer->text = g_strdup (text);
/* update the renamer */
thunarx_renamer_changed (THUNARX_RENAMER (insert_renamer));
/* notify listeners */
g_object_notify (G_OBJECT (insert_renamer), "text");
}
}

View file

@ -0,0 +1,72 @@
/* vi:set et ai sw=2 sts=2 ts=2: */
/*-
* Copyright (c) 2006 Benedikt Meurer <benny@xfce.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#ifndef __THUNAR_SBR_INSERT_RENAMER_H__
#define __THUNAR_SBR_INSERT_RENAMER_H__
#include <thunar-sbr/thunar-sbr-enum-types.h>
G_BEGIN_DECLS;
typedef struct _ThunarSbrInsertRenamerClass ThunarSbrInsertRenamerClass;
typedef struct _ThunarSbrInsertRenamer ThunarSbrInsertRenamer;
#define THUNAR_SBR_TYPE_INSERT_RENAMER (thunar_sbr_insert_renamer_get_type ())
#define THUNAR_SBR_INSERT_RENAMER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), THUNAR_SBR_TYPE_INSERT_RENAMER, ThunarSbrInsertRenamer))
#define THUNAR_SBR_INSERT_RENAMER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), THUNAR_SBR_TYPE_INSERT_RENAMER, ThunarSbrInsertRenamerClass))
#define THUNAR_SBR_IS_INSERT_RENAMER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), THUNAR_SBR_TYPE_INSERT_RENAMER))
#define THUNAR_SBR_IS_INSERT_RENAMER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), THUNAR_SBR_TYPE_INSERT_RENAMER))
#define THUNAR_SBR_INSERT_RENAMER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), THUNAR_SBR_TYPE_INSERT_RENAMER, ThunarSbrInsertRenamerClass))
GType
thunar_sbr_insert_renamer_get_type (void) G_GNUC_CONST;
void
thunar_sbr_insert_renamer_register_type (ThunarxProviderPlugin *plugin);
ThunarSbrInsertRenamer *
thunar_sbr_insert_renamer_new (void) G_GNUC_MALLOC;
ThunarSbrInsertMode
thunar_sbr_insert_renamer_get_mode (ThunarSbrInsertRenamer *insert_renamer);
void
thunar_sbr_insert_renamer_set_mode (ThunarSbrInsertRenamer *insert_renamer,
ThunarSbrInsertMode mode);
guint
thunar_sbr_insert_renamer_get_offset (ThunarSbrInsertRenamer *insert_renamer);
void
thunar_sbr_insert_renamer_set_offset (ThunarSbrInsertRenamer *insert_renamer,
guint offset);
ThunarSbrOffsetMode
thunar_sbr_insert_renamer_get_offset_mode (ThunarSbrInsertRenamer *insert_renamer);
void
thunar_sbr_insert_renamer_set_offset_mode (ThunarSbrInsertRenamer *insert_renamer,
ThunarSbrOffsetMode offset_mode);
const gchar *
thunar_sbr_insert_renamer_get_text (ThunarSbrInsertRenamer *insert_renamer);
void
thunar_sbr_insert_renamer_set_text (ThunarSbrInsertRenamer *insert_renamer,
const gchar *text);
G_END_DECLS;
#endif /* !__THUNAR_SBR_INSERT_RENAMER_H__ */

View file

@ -0,0 +1,715 @@
/* vi:set et ai sw=2 sts=2 ts=2: */
/*-
* Copyright (c) 2006 Benedikt Meurer <benny@xfce.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; 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_STDLIB_H
#include <stdlib.h>
#endif
#ifdef HAVE_STRING_H
#include <string.h>
#endif
#include <libxfce4util/libxfce4util.h>
#include <thunar-sbr/thunar-sbr-number-renamer.h>
/* Property identifiers */
enum
{
PROP_0,
PROP_MODE,
PROP_START,
PROP_TEXT,
PROP_TEXT_MODE,
};
static void
thunar_sbr_number_renamer_finalize (GObject *object);
static void
thunar_sbr_number_renamer_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec);
static void
thunar_sbr_number_renamer_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec);
static void
thunar_sbr_number_renamer_realize (GtkWidget *widget);
static gchar *
thunar_sbr_number_renamer_process (ThunarxRenamer *renamer,
ThunarxFileInfo *file,
const gchar *text,
guint idx);
static void
thunar_sbr_number_renamer_update (ThunarSbrNumberRenamer *number_renamer);
struct _ThunarSbrNumberRenamerClass
{
ThunarxRenamerClass __parent__;
};
struct _ThunarSbrNumberRenamer
{
ThunarxRenamer __parent__;
GtkWidget *start_entry;
ThunarSbrNumberMode mode;
gchar *start;
gchar *text;
ThunarSbrTextMode text_mode;
};
THUNARX_DEFINE_TYPE (ThunarSbrNumberRenamer, thunar_sbr_number_renamer, THUNARX_TYPE_RENAMER);
static void
thunar_sbr_number_renamer_class_init (ThunarSbrNumberRenamerClass *klass)
{
ThunarxRenamerClass *thunarxrenamer_class;
GtkWidgetClass *gtkwidget_class;
GObjectClass *gobject_class;
gobject_class = G_OBJECT_CLASS (klass);
gobject_class->finalize = thunar_sbr_number_renamer_finalize;
gobject_class->get_property = thunar_sbr_number_renamer_get_property;
gobject_class->set_property = thunar_sbr_number_renamer_set_property;
gtkwidget_class = GTK_WIDGET_CLASS (klass);
gtkwidget_class->realize = thunar_sbr_number_renamer_realize;
thunarxrenamer_class = THUNARX_RENAMER_CLASS (klass);
thunarxrenamer_class->process = thunar_sbr_number_renamer_process;
/**
* ThunarSbrNumberRenamer:mode:
*
* The #ThunarSbrNumberMode to use.
**/
g_object_class_install_property (gobject_class,
PROP_MODE,
g_param_spec_enum ("mode", "mode", "mode",
THUNAR_SBR_TYPE_NUMBER_MODE,
THUNAR_SBR_NUMBER_MODE_123,
G_PARAM_READWRITE));
/**
* ThunarSbrNumberRenamer:start:
*
* The starting value according to the #ThunarSbrNumberMode.
**/
g_object_class_install_property (gobject_class,
PROP_START,
g_param_spec_string ("start",
"start",
"start",
"1",
G_PARAM_CONSTRUCT | G_PARAM_READWRITE));
/**
* ThunarSbrNumberRenamer:text:
*
* The additional text, depending on the #ThunarSbrTextMode.
**/
g_object_class_install_property (gobject_class,
PROP_TEXT,
g_param_spec_string ("text",
"text",
"text",
". ",
G_PARAM_CONSTRUCT | G_PARAM_READWRITE));
/**
* ThunarSbrNumberRenamer:text-mode:
*
* The text mode for the renamer.
**/
g_object_class_install_property (gobject_class,
PROP_TEXT_MODE,
g_param_spec_enum ("text-mode",
"text-mode",
"text-mode",
THUNAR_SBR_TYPE_TEXT_MODE,
THUNAR_SBR_TEXT_MODE_NTO,
G_PARAM_CONSTRUCT | G_PARAM_READWRITE));
}
static void
thunar_sbr_number_renamer_init (ThunarSbrNumberRenamer *number_renamer)
{
AtkRelationSet *relations;
AtkRelation *relation;
GEnumClass *klass;
AtkObject *object;
GtkWidget *combo;
GtkWidget *entry;
GtkWidget *label;
GtkWidget *grid;
guint n;
grid = gtk_grid_new ();
gtk_grid_set_row_spacing (GTK_GRID (grid), 6);
gtk_grid_set_column_spacing (GTK_GRID (grid), 12);
gtk_box_pack_start (GTK_BOX (number_renamer), grid, FALSE, FALSE, 0);
gtk_widget_show (grid);
label = gtk_label_new_with_mnemonic (_("_Number Format:"));
gtk_grid_attach (GTK_GRID (grid), label, 0, 0, 1, 1);
gtk_widget_show (label);
combo = gtk_combo_box_text_new ();
klass = g_type_class_ref (THUNAR_SBR_TYPE_NUMBER_MODE);
for (n = 0; n < klass->n_values; ++n)
gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo), _(klass->values[n].value_nick));
g_object_bind_property (G_OBJECT (number_renamer), "mode", G_OBJECT (combo), "active", G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
gtk_grid_attach (GTK_GRID (grid), combo, 1, 0, 1, 1);
gtk_label_set_mnemonic_widget (GTK_LABEL (label), combo);
g_type_class_unref (klass);
gtk_widget_show (combo);
/* set Atk label relation for the combo */
object = gtk_widget_get_accessible (combo);
relations = atk_object_ref_relation_set (gtk_widget_get_accessible (label));
relation = atk_relation_new (&object, 1, ATK_RELATION_LABEL_FOR);
atk_relation_set_add (relations, relation);
g_object_unref (G_OBJECT (relation));
g_object_unref (relations);
number_renamer->start_entry = gtk_entry_new ();
gtk_entry_set_max_length (GTK_ENTRY (number_renamer->start_entry), 8);
gtk_entry_set_width_chars (GTK_ENTRY (number_renamer->start_entry), 3);
gtk_entry_set_alignment (GTK_ENTRY (number_renamer->start_entry), 1.0f);
gtk_entry_set_activates_default (GTK_ENTRY (number_renamer->start_entry), TRUE);
gtk_widget_set_hexpand (GTK_WIDGET (number_renamer->start_entry), TRUE);
g_object_bind_property (G_OBJECT (number_renamer->start_entry), "text", G_OBJECT (number_renamer), "start", G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
gtk_grid_attach (GTK_GRID (grid), number_renamer->start_entry, 3, 0, 1, 1);
gtk_widget_show (number_renamer->start_entry);
label = gtk_label_new_with_mnemonic (_("_Start With:"));
gtk_grid_attach (GTK_GRID (grid), label, 2, 0, 1, 1);
gtk_label_set_mnemonic_widget (GTK_LABEL (label), number_renamer->start_entry);
gtk_widget_show (label);
/* set Atk label relation for the entry */
object = gtk_widget_get_accessible (number_renamer->start_entry);
relations = atk_object_ref_relation_set (gtk_widget_get_accessible (label));
relation = atk_relation_new (&object, 1, ATK_RELATION_LABEL_FOR);
atk_relation_set_add (relations, relation);
g_object_unref (G_OBJECT (relation));
g_object_unref (relations);
label = gtk_label_new_with_mnemonic (_("Text _Format:"));
gtk_grid_attach (GTK_GRID (grid), label, 0, 1, 1, 1);
gtk_widget_show (label);
combo = gtk_combo_box_text_new ();
klass = g_type_class_ref (THUNAR_SBR_TYPE_TEXT_MODE);
for (n = 0; n < klass->n_values; ++n)
gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo), _(klass->values[n].value_nick));
g_object_bind_property (G_OBJECT (number_renamer), "text-mode", G_OBJECT (combo), "active", G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
gtk_grid_attach (GTK_GRID (grid), combo, 1, 1, 1, 1);
gtk_label_set_mnemonic_widget (GTK_LABEL (label), combo);
g_type_class_unref (klass);
gtk_widget_show (combo);
/* set Atk label relation for the combo */
object = gtk_widget_get_accessible (combo);
relations = atk_object_ref_relation_set (gtk_widget_get_accessible (label));
relation = atk_relation_new (&object, 1, ATK_RELATION_LABEL_FOR);
atk_relation_set_add (relations, relation);
g_object_unref (G_OBJECT (relation));
g_object_unref (relations);
entry = gtk_entry_new ();
gtk_entry_set_width_chars (GTK_ENTRY (entry), 12);
gtk_entry_set_activates_default (GTK_ENTRY (entry), TRUE);
g_object_bind_property (G_OBJECT (entry), "text", G_OBJECT (number_renamer), "text", G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
gtk_grid_attach (GTK_GRID (grid), entry, 3, 1, 1, 1);
gtk_widget_show (entry);
label = gtk_label_new_with_mnemonic (_("_Text:"));
gtk_label_set_xalign (GTK_LABEL (label), 1.0f);
gtk_grid_attach (GTK_GRID (grid), label, 2, 1, 1, 1);
gtk_label_set_mnemonic_widget (GTK_LABEL (label), entry);
gtk_widget_show (label);
/* set Atk label relation for the entry */
object = gtk_widget_get_accessible (entry);
relations = atk_object_ref_relation_set (gtk_widget_get_accessible (label));
relation = atk_relation_new (&object, 1, ATK_RELATION_LABEL_FOR);
atk_relation_set_add (relations, relation);
g_object_unref (G_OBJECT (relation));
g_object_unref (relations);
}
static void
thunar_sbr_number_renamer_finalize (GObject *object)
{
ThunarSbrNumberRenamer *number_renamer = THUNAR_SBR_NUMBER_RENAMER (object);
/* release renamer resources */
g_free (number_renamer->start);
g_free (number_renamer->text);
(*G_OBJECT_CLASS (thunar_sbr_number_renamer_parent_class)->finalize) (object);
}
static void
thunar_sbr_number_renamer_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
ThunarSbrNumberRenamer *number_renamer = THUNAR_SBR_NUMBER_RENAMER (object);
switch (prop_id)
{
case PROP_MODE:
g_value_set_enum (value, thunar_sbr_number_renamer_get_mode (number_renamer));
break;
case PROP_START:
g_value_set_string (value, thunar_sbr_number_renamer_get_start (number_renamer));
break;
case PROP_TEXT:
g_value_set_string (value, thunar_sbr_number_renamer_get_text (number_renamer));
break;
case PROP_TEXT_MODE:
g_value_set_enum (value, thunar_sbr_number_renamer_get_text_mode (number_renamer));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
thunar_sbr_number_renamer_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
ThunarSbrNumberRenamer *number_renamer = THUNAR_SBR_NUMBER_RENAMER (object);
switch (prop_id)
{
case PROP_MODE:
thunar_sbr_number_renamer_set_mode (number_renamer, g_value_get_enum (value));
break;
case PROP_START:
thunar_sbr_number_renamer_set_start (number_renamer, g_value_get_string (value));
break;
case PROP_TEXT:
thunar_sbr_number_renamer_set_text (number_renamer, g_value_get_string (value));
break;
case PROP_TEXT_MODE:
thunar_sbr_number_renamer_set_text_mode (number_renamer, g_value_get_enum (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
thunar_sbr_number_renamer_realize (GtkWidget *widget)
{
/* realize the widget */
(*GTK_WIDGET_CLASS (thunar_sbr_number_renamer_parent_class)->realize) (widget);
/* update the state */
thunar_sbr_number_renamer_update (THUNAR_SBR_NUMBER_RENAMER (widget));
}
static gchar *
thunar_sbr_number_renamer_process (ThunarxRenamer *renamer,
ThunarxFileInfo *file,
const gchar *text,
guint idx)
{
ThunarSbrNumberRenamer *number_renamer = THUNAR_SBR_NUMBER_RENAMER (renamer);
gboolean invalid = TRUE;
gchar *endp;
gchar *name;
gchar *number = NULL;
guint start = 0;
/* check whether "start" is valid for the "mode" */
if (number_renamer->mode < THUNAR_SBR_NUMBER_MODE_ABC)
{
/* "start" must be a positive number */
start = strtoul (number_renamer->start, &endp, 10);
invalid = (endp <= number_renamer->start || *endp != '\0');
}
else if (number_renamer->mode == THUNAR_SBR_NUMBER_MODE_ABC)
{
/* "start" property must be 'a', 'b', 'c', etc. */
start = *number_renamer->start;
invalid = (strlen (number_renamer->start) != 1
|| g_unichar_tolower (start) < 'a'
|| g_unichar_tolower (start) > 'z');
}
/* check if we have invalid settings */
if (G_UNLIKELY (invalid))
return g_strdup (text);
/* format the number */
switch (number_renamer->mode)
{
case THUNAR_SBR_NUMBER_MODE_123:
number = g_strdup_printf ("%u", start + idx);
break;
case THUNAR_SBR_NUMBER_MODE_010203:
number = g_strdup_printf ("%02u", start + idx);
break;
case THUNAR_SBR_NUMBER_MODE_001002003:
number = g_strdup_printf ("%03u", start + idx);
break;
case THUNAR_SBR_NUMBER_MODE_000100020003:
number = g_strdup_printf ("%04u", start + idx);
break;
case THUNAR_SBR_NUMBER_MODE_000010000200003:
number = g_strdup_printf ("%05u", start + idx);
break;
case THUNAR_SBR_NUMBER_MODE_ABC:
if (start >= 'a' && start <= 'z')
number = g_strdup_printf ("%c", (gchar) (MIN (start + idx, 'z')));
else if (start >= 'A' && start <= 'Z')
number = g_strdup_printf ("%c", (gchar) (MIN (start + idx, 'Z')));
else
g_assert_not_reached ();
break;
default:
g_assert_not_reached ();
break;
}
/* format the text */
switch (number_renamer->text_mode)
{
case THUNAR_SBR_TEXT_MODE_OTN:
name = g_strconcat (text, number_renamer->text, number, NULL);
break;
case THUNAR_SBR_TEXT_MODE_NTO:
name = g_strconcat (number, number_renamer->text, text, NULL);
break;
case THUNAR_SBR_TEXT_MODE_TN:
name = g_strconcat (number_renamer->text, number, NULL);
break;
case THUNAR_SBR_TEXT_MODE_NT:
name = g_strconcat (number, number_renamer->text, NULL);
break;
default:
g_assert_not_reached ();
break;
}
/* release the number */
g_free (number);
/* return the new name */
return name;
}
static void
thunar_sbr_number_renamer_update (ThunarSbrNumberRenamer *number_renamer)
{
gboolean invalid = TRUE;
gchar *endp;
/* check whether "start" is valid for the "mode" */
if (number_renamer->mode < THUNAR_SBR_NUMBER_MODE_ABC)
{
/* "start" must be a positive number */
strtoul (number_renamer->start, &endp, 10);
invalid = (endp <= number_renamer->start || *endp != '\0');
}
else if (number_renamer->mode == THUNAR_SBR_NUMBER_MODE_ABC)
{
/* "start" property must be 'a', 'b', 'c', etc. */
invalid = (strlen (number_renamer->start) != 1
|| g_unichar_tolower (*number_renamer->start) < 'a'
|| g_unichar_tolower (*number_renamer->start) > 'z');
}
/* check if the start entry is realized */
if (gtk_widget_get_realized (number_renamer->start_entry))
{
/* highlight invalid input by using theme specific colors */
if (G_UNLIKELY (invalid))
gtk_style_context_add_class (gtk_widget_get_style_context (GTK_WIDGET (number_renamer->start_entry)), "error");
else
gtk_style_context_remove_class (gtk_widget_get_style_context (GTK_WIDGET (number_renamer->start_entry)), "error");
}
/* notify everybody that we have a new state */
thunarx_renamer_changed (THUNARX_RENAMER (number_renamer));
}
/**
* thunar_sbr_number_renamer_new:
*
* Allocates a new #ThunarSbrNumberRenamer instance.
*
* Return value: the newly allocated #ThunarSbrNumberRenamer.
**/
ThunarSbrNumberRenamer *
thunar_sbr_number_renamer_new (void)
{
return g_object_new (THUNAR_SBR_TYPE_NUMBER_RENAMER,
"name", _("Numbering"),
NULL);
}
/**
* thunar_sbr_number_renamer_get_mode:
* @number_renamer : a #ThunarSbrNumberRenamer.
*
* Returns the mode of the @number_renamer.
*
* Return value: the mode of @number_renamer.
**/
ThunarSbrNumberMode
thunar_sbr_number_renamer_get_mode (ThunarSbrNumberRenamer *number_renamer)
{
g_return_val_if_fail (THUNAR_SBR_IS_NUMBER_RENAMER (number_renamer), THUNAR_SBR_NUMBER_MODE_123);
return number_renamer->mode;
}
/**
* thunar_sbr_number_renamer_set_mode:
* @number_renamer : a #ThunarSbrNumberRenamer.
* @mode : the new #ThunarSbrNumberMode for @number_renamer.
*
* Sets the mode of @number_renamer to @mode.
**/
void
thunar_sbr_number_renamer_set_mode (ThunarSbrNumberRenamer *number_renamer,
ThunarSbrNumberMode mode)
{
g_return_if_fail (THUNAR_SBR_IS_NUMBER_RENAMER (number_renamer));
/* check if we have a new mode */
if (G_LIKELY (number_renamer->mode != mode))
{
/* apply the new mode */
number_renamer->mode = mode;
/* update the renamer */
thunar_sbr_number_renamer_update (number_renamer);
/* notify listeners */
g_object_notify (G_OBJECT (number_renamer), "mode");
}
}
/**
* thunar_sbr_number_renamer_get_start:
* @number_renamer : a #ThunarSbrNumberRenamer.
*
* Returns the start for the @number_renamer.
*
* Return value: the start for @number_renamer.
**/
const gchar *
thunar_sbr_number_renamer_get_start (ThunarSbrNumberRenamer *number_renamer)
{
g_return_val_if_fail (THUNAR_SBR_IS_NUMBER_RENAMER (number_renamer), NULL);
return number_renamer->start;
}
/**
* thunar_sbr_number_renamer_set_start:
* @number_renamer : a #ThunarSbrNumberRenamer.
* @start : the new start for @number_renamer.
*
* Sets the start for @number_renamer to @start.
**/
void
thunar_sbr_number_renamer_set_start (ThunarSbrNumberRenamer *number_renamer,
const gchar *start)
{
g_return_if_fail (THUNAR_SBR_IS_NUMBER_RENAMER (number_renamer));
/* check if we have a new start */
if (g_strcmp0 (number_renamer->start, start) != 0)
{
/* apply the new start */
g_free (number_renamer->start);
number_renamer->start = g_strdup (start);
/* update the renamer */
thunar_sbr_number_renamer_update (number_renamer);
/* notify listeners */
g_object_notify (G_OBJECT (number_renamer), "start");
}
}
/**
* thunar_sbr_number_renamer_get_text:
* @number_renamer : a #ThunarSbrNumberRenamer.
*
* Returns the text for the @number_renamer.
*
* Return value: the text for @number_renamer.
**/
const gchar *
thunar_sbr_number_renamer_get_text (ThunarSbrNumberRenamer *number_renamer)
{
g_return_val_if_fail (THUNAR_SBR_IS_NUMBER_RENAMER (number_renamer), NULL);
return number_renamer->text;
}
/**
* thunar_sbr_number_renamer_set_text:
* @number_renamer : a #ThunarSbrNumberRenamer.
* @text : the new text for @number_renamer.
*
* Sets the text for @number_renamer to @text.
**/
void
thunar_sbr_number_renamer_set_text (ThunarSbrNumberRenamer *number_renamer,
const gchar *text)
{
g_return_if_fail (THUNAR_SBR_IS_NUMBER_RENAMER (number_renamer));
/* check if we have a new text */
if (G_LIKELY (g_strcmp0 (number_renamer->text, text) != 0))
{
/* apply the new text */
g_free (number_renamer->text);
number_renamer->text = g_strdup (text);
/* update the renamer */
thunar_sbr_number_renamer_update (number_renamer);
/* notify listeners */
g_object_notify (G_OBJECT (number_renamer), "text");
}
}
/**
* thunar_sbr_number_renamer_get_text_mode:
* @number_renamer : a #ThunarSbrNumberRenamer.
*
* Returns the text mode for the @number_renamer.
*
* Return value: the text mode for @number_renamer.
**/
ThunarSbrTextMode
thunar_sbr_number_renamer_get_text_mode (ThunarSbrNumberRenamer *number_renamer)
{
g_return_val_if_fail (THUNAR_SBR_IS_NUMBER_RENAMER (number_renamer), THUNAR_SBR_TEXT_MODE_NTO);
return number_renamer->text_mode;
}
/**
* thunar_sbr_number_renamer_set_offset_mode:
* @number_renamer : a #ThunarSbrNumberRenamer.
* @text_mode : the new text mode for @number_renamer.
*
* Sets the text mode for @number_renamer to @text_mode.
**/
void
thunar_sbr_number_renamer_set_text_mode (ThunarSbrNumberRenamer *number_renamer,
ThunarSbrTextMode text_mode)
{
g_return_if_fail (THUNAR_SBR_IS_NUMBER_RENAMER (number_renamer));
/* check if we have a new setting */
if (G_LIKELY (number_renamer->text_mode != text_mode))
{
/* apply the new setting */
number_renamer->text_mode = text_mode;
/* update the renamer */
thunar_sbr_number_renamer_update (number_renamer);
/* notify listeners */
g_object_notify (G_OBJECT (number_renamer), "text-mode");
}
}

View file

@ -0,0 +1,72 @@
/* vi:set et ai sw=2 sts=2 ts=2: */
/*-
* Copyright (c) 2006 Benedikt Meurer <benny@xfce.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#ifndef __THUNAR_SBR_NUMBER_RENAMER_H__
#define __THUNAR_SBR_NUMBER_RENAMER_H__
#include <thunar-sbr/thunar-sbr-enum-types.h>
G_BEGIN_DECLS;
typedef struct _ThunarSbrNumberRenamerClass ThunarSbrNumberRenamerClass;
typedef struct _ThunarSbrNumberRenamer ThunarSbrNumberRenamer;
#define THUNAR_SBR_TYPE_NUMBER_RENAMER (thunar_sbr_number_renamer_get_type ())
#define THUNAR_SBR_NUMBER_RENAMER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), THUNAR_SBR_TYPE_NUMBER_RENAMER, ThunarSbrNumberRenamer))
#define THUNAR_SBR_NUMBER_RENAMER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), THUNAR_SBR_TYPE_NUMBER_RENAMER, ThunarSbrNumberRenamerClass))
#define THUNAR_SBR_IS_NUMBER_RENAMER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), THUNAR_SBR_TYPE_NUMBER_RENAMER))
#define THUNAR_SBR_IS_NUMBER_RENAMER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), THUNAR_SBR_TYPE_NUMBER_RENAMER))
#define THUNAR_SBR_NUMBER_RENAMER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), THUNAR_SBR_TYPE_NUMBER_RENAMER, ThunarSbrNumberRenamerClass))
GType
thunar_sbr_number_renamer_get_type (void) G_GNUC_CONST;
void
thunar_sbr_number_renamer_register_type (ThunarxProviderPlugin *plugin);
ThunarSbrNumberRenamer *
thunar_sbr_number_renamer_new (void) G_GNUC_MALLOC;
ThunarSbrNumberMode
thunar_sbr_number_renamer_get_mode (ThunarSbrNumberRenamer *number_renamer);
void
thunar_sbr_number_renamer_set_mode (ThunarSbrNumberRenamer *number_renamer,
ThunarSbrNumberMode mode);
const gchar *
thunar_sbr_number_renamer_get_start (ThunarSbrNumberRenamer *number_renamer);
void
thunar_sbr_number_renamer_set_start (ThunarSbrNumberRenamer *number_renamer,
const gchar *start);
const gchar *
thunar_sbr_number_renamer_get_text (ThunarSbrNumberRenamer *number_renamer);
void
thunar_sbr_number_renamer_set_text (ThunarSbrNumberRenamer *number_renamer,
const gchar *text);
ThunarSbrTextMode
thunar_sbr_number_renamer_get_text_mode (ThunarSbrNumberRenamer *number_renamer);
void
thunar_sbr_number_renamer_set_text_mode (ThunarSbrNumberRenamer *number_renamer,
ThunarSbrTextMode text_mode);
G_END_DECLS;
#endif /* !__THUNAR_SBR_NUMBER_RENAMER_H__ */

View file

@ -0,0 +1,100 @@
/* vi:set et ai sw=2 sts=2 ts=2: */
/*-
* Copyright (c) 2006 Benedikt Meurer <benny@xfce.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; 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
#include <thunar-sbr/thunar-sbr-case-renamer.h>
#include <thunar-sbr/thunar-sbr-date-renamer.h>
#include <thunar-sbr/thunar-sbr-insert-renamer.h>
#include <thunar-sbr/thunar-sbr-number-renamer.h>
#include <thunar-sbr/thunar-sbr-provider.h>
#include <thunar-sbr/thunar-sbr-remove-renamer.h>
#include <thunar-sbr/thunar-sbr-replace-renamer.h>
G_MODULE_EXPORT void
thunar_extension_initialize (ThunarxProviderPlugin *plugin);
G_MODULE_EXPORT void
thunar_extension_shutdown (void);
G_MODULE_EXPORT void
thunar_extension_list_types (const GType **types,
gint *n_types);
static GType type_list[1];
G_MODULE_EXPORT void
thunar_extension_initialize (ThunarxProviderPlugin *plugin)
{
const gchar *mismatch;
/* verify that the thunarx versions are compatible */
mismatch = thunarx_check_version (THUNARX_MAJOR_VERSION, THUNARX_MINOR_VERSION, THUNARX_MICRO_VERSION);
if (G_UNLIKELY (mismatch != NULL))
{
g_warning ("Version mismatch: %s", mismatch);
return;
}
#ifdef G_ENABLE_DEBUG
g_message ("Initializing ThunarSbr extension");
#endif
/* register the enum types for this plugin */
thunar_sbr_register_enum_types (plugin);
/* register the classes provided by this plugin */
thunar_sbr_case_renamer_register_type (plugin);
thunar_sbr_insert_renamer_register_type (plugin);
thunar_sbr_number_renamer_register_type (plugin);
thunar_sbr_provider_register_type (plugin);
thunar_sbr_remove_renamer_register_type (plugin);
thunar_sbr_replace_renamer_register_type (plugin);
thunar_sbr_date_renamer_register_type (plugin);
/* setup the plugin provider type list */
type_list[0] = THUNAR_SBR_TYPE_PROVIDER;
}
G_MODULE_EXPORT void
thunar_extension_shutdown (void)
{
#ifdef G_ENABLE_DEBUG
g_message ("Shutting down ThunarSbr extension");
#endif
}
G_MODULE_EXPORT void
thunar_extension_list_types (const GType **types,
gint *n_types)
{
*types = type_list;
*n_types = G_N_ELEMENTS (type_list);
}

View file

@ -0,0 +1,97 @@
/* vi:set et ai sw=2 sts=2 ts=2: */
/*-
* Copyright (c) 2006 Benedikt Meurer <benny@xfce.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; 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
#include <thunar-sbr/thunar-sbr-case-renamer.h>
#include <thunar-sbr/thunar-sbr-date-renamer.h>
#include <thunar-sbr/thunar-sbr-insert-renamer.h>
#include <thunar-sbr/thunar-sbr-number-renamer.h>
#include <thunar-sbr/thunar-sbr-provider.h>
#include <thunar-sbr/thunar-sbr-remove-renamer.h>
#include <thunar-sbr/thunar-sbr-replace-renamer.h>
static void
thunar_sbr_provider_renamer_provider_init (ThunarxRenamerProviderIface *iface);
static GList *
thunar_sbr_provider_get_renamers (ThunarxRenamerProvider *renamer_provider);
struct _ThunarSbrProviderClass
{
GObjectClass __parent__;
};
struct _ThunarSbrProvider
{
GObject __parent__;
};
THUNARX_DEFINE_TYPE_WITH_CODE (ThunarSbrProvider,
thunar_sbr_provider,
G_TYPE_OBJECT,
THUNARX_IMPLEMENT_INTERFACE (THUNARX_TYPE_RENAMER_PROVIDER,
thunar_sbr_provider_renamer_provider_init));
static void
thunar_sbr_provider_class_init (ThunarSbrProviderClass *klass)
{
}
static void
thunar_sbr_provider_renamer_provider_init (ThunarxRenamerProviderIface *iface)
{
iface->get_renamers = thunar_sbr_provider_get_renamers;
}
static void
thunar_sbr_provider_init (ThunarSbrProvider *sbr_provider)
{
}
static GList *
thunar_sbr_provider_get_renamers (ThunarxRenamerProvider *renamer_provider)
{
GList *renamers = NULL;
renamers = g_list_prepend (renamers, thunar_sbr_replace_renamer_new ());
renamers = g_list_prepend (renamers, thunar_sbr_remove_renamer_new ());
renamers = g_list_prepend (renamers, thunar_sbr_number_renamer_new ());
renamers = g_list_prepend (renamers, thunar_sbr_insert_renamer_new ());
renamers = g_list_prepend (renamers, thunar_sbr_case_renamer_new ());
renamers = g_list_prepend (renamers, thunar_sbr_date_renamer_new ());
return renamers;
}

View file

@ -0,0 +1,45 @@
/* vi:set et ai sw=2 sts=2 ts=2: */
/*-
* Copyright (c) 2006 Benedikt Meurer <benny@xfce.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#ifndef __THUNAR_SBR_PROVIDER_H__
#define __THUNAR_SBR_PROVIDER_H__
#include <thunarx/thunarx.h>
G_BEGIN_DECLS;
typedef struct _ThunarSbrProviderClass ThunarSbrProviderClass;
typedef struct _ThunarSbrProvider ThunarSbrProvider;
#define THUNAR_SBR_TYPE_PROVIDER (thunar_sbr_provider_get_type ())
#define THUNAR_SBR_PROVIDER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), THUNAR_SBR_TYPE_PROVIDER, ThunarSbrProvider))
#define THUNAR_SBR_PROVIDER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), THUNAR_SBR_TYPE_PROVIDER, ThunarSbrProviderClass))
#define THUNAR_SBR_IS_PROVIDER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), THUNAR_SBR_TYPE_PROVIDER))
#define THUNAR_SBR_IS_PROVIDER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), THUNAR_SBR_TYPE_PROVIDER))
#define THUNAR_SBR_PROVIDER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), THUNAR_SBR_TYPE_PROVIDER, ThunarSbrProviderClass))
GType
thunar_sbr_provider_get_type (void) G_GNUC_CONST;
void
thunar_sbr_provider_register_type (ThunarxProviderPlugin *plugin);
G_END_DECLS;
#endif /* !__THUNAR_SBR_PROVIDER_H__ */

View file

@ -0,0 +1,609 @@
/* vi:set et ai sw=2 sts=2 ts=2: */
/*-
* Copyright (c) 2006 Benedikt Meurer <benny@xfce.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; 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
#include <exo/exo.h>
#include <thunar-sbr/thunar-sbr-remove-renamer.h>
/* Property identifiers */
enum
{
PROP_0,
PROP_END_OFFSET,
PROP_END_OFFSET_MODE,
PROP_START_OFFSET,
PROP_START_OFFSET_MODE,
};
static void
thunar_sbr_remove_renamer_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec);
static void
thunar_sbr_remove_renamer_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec);
static void
thunar_sbr_remove_renamer_realize (GtkWidget *widget);
static gchar *
thunar_sbr_remove_renamer_process (ThunarxRenamer *renamer,
ThunarxFileInfo *file,
const gchar *text,
guint idx);
static void
thunar_sbr_remove_renamer_update (ThunarSbrRemoveRenamer *remove_renamer);
struct _ThunarSbrRemoveRenamerClass
{
ThunarxRenamerClass __parent__;
};
struct _ThunarSbrRemoveRenamer
{
ThunarxRenamer __parent__;
GtkWidget *end_spinner;
GtkWidget *start_spinner;
guint end_offset;
ThunarSbrOffsetMode end_offset_mode;
guint start_offset;
ThunarSbrOffsetMode start_offset_mode;
};
THUNARX_DEFINE_TYPE (ThunarSbrRemoveRenamer, thunar_sbr_remove_renamer, THUNARX_TYPE_RENAMER);
static void
thunar_sbr_remove_renamer_class_init (ThunarSbrRemoveRenamerClass *klass)
{
ThunarxRenamerClass *thunarxrenamer_class;
GtkWidgetClass *gtkwidget_class;
GObjectClass *gobject_class;
gobject_class = G_OBJECT_CLASS (klass);
gobject_class->get_property = thunar_sbr_remove_renamer_get_property;
gobject_class->set_property = thunar_sbr_remove_renamer_set_property;
gtkwidget_class = GTK_WIDGET_CLASS (klass);
gtkwidget_class->realize = thunar_sbr_remove_renamer_realize;
thunarxrenamer_class = THUNARX_RENAMER_CLASS (klass);
thunarxrenamer_class->process = thunar_sbr_remove_renamer_process;
/**
* ThunarSbrRemoveRenamer:end-offset:
*
* The end offset for the character removal.
**/
g_object_class_install_property (gobject_class,
PROP_END_OFFSET,
g_param_spec_uint ("end-offset",
"end-offset",
"end-offset",
0, G_MAXUINT, 1,
G_PARAM_CONSTRUCT | G_PARAM_READWRITE));
/**
* ThunarSbrRemoveRenamer:end-offset-mode:
*
* The end offset mode for the character removal.
**/
g_object_class_install_property (gobject_class,
PROP_END_OFFSET_MODE,
g_param_spec_enum ("end-offset-mode",
"end-offset-mode",
"end-offset-mode",
THUNAR_SBR_TYPE_OFFSET_MODE,
THUNAR_SBR_OFFSET_MODE_LEFT,
G_PARAM_READWRITE));
/**
* ThunarSbrRemoveRenamer:start-offset:
*
* The start offset for the character removal.
**/
g_object_class_install_property (gobject_class,
PROP_START_OFFSET,
g_param_spec_uint ("start-offset",
"start-offset",
"start-offset",
0, G_MAXUINT, 0,
G_PARAM_READWRITE));
/**
* ThunarSbrRemoveRenamer:start-offset-mode:
*
* The start offset mode for the character removal.
**/
g_object_class_install_property (gobject_class,
PROP_START_OFFSET_MODE,
g_param_spec_enum ("start-offset-mode",
"start-offset-mode",
"start-offset-mode",
THUNAR_SBR_TYPE_OFFSET_MODE,
THUNAR_SBR_OFFSET_MODE_LEFT,
G_PARAM_READWRITE));
}
static void
thunar_sbr_remove_renamer_init (ThunarSbrRemoveRenamer *remove_renamer)
{
AtkRelationSet *relations;
GtkAdjustment *adjustment;
AtkRelation *relation;
GEnumClass *klass;
AtkObject *object;
GtkWidget *combo;
GtkWidget *label;
GtkWidget *grid;
guint n;
grid = gtk_grid_new ();
gtk_grid_set_column_spacing (GTK_GRID (grid), 12);
gtk_grid_set_row_spacing (GTK_GRID (grid), 6);
gtk_box_pack_start (GTK_BOX (remove_renamer), grid, TRUE, TRUE, 0);
gtk_widget_show (grid);
label = gtk_label_new_with_mnemonic (_("Remove _From Position:"));
gtk_label_set_xalign (GTK_LABEL (label), 1.0f);
gtk_grid_attach (GTK_GRID (grid), label, 0, 0, 1, 1);
gtk_widget_show (label);
remove_renamer->start_spinner = gtk_spin_button_new_with_range (0u, G_MAXUINT, 1u);
gtk_entry_set_width_chars (GTK_ENTRY (remove_renamer->start_spinner), 4);
gtk_entry_set_alignment (GTK_ENTRY (remove_renamer->start_spinner), 1.0f);
gtk_entry_set_activates_default (GTK_ENTRY (remove_renamer->start_spinner), TRUE);
gtk_spin_button_set_digits (GTK_SPIN_BUTTON (remove_renamer->start_spinner), 0);
gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (remove_renamer->start_spinner), TRUE);
gtk_spin_button_set_snap_to_ticks (GTK_SPIN_BUTTON (remove_renamer->start_spinner), TRUE);
gtk_grid_attach (GTK_GRID (grid), remove_renamer->start_spinner, 1, 0, 1, 1);
gtk_label_set_mnemonic_widget (GTK_LABEL (label), remove_renamer->start_spinner);
gtk_widget_show (remove_renamer->start_spinner);
adjustment = gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON (remove_renamer->start_spinner));
g_object_bind_property (G_OBJECT (remove_renamer), "start-offset", G_OBJECT (adjustment), "value", G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
/* set Atk label relation for the entry */
object = gtk_widget_get_accessible (remove_renamer->start_spinner);
relations = atk_object_ref_relation_set (gtk_widget_get_accessible (label));
relation = atk_relation_new (&object, 1, ATK_RELATION_LABEL_FOR);
atk_relation_set_add (relations, relation);
g_object_unref (G_OBJECT (relation));
g_object_unref (relations);
combo = gtk_combo_box_text_new ();
klass = g_type_class_ref (THUNAR_SBR_TYPE_OFFSET_MODE);
for (n = 0; n < klass->n_values; ++n)
gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo), _(klass->values[n].value_nick));
g_object_bind_property (G_OBJECT (remove_renamer), "start-offset-mode", G_OBJECT (combo), "active", G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
gtk_grid_attach (GTK_GRID (grid), combo, 2, 0, 1, 1);
g_type_class_unref (klass);
gtk_widget_show (combo);
label = gtk_label_new_with_mnemonic (_("_To Position:"));
gtk_label_set_xalign (GTK_LABEL (label), 1.0f);
gtk_grid_attach (GTK_GRID (grid), label, 0, 1, 1, 1);
gtk_widget_show (label);
remove_renamer->end_spinner = gtk_spin_button_new_with_range (0u, G_MAXUINT, 1u);
gtk_entry_set_width_chars (GTK_ENTRY (remove_renamer->end_spinner), 4);
gtk_entry_set_alignment (GTK_ENTRY (remove_renamer->end_spinner), 1.0f);
gtk_entry_set_activates_default (GTK_ENTRY (remove_renamer->end_spinner), TRUE);
gtk_spin_button_set_digits (GTK_SPIN_BUTTON (remove_renamer->end_spinner), 0);
gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (remove_renamer->end_spinner), TRUE);
gtk_spin_button_set_snap_to_ticks (GTK_SPIN_BUTTON (remove_renamer->end_spinner), TRUE);
gtk_grid_attach (GTK_GRID (grid), remove_renamer->end_spinner, 1, 1, 1, 1);
gtk_label_set_mnemonic_widget (GTK_LABEL (label), remove_renamer->end_spinner);
gtk_widget_show (remove_renamer->end_spinner);
adjustment = gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON (remove_renamer->end_spinner));
g_object_bind_property (G_OBJECT (remove_renamer), "end-offset", G_OBJECT (adjustment), "value", G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
/* set Atk label relation for the entry */
object = gtk_widget_get_accessible (remove_renamer->end_spinner);
relations = atk_object_ref_relation_set (gtk_widget_get_accessible (label));
relation = atk_relation_new (&object, 1, ATK_RELATION_LABEL_FOR);
atk_relation_set_add (relations, relation);
g_object_unref (G_OBJECT (relation));
g_object_unref (relations);
combo = gtk_combo_box_text_new ();
klass = g_type_class_ref (THUNAR_SBR_TYPE_OFFSET_MODE);
for (n = 0; n < klass->n_values; ++n)
gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo), _(klass->values[n].value_nick));
g_object_bind_property (G_OBJECT (remove_renamer), "end-offset-mode", G_OBJECT (combo), "active", G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
gtk_grid_attach (GTK_GRID (grid), combo, 2, 1, 1, 1);
g_type_class_unref (klass);
gtk_widget_show (combo);
}
static void
thunar_sbr_remove_renamer_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
ThunarSbrRemoveRenamer *remove_renamer = THUNAR_SBR_REMOVE_RENAMER (object);
switch (prop_id)
{
case PROP_END_OFFSET:
g_value_set_uint (value, thunar_sbr_remove_renamer_get_end_offset (remove_renamer));
break;
case PROP_END_OFFSET_MODE:
g_value_set_enum (value, thunar_sbr_remove_renamer_get_end_offset_mode (remove_renamer));
break;
case PROP_START_OFFSET:
g_value_set_uint (value, thunar_sbr_remove_renamer_get_start_offset (remove_renamer));
break;
case PROP_START_OFFSET_MODE:
g_value_set_enum (value, thunar_sbr_remove_renamer_get_start_offset_mode (remove_renamer));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
thunar_sbr_remove_renamer_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
ThunarSbrRemoveRenamer *remove_renamer = THUNAR_SBR_REMOVE_RENAMER (object);
switch (prop_id)
{
case PROP_END_OFFSET:
thunar_sbr_remove_renamer_set_end_offset (remove_renamer, g_value_get_uint (value));
break;
case PROP_END_OFFSET_MODE:
thunar_sbr_remove_renamer_set_end_offset_mode (remove_renamer, g_value_get_enum (value));
break;
case PROP_START_OFFSET:
thunar_sbr_remove_renamer_set_start_offset (remove_renamer, g_value_get_uint (value));
break;
case PROP_START_OFFSET_MODE:
thunar_sbr_remove_renamer_set_start_offset_mode (remove_renamer, g_value_get_enum (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
thunar_sbr_remove_renamer_realize (GtkWidget *widget)
{
/* realize the renamer widget */
(*GTK_WIDGET_CLASS (thunar_sbr_remove_renamer_parent_class)->realize) (widget);
/* update the renamer state */
thunar_sbr_remove_renamer_update (THUNAR_SBR_REMOVE_RENAMER (widget));
}
static gchar *
thunar_sbr_remove_renamer_process (ThunarxRenamer *renamer,
ThunarxFileInfo *file,
const gchar *text,
guint idx)
{
ThunarSbrRemoveRenamer *remove_renamer = THUNAR_SBR_REMOVE_RENAMER (renamer);
const gchar *start_pointer;
const gchar *end_pointer;
GString *result;
guint start_offset;
guint text_length;
guint end_offset;
/* determine the text length */
text_length = g_utf8_strlen (text, -1);
/* determine the offsets for this file name */
end_offset = (remove_renamer->end_offset_mode == THUNAR_SBR_OFFSET_MODE_LEFT)
? remove_renamer->end_offset
: (text_length - remove_renamer->end_offset);
start_offset = (remove_renamer->start_offset_mode == THUNAR_SBR_OFFSET_MODE_LEFT)
? remove_renamer->start_offset
: (text_length - remove_renamer->start_offset);
/* check if anything should be removed */
if (G_UNLIKELY (start_offset >= end_offset || end_offset > text_length))
return g_strdup (text);
/* determine start and end pointers */
end_pointer = g_utf8_offset_to_pointer (text, end_offset);
start_pointer = g_utf8_offset_to_pointer (text, start_offset);
/* allocate the resulting string */
result = g_string_sized_new (text_length);
g_string_append_len (result, text, start_pointer - text);
g_string_append (result, end_pointer);
return g_string_free (result, FALSE);
}
static void
thunar_sbr_remove_renamer_update (ThunarSbrRemoveRenamer *remove_renamer)
{
guint start_offset;
guint end_offset;
/* check if the renamer is realized */
if (gtk_widget_get_realized (GTK_WIDGET (remove_renamer)))
{
/* check if start and end offset make sense */
end_offset = (remove_renamer->end_offset_mode == THUNAR_SBR_OFFSET_MODE_LEFT)
? remove_renamer->end_offset
: (G_MAXUINT - remove_renamer->end_offset);
start_offset = (remove_renamer->start_offset_mode == THUNAR_SBR_OFFSET_MODE_LEFT)
? remove_renamer->start_offset
: (G_MAXUINT - remove_renamer->start_offset);
/* highlight invalid input by using theme specific colors */
if (G_UNLIKELY (start_offset >= end_offset))
{
gtk_style_context_add_class (gtk_widget_get_style_context (GTK_WIDGET (remove_renamer->start_spinner)), "error");
gtk_style_context_add_class (gtk_widget_get_style_context (GTK_WIDGET (remove_renamer->end_spinner)), "error");
}
else
{
gtk_style_context_remove_class (gtk_widget_get_style_context (GTK_WIDGET (remove_renamer->start_spinner)), "error");
gtk_style_context_remove_class (gtk_widget_get_style_context (GTK_WIDGET (remove_renamer->end_spinner)), "error");
}
}
/* tell the bulk renamer that we have a new state */
thunarx_renamer_changed (THUNARX_RENAMER (remove_renamer));
}
/**
* thunar_sbr_remove_renamer_new:
*
* Allocates a new #ThunarSbrRemoveRenamer instance.
*
* Return value: the newly allocated #ThunarSbrRemoveRenamer.
**/
ThunarSbrRemoveRenamer *
thunar_sbr_remove_renamer_new (void)
{
return g_object_new (THUNAR_SBR_TYPE_REMOVE_RENAMER,
"name", _("Remove Characters"),
NULL);
}
/**
* thunar_sbr_remove_renamer_get_end_offset:
* @remove_renamer : a #ThunarSbrRemoveRenamer.
*
* Returns the end offset of the character removal.
*
* Return value: the end offset for @remove_renamer.
**/
guint
thunar_sbr_remove_renamer_get_end_offset (ThunarSbrRemoveRenamer *remove_renamer)
{
g_return_val_if_fail (THUNAR_SBR_IS_REMOVE_RENAMER (remove_renamer), 0);
return remove_renamer->end_offset;
}
/**
* thunar_sbr_remove_renamer_set_end_offset:
* @remove_renamer : a #ThunarSbrRemoveRenamer.
* @end_offset : the new end offset.
*
* Sets the end offset of @remove_renamer to @end_offset.
**/
void
thunar_sbr_remove_renamer_set_end_offset (ThunarSbrRemoveRenamer *remove_renamer,
guint end_offset)
{
g_return_if_fail (THUNAR_SBR_IS_REMOVE_RENAMER (remove_renamer));
/* check if we have a new value */
if (G_LIKELY (remove_renamer->end_offset != end_offset))
{
/* apply the new setting */
remove_renamer->end_offset = end_offset;
/* update the renamer */
thunar_sbr_remove_renamer_update (remove_renamer);
/* notify listeners */
g_object_notify (G_OBJECT (remove_renamer), "end-offset");
}
}
/**
* thunar_sbr_remove_renamer_get_end_offset_mode:
* @remove_renamer : a #ThunarSbrRemoveRenamer.
*
* Returns the end offset mode for @remove_renamer.
*
* Return value: the end offset mode for @remove_renamer.
**/
ThunarSbrOffsetMode
thunar_sbr_remove_renamer_get_end_offset_mode (ThunarSbrRemoveRenamer *remove_renamer)
{
g_return_val_if_fail (THUNAR_SBR_IS_REMOVE_RENAMER (remove_renamer), THUNAR_SBR_OFFSET_MODE_LEFT);
return remove_renamer->end_offset_mode;
}
/**
* thunar_sbr_remove_renamer_set_end_offset_mode:
* @remove_renamer : a #ThunarSbrRemoveRenamer.
* @end_offset_mode : the new end #ThunarSbrOffsetMode.
*
* Sets the end offset mode for @remove_renamer to @end_offset_mode.
**/
void
thunar_sbr_remove_renamer_set_end_offset_mode (ThunarSbrRemoveRenamer *remove_renamer,
ThunarSbrOffsetMode end_offset_mode)
{
g_return_if_fail (THUNAR_SBR_IS_REMOVE_RENAMER (remove_renamer));
/* check if we have a new value */
if (G_LIKELY (remove_renamer->end_offset_mode != end_offset_mode))
{
/* apply the new setting */
remove_renamer->end_offset_mode = end_offset_mode;
/* update the renamer */
thunar_sbr_remove_renamer_update (remove_renamer);
/* notify listeners */
g_object_notify (G_OBJECT (remove_renamer), "end-offset-mode");
}
}
/**
* thunar_sbr_remove_renamer_get_start_offset:
* @remove_renamer : a #ThunarSbrRemoveRenamer.
*
* Returns the start offset of the character removal.
*
* Return value: the start offset for @remove_renamer.
**/
guint
thunar_sbr_remove_renamer_get_start_offset (ThunarSbrRemoveRenamer *remove_renamer)
{
g_return_val_if_fail (THUNAR_SBR_IS_REMOVE_RENAMER (remove_renamer), 0);
return remove_renamer->start_offset;
}
/**
* thunar_sbr_remove_renamer_set_start_offset:
* @remove_renamer : a #ThunarSbrRemoveRenamer.
* @start_offset : the new start offset.
*
* Sets the start offset of @remove_renamer to @start_offset.
**/
void
thunar_sbr_remove_renamer_set_start_offset (ThunarSbrRemoveRenamer *remove_renamer,
guint start_offset)
{
g_return_if_fail (THUNAR_SBR_IS_REMOVE_RENAMER (remove_renamer));
/* check if we have a new value */
if (G_LIKELY (remove_renamer->start_offset != start_offset))
{
/* apply the new setting */
remove_renamer->start_offset = start_offset;
/* update the renamer */
thunar_sbr_remove_renamer_update (remove_renamer);
/* notify listeners */
g_object_notify (G_OBJECT (remove_renamer), "start-offset");
}
}
/**
* thunar_sbr_remove_renamer_get_start_offset_mode:
* @remove_renamer : a #ThunarSbrRemoveRenamer.
*
* Returns the start offset mode for @remove_renamer.
*
* Return value: the start offset mode for @remove_renamer.
**/
ThunarSbrOffsetMode
thunar_sbr_remove_renamer_get_start_offset_mode (ThunarSbrRemoveRenamer *remove_renamer)
{
g_return_val_if_fail (THUNAR_SBR_IS_REMOVE_RENAMER (remove_renamer), THUNAR_SBR_OFFSET_MODE_LEFT);
return remove_renamer->start_offset_mode;
}
/**
* thunar_sbr_remove_renamer_set_start_offset_mode:
* @remove_renamer : a #ThunarSbrRemoveRenamer.
* @start_offset_mode : the new start #ThunarSbrOffsetMode.
*
* Sets the start offset mode for @remove_renamer to @start_offset_mode.
**/
void
thunar_sbr_remove_renamer_set_start_offset_mode (ThunarSbrRemoveRenamer *remove_renamer,
ThunarSbrOffsetMode start_offset_mode)
{
g_return_if_fail (THUNAR_SBR_IS_REMOVE_RENAMER (remove_renamer));
/* check if we have a new value */
if (G_LIKELY (remove_renamer->start_offset_mode != start_offset_mode))
{
/* apply the new setting */
remove_renamer->start_offset_mode = start_offset_mode;
/* update the renamer */
thunar_sbr_remove_renamer_update (remove_renamer);
/* notify listeners */
g_object_notify (G_OBJECT (remove_renamer), "start-offset-mode");
}
}

View file

@ -0,0 +1,72 @@
/* vi:set et ai sw=2 sts=2 ts=2: */
/*-
* Copyright (c) 2006 Benedikt Meurer <benny@xfce.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#ifndef __THUNAR_SBR_REMOVE_RENAMER_H__
#define __THUNAR_SBR_REMOVE_RENAMER_H__
#include <thunar-sbr/thunar-sbr-enum-types.h>
G_BEGIN_DECLS;
typedef struct _ThunarSbrRemoveRenamerClass ThunarSbrRemoveRenamerClass;
typedef struct _ThunarSbrRemoveRenamer ThunarSbrRemoveRenamer;
#define THUNAR_SBR_TYPE_REMOVE_RENAMER (thunar_sbr_remove_renamer_get_type ())
#define THUNAR_SBR_REMOVE_RENAMER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), THUNAR_SBR_TYPE_REMOVE_RENAMER, ThunarSbrRemoveRenamer))
#define THUNAR_SBR_REMOVE_RENAMER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), THUNAR_SBR_TYPE_REMOVE_RENAMER, ThunarSbrRemoveRenamerClass))
#define THUNAR_SBR_IS_REMOVE_RENAMER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), THUNAR_SBR_TYPE_REMOVE_RENAMER))
#define THUNAR_SBR_IS_REMOVE_RENAMER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), THUNAR_SBR_TYPE_REMOVE_RENAMER))
#define THUNAR_SBR_REMOVE_RENAMER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), THUNAR_SBR_TYPE_REMOVE_RENAMER, ThunarSbrRemoveRenamerClass))
GType
thunar_sbr_remove_renamer_get_type (void) G_GNUC_CONST;
void
thunar_sbr_remove_renamer_register_type (ThunarxProviderPlugin *plugin);
ThunarSbrRemoveRenamer *
thunar_sbr_remove_renamer_new (void) G_GNUC_MALLOC;
guint
thunar_sbr_remove_renamer_get_end_offset (ThunarSbrRemoveRenamer *remove_renamer);
void
thunar_sbr_remove_renamer_set_end_offset (ThunarSbrRemoveRenamer *remove_renamer,
guint end_offset);
ThunarSbrOffsetMode
thunar_sbr_remove_renamer_get_end_offset_mode (ThunarSbrRemoveRenamer *remove_renamer);
void
thunar_sbr_remove_renamer_set_end_offset_mode (ThunarSbrRemoveRenamer *remove_renamer,
ThunarSbrOffsetMode end_offset_mode);
guint
thunar_sbr_remove_renamer_get_start_offset (ThunarSbrRemoveRenamer *remove_renamer);
void
thunar_sbr_remove_renamer_set_start_offset (ThunarSbrRemoveRenamer *remove_renamer,
guint start_offset);
ThunarSbrOffsetMode
thunar_sbr_remove_renamer_get_start_offset_mode (ThunarSbrRemoveRenamer *remove_renamer);
void
thunar_sbr_remove_renamer_set_start_offset_mode (ThunarSbrRemoveRenamer *remove_renamer,
ThunarSbrOffsetMode start_offset_mode);
G_END_DECLS;
#endif /* !__THUNAR_SBR_REMOVE_RENAMER_H__ */

View file

@ -0,0 +1,814 @@
/* vi:set et ai sw=2 sts=2 ts=2: */
/*-
* Copyright (c) 2006 Benedikt Meurer <benny@xfce.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; 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_PCRE2
#define PCRE2_CODE_UNIT_WIDTH 8
#include <pcre2.h>
#endif
#include <libxfce4util/libxfce4util.h>
#include <thunar-sbr/thunar-sbr-replace-renamer.h>
/* Property identifiers */
enum
{
PROP_0,
PROP_CASE_SENSITIVE,
PROP_PATTERN,
PROP_REPLACEMENT,
PROP_REGEXP,
};
static void
thunar_sbr_replace_renamer_finalize (GObject *object);
static void
thunar_sbr_replace_renamer_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec);
static void
thunar_sbr_replace_renamer_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec);
static void
thunar_sbr_replace_renamer_realize (GtkWidget *widget);
static gchar *
thunar_sbr_replace_renamer_process (ThunarxRenamer *renamer,
ThunarxFileInfo *file,
const gchar *text,
guint idx);
#ifdef HAVE_PCRE2
static gchar *
thunar_sbr_replace_renamer_pcre_exec (ThunarSbrReplaceRenamer *replace_renamer,
const gchar *text);
static void
thunar_sbr_replace_renamer_pcre_update (ThunarSbrReplaceRenamer *replace_renamer);
#endif
struct _ThunarSbrReplaceRenamerClass
{
ThunarxRenamerClass __parent__;
};
struct _ThunarSbrReplaceRenamer
{
ThunarxRenamer __parent__;
GtkWidget *pattern_entry;
gboolean case_sensitive;
gboolean regexp;
gchar *pattern;
gchar *replacement;
/* TRUE if PCRE is available and supports UTF-8 */
gint utf8_regexp_supported;
/* PCRE compiled pattern */
#ifdef HAVE_PCRE2
pcre2_code *pcre_pattern;
gint pcre_capture_count;
#endif
};
THUNARX_DEFINE_TYPE (ThunarSbrReplaceRenamer, thunar_sbr_replace_renamer, THUNARX_TYPE_RENAMER);
static void
thunar_sbr_replace_renamer_class_init (ThunarSbrReplaceRenamerClass *klass)
{
ThunarxRenamerClass *thunarxrenamer_class;
GtkWidgetClass *gtkwidget_class;
GObjectClass *gobject_class;
gobject_class = G_OBJECT_CLASS (klass);
gobject_class->finalize = thunar_sbr_replace_renamer_finalize;
gobject_class->get_property = thunar_sbr_replace_renamer_get_property;
gobject_class->set_property = thunar_sbr_replace_renamer_set_property;
gtkwidget_class = GTK_WIDGET_CLASS (klass);
gtkwidget_class->realize = thunar_sbr_replace_renamer_realize;
thunarxrenamer_class = THUNARX_RENAMER_CLASS (klass);
thunarxrenamer_class->process = thunar_sbr_replace_renamer_process;
/**
* ThunarSbrReplaceRenamer:case-sensitive:
*
* Whether to use case sensitive search and replace.
**/
g_object_class_install_property (gobject_class,
PROP_CASE_SENSITIVE,
g_param_spec_boolean ("case-sensitive",
"case-sensitive",
"case-sensitive",
FALSE,
G_PARAM_READWRITE));
/**
* ThunarSbrReplaceRenamer:pattern:
*
* The search pattern.
**/
g_object_class_install_property (gobject_class,
PROP_PATTERN,
g_param_spec_string ("pattern",
"pattern",
"pattern",
NULL,
G_PARAM_READWRITE));
/**
* ThunarSbrReplaceRenamer:replacement:
*
* The replacement text.
**/
g_object_class_install_property (gobject_class,
PROP_REPLACEMENT,
g_param_spec_string ("replacement",
"replacement",
"replacement",
NULL,
G_PARAM_READWRITE));
/**
* ThunarSbrReplaceRenamer:regexp:
*
* Whether to use regular expressions.
**/
g_object_class_install_property (gobject_class,
PROP_REGEXP,
g_param_spec_boolean ("regexp",
"regexp",
"regexp",
FALSE,
G_PARAM_READWRITE));
}
static void
thunar_sbr_replace_renamer_init (ThunarSbrReplaceRenamer *replace_renamer)
{
AtkRelationSet *relations;
AtkRelation *relation;
AtkObject *object;
GtkWidget *grid;
GtkWidget *label;
GtkWidget *entry;
GtkWidget *button;
uint32_t pcre2_compiled_widths;
replace_renamer->utf8_regexp_supported = FALSE;
#ifdef HAVE_PCRE2
/* check if PCRE supports UTF-8 */
if (pcre2_config (PCRE2_CONFIG_COMPILED_WIDTHS, &pcre2_compiled_widths) >= 0)
{
/* bit0 indicates 8-bit support. bit1 and bit2 indicate 16-bit and 32-bit support respectively. */
if ((pcre2_compiled_widths & (1 << 0)) != 0)
replace_renamer->utf8_regexp_supported = TRUE;
}
#endif
grid = gtk_grid_new ();
gtk_grid_set_column_spacing (GTK_GRID (grid), 12);
gtk_grid_set_row_spacing (GTK_GRID (grid), 6);
gtk_box_pack_start (GTK_BOX (replace_renamer), grid, FALSE, FALSE, 0);
gtk_widget_show (grid);
label = gtk_label_new_with_mnemonic (_("_Search For:"));
gtk_label_set_xalign (GTK_LABEL (label), 1.0f);
gtk_grid_attach (GTK_GRID (grid), label, 0, 0, 1, 1);
gtk_widget_show (label);
replace_renamer->pattern_entry = gtk_entry_new ();
gtk_entry_set_activates_default (GTK_ENTRY (replace_renamer->pattern_entry), TRUE);
g_object_bind_property (G_OBJECT (replace_renamer->pattern_entry), "text", G_OBJECT (replace_renamer), "pattern", G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
gtk_widget_set_tooltip_text (replace_renamer->pattern_entry, _("Enter the text to search for in the file names."));
gtk_widget_set_hexpand (replace_renamer->pattern_entry, TRUE);
gtk_grid_attach (GTK_GRID (grid), replace_renamer->pattern_entry, 1, 0, 1, 1);
gtk_label_set_mnemonic_widget (GTK_LABEL (label), replace_renamer->pattern_entry);
gtk_widget_show (replace_renamer->pattern_entry);
/* set Atk label relation for the entry */
object = gtk_widget_get_accessible (replace_renamer->pattern_entry);
relations = atk_object_ref_relation_set (gtk_widget_get_accessible (label));
relation = atk_relation_new (&object, 1, ATK_RELATION_LABEL_FOR);
atk_relation_set_add (relations, relation);
g_object_unref (G_OBJECT (relation));
g_object_unref (relations);
button = gtk_check_button_new_with_mnemonic (_("Regular _Expression"));
g_object_bind_property (G_OBJECT (button), "active", G_OBJECT (replace_renamer), "regexp", G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
gtk_widget_set_tooltip_text (button, _("If you enable this option, the pattern will be treated as a regular expression and "
"matched using the Perl-compatible regular expressions (PCRE). Check the documentation "
"for details about the regular expression syntax."));
gtk_grid_attach (GTK_GRID (grid), button, 2, 0, 1, 1);
gtk_widget_set_sensitive (button, replace_renamer->utf8_regexp_supported);
gtk_widget_show (button);
label = gtk_label_new_with_mnemonic (_("Replace _With:"));
gtk_label_set_xalign (GTK_LABEL (label), 0.0f);
gtk_grid_attach (GTK_GRID (grid), label, 0, 1, 1, 1);
gtk_widget_show (label);
entry = gtk_entry_new ();
gtk_entry_set_activates_default (GTK_ENTRY (entry), TRUE);
g_object_bind_property (G_OBJECT (entry), "text", G_OBJECT (replace_renamer), "replacement", G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
gtk_widget_set_tooltip_text (entry, _("Enter the text that should be used as replacement for the pattern above."));
gtk_widget_set_hexpand (entry, TRUE);
gtk_grid_attach (GTK_GRID (grid), entry, 1, 1, 1, 1);
gtk_label_set_mnemonic_widget (GTK_LABEL (label), entry);
gtk_widget_show (entry);
/* set Atk label relation for the entry */
object = gtk_widget_get_accessible (entry);
relations = atk_object_ref_relation_set (gtk_widget_get_accessible (label));
relation = atk_relation_new (&object, 1, ATK_RELATION_LABEL_FOR);
atk_relation_set_add (relations, relation);
g_object_unref (G_OBJECT (relation));
g_object_unref (relations);
button = gtk_check_button_new_with_mnemonic (_("C_ase Sensitive Search"));
g_object_bind_property (G_OBJECT (button), "active", G_OBJECT (replace_renamer), "case-sensitive", G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
gtk_widget_set_tooltip_text (button, _("If you enable this option, the pattern will be searched in a case-sensitive manner. "
"The default is to use a case-insensitive search."));
gtk_grid_attach (GTK_GRID (grid), button, 2, 1, 1, 1);
gtk_widget_show (button);
}
static void
thunar_sbr_replace_renamer_finalize (GObject *object)
{
ThunarSbrReplaceRenamer *replace_renamer = THUNAR_SBR_REPLACE_RENAMER (object);
/* release the PCRE pattern (if any) */
#ifdef HAVE_PCRE2
if (G_UNLIKELY (replace_renamer->pcre_pattern != NULL))
pcre2_code_free (replace_renamer->pcre_pattern);
#endif
/* release the strings */
g_free (replace_renamer->replacement);
g_free (replace_renamer->pattern);
(*G_OBJECT_CLASS (thunar_sbr_replace_renamer_parent_class)->finalize) (object);
}
static void
thunar_sbr_replace_renamer_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
ThunarSbrReplaceRenamer *replace_renamer = THUNAR_SBR_REPLACE_RENAMER (object);
switch (prop_id)
{
case PROP_CASE_SENSITIVE:
g_value_set_boolean (value, thunar_sbr_replace_renamer_get_case_sensitive (replace_renamer));
break;
case PROP_PATTERN:
g_value_set_string (value, thunar_sbr_replace_renamer_get_pattern (replace_renamer));
break;
case PROP_REPLACEMENT:
g_value_set_string (value, thunar_sbr_replace_renamer_get_replacement (replace_renamer));
break;
case PROP_REGEXP:
g_value_set_boolean (value, thunar_sbr_replace_renamer_get_regexp (replace_renamer));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
thunar_sbr_replace_renamer_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
ThunarSbrReplaceRenamer *replace_renamer = THUNAR_SBR_REPLACE_RENAMER (object);
switch (prop_id)
{
case PROP_CASE_SENSITIVE:
thunar_sbr_replace_renamer_set_case_sensitive (replace_renamer, g_value_get_boolean (value));
break;
case PROP_PATTERN:
thunar_sbr_replace_renamer_set_pattern (replace_renamer, g_value_get_string (value));
break;
case PROP_REPLACEMENT:
thunar_sbr_replace_renamer_set_replacement (replace_renamer, g_value_get_string (value));
break;
case PROP_REGEXP:
thunar_sbr_replace_renamer_set_regexp (replace_renamer, g_value_get_boolean (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
thunar_sbr_replace_renamer_realize (GtkWidget *widget)
{
/* realize the widget */
(*GTK_WIDGET_CLASS (thunar_sbr_replace_renamer_parent_class)->realize) (widget);
#ifdef HAVE_PCRE2
/* update the PCRE pattern */
thunar_sbr_replace_renamer_pcre_update (THUNAR_SBR_REPLACE_RENAMER (widget));
#endif
}
static gchar *
tsrr_replace (const gchar *text,
const gchar *pattern,
const gchar *replacement,
gboolean case_sensitive)
{
const gchar *p;
const gchar *t;
gunichar pc;
gunichar tc;
GString *result = g_string_sized_new (32);
while (*text != '\0')
{
/* compare the pattern to this part of the text */
for (p = pattern, t = text; *p != '\0' && *t != '\0'; p = g_utf8_next_char (p), t = g_utf8_next_char (t))
{
/* determine the next unichars */
pc = g_utf8_get_char (p);
tc = g_utf8_get_char (t);
/* check if the chars don't match */
if (pc != tc && (case_sensitive || g_unichar_toupper (pc) != g_unichar_toupper (tc)))
break;
}
/* check if the pattern matches */
if (G_UNLIKELY (*p == '\0'))
{
/* append the replacement to the result... */
g_string_append (result, replacement);
/* ...and skip to the text after the pattern */
text = t;
}
else
{
/* just append the text char */
g_string_append_unichar (result, g_utf8_get_char (text));
text = g_utf8_next_char (text);
}
}
return g_string_free (result, FALSE);
}
static gchar *
thunar_sbr_replace_renamer_process (ThunarxRenamer *renamer,
ThunarxFileInfo *file,
const gchar *text,
guint idx)
{
ThunarSbrReplaceRenamer *replace_renamer = THUNAR_SBR_REPLACE_RENAMER (renamer);
/* nothing to replace if we don't have a pattern */
if (G_UNLIKELY (replace_renamer->pattern == NULL || *replace_renamer->pattern == '\0'))
return g_strdup (text);
/* check if we should use regular expression */
if (G_UNLIKELY (replace_renamer->regexp))
{
#ifdef HAVE_PCRE2
/* check if the pattern failed to compile */
if (G_UNLIKELY (replace_renamer->pcre_pattern == NULL))
return g_strdup (text);
/* just execute the pattern */
return thunar_sbr_replace_renamer_pcre_exec (replace_renamer, text);
#endif
}
/* perform the replace operation */
return tsrr_replace (text, replace_renamer->pattern, replace_renamer->replacement, replace_renamer->case_sensitive);
}
#ifdef HAVE_PCRE2
static gchar *
thunar_sbr_replace_renamer_pcre_exec (ThunarSbrReplaceRenamer *replace_renamer,
const gchar *subject)
{
GString *result;
int error;
PCRE2_SIZE erroffset;
gchar output[1024];
pcre2_code *compiled_pattern;
PCRE2_SIZE outlen;
int n_substitutions; /* number of substitutions that were carried out */
result = g_string_sized_new (32);
compiled_pattern = pcre2_compile_8 ((PCRE2_SPTR) replace_renamer->pattern,
PCRE2_ZERO_TERMINATED,
0,
&error,
&erroffset,
0);
if (compiled_pattern == NULL)
return g_strdup (subject);
pcre2_jit_compile (compiled_pattern, PCRE2_JIT_COMPLETE);
outlen = sizeof (output) / sizeof (PCRE2_UCHAR);
n_substitutions = pcre2_substitute (compiled_pattern,
(PCRE2_SPTR) subject,
PCRE2_ZERO_TERMINATED,
0,
PCRE2_SUBSTITUTE_GLOBAL | PCRE2_SUBSTITUTE_EXTENDED,
0,
0,
(PCRE2_SPTR) replace_renamer->replacement,
PCRE2_ZERO_TERMINATED,
(PCRE2_UCHAR *) output,
&outlen);
if (n_substitutions < 0)
{
PCRE2_UCHAR buffer[256];
pcre2_get_error_message (error, buffer, sizeof (buffer));
g_warning ("PCRE2 substitution failed at offset %d: %s\n", (int) erroffset, buffer);
return g_strdup (subject);
}
pcre2_code_free (compiled_pattern);
for (size_t i = 0; i < outlen; i++)
g_string_append_c (result, output[i]);
return g_string_free (result, FALSE);
}
static void
thunar_sbr_replace_renamer_pcre_update (ThunarSbrReplaceRenamer *replace_renamer)
{
const gchar *error_message = NULL;
gchar *tooltip;
gchar *message;
glong offset;
gint error_offset = -1;
int error;
PCRE2_SIZE erroffset;
/* pre-compile the pattern if regexp is enabled */
if (G_UNLIKELY (replace_renamer->regexp))
{
/* release the previous pattern (if any) */
if (G_LIKELY (replace_renamer->pcre_pattern != NULL))
pcre2_code_free (replace_renamer->pcre_pattern);
/* try to compile the new pattern */
replace_renamer->pcre_pattern = pcre2_compile ((PCRE2_SPTR) replace_renamer->pattern, PCRE2_ZERO_TERMINATED, 0, &error, &erroffset, 0);
if (replace_renamer->pcre_pattern == NULL)
{
PCRE2_UCHAR buffer[256];
pcre2_get_error_message (error, buffer, sizeof (buffer));
g_warning ("PCRE2 compilation failed at offset %d: %s\n", (int) erroffset, buffer);
}
}
/* check if there was an error compiling the pattern */
if (G_UNLIKELY (error_message != NULL))
{
/* convert the message to UTF-8 */
message = g_locale_to_utf8 (error_message, -1, NULL, NULL, NULL);
if (G_LIKELY (message != NULL))
{
/* determine the UTF-8 char offset */
offset = g_utf8_pointer_to_offset (replace_renamer->pattern, replace_renamer->pattern + error_offset);
/* setup a tooltip with the error message */
tooltip = g_strdup_printf (_("Invalid regular expression, at character position %ld: %s"), offset, message);
gtk_widget_set_tooltip_text (replace_renamer->pattern_entry, tooltip);
g_free (tooltip);
}
g_free (message);
/* check if the entry is realized */
if (gtk_widget_get_realized (replace_renamer->pattern_entry))
{
/* highlight invalid input by using theme-specific colors */
gtk_style_context_add_class (gtk_widget_get_style_context (GTK_WIDGET (replace_renamer->pattern_entry)), "error");
}
}
else
{
/* check if the entry is realized */
if (gtk_widget_get_realized (replace_renamer->pattern_entry))
{
/* stop highlight of invalid input if any */
gtk_style_context_remove_class (gtk_widget_get_style_context (GTK_WIDGET (replace_renamer->pattern_entry)), "error");
}
/* reset to default tooltip */
gtk_widget_set_tooltip_text (replace_renamer->pattern_entry, _("Enter the text to search for in the file names."));
}
}
#endif
/**
* thunar_sbr_replace_renamer_new:
*
* Allocates a new #ThunarSbrReplaceRenamer instance.
*
* Return value: the newly allocated #ThunarSbrReplaceRenamer.
**/
ThunarSbrReplaceRenamer *
thunar_sbr_replace_renamer_new (void)
{
return g_object_new (THUNAR_SBR_TYPE_REPLACE_RENAMER,
"name", _("Search & Replace"),
NULL);
}
/**
* thunar_sbr_replace_renamer_get_case_sensitive:
* @replace_renamer : a #ThunarSbrReplaceRenamer.
*
* Returns %TRUE if @replace_renamer<!---->s search should
* be case sensitive.
*
* Return value: %TRUE if @replace_renamer is case sensitive.
**/
gboolean
thunar_sbr_replace_renamer_get_case_sensitive (ThunarSbrReplaceRenamer *replace_renamer)
{
g_return_val_if_fail (THUNAR_SBR_IS_REPLACE_RENAMER (replace_renamer), FALSE);
return replace_renamer->case_sensitive;
}
/**
* thunar_sbr_replace_renamer_set_case_sensitive:
* @replace_renamer : a #ThunarSbrReplaceRenamer.
* @case_sensitive : %TRUE to use case sensitive search.
*
* If @case_sensitive is %TRUE the search of @replace_renamer
* will be case sensitive.
**/
void
thunar_sbr_replace_renamer_set_case_sensitive (ThunarSbrReplaceRenamer *replace_renamer,
gboolean case_sensitive)
{
g_return_if_fail (THUNAR_SBR_IS_REPLACE_RENAMER (replace_renamer));
/* normalize the value */
case_sensitive = !!case_sensitive;
/* check if we have a new setting */
if (G_LIKELY (replace_renamer->case_sensitive != case_sensitive))
{
/* apply the new value */
replace_renamer->case_sensitive = case_sensitive;
#ifdef HAVE_PCRE2
/* pre-compile the pattern */
thunar_sbr_replace_renamer_pcre_update (replace_renamer);
#endif
/* update the renamer */
thunarx_renamer_changed (THUNARX_RENAMER (replace_renamer));
/* notify listeners */
g_object_notify (G_OBJECT (replace_renamer), "case-sensitive");
}
}
/**
* thunar_sbr_replace_renamer_get_pattern:
* @replace_renamer : a #ThunarSbrReplaceRenamer.
*
* Returns the search pattern for @replace_renamer.
*
* Return value: the search pattern for @replace_renamer.
**/
const gchar *
thunar_sbr_replace_renamer_get_pattern (ThunarSbrReplaceRenamer *replace_renamer)
{
g_return_val_if_fail (THUNAR_SBR_IS_REPLACE_RENAMER (replace_renamer), NULL);
return replace_renamer->pattern;
}
/**
* thunar_sbr_replace_renamer_set_pattern:
* @replace_renamer : a #ThunarSbrReplaceRenamer.
* @pattern : the new pattern for @replace_renamer.
*
* Sets the search pattern of @replace_renamer to @pattern.
**/
void
thunar_sbr_replace_renamer_set_pattern (ThunarSbrReplaceRenamer *replace_renamer,
const gchar *pattern)
{
g_return_if_fail (THUNAR_SBR_IS_REPLACE_RENAMER (replace_renamer));
g_return_if_fail (g_utf8_validate (pattern, -1, NULL));
/* check if we have a new pattern */
if (g_strcmp0 (replace_renamer->pattern, pattern) != 0)
{
/* apply the new value */
g_free (replace_renamer->pattern);
replace_renamer->pattern = g_strdup (pattern);
#ifdef HAVE_PCRE2
/* pre-compile the pattern */
thunar_sbr_replace_renamer_pcre_update (replace_renamer);
#endif
/* update the renamer */
thunarx_renamer_changed (THUNARX_RENAMER (replace_renamer));
/* notify listeners */
g_object_notify (G_OBJECT (replace_renamer), "pattern");
}
}
/**
* thunar_sbr_replace_renamer_get_regexp:
* @replace_renamer : a #ThunarSbrReplaceRenamer.
*
* Returns %TRUE if @replace_renamer should use a regular
* expression.
*
* Return value: %TRUE if @replace_renamer uses a regexp.
**/
gboolean
thunar_sbr_replace_renamer_get_regexp (ThunarSbrReplaceRenamer *replace_renamer)
{
g_return_val_if_fail (THUNAR_SBR_IS_REPLACE_RENAMER (replace_renamer), FALSE);
return replace_renamer->regexp;
}
/**
* thunar_sbr_replace_renamer_set_regexp:
* @replace_renamer : a #ThunarSbrReplaceRenamer.
* @regexp : %TRUE to use regular expressions.
*
* If @regexp is %TRUE a regular expression should be used
* for @replace_renamer.
**/
void
thunar_sbr_replace_renamer_set_regexp (ThunarSbrReplaceRenamer *replace_renamer,
gboolean regexp)
{
g_return_if_fail (THUNAR_SBR_IS_REPLACE_RENAMER (replace_renamer));
/* normalize the value */
regexp = (!!regexp && replace_renamer->utf8_regexp_supported);
/* check if we have a new value */
if (G_LIKELY (replace_renamer->regexp != regexp))
{
/* apply the new value */
replace_renamer->regexp = regexp;
#ifdef HAVE_PCRE2
/* pre-compile the pattern */
thunar_sbr_replace_renamer_pcre_update (replace_renamer);
#endif
/* update the renamer */
thunarx_renamer_changed (THUNARX_RENAMER (replace_renamer));
/* notify listeners */
g_object_notify (G_OBJECT (replace_renamer), "regexp");
}
}
/**
* thunar_sbr_replace_renamer_get_replacement:
* @replace_renamer : a #ThunarSbrReplaceRenamer.
*
* Returns the replacement for the @replace_renamer.
*
* Return value: the replacement for @replace_renamer.
**/
const gchar *
thunar_sbr_replace_renamer_get_replacement (ThunarSbrReplaceRenamer *replace_renamer)
{
g_return_val_if_fail (THUNAR_SBR_IS_REPLACE_RENAMER (replace_renamer), NULL);
return replace_renamer->replacement;
}
/**
* thunar_sbr_replace_renamer_set_replacement:
* @replace_renamer : a #ThunarSbrReplaceRenamer.
* @replacement : the new replacement.
*
* Sets the replacement of @replace_renamer to @replacement.
**/
void
thunar_sbr_replace_renamer_set_replacement (ThunarSbrReplaceRenamer *replace_renamer,
const gchar *replacement)
{
g_return_if_fail (THUNAR_SBR_IS_REPLACE_RENAMER (replace_renamer));
g_return_if_fail (g_utf8_validate (replacement, -1, NULL));
/* check if we have a new replacement */
if (g_strcmp0 (replace_renamer->replacement, replacement) != 0)
{
/* apply the setting */
g_free (replace_renamer->replacement);
replace_renamer->replacement = g_strdup (replacement);
/* update the renamer */
thunarx_renamer_changed (THUNARX_RENAMER (replace_renamer));
/* notify listeners */
g_object_notify (G_OBJECT (replace_renamer), "replacement");
}
}

View file

@ -0,0 +1,72 @@
/* vi:set et ai sw=2 sts=2 ts=2: */
/*-
* Copyright (c) 2006 Benedikt Meurer <benny@xfce.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#ifndef __THUNAR_SBR_REPLACE_RENAMER_H__
#define __THUNAR_SBR_REPLACE_RENAMER_H__
#include <thunarx/thunarx.h>
G_BEGIN_DECLS;
typedef struct _ThunarSbrReplaceRenamerClass ThunarSbrReplaceRenamerClass;
typedef struct _ThunarSbrReplaceRenamer ThunarSbrReplaceRenamer;
#define THUNAR_SBR_TYPE_REPLACE_RENAMER (thunar_sbr_replace_renamer_get_type ())
#define THUNAR_SBR_REPLACE_RENAMER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), THUNAR_SBR_TYPE_REPLACE_RENAMER, ThunarSbrReplaceRenamer))
#define THUNAR_SBR_REPLACE_RENAMER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), THUNAR_SBR_TYPE_REPLACE_RENAMER, ThunarSbrReplaceRenamerClass))
#define THUNAR_SBR_IS_REPLACE_RENAMER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), THUNAR_SBR_TYPE_REPLACE_RENAMER))
#define THUNAR_SBR_IS_REPLACE_RENAMER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), THUNAR_SBR_TYPE_REPLACE_RENAMER))
#define THUNAR_SBR_REPLACE_RENAMER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), THUNAR_SBR_TYPE_REPLACE_RENAMER, ThunarSbrReplaceRenamerClass))
GType
thunar_sbr_replace_renamer_get_type (void) G_GNUC_CONST;
void
thunar_sbr_replace_renamer_register_type (ThunarxProviderPlugin *plugin);
ThunarSbrReplaceRenamer *
thunar_sbr_replace_renamer_new (void) G_GNUC_MALLOC;
gboolean
thunar_sbr_replace_renamer_get_case_sensitive (ThunarSbrReplaceRenamer *replace_renamer);
void
thunar_sbr_replace_renamer_set_case_sensitive (ThunarSbrReplaceRenamer *replace_renamer,
gboolean case_sensitive);
const gchar *
thunar_sbr_replace_renamer_get_pattern (ThunarSbrReplaceRenamer *replace_renamer);
void
thunar_sbr_replace_renamer_set_pattern (ThunarSbrReplaceRenamer *replace_renamer,
const gchar *pattern);
gboolean
thunar_sbr_replace_renamer_get_regexp (ThunarSbrReplaceRenamer *replace_renamer);
void
thunar_sbr_replace_renamer_set_regexp (ThunarSbrReplaceRenamer *replace_renamer,
gboolean regexp);
const gchar *
thunar_sbr_replace_renamer_get_replacement (ThunarSbrReplaceRenamer *replace_renamer);
void
thunar_sbr_replace_renamer_set_replacement (ThunarSbrReplaceRenamer *replace_renamer,
const gchar *replacement);
G_END_DECLS;
#endif /* !__THUNAR_SBR_REPLACE_RENAMER_H__ */

View file

@ -0,0 +1,46 @@
AM_CPPFLAGS = \
-I$(top_builddir) \
-I$(top_builddir)/plugins \
-I$(top_srcdir) \
-I$(top_srcdir)/plugins \
-DG_LOG_DOMAIN=\"thunar-sendto-email\" \
-DPACKAGE_LOCALE_DIR=\"$(localedir)\" \
$(PLATFORM_CPPFLAGS)
thunar_sendto_emaildir = \
$(HELPER_PATH_PREFIX)/Thunar
thunar_sendto_email_PROGRAMS = \
thunar-sendto-email
thunar_sendto_email_SOURCES = \
main.c
thunar_sendto_email_CFLAGS = \
$(EXO_CFLAGS) \
$(LIBXFCE4UTIL_CFLAGS) \
$(PLATFORM_CFLAGS)
thunar_sendto_email_LDADD = \
$(EXO_LIBS) \
$(LIBXFCE4UTIL_LIBS) \
$(PLATFORM_LDFLAGS)
desktopdir = $(datadir)/Thunar/sendto
desktop_in_in_files = thunar-sendto-email.desktop.in.in
desktop_in_files = $(desktop_in_in_files:.desktop.in.in=.desktop.in)
%.desktop.in: %.desktop.in.in
sed -e "s,\@HELPERDIR\@,$(HELPER_PATH_PREFIX),g" < $< > $@
desktop_DATA = $(desktop_in_files:.desktop.in=.desktop)
%.desktop: %.desktop.in Makefile
$(AM_V_GEN)$(MSGFMT) --desktop --template $< -d $(top_srcdir)/po -o $@
EXTRA_DIST = \
$(desktop_in_in_files) \
README.md
DISTCLEANFILES = \
$(desktop_in_files) \
$(desktop_DATA)
# vi:set ts=8 sw=8 noet ai nocindent syntax=automake:

View file

@ -0,0 +1,6 @@
Thunar Sendto Email (thunar-sendto-email)
=========================================
Thunar-sendto-email is an extension to Thunar, which adds an additional entry to the "Send To" sub menu, named "Mail Recipient", that starts the mail composer and attaches the selected files (using the exo-open mechanism).
The extension uses the zip command to compress folders prior to sending them to the mail client, since most mail clients cannot handle directories as attachments. For regular files, larger than 200KiB, the extension prompts the user whether to compress the files (using the zip command) prior to sending them to the mail client.

View file

@ -0,0 +1,705 @@
/* 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_SYS_TYPES_H
#include <sys/types.h>
#endif
#ifdef HAVE_SYS_WAIT_H
#include <sys/wait.h>
#endif
#ifdef HAVE_ERRNO_H
#include <errno.h>
#endif
#ifdef HAVE_MEMORY_H
#include <memory.h>
#endif
#ifdef HAVE_SIGNAL_H
#include <signal.h>
#endif
#ifdef HAVE_STDARG_H
#include <stdarg.h>
#endif
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
#ifdef HAVE_STRING_H
#include <string.h>
#endif
#ifdef HAVE_TIME_H
#include <time.h>
#endif
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <exo/exo.h>
#include <gio/gio.h>
#include <glib.h>
#include <glib/gstdio.h>
#include <gtk/gtk.h>
#include <libxfce4util/libxfce4util.h>
typedef struct _TseData TseData;
struct _TseData
{
GFileInfo *info;
GFile *file;
};
/* well known archive types */
static const char *TSE_MIME_TYPES[] = {
"application/x-7z-compressed",
"application/x-7z-compressed-tar",
"application/x-ar",
"application/x-arj",
"application/x-brotli",
"application/x-brotli-compressed-tar",
"application/x-bzip",
"application/x-bzip-compressed-tar",
"application/x-compress",
"application/x-compressed-tar",
"application/x-deb",
"application/x-gtar",
"application/x-gzip",
"application/x-lha",
"application/x-lhz",
"application/x-lzma",
"application/x-lzma-compressed-tar",
"application/x-rar",
"application/x-rar-compressed",
"application/x-tar",
"application/x-xz",
"application/x-xz-compressed-tar",
"application/x-zip",
"application/x-zip-compressed",
"application/zip",
"multipart/x-zip",
"application/x-rpm",
"application/x-jar",
"application/x-java-archive",
"application/x-lzop",
"application/x-zoo",
"application/x-cd-image",
"application/zstd",
"application/x-zstd-compressed-tar",
};
/* compress response ids */
enum
{
TSE_RESPONSE_COMPRESS,
TSE_RESPONSE_PLAIN,
};
static void
tse_error (GError *error,
const gchar *format,
...)
{
GtkWidget *dialog;
va_list args;
gchar *primary_text;
/* determine the primary error text */
va_start (args, format);
primary_text = g_strdup_vprintf (format, args);
va_end (args);
/* allocate the error dialog */
dialog = gtk_message_dialog_new (NULL, 0, GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, "%s.", primary_text);
gtk_window_set_title (GTK_WINDOW (dialog), _("Error"));
gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), "%s.", error->message);
/* display the dialog */
gtk_dialog_run (GTK_DIALOG (dialog));
/* cleanup */
gtk_widget_destroy (dialog);
g_free (primary_text);
}
/**
* Check if the single file is already an archive
**/
static gboolean
tse_file_is_archive (GFileInfo *file_info)
{
const gchar *content_type;
guint n;
/* determine the content type */
content_type = g_file_info_get_attribute_string (file_info, G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE);
if (content_type == NULL)
{
return FALSE;
}
for (n = 0; n < G_N_ELEMENTS (TSE_MIME_TYPES); ++n)
{
/* check if this mime type matches */
if (g_content_type_is_a (content_type, TSE_MIME_TYPES[n]))
{
/* yep, that's a match then */
return TRUE;
}
}
return FALSE;
}
static gint
tse_ask_compress (GList *infos)
{
TseData *tse_data;
GtkWidget *message;
guint64 total_size = 0;
GList *lp;
gint response = TSE_RESPONSE_PLAIN;
gint n_archives = 0;
gint n_infos = 0;
/* check the file infos */
for (lp = infos; lp != NULL; lp = lp->next, ++n_infos)
{
/* need to compress if we have any directories */
tse_data = (TseData *) lp->data;
if (g_file_info_get_file_type (tse_data->info) == G_FILE_TYPE_DIRECTORY)
return TSE_RESPONSE_COMPRESS;
if (tse_file_is_archive (tse_data->info))
++n_archives;
/* add file size to total */
total_size += g_file_info_get_size (tse_data->info);
}
/* check if the total size is larger than 200KiB, or we have more than
* one file, and at least one of the files is not already an archive.
*/
if ((n_infos > 1 || total_size > 200 * 1024) && n_infos != n_archives)
{
/* check if we have more than one file */
if (G_LIKELY (n_infos == 1))
{
/* ask the user whether to compress the file */
tse_data = (TseData *) infos->data;
message = gtk_message_dialog_new (NULL, 0, GTK_MESSAGE_QUESTION, GTK_BUTTONS_NONE,
_("Send \"%s\" as compressed archive?"),
g_file_info_get_display_name (tse_data->info));
gtk_window_set_title (GTK_WINDOW (message), _("Send email"));
gtk_dialog_add_button (GTK_DIALOG (message), _("_Cancel"), GTK_RESPONSE_CANCEL);
gtk_dialog_add_button (GTK_DIALOG (message), _("Send _directly"), TSE_RESPONSE_PLAIN);
gtk_dialog_add_button (GTK_DIALOG (message), _("Send com_pressed"), TSE_RESPONSE_COMPRESS);
gtk_dialog_set_default_response (GTK_DIALOG (message), TSE_RESPONSE_COMPRESS);
gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (message), _("When sending a file via email, you can either choose to "
"send the file directly, as is, or compress the file before "
"attaching it to an email. It is highly recommended to "
"compress large files before sending them."));
response = gtk_dialog_run (GTK_DIALOG (message));
gtk_widget_destroy (message);
}
else
{
/* ask the user whether to send files as archive */
message = gtk_message_dialog_new (NULL, 0, GTK_MESSAGE_QUESTION, GTK_BUTTONS_NONE,
ngettext ("Send %d file as compressed archive?",
"Send %d files as compressed archive?",
n_infos),
n_infos);
gtk_window_set_title (GTK_WINDOW (message), _("Send email"));
gtk_dialog_add_button (GTK_DIALOG (message), _("_Cancel"), GTK_RESPONSE_CANCEL);
gtk_dialog_add_button (GTK_DIALOG (message), _("Send _directly"), TSE_RESPONSE_PLAIN);
gtk_dialog_add_button (GTK_DIALOG (message), _("Send as _archive"), TSE_RESPONSE_COMPRESS);
gtk_dialog_set_default_response (GTK_DIALOG (message), TSE_RESPONSE_COMPRESS);
gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (message), _("When sending multiple files via email, you can either choose "
"to send the files directly, attaching multiple files to an "
"email, or send all files compressed into a single archive "
"file and attach the archive. It is highly recommended to "
"send multiple large files as archive."));
response = gtk_dialog_run (GTK_DIALOG (message));
gtk_widget_destroy (message);
}
}
return response;
}
static void
tse_child_watch (GPid pid,
gint status,
gpointer user_data)
{
g_object_set_data (G_OBJECT (user_data), I_ ("tse-child-status"), GINT_TO_POINTER (status));
gtk_dialog_response (GTK_DIALOG (user_data), GTK_RESPONSE_YES);
}
static gboolean
tse_progress_cb (gpointer user_data)
{
GtkWidget *pulse = user_data;
gtk_progress_bar_pulse (GTK_PROGRESS_BAR (pulse));
return TRUE;
}
static gboolean
tse_progress (const gchar *working_directory,
gchar **argv,
GError **error)
{
GtkWidget *progress;
GtkWidget *dialog;
GtkWidget *image;
GtkWidget *label;
GtkWidget *hbox;
GtkWidget *vbox;
gboolean succeed = FALSE;
GPid pid;
guint pulse_timer_id;
gint watch_id;
gint response;
gint status;
/* try to run the command */
if (!g_spawn_async (working_directory, argv, NULL, G_SPAWN_DO_NOT_REAP_CHILD | G_SPAWN_SEARCH_PATH, NULL, NULL, &pid, error))
return FALSE;
/* allocate the progress dialog */
dialog = gtk_dialog_new_with_buttons (_("Compressing files..."),
NULL, 0,
_("_Cancel"), GTK_RESPONSE_CANCEL,
NULL);
gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_CANCEL);
gtk_window_set_default_size (GTK_WINDOW (dialog), 300, -1);
/* setup the hbox */
hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
gtk_container_set_border_width (GTK_CONTAINER (hbox), 8);
gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))), hbox, TRUE, TRUE, 0);
gtk_widget_show (hbox);
/* setup the image */
image = gtk_image_new_from_icon_name ("gnome-package", GTK_ICON_SIZE_DIALOG);
gtk_widget_set_halign (image, GTK_ALIGN_CENTER);
gtk_widget_set_valign (image, GTK_ALIGN_START);
gtk_box_pack_start (GTK_BOX (hbox), image, FALSE, FALSE, 0);
gtk_widget_show (image);
/* setup the vbox */
vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
gtk_box_pack_start (GTK_BOX (hbox), vbox, TRUE, TRUE, 0);
gtk_widget_show (vbox);
/* setup the label */
label = gtk_label_new (_("Compressing files..."));
gtk_label_set_xalign (GTK_LABEL (label), 0.0f);
gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
gtk_widget_show (label);
/* setup the progress bar */
progress = gtk_progress_bar_new ();
gtk_box_pack_start (GTK_BOX (vbox), progress, FALSE, FALSE, 0);
gtk_widget_show (progress);
/* start the child watch */
watch_id = g_child_watch_add (pid, tse_child_watch, dialog);
/* start the pulse timer */
pulse_timer_id = g_timeout_add (125, tse_progress_cb, progress);
/* run the dialog */
response = gtk_dialog_run (GTK_DIALOG (dialog));
if (response == GTK_RESPONSE_YES)
{
/* check if the command failed */
status = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (dialog), I_ ("tse-child-status")));
if (!WIFEXITED (status) || WEXITSTATUS (status) != 0)
{
/* tell the user that the command failed */
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED, _("ZIP command terminated with error %d"), status);
}
else
{
/* the command succeed */
succeed = TRUE;
}
}
else
{
/* terminate the ZIP command */
kill (pid, SIGQUIT);
}
/* cleanup */
g_source_remove (pulse_timer_id);
gtk_widget_destroy (dialog);
g_source_remove (watch_id);
return succeed;
}
#ifndef HAVE_MKDTEMP
static gchar *
mkdtemp (gchar *tmpl)
{
static const gchar LETTERS[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
static guint64 value;
guint64 v;
gint len;
gint i, j;
len = strlen (tmpl);
if (len < 6 || strcmp (&tmpl[len - 6], "XXXXXX") != 0)
{
errno = EINVAL;
return NULL;
}
value += ((guint64) time (NULL)) ^ getpid ();
for (i = 0; i < TMP_MAX; ++i, value += 7777)
{
/* fill in the random bits */
for (j = 0, v = value; j < 6; ++j)
tmpl[(len - 6) + j] = LETTERS[v % 62];
v /= 62;
/* try to create the directory */
if (g_mkdir (tmpl, 0700) == 0)
return tmpl;
else if (errno != EEXIST)
return NULL;
}
errno = EEXIST;
return NULL;
}
#endif
static gboolean
tse_compress (GList *infos,
gchar **zipfile_return)
{
TseData *tse_data;
gboolean succeed = TRUE;
GError *error = NULL;
GFile *parent;
GFile *parent_parent;
gchar **argv;
gchar *base_name;
gchar *zipfile;
gchar *tmppath;
gchar *tmpdir;
gchar *path;
gchar *dot;
GList *lp;
gint n;
/* create a temporary directory */
tmpdir = g_strdup ("/tmp/thunar-sendto-email.XXXXXX");
if (G_UNLIKELY (mkdtemp (tmpdir) == NULL))
{
/* tell the user that we failed to create a temporary directory */
error = g_error_new_literal (G_FILE_ERROR, g_file_error_from_errno (errno), g_strerror (errno));
tse_error (error, _("Failed to create temporary directory"));
g_error_free (error);
return FALSE;
}
/* check if we have only a single file/folder to compress */
if (G_LIKELY (infos != NULL && infos->next == NULL))
{
/* determine the name for the ZIP file */
tse_data = (TseData *) infos->data;
path = g_strdup (g_file_info_get_display_name (tse_data->info));
dot = strrchr (path, '.');
if (G_LIKELY (dot != NULL))
*dot = '\0';
zipfile = g_strconcat (tmpdir, G_DIR_SEPARATOR_S, path, ".zip", NULL);
g_free (path);
}
else
{
/* determine the name for the ZIP file */
tse_data = (TseData *) infos->data;
parent = g_file_get_parent (tse_data->file);
/* we assume the parent exists because we only allow non-root
* files as TseData in main () */
g_assert (parent != NULL);
parent_parent = g_file_get_parent (parent);
if (parent_parent != NULL)
{
/* use the parent directory's name */
base_name = g_file_get_basename (parent);
zipfile = g_strconcat (tmpdir, G_DIR_SEPARATOR_S, base_name, ".zip", NULL);
g_free (base_name);
g_object_unref (parent_parent);
}
else
{
/* use the first file's name */
zipfile = g_strconcat (tmpdir, G_DIR_SEPARATOR_S, g_file_info_get_display_name (tse_data->info), ".zip", NULL);
}
g_object_unref (parent);
}
/* generate the argument list for the ZIP command */
argv = g_new0 (gchar *, g_list_length (infos) + 5);
argv[0] = g_strdup ("zip");
argv[1] = g_strdup ("-q");
argv[2] = g_strdup ("-r");
argv[3] = g_strdup (zipfile);
for (lp = infos, n = 4; lp != NULL && succeed; lp = lp->next, ++n)
{
/* create a symlink for the file to the tmp dir */
tse_data = (TseData *) lp->data;
path = g_file_get_path (tse_data->file);
if (path == NULL)
{
error = g_error_new_literal (G_FILE_ERROR, g_file_error_from_errno (ENOTSUP), g_strerror (errno));
tse_error (error, _("Failed to create symbolic link for \"%s\""), g_file_info_get_display_name (tse_data->info));
g_error_free (error);
}
else
{
tmppath = g_build_filename (tmpdir, g_file_get_basename (tse_data->file), NULL);
succeed = (symlink (path, tmppath) == 0);
if (G_UNLIKELY (!succeed))
{
/* tell the user that we failed to create a symlink for this file */
error = g_error_new_literal (G_FILE_ERROR, g_file_error_from_errno (errno), g_strerror (errno));
tse_error (error, _("Failed to create symbolic link for \"%s\""), g_file_info_get_display_name (tse_data->info));
g_error_free (error);
}
else
{
/* add the file to the ZIP command line */
argv[n] = g_path_get_basename (tmppath);
}
g_free (tmppath);
g_free (path);
}
}
/* check if we succeed up to this point */
if (G_LIKELY (succeed))
{
/* try to run the ZIP command */
succeed = tse_progress (tmpdir, argv, &error);
if (G_UNLIKELY (!succeed))
{
/* check if we failed or the user cancelled */
if (G_LIKELY (error != NULL))
{
/* tell the user that we failed to compress the file(s) */
tse_error (error, ngettext ("Failed to compress %d file", "Failed to compress %d files", g_list_length (infos)),
g_list_length (infos));
g_error_free (error);
}
/* delete the temporary directory */
dot = g_strdup_printf ("rm -rf '%s'", tmpdir);
g_spawn_command_line_sync (dot, NULL, NULL, NULL, NULL);
g_free (dot);
}
else
{
/* return the path to the compressed file */
*zipfile_return = zipfile;
zipfile = NULL;
}
}
/* cleanup */
g_strfreev (argv);
g_free (zipfile);
g_free (tmpdir);
return succeed;
}
int
main (int argc, char **argv)
{
GFileInfo *info;
TseData *tse_data;
GFile *file;
GFile *parent;
GString *mailto;
GError *error = NULL;
GList *infos = NULL;
gchar **attachments = NULL;
gchar *zipfile = NULL;
GList *lp;
gint response;
gint n;
/* setup translation domain */
xfce_textdomain (GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR, "UTF-8");
#ifdef G_ENABLE_DEBUG
/* Do NOT remove this line for now, If something doesn't work,
* fix your code instead!
*/
g_log_set_always_fatal (G_LOG_LEVEL_CRITICAL | G_LOG_LEVEL_WARNING);
#endif
/* initialize Gtk+ */
gtk_init (&argc, &argv);
/* set default icon for dialogs */
gtk_window_set_default_icon_name ("internet-mail");
/* verify that atleast one file was supplied */
if (G_UNLIKELY (argc == 1))
{
g_fprintf (stderr, "Usage: thunar-sendto-email [FILES...]\n");
return EXIT_FAILURE;
}
/* determine the GFiles and GFileInfos (bundled as TseData) for the files */
for (n = 1; n < argc; ++n)
{
/* try to transform to a GFile */
file = g_file_new_for_commandline_arg (argv[n]);
/* verify that we're not trying to send root */
parent = g_file_get_parent (file);
if (parent == NULL)
{
error = g_error_new_literal (G_FILE_ERROR, g_file_error_from_errno (EFBIG), g_strerror (EFBIG));
g_fprintf (stderr, "thunar-sendto-email: Invalid argument \"%s\": %s\n", argv[n], error->message);
g_error_free (error);
g_object_unref (file);
return EXIT_FAILURE;
}
else
{
g_object_unref (parent);
}
/* try to determine the info */
info = g_file_query_info (file,
G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME "," G_FILE_ATTRIBUTE_STANDARD_SIZE "," G_FILE_ATTRIBUTE_STANDARD_TYPE "," G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE,
G_FILE_QUERY_INFO_NONE, NULL, &error);
/* check if we failed */
if (G_UNLIKELY (info == NULL))
{
g_fprintf (stderr, "thunar-sendto-email: Invalid argument \"%s\": %s\n", argv[n], error->message);
g_error_free (error);
g_object_unref (file);
return EXIT_FAILURE;
}
tse_data = g_slice_new0 (TseData);
tse_data->info = info;
tse_data->file = file;
/* add to our list of infos */
infos = g_list_append (infos, tse_data);
}
/* check whether to compress the files */
response = tse_ask_compress (infos);
if (response == TSE_RESPONSE_COMPRESS)
{
/* try to compress the files */
if (tse_compress (infos, &zipfile))
{
/* we have only a single attachment now */
attachments = g_new0 (gchar *, 2);
attachments[0] = g_filename_to_uri (zipfile, NULL, NULL);
g_free (zipfile);
}
}
else if (response == TSE_RESPONSE_PLAIN)
{
/* attach the files one by one */
attachments = g_new0 (gchar *, g_list_length (infos) + 1);
for (lp = infos, n = 0; lp != NULL; lp = lp->next, ++n)
{
tse_data = (TseData *) lp->data;
attachments[n] = g_file_get_uri (tse_data->file);
}
}
/* check if we have anything to attach */
if (G_LIKELY (attachments != NULL))
{
/* no specific email address */
mailto = g_string_new ("?");
for (n = 0; attachments[n] != NULL; ++n)
g_string_append_printf (mailto, "attach=%s&", attachments[n]);
g_strfreev (attachments);
/* open the mail composer */
if (!exo_execute_preferred_application ("MailReader", mailto->str, NULL, NULL, &error))
{
/* tell the user that we failed */
tse_error (error, _("Failed to compose new email"));
g_error_free (error);
}
/* cleanup */
g_string_free (mailto, TRUE);
}
/* cleanup */
for (lp = infos; lp != NULL; lp = lp->next)
{
tse_data = (TseData *) lp->data;
g_object_unref (tse_data->file);
g_object_unref (tse_data->info);
g_slice_free (TseData, tse_data);
}
g_list_free (infos);
return EXIT_SUCCESS;
}

View file

@ -0,0 +1,6 @@
[Desktop Entry]
Type=Application
Version=1.0
Name=Mail Recipient
Icon=mail-send
Exec=@HELPERDIR@/Thunar/thunar-sendto-email %F

View file

@ -0,0 +1,63 @@
AM_CPPFLAGS = \
-I$(top_builddir) \
-I$(top_builddir)/plugins \
-I$(top_srcdir) \
-I$(top_srcdir)/plugins \
-DG_LOG_DOMAIN=\"libthunar-tpa\" \
$(PLATFORM_CPPFLAGS)
plugindir = $(libdir)/xfce4/panel/plugins
plugin_LTLIBRARIES = \
libthunar-tpa.la
libthunar_tpa_la_SOURCES = \
thunar-tpa.c \
thunar-tpa-bindings.c \
thunar-tpa-bindings.h
libthunar_tpa_la_CFLAGS = \
$(LIBXFCE4UI_CFLAGS) \
$(LIBXFCE4UTIL_CFLAGS) \
$(LIBXFCE4PANEL_CFLAGS) \
$(PLATFORM_CFLAGS)
libthunar_tpa_la_LIBADD = \
$(LIBXFCE4UTIL_LIBS) \
$(LIBXFCE4UI_LIBS) \
$(LIBXFCE4PANEL_LIBS)
libthunar_tpa_la_LDFLAGS = \
-avoid-version \
-module \
-no-undefined \
-export-symbols-regex '^xfce_panel_module_(preinit|init|construct)' \
$(PLATFORM_LDFLAGS)
desktopdir = $(datadir)/xfce4/panel/plugins
desktop_in_files = thunar-tpa.desktop.in
desktop_DATA = $(desktop_in_files:.desktop.in=.desktop)
%.desktop: %.desktop.in Makefile
$(AM_V_GEN)$(MSGFMT) --desktop --template $< -d $(top_srcdir)/po -o $@
if MAINTAINER_MODE
BUILT_SOURCES = \
thunar-tpa-bindings.c \
thunar-tpa-bindings.h
DISTCLEANFILES = \
thunar-tpa-bindings.c \
thunar-tpa-bindings.h
thunar-tpa-bindings.c thunar-tpa-bindings.h: $(srcdir)/thunar-tpa-bindings.xml Makefile
$(AM_V_GEN) gdbus-codegen --generate-c-code thunar-tpa-bindings --c-namespace thunarTPA --interface-prefix org.xfce. $(srcdir)/thunar-tpa-bindings.xml
endif
EXTRA_DIST = \
$(desktop_in_files) \
thunar-tpa-bindings.xml \
README.md
CLEANFILES = \
$(desktop_DATA)
# vi:set ts=8 sw=8 noet ai nocindent syntax=automake:

View file

@ -0,0 +1,20 @@
Thunar Trash Panel Applet (thunar-tpa)
======================================
Thunar-tpa is an extension for the Xfce Panel, which enables users to add a trash can to their panel, that
* displays the current state of the trash can
* can move files to the trash by dropping them on the trash icon
* can empty the trash can
* can open the trash can
In order to build and install this plugin, you will need to have the xfce4-panel development headers and libraries installed (the appropriate package is usually called xfce4-panel-dev or xfce4-panel-devel). In addition, you'll need to have D-BUS 0.34 or above installed and Thunar must be built with D-BUS support.
How does it work?
=================
To avoid running several desktop processes that all monitor and manage the trash can by itself, and thereby create an unnecessary maintaince and resource overhead, the trash applet simply connects to Thunar via D-BUS to query the state of the trash and send commands to the Trash.
The trash applet is not limited to Thunar, but can work with any file manager that implements the org.xfce.Trash interface and owns the org.xfce.FileManager service. See the thunar-tpa bindings.xml file for a details about the inter- face.

View file

@ -0,0 +1,104 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright (c) 2006 Benedikt Meurer <benny@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
-->
<node name="/org/xfce/FileManager">
<!--
org.xfce.Trash
The generic Xfce Trash Bin interface, implemented by all Xfce File
Managers that support a trash bin.
-->
<interface name="org.xfce.Trash">
<!--
DisplayTrash (display : STRING) : VOID
display : the screen on which to display the trash folder
contents or "" to use the default screen of the
file manager.
startup_id : the DESKTOP_STARTUP_ID environment variable for properly
handling startup notification and focus stealing.
Opens a new file manager window showing the trash folder
contents.
-->
<method name="DisplayTrash">
<arg direction="in" name="display" type="s" />
<arg direction="in" name="startup_id" type="s" />
</method>
<!--
EmptyTrash (display : STRING) : VOID
display : the screen on which to display the confirmation of
the operation and the progress status or "" to use
the default screen of the file manager.
startup_id : the DESKTOP_STARTUP_ID environment variable for properly
handling startup notification and focus stealing.
Tells the file manager to empty the trash bin.
-->
<method name="EmptyTrash">
<arg direction="in" name="display" type="s" />
<arg direction="in" name="startup_id" type="s" />
</method>
<!--
MoveToTrash (uris : ARRAY OF STRING, display : STRING) : VOID
filenames : an array of file:-URIs or absolute paths for the files
that should be moved to the trash can.
display : the screen on which to display the progress dialog
and possible error messages and confirmation dialogs
or "" to use the default screen of the file manager.
startup_id : the DESKTOP_STARTUP_ID environment variable for properly
handling startup notification and focus stealing.
Tells the file manager to move all files specified by the filenames
to the trash bin.
-->
<method name="MoveToTrash">
<arg direction="in" name="filenames" type="as" />
<arg direction="in" name="display" type="s" />
<arg direction="in" name="startup_id" type="s" />
</method>
<!--
QueryTrash () : BOOLEAN
Determines whether the trash bin contains atleast one
item.
Returns: TRUE if the trash bin is not empty, FALSE otherwise.
-->
<method name="QueryTrash">
<arg direction="out" name="full" type="b" />
</method>
<!--
TrashChanged ()
This signal is emitted by the file manager whenever the state
of the trash bin changes.
-->
<signal name="TrashChanged"/>
</interface>
</node>
<!-- vi:set ts=2 sw=2 et ai: -->

View file

@ -0,0 +1,610 @@
/*-
* Copyright (c) 2006 Benedikt Meurer <benny@xfce.org>
* Copyright (c) 2010 Nick Schermer <nick@xfce.org>
* Copyright (c) 2010 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
#include <glib.h>
#include <gtk/gtk.h>
#include <libxfce4panel/libxfce4panel.h>
#include <libxfce4panel/xfce-panel-macros.h>
#include <libxfce4ui/libxfce4ui.h>
#include <libxfce4util/libxfce4util.h>
#include <thunar-tpa/thunar-tpa-bindings.h>
typedef struct _ThunarTpaClass ThunarTpaClass;
typedef struct _ThunarTpa ThunarTpa;
#define THUNAR_TYPE_TPA (thunar_tpa_get_type ())
#define THUNAR_TPA(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), THUNAR_TYPE_TPA, ThunarTpa))
#define THUNAR_TPA_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), THUNAR_TYPE_TPA, ThunarTpaClass))
#define THUNAR_IS_TPA(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), THUNAR_TYPE_TPA))
#define THUNAR_IS_TPA_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), THUNAR_TYPE_TPA))
#define THUNAR_TPA_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), THUNAR_TYPE_TPA, ThunarTpaClass))
GType
thunar_tpa_get_type (void);
void
thunar_tpa_register_type (XfcePanelTypeModule *type_module);
static void
thunar_tpa_finalize (GObject *object);
static void
thunar_tpa_construct (XfcePanelPlugin *panel_plugin);
static gboolean
thunar_tpa_size_changed (XfcePanelPlugin *panel_plugin,
gint size);
static void
thunar_tpa_error (ThunarTpa *plugin,
GError *error);
static void
thunar_tpa_state (ThunarTpa *plugin,
gboolean full);
static void
thunar_tpa_display_trash_reply (GObject *source_object,
GAsyncResult *result,
gpointer user_data);
static void
thunar_tpa_empty_trash_reply (GObject *source_object,
GAsyncResult *result,
gpointer user_data);
static void
thunar_tpa_move_to_trash_reply (GObject *source_object,
GAsyncResult *result,
gpointer user_data);
static void
thunar_tpa_query_trash_reply (GObject *source_object,
GAsyncResult *result,
gpointer user_data);
static void
thunar_tpa_drag_data_received (GtkWidget *button,
GdkDragContext *context,
gint x,
gint y,
GtkSelectionData *selection_data,
guint info,
guint time,
ThunarTpa *plugin);
static gboolean
thunar_tpa_enter_notify_event (GtkWidget *button,
GdkEventCrossing *event,
ThunarTpa *plugin);
static gboolean
thunar_tpa_leave_notify_event (GtkWidget *button,
GdkEventCrossing *event,
ThunarTpa *plugin);
static void
thunar_tpa_on_trash_changed (thunarTPATrash *proxy,
gpointer user_data);
static void
thunar_tpa_display_trash (ThunarTpa *plugin);
static void
thunar_tpa_empty_trash (ThunarTpa *plugin);
static gboolean
thunar_tpa_move_to_trash (ThunarTpa *plugin,
const gchar **uri_list);
static void
thunar_tpa_query_trash (ThunarTpa *plugin);
struct _ThunarTpaClass
{
XfcePanelPluginClass __parent__;
};
struct _ThunarTpa
{
XfcePanelPlugin __parent__;
/* widgets */
GtkWidget *button;
GtkWidget *image;
GtkWidget *mi;
thunarTPATrash *proxy;
GCancellable *cancellable_display_trash;
GCancellable *cancellable_empty_trash;
GCancellable *cancellable_move_to_trash;
GCancellable *cancellable_query_trash;
};
/* Target types for dropping to the trash can */
enum
{
TARGET_TEXT_URI_LIST,
};
static const GtkTargetEntry drop_targets[] = {
{
"text/uri-list",
0,
TARGET_TEXT_URI_LIST,
},
};
/* define the plugin */
XFCE_PANEL_DEFINE_PLUGIN (ThunarTpa, thunar_tpa)
/* Hint: For debugging the plugin run the panel with "PANEL_DEBUG=1 xfce4-panel" */
static void
thunar_tpa_class_init (ThunarTpaClass *klass)
{
XfcePanelPluginClass *plugin_class;
GObjectClass *gobject_class;
gobject_class = G_OBJECT_CLASS (klass);
gobject_class->finalize = thunar_tpa_finalize;
plugin_class = XFCE_PANEL_PLUGIN_CLASS (klass);
plugin_class->construct = thunar_tpa_construct;
plugin_class->size_changed = thunar_tpa_size_changed;
}
static void
thunar_tpa_init (ThunarTpa *plugin)
{
GError *error = NULL;
/* setup the button for the trash plugin */
plugin->button = xfce_panel_create_button ();
xfce_panel_plugin_add_action_widget (XFCE_PANEL_PLUGIN (plugin), plugin->button);
gtk_drag_dest_set (plugin->button, GTK_DEST_DEFAULT_ALL, drop_targets, G_N_ELEMENTS (drop_targets), GDK_ACTION_MOVE);
g_signal_connect_swapped (G_OBJECT (plugin->button), "clicked", G_CALLBACK (thunar_tpa_display_trash), plugin);
g_signal_connect (G_OBJECT (plugin->button), "drag-data-received", G_CALLBACK (thunar_tpa_drag_data_received), plugin);
g_signal_connect (G_OBJECT (plugin->button), "enter-notify-event", G_CALLBACK (thunar_tpa_enter_notify_event), plugin);
g_signal_connect (G_OBJECT (plugin->button), "leave-notify-event", G_CALLBACK (thunar_tpa_leave_notify_event), plugin);
gtk_container_add (GTK_CONTAINER (plugin), plugin->button);
gtk_widget_show (plugin->button);
/* setup the image for the trash plugin */
plugin->image = gtk_image_new_from_icon_name ("user-trash", GTK_ICON_SIZE_BUTTON);
gtk_container_add (GTK_CONTAINER (plugin->button), plugin->image);
gtk_widget_show (plugin->image);
/* prepare the menu item */
plugin->mi = gtk_menu_item_new_with_mnemonic (_("_Empty Trash"));
g_signal_connect_swapped (G_OBJECT (plugin->mi), "activate", G_CALLBACK (thunar_tpa_empty_trash), plugin);
gtk_widget_show (plugin->mi);
plugin->cancellable_display_trash = g_cancellable_new ();
plugin->cancellable_empty_trash = g_cancellable_new ();
plugin->cancellable_move_to_trash = g_cancellable_new ();
plugin->cancellable_query_trash = g_cancellable_new ();
plugin->proxy = thunar_tpa_trash_proxy_new_for_bus_sync (G_BUS_TYPE_SESSION, G_DBUS_PROXY_FLAGS_NONE, "org.xfce.FileManager", "/org/xfce/FileManager", NULL, &error);
if (error != NULL)
thunar_tpa_error (plugin, error);
g_signal_connect (plugin->proxy, "trash_changed", G_CALLBACK (thunar_tpa_on_trash_changed), plugin);
}
static void
thunar_tpa_finalize (GObject *object)
{
ThunarTpa *plugin = THUNAR_TPA (object);
/* cancel any pending calls */
if (G_LIKELY (plugin->cancellable_display_trash != NULL))
{
g_cancellable_cancel (plugin->cancellable_display_trash);
g_object_unref (plugin->cancellable_display_trash);
}
if (G_LIKELY (plugin->cancellable_empty_trash != NULL))
{
g_cancellable_cancel (plugin->cancellable_empty_trash);
g_object_unref (plugin->cancellable_empty_trash);
}
if (G_LIKELY (plugin->cancellable_move_to_trash != NULL))
{
g_cancellable_cancel (plugin->cancellable_move_to_trash);
g_object_unref (plugin->cancellable_move_to_trash);
}
if (G_LIKELY (plugin->cancellable_query_trash != NULL))
{
g_cancellable_cancel (plugin->cancellable_query_trash);
g_object_unref (plugin->cancellable_query_trash);
}
/* release the proxy object */
if (G_LIKELY (plugin->proxy != NULL))
g_object_unref (G_OBJECT (plugin->proxy));
(*G_OBJECT_CLASS (thunar_tpa_parent_class)->finalize) (object);
}
static void
thunar_tpa_construct (XfcePanelPlugin *panel_plugin)
{
ThunarTpa *plugin = THUNAR_TPA (panel_plugin);
/* make the plugin fit a single row */
xfce_panel_plugin_set_small (panel_plugin, TRUE);
/* add the "Empty Trash" menu item */
xfce_panel_plugin_menu_insert_item (panel_plugin, GTK_MENU_ITEM (plugin->mi));
/* update the state of the trash plugin */
thunar_tpa_query_trash (plugin);
}
static gboolean
thunar_tpa_size_changed (XfcePanelPlugin *panel_plugin,
gint size)
{
ThunarTpa *plugin = THUNAR_TPA (panel_plugin);
gint image_size;
/* make the plugin fit a single row */
size /= xfce_panel_plugin_get_nrows (panel_plugin);
gtk_widget_set_size_request (GTK_WIDGET (panel_plugin), size, size);
#if LIBXFCE4PANEL_CHECK_VERSION(4, 13, 0)
image_size = xfce_panel_plugin_get_icon_size (panel_plugin);
#else
image_size = size - 2; // fall-back for older panel versions
#endif
gtk_image_set_pixel_size (GTK_IMAGE (plugin->image), image_size);
return TRUE;
}
static void
thunar_tpa_error (ThunarTpa *plugin,
GError *error)
{
gchar *tooltip;
/* reset to empty first */
thunar_tpa_state (plugin, FALSE);
/* strip off additional whitespace */
g_strstrip (error->message);
/* tell the user that we failed to connect to the trash */
tooltip = g_strdup_printf ("%s: %s.", _("Failed to connect to the Trash"), error->message);
gtk_widget_set_tooltip_text (plugin->button, tooltip);
g_free (tooltip);
/* setup an error plugin */
gtk_image_set_from_icon_name (GTK_IMAGE (plugin->image), "stock_dialog-error", GTK_ICON_SIZE_BUTTON);
}
static void
thunar_tpa_state (ThunarTpa *plugin,
gboolean full)
{
/* tell the user whether the trash is full or empty */
gtk_widget_set_tooltip_text (plugin->button, full ? _("Trash contains files") : _("Trash is empty"));
/* setup the appropriate plugin */
gtk_image_set_from_icon_name (GTK_IMAGE (plugin->image), full ? "user-trash-full" : "user-trash", GTK_ICON_SIZE_BUTTON);
/* sensitivity of the menu item */
gtk_widget_set_sensitive (plugin->mi, full);
}
static void
thunar_tpa_display_trash_reply (GObject *source_object,
GAsyncResult *result,
gpointer user_data)
{
thunarTPATrash *proxy = THUNAR_TPA_TRASH (source_object);
gboolean success = FALSE;
GError *error = NULL;
success = thunar_tpa_trash_call_display_trash_finish (proxy, result, &error);
if (G_UNLIKELY (success != TRUE))
{
/* display an error message to the user */
g_strstrip (error->message);
xfce_dialog_show_error (NULL, error, "%s.", _("Failed to connect to the Trash"));
g_error_free (error);
}
}
static void
thunar_tpa_empty_trash_reply (GObject *source_object,
GAsyncResult *result,
gpointer user_data)
{
thunarTPATrash *proxy = THUNAR_TPA_TRASH (source_object);
ThunarTpa *plugin = THUNAR_TPA (user_data);
gboolean success = FALSE;
GError *error = NULL;
success = thunar_tpa_trash_call_empty_trash_finish (proxy, result, &error);
if (G_LIKELY (success))
{
/* query the new state of the trash */
thunar_tpa_query_trash (plugin);
}
else
{
/* display an error message to the user */
g_strstrip (error->message);
xfce_dialog_show_error (NULL, error, "%s.", _("Failed to connect to the Trash"));
g_error_free (error);
}
}
static void
thunar_tpa_move_to_trash_reply (GObject *source_object,
GAsyncResult *result,
gpointer user_data)
{
thunarTPATrash *proxy = THUNAR_TPA_TRASH (source_object);
ThunarTpa *plugin = THUNAR_TPA (user_data);
gboolean success = FALSE;
GError *error = NULL;
success = thunar_tpa_trash_call_move_to_trash_finish (proxy, result, &error);
if (G_LIKELY (success))
{
/* query the new state of the trash */
thunar_tpa_query_trash (plugin);
}
else
{
/* display an error message to the user */
g_strstrip (error->message);
xfce_dialog_show_error (NULL, error, "%s.", _("Failed to connect to the Trash"));
g_error_free (error);
}
}
static void
thunar_tpa_query_trash_reply (GObject *source_object,
GAsyncResult *result,
gpointer user_data)
{
thunarTPATrash *proxy = THUNAR_TPA_TRASH (source_object);
ThunarTpa *plugin = THUNAR_TPA (user_data);
gboolean success = FALSE;
GError *error = NULL;
gboolean full;
success = thunar_tpa_trash_call_query_trash_finish (proxy, &full, result, &error);
if (G_LIKELY (success))
{
/* update the tooltip/plugin accordingly */
thunar_tpa_state (plugin, full);
}
else
{
/* setup an error tooltip/plugin */
thunar_tpa_error (plugin, error);
g_error_free (error);
}
}
static void
thunar_tpa_drag_data_received (GtkWidget *button,
GdkDragContext *context,
gint x,
gint y,
GtkSelectionData *selection_data,
guint info,
guint timestamp,
ThunarTpa *plugin)
{
gboolean succeed = FALSE;
gchar **uri_list;
g_return_if_fail (THUNAR_IS_TPA (plugin));
g_return_if_fail (plugin->button == button);
/* determine the type of drop we received */
if (G_LIKELY (info == TARGET_TEXT_URI_LIST))
{
/* check if the data is valid for text/uri-list */
uri_list = gtk_selection_data_get_uris (selection_data);
if (G_LIKELY (uri_list != NULL))
{
succeed = thunar_tpa_move_to_trash (plugin, (const gchar **) uri_list);
g_strfreev (uri_list);
}
}
/* finish the drag */
gtk_drag_finish (context, succeed, TRUE, timestamp);
}
static gboolean
thunar_tpa_enter_notify_event (GtkWidget *button,
GdkEventCrossing *event,
ThunarTpa *plugin)
{
g_return_val_if_fail (THUNAR_IS_TPA (plugin), FALSE);
g_return_val_if_fail (plugin->button == button, FALSE);
/* query the new state of the trash */
thunar_tpa_query_trash (plugin);
return FALSE;
}
static gboolean
thunar_tpa_leave_notify_event (GtkWidget *button,
GdkEventCrossing *event,
ThunarTpa *plugin)
{
g_return_val_if_fail (THUNAR_IS_TPA (plugin), FALSE);
g_return_val_if_fail (plugin->button == button, FALSE);
/* query the new state of the trash */
thunar_tpa_query_trash (plugin);
return FALSE;
}
static void
thunar_tpa_on_trash_changed (thunarTPATrash *proxy,
gpointer user_data)
{
ThunarTpa *plugin = THUNAR_TPA (user_data);
g_return_if_fail (THUNAR_IS_TPA (plugin));
g_return_if_fail (plugin->proxy == proxy);
/* update the state of the trash plugin */
thunar_tpa_query_trash (plugin);
}
static void
thunar_tpa_display_trash (ThunarTpa *plugin)
{
gchar *display_name;
gchar *startup_id;
GdkScreen *screen;
g_return_if_fail (THUNAR_IS_TPA (plugin));
/* check if we are connected to the bus */
if (G_LIKELY (plugin->proxy != NULL))
{
/* cancel any pending call and reset the cancellable */
g_cancellable_cancel (plugin->cancellable_display_trash);
g_cancellable_reset (plugin->cancellable_display_trash);
/* schedule a new call */
screen = gtk_widget_get_screen (GTK_WIDGET (plugin));
display_name = g_strdup (gdk_display_get_name (gdk_screen_get_display (screen)));
startup_id = g_strdup_printf ("_TIME%d", gtk_get_current_event_time ());
thunar_tpa_trash_call_display_trash (plugin->proxy, display_name, startup_id, plugin->cancellable_display_trash, thunar_tpa_display_trash_reply, plugin);
g_free (startup_id);
g_free (display_name);
}
}
static void
thunar_tpa_empty_trash (ThunarTpa *plugin)
{
gchar *display_name;
gchar *startup_id;
GdkScreen *screen;
g_return_if_fail (THUNAR_IS_TPA (plugin));
/* check if we are connected to the bus */
if (G_LIKELY (plugin->proxy != NULL))
{
/* cancel any pending call and reset the cancellable */
g_cancellable_cancel (plugin->cancellable_empty_trash);
g_cancellable_reset (plugin->cancellable_empty_trash);
/* schedule a new call */
screen = gtk_widget_get_screen (GTK_WIDGET (plugin));
display_name = g_strdup (gdk_display_get_name (gdk_screen_get_display (screen)));
startup_id = g_strdup_printf ("_TIME%d", gtk_get_current_event_time ());
thunar_tpa_trash_call_empty_trash (plugin->proxy, display_name, startup_id, plugin->cancellable_empty_trash, thunar_tpa_empty_trash_reply, plugin);
g_free (startup_id);
g_free (display_name);
}
}
static gboolean
thunar_tpa_move_to_trash (ThunarTpa *plugin,
const gchar **uri_list)
{
gchar *display_name;
gchar *startup_id;
GdkScreen *screen;
g_return_val_if_fail (THUNAR_IS_TPA (plugin), FALSE);
g_return_val_if_fail (uri_list != NULL, FALSE);
/* check if we are connected to the bus */
if (G_UNLIKELY (plugin->proxy == NULL))
return FALSE;
/* cancel any pending call and reset the cancellable */
g_cancellable_cancel (plugin->cancellable_move_to_trash);
g_cancellable_reset (plugin->cancellable_move_to_trash);
/* schedule a new call */
screen = gtk_widget_get_screen (GTK_WIDGET (plugin));
display_name = g_strdup (gdk_display_get_name (gdk_screen_get_display (screen)));
startup_id = g_strdup_printf ("_TIME%d", gtk_get_current_event_time ());
thunar_tpa_trash_call_move_to_trash (plugin->proxy, uri_list, display_name, startup_id, plugin->cancellable_move_to_trash, thunar_tpa_move_to_trash_reply, plugin);
g_free (startup_id);
g_free (display_name);
return TRUE;
}
static void
thunar_tpa_query_trash (ThunarTpa *plugin)
{
g_return_if_fail (THUNAR_IS_TPA (plugin));
/* check if we are connected to the bus */
if (G_LIKELY (plugin->proxy != NULL))
{
/* cancel any pending call and reset the cancellable */
g_cancellable_cancel (plugin->cancellable_query_trash);
g_cancellable_reset (plugin->cancellable_query_trash);
/* schedule a new call */
thunar_tpa_trash_call_query_trash (plugin->proxy, plugin->cancellable_query_trash, thunar_tpa_query_trash_reply, plugin);
}
}

View file

@ -0,0 +1,9 @@
[Xfce Panel]
Type=X-XFCE-PanelPlugin
Name=Trash Applet
Comment=Display the trash can
Icon=user-trash
X-XFCE-Module=thunar-tpa
X-XFCE-Internal=FALSE
X-XFCE-API=2.0
X-XFCE-Unique=true

View file

@ -0,0 +1,84 @@
AM_CPPFLAGS = \
-I$(top_builddir) \
-I$(top_builddir)/plugins \
-I$(top_srcdir) \
-I$(top_srcdir)/plugins \
-DBINDIR=\"$(bindir)\" \
-DDATADIR=\"$(datadir)\" \
-DG_LOG_DOMAIN=\"thunar-uca\" \
-DPACKAGE_LOCALE_DIR=\"$(localedir)\" \
$(PLATFORM_CPPFLAGS)
extensionsdir = $(libdir)/thunarx-$(THUNARX_VERSION_API)
extensions_LTLIBRARIES = \
thunar-uca.la
thunar_uca_la_SOURCES = \
thunar-uca-chooser.c \
thunar-uca-chooser.h \
thunar-uca-context.c \
thunar-uca-context.h \
thunar-uca-editor.c \
thunar-uca-editor.h \
thunar-uca-model.c \
thunar-uca-model.h \
thunar-uca-plugin.c \
thunar-uca-private.c \
thunar-uca-private.h \
thunar-uca-provider.c \
thunar-uca-provider.h \
thunar-uca.gresource.c
thunar_uca_la_CFLAGS = \
$(EXO_CFLAGS) \
$(LIBXFCE4UTIL_CFLAGS) \
$(LIBXFCE4UI_CFLAGS) \
$(LIBXFCE4KBD_PRIVATE_CFLAGS) \
$(PLATFORM_CFLAGS)
thunar_uca_la_LDFLAGS = \
-avoid-version \
-export-dynamic \
-no-undefined \
-export-symbols-regex "^thunar_extension_(shutdown|initialize|list_types)" \
-module \
$(PLATFORM_LDFLAGS)
thunar_uca_la_LIBADD = \
$(top_builddir)/thunarx/libthunarx-$(THUNARX_VERSION_API).la \
$(EXO_LIBS) \
$(LIBXFCE4UTIL_LIBS) \
$(LIBXFCE4UI_LIBS) \
$(LIBXFCE4KBD_PRIVATE_LIBS)
thunar_uca_la_DEPENDENCIES = \
$(top_builddir)/thunarx/libthunarx-$(THUNARX_VERSION_API).la
thunar_uca_templates = \
thunar-uca-ui.xml
GLIB_COMPILE_RESOURCES = $(shell $(PKG_CONFIG) --variable=glib_compile_resources gio-2.0)
resource_files = $(shell $(GLIB_COMPILE_RESOURCES) --sourcedir=$(srcdir) --generate-dependencies $(srcdir)/thunar-uca.gresource.xml)
thunar-uca.gresource.c: thunar-uca.gresource.xml $(resource_files)
$(AM_V_GEN) $(GLIB_COMPILE_RESOURCES) $< \
--target=$@ --sourcedir=$(srcdir) --generate-source
defaultsdir = $(sysconfdir)/xdg/Thunar
defaults_in_files = uca.xml.in
defaults_DATA = $(defaults_in_files:.xml.in=.xml)
$(defaults_DATA): $(defaults_in_files) Makefile
$(AM_V_GEN)GETTEXTDATADIRS=$(top_srcdir) $(MSGFMT) --xml --template $< -d $(top_srcdir)/po -o $@
EXTRA_DIST = \
README.md \
thunar-uca.gresource.xml \
thunar-uca-editor.ui \
thunar-uca-chooser.ui \
$(defaults_in_files)
CLEANFILES = \
$(defaults_DATA)
# vi:set ts=8 sw=8 noet ai nocindent syntax=automake:

View file

@ -0,0 +1,23 @@
Thunar User Customizable Actions (thunar-uca)
=============================================
Thunar-uca is an extension to Thunar, which enables sophisticated users to add additional actions to the file managers context menus, without having to write their own extensions for each and every action they want to use.
By default the thunar-uca extension will be installed, but as with every extension, it will slightly increase the resource usage of Thunar (this shouldn't be a real problem unless you're targeting an embedded system), and so can you pass `--disable-uca-plugin` to configure and the plugin won't be built and installed. Since it is an extension, you can also easily uninstall it
afterwards by removing the thunar-uca.so file from the lib/thunarx-2/ directory of your installation (be sure to quit Thunar before removing files though).
To actually manage the actions open the "Edit" menu in the menu bar of a Thunar window and select "Configure Custom Actions...". A dialog will appear which lists the currently configured actions.
List of valid command parameter variables
=========================================
| Variable | Usage |
| :------------ | :------------ |
| %f | The path of the first selected file. |
| %F | The paths to all selected files. |
|%u |The URI of the first selected file (using the file:-URI scheme). |
|%U |The URIs of all selected files (using the file:-URI scheme). |
| %d |The directory of the first selected file. |
| %D |The directories to all selected files. |
| %n |The name of the first selected file (without the path). |
|%N |The names of all selected files (without the paths). |

View file

@ -0,0 +1,454 @@
/* vi:set et ai sw=2 sts=2 ts=2: */
/*-
* Copyright (c) 2005-2006 Benedikt Meurer <benny@xfce.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; 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
#include <gdk/gdkkeysyms.h>
#include <glib/gi18n-lib.h>
#include <thunar-uca/thunar-uca-chooser.h>
#include <thunar-uca/thunar-uca-editor.h>
#include <thunar-uca/thunar-uca-model.h>
static gboolean
thunar_uca_chooser_key_press_event (GtkWidget *widget,
GdkEventKey *event);
static void
thunar_uca_chooser_response (GtkDialog *dialog,
gint response);
static void
thunar_uca_chooser_exchange (ThunarUcaChooser *uca_chooser,
GtkTreeSelection *selection,
GtkTreeModel *model,
GtkTreeIter *iter_a,
GtkTreeIter *iter_b);
static void
thunar_uca_chooser_open_editor (ThunarUcaChooser *uca_chooser,
gboolean edit);
static void
thunar_uca_chooser_save (ThunarUcaChooser *uca_chooser,
ThunarUcaModel *uca_model);
static void
thunar_uca_chooser_selection_changed (ThunarUcaChooser *uca_chooser,
GtkTreeSelection *selection);
static void
thunar_uca_chooser_add_clicked (ThunarUcaChooser *uca_chooser);
static void
thunar_uca_chooser_edit_clicked (ThunarUcaChooser *uca_chooser);
static void
thunar_uca_chooser_delete_clicked (ThunarUcaChooser *uca_chooser);
static void
thunar_uca_chooser_up_clicked (ThunarUcaChooser *uca_chooser);
static void
thunar_uca_chooser_down_clicked (ThunarUcaChooser *uca_chooser);
struct _ThunarUcaChooserClass
{
GtkDialogClass __parent__;
};
struct _ThunarUcaChooser
{
GtkDialog __parent__;
GtkWidget *treeview;
GtkWidget *add_button;
GtkWidget *edit_button;
GtkWidget *delete_button;
GtkWidget *up_button;
GtkWidget *down_button;
};
THUNARX_DEFINE_TYPE (ThunarUcaChooser, thunar_uca_chooser, GTK_TYPE_DIALOG);
static void
thunar_uca_chooser_class_init (ThunarUcaChooserClass *klass)
{
GtkDialogClass *dialog_class;
GtkWidgetClass *widget_class;
widget_class = GTK_WIDGET_CLASS (klass);
widget_class->key_press_event = thunar_uca_chooser_key_press_event;
dialog_class = GTK_DIALOG_CLASS (klass);
dialog_class->response = thunar_uca_chooser_response;
/* Setup the template xml */
gtk_widget_class_set_template_from_resource (widget_class, "/org/xfce/thunar/uca/chooser.ui");
/* bind stuff */
gtk_widget_class_bind_template_child (widget_class, ThunarUcaChooser, treeview);
gtk_widget_class_bind_template_child (widget_class, ThunarUcaChooser, add_button);
gtk_widget_class_bind_template_child (widget_class, ThunarUcaChooser, edit_button);
gtk_widget_class_bind_template_child (widget_class, ThunarUcaChooser, delete_button);
gtk_widget_class_bind_template_child (widget_class, ThunarUcaChooser, up_button);
gtk_widget_class_bind_template_child (widget_class, ThunarUcaChooser, down_button);
gtk_widget_class_bind_template_callback (widget_class, thunar_uca_chooser_add_clicked);
gtk_widget_class_bind_template_callback (widget_class, thunar_uca_chooser_edit_clicked);
gtk_widget_class_bind_template_callback (widget_class, thunar_uca_chooser_delete_clicked);
gtk_widget_class_bind_template_callback (widget_class, thunar_uca_chooser_up_clicked);
gtk_widget_class_bind_template_callback (widget_class, thunar_uca_chooser_down_clicked);
gtk_widget_class_bind_template_callback (widget_class, thunar_uca_chooser_selection_changed);
}
static void
thunar_uca_chooser_init (ThunarUcaChooser *uca_chooser)
{
GtkTreeViewColumn *column;
GtkCellRenderer *renderer;
ThunarUcaModel *uca_model;
gboolean use_header_bar = FALSE;
/* Initialize the template for this instance */
gtk_widget_init_template (GTK_WIDGET (uca_chooser));
/* configure the dialog window */
g_object_get (gtk_settings_get_for_screen (gtk_widget_get_screen (GTK_WIDGET (uca_chooser))),
"gtk-dialogs-use-header", &use_header_bar, NULL);
if (!use_header_bar)
{
/* add a regular close button, the header bar already provides one */
gtk_dialog_add_button (GTK_DIALOG (uca_chooser), _("_Close"), GTK_RESPONSE_CLOSE);
}
gtk_dialog_set_default_response (GTK_DIALOG (uca_chooser), GTK_RESPONSE_CLOSE);
/* configure the tree view */
uca_model = thunar_uca_model_get_default ();
gtk_tree_view_set_model (GTK_TREE_VIEW (uca_chooser->treeview), GTK_TREE_MODEL (uca_model));
g_object_unref (G_OBJECT (uca_model));
column = gtk_tree_view_column_new ();
gtk_tree_view_column_set_expand (column, TRUE);
gtk_tree_view_column_set_resizable (column, FALSE);
gtk_tree_view_append_column (GTK_TREE_VIEW (uca_chooser->treeview), column);
renderer = g_object_new (GTK_TYPE_CELL_RENDERER_PIXBUF, "stock-size", GTK_ICON_SIZE_DND, "xpad", 2, "ypad", 2, NULL);
gtk_tree_view_column_pack_start (column, renderer, FALSE);
gtk_tree_view_column_set_attributes (column, renderer, "gicon", THUNAR_UCA_MODEL_COLUMN_GICON, NULL);
renderer = gtk_cell_renderer_text_new ();
gtk_tree_view_column_pack_start (column, renderer, TRUE);
gtk_tree_view_column_set_attributes (column, renderer, "markup", THUNAR_UCA_MODEL_COLUMN_STOCK_LABEL, NULL);
/* configure the tree view selection */
thunar_uca_chooser_selection_changed (uca_chooser, gtk_tree_view_get_selection (GTK_TREE_VIEW (uca_chooser->treeview)));
}
static gboolean
thunar_uca_chooser_key_press_event (GtkWidget *widget,
GdkEventKey *event)
{
/* close chooser window on Esc key press */
if (G_UNLIKELY (event->keyval == GDK_KEY_Escape))
{
gtk_dialog_response (GTK_DIALOG (widget), GTK_RESPONSE_CLOSE);
return TRUE;
}
return (*GTK_WIDGET_CLASS (thunar_uca_chooser_parent_class)->key_press_event) (widget, event);
}
static void
thunar_uca_chooser_response (GtkDialog *dialog,
gint response)
{
if (response == GTK_RESPONSE_CLOSE)
{
gtk_widget_destroy (GTK_WIDGET (dialog));
}
else if (GTK_DIALOG_CLASS (thunar_uca_chooser_parent_class)->response != NULL)
{
(*GTK_DIALOG_CLASS (thunar_uca_chooser_parent_class)->response) (dialog, response);
}
}
static void
thunar_uca_chooser_selection_changed (ThunarUcaChooser *uca_chooser,
GtkTreeSelection *selection)
{
GtkTreeModel *model;
GtkTreePath *path = NULL;
GtkTreeIter iter;
gboolean selected;
g_return_if_fail (THUNAR_UCA_IS_CHOOSER (uca_chooser));
g_return_if_fail (GTK_IS_TREE_SELECTION (selection));
/* check if we have currently selected an item */
selected = gtk_tree_selection_get_selected (selection, &model, &iter);
if (G_LIKELY (selected))
{
/* determine the path for the selected iter */
path = gtk_tree_model_get_path (model, &iter);
}
/* change sensitivity of "Edit" and "Delete" appropritately */
gtk_widget_set_sensitive (uca_chooser->edit_button, selected);
gtk_widget_set_sensitive (uca_chooser->delete_button, selected);
/* change sensitivity of "Move Up" and "Move Down" appropritately */
gtk_widget_set_sensitive (uca_chooser->up_button, selected && gtk_tree_path_get_indices (path)[0] > 0);
gtk_widget_set_sensitive (uca_chooser->down_button, selected && gtk_tree_path_get_indices (path)[0] < gtk_tree_model_iter_n_children (model, NULL) - 1);
/* release path (if any) */
if (G_LIKELY (path != NULL))
gtk_tree_path_free (path);
}
static void
thunar_uca_chooser_exchange (ThunarUcaChooser *uca_chooser,
GtkTreeSelection *selection,
GtkTreeModel *model,
GtkTreeIter *iter_a,
GtkTreeIter *iter_b)
{
g_return_if_fail (THUNAR_UCA_IS_CHOOSER (uca_chooser));
g_return_if_fail (GTK_IS_TREE_SELECTION (selection));
g_return_if_fail (GTK_IS_TREE_MODEL (model));
g_return_if_fail (iter_a != NULL);
g_return_if_fail (iter_b != NULL);
/* perform the move */
thunar_uca_model_exchange (THUNAR_UCA_MODEL (model), iter_a, iter_b);
/* tell the chooser that the selection may have changed */
thunar_uca_chooser_selection_changed (uca_chooser, selection);
/* sync the model to persistent storage */
thunar_uca_chooser_save (uca_chooser, THUNAR_UCA_MODEL (model));
}
static void
thunar_uca_chooser_open_editor (ThunarUcaChooser *uca_chooser,
gboolean edit)
{
GtkTreeSelection *selection;
GtkTreeModel *model;
GtkTreeIter iter;
GtkWidget *editor;
gboolean use_header_bar = FALSE;
g_return_if_fail (THUNAR_UCA_IS_CHOOSER (uca_chooser));
/* allocate the new editor */
g_object_get (gtk_settings_get_for_screen (gtk_widget_get_screen (GTK_WIDGET (uca_chooser))),
"gtk-dialogs-use-header", &use_header_bar, NULL);
editor = g_object_new (THUNAR_UCA_TYPE_EDITOR, "use-header-bar", use_header_bar, NULL);
gtk_window_set_title (GTK_WINDOW (editor), edit ? _("Edit Action") : _("Create Action"));
gtk_window_set_transient_for (GTK_WINDOW (editor), GTK_WINDOW (uca_chooser));
/* load the editor with the currently selected item (when editing) */
selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (uca_chooser->treeview));
if (gtk_tree_selection_get_selected (selection, &model, &iter) && edit)
thunar_uca_editor_load (THUNAR_UCA_EDITOR (editor), THUNAR_UCA_MODEL (model), &iter);
/* run the editor */
if (gtk_dialog_run (GTK_DIALOG (editor)) == GTK_RESPONSE_OK)
{
/* append a new iter (when not editing) */
if (G_UNLIKELY (!edit))
thunar_uca_model_append (THUNAR_UCA_MODEL (model), &iter);
/* save the editor values to the model */
thunar_uca_editor_save (THUNAR_UCA_EDITOR (editor), THUNAR_UCA_MODEL (model), &iter);
/* hide the editor window */
gtk_widget_hide (editor);
/* sync the model to persistent storage */
thunar_uca_chooser_save (uca_chooser, THUNAR_UCA_MODEL (model));
}
/* destroy the editor */
gtk_widget_destroy (editor);
}
static void
thunar_uca_chooser_save (ThunarUcaChooser *uca_chooser,
ThunarUcaModel *uca_model)
{
GtkWidget *dialog;
GError *error = NULL;
g_return_if_fail (THUNAR_UCA_IS_CHOOSER (uca_chooser));
g_return_if_fail (THUNAR_UCA_IS_MODEL (uca_model));
/* sync the model to persistent storage */
if (!thunar_uca_model_save (uca_model, &error))
{
dialog = gtk_message_dialog_new (GTK_WINDOW (uca_chooser),
GTK_DIALOG_DESTROY_WITH_PARENT
| GTK_DIALOG_MODAL,
GTK_MESSAGE_ERROR,
GTK_BUTTONS_CLOSE,
_("Failed to save actions to disk."));
gtk_window_set_title (GTK_WINDOW (dialog), _("Error"));
gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), "%s.", error->message);
gtk_dialog_run (GTK_DIALOG (dialog));
gtk_widget_destroy (dialog);
g_error_free (error);
}
}
static void
thunar_uca_chooser_add_clicked (ThunarUcaChooser *uca_chooser)
{
g_return_if_fail (THUNAR_UCA_IS_CHOOSER (uca_chooser));
thunar_uca_chooser_open_editor (uca_chooser, FALSE);
}
static void
thunar_uca_chooser_edit_clicked (ThunarUcaChooser *uca_chooser)
{
g_return_if_fail (THUNAR_UCA_IS_CHOOSER (uca_chooser));
thunar_uca_chooser_open_editor (uca_chooser, TRUE);
}
static void
thunar_uca_chooser_delete_clicked (ThunarUcaChooser *uca_chooser)
{
GtkTreeSelection *selection;
GtkTreeModel *model;
GtkTreeIter iter;
gchar *name;
GtkWidget *dialog;
gint response;
g_return_if_fail (THUNAR_UCA_IS_CHOOSER (uca_chooser));
/* verify that we have an item selected and determine the iter for that item */
selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (uca_chooser->treeview));
if (gtk_tree_selection_get_selected (selection, &model, &iter))
{
/* create the question dialog */
gtk_tree_model_get (model, &iter, THUNAR_UCA_MODEL_COLUMN_NAME, &name, -1);
dialog = gtk_message_dialog_new (GTK_WINDOW (uca_chooser),
GTK_DIALOG_DESTROY_WITH_PARENT
| GTK_DIALOG_MODAL,
GTK_MESSAGE_QUESTION,
GTK_BUTTONS_NONE,
_("Are you sure that you want to delete\naction \"%s\"?"), name);
gtk_window_set_title (GTK_WINDOW (dialog), _("Delete action"));
gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), _("If you delete a custom action, it is permanently lost."));
gtk_dialog_add_buttons (GTK_DIALOG (dialog), _("_Cancel"), GTK_RESPONSE_CANCEL, _("_Delete"), GTK_RESPONSE_YES, NULL);
gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_YES);
g_free (name);
response = gtk_dialog_run (GTK_DIALOG (dialog));
gtk_widget_destroy (dialog);
if (response == GTK_RESPONSE_YES)
{
/* remove the row from the model */
thunar_uca_model_remove (THUNAR_UCA_MODEL (model), &iter);
/* sync the model to persistent storage */
thunar_uca_chooser_save (uca_chooser, THUNAR_UCA_MODEL (model));
}
}
}
static void
thunar_uca_chooser_up_clicked (ThunarUcaChooser *uca_chooser)
{
GtkTreeSelection *selection;
GtkTreeModel *model;
GtkTreePath *path;
GtkTreeIter iter_a;
GtkTreeIter iter_b;
g_return_if_fail (THUNAR_UCA_IS_CHOOSER (uca_chooser));
/* determine the currently selected item */
selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (uca_chooser->treeview));
if (gtk_tree_selection_get_selected (selection, &model, &iter_a))
{
/* determine the tree path to the iter */
path = gtk_tree_model_get_path (model, &iter_a);
if (gtk_tree_path_prev (path))
{
/* determine the iter for the previous item */
gtk_tree_model_get_iter (model, &iter_b, path);
/* perform the exchange operation */
thunar_uca_chooser_exchange (uca_chooser, selection, model, &iter_a, &iter_b);
}
/* release the path */
gtk_tree_path_free (path);
}
}
static void
thunar_uca_chooser_down_clicked (ThunarUcaChooser *uca_chooser)
{
GtkTreeSelection *selection;
GtkTreeModel *model;
GtkTreeIter iter_a;
GtkTreeIter iter_b;
g_return_if_fail (THUNAR_UCA_IS_CHOOSER (uca_chooser));
/* determine the currently selected item */
selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (uca_chooser->treeview));
if (gtk_tree_selection_get_selected (selection, &model, &iter_a))
{
/* determine the iter to the next item */
iter_b = iter_a;
if (gtk_tree_model_iter_next (model, &iter_b))
thunar_uca_chooser_exchange (uca_chooser, selection, model, &iter_a, &iter_b);
}
}

View file

@ -0,0 +1,45 @@
/* vi:set et ai sw=2 sts=2 ts=2: */
/*-
* Copyright (c) 2005 Benedikt Meurer <benny@xfce.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#ifndef __THUNAR_UCA_CHOOSER_H__
#define __THUNAR_UCA_CHOOSER_H__
#include <thunarx/thunarx.h>
G_BEGIN_DECLS;
typedef struct _ThunarUcaChooserClass ThunarUcaChooserClass;
typedef struct _ThunarUcaChooser ThunarUcaChooser;
#define THUNAR_UCA_TYPE_CHOOSER (thunar_uca_chooser_get_type ())
#define THUNAR_UCA_CHOOSER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), THUNAR_UCA_TYPE_CHOOSER, ThunarUcaChooser))
#define THUNAR_UCA_CHOOSER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), THUNAR_UCA_TYPE_CHOOSER, ThunarUcaChooserClass))
#define THUNAR_UCA_IS_CHOOSER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), THUNAR_UCA_TYPE_CHOOSER))
#define THUNAR_UCA_IS_CHOOSER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), THUNAR_UCA_TYPE_CHOOSER))
#define THUNAR_UCA_CHOOSER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), THUNAR_UCA_TYPE_CHOOSER, ThunarUcaChooserClass))
GType
thunar_uca_chooser_get_type (void) G_GNUC_CONST;
void
thunar_uca_chooser_register_type (ThunarxProviderPlugin *plugin);
G_END_DECLS;
#endif /* !__THUNAR_UCA_CHOOSER_H__ */

View file

@ -0,0 +1,139 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
For now, better don't edit this with glade, since glade insists upon inserting
deprecated properties and otherwise messing with the file.
-->
<interface>
<requires lib="gtk+" version="3.12"/>
<template class="ThunarUcaChooser" parent="GtkDialog">
<property name="default-width">500</property>
<property name="default-height">350</property>
<property name="type-hint">dialog</property>
<property name="title" translatable="yes">Custom Actions</property>
<property name="destroy-with-parent">True</property>
<child internal-child="vbox">
<object class="GtkBox" id="vbox">
<!-- label at the top -->
<child>
<object class="GtkBox">
<property name="border-width">6</property>
<property name="spacing">3</property>
<property name="visible">TRUE</property>
<child>
<object class="GtkImage">
<property name="icon-name">dialog-information</property>
<property name="icon-size">5</property>
<property name="visible">TRUE</property>
<property name="yalign">0.0</property>
</object>
</child>
<child>
<object class="GtkLabel">
<property name="label" translatable="yes">Configure custom actions that will appear in the file manager's context menus and/or toolbar.
These actions are applicable to folders or certain kinds of files.
Check the &lt;a href="https://docs.xfce.org/xfce/thunar/custom-actions"&gt;documentation&lt;/a&gt; for a collection of custom action examples.</property>
<property name="use-markup">True</property>
<property name="xalign">0.0</property>
<property name="yalign">0.5</property>
<property name="visible">TRUE</property>
<property name="wrap">TRUE</property>
</object>
</child>
</object>
</child>
<!-- main part -->
<child>
<object class="GtkFrame">
<property name="border-width">6</property>
<property name="visible">true</property>
<child>
<object class="GtkBox">
<property name="spacing">0</property>
<property name="visible">TRUE</property>
<property name="hexpand">TRUE</property>
<property name="vexpand">TRUE</property>
<!-- tree view containing the list of items -->
<child>
<object class="GtkScrolledWindow">
<property name="visible">TRUE</property>
<property name="expand">TRUE</property>
<child>
<object class="GtkTreeView" id="treeview">
<property name="headers-visible">FALSE</property>
<property name="visible">TRUE</property>
<signal name="row-activated" handler="thunar_uca_chooser_edit_clicked" swapped="true" />
<!-- TODO: setup columns here -->
<child internal-child="selection">
<object class="GtkTreeSelection">
<property name="mode">GTK_SELECTION_SINGLE</property>
<signal name="changed" handler="thunar_uca_chooser_selection_changed" swapped="true" />
</object>
</child>
</object>
</child>
</object>
</child>
<!-- buttons -->
<child>
<object class="GtkToolbar">
<property name="orientation">GTK_ORIENTATION_VERTICAL</property>
<property name="visible">TRUE</property>
<property name="toolbar-style">GTK_TOOLBAR_ICONS</property>
<property name="icon-size">GTK_ICON_SIZE_BUTTON</property>
<property name="show-arrow">false</property>
<!-- add button -->
<child>
<object class="GtkToolButton" id="add_button">
<property name="tooltip-text" translatable="yes">Add a new custom action.</property>
<property name="visible">TRUE</property>
<property name="icon-name">list-add-symbolic</property>
<signal name="clicked" handler="thunar_uca_chooser_add_clicked" swapped="true" />
</object>
</child>
<!-- edit button -->
<child>
<object class="GtkToolButton" id="edit_button">
<property name="tooltip-text" translatable="yes">Edit the currently selected action.</property>
<property name="visible">TRUE</property>
<property name="icon-name">emblem-system-symbolic</property>
<signal name="clicked" handler="thunar_uca_chooser_edit_clicked" swapped="true" />
</object>
</child>
<!-- remove button -->
<child>
<object class="GtkToolButton" id="delete_button">
<property name="tooltip-text" translatable="yes">Delete the currently selected action.</property>
<property name="visible">TRUE</property>
<property name="icon-name">list-remove-symbolic</property>
<signal name="clicked" handler="thunar_uca_chooser_delete_clicked" swapped="true" />
</object>
</child>
<!-- move up button -->
<child>
<object class="GtkToolButton" id="up_button">
<property name="tooltip-text" translatable="yes">Move the currently selected action up by one row.</property>
<property name="visible">TRUE</property>
<property name="icon-name">go-up-symbolic</property>
<signal name="clicked" handler="thunar_uca_chooser_up_clicked" swapped="true" />
</object>
</child>
<!-- move down button -->
<child>
<object class="GtkToolButton" id="down_button">
<property name="tooltip-text" translatable="yes">Move the currently selected action down by one row.</property>
<property name="visible">TRUE</property>
<property name="icon-name">go-down-symbolic</property>
<signal name="clicked" handler="thunar_uca_chooser_down_clicked" swapped="true" />
</object>
</child>
</object>
</child> <!-- end: buttons toolbar -->
</object>
</child> <!-- end of main part hbox -->
</object>
</child> <!-- end of main part frame -->
</object>
</child>
</template>
</interface>

View file

@ -0,0 +1,143 @@
/* vi:set et ai sw=2 sts=2 ts=2: */
/*-
* Copyright (c) 2005 Benedikt Meurer <benny@xfce.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; 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
#include <thunar-uca/thunar-uca-context.h>
#include <thunarx/thunarx.h>
struct _ThunarUcaContext
{
gint ref_count;
GList *files;
GtkWidget *window;
};
/**
* thunar_uca_context_new:
* @window : a #GtkWindow.
* @files : a #GList of #ThunarxFileInfo<!---->s.
*
* Allocates a new #ThunarUcaContext with the given @window and
* @files.
*
* The caller is responsible to free the returned context
* using thunar_uca_context_unref().
*
* Return value: the newly allocated #ThunarUcaContext.
**/
ThunarUcaContext *
thunar_uca_context_new (GtkWidget *window,
GList *files)
{
ThunarUcaContext *context;
context = g_new (ThunarUcaContext, 1);
context->ref_count = 1;
context->window = window;
context->files = thunarx_file_info_list_copy (files);
/* add a weak reference on the window */
if (G_LIKELY (context->window != NULL))
g_object_add_weak_pointer (G_OBJECT (context->window), (gpointer) &context->window);
return context;
}
/**
* thunar_uca_context_ref:
* @context : a #ThunarUcaContext.
*
* Increases the reference count on @context.
*
* Return value: reference to @context.
**/
ThunarUcaContext *
thunar_uca_context_ref (ThunarUcaContext *context)
{
context->ref_count += 1;
return context;
}
/**
* thunar_uca_context_unref:
* @context : a #ThunarUcaContext.
*
* Decreases the reference count on @context
* and frees @context once the reference count
* drops to zero.
**/
void
thunar_uca_context_unref (ThunarUcaContext *context)
{
if (--context->ref_count == 0)
{
/* drop the weak ref on the window */
if (G_LIKELY (context->window != NULL))
g_object_remove_weak_pointer (G_OBJECT (context->window), (gpointer) &context->window);
/* release the file infos */
thunarx_file_info_list_free (context->files);
/* remove the context data */
g_free (context);
}
}
/**
* thunar_uca_context_get_files:
* @context : a #ThunarUcaContext.
*
* Return value: the #ThunarxFileInfo<!---->s for @context.
**/
GList *
thunar_uca_context_get_files (const ThunarUcaContext *context)
{
return context->files;
}
/**
* thunar_uca_context_get_window:
* @context : a #ThunarUcaContext.
*
* Returns the #GtkWindow for @context or %NULL if the
* window was destroyed in the meantime.
*
* Return value: the #GtkWindow for @context.
**/
GtkWidget *
thunar_uca_context_get_window (const ThunarUcaContext *context)
{
return context->window;
}

View file

@ -0,0 +1,46 @@
/* vi:set et ai sw=2 sts=2 ts=2: */
/*-
* Copyright (c) 2005 Benedikt Meurer <benny@xfce.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#ifndef __THUNAR_UCA_CONTEXT_H__
#define __THUNAR_UCA_CONTEXT_H__
#include <gtk/gtk.h>
G_BEGIN_DECLS;
typedef struct _ThunarUcaContext ThunarUcaContext;
ThunarUcaContext *
thunar_uca_context_new (GtkWidget *window,
GList *files);
ThunarUcaContext *
thunar_uca_context_ref (ThunarUcaContext *context);
void
thunar_uca_context_unref (ThunarUcaContext *context);
GList *
thunar_uca_context_get_files (const ThunarUcaContext *context);
GtkWidget *
thunar_uca_context_get_window (const ThunarUcaContext *context);
G_END_DECLS;
#endif /* !__THUNAR_UCA_CONTEXT_H__ */

View file

@ -0,0 +1,705 @@
/* vi:set et ai sw=2 sts=2 ts=2: */
/*-
* Copyright (c) 2005-2007 Benedikt Meurer <benny@xfce.org>
* Copyright (c) 2011 Jannis Pohlmann <jannis@xfce.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; 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
#include <exo/exo.h>
#include <libxfce4kbd-private/xfce-shortcut-dialog.h>
#include <libxfce4ui/libxfce4ui.h>
#include <thunar-uca/thunar-uca-editor.h>
static const gchar *
thunar_uca_editor_get_icon_name (const ThunarUcaEditor *uca_editor);
static void
thunar_uca_editor_set_icon_name (ThunarUcaEditor *uca_editor,
const gchar *icon_name);
static ThunarUcaTypes
thunar_uca_editor_get_types (const ThunarUcaEditor *uca_editor);
static void
thunar_uca_editor_set_types (ThunarUcaEditor *uca_editor,
ThunarUcaTypes types);
static void
thunar_uca_editor_command_clicked (ThunarUcaEditor *uca_editor);
static void
thunar_uca_editor_shortcut_clicked (ThunarUcaEditor *uca_editor);
static void
thunar_uca_editor_shortcut_clear_clicked (ThunarUcaEditor *uca_editor);
static void
thunar_uca_editor_icon_clicked (ThunarUcaEditor *uca_editor);
static void
thunar_uca_editor_constructed (GObject *object);
static void
thunar_uca_editor_finalize (GObject *object);
struct _ThunarUcaEditorClass
{
GtkDialogClass __parent__;
};
struct _ThunarUcaEditor
{
GtkDialog __parent__;
GtkWidget *notebook;
GtkWidget *name_entry;
GtkWidget *sub_menu_entry;
GtkWidget *description_entry;
GtkWidget *icon_button;
GtkWidget *command_entry;
GtkWidget *shortcut_button;
GtkWidget *sn_button;
GtkWidget *patterns_entry;
GtkWidget *range_entry;
GtkWidget *directories_button;
GtkWidget *audio_files_button;
GtkWidget *image_files_button;
GtkWidget *text_files_button;
GtkWidget *video_files_button;
GtkWidget *other_files_button;
gchar *accel_path;
GdkModifierType accel_mods;
guint accel_key;
};
typedef struct
{
gboolean in_use;
GdkModifierType mods;
guint key;
gchar *current_path;
gchar *other_path;
} ShortcutInfo;
THUNARX_DEFINE_TYPE (ThunarUcaEditor, thunar_uca_editor, GTK_TYPE_DIALOG);
static void
thunar_uca_editor_class_init (ThunarUcaEditorClass *klass)
{
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
GObjectClass *object_class = G_OBJECT_CLASS (klass);
/* vfuncs */
object_class->constructed = thunar_uca_editor_constructed;
object_class->finalize = thunar_uca_editor_finalize;
/* Setup the template xml */
gtk_widget_class_set_template_from_resource (widget_class, "/org/xfce/thunar/uca/editor.ui");
/* bind stuff */
gtk_widget_class_bind_template_child (widget_class, ThunarUcaEditor, notebook);
gtk_widget_class_bind_template_child (widget_class, ThunarUcaEditor, name_entry);
gtk_widget_class_bind_template_child (widget_class, ThunarUcaEditor, sub_menu_entry);
gtk_widget_class_bind_template_child (widget_class, ThunarUcaEditor, description_entry);
gtk_widget_class_bind_template_child (widget_class, ThunarUcaEditor, icon_button);
gtk_widget_class_bind_template_child (widget_class, ThunarUcaEditor, command_entry);
gtk_widget_class_bind_template_child (widget_class, ThunarUcaEditor, shortcut_button);
gtk_widget_class_bind_template_child (widget_class, ThunarUcaEditor, sn_button);
gtk_widget_class_bind_template_child (widget_class, ThunarUcaEditor, patterns_entry);
gtk_widget_class_bind_template_child (widget_class, ThunarUcaEditor, range_entry);
gtk_widget_class_bind_template_child (widget_class, ThunarUcaEditor, directories_button);
gtk_widget_class_bind_template_child (widget_class, ThunarUcaEditor, audio_files_button);
gtk_widget_class_bind_template_child (widget_class, ThunarUcaEditor, image_files_button);
gtk_widget_class_bind_template_child (widget_class, ThunarUcaEditor, text_files_button);
gtk_widget_class_bind_template_child (widget_class, ThunarUcaEditor, video_files_button);
gtk_widget_class_bind_template_child (widget_class, ThunarUcaEditor, other_files_button);
gtk_widget_class_bind_template_callback (widget_class, thunar_uca_editor_icon_clicked);
gtk_widget_class_bind_template_callback (widget_class, thunar_uca_editor_command_clicked);
gtk_widget_class_bind_template_callback (widget_class, thunar_uca_editor_shortcut_clicked);
gtk_widget_class_bind_template_callback (widget_class, thunar_uca_editor_shortcut_clear_clicked);
}
static void
thunar_uca_editor_init (ThunarUcaEditor *uca_editor)
{
/* Initialize the template for this instance */
gtk_widget_init_template (GTK_WIDGET (uca_editor));
/* configure the dialog properties */
gtk_dialog_add_button (GTK_DIALOG (uca_editor), _("_Cancel"), GTK_RESPONSE_CANCEL);
gtk_dialog_add_button (GTK_DIALOG (uca_editor), _("_OK"), GTK_RESPONSE_OK);
gtk_dialog_set_default_response (GTK_DIALOG (uca_editor), GTK_RESPONSE_OK);
}
static void
thunar_uca_editor_constructed (GObject *object)
{
ThunarUcaEditor *editor = THUNAR_UCA_EDITOR (object);
G_OBJECT_CLASS (thunar_uca_editor_parent_class)->constructed (object);
/* Visual tweaks for header-bar mode only */
g_object_set (gtk_dialog_get_content_area (GTK_DIALOG (editor)), "border-width", 0, NULL);
}
static void
thunar_uca_editor_finalize (GObject *object)
{
ThunarUcaEditor *editor = THUNAR_UCA_EDITOR (object);
g_free (editor->accel_path);
(*G_OBJECT_CLASS (thunar_uca_editor_parent_class)->finalize) (object);
}
static void
thunar_uca_editor_command_clicked (ThunarUcaEditor *uca_editor)
{
GtkFileFilter *filter;
GtkWidget *chooser;
gchar *filename;
gchar **argv = NULL;
gchar *s;
gint argc;
g_return_if_fail (THUNAR_UCA_IS_EDITOR (uca_editor));
chooser = gtk_file_chooser_dialog_new (_("Select an Application"),
GTK_WINDOW (uca_editor),
GTK_FILE_CHOOSER_ACTION_OPEN,
_("_Cancel"), GTK_RESPONSE_CANCEL,
_("_Open"), GTK_RESPONSE_ACCEPT,
NULL);
gtk_dialog_set_default_response (GTK_DIALOG (chooser), GTK_RESPONSE_ACCEPT);
gtk_file_chooser_set_local_only (GTK_FILE_CHOOSER (chooser), TRUE);
/* add file chooser filters */
filter = gtk_file_filter_new ();
gtk_file_filter_set_name (filter, _("All Files"));
gtk_file_filter_add_pattern (filter, "*");
gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (chooser), filter);
filter = gtk_file_filter_new ();
gtk_file_filter_set_name (filter, _("Executable Files"));
gtk_file_filter_add_mime_type (filter, "application/x-csh");
gtk_file_filter_add_mime_type (filter, "application/x-executable");
gtk_file_filter_add_mime_type (filter, "application/x-perl");
gtk_file_filter_add_mime_type (filter, "application/x-python");
gtk_file_filter_add_mime_type (filter, "application/x-ruby");
gtk_file_filter_add_mime_type (filter, "application/x-shellscript");
gtk_file_filter_add_pattern (filter, "*.pl");
gtk_file_filter_add_pattern (filter, "*.py");
gtk_file_filter_add_pattern (filter, "*.rb");
gtk_file_filter_add_pattern (filter, "*.sh");
gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (chooser), filter);
gtk_file_chooser_set_filter (GTK_FILE_CHOOSER (chooser), filter);
filter = gtk_file_filter_new ();
gtk_file_filter_set_name (filter, _("Perl Scripts"));
gtk_file_filter_add_mime_type (filter, "application/x-perl");
gtk_file_filter_add_pattern (filter, "*.pl");
gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (chooser), filter);
filter = gtk_file_filter_new ();
gtk_file_filter_set_name (filter, _("Python Scripts"));
gtk_file_filter_add_mime_type (filter, "application/x-python");
gtk_file_filter_add_pattern (filter, "*.py");
gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (chooser), filter);
filter = gtk_file_filter_new ();
gtk_file_filter_set_name (filter, _("Ruby Scripts"));
gtk_file_filter_add_mime_type (filter, "application/x-ruby");
gtk_file_filter_add_pattern (filter, "*.rb");
gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (chooser), filter);
filter = gtk_file_filter_new ();
gtk_file_filter_set_name (filter, _("Shell Scripts"));
gtk_file_filter_add_mime_type (filter, "application/x-csh");
gtk_file_filter_add_mime_type (filter, "application/x-shellscript");
gtk_file_filter_add_pattern (filter, "*.sh");
gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (chooser), filter);
/* use the bindir as default folder */
gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (chooser), BINDIR);
/* setup the currently selected file */
filename = gtk_editable_get_chars (GTK_EDITABLE (uca_editor->command_entry), 0, -1);
if (G_LIKELY (filename != NULL))
{
/* use only the first argument */
s = strchr (filename, ' ');
if (G_UNLIKELY (s != NULL))
*s = '\0';
/* check if we have a file name */
if (G_LIKELY (*filename != '\0'))
{
/* check if the filename is not an absolute path */
if (G_LIKELY (!g_path_is_absolute (filename)))
{
/* try to lookup the filename in $PATH */
s = g_find_program_in_path (filename);
if (G_LIKELY (s != NULL))
{
/* use the absolute path instead */
g_free (filename);
filename = s;
}
}
/* check if we have an absolute path now */
if (G_LIKELY (g_path_is_absolute (filename)))
gtk_file_chooser_set_filename (GTK_FILE_CHOOSER (chooser), filename);
}
/* release the filename */
g_free (filename);
}
/* run the chooser dialog */
if (gtk_dialog_run (GTK_DIALOG (chooser)) == GTK_RESPONSE_ACCEPT)
{
/* determine the path to the selected file */
filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (chooser));
/* check if we need to quote the filename */
if (!g_shell_parse_argv (filename, &argc, &argv, NULL) || argc > 1)
{
/* shell is unable to interpret properly without quoting */
s = g_shell_quote (filename);
g_free (filename);
filename = s;
}
g_strfreev (argv);
/* append %f to filename, user may change that afterwards */
s = g_strconcat (filename, " %f", NULL);
gtk_entry_set_text (GTK_ENTRY (uca_editor->command_entry), s);
g_free (filename);
g_free (s);
}
gtk_widget_destroy (chooser);
}
static void
thunar_uca_editor_shortcut_check (gpointer data,
const gchar *path,
guint key,
GdkModifierType mods,
gboolean changed)
{
ShortcutInfo *info = (ShortcutInfo *) data;
if (info->in_use)
return;
info->in_use = info->mods == mods && info->key == key && g_strcmp0 (info->current_path, path) != 0;
if (info->in_use)
info->other_path = g_strdup (path);
}
static gboolean
thunar_uca_editor_validate_shortcut (XfceShortcutDialog *dialog,
const gchar *shortcut,
ThunarUcaEditor *uca_editor)
{
GdkModifierType accel_mods;
guint accel_key;
ShortcutInfo info;
gchar *command, *message;
g_return_val_if_fail (XFCE_IS_SHORTCUT_DIALOG (dialog), FALSE);
g_return_val_if_fail (shortcut != NULL, FALSE);
/* Ignore empty shortcuts */
if (G_UNLIKELY (g_utf8_strlen (shortcut, -1) == 0))
return FALSE;
/* Ignore raw 'Return' and 'space' since that may have been used to activate the shortcut row */
if (G_UNLIKELY (g_utf8_collate (shortcut, "Return") == 0 || g_utf8_collate (shortcut, "space") == 0))
return FALSE;
gtk_accelerator_parse (shortcut, &accel_key, &accel_mods);
info.in_use = FALSE;
info.mods = accel_mods;
info.key = accel_key;
info.current_path = uca_editor->accel_path;
info.other_path = NULL;
gtk_accel_map_foreach_unfiltered (&info, thunar_uca_editor_shortcut_check);
if (info.in_use)
{
command = g_strrstr (info.other_path, "/");
command = command == NULL ? info.other_path : command + 1; /* skip leading slash */
message = g_strdup_printf (_("This keyboard shortcut is currently used by: '%s'"),
command);
xfce_dialog_show_warning (GTK_WINDOW (dialog), message,
_("Keyboard shortcut already in use"));
g_free (message);
}
g_free (info.other_path);
return !info.in_use;
}
static void
thunar_uca_editor_shortcut_clicked (ThunarUcaEditor *uca_editor)
{
GtkWidget *dialog;
gint response;
const gchar *shortcut;
GdkModifierType accel_mods;
guint accel_key;
gchar *label;
dialog = xfce_shortcut_dialog_new ("thunar",
gtk_entry_get_text (GTK_ENTRY (uca_editor->name_entry)), "");
g_signal_connect (dialog, "validate-shortcut",
G_CALLBACK (thunar_uca_editor_validate_shortcut),
uca_editor);
response = xfce_shortcut_dialog_run (XFCE_SHORTCUT_DIALOG (dialog),
gtk_widget_get_toplevel (uca_editor->shortcut_button));
if (G_LIKELY (response == GTK_RESPONSE_OK))
{
shortcut = xfce_shortcut_dialog_get_shortcut (XFCE_SHORTCUT_DIALOG (dialog));
gtk_accelerator_parse (shortcut, &accel_key, &accel_mods);
label = gtk_accelerator_get_label (accel_key, accel_mods);
gtk_button_set_label (GTK_BUTTON (uca_editor->shortcut_button), label);
uca_editor->accel_key = accel_key;
uca_editor->accel_mods = accel_mods;
g_free (label);
}
gtk_widget_destroy (dialog);
}
static void
thunar_uca_editor_shortcut_clear_clicked (ThunarUcaEditor *uca_editor)
{
uca_editor->accel_key = 0;
uca_editor->accel_mods = 0;
gtk_button_set_label (GTK_BUTTON (uca_editor->shortcut_button), _("None"));
}
static void
thunar_uca_editor_icon_clicked (ThunarUcaEditor *uca_editor)
{
const gchar *name;
GtkWidget *chooser;
gchar *title;
gchar *icon;
g_return_if_fail (THUNAR_UCA_IS_EDITOR (uca_editor));
/* determine the name of the action being edited */
name = gtk_entry_get_text (GTK_ENTRY (uca_editor->name_entry));
if (G_UNLIKELY (name == NULL || *name == '\0'))
name = _("Unknown");
/* allocate the chooser dialog */
title = g_strdup_printf (_("Select an Icon for \"%s\""), name);
chooser = exo_icon_chooser_dialog_new (title, GTK_WINDOW (uca_editor),
_("_Cancel"), GTK_RESPONSE_CANCEL,
_("_OK"), GTK_RESPONSE_ACCEPT,
NULL);
gtk_dialog_set_default_response (GTK_DIALOG (chooser), GTK_RESPONSE_ACCEPT);
g_free (title);
/* setup the currently selected icon */
icon = g_object_get_data (G_OBJECT (uca_editor->icon_button), "thunar-uca-icon-name");
if (G_LIKELY (icon != NULL && *icon != '\0'))
exo_icon_chooser_dialog_set_icon (EXO_ICON_CHOOSER_DIALOG (chooser), icon);
/* run the icon chooser dialog */
if (gtk_dialog_run (GTK_DIALOG (chooser)) == GTK_RESPONSE_ACCEPT)
{
/* remember the selected icon from the chooser */
icon = exo_icon_chooser_dialog_get_icon (EXO_ICON_CHOOSER_DIALOG (chooser));
thunar_uca_editor_set_icon_name (uca_editor, icon);
g_free (icon);
}
/* destroy the chooser */
gtk_widget_destroy (chooser);
}
static const gchar *
thunar_uca_editor_get_icon_name (const ThunarUcaEditor *uca_editor)
{
g_return_val_if_fail (THUNAR_UCA_IS_EDITOR (uca_editor), NULL);
return g_object_get_data (G_OBJECT (uca_editor->icon_button), "thunar-uca-icon-name");
}
static void
thunar_uca_editor_set_icon_name (ThunarUcaEditor *uca_editor,
const gchar *icon_name)
{
GIcon *icon = NULL;
GtkWidget *image;
GtkWidget *label;
g_return_if_fail (THUNAR_UCA_IS_EDITOR (uca_editor));
/* drop the previous button child */
if (gtk_bin_get_child (GTK_BIN (uca_editor->icon_button)) != NULL)
gtk_widget_destroy (gtk_bin_get_child (GTK_BIN (uca_editor->icon_button)));
/* setup the icon button */
if (icon_name != NULL)
icon = g_icon_new_for_string (icon_name, NULL);
if (G_LIKELY (icon != NULL))
{
/* setup an image for the icon */
image = gtk_image_new_from_gicon (icon, GTK_ICON_SIZE_DIALOG);
g_object_set (image, "icon-size", GTK_ICON_SIZE_DND, NULL);
gtk_container_add (GTK_CONTAINER (uca_editor->icon_button), image);
gtk_widget_show (image);
/* remember the name for the icon */
g_object_set_data_full (G_OBJECT (uca_editor->icon_button), "thunar-uca-icon-name", g_strdup (icon_name), g_free);
/* release the icon */
g_object_unref (G_OBJECT (icon));
}
else
{
/* reset the remembered icon name */
g_object_set_data (G_OBJECT (uca_editor->icon_button), "thunar-uca-icon-name", NULL);
/* setup a label to tell that no icon was selected */
label = gtk_label_new (_("No icon"));
gtk_container_add (GTK_CONTAINER (uca_editor->icon_button), label);
gtk_widget_show (label);
}
}
static ThunarUcaTypes
thunar_uca_editor_get_types (const ThunarUcaEditor *uca_editor)
{
ThunarUcaTypes types = 0;
g_return_val_if_fail (THUNAR_UCA_IS_EDITOR (uca_editor), 0);
if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (uca_editor->directories_button)))
types |= THUNAR_UCA_TYPE_DIRECTORIES;
if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (uca_editor->audio_files_button)))
types |= THUNAR_UCA_TYPE_AUDIO_FILES;
if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (uca_editor->image_files_button)))
types |= THUNAR_UCA_TYPE_IMAGE_FILES;
if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (uca_editor->text_files_button)))
types |= THUNAR_UCA_TYPE_TEXT_FILES;
if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (uca_editor->video_files_button)))
types |= THUNAR_UCA_TYPE_VIDEO_FILES;
if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (uca_editor->other_files_button)))
types |= THUNAR_UCA_TYPE_OTHER_FILES;
return types;
}
static void
thunar_uca_editor_set_types (ThunarUcaEditor *uca_editor,
ThunarUcaTypes types)
{
g_return_if_fail (THUNAR_UCA_IS_EDITOR (uca_editor));
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (uca_editor->directories_button), (types & THUNAR_UCA_TYPE_DIRECTORIES));
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (uca_editor->audio_files_button), (types & THUNAR_UCA_TYPE_AUDIO_FILES));
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (uca_editor->image_files_button), (types & THUNAR_UCA_TYPE_IMAGE_FILES));
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (uca_editor->text_files_button), (types & THUNAR_UCA_TYPE_TEXT_FILES));
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (uca_editor->video_files_button), (types & THUNAR_UCA_TYPE_VIDEO_FILES));
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (uca_editor->other_files_button), (types & THUNAR_UCA_TYPE_OTHER_FILES));
}
/**
* thunar_uca_editor_load:
* @uca_editor : a #ThunarUcaEditor.
* @uca_model : a #ThunarUcaModel.
* @iter : a #GtkTreeIter.
*
* Loads @uca_editor with the data from @uca_model
* at the specified @iter.
**/
void
thunar_uca_editor_load (ThunarUcaEditor *uca_editor,
ThunarUcaModel *uca_model,
GtkTreeIter *iter)
{
ThunarUcaTypes types;
gchar *description;
gchar *patterns;
gchar *range;
gchar *command;
gchar *icon_name;
gchar *name;
gchar *submenu;
gchar *unique_id;
gchar *accel_label = NULL;
gboolean startup_notify;
GtkAccelKey key;
g_return_if_fail (THUNAR_UCA_IS_EDITOR (uca_editor));
g_return_if_fail (THUNAR_UCA_IS_MODEL (uca_model));
g_return_if_fail (iter != NULL);
/* determine the current values from the model */
gtk_tree_model_get (GTK_TREE_MODEL (uca_model), iter,
THUNAR_UCA_MODEL_COLUMN_DESCRIPTION, &description,
THUNAR_UCA_MODEL_COLUMN_PATTERNS, &patterns,
THUNAR_UCA_MODEL_COLUMN_RANGE, &range,
THUNAR_UCA_MODEL_COLUMN_COMMAND, &command,
THUNAR_UCA_MODEL_COLUMN_TYPES, &types,
THUNAR_UCA_MODEL_COLUMN_ICON_NAME, &icon_name,
THUNAR_UCA_MODEL_COLUMN_NAME, &name,
THUNAR_UCA_MODEL_COLUMN_SUB_MENU, &submenu,
THUNAR_UCA_MODEL_COLUMN_STARTUP_NOTIFY, &startup_notify,
THUNAR_UCA_MODEL_COLUMN_UNIQUE_ID, &unique_id,
-1);
/* setup the new selection */
thunar_uca_editor_set_types (uca_editor, types);
/* setup the new icon */
thunar_uca_editor_set_icon_name (uca_editor, icon_name);
/* Resolve shortcut from accelerator */
uca_editor->accel_path = g_strdup_printf ("<Actions>/ThunarActions/uca-action-%s", unique_id);
if (gtk_accel_map_lookup_entry (uca_editor->accel_path, &key) && key.accel_key != 0)
{
accel_label = gtk_accelerator_get_label (key.accel_key, key.accel_mods);
uca_editor->accel_key = key.accel_key;
uca_editor->accel_mods = key.accel_mods;
}
/* apply the new values */
gtk_entry_set_text (GTK_ENTRY (uca_editor->description_entry), (description != NULL) ? description : "");
gtk_entry_set_text (GTK_ENTRY (uca_editor->patterns_entry), (patterns != NULL) ? patterns : "");
gtk_entry_set_text (GTK_ENTRY (uca_editor->command_entry), (command != NULL) ? command : "");
gtk_entry_set_text (GTK_ENTRY (uca_editor->name_entry), (name != NULL) ? name : "");
gtk_entry_set_text (GTK_ENTRY (uca_editor->range_entry), (range != NULL) ? range : "");
gtk_entry_set_text (GTK_ENTRY (uca_editor->sub_menu_entry), (submenu != NULL) ? submenu : "");
gtk_button_set_label (GTK_BUTTON (uca_editor->shortcut_button), (accel_label != NULL) ? accel_label : _("None"));
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (uca_editor->sn_button), startup_notify);
/* cleanup */
g_free (description);
g_free (range);
g_free (patterns);
g_free (command);
g_free (icon_name);
g_free (name);
g_free (submenu);
g_free (unique_id);
g_free (accel_label);
}
/**
* thunar_uca_editor_save:
* @uca_editor : a #ThunarUcaEditor.
* @uca_model : a #ThunarUcaModel.
* @iter : a #GtkTreeIter.
*
* Stores the current values from the @uca_editor in
* the @uca_model at the item specified by @iter.
**/
void
thunar_uca_editor_save (ThunarUcaEditor *uca_editor,
ThunarUcaModel *uca_model,
GtkTreeIter *iter)
{
gchar *unique_id;
GtkAccelKey key;
g_return_if_fail (THUNAR_UCA_IS_EDITOR (uca_editor));
g_return_if_fail (THUNAR_UCA_IS_MODEL (uca_model));
g_return_if_fail (iter != NULL);
gtk_tree_model_get (GTK_TREE_MODEL (uca_model), iter,
THUNAR_UCA_MODEL_COLUMN_UNIQUE_ID, &unique_id,
-1);
/* always clear the accelerator, it'll be updated in thunar_uca_model_update */
if (uca_editor->accel_path != NULL && gtk_accel_map_lookup_entry (uca_editor->accel_path, &key) && key.accel_key != 0)
gtk_accel_map_change_entry (uca_editor->accel_path, 0, 0, TRUE);
thunar_uca_model_update (uca_model, iter,
gtk_entry_get_text (GTK_ENTRY (uca_editor->name_entry)),
gtk_entry_get_text (GTK_ENTRY (uca_editor->sub_menu_entry)),
unique_id,
gtk_entry_get_text (GTK_ENTRY (uca_editor->description_entry)),
thunar_uca_editor_get_icon_name (uca_editor),
gtk_entry_get_text (GTK_ENTRY (uca_editor->command_entry)),
gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (uca_editor->sn_button)),
gtk_entry_get_text (GTK_ENTRY (uca_editor->patterns_entry)),
gtk_entry_get_text (GTK_ENTRY (uca_editor->range_entry)),
thunar_uca_editor_get_types (uca_editor),
uca_editor->accel_key,
uca_editor->accel_mods);
g_free (unique_id);
}

View file

@ -0,0 +1,54 @@
/* vi:set et ai sw=2 sts=2 ts=2: */
/*-
* Copyright (c) 2005 Benedikt Meurer <benny@xfce.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#ifndef __THUNAR_UCA_EDITOR_H__
#define __THUNAR_UCA_EDITOR_H__
#include <thunar-uca/thunar-uca-model.h>
G_BEGIN_DECLS;
typedef struct _ThunarUcaEditorClass ThunarUcaEditorClass;
typedef struct _ThunarUcaEditor ThunarUcaEditor;
#define THUNAR_UCA_TYPE_EDITOR (thunar_uca_editor_get_type ())
#define THUNAR_UCA_EDITOR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), THUNAR_UCA_TYPE_EDITOR, ThunarUcaEditor))
#define THUNAR_UCA_EDITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), THUNAR_UCA_TYPE_EDITOR, ThunarUcaEditorwClass))
#define THUNAR_UCA_IS_EDITOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), THUNAR_UCA_TYPE_EDITOR))
#define THUNAR_UCA_IS_EDITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), THUNAR_UCA_TYPE_EDITOR))
#define THUNAR_UCA_EDITOR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), THUNAR_UCA_TYPE_EDITOR, ThunarUcaEditorwClass))
GType
thunar_uca_editor_get_type (void) G_GNUC_CONST;
void
thunar_uca_editor_register_type (ThunarxProviderPlugin *plugin);
void
thunar_uca_editor_load (ThunarUcaEditor *uca_editor,
ThunarUcaModel *uca_model,
GtkTreeIter *iter);
void
thunar_uca_editor_save (ThunarUcaEditor *uca_editor,
ThunarUcaModel *uca_model,
GtkTreeIter *iter);
G_END_DECLS;
#endif /* !__THUNAR_UCA_EDITOR_H__ */

View file

@ -0,0 +1,916 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.38.2 -->
<!--
For now, better don't edit this with glade, since glade insists upon inserting
deprecated properties and otherwise messing with the file.
-->
<interface>
<requires lib="gtk+" version="3.12"/>
<template class="ThunarUcaEditor" parent="GtkDialog">
<property name="can-focus">False</property>
<property name="title" translatable="yes">Edit Action</property>
<property name="modal">True</property>
<property name="default-width">400</property>
<property name="destroy-with-parent">True</property>
<property name="type-hint">dialog</property>
<child internal-child="vbox">
<object class="GtkBox" id="vbox">
<property name="can-focus">False</property>
<child internal-child="action_area">
<object class="GtkButtonBox">
<property name="can-focus">False</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkNotebook" id="notebook">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="border-width">6</property>
<child>
<!-- n-columns=3 n-rows=8 -->
<object class="GtkGrid" id="grid1">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="border-width">12</property>
<property name="row-spacing">6</property>
<property name="column-spacing">12</property>
<child>
<object class="GtkLabel" id="name_label">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="label" translatable="yes">_Name:</property>
<property name="use-underline">True</property>
<property name="mnemonic-widget">name_entry</property>
<property name="xalign">0</property>
<accessibility>
<relation type="label-for" target="name_entry"/>
</accessibility>
</object>
<packing>
<property name="left-attach">0</property>
<property name="top-attach">0</property>
</packing>
</child>
<child>
<object class="GtkEntry" id="name_entry">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="tooltip-text" translatable="yes">The name of the action that will be displayed in the context menu.</property>
<property name="hexpand">True</property>
<property name="activates-default">True</property>
</object>
<packing>
<property name="left-attach">1</property>
<property name="top-attach">0</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="description_label">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="label" translatable="yes">_Description:</property>
<property name="use-underline">True</property>
<property name="mnemonic-widget">description_entry</property>
<property name="xalign">0</property>
<accessibility>
<relation type="label-for" target="description_entry"/>
</accessibility>
</object>
<packing>
<property name="left-attach">0</property>
<property name="top-attach">1</property>
</packing>
</child>
<child>
<object class="GtkEntry" id="description_entry">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="tooltip-text" translatable="yes">The description of the action that will be displayed as tooltip in the statusbar when selecting the item from the context menu.</property>
<property name="hexpand">True</property>
<property name="activates-default">True</property>
</object>
<packing>
<property name="left-attach">1</property>
<property name="top-attach">1</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="sub_menu_label">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="label" translatable="yes">_Submenu:</property>
<property name="use-underline">True</property>
<property name="mnemonic-widget">sub_menu_entry</property>
<property name="xalign">0</property>
<accessibility>
<relation type="label-for" target="sub_menu_entry"/>
</accessibility>
</object>
<packing>
<property name="left-attach">0</property>
<property name="top-attach">2</property>
</packing>
</child>
<child>
<object class="GtkEntry" id="sub_menu_entry">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="tooltip-text" translatable="yes">The submenu in which the action will be displayed. Leave empty for no menu. Use '/' for nested menus.</property>
<property name="hexpand">True</property>
<property name="activates-default">True</property>
</object>
<packing>
<property name="left-attach">1</property>
<property name="top-attach">2</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="command_label">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="label" translatable="yes">_Command:</property>
<property name="use-underline">True</property>
<property name="mnemonic-widget">command_entry</property>
<property name="xalign">0</property>
<accessibility>
<relation type="label-for" target="command_entry"/>
</accessibility>
</object>
<packing>
<property name="left-attach">0</property>
<property name="top-attach">3</property>
</packing>
</child>
<child>
<object class="GtkBox" id="box1">
<property name="visible">True</property>
<property name="can-focus">False</property>
<child>
<object class="GtkEntry" id="command_entry">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="tooltip-text" translatable="yes">The command (including the necessary parameters) to perform the action. See the command parameter legend below for a list of supported parameter variables, which will be substituted when launching the command. When upper-case letters (e.g. %F, %D, %N) are used, the action will be applicable even if more than one item is selected. Else the action will only be applicable if exactly one item is selected.</property>
<property name="hexpand">True</property>
<property name="activates-default">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkButton" id="command_button">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="receives-default">True</property>
<property name="tooltip-text" translatable="yes">Browse the file system to select an application to use for this action.</property>
<signal name="clicked" handler="thunar_uca_editor_command_clicked" swapped="yes"/>
<child>
<object class="GtkImage" id="image1">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="icon-name">document-open</property>
</object>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<style>
<class name="linked"/>
</style>
</object>
<packing>
<property name="left-attach">1</property>
<property name="top-attach">3</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="shortcut_label">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="label" translatable="yes">Keyboard _Shortcut:</property>
<property name="use-underline">True</property>
<property name="xalign">0</property>
</object>
<packing>
<property name="left-attach">0</property>
<property name="top-attach">4</property>
</packing>
</child>
<child>
<object class="GtkBox" id="box2">
<property name="visible">True</property>
<property name="can-focus">False</property>
<child>
<object class="GtkButton" id="shortcut_button">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="receives-default">False</property>
<property name="tooltip-text" translatable="yes">The keyboard shortcut for the action.</property>
<property name="hexpand">True</property>
<signal name="clicked" handler="thunar_uca_editor_shortcut_clicked" swapped="yes"/>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkButton" id="shortcut_clear_button">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="receives-default">True</property>
<property name="tooltip-text" translatable="yes">Clear the keyboard shortcut for this action.</property>
<signal name="clicked" handler="thunar_uca_editor_shortcut_clear_clicked" swapped="yes"/>
<child>
<object class="GtkImage" id="image2">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="icon-name">edit-clear</property>
</object>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<style>
<class name="linked"/>
</style>
</object>
<packing>
<property name="left-attach">1</property>
<property name="top-attach">4</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="icon_label">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="label" translatable="yes">_Icon:</property>
<property name="use-underline">True</property>
<property name="mnemonic-widget">icon_button</property>
<property name="ellipsize">end</property>
<property name="xalign">0</property>
<accessibility>
<relation type="label-for" target="icon_button"/>
</accessibility>
</object>
<packing>
<property name="left-attach">0</property>
<property name="top-attach">5</property>
</packing>
</child>
<child>
<object class="GtkButton" id="icon_button">
<property name="label" translatable="yes">No icon</property>
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="receives-default">True</property>
<property name="tooltip-text" translatable="yes">Click this button to select an icon file that will be displayed in the context menu in addition to the action name chosen above.</property>
<property name="halign">start</property>
<signal name="clicked" handler="thunar_uca_editor_icon_clicked" swapped="yes"/>
</object>
<packing>
<property name="left-attach">1</property>
<property name="top-attach">5</property>
</packing>
</child>
<child>
<object class="GtkCheckButton" id="sn_button">
<property name="label" translatable="yes">Use Startup Notification</property>
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="receives-default">False</property>
<property name="tooltip-text" translatable="yes">Enable this option if you want a waiting cursor to be shown while the action is launched. This is also highly recommended if you have focus stealing prevention enabled in your window manager.</property>
<property name="draw-indicator">True</property>
</object>
<packing>
<property name="left-attach">0</property>
<property name="top-attach">6</property>
<property name="width">2</property>
</packing>
</child>
<child>
<object class="GtkBox" id="box3">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="vexpand">True</property>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
</object>
<packing>
<property name="left-attach">0</property>
<property name="top-attach">7</property>
<property name="width">2</property>
</packing>
</child>
<child>
<!-- n-columns=3 n-rows=7 -->
<object class="GtkGrid" id="grid2">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="valign">start</property>
<property name="row-spacing">3</property>
<property name="column-spacing">6</property>
<property name="margin-top">12</property>
<child>
<object class="GtkImage" id="image3">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="pixel-size">32</property>
<property name="icon-name">dialog-information</property>
<property name="icon_size">5</property>
</object>
<packing>
<property name="left-attach">0</property>
<property name="top-attach">0</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label4">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="label" translatable="yes">The following command parameters will be substituted when launching the action:</property>
<property name="wrap">True</property>
<property name="xalign">0</property>
</object>
<packing>
<property name="left-attach">1</property>
<property name="top-attach">0</property>
<property name="width">2</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label5">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="label">%n</property>
<property name="xalign">0</property>
<attributes>
<attribute name="weight" value="bold"/>
<attribute name="scale" value="0.80000000000000004"/>
</attributes>
</object>
<packing>
<property name="left-attach">1</property>
<property name="top-attach">1</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label6">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="label">%N</property>
<property name="xalign">0</property>
<attributes>
<attribute name="weight" value="bold"/>
<attribute name="scale" value="0.80000000000000004"/>
</attributes>
</object>
<packing>
<property name="left-attach">1</property>
<property name="top-attach">2</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label7">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="label">%d</property>
<property name="xalign">0</property>
<attributes>
<attribute name="weight" value="bold"/>
<attribute name="scale" value="0.80000000000000004"/>
</attributes>
</object>
<packing>
<property name="left-attach">1</property>
<property name="top-attach">3</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label8">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="label">%D</property>
<property name="xalign">0</property>
<attributes>
<attribute name="weight" value="bold"/>
<attribute name="scale" value="0.80000000000000004"/>
</attributes>
</object>
<packing>
<property name="left-attach">1</property>
<property name="top-attach">4</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label9">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="label">%f</property>
<property name="xalign">0</property>
<attributes>
<attribute name="weight" value="bold"/>
<attribute name="scale" value="0.80000000000000004"/>
</attributes>
</object>
<packing>
<property name="left-attach">1</property>
<property name="top-attach">5</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label10">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="label">%F</property>
<property name="xalign">0</property>
<attributes>
<attribute name="weight" value="bold"/>
<attribute name="scale" value="0.80000000000000004"/>
</attributes>
</object>
<packing>
<property name="left-attach">1</property>
<property name="top-attach">6</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label11">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="label" translatable="yes">file name of the selected file</property>
<property name="xalign">0</property>
<property name="hexpand">True</property>
<attributes>
<attribute name="scale" value="0.80000000000000004"/>
</attributes>
</object>
<packing>
<property name="left-attach">2</property>
<property name="top-attach">1</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label12">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="label" translatable="yes">file names of all selected files</property>
<property name="xalign">0</property>
<attributes>
<attribute name="scale" value="0.80000000000000004"/>
</attributes>
</object>
<packing>
<property name="left-attach">2</property>
<property name="top-attach">2</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label13">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="label" translatable="yes">path of the selected file's directory</property>
<property name="xalign">0</property>
<attributes>
<attribute name="scale" value="0.80000000000000004"/>
</attributes>
</object>
<packing>
<property name="left-attach">2</property>
<property name="top-attach">3</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label14">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="label" translatable="yes">paths of all selected files' directories</property>
<property name="xalign">0</property>
<attributes>
<attribute name="scale" value="0.80000000000000004"/>
</attributes>
</object>
<packing>
<property name="left-attach">2</property>
<property name="top-attach">4</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label15">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="label" translatable="yes">path of the selected file including the file name</property>
<property name="xalign">0</property>
<attributes>
<attribute name="scale" value="0.80000000000000004"/>
</attributes>
</object>
<packing>
<property name="left-attach">2</property>
<property name="top-attach">5</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label16">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="label" translatable="yes">paths of all selected files including the file names</property>
<property name="xalign">0</property>
<attributes>
<attribute name="scale" value="0.80000000000000004"/>
</attributes>
</object>
<packing>
<property name="left-attach">2</property>
<property name="top-attach">6</property>
</packing>
</child>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
</object>
<packing>
<property name="left-attach">0</property>
<property name="top-attach">7</property>
<property name="width">2</property>
</packing>
</child>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
</object>
</child>
<child type="tab">
<object class="GtkLabel" id="label1">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="label" translatable="yes">Basic</property>
</object>
<packing>
<property name="tab-fill">False</property>
</packing>
</child>
<child>
<!-- n-columns=3 n-rows=6 -->
<object class="GtkGrid" id="grid3">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="border-width">12</property>
<property name="row-spacing">6</property>
<property name="column-spacing">12</property>
<child>
<object class="GtkLabel" id="label3">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="label" translatable="yes">_File Pattern:</property>
<property name="use-underline">True</property>
<property name="mnemonic-widget">patterns_entry</property>
<property name="xalign">0</property>
<accessibility>
<relation type="label-for" target="patterns_entry"/>
</accessibility>
</object>
<packing>
<property name="left-attach">0</property>
<property name="top-attach">0</property>
</packing>
</child>
<child>
<object class="GtkEntry" id="patterns_entry">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="tooltip-text" translatable="yes">Enter a list of patterns that will be used to determine whether this action should be displayed for a selected file. If you specify more than one pattern here, the list items must be separated with semicolons (e.g. *.txt;*.doc).</property>
<property name="hexpand">True</property>
<property name="activates-default">True</property>
<property name="text" translatable="yes">*</property>
</object>
<packing>
<property name="left-attach">1</property>
<property name="top-attach">0</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label17">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="label" translatable="yes">_Range (min-max):</property>
<property name="use-underline">True</property>
<property name="mnemonic-widget">range_entry</property>
<property name="xalign">0</property>
<accessibility>
<relation type="label-for" target="range_entry"/>
</accessibility>
</object>
<packing>
<property name="left-attach">0</property>
<property name="top-attach">1</property>
</packing>
</child>
<child>
<object class="GtkEntry" id="range_entry">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="tooltip-text" translatable="yes">If the command should operate on say two or three files, enter the numerical range 2-3. If there are no limits, this string should be empty.</property>
<property name="hexpand">True</property>
<property name="activates-default">True</property>
<property name="text" translatable="yes">*</property>
</object>
<packing>
<property name="left-attach">1</property>
<property name="top-attach">1</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label18">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="label" translatable="yes">Appears if selection contains:</property>
<property name="ellipsize">middle</property>
<property name="xalign">0</property>
</object>
<packing>
<property name="left-attach">0</property>
<property name="top-attach">2</property>
<property name="width">2</property>
</packing>
</child>
<child>
<!-- n-columns=3 n-rows=3 -->
<object class="GtkGrid" id="grid4">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="margin-start">12</property>
<property name="row-spacing">6</property>
<property name="column-spacing">48</property>
<child>
<object class="GtkCheckButton" id="directories_button">
<property name="label" translatable="yes">_Directories **</property>
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="receives-default">False</property>
<property name="hexpand">False</property>
<property name="use-underline">True</property>
<property name="draw-indicator">True</property>
</object>
<packing>
<property name="left-attach">0</property>
<property name="top-attach">0</property>
</packing>
</child>
<child>
<object class="GtkCheckButton" id="audio_files_button">
<property name="label" translatable="yes">_Audio Files</property>
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="receives-default">False</property>
<property name="use-underline">True</property>
<property name="draw-indicator">True</property>
</object>
<packing>
<property name="left-attach">0</property>
<property name="top-attach">1</property>
</packing>
</child>
<child>
<object class="GtkCheckButton" id="image_files_button">
<property name="label" translatable="yes">_Image Files</property>
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="receives-default">False</property>
<property name="use-underline">True</property>
<property name="image-position">top</property>
<property name="draw-indicator">True</property>
</object>
<packing>
<property name="left-attach">0</property>
<property name="top-attach">2</property>
</packing>
</child>
<child>
<object class="GtkCheckButton" id="text_files_button">
<property name="label" translatable="yes">_Text Files</property>
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="receives-default">False</property>
<property name="use-underline">True</property>
<property name="draw-indicator">True</property>
</object>
<packing>
<property name="left-attach">1</property>
<property name="top-attach">0</property>
</packing>
</child>
<child>
<object class="GtkCheckButton" id="video_files_button">
<property name="label" translatable="yes">_Video Files</property>
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="receives-default">False</property>
<property name="use-underline">True</property>
<property name="draw-indicator">True</property>
</object>
<packing>
<property name="left-attach">1</property>
<property name="top-attach">1</property>
</packing>
</child>
<child>
<object class="GtkCheckButton" id="other_files_button">
<property name="label" translatable="yes">_Other Files</property>
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="receives-default">False</property>
<property name="use-underline">True</property>
<property name="draw-indicator">True</property>
</object>
<packing>
<property name="left-attach">1</property>
<property name="top-attach">2</property>
</packing>
</child>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
</object>
<packing>
<property name="left-attach">0</property>
<property name="top-attach">3</property>
<property name="width">2</property>
</packing>
</child>
<child>
<object class="GtkBox" id="box4">
<property name="visible">True</property>
<property name="can-focus">False</property>
<child>
<object class="GtkImage" id="image4">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="halign">start</property>
<property name="valign">start</property>
<property name="margin-right">6</property>
<property name="pixel-size">32</property>
<property name="icon-name">dialog-information</property>
<property name="icon_size">5</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label19">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="label" translatable="yes">This page lists the conditions under which the action will appear in the file managers context menus. The file patterns are specified as a list of simple file patterns separated by semicolons (e.g. *.txt;*.doc). For an action to appear in the context menu of a file or folder, at least one of these patterns must match the name of the file or folder. Additionally, you can specify that the action should only appear for certain kinds of files.
** The custom action will be eligible to appear in the toolbar.</property>
<property name="wrap">True</property>
<property name="xalign">0</property>
<property name="yalign">0</property>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="left-attach">0</property>
<property name="top-attach">5</property>
<property name="width">2</property>
</packing>
</child>
<child>
<object class="GtkBox" id="box5">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="vexpand">True</property>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
</object>
<packing>
<property name="left-attach">0</property>
<property name="top-attach">4</property>
<property name="width">2</property>
</packing>
</child>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
</object>
<packing>
<property name="position">1</property>
</packing>
</child>
<child type="tab">
<object class="GtkLabel" id="label2">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="label" translatable="yes">Appearance Conditions</property>
</object>
<packing>
<property name="position">1</property>
<property name="tab-fill">False</property>
</packing>
</child>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
</child>
</template>
</interface>

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,129 @@
/* vi:set et ai sw=2 sts=2 ts=2: */
/*-
* Copyright (c) 2005 Benedikt Meurer <benny@xfce.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#ifndef __THUNAR_UCA_MODEL_H__
#define __THUNAR_UCA_MODEL_H__
#include <thunarx/thunarx.h>
G_BEGIN_DECLS;
typedef struct _ThunarUcaModelClass ThunarUcaModelClass;
typedef struct _ThunarUcaModel ThunarUcaModel;
#define THUNAR_UCA_TYPE_MODEL (thunar_uca_model_get_type ())
#define THUNAR_UCA_MODEL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), THUNAR_UCA_TYPE_MODEL, ThunarUcaModel))
#define THUNAR_UCA_MODEL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), THUNAR_UCA_TYPE_MODEL, ThunarUcaModelClass))
#define THUNAR_UCA_IS_MODEL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), THUNAR_UCA_TYPE_MODEL))
#define THUNAR_UCA_IS_MODEL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), THUNAR_UCA_TYPE_MODEL))
#define THUNAR_UCA_MODEL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), THUNAR_UCA_TYPE_MODEL, ThunarUcaModelClass))
typedef enum
{
THUNAR_UCA_MODEL_COLUMN_NAME,
THUNAR_UCA_MODEL_COLUMN_SUB_MENU,
THUNAR_UCA_MODEL_COLUMN_DESCRIPTION,
THUNAR_UCA_MODEL_COLUMN_GICON,
THUNAR_UCA_MODEL_COLUMN_ICON_NAME,
THUNAR_UCA_MODEL_COLUMN_UNIQUE_ID,
THUNAR_UCA_MODEL_COLUMN_COMMAND,
THUNAR_UCA_MODEL_COLUMN_STARTUP_NOTIFY,
THUNAR_UCA_MODEL_COLUMN_PATTERNS,
THUNAR_UCA_MODEL_COLUMN_RANGE,
THUNAR_UCA_MODEL_COLUMN_TYPES,
THUNAR_UCA_MODEL_COLUMN_STOCK_LABEL,
THUNAR_UCA_MODEL_N_COLUMNS,
} ThunarUcaModelColumn;
/**
* ThunarUcaTypes:
* @THUNAR_UCA_TYPE_DIRECTORIES : directories.
* @THUNAR_UCA_TYPE_AUDIO_FILES : audio files.
* @THUNAR_UCA_TYPE_IMAGE_FILES : image files.
* @THUNAR_UCA_TYPE_OTHER_FILES : other files.
* @THUNAR_UCA_TYPE_TEXT_FILES : text files.
* @THUNAR_UCA_TYPE_VIDEO_FILES : video files.
**/
typedef enum /*< flags >*/
{
THUNAR_UCA_TYPE_DIRECTORIES = 1 << 0,
THUNAR_UCA_TYPE_AUDIO_FILES = 1 << 1,
THUNAR_UCA_TYPE_IMAGE_FILES = 1 << 2,
THUNAR_UCA_TYPE_OTHER_FILES = 1 << 3,
THUNAR_UCA_TYPE_TEXT_FILES = 1 << 4,
THUNAR_UCA_TYPE_VIDEO_FILES = 1 << 5,
} ThunarUcaTypes;
GType
thunar_uca_model_get_type (void) G_GNUC_CONST;
void
thunar_uca_model_register_type (ThunarxProviderPlugin *plugin);
ThunarUcaModel *
thunar_uca_model_get_default (void);
GList *
thunar_uca_model_match (ThunarUcaModel *uca_model,
GList *file_infos);
void
thunar_uca_model_append (ThunarUcaModel *uca_model,
GtkTreeIter *iter);
void
thunar_uca_model_exchange (ThunarUcaModel *uca_model,
GtkTreeIter *iter_a,
GtkTreeIter *iter_b);
void
thunar_uca_model_remove (ThunarUcaModel *uca_model,
GtkTreeIter *iter);
void
thunar_uca_model_update (ThunarUcaModel *uca_model,
GtkTreeIter *iter,
const gchar *name,
const gchar *submenu,
const gchar *unique_id,
const gchar *description,
const gchar *icon,
const gchar *command,
gboolean startup_notify,
const gchar *patterns,
const gchar *range,
ThunarUcaTypes types,
guint accel_key,
GdkModifierType accel_mods);
gboolean
thunar_uca_model_save (ThunarUcaModel *uca_model,
GError **error);
gboolean
thunar_uca_model_parse_argv (ThunarUcaModel *uca_model,
GtkTreeIter *iter,
GList *file_infos,
gint *argcp,
gchar ***argvp,
GError **error);
G_END_DECLS;
#endif /* !__THUNAR_UCA_MODEL_H__ */

View file

@ -0,0 +1,91 @@
/* vi:set et ai sw=2 sts=2 ts=2: */
/*-
* Copyright (c) 2005 Benedikt Meurer <benny@xfce.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; 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
#include <thunar-uca/thunar-uca-chooser.h>
#include <thunar-uca/thunar-uca-editor.h>
#include <thunar-uca/thunar-uca-model.h>
#include <thunar-uca/thunar-uca-provider.h>
G_MODULE_EXPORT void
thunar_extension_initialize (ThunarxProviderPlugin *plugin);
G_MODULE_EXPORT void
thunar_extension_shutdown (void);
G_MODULE_EXPORT void
thunar_extension_list_types (const GType **types,
gint *n_types);
static GType type_list[1];
G_MODULE_EXPORT void
thunar_extension_initialize (ThunarxProviderPlugin *plugin)
{
const gchar *mismatch;
/* verify that the thunarx versions are compatible */
mismatch = thunarx_check_version (THUNARX_MAJOR_VERSION, THUNARX_MINOR_VERSION, THUNARX_MICRO_VERSION);
if (G_UNLIKELY (mismatch != NULL))
{
g_warning ("Version mismatch: %s", mismatch);
return;
}
#ifdef G_ENABLE_DEBUG
g_message ("Initializing ThunarUca extension");
#endif
/* register the types provided by this plugin */
thunar_uca_chooser_register_type (plugin);
thunar_uca_editor_register_type (plugin);
thunar_uca_model_register_type (plugin);
thunar_uca_provider_register_type (plugin);
/* setup the plugin provider type list */
type_list[0] = THUNAR_UCA_TYPE_PROVIDER;
}
G_MODULE_EXPORT void
thunar_extension_shutdown (void)
{
#ifdef G_ENABLE_DEBUG
g_message ("Shutting down ThunarUca extension");
#endif
}
G_MODULE_EXPORT void
thunar_extension_list_types (const GType **types,
gint *n_types)
{
*types = type_list;
*n_types = G_N_ELEMENTS (type_list);
}

View file

@ -0,0 +1,49 @@
/* vi:set et ai sw=2 sts=2 ts=2: */
/*-
* Copyright (c) 2006 Benedikt Meurer <benny@xfce.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; 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_LIBINTL_H
#include <libintl.h>
#endif
#ifdef HAVE_LOCALE_H
#include <locale.h>
#endif
#include <exo/exo.h>
#include <thunar-uca/thunar-uca-private.h>
/**
* thunar_uca_i18n_init:
*
* Sets up i18n support for the thunar-uca plugin.
**/
void
thunar_uca_i18n_init (void)
{
bindtextdomain (GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR);
#ifdef HAVE_BIND_TEXTDOMAIN_CODESET
bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
#endif
}

View file

@ -0,0 +1,33 @@
/* vi:set et ai sw=2 sts=2 ts=2: */
/*-
* Copyright (c) 2006 Benedikt Meurer <benny@xfce.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#ifndef __THUNAR_UCA_PRIVATE_H__
#define __THUNAR_UCA_PRIVATE_H__
#include <glib.h>
G_BEGIN_DECLS;
void
thunar_uca_i18n_init (void);
G_END_DECLS;
#endif /* !__THUNAR_UCA_PRIVATE_H__ */

View file

@ -0,0 +1,604 @@
/* vi:set et ai sw=2 sts=2 ts=2: */
/*-
* Copyright (c) 2005-2006 Benedikt Meurer <benny@xfce.org>
* Copyright (c) 2009 Jannis Pohlmann <jannis@xfce.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; 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
#include <gio/gio.h>
#include <libxfce4ui/libxfce4ui.h>
#include <libxfce4util/libxfce4util.h>
#include <thunar-uca/thunar-uca-chooser.h>
#include <thunar-uca/thunar-uca-context.h>
#include <thunar-uca/thunar-uca-model.h>
#include <thunar-uca/thunar-uca-private.h>
#include <thunar-uca/thunar-uca-provider.h>
static void
thunar_uca_provider_menu_provider_init (ThunarxMenuProviderIface *iface);
static void
thunar_uca_provider_preferences_provider_init (ThunarxPreferencesProviderIface *iface);
static void
thunar_uca_provider_finalize (GObject *object);
static GList *
thunar_uca_provider_get_menu_items (ThunarxPreferencesProvider *preferences_provider,
GtkWidget *window);
static GList *
thunar_uca_provider_get_file_menu_items (ThunarxMenuProvider *menu_provider,
GtkWidget *window,
GList *files);
static GList *
thunar_uca_provider_get_folder_menu_items (ThunarxMenuProvider *menu_provider,
GtkWidget *window,
ThunarxFileInfo *folder);
static void
thunar_uca_provider_activated (ThunarUcaProvider *uca_provider,
ThunarxMenuItem *item);
static void
thunar_uca_provider_child_watch (ThunarUcaProvider *uca_provider,
gint exit_status);
static void
thunar_uca_provider_child_watch_destroy (gpointer user_data,
GClosure *closure);
struct _ThunarUcaProviderClass
{
GObjectClass __parent__;
};
struct _ThunarUcaProvider
{
GObject __parent__;
ThunarUcaModel *model;
gint last_action_id; /* used to generate unique action names */
/* child watch support for the last spawned child process
* to be able to refresh the folder contents after the
* child process has terminated.
*/
gchar *child_watch_path;
GClosure *child_watch;
};
static GQuark thunar_uca_context_quark;
static GQuark thunar_uca_folder_quark;
static GQuark thunar_uca_row_quark;
THUNARX_DEFINE_TYPE_WITH_CODE (ThunarUcaProvider,
thunar_uca_provider,
G_TYPE_OBJECT,
THUNARX_IMPLEMENT_INTERFACE (THUNARX_TYPE_MENU_PROVIDER,
thunar_uca_provider_menu_provider_init)
THUNARX_IMPLEMENT_INTERFACE (THUNARX_TYPE_PREFERENCES_PROVIDER,
thunar_uca_provider_preferences_provider_init));
static void
thunar_uca_provider_class_init (ThunarUcaProviderClass *klass)
{
GObjectClass *gobject_class;
/* setup the "thunar-uca-context", "thunar-uca-folder" and "thunar-uca-row" quarks */
thunar_uca_context_quark = g_quark_from_string ("thunar-uca-context");
thunar_uca_folder_quark = g_quark_from_string ("thunar-uca-folder");
thunar_uca_row_quark = g_quark_from_string ("thunar-uca-row");
gobject_class = G_OBJECT_CLASS (klass);
gobject_class->finalize = thunar_uca_provider_finalize;
}
static void
thunar_uca_provider_menu_provider_init (ThunarxMenuProviderIface *iface)
{
iface->get_file_menu_items = thunar_uca_provider_get_file_menu_items;
iface->get_folder_menu_items = thunar_uca_provider_get_folder_menu_items;
}
static void
thunar_uca_provider_preferences_provider_init (ThunarxPreferencesProviderIface *iface)
{
iface->get_menu_items = thunar_uca_provider_get_menu_items;
}
static void
thunar_uca_provider_init (ThunarUcaProvider *uca_provider)
{
/* setup the i18n support first */
thunar_uca_i18n_init ();
/* grab a reference on the default model */
uca_provider->model = thunar_uca_model_get_default ();
}
static void
thunar_uca_provider_finalize (GObject *object)
{
ThunarUcaProvider *uca_provider = THUNAR_UCA_PROVIDER (object);
/* give up maintaince of any pending child watch */
thunar_uca_provider_child_watch_destroy (uca_provider, NULL);
/* drop our reference on the model */
g_object_unref (G_OBJECT (uca_provider->model));
(*G_OBJECT_CLASS (thunar_uca_provider_parent_class)->finalize) (object);
}
static void
manage_menu_items (GtkWindow *window)
{
GtkWidget *dialog;
gboolean use_header_bar = FALSE;
g_object_get (gtk_settings_get_for_screen (gtk_widget_get_screen (GTK_WIDGET (window))),
"gtk-dialogs-use-header", &use_header_bar, NULL);
dialog = g_object_new (THUNAR_UCA_TYPE_CHOOSER, "use-header-bar", use_header_bar, NULL);
gtk_window_set_transient_for (GTK_WINDOW (dialog), window);
gtk_widget_show (dialog);
}
static GList *
thunar_uca_provider_get_menu_items (ThunarxPreferencesProvider *preferences_provider,
GtkWidget *window)
{
ThunarxMenuItem *item;
GClosure *closure;
item = thunarx_menu_item_new ("ThunarUca::manage-actions", _("Confi_gure custom actions..."), _("Setup custom actions that will appear in the file managers context menus"), NULL);
closure = g_cclosure_new_object_swap (G_CALLBACK (manage_menu_items), G_OBJECT (window));
g_signal_connect_closure (G_OBJECT (item), "activate", closure, TRUE);
return g_list_prepend (NULL, item);
}
/* returned menu must be freed with g_object_unref() */
static ThunarxMenu *
find_submenu_by_name (gchar *name, GList *items)
{
GList *lp;
GList *thunarx_menu_items;
for (lp = g_list_first (items); lp != NULL; lp = lp->next)
{
gchar *item_name = NULL;
ThunarxMenu *item_menu = NULL;
g_object_get (G_OBJECT (lp->data), "name", &item_name, "menu", &item_menu, NULL);
if (item_menu != NULL)
{
/* This menu is the correct menu */
if (g_strcmp0 (item_name, name) == 0)
{
g_free (item_name);
return item_menu;
}
/* Some other menu found .. lets check recursively if the menu we search for is inside */
thunarx_menu_items = thunarx_menu_get_items (item_menu);
g_object_unref (item_menu);
if (thunarx_menu_items != NULL)
{
ThunarxMenu *submenu = find_submenu_by_name (name, thunarx_menu_items);
if (submenu != NULL)
{
g_free (item_name);
return submenu;
}
thunarx_menu_item_list_free (thunarx_menu_items);
}
}
g_free (item_name);
}
/* not found */
return NULL;
}
static GList *
thunar_uca_provider_get_file_menu_items (ThunarxMenuProvider *menu_provider,
GtkWidget *window,
GList *files)
{
GtkTreeRowReference *row;
ThunarUcaProvider *uca_provider = THUNAR_UCA_PROVIDER (menu_provider);
ThunarUcaContext *uca_context = NULL;
GtkTreeIter iter;
ThunarxMenuItem *menu_item;
ThunarxMenuItem *item;
GList *items = NULL;
GList *paths;
GList *lp;
ThunarxMenu *submenu;
ThunarxMenu *parent_menu = NULL;
paths = thunar_uca_model_match (uca_provider->model, files);
for (lp = g_list_last (paths); lp != NULL; lp = lp->prev)
{
gchar *unique_id = NULL;
gchar *name = NULL;
gchar *sub_menu_string = NULL;
gchar **sub_menus_as_array = NULL;
gchar *label = NULL;
gchar *tooltip = NULL;
gchar *icon_name = NULL;
GIcon *gicon = NULL;
/* try to lookup the tree iter for the specified tree path */
if (gtk_tree_model_get_iter (GTK_TREE_MODEL (uca_provider->model), &iter, lp->data))
{
/* determine the label, tooltip and stock-id for the item */
gtk_tree_model_get (GTK_TREE_MODEL (uca_provider->model), &iter,
THUNAR_UCA_MODEL_COLUMN_NAME, &label,
THUNAR_UCA_MODEL_COLUMN_SUB_MENU, &sub_menu_string,
THUNAR_UCA_MODEL_COLUMN_GICON, &gicon,
THUNAR_UCA_MODEL_COLUMN_DESCRIPTION, &tooltip,
THUNAR_UCA_MODEL_COLUMN_UNIQUE_ID, &unique_id,
-1);
/* generate a unique action name */
name = g_strdup_printf ("uca-action-%s", unique_id);
if (gicon != NULL)
icon_name = g_icon_to_string (gicon);
/* Search or build the parent submenus, if required */
parent_menu = NULL;
sub_menus_as_array = g_strsplit (sub_menu_string, "/", -1);
for (int i = 0; sub_menus_as_array[i] != NULL; i++)
{
/* get the submenu path up to the iterator */
gchar *sub_menu_path = g_strdup (sub_menus_as_array[0]);
for (int j = 1; j <= i; j++)
sub_menu_path = g_strconcat (sub_menu_path, "/", sub_menus_as_array[j], NULL);
/* Check if the full path already exists */
submenu = find_submenu_by_name (sub_menu_path, items);
if (submenu != NULL)
{
/* This submenu already exists, we can just use it as new parent */
parent_menu = submenu;
/* no need to keep the extra reference on it */
g_object_unref (submenu);
}
else
{
/* Not found, we create a new submenu */
menu_item = thunarx_menu_item_new (sub_menu_path, sub_menus_as_array[i], "", "folder");
/* Only add base-submenus to the returned list */
if (parent_menu == NULL)
items = g_list_prepend (items, menu_item);
else
thunarx_menu_prepend_item (parent_menu, menu_item);
/* This sublevel becomes the new parent */
parent_menu = thunarx_menu_new ();
thunarx_menu_item_set_menu (menu_item, parent_menu);
}
g_free (sub_menu_path);
}
g_strfreev (sub_menus_as_array);
/* create the new menu item with the given parameters */
item = thunarx_menu_item_new (name, label, tooltip, icon_name);
/* grab a tree row reference on the given path */
row = gtk_tree_row_reference_new (GTK_TREE_MODEL (uca_provider->model), lp->data);
g_object_set_qdata_full (G_OBJECT (item), thunar_uca_row_quark, row,
(GDestroyNotify) gtk_tree_row_reference_free);
/* allocate a new context on-demand */
if (G_LIKELY (uca_context == NULL))
uca_context = thunar_uca_context_new (window, files);
else
uca_context = thunar_uca_context_ref (uca_context);
g_object_set_qdata_full (G_OBJECT (item), thunar_uca_context_quark, uca_context, (GDestroyNotify) thunar_uca_context_unref);
/* connect the "activate" signal */
g_signal_connect_data (G_OBJECT (item), "activate", G_CALLBACK (thunar_uca_provider_activated),
g_object_ref (G_OBJECT (uca_provider)),
(GClosureNotify) (void (*) (void)) g_object_unref,
G_CONNECT_SWAPPED);
/* set the action path */
g_object_set_data_full (G_OBJECT (item), "action_path",
g_strconcat ("<Actions>/ThunarActions/", name, NULL), g_free);
/* add only base menu items to the return list */
if (parent_menu == NULL)
items = g_list_prepend (items, item);
else
thunarx_menu_prepend_item (parent_menu, item);
/* cleanup */
g_free (tooltip);
g_free (label);
g_free (name);
g_free (sub_menu_string);
g_free (icon_name);
g_free (unique_id);
if (gicon != NULL)
g_object_unref (G_OBJECT (gicon));
}
/* release the tree path */
gtk_tree_path_free (lp->data);
}
g_list_free (paths);
return items;
}
static GList *
thunar_uca_provider_get_folder_menu_items (ThunarxMenuProvider *menu_provider,
GtkWidget *window,
ThunarxFileInfo *folder)
{
GList *items;
GList files;
GList *lp;
/* fake a file list... */
files.data = folder;
files.next = NULL;
files.prev = NULL;
/* ...and use the get_file_menu_items() method */
items = thunarx_menu_provider_get_file_menu_items (menu_provider, window, &files);
/* mark the menu items, so we can properly detect the working directory */
for (lp = items; lp != NULL; lp = lp->next)
g_object_set_qdata (G_OBJECT (lp->data), thunar_uca_folder_quark, GUINT_TO_POINTER (TRUE));
return items;
}
static void
thunar_uca_provider_activated (ThunarUcaProvider *uca_provider,
ThunarxMenuItem *item)
{
GtkTreeRowReference *row;
ThunarUcaContext *uca_context;
GtkTreePath *path;
GtkTreeIter iter;
GtkWidget *dialog;
GtkWidget *window;
gboolean succeed;
GError *error = NULL;
GList *files;
gchar **argv;
gchar *working_directory = NULL;
gchar *filename;
gchar *label;
GFile *location;
gint argc;
gchar *icon_name = NULL;
gboolean startup_notify;
GClosure *child_watch;
g_return_if_fail (THUNAR_UCA_IS_PROVIDER (uca_provider));
g_return_if_fail (THUNARX_IS_MENU_ITEM (item));
/* check if the row reference is still valid */
row = g_object_get_qdata (G_OBJECT (item), thunar_uca_row_quark);
if (G_UNLIKELY (!gtk_tree_row_reference_valid (row)))
return;
/* determine the iterator for the item */
path = gtk_tree_row_reference_get_path (row);
gtk_tree_model_get_iter (GTK_TREE_MODEL (uca_provider->model), &iter, path);
gtk_tree_path_free (path);
/* determine the files and the window for the menu item */
uca_context = g_object_get_qdata (G_OBJECT (item), thunar_uca_context_quark);
window = thunar_uca_context_get_window (uca_context);
files = thunar_uca_context_get_files (uca_context);
/* determine the argc/argv for the item */
succeed = thunar_uca_model_parse_argv (uca_provider->model, &iter, files, &argc, &argv, &error);
if (G_LIKELY (succeed))
{
/* get the icon name and whether startup notification is active */
gtk_tree_model_get (GTK_TREE_MODEL (uca_provider->model), &iter,
THUNAR_UCA_MODEL_COLUMN_ICON_NAME, &icon_name,
THUNAR_UCA_MODEL_COLUMN_STARTUP_NOTIFY, &startup_notify,
-1);
/* determine the working from the first file */
if (G_LIKELY (files != NULL))
{
/* determine the filename of the first selected file */
location = thunarx_file_info_get_location (files->data);
filename = g_file_get_path (location);
if (G_LIKELY (filename != NULL))
{
/* if this is a folder menu item, we just use the filename as working directory */
if (g_object_get_qdata (G_OBJECT (item), thunar_uca_folder_quark) != NULL)
{
working_directory = filename;
filename = NULL;
}
else
{
working_directory = g_path_get_dirname (filename);
}
}
g_free (filename);
g_object_unref (location);
}
/* build closre for child watch */
child_watch = g_cclosure_new_swap (G_CALLBACK (thunar_uca_provider_child_watch),
uca_provider, thunar_uca_provider_child_watch_destroy);
g_closure_ref (child_watch);
g_closure_sink (child_watch);
/* spawn the command on the window's screen */
succeed = xfce_spawn_on_screen_with_child_watch (gtk_widget_get_screen (GTK_WIDGET (window)),
working_directory, argv, NULL,
G_SPAWN_SEARCH_PATH,
startup_notify,
gtk_get_current_event_time (),
icon_name,
child_watch,
&error);
/* check if we succeed */
if (G_LIKELY (succeed))
{
/* release existing child watch */
thunar_uca_provider_child_watch_destroy (uca_provider, NULL);
/* set new closure */
uca_provider->child_watch = child_watch;
/* take over ownership of the working directory as child watch path */
uca_provider->child_watch_path = working_directory;
working_directory = NULL;
}
else
{
/* spawn failed, release watch */
g_closure_unref (child_watch);
}
/* cleanup */
g_free (working_directory);
g_strfreev (argv);
g_free (icon_name);
}
/* present error message to the user */
if (G_UNLIKELY (!succeed))
{
g_object_get (G_OBJECT (item), "label", &label, NULL);
dialog = gtk_message_dialog_new ((GtkWindow *) window,
GTK_DIALOG_DESTROY_WITH_PARENT
| GTK_DIALOG_MODAL,
GTK_MESSAGE_ERROR,
GTK_BUTTONS_CLOSE,
_("Failed to launch action \"%s\"."), label);
gtk_window_set_title (GTK_WINDOW (dialog), _("Error"));
gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), "%s.", error->message);
gtk_dialog_run (GTK_DIALOG (dialog));
gtk_widget_destroy (dialog);
g_error_free (error);
g_free (label);
}
}
static void
thunar_uca_provider_child_watch (ThunarUcaProvider *uca_provider,
gint exit_status)
{
GFileMonitor *monitor;
GFile *file;
g_return_if_fail (THUNAR_UCA_IS_PROVIDER (uca_provider));
/* verify that we still have a valid child_watch_path */
if (G_LIKELY (uca_provider->child_watch_path != NULL))
{
/* determine the corresponding file */
file = g_file_new_for_path (uca_provider->child_watch_path);
/* schedule a changed notification on the path */
monitor = g_file_monitor (file, G_FILE_MONITOR_NONE, NULL, NULL);
if (monitor != NULL)
{
g_file_monitor_emit_event (monitor, file, file, G_FILE_MONITOR_EVENT_CHANGED);
g_object_unref (monitor);
}
/* release the file */
g_object_unref (file);
}
thunar_uca_provider_child_watch_destroy (uca_provider, NULL);
}
static void
thunar_uca_provider_child_watch_destroy (gpointer user_data,
GClosure *closure)
{
ThunarUcaProvider *uca_provider = THUNAR_UCA_PROVIDER (user_data);
GClosure *child_watch;
/* leave if the closure is not the one we're watching */
if (uca_provider->child_watch == closure
|| closure == NULL)
{
/* reset child watch and path */
if (G_UNLIKELY (uca_provider->child_watch != NULL))
{
child_watch = uca_provider->child_watch;
uca_provider->child_watch = NULL;
g_closure_invalidate (child_watch);
g_closure_unref (child_watch);
}
g_free (uca_provider->child_watch_path);
uca_provider->child_watch_path = NULL;
}
}

View file

@ -0,0 +1,45 @@
/* vi:set et ai sw=2 sts=2 ts=2: */
/*-
* Copyright (c) 2005 Benedikt Meurer <benny@xfce.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#ifndef __THUNAR_UCA_PROVIDER_H__
#define __THUNAR_UCA_PROVIDER_H__
#include <thunarx/thunarx.h>
G_BEGIN_DECLS;
typedef struct _ThunarUcaProviderClass ThunarUcaProviderClass;
typedef struct _ThunarUcaProvider ThunarUcaProvider;
#define THUNAR_UCA_TYPE_PROVIDER (thunar_uca_provider_get_type ())
#define THUNAR_UCA_PROVIDER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), THUNAR_UCA_TYPE_PROVIDER, ThunarUcaProvider))
#define THUNAR_UCA_PROVIDER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), THUNAR_UCA_TYPE_PROVIDER, ThunarUcaProviderClass))
#define THUNAR_UCA_IS_PROVIDER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), THUNAR_UCA_TYPE_PROVIDER))
#define THUNAR_UCA_IS_PROVIDER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), THUNAR_UCA_TYPE_PROVIDER))
#define THUNAR_UCA_PROVIDER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), THUNAR_UCA_TYPE_PROVIDER, ThunarUcaProviderClass))
GType
thunar_uca_provider_get_type (void) G_GNUC_CONST;
void
thunar_uca_provider_register_type (ThunarxProviderPlugin *plugin);
G_END_DECLS;
#endif /* !__THUNAR_UCA_PROVIDER_H__ */

View file

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<gresources>
<gresource prefix="/org/xfce/thunar/uca">
<file compressed="true" alias="editor.ui">thunar-uca-editor.ui</file>
<file compressed="true" alias="chooser.ui">thunar-uca-chooser.ui</file>
</gresource>
</gresources>

View file

@ -0,0 +1,49 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE actions [
<!ELEMENT actions (action)+>
<!ELEMENT action (icon|patterns|name|unique-id|command|description|directories|audio-files|image-files|other-files|text-files|video-files)*>
<!ELEMENT icon (#PCDATA)>
<!ELEMENT command (#PCDATA)>
<!ELEMENT patterns (#PCDATA)>
<!ELEMENT name (#PCDATA)>
<!ATTLIST name xml:lang CDATA #IMPLIED>
<!ELEMENT unique-id (#PCDATA)>
<!ELEMENT description (#PCDATA)>
<!ATTLIST description xml:lang CDATA #IMPLIED>
<!ELEMENT startup-notify EMPTY>
<!ELEMENT directories EMPTY>
<!ELEMENT audio-files EMPTY>
<!ELEMENT image-files EMPTY>
<!ELEMENT other-files EMPTY>
<!ELEMENT text-files EMPTY>
<!ELEMENT video-files EMPTY>
]>
<!--
Copyright (c) 2005-2006 Benedikt Meurer <benny@xfce.org>
Default thunar-uca configuration file, which gets installed system-wide.
This should act more like an example, and is therefore kept simple.
-->
<actions>
<action>
<icon>utilities-terminal</icon>
<patterns>*</patterns>
<range></range>
<name>Open Terminal Here</name>
<command>exo-open --working-directory %f --launch TerminalEmulator</command>
<description>Example for a custom action</description>
<startup-notify/>
<directories/>
</action>
</actions>

View file

@ -0,0 +1,45 @@
AM_CPPFLAGS = \
-I$(top_builddir) \
-I$(top_srcdir) \
-DG_LOG_DOMAIN=\"thunar-wallpaper-plugin\" \
-DEXO_API_SUBJECT_TO_CHANGE \
-DPACKAGE_LOCALE_DIR=\"$(localedir)\" \
$(PLATFORM_CPPFLAGS)
extensionsdir = $(libdir)/thunarx-$(THUNARX_VERSION_API)
extensions_LTLIBRARIES = \
thunar-wallpaper-plugin.la
thunar_wallpaper_plugin_la_SOURCES = \
twp-provider.h \
twp-provider.c \
thunar-wallpaper-plugin.c
thunar_wallpaper_plugin_la_CFLAGS = \
$(EXO_CFLAGS) \
$(GLIB_CFLAGS) \
$(GTK_CFLAGS) \
$(LIBX11_CFLAGS) \
$(XFCONF_CFLAGS) \
$(PLATFORM_CFLAGS)
thunar_wallpaper_plugin_la_LDFLAGS = \
-avoid-version \
-export-dynamic \
-no-undefined \
-export-symbols-regex "^thunar_extension_(shutdown|initialize|list_types)" \
-module \
$(PLATFORM_LDFLAGS)
thunar_wallpaper_plugin_la_LIBADD = \
$(top_builddir)/thunarx/libthunarx-$(THUNARX_VERSION_API).la \
$(EXO_LIBS) \
$(GLIB_LIBS) \
$(GTK_LIBS) \
$(XFCONF_LIBS) \
$(LIBX11_LIBS)
thunar_wallpaper_plugin_la_DEPENDENCIES = \
$(top_builddir)/thunarx/libthunarx-$(THUNARX_VERSION_API).la
# vi:set ts=8 sw=8 noet ai nocindent syntax=automake:

View file

@ -0,0 +1,6 @@
Thunar Wallpaper Plugin (thunar-wallpaper)
===========================================
The Thunar Wallpaper plugin is an extension to Thunar. It depends on the xfconf-query command line to set the correct settings on the Xfce4 desktop.
To disable this plugin, pass `--disable-wallpaper-plugin` to ./configure.

View file

@ -0,0 +1,86 @@
/*
* Copyright (c) 2008 Stephan Arts <stephan@xfce.org>
* Copyright (c) 2008 Mike Massonnet <mmassonnet@xfce.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301 USA
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include "twp-provider.h"
#include <exo/exo.h>
G_MODULE_EXPORT void
thunar_extension_initialize (ThunarxProviderPlugin *plugin);
G_MODULE_EXPORT void
thunar_extension_shutdown (void);
G_MODULE_EXPORT void
thunar_extension_list_types (const GType **types,
gint *n_types);
static GType type_list[1];
G_MODULE_EXPORT void
thunar_extension_initialize (ThunarxProviderPlugin *plugin)
{
const gchar *mismatch;
/* verify that the thunarx versions are compatible */
mismatch = thunarx_check_version (THUNARX_MAJOR_VERSION, THUNARX_MINOR_VERSION, THUNARX_MICRO_VERSION);
if (G_UNLIKELY (mismatch != NULL))
{
g_warning ("Version mismatch: %s", mismatch);
return;
}
/* setup i18n support */
bindtextdomain (GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR);
#ifdef HAVE_BIND_TEXTDOMAIN_CODESET
bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
#endif
/* register the types provided by this plugin */
twp_provider_register_type (plugin);
/* setup the plugin provider type list */
type_list[0] = TWP_TYPE_PROVIDER;
}
G_MODULE_EXPORT void
thunar_extension_shutdown (void)
{
}
G_MODULE_EXPORT void
thunar_extension_list_types (const GType **types,
gint *n_types)
{
*types = type_list;
*n_types = G_N_ELEMENTS (type_list);
}

View file

@ -0,0 +1,384 @@
/*
* Copyright (c) 2008 Stephan Arts <stephan@xfce.org>
* Copyright (c) 2008-2009 Mike Massonnet <mmassonnet@xfce.org>
* Copyright (c) 2009 Jannis Pohlmann <jannis@xfce.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301 USA
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include "twp-provider.h"
#include <X11/Xatom.h>
#include <X11/Xlib.h>
#include <gdk/gdkx.h>
#include <gio/gio.h>
#include <glib/gi18n-lib.h>
#include <xfconf/xfconf.h>
#define XFDESKTOP_SELECTION_FMT "XFDESKTOP_SELECTION_%d"
#define NAUTILUS_SELECTION_FMT "NAUTILUS_DESKTOP_WINDOW_ID"
static void
twp_menu_provider_init (ThunarxMenuProviderIface *iface);
static void
twp_provider_finalize (GObject *object);
static GList *
twp_provider_get_file_menu_items (ThunarxMenuProvider *menu_provider,
GtkWidget *window,
GList *files);
static void
twp_action_set_wallpaper (ThunarxMenuItem *item,
gpointer user_data);
static gint
twp_get_active_workspace_number (GdkScreen *screen);
static gboolean _has_gsettings = FALSE;
static GtkWidget *main_window = NULL;
struct _TwpProviderClass
{
GObjectClass __parent__;
};
struct _TwpProvider
{
GObject __parent__;
gchar *child_watch_path;
gint child_watch_id;
};
THUNARX_DEFINE_TYPE_WITH_CODE (TwpProvider, twp_provider, G_TYPE_OBJECT, THUNARX_IMPLEMENT_INTERFACE (THUNARX_TYPE_MENU_PROVIDER, twp_menu_provider_init));
static void
twp_menu_provider_init (ThunarxMenuProviderIface *iface)
{
iface->get_file_menu_items = twp_provider_get_file_menu_items;
}
static void
twp_provider_class_init (TwpProviderClass *klass)
{
GObjectClass *gobject_class;
gobject_class = G_OBJECT_CLASS (klass);
gobject_class->finalize = twp_provider_finalize;
}
static void
twp_provider_init (TwpProvider *twp_provider)
{
gchar *program;
program = g_find_program_in_path ("gsettings");
if (G_LIKELY (program != NULL))
{
_has_gsettings = TRUE;
g_free (program);
}
}
static void
twp_provider_finalize (GObject *object)
{
G_OBJECT_CLASS (twp_provider_parent_class)->finalize (object);
}
static GList *
twp_provider_get_file_menu_items (ThunarxMenuProvider *menu_provider,
GtkWidget *window,
GList *files)
{
ThunarxMenuItem *item = NULL;
GFile *location;
GList *items = NULL;
gchar *mime_type;
main_window = window;
/* we can only set a single wallpaper */
if (files->next == NULL)
{
/* get the location of the file */
location = thunarx_file_info_get_location (files->data);
/* unable to handle non-local files */
if (G_UNLIKELY (!g_file_has_uri_scheme (location, "file")))
{
g_object_unref (location);
return NULL;
}
/* release the location */
g_object_unref (location);
if (!thunarx_file_info_is_directory (files->data))
{
/* get the mime type of the file */
mime_type = thunarx_file_info_get_mime_type (files->data);
if (g_str_has_prefix (mime_type, "image/")
&& (thunarx_file_info_has_mime_type (files->data, "image/jpeg")
|| thunarx_file_info_has_mime_type (files->data, "image/png")
|| thunarx_file_info_has_mime_type (files->data, "image/svg+xml")
|| thunarx_file_info_has_mime_type (files->data, "image/svg+xml-compressed")))
{
item = thunarx_menu_item_new ("Twp::setwallpaper", _("Set as wallpaper"), NULL, "preferences-desktop-wallpaper");
g_signal_connect (item, "activate", G_CALLBACK (twp_action_set_wallpaper), files->data);
items = g_list_append (items, item);
}
g_free (mime_type);
}
}
return items;
}
static void
twp_action_set_wallpaper (ThunarxMenuItem *item,
gpointer user_data)
{
ThunarxFileInfo *file_info = user_data;
GdkDisplay *display = gdk_display_get_default ();
gint screen_nr = 0;
gint n_monitors;
gint monitor_nr = 0;
gint workspace;
GdkScreen *screen;
GdkMonitor *monitor;
gchar *image_path_prop;
gchar *image_show_prop;
gchar *image_style_prop;
const char *monitor_name;
gchar *file_uri;
gchar *escaped_file_name;
gchar *file_name = NULL;
gchar *hostname = NULL;
gchar *command;
XfconfChannel *channel;
gboolean is_single_workspace;
gint current_image_style;
const gchar *desktop_type = NULL;
screen = gdk_display_get_default_screen (display);
desktop_type = g_getenv ("XDG_CURRENT_DESKTOP");
if (desktop_type == NULL)
{
g_warning ("Failed to set wallpaper: $XDG_CURRENT_DESKTOP is not defined");
return;
}
file_uri = thunarx_file_info_get_uri (file_info);
file_name = g_filename_from_uri (file_uri, &hostname, NULL);
if (hostname != NULL)
{
g_free (hostname);
g_free (file_uri);
g_free (file_name);
return;
}
workspace = twp_get_active_workspace_number (screen);
n_monitors = gdk_display_get_n_monitors (display);
if (n_monitors > 1 && main_window)
{
monitor = gdk_display_get_monitor_at_window (display, gtk_widget_get_window (main_window));
}
else
{
monitor = gdk_display_get_monitor (display, monitor_nr);
}
monitor_name = gdk_monitor_get_model (monitor);
escaped_file_name = g_shell_quote (file_name);
if (g_strcmp0 (desktop_type, "XFCE") == 0)
{
g_debug ("set on xfce");
channel = xfconf_channel_get ("xfce4-desktop");
/* This is the format for xfdesktop before 4.11 */
image_path_prop = g_strdup_printf ("/backdrop/screen%d/monitor%d/image-path", screen_nr, monitor_nr);
image_show_prop = g_strdup_printf ("/backdrop/screen%d/monitor%d/image-show", screen_nr, monitor_nr);
image_style_prop = g_strdup_printf ("/backdrop/screen%d/monitor%d/image-style", screen_nr, monitor_nr);
/* Set the wallpaper and ensure that it's set to show */
xfconf_channel_set_string (channel, image_path_prop, file_name);
xfconf_channel_set_bool (channel, image_show_prop, TRUE);
/* If there isn't a wallpaper style set (-1), or it is set to 'None' (which is 0) then set one */
current_image_style = xfconf_channel_get_int (channel, image_style_prop, -1);
if (current_image_style <= 0)
{
/* Lets hope that 5 = 'Zoomed' works fine for the selected picture */
xfconf_channel_set_int (channel, image_style_prop, 5);
}
g_free (image_path_prop);
g_free (image_show_prop);
g_free (image_style_prop);
/* Xfdesktop 4.11+ has a concept of a single-workspace-mode where
* the same workspace is used for everything but additionally allows
* the user to use any current workspace as the single active
* workspace, we'll need to check if it is enabled (which by default
* it is) and use that. */
is_single_workspace = xfconf_channel_get_bool (channel, "/backdrop/single-workspace-mode", TRUE);
if (is_single_workspace)
{
workspace = xfconf_channel_get_int (channel, "/backdrop/single-workspace-number", 0);
}
/* This is the format for xfdesktop post 4.11. A workspace number is
* added and the monitor is referred to name. We set both formats so
* that it works as the user expects. */
if (monitor_name)
{
image_path_prop = g_strdup_printf ("/backdrop/screen%d/monitor%s/workspace%d/last-image", screen_nr, monitor_name, workspace);
image_style_prop = g_strdup_printf ("/backdrop/screen%d/monitor%s/workspace%d/image-style", screen_nr, monitor_name, workspace);
}
else
{
/* gdk_screen_get_monitor_plug_name can return NULL, in those
* instances we fallback to monitor number but still include the
* workspace number */
image_path_prop = g_strdup_printf ("/backdrop/screen%d/monitor%d/workspace%d/last-image", screen_nr, monitor_nr, workspace);
image_style_prop = g_strdup_printf ("/backdrop/screen%d/monitor%d/workspace%d/image-style", screen_nr, monitor_nr, workspace);
}
xfconf_channel_set_string (channel, image_path_prop, file_name);
/* If there isn't a wallpaper style set (-1), or it is set to 'None' (which is 0) then set one */
current_image_style = xfconf_channel_get_int (channel, image_style_prop, -1);
if (current_image_style <= 0)
{
/* Lets hope that 5 = 'Zoomed' works fine for the selected picture */
xfconf_channel_set_int (channel, image_style_prop, 5);
}
g_free (image_path_prop);
g_free (image_style_prop);
}
else if (g_strcmp0 (desktop_type, "GNOME") == 0)
{
if (_has_gsettings)
{
g_debug ("set on gnome");
command = g_strdup_printf ("gsettings set "
"org.gnome.desktop.background picture-uri "
"'%s'",
file_uri);
g_spawn_command_line_async (command, NULL);
g_free (command);
}
else
{
g_warning ("Failed to set wallpaper: Missing executable 'gsettings'");
}
}
else
{
g_warning (("Failed to set wallpaper: $XDG_CURRENT_DESKTOP Desktop type '%s' not supported by thunar wallpaper plugin."), desktop_type);
}
g_free (escaped_file_name);
g_free (file_name);
g_free (file_uri);
}
/* Taken from xfce_spawn_get_active_workspace_number in xfce-spawn.c apart of
* the libxfce4ui library.
* https://git.xfce.org/xfce/libxfce4ui/tree/libxfce4ui/xfce-spawn.c#n193
*/
static gint
twp_get_active_workspace_number (GdkScreen *screen)
{
GdkWindow *root;
gulong bytes_after_ret = 0;
gulong nitems_ret = 0;
guint *prop_ret = NULL;
Atom _NET_CURRENT_DESKTOP;
Atom _WIN_WORKSPACE;
Atom type_ret = None;
gint format_ret;
gint ws_num = 0;
if (!GDK_IS_X11_DISPLAY (gdk_display_get_default ()))
return 0;
gdk_x11_display_error_trap_push (gdk_display_get_default ());
root = gdk_screen_get_root_window (screen);
/* determine the X atom values */
_NET_CURRENT_DESKTOP = XInternAtom (GDK_WINDOW_XDISPLAY (root), "_NET_CURRENT_DESKTOP", False);
_WIN_WORKSPACE = XInternAtom (GDK_WINDOW_XDISPLAY (root), "_WIN_WORKSPACE", False);
if (XGetWindowProperty (GDK_WINDOW_XDISPLAY (root),
gdk_x11_get_default_root_xwindow (),
_NET_CURRENT_DESKTOP, 0, 32, False, XA_CARDINAL,
&type_ret, &format_ret, &nitems_ret, &bytes_after_ret,
(gpointer) &prop_ret)
!= Success)
{
if (XGetWindowProperty (GDK_WINDOW_XDISPLAY (root),
gdk_x11_get_default_root_xwindow (),
_WIN_WORKSPACE, 0, 32, False, XA_CARDINAL,
&type_ret, &format_ret, &nitems_ret, &bytes_after_ret,
(gpointer) &prop_ret)
!= Success)
{
if (G_UNLIKELY (prop_ret != NULL))
{
XFree (prop_ret);
prop_ret = NULL;
}
}
}
if (G_LIKELY (prop_ret != NULL))
{
if (G_LIKELY (type_ret != None && format_ret != 0))
ws_num = *prop_ret;
XFree (prop_ret);
}
gdk_x11_display_error_trap_pop_ignored (gdk_display_get_default ());
return ws_num;
}

View file

@ -0,0 +1,45 @@
/*
* Copyright (c) 2008 Stephan Arts <stephan@xfce.org>
* Copyright (c) 2008 Mike Massonnet <mmassonnet@xfce.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301 USA
*/
#ifndef __TWP_PROVIDER_H__
#define __TWP_PROVIDER_H__
#include <thunarx/thunarx.h>
G_BEGIN_DECLS;
typedef struct _TwpProviderClass TwpProviderClass;
typedef struct _TwpProvider TwpProvider;
#define TWP_TYPE_PROVIDER (twp_provider_get_type ())
#define TWP_PROVIDER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), TWP_TYPE_PROVIDER, TwpProvider))
#define TWP_PROVIDER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), TWP_TYPE_PROVIDER, TwpProviderClass))
#define TWP_IS_PROVIDER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TWP_TYPE_PROVIDER))
#define TWP_IS_PROVIDER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), TWP_TYPE_PROVIDER))
#define TWP_PROVIDER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), TWP_TYPE_PROVIDER, TwpProviderClass))
GType
twp_provider_get_type (void) G_GNUC_CONST;
void
twp_provider_register_type (ThunarxProviderPlugin *plugin);
G_END_DECLS;
#endif /* !__TWP_PROVIDER_H__ */